diff --git a/.gitignore b/.gitignore
index d84736f4131..9ebcf941e71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -153,7 +153,7 @@ vowpalwabbit/parser/flatbuffer/generated/example_generated.h
test/flatbuffer/*.predict
test/flatbuffer/*.stderr
node_modules
-wasm/vw-wasm.js
+wasm/vw.js
wasm/package-lock.json
/.vs
diff --git a/wasm/.npmignore b/wasm/.npmignore
index 06c8b6cd329..0f6b9c51162 100644
--- a/wasm/.npmignore
+++ b/wasm/.npmignore
@@ -4,3 +4,5 @@ example.js
node_modules
*.txt
*.cc
+*.ts
+!*.d.ts
diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt
index 65f1841f145..02c1741b5c3 100644
--- a/wasm/CMakeLists.txt
+++ b/wasm/CMakeLists.txt
@@ -2,8 +2,8 @@ if(VW_INSTALL)
message(FATAL_ERROR "Install not supported for WASM build" )
endif()
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/out/")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/dist/")
-add_executable(vw-wasm wasm_wrapper.cc)
+add_executable(vw-wasm src/wasm_wrapper.cc)
set_target_properties(vw-wasm PROPERTIES LINK_FLAGS "-fexceptions -s WASM=1 -s SINGLE_FILE=1 -s NO_DYNAMIC_EXECUTION=1 --bind -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s EXPORTED_FUNCTIONS=\"['_malloc', '_free']\" -s MODULARIZE=1 -s EXPORT_NAME=\"vwWasmModule\"")
target_link_libraries(vw-wasm PUBLIC vw_explore vw_core "-fexceptions")
\ No newline at end of file
diff --git a/wasm/documentation.md b/wasm/documentation.md
index cb70061b589..6a6d22f83f1 100644
--- a/wasm/documentation.md
+++ b/wasm/documentation.md
@@ -67,7 +67,7 @@ Closes the logging stream. Logs a warning to the console if there is no logging
### vwExampleLogger.logLineSync(log_file, line)
-Takes a string and appends it to the log file. Line is logged in a synchronous manner.
+Takes a string and appends it to the log file. Line is logged in a synchronous manner.
Every call to this function will open a new file handle, append the line and close the file handle.
**Kind**: instance method of [VWExampleLogger
](#VWExampleLogger)
@@ -175,8 +175,8 @@ Can accept either or both string arguments and a model file.
### workspace.parse(line) ⇒
-Parse a line of text into a VW example.
-The example can then be used for prediction or learning.
+Parse a line of text into a VW example.
+The example can then be used for prediction or learning.
finishExample() must be called and then delete() on the example, when it is no longer needed.
**Kind**: instance method of [Workspace
](#Workspace)
@@ -252,7 +252,7 @@ Takes a file location and stores the VW model in binary format in the file.
### workspace.getModelAsArray() ⇒ Uint8Array
Gets the VW model in binary format as a Uint8Array that can be saved to a file.
-There is no need to delete or free the array returned by this function.
+There is no need to delete or free the array returned by this function.
If the same array is however used to re-load the model into VW, then the array needs to be stored in wasm memory (see loadModelFromArray)
**Kind**: instance method of [Workspace
](#Workspace)
@@ -281,7 +281,7 @@ The memory must be allocated via the WebAssembly module's _malloc function and s
| Param | Type | Description |
| --- | --- | --- |
-| model_array_ptr | \*
| the pre-loaded model's array pointer The memory must be allocated via the WebAssembly module's _malloc function and should later be freed via the _free function. |
+| model_array_ptr | number
| the pre-loaded model's array pointer The memory must be allocated via the WebAssembly module's _malloc function and should later be freed via the _free function. |
| model_array_len | number
| the pre-loaded model's array length |
@@ -300,7 +300,6 @@ A Wrapper around the Wowpal Wabbit C++ library for Contextual Bandit exploration
**Extends**: WorkspaceBase
* [CbWorkspace](#CbWorkspace) ⇐ WorkspaceBase
- * [new CbWorkspace([args_str], [model_file], [model_array])](#new_CbWorkspace_new)
* [.predict(example)](#CbWorkspace+predict) ⇒ array
* [.learn(example)](#CbWorkspace+learn)
* [.addLine(line)](#CbWorkspace+addLine)
@@ -317,62 +316,6 @@ A Wrapper around the Wowpal Wabbit C++ library for Contextual Bandit exploration
* [.loadModelFromArray(model_array_ptr, model_array_len)](#WorkspaceBase+loadModelFromArray)
* [.delete()](#WorkspaceBase+delete)
-
-
-### new CbWorkspace([args_str], [model_file], [model_array])
-Creates a new Vowpal Wabbit workspace for Contextual Bandit exploration algorithms.
-Can accept either or both string arguments and a model file.
-
-**Throws**:
-
-- Error
Throws an error if:
-- no argument is provided
-- both string arguments and a model file are provided, and the string arguments and arguments defined in the model clash
-- both string arguments and a model array are provided, and the string arguments and arguments defined in the model clash
-- both a model file and a model array are provided
-
-
-| Param | Type | Description |
-| --- | --- | --- |
-| [args_str] | string
| The arguments that are used to initialize Vowpal Wabbit (optional) |
-| [model_file] | string
| The path to the file where the model will be loaded from (optional) |
-| [model_array] | tuple
| The pre-loaded model's array pointer and length (optional). The memory must be allocated via the WebAssembly module's _malloc function and should later be freed via the _free function. |
-
-**Example**
-```js
-const vwPromise = require('./vw.js');
-// require returns a promise because we need to wait for the wasm module to be initialized
-
-vwPromise.then((vw) => {
- let model = new vw.CbWorkspace({ args_str: "--cb_explore_adf" });
- let vwLogger = new vw.VWExampleLogger();
-
- vwLogger.startLogStream("mylogfile.txt");
-
- let example = {
- text_context: `shared | s_1 s_2
- | a_1 b_1 c_1
- | a_2 b_2 c_2
- | a_3 b_3 c_3`,
- };
-
- let prediction = model.predictAndSample(example);
-
- example.labels = [{ action: prediction["action"], cost: 1.0, probability: prediction["score"] }];
-
- model.learn(example);
- vwLogger.logCBExampleToStream(example);
-
- model.saveModelToFile("my_model.vw");
- vwLogger.endLogStream();
- model.delete();
-
- let model2 = new vw.CbWorkspace({ model_file: "my_model.vw" });
- console.log(model2.predict(example));
- console.log(model2.predictAndSample(example));
- model2.delete();
-});
-```
### cbWorkspace.predict(example) ⇒ array
@@ -551,7 +494,7 @@ Takes a file location and stores the VW model in binary format in the file.
### cbWorkspace.getModelAsArray() ⇒ Uint8Array
Gets the VW model in binary format as a Uint8Array that can be saved to a file.
-There is no need to delete or free the array returned by this function.
+There is no need to delete or free the array returned by this function.
If the same array is however used to re-load the model into VW, then the array needs to be stored in wasm memory (see loadModelFromArray)
**Kind**: instance method of [CbWorkspace
](#CbWorkspace)
@@ -580,7 +523,7 @@ The memory must be allocated via the WebAssembly module's _malloc function and s
| Param | Type | Description |
| --- | --- | --- |
-| model_array_ptr | \*
| the pre-loaded model's array pointer The memory must be allocated via the WebAssembly module's _malloc function and should later be freed via the _free function. |
+| model_array_ptr | number
| the pre-loaded model's array pointer The memory must be allocated via the WebAssembly module's _malloc function and should later be freed via the _free function. |
| model_array_len | number
| the pre-loaded model's array length |
diff --git a/wasm/package.json b/wasm/package.json
index 8370d35099e..5828228cd65 100644
--- a/wasm/package.json
+++ b/wasm/package.json
@@ -3,16 +3,25 @@
"version": "0.0.1",
"description": "wasm bindings for vowpal wabbit",
"exports": {
- "require": "./vw.js"
+ "require": "./dist/vw.js"
},
- "main": "\"\"",
+ "main": "dist/vw.js",
+ "files": [
+ "dist/",
+ "src/**/*.ts",
+ "!src/**/*.cc"
+ ],
"devDependencies": {
+ "@types/node": "^20.2.1",
"jsdoc-to-markdown": "^8.0.0",
- "mocha": "^9.1.2"
+ "mocha": "^9.1.2",
+ "typescript": "^5.0.4"
},
"scripts": {
+ "postinstall": "npm run build",
+ "build": "tsc",
"test": "node --experimental-wasm-threads ./node_modules/mocha/bin/mocha --delay",
- "docs": "jsdoc2md ./vw.js > documentation.md"
+ "docs": "jsdoc2md ./dist/vw.js > documentation.md"
},
"dependencies": {
"out": "^1.1.0"
diff --git a/wasm/vw.js b/wasm/src/vw.ts
similarity index 89%
rename from wasm/vw.js
rename to wasm/src/vw.ts
index 547ff87d360..7470cb9c683 100644
--- a/wasm/vw.js
+++ b/wasm/src/vw.ts
@@ -1,6 +1,7 @@
-const fs = require('fs');
-const crypto = require('crypto');
-const VWWasmModule = require('./out/vw-wasm.js');
+import fs from 'fs';
+import crypto from 'crypto';
+
+const VWWasmModule = require('./vw-wasm.js');
// internals
@@ -17,6 +18,9 @@ const ProblemType =
* @class
*/
class VWExampleLogger {
+ _outputLogStream: fs.WriteStream | null;
+ _log_file: string | null;
+
constructor() {
this._outputLogStream = null;
this._log_file = null;
@@ -29,7 +33,7 @@ class VWExampleLogger {
* @param {string} log_file the path to the file where the log will be appended to
* @throws {Error} Throws an error if another logging stream has already been started
*/
- startLogStream(log_file) {
+ startLogStream(log_file: string) {
if (this._outputLogStream !== null) {
throw new Error("Can not start log stream, another log stream is currently active. Call endLogStream first if you want to change the log file. Current log file: " + this._log_file);
}
@@ -45,7 +49,7 @@ class VWExampleLogger {
* @param {string} line the line to be appended to the log file
* @throws {Error} Throws an error if no logging stream has been started
*/
- logLineToStream(line) {
+ logLineToStream(line: string) {
if (this._outputLogStream !== null) {
this._outputLogStream.write(line);
}
@@ -77,7 +81,7 @@ class VWExampleLogger {
* @param {string} line the line to be appended to the log file
* @throws {Error} Throws an error if another logging stream has already been started
*/
- logLineSync(log_file, line) {
+ logLineSync(log_file: string, line: string) {
if (this._outputLogStream !== null && this._log_file === log_file) {
throw new Error("Can not call logLineSync on log file while the same file has an async log writer active. Call endLogStream first. Log file: " + log_file);
}
@@ -92,7 +96,7 @@ class VWExampleLogger {
* @returns {string} the string representation of the CB example
* @throws {Error} Throws an error if the example is malformed
*/
- CBExampleToString(example) {
+ CBExampleToString(example: { text_context: string, labels: Array<{ action: number, cost: number, probability: number }> }): string {
let context = ""
if (example.hasOwnProperty('text_context')) {
context = example.text_context;
@@ -101,7 +105,8 @@ class VWExampleLogger {
throw new Error("Can not log example, there is no context available");
}
- const lines = context.split("\n").map((substr) => substr.trim());
+ const lines = context.trim().split("\n").map((substr) => substr.trim());
+ lines.push("");
lines.push("");
if (example.hasOwnProperty("labels") && example["labels"].length > 0) {
@@ -129,7 +134,7 @@ class VWExampleLogger {
* @param {object} example a CB example that will be stringified and appended to the log file
* @throws {Error} Throws an error if no logging stream has been started
*/
- logCBExampleToStream(example) {
+ logCBExampleToStream(example: { text_context: string, labels: Array<{ action: number, cost: number, probability: number }> }) {
let ex_str = this.CBExampleToString(example);
this.logLineToStream(ex_str);
}
@@ -143,17 +148,21 @@ class VWExampleLogger {
* @param {object} example a CB example that will be stringified and appended to the log file
* @throws {Error} Throws an error if another logging stream has already been started
*/
- logCBExampleSync(log_file, example) {
+ logCBExampleSync(log_file: string, example: { text_context: string, labels: Array<{ action: number, cost: number, probability: number }> }) {
let ex_str = this.CBExampleToString(example);
this.logLineSync(log_file, ex_str);
}
};
module.exports = new Promise((resolve) => {
- VWWasmModule().then(moduleInstance => {
+ VWWasmModule().then((moduleInstance: any) => {
class WorkspaceBase {
- constructor(type, { args_str, model_file, model_array = [] } = {}) {
-
+ _args_str: string | undefined;
+ _instance: any;
+
+ constructor(type: string, { args_str, model_file, model_array }:
+ { args_str?: string, model_file?: string, model_array?: [number | undefined, number | undefined] } = {}) {
+
let vwModelConstructor = null;
if (type === ProblemType.All) {
vwModelConstructor = moduleInstance.VWModel;
@@ -163,32 +172,36 @@ module.exports = new Promise((resolve) => {
else {
throw new Error("Unknown model type");
}
-
- const [model_array_ptr, model_array_len] = model_array;
-
+
+ let model_array_ptr: number | undefined = undefined;
+ let model_array_len: number | undefined = undefined;
+ if (model_array !== undefined) {
+ [model_array_ptr, model_array_len] = model_array;
+ }
+
let model_array_defined = model_array_ptr !== undefined && model_array_len !== undefined && model_array_ptr !== null && model_array_len > 0;
-
+
if (args_str === undefined && model_file === undefined && !model_array_defined) {
throw new Error("Can not initialize vw object without args_str or a model_file or a model_array");
}
-
+
if (model_file !== undefined && model_array_defined) {
throw new Error("Can not initialize vw object with both model_file and model_array");
}
-
+
this._args_str = args_str;
if (args_str === undefined) {
this._args_str = "";
}
-
+
if (model_file !== undefined) {
let modelBuffer = fs.readFileSync(model_file);
let ptr = moduleInstance._malloc(modelBuffer.byteLength);
let heapBytes = new Uint8Array(moduleInstance.HEAPU8.buffer, ptr, modelBuffer.byteLength);
heapBytes.set(new Uint8Array(modelBuffer));
-
+
this._instance = new vwModelConstructor(this._args_str, ptr, modelBuffer.byteLength);
-
+
moduleInstance._free(ptr);
}
else if (model_array_defined) {
@@ -197,10 +210,10 @@ module.exports = new Promise((resolve) => {
else {
this._instance = new vwModelConstructor(this._args_str);
}
-
+
return this;
}
-
+
/**
* Returns the enum value of the prediction type corresponding to the problem type of the model
* @returns enum value of prediction type
@@ -208,37 +221,37 @@ module.exports = new Promise((resolve) => {
predictionType() {
return this._instance.predictionType();
}
-
+
/**
* The current total sum of the progressive validation loss
*
* @returns {number} the sum of all losses accumulated by the model
*/
- sumLoss() {
+ sumLoss(): number {
return this._instance.sumLoss();
}
-
+
/**
*
* Takes a file location and stores the VW model in binary format in the file.
*
* @param {string} model_file the path to the file where the model will be saved
*/
- saveModelToFile(model_file) {
+ saveModelToFile(model_file: string) {
let char_vector = this._instance.getModel();
const size = char_vector.size();
const uint8Array = new Uint8Array(size);
-
+
for (let i = 0; i < size; ++i) {
uint8Array[i] = char_vector.get(i);
}
-
+
fs.writeFileSync(model_file, Buffer.from(uint8Array));
-
+
char_vector.delete();
}
-
-
+
+
/**
* Gets the VW model in binary format as a Uint8Array that can be saved to a file.
* There is no need to delete or free the array returned by this function.
@@ -246,7 +259,7 @@ module.exports = new Promise((resolve) => {
*
* @returns {Uint8Array} the VW model in binary format
*/
- getModelAsArray() {
+ getModelAsArray(): Uint8Array {
let char_vector = this._instance.getModel();
const size = char_vector.size();
const uint8Array = new Uint8Array(size);
@@ -254,17 +267,17 @@ module.exports = new Promise((resolve) => {
uint8Array[i] = char_vector.get(i);
}
char_vector.delete();
-
+
return uint8Array;
}
-
+
/**
*
* Takes a file location and loads the VW model from the file.
*
* @param {string} model_file the path to the file where the model will be loaded from
*/
- loadModelFromFile(model_file) {
+ loadModelFromFile(model_file: string) {
let modelBuffer = fs.readFileSync(model_file);
let ptr = moduleInstance._malloc(modelBuffer.byteLength);
let heapBytes = new Uint8Array(moduleInstance.HEAPU8.buffer, ptr, modelBuffer.byteLength);
@@ -272,19 +285,19 @@ module.exports = new Promise((resolve) => {
this._instance.loadModelFromBuffer(ptr, modelBuffer.byteLength);
moduleInstance._free(ptr);
}
-
+
/**
* Takes a model in an array binary format and loads it into the VW instance.
* The memory must be allocated via the WebAssembly module's _malloc function and should later be freed via the _free function.
*
- * @param {*} model_array_ptr the pre-loaded model's array pointer
+ * @param {number} model_array_ptr the pre-loaded model's array pointer
* The memory must be allocated via the WebAssembly module's _malloc function and should later be freed via the _free function.
* @param {number} model_array_len the pre-loaded model's array length
*/
- loadModelFromArray(model_array_ptr, model_array_len) {
+ loadModelFromArray(model_array_ptr: number, model_array_len: number) {
this._instance.loadModelFromBuffer(model_array_ptr, model_array_len);
}
-
+
/**
* Deletes the underlying VW instance. This function should be called when the instance is no longer needed.
*/
@@ -292,7 +305,7 @@ module.exports = new Promise((resolve) => {
this._instance.delete();
}
};
-
+
/**
* A Wrapper around the Wowpal Wabbit C++ library.
* @class
@@ -314,10 +327,11 @@ module.exports = new Promise((resolve) => {
* - both string arguments and a model array are provided, and the string arguments and arguments defined in the model clash
* - both a model file and a model array are provided
*/
- constructor({ args_str, model_file, model_array } = {}) {
+ constructor({ args_str, model_file, model_array }:
+ { args_str?: string, model_file?: string, model_array?: [number | undefined, number | undefined] } = {}) {
super(ProblemType.All, { args_str, model_file, model_array });
}
-
+
/**
* Parse a line of text into a VW example.
* The example can then be used for prediction or learning.
@@ -326,39 +340,39 @@ module.exports = new Promise((resolve) => {
* @param {string} line
* @returns a parsed vw example that can be used for prediction or learning
*/
- parse(line) {
+ parse(line: string): object {
return this._instance.parse(line);
}
-
+
/**
* Calls vw predict on the example and returns the prediction.
*
* @param {object} example returned from parse()
* @returns the prediction with a type corresponding to the reduction that was used
*/
- predict(example) {
+ predict(example: object) {
return this._instance.predict(example);
}
-
+
/**
* Calls vw learn on the example and updates the model
*
* @param {object} example returned from parse()
*/
- learn(example) {
+ learn(example: object) {
return this._instance.learn(example);
}
-
+
/**
* Cleans the example and returns it to the pool of available examples. delete() must also be called on the example object
*
* @param {object} example returned from parse()
*/
- finishExample(example) {
+ finishExample(example: object) {
return this._instance.finishExample(example);
}
};
-
+
/**
* A Wrapper around the Wowpal Wabbit C++ library for Contextual Bandit exploration algorithms.
* @class
@@ -380,12 +394,14 @@ module.exports = new Promise((resolve) => {
* - both string arguments and a model array are provided, and the string arguments and arguments defined in the model clash
* - both a model file and a model array are provided
*/
-
- constructor({ args_str, model_file, model_array } = {}) {
+
+ _ex: string;
+ constructor({ args_str, model_file, model_array }:
+ { args_str?: string, model_file?: string, model_array?: [number | undefined, number | undefined] } = {}) {
super(ProblemType.CB, { args_str, model_file, model_array });
this._ex = "";
}
-
+
/**
* Takes a CB example and returns an array of (action, score) pairs, representing the probability mass function over the available actions
* The returned pmf can be used with samplePmf to sample an action
@@ -396,10 +412,10 @@ module.exports = new Promise((resolve) => {
* @param {object} example the example object that will be used for prediction
* @returns {array} probability mass function, an array of action,score pairs that was returned by predict
*/
- predict(example) {
+ predict(example: object) {
return this._instance.predict(example);
}
-
+
/**
* Takes a CB example and uses it to update the model
*
@@ -415,17 +431,17 @@ module.exports = new Promise((resolve) => {
*
* @param {object} example the example object that will be used for prediction
*/
- learn(example) {
+ learn(example: object) {
return this._instance.learn(example);
}
-
+
/**
* Accepts a CB example (in text format) line by line. Once a full CB example is passed in it will call learnFromString.
* This is intended to be used with files that have CB examples, that were logged using logCBExampleToStream and are being read line by line.
*
* @param {string} line a string representing a line from a CB example in text Vowpal Wabbit format
*/
- addLine(line) {
+ addLine(line: string) {
if (line.trim() === '') {
this.learnFromString(this._ex);
this._ex = "";
@@ -433,27 +449,27 @@ module.exports = new Promise((resolve) => {
else {
this._ex = this._ex + line + "\n";
}
-
+
}
-
+
/**
* Takes a full multiline CB example in text format and uses it to update the model. This is intended to be used with examples that are logged to a file using logCBExampleToStream.
*
* @param {string} example a string representing the CB example in text Vowpal Wabbit format
* @throws {Error} Throws an error if the example is an object with a label and/or a text_context
*/
- learnFromString(example) {
+ learnFromString(example: string) {
if (example.hasOwnProperty("labels") || example.hasOwnProperty("text_context")) {
throw new Error("Example should not have a label or a text_context when using learnFromString, the label and context should just be in the string");
}
-
+
let ex = {
text_context: example
}
-
+
return this._instance.learnFromString(ex);
}
-
+
/**
*
* Takes an exploration prediction (array of action, score pairs) and returns a single action and score,
@@ -466,13 +482,13 @@ module.exports = new Promise((resolve) => {
* - uuid: the uuid that was passed to the predict function
* @throws {Error} Throws an error if the input is not an array of action,score pairs
*/
- samplePmf(pmf) {
+ samplePmf(pmf: Array): object {
let uuid = crypto.randomUUID();
let ret = this._instance._samplePmf(pmf, uuid);
ret["uuid"] = uuid;
return ret;
}
-
+
/**
*
* Takes an exploration prediction (array of action, score pairs) and a unique id that is used to seed the sampling,
@@ -486,12 +502,12 @@ module.exports = new Promise((resolve) => {
* - uuid: the uuid that was passed to the predict function
* @throws {Error} Throws an error if the input is not an array of action,score pairs
*/
- samplePmfWithUUID(pmf, uuid) {
+ samplePmfWithUUID(pmf: Array, uuid: string): object {
let ret = this._instance._samplePmf(pmf, uuid);
ret["uuid"] = uuid;
return ret;
}
-
+
/**
*
* Takes an example with a text_context field and calls predict. The prediction (a probability mass function over the available actions)
@@ -505,13 +521,13 @@ module.exports = new Promise((resolve) => {
* - uuid: the uuid that was passed to the predict function
* @throws {Error} if there is no text_context field in the example
*/
- predictAndSample(example) {
+ predictAndSample(example: object): object {
let uuid = crypto.randomUUID();
let ret = this._instance._predictAndSample(example, uuid);
ret["uuid"] = uuid;
return ret;
}
-
+
/**
*
* Takes an example with a text_context field and calls predict, and a unique id that is used to seed the sampling.
@@ -525,14 +541,14 @@ module.exports = new Promise((resolve) => {
* - uuid: the uuid that was passed to the predict function
* @throws {Error} if there is no text_context field in the example
*/
- predictAndSampleWithUUID(example, uuid) {
+ predictAndSampleWithUUID(example: object, uuid: string): object {
let ret = this._instance._predictAndSample(example, uuid);
ret["uuid"] = uuid;
return ret;
}
};
-
- function getExceptionMessage(exception) {
+
+ function getExceptionMessage(exception: number): string {
return moduleInstance.getExceptionMessage(exception)
};
@@ -558,10 +574,10 @@ module.exports = new Promise((resolve) => {
Workspace: Workspace,
CbWorkspace: CbWorkspace,
Prediction: Prediction,
- VWExampleLogger, VWExampleLogger,
+ VWExampleLogger: VWExampleLogger,
getExceptionMessage: getExceptionMessage,
wasmModule: moduleInstance
}
)
})
-})
+});
diff --git a/wasm/wasm_wrapper.cc b/wasm/src/wasm_wrapper.cc
similarity index 100%
rename from wasm/wasm_wrapper.cc
rename to wasm/src/wasm_wrapper.cc
diff --git a/wasm/example.js b/wasm/test/example.js
similarity index 98%
rename from wasm/example.js
rename to wasm/test/example.js
index aff1af9e161..db8577d728d 100644
--- a/wasm/example.js
+++ b/wasm/test/example.js
@@ -1,4 +1,4 @@
-const vwPromise = require('./vw.js');
+const vwPromise = require('vowpalwabbit');
const fs = require('fs');
// Delay test execution until the WASM VWModule is ready
diff --git a/wasm/test/test.js b/wasm/test/test.js
index f3c0cef8347..16b6adaae5f 100644
--- a/wasm/test/test.js
+++ b/wasm/test/test.js
@@ -4,7 +4,7 @@ const fs = require('fs');
const readline = require('readline');
const path = require('path');
-const vwPromise = require('../vw.js');
+const vwPromise = require('vowpalwabbit');
let vw;
async function run() {
diff --git a/wasm/tsconfig.json b/wasm/tsconfig.json
new file mode 100644
index 00000000000..1b23c5bb354
--- /dev/null
+++ b/wasm/tsconfig.json
@@ -0,0 +1,110 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "commonjs", /* Specify what module code is generated. */
+ "rootDir": "./src", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ "outDir": "./dist", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ },
+ "exclude": ["node_modules", "**/*.spec.ts", "test/example.js", "**/*.md"]
+}