From 64b91bb9e986b064c42025ab34481190e7303fce Mon Sep 17 00:00:00 2001 From: Dan Steren Date: Mon, 2 Oct 2023 00:08:28 -0600 Subject: [PATCH 01/10] Start updating docs to v0.18.0 --- the_azle_book/src/candid.md | 790 +++++++++++++--------------- the_azle_book/src/hello_world.md | 42 +- the_azle_book/src/installation.md | 6 +- the_azle_book/src/query_methods.md | 43 +- the_azle_book/src/update_methods.md | 190 +++---- 5 files changed, 523 insertions(+), 548 deletions(-) diff --git a/the_azle_book/src/candid.md b/the_azle_book/src/candid.md index de0b2c11dd..fd8b8965f9 100644 --- a/the_azle_book/src/candid.md +++ b/the_azle_book/src/candid.md @@ -40,112 +40,112 @@ The following is a simple example showing how to import and use most of the Cand ```typescript import { + Canister, + Func, + None, + Null, + Opt, + Principal, + Record, + Recursive, + Variant, + Vec, blob, - CallResult, - float64, + bool, float32, - Func, + float64, int, - int8, int16, int32, int64, + int8, nat, - nat8, nat16, nat32, nat64, - Opt, - Principal, - $query, - Query, - Record, - Service, - serviceQuery, - serviceUpdate, - Variant, - Vec + nat8, + query, + text, + update } from 'azle'; -type Candid = Record<{ - text: string; - blob: blob; - nat: nat; - nat64: nat64; - nat32: nat32; - nat16: nat16; - nat8: nat8; - int: int; - int64: int64; - int32: int32; - int16: int16; - int8: int8; - float64: float64; - float32: float32; - bool: boolean; - null: null; - vec: Vec; - opt: Opt; - record: Record<{ - firstName: string; - lastName: string; - age: nat8; - }>; - variant: Variant<{ - Tag1: null; - Tag2: null; - Tag3: int; - }>; - func: Func Candid>>; - service: MyService; - principal: Principal; -}>; - -class MyService extends Service { - @serviceQuery - query1: () => CallResult; - - @serviceUpdate - update1: () => CallResult; -} - -$query; -export function candidTypes(): Candid { - return { - text: 'text', - blob: Uint8Array.from([]), - nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n, - nat64: 18_446_744_073_709_551_615n, - nat32: 4_294_967_295, - nat16: 65_535, - nat8: 255, - int: 170_141_183_460_469_231_731_687_303_715_884_105_727n, - int64: 9_223_372_036_854_775_807n, - int32: 2_147_483_647, - int16: 32_767, - int8: 127, - float64: Math.E, - float32: Math.PI, - bool: true, - null: null, - vec: ['has one element'], - opt: Opt.None, - record: { - firstName: 'John', - lastName: 'Doe', - age: 35 - }, - variant: { - Tag1: null - }, - func: [ - Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), - 'candidTypes' - ], - service: new MyService(Principal.fromText('aaaaa-aa')), - principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai') - }; -} +const MyCanister = Canister({ + query1: query([], bool), + update1: update([], text) +}); + +const Candid = Record({ + text: text, + blob: blob, + nat: nat, + nat64: nat64, + nat32: nat32, + nat16: nat16, + nat8: nat8, + int: int, + int64: int64, + int32: int32, + int16: int16, + int8: int8, + float64: float64, + float32: float32, + bool: bool, + null: Null, + vec: Vec(text), + opt: Opt(nat), + record: Record({ + firstName: text, + lastName: text, + age: nat8 + }), + variant: Variant({ + Tag1: Null, + Tag2: Null, + Tag3: int + }), + func: Recursive(() => Func([], Candid, 'query')), + canister: MyCanister, + principal: Principal +}); + +export default Canister({ + candidTypes: query([], Candid, () => { + return { + text: 'text', + blob: Uint8Array.from([]), + nat: 340_282_366_920_938_463_463_374_607_431_768_211_455n, + nat64: 18_446_744_073_709_551_615n, + nat32: 4_294_967_295, + nat16: 65_535, + nat8: 255, + int: 170_141_183_460_469_231_731_687_303_715_884_105_727n, + int64: 9_223_372_036_854_775_807n, + int32: 2_147_483_647, + int16: 32_767, + int8: 127, + float64: Math.E, + float32: Math.PI, + bool: true, + null: null, + vec: ['has one element'], + opt: None, + record: { + firstName: 'John', + lastName: 'Doe', + age: 35 + }, + variant: { + Tag1: null + }, + func: [ + Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), + 'candidTypes' + ], + canister: MyCanister(Principal.fromText('aaaaa-aa')), + principal: Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai') + }; + }) +}); ``` Calling `candidTypes` with `dfx` will return: @@ -187,18 +187,17 @@ The TypeScript type `string` and the Azle type `text` both correspond to the [Ca TypeScript: ```typescript -import { $query } from 'azle'; +import { Canister, query, text } from 'azle'; -$query; -export function getString(): string { - return 'Hello world!'; -} - -$query; -export function printString(string: string): string { - console.log(typeof string); - return string; -} +export default Canister({ + getString: query([], text, () => { + return 'Hello world!'; + }), + printString: query([text], text, (string) => { + console.log(typeof string); + return string; + }) +}); ``` Candid: @@ -224,18 +223,17 @@ The Azle type `blob` corresponds to the [Candid type blob](https://internetcompu TypeScript: ```typescript -import { blob, $query } from 'azle'; +import { Canister, blob, query } from 'azle'; -$query; -export function getBlob(): blob { - return Uint8Array.from([68, 73, 68, 76, 0, 0]); -} - -$query; -export function printBlob(blob: blob): blob { - console.log(typeof blob); - return blob; -} +export default Canister({ + getBlob: query([], blob, () => { + return Uint8Array.from([68, 73, 68, 76, 0, 0]); + }), + printBlob: query([blob], blob, (blob) => { + console.log(typeof blob); + return blob; + }) +}); ``` Candid: @@ -264,18 +262,17 @@ The Azle type `nat` corresponds to the [Candid type nat](https://internetcompute TypeScript: ```typescript -import { nat, $query } from 'azle'; +import { Canister, nat, query } from 'azle'; -$query; -export function getNat(): nat { - return 340_282_366_920_938_463_463_374_607_431_768_211_455n; -} - -$query; -export function printNat(nat: nat): nat { - console.log(typeof nat); - return nat; -} +export default Canister({ + getNat: query([], nat, () => { + return 340_282_366_920_938_463_463_374_607_431_768_211_455n; + }), + printNat: query([nat], nat, (nat) => { + console.log(typeof nat); + return nat; + }) +}); ``` Candid: @@ -301,18 +298,17 @@ The Azle type `nat64` corresponds to the [Candid type nat64](https://internetcom TypeScript: ```typescript -import { nat64, $query } from 'azle'; +import { Canister, nat64, query } from 'azle'; -$query; -export function getNat64(): nat64 { - return 18_446_744_073_709_551_615n; -} - -$query; -export function printNat64(nat64: nat64): nat64 { - console.log(typeof nat64); - return nat64; -} +export default Canister({ + getNat64: query([], nat64, () => { + return 18_446_744_073_709_551_615n; + }), + printNat64: query([nat64], nat64, (nat64) => { + console.log(typeof nat64); + return nat64; + }) +}); ``` Candid: @@ -338,18 +334,17 @@ The Azle type `nat32` corresponds to the [Candid type nat32](https://internetcom TypeScript: ```typescript -import { nat32, $query } from 'azle'; +import { Canister, nat32, query } from 'azle'; -$query; -export function getNat32(): nat32 { - return 4_294_967_295; -} - -$query; -export function printNat32(nat32: nat32): nat32 { - console.log(typeof nat32); - return nat32; -} +export default Canister({ + getNat32: query([], nat32, () => { + return 4_294_967_295; + }), + printNat32: query([nat32], nat32, (nat32) => { + console.log(typeof nat32); + return nat32; + }) +}); ``` Candid: @@ -375,18 +370,17 @@ The Azle type `nat16` corresponds to the [Candid type nat16](https://internetcom TypeScript: ```typescript -import { nat16, $query } from 'azle'; - -$query; -export function getNat16(): nat16 { - return 65_535; -} +import { Canister, nat16, query } from 'azle'; -$query; -export function printNat16(nat16: nat16): nat16 { - console.log(typeof nat16); - return nat16; -} +export default Canister({ + getNat16: query([], nat16, () => { + return 65_535; + }), + printNat16: query([nat16], nat16, (nat16) => { + console.log(typeof nat16); + return nat16; + }) +}); ``` Candid: @@ -412,18 +406,17 @@ The Azle type `nat8` corresponds to the [Candid type nat8](https://internetcompu TypeScript: ```typescript -import { nat8, $query } from 'azle'; +import { Canister, nat8, query } from 'azle'; -$query; -export function getNat8(): nat8 { - return 255; -} - -$query; -export function printNat8(nat8: nat8): nat8 { - console.log(typeof nat8); - return nat8; -} +export default Canister({ + getNat8: query([], nat8, () => { + return 255; + }), + printNat8: query([nat8], nat8, (nat8) => { + console.log(typeof nat8); + return nat8; + }) +}); ``` Candid: @@ -449,18 +442,17 @@ The Azle type `int` corresponds to the [Candid type int](https://internetcompute TypeScript: ```typescript -import { int, $query } from 'azle'; - -$query; -export function getInt(): int { - return 170_141_183_460_469_231_731_687_303_715_884_105_727n; -} +import { Canister, int, query } from 'azle'; -$query; -export function printInt(int: int): int { - console.log(typeof int); - return int; -} +export default Canister({ + getInt: query([], int, () => { + return 170_141_183_460_469_231_731_687_303_715_884_105_727n; + }), + printInt: query([int], int, (int) => { + console.log(typeof int); + return int; + }) +}); ``` Candid: @@ -486,18 +478,17 @@ The Azle type `int64` corresponds to the [Candid type int64](https://internetcom TypeScript: ```typescript -import { int64, $query } from 'azle'; - -$query; -export function getInt64(): int64 { - return 9_223_372_036_854_775_807n; -} +import { Canister, int64, query } from 'azle'; -$query; -export function printInt64(int64: int64): int64 { - console.log(typeof int64); - return int64; -} +export default Canister({ + getInt64: query([], int64, () => { + return 9_223_372_036_854_775_807n; + }), + printInt64: query([int64], int64, (int64) => { + console.log(typeof int64); + return int64; + }) +}); ``` Candid: @@ -523,18 +514,17 @@ The Azle type `int32` corresponds to the [Candid type int32](https://internetcom TypeScript: ```typescript -import { int32, $query } from 'azle'; - -$query; -export function getInt32(): int32 { - return 2_147_483_647; -} +import { Canister, int32, query } from 'azle'; -$query; -export function printInt32(int32: int32): int32 { - console.log(typeof int32); - return int32; -} +export default Canister({ + getInt32: query([], int32, () => { + return 2_147_483_647; + }), + printInt32: query([int32], int32, (int32) => { + console.log(typeof int32); + return int32; + }) +}); ``` Candid: @@ -560,18 +550,17 @@ The Azle type `int16` corresponds to the [Candid type int16](https://internetcom TypeScript: ```typescript -import { int16, $query } from 'azle'; - -$query; -export function getInt16(): int16 { - return 32_767; -} +import { Canister, int16, query } from 'azle'; -$query; -export function printInt16(int16: int16): int16 { - console.log(typeof int16); - return int16; -} +export default Canister({ + getInt16: query([], int16, () => { + return 32_767; + }), + printInt16: query([int16], int16, (int16) => { + console.log(typeof int16); + return int16; + }) +}); ``` Candid: @@ -597,18 +586,17 @@ The Azle type `int8` corresponds to the [Candid type int8](https://internetcompu TypeScript: ```typescript -import { int8, $query } from 'azle'; - -$query; -export function getInt8(): int8 { - return 127; -} +import { Canister, int8, query } from 'azle'; -$query; -export function printInt8(int8: int8): int8 { - console.log(typeof int8); - return int8; -} +export default Canister({ + getInt8: query([], int8, () => { + return 127; + }), + printInt8: query([int8], int8, (int8) => { + console.log(typeof int8); + return int8; + }) +}); ``` Candid: @@ -634,18 +622,17 @@ The Azle type `float64` and the TypeScript type `number` both correspond to the TypeScript: ```typescript -import { float64, $query } from 'azle'; - -$query; -export function getFloat64(): float64 { - return Math.E; -} +import { Canister, float64, query } from 'azle'; -$query; -export function printFloat64(float64: float64): float64 { - console.log(typeof float64); - return float64; -} +export default Canister({ + getFloat64: query([], float64, () => { + return Math.E; + }), + printFloat64: query([float64], float64, (float64) => { + console.log(typeof float64); + return float64; + }) +}); ``` Candid: @@ -671,18 +658,17 @@ The Azle type `float32` corresponds to the [Candid type float32](https://interne TypeScript: ```typescript -import { float32, $query } from 'azle'; - -$query; -export function getFloat32(): float32 { - return Math.PI; -} +import { Canister, float32, query } from 'azle'; -$query; -export function printFloat32(float32: float32): float32 { - console.log(typeof float32); - return float32; -} +export default Canister({ + getFloat32: query([], float32, () => { + return Math.PI; + }), + printFloat32: query([float32], float32, (float32) => { + console.log(typeof float32); + return float32; + }) +}); ``` Candid: @@ -708,18 +694,17 @@ The TypeScript type `boolean` corresponds to the [Candid type bool](https://inte TypeScript: ```typescript -import { $query } from 'azle'; - -$query; -export function getBool(): boolean { - return true; -} +import { Canister, bool, query } from 'azle'; -$query; -export function printBool(bool: boolean): boolean { - console.log(typeof bool); - return bool; -} +export default Canister({ + getBool: query([], bool, () => { + return true; + }), + printBool: query([bool], bool, (bool) => { + console.log(typeof bool); + return bool; + }) +}); ``` Candid: @@ -745,18 +730,17 @@ The TypeScript type `null` corresponds to the [Candid type null](https://interne TypeScript: ```typescript -import { $query } from 'azle'; - -$query; -export function getNull(): null { - return null; -} +import { Canister, Null, query } from 'azle'; -$query; -export function printNull(null_: null): null { - console.log(typeof null_); - return null_; -} +export default Canister({ + getNull: query([], Null, () => { + return null; + }), + printNull: query([Null], Null, (null_) => { + console.log(typeof null_); + return null_; + }) +}); ``` Candid: @@ -782,18 +766,17 @@ The Azle type `Vec` corresponds to the [Candid type vec](https://internetcompute TypeScript: ```typescript -import { int32, $query, Vec } from 'azle'; +import { Canister, Vec, int32, query } from 'azle'; -$query; -export function getNumbers(): Vec { - return [0, 1, 2, 3]; -} - -$query; -export function printNumbers(numbers: Vec): Vec { - console.log(typeof numbers); - return numbers; -} +export default Canister({ + getNumbers: query([], Vec(int32), () => { + return [0, 1, 2, 3]; + }), + printNumbers: query([Vec(int32)], Vec(int32), (numbers) => { + console.log(typeof numbers); + return numbers; + }) +}); ``` Candid: @@ -819,17 +802,16 @@ The Azle type `Opt` corresponds to the [Candid type opt](https://internetcompute TypeScript: ```typescript -import { Opt, $query } from 'azle'; +import { Canister, None, Opt, Some, bool, query } from 'azle'; -$query; -export function getOptSome(): Opt { - return Opt.Some(true); -} - -$query; -export function getOptNone(): Opt { - return Opt.None; -} +export default Canister({ + getOptSome: query([], Opt(bool), () => { + return Some(true); + }), + getOptNone: query([], Opt(bool), () => { + return None; + }) +}); ``` Candid: @@ -858,26 +840,25 @@ TypeScript type aliases referring to object literals wrapped in the `Record` Azl TypeScript: ```typescript -import { Principal, $query, Record } from 'azle'; - -type User = Record<{ - id: Principal; - username: string; -}>; - -$query; -export function getUser(): User { - return { - id: Principal.fromUint8Array(Uint8Array.from([0])), - username: 'lastmjs' - }; -} - -$query; -export function printUser(user: User): User { - console.log(typeof user); - return user; -} +import { Canister, Principal, Record, query, text } from 'azle'; + +const User = Record({ + id: Principal, + username: text +}); + +export default Canister({ + getUser: query([], User, () => { + return { + id: Principal.fromUint8Array(Uint8Array.from([0])), + username: 'lastmjs' + }; + }), + printUser: query([User], User, (user) => { + console.log(typeof user); + return user; + }) +}); ``` Candid: @@ -904,32 +885,31 @@ TypeScript type aliases referring to object literals wrapped in the `Variant` Az TypeScript: ```typescript -import { $query, Variant } from 'azle'; - -type Reaction = Variant<{ - Fire: null; - ThumbsUp: null; - Emotion: Emotion; -}>; - -type Emotion = Variant<{ - Happy: null; - Indifferent: null; - Sad: null; -}>; - -$query; -export function getReaction(): Reaction { - return { - Fire: null - }; -} - -$query; -export function printReaction(reaction: Reaction): Reaction { - console.log(typeof reaction); - return reaction; -} +import { Canister, Null, Variant, query } from 'azle'; + +const Emotion = Variant({ + Happy: Null, + Indifferent: Null, + Sad: Null +}); + +const Reaction = Variant({ + Fire: Null, + ThumbsUp: Null, + Emotion: Emotion +}); + +export default Canister({ + getReaction: query([], Reaction, () => { + return { + Fire: null + }; + }), + printReaction: query([Reaction], Reaction, (reaction) => { + console.log(typeof reaction); + return reaction; + }) +}); ``` Candid: @@ -961,20 +941,22 @@ A `func` acts as a callback, allowing the `func` receiver to know which canister TypeScript: ```typescript -import { Func, Principal, $query, Query } from 'azle'; +import { Canister, Func, Principal, query, text } from 'azle'; -type BasicFunc = Func string>>; +const BasicFunc = Func([text], text, 'query'); -$query; -export function getBasicFunc(): BasicFunc { - return [Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), 'getBasicFunc']; -} - -$query; -export function printBasicFunc(basicFunc: BasicFunc): BasicFunc { - console.log(typeof basicFunc); - return basicFunc; -} +export default Canister({ + getBasicFunc: query([], BasicFunc, () => { + return [ + Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'), + 'getBasicFunc' + ]; + }), + printBasicFunc: query([BasicFunc], BasicFunc, (basicFunc) => { + console.log(typeof basicFunc); + return basicFunc; + }) +}); ``` Candid: @@ -1002,36 +984,21 @@ JavaScript classes that inherit from the Azle type `Service` correspond to the [ TypeScript: ```typescript -import { - CallResult, - Principal, - $query, - Result, - Service, - serviceQuery, - serviceUpdate, - $update -} from 'azle'; +import { Canister, Principal, bool, ic, query, text, update } from 'azle'; -class SomeService extends Service { - @serviceQuery - query1: () => CallResult; +const SomeCanister = Canister({ + query1: query([], bool), + update1: update([], text) +}); - @serviceUpdate - update1: () => CallResult; -} - -$query; -export function getService(): SomeService { - return new SomeService(Principal.fromText('aaaaa-aa')); -} - -$update; -export async function callService( - service: SomeService -): Promise> { - return await service.update1().call(); -} +export default Canister({ + getService: query([], SomeCanister, () => { + return SomeCanister(Principal.fromText('aaaaa-aa')); + }), + callService: update([SomeCanister], text, (service) => { + return ic.call(service.update1); + }) +}); ``` Candid: @@ -1062,18 +1029,17 @@ The Azle type `Principal` corresponds to the [Candid type principal](https://int TypeScript: ```typescript -import { Principal, $query } from 'azle'; - -$query; -export function getPrincipal(): Principal { - return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'); -} +import { Canister, Principal, query } from 'azle'; -$query; -export function printPrincipal(principal: Principal): Principal { - console.log(typeof principal); - return principal; -} +export default Canister({ + getPrincipal: query([], Principal, () => { + return Principal.fromText('rrkah-fqaaa-aaaaa-aaaaq-cai'); + }), + printPrincipal: query([Principal], Principal, (principal) => { + console.log(typeof principal); + return principal; + }) +}); ``` Candid: @@ -1099,18 +1065,17 @@ The Azle type `reserved` corresponds to the [Candid type reserved](https://inter TypeScript: ```typescript -import { $query, reserved } from 'azle'; - -$query; -export function getReserved(): reserved { - return 'anything'; -} +import { Canister, query, reserved } from 'azle'; -$query; -export function printReserved(reserved: reserved): reserved { - console.log(typeof reserved); - return reserved; -} +export default Canister({ + getReserved: query([], reserved, () => { + return 'anything'; + }), + printReserved: query([reserved], reserved, (reserved) => { + console.log(typeof reserved); + return reserved; + }) +}); ``` Candid: @@ -1136,20 +1101,19 @@ The Azle type `empty` corresponds to the [Candid type empty](https://internetcom TypeScript: ```typescript -import { empty, $query } from 'azle'; - -$query; -export function getEmpty(): empty { - throw 'Anything you want'; -} - -// Note: It is impossible to call this function because it requires an argument -// but there is no way to pass an "empty" value as an argument. -$query; -export function printEmpty(empty: empty): empty { - console.log(typeof empty); - throw 'Anything you want'; -} +import { Canister, empty, query } from 'azle'; + +export default Canister({ + getEmpty: query([], empty, () => { + throw 'Anything you want'; + }), + // Note: It is impossible to call this function because it requires an argument + // but there is no way to pass an "empty" value as an argument. + printEmpty: query([empty], empty, (empty) => { + console.log(typeof empty); + throw 'Anything you want'; + }) +}); ``` Candid: diff --git a/the_azle_book/src/hello_world.md b/the_azle_book/src/hello_world.md index de8e8691fc..bee77c1f05 100644 --- a/the_azle_book/src/hello_world.md +++ b/the_azle_book/src/hello_world.md @@ -63,32 +63,31 @@ Open up `azle_hello_world` in your text editor (we recommend [VS Code](https://c Here's the main code of the project, which you should put in the `azle_hello_world/src/index.ts` file of your canister: ```typescript -import { $query, $update } from 'azle'; +import { Canister, Void, query, text, update } from 'azle'; // This is a global variable that is stored on the heap let message: string = ''; -// Query calls complete quickly because they do not go through consensus -$query; -export function getMessage(): string { - return message; -} - -// Update calls take a few seconds to complete -// This is because they persist state changes and go through consensus -$update; -export function setMessage(newMessage: string): void { - message = newMessage; // This change will be persisted -} +export default Canister({ + // Query calls complete quickly because they do not go through consensus + getMessage: query([], text, () => { + return message; + }), + // Update calls take a few seconds to complete + // This is because they persist state changes and go through consensus + setMessage: update([text], Void, (newMessage) => { + message = newMessage; // This change will be persisted + }) +}); ``` Let's discuss each section of the code. ```typescript -import { $query, $update } from 'azle'; +import { Canister, Void, query, text, update } from 'azle'; ``` -The code starts off by importing the `$query` and `$update` annotations from `azle`. The `azle` module provides most of the Internet Computer (IC) APIs for your canister. +The code starts off by importing the `query` and `update` annotations from `azle`. The `azle` module provides most of the Internet Computer (IC) APIs for your canister. ```typescript // This is a global variable that is stored on the heap @@ -99,10 +98,9 @@ We have created a global variable to store the state of our application. This va ```typescript // Query calls complete quickly because they do not go through consensus -$query; -export function getMessage(): string { +getMessage: query([], text, () => { return message; -} +}), ``` We are exposing a canister query method here. When query methods are called they execute quickly because they do not have to go through consensus. This method simply returns our global `message` variable. @@ -110,10 +108,9 @@ We are exposing a canister query method here. When query methods are called they ```typescript // Update calls take a few seconds to complete // This is because they persist state changes and go through consensus -$update; -export function setMessage(newMessage: string): void { +setMessage: update([text], Void, (newMessage) => { message = newMessage; // This change will be persisted -} +}); ``` We are exposing an update method here. When update methods are called they take a few seconds to complete. This is because they persist changes and go through consensus. A majority of nodes in a subnet must agree on all state changes introduced in calls to update methods. This method accepts a `string` from the caller and will store it in our global `message` variable. @@ -149,9 +146,8 @@ Create the following in `azle_hello_world/dfx.json`: "canisters": { "azle_hello_world": { "type": "custom", + "main": "src/index.ts", "build": "npx azle azle_hello_world", - "root": "src", - "ts": "src/index.ts", "candid": "src/index.did", "wasm": ".azle/azle_hello_world/azle_hello_world.wasm.gz" } diff --git a/the_azle_book/src/installation.md b/the_azle_book/src/installation.md index b19bd60e2a..08d68afc50 100644 --- a/the_azle_book/src/installation.md +++ b/the_azle_book/src/installation.md @@ -5,7 +5,7 @@ Follow the instructions exactly as stated below to avoid issues. You should be using a \*nix environment (Linux, Mac OS, [WSL if using Windows](https://learn.microsoft.com/en-us/windows/wsl/install)) with bash and have the following installed on your system: - Node.js 18 -- dfx 0.14.2 +- dfx 0.15.0 ## Node.js @@ -23,10 +23,10 @@ nvm install 18 ## dfx -Run the following command to install dfx 0.14.2: +Run the following command to install dfx 0.15.0: ```bash -DFX_VERSION=0.14.2 sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)" +DFX_VERSION=0.15.0 sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)" ``` If after trying to run `dfx` commands you encounter an error such as `dfx: command not found`, you might need to add `$HOME/bin` to your path. Here's an example of doing this in your `.bashrc`: diff --git a/the_azle_book/src/query_methods.md b/the_azle_book/src/query_methods.md index d5c8ba3cde..0f9ad30ebd 100644 --- a/the_azle_book/src/query_methods.md +++ b/the_azle_book/src/query_methods.md @@ -2,7 +2,7 @@ ## TLDR -- Annotate functions with `$query` +- Annotate functions with `query` - Read-only - Executed on a single node - No consensus @@ -14,12 +14,13 @@ The most basic way to expose your canister's functionality publicly is through a query method. Here's an example of a simple query method: ```typescript -import { $query } from 'azle'; +import { Canister, query, text } from 'azle'; -$query; -export function getString(): string { - return 'This is a query method!'; -} +export default Canister({ + getString: query([], text, () => { + return 'This is a query method!'; + }) +}); ``` `getString` can be called from the outside world through the IC's HTTP API. You'll usually invoke this API from the [`dfx command line`, `dfx web UI`, or an agent](./deployment.md#interacting-with-your-canister). @@ -33,16 +34,17 @@ dfx canister call my_canister getString Query methods are read-only. They do not persist any state changes. Take a look at the following example: ```typescript -import { $query } from 'azle'; +import { Canister, Void, query, text } from 'azle'; let db: { [key: string]: string; } = {}; -$query; -export function set(key: string, value: string): void { - db[key] = value; -} +export default Canister({ + set: query([text, text], Void, (key, value) => { + db[key] = value; + }) +}); ``` Calling `set` will perform the operation of setting the `key` property on the `db` object to `value`, but after the call finishes that change will be discarded. @@ -52,15 +54,16 @@ This is because query methods are executed on a single node machine and do not g There is a limit to how much computation can be done in a single call to a query method. The current query call limit is [5 billion Wasm instructions](https://internetcomputer.org/docs/current/developer-docs/production/instruction-limits). Here's an example of a query method that runs the risk of reaching the limit: ```typescript -import { nat32, $query } from 'azle'; - -$query; -export function pyramid(levels: nat32): string { - return new Array(levels).fill(0).reduce((acc, _, index) => { - const asterisks = new Array(index + 1).fill('*').join(''); - return `${acc}${asterisks}\n`; - }, ''); -} +import { Canister, nat32, query, text } from 'azle'; + +export default Canister({ + pyramid: query([nat32], text, (levels) => { + return new Array(levels).fill(0).reduce((acc, _, index) => { + const asterisks = new Array(index + 1).fill('*').join(''); + return `${acc}${asterisks}\n`; + }, ''); + }) +}); ``` From the `dfx command line` you can call `pyramid` like this: diff --git a/the_azle_book/src/update_methods.md b/the_azle_book/src/update_methods.md index 98e6fe9915..13b4326f5e 100644 --- a/the_azle_book/src/update_methods.md +++ b/the_azle_book/src/update_methods.md @@ -2,7 +2,7 @@ ## TLDR -- Annotate functions with `$update` +- Annotate functions with `update` - Read-write - Executed on many nodes - Consensus @@ -15,14 +15,15 @@ Update methods are similar to query methods, but state changes can be persisted. Here's an example of a simple update method: ```typescript -import { nat64, $update } from 'azle'; +import { Canister, nat64, update } from 'azle'; let counter = 0n; -$update; -export function increment(): nat64 { - return counter++; -} +export default Canister({ + increment: update([], nat64, () => { + return counter++; + }) +}); ``` Calling `increment` will return the current value of `counter` and then increase its value by 1. Because `counter` is a global variable, the change will be persisted to the heap, and subsequent query and update calls will have access to the new `counter` value. @@ -32,19 +33,18 @@ Because the Internet Computer (IC) persists changes with certain fault tolerance Due to the latency and other expenses involved with update methods, it is best to use them only when necessary. Look at the following example: ```typescript -import { $query, $update } from 'azle'; +import { Canister, Void, query, text, update } from 'azle'; let message = ''; -$query; -export function getMessage(): string { - return message; -} - -$update; -export function setMessage(newMessage: string): void { - message = newMessage; -} +export default Canister({ + getMessage: query([], text, () => { + return message; + }), + setMessage: update([text], Void, (newMessage) => { + message = newMessage; + }) +}); ``` You'll notice that we use an update method, `setMessage`, only to perform the change to the global `message` variable. We use `getMessage`, a query method, to read the message. @@ -52,7 +52,7 @@ You'll notice that we use an update method, `setMessage`, only to perform the ch Keep in mind that the heap is limited to 4 GiB, and thus there is an upper bound to global variable storage capacity. You can imagine how a simple database like the following would eventually run out of memory with too many entries: ```typescript -import { Opt, $query, $update } from 'azle'; +import { Canister, None, Opt, Some, Void, query, text, update } from 'azle'; type Db = { [key: string]: string; @@ -60,34 +60,32 @@ type Db = { let db: Db = {}; -$query; -export function get(key: string): Opt { - const value = db[key]; - return value !== undefined ? Opt.Some(value) : Opt.None; -} - -$update; -export function set(key: string, value: string): void { - db[key] = value; -} +export default Canister({ + get: query([text], Opt(text), (key) => { + const value = db[key]; + return value !== undefined ? Some(value) : None; + }), + set: update([text, text], Void, (key, value) => { + db[key] = value; + }) +}); ``` If you need more than 4 GiB of storage, consider taking advantage of the 48 GiB of stable memory. Stable structures like `StableBTreeMap` give you a nice API for interacting with stable memory. These data structures will be [covered in more detail later](./stable_structures.md). Here's a simple example: ```typescript -import { Opt, $query, StableBTreeMap, $update } from 'azle'; - -let db = new StableBTreeMap(0, 10, 10); - -$query; -export function get(key: string): Opt { - return db.get(key); -} - -$update; -export function set(key: string, value: string): void { - db.insert(key, value); -} +import { Canister, Opt, StableBTreeMap, Void, query, text, update } from 'azle'; + +let db = StableBTreeMap(text, text, 0); + +export default Canister({ + get: query([text], Opt(text), (key) => { + return db.get(key); + }), + set: update([text, text], Void, (key, value) => { + db.insert(key, value); + }) +}); ``` So far we have only seen how state changes can be persisted. State changes can also be discarded by implicit or explicit traps. A trap is an immediate stop to execution with the ability to provide a message to the execution environment. @@ -97,35 +95,43 @@ Traps can be useful for ensuring that multiple operations are either all complet Here's an example of how to trap and ensure atomic changes to your database: ```typescript -import { ic, Opt, $query, Record, StableBTreeMap, $update, Vec } from 'azle'; - -type Entry = Record<{ - key: string; - value: string; -}>; - -let db = new StableBTreeMap(0, 10, 10); - -$query; -export function get(key: string): Opt { - return db.get(key); -} - -$update; -export function set(key: string, value: string): void { - db.insert(key, value); -} - -$update; -export function setMany(entries: Vec): void { - entries.forEach((entry) => { - if (entry.key === 'trap') { - ic.trap('explicit trap'); - } - - db.insert(entry.key, entry.value); - }); -} +import { + Canister, + Opt, + Record, + StableBTreeMap, + Vec, + Void, + ic, + query, + text, + update +} from 'azle'; + +const Entry = Record({ + key: text, + value: text +}); + +let db = StableBTreeMap(text, text, 0); + +export default Canister({ + get: query([text], Opt(text), (key) => { + return db.get(key); + }), + set: update([text, text], Void, (key, value) => { + db.insert(key, value); + }), + setMany: update([Vec(Entry)], Void, (entries) => { + entries.forEach((entry) => { + if (entry.key === 'trap') { + ic.trap('explicit trap'); + } + + db.insert(entry.key, entry.value); + }); + }) +}); ``` In addition to `ic.trap`, an explicit JavaScript `throw` or any unhandled exception will also trap. @@ -133,26 +139,32 @@ In addition to `ic.trap`, an explicit JavaScript `throw` or any unhandled except There is a limit to how much computation can be done in a single call to an update method. The current update call limit is [20 billion Wasm instructions](https://internetcomputer.org/docs/current/developer-docs/production/instruction-limits). If we modify our database example, we can introduce an update method that runs the risk of reaching the limit: ```typescript -import { nat64, Opt, $query, StableBTreeMap, $update } from 'azle'; - -let db = new StableBTreeMap(0, 1_000, 1_000); - -$query; -export function get(key: string): Opt { - return db.get(key); -} - -$update; -export function set(key: string, value: string): void { - db.insert(key, value); -} - -$update; -export function setMany(numEntries: nat64): void { - for (let i = 0; i < numEntries; i++) { - db.insert(i.toString(), i.toString()); - } -} +import { + Canister, + Opt, + StableBTreeMap, + Void, + nat64, + query, + text, + update +} from 'azle'; + +let db = StableBTreeMap(text, text, 0); + +export default Canister({ + get: query([text], Opt(text), (key) => { + return db.get(key); + }), + set: update([text, text], Void, (key, value) => { + db.insert(key, value); + }), + setMany: update([nat64], Void, (numEntries) => { + for (let i = 0; i < numEntries; i++) { + db.insert(i.toString(), i.toString()); + } + }) +}); ``` From the `dfx command line` you can call `setMany` like this: From fd2297a0e616cf7bbeaaf2e9a24df9b1e401045d Mon Sep 17 00:00:00 2001 From: Dan Steren Date: Mon, 2 Oct 2023 00:29:28 -0600 Subject: [PATCH 02/10] Update chapter 12 --- the_azle_book/src/stable_structures.md | 421 +++++++++++-------------- 1 file changed, 192 insertions(+), 229 deletions(-) diff --git a/the_azle_book/src/stable_structures.md b/the_azle_book/src/stable_structures.md index d4b0a852c0..765cea7066 100644 --- a/the_azle_book/src/stable_structures.md +++ b/the_azle_book/src/stable_structures.md @@ -6,8 +6,6 @@ - Persistent across upgrades - Familiar API - Must specify memory id -- Must specify maximum key size -- Must specify maximum value size - No migrations per memory id Stable structures are data structures with familiar APIs that allow access to stable memory. Stable memory is a separate memory location from the heap that currently allows up to 64 GiB of storage. Stable memory persists automatically across upgrades. @@ -21,80 +19,74 @@ Azle currently provides one stable structure called `StableBTreeMap`. It's simil Here's how to define a simple `StableBTreeMap`. Each `StableBTreeMap` must be defined in the global scope (not within any functions or objects etc): ```typescript -import { nat8, StableBTreeMap } from 'azle'; +import { StableBTreeMap, nat8, text } from 'azle'; -let map = new StableBTreeMap(0, 100, 1_000); +let map = StableBTreeMap(nat8, text, 0); ``` -This is a `StableBTreeMap` with a key of type `nat8` and a value of type `string`. Key and value types can be any [Candid type](candid.md). +This is a `StableBTreeMap` with a key of type `nat8` and a value of type `text`. Key and value types can be any [Candid type](candid.md). -This `StableBTreeMap` also has a `memory id` of `0`, a maximum key size of `100` bytes and a maximum value size of `1_000` bytes. You must statically specify the `memory id`, maximum key size, and maximum value sizes (they cannot be variables). - -Each `StableBTreeMap` instance must have a unique `memory id`. Once a `memory id` is allocated, it cannot be used with a different `StableBTreeMap`. This means you can't create another `StableBTreeMap` using the same `memory id`, and you can't change the key or value types of an existing `StableBTreeMap`. [This problem will be addressed](https://github.com/demergent-labs/azle/issues/843). +This `StableBTreeMap` also has a `memory id` of `0`. Each `StableBTreeMap` instance must have a unique `memory id`. Once a `memory id` is allocated, it cannot be used with a different `StableBTreeMap`. This means you can't create another `StableBTreeMap` using the same `memory id`, and you can't change the key or value types of an existing `StableBTreeMap`. [This problem will be addressed](https://github.com/demergent-labs/azle/issues/843). Here's an example showing all of the basic `StableBTreeMap` operations: ```typescript import { - nat64, - nat8, + Canister, Opt, - $query, StableBTreeMap, Tuple, - $update, - Vec + Vec, + bool, + nat64, + nat8, + query, + text, + update } from 'azle'; -type Key = nat8; -type Value = string; +const Key = nat8; +const Value = text; -let map = new StableBTreeMap(0, 100, 1_000); +let map = StableBTreeMap(Key, Value, 0); -$query; -export function containsKey(key: Key): boolean { - return map.containsKey(key); -} +export default Canister({ + containsKey: query([Key], bool, (key) => { + return map.containsKey(key); + }), -$query; -export function get(key: Key): Opt { - return map.get(key); -} + get: query([Key], Opt(Value), (key) => { + return map.get(key); + }), -$update; -export function insert(key: Key, value: Value): Opt { - return map.insert(key, value); -} + insert: update([Key, Value], Opt(Value), (key, value) => { + return map.insert(key, value); + }), -$query; -export function isEmpty(): boolean { - return map.isEmpty(); -} + isEmpty: query([], bool, () => { + return map.isEmpty(); + }), -$query; -export function items(): Vec> { - return map.items(); -} + items: query([], Vec(Tuple(Key, Value)), () => { + return map.items(); + }), -$query; -export function keys(): Vec { - return map.keys(); -} + keys: query([], Vec(Key), () => { + return map.keys(); + }), -$query; -export function len(): nat64 { - return map.len(); -} + len: query([], nat64, () => { + return map.len(); + }), -$update; -export function remove(key: Key): Opt { - return map.remove(key); -} + remove: update([Key], Opt(Value), (key) => { + return map.remove(key); + }), -$query; -export function values(): Vec { - return map.values(); -} + values: query([], Vec(Value), () => { + return map.values(); + }) +}); ``` With these basic operations you can build more complex CRUD database applications: @@ -102,111 +94,101 @@ With these basic operations you can build more complex CRUD database application ```typescript import { blob, + Canister, ic, + Err, nat64, + Ok, Opt, Principal, - $query, + query, Record, Result, StableBTreeMap, - $update, + text, + update, Variant, - Vec, - match + Vec } from 'azle'; -type User = Record<{ - id: Principal; - createdAt: nat64; - recordingIds: Vec; - username: string; -}>; - -type Recording = Record<{ - id: Principal; - audio: blob; - createdAt: nat64; - name: string; - userId: Principal; -}>; - -let users = new StableBTreeMap(0, 38, 100_000); -let recordings = new StableBTreeMap(1, 38, 5_000_000); - -$update; -export function createUser(username: string): User { - const id = generateId(); - const user: User = { - id, - createdAt: ic.time(), - recordingIds: [], - username - }; - - users.insert(user.id, user); - - return user; -} +const User = Record({ + id: Principal, + createdAt: nat64, + recordingIds: Vec(Principal), + username: text +}); -$query; -export function readUsers(): Vec { - return users.values(); -} +const Recording = Record({ + id: Principal, + audio: blob, + createdAt: nat64, + name: text, + userId: Principal +}); + +const AudioRecorderError = Variant({ + RecordingDoesNotExist: Principal, + UserDoesNotExist: Principal +}); + +let users = StableBTreeMap(Principal, User, 0); +let recordings = StableBTreeMap(Principal, Recording, 1); + +export default Canister({ + createUser: update([text], User, (username) => { + const id = generateId(); + const user: typeof User = { + id, + createdAt: ic.time(), + recordingIds: [], + username + }; + + users.insert(user.id, user); + + return user; + }), + readUsers: query([], Vec(User), () => { + return users.values(); + }), + readUserById: query([Principal], Opt(User), (id) => { + return users.get(id); + }), + deleteUser: update([Principal], Result(User, AudioRecorderError), (id) => { + const userOpt = users.get(id); + + if (userOpt.length === 0) { + return Err({ + UserDoesNotExist: id + }); + } -$query; -export function readUserById(id: Principal): Opt { - return users.get(id); -} + const user = userOpt[0]; -$update; -export function deleteUser(id: Principal): Result< - User, - Variant<{ - UserDoesNotExist: Principal; - }> -> { - const user = users.get(id); - - return match(user, { - Some: (user) => { - user.recordingIds.forEach((recordingId) => { - recordings.remove(recordingId); - }); + user.recordingIds.forEach((recordingId) => { + recordings.remove(recordingId); + }); - users.remove(user.id); + users.remove(user.id); - return { - Ok: user - }; - }, - None: () => { - return { - Err: { - UserDoesNotExist: id - } - }; - } - }); -} + return Ok(user); + }), + createRecording: update( + [blob, text, Principal], + Result(Recording, AudioRecorderError), + (audio, name, userId) => { + const userOpt = users.get(userId); + + if (userOpt.length === 0) { + return Err({ + UserDoesNotExist: userId + }); + } + + const user = userOpt[0]; -$update; -export function createRecording( - audio: blob, - name: string, - userId: Principal -): Result< - Recording, - Variant<{ - UserDoesNotExist: Principal; - }> -> { - const user = users.get(userId); - - return match(user, { - Some: (user) => { const id = generateId(); - const recording: Recording = { + const recording: typeof Recording = { id, audio, createdAt: ic.time(), @@ -216,87 +198,60 @@ export function createRecording( recordings.insert(recording.id, recording); - const updatedUser: User = { + const updatedUser: typeof User = { ...user, recordingIds: [...user.recordingIds, recording.id] }; users.insert(updatedUser.id, updatedUser); - return { - Ok: recording - }; - }, - None: () => { - return { - Err: { - UserDoesNotExist: userId - } - }; + return Ok(recording); } - }); -} + ), + readRecordings: query([], Vec(Recording), () => { + return recordings.values(); + }), + readRecordingById: query([Principal], Opt(Recording), (id) => { + return recordings.get(id); + }), + deleteRecording: update( + [Principal], + Result(Recording, AudioRecorderError), + (id) => { + const recordingOpt = recordings.get(id); + + if (recordingOpt.length === 0) { + return Err({ RecordingDoesNotExist: id }); + } + + const recording = recordingOpt[0]; + + const userOpt = users.get(recording.userId); + + if (userOpt.length === 0) { + return Err({ + UserDoesNotExist: recording.userId + }); + } + + const user = userOpt[0]; + + const updatedUser: typeof User = { + ...user, + recordingIds: user.recordingIds.filter( + (recordingId) => + recordingId.toText() !== recording.id.toText() + ) + }; -$query; -export function readRecordings(): Vec { - return recordings.values(); -} + users.insert(updatedUser.id, updatedUser); -$query; -export function readRecordingById(id: Principal): Opt { - return recordings.get(id); -} + recordings.remove(id); -$update; -export function deleteRecording(id: Principal): Result< - Recording, - Variant<{ - RecordingDoesNotExist: Principal; - UserDoesNotExist: Principal; - }> -> { - const recording = recordings.get(id); - - return match(recording, { - Some: (recording) => { - const user = users.get(recording.userId); - - return match(user, { - Some: (user) => { - const updatedUser: User = { - ...user, - recordingIds: user.recordingIds.filter( - (recordingId) => - recordingId.toText() !== recording.id.toText() - ) - }; - - users.insert(updatedUser.id, updatedUser); - - recordings.remove(id); - - return { - Ok: recording - }; - }, - None: () => { - return { - Err: { - UserDoesNotExist: recording.userId - } - }; - } - }); - }, - None: () => { - return { - Err: { - RecordingDoesNotExist: id - } - }; + return Ok(recording); } - }); -} + ) +}); function generateId(): Principal { const randomBytes = new Array(29) @@ -312,25 +267,33 @@ The example above shows a very basic audio recording backend application. There Each entity gets its own `StableBTreeMap`: ```typescript -import { blob, nat64, Principal, Record, StableBTreeMap, Vec } from 'azle'; - -type User = Record<{ - id: Principal; - createdAt: nat64; - recordingIds: Vec; - username: string; -}>; - -type Recording = Record<{ - id: Principal; - audio: blob; - createdAt: nat64; - name: string; - userId: Principal; -}>; - -let users = new StableBTreeMap(0, 38, 100_000); -let recordings = new StableBTreeMap(1, 38, 5_000_000); +import { + Principal, + Record, + StableBTreeMap, + Vec, + blob, + nat64, + text +} from 'azle'; + +const User = Record({ + id: Principal, + createdAt: nat64, + recordingIds: Vec(Principal), + username: text +}); + +const Recording = Record({ + id: Principal, + audio: blob, + createdAt: nat64, + name: text, + userId: Principal +}); + +let users = StableBTreeMap(Principal, User, 0); +let recordings = StableBTreeMap(Principal, Recording, 1); ``` Notice that each `StableBTreeMap` has a unique `memory id`. The maximum key and value sizes are also set according to the expected application usage. From 330feda41765b5451f8fdae7d94a0c2f05b76e8f Mon Sep 17 00:00:00 2001 From: Jordan Last Date: Mon, 2 Oct 2023 09:13:03 -0500 Subject: [PATCH 03/10] working on the azle book --- README.md | 30 +- the_azle_book/book/404.html | 2 +- the_azle_book/book/azle.html | 33 +- the_azle_book/book/candid.html | 834 +++++---- the_azle_book/book/canisters_overview.html | 6 +- the_azle_book/book/cross_canister.html | 2 +- the_azle_book/book/cycles.html | 2 +- the_azle_book/book/deployment.html | 2 +- the_azle_book/book/examples.html | 6 +- the_azle_book/book/http.html | 5 +- the_azle_book/book/index.html | 2 +- the_azle_book/book/installation.html | 12 +- .../book/internet_computer_overview.html | 6 +- the_azle_book/book/print.html | 1567 ++++++++--------- the_azle_book/book/searchindex.js | 2 +- the_azle_book/book/searchindex.json | 2 +- the_azle_book/book/timers.html | 2 +- the_azle_book/src/SUMMARY.md | 4 +- the_azle_book/src/azle.md | 28 +- 19 files changed, 1234 insertions(+), 1313 deletions(-) diff --git a/README.md b/README.md index 14e2ec9ae3..8566aa2236 100644 --- a/README.md +++ b/README.md @@ -15,25 +15,15 @@ # Azle (Beta) -TypeScript CDK for the [Internet Computer](https://internetcomputer.org/). +TypeScript and JavaScript CDK for the [Internet Computer](https://internetcomputer.org/). ## Disclaimer Azle may have unknown security vulnerabilities due to the following: -- Azle does not yet have many live, successful, continuously operating applications deployed to the IC - Azle does not yet have extensive automated property tests - Azle does not yet have multiple independent security reviews/audits -- Azle heavily relies on Boa which is [self-proclaimed to be experimental](https://github.com/boa-dev/boa#boa) - -## Roadmap - -We hope to get to a production-ready 1.0 in 2024. The following are the major blockers to 1.0: - -- QuickJS/SpiderMonkey integration for performance, security, and stability -- Broad npm package support -- Extensive automated property testing -- Multiple independent security reviews/audits +- Azle does not yet have many live, successful, continuously operating applications deployed to the IC ## Documentation @@ -43,6 +33,22 @@ See [The Azle Book](https://demergent-labs.github.io/azle/). Feel free to open issues or join us in the [Discord channel](https://discord.gg/5Hb6rM2QUM). +## Roadmap + +We hope to move Azle from beta to release candidates by the end of 2023, and to move from release candidates to 1.0 in early 2024. + +### Blockers for release candidates + +- Good automated property test coverage +- Settling of API/syntax +- Good npm package support + +### Blockers for 1.0 + +- Extensive automated property test coverage +- Multiple independent security reviews/audits +- Broad npm package support + ## Contributing All contributors must agree to and sign the [Azle License Extension](/LICENSE_EXTENSION.md). diff --git a/the_azle_book/book/404.html b/the_azle_book/book/404.html index 069ea6598a..cc0bf4e3f6 100644 --- a/the_azle_book/book/404.html +++ b/the_azle_book/book/404.html @@ -84,7 +84,7 @@ diff --git a/the_azle_book/book/azle.html b/the_azle_book/book/azle.html index bcf8a237c2..926c025b08 100644 --- a/the_azle_book/book/azle.html +++ b/the_azle_book/book/azle.html @@ -83,7 +83,7 @@ @@ -145,27 +145,32 @@

The Azle Book

Azle (Beta)

-

Azle is a TypeScript Canister Development Kit (CDK) for the Internet Computer (IC). In other words, it's a TypeScript/JavaScript runtime for building applications (canisters) on the IC.

+

Azle is a TypeScript and JavaScript Canister Development Kit (CDK) for the Internet Computer (IC). In other words, it's a TypeScript/JavaScript runtime for building applications (canisters) on the IC.

Disclaimer

Azle may have unknown security vulnerabilities due to the following:

    -
  • Azle does not yet have many live, successful, continuously operating applications deployed to the IC
  • Azle does not yet have extensive automated property tests
  • Azle does not yet have multiple independent security reviews/audits
  • -
  • Azle heavily relies on Boa which is self-proclaimed to be experimental
  • +
  • Azle does not yet have many live, successful, continuously operating applications deployed to the IC

Roadmap

-

We hope to get to a production-ready 1.0 in 2024. The following are the major blockers to 1.0:

+

We hope to move Azle from beta to release candidates by the end of 2023, and to move from release candidates to 1.0 in early 2024.

+

Blockers for release candidates

    -
  • QuickJS/SpiderMonkey integration for performance, security, and stability
  • -
  • Broad npm package support
  • -
  • Extensive automated property testing
  • +
  • Good automated property test coverage
  • +
  • Settling of API/syntax
  • +
  • Good npm package support
  • +
+

Blockers for 1.0

+
    +
  • Extensive automated property test coverage
  • Multiple independent security reviews/audits
  • +
  • Broad npm package support

Demergent Labs

Azle is currently developed by Demergent Labs, a for-profit company with a grant from DFINITY.

@@ -174,8 +179,8 @@

Azle and the IC provide unique benefits and drawbacks, and both are not currently suitable for all application use-cases.

The following information will help you to determine when Azle and the IC might be beneficial for your use-case.

Benefits

-

Azle intends to be a full TypeScript/JavaScript environment for the IC (a decentralized cloud platform), with support for all of the TypeScript/JavaScript language and as many relevant host APIs as possible. These host APIs will be similar to those available in the Node.js and web browser environments.

-

One of the core benefits of Azle is that it allows web developers to bring their TypeScript/JavaScript skills to the IC. For example, Azle allows the use of various npm packages and VS Code intellisense.

+

Azle intends to be a full TypeScript and JavaScript environment for the IC (a decentralized cloud platform), with support for all of the TypeScript and JavaScript language and as many relevant environment APIs as possible. These environment APIs will be similar to those available in the Node.js and web browser environments.

+

One of the core benefits of Azle is that it allows web developers to bring their TypeScript or JavaScript skills to the IC. For example, Azle allows the use of various npm packages and VS Code intellisense.

As for the IC, we believe its main benefits can be broken down into the following categories:

import {
-    Alias,
     ic,
     $init,
     match,
@@ -237,7 +236,7 @@ 

diff --git a/the_azle_book/book/installation.html b/the_azle_book/book/installation.html index 7adec042f9..59fe0abd9a 100644 --- a/the_azle_book/book/installation.html +++ b/the_azle_book/book/installation.html @@ -83,7 +83,7 @@ @@ -149,7 +149,7 @@

InstallationYou should be using a *nix environment (Linux, Mac OS, WSL if using Windows) with bash and have the following installed on your system:

  • Node.js 18
  • -
  • dfx 0.14.2
  • +
  • dfx 0.15.0

Node.js

We highly recommend using nvm to install Node.js (and npm, which is included with Node.js). Run the following commands to install Node.js and npm with nvm:

@@ -159,8 +159,8 @@

Node.js

nvm install 18
 

dfx

-

Run the following command to install dfx 0.14.2:

-
DFX_VERSION=0.14.2 sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)"
+

Run the following command to install dfx 0.15.0:

+
DFX_VERSION=0.15.0 sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)"
 

If after trying to run dfx commands you encounter an error such as dfx: command not found, you might need to add $HOME/bin to your path. Here's an example of doing this in your .bashrc:

echo 'export PATH="$PATH:$HOME/bin"' >> "$HOME/.bashrc"
@@ -170,7 +170,7 @@ 

dfx