api-ape 2.2.3 → 3.0.0
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.
- package/README.md +91 -19
- package/client/browser.js +7 -7
- package/client/connectSocket.js +257 -22
- package/client/index.js +3 -3
- package/dist/ape.js +1 -1
- package/dist/ape.js.map +3 -3
- package/dist/api-ape.min.js +1 -1
- package/dist/api-ape.min.js.map +3 -3
- package/index.d.ts +229 -21
- package/index.js +15 -0
- package/package.json +2 -2
- package/server/README.md +338 -6
- package/server/adapters/README.md +275 -0
- package/server/adapters/firebase.js +172 -0
- package/server/adapters/index.js +144 -0
- package/server/adapters/mongo.js +161 -0
- package/server/adapters/postgres.js +177 -0
- package/server/adapters/redis.js +154 -0
- package/server/adapters/supabase.js +199 -0
- package/server/client.js +299 -0
- package/server/index.js +29 -6
- package/server/lib/broadcast.js +115 -49
- package/server/lib/bun.js +4 -4
- package/server/lib/fileTransfer.js +129 -0
- package/server/lib/longPolling.js +22 -13
- package/server/lib/main.js +40 -8
- package/server/lib/wiring.js +23 -19
- package/server/socket/receive.js +46 -0
- package/server/socket/send.js +7 -0
package/index.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export interface ControllerContext {
|
|
|
50
50
|
os: { name?: string; version?: string }
|
|
51
51
|
device: { type?: string; vendor?: string; model?: string }
|
|
52
52
|
}
|
|
53
|
-
/** Custom embedded values from
|
|
53
|
+
/** Custom embedded values from onConnect */
|
|
54
54
|
[key: string]: any
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -63,7 +63,7 @@ export type ControllerFunction<T = any, R = any> = (
|
|
|
63
63
|
) => R | Promise<R>
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* Send function provided to
|
|
66
|
+
* Send function provided to onConnect
|
|
67
67
|
*/
|
|
68
68
|
export type SendFunction = {
|
|
69
69
|
(type: string, data: any): void
|
|
@@ -76,7 +76,7 @@ export type SendFunction = {
|
|
|
76
76
|
export type AfterHook = (err: Error | null, result: any) => void
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
* Connection lifecycle hooks returned from
|
|
79
|
+
* Connection lifecycle hooks returned from onConnect
|
|
80
80
|
*/
|
|
81
81
|
export interface ConnectionHandlers {
|
|
82
82
|
/** Values to embed into controller context */
|
|
@@ -88,11 +88,11 @@ export interface ConnectionHandlers {
|
|
|
88
88
|
/** Called on error */
|
|
89
89
|
onError?: (errorString: string) => void
|
|
90
90
|
/** Called when client disconnects */
|
|
91
|
-
|
|
91
|
+
onDisconnect?: () => void
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
*
|
|
95
|
+
* onConnect callback signature
|
|
96
96
|
*/
|
|
97
97
|
export type OnConnectCallback = (
|
|
98
98
|
socket: ApeWebSocket,
|
|
@@ -107,36 +107,225 @@ export interface ApeServerOptions {
|
|
|
107
107
|
/** Directory containing controller files */
|
|
108
108
|
where: string
|
|
109
109
|
/** Connection lifecycle hook */
|
|
110
|
-
|
|
110
|
+
onConnect?: OnConnectCallback
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// =============================================================================
|
|
114
|
+
// 🌲 FOREST - DISTRIBUTED MESH TYPES
|
|
115
|
+
// =============================================================================
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Supported database client types for Forest adapters
|
|
119
|
+
*/
|
|
120
|
+
export type ForestDatabaseClient =
|
|
121
|
+
| RedisClient
|
|
122
|
+
| MongoClient
|
|
123
|
+
| PostgresPool
|
|
124
|
+
| SupabaseClient
|
|
125
|
+
| FirebaseDatabase
|
|
126
|
+
| ForestCustomAdapter
|
|
127
|
+
|
|
128
|
+
/** Redis client (node-redis or ioredis) */
|
|
129
|
+
export interface RedisClient {
|
|
130
|
+
duplicate(): RedisClient
|
|
131
|
+
publish(channel: string, message: string): Promise<number>
|
|
132
|
+
subscribe(channel: string): Promise<void>
|
|
133
|
+
on(event: string, handler: (...args: any[]) => void): void
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** MongoDB client */
|
|
137
|
+
export interface MongoClient {
|
|
138
|
+
db(name?: string): any
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** PostgreSQL pool (pg) */
|
|
142
|
+
export interface PostgresPool {
|
|
143
|
+
query(text: string, values?: any[]): Promise<any>
|
|
144
|
+
connect(): Promise<any>
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Supabase client */
|
|
148
|
+
export interface SupabaseClient {
|
|
149
|
+
from(table: string): any
|
|
150
|
+
channel(name: string): any
|
|
151
|
+
removeChannel(channel: any): Promise<void>
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Firebase Realtime Database */
|
|
155
|
+
export interface FirebaseDatabase {
|
|
156
|
+
ref(path: string): any
|
|
157
|
+
goOnline?(): void
|
|
158
|
+
app?: any
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Forest adapter instance interface
|
|
163
|
+
*/
|
|
164
|
+
export interface ForestAdapterInstance {
|
|
165
|
+
/** This server's unique ID */
|
|
166
|
+
readonly serverId: string
|
|
167
|
+
|
|
168
|
+
/** Join the distributed mesh */
|
|
169
|
+
join(serverId?: string): Promise<void>
|
|
170
|
+
|
|
171
|
+
/** Leave the mesh and cleanup */
|
|
172
|
+
leave(): Promise<void>
|
|
173
|
+
|
|
174
|
+
/** Client-to-server lookup operations */
|
|
175
|
+
lookup: {
|
|
176
|
+
/** Register a client on this server */
|
|
177
|
+
add(clientId: string): Promise<void>
|
|
178
|
+
/** Find which server owns a client */
|
|
179
|
+
read(clientId: string): Promise<string | null>
|
|
180
|
+
/** Remove a client mapping (must own it) */
|
|
181
|
+
remove(clientId: string): Promise<void>
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Inter-server channel operations */
|
|
185
|
+
channels: {
|
|
186
|
+
/** Push message to a server's channel (empty string = broadcast) */
|
|
187
|
+
push(serverId: string, message: any): Promise<void>
|
|
188
|
+
/** Subscribe to a server's channel, returns unsubscribe function */
|
|
189
|
+
pull(serverId: string, handler: (message: any, senderServerId: string) => void): Promise<() => Promise<void>>
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Custom adapter interface for implementing your own Forest adapter
|
|
195
|
+
*/
|
|
196
|
+
export interface ForestCustomAdapter {
|
|
197
|
+
join(serverId: string): Promise<void>
|
|
198
|
+
leave(): Promise<void>
|
|
199
|
+
lookup: {
|
|
200
|
+
add(clientId: string): Promise<void>
|
|
201
|
+
read(clientId: string): Promise<string | null>
|
|
202
|
+
remove(clientId: string): Promise<void>
|
|
203
|
+
}
|
|
204
|
+
channels: {
|
|
205
|
+
push(serverId: string, message: any): Promise<void>
|
|
206
|
+
pull(serverId: string, handler: (message: any, senderServerId: string) => void): Promise<() => Promise<void>>
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Options for joinVia()
|
|
212
|
+
*/
|
|
213
|
+
export interface ForestOptions {
|
|
214
|
+
/** Prefix for keys/tables (default: 'ape') */
|
|
215
|
+
namespace?: string
|
|
216
|
+
/** Custom server ID (default: auto-generated) */
|
|
217
|
+
serverId?: string
|
|
111
218
|
}
|
|
112
219
|
|
|
113
220
|
/**
|
|
114
221
|
* Initialize api-ape on a Node.js HTTP/HTTPS server
|
|
222
|
+
*
|
|
223
|
+
* V3 Breaking Change:
|
|
224
|
+
* Old: const ape = require('api-ape')
|
|
225
|
+
* New: const { ape } = require('api-ape')
|
|
115
226
|
*/
|
|
116
227
|
declare function ape(server: HttpServer, options: ApeServerOptions): void
|
|
117
228
|
|
|
118
229
|
declare namespace ape {
|
|
119
230
|
/** Broadcast to all connected clients */
|
|
120
231
|
export function broadcast(type: string, data: any, excludeClientId?: string): void
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Read-only Map of connected clients
|
|
235
|
+
* Each ClientWrapper provides: clientId, sessionId, embed, agent, sendTo(type, data)
|
|
236
|
+
*/
|
|
237
|
+
export const clients: ReadonlyMap<string, ClientWrapper>
|
|
238
|
+
|
|
239
|
+
// =========================================================================
|
|
240
|
+
// 🌲 FOREST - DISTRIBUTED MESH
|
|
241
|
+
// =========================================================================
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Join the distributed mesh for multi-server coordination.
|
|
245
|
+
* Pass any supported database client - APE auto-detects the type.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* // Redis
|
|
249
|
+
* ape.joinVia(redisClient);
|
|
250
|
+
*
|
|
251
|
+
* // With options
|
|
252
|
+
* ape.joinVia(mongoClient, { namespace: 'myapp', serverId: 'srv-1' });
|
|
253
|
+
*
|
|
254
|
+
* // Custom adapter
|
|
255
|
+
* ape.joinVia({ join, leave, lookup, channels });
|
|
256
|
+
*/
|
|
257
|
+
export function joinVia(client: ForestDatabaseClient, options?: ForestOptions): Promise<ForestAdapterInstance>
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Leave the distributed mesh gracefully.
|
|
261
|
+
* Removes all client mappings and unsubscribes from channels.
|
|
262
|
+
*/
|
|
263
|
+
export function leaveCluster(): Promise<void>
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Current Forest adapter instance (null if not joined)
|
|
267
|
+
*/
|
|
268
|
+
export const cluster: ForestAdapterInstance | null
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* This server's ID in the cluster (null if not joined)
|
|
272
|
+
*/
|
|
273
|
+
export const serverId: string | null
|
|
125
274
|
|
|
126
275
|
// Browser client methods (available when imported in browser context)
|
|
127
276
|
/** Subscribe to broadcasts from the server */
|
|
128
277
|
export function on<T = any>(type: string, handler: (message: { err?: Error; type: string; data: T }) => void): void
|
|
129
278
|
/** Subscribe to connection state changes. Returns unsubscribe function. */
|
|
130
279
|
export function onConnectionChange(handler: (state: 'offline' | 'walled' | 'disconnected' | 'connecting' | 'connected') => void): () => void
|
|
131
|
-
/**
|
|
132
|
-
export
|
|
280
|
+
/** Current transport type (read-only) */
|
|
281
|
+
export const transport: 'websocket' | 'polling' | null
|
|
133
282
|
|
|
134
283
|
/** Call any server function dynamically (browser only) */
|
|
135
284
|
export function message<T = any, R = any>(data?: T): Promise<R>
|
|
136
285
|
}
|
|
137
286
|
|
|
138
|
-
//
|
|
139
|
-
|
|
287
|
+
// =============================================================================
|
|
288
|
+
// SERVER-SIDE CLIENT (api - same interface as browser)
|
|
289
|
+
// =============================================================================
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Server-side client for connecting to other api-ape servers
|
|
293
|
+
* 100% identical interface to the browser client
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* import api from 'api-ape'
|
|
297
|
+
*
|
|
298
|
+
* // Configure connection (or set APE_SERVER env)
|
|
299
|
+
* api.connect('ws://other-server:3000/api/ape')
|
|
300
|
+
*
|
|
301
|
+
* // Same usage as browser
|
|
302
|
+
* const result = await api.hello('World')
|
|
303
|
+
* api.on('message', (data) => console.log(data))
|
|
304
|
+
*/
|
|
305
|
+
export interface ApeServerClient extends ApeSender {
|
|
306
|
+
/** Subscribe to broadcasts from the remote server */
|
|
307
|
+
on<T = any>(type: string, handler: MessageHandler<T>): void
|
|
308
|
+
on(handler: MessageHandler): void
|
|
309
|
+
/** Subscribe to connection state changes */
|
|
310
|
+
onConnectionChange(handler: (state: ConnectionState) => void): () => void
|
|
311
|
+
/** Connect to a server (or set APE_SERVER env) */
|
|
312
|
+
connect(url: string): void
|
|
313
|
+
/** Close the connection */
|
|
314
|
+
close(): void
|
|
315
|
+
/** Current transport type (read-only) */
|
|
316
|
+
readonly transport: 'websocket' | null
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* The api client - works identically on browser and server
|
|
321
|
+
*/
|
|
322
|
+
declare const api: ApeServerClient
|
|
323
|
+
|
|
324
|
+
// Named export for V3 (Server usage: const { ape } = require('api-ape'))
|
|
325
|
+
export { ape, api }
|
|
326
|
+
|
|
327
|
+
// Default export: api client (same interface on browser and server)
|
|
328
|
+
export default api
|
|
140
329
|
|
|
141
330
|
// =============================================================================
|
|
142
331
|
// BROWSER CLIENT (Default export in browser context)
|
|
@@ -161,8 +350,8 @@ export interface ApeBrowserClient extends ApeSender {
|
|
|
161
350
|
/** Subscribe to connection state changes. Returns unsubscribe function. */
|
|
162
351
|
onConnectionChange(handler: (state: ConnectionState) => void): () => void
|
|
163
352
|
|
|
164
|
-
/**
|
|
165
|
-
|
|
353
|
+
/** Current transport type (read-only) */
|
|
354
|
+
readonly transport: TransportType | null
|
|
166
355
|
}
|
|
167
356
|
|
|
168
357
|
// =============================================================================
|
|
@@ -221,11 +410,11 @@ export type SetOnReceiver = {
|
|
|
221
410
|
*/
|
|
222
411
|
export interface ApeClient {
|
|
223
412
|
sender: ApeSender
|
|
224
|
-
|
|
413
|
+
setOnReceiver: SetOnReceiver
|
|
225
414
|
/** Subscribe to connection state changes. Returns unsubscribe function. */
|
|
226
415
|
onConnectionChange: (handler: (state: ConnectionState) => void) => () => void
|
|
227
|
-
/**
|
|
228
|
-
|
|
416
|
+
/** Current transport type (read-only) */
|
|
417
|
+
readonly transport: TransportType | null
|
|
229
418
|
}
|
|
230
419
|
|
|
231
420
|
/**
|
|
@@ -256,5 +445,24 @@ export { connectSocket }
|
|
|
256
445
|
// =============================================================================
|
|
257
446
|
|
|
258
447
|
export declare const broadcast: (type: string, data: any) => void
|
|
259
|
-
export declare const
|
|
260
|
-
|
|
448
|
+
export declare const clients: ReadonlyMap<string, ClientWrapper>
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Client wrapper providing client info and sendTo function
|
|
452
|
+
*/
|
|
453
|
+
export interface ClientWrapper {
|
|
454
|
+
/** Unique client identifier */
|
|
455
|
+
readonly clientId: string
|
|
456
|
+
/** Session ID from cookie (set by outer framework) */
|
|
457
|
+
readonly sessionId: string | null
|
|
458
|
+
/** Embedded values from onConnect */
|
|
459
|
+
readonly embed: Record<string, any>
|
|
460
|
+
/** Parsed user-agent info */
|
|
461
|
+
readonly agent: {
|
|
462
|
+
browser: { name?: string; version?: string }
|
|
463
|
+
os: { name?: string; version?: string }
|
|
464
|
+
device: { type?: string; vendor?: string; model?: string }
|
|
465
|
+
}
|
|
466
|
+
/** Send a message to this specific client */
|
|
467
|
+
sendTo(type: string, data: any): void
|
|
468
|
+
}
|
package/index.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api-ape unified entry point
|
|
3
|
+
*
|
|
4
|
+
* V3 Server Usage:
|
|
5
|
+
* const api = require('api-ape') // Client factory (default)
|
|
6
|
+
* const { ape } = require('api-ape') // Server initializer (named)
|
|
7
|
+
* import api, { ape } from 'api-ape' // ESM both
|
|
8
|
+
*
|
|
9
|
+
* Browser Usage:
|
|
10
|
+
* import api from 'api-ape' // Auto-connecting client
|
|
11
|
+
*/
|
|
1
12
|
|
|
2
13
|
let apiApe;
|
|
3
14
|
|
|
4
15
|
if ('undefined' === typeof window
|
|
5
16
|
|| 'undefined' === typeof window.document) {
|
|
17
|
+
// Server environment - exports: api (default), ape, broadcast, clients, createClient
|
|
6
18
|
apiApe = require('./server');
|
|
7
19
|
} else {
|
|
20
|
+
// Browser environment - client module has its own exports
|
|
8
21
|
apiApe = require('./client');
|
|
9
22
|
}
|
|
10
23
|
|
|
11
24
|
module.exports = apiApe
|
|
25
|
+
|
|
26
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "api-ape",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Remote Procedure Events (RPE) - A lightweight WebSocket framework for building real-time APIs. Call server functions from the browser like local methods with automatic reconnection, HTTP streaming fallback, and extended JSON encoding.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"browser": "./client/index.js",
|
|
@@ -69,4 +69,4 @@
|
|
|
69
69
|
"esbuild": "^0.27.2",
|
|
70
70
|
"jest": "^29.3.1"
|
|
71
71
|
}
|
|
72
|
-
}
|
|
72
|
+
}
|