@usecsv/react 0.2.16 → 0.2.18

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @usecsv/react v0.2.16
2
+ * @usecsv/react v0.2.18
3
3
  * (c) layercode
4
4
  * Released under the MIT License.
5
5
  */
@@ -10,7 +10,7 @@
10
10
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global["@usecsv/react"] = factory());
11
11
  })(this, (function () { 'use strict';
12
12
 
13
- /*! *****************************************************************************
13
+ /******************************************************************************
14
14
  Copyright (c) Microsoft Corporation.
15
15
 
16
16
  Permission to use, copy, modify, and/or distribute this software for any
@@ -38,12 +38,12 @@
38
38
  }
39
39
 
40
40
  /*!
41
- * @usecsv/js v0.12.0
41
+ * @usecsv/js v0.13.1
42
42
  * (c) layercode
43
43
  * Released under the MIT License.
44
44
  */
45
45
 
46
- /*! *****************************************************************************
46
+ /******************************************************************************
47
47
  Copyright (c) Microsoft Corporation.
48
48
 
49
49
  Permission to use, copy, modify, and/or distribute this software for any
@@ -251,14 +251,14 @@
251
251
  * Listens for "call" messages coming from the remote, executes the corresponding method, and
252
252
  * responds with the return value.
253
253
  */
254
- var connectCallReceiver = (info, methods, log) => {
254
+ var connectCallReceiver = (info, serializedMethods, log) => {
255
255
  const { localName, local, remote, originForSending, originForReceiving, } = info;
256
256
  let destroyed = false;
257
257
  const handleMessageEvent = (event) => {
258
258
  if (event.source !== remote || event.data.penpal !== MessageType.Call) {
259
259
  return;
260
260
  }
261
- if (event.origin !== originForReceiving) {
261
+ if (originForReceiving !== '*' && event.origin !== originForReceiving) {
262
262
  log(`${localName} received message from origin ${event.origin} which did not match expected origin ${originForReceiving}`);
263
263
  return;
264
264
  }
@@ -308,7 +308,7 @@
308
308
  }
309
309
  };
310
310
  };
311
- new Promise((resolve) => resolve(methods[methodName].apply(methods, args))).then(createPromiseHandler(Resolution.Fulfilled), createPromiseHandler(Resolution.Rejected));
311
+ new Promise((resolve) => resolve(serializedMethods[methodName].apply(serializedMethods, args))).then(createPromiseHandler(Resolution.Fulfilled), createPromiseHandler(Resolution.Rejected));
312
312
  };
313
313
  local.addEventListener(NativeEventType.Message, handleMessageEvent);
314
314
  return () => {
@@ -323,18 +323,86 @@
323
323
  */
324
324
  var generateId = () => ++id;
325
325
 
326
+ const KEY_PATH_DELIMITER = '.';
327
+ const keyPathToSegments = (keyPath) => keyPath ? keyPath.split(KEY_PATH_DELIMITER) : [];
328
+ const segmentsToKeyPath = (segments) => segments.join(KEY_PATH_DELIMITER);
329
+ const createKeyPath = (key, prefix) => {
330
+ const segments = keyPathToSegments(prefix || '');
331
+ segments.push(key);
332
+ return segmentsToKeyPath(segments);
333
+ };
334
+ /**
335
+ * Given a `keyPath`, set it to be `value` on `subject`, creating any intermediate
336
+ * objects along the way.
337
+ *
338
+ * @param {Object} subject The object on which to set value.
339
+ * @param {string} keyPath The key path at which to set value.
340
+ * @param {Object} value The value to store at the given key path.
341
+ * @returns {Object} Updated subject.
342
+ */
343
+ const setAtKeyPath = (subject, keyPath, value) => {
344
+ const segments = keyPathToSegments(keyPath);
345
+ segments.reduce((prevSubject, key, idx) => {
346
+ if (typeof prevSubject[key] === 'undefined') {
347
+ prevSubject[key] = {};
348
+ }
349
+ if (idx === segments.length - 1) {
350
+ prevSubject[key] = value;
351
+ }
352
+ return prevSubject[key];
353
+ }, subject);
354
+ return subject;
355
+ };
356
+ /**
357
+ * Given a dictionary of (nested) keys to function, flatten them to a map
358
+ * from key path to function.
359
+ *
360
+ * @param {Object} methods The (potentially nested) object to serialize.
361
+ * @param {string} prefix A string with which to prefix entries. Typically not intended to be used by consumers.
362
+ * @returns {Object} An map from key path in `methods` to functions.
363
+ */
364
+ const serializeMethods = (methods, prefix) => {
365
+ const flattenedMethods = {};
366
+ Object.keys(methods).forEach((key) => {
367
+ const value = methods[key];
368
+ const keyPath = createKeyPath(key, prefix);
369
+ if (typeof value === 'object') {
370
+ // Recurse into any nested children.
371
+ Object.assign(flattenedMethods, serializeMethods(value, keyPath));
372
+ }
373
+ if (typeof value === 'function') {
374
+ // If we've found a method, expose it.
375
+ flattenedMethods[keyPath] = value;
376
+ }
377
+ });
378
+ return flattenedMethods;
379
+ };
380
+ /**
381
+ * Given a map of key paths to functions, unpack the key paths to an object.
382
+ *
383
+ * @param {Object} flattenedMethods A map of key paths to functions to unpack.
384
+ * @returns {Object} A (potentially nested) map of functions.
385
+ */
386
+ const deserializeMethods = (flattenedMethods) => {
387
+ const methods = {};
388
+ for (const keyPath in flattenedMethods) {
389
+ setAtKeyPath(methods, keyPath, flattenedMethods[keyPath]);
390
+ }
391
+ return methods;
392
+ };
393
+
326
394
  /**
327
395
  * Augments an object with methods that match those defined by the remote. When these methods are
328
396
  * called, a "call" message will be sent to the remote, the remote's corresponding method will be
329
397
  * executed, and the method's return value will be returned via a message.
330
398
  * @param {Object} callSender Sender object that should be augmented with methods.
331
399
  * @param {Object} info Information about the local and remote windows.
332
- * @param {Array} methodNames Names of methods available to be called on the remote.
400
+ * @param {Array} methodKeyPaths Key paths of methods available to be called on the remote.
333
401
  * @param {Promise} destructionPromise A promise resolved when destroy() is called on the penpal
334
402
  * connection.
335
403
  * @returns {Object} The call sender object with methods that may be called.
336
404
  */
337
- var connectCallSender = (callSender, info, methodNames, destroyConnection, log) => {
405
+ var connectCallSender = (callSender, info, methodKeyPaths, destroyConnection, log) => {
338
406
  const { localName, local, remote, originForSending, originForReceiving, } = info;
339
407
  let destroyed = false;
340
408
  log(`${localName}: Connecting call sender`);
@@ -375,7 +443,8 @@
375
443
  event.data.id !== id) {
376
444
  return;
377
445
  }
378
- if (event.origin !== originForReceiving) {
446
+ if (originForReceiving !== '*' &&
447
+ event.origin !== originForReceiving) {
379
448
  log(`${localName} received message from origin ${event.origin} which did not match expected origin ${originForReceiving}`);
380
449
  return;
381
450
  }
@@ -399,10 +468,14 @@
399
468
  });
400
469
  };
401
470
  };
402
- methodNames.reduce((api, methodName) => {
403
- api[methodName] = createMethodProxy(methodName);
471
+ // Wrap each method in a proxy which sends it to the corresponding receiver.
472
+ const flattenedMethods = methodKeyPaths.reduce((api, name) => {
473
+ api[name] = createMethodProxy(name);
404
474
  return api;
405
- }, callSender);
475
+ }, {});
476
+ // Unpack the structure of the provided methods object onto the CallSender, exposing
477
+ // the methods in the same shape they were provided.
478
+ Object.assign(callSender, deserializeMethods(flattenedMethods));
406
479
  return () => {
407
480
  destroyed = true;
408
481
  };
@@ -411,7 +484,7 @@
411
484
  /**
412
485
  * Handles an ACK handshake message.
413
486
  */
414
- var handleAckMessageFactory = (methods, childOrigin, originForSending, destructor, log) => {
487
+ var handleAckMessageFactory = (serializedMethods, childOrigin, originForSending, destructor, log) => {
415
488
  const { destroy, onDestroy } = destructor;
416
489
  let destroyCallReceiver;
417
490
  let receiverMethodNames;
@@ -421,7 +494,7 @@
421
494
  // latest provided by the child.
422
495
  const callSender = {};
423
496
  return (event) => {
424
- if (event.origin !== childOrigin) {
497
+ if (childOrigin !== '*' && event.origin !== childOrigin) {
425
498
  log(`Parent: Handshake - Received ACK message from origin ${event.origin} which did not match expected origin ${childOrigin}`);
426
499
  return;
427
500
  }
@@ -438,7 +511,7 @@
438
511
  if (destroyCallReceiver) {
439
512
  destroyCallReceiver();
440
513
  }
441
- destroyCallReceiver = connectCallReceiver(info, methods, log);
514
+ destroyCallReceiver = connectCallReceiver(info, serializedMethods, log);
442
515
  onDestroy(destroyCallReceiver);
443
516
  // If the child reconnected, we need to remove the methods from the
444
517
  // previous call receiver off the sender.
@@ -457,16 +530,23 @@
457
530
  /**
458
531
  * Handles a SYN handshake message.
459
532
  */
460
- var handleSynMessageFactory = (log, methods, childOrigin, originForSending) => {
533
+ var handleSynMessageFactory = (log, serializedMethods, childOrigin, originForSending) => {
461
534
  return (event) => {
462
- if (event.origin !== childOrigin) {
535
+ // Under specific timing circumstances, we can receive an event
536
+ // whose source is null. This seems to happen when the child iframe is
537
+ // removed from the DOM about the same time it has sent the SYN event.
538
+ // https://github.com/Aaronius/penpal/issues/85
539
+ if (!event.source) {
540
+ return;
541
+ }
542
+ if (childOrigin !== '*' && event.origin !== childOrigin) {
463
543
  log(`Parent: Handshake - Received SYN message from origin ${event.origin} which did not match expected origin ${childOrigin}`);
464
544
  return;
465
545
  }
466
546
  log('Parent: Handshake - Received SYN, responding with SYN-ACK');
467
547
  const synAckMessage = {
468
548
  penpal: MessageType.SynAck,
469
- methodNames: Object.keys(methods),
549
+ methodNames: Object.keys(serializedMethods),
470
550
  };
471
551
  event.source.postMessage(synAckMessage, originForSending);
472
552
  };
@@ -537,8 +617,9 @@
537
617
  // must post messages with "*" as targetOrigin when sending messages.
538
618
  // https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Using_window.postMessage_in_extensions
539
619
  const originForSending = childOrigin === 'null' ? '*' : childOrigin;
540
- const handleSynMessage = handleSynMessageFactory(log, methods, childOrigin, originForSending);
541
- const handleAckMessage = handleAckMessageFactory(methods, childOrigin, originForSending, destructor, log);
620
+ const serializedMethods = serializeMethods(methods);
621
+ const handleSynMessage = handleSynMessageFactory(log, serializedMethods, childOrigin, originForSending);
622
+ const handleAckMessage = handleAckMessageFactory(serializedMethods, childOrigin, originForSending, destructor, log);
542
623
  const promise = new Promise((resolve, reject) => {
543
624
  const stopConnectionTimeout = startConnectionTimeout(timeout, destroy);
544
625
  const handleMessage = (event) => {
@@ -638,7 +719,7 @@
638
719
  return iframe;
639
720
  };
640
721
  var useCsvPlugin = function (_a) {
641
- var importerKey = _a.importerKey, user = _a.user, metadata = _a.metadata, onData = _a.onData, onRecordsInitial = _a.onRecordsInitial, onRecordEdit = _a.onRecordEdit, _b = _a.importerDisplay, importerDisplay = _b === void 0 ? "modal" : _b, onClose = _a.onClose, theme = _a.theme, batchSize = _a.batchSize;
722
+ var importerKey = _a.importerKey, user = _a.user, metadata = _a.metadata, onData = _a.onData, onRecordsInitial = _a.onRecordsInitial, onRecordEdit = _a.onRecordEdit, _b = _a.importerDisplay, importerDisplay = _b === void 0 ? "modal" : _b, onClose = _a.onClose, theme = _a.theme, batchSize = _a.batchSize, sampleFileURL = _a.sampleFileURL, _c = _a.downloadExampleButton, downloadExampleButton = _c === void 0 ? true : _c;
642
723
  var id = "usecsv-".concat(Math.round(Math.random() * 100000000));
643
724
  return whenDomReady().then(function () {
644
725
  var iframe = insertIframe(id, importerDisplay);
@@ -670,7 +751,18 @@
670
751
  var _a;
671
752
  (_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.classList.remove("loading");
672
753
  // eslint-disable-next-line dot-notation
673
- child["setParams"] && child["setParams"]({ importerKey: importerKey, user: user, metadata: metadata, importerDisplay: importerDisplay, theme: theme, batchSize: batchSize });
754
+ child["setParams"] &&
755
+ // eslint-disable-next-line dot-notation
756
+ child["setParams"]({
757
+ importerKey: importerKey,
758
+ user: user,
759
+ metadata: metadata,
760
+ importerDisplay: importerDisplay,
761
+ theme: theme,
762
+ batchSize: batchSize,
763
+ sampleFileURL: sampleFileURL,
764
+ downloadExampleButton: downloadExampleButton,
765
+ });
674
766
  });
675
767
  return iframeConnection;
676
768
  });