Skip to content

feat(actor): add actor command and scaffolding - #555

Draft
ImriKochWix wants to merge 17 commits into
mainfrom
feat/realtime-handler
Draft

feat(actor): add actor command and scaffolding#555
ImriKochWix wants to merge 17 commits into
mainfrom
feat/realtime-handler

Conversation

@ImriKochWix

@ImriKochWix ImriKochWix commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Note

Description

Adds actors as a first-class Base44 resource: stateful, long-lived server objects (prototyped earlier in this branch as "realtime handlers") that live in base44/actors/<ActorName>/entry.ts. The PR introduces a new base44 actor command group (new, deploy), wires actors into project config reading, unified base44 deploy, and TypeScript type generation. Actors can declare a schema.jsonc message catalog (types / toClient / toServer), which is compiled into a discriminated-union ActorRegistry so client and server message handling is fully typed.

Related Issue

None

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Other (please describe): CI fix — pin npm@11 in the publish workflows

Changes Made

New actor resource (src/core/resources/actor/)

  • schema.ts — Zod schemas for the actor config, the schema.jsonc message catalog (types / toClient / toServer), and the deploy response (deployed | unchanged)
  • config.ts — discovers **/entry.{js,ts} under the actors dir, derives the actor name from its subfolder, loads the optional schema.jsonc, and rejects duplicate names or an entry.ts placed directly in the actors root
  • api.tsPUT actors/:name with the entry file plus all sibling .ts files, Zod-validated response, ApiError.fromHttpError() on failure
  • deploy.ts — sequential per-actor deploy with onStart/onResult callbacks and per-actor timing; failures are captured per actor rather than aborting the run
  • resource.ts — standard Resource<Actor> (readAll / push) so actors join the unified resource pipeline

CLI commands (src/cli/commands/actor/)

  • base44 actor new <ActorName> — scaffolds base44/actors/<ActorName>/entry.ts with a typed Actor<State, Message> subclass that imports Actor from the virtual base44:runtime/actors module and Conn as a pure type from the SDK, then re-reads the project and regenerates base44/.types/types.d.ts (plus updateProjectConfig) so that import resolves in the editor immediately
  • base44 actor deploy [names...] — deploys all actors or a comma/space-separated subset, prints per-actor status plus a summary, and exits 1 via CLIExitError if any actor fails
  • Registered the actor command group in program.ts

Project integration

  • Added actorsDir (default "actors") to ProjectConfigSchema, and actors to ProjectData / ProjectResources
  • readProjectConfig() now reads actors in parallel with the other resources; plugin projects get an empty actor list
  • hasResourcesToDeploy(), the base44 deploy summary, and deployAll() all account for actors
  • base44 types generate passes actors through to the generator

Type generation (src/core/types/generator.ts)

  • New ActorNameRegistry and ActorRegistry augmentations; each actor's message catalog compiles in a single json-schema-to-typescript pass into one named interface per message (direction-prefixed) plus shared types, with the type discriminant injected from the message key and #/types/X refs rewritten to prefixed $defs
  • Throws TypeGenerationError on generated-name collisions instead of silently clobbering
  • Emits declare module 'base44:runtime/actors' re-exporting only the Actor base class (pure types keep coming from the SDK)
  • Detects @base44/sdk vs @base44-preview/sdk from the project package.json, and adds export {}; so the generated file is a module and declare module augments rather than replaces the SDK types
  • Updated the empty-project template text to mention actors

CI

  • Pinned npm@11 in manual-publish.yml and preview-publish.ymlnpm@latest is now 12.x, which requires Node >= 22 and fails EBADENGINE on the Node 20 runner

Testing

  • I have tested these changes locally
  • I have added/updated tests as needed
  • All tests pass (npm test)

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (if applicable)
  • My changes generate no new warnings
  • I have updated docs/ (AGENTS.md) if I made architectural changes

Additional Notes

  • New unit tests in tests/core/types-actor.spec.ts cover catalog compilation (discriminant injection, shared-type reuse, registry union assembly, the base44:runtime/actors declaration) and the name-collision error. tests/cli/types_generate.spec.ts was extended with actor registry assertions plus a new ChatRoom fixture (entry.ts + schema.jsonc). The suite was not run while generating this description, so the "tested locally" / "all tests pass" boxes are left unchecked.
  • Docs are not updated yet: docs/resources.md, docs/commands.md, and the README don't mention the new resource or the base44 actor commands — worth adding before merge, per rule 9 ("Keep docs updated").
  • Actors deploy sequentially rather than in batches (unlike functions), so a project with many actors deploys more slowly.
  • actor new reads the project config twice (once for paths, once after writing the scaffold) so type generation picks up the new actor.
  • Earlier commits on this branch land the resource as realtime-handler / RealtimeHandler; it was renamed to actor / Actor later in the branch, and toClient / toServer replaced an earlier inbound / outbound pair. Only the final naming ships.

