@eluvio/elv-client-js 4.2.16 → 4.2.17

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 (32) hide show
  1. package/dist/ElvClient-min.js +1 -1
  2. package/dist/ElvClient-node-min.js +1 -1
  3. package/dist/ElvFrameClient-min.js +1 -1
  4. package/dist/ElvPermissionsClient-min.js +1 -1
  5. package/dist/ElvWalletClient-min.js +1 -1
  6. package/dist/ElvWalletClient-node-min.js +1 -1
  7. package/dist/src/AuthorizationClient.js +2 -1
  8. package/dist/src/ContentObjectAudit.js +2 -1
  9. package/dist/src/ContentObjectVerification.js +281 -0
  10. package/dist/src/ElvClient.js +8 -9
  11. package/dist/src/FrameClient.js +1 -1
  12. package/dist/src/HttpClient.js +83 -47
  13. package/dist/src/NetworkUrls.js +8 -0
  14. package/dist/src/abr_profiles/abr_profile_live_drm.js +0 -10
  15. package/dist/src/client/ContentAccess.js +76 -85
  16. package/dist/src/client/LiveConf.js +170 -84
  17. package/dist/src/client/LiveStream.js +5205 -2118
  18. package/dist/src/live_recording_config_profiles/live_recording_config_default.js +45 -0
  19. package/package.json +3 -2
  20. package/src/FrameClient.js +23 -2
  21. package/src/abr_profiles/abr_profile_live_drm.js +0 -10
  22. package/src/client/ContentAccess.js +1 -2
  23. package/src/client/LiveConf.js +149 -65
  24. package/src/client/LiveStream.js +2592 -654
  25. package/src/live_recording_config_profiles/live_recording_config_default.js +54 -0
  26. package/src/live_recording_config_profiles/live_stream_profile_full.json +143 -0
  27. package/testScripts/StreamUpdateLinks.js +95 -0
  28. package/utilities/LiveOutputs.js +149 -0
  29. package/utilities/StreamCreate.js +53 -0
  30. package/utilities/lib/helpers.js +5 -1
  31. package/utilities/tests/mocks/ElvClient.mock.js +9 -1
  32. package/utilities/tests/unit/StreamCreate.test.js +39 -0
@@ -0,0 +1,45 @@
1
+ var LiveRecordingConfigDefault = {
2
+ drm_type: "clear",
3
+ recording_config: {
4
+ part_ttl: 86400,
5
+ reconnect_timeout: 3600,
6
+ connection_timeout: 3600,
7
+ copy_mpegts: false
8
+ },
9
+ profile: {
10
+ ladder_specs: {
11
+ audio: [{
12
+ bit_rate: 192000,
13
+ channels: 2,
14
+ codecs: "mp4a.40.2"
15
+ }, {
16
+ bit_rate: 384000,
17
+ channels: 6,
18
+ codecs: "mp4a.40.2"
19
+ }],
20
+ video: [{
21
+ bit_rate: 9500000,
22
+ codecs: "avc1.640028,mp4a.40.2",
23
+ height: 1080,
24
+ width: 1920
25
+ }, {
26
+ bit_rate: 4500000,
27
+ codecs: "avc1.640028,mp4a.40.2",
28
+ height: 720,
29
+ width: 1280
30
+ }, {
31
+ bit_rate: 2000000,
32
+ codecs: "avc1.640028,mp4a.40.2",
33
+ height: 540,
34
+ width: 960
35
+ }, {
36
+ bit_rate: 900000,
37
+ codecs: "avc1.640028,mp4a.40.2",
38
+ height: 540,
39
+ width: 960
40
+ }]
41
+ },
42
+ name: "Default"
43
+ }
44
+ };
45
+ module.exports = LiveRecordingConfigDefault;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eluvio/elv-client-js",
3
- "version": "4.2.16",
3
+ "version": "4.2.17",
4
4
  "description": "Javascript client for the Eluvio Content Fabric",
5
5
  "main": "src/index.js",
6
6
  "author": "Kevin Talmadge",
@@ -79,6 +79,7 @@
79
79
  "lodash": "^4.17.19",
80
80
  "lodash.isequal": "^4.5.0",
81
81
  "mime-types": "^2.1.24",
82
+ "moment": "^2.27.0",
82
83
  "multihashes": "^0.4.14",
