@thewhateverapp/platform 0.7.25 → 0.9.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/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/realtime/index.d.ts +138 -0
- package/dist/client/realtime/index.d.ts.map +1 -0
- package/dist/client/realtime/index.js +438 -0
- package/dist/client/realtime/index.js.map +1 -0
- package/dist/client/realtime/types.d.ts +143 -0
- package/dist/client/realtime/types.d.ts.map +1 -0
- package/dist/client/realtime/types.js +7 -0
- package/dist/client/realtime/types.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/keyspace/index.d.ts +86 -0
- package/dist/keyspace/index.d.ts.map +1 -0
- package/dist/keyspace/index.js +929 -0
- package/dist/keyspace/index.js.map +1 -0
- package/dist/keyspace/types.d.ts +377 -0
- package/dist/keyspace/types.d.ts.map +1 -0
- package/dist/keyspace/types.js +8 -0
- package/dist/keyspace/types.js.map +1 -0
- package/dist/realtime/index.d.ts +97 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/index.js +247 -0
- package/dist/realtime/index.js.map +1 -0
- package/dist/realtime/types.d.ts +198 -0
- package/dist/realtime/types.d.ts.map +1 -0
- package/dist/realtime/types.js +8 -0
- package/dist/realtime/types.js.map +1 -0
- package/package.json +7 -2
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realtime WebSocket Pub/Sub API
|
|
3
|
+
*
|
|
4
|
+
* Provides real-time messaging capabilities for tiles using
|
|
5
|
+
* Cloudflare Durable Objects with WebSocket hibernation.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { getCloudflareContext } from '@opennextjs/cloudflare';
|
|
10
|
+
* import { configurePlatformEnv, getRealtime } from '@thewhateverapp/platform';
|
|
11
|
+
*
|
|
12
|
+
* export async function POST(req: NextRequest) {
|
|
13
|
+
* // Configure env once per request
|
|
14
|
+
* configurePlatformEnv(() => getCloudflareContext().env);
|
|
15
|
+
*
|
|
16
|
+
* const realtime = await getRealtime();
|
|
17
|
+
*
|
|
18
|
+
* // Publish to all subscribers
|
|
19
|
+
* await realtime.publish('counter', { count: newCount });
|
|
20
|
+
*
|
|
21
|
+
* return NextResponse.json({ success: true });
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import { getPlatformEnv, isPlatformEnvConfigured } from '../env';
|
|
26
|
+
/**
|
|
27
|
+
* Default base URL for HTTP-based realtime access
|
|
28
|
+
*/
|
|
29
|
+
const DEFAULT_REALTIME_URL = 'https://realtime.thewhatever.app';
|
|
30
|
+
/**
|
|
31
|
+
* Cloudflare Durable Object-backed Realtime Provider
|
|
32
|
+
*
|
|
33
|
+
* Communicates with the RealtimeChannel Durable Object via HTTP
|
|
34
|
+
* to publish messages to connected WebSocket clients.
|
|
35
|
+
*/
|
|
36
|
+
class CloudflareRealtime {
|
|
37
|
+
stub;
|
|
38
|
+
constructor(stub) {
|
|
39
|
+
this.stub = stub;
|
|
40
|
+
}
|
|
41
|
+
async publish(channel, data, _options) {
|
|
42
|
+
const response = await this.stub.fetch('http://internal/publish', {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify({ channel, data }),
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
51
|
+
throw new Error(`Failed to publish: ${error.error}`);
|
|
52
|
+
}
|
|
53
|
+
return (await response.json());
|
|
54
|
+
}
|
|
55
|
+
async getStats() {
|
|
56
|
+
const response = await this.stub.fetch('http://internal/stats');
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new Error('Failed to get realtime stats');
|
|
59
|
+
}
|
|
60
|
+
return (await response.json());
|
|
61
|
+
}
|
|
62
|
+
async isHealthy() {
|
|
63
|
+
try {
|
|
64
|
+
const response = await this.stub.fetch('http://internal/health');
|
|
65
|
+
return response.ok;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* HTTP-based Realtime Provider
|
|
74
|
+
*
|
|
75
|
+
* Used when service bindings are not available (e.g., external services, testing).
|
|
76
|
+
* Authenticates via Platform API key (JWT).
|
|
77
|
+
*/
|
|
78
|
+
class HttpRealtime {
|
|
79
|
+
baseUrl;
|
|
80
|
+
apiKey;
|
|
81
|
+
fetchFn;
|
|
82
|
+
constructor(config) {
|
|
83
|
+
this.baseUrl = config.baseUrl || DEFAULT_REALTIME_URL;
|
|
84
|
+
this.apiKey = config.apiKey;
|
|
85
|
+
this.fetchFn = config.fetch || globalThis.fetch;
|
|
86
|
+
}
|
|
87
|
+
async publish(channel, data, _options) {
|
|
88
|
+
const response = await this.fetchFn(`${this.baseUrl}/publish`, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers: {
|
|
91
|
+
'Content-Type': 'application/json',
|
|
92
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify({ channel, data }),
|
|
95
|
+
});
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
98
|
+
throw new Error(`Failed to publish: ${error.error}`);
|
|
99
|
+
}
|
|
100
|
+
return (await response.json());
|
|
101
|
+
}
|
|
102
|
+
async getStats() {
|
|
103
|
+
// Stats endpoint doesn't require auth, but we pass it anyway
|
|
104
|
+
const response = await this.fetchFn(`${this.baseUrl}/stats`, {
|
|
105
|
+
headers: {
|
|
106
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
throw new Error('Failed to get realtime stats');
|
|
111
|
+
}
|
|
112
|
+
return (await response.json());
|
|
113
|
+
}
|
|
114
|
+
async isHealthy() {
|
|
115
|
+
try {
|
|
116
|
+
const response = await this.fetchFn(`${this.baseUrl}/health`);
|
|
117
|
+
return response.ok;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create a Realtime instance using HTTP with API key authentication
|
|
126
|
+
*
|
|
127
|
+
* Use this when service bindings are not available (e.g., external services, testing).
|
|
128
|
+
* The API key must have 'realtime' or '*' permission.
|
|
129
|
+
*
|
|
130
|
+
* @param config - HTTP configuration with API key
|
|
131
|
+
* @returns Realtime provider instance
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* const realtime = createRealtimeHttp({
|
|
136
|
+
* apiKey: 'wtvr_jwt_eyJ...',
|
|
137
|
+
* baseUrl: 'https://realtime.thewhatever.app', // optional
|
|
138
|
+
* });
|
|
139
|
+
*
|
|
140
|
+
* await realtime.publish('counter', { count: 42 });
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export function createRealtimeHttp(config) {
|
|
144
|
+
if (!config.apiKey) {
|
|
145
|
+
throw new Error('API key is required for HTTP-based realtime access');
|
|
146
|
+
}
|
|
147
|
+
return new HttpRealtime(config);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get a Realtime instance for the current request
|
|
151
|
+
*
|
|
152
|
+
* @param reqOrEnv - Optional: object with env property, or env object directly.
|
|
153
|
+
* If not provided, uses the env configured via configurePlatformEnv().
|
|
154
|
+
* @returns Realtime provider instance
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* // Using configured env (recommended)
|
|
159
|
+
* configurePlatformEnv(() => getCloudflareContext().env);
|
|
160
|
+
* const realtime = await getRealtime();
|
|
161
|
+
*
|
|
162
|
+
* // Or pass env directly
|
|
163
|
+
* const realtime = await getRealtime({ env });
|
|
164
|
+
* const realtime = await getRealtime(env);
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export async function getRealtime(reqOrEnv) {
|
|
168
|
+
let env;
|
|
169
|
+
// If no argument provided, use the configured env provider
|
|
170
|
+
if (!reqOrEnv) {
|
|
171
|
+
if (!isPlatformEnvConfigured()) {
|
|
172
|
+
throw new Error('getRealtime() called without env and configurePlatformEnv() was not called. ' +
|
|
173
|
+
'Either pass env directly: getRealtime({ env }) or call configurePlatformEnv(() => getCloudflareContext().env) first.');
|
|
174
|
+
}
|
|
175
|
+
env = getPlatformEnv();
|
|
176
|
+
}
|
|
177
|
+
else if ('env' in reqOrEnv) {
|
|
178
|
+
env = reqOrEnv.env;
|
|
179
|
+
}
|
|
180
|
+
else if ('REALTIME' in reqOrEnv) {
|
|
181
|
+
// Direct env object
|
|
182
|
+
env = reqOrEnv;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
env = reqOrEnv.env;
|
|
186
|
+
}
|
|
187
|
+
if (!env) {
|
|
188
|
+
throw new Error('No environment found. Ensure you are running in edge runtime.');
|
|
189
|
+
}
|
|
190
|
+
// Check for REALTIME Durable Object binding
|
|
191
|
+
if (!env.REALTIME) {
|
|
192
|
+
throw new Error('No REALTIME Durable Object binding found. ' +
|
|
193
|
+
'Add a service binding to the realtime-do worker in your wrangler.jsonc:\n' +
|
|
194
|
+
' "services": [{ "binding": "REALTIME", "service": "realtime-do", "entrypoint": "RealtimeChannel" }]');
|
|
195
|
+
}
|
|
196
|
+
// Get APP_ID for channel isolation (one DO per app)
|
|
197
|
+
const appId = env.APP_ID || 'default';
|
|
198
|
+
// Get Durable Object ID from app ID (deterministic)
|
|
199
|
+
const id = env.REALTIME.idFromName(appId);
|
|
200
|
+
const stub = env.REALTIME.get(id);
|
|
201
|
+
return new CloudflareRealtime(stub);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Create a Realtime instance directly (for advanced usage)
|
|
205
|
+
*
|
|
206
|
+
* @param env - Environment with REALTIME binding
|
|
207
|
+
* @param appId - Optional app ID for isolation (defaults to env.APP_ID or 'default')
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* const realtime = createRealtime({ REALTIME: env.REALTIME }, 'my-app-id');
|
|
212
|
+
* await realtime.publish('channel', { data: 'value' });
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export function createRealtime(env, appId) {
|
|
216
|
+
if (!env.REALTIME) {
|
|
217
|
+
throw new Error('No REALTIME Durable Object binding found.');
|
|
218
|
+
}
|
|
219
|
+
const resolvedAppId = appId || env.APP_ID || 'default';
|
|
220
|
+
const id = env.REALTIME.idFromName(resolvedAppId);
|
|
221
|
+
const stub = env.REALTIME.get(id);
|
|
222
|
+
return new CloudflareRealtime(stub);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get the WebSocket URL for connecting to realtime from the client
|
|
226
|
+
*
|
|
227
|
+
* This is typically used by the client SDK's useRealtime hook.
|
|
228
|
+
*
|
|
229
|
+
* @param appId - App ID for channel isolation
|
|
230
|
+
* @param channels - Optional initial channels to subscribe to
|
|
231
|
+
* @param baseUrl - Optional base URL (defaults to production)
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```typescript
|
|
235
|
+
* const wsUrl = getRealtimeWebSocketUrl('my-app', ['counter', 'chat']);
|
|
236
|
+
* // Returns: wss://realtime.thewhatever.app/ws?appId=my-app&channels=counter,chat
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
export function getRealtimeWebSocketUrl(appId, channels, baseUrl = 'wss://realtime.thewhatever.app') {
|
|
240
|
+
const url = new URL('/ws', baseUrl);
|
|
241
|
+
url.searchParams.set('appId', appId);
|
|
242
|
+
if (channels && channels.length > 0) {
|
|
243
|
+
url.searchParams.set('channels', channels.join(','));
|
|
244
|
+
}
|
|
245
|
+
return url.toString();
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realtime WebSocket Pub/Sub Types
|
|
3
|
+
*
|
|
4
|
+
* Provides type definitions for the realtime messaging system
|
|
5
|
+
* powered by Cloudflare Durable Objects.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Message types for WebSocket communication
|
|
9
|
+
*/
|
|
10
|
+
export type RealtimeMessageType = 'subscribe' | 'unsubscribe' | 'publish' | 'ping' | 'pong' | 'data' | 'error' | 'connected' | 'subscribed' | 'unsubscribed';
|
|
11
|
+
/**
|
|
12
|
+
* Base message structure for realtime communication
|
|
13
|
+
*/
|
|
14
|
+
export interface RealtimeMessage {
|
|
15
|
+
type: RealtimeMessageType;
|
|
16
|
+
channel?: string;
|
|
17
|
+
channels?: string[];
|
|
18
|
+
data?: unknown;
|
|
19
|
+
timestamp?: number;
|
|
20
|
+
message?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Options for publishing messages
|
|
24
|
+
*/
|
|
25
|
+
export interface PublishOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Whether to wait for confirmation
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
waitForConfirm?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Result of a publish operation
|
|
34
|
+
*/
|
|
35
|
+
export interface PublishResult {
|
|
36
|
+
success: boolean;
|
|
37
|
+
channel: string;
|
|
38
|
+
recipients: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Environment bindings required for Realtime (service binding mode)
|
|
42
|
+
*/
|
|
43
|
+
export interface RealtimeEnv {
|
|
44
|
+
/**
|
|
45
|
+
* Durable Object namespace binding for RealtimeChannel
|
|
46
|
+
*/
|
|
47
|
+
REALTIME?: DurableObjectNamespace;
|
|
48
|
+
/**
|
|
49
|
+
* App ID for channel isolation
|
|
50
|
+
*/
|
|
51
|
+
APP_ID?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Configuration for HTTP-based Realtime access
|
|
55
|
+
* Used when service bindings are not available (e.g., external services)
|
|
56
|
+
*/
|
|
57
|
+
export interface RealtimeHttpConfig {
|
|
58
|
+
/**
|
|
59
|
+
* Platform API key for authentication
|
|
60
|
+
* Format: wtvr_jwt_<jwt>
|
|
61
|
+
*/
|
|
62
|
+
apiKey: string;
|
|
63
|
+
/**
|
|
64
|
+
* Base URL for the realtime service
|
|
65
|
+
* @default 'https://realtime.thewhatever.app'
|
|
66
|
+
*/
|
|
67
|
+
baseUrl?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Custom fetch implementation (for testing or custom environments)
|
|
70
|
+
*/
|
|
71
|
+
fetch?: typeof fetch;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Server-side Realtime Provider interface
|
|
75
|
+
*
|
|
76
|
+
* Used in API routes to publish messages to connected clients.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* import { getRealtime } from '@thewhateverapp/platform';
|
|
81
|
+
*
|
|
82
|
+
* export async function POST(req: Request) {
|
|
83
|
+
* const realtime = await getRealtime();
|
|
84
|
+
*
|
|
85
|
+
* // Publish to a channel
|
|
86
|
+
* await realtime.publish('counter', { count: 42 });
|
|
87
|
+
*
|
|
88
|
+
* return new Response('OK');
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export interface RealtimeProvider {
|
|
93
|
+
/**
|
|
94
|
+
* Publish a message to a channel
|
|
95
|
+
*
|
|
96
|
+
* All connected clients subscribed to this channel will receive the message.
|
|
97
|
+
*
|
|
98
|
+
* @param channel - Channel name to publish to
|
|
99
|
+
* @param data - Data to send (will be JSON serialized)
|
|
100
|
+
* @param options - Optional publish options
|
|
101
|
+
* @returns Result with recipient count
|
|
102
|
+
*/
|
|
103
|
+
publish(channel: string, data: unknown, options?: PublishOptions): Promise<PublishResult>;
|
|
104
|
+
/**
|
|
105
|
+
* Get statistics about connected clients
|
|
106
|
+
*
|
|
107
|
+
* @returns Stats including total connections and per-channel counts
|
|
108
|
+
*/
|
|
109
|
+
getStats(): Promise<RealtimeStats>;
|
|
110
|
+
/**
|
|
111
|
+
* Check if the realtime service is healthy
|
|
112
|
+
*
|
|
113
|
+
* @returns True if the service is responding
|
|
114
|
+
*/
|
|
115
|
+
isHealthy(): Promise<boolean>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Statistics about realtime connections
|
|
119
|
+
*/
|
|
120
|
+
export interface RealtimeStats {
|
|
121
|
+
totalConnections: number;
|
|
122
|
+
channels: Record<string, number>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Options for creating a realtime connection (client-side)
|
|
126
|
+
*/
|
|
127
|
+
export interface RealtimeConnectionOptions {
|
|
128
|
+
/**
|
|
129
|
+
* Channels to subscribe to immediately on connect
|
|
130
|
+
*/
|
|
131
|
+
channels?: string[];
|
|
132
|
+
/**
|
|
133
|
+
* Auto-reconnect on disconnect
|
|
134
|
+
* @default true
|
|
135
|
+
*/
|
|
136
|
+
autoReconnect?: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Reconnect delay in milliseconds
|
|
139
|
+
* @default 1000
|
|
140
|
+
*/
|
|
141
|
+
reconnectDelay?: number;
|
|
142
|
+
/**
|
|
143
|
+
* Maximum reconnect attempts (0 = unlimited)
|
|
144
|
+
* @default 10
|
|
145
|
+
*/
|
|
146
|
+
maxReconnectAttempts?: number;
|
|
147
|
+
/**
|
|
148
|
+
* Ping interval in milliseconds (0 = disabled)
|
|
149
|
+
* @default 30000
|
|
150
|
+
*/
|
|
151
|
+
pingInterval?: number;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Connection state for client-side realtime
|
|
155
|
+
*/
|
|
156
|
+
export type RealtimeConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
|
|
157
|
+
/**
|
|
158
|
+
* Client-side realtime connection interface
|
|
159
|
+
*/
|
|
160
|
+
export interface RealtimeConnection {
|
|
161
|
+
/**
|
|
162
|
+
* Current connection state
|
|
163
|
+
*/
|
|
164
|
+
readonly state: RealtimeConnectionState;
|
|
165
|
+
/**
|
|
166
|
+
* Currently subscribed channels
|
|
167
|
+
*/
|
|
168
|
+
readonly channels: readonly string[];
|
|
169
|
+
/**
|
|
170
|
+
* Subscribe to a channel
|
|
171
|
+
*/
|
|
172
|
+
subscribe(channel: string): void;
|
|
173
|
+
/**
|
|
174
|
+
* Unsubscribe from a channel
|
|
175
|
+
*/
|
|
176
|
+
unsubscribe(channel: string): void;
|
|
177
|
+
/**
|
|
178
|
+
* Publish a message (client-to-server-to-all)
|
|
179
|
+
*/
|
|
180
|
+
publish(channel: string, data: unknown): void;
|
|
181
|
+
/**
|
|
182
|
+
* Add a message listener for a channel
|
|
183
|
+
*/
|
|
184
|
+
on(channel: string, callback: (data: unknown) => void): () => void;
|
|
185
|
+
/**
|
|
186
|
+
* Add a listener for all messages
|
|
187
|
+
*/
|
|
188
|
+
onAny(callback: (channel: string, data: unknown) => void): () => void;
|
|
189
|
+
/**
|
|
190
|
+
* Add a connection state listener
|
|
191
|
+
*/
|
|
192
|
+
onStateChange(callback: (state: RealtimeConnectionState) => void): () => void;
|
|
193
|
+
/**
|
|
194
|
+
* Disconnect and cleanup
|
|
195
|
+
*/
|
|
196
|
+
disconnect(): void;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/realtime/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/realtime/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thewhateverapp/platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Universal SDK for The Whatever App platform services",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -50,15 +50,20 @@
|
|
|
50
50
|
"homepage": "https://thewhatever.app",
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@cloudflare/workers-types": "^4.20250402.0",
|
|
53
|
+
"@types/react": "^18.0.0",
|
|
53
54
|
"typescript": "^5.7.3"
|
|
54
55
|
},
|
|
55
56
|
"peerDependencies": {
|
|
56
57
|
"@cloudflare/workers-types": "^4.0.0",
|
|
57
|
-
"@opennextjs/cloudflare": "^1.0.0"
|
|
58
|
+
"@opennextjs/cloudflare": "^1.0.0",
|
|
59
|
+
"react": "^18.0.0"
|
|
58
60
|
},
|
|
59
61
|
"peerDependenciesMeta": {
|
|
60
62
|
"@opennextjs/cloudflare": {
|
|
61
63
|
"optional": true
|
|
64
|
+
},
|
|
65
|
+
"react": {
|
|
66
|
+
"optional": true
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
}
|