@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.
Files changed (210) hide show
  1. package/CHANGELOG.md +320 -61
  2. package/README.md +57 -13
  3. package/bundle.js +5 -54
  4. package/package.json +7 -3
  5. package/src/ChangeTrailRefusedError.d.ts +27 -0
  6. package/src/ChangeTrailRefusedError.d.ts.map +1 -0
  7. package/src/ChangeTrailRefusedError.js +12 -0
  8. package/src/ChangeTrailRefusedError.js.map +7 -0
  9. package/src/EntityUuidInUseError.d.ts +19 -0
  10. package/src/EntityUuidInUseError.d.ts.map +1 -0
  11. package/src/EntityUuidInUseError.js +11 -0
  12. package/src/EntityUuidInUseError.js.map +7 -0
  13. package/src/WorkerTimeoutError.d.ts +25 -0
  14. package/src/WorkerTimeoutError.d.ts.map +1 -0
  15. package/src/WorkerTimeoutError.js +12 -0
  16. package/src/WorkerTimeoutError.js.map +7 -0
  17. package/src/bundle.d.ts +1 -1
  18. package/src/bundle.d.ts.map +1 -1
  19. package/src/bundle.js +1 -1
  20. package/src/bundle.js.map +2 -2
  21. package/src/constants.d.ts +9 -2
  22. package/src/constants.d.ts.map +1 -1
  23. package/src/constants.js +1 -1
  24. package/src/constants.js.map +2 -2
  25. package/src/create-worker.bundle.d.ts +1 -1
  26. package/src/create-worker.bundle.d.ts.map +1 -1
  27. package/src/create-worker.bundle.js.map +1 -1
  28. package/src/create-worker.d.ts +1 -1
  29. package/src/create-worker.d.ts.map +1 -1
  30. package/src/create-worker.js.map +1 -1
  31. package/src/elements/ShaeElement.d.ts +70 -2
  32. package/src/elements/ShaeElement.d.ts.map +1 -1
  33. package/src/elements/ShaeElement.js +129 -25
  34. package/src/elements/ShaeElement.js.map +3 -3
  35. package/src/elements/ShaeEntElement.d.ts +39 -1
  36. package/src/elements/ShaeEntElement.d.ts.map +1 -1
  37. package/src/elements/ShaeEntElement.js +397 -167
  38. package/src/elements/ShaeEntElement.js.map +2 -2
  39. package/src/elements/ShaeLifecycleElement.d.ts +71 -0
  40. package/src/elements/ShaeLifecycleElement.d.ts.map +1 -0
  41. package/src/elements/ShaeLifecycleElement.js +83 -0
  42. package/src/elements/ShaeLifecycleElement.js.map +7 -0
  43. package/src/elements/ShaePropElement.d.ts +52 -2
  44. package/src/elements/ShaePropElement.d.ts.map +1 -1
  45. package/src/elements/ShaePropElement.js +240 -219
  46. package/src/elements/ShaePropElement.js.map +2 -2
  47. package/src/elements/ShaeWorkerElement.d.ts +49 -5
  48. package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
  49. package/src/elements/ShaeWorkerElement.js +204 -73
  50. package/src/elements/ShaeWorkerElement.js.map +2 -2
  51. package/src/elements/constants.d.ts +6 -0
  52. package/src/elements/constants.d.ts.map +1 -1
  53. package/src/elements/constants.js +12 -0
  54. package/src/elements/constants.js.map +2 -2
  55. package/src/elements/deferredTeardown.d.ts +24 -0
  56. package/src/elements/deferredTeardown.d.ts.map +1 -0
  57. package/src/elements/deferredTeardown.js +30 -0
  58. package/src/elements/deferredTeardown.js.map +7 -0
  59. package/src/elements/displayContentsRule.d.ts +25 -0
  60. package/src/elements/displayContentsRule.d.ts.map +1 -0
  61. package/src/elements/displayContentsRule.js +37 -0
  62. package/src/elements/displayContentsRule.js.map +7 -0
  63. package/src/elements/events.d.ts +10 -7
  64. package/src/elements/events.d.ts.map +1 -1
  65. package/src/elements/forwardCustomEvents.d.ts +19 -0
  66. package/src/elements/forwardCustomEvents.d.ts.map +1 -0
  67. package/src/elements/forwardCustomEvents.js +47 -0
  68. package/src/elements/forwardCustomEvents.js.map +7 -0
  69. package/src/elements/hostedSlots.d.ts +36 -0
  70. package/src/elements/hostedSlots.d.ts.map +1 -0
  71. package/src/elements/hostedSlots.js +118 -0
  72. package/src/elements/hostedSlots.js.map +7 -0
  73. package/src/elements/parentRemoval.d.ts +18 -0
  74. package/src/elements/parentRemoval.d.ts.map +1 -0
  75. package/src/elements/parentRemoval.js +60 -0
  76. package/src/elements/parentRemoval.js.map +7 -0
  77. package/src/elements/propValueConverters.d.ts +3 -0
  78. package/src/elements/propValueConverters.d.ts.map +1 -0
  79. package/src/elements/propValueConverters.js +54 -0
  80. package/src/elements/propValueConverters.js.map +7 -0
  81. package/src/elements/requestEntAncestor.d.ts +32 -0
  82. package/src/elements/requestEntAncestor.d.ts.map +1 -0
  83. package/src/elements/requestEntAncestor.js +16 -0
  84. package/src/elements/requestEntAncestor.js.map +7 -0
  85. package/src/elements.js.map +1 -1
  86. package/src/in-the-dark/Entity.d.ts +113 -3
  87. package/src/in-the-dark/Entity.d.ts.map +1 -1
  88. package/src/in-the-dark/Entity.js +292 -63
  89. package/src/in-the-dark/Entity.js.map +3 -3
  90. package/src/in-the-dark/Kernel.d.ts +76 -17
  91. package/src/in-the-dark/Kernel.d.ts.map +1 -1
  92. package/src/in-the-dark/Kernel.js +410 -378
  93. package/src/in-the-dark/Kernel.js.map +3 -3
  94. package/src/in-the-dark/Registry.d.ts.map +1 -1
  95. package/src/in-the-dark/Registry.js +83 -14
  96. package/src/in-the-dark/Registry.js.map +2 -2
  97. package/src/in-the-dark/ShadowObject.d.ts.map +1 -1
  98. package/src/in-the-dark/ShadowObject.js +1 -0
  99. package/src/in-the-dark/ShadowObject.js.map +2 -2
  100. package/src/in-the-dark/ShadowObjectCreationScope.d.ts +126 -0
  101. package/src/in-the-dark/ShadowObjectCreationScope.d.ts.map +1 -0
  102. package/src/in-the-dark/ShadowObjectCreationScope.js +596 -0
  103. package/src/in-the-dark/ShadowObjectCreationScope.js.map +7 -0
  104. package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
  105. package/src/in-the-dark/SignalsPath.js +2 -2
  106. package/src/in-the-dark/SignalsPath.js.map +2 -2
  107. package/src/in-the-dark/events.js.map +1 -1
  108. package/src/in-the-dark/importModule.d.ts +7 -0
  109. package/src/in-the-dark/importModule.d.ts.map +1 -1
  110. package/src/in-the-dark/importModule.js +5 -2
  111. package/src/in-the-dark/importModule.js.map +2 -2
  112. package/src/index.d.ts +7 -1
  113. package/src/index.d.ts.map +1 -1
  114. package/src/index.js +5 -1
  115. package/src/index.js.map +2 -2
  116. package/src/shadow-objects.d.ts +2 -0
  117. package/src/shadow-objects.d.ts.map +1 -1
  118. package/src/shadow-objects.js +2 -0
  119. package/src/shadow-objects.js.map +2 -2
  120. package/src/shadow-objects.worker.js +0 -1
  121. package/src/shadow-objects.worker.js.map +2 -2
  122. package/src/shae-ent.js.map +1 -1
  123. package/src/shae-prop.js +2 -2
  124. package/src/shae-prop.js.map +2 -2
  125. package/src/shae-worker.js.map +1 -1
  126. package/src/types.d.ts +35 -4
  127. package/src/types.d.ts.map +1 -1
  128. package/src/utils/ConsoleLogger.d.ts +45 -10
  129. package/src/utils/ConsoleLogger.d.ts.map +1 -1
  130. package/src/utils/ConsoleLogger.js +51 -26
  131. package/src/utils/ConsoleLogger.js.map +2 -2
  132. package/src/utils/FrameLoop.d.ts +42 -3
  133. package/src/utils/FrameLoop.d.ts.map +1 -1
  134. package/src/utils/FrameLoop.js +71 -10
  135. package/src/utils/FrameLoop.js.map +2 -2
  136. package/src/utils/MicrotaskCollector.d.ts +45 -0
  137. package/src/utils/MicrotaskCollector.d.ts.map +1 -0
  138. package/src/utils/MicrotaskCollector.js +33 -0
  139. package/src/utils/MicrotaskCollector.js.map +7 -0
  140. package/src/utils/MicrotaskGate.d.ts +17 -0
  141. package/src/utils/MicrotaskGate.d.ts.map +1 -0
  142. package/src/utils/MicrotaskGate.js +20 -0
  143. package/src/utils/MicrotaskGate.js.map +7 -0
  144. package/src/utils/array-utils.js.map +1 -1
  145. package/src/utils/attr-utils.d.ts +6 -0
  146. package/src/utils/attr-utils.d.ts.map +1 -1
  147. package/src/utils/attr-utils.js +3 -1
  148. package/src/utils/attr-utils.js.map +2 -2
  149. package/src/utils/constants.js.map +1 -1
  150. package/src/utils/generateUUID.d.ts +11 -0
  151. package/src/utils/generateUUID.d.ts.map +1 -1
  152. package/src/utils/generateUUID.js +31 -266
  153. package/src/utils/generateUUID.js.map +2 -2
  154. package/src/utils/props-utils.d.ts +3 -2
  155. package/src/utils/props-utils.d.ts.map +1 -1
  156. package/src/utils/props-utils.js +13 -24
  157. package/src/utils/props-utils.js.map +2 -2
  158. package/src/utils/runGuarded.d.ts +27 -0
  159. package/src/utils/runGuarded.d.ts.map +1 -0
  160. package/src/utils/runGuarded.js +11 -0
  161. package/src/utils/runGuarded.js.map +7 -0
  162. package/src/utils/toMaybe.d.ts.map +1 -1
  163. package/src/utils/toMaybe.js.map +1 -1
  164. package/src/utils/toNamespace.d.ts.map +1 -1
  165. package/src/utils/toNamespace.js.map +1 -1
  166. package/src/utils/toUrlString.d.ts +12 -0
  167. package/src/utils/toUrlString.d.ts.map +1 -1
  168. package/src/utils/toUrlString.js.map +2 -2
  169. package/src/utils/waitForMessageOfType.d.ts +12 -1
  170. package/src/utils/waitForMessageOfType.d.ts.map +1 -1
  171. package/src/utils/waitForMessageOfType.js +16 -1
  172. package/src/utils/waitForMessageOfType.js.map +2 -2
  173. package/src/view/ComponentChanges.d.ts +88 -1
  174. package/src/view/ComponentChanges.d.ts.map +1 -1
  175. package/src/view/ComponentChanges.js +243 -73
  176. package/src/view/ComponentChanges.js.map +2 -2
  177. package/src/view/ComponentContext.d.ts +206 -16
  178. package/src/view/ComponentContext.d.ts.map +1 -1
  179. package/src/view/ComponentContext.js +548 -91
  180. package/src/view/ComponentContext.js.map +2 -2
  181. package/src/view/ComponentMemory.d.ts +3 -3
  182. package/src/view/ComponentMemory.d.ts.map +1 -1
  183. package/src/view/ComponentMemory.js.map +1 -1
  184. package/src/view/IShadowObjectEnvProxy.d.ts +29 -3
  185. package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
  186. package/src/view/LocalShadowObjectEnv.d.ts +1 -1
  187. package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
  188. package/src/view/LocalShadowObjectEnv.js +8 -6
  189. package/src/view/LocalShadowObjectEnv.js.map +2 -2
  190. package/src/view/RemoteWorkerEnv.d.ts +89 -5
  191. package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
  192. package/src/view/RemoteWorkerEnv.js +195 -77
  193. package/src/view/RemoteWorkerEnv.js.map +2 -2
  194. package/src/view/ShadowEnv.d.ts +49 -2
  195. package/src/view/ShadowEnv.d.ts.map +1 -1
  196. package/src/view/ShadowEnv.js +223 -60
  197. package/src/view/ShadowEnv.js.map +2 -2
  198. package/src/view/ViewComponent.d.ts +78 -14
  199. package/src/view/ViewComponent.d.ts.map +1 -1
  200. package/src/view/ViewComponent.js +90 -14
  201. package/src/view/ViewComponent.js.map +2 -2
  202. package/src/view/cloneChangeTrail.js.map +2 -2
  203. package/src/worker/MessageRouter.d.ts +14 -0
  204. package/src/worker/MessageRouter.d.ts.map +1 -1
  205. package/src/worker/MessageRouter.js +75 -15
  206. package/src/worker/MessageRouter.js.map +2 -2
  207. package/src/worker/WorkerRuntime.d.ts +22 -1
  208. package/src/worker/WorkerRuntime.d.ts.map +1 -1
  209. package/src/worker/WorkerRuntime.js +60 -8
  210. package/src/worker/WorkerRuntime.js.map +2 -2
