dcp-client 5.1.8 → 5.1.10

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/index.js CHANGED
@@ -304,9 +304,13 @@ KVIN.userCtors.dcpEth$$Address = require('dcp/wallet').Address;
304
304
  * existing object, so that own properties specified in the existing object graph overwrite, but
305
305
  * unspecified edges are left alone.
306
306
  *
307
- * Instances of URL and dcpUrl::URL receive special treatment: if they are being overwritten by a
308
- * string, the string is used the argument to the constructor to create a new object that replaces
309
- * the entire value.
307
+ * Instances of Address, URL and dcpUrl::DcpURL receive special treatment: if they are being overwritten
308
+ * by another type, that value is used as the argument to the constructor to create a new object that
309
+ * replaces the entire value. This allows us, for example, to replace a URL with a registry string and
310
+ * still have the correct type once the program loads.
311
+ *
312
+ * URL types are always copied by value, not by reference.
313
+ *
310
314
  *
311
315
  * Arrays are concatenated together when merging. In the case where an Array and an Object are merged,
312
316
  * the result will be an Array and it will be merged with the object's values.
@@ -318,7 +322,7 @@ KVIN.userCtors.dcpEth$$Address = require('dcp/wallet').Address;
318
322
  * It is possible to "merge" Objects with Arrays. We basically decide that the Array indicies have no
319
323
  * special meaning and neither do the own property names.
320
324
  *
321
- * {} -> {} => leaf merge
325
+ * {} -> {} => ~leaf merge
322
326
  * [] -> [] => concat except it leaves out duplicate elements
323
327
  * {} -> [] => Object.entries({}) -> [] => concat except it leaves out duplicate elements onto array
324
328
  * [] -> {} => [] -> Object with dynamic keys => merge into object
@@ -333,6 +337,11 @@ function addConfig (existing, neo, dotPath)
333
337
 
334
338
  if (neo === undefined || neo === existing)
335
339
  return;
340
+
341
+ /* Caveat debuggor: the "adding" output will only show changes from config files where they return
342
+ * objects. Any changes which are the result of the config file directly mutating properties of
343
+ * dcpConfig might not be displayed. See "magicView" for info about those.
344
+ */
336
345
  debug('dcp-client:config-verbose')('adding', neo);
337
346
  if (typeof neo !== 'object')
338
347
  throw new TypeError(`Unable to merge ${typeof neo} value '${neo}' into ${nodeName()}`);
@@ -351,7 +360,7 @@ function addConfig (existing, neo, dotPath)
351
360
  }
352
361
 
353
362
  /** Make an object from an array so that it can be merged into another object without key collision */
