respondable-event 0.1.1-main.29f6ea6

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 William Wong
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,77 @@
1
+ # `respondable-event`
2
+
3
+ Enables event listeners to send at-most-one response back to the dispatching `EventTarget`.
4
+
5
+ ## Background
6
+
7
+ Inspired by the [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent) which is available to [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) only, the `RespondableEvent` allows event listeners to send a response back to the dispatching [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget).
8
+
9
+ This is useful in scenarios where custom elements need to communicate with the host asynchronously or infrequently. For persisted messaging, use `RespondableEvent` to set up [`Channel Messaging`](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API/Using_channel_messaging).
10
+
11
+ ## How to use
12
+
13
+ The code snippet below send an `"authenticate"` event to the hosting page.
14
+
15
+ ```ts
16
+ const event = new RespondableEvent(
17
+ 'authenticate',
18
+ token => {
19
+ if (token) {
20
+ // Responded.
21
+ } else {
22
+ // No response.
23
+ }
24
+ },
25
+ { bubbles: true } // Make the event available to all ancestors.
26
+ );
27
+
28
+ element.dispatchEvent(event);
29
+
30
+ // If no response has been made or pending, calling the `checkResponse()` function
31
+ // will call the callback function with undefined.
32
+ event.checkResponse();
33
+
34
+ const token = await authenticate.promise;
35
+ ```
36
+
37
+ In the hosting page, the following code snippet respond with a token.
38
+
39
+ ```ts
40
+ window.addEventListener('authenticate', event => {
41
+ if (event.target === myTrustedElement && event.request === myTrustedRequest) {
42
+ event.respondWith('Hello, World!');
43
+ event.stopPropagation();
44
+ }
45
+ });
46
+ ```
47
+
48
+ ### New `checkResponse` function
49
+
50
+ The `checkResponse()` function guarantees the `callback` function must be called exactly once. This helps reduce code complexity and its design is similar to the [`HTMLFormElement.checkValidity()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/checkValidity) function:
51
+
52
+ - If a prior response has been made, `checkResponse()` will return `true`.
53
+ - If no prior response is made, `checkResponse()` will call the `callback` function with `undefined`, and return `false`.
54
+
55
+ It is recommended to call `checkResponse()` after `dispatchEvent()` to guarantee the `callback` function is being called regardless `respondWith()` is called or not.
56
+
57
+ ## Designs
58
+
59
+ ### Callback function in constructor
60
+
61
+ The callback function follows the pattern found in [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/MutationObserver) and other observer classes. It is designed to limit the audience who can receive the response to the creator of the event.
62
+
63
+ ## Behaviors
64
+
65
+ ### Differences between `RespondableEvent` and `FetchEvent`?
66
+
67
+ - `RespondableEvent` extends from `Event`, where `FetchEvent` extends from `ExtendableEvent` and `Event`
68
+ - `request` property is optional in `RespondableEvent` where it is required in `FetchEvent`
69
+ - [`checkResponse()` function](#new-checkresponse-function) is new in `RespondableEvent`
70
+
71
+ ## Contributions
72
+
73
+ Like us? [Star](https://github.com/compulim/respondable-event/stargazers) us.
74
+
75
+ Want to make it better? [File](https://github.com/compulim/respondable-event/issues) us an issue.
76
+
77
+ Don't like something you see? [Submit](https://github.com/compulim/respondable-event/pulls) a pull request.
@@ -0,0 +1,28 @@
1
+ interface RespondableEventInit<T> extends EventInit {
2
+ /** The object that would have triggered the event handler. */
3
+ request: T;
4
+ }
5
+ declare class RespondableEvent<T, R = undefined> extends Event {
6
+ #private;
7
+ constructor(
8
+ /** A string with the name of the event. */
9
+ type: string,
10
+ /** A function which will be called upon respond. */
11
+ callback: (result: T | undefined, event: RespondableEvent<T, R>) => void,
12
+ /**
13
+ * An object that, in addition of the properties defined in [Event()](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event), can have the following properties:
14
+ *
15
+ * - `request`: The object that would have triggered the event handler.
16
+ */
17
+ eventInitDict?: RespondableEventInit<R> | undefined);
18
+ /** The object that would have triggered the event handler. */
19
+ get request(): R | undefined;
20
+ checkResponse(): boolean;
21
+ respondWith(
22
+ /**
23
+ * An object or a Promise that resolves to an object.
24
+ */
25
+ response: T | Promise<T>): void;
26
+ }
27
+
28
+ export { RespondableEvent };
@@ -0,0 +1,28 @@
1
+ interface RespondableEventInit<T> extends EventInit {
2
+ /** The object that would have triggered the event handler. */
3
+ request: T;
4
+ }
5
+ declare class RespondableEvent<T, R = undefined> extends Event {
6
+ #private;
7
+ constructor(
8
+ /** A string with the name of the event. */
9
+ type: string,
10
+ /** A function which will be called upon respond. */
11
+ callback: (result: T | undefined, event: RespondableEvent<T, R>) => void,
12
+ /**
13
+ * An object that, in addition of the properties defined in [Event()](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event), can have the following properties:
14
+ *
15
+ * - `request`: The object that would have triggered the event handler.
16
+ */
17
+ eventInitDict?: RespondableEventInit<R> | undefined);
18
+ /** The object that would have triggered the event handler. */
19
+ get request(): R | undefined;
20
+ checkResponse(): boolean;
21
+ respondWith(
22
+ /**
23
+ * An object or a Promise that resolves to an object.
24
+ */
25
+ response: T | Promise<T>): void;
26
+ }
27
+
28
+ export { RespondableEvent };
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ RespondableEvent: () => RespondableEvent_default
24
+ });
25
+ module.exports = __toCommonJS(src_exports);
26
+
27
+ // src/RespondableEvent.ts
28
+ var RespondableEvent = class extends Event {
29
+ constructor(type, callback, eventInitDict) {
30
+ super(type, eventInitDict);
31
+ this.#request = eventInitDict?.request;
32
+ const resolvers = Promise.withResolvers();
33
+ resolvers.promise.then((result) => callback(result, this));
34
+ this.#resolve = (result) => {
35
+ if (this.#resolved) {
36
+ throw new Error("respondWith() has already been invoked.");
37
+ } else if (!this.target) {
38
+ throw new Error("respondWith() can only be called after dispatched.");
39
+ }
40
+ resolvers.resolve(result);
41
+ this.#resolved = true;
42
+ };
43
+ }
44
+ #request;
45
+ #resolve;
46
+ #resolved = false;
47
+ /** The object that would have triggered the event handler. */
48
+ get request() {
49
+ return this.#request;
50
+ }
51
+ checkResponse() {
52
+ const wasResolved = this.#resolved;
53
+ wasResolved || this.#resolve(void 0);
54
+ return wasResolved;
55
+ }
56
+ respondWith(response) {
57
+ this.#resolve(response);
58
+ }
59
+ };
60
+ var RespondableEvent_default = RespondableEvent;
61
+ // Annotate the CommonJS export names for ESM import in node:
62
+ 0 && (module.exports = {
63
+ RespondableEvent
64
+ });
65
+ //# sourceMappingURL=respondable-event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/RespondableEvent.ts"],"sourcesContent":["import RespondableEvent from './RespondableEvent.ts';\n\nexport { RespondableEvent };\n","interface RespondableEventInit<T> extends EventInit {\n /** The object that would have triggered the event handler. */\n request: T;\n}\n\nclass RespondableEvent<T, R = undefined> extends Event {\n constructor(\n /** A string with the name of the event. */\n type: string,\n /** A function which will be called upon respond. */\n callback: (result: T | undefined, event: RespondableEvent<T, R>) => void,\n /**\n * An object that, in addition of the properties defined in [Event()](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event), can have the following properties:\n *\n * - `request`: The object that would have triggered the event handler.\n */\n eventInitDict?: RespondableEventInit<R> | undefined\n ) {\n super(type, eventInitDict);\n\n this.#request = eventInitDict?.request;\n\n const resolvers = Promise.withResolvers<T | undefined>();\n\n resolvers.promise.then(result => callback(result, this));\n\n this.#resolve = (result: Promise<T> | T | undefined) => {\n if (this.#resolved) {\n throw new Error('respondWith() has already been invoked.');\n } else if (!this.target) {\n throw new Error('respondWith() can only be called after dispatched.');\n }\n\n resolvers.resolve(result);\n\n this.#resolved = true;\n };\n }\n\n #request: R | undefined;\n #resolve: (result: Promise<T> | T | undefined) => void;\n #resolved: boolean = false;\n\n /** The object that would have triggered the event handler. */\n get request(): R | undefined {\n return this.#request;\n }\n\n checkResponse(): boolean {\n const wasResolved = this.#resolved;\n\n wasResolved || this.#resolve(undefined);\n\n return wasResolved;\n }\n\n respondWith(\n /**\n * An object or a Promise that resolves to an object.\n */\n response: T | Promise<T>\n ): void {\n this.#resolve(response);\n }\n}\n\nexport default RespondableEvent;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,IAAM,mBAAN,cAAiD,MAAM;AAAA,EACrD,YAEE,MAEA,UAMA,eACA;AACA,UAAM,MAAM,aAAa;AAEzB,SAAK,WAAW,eAAe;AAE/B,UAAM,YAAY,QAAQ,cAA6B;AAEvD,cAAU,QAAQ,KAAK,YAAU,SAAS,QAAQ,IAAI,CAAC;AAEvD,SAAK,WAAW,CAAC,WAAuC;AACtD,UAAI,KAAK,WAAW;AAClB,cAAM,IAAI,MAAM,yCAAyC;AAAA,MAC3D,WAAW,CAAC,KAAK,QAAQ;AACvB,cAAM,IAAI,MAAM,oDAAoD;AAAA,MACtE;AAEA,gBAAU,QAAQ,MAAM;AAExB,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA,EAEA;AAAA,EACA;AAAA,EACA,YAAqB;AAAA;AAAA,EAGrB,IAAI,UAAyB;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,gBAAyB;AACvB,UAAM,cAAc,KAAK;AAEzB,mBAAe,KAAK,SAAS,MAAS;AAEtC,WAAO;AAAA,EACT;AAAA,EAEA,YAIE,UACM;AACN,SAAK,SAAS,QAAQ;AAAA,EACxB;AACF;AAEA,IAAO,2BAAQ;","names":[]}
@@ -0,0 +1,38 @@
1
+ // src/RespondableEvent.ts
2
+ var RespondableEvent = class extends Event {
3
+ constructor(type, callback, eventInitDict) {
4
+ super(type, eventInitDict);
5
+ this.#request = eventInitDict?.request;
6
+ const resolvers = Promise.withResolvers();
7
+ resolvers.promise.then((result) => callback(result, this));
8
+ this.#resolve = (result) => {
9
+ if (this.#resolved) {
10
+ throw new Error("respondWith() has already been invoked.");
11
+ } else if (!this.target) {
12
+ throw new Error("respondWith() can only be called after dispatched.");
13
+ }
14
+ resolvers.resolve(result);
15
+ this.#resolved = true;
16
+ };
17
+ }
18
+ #request;
19
+ #resolve;
20
+ #resolved = false;
21
+ /** The object that would have triggered the event handler. */
22
+ get request() {
23
+ return this.#request;
24
+ }
25
+ checkResponse() {
26
+ const wasResolved = this.#resolved;
27
+ wasResolved || this.#resolve(void 0);
28
+ return wasResolved;
29
+ }
30
+ respondWith(response) {
31
+ this.#resolve(response);
32
+ }
33
+ };
34
+ var RespondableEvent_default = RespondableEvent;
35
+ export {
36
+ RespondableEvent_default as RespondableEvent
37
+ };
38
+ //# sourceMappingURL=respondable-event.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/RespondableEvent.ts"],"sourcesContent":["interface RespondableEventInit<T> extends EventInit {\n /** The object that would have triggered the event handler. */\n request: T;\n}\n\nclass RespondableEvent<T, R = undefined> extends Event {\n constructor(\n /** A string with the name of the event. */\n type: string,\n /** A function which will be called upon respond. */\n callback: (result: T | undefined, event: RespondableEvent<T, R>) => void,\n /**\n * An object that, in addition of the properties defined in [Event()](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event), can have the following properties:\n *\n * - `request`: The object that would have triggered the event handler.\n */\n eventInitDict?: RespondableEventInit<R> | undefined\n ) {\n super(type, eventInitDict);\n\n this.#request = eventInitDict?.request;\n\n const resolvers = Promise.withResolvers<T | undefined>();\n\n resolvers.promise.then(result => callback(result, this));\n\n this.#resolve = (result: Promise<T> | T | undefined) => {\n if (this.#resolved) {\n throw new Error('respondWith() has already been invoked.');\n } else if (!this.target) {\n throw new Error('respondWith() can only be called after dispatched.');\n }\n\n resolvers.resolve(result);\n\n this.#resolved = true;\n };\n }\n\n #request: R | undefined;\n #resolve: (result: Promise<T> | T | undefined) => void;\n #resolved: boolean = false;\n\n /** The object that would have triggered the event handler. */\n get request(): R | undefined {\n return this.#request;\n }\n\n checkResponse(): boolean {\n const wasResolved = this.#resolved;\n\n wasResolved || this.#resolve(undefined);\n\n return wasResolved;\n }\n\n respondWith(\n /**\n * An object or a Promise that resolves to an object.\n */\n response: T | Promise<T>\n ): void {\n this.#resolve(response);\n }\n}\n\nexport default RespondableEvent;\n"],"mappings":";AAKA,IAAM,mBAAN,cAAiD,MAAM;AAAA,EACrD,YAEE,MAEA,UAMA,eACA;AACA,UAAM,MAAM,aAAa;AAEzB,SAAK,WAAW,eAAe;AAE/B,UAAM,YAAY,QAAQ,cAA6B;AAEvD,cAAU,QAAQ,KAAK,YAAU,SAAS,QAAQ,IAAI,CAAC;AAEvD,SAAK,WAAW,CAAC,WAAuC;AACtD,UAAI,KAAK,WAAW;AAClB,cAAM,IAAI,MAAM,yCAAyC;AAAA,MAC3D,WAAW,CAAC,KAAK,QAAQ;AACvB,cAAM,IAAI,MAAM,oDAAoD;AAAA,MACtE;AAEA,gBAAU,QAAQ,MAAM;AAExB,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA,EAEA;AAAA,EACA;AAAA,EACA,YAAqB;AAAA;AAAA,EAGrB,IAAI,UAAyB;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,gBAAyB;AACvB,UAAM,cAAc,KAAK;AAEzB,mBAAe,KAAK,SAAS,MAAS;AAEtC,WAAO;AAAA,EACT;AAAA,EAEA,YAIE,UACM;AACN,SAAK,SAAS,QAAQ;AAAA,EACxB;AACF;AAEA,IAAO,2BAAQ;","names":[]}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "respondable-event",
3
+ "version": "0.1.1-main.29f6ea6",
4
+ "description": "Enables event listeners to send zero-or-one response back to the dispatching EventTarget.",
5
+ "files": [
6
+ "./dist/"
7
+ ],
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/respondable-event.d.mts",
12
+ "default": "./dist/respondable-event.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/respondable-event.d.ts",
16
+ "default": "./dist/respondable-event.js"
17
+ }
18
+ }
19
+ },
20
+ "main": "./dist/respondable-event.js",
21
+ "typings": "./dist/respondable-event.d.ts",
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "bump": "npm run bump:prod && npm run bump:dev",
25
+ "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localPeerDependencies // {}) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | has($K) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true",
26
+ "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localPeerDependencies // {}) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | has($K) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true",
27
+ "precommit": "npm run precommit:eslint && npm run precommit:publint && npm run precommit:typescript:production && npm run precommit:typescript:test",
28
+ "precommit:eslint": "ESLINT_USE_FLAT_CONFIG=false eslint ./src/",
29
+ "precommit:publint": "publint",
30
+ "precommit:typescript:production": "tsc --noEmit --project ./src/tsconfig.precommit.production.json",
31
+ "precommit:typescript:test": "tsc --noEmit --project ./src/tsconfig.precommit.test.json",
32
+ "prepack": "cp ../../CHANGELOG.md . && cp ../../LICENSE . && cp ../../README.md .",
33
+ "start": "npm run build -- --onSuccess \"touch ../pages/package.json\" --watch",
34
+ "switch": "cat package.json | jq --arg SWITCH_NAME $SWITCH_NAME -r '(.[\"switch:\" + $SWITCH_NAME] // {}) as $TEMPLATE | .devDependencies += ($TEMPLATE.devDependencies // {}) | .dependencies += ($TEMPLATE.dependencies // {})' | tee ./package.json.tmp && mv ./package.json.tmp ./package.json",
35
+ "test": "jest"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/compulim/respondable-event.git"
40
+ },
41
+ "keywords": [
42
+ "event-target",
43
+ "extendable-event",
44
+ "fetch-event",
45
+ "respond-with"
46
+ ],
47
+ "author": "William Wong (https://github.com/compulim)",
48
+ "license": "MIT",
49
+ "bugs": {
50
+ "url": "https://github.com/compulim/respondable-event/issues"
51
+ },
52
+ "homepage": "https://github.com/compulim/respondable-event#readme",
53
+ "devDependencies": {
54
+ "@babel/core": "^7.26.0",
55
+ "@babel/preset-env": "^7.26.0",
56
+ "@babel/preset-typescript": "^7.26.0",
57
+ "@testing-library/react": "^16.0.1",
58
+ "@tsconfig/recommended": "^1.0.7",
59
+ "@tsconfig/strictest": "^2.0.5",
60
+ "@types/jest": "^29.5.13",
61
+ "babel-jest": "^29.7.0",
62
+ "esbuild": "^0.24.0",
63
+ "jest": "^29.7.0",
64
+ "nodemon": "^3.1.7",
65
+ "prettier": "^3.3.3",
66
+ "publint": "^0.2.11",
67
+ "tsup": "^8.3.0",
68
+ "typescript": "^5.6.3"
69
+ },
70
+ "dependencies": {
71
+ "respondable-event": "^0.1.1-main.29f6ea6"
72
+ }
73
+ }