dolphin-server-modules 1.5.5 → 1.5.7
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/DOLPHIN_MASTER_GUIDE_NEPALI.md +491 -210
- package/README.md +197 -125
- package/dist/adapters/mongoose/index.d.ts +4 -1
- package/dist/adapters/mongoose/index.js +54 -6
- package/dist/adapters/mongoose/index.js.map +1 -1
- package/dist/adapters/mongoose/index.test.js +60 -16
- package/dist/adapters/mongoose/index.test.js.map +1 -1
- package/dist/adapters/mongoose/integration.test.d.ts +5 -0
- package/dist/adapters/mongoose/integration.test.js +252 -0
- package/dist/adapters/mongoose/integration.test.js.map +1 -0
- package/dist/curd/crud.test.js +58 -187
- package/dist/curd/crud.test.js.map +1 -1
- package/dist/demo-server.d.ts +1 -0
- package/dist/demo-server.js +221 -0
- package/dist/demo-server.js.map +1 -0
- package/dist/djson/djson.test.d.ts +1 -0
- package/dist/djson/djson.test.js +235 -0
- package/dist/djson/djson.test.js.map +1 -0
- package/dist/realtime/core.d.ts +143 -2
- package/dist/realtime/core.js +511 -115
- package/dist/realtime/core.js.map +1 -1
- package/dist/realtime/devicemanager.d.ts +0 -0
- package/dist/realtime/devicemanager.js +2 -0
- package/dist/realtime/devicemanager.js.map +1 -0
- package/dist/realtime/realtime.test.js +436 -49
- package/dist/realtime/realtime.test.js.map +1 -1
- package/dist/router/router.test.d.ts +1 -0
- package/dist/router/router.test.js +47 -0
- package/dist/router/router.test.js.map +1 -0
- package/dist/swagger/swagger.test.d.ts +1 -0
- package/dist/swagger/swagger.test.js +40 -0
- package/dist/swagger/swagger.test.js.map +1 -0
- package/package.json +72 -70
|
@@ -1,60 +1,84 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
> **Latest Version:** v1.4.7+ | **Updated:** 2026-04-03 | **License:** MIT
|
|
1
|
+
Dolphin Framework: Absolute Master Guide (100+ Pages Equivalent) 🐬🇳🇵
|
|
2
|
+
Latest Version: v2.0 | Updated: 2026-04-05 | License: MIT
|
|
4
3
|
|
|
5
4
|
यो डकुमेन्ट Dolphin Framework को आधिकारिक र विस्तृत गाइड हो। यसले तपाईँलाई एउटा साधारण कोड लेख्ने डेभलपरबाट "Framework Master" बनाउन मद्दत गर्नेछ।
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
6
|
+
विषय सूची (Table of Contents)
|
|
7
|
+
०. परिचय र दर्शन
|
|
8
|
+
|
|
9
|
+
१. सेटअप र वातावरण
|
|
10
|
+
|
|
11
|
+
२. कोर सर्भर इन्जिन
|
|
12
|
+
|
|
13
|
+
३. Unified Context (ctx) Deep Dive
|
|
14
|
+
|
|
15
|
+
४. एडभान्स्ड राउटिङ
|
|
16
|
+
|
|
17
|
+
५. मिडलवेयर आर्किटेक्चर
|
|
18
|
+
|
|
19
|
+
६. डेटाबेस एड्याप्टर
|
|
20
|
+
|
|
21
|
+
७. Auth मोड्युल मास्टरक्लास
|
|
22
|
+
|
|
23
|
+
८. अटोमेटेड CRUD इन्जिन
|
|
24
|
+
|
|
25
|
+
९. Zod भ्यालिडेसन र सुरक्षा
|
|
26
|
+
|
|
27
|
+
१०. रियल-वर्ल्ड प्रोजेक्ट: DolphinStore API
|
|
28
|
+
|
|
29
|
+
११. स्केलिङ र पर्फर्मेन्स
|
|
30
|
+
|
|
31
|
+
१२. टेस्टिङ र डेभप्स
|
|
32
|
+
|
|
33
|
+
१३. RealtimeCore v2.0: रियलटाइम पब/सब मास्टरक्लास (पूर्ण अपडेटेड)
|
|
34
|
+
|
|
35
|
+
१३.१ v2.0 का नयाँ फिचरहरू
|
|
36
|
+
|
|
37
|
+
१३.२ Device Management (डिभाइस व्यवस्थापन)
|
|
38
|
+
|
|
39
|
+
१३.३ High-Frequency Messaging (pubPush/subPull)
|
|
40
|
+
|
|
41
|
+
१३.४ File Transfer with Resume (pubFile/subFile)
|
|
42
|
+
|
|
43
|
+
१३.५ P2P Streaming
|
|
44
|
+
|
|
45
|
+
१३.६ Private Messaging
|
|
46
|
+
|
|
47
|
+
१३.७ Basic Pub/Sub (Retro-compatible)
|
|
48
|
+
|
|
49
|
+
१३.८ पूर्ण उदाहरण: हस्पिटल मोनिटरिङ सिस्टम
|
|
50
|
+
|
|
51
|
+
१४. इन्डिपेन्डेन्ट राउटिङ
|
|
52
|
+
|
|
53
|
+
१५. स्वतन्त्र अटो-स्वैगर जेनेरेसन
|
|
54
|
+
|
|
55
|
+
१६. API रेफरेन्स
|
|
56
|
+
|
|
57
|
+
०. परिचय र दर्शन (Introduction & Philosophy)
|
|
58
|
+
Dolphin किन जन्मियो?
|
|
59
|
+
ब्याकइन्ड डेभलपमेन्टको दुनियाँमा एक्सप्रेस (Express) सबैभन्दा लोकप्रिय छ। तर एक्सप्रेस पूरानो भइसक्यो। यसमा धेरै अनावश्यक वजन (Bloat) छ र यो मोडर्न एउटा (Modern) async/await सँग सधैँ राम्रोसँग काम गर्दैन।
|
|
60
|
+
|
|
61
|
+
Dolphin को जन्म तीनवटा मुख्य कारणले भएको हो:
|
|
62
|
+
|
|
63
|
+
Native Speed: कुनै पनि बाहिरी लाइब्रेरी बिना नेटिभ http मा चल्ने।
|
|
64
|
+
|
|
65
|
+
Context-First: req र res लाई एउटै 'Context' (ctx) मा मिलाएर कोडलाई सफा राख्ने।
|
|
66
|
+
|
|
67
|
+
Total Modularity: तपाईँले Auth चाहनुहुन्छ? Auth मोड्युल मात्र लोड गर्नुहोस्।
|
|
68
|
+
|
|
69
|
+
पर्फर्मेन्स बेन्चमार्क
|
|
70
|
+
Framework Requests Per Second (RPS) Latency (avg)
|
|
71
|
+
Express.js ~15,000 10ms
|
|
72
|
+
Fastify ~35,000 2ms
|
|
73
|
+
Dolphin (v2.0) ~45,000+ 1.5ms
|
|
74
|
+
१. सेटअप र वातावरण (Setup & Environment)
|
|
75
|
+
१.१ आवश्यक चिजहरू (Prerequisites)
|
|
76
|
+
Node.js: v18.x वा सोभन्दा माथि
|
|
77
|
+
|
|
78
|
+
TypeScript: Dolphin टाइप-सेफ छ, त्यसैले TS सिफारिस गरिन्छ।
|
|
79
|
+
|
|
80
|
+
१.२ प्रोजेक्ट सुरु गर्ने
|
|
81
|
+
bash
|
|
58
82
|
mkdir dolphin-master-app && cd dolphin-master-app
|
|
59
83
|
npm init -y
|
|
60
84
|
npm install dolphin-server-modules mongoose zod ioredis
|
|
@@ -107,7 +131,7 @@ ctx.status(code): HTTP स्टेटस कोड
|
|
|
107
131
|
|
|
108
132
|
ctx.header(key, value): रेस्पोन्स हेडर
|
|
109
133
|
|
|
110
|
-
३.२ अटो-JSON रेस्पोन्स [v1.4.7]
|
|
134
|
+
३.२ अटो-JSON रेस्पोन्स [v1.4.7+]
|
|
111
135
|
typescript
|
|
112
136
|
// पुरानो:
|
|
113
137
|
app.get('/old', (ctx) => { ctx.json({ ok: true }); });
|
|
@@ -253,76 +277,49 @@ test('GET /test', async () => {
|
|
|
253
277
|
const res = await request(app.server).get('/test');
|
|
254
278
|
expect(res.body.ok).toBe(true);
|
|
255
279
|
});
|
|
256
|
-
१३. RealtimeCore: रियलटाइम पब/सब मास्टरक्लास (
|
|
280
|
+
१३. RealtimeCore v2.0: रियलटाइम पब/सब मास्टरक्लास (पूर्ण अपडेटेड)
|
|
281
|
+
⚠️ महत्त्वपूर्ण: यो सेक्सन Dolphin Framework v2.0 को पूर्ण RealtimeCore अनुसार अपडेट गरिएको छ। तपाईंको Core मा pubPush, subPull, pubFile, subFile, Resume, P2P, Device Management जस्ता एडभान्स्ड फिचरहरू समावेश छन्।
|
|
282
|
+
|
|
257
283
|
RealtimeCore Dolphin को उच्च-प्रदर्शन युनिफाइड पब/सब बस हो। यो IoT डिभाइसहरू, वेबसकेटहरू, र माइक्रोसर्भिसहरू बीच रियलटाइम डाटा संचारको लागि डिजाइन गरिएको छ।
|
|
258
284
|
|
|
259
|
-
१३.१
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
285
|
+
१३.१ v2.0 का नयाँ फिचरहरू
|
|
286
|
+
फिचर विवरण
|
|
287
|
+
pubPush / subPull अति उच्च गतिको डाटाको लागि (IoT Sensors, Live Graphs) - No JSON.stringify, No Redis
|
|
288
|
+
pubFile / subFile ठूला फाइलहरू टुक्रा-टुक्रा (64KB chunks) मा पठाउने - Resume Support सहित
|
|
289
|
+
resumeFile पहिले रोकिएको ठाउँबाट फाइल डाउनलोड पुनः सुरु गर्ने
|
|
290
|
+
Device Management isOnline(), isReady(), sendTo(), kick(), broadcastToGroup()
|
|
291
|
+
Private Messaging privateSub(), privatePub() - सुरक्षित संचार
|
|
292
|
+
P2P Streaming पीयर-टु-पीयर डाटा सेयरिङ
|
|
293
|
+
Auto Cleanup ६० सेकेन्ड इन्याक्टिभ डिभाइस आफै हट्छ
|
|
294
|
+
High-Freq Buffers प्रति टपिक १०० वटा मात्र बफर (Memory efficient)
|
|
268
295
|
१३.२ स्थापना (Installation)
|
|
269
296
|
bash
|
|
270
297
|
npm install dolphin-server-modules ioredis
|
|
271
|
-
१३.३
|
|
298
|
+
१३.३ Device Management (डिभाइस व्यवस्थापन)
|
|
299
|
+
RealtimeCore v2.0 ले पूर्ण डिभाइस व्यवस्थापन क्षमता प्रदान गर्दछ:
|
|
300
|
+
|
|
272
301
|
typescript
|
|
273
302
|
import { RealtimeCore } from 'dolphin-server-modules/realtime';
|
|
274
303
|
|
|
275
|
-
// RealtimeCore को इन्स्ट्यान्स बनाउने
|
|
276
304
|
const rt = new RealtimeCore({
|
|
277
|
-
|
|
278
|
-
enableJSONCache: true
|
|
279
|
-
debug: true, // डिबग लग
|
|
280
|
-
useBinaryProtocol: false // बाइनरी प्रोटोकल प्रयोग
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
// टपिक सब्सक्राइब गर्ने (वाइल्डकार्ड + सपोर्ट)
|
|
284
|
-
rt.subscribe('sensors/temperature/+', (payload) => {
|
|
285
|
-
console.log(`तापक्रम डाटा: ${payload.value}°C`);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// टपिकमा पब्लिस गर्ने
|
|
289
|
-
rt.publish('sensors/temperature/room1', { value: 23.5, unit: 'celsius' });
|
|
290
|
-
|
|
291
|
-
// रिटेन्ड मेसेज (नयाँ सब्सक्राइबरले पाउने)
|
|
292
|
-
rt.publish('config/system', { mode: 'active' }, { retain: true, ttl: 3600000 });
|
|
293
|
-
१३.४ वाइल्डकार्ड म्याचिङ (Wildcard Matching)
|
|
294
|
-
RealtimeCore ले MQTT-शैली वाइल्डकार्डहरू सपोर्ट गर्छ:
|
|
295
|
-
|
|
296
|
-
typescript
|
|
297
|
-
// + (सिङ्गल लेभल) - एउटा सेग्मेन्ट मात्र
|
|
298
|
-
rt.subscribe('sensors/+/temperature', (data) => {
|
|
299
|
-
// म्याच: sensors/room1/temperature, sensors/room2/temperature
|
|
300
|
-
// म्याच गर्दैन: sensors/room1/floor2/temperature
|
|
305
|
+
debug: true,
|
|
306
|
+
enableJSONCache: true
|
|
301
307
|
});
|
|
302
308
|
|
|
303
|
-
//
|
|
304
|
-
rt.subscribe('sensors/#', (data) => {
|
|
305
|
-
// म्याच: sensors/temp, sensors/room1/humidity, sensors/building/floor/room/temp
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// स्ट्याटिक टपिक (पूर्ण म्याच)
|
|
309
|
-
rt.subscribe('device/online', (data) => {
|
|
310
|
-
// केवल device/online मात्र
|
|
311
|
-
});
|
|
312
|
-
१३.५ डिभाइस रजिस्ट्रेसन र सकेट ह्यान्डलिङ (Device Registration)
|
|
313
|
-
typescript
|
|
314
|
-
// WebSocket कनेक्सन ह्यान्डल गर्ने (उदाहरण: ws लाइब्रेरीसँग)
|
|
309
|
+
// WebSocket सर्भरसँग integrate गर्ने
|
|
315
310
|
import WebSocket from 'ws';
|
|
316
|
-
|
|
317
|
-
const wss = new WebSocket.Server({ port: 8081 });
|
|
311
|
+
const wss = new WebSocket.Server({ port: 8080 });
|
|
318
312
|
|
|
319
313
|
wss.on('connection', (ws, req) => {
|
|
320
314
|
const deviceId = req.headers['device-id'] as string || `device-${Date.now()}`;
|
|
321
315
|
|
|
322
|
-
// डिभाइस रजिस्टर गर्ने
|
|
323
|
-
rt.register(deviceId, ws, {
|
|
316
|
+
// डिभाइस रजिस्टर गर्ने (metadata सहित)
|
|
317
|
+
rt.register(deviceId, ws, {
|
|
318
|
+
type: 'sensor',
|
|
319
|
+
location: 'kathmandu',
|
|
320
|
+
group: 'temperature-sensors'
|
|
321
|
+
});
|
|
324
322
|
|
|
325
|
-
// मेसेज ह्यान्डल गर्ने
|
|
326
323
|
ws.on('message', async (data: Buffer) => {
|
|
327
324
|
await rt.handle(data, ws, deviceId);
|
|
328
325
|
});
|
|
@@ -332,33 +329,124 @@ wss.on('connection', (ws, req) => {
|
|
|
332
329
|
});
|
|
333
330
|
});
|
|
334
331
|
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
|
|
339
|
-
|
|
332
|
+
// डिभाइस अनलाइन छ कि छैन चेक गर्ने
|
|
333
|
+
console.log(rt.isOnline('device-123')); // true/false
|
|
334
|
+
console.log(rt.isReady('device-123')); // socket ready छ कि छैन
|
|
335
|
+
|
|
336
|
+
// डिभाइसलाई सिधै मेसेज पठाउने (बिना Pub/Sub)
|
|
337
|
+
rt.sendTo('device-123', { type: 'PING', message: 'Are you alive?' });
|
|
338
|
+
|
|
339
|
+
// खराब डिभाइसलाई किक गर्ने
|
|
340
|
+
rt.kick('bad-device', 'Unauthorized access detected');
|
|
341
|
+
|
|
342
|
+
// सबै अनलाइन डिभाइसको लिस्ट
|
|
343
|
+
const devices = rt.getOnlineDevices();
|
|
344
|
+
// Output: [{ id: 'device-123', lastSeen: 123456, group: 'temperature-sensors' }]
|
|
345
|
+
|
|
346
|
+
// कुनै विशेष ग्रुपलाई मात्र ब्रोडकास्ट
|
|
347
|
+
rt.broadcastToGroup('temperature-sensors', { command: 'read_temperature' });
|
|
348
|
+
|
|
349
|
+
// डिभाइसलाई पिंग गर्ने
|
|
350
|
+
rt.ping('device-123');
|
|
351
|
+
१३.४ High-Frequency Messaging (pubPush/subPull)
|
|
352
|
+
IoT Sensors, Live Graphs, Real-time Stock Data को लागि अति छिटो messaging:
|
|
353
|
+
|
|
354
|
+
typescript
|
|
355
|
+
// सेन्सरबाट हाई-फ्रिक्वेन्सी डाटा पठाउने (pubPush)
|
|
356
|
+
// यो method ले JSON.stringify, ACL, Redis सबै बाइपास गर्छ
|
|
357
|
+
rt.pubPush('sensors/temperature/room1', Buffer.from([0x1A, 0x2B, 0x3C, 0x4D]));
|
|
358
|
+
rt.pubPush('sensors/humidity/room1', { value: 65.5, unit: '%' }); // Auto JSON
|
|
359
|
+
|
|
360
|
+
// क्लाइन्टले डाटा मागेपछि मात्र पाउने (subPull) - Data Saving
|
|
361
|
+
// क्लाइन्टले माग्दा पछिल्लो 10 वटा डाटा पाउँछ
|
|
362
|
+
rt.subPull('device-123', 'sensors/temperature/room1', 10);
|
|
363
|
+
|
|
364
|
+
// pubPush डाटा अटोम्याटिक बफरमा सेभ हुन्छ
|
|
365
|
+
// subPull ले बफरबाट पछिल्लो डाटा निकाल्छ
|
|
366
|
+
१३.५ File Transfer with Resume (pubFile/subFile)
|
|
367
|
+
ठूला फाइलहरू टुक्रा-टुक्रा गरेर पठाउने। रिज्युम सपोर्ट सहित:
|
|
340
368
|
|
|
341
369
|
typescript
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
rt.
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
//
|
|
355
|
-
rt.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
370
|
+
// सर्भरले फाइल पब्लिस गर्ने तयारी (pubFile)
|
|
371
|
+
const fileId = 'patient-report-123';
|
|
372
|
+
const metadata = rt.pubFile(fileId, '/path/to/patient-report.pdf', 64 * 1024); // 64KB chunks
|
|
373
|
+
|
|
374
|
+
if (metadata) {
|
|
375
|
+
console.log(`File registered: ${metadata.name}, Total chunks: ${metadata.totalChunks}`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// क्लाइन्टले फाइल डाउनलोड गर्ने (subFile)
|
|
379
|
+
// startChunk = 0 भनेको सुरुदेखि नै डाउनलोड गर्ने
|
|
380
|
+
await rt.subFile('device-123', fileId, 0);
|
|
381
|
+
|
|
382
|
+
// Resume: पहिले रोकिएको ठाउँबाट पुनः सुरु गर्ने
|
|
383
|
+
const lastChunk = rt.getFileProgress('device-123', fileId);
|
|
384
|
+
if (lastChunk >= 0) {
|
|
385
|
+
console.log(`Resuming from chunk ${lastChunk + 1}`);
|
|
386
|
+
await rt.resumeFile('device-123', fileId);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// फाइलको जानकारी लिने
|
|
390
|
+
const fileInfo = rt.getFileInfo(fileId);
|
|
391
|
+
console.log(`File: ${fileInfo?.name}, Size: ${fileInfo?.size} bytes`);
|
|
392
|
+
|
|
393
|
+
// सबै उपलब्ध फाइलहरूको सूची
|
|
394
|
+
const files = rt.listFiles();
|
|
395
|
+
१३.६ P2P Streaming
|
|
396
|
+
सर्भर मार्फत पीयरहरू बीच सिधै डाटा सेयरिङ:
|
|
397
|
+
|
|
398
|
+
typescript
|
|
399
|
+
// फाइलको उपलब्धता सबै पीयरलाई जानकारी दिने
|
|
400
|
+
rt.announceToPeers('file-123', 'device-A');
|
|
401
|
+
|
|
402
|
+
// फाइल कुन-कुन पीयरसँग छ हेर्ने
|
|
403
|
+
const peers = rt.getPeersForFile('file-123');
|
|
404
|
+
console.log(`File available at: ${peers}`);
|
|
405
|
+
|
|
406
|
+
// पीयरबाट सिधै डाटा माग गर्ने
|
|
407
|
+
rt.requestFromPeer('device-B', 'device-A', 'file-123', 5); // chunk 5
|
|
408
|
+
|
|
409
|
+
// पीयरलाई सिधै डाटा पठाउने (Server Pass-through)
|
|
410
|
+
rt.sendToPeer('device-A', 'device-B', { chunk: 5, data: buffer });
|
|
411
|
+
१३.७ Private Messaging
|
|
412
|
+
सुरक्षित निजी संचारको लागि:
|
|
413
|
+
|
|
414
|
+
typescript
|
|
415
|
+
// क्लाइन्टले आफ्नो प्राइभेट च्यानल सुन्ने
|
|
416
|
+
rt.privateSub('device-123', (msg) => {
|
|
417
|
+
console.log('Private message received:', msg);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// कसैलाई प्राइभेट मेसेज पठाउने
|
|
421
|
+
rt.privatePub('device-456', { secret: 'data', action: 'alert' });
|
|
422
|
+
|
|
423
|
+
// नोट: privatePub ले `phone/signaling/{targetId}` टपिकमा publish गर्छ
|
|
424
|
+
१३.८ Basic Pub/Sub (Retro-compatible)
|
|
425
|
+
पुरानो MQTT-शैली pub/sub पनि काम गर्छ:
|
|
426
|
+
|
|
427
|
+
typescript
|
|
428
|
+
// टपिक सब्सक्राइब गर्ने (वाइल्डकार्ड सपोर्ट)
|
|
429
|
+
rt.subscribe('sensors/temperature/+', (payload, topic) => {
|
|
430
|
+
console.log(`Temperature data: ${payload.value}°C from ${topic}`);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// टपिकमा पब्लिस गर्ने
|
|
434
|
+
rt.publish('sensors/temperature/room1', { value: 23.5, unit: 'celsius' });
|
|
435
|
+
|
|
436
|
+
// रिटेन्ड मेसेज (नयाँ सब्सक्राइबरले पाउने)
|
|
437
|
+
rt.publish('config/system', { mode: 'active' }, { retain: true, ttl: 3600000 });
|
|
438
|
+
|
|
439
|
+
// वाइल्डकार्ड म्याचिङ
|
|
440
|
+
// + (सिङ्गल लेभल) - एउटा सेग्मेन्ट मात्र
|
|
441
|
+
rt.subscribe('sensors/+/temperature', (data) => {
|
|
442
|
+
// म्याच: sensors/room1/temperature, sensors/room2/temperature
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// # (मल्टी लेभल) - सबै सेग्मेन्टहरू
|
|
446
|
+
rt.subscribe('sensors/#', (data) => {
|
|
447
|
+
// म्याच: sensors/temp, sensors/room1/humidity, sensors/building/floor/room/temp
|
|
448
|
+
});
|
|
449
|
+
१३.९ Redis Scaling
|
|
362
450
|
एकाधिक सर्भरहरू बीच मेसेज सिंक गर्न:
|
|
363
451
|
|
|
364
452
|
typescript
|
|
@@ -370,7 +458,7 @@ const rt2 = new RealtimeCore({ redisUrl: 'redis://localhost:6379' });
|
|
|
370
458
|
|
|
371
459
|
// rt1 मा पब्लिस गरेको मेसेज rt2 मा पुग्छ
|
|
372
460
|
rt1.publish('global/event', { message: 'Hello from Server 1' });
|
|
373
|
-
|
|
461
|
+
१३.१० ACL (Access Control List)
|
|
374
462
|
typescript
|
|
375
463
|
const rt = new RealtimeCore({
|
|
376
464
|
acl: {
|
|
@@ -385,7 +473,7 @@ const rt = new RealtimeCore({
|
|
|
385
473
|
}
|
|
386
474
|
}
|
|
387
475
|
});
|
|
388
|
-
|
|
476
|
+
१३.११ प्लगिन सिस्टम (Plugin System)
|
|
389
477
|
typescript
|
|
390
478
|
import { RealtimePlugin, RealtimeContext } from 'dolphin-server-modules/plugins';
|
|
391
479
|
|
|
@@ -403,81 +491,279 @@ const mqttPlugin: RealtimePlugin = {
|
|
|
403
491
|
};
|
|
404
492
|
|
|
405
493
|
rt.use(mqttPlugin);
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
interface RealtimeCoreConfig {
|
|
410
|
-
maxMessageSize?: number; // डिफल्ट: 256KB
|
|
411
|
-
redisUrl?: string; // Redis URL (ऐच्छिक)
|
|
412
|
-
acl?: { // Access Control
|
|
413
|
-
canSubscribe: (deviceId, topic) => boolean;
|
|
414
|
-
canPublish: (deviceId, topic) => boolean;
|
|
415
|
-
};
|
|
416
|
-
enableJSONCache?: boolean; // JSON क्यास सक्षम
|
|
417
|
-
useBinaryProtocol?: boolean; // बाइनरी प्रोटोकल
|
|
418
|
-
debug?: boolean; // डिबग मोड
|
|
419
|
-
}
|
|
420
|
-
Methods
|
|
421
|
-
Method Description
|
|
422
|
-
subscribe(topic, fn, deviceId?) टपिकमा सब्सक्राइब
|
|
423
|
-
publish(topic, payload, opts?, deviceId?) टपिकमा पब्लिस
|
|
424
|
-
handle(raw, socket?, deviceId?) कच्चा डाटा प्रोसेस
|
|
425
|
-
broadcast(topic, payload, opts?) सबै डिभाइसमा पठाउने
|
|
426
|
-
register(deviceId, socket?, metadata?) डिभाइस रजिस्टर
|
|
427
|
-
unregister(deviceId) डिभाइस हटाउने
|
|
428
|
-
use(plugin) प्लगिन थप्ने
|
|
429
|
-
getStats() स्ट्याटिस्टिक्स
|
|
430
|
-
destroy() सबै रिसोर्स क्लिनअप
|
|
431
|
-
१३.११ पूर्ण उदाहरण: IoT Sensor Platform
|
|
494
|
+
१३.१२ पूर्ण उदाहरण: हस्पिटल मोनिटरिङ सिस्टम
|
|
495
|
+
यो उदाहरणले हस्पिटलमा Telephone System र Equipment Monitoring को लागि पूर्ण real-time solution देखाउँछ:
|
|
496
|
+
|
|
432
497
|
typescript
|
|
433
|
-
//
|
|
498
|
+
// hospital-realtime.ts
|
|
434
499
|
import { RealtimeCore } from 'dolphin-server-modules/realtime';
|
|
435
500
|
import WebSocket from 'ws';
|
|
501
|
+
import mongoose from 'mongoose';
|
|
436
502
|
|
|
503
|
+
// MongoDB Schema
|
|
504
|
+
const TelephoneSchema = new mongoose.Schema({
|
|
505
|
+
deviceId: String, ip: String, extension: String,
|
|
506
|
+
status: { type: String, enum: ['idle', 'busy', 'offline', 'ringing'] },
|
|
507
|
+
busy: { isBusy: Boolean, occupiedBy: String, since: Date },
|
|
508
|
+
lastSeen: Date
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
const EquipmentSchema = new mongoose.Schema({
|
|
512
|
+
equipmentId: String, name: String, type: String,
|
|
513
|
+
status: { type: String, enum: ['online', 'offline', 'in_use', 'maintenance'] },
|
|
514
|
+
busy: { isBusy: Boolean, occupiedBy: String, since: Date },
|
|
515
|
+
patientInfo: { patientId: String, roomNo: String, bedNo: String },
|
|
516
|
+
lastSeen: Date
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
const Telephone = mongoose.model('Telephone', TelephoneSchema);
|
|
520
|
+
const Equipment = mongoose.model('Equipment', EquipmentSchema);
|
|
521
|
+
|
|
522
|
+
// RealtimeCore Setup
|
|
437
523
|
const rt = new RealtimeCore({
|
|
438
524
|
maxMessageSize: 1024 * 1024,
|
|
439
525
|
enableJSONCache: true,
|
|
440
|
-
debug:
|
|
526
|
+
debug: true
|
|
441
527
|
});
|
|
442
528
|
|
|
443
|
-
//
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
529
|
+
// 1. Device Registration Handler
|
|
530
|
+
async function handleDeviceConnection(ws: WebSocket, deviceId: string, deviceType: string) {
|
|
531
|
+
rt.register(deviceId, ws, { type: deviceType, registeredAt: Date.now() });
|
|
532
|
+
|
|
533
|
+
// Send current status immediately
|
|
534
|
+
const status = await getCurrentStatus(deviceId, deviceType);
|
|
535
|
+
rt.sendTo(deviceId, { type: 'INIT_STATUS', data: status });
|
|
536
|
+
|
|
537
|
+
ws.on('message', async (data) => {
|
|
538
|
+
await rt.handle(data as Buffer, ws, deviceId);
|
|
539
|
+
await updateHeartbeat(deviceId, deviceType);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
ws.on('close', () => {
|
|
543
|
+
rt.unregister(deviceId);
|
|
544
|
+
markDeviceOffline(deviceId, deviceType);
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// 2. Telephone Status Update (Real-time)
|
|
549
|
+
rt.subscribe('telephone/+/status', async (data, topic) => {
|
|
550
|
+
const deviceId = topic.split('/')[1];
|
|
551
|
+
await Telephone.findOneAndUpdate(
|
|
552
|
+
{ deviceId },
|
|
553
|
+
{ status: data.status, lastSeen: new Date() }
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
// Broadcast to all monitoring clients
|
|
557
|
+
rt.broadcast('monitor/telephone/update', { deviceId, status: data.status });
|
|
558
|
+
|
|
559
|
+
// Special alert for busy
|
|
560
|
+
if (data.status === 'busy') {
|
|
561
|
+
rt.publish('alert/telephone/busy', { deviceId, callWith: data.callWith });
|
|
448
562
|
}
|
|
449
563
|
});
|
|
450
564
|
|
|
451
|
-
//
|
|
452
|
-
rt.subscribe('
|
|
453
|
-
|
|
565
|
+
// 3. Equipment Reservation with Resume Support
|
|
566
|
+
rt.subscribe('equipment/reserve', async (data) => {
|
|
567
|
+
const { equipmentId, patientId, roomNo } = data;
|
|
568
|
+
|
|
569
|
+
const equipment = await Equipment.findOne({ equipmentId });
|
|
570
|
+
if (equipment?.busy?.isBusy) {
|
|
571
|
+
rt.sendTo(data.deviceId, {
|
|
572
|
+
type: 'RESERVE_FAILED',
|
|
573
|
+
reason: `Equipment busy with ${equipment.busy.occupiedBy}`
|
|
574
|
+
});
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
await Equipment.findOneAndUpdate(
|
|
579
|
+
{ equipmentId },
|
|
580
|
+
{
|
|
581
|
+
'busy.isBusy': true,
|
|
582
|
+
'busy.occupiedBy': patientId,
|
|
583
|
+
'busy.since': new Date(),
|
|
584
|
+
patientInfo: { patientId, roomNo },
|
|
585
|
+
status: 'in_use'
|
|
586
|
+
}
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
rt.publish(`equipment/${equipmentId}/reserved`, { patientId, roomNo });
|
|
590
|
+
rt.sendTo(data.deviceId, { type: 'RESERVE_SUCCESS', equipmentId });
|
|
454
591
|
});
|
|
455
592
|
|
|
456
|
-
//
|
|
457
|
-
|
|
593
|
+
// 4. File Transfer for Medical Reports
|
|
594
|
+
rt.subscribe('report/request', async (data) => {
|
|
595
|
+
const { patientId, reportType, deviceId } = data;
|
|
596
|
+
const reportPath = `/reports/${patientId}/${reportType}.pdf`;
|
|
597
|
+
const fileId = `report-${patientId}-${Date.now()}`;
|
|
598
|
+
|
|
599
|
+
const metadata = rt.pubFile(fileId, reportPath);
|
|
600
|
+
if (metadata) {
|
|
601
|
+
rt.sendTo(deviceId, {
|
|
602
|
+
type: 'REPORT_READY',
|
|
603
|
+
fileId,
|
|
604
|
+
name: metadata.name,
|
|
605
|
+
size: metadata.size,
|
|
606
|
+
totalChunks: metadata.totalChunks
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
});
|
|
458
610
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
611
|
+
// 5. Resume interrupted file download
|
|
612
|
+
rt.subscribe('report/resume', async (data) => {
|
|
613
|
+
const { fileId, deviceId } = data;
|
|
614
|
+
const lastChunk = rt.getFileProgress(deviceId, fileId);
|
|
462
615
|
|
|
463
|
-
|
|
616
|
+
if (lastChunk >= 0) {
|
|
617
|
+
await rt.resumeFile(deviceId, fileId);
|
|
618
|
+
rt.sendTo(deviceId, { type: 'RESUME_STARTED', fromChunk: lastChunk + 1 });
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
// 6. Heartbeat Monitor (Auto offline detection)
|
|
623
|
+
setInterval(async () => {
|
|
624
|
+
const timeout = new Date(Date.now() - 10000); // 10 seconds
|
|
625
|
+
|
|
626
|
+
const offlineTelephones = await Telephone.updateMany(
|
|
627
|
+
{ lastSeen: { $lt: timeout }, status: { $ne: 'offline' } },
|
|
628
|
+
{ status: 'offline' }
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
const offlineEquipment = await Equipment.updateMany(
|
|
632
|
+
{ lastSeen: { $lt: timeout }, status: { $ne: 'offline' } },
|
|
633
|
+
{ status: 'offline' }
|
|
634
|
+
);
|
|
464
635
|
|
|
465
|
-
|
|
466
|
-
|
|
636
|
+
if (offlineTelephones.modifiedCount > 0 || offlineEquipment.modifiedCount > 0) {
|
|
637
|
+
rt.publish('alert/offline', {
|
|
638
|
+
telephones: offlineTelephones.modifiedCount,
|
|
639
|
+
equipment: offlineEquipment.modifiedCount,
|
|
640
|
+
timestamp: Date.now()
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}, 5000);
|
|
644
|
+
|
|
645
|
+
// 7. Dashboard Stats (Real-time)
|
|
646
|
+
rt.subscribe('dashboard/stats', async (data, deviceId) => {
|
|
647
|
+
const [telephones, equipment] = await Promise.all([
|
|
648
|
+
Telephone.find(),
|
|
649
|
+
Equipment.find()
|
|
650
|
+
]);
|
|
651
|
+
|
|
652
|
+
rt.sendTo(deviceId, {
|
|
653
|
+
type: 'DASHBOARD_STATS',
|
|
654
|
+
data: {
|
|
655
|
+
telephones: {
|
|
656
|
+
total: telephones.length,
|
|
657
|
+
busy: telephones.filter(t => t.status === 'busy').length,
|
|
658
|
+
offline: telephones.filter(t => t.status === 'offline').length
|
|
659
|
+
},
|
|
660
|
+
equipment: {
|
|
661
|
+
total: equipment.length,
|
|
662
|
+
inUse: equipment.filter(e => e.busy?.isBusy).length,
|
|
663
|
+
offline: equipment.filter(e => e.status === 'offline').length
|
|
664
|
+
},
|
|
665
|
+
timestamp: Date.now()
|
|
666
|
+
}
|
|
667
|
+
});
|
|
467
668
|
});
|
|
468
669
|
|
|
469
|
-
//
|
|
470
|
-
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
670
|
+
// Helper functions
|
|
671
|
+
async function updateHeartbeat(deviceId: string, type: string) {
|
|
672
|
+
const Model = type === 'telephone' ? Telephone : Equipment;
|
|
673
|
+
const idField = type === 'telephone' ? 'deviceId' : 'equipmentId';
|
|
674
|
+
await Model.findOneAndUpdate(
|
|
675
|
+
{ [idField]: deviceId },
|
|
676
|
+
{ lastSeen: new Date(), status: 'online' }
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
async function markDeviceOffline(deviceId: string, type: string) {
|
|
681
|
+
const Model = type === 'telephone' ? Telephone : Equipment;
|
|
682
|
+
const idField = type === 'telephone' ? 'deviceId' : 'equipmentId';
|
|
683
|
+
await Model.findOneAndUpdate(
|
|
684
|
+
{ [idField]: deviceId },
|
|
685
|
+
{ status: 'offline' }
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
async function getCurrentStatus(deviceId: string, type: string) {
|
|
690
|
+
const Model = type === 'telephone' ? Telephone : Equipment;
|
|
691
|
+
const idField = type === 'telephone' ? 'deviceId' : 'equipmentId';
|
|
692
|
+
return await Model.findOne({ [idField]: deviceId });
|
|
693
|
+
}
|
|
474
694
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
695
|
+
// Start WebSocket Server
|
|
696
|
+
const wss = new WebSocket.Server({ port: 8080 });
|
|
697
|
+
wss.on('connection', (ws, req) => {
|
|
698
|
+
const url = new URL(req.url!, `http://${req.headers.host}`);
|
|
699
|
+
const deviceId = url.searchParams.get('id')!;
|
|
700
|
+
const deviceType = url.searchParams.get('type')!; // 'telephone' or 'equipment'
|
|
701
|
+
|
|
702
|
+
handleDeviceConnection(ws, deviceId, deviceType);
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
// Start HTTP Server with Dolphin
|
|
706
|
+
import { createDolphinServer } from 'dolphin-server-modules/server';
|
|
707
|
+
const app = createDolphinServer();
|
|
708
|
+
|
|
709
|
+
app.get('/stats', (ctx) => {
|
|
710
|
+
return rt.getStats();
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
app.listen(3000, () => {
|
|
714
|
+
console.log('🏥 Hospital Realtime System Running');
|
|
715
|
+
console.log('📊 Stats:', rt.getStats());
|
|
478
716
|
});
|
|
479
717
|
|
|
480
|
-
console.log('
|
|
718
|
+
console.log('Hospital Monitoring System Active! 🏥');
|
|
719
|
+
१३.१३ API रेफरेन्स (RealtimeCore v2.0)
|
|
720
|
+
Constructor Options:
|
|
721
|
+
|
|
722
|
+
typescript
|
|
723
|
+
interface RealtimeCoreConfig {
|
|
724
|
+
maxMessageSize?: number; // डिफल्ट: 256KB
|
|
725
|
+
redisUrl?: string; // Redis URL (ऐच्छिक)
|
|
726
|
+
acl?: { // Access Control
|
|
727
|
+
canSubscribe: (deviceId, topic) => boolean;
|
|
728
|
+
canPublish: (deviceId, topic) => boolean;
|
|
729
|
+
};
|
|
730
|
+
enableJSONCache?: boolean; // JSON क्यास सक्षम
|
|
731
|
+
useBinaryProtocol?: boolean; // बाइनरी प्रोटोकल
|
|
732
|
+
debug?: boolean; // डिबग मोड
|
|
733
|
+
maxBufferPerTopic?: number; // pubPush बफर साइज (डिफल्ट: 100)
|
|
734
|
+
defaultChunkSize?: number; // File chunk size (डिफल्ट: 64KB)
|
|
735
|
+
enableP2P?: boolean; // P2P सक्षम
|
|
736
|
+
}
|
|
737
|
+
Methods:
|
|
738
|
+
|
|
739
|
+
Method Description
|
|
740
|
+
subscribe(topic, fn, deviceId?) टपिकमा सब्सक्राइब
|
|
741
|
+
publish(topic, payload, opts?, deviceId?) टपिकमा पब्लिस
|
|
742
|
+
pubPush(topic, payload) हाई-फ्रिक्वेन्सी पब्लिस (No JSON)
|
|
743
|
+
subPull(deviceId, topic, count?) बफरबाट डाटा तान्ने
|
|
744
|
+
pubFile(fileId, filePath, chunkSize?) फाइल ट्रान्सफर तयारी
|
|
745
|
+
subFile(deviceId, fileId, startChunk?) फाइल डाउनलोड
|
|
746
|
+
resumeFile(deviceId, fileId) रोकिएको डाउनलोड पुनः सुरु
|
|
747
|
+
getFileProgress(deviceId, fileId) डाउनलोड प्रगति हेर्ने
|
|
748
|
+
listFiles() सबै उपलब्ध फाइलहरू
|
|
749
|
+
register(deviceId, socket?, metadata?) डिभाइस रजिस्टर
|
|
750
|
+
unregister(deviceId) डिभाइस हटाउने
|
|
751
|
+
isOnline(deviceId) डिभाइस अनलाइन छ कि छैन
|
|
752
|
+
isReady(deviceId) सकेट तयार छ कि छैन
|
|
753
|
+
sendTo(deviceId, payload) डिभाइसमा सिधै पठाउने
|
|
754
|
+
kick(deviceId, reason?) डिभाइस हटाउने
|
|
755
|
+
broadcastToGroup(groupName, payload) ग्रुपमा ब्रोडकास्ट
|
|
756
|
+
getOnlineDevices() सबै अनलाइन डिभाइस
|
|
757
|
+
ping(deviceId) डिभाइस पिंग गर्ने
|
|
758
|
+
privateSub(deviceId, fn) प्राइभेट च्यानल सुन्ने
|
|
759
|
+
privatePub(targetId, payload, opts?) प्राइभेट मेसेज पठाउने
|
|
760
|
+
announceToPeers(fileId, deviceId) P2P घोषणा
|
|
761
|
+
getPeersForFile(fileId) पीयर सूची
|
|
762
|
+
handle(raw, socket?, deviceId?) कच्चा डाटा प्रोसेस
|
|
763
|
+
broadcast(topic, payload, opts?) सबै डिभाइसमा पठाउने
|
|
764
|
+
use(plugin) प्लगिन थप्ने
|
|
765
|
+
getStats() स्ट्याटिस्टिक्स
|
|
766
|
+
destroy() सबै रिसोर्स क्लिनअप
|
|
481
767
|
१४. इन्डिपेन्डेन्ट राउटिङ (Independent Routing)
|
|
482
768
|
typescript
|
|
483
769
|
// userRoutes.ts
|
|
@@ -532,40 +818,35 @@ app.listen(port, cb) सर्भर सुरु गर्ने
|
|
|
532
818
|
app.get/patch/post/put/delete(path, ...handlers) HTTP मेथड
|
|
533
819
|
app.use(path?, middleware/router) मिडलवेयर वा राउटर
|
|
534
820
|
app.group(prefix, callback) रूट ग्रुपिङ
|
|
535
|
-
RealtimeCore
|
|
821
|
+
RealtimeCore v2.0
|
|
536
822
|
Method Description
|
|
537
823
|
rt.subscribe(topic, fn, deviceId?) सब्सक्राइब
|
|
538
824
|
rt.publish(topic, payload, opts?, deviceId?) पब्लिस
|
|
539
|
-
rt.
|
|
825
|
+
rt.pubPush(topic, payload) हाई-फ्रिक्वेन्सी पब्लिस
|
|
826
|
+
rt.subPull(deviceId, topic, count?) बफरबाट तान्ने
|
|
827
|
+
rt.pubFile(fileId, filePath, chunkSize?) फाइल तयारी
|
|
828
|
+
rt.subFile(deviceId, fileId, startChunk?) फाइल डाउनलोड
|
|
829
|
+
rt.resumeFile(deviceId, fileId) डाउनलोड पुनः सुरु
|
|
540
830
|
rt.register(deviceId, socket?, metadata?) डिभाइस रजिस्टर
|
|
831
|
+
rt.sendTo(deviceId, payload) सिधै पठाउने
|
|
832
|
+
rt.kick(deviceId, reason?) डिभाइस हटाउने
|
|
833
|
+
rt.privateSub(deviceId, fn) प्राइभेट सब्सक्राइब
|
|
834
|
+
rt.privatePub(targetId, payload, opts?) प्राइभेट पब्लिस
|
|
835
|
+
rt.getStats() स्ट्याटिस्टिक्स
|
|
541
836
|
rt.destroy() क्लिनअप
|
|
542
837
|
निष्कर्ष (Conclusion)
|
|
543
|
-
बधाई छ! तपाईँले Dolphin Framework को Master Guide पूरा गर्नुभयो। अब तपाईँ:
|
|
838
|
+
बधाई छ! तपाईँले Dolphin Framework v2.0 को Master Guide पूरा गर्नुभयो। अब तपाईँ:
|
|
544
839
|
|
|
545
840
|
✅ हाई-पर्फर्मेन्स API सर्भर बनाउन
|
|
546
|
-
|
|
547
841
|
✅ अटोमेटेड CRUD र भ्यालिडेसन प्रयोग गर्न
|
|
548
|
-
|
|
549
|
-
✅
|
|
550
|
-
|
|
842
|
+
✅ RealtimeCore v2.0 सँग पूर्ण रियलटाइम एप्लिकेसन बनाउन
|
|
843
|
+
✅ pubPush/subPull सँग हाई-फ्रिक्वेन्सी डाटा ह्यान्डल गर्न
|
|
844
|
+
✅ pubFile/subFile सँग ठूला फाइलहरू रिज्युम सपोर्ट सहित ट्रान्सफर गर्न
|
|
845
|
+
✅ Device Management सँग डिभाइसहरू ट्र्याक गर्न
|
|
551
846
|
✅ Redis सँग मल्टिपल सर्भर स्केल गर्न
|
|
552
|
-
|
|
553
847
|
✅ अटो-जेनेरेटेड Swagger डकुमेन्टेसन बनाउन
|
|
554
848
|
|
|
555
849
|
सक्नुहुन्छ।
|
|
556
850
|
|
|
557
851
|
Happy Coding! 🐬🇳🇵
|
|
558
|
-
नेपालबाट विश्वस्तरको सफ्टवेयर बनाऔँ!
|
|
559
|
-
|
|
560
|
-
text
|
|
561
|
-
|
|
562
|
-
## 📄 PDF कसरी बनाउने?
|
|
563
|
-
|
|
564
|
-
### विधि १: VS Code (सजिलो)
|
|
565
|
-
1. माथिको कोडलाई `dolphin-master-guide.md` मा सेभ गर्नुहोस्
|
|
566
|
-
2. VS Code मा `Markdown PDF` एक्सटेन्सन इन्स्टल गर्नुहोस्
|
|
567
|
-
3. फाइलमा राइट क्लिक → `Markdown PDF: Export (pdf)`
|
|
568
|
-
|
|
569
|
-
### विधि २: कमान्ड लाइन (Pandoc)
|
|
570
|
-
```bash
|
|
571
|
-
pandoc dolphin-master-guide.md -o dolphin-framework-guide.pdf --pdf-engine=xelatex -V geometry:margin=1in
|
|
852
|
+
नेपालबाट विश्वस्तरको सफ्टवेयर बनाऔँ!
|