@superfaceai/one-sdk
Advanced tools
Comparing version 3.0.0-beta.13 to 3.0.0-beta.14
@@ -1,27 +0,3 @@ | ||
import { AppContext, FileSystem, Network, Persistence, TextCoder, Timers, WasiContext } from './interfaces.js'; | ||
import { SecurityValuesMap } from './security.js'; | ||
/** Async mutex allows us to synchronize multiple async tasks. | ||
* | ||
* For example, if a perform is in-flight but is waiting for I/O the async task is suspended. If at the same time | ||
* the periodic timer fires, this could cause core to be invoked twice within the same asyncify context, causing undefined behavior. | ||
* | ||
* We can avoid this by synchronizing over core. | ||
* | ||
* Note that this is not thread safe (concurrent), but merely task safe (asynchronous). | ||
*/ | ||
export declare class AsyncMutex<T> { | ||
/** Promise to be awaited to synchronize between tasks. */ | ||
private condvar; | ||
/** Indicator of whether the mutex is currently locked. */ | ||
private isLocked; | ||
private value; | ||
constructor(value: T); | ||
/** | ||
* Get the protected value without respecting the lock. | ||
* | ||
* This is unsafe, but it is needed to get access to memory in sf_host imports. | ||
*/ | ||
get unsafeValue(): T; | ||
withLock<R>(fn: (value: T) => R): Promise<Awaited<R>>; | ||
} | ||
import type { SecurityValuesMap } from './security.js'; | ||
import type { AppContext, FileSystem, Network, Persistence, TextCoder, Timers, WasiContext } from './interfaces.js'; | ||
export declare class App implements AppContext { | ||
@@ -28,0 +4,0 @@ private readonly textCoder; |
@@ -1,84 +0,4 @@ | ||
import { Asyncify } from './asyncify.js'; | ||
import { PerformError, UnexpectedError, UninitializedError, ValidationError, WasiErrno, WasiError } from './error.js'; | ||
import { HandleMap } from './handle_map.js'; | ||
import { AsyncMutex, Asyncify, HandleMap, ReadableStreamAdapter } from './lib/index.js'; | ||
import * as sf_host from './sf_host.js'; | ||
class ReadableStreamAdapter { | ||
constructor(stream) { | ||
this.reader = stream?.getReader(); | ||
this.chunks = []; | ||
} | ||
async read(out) { | ||
if (this.reader === undefined) { | ||
return 0; | ||
} | ||
if (this.chunks.length === 0) { | ||
const readResult = await this.reader.read(); | ||
if (readResult.value === undefined) { | ||
return 0; | ||
} | ||
this.chunks.push(readResult.value); | ||
} | ||
// TODO: coalesce multiple smaller chunks into one read | ||
let chunk = this.chunks.shift(); | ||
if (chunk.byteLength > out.byteLength) { | ||
const remaining = chunk.subarray(out.byteLength); | ||
chunk = chunk.subarray(0, out.byteLength); | ||
this.chunks.unshift(remaining); | ||
} | ||
const count = Math.min(chunk.byteLength, out.byteLength); | ||
for (let i = 0; i < count; i += 1) { | ||
out[i] = chunk[i]; | ||
} | ||
return count; | ||
} | ||
async write(data) { | ||
throw new Error('not implemented'); | ||
} | ||
async close() { | ||
// TODO: what to do here? | ||
} | ||
} | ||
/** Async mutex allows us to synchronize multiple async tasks. | ||
* | ||
* For example, if a perform is in-flight but is waiting for I/O the async task is suspended. If at the same time | ||
* the periodic timer fires, this could cause core to be invoked twice within the same asyncify context, causing undefined behavior. | ||
* | ||
* We can avoid this by synchronizing over core. | ||
* | ||
* Note that this is not thread safe (concurrent), but merely task safe (asynchronous). | ||
*/ | ||
export class AsyncMutex { | ||
constructor(value) { | ||
this.condvar = Promise.resolve(); | ||
this.isLocked = false; | ||
this.value = value; | ||
} | ||
/** | ||
* Get the protected value without respecting the lock. | ||
* | ||
* This is unsafe, but it is needed to get access to memory in sf_host imports. | ||
*/ | ||
get unsafeValue() { | ||
return this.value; | ||
} | ||
async withLock(fn) { | ||
do { | ||
// Under the assumption that we do not have concurrency it can never happen that two tasks | ||
// pass over the condition of this loop and think they both have a lock - that would imply there exists task preemption in synchronous code. | ||
// | ||
// If there ever is threading or task preemption, we will need to use other means (atomics, spinlocks). | ||
await this.condvar; | ||
} while (this.isLocked); | ||
this.isLocked = true; | ||
let notify; | ||
this.condvar = new Promise((resolve) => { notify = resolve; }); | ||
try { | ||
return await fn(this.value); | ||
} | ||
finally { | ||
this.isLocked = false; | ||
notify(); | ||
} | ||
} | ||
} | ||
function headersToMultimap(headers) { | ||
@@ -294,3 +214,3 @@ const result = {}; | ||
errName = 'WebAssemblyRuntimeError'; | ||
errMessage = err.message; | ||
errMessage = err.stack ?? err.message; | ||
} | ||
@@ -297,0 +217,0 @@ // in case we got here while already attempting to dump during an error, this condition prevents recursion |
@@ -1,6 +0,5 @@ | ||
export { App, AsyncMutex } from './app.js'; | ||
export { App } from './app.js'; | ||
export * from './error.js'; | ||
export { HandleMap } from './handle_map.js'; | ||
export type { FileSystem, Network, Persistence, TextCoder, Timers, WasiContext } from './interfaces.js'; | ||
export { SecurityValuesMap } from './security.js'; | ||
export * from './wasm.js'; | ||
export type { SecurityValuesMap } from './security.js'; | ||
export { HandleMap, AsyncMutex, corePathURL } from './lib/index.js'; |
@@ -1,5 +0,4 @@ | ||
export { App, AsyncMutex } from './app.js'; | ||
export { App } from './app.js'; | ||
export * from './error.js'; | ||
export { HandleMap } from './handle_map.js'; | ||
export * from './wasm.js'; | ||
export { HandleMap, AsyncMutex, corePathURL } from './lib/index.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -10,2 +10,4 @@ export interface AppContext { | ||
export interface FileSystem { | ||
/** Return true if the file exists (can be `stat`ed). */ | ||
exists(path: string): Promise<boolean>; | ||
open(path: string, options: { | ||
@@ -12,0 +14,0 @@ createNew?: boolean; |
import type { TextCoder, AppContext } from './interfaces'; | ||
import { Asyncify } from './asyncify.js'; | ||
import { Asyncify } from './lib/index.js'; | ||
export declare function link(app: AppContext, textCoder: TextCoder, asyncify: Asyncify): WebAssembly.Imports; |
@@ -1,3 +0,2 @@ | ||
import { HandleMap } from './handle_map.js'; | ||
import { AsyncifyState } from './asyncify.js'; | ||
import { AsyncifyState, HandleMap } from './lib/index.js'; | ||
import { WasiErrno } from './error.js'; | ||
@@ -4,0 +3,0 @@ function strace(name, fn, asyncify) { |
@@ -36,2 +36,3 @@ import { SecurityValuesMap } from './common/index.js'; | ||
private readyState; | ||
private readonly fileSystem; | ||
constructor(options?: ClientOptions); | ||
@@ -38,0 +39,0 @@ init(): Promise<void>; |
@@ -7,3 +7,3 @@ import fs from 'node:fs/promises'; | ||
import { WASI } from 'node:wasi'; | ||
import { AsyncMutex } from './common/app.js'; | ||
import { AsyncMutex } from './common/lib/index.js'; | ||
import { App, HandleMap, UnexpectedError, WasiErrno, WasiError, corePathURL } from './common/index.js'; | ||
@@ -31,2 +31,11 @@ import { fetchErrorToHostError, systemErrorToWasiError } from './error.js'; | ||
} | ||
async exists(path) { | ||
try { | ||
await fs.stat(path); | ||
return true; | ||
} | ||
catch { | ||
return false; | ||
} | ||
} | ||
async open(path, options) { | ||
@@ -166,5 +175,6 @@ let flags = ''; | ||
this.readyState = new AsyncMutex({ ready: false }); | ||
this.fileSystem = new NodeFileSystem(); | ||
this.app = new App({ | ||
network: new NodeNetwork(), | ||
fileSystem: new NodeFileSystem(), | ||
fileSystem: this.fileSystem, | ||
textCoder: new NodeTextCoder(), | ||
@@ -222,3 +232,9 @@ timers: new NodeTimers(), | ||
const resolvedProfile = profile.replace(/\//g, '.'); | ||
const path = resolvePath(this.assetsPath, `${resolvedProfile}.profile`); | ||
let path = resolvePath(this.assetsPath, `${resolvedProfile}.profile.ts`); | ||
// migration from Comlink to TypeScript profiles | ||
const pathComlink = resolvePath(this.assetsPath, `${resolvedProfile}.profile`); | ||
if (!(await this.fileSystem.exists(path)) | ||
&& (await this.fileSystem.exists(pathComlink))) { | ||
path = pathComlink; | ||
} | ||
return `file://${path}`; | ||
@@ -225,0 +241,0 @@ } |
{ | ||
"name": "@superfaceai/one-sdk", | ||
"version": "3.0.0-beta.13", | ||
"version": "3.0.0-beta.14", | ||
"exports": { | ||
@@ -5,0 +5,0 @@ ".": "./dist/index.js", |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
47
1485
5248880