dcp-client 5.5.4 → 5.6.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/dcp-client.js CHANGED
@@ -14,17 +14,16 @@
14
14
  */
15
15
  'use strict';
16
16
  (function namespaceIIFE() {
17
-
18
17
  console.log(`%c
19
- _____ _____ ___________ _
20
- / ___|_ _| _ | ___ \\ | |
21
- \\ \`--. | | | | | | |_/ / | |
22
- \`--. \\ | | | | | | __/ |_|
23
- /\\__/ / | | \\ \\_/ / | _
24
- \\____/ \\_/ \\___/\\_| |_|
25
-
18
+ _____ _____ ___________ _
19
+ / ___|_ _| _ | ___ \\ | |
20
+ \\ \`--. | | | | | | |_/ / | |
21
+ \`--. \\ | | | | | | __/ |_|
22
+ /\\__/ / | | \\ \\_/ / | _
23
+ \\____/ \\_/ \\___/\\_| |_|
24
+
26
25
  %c
27
- The console is a browser feature intended for developers. If somebody told you to paste something here it may be a scam and your information could be stolen. Help us keep security in mind and keep your keystores safe.
26
+ The console is a browser feature intended for developers. If somebody told you to paste something here it may be a scam and your information could be stolen. Help us keep security in mind and keep your data safe.
28
27
  ~ DCP Team
29
28
 
30
29
  https://distributive.network/`, "font-weight: bold; font-size: 1.2em; color: #00a473;", "font-size: 1.2em;");
@@ -179,32 +178,124 @@ https://distributive.network/`, "font-weight: bold; font-size: 1.2em; color: #00
179
178
  }
180
179
  }
181
180
 
182
- /* Shim to make CommonJS Modules/2.0d8 environment (BravoJS, NobleJS) work with dcpClient in requireNative mode */
183
- function loadCJS2Shim()
181
+ /**
182
+ * Utility code for loading content relative to the Distributive CDN or Auth subsystems, using
183
+ * information about their locations gleaned from dcpConfig. Since the browser is unable to preload
184
+ * content based on these tags, page load will be slightly slower than using hard-coded tags. Note
185
+ * also that loading scripts this way has may cause them to load after the DOMContentLoadeded event
186
+ * has fired; caveat elitor.
187
+ *
188
+ * Examples:
189
+ * 1. load stylesheet relative to CDN
190
+ * <link type="text/css" type="stylesheet" dcp-cdn-href="/path/to/dcp-style.css">
191
+ * 2. load javascript relative to auth host
192
+ * <script dcp-auth-src="/path/to/dcp-script.js"></script>
193
+ */
194
+ function initMagicAttribs()
184
195
  {
185
- var shimScript = document.createElement('SCRIPT');
186
- var shimSrc = thisScript.getAttribute("shim") || (thisScript.src.replace('/dcp-client.js', '/cjs2-shim.js'));
187
-
188
- shimScript.setAttribute('type', 'text/javascript');
189
- shimScript.setAttribute('src', shimSrc);
190
- shimScript.setAttribute('id', '_dcp_client_cjs2_shim');
191
- shimScript.setAttribute('dcp-env', 'vanilla-web');
192
- shimScript.setAttribute('onerror', `alert('Error DCP-1003: Could not load cjs2 shim from URL ("${shimSrc}")')`);
193
-
194
- document.write(shimScript.outerHTML);
196
+ if (true
197
+ && thisScript.getAttribute('disable-magic-attribs')
198
+ && thisScript.getAttribute('disable-magic-attribs') !== 'false')
199
+ return;
200
+
201
+ function resolveWithQueryString(dcpUrl, urlTail)
202
+ {
203
+ const components = urlTail.split('?');
204
+ const pathname = components[0];
205
+ const queryString = components[1] ? ('?' + components[1]) : '';
206
+ return String(dcpUrl.resolve(pathname)) + queryString;
207
+ }
208
+
209
+ function scanForTags()
210
+ {
211
+ if (scanForTags.busy)
212
+ return;
213
+ scanForTags.busy = true;
214
+ try
215
+ {
216
+ const magic = { cdn: 'cdn', auth: 'pxAuth' }; /* xlate attrib fragment to conf prop */
217
+ for (const service in magic)
218
+ {
219
+ const srcAttribName = `dcp-${service}-src`;
220
+ const hrefAttribName = `dcp-${service}-href`;
221
+ const scripts = document.querySelectorAll(`SCRIPT[${srcAttribName}]`);
222
+ const location = dcpConfig[magic[service]].location;
223
+
224
+ for (const script of scripts)
225
+ {
226
+ const src = script.getAttribute(srcAttribName);
227
+ script.removeAttribute(srcAttribName);
228
+ script.src = resolveWithQueryString(location, src);
229
+ }
230
+
231
+ const links = document.querySelectorAll(`LINK[${hrefAttribName}]`);
232
+ for (const link of links)
233
+ {
234
+ const href = link.getAttribute(hrefAttribName);
235
+ link.removeAttribute(hrefAttribName);
236
+ link.href = resolveWithQueryString(location, href);
237
+ }
238
+ }
239
+ }
240
+ catch (error)
241
+ {
242
+ console.error(error);
243
+ throw error;
244
+ }
245
+ finally
246
+ {
247
+ scanForTags.busy = false;
248
+ }
249
+ } /* scanForTags */
250
+
251
+ document.addEventListener('readystatechange', scanForTags);
252
+
253
+ /* Watch DOM for mutations, initialize new magic relative attrib tags as they appear. */
254
+ function setupMutationObserver()
255
+ {
256
+ function handleMutation(mutationList, observer)
257
+ {
258
+ for (const mutation of mutationList)
259
+ {
260
+ if (true
261
+ && mutation.type === 'childList'
262
+ && mutation.target.querySelectorAll('LINK:not([href]), SCRIPT:not([src])').length)
263
+ {
264
+ scanForTags();
265
+ break;
266
+ }
267
+ }
268
+ }
269
+
270
+ scanForTags(); /* handle mutations that were missed to due startup races */
271
+ const observer = new MutationObserver(handleMutation);
272
+ observer.observe(document.head, { childList: true, subtree: true });
273
+
274
+ if (document.body)
275
+ observer.observe(document.body, { childList: true, subtree: true });
276
+ else
277
+ window.addEventListener('DOMContentLoaded', () => observer.observe(document.body, { childList: true, subtree: true }));
278
+ }
195
279
  }
