@spearwolf/shadow-objects 0.33.0 → 0.35.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 (312) hide show
  1. package/CHANGELOG.md +350 -61
  2. package/README.md +66 -13
  3. package/bundle.js +6 -53
  4. package/package.json +15 -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 +26 -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 +13 -2
  22. package/src/constants.d.ts.map +1 -1
  23. package/src/constants.js +7 -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 +61 -2
  44. package/src/elements/ShaePropElement.d.ts.map +1 -1
  45. package/src/elements/ShaePropElement.js +264 -219
  46. package/src/elements/ShaePropElement.js.map +2 -2
  47. package/src/elements/ShaeWorkerElement.d.ts +59 -5
  48. package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
  49. package/src/elements/ShaeWorkerElement.js +271 -73
  50. package/src/elements/ShaeWorkerElement.js.map +2 -2
  51. package/src/elements/constants.d.ts +9 -0
  52. package/src/elements/constants.d.ts.map +1 -1
  53. package/src/elements/constants.js +18 -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 +143 -3
  87. package/src/in-the-dark/Entity.d.ts.map +1 -1
  88. package/src/in-the-dark/Entity.js +330 -63
  89. package/src/in-the-dark/Entity.js.map +3 -3
  90. package/src/in-the-dark/Kernel.d.ts +98 -16
  91. package/src/in-the-dark/Kernel.d.ts.map +1 -1
  92. package/src/in-the-dark/Kernel.js +466 -379
  93. package/src/in-the-dark/Kernel.js.map +3 -3
  94. package/src/in-the-dark/Registry.d.ts +7 -1
  95. package/src/in-the-dark/Registry.d.ts.map +1 -1
  96. package/src/in-the-dark/Registry.js +112 -14
  97. package/src/in-the-dark/Registry.js.map +2 -2
  98. package/src/in-the-dark/ShadowObject.d.ts.map +1 -1
  99. package/src/in-the-dark/ShadowObject.js +1 -0
  100. package/src/in-the-dark/ShadowObject.js.map +2 -2
  101. package/src/in-the-dark/ShadowObjectCreationScope.d.ts +133 -0
  102. package/src/in-the-dark/ShadowObjectCreationScope.d.ts.map +1 -0
  103. package/src/in-the-dark/ShadowObjectCreationScope.js +603 -0
  104. package/src/in-the-dark/ShadowObjectCreationScope.js.map +7 -0
  105. package/src/in-the-dark/SignalsPath.d.ts +2 -0
  106. package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
  107. package/src/in-the-dark/SignalsPath.js +6 -2
  108. package/src/in-the-dark/SignalsPath.js.map +2 -2
  109. package/src/in-the-dark/displayName.d.ts +4 -0
  110. package/src/in-the-dark/displayName.d.ts.map +1 -0
  111. package/src/in-the-dark/displayName.js +5 -0
  112. package/src/in-the-dark/displayName.js.map +7 -0
  113. package/src/in-the-dark/events.js.map +1 -1
  114. package/src/in-the-dark/importModule.d.ts +7 -0
  115. package/src/in-the-dark/importModule.d.ts.map +1 -1
  116. package/src/in-the-dark/importModule.js +5 -2
  117. package/src/in-the-dark/importModule.js.map +2 -2
  118. package/src/index.d.ts +9 -1
  119. package/src/index.d.ts.map +1 -1
  120. package/src/index.js +9 -1
  121. package/src/index.js.map +2 -2
  122. package/src/inspect/createKernelSnapshot.d.ts +17 -0
  123. package/src/inspect/createKernelSnapshot.d.ts.map +1 -0
  124. package/src/inspect/createKernelSnapshot.js +247 -0
  125. package/src/inspect/createKernelSnapshot.js.map +7 -0
  126. package/src/inspect/createViewSnapshot.d.ts +10 -0
  127. package/src/inspect/createViewSnapshot.d.ts.map +1 -0
  128. package/src/inspect/createViewSnapshot.js +143 -0
  129. package/src/inspect/createViewSnapshot.js.map +7 -0
  130. package/src/inspect/normalizeInspectRequest.d.ts +32 -0
  131. package/src/inspect/normalizeInspectRequest.d.ts.map +1 -0
  132. package/src/inspect/normalizeInspectRequest.js +55 -0
  133. package/src/inspect/normalizeInspectRequest.js.map +7 -0
  134. package/src/inspect/serializeValue.d.ts +13 -0
  135. package/src/inspect/serializeValue.d.ts.map +1 -0
  136. package/src/inspect/serializeValue.js +149 -0
  137. package/src/inspect/serializeValue.js.map +7 -0
  138. package/src/inspect/types.d.ts +260 -0
  139. package/src/inspect/types.d.ts.map +1 -0
  140. package/src/inspect/types.js +1 -0
  141. package/src/inspect/types.js.map +7 -0
  142. package/src/model-context/ModelContextLike.d.ts +59 -0
  143. package/src/model-context/ModelContextLike.d.ts.map +1 -0
  144. package/src/model-context/ModelContextLike.js +15 -0
  145. package/src/model-context/ModelContextLike.js.map +7 -0
  146. package/src/model-context/exposeShadowEnvsToModelContext.d.ts +48 -0
  147. package/src/model-context/exposeShadowEnvsToModelContext.d.ts.map +1 -0
  148. package/src/model-context/exposeShadowEnvsToModelContext.js +39 -0
  149. package/src/model-context/exposeShadowEnvsToModelContext.js.map +7 -0
  150. package/src/model-context/redactProps.d.ts +14 -0
  151. package/src/model-context/redactProps.d.ts.map +1 -0
  152. package/src/model-context/redactProps.js +22 -0
  153. package/src/model-context/redactProps.js.map +7 -0
  154. package/src/model-context/sharedExposure.d.ts +32 -0
  155. package/src/model-context/sharedExposure.d.ts.map +1 -0
  156. package/src/model-context/sharedExposure.js +108 -0
  157. package/src/model-context/sharedExposure.js.map +7 -0
  158. package/src/model-context/toolSupport.d.ts +72 -0
  159. package/src/model-context/toolSupport.d.ts.map +1 -0
  160. package/src/model-context/toolSupport.js +145 -0
  161. package/src/model-context/toolSupport.js.map +7 -0
  162. package/src/model-context/tools/findEntities.d.ts +15 -0
  163. package/src/model-context/tools/findEntities.d.ts.map +1 -0
  164. package/src/model-context/tools/findEntities.js +71 -0
  165. package/src/model-context/tools/findEntities.js.map +7 -0
  166. package/src/model-context/tools/getEntity.d.ts +16 -0
  167. package/src/model-context/tools/getEntity.d.ts.map +1 -0
  168. package/src/model-context/tools/getEntity.js +65 -0
  169. package/src/model-context/tools/getEntity.js.map +7 -0
  170. package/src/model-context/tools/getEntityTree.d.ts +4 -0
  171. package/src/model-context/tools/getEntityTree.d.ts.map +1 -0
  172. package/src/model-context/tools/getEntityTree.js +58 -0
  173. package/src/model-context/tools/getEntityTree.js.map +7 -0
  174. package/src/model-context/tools/getRegistry.d.ts +14 -0
  175. package/src/model-context/tools/getRegistry.d.ts.map +1 -0
  176. package/src/model-context/tools/getRegistry.js +39 -0
  177. package/src/model-context/tools/getRegistry.js.map +7 -0
  178. package/src/model-context/tools/index.d.ts +9 -0
  179. package/src/model-context/tools/index.d.ts.map +1 -0
  180. package/src/model-context/tools/index.js +16 -0
  181. package/src/model-context/tools/index.js.map +7 -0
  182. package/src/model-context/tools/listEnvs.d.ts +33 -0
  183. package/src/model-context/tools/listEnvs.d.ts.map +1 -0
  184. package/src/model-context/tools/listEnvs.js +37 -0
  185. package/src/model-context/tools/listEnvs.js.map +7 -0
  186. package/src/model-context.d.ts +13 -0
  187. package/src/model-context.d.ts.map +1 -0
  188. package/src/model-context.js +15 -0
  189. package/src/model-context.js.map +7 -0
  190. package/src/shadow-objects.d.ts +4 -0
  191. package/src/shadow-objects.d.ts.map +1 -1
  192. package/src/shadow-objects.js +6 -0
  193. package/src/shadow-objects.js.map +2 -2
  194. package/src/shadow-objects.worker.js +0 -1
  195. package/src/shadow-objects.worker.js.map +2 -2
  196. package/src/shae-ent.js.map +1 -1
  197. package/src/shae-prop.js +2 -2
  198. package/src/shae-prop.js.map +2 -2
  199. package/src/shae-worker.js.map +1 -1
  200. package/src/testing/TestEntity.d.ts +79 -0
  201. package/src/testing/TestEntity.d.ts.map +1 -0
  202. package/src/testing/TestEntity.js +135 -0
  203. package/src/testing/TestEntity.js.map +7 -0
  204. package/src/testing/createTestKernel.d.ts +11 -0
  205. package/src/testing/createTestKernel.d.ts.map +1 -0
  206. package/src/testing/createTestKernel.js +132 -0
  207. package/src/testing/createTestKernel.js.map +7 -0
  208. package/src/testing/mountShadowObject.d.ts +21 -0
  209. package/src/testing/mountShadowObject.d.ts.map +1 -0
  210. package/src/testing/mountShadowObject.js +72 -0
  211. package/src/testing/mountShadowObject.js.map +7 -0
  212. package/src/testing/recordKernelErrors.d.ts +21 -0
  213. package/src/testing/recordKernelErrors.d.ts.map +1 -0
  214. package/src/testing/recordKernelErrors.js +39 -0
  215. package/src/testing/recordKernelErrors.js.map +7 -0
  216. package/src/testing/settle.d.ts +15 -0
  217. package/src/testing/settle.d.ts.map +1 -0
  218. package/src/testing/settle.js +19 -0
  219. package/src/testing/settle.js.map +7 -0
  220. package/src/testing/types.d.ts +119 -0
  221. package/src/testing/types.d.ts.map +1 -0
  222. package/src/testing/types.js +1 -0
  223. package/src/testing/types.js.map +7 -0
  224. package/src/testing.d.ts +14 -0
  225. package/src/testing.d.ts.map +1 -0
  226. package/src/testing.js +11 -0
  227. package/src/testing.js.map +7 -0
  228. package/src/types.d.ts +185 -14
  229. package/src/types.d.ts.map +1 -1
  230. package/src/utils/ConsoleLogger.d.ts +45 -10
  231. package/src/utils/ConsoleLogger.d.ts.map +1 -1
  232. package/src/utils/ConsoleLogger.js +51 -26
  233. package/src/utils/ConsoleLogger.js.map +2 -2
  234. package/src/utils/FrameLoop.d.ts +42 -3
  235. package/src/utils/FrameLoop.d.ts.map +1 -1
  236. package/src/utils/FrameLoop.js +71 -10
  237. package/src/utils/FrameLoop.js.map +2 -2
  238. package/src/utils/MicrotaskCollector.d.ts +45 -0
  239. package/src/utils/MicrotaskCollector.d.ts.map +1 -0
  240. package/src/utils/MicrotaskCollector.js +33 -0
  241. package/src/utils/MicrotaskCollector.js.map +7 -0
  242. package/src/utils/MicrotaskGate.d.ts +17 -0
  243. package/src/utils/MicrotaskGate.d.ts.map +1 -0
  244. package/src/utils/MicrotaskGate.js +20 -0
  245. package/src/utils/MicrotaskGate.js.map +7 -0
  246. package/src/utils/array-utils.js.map +1 -1
  247. package/src/utils/attr-utils.d.ts +11 -0
  248. package/src/utils/attr-utils.d.ts.map +1 -1
  249. package/src/utils/attr-utils.js +5 -1
  250. package/src/utils/attr-utils.js.map +2 -2
  251. package/src/utils/constants.js.map +1 -1
  252. package/src/utils/generateUUID.d.ts +11 -0
  253. package/src/utils/generateUUID.d.ts.map +1 -1
  254. package/src/utils/generateUUID.js +31 -266
  255. package/src/utils/generateUUID.js.map +2 -2
  256. package/src/utils/props-utils.d.ts +3 -2
  257. package/src/utils/props-utils.d.ts.map +1 -1
  258. package/src/utils/props-utils.js +13 -24
  259. package/src/utils/props-utils.js.map +2 -2
  260. package/src/utils/runGuarded.d.ts +27 -0
  261. package/src/utils/runGuarded.d.ts.map +1 -0
  262. package/src/utils/runGuarded.js +11 -0
  263. package/src/utils/runGuarded.js.map +7 -0
  264. package/src/utils/toMaybe.d.ts.map +1 -1
  265. package/src/utils/toMaybe.js.map +1 -1
  266. package/src/utils/toNamespace.d.ts.map +1 -1
  267. package/src/utils/toNamespace.js.map +1 -1
  268. package/src/utils/toUrlString.d.ts +12 -0
  269. package/src/utils/toUrlString.d.ts.map +1 -1
  270. package/src/utils/toUrlString.js.map +2 -2
  271. package/src/utils/waitForMessageOfType.d.ts +12 -1
  272. package/src/utils/waitForMessageOfType.d.ts.map +1 -1
  273. package/src/utils/waitForMessageOfType.js +16 -1
  274. package/src/utils/waitForMessageOfType.js.map +2 -2
  275. package/src/view/ComponentChanges.d.ts +88 -1
  276. package/src/view/ComponentChanges.d.ts.map +1 -1
  277. package/src/view/ComponentChanges.js +243 -73
  278. package/src/view/ComponentChanges.js.map +2 -2
  279. package/src/view/ComponentContext.d.ts +206 -16
  280. package/src/view/ComponentContext.d.ts.map +1 -1
  281. package/src/view/ComponentContext.js +548 -91
  282. package/src/view/ComponentContext.js.map +2 -2
  283. package/src/view/ComponentMemory.d.ts +3 -3
  284. package/src/view/ComponentMemory.d.ts.map +1 -1
  285. package/src/view/ComponentMemory.js.map +1 -1
  286. package/src/view/IShadowObjectEnvProxy.d.ts +38 -3
  287. package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
  288. package/src/view/LocalShadowObjectEnv.d.ts +8 -1
  289. package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
  290. package/src/view/LocalShadowObjectEnv.js +23 -7
  291. package/src/view/LocalShadowObjectEnv.js.map +2 -2
  292. package/src/view/RemoteWorkerEnv.d.ts +108 -5
  293. package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
  294. package/src/view/RemoteWorkerEnv.js +244 -77
  295. package/src/view/RemoteWorkerEnv.js.map +2 -2
  296. package/src/view/ShadowEnv.d.ts +79 -2
  297. package/src/view/ShadowEnv.d.ts.map +1 -1
  298. package/src/view/ShadowEnv.js +323 -59
  299. package/src/view/ShadowEnv.js.map +3 -3
  300. package/src/view/ViewComponent.d.ts +78 -14
  301. package/src/view/ViewComponent.d.ts.map +1 -1
  302. package/src/view/ViewComponent.js +90 -14
  303. package/src/view/ViewComponent.js.map +2 -2
  304. package/src/view/cloneChangeTrail.js.map +2 -2
  305. package/src/worker/MessageRouter.d.ts +14 -0
  306. package/src/worker/MessageRouter.d.ts.map +1 -1
  307. package/src/worker/MessageRouter.js +98 -15
  308. package/src/worker/MessageRouter.js.map +2 -2
  309. package/src/worker/WorkerRuntime.d.ts +22 -1
  310. package/src/worker/WorkerRuntime.d.ts.map +1 -1
  311. package/src/worker/WorkerRuntime.js +60 -8
  312. package/src/worker/WorkerRuntime.js.map +2 -2
