@opetope/runtime 0.1.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 (314) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +345 -0
  4. package/README.ru.md +344 -0
  5. package/dist/application-compiler-edges.d.ts +3 -0
  6. package/dist/application-compiler-edges.js +2 -0
  7. package/dist/application-compiler-edges.js.map +1 -0
  8. package/dist/application-compiler-graph.d.ts +8 -0
  9. package/dist/application-compiler-graph.js +2 -0
  10. package/dist/application-compiler-graph.js.map +1 -0
  11. package/dist/application-compiler.d.ts +116 -0
  12. package/dist/application-compiler.js +2 -0
  13. package/dist/application-compiler.js.map +1 -0
  14. package/dist/application-conditions.d.ts +18 -0
  15. package/dist/application-conditions.js +2 -0
  16. package/dist/application-conditions.js.map +1 -0
  17. package/dist/application-definition.d.ts +30 -0
  18. package/dist/application-definition.js +2 -0
  19. package/dist/application-definition.js.map +1 -0
  20. package/dist/application-error.d.ts +10 -0
  21. package/dist/application-error.js +2 -0
  22. package/dist/application-error.js.map +1 -0
  23. package/dist/application-execution.d.ts +43 -0
  24. package/dist/application-execution.js +2 -0
  25. package/dist/application-execution.js.map +1 -0
  26. package/dist/application-feature-bindings.d.ts +11 -0
  27. package/dist/application-feature-bindings.js +2 -0
  28. package/dist/application-feature-bindings.js.map +1 -0
  29. package/dist/application-feature-instance.d.ts +5 -0
  30. package/dist/application-feature-instance.js +2 -0
  31. package/dist/application-feature-instance.js.map +1 -0
  32. package/dist/application-group-order.d.ts +29 -0
  33. package/dist/application-group-order.js +2 -0
  34. package/dist/application-group-order.js.map +1 -0
  35. package/dist/application-instance-retirement.d.ts +36 -0
  36. package/dist/application-instance-retirement.js +2 -0
  37. package/dist/application-instance-retirement.js.map +1 -0
  38. package/dist/application-open-options.d.ts +29 -0
  39. package/dist/application-open-options.js +2 -0
  40. package/dist/application-open-options.js.map +1 -0
  41. package/dist/application-port-compiler.d.ts +28 -0
  42. package/dist/application-port-compiler.js +2 -0
  43. package/dist/application-port-compiler.js.map +1 -0
  44. package/dist/attachment-call-declaration.d.ts +43 -0
  45. package/dist/attachment-call-declaration.js +2 -0
  46. package/dist/attachment-call-declaration.js.map +1 -0
  47. package/dist/attachment-declaration.d.ts +68 -0
  48. package/dist/attachment-declaration.js +2 -0
  49. package/dist/attachment-declaration.js.map +1 -0
  50. package/dist/attachment-execution.d.ts +9 -0
  51. package/dist/attachment-execution.js +2 -0
  52. package/dist/attachment-execution.js.map +1 -0
  53. package/dist/attachment-retirement-scheduler.d.ts +14 -0
  54. package/dist/attachment-retirement-scheduler.js +2 -0
  55. package/dist/attachment-retirement-scheduler.js.map +1 -0
  56. package/dist/call-option-snapshot.d.ts +20 -0
  57. package/dist/call-option-snapshot.js +2 -0
  58. package/dist/call-option-snapshot.js.map +1 -0
  59. package/dist/compile-call-target-bindings.d.ts +14 -0
  60. package/dist/compile-call-target-bindings.js +2 -0
  61. package/dist/compile-call-target-bindings.js.map +1 -0
  62. package/dist/compile-module-template.d.ts +38 -0
  63. package/dist/compile-module-template.js +2 -0
  64. package/dist/compile-module-template.js.map +1 -0
  65. package/dist/condition-group-execution.d.ts +44 -0
  66. package/dist/condition-group-execution.js +2 -0
  67. package/dist/condition-group-execution.js.map +1 -0
  68. package/dist/condition-override.d.ts +28 -0
  69. package/dist/condition-override.js +2 -0
  70. package/dist/condition-override.js.map +1 -0
  71. package/dist/condition-source.d.ts +10 -0
  72. package/dist/condition-source.js +2 -0
  73. package/dist/condition-source.js.map +1 -0
  74. package/dist/condition-types.d.ts +14 -0
  75. package/dist/condition.d.ts +28 -0
  76. package/dist/condition.js +2 -0
  77. package/dist/condition.js.map +1 -0
  78. package/dist/control-registry.d.ts +56 -0
  79. package/dist/control-registry.js +2 -0
  80. package/dist/control-registry.js.map +1 -0
  81. package/dist/dynamic-scope-child.d.ts +23 -0
  82. package/dist/dynamic-scope-child.js +2 -0
  83. package/dist/dynamic-scope-child.js.map +1 -0
  84. package/dist/dynamic-scope-controller.d.ts +16 -0
  85. package/dist/dynamic-scope-controller.js +2 -0
  86. package/dist/dynamic-scope-controller.js.map +1 -0
  87. package/dist/feature-attachment-authoring-types.d.ts +51 -0
  88. package/dist/feature-attachment-lowering.d.ts +18 -0
  89. package/dist/feature-attachment-lowering.js +2 -0
  90. package/dist/feature-attachment-lowering.js.map +1 -0
  91. package/dist/feature-attachment.d.ts +45 -0
  92. package/dist/feature-attachment.js +2 -0
  93. package/dist/feature-attachment.js.map +1 -0
  94. package/dist/feature-authoring-types.d.ts +196 -0
  95. package/dist/feature-authoring.d.ts +15 -0
  96. package/dist/feature-authoring.js +2 -0
  97. package/dist/feature-authoring.js.map +1 -0
  98. package/dist/feature-body.d.ts +53 -0
  99. package/dist/feature-body.js +2 -0
  100. package/dist/feature-body.js.map +1 -0
  101. package/dist/feature-call-authority.d.ts +8 -0
  102. package/dist/feature-call-authority.js +2 -0
  103. package/dist/feature-call-authority.js.map +1 -0
  104. package/dist/feature-call-types.d.ts +39 -0
  105. package/dist/feature-call.d.ts +18 -0
  106. package/dist/feature-call.js +2 -0
  107. package/dist/feature-call.js.map +1 -0
  108. package/dist/feature-calls.d.ts +6 -0
  109. package/dist/feature-calls.js +2 -0
  110. package/dist/feature-calls.js.map +1 -0
  111. package/dist/feature-contract.d.ts +70 -0
  112. package/dist/feature-contract.js +2 -0
  113. package/dist/feature-contract.js.map +1 -0
  114. package/dist/feature-contribution-model.d.ts +38 -0
  115. package/dist/feature-contribution-model.js +2 -0
  116. package/dist/feature-contribution-model.js.map +1 -0
  117. package/dist/feature-contribution.d.ts +126 -0
  118. package/dist/feature-contribution.js +2 -0
  119. package/dist/feature-contribution.js.map +1 -0
  120. package/dist/feature-definition-api.d.ts +53 -0
  121. package/dist/feature-definition-support.d.ts +21 -0
  122. package/dist/feature-definition-support.js +2 -0
  123. package/dist/feature-definition-support.js.map +1 -0
  124. package/dist/feature-effect.d.ts +26 -0
  125. package/dist/feature-effect.js +2 -0
  126. package/dist/feature-effect.js.map +1 -0
  127. package/dist/feature-event.d.ts +32 -0
  128. package/dist/feature-event.js +2 -0
  129. package/dist/feature-event.js.map +1 -0
  130. package/dist/feature-generation.d.ts +34 -0
  131. package/dist/feature-generation.js +2 -0
  132. package/dist/feature-generation.js.map +1 -0
  133. package/dist/feature-lazy-generation.d.ts +6 -0
  134. package/dist/feature-lazy-generation.js +2 -0
  135. package/dist/feature-lazy-generation.js.map +1 -0
  136. package/dist/feature-lazy.d.ts +44 -0
  137. package/dist/feature-lazy.js +2 -0
  138. package/dist/feature-lazy.js.map +1 -0
  139. package/dist/feature-materialization-binding.d.ts +31 -0
  140. package/dist/feature-materialization-binding.js +2 -0
  141. package/dist/feature-materialization-binding.js.map +1 -0
  142. package/dist/feature-materialization-types.d.ts +45 -0
  143. package/dist/feature-model-dependencies.d.ts +23 -0
  144. package/dist/feature-model-dependencies.js +2 -0
  145. package/dist/feature-model-dependencies.js.map +1 -0
  146. package/dist/feature-model.d.ts +60 -0
  147. package/dist/feature-model.js +2 -0
  148. package/dist/feature-model.js.map +1 -0
  149. package/dist/feature-optional.d.ts +15 -0
  150. package/dist/feature-optional.js +2 -0
  151. package/dist/feature-optional.js.map +1 -0
  152. package/dist/feature-own-lowering.d.ts +22 -0
  153. package/dist/feature-own-lowering.js +2 -0
  154. package/dist/feature-own-lowering.js.map +1 -0
  155. package/dist/feature-port-binding.d.ts +6 -0
  156. package/dist/feature-port-binding.js +2 -0
  157. package/dist/feature-port-binding.js.map +1 -0
  158. package/dist/feature-port.d.ts +66 -0
  159. package/dist/feature-port.js +2 -0
  160. package/dist/feature-port.js.map +1 -0
  161. package/dist/feature-record.d.ts +6 -0
  162. package/dist/feature-record.js +2 -0
  163. package/dist/feature-record.js.map +1 -0
  164. package/dist/feature-resource.d.ts +29 -0
  165. package/dist/feature-resource.js +2 -0
  166. package/dist/feature-resource.js.map +1 -0
  167. package/dist/feature-scope-types.d.ts +35 -0
  168. package/dist/feature-scope.d.ts +29 -0
  169. package/dist/feature-scope.js +2 -0
  170. package/dist/feature-scope.js.map +1 -0
  171. package/dist/feature-stream.d.ts +37 -0
  172. package/dist/feature-stream.js +2 -0
  173. package/dist/feature-stream.js.map +1 -0
  174. package/dist/feature-timers.d.ts +14 -0
  175. package/dist/feature-timers.js +2 -0
  176. package/dist/feature-timers.js.map +1 -0
  177. package/dist/index.d.ts +19 -0
  178. package/dist/index.js +2 -0
  179. package/dist/index.js.map +1 -0
  180. package/dist/inspection-activity-protocol.d.ts +63 -0
  181. package/dist/inspection-activity.d.ts +54 -0
  182. package/dist/inspection-activity.js +2 -0
  183. package/dist/inspection-activity.js.map +1 -0
  184. package/dist/inspection-diff.d.ts +10 -0
  185. package/dist/inspection-diff.js +2 -0
  186. package/dist/inspection-diff.js.map +1 -0
  187. package/dist/inspection-module-activity.d.ts +4 -0
  188. package/dist/inspection-module-activity.js +2 -0
  189. package/dist/inspection-module-activity.js.map +1 -0
  190. package/dist/inspection-observer.d.ts +38 -0
  191. package/dist/inspection-observer.js +2 -0
  192. package/dist/inspection-observer.js.map +1 -0
  193. package/dist/inspection-plan.d.ts +33 -0
  194. package/dist/inspection-plan.js +2 -0
  195. package/dist/inspection-plan.js.map +1 -0
  196. package/dist/inspection-protocol.d.ts +333 -0
  197. package/dist/inspection-protocol.js +2 -0
  198. package/dist/inspection-protocol.js.map +1 -0
  199. package/dist/inspection-registry.d.ts +41 -0
  200. package/dist/inspection-registry.js +2 -0
  201. package/dist/inspection-registry.js.map +1 -0
  202. package/dist/inspection-session.d.ts +92 -0
  203. package/dist/inspection-session.js +2 -0
  204. package/dist/inspection-session.js.map +1 -0
  205. package/dist/inspection-snapshot.d.ts +4 -0
  206. package/dist/inspection-snapshot.js +2 -0
  207. package/dist/inspection-snapshot.js.map +1 -0
  208. package/dist/inspection-state.d.ts +91 -0
  209. package/dist/inspection-state.js +2 -0
  210. package/dist/inspection-state.js.map +1 -0
  211. package/dist/instance-demand.d.ts +31 -0
  212. package/dist/instance-demand.js +2 -0
  213. package/dist/instance-demand.js.map +1 -0
  214. package/dist/internal.d.ts +41 -0
  215. package/dist/internal.js +2 -0
  216. package/dist/internal.js.map +1 -0
  217. package/dist/keyed-scope-controller.d.ts +11 -0
  218. package/dist/keyed-scope-controller.js +2 -0
  219. package/dist/keyed-scope-controller.js.map +1 -0
  220. package/dist/model-kernel.d.ts +18 -0
  221. package/dist/model-kernel.js +2 -0
  222. package/dist/model-kernel.js.map +1 -0
  223. package/dist/module-call-context.d.ts +7 -0
  224. package/dist/module-call-context.js +2 -0
  225. package/dist/module-call-context.js.map +1 -0
  226. package/dist/module-call-runtime.d.ts +21 -0
  227. package/dist/module-call-runtime.js +2 -0
  228. package/dist/module-call-runtime.js.map +1 -0
  229. package/dist/module-generation.d.ts +57 -0
  230. package/dist/module-generation.js +2 -0
  231. package/dist/module-generation.js.map +1 -0
  232. package/dist/module-instance-types.d.ts +156 -0
  233. package/dist/module-instance.d.ts +19 -0
  234. package/dist/module-instance.js +2 -0
  235. package/dist/module-instance.js.map +1 -0
  236. package/dist/module-runtime-identity.d.ts +4 -0
  237. package/dist/module-runtime-identity.js +2 -0
  238. package/dist/module-runtime-identity.js.map +1 -0
  239. package/dist/module-scope-open.d.ts +4 -0
  240. package/dist/module-scope-open.js +2 -0
  241. package/dist/module-scope-open.js.map +1 -0
  242. package/dist/module-scope-retirement.d.ts +3 -0
  243. package/dist/module-scope-retirement.js +2 -0
  244. package/dist/module-scope-retirement.js.map +1 -0
  245. package/dist/module-template-ir.d.ts +59 -0
  246. package/dist/owner-generation-retirement.d.ts +6 -0
  247. package/dist/owner-generation-retirement.js +2 -0
  248. package/dist/owner-generation-retirement.js.map +1 -0
  249. package/dist/owner-generation-state.d.ts +123 -0
  250. package/dist/owner-generation-state.js +2 -0
  251. package/dist/owner-generation-state.js.map +1 -0
  252. package/dist/owner-generation.d.ts +16 -0
  253. package/dist/owner-generation.js +2 -0
  254. package/dist/owner-generation.js.map +1 -0
  255. package/dist/public-module-definition.d.ts +9 -0
  256. package/dist/public-module-definition.js +2 -0
  257. package/dist/public-module-definition.js.map +1 -0
  258. package/dist/public-module-instance.d.ts +6 -0
  259. package/dist/public-module-instance.js +2 -0
  260. package/dist/public-module-instance.js.map +1 -0
  261. package/dist/public-module-retirement-diagnostics.d.ts +5 -0
  262. package/dist/public-module-retirement-diagnostics.js +2 -0
  263. package/dist/public-module-retirement-diagnostics.js.map +1 -0
  264. package/dist/public-module-retirement.d.ts +3 -0
  265. package/dist/public-module-retirement.js +2 -0
  266. package/dist/public-module-retirement.js.map +1 -0
  267. package/dist/public-module-scope.d.ts +5 -0
  268. package/dist/public-module-scope.js +2 -0
  269. package/dist/public-module-scope.js.map +1 -0
  270. package/dist/public-module-state.d.ts +28 -0
  271. package/dist/public-module-state.js +2 -0
  272. package/dist/public-module-state.js.map +1 -0
  273. package/dist/public-module-types.d.ts +295 -0
  274. package/dist/public-module.d.ts +4 -0
  275. package/dist/resource-cache.d.ts +12 -0
  276. package/dist/resource-cache.js +2 -0
  277. package/dist/resource-cache.js.map +1 -0
  278. package/dist/resource-controller.d.ts +29 -0
  279. package/dist/resource-controller.js +2 -0
  280. package/dist/resource-controller.js.map +1 -0
  281. package/dist/resource-policy.d.ts +16 -0
  282. package/dist/resource-policy.js +2 -0
  283. package/dist/resource-policy.js.map +1 -0
  284. package/dist/resource-snapshot.d.ts +12 -0
  285. package/dist/resource-snapshot.js +2 -0
  286. package/dist/resource-snapshot.js.map +1 -0
  287. package/dist/resource-types.d.ts +3 -0
  288. package/dist/runtime-error-reporting.d.ts +4 -0
  289. package/dist/runtime-error-reporting.js +2 -0
  290. package/dist/runtime-error-reporting.js.map +1 -0
  291. package/dist/stream-backpressure.d.ts +18 -0
  292. package/dist/stream-backpressure.js +2 -0
  293. package/dist/stream-backpressure.js.map +1 -0
  294. package/dist/stream-cleanup.d.ts +17 -0
  295. package/dist/stream-cleanup.js +2 -0
  296. package/dist/stream-cleanup.js.map +1 -0
  297. package/dist/stream-controller-types.d.ts +51 -0
  298. package/dist/stream-controller.d.ts +5 -0
  299. package/dist/stream-controller.js +2 -0
  300. package/dist/stream-controller.js.map +1 -0
  301. package/docs/agent-guide.md +214 -0
  302. package/docs/agent-guide.ru.md +208 -0
  303. package/docs/cookbook.md +734 -0
  304. package/docs/cookbook.ru.md +729 -0
  305. package/docs/decisions.md +1437 -0
  306. package/docs/devtools.md +423 -0
  307. package/docs/devtools.ru.md +419 -0
  308. package/docs/how-it-works.md +521 -0
  309. package/docs/how-it-works.ru.md +495 -0
  310. package/docs/releases.md +78 -0
  311. package/docs/releases.ru.md +78 -0
  312. package/docs/spec.md +874 -0
  313. package/docs/spec.ru.md +884 -0
  314. package/package.json +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile-module-template.js","sources":["../src/compile-module-template.ts"],"sourcesContent":["import { declarationId } from '@opetope/core';\nimport type { DeclarationId } from '@opetope/core';\nimport type { ExactInput } from '@opetope/core/internal';\nimport { hasOwn } from '@opetope/core/internal';\n\nimport { isAttachmentCallRef } from './attachment-call-declaration';\nimport type {\n AttachmentCallRef,\n AttachmentCallRefIdentity,\n CompiledAttachmentCallRecipe,\n} from './attachment-call-declaration';\nimport {\n getAttachmentDefinition,\n getScopeAttachmentRefs,\n isLifetimeAttachmentRef,\n isScopeBlueprint,\n sealScopeBlueprint,\n} from './attachment-declaration';\nimport type {\n CompiledLifetimeAttachmentRecipe,\n LifetimeAttachmentRef,\n LifetimeAttachmentRefIdentity,\n ScopeBlueprint,\n ScopeBlueprintIdentity,\n} from './attachment-declaration';\nimport { compileCallTargetBindings } from './compile-call-target-bindings';\nimport { runtimeIdentity } from './module-runtime-identity';\nimport type {\n AttachmentNodeIR,\n AttachmentTemplateSlot,\n CallTargetBindingSlot,\n CompiledModuleTemplateIR,\n RuntimeNodeIdentityIR,\n ScopeAttachmentSlot,\n ScopeNodeIR,\n ScopeTemplateSlot,\n} from './module-template-ir';\n\ndeclare const localAttachmentModuleDefinitionBrand: unique symbol;\n\ntype RuntimeBlueprint = AttachmentCallRefIdentity | LifetimeAttachmentRefIdentity | ScopeBlueprintIdentity;\ntype RuntimeBlueprintRecord = Readonly<Record<string, RuntimeBlueprint>>;\ntype ModuleDefinitionCandidate = {\n readonly id: string;\n readonly runtime: RuntimeBlueprintRecord;\n};\n\ninterface LocalAttachmentModuleDefinition<Runtime extends RuntimeBlueprintRecord = RuntimeBlueprintRecord> {\n readonly id: DeclarationId;\n readonly [localAttachmentModuleDefinitionBrand]: true;\n readonly runtime: Runtime;\n}\n\ntype RuntimeEntry = {\n readonly key: string;\n readonly value: RuntimeBlueprint;\n};\n\ntype LocalAttachmentModuleDefinitionRecord = {\n readonly entries: readonly RuntimeEntry[];\n readonly moduleId: DeclarationId;\n};\n\ntype CompiledModuleTemplateRecord = {\n readonly attachmentCallRecipes: readonly CompiledAttachmentCallRecipe<unknown, unknown>[];\n readonly attachmentCallSlots: ReadonlyMap<AttachmentCallRefIdentity, CallTargetBindingSlot>;\n readonly attachmentSlots: ReadonlyMap<LifetimeAttachmentRefIdentity, AttachmentTemplateSlot>;\n readonly recipes: readonly CompiledLifetimeAttachmentRecipe<unknown>[];\n readonly scopeSlots: ReadonlyMap<ScopeBlueprintIdentity, ScopeTemplateSlot>;\n};\n\nconst moduleDefinitions = new WeakMap<LocalAttachmentModuleDefinition, LocalAttachmentModuleDefinitionRecord>();\nconst compiledDefinitions = new WeakMap<LocalAttachmentModuleDefinition, CompiledModuleTemplateIR>();\nconst compiledTemplates = new WeakMap<CompiledModuleTemplateIR, CompiledModuleTemplateRecord>();\nconst blueprintOwners = new WeakMap<RuntimeBlueprint, LocalAttachmentModuleDefinition>();\nconst moduleDefinitionOptionKeys = new Set<PropertyKey>(['id', 'runtime']);\nconst runtimeKeyPattern = /^[A-Za-z][A-Za-z0-9]*$/;\n\nfunction assertKnownModuleDefinitionOptions(options: object): void {\n const prototype: unknown = Object.getPrototypeOf(options);\n\n if (prototype !== Object.prototype && prototype !== null) {\n throw new TypeError('Local attachment options must be a plain object.');\n }\n\n for (const key of Reflect.ownKeys(options)) {\n if (!moduleDefinitionOptionKeys.has(key)) {\n throw new TypeError(`Unknown local attachment option ${String(key)}.`);\n }\n }\n\n if (!hasOwn(options, 'id') || !hasOwn(options, 'runtime')) {\n throw new TypeError('Local attachment definition requires own id and runtime fields.');\n }\n}\n\nfunction assertRuntimeRecord(runtime: RuntimeBlueprintRecord): void {\n if (typeof runtime !== 'object' || runtime === null) throw new TypeError('Feature runtime must be an object record.');\n\n const prototype: unknown = Object.getPrototypeOf(runtime);\n\n if (prototype !== Object.prototype && prototype !== null) {\n throw new TypeError('Feature runtime must be a plain object record.');\n }\n}\n\nfunction snapshotRuntimeEntry(runtime: RuntimeBlueprintRecord, propertyKey: string): RuntimeEntry {\n if (!runtimeKeyPattern.test(propertyKey)) {\n throw new TypeError(`Feature runtime key ${propertyKey} must be one identifier segment.`);\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(runtime, propertyKey);\n\n if (!descriptor?.enumerable) {\n throw new TypeError(`Feature runtime key ${propertyKey} must be an enumerable own property.`);\n }\n\n const value = Reflect.get(runtime, propertyKey) as unknown;\n\n if (!isAttachmentCallRef(value) && !isScopeBlueprint(value) && !isLifetimeAttachmentRef(value)) {\n throw new TypeError(`own.${propertyKey} is not a value created by the own builder.`);\n }\n\n return Object.freeze({ key: propertyKey, value });\n}\n\nfunction snapshotRuntimeEntries(runtime: RuntimeBlueprintRecord): readonly RuntimeEntry[] {\n assertRuntimeRecord(runtime);\n const propertyKeys = Reflect.ownKeys(runtime);\n\n for (const propertyKey of propertyKeys) {\n if (typeof propertyKey !== 'string') throw new TypeError('Feature runtime keys must be strings.');\n }\n\n const stringKeys = propertyKeys as string[];\n stringKeys.sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));\n const entries = stringKeys.map(propertyKey => snapshotRuntimeEntry(runtime, propertyKey));\n\n return Object.freeze(entries);\n}\n\nfunction createLocalAttachmentModuleDefinition<const Options extends ModuleDefinitionCandidate>(\n options: ExactInput<Options, ModuleDefinitionCandidate>,\n): LocalAttachmentModuleDefinition<Options['runtime']> {\n if (typeof options !== 'object' || options === null) {\n throw new TypeError('Local attachment options must be an object.');\n }\n\n assertKnownModuleDefinitionOptions(options);\n const id = options.id;\n const sourceRuntime = options.runtime;\n const moduleId = declarationId(id);\n const entries = snapshotRuntimeEntries(sourceRuntime);\n const runtime = Object.freeze(\n Object.fromEntries(entries.map(entry => [entry.key, entry.value])),\n ) as Options['runtime'];\n const definition = Object.freeze({ id: moduleId, runtime }) as LocalAttachmentModuleDefinition<Options['runtime']>;\n moduleDefinitions.set(definition, { entries, moduleId });\n\n return definition;\n}\n\nfunction getModuleDefinition(definition: LocalAttachmentModuleDefinition): LocalAttachmentModuleDefinitionRecord {\n const record = moduleDefinitions.get(definition);\n\n if (record === undefined) throw new TypeError('Local attachment definition is not authentic.');\n\n return record;\n}\n\nfunction assertBlueprintOwnership(\n definition: LocalAttachmentModuleDefinition,\n blueprints: readonly RuntimeBlueprint[],\n): void {\n for (const blueprint of blueprints) {\n const owner = blueprintOwners.get(blueprint);\n\n if (owner !== undefined && owner !== definition) {\n throw new TypeError('Runtime blueprint already belongs to another definition.');\n }\n }\n}\n\nfunction claimBlueprintOwnership(\n definition: LocalAttachmentModuleDefinition,\n blueprints: readonly RuntimeBlueprint[],\n): void {\n blueprints.forEach(blueprint => blueprintOwners.set(blueprint, definition));\n}\n\nfunction classifyEntries(entries: readonly RuntimeEntry[]): {\n readonly attachmentCallEntries: readonly RuntimeEntry[];\n readonly attachmentEntries: readonly RuntimeEntry[];\n readonly blueprints: readonly RuntimeBlueprint[];\n readonly scopeEntries: readonly RuntimeEntry[];\n} {\n const seen = new Set<RuntimeBlueprint>();\n const attachmentCalls: RuntimeEntry[] = [];\n const attachments: RuntimeEntry[] = [];\n const scopes: RuntimeEntry[] = [];\n\n for (const entry of entries) {\n if (seen.has(entry.value)) {\n throw new TypeError('A value of the own builder must be returned under exactly one key.');\n }\n\n seen.add(entry.value);\n\n if (isScopeBlueprint(entry.value)) scopes.push(entry);\n else if (isLifetimeAttachmentRef(entry.value)) attachments.push(entry);\n else attachmentCalls.push(entry);\n }\n\n return {\n attachmentCallEntries: Object.freeze(attachmentCalls),\n attachmentEntries: Object.freeze(attachments),\n blueprints: Object.freeze([...seen]),\n scopeEntries: Object.freeze(scopes),\n };\n}\n\nfunction scopeSlotsFor(entries: readonly RuntimeEntry[]): ReadonlyMap<ScopeBlueprintIdentity, ScopeTemplateSlot> {\n const slots = new Map<ScopeBlueprintIdentity, ScopeTemplateSlot>();\n\n entries.forEach((entry, index) => slots.set(entry.value as ScopeBlueprintIdentity, index as ScopeTemplateSlot));\n\n return slots;\n}\n\nfunction assertReturnedAttachmentCoverage(\n scopeEntries: readonly RuntimeEntry[],\n returnedAttachments: ReadonlySet<LifetimeAttachmentRefIdentity>,\n): void {\n for (const scopeEntry of scopeEntries) {\n const scope = scopeEntry.value as ScopeBlueprintIdentity;\n\n for (const attachment of getScopeAttachmentRefs(scope)) {\n if (!returnedAttachments.has(attachment)) {\n throw new TypeError(`Attachment created by scope ${scopeEntry.key} must be returned under one runtime key.`);\n }\n }\n }\n}\n\ntype AttachmentDraft = {\n readonly identity: RuntimeNodeIdentityIR & { readonly kind: 'attachment' };\n readonly ownerScopeSlot: ScopeTemplateSlot;\n readonly readiness: 'critical' | 'deferred';\n readonly slot: AttachmentTemplateSlot;\n};\n\ntype MutableScopeAttachmentBucket = {\n readonly attachmentSlots: AttachmentTemplateSlot[];\n readonly criticalAttachmentSlots: AttachmentTemplateSlot[];\n readonly drafts: AttachmentDraft[];\n};\n\ntype CompiledScopeAttachmentBucket = {\n readonly attachmentSlots: readonly AttachmentTemplateSlot[];\n readonly criticalAttachmentSlots: readonly AttachmentTemplateSlot[];\n};\n\nfunction attachmentSlotsFor(\n entries: readonly RuntimeEntry[],\n): ReadonlyMap<LifetimeAttachmentRefIdentity, AttachmentTemplateSlot> {\n const slots = new Map<LifetimeAttachmentRefIdentity, AttachmentTemplateSlot>();\n\n entries.forEach((entry, index) =>\n slots.set(entry.value as LifetimeAttachmentRefIdentity, index as AttachmentTemplateSlot),\n );\n\n return slots;\n}\n\nfunction compileAttachmentDrafts(\n moduleId: DeclarationId,\n entries: readonly RuntimeEntry[],\n scopeSlots: ReadonlyMap<ScopeBlueprintIdentity, ScopeTemplateSlot>,\n): {\n readonly drafts: readonly AttachmentDraft[];\n readonly recipes: readonly CompiledLifetimeAttachmentRecipe<unknown>[];\n} {\n const drafts: AttachmentDraft[] = [];\n const recipes: CompiledLifetimeAttachmentRecipe<unknown>[] = [];\n\n entries.forEach((entry, index) => {\n const attachment = entry.value as LifetimeAttachmentRef<unknown>;\n const definition = getAttachmentDefinition(attachment);\n const ownerScopeSlot = scopeSlots.get(definition.owner);\n\n if (ownerScopeSlot === undefined) {\n throw new TypeError(`Attachment runtime key ${entry.key} has an owner scope outside this feature runtime.`);\n }\n\n const slot = index as AttachmentTemplateSlot;\n const identity = runtimeIdentity(moduleId, 'attachment', entry.key) as RuntimeNodeIdentityIR & {\n readonly kind: 'attachment';\n };\n drafts.push({\n identity,\n ownerScopeSlot,\n readiness: definition.recipe.readiness,\n slot,\n });\n recipes.push(definition.recipe);\n });\n\n return { drafts, recipes: Object.freeze(recipes) };\n}\n\nfunction bucketAttachmentDrafts(\n scopeCount: number,\n drafts: readonly AttachmentDraft[],\n): readonly MutableScopeAttachmentBucket[] {\n const buckets = Array.from({ length: scopeCount }, (): MutableScopeAttachmentBucket => ({\n attachmentSlots: [],\n criticalAttachmentSlots: [],\n drafts: [],\n }));\n\n for (const draft of drafts) {\n const bucket = buckets[draft.ownerScopeSlot];\n\n if (bucket === undefined) throw new TypeError(`Attachment slot ${draft.slot} lost its owner scope.`);\n\n bucket.attachmentSlots.push(draft.slot);\n bucket.drafts.push(draft);\n\n if (draft.readiness === 'critical') bucket.criticalAttachmentSlots.push(draft.slot);\n }\n\n return buckets;\n}\n\nfunction compileAttachments(\n moduleId: DeclarationId,\n entries: readonly RuntimeEntry[],\n scopeSlots: ReadonlyMap<ScopeBlueprintIdentity, ScopeTemplateSlot>,\n scopeCount: number,\n): {\n readonly attachments: readonly AttachmentNodeIR[];\n readonly buckets: readonly CompiledScopeAttachmentBucket[];\n readonly recipes: readonly CompiledLifetimeAttachmentRecipe<unknown>[];\n readonly slots: ReadonlyMap<LifetimeAttachmentRefIdentity, AttachmentTemplateSlot>;\n} {\n const slots = attachmentSlotsFor(entries);\n const { drafts, recipes } = compileAttachmentDrafts(moduleId, entries, scopeSlots);\n const mutableBuckets = bucketAttachmentDrafts(scopeCount, drafts);\n const attachments = Array.from<AttachmentNodeIR | undefined>({ length: drafts.length });\n // Attachments of one scope open and close as one wave, so the scope-local slot is the declaration order (D163).\n const buckets = mutableBuckets.map((bucket): CompiledScopeAttachmentBucket => {\n bucket.drafts.forEach((draft, scopeAttachmentSlot) => {\n attachments[draft.slot] = Object.freeze({\n identity: draft.identity,\n ownerScopeSlot: draft.ownerScopeSlot,\n readiness: draft.readiness,\n scopeAttachmentSlot: scopeAttachmentSlot as ScopeAttachmentSlot,\n slot: draft.slot,\n });\n });\n\n return Object.freeze({\n attachmentSlots: Object.freeze(bucket.attachmentSlots),\n criticalAttachmentSlots: Object.freeze(bucket.criticalAttachmentSlots),\n });\n });\n\n if (attachments.some(attachment => attachment === undefined)) {\n throw new TypeError('Compiled attachment graph did not produce every attachment node.');\n }\n\n return {\n attachments: Object.freeze(attachments as AttachmentNodeIR[]),\n buckets: Object.freeze(buckets),\n recipes,\n slots,\n };\n}\n\nfunction compileScopes(\n moduleId: DeclarationId,\n entries: readonly RuntimeEntry[],\n scopeSlots: ReadonlyMap<ScopeBlueprintIdentity, ScopeTemplateSlot>,\n attachmentBuckets: readonly CompiledScopeAttachmentBucket[],\n): readonly ScopeNodeIR[] {\n return Object.freeze(\n entries.map(entry => {\n const slot = scopeSlots.get(entry.value as ScopeBlueprintIdentity);\n\n if (slot === undefined) throw new TypeError(`Scope runtime key ${entry.key} lost its dense slot.`);\n\n const attachmentBucket = attachmentBuckets[slot];\n\n if (attachmentBucket === undefined)\n throw new TypeError(`Scope runtime key ${entry.key} lost its attachment bucket.`);\n\n const identity = runtimeIdentity(moduleId, 'scope', entry.key) as RuntimeNodeIdentityIR & {\n readonly kind: 'scope';\n };\n\n return Object.freeze({\n attachmentSlots: Object.freeze(attachmentBucket.attachmentSlots),\n criticalAttachmentSlots: Object.freeze(attachmentBucket.criticalAttachmentSlots),\n identity,\n slot,\n });\n }),\n );\n}\n\nfunction compileModuleTemplate(definition: LocalAttachmentModuleDefinition): CompiledModuleTemplateIR {\n const cached = compiledDefinitions.get(definition);\n\n if (cached !== undefined) return cached;\n\n const { entries, moduleId } = getModuleDefinition(definition);\n const { attachmentCallEntries, attachmentEntries, blueprints, scopeEntries } = classifyEntries(entries);\n assertBlueprintOwnership(definition, blueprints);\n const returnedAttachments = new Set(attachmentEntries.map(entry => entry.value as LifetimeAttachmentRefIdentity));\n assertReturnedAttachmentCoverage(scopeEntries, returnedAttachments);\n const scopeSlots = scopeSlotsFor(scopeEntries);\n const compiledAttachments = compileAttachments(moduleId, attachmentEntries, scopeSlots, scopeEntries.length);\n const scopes = compileScopes(moduleId, scopeEntries, scopeSlots, compiledAttachments.buckets);\n const compiledCallTargets = compileCallTargetBindings(\n moduleId,\n attachmentCallEntries,\n compiledAttachments.attachments,\n compiledAttachments.slots,\n );\n const template = Object.freeze({\n attachments: compiledAttachments.attachments,\n callTargetBindings: compiledCallTargets.bindings,\n moduleId,\n scopes,\n }) as CompiledModuleTemplateIR;\n claimBlueprintOwnership(definition, blueprints);\n scopeEntries.forEach(entry => sealScopeBlueprint(entry.value as ScopeBlueprintIdentity));\n compiledTemplates.set(template, {\n attachmentCallRecipes: compiledCallTargets.recipes,\n attachmentCallSlots: compiledCallTargets.slots,\n attachmentSlots: compiledAttachments.slots,\n recipes: compiledAttachments.recipes,\n scopeSlots,\n });\n compiledDefinitions.set(definition, template);\n\n return template;\n}\n\nfunction getCompiledTemplateRecord(template: CompiledModuleTemplateIR): CompiledModuleTemplateRecord {\n const record = compiledTemplates.get(template);\n\n if (record === undefined) throw new TypeError('Compiled template is not authentic.');\n\n return record;\n}\n\n/**\n * Authenticity of one template is one fact: instantiation resolves the record once and reads slots from it instead of\n * re-proving the same template on every lookup (D165).\n */\nfunction compiledAttachmentRecipe(\n record: CompiledModuleTemplateRecord,\n slot: AttachmentTemplateSlot,\n): CompiledLifetimeAttachmentRecipe<unknown> {\n const recipe = record.recipes[slot];\n\n if (recipe === undefined) throw new TypeError(`Attachment template slot ${slot} is outside this compilation.`);\n\n return recipe;\n}\n\nfunction compiledAttachmentCallRecipe(\n record: CompiledModuleTemplateRecord,\n slot: CallTargetBindingSlot,\n): CompiledAttachmentCallRecipe<unknown, unknown> {\n const recipe = record.attachmentCallRecipes[slot];\n\n if (recipe === undefined) throw new TypeError(`Call-target binding slot ${slot} is outside this compilation.`);\n\n return recipe;\n}\n\nfunction getCompiledCallTargetBindingSlot<Input, Output>(\n template: CompiledModuleTemplateIR,\n call: AttachmentCallRef<Input, Output>,\n): CallTargetBindingSlot {\n const slot = getCompiledTemplateRecord(template).attachmentCallSlots.get(call);\n\n if (slot === undefined) throw new TypeError('AttachmentCallRef does not belong to this compiled template.');\n\n return slot;\n}\n\nfunction getCompiledScopeSlot<Value>(\n template: CompiledModuleTemplateIR,\n scope: ScopeBlueprint<Value>,\n): ScopeTemplateSlot {\n const slot = getCompiledTemplateRecord(template).scopeSlots.get(scope);\n\n if (slot === undefined) throw new TypeError('ScopeBlueprint does not belong to this compiled template.');\n\n return slot;\n}\n\nfunction getCompiledAttachmentSlot<Value>(\n template: CompiledModuleTemplateIR,\n attachment: LifetimeAttachmentRef<Value>,\n): AttachmentTemplateSlot {\n const slot = getCompiledTemplateRecord(template).attachmentSlots.get(attachment);\n\n if (slot === undefined) throw new TypeError('LifetimeAttachmentRef does not belong to this compiled template.');\n\n return slot;\n}\n\nexport {\n compiledAttachmentCallRecipe,\n compiledAttachmentRecipe,\n compileModuleTemplate,\n createLocalAttachmentModuleDefinition,\n getCompiledAttachmentSlot,\n getCompiledCallTargetBindingSlot,\n getCompiledScopeSlot,\n getCompiledTemplateRecord,\n};\nexport type { CompiledModuleTemplateRecord, LocalAttachmentModuleDefinition, RuntimeBlueprint, RuntimeBlueprintRecord };\n"],"names":["moduleDefinitions","compiledDefinitions","compiledTemplates","blueprintOwners","moduleDefinitionOptionKeys","runtimeKeyPattern","assertKnownModuleDefinitionOptions","options","prototype","key","hasOwn","assertRuntimeRecord","runtime","snapshotRuntimeEntry","propertyKey","descriptor","value","isAttachmentCallRef","isScopeBlueprint","isLifetimeAttachmentRef","snapshotRuntimeEntries","propertyKeys","stringKeys","left","right","entries","createLocalAttachmentModuleDefinition","id","sourceRuntime","moduleId","declarationId","entry","definition","getModuleDefinition","record","assertBlueprintOwnership","blueprints","blueprint","owner","claimBlueprintOwnership","classifyEntries","seen","attachmentCalls","attachments","scopes","scopeSlotsFor","slots","index","assertReturnedAttachmentCoverage","scopeEntries","returnedAttachments","scopeEntry","scope","attachment","getScopeAttachmentRefs","attachmentSlotsFor","compileAttachmentDrafts","scopeSlots","drafts","recipes","getAttachmentDefinition","ownerScopeSlot","slot","identity","runtimeIdentity","bucketAttachmentDrafts","scopeCount","buckets","draft","bucket","compileAttachments","mutableBuckets","scopeAttachmentSlot","compileScopes","attachmentBuckets","attachmentBucket","compileModuleTemplate","cached","attachmentCallEntries","attachmentEntries","compiledAttachments","compiledCallTargets","compileCallTargetBindings","template","sealScopeBlueprint","getCompiledTemplateRecord","compiledAttachmentRecipe","recipe","compiledAttachmentCallRecipe","getCompiledCallTargetBindingSlot","call","getCompiledScopeSlot","getCompiledAttachmentSlot"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { ObservationSlot } from './inspection-observer.js';
2
+ import type { RuntimeErrorReporter } from './runtime-error-reporting.js';
3
+ type ConditionSnapshot = Readonly<{
4
+ active: boolean;
5
+ revision: number;
6
+ }>;
7
+ interface GroupMemberInstance {
8
+ /** Stops admission now, without starting cleanup: the ordered drain below still owns the physical order (D189). */
9
+ readonly fence: () => void;
10
+ readonly ready: Promise<unknown>;
11
+ readonly retire: () => Promise<void>;
12
+ }
13
+ interface ConditionGroupEntry {
14
+ readonly id: string;
15
+ readonly open: () => GroupMemberInstance;
16
+ }
17
+ /**
18
+ * The order between groups (D123). `awaitProvidersReady` resolves when every hard provider this group imports from
19
+ * another group has a ready instance, or when the group stopped wanting to open; `awaitConsumersClosed` resolves when
20
+ * no consumer in another group holds an instance any more. Both come from the application, the only layer that sees
21
+ * every group at once.
22
+ */
23
+ interface ConditionGroupOrder {
24
+ readonly awaitConsumersClosed: () => Promise<void>;
25
+ readonly awaitProvidersReady: (isCurrent: () => boolean, observeStale: (wake: () => void) => () => void) => Promise<void>;
26
+ }
27
+ interface ConditionGroupExecutionOptions {
28
+ readonly entries: readonly ConditionGroupEntry[];
29
+ readonly id: string;
30
+ /** Empty until an inspection session attaches; then the group reports its own automaton (`docs/devtools.md` §2.4). */
31
+ readonly observation?: ObservationSlot;
32
+ /** Absent when no hard edge of the plan crosses into or out of this group. */
33
+ readonly order?: ConditionGroupOrder;
34
+ readonly reporter: RuntimeErrorReporter;
35
+ }
36
+ interface ConditionGroupExecution {
37
+ readonly close: () => Promise<void>;
38
+ readonly publish: (snapshot: ConditionSnapshot) => void;
39
+ readonly settled: () => Promise<void>;
40
+ }
41
+ declare function retireGroupMembersReverse(generations: readonly GroupMemberInstance[]): Promise<readonly unknown[]>;
42
+ declare function createConditionGroupExecution(options: ConditionGroupExecutionOptions): ConditionGroupExecution;
43
+ export { createConditionGroupExecution, retireGroupMembersReverse };
44
+ export type { ConditionGroupEntry, ConditionGroupExecution, ConditionGroupExecutionOptions, ConditionGroupOrder, ConditionSnapshot, GroupMemberInstance, };
@@ -0,0 +1,2 @@
1
+ import{isCancellation as c,createAggregateError as h}from"@opetope/core/internal";import{reportRuntimeFailure as d}from"./runtime-error-reporting.js";const a=-1;function r(s,e){return e.length===1?e[0]:h(e,s)}async function o(s){var i;const e=[];for(let t=s.length-1;t>=0;t-=1)try{await((i=s[t])==null?void 0:i.retire())}catch(n){e.push(n)}return e}class l{options;activeGenerations;attemptedEpoch=a;closeAttempt;closing=!1;desiredActive=!1;desiredEpoch=0;lastConditionRevision=a;needsReconcile=!1;opening;staleWakers=new Set;workspaceFailures=[];worker;constructor(e){this.options=e}close=()=>{var e;if(this.closeAttempt!==void 0)return this.closeAttempt;this.closing=!0,this.desiredActive=!1,this.desiredEpoch+=1,this.wakeStale();for(const i of((e=this.opening)==null?void 0:e.generations)??[])i.fence();this.fenceOpeningAttempt();for(const i of this.activeGenerations??[])i.fence();return this.schedule(),this.closeAttempt=(async()=>{if(await this.settled(),this.workspaceFailures.length!==0)throw r(`Condition group ${this.options.id} failed to close.`,this.workspaceFailures)})(),this.closeAttempt};publish=e=>{this.closing||e.revision<=this.lastConditionRevision||(this.lastConditionRevision=e.revision,e.active!==this.desiredActive&&(this.desiredActive=e.active,this.desiredEpoch+=1,this.wakeStale(),this.fenceOpeningAttempt(),this.schedule()))};settled=async()=>{for(;this.worker!==void 0;)await this.worker};deliverFailure(e){this.closing?this.workspaceFailures.push(e):d(e,this.options.reporter)}fenceOpeningAttempt(){var t,n;if(((t=this.opening)==null?void 0:t.starting)===!0)return;const e=(n=this.opening)==null?void 0:n.generations,i=e==null?void 0:e[e.length-1];if(i!==void 0)try{i.retire().catch(()=>{})}catch{}}isCurrent(e){return!this.closing&&this.desiredActive&&this.desiredEpoch===e}observeStale=e=>(this.staleWakers.add(e),()=>{this.staleWakers.delete(e)});async openCurrent(e){const i={epoch:e,generations:[],starting:!1};this.opening=i,this.report({kind:"opening",opened:0,total:this.options.entries.length});const t=await this.openUnits(i);t.kind==="opened"?(this.activeGenerations=Object.freeze([...i.generations]),this.report({kind:"active"})):await this.rollbackOpening(i,t),this.opening===i&&(this.opening=void 0)}openFailure(e,i){return!this.isCurrent(e)&&c(i)?{kind:"stale"}:{error:i,kind:"failed"}}openMember(e,i){let t;i.starting=!0;try{t=e.open()}finally{i.starting=!1}return i.generations.push(t),this.isCurrent(i.epoch)||(t.fence(),this.fenceOpeningAttempt()),t}async openUnits(e){try{const{order:i}=this.options;if(i!==void 0&&(await i.awaitProvidersReady(()=>this.isCurrent(e.epoch),this.observeStale),!this.isCurrent(e.epoch)))return{kind:"stale"};for(const t of this.options.entries){if(!this.isCurrent(e.epoch))return{kind:"stale"};await this.openMember(t,e).ready,this.report({kind:"opening",opened:e.generations.length,total:this.options.entries.length})}}catch(i){return this.openFailure(e.epoch,i)}return this.isCurrent(e.epoch)?{kind:"opened"}:{kind:"stale"}}async pump(){for(;this.needsReconcile;)this.needsReconcile=!1,await this.reconcile()}async pumpRetire(e){this.report({kind:"retiring",remaining:e.length}),this.options.order!==void 0&&await this.options.order.awaitConsumersClosed();const i=await o(e);if(i.length>0){this.deliverFailure(r(`Condition group ${this.options.id} failed to retire.`,i)),this.report({failure:{code:"cleanup-failed",phase:"close",subject:"feature"},kind:"failed"});return}this.report(this.closing?{kind:"closed"}:{kind:"inactive",reason:"condition-inactive"})}async reconcile(){if(this.desiredActive&&!this.closing){if(this.activeGenerations!==void 0||this.attemptedEpoch===this.desiredEpoch)return;const i=this.desiredEpoch;this.attemptedEpoch=i,await this.openCurrent(i);return}if(this.activeGenerations===void 0)return;const e=this.activeGenerations;this.activeGenerations=void 0,await this.pumpRetire(e)}report(e){var i,t;(t=(i=this.options.observation)==null?void 0:i.observer)==null||t.groupState(this.options.id,e)}async rollbackOpening(e,i){const t=await o(e.generations),n=i.kind==="failed"?[i.error,...t]:t;if(n.length>0){this.deliverFailure(r(`Condition group ${this.options.id} failed to open.`,n)),this.report({failure:{code:"not-ready",phase:"open",subject:"feature"},kind:"failed"});return}this.report(this.closing?{kind:"closed"}:{kind:"inactive",reason:"condition-inactive"})}schedule(){if(this.needsReconcile=!0,this.worker!==void 0)return;const e=Promise.resolve().then(()=>this.pump()).catch(i=>this.deliverFailure(i)).finally(()=>{this.worker===e&&(this.worker=void 0),this.needsReconcile&&this.schedule()});this.worker=e}wakeStale(){const e=[...this.staleWakers];this.staleWakers.clear();for(const i of e)i()}}function p(s){return new l(s)}export{p as createConditionGroupExecution,o as retireGroupMembersReverse};
2
+ //# sourceMappingURL=condition-group-execution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition-group-execution.js","sources":["../src/condition-group-execution.ts"],"sourcesContent":["import { createAggregateError, isCancellation } from '@opetope/core/internal';\n\nimport type { ObservationSlot } from './inspection-observer';\nimport type { ConditionGroupState } from './inspection-protocol';\nimport { reportRuntimeFailure } from './runtime-error-reporting';\nimport type { RuntimeErrorReporter } from './runtime-error-reporting';\n\ntype ConditionSnapshot = Readonly<{\n active: boolean;\n revision: number;\n}>;\n\ninterface GroupMemberInstance {\n /** Stops admission now, without starting cleanup: the ordered drain below still owns the physical order (D189). */\n readonly fence: () => void;\n readonly ready: Promise<unknown>;\n readonly retire: () => Promise<void>;\n}\n\ninterface ConditionGroupEntry {\n readonly id: string;\n readonly open: () => GroupMemberInstance;\n}\n\n/**\n * The order between groups (D123). `awaitProvidersReady` resolves when every hard provider this group imports from\n * another group has a ready instance, or when the group stopped wanting to open; `awaitConsumersClosed` resolves when\n * no consumer in another group holds an instance any more. Both come from the application, the only layer that sees\n * every group at once.\n */\ninterface ConditionGroupOrder {\n readonly awaitConsumersClosed: () => Promise<void>;\n readonly awaitProvidersReady: (\n isCurrent: () => boolean,\n observeStale: (wake: () => void) => () => void,\n ) => Promise<void>;\n}\n\ninterface ConditionGroupExecutionOptions {\n readonly entries: readonly ConditionGroupEntry[];\n readonly id: string;\n /** Empty until an inspection session attaches; then the group reports its own automaton (`docs/devtools.md` §2.4). */\n readonly observation?: ObservationSlot;\n /** Absent when no hard edge of the plan crosses into or out of this group. */\n readonly order?: ConditionGroupOrder;\n readonly reporter: RuntimeErrorReporter;\n}\n\ninterface ConditionGroupExecution {\n readonly close: () => Promise<void>;\n readonly publish: (snapshot: ConditionSnapshot) => void;\n readonly settled: () => Promise<void>;\n}\n\ntype OpeningAttempt = {\n readonly epoch: number;\n readonly generations: GroupMemberInstance[];\n starting: boolean;\n};\n\ntype OpeningOutcome = { readonly error: unknown; readonly kind: 'failed' } | { readonly kind: 'opened' | 'stale' };\n\nconst staleRevision = -1;\n\nfunction combineFailures(message: string, failures: readonly unknown[]): unknown {\n if (failures.length === 1) return failures[0];\n\n return createAggregateError(failures, message);\n}\n\nasync function retireGroupMembersReverse(generations: readonly GroupMemberInstance[]): Promise<readonly unknown[]> {\n const failures: unknown[] = [];\n\n for (let index = generations.length - 1; index >= 0; index -= 1) {\n try {\n await generations[index]?.retire();\n } catch (error) {\n failures.push(error);\n }\n }\n\n return failures;\n}\n\nclass ConditionGroupController implements ConditionGroupExecution {\n private activeGenerations: readonly GroupMemberInstance[] | undefined;\n private attemptedEpoch = staleRevision;\n private closeAttempt: Promise<void> | undefined;\n private closing = false;\n private desiredActive = false;\n private desiredEpoch = 0;\n private lastConditionRevision = staleRevision;\n private needsReconcile = false;\n private opening: OpeningAttempt | undefined;\n private readonly staleWakers = new Set<() => void>();\n private readonly workspaceFailures: unknown[] = [];\n private worker: Promise<void> | undefined;\n\n constructor(private readonly options: ConditionGroupExecutionOptions) {}\n\n readonly close = (): Promise<void> => {\n if (this.closeAttempt !== undefined) return this.closeAttempt;\n\n this.closing = true;\n this.desiredActive = false;\n this.desiredEpoch += 1;\n this.wakeStale();\n // D189: ready predecessors remain in the opening attempt until the whole group has opened.\n for (const generation of this.opening?.generations ?? []) generation.fence();\n this.fenceOpeningAttempt();\n // D189: every live member stops admitting work in the same turn as `close()`; the drain below keeps its order.\n for (const generation of this.activeGenerations ?? []) generation.fence();\n\n this.schedule();\n\n this.closeAttempt = (async () => {\n await this.settled();\n\n if (this.workspaceFailures.length === 0) return;\n\n throw combineFailures(`Condition group ${this.options.id} failed to close.`, this.workspaceFailures);\n })();\n\n return this.closeAttempt;\n };\n\n readonly publish = (snapshot: ConditionSnapshot): void => {\n if (this.closing || snapshot.revision <= this.lastConditionRevision) return;\n\n this.lastConditionRevision = snapshot.revision;\n\n if (snapshot.active === this.desiredActive) return;\n\n this.desiredActive = snapshot.active;\n this.desiredEpoch += 1;\n this.wakeStale();\n this.fenceOpeningAttempt();\n this.schedule();\n };\n\n readonly settled = async (): Promise<void> => {\n while (this.worker !== undefined) await this.worker;\n };\n\n private deliverFailure(error: unknown): void {\n if (this.closing) this.workspaceFailures.push(error);\n else reportRuntimeFailure(error, this.options.reporter);\n }\n\n private fenceOpeningAttempt(): void {\n // A foreign open callback may close before returning its handle. That member must drain before its predecessors.\n if (this.opening?.starting === true) return;\n\n const generations = this.opening?.generations;\n const generation = generations?.[generations.length - 1];\n\n if (generation === undefined) return;\n\n try {\n void generation.retire().catch(() => undefined);\n } catch {\n // The serialized rollback below owns failure delivery.\n }\n }\n\n private isCurrent(epoch: number): boolean {\n return !this.closing && this.desiredActive && this.desiredEpoch === epoch;\n }\n\n /** Resolves as soon as this group stops wanting the attempt that is waiting: a new condition, or a close. */\n private readonly observeStale = (wake: () => void): (() => void) => {\n this.staleWakers.add(wake);\n\n return () => void this.staleWakers.delete(wake);\n };\n\n private async openCurrent(epoch: number): Promise<void> {\n const attempt: OpeningAttempt = { epoch, generations: [], starting: false };\n this.opening = attempt;\n this.report({ kind: 'opening', opened: 0, total: this.options.entries.length });\n const outcome = await this.openUnits(attempt);\n\n if (outcome.kind === 'opened') {\n this.activeGenerations = Object.freeze([...attempt.generations]);\n this.report({ kind: 'active' });\n } else await this.rollbackOpening(attempt, outcome);\n\n if (this.opening === attempt) this.opening = undefined;\n }\n\n /** A rejection while the group still wants this attempt is a real failure; otherwise the open was just stale. */\n private openFailure(epoch: number, error: unknown): OpeningOutcome {\n return !this.isCurrent(epoch) && isCancellation(error) ? { kind: 'stale' } : { error, kind: 'failed' };\n }\n\n private openMember(entry: ConditionGroupEntry, attempt: OpeningAttempt): GroupMemberInstance {\n let generation: GroupMemberInstance;\n attempt.starting = true;\n try {\n generation = entry.open();\n } finally {\n attempt.starting = false;\n }\n attempt.generations.push(generation);\n\n if (!this.isCurrent(attempt.epoch)) {\n generation.fence();\n this.fenceOpeningAttempt();\n }\n\n return generation;\n }\n\n private async openUnits(attempt: OpeningAttempt): Promise<OpeningOutcome> {\n try {\n // `spec.md` §4: a hard provider opens before its consumer. Across groups that order is this wait and nothing else; a\n // group with no crossing edge must not pay even a tick for it.\n const { order } = this.options;\n\n if (order !== undefined) {\n await order.awaitProvidersReady(() => this.isCurrent(attempt.epoch), this.observeStale);\n\n if (!this.isCurrent(attempt.epoch)) return { kind: 'stale' };\n }\n\n for (const entry of this.options.entries) {\n if (!this.isCurrent(attempt.epoch)) return { kind: 'stale' };\n\n const generation = this.openMember(entry, attempt);\n await generation.ready;\n this.report({ kind: 'opening', opened: attempt.generations.length, total: this.options.entries.length });\n }\n } catch (error) {\n return this.openFailure(attempt.epoch, error);\n }\n\n return this.isCurrent(attempt.epoch) ? { kind: 'opened' } : { kind: 'stale' };\n }\n\n private async pump(): Promise<void> {\n while (this.needsReconcile) {\n this.needsReconcile = false;\n await this.reconcile();\n }\n }\n\n private async pumpRetire(generations: readonly GroupMemberInstance[]): Promise<void> {\n this.report({ kind: 'retiring', remaining: generations.length });\n\n // `spec.md` §4: the consumer closes before its provider, so a group holds its members until its consumers have let go.\n if (this.options.order !== undefined) await this.options.order.awaitConsumersClosed();\n\n const failures = await retireGroupMembersReverse(generations);\n\n if (failures.length > 0) {\n this.deliverFailure(combineFailures(`Condition group ${this.options.id} failed to retire.`, failures));\n this.report({ failure: { code: 'cleanup-failed', phase: 'close', subject: 'feature' }, kind: 'failed' });\n\n return;\n }\n\n this.report(this.closing ? { kind: 'closed' } : { kind: 'inactive', reason: 'condition-inactive' });\n }\n\n private async reconcile(): Promise<void> {\n if (this.desiredActive && !this.closing) {\n if (this.activeGenerations !== undefined || this.attemptedEpoch === this.desiredEpoch) return;\n\n const epoch = this.desiredEpoch;\n this.attemptedEpoch = epoch;\n await this.openCurrent(epoch);\n\n return;\n }\n\n if (this.activeGenerations === undefined) return;\n\n const generations = this.activeGenerations;\n this.activeGenerations = undefined;\n\n await this.pumpRetire(generations);\n }\n\n private report(state: ConditionGroupState): void {\n this.options.observation?.observer?.groupState(this.options.id, state);\n }\n\n private async rollbackOpening(\n attempt: OpeningAttempt,\n outcome: Exclude<OpeningOutcome, { kind: 'opened' }>,\n ): Promise<void> {\n const cleanupFailures = await retireGroupMembersReverse(attempt.generations);\n const failures = outcome.kind === 'failed' ? [outcome.error, ...cleanupFailures] : cleanupFailures;\n\n if (failures.length > 0) {\n this.deliverFailure(combineFailures(`Condition group ${this.options.id} failed to open.`, failures));\n this.report({ failure: { code: 'not-ready', phase: 'open', subject: 'feature' }, kind: 'failed' });\n\n return;\n }\n\n this.report(this.closing ? { kind: 'closed' } : { kind: 'inactive', reason: 'condition-inactive' });\n }\n\n private schedule(): void {\n this.needsReconcile = true;\n\n if (this.worker !== undefined) return;\n\n const worker = Promise.resolve()\n .then(() => this.pump())\n .catch((error: unknown) => this.deliverFailure(error))\n .finally(() => {\n if (this.worker === worker) this.worker = undefined;\n\n if (this.needsReconcile) this.schedule();\n });\n this.worker = worker;\n }\n\n private wakeStale(): void {\n const wakers = [...this.staleWakers];\n this.staleWakers.clear();\n\n for (const wake of wakers) wake();\n }\n}\n\nfunction createConditionGroupExecution(options: ConditionGroupExecutionOptions): ConditionGroupExecution {\n return new ConditionGroupController(options);\n}\n\nexport { createConditionGroupExecution, retireGroupMembersReverse };\nexport type {\n ConditionGroupEntry,\n ConditionGroupExecution,\n ConditionGroupExecutionOptions,\n ConditionGroupOrder,\n ConditionSnapshot,\n GroupMemberInstance,\n};\n"],"names":["staleRevision","combineFailures","message","failures","createAggregateError","retireGroupMembersReverse","generations","index","_a","error","ConditionGroupController","options","generation","snapshot","reportRuntimeFailure","_b","epoch","wake","attempt","outcome","isCancellation","entry","order","state","cleanupFailures","worker","wakers","createConditionGroupExecution"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The one thing devtools may write (`docs/devtools.md` §5.1, D176): a debug override that forces one condition to read `false`.
3
+ * The override wraps the condition's own `Readable`, so a suspended group deactivates through the ordinary
4
+ * transactional path and nobody downstream learns a new way to lose its instances.
5
+ */
6
+ import type { Readable } from '@opetope/core';
7
+ /**
8
+ * Who is asking for this move and whether they still may. `owned` is read after the product source, because reading
9
+ * it runs foreign code that can close the asking session; `owner` says whose override this is, so a session that
10
+ * lost the control slot undoes what it set and never what its successor set (D191).
11
+ */
12
+ interface OverrideClaim {
13
+ readonly owned: () => boolean;
14
+ readonly owner: object;
15
+ }
16
+ interface ControllableCondition {
17
+ /** Whether the debug override is set right now, which the observation port reports as `force-inactive`. */
18
+ readonly overridden: () => boolean;
19
+ readonly readable: Readable<boolean>;
20
+ /** Both report whether the visible truth moved: an override over an already false source changes no transition. */
21
+ readonly resume: (claim: OverrideClaim) => boolean;
22
+ /** What the product source says, override or not: the port reports both halves so the panel can show the badge. */
23
+ readonly source: () => boolean;
24
+ readonly suspend: (claim: OverrideClaim) => boolean;
25
+ }
26
+ declare function createControllableCondition(source: Readable<boolean>): ControllableCondition;
27
+ export { createControllableCondition };
28
+ export type { ControllableCondition };
@@ -0,0 +1,2 @@
1
+ function a(n){const s=new Set;let t=!1,i;const o=()=>!t&&n.getSnapshot(),l=()=>{for(const{listener:e}of[...s])e()},u=(e,r)=>{if(t===e||!e&&i!==r.owner)return!1;const f=o();return!r.owned()||(t=e,i=e?r.owner:void 0,o()===f)?!1:(l(),!0)};return Object.freeze({overridden:()=>t,readable:Object.freeze({getSnapshot:o,subscribe:e=>{const r={listener:e};s.add(r);const f=n.subscribe(e);let d=!0;return()=>{d&&(d=!1,s.delete(r),f())}}}),resume:e=>u(!1,e),source:()=>n.getSnapshot(),suspend:e=>u(!0,e)})}export{a as createControllableCondition};
2
+ //# sourceMappingURL=condition-override.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition-override.js","sources":["../src/condition-override.ts"],"sourcesContent":["/**\n * The one thing devtools may write (`docs/devtools.md` §5.1, D176): a debug override that forces one condition to read `false`.\n * The override wraps the condition's own `Readable`, so a suspended group deactivates through the ordinary\n * transactional path and nobody downstream learns a new way to lose its instances.\n */\n\nimport type { Readable } from '@opetope/core';\n\n/**\n * Who is asking for this move and whether they still may. `owned` is read after the product source, because reading\n * it runs foreign code that can close the asking session; `owner` says whose override this is, so a session that\n * lost the control slot undoes what it set and never what its successor set (D191).\n */\ninterface OverrideClaim {\n readonly owned: () => boolean;\n readonly owner: object;\n}\n\ninterface ControllableCondition {\n /** Whether the debug override is set right now, which the observation port reports as `force-inactive`. */\n readonly overridden: () => boolean;\n readonly readable: Readable<boolean>;\n /** Both report whether the visible truth moved: an override over an already false source changes no transition. */\n readonly resume: (claim: OverrideClaim) => boolean;\n /** What the product source says, override or not: the port reports both halves so the panel can show the badge. */\n readonly source: () => boolean;\n readonly suspend: (claim: OverrideClaim) => boolean;\n}\n\nfunction createControllableCondition(source: Readable<boolean>): ControllableCondition {\n const listeners = new Set<{ readonly listener: () => void }>();\n // One live control session owns the override at a time (D191), and the token of that session says which one.\n let overridden = false;\n let overrideOwner: object | undefined;\n const visible = (): boolean => !overridden && source.getSnapshot();\n const notify = (): void => {\n for (const { listener } of [...listeners]) listener();\n };\n const setOverride = (next: boolean, claim: OverrideClaim): boolean => {\n if (overridden === next) return false;\n\n // D191: an override is undone by the session that set it. A session whose slot was handed over during its own\n // command finds the successor's override here and leaves it alone.\n if (!next && overrideOwner !== claim.owner) return false;\n\n const before = visible();\n\n // D191: reading the product source runs foreign code, and that code may close the session which asked for this\n // override. An override nobody owns any more is never set, so no closed session can strand one.\n if (!claim.owned()) return false;\n\n overridden = next;\n overrideOwner = next ? claim.owner : undefined;\n\n if (visible() === before) return false;\n\n notify();\n\n return true;\n };\n\n return Object.freeze({\n overridden: (): boolean => overridden,\n readable: Object.freeze({\n getSnapshot: visible,\n subscribe: (listener: () => void): (() => void) => {\n const registration = { listener };\n listeners.add(registration);\n const release = source.subscribe(listener);\n let subscribed = true;\n\n return (): void => {\n if (!subscribed) return;\n\n subscribed = false;\n listeners.delete(registration);\n release();\n };\n },\n }),\n resume: (claim: OverrideClaim): boolean => setOverride(false, claim),\n source: (): boolean => source.getSnapshot(),\n suspend: (claim: OverrideClaim): boolean => setOverride(true, claim),\n });\n}\n\nexport { createControllableCondition };\nexport type { ControllableCondition };\n"],"names":["createControllableCondition","source","listeners","overridden","overrideOwner","visible","notify","listener","setOverride","next","claim","before","registration","release","subscribed"],"mappings":"AA6BA,SAASA,EAA4BC,EAAyB,CAC5D,MAAMC,EAAY,IAAI,IAEtB,IAAIC,EAAa,GACbC,EACJ,MAAMC,EAAU,IAAe,CAACF,GAAcF,EAAO,YAAW,EAC1DK,EAAS,IAAW,CACxB,SAAW,CAAE,SAAAC,IAAc,CAAC,GAAGL,CAAS,EAAGK,EAAQ,CACrD,EACMC,EAAc,CAACC,EAAeC,IAAiC,CAKnE,GAJIP,IAAeM,GAIf,CAACA,GAAQL,IAAkBM,EAAM,MAAO,MAAO,GAEnD,MAAMC,EAASN,EAAO,EAStB,MALI,CAACK,EAAM,MAAK,IAEhBP,EAAaM,EACbL,EAAgBK,EAAOC,EAAM,MAAQ,OAEjCL,EAAO,IAAOM,GAAe,IAEjCL,EAAM,EAEC,GACT,EAEA,OAAO,OAAO,OAAO,CACnB,WAAY,IAAeH,EAC3B,SAAU,OAAO,OAAO,CACtB,YAAaE,EACb,UAAYE,GAAsC,CAChD,MAAMK,EAAe,CAAE,SAAAL,CAAQ,EAC/BL,EAAU,IAAIU,CAAY,EAC1B,MAAMC,EAAUZ,EAAO,UAAUM,CAAQ,EACzC,IAAIO,EAAa,GAEjB,MAAO,IAAW,CACXA,IAELA,EAAa,GACbZ,EAAU,OAAOU,CAAY,EAC7BC,EAAO,EACT,CACF,EACD,EACD,OAASH,GAAkCF,EAAY,GAAOE,CAAK,EACnE,OAAQ,IAAeT,EAAO,YAAW,EACzC,QAAUS,GAAkCF,EAAY,GAAME,CAAK,CACpE,CAAA,CACH"}
@@ -0,0 +1,10 @@
1
+ import type { Readable } from '@opetope/core';
2
+ /** How a group learns whether its conditions hold: read now, then publish every change until released. */
3
+ interface ConditionSource {
4
+ readonly readAndSubscribe: (publish: (active: boolean) => void) => () => void;
5
+ }
6
+ declare function conditionFrom(readable: Readable<boolean>): ConditionSource;
7
+ /** A group lives while every one of its conditions holds, so the combined source publishes their conjunction. */
8
+ declare function combineConditionSources(sources: readonly ConditionSource[]): ConditionSource;
9
+ export { combineConditionSources, conditionFrom };
10
+ export type { ConditionSource };
@@ -0,0 +1,2 @@
1
+ import{createAggregateError as h}from"@opetope/core/internal";function l(e,o=[]){const t=[...o];for(let r=e.length-1;r>=0;r-=1)try{e[r]()}catch(n){t.push(n)}if(t.length===1)throw t[0];if(t.length>1)throw h(t,"Condition sources failed to release.")}function b(e){if(typeof e!="boolean")throw new TypeError("Condition source must read a boolean.");return e}function p(e){if(typeof e!="object"||e===null||typeof e.getSnapshot!="function"||typeof e.subscribe!="function")throw new TypeError("A condition requires a Readable<boolean>.");return Object.freeze({readAndSubscribe(o){let t,r=!0;const n=[],f=()=>{const c=b(e.getSnapshot());Object.is(t,c)||(t=c,r?n.push(t):o(t))},i=e.subscribe(f);if(typeof i!="function")throw new TypeError("Condition source must return a disposer.");try{for(f();n.length>0;)o(n.shift());r=!1}catch(c){l([i],[c])}return i}})}function y(e){const[o]=e;return e.length===1&&o!==void 0?o:Object.freeze({readAndSubscribe(t){const r=e.map(()=>!1),n=[];let f=!0,i;const c=()=>{const s=r.every(u=>u);Object.is(i,s)||(i=s,f||t(s))};try{for(const[s,u]of e.entries())n.push(u.readAndSubscribe(d=>{r[s]=d,c()}))}catch(s){l(n,[s])}f=!1,t(i??!1);let a=!1;return()=>{a||(a=!0,l(n))}}})}export{y as combineConditionSources,p as conditionFrom};
2
+ //# sourceMappingURL=condition-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition-source.js","sources":["../src/condition-source.ts"],"sourcesContent":["import type { Readable } from '@opetope/core';\nimport { createAggregateError } from '@opetope/core/internal';\n\nfunction releaseConditionSources(releases: readonly (() => void)[], initial: readonly unknown[] = []): void {\n const failures = [...initial];\n\n for (let index = releases.length - 1; index >= 0; index -= 1) {\n try {\n releases[index]!();\n } catch (error) {\n failures.push(error);\n }\n }\n\n if (failures.length === 1) throw failures[0];\n\n if (failures.length > 1) throw createAggregateError(failures, 'Condition sources failed to release.');\n}\n\n/** How a group learns whether its conditions hold: read now, then publish every change until released. */\ninterface ConditionSource {\n readonly readAndSubscribe: (publish: (active: boolean) => void) => () => void;\n}\n\nfunction requireActive(value: unknown): boolean {\n if (typeof value !== 'boolean') throw new TypeError('Condition source must read a boolean.');\n\n return value;\n}\n\nfunction conditionFrom(readable: Readable<boolean>): ConditionSource {\n if (\n typeof readable !== 'object' ||\n readable === null ||\n typeof readable.getSnapshot !== 'function' ||\n typeof readable.subscribe !== 'function'\n ) {\n throw new TypeError('A condition requires a Readable<boolean>.');\n }\n\n return Object.freeze({\n readAndSubscribe(publish: (active: boolean) => void): () => void {\n let active: boolean | undefined;\n let buffering = true;\n const buffered: boolean[] = [];\n const observe = (): void => {\n const next = requireActive(readable.getSnapshot());\n\n if (Object.is(active, next)) return;\n\n active = next;\n\n if (buffering) buffered.push(active);\n else publish(active);\n };\n const release = readable.subscribe(observe);\n\n if (typeof release !== 'function') throw new TypeError('Condition source must return a disposer.');\n\n try {\n observe();\n\n while (buffered.length > 0) publish(buffered.shift()!);\n\n buffering = false;\n } catch (error) {\n releaseConditionSources([release], [error]);\n }\n\n return release;\n },\n });\n}\n\n/** A group lives while every one of its conditions holds, so the combined source publishes their conjunction. */\nfunction combineConditionSources(sources: readonly ConditionSource[]): ConditionSource {\n const [only] = sources;\n\n if (sources.length === 1 && only !== undefined) return only;\n\n return Object.freeze({\n readAndSubscribe(publish: (active: boolean) => void): () => void {\n const values = sources.map(() => false);\n const releases: (() => void)[] = [];\n let opening = true;\n let published: boolean | undefined;\n const emit = (): void => {\n const next = values.every(value => value);\n\n if (Object.is(published, next)) return;\n\n published = next;\n\n if (!opening) publish(next);\n };\n\n try {\n for (const [index, source] of sources.entries()) {\n releases.push(\n source.readAndSubscribe(active => {\n values[index] = active;\n emit();\n }),\n );\n }\n } catch (error) {\n releaseConditionSources(releases, [error]);\n }\n\n opening = false;\n publish(published ?? false);\n\n let released = false;\n\n return (): void => {\n if (released) return;\n\n released = true;\n releaseConditionSources(releases);\n };\n },\n });\n}\n\nexport { combineConditionSources, conditionFrom };\nexport type { ConditionSource };\n"],"names":["releaseConditionSources","releases","initial","failures","index","error","createAggregateError","requireActive","value","conditionFrom","readable","publish","active","buffering","buffered","observe","next","release","combineConditionSources","sources","only","values","opening","published","emit","source","released"],"mappings":"8DAGA,SAASA,EAAwBC,EAAmCC,EAA8B,GAAE,CAClG,MAAMC,EAAW,CAAC,GAAGD,CAAO,EAE5B,QAASE,EAAQH,EAAS,OAAS,EAAGG,GAAS,EAAGA,GAAS,EACzD,GAAI,CACFH,EAASG,CAAK,EAAE,CAClB,OAASC,EAAO,CACdF,EAAS,KAAKE,CAAK,CACrB,CAGF,GAAIF,EAAS,SAAW,EAAG,MAAMA,EAAS,CAAC,EAE3C,GAAIA,EAAS,OAAS,EAAG,MAAMG,EAAqBH,EAAU,sCAAsC,CACtG,CAOA,SAASI,EAAcC,EAAc,CACnC,GAAI,OAAOA,GAAU,UAAW,MAAM,IAAI,UAAU,uCAAuC,EAE3F,OAAOA,CACT,CAEA,SAASC,EAAcC,EAA2B,CAChD,GACE,OAAOA,GAAa,UACpBA,IAAa,MACb,OAAOA,EAAS,aAAgB,YAChC,OAAOA,EAAS,WAAc,WAE9B,MAAM,IAAI,UAAU,2CAA2C,EAGjE,OAAO,OAAO,OAAO,CACnB,iBAAiBC,EAAkC,CACjD,IAAIC,EACAC,EAAY,GAChB,MAAMC,EAAsB,CAAA,EACtBC,EAAU,IAAW,CACzB,MAAMC,EAAOT,EAAcG,EAAS,YAAW,CAAE,EAE7C,OAAO,GAAGE,EAAQI,CAAI,IAE1BJ,EAASI,EAELH,EAAWC,EAAS,KAAKF,CAAM,EAC9BD,EAAQC,CAAM,EACrB,EACMK,EAAUP,EAAS,UAAUK,CAAO,EAE1C,GAAI,OAAOE,GAAY,WAAY,MAAM,IAAI,UAAU,0CAA0C,EAEjG,GAAI,CAGF,IAFAF,EAAO,EAEAD,EAAS,OAAS,GAAGH,EAAQG,EAAS,OAAQ,EAErDD,EAAY,EACd,OAASR,EAAO,CACdL,EAAwB,CAACiB,CAAO,EAAG,CAACZ,CAAK,CAAC,CAC5C,CAEA,OAAOY,CACT,CACD,CAAA,CACH,CAGA,SAASC,EAAwBC,EAAmC,CAClE,KAAM,CAACC,CAAI,EAAID,EAEf,OAAIA,EAAQ,SAAW,GAAKC,IAAS,OAAkBA,EAEhD,OAAO,OAAO,CACnB,iBAAiBT,EAAkC,CACjD,MAAMU,EAASF,EAAQ,IAAI,IAAM,EAAK,EAChClB,EAA2B,CAAA,EACjC,IAAIqB,EAAU,GACVC,EACJ,MAAMC,EAAO,IAAW,CACtB,MAAMR,EAAOK,EAAO,MAAMb,GAASA,CAAK,EAEpC,OAAO,GAAGe,EAAWP,CAAI,IAE7BO,EAAYP,EAEPM,GAASX,EAAQK,CAAI,EAC5B,EAEA,GAAI,CACF,SAAW,CAACZ,EAAOqB,CAAM,IAAKN,EAAQ,QAAO,EAC3ClB,EAAS,KACPwB,EAAO,iBAAiBb,GAAS,CAC/BS,EAAOjB,CAAK,EAAIQ,EAChBY,EAAI,CACN,CAAC,CAAC,CAGR,OAASnB,EAAO,CACdL,EAAwBC,EAAU,CAACI,CAAK,CAAC,CAC3C,CAEAiB,EAAU,GACVX,EAAQY,GAAa,EAAK,EAE1B,IAAIG,EAAW,GAEf,MAAO,IAAW,CACZA,IAEJA,EAAW,GACX1B,EAAwBC,CAAQ,EAClC,CACF,CACD,CAAA,CACH"}
@@ -0,0 +1,14 @@
1
+ import type { DeclarationId } from '@opetope/core';
2
+ declare const conditionBrand: unique symbol;
3
+ /**
4
+ * A fact a feature waits for. Without `from` the host binds it at `openApplication`; with `from` it is computed from
5
+ * the exports of one feature and the host must not bind it (D93). The type is a leaf so the feature vocabulary and
6
+ * the condition declaration do not import each other.
7
+ */
8
+ interface Condition<Id extends string = string, Source = unknown> {
9
+ readonly [conditionBrand]: () => readonly [Id, Source];
10
+ readonly id: DeclarationId & Id;
11
+ }
12
+ /** The ids the host binds: a condition with `from` reads its source feature instead (D93). */
13
+ type HostConditionId<Value> = Value extends Condition<infer Id, infer Source> ? ([Source] extends [undefined] ? Id : never) : never;
14
+ export type { Condition, HostConditionId };
@@ -0,0 +1,28 @@
1
+ import type { DeclarationId, Readable } from '@opetope/core';
2
+ import type { Condition } from './condition-types.js';
3
+ import type { AnyFeature, FeatureExportsOf } from './feature-authoring-types.js';
4
+ interface HostConditionOptions<Id extends string> {
5
+ readonly id: Id;
6
+ }
7
+ interface FeatureConditionOptions<Id extends string, Source extends AnyFeature> {
8
+ readonly from: Source;
9
+ readonly id: Id;
10
+ readonly select: (exports: FeatureExportsOf<Source>) => Readable<boolean>;
11
+ }
12
+ type ConditionRecord = Readonly<{
13
+ from: AnyFeature | undefined;
14
+ id: DeclarationId;
15
+ select: ((exports: unknown) => Readable<boolean>) | undefined;
16
+ }>;
17
+ interface DefineCondition {
18
+ /** The computed form comes first: `select` needs its context before the host form's excess-property check. */
19
+ <const Id extends string, const Source extends AnyFeature>(options: FeatureConditionOptions<Id, Source>): Condition<Id, Source>;
20
+ <const Id extends string>(options: HostConditionOptions<Id>): Condition<Id, undefined>;
21
+ }
22
+ declare const defineCondition: DefineCondition;
23
+ declare function requireConditionRecord(condition: Condition): ConditionRecord;
24
+ /** Conditions are a set: the same set in any order is one group, so the canonical key sorts the ids (D93). */
25
+ declare function conditionSetKey(conditions: readonly Condition[]): string;
26
+ declare function snapshotFeatureConditions(value: unknown): readonly Condition[];
27
+ export { conditionSetKey, defineCondition, requireConditionRecord, snapshotFeatureConditions };
28
+ export type { Condition, HostConditionId } from './condition-types.js';
@@ -0,0 +1,2 @@
1
+ import{declarationId as f}from"@opetope/core";import{isFeature as a}from"./feature-definition-support.js";import{assertPlainRecord as u,dataEntries as m,assertExactDataKeys as s}from"./feature-record.js";const r=new WeakMap;function h(e){if(typeof e!="string")throw new TypeError("Condition id must be a string.");return f(e)}function w(e,n){const o=e.from,t=e.select;if(!a(o))throw new TypeError("Condition from must be an authentic Feature.");if(typeof t!="function")throw new TypeError("Condition select must be a function.");return Object.freeze({from:o,id:n,select:t})}const p=(e=>{u(e,"Condition options");const n=m(e,"Condition options"),o=new Set(n.map(([c])=>c));o.has("from")||o.has("select")?s(e,n,["from","id","select"],"Condition options"):s(e,n,["id"],"Condition options");const t=h(e.id),i=Object.freeze({id:t}),d=o.has("from")?w(e,t):Object.freeze({from:void 0,id:t,select:void 0});return r.set(i,d),i});function y(e){return typeof e=="object"&&e!==null&&r.has(e)}function C(e){const n=r.get(e);if(n===void 0)throw new TypeError("Condition is not authentic.");return n}function b(e){return[...e.map(n=>n.id)].sort().join("|")}function E(e){if(e===void 0)return Object.freeze([]);if(!Array.isArray(e))throw new TypeError("defineFeature when must be an array of Conditions.");const n=Array.from(e);for(const t of n)if(!y(t))throw new TypeError("defineFeature when must contain authentic Conditions.");if(new Set(n.map(t=>t.id)).size!==n.length)throw new TypeError("defineFeature when must not repeat a Condition.");return Object.freeze(n)}export{b as conditionSetKey,p as defineCondition,C as requireConditionRecord,E as snapshotFeatureConditions};
2
+ //# sourceMappingURL=condition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition.js","sources":["../src/condition.ts"],"sourcesContent":["import { declarationId } from '@opetope/core';\nimport type { DeclarationId, Readable } from '@opetope/core';\n\nimport type { Condition } from './condition-types';\nimport type { AnyFeature, FeatureExportsOf } from './feature-authoring-types';\nimport { isFeature } from './feature-definition-support';\nimport { assertExactDataKeys, assertPlainRecord, dataEntries } from './feature-record';\n\ninterface HostConditionOptions<Id extends string> {\n readonly id: Id;\n}\n\ninterface FeatureConditionOptions<Id extends string, Source extends AnyFeature> {\n readonly from: Source;\n readonly id: Id;\n readonly select: (exports: FeatureExportsOf<Source>) => Readable<boolean>;\n}\n\ntype ConditionRecord = Readonly<{\n from: AnyFeature | undefined;\n id: DeclarationId;\n select: ((exports: unknown) => Readable<boolean>) | undefined;\n}>;\n\nconst conditionRecords = new WeakMap<object, ConditionRecord>();\n\nfunction requireConditionId(value: unknown): DeclarationId {\n if (typeof value !== 'string') throw new TypeError('Condition id must be a string.');\n\n return declarationId(value);\n}\n\nfunction snapshotFeatureCondition(options: Readonly<Record<string, unknown>>, id: DeclarationId): ConditionRecord {\n const from = options['from'];\n const select = options['select'];\n\n if (!isFeature(from)) throw new TypeError('Condition from must be an authentic Feature.');\n\n if (typeof select !== 'function') throw new TypeError('Condition select must be a function.');\n\n return Object.freeze({ from, id, select: select as (exports: unknown) => Readable<boolean> });\n}\n\ninterface DefineCondition {\n /** The computed form comes first: `select` needs its context before the host form's excess-property check. */\n <const Id extends string, const Source extends AnyFeature>(\n options: FeatureConditionOptions<Id, Source>,\n ): Condition<Id, Source>;\n <const Id extends string>(options: HostConditionOptions<Id>): Condition<Id, undefined>;\n}\n\nconst defineCondition = ((options: unknown) => {\n assertPlainRecord(options, 'Condition options');\n const entries = dataEntries(options, 'Condition options');\n const keys = new Set(entries.map(([key]) => key));\n\n if (keys.has('from') || keys.has('select')) {\n assertExactDataKeys(options, entries, ['from', 'id', 'select'], 'Condition options');\n } else {\n assertExactDataKeys(options, entries, ['id'], 'Condition options');\n }\n\n const id = requireConditionId(options['id']);\n const condition = Object.freeze({ id }) as Condition;\n const record = keys.has('from')\n ? snapshotFeatureCondition(options, id)\n : Object.freeze({ from: undefined, id, select: undefined });\n\n conditionRecords.set(condition, record);\n\n return condition;\n}) as DefineCondition;\n\nfunction isCondition(value: unknown): value is Condition {\n return typeof value === 'object' && value !== null && conditionRecords.has(value);\n}\n\nfunction requireConditionRecord(condition: Condition): ConditionRecord {\n const record = conditionRecords.get(condition);\n\n if (record === undefined) throw new TypeError('Condition is not authentic.');\n\n return record;\n}\n\n/** Conditions are a set: the same set in any order is one group, so the canonical key sorts the ids (D93). */\nfunction conditionSetKey(conditions: readonly Condition[]): string {\n return [...conditions.map(condition => condition.id as string)].sort().join('|');\n}\n\nfunction snapshotFeatureConditions(value: unknown): readonly Condition[] {\n if (value === undefined) return Object.freeze([]);\n\n if (!Array.isArray(value)) throw new TypeError('defineFeature when must be an array of Conditions.');\n\n const conditions = Array.from(value as readonly unknown[]);\n\n for (const condition of conditions) {\n if (!isCondition(condition)) throw new TypeError('defineFeature when must contain authentic Conditions.');\n }\n\n const ids = new Set(conditions.map(condition => (condition as Condition).id));\n\n if (ids.size !== conditions.length) throw new TypeError('defineFeature when must not repeat a Condition.');\n\n return Object.freeze(conditions as Condition[]);\n}\n\nexport { conditionSetKey, defineCondition, requireConditionRecord, snapshotFeatureConditions };\nexport type { Condition, HostConditionId } from './condition-types';\n"],"names":["conditionRecords","requireConditionId","value","declarationId","snapshotFeatureCondition","options","id","from","select","isFeature","defineCondition","assertPlainRecord","entries","dataEntries","keys","key","assertExactDataKeys","condition","record","isCondition","requireConditionRecord","conditionSetKey","conditions","snapshotFeatureConditions"],"mappings":"4MAwBA,MAAMA,EAAmB,IAAI,QAE7B,SAASC,EAAmBC,EAAc,CACxC,GAAI,OAAOA,GAAU,SAAU,MAAM,IAAI,UAAU,gCAAgC,EAEnF,OAAOC,EAAcD,CAAK,CAC5B,CAEA,SAASE,EAAyBC,EAA4CC,EAAiB,CAC7F,MAAMC,EAAOF,EAAQ,KACfG,EAASH,EAAQ,OAEvB,GAAI,CAACI,EAAUF,CAAI,EAAG,MAAM,IAAI,UAAU,8CAA8C,EAExF,GAAI,OAAOC,GAAW,WAAY,MAAM,IAAI,UAAU,sCAAsC,EAE5F,OAAO,OAAO,OAAO,CAAE,KAAAD,EAAM,GAAAD,EAAI,OAAQE,EAAmD,CAC9F,CAUA,MAAME,GAAoBL,GAAoB,CAC5CM,EAAkBN,EAAS,mBAAmB,EAC9C,MAAMO,EAAUC,EAAYR,EAAS,mBAAmB,EAClDS,EAAO,IAAI,IAAIF,EAAQ,IAAI,CAAC,CAACG,CAAG,IAAMA,CAAG,CAAC,EAE5CD,EAAK,IAAI,MAAM,GAAKA,EAAK,IAAI,QAAQ,EACvCE,EAAoBX,EAASO,EAAS,CAAC,OAAQ,KAAM,QAAQ,EAAG,mBAAmB,EAEnFI,EAAoBX,EAASO,EAAS,CAAC,IAAI,EAAG,mBAAmB,EAGnE,MAAMN,EAAKL,EAAmBI,EAAQ,EAAK,EACrCY,EAAY,OAAO,OAAO,CAAE,GAAAX,CAAE,CAAE,EAChCY,EAASJ,EAAK,IAAI,MAAM,EAC1BV,EAAyBC,EAASC,CAAE,EACpC,OAAO,OAAO,CAAE,KAAM,OAAW,GAAAA,EAAI,OAAQ,OAAW,EAE5D,OAAAN,EAAiB,IAAIiB,EAAWC,CAAM,EAE/BD,CACT,GAEA,SAASE,EAAYjB,EAAc,CACjC,OAAO,OAAOA,GAAU,UAAYA,IAAU,MAAQF,EAAiB,IAAIE,CAAK,CAClF,CAEA,SAASkB,EAAuBH,EAAoB,CAClD,MAAMC,EAASlB,EAAiB,IAAIiB,CAAS,EAE7C,GAAIC,IAAW,OAAW,MAAM,IAAI,UAAU,6BAA6B,EAE3E,OAAOA,CACT,CAGA,SAASG,EAAgBC,EAAgC,CACvD,MAAO,CAAC,GAAGA,EAAW,IAAIL,GAAaA,EAAU,EAAY,CAAC,EAAE,OAAO,KAAK,GAAG,CACjF,CAEA,SAASM,EAA0BrB,EAAc,CAC/C,GAAIA,IAAU,OAAW,OAAO,OAAO,OAAO,EAAE,EAEhD,GAAI,CAAC,MAAM,QAAQA,CAAK,EAAG,MAAM,IAAI,UAAU,oDAAoD,EAEnG,MAAMoB,EAAa,MAAM,KAAKpB,CAA2B,EAEzD,UAAWe,KAAaK,EACtB,GAAI,CAACH,EAAYF,CAAS,EAAG,MAAM,IAAI,UAAU,uDAAuD,EAK1G,GAFY,IAAI,IAAIK,EAAW,IAAIL,GAAcA,EAAwB,EAAE,CAAC,EAEpE,OAASK,EAAW,OAAQ,MAAM,IAAI,UAAU,iDAAiD,EAEzG,OAAO,OAAO,OAAOA,CAAyB,CAChD"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The write half of the devtools seam (`docs/devtools.md` §5.1, D176). Holding an inspection
3
+ * session does not mean holding the right to write, so this is its own factory over its own registry: the only way
4
+ * to reach it is the `ApplicationExecution` the host created.
5
+ *
6
+ * The only controllable subject is a condition and, through it, its group. A feature without `when` has no condition
7
+ * and is therefore not controllable — devtools never becomes a source of lifetime the plan does not have (§5.1).
8
+ */
9
+ import type { CompiledApplicationPlanIR } from './application-compiler.js';
10
+ import type { ConditionGroupExecution } from './condition-group-execution.js';
11
+ import type { ControllableCondition } from './condition-override.js';
12
+ import type { ApplicationActivity } from './inspection-activity.js';
13
+ import type { ObservationSlot } from './inspection-observer.js';
14
+ import type { DiagnosticNodeId } from './inspection-protocol.js';
15
+ import type { CleanupRetryError } from './instance-demand.js';
16
+ type ControlRejection = 'not-controllable' | 'stale' | 'unknown-target';
17
+ type ControlOutcome = Readonly<{
18
+ kind: 'applied';
19
+ sequence: number;
20
+ }> | Readonly<{
21
+ kind: 'rejected';
22
+ reason: ControlRejection;
23
+ }>;
24
+ interface RuntimeControlSession {
25
+ readonly close: () => void;
26
+ readonly resume: (conditionId: string) => Promise<ControlOutcome>;
27
+ readonly retryCleanup: (instanceId: string) => Promise<ControlOutcome>;
28
+ readonly suspend: (conditionId: string) => Promise<ControlOutcome>;
29
+ }
30
+ type ControllableInstance = Readonly<{
31
+ featureIndex: number;
32
+ node: DiagnosticNodeId;
33
+ }>;
34
+ type ControllableApplication = Readonly<{
35
+ activity: ApplicationActivity;
36
+ /** Whether the application has started closing: a control command after the fence is stale, not applied. */
37
+ closed: () => boolean;
38
+ /** Every condition of the plan, wrapped once when the application opened, addressed by the id the panel shows. */
39
+ conditions: ReadonlyMap<string, ControllableCondition>;
40
+ groups: readonly ConditionGroupExecution[];
41
+ /** Which groups read one condition: the command is awaited on exactly those and on nothing else. */
42
+ groupsOfCondition: ReadonlyMap<string, readonly number[]>;
43
+ /** Addressed by feature id and by the instance node id the panel shows, so the panel may send either. */
44
+ instances: ReadonlyMap<string, ControllableInstance>;
45
+ observation: ObservationSlot;
46
+ /** The last quarantined cleanup of an instance, recorded on the failure path only (D126). */
47
+ quarantines: Map<number, CleanupRetryError>;
48
+ }>;
49
+ /** Which groups read one condition: a suspend is awaited on exactly those groups and on nothing else (§5.3). */
50
+ declare function groupsOfCondition(plan: CompiledApplicationPlanIR): ReadonlyMap<string, readonly number[]>;
51
+ /** Addressed by feature id and by the instance node id, so the panel may send back either of the two it shows. */
52
+ declare function controllableInstances(plan: CompiledApplicationPlanIR): ReadonlyMap<string, ControllableInstance>;
53
+ declare function registerControllableApplication(execution: object, application: ControllableApplication): void;
54
+ declare function createControlSession(execution: object): RuntimeControlSession;
55
+ export { controllableInstances, createControlSession, groupsOfCondition, registerControllableApplication };
56
+ export type { ControlOutcome, RuntimeControlSession };
@@ -0,0 +1,2 @@
1
+ import{nodeIds as g,conditionAtomId as I}from"./inspection-plan.js";import{isCleanupRetryError as v}from"./instance-demand.js";const m=new WeakMap,l=new WeakMap;function j(e){const n=new Map;return e.groups.forEach((r,t)=>{for(const s of r.conditions){const u=n.get(s.id);u===void 0?n.set(s.id,[t]):u.push(t)}}),n}function h(e){const n=new Map;for(const r of e.features){const t=Object.freeze({featureIndex:r.featureIndex,node:g.instance(r.featureId)});n.set(r.featureId,t),n.set(t.node,t)}return n}function z(e,n){m.set(e,n)}function d(e){return Object.freeze({kind:"rejected",reason:e})}function y(e){var n;return Object.freeze({kind:"applied",sequence:((n=e.observation.observer)==null?void 0:n.currentSequence())??0})}async function C(e,n){for(const r of e.groupsOfCondition.get(n)??[]){const t=e.groups[r];t!==void 0&&await t.settled()}}function q(e,n){const r=e.instances.get(n);if(r===void 0)return Object.freeze({found:!1,outcome:d("unknown-target")});const t=e.quarantines.get(r.featureIndex);return t===void 0||!v(t)?Object.freeze({found:!1,outcome:d("not-controllable")}):Object.freeze({featureIndex:r.featureIndex,found:!0,node:r.node,quarantine:t})}async function x(e,n,r){var s,u;if(n||e.closed())return d("stale");const t=q(e,r);if(!t.found)return t.outcome;(s=e.observation.observer)==null||s.controlAttributed(t.node,void 0);try{await t.quarantine.retryCleanup()}catch{return d("not-controllable")}return e.quarantines.delete(t.featureIndex),e.activity.retire(t.featureIndex),(u=e.observation.observer)==null||u.instanceRetired(t.featureIndex),y(e)}function k(e){const n=m.get(e);if(n===void 0)throw new TypeError("Control requires an application opened by openApplication.");if(l.has(e))throw new TypeError("Application already has a control session.");let r=!1;const t=new Set,s=Object.freeze({}),u=o=>{for(const[i,a]of n.conditions)if(I(i)===o)return{condition:a,conditionId:i}},O=(o,i)=>(o.resume({owned:()=>!0,owner:s}),t.delete(i),d("stale")),b=async(o,i)=>{var w;if(r||n.closed())return d("stale");const a=u(o);if(a===void 0)return d("unknown-target");const{condition:f,conditionId:c}=a;return(w=n.observation.observer)==null||w.controlAttributed(g.condition(c),I(c)),i?(t.add(c),f.suspend({owned:()=>!r,owner:s})):(t.delete(c),f.resume({owned:()=>!r,owner:s})),r?O(f,c):(await C(n,c),y(n))},p=Object.freeze({close:()=>{var o;if(!r){r=!0,l.delete(e);for(const i of t)(o=n.conditions.get(i))==null||o.resume({owned:()=>!0,owner:s});t.clear()}},resume:o=>b(o,!1),retryCleanup:o=>x(n,r,o),suspend:o=>b(o,!0)});return l.set(e,p),p}export{h as controllableInstances,k as createControlSession,j as groupsOfCondition,z as registerControllableApplication};
2
+ //# sourceMappingURL=control-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-registry.js","sources":["../src/control-registry.ts"],"sourcesContent":["/**\n * The write half of the devtools seam (`docs/devtools.md` §5.1, D176). Holding an inspection\n * session does not mean holding the right to write, so this is its own factory over its own registry: the only way\n * to reach it is the `ApplicationExecution` the host created.\n *\n * The only controllable subject is a condition and, through it, its group. A feature without `when` has no condition\n * and is therefore not controllable — devtools never becomes a source of lifetime the plan does not have (§5.1).\n */\n\nimport type { CompiledApplicationPlanIR } from './application-compiler';\nimport type { ConditionGroupExecution } from './condition-group-execution';\nimport type { ControllableCondition } from './condition-override';\nimport type { ApplicationActivity } from './inspection-activity';\nimport type { ObservationSlot } from './inspection-observer';\nimport { conditionAtomId, nodeIds } from './inspection-plan';\nimport type { DiagnosticNodeId } from './inspection-protocol';\nimport { isCleanupRetryError } from './instance-demand';\nimport type { CleanupRetryError } from './instance-demand';\n\ntype ControlRejection = 'not-controllable' | 'stale' | 'unknown-target';\n\ntype ControlOutcome =\n | Readonly<{ kind: 'applied'; sequence: number }>\n | Readonly<{ kind: 'rejected'; reason: ControlRejection }>;\n\ninterface RuntimeControlSession {\n readonly close: () => void;\n readonly resume: (conditionId: string) => Promise<ControlOutcome>;\n readonly retryCleanup: (instanceId: string) => Promise<ControlOutcome>;\n readonly suspend: (conditionId: string) => Promise<ControlOutcome>;\n}\n\ntype ControllableInstance = Readonly<{ featureIndex: number; node: DiagnosticNodeId }>;\n\ntype ControllableApplication = Readonly<{\n activity: ApplicationActivity;\n /** Whether the application has started closing: a control command after the fence is stale, not applied. */\n closed: () => boolean;\n /** Every condition of the plan, wrapped once when the application opened, addressed by the id the panel shows. */\n conditions: ReadonlyMap<string, ControllableCondition>;\n groups: readonly ConditionGroupExecution[];\n /** Which groups read one condition: the command is awaited on exactly those and on nothing else. */\n groupsOfCondition: ReadonlyMap<string, readonly number[]>;\n /** Addressed by feature id and by the instance node id the panel shows, so the panel may send either. */\n instances: ReadonlyMap<string, ControllableInstance>;\n observation: ObservationSlot;\n /** The last quarantined cleanup of an instance, recorded on the failure path only (D126). */\n quarantines: Map<number, CleanupRetryError>;\n}>;\n\nconst controllable = new WeakMap<object, ControllableApplication>();\n/** One live control session per execution (D191): the slot is freed by `close()`, so a reload may open a new one. */\nconst liveSessions = new WeakMap<object, RuntimeControlSession>();\n\n/** Which groups read one condition: a suspend is awaited on exactly those groups and on nothing else (§5.3). */\nfunction groupsOfCondition(plan: CompiledApplicationPlanIR): ReadonlyMap<string, readonly number[]> {\n const groups = new Map<string, number[]>();\n\n plan.groups.forEach((group, index) => {\n for (const condition of group.conditions) {\n const existing = groups.get(condition.id);\n\n if (existing === undefined) groups.set(condition.id, [index]);\n else existing.push(index);\n }\n });\n\n return groups;\n}\n\n/** Addressed by feature id and by the instance node id, so the panel may send back either of the two it shows. */\nfunction controllableInstances(plan: CompiledApplicationPlanIR): ReadonlyMap<string, ControllableInstance> {\n const instances = new Map<string, ControllableInstance>();\n\n for (const feature of plan.features) {\n const entry = Object.freeze({ featureIndex: feature.featureIndex, node: nodeIds.instance(feature.featureId) });\n instances.set(feature.featureId, entry);\n instances.set(entry.node, entry);\n }\n\n return instances;\n}\n\nfunction registerControllableApplication(execution: object, application: ControllableApplication): void {\n controllable.set(execution, application);\n}\n\nfunction rejectedOutcome(reason: ControlRejection): ControlOutcome {\n return Object.freeze({ kind: 'rejected' as const, reason });\n}\n\n/** The effect is visible in the frame the observation port publishes for it; without a session there is no frame. */\nfunction appliedOutcome(application: ControllableApplication): ControlOutcome {\n return Object.freeze({\n kind: 'applied' as const,\n sequence: application.observation.observer?.currentSequence() ?? 0,\n });\n}\n\nasync function settleGroupsOf(application: ControllableApplication, conditionId: string): Promise<void> {\n for (const index of application.groupsOfCondition.get(conditionId) ?? []) {\n const group = application.groups[index];\n\n if (group !== undefined) await group.settled();\n }\n}\n\ntype QuarantineTarget =\n | Readonly<{ featureIndex: number; found: true; node: DiagnosticNodeId; quarantine: CleanupRetryError }>\n | Readonly<{ found: false; outcome: ControlOutcome }>;\n\n/** Only an exact quarantined frontier is retried: an instance that is not quarantined has nothing to clean up. */\nfunction findQuarantine(application: ControllableApplication, instanceId: string): QuarantineTarget {\n const instance = application.instances.get(instanceId);\n\n if (instance === undefined)\n return Object.freeze({ found: false as const, outcome: rejectedOutcome('unknown-target') });\n\n const quarantine = application.quarantines.get(instance.featureIndex);\n\n if (quarantine === undefined || !isCleanupRetryError(quarantine)) {\n return Object.freeze({ found: false as const, outcome: rejectedOutcome('not-controllable') });\n }\n\n return Object.freeze({ featureIndex: instance.featureIndex, found: true as const, node: instance.node, quarantine });\n}\n\nasync function retryQuarantine(\n application: ControllableApplication,\n closed: boolean,\n instanceId: string,\n): Promise<ControlOutcome> {\n if (closed || application.closed()) return rejectedOutcome('stale');\n\n const target = findQuarantine(application, instanceId);\n\n if (!target.found) return target.outcome;\n\n application.observation.observer?.controlAttributed(target.node, undefined);\n\n try {\n await target.quarantine.retryCleanup();\n } catch {\n // The frontier is still there, so the entry stays and the panel may ask again.\n return rejectedOutcome('not-controllable');\n }\n\n application.quarantines.delete(target.featureIndex);\n application.activity.retire(target.featureIndex);\n\n application.observation.observer?.instanceRetired(target.featureIndex);\n\n return appliedOutcome(application);\n}\n\nfunction createControlSession(execution: object): RuntimeControlSession {\n const application = controllable.get(execution);\n\n if (application === undefined) throw new TypeError('Control requires an application opened by openApplication.');\n\n if (liveSessions.has(execution)) throw new TypeError('Application already has a control session.');\n\n let closed = false;\n const held = new Set<string>();\n /** What this session signs its overrides with: a successor's override carries another token and is left alone. */\n const owner = Object.freeze({});\n /**\n * D200: the port publishes the diagnostic atom of a condition, so a command addresses that atom and the session\n * resolves it back to the declaration the application knows. An id the grammar already accepts encodes to itself,\n * so a host that names the declaration directly keeps working.\n */\n const addressed = (\n requested: string,\n ): Readonly<{ condition: ControllableCondition; conditionId: string }> | undefined => {\n for (const [conditionId, condition] of application.conditions) {\n if (conditionAtomId(conditionId) === requested) return { condition, conditionId };\n }\n\n return undefined;\n };\n /** The session closed from inside the read that would have set the override, so nothing of it is left behind. */\n const abandonOverride = (condition: ControllableCondition, conditionId: string): ControlOutcome => {\n condition.resume({ owned: () => true, owner });\n held.delete(conditionId);\n\n return rejectedOutcome('stale');\n };\n const move = async (requested: string, suspending: boolean): Promise<ControlOutcome> => {\n if (closed || application.closed()) return rejectedOutcome('stale');\n\n const target = addressed(requested);\n\n if (target === undefined) return rejectedOutcome('unknown-target');\n\n const { condition, conditionId } = target;\n // The frame that carries the transition is the audit record: it names the control command as its cause.\n application.observation.observer?.controlAttributed(nodeIds.condition(conditionId), conditionAtomId(conditionId));\n\n // D191: the session owns the override before the read that sets it, so a `close()` from inside that foreign\n // read finds the entry it has to undo, and the override of a session that is gone is never left behind.\n if (suspending) {\n held.add(conditionId);\n condition.suspend({ owned: () => !closed, owner });\n } else {\n held.delete(conditionId);\n condition.resume({ owned: () => !closed, owner });\n }\n\n if (closed) return abandonOverride(condition, conditionId);\n\n await settleGroupsOf(application, conditionId);\n\n return appliedOutcome(application);\n };\n\n const session: RuntimeControlSession = Object.freeze({\n close: (): void => {\n if (closed) return;\n\n closed = true;\n liveSessions.delete(execution);\n\n // §5.4: closing the panel must not leave the application in a debugged state.\n for (const conditionId of held) {\n application.conditions.get(conditionId)?.resume({ owned: () => true, owner });\n }\n\n held.clear();\n },\n resume: (conditionId: string): Promise<ControlOutcome> => move(conditionId, false),\n retryCleanup: (instanceId: string): Promise<ControlOutcome> => retryQuarantine(application, closed, instanceId),\n suspend: (conditionId: string): Promise<ControlOutcome> => move(conditionId, true),\n });\n liveSessions.set(execution, session);\n\n return session;\n}\n\nexport { controllableInstances, createControlSession, groupsOfCondition, registerControllableApplication };\nexport type { ControlOutcome, RuntimeControlSession };\n"],"names":["controllable","liveSessions","groupsOfCondition","plan","groups","group","index","condition","existing","controllableInstances","instances","feature","entry","nodeIds","registerControllableApplication","execution","application","rejectedOutcome","reason","appliedOutcome","_a","settleGroupsOf","conditionId","findQuarantine","instanceId","instance","quarantine","isCleanupRetryError","retryQuarantine","closed","target","_b","createControlSession","held","owner","addressed","requested","conditionAtomId","abandonOverride","move","suspending","session"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { Awaitable } from '@opetope/core/internal';
2
+ import type { FeatureDisposer } from './feature-attachment.js';
3
+ import type { FeatureTimers } from './feature-timers.js';
4
+ import type { ErrorReporter, ModuleDefinition, ModuleScopeRef } from './public-module.js';
5
+ type DynamicScopeOpenContext = {
6
+ readonly signal: AbortSignal;
7
+ readonly timers: FeatureTimers;
8
+ };
9
+ type DynamicScopeOpen<Value> = (value: Value, context: DynamicScopeOpenContext) => Awaitable<FeatureDisposer | void>;
10
+ interface DynamicScopeChild<Value> {
11
+ readonly definition: ModuleDefinition<string>;
12
+ readonly lifetime: ModuleScopeRef<Value>;
13
+ }
14
+ interface DynamicScopeChildInstance<Value> {
15
+ readonly beginClose: () => void;
16
+ readonly close: () => Promise<void>;
17
+ readonly dispose: () => Promise<void>;
18
+ readonly open: (value: Value) => Promise<boolean>;
19
+ }
20
+ declare function createDynamicScopeChild<Value>(id: string, open: DynamicScopeOpen<Value>): DynamicScopeChild<Value>;
21
+ declare function instantiateDynamicScopeChild<Value>(child: DynamicScopeChild<Value>, reporter: ErrorReporter | undefined): DynamicScopeChildInstance<Value>;
22
+ export { createDynamicScopeChild, instantiateDynamicScopeChild };
23
+ export type { DynamicScopeChild, DynamicScopeChildInstance };
@@ -0,0 +1,2 @@
1
+ import{createAttachmentRecipe as k}from"./feature-attachment.js";import{defineModule as R}from"./public-module-definition.js";import{instantiateModule as v}from"./public-module-instance.js";class b extends Error{scopeId;cause;name="DynamicScopeOpenError";constructor(e,r){super(`Dynamic scope ${e} failed to open.`),this.scopeId=e,this.cause=r}}class g extends Error{scopeId;failures;name="DynamicScopeCleanupError";constructor(e,r){super(`Dynamic scope ${e} cleanup is quarantined.`),this.scopeId=e,this.failures=r}}function O(n,e){return new g(n,Object.freeze([...e]))}function S(n,e){if(e.kind!=="closed")throw O(n,e.failures.flatMap(r=>r.errors))}function q(n,e){if(e.kind!=="closed")throw O(n,e.failures)}function M(n,e){const r=R({id:n,runtime:({scope:l})=>{const o=l();return{attachment:k(o,a=>a,{kind:"open-only",open:a=>e(a.source,{signal:a.signal,timers:a.timers})}),lifetime:o}}});return Object.freeze({definition:r,lifetime:r.runtime.lifetime})}function j(n,e){const r=v(n.definition,{cleanupFailure:"quarantine",...e===void 0?{}:{reporter:e}}),l=r.scope(n.lifetime);let o=!1,p=!1,a=0,s,c,d,w=!1,u,m=!1;const y=()=>{const t=u;if(t===void 0)return Promise.resolve();const f=m?t.retry():t.drain();m=!0;const i=f.then(E=>{S(n.definition.id,E),u=void 0,m=!1});return s=i,i.then(()=>{s===i&&(s=void 0)},()=>{s===i&&(s=void 0)}),i},D=()=>c!==void 0||d!==void 0,C=t=>!D()&&!p&&t===a,h=()=>{p=!0,!(!o||u!==void 0)&&(o=!1,u=l.retire(),y().catch(()=>{}))};return Object.freeze({beginClose:h,close(){return h(),s??y()},dispose(){if(c!==void 0)return c;h();const t=(async()=>{await(s??y()),d??(d=r.retire());const f=await(w?d.retry():d.drain());w=!0,q(n.definition.id,f),d=void 0})();return c=t,t.catch(()=>{c===t&&(c=void 0)}),t},async open(t){if(D())throw new TypeError("Dynamic scope child is retired.");const f=a+=1;if(p=!1,await(s??y()),!C(f))return!1;o=!0;const i=await l.open(t);if(i.kind==="ready")return o;if(o=!1,i.drain.kind==="quarantined"&&(u=i.retirement,m=!0),S(n.definition.id,i.drain),i.kind==="retired")return!1;throw new b(n.definition.id,i.cause)}})}export{M as createDynamicScopeChild,j as instantiateDynamicScopeChild};
2
+ //# sourceMappingURL=dynamic-scope-child.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-scope-child.js","sources":["../src/dynamic-scope-child.ts"],"sourcesContent":["import type { DeclarationId } from '@opetope/core';\nimport type { Awaitable } from '@opetope/core/internal';\n\nimport { createAttachmentRecipe } from './feature-attachment';\nimport type { FeatureDisposer } from './feature-attachment';\nimport type { FeatureTimers } from './feature-timers';\nimport { defineModule, instantiateModule } from './public-module';\nimport type {\n ErrorReporter,\n ModuleDefinition,\n ModuleDrainOutcome,\n ModuleScopeDrainOutcome,\n ModuleScopeRef,\n ModuleScopeRetirement,\n} from './public-module';\n\ntype DynamicScopeOpenContext = {\n readonly signal: AbortSignal;\n readonly timers: FeatureTimers;\n};\n\ntype DynamicScopeOpen<Value> = (value: Value, context: DynamicScopeOpenContext) => Awaitable<FeatureDisposer | void>;\n\ninterface DynamicScopeChild<Value> {\n readonly definition: ModuleDefinition<string>;\n readonly lifetime: ModuleScopeRef<Value>;\n}\n\ninterface DynamicScopeChildInstance<Value> {\n readonly beginClose: () => void;\n readonly close: () => Promise<void>;\n readonly dispose: () => Promise<void>;\n readonly open: (value: Value) => Promise<boolean>;\n}\n\nclass DynamicScopeOpenError extends Error {\n override readonly name = 'DynamicScopeOpenError';\n\n constructor(\n readonly scopeId: DeclarationId,\n readonly cause: unknown,\n ) {\n super(`Dynamic scope ${scopeId} failed to open.`);\n }\n}\n\nclass DynamicScopeCleanupError extends Error {\n override readonly name = 'DynamicScopeCleanupError';\n\n constructor(\n readonly scopeId: DeclarationId,\n readonly failures: readonly unknown[],\n ) {\n super(`Dynamic scope ${scopeId} cleanup is quarantined.`);\n }\n}\n\nfunction cleanupFailure(scopeId: DeclarationId, failures: readonly unknown[]): DynamicScopeCleanupError {\n return new DynamicScopeCleanupError(scopeId, Object.freeze([...failures]));\n}\n\nfunction assertClosedScopeDrain(scopeId: DeclarationId, outcome: ModuleScopeDrainOutcome): void {\n if (outcome.kind === 'closed') return;\n\n throw cleanupFailure(\n scopeId,\n outcome.failures.flatMap(failure => failure.errors),\n );\n}\n\nfunction assertClosedModuleDrain(scopeId: DeclarationId, outcome: ModuleDrainOutcome): void {\n if (outcome.kind === 'closed') return;\n\n throw cleanupFailure(scopeId, outcome.failures);\n}\n\nfunction createDynamicScopeChild<Value>(id: string, open: DynamicScopeOpen<Value>): DynamicScopeChild<Value> {\n const definition = defineModule({\n id,\n runtime: ({ scope }) => {\n const lifetime = scope<Value>();\n const attachment = createAttachmentRecipe(lifetime, value => value, {\n kind: 'open-only',\n open: context => open(context.source, { signal: context.signal, timers: context.timers }),\n });\n\n return { attachment, lifetime };\n },\n });\n\n return Object.freeze({\n definition,\n lifetime: definition.runtime.lifetime,\n });\n}\n\nfunction instantiateDynamicScopeChild<Value>(\n child: DynamicScopeChild<Value>,\n reporter: ErrorReporter | undefined,\n): DynamicScopeChildInstance<Value> {\n const instance = instantiateModule(child.definition, {\n cleanupFailure: 'quarantine',\n ...(reporter === undefined ? {} : { reporter }),\n });\n const lifetime = instance.scope(child.lifetime);\n let active = false;\n /** An open that is still awaiting has no `active` flag yet, so the intent to close it is recorded here (D194). */\n let closeRequested = false;\n let openGeneration = 0;\n let closeOperation: Promise<void> | undefined;\n let disposeOperation: Promise<void> | undefined;\n let moduleRetirement: ReturnType<typeof instance.retire> | undefined;\n let moduleRetirementAttempted = false;\n let scopeRetirement: ModuleScopeRetirement | undefined;\n let scopeRetirementAttempted = false;\n\n const startScopeRetirement = (): Promise<void> => {\n const retirement = scopeRetirement;\n\n if (retirement === undefined) return Promise.resolve();\n\n const source = scopeRetirementAttempted ? retirement.retry() : retirement.drain();\n scopeRetirementAttempted = true;\n const operation = source.then(outcome => {\n assertClosedScopeDrain(child.definition.id, outcome);\n scopeRetirement = undefined;\n scopeRetirementAttempted = false;\n });\n closeOperation = operation;\n void operation.then(\n () => {\n if (closeOperation === operation) closeOperation = undefined;\n },\n () => {\n if (closeOperation === operation) closeOperation = undefined;\n },\n );\n\n return operation;\n };\n\n /** Whether this child is on its workspace path: either disposing, or already retiring its module. */\n const retired = (): boolean => disposeOperation !== undefined || moduleRetirement !== undefined;\n /** Whether the open that reserved this generation may still start: nothing fenced it and nothing replaced it. */\n const admitsOpen = (generation: number): boolean => !retired() && !closeRequested && generation === openGeneration;\n\n const beginClose = (): void => {\n closeRequested = true;\n\n if (!active || scopeRetirement !== undefined) return;\n\n active = false;\n scopeRetirement = lifetime.retire();\n void startScopeRetirement().catch(() => undefined);\n };\n\n return Object.freeze({\n beginClose,\n close(): Promise<void> {\n beginClose();\n\n return closeOperation ?? startScopeRetirement();\n },\n dispose(): Promise<void> {\n if (disposeOperation !== undefined) return disposeOperation;\n\n beginClose();\n const operation = (async () => {\n await (closeOperation ?? startScopeRetirement());\n moduleRetirement ??= instance.retire();\n const outcome = await (moduleRetirementAttempted ? moduleRetirement.retry() : moduleRetirement.drain());\n moduleRetirementAttempted = true;\n assertClosedModuleDrain(child.definition.id, outcome);\n moduleRetirement = undefined;\n })();\n disposeOperation = operation;\n void operation.catch(() => {\n if (disposeOperation === operation) disposeOperation = undefined;\n });\n\n return operation;\n },\n async open(value: Value): Promise<boolean> {\n if (retired()) throw new TypeError('Dynamic scope child is retired.');\n\n // The admission of this open is reserved before it awaits anything, so a fence or a newer selection that\n // arrives while it waits supersedes it: it opens nothing and the caller reads a selection that never\n // became ready (D194).\n const generation = (openGeneration += 1);\n closeRequested = false;\n await (closeOperation ?? startScopeRetirement());\n\n if (!admitsOpen(generation)) return false;\n\n active = true;\n const outcome = await lifetime.open(value);\n\n if (outcome.kind === 'ready') return active;\n\n active = false;\n\n if (outcome.drain.kind === 'quarantined') {\n scopeRetirement = outcome.retirement;\n scopeRetirementAttempted = true;\n }\n\n assertClosedScopeDrain(child.definition.id, outcome.drain);\n\n if (outcome.kind === 'retired') return false;\n\n throw new DynamicScopeOpenError(child.definition.id, outcome.cause);\n },\n });\n}\n\nexport { createDynamicScopeChild, instantiateDynamicScopeChild };\nexport type { DynamicScopeChild, DynamicScopeChildInstance };\n"],"names":["DynamicScopeOpenError","scopeId","cause","DynamicScopeCleanupError","failures","cleanupFailure","assertClosedScopeDrain","outcome","failure","assertClosedModuleDrain","createDynamicScopeChild","id","open","definition","defineModule","scope","lifetime","createAttachmentRecipe","value","context","instantiateDynamicScopeChild","child","reporter","instance","instantiateModule","active","closeRequested","openGeneration","closeOperation","disposeOperation","moduleRetirement","moduleRetirementAttempted","scopeRetirement","scopeRetirementAttempted","startScopeRetirement","retirement","source","operation","retired","admitsOpen","generation","beginClose"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { DynamicScopeChild } from './dynamic-scope-child.js';
2
+ import type { ErrorReporter } from './public-module.js';
3
+ declare const closedSelection: unique symbol;
4
+ type DynamicScopeSelection<Value> = typeof closedSelection | {
5
+ readonly key: unknown;
6
+ readonly value: Value;
7
+ };
8
+ interface DynamicScopeController<Value> {
9
+ readonly dispose: () => Promise<void>;
10
+ readonly fence: () => void;
11
+ readonly update: (selection: DynamicScopeSelection<Value>) => Promise<void>;
12
+ }
13
+ declare function createDynamicScopeController<Value>(child: DynamicScopeChild<Value>, reporter: ErrorReporter | undefined): DynamicScopeController<Value>;
14
+ declare function dynamicScopeSelection<Value>(value: Value, key: unknown): DynamicScopeSelection<Value>;
15
+ export { closedSelection, createDynamicScopeController, dynamicScopeSelection };
16
+ export type { DynamicScopeController };
@@ -0,0 +1,2 @@
1
+ import{instantiateDynamicScopeChild as T}from"./dynamic-scope-child.js";const o=Symbol("opetope.dynamic-scope.closed");function C(c,l){return c===o||l===o?c===l:Object.is(c.key,l.key)}function B(c,l){let t=o,u=!1,n={revision:0,selection:o},m=!1,v,d,s,p,f=0;const w=[],a=(e,i)=>{f=Math.max(f,e);for(let r=w.length-1;r>=0;r-=1){const y=w[r];y.revision>f||(w.splice(r,1),i===void 0?y.resolve():y.reject(i))}},h=()=>s??(s=T(c,l)),b=()=>{t!==o&&h().beginClose()},D=async e=>{if(t===o)return"select";if(!u&&C(t,e.selection))return a(n.revision),"stop";const i=h();i.beginClose();try{await i.close()}catch(r){return u=!0,p=r,a(n.revision,r),"stop"}return t=o,u=!1,p=void 0,e.revision===n.revision?"select":"reselect"},O=(e,i,r)=>{if(!r){t=o,e.revision===n.revision&&a(e.revision);return}(e.revision===n.revision||C(i,n.selection))&&a(n.revision)},P=async e=>{if(e.selection===o){a(e.revision);return}const i=e.selection;t=i;try{const r=await h().open(i.value);O(e,i,r)}catch(r){t=o,e.revision===n.revision&&a(e.revision,r)}},k=async()=>{for(;f<n.revision;){const e=n,i=await D(e);i!=="reselect"&&e.revision===n.revision&&i==="select"&&await P(e)}},S=()=>{d===void 0&&(d=k().finally(()=>{d=void 0,f<n.revision&&S()}),d.catch(()=>{}))},x=async()=>{for(;d!==void 0;)await d},z=e=>{if(m)return Promise.reject(new TypeError("Dynamic scope controller is retired."));const i=n.revision+1;n={revision:i,selection:e},C(t,e)||b();const r=i<=f?Promise.resolve():new Promise((y,E)=>{w.push({reject:E,resolve:y,revision:i})});return S(),r},j=()=>{m||(n={revision:n.revision+1,selection:o},m=!0,b(),S())};return Object.freeze({dispose(){if(v!==void 0)return v;j();const e=(async()=>{if(await x(),a(n.revision),p!==void 0){const i=p;return p=void 0,Promise.reject(i)}u&&(await(s==null?void 0:s.close()),u=!1,t=o),await(s==null?void 0:s.dispose()),s=void 0})();return v=e,e.catch(()=>{v===e&&(v=void 0)}),e},fence:j,update:z})}function I(c,l){return Object.freeze({key:l,value:c})}export{o as closedSelection,B as createDynamicScopeController,I as dynamicScopeSelection};
2
+ //# sourceMappingURL=dynamic-scope-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-scope-controller.js","sources":["../src/dynamic-scope-controller.ts"],"sourcesContent":["import type { DynamicScopeChild, DynamicScopeChildInstance } from './dynamic-scope-child';\nimport { instantiateDynamicScopeChild } from './dynamic-scope-child';\nimport type { ErrorReporter } from './public-module';\n\nconst closedSelection = Symbol('opetope.dynamic-scope.closed');\n\ntype DynamicScopeSelection<Value> =\n | typeof closedSelection\n | {\n readonly key: unknown;\n readonly value: Value;\n };\n\ntype DynamicScopeWaiter = {\n readonly reject: (error: unknown) => void;\n readonly resolve: () => void;\n readonly revision: number;\n};\n\ninterface DynamicScopeController<Value> {\n readonly dispose: () => Promise<void>;\n readonly fence: () => void;\n readonly update: (selection: DynamicScopeSelection<Value>) => Promise<void>;\n}\n\ntype DesiredSelection<Value> = {\n readonly revision: number;\n readonly selection: DynamicScopeSelection<Value>;\n};\n\ntype CurrentReconciliation = 'reselect' | 'select' | 'stop';\n\nfunction sameSelectionKey<Value>(left: DynamicScopeSelection<Value>, right: DynamicScopeSelection<Value>): boolean {\n if (left === closedSelection || right === closedSelection) return left === right;\n\n return Object.is(left.key, right.key);\n}\n\nfunction createDynamicScopeController<Value>(\n child: DynamicScopeChild<Value>,\n reporter: ErrorReporter | undefined,\n): DynamicScopeController<Value> {\n let current: DynamicScopeSelection<Value> = closedSelection;\n let closeBlocked = false;\n let desired: DesiredSelection<Value> = { revision: 0, selection: closedSelection };\n let disposed = false;\n let disposeOperation: Promise<void> | undefined;\n let driver: Promise<void> | undefined;\n let instance: DynamicScopeChildInstance<Value> | undefined;\n let pendingCleanupError: unknown;\n let settledRevision = 0;\n const waiters: DynamicScopeWaiter[] = [];\n\n const settleWaiters = (revision: number, error?: unknown): void => {\n settledRevision = Math.max(settledRevision, revision);\n\n for (let index = waiters.length - 1; index >= 0; index -= 1) {\n const waiter = waiters[index]!;\n\n if (waiter.revision > settledRevision) continue;\n\n waiters.splice(index, 1);\n\n if (error === undefined) waiter.resolve();\n else waiter.reject(error);\n }\n };\n const getInstance = (): DynamicScopeChildInstance<Value> =>\n (instance ??= instantiateDynamicScopeChild(child, reporter));\n const fenceCurrent = (): void => {\n if (current === closedSelection) return;\n\n getInstance().beginClose();\n };\n const reconcileCurrent = async (target: DesiredSelection<Value>): Promise<CurrentReconciliation> => {\n if (current === closedSelection) return 'select';\n\n if (!closeBlocked && sameSelectionKey(current, target.selection)) {\n settleWaiters(desired.revision);\n\n return 'stop';\n }\n\n const active = getInstance();\n active.beginClose();\n\n try {\n await active.close();\n } catch (error) {\n closeBlocked = true;\n pendingCleanupError = error;\n settleWaiters(desired.revision, error);\n\n return 'stop';\n }\n\n current = closedSelection;\n closeBlocked = false;\n pendingCleanupError = undefined;\n\n return target.revision === desired.revision ? 'select' : 'reselect';\n };\n const settleOpenSuccess = (\n target: DesiredSelection<Value>,\n opening: Exclude<DynamicScopeSelection<Value>, typeof closedSelection>,\n ready: boolean,\n ): void => {\n if (!ready) {\n current = closedSelection;\n\n if (target.revision === desired.revision) settleWaiters(target.revision);\n\n return;\n }\n\n if (target.revision === desired.revision || sameSelectionKey(opening, desired.selection)) {\n settleWaiters(desired.revision);\n }\n };\n const openTarget = async (target: DesiredSelection<Value>): Promise<void> => {\n if (target.selection === closedSelection) {\n settleWaiters(target.revision);\n\n return;\n }\n\n const opening = target.selection;\n current = opening;\n\n try {\n const ready = await getInstance().open(opening.value);\n settleOpenSuccess(target, opening, ready);\n } catch (error) {\n current = closedSelection;\n\n if (target.revision === desired.revision) settleWaiters(target.revision, error);\n }\n };\n const drive = async (): Promise<void> => {\n while (settledRevision < desired.revision) {\n const target = desired;\n const reconciliation = await reconcileCurrent(target);\n\n if (reconciliation === 'reselect') continue;\n\n // D194: reconciling waits for foreign cleanup, and the owner may fence the scope while that runs. What opens\n // is the selection that is still desired; a superseded one goes back to the loop instead of opening.\n if (target.revision !== desired.revision) continue;\n\n if (reconciliation === 'select') await openTarget(target);\n }\n };\n const startDriver = (): void => {\n if (driver !== undefined) return;\n\n driver = drive().finally(() => {\n driver = undefined;\n\n if (settledRevision < desired.revision) startDriver();\n });\n void driver.catch(() => undefined);\n };\n const awaitDriverDrain = async (): Promise<void> => {\n while (driver !== undefined) await driver;\n };\n const schedule = (selection: DynamicScopeSelection<Value>): Promise<void> => {\n if (disposed) return Promise.reject(new TypeError('Dynamic scope controller is retired.'));\n\n const revision = desired.revision + 1;\n desired = { revision, selection };\n\n if (!sameSelectionKey(current, selection)) fenceCurrent();\n\n const waiting =\n revision <= settledRevision\n ? Promise.resolve()\n : new Promise<void>((resolve, reject) => {\n waiters.push({ reject, resolve, revision });\n });\n startDriver();\n\n return waiting;\n };\n const fence = (): void => {\n if (disposed) return;\n\n desired = { revision: desired.revision + 1, selection: closedSelection };\n disposed = true;\n fenceCurrent();\n startDriver();\n };\n\n return Object.freeze({\n dispose(): Promise<void> {\n if (disposeOperation !== undefined) return disposeOperation;\n\n fence();\n const operation = (async () => {\n await awaitDriverDrain();\n settleWaiters(desired.revision);\n\n if (pendingCleanupError !== undefined) {\n const error = pendingCleanupError;\n pendingCleanupError = undefined;\n\n return Promise.reject(error);\n }\n\n if (closeBlocked) {\n await instance?.close();\n closeBlocked = false;\n current = closedSelection;\n }\n\n await instance?.dispose();\n instance = undefined;\n })();\n disposeOperation = operation;\n void operation.catch(() => {\n if (disposeOperation === operation) disposeOperation = undefined;\n });\n\n return operation;\n },\n fence,\n update: schedule,\n });\n}\n\nfunction dynamicScopeSelection<Value>(value: Value, key: unknown): DynamicScopeSelection<Value> {\n return Object.freeze({ key, value });\n}\n\nexport { closedSelection, createDynamicScopeController, dynamicScopeSelection };\nexport type { DynamicScopeController };\n"],"names":["closedSelection","sameSelectionKey","left","right","createDynamicScopeController","child","reporter","current","closeBlocked","desired","disposed","disposeOperation","driver","instance","pendingCleanupError","settledRevision","waiters","settleWaiters","revision","error","index","waiter","getInstance","instantiateDynamicScopeChild","fenceCurrent","reconcileCurrent","target","active","settleOpenSuccess","opening","ready","openTarget","drive","reconciliation","startDriver","awaitDriverDrain","schedule","selection","waiting","resolve","reject","fence","operation","dynamicScopeSelection","value","key"],"mappings":"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"}