An icp-cli sync plugin that runs a JavaScript script against the canister being synced. It exposes to the script roughly the same capabilities a native sync plugin has — calling the target canister, reading its metadata, setting its environment variables, the sync inputs, and read-only filesystem access — plus Candid, principal, and encoding helpers convenient for canister work.
Scripts run on QuickJS via
rquickjs; it is a small ES2020-class engine
without Node or Web APIs, so no require/import, no fetch, no timers — just
the language plus the host functions the plugin provides.
API.md documents the scripting API in full.
The plugin is a WebAssembly component targeting wasm32-wasip2:
rustup target add wasm32-wasip2
cargo build --target wasm32-wasip2 --releaseThe component is emitted at target/wasm32-wasip2/release/icp_js_plugin.wasm.
The crate also builds for the host, so cargo check and cargo test run
without a WebAssembly runtime.
Declare the plugin as a sync step, with the entry script under the script key
(or inline in a script field). Any other files declared are read by the host
and handed to the script by path; directories under dirs: are preopened
read-only.
sync:
steps:
- plugin: ./icp_js_plugin.wasm
canisters: [ledger]
files:
script: sync.js
config: config.jsonA script runs to completion for a clean sync; throwing fails the step with the thrown message.
Push a list of authorized principals from a JSON file to a sibling canister:
// { "authorized": ["aaaaa-aa", "ryjl3-tyaaa-aaaaa-aaaba-cai"] }
const config = JSON.parse(files["config.json"]);
// `set_authorized : (vec principal) -> ()`. The strings out of the file are
// wrapped in `Principal.from(..)`, which both validates them and tells the encoder
// they are principals rather than text.
const authorized = config.authorized.map((p) => Principal.from(p));
callUpdate("example", "set_authorized", candid`(${authorized})`);Or the same call written against the canister's own interface, which turns the strings into principals itself:
const config = JSON.parse(files["config.json"]);
callTyped("example", "set_authorized", config.authorized);Each of these is covered in API.md:
| Sync inputs | canisterId, identity, environment, proxy, files, dirs, fields, canisterIds and friends, as globals. |
| Canister calls | callQuery / callUpdate / canisterCall, against the synced canister or any canister the step declared. |
| Coerced calls | callTyped / canisterCallTyped and CandidInterface, which encode and decode against the callee's own .did. |
| Candid | The candid template tag, CandidArgs, candidEncode / candidDecode, the number types, and the exact-encoding classes for variants, optionals, tuples and references. |
| Metadata sections | canisterMetadata, reading a canister's custom sections. |
| Environment variables | canisterSetenv, setting one runtime variable on a canister. |
| Principals | The Principal class of icp-js-core. |
| Helpers | sha256, encodeUtf8 / decodeUtf8, and randomBytes. |
| Filesystem | Read-only reads, predicates and joinPath over the declared dirs:. |
| Output | print / eprint and the console methods. |
This project is licensed under the Apache-2.0 license.
This project does not accept external contributions. Pull requests from individuals outside the organization will be automatically closed.