@zakkster/lite-observe 1.0.0 → 1.0.2
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/CHANGELOG.md +21 -0
- package/index.d.ts +3 -0
- package/index.js +1 -1
- package/package.json +10 -3
- package/src/_finalize.js +32 -65
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.1 -- Internal: delegate to @zakkster/lite-cleanup
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Internal: `FinalizationRegistry` bookkeeping now delegated to
|
|
8
|
+
`@zakkster/lite-cleanup`. Behavior identical; extracted the shared
|
|
9
|
+
pattern used by lite-observe and lite-floating into a canonical primitive
|
|
10
|
+
that both packages consume.
|
|
11
|
+
- New diagnostic export `_registrySize()` surfaces the live handle count
|
|
12
|
+
from the underlying disposal registry.
|
|
13
|
+
|
|
14
|
+
### Notes
|
|
15
|
+
|
|
16
|
+
- No public API changes.
|
|
17
|
+
- Allocation gate unchanged (verified: 0 B/call, 0 scavenges on all
|
|
18
|
+
dispatch paths).
|
|
19
|
+
- All existing tests pass unchanged.
|
|
20
|
+
- `@zakkster/lite-cleanup` added as a runtime dependency (~500 bytes
|
|
21
|
+
gzipped); this is the shared cost paid once across all consumers of
|
|
22
|
+
the ecosystem.
|
|
23
|
+
|
|
3
24
|
## 1.0.0 -- Production cut
|
|
4
25
|
|
|
5
26
|
The 0.2.x line proved out FinalizationRegistry-based orphan cleanup; the
|
package/index.d.ts
CHANGED
|
@@ -217,3 +217,6 @@ export function attachFinalizer<T extends { dispose: () => void }>(
|
|
|
217
217
|
* browsers + Node 14+). Returns false on environments without it; in that
|
|
218
218
|
* case `attachFinalizer` becomes a no-op pass-through. */
|
|
219
219
|
export function _hasFinalizationRegistry(): boolean;
|
|
220
|
+
|
|
221
|
+
/** Number of live handles tracked by the shared disposal registry. */
|
|
222
|
+
export function _registrySize(): number;
|
package/index.js
CHANGED
|
@@ -22,4 +22,4 @@ export { _resetIntersection, _intersectionBucketCount } from './src/Intersection
|
|
|
22
22
|
export { _resetMutation, _mutationObserverCount } from './src/Mutation.js';
|
|
23
23
|
export { _resetMedia, _mediaCacheSize } from './src/Media.js';
|
|
24
24
|
export { _forceVisibilitySync } from './src/Visibility.js';
|
|
25
|
-
export { _hasFinalizationRegistry, attachFinalizer } from './src/_finalize.js';
|
|
25
|
+
export { _hasFinalizationRegistry, _registrySize, attachFinalizer } from './src/_finalize.js';
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zakkster/lite-observe",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"author": "Zahary Shinikchiev <shinikchiev@yahoo.com>",
|
|
5
5
|
"description": "Zero-GC reactive bridge for the DOM observer APIs. ResizeObserver, IntersectionObserver, MutationObserver, matchMedia, and Page Visibility collapsed to fine-grained lite-signal signals. Shared registry: N components observing the same element pay one observer cost.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
8
|
-
"main": "index.js",
|
|
9
|
-
"module": "index.js",
|
|
8
|
+
"main": "./index.js",
|
|
9
|
+
"module": "./index.js",
|
|
10
10
|
"types": "./index.d.ts",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
@@ -49,6 +49,9 @@
|
|
|
49
49
|
"tiny",
|
|
50
50
|
"esm"
|
|
51
51
|
],
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@zakkster/lite-cleanup": "^1.0.0"
|
|
54
|
+
},
|
|
52
55
|
"peerDependencies": {
|
|
53
56
|
"@zakkster/lite-signal": "^1.2.2"
|
|
54
57
|
},
|
|
@@ -65,6 +68,10 @@
|
|
|
65
68
|
"url": "https://github.com/PeshoVurtoleta/lite-observe/issues",
|
|
66
69
|
"email": "shinikchiev@yahoo.com"
|
|
67
70
|
},
|
|
71
|
+
"funding": {
|
|
72
|
+
"type": "github",
|
|
73
|
+
"url": "https://github.com/sponsors/PeshoVurtoleta"
|
|
74
|
+
},
|
|
68
75
|
"engines": {
|
|
69
76
|
"node": ">=18"
|
|
70
77
|
}
|
package/src/_finalize.js
CHANGED
|
@@ -1,44 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @zakkster/lite-observe -- shared orphan-handle finalizer.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Delegates to @zakkster/lite-cleanup's disposal registry. The semantics
|
|
5
|
+
* are identical to the previous inline FinalizationRegistry: dropped
|
|
6
|
+
* handles are eventually cleaned up by GC; explicit dispose unregisters
|
|
7
|
+
* the finalizer and runs cleanup synchronously; double-fire is prevented
|
|
8
|
+
* by the registry's `disposed` guard.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* The `attachFinalizer` contract is unchanged:
|
|
11
|
+
* - `innerCleanup` MUST NOT reference `handle`.
|
|
12
|
+
* - `innerCleanup` MUST be idempotent.
|
|
13
|
+
* - Errors thrown by cleanup are swallowed by the registry.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* first. Explicit dispose remains the primary path; this is a safety
|
|
19
|
-
* net for the forgot-to-dispose case.
|
|
20
|
-
* 2. The held value (second arg to register) must not reference the
|
|
21
|
-
* handle. We pass the bare inner-cleanup closure, which captures only
|
|
22
|
-
* slot / element / Map references -- never the handle itself.
|
|
23
|
-
* 3. Calling explicit dispose unregisters the finalizer so it doesn't
|
|
24
|
-
* double-fire. The wrapper that does this DOES capture the handle
|
|
25
|
-
* (via `registry.unregister(handle)`), but it's set as a property of
|
|
26
|
-
* the handle, so the self-reference is collected with the handle.
|
|
27
|
-
*
|
|
28
|
-
* Single registry shared across the three modules so finalisation pressure
|
|
29
|
-
* is concentrated -- the runtime processes one queue, not three.
|
|
15
|
+
* Single registry shared across the three observer modules so
|
|
16
|
+
* finalisation pressure is concentrated -- the runtime processes one
|
|
17
|
+
* queue, not three.
|
|
30
18
|
*/
|
|
31
19
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// for other pending finalizations.
|
|
36
|
-
try { cleanup(); } catch (_e) { /* intentional */ }
|
|
37
|
-
})
|
|
38
|
-
: null;
|
|
20
|
+
import { createDisposalRegistry } from '@zakkster/lite-cleanup';
|
|
21
|
+
|
|
22
|
+
const registry = createDisposalRegistry({ name: 'lite-observe' });
|
|
39
23
|
|
|
40
24
|
/**
|
|
41
|
-
* Register a handle for
|
|
25
|
+
* Register a handle for orphan cleanup via @zakkster/lite-cleanup.
|
|
42
26
|
*
|
|
43
27
|
* Used internally by `observeResize`, `observeIntersection`, and
|
|
44
28
|
* `observeMutation` so dropped handles trigger disconnect via GC.
|
|
@@ -55,31 +39,6 @@ const registry = typeof FinalizationRegistry !== 'undefined'
|
|
|
55
39
|
* - Errors thrown are swallowed by the registry so the finalizer
|
|
56
40
|
* queue continues to drain for other pending finalisations.
|
|
57
41
|
*
|
|
58
|
-
* Note on timing: FinalizationRegistry is non-deterministic. The
|
|
59
|
-
* runtime decides when to process its queue. Explicit `dispose()` is
|
|
60
|
-
* still the primary path when you have a lifecycle hook; this is the
|
|
61
|
-
* safety net for the forgot-to-dispose case.
|
|
62
|
-
*
|
|
63
|
-
* Example -- building a ScrollObserver primitive:
|
|
64
|
-
*
|
|
65
|
-
* function observeScroll(target) {
|
|
66
|
-
* const positionSig = signal({ x: 0, y: 0 });
|
|
67
|
-
* let disposed = false;
|
|
68
|
-
* function onScroll() {
|
|
69
|
-
* positionSig.set({ x: target.scrollLeft, y: target.scrollTop });
|
|
70
|
-
* }
|
|
71
|
-
* target.addEventListener('scroll', onScroll);
|
|
72
|
-
* function cleanup() {
|
|
73
|
-
* if (disposed) return;
|
|
74
|
-
* disposed = true;
|
|
75
|
-
* target.removeEventListener('scroll', onScroll);
|
|
76
|
-
* }
|
|
77
|
-
* return attachFinalizer(
|
|
78
|
-
* { position: positionSig, dispose: cleanup },
|
|
79
|
-
* cleanup
|
|
80
|
-
* );
|
|
81
|
-
* }
|
|
82
|
-
*
|
|
83
42
|
* @param {object} handle Handle returned to the consumer; must
|
|
84
43
|
* have a `.dispose` method to wrap.
|
|
85
44
|
* @param {() => void} innerCleanup Bare cleanup; MUST NOT reference
|
|
@@ -88,24 +47,32 @@ const registry = typeof FinalizationRegistry !== 'undefined'
|
|
|
88
47
|
* unregister the finalizer before running cleanup.
|
|
89
48
|
*/
|
|
90
49
|
export function attachFinalizer(handle, innerCleanup) {
|
|
91
|
-
|
|
92
|
-
registry.register(handle, innerCleanup, handle);
|
|
50
|
+
const handleId = registry.register(handle, innerCleanup);
|
|
93
51
|
const originalDispose = handle.dispose;
|
|
94
52
|
handle.dispose = function () {
|
|
95
|
-
registry.unregister(
|
|
53
|
+
registry.unregister(handleId);
|
|
96
54
|
originalDispose();
|
|
97
55
|
};
|
|
98
56
|
return handle;
|
|
99
57
|
}
|
|
100
58
|
|
|
101
59
|
/**
|
|
102
|
-
* Test hook.
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* portable way to encourage processing.
|
|
60
|
+
* Test hook. Returns true when the underlying disposal registry is
|
|
61
|
+
* backed by a FinalizationRegistry (always true on modern runtimes,
|
|
62
|
+
* since lite-cleanup requires it).
|
|
106
63
|
*
|
|
107
64
|
* @internal
|
|
108
65
|
*/
|
|
109
66
|
export function _hasFinalizationRegistry() {
|
|
110
|
-
return
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Diagnostic: number of live (registered, not yet disposed or finalized)
|
|
72
|
+
* handles tracked by the shared registry.
|
|
73
|
+
*
|
|
74
|
+
* @internal
|
|
75
|
+
*/
|
|
76
|
+
export function _registrySize() {
|
|
77
|
+
return registry.size();
|
|
111
78
|
}
|