Skip to content

feat(platform)!: getSession with the socket path from the session, and asyncapi.json from production - #313

Merged
amitbardos merged 6 commits into
mainfrom
amitb/platform-asyncapi-sync
Oct 14, 2026
Merged

amitbardos merged 6 commits into
mainfrom
amitb/platform-asyncapi-sync

Conversation

@amitbardos

@amitbardos amitbardos commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Based on main now that #312 is merged.

Four commits. #317 and #318 are folded in here and closed; #329 is folded in too. The Socket messages page is no longer generated in this repo: mintlify-docs builds it from the AsyncAPI document production serves (base44-dev/mintlify-docs#2479), and the JSDoc links to it at /developers/references/apps-api/sections/socket-messages.

1. asyncapi.json from production (2316986)

The committed document is the one production serves now that base44-dev/apper#29607 has deployed (identical to https://app.base44.com/api/asyncapi.json), and gen:events regenerated the types. TaskProgressed.event_type is now the five task values, and current/total are integers.

2. getSession and the socket path from the session (686327c, breaking, 0.2.0)

The browser passes one function instead of socketUrl and getSessionToken, and the socket path comes from the session instead of the hard-coded /ws/socket.io/.

const client = createPlatformClient();
const builder = client.builder.init({
  async getSession() {
    const response = await fetch('/api/builder-socket-session', { method: 'POST' });
    return response.json(); // the Create socket session body, unchanged
  },
  onError,
});
  • builder.init({ getSession, onError }): getSession: () => Promise<SocketSession>, where SocketSession is { session_token, socket_url, socket_path }. Other keys (session_id, expires_in) are ignored. getSession is called everywhere getSessionToken was called before: the first connect, renewal after session.ended with reason expired, and the single renewal after a connection_denied on a cached token. The 20-second timeout and the cancellation in authenticate() are unchanged.
  • The socket is created on the first connect(), because the address is known only after the first session. Transport reconnects keep using the cached session.
  • A new address means a new socket. If a renewed session has a different socket_url or socket_path, the SDK doesn't call the old socket's auth callback. It removes the old socket's listeners, disconnects it, opens a socket at the new address, and rejoins every active subscription.
  • Validation. socket_url must be an HTTP(S) origin with no credentials, path, query or fragment (the same check as before). socket_path must be an absolute path with no query or fragment, and can't start with //.
  • createPlatformClient() takes no arguments. No options are left. TypeScript callers that still pass { socketUrl, getSessionToken } get a compile error instead of having the values silently ignored. An optional parameter can be added later without a breaking change. PlatformClientOptions is removed.
  • A bad session is session_unavailable, whether the token, the URL or the path is invalid. connect() rejects with it and onError receives it. No TypeError is thrown. The code already meant that the session endpoint gave the SDK nothing usable.
  • Version 0.2.0, with a new CHANGELOG.md written against what 0.1.0 shipped (Base44PlatformClient, serverUrl), including the type renames from feat(platform): generate event types from AsyncAPI, and Mintlify docs for @base44/platform #312. JSDoc, examples, README.md, unit, type and package tests are updated. SocketSession is listed on the builder reference page.

3. Hourly AsyncAPI sync (5bea8e5)

.github/workflows/sync-asyncapi.yml runs every hour, like mintlify-docs' OpenAPI syncs, and on workflow_dispatch. It first compares production's document with the committed one, so an hour with nothing new stops before npm ci. Otherwise it runs sync:asyncapi and gen:events. If asyncapi.json or the generated types changed, it builds the docs, runs the Platform SDK tests and opens or updates one rolling PR on bot/platform-asyncapi-sync, rebuilt from main on every run. When an open sync PR already has the same tree, it skips the commit and force-push, so a waiting PR isn't pushed every hour. When nothing changed, it closes a leftover sync PR. It uses the pinned actions/checkout and actions/setup-node, the Wix gateway proxy and the runner's gh, and adds no dependencies. Production already serves a document identical to the committed one, so a run today finds no diff. After merging, trigger it once by hand.

4. Forward events the SDK doesn't know (04e627a, changes Oz's event filtering)

Before, the session registered a handler only for the generated event names, so a new backend event was dropped until a partner upgraded. Now known app events keep their own handlers, and socket.onAny forwards any other name to onEvent as { type: "unknown"; name: string; appId: string; data: Record<string, unknown> }. Session events (app.snapshot, session.ended) and room.* notices are still not forwarded; a new notice has no error code, so it's dropped. Envelope validation, per-app routing, protocol_error and the rejoin after a main conversation.changed are unchanged. discard() also calls offAny().

The type is "unknown", not an OtherToolCall-style type: string. In TypeScript 5.9 a string & {} member stops event.type === "message.updated" from narrowing at all, and plain string narrows to that event plus the catch-all, so event.data.message becomes unknown. Either one breaks existing partner code. Backend event names always contain a dot, so "unknown" can't clash with a real one. A switch covering every type now needs case "unknown"; it's in the CHANGELOG.

New tests: an unknown event reaches only its app, known/session/room.* names aren't delivered twice, a malformed unknown frame fails only its app, close() calls offAny, an unknown event's data needs a check before use, and known types still narrow.

Tests

PATH=/opt/homebrew/bin:$PATH npm test in packages/platform:

 Test Files  2 passed (2)
      Tests  44 passed (44)
ℹ pass 5
ℹ fail 0

npm run check:events reports builder.events.generated.ts is up to date. npm run create-docs, run after the tests, completes. npm run lint is clean.

