Skip to main content
Home

Built and signed on GitHub Actions

Works with
This package works with Deno, BrowsersIt is unknown whether this package works with Cloudflare Workers, Node.js, Bun
It is unknown whether this package works with Cloudflare Workers
It is unknown whether this package works with Node.js
This package works with Deno
It is unknown whether this package works with Bun
This package works with Browsers
JSR Score76%
Published7 months ago (0.2.0)

A lean webworker actor framework, work in progress

StageForge is a TypeScript library that simplifies creating and managing actor-based web worker communication.

Mainly used in https://github.com/goodpuppies/petplay

Basic Usage

main.ts - Create the coordinator

import { type ActorId, PostalService } from "@goodpuppies/stageforge";

// Initialize the postal service (central coordinator)
const postalService = new PostalService();

// Create a new actor from the specified file
const mainActorId = await postalService.functions.CREATE({ file: "./actor.ts" }) as ActorId;

// Send a message and wait for response
const response = await postalService.PostMessage({
  target: mainActorId,
  type: "HELLO",
}, true);

console.log(response); // "hi"

actor.ts - Define an actor

import { actorState, PostMan } from "@goodpuppies/stageforge";

// Create a type-safe state with proper initialization
const state = actorState({
  name: "main",
  // Add your custom state properties
});

// Define your actor's message handlers with strict typing
export const api = {
  // Special initialization handler
  __INIT__: (payload: string) => {
    console.log("Actor initialized with ID:", state.id);
    main(payload);
  },
  HELLO: (_payload: null) => {
    return "hi";
  },
  LOG: (_payload: null) => {
    console.log("Actor:", state.id);
  },
} as const;

// Initialize the actor with state and API
new PostMan(state, api);

// Your main actor logic
async function main(_payload: string) {
  // Create a child actor
  const subActorId = await PostMan.create("./sub.ts");

  // Send a message with type checking
  const response = await PostMan.PostMessage<typeof subApi>({
    target: subActorId,
    type: "GETSTRING",
  }, true);

  console.log(response);
}

sub.ts - Define a child actor

import { actorState, PostMan } from "jsr:@goodpuppies/stageforge";

const state = actorState({
  name: "sub",
});

// Export API for type checking in other actors
export const api = {
  __INIT__: (_payload: null) => {
    console.log("Sub actor initialized");
  },
  GETSTRING: (_payload: null) => {
    return "Hello from sub actor";
  },
  LOG: (_payload: null) => {
    console.log("Hello from", state.id);
  },
} as const;

// Initialize the actor with state and API
new PostMan(state, api);

New Features in 0.2.0

Stageforge signals

see examples/signals

Worker plugins for networking

unstable

PostalService now exposes GenericActorFunctions

const mainActorId = await postalService.functions.CREATE({ file: "./actor.ts" });

postalService.functions.MURDER(mainActorId);

Payloads are now optional

PostMan.PostMessage({
  target: actors,
  type: "LOG",
});

//they are nulled when missing
if (!message.payload) {
  message.payload = null;
}

Parent api

Actor state now includes parent as an explicit property

const state = actorState({
  name: "main",
});

console.log("my parent is", state.parent);

//parents can be overriden on creation
const sub = await PostMan.create("./actors/sub.ts", undefined, System);

PostMan context is exposed

Handlers can now check internal postman properties such as ctx.sender to see the senders address

export const api = {
  GETSTRING: (_payload: null, ctx: typeof PostMan) => {
    console.log("getstring ctx sender", ctx.sender);
    console.log("getstring ctx", ctx);
    return "some text";
  },
} as const;

New Features in 0.1.0

Topics API

The Topics API allows actors to discover each other automatically by subscribing to named topics:

// In actor1.ts
PostMan.setTopic("shared-channel");

// In actor2.ts
PostMan.setTopic("shared-channel");
// Now actor1 and actor2 automatically know about each other
// Their IDs are added to each other's addressBook

// To find actors in the addressBook you can do something like
const actors = Array.from(state.addressBook)
  .filter((addr) => addr.startsWith("sub@"));

// To unsubscribe from a topic:
PostMan.delTopic("shared-channel");

Typed Message API

Better typing support is in the works. 0.1 adds typing to PostMessage.

// In sub.ts
export const api = {
  ADD: (payload: { a: number; b: number }) => {
    return payload.a + payload.b;
  },
} as const;

// In main.ts
import type { api as subApi } from "./sub.ts";

// Type-checked message - compiler will validate type and payload
const result = await PostMan.PostMessage<typeof subApi>({
  target: subActorId,
  type: "ADD", // Autocomplete works here
  payload: { a: 5, b: 3 }, // Type checked!
}, true);

Improved Actor State

Actor state api now has better ts support so you can always get type info about the state.

import { actorState } from "jsr:@goodpuppies/stageforge";

const state = actorState({
  name: "myActor",
  // Your custom state properties:
  counter: 0,
  data: new Map(),
  custom: null as null | myType,
});

License

MIT

Built and signed on
GitHub Actions

Report package

Please provide a reason for reporting this package. We will review your report and take appropriate action.

Please review the JSR usage policy before submitting a report.

Add Package

deno add jsr:@goodpuppies/stageforge

Import symbol

import * as stageforge from "@goodpuppies/stageforge";
or

Import directly with a jsr specifier

import * as stageforge from "jsr:@goodpuppies/stageforge";

Add Package

pnpm i jsr:@goodpuppies/stageforge
or (using pnpm 10.8 or older)
pnpm dlx jsr add @goodpuppies/stageforge

Import symbol

import * as stageforge from "@goodpuppies/stageforge";

Add Package

yarn add jsr:@goodpuppies/stageforge
or (using Yarn 4.8 or older)
yarn dlx jsr add @goodpuppies/stageforge

Import symbol

import * as stageforge from "@goodpuppies/stageforge";

Add Package

vlt install jsr:@goodpuppies/stageforge

Import symbol

import * as stageforge from "@goodpuppies/stageforge";

Add Package

npx jsr add @goodpuppies/stageforge

Import symbol

import * as stageforge from "@goodpuppies/stageforge";

Add Package

bunx jsr add @goodpuppies/stageforge

Import symbol

import * as stageforge from "@goodpuppies/stageforge";