354
- function objFromArrForObj(arr, obj)
363
+ function objFromArrForObj(arr)
355
364
  {
356
365
  const numericKeys = Object.keys(arr).map(key => Number(key)).filter(key => !isNaN(key))
357
366
  const maxNumericKey = numericKeys.length ? numericKeys[numericKeys.length - 1] : NaN;
@@ -367,15 +376,28 @@ function addConfig (existing, neo, dotPath)
367
376
 
368
377
  for (const prop of Object.getOwnPropertyNames(neo))
369
378
  {
370
- if (false
371
- || !Object.hasOwnProperty.call(existing, prop) /* no collision? use new */
372
- || existing[prop] === neo[prop]
373
- || isIntrinsic(neo[prop]) /* instrinsic? use new */
374
- || isIntrinsic(existing[prop])
375
- || neo[prop] instanceof URL /* new URLs overwrite old nodes */
376
- || DcpURL.isURL(neo[prop])
377
- || neo[prop].constructor === RegExp /* new RegExps overwrite old nodes */
378
- )
379
+ /* When existing prop is URL and new prop isn't, use new prop to build a URL.
380
+ * Higher precedence than neo[prop] being intrinsic, convert 'http://example.com' to a URL if the existing is a URL
381
+ */
382
+ if (DcpURL.isURL(existing?.[prop]))
383
+ {
384
+ existing[prop] = new existing[prop].constructor(neo[prop]);
385
+ continue;
386
+ }
387
+
388
+ if (isIntrinsic(neo[prop]))
389
+ {
390
+ existing[prop] = neo[prop];
391
+ continue;
392
+ }
393
+
394
+ if (neo[prop].constructor === RegExp) /* Regexps are unmergeable and must be cloned */
395
+ {
396
+ existing[prop] = new RegExp(neo[prop].source, neo[prop].flags);
397
+ continue;
398
+ }
399
+
400
+ if (neo[prop].constructor === Promise)
379
401
  {
380
402
  existing[prop] = neo[prop];
381
403
  continue;
@@ -383,36 +405,64 @@ function addConfig (existing, neo, dotPath)
383
405
 
384
406
  if (typeof neo[prop] !== 'object')
385
407
  throw new TypeError(`Unable to merge ${typeof neo[prop]} value into ${nodeName(prop)}`);
386
- else
408
+
409
+ /* When new prop is URL, copy it by value instead of merging */
410
+ if (DcpURL.isURL(neo[prop]))
387
411
  {
388
- switch(neo[prop].constructor)
389
- {
390
- case RegExp: case Object: case DcpURL: case URL: case Array:
391
- case bootstrapClasses.DcpURL: case bootstrapClasses.Address:
392
- break;
393
- case Function: /* previously supported: do we really need this? /wg May 2025 */
394
- default:
395
- throw new TypeError(`Unable to merge ${neo[prop].constructor.name} object into ${nodeName(prop)}`);
396
- }
412
+ const neoUrl = neo[prop];
413
+ existing[prop] = new neoUrl.constructor(neoUrl.href);
414
+ continue;
397
415
  }
398
416
 
399
- let neoOfProp = neo[prop]; /* !!! do not use neo[prop] below here !!! */
417
+ /* When existing prop is Address and new prop isn't, use new prop to build an Address */
418
+ if (existing[prop]?.constructor === bootstrapClasses.Address &&
419
+ neo [prop] .constructor !== bootstrapClasses.Address)
420
+ {
421
+ existing[prop] = new bootstrapClasses.Address(neo[prop]);
422
+ continue;
423
+ }
400
424
 
401
- if (typeof existing !== 'object' || (existing[prop].constructor !== Object && existing[prop].constructor !== Array))
402
- throw new TypeError(`Unable to merge into ${existing[prop].constructor?.name || typeof existing} value of ${nodeName(prop)}`);
403
- if (typeof neoOfProp !== 'object' || (neoOfProp.constructor !== Object && neoOfProp.constructor !== Array))
404
- throw new TypeError(`Unable to merge ${neoOfProp.constructor?.name || typeof neoOfProp} value into ${nodeName(prop)}`);
425
+ switch(neo[prop].constructor)
426
+ {
427
+ case RegExp: case bootstrapClasses.DcpURL: case DcpURL: case URL:
428
+ throw new TypeError(`Unexpected URL type merging ${neo[prop].constructor.name} object into ${nodeName(prop)}`);
429
+ case Object:
430
+ case Array:
431
+ case bootstrapClasses.Address:
432
+ break;
433
+ case Function: /* previously supported: do we really need this? /wg May 2025 */
434
+ default:
435
+ throw new TypeError(`Unable to merge ${neo[prop].constructor.name} object into ${nodeName(prop)}`);
436
+ }
437
+
438
+ if (!existing.hasOwnProperty(prop) || isIntrinsic(existing[prop])) /* clone by merging into empty */
439
+ existing[prop] = Array.isArray(neo[prop]) ? [] : {};
440
+
441
+ const neoPropIsArray = Array.isArray(neo [prop]);
442
+ const existingPropIsArray = Array.isArray(existing?.[prop]);
443
+
444
+ const isNotMergeable = (obj) => {
445
+ return false
446
+ || typeof obj[prop] !== 'object'
447
+ || (obj[prop].constructor !== Object && obj[prop].constructor !== Array);
448
+ };
449
+ if (isNotMergeable(existing, prop))
450
+ throw new TypeError(`Unable to merge into ${existing?.[prop].constructor?.name || typeof existing} value of ${nodeName(prop)}`);
451
+ if (isNotMergeable(neo, prop))
452
+ throw new TypeError(`Unable to merge ${neo[prop].constructor?.name || typeof neo[prop]} value into ${nodeName(prop)}`);
453
+
454
+ let neoOfProp = neo[prop]; /* !!! do not use neo[prop] below here !!! */
405
455
 
406
456
  /* When one of the objects is an array and the other is a plain object, we transform the new one to
407
457
  * match the existing one.
408
458
  */
409
- if (!Array.isArray(neoOfProp) && Array.isArray(existing[prop])) /* {} -> [] */
459
+ if (!neoPropIsArray && existingPropIsArray) /* {} -> [] - merge object onto an array? make an array from the object and merge that. */
410
460
  neoOfProp = Object.entries(neoOfProp);
411
- else if (Array.isArray(neoOfProp) && !Array.isArray(existing[prop])) /* [] -> {} */
461
+ else if (neoPropIsArray && !existingPropIsArray) /* [] -> {} - merge array onto an object? make an object from the array and merge that. */
412
462
  neoOfProp = objFromArrForObj(neoOfProp, existing[prop]);
413
463
 
414
464
  /* Either merge objects or arrays. Objects collide props, Arrays collide exactly equal values. */
415
- if (!Array.isArray(existing[prop]))
465
+ if (!existingPropIsArray)
416
466
  addConfig(existing[prop], neoOfProp, nodeName(prop));
417
467
  else
418
468
  {
@@ -436,6 +486,10 @@ function addConfig (existing, neo, dotPath)
436
486
  * @param {object} seen internal use only
437
487
  *
438
488
  * @returns {object}
489
+ *
490
+ * @todo inherited objects should actually be read-only Proxies or similar, to prevent users from
491
+ * accidentally mutating the internal contents of the underlying object; eg setting the href
492
+ * property of a URL inside the default config, when they meant to set the URL itself.
439
493
  */
440
494
  function magicView(node, seen)
441
495
  {
@@ -542,7 +596,7 @@ function addConfigFile(existing /*, file path components ... */) {
542
596
  const fpSnap = fullPath;
543
597
 
544
598
  /**
545
- * Make a the global object for this context this config file is evaluated in.
599
+ * Make a global object for this context for this config file's evaluation.
546
600
  * - Top-level keys from dcpConfig become properties of this object, so that we can write statements
547
601
  * like scheduler.location='XXX' in the file.
548
602
  * - A variable named `dcpConfig` is also added, so that we could replace nodes wholesale, eg
@@ -553,11 +607,12 @@ function addConfigFile(existing /*, file path components ... */) {
553
607
  function makeConfigFileSymbols()
554
608
  {
555
609
  var configFileScope = Object.assign({}, bundleScope, {
556
- dcpConfig: existing,
557
- require: moduleSystem.createRequire(fullPath),
558
- url: (href) => new (require('dcp/dcp-url').DcpURL)(href),
559
- env: process.env,
560
- dcp: { 'dcp-env': require('dcp/dcp-env') }, /* used for web-compat confs */
610
+ __filename: fullPath,
611
+ dcpConfig: existing,
612
+ require: moduleSystem.createRequire(fullPath),
613
+ url: (href) => new (require('dcp/dcp-url').DcpURL)(href),
614
+ env: process.env,
615
+ dcp: { 'dcp-env': require('dcp/dcp-env') }, /* used for web-compat confs */
561
616
  });
562
617
 
563
618
  for (let key in existing)
@@ -867,8 +922,7 @@ function initTail(configFrags, options, finalBundleCode, finalBundleURL)
867
922
  addConfig(workingDcpConfig, configFrags.localConfig);
868
923
  addConfig(workingDcpConfig, originalDcpConfig);
869
924
 
870
- bundleScope.dcpConfig = workingDcpConfig;
871
- globalThis.dcpConfig = workingDcpConfig;
925
+ globalThis.dcpConfig = bundleScope.dcpConfig = workingDcpConfig;
872
926
  bundleScope.dcpConfig.build = require('dcp/build').config.build; /* dcpConfig.build deprecated mar 2023 /wg */
873
927
 
874
928
  /* 4 */
@@ -1179,13 +1233,20 @@ exports.createConfigFragments = async function dcpClient$$createConfigFragments(
1179
1233
  resourceDir = path.resolve(os.homedir(), '.dcp');
1180
1234
  let programName = options.programName;
1181
1235
  const progDir = process.mainModule ? path.dirname(process.mainModule.filename) : process.cwd();
1236
+ var bin = require.main?.filename && path.dirname(require.main.filename);
1182
1237
  var remoteConfig, remoteConfigKVIN;
1183
1238
  const internalConfig = require('dcp/dcp-config');
1184
- const defaultConfig = Object.assign({}, bootstrapConfig);
1239
+ const defaultConfig = {};
1240
+ addConfig(defaultConfig, bootstrapConfig);
1241
+ assert(defaultConfig.__bootstrapConfig);
1242
+ defaultConfig.__bootstrapConfig = false;
1185
1243
  addConfig(defaultConfig, internalConfig);
1186
1244
  addConfig(defaultConfig, KVIN.unmarshal(require('dcp/internal/dcp-default-config')));
1187
1245
  defaultConfig.scheduler = { location: new URL('https://scheduler.distributed.computer/') };
1188
1246
 
1247
+ if (bin && !bin.endsWith('/bin'))
1248
+ bin = false;
1249
+
1189
1250
  /* localConfig eventually overrides remoteConfig, and is the dcpConfig variable that is modified by
1190
1251
  * local include files. Pre-populating the graph edges with config nodes that always exist allows
1191
1252
  * config file writers to add properties to leaf nodes without having to construct the entire graph;
@@ -1237,10 +1298,10 @@ exports.createConfigFragments = async function dcpClient$$createConfigFragments(
1237
1298
  * any intelligent user can always change the source code to do whatever they please, but it does
1238
1299
  * make sense for campus configurations where sysadmins believe the machines are locked down, etc.
1239
1300
  */
1240
- let cn;
1241
1301
  addConfigFile(localConfig, etc, 'dcp/dcp-config');
1242
1302
  await addConfigRKey(localConfig, 'HKLM', 'dcp/dcp-config');
1243
- cn = addConfigFile(localConfig, options.configName && path.resolve(progDir, options.configName));
1303
+ addConfigFile(localConfig, bin, '../etc/dcp/dcp-config');
1304
+ addConfigFile(localConfig, options.configName && path.resolve(progDir, options.configName));
1244
1305
  addConfigFile(localConfig, resourceDir, 'dcp-config');
1245
1306
  await addConfigRKey(localConfig, 'HKCU', 'dcp/dcp-config');
1246
1307
  addConfigFile(localConfig, resourceDir, `${programName}/dcp-config`);
@@ -1255,8 +1316,6 @@ exports.createConfigFragments = async function dcpClient$$createConfigFragments(
1255
1316
  await addConfigRKey(localConfig, 'HKLM', 'dcp/override-dcp-config');
1256
1317
  await addConfigRKey(localConfig, 'HKLM', 'dcp-client/dcp-config'); /* legacy - used by screen saver, /wg sep'22 */
1257
1318
 
1258
- exports.__cn = cn; /* memoize for use by dcp-worker etc who need to know where local conf came from */
1259
-
1260
1319
  /* 5. Use the aggregate of the default and local configs to figure out where the scheduler is. Use
1261
1320
  * this to figure where the web config is and where an auto-update bundle would be if auto-update
1262
1321
  * were enabled.
@@ -1434,7 +1493,7 @@ function consumeArg(ckArg, bool)
1434
1493
  var ret;
1435
1494
  ckArg = '--dcp-' + ckArg;
1436
1495
 
1437
- for (let opt, i=2; i < process.argv.length; i++)
1496
+ for (let i=2; i < process.argv.length; i++)
1438
1497
  {
1439
1498
  const arg = process.argv[i];
1440
1499
 
@@ -40,6 +40,7 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
40
40
  'escape',
41
41
  'eval',
42
42
  'EvalError',
43
+ 'FinalizationRegistry',
43
44
  'Float32Array',
44
45
  'Float64Array',
45
46
  'Function',
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "dcp-client",
3
- "version": "5.1.8",
3
+ "version": "5.1.10",
4
4
  "dcp": {
5
- "version": "b23316107ba5b96b39a45d1ddda4f63f69dcc680",
5
+ "version": "2128884f53aa660dc56c4e8ba21e095f40518c32",
6
6
  "repository": "git@gitlab.com:Distributed-Compute-Protocol/dcp.git"
7
7
  },
8
8
  "description": "Core libraries for accessing DCP network",