@gjsify/abort-controller 0.0.4 → 0.1.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.
package/README.md CHANGED
@@ -1,4 +1,32 @@
1
1
  # @gjsify/abort-controller
2
2
 
3
- Web abort-controller module for Gjs
3
+ GJS implementation of the AbortController and AbortSignal Web APIs.
4
4
 
5
+ Part of the [gjsify](https://github.com/gjsify/gjsify) project — Node.js and Web APIs for GJS (GNOME JavaScript).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @gjsify/abort-controller
11
+ # or
12
+ yarn add @gjsify/abort-controller
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```typescript
18
+ import { AbortController, AbortSignal } from '@gjsify/abort-controller';
19
+
20
+ const controller = new AbortController();
21
+ const signal = controller.signal;
22
+
23
+ signal.addEventListener('abort', () => {
24
+ console.log('Aborted!');
25
+ });
26
+
27
+ controller.abort();
28
+ ```
29
+
30
+ ## License
31
+
32
+ MIT
package/globals.mjs ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Re-exports native AbortController/AbortSignal globals for use in Node.js builds.
3
+ * On Node.js, AbortController and AbortSignal are native globals.
4
+ */
5
+ export const AbortController = globalThis.AbortController;
6
+ export const AbortSignal = globalThis.AbortSignal;
package/lib/esm/index.js CHANGED
@@ -1,2 +1,94 @@
1
- import "@gjsify/dom-events";
2
- export * from "@gjsify/deno-runtime/ext/web/03_abort_signal";
1
+ import { Event, EventTarget, DOMException } from "@gjsify/dom-events";
2
+ const kAbort = /* @__PURE__ */ Symbol("abort");
3
+ const kInternal = /* @__PURE__ */ Symbol("internal");
4
+ class AbortSignal extends EventTarget {
5
+ #aborted = false;
6
+ reason = void 0;
7
+ onabort = null;
8
+ constructor(key) {
9
+ super();
10
+ if (key !== kInternal) {
11
+ throw new TypeError("Illegal constructor.");
12
+ }
13
+ }
14
+ get aborted() {
15
+ if (!(this instanceof AbortSignal)) {
16
+ throw new TypeError("'get aborted' called on an object that is not a valid instance of AbortSignal.");
17
+ }
18
+ return this.#aborted;
19
+ }
20
+ get [Symbol.toStringTag]() {
21
+ return "AbortSignal";
22
+ }
23
+ throwIfAborted() {
24
+ if (this.#aborted) {
25
+ throw this.reason;
26
+ }
27
+ }
28
+ [kAbort](reason) {
29
+ if (this.#aborted) return;
30
+ this.#aborted = true;
31
+ this.reason = reason ?? new DOMException("The operation was aborted.", "AbortError");
32
+ const event = new Event("abort");
33
+ if (typeof this.onabort === "function") {
34
+ this.onabort.call(this, event);
35
+ }
36
+ this.dispatchEvent(event);
37
+ }
38
+ static abort(reason) {
39
+ const signal = new AbortSignal(kInternal);
40
+ signal[kAbort](reason);
41
+ return signal;
42
+ }
43
+ static timeout(milliseconds) {
44
+ const signal = new AbortSignal(kInternal);
45
+ setTimeout(() => {
46
+ signal[kAbort](new DOMException("The operation timed out.", "TimeoutError"));
47
+ }, milliseconds);
48
+ return signal;
49
+ }
50
+ static any(signals) {
51
+ const combined = new AbortSignal(kInternal);
52
+ for (const signal of signals) {
53
+ if (signal.aborted) {
54
+ combined[kAbort](signal.reason);
55
+ return combined;
56
+ }
57
+ }
58
+ const onAbort = () => {
59
+ if (!combined.aborted) {
60
+ const aborted = signals.find((s) => s.aborted);
61
+ combined[kAbort](aborted?.reason);
62
+ }
63
+ };
64
+ for (const signal of signals) {
65
+ signal.addEventListener("abort", onAbort, { once: true });
66
+ }
67
+ return combined;
68
+ }
69
+ }
70
+ class AbortController {
71
+ signal;
72
+ constructor() {
73
+ this.signal = new AbortSignal(kInternal);
74
+ }
75
+ abort(reason) {
76
+ if (!(this instanceof AbortController)) {
77
+ throw new TypeError("'abort' called on an object that is not a valid instance of AbortController.");
78
+ }
79
+ this.signal[kAbort](reason);
80
+ }
81
+ }
82
+ if (typeof globalThis.AbortController === "undefined") {
83
+ globalThis.AbortController = AbortController;
84
+ }
85
+ if (typeof globalThis.AbortSignal === "undefined") {
86
+ globalThis.AbortSignal = AbortSignal;
87
+ }
88
+ var index_default = { AbortController, AbortSignal };
89
+ export {
90
+ AbortController,
91
+ AbortSignal,
92
+ DOMException,
93
+ index_default as default
94
+ };
@@ -1,2 +1,26 @@
1
- import '@gjsify/dom-events';
2
- export * from '@gjsify/deno-runtime/ext/web/03_abort_signal';
1
+ import { Event, EventTarget, DOMException } from '@gjsify/dom-events';
2
+ declare const kAbort: unique symbol;
3
+ export declare class AbortSignal extends EventTarget {
4
+ #private;
5
+ reason: any;
6
+ onabort: ((this: AbortSignal, ev: Event) => any) | null;
7
+ constructor(key?: symbol);
8
+ get aborted(): boolean;
9
+ get [Symbol.toStringTag](): string;
10
+ throwIfAborted(): void;
11
+ [kAbort](reason?: any): void;
12
+ static abort(reason?: any): AbortSignal;
13
+ static timeout(milliseconds: number): AbortSignal;
14
+ static any(signals: AbortSignal[]): AbortSignal;
15
+ }
16
+ export declare class AbortController {
17
+ readonly signal: AbortSignal;
18
+ constructor();
19
+ abort(reason?: any): void;
20
+ }
21
+ export { DOMException };
22
+ declare const _default: {
23
+ AbortController: typeof AbortController;
24
+ AbortSignal: typeof AbortSignal;
25
+ };
26
+ export default _default;
package/package.json CHANGED
@@ -1,33 +1,27 @@
1
1
  {
2
2
  "name": "@gjsify/abort-controller",
3
- "version": "0.0.4",
3
+ "version": "0.1.1",
4
4
  "description": "Web AbortController module for Gjs",
5
5
  "type": "module",
6
- "main": "lib/cjs/index.js",
7
6
  "module": "lib/esm/index.js",
8
7
  "types": "lib/types/index.d.ts",
9
8
  "exports": {
10
9
  ".": {
11
- "import": {
12
- "types": "./lib/types/index.d.ts",
13
- "default": "./lib/esm/index.js"
14
- },
15
- "require": {
16
- "types": "./lib/types/index.d.ts",
17
- "default": "./lib/cjs/index.js"
18
- }
19
- }
10
+ "types": "./lib/types/index.d.ts",
11
+ "default": "./lib/esm/index.js"
12
+ },
13
+ "./globals": "./globals.mjs"
20
14
  },
21
15
  "scripts": {
22
- "clear": "rm -rf lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo || exit 0",
23
- "print:name": "echo '@gjsify/abort-controller'",
24
- "build": "yarn print:name && yarn build:gjsify && yarn build:types",
16
+ "clear": "rm -rf lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo test.gjs.mjs test.node.mjs || exit 0",
17
+ "check": "tsc --noEmit",
18
+ "build": "yarn build:gjsify && yarn build:types",
25
19
  "build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
26
- "build:types": "tsc --project tsconfig.types.json || exit 0",
20
+ "build:types": "tsc",
27
21
  "build:test": "yarn build:test:gjs && yarn build:test:node",
28
22
  "build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
29
23
  "build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
30
- "test": "yarn print:name && yarn build:gjsify && yarn build:test && yarn test:gjs",
24
+ "test": "yarn build:gjsify && yarn build:test && yarn test:gjs",
31
25
  "test:gjs": "gjs -m test.gjs.mjs",
32
26
  "test:node": "node test.node.mjs"
33
27
  },
@@ -37,14 +31,12 @@
37
31
  "buffer"
38
32
  ],
39
33
  "devDependencies": {
40
- "@gjsify/cli": "^0.0.4",
41
- "@gjsify/unit": "^0.0.4",
42
- "@types/node": "^20.10.5",
43
- "typescript": "^5.3.3"
34
+ "@gjsify/cli": "^0.1.1",
35
+ "@gjsify/unit": "^0.1.1",
36
+ "@types/node": "^25.5.0",
37
+ "typescript": "^6.0.2"
44
38
  },
45
39
  "dependencies": {
46
- "@gjsify/deno-runtime": "^0.0.4",
47
- "@gjsify/deno_std": "^0.0.4",
48
- "@gjsify/dom-events": "^0.0.4"
40
+ "@gjsify/dom-events": "^0.1.1"
49
41
  }
50
42
  }
