@base44-preview/platform 0.0.0 → 0.1.0-pr.319.850fe58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +225 -0
  3. package/dist/client/client.d.ts +9 -0
  4. package/dist/client/client.js +9 -0
  5. package/dist/client/client.types.d.ts +12 -0
  6. package/dist/client/client.types.js +1 -0
  7. package/dist/client/errors.d.ts +10 -0
  8. package/dist/client/errors.js +10 -0
  9. package/dist/client/errors.types.d.ts +2 -0
  10. package/dist/client/errors.types.js +1 -0
  11. package/dist/client/index.d.ts +7 -0
  12. package/dist/client/index.js +3 -0
  13. package/dist/client/modules/builder-protocol.d.ts +15 -0
  14. package/dist/client/modules/builder-protocol.js +34 -0
  15. package/dist/client/modules/builder-socket.d.ts +42 -0
  16. package/dist/client/modules/builder-socket.js +252 -0
  17. package/dist/client/modules/builder-subscription.d.ts +23 -0
  18. package/dist/client/modules/builder-subscription.js +55 -0
  19. package/dist/client/modules/builder.d.ts +4 -0
  20. package/dist/client/modules/builder.events.types.d.ts +368 -0
  21. package/dist/client/modules/builder.events.types.js +1 -0
  22. package/dist/client/modules/builder.js +7 -0
  23. package/dist/client/modules/builder.types.d.ts +47 -0
  24. package/dist/client/modules/builder.types.js +1 -0
  25. package/dist/server/errors.d.ts +15 -0
  26. package/dist/server/errors.js +18 -0
  27. package/dist/server/errors.types.d.ts +4 -0
  28. package/dist/server/errors.types.js +1 -0
  29. package/dist/server/index.d.ts +6 -0
  30. package/dist/server/index.js +3 -0
  31. package/dist/server/modules/webhooks-signature.d.ts +11 -0
  32. package/dist/server/modules/webhooks-signature.js +65 -0
  33. package/dist/server/modules/webhooks.d.ts +3 -0
  34. package/dist/server/modules/webhooks.events.generated.d.ts +174 -0
  35. package/dist/server/modules/webhooks.events.generated.js +3 -0
  36. package/dist/server/modules/webhooks.js +77 -0
  37. package/dist/server/modules/webhooks.types.d.ts +102 -0
  38. package/dist/server/modules/webhooks.types.js +1 -0
  39. package/dist/server/server.d.ts +10 -0
  40. package/dist/server/server.js +23 -0
  41. package/dist/server/server.types.d.ts +10 -0
  42. package/dist/server/server.types.js +1 -0
  43. package/dist/shared/server-url.d.ts +2 -0
  44. package/dist/shared/server-url.js +8 -0
  45. package/package.json +63 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 base44
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # @base44/platform
2
+
3
+ ```sh
4
+ npm install @base44/platform
5
+ ```
6
+
7
+ This package implements the read-only white-label builder socket contract: a
8
+ partner's browser watches the builder chat of up to eight apps over the platform's
9
+ shared socket. It is independent of the app SDK (`@base44/sdk`).
10
+ The workspace must have white-label sockets enabled; this SDK does not open sessions.
11
+
12
+ ```ts
13
+ import { Base44PlatformClient } from "@base44/platform";
14
+
15
+ const client = new Base44PlatformClient({
16
+ serverUrl: "https://base44.app",
17
+ async getSessionToken() {
18
+ const response = await fetch("/api/builder-socket-session", { method: "POST" });
19
+ if (!response.ok) throw new Error("Session request failed");
20
+ return (await response.json()).session_token;
21
+ },
22
+ });
23
+ const builder = client.builder.init({
24
+ onError(error) { showConnectionError(error.code); },
25
+ });
26
+ const subscription = builder.subscribe(appId, {
27
+ onSnapshot(snapshot) { replaceAppState(snapshot); },
28
+ async onEvent(event) { await applyEvent(event); }, // awaited before the next event
29
+ onError(error) { showSubscriptionError(error.code); },
30
+ });
31
+ await builder.connect();
32
+ // On view teardown:
33
+ subscription.unsubscribe();
34
+ builder.close();
35
+ ```
36
+
37
+ ## Receiving webhooks (server)
38
+
39
+ `@base44/platform/server` verifies Base44 outbound webhooks and answers the endpoint
40
+ activation challenge. It uses WebCrypto only (Node 20+, Deno, Bun, Workers, edge runtimes) and
41
+ makes no network calls: pass your workspace's public keys (`whpk_…`) explicitly.
42
+
43
+ The simplest route is `createHandler`. It reads the raw body, verifies it, answers the
44
+ activation challenge and picks the status code: 400 for a delivery that fails verification,
45
+ 500 when your keys are misconfigured or a callback throws (Base44 retries), 204 otherwise,
46
+ including for event types you have no handler for.
47
+
48
+ ```ts
49
+ import { Base44PlatformServer } from "@base44/platform/server";
50
+
51
+ const platform = new Base44PlatformServer({
52
+ serverUrl: "https://base44.app",
53
+ apiKey: process.env.BASE44_API_KEY!,
54
+ webhooks: { publicKeys: process.env.BASE44_WEBHOOK_PUBLIC_KEYS },
55
+ });
56
+
57
+ export const POST = platform.webhooks.createHandler({
58
+ on: {
59
+ "app.deleted.v1": async event => forget(event.data.app_id), // event is narrowed per type
60
+ },
61
+ onEvent: event => log(event.type), // every event, before its handler
62
+ onSuccess: event => {}, // return a Response to replace the 204
63
+ onError: (error, event) => report(error), // return a Response to replace the 400/500
64
+ });
65
+ ```
66
+
67
+ For full control, verify and dispatch yourself:
68
+
69
+ ```ts
70
+ export async function POST(request: Request) {
71
+ const rawBody = await request.text(); // exactly as received
72
+ let event;
73
+ try {
74
+ event = await platform.webhooks.verifyEvent(rawBody, request.headers);
75
+ } catch (error) {
76
+ if (error instanceof WebhookVerificationError) return new Response(null, { status: 400 });
77
+ throw error; // missing/malformed keys: a 500, so Base44 retries
78
+ }
79
+ switch (event.type) {
80
+ case "webhook.test.v1": return Response.json({ challenge: event.data.challenge }); // activates the endpoint
81
+ case "app.deleted.v1": await forget(event.data.app_id); break;
82
+ }
83
+ return new Response(null, { status: 204 });
84
+ }
85
+ ```
86
+
87
+ Delivery is at least once with no ordering guarantee: deduplicate on `event.id`, and answer
88
+ 2xx to event types you do not handle. Event types are generated from the schemas published at
89
+ `https://base44.com/webhooks/schemas`; run `npm run generate:webhooks` to refresh them.
90
+
91
+ ## Sessions
92
+
93
+ `/api/builder-socket-session` is your backend's route, not an SDK endpoint. It opens a
94
+ socket session with its workspace key (`apps:watch` scope) through
95
+ `POST /api/service/socket-sessions` with `{ app_ids }`, and returns only the
96
+ `session_token` to the browser. Never pass the workspace key or any other server
97
+ credential to this client. Subscribe only to apps on the session's allowlist; your
98
+ backend adds or removes apps with `PUT`/`DELETE /api/service/socket-sessions/{session_id}/rooms/{app_id}`.
99
+
100
+ A session lasts one hour and is never extended. The token goes exclusively in
101
+ CONNECT `auth.session_token`, never a URL, and the SDK keeps it in memory only. It
102
+ is reused across automatic reconnects. `getSessionToken` is called when a builder
103
+ session first connects, and again only when the server rejects the cached token or
104
+ expires the session; the SDK then reconnects once with the new token. Retrieval has
105
+ a twenty-second timeout and reports `session_unavailable` on failure.
106
+
107
+ One live socket per session: a newer connection with the same token replaces the
108
+ older one, which stops with `session_replaced`. Open one session per page.
109
+
110
+ ## Lifecycle
111
+
112
+ `new Base44PlatformClient({ serverUrl, getSessionToken })` creates a lightweight module
113
+ container: no Socket.IO instance, timers, token request or network activity. Its
114
+ `builder` module follows the server SDK's module-factory pattern.
115
+
116
+ `client.builder.init({ onError })` synchronously creates an independent `BuilderSession`
117
+ without connecting. Repeated calls create separate sessions, each owning its own
118
+ socket, listeners, subscriptions and cleanup. Close the returned session when its
119
+ view is disposed; the root client and other sessions remain usable.
120
+
121
+ `builder.connect()` resolves on CONNECT, before any snapshot. The socket uses the
122
+ default namespace on `/ws/socket.io/`, WebSocket only, with a dedicated manager.
123
+ Transport reconnection uses five attempts, starting at one second and capped at ten
124
+ seconds with jitter; each attempt has a twenty-second timeout. A temporary server
125
+ refusal is retried the same way. After exhaustion, a refusal or `session_replaced`,
126
+ fix the cause and call `builder.connect()`.
127
+
128
+ `builder.subscribe(appId, options)` returns a handle with `appId`, `active` and
129
+ idempotent `unsubscribe()`. App IDs are 24 lowercase hexadecimal characters. One
130
+ subscription per app and at most eight subscriptions are allowed per builder session.
131
+ Subscriptions can be created before connecting. `builder.close()` is terminal for that
132
+ session; call `client.builder.init(...)` again to start another.
133
+
134
+ ## Snapshots and events
135
+
136
+ There is no replay cursor. Every join, including each rejoin after a reconnect, is
137
+ answered with a `Snapshot`: the app's current `status` and its last 50 public
138
+ messages. After a main-conversation rewrite (undo, restore, sync), the SDK delivers
139
+ `conversation.changed` and rejoins the room, so a fresh snapshot follows. Treat a
140
+ snapshot as the app's current state. The app's live events follow it, including
141
+ those that landed while it was read. It holds only the last 50 messages, so merge
142
+ by `id` if you keep longer history.
143
+
144
+ Delivery is serialized **per app**, snapshots included; a slow app does not block
145
+ others. Callbacks must settle; they cannot be forcibly cancelled. The client bounds
146
+ pending delivery to 1,000 items per app. Error observers are synchronous notifications;
147
+ their exceptions are isolated so they cannot interrupt another app's delivery.
148
+
149
+ ## Public shapes
150
+
151
+ - `PlatformClientOptions` (`client.types.ts`): `serverUrl`, `getSessionToken`.
152
+ - `BuilderModule` (`modules/builder.types.ts`): lazy `init(options)` factory.
153
+ - `BuilderInitOptions`: connection-level `onError` observer.
154
+ - `BuilderSession`: `connect()`, `subscribe(appId, options)`, `close()`.
155
+ - `SubscriptionOptions`: required `onSnapshot`, `onEvent` and `onError`.
156
+ - `PlatformSubscription`: read-only `appId`, `active`, and `unsubscribe()`.
157
+ - Event/message models live in `modules/builder.events.types.ts`; error codes in `errors.types.ts`.
158
+
159
+ Every exported shape and field has JSDoc. Run `npm run docs` for
160
+ validated reference pages in `docs`. `PlatformEvent` is a
161
+ **discriminated union**: switch on `event.type` to narrow `event.data`. Each delivery
162
+ contains `type`, `appId` and `data`. On the wire every event is `{room, data}`; the SDK
163
+ routes by `room` and validates the envelope, while payload schemas and private-field
164
+ filtering are the server's. Omitted keys mean unchanged and explicit null clears.
165
+ Events carrying `branch_id` belong to that branch; null or absent is main. Unknown
166
+ event names are not forwarded.
167
+
168
+ | Event | Data contract |
169
+ | --- | --- |
170
+ | `message.updated` | `MessageUpdated`: the whole `message`, optional `conversation_id`. Replace the message with the same `id`. |
171
+ | `message.removed` | `MessageRemoved`: `message_id`, optional `conversation_id`. Sent when a message is deleted or hidden; an unknown ID is a no-op. |
172
+ | `app.status_changed` | `AppStatusChanged`: `status` (`AppStatus`), null clears it. |
173
+ | `preview.reload_requested` | Reload the app preview. |
174
+ | `preview.navigation_requested` | `PreviewNavigationRequested`: `path`, and `force` to navigate even when the user moved away. |
175
+ | `queue.updated` | `QueueUpdated`: `items`, `is_paused`, optional `processed_item_id`. Replaces the entire queue. |
176
+ | `task.progressed` | `TaskProgressed`: optional `tool_call_id`, `message_id`, `event_type`, numeric `progress`. |
177
+ | `image.resolved` | `ImageResolved`: `placeholder_url`, `status` (`pending`, `completed`, `failed`), nullable `image_url`. Replace the placeholder wherever it appears. |
178
+ | `conversation.changed` | The conversation was rewritten. On main, a fresh snapshot follows. |
179
+ | `files.changed`, `branch.deleted`, `repository.changed`, `pull_request.changed` | Re-read signals with no content. |
180
+
181
+ `ChatMessage` has optional `id`, `role` (`user`/`assistant`), text/null `content`,
182
+ `tool_calls`, timestamp-only `metadata.created_date`, `checkpoint_id`, and scalar
183
+ `additional_message_params` (`client_creation_id`, `plan_mode`, `plan_approved`,
184
+ `skip_ai_response`, `system_message_type`). `ToolCall` has optional `id`, `name`,
185
+ `status`, `requires_user_input`, `auto_approved`, `mutation_applied` and
186
+ `waiting_on.kind` (`approval`/`choice`/`input`). Each builder tool and guard declares
187
+ which of its parts are public; a tool with no declaration shows only these fields.
188
+
189
+ | Field | Public contract |
190
+ | --- | --- |
191
+ | `arguments` | One of `ToolQuestionArguments`, `ToolSecretArguments`, `ToolPackageArguments`, `ToolPlanArguments`, `ToolPrdArguments` (`generate_prd`, the plan in plan mode) or `ToolMediaArguments`, narrowed by the tool's `name`. Absent for other tools and partial streaming arguments. |
192
+ | `display` | File activity has `file_paths` and optional `content_empty`; execution activity has `summary` and `writes_entities`; entity activity has `entity_name` and `record_count`. |
193
+ | `user_input` | `ToolQuestionInput` only, for clarifying-question answers (`selected_label` for single-select, `selected_labels` for multi-select). Secret-form values are never exposed. |
194
+ | `results` | Once the call is not waiting on a guard: `ToolMediaResult` for image generation, or a URL string for game images and backgrounds (the placeholder `image.resolved` replaces) and videos (the finished video). |
195
+ | `approval` | While a call waits on a safety guard: `ToolGuardApproval` with `guard`, and `reason` and `details` where the guard declares them (shell-command approvals carry only `guard`). |
196
+
197
+ In plan mode the message `content` beside a `generate_prd` call can be empty; render the
198
+ plan from its arguments. Approving a plan, answering a question and approving a
199
+ guarded call are HTTP actions of your backend. `Snapshot` contains `room`, `status`
200
+ (`AppStatus`: optional `state` of `ready`/`processing`/`error`, nullable
201
+ `last_updated_date`, and nullable `turn_id`, the user message that started the turn),
202
+ `messages`, and `queue` (`SnapshotQueue`: the main branch's `items` and `is_paused`;
203
+ absent from older servers). Dates remain wire strings. Structural filtering
204
+ does not promise redaction of generated prose or user content.
205
+
206
+ `PlatformSocketError` extends `Error` with `code` and optional `appId`. Original
207
+ exceptions, payloads and credentials are never attached.
208
+
209
+ | Error code | Action |
210
+ | --- | --- |
211
+ | `connection_denied` | The session was rejected even with a fresh token: the workspace, key or session is not usable. |
212
+ | `session_unavailable` | `getSessionToken` failed or timed out; no provider details are forwarded. |
213
+ | `session_replaced` | Another connection with the same session took over. Stop, or open a new session. |
214
+ | `connection_failed` | Transport failure or exhausted retries; call `connect()` after fixing connectivity. |
215
+ | `access_denied` | The join was refused: the app is not on the session's allowlist, or joins were rate limited. |
216
+ | `access_revoked` | The app left the session's allowlist; do not resubscribe. |
217
+ | `snapshot_unavailable` | Joined, but the snapshot failed. Live events still flow; the subscription stays active. |
218
+ | `subscription_limit` | Release a subscription before adding another. |
219
+ | `delivery_overflow` | 1,000 deliveries queued for the app; subscribe again for a fresh snapshot. |
220
+ | `protocol_error` | Invalid routing or envelope. Stop and investigate. |
221
+ | `handler_failed` | An application callback failed; fix it and subscribe again. |
222
+ | `client_closed` | The builder session was explicitly closed; initialize another session. |
223
+
224
+ No SDK dependency, package version or lockfile changes are required. Socket.IO
225
+ remains the existing SDK dependency. Chat commands and HTTP APIs are outside this entry point.
@@ -0,0 +1,9 @@
1
+ import type { PlatformClientOptions } from "./client.types.js";
2
+ import type { BuilderModule } from "./modules/builder.types.js";
3
+ /** Browser platform client. Construction creates no sockets, timers or network requests. */
4
+ export declare class Base44PlatformClient {
5
+ /** Lazy builder subscriptions with independent session lifecycles. */
6
+ readonly builder: BuilderModule;
7
+ /** Configure shared service/auth settings; each module initializes its own resources. */
8
+ constructor(options: PlatformClientOptions);
9
+ }
@@ -0,0 +1,9 @@
1
+ import { normalizeServerUrl } from "../shared/server-url.js";
2
+ import { createBuilder } from "./modules/builder.js";
3
+ /** Browser platform client. Construction creates no sockets, timers or network requests. */
4
+ export class Base44PlatformClient {
5
+ /** Configure shared service/auth settings; each module initializes its own resources. */
6
+ constructor(options) {
7
+ this.builder = Object.freeze(createBuilder({ ...options, serverUrl: normalizeServerUrl(options.serverUrl) }));
8
+ }
9
+ }
@@ -0,0 +1,12 @@
1
+ /** Shared configuration for browser platform modules. Never supply an API key. */
2
+ export interface PlatformClientOptions {
3
+ /** Origin of the platform service, e.g. https://base44.app. No path/query/credentials. */
4
+ serverUrl: string;
5
+ /**
6
+ * Return a socket-session token from your backend, which opens the session with its
7
+ * workspace key (`POST /api/service/socket-sessions`) for the apps this page watches.
8
+ * Called when a builder session first connects, and again only after the server
9
+ * rejects or expires the current session. Sessions last one hour.
10
+ */
11
+ getSessionToken: () => string | Promise<string>;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { PlatformSocketErrorCode } from "./errors.types.js";
2
+ /** Sanitized failure. Original session-token provider, handler and server exceptions are not retained. */
3
+ export declare class PlatformSocketError extends Error {
4
+ /** Stable machine-readable category. */
5
+ readonly code: PlatformSocketErrorCode;
6
+ /** Associated app, when the server identifies a valid room. */
7
+ readonly appId?: string;
8
+ /** Create a sanitized error with no credential-bearing cause or payload. */
9
+ constructor(code: PlatformSocketErrorCode, appId?: string);
10
+ }
@@ -0,0 +1,10 @@
1
+ /** Sanitized failure. Original session-token provider, handler and server exceptions are not retained. */
2
+ export class PlatformSocketError extends Error {
3
+ /** Create a sanitized error with no credential-bearing cause or payload. */
4
+ constructor(code, appId) {
5
+ super(`Platform socket: ${code}`);
6
+ this.name = "PlatformSocketError";
7
+ this.code = code;
8
+ this.appId = appId;
9
+ }
10
+ }
@@ -0,0 +1,2 @@
1
+ /** Server refusals and client transport/processing failures. */
2
+ export type PlatformSocketErrorCode = "connection_denied" | "connection_failed" | "session_unavailable" | "session_revoked" | "session_replaced" | "access_denied" | "access_revoked" | "snapshot_unavailable" | "subscription_limit" | "delivery_overflow" | "protocol_error" | "handler_failed" | "client_closed";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ /** Browser platform modules, separate from the runtime and server SDKs. */
2
+ export { Base44PlatformClient } from "./client.js";
3
+ export { PlatformSocketError } from "./errors.js";
4
+ export type { PlatformSocketErrorCode } from "./errors.types.js";
5
+ export type { PlatformClientOptions } from "./client.types.js";
6
+ export type { BuilderModule, BuilderInitOptions, BuilderSession, PlatformSubscription, SubscriptionOptions } from "./modules/builder.types.js";
7
+ export type { AppStatus, AppStatusChanged, BranchScoped, ChatMessage, ImageResolved, MessageParams, MessageRemoved, MessageUpdated, PlatformEvent, PlatformEventMap, PreviewNavigationRequested, QueueItem, QueueUpdated, Snapshot, SnapshotQueue, TaskProgressed, ToolArguments, ToolCall, ToolDisplay, ToolEntityMutationArguments, ToolGuardApproval, ToolMediaArguments, ToolMediaResult, ToolPackageArguments, ToolPackageOperation, ToolPlanArguments, ToolPlanUpdate, ToolPrdArguments, ToolQuestion, ToolQuestionAnswer, ToolQuestionArguments, ToolQuestionInput, ToolQuestionOption, ToolSecretArguments, ToolSecretField, } from "./modules/builder.events.types.js";
@@ -0,0 +1,3 @@
1
+ /** Browser platform modules, separate from the runtime and server SDKs. */
2
+ export { Base44PlatformClient } from "./client.js";
3
+ export { PlatformSocketError } from "./errors.js";
@@ -0,0 +1,15 @@
1
+ import type { PlatformEvent, PlatformEventMap, Snapshot } from "./builder.events.types.js";
2
+ export declare const eventNames: readonly ["message.updated", "message.removed", "app.status_changed", "preview.reload_requested", "preview.navigation_requested", "queue.updated", "task.progressed", "image.resolved", "conversation.changed", "files.changed", "branch.deleted", "repository.changed", "pull_request.changed"];
3
+ /** Notices that name an app room, by the error code each one reports. */
4
+ export declare const roomNotices: {
5
+ readonly "room.access_denied": "access_denied";
6
+ readonly "room.access_revoked": "access_revoked";
7
+ readonly "room.snapshot_unavailable": "snapshot_unavailable";
8
+ };
9
+ export declare const appPattern: RegExp;
10
+ export declare const roomFor: (appId: string) => string;
11
+ export declare function object(value: unknown): Record<string, unknown>;
12
+ export declare function appFromRoom(value: unknown): string | undefined;
13
+ /** Every event is `{room, data}`; payload schemas are the service's, so only the envelope is validated. */
14
+ export declare function decode(type: keyof PlatformEventMap, appId: string, raw: unknown): PlatformEvent;
15
+ export declare function decodeSnapshot(raw: unknown): Snapshot;
@@ -0,0 +1,34 @@
1
+ export const eventNames = [
2
+ "message.updated", "message.removed", "app.status_changed", "preview.reload_requested",
3
+ "preview.navigation_requested", "queue.updated", "task.progressed", "image.resolved",
4
+ "conversation.changed", "files.changed", "branch.deleted", "repository.changed", "pull_request.changed",
5
+ ];
6
+ /** Notices that name an app room, by the error code each one reports. */
7
+ export const roomNotices = {
8
+ "room.access_denied": "access_denied",
9
+ "room.access_revoked": "access_revoked",
10
+ "room.snapshot_unavailable": "snapshot_unavailable",
11
+ };
12
+ export const appPattern = /^[a-f0-9]{24}$/;
13
+ export const roomFor = (appId) => `/apps/${appId}`;
14
+ export function object(value) {
15
+ if (!value || typeof value !== "object" || Array.isArray(value))
16
+ throw new Error("Invalid frame");
17
+ return value;
18
+ }
19
+ export function appFromRoom(value) {
20
+ return typeof value === "string" && /^\/apps\/[a-f0-9]{24}$/.test(value) ? value.slice(6) : undefined;
21
+ }
22
+ /** Every event is `{room, data}`; payload schemas are the service's, so only the envelope is validated. */
23
+ export function decode(type, appId, raw) {
24
+ return { type, appId, data: object(object(raw).data) };
25
+ }
26
+ export function decodeSnapshot(raw) {
27
+ const frame = object(raw);
28
+ const data = object(frame.data);
29
+ if (!appFromRoom(frame.room) || !Array.isArray(data.messages) || (data.status !== null && typeof data.status !== "object")) {
30
+ throw new Error("Invalid snapshot");
31
+ }
32
+ const queue = data.queue !== null && typeof data.queue === "object" ? { queue: data.queue } : {};
33
+ return { room: frame.room, status: data.status, messages: data.messages, ...queue };
34
+ }
@@ -0,0 +1,42 @@
1
+ import type { PlatformClientOptions } from "../client.types.js";
2
+ import type { BuilderInitOptions, BuilderSession, PlatformSubscription, SubscriptionOptions } from "./builder.types.js";
3
+ /** @internal */
4
+ export declare class BuilderSocket implements BuilderSession {
5
+ private readonly socket;
6
+ private readonly subscriptions;
7
+ private readonly options;
8
+ private closed;
9
+ private token?;
10
+ private tokenFresh;
11
+ private ending?;
12
+ private denialRetries;
13
+ private retryTimer?;
14
+ private authAttempt;
15
+ private cancelAuth?;
16
+ private connecting?;
17
+ private resolveConnect?;
18
+ private rejectConnect?;
19
+ constructor(config: PlatformClientOptions, options: BuilderInitOptions);
20
+ /** Connect with the session token. Resolves on CONNECT, before snapshots arrive.
21
+ * Transport loss retries up to five times and rejoins active subscriptions.
22
+ * Call again after a failure or `session_replaced`; concurrent calls share one attempt.
23
+ */
24
+ connect(): Promise<void>;
25
+ /** Subscribe before or after connecting. One subscription per app, maximum eight, each on the
26
+ * session's allowlist. Snapshots and events are awaited in order per app; a failed callback,
27
+ * an invalid frame or a server refusal ends the subscription.
28
+ */
29
+ subscribe(appId: string, options: SubscriptionOptions): PlatformSubscription;
30
+ /** Stop delivery, cancel reconnection and release all listeners. Idempotent and terminal. */
31
+ close(): void;
32
+ private join;
33
+ private authenticate;
34
+ /** A connect the server refused. Transport errors are Socket.IO's to retry. */
35
+ private refused;
36
+ private retryDenied;
37
+ private clearConnecting;
38
+ private connectionError;
39
+ private sessionEnded;
40
+ private roomNotice;
41
+ private protocolError;
42
+ }