# API Reference

## Exports

The package exports `didToJs`, `didToTs`, `parseDid`, `validateIDL`,
`verifyCompatibility`, the deprecated `verifyCompatability`, and a default WASM
initializer, `init`, with a synchronous `initSync` companion.

**Initialize the WASM module first:** In browsers and bundlers the package resolves to `dist/web`, where the WASM
  module is only instantiated by `await init()`. Calling any function before
  that throws. The Node build (`dist/nodejs`) instantiates itself at await init() // required on the web build
const js = didToJs("service:{}")
```

**Candid imports:** Every function throws for `import service` declarations, because a source
  string cannot load the imported file and its methods would be missing from the
  result. A plain `import` is ignored, so a type that only the imported file
  declares is reported as unbound.

### `didToJs`

`didToJs(candid: string): string`

Compiles a Candid interface string into a JavaScript module string.

**Parameters:**

- `candid`: The Candid interface definition string.

**Returns:**

- A string containing the JavaScript module source.

**Output Exports:**
The generated JavaScript module exports:

- `idlFactory`: An `IDL.InterfaceFactory` function.
- `init`: A function that returns initialization arguments.

```typescript
const js = didToJs("service:{}")
// export const idlFactory ...
```

### `didToTs`

`didToTs(candid: string): string`

Compiles a Candid interface string into a TypeScript declaration string.

**Parameters:**

- `candid`: The Candid interface definition string.

**Returns:**

- A string containing the TypeScript declaration source.

**Output Includes:**

- Imports from `@icp-sdk/core` (or legacy `@dfinity/agent`).
- `_SERVICE` interface defining the actor type.
- `idlFactory` and `init` type declarations.

```typescript
const ts = didToTs("service:{}")
// export interface _SERVICE ...
```

#### Renamed types

`didToTs` exports each Candid type under its own name, next to the `_SERVICE`
interface. A type TypeScript cannot declare or refer to under its name is
renamed with `_` appended, and `didToJs` uses the same name:

- a type named like a TypeScript type keyword: `string`, `number`, `bigint`,
  `symbol`, `object`, `any`, `unknown`, `never` or `undefined`;
- a type named like a TypeScript type operator: `keyof`, `readonly`, `unique`
  or `infer`;
- a type named `_SERVICE` that is not the service itself;
- a type named like a JavaScript keyword, such as `class` (candid_parser's own
  rule).

```typescript
didToTs(`type string = record { value : text };
service : { get : () -> (string) query }`)
// export interface string_ { 'value' : string }
// export interface _SERVICE { 'get' : ActorMethod<[], string_> }
```

When `string_` is taken by another type, the next free name (`string__`) is
used. A service type named `_SERVICE` that the service uses
(`service : _SERVICE`) keeps its name and is declared once, as the `_SERVICE`
interface.

#### Field names that look like numeric ids

Candid identifies a named record field or variant tag by the hash of its name.
`@icp-sdk/core` reads an IDL key spelled `_<digits>_` or `_0x<hex digits>_`,
for a number below 2^32, as that number instead, the spelling it uses for
numeric fields such as `0 : nat`. So `didToJs` and `didToTs` print a field
_named_ like that under its hash, and it is sent and read by that hash:

```typescript
didToTs(`service : { get : () -> (record { _0_ : nat; 0 : text }) query }`)
// … ActorMethod<[], { _0_ : string, _4735054_ : bigint }>
```

Use the printed key (`_4735054_`) for the value in calls and results.
[`parseDid`](#parsedid) names the field by the same key. Names that do not
match the pattern print as written. A hand-written `.did` that
spells a Motoko numeric field as `_0_` must write it as `0`, the way Motoko
prints it; spelled `_0_`, it is the field named `_0_`.

### `parseDid`

`parseDid(candid: string): CandidSchema`

Parses a Candid interface string into a structured schema object (type
declarations plus the service declaration), including any metadata tags.

**Returns:**

- A `CandidSchema`: `{ types: CandidTypeDeclaration[]; service: CandidServiceDeclaration | null }`.
  Doc-comment metadata and validation tags are carried on the nodes — see
  [Candid Metadata Tags](https://ic-reactor.b3pay.net/v3/packages/parser/metadata-tags).
- Each record field and variant tag is named by the key `didToJs` prints for
  it: `_0_` for the numeric field `0`, and `_4735054_` for a field named `_0_`
  (see [Field names that look like numeric ids](#field-names-that-look-like-numeric-ids)).
  Types keep their Candid names, including one `didToTs` renames.

```typescript
const schema = parseDid("service:{}")
// { types: [...], service: { ... } | null }
```

### `validateIDL`

`validateIDL(candid: string): boolean`

Returns `true` when the Candid source parses and type-checks. It never returns
`false`: for invalid Candid it throws the parser's message as a string (a syntax
error, or a type error such as `Unbound type identifier T`), so call it inside
`try` / `catch`.
[`CandidAdapter.validateCandid()`](https://ic-reactor.b3pay.net/v3/packages/candid/candidadapter), which it
backs, catches that and returns `false`.

```typescript
try {
  validateIDL("service : { greet : (text) -> (text) query }") // true
} catch (message) {
  console.error(message) // e.g. "Unbound type identifier T"
}
```

### `verifyCompatibility`

`verifyCompatibility(oldDid: string, newDid: string): boolean`

Returns `true` when `newDid` is a compatible upgrade of `oldDid`, so every
client of the old interface can keep calling the new one, and `false` when it
is not. Use it to guard canister upgrades against breaking interface changes.

Throws when either source does not parse or type-check, or declares no service.

```typescript
const oldDid = "service : { greet : (text) -> (text) query }"
// Adding a method keeps every client of the old interface working
const newDid = `service : {
  greet : (text) -> (text) query;
  farewell : (text) -> (text) query;
}`

if (!verifyCompatibility(oldDid, newDid)) {
  throw new Error("The new interface breaks existing clients")
}
```

### `verifyCompatability` (deprecated)

`verifyCompatability(a: string, b: string): boolean`

Use `verifyCompatibility(oldDid, newDid)` instead. This older export returns
`true` when `a` is a compatible upgrade of `b`, so it takes the new interface
first, and it throws instead of returning `false` when the upgrade is not
compatible.

### `default` (`init`)

`init(module_or_path?): Promise<InitOutput>`

Instantiates the WASM module. Required once before any other function on the
web build. A synchronous `initSync(module)` variant is also exported for
environments where the WASM bytes are already available.

On Node both exist for ES module imports and do nothing, because the Node build
instantiates the module when it loads. `require` returns only the parser
functions.

```typescript
await init()
```

## Errors

Invalid Candid makes a function throw the parser's message as a string, such as
`Candid parser error: Unrecognized EOF found at 11`.

Some inputs make the parser itself fail, through a Rust panic or input nested or
repeated deeply enough to exhaust the stack. The function then throws an `Error`
whose message says the input could not be parsed, with the original
`WebAssembly.RuntimeError` or `RangeError` as its `cause`. Before it throws, the
parser replaces its WebAssembly instance with a new one built from the module it
already compiled, so later calls work normally and nothing is fetched again.

The web and Node builds behave this way. `dist/bundler`, which no export
condition selects, keeps wasm-bindgen's generated loader and does not restart.

## Module Formats

The package builds for multiple targets to ensure compatibility across environments.

| Folder         | Format                                          | Resolved by                                      |
| :------------- | :---------------------------------------------- | :----------------------------------------------- |
| `dist/web`     | ES Modules                                      | `browser`, `workerd` and `default` conditions    |
| `dist/nodejs`  | ES Modules (`index.mjs`), CommonJS (`index.js`) | `node` condition, for `import` and `require`     |
| `dist/bundler` | ES Modules                                      | Built and shipped, but not reachable via exports |

There is no `./web`, `./nodejs` or `./bundler` subpath — the package has a
single `.` entry and the right build is picked by export conditions. Bundlers
resolve the `browser` condition to `dist/web`, which requires `await init()`.