@spearwolf/shadow-objects 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +320 -61
- package/README.md +57 -13
- package/bundle.js +5 -54
- package/package.json +7 -3
- package/src/ChangeTrailRefusedError.d.ts +27 -0
- package/src/ChangeTrailRefusedError.d.ts.map +1 -0
- package/src/ChangeTrailRefusedError.js +12 -0
- package/src/ChangeTrailRefusedError.js.map +7 -0
- package/src/EntityUuidInUseError.d.ts +19 -0
- package/src/EntityUuidInUseError.d.ts.map +1 -0
- package/src/EntityUuidInUseError.js +11 -0
- package/src/EntityUuidInUseError.js.map +7 -0
- package/src/WorkerTimeoutError.d.ts +25 -0
- package/src/WorkerTimeoutError.d.ts.map +1 -0
- package/src/WorkerTimeoutError.js +12 -0
- package/src/WorkerTimeoutError.js.map +7 -0
- package/src/bundle.d.ts +1 -1
- package/src/bundle.d.ts.map +1 -1
- package/src/bundle.js +1 -1
- package/src/bundle.js.map +2 -2
- package/src/constants.d.ts +9 -2
- package/src/constants.d.ts.map +1 -1
- package/src/constants.js +1 -1
- package/src/constants.js.map +2 -2
- package/src/create-worker.bundle.d.ts +1 -1
- package/src/create-worker.bundle.d.ts.map +1 -1
- package/src/create-worker.bundle.js.map +1 -1
- package/src/create-worker.d.ts +1 -1
- package/src/create-worker.d.ts.map +1 -1
- package/src/create-worker.js.map +1 -1
- package/src/elements/ShaeElement.d.ts +70 -2
- package/src/elements/ShaeElement.d.ts.map +1 -1
- package/src/elements/ShaeElement.js +129 -25
- package/src/elements/ShaeElement.js.map +3 -3
- package/src/elements/ShaeEntElement.d.ts +39 -1
- package/src/elements/ShaeEntElement.d.ts.map +1 -1
- package/src/elements/ShaeEntElement.js +397 -167
- package/src/elements/ShaeEntElement.js.map +2 -2
- package/src/elements/ShaeLifecycleElement.d.ts +71 -0
- package/src/elements/ShaeLifecycleElement.d.ts.map +1 -0
- package/src/elements/ShaeLifecycleElement.js +83 -0
- package/src/elements/ShaeLifecycleElement.js.map +7 -0
- package/src/elements/ShaePropElement.d.ts +52 -2
- package/src/elements/ShaePropElement.d.ts.map +1 -1
- package/src/elements/ShaePropElement.js +240 -219
- package/src/elements/ShaePropElement.js.map +2 -2
- package/src/elements/ShaeWorkerElement.d.ts +49 -5
- package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
- package/src/elements/ShaeWorkerElement.js +204 -73
- package/src/elements/ShaeWorkerElement.js.map +2 -2
- package/src/elements/constants.d.ts +6 -0
- package/src/elements/constants.d.ts.map +1 -1
- package/src/elements/constants.js +12 -0
- package/src/elements/constants.js.map +2 -2
- package/src/elements/deferredTeardown.d.ts +24 -0
- package/src/elements/deferredTeardown.d.ts.map +1 -0
- package/src/elements/deferredTeardown.js +30 -0
- package/src/elements/deferredTeardown.js.map +7 -0
- package/src/elements/displayContentsRule.d.ts +25 -0
- package/src/elements/displayContentsRule.d.ts.map +1 -0
- package/src/elements/displayContentsRule.js +37 -0
- package/src/elements/displayContentsRule.js.map +7 -0
- package/src/elements/events.d.ts +10 -7
- package/src/elements/events.d.ts.map +1 -1
- package/src/elements/forwardCustomEvents.d.ts +19 -0
- package/src/elements/forwardCustomEvents.d.ts.map +1 -0
- package/src/elements/forwardCustomEvents.js +47 -0
- package/src/elements/forwardCustomEvents.js.map +7 -0
- package/src/elements/hostedSlots.d.ts +36 -0
- package/src/elements/hostedSlots.d.ts.map +1 -0
- package/src/elements/hostedSlots.js +118 -0
- package/src/elements/hostedSlots.js.map +7 -0
- package/src/elements/parentRemoval.d.ts +18 -0
- package/src/elements/parentRemoval.d.ts.map +1 -0
- package/src/elements/parentRemoval.js +60 -0
- package/src/elements/parentRemoval.js.map +7 -0
- package/src/elements/propValueConverters.d.ts +3 -0
- package/src/elements/propValueConverters.d.ts.map +1 -0
- package/src/elements/propValueConverters.js +54 -0
- package/src/elements/propValueConverters.js.map +7 -0
- package/src/elements/requestEntAncestor.d.ts +32 -0
- package/src/elements/requestEntAncestor.d.ts.map +1 -0
- package/src/elements/requestEntAncestor.js +16 -0
- package/src/elements/requestEntAncestor.js.map +7 -0
- package/src/elements.js.map +1 -1
- package/src/in-the-dark/Entity.d.ts +113 -3
- package/src/in-the-dark/Entity.d.ts.map +1 -1
- package/src/in-the-dark/Entity.js +292 -63
- package/src/in-the-dark/Entity.js.map +3 -3
- package/src/in-the-dark/Kernel.d.ts +76 -17
- package/src/in-the-dark/Kernel.d.ts.map +1 -1
- package/src/in-the-dark/Kernel.js +410 -378
- package/src/in-the-dark/Kernel.js.map +3 -3
- package/src/in-the-dark/Registry.d.ts.map +1 -1
- package/src/in-the-dark/Registry.js +83 -14
- package/src/in-the-dark/Registry.js.map +2 -2
- package/src/in-the-dark/ShadowObject.d.ts.map +1 -1
- package/src/in-the-dark/ShadowObject.js +1 -0
- package/src/in-the-dark/ShadowObject.js.map +2 -2
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts +126 -0
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts.map +1 -0
- package/src/in-the-dark/ShadowObjectCreationScope.js +596 -0
- package/src/in-the-dark/ShadowObjectCreationScope.js.map +7 -0
- package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
- package/src/in-the-dark/SignalsPath.js +2 -2
- package/src/in-the-dark/SignalsPath.js.map +2 -2
- package/src/in-the-dark/events.js.map +1 -1
- package/src/in-the-dark/importModule.d.ts +7 -0
- package/src/in-the-dark/importModule.d.ts.map +1 -1
- package/src/in-the-dark/importModule.js +5 -2
- package/src/in-the-dark/importModule.js.map +2 -2
- package/src/index.d.ts +7 -1
- package/src/index.d.ts.map +1 -1
- package/src/index.js +5 -1
- package/src/index.js.map +2 -2
- package/src/shadow-objects.d.ts +2 -0
- package/src/shadow-objects.d.ts.map +1 -1
- package/src/shadow-objects.js +2 -0
- package/src/shadow-objects.js.map +2 -2
- package/src/shadow-objects.worker.js +0 -1
- package/src/shadow-objects.worker.js.map +2 -2
- package/src/shae-ent.js.map +1 -1
- package/src/shae-prop.js +2 -2
- package/src/shae-prop.js.map +2 -2
- package/src/shae-worker.js.map +1 -1
- package/src/types.d.ts +35 -4
- package/src/types.d.ts.map +1 -1
- package/src/utils/ConsoleLogger.d.ts +45 -10
- package/src/utils/ConsoleLogger.d.ts.map +1 -1
- package/src/utils/ConsoleLogger.js +51 -26
- package/src/utils/ConsoleLogger.js.map +2 -2
- package/src/utils/FrameLoop.d.ts +42 -3
- package/src/utils/FrameLoop.d.ts.map +1 -1
- package/src/utils/FrameLoop.js +71 -10
- package/src/utils/FrameLoop.js.map +2 -2
- package/src/utils/MicrotaskCollector.d.ts +45 -0
- package/src/utils/MicrotaskCollector.d.ts.map +1 -0
- package/src/utils/MicrotaskCollector.js +33 -0
- package/src/utils/MicrotaskCollector.js.map +7 -0
- package/src/utils/MicrotaskGate.d.ts +17 -0
- package/src/utils/MicrotaskGate.d.ts.map +1 -0
- package/src/utils/MicrotaskGate.js +20 -0
- package/src/utils/MicrotaskGate.js.map +7 -0
- package/src/utils/array-utils.js.map +1 -1
- package/src/utils/attr-utils.d.ts +6 -0
- package/src/utils/attr-utils.d.ts.map +1 -1
- package/src/utils/attr-utils.js +3 -1
- package/src/utils/attr-utils.js.map +2 -2
- package/src/utils/constants.js.map +1 -1
- package/src/utils/generateUUID.d.ts +11 -0
- package/src/utils/generateUUID.d.ts.map +1 -1
- package/src/utils/generateUUID.js +31 -266
- package/src/utils/generateUUID.js.map +2 -2
- package/src/utils/props-utils.d.ts +3 -2
- package/src/utils/props-utils.d.ts.map +1 -1
- package/src/utils/props-utils.js +13 -24
- package/src/utils/props-utils.js.map +2 -2
- package/src/utils/runGuarded.d.ts +27 -0
- package/src/utils/runGuarded.d.ts.map +1 -0
- package/src/utils/runGuarded.js +11 -0
- package/src/utils/runGuarded.js.map +7 -0
- package/src/utils/toMaybe.d.ts.map +1 -1
- package/src/utils/toMaybe.js.map +1 -1
- package/src/utils/toNamespace.d.ts.map +1 -1
- package/src/utils/toNamespace.js.map +1 -1
- package/src/utils/toUrlString.d.ts +12 -0
- package/src/utils/toUrlString.d.ts.map +1 -1
- package/src/utils/toUrlString.js.map +2 -2
- package/src/utils/waitForMessageOfType.d.ts +12 -1
- package/src/utils/waitForMessageOfType.d.ts.map +1 -1
- package/src/utils/waitForMessageOfType.js +16 -1
- package/src/utils/waitForMessageOfType.js.map +2 -2
- package/src/view/ComponentChanges.d.ts +88 -1
- package/src/view/ComponentChanges.d.ts.map +1 -1
- package/src/view/ComponentChanges.js +243 -73
- package/src/view/ComponentChanges.js.map +2 -2
- package/src/view/ComponentContext.d.ts +206 -16
- package/src/view/ComponentContext.d.ts.map +1 -1
- package/src/view/ComponentContext.js +548 -91
- package/src/view/ComponentContext.js.map +2 -2
- package/src/view/ComponentMemory.d.ts +3 -3
- package/src/view/ComponentMemory.d.ts.map +1 -1
- package/src/view/ComponentMemory.js.map +1 -1
- package/src/view/IShadowObjectEnvProxy.d.ts +29 -3
- package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.js +8 -6
- package/src/view/LocalShadowObjectEnv.js.map +2 -2
- package/src/view/RemoteWorkerEnv.d.ts +89 -5
- package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
- package/src/view/RemoteWorkerEnv.js +195 -77
- package/src/view/RemoteWorkerEnv.js.map +2 -2
- package/src/view/ShadowEnv.d.ts +49 -2
- package/src/view/ShadowEnv.d.ts.map +1 -1
- package/src/view/ShadowEnv.js +223 -60
- package/src/view/ShadowEnv.js.map +2 -2
- package/src/view/ViewComponent.d.ts +78 -14
- package/src/view/ViewComponent.d.ts.map +1 -1
- package/src/view/ViewComponent.js +90 -14
- package/src/view/ViewComponent.js.map +2 -2
- package/src/view/cloneChangeTrail.js.map +2 -2
- package/src/worker/MessageRouter.d.ts +14 -0
- package/src/worker/MessageRouter.d.ts.map +1 -1
- package/src/worker/MessageRouter.js +75 -15
- package/src/worker/MessageRouter.js.map +2 -2
- package/src/worker/WorkerRuntime.d.ts +22 -1
- package/src/worker/WorkerRuntime.d.ts.map +1 -1
- package/src/worker/WorkerRuntime.js +60 -8
- package/src/worker/WorkerRuntime.js.map +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/elements/ShaeEntElement.ts"],
|
|
4
|
-
"sourcesContent": ["import {on} from '@spearwolf/eventize';\nimport {beQuiet, createEffect, createSignal} from '@spearwolf/signalize';\nimport {VoidToken} from '../constants.js';\nimport {ComponentContext} from '../view/ComponentContext.js';\nimport {ShadowEnv} from '../view/ShadowEnv.js';\nimport {ViewComponent} from '../view/ViewComponent.js';\nimport {ATTR_FORWARD_CUSTOM_EVENTS, ATTR_TOKEN, RequestEntParentEventName, ReRequestEntParentEventName} from './constants.js';\nimport {ShaeElement} from './ShaeElement.js';\n\nexport class ShaeEntElement extends ShaeElement {\n static override observedAttributes = [...ShaeElement.observedAttributes, ATTR_TOKEN, ATTR_FORWARD_CUSTOM_EVENTS];\n\n readonly isShaeEntElement = true;\n\n readonly componentContext$ = createSignal<ComponentContext | undefined>();\n readonly viewComponent$ = createSignal<ViewComponent | undefined>();\n readonly token$ = createSignal<string | undefined>();\n readonly forwardCustomEvents$ = createSignal<Set<string> | boolean>(false);\n\n get componentContext(): ComponentContext | undefined {\n return this.componentContext$.value;\n }\n\n get viewComponent(): ViewComponent | undefined {\n return this.viewComponent$.value;\n }\n\n get uuid(): string | undefined {\n return this.viewComponent?.uuid;\n }\n\n get token(): string | undefined {\n return this.token$.value;\n }\n\n set token(token: string | undefined) {\n this.token$.set(token);\n }\n\n entParentNode?: ShaeEntElement;\n\n #parentObserver?: MutationObserver;\n\n constructor() {\n super();\n\n this.ns$.onChange((ns) => {\n this.componentContext$.set(ComponentContext.get(ns));\n if (this.isConnected) {\n this.#dispatchRequestParent();\n }\n });\n\n this.#updateTokenValue();\n\n this.token$.onChange((token) => {\n if (token == null) {\n this.removeAttribute(ATTR_TOKEN);\n } else if (this.getAttribute(ATTR_TOKEN) !== token) {\n this.setAttribute(ATTR_TOKEN, token);\n }\n });\n\n this.#updateForwardCustomEventsValue();\n\n this.forwardCustomEvents$.onChange((val) => {\n if (!val) {\n this.removeAttribute(ATTR_FORWARD_CUSTOM_EVENTS);\n } else if (val === true) {\n if (!this.hasAttribute(ATTR_FORWARD_CUSTOM_EVENTS)) {\n this.setAttribute(ATTR_FORWARD_CUSTOM_EVENTS, '');\n }\n } else {\n const str = Array.from(val).join(',');\n if (this.getAttribute(ATTR_FORWARD_CUSTOM_EVENTS) !== str) {\n this.setAttribute(ATTR_FORWARD_CUSTOM_EVENTS, str);\n }\n }\n });\n\n createEffect(() => {\n const vc = this.viewComponent$.get();\n if (vc) {\n const unsubcribe = on(vc, ComponentContext.ReRequestParentRoots, () => this.#reReuestParentRoot());\n const oldNs = vc.context?.ns;\n return () => {\n unsubcribe();\n vc.destroy();\n if (oldNs && oldNs !== this.ns) {\n ShadowEnv.get(oldNs)?.sync();\n } else {\n this.syncShadowObjects();\n }\n };\n }\n });\n\n createEffect(() => {\n const vc = this.viewComponent$.get();\n if (!vc) return;\n\n // Make sure we are patching the instance method, not the prototype\n const originalDispatchEvent = Object.hasOwn(vc, 'dispatchEvent')\n ? Object.getPrototypeOf(vc).dispatchEvent\n : vc.dispatchEvent;\n\n const filter = this.forwardCustomEvents$.get();\n if (!filter) return;\n\n const allowedTypes = filter instanceof Set ? filter : undefined;\n\n const newDispatch = (type: string, data: unknown, traverseChildren: boolean) => {\n originalDispatchEvent.call(vc, type, data, traverseChildren);\n\n if (type === ComponentContext.ReRequestParentRoots) return;\n\n if (allowedTypes && !allowedTypes.has(type)) return;\n\n this.dispatchEvent(\n new CustomEvent(type, {\n bubbles: true,\n composed: true,\n detail: data,\n }),\n );\n };\n\n // We use defineProperty to ensure it is writable and configurable\n Object.defineProperty(vc, 'dispatchEvent', {\n value: newDispatch,\n writable: true,\n configurable: true,\n });\n\n return () => {\n if (Object.hasOwn(vc, 'dispatchEvent') && vc.dispatchEvent === newDispatch) {\n // the property lives on the instance (defineProperty above), the method it shadows lives on\n // the prototype: deleting the own property restores the original, it does not remove the method\n delete (vc as {dispatchEvent?: ViewComponent['dispatchEvent']}).dispatchEvent;\n }\n };\n });\n\n this.token$.onChange((token) => {\n const vc = this.viewComponent$.value;\n if (vc) {\n vc.token = token;\n this.syncShadowObjects();\n }\n });\n\n this.style.display = 'contents';\n }\n\n #unsubscribeViewComponentEffect?: () => void;\n\n #setupViewComponentEffect() {\n this.#unsubscribeViewComponentEffect?.();\n\n const unsubscribeComponentContext = this.componentContext$.onChange((context) => {\n const token = this.token$.value;\n\n let vc = this.viewComponent$.value;\n\n if (vc) {\n vc.context = context;\n } else if (context) {\n vc = new ViewComponent(token ?? VoidToken, {context});\n this.viewComponent$.set(vc);\n }\n\n this.syncShadowObjects();\n });\n\n this.#unsubscribeViewComponentEffect = () => {\n unsubscribeComponentContext();\n };\n }\n\n #destroyViewComponentEffect() {\n this.#unsubscribeViewComponentEffect?.();\n this.#unsubscribeViewComponentEffect = undefined;\n }\n\n #shadowRootHost?: HTMLElement;\n #shadowRootHostNeedsUpdate = true;\n\n findShadowRootHost(): HTMLElement | undefined {\n if (this.#shadowRootHostNeedsUpdate) {\n this.#shadowRootHostNeedsUpdate = false;\n\n let current: HTMLElement = this;\n while (current) {\n if (current.parentElement == null) {\n const root = current.parentNode as ShadowRoot;\n if (root) {\n this.#shadowRootHost = root.host as HTMLElement;\n }\n break;\n }\n current = current.parentElement;\n }\n }\n return this.#shadowRootHost;\n }\n\n protected getParentNodeForObserver(): Node | undefined {\n // a node still sitting in a tree answers from the first term, and for one placed directly\n // under a shadow root that term is the shadow root itself. Once it has no parent left it is\n // its own root, so the host lookup comes up empty too and the answer is: no parent\n return this.parentNode ?? (this.getRootNode() as ShadowRoot)?.host ?? undefined;\n }\n\n connectedCallback() {\n this.#shadowRootHostNeedsUpdate = true;\n\n this.addEventListener('slotchange', this.#onSlotChange, {capture: false, passive: false});\n this.addEventListener(RequestEntParentEventName, this.#onRequestParent, {capture: false, passive: false});\n\n this.#setupViewComponentEffect();\n\n // --- token ---\n beQuiet(() => this.#updateTokenValue());\n\n // --- forward-custom-events ---\n beQuiet(() => this.#updateForwardCustomEventsValue());\n\n // --- componentContext | viewComponent ---\n if (this.componentContext == null) {\n this.componentContext$.set(ComponentContext.get(this.ns));\n }\n\n // --- viewComponent.parent ---\n this.#dispatchRequestParent();\n\n // --- parents ---\n this.componentContext?.dispatchReRequestParentRoots();\n this.#createParentObserver();\n\n // --- sync! ---\n this.syncShadowObjects();\n }\n\n #createParentObserver() {\n this.#destroyParentObserver();\n const parent = this.getParentNodeForObserver();\n if (parent) {\n this.#parentObserver = new MutationObserver((mutations, _observer) => {\n for (const {target, removedNodes} of mutations) {\n if (target === parent) {\n for (const node of removedNodes) {\n if (node === this) {\n this.#destroyParentObserver();\n this.onParentChanged(this.getParentNodeForObserver(), parent);\n break;\n }\n }\n }\n }\n });\n this.#parentObserver.observe(parent, {childList: true, subtree: false, attributes: false});\n }\n }\n\n onParentChanged(_newParent: Node | undefined, _oldParent: Node) {\n this.#setParent(undefined);\n this.#dispatchRequestParent();\n }\n\n #destroyParentObserver() {\n this.#parentObserver?.disconnect();\n this.#parentObserver = undefined;\n }\n\n override attributeChangedCallback(name: string) {\n super.attributeChangedCallback(name);\n if (name === ATTR_TOKEN) {\n this.#updateTokenValue();\n } else if (name === ATTR_FORWARD_CUSTOM_EVENTS) {\n this.#updateForwardCustomEventsValue();\n }\n }\n\n disconnectedCallback() {\n this.#shadowRootHostNeedsUpdate = true;\n\n this.#destroyParentObserver();\n\n this.removeEventListener('slotchange', this.#onSlotChange, {capture: false});\n this.removeEventListener(RequestEntParentEventName, this.#onRequestParent, {capture: false});\n\n this.#setParent(undefined);\n\n this.componentContext$.set(undefined);\n\n this.syncShadowObjects();\n\n this.#destroyViewComponentEffect();\n }\n\n #reReuestParentRoot() {\n if (this.isConnected) {\n this.#setParent(undefined);\n this.#dispatchRequestParent();\n }\n }\n\n #dispatchRequestParent() {\n // https://pm.dartus.fr/blog/a-complete-guide-on-shadow-dom-and-event-propagation/\n this.dispatchEvent(\n new CustomEvent(RequestEntParentEventName, {\n bubbles: true,\n composed: true,\n detail: {requester: this},\n }),\n );\n }\n\n #unsubscribeFromParent?: () => void;\n\n #setParent(parent?: ShaeEntElement) {\n if (this.entParentNode === parent) return;\n\n if (this.entParentNode) {\n this.entParentNode.removeEventListener(ReRequestEntParentEventName, this.#onReRequestParent, {capture: false});\n }\n\n this.entParentNode = parent;\n\n if (this.entParentNode) {\n this.entParentNode.addEventListener(ReRequestEntParentEventName, this.#onReRequestParent, {\n capture: false,\n passive: false,\n });\n }\n\n this.#unsubscribeFromParent?.();\n this.#unsubscribeFromParent = undefined;\n\n if (parent) {\n const e = createEffect(() => {\n const vc = this.viewComponent$.get();\n if (vc) {\n const parentVC = parent.viewComponent$.get();\n vc.parent = parentVC && parentVC.context === vc.context ? parentVC : undefined;\n if (vc.parent == null) {\n queueMicrotask(() => {\n this.#dispatchRequestParent();\n });\n }\n this.syncShadowObjects();\n }\n });\n this.#unsubscribeFromParent = () => e.destroy();\n } else {\n const vc = this.viewComponent;\n if (vc?.parent) {\n vc.parent = undefined;\n this.syncShadowObjects();\n }\n }\n }\n\n #onSlotChange = () => {\n const shadowRootHost = this.findShadowRootHost();\n if (shadowRootHost == null) return;\n this.dispatchEvent(\n new CustomEvent(ReRequestEntParentEventName, {\n bubbles: true,\n composed: true,\n detail: {requester: this, shadowRootHost},\n }),\n );\n };\n\n #onReRequestParent = (event: CustomEvent) => {\n const requester = event.detail?.requester as ShaeEntElement | undefined;\n\n if (requester === this) return;\n if (!requester?.isShaeEntElement) return;\n if (requester.ns !== this.ns) return;\n\n const shadowRootHost = event.detail?.shadowRootHost as HTMLElement | undefined;\n\n if (shadowRootHost) {\n this.#dispatchRequestParent();\n }\n };\n\n #onRequestParent = (event: CustomEvent) => {\n const requester = event.detail?.requester as ShaeEntElement | undefined;\n\n if (requester === this) return;\n if (!requester?.isShaeEntElement) return;\n if (requester.ns !== this.ns) return;\n\n event.stopPropagation();\n\n requester.#setParent(this);\n };\n\n #updateTokenValue() {\n if (this.hasAttribute(ATTR_TOKEN)) {\n const token = this.getAttribute(ATTR_TOKEN)?.trim() || undefined;\n this.token$.set(token);\n }\n }\n\n #updateForwardCustomEventsValue() {\n if (this.hasAttribute(ATTR_FORWARD_CUSTOM_EVENTS)) {\n const val = this.getAttribute(ATTR_FORWARD_CUSTOM_EVENTS);\n if (!val || val.trim().length === 0) {\n this.forwardCustomEvents$.set(true);\n } else {\n this.forwardCustomEvents$.set(\n new Set(\n val\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0),\n ),\n );\n }\n } else {\n this.forwardCustomEvents$.set(false);\n }\n }\n}"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import {on} from '@spearwolf/eventize';\nimport {beQuiet, createEffect, createSignal, Effect, hibernate} from '@spearwolf/signalize';\nimport {VoidToken} from '../constants.js';\nimport {readBooleanAttribute} from '../utils/attr-utils.js';\nimport {ConsoleLogger} from '../utils/ConsoleLogger.js';\nimport {MicrotaskGate} from '../utils/MicrotaskGate.js';\nimport {ComponentContext} from '../view/ComponentContext.js';\nimport {ViewComponent} from '../view/ViewComponent.js';\nimport {\n ATTR_AUTO_DESTRUCT,\n ATTR_FORWARD_CUSTOM_EVENTS,\n ATTR_TOKEN,\n RequestEntParentEventName,\n ReRequestEntHostEventName,\n ReRequestEntParentEventName,\n} from './constants.js';\nimport {forwardCustomEventsFrom, isEmptyFilter, isSameFilter} from './forwardCustomEvents.js';\nimport {HostedSlots} from './hostedSlots.js';\nimport {stopWatchingForRemovalFrom, watchForRemovalFrom} from './parentRemoval.js';\nimport {type EntAncestorRequest, requestEntAncestor} from './requestEntAncestor.js';\nimport {ShaeElement} from './ShaeElement.js';\n\n/**\n * The parent of `node` in the flattened tree: the slot it is assigned to, otherwise its parent\n * node, and for a node sitting directly under a shadow root the host of that root.\n */\nconst flattenedParentOf = (node: Node): Node | undefined =>\n (node as Element).assignedSlot ?? node.parentNode ?? (node as unknown as ShadowRoot).host ?? undefined;\n\n/**\n * Whether `node` sits below `ancestor`, across shadow boundaries and slot projections.\n *\n * One ascent per candidate, and the round it saves is a full ancestor request per candidate \u2014 a\n * bubbling, composed event through the whole chain against a few pointer hops. What it buys grows\n * with the candidate set and with nothing else, so it is worth having wherever one element is\n * inserted below a parent that already holds many.\n *\n * Deciding on the sending side instead runs into two walls: the root channel carries no sender to\n * decide about, and an ancestor test that skips the ascent cannot see through a closed shadow\n * boundary \u2014 which is why the caller drops the ancestor for an element inside a closed tree and\n * asks everyone.\n */\nconst isBelow = (node: Node, ancestor: Node): boolean => {\n for (let current = flattenedParentOf(node); current != null; current = flattenedParentOf(current)) {\n if (current === ancestor) return true;\n }\n return false;\n};\n\n/**\n * Whether `node` sits inside a closed shadow tree, at any level.\n *\n * This is the one question {@link isBelow} cannot answer for itself. A node projected into a\n * closed shadow root reports no `assignedSlot`, so the ascent steps from it straight to the host\n * and skips the slot along with everything the closed tree holds. It rejoins the real event path\n * at that host, which means the only nodes it can ever miss are the ones inside a closed tree \u2014\n * and an ancestor it cannot see is one it must not rule out.\n */\nconst isInClosedShadowTree = (node: Node): boolean => {\n for (let root = node.getRootNode() as ShadowRoot; root?.host != null; root = root.host.getRootNode() as ShadowRoot) {\n if (root.mode === 'closed') return true;\n }\n return false;\n};\n\ninterface ReRequestParentData {\n newAncestor?: ShaeEntElement | undefined;\n}\n\nexport class ShaeEntElement extends ShaeElement {\n static override observedAttributes = [...ShaeElement.observedAttributes, ATTR_TOKEN, ATTR_FORWARD_CUSTOM_EVENTS];\n\n readonly isShaeEntElement = true;\n\n readonly componentContext$ = createSignal<ComponentContext | undefined>();\n readonly viewComponent$ = createSignal<ViewComponent | undefined>();\n readonly token$ = createSignal<string | undefined>();\n readonly forwardCustomEvents$ = createSignal<Set<string> | boolean>(false);\n\n readonly #logger = new ConsoleLogger('ShaeEntElement');\n\n /** The logger this element reports through: a subclass reads it and does not replace it. */\n protected get logger(): ConsoleLogger {\n return this.#logger;\n }\n\n get componentContext(): ComponentContext | undefined {\n return this.componentContext$.value;\n }\n\n get viewComponent(): ViewComponent | undefined {\n return this.viewComponent$.value;\n }\n\n get uuid(): string | undefined {\n return this.viewComponent?.uuid;\n }\n\n get token(): string | undefined {\n return this.token$.value;\n }\n\n set token(token: string | undefined) {\n this.token$.set(token);\n }\n\n entParentNode?: ShaeEntElement | undefined;\n\n /**\n * Whether this element already sat in a live tree while its constructor ran.\n *\n * An element created by the parser or by `createElement` runs its constructor before it enters\n * the tree, so everything below it connects after it and finds it on the first request. Only an\n * element upgraded in place can have entities below it that bound to a further ancestor while it\n * was not yet answering. Markup written into a connected node through `innerHTML` reports the\n * same, because the fragment parser builds the element undefined and upgrades it on insertion \u2014\n * that keeps the guard on the safe side, it errs towards asking too often.\n */\n readonly #wasUpgradedInPlace = this.isConnected;\n\n /** The node this element is watched on, for as long as it hangs in one. */\n #observedParentNode?: Node | undefined;\n\n /**\n * Bumped whenever the subscriptions this element holds on its component have to be set up again.\n *\n * Ending a component takes every subscription off it, this element's among them, and the\n * component itself stays where it is \u2014 so nothing about `viewComponent$` changes and the effects\n * below have no reason of their own to run again. This signal is that reason.\n */\n readonly #reSubscribe$ = createSignal(0);\n\n /**\n * The wait for the next microtask is the point, not a detail: the subscriptions come off right\n * behind the announcement, so a listener set up inside the handler would be taken off with them.\n * And a context tearing itself down walks its own set of instances \u2014 the element stays out of\n * that walk and comes back once it is over. However many announcements arrive in one task, one\n * round of setting up answers them all.\n */\n readonly #reSubscribe = new MicrotaskGate(() => {\n this.#reSubscribe$.set(this.#reSubscribe$.value + 1);\n });\n\n /** Moves this element's entity into the context of whichever namespace it names. */\n #namespaceBinding?: (() => void) | undefined;\n\n /** Writes the token back onto the attribute. */\n #tokenReflection?: (() => void) | undefined;\n\n /** Writes the event filter back onto the attribute. */\n #forwardCustomEventsReflection?: (() => void) | undefined;\n\n /** The eventize subscriptions this element holds on its component. */\n #viewComponentListeners?: Effect | undefined;\n\n /** The patch that carries the component's own events out as DOM events. */\n #forwardCustomEventsPatch?: Effect | undefined;\n\n /** Carries a new token through to the entity. */\n #tokenToViewComponent?: (() => void) | undefined;\n\n constructor() {\n super();\n\n // both reads run with nothing listening yet: what they put into the signals is the state the\n // attributes already spell out. The subscriptions are taken up at the first connect\n this.#updateTokenValue();\n this.#updateForwardCustomEventsValue();\n }\n\n /**\n * Take up everything this element listens to.\n *\n * Called from {@link ShaeEntElement.restore} and from nowhere else \u2014 that is the one place where\n * this element's subscriptions begin, at the first connect as well as after a teardown.\n */\n #subscribe(): void {\n this.#namespaceBinding = this.ns$.onChange((ns) => {\n // the order below is what makes the change symmetric: every entity that could be affected by\n // it is told before this element settles into its new context, and the element asks for its\n // own parent only once it is there\n\n // the entities hanging on this element stay in the namespace they are in, and this element\n // is about to leave it. `this.ns` already carries the new value at this point, so a child\n // that asks again right away is turned away by #onRequestParent\n const previousContext = this.componentContext;\n const previousNs = previousContext?.ns;\n const previousViewComponent = this.viewComponent;\n if (previousContext != null && previousViewComponent != null) {\n previousContext.dispatchReRequestParentChildren(previousViewComponent);\n }\n\n // this element's own binding: it belongs to the namespace it is leaving. Clearing it here\n // takes nothing but this element out of its parent's children\n this.#setParent(undefined);\n\n this.componentContext$.set(ComponentContext.get(ns));\n\n if (this.isConnected) {\n this.#dispatchRequestParent();\n // ...and this element may well be the closest ancestor for entities that were already\n // there when it arrived in this namespace\n this.#askPeersToReRequestParent();\n }\n\n // The properties below this element are deliberately left alone here. A property belongs to\n // the closest entity above it whatever namespace that entity is in, and a namespace change\n // moves no element \u2014 the closest one is still the closest one.\n\n // the environment it leaves holds the destruction of this entity and hears about it from\n // nobody else: `syncShadowObjects()` without an argument reads `this.ns`, which carries the\n // new value by now. The call is cheap \u2014 the namespaces are collected per namespace and\n // handed over in one microtask\n if (previousNs != null && previousNs !== ns) {\n this.syncShadowObjectsOf(previousNs);\n }\n });\n\n this.#tokenReflection = this.token$.onChange((token) => this.#reflectToken(token));\n\n this.#forwardCustomEventsReflection = this.forwardCustomEvents$.onChange((val) => this.#reflectForwardCustomEvents(val));\n\n this.#viewComponentListeners = createEffect(() => {\n const vc = this.viewComponent$.get();\n // both reads stand before the early return, otherwise the dependency on the re-subscribe\n // signal would hang on there being a component at the moment the effect first runs\n this.#reSubscribe$.get();\n if (!vc) return;\n\n const unsubscribe = [\n on(vc, ComponentContext.ReRequestParentRoots, () => this.#reRequestParentAsRoot()),\n on(vc, ComponentContext.ReRequestParent, (data?: ReRequestParentData) => this.#reRequestParent(data?.newAncestor)),\n on(vc, ComponentContext.ReRequestEntHost, () => this.#askPropertiesToReRequestHost()),\n // this one hangs on the same component as the three above and comes off with them, which\n // is exactly why it belongs in the same effect: it is what puts all four back\n on(vc, ViewComponent.Destroyed, () => this.#reSubscribe.schedule()),\n ];\n\n return () => {\n for (const un of unsubscribe) un();\n };\n });\n\n this.#forwardCustomEventsPatch = createEffect(() => {\n const vc = this.viewComponent$.get();\n // the teardown of a component drops an own `dispatchEvent` along with the subscriptions, so\n // the patch is set again on the same signal that puts those back\n this.#reSubscribe$.get();\n if (!vc) return;\n\n return forwardCustomEventsFrom(vc, this, this.forwardCustomEvents$.get());\n });\n\n this.#tokenToViewComponent = this.token$.onChange((token) => this.#writeTokenToViewComponent(token));\n }\n\n #reflectToken(token: string | undefined): void {\n this.reflectAttribute(ATTR_TOKEN, () => {\n if (token == null) {\n this.removeAttribute(ATTR_TOKEN);\n } else if (this.getAttribute(ATTR_TOKEN) !== token) {\n this.setAttribute(ATTR_TOKEN, token);\n }\n });\n }\n\n #reflectForwardCustomEvents(val: Set<string> | boolean): void {\n this.reflectAttribute(ATTR_FORWARD_CUSTOM_EVENTS, () => {\n if (!val || isEmptyFilter(val)) {\n this.removeAttribute(ATTR_FORWARD_CUSTOM_EVENTS);\n } else if (val === true) {\n // `getAttribute` answers `null` for an absent attribute, and `null !== ''` \u2014 the one\n // comparison covers both the missing attribute and a value that says something else\n if (this.getAttribute(ATTR_FORWARD_CUSTOM_EVENTS) !== '') {\n this.setAttribute(ATTR_FORWARD_CUSTOM_EVENTS, '');\n }\n } else {\n const str = Array.from(val).join(',');\n if (this.getAttribute(ATTR_FORWARD_CUSTOM_EVENTS) !== str) {\n this.setAttribute(ATTR_FORWARD_CUSTOM_EVENTS, str);\n }\n }\n });\n }\n\n #writeTokenToViewComponent(token: string | undefined): void {\n // the comparison is what makes the catch-up in `restore()` free: a return during which nobody\n // touched the token hands the entity the value it is already on, and a sync for a change trail\n // that would carry nothing is a sync not worth queueing\n const vc = this.viewComponent$.value;\n if (vc == null || vc.token === token) return;\n\n vc.token = token;\n this.syncShadowObjects();\n }\n\n /**\n * Take the subscriptions up and settle what drifted while none of them were listening.\n *\n * Runs on the first connect, where this element has never listened to anything, and again for one\n * that comes back after a teardown.\n *\n * The three catch-up calls are not decoration. A released element still takes writes, so `token`,\n * `ns` and `forward-custom-events` can each stand on a value its attribute never heard about \u2014\n * and `connectedCallback` reads exactly those attributes back into exactly those signals a few\n * lines further on. Without the catch-up that read would overwrite the newer value with the older\n * one, and the entity would go on carrying a token nobody wrote.\n *\n * `super.restore()` runs first and takes `ns` with it. That it writes the `ns` attribute before\n * this class has re-subscribed to `ns$` costs nothing: the write puts the value the signal\n * already holds onto the attribute, so the read it triggers changes no signal and reaches no\n * handler.\n */\n protected override restore(): void {\n super.restore();\n this.#subscribe();\n\n this.#reflectToken(this.token$.value);\n this.#reflectForwardCustomEvents(this.forwardCustomEvents$.value);\n this.#writeTokenToViewComponent(this.token$.value);\n }\n\n #unsubscribeViewComponentEffect?: (() => void) | undefined;\n\n #applyComponentContext = (context: ComponentContext | undefined) => {\n const token = this.token$.value;\n\n let vc = this.viewComponent$.value;\n\n // This catch is unconditional, and wider than \"the join failed\" on purpose: `vc.context = \u2026`\n // throws `ComponentUuidInUseError` or `ComponentContextDisposedError`; `new ViewComponent(\u2026)`\n // below can throw either one too, from inside its own constructor; and `viewComponent$.set(vc)`\n // right after it runs this element's own effects synchronously, so a throw from one of those \u2014\n // a `TypeError` included \u2014 lands here as well. Neither call site of this method has anywhere of\n // its own to send such a throw: `connectedCallback()` is a custom element reaction, whose\n // exception the browser reports to the global `error` event instead of reaching the code that\n // triggered the connect, and the `componentContext$.onChange` listener is a signal effect,\n // whose exception is collected and re-thrown out of whichever `set()` started the effect chain\n // \u2014 which can be a plain `el.ns = \u2026` assignment several calls up.\n //\n // The two documented errors leave the component differently. A uuid already in use costs it\n // whatever context it had: the setter has already left that context, if there was one, before\n // the failure surfaces, so `vc.isDestroyed` ends up `true` either way. A disposed target is\n // rejected before anything is undone, so the component keeps exactly the context it already\n // had \u2014 `isDestroyed` stays whatever it already was, live context included.\n try {\n if (vc) {\n vc.context = context;\n } else if (context) {\n vc = new ViewComponent(token ?? VoidToken, {\n context,\n // read here and only here: the flag is immutable on a ViewComponent, so the moment the\n // component is built is the only moment the attribute can still decide anything. That is\n // also why it is not observed \u2014 a later write has nowhere to go\n autoDestructionOnParentRemoval: readBooleanAttribute(this, ATTR_AUTO_DESTRUCT),\n });\n this.viewComponent$.set(vc);\n }\n } catch (error) {\n this.logger.error('applying the component context of this entity failed:', {ns: this.ns, uuid: vc?.uuid}, error);\n }\n\n this.syncShadowObjects();\n };\n\n #setupViewComponentEffect() {\n this.#unsubscribeViewComponentEffect?.();\n\n const unsubscribeComponentContext = this.componentContext$.onChange(this.#applyComponentContext);\n\n this.#unsubscribeViewComponentEffect = () => {\n unsubscribeComponentContext();\n };\n }\n\n #destroyViewComponentEffect() {\n this.#unsubscribeViewComponentEffect?.();\n this.#unsubscribeViewComponentEffect = undefined;\n }\n\n // `getRootNode()` answers for every state of the tree \u2014 the shadow root for a node inside\n // one, and the topmost node of its own chain for a node outside every tree. This reads `.host`\n // off the result without checking for a `ShadowRoot` first: only a shadow root has one, so a\n // node outside every tree reads no host, the same answer a check would have given\n #readShadowRootHost(): HTMLElement | undefined {\n return (this.getRootNode() as ShadowRoot)?.host as HTMLElement | undefined;\n }\n\n #shadowRootHost?: HTMLElement | undefined;\n #shadowRootHostNeedsUpdate = true;\n\n findShadowRootHost(): HTMLElement | undefined {\n if (this.#shadowRootHostNeedsUpdate) {\n this.#shadowRootHostNeedsUpdate = false;\n this.#shadowRootHost = this.#readShadowRootHost();\n }\n return this.#shadowRootHost;\n }\n\n protected getParentNodeForObserver(): Node | undefined {\n // a node still sitting in a tree answers from the first term, and for one placed directly\n // under a shadow root that term is the shadow root itself. Once it has no parent left it is\n // its own root, so the host lookup comes up empty too and the answer is: no parent\n return this.parentNode ?? this.#readShadowRootHost() ?? undefined;\n }\n\n override connectedCallback() {\n // the whole body outside any reactive context of the caller, for the reason spelled out in\n // `ShaeLifecycleElement.connectedCallback`: an `append()` inside a foreign effect would\n // otherwise own every subscription taken up here. `#setupViewComponentEffect()` carries that\n // just as much as `restore()` does \u2014 it subscribes to `componentContext$` on every single\n // connect. Nesting is fine: `super.connectedCallback()` opens a frame of its own inside this one\n hibernate(() => {\n super.connectedCallback();\n\n this.#shadowRootHostNeedsUpdate = true;\n\n this.addEventListener('slotchange', this.#onSlotChange, {capture: false, passive: false});\n this.addEventListener(RequestEntParentEventName, this.#onRequestParent, {capture: false, passive: false});\n\n this.#setupViewComponentEffect();\n\n // --- token ---\n beQuiet(() => this.#updateTokenValue());\n\n // --- forward-custom-events ---\n // the patch on the ViewComponent's dispatchEvent hangs on this signal, so the read-back has to\n // reach it: a write nothing observes would leave the patch standing on a filter the element no\n // longer carries. A list is a fresh Set on every read and would count as a change by identity\n // alone, so an unchanged filter is left alone instead of written again\n const forwardCustomEvents = this.#readForwardCustomEventsAttribute();\n if (!isSameFilter(forwardCustomEvents, this.forwardCustomEvents$.value)) {\n this.forwardCustomEvents$.set(forwardCustomEvents);\n }\n\n // --- componentContext | viewComponent ---\n if (this.componentContext == null) {\n this.componentContext$.set(ComponentContext.get(this.ns));\n } else {\n // the namespace may have been set or changed while the element was outside the tree. The\n // context signal then already stands on its new value, nothing is going to fire for it\n // again, and the element would sit in a namespace without ever becoming an entity in it.\n // In the ordinary case this hands over the very same context the component already has,\n // which the setter answers with a return\n this.#applyComponentContext(this.componentContext);\n }\n\n // --- viewComponent.parent ---\n this.#dispatchRequestParent();\n\n // --- parents ---\n // the order matters: before the line above, this element's own parent is not settled and the\n // candidate set would be the wrong one.\n //\n // The guard decides in constant time whether the question arises at all, and it has to,\n // because this runs on every connect. It cannot hide a case the request would have found: an\n // element constructed before it entered the tree is answering by the time anything below it\n // connects. What this element holds is no such question \u2014 a shadow root can be attached to it\n // before it is defined, and a closed one is invisible from the inside, so `shadowRoot` reads\n // null while the entities in it are bound to an ancestor further up. An empty element is not\n // an element with nothing below it.\n //\n // --- properties ---\n // The same reasoning carries the second call, and so does the same limit: an element built in\n // a detached subtree and inserted afterwards does *not* announce itself, because the\n // properties below it connect after it and find it on their own. A property already connected\n // and then projected into that subtree is reached by the `#onSlotChange` call further down.\n if (this.#wasUpgradedInPlace) {\n this.#askPeersToReRequestParent();\n this.#askPropertiesToReRequestHost();\n }\n\n // --- hosted slots ---\n this.#hostedSlots.collect();\n\n this.#observeParentNode();\n\n // --- sync! ---\n this.syncShadowObjects();\n });\n }\n\n #observeParentNode() {\n this.#unobserveParentNode();\n const parent = this.getParentNodeForObserver();\n if (parent) {\n watchForRemovalFrom(parent, this, () => {\n // the watch ended when it fired, and the field says so before anything else runs\n this.#observedParentNode = undefined;\n\n const newParent = this.getParentNodeForObserver();\n\n // an atomic move reports the removal even where it puts the element back under the node\n // it came from \u2014 reordering among siblings is such a move, and nothing about the parent\n // changed. A subclass reading the two arguments against each other would be told\n // otherwise, so the report is left out, and with it the two things `onParentChanged` does\n // before it watches again. Those drop the resolved entity ancestor and ask for it anew,\n // and the ascent that answers begins at this element and passes the node it hangs on \u2014\n // this move leaves that node and everything above it where it stood, so the answer would\n // be the one already held.\n //\n // The watch fired and is gone all the same, and taking it out again is what stands in its\n // place. That call registers and returns: `watchForRemovalFrom` dispatches the records\n // already due before it adds this element, and this element is not among the watchers it\n // finds there \u2014 its entry came off when this callback fired. A watcher in that batch that\n // moves the element somewhere else lands in the `else` further down, which says where the\n // chain ends.\n if (newParent === parent) {\n this.#observeParentNode();\n return;\n }\n\n this.onParentChanged(newParent, parent);\n });\n // asked a second time rather than remembered from above: `watchForRemovalFrom` runs the\n // watchers this parent has come due for before it adds its own, and one of them is\n // `onParentChanged` on some other element \u2014 a documented extension point, free to move or\n // disconnect this element in turn. The answer below says where the element hangs once that\n // code has had its turn: the node just registered for, and the registration stands; anything\n // else, and it belongs to a node the element has left and comes off again. Setting the field\n // ahead of the registration would close this half and open the other one \u2014 a callback that\n // disconnects the element would then unwatch a `this` nothing has added yet\n const parentAfterRegistration = this.getParentNodeForObserver();\n if (parentAfterRegistration === parent) {\n this.#observedParentNode = parent;\n } else {\n stopWatchingForRemovalFrom(parent, this);\n // the element left `parent`, and the watcher that would have reported that is the one just\n // taken back \u2014 so this call owes the report. A move that carried the element's own\n // `connectedCallback` with it has already re-resolved the parent and watched the node it\n // landed on; an empty field is what says no such reaction ran, which is the case for a\n // `moveBefore` on a subclass with an empty `connectedMoveCallback`, and then nothing else\n // is left in a position to say it. `isConnected` because an element that ended up in a\n // detached subtree announces itself again through `connectedCallback` when it is put back.\n //\n // The inherited implementation ends in `#observeParentNode()`, so this reaches itself:\n // every further level needs a watcher on the new parent node that has come due and moves\n // this very element, and with none running the answer above matches and the call ends\n if (this.#observedParentNode == null && this.isConnected) {\n // guarded like every other delivery to this extension point, and reported in the same\n // words: which of the two paths carries the report is a detail of the registration\n // window, and a subclass whose override throws reads one message either way\n try {\n this.onParentChanged(parentAfterRegistration, parent);\n } catch (error) {\n console.error('a removal watcher failed:', error);\n }\n }\n }\n }\n }\n\n #unobserveParentNode() {\n if (this.#observedParentNode == null) return;\n stopWatchingForRemovalFrom(this.#observedParentNode, this);\n this.#observedParentNode = undefined;\n }\n\n onParentChanged(_newParent: Node | undefined, _oldParent: Node) {\n this.#setParent(undefined);\n this.#dispatchRequestParent();\n // the observation watches one specific parent node, so it has to travel with the element:\n // left at the old position it would report the next move of a node that is no longer here.\n // The call is idempotent \u2014 #observeParentNode starts with #unobserveParentNode\n this.#observeParentNode();\n }\n\n override attributeChangedCallback(name: string) {\n super.attributeChangedCallback(name);\n if (name === ATTR_TOKEN) {\n this.#updateTokenValue();\n } else if (name === ATTR_FORWARD_CUSTOM_EVENTS) {\n this.#updateForwardCustomEventsValue();\n }\n }\n\n override disconnectedCallback() {\n this.#shadowRootHostNeedsUpdate = true;\n\n this.#unobserveParentNode();\n\n // an entity outside the tree answers for no slot any more, and its listeners must not outlive\n // what it is itself\n this.#hostedSlots.releaseAll();\n\n this.#stopAnswering();\n\n // after the listener came off, never before: a property that asks again because of this must\n // not be answered by this element any more. The element is out of the tree by now, so the\n // event reaches only whoever listens on the element itself \u2014 which is exactly the set of\n // properties bound to it\n this.#askPropertiesToReRequestHost();\n\n this.#setParent(undefined);\n\n // an entity that hung on this element can outlive its departure: it stays connected whenever\n // only the way between them is cut, which is what happens to everything projected into a slot\n // this element holds. Leaving the context detaches the ViewComponent \u2014 it keeps everything on\n // it and can be taken back in \u2014 and promotes those entities to roots, so they have to look for\n // an ancestor again; this element is past answering by now, its listener came off a few lines\n // above. An entity that leaves the tree along with this one is not affected: it turns the\n // message down while disconnected\n const vc = this.viewComponent;\n if (vc != null) {\n this.componentContext?.dispatchReRequestParentChildren(vc);\n }\n\n this.componentContext$.set(undefined);\n\n this.syncShadowObjects();\n\n this.#destroyViewComponentEffect();\n\n // last: everything above still reads `this.ns` and the signals, and the deferred teardown has\n // to be booked behind them rather than in front of them\n super.disconnectedCallback();\n }\n\n /**\n * Let go of everything this element listens to, and leave what it is made of standing.\n *\n * The signals are not destroyed \u2014 they are what carries this element's state across a teardown,\n * and {@link ShaeEntElement.restore} subscribes to them again on the way back in.\n *\n * The `ViewComponent` stays too. `disconnectedCallback` has already taken it out of its\n * `ComponentContext`, which is what unbinds it from this element's position; ending it on top of\n * that would write a destroy entry into the change trail and take the entity down in the worker\n * \u2014 a decision about the entity, not about this element. It is also what lets an element that\n * comes back carry the same entity and the same uuid it left with.\n *\n * Both effects run their cleanup on `destroy()`, which is what this depends on: the one on the\n * `dispatchEvent` patch has to, or the patched function would go on holding the component and\n * everything reachable from it.\n */\n protected override teardown() {\n this.#namespaceBinding?.();\n this.#namespaceBinding = undefined;\n\n this.#tokenReflection?.();\n this.#tokenReflection = undefined;\n\n this.#forwardCustomEventsReflection?.();\n this.#forwardCustomEventsReflection = undefined;\n\n this.#viewComponentListeners?.destroy();\n this.#viewComponentListeners = undefined;\n\n this.#forwardCustomEventsPatch?.destroy();\n this.#forwardCustomEventsPatch = undefined;\n\n this.#tokenToViewComponent?.();\n this.#tokenToViewComponent = undefined;\n\n this.#destroyViewComponentEffect();\n this.#unobserveParentNode();\n this.#hostedSlots.releaseAll();\n // the listener this element holds on its entity parent comes off with the binding. Along the\n // ordinary way out that has already happened in `disconnectedCallback`, and this call turns\n // around without touching anything; a `destroy()` made by hand reaches it here\n this.#setParent(undefined);\n\n // An element released by hand can still be sitting in the document, and one that answers\n // requests it no longer acts on is worse than one that stays silent: an entity below it would\n // bind to a parent that resolves nothing, and a property below it would declare itself on an\n // entity nobody is maintaining. So the answering stops here too, and whoever asked is told to\n // ask again \u2014 the next entity up is the right answer now. Along the ordinary way out this has\n // already happened in `disconnectedCallback`, where the same two calls stand in the same order.\n if (this.isConnected) {\n this.#stopAnswering();\n this.#askPropertiesToReRequestHost();\n const vc = this.viewComponent;\n if (vc != null) {\n this.componentContext?.dispatchReRequestParentChildren(vc);\n }\n }\n\n super.teardown();\n }\n\n /** Stop answering the parent and host requests that travel past this element. */\n #stopAnswering(): void {\n this.removeEventListener('slotchange', this.#onSlotChange, {capture: false});\n this.removeEventListener(RequestEntParentEventName, this.#onRequestParent, {capture: false});\n }\n\n #reRequestParentAsRoot() {\n if (this.isConnected) {\n this.#setParent(undefined);\n this.#dispatchRequestParent();\n }\n }\n\n // nothing is cleared here: an element already bound to its closest ancestor gets the same answer\n // back and #setParent turns around without touching anything, so the round leaves every\n // correctly bound sibling standing in its parent's children. Only an element that gets no answer\n // at all loses its binding, and that one sits below nothing any more\n #reRequestParent(newAncestor?: ShaeEntElement) {\n if (!this.isConnected) return;\n\n // the peers of an entity are its siblings in the component context, which says nothing about\n // where their elements sit. Only one below `newAncestor` can get a different answer, and\n // walking up to find out costs a few pointer hops against a bubbling event through the whole\n // ancestor chain. A signal that carries no ancestor asks unconditionally \u2014 the sender leaves\n // it out wherever the ascent could not see the whole way.\n //\n // An ancestor that is out of the tree is such a place too, and it reaches here through a\n // direct `dispatchReRequestParentSiblings()`: the collected round drops a sender that has\n // left the ComponentContext, and an element leaving the tree leaves the context with it.\n // The ascent against a detached element answers \"not below\" for every receiver alike, which\n // is true and useless \u2014 the tree that element left behind is a different one. Asking is the\n // answer that costs an ascent instead of a wrong parent\n if (newAncestor?.isConnected && !isBelow(this, newAncestor)) return;\n\n this.#dispatchRequestParent();\n }\n\n // An element that becomes an entity while the tree around it already stands can be the new\n // closest ancestor for entities that bound while it was not yet answering. Those entities are\n // its peers in the component context \u2014 the children of its own parent, or the roots while it\n // has none \u2014 and they are asked to request their parent once more.\n //\n // The context collects the request and runs one round a microtask later, for everything that\n // arrives in the same task. Which peers that round reaches is decided when it runs, from this\n // element's parent as it stands then \u2014 an element with no entity of its own is in no tree and\n // is nobody's ancestor, so there is nothing to ask for.\n //\n // Both ways of having no entity end here, and neither costs a peer its parent. This element\n // reaches the early return only when the join into the context threw before a component was\n // ever built; the reachable half of that case is a component that was built and refused, which\n // the round drops on its own because a refused component is no member of the context. What such\n // an element could have handed a peer is an `entParentNode` pointing at an element that stands\n // in no entity tree \u2014 the peer's `viewComponent.parent` would stay empty and the peer would\n // stay a root either way. The next namespace change books the round again.\n #askPeersToReRequestParent() {\n const vc = this.viewComponent;\n if (vc == null) return;\n\n // the peer decides from the element tree whether it really sits below this element, and it\n // can only do so while the way up to here stays visible to it. From inside a closed shadow\n // tree it does not: the ascent of a projected peer steps over the closed tree and would\n // rule out exactly the case that needs correcting. The filter is an optimization, so where\n // it cannot see, it does not travel \u2014 every peer is then asked and answers for itself\n const newAncestor = isInClosedShadowTree(this) ? undefined : this;\n this.componentContext?.collectPeerReRequest(vc, {newAncestor} satisfies ReRequestParentData);\n }\n\n // Properties below this element bind to the closest entity above them. This element becoming\n // one \u2014 or ceasing to be one \u2014 changes that answer, and no component context can carry the\n // message: a <shae-prop> has no view component to receive it. The event travels the same\n // ascent every request travels, so it passes exactly the entities a property could be bound\n // to right now, and nothing else.\n #askPropertiesToReRequestHost() {\n this.dispatchEvent(new CustomEvent(ReRequestEntHostEventName, {bubbles: true, composed: true, detail: {requester: this}}));\n }\n\n #dispatchRequestParent() {\n let entParent: ShaeEntElement | undefined;\n // an entity takes only an ancestor from its own namespace as a parent\n requestEntAncestor(this, {ns: this.ns, answer: (entNode) => (entParent = entNode)});\n // `requestEntAncestor` sends a synchronous `dispatchEvent`, so by the time it returns it is\n // settled whether anyone answered. No answer is an answer as well: an entity nobody claims is\n // a root of its context, and that is what gets written here\n this.#setParent(entParent);\n }\n\n #unsubscribeFromParent?: (() => void) | undefined;\n\n #setParent(parent?: ShaeEntElement) {\n if (this.entParentNode === parent) return;\n\n if (this.entParentNode) {\n this.entParentNode.removeEventListener(ReRequestEntParentEventName, this.#onReRequestParent, {capture: false});\n }\n\n this.entParentNode = parent;\n\n if (this.entParentNode) {\n this.entParentNode.addEventListener(ReRequestEntParentEventName, this.#onReRequestParent, {\n capture: false,\n passive: false,\n });\n }\n\n this.#unsubscribeFromParent?.();\n this.#unsubscribeFromParent = undefined;\n\n if (parent) {\n const e = createEffect(() => {\n const vc = this.viewComponent$.get();\n if (vc) {\n const parentVC = parent.viewComponent$.get();\n // a parent link needs a ComponentContext, and the same one on both sides: an entity\n // that holds none stands in no entity tree at all, and two entities of different\n // ComponentContexts stand in two trees that never meet\n const ctx = vc.context;\n vc.parent = ctx != null && parentVC?.context === ctx ? parentVC : undefined;\n if (vc.parent == null) {\n queueMicrotask(() => {\n this.#dispatchRequestParent();\n });\n }\n this.syncShadowObjects();\n }\n });\n this.#unsubscribeFromParent = () => e.destroy();\n } else {\n const vc = this.viewComponent;\n if (vc?.parent) {\n vc.parent = undefined;\n this.syncShadowObjects();\n }\n }\n }\n\n /** The `<slot>`s this element answers for. */\n readonly #hostedSlots = new HostedSlots(this);\n\n #onSlotChange = (event: Event) => {\n // this stands before the shadow-root condition below, and that is where the two channels part\n // ways: the entity channel only cares about a slot change inside a shadow root, because that\n // is what moves a parent binding across the boundary. For a property every changed slot\n // assignment counts \u2014 it moves what sits below this element.\n this.#askPropertiesToReRequestHost();\n\n this.#hostedSlots.takeUp(event);\n\n const shadowRootHost = this.findShadowRootHost();\n if (shadowRootHost == null) return;\n this.dispatchEvent(\n new CustomEvent(ReRequestEntParentEventName, {\n bubbles: true,\n composed: true,\n detail: {requester: this, shadowRootHost},\n }),\n );\n };\n\n #onReRequestParent = (event: CustomEvent) => {\n const requester = event.detail?.requester as ShaeEntElement | undefined;\n\n if (requester === this) return;\n if (!requester?.isShaeEntElement) return;\n if (requester.ns !== this.ns) return;\n\n const shadowRootHost = event.detail?.shadowRootHost as HTMLElement | undefined;\n\n if (shadowRootHost) {\n this.#dispatchRequestParent();\n }\n };\n\n #onRequestParent = (event: CustomEvent<EntAncestorRequest>) => {\n const request = event.detail;\n\n // a request has to name its sender, or this element could end up answering itself\n if (request?.requester == null || request.requester === this) return;\n // only a request built by `requestEntAncestor` carries a way to answer it\n if (typeof request.answer !== 'function') return;\n\n // an ancestor in another namespace *skips* the request, it does not block it: the `return`\n // stands before the `stopPropagation()` so the event keeps travelling to the next one.\n // A request without a namespace takes the closest ancestor the DOM shows \u2014 that is how a\n // property asks\n if (request.ns !== undefined && request.ns !== this.ns) return;\n\n event.stopPropagation();\n\n request.answer(this);\n };\n\n #updateTokenValue() {\n if (this.hasAttribute(ATTR_TOKEN)) {\n const token = this.getAttribute(ATTR_TOKEN)?.trim() || undefined;\n this.token$.set(token);\n } else {\n // both ways of taking the token away end in the same place: the entity falls back to\n // VoidToken, and the reflection has nothing left to write\n this.token$.set(undefined);\n }\n }\n\n /** The filter the `forward-custom-events` attribute currently spells out. */\n #readForwardCustomEventsAttribute(): Set<string> | boolean {\n if (!this.hasAttribute(ATTR_FORWARD_CUSTOM_EVENTS)) return false;\n\n const val = this.getAttribute(ATTR_FORWARD_CUSTOM_EVENTS);\n if (!val || val.trim().length === 0) return true;\n\n const types = new Set(\n val\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0),\n );\n\n // a reflected empty string is the spelling for \"all events\" \u2014 a list that names none of\n // them has to mean the opposite, so it falls back to the signal's own default instead of\n // becoming a Set that would round-trip into that same empty string\n return types.size > 0 ? types : false;\n }\n\n // the write is unconditional on purpose: a list read from the attribute is a fresh Set every\n // time, and it is that change of identity which drives the normalization of the attribute back\n // to its canonical spelling\n #updateForwardCustomEventsValue() {\n this.forwardCustomEvents$.set(this.#readForwardCustomEventsAttribute());\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The lifecycle every custom element of this library carries: when it starts listening, when it
|
|
3
|
+
* lets go, and what it keeps across the two.
|
|
4
|
+
*
|
|
5
|
+
* The subscriptions of such an element begin at its first connect, not at its construction. That is
|
|
6
|
+
* what makes an element that is built and never used collectable at all: signalize registers every
|
|
7
|
+
* effect and every `onChange` handler under a signal id in a module-level queue, so an element that
|
|
8
|
+
* subscribed in its constructor would hang there for the lifetime of the page, however little the
|
|
9
|
+
* application ever did with it. What the constructor does instead is read attributes into signals,
|
|
10
|
+
* which nobody is listening to yet.
|
|
11
|
+
*
|
|
12
|
+
* An element that leaves the tree and stays out is torn down: every effect, every `onChange`
|
|
13
|
+
* subscription and every link it holds comes off, and with them the last thing on the module level
|
|
14
|
+
* that pointed at it. The decision waits one microtask, so a move inside a single task — every
|
|
15
|
+
* re-render is one — costs the element nothing.
|
|
16
|
+
*
|
|
17
|
+
* What the element carries as state it keeps. Its signals are not destroyed: they hold their
|
|
18
|
+
* identity and their values, a write between the teardown and a return lands in the value, and a
|
|
19
|
+
* return into the tree subscribes to them again through {@link ShaeLifecycleElement.restore}.
|
|
20
|
+
* Destroying them would make the teardown one-way for no gain — a destroyed signal cannot be
|
|
21
|
+
* revived, and it is not what holds the element anyway.
|
|
22
|
+
*
|
|
23
|
+
* A subclass whose teardown *is* final refuses the return in its own `connectedCallback` — see
|
|
24
|
+
* `ShaeWorkerElement`, whose teardown takes an environment with it that cannot be rebuilt. For such
|
|
25
|
+
* an element {@link ShaeLifecycleElement.restore} runs exactly once, on the first connect.
|
|
26
|
+
*/
|
|
27
|
+
export declare class ShaeLifecycleElement extends HTMLElement {
|
|
28
|
+
#private;
|
|
29
|
+
/** Whether this element has been torn down. */
|
|
30
|
+
get isDestroyed(): boolean;
|
|
31
|
+
connectedCallback(): void;
|
|
32
|
+
disconnectedCallback(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Tear this element down: let go of everything that listens, and keep everything that is state.
|
|
35
|
+
*
|
|
36
|
+
* Called for an element that has left the tree and stayed out, and callable by hand for one
|
|
37
|
+
* whose end is known earlier. Every call after the first finds nothing left to do.
|
|
38
|
+
*
|
|
39
|
+
* The guard and the flag live here, in front of the work, and that is the whole reason this
|
|
40
|
+
* method is not the one a subclass overrides: releasing what an element holds can call back into
|
|
41
|
+
* it — an environment being torn down dispatches a DOM event on its way out, and a listener on
|
|
42
|
+
* that event can reach `destroy()` again — and a flag that only fell at the end would let the
|
|
43
|
+
* second call run the whole teardown a second time. {@link ShaeLifecycleElement.teardown} is the
|
|
44
|
+
* extension point; it runs with the flag already down and `isDestroyed` already `true`.
|
|
45
|
+
*/
|
|
46
|
+
destroy(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Take the subscriptions up.
|
|
49
|
+
*
|
|
50
|
+
* Called from `connectedCallback` and from nowhere else — a constructor in particular, where the
|
|
51
|
+
* subclass fields these subscriptions read are not there yet. It runs at the first connect, where
|
|
52
|
+
* the element has never listened to anything, and again for one that comes back after a teardown;
|
|
53
|
+
* the two are the same job, and the element between them is in the same state either way.
|
|
54
|
+
*
|
|
55
|
+
* A subclass overrides this, calls `super.restore()` first and takes its own subscriptions up.
|
|
56
|
+
* Every subscription released in {@link ShaeLifecycleElement.teardown} has to come back here —
|
|
57
|
+
* one that does not is gone for the rest of the element's life, silently.
|
|
58
|
+
*/
|
|
59
|
+
protected restore(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Release what this element holds. The overridable half of
|
|
62
|
+
* {@link ShaeLifecycleElement.destroy}.
|
|
63
|
+
*
|
|
64
|
+
* A subclass releases its own subscriptions and calls `super.teardown()` last, so the element
|
|
65
|
+
* comes apart from the outside in. Whatever is released here has to be taken up again in
|
|
66
|
+
* {@link ShaeLifecycleElement.restore} — the two are one pair, and a subscription missing from
|
|
67
|
+
* either side is a leak or a silently dead element.
|
|
68
|
+
*/
|
|
69
|
+
protected teardown(): void;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=ShaeLifecycleElement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShaeLifecycleElement.d.ts","sourceRoot":"","sources":["../../../src/elements/ShaeLifecycleElement.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,oBAAqB,SAAQ,WAAW;;IAgBnD,+CAA+C;IAC/C,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,iBAAiB,SAwBhB;IAED,oBAAoB,IAAI,IAAI,CAE3B;IAED;;;;;;;;;;;;OAYG;IACH,OAAO,IAAI,IAAI,CAMd;IAED;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,OAAO,IAAI,IAAI,CAGxB;IAED;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,IAAI,IAAI,CAEzB;CACF"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { hibernate } from "@spearwolf/signalize";
|
|
2
|
+
import { DeferredTeardown } from "./deferredTeardown.js";
|
|
3
|
+
import { ensureDisplayContentsRule } from "./displayContentsRule.js";
|
|
4
|
+
class ShaeLifecycleElement extends HTMLElement {
|
|
5
|
+
#destroyed = false;
|
|
6
|
+
/**
|
|
7
|
+
* Whether this element is listening right now.
|
|
8
|
+
*
|
|
9
|
+
* A separate field from `#destroyed`, and they must not be folded into one: the two disagree for
|
|
10
|
+
* a freshly built element, which is listening to nothing and has been torn down by nobody. Read
|
|
11
|
+
* off a single field, that element would have to report `isDestroyed === true` — which is a lie
|
|
12
|
+
* the whole public surface would then carry: `destroy()` would find nothing to do, and the
|
|
13
|
+
* documented promise that a new element reads `false` would be gone.
|
|
14
|
+
*/
|
|
15
|
+
#subscribed = false;
|
|
16
|
+
#teardown = new DeferredTeardown(() => this.destroy());
|
|
17
|
+
/** Whether this element has been torn down. */
|
|
18
|
+
get isDestroyed() {
|
|
19
|
+
return this.#destroyed;
|
|
20
|
+
}
|
|
21
|
+
connectedCallback() {
|
|
22
|
+
hibernate(() => {
|
|
23
|
+
this.#teardown.cancel();
|
|
24
|
+
this.#destroyed = false;
|
|
25
|
+
if (!this.#subscribed) {
|
|
26
|
+
this.#subscribed = true;
|
|
27
|
+
this.restore();
|
|
28
|
+
}
|
|
29
|
+
ensureDisplayContentsRule(this.getRootNode(), this.localName);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
disconnectedCallback() {
|
|
33
|
+
this.#teardown.schedule();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Tear this element down: let go of everything that listens, and keep everything that is state.
|
|
37
|
+
*
|
|
38
|
+
* Called for an element that has left the tree and stayed out, and callable by hand for one
|
|
39
|
+
* whose end is known earlier. Every call after the first finds nothing left to do.
|
|
40
|
+
*
|
|
41
|
+
* The guard and the flag live here, in front of the work, and that is the whole reason this
|
|
42
|
+
* method is not the one a subclass overrides: releasing what an element holds can call back into
|
|
43
|
+
* it — an environment being torn down dispatches a DOM event on its way out, and a listener on
|
|
44
|
+
* that event can reach `destroy()` again — and a flag that only fell at the end would let the
|
|
45
|
+
* second call run the whole teardown a second time. {@link ShaeLifecycleElement.teardown} is the
|
|
46
|
+
* extension point; it runs with the flag already down and `isDestroyed` already `true`.
|
|
47
|
+
*/
|
|
48
|
+
destroy() {
|
|
49
|
+
if (this.#destroyed) return;
|
|
50
|
+
this.#destroyed = true;
|
|
51
|
+
this.#subscribed = false;
|
|
52
|
+
this.teardown();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Take the subscriptions up.
|
|
56
|
+
*
|
|
57
|
+
* Called from `connectedCallback` and from nowhere else — a constructor in particular, where the
|
|
58
|
+
* subclass fields these subscriptions read are not there yet. It runs at the first connect, where
|
|
59
|
+
* the element has never listened to anything, and again for one that comes back after a teardown;
|
|
60
|
+
* the two are the same job, and the element between them is in the same state either way.
|
|
61
|
+
*
|
|
62
|
+
* A subclass overrides this, calls `super.restore()` first and takes its own subscriptions up.
|
|
63
|
+
* Every subscription released in {@link ShaeLifecycleElement.teardown} has to come back here —
|
|
64
|
+
* one that does not is gone for the rest of the element's life, silently.
|
|
65
|
+
*/
|
|
66
|
+
restore() {
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Release what this element holds. The overridable half of
|
|
70
|
+
* {@link ShaeLifecycleElement.destroy}.
|
|
71
|
+
*
|
|
72
|
+
* A subclass releases its own subscriptions and calls `super.teardown()` last, so the element
|
|
73
|
+
* comes apart from the outside in. Whatever is released here has to be taken up again in
|
|
74
|
+
* {@link ShaeLifecycleElement.restore} — the two are one pair, and a subscription missing from
|
|
75
|
+
* either side is a leak or a silently dead element.
|
|
76
|
+
*/
|
|
77
|
+
teardown() {
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
ShaeLifecycleElement
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=ShaeLifecycleElement.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/elements/ShaeLifecycleElement.ts"],
|
|
4
|
+
"sourcesContent": ["import {hibernate} from '@spearwolf/signalize';\nimport {DeferredTeardown} from './deferredTeardown.js';\nimport {ensureDisplayContentsRule} from './displayContentsRule.js';\n\n/**\n * The lifecycle every custom element of this library carries: when it starts listening, when it\n * lets go, and what it keeps across the two.\n *\n * The subscriptions of such an element begin at its first connect, not at its construction. That is\n * what makes an element that is built and never used collectable at all: signalize registers every\n * effect and every `onChange` handler under a signal id in a module-level queue, so an element that\n * subscribed in its constructor would hang there for the lifetime of the page, however little the\n * application ever did with it. What the constructor does instead is read attributes into signals,\n * which nobody is listening to yet.\n *\n * An element that leaves the tree and stays out is torn down: every effect, every `onChange`\n * subscription and every link it holds comes off, and with them the last thing on the module level\n * that pointed at it. The decision waits one microtask, so a move inside a single task \u2014 every\n * re-render is one \u2014 costs the element nothing.\n *\n * What the element carries as state it keeps. Its signals are not destroyed: they hold their\n * identity and their values, a write between the teardown and a return lands in the value, and a\n * return into the tree subscribes to them again through {@link ShaeLifecycleElement.restore}.\n * Destroying them would make the teardown one-way for no gain \u2014 a destroyed signal cannot be\n * revived, and it is not what holds the element anyway.\n *\n * A subclass whose teardown *is* final refuses the return in its own `connectedCallback` \u2014 see\n * `ShaeWorkerElement`, whose teardown takes an environment with it that cannot be rebuilt. For such\n * an element {@link ShaeLifecycleElement.restore} runs exactly once, on the first connect.\n */\nexport class ShaeLifecycleElement extends HTMLElement {\n #destroyed = false;\n\n /**\n * Whether this element is listening right now.\n *\n * A separate field from `#destroyed`, and they must not be folded into one: the two disagree for\n * a freshly built element, which is listening to nothing and has been torn down by nobody. Read\n * off a single field, that element would have to report `isDestroyed === true` \u2014 which is a lie\n * the whole public surface would then carry: `destroy()` would find nothing to do, and the\n * documented promise that a new element reads `false` would be gone.\n */\n #subscribed = false;\n\n readonly #teardown = new DeferredTeardown(() => this.destroy());\n\n /** Whether this element has been torn down. */\n get isDestroyed(): boolean {\n return this.#destroyed;\n }\n\n connectedCallback() {\n // The whole body runs outside whatever reactive context the caller is in. An `append()` is an\n // ordinary call, and it can perfectly well stand inside a `createEffect()` of the application:\n // a new effect and an `onChange()` hang themselves on the effect that is running at the time,\n // so every subscription taken up below would become a child of that foreign effect, and its\n // next run would release them all. The element would go quiet with `isDestroyed` still reading\n // `false` and nothing said. `hibernate()` clears the effect stack for the duration, so the\n // subscriptions belong to the element and come off where the element decides.\n hibernate(() => {\n // first, before anything reads or writes: being in the tree is the condition the deferred\n // teardown waits on, so arriving here calls a booked teardown off\n this.#teardown.cancel();\n\n // an element that is not listening takes its subscriptions up: the one that has never\n // listened yet and the one whose teardown ran are the same case. The signals stood untouched\n // either way, so this is a subscribe and never a rebuild\n this.#destroyed = false;\n if (!this.#subscribed) {\n this.#subscribed = true;\n this.restore();\n }\n\n ensureDisplayContentsRule(this.getRootNode(), this.localName);\n });\n }\n\n disconnectedCallback(): void {\n this.#teardown.schedule();\n }\n\n /**\n * Tear this element down: let go of everything that listens, and keep everything that is state.\n *\n * Called for an element that has left the tree and stayed out, and callable by hand for one\n * whose end is known earlier. Every call after the first finds nothing left to do.\n *\n * The guard and the flag live here, in front of the work, and that is the whole reason this\n * method is not the one a subclass overrides: releasing what an element holds can call back into\n * it \u2014 an environment being torn down dispatches a DOM event on its way out, and a listener on\n * that event can reach `destroy()` again \u2014 and a flag that only fell at the end would let the\n * second call run the whole teardown a second time. {@link ShaeLifecycleElement.teardown} is the\n * extension point; it runs with the flag already down and `isDestroyed` already `true`.\n */\n destroy(): void {\n if (this.#destroyed) return;\n this.#destroyed = true;\n this.#subscribed = false;\n\n this.teardown();\n }\n\n /**\n * Take the subscriptions up.\n *\n * Called from `connectedCallback` and from nowhere else \u2014 a constructor in particular, where the\n * subclass fields these subscriptions read are not there yet. It runs at the first connect, where\n * the element has never listened to anything, and again for one that comes back after a teardown;\n * the two are the same job, and the element between them is in the same state either way.\n *\n * A subclass overrides this, calls `super.restore()` first and takes its own subscriptions up.\n * Every subscription released in {@link ShaeLifecycleElement.teardown} has to come back here \u2014\n * one that does not is gone for the rest of the element's life, silently.\n */\n protected restore(): void {\n // the extension point holds nothing of its own: what an element listens to belongs to the\n // element, and this class decides only when the subscriptions are taken up\n }\n\n /**\n * Release what this element holds. The overridable half of\n * {@link ShaeLifecycleElement.destroy}.\n *\n * A subclass releases its own subscriptions and calls `super.teardown()` last, so the element\n * comes apart from the outside in. Whatever is released here has to be taken up again in\n * {@link ShaeLifecycleElement.restore} \u2014 the two are one pair, and a subscription missing from\n * either side is a leak or a silently dead element.\n */\n protected teardown(): void {\n // the counterpart of `restore()`, and empty for the same reason\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAQ,iBAAgB;AACxB,SAAQ,wBAAuB;AAC/B,SAAQ,iCAAgC;AA4BjC,MAAM,6BAA6B,YAAY;AAAA,EACpD,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWb,cAAc;AAAA,EAEL,YAAY,IAAI,iBAAiB,MAAM,KAAK,QAAQ,CAAC;AAAA;AAAA,EAG9D,IAAI,cAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,oBAAoB;AAQlB,cAAU,MAAM;AAGd,WAAK,UAAU,OAAO;AAKtB,WAAK,aAAa;AAClB,UAAI,CAAC,KAAK,aAAa;AACrB,aAAK,cAAc;AACnB,aAAK,QAAQ;AAAA,MACf;AAEA,gCAA0B,KAAK,YAAY,GAAG,KAAK,SAAS;AAAA,IAC9D,CAAC;AAAA,EACH;AAAA,EAEA,uBAA6B;AAC3B,SAAK,UAAU,SAAS;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,UAAgB;AACd,QAAI,KAAK,WAAY;AACrB,SAAK,aAAa;AAClB,SAAK,cAAc;AAEnB,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcU,UAAgB;AAAA,EAG1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWU,WAAiB;AAAA,EAE3B;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,7 +1,37 @@
|
|
|
1
1
|
import { ConsoleLogger } from '../utils/ConsoleLogger.js';
|
|
2
2
|
import type { ViewComponent } from '../view/ViewComponent.js';
|
|
3
3
|
import type { ShaeEntElement } from './ShaeEntElement.js';
|
|
4
|
-
|
|
4
|
+
import { ShaeLifecycleElement } from './ShaeLifecycleElement.js';
|
|
5
|
+
/**
|
|
6
|
+
* Sets a property on the entity above it.
|
|
7
|
+
*
|
|
8
|
+
* Unlike `<shae-ent>` and `<shae-worker>`, this element does not extend `ShaeElement` and has no
|
|
9
|
+
* namespace of its own. It shares their lifecycle: `ShaeLifecycleElement` is the base of all three,
|
|
10
|
+
* and `destroy()`, `isDestroyed`, `restore()` and `teardown()` mean the same thing on every one of
|
|
11
|
+
* them. What it does not share is the layer above it. `ShaeElement` exists for elements that pick
|
|
12
|
+
* an environment: their `ns` attribute names the one they live in, and what they do goes there by
|
|
13
|
+
* default — `ShaeElement.syncShadowObjectsOf()` is the explicit way to reach a different one, as
|
|
14
|
+
* `<shae-ent>` does on a namespace change. A property picks nothing. It belongs to the closest
|
|
15
|
+
* entity above it in the flattened tree, whatever namespace that entity happens to be in —
|
|
16
|
+
* proximity decides, not membership. An `ns` on a `<shae-prop>` would therefore be an attribute
|
|
17
|
+
* that changes no answer, and the convention both siblings follow — spreading
|
|
18
|
+
* `ShaeElement.observedAttributes` into their own — would put it into `observedAttributes` for
|
|
19
|
+
* every reader to trip over.
|
|
20
|
+
*
|
|
21
|
+
* That is also why the sync runs through the host: the environment that has to hear about a
|
|
22
|
+
* property is the one the host entity lives in, and this element is the wrong place to ask.
|
|
23
|
+
* `entNode.syncShadowObjects()` reaches it; a sync of this element's own would target the
|
|
24
|
+
* global namespace instead — nothing happens if no environment answers to that, and if one
|
|
25
|
+
* does, it still leaves the namespaced host waiting.
|
|
26
|
+
*
|
|
27
|
+
* The marker for this element is `isShaePropElement`, beside `isShaeEntElement` and
|
|
28
|
+
* `isShaeWorkerElement`. Each names one tag for consumers and tests to check — this file's own
|
|
29
|
+
* spec included — not for the host lookup itself: that is decided by `ShaeEntElement`'s
|
|
30
|
+
* `#onRequestParent`, the only listener registered for the request, and it checks `requester`,
|
|
31
|
+
* `answer` and `ns`, never a flag. A `<shae-prop>` never answers a host request because it never
|
|
32
|
+
* listens for one, not because its marker reads differently.
|
|
33
|
+
*/
|
|
34
|
+
export declare class ShaePropElement extends ShaeLifecycleElement {
|
|
5
35
|
#private;
|
|
6
36
|
static observedAttributes: string[];
|
|
7
37
|
readonly isShaePropElement = true;
|
|
@@ -12,7 +42,8 @@ export declare class ShaePropElement extends HTMLElement {
|
|
|
12
42
|
protected readonly valueOut$: import("@spearwolf/signalize").Signal<unknown>;
|
|
13
43
|
protected readonly type$: import("@spearwolf/signalize").Signal<string | undefined>;
|
|
14
44
|
protected readonly shouldTrim$: import("@spearwolf/signalize").Signal<boolean>;
|
|
15
|
-
|
|
45
|
+
/** The logger this element reports through: a subclass reads it and does not replace it. */
|
|
46
|
+
protected get logger(): ConsoleLogger;
|
|
16
47
|
get name(): string | undefined;
|
|
17
48
|
get value(): unknown;
|
|
18
49
|
set value(val: unknown);
|
|
@@ -21,8 +52,27 @@ export declare class ShaePropElement extends HTMLElement {
|
|
|
21
52
|
set entNode(el: ShaeEntElement | undefined);
|
|
22
53
|
get viewComponent(): ViewComponent | undefined;
|
|
23
54
|
constructor();
|
|
55
|
+
/**
|
|
56
|
+
* Take this element's own subscriptions up. Its half of {@link ShaeLifecycleElement.restore}.
|
|
57
|
+
*
|
|
58
|
+
* There is nothing to catch up on here, unlike `<shae-ent>`: `connectedCallback` reads `value`,
|
|
59
|
+
* `name`, `type` and `no-trim` off the attributes and looks the host up again straight after
|
|
60
|
+
* this, so the attributes decide, and whatever was written to a released element through
|
|
61
|
+
* `prop.value` or `prop.entNode` is replaced by what the tree and the markup say. That is this
|
|
62
|
+
* element's rule in and out of a teardown alike — a `<shae-prop>` reads its position on every
|
|
63
|
+
* connect — and the documentation states it as the difference it is.
|
|
64
|
+
*/
|
|
65
|
+
protected restore(): void;
|
|
24
66
|
connectedCallback(): void;
|
|
25
67
|
attributeChangedCallback(name: string): void;
|
|
26
68
|
disconnectedCallback(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Release this element's own subscriptions. Its half of {@link ShaeLifecycleElement.teardown}.
|
|
71
|
+
*
|
|
72
|
+
* The `link()` between the host's `viewComponent$` and this element's own needs no line of its
|
|
73
|
+
* own: it is built in the cleanup path of the `entNode$` subscription, and taking that
|
|
74
|
+
* subscription off runs the cleanup that destroys it.
|
|
75
|
+
*/
|
|
76
|
+
protected teardown(): void;
|
|
27
77
|
}
|
|
28
78
|
//# sourceMappingURL=ShaePropElement.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShaePropElement.d.ts","sourceRoot":"","sources":["../../../src/elements/ShaePropElement.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,aAAa,EAAC,MAAM,2BAA2B,CAAC;AAExD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"ShaePropElement.d.ts","sourceRoot":"","sources":["../../../src/elements/ShaePropElement.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,aAAa,EAAC,MAAM,2BAA2B,CAAC;AAExD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;AAI5D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAC,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAuC/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,eAAgB,SAAQ,oBAAoB;;IACvD,MAAM,CAAC,kBAAkB,WAAoD;IAE7E,QAAQ,CAAC,iBAAiB,QAAQ;IAElC,SAAS,CAAC,QAAQ,CAAC,QAAQ,oEAA8C;IACzE,SAAS,CAAC,QAAQ,CAAC,cAAc,mEAA6C;IAE9E,SAAS,CAAC,QAAQ,CAAC,KAAK,4DAAsC;IAC9D,SAAS,CAAC,QAAQ,CAAC,QAAQ,iDAAkB;IAC7C,SAAS,CAAC,QAAQ,CAAC,SAAS,iDAAkB;IAC9C,SAAS,CAAC,QAAQ,CAAC,KAAK,4DAAsC;IAC9D,SAAS,CAAC,QAAQ,CAAC,WAAW,iDAAsB;IAIpD,4FAA4F;IAC5F,SAAS,KAAK,MAAM,IAAI,aAAa,CAEpC;IAqCD,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,IAAI,KAAK,CAAC,GAAG,EAAE,OAAO,EAErB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,OAAO,IAAI,cAAc,GAAG,SAAS,CAExC;IAED,IAAI,OAAO,CAAC,EAAE,EAAE,cAAc,GAAG,SAAS,EAEzC;IAED,IAAI,aAAa,IAAI,aAAa,GAAG,SAAS,CAE7C;IAED,cAWC;IAyHD;;;;;;;;;OASG;IACH,UAAmB,OAAO,IAAI,IAAI,CAIjC;IAEQ,iBAAiB,SAezB;IAED,wBAAwB,CAAC,IAAI,EAAE,MAAM,QAkBpC;IAEQ,oBAAoB,SAU5B;IAED;;;;;;OAMG;IACH,UAAmB,QAAQ,IAAI,IAAI,CAgBlC;CAiHF"}
|