From b3643e11614a931577f0c88986beb15a35b4a020 Mon Sep 17 00:00:00 2001 From: Jo Franchetti Date: Mon, 4 Aug 2025 22:00:41 +0100 Subject: [PATCH 1/5] move api reference navigation to sidebar --- .vscode/tasks.json | 12 ++ _components/Base.tsx | 5 +- _components/Navigation.tsx | 2 +- _components/SidebarNav.tsx | 266 ++++++++++++++++++++++++-- _components/SubNav.tsx | 26 +-- _config.ts | 80 ++++++++ _includes/layout.tsx | 3 +- reference/_components/Breadcrumbs.tsx | 13 +- 8 files changed, 365 insertions(+), 42 deletions(-) create mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..b2a96a4f8 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,12 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Dev Server", + "type": "shell", + "command": "deno task start", + "group": "serve", + "isBackground": true + } + ] +} diff --git a/_components/Base.tsx b/_components/Base.tsx index 5ca616f34..91b2ebfd2 100644 --- a/_components/Base.tsx +++ b/_components/Base.tsx @@ -8,7 +8,10 @@ export default function Base( <>
- +
{children}
diff --git a/_components/Navigation.tsx b/_components/Navigation.tsx index c31979b36..d0a02c9ed 100644 --- a/_components/Navigation.tsx +++ b/_components/Navigation.tsx @@ -43,7 +43,7 @@ function getSectionData(data: Lume.Data, currentUrl: string) { const childItems = categoryPanel.categories; childItems.push({ - name: `View all ${categoryPanel.total_symbols} symbols`, + name: `All ${categoryPanel.total_symbols} symbols`, href: categoryPanel.all_symbols_href, active: currentUrl.includes("all_symbols"), }); diff --git a/_components/SidebarNav.tsx b/_components/SidebarNav.tsx index fe30a2a81..4d6737993 100644 --- a/_components/SidebarNav.tsx +++ b/_components/SidebarNav.tsx @@ -1,28 +1,241 @@ +// deno-lint-ignore-file no-explicit-any export default function (data: Lume.Data) { const sectionData = data.sectionData; const currentUrl = data.currentUrl.replace(/\/$/, ""); const isReference = currentUrl.startsWith("/api/"); - const isDenoAPI = currentUrl.startsWith("/api/deno/"); + + // Access global API categories data + const apiCategories = data.apiCategories; if (isReference) { + // Note: Per-category symbol counts are not available in the generated API data structure + // The categories_panel only provides total symbol counts, not per-category breakdowns + // For API reference, we want to add section headings for the three API types + + const apiSections = [ + { + title: "Deno APIs", + key: "deno", + isCurrentSection: currentUrl.startsWith("/api/deno"), + items: currentUrl.startsWith("/api/deno") ? sectionData : undefined, + extraItems: [ + { href: "/api/deno", title: "Deno specific APIs" }, + { + href: "/api/deno/all_symbols", + title: "All symbols", + }, + ], + }, + { + title: "Web APIs", + key: "web", + isCurrentSection: currentUrl.startsWith("/api/web"), + items: currentUrl.startsWith("/api/web") ? sectionData : undefined, + extraItems: [ + { + href: "/api/web/all_symbols", + title: "All symbols", + }, + ], + }, + { + title: "Node APIs", + key: "node", + isCurrentSection: currentUrl.startsWith("/api/node"), + items: currentUrl.startsWith("/api/node") ? sectionData : undefined, + extraItems: [ + { href: "/api/node", title: "Node support in deno" }, + { + href: "/api/node/all_symbols", + title: "All symbols", + }, + ], + }, + ]; + return ( <> - {sectionData.map((nav: any) => ( -
{(isReference && data.children.props.data.toc_ctx) && ( diff --git a/api/deno/index.md b/api/deno/index.md new file mode 100644 index 000000000..c14e34ef7 --- /dev/null +++ b/api/deno/index.md @@ -0,0 +1,895 @@ +--- +title: "Deno Namespace APIs" +description: "A guide to Deno's built-in runtime APIs. Learn about file system operations, network functionality, permissions management, and other core capabilities available through the global Deno namespace." +layout: layout.tsx +oldUrl: +- /runtime/manual/runtime/ +- /runtime/manual/runtime/builtin_apis/ +- /runtime/manual/runtime/permission_apis/ +- /runtime/manual/runtime/import_meta_api/ +- /runtime/manual/runtime/ffi_api/ +- /runtime/manual/runtime/program_lifecycle/ +- /runtime/reference/deno_namespace_apis/ +--- + +The global `Deno` namespace contains APIs that are not web standard, including +APIs for reading from files, opening TCP sockets, serving HTTP, and executing +subprocesses, etc. + +[Explore all Deno APIs](/api/deno/all_symbols){.docs-cta .runtime-cta} + +Below we highlight some of the most important Deno APIs to know. + +## File System + +The Deno runtime comes with +[various functions for working with files and directories](/api/deno/file-system). +You will need to use --allow-read and --allow-write permissions to gain access +to the file system. + +Refer to the links below for code examples of how to use the file system +functions. + +- [Reading files in streams](/examples/file_server_tutorial/) +- [Reading a text file (`Deno.readTextFile`)](/examples/reading_files/) +- [Writing a text file (`Deno.writeTextFile`)](/examples/writing_files/) + +## Network + +The Deno runtime comes with +[built-in functions for dealing with connections to network ports](/api/deno/network). + +Refer to the links below for code examples for common functions. + +- [Connect to the hostname and port (`Deno.connect`)](/api/deno/~/Deno.connect) +- [Announcing on the local transport address (`Deno.listen`)](/api/deno/~/Deno.listen) + +For practical examples of networking functionality: + +- [HTTP Server: Hello world](/examples/http_server/) +- [HTTP Server: Routing](/examples/http_server_routing/) +- [TCP Echo Server](/examples/tcp_echo_server/) +- [WebSockets example](/examples/http_server_websocket/) +- [Build a chat app with WebSockets tutorial](/examples/chat_app_tutorial/) + +## Subprocesses + +The Deno runtime comes with +[built-in functions for spinning up subprocesses](/api/deno/subprocess). + +Refer to the links below for code samples of how to create a subprocess. + +- [Creating a subprocess (`Deno.Command`)](/examples/subprocess_tutorial/) +- [Collecting output from subprocesses](/examples/subprocesses_output/) + +## Errors + +The Deno runtime comes with [20 error classes](/api/deno/errors) that can be +raised in response to a number of conditions. + +Some examples are: + +```sh +Deno.errors.NotFound; +Deno.errors.WriteZero; +``` + +They can be used as below: + +```ts +try { + const file = await Deno.open("./some/file.txt"); +} catch (error) { + if (error instanceof Deno.errors.NotFound) { + console.error("the file was not found"); + } else { + // otherwise re-throw + throw error; + } +} +``` + +## HTTP Server + +Deno has two HTTP Server APIs: + +- [`Deno.serve`](/api/deno/~/Deno.serve): native, _higher-level_, supports + HTTP/1.1 and HTTP2, this is the preferred API to write HTTP servers in Deno. +- [`Deno.serveHttp`](/api/deno/~/Deno.serveHttp): native, _low-level_, supports + HTTP/1.1 and HTTP2. + +To start an HTTP server on a given port, use the `Deno.serve` function. This +function takes a handler function that will be called for each incoming request, +and is expected to return a response (or a promise resolving to a response). For +example: + +```ts +Deno.serve((_req) => { + return new Response("Hello, World!"); +}); +``` + +By default `Deno.serve` will listen on port `8000`, but this can be changed by +passing in a port number in options bag as the first or second argument. + +You can +[read more about how to use the HTTP server APIs](/runtime/fundamentals/http_server/). + +For practical examples of HTTP servers: + +- [Simple file server tutorial](/examples/file_server_tutorial/) +- [HTTP server serving files](/examples/http_server_files/) +- [HTTP server with streaming](/examples/http_server_streaming/) +- [HTTP server WebSockets](/examples/http_server_websocket/) + +## Permissions + +Permissions are granted from the CLI when running the `deno` command. User code +will often assume its own set of required permissions, but there is no guarantee +during execution that the set of **granted** permissions will align with this. + +In some cases, ensuring a fault-tolerant program requires a way to interact with +the permission system at runtime. + +### Permission descriptors + +On the CLI, read permission for `/foo/bar` is represented as +`--allow-read=/foo/bar`. In runtime JS, it is represented as the following: + +```ts +const desc = { name: "read", path: "/foo/bar" } as const; +``` + +Other examples: + +```ts +// Global write permission. +const desc1 = { name: "write" } as const; + +// Write permission to `$PWD/foo/bar`. +const desc2 = { name: "write", path: "foo/bar" } as const; + +// Global net permission. +const desc3 = { name: "net" } as const; + +// Net permission to 127.0.0.1:8000. +const desc4 = { name: "net", host: "127.0.0.1:8000" } as const; + +// High-resolution time permission. +const desc5 = { name: "hrtime" } as const; +``` + +See [`PermissionDescriptor`](/api/deno/~/Deno.PermissionDescriptor) in API +reference for more details. Synchronous API counterparts (ex. +`Deno.permissions.querySync`) exist for all the APIs described below. + +### Query permissions + +Check, by descriptor, if a permission is granted or not. + +```ts +// deno run --allow-read=/foo main.ts + +const desc1 = { name: "read", path: "/foo" } as const; +console.log(await Deno.permissions.query(desc1)); +// PermissionStatus { state: "granted", partial: false } + +const desc2 = { name: "read", path: "/foo/bar" } as const; +console.log(await Deno.permissions.query(desc2)); +// PermissionStatus { state: "granted", partial: false } + +const desc3 = { name: "read", path: "/bar" } as const; +console.log(await Deno.permissions.query(desc3)); +// PermissionStatus { state: "prompt", partial: false } +``` + +If `--deny-read` flag was used to restrict some of the filepaths, the result +will contain `partial: true` describing that not all subpaths have permissions +granted: + +```ts +// deno run --allow-read=/foo --deny-read=/foo/bar main.ts + +const desc1 = { name: "read", path: "/foo" } as const; +console.log(await Deno.permissions.query(desc1)); +// PermissionStatus { state: "granted", partial: true } + +const desc2 = { name: "read", path: "/foo/bar" } as const; +console.log(await Deno.permissions.query(desc2)); +// PermissionStatus { state: "denied", partial: false } + +const desc3 = { name: "read", path: "/bar" } as const; +console.log(await Deno.permissions.query(desc3)); +// PermissionStatus { state: "prompt", partial: false } +``` + +### Permission states + +A permission state can be either "granted", "prompt" or "denied". Permissions +which have been granted from the CLI will query to `{ state: "granted" }`. Those +which have not been granted query to `{ state: "prompt" }` by default, while +`{ state: "denied" }` reserved for those which have been explicitly refused. +This will come up in [Request permissions](#request-permissions). + +### Permission strength + +The intuitive understanding behind the result of the second query in +[Query permissions](#query-permissions) is that read access was granted to +`/foo` and `/foo/bar` is within `/foo` so `/foo/bar` is allowed to be read. This +hold true, unless the CLI-granted permission is _partial_ to the queried +permissions (as an effect of using a `--deny-*` flag). + +We can also say that `desc1` is +_[stronger than](https://www.w3.org/TR/permissions/#ref-for-permissiondescriptor-stronger-than)_ +`desc2`. This means that for any set of CLI-granted permissions: + +1. If `desc1` queries to `{ state: "granted", partial: false }` then so must + `desc2`. +2. If `desc2` queries to `{ state: "denied", partial: false }` then so must + `desc1`. + +More examples: + +```ts +const desc1 = { name: "write" } as const; +// is stronger than +const desc2 = { name: "write", path: "/foo" } as const; + +const desc3 = { name: "net", host: "127.0.0.1" } as const; +// is stronger than +const desc4 = { name: "net", host: "127.0.0.1:8000" } as const; +``` + +### Request permissions + +Request an ungranted permission from the user via CLI prompt. + +```ts +// deno run main.ts + +const desc1 = { name: "read", path: "/foo" } as const; +const status1 = await Deno.permissions.request(desc1); +// ⚠️ Deno requests read access to "/foo". Grant? [y/n (y = yes allow, n = no deny)] y +console.log(status1); +// PermissionStatus { state: "granted", partial: false } + +const desc2 = { name: "read", path: "/bar" } as const; +const status2 = await Deno.permissions.request(desc2); +// ⚠️ Deno requests read access to "/bar". Grant? [y/n (y = yes allow, n = no deny)] n +console.log(status2); +// PermissionStatus { state: "denied", partial: false } +``` + +If the current permission state is "prompt", a prompt will appear on the user's +terminal asking them if they would like to grant the request. The request for +`desc1` was granted so its new status is returned and execution will continue as +if `--allow-read=/foo` was specified on the CLI. The request for `desc2` was +denied so its permission state is downgraded from "prompt" to "denied". + +If the current permission state is already either "granted" or "denied", the +request will behave like a query and just return the current status. This +prevents prompts both for already granted permissions and previously denied +requests. + +### Revoke permissions + +Downgrade a permission from "granted" to "prompt". + +```ts +// deno run --allow-read=/foo main.ts + +const desc = { name: "read", path: "/foo" } as const; +console.log(await Deno.permissions.revoke(desc)); +// PermissionStatus { state: "prompt", partial: false } +``` + +What happens when you try to revoke a permission which is _partial_ to one +granted on the CLI? + +```ts +// deno run --allow-read=/foo main.ts + +const desc = { name: "read", path: "/foo/bar" } as const; +console.log(await Deno.permissions.revoke(desc)); +// PermissionStatus { state: "prompt", partial: false } +const cliDesc = { name: "read", path: "/foo" } as const; +console.log(await Deno.permissions.revoke(cliDesc)); +// PermissionStatus { state: "prompt", partial: false } +``` + +The CLI-granted permission, which implies the revoked permission, was also +revoked. + +To understand this behavior, imagine that Deno stores an internal set of +_explicitly granted permission descriptors_. Specifying `--allow-read=/foo,/bar` +on the CLI initializes this set to: + +```ts +[ + { name: "read", path: "/foo" }, + { name: "read", path: "/bar" }, +]; +``` + +Granting a runtime request for `{ name: "write", path: "/foo" }` updates the set +to: + +```ts +[ + { name: "read", path: "/foo" }, + { name: "read", path: "/bar" }, + { name: "write", path: "/foo" }, +]; +``` + +Deno's permission revocation algorithm works by removing every element from this +set which is _stronger than_ the argument permission descriptor. + +Deno does not allow "fragmented" permission states, where some strong permission +is granted with exclusions of weak permissions implied by it. Such a system +would prove increasingly complex and unpredictable as you factor in a wider +variety of use cases and the `"denied"` state. This is a calculated trade-off of +granularity for security. + +## import.meta + +Deno supports a number of properties and methods on the +[`import.meta`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta) +API. It can be used to get information about the module, such as the module's +URL. + +### import.meta.url + +Returns the URL of the current module. + +```ts title="main.ts" +console.log(import.meta.url); +``` + +```sh +$ deno run main.ts +file:///dev/main.ts + +$ deno run https:/example.com/main.ts +https://example.com/main.ts +``` + +### import.meta.main + +Returns whether the current module is the entry point to your program. + +```ts title="main.ts" +import "./other.ts"; + +console.log(`Is ${import.meta.url} the main module?`, import.meta.main); +``` + +```ts title="other.ts" +console.log(`Is ${import.meta.url} the main module?`, import.meta.main); +``` + +```sh +$ deno run main.ts +Is file:///dev/other.ts the main module? false +Is file:///dev/main.ts the main module? true +``` + +### import.meta.filename + +_This property is only available for local modules (module that have +`file:///...` specifier) and returns `undefined` for remote modules._ + +Returns the fully resolved path to the current module. The value contains OS +specific path separators. + +```ts title="main.ts" +console.log(import.meta.filename); +``` + +On Unix: + +```sh +$ deno run main.ts +/dev/main.ts + +$ deno run https://example.com/main.ts +undefined +``` + +On Windows: + +```sh +$ deno run main.ts +C:\dev\main.ts + +$ deno run https://example.com/main.ts +undefined +``` + +### import.meta.dirname + +_This property is only available for local modules (module that have +`file:///...` specifier) and returns `undefined` for remote modules._ + +Returns the fully resolved path to the directory containing the current module. +The value contains OS specific path separators. + +```ts title="main.ts" +console.log(import.meta.dirname); +``` + +On Unix: + +```sh +$ deno run main.ts +/dev/ + +$ deno run https://example.com/main.ts +undefined +``` + +On Windows: + +```sh +$ deno run main.ts +C:\dev\ + +$ deno run https://example.com/main.ts +undefined +``` + +### import.meta.resolve + +Resolve specifiers relative to the current module. + +```ts +const worker = new Worker(import.meta.resolve("./worker.ts")); +``` + +The `import.meta.resolve` API takes into account the currently applied import +map, which gives you the ability to resolve "bare" specifiers as well. + +With such import map loaded... + +```json +{ + "imports": { + "fresh": "https://deno.land/x/fresh@1.0.1/dev.ts" + } +} +``` + +...you can now resolve: + +```js title="resolve.js" +console.log(import.meta.resolve("fresh")); +``` + +```sh +$ deno run resolve.js +https://deno.land/x/fresh@1.0.1/dev.ts +``` + +## FFI + +The FFI (foreign function interface) API allows users to call libraries written +in native languages that support the C ABIs (C/C++, Rust, Zig, V, etc.) using +`Deno.dlopen`. + +Here's an example showing how to call a Rust function from Deno: + +```rust +// add.rs +#[no_mangle] +pub extern "C" fn add(a: isize, b: isize) -> isize { + a + b +} +``` + +Compile it to a C dynamic library (`libadd.so` on Linux): + +```sh +rustc --crate-type cdylib add.rs +``` + +In C you can write it as: + +```c +// add.c +int add(int a, int b) { + return a + b; +} +``` + +And compile it: + +```sh +// unix +cc -c -o add.o add.c +cc -shared -W -o libadd.so add.o +// Windows +cl /LD add.c /link /EXPORT:add +``` + +Calling the library from Deno: + +```typescript +// ffi.ts + +// Determine library extension based on +// your OS. +let libSuffix = ""; +switch (Deno.build.os) { + case "windows": + libSuffix = "dll"; + break; + case "darwin": + libSuffix = "dylib"; + break; + default: + libSuffix = "so"; + break; +} + +const libName = `./libadd.${libSuffix}`; +// Open library and define exported symbols +const dylib = Deno.dlopen( + libName, + { + "add": { parameters: ["isize", "isize"], result: "isize" }, + } as const, +); + +// Call the symbol `add` +const result = dylib.symbols.add(35, 34); // 69 + +console.log(`Result from external addition of 35 and 34: ${result}`); +``` + +Run with `--allow-ffi` and `--unstable` flag: + +```sh +deno run --allow-ffi --unstable ffi.ts +``` + +### Non-blocking FFI + +There are many use cases where users might want to run CPU-bound FFI functions +in the background without blocking other tasks on the main thread. + +As of Deno 1.15, symbols can be marked `nonblocking` in `Deno.dlopen`. These +function calls will run on a dedicated blocking thread and will return a +`Promise` resolving to the desired `result`. + +Example of executing expensive FFI calls with Deno: + +```c +// sleep.c +#ifdef _WIN32 +#include +#else +#include +#endif + +int sleep(unsigned int ms) { + #ifdef _WIN32 + Sleep(ms); + #else + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + nanosleep(&ts, NULL); + #endif +} +``` + +Calling it from Deno: + +```typescript +// nonblocking_ffi.ts +const library = Deno.dlopen( + "./sleep.so", + { + sleep: { + parameters: ["usize"], + result: "void", + nonblocking: true, + }, + } as const, +); + +library.symbols.sleep(500).then(() => console.log("After")); +console.log("Before"); +``` + +Result: + +```sh +$ deno run --allow-ffi --unstable unblocking_ffi.ts +Before +After +``` + +### Callbacks + +Deno FFI API supports creating C callbacks from JavaScript functions for calling +back into Deno from dynamic libraries. An example of how callbacks are created +and used is as follows: + +```typescript +// callback_ffi.ts +const library = Deno.dlopen( + "./callback.so", + { + set_status_callback: { + parameters: ["function"], + result: "void", + }, + start_long_operation: { + parameters: [], + result: "void", + }, + check_status: { + parameters: [], + result: "void", + }, + } as const, +); + +const callback = new Deno.UnsafeCallback( + { + parameters: ["u8"], + result: "void", + } as const, + (success: number) => {}, +); + +// Pass the callback pointer to dynamic library +library.symbols.set_status_callback(callback.pointer); +// Start some long operation that does not block the thread +library.symbols.start_long_operation(); + +// Later, trigger the library to check if the operation is done. +// If it is, this call will trigger the callback. +library.symbols.check_status(); +``` + +If an `UnsafeCallback`'s callback function throws an error, the error will get +propagated up to the function that triggered the callback to be called (above, +that would be `check_status()`) and can be caught there. If a callback returning +a value throws then Deno will return 0 (null pointer for pointers) as the +result. + +`UnsafeCallback` is not deallocated by default as it can cause use-after-free +bugs. To properly dispose of an `UnsafeCallback` its `close()` method must be +called. + +```typescript +const callback = new Deno.UnsafeCallback( + { parameters: [], result: "void" } as const, + () => {}, +); + +// After callback is no longer needed +callback.close(); +// It is no longer safe to pass the callback as a parameter. +``` + +It is also possible for native libraries to setup interrupt handlers and to have +those directly trigger the callback. However, this is not recommended and may +cause unexpected side-effects and undefined behaviour. Preferably any interrupt +handlers would only set a flag that can later be polled similarly to how +`check_status()` is used above. + +### Supported types + +Here's a list of types supported currently by the Deno FFI API. + +| FFI Type | Deno | C | Rust | +| ---------------------- | -------------------- | ------------------------ | ------------------------- | +| `i8` | `number` | `char` / `signed char` | `i8` | +| `u8` | `number` | `unsigned char` | `u8` | +| `i16` | `number` | `short int` | `i16` | +| `u16` | `number` | `unsigned short int` | `u16` | +| `i32` | `number` | `int` / `signed int` | `i32` | +| `u32` | `number` | `unsigned int` | `u32` | +| `i64` | `bigint` | `long long int` | `i64` | +| `u64` | `bigint` | `unsigned long long int` | `u64` | +| `usize` | `bigint` | `size_t` | `usize` | +| `isize` | `bigint` | `size_t` | `isize` | +| `f32` | `number` | `float` | `f32` | +| `f64` | `number` | `double` | `f64` | +| `void`[1] | `undefined` | `void` | `()` | +| `pointer` | `{} \| null` | `void *` | `*mut c_void` | +| `buffer`[2] | `TypedArray \| null` | `uint8_t *` | `*mut u8` | +| `function`[3] | `{} \| null` | `void (*fun)()` | `Option` | +| `{ struct: [...] }`[4] | `TypedArray` | `struct MyStruct` | `MyStruct` | + +As of Deno 1.25, the `pointer` type has been split into a `pointer` and a +`buffer` type to ensure users take advantage of optimizations for Typed Arrays, +and as of Deno 1.31 the JavaScript representation of `pointer` has become an +opaque pointer object or `null` for null pointers. + +- [1] `void` type can only be used as a result type. +- [2] `buffer` type accepts TypedArrays as parameter, but it always returns a + pointer object or `null` when used as result type like the `pointer` type. +- [3] `function` type works exactly the same as the `pointer` type as a + parameter and result type. +- [4] `struct` type is for passing and returning C structs by value (copy). The + `struct` array must enumerate each of the struct's fields' type in order. The + structs are padded automatically: Packed structs can be defined by using an + appropriate amount of `u8` fields to avoid padding. Only TypedArrays are + supported as structs, and structs are always returned as `Uint8Array`s. + +### deno_bindgen + +[`deno_bindgen`](https://github.com/denoland/deno_bindgen) is the official tool +to simplify glue code generation of Deno FFI libraries written in Rust. + +It is similar to [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) in +the Rust Wasm ecosystem. + +Here's an example showing its usage: + +```rust +// mul.rs +use deno_bindgen::deno_bindgen; + +#[deno_bindgen] +struct Input { + a: i32, + b: i32, +} + +#[deno_bindgen] +fn mul(input: Input) -> i32 { + input.a * input.b +} +``` + +Run `deno_bindgen` to generate bindings. You can now directly import them into +Deno: + +```ts +// mul.ts +import { mul } from "./bindings/bindings.ts"; +mul({ a: 10, b: 2 }); // 20 +``` + +Any issues related to `deno_bindgen` should be reported at + + +## Program Lifecycle + +Deno supports browser compatible lifecycle events: + +- [`load`](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event#:~:text=The%20load%20event%20is%20fired,for%20resources%20to%20finish%20loading.): + fired when the whole page has loaded, including all dependent resources such + as stylesheets and images. +- [`beforeunload`](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#:~:text=The%20beforeunload%20event%20is%20fired,want%20to%20leave%20the%20page.): + fired when the event loop has no more work to do and is about to exit. + Scheduling more asynchronous work (like timers or network requests) will cause + the program to continue. +- [`unload`](https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event): + fired when the document or a child resource is being unloaded. +- [`unhandledrejection`](https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event): + fired when a promise that has no rejection handler is rejected, ie. a promise + that has no `.catch()` handler or a second argument to `.then()`. +- [`rejectionhandled`](https://developer.mozilla.org/en-US/docs/Web/API/Window/rejectionhandled_event): + fired when a `.catch()` handler is added to a a promise that has already + rejected. This event is fired only if there's `unhandledrejection` listener + installed that prevents propagation of the event (which would result in the + program terminating with an error). + +You can use these events to provide setup and cleanup code in your program. + +Listeners for `load` events can be asynchronous and will be awaited, this event +cannot be canceled. Listeners for `beforeunload` need to be synchronous and can +be cancelled to keep the program running. Listeners for `unload` events need to +be synchronous and cannot be cancelled. + +## main.ts + +```ts title="main.ts" +import "./imported.ts"; + +const handler = (e: Event): void => { + console.log(`got ${e.type} event in event handler (main)`); +}; + +globalThis.addEventListener("load", handler); + +globalThis.addEventListener("beforeunload", handler); + +globalThis.addEventListener("unload", handler); + +globalThis.onload = (e: Event): void => { + console.log(`got ${e.type} event in onload function (main)`); +}; + +globalThis.onbeforeunload = (e: Event): void => { + console.log(`got ${e.type} event in onbeforeunload function (main)`); +}; + +globalThis.onunload = (e: Event): void => { + console.log(`got ${e.type} event in onunload function (main)`); +}; + +console.log("log from main script"); +``` + +```ts title="imported.ts" +const handler = (e: Event): void => { + console.log(`got ${e.type} event in event handler (imported)`); +}; + +globalThis.addEventListener("load", handler); +globalThis.addEventListener("beforeunload", handler); +globalThis.addEventListener("unload", handler); + +globalThis.onload = (e: Event): void => { + console.log(`got ${e.type} event in onload function (imported)`); +}; + +globalThis.onbeforeunload = (e: Event): void => { + console.log(`got ${e.type} event in onbeforeunload function (imported)`); +}; + +globalThis.onunload = (e: Event): void => { + console.log(`got ${e.type} event in onunload function (imported)`); +}; + +console.log("log from imported script"); +``` + +A couple notes on this example: + +- `addEventListener` and `onload`/`onunload` are prefixed with `globalThis`, but + you could also use `self` or no prefix at all. + [It is not recommended to use `window` as a prefix](https://docs.deno.com/lint/rules/no-window-prefix). +- You can use `addEventListener` and/or `onload`/`onunload` to define handlers + for events. There is a major difference between them, let's run the example: + +```shell +$ deno run main.ts +log from imported script +log from main script +got load event in event handler (imported) +got load event in event handler (main) +got load event in onload function (main) +got onbeforeunload event in event handler (imported) +got onbeforeunload event in event handler (main) +got onbeforeunload event in onbeforeunload function (main) +got unload event in event handler (imported) +got unload event in event handler (main) +got unload event in onunload function (main) +``` + +All listeners added using `addEventListener` were run, but `onload`, +`onbeforeunload` and `onunload` defined in `main.ts` overrode handlers defined +in `imported.ts`. + +In other words, you can use `addEventListener` to register multiple `"load"` or +`"unload"` event handlers, but only the last defined `onload`, `onbeforeunload`, +`onunload` event handlers will be executed. It is preferable to use +`addEventListener` when possible for this reason. + +### beforeunload + +```js +// beforeunload.js +let count = 0; + +console.log(count); + +globalThis.addEventListener("beforeunload", (e) => { + console.log("About to exit..."); + count++; +}); + +globalThis.addEventListener("unload", (e) => { + console.log("Exiting"); +}); + +count++; +``` diff --git a/api/node/index.md b/api/node/index.md new file mode 100644 index 000000000..67e447942 --- /dev/null +++ b/api/node/index.md @@ -0,0 +1,153 @@ +--- +title: "Node.js Built-in APIs" +description: "Complete reference for Node.js built-in modules and globals supported in Deno. Explore Node.js APIs including fs, http, crypto, process, buffer, and more with compatibility information." +layout: layout.tsx +oldUrl: + - /runtime/manual/node/compatibility/ + - /runtime/manual/npm_nodejs/compatibility_mode/ +--- + +Deno provides comprehensive support for Node.js built-in modules and globals, +enabling seamless migration of Node.js applications and libraries. These APIs +follow Node.js specifications and provide familiar functionality for developers +transitioning from Node.js. + +## Key Features + +- **Built-in Module Support**: Access Node.js modules using `node:` prefix + (e.g., `import fs from "node:fs"`) +- **Global Objects**: Node.js global objects available in npm package scope +- **Compatibility Layer**: Seamless interoperability with existing Node.js code +- **Performance**: Native implementations optimized for Deno runtime + +## Core Modules + +### File System + +- **`node:fs`** - File system operations (read, write, watch, stats) +- **`node:fs/promises`** - Promise-based file system API +- **`node:path`** - Cross-platform path utilities + +### Network & HTTP + +- **`node:http`** - HTTP server and client functionality +- **`node:https`** - HTTPS server and client with TLS support +- **`node:http2`** - HTTP/2 server and client implementation +- **`node:net`** - TCP networking utilities +- **`node:dns`** - DNS resolution and lookup functions + +### Process & System + +- **`node:process`** - Process information and control +- **`node:os`** - Operating system utilities and information +- **`node:child_process`** - Spawn and manage child processes +- **`node:cluster`** - Multi-process clustering support + +### Crypto & Security + +- **`node:crypto`** - Cryptographic functionality (hashing, encryption, + certificates) +- **`node:tls`** - TLS/SSL secure communication layer + +### Data & Streams + +- **`node:stream`** - Stream interfaces (readable, writable, transform) +- **`node:buffer`** - Binary data handling with Buffer class +- **`node:zlib`** - Data compression and decompression +- **`node:string_decoder`** - Decode buffers to strings + +### Utilities + +- **`node:util`** - Utility functions (promisify, inspect, types) +- **`node:events`** - Event emitter pattern implementation +- **`node:url`** - URL parsing and formatting utilities +- **`node:querystring`** - Query string utilities +- **`node:assert`** - Assertion testing support + +### Development & Testing + +- **`node:vm`** - Virtual machine contexts for code execution +- **`node:repl`** - Read-Eval-Print Loop functionality +- **`node:inspector`** - V8 inspector integration for debugging + +## Global Objects + +Node.js global objects are available in the npm package scope and can be +imported from relevant `node:` modules: + +- **`Buffer`** - Binary data manipulation +- **`process`** - Process information and environment +- **`global`** - Global namespace object +- **`__dirname`** / **`__filename`** - Module path information +- **Web Standards** - `fetch`, `URL`, `TextEncoder`, `crypto`, and more + +## Usage Examples + +### Basic Module Import + +```javascript +import fs from "node:fs"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; + +// Synchronous file reading +const data = fs.readFileSync("file.txt", "utf8"); + +// Asynchronous file reading +const content = await readFile("file.txt", "utf8"); + +// Path manipulation +const fullPath = path.join("/users", "documents", "file.txt"); +``` + +### HTTP Server + +```javascript +import http from "node:http"; + +const server = http.createServer((req, res) => { + res.writeHead(200, { "Content-Type": "text/plain" }); + res.end("Hello from Node.js API in Deno!"); +}); + +server.listen(3000, () => { + console.log("Server running on port 3000"); +}); +``` + +### Crypto Operations + +```javascript +import crypto from "node:crypto"; + +// Generate hash +const hash = crypto.createHash("sha256"); +hash.update("Hello World"); +const digest = hash.digest("hex"); + +// Generate random bytes +const randomBytes = crypto.randomBytes(16); +``` + +## Compatibility + +Node compatibility is an ongoing project. Most core Node.js APIs are supported +with high fidelity. For detailed compatibility information: + +- View our [Node.js compatibility guide](/runtime/reference/node_apis/) +- Check [Node.js test results](https://node-test-viewer.deno.dev/) for specific + test coverage +- [Report compatibility issues](https://github.com/denoland/deno/issues) on + GitHub + +## Migration from Node.js + +When migrating from Node.js to Deno: + +1. **Update imports**: Use `node:` prefix for built-in modules +2. **Check compatibility**: Verify your dependencies work with Deno +3. **Use npm specifiers**: Import npm packages with `npm:` prefix +4. **Review permissions**: Configure Deno's permission system as needed + +For more guidance, see our +[migration guide](/runtime/reference/migration_guide/). diff --git a/api/web/index.md b/api/web/index.md new file mode 100644 index 000000000..9d18d383b --- /dev/null +++ b/api/web/index.md @@ -0,0 +1,610 @@ +--- +title: "Web Platform APIs" +description: "A guide to the Web Platform APIs available in Deno. Learn about fetch, events, workers, storage, and other web standard APIs, including implementation details and deviations from browser specifications." +layout: layout.tsx +oldUrl: + - /runtime/manual/runtime/navigator_api/ + - /runtime/manual/runtime/web_platform_apis/ + - /runtime/manual/runtime/location_api/ + - /runtime/manual/runtime/web_storage_api/ + - /runtime/manual/runtime/workers/ +--- + +Deno implements many of the standard Web Platform APIs that are available in +modern browsers. This means if you've built for the web, you're likely already +familiar with many of Deno's APIs. Learning Deno also means investing in your +knowledge of the web platform. + +## Core Web APIs + +Deno supports a comprehensive set of web standard APIs: + +### Network & Communication + +- **[`fetch`](/api/web/~/fetch)** - HTTP requests following the WHATWG fetch + specification +- **[WebSocket](/api/web/~/WebSocket)** - Full-duplex communication over TCP +- **[Server-Sent Events](/api/web/~/EventSource)** - Server-to-client event + streaming +- **[BroadcastChannel](/api/web/~/BroadcastChannel)** - Cross-context messaging + +### Storage & State + +- **[Web Storage](/api/web/~/Storage)** - localStorage and sessionStorage APIs +- **[Cache API](/api/web/~/Cache)** - HTTP response caching interface +- **[IndexedDB](/api/web/~/IDBDatabase)** - Client-side database storage + +### Workers & Concurrency + +- **[Web Workers](/api/web/~/Worker)** - Background thread execution +- **[SharedArrayBuffer](/api/web/~/SharedArrayBuffer)** - Shared memory between + workers + +### Streams & Data + +- **[Streams API](/api/web/~/ReadableStream)** - Composable streaming data + interfaces +- **[Compression](/api/web/~/CompressionStream)** - Built-in gzip/deflate + compression +- **[File API](/api/web/~/File)** - File and Blob manipulation + +### Events & DOM + +- **[Event API](/api/web/~/Event)** - Standard event handling patterns +- **[CustomEvent](/api/web/~/CustomEvent)** - Custom event creation and dispatch +- **[EventTarget](/api/web/~/EventTarget)** - Event listener management + +### Encoding & Crypto + +- **[TextEncoder/TextDecoder](/api/web/~/TextEncoder)** - UTF-8 text + encoding/decoding +- **[Web Crypto](/api/web/~/SubtleCrypto)** - Cryptographic operations +- **[URL API](/api/web/~/URL)** - URL parsing and manipulation + +### Timers & Performance + +- **[Performance API](/api/web/~/Performance)** - High-resolution timing +- **[Timers](/api/web/~/setTimeout)** - setTimeout, setInterval, and + setImmediate + +## Key Benefits + +- **Standards Compliance**: APIs follow WHATWG and W3C specifications +- **Cross-Platform**: Code works consistently across Deno, browsers, and other + runtimes +- **No Polyfills**: Native implementations provide optimal performance +- **Future-Proof**: Built on evolving web standards + +## Usage Examples + +### Making HTTP Requests + +```javascript +const response = await fetch("https://api.github.com/users/denoland"); +const data = await response.json(); +console.log(data); +``` + +### Using Web Workers + +```javascript +// main.js +const worker = new Worker(import.meta.resolve("./worker.js"), { + type: "module", +}); + +worker.postMessage({ task: "process_data", data: [1, 2, 3] }); +worker.onmessage = (e) => console.log("Result:", e.data); +``` + +```javascript +// worker.js +self.onmessage = (e) => { + const { task, data } = e.data; + const result = data.map((x) => x * 2); + self.postMessage(result); +}; +``` + +### Local Storage + +```javascript +// Persist data across sessions +localStorage.setItem("user_preference", "dark_mode"); +const preference = localStorage.getItem("user_preference"); +``` + +## Compatibility Notes + +To check if a Web Platform API is available in Deno, refer to the interface +documentation on +[MDN](https://developer.mozilla.org/en-US/docs/Web/API#interfaces) and its +Browser Compatibility table. + +Most APIs are implemented according to their specifications, with some +Deno-specific adaptations for server environments. Notable differences are +documented below. + +## fetch + +The [`fetch`](/api/web/~/fetch) API can be used to make HTTP requests. It is +implemented as specified in the +[WHATWG `fetch` spec](https://fetch.spec.whatwg.org/). + +### Spec deviations + +- The Deno user agent does not have a cookie jar. As such, the `set-cookie` + header on a response is not processed, or filtered from the visible response + headers. +- Deno does not follow the same-origin policy, because the Deno user agent + currently does not have the concept of origins, and it does not have a cookie + jar. This means Deno does not need to protect against leaking authenticated + data cross origin. Because of this Deno does not implement the following + sections of the WHATWG `fetch` specification: + - Section `3.1. 'Origin' header`. + - Section `3.2. CORS protocol`. + - Section `3.5. CORB`. + - Section `3.6. 'Cross-Origin-Resource-Policy' header`. + - `Atomic HTTP redirect handling`. + - The `opaqueredirect` response type. +- A `fetch` with a `redirect` mode of `manual` will return a `basic` response + rather than an `opaqueredirect` response. +- The specification is vague on how + [`file:` URLs are to be handled](https://fetch.spec.whatwg.org/#scheme-fetch). + Firefox is the only mainstream browser that implements fetching `file:` URLs, + and even then it doesn't work by default. As of Deno 1.16, Deno supports + fetching local files. See the next section for details. +- The `request` and `response` header guards are implemented, but unlike + browsers do not have any constraints on which header names are allowed. +- The `referrer`, `referrerPolicy`, `mode`, `credentials`, `cache`, `integrity`, + `keepalive`, and `window` properties and their relevant behaviours in + `RequestInit` are not implemented. The relevant fields are not present on the + `Request` object. +- Request body upload streaming is supported (on HTTP/1.1 and HTTP/2). Unlike + the current fetch proposal, the implementation supports duplex streaming. +- The `set-cookie` header is not concatenated when iterated over in the + `headers` iterator. This behaviour is in the + [process of being specified](https://github.com/whatwg/fetch/pull/1346). + +### Fetching local files + +Deno supports fetching `file:` URLs. This makes it easier to write code that +uses the same code path on a server as local, as well as easier to author code +that works both with the Deno CLI and Deno Deploy. + +Deno only supports absolute file URLs, this means that `fetch("./some.json")` +will not work. It should be noted though that if [`--location`](#location) is +specified, relative URLs use the `--location` as the base, but a `file:` URL +cannot be passed as the `--location`. + +To be able to fetch a resource, relative to the current module, which would work +if the module is local or remote, you should to use `import.meta.url` as the +base. For example: + +```js +const response = await fetch(new URL("./config.json", import.meta.url)); +const config = await response.json(); +``` + +Notes on fetching local files: + +- Permissions are applied to reading resources, so an appropriate `--allow-read` + permission is needed to be able to read a local file. +- Fetching locally only supports the `GET` method, and will reject the promise + with any other method. +- A file that does not exist simply rejects the promise with a vague + `TypeError`. This is to avoid the potential of fingerprinting attacks. +- No headers are set on the response. Therefore it is up to the consumer to + determine things like the content type or content length. +- Response bodies are streamed from the Rust side, so large files are available + in chunks, and can be cancelled. + +## CustomEvent and EventTarget + +The [DOM Event API](/api/web/~/Event) can be used to dispatch and listen to +events happening in an application. It is implemented as specified in the +[WHATWG DOM spec](https://dom.spec.whatwg.org/#events). + +### Event API Deviations + +- Events do not bubble, because Deno does not have a DOM hierarchy, so there is + no tree for Events to bubble/capture through. +- `timeStamp` property is always set to `0`. + +## Typings + +The TypeScript definitions for the implemented web APIs can be found in the +[`lib.deno.shared_globals.d.ts`](https://github.com/denoland/deno/blob/main/cli/tsc/dts/lib.deno.shared_globals.d.ts) +and +[`lib.deno.window.d.ts`](https://github.com/denoland/deno/blob/main/cli/tsc/dts/lib.deno.window.d.ts) +files. + +Definitions that are specific to workers can be found in the +[`lib.deno.worker.d.ts`](https://github.com/denoland/deno/blob/main/cli/tsc/dts/lib.deno.worker.d.ts) +file. + +## Location + +Deno supports the [`location`](/api/web/~/Location) global from the web. + +### Location flag + +There is no "web page" whose URL we can use for a location in a Deno process. We +instead allow users to emulate a document location by specifying one on the CLI +using the `--location` flag. It can be a `http` or `https` URL. + +```ts +// deno run --location https://example.com/path main.ts + +console.log(location.href); +// "https://example.com/path" +``` + +You must pass `--location ` for this to work. If you don't, any access to +the `location` global will throw an error. + +```ts +// deno run main.ts + +console.log(location.href); +// error: Uncaught ReferenceError: Access to "location", run again with --location . +``` + +Setting `location` or any of its fields will normally cause navigation in +browsers. This is not applicable in Deno, so it will throw in this situation. + +```ts +// deno run --location https://example.com/path main.ts + +location.pathname = "./foo"; +// error: Uncaught NotSupportedError: Cannot set "location.pathname". +``` + +### Extended usage + +On the web, resource resolution (excluding modules) typically uses the value of +`location.href` as the root on which to base any relative URLs. This affects +some web APIs adopted by Deno. + +#### Fetch API + +```ts +// deno run --location https://api.github.com/ --allow-net main.ts + +const response = await fetch("./orgs/denoland"); +// Fetches "https://api.github.com/orgs/denoland". +``` + +The `fetch()` call above would throw if the `--location` flag was not passed, +since there is no web-analogous location to base it onto. + +#### Worker modules + +```ts +// deno run --location https://example.com/index.html --allow-net main.ts + +const worker = new Worker("./workers/hello.ts", { type: "module" }); +// Fetches worker module at "https://example.com/workers/hello.ts". +``` + +:::note + +For the above use cases, it is preferable to pass URLs in full rather than +relying on `--location`. You can manually base a relative URL using the `URL` +constructor if needed. + +::: + +The `--location` flag is intended for those who have a specific purpose in mind +for emulating a document location and are aware that this will only work at +application-level. However, you may also use it to silence errors from a +dependency which is frivolously accessing the `location` global. + +## Web Storage + +The [Web Storage API](/api/web/storage) provides an API for storing string keys +and values. Persisting data works similar to a browser, and has a 10MB storage +limit. The global `sessionStorage` object only persists data for the current +execution context, while `localStorage` persists data from execution to +execution. + +In a browser, `localStorage` persists data uniquely per origin (effectively the +protocol plus hostname plus port). As of Deno 1.16, Deno has a set of rules to +determine what is a unique storage location: + +- When using the `--location` flag, the origin for the location is used to + uniquely store the data. That means a location of `http://example.com/a.ts` + and `http://example.com/b.ts` and `http://example.com:80/` would all share the + same storage, but `https://example.com/` would be different. +- If there is no location specifier, but there is a `--config` configuration + file specified, the absolute path to that configuration file is used. That + means `deno run --config deno.jsonc a.ts` and + `deno run --config deno.jsonc b.ts` would share the same storage, but + `deno run --config tsconfig.json a.ts` would be different. +- If there is no configuration or location specifier, Deno uses the absolute + path to the main module to determine what storage is shared. The Deno REPL + generates a "synthetic" main module that is based off the current working + directory where `deno` is started from. This means that multiple invocations + of the REPL from the same path will share the persisted `localStorage` data. + +To set, get and remove items from `localStorage`, you can use the following: + +```ts +// Set an item in localStorage +localStorage.setItem("myDemo", "Deno App"); + +// Read an item from localStorage +const cat = localStorage.getItem("myDemo"); + +// Remove an item from localStorage +localStorage.removeItem("myDemo"); + +// Remove all items from localStorage +localStorage.clear(); +``` + +## Web Workers + +Deno supports the [`Web Worker API`](/api/web/workers). + +Workers can be used to run code on multiple threads. Each instance of `Worker` +is run on a separate thread, dedicated only to that worker. + +Currently Deno supports only `module` type workers; thus it's essential to pass +the `type: "module"` option when creating a new worker. + +Use of relative module specifiers in the main worker are only supported with +`--location ` passed on the CLI. This is not recommended for portability. +You can instead use the `URL` constructor and `import.meta.url` to easily create +a specifier for some nearby script. Dedicated workers, however, have a location +and this capability by default. + +```ts +// Good +new Worker(import.meta.resolve("./worker.js"), { type: "module" }); + +// Bad +new Worker(import.meta.resolve("./worker.js")); +new Worker(import.meta.resolve("./worker.js"), { type: "classic" }); +new Worker("./worker.js", { type: "module" }); +``` + +As with regular modules, you can use top-level `await` in worker modules. +However, you should be careful to always register the message handler before the +first `await`, since messages can be lost otherwise. This is not a bug in Deno, +it's just an unfortunate interaction of features, and it also happens in all +browsers that support module workers. + +```ts +import { delay } from "jsr:@std/async@1/delay"; + +// First await: waits for a second, then continues running the module. +await delay(1000); + +// The message handler is only set after that 1s delay, so some of the messages +// that reached the worker during that second might have been fired when no +// handler was registered. +self.onmessage = (evt) => { + console.log(evt.data); +}; +``` + +### Instantiation permissions + +Creating a new `Worker` instance is similar to a dynamic import; therefore Deno +requires appropriate permission for this action. + +For workers using local modules; `--allow-read` permission is required: + +```ts title="main.ts" +new Worker(import.meta.resolve("./worker.ts"), { type: "module" }); +``` + +```ts title="worker.ts" +console.log("hello world"); +self.close(); +``` + +```shell +$ deno run main.ts +error: Uncaught PermissionDenied: read access to "./worker.ts", run again with the --allow-read flag + +$ deno run --allow-read main.ts +hello world +``` + +For workers using remote modules; `--allow-net` permission is required: + +```ts title="main.ts" +new Worker("https://example.com/worker.ts", { type: "module" }); +``` + +```ts title="worker.ts" +// This file is hosted at https://example.com/worker.ts +console.log("hello world"); +self.close(); +``` + +```shell +$ deno run main.ts +error: Uncaught PermissionDenied: net access to "https://example.com/worker.ts", run again with the --allow-net flag + +$ deno run --allow-net main.ts +hello world +``` + +### Using Deno in a worker + +```js title="main.js" +const worker = new Worker(import.meta.resolve("./worker.js"), { + type: "module", +}); + +worker.postMessage({ filename: "./log.txt" }); +``` + +```js title="worker.js" +self.onmessage = async (e) => { + const { filename } = e.data; + const text = await Deno.readTextFile(filename); + console.log(text); + self.close(); +}; +``` + +```text title="log.txt" +hello world +``` + +```shell +$ deno run --allow-read main.js +hello world +``` + +### Specifying worker permissions + +:::caution + +This is an unstable Deno feature. Learn more about +[unstable features](/runtime/fundamentals/stability_and_releases/#unstable-apis). + +::: + +The permissions available for the worker are analogous to the CLI permission +flags, meaning every permission enabled there can be disabled at the level of +the Worker API. You can find a more detailed description of each of the +permission options in the +[security documentation](/runtime/fundamentals/security/). + +By default a worker will inherit permissions from the thread it was created in, +however in order to allow users to limit the access of this worker we provide +the `deno.permissions` option in the worker API. + +For permissions that support granular access you can pass in a list of the +desired resources the worker will have access to, and for those who only have +the on/off option you can pass true/false respectively: + +```ts +const worker = new Worker(import.meta.resolve("./worker.js"), { + type: "module", + deno: { + permissions: { + net: [ + "deno.land", + ], + read: [ + new URL("./file_1.txt", import.meta.url), + new URL("./file_2.txt", import.meta.url), + ], + write: false, + }, + }, +}); +``` + +Granular access permissions receive both absolute and relative routes as +arguments, however take into account that relative routes will be resolved +relative to the file the worker is instantiated in, not the path the worker file +is currently in: + +```ts +const worker = new Worker( + new URL("./worker/worker.js", import.meta.url).href, + { + type: "module", + deno: { + permissions: { + read: [ + "/home/user/Documents/deno/worker/file_1.txt", + "./worker/file_2.txt", + ], + }, + }, + }, +); +``` + +Both `deno.permissions` and its children support the option `"inherit"`, which +implies it will borrow its parent permissions: + +```ts +// This worker will inherit its parent permissions +const worker = new Worker(import.meta.resolve("./worker.js"), { + type: "module", + deno: { + permissions: "inherit", + }, +}); +``` + +```ts +// This worker will inherit only the net permissions of its parent +const worker = new Worker(import.meta.resolve("./worker.js"), { + type: "module", + deno: { + permissions: { + env: false, + hrtime: false, + net: "inherit", + ffi: false, + read: false, + run: false, + write: false, + }, + }, +}); +``` + +Not specifying the `deno.permissions` option or one of its children will cause +the worker to inherit by default: + +```ts +// This worker will inherit its parent permissions +const worker = new Worker(import.meta.resolve("./worker.js"), { + type: "module", +}); +``` + +```ts +// This worker will inherit all the permissions of its parent BUT net +const worker = new Worker(import.meta.resolve("./worker.js"), { + type: "module", + deno: { + permissions: { + net: false, + }, + }, +}); +``` + +You can disable the permissions of the worker all together by passing `"none"` +to the `deno.permissions` option: + +```ts +// This worker will not have any permissions enabled +const worker = new Worker(import.meta.resolve("./worker.js"), { + type: "module", + deno: { + permissions: "none", + }, +}); +``` + +## Deviations of other APIs from spec + +### Cache API + +Only the following APIs are implemented: + +- [CacheStorage::open()](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage/open) +- [CacheStorage::has()](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage/has) +- [CacheStorage::delete()](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage/delete) +- [Cache::match()](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match) +- [Cache::put()](https://developer.mozilla.org/en-US/docs/Web/API/Cache/put) +- [Cache::delete()](https://developer.mozilla.org/en-US/docs/Web/API/Cache/delete) + +A few things that are different compared to browsers: + +1. You cannot pass relative paths to the APIs. The request can be an instance of + Request or URL or a url string. +2. `match()` & `delete()` don't support query options yet. diff --git a/examples/tutorials/connecting_to_databases.md b/examples/tutorials/connecting_to_databases.md index 2d9fcabba..03705e4ad 100644 --- a/examples/tutorials/connecting_to_databases.md +++ b/examples/tutorials/connecting_to_databases.md @@ -132,7 +132,7 @@ db.close(); ### Connect to SQLite with the FFI Module [@db/sqlite](https://jsr.io/@db/sqlite) provides JavaScript bindings to the -SQLite3 C API, using [Deno FFI](/runtime/reference/deno_namespace_apis/#ffi). +SQLite3 C API, using [Deno FFI](/api/deno/#ffi). ```ts import { Database } from "jsr:@db/sqlite@0.12"; diff --git a/oldurls.json b/oldurls.json index d45da950c..5cd5f496b 100644 --- a/oldurls.json +++ b/oldurls.json @@ -75,10 +75,9 @@ "/examples/web-workers": "/examples/web_workers", "/examples/writing-files": "/examples/writing_files", "/examples/writing-tests": "/examples/writing_tests", - "/api/deno/": "/api/deno/~/Deno", - "/api/web/": "/api/web/all_symbols", "/runtime/contributing/profiling/": "/runtime/contributing#profiling", "/runtime/manual/advanced/jsx_dom/jsdom": "/runtime/reference/jsx/", "/examples/stubs": "/examples/stubbing_tutorial", "/examples/create_react_tutorial/": "/examples/react_tutorial/" } + diff --git a/reference/reference.page.tsx b/reference/reference.page.tsx index 7ed6ae1c7..634b55f65 100644 --- a/reference/reference.page.tsx +++ b/reference/reference.page.tsx @@ -45,6 +45,14 @@ export default function* () { continue; } + // Skip generating index pages since we have static versions + if ( + (name === "Deno" || name === "Web" || name === "Node") && + filepath === "./index.json" + ) { + continue; + } + const trailingLength = filepath.endsWith("index.json") ? -"index.json".length : -".json".length; diff --git a/runtime/_data.ts b/runtime/_data.ts index 509955ef0..b1a720e2f 100644 --- a/runtime/_data.ts +++ b/runtime/_data.ts @@ -224,7 +224,7 @@ export const sidebar = [ }, { title: "Deno APIs", - href: "/runtime/reference/deno_namespace_apis/", + href: "/api/deno/", }, { title: "Web APIs", diff --git a/runtime/reference/deno_namespace_apis.md b/runtime/reference/deno_namespace_apis.md index 9ed8e1cd1..0154345f7 100644 --- a/runtime/reference/deno_namespace_apis.md +++ b/runtime/reference/deno_namespace_apis.md @@ -1,6 +1,7 @@ --- title: "Deno Namespace APIs" description: "A guide to Deno's built-in runtime APIs. Learn about file system operations, network functionality, permissions management, and other core capabilities available through the global Deno namespace." +layout: layout.tsx oldUrl: - /runtime/manual/runtime/ - /runtime/manual/runtime/builtin_apis/ @@ -10,949 +11,4 @@ oldUrl: - /runtime/manual/runtime/program_lifecycle/ --- -The global `Deno` namespace contains APIs that are not web standard, including -APIs for reading from files, opening TCP sockets, serving HTTP, and executing -subprocesses, etc. - -Explore all Deno APIs - -Below we highlight some of the most important Deno APIs to know. - -## File System - -The Deno runtime comes with -[various functions for working with files and directories](/api/deno/file-system). -You will need to use --allow-read and --allow-write permissions to gain access -to the file system. - -Refer to the links below for code examples of how to use the file system -functions. - -- [Reading files in streams](/examples/file_server_tutorial/) -- [Reading a text file (`Deno.readTextFile`)](/examples/reading_files/) -- [Writing a text file (`Deno.writeTextFile`)](/examples/writing_files/) - -## Network - -The Deno runtime comes with -[built-in functions for dealing with connections to network ports](/api/deno/network). - -Refer to the links below for code examples for common functions. - -- [Connect to the hostname and port (`Deno.connect`)](/api/deno/~/Deno.connect) -- [Announcing on the local transport address (`Deno.listen`)](/api/deno/~/Deno.listen) - -For practical examples of networking functionality: - -- [HTTP Server: Hello world](/examples/http_server/) -- [HTTP Server: Routing](/examples/http_server_routing/) -- [TCP Echo Server](/examples/tcp_echo_server/) -- [WebSockets example](/examples/http_server_websocket/) -- [Build a chat app with WebSockets tutorial](/examples/chat_app_tutorial/) - -## Subprocesses - -The Deno runtime comes with -[built-in functions for spinning up subprocesses](/api/deno/subprocess). - -Refer to the links below for code samples of how to create a subprocess. - -- [Creating a subprocess (`Deno.Command`)](/examples/subprocess_tutorial/) -- [Collecting output from subprocesses](/examples/subprocesses_output/) - -## Errors - -The Deno runtime comes with [20 error classes](/api/deno/errors) that can be -raised in response to a number of conditions. - -Some examples are: - -```sh -Deno.errors.NotFound; -Deno.errors.WriteZero; -``` - -They can be used as below: - -```ts -try { - const file = await Deno.open("./some/file.txt"); -} catch (error) { - if (error instanceof Deno.errors.NotFound) { - console.error("the file was not found"); - } else { - // otherwise re-throw - throw error; - } -} -``` - -## HTTP Server - -Deno has two HTTP Server APIs: - -- [`Deno.serve`](/api/deno/~/Deno.serve): native, _higher-level_, supports - HTTP/1.1 and HTTP2, this is the preferred API to write HTTP servers in Deno. -- [`Deno.serveHttp`](/api/deno/~/Deno.serveHttp): native, _low-level_, supports - HTTP/1.1 and HTTP2. - -To start an HTTP server on a given port, use the `Deno.serve` function. This -function takes a handler function that will be called for each incoming request, -and is expected to return a response (or a promise resolving to a response). For -example: - -```ts -Deno.serve((_req) => { - return new Response("Hello, World!"); -}); -``` - -By default `Deno.serve` will listen on port `8000`, but this can be changed by -passing in a port number in options bag as the first or second argument. - -You can -[read more about how to use the HTTP server APIs](/runtime/fundamentals/http_server/). - -For practical examples of HTTP servers: - -- [Simple file server tutorial](/examples/file_server_tutorial/) -- [HTTP server serving files](/examples/http_server_files/) -- [HTTP server with streaming](/examples/http_server_streaming/) -- [HTTP server WebSockets](/examples/http_server_websocket/) - -## Permissions - -Permissions are granted from the CLI when running the `deno` command. User code -will often assume its own set of required permissions, but there is no guarantee -during execution that the set of **granted** permissions will align with this. - -In some cases, ensuring a fault-tolerant program requires a way to interact with -the permission system at runtime. - -### Permission descriptors - -On the CLI, read permission for `/foo/bar` is represented as -`--allow-read=/foo/bar`. In runtime JS, it is represented as the following: - -```ts -const desc = { name: "read", path: "/foo/bar" } as const; -``` - -Other examples: - -```ts -// Global write permission. -const desc1 = { name: "write" } as const; - -// Write permission to `$PWD/foo/bar`. -const desc2 = { name: "write", path: "foo/bar" } as const; - -// Global net permission. -const desc3 = { name: "net" } as const; - -// Net permission to 127.0.0.1:8000. -const desc4 = { name: "net", host: "127.0.0.1:8000" } as const; - -// High-resolution time permission. -const desc5 = { name: "hrtime" } as const; -``` - -See [`PermissionDescriptor`](/api/deno/~/Deno.PermissionDescriptor) in API -reference for more details. Synchronous API counterparts (ex. -`Deno.permissions.querySync`) exist for all the APIs described below. - -### Query permissions - -Check, by descriptor, if a permission is granted or not. - -```ts -// deno run --allow-read=/foo main.ts - -const desc1 = { name: "read", path: "/foo" } as const; -console.log(await Deno.permissions.query(desc1)); -// PermissionStatus { state: "granted", partial: false } - -const desc2 = { name: "read", path: "/foo/bar" } as const; -console.log(await Deno.permissions.query(desc2)); -// PermissionStatus { state: "granted", partial: false } - -const desc3 = { name: "read", path: "/bar" } as const; -console.log(await Deno.permissions.query(desc3)); -// PermissionStatus { state: "prompt", partial: false } -``` - -If `--deny-read` flag was used to restrict some of the filepaths, the result -will contain `partial: true` describing that not all subpaths have permissions -granted: - -```ts -// deno run --allow-read=/foo --deny-read=/foo/bar main.ts - -const desc1 = { name: "read", path: "/foo" } as const; -console.log(await Deno.permissions.query(desc1)); -// PermissionStatus { state: "granted", partial: true } - -const desc2 = { name: "read", path: "/foo/bar" } as const; -console.log(await Deno.permissions.query(desc2)); -// PermissionStatus { state: "denied", partial: false } - -const desc3 = { name: "read", path: "/bar" } as const; -console.log(await Deno.permissions.query(desc3)); -// PermissionStatus { state: "prompt", partial: false } -``` - -### Permission states - -A permission state can be either "granted", "prompt" or "denied". Permissions -which have been granted from the CLI will query to `{ state: "granted" }`. Those -which have not been granted query to `{ state: "prompt" }` by default, while -`{ state: "denied" }` reserved for those which have been explicitly refused. -This will come up in [Request permissions](#request-permissions). - -### Permission strength - -The intuitive understanding behind the result of the second query in -[Query permissions](#query-permissions) is that read access was granted to -`/foo` and `/foo/bar` is within `/foo` so `/foo/bar` is allowed to be read. This -hold true, unless the CLI-granted permission is _partial_ to the queried -permissions (as an effect of using a `--deny-*` flag). - -We can also say that `desc1` is -_[stronger than](https://www.w3.org/TR/permissions/#ref-for-permissiondescriptor-stronger-than)_ -`desc2`. This means that for any set of CLI-granted permissions: - -1. If `desc1` queries to `{ state: "granted", partial: false }` then so must - `desc2`. -2. If `desc2` queries to `{ state: "denied", partial: false }` then so must - `desc1`. - -More examples: - -```ts -const desc1 = { name: "write" } as const; -// is stronger than -const desc2 = { name: "write", path: "/foo" } as const; - -const desc3 = { name: "net", host: "127.0.0.1" } as const; -// is stronger than -const desc4 = { name: "net", host: "127.0.0.1:8000" } as const; -``` - -### Request permissions - -Request an ungranted permission from the user via CLI prompt. - -```ts -// deno run main.ts - -const desc1 = { name: "read", path: "/foo" } as const; -const status1 = await Deno.permissions.request(desc1); -// ⚠️ Deno requests read access to "/foo". Grant? [y/n (y = yes allow, n = no deny)] y -console.log(status1); -// PermissionStatus { state: "granted", partial: false } - -const desc2 = { name: "read", path: "/bar" } as const; -const status2 = await Deno.permissions.request(desc2); -// ⚠️ Deno requests read access to "/bar". Grant? [y/n (y = yes allow, n = no deny)] n -console.log(status2); -// PermissionStatus { state: "denied", partial: false } -``` - -If the current permission state is "prompt", a prompt will appear on the user's -terminal asking them if they would like to grant the request. The request for -`desc1` was granted so its new status is returned and execution will continue as -if `--allow-read=/foo` was specified on the CLI. The request for `desc2` was -denied so its permission state is downgraded from "prompt" to "denied". - -If the current permission state is already either "granted" or "denied", the -request will behave like a query and just return the current status. This -prevents prompts both for already granted permissions and previously denied -requests. - -### Revoke permissions - -Downgrade a permission from "granted" to "prompt". - -```ts -// deno run --allow-read=/foo main.ts - -const desc = { name: "read", path: "/foo" } as const; -console.log(await Deno.permissions.revoke(desc)); -// PermissionStatus { state: "prompt", partial: false } -``` - -What happens when you try to revoke a permission which is _partial_ to one -granted on the CLI? - -```ts -// deno run --allow-read=/foo main.ts - -const desc = { name: "read", path: "/foo/bar" } as const; -console.log(await Deno.permissions.revoke(desc)); -// PermissionStatus { state: "prompt", partial: false } -const cliDesc = { name: "read", path: "/foo" } as const; -console.log(await Deno.permissions.revoke(cliDesc)); -// PermissionStatus { state: "prompt", partial: false } -``` - -The CLI-granted permission, which implies the revoked permission, was also -revoked. - -To understand this behavior, imagine that Deno stores an internal set of -_explicitly granted permission descriptors_. Specifying `--allow-read=/foo,/bar` -on the CLI initializes this set to: - -```ts -[ - { name: "read", path: "/foo" }, - { name: "read", path: "/bar" }, -]; -``` - -Granting a runtime request for `{ name: "write", path: "/foo" }` updates the set -to: - -```ts -[ - { name: "read", path: "/foo" }, - { name: "read", path: "/bar" }, - { name: "write", path: "/foo" }, -]; -``` - -Deno's permission revocation algorithm works by removing every element from this -set which is _stronger than_ the argument permission descriptor. - -Deno does not allow "fragmented" permission states, where some strong permission -is granted with exclusions of weak permissions implied by it. Such a system -would prove increasingly complex and unpredictable as you factor in a wider -variety of use cases and the `"denied"` state. This is a calculated trade-off of -granularity for security. - -## import.meta - -Deno supports a number of properties and methods on the -[`import.meta`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta) -API. It can be used to get information about the module, such as the module's -URL. - -### import.meta.url - -Returns the URL of the current module. - -```ts title="main.ts" -console.log(import.meta.url); -``` - -```sh -$ deno run main.ts -file:///dev/main.ts - -$ deno run https:/example.com/main.ts -https://example.com/main.ts -``` - -### import.meta.main - -Returns whether the current module is the entry point to your program. - -```ts title="main.ts" -import "./other.ts"; - -console.log(`Is ${import.meta.url} the main module?`, import.meta.main); -``` - -```ts title="other.ts" -console.log(`Is ${import.meta.url} the main module?`, import.meta.main); -``` - -```sh -$ deno run main.ts -Is file:///dev/other.ts the main module? false -Is file:///dev/main.ts the main module? true -``` - -### import.meta.filename - -_This property is only available for local modules (module that have -`file:///...` specifier) and returns `undefined` for remote modules._ - -Returns the fully resolved path to the current module. The value contains OS -specific path separators. - -```ts title="main.ts" -console.log(import.meta.filename); -``` - -On Unix: - -```sh -$ deno run main.ts -/dev/main.ts - -$ deno run https://example.com/main.ts -undefined -``` - -On Windows: - -```sh -$ deno run main.ts -C:\dev\main.ts - -$ deno run https://example.com/main.ts -undefined -``` - -### import.meta.dirname - -_This property is only available for local modules (module that have -`file:///...` specifier) and returns `undefined` for remote modules._ - -Returns the fully resolved path to the directory containing the current module. -The value contains OS specific path separators. - -```ts title="main.ts" -console.log(import.meta.dirname); -``` - -On Unix: - -```sh -$ deno run main.ts -/dev/ - -$ deno run https://example.com/main.ts -undefined -``` - -On Windows: - -```sh -$ deno run main.ts -C:\dev\ - -$ deno run https://example.com/main.ts -undefined -``` - -### import.meta.resolve - -Resolve specifiers relative to the current module. - -```ts -const worker = new Worker(import.meta.resolve("./worker.ts")); -``` - -The `import.meta.resolve` API takes into account the currently applied import -map, which gives you the ability to resolve "bare" specifiers as well. - -With such import map loaded... - -```json -{ - "imports": { - "fresh": "https://deno.land/x/fresh@1.0.1/dev.ts" - } -} -``` - -...you can now resolve: - -```js title="resolve.js" -console.log(import.meta.resolve("fresh")); -``` - -```sh -$ deno run resolve.js -https://deno.land/x/fresh@1.0.1/dev.ts -``` - -## FFI - -The FFI (foreign function interface) API allows users to call libraries written -in native languages that support the C ABIs (C/C++, Rust, Zig, V, etc.) using -`Deno.dlopen`. - -Here's an example showing how to call a Rust function from Deno: - -```rust -// add.rs -#[no_mangle] -pub extern "C" fn add(a: isize, b: isize) -> isize { - a + b -} -``` - -Compile it to a C dynamic library (`libadd.so` on Linux): - -```sh -rustc --crate-type cdylib add.rs -``` - -In C you can write it as: - -```c -// add.c -int add(int a, int b) { - return a + b; -} -``` - -And compile it: - -```sh -// unix -cc -c -o add.o add.c -cc -shared -W -o libadd.so add.o -// Windows -cl /LD add.c /link /EXPORT:add -``` - -Calling the library from Deno: - -```typescript -// ffi.ts - -// Determine library extension based on -// your OS. -let libSuffix = ""; -switch (Deno.build.os) { - case "windows": - libSuffix = "dll"; - break; - case "darwin": - libSuffix = "dylib"; - break; - default: - libSuffix = "so"; - break; -} - -const libName = `./libadd.${libSuffix}`; -// Open library and define exported symbols -const dylib = Deno.dlopen( - libName, - { - "add": { parameters: ["isize", "isize"], result: "isize" }, - } as const, -); - -// Call the symbol `add` -const result = dylib.symbols.add(35, 34); // 69 - -console.log(`Result from external addition of 35 and 34: ${result}`); -``` - -Run with `--allow-ffi` and `--unstable` flag: - -```sh -deno run --allow-ffi --unstable ffi.ts -``` - -### Non-blocking FFI - -There are many use cases where users might want to run CPU-bound FFI functions -in the background without blocking other tasks on the main thread. - -As of Deno 1.15, symbols can be marked `nonblocking` in `Deno.dlopen`. These -function calls will run on a dedicated blocking thread and will return a -`Promise` resolving to the desired `result`. - -Example of executing expensive FFI calls with Deno: - -```c -// sleep.c -#ifdef _WIN32 -#include -#else -#include -#endif - -int sleep(unsigned int ms) { - #ifdef _WIN32 - Sleep(ms); - #else - struct timespec ts; - ts.tv_sec = ms / 1000; - ts.tv_nsec = (ms % 1000) * 1000000; - nanosleep(&ts, NULL); - #endif -} -``` - -Calling it from Deno: - -```typescript -// nonblocking_ffi.ts -const library = Deno.dlopen( - "./sleep.so", - { - sleep: { - parameters: ["usize"], - result: "void", - nonblocking: true, - }, - } as const, -); - -library.symbols.sleep(500).then(() => console.log("After")); -console.log("Before"); -``` - -Result: - -```sh -$ deno run --allow-ffi --unstable unblocking_ffi.ts -Before -After -``` - -### Callbacks - -Deno FFI API supports creating C callbacks from JavaScript functions for calling -back into Deno from dynamic libraries. An example of how callbacks are created -and used is as follows: - -```typescript -// callback_ffi.ts -const library = Deno.dlopen( - "./callback.so", - { - set_status_callback: { - parameters: ["function"], - result: "void", - }, - start_long_operation: { - parameters: [], - result: "void", - }, - check_status: { - parameters: [], - result: "void", - }, - } as const, -); - -const callback = new Deno.UnsafeCallback( - { - parameters: ["u8"], - result: "void", - } as const, - (success: number) => {}, -); - -// Pass the callback pointer to dynamic library -library.symbols.set_status_callback(callback.pointer); -// Start some long operation that does not block the thread -library.symbols.start_long_operation(); - -// Later, trigger the library to check if the operation is done. -// If it is, this call will trigger the callback. -library.symbols.check_status(); -``` - -If an `UnsafeCallback`'s callback function throws an error, the error will get -propagated up to the function that triggered the callback to be called (above, -that would be `check_status()`) and can be caught there. If a callback returning -a value throws then Deno will return 0 (null pointer for pointers) as the -result. - -`UnsafeCallback` is not deallocated by default as it can cause use-after-free -bugs. To properly dispose of an `UnsafeCallback` its `close()` method must be -called. - -```typescript -const callback = new Deno.UnsafeCallback( - { parameters: [], result: "void" } as const, - () => {}, -); - -// After callback is no longer needed -callback.close(); -// It is no longer safe to pass the callback as a parameter. -``` - -It is also possible for native libraries to setup interrupt handlers and to have -those directly trigger the callback. However, this is not recommended and may -cause unexpected side-effects and undefined behaviour. Preferably any interrupt -handlers would only set a flag that can later be polled similarly to how -`check_status()` is used above. - -### Supported types - -Here's a list of types supported currently by the Deno FFI API. - -| FFI Type | Deno | C | Rust | -| ---------------------- | -------------------- | ------------------------ | ------------------------- | -| `i8` | `number` | `char` / `signed char` | `i8` | -| `u8` | `number` | `unsigned char` | `u8` | -| `i16` | `number` | `short int` | `i16` | -| `u16` | `number` | `unsigned short int` | `u16` | -| `i32` | `number` | `int` / `signed int` | `i32` | -| `u32` | `number` | `unsigned int` | `u32` | -| `i64` | `bigint` | `long long int` | `i64` | -| `u64` | `bigint` | `unsigned long long int` | `u64` | -| `usize` | `bigint` | `size_t` | `usize` | -| `isize` | `bigint` | `size_t` | `isize` | -| `f32` | `number` | `float` | `f32` | -| `f64` | `number` | `double` | `f64` | -| `void`[1] | `undefined` | `void` | `()` | -| `pointer` | `{} \| null` | `void *` | `*mut c_void` | -| `buffer`[2] | `TypedArray \| null` | `uint8_t *` | `*mut u8` | -| `function`[3] | `{} \| null` | `void (*fun)()` | `Option` | -| `{ struct: [...] }`[4] | `TypedArray` | `struct MyStruct` | `MyStruct` | - -As of Deno 1.25, the `pointer` type has been split into a `pointer` and a -`buffer` type to ensure users take advantage of optimizations for Typed Arrays, -and as of Deno 1.31 the JavaScript representation of `pointer` has become an -opaque pointer object or `null` for null pointers. - -- [1] `void` type can only be used as a result type. -- [2] `buffer` type accepts TypedArrays as parameter, but it always returns a - pointer object or `null` when used as result type like the `pointer` type. -- [3] `function` type works exactly the same as the `pointer` type as a - parameter and result type. -- [4] `struct` type is for passing and returning C structs by value (copy). The - `struct` array must enumerate each of the struct's fields' type in order. The - structs are padded automatically: Packed structs can be defined by using an - appropriate amount of `u8` fields to avoid padding. Only TypedArrays are - supported as structs, and structs are always returned as `Uint8Array`s. - -### deno_bindgen - -[`deno_bindgen`](https://github.com/denoland/deno_bindgen) is the official tool -to simplify glue code generation of Deno FFI libraries written in Rust. - -It is similar to [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) in -the Rust Wasm ecosystem. - -Here's an example showing its usage: - -```rust -// mul.rs -use deno_bindgen::deno_bindgen; - -#[deno_bindgen] -struct Input { - a: i32, - b: i32, -} - -#[deno_bindgen] -fn mul(input: Input) -> i32 { - input.a * input.b -} -``` - -Run `deno_bindgen` to generate bindings. You can now directly import them into -Deno: - -```ts -// mul.ts -import { mul } from "./bindings/bindings.ts"; -mul({ a: 10, b: 2 }); // 20 -``` - -Any issues related to `deno_bindgen` should be reported at -https://github.com/denoland/deno_bindgen/issues - -## Program Lifecycle - -Deno supports browser compatible lifecycle events: - -- [`load`](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event#:~:text=The%20load%20event%20is%20fired,for%20resources%20to%20finish%20loading.): - fired when the whole page has loaded, including all dependent resources such - as stylesheets and images. -- [`beforeunload`](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#:~:text=The%20beforeunload%20event%20is%20fired,want%20to%20leave%20the%20page.): - fired when the event loop has no more work to do and is about to exit. - Scheduling more asynchronous work (like timers or network requests) will cause - the program to continue. -- [`unload`](https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event): - fired when the document or a child resource is being unloaded. -- [`unhandledrejection`](https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event): - fired when a promise that has no rejection handler is rejected, ie. a promise - that has no `.catch()` handler or a second argument to `.then()`. -- [`rejectionhandled`](https://developer.mozilla.org/en-US/docs/Web/API/Window/rejectionhandled_event): - fired when a `.catch()` handler is added to a a promise that has already - rejected. This event is fired only if there's `unhandledrejection` listener - installed that prevents propagation of the event (which would result in the - program terminating with an error). - -You can use these events to provide setup and cleanup code in your program. - -Listeners for `load` events can be asynchronous and will be awaited, this event -cannot be canceled. Listeners for `beforeunload` need to be synchronous and can -be cancelled to keep the program running. Listeners for `unload` events need to -be synchronous and cannot be cancelled. - -**main.ts** - -```ts title="main.ts" -import "./imported.ts"; - -const handler = (e: Event): void => { - console.log(`got ${e.type} event in event handler (main)`); -}; - -globalThis.addEventListener("load", handler); - -globalThis.addEventListener("beforeunload", handler); - -globalThis.addEventListener("unload", handler); - -globalThis.onload = (e: Event): void => { - console.log(`got ${e.type} event in onload function (main)`); -}; - -globalThis.onbeforeunload = (e: Event): void => { - console.log(`got ${e.type} event in onbeforeunload function (main)`); -}; - -globalThis.onunload = (e: Event): void => { - console.log(`got ${e.type} event in onunload function (main)`); -}; - -console.log("log from main script"); -``` - -```ts title="imported.ts" -const handler = (e: Event): void => { - console.log(`got ${e.type} event in event handler (imported)`); -}; - -globalThis.addEventListener("load", handler); -globalThis.addEventListener("beforeunload", handler); -globalThis.addEventListener("unload", handler); - -globalThis.onload = (e: Event): void => { - console.log(`got ${e.type} event in onload function (imported)`); -}; - -globalThis.onbeforeunload = (e: Event): void => { - console.log(`got ${e.type} event in onbeforeunload function (imported)`); -}; - -globalThis.onunload = (e: Event): void => { - console.log(`got ${e.type} event in onunload function (imported)`); -}; - -console.log("log from imported script"); -``` - -A couple notes on this example: - -- `addEventListener` and `onload`/`onunload` are prefixed with `globalThis`, but - you could also use `self` or no prefix at all. - [It is not recommended to use `window` as a prefix](https://docs.deno.com/lint/rules/no-window-prefix). -- You can use `addEventListener` and/or `onload`/`onunload` to define handlers - for events. There is a major difference between them, let's run the example: - -```shell -$ deno run main.ts -log from imported script -log from main script -got load event in event handler (imported) -got load event in event handler (main) -got load event in onload function (main) -got onbeforeunload event in event handler (imported) -got onbeforeunload event in event handler (main) -got onbeforeunload event in onbeforeunload function (main) -got unload event in event handler (imported) -got unload event in event handler (main) -got unload event in onunload function (main) -``` - -All listeners added using `addEventListener` were run, but `onload`, -`onbeforeunload` and `onunload` defined in `main.ts` overrode handlers defined -in `imported.ts`. - -In other words, you can use `addEventListener` to register multiple `"load"` or -`"unload"` event handlers, but only the last defined `onload`, `onbeforeunload`, -`onunload` event handlers will be executed. It is preferable to use -`addEventListener` when possible for this reason. - -### beforeunload - -```js -// beforeunload.js -let count = 0; - -console.log(count); - -globalThis.addEventListener("beforeunload", (e) => { - console.log("About to exit..."); - if (count < 4) { - e.preventDefault(); - console.log("Scheduling more work..."); - setTimeout(() => { - console.log(count); - }, 100); - } - - count++; -}); - -globalThis.addEventListener("unload", (e) => { - console.log("Exiting"); -}); - -count++; -console.log(count); - -setTimeout(() => { - count++; - console.log(count); -}, 100); -``` - -Running this program will print: - -```sh -$ deno run beforeunload.js -0 -1 -2 -About to exit... -Scheduling more work... -3 -About to exit... -Scheduling more work... -4 -About to exit... -Exiting -``` - -### unhandledrejection event - -This event is fired when a promise that has no rejection handler is rejected, -ie. a promise that has no .catch() handler or a second argument to .then(). - -```js -// unhandledrejection.js -globalThis.addEventListener("unhandledrejection", (e) => { - console.log("unhandled rejection at:", e.promise, "reason:", e.reason); - e.preventDefault(); -}); - -function Foo() { - this.bar = Promise.reject(new Error("bar not available")); -} - -new Foo(); -Promise.reject(); -``` - -Running this program will print: - -```sh -$ deno run unhandledrejection.js -unhandled rejection at: Promise { - Error: bar not available - at new Foo (file:///dev/unhandled_rejection.js:7:29) - at file:///dev/unhandled_rejection.js:10:1 -} reason: Error: bar not available - at new Foo (file:///dev/unhandled_rejection.js:7:29) - at file:///dev/unhandled_rejection.js:10:1 -unhandled rejection at: Promise { undefined } reason: undefined -``` +This page has moved to [/api/deno/](/api/deno/). From 164b66c6dd41c2218031dcb7516155e732ef9cb0 Mon Sep 17 00:00:00 2001 From: Jo Franchetti Date: Thu, 14 Aug 2025 15:27:37 +0100 Subject: [PATCH 3/5] style landing page --- .vscode/tasks.json | 30 ++++++++++++++++++++---------- _includes/doc.tsx | 46 ++++++++++++++++++++++++++++++++++++++-------- api/deno/index.md | 2 +- api/node/index.md | 2 +- api/web/index.md | 2 +- 5 files changed, 61 insertions(+), 21 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b2a96a4f8..df31db49d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,12 +1,22 @@ { - "version": "2.0.0", - "tasks": [ - { - "label": "Dev Server", - "type": "shell", - "command": "deno task start", - "group": "serve", - "isBackground": true - } - ] + "version": "2.0.0", + "tasks": [ + { + "label": "Dev Server", + "type": "shell", + "command": "deno task start", + "group": "serve", + "isBackground": true + }, + { + "label": "Dev Server", + "type": "shell", + "command": "deno task start", + "isBackground": true, + "problemMatcher": [ + "$deno" + ], + "group": "serve" + } + ] } diff --git a/_includes/doc.tsx b/_includes/doc.tsx index 9047b3c33..a43d2ee2f 100644 --- a/_includes/doc.tsx +++ b/_includes/doc.tsx @@ -33,6 +33,33 @@ export default function Doc(data: Lume.Data, helpers: Lume.Helpers) { file = `/lint/rules/${encodeURIComponent(data.title ?? "")}.md`; } + function getTocCtx( + d: Lume.Data, + ): { document_navigation: unknown; document_navigation_str: string } | null { + const ch: unknown = (d as unknown as { children?: unknown })?.children; + if (ch && typeof ch === "object" && "props" in ch) { + const props: unknown = (ch as { props?: unknown }).props; + if (props && typeof props === "object" && "data" in props) { + const pdata: unknown = (props as { data?: unknown }).data; + if (pdata && typeof pdata === "object" && "toc_ctx" in pdata) { + const toc: unknown = (pdata as { toc_ctx?: unknown }).toc_ctx; + if ( + toc && typeof toc === "object" && + "document_navigation" in toc && + "document_navigation_str" in toc + ) { + const t = toc as { + document_navigation: unknown; + document_navigation_str: string; + }; + return t; + } + } + } + } + return null; + } + return (
- {(isReference && data.children.props.data.toc_ctx) && ( - - )} + {(() => { + const tocCtx = getTocCtx(data); + return isReference && tocCtx + ? ( + + ) + : null; + })()}
); } diff --git a/api/deno/index.md b/api/deno/index.md index c14e34ef7..b3c6db42b 100644 --- a/api/deno/index.md +++ b/api/deno/index.md @@ -1,7 +1,7 @@ --- title: "Deno Namespace APIs" description: "A guide to Deno's built-in runtime APIs. Learn about file system operations, network functionality, permissions management, and other core capabilities available through the global Deno namespace." -layout: layout.tsx +layout: doc.tsx oldUrl: - /runtime/manual/runtime/ - /runtime/manual/runtime/builtin_apis/ diff --git a/api/node/index.md b/api/node/index.md index 67e447942..ac5afd6df 100644 --- a/api/node/index.md +++ b/api/node/index.md @@ -1,7 +1,7 @@ --- title: "Node.js Built-in APIs" description: "Complete reference for Node.js built-in modules and globals supported in Deno. Explore Node.js APIs including fs, http, crypto, process, buffer, and more with compatibility information." -layout: layout.tsx +layout: doc.tsx oldUrl: - /runtime/manual/node/compatibility/ - /runtime/manual/npm_nodejs/compatibility_mode/ diff --git a/api/web/index.md b/api/web/index.md index 9d18d383b..196e56dc3 100644 --- a/api/web/index.md +++ b/api/web/index.md @@ -1,7 +1,7 @@ --- title: "Web Platform APIs" description: "A guide to the Web Platform APIs available in Deno. Learn about fetch, events, workers, storage, and other web standard APIs, including implementation details and deviations from browser specifications." -layout: layout.tsx +layout: doc.tsx oldUrl: - /runtime/manual/runtime/navigator_api/ - /runtime/manual/runtime/web_platform_apis/ From 8f93cdd982495b714ce5154e9fd63d44ac71323f Mon Sep 17 00:00:00 2001 From: Jo Franchetti Date: Thu, 14 Aug 2025 15:46:03 +0100 Subject: [PATCH 4/5] fmt --- .vscode/tasks.json | 40 ++++++++++++++++++++-------------------- oldurls.json | 1 - 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index df31db49d..88337de58 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,22 +1,22 @@ { - "version": "2.0.0", - "tasks": [ - { - "label": "Dev Server", - "type": "shell", - "command": "deno task start", - "group": "serve", - "isBackground": true - }, - { - "label": "Dev Server", - "type": "shell", - "command": "deno task start", - "isBackground": true, - "problemMatcher": [ - "$deno" - ], - "group": "serve" - } - ] + "version": "2.0.0", + "tasks": [ + { + "label": "Dev Server", + "type": "shell", + "command": "deno task start", + "group": "serve", + "isBackground": true + }, + { + "label": "Dev Server", + "type": "shell", + "command": "deno task start", + "isBackground": true, + "problemMatcher": [ + "$deno" + ], + "group": "serve" + } + ] } diff --git a/oldurls.json b/oldurls.json index 5cd5f496b..337214ab7 100644 --- a/oldurls.json +++ b/oldurls.json @@ -80,4 +80,3 @@ "/examples/stubs": "/examples/stubbing_tutorial", "/examples/create_react_tutorial/": "/examples/react_tutorial/" } - From 889d0f2e901954bb283cafb32f3e3d7b17c8423b Mon Sep 17 00:00:00 2001 From: Jo Franchetti Date: Mon, 18 Aug 2025 09:41:13 +0100 Subject: [PATCH 5/5] update copy --- api/deno/index.md | 8 +++++++- api/node/index.md | 2 ++ api/web/index.md | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/api/deno/index.md b/api/deno/index.md index b3c6db42b..e4d20320a 100644 --- a/api/deno/index.md +++ b/api/deno/index.md @@ -16,7 +16,13 @@ The global `Deno` namespace contains APIs that are not web standard, including APIs for reading from files, opening TCP sockets, serving HTTP, and executing subprocesses, etc. -[Explore all Deno APIs](/api/deno/all_symbols){.docs-cta .runtime-cta} +## API Documentation + +This section provides comprehensive documentation for all Deno-specific APIs +available through the global `Deno` namespace. You can +[browse all symbols](/api/deno/all_symbols) to view the complete list of +available APIs or search by category. Click on any function or interface to see +detailed documentation with examples Below we highlight some of the most important Deno APIs to know. diff --git a/api/node/index.md b/api/node/index.md index ac5afd6df..1f9f8bff8 100644 --- a/api/node/index.md +++ b/api/node/index.md @@ -12,6 +12,8 @@ enabling seamless migration of Node.js applications and libraries. These APIs follow Node.js specifications and provide familiar functionality for developers transitioning from Node.js. + + ## Key Features - **Built-in Module Support**: Access Node.js modules using `node:` prefix diff --git a/api/web/index.md b/api/web/index.md index 196e56dc3..356b46655 100644 --- a/api/web/index.md +++ b/api/web/index.md @@ -15,6 +15,11 @@ modern browsers. This means if you've built for the web, you're likely already familiar with many of Deno's APIs. Learning Deno also means investing in your knowledge of the web platform. +This section provides comprehensive documentation for all Deno's supported Web +APIs You can [browse all symbols](/api/web/all_symbols) to view the complete +list or search by category. Click on any function or interface to see detailed +documentation with examples + ## Core Web APIs Deno supports a comprehensive set of web standard APIs: