diff --git a/contributing_versioned_docs/version-latest/01-start-here/01-contributing-to-wing.md b/contributing_versioned_docs/version-latest/01-start-here/01-contributing-to-wing.md index 10d1073ed..a6a451568 100644 --- a/contributing_versioned_docs/version-latest/01-start-here/01-contributing-to-wing.md +++ b/contributing_versioned_docs/version-latest/01-start-here/01-contributing-to-wing.md @@ -11,14 +11,14 @@ to know to help out with the Wing project. There are many ways to contribute to Wing: * Reporting bugs through a [GitHub issue](https://github.com/winglang/wing/issues) -* Writing [documentation and guides](https://github.com/winglang/wing/issues?q=is:issue+is:open+sort:updated-desc+label:documentation) or adding [examples](docs) -* Setting up your [development environment](./development) and working on the code -* Submitting [pull requests](./pull_requests) for new features or helping with [reviews](https://github.com/winglang/wing/pulls) -* Picking up a [good first issue](https://github.com/winglang/wing/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+no%3Aassignee+sort%3Aupdated-desc+) to work on -* Reporting and fixing [bugs](./bugs) -* Contributing to the [Wing SDK (standard library)](./wingsdk) -* Find solutions to common issues in our [troubleshooting guide](./troubleshooting) -* Commenting on [RFCs](/category/rfcs) or submitting one for major features +* Writing [documentation and guides](https://github.com/winglang/wing/issues?q=is:issue+is:open+sort:updated-desc+label:documentation) or adding [examples](/contributing/start-here/docs#%EF%B8%8F-how-do-i-add-an-example) +* Setting up your [development environment](/contributing/start-here/development) and working on the code +* Submitting [pull requests](/contributing/start-here/pull_requests) for new features or helping with [reviews](https://github.com/winglang/wing/pulls) +* Picking up a [good first issue](https://github.com/winglang/wing/issues?q=is%3Aissue+is%3Aopen+label%3A%221%EF%B8%8F%E2%83%A3+good+first+issue%22+no%3Aassignee+sort%3Aupdated-desc) to work on +* Reporting and fixing [bugs](/contributing/start-here/bugs) +* Contributing to the [Wing SDK (standard library)](/contributing/start-here/wingsdk) +* Find solutions to common issues in our [troubleshooting guide](/contributing/start-here/troubleshooting) +* Commenting on [RFCs](/contributing/category/rfcs) or submitting one for major features * Asking and answering questions in the [Wing Slack](https://t.winglang.io/slack) * Posting or answering questions in [Wing Discussions](https://github.com/winglang/wing/discussions) diff --git a/contributing_versioned_docs/version-latest/01-start-here/05-development.md b/contributing_versioned_docs/version-latest/01-start-here/05-development.md index f83bb7422..ddd036f34 100644 --- a/contributing_versioned_docs/version-latest/01-start-here/05-development.md +++ b/contributing_versioned_docs/version-latest/01-start-here/05-development.md @@ -207,7 +207,7 @@ The [insta](https://marketplace.visualstudio.com/items?itemName=mitsuhiko.insta) To debug the Rust compiler on VSCode, first you need to install the [CodeLLDB extension](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb).
Next, you can use the `Debug Wing Compiler` launch configuration available on our [launch.json](https://github.com/winglang/wing/blob/main/.vscode/launch.json). -Hit F5 to start debugging. You'll be prompted to insert the path to the `.w` file you want to debug.
You can use the `${workspaceFolder}/examples/tests/valid/hello.w` file for example. +Open the `.w` file you wish to debug compilation for (e.g. `${workspaceFolder}/examples/tests/valid/hello.w`) and hit F5 to start debugging. ## How do I make changes to the Wing grammar? diff --git a/versioned_docs/version-latest/01-start-here/02-installation.md b/versioned_docs/version-latest/01-start-here/02-installation.md index bae25440d..3057edcc4 100644 --- a/versioned_docs/version-latest/01-start-here/02-installation.md +++ b/versioned_docs/version-latest/01-start-here/02-installation.md @@ -23,14 +23,14 @@ The toolchain includes three tools: 1. **Wing CLI** - the compiler toolchain 2. **Wing VSCode Extension** - IDE support for Wing -3. **Wing Console** - a desktop app for interacting with your Wing programs. +3. **Wing Console** - a web application for viewing and interacting with your Wing programs. To install Wing, you will need the following setup: * [Node.js](https://nodejs.org/en/) (v18 or later) - * We recommend [volta](https://volta.sh) to manage node tools + * We recommend [volta](https://volta.sh) to manage node tools * [VSCode] - * Not required, but currently supported with an [extension](#wing-ide-extension) + * Not required, but currently supported with an [extension](#wing-ide-extension) ## Wing CLI @@ -83,26 +83,9 @@ to use it, but it's great. It's available through the VSCode Marketplace [here]( ## Wing Console -:::caution Wing Console doesn't support Linux yet - -Click :thumbsup: on [this issue](https://github.com/winglang/wing/issues/723) to indicate that you -are looking for Linux support. - -::: - - -The Wing Console is a desktop application that allows you to interact with your -Wing applications running locally on the cloud simulator. - -1. Download the latest version of the **Wing Console**: - * [macOS](https://wing-console.s3.amazonaws.com/wing-console.dmg) - * [macOS arm64](https://wing-console.s3.amazonaws.com/wing-console-arm64.dmg) - * [Windows](https://wing-console.s3.amazonaws.com/wing-console.exe) -2. Accept the license agreement. -3. In macOS: drag the Wing Console app into **Applications** - -![Drag the Wing Console app into Applications](./console-install.png 'Wing Console app installation') +The Wing Console is a web application that provides a developer-friendly interface for viewing, exploring, and interacting with your Wing applications running on the local cloud simulator. +The Wing Console is included as part of the Wing CLI package. There is no need to install it separately. Once the Wing CLI is successfully installed, you can directly access the Wing Console and begin utilizing its features. [AWS account]: https://portal.aws.amazon.com/billing/signup [AWS CLI]: https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html diff --git a/versioned_docs/version-latest/01-start-here/04-run-locally.md b/versioned_docs/version-latest/01-start-here/04-run-locally.md index f4fc02e27..dadecaa9d 100644 --- a/versioned_docs/version-latest/01-start-here/04-run-locally.md +++ b/versioned_docs/version-latest/01-start-here/04-run-locally.md @@ -4,8 +4,7 @@ id: local keywords: [Wing Local, Wing testing, Wing Console installation, Wing Console usage, Wing Console setup] --- -The Wing Console is a graphical user interface that can be used to interact with -Wing applications on the local machine. +The Wing Console is a web application that provides a developer-friendly interface that can be used to view, explore, and interact with Wing applications running on your local machine. ## Opening your app in the console @@ -14,12 +13,9 @@ Wing applications on the local machine. Make sure you have [installed](./installation#wing-console) the Wing Console on your system before getting started with this step. -If you are unable to install the Wing Console (e.g. you are running on Linux), you can -also test your application against the [Wing simulator](../concepts/simulator) or [deploy it to AWS](./aws). - ::: -We can use the Wing CLI to start the console with our newly created Wing source file: +Use the Wing CLI to start the console with our newly created Wing source file: ```sh wing it hello.w @@ -30,46 +26,41 @@ and load the application. > The console will "hot reload" your app on each change in the source file parent directory. -Wing Console desktop application will show the following view of you application: +In your Wing Console you will see the following view of you application: ![Wing Console desktop application view](./console-app.png 'Wing Console') You are now able to run your IDE and the Wing Console side-by-side and watch how changes to your code update your application. -## The console view +## The Wing Console view -In the main view you'll see two resources: a **Queue** and a **Function**. -You'll also notice that the function is connected to the queue through the -`message` event. +In the console [map view](../tools/wing-console#view-and-explore-your-wing-application) you'll see three resources: a **Queue**, a **Function** and a **Bucket**. +The Queue and the Bucket are connected through the Function resource, which serves as a message handler and is set as the consumer for the Queue. -Once you click on the queue resource, the console will navigate into the queue -resource. The inspector pane on the right will show information about your -queue, as well as any relationships (in our case, an outbound relationship to -the function that handles messages). +Click on the Queue resource and pay attention to the right hand panel. This is the [resource interaction panel](../tools/wing-console#interact-with-your-wing-application). ![Queue resource view in Wing Console](./console-queue.png 'Queue resource') -## Sending a message to the queue +## Push a message to the Queue -In the center you should be able to type in a message and send it to the queue. -Type `Wing` and hit **Send Message**. +On the right side interaction panel view, Type a message in the `Push Message` text area (let's say `Wing it`) and hit **Push** in order to push it to the queue. -## Viewing the file +## View the file in the Bucket -On the left sidebar click on the Bucket, you will see the following view: +Now, click on the Bucket in the Console map view. Notice that the interaction panel has changed and now shows the Bucket's interaction view. -![Bucket resource view in Wing Console](./console-bucket-1.png 'Bucket resource') +Check out the `wing.txt` file, click on it and see it's preview. -Now, check the `wing.txt` file and download it, using the download button +![Bucket resource view in Wing Console](./console-bucket-1.png 'Bucket resource') -![Download bucket files in Wing Console](./console-bucket-2.png 'Download bucket files') +You can also download the file, using the **download** button. -The downloaded file should contain `Hello, Wing` text +The downloaded file should contain `Hello, Wing it` text (as you already saw in the preview). ## Congrats! :clap: -You have just written and tested your first Wing program! +You have just written and ran your first Wing program! --- -Now, after you have tested your application, lets deploy it to AWS. +Now, after you have made sure your application works, lets deploy it to AWS. diff --git a/versioned_docs/version-latest/01-start-here/console-app.png b/versioned_docs/version-latest/01-start-here/console-app.png index efa4c80a4..bcf04f57d 100644 Binary files a/versioned_docs/version-latest/01-start-here/console-app.png and b/versioned_docs/version-latest/01-start-here/console-app.png differ diff --git a/versioned_docs/version-latest/01-start-here/console-bucket-1.png b/versioned_docs/version-latest/01-start-here/console-bucket-1.png index c33f58597..cc51f6043 100644 Binary files a/versioned_docs/version-latest/01-start-here/console-bucket-1.png and b/versioned_docs/version-latest/01-start-here/console-bucket-1.png differ diff --git a/versioned_docs/version-latest/01-start-here/console-bucket-2.png b/versioned_docs/version-latest/01-start-here/console-bucket-2.png deleted file mode 100644 index 09f953470..000000000 Binary files a/versioned_docs/version-latest/01-start-here/console-bucket-2.png and /dev/null differ diff --git a/versioned_docs/version-latest/01-start-here/console-install.png b/versioned_docs/version-latest/01-start-here/console-install.png deleted file mode 100644 index 3c8acfe0a..000000000 Binary files a/versioned_docs/version-latest/01-start-here/console-install.png and /dev/null differ diff --git a/versioned_docs/version-latest/01-start-here/console-queue.png b/versioned_docs/version-latest/01-start-here/console-queue.png index 1bf67da35..733054fc8 100644 Binary files a/versioned_docs/version-latest/01-start-here/console-queue.png and b/versioned_docs/version-latest/01-start-here/console-queue.png differ diff --git a/versioned_docs/version-latest/02-concepts/03-compile-targets.md b/versioned_docs/version-latest/02-concepts/03-compile-targets.md index d93b41697..c61a05b96 100644 --- a/versioned_docs/version-latest/02-concepts/03-compile-targets.md +++ b/versioned_docs/version-latest/02-concepts/03-compile-targets.md @@ -33,7 +33,7 @@ These targets contain a combination of provisioning engine and cloud environment ## Portability -Wing's [cloud library](docs/docs/04-standard-librarylibrary/01-cloud) has several classes representing abstracted cloud resources, whose APIs are not specific to a single cloud provider. This allows you to write code that can be deployed to any supported cloud environment or provisioning engine. +Wing's [cloud library](/docs/standard-library/cloud/api-reference) has several classes representing abstracted cloud resources, whose APIs are not specific to a single cloud provider. This allows you to write code that can be deployed to any supported cloud environment or provisioning engine. Here's an example of a portable code snippet: @@ -44,7 +44,7 @@ let bucket = new cloud.Bucket(); ``` Each resource needs a dedicated implementation to work on a given compiler target. -A catalog of which resources are supported on each cloud can be found [here](docs/docs/04-standard-library/03-winglang-support-matrix). +A catalog of which resources are supported on each cloud can be found [here](/docs/standard-library/compatibility-matrix). ### Provisioning engines diff --git a/versioned_docs/version-latest/03-language-guide/80-equality.md b/versioned_docs/version-latest/03-language-guide/80-equality.md index f413ac5ee..d8c87b8b5 100644 --- a/versioned_docs/version-latest/03-language-guide/80-equality.md +++ b/versioned_docs/version-latest/03-language-guide/80-equality.md @@ -11,7 +11,7 @@ Checking for equality is performed with the `==` operator. It returns `true` if Equality in Wing is a symmetric and transitive relationship - that is, (1) if `a == b`, then `b == a`, and (2) if `a == b` and `b == c`, then `a == c`. -The execution phase ([preflight or inflight](../02-core-concepts/01-preflight-and-inflight.md)) that a value was created in does not affect its equality. For example, a value created in preflight can be equal to a value created in inflight. +The execution phase ([preflight or inflight](/docs/concepts/inflights)) that a value was created in does not affect its equality. For example, a value created in preflight can be equal to a value created in inflight. Some types are compared *by value*, which means that two values are equal if their contents are equivalent. For example, two `str` values are equal if they have the same characters in the same order, even if they are stored in different places in memory. diff --git a/versioned_docs/version-latest/03-language-guide/90-reference.md b/versioned_docs/version-latest/03-language-guide/90-reference.md index 23557f6f7..80777cb30 100644 --- a/versioned_docs/version-latest/03-language-guide/90-reference.md +++ b/versioned_docs/version-latest/03-language-guide/90-reference.md @@ -1,22 +1,15 @@ --- -title: Language reference -id: reference -description: The Wing Language Specification +title: Language Reference +id: language-reference +description: The Wing Language Reference keywords: [Wing reference, Wing language, language, Wing language spec, Wing programming language] --- -:::caution Not fully implemented yet - -This document is a *specification* of the programming language, and many features -are still not implemented (see [project board](https://github.com/orgs/winglang/projects/1)). - -::: - ## 0. Preface ### 0.1 Motivation -The Wing Programming Language (aka winglang[RFC](/2022-05-28-winglang-reqs)) is a general +The Wing Programming Language (aka Winglang) is a general purpose programming language designed for building applications for the cloud. What makes Wing special? Traditional programming languages are designed around @@ -68,40 +61,18 @@ import TOCInline from '@theme/TOCInline'; | Name | Extra information | | ------ | ---------------------------------- | | `void` | represents the absence of a type | -| `nil` | represents the absence of a value | -| `any` | represents everything and anything | | `num` | represents numbers (doubles) | | `str` | UTF-16 encoded strings | | `bool` | represents true or false | -> `any` is only available to JSII imported modules. - -User defined explicit "any" is supported iff declared by the user. -Almost all types can be implicitly resolved by the compiler except for "any". -"any" must be explicitly declared and annotated. - > ```TS > let x = 1; // x is a num > let v = 23.6; // v is a num > let y = "Hello"; // y is a str > let z = true; // z is a bool -> let w: any = 1; // w is an any > let q: num? = nil; // q is an optional num > ``` -
Equivalent TypeScript Code - -> ```TS -> const x: number = 1; -> const v: number = 23.6; -> const y: string = "Hello"; -> const z: boolean = true; -> const w: any = 1; -> const q: number? = undefined; -> ``` - -
- [`▲ top`][top] --- @@ -116,9 +87,6 @@ Almost all types can be implicitly resolved by the compiler except for "any". | `MutSet` | mutable set type | | `MutMap` | mutable map type | | `MutArray` | mutable array type | -| `Promise` | promise type (inflight code) | - -> `Promise` is only available to JSII imported modules. > ```TS > let z = {1, 2, 3}; // immutable set, Set is inferred @@ -157,8 +125,8 @@ The `inflight` modifier indicates that a function is an inflight function. `inflight` in Wing implies `async` in JavaScript. ```pre -(arg1: , arg2: , ...) => -inflight (arg1: , arg2: , ...) => +(arg1: , arg2: , ...): => +inflight (arg1: , arg2: , ...): => ``` > ```TS @@ -168,16 +136,33 @@ inflight (arg1: , arg2: , ...) => > let f2 = inflight (x: num, s: str) => { /* no-op */ }; > ``` +Return type is required for function types. +> ```TS +> let my_func = (callback: (num): void) => { }; +> let my_func2 = (callback: ((num): void): (str): void) => { }; +> ``` + +Return type is optional for closures. +> ```TS +> let my_func3 = (x: num) => { }; +> let my_func4 = (x: num): void => { }; +> let my_func5 = inflight (x: num) => { }; +> let my_func6 = inflight (x: num): void => { }; +> ``` + [`▲ top`][top] --- #### 1.1.4 Json type -Wing has a data type called `Json` (alias is `json`). This type represents an immutable untyped [JSON +> 🚧 Json support is still a work in progress 🚧
+> Check out the [roadmap](#1149-roadmap) section below, to see what parts are still not implemented. + +Wing has a primitive data type called `Json`. This type represents an immutable untyped [JSON value](https://www.json.org/json-en.html), including JSON primitives (`string`, `number`, `boolean`), arrays (both heterogenous and homogenous) and objects (key-value maps where keys are -strings and values can be any other JSON value)). +strings and values can be any other JSON value). `Json` objects are immutable and can be referenced across inflight context. @@ -186,35 +171,34 @@ since Wing is statically-typed (type must be known during compilation) and JSON (type is only known at runtime), bridging is required between these two models. Let's look at a quick example: - ```js struct Employee { id: str; name: str; } -let response = httpGet("/employees"); +let response = http.get("/employees"); // returns something like { "items": [ { "id": "12234", "name": "bob" }, ... ] } - -let employees = Array.fromJson(response.items); + +let employees = Array.fromJson(response.get("items")); //NOTE: Array.fromJson is currently not implemented for e in employees { log("hello, ${e.name}, your employee id is ${e.id}"); } ``` - -In the above example, the `httpGet` function returns a `Json` object from the server that has a +In the above example, the `http.get` function returns a `Json` object from the server that has a single field `items`, with a JSON array of JSON objects, each with an `id` and `name` fields. -The expression `response.items` returns a `Json` array, and we use `Array.fromJson` to convert -this array from `Json` to an `Array`. Note that by default `fromJson` will perform schema -validation on the array and on each item (based on the declaration of the `Employee` struct). +The expression `response.get("items")` returns a `Json` array, and we use `Array.fromJson` to +convert this array from `Json` to an `Array`. Note that by default `fromJson` will +perform schema validation on the array and on each item (based on the declaration of the `Employee` +struct). ##### 1.1.4.1 Literals Literals can be defined using the `Json` type initializers: -```js +```TS let jsonString = Json "hello"; let jsonNumber = Json 123; let jsonBool = Json true; @@ -229,7 +213,7 @@ let jsonMutObj = MutJson { The `Json` keyword can be omitted from `Json` object literals: -```js +```TS let jsonObj = { boom: 123, bam: [4, 5, 6] }; ``` @@ -237,63 +221,42 @@ Every value within a `Json` array or object also has a type of `Json`. ##### 1.1.4.2 JSON objects -To access a field within an object, use the `.` notation: +To access a field within an object, use `.get("{field name}")`: -```js -let boom: Json = jsonObj.boom; +```TS +let boom: Json = jsonObj.get("boom"); ``` Trying to access a non-existent field will fail at runtime. For example: -```js -log(jsonObj.boom.dude.world); -// RUNTIME ERROR: Uncaught TypeError: Cannot read properties of undefined (reading 'world') -``` - -Like in JavaScript, it is also possible to access object fields using `[]`: - -```js -let foo = j["my-field"].yourField["their-field"]; +```TS +log("${jsonObj.get("boom").get("dude").get("world")}"); +// ERROR: Cannot read properties of undefined (reading 'world') ``` To obtain an array of all the keys within a JSON object use the `Json.keys(o)` method. - -```js +```TS let j = Json { hello: 123, world: [ 1, 2, 3 ] }; -assert(Json.keys(j) == ["hello", "world"]); +assert(Json.keys(j).at(0) == "hello"); +assert(Json.keys(j).at(1) == "world"); ``` -To obtain an array of all the values, use `Json.values(o)`. To obtain an array of all key/value -pairs use `Json.entries(o)` (P2): +To obtain an array of all the values, use `Json.values(o)`: -```js +```TS assert(Json.values(j).equals([ Json 123, Json [ 1, 2, 3 ] ])); -assert(Json.entries(j).equals([ - [ Json "hello", Json 123 ], - [ Json "world", Json [ 1, 2, 3 ] ] -])); ``` -> NOTE: `values()` and `entries()` return an array inside a `Json` object because at the moment we +> NOTE: `values()` returns an array inside a `Json` object because at the moment we > cannot represent heterogenous arrays in Wing. -##### 1.1.4.3 JSON arrays - -To access an array element, use the `[]` notation: - -```js -let item2 = jsonArray[2]; // type: Json -``` - -Trying to index a value that is not an array will return JavaScript `undefined`. - -##### 1.1.4.4 Assignment from native types +##### 1.1.4.3 Assignment from native types It is also possible to assign the native `str`, `num`, `bool` and `Array` values and they will implicitly be casted to `Json`: -```js +```TS let myStr: str = "hello"; let myNum: num = 183; let myBool: bool = true; @@ -307,13 +270,12 @@ let jsonObj = Json { }; ``` - -##### 1.1.4.5 Assignment to native types +##### 1.1.4.4 Assignment to native types We only allow implicit assignment from *safe* to *unsafe* types because otherwise we cannot guarantee safety (e.g. from `str` to `Json` but not from `Json` to `str`), so this won't work: -```js +```TS let j = Json "hello"; let s: str = j; // ^ cannot assign `Json` to `str`. @@ -322,144 +284,62 @@ let s: str = j; To assign a `Json` to a strong-type variable, use the `fromJson()` static method on the target type: -```js +```TS let myStr = str.fromJson(jsonString); let myNumber = num.fromJson(jsonNumber); -let myArr = Array.fromJson(jsonArray); ``` -##### 1.1.4.6 Schema validation +##### 1.1.4.5 Schema validation All `fromJson()` methods will validate that the runtime type is compatible with the target type in order to ensure type safety (at a runtime cost): -```js +```TS str.fromJson(jsonNumber); // RUNTIME ERROR: unable to parse number `123` as a string. num.fromJson(Json "\"hello\""); // RUNTIME ERROR: unable to parse string "hello" as a number - -let myArray = Json [1,2,3,"hello"]; -Array.fromJson(myArray); // RUNTIME ERROR: unable to parse `[1,2,3,"hello"]` as an array of `num`. ``` -Use `unsafe: true` to disable this check at your own risk (P2): - -```js -let trustMe = Json [1,2,3]; -let x = Array.fromJson(trustMe, unsafe: true); -assert(x.at(1) == 2); -``` - -For each `fromJson()`, there is a `tryFromJson()` method which returns an optional `T?` which -indicates if parsing was successful or not: - -```js -let s = str.tryFromJson(myJson) ?? "invalid string"; -``` - -##### 1.1.4.7 Assignment to user-defined structs - -All [structs](#31-structs) also have a `fromJson()` method that can be used to parse `Json` into a -struct: - -```js -struct Contact { - first: str; - last: str; - phone: str?; -} - -let j = Json { first: "Wing", last: "Lyly" }; -let myContact = Contact.fromJson(j); -assert(myContact.first == "Wing"); -``` - -When a `Json` is parsed into a struct, the schema will be validated to ensure the result is -type-safe: - -```js -let p = Json { first: "Wing", phone: 1234 }; -Contact.fromJson(p); -// RUNTIME ERROR: unable to parse Contact: -// - field "last" is required and missing -// - field "phone" is expected to be a string, got number. -``` - -Same as with primitives and containers, it is possible to opt-out of validation using `unsafe: -true`: - -```js -let p = Json { first: "Wing", phone: 1234 }; -let x = Contact.fromJson(p, unsafe: true); -assert(x.last.len > 0); -// RUNTIME ERROR: Cannot read properties of undefined (reading 'length') -``` - -Struct parsing is *partial* by default. This means that parsing is successful even if the `Json` -includes extraneous fields: - -```js -let p = Json { first: "hello", last: "world", anotherField: "ignored" }; -let c = Contact.fromJson(p); -assert(c.first == "hello"); -assert(c.last == "world"); -// `c.anotherField` is not a thing -``` - -This can be disabled using `partial: false` (P2): - -```js -Contact.fromJson(Json { first: "hello", last: "world", anotherField: "ignored" }, partial: false); -// RUNTIME ERROR: cannot parse Contact due to extraneous field "anotherField" -``` - -##### 1.1.4.7 Schemas - -Structs have a `schema` static method which returns a `JsonSchema` object (P2): - -```js -let schema = Contact.schema(); -schema.validate(j); -``` - -##### 1.1.4.8 Mutability +##### 1.1.4.6 Mutability To define a mutable JSON container, use the `MutJson` type: -```js +```TS let myObj = MutJson { hello: "dear" }; ``` Now you can mutate the contents by assigning values: -```js +```TS +let myObj = MutJson { hello: "dear" }; let fooNum = 123; -myObj.world = "world"; -myObj.dang = [1,2,3,4]; -myObj.subObject = {}; -myObj.subObject.arr = [1,"hello","world"]; -myObj.foo = fooNum; +myObj.set("world", "world"); +myObj.set("dang", [1,2,3,4]); +myObj.set("subObject", MutJson {}); +myObj.get("subObject").set("arr", MutJson [1,"hello","world"]); +myObj.set("foo", fooNum); ``` For the sake of completeness, it is possible to also define primitives using `MutJson` but that's not very interesting because there is no way to mutate them: -```js +```TS let foo = MutJson "hello"; // ok what now? ``` -Use the `MutJson.deepCopy()` method to get an immutable *deep copy* of the object: +Use the `Json.deepCopyMut(MutJson json)` method to get an mutable *deep copy* of a `Json` object. +Use the `MutJson.deepCopy(Json json)` method to get an immutable *deep copy* of a `MutJson` object: -```js +```TS let mutObj = MutJson { hello: 123 }; -let immutObj = mutObj.deepCopy(mutObj); -mutObj.hello = 999; -assert(immutObj.hello == 123); +let immutObj = MutJson.deepCopy(mutObj); +mutObj.set("hello", 999); +assert(immutObj.get("hello") == 123); ``` To delete a key from an object, use the `Json.delete()` method: -```js +```TS let myObj = MutJson { hello: 123, world: 555 }; Json.delete(myObj, "world"); @@ -468,101 +348,57 @@ Json.delete(immutObj, "hello"); // ^^^^^^^^^ expected `JsonMut` ``` -To modify a Json array, you will need to parse it into a native `MutArray` and then modify it. This -implies that at the moment, it is not possible to mutate heterogenous JSON arrays: - -```js -let j1 = MutJson { hello: [1,2,3,4] }; -let a1 = MutArray.fromJson(j1); -a1.push(5); - -j1.hello = a1; -``` - -> We will need to revisit this as we progress if this is a major use case. - -##### 1.1.4.9 Serialization +##### 1.1.4.7 Serialization The `Json.stringify(j: Json): str` static method can be used to serialize a `Json` as a string ([JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)): -```js +```TS +let jsonString = Json "hello"; +let jsonObj = Json { boom: 123 }; assert(Json.stringify(jsonString) == "\"hello\""); assert(Json.stringify(jsonObj) == "{\"boom\":123}"); -assert(Json.stringify(jsonMutObj, indent: 2) == "{\n\"hello\": 123,\n" \"world\": [\n 1,\n 2,\n 3\n ],\n \"boom\": {\n \"hello\": 1233\n }\n}"); ``` The `Json.parse(s: str): Json` static method can be used to parse a string into a `Json`: -```js -let jArray = Json.parse("[1,2,3]"); -let arr = Array.fromJson(jArray); +```TS +let j = Json.parse("{ \"boom\": 123 }"); +let boom = num.fromJson(j.get("boom")); ``` `Json.tryParse` returns an optional: -```js +```TS let o = Json.tryParse("xxx") ?? Json [1,2,3]; ``` -##### 1.1.4.10 Equality, diff and patch - -The `Json.equals(lhs: Json, rhs: Json): bool` static method can be used to determine if two values -are equal (recursively comparing arrays and objects): - -```js -assert(Json.equals(jsonString, Json "hello")); -assert(Json.equals(jsonObj, { boom: [ 1, 2, 3 ] })); -assert(!Json.equals(Json { hello: [ 1, 2, 3 ] }, Json { hello: [ 1, 2 ] })); -``` - -The `Json.diff(lhs: Json, rhs: Json): JsonPatch` static method can be used to calculate the deep -difference between two JSON values. It returns a list of differences in -[json-patch](https://jsonpatch.com/) format (P2). - -```js -let j1 = Json { - baz: "qux", - foo: "bar" -}; - -let j2 = Json { - baz: "boo", - hello: ["world"] -}; - -assert(Json.diff(j1, j2) = [ - { op: JsonPatch.REPLACE, path: "/baz", value: "boo" }, - { op: JsonPatch.ADD, path: "/hello", value: ["world"] }, - { op: JsonPatch.REMOVE, path: "/foo" } -]); -``` - -The `Json.patch(j: Json, patch: JsonPatch): Json` static method applies a `JsonPatch` to a `Json` object (P2). - -##### 1.1.4.11 Logging +##### 1.1.4.8 Logging A `Json` value can be logged using `log()`, in which case it will be pretty-formatted: -```js +```TS log("my object is: ${jsonObj}"); // is equivalent to -log("my object is: ${Json.stringify(jsonObj, indent: 2)}"); +log("my object is: ${Json.stringify(jsonObj)}"); ``` This will output: -```js +```TS my object is: { boom: 123 } ``` -It is also legal to just log a json object: +#### 1.1.4.9 Roadmap -```js -log(jsonMutObj); -``` +The following features are not yet implemented, but we are planning to add them in the future: + +* Array/Set/Map.fromJson() - see https://github.com/winglang/wing/issues/1796 to track. +* Json.entries() - see https://github.com/winglang/wing/issues/3142 to track. +* Schema validation and assignment to struct - see https://github.com/winglang/wing/issues/3139 to track. +* Equality, diff and patch - see https://github.com/winglang/wing/issues/3140 to track. [`▲ top`][top] @@ -574,7 +410,7 @@ The `Duration` (alias `duration`) type represents a time duration. Duration literals are numbers with `m`, `s`, `h` suffixes: -```js +```TS let oneMinute = 1m; let twoSeconds = 2s; let threeHours = 3h; @@ -583,7 +419,7 @@ let halfMinute: duration = 0.5m; Then: -```js +```TS assert(oneMinute.seconds == 60); assert(halfMinute.seconds == 30); assert(threeHours.minutes == 180); @@ -591,109 +427,47 @@ assert(threeHours.minutes == 180); Duration objects are immutable and can be referenced across inflight context. -#### 1.1.6 `Datetime` +#### 1.1.5.1 Roadmap -The `Datetime` (alias `datetime`) type represents a single moment in time in a platform-independent +An additional built-in `datetime` type is planned and not yet implemented. `datetime` represents a single moment in time in a platform-independent format. - -`Datetime` objects are immutable and can be referenced across inflight context. - -Here is the initial API for the `Datetime` type: - -```js -struct DatetimeComponents { - year: num; - month: num; - day: num; - hour: num; - min: num; - sec: num; - ms: num; - tz: num; // timezone offset in minutes from UTC -} - -class Datetime { - static utcNow(): Datetime; // returns the current time in UTC timezone - static systemNow(): Datetime; // returns the current time in system timezone - static fromIso(iso: str): Datetime; // creates an instance from an ISO-8601 string - static fromComponents(c: DatetimeComponents): Datetime; - - timestamp: num; // Date.valueOf()/1000 (non-leap seconds since epoch) - timestampMs: num; // Date.valueOf() (non-leap milliseconds since epoch) - - hours: num; // Date.getHours() - min: num; // Date.getMinutes() - sec: num; // Date.getSeconds() - ms: num; // Date.getMilliseconds() - day: num; // Date.getDay() - month: num; // Date.getMonth() - year: num; // Date.getFullYear() - - timezone: num; // Date.getTimezoneOffset() (offset in minutes from UTC) - utc: Datetime; // returns the same time in UTC timezone - - toIso(): str; // returns ISO-8601 string -} -``` - -A few examples: - -```js -let now = Datetime.utcNow(); -log("It is now ${now.month}/${now.day}/${now.year} at ${now.hours}:${now.min}:${now.sec})"); -assert(now.timezone == 0); // UTC - -let t1 = DateTime.fromIso("2023-02-09T06:20:17.573Z"); -log("Timezone is GMT${d.timezone() / 60}"); // output: Timezone is GMT-2 -log("UTC: ${t1.utc.toIso())}"); // output: 2023-02-09T06:21:03.000Z -``` - -[`▲ top`][top] - ---- - +See https://github.com/winglang/wing/issues/2102 to track. ### 1.2 Utility Functions | Name | Extra information | | -------- | -------------------------------------------------------- | -| `log` | logs anything serializable. | +| `log` | logs str | | `throw` | creates and throws an instance of an exception | | `panic` | exits with a serializable, dumps the trace + a core dump | | `assert` | checks a condition and _panics_ if evaluated to false | -Wing is a statically typed language, so attempting to redefine any of the above -functions, just like any other "symbol" will result in a compile-time error. - -The above functions can accept variadic arguments of any type except `throw` which -only accepts one argument and that is the message to be contained in the error. - `panic` is a fatal call by design. If the intention is error handling, panic is the last resort. Exceptions are non fatal and should be used instead for effectively communicating errors to the user. > ```TS -> log(23, "Hello", true, { "a": 1, "b": 2 }); +> log("Hello ${name}"); > throw("a recoverable error occurred"); -> panic("a fatal error encountered", [1,2]); -> assert(x > 0, x < 10); +> panic("a fatal error encountered"); +> assert(x > 0); > ```
Equivalent TypeScript Code > ```TS -> console.log(23, "Hello", true, Object.freeze(new Map([["a", 1], ["b", 2]]))); +> console.log("Hello ${name}"); > // throws > throw new Error("a recoverable error occurred"); > // calling panic in wing is fatal > (() => { -> console.error("Something went wrong", [1,2]); +> console.error("Something went wrong"); > // generate core dump > // show stack trace > process.exit(1); > })(); -> // multiple assertions -> (() => { assert.ok(x > 0); assert.ok(x < 10); })(); +> // assertion +> (() => { assert.ok(x > 0); })(); > ```
@@ -750,11 +524,11 @@ inflight class Foo { For example (continuing the `Bucket` example above): -```ts +```TS let bucket = new Bucket(); // OK! We are calling a preflight method from a preflight context bucket.allowPublicAccess(); -// ERROR: cannot call inflight methods from preflight context +// ERROR: Cannot call into inflight phase while preflight bucket.put("file.txt", "hello"); let handler = inflight () => { @@ -772,7 +546,7 @@ class Bar {} new Bar(); // OK! Bar is a preflight class let handler2 = inflight() => { - new Bar(); // ERROR: cannot instantiate a preflight class from an inflight context + new Bar(); // ERROR: Cannot create preflight class "Bar" in inflight phase } ``` @@ -805,9 +579,6 @@ A declaration for a static member is a member declaration whose declaration specifiers contain the keyword static. The keyword static must appear before other specifiers. More details in the [classes](#32-classes) section. -The name of any static data member and static member function must be different -from the name of the containing class regardless of the casing. - Code samples for `static` are not shown here. They are shown in the relevant sections below. @@ -815,38 +586,19 @@ To avoid confusion, it is invalid to have a static and a non-static with the same name. Overloading a static is allowed however. Accessing static is done via the type name and the `.` operator. -[`▲ top`][top] - ---- - -### 1.5 Access Modifiers - -Visibility inference is done with the following rules: - -- Default visibility is `private` for all members. If modifiers are missing, the - symbol is assumed private by the compiler and not exported. -- `public` is to declare a symbol that is visible for and exported publicly. -- `protected` is to declare a symbol that is visible for and exported publicly - but only for the class and its subclasses. -- `internal` is to declare a symbol that is visible for and exported publicly - but only for the current compilation unit. - -Accessing fields, members, or structured data is done with `.`. - -Visibility modifiers can be applied to members of classes. -Mixing `protected` and `internal` is not allowed. +Static class fields are not supported yet, see https://github.com/winglang/wing/issues/1668 [`▲ top`][top] --- -### 1.6 Reassignability +### 1.5 Reassignability Re-assignment to variables that are defined with `let` is not allowed in Wing. Variables can be reassigned to by adding the `var` modifier: -```ts +```TS // wing let var sum = 0; for item in [1,2,3] { @@ -864,21 +616,21 @@ to assigning non `readonly`s to `readonly`s in TypeScript. By default function closure arguments are non-reassignable. By prefixing `var` to an argument definition you can make a re-assignable function argument: -```ts +```TS // wing let f = (arg1: num, var arg2: num) => { if (arg2 > 100) { // We can reassign a value to arg2 since it's marked `var` - args2 = 100; + arg2 = 100; } -} +}; ``` [`▲ top`][top] --- -### 1.7 Optionality +### 1.6 Optionality Nullity is a primary source of bugs in software. Being able to guarantee that a value will never be null makes it easier to write safe code without constantly having to take nullity into account. @@ -897,24 +649,17 @@ Here's a quick summary of how optionality works in Wing: block only if `x` has a value. Otherwise, the `else` block will be executed. * The `x?.y?.z` notation can be used to access fields only if they have a value. The type of this expression is `Z?` (an optional based on the type of the last component). -* The `x ?? y ?? z` notation will return the value in `x` if there is one, `y` otherwise or `z`. The - last expression in a `??` chain (e.g. `z`) must be of type `T` (not `T?`). -* The default value notation (`= y`) in declarations of struct fields or function arguments will use - this value if a value is not provided, and implies type is `T` (not `T?`). -* The `x ??= y` notation returns `x` if it has a value or assigns `x` with `y` and returns the value - of `y`. -* The `x ?? throw(message)` and `x ?? return val` are special cases of `??` which can be used for - unwrapping (if a value exists) or early bailout. +* The `x ?? y` notation will return the value in `x` if there is one, `y` otherwise. * The keyword `nil` can be used in assignment scenarios to indicate that an optional doesn't have a value. It cannot be used to test if an optional has a value or not. -#### 1.7.1 Declaration +#### 1.6.1 Declaration -##### 1.7.1.1 Struct fields +##### 1.6.1.1 Struct fields One of the more common use cases for optionals is to use them in struct declarations. -```js +```TS struct Person { name: str; address: str?; @@ -924,51 +669,18 @@ struct Person { In the `Person` struct above, the `address` field is marked as optional using `?`. This means that we can initialize without defining the `address` field: -```js +```TS let david = Person { name: "david" }; let jonathan = Person { name: "jonathan", address: "earth" }; assert(david.address? == false); assert(jonathan.address? == true); ``` -The *default value notation* (`=`) can also be used in struct declarations. If provided, the field -is also not required in a struct literal definition, and the default value will be implied. It also -means that the type of the field must be `T` and not `T?`, because we can ensure it has a value (in -the example below the field `radix` as a type of `num`). - -```js -struct FormatOpts { - radix: num = 10; - someOptional: str?; -} - -let opts = FormatOpts {}; -assert(opts.radix == 10); -assert(opts.someOptional? == false); // <-- no value inside `someOptional` -``` - -A value can be omitted from a struct literal if the field is optional _or_ if it has a default value -in the struct declaration. If an optional field doesn't have a default value, its type must be `T?` -(`someOptional` above). If it has a default value it's type must be `T` (`radix` above). - -This is a compilation error: - -```js -struct Test { - hello: str? = "hello"; -// ^^^^ type should be `str` since a default value is provided -} -``` - -> NOTE: Default values can only be serializable values (immutable primitives, collections of -> primitives or other serializable structs). This limitation exists because we will evaluate the -> expression of the default value only upon struct initialization (it is stored in the type system). - -##### 1.7.1.2 Variables +##### 1.6.1.2 Variables Use `T?` to indicate that a variable is optional. To initialize it without a value use `= nil`. -```js +```TS let var x: num? = 12; let var y: num? = nil; assert(y? == false); // y doesn't have a value @@ -982,11 +694,11 @@ x = nil; assert(x? == false); ``` -##### 1.7.1.3 Class fields +##### 1.6.1.3 Class fields Similarly to struct fields, fields of classes can be also defined as optional using `T?`: -```js +```TS class Foo { myOpt: num?; var myVar: str?; @@ -1002,12 +714,12 @@ class Foo { } ``` -##### 1.7.1.4 Function arguments +##### 1.6.1.4 Function arguments In the following example, the argument `by` is optional, so it is possible to call `increment()` without supplying a value for `by`: -```js +```TS let increment = (x: num, by: num?): num => { return x + (by ?? 1); }; @@ -1016,77 +728,73 @@ assert(increment(88) == 89); assert(increment(88, 2) == 90); ``` -Alternatively, using the default value notation can be used to allow a parameter not to be assigned -when calling the function. Using a default value in the function declaration ensures that `by` -always has a value so there is no need to unwrap it (this is why its type is `num` and not `num?`): - -```js -let increment = (x: num, by: num = 1): num { - return x + by; -} -``` - Non-optional arguments can only be used before all optional arguments: -```js -let myFun = (a: str, x?: num, y: str): void = { /* ... */ }; +```TS +let myFun = (a: str, x: num?, y: str): void => { /* ... */ }; //-----------------------------^^^^^^ ERROR: cannot declare a non-optional argument after an optional ``` If a function uses a keyword argument struct as the last argument, and there are other optional arguments before, it also has to be declared as optional. -```js +```TS let parseInt = (x: str, radix: num?, opts?: ParseOpts): num { /* ... */ }; -// or -let parseInt = (x: str, radix: num = 10, opts: ParseOpts = ParseOpts {}): num { /* ... */ }; ``` The optionality of keyword arguments is determined by the struct field's optionality: -```js +```TS struct Options { myRequired: str; myOptional: num?; - implicitOptional: bool = false; } -let f = (opts: Options) => { } +let f = (opts: Options) => { }; f(myRequired: "hello"); f(myOptional: 12, myRequired: "dang"); -f(myRequired: "dude", implicitOptional: true); ``` -##### 1.7.1.5 Function return types +##### 1.6.1.5 Function return types If a function returns an optional type, use the `return nil;` statement to indicate that the value is not defined. -```js -struct Name { first: str, last: str }; +```TS +struct Name { + first: str; + last: str; +} let tryParseName = (fullName: str): Name? => { let parts = fullName.split(" "); - if parts.len < 2 { + if parts.length < 2 { return nil; } return Name { first: parts.at(0), last: parts.at(1) }; -} +}; // since result is optional, it needs to be unwrapped in order to be used if let name = tryParseName("Neo Matrix") { - print("Hello, ${name.first}!"); + log("Hello, ${name.first}!"); } ``` -#### 1.7.2 Testing using `x?` +#### 1.6.2 Testing using `x?` To test if an optional has a value or not, you can either use `x == nil` or `x != nil` or the special syntax `x?`. -```js +```TS +struct MyPerson { + name: str; + address: str?; +} +let myPerson = MyPerson {name: "John", address: nil}; + + let isAddressDefined = myPerson.address?; // type is `bool` let isAddressReallyDefined = myPerson.address != nil; // equivalent @@ -1101,19 +809,19 @@ if !myPerson.address? { } if myPerson.address == nil { - log("no address") + log("no address"); } ``` -#### 1.7.3 Unwrapping using `if let` +#### 1.6.3 Unwrapping using `if let` The `if let` statement can be used to test if an optional is defined and *unwrap* it into a non-optional variable defined inside the block: -```js +```TS if let address = myPerson.address { - print(address.len); - print(address); // address is type `str` + log("${address.length}"); + log(address); // type of address is `str` } ``` @@ -1121,52 +829,52 @@ if let address = myPerson.address { > multiple conditions, or unwrapping multiple optionals. This is something we might consider in the > future. -#### 1.7.4 Unwrapping or default value using `??` +#### 1.6.4 Unwrapping or default value using `??` The `??` operator can be used to unwrap or provide a default value. This returns a value of `T` that can safely be used. -```js +```TS let address: str = myPerson.address ?? "Planet Earth"; ``` -`??` can be chained: - -```js -let address = myPerson.address ?? yourPerson.address ?? "No address"; -// <----- str? ----> <----- str? ------> <-- str ---> -``` - -The last element in a `??` chain must be a non-optional type `T`. - -#### 1.7.5 Optional chaining using `?.` +#### 1.6.5 Optional chaining using `?.` The `?.` syntax can be used for optional chaining. Optional chaining returns a value of type `T?` which must be unwrapped in order to be used. -```js +```TS let ipAddress: str? = options.networking?.ipAddress; if let ip = ipAddress { - print("the ip address is defined and it is: ${ip}"); + log("the ip address is defined and it is: ${ip}"); } ``` -#### 1.7.6 Roadmap +[`▲ top`][top] -In the future we will consider the following additional sugar syntax: +--- + +#### 1.6.6 Roadmap +The following features are not yet implemented, but we are planning to add them in the future: + +* Default value: the default value notation (`= y`) may appear in declarations of struct fields, class fields or function arguments. + See https://github.com/winglang/wing/issues/3121 to track. +* Chaining `??` operations: the `x ?? y ?? z` notation will return the value in `x` if there is one, `y` otherwise or `z`. The last expression in a `??` chain (e.g. `z`) must be of type `T` (not `T?`). + See https://github.com/winglang/wing/issues/1875 to track. +* The `x ??= y` notation returns `x` if it has a value or assigns `x` with `y` and returns the value of `y`. + See https://github.com/winglang/wing/issues/2103 to track. * `x ?? throw("message")` to unwrap `x` or throw if `x` is not defined. + See https://github.com/winglang/wing/issues/2103 to track. * `x ??= value` returns `x` or assigns a value to it and returns it to support lazy evaluation/memoization (inspired by [Nullish coalescing assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_assignment)). + See https://github.com/winglang/wing/issues/2103 to track. * Support `??` for different types if they have a common ancestor (and also think of interfaces). + See https://github.com/winglang/wing/issues/2103 to track. -[`▲ top`][top] - ---- - -### 1.8 Type Inference +### 1.7 Type Inference Type can optionally be put between name and the equal sign, using a colon. Partial type inference is allowed while using the `?` keyword immediately after @@ -1178,16 +886,15 @@ r-value refers to the right hand side of an assignment here. All defined symbols are immutable (constant) by default. Type casting is generally not allowed unless otherwise specified. -Function arguments and their return type is always required. Function argument -type is inferred iff a default value is provided. +Function arguments and their return type are always required. > ```TS > let i = 5; > let m = i; -> let arrOpt? = MutArray[]; +> let arrOpt: MutArray? = MutArray []; > let arr = Array[]; > let copy = arr; -> let i1? = nil; +> let i1: num? = nil; > let i2: num? = i; > ``` @@ -1196,11 +903,11 @@ type is inferred iff a default value is provided. > ```TS > const i: number = 5; > const m: number = i; -> const arrOpt: number[]? = []; -> const arr: number[] = Object.freeze([]); -> const copy: number[] = Object.freeze([...arr]); -> const i1: any = undefined; -> const i2: number? = i; +> const arrOpt: number[] | undefined = []; +> const arr: readonly number[] = Object.freeze([]); +> const copy: readonly number[] = Object.freeze([...arr]); +> const i1: number | undefined = undefined; +> const i2: number | undefined = i; > ``` @@ -1209,7 +916,7 @@ type is inferred iff a default value is provided. --- -### 1.9 Error Handling +### 1.8 Error Handling Exceptions and `try/catch/finally` are the error mechanism. Mechanics directly translate to JavaScript. You can create a new exception with a `throw` call. @@ -1221,7 +928,7 @@ In the presence of `catch` the variable holding the exception (`e` in the exampl `throw` is meant to be recoverable error handling. An uncaught exception is considered user error but a panic call is not. Compiler -must guarantee exception safety by throwing a compile error if an exception is +guarantees exception safety by throwing a compile error if an exception is expected from a call and it is not being caught. > ```TS @@ -1239,7 +946,7 @@ expected from a call and it is not being caught. > ```TS > try { -> let x: number? = 1; +> let x: number | undefined = 1; > throw new Error("hello exception"); > } catch (e) { > console.log(e); @@ -1254,13 +961,13 @@ expected from a call and it is not being caught. --- -### 1.10 Recommended Formatting +### 1.9 Recommended Formatting Wing recommends the following formatting and naming conventions: - Interface names should start with capital letter "I". -- Class, struct, and interface names should be TitleCased. -- Members of classes, and interfaces cannot share the same TitleCased +- Class, struct, and interface names should be PascalCased. +- Members of classes, and interfaces cannot share the same PascalCased representation as the declaring expression itself. - Parentheses are optional in expressions. Any Wing expression can be surrounded by parentheses to enforce precedence, which implies that the expression inside @@ -1270,7 +977,7 @@ Wing recommends the following formatting and naming conventions: --- -### 1.11 Memory Management +### 1.10 Memory Management There is no implicit memory de-allocation function, dynamic memory is managed by Wing and is garbage collected (relying on JSII target GC for the meantime). @@ -1279,7 +986,7 @@ Wing and is garbage collected (relying on JSII target GC for the meantime). --- -### 1.12 Execution Model +### 1.11 Execution Model Execution model currently is delegated to the JSII target. This means if you are targeting JSII with Node, Wing will use the event based loop that Node offers. @@ -1321,7 +1028,7 @@ AWS CDK or `TerraformApp` in case of CDK for Terraform target. --- -### 1.13 Asynchronous Model +### 1.12 Asynchronous Model Wing builds upon the asynchronous model of JavaScript currently and expands upon it with new keywords and concepts. The `async` keyword of JavaScript is replaced @@ -1332,22 +1039,25 @@ Main concepts to understand: - `preflight` implies synchronous execution. - `inflight` implies asynchronous execution. -Contrary to JavaScript, any call to an async function is implicitly awaited in Wing. As -a result, `await` in Wing is a rarely used keyword, since its use is implied by -the `inflight` keyword. `await` is only used when you want a `defer`ed `Promise` -to be fulfilled before execution flow continues. +Contrary to JavaScript, any call to an async function is implicitly awaited in Wing. -The Wing compiler emits `await`s when encountering `Promise` types as r-values -in expressions. Use the `defer` keyword to defer the resolution of a promise and -obtain a `Promise` type instead (a.k.a un`await` what the compiler does). +#### 1.12.1 Roadmap -The `Promise` type is not allowed to hold nested promises in `T`. +The following features are not yet implemented, but we are planning to add them in the future: + +* `await`/`defer` statements - see https://github.com/winglang/wing/issues/116 to track. +* Promise function type - see https://github.com/winglang/wing/issues/1004 to track. + +### 1.13 Roadmap + +Access modifiers (`private`/`public`/`internal`/`protected`) are not yet implemented. +See https://github.com/winglang/wing/issues/108 to track. ## 2. Statements ### 2.1 bring -**bring** statement can be used to import and reuse code from other Wing files or +**bring** statement can be used to import and reuse code from other JSII supported languages. The statement is detailed in its own section in this document: [Module System](#4-module-system). @@ -1357,15 +1067,15 @@ this document: [Module System](#4-module-system). ### 2.2 break -**break** statement allows to end execution of a cycle. This includes for and -while loops currently. +**break** statement allows to end execution of a cycle. This includes `for` and +`while` loops. > ```TS -> for let i in 1..10 { +> for i in 1..10 { > if i > 5 { > break; > } -> log(i); +> log("${i}"); > } > ``` @@ -1392,11 +1102,11 @@ while loops currently. includes for and while loops currently. > ```TS -> for let i in 1..10 { +> for i in 1..10 { > if i > 5 { > continue; > } -> log(i); +> log("${i}"); > } > ``` @@ -1423,10 +1133,10 @@ includes for and while loops currently. > ```TS > class MyClass { -> public myPublicMethod() {} -> private _myPrivateMethod(): void {} -> protected myProtectedMethod(): nil { return nil; } -> internal _myInternalMethod(): str { return "hi!"; } +> myMethod() {} +> myMethod2(): void {} +> myMethod3(): void { return; } +> myMethod4(): str { return "hi!"; } > } > ``` @@ -1434,11 +1144,10 @@ includes for and while loops currently. > ```TS > class MyClass { -> public myPublicMethod(): void {} -> private myPrivateMethod(): undefined {} -> protected myProtectedMethod(): undefined { return undefined; } -> // specific compiled instruction is up to implementation of the compiler -> public __wing_InternalMyInternalMethod(): string { return "hi!"; } +> public myMethod(): void {} +> public myMethod2(): void {} +> public myMethod3(): void { return; } +> public myMethod4(): string { return "hi!"; } > } > ``` @@ -1448,54 +1157,7 @@ includes for and while loops currently. --- -### 2.5 defer/await - -> Read [Asynchronous Model](#114-asynchronous-model) section as a prerequisite. - -You mostly do not need to use **defer** and **await** keywords in Wing. -"defer" prevents the compiler from awaiting a promise and grabs a reference. -"await" and "Promise" are semantically similar to JavaScript's promises. -"await" statement is only valid in `inflight` function declarations. -Awaiting non promises in Wing is a no-op just like in JavaScript. - -> ```TS -> // Wing program: -> class MyClass { -> inflight foo(): num { -> let w = defer somePromise(); -> let x = await w; -> return x; -> } -> inflight boo(): num { -> let x = somePromise(); -> return x; -> } -> } -> ``` - -
Equivalent TypeScript Code - -> ```TS -> class MyClass { -> async foo(): Promise { -> const w = somePromise(); -> const x = Object.freeze(await w); -> return x; -> } -> async boo(): Promise { -> const x = Object.freeze(await somePromise()); -> return x; -> } -> } -> ``` - -
- -[`▲ top`][top] - ---- - -### 2.6 if +### 2.5 if Flow control can be done with `if/elif/else` statements. The **if** statement is optionally followed by **elif** and **else**. @@ -1533,10 +1195,10 @@ The **if** statement is optionally followed by **elif** and **else**. --- -### 2.7 for +### 2.6 for -`for..in` statement is used to iterate over an array or a set. -Type annotation after an iteratee (left hand side of **in**) is optional. +`for..in` statement is used to iterate over an array, a set or a range. +Range is inclusive of the start value and exclusive of the end value. The loop invariant in for loops is implicitly re-assignable (`var`). > ```TS @@ -1544,20 +1206,20 @@ The loop invariant in for loops is implicitly re-assignable (`var`). > let arr = [1, 2, 3]; > let set = {1, 2, 3}; > for item in arr { -> log(item); +> log("${item}"); > } > for item: num in set { -> log(item); +> log("${item}"); > } > for item in 0..100 { -> log(item); +> log("${item}"); // prints 0 to 99 > } > ```
Equivalent TypeScript Code > ```TS -> const arr: number[] = Object.freeze([1, 2, 3]); +> const arr: readonly number[] = Object.freeze([1, 2, 3]); > const set: Set = Object.freeze(new Set([1, 2, 3])); > for (const item of arr) { > console.log(item); @@ -1566,7 +1228,7 @@ The loop invariant in for loops is implicitly re-assignable (`var`). > console.log(item); > } > // calling 0..100 does not allocate, just returns an iterator -> function* iterator(start, end) { +> function* iterator(start: number, end: number) { > let i = start; > while (i < end) yield i++; > while (i > end) yield i--; @@ -1583,7 +1245,7 @@ The loop invariant in for loops is implicitly re-assignable (`var`). --- -### 2.8 while +### 2.7 while **while** statement is used to execute a block of code while a condition is true. @@ -1627,11 +1289,11 @@ Structs can inherit from multiple other structs. > struct MyDataModel1 { > field1: num; > field2: str; -> }; +> } > struct MyDataModel2 { > field3: num; > field4: bool?; -> }; +> } > struct MyDataModel3 extends MyDataModel1, MyDataModel2 { > field5: str; > } @@ -1651,16 +1313,16 @@ Structs can inherit from multiple other structs. > ```TS > interface MyDataModel1 { -> public readonly field1: number; -> public readonly field2: string; +> readonly field1: number; +> readonly field2: string; > } > interface MyDataModel2 { -> public readonly field3: number; -> public readonly field4?: boolean; +> readonly field3: number; +> readonly field4?: boolean; > } > interface MyDataModel3 extends MyDataModel1, MyDataModel2 { -> public readonly field5: string; -> public readonly field6: number; +> readonly field5: string; +> readonly field6: number; > } > const s1: MyDataModel1 = { field1: 1, field2: "sample" }; > const s2: MyDataModel2 = { field3: 1, field4: true }; @@ -1699,10 +1361,14 @@ program's entrypoint), it will be a *preflight class*. If a class is declared wi scope, it will be implicitly an inflight class. A method that has the name **init** is considered to be a class -constructor (or initializer, or allocator). +constructor (or initializer). ```TS inflight class Name extends Base impl IMyInterface1, IMyInterface2 { + // class fields + _field1: num; + _field2: str; + init() { // constructor implementation // order is up to user @@ -1710,35 +1376,17 @@ inflight class Name extends Base impl IMyInterface1, IMyInterface2 { this._field2 = "sample"; } - // class fields (private by due to having leading underscore) - _field1: num; - _field2: str; - // static method (access with Name.staticMethod(...)) - public static staticMethod(arg: type, arg: type, ...) { /* impl */ } - // private method - private _privateMethod(arg: type, arg: type, ...): type { /* impl */ } + static staticMethod(arg: type, arg: type, ...) { /* impl */ } // visible to outside the instance - public publicMethod(arg:type, arg:type, ...) { /* impl */ } - // visible to children only - protected protectedMethod(type:arg, type:arg, ...) { /* impl */ } - // public in current compilation unit only - internal _internalMethod3(type:arg, type:arg, ...): type { /* impl */ } + publicMethod(arg:type, arg:type, ...) { /* impl */ } } ``` +If no `init()` is defined, the class will have a default constructor that does nothing. -Default initialization does not exist in Wing. All member fields must be +Implicit default field initialization does not exist in Wing. All member fields must be initialized in the constructor. Absent initialization is a compile error. All -field types, including the optional types must be initialized. Optionals are -initialized to `nil` if omitted, unless the type is `nil?`, which in that case, -absent initialization is a compile error. - -Member function and field access in constructor with the "this" keyword before -all fields are initialized is invalid and would throw a compile error. - -In other words, the `this` keyword is immutable to its field access operator `.` -before all the member fields are properly initialized. The behavior is similar -to JavaScript and TypeScript in their "strict" mode. +field types, including the optional types must be initialized. ```TS class Foo { @@ -1749,23 +1397,20 @@ class Bar { y: num; z: Foo; init() { - // this.log() // is compile error here this.y = 1; - // this.log() // is also compile error here this.z = new Foo(); this.log(); // OK to call here } public log() { - log(this.y); + log("${this.y}"); } } let a = new Bar(); -a.log(); // logs 20. +a.log(); // logs 1 ``` -Overloading methods is allowed. This means functions can be overloaded with many +Overloading methods is currently not allowed. This means functions cannot be overloaded with many signatures only varying in the number of arguments and their unique type order. -Overloading the constructor is also allowed. Inheritance is allowed with the `extends` keyword. `super` can be used to access the base class, immediately up the inheritance chain (parent class). @@ -1785,15 +1430,11 @@ class Boo extends Foo { super(); this.x = 10; // OK } - public override method() { - // override implementation - } } ``` Classes can inherit and extend other classes using the `extends` keyword. Classes can implement interfaces iff the interfaces do not contain `inflight`. -You can use the keyword `final` to stop the inheritance chain. ```TS class Foo { @@ -1801,33 +1442,30 @@ class Foo { init() { this.x = 0; } public method() { } } -final class Boo extends Foo { +class Boo extends Foo { init() { super(); this.x = 10; } - public override method() { - // override implementation - } } -// compile error -// class FinalBoo extends Boo {} -``` -By default all methods are virtual. But if you are about to override a method, -you need to explicitly provide the keyword **override**. -Static, private, and internal methods cannot be and are not virtual. +``` Statics are not inherited. As a result, statics can be overridden mid hierarchy chain. Access to statics is through the class name that originally defined it: `.Foo`. -Child class must not introduce additional signatures (overloads) for overridden -(virtual) methods. - Multiple inheritance is invalid and forbidden. Multiple implementations of various interfaces is allowed. Multiple implementations of the same interface is invalid and forbidden. In methods if return type is missing, `: void` is assumed. +#### Roadmap + +The following features are not yet implemented, but we are planning to add them in the future: + +* Overloading class methods (including `init`) - see https://github.com/winglang/wing/issues/3123 to track. +* Overriding class methods - see https://github.com/winglang/wing/issues/1124 to track. +* Using the `final` keyword to stop the inheritance chain - see https://github.com/winglang/wing/issues/460 to track. + [`▲ top`][top] --- @@ -1842,35 +1480,50 @@ For example: ```TS // Wing Code: class Foo { - init() { /* initialize preflight fields */ } // preflight constructor - inflight init() {} // optional client initializer + // preflight fields + field1: num; + field2: str; + field3: bool; - // inflight members - inflight foo(arg: num): num { return arg; } - inflight boo(): num { return 32; } + // re-assignable class fields (preflight, in this case), read about them in the mutability section + var field4: num; + var field5: str; // inflight fields - inflight field1: num; - inflight field2: str; - inflight field3: bool; - - // preflight members - foo(arg: num): num { return arg; } - boo(): num { return 32; } + inflight field6: num; + inflight field7: str; + inflight field8: bool; + + // preflight constructor + init(field1: num, field2: str, field3: bool, field4: num, field5: str) { + /* initialize preflight fields */ + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + this.field4 = field4; + this.field5 = field5; + } + + // inflight constructor + inflight init() { + /* initialize inflight fields */ + this.field6 = 123; + this.field7 = "hello"; + this.field8 = true; + } - // preflight fields - field4: num; - field5: str; - field6: bool; + // preflight methods + foo1(arg: num): num { return arg; } + boo1(): num { return 32; } - // re-assignable class fields, read about them in the mutability section - var field4: num; - var field5: str; + // inflight methods + inflight foo2(arg: num): num { return arg; } + inflight boo2(): num { return 32; } } ``` Preflight objects all have a scope and a unique ID. Compiler provides an implicit scope -and ID for each object, both overrideable by user-defined ones in constructor. +and ID for each object. The default for scope is `this`, which means the scope in which the object was defined (instantiated). The implicit ID is the type name of the class iff the type @@ -1882,8 +1535,8 @@ Preflight objects instantiated at block scope root level of entrypoint are assig root app as their default implicit scope. Preflight object instantiation syntax uses the `let` keyword the same way variables are declared in -Wing. The `as` and `in` keywords can be used to customize the scope and identifier assigned to this -preflight object. +Wing. The `as` and `in` keywords can be used to customize the identifier and scope assigned to this +preflight object respectively. ```pre let [: ] = new () [as ] [in ]; @@ -1891,12 +1544,11 @@ let [: ] = new () [as ] [in ]; ```TS // Wing Code: -let a = Foo(); // with default scope and id -let a = Foo() in scope; // with user-defined scope +let a = new Foo(); // with default scope and id +let a = new Foo() in scope; // with user-defined scope let a = new Foo() as "custom-id" in scope; // with user-defined scope and id let a = new Foo(...) as "custom-id2" in scope; // with constructor arguments ``` - "id" must be of type string. It can also be a string literal with substitution support (normal strings as well as shell strings). "scope" must be an expression that resolves to a preflight object. @@ -1905,12 +1557,11 @@ Preflight objects can be captured into inflight scopes and once that happens, in the capture block only the inflight members are available. Preflight classes can extend other preflight classes (but not [structs](#31-structs)) and implement -[interfaces](#34-interfaces). If a class implements an interface marked `inflight interface`, then -all of the implemented methods must be `inflight`. +[interfaces](#34-interfaces). -Declaration of fields of the same name with different phases is not allowed due to requirement of -having inflight fields of same name being implicitly initialized by the compiler. But declaration -of methods with different phases is allowed. +Declaration of fields of the same name with different phases is not allowed due to the requirement of +having inflight fields of same name being implicitly initialized by the compiler. Declaration +of methods with different phases is not allowed as well. [`▲ top`][top] @@ -1920,12 +1571,13 @@ of methods with different phases is allowed. Interfaces represent a contract that a class must fulfill. Interfaces are defined with the `interface` keyword. -Both preflight and inflight signatures are allowed. -If the `inflight` modifier is used in the interface declaration, all methods will be automatically considered inflight methods. `impl` keyword is used to implement an interface or multiple interfaces that are +Currently, preflight interfaces are allowed, while inflight interfaces are not supported yet (see https://github.com/winglang/wing/issues/1961). +`impl` keyword is used to implement an interface or multiple interfaces that are separated with commas. All methods of an interface are implicitly public and cannot be of any other type of visibility (private, protected, etc.). +Return type is required for interface methods. Interface fields are not supported. @@ -1934,18 +1586,17 @@ Interface fields are not supported. > interface IMyInterface1 { > method1(x: num): str; > inflight method3(): void; -> }; +> } > -> inflight interface IMyInterface2 { -> method2(): str; // <-- "inflight" is implied -> }; +> interface IMyInterface2 { +> method2(): str; +> } > > class MyResource impl IMyInterface1, IMyInterface2 { > field1: num; > field2: str; > -> inflight init(x: num) { -> // inflight client initialization +> init(x: num) { > this.field1 = x; > this.field2 = "sample"; > } @@ -1953,22 +1604,22 @@ Interface fields are not supported. > return "sample: ${x}"; > } > inflight method3(): void { } -> inflight method2(): str { +> method2(): str { > return this.field2; > } -> }; +> } > ```
Equivalent TypeScript Code > ```TS > interface IMyInterface1 { -> public readonly field1: number; -> public method1(x: number): string; +> readonly field1: number; +> method1(x: number): string; > } > interface IMyInterface2 { -> public readonly __inflight__field2: string; -> public __inflight__method2(): string; +> readonly __inflight__field2: string; +> __inflight__method2(): string; > } > // this is only shown as a hypothetical sample > class MyResource extends constructs.Construct @@ -2000,7 +1651,7 @@ Interface fields are not supported. > Let let be let. (Elad B. 2022) ```pre -let [: ] = ; +let [var] [: ] = [] ; ``` Assignment operator is `=`. @@ -2036,46 +1687,21 @@ However, it is possible to create anonymous closures and assign to variables > ```TS > // preflight closure: -> let f1 = (a: num, b: num) => { log(a + b); } +> let f1 = (a: num, b: num) => { log("${a + b}"); }; > // inflight closure: -> let f2 = inflight (a: num, b: num) => { log(a + b); } +> let f2 = inflight (a: num, b: num) => { log("${a + b}"); }; > // OR: > // preflight closure: -> let f4 = (a: num, b: num): void => { log(a + b); } +> let f4 = (a: num, b: num): void => { log("${a + b}"); }; > // inflight closure: -> let f5 = inflight (a: num, b: num): void => { log(a + b); } -> ``` - -[`▲ top`][top] - ---- - -#### 3.6.2 Promises - -Promises in Wing are defined with `Promise` syntax. -All `inflight` functions implicitly wrap their return type in `Promise`. - -> ```TS -> let schema = inflight (): Struct => { -> return someCallForSchema(); -> } -> ``` - -
Equivalent TypeScript Code - -> ```TS -> const schema = async (): Promise => { -> return await someCallForSchema(); -> } +> let f5 = inflight (a: num, b: num): void => { log("${a + b}"); }; > ``` - -
[`▲ top`][top] --- -#### 3.6.3 Struct Expansion +#### 3.6.2 Struct Expansion If the last argument of a function call is a struct, then the struct in the call is "expandable" with a special `:` syntax. @@ -2093,33 +1719,20 @@ is safe to omit optional struct fields, or have order of arguments mixed. struct MyStruct { field1: num; field2: num; -}; -let f = (x: num, y: num, z: MyStruct) => { - log(x + y + z.field1 + z.field2); } +let f = (x: num, y: num, z: MyStruct) => { + log("${x + y + z.field1 + z.field2}"); +}; // last arguments are expanded into their struct f(1, 2, field1: 3, field2: 4); // f(1, 2, field1: 3); // can't do this, partial expansion is not allowed ``` -[`▲ top`][top] - ---- - -#### 3.6.4 Variadic Arguments +#### 3.6.3 Roadmap -If the last argument of a function type is the `...args` keyword followed by an -`Array` type, then the function accepts typed variadic arguments. Expansion of -variadic arguments is not supported currently and the container of variadic -arguments is accessible with the `args` key like a normal array instance. +The following features are not yet implemented, but we are planning to add them in the future: -```TS -let f = (x: num, ...args: Array) => { - log(x + y + args.len); -} -// last arguments are expanded into their array -f(1, 2, 3, 4, 5, 6, 34..100); -``` +* Variadic arguments (`...args`) - see https://github.com/winglang/wing/issues/125 to track. [`▲ top`][top] @@ -2128,21 +1741,21 @@ f(1, 2, 3, 4, 5, 6, 34..100); ### 3.7 Arrays `Array`s are dynamically sized in Wing and are defined with the `[]` syntax. -Individual array items are also accessed with the `[]` syntax. +Individual array items are accessed using the `.at(index: num)` method. Arrays are similar to dynamically sized arrays or vectors in other languages. > ```TS > let arr1 = [1, 2, 3]; > let arr2 = ["a", "b", "c"]; > let arr3 = MutArray["a1", "b2", "c3"]; -> let l = arr1.len + arr2.len + arr3.len + arr1[0]; +> let l = arr1.length + arr2.length + arr3.length + arr1.at(0); > ```
Equivalent TypeScript Code > ```TS -> const arr1: number[] = Object.freeze([1, 2, 3]); -> const arr2: string[] = Object.freeze(["a", "b", "c"]); +> const arr1: readonly number[] = Object.freeze([1, 2, 3]); +> const arr2: readonly string[] = Object.freeze(["a", "b", "c"]); > const arr3: string[] = ["a1", "b2", "c3"]; > const l = arr1.length + arr2.length + arr3.length + arr1[0]; > ``` @@ -2157,17 +1770,16 @@ Arrays are similar to dynamically sized arrays or vectors in other languages. Enumeration type (`enum`) is a type that groups a list of named constant members. Enumeration is defined by writing **enum**, followed by enumeration name and a -list of comma-separated constants in a {}. Last comma is optional in single line -definitions but required in multi line definitions. +list of comma-separated constants in a {}. Naming convention for enums is to use "TitleCase" for name and ALL_CAPS for members. > ```TS -> enum SomeEnum { ONE, TWO, THREE }; +> enum SomeEnum { ONE, TWO, THREE } > enum MyFoo { > A, > B, > C, -> }; +> } > let x = MyFoo.B; > let y = x; // type is MyFoo > ``` @@ -2187,113 +1799,16 @@ Naming convention for enums is to use "TitleCase" for name and ALL_CAPS for memb
-`nameof` operator is used to get the name of a constant member at compile time. -For example `nameof(MyEnum.MEMBER)` resolves to `"MEMBER"` at compile time. - -This allows painless conditionals when enums are serialized and deserialized -over the wire without littering the source with strings everywhere. Compare: - -```TS -// Wing Code: -enum SomeEnum { ONE, TWO, THREE }; -let someVal: str = "ONE"; -if someVal == nameof(SomeEnum.ONE) { - // whatever1 -} elif someVal == nameof(SomeEnum.TWO) { - // whatever2 -} -``` - -Which is functionally equivalent to: - -```TS -// Wing Code: -enum SomeEnum { ONE, TWO, THREE }; -let someVal: str = getEnumSerializedFromNetwork(); -if someVal == "ONE" { - // whatever1 -} elif someVal == "TWO" { - // whatever2 -} -``` - [`▲ top`][top] --- -### 3.9 Computed Properties - -You may use the following syntax to define computed properties. -Computed properties are syntactic sugar for getters and setters which themselves -are syntactic sugar for methods, therefore omitting either one is acceptable. - -"Read block" must always return a value of the same type as the property. - -Keyword `new` can be used inside the write block to access the incoming r-value -of the assignment (write) operation. `new` is always the same type as the type -of the property itself. - -Both preflight and inflight computed properties are allowed. -Keyword `var` behind computed properties is not allowed. -`inflight` computed properties are also allowed. - -```TS -// Wing Code: -struct Vec2 { - x: num; - y: num; -} - -class Rect { - var size: Vec2; - var origin: Vec2; - - center: Vec2 { - read { - let centerX = origin.x + (size.width / 2); - let centerY = origin.y + (size.height / 2); - return Vec2(x: centerX, y: centerY); - } - write { - origin.x = new.x - (size.width / 2); - origin.y = new.y - (size.height / 2); - } - }; - - inflight prop: num { /* ... */ } -} -``` - -
Equivalent TypeScript Code - -```TS -interface Vec2 { x: number; y: number; } -class Rect { - size: Vec2; - origin: Vec2; - // computed property with a getter and setter block - get center(): Vec2 { - let centerX = origin.x + (size.width / 2); - let centerY = origin.y + (size.height / 2); - return Vec2(x: centerX, y: centerY); - } - set center(_new: Vec2) { - origin.x = _new.x - (size.width / 2); - origin.y = _new.y - (size.height / 2); - } -} -``` - -
- -[`▲ top`][top] - -### 3.10 Unit tests +### 3.9 Unit tests Unit tests can be defined in Wing using the built-in test statement. A test statement expects a name and a block of inflight code to execute. -```js +```TS let b = new cloud.Bucket(); test "can add objects" { @@ -2306,6 +1821,12 @@ The behavior of running tests with `wing test` CLI command is determined by the See the [CLI User Manual](https://docs.winglang.io/reference/cli#test-wing-test) for more details on running tests. +### 3.10 Roadmap + +The following features are not yet implemented, but we are planning to add them in the future: + +* Computed properties (syntactic sugar for getters and setters) - see https://github.com/winglang/wing/issues/128 to track. + [`▲ top`][top] --- @@ -2320,34 +1841,22 @@ code. Comments before the first bring expression are valid. ### 4.1 Imports -To import a JSII / Wing package under a named import, you may use the following +To import a JSII package under a named import, you may use the following syntax: ```TS -bring std; // from std bring * as std; +bring util; // from util bring * as util; bring cloud; // from cloud bring * as cloud; -bring "path/to/what.js" as what; // from "path/to/what.js" bring * as what; +bring "cdktf" as cdktf; // from "cdktf" bring * as cdktf; ``` -Currently, "bringing" other Wing files is treated as a preprocessor step and it -acts like C `#include`s. Symbols collision is fatal in this style of imports. - [`▲ top`][top] --- ### 4.2 Exports -In preflight, anything with `public` at block scope level is importable. -This includes functions, classes, structs and interfaces. -In inflight, all of the above excluding preflight classes are importable. -Variables are not exportable. - -Preflight classes cannot be instantiated in inflight functions. There is no synthesizer inside -and no deployment system in the inflight body to provision. - -"Bringing" other Wing files currently ignores exports, but bringing JSII modules -respect the visibility of JSII module exports. +Wing currently does not not support exporting symbols from a module - see https://github.com/winglang/wing/issues/129 to track. [`▲ top`][top] @@ -2363,18 +1872,13 @@ You may import JSII modules in Wing and they are considered preflight classes if JSII type manifest shows that the JSII module is a construct. Wing is a consumer of JSII modules currently. -```ts -bring "aws-cdk-lib" as cdk; -let bucket = cdk.awsS3.Bucket( - publicAccess: true, +```TS +bring "aws-cdk-lib" as awscdk; +let bucket = new awscdk.aws_s3.Bucket( + blockPublicAccess: awscdk.aws_s3.BlockPublicAccess.BLOCK_ALL, ); ``` -### 5.1.2 Internal Libraries - -Wing libraries themselves are JSII modules. They can be used in all other JSII -supported languages. - ## 5.2 JavaScript The `extern ""` modifier can be used on method declarations in classes to indicate that a method is backed by an implementation imported from a JavaScript module. The module can either be a relative path or a name and will be loaded via [require()](https://nodejs.org/api/modules.html#requireid). @@ -2382,45 +1886,43 @@ The `extern ""` modifier can be used on method dec In the following example, the static inflight method `makeId` is implemented in `helper.js`: -```js +```TS // task-list.w class TaskList { // ... - + inflight addTask(title: str) { - let id = TaskList.makeId(); // or TaskList.v6(); - this.bucket.put(id, title); + let id1 = TaskList.makeId(); + let id2 = TaskList.v4(); + log(id1); + log(id2); + // ... } + // Load js helper file extern "./helpers.js" static inflight makeId(): str; // Alternatively, you can use a module name - extern "uuid" static inflight v6(): str; + extern "uuid" static inflight v4(): str; } // helpers.js const uuid = require("uuid"); -exports.makeId = function() { - return uuid.v6(); +exports.makeId = function () { + return uuid.v4(); }; ``` Given a method of name X, the compiler will map the method to the JavaScript export with the matching name (without any case conversion). -Initially we only support specifying `extern` for static methods (either inflight or preflight), -but we will consider adding support for instance methods in the future. In those cases the first -argument to the method will implicitly be `this`. - ### 5.2.1 TypeScript -It is possible to use TypeScript to write helpers, but at the moment this will not be +It is possible to use TypeScript to write helpers, but at the moment this is not directly supported by Wing. This means that you will need to setup the TypeScript toolchain to compile your code to JavaScript and then use `extern` against the JavaScript file. -In the future we will consider adding direct support for `extern "./helpers.ts"`. - ### 5.2.2 Type model The table below shows the mapping between Wing types and JavaScript types, represented with TypeScript syntax. @@ -2442,7 +1944,6 @@ If [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Gl | `MutSet` | `Set` | | | `MutMap` | `{ [key: string]: T }` | | | `MutArray` | `T[]` | | -| `Promise` | `Promise` | | | `Json` | `string ⏐ number ⏐ boolean ⏐ null ⏐ json[] ⏐ { [key: string]: json }` | Yes | | `MutJson` | `string ⏐ number ⏐ boolean ⏐ null ⏐ json[] ⏐ { [key: string]: json }` | | @@ -2475,12 +1976,12 @@ The string inside the double quotes is processed, and all notations of form `${}` are substituted from their respective scopes. The behavior is similar to `` `text ${sub.prop}` `` notation in JavaScript. Processing unicode escape sequences happens in these strings. -`"` and `$` can be escaped with backslash `\` inside string substitutions. +`"` can be escaped with backslash `\` inside string substitutions. > ```TS > let name = "World"; > let s = "Hello, ${name}!"; -> let l = s.len; +> let l = s.length; > ```
Equivalent TypeScript Code @@ -2497,33 +1998,10 @@ Processing unicode escape sequences happens in these strings. --- -#### 6.1.2 Shell strings \`...\` - -If string is enclosed with backticks, the contents of that string will be -interpreted as a shell command and its output will be used as a string. `` `echo -"Hello"` `` is equal to `"Hello"` for example. - -Shell strings are invalid in the bring expression. - -> ```TS -> let name = `echo "World"`; -> let s = "Hello, ${name}!"; -> ``` - -Shell strings are executed in an instance of the BusyBox shell, compiled to run -in WebAssembly to guarantee portability, in runtime. The stdout is returned as a -string, interleaved with stderr. If BusyBox exits with a non-zero exit code, the -stderr is thrown as an exception. - -[`▲ top`][top] - ---- - ### 6.2 Comments Single line comments start with a `//` and continue to the end of the line. Multi-line comments are supported with the `/* ... */` syntax. -Commenting in Wing has a style that's described earlier in this document. > ```TS > // comment @@ -2632,7 +2110,7 @@ Using a `nil?` type is also ambiguous and results in a compile error. ```TS let x: num? = 1; -if x { +if x? { // ... } ``` @@ -2670,99 +2148,11 @@ for immutable data (on top of nominal typing). --- -### 6.4 Kitchen Sink - -This is an example with almost every feature of the Wing, showing you a whole -picture of what the syntax feels like. - -```TS -bring cloud; -bring fs; - -struct DenyListRule { - packageName: str; - version: str?; - reason: str; -} - -struct DenyListProps { - rules: MutArray[]; -} - -class DenyList { - _bucket: cloud.Bucket; - _objectKey: str; - - init(props: DenyListProps) { - this._bucket = cloud.Bucket(); - this._objectKey = "deny-list.json"; - - let rulesDir = this._writeToFile(props.rules, this._objectKey); - this._bucket.upload("${rulesDir}/*/**", prune: true, retainOnDelete: true); - } - - _writeToFile(list: MutArray[], filename: str): str { - let tmpdir = fs.mkdtemp(); - let filepath = "${tmpdir}/${filename}"; - let map = MutMap{}; - for rule in list { - let suffix = DenyList._maybeSuffix(rule.version); - let path = "${rule.packageName}${suffix}"; - map[path] = rule; - } - fs.writeJson(filepath, map); - return tmpdir; - } - - inflight rules: MutMap{}?; - - inflight init() { - // this._bucket is already initialized by the capture mechanic! - this.rules = this._bucket.get(this._objectKey) ?? MutMap{}; - } - - public inflight lookup(name: str, version: str): DenyListRule? { - return this.rules[name] ?? this.rules["${name}/v${version}"]; - } - - static _maybeSuffix(version: str?): str { - if version { - return "/v${version}"; - } else { - return ""; - } - } -} - -let denyList = DenyList(); -let filterFn = inflight (event: cloud.QueueEvent) => { - let packageName = event.data["packageName"]; - let version = event.data["version"]; - let reason = event.data["reason"]; - if denyList.lookup(packageName, version) { - log("Package rejected: ${packageName}"); - } else { - log("Package accepted: ${packageName}"); - } -}; - -queue = cloud.Queue(); -filter = cloud.Function(filterFn); -queue.setConsumer(filter); -``` - -[`▲ top`][top] - ---- - -### 6.5 Roadmap - +### 6.4 Roadmap - [ ] Asynchronous Execution Safety Model. - [ ] Make the language `async` by default. -- [x] Make inflight functions `async` by default. -- [ ] First class support for `regx`, `glob`, and `cron` types. +- [ ] First class support for `regex`, `glob`, and `cron` types. - [ ] Support of math operations over `date` and `duration` types. -- [x] Add `time`, `date`, and `durations` as first class types with syntax. - [ ] More useful enums: Support for Enum Classes and Swift style enums. - [ ] Reflection: add an extended `typeof` operator to get type information. - [ ] Advanced OOP: Support for `abstract` and `private` implementations. @@ -2774,28 +2164,24 @@ queue.setConsumer(filter); - [ ] Distributed data structures. [`▲ top`][top] - --- -### 6.6 Credits +### 6.5 Credits * **Contributors (A-Z):** * Chris R. ([@Chriscbr](https://github.com/Chriscbr)) + * Cristian P. ([@skyrpex](https://github.com/skyrpex)) * Elad B. ([@eladb](https://github.com/eladb)) * Eyal K. ([@ekeren](https://github.com/ekeren)) + * Hasan AR. ([@hasanaburayyan](https://github.com/hasanaburayyan)) * Mark MC. ([@MarkMcCulloh](https://github.com/MarkMcCulloh)) + * Pol A. ([@polamoros](https://github.com/polamoros)) + * Revital B. ([@revitalbarletz](https://github.com/revitalbarletz)) * Sepehr L. ([@3p3r](https://github.com/3p3r)) + * Shai A. ([@ainvoner](https://github.com/ainvoner)) * Shai B. ([@ShaiBer](https://github.com/ShaiBer)) + * Tsuf C. ([@tsuf239](https://github.com/tsuf239)) * Uri B. ([@staycoolcall911](https://github.com/staycoolcall911)) * Yoav S. ([@yoav-steinberg](https://github.com/yoav-steinberg)) -Inspiration: - -- -- -- -- -- -- - [top]: #0-preface diff --git a/versioned_docs/version-latest/04-standard-library/compatibility/compatibility.json b/versioned_docs/version-latest/04-standard-library/compatibility/compatibility.json index 453afc89b..3261199c7 100644 --- a/versioned_docs/version-latest/04-standard-library/compatibility/compatibility.json +++ b/versioned_docs/version-latest/04-standard-library/compatibility/compatibility.json @@ -14,35 +14,35 @@ "tf-gcp": {"implemented": false}, "aws-cdk": {"implemented": true} }, - "on_upload": { + "onUpload": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 1954}, "tf-gcp": {"implemented": false}, - "aws-cdk": {"implemented": false} + "aws-cdk": {"implemented": true} }, - "on_delete": { + "onDelete": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 1954}, "tf-gcp": {"implemented": false}, - "aws-cdk": {"implemented": false} + "aws-cdk": {"implemented": true} }, - "on_update": { + "onUpdate": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 1954}, "tf-gcp": {"implemented": false}, - "aws-cdk": {"implemented": false} + "aws-cdk": {"implemented": true} }, - "on_event": { + "onEvent": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 1954}, "tf-gcp": {"implemented": false}, - "aws-cdk": {"implemented": false} + "aws-cdk": {"implemented": true} }, - "add_object": { + "addObject": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": true}, @@ -56,7 +56,7 @@ "tf-gcp": {"implemented": false, "issue": 1282}, "aws-cdk": {"implemented": true} }, - "put_json": { + "putJson": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": true}, @@ -70,21 +70,21 @@ "tf-gcp": {"implemented": false, "issue": 1282}, "aws-cdk": {"implemented": true} }, - "try_get": { + "tryGet": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": true}, "tf-gcp": {"implemented": false}, "aws-cdk": {"implemented": false} }, - "get_json": { + "getJson": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": true}, "tf-gcp": {"implemented": false}, "aws-cdk": {"implemented": false} }, - "try_get_json": { + "tryGetJson": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": true}, @@ -98,7 +98,7 @@ "tf-gcp": {"implemented": false, "issue": 1282}, "aws-cdk": {"implemented": true} }, - "try_delete": { + "tryDelete": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": true}, @@ -140,14 +140,14 @@ "tf-gcp": {"implemented": false}, "aws-cdk": {"implemented": true} }, - "public_url": { + "publicUrl": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 1805}, "tf-gcp": {"implemented": false, "issue": 1282}, "aws-cdk": {"implemented": true} }, - "signed_url": { + "signedUrl": { "sim": {"implemented": false, "issue": 1383}, "tf-aws": {"implemented": false, "issue": 1383}, "tf-azure": {"implemented": false}, @@ -184,14 +184,14 @@ "tf-gcp": {"implemented": false}, "aws-cdk": {"implemented": true} }, - "add_consumer": { + "setConsumer": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false}, "tf-gcp": {"implemented": false}, "aws-cdk": {"implemented": true} }, - "approx_size": { + "approxSize": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false}, @@ -270,7 +270,7 @@ "tf-gcp": {"implemented": false}, "aws-cdk": {"implemented": false} }, - "add_env": { + "addEnv": { "sim": {"implemented": false}, "tf-aws": {"implemented": false}, "tf-azure": {"implemented": false}, @@ -284,7 +284,7 @@ "tf-gcp": {"implemented": false, "issue": 614}, "aws-cdk": {"implemented": true} }, - "invoke_async": { + "invokeAsync": { "sim": {"implemented": false}, "tf-aws": {"implemented": false}, "tf-azure": {"implemented": false}, @@ -337,7 +337,7 @@ "tf-gcp": {"implemented": false, "issue": 620}, "aws-cdk": {"implemented": true} }, - "on_message": { + "onMessage": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 621}, @@ -346,21 +346,21 @@ } }, "Schedule": { - "from_cron": { + "fromCron": { "sim": {"implemented": false}, "tf-aws": {"implemented": false}, "tf-azure": {"implemented": false, "issue": 1291}, "tf-gcp": {"implemented": false, "issue": 1292}, "aws-cdk": {"implemented": false} }, - "from_rate": { + "fromRate": { "sim": {"implemented": false}, "tf-aws": {"implemented": false}, "tf-azure": {"implemented": false, "issue": 1291}, "tf-gcp": {"implemented": false, "issue": 1292}, "aws-cdk": {"implemented": false} }, - "on_tick": { + "onTick": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 1291}, @@ -486,7 +486,7 @@ "tf-gcp": {"implemented": false, "issue": 2179}, "aws-cdk": {"implemented": true} }, - "SecretProps.value_json": { + "SecretProps.valueJson": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 2178}, @@ -495,7 +495,7 @@ } }, "Service": { - "ServiceProps.buildpack_path": { + "ServiceProps.buildpackPath": { "sim": {"implemented": false, "issue": 1305}, "tf-aws": {"implemented": false, "issue": 1306}, "tf-azure": {"implemented": false, "issue": 1307}, @@ -544,7 +544,7 @@ "tf-gcp": {"implemented": false, "issue": 1308}, "aws-cdk": {"implemented": false} }, - "buildpack_path": { + "buildpackPath": { "sim": {"implemented": false, "issue": 1305}, "tf-aws": {"implemented": false, "issue": 1306}, "tf-azure": {"implemented": false, "issue": 1307}, @@ -586,7 +586,7 @@ "tf-gcp": {"implemented": false, "issue": 1308}, "aws-cdk": {"implemented": false} }, - "add_env": { + "addEnv": { "sim": {"implemented": false, "issue": 1305}, "tf-aws": {"implemented": false, "issue": 1306}, "tf-azure": {"implemented": false, "issue": 1307}, @@ -616,7 +616,7 @@ "tf-gcp": {"implemented": false, "issue": 1316}, "aws-cdk": {"implemented": false} }, - "TableProps.primary_key": { + "TableProps.primaryKey": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 1315}, @@ -637,13 +637,20 @@ "tf-gcp": {"implemented": false, "issue": 1316}, "aws-cdk": {"implemented": false} }, - "primary_key": { + "primaryKey": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, "tf-azure": {"implemented": false, "issue": 1315}, "tf-gcp": {"implemented": false, "issue": 1316}, "aws-cdk": {"implemented": false} }, + "addRow": { + "sim": {"implemented": true}, + "tf-aws": {"implemented": true}, + "tf-azure": {"implemented": false}, + "tf-gcp": {"implemented": false}, + "aws-cdk": {"implemented": false} + }, "insert": { "sim": {"implemented": true}, "tf-aws": {"implemented": true}, diff --git a/versioned_docs/version-latest/05-wing-console/01-installation.md b/versioned_docs/version-latest/05-wing-console/01-installation.md deleted file mode 100644 index 29a2ed09a..000000000 --- a/versioned_docs/version-latest/05-wing-console/01-installation.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Installation -id: installation ---- - -TODO diff --git a/versioned_docs/version-latest/05-wing-console/02-navigation.md b/versioned_docs/version-latest/05-wing-console/02-navigation.md deleted file mode 100644 index cf094ad39..000000000 --- a/versioned_docs/version-latest/05-wing-console/02-navigation.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Navigation -id: navigation ---- - -TODO diff --git a/versioned_docs/version-latest/05-wing-console/03-interacting.md b/versioned_docs/version-latest/05-wing-console/03-interacting.md deleted file mode 100644 index 105ab774d..000000000 --- a/versioned_docs/version-latest/05-wing-console/03-interacting.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Interacting with resources -id: interacting-with-resources ---- - -TODO diff --git a/versioned_docs/version-latest/05-wing-console/04-testing.md b/versioned_docs/version-latest/05-wing-console/04-testing.md deleted file mode 100644 index 84c6ef68a..000000000 --- a/versioned_docs/version-latest/05-wing-console/04-testing.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Testing -id: testing ---- - -TODO diff --git a/versioned_docs/version-latest/05-wing-console/05-logging.md b/versioned_docs/version-latest/05-wing-console/05-logging.md deleted file mode 100644 index 9e6ff0bff..000000000 --- a/versioned_docs/version-latest/05-wing-console/05-logging.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Logging -id: logging ---- - -TODO diff --git a/versioned_docs/version-latest/05-wing-console/_category_.yml b/versioned_docs/version-latest/05-wing-console/_category_.yml deleted file mode 100644 index e9dbfe552..000000000 --- a/versioned_docs/version-latest/05-wing-console/_category_.yml +++ /dev/null @@ -1,3 +0,0 @@ -label: Wing Console -collapsible: true -collapsed: true diff --git a/versioned_docs/version-latest/06-tools/02-wing-console.md b/versioned_docs/version-latest/06-tools/02-wing-console.md new file mode 100644 index 000000000..3eb5cfe97 --- /dev/null +++ b/versioned_docs/version-latest/06-tools/02-wing-console.md @@ -0,0 +1,45 @@ +--- +title: Wing Console +id: wing-console +description: A web application that provides a developer-friendly interface for viewing, exploring, and interacting Wing applications running on the local cloud simulator +keywords: [Wing Console, console, local simulator, local machine, wing it] +--- + +## Overview + +The Wing Console is a web application that offers a user-friendly interface designed to enhance the developer experience when viewing, exploring, and interacting with Wing applications running on the local cloud simulator. + +With a primary focus on developer experience, the Console aims to provide instant feedback to developers during the cloud application development process. + +Unlike traditional cloud development iterations that involve time-consuming deployments to the cloud, Wing and the Wing Console streamline this process, enabling cloud developers to swiftly receive immediate feedback on code changes directly on their local machines. + +![The Wing Console](console-demo-1.png 'Wing Console') + +## Installation + +The Wing Console is installed as part of the [Wing CLI installation](../01-start-here/02-installation.md#wing-console). + +## View And Explore your Wing application +The Console offers two primary views to provide a comprehensive understanding of your Wing application: the Explorer view and the Map view. +1. The left side of the Console features the **Explorer** view, displaying the hierarchical structure of resources within your application. +![The Wing Console Explorer view](console-explorer-1.png 'Wing Console Explorer') +2. Positioned at the center is the **Application Map** view, presenting an overview of the resources in your application along with their relations with each other. +![The Wing Console Map view](console-map-1.png 'Wing Console Map') + +## Interact with your Wing application +The Console provides seamless interaction with your Wing application resources, right within the Console interface itself. + +When you click on a resource in either the Explorer or Map view, the resource interaction panel on the right side of the Console will automatically update. This panel enables you to conveniently interact with the selected resource. + +From there, you can perform various actions such as sending messages to queues, downloading files from buckets, making API requests to your API resource, and more. +![The Wing Console Interaction view](console-interaction-1.png 'Wing Console Interaction') + +## Run Tests +The Console enables you to run the tests you wrote for your Wing application and view the results of these tests in real-time. +![The Wing Console Tests](console-tests-1.png 'Wing Console Tests') + +## Logs +In addition to Interaction abilities, the Console includes a dedicated Logs view, designed for testing and debugging purposes. This view presents real-time logs of your application. + +Any logs that you generate within your Wing Application will be automatically displayed within the Logs view, providing you with convenient monitoring of your application's behavior. +![The Wing Console Logs](console-logs-1.png 'Wing Console Logs') \ No newline at end of file diff --git a/versioned_docs/version-latest/06-tools/02-plugins.md b/versioned_docs/version-latest/06-tools/03-plugins.md similarity index 100% rename from versioned_docs/version-latest/06-tools/02-plugins.md rename to versioned_docs/version-latest/06-tools/03-plugins.md diff --git a/versioned_docs/version-latest/06-tools/console-demo-1.png b/versioned_docs/version-latest/06-tools/console-demo-1.png new file mode 100644 index 000000000..052a93ccf Binary files /dev/null and b/versioned_docs/version-latest/06-tools/console-demo-1.png differ diff --git a/versioned_docs/version-latest/06-tools/console-explorer-1.png b/versioned_docs/version-latest/06-tools/console-explorer-1.png new file mode 100644 index 000000000..386390a70 Binary files /dev/null and b/versioned_docs/version-latest/06-tools/console-explorer-1.png differ diff --git a/versioned_docs/version-latest/06-tools/console-interaction-1.png b/versioned_docs/version-latest/06-tools/console-interaction-1.png new file mode 100644 index 000000000..f60539b4f Binary files /dev/null and b/versioned_docs/version-latest/06-tools/console-interaction-1.png differ diff --git a/versioned_docs/version-latest/06-tools/console-logs-1.png b/versioned_docs/version-latest/06-tools/console-logs-1.png new file mode 100644 index 000000000..032eb57c5 Binary files /dev/null and b/versioned_docs/version-latest/06-tools/console-logs-1.png differ diff --git a/versioned_docs/version-latest/06-tools/console-map-1.png b/versioned_docs/version-latest/06-tools/console-map-1.png new file mode 100644 index 000000000..a20c75f90 Binary files /dev/null and b/versioned_docs/version-latest/06-tools/console-map-1.png differ diff --git a/versioned_docs/version-latest/06-tools/console-tests-1.png b/versioned_docs/version-latest/06-tools/console-tests-1.png new file mode 100644 index 000000000..058d80bb2 Binary files /dev/null and b/versioned_docs/version-latest/06-tools/console-tests-1.png differ