New unit tests cover: the path taken from the session, extra keys in the body ignored, renewal at a new address moving the socket and rejoining both subscriptions, a failed renewal, a late session after close(), and 11 invalid bodies.

Needs attention

  • Oz's agreement on commit 4. It changes the original event filtering @ozsay wrote.

  • Publishing 0.2.0. package.json is already at 0.2.0, so manual-publish.yml with version: 0.2.0 fails ("Version not changed"), and with minor it would publish 0.3.0. Either add --allow-same-version to the workflow, or set package.json back to 0.1.0 and publish with minor.

  • One release. feat(platform): generate event types from AsyncAPI, and Mintlify docs for @base44/platform #312 is on main and breaking without a version bump. Don't publish from main until this PR merges; then publish both as 0.2.0.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/sdk@0.8.53-pr.313.7fe73f7

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

npm i "@base44/sdk@npm:@base44-preview/sdk@0.8.53-pr.313.7fe73f7"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "@base44/sdk": "npm:@base44-preview/sdk@0.8.53-pr.313.7fe73f7"
  }
}

Preview published to npm registry — try new features instantly!

@amitbardos
amitbardos force-pushed the amitb/platform-asyncapi-sync branch from bb9a3d5 to 10d8bae Compare October 11, 2026 12:41
@amitbardos amitbardos changed the title Sync Platform SDK events from production every week feat(platform)!: Socket messages page, and getSession with the socket path from the session Oct 11, 2026
@amitbardos
amitbardos force-pushed the amitb/platform-asyncapi-sync branch 2 times, most recently from 43c68a6 to ffc290a Compare October 12, 2026 14:16
@amitbardos amitbardos changed the title feat(platform)!: Socket messages page, and getSession with the socket path from the session feat(platform)!: getSession with the socket path from the session, and asyncapi.json from production Oct 12, 2026
@amitbardos
amitbardos force-pushed the amitb/platform-event-codegen branch from 7e08ce4 to 419e113 Compare October 12, 2026 14:19
@amitbardos
amitbardos force-pushed the amitb/platform-asyncapi-sync branch from ffc290a to c6d7478 Compare October 12, 2026 14:19
@amitbardos
amitbardos marked this pull request as ready for review October 13, 2026 07:20
amitbardos and others added 2 commits October 13, 2026 15:05
asyncapi.json is the document production serves now that
base44-dev/apper#29607 has deployed, and gen:events regenerated the types:
TaskProgressed.event_type is the five task values, and current/total are
integers. sync-asyncapi.mjs documents production as its default source,
and PlatformSnapshot's JSDoc links to app.snapshot on Socket messages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ough

builder.init() takes getSession, which returns the Create socket session
body unchanged. The socket opens at the session's socket_url and
socket_path on the first connect(), and moves when a renewed session has
a different address. createPlatformClient() takes no options; socketUrl,
getSessionToken and PlatformClientOptions are gone. An invalid session
rejects connect() with session_unavailable. SocketSession is listed with
PlatformSnapshot under the builder page's Type Definitions. Version 0.2.0,
with a CHANGELOG.md written against what 0.1.0 shipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@amitbardos
amitbardos changed the base branch from amitb/platform-event-codegen to main October 13, 2026 12:05
@amitbardos
amitbardos force-pushed the amitb/platform-asyncapi-sync branch from 95678d9 to 99cb9ca Compare October 13, 2026 12:06
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🚀 Package Preview Available! (@base44/platform)


Install this PR's preview build with npm:

npm i @base44-preview/platform@0.2.0-pr.313.7fe73f7

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

npm i "@base44/platform@npm:@base44-preview/platform@0.2.0-pr.313.7fe73f7"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "@base44/platform": "npm:@base44-preview/platform@0.2.0-pr.313.7fe73f7"
  }
}

Preview published to npm registry — try new features instantly!

@amitbardos
amitbardos requested a review from ozsay October 13, 2026 12:09
An hourly workflow (and workflow_dispatch) compares
https://app.base44.com/api/asyncapi.json with the committed copy first, so
a run with nothing new stops before installing anything. When they differ
it regenerates the event types, builds the docs, runs the Platform SDK
tests and opens or updates one rolling PR on bot/platform-asyncapi-sync.
It closes that PR when main already matches production.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@amitbardos
amitbardos force-pushed the amitb/platform-asyncapi-sync branch from 99cb9ca to 5bea8e5 Compare October 13, 2026 12:11
A new backend event now reaches partners when the service deploys it, as
{ type: "unknown", name, appId, data }, instead of being dropped until they
upgrade. Session events and room.* notices are still not forwarded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/platform/src/modules/builder-socket.ts Outdated
Comment thread .github/workflows/sync-asyncapi.yml
amitbardos and others added 2 commits October 14, 2026 12:59
The catch-all sent every unknown event to the app-event path, which treats
a frame with no app room as unroutable and fails every subscription with
protocol_error. A future session-wide event (null room, like
session.ended) would have ended all live subscriptions on 0.2.0. Now an
unknown event is forwarded only when its room names an app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nges

Both change checks compare against main, so while a sync PR waited every
hourly run made a new commit with the same files and force-pushed it. The
job now compares the new commit's tree with the open sync branch's and
stops when they match. A moved main still changes the tree, so the branch
is rebuilt then, and a closed PR still gets a new one on the next run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@amitbardos
amitbardos requested a review from ozsay October 14, 2026 12:43
@amitbardos
amitbardos merged commit 166b3b2 into main Oct 14, 2026
8 checks passed
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.

2 participants