@@ -1,13 +1,13 @@
1
1
  import { describe, it, expect, assert } from '@gjsify/unit';
2
2
 
3
- // TODO add a fake module for node and deno to run this tests also in his runtime?
4
- import { AbortController, AbortSignal } from '@gjsify/abort-controller';
3
+ import { AbortController, AbortSignal } from 'abort-controller';
5
4
 
6
5
  const HAS_EVENT_TARGET_INTERFACE = typeof EventTarget !== "undefined"
7
6
 
8
7
  export default async () => {
9
8
 
10
- // Credits https://github.com/mysticatea/abort-controller/tree/master/test
9
+ // Ported from abort-controller (https://github.com/mysticatea/abort-controller/tree/master/test)
10
+ // Original: MIT license, Toru Nagashima
11
11
 
12
12
  await describe('AbortController', async () => {
13
13
  await it('should have a callable constructor', async () => {
@@ -23,18 +23,10 @@ export default async () => {
23
23
  await it('should have 2 properties', async () => {
24
24
  const controller = new AbortController();
25
25
 
26
- const keys = new Set()
27
- keys.add("signal")
28
- keys.add("abort")
29
-
30
- for (const key in controller) {
31
- assert(keys.has(key), `'${key}' found, but should not have it`);
32
- keys.delete(key)
26
+ const expected = ["signal", "abort"];
27
+ for (const key of expected) {
28
+ assert(key in controller, `'${key}' not found`);
33
29
  }
34
-
35
- keys.forEach((key) => {
36
- assert(false, `'${key}' not found`);
37
- })
38
30
  });
39
31
 
40
32
  // TODO
@@ -46,11 +38,11 @@ export default async () => {
46
38
  await describe("'signal' property", async () => {
47
39
  const controller = new AbortController();
48
40
  const signal = controller.signal
49
-
41
+
50
42
  await it("should return the same instance always", async () => {
51
43
  expect(signal === controller.signal).toBeTruthy()
52
44
  })
53
-
45
+
54
46
  await it("should be a AbortSignal object", async () => {
55
47
  expect(signal instanceof AbortSignal).toBeTruthy()
56
48
  })
@@ -60,7 +52,7 @@ export default async () => {
60
52
  expect(signal instanceof EventTarget).toBeTruthy()
61
53
  })
62
54
  }
63
-
55
+
64
56
  await it("should have 5 properties", async () => {
65
57
  const keys = new Set<string>()
66
58
  keys.add("addEventListener")
@@ -68,13 +60,13 @@ export default async () => {
68
60
  keys.add("dispatchEvent")
69
61
  keys.add("aborted")
70
62
  keys.add("onabort")
71
-
63
+
72
64
  // TODO
73
65
  // for (const key in signal) {
74
66
  // assert(keys.has(key), `'${key}' found, but should not have it`);
75
67
  // keys.delete(key)
76
68
  // }
77
-
69
+
78
70
  keys.forEach(key => {
79
71
  // WORKAROUND for getter / setter
80
72
  const exists = (signal as any)[key] !== undefined;
@@ -82,16 +74,16 @@ export default async () => {
82
74
  assert(exists, `'${key}' not found, but should have it: `);
83
75
  })
84
76
  })
85
-
77
+
86
78
  await it("should have 'aborted' property which is false by default", async () => {
87
79
  expect(signal.aborted).toBeFalsy();
88
80
  })
89
-
81
+
90
82
  await it("should have 'onabort' property which is null by default", async () => {
91
83
  // TODO:
92
84
  expect((signal as any).onabort).toBeNull()
93
85
  })
94
-
86
+
95
87
  await it("should throw a TypeError if 'signal.aborted' getter is called with non AbortSignal object", async () => {
96
88
  const getAborted = Object.getOwnPropertyDescriptor(
97
89
  (signal as any).__proto__,
@@ -116,7 +108,7 @@ export default async () => {
116
108
  controller.abort()
117
109
  assert(controller.signal.aborted)
118
110
  })
119
-
111
+
120
112
  await it("should fire 'abort' event on 'signal' (addEventListener)", async () => {
121
113
  const controller = new AbortController();
122
114
 
@@ -125,10 +117,10 @@ export default async () => {
125
117
  ++calls;
126
118
  });
127
119
  controller.abort()
128
-
120
+
129
121
  assert(calls === 1)
130
122
  })
131
-
123
+
132
124
  await it("should fire 'abort' event on 'signal' (onabort)", async () => {
133
125
  const controller = new AbortController();
134
126
 
@@ -138,10 +130,10 @@ export default async () => {
138
130
  ++calls;
139
131
  }
140
132
  controller.abort()
141
-
133
+
142
134
  assert(calls === 1)
143
135
  })
144
-
136
+
145
137
  await it("should not fire 'abort' event twice", async () => {
146
138
  const controller = new AbortController();
147
139
 
@@ -152,10 +144,10 @@ export default async () => {
152
144
  controller.abort()
153
145
  controller.abort()
154
146
  controller.abort()
155
-
147
+
156
148
  assert(calls === 1)
157
149
  })
158
-
150
+
159
151
  await it("should throw a TypeError if 'this' is not an AbortController object", async () => {
160
152
  const controller = new AbortController();
161
153
 
@@ -1,13 +1,14 @@
1
1
  import { describe, it, expect } from '@gjsify/unit';
2
2
 
3
- import { AbortSignal } from '@gjsify/abort-controller';
3
+ import { AbortSignal } from 'abort-controller';
4
4
 
5
5
  // Use build in AbortSignal on Node.js tests and the custom implementation on Gjs
6
6
  // export const AbortSignal = globalThis.AbortSignal || GjsifyAbortSignal;
7
7
 
8
8
  export default async () => {
9
9
 
10
- // Credits https://github.com/mysticatea/abort-controller/tree/master/test
10
+ // Ported from abort-controller (https://github.com/mysticatea/abort-controller/tree/master/test)
11
+ // Original: MIT license, Toru Nagashima
11
12
 
12
13
  await describe("AbortSignal", async () => {
13
14
  await it("should not be callable", async () => {
package/src/index.ts CHANGED
@@ -1,2 +1,114 @@
1
- import '@gjsify/dom-events'
2
- export * from '@gjsify/deno-runtime/ext/web/03_abort_signal'
1
+ // Reference: W3C DOM Abort API
2
+ // Reimplemented for GJS
3
+
4
+ import { Event, EventTarget, DOMException } from '@gjsify/dom-events';
5
+
6
+ const kAbort = Symbol('abort');
7
+ const kInternal = Symbol('internal');
8
+
9
+ export class AbortSignal extends EventTarget {
10
+ #aborted: boolean = false;
11
+ reason: any = undefined;
12
+
13
+ onabort: ((this: AbortSignal, ev: Event) => any) | null = null;
14
+
15
+ constructor(key?: symbol) {
16
+ super();
17
+ if (key !== kInternal) {
18
+ throw new TypeError('Illegal constructor.');
19
+ }
20
+ }
21
+
22
+ get aborted(): boolean {
23
+ if (!(this instanceof AbortSignal)) {
24
+ throw new TypeError("'get aborted' called on an object that is not a valid instance of AbortSignal.");
25
+ }
26
+ return this.#aborted;
27
+ }
28
+
29
+ get [Symbol.toStringTag]() { return 'AbortSignal'; }
30
+
31
+ throwIfAborted(): void {
32
+ if (this.#aborted) {
33
+ throw this.reason;
34
+ }
35
+ }
36
+
37
+ [kAbort](reason?: any): void {
38
+ if (this.#aborted) return;
39
+
40
+ this.#aborted = true;
41
+ this.reason = reason ?? new DOMException('The operation was aborted.', 'AbortError');
42
+
43
+ const event = new Event('abort');
44
+ if (typeof this.onabort === 'function') {
45
+ this.onabort.call(this, event);
46
+ }
47
+ this.dispatchEvent(event);
48
+ }
49
+
50
+ static abort(reason?: any): AbortSignal {
51
+ const signal = new AbortSignal(kInternal);
52
+ signal[kAbort](reason);
53
+ return signal;
54
+ }
55
+
56
+ static timeout(milliseconds: number): AbortSignal {
57
+ const signal = new AbortSignal(kInternal);
58
+ setTimeout(() => {
59
+ signal[kAbort](new DOMException('The operation timed out.', 'TimeoutError'));
60
+ }, milliseconds);
61
+ return signal;
62
+ }
63
+
64
+ static any(signals: AbortSignal[]): AbortSignal {
65
+ const combined = new AbortSignal(kInternal);
66
+
67
+ for (const signal of signals) {
68
+ if (signal.aborted) {
69
+ combined[kAbort](signal.reason);
70
+ return combined;
71
+ }
72
+ }
73
+
74
+ const onAbort = () => {
75
+ if (!combined.aborted) {
76
+ const aborted = signals.find(s => s.aborted);
77
+ combined[kAbort](aborted?.reason);
78
+ }
79
+ };
80
+
81
+ for (const signal of signals) {
82
+ signal.addEventListener('abort', onAbort, { once: true });
83
+ }
84
+
85
+ return combined;
86
+ }
87
+ }
88
+
89
+ export class AbortController {
90
+ readonly signal: AbortSignal;
91
+
92
+ constructor() {
93
+ this.signal = new AbortSignal(kInternal);
94
+ }
95
+
96
+ abort(reason?: any): void {
97
+ if (!(this instanceof AbortController)) {
98
+ throw new TypeError("'abort' called on an object that is not a valid instance of AbortController.");
99
+ }
100
+ this.signal[kAbort](reason);
101
+ }
102
+ }
103
+
104
+ export { DOMException };
105
+
106
+ // Register globals on GJS if needed (pattern: @gjsify/web-streams)
107
+ if (typeof globalThis.AbortController === 'undefined') {
108
+ (globalThis as any).AbortController = AbortController;
109
+ }
110
+ if (typeof globalThis.AbortSignal === 'undefined') {
111
+ (globalThis as any).AbortSignal = AbortSignal;
112
+ }
113
+
114
+ export default { AbortController, AbortSignal };
package/tsconfig.json CHANGED
@@ -1,19 +1,32 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "module": "ESNext",
4
- "types": ["node"],
4
+ "types": [
5
+ "node"
6
+ ],
5
7
  "target": "ESNext",
6
8
  "experimentalDecorators": true,
9
+ "emitDeclarationOnly": true,
10
+ "declaration": true,
7
11
  "outDir": "lib",
8
12
  "rootDir": "src",
9
13
  "declarationDir": "lib/types",
10
14
  "composite": true,
11
15
  "moduleResolution": "bundler",
12
- "allowImportingTsExtensions": true
16
+ "allowImportingTsExtensions": true,
17
+ "skipLibCheck": true,
18
+ "allowJs": true,
19
+ "checkJs": false,
20
+ "strict": false
13
21
  },
14
- "skipLibCheck": true,
15
- "allowJs": true,
16
- "checkJs": false,
17
22
  "reflection": false,
18
- "include": ["src/**/*.ts"]
19
- }
23
+ "include": [
24
+ "src/**/*.ts"
25
+ ],
26
+ "exclude": [
27
+ "src/test.ts",
28
+ "src/test.mts",
29
+ "src/**/*.spec.ts",
30
+ "src/**/*.spec.mts"
31
+ ]
32
+ }