@@ -0,0 +1,118 @@
1
+ import { ComponentContext } from "../view/ComponentContext.js";
2
+ const entHostOfSlot = /* @__PURE__ */ new WeakMap();
3
+ const reRequestedForSlotChange = /* @__PURE__ */ new WeakSet();
4
+ const askEveryoneToReRequest = (event) => {
5
+ if (reRequestedForSlotChange.has(event)) return;
6
+ reRequestedForSlotChange.add(event);
7
+ for (const context of ComponentContext.getContextsMap().values()) {
8
+ context.broadcastEvent(ComponentContext.ReRequestParent);
9
+ context.broadcastEvent(ComponentContext.ReRequestEntHost);
10
+ }
11
+ };
12
+ class HostedSlots {
13
+ #owner;
14
+ // The `<slot>`s this element currently answers for. A listener on the slot itself is the only
15
+ // thing that tells the side losing a slot about the loss: `slotchange` fires at the slot,
16
+ // wherever it has landed, and the ascent from there is the one reading of "is this still mine"
17
+ // that survives the move. `WeakRef` for the same reason as in the register — a slot that is gone
18
+ // is held by nobody here.
19
+ #slots = /* @__PURE__ */ new Set();
20
+ constructor(owner) {
21
+ this.#owner = owner;
22
+ }
23
+ // Whether this element is the closest entity above `slot`. The ascent goes over `parentElement`
24
+ // and nothing else, and both callers stand on the same ground for it: `slotchange` bubbles along
25
+ // the node tree of one shadow root, so the slot and every entity that can hear it sit in the same
26
+ // tree, and a slot found by `querySelectorAll` never leaves the node tree it was searched in
27
+ // either. The flattened parent is not the question here. Without the test every entity of the
28
+ // chain would write the register and the outermost one would win — the answer a projected node
29
+ // gets comes from the closest.
30
+ #isClosestEntAbove(slot) {
31
+ for (let current = slot.parentElement; current != null; current = current.parentElement) {
32
+ if (current === this.#owner) return true;
33
+ if (current.isShaeEntElement) return false;
34
+ }
35
+ return false;
36
+ }
37
+ #watch(slot) {
38
+ for (const ref of this.#slots) {
39
+ const el = ref.deref();
40
+ if (el === void 0) this.#slots.delete(ref);
41
+ else if (el === slot) return;
42
+ }
43
+ this.#slots.add(new WeakRef(slot));
44
+ slot.addEventListener("slotchange", this.#onSlotChange, { capture: false, passive: false });
45
+ }
46
+ #release(slot) {
47
+ slot.removeEventListener("slotchange", this.#onSlotChange, { capture: false });
48
+ for (const ref of this.#slots) {
49
+ const el = ref.deref();
50
+ if (el === void 0 || el === slot) this.#slots.delete(ref);
51
+ }
52
+ }
53
+ // `currentTarget` rather than `target`: a `<slot>` can stand in the fallback content of another
54
+ // slot, and what reports then is the inner one while the slot this listener hangs on is the one
55
+ // in question. The early return on "still mine" hands the whole job — register and round alike —
56
+ // to the bubbling listener, which would otherwise do it a second time. And the `null` entry: a
57
+ // slot that comes back to this entity has changed hands twice, and the round has to run for the
58
+ // second change as well.
59
+ #onSlotChange = (event) => {
60
+ const slot = event.currentTarget;
61
+ if (this.#isClosestEntAbove(slot)) return;
62
+ this.#release(slot);
63
+ if (entHostOfSlot.get(slot)?.deref() === this.#owner) {
64
+ entHostOfSlot.set(slot, null);
65
+ }
66
+ askEveryoneToReRequest(event);
67
+ };
68
+ /**
69
+ * Take up the `<slot>`s below this element that project something.
70
+ *
71
+ * A slot is answered for from the moment it reports an assignment, and an entity leaving the
72
+ * tree lets go of every slot it holds. Entering the tree is the counterpart: the entity takes
73
+ * up the slots below it, because the assignment inside a shadow root does not change while
74
+ * its host is out of the document — nothing reports on the way back in, so the entity has to
75
+ * look for itself.
76
+ *
77
+ * Only inside a shadow root, and that test is exact rather than an optimization: a `<slot>`
78
+ * in a node tree that is not a shadow tree has no assignment and never reports one, so there
79
+ * is nothing below such an element to take up.
80
+ */
81
+ collect() {
82
+ if (this.#owner.findShadowRootHost() == null) return;
83
+ for (const slot of this.#owner.querySelectorAll("slot")) {
84
+ if (slot.assignedNodes().length === 0) continue;
85
+ if (!this.#isClosestEntAbove(slot)) continue;
86
+ entHostOfSlot.set(slot, new WeakRef(this.#owner));
87
+ this.#watch(slot);
88
+ }
89
+ }
90
+ /**
91
+ * Take up the slot a `slotchange` names, if this element is the closest entity above it.
92
+ *
93
+ * `event.target` is the slot that reports, which is what the bubbling channel at the element
94
+ * carries — a different reading from the one {@link HostedSlots} makes of a `slotchange` on a
95
+ * slot it listens to itself, where the slot in question is the one the listener hangs on.
96
+ */
97
+ takeUp(event) {
98
+ const slot = event.target;
99
+ if (this.#isClosestEntAbove(slot)) {
100
+ const previous = entHostOfSlot.get(slot);
101
+ entHostOfSlot.set(slot, new WeakRef(this.#owner));
102
+ this.#watch(slot);
103
+ if (previous !== void 0 && previous?.deref() !== this.#owner) {
104
+ askEveryoneToReRequest(event);
105
+ }
106
+ }
107
+ }
108
+ releaseAll() {
109
+ for (const ref of this.#slots) {
110
+ ref.deref()?.removeEventListener("slotchange", this.#onSlotChange, { capture: false });
111
+ }
112
+ this.#slots.clear();
113
+ }
114
+ }
115
+ export {
116
+ HostedSlots
117
+ };
118
+ //# sourceMappingURL=hostedSlots.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/elements/hostedSlots.ts"],
4
+ "sourcesContent": ["import {ComponentContext} from '../view/ComponentContext.js';\nimport type {ShaeEntElement} from './ShaeEntElement.js';\n\n/**\n * The entity that answers for a `<slot>`, written on either of the two occasions an entity comes\n * to answer for one: the slot reports an assignment and the closest entity above it takes it up,\n * or an entity enters the tree and takes up the slots below it. The reporting writer takes the\n * slot as it finds it: `slotchange` announces every change of an assignment, one that has just\n * become empty included, so an entry can name a slot that projects nothing at this moment. What\n * no entry ever names is a slot that has not reported at all \u2014 and the entering writer holds that\n * line by skipping a slot with an empty assignment, because a name put on such a slot would let\n * its first report, under whatever entity it sits below by then, read as a change of hands.\n *\n * `slotchange` fires after the move and therefore at the new location; the place the slot came\n * from cannot be read from there any more \u2014 this register is the only place where it still has a\n * name. A `WeakMap` so a slot that is gone holds nothing, and a `WeakRef` as its value so a slot\n * that stays does not hold the entity it left: a slot can outlive its entity, and an entry read\n * back as empty is one more way of saying \"not the entity asking now\", which is the only thing\n * the register is ever asked.\n *\n * An entry of `null` names no entity and is still an entry: it says that the entity which answered\n * for this slot has let go of it. That is a different statement from `undefined`, which is a slot\n * nobody has ever reported.\n */\nconst entHostOfSlot = new WeakMap<Element, WeakRef<ShaeEntElement> | null>();\n\n/** The `slotchange` events whose re-request round has already run. */\nconst reRequestedForSlotChange = new WeakSet<Event>();\n\n/**\n * Everything the slot projects hangs on the entity above the slot, and the slot just took a\n * different one. There is no named counterpart to inform: the projected nodes can sit in any\n * namespace, below any entity, and the entities involved are not reachable from here. So the\n * request goes to every candidate there is, in every namespace: a property binds to the closest\n * entity above it whatever namespace that entity carries, and an entity from another namespace can\n * be projected through the same slot.\n *\n * Both sides of a move see the same event \u2014 the entity losing the slot as a listener on the slot\n * itself, the one gaining it while the event bubbles \u2014 and the round either of them would start is\n * the same round over the whole document. Whoever gets here first runs it.\n */\nconst askEveryoneToReRequest = (event: Event): void => {\n if (reRequestedForSlotChange.has(event)) return;\n reRequestedForSlotChange.add(event);\n for (const context of ComponentContext.getContextsMap().values()) {\n context.broadcastEvent(ComponentContext.ReRequestParent);\n context.broadcastEvent(ComponentContext.ReRequestEntHost);\n }\n};\n\n/**\n * The `<slot>`s one entity element answers for.\n *\n * The owner asks it to take up what it finds \u2014 {@link HostedSlots.collect} on the way into the\n * tree, {@link HostedSlots.takeUp} for a slot that reports \u2014 and to let go of everything at\n * once with {@link HostedSlots.releaseAll}. Everything beyond that it decides for itself.\n */\nexport class HostedSlots {\n readonly #owner: ShaeEntElement;\n\n // The `<slot>`s this element currently answers for. A listener on the slot itself is the only\n // thing that tells the side losing a slot about the loss: `slotchange` fires at the slot,\n // wherever it has landed, and the ascent from there is the one reading of \"is this still mine\"\n // that survives the move. `WeakRef` for the same reason as in the register \u2014 a slot that is gone\n // is held by nobody here.\n readonly #slots = new Set<WeakRef<Element>>();\n\n constructor(owner: ShaeEntElement) {\n this.#owner = owner;\n }\n\n // Whether this element is the closest entity above `slot`. The ascent goes over `parentElement`\n // and nothing else, and both callers stand on the same ground for it: `slotchange` bubbles along\n // the node tree of one shadow root, so the slot and every entity that can hear it sit in the same\n // tree, and a slot found by `querySelectorAll` never leaves the node tree it was searched in\n // either. The flattened parent is not the question here. Without the test every entity of the\n // chain would write the register and the outermost one would win \u2014 the answer a projected node\n // gets comes from the closest.\n #isClosestEntAbove(slot: Element): boolean {\n for (let current = slot.parentElement; current != null; current = current.parentElement) {\n if (current === this.#owner) return true;\n if ((current as ShaeEntElement).isShaeEntElement) return false;\n }\n return false;\n }\n\n #watch(slot: Element) {\n for (const ref of this.#slots) {\n const el = ref.deref();\n if (el === undefined) this.#slots.delete(ref);\n else if (el === slot) return;\n }\n this.#slots.add(new WeakRef(slot));\n slot.addEventListener('slotchange', this.#onSlotChange, {capture: false, passive: false});\n }\n\n #release(slot: Element) {\n slot.removeEventListener('slotchange', this.#onSlotChange, {capture: false});\n for (const ref of this.#slots) {\n const el = ref.deref();\n if (el === undefined || el === slot) this.#slots.delete(ref);\n }\n }\n\n // `currentTarget` rather than `target`: a `<slot>` can stand in the fallback content of another\n // slot, and what reports then is the inner one while the slot this listener hangs on is the one\n // in question. The early return on \"still mine\" hands the whole job \u2014 register and round alike \u2014\n // to the bubbling listener, which would otherwise do it a second time. And the `null` entry: a\n // slot that comes back to this entity has changed hands twice, and the round has to run for the\n // second change as well.\n #onSlotChange = (event: Event) => {\n const slot = event.currentTarget as Element;\n if (this.#isClosestEntAbove(slot)) return;\n\n this.#release(slot);\n if (entHostOfSlot.get(slot)?.deref() === this.#owner) {\n entHostOfSlot.set(slot, null);\n }\n askEveryoneToReRequest(event);\n };\n\n /**\n * Take up the `<slot>`s below this element that project something.\n *\n * A slot is answered for from the moment it reports an assignment, and an entity leaving the\n * tree lets go of every slot it holds. Entering the tree is the counterpart: the entity takes\n * up the slots below it, because the assignment inside a shadow root does not change while\n * its host is out of the document \u2014 nothing reports on the way back in, so the entity has to\n * look for itself.\n *\n * Only inside a shadow root, and that test is exact rather than an optimization: a `<slot>`\n * in a node tree that is not a shadow tree has no assignment and never reports one, so there\n * is nothing below such an element to take up.\n */\n collect() {\n if (this.#owner.findShadowRootHost() == null) return;\n\n for (const slot of this.#owner.querySelectorAll('slot')) {\n if (slot.assignedNodes().length === 0) continue;\n if (!this.#isClosestEntAbove(slot)) continue;\n entHostOfSlot.set(slot, new WeakRef(this.#owner));\n this.#watch(slot);\n }\n }\n\n /**\n * Take up the slot a `slotchange` names, if this element is the closest entity above it.\n *\n * `event.target` is the slot that reports, which is what the bubbling channel at the element\n * carries \u2014 a different reading from the one {@link HostedSlots} makes of a `slotchange` on a\n * slot it listens to itself, where the slot in question is the one the listener hangs on.\n */\n takeUp(event: Event) {\n const slot = event.target as Element;\n if (this.#isClosestEntAbove(slot)) {\n const previous = entHostOfSlot.get(slot);\n entHostOfSlot.set(slot, new WeakRef(this.#owner));\n this.#watch(slot);\n // the gate in front of the round, and it is closed twice. What a slot reporting for the\n // first time projects is reached by the two calls that frame this block, so the first\n // registration writes the register and pays nothing beyond it. Afterwards a slot whose\n // entity above it is the same one as last time reports changed content, and content moves\n // no binding. What is left is the slot that arrived here from somewhere else \u2014 and an entry\n // naming nobody is such an arrival too: that slot stood under no entity in between\n if (previous !== undefined && previous?.deref() !== this.#owner) {\n askEveryoneToReRequest(event);\n }\n }\n }\n\n releaseAll() {\n for (const ref of this.#slots) {\n ref.deref()?.removeEventListener('slotchange', this.#onSlotChange, {capture: false});\n }\n this.#slots.clear();\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Run `onRemoved` once `node` is taken out of `parent`'s child list.
3
+ *
4
+ * A second call for the same pair replaces the callback. The watch ends the moment it fires
5
+ * — whoever wants to go on watching says so from inside the callback.
6
+ *
7
+ * A shared observer can already be sitting on a `childList` record from earlier in the same task
8
+ * — a sibling taken out a moment ago, still waiting for the microtask that delivers observer
9
+ * callbacks. Registering `node` now must not let that stale record apply to it once delivery
10
+ * catches up, so pending records are taken out and dispatched first, against the watchers this
11
+ * call still finds. `node` itself is not among them yet, so it cannot be the one a stale record
12
+ * matches — the only nodes a `takeRecords()` batch can name here are ones some earlier call
13
+ * already put in `parent`'s child list and that have since left it again.
14
+ */
15
+ export declare const watchForRemovalFrom: (parent: Node, node: Node, onRemoved: () => void) => void;
16
+ /** Stop watching `node` under `parent`. A pair nobody watches is left alone. */
17
+ export declare const stopWatchingForRemovalFrom: (parent: Node, node: Node) => void;
18
+ //# sourceMappingURL=parentRemoval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parentRemoval.d.ts","sourceRoot":"","sources":["../../../src/elements/parentRemoval.ts"],"names":[],"mappings":"AA8EA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,WAAY,IAAI,QAAQ,IAAI,aAAa,MAAM,IAAI,KAAG,IAerF,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,0BAA0B,WAAY,IAAI,QAAQ,IAAI,KAAG,IAQrE,CAAC"}
@@ -0,0 +1,60 @@
1
+ const parentWatches = /* @__PURE__ */ new WeakMap();
2
+ const dispatchRemovals = (watch, mutations) => {
3
+ const removed = [];
4
+ for (const { removedNodes } of mutations) {
5
+ for (const node of removedNodes) {
6
+ const onRemoved = watch.watchers.get(node);
7
+ if (onRemoved == null) continue;
8
+ watch.watchers.delete(node);
9
+ removed.push(onRemoved);
10
+ }
11
+ }
12
+ for (const onRemoved of removed) {
13
+ try {
14
+ onRemoved();
15
+ } catch (error) {
16
+ console.error("a removal watcher failed:", error);
17
+ }
18
+ }
19
+ };
20
+ const createParentWatch = (parent) => {
21
+ const watchers = /* @__PURE__ */ new Map();
22
+ const watch = {
23
+ observer: new MutationObserver((mutations, activeObserver) => {
24
+ dispatchRemovals(watch, mutations);
25
+ if (watchers.size === 0) {
26
+ activeObserver.disconnect();
27
+ if (parentWatches.get(parent) === watch) parentWatches.delete(parent);
28
+ }
29
+ }),
30
+ watchers
31
+ };
32
+ watch.observer.observe(parent, { childList: true, subtree: false, attributes: false });
33
+ return watch;
34
+ };
35
+ const watchForRemovalFrom = (parent, node, onRemoved) => {
36
+ let watch = parentWatches.get(parent);
37
+ if (watch != null) {
38
+ dispatchRemovals(watch, watch.observer.takeRecords());
39
+ watch = parentWatches.get(parent);
40
+ }
41
+ if (watch == null) {
42
+ watch = createParentWatch(parent);
43
+ parentWatches.set(parent, watch);
44
+ }
45
+ watch.watchers.set(node, onRemoved);
46
+ };
47
+ const stopWatchingForRemovalFrom = (parent, node) => {
48
+ const watch = parentWatches.get(parent);
49
+ if (watch == null) return;
50
+ if (!watch.watchers.delete(node)) return;
51
+ if (watch.watchers.size === 0) {
52
+ watch.observer.disconnect();
53
+ parentWatches.delete(parent);
54
+ }
55
+ };
56
+ export {
57
+ stopWatchingForRemovalFrom,
58
+ watchForRemovalFrom
59
+ };
60
+ //# sourceMappingURL=parentRemoval.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/elements/parentRemoval.ts"],
4
+ "sourcesContent": ["interface ParentWatch {\n observer: MutationObserver;\n /** What to run for each watched child of this parent, keyed by that child. */\n watchers: Map<Node, () => void>;\n}\n\n/**\n * One `MutationObserver` per watched parent node, however many nodes are watched on it.\n *\n * A `childList` observation belongs to the observer and not to the child it was taken out\n * for: n observers on one node see every mutation of that node n times, and each of them\n * walks the whole `removedNodes` list. Emptying a list of n siblings costs n\u00B2 callback runs\n * that way. One observer per node costs one run per mutation, and the lookup behind it is\n * keyed by the removed node \u2014 a set that is small against the number of siblings.\n *\n * The map is keyed by the parent node, so an entry cannot outlive the node it belongs to.\n * What it holds are children of that node, which the node already holds itself.\n */\nconst parentWatches = new WeakMap<Node, ParentWatch>();\n\n/**\n * Take every callback whose node shows up in `mutations`' `removedNodes` out of `watch.watchers`\n * and run it.\n *\n * Taken off before it runs, and that order is the whole point: the callback decides where its\n * node is watched next, and an entry deleted afterwards would take that answer with it. It is\n * also what makes a node that appears twice in one batch \u2014 taken out, put back, taken out again \u2014\n * run its callback once. A callback runs guarded: a `MutationObserver` callback that throws\n * reaches no caller of its own, and a watcher is code this module does not own \u2014 `onParentChanged`\n * is a documented extension point, called on every watcher in the same batch whether or not one of\n * them throws.\n */\nconst dispatchRemovals = (watch: ParentWatch, mutations: MutationRecord[]): void => {\n const removed: Array<() => void> = [];\n\n for (const {removedNodes} of mutations) {\n for (const node of removedNodes) {\n const onRemoved = watch.watchers.get(node);\n if (onRemoved == null) continue;\n watch.watchers.delete(node);\n removed.push(onRemoved);\n }\n }\n\n for (const onRemoved of removed) {\n try {\n onRemoved();\n } catch (error) {\n console.error('a removal watcher failed:', error);\n }\n }\n};\n\nconst createParentWatch = (parent: Node): ParentWatch => {\n const watchers = new Map<Node, () => void>();\n\n const watch: ParentWatch = {\n observer: new MutationObserver((mutations, activeObserver) => {\n dispatchRemovals(watch, mutations);\n\n // behind the callbacks, never in front of them: one that puts its node back under this\n // same parent keeps the observation standing instead of paying for a fresh one\n if (watchers.size === 0) {\n activeObserver.disconnect();\n // a callback above can have watched this same parent again on its own, which builds a\n // second watch and replaces this one in the registry before this line runs \u2014 deleting\n // unconditionally would then throw away that live entry instead of this dead one\n if (parentWatches.get(parent) === watch) parentWatches.delete(parent);\n }\n }),\n watchers,\n };\n\n watch.observer.observe(parent, {childList: true, subtree: false, attributes: false});\n\n return watch;\n};\n\n/**\n * Run `onRemoved` once `node` is taken out of `parent`'s child list.\n *\n * A second call for the same pair replaces the callback. The watch ends the moment it fires\n * \u2014 whoever wants to go on watching says so from inside the callback.\n *\n * A shared observer can already be sitting on a `childList` record from earlier in the same task\n * \u2014 a sibling taken out a moment ago, still waiting for the microtask that delivers observer\n * callbacks. Registering `node` now must not let that stale record apply to it once delivery\n * catches up, so pending records are taken out and dispatched first, against the watchers this\n * call still finds. `node` itself is not among them yet, so it cannot be the one a stale record\n * matches \u2014 the only nodes a `takeRecords()` batch can name here are ones some earlier call\n * already put in `parent`'s child list and that have since left it again.\n */\nexport const watchForRemovalFrom = (parent: Node, node: Node, onRemoved: () => void): void => {\n let watch = parentWatches.get(parent);\n if (watch != null) {\n dispatchRemovals(watch, watch.observer.takeRecords());\n // a callback the dispatch just ran can have unwatched the last other node on this same\n // parent, which tears the watch down and drops it from the registry \u2014 resolving the watch\n // again after the dispatch, instead of trusting the one this call started with, is what\n // makes `node` land in whichever watch is live once that outside code has had its turn\n watch = parentWatches.get(parent);\n }\n if (watch == null) {\n watch = createParentWatch(parent);\n parentWatches.set(parent, watch);\n }\n watch.watchers.set(node, onRemoved);\n};\n\n/** Stop watching `node` under `parent`. A pair nobody watches is left alone. */\nexport const stopWatchingForRemovalFrom = (parent: Node, node: Node): void => {\n const watch = parentWatches.get(parent);\n if (watch == null) return;\n if (!watch.watchers.delete(node)) return;\n if (watch.watchers.size === 0) {\n watch.observer.disconnect();\n parentWatches.delete(parent);\n }\n};\n"],
5
+ "mappings": "AAkBA,MAAM,gBAAgB,oBAAI,QAA2B;AAcrD,MAAM,mBAAmB,CAAC,OAAoB,cAAsC;AAClF,QAAM,UAA6B,CAAC;AAEpC,aAAW,EAAC,aAAY,KAAK,WAAW;AACtC,eAAW,QAAQ,cAAc;AAC/B,YAAM,YAAY,MAAM,SAAS,IAAI,IAAI;AACzC,UAAI,aAAa,KAAM;AACvB,YAAM,SAAS,OAAO,IAAI;AAC1B,cAAQ,KAAK,SAAS;AAAA,IACxB;AAAA,EACF;AAEA,aAAW,aAAa,SAAS;AAC/B,QAAI;AACF,gBAAU;AAAA,IACZ,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAAA,IAClD;AAAA,EACF;AACF;AAEA,MAAM,oBAAoB,CAAC,WAA8B;AACvD,QAAM,WAAW,oBAAI,IAAsB;AAE3C,QAAM,QAAqB;AAAA,IACzB,UAAU,IAAI,iBAAiB,CAAC,WAAW,mBAAmB;AAC5D,uBAAiB,OAAO,SAAS;AAIjC,UAAI,SAAS,SAAS,GAAG;AACvB,uBAAe,WAAW;AAI1B,YAAI,cAAc,IAAI,MAAM,MAAM,MAAO,eAAc,OAAO,MAAM;AAAA,MACtE;AAAA,IACF,CAAC;AAAA,IACD;AAAA,EACF;AAEA,QAAM,SAAS,QAAQ,QAAQ,EAAC,WAAW,MAAM,SAAS,OAAO,YAAY,MAAK,CAAC;AAEnF,SAAO;AACT;AAgBO,MAAM,sBAAsB,CAAC,QAAc,MAAY,cAAgC;AAC5F,MAAI,QAAQ,cAAc,IAAI,MAAM;AACpC,MAAI,SAAS,MAAM;AACjB,qBAAiB,OAAO,MAAM,SAAS,YAAY,CAAC;AAKpD,YAAQ,cAAc,IAAI,MAAM;AAAA,EAClC;AACA,MAAI,SAAS,MAAM;AACjB,YAAQ,kBAAkB,MAAM;AAChC,kBAAc,IAAI,QAAQ,KAAK;AAAA,EACjC;AACA,QAAM,SAAS,IAAI,MAAM,SAAS;AACpC;AAGO,MAAM,6BAA6B,CAAC,QAAc,SAAqB;AAC5E,QAAM,QAAQ,cAAc,IAAI,MAAM;AACtC,MAAI,SAAS,KAAM;AACnB,MAAI,CAAC,MAAM,SAAS,OAAO,IAAI,EAAG;AAClC,MAAI,MAAM,SAAS,SAAS,GAAG;AAC7B,UAAM,SAAS,WAAW;AAC1B,kBAAc,OAAO,MAAM;AAAA,EAC7B;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,3 @@
1
+ export type PropValueConverter = (value: string) => unknown;
2
+ export declare const propValueConverters: ReadonlyMap<string, PropValueConverter>;
3
+ //# sourceMappingURL=propValueConverters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propValueConverters.d.ts","sourceRoot":"","sources":["../../../src/elements/propValueConverters.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAuF5D,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAEvE,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { TRUTHY_VALUES } from "../utils/constants.js";
2
+ const words = (value) => value.split(/\W+/);
3
+ const fields = (value) => value.split(/\s+/);
4
+ const assertNumber = (value, token) => {
5
+ if (Number.isNaN(value)) {
6
+ throw new TypeError(`not a number: "${token}"`);
7
+ }
8
+ return value;
9
+ };
10
+ const toNumber = (parse) => (value) => assertNumber(parse(value), value);
11
+ const toNumberList = (parse, split) => (value) => split(value).map((v) => assertNumber(parse(v), v));
12
+ const toRadix = (base) => toNumber((value) => parseInt(value, base));
13
+ const toRadixList = (base) => toNumberList((value) => parseInt(value, base), words);
14
+ const toNumericArray = (Ctor, split) => (value) => new Ctor(split(value).map((v) => assertNumber(Number(v), v)));
15
+ const toBigIntArray = (Ctor) => (value) => new Ctor(words(value).map((v) => BigInt(v)));
16
+ const toBoolean = (value) => TRUTHY_VALUES.has(value.toLowerCase());
17
+ const CONVERTER_GROUPS = [
18
+ [["string", "text"], (value) => value],
19
+ [["number"], toNumber(Number)],
20
+ [["bigint"], (value) => BigInt(value)],
21
+ [["float"], toNumber(parseFloat)],
22
+ [["int", "integer"], toRadix(10)],
23
+ [["hex", "hexadecimal"], toRadix(16)],
24
+ [["oct", "octal"], toRadix(8)],
25
+ [["bin", "binary"], toRadix(2)],
26
+ [["bool", "boolean"], toBoolean],
27
+ [["[]", "text[]", "string[]"], words],
28
+ [["number[]"], toNumberList(Number, fields)],
29
+ [["float[]"], toNumberList(parseFloat, fields)],
30
+ [["int[]", "integer[]"], toNumberList((value) => parseInt(value, 10), fields)],
31
+ [["hex[]", "hexadecimal[]"], toRadixList(16)],
32
+ [["oct[]", "octal[]"], toRadixList(8)],
33
+ [["bin[]", "binary[]"], toRadixList(2)],
34
+ [["bool[]", "boolean[]"], (value) => words(value).map((v) => TRUTHY_VALUES.has(v.toLowerCase()))],
35
+ [["int8array"], toNumericArray(Int8Array, words)],
36
+ [["uint8array"], toNumericArray(Uint8Array, words)],
37
+ [["uint8clampedarray"], toNumericArray(Uint8ClampedArray, words)],
38
+ [["int16array"], toNumericArray(Int16Array, words)],
39
+ [["uint16array"], toNumericArray(Uint16Array, words)],
40
+ [["int32array"], toNumericArray(Int32Array, words)],
41
+ [["uint32array"], toNumericArray(Uint32Array, words)],
42
+ [["float32array"], toNumericArray(Float32Array, fields)],
43
+ [["float64array"], toNumericArray(Float64Array, fields)],
44
+ [["bigint64array"], toBigIntArray(BigInt64Array)],
45
+ [["biguint64array"], toBigIntArray(BigUint64Array)],
46
+ [["json"], (value) => JSON.parse(value)]
47
+ ];
48
+ const propValueConverters = new Map(
49
+ CONVERTER_GROUPS.flatMap(([names, convert]) => names.map((name) => [name, convert]))
50
+ );
51
+ export {
52
+ propValueConverters
53
+ };
54
+ //# sourceMappingURL=propValueConverters.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/elements/propValueConverters.ts"],
4
+ "sourcesContent": ["import {TRUTHY_VALUES} from '../utils/constants.js';\n\nexport type PropValueConverter = (value: string) => unknown;\n\nconst words = (value: string): string[] => value.split(/\\W+/);\nconst fields = (value: string): string[] => value.split(/\\s+/);\n\ntype NumberParser = (value: string) => number;\ntype Splitter = (value: string) => string[];\n\n// The check sits here, ahead of every constructor: `new Int8Array([NaN])` is `[0]` and\n// `new Float32Array([NaN])` carries the `NaN` into its buffer, so once the array exists nothing\n// separates a token that could not be read from one that was meant. A numeric branch answers with\n// a number or it answers with nothing \u2014 the token is named rather than the whole attribute value,\n// so a list says which of its segments does not carry.\nconst assertNumber = (value: number, token: string): number => {\n if (Number.isNaN(value)) {\n throw new TypeError(`not a number: \"${token}\"`);\n }\n return value;\n};\n\nconst toNumber =\n (parse: NumberParser): PropValueConverter =>\n (value) =>\n assertNumber(parse(value), value);\n\nconst toNumberList =\n (parse: NumberParser, split: Splitter): PropValueConverter =>\n (value) =>\n split(value).map((v) => assertNumber(parse(v), v));\n\nconst toRadix = (base: number): PropValueConverter => toNumber((value) => parseInt(value, base));\n\nconst toRadixList = (base: number): PropValueConverter => toNumberList((value) => parseInt(value, base), words);\n\ntype NumericArrayCtor = new (values: number[]) => ArrayBufferView;\ntype BigIntArrayCtor = new (values: bigint[]) => ArrayBufferView;\n\nconst toNumericArray =\n (Ctor: NumericArrayCtor, split: Splitter): PropValueConverter =>\n (value) =>\n new Ctor(split(value).map((v) => assertNumber(Number(v), v)));\n\nconst toBigIntArray =\n (Ctor: BigIntArrayCtor): PropValueConverter =>\n (value) =>\n new Ctor(words(value).map((v) => BigInt(v)));\n\nconst toBoolean: PropValueConverter = (value) => TRUTHY_VALUES.has(value.toLowerCase());\n\n// 29 groups: the names a group serves, then the one function they share. Alias marks (e.g.\n// `int`/`integer`) point at the same function object on purpose \u2014 the identity is structural, not\n// a matter of discipline. Every row therefore builds its converter in a single call.\nconst CONVERTER_GROUPS: ReadonlyArray<readonly [readonly string[], PropValueConverter]> = [\n [['string', 'text'], (value) => value],\n [['number'], toNumber(Number)],\n [['bigint'], (value) => BigInt(value)],\n [['float'], toNumber(parseFloat)],\n [['int', 'integer'], toRadix(10)],\n [['hex', 'hexadecimal'], toRadix(16)],\n [['oct', 'octal'], toRadix(8)],\n [['bin', 'binary'], toRadix(2)],\n [['bool', 'boolean'], toBoolean],\n [['[]', 'text[]', 'string[]'], words],\n [['number[]'], toNumberList(Number, fields)],\n [['float[]'], toNumberList(parseFloat, fields)],\n [['int[]', 'integer[]'], toNumberList((value) => parseInt(value, 10), fields)],\n [['hex[]', 'hexadecimal[]'], toRadixList(16)],\n [['oct[]', 'octal[]'], toRadixList(8)],\n [['bin[]', 'binary[]'], toRadixList(2)],\n [['bool[]', 'boolean[]'], (value) => words(value).map((v) => TRUTHY_VALUES.has(v.toLowerCase()))],\n [['int8array'], toNumericArray(Int8Array, words)],\n [['uint8array'], toNumericArray(Uint8Array, words)],\n [['uint8clampedarray'], toNumericArray(Uint8ClampedArray, words)],\n [['int16array'], toNumericArray(Int16Array, words)],\n [['uint16array'], toNumericArray(Uint16Array, words)],\n [['int32array'], toNumericArray(Int32Array, words)],\n [['uint32array'], toNumericArray(Uint32Array, words)],\n [['float32array'], toNumericArray(Float32Array, fields)],\n [['float64array'], toNumericArray(Float64Array, fields)],\n [['bigint64array'], toBigIntArray(BigInt64Array)],\n [['biguint64array'], toBigIntArray(BigUint64Array)],\n [['json'], (value) => JSON.parse(value)],\n];\n\n// A `Map`, not an object literal: a type name is whatever string stands in the `type` attribute,\n// and `'toString' in {}` is `true` \u2014 an object literal would answer to inherited keys it never\n// declared.\nexport const propValueConverters: ReadonlyMap<string, PropValueConverter> = new Map(\n CONVERTER_GROUPS.flatMap(([names, convert]) => names.map((name) => [name, convert] as const)),\n);\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,32 @@
1
+ import type { NamespaceType } from '../types.js';
2
+ import type { ShaeEntElement } from './ShaeEntElement.js';
3
+ /**
4
+ * The request an element sends out to find the entity above it.
5
+ *
6
+ * It travels as the `detail` of a bubbling, composed `shaeRequestEntParent` event. The first
7
+ * ancestor that matches calls {@link EntAncestorRequest.answer} and stops the event there, so the
8
+ * answer always comes from the closest match and never from more than one.
9
+ */
10
+ export interface EntAncestorRequest {
11
+ /** the element asking */
12
+ requester: HTMLElement;
13
+ /** the namespace the answer has to match — `undefined` lets any ancestor answer */
14
+ ns?: NamespaceType;
15
+ /** called by the first ancestor that matches; it stops the event right afterwards */
16
+ answer(entNode: ShaeEntElement): void;
17
+ }
18
+ /**
19
+ * Ask the closest entity above `requester` to identify itself.
20
+ *
21
+ * The path of a composed event *is* the ascent through the flattened tree, and it is the only one
22
+ * that sees all of it: `assignedSlot` reports `null` at a closed boundary, so an ascent along node
23
+ * pointers steps from a projected node straight to the host and skips the entire closed tree —
24
+ * the event travels through it.
25
+ *
26
+ * Both `<shae-ent>` and `<shae-prop>` therefore send this one request, and there is exactly one
27
+ * place that answers it.
28
+ *
29
+ * @see https://pm.dartus.fr/blog/a-complete-guide-on-shadow-dom-and-event-propagation/
30
+ */
31
+ export declare const requestEntAncestor: (requester: HTMLElement, request: Omit<EntAncestorRequest, 'requester'>) => void;
32
+ //# sourceMappingURL=requestEntAncestor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestEntAncestor.d.ts","sourceRoot":"","sources":["../../../src/elements/requestEntAncestor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAC;AAE/C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,yBAAyB;IACzB,SAAS,EAAE,WAAW,CAAC;IAEvB,mFAAmF;IACnF,EAAE,CAAC,EAAE,aAAa,CAAC;IAEnB,qFAAqF;IACrF,MAAM,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,cAAe,WAAW,WAAW,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,KAAG,IAU3G,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { RequestEntParentEventName } from "./constants.js";
2
+ const requestEntAncestor = (requester, request) => {
3
+ requester.dispatchEvent(
4
+ new CustomEvent(RequestEntParentEventName, {
5
+ bubbles: true,
6
+ composed: true,
7
+ // `requester` last: the element sending the request is the one fact about it that no
8
+ // caller gets to overwrite, and a caller not going through TypeScript could try
9
+ detail: { ...request, requester }
10
+ })
11
+ );
12
+ };
13
+ export {
14
+ requestEntAncestor
15
+ };
16
+ //# sourceMappingURL=requestEntAncestor.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/elements/requestEntAncestor.ts"],
4
+ "sourcesContent": ["import type {NamespaceType} from '../types.js';\nimport {RequestEntParentEventName} from './constants.js';\nimport type {ShaeEntElement} from './ShaeEntElement.js';\n\n/**\n * The request an element sends out to find the entity above it.\n *\n * It travels as the `detail` of a bubbling, composed `shaeRequestEntParent` event. The first\n * ancestor that matches calls {@link EntAncestorRequest.answer} and stops the event there, so the\n * answer always comes from the closest match and never from more than one.\n */\nexport interface EntAncestorRequest {\n /** the element asking */\n requester: HTMLElement;\n\n /** the namespace the answer has to match \u2014 `undefined` lets any ancestor answer */\n ns?: NamespaceType;\n\n /** called by the first ancestor that matches; it stops the event right afterwards */\n answer(entNode: ShaeEntElement): void;\n}\n\n/**\n * Ask the closest entity above `requester` to identify itself.\n *\n * The path of a composed event *is* the ascent through the flattened tree, and it is the only one\n * that sees all of it: `assignedSlot` reports `null` at a closed boundary, so an ascent along node\n * pointers steps from a projected node straight to the host and skips the entire closed tree \u2014\n * the event travels through it.\n *\n * Both `<shae-ent>` and `<shae-prop>` therefore send this one request, and there is exactly one\n * place that answers it.\n *\n * @see https://pm.dartus.fr/blog/a-complete-guide-on-shadow-dom-and-event-propagation/\n */\nexport const requestEntAncestor = (requester: HTMLElement, request: Omit<EntAncestorRequest, 'requester'>): void => {\n requester.dispatchEvent(\n new CustomEvent<EntAncestorRequest>(RequestEntParentEventName, {\n bubbles: true,\n composed: true,\n // `requester` last: the element sending the request is the one fact about it that no\n // caller gets to overwrite, and a caller not going through TypeScript could try\n detail: {...request, requester},\n }),\n );\n};\n"],
5
+ "mappings": "AACA,SAAQ,iCAAgC;AAkCjC,MAAM,qBAAqB,CAAC,WAAwB,YAAyD;AAClH,YAAU;AAAA,IACR,IAAI,YAAgC,2BAA2B;AAAA,MAC7D,SAAS;AAAA,MACT,UAAU;AAAA;AAAA;AAAA,MAGV,QAAQ,EAAC,GAAG,SAAS,UAAS;AAAA,IAChC,CAAC;AAAA,EACH;AACF;",
6
+ "names": []
7
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/elements.ts"],
4
- "sourcesContent": ["import './shae-ent.js';\nimport './shae-prop.js';\nimport './shae-worker.js';"],
4
+ "sourcesContent": ["import './shae-ent.js';\nimport './shae-prop.js';\nimport './shae-worker.js';\n"],
5
5
  "mappings": "AAAA,OAAO;AACP,OAAO;AACP,OAAO;",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import { Signal, type SignalReader, type SignalWriter } from '@spearwolf/signalize';
2
- import type { IComponentEvent } from '../types.js';
2
+ import type { ComponentPropertiesType, IComponentEvent } from '../types.js';
3
3
  import { onDestroy } from './events.js';
4
4
  import { Kernel } from './Kernel.js';
5
5
  type ContextNameType = string | symbol;
@@ -24,20 +24,105 @@ export declare class Entity {
24
24
  get children(): readonly Entity[];
25
25
  constructor(kernel: Kernel, uuid: string);
26
26
  traverse(callback: (entity: Entity) => void): void;
27
+ /**
28
+ * Releases everything the entity holds: its properties, its subscriptions, its contexts and its place
29
+ * in the entity tree.
30
+ *
31
+ * It runs once, whichever way it is reached. The kernel reaches it directly, right behind the
32
+ * destruction notification it sends on the entity, so that every listener on that notification has
33
+ * had its turn first -- down to `Priority.Min`, which is nobody's but the caller's. The entity is
34
+ * not a listener on its own notification: registering it in the constructor would put it ahead of
35
+ * anything registered at the same priority, because eventize breaks a tie by order of registration,
36
+ * and the bulk `off(this)` below would then take the listeners still waiting in that delivery with
37
+ * it. The flag makes a second call cost nothing and keeps every step below written for a
38
+ * single run: `kernel.getEntity()` hands the entity to anyone, and this method is as reachable as
39
+ * the rest of it.
40
+ *
41
+ * The flag is raised before the first step rather than after the last, because that is what a release
42
+ * running twice would cost: the steps below are written for one pass, not for a repeat.
43
+ *
44
+ * Every step below runs behind a guard of its own, the way `ShadowObjectCreationScope.tearDown()`
45
+ * gives each of its steps one: a step that throws is reported through the kernel's logger, named by
46
+ * the step and by the uuid of this entity, and costs only itself -- the steps behind it still run.
47
+ * The method therefore never throws to its own caller. The plain resets at the end -- clearing a
48
+ * collection, overwriting a field -- cannot throw and run unguarded, exactly as `tearDown()` empties
49
+ * its own sets at the end.
50
+ */
27
51
  [onDestroy](): void;
52
+ /**
53
+ * Refuses a parent that would close the entity tree into a ring: the entity itself, or an entity that
54
+ * already sits below it. The walk follows the parent chain upwards, which is a chain exactly because
55
+ * this guard keeps it one.
56
+ *
57
+ * `Kernel.setParent()` calls it before it detaches, and the `parentUuid` setter before it resolves the
58
+ * link, so a refused attachment leaves the entity where it was instead of orphaned halfway through.
59
+ * `ViewComponent.addChild()` guards the same thing on the view side.
60
+ */
61
+ assertAttachableTo(nextParent: Entity): void;
62
+ /**
63
+ * Puts `child` into the children list, at its place among the siblings.
64
+ *
65
+ * Nothing here notifies. Neither this method nor {@link Entity.removeChild} nor a detachment
66
+ * sends anything to a listener on the entity -- the one notification over a moved entity is
67
+ * `onParentChanged`, and `Kernel.setParent()` is the call that sends it.
68
+ */
28
69
  addChild(child: Entity): void;
29
70
  resortChildren(): void;
30
71
  removeChild(child: Entity): void;
31
- removeFromParent(): void;
72
+ /**
73
+ * @param rebindContexts bind every context back to the root of the entity context tree. An entity
74
+ * that carries a context out of a subtree keeps following the value it can still see, rather
75
+ * than freezing on the last one the former parent held. Only a caller that attaches the entity
76
+ * to the next parent in the same breath passes `false` -- that parent binds the contexts itself,
77
+ * and the detour over the root would be visible to `useParentContext()`, which hands out the
78
+ * inherited value without the microtask collector `useContext()` has in front of it.
79
+ */
80
+ removeFromParent(rebindContexts?: boolean): void;
32
81
  get autoDestructionOnParentRemoval(): boolean;
33
82
  set autoDestructionOnParentRemoval(autoDestruct: boolean);
83
+ /**
84
+ * Binds every context of this entity to its current position in the entity tree -- to the parent
85
+ * if there is one, to the root otherwise. `addChild()` and `removeFromParent()` each do this for
86
+ * the entity they move, so a caller needs this only when it changed the position by other means
87
+ * or wants the binding re-established without knowing which of the two ran.
88
+ *
89
+ * Moving an entity through the kernel is not such a case: both directions bring their own binding,
90
+ * and a third one would reach every `useParentContext()` reader as another change.
91
+ */
34
92
  reSubscribeToParentContexts(): void;
35
93
  dispatchMessageToView(type: string, data?: unknown, transferables?: Transferable[], traverseChildren?: boolean): void;
94
+ /**
95
+ * Hands the events the view sent to whoever listens on this entity, one delivery per event.
96
+ *
97
+ * Each delivery is guarded, and the reason is where this call sits: the kernel makes it from
98
+ * inside a change trail, on a path that builds. Unguarded, a single shadow-object that cannot cope
99
+ * with a single view event would leave through `Kernel.run()` and refuse the whole trail -- the
100
+ * same argument that puts a guard around the `onParentChanged` notification, which is a delivery
101
+ * on a building path as well. The guard is per event, so one that fails costs neither the events
102
+ * behind it nor the caller anything; `emitStrict()` inside it makes the same promise one level
103
+ * down, to the listeners of one event.
104
+ */
36
105
  dispatchViewEvents(events: IComponentEvent[]): void;
37
106
  dispatchViewEvent(type: string, data: unknown): void;
38
107
  getPropertyReader<T = unknown>(key: string): SignalReader<T>;
108
+ /**
109
+ * A write head for one property. Every write through it drops the cache behind
110
+ * `truthyProps()`: that cache answers `Kernel.#updateShadowObjects()`, which picks the shadow
111
+ * objects of this entity along the property routes of the registry, and a write the cache does
112
+ * not see routes the entity to a state it no longer has.
113
+ *
114
+ * What comes back is a function of the entity, not the `set` of the signal behind the property.
115
+ * The signalize helpers that take a signal-like -- `isSignal()`, `destroySignal()`, `touch()` --
116
+ * do not recognise it; `getPropertyReader()` hands out the signal's own reader for that.
117
+ *
118
+ * A fresh closure is handed out on every call rather than one cached per key: `SignalAutoMap`
119
+ * hands out "a fresh, live" signal for a key whose signal was destroyed from the outside, so a
120
+ * writer cached against the signal captured on an earlier call could end up closing over a corpse.
121
+ * The call sites this feeds -- `setProperty()` from a change-trail application, at most once per
122
+ * property that actually changed -- do not turn this into a hot path.
123
+ */
39
124
  getPropertyWriter<T = unknown>(key: string): SignalWriter<T>;
40
- setProperties(properties: [string, unknown][]): void;
125
+ setProperties(properties: ComponentPropertiesType): void;
41
126
  setProperty(key: string, value: unknown): void;
42
127
  getProperty<T = unknown>(key: string): T;
43
128
  propKeys(): string[];
@@ -49,6 +134,31 @@ export declare class Entity {
49
134
  useParentContext<T = unknown>(name: ContextNameType): SignalReader<T>;
50
135
  provideContext<T = unknown>(name: ContextNameType): Signal<T>;
51
136
  provideGlobalContext<T = unknown>(name: ContextNameType): Signal<T>;
137
+ /**
138
+ * Attaches a provider signal to the context of `name` on this entity and answers with the release
139
+ * that detaches it again.
140
+ *
141
+ * Every provider of one name writes into the single signal that `provideContext()` hands out, so
142
+ * what a consumer reads is what was written last. The release therefore does two things: it cuts
143
+ * this feed, and it then lets a provider that is still attached write its value once more. Without
144
+ * that second step the context would keep whatever the departure left standing -- the `undefined`
145
+ * of a clearing provider, or the last value of one that opted out of it -- even though this entity
146
+ * still has a provider of the name that says otherwise.
147
+ *
148
+ * A value written straight into the signal from `provideContext()` is not a feed and is therefore
149
+ * overwritten by the next release. Feeding a value in through this method is what makes it survive
150
+ * one.
151
+ */
152
+ attachContextProvider(name: ContextNameType, provider: Signal<any>): () => void;
153
+ /**
154
+ * The counterpart of {@link Entity.attachContextProvider} for the global context of `name`.
155
+ *
156
+ * Within one entity the situation is the same one: every provider feeds the single signal this
157
+ * entity contributes to the kernel-wide chain of that name, and a release hands the name back to a
158
+ * provider that stays. Across entities the chain decides on its own -- it resolves to the first
159
+ * entry that holds something, so an entity whose signal falls empty lets the next one through.
160
+ */
161
+ attachGlobalContextProvider(name: ContextNameType, provider: Signal<any>): () => void;
52
162
  }
53
163
  export {};
54
164
  //# sourceMappingURL=Entity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Entity.d.ts","sourceRoot":"","sources":["../../../src/in-the-dark/Entity.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,MAAM,EAEN,KAAK,YAAY,EACjB,KAAK,YAAY,EAElB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AACjD,OAAO,EAAC,SAAS,EAAc,MAAM,aAAa,CAAC;AACnD,OAAO,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAGnC,KAAK,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AA+BvC;;;;;;GAMG;AACH,qBAAa,MAAM;;IAiBjB,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAOtB;IAED,IAAI,UAAU,IAAI,MAAM,GAAG,SAAS,CAEnC;IAED,IAAI,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAgB5C;IAED,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAK/B;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAEpC;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,QAAQ,IAAI,SAAS,MAAM,EAAE,CAEhC;gBAEW,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAMxC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI;IAO3C,CAAC,SAAS,CAAC;IA6BX,QAAQ,CAAC,KAAK,EAAE,MAAM;IA0BtB,cAAc;IAId,WAAW,CAAC,KAAK,EAAE,MAAM;IAQzB,gBAAgB;IAyBhB,IAAI,8BAA8B,IAAI,OAAO,CAE5C;IAED,IAAI,8BAA8B,CAAC,YAAY,EAAE,OAAO,EAIvD;IAmBD,2BAA2B;IAM3B,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,YAAY,EAAE,EAAE,gBAAgB,UAAQ;IAI5G,kBAAkB,CAAC,MAAM,EAAE,eAAe,EAAE;IAM5C,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;IAQ7C,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC;IAI5D,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC;IAI5D,aAAa,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;IAS7C,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAIvC,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC;IAIxC,QAAQ,IAAI,MAAM,EAAE;IAIpB,WAAW,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;IAMlC,qBAAqB;IAIrB,WAAW,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS;IAiBtC,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO;IAI1C,UAAU,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,eAAe,GAAG,YAAY,CAAC,CAAC,CAAC;IAI/D,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,eAAe,GAAG,YAAY,CAAC,CAAC,CAAC;IAIrE,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAAC,CAAC,CAAC;IAI7D,oBAAoB,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAAC,CAAC,CAAC;CA+CpE"}
1
+ {"version":3,"file":"Entity.d.ts","sourceRoot":"","sources":["../../../src/in-the-dark/Entity.ts"],"names":[],"mappings":"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"}