@kcuf/messenger 0.0.1

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 (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +82 -0
  3. package/dist/cjs/index.js +10 -0
  4. package/dist/cjs/messenger/index.js +192 -0
  5. package/dist/cjs/types/index.js +5 -0
  6. package/dist/cjs/util/build-payload-for-promise.js +14 -0
  7. package/dist/cjs/util/error-to-plain.js +27 -0
  8. package/dist/cjs/util/get-target-window.js +20 -0
  9. package/dist/cjs/util/index.js +34 -0
  10. package/dist/cjs/util/plain-to-error.js +19 -0
  11. package/dist/esm/index.js +5 -0
  12. package/dist/esm/index.js.map +1 -0
  13. package/dist/esm/messenger/index.js +188 -0
  14. package/dist/esm/messenger/index.js.map +1 -0
  15. package/dist/esm/types/index.js +2 -0
  16. package/dist/esm/types/index.js.map +1 -0
  17. package/dist/esm/util/build-payload-for-promise.js +9 -0
  18. package/dist/esm/util/build-payload-for-promise.js.map +1 -0
  19. package/dist/esm/util/error-to-plain.js +22 -0
  20. package/dist/esm/util/error-to-plain.js.map +1 -0
  21. package/dist/esm/util/get-target-window.js +15 -0
  22. package/dist/esm/util/get-target-window.js.map +1 -0
  23. package/dist/esm/util/index.js +5 -0
  24. package/dist/esm/util/index.js.map +1 -0
  25. package/dist/esm/util/plain-to-error.js +13 -0
  26. package/dist/esm/util/plain-to-error.js.map +1 -0
  27. package/dist/types/index.d.ts +3 -0
  28. package/dist/types/messenger/index.d.ts +40 -0
  29. package/dist/types/types/index.d.ts +33 -0
  30. package/dist/types/util/build-payload-for-promise.d.ts +2 -0
  31. package/dist/types/util/error-to-plain.d.ts +4 -0
  32. package/dist/types/util/get-target-window.d.ts +2 -0
  33. package/dist/types/util/index.d.ts +4 -0
  34. package/dist/types/util/plain-to-error.d.ts +1 -0
  35. package/package.json +62 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Alibaba Cloud
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # @kcuf/messenger
2
+
3
+ 封装 `postMessage`,有可以将 `postMessage` 转化成 `Promise` 的方法。
4
+
5
+ 注意:需要使用者自己保证 `Promise`。
6
+
7
+ ## 兼容性
8
+
9
+ `postMessage` 的 [兼容性](https://caniuse.com/#search=postMessage) 已经很好了,我们不做无谓判断。
10
+
11
+ ## INSTALL
12
+
13
+ ```shell
14
+ pnpm add @kcuf/messenger
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```ts
20
+ import messenger from '@kcuf/messenger';
21
+
22
+ messenger.emit<P>(type, payload); // 这里 P 是 `payload` 的类型定义
23
+ messenger.on<P>(type, payload => {
24
+ // ...
25
+ });
26
+ messenger.once<P>(type, payload => {
27
+ // ...
28
+ });
29
+
30
+ messenger.emitPromise<P>(type, payload);
31
+ messenger.onPromise<P>(type, payload => {
32
+ // ...
33
+ });
34
+ ```
35
+
36
+ ## APIs
37
+
38
+ * `messenger.emit(type, payload?)` 相当于 `postMessage({ type, payload })`
39
+ * `messenger.emitPromise(type, payload?)` 也是 `postMessage`,返回 `Promise`,必须要有 `onPromise` 来承接,否则此 Promise 将永远无法结束
40
+ * `messenger.on(type, fn)` 相当于 `addEventListener('message' ...` 并判断 `e.data.type === type`
41
+ * `messenger.once(type, fn)` 相当于 `on`,调用后自动解绑
42
+ * `messenger.onPromise(type, fn)` 相当于 `on`,调用后自动解绑
43
+
44
+ ## FAQ
45
+
46
+ ### 如何向其他窗口(比如父 `parent` 或 `top`,甚至某个 `iframe` 的窗口)进行 `postMessage`?
47
+
48
+ 给 `emit` / `emitPromise` 传第三个参数:
49
+
50
+ ```ts
51
+ messenger.emit(type, payload, {
52
+ targetWindow: 'parent' // 或 'top' 或其他 Window 对象
53
+ })
54
+ ```
55
+
56
+ ### 如何解绑?
57
+
58
+ 之所以没有类似 `off` 之类的 API,是因为 `on`、`once`、`onPromise` 这几个方法的返回就是一个无参的解绑函数。
59
+
60
+ 使用 hook 的例子
61
+
62
+ ```tsx
63
+ import {
64
+ useEffect
65
+ } from 'react';
66
+
67
+ import messenger from '@kcuf/messenger';
68
+
69
+ function useEffectOnMessengerXx(): void {
70
+ useEffect(() => messenger.on(_MESSAGE_TYPE_ENUM_, () => {
71
+ // ... do sth
72
+ }), []);
73
+ }
74
+ ```
75
+
76
+ ### 必须 `@kcuf/messenger#emit` 联合 `@kcuf/messenger#on` 使用吗?
77
+
78
+ 并不是。
79
+
80
+ 你还是可以用原生的 `addEventListener('messenger', ...)` 来监听,唯一的区别就是你要自己去判断 `e.data.type` 等。
81
+
82
+ 同样的,`@kcuf/messenger#on` 也可以接收裸写的 `postMessage({ type, data })`。只不过,你裸写 `postMessage` 很可能漏掉 `targetOrigin`。
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _messenger = _interopRequireDefault(require("./messenger"));
9
+ // the globally solo messenger instance
10
+ var _default = exports.default = new _messenger.default();
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
11
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
12
+ var _util = require("../util");
13
+ /**
14
+ * 封装 `postMessage`,全局只有一个监听事件
15
+ */
16
+ var Messenger = exports.default = /*#__PURE__*/function () {
17
+ function Messenger() {
18
+ var _this = this;
19
+ (0, _classCallCheck2.default)(this, Messenger);
20
+ (0, _defineProperty2.default)(this, "messageReceiverMap", {});
21
+ var thisWindow = (0, _util.getTargetWindow)();
22
+ thisWindow.addEventListener('message', function (e) {
23
+ var _e$data = e.data,
24
+ type = _e$data.type,
25
+ payload = _e$data.payload;
26
+ var _this$getReceivers = _this.getReceivers(type),
27
+ _this$getReceivers2 = (0, _slicedToArray2.default)(_this$getReceivers, 2),
28
+ receivers = _this$getReceivers2[0],
29
+ receiversOnce = _this$getReceivers2[1];
30
+ receivers.forEach(function (v) {
31
+ return v.fn(payload);
32
+ });
33
+ receiversOnce.forEach(function (v) {
34
+ return v.fn(payload);
35
+ });
36
+ receiversOnce.forEach(function (v) {
37
+ return _this.removeReceiver(type, v);
38
+ });
39
+ });
40
+ }
41
+
42
+ /**
43
+ * 广播消息,当传入的对象 payload 不是 plain 对象的时候(如 Error、function、DOMElement、JSX 等),这里会报错
44
+ * 「Uncaught DOMException: The object could not be cloned.」或 「DataCloneError: The object could not be cloned.」
45
+ * 这个错误不能吃掉,因为使用者需要同步地知道调用失败了
46
+ */
47
+ return (0, _createClass2.default)(Messenger, [{
48
+ key: "emit",
49
+ value:
50
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
51
+ function emit(type, payload) {
52
+ var _options$targetOrigin;
53
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
54
+ var theWindow = (0, _util.getTargetWindow)(options.targetWindow);
55
+ theWindow.postMessage({
56
+ type: type,
57
+ payload: payload
58
+ }, (_options$targetOrigin = options.targetOrigin) !== null && _options$targetOrigin !== void 0 ? _options$targetOrigin : theWindow.location.origin);
59
+ }
60
+
61
+ /**
62
+ * 广播事件,返回 Promise,必须要有 `onPromise` 来承接该事件,否则此 `Promise` 将永远无法结束
63
+ */
64
+ }, {
65
+ key: "emitPromise",
66
+ value:
67
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
68
+ function emitPromise(type, payload, options) {
69
+ var _this2 = this;
70
+ var payloadForPromise = (0, _util.buildPayloadForPromise)(type, payload);
71
+
72
+ // 触发 `onPromise` 的回调,不要放到 `new Promise` 内部,因为它可能会报错,这个错需要保持是同步的
73
+ this.emit(type, payloadForPromise, options);
74
+ return new Promise(function (resolve, reject) {
75
+ // `onPromise` 的回调返回的是 `Promise`,它 `resolve` 或 `reject` 都会广播一个以 `_dismiss_` 为类型的 `message`,
76
+ // 这里使用单次订阅是因为这个 `message` 只需要消费一次。
77
+ _this2.once(payloadForPromise._dismiss_, function (payloadBack) {
78
+ if (!payloadBack) {
79
+ // 一般来说不可能没有 payloadBack,但代码需要严谨
80
+ reject(new Error('Messenger:emitPromise MessengerPayloadPromiseBack is missing.'));
81
+ return;
82
+ }
83
+ var value = payloadBack.value,
84
+ error = payloadBack.error;
85
+ if (error) {
86
+ reject((0, _util.plainToError)(error));
87
+ } else {
88
+ resolve(value);
89
+ }
90
+ });
91
+ });
92
+ }
93
+
94
+ /**
95
+ * 注册回调,返回用于注销的方法
96
+ */
97
+ }, {
98
+ key: "on",
99
+ value:
100
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
101
+ function on(type, fn) {
102
+ return this.addReceiver(type, fn);
103
+ }
104
+
105
+ /**
106
+ * 注册单次回调,运行一次后将自动注销
107
+ */
108
+ }, {
109
+ key: "once",
110
+ value:
111
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
112
+ function once(type, fn) {
113
+ return this.addReceiver(type, fn, true);
114
+ }
115
+
116
+ /**
117
+ * 对 emitPromise 对应的 type 进行响应,这里关心的 payload 还是 emitPromise 所传入的 payload
118
+ */
119
+ }, {
120
+ key: "onPromise",
121
+ value:
122
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
123
+ function onPromise(type, fn) {
124
+ var _this3 = this;
125
+ return this.on(type, function (payload) {
126
+ if (!(payload !== null && payload !== void 0 && payload._dismiss_)) {
127
+ // 得到的 payload 下有 _dismiss_ 参数才响应,否则 pass
128
+ return;
129
+ }
130
+
131
+ // 这里广播是事件会被 `emitPromise` 方法内部的 once 消化
132
+ Promise.resolve(fn(payload.payload)).then(function (value) {
133
+ _this3.emit(payload._dismiss_, {
134
+ value: value
135
+ });
136
+ }, function (err) {
137
+ _this3.emit(payload._dismiss_, {
138
+ error: (0, _util.errorToPlain)(err)
139
+ });
140
+ });
141
+ });
142
+ }
143
+ }, {
144
+ key: "getReceivers",
145
+ value: function getReceivers(type) {
146
+ var _receiversForType$red;
147
+ var receiversForType = this.messageReceiverMap[type];
148
+ var receiversTuple = [[], []];
149
+ return (_receiversForType$red = receiversForType === null || receiversForType === void 0 ? void 0 : receiversForType.reduce(function (result, v) {
150
+ if (v.once) {
151
+ result[1].push(v);
152
+ } else {
153
+ result[0].push(v);
154
+ }
155
+ return result;
156
+ }, receiversTuple)) !== null && _receiversForType$red !== void 0 ? _receiversForType$red : receiversTuple;
157
+ }
158
+ }, {
159
+ key: "addReceiver",
160
+ value: function addReceiver(type, fn, once) {
161
+ var _this4 = this;
162
+ var receivers = this.messageReceiverMap[type];
163
+ if (!receivers) {
164
+ receivers = [];
165
+ this.messageReceiverMap[type] = receivers;
166
+ }
167
+ var receiver = {
168
+ fn: fn,
169
+ once: once
170
+ };
171
+ receivers.push(receiver);
172
+ return function () {
173
+ return _this4.removeReceiver(type, receiver);
174
+ };
175
+ }
176
+ }, {
177
+ key: "removeReceiver",
178
+ value: function removeReceiver(type, receiver) {
179
+ var receivers = this.messageReceiverMap[type];
180
+ if (!receivers) {
181
+ return;
182
+ }
183
+ var index = receivers.indexOf(receiver);
184
+ if (index >= 0) {
185
+ receivers.splice(index, 1);
186
+ }
187
+ if (!receivers.length) {
188
+ delete this.messageReceiverMap[type]; // eslint-disable-line @typescript-eslint/no-dynamic-delete
189
+ }
190
+ }
191
+ }]);
192
+ }();
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = buildPayloadForPromise;
7
+ function buildPayloadForPromise(type, payload) {
8
+ // 生成一个事件 type,用于 onPromise 里进行事件回调,因为 `postMessage` 无法传输 function,
9
+ // 所以只好经由这种「曲线救国」的方式。
10
+ return {
11
+ _dismiss_: "".concat(type, "/end/").concat(Date.now(), "-").concat(Math.round(Math.random() * 100000)),
12
+ payload: payload
13
+ };
14
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = errorToPlain;
8
+ var _isError2 = _interopRequireDefault(require("lodash/isError"));
9
+ var _forEach2 = _interopRequireDefault(require("lodash/forEach"));
10
+ /**
11
+ * `postMessage` 不支持传 Error 对象
12
+ */
13
+ function errorToPlain(err) {
14
+ if (!(0, _isError2.default)(err)) {
15
+ return err;
16
+ }
17
+ var plain = {};
18
+ (0, _forEach2.default)(err, function (v, k) {
19
+ plain[k] = v;
20
+ });
21
+ ['message', 'name', 'stack'].forEach(function (v) {
22
+ if (!plain[v]) {
23
+ plain[v] = err[v];
24
+ }
25
+ });
26
+ return plain;
27
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = getTargetWindow;
7
+ var _sandboxEscape = require("@kcuf/sandbox-escape");
8
+ var thisWindow = (0, _sandboxEscape.getWindow)();
9
+ function getTargetWindow() {
10
+ var _thisWindow$top;
11
+ var targetWindow = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : thisWindow;
12
+ switch (targetWindow) {
13
+ case 'top':
14
+ return (_thisWindow$top = thisWindow.top) !== null && _thisWindow$top !== void 0 ? _thisWindow$top : thisWindow;
15
+ case 'parent':
16
+ return thisWindow.parent;
17
+ default:
18
+ return targetWindow;
19
+ }
20
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ Object.defineProperty(exports, "buildPayloadForPromise", {
8
+ enumerable: true,
9
+ get: function get() {
10
+ return _buildPayloadForPromise.default;
11
+ }
12
+ });
13
+ Object.defineProperty(exports, "errorToPlain", {
14
+ enumerable: true,
15
+ get: function get() {
16
+ return _errorToPlain.default;
17
+ }
18
+ });
19
+ Object.defineProperty(exports, "getTargetWindow", {
20
+ enumerable: true,
21
+ get: function get() {
22
+ return _getTargetWindow.default;
23
+ }
24
+ });
25
+ Object.defineProperty(exports, "plainToError", {
26
+ enumerable: true,
27
+ get: function get() {
28
+ return _plainToError.default;
29
+ }
30
+ });
31
+ var _getTargetWindow = _interopRequireDefault(require("./get-target-window"));
32
+ var _buildPayloadForPromise = _interopRequireDefault(require("./build-payload-for-promise"));
33
+ var _errorToPlain = _interopRequireDefault(require("./error-to-plain"));
34
+ var _plainToError = _interopRequireDefault(require("./plain-to-error"));
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = plainToError;
8
+ var _isPlainObject2 = _interopRequireDefault(require("lodash/isPlainObject"));
9
+ var _forEach2 = _interopRequireDefault(require("lodash/forEach"));
10
+ function plainToError(o) {
11
+ if (!(0, _isPlainObject2.default)(o)) {
12
+ return o;
13
+ }
14
+ var err = new Error();
15
+ (0, _forEach2.default)(o, function (_v, k) {
16
+ err[k] = o[k];
17
+ });
18
+ return err;
19
+ }
@@ -0,0 +1,5 @@
1
+ import Messenger from './messenger';
2
+
3
+ // the globally solo messenger instance
4
+ export default new Messenger();
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["Messenger"],"sources":["../../src/index.ts"],"sourcesContent":["import Messenger from './messenger';\n\n// the globally solo messenger instance\nexport default new Messenger();\n"],"mappings":"AAAA,OAAOA,SAAS,MAAM,aAAa;;AAEnC;AACA,eAAe,IAAIA,SAAS,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,188 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
+ import _createClass from "@babel/runtime/helpers/createClass";
4
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
5
+ import { buildPayloadForPromise, errorToPlain, getTargetWindow, plainToError } from '../util';
6
+
7
+ /**
8
+ * 封装 `postMessage`,全局只有一个监听事件
9
+ */
10
+ var Messenger = /*#__PURE__*/function () {
11
+ function Messenger() {
12
+ var _this = this;
13
+ _classCallCheck(this, Messenger);
14
+ _defineProperty(this, "messageReceiverMap", {});
15
+ var thisWindow = getTargetWindow();
16
+ thisWindow.addEventListener('message', function (e) {
17
+ var _e$data = e.data,
18
+ type = _e$data.type,
19
+ payload = _e$data.payload;
20
+ var _this$getReceivers = _this.getReceivers(type),
21
+ _this$getReceivers2 = _slicedToArray(_this$getReceivers, 2),
22
+ receivers = _this$getReceivers2[0],
23
+ receiversOnce = _this$getReceivers2[1];
24
+ receivers.forEach(function (v) {
25
+ return v.fn(payload);
26
+ });
27
+ receiversOnce.forEach(function (v) {
28
+ return v.fn(payload);
29
+ });
30
+ receiversOnce.forEach(function (v) {
31
+ return _this.removeReceiver(type, v);
32
+ });
33
+ });
34
+ }
35
+
36
+ /**
37
+ * 广播消息,当传入的对象 payload 不是 plain 对象的时候(如 Error、function、DOMElement、JSX 等),这里会报错
38
+ * 「Uncaught DOMException: The object could not be cloned.」或 「DataCloneError: The object could not be cloned.」
39
+ * 这个错误不能吃掉,因为使用者需要同步地知道调用失败了
40
+ */
41
+ return _createClass(Messenger, [{
42
+ key: "emit",
43
+ value:
44
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
45
+ function emit(type, payload) {
46
+ var _options$targetOrigin;
47
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
48
+ var theWindow = getTargetWindow(options.targetWindow);
49
+ theWindow.postMessage({
50
+ type: type,
51
+ payload: payload
52
+ }, (_options$targetOrigin = options.targetOrigin) !== null && _options$targetOrigin !== void 0 ? _options$targetOrigin : theWindow.location.origin);
53
+ }
54
+
55
+ /**
56
+ * 广播事件,返回 Promise,必须要有 `onPromise` 来承接该事件,否则此 `Promise` 将永远无法结束
57
+ */
58
+ }, {
59
+ key: "emitPromise",
60
+ value:
61
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
62
+ function emitPromise(type, payload, options) {
63
+ var _this2 = this;
64
+ var payloadForPromise = buildPayloadForPromise(type, payload);
65
+
66
+ // 触发 `onPromise` 的回调,不要放到 `new Promise` 内部,因为它可能会报错,这个错需要保持是同步的
67
+ this.emit(type, payloadForPromise, options);
68
+ return new Promise(function (resolve, reject) {
69
+ // `onPromise` 的回调返回的是 `Promise`,它 `resolve` 或 `reject` 都会广播一个以 `_dismiss_` 为类型的 `message`,
70
+ // 这里使用单次订阅是因为这个 `message` 只需要消费一次。
71
+ _this2.once(payloadForPromise._dismiss_, function (payloadBack) {
72
+ if (!payloadBack) {
73
+ // 一般来说不可能没有 payloadBack,但代码需要严谨
74
+ reject(new Error('Messenger:emitPromise MessengerPayloadPromiseBack is missing.'));
75
+ return;
76
+ }
77
+ var value = payloadBack.value,
78
+ error = payloadBack.error;
79
+ if (error) {
80
+ reject(plainToError(error));
81
+ } else {
82
+ resolve(value);
83
+ }
84
+ });
85
+ });
86
+ }
87
+
88
+ /**
89
+ * 注册回调,返回用于注销的方法
90
+ */
91
+ }, {
92
+ key: "on",
93
+ value:
94
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
95
+ function on(type, fn) {
96
+ return this.addReceiver(type, fn);
97
+ }
98
+
99
+ /**
100
+ * 注册单次回调,运行一次后将自动注销
101
+ */
102
+ }, {
103
+ key: "once",
104
+ value:
105
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
106
+ function once(type, fn) {
107
+ return this.addReceiver(type, fn, true);
108
+ }
109
+
110
+ /**
111
+ * 对 emitPromise 对应的 type 进行响应,这里关心的 payload 还是 emitPromise 所传入的 payload
112
+ */
113
+ }, {
114
+ key: "onPromise",
115
+ value:
116
+ // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters
117
+ function onPromise(type, fn) {
118
+ var _this3 = this;
119
+ return this.on(type, function (payload) {
120
+ if (!(payload !== null && payload !== void 0 && payload._dismiss_)) {
121
+ // 得到的 payload 下有 _dismiss_ 参数才响应,否则 pass
122
+ return;
123
+ }
124
+
125
+ // 这里广播是事件会被 `emitPromise` 方法内部的 once 消化
126
+ Promise.resolve(fn(payload.payload)).then(function (value) {
127
+ _this3.emit(payload._dismiss_, {
128
+ value: value
129
+ });
130
+ }, function (err) {
131
+ _this3.emit(payload._dismiss_, {
132
+ error: errorToPlain(err)
133
+ });
134
+ });
135
+ });
136
+ }
137
+ }, {
138
+ key: "getReceivers",
139
+ value: function getReceivers(type) {
140
+ var _receiversForType$red;
141
+ var receiversForType = this.messageReceiverMap[type];
142
+ var receiversTuple = [[], []];
143
+ return (_receiversForType$red = receiversForType === null || receiversForType === void 0 ? void 0 : receiversForType.reduce(function (result, v) {
144
+ if (v.once) {
145
+ result[1].push(v);
146
+ } else {
147
+ result[0].push(v);
148
+ }
149
+ return result;
150
+ }, receiversTuple)) !== null && _receiversForType$red !== void 0 ? _receiversForType$red : receiversTuple;
151
+ }
152
+ }, {
153
+ key: "addReceiver",
154
+ value: function addReceiver(type, fn, once) {
155
+ var _this4 = this;
156
+ var receivers = this.messageReceiverMap[type];
157
+ if (!receivers) {
158
+ receivers = [];
159
+ this.messageReceiverMap[type] = receivers;
160
+ }
161
+ var receiver = {
162
+ fn: fn,
163
+ once: once
164
+ };
165
+ receivers.push(receiver);
166
+ return function () {
167
+ return _this4.removeReceiver(type, receiver);
168
+ };
169
+ }
170
+ }, {
171
+ key: "removeReceiver",
172
+ value: function removeReceiver(type, receiver) {
173
+ var receivers = this.messageReceiverMap[type];
174
+ if (!receivers) {
175
+ return;
176
+ }
177
+ var index = receivers.indexOf(receiver);
178
+ if (index >= 0) {
179
+ receivers.splice(index, 1);
180
+ }
181
+ if (!receivers.length) {
182
+ delete this.messageReceiverMap[type]; // eslint-disable-line @typescript-eslint/no-dynamic-delete
183
+ }
184
+ }
185
+ }]);
186
+ }();
187
+ export { Messenger as default };
188
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["buildPayloadForPromise","errorToPlain","getTargetWindow","plainToError","Messenger","_this","_classCallCheck","_defineProperty","thisWindow","addEventListener","e","_e$data","data","type","payload","_this$getReceivers","getReceivers","_this$getReceivers2","_slicedToArray","receivers","receiversOnce","forEach","v","fn","removeReceiver","_createClass","key","value","emit","_options$targetOrigin","options","arguments","length","undefined","theWindow","targetWindow","postMessage","targetOrigin","location","origin","emitPromise","_this2","payloadForPromise","Promise","resolve","reject","once","_dismiss_","payloadBack","Error","error","on","addReceiver","onPromise","_this3","then","err","_receiversForType$red","receiversForType","messageReceiverMap","receiversTuple","reduce","result","push","_this4","receiver","index","indexOf","splice","default"],"sources":["../../../src/messenger/index.ts"],"sourcesContent":["import {\n TFnOff,\n IMessageData,\n TMessengerCallback,\n TMessengerReceiverMap,\n IMessengerEmitOptions,\n IMessengerPayloadPromise,\n IMessengerPayloadPromiseBack,\n IMessengerReceiver\n} from '../types';\nimport {\n buildPayloadForPromise,\n errorToPlain,\n getTargetWindow,\n plainToError\n} from '../util';\n\n/**\n * 封装 `postMessage`,全局只有一个监听事件\n */\nexport default class Messenger {\n private messageReceiverMap: TMessengerReceiverMap = {};\n \n constructor() {\n const thisWindow = getTargetWindow();\n \n thisWindow.addEventListener('message', (e: MessageEvent<IMessageData>): void => {\n const {\n data: {\n type,\n payload\n }\n } = e;\n const [receivers, receiversOnce] = this.getReceivers(type);\n \n receivers.forEach(v => v.fn(payload));\n receiversOnce.forEach(v => v.fn(payload));\n \n receiversOnce.forEach(v => this.removeReceiver(type, v));\n });\n }\n \n /**\n * 广播消息,当传入的对象 payload 不是 plain 对象的时候(如 Error、function、DOMElement、JSX 等),这里会报错\n * 「Uncaught DOMException: The object could not be cloned.」或 「DataCloneError: The object could not be cloned.」\n * 这个错误不能吃掉,因为使用者需要同步地知道调用失败了\n */\n emit(type: string): void;\n emit(type: string, payload: undefined, options: IMessengerEmitOptions): void;\n emit<P = unknown>(type: string, payload: P, options?: IMessengerEmitOptions): void; // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters\n emit(type: string, payload?: unknown, options: IMessengerEmitOptions = {}): void {\n const theWindow = getTargetWindow(options.targetWindow);\n \n theWindow.postMessage({\n type,\n payload\n }, options.targetOrigin ?? theWindow.location.origin);\n }\n \n /**\n * 广播事件,返回 Promise,必须要有 `onPromise` 来承接该事件,否则此 `Promise` 将永远无法结束\n */\n emitPromise<T = void>(type: string): Promise<T>;\n emitPromise<T = void>(type: string, payload: undefined, options: IMessengerEmitOptions): Promise<T>;\n emitPromise<T = void, P = unknown>(type: string, payload: P, options?: IMessengerEmitOptions): Promise<T>; // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters\n emitPromise<T = void, P = unknown>(type: string, payload?: P, options?: IMessengerEmitOptions): Promise<T> {\n const payloadForPromise = buildPayloadForPromise<P | undefined>(type, payload);\n \n // 触发 `onPromise` 的回调,不要放到 `new Promise` 内部,因为它可能会报错,这个错需要保持是同步的\n this.emit<IMessengerPayloadPromise<P | undefined>>(type, payloadForPromise, options);\n \n return new Promise<T>((resolve, reject) => {\n // `onPromise` 的回调返回的是 `Promise`,它 `resolve` 或 `reject` 都会广播一个以 `_dismiss_` 为类型的 `message`,\n // 这里使用单次订阅是因为这个 `message` 只需要消费一次。\n this.once<IMessengerPayloadPromiseBack<T>>(payloadForPromise._dismiss_, (payloadBack?: IMessengerPayloadPromiseBack<T>): void => {\n if (!payloadBack) { // 一般来说不可能没有 payloadBack,但代码需要严谨\n reject(new Error('Messenger:emitPromise MessengerPayloadPromiseBack is missing.'));\n \n return;\n }\n \n const {\n value,\n error\n } = payloadBack;\n \n if (error) {\n reject(plainToError(error));\n } else {\n resolve(value as T);\n }\n });\n });\n }\n \n /**\n * 注册回调,返回用于注销的方法\n */\n on(type: string, fn: () => void): TFnOff;\n on<P = unknown>(type: string, fn: (payload: P) => void): TFnOff; // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters\n on(type: string, fn: (payload: unknown) => void): TFnOff {\n return this.addReceiver(type, fn);\n }\n \n /**\n * 注册单次回调,运行一次后将自动注销\n */\n once(type: string, fn: () => void): TFnOff;\n once<P = unknown>(type: string, fn: (payload: P) => void): TFnOff; // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters\n once<P = unknown>(type: string, fn: (payload: P) => void): TFnOff {\n return this.addReceiver(type, fn, true);\n }\n \n /**\n * 对 emitPromise 对应的 type 进行响应,这里关心的 payload 还是 emitPromise 所传入的 payload\n */\n onPromise<T = void>(type: string, fn: () => T | Promise<T>): TFnOff;\n onPromise<T, P = unknown>(type: string, fn: (payload: P) => T | Promise<T>): TFnOff; // eslint-disable-line @typescript-eslint/no-unnecessary-type-parameters\n onPromise<T, P = unknown>(type: string, fn: (payload: P) => T | Promise<T>): TFnOff {\n return this.on(type, (payload?: IMessengerPayloadPromise<P>) => {\n if (!payload?._dismiss_) { // 得到的 payload 下有 _dismiss_ 参数才响应,否则 pass\n return;\n }\n \n // 这里广播是事件会被 `emitPromise` 方法内部的 once 消化\n Promise.resolve(fn(payload.payload)).then((value: T) => {\n this.emit<IMessengerPayloadPromiseBack<T>>(payload._dismiss_, {\n value\n });\n }, (err: unknown) => {\n this.emit<IMessengerPayloadPromiseBack<T>>(payload._dismiss_, {\n error: errorToPlain(err)\n });\n });\n });\n }\n \n private getReceivers(type: string): [IMessengerReceiver[], IMessengerReceiver[]] {\n const receiversForType = this.messageReceiverMap[type];\n const receiversTuple: [IMessengerReceiver[], IMessengerReceiver[]] = [[], []];\n \n return receiversForType?.reduce((result: [IMessengerReceiver[], IMessengerReceiver[]], v) => {\n if (v.once) {\n result[1].push(v);\n } else {\n result[0].push(v);\n }\n \n return result;\n }, receiversTuple) ?? receiversTuple;\n }\n \n private addReceiver<P>(type: string, fn: TMessengerCallback<P>, once?: boolean): TFnOff {\n let receivers = this.messageReceiverMap[type];\n \n if (!receivers) {\n receivers = [];\n this.messageReceiverMap[type] = receivers;\n }\n \n const receiver: IMessengerReceiver = {\n fn,\n once\n };\n \n receivers.push(receiver);\n \n return () => this.removeReceiver(type, receiver);\n }\n \n private removeReceiver(type: string, receiver: IMessengerReceiver): void {\n const receivers = this.messageReceiverMap[type];\n \n if (!receivers) {\n return;\n }\n \n const index = receivers.indexOf(receiver);\n \n if (index >= 0) {\n receivers.splice(index, 1);\n }\n \n if (!receivers.length) {\n delete this.messageReceiverMap[type]; // eslint-disable-line @typescript-eslint/no-dynamic-delete\n }\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/types/index.ts"],"sourcesContent":["export type TTargetWindow = Window | 'top' | 'parent';\n\nexport interface IMessageData {\n type: string;\n payload: never;\n}\n\nexport interface IMessengerEmitOptions {\n targetWindow?: TTargetWindow;\n targetOrigin?: string;\n}\n\nexport type TMessengerCallback<P = unknown> = (payload: P) => void;\n\nexport interface IMessengerReceiver<P = never> {\n fn: TMessengerCallback<P>;\n once?: boolean;\n // priority?: number; // 优先级,越大越优先\n}\n\n/**\n * 为每个 MessageEvent.data 的 `type`,建立一个接收序列\n */\nexport type TMessengerReceiverMap = Record<string, IMessengerReceiver[]>;\n\n/**\n * 以 Promise 的形式广播事件的时候的 payload 包裹\n */\nexport interface IMessengerPayloadPromise<P = void> {\n _dismiss_: string; // onPromise 中会将此包裹打开,如果里边有该值,表明此事件合法,否则不会响应\n payload: P; // 原 payload\n}\n\n/**\n * `emitPromise` 和 `onPromise` 的纽带\n */\nexport interface IMessengerPayloadPromiseBack<T> {\n value?: T;\n error?: Record<string, unknown>; // 可能是 string、对象或 Error\n}\n\nexport type TFnOff = () => void;\n"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,9 @@
1
+ export default function buildPayloadForPromise(type, payload) {
2
+ // 生成一个事件 type,用于 onPromise 里进行事件回调,因为 `postMessage` 无法传输 function,
3
+ // 所以只好经由这种「曲线救国」的方式。
4
+ return {
5
+ _dismiss_: "".concat(type, "/end/").concat(Date.now(), "-").concat(Math.round(Math.random() * 100000)),
6
+ payload: payload
7
+ };
8
+ }
9
+ //# sourceMappingURL=build-payload-for-promise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-payload-for-promise.js","names":["buildPayloadForPromise","type","payload","_dismiss_","concat","Date","now","Math","round","random"],"sources":["../../../src/util/build-payload-for-promise.ts"],"sourcesContent":["import {\n IMessengerPayloadPromise\n} from '../types';\n\nexport default function buildPayloadForPromise<P = unknown>(type: string, payload: P): IMessengerPayloadPromise<P> {\n // 生成一个事件 type,用于 onPromise 里进行事件回调,因为 `postMessage` 无法传输 function,\n // 所以只好经由这种「曲线救国」的方式。\n return {\n _dismiss_: `${type}/end/${Date.now()}-${Math.round(Math.random() * 100000)}`,\n payload\n };\n}\n"],"mappings":"AAIA,eAAe,SAASA,sBAAsBA,CAAcC,IAAY,EAAEC,OAAU,EAA+B;EACjH;EACA;EACA,OAAO;IACLC,SAAS,KAAAC,MAAA,CAAKH,IAAI,WAAAG,MAAA,CAAQC,IAAI,CAACC,GAAG,CAAC,CAAC,OAAAF,MAAA,CAAIG,IAAI,CAACC,KAAK,CAACD,IAAI,CAACE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAE;IAC5EP,OAAO,EAAPA;EACF,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,22 @@
1
+ import _isError from 'lodash/isError';
2
+ import _forEach from 'lodash/forEach';
3
+
4
+ /**
5
+ * `postMessage` 不支持传 Error 对象
6
+ */
7
+ export default function errorToPlain(err) {
8
+ if (!_isError(err)) {
9
+ return err;
10
+ }
11
+ var plain = {};
12
+ _forEach(err, function (v, k) {
13
+ plain[k] = v;
14
+ });
15
+ ['message', 'name', 'stack'].forEach(function (v) {
16
+ if (!plain[v]) {
17
+ plain[v] = err[v];
18
+ }
19
+ });
20
+ return plain;
21
+ }
22
+ //# sourceMappingURL=error-to-plain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-to-plain.js","names":["_isError","_forEach","errorToPlain","err","plain","v","k","forEach"],"sources":["../../../src/util/error-to-plain.ts"],"sourcesContent":["import _isError from 'lodash/isError';\nimport _forEach from 'lodash/forEach';\n\n/**\n * `postMessage` 不支持传 Error 对象\n */\nexport default function errorToPlain(err: unknown): Record<string, unknown> {\n if (!_isError(err)) {\n return err as Record<string, unknown>;\n }\n \n const plain: Record<string, unknown> = {};\n \n _forEach(err, (v, k) => {\n plain[k] = v;\n });\n \n ['message', 'name', 'stack'].forEach(v => {\n if (!plain[v]) {\n plain[v] = (err as unknown as Record<string, unknown>)[v];\n }\n });\n \n return plain;\n}\n"],"mappings":"AAAA,OAAOA,QAAQ,MAAM,gBAAgB;AACrC,OAAOC,QAAQ,MAAM,gBAAgB;;AAErC;AACA;AACA;AACA,eAAe,SAASC,YAAYA,CAACC,GAAY,EAA2B;EAC1E,IAAI,CAACH,QAAQ,CAACG,GAAG,CAAC,EAAE;IAClB,OAAOA,GAAG;EACZ;EAEA,IAAMC,KAA8B,GAAG,CAAC,CAAC;EAEzCH,QAAQ,CAACE,GAAG,EAAE,UAACE,CAAC,EAAEC,CAAC,EAAK;IACtBF,KAAK,CAACE,CAAC,CAAC,GAAGD,CAAC;EACd,CAAC,CAAC;EAEF,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAACE,OAAO,CAAC,UAAAF,CAAC,EAAI;IACxC,IAAI,CAACD,KAAK,CAACC,CAAC,CAAC,EAAE;MACbD,KAAK,CAACC,CAAC,CAAC,GAAIF,GAAG,CAAwCE,CAAC,CAAC;IAC3D;EACF,CAAC,CAAC;EAEF,OAAOD,KAAK;AACd","ignoreList":[]}
@@ -0,0 +1,15 @@
1
+ import { getWindow } from '@kcuf/sandbox-escape';
2
+ var thisWindow = getWindow();
3
+ export default function getTargetWindow() {
4
+ var _thisWindow$top;
5
+ var targetWindow = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : thisWindow;
6
+ switch (targetWindow) {
7
+ case 'top':
8
+ return (_thisWindow$top = thisWindow.top) !== null && _thisWindow$top !== void 0 ? _thisWindow$top : thisWindow;
9
+ case 'parent':
10
+ return thisWindow.parent;
11
+ default:
12
+ return targetWindow;
13
+ }
14
+ }
15
+ //# sourceMappingURL=get-target-window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-target-window.js","names":["getWindow","thisWindow","getTargetWindow","_thisWindow$top","targetWindow","arguments","length","undefined","top","parent"],"sources":["../../../src/util/get-target-window.ts"],"sourcesContent":["import {\n getWindow\n} from '@kcuf/sandbox-escape';\n\nimport {\n TTargetWindow\n} from '../types';\n\nconst thisWindow = getWindow();\n\nexport default function getTargetWindow(targetWindow: TTargetWindow = thisWindow): Window {\n switch (targetWindow) {\n case 'top':\n return thisWindow.top ?? thisWindow;\n case 'parent':\n return thisWindow.parent;\n default:\n return targetWindow;\n }\n}\n"],"mappings":"AAAA,SACEA,SAAS,QACJ,sBAAsB;AAM7B,IAAMC,UAAU,GAAGD,SAAS,CAAC,CAAC;AAE9B,eAAe,SAASE,eAAeA,CAAA,EAAmD;EAAA,IAAAC,eAAA;EAAA,IAAlDC,YAA2B,GAAAC,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAGJ,UAAU;EAC9E,QAAQG,YAAY;IACpB,KAAK,KAAK;MACR,QAAAD,eAAA,GAAOF,UAAU,CAACO,GAAG,cAAAL,eAAA,cAAAA,eAAA,GAAIF,UAAU;IACrC,KAAK,QAAQ;MACX,OAAOA,UAAU,CAACQ,MAAM;IAC1B;MACE,OAAOL,YAAY;EACrB;AACF","ignoreList":[]}
@@ -0,0 +1,5 @@
1
+ export { default as getTargetWindow } from './get-target-window';
2
+ export { default as buildPayloadForPromise } from './build-payload-for-promise';
3
+ export { default as errorToPlain } from './error-to-plain';
4
+ export { default as plainToError } from './plain-to-error';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["default","getTargetWindow","buildPayloadForPromise","errorToPlain","plainToError"],"sources":["../../../src/util/index.ts"],"sourcesContent":["export { default as getTargetWindow } from './get-target-window';\nexport { default as buildPayloadForPromise } from './build-payload-for-promise';\nexport { default as errorToPlain } from './error-to-plain';\nexport { default as plainToError } from './plain-to-error';\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,eAAe,QAAQ,qBAAqB;AAChE,SAASD,OAAO,IAAIE,sBAAsB,QAAQ,6BAA6B;AAC/E,SAASF,OAAO,IAAIG,YAAY,QAAQ,kBAAkB;AAC1D,SAASH,OAAO,IAAII,YAAY,QAAQ,kBAAkB","ignoreList":[]}
@@ -0,0 +1,13 @@
1
+ import _isPlainObject from 'lodash/isPlainObject';
2
+ import _forEach from 'lodash/forEach';
3
+ export default function plainToError(o) {
4
+ if (!_isPlainObject(o)) {
5
+ return o;
6
+ }
7
+ var err = new Error();
8
+ _forEach(o, function (_v, k) {
9
+ err[k] = o[k];
10
+ });
11
+ return err;
12
+ }
13
+ //# sourceMappingURL=plain-to-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain-to-error.js","names":["_isPlainObject","_forEach","plainToError","o","err","Error","_v","k"],"sources":["../../../src/util/plain-to-error.ts"],"sourcesContent":["import _isPlainObject from 'lodash/isPlainObject';\nimport _forEach from 'lodash/forEach';\n\nexport default function plainToError(o: Record<string, unknown>): Error {\n if (!_isPlainObject(o)) {\n return o as unknown as Error;\n }\n \n const err = new Error();\n \n _forEach(o, (_v, k) => {\n (err as unknown as Record<string, unknown>)[k] = o[k];\n });\n \n return err;\n}\n"],"mappings":"AAAA,OAAOA,cAAc,MAAM,sBAAsB;AACjD,OAAOC,QAAQ,MAAM,gBAAgB;AAErC,eAAe,SAASC,YAAYA,CAACC,CAA0B,EAAS;EACtE,IAAI,CAACH,cAAc,CAACG,CAAC,CAAC,EAAE;IACtB,OAAOA,CAAC;EACV;EAEA,IAAMC,GAAG,GAAG,IAAIC,KAAK,CAAC,CAAC;EAEvBJ,QAAQ,CAACE,CAAC,EAAE,UAACG,EAAE,EAAEC,CAAC,EAAK;IACpBH,GAAG,CAAwCG,CAAC,CAAC,GAAGJ,CAAC,CAACI,CAAC,CAAC;EACvD,CAAC,CAAC;EAEF,OAAOH,GAAG;AACZ","ignoreList":[]}
@@ -0,0 +1,3 @@
1
+ import Messenger from './messenger';
2
+ declare const _default: Messenger;
3
+ export default _default;
@@ -0,0 +1,40 @@
1
+ import { TFnOff, IMessengerEmitOptions } from '../types';
2
+ /**
3
+ * 封装 `postMessage`,全局只有一个监听事件
4
+ */
5
+ export default class Messenger {
6
+ private messageReceiverMap;
7
+ constructor();
8
+ /**
9
+ * 广播消息,当传入的对象 payload 不是 plain 对象的时候(如 Error、function、DOMElement、JSX 等),这里会报错
10
+ * 「Uncaught DOMException: The object could not be cloned.」或 「DataCloneError: The object could not be cloned.」
11
+ * 这个错误不能吃掉,因为使用者需要同步地知道调用失败了
12
+ */
13
+ emit(type: string): void;
14
+ emit(type: string, payload: undefined, options: IMessengerEmitOptions): void;
15
+ emit<P = unknown>(type: string, payload: P, options?: IMessengerEmitOptions): void;
16
+ /**
17
+ * 广播事件,返回 Promise,必须要有 `onPromise` 来承接该事件,否则此 `Promise` 将永远无法结束
18
+ */
19
+ emitPromise<T = void>(type: string): Promise<T>;
20
+ emitPromise<T = void>(type: string, payload: undefined, options: IMessengerEmitOptions): Promise<T>;
21
+ emitPromise<T = void, P = unknown>(type: string, payload: P, options?: IMessengerEmitOptions): Promise<T>;
22
+ /**
23
+ * 注册回调,返回用于注销的方法
24
+ */
25
+ on(type: string, fn: () => void): TFnOff;
26
+ on<P = unknown>(type: string, fn: (payload: P) => void): TFnOff;
27
+ /**
28
+ * 注册单次回调,运行一次后将自动注销
29
+ */
30
+ once(type: string, fn: () => void): TFnOff;
31
+ once<P = unknown>(type: string, fn: (payload: P) => void): TFnOff;
32
+ /**
33
+ * 对 emitPromise 对应的 type 进行响应,这里关心的 payload 还是 emitPromise 所传入的 payload
34
+ */
35
+ onPromise<T = void>(type: string, fn: () => T | Promise<T>): TFnOff;
36
+ onPromise<T, P = unknown>(type: string, fn: (payload: P) => T | Promise<T>): TFnOff;
37
+ private getReceivers;
38
+ private addReceiver;
39
+ private removeReceiver;
40
+ }
@@ -0,0 +1,33 @@
1
+ export type TTargetWindow = Window | 'top' | 'parent';
2
+ export interface IMessageData {
3
+ type: string;
4
+ payload: never;
5
+ }
6
+ export interface IMessengerEmitOptions {
7
+ targetWindow?: TTargetWindow;
8
+ targetOrigin?: string;
9
+ }
10
+ export type TMessengerCallback<P = unknown> = (payload: P) => void;
11
+ export interface IMessengerReceiver<P = never> {
12
+ fn: TMessengerCallback<P>;
13
+ once?: boolean;
14
+ }
15
+ /**
16
+ * 为每个 MessageEvent.data 的 `type`,建立一个接收序列
17
+ */
18
+ export type TMessengerReceiverMap = Record<string, IMessengerReceiver[]>;
19
+ /**
20
+ * 以 Promise 的形式广播事件的时候的 payload 包裹
21
+ */
22
+ export interface IMessengerPayloadPromise<P = void> {
23
+ _dismiss_: string;
24
+ payload: P;
25
+ }
26
+ /**
27
+ * `emitPromise` 和 `onPromise` 的纽带
28
+ */
29
+ export interface IMessengerPayloadPromiseBack<T> {
30
+ value?: T;
31
+ error?: Record<string, unknown>;
32
+ }
33
+ export type TFnOff = () => void;
@@ -0,0 +1,2 @@
1
+ import { IMessengerPayloadPromise } from '../types';
2
+ export default function buildPayloadForPromise<P = unknown>(type: string, payload: P): IMessengerPayloadPromise<P>;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * `postMessage` 不支持传 Error 对象
3
+ */
4
+ export default function errorToPlain(err: unknown): Record<string, unknown>;
@@ -0,0 +1,2 @@
1
+ import { TTargetWindow } from '../types';
2
+ export default function getTargetWindow(targetWindow?: TTargetWindow): Window;
@@ -0,0 +1,4 @@
1
+ export { default as getTargetWindow } from './get-target-window';
2
+ export { default as buildPayloadForPromise } from './build-payload-for-promise';
3
+ export { default as errorToPlain } from './error-to-plain';
4
+ export { default as plainToError } from './plain-to-error';
@@ -0,0 +1 @@
1
+ export default function plainToError(o: Record<string, unknown>): Error;
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@kcuf/messenger",
3
+ "version": "0.0.1",
4
+ "description": "A simple and type safe post-message enhancement.",
5
+ "keywords": [
6
+ "postMessage",
7
+ "promise",
8
+ "emit",
9
+ "once",
10
+ "broadcast",
11
+ "subscribe"
12
+ ],
13
+ "license": "MIT",
14
+ "sideEffects": false,
15
+ "main": "dist/cjs/index.js",
16
+ "module": "dist/esm/index.js",
17
+ "types": "dist/types/index.d.ts",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/justnewbee/kcuf.git"
21
+ },
22
+ "homepage": "https://github.com/justnewbee/kcuf/tree/main/packages-util/messenger",
23
+ "bugs": "https://github.com/justnewbee/kcuf/issues",
24
+ "author": {
25
+ "name": "Jianchun Wang",
26
+ "email": "justnewbee@gmail.com"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "devDependencies": {
32
+ "@babel/cli": "^7.28.6",
33
+ "@babel/core": "^7.28.6",
34
+ "@babel/plugin-transform-runtime": "^7.28.5",
35
+ "@babel/preset-env": "^7.28.6",
36
+ "@babel/preset-typescript": "^7.28.5",
37
+ "@types/lodash": "^4.17.23",
38
+ "@vitest/coverage-v8": "^4.0.18",
39
+ "jsdom": "^27.4.0",
40
+ "rimraf": "^6.1.2",
41
+ "typescript": "^5.9.3",
42
+ "vitest": "^4.0.18",
43
+ "@kcuf/ts-config": "^0.1.0"
44
+ },
45
+ "peerDependencies": {
46
+ "@babel/runtime": ">=7.0.0"
47
+ },
48
+ "dependencies": {
49
+ "lodash": "^4.17.23",
50
+ "@kcuf/sandbox-escape": "^0.0.1"
51
+ },
52
+ "scripts": {
53
+ "build:clean": "rimraf dist",
54
+ "build:esm": "ESM=1 babel src -d dist/esm --extensions .ts,.tsx --source-maps",
55
+ "build:cjs": "ESM=0 babel src -d dist/cjs --extensions .ts,.tsx",
56
+ "build:types": "tsc -rootDir src --outDir dist/types --declaration --noEmit false --emitDeclarationOnly --isolatedModules false",
57
+ "build": "pnpm build:esm && pnpm build:cjs && pnpm build:types",
58
+ "watch": "pnpm build:esm -w",
59
+ "test": "vitest",
60
+ "test:cov": "vitest --coverage --coverage.include=src"
61
+ }
62
+ }