@@ -1,3 +1,4 @@
1
+ import type { InspectRequest, KernelSnapshot } from '../inspect/types.js';
1
2
  import type { ChangeTrailType } from '../types.js';
2
3
  import { ConsoleLogger } from '../utils/ConsoleLogger.js';
3
4
  import type { IShadowObjectEnvProxy } from './IShadowObjectEnvProxy.js';
@@ -17,6 +18,45 @@ export declare class WorkerFailedError extends Error {
17
18
  export declare class WorkerDestroyedError extends Error {
18
19
  constructor(message?: string);
19
20
  }
21
+ /**
22
+ * The reason a request is rejected with when the worker reported a failure of its own -- a
23
+ * module that would not import, a change trail its Kernel refused. An error does not survive
24
+ * structured cloning as the object it is, so the two fields that do are rebuilt here, and
25
+ * `name` is the name the error called itself inside the worker: a caller reads it the same
26
+ * way it reads the name of an error a `LocalShadowObjectEnv` hands it. A name that is missing
27
+ * or empty names no class, and `Error` is what such a reason is called -- the same name a
28
+ * plain `new Error(message)` carries on the local side.
29
+ *
30
+ * What does not come across is the class and everything it added. `instanceof EntityUuidInUseError`
31
+ * is `false` here and there is no `uuid` field, whatever `name` says; `instanceof WorkerReportedError`
32
+ * is what tells such a reason apart from one raised on this side.
33
+ */
34
+ export declare class WorkerReportedError extends Error {
35
+ constructor(name: string | undefined, message: string);
36
+ }
37
+ /**
38
+ * How long a {@link RemoteWorkerEnv} waits for each of the five replies a worker owes it,
39
+ * in milliseconds.
40
+ */
41
+ export interface WorkerTimeouts {
42
+ /** the `Loaded` handshake at the start */
43
+ loadTimeout: number;
44
+ /** the `ImportedModule` reply to an `importScript()` */
45
+ configureTimeout: number;
46
+ /** the `AppliedChangeTrail` confirmation of a change trail sent with `waitForConfirmation` */
47
+ changeTrailTimeout: number;
48
+ /** the `Inspected` answer to an `inspect()` */
49
+ inspectTimeout: number;
50
+ /** the `Destroyed` acknowledgement of a teardown */
51
+ destroyTimeout: number;
52
+ }
53
+ /**
54
+ * What {@link RemoteWorkerEnv} takes. Every value left out keeps its default — the five
55
+ * `Worker*Timeout` constants. A value that is not a number of milliseconds from 1 to
56
+ * 2147483647 — close to 25 days, the longest delay a timer keeps — is reported through the
57
+ * logger and the default applies.
58
+ */
59
+ export type RemoteWorkerEnvOptions = Partial<WorkerTimeouts>;
20
60
  /** What {@link RemoteWorkerEnv.WorkerFailed} carries. */
21
61
  export interface WorkerFailedEvent {
22
62
  /** the environment whose worker failed */
@@ -34,7 +74,14 @@ export declare class RemoteWorkerEnv implements IShadowObjectEnvProxy {
34
74
  #private;
35
75
  static WorkerLoaded: string;
36
76
  static WorkerFailed: string;
37
- readonly logger: ConsoleLogger;
77
+ /** The logger this environment reports through. */
78
+ get logger(): ConsoleLogger;
79
+ /**
80
+ * The five timeouts this environment holds itself to, resolved once when it is built. The
81
+ * object is frozen and the slot holds no setter, so the constructor is the one way in --
82
+ * and `resolveTimeouts()` vets every value that goes through it.
83
+ */
84
+ get timeouts(): Readonly<WorkerTimeouts>;
38
85
  get isDestroyed(): boolean;
39
86
  /**
40
87
  * Resolves once the worker is up.
@@ -44,17 +91,73 @@ export declare class RemoteWorkerEnv implements IShadowObjectEnvProxy {
44
91
  * `catch()` — otherwise a worker failure surfaces as an unhandled rejection.
45
92
  *
46
93
  * @throws {WorkerFailedError} if the worker fails before or after that happens
94
+ * @throws {WorkerDestroyedError} once the environment has been torn down — including every
95
+ * read after a load that had already succeeded, because there is no environment left to hand out
47
96
  */
48
97
  get workerLoaded(): Promise<RemoteWorkerEnv>;
49
- constructor();
98
+ constructor(options?: RemoteWorkerEnvOptions);
50
99
  start(): Promise<void>;
51
100
  applyChangeTrail(changeTrail: ChangeTrailType, waitForConfirmation: boolean): Promise<void>;
52
101
  importScript(url: URL | string): Promise<void>;
102
+ /**
103
+ * Asks the worker for a snapshot of its Kernel. The request travels as an `Inspect` message
104
+ * under a serial of its own, and the `Inspected` answer that carries the same serial settles
105
+ * the call: with the snapshot, or with a `WorkerReportedError` rebuilt from the failure the
106
+ * worker described. An answer that stays out past `inspectTimeout` rejects with a
107
+ * `WorkerTimeoutError`; a worker that failed or an environment that was torn down rejects right
108
+ * away, before or while the answer is awaited.
109
+ *
110
+ * The caller's `signal` ends the wait with its own reason. The worker is not told: an answer
111
+ * that arrives afterwards carries a serial nobody waits for any more and is discarded like any
112
+ * other unmatched message.
113
+ *
114
+ * The message runs through the same queue as the change trails, so the snapshot reflects every
115
+ * trail posted before this call and none posted after it.
116
+ */
117
+ inspect(request?: InspectRequest, signal?: AbortSignal): Promise<KernelSnapshot>;
53
118
  destroy(): void;
54
- private onWorkerError;
55
- private onWorkerMessageError;
119
+ private readonly onWorkerError;
120
+ private readonly onWorkerMessageError;
56
121
  private handleWorkerFailure;
57
- private onMessageFromWorker;
122
+ /**
123
+ * Announces the completed handshake to the consumers.
124
+ *
125
+ * `emitStrict()` rather than `emit()`, and the retain policy is what decides it. `workerLoaded`
126
+ * reads the retained value, and eventize writes that value only once the dispatch has run
127
+ * through: under the plain dispatch a single listener that throws would take the replay for
128
+ * every later subscriber with it, and every later read of `workerLoaded` would wait for a
129
+ * failure or a teardown instead of resolving. The guarded dispatch serves every listener and
130
+ * writes the retained value, because the event was delivered.
131
+ *
132
+ * What the listeners threw arrives here afterwards -- one of them unchanged, several as an
133
+ * `AggregateError` in dispatch order -- and is reported. It goes no further: this runs from a
134
+ * microtask, where a throw has no caller left to reach and would surface as an unhandled error.
135
+ */
136
+ private announceLoaded;
137
+ /**
138
+ * Announces the failure to the consumers. The same guarded dispatch as
139
+ * {@link RemoteWorkerEnv.announceLoaded}, for the same reason: `WorkerFailed` is documented as
140
+ * retained, and a consumer that subscribes only after the failure has to be able to hear about
141
+ * it. Every listener is served, the retained value is written, and whatever was thrown is
142
+ * reported here and reaches no caller.
143
+ */
144
+ private announceFailure;
145
+ private readonly onMessageFromWorker;
146
+ /**
147
+ * Takes every listener of this environment off a worker. Whoever registered them takes them
148
+ * off again -- between the teardown and the `terminate()` that ends it the worker stays
149
+ * alive, and through its listeners it keeps this environment and everything it references
150
+ * reachable for exactly that long.
151
+ */
152
+ private stopListeningTo;
58
153
  private configureConsoleLogger;
154
+ /**
155
+ * The console-logger config for the worker, read from the storage key this environment
156
+ * announces. The key is a diagnostic switch, set by hand and sharing its namespace with
157
+ * everything else on this origin, so its content decides nothing about whether the worker
158
+ * comes up: anything that is not a plain JSON object counts as no config at all, exactly
159
+ * like an absent key.
160
+ */
161
+ private readWorkerConfig;
59
162
  }
60
163
  //# sourceMappingURL=RemoteWorkerEnv.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RemoteWorkerEnv.d.ts","sourceRoot":"","sources":["../../../src/view/RemoteWorkerEnv.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAA0B,eAAe,EAAyC,MAAM,aAAa,CAAC;AAClH,OAAO,EAAiB,aAAa,EAAC,MAAM,2BAA2B,CAAC;AAGxE,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,4BAA4B,CAAC;AAqBtE;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,sHAAsH;IACtH,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC;gBAE5B,IAAI,EAAE,OAAO,GAAG,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAKpF;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,SAA8C;CAIlE;AAED,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,GAAG,EAAE,eAAe,CAAC;IACrB,+BAA+B;IAC/B,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC;IAC/B,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,MAAM,EAAE,iBAAiB,CAAC;IAC1B,iDAAiD;IACjD,KAAK,EAAE,UAAU,GAAG,YAAY,CAAC;CAClC;AAED,qBAAa,eAAgB,YAAW,qBAAqB;;IAC3D,MAAM,CAAC,YAAY,SAAkB;IACrC,MAAM,CAAC,YAAY,SAAkB;IAYrC,QAAQ,CAAC,MAAM,gBAAwC;IAEvD,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;;;;;OAQG;IACH,IAAI,YAAY,IAAI,OAAO,CAAC,eAAe,CAAC,CA4B3C;;IAQK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiE5B,gBAAgB,CAAC,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiC3F,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB9C,OAAO,IAAI,IAAI;IAef,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,mBAAmB;IAsC3B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,sBAAsB;CAkB/B"}
1
+ {"version":3,"file":"RemoteWorkerEnv.d.ts","sourceRoot":"","sources":["../../../src/view/RemoteWorkerEnv.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { emit, once, retain } from "@spearwolf/eventize";
1
+ import { emitStrict, once, retain } from "@spearwolf/eventize";
2
+ import { ChangeTrailRefusedError } from "../ChangeTrailRefusedError.js";
2
3
  import {
3
4
  AppliedChangeTrail,
4
5
  ChangeTrail,
@@ -6,32 +7,35 @@ import {
6
7
  Destroy,
7
8
  Destroyed,
8
9
  ImportedModule,
10
+ Inspect,
11
+ Inspected,
9
12
  Loaded,
10
13
  MessageToView,
11
14
  WorkerChangeTrailTimeout,
12
15
  WorkerConfigureTimeout,
13
16
  WorkerDestroyTimeout,
17
+ WorkerInspectTimeout,
14
18
  WorkerLoadTimeout
15
19
  } from "../constants.js";
16
20
  import createWorker from "../create-worker.js";
17
- import { CONSOLE_LOGGER, ConsoleLogger } from "../utils/ConsoleLogger.js";
21
+ import { CONSOLE_LOGGER, ConsoleLogger, consoleLoggerConfigKey, loadConsoleLoggerConfig } from "../utils/ConsoleLogger.js";
18
22
  import { toUrlString } from "../utils/toUrlString.js";
19
- import { waitForMessageOfType } from "../utils/waitForMessageOfType.js";
20
- const removeTransferables = (changeTrail) => {
23
+ import { isTimeout, MaxWorkerTimeout, waitForMessageOfType } from "../utils/waitForMessageOfType.js";
24
+ const splitTransferables = (changeTrail) => {
25
+ if (!Array.isArray(changeTrail)) return { changeTrail };
26
+ let outbound;
21
27
  let transferables;
22
- if (changeTrail != null && Array.isArray(changeTrail)) {
23
- for (const changeItem of changeTrail) {
24
- if (changeItem.transferables) {
25
- if (!transferables) {
26
- transferables = changeItem.transferables;
27
- } else {
28
- transferables = [...transferables, ...changeItem.transferables];
29
- }
30
- delete changeItem.transferables;
31
- }
28
+ for (let i = 0; i < changeTrail.length; i++) {
29
+ const changeItem = changeTrail[i];
30
+ if (changeItem?.transferables) {
31
+ transferables = transferables ? [...transferables, ...changeItem.transferables] : [...changeItem.transferables];
32
+ outbound ??= [...changeTrail];
33
+ const withoutTransferables = { ...changeItem };
34
+ delete withoutTransferables.transferables;
35
+ outbound[i] = withoutTransferables;
32
36
  }
33
37
  }
34
- return transferables;
38
+ return { changeTrail: outbound ?? changeTrail, transferables };
35
39
  };
36
40
  class WorkerFailedError extends Error {
37
41
  constructor(type, message, options) {
@@ -46,16 +50,61 @@ class WorkerDestroyedError extends Error {
46
50
  this.name = "WorkerDestroyedError";
47
51
  }
48
52
  }
53
+ class WorkerReportedError extends Error {
54
+ constructor(name, message) {
55
+ super(message);
56
+ this.name = name || "Error";
57
+ }
58
+ }
59
+ const DefaultWorkerTimeouts = {
60
+ loadTimeout: WorkerLoadTimeout,
61
+ configureTimeout: WorkerConfigureTimeout,
62
+ changeTrailTimeout: WorkerChangeTrailTimeout,
63
+ inspectTimeout: WorkerInspectTimeout,
64
+ destroyTimeout: WorkerDestroyTimeout
65
+ };
66
+ const resolveTimeouts = (options, logger) => {
67
+ const resolved = { ...DefaultWorkerTimeouts };
68
+ if (options == null) return resolved;
69
+ for (const key of Object.keys(DefaultWorkerTimeouts)) {
70
+ if (!Object.hasOwn(options, key)) continue;
71
+ const value = options[key];
72
+ if (value === void 0) continue;
73
+ if (isTimeout(value)) {
74
+ resolved[key] = value;
75
+ } else {
76
+ logger.error(`ignoring the ${key} option: expected a number of milliseconds from 1 to ${MaxWorkerTimeout}, got`, value);
77
+ }
78
+ }
79
+ return resolved;
80
+ };
49
81
  class RemoteWorkerEnv {
50
- constructor() {
82
+ constructor(options) {
51
83
  this.#isDestroyed = false;
52
84
  this.#changeTrailSerial = 0;
85
+ this.#inspectSerial = 0;
53
86
  /**
54
- * Aborted exactly once, with the {@link WorkerFailedError} that describes the failure.
87
+ * Aborted exactly once, with the error that ends this environment: a {@link WorkerFailedError}
88
+ * when the worker broke down, a {@link WorkerDestroyedError} when it was torn down. Whichever
89
+ * comes first keeps the signal, so a teardown never buries the failure that provoked it.
55
90
  * It settles every request already waiting for a reply and turns away every later one.
56
91
  */
57
92
  this.#workerFailure = new AbortController();
58
- this.logger = new ConsoleLogger("RemoteWorkerEnv");
93
+ this.#logger = new ConsoleLogger("RemoteWorkerEnv");
94
+ this.onWorkerError = (event) => {
95
+ this.handleWorkerFailure("error", event, event.message || "the worker reported an error");
96
+ };
97
+ this.onWorkerMessageError = (event) => {
98
+ this.handleWorkerFailure("messageerror", event, "the worker sent a message that could not be deserialized");
99
+ };
100
+ this.onMessageFromWorker = (event) => {
101
+ if (event.data?.type === MessageToView) {
102
+ this.onMessageToView?.(event.data.data);
103
+ } else {
104
+ this.logger.debug("message from worker", event);
105
+ }
106
+ };
107
+ this.#timeouts = Object.freeze(resolveTimeouts(options, this.logger));
59
108
  retain(this, RemoteWorkerEnv.WorkerLoaded);
60
109
  retain(this, RemoteWorkerEnv.WorkerFailed);
61
110
  }
@@ -68,7 +117,22 @@ class RemoteWorkerEnv {
68
117
  #worker;
69
118
  #isDestroyed;
70
119
  #changeTrailSerial;
120
+ #inspectSerial;
71
121
  #workerFailure;
122
+ #logger;
123
+ /** The logger this environment reports through. */
124
+ get logger() {
125
+ return this.#logger;
126
+ }
127
+ #timeouts;
128
+ /**
129
+ * The five timeouts this environment holds itself to, resolved once when it is built. The
130
+ * object is frozen and the slot holds no setter, so the constructor is the one way in --
131
+ * and `resolveTimeouts()` vets every value that goes through it.
132
+ */
133
+ get timeouts() {
134
+ return this.#timeouts;
135
+ }
72
136
  get isDestroyed() {
73
137
  return this.#isDestroyed;
74
138
  }
@@ -80,6 +144,8 @@ class RemoteWorkerEnv {
80
144
  * `catch()` — otherwise a worker failure surfaces as an unhandled rejection.
81
145
  *
82
146
  * @throws {WorkerFailedError} if the worker fails before or after that happens
147
+ * @throws {WorkerDestroyedError} once the environment has been torn down — including every
148
+ * read after a load that had already succeeded, because there is no environment left to hand out
83
149
  */
84
150
  get workerLoaded() {
85
151
  const { signal } = this.#workerFailure;
@@ -106,11 +172,8 @@ class RemoteWorkerEnv {
106
172
  async start() {
107
173
  const { signal } = this.#workerFailure;
108
174
  if (signal.aborted) throw signal.reason;
109
- if (this.#isDestroyed) throw new WorkerDestroyedError();
110
175
  if (this.#worker) {
111
- if (this.logger.isWarn) {
112
- this.logger.warn("already started");
113
- }
176
+ this.logger.warn("already started");
114
177
  return this.workerLoaded.then(() => {
115
178
  if (this.isDestroyed) {
116
179
  throw new WorkerDestroyedError();
@@ -118,22 +181,23 @@ class RemoteWorkerEnv {
118
181
  });
119
182
  }
120
183
  const worker = this.#worker = createWorker();
121
- worker.addEventListener("error", this.onWorkerError.bind(this));
122
- worker.addEventListener("messageerror", this.onWorkerMessageError.bind(this));
184
+ worker.addEventListener("error", this.onWorkerError);
185
+ worker.addEventListener("messageerror", this.onWorkerMessageError);
123
186
  this.configureConsoleLogger(worker);
124
187
  try {
125
- await waitForMessageOfType(worker, Loaded, WorkerLoadTimeout, void 0, signal);
188
+ await waitForMessageOfType(worker, Loaded, this.timeouts.loadTimeout, void 0, signal);
126
189
  if (this.isDestroyed) {
127
- throw new WorkerDestroyedError();
190
+ throw signal.aborted ? signal.reason : new WorkerDestroyedError();
128
191
  }
129
- worker.addEventListener("message", this.onMessageFromWorker.bind(this));
192
+ worker.addEventListener("message", this.onMessageFromWorker);
130
193
  queueMicrotask(() => {
131
- emit(this, RemoteWorkerEnv.WorkerLoaded, this);
194
+ this.announceLoaded();
132
195
  });
133
196
  } catch (error) {
134
197
  if (error !== signal.reason) {
135
198
  this.logger.error("failed to start", error);
136
199
  }
200
+ this.stopListeningTo(worker);
137
201
  this.#worker = void 0;
138
202
  if (!this.#isDestroyed) {
139
203
  worker.terminate();
@@ -146,27 +210,32 @@ class RemoteWorkerEnv {
146
210
  if (signal.aborted) return Promise.reject(signal.reason);
147
211
  const worker = this.#worker;
148
212
  if (worker == null) return Promise.reject(new WorkerDestroyedError());
149
- const transferables = removeTransferables(changeTrail);
150
- const message = { type: ChangeTrail, changeTrail };
151
- const serial = ++this.#changeTrailSerial;
152
- if (waitForConfirmation) {
153
- message.serial = serial;
154
- }
155
- worker.postMessage(message, transferables ?? []);
156
- if (waitForConfirmation) {
157
- return waitForMessageOfType(
158
- worker,
159
- AppliedChangeTrail,
160
- WorkerChangeTrailTimeout,
161
- (data) => {
162
- if (data.error) throw data.error;
163
- return data.serial === serial;
164
- },
165
- signal
166
- );
167
- } else {
213
+ const { changeTrail: outbound, transferables } = splitTransferables(changeTrail);
214
+ const message = { type: ChangeTrail, changeTrail: outbound };
215
+ if (!waitForConfirmation) {
216
+ worker.postMessage(message, transferables ?? []);
168
217
  return Promise.resolve();
169
218
  }
219
+ const serial = ++this.#changeTrailSerial;
220
+ message.serial = serial;
221
+ worker.postMessage(message, transferables ?? []);
222
+ return waitForMessageOfType(
223
+ worker,
224
+ AppliedChangeTrail,
225
+ this.timeouts.changeTrailTimeout,
226
+ (data) => {
227
+ if (data.serial !== serial) return false;
228
+ if (data.error) {
229
+ const reason = new WorkerReportedError(data.errorName, data.error);
230
+ if (typeof data.appliedCount === "number") {
231
+ throw new ChangeTrailRefusedError(data.appliedCount, changeTrail.length, { cause: reason });
232
+ }
233
+ throw reason;
234
+ }
235
+ return true;
236
+ },
237
+ signal
238
+ );
170
239
  }
171
240
  importScript(url) {
172
241
  const { signal } = this.#workerFailure;
@@ -178,30 +247,72 @@ class RemoteWorkerEnv {
178
247
  return waitForMessageOfType(
179
248
  worker,
180
249
  ImportedModule,
181
- WorkerConfigureTimeout,
250
+ this.timeouts.configureTimeout,
182
251
  (data) => {
183
- if (data.error) throw data.error;
184
- return data.url === url;
252
+ if (data.url !== url) return false;
253
+ if (data.error) throw new WorkerReportedError(data.errorName, data.error);
254
+ return true;
185
255
  },
186
256
  signal
187
257
  );
188
258
  }
259
+ /**
260
+ * Asks the worker for a snapshot of its Kernel. The request travels as an `Inspect` message
261
+ * under a serial of its own, and the `Inspected` answer that carries the same serial settles
262
+ * the call: with the snapshot, or with a `WorkerReportedError` rebuilt from the failure the
263
+ * worker described. An answer that stays out past `inspectTimeout` rejects with a
264
+ * `WorkerTimeoutError`; a worker that failed or an environment that was torn down rejects right
265
+ * away, before or while the answer is awaited.
266
+ *
267
+ * The caller's `signal` ends the wait with its own reason. The worker is not told: an answer
268
+ * that arrives afterwards carries a serial nobody waits for any more and is discarded like any
269
+ * other unmatched message.
270
+ *
271
+ * The message runs through the same queue as the change trails, so the snapshot reflects every
272
+ * trail posted before this call and none posted after it.
273
+ */
274
+ inspect(request = {}, signal) {
275
+ const { signal: failure } = this.#workerFailure;
276
+ if (failure.aborted) return Promise.reject(failure.reason);
277
+ if (signal?.aborted) return Promise.reject(signal.reason);
278
+ const worker = this.#worker;
279
+ if (worker == null) return Promise.reject(new WorkerDestroyedError());
280
+ const serial = ++this.#inspectSerial;
281
+ const message = { type: Inspect, serial, request };
282
+ worker.postMessage(message);
283
+ let snapshot;
284
+ return waitForMessageOfType(
285
+ worker,
286
+ Inspected,
287
+ this.timeouts.inspectTimeout,
288
+ (data) => {
289
+ if (data.serial !== serial) return false;
290
+ if (data.error) throw new WorkerReportedError(data.errorName, data.error);
291
+ snapshot = data.snapshot;
292
+ return true;
293
+ },
294
+ signal === void 0 ? failure : AbortSignal.any([failure, signal])
295
+ ).then(() => {
296
+ if (snapshot === void 0) {
297
+ throw new WorkerReportedError(void 0, "the worker answered the inspection without a snapshot");
298
+ }
299
+ return snapshot;
300
+ });
301
+ }
189
302
  destroy() {
190
- if (!this.#worker) return;
303
+ this.#isDestroyed = true;
191
304
  const worker = this.#worker;
192
305
  this.#worker = void 0;
193
- this.#isDestroyed = true;
306
+ this.#workerFailure.abort(new WorkerDestroyedError());
307
+ if (worker == null) return;
308
+ this.stopListeningTo(worker);
194
309
  worker.postMessage({ type: Destroy });
195
- waitForMessageOfType(worker, Destroyed, WorkerDestroyTimeout).finally(() => {
310
+ waitForMessageOfType(worker, Destroyed, this.timeouts.destroyTimeout).catch((error) => {
311
+ this.logger.warn("the worker did not acknowledge the teardown", error);
312
+ }).finally(() => {
196
313
  worker.terminate();
197
314
  });
198
315
  }
199
- onWorkerError(event) {
200
- this.handleWorkerFailure("error", event, event.message || "the worker reported an error");
201
- }
202
- onWorkerMessageError(event) {
203
- this.handleWorkerFailure("messageerror", event, "the worker sent a message that could not be deserialized");
204
- }
205
316
  handleWorkerFailure(type, event, message) {
206
317
  if (this.#isDestroyed) return;
207
318
  this.#isDestroyed = true;
@@ -211,6 +322,7 @@ class RemoteWorkerEnv {
211
322
  this.logger.error(message, event);
212
323
  const worker = this.#worker;
213
324
  this.#worker = void 0;
325
+ if (worker != null) this.stopListeningTo(worker);
214
326
  this.#workerFailure.abort(reason);
215
327
  worker?.terminate();
216
328
  try {
@@ -218,27 +330,59 @@ class RemoteWorkerEnv {
218
330
  } catch (error) {
219
331
  this.logger.error("the proxy-failed callback threw", error);
220
332
  }
221
- emit(this, RemoteWorkerEnv.WorkerFailed, {
222
- env: this,
223
- type,
224
- message,
225
- reason,
226
- event
227
- });
333
+ this.announceFailure({ env: this, type, message, reason, event });
228
334
  }
229
- onMessageFromWorker(event) {
230
- if (event.data?.type === MessageToView) {
231
- this.onMessageToView?.(event.data.data);
232
- } else if (this.logger.isDebug) {
233
- this.logger.debug("message from worker", event);
335
+ /**
336
+ * Announces the completed handshake to the consumers.
337
+ *
338
+ * `emitStrict()` rather than `emit()`, and the retain policy is what decides it. `workerLoaded`
339
+ * reads the retained value, and eventize writes that value only once the dispatch has run
340
+ * through: under the plain dispatch a single listener that throws would take the replay for
341
+ * every later subscriber with it, and every later read of `workerLoaded` would wait for a
342
+ * failure or a teardown instead of resolving. The guarded dispatch serves every listener and
343
+ * writes the retained value, because the event was delivered.
344
+ *
345
+ * What the listeners threw arrives here afterwards -- one of them unchanged, several as an
346
+ * `AggregateError` in dispatch order -- and is reported. It goes no further: this runs from a
347
+ * microtask, where a throw has no caller left to reach and would surface as an unhandled error.
348
+ */
349
+ announceLoaded() {
350
+ try {
351
+ emitStrict(this, RemoteWorkerEnv.WorkerLoaded, this);
352
+ } catch (error) {
353
+ this.logger.error("a workerLoaded listener threw", error);
234
354
  }
235
355
  }
236
- configureConsoleLogger(worker) {
237
- const workerConfigKey = `${CONSOLE_LOGGER}.RemoteWorkerEnv.workerConfig`;
238
- const workerConfig = JSON.parse(localStorage.getItem(workerConfigKey) ?? "{}");
239
- if (this.logger.isInfo) {
240
- this.logger.info("load console-logger worker config", { localStorageKey: workerConfigKey, workerConfig });
356
+ /**
357
+ * Announces the failure to the consumers. The same guarded dispatch as
358
+ * {@link RemoteWorkerEnv.announceLoaded}, for the same reason: `WorkerFailed` is documented as
359
+ * retained, and a consumer that subscribes only after the failure has to be able to hear about
360
+ * it. Every listener is served, the retained value is written, and whatever was thrown is
361
+ * reported here and reaches no caller.
362
+ */
363
+ announceFailure(payload) {
364
+ try {
365
+ emitStrict(this, RemoteWorkerEnv.WorkerFailed, payload);
366
+ } catch (error) {
367
+ this.logger.error("a workerFailed listener threw", error);
241
368
  }
369
+ }
370
+ /**
371
+ * Takes every listener of this environment off a worker. Whoever registered them takes them
372
+ * off again -- between the teardown and the `terminate()` that ends it the worker stays
373
+ * alive, and through its listeners it keeps this environment and everything it references
374
+ * reachable for exactly that long.
375
+ */
376
+ stopListeningTo(worker) {
377
+ worker.removeEventListener("error", this.onWorkerError);
378
+ worker.removeEventListener("messageerror", this.onWorkerMessageError);
379
+ worker.removeEventListener("message", this.onMessageFromWorker);
380
+ }
381
+ configureConsoleLogger(worker) {
382
+ const configKey = ["RemoteWorkerEnv", "workerConfig"];
383
+ const storageKey = consoleLoggerConfigKey(configKey);
384
+ const workerConfig = this.readWorkerConfig(configKey, storageKey);
385
+ this.logger.info("load console-logger worker config", { storageKey, workerConfig });
242
386
  worker.postMessage({
243
387
  type: CONSOLE_LOGGER,
244
388
  config: {
@@ -249,10 +393,33 @@ class RemoteWorkerEnv {
249
393
  }
250
394
  });
251
395
  }
396
+ /**
397
+ * The console-logger config for the worker, read from the storage key this environment
398
+ * announces. The key is a diagnostic switch, set by hand and sharing its namespace with
399
+ * everything else on this origin, so its content decides nothing about whether the worker
400
+ * comes up: anything that is not a plain JSON object counts as no config at all, exactly
401
+ * like an absent key.
402
+ */
403
+ readWorkerConfig(configKey, storageKey) {
404
+ const stored = loadConsoleLoggerConfig(configKey, "{}");
405
+ let parsed;
406
+ try {
407
+ parsed = JSON.parse(stored);
408
+ } catch (error) {
409
+ this.logger.error(`ignoring the unreadable console-logger worker config at "${storageKey}"`, stored, error);
410
+ return {};
411
+ }
412
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
413
+ this.logger.error(`ignoring the console-logger worker config at "${storageKey}": expected a JSON object`, stored);
414
+ return {};
415
+ }
416
+ return parsed;
417
+ }
252
418
  }
253
419
  export {
254
420
  RemoteWorkerEnv,
255
421
  WorkerDestroyedError,
256
- WorkerFailedError
422
+ WorkerFailedError,
423
+ WorkerReportedError
257
424
  };
258
425
  //# sourceMappingURL=RemoteWorkerEnv.js.map