Socket
Socket
Sign inDemoInstall

@bytecodealliance/jco

Package Overview
Dependencies
Maintainers
3
Versions
57
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bytecodealliance/jco - npm Package Compare versions

Comparing version 0.13.3 to 0.14.0

obj/interfaces/wasi-io-error.d.ts

7

obj/interfaces/wasi-filesystem-types.d.ts

@@ -137,7 +137,2 @@ export namespace WasiFilesystemTypes {

}
export interface Modes {
readable?: boolean,
writable?: boolean,
executable?: boolean,
}
export interface MetadataHashValue {

@@ -159,4 +154,4 @@ lower: bigint,

stat(): DescriptorStat;
openAt(pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: DescriptorFlags, modes: Modes): Descriptor;
openAt(pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: DescriptorFlags): Descriptor;
metadataHash(): MetadataHashValue;
}
export namespace WasiIoStreams {
export { InputStream };
export { OutputStream };
export { Error };
}
import type { Error } from '../interfaces/wasi-io-error.js';
export { Error };
export type StreamError = StreamErrorLastOperationFailed | StreamErrorClosed;

@@ -15,2 +16,7 @@ export interface StreamErrorLastOperationFailed {

export class InputStream {
read(len: bigint): Uint8Array;
blockingRead(len: bigint): Uint8Array;
}
export class OutputStream {

@@ -22,9 +28,1 @@ checkWrite(): bigint;

}
export class InputStream {
read(len: bigint): Uint8Array;
blockingRead(len: bigint): Uint8Array;
}
export class Error {
}
export type Files = [string, Uint8Array][];
export type Maps = [string, string][];
export type InstantiationMode = InstantiationModeAsync | InstantiationModeSync;
export interface InstantiationModeAsync {
tag: 'async',
}
export interface InstantiationModeSync {
tag: 'sync',
}
export interface GenerateOptions {
name: string,
noTypescript?: boolean,
instantiation?: boolean,
instantiation?: InstantiationMode,
map?: Maps,

@@ -14,2 +21,3 @@ compat?: boolean,

tracing?: boolean,
noNamespacedExports?: boolean,
}

@@ -33,3 +41,3 @@ export type Wit = WitSource | WitBinary | WitPath;

tlaCompat?: boolean,
instantiation?: boolean,
instantiation?: InstantiationMode,
map?: Maps,

@@ -63,2 +71,3 @@ }

import { WasiFilesystemTypes } from './interfaces/wasi-filesystem-types.js';
import { WasiIoError } from './interfaces/wasi-io-error.js';
import { WasiIoStreams } from './interfaces/wasi-io-streams.js';

@@ -65,0 +74,0 @@ import { WasiRandomRandom } from './interfaces/wasi-random-random.js';

@@ -14,2 +14,3 @@ import { WasiCliEnvironment } from './interfaces/wasi-cli-environment.js';

import { WasiFilesystemTypes } from './interfaces/wasi-filesystem-types.js';
import { WasiIoError } from './interfaces/wasi-io-error.js';
import { WasiIoStreams } from './interfaces/wasi-io-streams.js';

@@ -16,0 +17,0 @@ import { WasiRandomRandom } from './interfaces/wasi-random-random.js';

