@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
@@ -0,0 +1,368 @@
1
+ /** Activity a file, execution or entity tool declared public. */
2
+ export interface ToolDisplay {
3
+ /** Changed file paths for a file operation. Source bodies and diffs are never included. */
4
+ file_paths?: string[];
5
+ /** Whether a file write intentionally used empty content. */
6
+ content_empty?: boolean;
7
+ /** Reviewed execution activity summary. Commands and execution output are never included. */
8
+ summary?: string;
9
+ /** Whether a reviewed execution action changed entity data. */
10
+ writes_entities?: boolean;
11
+ /** Entity type affected by an entity operation. Records and query values are never included. */
12
+ entity_name?: string;
13
+ /** Number of records affected when supplied by the producer. */
14
+ record_count?: number;
15
+ }
16
+ /** A selectable answer to a builder question. */
17
+ export interface ToolQuestionOption {
18
+ /** Visible option label. */
19
+ label?: string;
20
+ }
21
+ /** A builder question. Image/HTML source and private design context are excluded. */
22
+ export interface ToolQuestion {
23
+ /** Visible question text. */
24
+ question?: string;
25
+ /** Question category. */
26
+ type?: string;
27
+ /** Optional visible supporting text. */
28
+ description?: string;
29
+ /** Whether more than one answer may be selected. */
30
+ multi_select?: boolean;
31
+ /** Plan section the question covers. */
32
+ covers?: string;
33
+ /** Selectable options; a bare string option is its label. */
34
+ options?: (ToolQuestionOption | string)[];
35
+ }
36
+ /** `ask_clarifying_questions` and `ask_plan_questions` arguments. */
37
+ export interface ToolQuestionArguments {
38
+ /** Questions presented to the user. */
39
+ questions?: ToolQuestion[];
40
+ }
41
+ /** A requested secret field. The secret value is never sent over the socket. */
42
+ export interface ToolSecretField {
43
+ /** Requested secret name. */
44
+ secretName?: string;
45
+ /** Optional explanation of where to obtain it. */
46
+ description?: string;
47
+ }
48
+ /** `set_secrets` arguments. */
49
+ export interface ToolSecretArguments {
50
+ /** Requested secret fields. */
51
+ secrets_schema?: ToolSecretField[];
52
+ }
53
+ /** A package operation. Versions and package-manager output are excluded. */
54
+ export interface ToolPackageOperation {
55
+ /** Package name. */
56
+ name?: string;
57
+ /** Requested package operation. */
58
+ action?: string;
59
+ }
60
+ /** `install_npm_package` arguments. */
61
+ export interface ToolPackageArguments {
62
+ /** Requested package operations. */
63
+ packages?: ToolPackageOperation[];
64
+ }
65
+ /** An add-only builder plan update. */
66
+ export interface ToolPlanUpdate {
67
+ /** Update action. */
68
+ action?: string;
69
+ /** Plan section key. */
70
+ section?: string;
71
+ /** Optional user-facing section label. */
72
+ section_label?: string;
73
+ /** Plan point. */
74
+ text?: string;
75
+ }
76
+ /** `update_plan` arguments. */
77
+ export interface ToolPlanArguments {
78
+ /** Plan updates in their emitted order. */
79
+ updates?: ToolPlanUpdate[];
80
+ /** Base plan sections the builder considers sufficiently specified. */
81
+ sections_with_enough?: string[];
82
+ }
83
+ /** `generate_prd` arguments: the user's own plan. In plan mode the message `content` beside it can be empty. */
84
+ export interface ToolPrdArguments {
85
+ /** App or feature name. */
86
+ app_name?: string;
87
+ /** What is being built and why. */
88
+ intent_and_goal?: string;
89
+ /** Who uses it and their roles. */
90
+ audience_and_roles?: string;
91
+ /** End-to-end user flows. */
92
+ core_flows?: string[];
93
+ /** Specific technical requirements. */
94
+ technical_requirements?: string;
95
+ /** Design preferences. */
96
+ design_preferences?: string;
97
+ /** Build or plan declaration, when the builder asks for one. */
98
+ prd_type?: string;
99
+ /** Store platform settled in planning, for e-commerce plans. */
100
+ store_platform?: string;
101
+ /** Engine settled in planning, for game plans. */
102
+ game_engine?: string;
103
+ }
104
+ /** Image, game-image and video generation arguments. */
105
+ export interface ToolMediaArguments {
106
+ /** Visible media label. */
107
+ label?: string;
108
+ /** Requested image or video aspect ratio. */
109
+ aspect_ratio?: string;
110
+ }
111
+ /** `update_entities` and `delete_entities` arguments; the entity is in `display.entity_name`. */
112
+ export interface ToolEntityMutationArguments {
113
+ /** The agent's summary of the change. Queries and record values are never included. */
114
+ summary?: string;
115
+ }
116
+ /** The public arguments of a tool that declares them; narrow by the tool call's `name`. */
117
+ export type ToolArguments = ToolQuestionArguments | ToolSecretArguments | ToolPackageArguments | ToolPlanArguments | ToolPrdArguments | ToolMediaArguments | ToolEntityMutationArguments;
118
+ /** Generated-media state. Raw generation prompts and IDs are never included. */
119
+ export interface ToolMediaResult {
120
+ /** Placeholder URL, used to associate an `image.resolved` event with this tool. */
121
+ placeholder_url?: string;
122
+ /** Current media-generation status. */
123
+ status?: "pending" | "completed" | "failed";
124
+ /** Generated asset URL, once available. */
125
+ image_url?: string | null;
126
+ }
127
+ /** An answer to a clarifying question. Secret-form input is never included. */
128
+ export interface ToolQuestionAnswer {
129
+ /** Zero-based question index. */
130
+ question_index?: number;
131
+ /** Label selected for a single-select question. */
132
+ selected_label?: string;
133
+ /** Labels selected for a multi-select question. */
134
+ selected_labels?: string[];
135
+ /** User-provided free-text answer. Structural filtering does not redact prose. */
136
+ custom_text?: string;
137
+ }
138
+ /** Clarifying-question answers. */
139
+ export interface ToolQuestionInput {
140
+ /** Answers supplied to the question card. */
141
+ answers?: ToolQuestionAnswer[];
142
+ }
143
+ /** Why a safety guard parked a call for approval. Only present while the call waits. */
144
+ export interface ToolGuardApproval {
145
+ /** Guard identifier, e.g. `exec_tool_send_email` or `bash_approval`. */
146
+ guard: string;
147
+ /** Human-readable reason, for guards whose reason never quotes input (not shell commands). */
148
+ reason?: string;
149
+ /** The details the guard declares public; commands and record data are never included. */
150
+ details?: {
151
+ /** Entity method the code calls, e.g. `delete`. */
152
+ method?: string;
153
+ /** The agent's summary of what the code does. */
154
+ summary?: string;
155
+ /** Entity whose access rules change. */
156
+ entity_name?: string;
157
+ /** How the access rules change. */
158
+ change_type?: string;
159
+ /** Access operations whose rules change. */
160
+ changed_ops?: string[];
161
+ /** Backend function under test. */
162
+ function_name?: string;
163
+ /** The guard could not verify the function's source. */
164
+ inspection_failed?: boolean;
165
+ };
166
+ }
167
+ /** Public progress of a builder tool call. Only what the tool and its guard declare public is present. */
168
+ export interface ToolCall {
169
+ /** Stable tool call identifier. */
170
+ id?: string;
171
+ /** Tool name. */
172
+ name?: string;
173
+ /** Current execution state. */
174
+ status?: "running" | "success" | "error" | "stopped" | "waiting_for_user_input";
175
+ /** Whether the tool needs a user response through the partner backend. */
176
+ requires_user_input?: boolean;
177
+ /** Whether the builder auto-approved the operation. */
178
+ auto_approved?: boolean;
179
+ /** Whether the mutation was applied. */
180
+ mutation_applied?: boolean | null;
181
+ /** Kind of response a parked call waits for. */
182
+ waiting_on?: {
183
+ /** Kind of response expected. */
184
+ kind?: "approval" | "choice" | "input";
185
+ };
186
+ /** The tool's public arguments; absent for tools that declare none and for partial streaming arguments. */
187
+ arguments?: ToolArguments;
188
+ /** What a file, execution or entity tool touched. */
189
+ display?: ToolDisplay;
190
+ /** Clarifying-question answers. */
191
+ user_input?: ToolQuestionInput;
192
+ /** Generated media, once the call is not waiting on a guard: the image tool's state, or a URL alone
193
+ * (a game asset's placeholder, which `image.resolved` replaces, or a finished video). */
194
+ results?: ToolMediaResult | string;
195
+ /** A parked call's guard, and its reason and details where the guard declares them. */
196
+ approval?: ToolGuardApproval;
197
+ }
198
+ /** Scalar parameters a chat UI needs while a turn runs. */
199
+ export interface MessageParams {
200
+ /** Identifier the sending client attached, to match its optimistic message. */
201
+ client_creation_id?: string;
202
+ /** The message was sent in plan mode. */
203
+ plan_mode?: boolean;
204
+ /** The message approves a plan. */
205
+ plan_approved?: boolean;
206
+ /** The message expects no AI reply. */
207
+ skip_ai_response?: boolean;
208
+ /** System message category. */
209
+ system_message_type?: string;
210
+ }
211
+ /** A public chat message. Replace the message with the same `id` as a whole. */
212
+ export interface ChatMessage {
213
+ /** Message identifier. */
214
+ id?: string;
215
+ /** Author category. System and hidden messages are never delivered. */
216
+ role?: "user" | "assistant";
217
+ /** Message text; structured content is withheld. Structural filtering is not prose redaction. */
218
+ content?: string | null;
219
+ /** Public tool progress. */
220
+ tool_calls?: ToolCall[] | null;
221
+ /** Message timestamp, without author identity. */
222
+ metadata?: {
223
+ /** ISO timestamp string. */
224
+ created_date?: string;
225
+ };
226
+ /** Checkpoint reference; mutations remain on the partner backend. */
227
+ checkpoint_id?: string | null;
228
+ /** Scalar message parameters. */
229
+ additional_message_params?: MessageParams;
230
+ }
231
+ /** Public builder state, without error diagnostics or billing context. */
232
+ export interface AppStatus {
233
+ /** Current builder state. */
234
+ state?: "ready" | "processing" | "error";
235
+ /** State timestamp. */
236
+ last_updated_date?: string | null;
237
+ /** The user message that started the turn, when a turn set this state. Quote it to Base44 support. */
238
+ turn_id?: string | null;
239
+ }
240
+ /** Branch scope carried by app events. Null or absent is the main branch. */
241
+ export interface BranchScoped {
242
+ /** Branch the change belongs to; null is main. */
243
+ branch_id?: string | null;
244
+ }
245
+ /** `message.updated`: a chat message was added or replaced. */
246
+ export interface MessageUpdated extends BranchScoped {
247
+ /** The whole message; replace the one with the same `id`. */
248
+ message: ChatMessage;
249
+ /** Conversation containing the message. */
250
+ conversation_id?: string | null;
251
+ }
252
+ /** `message.removed`: a message was removed or hidden. An `id` the viewer never saw is a no-op. */
253
+ export interface MessageRemoved extends BranchScoped {
254
+ /** Identifier of the message to remove. */
255
+ message_id: string;
256
+ /** Conversation that contained the message. */
257
+ conversation_id?: string | null;
258
+ }
259
+ /** `app.status_changed`: a turn started, finished or failed. */
260
+ export interface AppStatusChanged extends BranchScoped {
261
+ /** The new state; null clears it. */
262
+ status: AppStatus | null;
263
+ }
264
+ /** `preview.navigation_requested`: show this page of the app in the preview. */
265
+ export interface PreviewNavigationRequested extends BranchScoped {
266
+ /** App path to show. */
267
+ path: string;
268
+ /** Navigate even when the user has moved away from the page. */
269
+ force: boolean;
270
+ }
271
+ /** A queued builder request. */
272
+ export interface QueueItem {
273
+ /** Queue item identifier. */
274
+ id?: string;
275
+ /** User-authored request text. */
276
+ content?: string;
277
+ /** Creation timestamp. */
278
+ created_at?: string;
279
+ /** Branch the request belongs to. */
280
+ branch_id?: string | null;
281
+ }
282
+ /** `queue.updated`: the whole prompt queue, replacing the previous one. */
283
+ export interface QueueUpdated extends BranchScoped {
284
+ /** Pending items. */
285
+ items?: QueueItem[];
286
+ /** Whether queue processing is paused. */
287
+ is_paused?: boolean;
288
+ /** Identifier of the item just processed, when supplied. */
289
+ processed_item_id?: string | null;
290
+ }
291
+ /** `task.progressed`: progress of a long-running tool. */
292
+ export interface TaskProgressed extends BranchScoped {
293
+ /** Associated tool call. */
294
+ tool_call_id?: string | null;
295
+ /** Associated chat message. */
296
+ message_id?: string | null;
297
+ /** Task lifecycle event, e.g. `task_progress`. */
298
+ event_type?: string | null;
299
+ /** Numeric progress only; diagnostic text is withheld. */
300
+ progress?: {
301
+ /** Completed work units. */
302
+ current?: number | null;
303
+ /** Total work units, when known. */
304
+ total?: number | null;
305
+ /** Producer-supplied percentage. */
306
+ percentage?: number | null;
307
+ } | null;
308
+ }
309
+ /** `image.resolved`: a generated image finished or failed. Replace its `placeholder_url` wherever it appears. */
310
+ export type ImageResolved = ToolMediaResult;
311
+ /** Mapping of public event names to their payloads. */
312
+ export interface PlatformEventMap {
313
+ /** A chat message was added or replaced. */
314
+ "message.updated": MessageUpdated;
315
+ /** A chat message was removed or hidden. */
316
+ "message.removed": MessageRemoved;
317
+ /** The builder started, finished or failed a turn. */
318
+ "app.status_changed": AppStatusChanged;
319
+ /** Reload the app preview. */
320
+ "preview.reload_requested": BranchScoped;
321
+ /** Show a page of the app in the preview. */
322
+ "preview.navigation_requested": PreviewNavigationRequested;
323
+ /** The whole prompt queue. */
324
+ "queue.updated": QueueUpdated;
325
+ /** Progress of a long-running tool. */
326
+ "task.progressed": TaskProgressed;
327
+ /** A generated image finished or failed. */
328
+ "image.resolved": ImageResolved;
329
+ /** The conversation was rewritten (undo, restore, sync). On main, the SDK rejoins for a fresh snapshot. */
330
+ "conversation.changed": BranchScoped;
331
+ /** The app's files changed outside a chat turn. Re-read them. */
332
+ "files.changed": BranchScoped;
333
+ /** A branch was deleted. */
334
+ "branch.deleted": BranchScoped;
335
+ /** The imported app's repository changed. */
336
+ "repository.changed": BranchScoped;
337
+ /** The imported app's pull request changed. */
338
+ "pull_request.changed": BranchScoped;
339
+ }
340
+ /** Ordered delivery of one public event. */
341
+ export type PlatformEvent = {
342
+ [K in keyof PlatformEventMap]: {
343
+ /** Public event name; narrows the payload type. */
344
+ type: K;
345
+ /** Authorized app receiving this event. */
346
+ appId: string;
347
+ /** Event payload. Omitted keys mean unchanged; explicit null clears. */
348
+ data: PlatformEventMap[K];
349
+ };
350
+ }[keyof PlatformEventMap];
351
+ /** An app's current state: sent after every join and rejoin, and after a main-conversation rewrite. */
352
+ export interface Snapshot {
353
+ /** Canonical app room. */
354
+ room: string;
355
+ /** Current builder state. */
356
+ status: AppStatus | null;
357
+ /** The last 50 public messages, oldest first. */
358
+ messages: ChatMessage[];
359
+ /** The main branch's prompt queue. Replace it, as with `queue.updated`. Absent from older servers. */
360
+ queue?: SnapshotQueue;
361
+ }
362
+ /** The prompt queue in a snapshot. */
363
+ export interface SnapshotQueue {
364
+ /** Pending items. */
365
+ items: QueueItem[];
366
+ /** Whether queue processing is paused. */
367
+ is_paused: boolean;
368
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { BuilderSocket } from "./builder-socket.js";
2
+ /** @internal */
3
+ export function createBuilder(config) {
4
+ return {
5
+ init(options) { return new BuilderSocket(config, options); },
6
+ };
7
+ }
@@ -0,0 +1,47 @@
1
+ import type { PlatformEvent, Snapshot } from "./builder.events.types.js";
2
+ import type { PlatformSocketError } from "../errors.js";
3
+ /** Options for an independent builder socket session. */
4
+ export interface BuilderInitOptions {
5
+ /** Connection-level errors, sanitized to exclude tokens and server exception text. */
6
+ onError: (error: PlatformSocketError) => void;
7
+ }
8
+ /** Lazy builder module; no socket exists until init is called. */
9
+ export interface BuilderModule {
10
+ /** Create an independent session without connecting. Call connect on the returned session. */
11
+ init(options: BuilderInitOptions): BuilderSession;
12
+ }
13
+ /** One builder socket session. Owns its subscriptions and connection lifecycle. */
14
+ export interface BuilderSession {
15
+ /** Connect with the session token; resolves on CONNECT, before snapshots arrive.
16
+ * Transport loss retries five times and rejoins active subscriptions, each with a fresh
17
+ * snapshot. An expired or rejected session is replaced once through `getSessionToken`.
18
+ * Call again after a failure or `session_replaced`. Concurrent calls share one attempt.
19
+ */
20
+ connect(): Promise<void>;
21
+ /** Subscribe before or after connecting. One subscription per app, up to eight per session,
22
+ * and each app must be on the session's allowlist. Callbacks run serially per app.
23
+ */
24
+ subscribe(appId: string, options: SubscriptionOptions): PlatformSubscription;
25
+ /** Stop this session, its subscriptions and reconnection. Idempotent and terminal. */
26
+ close(): void;
27
+ }
28
+ /** One app subscription; at most eight may be active per builder session. */
29
+ export interface SubscriptionOptions {
30
+ /** Replace this app's state. Arrives after every join and rejoin, and after a main-conversation
31
+ * rewrite (undo, restore, sync). Live events follow it; it holds only the last 50 messages.
32
+ */
33
+ onSnapshot: (snapshot: Snapshot) => void | Promise<void>;
34
+ /** Apply each live event. Delivery is serial per app; a rejection ends this subscription. */
35
+ onEvent: (event: PlatformEvent) => void | Promise<void>;
36
+ /** Subscription errors. Only `snapshot_unavailable` leaves the subscription active. */
37
+ onError: (error: PlatformSocketError) => void;
38
+ }
39
+ /** One app subscription's lifetime. */
40
+ export interface PlatformSubscription {
41
+ /** App identifier. */
42
+ readonly appId: string;
43
+ /** True while subscribed; false after an error or explicit unsubscribe. */
44
+ readonly active: boolean;
45
+ /** Stop this app's delivery and release its subscription slot. Idempotent. */
46
+ unsubscribe(): void;
47
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import type { WebhookConfigurationErrorCode, WebhookVerificationErrorCode } from "./errors.types.js";
2
+ /** A webhook delivery that cannot be trusted. The message never includes the body or signature. */
3
+ export declare class WebhookVerificationError extends Error {
4
+ /** Stable machine-readable reason. */
5
+ readonly code: WebhookVerificationErrorCode;
6
+ /** Create an error carrying only the reason. */
7
+ constructor(code: WebhookVerificationErrorCode);
8
+ }
9
+ /** Webhook public keys are missing or malformed, so no delivery can be verified. */
10
+ export declare class WebhookConfigurationError extends Error {
11
+ /** Stable machine-readable reason. */
12
+ readonly code: WebhookConfigurationErrorCode;
13
+ /** Create an error describing the misconfiguration. */
14
+ constructor(code: WebhookConfigurationErrorCode, detail: string);
15
+ }
@@ -0,0 +1,18 @@
1
+ /** A webhook delivery that cannot be trusted. The message never includes the body or signature. */
2
+ export class WebhookVerificationError extends Error {
3
+ /** Create an error carrying only the reason. */
4
+ constructor(code) {
5
+ super(`Webhook verification failed: ${code}`);
6
+ this.name = "WebhookVerificationError";
7
+ this.code = code;
8
+ }
9
+ }
10
+ /** Webhook public keys are missing or malformed, so no delivery can be verified. */
11
+ export class WebhookConfigurationError extends Error {
12
+ /** Create an error describing the misconfiguration. */
13
+ constructor(code, detail) {
14
+ super(`Webhook configuration: ${detail}`);
15
+ this.name = "WebhookConfigurationError";
16
+ this.code = code;
17
+ }
18
+ }
@@ -0,0 +1,4 @@
1
+ /** Why a delivery was refused. Respond 400: the request is not a genuine, current Base44 event. */
2
+ export type WebhookVerificationErrorCode = "missing_headers" | "timestamp_outside_tolerance" | "no_signature_entries" | "signature_mismatch" | "invalid_payload";
3
+ /** Why verification could not run at all. This is your deployment, not the request: let it surface as a 500 so Base44 retries. */
4
+ export type WebhookConfigurationErrorCode = "missing_public_keys" | "invalid_public_key";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ /** Server platform modules, authenticated with a workspace API key. */
2
+ export { Base44PlatformServer } from "./server.js";
3
+ export { WebhookConfigurationError, WebhookVerificationError } from "./errors.js";
4
+ export type { WebhookConfigurationErrorCode, WebhookVerificationErrorCode } from "./errors.types.js";
5
+ export type { PlatformServerOptions } from "./server.types.js";
6
+ export type { AppCreatedV1Data, AppDeletedV1Data, AppPublishedV1Data, AppRestoredV1Data, AppUnpublishedV1Data, CloudEvent, CreateHandlerOptions, DeliveredWebhookEvent, VerifyEventOptions, WebhookEventHandlers, WebhookEvent, WebhookEventMap, WebhookEventType, WebhookHeaders, WebhookPublicKeys, WebhooksModule, WebhooksOptions, WebhookTestV1Data, } from "./modules/webhooks.types.js";
@@ -0,0 +1,3 @@
1
+ /** Server platform modules, authenticated with a workspace API key. */
2
+ export { Base44PlatformServer } from "./server.js";
3
+ export { WebhookConfigurationError, WebhookVerificationError } from "./errors.js";
@@ -0,0 +1,11 @@
1
+ import type { WebhookHeaders, WebhookPublicKeys } from "./webhooks.types.js";
2
+ /** Split the configured keys into raw 32-byte Ed25519 keys. A truncated paste throws: a wrong key reads as every event being forged. */
3
+ export declare function parsePublicKeys(keys: WebhookPublicKeys | undefined): Uint8Array[];
4
+ export declare function importPublicKey(raw: Uint8Array): Promise<CryptoKey>;
5
+ /** Read one header from a `Headers` or a Node-style object. */
6
+ export declare function readHeader(headers: WebhookHeaders, name: string, separator?: string): string | null;
7
+ /** The `v1a,<base64>` entries of a `webhook-signature` header, decoded. */
8
+ export declare function parseSignatures(header: string): Uint8Array[];
9
+ /** The signed bytes: `<id>.<timestamp>.<raw body>`. */
10
+ export declare function signedPayload(id: string, timestamp: string, body: string | Uint8Array): Uint8Array;
11
+ export declare function anySignatureVerifies(keys: CryptoKey[], signatures: Uint8Array[], payload: Uint8Array): Promise<boolean>;
@@ -0,0 +1,65 @@
1
+ import { WebhookConfigurationError } from "../errors.js";
2
+ const SIGNATURE_SCHEME = "v1a";
3
+ const encoder = new TextEncoder();
4
+ /** Standard base64 to bytes, or `null` when it is not valid base64. */
5
+ function decodeBase64(value) {
6
+ try {
7
+ const binary = atob(value);
8
+ return Uint8Array.from(binary, char => char.charCodeAt(0));
9
+ }
10
+ catch (_a) {
11
+ return null;
12
+ }
13
+ }
14
+ /** Split the configured keys into raw 32-byte Ed25519 keys. A truncated paste throws: a wrong key reads as every event being forged. */
15
+ export function parsePublicKeys(keys) {
16
+ const entries = (Array.isArray(keys) ? keys : (keys !== null && keys !== void 0 ? keys : "").split(/[\s,]+/)).map(key => key.trim()).filter(Boolean);
17
+ if (entries.length === 0)
18
+ throw new WebhookConfigurationError("missing_public_keys", "no public keys were provided");
19
+ return entries.map(entry => {
20
+ const raw = decodeBase64(entry.replace(/^whpk_/, ""));
21
+ if (!raw || raw.length !== 32) {
22
+ throw new WebhookConfigurationError("invalid_public_key", "a public key is not a base64-encoded 32-byte Ed25519 key");
23
+ }
24
+ return raw;
25
+ });
26
+ }
27
+ export function importPublicKey(raw) {
28
+ return crypto.subtle.importKey("raw", raw, "Ed25519", false, ["verify"]);
29
+ }
30
+ /** Read one header from a `Headers` or a Node-style object. */
31
+ export function readHeader(headers, name, separator = ", ") {
32
+ if (typeof headers.get === "function")
33
+ return headers.get(name);
34
+ for (const [key, value] of Object.entries(headers)) {
35
+ if (key.toLowerCase() === name && value !== undefined)
36
+ return Array.isArray(value) ? value.join(separator) : value;
37
+ }
38
+ return null;
39
+ }
40
+ /** The `v1a,<base64>` entries of a `webhook-signature` header, decoded. */
41
+ export function parseSignatures(header) {
42
+ return header
43
+ .split(" ")
44
+ .filter(entry => entry.startsWith(`${SIGNATURE_SCHEME},`))
45
+ .map(entry => decodeBase64(entry.slice(SIGNATURE_SCHEME.length + 1)))
46
+ .filter((signature) => signature !== null);
47
+ }
48
+ /** The signed bytes: `<id>.<timestamp>.<raw body>`. */
49
+ export function signedPayload(id, timestamp, body) {
50
+ const prefix = encoder.encode(`${id}.${timestamp}.`);
51
+ const bytes = typeof body === "string" ? encoder.encode(body) : body;
52
+ const payload = new Uint8Array(prefix.length + bytes.length);
53
+ payload.set(prefix);
54
+ payload.set(bytes, prefix.length);
55
+ return payload;
56
+ }
57
+ export async function anySignatureVerifies(keys, signatures, payload) {
58
+ for (const key of keys) {
59
+ for (const signature of signatures) {
60
+ if (await crypto.subtle.verify("Ed25519", key, signature, payload))
61
+ return true;
62
+ }
63
+ }
64
+ return false;
65
+ }
@@ -0,0 +1,3 @@
1
+ import type { WebhooksModule, WebhooksOptions } from "./webhooks.types.js";
2
+ /** Webhook receiving utilities. Verification is local: no network calls, no timers. */
3
+ export declare function createWebhooks(options?: WebhooksOptions): WebhooksModule;