Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 23 additions & 44 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ the language plus the host functions documented here.
- [Sync inputs (globals)](#sync-inputs-globals)
- [Canister calls](#canister-calls)
- [Metadata sections](#metadata-sections)
- [Environment variables](#environment-variables)
- [Candid](#candid)
- [Number types](#number-types)
- [Exact-encoding classes](#exact-encoding-classes)
Expand All @@ -38,15 +37,16 @@ sync:
files:
script: sync.js
seed: [seed/users.json, seed/roles.json]
dirs:
assets: assets
```

Every declared file — the entry script included — is read by the host and handed
to the script via the `files` object, keyed by path. Directories declared in
`dirs` are preopened read-only and reachable with the filesystem functions below.
Declaring `files:`/`dirs:` as a map instead of a plain list tags each entry with
its key, which the script reads back through `fileKeys` / `dirKeys`.
`files:` is a map of name → path (or list of paths), and holds directories as
well as files; the host sorts them by what it finds on disk. Every declared
file — the entry script included — is read by the host and handed to the script
via the `files` object, keyed by path. Every declared directory is preopened
read-only and reachable with the filesystem functions below, and appears in
`dirs`. Each entry keeps the key it was declared under, which the script reads
back through `fileKeys` / `dirKeys`.

A script runs to completion for a clean sync; throwing (or a runtime error)
fails the step with the thrown message.
Expand All @@ -69,22 +69,30 @@ suspends itself.
| `identityId` | `string` | Textual principal of the signing identity. |
| `identity` | `Principal` | The signing identity as a `Principal`. |
| `proxy` | `Principal` \| `null` | Proxy canister if `--proxy` was set, else `null`. |
| `apiUrl` | `string` | The network's API endpoint, with a trailing slash. |
| `gatewayUrl` | `string` \| `null` | The network's HTTP gateway, or `null` if it has none. |
| `dirs` | `string[]` | Declared directory paths (preopened read-only). |
| `dirKeys` | `object` (key → `string[]`) | Manifest key → the directory paths declared under it. |
| `files` | `object` (path → `string`) | Contents of every declared file, by path. |
| `fileKeys` | `object` (key → `string[]`) | Manifest key → the file paths declared under it. |
| `fields` | `object` (name → `string`) | Key-value fields declared in the step's `fields`. |
| `canisterIds` | `object` (name → `string`) | Every project canister's name → textual principal. |

`dirKeys` and `fileKeys` cover only the entries declared under a map key; a
plain-list `dirs:`/`files:` has none, and appears only in `dirs`/`files`. One key
may name several paths, so each maps to an array:
Every `files:` entry is declared under a key, so `dirKeys` and `fileKeys`
between them cover all of `dirs` and `files`. One key may name several paths, so
each maps to an array:

```js
// Contents of every file declared under the `seed` key.
let seeds = fileKeys.seed.map((path) => files[path]);
```

`apiUrl` and `gatewayUrl` say where the network is reached, normalized so a URL
with no path carries a trailing slash (`"http://127.0.0.1:4943/"`). They are
there to compose a URL from — to hand to a canister, or to print — not to fetch:
the plugin has no sockets, and the host makes every canister call on the
script's behalf.

`canisterIds` is informational: it maps each named canister in the project
(both `subproject:local` keys and bare local names for same-subproject siblings)
to its textual principal for the environment being synced. Being listed does not
Expand Down Expand Up @@ -151,35 +159,6 @@ signed by the sync identity, which reaches a private section only if that
identity controls the target; a proxied read reaches one private to the proxy's
control.

## Environment variables

`canisterSetenv` sets one of a canister's runtime environment variables, leaving
its other variables — and the rest of its settings — as they are. It names its
receiver first, as a call shorthand does.

```js
canisterSetenv(self, "SEEDED_BY", environment);
canisterSetenv("ledger", "ADMIN", canisterIds.backend);

// Optional trailing options; `direct` is the only one.
canisterSetenv(self, "ADMIN", identityId, { direct: true });
```

The value is a string: the canister reads it back verbatim, so anything else is
the script's to render (`String(x)`, or `x.toText()` for a `Principal`). The
update is controller-gated — with `direct` the sync identity must control the
receiver, and by default the proxy configured via `--proxy` makes it, so that is
what must control it. With no proxy configured the sync identity signs either
way.

Set the variable on every sync rather than once. The management canister can only
replace a canister's variables as a whole list, so the host reads them and writes
them back with yours added — and a later `icp deploy` rewrites that list from the
manifest, dropping what a plugin added. Deploy runs the sync phase afterwards, so
a script that always sets it always restores it. For a variable that should not
depend on the plugin running, declare it in the manifest's
`environment_variables` setting instead.

## Candid

An argument is written as Candid source with JavaScript values interpolated
Expand Down Expand Up @@ -458,7 +437,7 @@ call returns at most 1 MiB.

## Filesystem

Read-only access to the directories the step declared under `dirs:`, backed by
Read-only access to the directories the step declared under `files:`, backed by
WASI. Each is readable at the path the manifest declared it at, and nothing
outside them is readable at all.

Expand Down Expand Up @@ -492,10 +471,10 @@ for (const name of readDir("assets")) {

The reads throw with the underlying error; the predicates answer `false`
instead, so a path that may not be there — or may not be reachable — can be
asked about. Since only the declared `dirs:` are readable, a failed read also
says what the step declared, and a path naming a declared *file* says to read it
from `files`: the host passes those contents inline rather than putting them on
the filesystem.
asked about. Since only the declared directories are readable, a failed read
also says which ones the step declared, and a path naming a declared *file* says
to read it from `files`: the host passes those contents inline rather than
putting them on the filesystem.

`joinPath` separates the parts it is given with single slashes however they are
punctuated, dropping empty ones. A part that starts at the root replaces what
Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
An [icp-cli](https://github.com/dfinity/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.
calling the target canister, reading its metadata, the sync inputs, and
read-only filesystem access — plus Candid, principal, and encoding helpers
convenient for canister work.

Scripts run on [QuickJS](https://bellard.org/quickjs/) via
[rquickjs](https://crates.io/crates/rquickjs); it is a small ES2020-class engine
Expand All @@ -31,9 +31,9 @@ without a WebAssembly runtime.
## Using it

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.
(or inline in a `script` field). `files:` is a map of name → path, and holds
directories as well as files: the host reads each file and hands its contents to
the script by path, and preopens each directory read-only.

```yaml
sync:
Expand All @@ -43,6 +43,7 @@ sync:
files:
script: sync.js
config: config.json
assets: assets/
```

A script runs to completion for a clean sync; throwing fails the step with the
Expand Down Expand Up @@ -77,15 +78,14 @@ Each of these is covered in [API.md](./API.md):

| | |
| --- | --- |
| [Sync inputs](./API.md#sync-inputs-globals) | `canisterId`, `identity`, `environment`, `proxy`, `files`, `dirs`, `fields`, `canisterIds` and friends, as globals. |
| [Sync inputs](./API.md#sync-inputs-globals) | `canisterId`, `identity`, `environment`, `proxy`, `apiUrl`, `files`, `dirs`, `fields`, `canisterIds` and friends, as globals. |
| [Canister calls](./API.md#canister-calls) | `callQuery` / `callUpdate` / `canisterCall`, against the synced canister or any canister the step declared. |
| [Coerced calls](./API.md#coerced-calls) | `callTyped` / `canisterCallTyped` and `CandidInterface`, which encode and decode against the callee's own `.did`. |
| [Candid](./API.md#candid) | The `candid` template tag, `CandidArgs`, `candidEncode` / `candidDecode`, the [number types](./API.md#number-types), and the [exact-encoding classes](./API.md#exact-encoding-classes) for variants, optionals, tuples and references. |
| [Metadata sections](./API.md#metadata-sections) | `canisterMetadata`, reading a canister's custom sections. |
| [Environment variables](./API.md#environment-variables) | `canisterSetenv`, setting one runtime variable on a canister. |
| [Principals](./API.md#principals) | The `Principal` class of [icp-js-core](https://github.com/dfinity/icp-js-core). |
| [Helpers](./API.md#encoding-helpers) | `sha256`, `encodeUtf8` / `decodeUtf8`, and [`randomBytes`](./API.md#randomness). |
| [Filesystem](./API.md#filesystem) | Read-only reads, predicates and `joinPath` over the declared `dirs:`. |
| [Filesystem](./API.md#filesystem) | Read-only reads, predicates and `joinPath` over the directories the step declared. |
| [Output](./API.md#output) | `print` / `eprint` and the `console` methods. |

## License
Expand Down
Loading
Loading