🤖 Generated by Claude | 2026-07-28 11:16 UTC | eaa386d

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/cli@0.1.6-pr.555.eaa386d

Prefer not to change any import paths? Install using npm alias so your code still imports base44:

npm i "base44@npm:@base44-preview/cli@0.1.6-pr.555.eaa386d"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/cli@0.1.6-pr.555.eaa386d"
  }
}

Preview published to npm registry — try new features instantly!

ImriKochWix and others added 12 commits June 30, 2026 13:54
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
new.ts used project.root but readAllRealtimeHandlers uses dirname(configPath),
causing handlers to be created at realtime/ instead of base44/realtime/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
'base44' is the CLI package name and has no exported types.
@base44/sdk now exports RealtimeHandler and Conn for type-checking,
and the bundler rewrites the import to the CF shim at deploy time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- base44 types generate now includes realtime handlers in types.d.ts
- RealtimeHandlerNameRegistry: auto-registers handler names (no manual declare needed)
- RealtimeHandlerRegistry: compiled from schema.jsonc inbound/outbound JSON schemas
- Add schema.jsonc support to realtime-handler resource reader
- Update test fixture with ChatRoom schema and assertions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Detect @base44/sdk vs @base44-preview/sdk from project's package.json
  so declare module targets the correct package name
- Add export {} to generated types.d.ts to ensure module context,
  preventing ambient module from shadowing the SDK package types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove unused RealtimeHandlerConfig type alias
- Replace [^]* regex with [\s\S]* (Biome noEmptyCharacterClassInRegex)
- Auto-format long lines per Biome formatter rules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The dedicated endpoint calls ensure_cfw_backend and uses force_per_function
so the bundler runs applyRealtimeCompat instead of the per-app path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
schema.jsonc is now a catalog of named messages (inbound/outbound maps of
message-name -> full JSON Schema, like entities) plus optional shared `types`.
compileRealtimeHandler emits one named interface per message (direction- and
handler-prefixed to avoid collisions) + shared types, and composes the
inbound/outbound unions in the registry.

Removes the /\{([\s\S]*)\}/ body-scrape, which produced invalid TS whenever
json-schema-to-typescript emitted more than one declaration (unions with
$defs). Because every message is a single flat object, that multi-declaration
case can no longer arise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lient/toServer

The old names were written from the client's perspective, so handler code
read backwards (InMsg = Reg["outbound"]) and every reader had to do the
double-negative. toClient/toServer read correctly from both sides:
Reg["toServer"] is what the handler receives, Reg["toClient"] is what it
sends. Generated interface prefixes follow (GameRoomToClientInit).

Breaking for schema.jsonc files and the generated registry shape; done now
while there are zero external users.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rename the Durable-Object abstraction and its CLI surface to the actor model:
- command group `base44 realtime <cmd>` -> `base44 actor <cmd>`
- resource dir src/core/resources/realtime-handler/ -> resources/actor/;
  commands/realtime/ -> commands/actor/
- project config key realtimeDir("realtime") -> actorsDir("actors");
  ProjectData.realtimeHandlers -> actors
- project directory convention base44/realtime/<Name>/ -> base44/actors/<Name>/
  (resource discovery + generated actor message types)
- builder deploy route PUT realtime-handlers/<name> -> PUT actors/<name>
- scaffold template emits `import { Actor } ... extends Actor`

The entity live-update Socket.IO dev-server (dev-server/realtime.ts,
createRealtimeServer) is intentionally left as "realtime" — it's the
entity-change feature, not the Actor DO.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Biome organizeImports across the files touched by the actor rename (import
  order shifted when realtime-handler paths became actor paths).
- preview-publish + manual-publish: pin npm@11; npm@latest is now 12.x which
  requires node >=22 and fails EBADENGINE on the node-20 runner (.node-version).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ImriKochWix ImriKochWix changed the title feat(realtime): add realtime command and handler scaffolding feat(actor): add actor command and scaffolding Jul 9, 2026
ImriKochWix and others added 4 commits July 26, 2026 14:58
Actors now import their base class from the bundler-served virtual module
`base44:runtime/actors` instead of `@base44/sdk`. Emit a matching ambient
declaration into the generated types.d.ts (when the app has actors) that
re-exports Actor / Conn / ActorRegistry from the SDK package, so the import
typechecks in the editor and ActorRegistry keeps its app-specific augmentation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pure types (Conn, ActorRegistry) have no runtime and ActorRegistry is
augmented onto the SDK, so they belong in @base44/sdk, not the bundler-served
runtime virtual module. The declare module now re-exports only Actor — the one
value whose runtime the bundler swaps. Authoring: `import { Actor } from
"base44:runtime/actors"` + `import type { Conn, ActorRegistry } from "@base44/sdk"`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Actor base class is the one value the bundler swaps at deploy/dev, so it
comes from the base44:runtime/actors virtual module; pure types (Conn) come
from the SDK. Matches the taught authoring shape and the type-gen declare
module.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e44:runtime/actors import resolves

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant