dolphin-server-modules 2.11.7 → 2.11.9

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.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * HTTP Adapters for Express and Fastify
3
+ * Maps native requests/responses to Dolphin's standard ctx structure.
4
+ */
5
+ /**
6
+ * Express Adapter
7
+ * Maps (req, res, next) to Dolphin's standard ctx structure.
8
+ */
9
+ export declare function toExpress(handler: Function): (req: any, res: any, next: any) => Promise<void>;
10
+ /**
11
+ * Fastify Adapter
12
+ * Maps (request, reply) to Dolphin's standard ctx structure.
13
+ */
14
+ export declare function toFastify(handler: Function): (request: any, reply: any) => Promise<void>;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * HTTP Adapters for Express and Fastify
3
+ * Maps native requests/responses to Dolphin's standard ctx structure.
4
+ */
5
+ /**
6
+ * Express Adapter
7
+ * Maps (req, res, next) to Dolphin's standard ctx structure.
8
+ */
9
+ export function toExpress(handler) {
10
+ return async (req, res, next) => {
11
+ let pendingStatus = 200;
12
+ const ctx = {
13
+ req,
14
+ res,
15
+ params: req.params || {},
16
+ query: req.query || {},
17
+ body: req.body || {},
18
+ state: res.locals || {},
19
+ json: (data, status) => {
20
+ const finalStatus = status !== undefined ? status : pendingStatus;
21
+ res.status(finalStatus).json(data);
22
+ return ctx;
23
+ },
24
+ text: (data, status) => {
25
+ const finalStatus = status !== undefined ? status : pendingStatus;
26
+ res.status(finalStatus).send(String(data));
27
+ return ctx;
28
+ },
29
+ html: (data, status) => {
30
+ const finalStatus = status !== undefined ? status : pendingStatus;
31
+ res.status(finalStatus).send(String(data));
32
+ return ctx;
33
+ },
34
+ status: (code) => {
35
+ pendingStatus = code;
36
+ return ctx;
37
+ },
38
+ setHeader: (name, value) => {
39
+ res.setHeader(name, value);
40
+ return ctx;
41
+ },
42
+ getHeader: (name) => {
43
+ return req.headers[name.toLowerCase()];
44
+ }
45
+ };
46
+ try {
47
+ // If the handler is a middleware and takes next
48
+ if (handler.length >= 2) {
49
+ await handler(ctx, next);
50
+ }
51
+ else {
52
+ const result = await handler(ctx);
53
+ // If the handler returned a response directly instead of calling ctx.json()
54
+ if (result !== undefined && result !== null && !res.headersSent) {
55
+ ctx.json(result);
56
+ }
57
+ }
58
+ }
59
+ catch (err) {
60
+ next(err);
61
+ }
62
+ };
63
+ }
64
+ /**
65
+ * Fastify Adapter
66
+ * Maps (request, reply) to Dolphin's standard ctx structure.
67
+ */
68
+ export function toFastify(handler) {
69
+ return async (request, reply) => {
70
+ let pendingStatus = 200;
71
+ const ctx = {
72
+ req: request.raw,
73
+ res: reply.raw,
74
+ params: request.params || {},
75
+ query: request.query || {},
76
+ body: request.body || {},
77
+ state: request.state || {},
78
+ json: (data, status) => {
79
+ const finalStatus = status !== undefined ? status : pendingStatus;
80
+ reply.status(finalStatus).send(data);
81
+ return ctx;
82
+ },
83
+ text: (data, status) => {
84
+ const finalStatus = status !== undefined ? status : pendingStatus;
85
+ reply.status(finalStatus).type('text/plain').send(String(data));
86
+ return ctx;
87
+ },
88
+ html: (data, status) => {
89
+ const finalStatus = status !== undefined ? status : pendingStatus;
90
+ reply.status(finalStatus).type('text/html').send(String(data));
91
+ return ctx;
92
+ },
93
+ status: (code) => {
94
+ pendingStatus = code;
95
+ return ctx;
96
+ },
97
+ setHeader: (name, value) => {
98
+ reply.header(name, value);
99
+ return ctx;
100
+ },
101
+ getHeader: (name) => {
102
+ return request.headers[name.toLowerCase()];
103
+ }
104
+ };
105
+ try {
106
+ const result = await handler(ctx);
107
+ if (result !== undefined && result !== null && !reply.sent) {
108
+ ctx.json(result);
109
+ }
110
+ }
111
+ catch (err) {
112
+ reply.send(err);
113
+ }
114
+ };
115
+ }
116
+ //# sourceMappingURL=adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.js","sourceRoot":"","sources":["../../src/utils/adapters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,OAAiB;IACzC,OAAO,KAAK,EAAE,GAAQ,EAAE,GAAQ,EAAE,IAAS,EAAE,EAAE;QAC7C,IAAI,aAAa,GAAG,GAAG,CAAC;QAExB,MAAM,GAAG,GAAQ;YACf,GAAG;YACH,GAAG;YACH,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;YACxB,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;YACtB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE;YACpB,KAAK,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;YAEvB,IAAI,EAAE,CAAC,IAAS,EAAE,MAAe,EAAE,EAAE;gBACnC,MAAM,WAAW,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;gBAClE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnC,OAAO,GAAG,CAAC;YACb,CAAC;YAED,IAAI,EAAE,CAAC,IAAS,EAAE,MAAe,EAAE,EAAE;gBACnC,MAAM,WAAW,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;gBAClE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC3C,OAAO,GAAG,CAAC;YACb,CAAC;YAED,IAAI,EAAE,CAAC,IAAS,EAAE,MAAe,EAAE,EAAE;gBACnC,MAAM,WAAW,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;gBAClE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC3C,OAAO,GAAG,CAAC;YACb,CAAC;YAED,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;gBACvB,aAAa,GAAG,IAAI,CAAC;gBACrB,OAAO,GAAG,CAAC;YACb,CAAC;YAED,SAAS,EAAE,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE;gBACzC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAC3B,OAAO,GAAG,CAAC;YACb,CAAC;YAED,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE;gBAC1B,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YACzC,CAAC;SACF,CAAC;QAEF,IAAI,CAAC;YACH,gDAAgD;YAChD,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBACxB,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;gBAClC,4EAA4E;gBAC5E,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;oBAChE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACnB,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,OAAiB;IACzC,OAAO,KAAK,EAAE,OAAY,EAAE,KAAU,EAAE,EAAE;QACxC,IAAI,aAAa,GAAG,GAAG,CAAC;QAExB,MAAM,GAAG,GAAQ;YACf,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;YAC5B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;YAC1B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;YAE1B,IAAI,EAAE,CAAC,IAAS,EAAE,MAAe,EAAE,EAAE;gBACnC,MAAM,WAAW,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;gBAClE,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACrC,OAAO,GAAG,CAAC;YACb,CAAC;YAED,IAAI,EAAE,CAAC,IAAS,EAAE,MAAe,EAAE,EAAE;gBACnC,MAAM,WAAW,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;gBAClE,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;gBAChE,OAAO,GAAG,CAAC;YACb,CAAC;YAED,IAAI,EAAE,CAAC,IAAS,EAAE,MAAe,EAAE,EAAE;gBACnC,MAAM,WAAW,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;gBAClE,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC/D,OAAO,GAAG,CAAC;YACb,CAAC;YAED,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;gBACvB,aAAa,GAAG,IAAI,CAAC;gBACrB,OAAO,GAAG,CAAC;YACb,CAAC;YAED,SAAS,EAAE,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE;gBACzC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAC1B,OAAO,GAAG,CAAC;YACb,CAAC;YAED,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE;gBAC1B,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YAC7C,CAAC;SACF,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;gBAC3D,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dolphin-server-modules",
3
- "version": "2.11.7",
3
+ "version": "2.11.9",
4
4
  "type": "module",
5
5
  "homepage": "https://github.com/Phuyalshankar/dolphin-server-modules#readme",
6
6
  "description": "Core utility modules for Auth, CRUD, and Controllers",
package/scripts/client.js CHANGED
@@ -52,6 +52,7 @@
52
52
  * @property {number} [chunkSize=65536] — file upload chunk size (bytes)
53
53
  * @property {number} [maxReconnect=5] — max WebSocket reconnect attempts
54
54
  * @property {boolean} [autoRefreshToken=true] — auto-refresh expired access token
55
+ * @property {boolean} [autoBroadcast=false] — auto-publish non-GET API requests to realtime
55
56
  */
56
57
 
57
58
  // ─── APIHandler ───────────────────────────────────────────────────────────────
@@ -168,6 +169,13 @@ class APIHandler {
168
169
  : await response.text();
169
170
 
170
171
  if (!response.ok) throw { status: response.status, data };
172
+
173
+ // Auto Realtime Broadcast (Dual-Execution)
174
+ if (this.client.options.autoBroadcast && method !== 'GET') {
175
+ const topic = path.startsWith('/') ? path.slice(1) : path;
176
+ this.client.publish(topic, { method, payload: body, result: data });
177
+ }
178
+
171
179
  return data;
172
180
 
173
181
  } catch (err) {
@@ -473,6 +481,7 @@ class DolphinClient {
473
481
  chunkSize: 65536, // 64 KB
474
482
  maxReconnect: 5,
475
483
  autoRefreshToken: true,
484
+ autoBroadcast: false,
476
485
  ...options,
477
486
  };
478
487
 
@@ -505,6 +514,14 @@ class DolphinClient {
505
514
  this._offlineQueue = [];
506
515
 
507
516
  this.reconnectAttempts = 0;
517
+
518
+ if (typeof window !== 'undefined') {
519
+ if (document.readyState === 'loading') {
520
+ document.addEventListener('DOMContentLoaded', () => this._initDOMBinding());
521
+ } else {
522
+ this._initDOMBinding();
523
+ }
524
+ }
508
525
  }
509
526
 
510
527
  /** Save or clear the access token */
@@ -822,6 +839,124 @@ class DolphinClient {
822
839
  * @param {function(FileMetadata): void} handler
823
840
  */
824
841
  offFileAvailable(handler) { this.fileHandlers.delete(handler); }
842
+
843
+ // ── DOM Binding (Hookless Realtime) ───────────────────────────────────────
844
+
845
+ /** @private */
846
+ _initDOMBinding() {
847
+ if (this._domInitialized) return;
848
+ this._domInitialized = true;
849
+
850
+ // 1. Listen for inputs
851
+ document.addEventListener('input', (e) => {
852
+ if (!e.target || !e.target.getAttribute) return;
853
+ const topic = e.target.getAttribute('data-rt-push');
854
+ if (topic) {
855
+ const payload = { name: e.target.name, value: e.target.value };
856
+ this.pubPush(topic, payload);
857
+ }
858
+ });
859
+
860
+ // 2. Listen for form submits (RT + API)
861
+ document.addEventListener('submit', async (e) => {
862
+ if (!e.target || !e.target.getAttribute) return;
863
+
864
+ const rtTopic = e.target.getAttribute('data-rt-submit');
865
+ const apiTarget = e.target.getAttribute('data-api-submit');
866
+
867
+ if (rtTopic || apiTarget) {
868
+ e.preventDefault();
869
+ const formData = new FormData(e.target);
870
+ const data = Object.fromEntries(formData.entries());
871
+
872
+ if (rtTopic) {
873
+ this.publish(rtTopic, data);
874
+ } else if (apiTarget) {
875
+ const parts = apiTarget.trim().split(' ');
876
+ const method = parts.length > 1 ? parts[0].toUpperCase() : 'POST';
877
+ const path = parts.length > 1 ? parts[1] : parts[0];
878
+ try {
879
+ const result = await this.api.request(method, path, data);
880
+ const resultBind = e.target.getAttribute('data-api-result');
881
+ if (resultBind) this._updateDOM(resultBind, result);
882
+ } catch (err) {
883
+ console.error('[Dolphin] API Submit Error:', err);
884
+ }
885
+ }
886
+ }
887
+ });
888
+
889
+ // 3. Listen for clicks (RT + API)
890
+ document.addEventListener('click', async (e) => {
891
+ if (!e.target || !e.target.closest) return;
892
+
893
+ const rtBtn = e.target.closest('[data-rt-click]');
894
+ const apiBtn = e.target.closest('[data-api-click]');
895
+
896
+ if (rtBtn) {
897
+ const topic = rtBtn.getAttribute('data-rt-click');
898
+ const actionData = rtBtn.getAttribute('data-rt-payload');
899
+ const payload = actionData ? JSON.parse(actionData) : {};
900
+ this.publish(topic, payload);
901
+ } else if (apiBtn) {
902
+ const apiTarget = apiBtn.getAttribute('data-api-click');
903
+ const actionData = apiBtn.getAttribute('data-api-payload');
904
+ const payload = actionData ? JSON.parse(actionData) : null;
905
+ const parts = apiTarget.trim().split(' ');
906
+ const method = parts.length > 1 ? parts[0].toUpperCase() : 'POST';
907
+ const path = parts.length > 1 ? parts[1] : parts[0];
908
+ try {
909
+ const result = await this.api.request(method, path, payload);
910
+ const resultBind = apiBtn.getAttribute('data-api-result');
911
+ if (resultBind) this._updateDOM(resultBind, result);
912
+ } catch (err) {
913
+ console.error('[Dolphin] API Click Error:', err);
914
+ }
915
+ }
916
+ });
917
+
918
+ // 4. Update DOM when RT data arrives
919
+ // Note: Subscribe to all topics ('#') to auto-update DOM bindings
920
+ this.subscribe('#', (payload, topic) => {
921
+ this._updateDOM(topic, payload);
922
+ });
923
+
924
+ // 5. Auto-fetch API GET bindings
925
+ this._scanAndFetchAPIBinds();
926
+ }
927
+
928
+ /** @private */
929
+ async _scanAndFetchAPIBinds() {
930
+ if (typeof document === 'undefined') return;
931
+ const elements = document.querySelectorAll('[data-api-get]');
932
+ for (const el of elements) {
933
+ const path = el.getAttribute('data-api-get');
934
+ if (!path) continue;
935
+ try {
936
+ const result = await this.api.get(path);
937
+ if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
938
+ el.value = typeof result === 'object' ? (result.value !== undefined ? result.value : '') : result;
939
+ } else {
940
+ el.innerHTML = typeof result === 'object' ? (result.html || result.text || JSON.stringify(result)) : result;
941
+ }
942
+ } catch(e) {
943
+ console.error('[Dolphin] API Get Error:', e);
944
+ }
945
+ }
946
+ }
947
+
948
+ /** @private */
949
+ _updateDOM(topic, payload) {
950
+ if (typeof document === 'undefined') return;
951
+ const elements = document.querySelectorAll(`[data-rt-bind="${topic}"]`);
952
+ elements.forEach(el => {
953
+ if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
954
+ el.value = typeof payload === 'object' ? (payload.value !== undefined ? payload.value : '') : payload;
955
+ } else {
956
+ el.innerHTML = typeof payload === 'object' ? (payload.html || payload.text || JSON.stringify(payload)) : payload;
957
+ }
958
+ });
959
+ }
825
960
  }
826
961
 
827
962
  // ─── Exports ──────────────────────────────────────────────────────────────────