83
84
  "node-fetch": "^2.6.7",
84
85
  "object-path": "^0.11.8",
@@ -112,7 +113,7 @@
112
113
  "eslint": "^9.32.0",
113
114
  "jsdoc": "^4.0.0",
114
115
  "jsdom": "^26.1.0",
115
- "moment": "^2.27.0",
116
+ "mocha": "^11.7.5",
116
117
  "raw-loader": "^0.5.1",
117
118
  "shell-quote": "^1.7.2",
118
119
  "showdown": "^1.9.1",
@@ -447,6 +447,15 @@ class FrameClient {
447
447
  "Nodes",
448
448
  "NTPInstance",
449
449
  "ObjectCleanup",
450
+ "OutputsCreate",
451
+ "OutputsDelete",
452
+ "OutputsList",
453
+ "OutputsListItem",
454
+ "OutputsModify",
455
+ "OutputsModifyBatch",
456
+ "OutputsResolveSrtPullUrls",
457
+ "OutputsState",
458
+ "OutputsStop",
450
459
  "Permission",
451
460
  "PlayoutOptions",
452
461
  "PlayoutPathResolution",
@@ -472,6 +481,7 @@ class FrameClient {
472
481
  "RevokeShare",
473
482
  "SendFunds",
474
483
  "SetAccessCharge",
484
+ "StreamApplyProfile",
475
485
  "SetAuth",
476
486
  "SetAuthContext",
477
487
  "SetAuthPolicy",
@@ -496,17 +506,28 @@ class FrameClient {
496
506
  "SpaceNodes",
497
507
  "StartABRMezzanineJobs",
498
508
  "StreamAddWatermark",
509
+ "StreamApplyProfile",
510
+ "StreamAssignProfile",
499
511
  "StreamConfig",
512
+ "StreamConfigProfile",
513
+ "StreamConfigProfiles",
500
514
  "StreamCopyToVod",
501
515
  "StreamCreate",
502
516
  "StreamInitialize",
503
517
  "StreamInsertion",
518
+ "StreamLinkToSite",
504
519
  "StreamListUrls",
520
+ "StreamRemoveLinkToSite",
505
521
  "StreamRemoveWatermark",
522
+ "StreamSaveConfigProfile",
506
523
  "StreamSetOfferingAndDRM",
507
- "StreamStatus",
524
+ "StreamSiteSettings",
525
+ "StreamStartRecording",
508
526
  "StreamStartOrStopOrReset",
509
- "StreamStopSession",
527
+ "StreamStatus",
528
+ "StreamStopRecording",
529
+ "StreamUnassignProfile",
530
+ "StreamUpdateConfig",
510
531
  "SuspendNTPInstance",
511
532
  "TenantContractId",
512
533
  "TenantId",
@@ -1863,16 +1863,6 @@ const AbrProfileLiveDrm = {
1863
1863
  }
1864
1864
  },
1865
1865
  "playout_formats": {
1866
- "dash-playready-cenc": {
1867
- "drm": {
1868
- "enc_scheme_name": "cenc",
1869
- "type": "DrmPlayReady"
1870
- },
1871
- "protocol": {
1872
- "min_buffer_length": 2,
1873
- "type": "ProtoDash"
1874
- }
1875
- },
1876
1866
  "dash-widevine": {
1877
1867
  "drm": {
1878
1868
  "content_id": "",
@@ -1804,7 +1804,6 @@ exports.GlobalUrl = async function({
1804
1804
  }
1805
1805
  }
1806
1806
 
1807
- console.log("Updated")
1808
1807
  let urlPath = UrlJoin("s", network);
1809
1808
  if(versionHash) {
1810
1809
  objectId = this.utils.DecodeVersionHash(versionHash).objectId;
@@ -2824,7 +2823,7 @@ exports.EncryptionConk = async function({libraryId, objectId, versionHash, write
2824
2823
  const owner = await this.authClient.Owner({id: objectId});
2825
2824
 
2826
2825
  const ownerCapKey = `eluv.caps.iusr${this.utils.AddressToHash(this.signer.address)}`;
2827
- const ownerCap = await this.ContentObjectMetadata({libraryId, objectId, versionHash, metadataSubtree: ownerCapKey});
2826
+ const ownerCap = await this.ContentObjectMetadata({libraryId, objectId, versionHash, writeToken, metadataSubtree: ownerCapKey});
2828
2827
 
2829
2828
  if(!this.utils.EqualAddress(owner, this.signer.address) && !ownerCap) {
2830
2829
  if(download) {
@@ -1,7 +1,8 @@
1
1
  /* eslint no-console: 0 */
2
+ const R = require("ramda");
2
3
 
3
4
  const DefaultABRLadder = {
4
- "video" : [
5
+ "video": [
5
6
  {
6
7
  bit_rate: 14000000,
7
8
  codecs: "avc1.640028,mp4a.40.2",
@@ -33,7 +34,7 @@ const DefaultABRLadder = {
33
34
  width: 960
34
35
  }
35
36
  ],
36
- "audio" : [
37
+ "audio": [
37
38
  {
38
39
  bit_rate: 192000,
39
40
  channels: 2,
@@ -55,8 +56,9 @@ const LiveconfTemplate = {
55
56
  },
56
57
  playout_config: {
57
58
  dvr_enabled: true,
58
- dvr_max_duration: 0,
59
+ dvr_max_duration: 14400,
59
60
  rebroadcast_start_time_sec_epoch: 0,
61
+ playout_sharding_level: 2,
60
62
  vod_enabled: false
61
63
  },
62
64
  recording_config: {
@@ -64,11 +66,11 @@ const LiveconfTemplate = {
64
66
  description: "",
65
67
  ladder_specs: [],
66
68
  listen: true,
67
- live_delay_nano: 2000000000,
69
+ live_delay_nano: 6000000000,
68
70
  max_duration_sec: -1,
69
71
  name: "",
70
72
  origin_url: "",
71
- part_ttl: 3600,
73
+ part_ttl: 86400,
72
74
  playout_type: "live",
73
75
  source_timescale: null,
74
76
  reconnect_timeout: 600,
@@ -113,18 +115,49 @@ const LiveconfTemplate = {
113
115
  };
114
116
 
115
117
  class LiveConf {
116
- constructor(probeData, nodeId, nodeUrl, includeAVSegDurations, overwriteOriginUrl, syncAudioToVideo) {
118
+ constructor({
119
+ url,
120
+ probeData,
121
+ nodeId,
122
+ nodeUrl,
123
+ includeAVSegDurations,
124
+ overwriteOriginUrl,
125
+ syncAudioToVideo,
126
+ liveRecordingMeta
127
+ }) {
128
+ this.url = url;
117
129
  this.probeData = probeData;
118
130
  this.nodeId = nodeId;
119
131
  this.nodeUrl = nodeUrl;
120
132
  this.includeAVSegDurations = includeAVSegDurations;
121
133
  this.overwriteOriginUrl = overwriteOriginUrl;
122
134
  this.syncAudioToVideo = syncAudioToVideo;
135
+ this.currentLiveRecordingMeta = liveRecordingMeta;
123
136
  }
124
137
 
125
- probeKind() {
126
- let fileNameSplit = this.probeData.format.filename.split(":");
127
- return fileNameSplit[0];
138
+ getFormat() {
139
+ if (this.probeData.format.format_name) {
140
+ return this.probeData.format.format_name;
141
+ }
142
+ const fileNameSplit = this.probeData.format?.filename?.split(":");
143
+ if (fileNameSplit.length > 1) {
144
+ const protoScheme = fileNameSplit[0];
145
+ switch(protoScheme) {
146
+ case "rtmp":
147
+ return "flv";
148
+ case "udp":
149
+ case "rtp":
150
+ case "srt":
151
+ return "mpegts";
152
+ default:
153
+ return "format_unknown";
154
+ }
155
+ }
156
+ }
157
+
158
+ getProtocol() {
159
+ const protoScheme = this.url.split(":")[0];
160
+ return protoScheme;
128
161
  }
129
162
 
130
163
  getStreamDataForCodecType(codecType) {
@@ -139,16 +172,18 @@ class LiveConf {
139
172
 
140
173
  // Return all audio streams found in the probe
141
174
  // Used by generateAudioStreamsConfig()
142
- getAudioStreamsFromProbe() {
143
- let audioStreams = {};
175
+ getAudioStreamsFromProbe({ladderProfile}) {
176
+ const audioStreams = {};
144
177
  const audioStreamData = this.probeData.streams.filter((value) => value.codec_type === "audio");
145
178
 
146
179
  for(let index = 0; index < audioStreamData.length; index++) {
147
180
  const currentStreamIndex = audioStreamData[index].stream_index;
148
181
  const currentStreamData = audioStreamData[index];
149
182
 
183
+ const profileAudioForType = ladderProfile?.audio?.find(a => a.channels === currentStreamData.channels);
184
+
150
185
  audioStreams[currentStreamIndex] = {
151
- recordingBitrate: Math.max(currentStreamData.bit_rate, 128000),
186
+ recordingBitrate: profileAudioForType?.bit_rate ?? Math.max(currentStreamData.bit_rate ?? 0, 12800),
152
187
  recordingChannels: currentStreamData.channels,
153
188
  playoutLabel: `Audio ${index + 1}`
154
189
  };
@@ -205,16 +240,15 @@ class LiveConf {
205
240
  calcSegDuration({sourceTimescale, sampleRate, audioCodec}) {
206
241
  let seg = {};
207
242
 
208
- switch(this.probeKind()) {
209
- case "rtmp":
243
+ switch(this.getFormat()) {
244
+ case "flv":
210
245
  seg = this.calcSegDurationRtmp({sourceTimescale, sampleRate, audioCodec});
211
246
  break;
212
- case "udp":
213
- case "srt":
247
+ case "mpegts":
214
248
  seg = this.calcSegDurationMpegts({sourceTimescale, sampleRate, audioCodec});
215
249
  break;
216
250
  default:
217
- throw "protocol not supported - " + this.probeKind();
251
+ throw "protocol not supported - " + this.getFormat();
218
252
  }
219
253
 
220
254
  if(audioCodec == "aac") {
@@ -362,32 +396,79 @@ class LiveConf {
362
396
  syncAudioToStreamIdValue() {
363
397
  let sync_id = -1;
364
398
  let videoStream = this.getStreamDataForCodecType("video");
365
- switch(this.probeKind()) {
366
- case "udp":
367
- case "srt":
399
+ switch(this.getFormat()) {
400
+ case "mpegts":
368
401
  sync_id = videoStream.stream_id;
369
402
  break;
370
- case "rtmp":
403
+ case "flv":
371
404
  sync_id = videoStream.stream_index;
372
405
  break;
373
406
  }
374
407
  return sync_id;
375
408
  }
376
409
 
410
+ /**
411
+ * Map custom live recording profile to the expected config structure
412
+ * @param {Object} customProfile - User's custom recording profile
413
+ * @return {Object} - Mapped config in live_recording format
414
+ */
415
+ MapCustomProfileToLiveConfig({customProfile}) {
416
+ if(!customProfile) return {};
417
+
418
+ const CompactDeep = (obj) => {
419
+ if(obj === null || typeof obj !== "object" || Array.isArray(obj)) {
420
+ return obj;
421
+ }
422
+
423
+ return R.pipe(
424
+ R.reject(R.isNil),
425
+ R.map(val => typeof val === "object" ? CompactDeep(val) : val)
426
+ )(obj);
427
+ };
428
+
429
+ const {recording_config, recording_params, ...rest} = customProfile;
430
+
431
+ return CompactDeep({
432
+ live_recording: {
433
+ ...rest,
434
+ recording_config: {
435
+ recording_params: {
436
+ ...recording_params,
437
+ part_ttl: recording_config?.part_ttl,
438
+ reconnect_timeout: recording_config?.reconnect_timeout,
439
+ xc_params: {
440
+ ...(recording_params?.xc_params || {}),
441
+ connection_timeout: recording_config?.connection_timeout,
442
+ copy_mpegts: recording_config?.copy_mpegts,
443
+ input_cfg: recording_config?.input_cfg
444
+ }
445
+ }
446
+ }
447
+ }
448
+ });
449
+ }
450
+
377
451
  /*
378
452
  * Generate audio streams recording configuration based on the optional custom settings.
379
453
  * If no custom "audio" section is present, record all the acceptable audio streams found in the probe
380
454
  */
381
- generateAudioStreamsConfig({customSettings}) {
455
+ generateAudioStreamsConfig({liveRecordingConfigProfile}) {
456
+ const ladderProfile = liveRecordingConfigProfile?.playout_config?.ladder_specs || DefaultABRLadder;
457
+ const audioSettings = liveRecordingConfigProfile?.recording_stream_config?.audio;
382
458
 
383
459
  let audioStreams = {};
384
- if(customSettings && customSettings.audio && Object.keys(customSettings.audio).length > 0) {
385
- for(let i = 0; i < Object.keys(customSettings.audio).length; i ++) {
386
- let audioIdx = Object.keys(customSettings.audio)[i];
387
- let audio = customSettings.audio[audioIdx];
460
+
461
+ if(audioSettings && Object.keys(audioSettings).length > 0) {
462
+ for(let i = 0; i < Object.keys(audioSettings).length; i ++) {
463
+ const audioIdx = Object.keys(audioSettings)[i];
464
+ const audio = audioSettings[audioIdx];
465
+ const profileAudioForType = ladderProfile?.audio.find(a => a.channels === (audio.channels ?? 2));
466
+
388
467
  audioStreams[audioIdx] = {
389
- recordingBitrate: audio.recording_bitrate || 192000,
468
+ recordingBitrate: ladderProfile?.audio ? profileAudioForType.bit_rate : audio.recording_bitrate ?? 192000,
390
469
  recordingChannels: audio.recording_channels || 2,
470
+ lang: audio.lang,
471
+ isDefault: audio.default
391
472
  };
392
473
  if(audio.playout) {
393
474
  audioStreams[audioIdx].playoutLabel = audio.playout_label || `Audio ${i + 1}`;
@@ -396,21 +477,34 @@ class LiveConf {
396
477
  }
397
478
 
398
479
  // If no audio streams specified in custom config, set up all the suitable audio streams in the probe
399
- if(!customSettings.audio || Object.keys(customSettings.audio).length == 0) {
400
- audioStreams = this.getAudioStreamsFromProbe();
480
+ if(!audioSettings || Object.keys(audioSettings).length === 0) {
481
+ audioStreams = this.getAudioStreamsFromProbe({ladderProfile});
401
482
  }
402
483
 
403
484
  return audioStreams;
404
485
  }
405
486
 
406
487
  /*
407
- * Generate the live recording config as required by QFAB, based on defaults and optional custom settings.
488
+ * Generate the live recording config as required by QFAB, based on defaults, existing settings and optional custom settings.
408
489
  */
409
490
  generateLiveConf({customSettings}) {
410
- // gather required data
411
- const conf = JSON.parse(JSON.stringify(LiveconfTemplate));
412
- const fileName = this.overwriteOriginUrl || this.probeData.format.filename;
413
- const audioStreams = this.generateAudioStreamsConfig({customSettings});
491
+ // Saved config overrides defaults and is preserved on reconfiguration
492
+ let conf = R.clone(LiveconfTemplate);
493
+
494
+ if(this.currentLiveRecordingMeta) {
495
+ conf = R.mergeDeepRight(conf,
496
+ {live_recording: this.currentLiveRecordingMeta});
497
+ }
498
+
499
+ if(customSettings.liveRecordingConfigProfile) {
500
+ conf = R.mergeDeepRight(conf,
501
+ this.MapCustomProfileToLiveConfig({
502
+ customProfile: customSettings.liveRecordingConfigProfile
503
+ }));
504
+ }
505
+
506
+ const fileName = this.url;
507
+ const audioStreams = this.generateAudioStreamsConfig({liveRecordingConfigProfile: customSettings.liveRecordingConfigProfile});
414
508
 
415
509
  // Retrieve one audio stream from the probe to read the sample rate and codec name
416
510
  const audioStream = this.getStreamDataForCodecType("audio");
@@ -440,15 +534,10 @@ class LiveConf {
440
534
  }
441
535
 
442
536
  // Fill in specifics for protocol
443
- switch(this.probeKind()) {
444
- case "udp":
445
- sourceTimescale = 90000;
446
- conf.live_recording.recording_config.recording_params.source_timescale = sourceTimescale;
447
- break;
448
- case "srt":
537
+ switch(this.getFormat()) {
538
+ case "mpegts":
449
539
  sourceTimescale = 90000;
450
540
  conf.live_recording.recording_config.recording_params.source_timescale = sourceTimescale;
451
- conf.live_recording.recording_config.recording_params.live_delay_nano = 4000000000;
452
541
  break;
453
542
  case "rtmp":
454
543
  sourceTimescale = 16000;
@@ -458,7 +547,15 @@ class LiveConf {
458
547
  console.log("HLS detected. Not yet implemented");
459
548
  break;
460
549
  default:
461
- console.log("Unsupported media", this.probeKind());
550
+ console.log("Unsupported media", this.getFormat());
551
+ break;
552
+ }
553
+
554
+ switch(this.getProtocol()) {
555
+ case "srt":
556
+ if(!customSettings.liveRecordingConfigProfile?.recording_params?.live_delay_nano) {
557
+ conf.live_recording.recording_config.recording_params.live_delay_nano = 6000000000;
558
+ }
462
559
  break;
463
560
  }
464
561
 
@@ -481,11 +578,15 @@ class LiveConf {
481
578
  conf.live_recording.recording_config.recording_params.xc_params.video_frame_duration_ts = segDurations.videoFrameDurationTs;
482
579
  }
483
580
 
484
- const ladderProfile = customSettings.ladder_profile || DefaultABRLadder;
581
+ const ladder_specs = customSettings.liveRecordingConfigProfile?.playout_config?.ladder_specs;
582
+ const ladderProfile = ladder_specs?.video?.length > 0 ? ladder_specs : DefaultABRLadder;
485
583
 
486
584
  conf.live_recording.recording_config.recording_params.xc_params.enc_height = videoStream.height;
487
585
  conf.live_recording.recording_config.recording_params.xc_params.enc_width = videoStream.width;
488
586
 
587
+ // Reset ladder specs (updating existing stream will carry over old specs
588
+ conf.live_recording.recording_config.recording_params.ladder_specs = [];
589
+
489
590
  // Determine video recording bitrate and ABR ladder
490
591
  let topLadderRate = 0;
491
592
  for(let i = 0; i < ladderProfile.video.length; i ++) {
@@ -521,6 +622,7 @@ class LiveConf {
521
622
  break;
522
623
  }
523
624
  }
625
+
524
626
  if(Object.keys(audioLadderSpec).length === 0) {
525
627
  // If no channels layout match, just use the first element in the ladder
526
628
  audioLadderSpec = {...ladderProfile.audio[0]};
@@ -532,8 +634,10 @@ class LiveConf {
532
634
  audioLadderSpec.stream_name = `audio_${audioIndex}`;
533
635
  audioLadderSpec.stream_label = audio.playoutLabel ? audio.playoutLabel : null;
534
636
  audioLadderSpec.media_type = 2;
637
+ audioLadderSpec.lang = audio.lang ?? "";
535
638
 
536
- if(Object.keys(audioStreams).length === 1) {
639
+
640
+ if(Object.keys(audioStreams).length === 1 || audio.isDefault) {
537
641
  audioLadderSpec.default = true;
538
642
  }
539
643
 
@@ -541,33 +645,13 @@ class LiveConf {
541
645
  if(audio.recordingBitrate > globalAudioBitrate) {
542
646
  globalAudioBitrate = audio.recordingBitrate;
543
647
  }
544
- nAudio ++;
648
+ nAudio++;
545
649
  }
546
650
 
547
651
  // Global recording bitrate for all audio streams
548
652
  conf.live_recording.recording_config.recording_params.xc_params.audio_bitrate = globalAudioBitrate;
549
653
  conf.live_recording.recording_config.recording_params.xc_params.n_audio = nAudio;
550
654
 
551
- // Iterate through custom settings (which will override any existing setting)
552
- function SetByPath({obj, path, value}) {
553
- const keys = path.split(".");
554
- let temp = obj;
555
- for(let i = 0; i < keys.length - 1; i++) {
556
- if(!temp[keys[i]]) {
557
- temp[keys[i]] = {};
558
- }
559
- temp = temp[keys[i]];
560
- }
561
-
562
- temp[keys[keys.length - 1]] = value;
563
- }
564
-
565
- const {metaPathValues} = customSettings;
566
-
567
- for(let [path, value] of Object.entries(metaPathValues || {})) {
568
- SetByPath({obj: conf, path, value});
569
- }
570
-
571
655
  return conf;
572
656
  }
573
657
  }