@fusion.dev/helix-core 0.0.8 → 0.0.10
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 +30 -0
- package/custom-elements.json +26 -0
- package/dist/utils/ticker/ticker.d.ts +30 -0
- package/dist/utils/ticker/ticker.js +31 -6
- package/package.json +4 -4
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @fusion.dev/helix-core
|
|
2
|
+
|
|
3
|
+
The primitives both Helix tiers are built from: base classes, mixins, decorators
|
|
4
|
+
and utilities.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npm install @fusion.dev/helix-core
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Useful directly if you are writing custom elements that need to sit alongside
|
|
11
|
+
Helix and behave the same way — controlled properties that answer, a single
|
|
12
|
+
page-wide ticker rather than one timer per element, scroll-parent resolution that
|
|
13
|
+
crosses shadow boundaries, and duplicate-registration detection.
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { duplicateRegistrations } from '@fusion.dev/helix-core'
|
|
17
|
+
|
|
18
|
+
// Two copies of a library on one page is silent: the first registration keeps
|
|
19
|
+
// the tag and `instanceof` against the second is quietly false.
|
|
20
|
+
console.log(duplicateRegistrations())
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Most consumers get this transitively through
|
|
24
|
+
[`@fusion.dev/helix`](https://www.npmjs.com/package/@fusion.dev/helix) or
|
|
25
|
+
[`@fusion.dev/helix-headless`](https://www.npmjs.com/package/@fusion.dev/helix-headless)
|
|
26
|
+
and never import it by name.
|
|
27
|
+
|
|
28
|
+
[Documentation](https://helix.fusion.dev)
|
|
29
|
+
|
|
30
|
+
MIT
|
package/custom-elements.json
CHANGED
|
@@ -7782,6 +7782,24 @@
|
|
|
7782
7782
|
"kind": "javascript-module",
|
|
7783
7783
|
"path": "src/utils/ticker/ticker.ts",
|
|
7784
7784
|
"declarations": [
|
|
7785
|
+
{
|
|
7786
|
+
"kind": "function",
|
|
7787
|
+
"name": "withClock",
|
|
7788
|
+
"return": {
|
|
7789
|
+
"type": {
|
|
7790
|
+
"text": "(() => void)"
|
|
7791
|
+
}
|
|
7792
|
+
},
|
|
7793
|
+
"parameters": [
|
|
7794
|
+
{
|
|
7795
|
+
"name": "clock",
|
|
7796
|
+
"type": {
|
|
7797
|
+
"text": "Clock"
|
|
7798
|
+
}
|
|
7799
|
+
}
|
|
7800
|
+
],
|
|
7801
|
+
"description": "Replaces the ticker with an empty one on the given clock, and hands back the\nway to put the real one back.\n\n**On `globalThis` with the rest of it, deliberately.** The spec bundler emits\none bundle per family and the runner loads them all into one page, so a\nmodule-level clock would have the reach of one bundle while the timer it\nschedules has the reach of the document. That is the fault the arrangement\nabove exists to avoid, and this must not reintroduce it."
|
|
7802
|
+
},
|
|
7785
7803
|
{
|
|
7786
7804
|
"kind": "function",
|
|
7787
7805
|
"name": "subscribe",
|
|
@@ -7808,6 +7826,14 @@
|
|
|
7808
7826
|
}
|
|
7809
7827
|
],
|
|
7810
7828
|
"exports": [
|
|
7829
|
+
{
|
|
7830
|
+
"kind": "js",
|
|
7831
|
+
"name": "withClock",
|
|
7832
|
+
"declaration": {
|
|
7833
|
+
"name": "withClock",
|
|
7834
|
+
"module": "src/utils/ticker/ticker.ts"
|
|
7835
|
+
}
|
|
7836
|
+
},
|
|
7811
7837
|
{
|
|
7812
7838
|
"kind": "js",
|
|
7813
7839
|
"name": "subscribe",
|
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three platform calls this makes, taken as values so a test can drive them.
|
|
3
|
+
*
|
|
4
|
+
* **Not a configuration point.** It exists because the alternative is asserting
|
|
5
|
+
* on the machine: a spec that subscribes at 1000ms and waits 1400 is measuring
|
|
6
|
+
* how loaded the runner is, and a loaded runner eats 400ms. One did, on a green
|
|
7
|
+
* branch, and blocked a merge that had nothing wrong with it. A bigger margin is
|
|
8
|
+
* the same bug at a lower rate.
|
|
9
|
+
*
|
|
10
|
+
* Bun 1.1.43 is what this workspace pins, and it has `jest.useFakeTimers()`
|
|
11
|
+
* without `jest.advanceTimersByTime`, so there is nothing to advance a faked
|
|
12
|
+
* timer with. Measured rather than assumed: a spec asserting that function
|
|
13
|
+
* exists throws.
|
|
14
|
+
*/
|
|
15
|
+
export interface Clock {
|
|
16
|
+
clearTimer: (timer: ReturnType<typeof setTimeout>) => void;
|
|
17
|
+
now: () => number;
|
|
18
|
+
setTimer: (run: () => void, wait: number) => ReturnType<typeof setTimeout>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Replaces the ticker with an empty one on the given clock, and hands back the
|
|
22
|
+
* way to put the real one back.
|
|
23
|
+
*
|
|
24
|
+
* **On `globalThis` with the rest of it, deliberately.** The spec bundler emits
|
|
25
|
+
* one bundle per family and the runner loads them all into one page, so a
|
|
26
|
+
* module-level clock would have the reach of one bundle while the timer it
|
|
27
|
+
* schedules has the reach of the document. That is the fault the arrangement
|
|
28
|
+
* above exists to avoid, and this must not reintroduce it.
|
|
29
|
+
*/
|
|
30
|
+
export declare const withClock: (clock: Clock) => (() => void);
|
|
1
31
|
/**
|
|
2
32
|
* Asks to be run in `delay` milliseconds, and returns the way to stop asking.
|
|
3
33
|
*
|
|
@@ -1,18 +1,43 @@
|
|
|
1
|
+
const REAL = {
|
|
2
|
+
clearTimer: (timer) => {
|
|
3
|
+
clearTimeout(timer);
|
|
4
|
+
},
|
|
5
|
+
now: () => Date.now(),
|
|
6
|
+
setTimer: (run, wait) => setTimeout(run, wait),
|
|
7
|
+
};
|
|
1
8
|
const KEY = Symbol.for('helix.ticker');
|
|
2
9
|
const ticker = () => {
|
|
3
10
|
const global = globalThis;
|
|
4
11
|
let found = global[KEY];
|
|
5
12
|
if (!found) {
|
|
6
|
-
found = { timer: undefined, waiting: new Set() };
|
|
13
|
+
found = { clock: REAL, timer: undefined, waiting: new Set() };
|
|
7
14
|
global[KEY] = found;
|
|
8
15
|
}
|
|
9
16
|
return found;
|
|
10
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Replaces the ticker with an empty one on the given clock, and hands back the
|
|
20
|
+
* way to put the real one back.
|
|
21
|
+
*
|
|
22
|
+
* **On `globalThis` with the rest of it, deliberately.** The spec bundler emits
|
|
23
|
+
* one bundle per family and the runner loads them all into one page, so a
|
|
24
|
+
* module-level clock would have the reach of one bundle while the timer it
|
|
25
|
+
* schedules has the reach of the document. That is the fault the arrangement
|
|
26
|
+
* above exists to avoid, and this must not reintroduce it.
|
|
27
|
+
*/
|
|
28
|
+
export const withClock = (clock) => {
|
|
29
|
+
const global = globalThis;
|
|
30
|
+
const before = global[KEY];
|
|
31
|
+
global[KEY] = { clock, timer: undefined, waiting: new Set() };
|
|
32
|
+
return () => {
|
|
33
|
+
global[KEY] = before;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
11
36
|
/** Sets the one timeout, for the soonest thing anybody is waiting on. */
|
|
12
37
|
const schedule = () => {
|
|
13
38
|
const held = ticker();
|
|
14
39
|
if (held.timer !== undefined) {
|
|
15
|
-
|
|
40
|
+
held.clock.clearTimer(held.timer);
|
|
16
41
|
held.timer = undefined;
|
|
17
42
|
}
|
|
18
43
|
if (held.waiting.size === 0) {
|
|
@@ -21,10 +46,10 @@ const schedule = () => {
|
|
|
21
46
|
const soonest = Math.min(...[...held.waiting].map((one) => one.at));
|
|
22
47
|
/* Floored at a second: a subscriber handed a zero would be re-armed inside the
|
|
23
48
|
callback that fired it, which is a busy loop wearing a timer's clothes. */
|
|
24
|
-
const wait = Math.max(1000, soonest -
|
|
25
|
-
held.timer =
|
|
49
|
+
const wait = Math.max(1000, soonest - held.clock.now());
|
|
50
|
+
held.timer = held.clock.setTimer(() => {
|
|
26
51
|
held.timer = undefined;
|
|
27
|
-
const now =
|
|
52
|
+
const now = held.clock.now();
|
|
28
53
|
/* Copied before iterating, because every callback re-subscribes and would
|
|
29
54
|
otherwise be walked again inside the same pass. */
|
|
30
55
|
for (const one of [...held.waiting]) {
|
|
@@ -45,7 +70,7 @@ const schedule = () => {
|
|
|
45
70
|
*/
|
|
46
71
|
export const subscribe = (delay, run) => {
|
|
47
72
|
const held = ticker();
|
|
48
|
-
const entry = { at:
|
|
73
|
+
const entry = { at: held.clock.now() + delay, run };
|
|
49
74
|
held.waiting.add(entry);
|
|
50
75
|
schedule();
|
|
51
76
|
return () => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusion.dev/helix-core",
|
|
3
3
|
"description": "Helix element primitives: base classes, mixins, decorators, utilities.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.10",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ross Sheppard <ross.sheppard@fusion.dev>",
|
|
7
7
|
"repository": {
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
"bugs": "https://github.com/project-fusion/fusion/issues",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@floating-ui/dom": "^1.8.0",
|
|
36
|
-
"@fusion.dev/helix-icons": "0.0.
|
|
36
|
+
"@fusion.dev/helix-icons": "0.0.10"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@custom-elements-manifest/analyzer": "^0.11.0",
|
|
40
|
-
"@fusion.dev/helix-cem": "0.0.
|
|
41
|
-
"@fusion.dev/helix-testing": "0.0.
|
|
40
|
+
"@fusion.dev/helix-cem": "0.0.10",
|
|
41
|
+
"@fusion.dev/helix-testing": "0.0.10",
|
|
42
42
|
"lit": "^3.2.1"
|
|
43
43
|
},
|
|
44
44
|
"homepage": "https://fusion.dev",
|