Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@thi.ng/wasm-api

Package Overview
Dependencies
Maintainers
0
Versions
132
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thi.ng/wasm-api - npm Package Compare versions

Comparing version 1.6.7 to 2.0.0

memory.d.ts

54

api.d.ts

@@ -1,2 +0,2 @@

import type { EVENT_ALL, Fn, IDeref, ILength } from "@thi.ng/api";
import type { BigTypedArray, EVENT_ALL, Fn, IDeref, ILength, TypedArray, Values } from "@thi.ng/api";
import type { WasmBridge } from "./bridge.js";

@@ -56,14 +56,2 @@ export declare const EVENT_MEMORY_CHANGED = "memory-changed";

/**
* The unique ID for grouping the WASM imports of this module. MUST be the
* same as used by the native side of the module.
*/
readonly id: string;
/**
* IDs of other WASM API modules which this module depends on. Used to infer
* correct initialization order. The core module (w/ unique ID: `wasmapi`)
* is always considered an implicit dependency, will be initialized first
* and MUST NOT be stated here.
*/
readonly dependencies?: string[];
/**
* Called by {@link WasmBridge.init} to initialize all child APIs (async)

@@ -83,2 +71,27 @@ * after the WASM module has been instantiated. If the method returns false

}
export interface WasmModuleSpec<T extends WasmExports = WasmExports> {
/**
* The unique ID for grouping the WASM imports of this module. MUST be the
* same as used by the native side of the module.
*/
id: string;
/**
* Optional array of other {@link WasmModuleSpec}s which this module depends
* on (element order is irrelevant). Used to construct a module dependency
* graph and the correct initialization order of modules. The core module
* (defined via {@link WasmBridge} w/ unique ID: `wasmapi`) is always
* considered an implicit dependency, will be initialized first and MUST NOT
* be stated here.
*/
deps?: WasmModuleSpec<T>[];
/**
* Factory function to pre-instantiate the API module.
*
* @remarks
* Note: All modules will only be fully initialized at a later point via
* {@link WasmBridge.instantiate} or {@link WasmBridge.init} and each
* modules own {@link IWasmAPI.init} method.
*/
factory: Fn<WasmBridge<T>, IWasmAPI<T>>;
}
/**

@@ -138,2 +151,17 @@ * Base interface of exports declared by the WASM module. At the very least, a

export type MemorySlice = [addr: number, len: number];
export type MemoryViewType = "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "f32" | "f64";
export interface MemoryViewTypeMap extends Record<MemoryViewType, TypedArray | BigTypedArray> {
u8: Uint8Array;
u8c: Uint8ClampedArray;
i8: Int8Array;
u16: Uint16Array;
i16: Int16Array;
u32: Uint32Array;
i32: Int32Array;
i64: BigInt64Array;
u64: BigUint64Array;
f32: Float32Array;
f64: Float64Array;
}
export type MemoryView = Values<MemoryViewTypeMap>;
export interface IWasmMemoryAccess {

@@ -140,0 +168,0 @@ i8: Int8Array;

import type { Event, INotify, IObjectOf, Listener, NumericArray } from "@thi.ng/api";
import type { ILogger } from "@thi.ng/logger";
import { type BigIntArray, type BridgeEventType, type CoreAPI, type IWasmAPI, type IWasmMemoryAccess, type MemorySlice, type WasmExports } from "./api.js";
import { type BigIntArray, type BridgeEventType, type CoreAPI, type IWasmAPI, type IWasmMemoryAccess, type MemorySlice, type WasmExports, type WasmModuleSpec } from "./api.js";
export declare const Panic: {

@@ -63,4 +63,18 @@ new (msg?: string | undefined): {

modules: IObjectOf<IWasmAPI<T>>;
constructor(modules?: IWasmAPI<T>[], logger?: ILogger);
order: string[];
constructor(modules?: WasmModuleSpec<T>[], logger?: ILogger);
/**
* Takes array of root module specs, extracts all transitive dependencies,
* pre-computes their topological order, then calls
* {@link WasmModuleSpec.factory} for each module and stores all modules for
* future reference.
*
* @remarks
* Note: The pre-instantiated modules will only be fully initialized later
* via {@link WasmBridge.instantiate} or {@link WasmBridge.init}.
*
* @param specs
*/
protected _buildModuleGraph(specs: WasmModuleSpec<T>[]): void;
/**
* Instantiates WASM module from given `src` (and optional provided extra

@@ -67,0 +81,0 @@ * imports), then automatically calls {@link WasmBridge.init} with the

@@ -73,10 +73,3 @@ var __defProp = Object.defineProperty;

};
this.modules = modules.reduce((acc, x) => {
assert(
acc[x.id] === void 0 && x.id !== this.id,
`duplicate API module ID: ${x.id}`
);
acc[x.id] = x;
return acc;
}, {});
this._buildModuleGraph(modules);
}

@@ -100,3 +93,41 @@ id = "wasmapi";

modules;
order;
/**
* Takes array of root module specs, extracts all transitive dependencies,
* pre-computes their topological order, then calls
* {@link WasmModuleSpec.factory} for each module and stores all modules for
* future reference.
*
* @remarks
* Note: The pre-instantiated modules will only be fully initialized later
* via {@link WasmBridge.instantiate} or {@link WasmBridge.init}.
*
* @param specs
*/
_buildModuleGraph(specs) {
const unique = /* @__PURE__ */ new Set();
const queue = [...specs];
while (queue.length) {
const mod = queue.shift();
unique.add(mod);
if (!mod.deps) continue;
for (let d of mod.deps) {
if (!unique.has(d)) queue.push(d);
}
}
const graph = [...unique].reduce((acc, mod) => {
assert(
(acc[mod.id] === void 0 || acc[mod.id] === mod) && mod.id !== this.id,
`duplicate API module ID: ${mod.id}`
);
acc[mod.id] = mod;
return acc;
}, {});
this.order = topoSort(graph, (mod) => mod.deps?.map((x) => x.id));
this.modules = this.order.reduce((acc, id) => {
acc[id] = graph[id].factory(this);
return acc;
}, {});
}
/**
* Instantiates WASM module from given `src` (and optional provided extra

@@ -135,7 +166,3 @@ * imports), then automatically calls {@link WasmBridge.init} with the

this.ensureMemory(false);
for (let id of topoSort(
this.modules,
(module) => module.dependencies
)) {
assert(!!this.modules[id], `missing API module: ${id}`);
for (let id of this.order) {
this.logger.debug(`initializing API module: ${id}`);

@@ -142,0 +169,0 @@ const status = await this.modules[id].init(this);

# Change Log
- **Last updated**: 2024-08-10T15:03:07Z
- **Last updated**: 2024-08-18T14:11:34Z
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)

@@ -12,2 +12,28 @@

# [2.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@2.0.0) (2024-08-18)
#### 🛑 Breaking changes
- update WASM module dependency handling ([31f0358](https://github.com/thi-ng/umbrella/commit/31f0358))
- BREAKING CHANGE: update WASM module dependency handling
- add `WasmModuleSpec` interface to declare WASM modules with their deps
- update `WasmBridge` ctor to accept array of module specs
- add buildModuleGraph() to process dependencies & init modules from specs
- add/update docstrings
- update tests
#### 🚀 Features
- add ObjectIndex.addUnique() ([e79275d](https://github.com/thi-ng/umbrella/commit/e79275d))
- add MemoryView types ([b621adc](https://github.com/thi-ng/umbrella/commit/b621adc))
- add internal memory view accessors ([6830337](https://github.com/thi-ng/umbrella/commit/6830337))
- these accessors are shared by various types generated via [@thi.ng/wasm-api-bindgen](https://github.com/thi-ng/umbrella/tree/main/packages/wasm-api-bindgen)
and help to drastically cut down filesize of generated code
- update pkg exports
- update ObjectIndex ctor, make opts fully optional ([564e0f3](https://github.com/thi-ng/umbrella/commit/564e0f3))
#### ♻️ Refactoring
- update internal array mem accessors ([b442d92](https://github.com/thi-ng/umbrella/commit/b442d92))
## [1.6.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@1.6.0) (2024-06-21)

@@ -14,0 +40,0 @@

export * from "./api.js";
export * from "./bridge.js";
export * from "./memory.js";
export * from "./object-index.js";

@@ -4,0 +5,0 @@ export * from "./pointer.js";

export * from "./api.js";
export * from "./bridge.js";
export * from "./memory.js";
export * from "./object-index.js";
export * from "./pointer.js";
export * from "./string.js";

@@ -12,3 +12,3 @@ import type { Maybe, Predicate, Range1_32 } from "@thi.ng/api";

*/
logger?: ILogger;
logger: ILogger;
/**

@@ -19,4 +19,10 @@ * Number of bits for IDs, [1..32] range.

*/
bits?: Range1_32;
bits: Range1_32;
}
/**
* Object cache with numeric ID handle management.
*
* @remarks
* [Further reference](https://docs.thi.ng/umbrella/wasm-api/#md:object-indices--handles)
*/
export declare class ObjectIndex<T> {

@@ -27,3 +33,3 @@ readonly name: string;

protected items: T[];
constructor(opts: ObjectIndexOpts);
constructor(opts?: Partial<ObjectIndexOpts>);
keys(): Generator<number, void, unknown>;

@@ -39,2 +45,15 @@ values(): Generator<T, void, unknown>;

/**
* Similar to {@link ObjectIndex.add}, but first checks if `item` has
* already been indexed and if so returns the ID of already indexed item
* without adding `item` to the index again. Uses `equiv` for checking item
* equality (by default: `===`).
*
* @remarks
* Currently an O(n) implementation.
*
* @param item
* @param equiv
*/
addUnique(item: T, equiv?: Predicate<T>): number;
/**
* Returns true if the given `id` is valid/active.

@@ -41,0 +60,0 @@ *

import { assert } from "@thi.ng/errors/assert";
import { IDGen } from "@thi.ng/idgen";
let __nextID = 0;
class ObjectIndex {

@@ -9,5 +10,5 @@ name;

constructor(opts) {
this.name = opts.name;
this.logger = opts.logger;
this.idgen = new IDGen(opts.bits || 32, 0);
this.name = opts?.name ?? `idx-${__nextID++}`;
this.logger = opts?.logger;
this.idgen = new IDGen(opts?.bits ?? 32, 0);
}

@@ -35,2 +36,18 @@ keys() {

/**
* Similar to {@link ObjectIndex.add}, but first checks if `item` has
* already been indexed and if so returns the ID of already indexed item
* without adding `item` to the index again. Uses `equiv` for checking item
* equality (by default: `===`).
*
* @remarks
* Currently an O(n) implementation.
*
* @param item
* @param equiv
*/
addUnique(item, equiv = (x) => x === item) {
const id = this.find(equiv, false);
return id === void 0 ? this.add(item) : id;
}
/**
* Returns true if the given `id` is valid/active.

@@ -37,0 +54,0 @@ *

{
"name": "@thi.ng/wasm-api",
"version": "1.6.7",
"version": "2.0.0",
"description": "Generic, modular, extensible API bridge and infrastructure for hybrid JS & WebAssembly projects",

@@ -40,9 +40,9 @@ "type": "module",

"dependencies": {
"@thi.ng/api": "^8.11.8",
"@thi.ng/arrays": "^2.9.14",
"@thi.ng/checks": "^3.6.10",
"@thi.ng/errors": "^2.5.14",
"@thi.ng/hex": "^2.3.52",
"@thi.ng/idgen": "^2.2.48",
"@thi.ng/logger": "^3.0.18"
"@thi.ng/api": "^8.11.9",
"@thi.ng/arrays": "^2.10.0",
"@thi.ng/checks": "^3.6.11",
"@thi.ng/errors": "^2.5.15",
"@thi.ng/hex": "^2.3.53",
"@thi.ng/idgen": "^2.2.49",
"@thi.ng/logger": "^3.0.19"
},

@@ -106,2 +106,5 @@ "devDependencies": {

},
"./memory": {
"default": "./memory.js"
},
"./object-index": {

@@ -121,3 +124,3 @@ "default": "./object-index.js"

},
"gitHead": "ec78f98d015e4d214a0b840e72e497407807daf3\n"
"gitHead": "f6e26ea1142525171de5d36b9c3119f2782bb437\n"
}

@@ -10,3 +10,3 @@ <!-- This file is generated - DO NOT EDIT! -->

> [!NOTE]
> This is one of 198 standalone projects, maintained as part
> This is one of 199 standalone projects, maintained as part
> of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo

@@ -19,2 +19,3 @@ > and anti-framework.

- [About](#about)
- [Polyglot bindings generator](#polyglot-bindings-generator)
- [Custom API modules](#custom-api-modules)

@@ -51,9 +52,12 @@ - [String handling](#string-handling)

This package provides the following:
This package form the core of a larger WASM toolkit which includes a polyglot
bindings generator and a growing number of API modules to interop with different
browser APIs. This core package provides the following:
1. A small
[`WasmBridge`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html)
class as generic interop basis and much reduced boilerplate for hybrid JS/WebAssembly
applications.
2. A minimal core API for debug output, string/pointer/typedarray accessors for
class as generic interop basis and much reduced boilerplate for hybrid
JS/WebAssembly applications.
2. Child WASM API modules with dependency graph resolution & initialization
3. A minimal core API for debug output, string/pointer/typedarray accessors for
8/16/32/64 bit (u)ints and 32/64 bit floats. Additionally, a number of support

@@ -65,5 +69,4 @@ modules for [DOM

WebGL, WebGPU, WebAudio etc. is being actively worked on.
3. Different types of memory-mapped (UTF-8) string abstractions (slice or pointer based)
4. Different types of memory-mapped (UTF-8) string abstractions (slice or pointer based)
5. Shared (opt-in) memory allocation mechanism, also accessible from JS/TS side
4. Simple registration & dependency-order initialization for child WASM API modules
6. Include files for

@@ -78,12 +81,14 @@ [Zig](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api/zig),

TS/Zig packages with the built-in build system
8. Extensible shared [datatype code generator
infrastructure](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-bindgen/)
for (currently) Zig & TypeScript and C11. For TS fully type checked and
memory-mapped (zero-copy) accessors of WASM-side data are generated. In
principle, all languages with a WASM target are supported, however currently
only bindings for these mentioned langs are included.
9. [CLI
frontend/utility](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api-bindgen/README.md#cli-generator)
for the code generator(s)
## Polyglot bindings generator
The toolkit includes an extensible [code
generator](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-bindgen/)
for shared datatypes and (currently) supports Zig & TypeScript and C11. For TS
fully type checked and memory-mapped (mostly zero-copy) accessors of WASM-side
data are generated. In principle, all languages with a WASM target are
supported, however currently only bindings for the languages mentioned are
included. The codegen also includes a [CLI
frontend/utility](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api-bindgen/README.md#cli-generator).
## Custom API modules

@@ -93,28 +98,67 @@

[`WasmBridge`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html)
can be extented via custom defined API modules. Such API extensions will consist
can be extented via custom defined API modules. Such child modules will consist
of a collection of JS/TS functions & variables, their related counterparts
(import definitions) for the WASM target and (optionally) some shared data types
([bindings for which _can_ be generated by this package
too](#data-bindings--code-generators)).
([bindings for which can be generated via
thi.ng/wasm-api-bindgen](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api-bindgen)).
On the JS side, custom API modules can be easily integrated via the [`IWasmAPI`
interface](https://docs.thi.ng/umbrella/wasm-api/interfaces/IWasmAPI.html). The
following example provides a brief overview:
On the JS side, custom API extensions can be easily integrated and exposed via
the [`IWasmAPI`
interface](https://docs.thi.ng/umbrella/wasm-api/interfaces/IWasmAPI.html) and
their module descriptors via
[`WasmModuleSpec`](https://docs.thi.ng/umbrella/wasm-api/interfaces/WasmModuleSpec.html).
The following example provides a brief overview:
```ts
import { IWasmAPI, WasmBridge } from "@thi.ng/wasm-api";
import { WasmBridge, type IWasmAPI, type WasmModuleSpec } from "@thi.ng/wasm-api";
import { WasmDomModule, type WasmDom, type WasmDomExports } from "@thi.ng/wasm-api-dom";
export class CustomAPI implements IWasmAPI {
// Unique API module identifier to group WASM imports,
// must match ID used by native code (see further below).
readonly id = "custom";
// optionally list IDs of other API modules this module depends on
// these are used to infer the correct initialization order
readonly dependencies = [];
export const CustomModule: WasmModuleSpec = {
// Unique API module identifier to group WASM imports, must match ID used
// by the native code (see further below).
id: "custom",
// Optional array of API modules this module depends on. This is used to
// infer the full dependency graph and correct initialization order.
// Note: Each of the wasm-api support packages includes a module spec...
depes: [WasmDomModule],
// Factory function to pre-instantiate the API module. Full initialization
// only happens at a later point via WasmBridge.instantiate() or
// WasmBridge.init() and each module's own init() method...
factory: () => new CustomAPI(),
};
// Optional declarations for JS-side functions which can be used from the WASM side.
// Using an interface for this is useful to establish a contract.
export interface CustomImports extends WebAssembly.ModuleImports {
/**
* Writes `num` random float32 numbers from given address
*/
fillRandom(addr: number, num: number): void;
}
// Optional (but likely used) interface declarations of functions exposed
// by the WASM binary and callable from the JS side (i.e. the opposite of the above)
//
// Note: If your module has dependencies on other modules you should declare
// your exports as extension of these other module's exports for better
// downstream developer experience
export interface CustomWasmExports extends WasmDomExports {
// ...
}
// Actual custom API extension implentation
export class CustomAPI implements IWasmAPI<CustomWasmExports> {
parent!: WasmBridge;
dom!: WasmDom;
/**
* Actual module initialization, called from the main WasmBridge which
* initializes all modules in dependency order...
*/
async init(parent: WasmBridge) {
this.parent = parent;
this.parent.logger.debug("initializing custom API");
// Store a direct reference to the DOM module for future ref
// (just shown as example, we're not actually using this module here...)
this.dom = <WasmDom>this.parent.modules[WasmDomModule.id];

@@ -127,11 +171,11 @@ // any other tasks you might need to do...

/**
* Returns object of functions to import as externals into the
* WASM module during instantiation. These imports are merged
* into a larger imports object alongside the bridge's core API...
* Returns object of functions/constants to import as externals into the
* WASM module during instantiation. These imports are merged into a
* larger imports object alongside the bridge's core API...
*
* Each module's imports will be grouped by its declared module ID, which
* also needs to be used to declare extern functions on the WASM side.
*/
getImports(): WebAssembly.Imports {
getImports(): CustomImports {
return {
/**
* Writes `num` random float32 numbers from given address
*/
fillRandom: (addr: number, num: number) => {

@@ -146,9 +190,8 @@ addr >>>= 2;

// now we can supply this custom API when creating the main WASM bridge:
export const bridge = new WasmBridge([new CustomAPI()]);
export const bridge = new WasmBridge([CustomModule]);
```
In Zig (or any other language of your choice) we can then utilize this custom
API like so (Please also see [example
projects](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-canvas/)
& other example snippets in this readme):
API like so (Please also see [example projects](#usage-examples) & other code
snippets in this readme):

@@ -166,2 +209,7 @@ Bindings file / lib:

extern "custom" fn fillRandom(addr: [*]f32, num: usize) void;
/// Syntax sugar for `fillRandom()`
pub fn fillRandomSlice(slice: []f32) void {
fillRandom(slice.ptr, slice.len);
}
```

@@ -173,3 +221,3 @@

// Import JS core API
const js = @import("wasm-api");
const wasm = @import("wasm-api");
const custom = @import("custom.zig");

@@ -181,9 +229,9 @@

// print original
js.printF32Array(foo[0..]);
wasm.printF32Array(foo[0..]);
// populate foo with random numbers
custom.fillRandom(&foo, foo.len);
custom.fillRandomSlice(foo[0..]);
// print result
js.printF32Array(foo[0..]);
wasm.printF32Array(foo[0..]);
}

@@ -271,22 +319,25 @@ ```

allocator is defined and/or a custom allocator should be used, then these API
modules will be have to be initialized manually.
modules will have to be initialized manually.
## Object indices & handles
Since only numeric values can be exchanged between the WASM module and the JS
host, any JS native objects the WASM side might want to be working with must be
managed manually in JS. For this purpose the [`ObjectIndex`
Since only numeric values can be directly passed between the WASM module and the
JS host, any JS native objects the WASM side might want to be working with must
be managed manually in JS. For this purpose the [`ObjectIndex`
class](https://docs.thi.ng/umbrella/wasm-api/classes/ObjectIndex.html) can be
used by API modules to handle ID generation (incl. recycling, using
[@thi.ng/idgen](https://github.com/thi-ng/umbrella/tree/develop/packages/idgen))
and the indexing of different types of JS objects/values. Only the numeric IDs
(handles) will then need to be exchanged with the WASM module...
used by API modules to handle the indexing of different types of JS
objects/values and their ID generation (incl. recycling of IDs, using
[@thi.ng/idgen](https://github.com/thi-ng/umbrella/tree/develop/packages/idgen)).
Using this approach, only the numeric IDs (handles) will then need to be
exchanged with the WASM module...
```ts
import { ObjectIndex } from "@thi.ng/wasm-api";
import { ConsoleLogger } from "@thi.ng/logger";
// create index (see API docs for options)
const canvases = new ObjectIndex<HTMLCanvasElement>({ name: "canvas" });
// index item and assign new ID
canvases.add(document.createElement("canvas"));
canvases.add(<HTMLCanvasElement>document.createElement("canvas"));
// 0

@@ -298,2 +349,8 @@

// by default, items to be indexed are not checked for equality
// but we can use the below to ensure double indexing is avoided...
// (supports custom predicates to check for equality)
canvases.addUnique(canvases.get(0));
// 0 (returned already known ID)
// work w/ retrieved item

@@ -342,4 +399,4 @@ canvases.get(0).id = "foo";

[v0.13.0](https://ziglang.org/download/0.13.0/release-notes.html), older Zig
versions are not actively supported (however, [build files for older versions
are still
versions are not actively supported anymore (however, [build files for older
versions are still
included](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig))

@@ -376,4 +433,3 @@

All bundled example projects (see [list below](#usage-examples)) are being built
via this script. **Please find more details/options in the commented source
code:**
via this script. **More details/options in the commented source code:**

@@ -400,2 +456,3 @@ - [`/zig/build.zig`](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/build.zig)

- [@thi.ng/wasm-api-schedule](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-schedule) - Delayed & scheduled function execution (via setTimeout() etc.) for hybrid WASM apps
- [@thi.ng/wasm-api-webgl](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-webgl) - WebGL bridge API for hybrid TypeScript & WASM (Zig) applications

@@ -422,3 +479,3 @@ ## Installation

Package sizes (brotli'd, pre-treeshake): ESM: 2.69 KB
Package sizes (brotli'd, pre-treeshake): ESM: 3.00 KB

@@ -439,3 +496,3 @@ ## Dependencies

Four projects in this repo's
Five projects in this repo's
[/examples](https://github.com/thi-ng/umbrella/tree/develop/examples)

@@ -450,2 +507,3 @@ directory are using this package:

| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-todo-list.png" width="240"/> | Zig-based To-Do list, DOM creation, local storage task persistence | [Demo](https://demo.thi.ng/umbrella/zig-todo-list/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-todo-list) |
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-webgl.avif" width="240"/> | Basic Zig/WebAssembly WebGL demo | [Demo](https://demo.thi.ng/umbrella/zig-webgl/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-webgl) |

@@ -452,0 +510,0 @@ ## API

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc