@meantjs/cli 0.0.0-bootstrap → 0.0.0-stage

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md DELETED
@@ -1,733 +0,0 @@
1
- # Changelog
2
-
3
- What changed between released versions of `@meantjs/cli`, which was
4
- published as `@candid-core/cli`, with the command `candid-core-cli`, through
5
- 0.4.0; the entries for those releases name them so. This file ships inside
6
- the published tarball, so the record travels with the artifact.
7
-
8
- **Every entry names the exact revisions it embeds** — the compiler crate
9
- (`meant` from 0.4.0's entry on, published as `candid-core` before) and the
10
- `candid-core-ts` generator are compiled into the wasm artifact from one
11
- repository commit, and the pairing is the artifact's
12
- provenance: the parity gates prove the emitted module and contract are
13
- byte-identical to that commit's Rust-native outputs over the golden
14
- fixtures.
15
-
16
- `@meantjs/cli` is pre-1.0. Until 1.0 any release may change the CLI
17
- grammar, the library API, and the request/response shapes. Pin an exact
18
- version.
19
-
20
- ## 0.5.0 — 2026-11-01
21
-
22
- Embeds `meant` 0.1.0 and the `candid-core-ts` generator from the repository
23
- commit the release is dispatched from; the release record names the exact
24
- SHA. The dispatched commit's `meant` source is ahead of the crates.io 0.1.0
25
- archive by two changes, and neither moves a Contract or an identity: the
26
- reader of doc attributes behind `didToDocAttributes`, which 0.4.0 embedded
27
- already, and type-depth walks that allocate the same way for the same input
28
- whatever the heap layout, refusing and returning what they did before. So no
29
- crate release comes first.
30
-
31
- **The first release under the name `@meantjs/cli`.** Through 0.4.0 this
32
- package was published as `@candid-core/cli`, with the command
33
- `candid-core-cli`. 0.5.0 is 0.4.0's code under new names: the package is
34
- `@meantjs/cli`, its one command is `meant`, the name of the Rust binary,
35
- and the modules it writes import `@meantjs/schema`, the new name of
36
- `@candid-core/schema`. The packages moved to the `@meantjs` scope beside the
37
- profile packages, so that the project ships under two names: ic-reactor, for
38
- the Internet Computer, and Meant, which is not tied to one chain.
39
- `@candid-core/cli` keeps every version it published and gets no new one.
40
-
41
- A stable release, published under the npm `latest` dist-tag; keep it pinned,
42
- `npm install --save-dev --save-exact @meantjs/cli`. Run it as
43
- `npx @meantjs/cli gen …`, or as `meant gen …` from a script in your
44
- `package.json`. Never as a bare `npx meant`: the unscoped `meant` package on
45
- npm belongs to another author, so where this package is not installed, npx
46
- would offer to download and run that one. In a shell, a bare `meant` runs
47
- whichever `meant` comes first on the `PATH`, which can be the Rust binary from
48
- `cargo install meant`, whose commands are `compile` and `validate`; a script
49
- in `package.json` puts this package's command first. The optional peer is
50
- `@meantjs/schema` at exactly `0.6.0`, which is `@candid-core/schema` 0.5.0's
51
- code under its new name; install the two together, each at its exact
52
- version.
53
-
54
- **Upgrading from `@candid-core/cli` 0.4.0 changes names, and the first two
55
- lines of every module.** Install `@meantjs/cli` and `@meantjs/schema` in
56
- place of the old pair, replace `candid-core-cli` with `meant` in your
57
- scripts, and replace `@candid-core/cli` with `@meantjs/cli` where you import
58
- the library. Then run `meant gen` again over every committed module: each
59
- module's header line becomes
60
- `// Generated by Meant from a Meant Contract. Do not edit.` and its import
61
- `import * as $ from "@meantjs/schema";`, and nothing else in it changes. Until
62
- then `meant gen --check` reports each module 0.4.0 wrote as drifted, exit 1.
63
- The envelopes do not change. Measured over the repository's 44 tracked
64
- `.did` files: both releases compile the same 40 and refuse the other 4 with
65
- the same diagnostics, the 40 envelopes are byte-identical, and the 40 modules
66
- differ in those two lines alone.
67
-
68
- **What keeps the old name.** The strings inside Contract documents are not
69
- package names, and this release changes none of them: the `format`
70
- `candid-core`, the canonical form `candid-core-canon-1`, the
71
- `candid-core:contract:v1:sha256:…` and `candid-core:interface:v1:sha256:…`
72
- identities that `gen`, `project` and `check` print, and the
73
- `org.candid-core.field-names/v1` extension of every envelope. The two
74
- identity prefixes are hashed into every identity, so renaming them would
75
- move every `contract_id` and `interface_id`. The `--json` documents keep
76
- `schemaVersion` 1.
77
-
78
- ### Changed
79
-
80
- - **BREAKING: the package is `@meantjs/cli`, and its command is `meant`.**
81
- The package installs one bin, `meant`, where it installed
82
- `candid-core-cli`, and the usage text names it:
83
- `usage: meant gen <service.did>... [-o <dir>] [--json] [--check]`, with
84
- `meant project` and `meant check` on the lines below. The grammar, the
85
- exit codes and the `--json` documents are 0.4.0's. Import the library from
86
- `@meantjs/cli`; its exports and their declarations are 0.4.0's.
87
- - **BREAKING: generated modules import `@meantjs/schema`**, and their header
88
- line is `// Generated by Meant from a Meant Contract. Do not edit.`; it was
89
- `// Generated by candid-core-ts from a candid-core Contract. Do not edit.`
90
- `didToModule` returns the same text. A module's other lines are unchanged.
91
- - The optional peer is `@meantjs/schema` at exactly `0.6.0`, where it was
92
- `@candid-core/schema` at exactly `0.5.0`.
93
- - `repository` and `homepage` in `package.json` name
94
- `https://github.com/B3Pay/Meant`, where the repository moved on
95
- 2026-10-28; GitHub redirects the old address.
96
-
97
- ## 0.4.0 — 2026-10-27
98
-
99
- Embeds `meant` 0.1.0 and the `candid-core-ts` generator from the repository
100
- commit the release is dispatched from; the release record names the exact
101
- SHA. `meant` is the compiler crate formerly published as `candid-core`, and
102
- `meant` 0.1.0 is `candid-core` 0.1.0's code under the new name: the crate's
103
- first version without a prerelease suffix. The dispatched commit's `meant`
104
- source is ahead of the crates.io 0.1.0 archive by one change, the reader of
105
- doc attributes behind `didToDocAttributes` (below), which moves no Contract
106
- and no identity, so no crate release comes first.
107
-
108
- A stable release, published under the npm `latest` dist-tag, so a plain
109
- `npx @candid-core/cli` runs it; keep it pinned,
110
- `npm install --save-dev --save-exact @candid-core/cli`. A `^0.3.0` range does
111
- not admit it. The `beta` dist-tag stays on 0.2.0-beta.1. The optional peer is
112
- `@candid-core/schema` at exactly `0.5.0` (exactly `0.4.0` in 0.3.0), and npm
113
- refuses a mismatched pair (`ERESOLVE could not resolve`), so move the two
114
- packages together.
115
-
116
- Against the compiler 0.3.0 embeds (`candid-core` 0.1.0-beta.3 and five later
117
- changes), three things change. A record field whose id is 4294967295, written
118
- as that number or as a name that hashes to it, is refused with
119
- `did_parse_error`; 0.3.0's compiler numbered the field after it 0. Collecting
120
- the provenance sidecar no longer grows with the square of the declarations,
121
- so a large `.did` file compiles in time proportional to its size. And every
122
- envelope `gen` writes names `meant` 0.1.0 as its producer, where 0.3.0's
123
- named `candid-core` 0.1.0-beta.3, so `gen --check` reports each envelope
124
- 0.3.0 wrote as drifted, exit 1, until `gen` runs again. Every other input
125
- compiles to the same Contract and identities: of the repository's 44 tracked
126
- `.did` files, the 40 that both releases compile give envelopes that differ in
127
- the `producer` alone, and the 4 that 0.3.0 refuses this release refuses with
128
- the same diagnostics.
129
-
130
- ### Added
131
-
132
- - **`didToDocAttributes` reads `@name(…)` attributes in doc comments.** It
133
- compiles the sources and returns `{ ok: true, contract, source_info,
134
- doc_attributes }`: the Contract and the SourceInfo sidecar
135
- `meant compile <path>` prints, which no function of this package
136
- exposed before, and every documented item's prose and attributes keyed by
137
- its SourceInfo path, as `docs/doc-attributes.md` specifies (doc attributes
138
- v1). A malformed attribute fails the call with `doc_attribute_malformed`
139
- or `doc_attribute_duplicate_key`. The declarations add `DocAttributes`,
140
- `DocItem`, `DocAttribute`, `DocArgument`, `DocValue`, `SourceOrigin` and
141
- `DocAttributesSuccess`. The other functions are unchanged.
142
- - **`gen --json` reports each entry's identities.** Every entry that compiled
143
- (`written`, `unchanged` or `drifted`, with or without `--check`) carries
144
- `identities: { contract_id, interface_id }`, equal to its envelope's
145
- `contract.identities` and to the lines the human report prints, under the
146
- key names `project --json` and `check --json` already use. `interface_id`
147
- is absent when the `.did` has no actor; a `failed` entry has no
148
- `identities`. The declarations add `CliIdentities` and the optional
149
- `CliEntryReport.identities`; `Identities` is unchanged, its `interface_id`
150
- still required. `schemaVersion` stays `1`, since adding a field does not
151
- change it. A build step no longer has to parse the envelope to learn the
152
- interface identity.
153
-
154
- ### Generated modules
155
-
156
- - **BREAKING**: a declared `blob`, `record {}` or `variant {}` names only
157
- itself. Each of the three has one fixed rendering (`Uint8Array` /
158
- `$.c.blob()`, `Record<string, never>` / `$.c.unit()`, `never` /
159
- `$.c.variant({})`), and every use now renders it, as every use of a
160
- primitive already did. Before, the alias of that shape whose name sorted
161
- first, wherever it sat in the file, named every use of it: beside
162
- `type Hash = blob`, a field written `memo : opt blob` read `Hash | null`
163
- and its builder was `$.c.opt($Hash)`. Now it reads `Uint8Array | null`
164
- with `$.c.opt($.c.blob())`, and `type Subaccount = blob` is emitted as
165
- `export type Subaccount = Uint8Array`, so a field written
166
- `subaccount : opt Subaccount` reads `Uint8Array | null` too. Value types,
167
- wire bytes and envelopes do not change for it, and every declaration is still
168
- exported under its name. A module with two aliases of one of these shapes,
169
- or one alias and a plain use, changes text, so `gen --check` reports it as
170
- drifted until `gen` runs again. Measured on the repository's 44 tracked
171
- `.did` files against 0.3.0: of the 40 that generate, three modules change,
172
- `shadowing.did`'s and the two fixtures added for this change. A
173
- declaration of one of these shapes named after an export of the module
174
- (`actor`) is omitted on its own and no longer takes the other uses of the
175
- shape with it; `@candid-core/schema` 0.5.0's `schemaFromContract` applies
176
- the same rule.
177
-
178
- ## 0.3.0 — 2026-10-10
179
-
180
- Embeds `candid-core` 0.1.0-beta.3 and the `candid-core-ts` generator from the
181
- same repository commit the release is dispatched from; the release record
182
- names the exact SHA. That commit's `candid-core` source is ahead of the
183
- 0.1.0-beta.3 archive on crates.io by five changes not yet in a crate release:
184
- the four 0.2.0 already carried (a `.did` source may begin with one UTF-8 byte
185
- order mark; a run of more than 256 consecutive comments is refused; a type on
186
- a cycle that passes only through `opt` is refused; the crate's Contract JSON
187
- loaders refuse a struct written as a JSON array) and one more, the crate's
188
- Contract JSON loaders refuse a unit variant written as a JSON map
189
- (`"primitive": {"nat": null}` for `"primitive": "nat"`), which beta.3's
190
- loaders accept. No input of this package reaches the last two: `gen`,
191
- `project`, `check` and the four library functions read `.did` sources and
192
- never load a Contract document. The embedded compiler therefore compiles
193
- every `.did` as 0.2.0's does: every input both accept compiles to the same
194
- Contract and identities, every envelope `gen` writes is byte-identical to
195
- 0.2.0's, and envelopes still name 0.1.0-beta.3 as their producer.
196
-
197
- A stable release, published under the npm `latest` dist-tag, so a plain
198
- `npx @candid-core/cli` runs it; pin it in a project,
199
- `npm install --save-dev --save-exact @candid-core/cli`. The `beta` dist-tag
200
- stays on 0.2.0-beta.1.
201
-
202
- **It pairs with exactly one schema release.** The optional peer is exactly
203
- `@candid-core/schema` `0.4.0` (exactly `0.3.0` in 0.2.0). Each method of the
204
- `Actor` a generated module declares is typed with `$.WithMode`, which
205
- `@candid-core/schema` 0.4.0 adds, so a module with an actor method does not
206
- compile against 0.3.0 (`TS2694`, "has no exported member 'WithMode'", once
207
- per method). npm refuses to install a mismatched pair (`ERESOLVE could not
208
- resolve`). Install the pair at exact versions, together.
209
-
210
- **Upgrading from 0.2.0.** Two changes break something, each marked
211
- **BREAKING** below, and both are about generated modules: the peer moves to
212
- `@candid-core/schema` 0.4.0, which a module with an actor method needs; and
213
- the text of every generated module with a declaration or an actor changes,
214
- so `gen --check` reports each module 0.2.0 wrote as drifted (exit 1) until
215
- you run `gen` again. Upgrade both packages in one step and regenerate.
216
- Envelopes do not change. Code that imports the generated names keeps
217
- compiling, with one exception: type-level code that compares a method type
218
- of `Actor` for exact equality, such as an `Equal<A, B>` test helper, now
219
- sees the `WithMode` intersection. The commands `project` and `check`, the
220
- library functions `projectDid` and `checkCompatible`, and their types are
221
- additive; `gen`, `didToContract` and `didToModule` keep their grammar,
222
- signatures and results but for the module text.
223
-
224
- ### Added
225
-
226
- - **`candid-core-cli project <in.did> --methods <a,b,...> -o <out.did>`**
227
- writes a `.did` holding only the methods named and every declaration they
228
- reach, as one self-contained file, and prints the interface identities of
229
- the input and of the projection. The output is deterministic: the same
230
- input and the same set of names give the same bytes, in any order. It
231
- keeps declaration names, doc comments (a tuple element's included: a
232
- tuple with a documented element is written one element per line) and
233
- argument names, so `gen` on the projection emits the same declarations,
234
- docs and modes as on the full interface, with an `Actor` that lists only
235
- the projected methods. A service class's init arguments are dropped. An
236
- unknown method name fails with `unknown_method`, one per distinct name,
237
- the first listing the service's methods in its `notes`, once; an empty
238
- list (`--methods ""`) with `empty_method_list`, a source with no service
239
- with `no_service`; each exits 1 and writes nothing. The unknown-method
240
- diagnostics are bounded at 4 MiB in all, each counted as the JSON writes
241
- it (the list of methods aside), the names past that bound counted in one
242
- `resource_limit_exceeded` diagnostic (`projection_diagnostic_bytes`)
243
- instead of reported. A projection is at
244
- most 1 MiB, the compiler's bound on one source, since it is compiled
245
- again: a larger one fails with `resource_limit_exceeded`
246
- (`projection_bytes`), exit 1, as soon as writing it reaches that bound.
247
- Every name, requested or reached, is found by binary search among names
248
- sorted once. Methods are written in name order, a service import's among
249
- the entry's. `-o` is required, and may not be, or become, a source of the
250
- input (the entry or any `.did` beneath its directory, which `project` reads
251
- as `gen` does): naming one of those files by any path, symlink or hard
252
- link, or a new `.did` beneath that directory, fails with
253
- `output_is_input`, exit 1, and writes nothing. An output that cannot be
254
- read or written fails with `output_write_failed` (its `notes` hold the
255
- system error code), exit 1; an existing output of another size is
256
- replaced without being read. `--json` prints one document instead of the
257
- report, and `{ ok: false, diagnostics }` for every failure.
258
- - **`candid-core-cli check <written.did> --against <live.did>`** exits 0
259
- when the live interface is still a Candid subtype of the written one, and
260
- 1 otherwise: every written method must exist in the live service with the
261
- same mode, its arguments contravariant and its results covariant, and
262
- methods only the live service has are ignored. Each finding names its
263
- method and a stable code (`method_missing`, `mode_changed`,
264
- `method_incompatible`, the warning `special_opt_rule`, and
265
- `resource_limit_exceeded`) and, inside a type, the path where the check
266
- failed. A `special_opt_rule` warning is reported at every path that
267
- decodes as `null` along which no pair of types repeats, a shared type
268
- under each path that reaches it. Past 1,000 warnings a method reports the
269
- first 1,000 and a `resource_limit_exceeded` warning; its verdict stands.
270
- A method whose check reaches its depth or work bound fails closed; work
271
- is counted in steps, each one unit of work done (a pair of types visited,
272
- a field, arm, method or value examined or passed over, a path segment a
273
- warning or a failure copies or a warning re-examines). A step reads no
274
- name: service methods are paired by ranks computed once per check and a
275
- field's name is read by its position, so a step's cost does not grow with
276
- the length of the names: it does a bounded amount of work, and its
277
- lookups in the maps of type pairs the walk keeps take time logarithmic in
278
- their size. The
279
- check as a whole is bounded too, so many methods sharing one type cannot
280
- multiply the per-method bounds: 10,000,000 steps (one more per method),
281
- 10,000 `special_opt_rule` warnings and 4 MiB of reported diagnostics, each
282
- counted as the JSON response writes it, keys and indentation included, in
283
- all (`check_total_steps`, `check_total_warnings`, `check_output_bytes`);
284
- a diagnostic is measured only up to the first string that passes the byte
285
- bound, so long names cannot make the measuring itself slow. So the walk's
286
- time grows with its steps (times that logarithm) and the reported
287
- diagnostics' with their bytes; compiling both sides, within the
288
- compiler's own limits, and ranking the method names are done once per
289
- check, before the walk. Methods are checked in name order; when the step
290
- or warning bound is reached, that method and every method after it get
291
- one `resource_limit_exceeded` error each and nothing else. Those count
292
- against the 4 MiB like any other diagnostic, and once a method's
293
- diagnostics would pass it, that method and every method after it are
294
- counted in one last `resource_limit_exceeded` error
295
- (`check_output_bytes`), which names no method and holds the count in
296
- `unreported_methods`. Either way the check exits 1, and a response is at
297
- most 4 MiB and 1,016 bytes, however many methods the written side has.
298
- Below these bounds a method's findings do not depend on the other
299
- methods. The report prints the live interface identity, so an unchanged
300
- interface can be told from one that changed compatibly.
301
- - **`projectDid(sources, methods)` and `checkCompatible(written, live)`**,
302
- the library functions behind the two commands, exported beside
303
- `didToContract` and `didToModule`, with their types (`ProjectionSuccess`,
304
- `CompatibilityReport`, `CompatibilityDiagnostic`, whose `method` is absent
305
- only on the `check_output_bytes` diagnostic and whose
306
- `unreported_methods` is present only there, `CheckFailure`,
307
- `Identities`). Neither fetches anything; the package still has no runtime
308
- dependencies.
309
-
310
- ### Changed
311
-
312
- - **The usage text lists the three commands.** A usage error still exits 64
313
- with the usage on stderr and nothing on stdout; its text now has three
314
- lines, one per command.
315
-
316
- ### Generated modules
317
-
318
- - **Compiler errors name the types you import.** A generated module now
319
- declares each declaration's type under its Candid name and keeps the
320
- `$`-prefixed local for the schema value only, so an app's TypeScript errors
321
- read `Account` and `Actor`, the names it imports, where they read
322
- `$Account` and `$Actor`:
323
-
324
- ```ts
325
- export type Account = { owner: $.Principal; subaccount: Uint8Array | null };
326
- const $Account: $.Schema<Account> = $.c.rec(() => $.c.record({ … }));
327
- export { $Account as Account };
328
- ```
329
-
330
- Every export name, and what each one is as a type and as a value, is
331
- unchanged, and so is every module's set of declarations. A name that
332
- cannot be a type in the module keeps the `$` local for its type too: a
333
- global type the generated types use (`Array`, `Record`, `Uint8Array`,
334
- `Promise`), and a word TypeScript refuses as a type's name (`delete`,
335
- `string`, `default`, `keyof`, `readonly`, …). So `type $Array = …` still
336
- appears, and errors about it still name `$Array`. Code that imports the
337
- generated names is unaffected; code that parses or patches the generated
338
- source must follow the new text.
339
- - **BREAKING**: the text of every generated module with a declaration or an
340
- actor changes, by the item above and the one below, so `gen --check`
341
- against a module 0.2.0 wrote reports it as drifted, exit 1, until `gen`
342
- runs again. Measured on the repository's 41 tracked `.did` files: the 37
343
- that generate give a different module and a byte-identical envelope.
344
- - **Each `Actor` method carries its mode.** `gen` and `didToModule` now emit
345
- each method of `Actor` as its call signature intersected with
346
- `$.WithMode<mode>` (`"query"`, `"composite_query"`, `"update"` or
347
- `"oneway"`), which `@candid-core/schema`'s `ModeOf` reads back, so a call
348
- layer can tell a query from an update at compile time:
349
-
350
- ```ts
351
- export type Actor = {
352
- icrc1_balance_of: ((arg0: Account) => Promise<bigint>) & $.WithMode<"query">;
353
- icrc1_transfer: ((arg0: TransferArg) => Promise<TransferResult>) & $.WithMode<"update">;
354
- };
355
- ```
356
-
357
- The call signatures, the method names and the module's exports are
358
- unchanged, and an actor with no methods still generates an empty `Actor`.
359
- Type-level code that compares a method type for exact equality sees the
360
- intersection; `Parameters<>` and `ReturnType<>` do not.
361
- - **BREAKING**: `WithMode` is new in `@candid-core/schema` 0.4.0, so a
362
- module with an actor method needs that release: against 0.3.0 it fails to
363
- compile with `TS2694` ("has no exported member 'WithMode'"), once per
364
- method. The peer moves to exactly `0.4.0` for this reason.
365
-
366
- ## 0.2.0 — 2026-10-06
367
-
368
- Embeds `candid-core` 0.1.0-beta.3 and the `candid-core-ts` generator from the
369
- same repository commit the release is dispatched from; the release record
370
- names the exact SHA. That commit's `candid-core` source is ahead of the
371
- 0.1.0-beta.3 archive on crates.io by four changes not yet in a crate release:
372
- a `.did` source may begin with one UTF-8 byte order mark, which beta.3 refuses
373
- with "Unknown token"; a run of more than 256 consecutive comments between two
374
- tokens is refused with `resource_limit_exceeded` (below), which beta.3
375
- compiles up to a stack-dependent length and then aborts on; a type on a cycle
376
- that passes only through `opt` is refused with `did_type_check_error`
377
- (below), which beta.3 compiles; and the crate's Contract JSON loaders refuse
378
- a struct written as a JSON array, which beta.3's loaders accept. No input of
379
- this package reaches the last one: `gen`, `didToContract` and `didToModule`
380
- read `.did` sources and never load a Contract document. Every input both
381
- accept compiles to the same Contract and identities, and envelopes still name
382
- 0.1.0-beta.3 as their producer.
383
-
384
- `@candid-core/schema` 0.3 breaks the 0.2 API (principal values are canonical text, collapsing opts are boxed, every `vec nat8` is a `Uint8Array`, generated modules use a new binding layout and may omit declarations, and the `./actor`, `./transport-icp`, `./forms` and `./labels` subpaths are gone); there is no compatibility layer.
385
-
386
- The first stable release of the 0.2 line. It is published under the npm
387
- `latest` dist-tag, so a plain `npx @candid-core/cli` runs it; pin it in a
388
- project, `npm install --save-dev --save-exact @candid-core/cli`. The `beta`
389
- dist-tag stays on 0.2.0-beta.1.
390
-
391
- **It pairs with exactly one schema release.** The optional peer is exactly
392
- `@candid-core/schema` `0.3.0` (`^0.2.0` in 0.1.0, exactly `0.3.0-beta.1` in
393
- 0.2.0-beta.1). The modules this generator emits need that release's
394
- `Principal` export and boxed options, which 0.2.0 does not have, and the
395
- generator and that release's `schemaFromContract` agree on blobs and on
396
- omitted declarations only when both come from the same release. npm refuses
397
- to install a mismatched pair (measured with npm 11.3.0: `ERESOLVE could not
398
- resolve`, exit status 1); the 0.2.0-beta.1 entry below says npm only warns,
399
- which was wrong for a published peer. Install the pair at exact versions.
400
-
401
- **Upgrading from 0.1.0.** 0.2.0 is 0.2.0-beta.1 plus the changes in this
402
- entry, so the upgrade is this entry and the 0.2.0-beta.1 entry below, read
403
- together. Every break in either is marked **BREAKING**: from 0.2.0-beta.1, the
404
- `$` binding layout, `$.Principal`, boxed collapsing opts, a declared primitive
405
- naming only itself and every `vec nat8` typed `Uint8Array`, and omission in
406
- place of refusal (`gen`'s exit status and `didToModule`'s result); from this
407
- entry, the refusals of a long comment run and of a type on a cycle through
408
- `opt` alone. JSDoc from `.did` doc comments and
409
- `gen`'s several entries, `--json` and `--check` are additive. The library's
410
- three functions keep their names and signatures, and `lib/index.d.ts` and the
411
- command grammar are unchanged since 0.2.0-beta.1.
412
-
413
- ### A long run of comments is refused with a resource diagnostic
414
-
415
- The embedded compiler now refuses a `.did` file with more than 256 consecutive
416
- comments between two tokens. The upstream Candid tokenizer it embeds spends one
417
- stack frame on each comment it skips, so the compiler counts the run first,
418
- without recursing, and refuses an over-long one before that tokenizer runs.
419
- Before, a long enough run exhausted the WebAssembly stack: measured with the
420
- published tarballs (Node 24.2, macOS arm64), 0.2.0-beta.1's `gen` compiled a
421
- file with 2 728 consecutive `//` lines and failed at 2 729 with
422
- `internal_error` "Maximum call stack size exceeded", and 0.1.0's compiled
423
- 2 825 and crashed at 2 826 with an uncaught `RangeError`; the threshold moves
424
- with the toolchain and the engine.
425
-
426
- - **BREAKING (input)**: this refuses files that `gen` accepted before: any
427
- `.did` in the bundle with a run of 257 to about 2 700 or 2 800 comments,
428
- such as a license header written as `//` lines. `gen`, `didToContract` and
429
- `didToModule` report `resource_limit_exceeded` with resource
430
- `source_nesting`, limit 256 and `observed` 257, through the existing
431
- diagnostic path (the `{ ok: false, diagnostics }` document, or `--json`)
432
- with exit status 1. A longer run, which failed with `internal_error` before
433
- (an uncaught `RangeError` in 0.1.0), gets the same diagnostic. The CLI
434
- compiles at the compiler's default limits and has no flag to raise this one:
435
- write such a header as a single `/* */` block, which counts as one comment,
436
- or shorten it. Line and block comments count alike;
437
- blank lines between comments do not end a run, any token does, a nested
438
- block comment counts once, and comment markers inside a string are not
439
- comments.
440
- - **Nothing else moves.** A file within the limit generates the same module and
441
- the same envelope, with the same identities and documentation, as before.
442
-
443
- ### The embedded generator renders on an explicit stack
444
-
445
- - The embedded `candid-core-ts` generator keeps its pending work on an explicit
446
- stack instead of recursing once per type level, so generation uses constant
447
- call-stack depth in the Contract's nesting. It also refuses a Contract with a
448
- cycle that passes through no declared node, which a TypeScript type alias
449
- cannot spell. No `.did` source produces such a Contract (a recursive Candid
450
- type needs a name), so no input `gen` accepts is affected: every module it
451
- writes is byte-identical to what 0.2.0-beta.1 writes for the same input, and
452
- `didToModule`, which takes `.did` sources, returns the same result.
453
-
454
- ### A type on a cycle through `opt` alone is refused
455
-
456
- The embedded compiler now refuses a type that lies on a cycle passing only
457
- through `opt`: `type T = opt T;`, `type T = opt opt T;`,
458
- `type A = opt B; type B = opt A;`, or the same through an alias. Decoding a
459
- value that is not an `opt` at such a type unwraps `opt` without end, so no
460
- decoder can answer for it; a generated schema for it ended in a depth refusal.
461
-
462
- - **BREAKING (input)**: this refuses files that `gen` accepted before:
463
- `gen`, `didToContract` and `didToModule` report `did_type_check_error`,
464
- naming the type ("type T lies on a cycle that passes only through opt;
465
- …"), through the existing diagnostic path (the `{ ok: false,
466
- diagnostics }` document, or `--json`) with exit status 1, and write nothing
467
- for that entry. The upstream Candid checker accepts these files; the
468
- refusal is candid-core's own. 0.1.0 refused them too, with
469
- `ts_generation_refused` at generation, because each such type wraps `opt`
470
- in `opt`; 0.2.0-beta.1 generates them, boxed.
471
- - **A cycle with any other constructor on it still compiles**: `type L = opt
472
- record { head : nat; tail : L }`, `type T = opt vec T`, `type T = record {
473
- a : opt T }`, `type T = opt variant { a : T }`.
474
- - **Nothing else moves.** Every other file generates the same module and the
475
- same envelope, with the same identities and documentation, as before.
476
-
477
- ## 0.2.0-beta.1 — 2026-10-02
478
-
479
- Embeds `candid-core` 0.1.0-beta.3 and the `candid-core-ts` generator from the
480
- same repository commit the release is dispatched from; the release record
481
- names the exact SHA. That commit's `candid-core` source is ahead of the
482
- 0.1.0-beta.3 archive on crates.io by one change not yet in a crate release: a
483
- `.did` source may begin with one UTF-8 byte order mark, which beta.3 refuses
484
- with "Unknown token". Every input beta.3 accepts compiles to the same Contract
485
- and identities, and envelopes still name 0.1.0-beta.3 as their producer.
486
-
487
- `@candid-core/schema` 0.3 betas break the 0.2 API (principal values are canonical text, collapsing opts are boxed, every `vec nat8` is a `Uint8Array`, generated modules use a new binding layout and may omit declarations, and the `./actor`, `./transport-icp`, `./forms` and `./labels` subpaths are gone); there is no compatibility layer.
488
-
489
- The first beta of the 0.2 line, and the first prerelease of this package. It is
490
- published under the npm `beta` dist-tag, so `latest` stays on 0.1.0; ask for it
491
- by its exact version, `npm install --save-exact @candid-core/cli@0.2.0-beta.1`,
492
- or run it as `npx @candid-core/cli@0.2.0-beta.1`.
493
-
494
- **It pairs with exactly one schema release.** The optional peer moves from
495
- `@candid-core/schema` `^0.2.0` to exactly `0.3.0-beta.1`. The modules this
496
- generator emits need that release's `Principal` export and boxed options, which
497
- 0.2.0 does not have, and the generator and that release's `schemaFromContract`
498
- agree on blobs and on omitted declarations only when both come from the same
499
- release. While the two packages move in lockstep, every `@candid-core/schema`
500
- beta is paired with a new beta of this package that raises the exact peer.
501
- Because the peer is optional, npm does not refuse a mismatched pair: it warns
502
- `ERESOLVE overriding peer dependency` and installs anyway, and the result does
503
- not type-check. Install the pair at exact versions.
504
-
505
- Every break below is marked **BREAKING**, ordered for a reader upgrading from
506
- 0.1.0: what the emitted module looks like, then what it emits for interfaces it
507
- used to refuse, then the command line. The library's three functions keep
508
- their names and signatures.
509
-
510
- ### Generated modules bind `$`-prefixed locals
511
-
512
- The embedded generator changes the layout of every module it emits. A module
513
- imports the schema runtime as a namespace and binds each declaration as a local
514
- whose name starts with `$`, exported under its Candid name:
515
-
516
- ```ts
517
- import * as $ from "@candid-core/schema";
518
-
519
- type $Tokens = { e8s: bigint };
520
- const $Tokens: $.Schema<$Tokens> = $.c.rec(() => $.c.record({ e8s: $.c.nat64 }));
521
- export { $Tokens as Tokens };
522
- ```
523
-
524
- - **BREAKING**: the text of every generated module changes. `import
525
- { Tokens, actor, type Actor } from "./ledger"` resolves exactly as before,
526
- so code that imports the generated names is unaffected by the layout;
527
- code that parses or patches the generated source, or relied on its
528
- module-scope names (`export type X`, `export const X`, the `c` and `Schema`
529
- bindings, the separate `import type { PrincipalValue }` line), must follow
530
- the new layout.
531
- - **BREAKING**: declarations the generator used to refuse with
532
- `ts_generation_refused` now generate: names that collided with the module's
533
- own bindings (`c`, `Schema`, `PrincipalValue`), with the global types its
534
- lowerings use (`Array`, `Record`, `Uint8Array`, `Promise`), or that are
535
- TypeScript reserved words (`delete`, `string`). Import them by name,
536
- renaming where needed (`import { delete as del } from "./service"`). A
537
- declaration named `default` becomes the module's default export.
538
- - Declarations named `actor` or `Actor` stay reserved: those are the
539
- module's own export names. They are omitted from the module rather than
540
- refusing it; see "Unrepresentable declarations are omitted, not refused".
541
-
542
- ### Generated modules type principals as `$.Principal`
543
-
544
- - **BREAKING**: the embedded generator emits `$.Principal` wherever it
545
- emitted `$.PrincipalValue`: principal fields, func reference aliases
546
- (`{ principal: $.Principal; method: string }`), service aliases, and the
547
- `actor` schema (`$.Schema<$.Principal>`). The paired `@candid-core/schema`
548
- release replaces `PrincipalValue` (`{ toText(): string }`) with
549
- `Principal`, canonical principal text as a branded string: a decoded
550
- principal is that text, and encoding accepts only it, so an SDK `Principal`
551
- converts once with `principal(sdkPrincipal)`.
552
- - A declaration named `Principal` still generates, bound as `$Principal` and
553
- exported as `Principal`.
554
-
555
- ### Options whose inner type admits `null` are boxed
556
-
557
- - **BREAKING**: the embedded `candid-core-ts` generator no longer refuses an
558
- `opt` whose inner type is another `opt`, `null`, or `reserved` (its
559
- `TsGenError::UnrepresentableOption` is removed). Such declarations now
560
- generate, with the present value boxed: `opt opt nat` emits
561
- `{ some: bigint | null } | null` with builder `$.c.opt($.c.opt($.c.nat))`.
562
- Interfaces without such an `opt` are unaffected by this change.
563
-
564
- ### A declared primitive names only itself
565
-
566
- - **BREAKING**: the embedded generator no longer renders a use of a primitive
567
- by a declaration's name. The Contract arena shares one node per structure, so
568
- a name recorded for `nat64` was rendered at *every* `nat64` in the interface,
569
- including ones that never wrote it. A use of a primitive — a field, an array
570
- element, a variant arm, a method argument — now renders structurally
571
- (`bigint`, `$.c.nat64`, and `$.Principal` for `principal`); a declaration of
572
- the primitive is still emitted and exported as itself. `type Memo = nat64;
573
- type R = record { a : nat64; b : Memo }` now generates `R = { a: bigint; b:
574
- bigint }` where it generated `{ a: Memo; b: Memo }`, and the ICRC-1
575
- ledger's `TransferArg.amount`, written `Tokens` beside `type BlockIndex =
576
- nat`, is `bigint` and no longer `BlockIndex`.
577
- - **BREAKING (value type)**: every `vec nat8` is `Uint8Array` / `$.c.blob()`,
578
- whatever its element type is called. Before, a single `type Byte = nat8`
579
- anywhere in the interface turned every `blob` and `vec Byte` into
580
- `Array<Byte>`, a `number[]`. For an interface with a declared `nat8` alias
581
- the generated blob types change from `number[]` to `Uint8Array`; the encoded
582
- bytes do not change. The paired `@candid-core/schema` release makes
583
- `schemaFromContract` load every `vec nat8` as a blob too, so a generated
584
- module and a loaded schema agree.
585
- - **Trade-off**: a declared alias no longer survives as the *spelling* of a
586
- field's type (`amount : Tokens` reads `amount: bigint`); the type is the
587
- same. Two structurally equal *record* declarations still collapse to the
588
- first name, as before.
589
-
590
- ### `.did` doc comments become JSDoc
591
-
592
- - The embedded generator writes each `.did` doc comment as JSDoc: above the
593
- generated type and const of a declaration, on a record property, on a
594
- variant arm's `tag`, on a method of the `Actor` type, and on the `actor`
595
- export. Docs are the `///` run (or plain `//` lines) directly above the
596
- item; block comments are not docs. The doc text is escaped — `*/` becomes
597
- `*\/`, an `@` that could start a tag becomes `\@`, a code fence has each
598
- backtick escaped — so a comment cannot end early, forge a tag or swallow the
599
- generated ones.
600
- - A method's `@param` tags name the `.did`'s argument names, and the `Actor`
601
- method's parameters take the same names; an unnamed argument, a reserved
602
- word, a name that is not identifier-shaped or a collision stays `arg{n}`.
603
- - A record or union with a documented member spans several lines; without
604
- docs its one-line form is unchanged. Tuple elements carry no docs.
605
- - Which occurrence documents a node the arena has de-duplicated is one rule:
606
- the declaration (or actor) whose structure is being emitted; occurrences
607
- inside one declaration that disagree are dropped. No Contract, envelope,
608
- identity or wire byte changes — docs are provenance — and the
609
- `org.candid-core.field-names/v1` extension is untouched.
610
- - **Additive**, with one naming change: a method written `a : (x : nat) -> ()`
611
- now takes `x` as its parameter name where it took `arg0`, and gains an
612
- `@param x` block, whether or not the file has a single comment. Types,
613
- values and wire bytes do not move either way.
614
-
615
- ### Unrepresentable declarations are omitted, not refused
616
-
617
- The embedded generator leaves out a declaration it cannot represent, with
618
- everything that references it, instead of refusing the whole interface. One
619
- exotic declaration no longer costs the module. The paired
620
- `@candid-core/schema` release makes `schemaFromContract` leave out the same
621
- declarations and methods, with the same reasons, so a generated module and a
622
- loaded contract agree.
623
-
624
- - **BREAKING (exit status)**: `candid-core-cli gen` used to print a
625
- `ts_generation_refused` diagnostics document and exit 1 for an interface
626
- with a record field or variant arm named like the `_N_` numeric-id
627
- rendering (`record { _0_ : nat }`), a variant arm whose payload is a
628
- declared `opt` of an uninhabited type (`type W = opt empty; type V =
629
- variant { a : W }`), or a declaration named `actor` or `Actor`. It now
630
- writes the module without those declarations and exits 0, printing one
631
- `warning: omitted …` line per omission on stderr. A script that relied on
632
- the non-zero exit to reject such an interface must read the warnings, or
633
- `omitted`, instead. Stdout's report is unchanged.
634
- - **BREAKING**: `didToModule` returns `{ ok: true, module, omitted }` for
635
- those inputs where it returned `{ ok: false, diagnostics }`. No Candid
636
- source reaches `ts_generation_refused` any more; the code stays, reserved
637
- for an invalid contract graph.
638
- - **The closure**: an omitted declaration takes with it every declaration
639
- that references it through any edge — a field, an alias, `vec`/`opt`/record
640
- nesting, a `func` type's arguments or results, a `service` type's methods —
641
- up to the containing declaration. A record holding a
642
- `service { f : (Bad) -> () }` is omitted whole, not stripped of `f`: a
643
- service value's wire type is its full method table, and a peer would see a
644
- different type. Only the actor drops individual methods, from both the
645
- `actor` schema and the `Actor` type, since calling a method never encodes
646
- the actor's own service type. The actor itself is never omitted, and a class
647
- actor whose init arguments reference an omitted declaration keeps every
648
- method (init arguments are not generated).
649
- - **Everything kept is unchanged**: the modules for the other golden
650
- interfaces are unchanged by omission, and a test pins that, for an interface
651
- with omissions, every emitted declaration, `Actor` signature and actor
652
- method is byte-identical to the module generated with the omitted
653
- declarations deleted from the source.
654
- - **`ModuleSuccess.omitted`** (new, always present, empty when nothing is
655
- omitted): an array of `{ kind, name, reason, via? }`, declarations first,
656
- then methods, each sorted by name. `kind` is `"declaration"` or
657
- `"method"`. `reason` is one of `reserved_field_name`,
658
- `ambiguous_variant_arm`, `reserved_export_name`, `invalid_declaration_name`
659
- (Contract documents only; Candid source cannot produce one) or
660
- `references_omitted`; `via` is present only for `references_omitted` and
661
- names the omitted declaration referenced. This is a serialized shape: the
662
- set of `reason` codes is closed, and a new code is a breaking change. The
663
- type is exported by name, as `Omission`.
664
- - **The module header** lists the same entries in the same order, one line
665
- each, directly under the first line:
666
- `// Omitted: type Holder (references_omitted via Bad)`. A module that omits
667
- nothing has no such line.
668
-
669
- ### `gen` takes several entries, and gains `--json` and `--check`
670
-
671
- - `candid-core-cli gen <a.did> [<b.did> …] [-o <dir>] [--json] [--check]`.
672
- Each entry generates its own `<stem>.ts` and `<stem>.envelope.json` into the
673
- one `-o` directory, byte-for-byte what a run of that entry alone writes. An
674
- entry's bundle is the `.did` files beneath its own directory, with the bundle
675
- limits applied per entry; entries sharing a directory share one read of it.
676
- Two entries with the same stem (compared case-insensitively) are a usage
677
- error (exit 64) before any work. Both rules may still change before 1.0.
678
- - A failing entry no longer ends the run: every entry is attempted, and the
679
- exit code is 1 if any failed, 0 otherwise. Omissions do not fail an entry.
680
- For one entry the behaviour is unchanged: the same `{ ok: false,
681
- diagnostics }` document on stdout and exit 1.
682
- - **`--json`** prints exactly one JSON document on stdout and nothing else
683
- (stderr stays empty): `{ schemaVersion, ok, check, entries, drift }`, with
684
- per entry its `entry`, `status` (`written`, `unchanged`, `drifted` or
685
- `failed`), `module` and `envelope` paths, `omitted`, and `diagnostics`. The
686
- shape is documented in the README and typed as `CliReport` in
687
- `lib/index.d.ts`; `schemaVersion` is `1`. The content-addressed identities
688
- stay in the human report only.
689
- - **`--check`** generates in memory, compares byte-for-byte with the files on
690
- disk, writes nothing (not even the output directory) and exits 1 listing
691
- each missing or drifted file; it combines with `--json` (`drift` lists the
692
- paths).
693
- - The CLI never reads stdin, and a test now pins that with a stdin that is an
694
- open pipe nobody writes to.
695
- - **Behaviour change, not breaking**: an output file that already holds the
696
- generated bytes is no longer rewritten, and its line reads
697
- `unchanged <path>` where it read `wrote <path>`. Bytes, names and exit codes
698
- are unchanged; scripts matching `wrote ` on a re-run should match
699
- `unchanged ` too, or use `--json`.
700
- - Every path the CLI reports, in the document and in the human output, is the
701
- entry or directory as passed (or `-o` joined with a file name), never a
702
- resolved absolute path, so the same invocation prints the same bytes from any
703
- working directory.
704
- - The library (`didToModule`, `didToContract`, `init`) keeps its signatures;
705
- `ModuleSuccess` gains `omitted` (above), and `lib/index.d.ts` adds the
706
- `Omission`, `CliReport`, `CliEntryReport` and `CliEntryStatus` types.
707
-
708
- ## 0.1.0 — 2026-08-27
709
-
710
- Embeds `candid-core` 0.1.0-beta.3 and the `candid-core-ts` generator from
711
- the same repository commit the release is dispatched from; the release
712
- record names the exact SHA.
713
-
714
- The first version: the JavaScript-only on-ramp.
715
-
716
- - **`gen <service.did> [-o <dir>]`** emits the generated
717
- `@candid-core/schema` module, the one-document `ContractEnvelope` with the
718
- `org.candid-core.field-names/v1` field-name table, and prints the content-addressed identities. Imports resolve
719
- from the entry's directory as an in-memory bundle; every generation
720
- double-runs and the tool refuses to write on any byte mismatch.
721
- - **`didToContract` / `didToModule`** — the same two operations as a
722
- library, for Node and browsers alike: one wasm-bindgen artifact over
723
- `candid-core`'s `compiler` feature (`compile_with_resolver` +
724
- `MemoryResolver`; no filesystem, no eval, no dynamic import) plus the
725
- `candid-core-ts` generator. Failures are `{ ok: false, diagnostics }` with
726
- the compiler's diagnostics verbatim; generator refusals surface as
727
- `ts_generation_refused`, malformed requests as `invalid_request`.
728
- - **Parity is a gate, not a claim**: CI compares the wasm-built outputs
729
- byte-for-byte against the repository's reviewed goldens and against the
730
- native `candid-core compile --envelope` fixture, builds the artifact twice
731
- from clean and requires identical bytes, and runs `didToContract` inside
732
- headless Chrome feeding `schemaFromContract`, envelope-carried names
733
- included.