@vielzeug/sentinel 3.0.0 → 26.10.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 +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/element-size.cjs +1 -1
- package/dist/element-size.js +1 -1
- package/dist/fullscreen.cjs +2 -0
- package/dist/fullscreen.cjs.map +1 -0
- package/dist/fullscreen.d.ts +15 -0
- package/dist/fullscreen.d.ts.map +1 -0
- package/dist/fullscreen.js +2 -0
- package/dist/fullscreen.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/intersection.cjs +1 -1
- package/dist/intersection.js +1 -1
- package/dist/media-query.cjs +1 -1
- package/dist/media-query.js +1 -1
- package/dist/network.cjs +1 -1
- package/dist/network.js +1 -1
- package/dist/sentinel.cjs +1 -1
- package/dist/sentinel.cjs.map +1 -1
- package/dist/sentinel.iife.js +1 -1
- package/dist/sentinel.iife.js.map +1 -1
- package/dist/sentinel.js +1 -1
- package/dist/sentinel.js.map +1 -1
- package/dist/types.d.ts +54 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/viewport.cjs +1 -1
- package/dist/viewport.js +1 -1
- package/dist/wake-lock.cjs +2 -0
- package/dist/wake-lock.cjs.map +1 -0
- package/dist/wake-lock.d.ts +9 -0
- package/dist/wake-lock.d.ts.map +1 -0
- package/dist/wake-lock.js +2 -0
- package/dist/wake-lock.js.map +1 -0
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -45,4 +45,4 @@ const stopObserving = observeViewport();
|
|
|
45
45
|
|
|
46
46
|
## License
|
|
47
47
|
|
|
48
|
-
MIT © [Helmuth Saatkamp](https://github.com/helmuthdu)
|
|
48
|
+
MIT © [Helmuth Saatkamp](https://github.com/helmuthdu): part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
|
package/dist/core.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
const e=require("./errors.cjs");let t=require("@vielzeug/arsenal");function n(e,t){return new r(e,t)}var r=class{disposalSignal;abortController=new AbortController;currentValue;listeners=new Set;cleanup;disposedValue=!1;externalSignal;externalAbortListener;tapper=(0,t.tapper)();constructor(t,n){if(t.signal?.aborted)throw new e.SentinelError(`Sentinel signal is already aborted`);this.currentValue=t.initialValue,this.disposalSignal=this.abortController.signal;try{this.cleanup=n(e=>{this.disposedValue||(this.currentValue=e,this.notify())})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}t.signal&&(this.externalSignal=t.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0}))}get disposed(){return this.disposedValue}getSnapshot=()=>this.currentValue;subscribe=e=>this.disposedValue?()=>{}:(this.listeners.add(e),()=>this.listeners.delete(e));dispose=()=>{if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0,this.listeners.clear();try{e?.()}finally{this.tapper.emit({type:`dispose`}),this.tapper.clear(),this.abortController.abort()}};tap=(e,t)=>this.tapper.tap(e,t);notify(){for(let e of[...this.listeners])try{e()}catch(e){this.tapper.emit({error:e,type:`error`}),queueMicrotask(()=>{throw e})}}[Symbol.dispose](){this.dispose()}};exports.createSentinel=n;
|
|
2
2
|
//# sourceMappingURL=core.cjs.map
|
package/dist/core.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.cjs","names":[],"sources":["../src/core.ts"],"sourcesContent":["import type { Sentinel, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n
|
|
1
|
+
{"version":3,"file":"core.cjs","names":[],"sources":["../src/core.ts"],"sourcesContent":["import { type Tapper, tapper } from '@vielzeug/arsenal';\n\nimport { SentinelError } from './errors.ts';\nimport type { Sentinel, SentinelEvent, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n private readonly tapper: Tapper<SentinelEvent> = tapper<SentinelEvent>();\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n if (options.signal?.aborted) {\n throw new SentinelError('Sentinel signal is already aborted');\n }\n\n this.currentValue = options.initialValue;\n this.disposalSignal = this.abortController.signal;\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) {\n this.currentValue = value;\n this.notify();\n }\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n readonly getSnapshot = (): T => this.currentValue;\n\n readonly subscribe = (listener: () => void): Unsubscribe => {\n if (this.disposedValue) return () => {};\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n readonly dispose = (): void => {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n this.listeners.clear();\n\n try {\n cleanup?.();\n } finally {\n this.tapper.emit({ type: 'dispose' });\n this.tapper.clear();\n this.abortController.abort();\n }\n };\n\n readonly tap = (handler: (event: SentinelEvent) => void, options?: { readonly signal?: AbortSignal }): Unsubscribe =>\n this.tapper.tap(handler, options);\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch (error) {\n this.tapper.emit({ error, type: 'error' });\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n"],"mappings":"mEASA,SAAgB,EACd,EACA,EACa,CACb,OAAO,IAAI,EAAe,EAAS,CAAK,CAC1C,CAEA,IAAM,EAAN,KAA+C,CAC7C,eACA,gBAAmC,IAAI,gBACvC,aACA,UAA6B,IAAI,IACjC,QACA,cAAwB,GACxB,eACA,sBACA,QAAA,EAAiD,EAAA,OAAA,CAAsB,EAEvE,YAAY,EAAmC,EAAmD,CAChG,GAAI,EAAQ,QAAQ,QAClB,MAAM,IAAI,EAAA,cAAc,oCAAoC,EAG9D,KAAK,aAAe,EAAQ,aAC5B,KAAK,eAAiB,KAAK,gBAAgB,OAE3C,GAAI,CACF,KAAK,QAAU,EAAO,GAAU,CACzB,KAAK,gBACR,KAAK,aAAe,EACpB,KAAK,OAAO,EAEhB,CAAC,CACH,OAAS,EAAO,CAGd,KAFA,MAAK,cAAgB,GACrB,KAAK,gBAAgB,MAAM,EACrB,CACR,CAEK,EAAQ,SAEb,KAAK,eAAiB,EAAQ,OAC9B,KAAK,0BAA8B,KAAK,QAAQ,EAChD,KAAK,eAAe,iBAAiB,QAAS,KAAK,sBAAuB,CAAE,KAAM,EAAK,CAAC,EAC1F,CAEA,IAAI,UAAoB,CACtB,OAAO,KAAK,aACd,CAEA,gBAAgC,KAAK,aAErC,UAAsB,GAChB,KAAK,kBAA4B,CAAC,GACtC,KAAK,UAAU,IAAI,CAAQ,MACd,KAAK,UAAU,OAAO,CAAQ,GAG7C,YAA+B,CAC7B,GAAI,KAAK,cAAe,OACxB,KAAK,cAAgB,GAEjB,KAAK,gBAAkB,KAAK,uBAC9B,KAAK,eAAe,oBAAoB,QAAS,KAAK,qBAAqB,EAG7E,IAAM,EAAU,KAAK,QACrB,KAAK,QAAU,IAAA,GACf,KAAK,eAAiB,IAAA,GACtB,KAAK,sBAAwB,IAAA,GAC7B,KAAK,UAAU,MAAM,EAErB,GAAI,CACF,IAAU,CACZ,QAAU,CACR,KAAK,OAAO,KAAK,CAAE,KAAM,SAAU,CAAC,EACpC,KAAK,OAAO,MAAM,EAClB,KAAK,gBAAgB,MAAM,CAC7B,CACF,EAEA,KAAgB,EAAyC,IACvD,KAAK,OAAO,IAAI,EAAS,CAAO,EAElC,QAAuB,CACrB,IAAK,IAAM,IAAY,CAAC,GAAG,KAAK,SAAS,EACvC,GAAI,CACF,EAAS,CACX,OAAS,EAAO,CACd,KAAK,OAAO,KAAK,CAAE,QAAO,KAAM,OAAQ,CAAC,EACzC,mBAAqB,CACnB,MAAM,CACR,CAAC,CACH,CAEJ,CAEA,CAAC,OAAO,UAAiB,CACvB,KAAK,QAAQ,CACf,CACF"}
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAiB,eAAe,EAAe,MAAM,YAAY,CAAC;AAExF,MAAM,WAAW,qBAAqB,CAAC,CAAC,CAAE,SAAQ,eAAe;IAC/D,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,qBAAqB,CAAC,CAAC,CAAC,EACjC,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,GAChD,QAAQ,CAAC,CAAC,CAAC,CAEb"}
|
package/dist/core.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function
|
|
1
|
+
import{SentinelError as e}from"./errors.js";import{tapper as t}from"@vielzeug/arsenal";function n(e,t){return new r(e,t)}var r=class{disposalSignal;abortController=new AbortController;currentValue;listeners=new Set;cleanup;disposedValue=!1;externalSignal;externalAbortListener;tapper=t();constructor(t,n){if(t.signal?.aborted)throw new e(`Sentinel signal is already aborted`);this.currentValue=t.initialValue,this.disposalSignal=this.abortController.signal;try{this.cleanup=n(e=>{this.disposedValue||(this.currentValue=e,this.notify())})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}t.signal&&(this.externalSignal=t.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0}))}get disposed(){return this.disposedValue}getSnapshot=()=>this.currentValue;subscribe=e=>this.disposedValue?()=>{}:(this.listeners.add(e),()=>this.listeners.delete(e));dispose=()=>{if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0,this.listeners.clear();try{e?.()}finally{this.tapper.emit({type:`dispose`}),this.tapper.clear(),this.abortController.abort()}};tap=(e,t)=>this.tapper.tap(e,t);notify(){for(let e of[...this.listeners])try{e()}catch(e){this.tapper.emit({error:e,type:`error`}),queueMicrotask(()=>{throw e})}}[Symbol.dispose](){this.dispose()}};export{n as createSentinel};
|
|
2
2
|
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","names":[],"sources":["../src/core.ts"],"sourcesContent":["import type { Sentinel, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n
|
|
1
|
+
{"version":3,"file":"core.js","names":[],"sources":["../src/core.ts"],"sourcesContent":["import { type Tapper, tapper } from '@vielzeug/arsenal';\n\nimport { SentinelError } from './errors.ts';\nimport type { Sentinel, SentinelEvent, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n private readonly tapper: Tapper<SentinelEvent> = tapper<SentinelEvent>();\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n if (options.signal?.aborted) {\n throw new SentinelError('Sentinel signal is already aborted');\n }\n\n this.currentValue = options.initialValue;\n this.disposalSignal = this.abortController.signal;\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) {\n this.currentValue = value;\n this.notify();\n }\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n readonly getSnapshot = (): T => this.currentValue;\n\n readonly subscribe = (listener: () => void): Unsubscribe => {\n if (this.disposedValue) return () => {};\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n readonly dispose = (): void => {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n this.listeners.clear();\n\n try {\n cleanup?.();\n } finally {\n this.tapper.emit({ type: 'dispose' });\n this.tapper.clear();\n this.abortController.abort();\n }\n };\n\n readonly tap = (handler: (event: SentinelEvent) => void, options?: { readonly signal?: AbortSignal }): Unsubscribe =>\n this.tapper.tap(handler, options);\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch (error) {\n this.tapper.emit({ error, type: 'error' });\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n"],"mappings":"uFASA,SAAgB,EACd,EACA,EACa,CACb,OAAO,IAAI,EAAe,EAAS,CAAK,CAC1C,CAEA,IAAM,EAAN,KAA+C,CAC7C,eACA,gBAAmC,IAAI,gBACvC,aACA,UAA6B,IAAI,IACjC,QACA,cAAwB,GACxB,eACA,sBACA,OAAiD,EAAsB,EAEvE,YAAY,EAAmC,EAAmD,CAChG,GAAI,EAAQ,QAAQ,QAClB,MAAM,IAAI,EAAc,oCAAoC,EAG9D,KAAK,aAAe,EAAQ,aAC5B,KAAK,eAAiB,KAAK,gBAAgB,OAE3C,GAAI,CACF,KAAK,QAAU,EAAO,GAAU,CACzB,KAAK,gBACR,KAAK,aAAe,EACpB,KAAK,OAAO,EAEhB,CAAC,CACH,OAAS,EAAO,CAGd,KAFA,MAAK,cAAgB,GACrB,KAAK,gBAAgB,MAAM,EACrB,CACR,CAEK,EAAQ,SAEb,KAAK,eAAiB,EAAQ,OAC9B,KAAK,0BAA8B,KAAK,QAAQ,EAChD,KAAK,eAAe,iBAAiB,QAAS,KAAK,sBAAuB,CAAE,KAAM,EAAK,CAAC,EAC1F,CAEA,IAAI,UAAoB,CACtB,OAAO,KAAK,aACd,CAEA,gBAAgC,KAAK,aAErC,UAAsB,GAChB,KAAK,kBAA4B,CAAC,GACtC,KAAK,UAAU,IAAI,CAAQ,MACd,KAAK,UAAU,OAAO,CAAQ,GAG7C,YAA+B,CAC7B,GAAI,KAAK,cAAe,OACxB,KAAK,cAAgB,GAEjB,KAAK,gBAAkB,KAAK,uBAC9B,KAAK,eAAe,oBAAoB,QAAS,KAAK,qBAAqB,EAG7E,IAAM,EAAU,KAAK,QACrB,KAAK,QAAU,IAAA,GACf,KAAK,eAAiB,IAAA,GACtB,KAAK,sBAAwB,IAAA,GAC7B,KAAK,UAAU,MAAM,EAErB,GAAI,CACF,IAAU,CACZ,QAAU,CACR,KAAK,OAAO,KAAK,CAAE,KAAM,SAAU,CAAC,EACpC,KAAK,OAAO,MAAM,EAClB,KAAK,gBAAgB,MAAM,CAC7B,CACF,EAEA,KAAgB,EAAyC,IACvD,KAAK,OAAO,IAAI,EAAS,CAAO,EAElC,QAAuB,CACrB,IAAK,IAAM,IAAY,CAAC,GAAG,KAAK,SAAS,EACvC,GAAI,CACF,EAAS,CACX,OAAS,EAAO,CACd,KAAK,OAAO,KAAK,CAAE,QAAO,KAAM,OAAQ,CAAC,EACzC,mBAAqB,CACnB,MAAM,CACR,CAAC,CACH,CAEJ,CAEA,CAAC,OAAO,UAAiB,CACvB,KAAK,QAAQ,CACf,CACF"}
|
package/dist/element-size.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs"),t=require("./
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./core.cjs"),n=require("./_platform.cjs");function r(r,i){let a=n.resolveElementWindow(r).ResizeObserver;if(typeof a!=`function`)throw new e.SentinelUnavailableError(`ResizeObserver is not available in this environment.`);return t.createSentinel({initialValue:null,...i},e=>{let t=new a(t=>{let n=t.at(-1);n&&e({height:n.contentRect.height,width:n.contentRect.width})});try{t.observe(r,{box:`content-box`})}catch(e){throw t.disconnect(),e}return()=>t.disconnect()})}exports.createElementSize=r;
|
|
2
2
|
//# sourceMappingURL=element-size.cjs.map
|
package/dist/element-size.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{SentinelUnavailableError as e}from"./errors.js";import{
|
|
1
|
+
import{SentinelUnavailableError as e}from"./errors.js";import{createSentinel as t}from"./core.js";import{resolveElementWindow as n}from"./_platform.js";function r(r,i){let a=n(r).ResizeObserver;if(typeof a!=`function`)throw new e(`ResizeObserver is not available in this environment.`);return t({initialValue:null,...i},e=>{let t=new a(t=>{let n=t.at(-1);n&&e({height:n.contentRect.height,width:n.contentRect.width})});try{t.observe(r,{box:`content-box`})}catch(e){throw t.disconnect(),e}return()=>t.disconnect()})}export{r as createElementSize};
|
|
2
2
|
//# sourceMappingURL=element-size.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./core.cjs"),t=require("./_platform.cjs");function n(n){let r=t.resolveWindow(n?.target).document,i=typeof r.documentElement?.requestFullscreen==`function`,a=()=>({active:r.fullscreenElement!==null,supported:i}),o=()=>{},s=e=>{!r.fullscreenElement&&i&&(e??r.documentElement).requestFullscreen().catch(()=>{})},c=()=>{r.fullscreenElement&&r.exitFullscreen().catch(()=>{})};return{...e.createSentinel({initialValue:a(),...n},e=>{o=e;let t=()=>o(a());return r.addEventListener(`fullscreenchange`,t),()=>{r.removeEventListener(`fullscreenchange`,t),r.fullscreenElement&&c()}}),exit:c,request:s,toggle(e){r.fullscreenElement?c():s(e)}}}exports.createFullscreen=n;
|
|
2
|
+
//# sourceMappingURL=fullscreen.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fullscreen.cjs","names":[],"sources":["../src/fullscreen.ts"],"sourcesContent":["import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { FullscreenSentinel, FullscreenState, SentinelOptions } from './types.ts';\n\ntype FullscreenWindow = Window & { document: Document };\n\n/**\n * A sentinel-backed fullscreen view. `request()` enters fullscreen for an element (default:\n * the document element), `exit()` leaves it, and `toggle()` flips it: all degrade silently\n * when the Fullscreen API is missing or the browser refuses (user-activation requirements).\n * Disposing the sentinel exits fullscreen and stops tracking `fullscreenchange`.\n */\nexport function createFullscreen(\n options?: SentinelOptions & { readonly target?: FullscreenWindow },\n): FullscreenSentinel {\n const target = resolveWindow(options?.target);\n const doc = target.document;\n const supported = typeof doc.documentElement?.requestFullscreen === 'function';\n\n const read = (): FullscreenState => ({ active: doc.fullscreenElement !== null, supported });\n let updateState: (value: FullscreenState) => void = () => {};\n\n const request = (element?: Element): void => {\n if (doc.fullscreenElement || !supported) return;\n void (element ?? doc.documentElement).requestFullscreen().catch(() => {\n // Requires user activation in some browsers, or the element may refuse; degrade silently.\n });\n };\n\n const exit = (): void => {\n if (!doc.fullscreenElement) return;\n void doc.exitFullscreen().catch(() => {\n // Degrade silently.\n });\n };\n\n const sentinel = createSentinel<FullscreenState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onFullscreenChange = (): void => updateState(read());\n\n doc.addEventListener('fullscreenchange', onFullscreenChange);\n\n return () => {\n doc.removeEventListener('fullscreenchange', onFullscreenChange);\n if (doc.fullscreenElement) exit();\n };\n });\n\n return {\n ...sentinel,\n exit,\n request,\n toggle(element?: Element): void {\n if (doc.fullscreenElement) exit();\n else request(element);\n },\n };\n}\n"],"mappings":"2DAYA,SAAgB,EACd,EACoB,CAEpB,IAAM,EADS,EAAA,cAAc,GAAS,MAC1B,CAAA,CAAO,SACb,EAAY,OAAO,EAAI,iBAAiB,mBAAsB,WAE9D,OAA+B,CAAE,OAAQ,EAAI,oBAAsB,KAAM,WAAU,GACrF,MAAsD,CAAC,EAErD,EAAW,GAA4B,CACvC,GAAI,mBAAsB,IACxB,GAAW,EAAI,gBAAA,CAAiB,kBAAkB,CAAC,CAAC,UAAY,CAEtE,CAAC,CACH,EAEM,MAAmB,CAClB,EAAI,mBACT,EAAS,eAAe,CAAC,CAAC,UAAY,CAEtC,CAAC,CACH,EAeA,MAAO,CACL,GAde,EAAA,eAAgC,CAAE,aAAc,EAAK,EAAG,GAAG,CAAQ,EAAI,GAAW,CACjG,EAAc,EAEd,IAAM,MAAiC,EAAY,EAAK,CAAC,EAIzD,OAFA,EAAI,iBAAiB,mBAAoB,CAAkB,MAE9C,CACX,EAAI,oBAAoB,mBAAoB,CAAkB,EAC1D,EAAI,mBAAmB,EAAK,CAClC,CACF,CAGK,EACH,OACA,UACA,OAAO,EAAyB,CAC1B,EAAI,kBAAmB,EAAK,EAC3B,EAAQ,CAAO,CACtB,CACF,CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FullscreenSentinel, SentinelOptions } from './types.ts';
|
|
2
|
+
type FullscreenWindow = Window & {
|
|
3
|
+
document: Document;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* A sentinel-backed fullscreen view. `request()` enters fullscreen for an element (default:
|
|
7
|
+
* the document element), `exit()` leaves it, and `toggle()` flips it: all degrade silently
|
|
8
|
+
* when the Fullscreen API is missing or the browser refuses (user-activation requirements).
|
|
9
|
+
* Disposing the sentinel exits fullscreen and stops tracking `fullscreenchange`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createFullscreen(options?: SentinelOptions & {
|
|
12
|
+
readonly target?: FullscreenWindow;
|
|
13
|
+
}): FullscreenSentinel;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=fullscreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fullscreen.d.ts","sourceRoot":"","sources":["../src/fullscreen.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAmB,eAAe,EAAE,MAAM,YAAY,CAAC;AAEvF,KAAK,gBAAgB,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC;AAExD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAA;CAAE,GACjE,kBAAkB,CA4CpB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createSentinel as e}from"./core.js";import{resolveWindow as t}from"./_platform.js";function n(n){let r=t(n?.target).document,i=typeof r.documentElement?.requestFullscreen==`function`,a=()=>({active:r.fullscreenElement!==null,supported:i}),o=()=>{},s=e=>{!r.fullscreenElement&&i&&(e??r.documentElement).requestFullscreen().catch(()=>{})},c=()=>{r.fullscreenElement&&r.exitFullscreen().catch(()=>{})};return{...e({initialValue:a(),...n},e=>{o=e;let t=()=>o(a());return r.addEventListener(`fullscreenchange`,t),()=>{r.removeEventListener(`fullscreenchange`,t),r.fullscreenElement&&c()}}),exit:c,request:s,toggle(e){r.fullscreenElement?c():s(e)}}}export{n as createFullscreen};
|
|
2
|
+
//# sourceMappingURL=fullscreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fullscreen.js","names":[],"sources":["../src/fullscreen.ts"],"sourcesContent":["import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { FullscreenSentinel, FullscreenState, SentinelOptions } from './types.ts';\n\ntype FullscreenWindow = Window & { document: Document };\n\n/**\n * A sentinel-backed fullscreen view. `request()` enters fullscreen for an element (default:\n * the document element), `exit()` leaves it, and `toggle()` flips it: all degrade silently\n * when the Fullscreen API is missing or the browser refuses (user-activation requirements).\n * Disposing the sentinel exits fullscreen and stops tracking `fullscreenchange`.\n */\nexport function createFullscreen(\n options?: SentinelOptions & { readonly target?: FullscreenWindow },\n): FullscreenSentinel {\n const target = resolveWindow(options?.target);\n const doc = target.document;\n const supported = typeof doc.documentElement?.requestFullscreen === 'function';\n\n const read = (): FullscreenState => ({ active: doc.fullscreenElement !== null, supported });\n let updateState: (value: FullscreenState) => void = () => {};\n\n const request = (element?: Element): void => {\n if (doc.fullscreenElement || !supported) return;\n void (element ?? doc.documentElement).requestFullscreen().catch(() => {\n // Requires user activation in some browsers, or the element may refuse; degrade silently.\n });\n };\n\n const exit = (): void => {\n if (!doc.fullscreenElement) return;\n void doc.exitFullscreen().catch(() => {\n // Degrade silently.\n });\n };\n\n const sentinel = createSentinel<FullscreenState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onFullscreenChange = (): void => updateState(read());\n\n doc.addEventListener('fullscreenchange', onFullscreenChange);\n\n return () => {\n doc.removeEventListener('fullscreenchange', onFullscreenChange);\n if (doc.fullscreenElement) exit();\n };\n });\n\n return {\n ...sentinel,\n exit,\n request,\n toggle(element?: Element): void {\n if (doc.fullscreenElement) exit();\n else request(element);\n },\n };\n}\n"],"mappings":"0FAYA,SAAgB,EACd,EACoB,CAEpB,IAAM,EADS,EAAc,GAAS,MAC1B,CAAA,CAAO,SACb,EAAY,OAAO,EAAI,iBAAiB,mBAAsB,WAE9D,OAA+B,CAAE,OAAQ,EAAI,oBAAsB,KAAM,WAAU,GACrF,MAAsD,CAAC,EAErD,EAAW,GAA4B,CACvC,GAAI,mBAAsB,IACxB,GAAW,EAAI,gBAAA,CAAiB,kBAAkB,CAAC,CAAC,UAAY,CAEtE,CAAC,CACH,EAEM,MAAmB,CAClB,EAAI,mBACT,EAAS,eAAe,CAAC,CAAC,UAAY,CAEtC,CAAC,CACH,EAeA,MAAO,CACL,GAde,EAAgC,CAAE,aAAc,EAAK,EAAG,GAAG,CAAQ,EAAI,GAAW,CACjG,EAAc,EAEd,IAAM,MAAiC,EAAY,EAAK,CAAC,EAIzD,OAFA,EAAI,iBAAiB,mBAAoB,CAAkB,MAE9C,CACX,EAAI,oBAAoB,mBAAoB,CAAkB,EAC1D,EAAI,mBAAmB,EAAK,CAClC,CACF,CAGK,EACH,OACA,UACA,OAAO,EAAyB,CAC1B,EAAI,kBAAmB,EAAK,EAC3B,EAAQ,CAAO,CACtB,CACF,CACF"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./element-size.cjs"),
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./core.cjs"),n=require("./element-size.cjs"),r=require("./fullscreen.cjs"),i=require("./intersection.cjs"),a=require("./media-query.cjs"),o=require("./network.cjs"),s=require("./viewport.cjs"),c=require("./wake-lock.cjs");exports.SentinelError=e.SentinelError,exports.SentinelUnavailableError=e.SentinelUnavailableError,exports.createElementSize=n.createElementSize,exports.createFullscreen=r.createFullscreen,exports.createIntersection=i.createIntersection,exports.createMediaQuery=a.createMediaQuery,exports.createNetwork=o.createNetwork,exports.createSentinel=t.createSentinel,exports.createViewport=s.createViewport,exports.createWakeLock=c.createWakeLock;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
export type { CreateSentinelOptions } from './core.ts';
|
|
2
|
+
export { createSentinel } from './core.ts';
|
|
1
3
|
export { createElementSize } from './element-size.ts';
|
|
2
4
|
export { SentinelError, SentinelUnavailableError } from './errors.ts';
|
|
5
|
+
export { createFullscreen } from './fullscreen.ts';
|
|
3
6
|
export type { CreateIntersectionOptions } from './intersection.ts';
|
|
4
7
|
export { createIntersection } from './intersection.ts';
|
|
5
8
|
export { createMediaQuery } from './media-query.ts';
|
|
6
9
|
export { createNetwork } from './network.ts';
|
|
7
|
-
export type { ElementSizeState, IntersectionState, MediaQueryState, NetworkConnectionSnapshot, NetworkState, Sentinel, SentinelOptions, ViewportState, WindowSentinelOptions, } from './types.ts';
|
|
10
|
+
export type { ElementSizeState, FullscreenSentinel, FullscreenState, IntersectionState, MediaQueryState, NetworkConnectionSnapshot, NetworkState, Sentinel, SentinelEvent, SentinelOptions, Subscribable, Unsubscribe, ViewportState, WakeLockSentinel, WakeLockState, WindowSentinelOptions, } from './types.ts';
|
|
8
11
|
export { createViewport } from './viewport.ts';
|
|
12
|
+
export { createWakeLock } from './wake-lock.ts';
|
|
9
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACtE,YAAY,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,yBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,aAAa,EACb,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,yBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,eAAe,EACf,YAAY,EACZ,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SentinelError as e,SentinelUnavailableError as t}from"./errors.js";import{
|
|
1
|
+
import{SentinelError as e,SentinelUnavailableError as t}from"./errors.js";import{createSentinel as n}from"./core.js";import{createElementSize as r}from"./element-size.js";import{createFullscreen as i}from"./fullscreen.js";import{createIntersection as a}from"./intersection.js";import{createMediaQuery as o}from"./media-query.js";import{createNetwork as s}from"./network.js";import{createViewport as c}from"./viewport.js";import{createWakeLock as l}from"./wake-lock.js";export{e as SentinelError,t as SentinelUnavailableError,r as createElementSize,i as createFullscreen,a as createIntersection,o as createMediaQuery,s as createNetwork,n as createSentinel,c as createViewport,l as createWakeLock};
|
package/dist/intersection.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs"),t=require("./
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./core.cjs"),n=require("./_platform.cjs");function r(r,i){let a=n.resolveElementWindow(r).IntersectionObserver;if(typeof a!=`function`)throw new e.SentinelUnavailableError(`IntersectionObserver is not available in this environment.`);return t.createSentinel({initialValue:null,...i},e=>{let t=new a(t=>{let n=t.at(-1);n&&e({intersectionRatio:n.intersectionRatio,isIntersecting:n.isIntersecting})},{root:i?.root,rootMargin:i?.rootMargin,scrollMargin:i?.scrollMargin,threshold:i?.threshold});try{t.observe(r)}catch(e){throw t.disconnect(),e}return()=>t.disconnect()})}exports.createIntersection=r;
|
|
2
2
|
//# sourceMappingURL=intersection.cjs.map
|
package/dist/intersection.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{SentinelUnavailableError as e}from"./errors.js";import{
|
|
1
|
+
import{SentinelUnavailableError as e}from"./errors.js";import{createSentinel as t}from"./core.js";import{resolveElementWindow as n}from"./_platform.js";function r(r,i){let a=n(r).IntersectionObserver;if(typeof a!=`function`)throw new e(`IntersectionObserver is not available in this environment.`);return t({initialValue:null,...i},e=>{let t=new a(t=>{let n=t.at(-1);n&&e({intersectionRatio:n.intersectionRatio,isIntersecting:n.isIntersecting})},{root:i?.root,rootMargin:i?.rootMargin,scrollMargin:i?.scrollMargin,threshold:i?.threshold});try{t.observe(r)}catch(e){throw t.disconnect(),e}return()=>t.disconnect()})}export{r as createIntersection};
|
|
2
2
|
//# sourceMappingURL=intersection.js.map
|
package/dist/media-query.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs"),t=require("./
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./core.cjs"),n=require("./_platform.cjs");function r(r,i){let a=n.resolveWindow(i?.target);if(typeof a.matchMedia!=`function`)throw new e.SentinelUnavailableError(`matchMedia is not available for query "${r}".`);let o=a.matchMedia(r),s=()=>({matches:o.matches});return t.createSentinel({initialValue:s(),...i},e=>n.listenMediaQuery(o,()=>e(s())))}exports.createMediaQuery=r;
|
|
2
2
|
//# sourceMappingURL=media-query.cjs.map
|
package/dist/media-query.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{SentinelUnavailableError as e}from"./errors.js";import{
|
|
1
|
+
import{SentinelUnavailableError as e}from"./errors.js";import{createSentinel as t}from"./core.js";import{listenMediaQuery as n,resolveWindow as r}from"./_platform.js";function i(i,a){let o=r(a?.target);if(typeof o.matchMedia!=`function`)throw new e(`matchMedia is not available for query "${i}".`);let s=o.matchMedia(i),c=()=>({matches:s.matches});return t({initialValue:c(),...a},e=>n(s,()=>e(c())))}export{i as createMediaQuery};
|
|
2
2
|
//# sourceMappingURL=media-query.js.map
|
package/dist/network.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./core.cjs"),t=require("./_platform.cjs");function n(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function r(r){let i=t.resolveWindow(r?.target),a=i.navigator,o=()=>({connection:n(a.connection),online:a.onLine});return e.createSentinel({initialValue:o(),...r},e=>{let t=()=>e(o()),n=a.connection;return i.addEventListener(`online`,t),i.addEventListener(`offline`,t),n?.addEventListener(`change`,t),()=>{i.removeEventListener(`online`,t),i.removeEventListener(`offline`,t),n?.removeEventListener(`change`,t)}})}exports.createNetwork=r;
|
|
2
2
|
//# sourceMappingURL=network.cjs.map
|
package/dist/network.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{createSentinel as e}from"./core.js";import{resolveWindow as t}from"./_platform.js";function n(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function r(r){let i=t(r?.target),a=i.navigator,o=()=>({connection:n(a.connection),online:a.onLine});return e({initialValue:o(),...r},e=>{let t=()=>e(o()),n=a.connection;return i.addEventListener(`online`,t),i.addEventListener(`offline`,t),n?.addEventListener(`change`,t),()=>{i.removeEventListener(`online`,t),i.removeEventListener(`offline`,t),n?.removeEventListener(`change`,t)}})}export{r as createNetwork};
|
|
2
2
|
//# sourceMappingURL=network.js.map
|
package/dist/sentinel.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/arsenal");var SentinelError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},SentinelUnavailableError=class extends SentinelError{};function createSentinel(e,t){return new SentinelHandle(e,t)}var SentinelHandle=class{disposalSignal;abortController=new AbortController;currentValue;listeners=new Set;cleanup;disposedValue=!1;externalSignal;externalAbortListener;tapper=(0,e.tapper)();constructor(e,t){if(e.signal?.aborted)throw new SentinelError(`Sentinel signal is already aborted`);this.currentValue=e.initialValue,this.disposalSignal=this.abortController.signal;try{this.cleanup=t(e=>{this.disposedValue||(this.currentValue=e,this.notify())})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}e.signal&&(this.externalSignal=e.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0}))}get disposed(){return this.disposedValue}getSnapshot=()=>this.currentValue;subscribe=e=>this.disposedValue?()=>{}:(this.listeners.add(e),()=>this.listeners.delete(e));dispose=()=>{if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0,this.listeners.clear();try{e?.()}finally{this.tapper.emit({type:`dispose`}),this.tapper.clear(),this.abortController.abort()}};tap=(e,t)=>this.tapper.tap(e,t);notify(){for(let e of[...this.listeners])try{e()}catch(e){this.tapper.emit({error:e,type:`error`}),queueMicrotask(()=>{throw e})}}[Symbol.dispose](){this.dispose()}};function resolveWindow(e){if(e)return e;if(typeof window>`u`)throw new SentinelUnavailableError(`Window is not available in this environment.`);return window}function resolveElementWindow(e){let t=e.ownerDocument.defaultView;if(!t)throw new SentinelUnavailableError(`The element is not connected to a Window.`);return t}function listenMediaQuery(e,t){let onChange=()=>t();if(typeof e.addEventListener==`function`)return e.addEventListener(`change`,onChange),()=>e.removeEventListener(`change`,onChange);let n=e;return typeof n.addListener==`function`?(n.addListener(onChange),()=>n.removeListener?.(onChange)):()=>{}}function createElementSize(e,t){let n=resolveElementWindow(e).ResizeObserver;if(typeof n!=`function`)throw new SentinelUnavailableError(`ResizeObserver is not available in this environment.`);return createSentinel({initialValue:null,...t},t=>{let r=new n(e=>{let n=e.at(-1);n&&t({height:n.contentRect.height,width:n.contentRect.width})});try{r.observe(e,{box:`content-box`})}catch(e){throw r.disconnect(),e}return()=>r.disconnect()})}function createFullscreen(e){let t=resolveWindow(e?.target).document,n=typeof t.documentElement?.requestFullscreen==`function`,read=()=>({active:t.fullscreenElement!==null,supported:n}),updateState=()=>{},request=e=>{!t.fullscreenElement&&n&&(e??t.documentElement).requestFullscreen().catch(()=>{})},exit=()=>{t.fullscreenElement&&t.exitFullscreen().catch(()=>{})};return{...createSentinel({initialValue:read(),...e},e=>{updateState=e;let onFullscreenChange=()=>updateState(read());return t.addEventListener(`fullscreenchange`,onFullscreenChange),()=>{t.removeEventListener(`fullscreenchange`,onFullscreenChange),t.fullscreenElement&&exit()}}),exit,request,toggle(e){t.fullscreenElement?exit():request(e)}}}function createIntersection(e,t){let n=resolveElementWindow(e).IntersectionObserver;if(typeof n!=`function`)throw new SentinelUnavailableError(`IntersectionObserver is not available in this environment.`);return createSentinel({initialValue:null,...t},r=>{let i=new n(e=>{let t=e.at(-1);t&&r({intersectionRatio:t.intersectionRatio,isIntersecting:t.isIntersecting})},{root:t?.root,rootMargin:t?.rootMargin,scrollMargin:t?.scrollMargin,threshold:t?.threshold});try{i.observe(e)}catch(e){throw i.disconnect(),e}return()=>i.disconnect()})}function createMediaQuery(e,t){let n=resolveWindow(t?.target);if(typeof n.matchMedia!=`function`)throw new SentinelUnavailableError(`matchMedia is not available for query "${e}".`);let r=n.matchMedia(e),read=()=>({matches:r.matches});return createSentinel({initialValue:read(),...t},e=>listenMediaQuery(r,()=>e(read())))}function normalizeConnection(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function createNetwork(e){let t=resolveWindow(e?.target),n=t.navigator,read=()=>({connection:normalizeConnection(n.connection),online:n.onLine});return createSentinel({initialValue:read(),...e},e=>{let onChange=()=>e(read()),r=n.connection;return t.addEventListener(`online`,onChange),t.addEventListener(`offline`,onChange),r?.addEventListener(`change`,onChange),()=>{t.removeEventListener(`online`,onChange),t.removeEventListener(`offline`,onChange),r?.removeEventListener(`change`,onChange)}})}function createViewport(e){let t=resolveWindow(e?.target),read=()=>({dpr:t.devicePixelRatio,height:t.innerHeight,width:t.innerWidth});return createSentinel({initialValue:read(),...e},e=>{let onResize=()=>e(read()),n,observeDpr=()=>{n?.(),n=void 0,typeof t.matchMedia==`function`&&(n=listenMediaQuery(t.matchMedia(`(resolution: ${t.devicePixelRatio}dppx)`),()=>{e(read()),observeDpr()}))};return t.addEventListener(`resize`,onResize),observeDpr(),()=>{t.removeEventListener(`resize`,onResize),n?.()}})}function createWakeLock(e){let t=resolveWindow(e?.target),n=t.navigator,r=typeof n.wakeLock?.request==`function`,i=null,a=!1,o=!1,read=()=>({active:i!==null,supported:r}),updateState=()=>{};async function acquire(){if(r&&n.wakeLock){a=!0;try{let e=await n.wakeLock.request(`screen`);if(!o){e.release();return}i=e,i.addEventListener(`release`,()=>{i=null,updateState(read())}),updateState(read())}catch{}finally{a=!1}}}return{...createSentinel({initialValue:read(),...e},e=>{updateState=e;let onVisibilityChange=()=>{t.document.visibilityState===`visible`&&o&&!i&&!a&&acquire()};return t.document.addEventListener(`visibilitychange`,onVisibilityChange),()=>{t.document.removeEventListener(`visibilitychange`,onVisibilityChange),i&&i.release(),i=null,o=!1}}),release(){o=!1,i&&(i.release(),i=null,updateState(read()))},request(){i||a||!r||(o=!0,acquire())}}}exports.SentinelError=SentinelError,exports.SentinelUnavailableError=SentinelUnavailableError,exports.createElementSize=createElementSize,exports.createFullscreen=createFullscreen,exports.createIntersection=createIntersection,exports.createMediaQuery=createMediaQuery,exports.createNetwork=createNetwork,exports.createSentinel=createSentinel,exports.createViewport=createViewport,exports.createWakeLock=createWakeLock;
|
|
2
2
|
//# sourceMappingURL=sentinel.cjs.map
|
package/dist/sentinel.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentinel.cjs","names":[],"sources":["../src/errors.ts","../src/_platform.ts","../src/core.ts","../src/element-size.ts","../src/intersection.ts","../src/media-query.ts","../src/network.ts","../src/viewport.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n","import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n","import type { Sentinel, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n this.currentValue = options.initialValue;\n this.disposalSignal = this.abortController.signal;\n\n if (options.signal?.aborted) {\n this.disposedValue = true;\n this.abortController.abort();\n return;\n }\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) {\n this.currentValue = value;\n this.notify();\n }\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n readonly getSnapshot = (): T => this.currentValue;\n\n readonly subscribe = (listener: () => void): Unsubscribe => {\n if (this.disposedValue) return () => {};\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n readonly dispose = (): void => {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n this.listeners.clear();\n\n try {\n cleanup?.();\n } finally {\n this.abortController.abort();\n }\n };\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sentinel.cjs","names":[],"sources":["../src/errors.ts","../src/core.ts","../src/_platform.ts","../src/element-size.ts","../src/fullscreen.ts","../src/intersection.ts","../src/media-query.ts","../src/network.ts","../src/viewport.ts","../src/wake-lock.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n","import { type Tapper, tapper } from '@vielzeug/arsenal';\n\nimport { SentinelError } from './errors.ts';\nimport type { Sentinel, SentinelEvent, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n private readonly tapper: Tapper<SentinelEvent> = tapper<SentinelEvent>();\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n if (options.signal?.aborted) {\n throw new SentinelError('Sentinel signal is already aborted');\n }\n\n this.currentValue = options.initialValue;\n this.disposalSignal = this.abortController.signal;\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) {\n this.currentValue = value;\n this.notify();\n }\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n readonly getSnapshot = (): T => this.currentValue;\n\n readonly subscribe = (listener: () => void): Unsubscribe => {\n if (this.disposedValue) return () => {};\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n readonly dispose = (): void => {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n this.listeners.clear();\n\n try {\n cleanup?.();\n } finally {\n this.tapper.emit({ type: 'dispose' });\n this.tapper.clear();\n this.abortController.abort();\n }\n };\n\n readonly tap = (handler: (event: SentinelEvent) => void, options?: { readonly signal?: AbortSignal }): Unsubscribe =>\n this.tapper.tap(handler, options);\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch (error) {\n this.tapper.emit({ error, type: 'error' });\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n","import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { FullscreenSentinel, FullscreenState, SentinelOptions } from './types.ts';\n\ntype FullscreenWindow = Window & { document: Document };\n\n/**\n * A sentinel-backed fullscreen view. `request()` enters fullscreen for an element (default:\n * the document element), `exit()` leaves it, and `toggle()` flips it: all degrade silently\n * when the Fullscreen API is missing or the browser refuses (user-activation requirements).\n * Disposing the sentinel exits fullscreen and stops tracking `fullscreenchange`.\n */\nexport function createFullscreen(\n options?: SentinelOptions & { readonly target?: FullscreenWindow },\n): FullscreenSentinel {\n const target = resolveWindow(options?.target);\n const doc = target.document;\n const supported = typeof doc.documentElement?.requestFullscreen === 'function';\n\n const read = (): FullscreenState => ({ active: doc.fullscreenElement !== null, supported });\n let updateState: (value: FullscreenState) => void = () => {};\n\n const request = (element?: Element): void => {\n if (doc.fullscreenElement || !supported) return;\n void (element ?? doc.documentElement).requestFullscreen().catch(() => {\n // Requires user activation in some browsers, or the element may refuse; degrade silently.\n });\n };\n\n const exit = (): void => {\n if (!doc.fullscreenElement) return;\n void doc.exitFullscreen().catch(() => {\n // Degrade silently.\n });\n };\n\n const sentinel = createSentinel<FullscreenState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onFullscreenChange = (): void => updateState(read());\n\n doc.addEventListener('fullscreenchange', onFullscreenChange);\n\n return () => {\n doc.removeEventListener('fullscreenchange', onFullscreenChange);\n if (doc.fullscreenElement) exit();\n };\n });\n\n return {\n ...sentinel,\n exit,\n request,\n toggle(element?: Element): void {\n if (doc.fullscreenElement) exit();\n else request(element);\n },\n };\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { SentinelOptions, WakeLockSentinel, WakeLockState } from './types.ts';\n\ntype WakeLockWindow = Window & { document: Document };\n\nexport function createWakeLock(options?: SentinelOptions & { readonly target?: WakeLockWindow }): WakeLockSentinel {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator;\n const supported = typeof navigator.wakeLock?.request === 'function';\n\n let handle: globalThis.WakeLockSentinel | null = null;\n let pending = false;\n let wantsLock = false;\n\n const read = (): WakeLockState => ({ active: handle !== null, supported });\n let updateState: (value: WakeLockState) => void = () => {};\n\n async function acquire(): Promise<void> {\n if (!supported || !navigator.wakeLock) return;\n pending = true;\n try {\n const requested = await navigator.wakeLock.request('screen');\n // release()/dispose() during the pending request must not leave the lock held.\n if (!wantsLock) {\n void requested.release();\n\n return;\n }\n handle = requested;\n handle.addEventListener('release', () => {\n handle = null;\n updateState(read());\n });\n updateState(read());\n } catch {\n // Not supported, denied, or not in a secure context: degrade silently.\n } finally {\n pending = false;\n }\n }\n\n const sentinel = createSentinel<WakeLockState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onVisibilityChange = (): void => {\n if (target.document.visibilityState === 'visible' && wantsLock && !handle && !pending) {\n void acquire();\n }\n };\n\n target.document.addEventListener('visibilitychange', onVisibilityChange);\n\n return () => {\n target.document.removeEventListener('visibilitychange', onVisibilityChange);\n if (handle) void handle.release();\n handle = null;\n wantsLock = false;\n };\n });\n\n return {\n ...sentinel,\n release(): void {\n wantsLock = false;\n if (handle) {\n void handle.release();\n handle = null;\n updateState(read());\n }\n },\n request(): void {\n if (handle || pending || !supported) return;\n wantsLock = true;\n void acquire();\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/sentinel.iife.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var Sentinel=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var
|
|
1
|
+
var Sentinel=(function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var SentinelError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},SentinelUnavailableError=class extends SentinelError{};function createSentinel(e,t){return new SentinelHandle(e,t)}var SentinelHandle=class{disposalSignal;abortController=new AbortController;currentValue;listeners=new Set;cleanup;disposedValue=!1;externalSignal;externalAbortListener;tapper=(0,t.tapper)();constructor(e,t){if(e.signal?.aborted)throw new SentinelError(`Sentinel signal is already aborted`);this.currentValue=e.initialValue,this.disposalSignal=this.abortController.signal;try{this.cleanup=t(e=>{this.disposedValue||(this.currentValue=e,this.notify())})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}e.signal&&(this.externalSignal=e.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0}))}get disposed(){return this.disposedValue}getSnapshot=()=>this.currentValue;subscribe=e=>this.disposedValue?()=>{}:(this.listeners.add(e),()=>this.listeners.delete(e));dispose=()=>{if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0,this.listeners.clear();try{e?.()}finally{this.tapper.emit({type:`dispose`}),this.tapper.clear(),this.abortController.abort()}};tap=(e,t)=>this.tapper.tap(e,t);notify(){for(let e of[...this.listeners])try{e()}catch(e){this.tapper.emit({error:e,type:`error`}),queueMicrotask(()=>{throw e})}}[Symbol.dispose](){this.dispose()}};function resolveWindow(e){if(e)return e;if(typeof window>`u`)throw new SentinelUnavailableError(`Window is not available in this environment.`);return window}function resolveElementWindow(e){let t=e.ownerDocument.defaultView;if(!t)throw new SentinelUnavailableError(`The element is not connected to a Window.`);return t}function listenMediaQuery(e,t){let onChange=()=>t();if(typeof e.addEventListener==`function`)return e.addEventListener(`change`,onChange),()=>e.removeEventListener(`change`,onChange);let n=e;return typeof n.addListener==`function`?(n.addListener(onChange),()=>n.removeListener?.(onChange)):()=>{}}function createElementSize(e,t){let n=resolveElementWindow(e).ResizeObserver;if(typeof n!=`function`)throw new SentinelUnavailableError(`ResizeObserver is not available in this environment.`);return createSentinel({initialValue:null,...t},t=>{let r=new n(e=>{let n=e.at(-1);n&&t({height:n.contentRect.height,width:n.contentRect.width})});try{r.observe(e,{box:`content-box`})}catch(e){throw r.disconnect(),e}return()=>r.disconnect()})}function createFullscreen(e){let t=resolveWindow(e?.target).document,n=typeof t.documentElement?.requestFullscreen==`function`,read=()=>({active:t.fullscreenElement!==null,supported:n}),updateState=()=>{},request=e=>{!t.fullscreenElement&&n&&(e??t.documentElement).requestFullscreen().catch(()=>{})},exit=()=>{t.fullscreenElement&&t.exitFullscreen().catch(()=>{})};return{...createSentinel({initialValue:read(),...e},e=>{updateState=e;let onFullscreenChange=()=>updateState(read());return t.addEventListener(`fullscreenchange`,onFullscreenChange),()=>{t.removeEventListener(`fullscreenchange`,onFullscreenChange),t.fullscreenElement&&exit()}}),exit,request,toggle(e){t.fullscreenElement?exit():request(e)}}}function createIntersection(e,t){let n=resolveElementWindow(e).IntersectionObserver;if(typeof n!=`function`)throw new SentinelUnavailableError(`IntersectionObserver is not available in this environment.`);return createSentinel({initialValue:null,...t},r=>{let i=new n(e=>{let t=e.at(-1);t&&r({intersectionRatio:t.intersectionRatio,isIntersecting:t.isIntersecting})},{root:t?.root,rootMargin:t?.rootMargin,scrollMargin:t?.scrollMargin,threshold:t?.threshold});try{i.observe(e)}catch(e){throw i.disconnect(),e}return()=>i.disconnect()})}function createMediaQuery(e,t){let n=resolveWindow(t?.target);if(typeof n.matchMedia!=`function`)throw new SentinelUnavailableError(`matchMedia is not available for query "${e}".`);let r=n.matchMedia(e),read=()=>({matches:r.matches});return createSentinel({initialValue:read(),...t},e=>listenMediaQuery(r,()=>e(read())))}function normalizeConnection(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function createNetwork(e){let t=resolveWindow(e?.target),n=t.navigator,read=()=>({connection:normalizeConnection(n.connection),online:n.onLine});return createSentinel({initialValue:read(),...e},e=>{let onChange=()=>e(read()),r=n.connection;return t.addEventListener(`online`,onChange),t.addEventListener(`offline`,onChange),r?.addEventListener(`change`,onChange),()=>{t.removeEventListener(`online`,onChange),t.removeEventListener(`offline`,onChange),r?.removeEventListener(`change`,onChange)}})}function createViewport(e){let t=resolveWindow(e?.target),read=()=>({dpr:t.devicePixelRatio,height:t.innerHeight,width:t.innerWidth});return createSentinel({initialValue:read(),...e},e=>{let onResize=()=>e(read()),n,observeDpr=()=>{n?.(),n=void 0,typeof t.matchMedia==`function`&&(n=listenMediaQuery(t.matchMedia(`(resolution: ${t.devicePixelRatio}dppx)`),()=>{e(read()),observeDpr()}))};return t.addEventListener(`resize`,onResize),observeDpr(),()=>{t.removeEventListener(`resize`,onResize),n?.()}})}function createWakeLock(e){let t=resolveWindow(e?.target),n=t.navigator,r=typeof n.wakeLock?.request==`function`,i=null,a=!1,o=!1,read=()=>({active:i!==null,supported:r}),updateState=()=>{};async function acquire(){if(r&&n.wakeLock){a=!0;try{let e=await n.wakeLock.request(`screen`);if(!o){e.release();return}i=e,i.addEventListener(`release`,()=>{i=null,updateState(read())}),updateState(read())}catch{}finally{a=!1}}}return{...createSentinel({initialValue:read(),...e},e=>{updateState=e;let onVisibilityChange=()=>{t.document.visibilityState===`visible`&&o&&!i&&!a&&acquire()};return t.document.addEventListener(`visibilitychange`,onVisibilityChange),()=>{t.document.removeEventListener(`visibilitychange`,onVisibilityChange),i&&i.release(),i=null,o=!1}}),release(){o=!1,i&&(i.release(),i=null,updateState(read()))},request(){i||a||!r||(o=!0,acquire())}}}return e.SentinelError=SentinelError,e.SentinelUnavailableError=SentinelUnavailableError,e.createElementSize=createElementSize,e.createFullscreen=createFullscreen,e.createIntersection=createIntersection,e.createMediaQuery=createMediaQuery,e.createNetwork=createNetwork,e.createSentinel=createSentinel,e.createViewport=createViewport,e.createWakeLock=createWakeLock,e})({},Arsenal);
|
|
2
2
|
//# sourceMappingURL=sentinel.iife.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentinel.iife.js","names":[],"sources":["../src/errors.ts","../src/_platform.ts","../src/core.ts","../src/element-size.ts","../src/intersection.ts","../src/media-query.ts","../src/network.ts","../src/viewport.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n","import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n","import type { Sentinel, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n this.currentValue = options.initialValue;\n this.disposalSignal = this.abortController.signal;\n\n if (options.signal?.aborted) {\n this.disposedValue = true;\n this.abortController.abort();\n return;\n }\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) {\n this.currentValue = value;\n this.notify();\n }\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n readonly getSnapshot = (): T => this.currentValue;\n\n readonly subscribe = (listener: () => void): Unsubscribe => {\n if (this.disposedValue) return () => {};\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n readonly dispose = (): void => {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n this.listeners.clear();\n\n try {\n cleanup?.();\n } finally {\n this.abortController.abort();\n }\n };\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sentinel.iife.js","names":[],"sources":["../src/errors.ts","../src/core.ts","../src/_platform.ts","../src/element-size.ts","../src/fullscreen.ts","../src/intersection.ts","../src/media-query.ts","../src/network.ts","../src/viewport.ts","../src/wake-lock.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n","import { type Tapper, tapper } from '@vielzeug/arsenal';\n\nimport { SentinelError } from './errors.ts';\nimport type { Sentinel, SentinelEvent, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n private readonly tapper: Tapper<SentinelEvent> = tapper<SentinelEvent>();\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n if (options.signal?.aborted) {\n throw new SentinelError('Sentinel signal is already aborted');\n }\n\n this.currentValue = options.initialValue;\n this.disposalSignal = this.abortController.signal;\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) {\n this.currentValue = value;\n this.notify();\n }\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n readonly getSnapshot = (): T => this.currentValue;\n\n readonly subscribe = (listener: () => void): Unsubscribe => {\n if (this.disposedValue) return () => {};\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n readonly dispose = (): void => {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n this.listeners.clear();\n\n try {\n cleanup?.();\n } finally {\n this.tapper.emit({ type: 'dispose' });\n this.tapper.clear();\n this.abortController.abort();\n }\n };\n\n readonly tap = (handler: (event: SentinelEvent) => void, options?: { readonly signal?: AbortSignal }): Unsubscribe =>\n this.tapper.tap(handler, options);\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch (error) {\n this.tapper.emit({ error, type: 'error' });\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n","import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { FullscreenSentinel, FullscreenState, SentinelOptions } from './types.ts';\n\ntype FullscreenWindow = Window & { document: Document };\n\n/**\n * A sentinel-backed fullscreen view. `request()` enters fullscreen for an element (default:\n * the document element), `exit()` leaves it, and `toggle()` flips it: all degrade silently\n * when the Fullscreen API is missing or the browser refuses (user-activation requirements).\n * Disposing the sentinel exits fullscreen and stops tracking `fullscreenchange`.\n */\nexport function createFullscreen(\n options?: SentinelOptions & { readonly target?: FullscreenWindow },\n): FullscreenSentinel {\n const target = resolveWindow(options?.target);\n const doc = target.document;\n const supported = typeof doc.documentElement?.requestFullscreen === 'function';\n\n const read = (): FullscreenState => ({ active: doc.fullscreenElement !== null, supported });\n let updateState: (value: FullscreenState) => void = () => {};\n\n const request = (element?: Element): void => {\n if (doc.fullscreenElement || !supported) return;\n void (element ?? doc.documentElement).requestFullscreen().catch(() => {\n // Requires user activation in some browsers, or the element may refuse; degrade silently.\n });\n };\n\n const exit = (): void => {\n if (!doc.fullscreenElement) return;\n void doc.exitFullscreen().catch(() => {\n // Degrade silently.\n });\n };\n\n const sentinel = createSentinel<FullscreenState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onFullscreenChange = (): void => updateState(read());\n\n doc.addEventListener('fullscreenchange', onFullscreenChange);\n\n return () => {\n doc.removeEventListener('fullscreenchange', onFullscreenChange);\n if (doc.fullscreenElement) exit();\n };\n });\n\n return {\n ...sentinel,\n exit,\n request,\n toggle(element?: Element): void {\n if (doc.fullscreenElement) exit();\n else request(element);\n },\n };\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { SentinelOptions, WakeLockSentinel, WakeLockState } from './types.ts';\n\ntype WakeLockWindow = Window & { document: Document };\n\nexport function createWakeLock(options?: SentinelOptions & { readonly target?: WakeLockWindow }): WakeLockSentinel {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator;\n const supported = typeof navigator.wakeLock?.request === 'function';\n\n let handle: globalThis.WakeLockSentinel | null = null;\n let pending = false;\n let wantsLock = false;\n\n const read = (): WakeLockState => ({ active: handle !== null, supported });\n let updateState: (value: WakeLockState) => void = () => {};\n\n async function acquire(): Promise<void> {\n if (!supported || !navigator.wakeLock) return;\n pending = true;\n try {\n const requested = await navigator.wakeLock.request('screen');\n // release()/dispose() during the pending request must not leave the lock held.\n if (!wantsLock) {\n void requested.release();\n\n return;\n }\n handle = requested;\n handle.addEventListener('release', () => {\n handle = null;\n updateState(read());\n });\n updateState(read());\n } catch {\n // Not supported, denied, or not in a secure context: degrade silently.\n } finally {\n pending = false;\n }\n }\n\n const sentinel = createSentinel<WakeLockState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onVisibilityChange = (): void => {\n if (target.document.visibilityState === 'visible' && wantsLock && !handle && !pending) {\n void acquire();\n }\n };\n\n target.document.addEventListener('visibilitychange', onVisibilityChange);\n\n return () => {\n target.document.removeEventListener('visibilitychange', onVisibilityChange);\n if (handle) void handle.release();\n handle = null;\n wantsLock = false;\n };\n });\n\n return {\n ...sentinel,\n release(): void {\n wantsLock = false;\n if (handle) {\n void handle.release();\n handle = null;\n updateState(read());\n }\n },\n request(): void {\n if (handle || pending || !supported) return;\n wantsLock = true;\n void acquire();\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/sentinel.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
import{tapper as e}from"@vielzeug/arsenal";var SentinelError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},SentinelUnavailableError=class extends SentinelError{};function createSentinel(e,t){return new SentinelHandle(e,t)}var SentinelHandle=class{disposalSignal;abortController=new AbortController;currentValue;listeners=new Set;cleanup;disposedValue=!1;externalSignal;externalAbortListener;tapper=e();constructor(e,t){if(e.signal?.aborted)throw new SentinelError(`Sentinel signal is already aborted`);this.currentValue=e.initialValue,this.disposalSignal=this.abortController.signal;try{this.cleanup=t(e=>{this.disposedValue||(this.currentValue=e,this.notify())})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}e.signal&&(this.externalSignal=e.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0}))}get disposed(){return this.disposedValue}getSnapshot=()=>this.currentValue;subscribe=e=>this.disposedValue?()=>{}:(this.listeners.add(e),()=>this.listeners.delete(e));dispose=()=>{if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0,this.listeners.clear();try{e?.()}finally{this.tapper.emit({type:`dispose`}),this.tapper.clear(),this.abortController.abort()}};tap=(e,t)=>this.tapper.tap(e,t);notify(){for(let e of[...this.listeners])try{e()}catch(e){this.tapper.emit({error:e,type:`error`}),queueMicrotask(()=>{throw e})}}[Symbol.dispose](){this.dispose()}};function resolveWindow(e){if(e)return e;if(typeof window>`u`)throw new SentinelUnavailableError(`Window is not available in this environment.`);return window}function resolveElementWindow(e){let t=e.ownerDocument.defaultView;if(!t)throw new SentinelUnavailableError(`The element is not connected to a Window.`);return t}function listenMediaQuery(e,t){let onChange=()=>t();if(typeof e.addEventListener==`function`)return e.addEventListener(`change`,onChange),()=>e.removeEventListener(`change`,onChange);let n=e;return typeof n.addListener==`function`?(n.addListener(onChange),()=>n.removeListener?.(onChange)):()=>{}}function createElementSize(e,t){let n=resolveElementWindow(e).ResizeObserver;if(typeof n!=`function`)throw new SentinelUnavailableError(`ResizeObserver is not available in this environment.`);return createSentinel({initialValue:null,...t},t=>{let r=new n(e=>{let n=e.at(-1);n&&t({height:n.contentRect.height,width:n.contentRect.width})});try{r.observe(e,{box:`content-box`})}catch(e){throw r.disconnect(),e}return()=>r.disconnect()})}function createFullscreen(e){let t=resolveWindow(e?.target).document,n=typeof t.documentElement?.requestFullscreen==`function`,read=()=>({active:t.fullscreenElement!==null,supported:n}),updateState=()=>{},request=e=>{!t.fullscreenElement&&n&&(e??t.documentElement).requestFullscreen().catch(()=>{})},exit=()=>{t.fullscreenElement&&t.exitFullscreen().catch(()=>{})};return{...createSentinel({initialValue:read(),...e},e=>{updateState=e;let onFullscreenChange=()=>updateState(read());return t.addEventListener(`fullscreenchange`,onFullscreenChange),()=>{t.removeEventListener(`fullscreenchange`,onFullscreenChange),t.fullscreenElement&&exit()}}),exit,request,toggle(e){t.fullscreenElement?exit():request(e)}}}function createIntersection(e,t){let n=resolveElementWindow(e).IntersectionObserver;if(typeof n!=`function`)throw new SentinelUnavailableError(`IntersectionObserver is not available in this environment.`);return createSentinel({initialValue:null,...t},r=>{let i=new n(e=>{let t=e.at(-1);t&&r({intersectionRatio:t.intersectionRatio,isIntersecting:t.isIntersecting})},{root:t?.root,rootMargin:t?.rootMargin,scrollMargin:t?.scrollMargin,threshold:t?.threshold});try{i.observe(e)}catch(e){throw i.disconnect(),e}return()=>i.disconnect()})}function createMediaQuery(e,t){let n=resolveWindow(t?.target);if(typeof n.matchMedia!=`function`)throw new SentinelUnavailableError(`matchMedia is not available for query "${e}".`);let r=n.matchMedia(e),read=()=>({matches:r.matches});return createSentinel({initialValue:read(),...t},e=>listenMediaQuery(r,()=>e(read())))}function normalizeConnection(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function createNetwork(e){let t=resolveWindow(e?.target),n=t.navigator,read=()=>({connection:normalizeConnection(n.connection),online:n.onLine});return createSentinel({initialValue:read(),...e},e=>{let onChange=()=>e(read()),r=n.connection;return t.addEventListener(`online`,onChange),t.addEventListener(`offline`,onChange),r?.addEventListener(`change`,onChange),()=>{t.removeEventListener(`online`,onChange),t.removeEventListener(`offline`,onChange),r?.removeEventListener(`change`,onChange)}})}function createViewport(e){let t=resolveWindow(e?.target),read=()=>({dpr:t.devicePixelRatio,height:t.innerHeight,width:t.innerWidth});return createSentinel({initialValue:read(),...e},e=>{let onResize=()=>e(read()),n,observeDpr=()=>{n?.(),n=void 0,typeof t.matchMedia==`function`&&(n=listenMediaQuery(t.matchMedia(`(resolution: ${t.devicePixelRatio}dppx)`),()=>{e(read()),observeDpr()}))};return t.addEventListener(`resize`,onResize),observeDpr(),()=>{t.removeEventListener(`resize`,onResize),n?.()}})}function createWakeLock(e){let t=resolveWindow(e?.target),n=t.navigator,r=typeof n.wakeLock?.request==`function`,i=null,a=!1,o=!1,read=()=>({active:i!==null,supported:r}),updateState=()=>{};async function acquire(){if(r&&n.wakeLock){a=!0;try{let e=await n.wakeLock.request(`screen`);if(!o){e.release();return}i=e,i.addEventListener(`release`,()=>{i=null,updateState(read())}),updateState(read())}catch{}finally{a=!1}}}return{...createSentinel({initialValue:read(),...e},e=>{updateState=e;let onVisibilityChange=()=>{t.document.visibilityState===`visible`&&o&&!i&&!a&&acquire()};return t.document.addEventListener(`visibilitychange`,onVisibilityChange),()=>{t.document.removeEventListener(`visibilitychange`,onVisibilityChange),i&&i.release(),i=null,o=!1}}),release(){o=!1,i&&(i.release(),i=null,updateState(read()))},request(){i||a||!r||(o=!0,acquire())}}}export{SentinelError,SentinelUnavailableError,createElementSize,createFullscreen,createIntersection,createMediaQuery,createNetwork,createSentinel,createViewport,createWakeLock};
|
|
2
2
|
//# sourceMappingURL=sentinel.js.map
|
package/dist/sentinel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentinel.js","names":[],"sources":["../src/errors.ts","../src/_platform.ts","../src/core.ts","../src/element-size.ts","../src/intersection.ts","../src/media-query.ts","../src/network.ts","../src/viewport.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n","import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n","import type { Sentinel, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n this.currentValue = options.initialValue;\n this.disposalSignal = this.abortController.signal;\n\n if (options.signal?.aborted) {\n this.disposedValue = true;\n this.abortController.abort();\n return;\n }\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) {\n this.currentValue = value;\n this.notify();\n }\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n readonly getSnapshot = (): T => this.currentValue;\n\n readonly subscribe = (listener: () => void): Unsubscribe => {\n if (this.disposedValue) return () => {};\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n readonly dispose = (): void => {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n this.listeners.clear();\n\n try {\n cleanup?.();\n } finally {\n this.abortController.abort();\n }\n };\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sentinel.js","names":[],"sources":["../src/errors.ts","../src/core.ts","../src/_platform.ts","../src/element-size.ts","../src/fullscreen.ts","../src/intersection.ts","../src/media-query.ts","../src/network.ts","../src/viewport.ts","../src/wake-lock.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n","import { type Tapper, tapper } from '@vielzeug/arsenal';\n\nimport { SentinelError } from './errors.ts';\nimport type { Sentinel, SentinelEvent, SentinelOptions, Unsubscribe } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private currentValue: T;\n private readonly listeners = new Set<() => void>();\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n private readonly tapper: Tapper<SentinelEvent> = tapper<SentinelEvent>();\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n if (options.signal?.aborted) {\n throw new SentinelError('Sentinel signal is already aborted');\n }\n\n this.currentValue = options.initialValue;\n this.disposalSignal = this.abortController.signal;\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) {\n this.currentValue = value;\n this.notify();\n }\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n readonly getSnapshot = (): T => this.currentValue;\n\n readonly subscribe = (listener: () => void): Unsubscribe => {\n if (this.disposedValue) return () => {};\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n readonly dispose = (): void => {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n this.listeners.clear();\n\n try {\n cleanup?.();\n } finally {\n this.tapper.emit({ type: 'dispose' });\n this.tapper.clear();\n this.abortController.abort();\n }\n };\n\n readonly tap = (handler: (event: SentinelEvent) => void, options?: { readonly signal?: AbortSignal }): Unsubscribe =>\n this.tapper.tap(handler, options);\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch (error) {\n this.tapper.emit({ error, type: 'error' });\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n","import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { FullscreenSentinel, FullscreenState, SentinelOptions } from './types.ts';\n\ntype FullscreenWindow = Window & { document: Document };\n\n/**\n * A sentinel-backed fullscreen view. `request()` enters fullscreen for an element (default:\n * the document element), `exit()` leaves it, and `toggle()` flips it: all degrade silently\n * when the Fullscreen API is missing or the browser refuses (user-activation requirements).\n * Disposing the sentinel exits fullscreen and stops tracking `fullscreenchange`.\n */\nexport function createFullscreen(\n options?: SentinelOptions & { readonly target?: FullscreenWindow },\n): FullscreenSentinel {\n const target = resolveWindow(options?.target);\n const doc = target.document;\n const supported = typeof doc.documentElement?.requestFullscreen === 'function';\n\n const read = (): FullscreenState => ({ active: doc.fullscreenElement !== null, supported });\n let updateState: (value: FullscreenState) => void = () => {};\n\n const request = (element?: Element): void => {\n if (doc.fullscreenElement || !supported) return;\n void (element ?? doc.documentElement).requestFullscreen().catch(() => {\n // Requires user activation in some browsers, or the element may refuse; degrade silently.\n });\n };\n\n const exit = (): void => {\n if (!doc.fullscreenElement) return;\n void doc.exitFullscreen().catch(() => {\n // Degrade silently.\n });\n };\n\n const sentinel = createSentinel<FullscreenState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onFullscreenChange = (): void => updateState(read());\n\n doc.addEventListener('fullscreenchange', onFullscreenChange);\n\n return () => {\n doc.removeEventListener('fullscreenchange', onFullscreenChange);\n if (doc.fullscreenElement) exit();\n };\n });\n\n return {\n ...sentinel,\n exit,\n request,\n toggle(element?: Element): void {\n if (doc.fullscreenElement) exit();\n else request(element);\n },\n };\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { SentinelOptions, WakeLockSentinel, WakeLockState } from './types.ts';\n\ntype WakeLockWindow = Window & { document: Document };\n\nexport function createWakeLock(options?: SentinelOptions & { readonly target?: WakeLockWindow }): WakeLockSentinel {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator;\n const supported = typeof navigator.wakeLock?.request === 'function';\n\n let handle: globalThis.WakeLockSentinel | null = null;\n let pending = false;\n let wantsLock = false;\n\n const read = (): WakeLockState => ({ active: handle !== null, supported });\n let updateState: (value: WakeLockState) => void = () => {};\n\n async function acquire(): Promise<void> {\n if (!supported || !navigator.wakeLock) return;\n pending = true;\n try {\n const requested = await navigator.wakeLock.request('screen');\n // release()/dispose() during the pending request must not leave the lock held.\n if (!wantsLock) {\n void requested.release();\n\n return;\n }\n handle = requested;\n handle.addEventListener('release', () => {\n handle = null;\n updateState(read());\n });\n updateState(read());\n } catch {\n // Not supported, denied, or not in a secure context: degrade silently.\n } finally {\n pending = false;\n }\n }\n\n const sentinel = createSentinel<WakeLockState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onVisibilityChange = (): void => {\n if (target.document.visibilityState === 'visible' && wantsLock && !handle && !pending) {\n void acquire();\n }\n };\n\n target.document.addEventListener('visibilitychange', onVisibilityChange);\n\n return () => {\n target.document.removeEventListener('visibilitychange', onVisibilityChange);\n if (handle) void handle.release();\n handle = null;\n wantsLock = false;\n };\n });\n\n return {\n ...sentinel,\n release(): void {\n wantsLock = false;\n if (handle) {\n void handle.release();\n handle = null;\n updateState(read());\n }\n },\n request(): void {\n if (handle || pending || !supported) return;\n wantsLock = true;\n void acquire();\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
export type Unsubscribe = () => void;
|
|
2
|
-
|
|
2
|
+
/** Runtime events emitted by {@link Sentinel.tap}. Handler errors are swallowed. */
|
|
3
|
+
export type SentinelEvent = {
|
|
4
|
+
readonly error: unknown;
|
|
5
|
+
readonly type: 'error';
|
|
6
|
+
} | {
|
|
7
|
+
readonly type: 'dispose';
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Read-only observable contract: `getSnapshot()` reads current state,
|
|
11
|
+
* `subscribe()` registers a change listener. Structural by design; matches
|
|
12
|
+
* `Subscribable` in `@vielzeug/arsenal`.
|
|
13
|
+
*/
|
|
14
|
+
export interface Subscribable<T> {
|
|
3
15
|
getSnapshot(): T;
|
|
4
16
|
subscribe(listener: () => void): Unsubscribe;
|
|
5
17
|
}
|
|
@@ -9,7 +21,11 @@ export interface Disposable {
|
|
|
9
21
|
readonly disposed: boolean;
|
|
10
22
|
[Symbol.dispose](): void;
|
|
11
23
|
}
|
|
12
|
-
export interface Sentinel<T> extends
|
|
24
|
+
export interface Sentinel<T> extends Subscribable<T>, Disposable {
|
|
25
|
+
/** Observe runtime errors from listener dispatch. Returns an unsubscribe function. */
|
|
26
|
+
tap(handler: (event: SentinelEvent) => void, options?: {
|
|
27
|
+
readonly signal?: AbortSignal;
|
|
28
|
+
}): Unsubscribe;
|
|
13
29
|
}
|
|
14
30
|
export interface SentinelOptions {
|
|
15
31
|
readonly signal?: AbortSignal;
|
|
@@ -43,4 +59,40 @@ export interface IntersectionState {
|
|
|
43
59
|
readonly intersectionRatio: number;
|
|
44
60
|
readonly isIntersecting: boolean;
|
|
45
61
|
}
|
|
62
|
+
export interface WakeLockState {
|
|
63
|
+
/** Whether a wake lock is currently held. */
|
|
64
|
+
readonly active: boolean;
|
|
65
|
+
/** Whether the Screen Wake Lock API is available in this environment. */
|
|
66
|
+
readonly supported: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A sentinel-backed screen wake lock. `request()` acquires the lock; `release()` frees it.
|
|
70
|
+
* When the tab is hidden the browser auto-releases the lock; `request()` re-acquires it
|
|
71
|
+
* automatically on `visibilitychange` if the lock was active.
|
|
72
|
+
*/
|
|
73
|
+
export interface WakeLockSentinel extends Sentinel<WakeLockState> {
|
|
74
|
+
/** Release the wake lock. No-op if not active. */
|
|
75
|
+
release(): void;
|
|
76
|
+
/** Acquire the screen wake lock. No-op if already active or unsupported. */
|
|
77
|
+
request(): void;
|
|
78
|
+
}
|
|
79
|
+
export interface FullscreenState {
|
|
80
|
+
/** Whether an element is currently shown fullscreen. */
|
|
81
|
+
readonly active: boolean;
|
|
82
|
+
/** Whether the Fullscreen API is available in this environment. */
|
|
83
|
+
readonly supported: boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A sentinel-backed fullscreen view. `request()` enters fullscreen for an element (default:
|
|
87
|
+
* the document element), `exit()` leaves it, `toggle()` flips it. All degrade silently when
|
|
88
|
+
* the API is missing or the browser refuses. Disposing the sentinel exits fullscreen.
|
|
89
|
+
*/
|
|
90
|
+
export interface FullscreenSentinel extends Sentinel<FullscreenState> {
|
|
91
|
+
/** Leave fullscreen. No-op when not active. */
|
|
92
|
+
exit(): void;
|
|
93
|
+
/** Enter fullscreen for `element` (default: the document element). No-op when already active or unsupported. */
|
|
94
|
+
request(element?: Element): void;
|
|
95
|
+
/** Flip fullscreen for `element` (default: the document element). */
|
|
96
|
+
toggle(element?: Element): void;
|
|
97
|
+
}
|
|
46
98
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/viewport.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./core.cjs"),t=require("./_platform.cjs");function n(n){let r=t.resolveWindow(n?.target),i=()=>({dpr:r.devicePixelRatio,height:r.innerHeight,width:r.innerWidth});return e.createSentinel({initialValue:i(),...n},e=>{let n=()=>e(i()),a,o=()=>{if(a?.(),a=void 0,typeof r.matchMedia!=`function`)return;let n=r.matchMedia(`(resolution: ${r.devicePixelRatio}dppx)`);a=t.listenMediaQuery(n,()=>{e(i()),o()})};return r.addEventListener(`resize`,n),o(),()=>{r.removeEventListener(`resize`,n),a?.()}})}exports.createViewport=n;
|
|
2
2
|
//# sourceMappingURL=viewport.cjs.map
|
package/dist/viewport.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{createSentinel as e}from"./core.js";import{listenMediaQuery as t,resolveWindow as n}from"./_platform.js";function r(r){let i=n(r?.target),a=()=>({dpr:i.devicePixelRatio,height:i.innerHeight,width:i.innerWidth});return e({initialValue:a(),...r},e=>{let n=()=>e(a()),r,o=()=>{if(r?.(),r=void 0,typeof i.matchMedia!=`function`)return;let n=i.matchMedia(`(resolution: ${i.devicePixelRatio}dppx)`);r=t(n,()=>{e(a()),o()})};return i.addEventListener(`resize`,n),o(),()=>{i.removeEventListener(`resize`,n),r?.()}})}export{r as createViewport};
|
|
2
2
|
//# sourceMappingURL=viewport.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./core.cjs"),t=require("./_platform.cjs");function n(n){let r=t.resolveWindow(n?.target),i=r.navigator,a=typeof i.wakeLock?.request==`function`,o=null,s=!1,c=!1,l=()=>({active:o!==null,supported:a}),u=()=>{};async function d(){if(a&&i.wakeLock){s=!0;try{let e=await i.wakeLock.request(`screen`);if(!c){e.release();return}o=e,o.addEventListener(`release`,()=>{o=null,u(l())}),u(l())}catch{}finally{s=!1}}}return{...e.createSentinel({initialValue:l(),...n},e=>{u=e;let t=()=>{r.document.visibilityState===`visible`&&c&&!o&&!s&&d()};return r.document.addEventListener(`visibilitychange`,t),()=>{r.document.removeEventListener(`visibilitychange`,t),o&&o.release(),o=null,c=!1}}),release(){c=!1,o&&(o.release(),o=null,u(l()))},request(){o||s||!a||(c=!0,d())}}}exports.createWakeLock=n;
|
|
2
|
+
//# sourceMappingURL=wake-lock.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wake-lock.cjs","names":[],"sources":["../src/wake-lock.ts"],"sourcesContent":["import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { SentinelOptions, WakeLockSentinel, WakeLockState } from './types.ts';\n\ntype WakeLockWindow = Window & { document: Document };\n\nexport function createWakeLock(options?: SentinelOptions & { readonly target?: WakeLockWindow }): WakeLockSentinel {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator;\n const supported = typeof navigator.wakeLock?.request === 'function';\n\n let handle: globalThis.WakeLockSentinel | null = null;\n let pending = false;\n let wantsLock = false;\n\n const read = (): WakeLockState => ({ active: handle !== null, supported });\n let updateState: (value: WakeLockState) => void = () => {};\n\n async function acquire(): Promise<void> {\n if (!supported || !navigator.wakeLock) return;\n pending = true;\n try {\n const requested = await navigator.wakeLock.request('screen');\n // release()/dispose() during the pending request must not leave the lock held.\n if (!wantsLock) {\n void requested.release();\n\n return;\n }\n handle = requested;\n handle.addEventListener('release', () => {\n handle = null;\n updateState(read());\n });\n updateState(read());\n } catch {\n // Not supported, denied, or not in a secure context: degrade silently.\n } finally {\n pending = false;\n }\n }\n\n const sentinel = createSentinel<WakeLockState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onVisibilityChange = (): void => {\n if (target.document.visibilityState === 'visible' && wantsLock && !handle && !pending) {\n void acquire();\n }\n };\n\n target.document.addEventListener('visibilitychange', onVisibilityChange);\n\n return () => {\n target.document.removeEventListener('visibilitychange', onVisibilityChange);\n if (handle) void handle.release();\n handle = null;\n wantsLock = false;\n };\n });\n\n return {\n ...sentinel,\n release(): void {\n wantsLock = false;\n if (handle) {\n void handle.release();\n handle = null;\n updateState(read());\n }\n },\n request(): void {\n if (handle || pending || !supported) return;\n wantsLock = true;\n void acquire();\n },\n };\n}\n"],"mappings":"2DAMA,SAAgB,EAAe,EAAoF,CACjH,IAAM,EAAS,EAAA,cAAc,GAAS,MAAM,EACtC,EAAY,EAAO,UACnB,EAAY,OAAO,EAAU,UAAU,SAAY,WAErD,EAA6C,KAC7C,EAAU,GACV,EAAY,GAEV,OAA6B,CAAE,OAAQ,IAAW,KAAM,WAAU,GACpE,MAAoD,CAAC,EAEzD,eAAe,GAAyB,CAClC,GAAC,GAAc,EAAU,SAC7B,GAAU,GACV,GAAI,CACF,IAAM,EAAY,MAAM,EAAU,SAAS,QAAQ,QAAQ,EAE3D,GAAI,CAAC,EAAW,CACd,EAAe,QAAQ,EAEvB,MACF,CACA,EAAS,EACT,EAAO,iBAAiB,cAAiB,CACvC,EAAS,KACT,EAAY,EAAK,CAAC,CACpB,CAAC,EACD,EAAY,EAAK,CAAC,CACpB,MAAQ,CAER,QAAU,CACR,EAAU,EACZ,CAnBU,CAoBZ,CAqBA,MAAO,CACL,GApBe,EAAA,eAA8B,CAAE,aAAc,EAAK,EAAG,GAAG,CAAQ,EAAI,GAAW,CAC/F,EAAc,EAEd,IAAM,MAAiC,CACjC,EAAO,SAAS,kBAAoB,WAAa,GAAa,CAAC,GAAU,CAAC,GAC5E,EAAa,CAEjB,EAIA,OAFA,EAAO,SAAS,iBAAiB,mBAAoB,CAAkB,MAE1D,CACX,EAAO,SAAS,oBAAoB,mBAAoB,CAAkB,EACtE,GAAQ,EAAY,QAAQ,EAChC,EAAS,KACT,EAAY,EACd,CACF,CAGK,EACH,SAAgB,CACd,EAAY,GACR,IACF,EAAY,QAAQ,EACpB,EAAS,KACT,EAAY,EAAK,CAAC,EAEtB,EACA,SAAgB,CACV,GAAU,GAAW,CAAC,IAC1B,EAAY,GACZ,EAAa,EACf,CACF,CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SentinelOptions, WakeLockSentinel } from './types.ts';
|
|
2
|
+
type WakeLockWindow = Window & {
|
|
3
|
+
document: Document;
|
|
4
|
+
};
|
|
5
|
+
export declare function createWakeLock(options?: SentinelOptions & {
|
|
6
|
+
readonly target?: WakeLockWindow;
|
|
7
|
+
}): WakeLockSentinel;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=wake-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wake-lock.d.ts","sourceRoot":"","sources":["../src/wake-lock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAiB,MAAM,YAAY,CAAC;AAEnF,KAAK,cAAc,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEtD,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAA;CAAE,GAAG,gBAAgB,CAuEjH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createSentinel as e}from"./core.js";import{resolveWindow as t}from"./_platform.js";function n(n){let r=t(n?.target),i=r.navigator,a=typeof i.wakeLock?.request==`function`,o=null,s=!1,c=!1,l=()=>({active:o!==null,supported:a}),u=()=>{};async function d(){if(a&&i.wakeLock){s=!0;try{let e=await i.wakeLock.request(`screen`);if(!c){e.release();return}o=e,o.addEventListener(`release`,()=>{o=null,u(l())}),u(l())}catch{}finally{s=!1}}}return{...e({initialValue:l(),...n},e=>{u=e;let t=()=>{r.document.visibilityState===`visible`&&c&&!o&&!s&&d()};return r.document.addEventListener(`visibilitychange`,t),()=>{r.document.removeEventListener(`visibilitychange`,t),o&&o.release(),o=null,c=!1}}),release(){c=!1,o&&(o.release(),o=null,u(l()))},request(){o||s||!a||(c=!0,d())}}}export{n as createWakeLock};
|
|
2
|
+
//# sourceMappingURL=wake-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wake-lock.js","names":[],"sources":["../src/wake-lock.ts"],"sourcesContent":["import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { SentinelOptions, WakeLockSentinel, WakeLockState } from './types.ts';\n\ntype WakeLockWindow = Window & { document: Document };\n\nexport function createWakeLock(options?: SentinelOptions & { readonly target?: WakeLockWindow }): WakeLockSentinel {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator;\n const supported = typeof navigator.wakeLock?.request === 'function';\n\n let handle: globalThis.WakeLockSentinel | null = null;\n let pending = false;\n let wantsLock = false;\n\n const read = (): WakeLockState => ({ active: handle !== null, supported });\n let updateState: (value: WakeLockState) => void = () => {};\n\n async function acquire(): Promise<void> {\n if (!supported || !navigator.wakeLock) return;\n pending = true;\n try {\n const requested = await navigator.wakeLock.request('screen');\n // release()/dispose() during the pending request must not leave the lock held.\n if (!wantsLock) {\n void requested.release();\n\n return;\n }\n handle = requested;\n handle.addEventListener('release', () => {\n handle = null;\n updateState(read());\n });\n updateState(read());\n } catch {\n // Not supported, denied, or not in a secure context: degrade silently.\n } finally {\n pending = false;\n }\n }\n\n const sentinel = createSentinel<WakeLockState>({ initialValue: read(), ...options }, (update) => {\n updateState = update;\n\n const onVisibilityChange = (): void => {\n if (target.document.visibilityState === 'visible' && wantsLock && !handle && !pending) {\n void acquire();\n }\n };\n\n target.document.addEventListener('visibilitychange', onVisibilityChange);\n\n return () => {\n target.document.removeEventListener('visibilitychange', onVisibilityChange);\n if (handle) void handle.release();\n handle = null;\n wantsLock = false;\n };\n });\n\n return {\n ...sentinel,\n release(): void {\n wantsLock = false;\n if (handle) {\n void handle.release();\n handle = null;\n updateState(read());\n }\n },\n request(): void {\n if (handle || pending || !supported) return;\n wantsLock = true;\n void acquire();\n },\n };\n}\n"],"mappings":"0FAMA,SAAgB,EAAe,EAAoF,CACjH,IAAM,EAAS,EAAc,GAAS,MAAM,EACtC,EAAY,EAAO,UACnB,EAAY,OAAO,EAAU,UAAU,SAAY,WAErD,EAA6C,KAC7C,EAAU,GACV,EAAY,GAEV,OAA6B,CAAE,OAAQ,IAAW,KAAM,WAAU,GACpE,MAAoD,CAAC,EAEzD,eAAe,GAAyB,CAClC,GAAC,GAAc,EAAU,SAC7B,GAAU,GACV,GAAI,CACF,IAAM,EAAY,MAAM,EAAU,SAAS,QAAQ,QAAQ,EAE3D,GAAI,CAAC,EAAW,CACd,EAAe,QAAQ,EAEvB,MACF,CACA,EAAS,EACT,EAAO,iBAAiB,cAAiB,CACvC,EAAS,KACT,EAAY,EAAK,CAAC,CACpB,CAAC,EACD,EAAY,EAAK,CAAC,CACpB,MAAQ,CAER,QAAU,CACR,EAAU,EACZ,CAnBU,CAoBZ,CAqBA,MAAO,CACL,GApBe,EAA8B,CAAE,aAAc,EAAK,EAAG,GAAG,CAAQ,EAAI,GAAW,CAC/F,EAAc,EAEd,IAAM,MAAiC,CACjC,EAAO,SAAS,kBAAoB,WAAa,GAAa,CAAC,GAAU,CAAC,GAC5E,EAAa,CAEjB,EAIA,OAFA,EAAO,SAAS,iBAAiB,mBAAoB,CAAkB,MAE1D,CACX,EAAO,SAAS,oBAAoB,mBAAoB,CAAkB,EACtE,GAAQ,EAAY,QAAQ,EAChC,EAAS,KACT,EAAY,EACd,CACF,CAGK,EACH,SAAgB,CACd,EAAY,GACR,IACF,EAAY,QAAQ,EACpB,EAAS,KACT,EAAY,EAAK,CAAC,EAEtB,EACA,SAAgB,CACV,GAAU,GAAW,CAAC,IAC1B,EAAY,GACZ,EAAa,EACf,CACF,CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vielzeug/sentinel",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "26.10.1",
|
|
4
4
|
"description": "Subscribable snapshots for external browser environment state",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -39,11 +39,14 @@
|
|
|
39
39
|
"test": "vitest",
|
|
40
40
|
"test:e2e": "pnpm --silent run build && playwright test"
|
|
41
41
|
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@vielzeug/arsenal": "26.10.1"
|
|
44
|
+
},
|
|
42
45
|
"devDependencies": {
|
|
43
46
|
"@playwright/test": "^1.63.0",
|
|
44
|
-
"@types/node": "^26.
|
|
47
|
+
"@types/node": "^26.6.2",
|
|
45
48
|
"typescript": "^6.0.3",
|
|
46
|
-
"vite": "^8.
|
|
47
|
-
"vitest": "^5.0.
|
|
49
|
+
"vite": "^8.3.0",
|
|
50
|
+
"vitest": "^5.0.1"
|
|
48
51
|
}
|
|
49
52
|
}
|