{
"name": "@bytecodealliance/jco",
"version": "0.13.3",
"version": "0.14.0",
"description": "JavaScript tooling for working with WebAssembly Components",

@@ -21,3 +21,3 @@ "author": "Guy Bedford",

"dependencies": {
"@bytecodealliance/preview2-shim": "0.0.21",
"@bytecodealliance/preview2-shim": "0.14.0",
"binaryen": "^111.0.0",

@@ -24,0 +24,0 @@ "chalk-template": "^0.4.0",

@@ -5,3 +5,3 @@ <div align="center">

<p>
<strong>JavaScript component toolchain for working with <a href="https://github.com/WebAssembly/component-model">WebAssembly Components</a></strong>
<strong>JavaScript toolchain for working with <a href="https://github.com/WebAssembly/component-model">WebAssembly Components</a></strong>
</p>

@@ -18,3 +18,3 @@

`jco` is a fully native JS tool for working with the emerging [WebAssembly Components](https://github.com/WebAssembly/component-model) specification in JavaScript.
`jco` is a fully native JS tool for working with [WebAssembly Components](https://github.com/WebAssembly/component-model) in JavaScript.

@@ -24,9 +24,9 @@ Features include:

* "Transpiling" Wasm Component binaries into ES modules that can run in any JS environment.
* Optimization helpers for Components via Binaryen.
* Component builds of [Wasm Tools](https://github.com/bytecodealliance/wasm-tools) helpers, available for use as a library or CLI commands for use in native JS environments.
* "Componentize" for WebAssembly Components from JavaScript sources and a WIT world
* WASI Preview2 support in Node.js ([undergoing stabilization](https://github.com/bytecodealliance/jco/milestone/1)) & browsers (experimental).
* Component builds of [Wasm Tools](https://github.com/bytecodealliance/wasm-tools) helpers, available for use as a library or CLI commands for use in native JS environments, as well as optimization helper for Components via Binaryen.
* "Componentize" command to easily create components written in JavaScript (wrapper of [ComponentizeJS](https://github.com/bytecodealliance/ComponentizeJS)).
For creating components in other languages, see the [Cargo Component](https://github.com/bytecodealliance/cargo-Component) project for Rust and [Wit Bindgen](https://github.com/bytecodealliance/wit-bindgen) for various guest bindgen helpers.
> **Note**: This is an experimental project, no guarantees are provided for stability or support and breaking changes may be made in future.
> **Note**: This is an experimental project, no guarantees are provided for stability, security or support and breaking changes may be made without notice.

@@ -39,3 +39,3 @@ ## Installation

jco can be used as either a library or as a CLI via the `jco` CLI command.
jco can be used as either a library or a CLI via the `jco` CLI command.

@@ -108,4 +108,6 @@ ## Example

* `--no-nodejs-compat`: Disables Node.js compat in the output to load core Wasm with FS methods.
* `--instantiation`: Instead of a direct ES module, export an `instantiate` function which can take the imports as an argument instead of implicit imports.
* `--instantiation [mode]`: Instead of a direct ES module, export an `instantiate` function which can take the imports as an argument instead of implicit imports. The `instantiate` function can be async (with `--instantiation` or `--instantiation async`), or sync (with `--instantiation sync`).
* `--valid-lifting-optimization`: Internal validations are removed assuming that core Wasm binaries are valid components, providing a minor output size saving.
* `--tracing`: Emit tracing calls for all function entry and exits.
* `--no-namespaced-exports`: Removes exports of the type `test as "test:flavorful/test"` which are not compatible with typescript

@@ -116,2 +118,14 @@ #### Bindgen Crate

### Run
For Wasm components that implement the WASI Command world, a `jco run` utility is provided to run these applications in Node.js:
```
jco run cowasy.component.wasm hello
```
Using the preview2-shim WASI implementation, full access to the underlying system primitives is provided, including filesystem and environment variable permissions.
> [preview2-shim](packages/preview2-shim) is currently being stabilized in Node.js, tracking in https://github.com/bytecodealliance/jco/milestone/1.
### Componentize

@@ -131,16 +145,4 @@

> Additional engines may be supported in future via an `--engine` field or otherwise.
> Additional engines might be supported in future via an `--engine` field or otherwise.
### Run
For Wasm components that implement the WASI Command world, a `jco run` utility is provided to run these applications in Node.js:
```
jco run cowasy.component.wasm hello
```
Using the preview2-shim WASI implementation, full access to the underlying system primitives is provided, including filesystem and environment variable permissions.
> Since [preview2-shim](packages/preview2-shim) is an experimental work-in-progress implementation, there will likely be bugs.
## API

@@ -147,0 +149,0 @@

import { readFile, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import c from 'chalk-template';
import { isWindows } from '../common.js';

@@ -5,0 +6,0 @@ export async function componentize (jsSource, opts) {

@@ -7,11 +7,16 @@ import { getTmpDir } from '../common.js';

import process from 'node:process';
import { fileURLToPath } from 'node:url';
import { fileURLToPath, pathToFileURL } from 'node:url';
import c from 'chalk-template';
export async function run (componentPath, args) {
export async function run (componentPath, args, opts) {
// Ensure that `args` is an array
args = [...args];
const jcoImport = opts.jcoImport ? resolve(opts.jcoImport) : null;
const name = basename(componentPath.slice(0, -extname(componentPath).length || Infinity));
const outDir = await getTmpDir();
const outDir = opts.jcoDir || await getTmpDir();
if (opts.jcoDir) {
await mkdir(outDir, { recursive: true });
}
try {

@@ -24,3 +29,4 @@ try {

wasiShim: true,
outDir
outDir,
tracing: opts.jcoTrace
});

@@ -51,6 +57,13 @@ }

await mkdir(modulesDir, { recursive: true });
await symlink(preview2ShimPath, resolve(modulesDir, 'preview2-shim'), 'dir');
try {
await symlink(preview2ShimPath, resolve(modulesDir, 'preview2-shim'), 'dir');
} catch (e) {
if (e.code !== 'EEXIST')
throw e;
}
const runPath = resolve(outDir, '_run.js');
await writeFile(runPath, `
${jcoImport ? `import ${JSON.stringify(pathToFileURL(jcoImport))}` : ''}
function logInvalidCommand () {

@@ -68,2 +81,4 @@ console.error('Not a valid command component to execute, make sure it was built to a command adapter and with the same version.');

mod.run.run();
// TODO: figure out stdout flush!
setTimeout(() => {});
}

@@ -90,5 +105,6 @@ catch (e) {

try {
await rm(outDir, { recursive: true });
if (!opts.jcoDir)
await rm(outDir, { recursive: true });
} catch {}
}
}

@@ -49,3 +49,3 @@ import { $init, generate } from '../../obj/js-component-bindgen-component.js';

/**
* @param {Uint8Array} source
* @param {Uint8Array} source
* @returns {Promise<Uint8Array>}

@@ -64,11 +64,11 @@ */

/**
*
* @param {Uint8Array} component
*
* @param {Uint8Array} component
* @param {{
* name: string,
* instantiation?: bool,
* instantiation?: 'async' | 'sync',
* map?: Record<string, string>,
* validLiftingOptimization?: bool,
* tracing?: bool,
* noNodejsCompat?: bool,
* nodejsCompat?: bool,
* tlaCompat?: bool,

@@ -79,4 +79,5 @@ * base64Cutoff?: bool,

* optimize?: bool,
* namespacedExports?: bool,
* optArgs?: string[],
* }} opts
* }} opts
* @returns {Promise<{ files: { [filename: string]: Uint8Array }, imports: string[], exports: [string, 'function' | 'instance'][] }>}

@@ -109,6 +110,17 @@ */

let instantiation = null;
// Let's define `instantiation` from `--instantiation` if it's present.
if (opts.instantiation) {
instantiation = { tag: opts.instantiation };
}
// Otherwise, if `--js` is present, an `instantiate` function is required.
else if (opts.js) {
instantiation = { tag: 'async' };
}
let { files, imports, exports } = generate(component, {
name: opts.name ?? 'component',
map: Object.entries(opts.map ?? {}),
instantiation: opts.instantiation || opts.js,
instantiation,
validLiftingOptimization: opts.validLiftingOptimization ?? false,

@@ -119,3 +131,4 @@ tracing: opts.tracing ?? false,

tlaCompat: opts.tlaCompat ?? false,
base64Cutoff: opts.js ? 0 : opts.base64Cutoff ?? 5000
base64Cutoff: opts.js ? 0 : opts.base64Cutoff ?? 5000,
noNamespacedExports: opts.namespacedExports === false,
});

@@ -130,12 +143,63 @@

// Generate code for the `--js` option.
//
// `--js` can be called with or without `--instantiation`. The generated code
// isn't exactly the same!
//
// `--js` needs an `instantiate` function to work, so it might look like
// `--instantiation` is always implied, but actually no. It is correct
// that when `--js` is present, an `instantiate` function _is_ generated,
// but it doesn't mean that we expect the function to be used, it's simply
// not exported, plus `instantiate` is automatically called (if `--tla-compat`
// is `false`). When `--instantiation` is missing, functions are exported
// with the `export` directive, and imports are imported with the `import`
// directive. When `--instantiation` is present, there is no `export` and no
// `import`: only a single exported `instantiate` function.
//
// Basically, we get this:
//
// * `--js` only:
// * `instantiate` is renamed to `_instantiate`,
// * A new `instantiate` function is created, that calls `_instantiate` with
// the correct imports (which are ASM.js code) and returns the exports,
// * A new `$init` function is created, that calls `instantiate` and maps
// the returned exports to their respective trampolines,
// * Trampolines are exported,
// * `$init` is called automatically.
//
// * `--js` with `--tla-compat`:
// * Same as with `--js` only, except that `$init` is exported instead of
// being called immediately.
//
// * `--js` with `--instantiation[=async]`:
// * `instantiate` is renamed to `_instantiate`,
// * A new `instantiate` function is created, that calls `_instantiate` with
// the correct imports (which are ASM.js code) and returns the exports,
// * `instantiate` is exported.
//
// * `--js` with `--instantiation=sync`:
// * Same as `--js` with `--instantiation[=async]`, except that
// `_instantiate` and `instantiate` are non-async.
//
// Be careful with the variables: `opts.instantiation` reflects the presence
// or the absence of the `--instantiation` flag, whilst `instantiation`
// reflects how the `instantiate` function must be generated. We also use
// `instantiation` to know whether the generated code must be async or
// non-async.
if (opts.js) {
const withInstantiation = opts.instantiation !== undefined;
const async_ = instantiation.tag == 'async' ? 'async ' : '';
const await_ = instantiation.tag == 'async' ? 'await ' : '';
// Format the previously generated code.
const source = Buffer.from(jsFile[1]).toString('utf8')
// update imports manging to match emscripten asm
.replace(/exports(\d+)\['([^']+)']/g, (_, i, s) => `exports${i}['${asmMangle(s)}']`);
.replace(/exports(\d+)\['([^']+)']/g, (_, i, s) => `exports${i}['${asmMangle(s)}']`)
.replace(/export (async )?function instantiate/, '$1function _instantiate') ;
// Collect all Wasm files.
const wasmFiles = files.filter(([name]) => name.endsWith('.wasm'));
files = files.filter(([name]) => !name.endsWith('.wasm'));
// TODO: imports as specifier list
// Configure the spinner.
let completed = 0;

@@ -151,2 +215,3 @@ const spinnerText = () => c`{cyan ${completed} / ${wasmFiles.length}} Running Binaryen wasm2js on Wasm core modules (this takes a while)...\n`;

// Compile all Wasm modules into ASM.js codes.
try {

@@ -162,40 +227,136 @@ const asmFiles = await Promise.all(wasmFiles.map(async ([, source]) => {

const asms = asmFiles.map((asm, i) =>`function asm${i}(imports) {
const asms = asmFiles.map((asm, nth) => `function asm${nth}(imports) {
${
// strip and replace the asm instantiation wrapper
asm
.replace(/import \* as [^ ]+ from '[^']*';/g, '')
.replace('function asmFunc(imports) {', '')
.replace(/export var ([^ ]+) = ([^. ]+)\.([^ ]+);/g, '')
.replace(/var retasmFunc = [\s\S]*$/, '')
.replace(/var memasmFunc = new ArrayBuffer\(0\);/g, '')
.replace('memory.grow = __wasm_memory_grow;', '')
.trim()
}`).join(',\n');
// strip and replace the asm instantiation wrapper
asm
.replace(/import \* as [^ ]+ from '[^']*';/g, '')
.replace('function asmFunc(imports) {', '')
.replace(/export var ([^ ]+) = ([^. ]+)\.([^ ]+);/g, '')
.replace(/var retasmFunc = [\s\S]*$/, '')
.replace(/var memasmFunc = new ArrayBuffer\(0\);/g, '')
.replace('memory.grow = __wasm_memory_grow;', '')
.trim()
}`)
.join(',\n');
const outSource = `${
imports.map((impt, i) => `import * as import${i} from '${impt}';`).join('\n')}
${source.replace('export async function instantiate', 'async function instantiate')}
// The `instantiate` function.
const instantiateFunction =
`${
withInstantiation ? 'export ' : ''
}${async_}function instantiate(imports) {
const wasm_file_to_asm_index = {
${wasmFiles.map(([path], nth) => `'${basename(path)}': ${nth}`).join(',\n ')}
};
let ${exports.filter(([, ty]) => ty === 'function').map(([name]) => '_' + name).join(', ')};
return ${await_}_instantiate(
module_name => wasm_file_to_asm_index[module_name],
imports,
(module_index, imports) => ({ exports: asmInit[module_index](imports) })
);
}`;
${exports.map(([name, ty]) => ty === 'function' ? `\nfunction ${asmMangle(name)} () {
return _${name}.apply(this, arguments);
}` : `\nlet ${asmMangle(name)};`).join('\n')}
// If `--js` is used without `--instantiation`.
let importDirectives = '';
let exportDirectives = '';
let exportTrampolines = '';
let autoInstantiate = '';
if (!withInstantiation) {
importDirectives = imports
.map((import_file, nth) => `import * as import${nth} from '${import_file}';`)
.join('\n');
if (exports.length > 0 || opts.tlaCompat) {
exportDirectives =
`export {
${
// Exporting `$init` must come first to not break the transpiling tests.
(opts.tlaCompat) ? ' $init,\n' : ''
}${
exports
.map(([name]) => {
if (name === asmMangle(name)) {
return ` ${name},`;
} else {
return ` ${asmMangle(name)} as '${name}',`;
}
})
.join('\n')
}
}`
}
exportTrampolines =
`let ${
exports
.filter(([, ty]) => ty === 'function')
.map(([name]) => `_${asmMangle(name)}`)
.join(', ')
};
${
exports
.map(([name, ty]) => {
if (ty === 'function') {
return `\nfunction ${asmMangle(name)} () {
return _${asmMangle(name)}.apply(this, arguments);
}`;
} else {
return `\nlet ${asmMangle(name)};`;
}
})
.join('\n')
}`;
autoInstantiate =
`${async_}function $init() {
( {
${
exports
.map( ([name, ty]) => {
if (ty === 'function') {
return ` '${ name }': _${ asmMangle(name) },`;
} else if (asmMangle(name) === name) {
return ` ${ name },`;
} else {
return ` '${ name }': ${ asmMangle(name) },`;
}
})
.join('\n')
}
} = ${await_}instantiate(
{
${
imports
.map((import_file, nth) => ` '${import_file}': import${ nth },`)
.join('\n')
}
}
) )
}
${ opts.tlaCompat ? '' : `${await_}$init();`}`;
}
// Prepare the final generated code.
const outSource = `${importDirectives}
${source}
const asmInit = [${asms}];
${opts.tlaCompat ? 'export ' : ''}const $init = (async () => {
let idx = 0;
({ ${exports.map(([name, ty]) => asmMangle(name) === name ? name : `'${name}': ${asmMangle(name)}`).join(',\n')} } = await instantiate(n => idx++, {
${imports.map((impt, i) => ` '${impt}': import${i},`).join('\n')}
}, (i, imports) => ({ exports: asmInit[i](imports) })));
})();${exports.length > 0 ? `\nexport { ${exports.map(([name]) => name === asmMangle(name) ? `${name}` : `${asmMangle(name)} as "${name}"`).join(', ')} }` : ''}
${opts.tlaCompat ? '' : '\nawait $init;\n'}`;
${exportTrampolines}
${instantiateFunction}
${exportDirectives}
${autoInstantiate}`;
// Save the final generated code.
jsFile[1] = Buffer.from(outSource);
}
finally {
if (spinner)
if (spinner) {
spinner.stop();
}
}

@@ -226,6 +387,6 @@ }

return '$';
let mightBeKeyword = true;
let i = 1;
// Names must start with a character, $ or _

@@ -260,3 +421,3 @@ switch (name[0]) {

}
// Names must contain only characters, digits, $ or _

@@ -290,3 +451,3 @@ let len = name.length;

}
// Names must not collide with keywords

@@ -293,0 +454,0 @@ if (mightBeKeyword && len >= 2 && len <= 10) {

import { writeFile } from "node:fs/promises";
import { readFile } from '../common.js';
import { readFile, isWindows } from '../common.js';
import { $init, tools } from "../../obj/wasm-tools.js";

@@ -7,2 +7,3 @@ const { print: printFn, parse: parseFn, componentWit: componentWitFn, componentNew: componentNewFn, componentEmbed: componentEmbedFn, metadataAdd: metadataAddFn, metadataShow: metadataShowFn } = tools;

import c from 'chalk-template';
import { platform } from 'node:process';

@@ -72,3 +73,3 @@ export async function parse(file, opts) {

opts.binary = source;
opts.witPath = resolve(opts.wit);
opts.witPath = (isWindows ? '//?/' : '') + resolve(opts.wit);
const output = componentEmbedFn(opts);

@@ -75,0 +76,0 @@ await writeFile(opts.output, output);

@@ -7,3 +7,6 @@ import { normalize, resolve, sep } from 'node:path';

import c from 'chalk-template';
import { platform } from 'node:process';
export const isWindows = platform === 'win32';
let _showSpinner = false;

@@ -10,0 +13,0 @@ export function setShowSpinner (val) {

#!/usr/bin/env node
import { program } from 'commander';
import { program, Option } from 'commander';
import { opt } from './cmd/opt.js';

@@ -14,3 +14,3 @@ import { transpile } from './cmd/transpile.js';

.usage('<command> [options]')
.version('0.13.3');
.version('0.14.0');

@@ -49,11 +49,16 @@ function myParseInt(value) {

.option('--js', 'output JS instead of core WebAssembly')
.option('-I, --instantiation', 'output for custom module instantiation')
.addOption(new Option('-I, --instantiation [mode]', 'output for custom module instantiation').choices(['async', 'sync']).preset('async'))
.option('-q, --quiet', 'disable logging')
.option('--no-namespaced-exports', 'disable namespaced exports for typescript compatibility')
.option('--', 'for --optimize, custom wasm-opt arguments (defaults to best size optimization)')
.action(asyncAction(transpile));
program.command('run')
program.command('run')
.description('Run a WebAssembly Command component')
.usage('<command.wasm> <args...>')
.helpOption(false)
.argument('<command>', 'Wasm command binary to run')
.option('--jco-dir <dir>', 'Instead of using a temporary dir, set the output directory for the run command')
.option('--jco-trace', 'Enable call tracing')
.option('--jco-import <module>', 'Custom module to import before the run executes to support custom environment setup')
.argument('[args...]', 'Any CLI arguments to provide to the command')

@@ -60,0 +65,0 @@ .action(asyncAction(run));

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

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