webspresso 0.0.89 → 0.0.91

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 (102) hide show
  1. package/README.md +450 -53
  2. package/bin/commands/service.js +193 -0
  3. package/bin/webspresso.js +2 -0
  4. package/core/auth/manager.js +36 -26
  5. package/core/auth/middleware.js +21 -2
  6. package/core/compression/compressible.js +51 -0
  7. package/core/compression/compression-stream.js +59 -0
  8. package/core/compression/index.js +25 -0
  9. package/core/compression/middleware.js +301 -0
  10. package/core/compression/negotiate.js +153 -0
  11. package/core/errors/base.js +31 -0
  12. package/core/errors/domain.js +193 -0
  13. package/core/errors/http.js +133 -0
  14. package/core/errors/index.js +77 -0
  15. package/core/errors/middleware.js +162 -0
  16. package/core/errors/normalize.js +171 -0
  17. package/core/orm/complexity.js +120 -0
  18. package/core/orm/events.js +64 -14
  19. package/core/orm/index.js +45 -10
  20. package/core/orm/model.js +10 -2
  21. package/core/orm/query-builder.js +30 -4
  22. package/core/orm/repository.js +57 -25
  23. package/core/orm/scopes.js +2 -1
  24. package/core/orm/transaction.js +39 -5
  25. package/core/orm/utils.js +6 -3
  26. package/core/queue/adapters/database.js +284 -0
  27. package/core/queue/adapters/memory.js +155 -0
  28. package/core/queue/adapters/redis.js +336 -0
  29. package/core/queue/index.js +21 -0
  30. package/core/queue/job.js +103 -0
  31. package/core/queue/manager.js +387 -0
  32. package/core/realtime/adapters/redis.js +431 -0
  33. package/core/realtime/adapters/socket-io.js +203 -0
  34. package/core/realtime/adapters/sse.js +210 -0
  35. package/core/realtime/adapters/websocket.js +337 -0
  36. package/core/realtime/auth-manager.js +140 -0
  37. package/core/realtime/core.js +359 -0
  38. package/core/realtime/emitter.js +98 -0
  39. package/core/realtime/errors.js +71 -0
  40. package/core/realtime/index.js +79 -0
  41. package/core/realtime/reconnect-manager.js +118 -0
  42. package/core/realtime/registry.js +243 -0
  43. package/core/shutdown/http-adapter.js +134 -0
  44. package/core/shutdown/index.js +12 -0
  45. package/core/shutdown/shutdown-manager.js +312 -0
  46. package/core/ssr/index.js +13 -0
  47. package/core/ssr/stream.js +174 -0
  48. package/index.d.ts +829 -52
  49. package/index.js +103 -0
  50. package/package.json +22 -2
  51. package/plugins/admin-panel/api.js +37 -4
  52. package/plugins/admin-panel/app.js +67 -17
  53. package/plugins/admin-panel/client/parts/01-state-api-breadcrumb.js +4 -1
  54. package/plugins/admin-panel/client/parts/03-pagination-intro.js +25 -18
  55. package/plugins/admin-panel/client/parts/04-field-renderers.js +102 -58
  56. package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +37 -23
  57. package/plugins/admin-panel/client/parts/08-record-list.js +27 -34
  58. package/plugins/admin-panel/client/parts/09-record-form.js +168 -19
  59. package/plugins/admin-panel/core/admin-module.js +2 -2
  60. package/plugins/admin-panel/core/api-extensions.js +149 -0
  61. package/plugins/admin-panel/field-renderers/rich-text.js +3 -2
  62. package/plugins/admin-panel/index.js +131 -18
  63. package/plugins/admin-panel/modules/admin-users.js +770 -0
  64. package/plugins/admin-panel/modules/custom-pages.js +144 -45
  65. package/plugins/admin-panel/modules/menu.js +85 -0
  66. package/plugins/admin-panel/modules/user-management.js +5 -2
  67. package/plugins/admin-panel/vendor/mithril.min.js +1 -0
  68. package/plugins/admin-panel/vendor/quill.min.js +7 -0
  69. package/plugins/admin-panel/vendor/quill.snow.min.css +945 -0
  70. package/plugins/admin-panel/vendor/tailwind.min.js +65 -0
  71. package/plugins/basic-auth/index.js +293 -0
  72. package/plugins/content/client/inline-edit.js +41 -17
  73. package/plugins/cors.js +12 -8
  74. package/plugins/csrf/index.js +8 -8
  75. package/plugins/data-exchange/export-xlsx.js +5 -1
  76. package/plugins/data-exchange/import.js +16 -2
  77. package/plugins/data-exchange/index.js +14 -0
  78. package/plugins/email/index.js +14 -1
  79. package/plugins/index.js +29 -0
  80. package/plugins/queue/index.js +108 -0
  81. package/plugins/realtime/index.js +150 -0
  82. package/plugins/recaptcha/index.js +11 -0
  83. package/plugins/redirect/index.js +9 -1
  84. package/plugins/rest-resources/index.js +280 -11
  85. package/plugins/upload/index.js +16 -0
  86. package/src/app-context.js +46 -6
  87. package/src/file-router.js +220 -28
  88. package/src/helpers.js +24 -5
  89. package/src/plugin-manager.js +134 -7
  90. package/src/server.js +470 -44
  91. package/src/services/builtins/auth.js +273 -0
  92. package/src/services/builtins/exchange.js +229 -0
  93. package/src/services/builtins/mail.js +134 -0
  94. package/src/services/builtins/media.js +196 -0
  95. package/src/services/builtins/system.js +221 -0
  96. package/src/services/discovery.js +140 -0
  97. package/src/services/executor.js +292 -0
  98. package/src/services/index.js +70 -0
  99. package/src/services/memoize.js +256 -0
  100. package/src/services/registry.js +288 -0
  101. package/src/services/validator.js +281 -0
  102. package/templates/skills/webspresso-usage/REFERENCE-framework.md +2 -2
@@ -0,0 +1,359 @@
1
+ /**
2
+ * Realtime Core Client
3
+ * @module core/realtime/core
4
+ */
5
+
6
+ const { EventEmitter } = require('./emitter');
7
+ const { AuthManager } = require('./auth-manager');
8
+ const { SubscriptionRegistry } = require('./registry');
9
+ const { ReconnectManager } = require('./reconnect-manager');
10
+ const { RealtimeError, normalizeRealtimeError } = require('./errors');
11
+
12
+ const DEFAULT_CAPABILITIES = Object.freeze({
13
+ send: true,
14
+ perform: true,
15
+ multiplexing: true,
16
+ serverEvents: true,
17
+ binary: false,
18
+ });
19
+
20
+ /**
21
+ * Check whether the current execution context is in a browser environment
22
+ * @returns {boolean}
23
+ */
24
+ function isBrowserEnvironment() {
25
+ return typeof window !== 'undefined' && typeof document !== 'undefined';
26
+ }
27
+
28
+ class RealtimeClient {
29
+ /**
30
+ * @param {Object} options
31
+ * @param {Object} options.adapter - Realtime transport adapter (e.g. websocket, sse, socketIo)
32
+ * @param {Object|boolean} [options.auth=false] - Auth strategy configuration
33
+ * @param {boolean} [options.autoConnect=true] - Auto-connect when browser is ready
34
+ * @param {Object} [options.reconnect] - Reconnection options
35
+ * @param {boolean} [options.isBrowser] - Force browser environment flag for testing
36
+ */
37
+ constructor(options = {}) {
38
+ if (!options.adapter || typeof options.adapter !== 'object') {
39
+ throw new RealtimeError('INVALID_ARGUMENT', 'RealtimeClient requires a valid adapter');
40
+ }
41
+
42
+ this.options = options;
43
+ this.adapter = options.adapter;
44
+ this.auth = new AuthManager(options.auth);
45
+ this.registry = new SubscriptionRegistry();
46
+ this.reconnectManager = new ReconnectManager(options.reconnect);
47
+ this.emitter = new EventEmitter();
48
+
49
+ this._autoConnect = options.autoConnect !== false;
50
+ this._isBrowserOverride = typeof options.isBrowser === 'boolean' ? options.isBrowser : null;
51
+ this._isConnecting = false;
52
+ this._isDestroyed = false;
53
+ this._hasConnectedBefore = false;
54
+
55
+ // Wire up adapter low-level events
56
+ this._wireAdapterEvents();
57
+
58
+ // Auto-connect if in browser environment
59
+ if (this._autoConnect && this.isBrowser()) {
60
+ Promise.resolve().then(() => {
61
+ if (!this._isDestroyed && !this.isConnected()) {
62
+ this.connect().catch(() => {});
63
+ }
64
+ });
65
+ }
66
+ }
67
+
68
+ isBrowser() {
69
+ if (this._isBrowserOverride !== null) {
70
+ return this._isBrowserOverride;
71
+ }
72
+ return isBrowserEnvironment();
73
+ }
74
+
75
+ get capabilities() {
76
+ if (this.adapter && this.adapter.capabilities && typeof this.adapter.capabilities === 'object') {
77
+ return { ...DEFAULT_CAPABILITIES, ...this.adapter.capabilities };
78
+ }
79
+ return { ...DEFAULT_CAPABILITIES };
80
+ }
81
+
82
+ isConnected() {
83
+ if (this._isDestroyed) return false;
84
+ if (this.adapter && typeof this.adapter.isConnected === 'function') {
85
+ return this.adapter.isConnected();
86
+ }
87
+ return false;
88
+ }
89
+
90
+ on(event, handler) {
91
+ return this.emitter.on(event, handler);
92
+ }
93
+
94
+ once(event, handler) {
95
+ return this.emitter.once(event, handler);
96
+ }
97
+
98
+ off(event, handler) {
99
+ this.emitter.off(event, handler);
100
+ }
101
+
102
+ async connect(overrideAuth = null) {
103
+ if (this._isDestroyed) {
104
+ throw new RealtimeError('DESTROYED', 'Cannot connect on a destroyed Realtime instance');
105
+ }
106
+
107
+ // SSR Guard: Never open network connections during server-side rendering
108
+ if (!this.isBrowser()) {
109
+ return;
110
+ }
111
+
112
+ if (this.isConnected() || this._isConnecting) {
113
+ return;
114
+ }
115
+
116
+ this._isConnecting = true;
117
+
118
+ try {
119
+ let authMetadata = overrideAuth;
120
+ if (!authMetadata) {
121
+ authMetadata = await this.auth.resolveAuthMetadata();
122
+ }
123
+
124
+ const connectionContext = {
125
+ auth: authMetadata,
126
+ client: this,
127
+ };
128
+
129
+ await this.adapter.connect(connectionContext);
130
+
131
+ this._isConnecting = false;
132
+ const isReconnection = this._hasConnectedBefore;
133
+ this._hasConnectedBefore = true;
134
+
135
+ this.reconnectManager.reset();
136
+
137
+ this.registry.restoreAll((sub) => {
138
+ return this._adapterSubscribe(sub);
139
+ });
140
+
141
+ if (isReconnection) {
142
+ this.emitter.emit('reconnected');
143
+ }
144
+ this.emitter.emit('connected');
145
+ } catch (err) {
146
+ this._isConnecting = false;
147
+ const error = normalizeRealtimeError(err, 'CONNECTION_FAILED', 'Realtime connection failed');
148
+
149
+ if (error.code === 'UNAUTHORIZED') {
150
+ this.auth.handleUnauthorized(error);
151
+ this.emitter.emit('unauthorized', error);
152
+ }
153
+
154
+ this.emitter.emit('error', error);
155
+ throw error;
156
+ }
157
+ }
158
+
159
+ disconnect() {
160
+ if (this._isDestroyed) return;
161
+
162
+ this.reconnectManager.reset();
163
+
164
+ if (this.adapter && typeof this.adapter.disconnect === 'function') {
165
+ try {
166
+ this.adapter.disconnect();
167
+ } catch (err) {
168
+ console.error('[realtime] Error during adapter disconnect:', err);
169
+ }
170
+ }
171
+
172
+ this.registry.notifyAllDisconnected();
173
+ this.emitter.emit('disconnected');
174
+ }
175
+
176
+ async reconnect() {
177
+ if (this._isDestroyed) {
178
+ throw new RealtimeError('DESTROYED', 'Cannot reconnect on a destroyed Realtime instance');
179
+ }
180
+
181
+ this.disconnect();
182
+ return this.connect();
183
+ }
184
+
185
+ async reauthenticate() {
186
+ if (this._isDestroyed) {
187
+ throw new RealtimeError('DESTROYED', 'Cannot reauthenticate on a destroyed Realtime instance');
188
+ }
189
+
190
+ if (!this.isBrowser()) return;
191
+
192
+ try {
193
+ this.disconnect();
194
+ const newAuth = await this.auth.refreshToken();
195
+ await this.connect(newAuth);
196
+ } catch (err) {
197
+ const error = normalizeRealtimeError(err, 'TOKEN_REFRESH_FAILED', 'Reauthentication failed');
198
+ this.emitter.emit('error', error);
199
+ throw error;
200
+ }
201
+ }
202
+
203
+ subscribe(identifier, params = {}, callbacks = {}) {
204
+ if (this._isDestroyed) {
205
+ throw new RealtimeError('DESTROYED', 'Cannot subscribe on a destroyed Realtime instance');
206
+ }
207
+
208
+ return this.registry.register({
209
+ identifier,
210
+ params,
211
+ callbacks,
212
+ onSend: (sub, data) => this._handleSubscriptionSend(sub, data),
213
+ onPerform: (sub, action, data) => this._handleSubscriptionPerform(sub, action, data),
214
+ onAdapterSubscribe: (sub) => {
215
+ if (this.isConnected()) {
216
+ return this._adapterSubscribe(sub);
217
+ }
218
+ return null;
219
+ },
220
+ onAdapterUnsubscribe: (sub) => {
221
+ if (this.adapter && typeof this.adapter.unsubscribe === 'function') {
222
+ try {
223
+ this.adapter.unsubscribe(sub.identifier, sub.params, sub);
224
+ } catch (err) {
225
+ console.error(`[realtime] Error unsubscribing "${sub.identifier}":`, err);
226
+ }
227
+ }
228
+ },
229
+ });
230
+ }
231
+
232
+ _adapterSubscribe(sub) {
233
+ if (!this.adapter || typeof this.adapter.subscribe !== 'function') {
234
+ return null;
235
+ }
236
+
237
+ const adapterCallbacks = {
238
+ connected: () => sub.notifyConnected(),
239
+ disconnected: () => sub.notifyDisconnected(),
240
+ rejected: (err) => {
241
+ const error = normalizeRealtimeError(err, 'SUBSCRIPTION_REJECTED', `Subscription "${sub.identifier}" rejected`);
242
+ sub.notifyRejected(error);
243
+ this.emitter.emit('error', error);
244
+ },
245
+ received: (data) => sub.notifyReceived(data),
246
+ };
247
+
248
+ try {
249
+ return this.adapter.subscribe(sub.identifier, sub.params, adapterCallbacks);
250
+ } catch (err) {
251
+ const error = normalizeRealtimeError(err, 'SUBSCRIPTION_REJECTED', `Failed to subscribe to "${sub.identifier}"`);
252
+ sub.notifyRejected(error);
253
+ this.emitter.emit('error', error);
254
+ return null;
255
+ }
256
+ }
257
+
258
+ _handleSubscriptionSend(sub, data) {
259
+ if (!this.capabilities.send) {
260
+ throw new RealtimeError('UNSUPPORTED_CAPABILITY', 'Active realtime adapter does not support send()');
261
+ }
262
+
263
+ if (this.adapter && typeof this.adapter.send === 'function') {
264
+ this.adapter.send(sub.identifier, data, sub);
265
+ } else if (sub._adapterSub && typeof sub._adapterSub.send === 'function') {
266
+ sub._adapterSub.send(data);
267
+ } else {
268
+ throw new RealtimeError('UNSUPPORTED_CAPABILITY', 'Adapter does not implement send');
269
+ }
270
+ }
271
+
272
+ _handleSubscriptionPerform(sub, action, data) {
273
+ if (this.capabilities.perform) {
274
+ if (this.adapter && typeof this.adapter.perform === 'function') {
275
+ this.adapter.perform(sub.identifier, action, data, sub);
276
+ return;
277
+ }
278
+
279
+ if (sub._adapterSub && typeof sub._adapterSub.perform === 'function') {
280
+ sub._adapterSub.perform(action, data);
281
+ return;
282
+ }
283
+ }
284
+
285
+ // If perform is unsupported but send is available, map to generic action command
286
+ if (this.capabilities.send) {
287
+ this._handleSubscriptionSend(sub, { action, data });
288
+ return;
289
+ }
290
+
291
+ throw new RealtimeError('UNSUPPORTED_CAPABILITY', 'Active realtime adapter does not support perform() or send()');
292
+ }
293
+
294
+ _wireAdapterEvents() {
295
+ if (!this.adapter || typeof this.adapter.on !== 'function') return;
296
+
297
+ this.adapter.on('disconnected', (reason) => {
298
+ this.registry.notifyAllDisconnected();
299
+ this.emitter.emit('disconnected', reason);
300
+
301
+ if (!this._isDestroyed && this.reconnectManager.canReconnect()) {
302
+ this.reconnectManager.schedule(
303
+ async () => {
304
+ await this.connect();
305
+ },
306
+ ({ attempt, delay, maxAttempts }) => {
307
+ this.emitter.emit('reconnecting', { attempt, delay, maxAttempts });
308
+ }
309
+ );
310
+ }
311
+ });
312
+
313
+ this.adapter.on('unauthorized', (err) => {
314
+ const error = normalizeRealtimeError(err, 'UNAUTHORIZED', 'Realtime connection unauthorized');
315
+ this.auth.handleUnauthorized(error);
316
+ this.emitter.emit('unauthorized', error);
317
+ });
318
+
319
+ this.adapter.on('error', (err) => {
320
+ const error = normalizeRealtimeError(err, 'CONNECTION_FAILED', 'Adapter error');
321
+ this.emitter.emit('error', error);
322
+ });
323
+ }
324
+
325
+ browserReady() {
326
+ if (this._isDestroyed) return;
327
+ if (this._autoConnect && !this.isConnected() && !this._isConnecting) {
328
+ this.connect().catch(() => {});
329
+ }
330
+ }
331
+
332
+ destroy() {
333
+ if (this._isDestroyed) return;
334
+ this._isDestroyed = true;
335
+
336
+ this.reconnectManager.cancel();
337
+ this.registry.clear();
338
+
339
+ if (this.adapter) {
340
+ try {
341
+ if (typeof this.adapter.disconnect === 'function') {
342
+ this.adapter.disconnect();
343
+ }
344
+ if (typeof this.adapter.destroy === 'function') {
345
+ this.adapter.destroy();
346
+ }
347
+ } catch (err) {
348
+ console.error('[realtime] Error destroying adapter:', err);
349
+ }
350
+ }
351
+
352
+ this.emitter.clear();
353
+ }
354
+ }
355
+
356
+ module.exports = {
357
+ RealtimeClient,
358
+ DEFAULT_CAPABILITIES,
359
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Lightweight, zero-dependency Event Emitter
3
+ * @module core/realtime/emitter
4
+ */
5
+
6
+ class EventEmitter {
7
+ constructor() {
8
+ /** @type {Map<string, Set<Function>>} */
9
+ this._listeners = new Map();
10
+ }
11
+
12
+ /**
13
+ * Subscribe to an event
14
+ *
15
+ * @param {string} event
16
+ * @param {Function} handler
17
+ * @returns {() => void} Unsubscribe function
18
+ */
19
+ on(event, handler) {
20
+ if (typeof handler !== 'function') {
21
+ throw new TypeError(`Event handler for "${event}" must be a function`);
22
+ }
23
+ if (!this._listeners.has(event)) {
24
+ this._listeners.set(event, new Set());
25
+ }
26
+ this._listeners.get(event).add(handler);
27
+
28
+ return () => this.off(event, handler);
29
+ }
30
+
31
+ /**
32
+ * Subscribe to an event once
33
+ *
34
+ * @param {string} event
35
+ * @param {Function} handler
36
+ * @returns {() => void} Unsubscribe function
37
+ */
38
+ once(event, handler) {
39
+ if (typeof handler !== 'function') {
40
+ throw new TypeError(`Event handler for "${event}" must be a function`);
41
+ }
42
+ const wrapper = (...args) => {
43
+ this.off(event, wrapper);
44
+ handler(...args);
45
+ };
46
+ return this.on(event, wrapper);
47
+ }
48
+
49
+ /**
50
+ * Unsubscribe from an event
51
+ *
52
+ * @param {string} event
53
+ * @param {Function} handler
54
+ */
55
+ off(event, handler) {
56
+ const set = this._listeners.get(event);
57
+ if (set) {
58
+ set.delete(handler);
59
+ if (set.size === 0) {
60
+ this._listeners.delete(event);
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Emit an event to all subscribers
67
+ *
68
+ * @param {string} event
69
+ * @param {...any} args
70
+ */
71
+ emit(event, ...args) {
72
+ const set = this._listeners.get(event);
73
+ if (!set || set.size === 0) return;
74
+
75
+ // Iterate over shallow copy to allow listeners to modify set during execution
76
+ const handlers = Array.from(set);
77
+ for (const handler of handlers) {
78
+ try {
79
+ handler(...args);
80
+ } catch (err) {
81
+ if (event !== 'error' && this._listeners.has('error')) {
82
+ this.emit('error', err);
83
+ } else {
84
+ console.error(`[realtime] Error in event listener for "${event}":`, err);
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Clear all registered listeners
92
+ */
93
+ clear() {
94
+ this._listeners.clear();
95
+ }
96
+ }
97
+
98
+ module.exports = { EventEmitter };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Realtime error classes & normalization utilities
3
+ * @module core/realtime/errors
4
+ */
5
+
6
+ /**
7
+ * Strips tokens, secrets, and sensitive query parameters from strings/URLs
8
+ *
9
+ * @param {string} input
10
+ * @returns {string}
11
+ */
12
+ function sanitizeErrorMessage(input) {
13
+ if (typeof input !== 'string') return '';
14
+ return input
15
+ .replace(/([?&](?:token|access_token|secret|key|auth|password|apiKey)=)[^&#\s]+/gi, '$1***')
16
+ .replace(/(Bearer\s+)[A-Za-z0-9-_=.]+/gi, '$1***')
17
+ .replace(/(Basic\s+)[A-Za-z0-9-_=.]+/gi, '$1***');
18
+ }
19
+
20
+ /**
21
+ * Standard Realtime Error
22
+ */
23
+ class RealtimeError extends Error {
24
+ /**
25
+ * @param {string} code - Standardized error code
26
+ * @param {string} message - Human-readable error description (sanitized)
27
+ * @param {Error|unknown} [cause] - Original root cause error
28
+ */
29
+ constructor(code, message, cause = null) {
30
+ const sanitizedMsg = sanitizeErrorMessage(message);
31
+ super(sanitizedMsg);
32
+ this.name = 'RealtimeError';
33
+ this.code = code;
34
+ if (cause) {
35
+ this.cause = cause instanceof Error ? cause : new Error(sanitizeErrorMessage(String(cause)));
36
+ }
37
+ }
38
+
39
+ toJSON() {
40
+ return {
41
+ code: this.code,
42
+ message: this.message,
43
+ cause: this.cause ? this.cause.message || String(this.cause) : null,
44
+ };
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Normalize any error into a standard RealtimeError
50
+ *
51
+ * @param {unknown} err
52
+ * @param {string} [defaultCode='REALTIME_ERROR']
53
+ * @param {string} [defaultMessage='An unexpected realtime error occurred']
54
+ * @returns {RealtimeError}
55
+ */
56
+ function normalizeRealtimeError(err, defaultCode = 'REALTIME_ERROR', defaultMessage = 'An unexpected realtime error occurred') {
57
+ if (err instanceof RealtimeError) {
58
+ return err;
59
+ }
60
+ if (err instanceof Error) {
61
+ const code = err.code && typeof err.code === 'string' ? err.code : defaultCode;
62
+ return new RealtimeError(code, err.message || defaultMessage, err);
63
+ }
64
+ return new RealtimeError(defaultCode, typeof err === 'string' ? err : defaultMessage, err);
65
+ }
66
+
67
+ module.exports = {
68
+ RealtimeError,
69
+ normalizeRealtimeError,
70
+ sanitizeErrorMessage,
71
+ };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Webspresso Realtime Core
3
+ * @module core/realtime
4
+ */
5
+
6
+ const { RealtimeClient, DEFAULT_CAPABILITIES } = require('./core');
7
+ const { RealtimeSubscription, SubscriptionRegistry, createSubscriptionKey } = require('./registry');
8
+ const { AuthManager } = require('./auth-manager');
9
+ const { ReconnectManager } = require('./reconnect-manager');
10
+ const { RealtimeError, normalizeRealtimeError, sanitizeErrorMessage } = require('./errors');
11
+ const { EventEmitter } = require('./emitter');
12
+
13
+ // Adapters
14
+ const { createWebSocketAdapter, websocket, WebSocketAdapter, resolveWebSocketUrl } = require('./adapters/websocket');
15
+ const { createSseAdapter, sse, SseAdapter, resolveSseUrl } = require('./adapters/sse');
16
+ const { createSocketIoAdapter, socketIo, SocketIoAdapter } = require('./adapters/socket-io');
17
+ const { createRedisAdapter, redis, RedisAdapter, InMemoryDistributedBus } = require('./adapters/redis');
18
+
19
+ /**
20
+ * Factory to create a RealtimeClient instance
21
+ *
22
+ * @param {Object} options
23
+ * @param {Object} options.adapter - Transport adapter (e.g. websocket, sse, socketIo, redis)
24
+ * @param {Object|boolean} [options.auth=false] - Auth strategy configuration
25
+ * @param {boolean} [options.autoConnect=true] - Auto-connect when browser is ready
26
+ * @param {Object} [options.reconnect] - Reconnection options
27
+ * @returns {RealtimeClient}
28
+ */
29
+ function createRealtime(options = {}) {
30
+ return new RealtimeClient(options);
31
+ }
32
+
33
+ /**
34
+ * Universal realtime helper
35
+ *
36
+ * @param {Object} options
37
+ * @returns {RealtimeClient}
38
+ */
39
+ function realtime(options = {}) {
40
+ const client = createRealtime(options);
41
+ return client;
42
+ }
43
+
44
+ module.exports = {
45
+ createRealtime,
46
+ realtime,
47
+ RealtimeClient,
48
+ RealtimeSubscription,
49
+ SubscriptionRegistry,
50
+ createSubscriptionKey,
51
+ AuthManager,
52
+ ReconnectManager,
53
+ RealtimeError,
54
+ normalizeRealtimeError,
55
+ sanitizeErrorMessage,
56
+ EventEmitter,
57
+ DEFAULT_CAPABILITIES,
58
+
59
+ // Adapters
60
+ websocket,
61
+ createWebSocketAdapter,
62
+ WebSocketAdapter,
63
+ resolveWebSocketUrl,
64
+
65
+ sse,
66
+ createSseAdapter,
67
+ SseAdapter,
68
+ resolveSseUrl,
69
+
70
+ socketIo,
71
+ createSocketIoAdapter,
72
+ SocketIoAdapter,
73
+
74
+ redis,
75
+ createRedisAdapter,
76
+ RedisAdapter,
77
+ InMemoryDistributedBus,
78
+ };
79
+