polite-media 0.3.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/CHANGELOG.md +113 -0
- package/LICENSE +21 -0
- package/README.md +675 -0
- package/dist/coordinator.d.ts +231 -0
- package/dist/coordinator.d.ts.map +1 -0
- package/dist/coordinator.js +1017 -0
- package/dist/coordinator.js.map +1 -0
- package/dist/env.d.ts +30 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +49 -0
- package/dist/env.js.map +1 -0
- package/dist/events.d.ts +70 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +42 -0
- package/dist/events.js.map +1 -0
- package/dist/image.css +1 -0
- package/dist/image.d.ts +45 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +127 -0
- package/dist/image.js.map +1 -0
- package/dist/layer.css +1 -0
- package/dist/reveal.d.ts +34 -0
- package/dist/reveal.d.ts.map +1 -0
- package/dist/reveal.js +72 -0
- package/dist/reveal.js.map +1 -0
- package/dist/sources.d.ts +22 -0
- package/dist/sources.d.ts.map +1 -0
- package/dist/sources.js +148 -0
- package/dist/sources.js.map +1 -0
- package/dist/targets.d.ts +20 -0
- package/dist/targets.d.ts.map +1 -0
- package/dist/targets.js +17 -0
- package/dist/targets.js.map +1 -0
- package/dist/video.css +1 -0
- package/dist/video.d.ts +15 -0
- package/dist/video.d.ts.map +1 -0
- package/dist/video.js +17 -0
- package/dist/video.js.map +1 -0
- package/dist/warm.d.ts +13 -0
- package/dist/warm.d.ts.map +1 -0
- package/dist/warm.js +12 -0
- package/dist/warm.js.map +1 -0
- package/dist/warming.d.ts +62 -0
- package/dist/warming.d.ts.map +1 -0
- package/dist/warming.js +136 -0
- package/dist/warming.js.map +1 -0
- package/package.json +96 -0
- package/src/coordinator.ts +1337 -0
- package/src/env.ts +56 -0
- package/src/events.ts +78 -0
- package/src/image.css +74 -0
- package/src/image.ts +160 -0
- package/src/layer.css +60 -0
- package/src/reveal.ts +75 -0
- package/src/sources.ts +164 -0
- package/src/targets.ts +27 -0
- package/src/video.css +74 -0
- package/src/video.ts +32 -0
- package/src/warm.ts +12 -0
- package/src/warming.ts +162 -0
package/dist/sources.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { mediaQuery } from './env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Picks which `<source>` a video actually loads, and moves on when one turns out
|
|
4
|
+
* to be undecodable.
|
|
5
|
+
*
|
|
6
|
+
* Two problems, one mechanism.
|
|
7
|
+
*
|
|
8
|
+
* `<source media>` is evaluated once, during resource selection, and never
|
|
9
|
+
* again -- in every engine, not just Safari. Verified in Chromium: after loading
|
|
10
|
+
* at 1200px, resizing to 500px left `currentSrc` on the wide source even though
|
|
11
|
+
* the narrow query then matched. The spec has no hook for it, since resource
|
|
12
|
+
* selection only re-runs when `src` or the `<source>` children change. So a
|
|
13
|
+
* source list alone cannot respond to a viewport that moved since parse.
|
|
14
|
+
* Resolving the list here and assigning `video.src` directly sidesteps that
|
|
15
|
+
* entirely: one assignment, no negotiation left to go stale.
|
|
16
|
+
*
|
|
17
|
+
* And `canPlayType` is only ever a claim. The HTML Standard sets the bar at the
|
|
18
|
+
* user agent being "confident that the type represents a media resource that it
|
|
19
|
+
* can render", and confidence is not a guarantee: in this project's own fixtures
|
|
20
|
+
* Chromium answered "probably" for AV1 and then failed at
|
|
21
|
+
* `dav1d_send_data()`. So the codec check only drops the flat "no" answers;
|
|
22
|
+
* among what survives, document order sets the try order and the `error` event
|
|
23
|
+
* decides the outcome.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately *not* re-selecting on resize. Reassigning `src` restarts
|
|
26
|
+
* playback from frame 0, so a phone rotating mid-scroll would visibly rewind the
|
|
27
|
+
* video. The first choice sticks for the page's lifetime.
|
|
28
|
+
*/
|
|
29
|
+
/** `MediaError.MEDIA_ERR_DECODE` and `MEDIA_ERR_SRC_NOT_SUPPORTED`. */
|
|
30
|
+
const MEDIA_ERR_DECODE = 3;
|
|
31
|
+
const MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
|
|
32
|
+
/**
|
|
33
|
+
* Whether an error means "this file is unusable, try another" as opposed to
|
|
34
|
+
* "something happened to this attempt".
|
|
35
|
+
*
|
|
36
|
+
* Codes matter here. `MEDIA_ERR_ABORTED` (1) is what the element reports when
|
|
37
|
+
* its `src` is reassigned -- which this module does itself -- so treating every
|
|
38
|
+
* error as fatal would cascade through the whole candidate list on the first
|
|
39
|
+
* assignment. `MEDIA_ERR_NETWORK` (2) means the fetch failed on a resource that
|
|
40
|
+
* was previously fine; another candidate is unlikely to fare better on the same
|
|
41
|
+
* connection, and burning the list would leave nothing to retry with.
|
|
42
|
+
*/
|
|
43
|
+
export function isUnusable(error) {
|
|
44
|
+
if (!error)
|
|
45
|
+
return false;
|
|
46
|
+
return error.code === MEDIA_ERR_DECODE || error.code === MEDIA_ERR_SRC_NOT_SUPPORTED;
|
|
47
|
+
}
|
|
48
|
+
let warnedMediaGap = false;
|
|
49
|
+
/**
|
|
50
|
+
* Which `<source>` this video could actually load, best first.
|
|
51
|
+
*
|
|
52
|
+
* `media` is treated as a *preference*, not an exclusion. Two queries meant to
|
|
53
|
+
* partition the viewport often do not quite meet: `(max-width: 50rem)` beside
|
|
54
|
+
* `(min-width: 50.001rem)` leaves 0.016px matching neither at a 16px root, and
|
|
55
|
+
* a root font-size that is not 16px moves the boundary somewhere else again.
|
|
56
|
+
* Widths in the gap select nothing and the video has nothing to play. The
|
|
57
|
+
* browser behaves that way too, which is no help to anyone.
|
|
58
|
+
*
|
|
59
|
+
* So when no source claims the current viewport, every decodable one is a
|
|
60
|
+
* candidate and document order decides. The alternative was a markup rule, "the
|
|
61
|
+
* last <source> must carry no media attribute", which every consumer had to
|
|
62
|
+
* remember and which a real project had already got wrong.
|
|
63
|
+
*
|
|
64
|
+
* The cost is that `media` can no longer mean "and otherwise play nothing". It
|
|
65
|
+
* never reliably could, since the markup contract already required an
|
|
66
|
+
* unconditional fallback, and `atOnce: { small: 0 }` says that properly.
|
|
67
|
+
*/
|
|
68
|
+
function candidatesFor(video) {
|
|
69
|
+
// `:scope >` so a <source> belonging to some nested media element is never
|
|
70
|
+
// mistaken for this one's.
|
|
71
|
+
const sources = [...video.querySelectorAll(':scope > source')];
|
|
72
|
+
const decodable = sources.filter((source) => {
|
|
73
|
+
// The attribute, not the property: `.src` resolves against the document
|
|
74
|
+
// base, so `<source src="">` comes back as the page's own URL -- truthy,
|
|
75
|
+
// and it would then be handed to the video as if it were a media file.
|
|
76
|
+
if (!source.getAttribute('src'))
|
|
77
|
+
return false;
|
|
78
|
+
// Empty string is the only definite "no" canPlayType offers. "maybe" is
|
|
79
|
+
// kept, because "maybe" is what browsers say about most things.
|
|
80
|
+
return !source.type || video.canPlayType(source.type) !== '';
|
|
81
|
+
});
|
|
82
|
+
const claiming = decodable.filter((source) => {
|
|
83
|
+
const media = source.getAttribute('media');
|
|
84
|
+
return !media || mediaQuery(media).matches;
|
|
85
|
+
});
|
|
86
|
+
if (claiming.length === 0 && decodable.length > 0)
|
|
87
|
+
warnMediaGap(video);
|
|
88
|
+
return {
|
|
89
|
+
declared: sources.length,
|
|
90
|
+
playable: (claiming.length > 0 ? claiming : decodable).map((source) => source.src),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Fires only when the gap actually opens, rather than whenever the markup looks
|
|
95
|
+
* capable of it. A static check would have to nag every page whose sources are
|
|
96
|
+
* all conditional, including the overwhelming majority whose queries really do
|
|
97
|
+
* cover every width they will ever see.
|
|
98
|
+
*/
|
|
99
|
+
function warnMediaGap(video) {
|
|
100
|
+
if (warnedMediaGap)
|
|
101
|
+
return;
|
|
102
|
+
warnedMediaGap = true;
|
|
103
|
+
console.warn('polite-media: no <source> claims this viewport, so the first decodable one was used ' +
|
|
104
|
+
'instead of nothing. Give the last <source> no media attribute to choose the fallback ' +
|
|
105
|
+
'yourself.', video);
|
|
106
|
+
}
|
|
107
|
+
export function manageSources(video) {
|
|
108
|
+
const { declared, playable: candidates } = candidatesFor(video);
|
|
109
|
+
let index = -1;
|
|
110
|
+
const load = () => {
|
|
111
|
+
const next = candidates[index];
|
|
112
|
+
if (next === undefined)
|
|
113
|
+
return false;
|
|
114
|
+
video.src = next;
|
|
115
|
+
// Required: assigning `src` alone does not restart resource selection on an
|
|
116
|
+
// element that has already loaded.
|
|
117
|
+
video.load();
|
|
118
|
+
return true;
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
select() {
|
|
122
|
+
// "No <source> children at all" and "sources were declared but none
|
|
123
|
+
// qualified" are different situations and must not share a branch. The
|
|
124
|
+
// first is a video authored with a plain `src` -- somebody else's
|
|
125
|
+
// arrangement, left exactly as it is. The second is a video with nothing
|
|
126
|
+
// playable, which has to report failure so the poster stays and the host
|
|
127
|
+
// is told.
|
|
128
|
+
if (declared === 0)
|
|
129
|
+
return Boolean(video.currentSrc || video.getAttribute('src'));
|
|
130
|
+
// Re-checked rather than assumed: a first select() that found nothing
|
|
131
|
+
// playable still advanced the index, so answering a later call with a bare
|
|
132
|
+
// `true` would report success for a video that never loaded anything.
|
|
133
|
+
if (index >= 0)
|
|
134
|
+
return candidates[index] !== undefined;
|
|
135
|
+
index = 0;
|
|
136
|
+
return load();
|
|
137
|
+
},
|
|
138
|
+
advance() {
|
|
139
|
+
index += 1;
|
|
140
|
+
return load();
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Internal reset for tests. Not exported from the package entry point. */
|
|
145
|
+
export function resetSourceWarnings() {
|
|
146
|
+
warnedMediaGap = false;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sources.js","sourceRoot":"","sources":["../src/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,uEAAuE;AACvE,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAC3B,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAEtC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,KAAwB;IACjD,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,OAAO,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,2BAA2B,CAAC;AACvF,CAAC;AASD,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,aAAa,CAAC,KAAuB;IAC5C,2EAA2E;IAC3E,2BAA2B;IAC3B,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,gBAAgB,CAAoB,iBAAiB,CAAC,CAAC,CAAC;IAElF,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;QAC1C,wEAAwE;QACxE,yEAAyE;QACzE,uEAAuE;QACvE,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC9C,wEAAwE;QACxE,gEAAgE;QAChE,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;QAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC3C,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IAEvE,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,MAAM;QACxB,QAAQ,EAAE,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC;KACnF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,KAAuB;IAC3C,IAAI,cAAc;QAAE,OAAO;IAC3B,cAAc,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,IAAI,CACV,sFAAsF;QACpF,uFAAuF;QACvF,WAAW,EACb,KAAK,CACN,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAuB;IACnD,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAChE,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;IAEf,MAAM,IAAI,GAAG,GAAY,EAAE;QACzB,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACrC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC;QACjB,4EAA4E;QAC5E,mCAAmC;QACnC,KAAK,CAAC,IAAI,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,OAAO;QACL,MAAM;YACJ,oEAAoE;YACpE,uEAAuE;YACvE,kEAAkE;YAClE,yEAAyE;YACzE,yEAAyE;YACzE,WAAW;YACX,IAAI,QAAQ,KAAK,CAAC;gBAAE,OAAO,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;YAClF,sEAAsE;YACtE,2EAA2E;YAC3E,sEAAsE;YACtE,IAAI,KAAK,IAAI,CAAC;gBAAE,OAAO,UAAU,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC;YACvD,KAAK,GAAG,CAAC,CAAC;YACV,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QACD,OAAO;YACL,KAAK,IAAI,CAAC,CAAC;YACX,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,mBAAmB;IACjC,cAAc,GAAG,KAAK,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* How both halves name the elements they act on, so `revealImages('.card img')`
|
|
4
|
+
* and `registerAll('[data-polite-media] video')` mean the same thing rather than
|
|
5
|
+
* being two similar-looking ideas.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Anything that names one or more elements: a selector, a single element, or any
|
|
9
|
+
* collection of them.
|
|
10
|
+
*
|
|
11
|
+
* `ArrayLike` is listed alongside `Iterable` deliberately. `NodeListOf` and
|
|
12
|
+
* `HTMLCollectionOf` are iterable at runtime, but their `[Symbol.iterator]` lives
|
|
13
|
+
* in `lib.dom.iterable`, so a consumer whose `lib` omits it cannot pass
|
|
14
|
+
* `document.querySelectorAll('img')` to an `Iterable`-only parameter even though
|
|
15
|
+
* it works. `ArrayLike` is structural and needs no `lib` support.
|
|
16
|
+
*/
|
|
17
|
+
export type Target<T extends Element> = string | T | ArrayLike<T> | Iterable<T>;
|
|
18
|
+
/** Resolves a {@link Target} to the elements it names. */
|
|
19
|
+
export declare function resolveTargets<T extends Element>(target: Target<T>): T[];
|
|
20
|
+
//# sourceMappingURL=targets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;GASG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,OAAO,IAAI,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AAEhF,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,CAAC,SAAS,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAMxE"}
|
package/dist/targets.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* How both halves name the elements they act on, so `revealImages('.card img')`
|
|
4
|
+
* and `registerAll('[data-polite-media] video')` mean the same thing rather than
|
|
5
|
+
* being two similar-looking ideas.
|
|
6
|
+
*/
|
|
7
|
+
/** Resolves a {@link Target} to the elements it names. */
|
|
8
|
+
export function resolveTargets(target) {
|
|
9
|
+
if (typeof target === 'string')
|
|
10
|
+
return [...document.querySelectorAll(target)];
|
|
11
|
+
// A single element is the obvious thing to pass when you already hold one, and
|
|
12
|
+
// it used to be rejected: `revealImages(myImg)` did not compile.
|
|
13
|
+
if (target instanceof Element)
|
|
14
|
+
return [target];
|
|
15
|
+
return Array.from(target);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=targets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.js","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,0DAA0D;AAC1D,MAAM,UAAU,cAAc,CAAoB,MAAiB;IACjE,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,gBAAgB,CAAI,MAAM,CAAC,CAAC,CAAC;IACjF,+EAA+E;IAC/E,iEAAiE;IACjE,IAAI,MAAM,YAAY,OAAO;QAAE,OAAO,CAAC,MAAW,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAsB,CAAC,CAAC;AAC5C,CAAC"}
|
package/dist/video.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[data-polite-media]>video{opacity:0;transition:opacity var(--polite-fade, 0s) ease}[data-polite-media][data-polite-ready]>video{opacity:1}[data-polite-media][data-polite-ready]>:is(img,picture){visibility:hidden;transition:visibility 0s linear var(--polite-fade, 0s)}[data-polite-media][data-polite-failed]>video{opacity:0}@media(prefers-reduced-motion:reduce){[data-polite-media]>video,[data-polite-media]>:is(img,picture){transition:none}}
|
package/dist/video.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Everything for background video. Imported on its own so an image-only page
|
|
4
|
+
* never pays for the IntersectionObserver, source selection or arbitration.
|
|
5
|
+
*
|
|
6
|
+
* The export list is deliberately small. Every name here is permanent once
|
|
7
|
+
* published, and adding one later is not a breaking change while removing one
|
|
8
|
+
* is -- so anything without a caller that can be named stays internal. The
|
|
9
|
+
* environment gates and the reveal primitive are used by this package and are
|
|
10
|
+
* not part of its interface.
|
|
11
|
+
*/
|
|
12
|
+
export { configure, pauseAll, register, registerAll, resumeAll, unregister, unregisterAll, } from './coordinator.js';
|
|
13
|
+
export type { AtOnce, ConfigureOptions, RegisterOptions, VideoTarget } from './coordinator.js';
|
|
14
|
+
export { POLITE_VIDEO_PAUSECHANGE, POLITE_VIDEO_FAILED, POLITE_VIDEO_READY, type PolitePauseEventDetail, type PoliteVideoEventDetail, } from './events.js';
|
|
15
|
+
//# sourceMappingURL=video.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../src/video.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EACL,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,SAAS,EACT,UAAU,EACV,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAK/F,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAC"}
|
package/dist/video.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Everything for background video. Imported on its own so an image-only page
|
|
4
|
+
* never pays for the IntersectionObserver, source selection or arbitration.
|
|
5
|
+
*
|
|
6
|
+
* The export list is deliberately small. Every name here is permanent once
|
|
7
|
+
* published, and adding one later is not a breaking change while removing one
|
|
8
|
+
* is -- so anything without a caller that can be named stays internal. The
|
|
9
|
+
* environment gates and the reveal primitive are used by this package and are
|
|
10
|
+
* not part of its interface.
|
|
11
|
+
*/
|
|
12
|
+
export { configure, pauseAll, register, registerAll, resumeAll, unregister, unregisterAll, } from './coordinator.js';
|
|
13
|
+
// Loaded for its `declare global` block as much as for the constants: the
|
|
14
|
+
// ElementEventMap and DocumentEventMap augmentation only reaches a consumer if
|
|
15
|
+
// this module is part of their program.
|
|
16
|
+
export { POLITE_VIDEO_PAUSECHANGE, POLITE_VIDEO_FAILED, POLITE_VIDEO_READY, } from './events.js';
|
|
17
|
+
//# sourceMappingURL=video.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video.js","sourceRoot":"","sources":["../src/video.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EACL,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,SAAS,EACT,UAAU,EACV,aAAa,GACd,MAAM,kBAAkB,CAAC;AAG1B,0EAA0E;AAC1E,+EAA+E;AAC/E,wCAAwC;AACxC,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,GAGnB,MAAM,aAAa,CAAC"}
|
package/dist/warm.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Warming a destination page's image before the visitor gets there. Imported on
|
|
4
|
+
* its own, so a page that only warms never pays for the video coordinator.
|
|
5
|
+
*
|
|
6
|
+
* A barrel for the same reason `video.ts` is one: this file is what the `./warm`
|
|
7
|
+
* subpath resolves to, so every name it exports is public and permanent once
|
|
8
|
+
* published. `resetWarmed` clears the dedup record for a test and has no caller
|
|
9
|
+
* outside the suite, so it stays in `warming.ts` where tests reach it directly.
|
|
10
|
+
*/
|
|
11
|
+
export { warm, warmOnIntent } from './warming.js';
|
|
12
|
+
export type { WarmOptions, WarmSource } from './warming.js';
|
|
13
|
+
//# sourceMappingURL=warm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warm.d.ts","sourceRoot":"","sources":["../src/warm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAClD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/warm.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Warming a destination page's image before the visitor gets there. Imported on
|
|
4
|
+
* its own, so a page that only warms never pays for the video coordinator.
|
|
5
|
+
*
|
|
6
|
+
* A barrel for the same reason `video.ts` is one: this file is what the `./warm`
|
|
7
|
+
* subpath resolves to, so every name it exports is public and permanent once
|
|
8
|
+
* published. `resetWarmed` clears the dedup record for a test and has no caller
|
|
9
|
+
* outside the suite, so it stays in `warming.ts` where tests reach it directly.
|
|
10
|
+
*/
|
|
11
|
+
export { warm, warmOnIntent } from './warming.js';
|
|
12
|
+
//# sourceMappingURL=warm.js.map
|
package/dist/warm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warm.js","sourceRoot":"","sources":["../src/warm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Warming a destination page's image before the visitor gets there, so the click
|
|
4
|
+
* lands on a picture that is already in the cache.
|
|
5
|
+
*
|
|
6
|
+
* Every document prefetcher fetches the HTML and stops: Astro's `data-astro-prefetch`,
|
|
7
|
+
* Next's `<Link>`, quicklink. The image inside that HTML is discovered only after
|
|
8
|
+
* the new document parses, which is exactly when it is too late to matter.
|
|
9
|
+
*
|
|
10
|
+
* The platform has the pieces but not in one place. `imagesrcset` and `imagesizes`
|
|
11
|
+
* do responsive selection, but MDN scopes them to `rel="preload"` with `as="image"`
|
|
12
|
+
* only, and scopes preload itself to resources "your page will need very soon". A
|
|
13
|
+
* speculative navigation wants `prefetch` semantics, where those attributes do not
|
|
14
|
+
* apply. So you can have the right selection or the right timing, not both, and
|
|
15
|
+
* anyone who wants both ends up re-implementing the browser's own rules in JS:
|
|
16
|
+
* parsing `sizes`, comparing `w` descriptors, guessing format support.
|
|
17
|
+
*
|
|
18
|
+
* This module does none of that. It builds the candidates as a detached
|
|
19
|
+
* `<picture>` and lets the browser choose, which is measurably the same algorithm
|
|
20
|
+
* the destination page will run (e2e/warm.spec.ts, all three engines). Nothing
|
|
21
|
+
* here parses a media query, so nothing here can disagree with one.
|
|
22
|
+
*
|
|
23
|
+
* A detached `<img>` also *fetches* what it selects, so no `<link>` is injected at
|
|
24
|
+
* all. That sidesteps the two documented failures of the link approach: Safari
|
|
25
|
+
* does not support `<link rel="prefetch">`, and Firefox aborts it with
|
|
26
|
+
* NS_BINDING_ABORTED when the response carries no explicit cache header.
|
|
27
|
+
*
|
|
28
|
+
* Not an image pipeline. It generates no URLs and knows no widths; it warms the
|
|
29
|
+
* candidates you already build.
|
|
30
|
+
*/
|
|
31
|
+
/** One `<picture>` candidate: a format, and the variants available in it. */
|
|
32
|
+
export interface WarmSource {
|
|
33
|
+
/** A MIME type such as `image/avif`. Omitted means "always a candidate". */
|
|
34
|
+
type?: string;
|
|
35
|
+
srcset: string;
|
|
36
|
+
}
|
|
37
|
+
export interface WarmOptions {
|
|
38
|
+
/** Ordered, first supported wins, exactly as `<source>` children behave. */
|
|
39
|
+
sources?: WarmSource[];
|
|
40
|
+
srcset?: string;
|
|
41
|
+
/** A single URL, for an image with no variants. */
|
|
42
|
+
src?: string;
|
|
43
|
+
/** Handed to the browser verbatim. Nothing in this package parses it. */
|
|
44
|
+
sizes?: string;
|
|
45
|
+
}
|
|
46
|
+
/** Warms one image: selects the variant this browser would request, and fetches it. */
|
|
47
|
+
export declare function warm(options: WarmOptions): void;
|
|
48
|
+
/**
|
|
49
|
+
* Warms whatever `resolve` names when the visitor shows intent toward `selector`.
|
|
50
|
+
*
|
|
51
|
+
* Delegated on the document, so links added later are covered without rebinding
|
|
52
|
+
* and one listener serves a whole grid. `resolve` returns the candidates rather
|
|
53
|
+
* than the library inventing a `data-*` vocabulary for them: where an app keeps
|
|
54
|
+
* its srcsets is the app's business.
|
|
55
|
+
*
|
|
56
|
+
* Returns a teardown. Listeners on `document` survive a `<ClientRouter />` swap,
|
|
57
|
+
* so a caller that re-binds per navigation stacks duplicates without one.
|
|
58
|
+
*/
|
|
59
|
+
export declare function warmOnIntent(selector: string, resolve: (element: Element) => WarmOptions | null | undefined): () => void;
|
|
60
|
+
/** Drops the dedup record so a test can warm the same thing twice. Not public API. */
|
|
61
|
+
export declare function resetWarmed(): void;
|
|
62
|
+
//# sourceMappingURL=warming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warming.d.ts","sourceRoot":"","sources":["../src/warming.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,4EAA4E;IAC5E,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4BD,uFAAuF;AACvF,wBAAgB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAyC/C;AASD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,WAAW,GAAG,IAAI,GAAG,SAAS,GAC5D,MAAM,IAAI,CAcZ;AAED,sFAAsF;AACtF,wBAAgB,WAAW,IAAI,IAAI,CAGlC"}
|
package/dist/warming.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Warming a destination page's image before the visitor gets there, so the click
|
|
4
|
+
* lands on a picture that is already in the cache.
|
|
5
|
+
*
|
|
6
|
+
* Every document prefetcher fetches the HTML and stops: Astro's `data-astro-prefetch`,
|
|
7
|
+
* Next's `<Link>`, quicklink. The image inside that HTML is discovered only after
|
|
8
|
+
* the new document parses, which is exactly when it is too late to matter.
|
|
9
|
+
*
|
|
10
|
+
* The platform has the pieces but not in one place. `imagesrcset` and `imagesizes`
|
|
11
|
+
* do responsive selection, but MDN scopes them to `rel="preload"` with `as="image"`
|
|
12
|
+
* only, and scopes preload itself to resources "your page will need very soon". A
|
|
13
|
+
* speculative navigation wants `prefetch` semantics, where those attributes do not
|
|
14
|
+
* apply. So you can have the right selection or the right timing, not both, and
|
|
15
|
+
* anyone who wants both ends up re-implementing the browser's own rules in JS:
|
|
16
|
+
* parsing `sizes`, comparing `w` descriptors, guessing format support.
|
|
17
|
+
*
|
|
18
|
+
* This module does none of that. It builds the candidates as a detached
|
|
19
|
+
* `<picture>` and lets the browser choose, which is measurably the same algorithm
|
|
20
|
+
* the destination page will run (e2e/warm.spec.ts, all three engines). Nothing
|
|
21
|
+
* here parses a media query, so nothing here can disagree with one.
|
|
22
|
+
*
|
|
23
|
+
* A detached `<img>` also *fetches* what it selects, so no `<link>` is injected at
|
|
24
|
+
* all. That sidesteps the two documented failures of the link approach: Safari
|
|
25
|
+
* does not support `<link rel="prefetch">`, and Firefox aborts it with
|
|
26
|
+
* NS_BINDING_ABORTED when the response carries no explicit cache header.
|
|
27
|
+
*
|
|
28
|
+
* Not an image pipeline. It generates no URLs and knows no widths; it warms the
|
|
29
|
+
* candidates you already build.
|
|
30
|
+
*/
|
|
31
|
+
import { connectionAllowsMedia } from './env.js';
|
|
32
|
+
const warmed = new Set();
|
|
33
|
+
/**
|
|
34
|
+
* Images with a fetch still in flight.
|
|
35
|
+
*
|
|
36
|
+
* A detached element is reachable from nothing once this function returns, and a
|
|
37
|
+
* collected image would be an aborted fetch. Precautionary rather than a fix for
|
|
38
|
+
* something observed: no engine was seen dropping one. It is cheap, and the bug
|
|
39
|
+
* it forecloses would be silent and load dependent, which is the kind no test
|
|
40
|
+
* here would catch.
|
|
41
|
+
*/
|
|
42
|
+
const inFlight = new Set();
|
|
43
|
+
/**
|
|
44
|
+
* Deduped on what the caller asked for rather than on the URL the browser picks,
|
|
45
|
+
* because the URL is not known until selection has already happened, and the
|
|
46
|
+
* repeat this exists to stop is the same link hovered twice.
|
|
47
|
+
*/
|
|
48
|
+
function keyOf(options) {
|
|
49
|
+
const sources = (options.sources ?? []).map((source) => `${source.type ?? ''}|${source.srcset}`);
|
|
50
|
+
// Joined on NUL rather than a newline: a srcset spanning several lines is
|
|
51
|
+
// ordinary formatting, and would otherwise let two different candidate sets
|
|
52
|
+
// build the same key and silently drop the second warm.
|
|
53
|
+
return [...sources, options.srcset ?? '', options.src ?? '', options.sizes ?? ''].join('\0');
|
|
54
|
+
}
|
|
55
|
+
/** Warms one image: selects the variant this browser would request, and fetches it. */
|
|
56
|
+
export function warm(options) {
|
|
57
|
+
// Speculative bytes are the first thing to drop on a metered connection. Astro
|
|
58
|
+
// downgrades to its `tap` strategy here rather than skipping; for an image
|
|
59
|
+
// nobody has asked for yet, not spending them at all is the better trade.
|
|
60
|
+
if (!connectionAllowsMedia())
|
|
61
|
+
return;
|
|
62
|
+
const sources = options.sources ?? [];
|
|
63
|
+
// Checked before anything is built, so an empty call allocates nothing, and
|
|
64
|
+
// before `warmed` is touched, so it cannot make a later correct call a no-op.
|
|
65
|
+
if (sources.length === 0 && !options.srcset && !options.src) {
|
|
66
|
+
console.warn('polite-media: warm() was given no src, srcset or sources, so nothing was warmed.');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const key = keyOf(options);
|
|
70
|
+
if (warmed.has(key))
|
|
71
|
+
return;
|
|
72
|
+
const picture = document.createElement('picture');
|
|
73
|
+
for (const source of sources) {
|
|
74
|
+
const element = document.createElement('source');
|
|
75
|
+
if (source.type)
|
|
76
|
+
element.type = source.type;
|
|
77
|
+
element.srcset = source.srcset;
|
|
78
|
+
picture.append(element);
|
|
79
|
+
}
|
|
80
|
+
const img = document.createElement('img');
|
|
81
|
+
picture.append(img);
|
|
82
|
+
// The visitor has not asked for this image and may never ask. It must not
|
|
83
|
+
// compete with the page they are actually looking at.
|
|
84
|
+
img.fetchPriority = 'low';
|
|
85
|
+
if (options.sizes)
|
|
86
|
+
img.sizes = options.sizes;
|
|
87
|
+
if (options.srcset)
|
|
88
|
+
img.srcset = options.srcset;
|
|
89
|
+
if (options.src)
|
|
90
|
+
img.src = options.src;
|
|
91
|
+
warmed.add(key);
|
|
92
|
+
inFlight.add(img);
|
|
93
|
+
const settled = () => void inFlight.delete(img);
|
|
94
|
+
img.addEventListener('load', settled, { once: true });
|
|
95
|
+
img.addEventListener('error', settled, { once: true });
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The events that mean "about to navigate". `pointerover` and `focusin` cover
|
|
99
|
+
* Astro's documented `hover` strategy, "when you hover over or focus on the
|
|
100
|
+
* link"; `touchstart` covers the touch case, where a hover never happens.
|
|
101
|
+
*/
|
|
102
|
+
const INTENT_EVENTS = ['pointerover', 'focusin', 'touchstart'];
|
|
103
|
+
/**
|
|
104
|
+
* Warms whatever `resolve` names when the visitor shows intent toward `selector`.
|
|
105
|
+
*
|
|
106
|
+
* Delegated on the document, so links added later are covered without rebinding
|
|
107
|
+
* and one listener serves a whole grid. `resolve` returns the candidates rather
|
|
108
|
+
* than the library inventing a `data-*` vocabulary for them: where an app keeps
|
|
109
|
+
* its srcsets is the app's business.
|
|
110
|
+
*
|
|
111
|
+
* Returns a teardown. Listeners on `document` survive a `<ClientRouter />` swap,
|
|
112
|
+
* so a caller that re-binds per navigation stacks duplicates without one.
|
|
113
|
+
*/
|
|
114
|
+
export function warmOnIntent(selector, resolve) {
|
|
115
|
+
const onIntent = (event) => {
|
|
116
|
+
const element = event.target?.closest?.(selector);
|
|
117
|
+
if (!element)
|
|
118
|
+
return;
|
|
119
|
+
const options = resolve(element);
|
|
120
|
+
if (options)
|
|
121
|
+
warm(options);
|
|
122
|
+
};
|
|
123
|
+
for (const type of INTENT_EVENTS) {
|
|
124
|
+
document.addEventListener(type, onIntent, { passive: true });
|
|
125
|
+
}
|
|
126
|
+
return () => {
|
|
127
|
+
for (const type of INTENT_EVENTS)
|
|
128
|
+
document.removeEventListener(type, onIntent);
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** Drops the dedup record so a test can warm the same thing twice. Not public API. */
|
|
132
|
+
export function resetWarmed() {
|
|
133
|
+
warmed.clear();
|
|
134
|
+
inFlight.clear();
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=warming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warming.js","sourceRoot":"","sources":["../src/warming.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "polite-media",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Background video, image reveals and next-page image warming. Reveals on the frame that actually painted, plays only what is on screen, respects data and motion.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"video",
|
|
7
|
+
"background-video",
|
|
8
|
+
"image",
|
|
9
|
+
"lazy",
|
|
10
|
+
"poster",
|
|
11
|
+
"autoplay",
|
|
12
|
+
"intersection-observer",
|
|
13
|
+
"requestVideoFrameCallback",
|
|
14
|
+
"performance",
|
|
15
|
+
"lcp",
|
|
16
|
+
"prefetch",
|
|
17
|
+
"srcset"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "sixra",
|
|
21
|
+
"packageManager": "pnpm@11.25.0",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/sixra/polite-media.git"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/sixra/polite-media#readme",
|
|
27
|
+
"bugs": "https://github.com/sixra/polite-media/issues",
|
|
28
|
+
"type": "module",
|
|
29
|
+
"sideEffects": [
|
|
30
|
+
"*.css"
|
|
31
|
+
],
|
|
32
|
+
"exports": {
|
|
33
|
+
"./video": {
|
|
34
|
+
"types": "./dist/video.d.ts",
|
|
35
|
+
"default": "./dist/video.js"
|
|
36
|
+
},
|
|
37
|
+
"./image": {
|
|
38
|
+
"types": "./dist/image.d.ts",
|
|
39
|
+
"default": "./dist/image.js"
|
|
40
|
+
},
|
|
41
|
+
"./warm": {
|
|
42
|
+
"types": "./dist/warm.d.ts",
|
|
43
|
+
"default": "./dist/warm.js"
|
|
44
|
+
},
|
|
45
|
+
"./video.css": "./dist/video.css",
|
|
46
|
+
"./image.css": "./dist/image.css",
|
|
47
|
+
"./layer.css": "./dist/layer.css",
|
|
48
|
+
"./package.json": "./package.json"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"dist",
|
|
52
|
+
"src",
|
|
53
|
+
"CHANGELOG.md"
|
|
54
|
+
],
|
|
55
|
+
"devEngines": {
|
|
56
|
+
"runtime": {
|
|
57
|
+
"name": "node",
|
|
58
|
+
"version": ">=24",
|
|
59
|
+
"onFail": "error"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "node scripts/clean.js && tsc -p tsconfig.build.json && esbuild src/video.css src/image.css src/layer.css --minify --outdir=dist",
|
|
64
|
+
"clean": "node scripts/clean.js",
|
|
65
|
+
"fixtures": "sh scripts/make-fixtures.sh",
|
|
66
|
+
"format": "prettier --write .",
|
|
67
|
+
"format:check": "prettier --check .",
|
|
68
|
+
"lint": "eslint .",
|
|
69
|
+
"lint:fix": "eslint . --fix",
|
|
70
|
+
"prepack": "pnpm run build",
|
|
71
|
+
"prepare": "lefthook install",
|
|
72
|
+
"size": "node scripts/size-check.js",
|
|
73
|
+
"test": "vitest run",
|
|
74
|
+
"test:e2e": "pnpm run build && playwright test",
|
|
75
|
+
"test:watch": "vitest",
|
|
76
|
+
"typecheck": "tsc --noEmit",
|
|
77
|
+
"verify": "pnpm lint && pnpm format:check && pnpm typecheck && pnpm test && pnpm build && pnpm size"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@axe-core/playwright": "^4.13.0",
|
|
81
|
+
"@commitlint/cli": "^21.2.2",
|
|
82
|
+
"@commitlint/config-conventional": "^21.2.2",
|
|
83
|
+
"@eslint/js": "^10.0.1",
|
|
84
|
+
"@playwright/test": "^1.62.1",
|
|
85
|
+
"@types/node": "^26.4.0",
|
|
86
|
+
"esbuild": "^0.28.2",
|
|
87
|
+
"eslint": "^10.9.1",
|
|
88
|
+
"globals": "^17.11.0",
|
|
89
|
+
"happy-dom": "^20.12.0",
|
|
90
|
+
"lefthook": "^2.1.12",
|
|
91
|
+
"prettier": "^3.9.6",
|
|
92
|
+
"typescript": "^6.0.3",
|
|
93
|
+
"typescript-eslint": "^8.68.0",
|
|
94
|
+
"vitest": "^4.1.11"
|
|
95
|
+
}
|
|
96
|
+
}
|