@defuse-protocol/outlayer-sdk
v0.6.0
Published
TypeScript gRPC SDK for the Outlayer service
Readme
@defuse-protocol/outlayer-sdk
TypeScript gRPC SDK for the Outlayer
service, built with Connect-ES. Node.js only —
speaks gRPC over HTTP/2 to the worker, and ships the generated message types plus an
execute helper.
Install
The SDK is published publicly to npm:
npm install @defuse-protocol/outlayer-sdkUsage
import { createOutlayerClient } from "@defuse-protocol/outlayer-sdk";
const client = createOutlayerClient("http://localhost:50051");
const res = await client.execute({
app: { appId: "app.near" },
input: new TextEncoder().encode("{}"),
});
if (res.error) {
console.error(res.error, res.fuelConsumed); // WASM run failed (returned as data)
} else {
console.log(new TextDecoder().decode(res.output), res.fuelConsumed);
}Apps can also be executed directly from a URL with either a hexadecimal or raw-byte SHA-256 hash:
const res = await client.execute({
app: {
codeUrl: "https://example.com/app.wasm",
codeHash: "8f2466da...",
},
input: new TextEncoder().encode("{}"),
});execute resolves to { output, logs, fuelConsumed, error? }; output/logs are raw
Uint8Arrays. A populated error means the run reached the worker but the WASM
failed — returned as data, not thrown. Transport failures reject with a ConnectError.
Deadlines, cancellation, and fuel
execute takes three more options. All of them are optional:
const controller = new AbortController();
const res = await client.execute(
{ app, input },
{
timeoutMs: 5_000,
signal: controller.signal,
fuelLimit: 1_000_000_000n,
},
);timeoutMs is one budget for the whole call. Getting the token, the request, and the
retry with a refreshed token all share it, so a call never takes two full timeouts. To
give every call a deadline, set a default on the client. A single call can still override
it:
const client = createOutlayerClient({
endpoint: "https://worker.example.com:443",
defaultTimeoutMs: 30_000,
});timeoutMs and signal only end the call on your side. The worker finishes a run that
has already started, so neither of them frees its capacity. fuelLimit is the only limit
that can stop a run while it runs. Leave it out and the worker uses its own default.
Authentication
A worker behind Google IAM wants a Google ID token. Create a Google token manager and inject it into the client. The manager mints, caches, and refreshes tokens. If the worker rejects a request as unauthenticated, the Google manager invalidates the cached token, generates a fresh one, and retries once:
import {
createGoogleJwtTokenManager,
createOutlayerClient,
type JwtTokenManager,
} from "@defuse-protocol/outlayer-sdk";
const jwtTokenManager = createGoogleJwtTokenManager({
impersonate: "[email protected]",
});
const client = createOutlayerClient({
endpoint: "https://worker.example.com:443",
auth: jwtTokenManager,
});Omit auth for a worker with authentication disabled. With Google,
omit impersonate to use Application Default Credentials directly:
createOutlayerClient("http://localhost:50051");
createGoogleJwtTokenManager();
createGoogleJwtTokenManager({ impersonate });Google auth needs the optional peer dependency google-auth-library. Install it only
when using createGoogleJwtTokenManager.
The audience defaults to outlayer, which is what the worker expects unless its
deployment sets another one. Override it in the manager options:
createGoogleJwtTokenManager({ audience: "another-audience" });Other identity providers can implement the provider-neutral interface:
const jwtTokenManager: JwtTokenManager = {
generateToken: () => myTokenSource(),
invalidateToken: async () => clearMyTokenCache(),
handleAuthError: async (error) => {
throw error;
},
};Call invalidateToken() at any time to force the next request to generate a fresh
token:
jwtTokenManager.invalidateToken();An authToken passed to execute overrides the configured manager for that
request. The override can be a token or a synchronous or asynchronous function:
await client.execute({ app, input }, { authToken: idToken });
await client.execute({ app, input }, { authToken: () => getFreshToken() });Override tokens are sent as given and bypass the manager's retry behavior.
The JWT-protected worker integration test is disabled unless its endpoint or token is configured. Provide both variables to run it:
OUTLAYER_ENDPOINT=https://worker.example.com:443 \
OUTLAYER_JWT_TOKEN="$(get-token)" \
pnpm test:jwt-liveFor inline WASM or a custom transport, use the underlying Connect client at
client.raw, or build your own from the re-exported OutlayerService.
