@gjsify/timers 0.53.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/lib/esm/index.js +1 -1
- package/lib/esm/timeout.js +1 -1
- package/lib/types/index.d.ts +49 -9
- package/lib/types/timeout.d.ts +46 -11
- package/lib/types/timeout.gjs.spec.d.ts +2 -0
- package/lib/types/timeout.spec.d.ts +2 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -26,6 +26,45 @@ await delay(1000);
|
|
|
26
26
|
console.log('1 second later');
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
## The returned handle is Node-compatible
|
|
30
|
+
|
|
31
|
+
`setTimeout` / `setInterval` return a handle carrying the full `NodeJS.Timeout`
|
|
32
|
+
surface, so the cast third-party code used to need is gone:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
const sweep = setInterval(() => rooms.sweep(), 30_000);
|
|
36
|
+
sweep.unref(); // no cast, no `as unknown as { unref?: () => void }`
|
|
37
|
+
sweep.hasRef(); // → boolean
|
|
38
|
+
sweep.refresh(); // reschedule from now, returns the same handle
|
|
39
|
+
clearInterval(sweep); // or `sweep.close()`, or `+sweep` for the numeric ID
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Members: `ref()`, `unref()`, `hasRef()`, `refresh()`, `close()`,
|
|
43
|
+
`[Symbol.toPrimitive]()` (→ the numeric timer ID, for libraries that key a Map
|
|
44
|
+
on it), `[Symbol.dispose]()` and `_onTimeout()` (what `@types/node` declares, and
|
|
45
|
+
what libraries call directly when they drive a handle themselves).
|
|
46
|
+
`clearTimeout` / `clearInterval` accept the handle itself, a foreign Node
|
|
47
|
+
handle, or a bare number — the check is structural, so it survives a duplicated
|
|
48
|
+
copy of this module in a bundle.
|
|
49
|
+
|
|
50
|
+
### What `unref()` means on GJS
|
|
51
|
+
|
|
52
|
+
**On Node, `unref()` really releases the event loop** and the process may exit
|
|
53
|
+
while the timer is pending. On GJS it **cannot**, and this package does not
|
|
54
|
+
pretend otherwise:
|
|
55
|
+
|
|
56
|
+
- `Gtk.Application` / `GLib.MainLoop` own the loop — not the JS heap. Nothing a
|
|
57
|
+
timer does decides whether the app keeps running.
|
|
58
|
+
- The only GLib operation that releases a source's hold on the loop
|
|
59
|
+
(`g_source_unref`) also destroys the source. That is the double-unref
|
|
60
|
+
SIGSEGV this package exists to avoid, so the timers are backed by
|
|
61
|
+
`GLib.timeout_add` and numeric source IDs for exactly this reason.
|
|
62
|
+
|
|
63
|
+
So on GJS `unref()` is **recorded and reported**: `hasRef()` returns `false`
|
|
64
|
+
after it, and the timer keeps firing. Code that calls `unref()` to mean "don't
|
|
65
|
+
let this timer hold my process open" is correct on both runtimes — it simply
|
|
66
|
+
has no work to do on GJS, because the app, not the timer, decides that.
|
|
67
|
+
|
|
29
68
|
## License
|
|
30
69
|
|
|
31
70
|
MIT
|
package/lib/esm/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{Immediate as e,Timeout as t}from"./timeout.js";function
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{Immediate as e,Timeout as t}from"./timeout.js";function isNodeTimerHandle(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.unref==`function`&&typeof t.hasRef==`function`&&typeof t.close==`function`}function _clearTimeout(e){isNodeTimerHandle(e)?e.close():e!=null&&clearTimeout(e)}function _setTimeout(e,n=0,...r){return new t(e,n,r,!1)}function _setInterval(e,n=0,...r){return new t(e,n,r,!0)}function _clearInterval(e){isNodeTimerHandle(e)?e.close():e!=null&&clearInterval(e)}function _setImmediate(t,...n){return new e(t,n)}function _clearImmediate(e){isNodeTimerHandle(e)?e.close():e!=null&&clearTimeout(e)}var n={setTimeout:_setTimeout,clearTimeout:_clearTimeout,setInterval:_setInterval,clearInterval:_clearInterval,setImmediate:_setImmediate,clearImmediate:_clearImmediate,Timeout:t,Immediate:e};export{e as Immediate,t as Timeout,_clearImmediate as clearImmediate,_clearInterval as clearInterval,_clearTimeout as clearTimeout,n as default,_setImmediate as setImmediate,_setInterval as setInterval,_setTimeout as setTimeout};
|
package/lib/esm/timeout.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";var Timeout=class{_id;_ref=!0;_callback;_delay;_args;_isInterval;constructor(e,t,n,r){this._callback=e,this._delay=t,this._args=n,this._isInterval=r,r?this._id=setInterval(e,t,...n):this._id=setTimeout(e,t,...n)}ref(){return this._ref=!0,this}unref(){return this._ref=!1,this}hasRef(){return this._ref}refresh(){return this._id!=null&&(this._isInterval?(clearInterval(this._id),this._id=setInterval(this._callback,this._delay,...this._args)):(clearTimeout(this._id),this._id=setTimeout(this._callback,this._delay,...this._args))),this}close(){this._id!=null&&(this._isInterval?clearInterval(this._id):clearTimeout(this._id),this._id=null)}[Symbol.toPrimitive](){return this.
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function canDelegateRef(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.unref==`function`&&typeof t.ref==`function`&&typeof t.hasRef==`function`&&typeof t.refresh==`function`}var Timeout=class{_id;_ref=!0;_callback;_delay;_args;_isInterval;_numericId;constructor(e,t,n,r){this._callback=e,this._delay=t,this._args=n,this._isInterval=r,r?this._id=setInterval(e,t,...n):this._id=setTimeout(e,t,...n),this._numericId=Number(this._id)}ref(){return this._ref=!0,this._id!=null&&canDelegateRef(this._id)&&this._id.ref(),this}unref(){return this._ref=!1,this._id!=null&&canDelegateRef(this._id)&&this._id.unref(),this}hasRef(){return this._ref}refresh(){return this._id!=null&&(this._isInterval?(clearInterval(this._id),this._id=setInterval(this._callback,this._delay,...this._args)):(clearTimeout(this._id),this._id=setTimeout(this._callback,this._delay,...this._args)),this._numericId=Number(this._id),!this._ref&&canDelegateRef(this._id)&&this._id.unref()),this}close(){return this._id!=null&&(this._isInterval?clearInterval(this._id):clearTimeout(this._id),this._id=null),this}_onTimeout(...e){this._callback(...e)}[Symbol.dispose](){this.close()}[Symbol.toPrimitive](){return this._numericId}},Immediate=class{_cancelled=!1;_ref=!0;_callback;_args;constructor(e,t){this._callback=e,this._args=t,Promise.resolve().then(()=>{this._cancelled||this._onImmediate(...this._args)})}ref(){return this._ref=!0,this}unref(){return this._ref=!1,this}hasRef(){return this._ref}close(){return this._cancelled=!0,this}_onImmediate(...e){this._callback(...e)}[Symbol.dispose](){this.close()}};export{Immediate,Timeout};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,32 +1,72 @@
|
|
|
1
1
|
import { Timeout, Immediate } from './timeout.js';
|
|
2
2
|
export { Timeout, Immediate };
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Structural shape of a Node timer handle, as `@types/node` declares it for
|
|
5
|
+
* `NodeJS.Timeout`.
|
|
6
|
+
*
|
|
7
|
+
* This is what makes the return value usable WITHOUT a cast: a consumer's
|
|
8
|
+
* `setInterval(...).unref()` type-checks because the object we return satisfies
|
|
9
|
+
* this interface, which is why the globals register path and this module agree
|
|
10
|
+
* on the same members.
|
|
11
|
+
*
|
|
12
|
+
* `close(): unknown` because `@types/node` declares `close(): void` while ours
|
|
13
|
+
* returns `this` for chaining — a `this` return satisfies a `void` one.
|
|
6
14
|
*/
|
|
7
|
-
|
|
15
|
+
export interface NodeTimerHandle {
|
|
16
|
+
ref(): unknown;
|
|
17
|
+
unref(): unknown;
|
|
18
|
+
hasRef(): boolean;
|
|
19
|
+
refresh(): unknown;
|
|
20
|
+
close(): unknown;
|
|
21
|
+
[Symbol.toPrimitive](): number;
|
|
22
|
+
[Symbol.dispose](): void;
|
|
23
|
+
}
|
|
24
|
+
/** A Node `Immediate` has no `refresh`/`toPrimitive`, so it gets its own shape. */
|
|
25
|
+
export interface NodeImmediateHandle {
|
|
26
|
+
ref(): unknown;
|
|
27
|
+
unref(): unknown;
|
|
28
|
+
hasRef(): boolean;
|
|
29
|
+
[Symbol.dispose](): void;
|
|
30
|
+
}
|
|
8
31
|
/**
|
|
9
32
|
* Cancel a timeout created by setTimeout.
|
|
33
|
+
*
|
|
34
|
+
* Accepts the Node spellings, not just our own instance: `setTimeout` may have
|
|
35
|
+
* come from a third-party module, from a `worker_threads` realm, or as a bare
|
|
36
|
+
* number (a browser handle). Anything with the `unref`/`hasRef`/`close` trio is
|
|
37
|
+
* structurally a Node `Timeout`, so duck-type it rather than `instanceof` — the
|
|
38
|
+
* check has to survive a duplicate copy of this module in the bundle, which is
|
|
39
|
+
* exactly the situation that made consumers cast in the first place.
|
|
40
|
+
*/
|
|
41
|
+
declare function _clearTimeout(timeout: NodeTimerHandle | number | undefined): void;
|
|
42
|
+
/**
|
|
43
|
+
* Schedule a callback to be called after `delay` milliseconds.
|
|
44
|
+
*
|
|
45
|
+
* The returned handle satisfies {@link NodeTimerHandle}, so a consumer typed
|
|
46
|
+
* against `@types/node` calls `.unref()` on it without a cast.
|
|
10
47
|
*/
|
|
11
|
-
declare function
|
|
48
|
+
declare function _setTimeout<T extends unknown[]>(callback: (...args: T) => void, delay?: number, ...args: T): Timeout;
|
|
12
49
|
/**
|
|
13
50
|
* Schedule a callback to be called repeatedly every `delay` milliseconds.
|
|
14
|
-
* Returns
|
|
51
|
+
* Returns the same handle shape as {@link _setTimeout}.
|
|
15
52
|
*/
|
|
16
53
|
declare function _setInterval<T extends unknown[]>(callback: (...args: T) => void, delay?: number, ...args: T): Timeout;
|
|
17
54
|
/**
|
|
18
|
-
* Cancel an interval created by setInterval.
|
|
55
|
+
* Cancel an interval created by setInterval. Accepts the same shapes as
|
|
56
|
+
* {@link _clearTimeout}.
|
|
19
57
|
*/
|
|
20
|
-
declare function _clearInterval(timeout:
|
|
58
|
+
declare function _clearInterval(timeout: NodeTimerHandle | number | undefined): void;
|
|
21
59
|
/**
|
|
22
60
|
* Schedule a callback to be called on the next iteration of the event loop.
|
|
23
61
|
* Returns an Immediate object with ref/unref methods.
|
|
24
62
|
*/
|
|
25
63
|
declare function _setImmediate<T extends unknown[]>(callback: (...args: T) => void, ...args: T): Immediate;
|
|
26
64
|
/**
|
|
27
|
-
* Cancel an immediate created by setImmediate.
|
|
65
|
+
* Cancel an immediate created by setImmediate. Accepts a foreign Node `Immediate`
|
|
66
|
+
* through the same duck-type as {@link _clearTimeout}, because `instanceof` fails
|
|
67
|
+
* across a duplicated module and callers hold whatever `setImmediate` gave them.
|
|
28
68
|
*/
|
|
29
|
-
declare function _clearImmediate(immediate: Immediate | number | undefined): void;
|
|
69
|
+
declare function _clearImmediate(immediate: Immediate | NodeImmediateHandle | number | undefined): void;
|
|
30
70
|
export { _setTimeout as setTimeout, _clearTimeout as clearTimeout, _setInterval as setInterval, _clearInterval as clearInterval, _setImmediate as setImmediate, _clearImmediate as clearImmediate, };
|
|
31
71
|
declare const _default: {
|
|
32
72
|
setTimeout: typeof _setTimeout;
|
package/lib/types/timeout.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
type TimerCallback = (...args: unknown[]) => void;
|
|
1
2
|
export declare class Timeout {
|
|
2
3
|
private _id;
|
|
3
4
|
private _ref;
|
|
@@ -5,36 +6,70 @@ export declare class Timeout {
|
|
|
5
6
|
private _delay;
|
|
6
7
|
private _args;
|
|
7
8
|
private _isInterval;
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Retained so `+timeout` still yields a number after `close()`, where the
|
|
11
|
+
* host handle is gone. Node keeps its id readable for the object's life.
|
|
12
|
+
*/
|
|
13
|
+
private _numericId;
|
|
14
|
+
constructor(callback: TimerCallback, delay: number, args: unknown[], isInterval: boolean);
|
|
9
15
|
/**
|
|
10
16
|
* Mark this timeout as referenced (default).
|
|
11
|
-
*
|
|
17
|
+
*
|
|
18
|
+
* Delegated to the host where the host is a Node-shaped handle (see
|
|
19
|
+
* {@link canDelegateRef}); recorded only otherwise.
|
|
12
20
|
*/
|
|
13
21
|
ref(): this;
|
|
14
22
|
/**
|
|
15
|
-
* Mark this timeout as unreferenced
|
|
16
|
-
*
|
|
23
|
+
* Mark this timeout as unreferenced — the caller asks that it not require
|
|
24
|
+
* the host loop to stay alive.
|
|
25
|
+
*
|
|
26
|
+
* Honoured on Node (delegated to libuv). On GJS it is RECORDED ONLY and the
|
|
27
|
+
* timer keeps firing: the app owns the main loop, and releasing liveness
|
|
28
|
+
* there would mean destroying the GLib source. `hasRef()` reports the
|
|
29
|
+
* recorded flag on both runtimes.
|
|
17
30
|
*/
|
|
18
31
|
unref(): this;
|
|
19
32
|
/** Whether this timeout is referenced. */
|
|
20
33
|
hasRef(): boolean;
|
|
21
34
|
/**
|
|
22
35
|
* Reset the timer's start time to now and reschedule.
|
|
36
|
+
*
|
|
37
|
+
* The ref state is carried over: rescheduling creates a NEW host handle,
|
|
38
|
+
* which arrives refed, so an `unref()`ed timer that is refreshed would
|
|
39
|
+
* silently start holding the event loop open again — on Node that is the
|
|
40
|
+
* difference between a process that exits and one that hangs.
|
|
23
41
|
*/
|
|
24
42
|
refresh(): this;
|
|
25
|
-
/** Close/clear this timer. */
|
|
26
|
-
close():
|
|
43
|
+
/** Close/clear this timer. Returns `this`, matching `@types/node`. */
|
|
44
|
+
close(): this;
|
|
45
|
+
/**
|
|
46
|
+
* Node libraries call this directly rather than going through the scheduled
|
|
47
|
+
* dispatch (`@types/node` declares it on `NodeJS.Timeout`).
|
|
48
|
+
*/
|
|
49
|
+
_onTimeout(...args: unknown[]): void;
|
|
50
|
+
[Symbol.dispose](): void;
|
|
27
51
|
/** Get the underlying timer ID (for clearTimeout/clearInterval). */
|
|
28
52
|
[Symbol.toPrimitive](): number;
|
|
29
53
|
}
|
|
30
54
|
export declare class Immediate {
|
|
31
|
-
private _id;
|
|
32
|
-
private _ref;
|
|
33
55
|
private _cancelled;
|
|
34
|
-
|
|
56
|
+
private _ref;
|
|
57
|
+
readonly _callback: TimerCallback;
|
|
58
|
+
readonly _args: unknown[];
|
|
59
|
+
constructor(callback: TimerCallback, args: unknown[]);
|
|
60
|
+
/**
|
|
61
|
+
* Recorded only, never delegated — an `Immediate` here is a microtask, not a
|
|
62
|
+
* host handle, so there is nothing to release. See {@link Timeout.unref} for
|
|
63
|
+
* why that is the honest answer on GJS.
|
|
64
|
+
*/
|
|
35
65
|
ref(): this;
|
|
66
|
+
/** Recorded only — see {@link Timeout.unref}. */
|
|
36
67
|
unref(): this;
|
|
37
68
|
hasRef(): boolean;
|
|
38
|
-
close
|
|
39
|
-
|
|
69
|
+
/** Cancels the immediate. Returns `this` for the same reason `Timeout.close` does. */
|
|
70
|
+
close(): this;
|
|
71
|
+
/** Node libraries call this directly (`@types/node` declares it). */
|
|
72
|
+
_onImmediate(...args: unknown[]): void;
|
|
73
|
+
[Symbol.dispose](): void;
|
|
40
74
|
}
|
|
75
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/timers",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0",
|
|
4
4
|
"description": "Node.js timers module for Gjs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"timers"
|
|
41
41
|
],
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@gjsify/cli": "^0.
|
|
44
|
-
"@gjsify/unit": "^0.
|
|
43
|
+
"@gjsify/cli": "^0.54.0",
|
|
44
|
+
"@gjsify/unit": "^0.54.0",
|
|
45
45
|
"@types/node": "^25.9.2",
|
|
46
46
|
"typescript": "^6.0.3"
|
|
47
47
|
},
|