196
280
 
197
- /**
198
- * This function is never run directly; it is stringified and emitted in a
199
- * SCRIPT tag that is injected into the document. As such, it cannot close
281
+ /**
282
+ * This function is never run directly; it is stringified and emitted in a
283
+ * SCRIPT tag that is injected into the document. As such, it cannot close
200
284
  * over any non-global variables.
201
285
  */
202
- function bundleReadyIIFE() {
203
- const configScript = document.getElementById("_dcp_config");
286
+ async function bundleReadyIIFE() {
204
287
  const bundleScript = document.getElementById("_dcp_client_bundle");
205
288
  const ready = bundleScript.getAttribute('onready');
206
289
  const dcp = bundleScript.exports;
207
290
  const KVIN = new dcp.kvin.KVIN();
291
+ const thisScript = document.currentScript;
292
+ const ihc = (true
293
+ && thisScript.hasAttribute('inherit-config')
294
+ && thisScript.getAttribute('inherit-config') !== 'false'
295
+ && (window !== top || window.opener));
296
+
297
+ KVIN.userCtors.dcpUrl$$DcpURL = dcp['dcp-url'].DcpURL;
298
+ KVIN.userCtors.dcpEth$$Address = dcp.wallet.Address;
208
299
 
209
300
  if (typeof module !== 'undefined' && typeof module.declare !== 'undefined')
210
301
  require('/internal/dcp/cjs2-shim').init(bundleScript.exports); /* CommonJS Modules/2.0d8 environment (BravoJS, NobleJS) */
@@ -212,33 +303,47 @@ https://distributive.network/`, "font-weight: bold; font-size: 1.2em; color: #00
212
303
  window.dcp = dcp; /* vanilla JS */
213
304
 
214
305
  /** Let protocol know where we got out config from, so origin can be reasoned about vis a vis security */
215
- dcp.protocol.setSchedulerConfigLocation_fromScript(configScript);
306
+ if (ihc)
307
+ dcp.protocol.setSchedulerConfigLocation_fromScript((window.opener || top).document.getElementById("_dcp_config"));
308
+ else
309
+ dcp.protocol.setSchedulerConfigLocation_fromScript(document.getElementById("_dcp_config"));
216
310
 
217
311
  /**
218
- * Slide baked-in config underneath the remote config to provide default values.
312
+ * Slide baked-in config underneath the remote config to provide default values, unless inheriting
313
+ * config, in which case we deep-clone to get correct constructors for this context etc
219
314
  */
220
- KVIN.userCtors.dcpUrl$$DcpURL = dcp['dcp-url'].DcpURL;
221
- KVIN.userCtors.dcpEth$$Address = dcp.wallet.Address;
222
- dcpConfig = dcp['dcp-config'] = dcp.utils.leafMerge(KVIN.unmarshal(dcp['dcp-default-config']), dcp['dcp-config']);
223
-
224
- /**
225
- * Transform instances of Address-like values into Addresses. Necessary since
226
- * the config can't access the Address class before the bundle is loaded.
227
- */
228
- dcp.wallet.Address.patchUp(dcpConfig);
229
- dcp['dcp-url'].patchup(dcpConfig);
315
+ if (!ihc)
316
+ {
317
+ dcpConfig = dcp['dcp-config'] = dcp.utils.leafMerge(KVIN.unmarshal(dcp['dcp-default-config']), dcp['dcp-config']);
318
+ /**
319
+ * Transform instances of Address-like values into Addresses. Necessary since
320
+ * the config can't access the Address class before the bundle is loaded.
321
+ */
322
+ dcp.wallet.Address.patchUp(dcpConfig);
323
+ dcp['dcp-url'].patchup(dcpConfig);
324
+ }
325
+ else
326
+ {
327
+ const parent = window.opener || top;
328
+ dcpConfig = dcp['dcp-config'] = KVIN.unmarshal(await parent.dcp.kvin.marshalAsync(parent.dcpConfig));
329
+ }
230
330
 
231
331
  if (ready)
232
332
  window.setTimeout(function bundleReadyFire() { let indirectEval=eval; indirectEval(ready) }, 0);
333
+ window.dispatchEvent(new CustomEvent('dcpclientbundleready', { detail: { dcp, KVIN } })); /* fires as soon as bundle has been parsed */
334
+ window.dispatchEvent(new CustomEvent('dcpclientready', { detail: dcp })); /* fires as soon as bundle has been parsed and dcp-config is ready */
233
335
  }
234
336
 
235
- /* Load dcp-client bundle from the same location as this module, extract the exports
337
+ /* Load dcp-client bundle from the same location as this module, extract the exports
236
338
  * from it, and attach them to the global dcp object.
339
+ *
340
+ * @param {boolean} ihc true if we should inject the inherit-config attribute onto the
341
+ * bundle's script element
237
342
  */
238
- function loadBundle(shimCallback) {
343
+ function loadBundle(ihc) {
239
344
  var bundleScript = document.createElement('SCRIPT');
240
345
  var bundleSrc = thisScript.getAttribute("bundle") || (thisScript.src.replace('/dcp-client.js', '/dist/dcp-client-bundle.js'));
241
-
346
+
242
347
  bundleScript.setAttribute('type', 'text/javascript');
243
348
  bundleScript.setAttribute('src', bundleSrc);
244
349
  bundleScript.setAttribute('id', '_dcp_client_bundle');
@@ -249,7 +354,7 @@ https://distributive.network/`, "font-weight: bold; font-size: 1.2em; color: #00
249
354
  bundleScript.setAttribute('onready', thisScript.getAttribute('onready'));
250
355
  thisScript.removeAttribute('onready');
251
356
  document.write(bundleScript.outerHTML);
252
- document.write(`<script id='_dcp_bundleReadyIIFE'>/* bundleReadyIIFE */;(${bundleReadyIIFE})()</scr` + `ipt>`);
357
+ document.write(`<script id='_dcp_bundleReadyIIFE' inherit-config="${ihc}">/* bundleReadyIIFE */;(${bundleReadyIIFE})()</scr` + `ipt>`);
253
358
  bundleScript = document.getElementById('_dcp_client_bundle');
254
359
  if (bundleScript)
255
360
  bundleScript.onerror = function(e) {
@@ -258,44 +363,58 @@ https://distributive.network/`, "font-weight: bold; font-size: 1.2em; color: #00
258
363
  };
259
364
  }
260
365
 
261
- /* Load the default appearance CSS. This lets us style trivial DCP programs so that the modals use
262
- * e.g. so that popups in wallet API look right.
366
+ /* Load the default CSS. This is enough to make the modals, etc, work without affecting the user
367
+ * program appearance. If desired, a full DCP style can be loaded from the Distributive CDN; i.e.
368
+ * <link rel="stylesheet" type="text/css" dcp-cdn-href="/css/dcp-style.css">
263
369
  */
264
370
  function loadCSS () {
265
- // Best-effort to find cdn location
266
- // 1. use dcpConfig.cdn.location
267
- // 2. use dcpConfigHref to infer where cdn is (when dcpConfigHref is loading through some service)
268
- // 3. use thisScriptURL to infer where cdn is
269
- var cdnOrigin;
270
- if (typeof dcpConfig === 'object' && dcpConfig.cdn?.location?.href)
271
- cdnOrigin = dcpConfig.cdn.location.origin
272
- else if (typeof dcpConfigHref === 'string' && dcpConfigHref.match(/(portal|auth|cdn|scheduler)\.?/))
273
- {
274
- cdnOrigin = new URL(dcpConfigHref).origin;
275
- cdnOrigin = cdnOrigin.replace(/(portal|auth|cdn|scheduler)\.?/, 'cdn.');
276
- }
277
- else
278
- {
279
- cdnOrigin = thisScriptURL.origin.replace(/(portal|auth|cdn|scheduler)\.?/, 'cdn.');
280
- if (!cdnOrigin.match(/^(http|https):\/\/cdn/)) // Loaded directly
281
- cdnOrigin = cdnOrigin.replace(/^(http:\/\/|https:\/\/)/, '$1cdn.');
282
- }
283
-
371
+ var href = thisScript.getAttribute('load-css');
372
+ if (href === 'false')
373
+ return;
374
+ href = thisScriptURL.origin + thisScriptURL.pathname
375
+ .replace(/\/dcp-client\/dcp-client.js$/, '/dcp-client/assets/dcp-client.css');
284
376
  const head = document.getElementsByTagName('head')[0];
285
377
  let styleLink = document.createElement('link');
286
378
  styleLink.rel = 'stylesheet';
287
- styleLink.href = cdnOrigin + '/css/dcp-modals.css';
288
- styleLink.id = 'dcp-modal-styles'
379
+ styleLink.href = href;
289
380
  head.appendChild(styleLink);
290
381
  }
291
382
 
292
- let shimCallback;
293
- if (typeof module === 'object' && typeof module.declare === 'function')
294
- shimCallback = loadCJS2Shim(); /* BravoJS, NobleJS, etc - set up for requireNative */
383
+ /**
384
+ * Determine if an inherit attribute has been set on an element IFF we are in a heritable context;
385
+ * eg the document has been loaded into a popup or frame with the same origin.
386
+ */
387
+ function hasInheritAttribute(element, suffix)
388
+ {
389
+ return (true
390
+ && element.hasAttribute(`inherit-${suffix}`)
391
+ && element.getAttribute(`inherit-${suffix}`) !== 'false'
392
+ && (window !== top || window.opener)
393
+ && window.location.origin == (window.opener || top).location.origin);
394
+ }
395
+
396
+ const ihc = hasInheritAttribute(thisScript, 'config');
397
+ if (!ihc)
398
+ loadConfig();
399
+ else
400
+ window.dcpConfig = (window.opener || top).dcpConfig; /* temp plumbing, re-written in bundleReadyIIFE */
295
401
 
296
- loadConfig();
402
+ initMagicAttribs();
297
403
  loadCSS();
298
- loadBundle(shimCallback);
404
+
405
+ if (hasInheritAttribute(thisScript, 'identity'))
406
+ {
407
+ window.addEventListener('dcpclientbundleready', ev => {
408
+ const { dcp, KVIN } = ev.detail;
409
+ const inheritedIdentity = (window.opener || top).dcp.identity.get();
410
+ dcp.identity.set(inheritedIdentity);
411
+ });
412
+ }
413
+
414
+ /* warning: bundle inheritance has context problems; avoid using this feature for non-trivial tasks /wg sep 2025 */
415
+ if (hasInheritAttribute(thisScript, 'bundle'))
416
+ window.dcp = window.opener ? window.opener.dcp : top.dcp;
417
+ else
418
+ loadBundle(ihc);
299
419
  }
300
- })();
301
-
420
+ }) /* namespaceIIFE */();