UNPKG

@aegisjsproject/router

Version:
1 lines 152 kB
{"version":3,"file":"router.mjs","sources":["node_modules/@aegisjsproject/url/utils.js","node_modules/@aegisjsproject/url/parser.js","node_modules/@aegisjsproject/callback-registry/callbacks.js","node_modules/@aegisjsproject/callback-registry/events.js","node_modules/@shgysk8zer0/signals/signals.js","router.js"],"sourcesContent":["export function stringify(thing) {\n\tswitch(typeof thing) {\n\t\tcase 'string':\n\t\t\treturn thing;\n\n\t\tcase 'function':\n\t\t\tthrow new TypeError('Functions are not supported.');\n\n\t\tcase 'undefined':\n\t\t\treturn '';\n\n\t\tcase 'object':\n\t\t\tif (thing === null) {\n\t\t\t\treturn '';\n\t\t\t} else if (thing instanceof Date) {\n\t\t\t\treturn thing.toISOString();\n\t\t\t} else if (Array.isArray(thing)) {\n\t\t\t\treturn thing.map(stringify).join(',');\n\t\t\t} else if (thing instanceof ArrayBuffer && Uint8Array.prototype.toBase64 instanceof Function) {\n\t\t\t\treturn new Uint8Array(thing).toBase64();\n\t\t\t} else if (ArrayBuffer.isView(thing) && thing.toBase64 instanceof Function) {\n\t\t\t\treturn thing.toBase64();\n\t\t\t} else if (thing instanceof Blob) {\n\t\t\t\treturn URL.createObjectURL(thing);\n\t\t\t} else {\n\t\t\t\treturn thing.toString();\n\t\t\t}\n\n\t\tdefault:\n\t\t\treturn thing.toString();\n\t}\n}\n","import { stringify } from './utils.js';\n\n/**\n * Escapes a component of a URL, also protecting against path traversal\n *\n * @param {string} str\n * @returns {string} The URL-safe string\n */\nexport function escape(str) {\n\treturn encodeURIComponent(stringify(str).trim()).replaceAll('..%2F', '%2E%2E%2F').replaceAll('.%2F', '%2E%2E%2F');\n}\n\n/**\n * Creates a URL parser tagged template with a custom base\n *\n * @param {string} [base=document.baseURI] Base to parse relative URLs from\n * @returns {Function} A URL parsing tagged template\n */\nexport function createURLParser(base = globalThis?.document?.baseURI) {\n\treturn function url(strings, value, ...values) {\n\t\tif (value instanceof Blob && strings.length === 2 && strings[0] === '' && strings[1] === '') {\n\t\t\treturn new URL(URL.createObjectURL(value));\n\t\t} else if (URL.canParse(value)) {\n\t\t\treturn URL.parse(String.raw(strings, '', ...values.map(escape)), value);\n\t\t} else if (strings[0].startsWith('/')) {\n\t\t\treturn URL.parse(String.raw(strings, escape(value), ...values.map(escape)), base);\n\t\t} else if (strings[0].startsWith('./') || strings[0].startsWith('../')) {\n\t\t\treturn URL.parse(String.raw(strings, escape(value), ...values.map(escape)), base);\n\t\t} else {\n\t\t\treturn URL.parse(String.raw(strings, escape(value), ...values.map(escape)));\n\t\t}\n\t};\n}\n\n/**\n * A function for creating URL objects from tagged template literals.\n *\n * @param {TemplateStringsArray} strings - The template string parts.\n * @param {...any} args - The template string substitutions.\n * @returns {URL | null} - A URL object if the URL is valid, otherwise null.\n */\nexport const url = createURLParser();\n","import {\n\teventToProp, capture as captureAttr, once as onceAttr, passive as passiveAttr, signal as signalAttr,\n\tregisterEventAttribute, hasEventAttribute, registerSignal, abortController,\n} from './events.js';\n\nlet _isRegistrationOpen = true;\n\nconst $$ = (selector, base = document) => base.querySelectorAll(selector);\n\nconst $ = (selector, base = document) => base.querySelector(selector);\n\nexport const requestFullscreen = { attr: 'data-request-fullscreen-selector', data: 'requestFullscreenSelector' };\nexport const toggleFullsceen = { attr: 'data-toggle-fullscreen-selector', data: 'toggleFullscreenSelector' };\n\nexport const FUNCS = {\n\tdebug: {\n\t\tlog: 'aegis:debug:log',\n\t\tinfo: 'aegis:debug:info',\n\t\twarn: 'aegis:debug:warn',\n\t\terror: 'aegis:debug:error',\n\t},\n\tnavigate: {\n\t\tback: 'aegis:navigate:back',\n\t\tforward: 'aegis:navigate:forward',\n\t\treload: 'aegis:navigate:reload',\n\t\tclose: 'aegis:navigate:close',\n\t\tlink: 'aegis:navigate:go',\n\t\tpopup: 'aegis:navigate:popup',\n\t},\n\tui: {\n\t\tcommand: 'aegis:ui:command',\n\t\tprint: 'aegis:ui:print',\n\t\tremove: 'aegis:ui:remove',\n\t\thide: 'aegis:ui:hide',\n\t\tunhide: 'aegis:ui:unhide',\n\t\tshowModal: 'aegis:ui:showModal',\n\t\tcloseModal: 'aegis:ui:closeModal',\n\t\tshowPopover: 'aegis:ui:showPopover',\n\t\thidePopover: 'aegis:ui:hidePopover',\n\t\ttogglePopover: 'aegis:ui:togglePopover',\n\t\tenable: 'aegis:ui:enable',\n\t\tdisable: 'aegis:ui:disable',\n\t\tscrollTo: 'aegis:ui:scrollTo',\n\t\tprevent: 'aegis:ui:prevent',\n\t\trevokeObjectURL: 'aegis:ui:revokeObjectURL',\n\t\tcancelAnimationFrame: 'aegis:ui:cancelAnimationFrame',\n\t\tclearInterval: 'aegis:clearInterval',\n\t\tclearTimeout: 'aegis:clearTimeout',\n\t\trequestFullscreen: 'aegis:ui:requestFullscreen',\n\t\ttoggleFullscreen: 'aegis:ui:toggleFullsceen',\n\t\texitFullsceen: 'aegis:ui:exitFullscreen',\n\t\topen: 'aegis:ui:open',\n\t\tclose: 'aegis:ui:close',\n\t\tabortController: 'aegis:ui:controller:abort',\n\t},\n};\n\n/**\n * @type {Map<string, function>}\n */\nconst registry = new Map([\n\t[FUNCS.debug.log, console.log],\n\t[FUNCS.debug.warn, console.warn],\n\t[FUNCS.debug.error, console.error],\n\t[FUNCS.debug.info, console.info],\n\t[FUNCS.navigate.back, () => history.back()],\n\t[FUNCS.navigate.forward, () => history.forward()],\n\t[FUNCS.navigate.reload, () => history.go(0)],\n\t[FUNCS.navigate.close, () => globalThis.close()],\n\t[FUNCS.navigate.link, event => {\n\t\tif (event.isTrusted) {\n\t\t\tevent.preventDefault();\n\t\t\tlocation.href = event.currentTarget.dataset.url;\n\t\t}\n\t}],\n\t[FUNCS.navigate.popup, event => {\n\t\tif (event.isTrusted) {\n\t\t\tevent.preventDefault();\n\t\t\tglobalThis.open(event.currentTarget.dataset.url);\n\t\t}\n\t}],\n\t[FUNCS.ui.hide, ({ currentTarget }) => {\n\t\t$$(currentTarget.dataset.hideSelector).forEach(el => el.hidden = true);\n\t}],\n\t[FUNCS.ui.unhide, ({ currentTarget }) => {\n\t\t$$(currentTarget.dataset.unhideSelector).forEach(el => el.hidden = false);\n\t}],\n\t[FUNCS.ui.disable, ({ currentTarget }) => {\n\t\t$$(currentTarget.dataset.disableSelector).forEach(el => el.disabled = true);\n\t}],\n\t[FUNCS.ui.enable, ({ currentTarget }) => {\n\t\t$$(currentTarget.dataset.enableSelector).forEach(el => el.disabled = false);\n\t}],\n\t[FUNCS.ui.remove, ({ currentTarget }) => {\n\t\t$$(currentTarget.dataset.removeSelector).forEach(el => el.remove());\n\t}],\n\t[FUNCS.ui.scrollTo, ({ currentTarget }) => {\n\t\tconst target = $(currentTarget.dataset.scrollToSelector);\n\n\t\tif (target instanceof Element) {\n\t\t\ttarget.scrollIntoView({\n\t\t\t\tbehavior: matchMedia('(prefers-reduced-motion: reduce)').matches\n\t\t\t\t\t? 'instant'\n\t\t\t\t\t: 'smooth',\n\t\t\t});\n\t\t}\n\t}],\n\t[FUNCS.ui.revokeObjectURL, ({ currentTarget }) => URL.revokeObjectURL(currentTarget.src)],\n\t[FUNCS.ui.cancelAnimationFrame, ({ currentTarget }) => cancelAnimationFrame(parseInt(currentTarget.dataset.animationFrame))],\n\t[FUNCS.ui.clearInterval, ({ currentTarget }) => clearInterval(parseInt(currentTarget.dataset.clearInterval))],\n\t[FUNCS.ui.clearTimeout, ({ currentTarget }) => clearTimeout(parseInt(currentTarget.dataset.timeout))],\n\t[FUNCS.ui.abortController, ({ currentTarget }) => abortController(currentTarget.dataset.aegisEventController, currentTarget.dataset.aegisControllerReason)],\n\t[FUNCS.ui.open, ({ currentTarget }) => document.querySelector(currentTarget.dataset.openSelector).open = true],\n\t[FUNCS.ui.close, ({ currentTarget }) => document.querySelector(currentTarget.dataset.closeSelector).open = false],\n\t[FUNCS.ui.showModal, ({ currentTarget }) => {\n\t\tconst target = $(currentTarget.dataset.showModalSelector);\n\n\t\tif (target instanceof HTMLDialogElement) {\n\t\t\ttarget.showModal();\n\t\t}\n\t}],\n\t[FUNCS.ui.closeModal, ({ currentTarget }) => {\n\t\tconst target = $(currentTarget.dataset.closeModalSelector);\n\n\t\tif (target instanceof HTMLDialogElement) {\n\t\t\ttarget.close();\n\t\t}\n\t}],\n\t[FUNCS.ui.showPopover, ({ currentTarget }) => {\n\t\tconst target = $(currentTarget.dataset.showPopoverSelector);\n\n\t\tif (target instanceof HTMLElement) {\n\t\t\ttarget.showPopover();\n\t\t}\n\t}],\n\t[FUNCS.ui.hidePopover, ({ currentTarget }) => {\n\t\tconst target = $(currentTarget.dataset.hidePopoverSelector);\n\n\t\tif (target instanceof HTMLElement) {\n\t\t\ttarget.hidePopover();\n\t\t}\n\t}],\n\t[FUNCS.ui.togglePopover, ({ currentTarget }) => {\n\t\tconst target = $(currentTarget.dataset.togglePopoverSelector);\n\n\t\tif (target instanceof HTMLElement) {\n\t\t\ttarget.togglePopover();\n\t\t}\n\t}],\n\t[FUNCS.ui.print, () => globalThis.print()],\n\t[FUNCS.ui.prevent, event => event.preventDefault()],\n\t[FUNCS.ui.requestFullscreen, ({ currentTarget}) => {\n\t\tif (currentTarget.dataset.hasOwnProperty(requestFullscreen.data)) {\n\t\t\tdocument.getElementById(currentTarget.dataset[requestFullscreen.data]).requestFullscreen();\n\t\t} else {\n\t\t\tcurrentTarget.requestFullscreen();\n\t\t}\n\t}],\n\t[FUNCS.ui.toggleFullscreen, ({ currentTarget }) => {\n\t\tconst target = currentTarget.dataset.hasOwnProperty(toggleFullsceen.data)\n\t\t\t? document.getElementById(currentTarget.dataset[toggleFullsceen.data])\n\t\t\t: currentTarget;\n\n\t\tif (target.isSameNode(document.fullscreenElement)) {\n\t\t\tdocument.exitFullscreen();\n\t\t} else {\n\t\t\ttarget.requestFullscreen();\n\t\t}\n\t}],\n\t[FUNCS.ui.exitFullsceen, () => document.exitFullscreen()],\n]);\n\nexport class CallbackRegistryKey extends String {\n\t[Symbol.dispose]() {\n\t\tregistry.delete(this.toString());\n\t}\n}\n\n/**\n * Check if callback registry is open\n *\n * @returns {boolean} Whether or not callback registry is open\n */\nexport const isRegistrationOpen = () => _isRegistrationOpen;\n\n/**\n * Close callback registry\n *\n * @returns {boolean} Whether or not the callback was succesfully removed\n */\nexport const closeRegistration = () => _isRegistrationOpen = false;\n\n/**\n * Get an array of registered callbacks\n *\n * @returns {Array} A frozen array listing keys to all registered callbacks\n */\nexport const listCallbacks = () => Object.freeze(Array.from(registry.keys()));\n\n/**\n * Check if a callback is registered\n *\n * @param {CallbackRegistryKey|string} name The name/key to check for in callback registry\n * @returns {boolean} Whether or not a callback is registered\n */\nexport const hasCallback = name => registry.has(name?.toString());\n\n/**\n * Get a callback from the registry by name/key\n *\n * @param {CallbackRegistryKey|string} name The name/key of the callback to get\n * @returns {Function|undefined} The corresponding function registered under that name/key\n */\nexport const getCallback = name => registry.get(name?.toString());\n\n/**\n *\t Remove a callback from the registry\n *\n * @param {CallbackRegistryKey|string} name The name/key of the callback to get\n * @returns {boolean} Whether or not the callback was successfully unregisterd\n */\nexport const unregisterCallback = name => _isRegistrationOpen && registry.delete(name?.toString());\n\n/**\n * Remove all callbacks from the registry\n *\n * @returns {void}\n */\nexport const clearRegistry = () => registry.clear();\n\n/**\n * Create a registered callback with a randomly generated name\n *\n * @param {Function} callback Callback function to register\n * @param {object} [config]\n * @param {DisposableStack|AsyncDisposableStack} [config.stack] Optional `DisposableStack` to handle disposal and unregistering.\n * @returns {CallbackRegistryKey} The automatically generated key/name of the registered callback\n */\nexport const createCallback = (callback, { stack } = {}) => registerCallback('aegis:callback:' + crypto.randomUUID(), callback, { stack });\n\n/**\n * Call a callback fromt the registry by name/key\n *\n * @param {CallbackRegistryKey|string} name The name/key of the registered function\n * @param {...any} args Any arguments to pass along to the function\n * @returns {any} Whatever the return value of the function is\n * @throws {Error} Throws if callback is not found or any error resulting from calling the function\n */\nexport function callCallback(name, ...args) {\n\tif (registry.has(name?.toString())) {\n\t\treturn registry.get(name?.toString()).apply(this || globalThis, args);\n\t} else {\n\t\tthrow new Error(`No ${name} function registered.`);\n\t}\n}\n\n/**\n * Register a named callback in registry\n *\n * @param {CallbackRegistryKey|string} name The name/key to register the callback under\n * @param {Function} callback The callback value to register\n * @param {object} config\n * @param {DisposableStack|AsyncDisposableStack} [config.stack] Optional `DisposableStack` to handle disposal and unregistering.\n * @returns {CallbackRegistryKey} The registered name/key\n */\nexport function registerCallback(name, callback, { stack } = {}) {\n\tif (typeof name === 'string') {\n\t\treturn registerCallback(new CallbackRegistryKey(name), callback, { stack });\n\t} else if (! (name instanceof CallbackRegistryKey)) {\n\t\tthrow new TypeError('Callback name must be a disposable string/CallbackRegistryKey.');\n\t} else if (typeof callback === 'object' && typeof callback.handleEvent === 'function') {\n\t\treturn registerCallback(name, callback.handleEvent.bind(callback), { stack });\n\t} else if (! (typeof callback === 'function' || typeof callback?.handleEvent === 'function')) {\n\t\tthrow new TypeError('Callback must be a function.');\n\t} else if (! _isRegistrationOpen) {\n\t\tthrow new TypeError('Cannot register new callbacks because registry is closed.');\n\t} else if (registry.has(name?.toString())) {\n\t\tthrow new Error(`Handler \"${name}\" is already registered.`);\n\t} else if (stack instanceof DisposableStack || stack instanceof AsyncDisposableStack) {\n\t\tconst key = stack.use(new CallbackRegistryKey(name));\n\t\tregistry.set(key.toString(), callback);\n\n\t\treturn key;\n\t} else {\n\t\tconst key = new CallbackRegistryKey(name);\n\t\tregistry.set(key.toString(), callback);\n\n\t\treturn key;\n\t}\n}\n\n/**\n * Get the host/root node of a given thing.\n *\n * @param {Event|Document|Element|ShadowRoot} target Source thing to search for host of\n * @returns {Document|Element|null} The host/root node, or null\n */\nexport function getHost(target) {\n\tif (target instanceof Event) {\n\t\treturn getHost(target.currentTarget);\n\t} else if (target instanceof Document) {\n\t\treturn target;\n\t} else if (target instanceof Element) {\n\t\treturn getHost(target.getRootNode());\n\t} else if (target instanceof ShadowRoot) {\n\t\treturn target.host;\n\t} else {\n\t\treturn null;\n\t}\n}\n\nexport function on(event, callback, { capture = false, passive = false, once = false, signal, stack } = {}) {\n\tif (callback instanceof Function) {\n\t\treturn on(event, createCallback(callback, { stack }), { capture, passive, once, signal });\n\t} else if (! (callback instanceof String || typeof callback === 'string') || callback.length === 0) {\n\t\tthrow new TypeError('Callback must be a function or a registered callback string.');\n\t} else if (typeof event !== 'string' || event.length === 0) {\n\t\tthrow new TypeError('Event must be a non-empty string.');\n\t} else {\n\t\tif (! hasEventAttribute(event)) {\n\t\t\tregisterEventAttribute(event);\n\t\t}\n\n\t\tconst parts = [[eventToProp(event), callback]];\n\n\t\tif (capture) {\n\t\t\tparts.push([captureAttr, '']);\n\t\t}\n\n\t\tif (passive) {\n\t\t\tparts.push([passiveAttr, '']);\n\t\t}\n\n\t\tif (once) {\n\t\t\tparts.push([onceAttr, '']);\n\t\t}\n\n\t\tif (signal instanceof AbortSignal) {\n\t\t\tparts.push([signalAttr, registerSignal(signal)]);\n\t\t} else if (typeof signal === 'string') {\n\t\t\tparts.push([signalAttr, signal]);\n\t\t}\n\n\t\treturn parts.map(([prop, val]) => `${prop}=\"${val}\"`).join(' ');\n\t}\n}\n","import { hasCallback, getCallback } from './callbacks.js';\nimport { Signal } from '@shgysk8zer0/signals';\n\nconst PREFIX = 'data-aegis-event-';\nconst EVENT_PREFIX = PREFIX + 'on-';\nconst EVENT_PREFIX_LENGTH = EVENT_PREFIX.length;\nconst DATA_PREFIX = 'aegisEventOn';\nconst DATA_PREFIX_LENGTH = DATA_PREFIX.length;\nconst signalSymbol = Symbol('aegis:signal');\nconst controllerSymbol = Symbol('aegis:controller');\nconst signalRegistry = new Map();\nconst controllerRegistry = new Map();\n\nexport const once = PREFIX + 'once';\nexport const passive = PREFIX + 'passive';\nexport const capture = PREFIX + 'capture';\nexport const signal = PREFIX + 'signal';\nexport const controller = PREFIX + 'controller';\nexport const onAbort = EVENT_PREFIX + 'abort';\nexport const onBlur = EVENT_PREFIX + 'blur';\nexport const onFocus = EVENT_PREFIX + 'focus';\nexport const onCancel = EVENT_PREFIX + 'cancel';\nexport const onAuxclick = EVENT_PREFIX + 'auxclick';\nexport const onBeforeinput = EVENT_PREFIX + 'beforeinput';\nexport const onBeforetoggle = EVENT_PREFIX + 'beforetoggle';\nexport const onCanplay = EVENT_PREFIX + 'canplay';\nexport const onCanplaythrough = EVENT_PREFIX + 'canplaythrough';\nexport const onChange = EVENT_PREFIX + 'change';\nexport const onClick = EVENT_PREFIX + 'click';\nexport const onClose = EVENT_PREFIX + 'close';\nexport const onCommand = EVENT_PREFIX + 'command';\nexport const onContextmenu = EVENT_PREFIX + 'contextmenu';\nexport const onCopy = EVENT_PREFIX + 'copy';\nexport const onCuechange = EVENT_PREFIX + 'cuechange';\nexport const onCut = EVENT_PREFIX + 'cut';\nexport const onDblclick = EVENT_PREFIX + 'dblclick';\nexport const onDrag = EVENT_PREFIX + 'drag';\nexport const onDragend = EVENT_PREFIX + 'dragend';\nexport const onDragenter = EVENT_PREFIX + 'dragenter';\nexport const onDragexit = EVENT_PREFIX + 'dragexit';\nexport const onDragleave = EVENT_PREFIX + 'dragleave';\nexport const onDragover = EVENT_PREFIX + 'dragover';\nexport const onDragstart = EVENT_PREFIX + 'dragstart';\nexport const onDrop = EVENT_PREFIX + 'drop';\nexport const onDurationchange = EVENT_PREFIX + 'durationchange';\nexport const onEmptied = EVENT_PREFIX + 'emptied';\nexport const onEnded = EVENT_PREFIX + 'ended';\nexport const onFormdata = EVENT_PREFIX + 'formdata';\nexport const onInput = EVENT_PREFIX + 'input';\nexport const onInvalid = EVENT_PREFIX + 'invalid';\nexport const onKeydown = EVENT_PREFIX + 'keydown';\nexport const onKeypress = EVENT_PREFIX + 'keypress';\nexport const onKeyup = EVENT_PREFIX + 'keyup';\nexport const onLoad = EVENT_PREFIX + 'load';\nexport const onLoadeddata = EVENT_PREFIX + 'loadeddata';\nexport const onLoadedmetadata = EVENT_PREFIX + 'loadedmetadata';\nexport const onLoadstart = EVENT_PREFIX + 'loadstart';\nexport const onMousedown = EVENT_PREFIX + 'mousedown';\nexport const onMouseenter = EVENT_PREFIX + 'mouseenter';\nexport const onMouseleave = EVENT_PREFIX + 'mouseleave';\nexport const onMousemove = EVENT_PREFIX + 'mousemove';\nexport const onMouseout = EVENT_PREFIX + 'mouseout';\nexport const onMouseover = EVENT_PREFIX + 'mouseover';\nexport const onMouseup = EVENT_PREFIX + 'mouseup';\nexport const onWheel = EVENT_PREFIX + 'wheel';\nexport const onPaste = EVENT_PREFIX + 'paste';\nexport const onPause = EVENT_PREFIX + 'pause';\nexport const onPlay = EVENT_PREFIX + 'play';\nexport const onPlaying = EVENT_PREFIX + 'playing';\nexport const onProgress = EVENT_PREFIX + 'progress';\nexport const onRatechange = EVENT_PREFIX + 'ratechange';\nexport const onReset = EVENT_PREFIX + 'reset';\nexport const onResize = EVENT_PREFIX + 'resize';\nexport const onScroll = EVENT_PREFIX + 'scroll';\nexport const onScrollend = EVENT_PREFIX + 'scrollend';\nexport const onSecuritypolicyviolation = EVENT_PREFIX + 'securitypolicyviolation';\nexport const onSeeked = EVENT_PREFIX + 'seeked';\nexport const onSeeking = EVENT_PREFIX + 'seeking';\nexport const onSelect = EVENT_PREFIX + 'select';\nexport const onSlotchange = EVENT_PREFIX + 'slotchange';\nexport const onStalled = EVENT_PREFIX + 'stalled';\nexport const onSubmit = EVENT_PREFIX + 'submit';\nexport const onSuspend = EVENT_PREFIX + 'suspend';\nexport const onTimeupdate = EVENT_PREFIX + 'timeupdate';\nexport const onVolumechange = EVENT_PREFIX + 'volumechange';\nexport const onWaiting = EVENT_PREFIX + 'waiting';\nexport const onSelectstart = EVENT_PREFIX + 'selectstart';\nexport const onSelectionchange = EVENT_PREFIX + 'selectionchange';\nexport const onToggle = EVENT_PREFIX + 'toggle';\nexport const onPointercancel = EVENT_PREFIX + 'pointercancel';\nexport const onPointerdown = EVENT_PREFIX + 'pointerdown';\nexport const onPointerup = EVENT_PREFIX + 'pointerup';\nexport const onPointermove = EVENT_PREFIX + 'pointermove';\nexport const onPointerout = EVENT_PREFIX + 'pointerout';\nexport const onPointerover = EVENT_PREFIX + 'pointerover';\nexport const onPointerenter = EVENT_PREFIX + 'pointerenter';\nexport const onPointerleave = EVENT_PREFIX + 'pointerleave';\nexport const onGotpointercapture = EVENT_PREFIX + 'gotpointercapture';\nexport const onLostpointercapture = EVENT_PREFIX + 'lostpointercapture';\nexport const onMozfullscreenchange = EVENT_PREFIX + 'mozfullscreenchange';\nexport const onMozfullscreenerror = EVENT_PREFIX + 'mozfullscreenerror';\nexport const onAnimationcancel = EVENT_PREFIX + 'animationcancel';\nexport const onAnimationend = EVENT_PREFIX + 'animationend';\nexport const onAnimationiteration = EVENT_PREFIX + 'animationiteration';\nexport const onAnimationstart = EVENT_PREFIX + 'animationstart';\nexport const onTransitioncancel = EVENT_PREFIX + 'transitioncancel';\nexport const onTransitionend = EVENT_PREFIX + 'transitionend';\nexport const onTransitionrun = EVENT_PREFIX + 'transitionrun';\nexport const onTransitionstart = EVENT_PREFIX + 'transitionstart';\nexport const onWebkitanimationend = EVENT_PREFIX + 'webkitanimationend';\nexport const onWebkitanimationiteration = EVENT_PREFIX + 'webkitanimationiteration';\nexport const onWebkitanimationstart = EVENT_PREFIX + 'webkitanimationstart';\nexport const onWebkittransitionend = EVENT_PREFIX + 'webkittransitionend';\nexport const onError = EVENT_PREFIX + 'error';\n\nconst eventAttrs = new Signal.State([\n\tonAbort,\n\tonBlur,\n\tonFocus,\n\tonCancel,\n\tonAuxclick,\n\tonBeforeinput,\n\tonBeforetoggle,\n\tonCanplay,\n\tonCanplaythrough,\n\tonChange,\n\tonClick,\n\tonClose,\n\tonCommand,\n\tonContextmenu,\n\tonCopy,\n\tonCuechange,\n\tonCut,\n\tonDblclick,\n\tonDrag,\n\tonDragend,\n\tonDragenter,\n\tonDragexit,\n\tonDragleave,\n\tonDragover,\n\tonDragstart,\n\tonDrop,\n\tonDurationchange,\n\tonEmptied,\n\tonEnded,\n\tonFormdata,\n\tonInput,\n\tonInvalid,\n\tonKeydown,\n\tonKeypress,\n\tonKeyup,\n\tonLoad,\n\tonLoadeddata,\n\tonLoadedmetadata,\n\tonLoadstart,\n\tonMousedown,\n\tonMouseenter,\n\tonMouseleave,\n\tonMousemove,\n\tonMouseout,\n\tonMouseover,\n\tonMouseup,\n\tonWheel,\n\tonPaste,\n\tonPause,\n\tonPlay,\n\tonPlaying,\n\tonProgress,\n\tonRatechange,\n\tonReset,\n\tonResize,\n\tonScroll,\n\tonScrollend,\n\tonSecuritypolicyviolation,\n\tonSeeked,\n\tonSeeking,\n\tonSelect,\n\tonSlotchange,\n\tonStalled,\n\tonSubmit,\n\tonSuspend,\n\tonTimeupdate,\n\tonVolumechange,\n\tonWaiting,\n\tonSelectstart,\n\tonSelectionchange,\n\tonToggle,\n\tonPointercancel,\n\tonPointerdown,\n\tonPointerup,\n\tonPointermove,\n\tonPointerout,\n\tonPointerover,\n\tonPointerenter,\n\tonPointerleave,\n\tonGotpointercapture,\n\tonLostpointercapture,\n\tonMozfullscreenchange,\n\tonMozfullscreenerror,\n\tonAnimationcancel,\n\tonAnimationend,\n\tonAnimationiteration,\n\tonAnimationstart,\n\tonTransitioncancel,\n\tonTransitionend,\n\tonTransitionrun,\n\tonTransitionstart,\n\tonWebkitanimationend,\n\tonWebkitanimationiteration,\n\tonWebkitanimationstart,\n\tonWebkittransitionend,\n\tonError,\n]);\n\n\nconst attrToProp = attr => `on${attr[EVENT_PREFIX_LENGTH].toUpperCase()}${attr.substring(EVENT_PREFIX_LENGTH + 1)}`;\nconst selector = new Signal.Computed(() => eventAttrs.get().map(attr => `[${CSS.escape(attr)}]`).join(','));\n\nexport const eventToProp = event => EVENT_PREFIX + event;\n\nexport const hasEventAttribute = event => eventAttrs.get().includes(EVENT_PREFIX + event);\n\nconst isEventDataAttr = ([name]) => name.startsWith(DATA_PREFIX);\n\nfunction _addListeners(el, { signal, attrFilter = EVENTS } = {}) {\n\tconst dataset = el.dataset;\n\n\tfor (const [attr, val] of Object.entries(dataset).filter(isEventDataAttr)) {\n\t\ttry {\n\t\t\tconst event = 'on' + attr.substring(DATA_PREFIX_LENGTH);\n\n\t\t\tif (attrFilter.hasOwnProperty(event) && hasCallback(val)) {\n\t\t\t\tel.addEventListener(event.substring(2).toLowerCase(), getCallback(val), {\n\t\t\t\t\tpassive: dataset.hasOwnProperty('aegisEventPassive'),\n\t\t\t\t\tcapture: dataset.hasOwnProperty('aegisEventCapture'),\n\t\t\t\t\tonce: dataset.hasOwnProperty('aegisEventOnce'),\n\t\t\t\t\tsignal: dataset.hasOwnProperty('aegisEventSignal') ? getSignal(dataset.aegisEventSignal) : signal,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch(err) {\n\t\t\treportError(err);\n\t\t}\n\t}\n}\n\nconst observer = new MutationObserver(records => {\n\trecords.forEach(record => {\n\t\tswitch(record.type) {\n\t\t\tcase 'childList':\n\t\t\t\t[...record.addedNodes]\n\t\t\t\t\t.filter(node => node.nodeType === Node.ELEMENT_NODE)\n\t\t\t\t\t.forEach(node => attachListeners(node));\n\t\t\t\tbreak;\n\n\t\t\tcase 'attributes':\n\t\t\t\tif (typeof record.oldValue === 'string' && hasCallback(record.oldValue)) {\n\t\t\t\t\trecord.target.removeEventListener(\n\t\t\t\t\t\trecord.attributeName.substring(EVENT_PREFIX_LENGTH),\n\t\t\t\t\t\tgetCallback(record.oldValue), {\n\t\t\t\t\t\t\tonce: record.target.hasAttribute(once),\n\t\t\t\t\t\t\tcapture: record.target.hasAttribute(capture),\n\t\t\t\t\t\t\tpassive: record.target.hasAttribute(passive),\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\trecord.target.hasAttribute(record.attributeName)\n\t\t\t\t\t&& hasCallback(record.target.getAttribute(record.attributeName))\n\t\t\t\t) {\n\t\t\t\t\trecord.target.addEventListener(\n\t\t\t\t\t\trecord.attributeName.substring(EVENT_PREFIX_LENGTH),\n\t\t\t\t\t\tgetCallback(record.target.getAttribute(record.attributeName)), {\n\t\t\t\t\t\t\tonce: record.target.hasAttribute(once),\n\t\t\t\t\t\t\tcapture: record.target.hasAttribute(capture),\n\t\t\t\t\t\t\tpassive: record.target.hasAttribute(passive),\n\t\t\t\t\t\t\tsignal: record.target.hasAttribute(signal) ? getSignal(record.target.getAttribute(signal)) : undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t});\n});\n\nexport const EVENTS = {\n\tonAbort,\n\tonBlur,\n\tonFocus,\n\tonCancel,\n\tonAuxclick,\n\tonBeforeinput,\n\tonBeforetoggle,\n\tonCanplay,\n\tonCanplaythrough,\n\tonChange,\n\tonClick,\n\tonClose,\n\tonCommand,\n\tonContextmenu,\n\tonCopy,\n\tonCuechange,\n\tonCut,\n\tonDblclick,\n\tonDrag,\n\tonDragend,\n\tonDragenter,\n\tonDragexit,\n\tonDragleave,\n\tonDragover,\n\tonDragstart,\n\tonDrop,\n\tonDurationchange,\n\tonEmptied,\n\tonEnded,\n\tonFormdata,\n\tonInput,\n\tonInvalid,\n\tonKeydown,\n\tonKeypress,\n\tonKeyup,\n\tonLoad,\n\tonLoadeddata,\n\tonLoadedmetadata,\n\tonLoadstart,\n\tonMousedown,\n\tonMouseenter,\n\tonMouseleave,\n\tonMousemove,\n\tonMouseout,\n\tonMouseover,\n\tonMouseup,\n\tonWheel,\n\tonPaste,\n\tonPause,\n\tonPlay,\n\tonPlaying,\n\tonProgress,\n\tonRatechange,\n\tonReset,\n\tonResize,\n\tonScroll,\n\tonScrollend,\n\tonSecuritypolicyviolation,\n\tonSeeked,\n\tonSeeking,\n\tonSelect,\n\tonSlotchange,\n\tonStalled,\n\tonSubmit,\n\tonSuspend,\n\tonTimeupdate,\n\tonVolumechange,\n\tonWaiting,\n\tonSelectstart,\n\tonSelectionchange,\n\tonToggle,\n\tonPointercancel,\n\tonPointerdown,\n\tonPointerup,\n\tonPointermove,\n\tonPointerout,\n\tonPointerover,\n\tonPointerenter,\n\tonPointerleave,\n\tonGotpointercapture,\n\tonLostpointercapture,\n\tonMozfullscreenchange,\n\tonMozfullscreenerror,\n\tonAnimationcancel,\n\tonAnimationend,\n\tonAnimationiteration,\n\tonAnimationstart,\n\tonTransitioncancel,\n\tonTransitionend,\n\tonTransitionrun,\n\tonTransitionstart,\n\tonWebkitanimationend,\n\tonWebkitanimationiteration,\n\tonWebkitanimationstart,\n\tonWebkittransitionend,\n\tonError,\n\tonce,\n\tpassive,\n\tcapture,\n};\n\n/**\n * Register an attribute to observe for adding/removing event listeners\n *\n * @param {string} attr Name of the attribute to observe\n * @param {object} options\n * @param {boolean} [options.addListeners=false] Whether or not to automatically add listeners\n * @param {Document|Element} [options.base=document.body] Root node to observe\n * @param {AbortSignal} [options.signal] An abort signal to remove any listeners when aborted\n * @returns {string} The resulting `data-*` attribute name\n */\nexport function registerEventAttribute(attr, {\n\taddListeners = false,\n\tbase = document.body,\n\tsignal,\n} = {}) {\n\tconst fullAttr = EVENT_PREFIX + attr.toLowerCase();\n\tconst attrs = eventAttrs.get();\n\n\tif (! attrs.includes(fullAttr)) {\n\t\tconst sel = `[${CSS.escape(fullAttr)}]`;\n\t\tconst prop = attrToProp(fullAttr);\n\t\teventAttrs.set([...attrs, fullAttr]);\n\t\tEVENTS[prop] = fullAttr;\n\n\t\tif (addListeners) {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tconst config = { attrFilter: { [prop]: sel }, signal };\n\t\t\t\t[base, ...base.querySelectorAll(sel)].forEach(el => _addListeners(el, config));\n\t\t\t});\n\t\t}\n\t}\n\n\treturn fullAttr;\n}\n\n/**\n * Registers an `AbortController` in the controller registry and returns the key for it\n *\n * @param {AbortController} controller\n * @returns {string} The randomly generated key with which the controller is registered\n * @throws {TypeError} If controller is not an `AbortController`\n * @throws {Error} Any `reason` if controller is already aborted\n */\nexport function registerController(controller) {\n\tif (! (controller instanceof AbortController)) {\n\t\tthrow new TypeError('Controller is not an `AbortSignal.');\n\t} else if (controller.signal.aborted) {\n\t\tthrow controller.signal.reason;\n\t} else if (typeof controller.signal[controllerSymbol] === 'string') {\n\t\treturn controller.signal[controllerSymbol];\n\t} else {\n\t\tconst key = 'aegis:event:controller:' + crypto.randomUUID();\n\t\tObject.defineProperty(controller.signal, controllerSymbol, { value: key, writable: false, enumerable: false });\n\t\tcontrollerRegistry.set(key, controller);\n\n\t\tcontroller.signal.addEventListener('abort', unregisterController, { once: true });\n\n\t\treturn key;\n\t}\n}\n\n/**\n * Removes a controller from the registry\n *\n * @param {AbortController|AbortSignal|string} key The registed key or the controller or signal it corresponds to\n * @returns {boolean} Whether or not the controller was successfully unregistered\n */\nexport function unregisterController(key) {\n\tif (key instanceof AbortController) {\n\t\treturn controllerRegistry.delete(key.signal[controllerSymbol]);\n\t} else if (key instanceof AbortSignal) {\n\t\treturn controllerRegistry.delete(key[controllerSymbol]);\n\t} else {\n\t\treturn controllerRegistry.delete(key);\n\t}\n}\n\n/**\n * Creates and registers an `AbortController` in the controller registry and returns the key for it\n *\n * @param {object} options\n * @param {AbortSignal} [options.signal] An optional `AbortSignal` to externally abort the controller with\n * @returns {string} The randomly generated key with which the controller is registered\n */\nexport function createController({ signal } = {}) {\n\tconst controller = new AbortController();\n\n\tif (signal instanceof AbortSignal) {\n\t\tsignal.addEventListener('abort', ({ target }) => controller.abort(target.reason), { signal: controller.signal});\n\t}\n\n\treturn registerController(controller);\n}\n\n/**\n * Get a registetd controller from the registry\n *\n * @param {string} key Generated key with which the controller was registered\n * @returns {AbortController|void} Any registered controller, if any\n */\nexport const getController = key => controllerRegistry.get(key);\n\nexport function abortController(key, reason) {\n\tconst controller = getController(key);\n\n\tif (! (controller instanceof AbortController)) {\n\t\treturn false;\n\t} else if (typeof reason === 'string') {\n\t\tcontroller.abort(new Error(reason));\n\t\treturn true;\n\t} else {\n\t\tcontroller.abort(reason);\n\t\treturn true;\n\t}\n}\n\n/**\n * Register an `AbortSignal` to be used in declarative HTML as a value for `data-aegis-event-signal`\n *\n * @param {AbortSignal} signal The signal to register\n * @returns {string} The registered key\n * @throws {TypeError} Thrown if not an `AbortSignal`\n */\nexport function registerSignal(signal) {\n\tif (! (signal instanceof AbortSignal)) {\n\t\tthrow new TypeError('Signal must be an `AbortSignal`.');\n\t} else if (typeof signal[signalSymbol] === 'string') {\n\t\treturn signal[signalSymbol];\n\t} else {\n\t\tconst key = 'aegis:event:signal:' + crypto.randomUUID();\n\t\tObject.defineProperty(signal, signalSymbol, { value: key, writable: false, enumerable: false });\n\t\tsignalRegistry.set(key, signal);\n\t\tsignal.addEventListener('abort', ({ target }) => unregisterSignal(target[signalSymbol]), { once: true });\n\n\t\treturn key;\n\t}\n}\n\n/**\n * Gets and `AbortSignal` from the registry\n *\n * @param {string} key The registered key for the signal\n * @returns {AbortSignal|void} The corresponding `AbortSignal`, if any\n */\nexport const getSignal = key => signalRegistry.get(key);\n\n/**\n * Removes an `AbortSignal` from the registry\n *\n * @param {AbortSignal|string} signal An `AbortSignal` or the registered key for one\n * @returns {boolean} Whether or not the signal was sucessfully unregistered\n * @throws {TypeError} Throws if `signal` is not an `AbortSignal` or the key for a registered signal\n */\nexport function unregisterSignal(signal) {\n\tif (signal instanceof AbortSignal) {\n\t\treturn signalRegistry.delete(signal[signalSymbol]);\n\t} else if (typeof signal === 'string') {\n\t\treturn signalRegistry.delete(signal);\n\t} else {\n\t\tthrow new TypeError('Signal must be an `AbortSignal` or registered key/attribute.');\n\t}\n}\n\n/**\n * Add listeners to an element and its children, matching a generated query based on registered attributes\n *\n * @param {Element|Document} target Root node to add listeners from\n * @param {object} options\n * @param {AbortSignal} [options.signal] Optional signal to remove event listeners\n * @returns {Element|Document} Returns the passed target node\n */\nexport function attachListeners(target, { signal } = {}) {\n\tconst nodes = target instanceof Element && target.matches(selector.get())\n\t\t? [target, ...target.querySelectorAll(selector.get())]\n\t\t: target.querySelectorAll(selector.get());\n\n\tnodes.forEach(el => _addListeners(el, { signal }));\n\n\treturn target;\n}\n\n/**\n * Add a node to the `MutationObserver` to observe attributes and add/remove event listeners\n *\n * @param {Document|Element} root Element to observe attributes on\n */\nexport function observeEvents(root = document) {\n\tattachListeners(root);\n\n\tobserver.observe(root, {\n\t\tsubtree: true,\n\t\tchildList:true,\n\t\tattributes: true,\n\t\tattributeOldValue: true,\n\t\tattributeFilter: eventAttrs.get(),\n\t});\n}\n\n/**\n * Disconnects the `MutationObserver`, disabling observing of all attribute changes\n *\n * @returns {void}\n */\nexport const disconnectEventsObserver = () => observer.disconnect();\n\n/**\n * Register a global error handler callback\n *\n * @param {Function} callback Callback to register as a global error handler\n * @param {EventInit} config Typical event listener config object\n */\nexport function setGlobalErrorHandler(callback, { capture, once, passive, signal } = {}) {\n\tif (callback instanceof Function) {\n\t\tglobalThis.addEventListener('error', callback, { capture, once, passive, signal });\n\t} else {\n\t\tthrow new TypeError('Callback is not a function.');\n\t}\n}\n","// @ts-check\n/**\n * Internal slot for the callback to call to nnotify of changes\n * @type {unique symbol}\n */\nconst notify = Symbol('signal:watcher:notify');\n\n/**\n * @type {unique symbol}\n */\nconst currentComputed = Symbol('signal:currentComputed');\n\n/**\n * Callback called when isWatched becomes true, if it was previously false (`Signal.subtle.watched`)\n *\n * @type {unique symbol}\n */\nconst watched = Symbol('Signal:subtle:watched');\n\n/**\n * Callback called whenever isWatched becomes false, if it was previously true (`Signal.subtle.unwatched`)\n *\n * @type {unique symbol}\n */\nconst unwatched = Symbol('Signal:subtle:unwatched');\n\n/**\n * For equality checks in Computed, it must be a unique value\n * @type {unique symbol}\n */\nconst initial = Symbol('signal:initial');\n\n/**\n * Internal slot for determining calling `Signal.subtle.watched` and `Signal.subtle.unwatched` callbacks\n * @type {unique symbol}\n */\nconst isWatched = Symbol('Signal:isWatched');\n\n/**\n * Internal slot for State|Computed to store the `Signal.subtle.watched` callback\n * @type {unique symbol}\n */\nconst onWatch = Symbol('Signal:onWatch');\n\n/**\n * Internal slot for State|Computed to store the `Signal.subtle.unwatched` callback\n * @type {unique symbol}\n */\nconst onUnwatch = Symbol('Signal:onUnwatch');\n\n/**\n * @type {unique symbol}\n */\nconst sources = Symbol('Signal:sources');\n\n/**\n * @type {unique symbol}\n */\nconst sinks = Symbol('Signal:sinks');\n\n/**\n * @typedef {(t: any, t2: any) => boolean} EqualityCheck\n */\n\n/**\n * Custom comparison function between old and new value. Default: Object.is.\n * The signal is passed in as the this value for context.\n *\n * @type {EqualityCheck}\n */\nconst equals = Object.is;\n\n/**\n * @typedef {{\n * \tequals?: EqualityCheck,\n * \t[watched]?: (this: AnySignal<any>) => void,\n * [unwatched]?: (this: AnySignal<any>) => void,\n * }} SignalOptions\n */\n\n/**\n * @type {SignalOptions}\n */\nconst opts = Object.freeze({ equals });\n\n/**\n * @template T\n * @typedef {State<T> | Computed<T>} AnySignal<T>\n */\n\n/**\n * A read-write Signal\n * @template T\n */\nclass State {\n\t/**\n\t * @type {T}\n\t */\n\t#value;\n\n\t/**\n\t * @type {EqualityCheck}\n\t */\n\t#equals;\n\n\t/**\n\t * @type {VoidFunction|null}\n\t */\n\t[unwatched] = null;\n\n\t/**\n\t * @type {VoidFunction|null}\n\t */\n\t[onWatch] = null;\n\n\t/**\n\t * @type {VoidFunction|null}\n\t */\n\t[onUnwatch] = null;\n\n\t/**\n\t * @type {boolean}\n\t */\n\t[isWatched] = false;\n\n\t/**\n\t * @type {Set<Computed<any>|Watcher>}\n\t */\n\t[sinks] = new Set();\n\n\t/**\n\t * @type {Set<Set<any>|Computed<any>>}\n\t */\n\t[sources] = new Set();\n\n\t/**\n\t * Create a state Signal starting with the value T\n\t * @param {T} value - The initial value.\n\t * @param {SignalOptions} options\n\t */\n\tconstructor(value, options = opts) {\n\t\tif (typeof options !== 'object') {\n\t\t\tthrow new TypeError('Invalid options.');\n\t\t} else {\n\t\t\tthis.#equals = options.equals ?? equals;\n\t\t\tthis.#value = value;\n\n\t\t\tif (typeof options?.[watched] === 'function') {\n\t\t\t\tthis[onWatch] = options[watched].bind(this);\n\t\t\t}\n\n\t\t\tif (typeof options?.[unwatched] === 'function') {\n\t\t\t\tthis[onUnwatch] = options[unwatched].bind(this);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the value of the signal\n\t *\n\t * @returns {T}\n\t */\n\tget() {\n\t\tconst currentComputed = Signal.subtle.currentComputed();\n\n\t\tif (currentComputed instanceof Computed && ! currentComputed[sources].has(this)) {\n\t\t\tcurrentComputed[sources].add(this);\n\t\t\tthis[sinks].add(currentComputed);\n\t\t}\n\n\t\treturn this.#value;\n\t}\n\n\t/**\n\t * Set the state Signal value to T\n\t *\n\t * @param {T} newValue\n\t */\n\tset(newValue) {\n\t\tif (! this.#equals(this.#value, newValue)) {\n\t\t\tthis.#value = newValue;\n\n\t\t\tfor (const sink of Signal.subtle.introspectSinks(this)) {\n\t\t\t\tsink[notify](this);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * A Signal which is a formula based on other Signals\n *\n * @template T\n */\nclass Computed {\n\t/**\n\t * @type {EqualityCheck}\n\t */\n\t#equals;\n\n\t/**\n\t * @type {() => T}\n\t */\n\t#computation;\n\n\t/**\n\t * @type {boolean}\n\t */\n\t#dirty = true;\n\n\t/**\n\t * @type {T|initial}\n\t */\n\t#value = initial;\n\n\t/**\n\t * @type {VoidFunction|null}\n\t */\n\t[watched] = null;\n\n\t/**\n\t * @type {VoidFunction|null}\n\t */\n\t[unwatched] = null;\n\n\t/**\n\t * @type {VoidFunction|null}\n\t */\n\t[onWatch] = null;\n\n\t/**\n\t * @type {VoidFunction|null}\n\t */\n\t[onUnwatch] = null;\n\n\t/**\n\t * @type {boolean}\n\t */\n\t[isWatched] = false;\n\n\t/**\n\t * @type {Set<Computed<any>|Watcher>}\n\t */\n\t[sinks] = new Set();\n\n\t/**\n\t * @type {Set<State<any>|Computed<any>>}\n\t */\n\t[sources] = new Set();\n\n\t/**\n\t * Create a Signal which evaluates to the value returned by the callback.\n\t * Callback is called with this signal as the this value.\n\t *\n\t * @param {() => T} computation - The function to calculate the value.\n\t * @param {SignalOptions} [options]\n\t */\n\tconstructor(computation, options = opts) {\n\t\tif (typeof computation !== 'function') {\n\t\t\tthrow new TypeError('Computation must be a function.');\n\t\t} else if (typeof options !== 'object') {\n\t\t\tthrow new TypeError('Invalid options.');\n\t\t} else {\n\t\t\tthis.#equals = options.equals ?? equals;\n\t\t\tthis.#computation = computation;\n\n\t\t\tif (typeof options[watched] === 'function') {\n\t\t\t\tthis[onWatch] = options[watched].bind(this);\n\t\t\t}\n\n\t\t\tif (typeof options[unwatched] === 'function') {\n\t\t\t\tthis[onUnwatch] = options[unwatched].bind(this);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the value of the signal\n\t *\n\t * @this {Computed<T>}\n\t * @returns {T|initial}\n\t */\n\tget() {\n\t\tconst oldComputed = Signal.subtle.currentComputed();\n\n\t\ttry {\n\t\t\tif (oldComputed !== this && oldComputed !== null) {\n\t\t\t\toldComputed[sources].add(this);\n\t\t\t\tthis[sinks].add(oldComputed);\n\t\t\t}\n\n\t\t\tSignal[currentComputed] = this;\n\n\t\t\tif (this.#dirty) {\n\t\t\t\t// Must clear prior dependencies BEFORE computation\n\t\t\t\tfor (const source of Signal.subtle.introspectSources(this)) {\n\t\t\t\t\tsource[sinks].delete(this);\n\t\t\t\t}\n\n\t\t\t\tthis[sources].clear();\n\n\t\t\t\tconst val = this.#computation();\n\n\t\t\t\tif (! this.#equals(val, this.#value)) {\n\t\t\t\t\tthis.#value = val;\n\n\t\t\t\t\tfor (const sink of Signal.subtle.introspectSinks(this)) {\n\t\t\t\t\t\tsink[notify](this);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.#dirty = false;\n\t\t\t\treturn val;\n\t\t\t} else {\n\t\t\t\treturn this.#value;\n\t\t\t}\n\t\t} finally {\n\t\t\t// Restore previous context\n\t\t\tSignal[currentComputed] = oldComputed;\n\t\t}\n\t}\n\n\t/**\n\t * Notifiies a `Signal.Computed` when a source has changed\n\t *\n\t * @param {State<any>|Computed<any>} source\n\t */\n\t[notify](source) {\n\t\tthis.#dirty = true;\n\t\tthis[sources].add(source);\n\t\tsource[sinks].add(this);\n\n\t\tfor (const sink of Signal.subtle.introspectSinks(this)) {\n\t\t\tsink[notify](this);\n\t\t}\n\t}\n}\n\n/**\n * Watches for changes to specific signals.\n * @memberof Signal.subtle\n */\nclass Watcher {\n\t/**\n\t * @type {boolean}\n\t */\n\t#isWatched = false;\n\n\t/**\n\t * @type {Set<AnySignal<any>>}\n\t */\n\t#pending = new Set();\n\n\t/**\n\t * @type {(this: Watcher) => void}\n\t */\n\t#notify;\n\n\t/**\n\t * @type {Set<AnySignal<any>>}\n\t */\n\t[sources] = new Set();\n\n\n\t/**\n\t * When a (recursive) source of Watcher is written to, call this callback,\n\t * if it hasn't already been called since the last `watch` call.\n\t * No signals may be read or written during the notify.\n\t *\n\t * @param {(this: Watcher) => void} notify - Called synchronously when a watched signal becomes dirty.\n\t */\n\tconstructor(notify) {\n\t\tif (typeof notify !== 'function') {\n\t\t\tthrow new TypeError(`Notify must be a function but got a ${typeof notify}.`);\n\t\t} else {\n\t\t\tthis.#notify = notify;\n\t\t}\n\t}\n\n\t/**\n\t * Add these signals to the Watcher's set, and set the watcher to run its\n\t * notify callback next time any signal in the set (or one of its dependencies) changes.\n\t * Can be called with no arguments just to reset the \"notified\" state, so that\n\t * the notify callback will be invoked again.\n\t *\n\t * @param {...AnySignal<any>} signals\n\t */\n\twatch(...signals) {\n\t\tthis.#isWatched = true;\n\n\t\tfor (const signal of signals) {\n\t\t\tif (! (signal instanceof State || signal instanceof Computed)) {\n\t\t\t\tthrow new TypeError('Signal must be an instance of `Signal.State` or `Signal.Computed`.');\n\t\t\t} else if (! this[sources].has(signal)) {\n\t\t\t\tif (typeof signal[onWatch] === 'function' && ! signal[isWatched]) {\n\t\t\t\t\tsignal[isWatched] = true;\n\t\t\t\t\tsignal[onWatch].call(signal);\n\t\t\t\t}\n\n\t\t\t\tthis[sources].add(signal);\n\t\t\t\tsignal[sinks].add(this);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Remove these signals from the watched set (e.g., for an effect which is disposed)\n\t * @template T\n\t * @param {...AnySignal<T>} signals\n\t */\n\tunwatch(...signals) {\n\t\tfor (const signal of signals) {\n\t\t\tif (! (signal instanceof State || signal instanceof Computed)) {\n\t\t\t\tthrow new TypeError('Signal must be an instance of `Signal.State` or `Signal.Computed`.');\n\t\t\t} else {\n\t\t\t\tif (typeof signal[onUnwatch] === 'function' && signal[isWatched]) {\n\t\t\t\t\tconst watchers = Signal.subtle.introspectSinks(signal).filter(sink => sink instanceof Signal.subtle.Watcher);\n\n\t\t\t\t\tif (watchers.length === 1) {\n\t\t\t\t\t\tsignal[isWatched] = false;\n\t\t\t\t\t\tsignal[onUnwatch].call(signal);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tsignal[sinks].delete(this);\n\t\t\t\tthis[sources].delete(signal);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns the set of sources in the Watcher's set which are still dirty, or is a computed signal\n\t * with a source which is dirty or pending and hasn't yet been re-evaluated\n\t *\n\t * @returns {Array<AnySignal<any>>}\n\t */\n\tgetPending() {\n\t\tconst pending = Array.from(this.#pending);\n\t\tthis.#pending.clear();\n\t\treturn pending;\n\t}\n\n\t/**\n\t * Notify a `Signal.subtle.Watcher` when a source has changed\n\t *\n\t * @template T\n\t * @param {AnySignal<T>} signal\n\t */\n\t[notify](signal) {\n\t\tthis.#pending.add(signal);\n\t\tif (this.#isWatched) {\n\t\t\tthis.#notify.call(this);\n\t\t}\n\t}\n}\n\n/**\n * This namespace includes \"advanced\" features that are better to\n * leave for framework authors rather than application developers.\n * Analogous to `crypto.subtle`\n *\n * @namespace subtle\n */\nconst subtle = {\n\tWatcher,\n\t/**\n\t * Hook to observe being watched\n\t * @type {unique symbol}\n\t */\n\twatched,\n\n\t/**\n\t * Hook to observe no longer watched\n\t * @type {unique s}\n\t */\n\tunwatched,\n\n\t/**\n\t * Run a callback with all tracking disabled\n\t *\n\t * @template T\n\t * @param {() => T} cb\n\t * @returns {T}\n\t */\n\tuntrack(cb) {\n\t\tif (typeof cb !== 'function') {\n\t\t\tthrow new TypeError('Callback must be a function.');\n\t\t} else {\n\t\t\tconst prev = Signal.subtle.currentComputed();\n\t\t\tSignal[currentComputed] = null;\n\n\t\t\ttry {\n\t\t\t\treturn cb();\n\t\t\t} finally {\n\t\t\t\tSignal[currentComputed] = prev;\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Get the current computed signal which is tracking any signal reads, if any\n\t *\n\t * @returns {Computed<any>|null}\n\t */\n\tcurrentComputed() {\n\t\treturn Signal[currentComputed];\n\t},\n\n\t/**\n\t * Returns ordered list of all signals which this one referenced\n\t * during the last time it was evaluated.\n\t * For a Watcher, lists the set of signals which it is watching.\n\t *\n\t * @param {Computed<any>|Watcher} s\n\t * @returns {(State<any>|Computed<any>)[]}\n\t */\n\tintrospectSources(s) {\n\t\tif (! (s instanceof Computed || s instanceof Watcher)) {\n\t\t\tthrow new TypeError('Expected a `Signal.Watcher` or `Signal.Computed`.');\n\t\t} else {\n\t\t\treturn Array.from(s[sources]);\n\t\t}\n\t},\n\n\t/**\n\t * Returns the Watchers that this signal is contained in, plus any\n\t * Computed signals which read this signal last time they were evaluated,\n\t * if that computed signal is (recursively) watched.\n\t *\n\t * @param {State<any>|Computed<any>} s\n\t * @returns {(Computed<any>|Watcher)[]}\n\t */\n\tintrospectSinks(s) {\n\t\tif (! (s instanceof State || s instanceof Computed)) {\n\t\t\tthrow new TypeError('Expected a `Signal.State` or `Signal.Computed`.');\n\t\t} else {\n\t\t\treturn Array.from(s[sinks]);\n\t\t}\n\t},\n\n\t/**\n\t * True if this signal is \"live\", in that it is watched by a Watcher,\n\t * or it is read by a Computed signal which is (recursively) live.\n\t *\n\t * @param {State<any>|Computed<any>} s\n\t * @return {boolean}\n\t */\n\thasSinks(s) {\n\t\tif (! (s instanceof State || s instanceof Computed)) {\n\t\t\tthrow new TypeError('Expected a `Signal.State` or `Signal.Computed`.');\n\t\t} else {\n\t\t\treturn s[sinks].size !== 0;\n\t\t}\n\t},\n\n\t/**\n\t * True if this element is \"reactive\", in that it depends\n\t * on some other signal. A Computed where hasSources is false\n\t * will always return the same constant.\n\t *\n\t * @param {Computed<any>|Watcher} s\n\t * @returns {boolean}\n\t */\n\thasSources(s) {\n\t\tif (! (s instanceof Computed || s instanceof Watcher)) {\n\t\t\tthrow new TypeError('Expected a `Signal.Watcher` or `Signal.Computed`.');\n\t\t} else {\n\t\t\treturn s[sources].size !== 0;\n\t\t}\n\t},\n};\n\n/**\n * The core Signals namespace.\n * @namespace Signal\n */\nexport const Signal = {\n\tState,\n\tComputed,\n\tsubtle,\n\n\t/**\n\t * @type {Computed<any>|null}\n\t */\n\t[currentComputed]: null,\n};\n\nexport { Signal as SignalShim };\n","import { getStateObj, diffState, notifyStateChange } from '@aegisjsproject/state';\nexport { url } from '@aegisjsproject/url/url.js';\nimport { onClick, onSubmit } from '@aegisjsproject/callback-registry/events.js';\n\nconst isModule = ! (document.currentScript instanceof HTMLScriptElement);\nconst SUPPORTS_IMPORTMAP = HTMLScriptElement.supports