@hitchy/plugin-odem-socket.io 0.3.0 → 0.5.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/api/services/odem/websocket-provider.js +175 -64
- package/config/socket.js +1 -3
- package/index.js +2 -3
- package/package.json +20 -13
- package/readme.md +8 -4
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { promisify } from "node:util";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
module.exports = function() {
|
|
3
|
+
/** */
|
|
4
|
+
export default function() {
|
|
6
5
|
const api = this;
|
|
7
6
|
|
|
8
7
|
const logDebug = api.log( "hitchy:plugin:odem:socket.io:debug" );
|
|
@@ -32,7 +31,7 @@ module.exports = function() {
|
|
|
32
31
|
const txPerModel = {};
|
|
33
32
|
|
|
34
33
|
for ( const [ modelName, model ] of Object.entries( api.models ) ) {
|
|
35
|
-
if ( api.service.OdemSchema.mayBeExposed(
|
|
34
|
+
if ( api.service.OdemSchema.mayBeExposed( undefined, model ) ) {
|
|
36
35
|
txPerModel[modelName] = this.broadcastModelNotifications( notifications, namespace, model );
|
|
37
36
|
}
|
|
38
37
|
}
|
|
@@ -85,10 +84,10 @@ module.exports = function() {
|
|
|
85
84
|
const handlers = {
|
|
86
85
|
created: ( uuid, newRecord, asyncGeneratorFn ) => {
|
|
87
86
|
asyncGeneratorFn()
|
|
88
|
-
.then( item => this.namespaceEmit( namespace,
|
|
87
|
+
.then( item => this.namespaceEmit( namespace, item, {
|
|
89
88
|
change: "created",
|
|
90
89
|
model: Model.name,
|
|
91
|
-
properties:
|
|
90
|
+
properties: this.serializeItem( item, false ),
|
|
92
91
|
}, true ) )
|
|
93
92
|
.catch( cause => {
|
|
94
93
|
logError( "broadcasting notification on having created item %s of %s has failed:", uuid, Model.name, cause.stack );
|
|
@@ -96,21 +95,22 @@ module.exports = function() {
|
|
|
96
95
|
},
|
|
97
96
|
changed: ( uuid, newRecord, oldRecord, asyncGeneratorFn ) => {
|
|
98
97
|
asyncGeneratorFn()
|
|
99
|
-
.then( item => this.namespaceEmit( namespace,
|
|
100
|
-
change: "
|
|
98
|
+
.then( item => this.namespaceEmit( namespace, item, {
|
|
99
|
+
change: "changed",
|
|
101
100
|
model: Model.name,
|
|
102
|
-
properties:
|
|
101
|
+
properties: this.serializeItem( item, false ),
|
|
103
102
|
}, true ) )
|
|
104
103
|
.catch( cause => {
|
|
105
104
|
logError( "broadcasting notification on having updated item %s of %s has failed:", uuid, Model.name, cause.stack );
|
|
106
105
|
} );
|
|
107
106
|
},
|
|
108
|
-
removed: uuid => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
removed: ( uuid, lastRecord, asyncGeneratorFn ) => {
|
|
108
|
+
asyncGeneratorFn()
|
|
109
|
+
.then( item => this.namespaceEmit( namespace, item, {
|
|
110
|
+
change: "removed",
|
|
111
|
+
model: Model.name,
|
|
112
|
+
properties: { uuid: String( uuid ) },
|
|
113
|
+
}, false ) )
|
|
114
114
|
.catch( cause => {
|
|
115
115
|
logError( "broadcasting notification on having removed item %s of %s has failed:", uuid, Model.name, cause.stack );
|
|
116
116
|
} );
|
|
@@ -181,6 +181,8 @@ module.exports = function() {
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
// TODO track the whole request instead of just its user to enable the plugin to assign $context to processed model instances
|
|
185
|
+
|
|
184
186
|
// pick user eventually injected into mocked request
|
|
185
187
|
return request.user || undefined;
|
|
186
188
|
}
|
|
@@ -198,14 +200,16 @@ module.exports = function() {
|
|
|
198
200
|
* accessing the changed model and/or its properties.
|
|
199
201
|
*
|
|
200
202
|
* @param {Server} namespace namespace managing a pool of sockets that have joined the namespace
|
|
201
|
-
* @param {
|
|
202
|
-
* @param {{change:
|
|
203
|
+
* @param {Hitchy.Plugin.Odem.Model} item instance of model notification is related to
|
|
204
|
+
* @param {{change: "created"|"changed"|"removed", model: string, properties: Object}} payload raw payload of model-specific notification
|
|
203
205
|
* @param {boolean} filterProperties if true, properties need to be filtered per peer based on its authorization
|
|
204
206
|
* @returns {Promise<void>} promise resolved when all authorized peers have been notified
|
|
205
207
|
*/
|
|
206
|
-
static async namespaceEmit( namespace,
|
|
208
|
+
static async namespaceEmit( namespace, item, payload, filterProperties ) {
|
|
207
209
|
const hasPluginAuth = Boolean( api.plugins.authentication );
|
|
208
210
|
const sockets = await namespace.fetchSockets();
|
|
211
|
+
const Model = item.constructor;
|
|
212
|
+
const userCache = {};
|
|
209
213
|
|
|
210
214
|
logDebug( "forwarding notification to %d socket(s)", sockets.length );
|
|
211
215
|
|
|
@@ -220,46 +224,73 @@ module.exports = function() {
|
|
|
220
224
|
return;
|
|
221
225
|
}
|
|
222
226
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
+
const userId = user?.uuid ?? "(null)";
|
|
228
|
+
let cached = userCache[userId];
|
|
229
|
+
|
|
230
|
+
if ( cached == null ) {
|
|
231
|
+
cached = userCache[userId] = false;
|
|
227
232
|
|
|
228
|
-
|
|
229
|
-
|
|
233
|
+
// check connected user's common authorization to access the
|
|
234
|
+
// notification's model or the instance properties included with
|
|
235
|
+
// the notification
|
|
236
|
+
if ( !OdemSchema.mayBeExposed( user, Model ) ) {
|
|
237
|
+
logDebug( `omit notification to user ${userId} (${user?.name}) at peer ${socket.id} with access on ${Model.name} forbidden by model` );
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
230
240
|
|
|
231
|
-
|
|
232
|
-
|
|
241
|
+
let mayRead = OdemSchema.isAdmin( user );
|
|
242
|
+
let scope = "read";
|
|
233
243
|
|
|
234
|
-
if ( !mayRead ) {
|
|
235
|
-
mayRead = await Authorization.mayAccess( user, `@hitchy.odem.model.${Model.name}.
|
|
236
|
-
|
|
244
|
+
if ( !mayRead && hasPluginAuth ) {
|
|
245
|
+
mayRead = await Authorization.mayAccess( user, `@hitchy.odem.model.${Model.name}.read` );
|
|
246
|
+
|
|
247
|
+
if ( !mayRead ) {
|
|
248
|
+
mayRead = await Authorization.mayAccess( user, `@hitchy.odem.model.${Model.name}.list` );
|
|
249
|
+
scope = "list";
|
|
250
|
+
}
|
|
237
251
|
}
|
|
238
|
-
}
|
|
239
252
|
|
|
240
|
-
|
|
253
|
+
// check model's life cycle hook individually controlling
|
|
254
|
+
// if current socket's user may receive the notification
|
|
255
|
+
const mayBeNotified = await item.beforeNotify( user, payload.change );
|
|
256
|
+
|
|
257
|
+
if ( !mayBeNotified ) {
|
|
258
|
+
logDebug( `notification to user ${userId} (${user?.name}) at peer ${socket.id} prevented by beforeNotify() of model ${Model.name}` );
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
241
261
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
262
|
+
// eventually send the notification ...
|
|
263
|
+
if ( !mayRead ) {
|
|
264
|
+
// user must not read items of this model -> do not
|
|
265
|
+
// provide any identifier, but basically notify on some
|
|
266
|
+
// (unspecified) change to the model
|
|
267
|
+
cached = userCache[userId] = { ...payload, properties: {} };
|
|
268
|
+
} else if ( filterProperties ) {
|
|
269
|
+
// user may read items of this model, but access to some
|
|
270
|
+
// properties may be restricted by the model's schema
|
|
271
|
+
cached = userCache[userId] = {
|
|
272
|
+
...payload,
|
|
273
|
+
properties: OdemSchema.filterItem( payload.properties, user, Model, scope ),
|
|
274
|
+
};
|
|
275
|
+
} else {
|
|
276
|
+
// user gets notification with all properties of item
|
|
277
|
+
cached = userCache[userId] = payload;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
248
280
|
|
|
249
|
-
|
|
281
|
+
if ( cached ) {
|
|
282
|
+
// user may receive notification -> emit it eventually
|
|
283
|
+
const prefix = api.utility.case.pascalToKebab( Model.name );
|
|
250
284
|
|
|
251
|
-
socket.emit( `${prefix}:changed`,
|
|
252
|
-
socket.emit( "*:changed",
|
|
253
|
-
} else {
|
|
254
|
-
socket.emit( `${prefix}:changed`, payload );
|
|
255
|
-
socket.emit( "*:changed", payload );
|
|
285
|
+
socket.emit( `${prefix}:changed`, cached );
|
|
286
|
+
socket.emit( "*:changed", cached );
|
|
256
287
|
}
|
|
257
288
|
};
|
|
258
289
|
|
|
259
290
|
for ( const socket of sockets ) {
|
|
260
291
|
notifyPeer( socket ).catch( cause => {
|
|
261
292
|
logError(
|
|
262
|
-
`notifying peer ${socket.id} on having ${payload.change} item of ${Model.name} has failed:`,
|
|
293
|
+
`notifying peer ${socket.id} on having ${payload.change} item ${item.uuid} of ${Model.name} has failed:`,
|
|
263
294
|
cause.stack
|
|
264
295
|
);
|
|
265
296
|
} );
|
|
@@ -309,7 +340,7 @@ module.exports = function() {
|
|
|
309
340
|
}[action];
|
|
310
341
|
|
|
311
342
|
if ( !handler ) {
|
|
312
|
-
logDebug( "ignoring socket-based request for
|
|
343
|
+
logDebug( "ignoring socket-based request for action %s on model %s due to lack of handler", action, Model.name );
|
|
313
344
|
return;
|
|
314
345
|
}
|
|
315
346
|
|
|
@@ -344,7 +375,7 @@ module.exports = function() {
|
|
|
344
375
|
}
|
|
345
376
|
}
|
|
346
377
|
|
|
347
|
-
if ( !api.service.OdemSchema.mayBeExposed(
|
|
378
|
+
if ( !api.service.OdemSchema.mayBeExposed( user, Model ) ) {
|
|
348
379
|
throw new Error( "access forbidden by model" );
|
|
349
380
|
}
|
|
350
381
|
|
|
@@ -353,7 +384,7 @@ module.exports = function() {
|
|
|
353
384
|
let filter;
|
|
354
385
|
|
|
355
386
|
if ( loadRecords ) {
|
|
356
|
-
filter = item => api.service.OdemSchema.filterItem(
|
|
387
|
+
filter = item => api.service.OdemSchema.filterItem( this.serializeItem( item, false ), user, Model, "list" );
|
|
357
388
|
} else {
|
|
358
389
|
filter = item => ( { uuid: item.uuid } );
|
|
359
390
|
}
|
|
@@ -396,7 +427,7 @@ module.exports = function() {
|
|
|
396
427
|
}
|
|
397
428
|
}
|
|
398
429
|
|
|
399
|
-
if ( !api.service.OdemSchema.mayBeExposed(
|
|
430
|
+
if ( !api.service.OdemSchema.mayBeExposed( user, Model ) ) {
|
|
400
431
|
throw new Error( "access forbidden by model" );
|
|
401
432
|
}
|
|
402
433
|
|
|
@@ -405,7 +436,7 @@ module.exports = function() {
|
|
|
405
436
|
let filter;
|
|
406
437
|
|
|
407
438
|
if ( loadRecords ) {
|
|
408
|
-
filter = item => api.service.OdemSchema.filterItem(
|
|
439
|
+
filter = item => api.service.OdemSchema.filterItem( this.serializeItem( item, false ), user, Model, "list" );
|
|
409
440
|
} else {
|
|
410
441
|
filter = item => ( { uuid: item.uuid } );
|
|
411
442
|
}
|
|
@@ -434,7 +465,7 @@ module.exports = function() {
|
|
|
434
465
|
* @returns {Promise<void>} promise settled on request handled
|
|
435
466
|
*/
|
|
436
467
|
static async handleCreateRequest( socket, Model, properties, response ) {
|
|
437
|
-
logDebug( "request for creating %s instance with %j", Model.name, properties );
|
|
468
|
+
logDebug( "request for creating %s instance with %j", Model.name, reduce( properties ) );
|
|
438
469
|
|
|
439
470
|
const { Authorization, OdemSchema } = api.service;
|
|
440
471
|
|
|
@@ -445,17 +476,17 @@ module.exports = function() {
|
|
|
445
476
|
throw new Error( "access forbidden" );
|
|
446
477
|
}
|
|
447
478
|
|
|
448
|
-
if ( !OdemSchema.mayBeExposed(
|
|
479
|
+
if ( !OdemSchema.mayBeExposed( user, Model ) ) {
|
|
449
480
|
throw new Error( "access forbidden by model" );
|
|
450
481
|
}
|
|
451
482
|
|
|
452
483
|
const schema = Model.schema;
|
|
453
|
-
const filtered = OdemSchema.filterItem( properties,
|
|
484
|
+
const filtered = OdemSchema.filterItem( properties, user, Model, "create" );
|
|
454
485
|
const instance = new Model();
|
|
455
486
|
|
|
456
|
-
for ( const
|
|
487
|
+
for ( const propName of Object.keys( filtered ) ) {
|
|
457
488
|
if ( schema.props.hasOwnProperty( propName ) || schema.computed.hasOwnProperty( propName ) ) {
|
|
458
|
-
instance[propName] =
|
|
489
|
+
instance[propName] = filtered[propName];
|
|
459
490
|
}
|
|
460
491
|
}
|
|
461
492
|
|
|
@@ -463,7 +494,7 @@ module.exports = function() {
|
|
|
463
494
|
|
|
464
495
|
response( {
|
|
465
496
|
success: true,
|
|
466
|
-
properties: OdemSchema.filterItem(
|
|
497
|
+
properties: OdemSchema.filterItem( this.serializeItem( instance, false ), user, Model, "read" ),
|
|
467
498
|
} );
|
|
468
499
|
} catch ( error ) {
|
|
469
500
|
logError( "creating %s instance failed: %s", Model.name, error.stack );
|
|
@@ -495,7 +526,7 @@ module.exports = function() {
|
|
|
495
526
|
}
|
|
496
527
|
}
|
|
497
528
|
|
|
498
|
-
if ( !api.service.OdemSchema.mayBeExposed(
|
|
529
|
+
if ( !api.service.OdemSchema.mayBeExposed( user, Model ) ) {
|
|
499
530
|
throw new Error( "access forbidden by model" );
|
|
500
531
|
}
|
|
501
532
|
|
|
@@ -504,7 +535,7 @@ module.exports = function() {
|
|
|
504
535
|
|
|
505
536
|
response( {
|
|
506
537
|
success: true,
|
|
507
|
-
properties: api.service.OdemSchema.filterItem(
|
|
538
|
+
properties: api.service.OdemSchema.filterItem( this.serializeItem( instance, true ), user, Model, "read" ),
|
|
508
539
|
} );
|
|
509
540
|
} catch ( error ) {
|
|
510
541
|
logError( "reading %s instance failed: %s", Model.name, error.stack );
|
|
@@ -526,7 +557,7 @@ module.exports = function() {
|
|
|
526
557
|
* @returns {Promise<void>} promise settled on request handled
|
|
527
558
|
*/
|
|
528
559
|
static async handleUpdateRequest( socket, Model, uuid, properties, response ) {
|
|
529
|
-
logDebug( "request for updating %s instance %s with %j", Model.name, uuid, properties );
|
|
560
|
+
logDebug( "request for updating %s instance %s with %j", Model.name, uuid, reduce( properties ) );
|
|
530
561
|
|
|
531
562
|
try {
|
|
532
563
|
const user = await this.getUserForSocket( socket );
|
|
@@ -537,12 +568,12 @@ module.exports = function() {
|
|
|
537
568
|
}
|
|
538
569
|
}
|
|
539
570
|
|
|
540
|
-
if ( !api.service.OdemSchema.mayBeExposed(
|
|
571
|
+
if ( !api.service.OdemSchema.mayBeExposed( user, Model ) ) {
|
|
541
572
|
throw new Error( "access forbidden by model" );
|
|
542
573
|
}
|
|
543
574
|
|
|
544
575
|
const schema = Model.schema;
|
|
545
|
-
const filtered = api.service.OdemSchema.filterItem( properties,
|
|
576
|
+
const filtered = api.service.OdemSchema.filterItem( properties, user, Model, "write" );
|
|
546
577
|
const instance = new Model( uuid );
|
|
547
578
|
await instance.load();
|
|
548
579
|
|
|
@@ -556,7 +587,7 @@ module.exports = function() {
|
|
|
556
587
|
|
|
557
588
|
response( {
|
|
558
589
|
success: true,
|
|
559
|
-
properties: api.service.OdemSchema.filterItem(
|
|
590
|
+
properties: api.service.OdemSchema.filterItem( this.serializeItem( instance, false ), user, Model, "read" ),
|
|
560
591
|
} );
|
|
561
592
|
} catch ( error ) {
|
|
562
593
|
logError( "updating %s instance failed: %s", Model.name, error.stack );
|
|
@@ -588,7 +619,7 @@ module.exports = function() {
|
|
|
588
619
|
}
|
|
589
620
|
}
|
|
590
621
|
|
|
591
|
-
if ( !api.service.OdemSchema.mayBeExposed(
|
|
622
|
+
if ( !api.service.OdemSchema.mayBeExposed( user, Model ) ) {
|
|
592
623
|
throw new Error( "access forbidden by model" );
|
|
593
624
|
}
|
|
594
625
|
|
|
@@ -607,6 +638,47 @@ module.exports = function() {
|
|
|
607
638
|
} );
|
|
608
639
|
}
|
|
609
640
|
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Serializes provided instance of a model optionally including/excluding
|
|
644
|
+
* properties marked as _lazy_.
|
|
645
|
+
*
|
|
646
|
+
* This method is used to provide reduced sets of properties per item
|
|
647
|
+
* on listing/updating items while delivering all properties when
|
|
648
|
+
* reading individual items.
|
|
649
|
+
*
|
|
650
|
+
* @param {Hitchy.Plugin.Odem.Model} item instance of model
|
|
651
|
+
* @param {boolean} includeLazyProperties if true, properties marked as lazy in schema of model are included with resulting record
|
|
652
|
+
* @returns {object} record of provided item's serialized properties
|
|
653
|
+
*/
|
|
654
|
+
static serializeItem( item, includeLazyProperties = false ) {
|
|
655
|
+
const record = item.toObject( { serialized: true } );
|
|
656
|
+
|
|
657
|
+
if ( includeLazyProperties ) {
|
|
658
|
+
return record;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const filtered = {};
|
|
662
|
+
|
|
663
|
+
for ( const name of Object.keys( record ) ) {
|
|
664
|
+
let definition = item.constructor.schema.props[name];
|
|
665
|
+
|
|
666
|
+
if ( definition == null ) {
|
|
667
|
+
definition = item.constructor.schema.computed[name];
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const lazy = definition?.lazy;
|
|
671
|
+
|
|
672
|
+
if ( !lazy ||
|
|
673
|
+
( typeof lazy === "string" && lazy !== "ws" && lazy !== "socket" && lazy !== "websocket" ) ||
|
|
674
|
+
( typeof lazy === "object" && !lazy.ws && !lazy.socket && !lazy.websocket )
|
|
675
|
+
) {
|
|
676
|
+
filtered[name] = record[name];
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return filtered;
|
|
681
|
+
}
|
|
610
682
|
}
|
|
611
683
|
|
|
612
684
|
return OdemWebsocketProvider;
|
|
@@ -615,7 +687,7 @@ module.exports = function() {
|
|
|
615
687
|
/**
|
|
616
688
|
* Commonly responds to action requests in case of having disabled them.
|
|
617
689
|
*
|
|
618
|
-
* @param {function(
|
|
690
|
+
* @param {function(any):void} respondFn callback to be invoked for responding to a peer event
|
|
619
691
|
* @returns {void}
|
|
620
692
|
*/
|
|
621
693
|
function reject( respondFn ) {
|
|
@@ -623,3 +695,42 @@ function reject( respondFn ) {
|
|
|
623
695
|
error: "requested action is not available due to runtime configuration",
|
|
624
696
|
} );
|
|
625
697
|
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Recursively trims values of provided data.
|
|
701
|
+
*
|
|
702
|
+
* @param {*} data data to be trimmed
|
|
703
|
+
* @param {number} limit number of characters to keep in strings when trimming
|
|
704
|
+
* @returns {*} provided data trimmed as necessary
|
|
705
|
+
*/
|
|
706
|
+
function reduce( data, limit = 200 ) {
|
|
707
|
+
if ( Array.isArray( data ) ) {
|
|
708
|
+
return data.map( item => reduce( item, Math.max( 10, limit / 5 ) ) );
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if ( data && typeof data === "object" ) {
|
|
712
|
+
const copy = {};
|
|
713
|
+
|
|
714
|
+
for ( const name of Object.keys( data ) ) {
|
|
715
|
+
copy[name] = reduce( data[name], Math.max( 10, limit / 5 ) );
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return copy;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
switch ( typeof data ) {
|
|
722
|
+
case "function" :
|
|
723
|
+
case "string" : {
|
|
724
|
+
const s = String( data );
|
|
725
|
+
|
|
726
|
+
if ( s.length > limit ) {
|
|
727
|
+
return s.substring( 0, limit ) + "…+" + String( limit - s.length );
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return s;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
default :
|
|
734
|
+
return data;
|
|
735
|
+
}
|
|
736
|
+
}
|
package/config/socket.js
CHANGED
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hitchy/plugin-odem-socket.io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "exposing Hitchy's document-oriented database via websocket",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"lint": "eslint .",
|
|
8
|
-
"test": "hitchy-pm odem socket.io auth --exec mocha --ui=tdd
|
|
9
|
-
"coverage": "hitchy-pm odem socket.io auth --exec c8 mocha --ui=tdd
|
|
9
|
+
"test": "hitchy-pm odem socket.io auth --exec mocha --ui=tdd test/**/*.spec.js",
|
|
10
|
+
"coverage": "hitchy-pm odem socket.io auth --exec c8 mocha --ui=tdd test/**/*.spec.js"
|
|
10
11
|
},
|
|
11
12
|
"repository": {
|
|
12
13
|
"type": "git",
|
|
@@ -29,18 +30,24 @@
|
|
|
29
30
|
"index.js"
|
|
30
31
|
],
|
|
31
32
|
"peerDependencies": {
|
|
32
|
-
"@hitchy/core": "
|
|
33
|
-
"@hitchy/plugin-odem": "
|
|
34
|
-
"@hitchy/plugin-socket.io": "
|
|
33
|
+
"@hitchy/core": "^1.5.5",
|
|
34
|
+
"@hitchy/plugin-odem": "^0.14.0",
|
|
35
|
+
"@hitchy/plugin-socket.io": "^0.1.3"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
|
-
"@hitchy/server-dev-tools": "^0.
|
|
38
|
-
"c8": "^10.1.
|
|
39
|
-
"eslint": "^
|
|
40
|
-
"eslint-config-cepharum": "^
|
|
41
|
-
"
|
|
42
|
-
"mocha": "^10.7.3",
|
|
38
|
+
"@hitchy/server-dev-tools": "^0.9.6",
|
|
39
|
+
"c8": "^10.1.3",
|
|
40
|
+
"eslint": "^9.39.2",
|
|
41
|
+
"eslint-config-cepharum": "^2.0.2",
|
|
42
|
+
"mocha": "^11.7.5",
|
|
43
43
|
"should": "^13.2.3",
|
|
44
|
-
"socket.io-client": "^4.
|
|
44
|
+
"socket.io-client": "^4.8.3"
|
|
45
|
+
},
|
|
46
|
+
"c8": {
|
|
47
|
+
"reporter": [
|
|
48
|
+
"text",
|
|
49
|
+
"html"
|
|
50
|
+
],
|
|
51
|
+
"all": true
|
|
45
52
|
}
|
|
46
53
|
}
|
package/readme.md
CHANGED
|
@@ -59,7 +59,7 @@ socket.on( "connect", () => {
|
|
|
59
59
|
} );
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
> socket.io requires all arguments to be provided no matter what. Thus, you can
|
|
62
|
+
> socket.io requires all arguments to be provided no matter what. Thus, you can not omit any of the arguments given in examples below but have to provide values assumed to be default, at least.
|
|
63
63
|
|
|
64
64
|
The following code excerpts are focusing on the inner part of emitting an action event and processing the response.
|
|
65
65
|
|
|
@@ -166,8 +166,12 @@ Last argument is a callback to be invoked with the resulting response from serve
|
|
|
166
166
|
The type of change is
|
|
167
167
|
|
|
168
168
|
* `created` on notifications about having created another instance of model,
|
|
169
|
-
* `
|
|
170
|
-
* `
|
|
169
|
+
* `changed` on notifications about having adjusted one or more properties of an existing instance of model and
|
|
170
|
+
* `removed` on notifications about having removed an existing instance of model.
|
|
171
|
+
|
|
172
|
+
:::danger Breaking change
|
|
173
|
+
Notification types have changed in v0.5.0 from `updated` to `changed` and from `deleted` to `removed` to match names used on server side.
|
|
174
|
+
:::
|
|
171
175
|
|
|
172
176
|
In last case the provided set of properties is limited to the instance's UUID. The common pattern on client-side for listening to server-side notifications looks like this:
|
|
173
177
|
|
|
@@ -175,7 +179,7 @@ In last case the provided set of properties is limited to the instance's UUID. T
|
|
|
175
179
|
const socket = io( "/hitchy/odem" );
|
|
176
180
|
|
|
177
181
|
socket.on( "<modelName>:changed", info => {
|
|
178
|
-
// type of change is in `info.change`, e.g. "
|
|
182
|
+
// type of change is in `info.change`, e.g. "created", "changed" or "removed"
|
|
179
183
|
// process properties of changed instance in `info.properties`
|
|
180
184
|
} );
|
|
181
185
|
```
|