Imports and Package Management (upcoming)
Available in version 1.1.72 (not yet live).
Use standard JavaScript imports like import dayjs from "[email protected]"
or zw.import()
to install (if needed) and load packages during a run. ZeroWork installs missing packages and makes them available to your code.
Manage packages with zw.packages.list()
, zw.packages.uninstall()
, and zw.packages.uninstallAll()
.
API zw.import()
, zw.packages.*
// @zw-run-locally
import dayjs from "[email protected]"; // auto-installs if needed
await zw.log("now", dayjs().toISOString());
// Example with options — 'isolate' scopes the package to this TaskBot only
const lodash = await zw.import({ isolate: true }, "[email protected]");
await zw.log("chunked result", lodash.chunk([1, 2, 3, 4], 2));
1. Imports
Standard Imports
You can use standard ESM or CommonJS syntax. Any such import installs the package if needed, then loads it.
// @zw-run-locally
// ESM default import
import dayjs from "[email protected]";
await zw.log("now", dayjs().toISOString());
// ESM subpath import
import chunk from "lodash/chunk";
await zw.log("chunked result", chunk([1, 2, 3, 4], 2));
// CommonJS require
const _ = require("[email protected]");
await zw.log("uniq result", _.uniq([1, 1, 2, 3]));
// Import from Git (HTTPS only)
import lodash from "git+https://github.com/lodash/lodash.git"
Notes
Installed packages are available to any TaskBot by default. To scope per TaskBot, use
zw.import()
with{ isolate: true }
(see details further below).If a package isn’t used for one week, it's removed automatically. To change this window or prevent automatic uninstall, use
zw.import()
withuninstallIfUnusedFor
(see details further below).Subpaths like
"lodash/chunk"
and Git repositories are supported.
Imports with zw.import()
zw.import()
Use zw.import()
when you need options or want to import several packages at once.
At a Glance
await zw.import(options: ImportOptions, pkg: string | string[] | { [key: string]: string })
→ installs if needed and returns the loaded module(s)
// Reference only — not executable in Write JS
type ImportOptions = {
uninstallIfUnusedFor: number | null; // hours; default 168; null = never uninstall
isolate: boolean; // scope to this TaskBot only; default false
preferDefault: boolean; // advanced; default false
};
Examples
// @zw-run-locally
// Single package with options
const dayjs = await zw.import(
{ isolate: true, uninstallIfUnusedFor: 300 },
"[email protected]"
);
// Multiple packages as an array
const [dayjs2, lodash] = await zw.import(
{ isolate: true },
["[email protected]", "[email protected]"]
);
// Named mapping as an object
const { time, dash } = await zw.import(
{ uninstallIfUnusedFor: null },
{ time: "[email protected]", dash: "[email protected]" }
);
// Installing from Git (HTTPS only)
const gitLodash = await zw.import(
{ uninstallIfUnusedFor: null },
"git+https://github.com/lodash/lodash.git"
);
// Use default options by passing an empty object {}.
const dash2 = await zw.import({}, "[email protected]");
Import Option Details
uninstallIfUnusedFor
Default 168 hours (one week). Set to null to keep the package on the device indefinitely.isolate
Default false. When true, the package is scoped to this TaskBot only and isn’t visible in other TaskBots. Useful when different TaskBots need different versions of the same package. Note: If the TaskBot is deleted, the package isn’t removed automatically; you can manage it later withzw.packages.*
(see details further below).preferDefault
(advanced) Default false. If set to true, it returnsmodule.default
when present (otherwise the module object). This only changes the return shape; it doesn’t affect how the module is resolved. In most cases, leave it false.// @zw-run-locally // In most cases, leave preferDefault as is (false). const mysql = await zw.import({}, "mysql2@latest/promise"); // works const { chalk } = await zw.import({}, { chalk: "chalk@4" }); // works
Supported Package Inputs
You can pass any of the following to zw.import()
as the second argument.
String
"lodash"
,"[email protected]"
,"lodash/chunk"
,"https://github.com/user/repo.git#main"
or"git+https://github.com/user/repo.git#main"
Array of strings
["[email protected]", "[email protected]"]
Object mapping
{ util: "[email protected]", time: "[email protected]" }
Invalid inputs that are rejected
Tarball URLs, local file paths, and directories
Non-HTTPS Git URLs
Built-in Packages
These packages are pre-bundled and resolve without installation.
Node core modules such as
fs
,os
, etc.axios
pinned to^1.6.6
playwright
pinned to^1.45.0
They don’t appear in zw.packages.list()
, and you can’t change their versions or uninstall them.
// @zw-run-locally
import * as fs from "fs"; // pre-bundled; no installation occurs
import axios from "axios"; // resolves to axios@^1.6.6
// Version spec is ignored; resolves to pinned playwright@^1.45.0
import playwright from "[email protected]";
Pure ESM Packages
ZeroWork isn’t pure ESM yet. Pure ESM packages aren’t supported and will throw an error similar to:
This package appears to be pure ESM. Pure ESM packages are currently not supported. To confirm, check the package documentation.
Workarounds for pure ESM packages
Use a maintained non-ESM fork if available. For example,
cacheable-lookup
is a pure ESM module but a maintained fork is available:@esm2cjs/cacheable-lookup
.Pin a non-ESM version if the project provides one. For example, the latest
chalk
version is pure ESM but its documentation recommends pinning to v4chalk@4
for a CommonJS build.If the package is pure ESM with no CommonJS fork or version, you will have to find an alternative.
Reusing References
Import once and reuse the reference, as is generally best practice. The package won’t reinstall if it’s already installed, but package parsing and resolution still add overhead.
// @zw-run-locally
// 1. Anti-pattern: import inside the loop (re-parses/resolves each time)
for (let i = 0; i < 1_000_000; i++) {
await zw.log((await zw.import({}, "[email protected]"))().toISOString());
}
// 2. Recommended: reuse a local reference
const dayjs = await zw.import({}, "[email protected]");
for (let i = 0; i < 1_000_000; i++) {
await zw.log(dayjs().toISOString());
}
// 3. Optional (micro-optimization): reuse across Write JS blocks via run state
const cachedDayjs = await zw.import({}, "[email protected]");
(zw.state.access().cachedResolvedImports ||={}).dayjs = cachedDayjs;
// In a later building block:
await zw.log(zw.state.access().cachedResolvedImports.dayjs().toISOString());
2. Package Management
At a Glance
await zw.packages.list()
→string[]
of package IDsawait zw.packages.uninstall(id: string)
→ removes oneawait zw.packages.uninstallAll()
→ removes all user-managed packages
What’s a Package ID?
A package ID uniquely identifies an installed package instance. It’s an opaque string made of an optional isolation prefix (<taskbotId>_
), a lower-cased, sanitized package name, @
, and a version tag (<semver>
, latest
, git
, or a commit hash). It may differ from your original import string (subpaths aren’t included; characters are normalized).
Examples
[email protected]
[email protected]
[email protected] // isolated to a specific TaskBot (prefix includes its ID)
repo_name@git // Git source without a specific commit
repo_name@9f3a2c4 // Git source pinned to a commit
Use these IDs with zw.packages.uninstall(id)
.
Examples
List packages
// @zw-run-locally
const ids = await zw.packages.list();
await zw.log("packages", ids);
Uninstall by package name/version
// @zw-run-locally
const ids = await zw.packages.list();
const target = ids.find(id => id.includes("[email protected]"));
if (target) await zw.packages.uninstall(target);
Uninstall all for a specific TaskBot
// @zw-run-locally
const myTaskBotId = 12345;
const ids = await zw.packages.list();
const targets = ids.filter(id => id.startsWith(`${myTaskBotId.toString()}_`));
for (const target of targets) {
await zw.packages.uninstall(target);
}
Uninstall all
// @zw-run-locally
await zw.packages.uninstallAll();
3. Local and Browser Execution
Package installation and management run locally only. Standard import
/require
, zw.import()
, and zw.packages.*
are supported only when the Write JS block runs outside the browser context. Enable Run locally in the app checkbox in the block UI, or add the comment // @zw-run-locally
.
You can still use imported packages in the browser by exposing functions from a local block (see below).
How to Use Imported Packages in the Browser
Import locally in one block, expose a function, then call it from a later block that runs in the browser.
// Write JS Block A — runs locally
// @zw-run-locally
import lodash from "lodash";
const context = zw.browserContext.getContext();
await context.exposeFunction("exposedChunkFn", (arr) => lodash.chunk(arr, 2));
// Write JS Block B — runs in the browser
const result = exposedChunkFn([1, 2, 3, 4]); // [[1,2],[3,4]]
await zw.log("exposedChunkFn result", result);
Last updated
Was this helpful?