@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":"application-compiler.js","sources":["../src/application-compiler.ts"],"sourcesContent":["import type { DeclarationId } from '@opetope/core';\n\nimport { compileApplicationEdges } from './application-compiler-edges';\nimport { compileCanonicalOrder } from './application-compiler-graph';\nimport type { ApplicationHardImportIR } from './application-compiler-graph';\nimport { compileApplicationPorts } from './application-port-compiler';\nimport type { ApplicationPortProviderIR, CompiledApplicationRequirementIR } from './application-port-compiler';\nimport type { Condition } from './condition';\nimport { conditionSetKey, requireConditionRecord } from './condition';\nimport type { AnyFeature } from './feature-authoring';\nimport { getFeatureContributionDescriptors, isFeature } from './feature-authoring';\nimport {\n getDirectHostContract,\n getHostContractDemand,\n isFeatureContract,\n isHostContractOptional,\n} from './feature-contract';\nimport type { ImportDemand } from './feature-contract';\n\ntype HostContractIdentity = ReturnType<typeof getDirectHostContract>;\n\ntype ApplicationLifetimeOwnerIR =\n | { readonly groupId: string; readonly kind: 'group' }\n | { readonly kind: 'application' };\n\ntype ApplicationImportProviderIR =\n | {\n readonly contract: HostContractIdentity;\n readonly contractId: DeclarationId;\n readonly kind: 'absent';\n }\n | {\n readonly contract: HostContractIdentity;\n readonly contractId: DeclarationId;\n readonly kind: 'application-input';\n }\n | {\n readonly definition: AnyFeature;\n readonly featureId: DeclarationId;\n readonly featureIndex: number;\n readonly kind: 'feature';\n };\n\ninterface CompiledApplicationImportIR {\n readonly consumerFeatureId: DeclarationId;\n readonly consumerFeatureIndex: number;\n readonly contract: HostContractIdentity;\n readonly contractId: DeclarationId;\n readonly demand: ImportDemand;\n readonly importKey: string;\n /** `optional(x)` in `imports`: the provider may be absent, and the consumer reads a `Lookup` projection (D105). */\n readonly optional: boolean;\n readonly provider: ApplicationImportProviderIR;\n}\n\ninterface CompiledApplicationFeatureIR {\n readonly definition: AnyFeature;\n readonly exportContractId: DeclarationId;\n readonly featureId: DeclarationId;\n readonly featureIndex: number;\n readonly imports: readonly CompiledApplicationImportIR[];\n readonly owner: ApplicationLifetimeOwnerIR;\n readonly requirements: readonly CompiledApplicationRequirementIR[];\n}\n\ninterface CompiledApplicationContributionIR {\n readonly contributionId: DeclarationId;\n readonly contributorFeatureId: DeclarationId;\n readonly contributorFeatureIndex: number;\n readonly priority: number;\n readonly target: ReturnType<typeof getFeatureContributionDescriptors>[number]['target'];\n readonly targetId: DeclarationId;\n}\n\n/** Features waiting for the same set of conditions form one group: the inferred domain of D93. */\ninterface CompiledConditionGroupIR {\n readonly activationOrder: readonly number[];\n readonly conditions: readonly Condition[];\n readonly groupId: string;\n readonly members: readonly number[];\n}\n\ntype ApplicationTopologyEdgeIR =\n | {\n readonly condition: Condition;\n readonly conditionId: DeclarationId;\n readonly groupId: string;\n readonly kind: 'condition-control';\n }\n | {\n readonly consumerFeatureId: DeclarationId;\n readonly contractId: DeclarationId;\n readonly importKey: string;\n readonly kind: 'absent-import' | 'hard-import' | 'on-demand-import' | 'optional-import';\n readonly provider: ApplicationImportProviderIR;\n }\n | {\n readonly consumerFeatureId: DeclarationId;\n readonly kind: 'absent-port';\n readonly portId: DeclarationId;\n readonly requirementKey: string;\n }\n | {\n readonly consumerFeatureId: DeclarationId;\n readonly kind: 'requires-port';\n readonly portId: DeclarationId;\n readonly providerFeatureId: DeclarationId;\n readonly requirementKey: string;\n }\n | {\n readonly contributionId: DeclarationId;\n readonly contributorFeatureId: DeclarationId;\n readonly kind: 'contributes-to';\n readonly targetId: DeclarationId;\n }\n | {\n readonly featureId: DeclarationId;\n readonly kind: 'owns';\n readonly owner: ApplicationLifetimeOwnerIR;\n }\n | {\n readonly featureId: DeclarationId;\n readonly kind: 'provides-port';\n readonly portId: DeclarationId;\n readonly providerKey: string;\n };\n\ninterface CompiledApplicationPlanIR {\n readonly applicationOrder: readonly number[];\n readonly contributions: readonly CompiledApplicationContributionIR[];\n readonly edges: readonly ApplicationTopologyEdgeIR[];\n readonly features: readonly CompiledApplicationFeatureIR[];\n readonly groups: readonly CompiledConditionGroupIR[];\n readonly imports: readonly CompiledApplicationImportIR[];\n readonly providers: readonly ApplicationPortProviderIR[];\n readonly requirements: readonly CompiledApplicationRequirementIR[];\n}\n\ninterface ApplicationCompileInput {\n readonly features: readonly AnyFeature[];\n}\n\nconst applicationOwner: ApplicationLifetimeOwnerIR = Object.freeze({ kind: 'application' });\n\nfunction compareIds(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction canonicalFeatures(features: readonly AnyFeature[]): readonly AnyFeature[] {\n const seen = new Set<object>();\n const ids = new Set<DeclarationId>();\n\n for (const feature of features) {\n if (!isFeature(feature)) {\n throw new TypeError('Application features contain a non-authentic Feature.');\n }\n\n if (seen.has(feature)) throw new TypeError(`Application feature ${feature.id} is declared more than once.`);\n\n if (ids.has(feature.id)) throw new TypeError(`Application feature id ${feature.id} has more than one definition.`);\n\n seen.add(feature);\n ids.add(feature.id);\n }\n\n return Object.freeze([...features].sort((left, right) => compareIds(left.id, right.id)));\n}\n\n/** Owner is the condition set: no conditions means the feature lives as long as the application (D93). */\nfunction assignLifetimeOwners(features: readonly AnyFeature[]): ReadonlyMap<AnyFeature, ApplicationLifetimeOwnerIR> {\n const owners = new Map<AnyFeature, ApplicationLifetimeOwnerIR>();\n\n for (const feature of features) {\n owners.set(\n feature,\n feature.when.length === 0\n ? applicationOwner\n : Object.freeze({ groupId: conditionSetKey(feature.when), kind: 'group' as const }),\n );\n }\n\n return owners;\n}\n\nfunction featureProviderIndexes(features: readonly AnyFeature[]): {\n readonly byContract: ReadonlyMap<object, number>;\n readonly byContractId: ReadonlyMap<DeclarationId, number>;\n} {\n const byContract = new Map<object, number>();\n const byContractId = new Map<DeclarationId, number>();\n\n for (const [featureIndex, feature] of features.entries()) {\n const contractId = feature.exports.id;\n const duplicate = byContractId.get(contractId);\n\n if (duplicate !== undefined) {\n throw new TypeError(\n `Feature export contract ${contractId} has duplicate providers ${features[duplicate]?.id ?? '<unknown>'} and ${feature.id}.`,\n );\n }\n\n byContract.set(feature.exports, featureIndex);\n byContractId.set(contractId, featureIndex);\n }\n\n return { byContract, byContractId };\n}\n\n/** A hard edge is legal only when the provider outlives the consumer, that is when its condition set is a subset. */\nfunction conditionsInclude(outer: readonly Condition[], inner: readonly Condition[]): boolean {\n const ids = new Set(outer.map(condition => condition.id as string));\n\n return inner.every(condition => ids.has(condition.id as string));\n}\n\nfunction assertCompatibleLifetime(\n provider: AnyFeature,\n consumer: AnyFeature,\n importKey: string,\n dependencyKind: 'import' | 'requirement' = 'import',\n): void {\n if (conditionsInclude(consumer.when, provider.when)) return;\n\n throw new TypeError(\n `Feature ${dependencyKind} ${consumer.id}.${importKey} cannot depend on shorter-lived provider ${provider.id}; use optional for a weak edge.`,\n );\n}\n\nfunction resolveFeatureProvider(\n features: readonly AnyFeature[],\n providers: ReturnType<typeof featureProviderIndexes>,\n consumer: AnyFeature,\n importKey: string,\n contract: HostContractIdentity,\n demand: ImportDemand,\n isOptional: boolean,\n): ApplicationImportProviderIR {\n // `onDemand` wraps a host contract, never a feature definition, so this shape cannot be authored (D105).\n if (demand === 'onDemand') {\n throw new TypeError(`Feature import ${consumer.id}.${importKey} cannot defer a feature provider with onDemand.`);\n }\n\n const exactProviderIndex = providers.byContract.get(contract);\n const sameIdProviderIndex = providers.byContractId.get(contract.id);\n\n if (exactProviderIndex === undefined) {\n if (sameIdProviderIndex !== undefined) {\n throw new TypeError(\n `Feature import ${consumer.id}.${importKey} uses a different contract identity for ${contract.id}.`,\n );\n }\n\n if (isOptional) return Object.freeze({ contract, contractId: contract.id, kind: 'absent' as const });\n\n throw new TypeError(\n `Feature import ${consumer.id}.${importKey} has no provider for ${contract.id}: add the providing feature to ` +\n `defineApplication features, or make the import optional(...).`,\n );\n }\n\n const provider = features[exactProviderIndex];\n\n if (provider === undefined) throw new TypeError(`Feature provider ${contract.id} is unavailable.`);\n\n // A weak edge is free of the inclusion law: the consumer reads a projection when the provider is closed (D105).\n if (!isOptional) assertCompatibleLifetime(provider, consumer, importKey);\n\n return Object.freeze({\n definition: provider,\n featureId: provider.id,\n featureIndex: exactProviderIndex,\n kind: 'feature',\n });\n}\n\nfunction resolveApplicationInputProvider(\n contract: HostContractIdentity,\n providers: ReturnType<typeof featureProviderIndexes>,\n inputs: Map<DeclarationId, Extract<ApplicationImportProviderIR, { kind: 'application-input' }>>,\n): ApplicationImportProviderIR {\n if (providers.byContractId.has(contract.id)) {\n throw new TypeError(`Application input ${contract.id} conflicts with a feature export contract.`);\n }\n\n const existing = inputs.get(contract.id);\n\n if (existing?.contract !== contract) {\n if (existing !== undefined) {\n throw new TypeError(`Application input contract id ${contract.id} has more than one definition.`);\n }\n\n const provider = Object.freeze({ contract, contractId: contract.id, kind: 'application-input' as const });\n\n inputs.set(contract.id, provider);\n\n return provider;\n }\n\n return existing;\n}\n\nfunction compileImports(features: readonly AnyFeature[]): readonly CompiledApplicationImportIR[] {\n const providers = featureProviderIndexes(features);\n const inputs = new Map<DeclarationId, Extract<ApplicationImportProviderIR, { kind: 'application-input' }>>();\n const imports: CompiledApplicationImportIR[] = [];\n\n for (const [consumerFeatureIndex, consumer] of features.entries()) {\n const declarations = Object.entries(consumer.imports).sort(([left], [right]) => compareIds(left, right));\n\n for (const [importKey, declaration] of declarations) {\n const contract = getDirectHostContract(declaration as HostContractIdentity);\n const contractId = contract.id;\n const demand = getHostContractDemand(declaration as HostContractIdentity);\n const isOptional = isHostContractOptional(declaration);\n const provider = isFeatureContract(declaration)\n ? resolveFeatureProvider(features, providers, consumer, importKey, contract, demand, isOptional)\n : resolveApplicationInputProvider(contract, providers, inputs);\n\n imports.push(\n Object.freeze({\n consumerFeatureId: consumer.id,\n consumerFeatureIndex,\n contract,\n contractId,\n demand,\n importKey,\n optional: isOptional,\n provider,\n }),\n );\n }\n }\n\n return Object.freeze(imports);\n}\n\nfunction selectHardFeatureImports(imports: readonly CompiledApplicationImportIR[]): readonly ApplicationHardImportIR[] {\n return Object.freeze(\n imports.flatMap(binding =>\n binding.demand === 'instance' && !binding.optional && binding.provider.kind === 'feature'\n ? [\n Object.freeze({\n consumerFeatureIndex: binding.consumerFeatureIndex,\n providerFeatureIndex: binding.provider.featureIndex,\n }),\n ]\n : [],\n ),\n );\n}\n\n/** D213: each edge belongs to one consumer; avoid scanning all edges again for every feature. */\nfunction indexConsumerBindings<Binding extends { readonly consumerFeatureIndex: number }>(\n featureCount: number,\n bindings: readonly Binding[],\n): readonly (readonly Binding[])[] {\n const indexed = Array.from({ length: featureCount }, (): Binding[] => []);\n\n for (const binding of bindings) indexed[binding.consumerFeatureIndex]!.push(binding);\n\n return indexed.map(values => Object.freeze(values));\n}\n\nfunction compileFeatures(\n features: readonly AnyFeature[],\n owners: ReadonlyMap<AnyFeature, ApplicationLifetimeOwnerIR>,\n imports: readonly CompiledApplicationImportIR[],\n requirements: readonly CompiledApplicationRequirementIR[],\n): readonly CompiledApplicationFeatureIR[] {\n const importsByConsumer = indexConsumerBindings(features.length, imports);\n const requirementsByConsumer = indexConsumerBindings(features.length, requirements);\n\n return Object.freeze(\n features.map((definition, featureIndex) =>\n Object.freeze({\n definition,\n exportContractId: definition.exports.id,\n featureId: definition.id,\n featureIndex,\n imports: importsByConsumer[featureIndex]!,\n owner: owners.get(definition) ?? applicationOwner,\n requirements: requirementsByConsumer[featureIndex]!,\n }),\n ),\n );\n}\n\nfunction compileContributions(features: readonly AnyFeature[]): readonly CompiledApplicationContributionIR[] {\n const contributions = features.flatMap((feature, contributorFeatureIndex) =>\n getFeatureContributionDescriptors(feature).map(descriptor =>\n Object.freeze({\n contributionId: descriptor.id,\n contributorFeatureId: feature.id,\n contributorFeatureIndex,\n priority: descriptor.priority,\n target: descriptor.target,\n targetId: descriptor.target.id,\n }),\n ),\n );\n\n contributions.sort((left, right) => compareIds(left.contributionId, right.contributionId));\n\n return Object.freeze(contributions);\n}\n\n/** One group per distinct condition set, ordered by its canonical key so two equal plans compare equal (D93). */\n/**\n * One id names one condition (D193). Grouping keys features by the ids of their `when`, so two authentic conditions\n * that share an id — a host-bound one and a derived one, say — would land in one group and one of the two facts\n * would silently decide the lifetime of both. The same object used by several features stays legal: that is the set.\n */\nfunction assertDistinctConditionIdentities(features: readonly AnyFeature[]): void {\n const byId = new Map<string, Condition>();\n\n for (const feature of features) {\n for (const condition of feature.when) {\n const id = condition.id as string;\n const known = byId.get(id);\n\n if (known === undefined) {\n byId.set(id, condition);\n\n continue;\n }\n\n if (known !== condition) {\n throw new TypeError(`Condition ${id} is declared twice: one id names one condition, not two.`);\n }\n }\n }\n}\n\nfunction compileConditionGroups(\n features: readonly AnyFeature[],\n activationOrder: readonly number[],\n): readonly CompiledConditionGroupIR[] {\n assertDistinctConditionIdentities(features);\n const groups = new Map<\n string,\n { readonly activationOrder: number[]; readonly conditions: readonly Condition[]; readonly members: number[] }\n >();\n const groupOrders: (number[] | undefined)[] = [];\n\n for (const [featureIndex, feature] of features.entries()) {\n if (feature.when.length === 0) continue;\n\n const groupId = conditionSetKey(feature.when);\n const existing = groups.get(groupId);\n\n if (existing === undefined) {\n const group = { activationOrder: [], conditions: feature.when, members: [featureIndex] };\n groups.set(groupId, group);\n groupOrders[featureIndex] = group.activationOrder;\n } else {\n existing.members.push(featureIndex);\n groupOrders[featureIndex] = existing.activationOrder;\n }\n }\n\n for (const featureIndex of activationOrder) groupOrders[featureIndex]?.push(featureIndex);\n\n return Object.freeze(\n [...groups.entries()]\n .sort(([left], [right]) => compareIds(left, right))\n .map(([groupId, group]) =>\n Object.freeze({\n activationOrder: Object.freeze(group.activationOrder),\n conditions: Object.freeze(\n [...group.conditions].sort((left, right) => compareIds(left.id as string, right.id as string)),\n ),\n groupId,\n members: Object.freeze([...group.members].sort((left, right) => left - right)),\n }),\n ),\n );\n}\n\n/** A `from` condition reads one feature's exports, so that feature must outlive everything the condition gates. */\nfunction assertConditionSources(features: readonly AnyFeature[]): void {\n const world = new Set(features);\n\n for (const feature of features) {\n for (const condition of feature.when) {\n const source = requireConditionRecord(condition).from;\n\n if (source === undefined) continue;\n\n if (!world.has(source)) {\n throw new TypeError(`Condition ${condition.id} reads ${source.id}, which the application does not enable.`);\n }\n\n if (!conditionsInclude(feature.when, source.when)) {\n throw new TypeError(`Condition ${condition.id} reads shorter-lived ${source.id} than dependent ${feature.id}.`);\n }\n }\n }\n}\n\nfunction compileApplicationTopology(input: ApplicationCompileInput): CompiledApplicationPlanIR {\n const features = canonicalFeatures(input.features);\n const owners = assignLifetimeOwners(features);\n\n assertConditionSources(features);\n const imports = compileImports(features);\n const ports = compileApplicationPorts(features, (provider, consumer, key) =>\n assertCompatibleLifetime(provider, consumer, key, 'requirement'),\n );\n const hardImports = Object.freeze([\n ...selectHardFeatureImports(imports),\n ...ports.requirements.flatMap(binding =>\n binding.optional || binding.provider === undefined\n ? []\n : [\n Object.freeze({\n consumerFeatureIndex: binding.consumerFeatureIndex,\n providerFeatureIndex: binding.provider.featureIndex,\n }),\n ],\n ),\n ]);\n const activationOrder = compileCanonicalOrder(\n features.map(feature => feature.id),\n hardImports,\n );\n const compiledFeatures = compileFeatures(features, owners, imports, ports.requirements);\n const contributions = compileContributions(features);\n const groups = compileConditionGroups(features, activationOrder);\n\n return Object.freeze({\n applicationOrder: Object.freeze(\n activationOrder.filter(index => compiledFeatures[index]?.owner.kind === 'application'),\n ),\n contributions,\n edges: compileApplicationEdges(\n compiledFeatures,\n imports,\n ports.providers,\n ports.requirements,\n contributions,\n groups,\n ),\n features: compiledFeatures,\n groups,\n imports,\n providers: ports.providers,\n requirements: ports.requirements,\n });\n}\n\nexport { compileApplicationTopology };\nexport type {\n ApplicationCompileInput,\n ApplicationImportProviderIR,\n ApplicationLifetimeOwnerIR,\n ApplicationPortProviderIR,\n ApplicationTopologyEdgeIR,\n CompiledApplicationContributionIR,\n CompiledApplicationFeatureIR,\n CompiledApplicationImportIR,\n CompiledApplicationPlanIR,\n CompiledApplicationRequirementIR,\n CompiledConditionGroupIR,\n};\n"],"names":["applicationOwner","compareIds","left","right","canonicalFeatures","features","seen","ids","feature","isFeature","assignLifetimeOwners","owners","conditionSetKey","featureProviderIndexes","byContract","byContractId","featureIndex","contractId","duplicate","_a","conditionsInclude","outer","inner","condition","assertCompatibleLifetime","provider","consumer","importKey","dependencyKind","resolveFeatureProvider","providers","contract","demand","isOptional","exactProviderIndex","sameIdProviderIndex","resolveApplicationInputProvider","inputs","existing","compileImports","imports","consumerFeatureIndex","declarations","declaration","getDirectHostContract","getHostContractDemand","isHostContractOptional","isFeatureContract","selectHardFeatureImports","binding","indexConsumerBindings","featureCount","bindings","indexed","values","compileFeatures","requirements","importsByConsumer","requirementsByConsumer","definition","compileContributions","contributions","contributorFeatureIndex","getFeatureContributionDescriptors","descriptor","assertDistinctConditionIdentities","byId","id","known","compileConditionGroups","activationOrder","groups","groupOrders","groupId","group","assertConditionSources","world","source","requireConditionRecord","compileApplicationTopology","input","ports","compileApplicationPorts","key","hardImports","compileCanonicalOrder","compiledFeatures","index","compileApplicationEdges"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { Readable } from '@opetope/core';
2
+ import type { CompiledApplicationPlanIR } from './application-compiler.js';
3
+ import type { ReadyListeners } from './application-instance-retirement.js';
4
+ import type { ControllableCondition } from './condition-override.js';
5
+ import type { ConditionSource } from './condition-source.js';
6
+ import type { FeatureReady } from './feature-generation.js';
7
+ /** What a condition needs from the running application: the plan, the live ready values and their change hook. */
8
+ type ConditionRuntimeContext = {
9
+ readonly plan: CompiledApplicationPlanIR;
10
+ readonly readyListeners: ReadyListeners;
11
+ readonly readyValues: Map<number, FeatureReady<unknown>>;
12
+ };
13
+ declare function indexConditionSources(context: ConditionRuntimeContext, conditions: Readonly<Record<string, Readable<boolean>>>): Readonly<{
14
+ controls: ReadonlyMap<string, ControllableCondition>;
15
+ readables: ReadonlyMap<object, Readable<boolean>>;
16
+ sources: ReadonlyMap<object, ConditionSource>;
17
+ }>;
18
+ export { indexConditionSources };
@@ -0,0 +1,2 @@
1
+ import{requireConditionRecord as C}from"./condition.js";import{createControllableCondition as y}from"./condition-override.js";import{conditionFrom as g}from"./condition-source.js";function E(o,t,r){const e=()=>{const n=o.readyValues.get(t);return n===void 0?void 0:r(n.exports)};return Object.freeze({getSnapshot:()=>{var n;return((n=e())==null?void 0:n.getSnapshot())??!1},subscribe:n=>{let d=!0,c=0,a;const u=i=>d&&i===c,b=(i,s,f)=>{if(!u(i)){s==null||s();return}a=s,f&&n()},p=i=>{const s=++c,f=a;if(a=void 0,f==null||f(),!u(s))return;const l=e();u(s)&&b(s,l==null?void 0:l.subscribe(n),i)},h=o.readyListeners.add(t,()=>p(!0)),w=()=>{if(!d)return;d=!1,c+=1,h();const i=a;a=void 0,i==null||i()};try{p(!1)}catch(i){throw w(),i}return w}})}function m(o,t,r){const e=y(r);o.controls.set(t.id,e),o.sources.set(t,g(e.readable)),o.readables.set(t,e.readable)}function T(o,t,r){const e=o.features.findIndex(n=>n.definition===r);if(e<0)throw new TypeError(`Condition ${t.id} reads a feature outside the application.`);return e}function $(o,t,r,e){const n=o.get(r);if(n===void 0)throw new TypeError(`openApplication received an unknown condition ${r}.`);if(C(n).from!==void 0)throw new TypeError(`Condition ${r} is computed from a feature, so openApplication must not bind it.`);m(t,n,e)}function M(o){const t=new Map;for(const r of o.groups)for(const e of r.conditions){const n=t.get(e.id);if(n!==void 0&&n!==e)throw new TypeError(`Condition ${e.id} is declared twice: one id names one condition, not two.`);t.set(e.id,e)}return t}function j(o,t){const r=M(o.plan),e={controls:new Map,readables:new Map,sources:new Map};for(const[n,d]of Object.entries(t))$(r,e,n,d);for(const n of r.values()){const d=C(n);if(d.from===void 0){if(!e.controls.has(n.id))throw new TypeError(`Condition ${n.id} requires a binding: pass it in the conditions record of openApplication.`);continue}const c=T(o.plan,n,d.from);m(e,n,E(o,c,d.select))}return Object.freeze({controls:e.controls,readables:e.readables,sources:e.sources})}export{j as indexConditionSources};
2
+ //# sourceMappingURL=application-conditions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-conditions.js","sources":["../src/application-conditions.ts"],"sourcesContent":["import type { Readable } from '@opetope/core';\n\nimport type { CompiledApplicationPlanIR } from './application-compiler';\nimport type { ReadyListeners } from './application-instance-retirement';\nimport { requireConditionRecord } from './condition';\nimport { createControllableCondition } from './condition-override';\nimport type { ControllableCondition } from './condition-override';\nimport { conditionFrom } from './condition-source';\nimport type { ConditionSource } from './condition-source';\nimport type { Condition } from './condition-types';\nimport type { FeatureReady } from './feature-generation';\n\n/** What a condition needs from the running application: the plan, the live ready values and their change hook. */\ntype ConditionRuntimeContext = {\n readonly plan: CompiledApplicationPlanIR;\n readonly readyListeners: ReadyListeners;\n readonly readyValues: Map<number, FeatureReady<unknown>>;\n};\n\n/** A `from` condition reads its source instance while it is open and counts as `false` while it is not (D93). */\nfunction featureConditionReadable(\n context: ConditionRuntimeContext,\n featureIndex: number,\n select: (exports: unknown) => Readable<boolean>,\n): Readable<boolean> {\n const read = (): Readable<boolean> | undefined => {\n const ready = context.readyValues.get(featureIndex);\n\n return ready === undefined ? undefined : select(ready.exports);\n };\n\n return Object.freeze({\n getSnapshot: (): boolean => read()?.getSnapshot() ?? false,\n subscribe: (listener: () => void): (() => void) => {\n let active = true;\n let revision = 0;\n let inner: (() => void) | undefined;\n const isCurrent = (current: number): boolean => active && current === revision;\n const adopt = (current: number, subscribed: (() => void) | undefined, notify: boolean): void => {\n if (!isCurrent(current)) {\n subscribed?.();\n\n return;\n }\n\n inner = subscribed;\n\n if (notify) listener();\n };\n const bind = (notify: boolean): void => {\n const current = ++revision;\n const previous = inner;\n inner = undefined;\n previous?.();\n\n if (!isCurrent(current)) return;\n\n const source = read();\n\n if (!isCurrent(current)) return;\n\n adopt(current, source?.subscribe(listener), notify);\n };\n // Only this feature's transitions can rebind the condition, so the listener sits on its index.\n const release = context.readyListeners.add(featureIndex, () => bind(true));\n const close = (): void => {\n if (!active) return;\n\n active = false;\n revision += 1;\n release();\n const previous = inner;\n inner = undefined;\n previous?.();\n };\n\n try {\n bind(false);\n } catch (error) {\n close();\n throw error;\n }\n\n return close;\n },\n });\n}\n\n/**\n * Both halves of one indexing pass plus the debug override: what a group subscribes to, the plain `Readable` the\n * observation port pulls (`docs/devtools.md` §3.4), and the wrapper the control port writes through (D176).\n */\ntype ConditionIndex = {\n /** Keyed by condition id: the debug override is addressed by the id the panel shows, not by the declaration. */\n readonly controls: Map<string, ControllableCondition>;\n readonly readables: Map<object, Readable<boolean>>;\n readonly sources: Map<object, ConditionSource>;\n};\n\n/**\n * One wrapper per condition, always: the group and the observation port then read the same truth, and the control\n * port has something to write without a second path into the scheduler.\n */\nfunction bindControllableCondition(index: ConditionIndex, condition: Condition, source: Readable<boolean>): void {\n const controllable = createControllableCondition(source);\n index.controls.set(condition.id, controllable);\n index.sources.set(condition, conditionFrom(controllable.readable));\n index.readables.set(condition, controllable.readable);\n}\n\nfunction requireConditionFeatureIndex(plan: CompiledApplicationPlanIR, condition: Condition, source: object): number {\n const index = plan.features.findIndex(feature => feature.definition === source);\n\n if (index < 0) throw new TypeError(`Condition ${condition.id} reads a feature outside the application.`);\n\n return index;\n}\n\nfunction bindHostCondition(\n used: ReadonlyMap<string, Condition>,\n index: ConditionIndex,\n id: string,\n readable: Readable<boolean>,\n): void {\n const condition = used.get(id);\n\n if (condition === undefined) throw new TypeError(`openApplication received an unknown condition ${id}.`);\n\n if (requireConditionRecord(condition).from !== undefined) {\n throw new TypeError(`Condition ${id} is computed from a feature, so openApplication must not bind it.`);\n }\n\n bindControllableCondition(index, condition, readable);\n}\n\n/** The compiler already refused two conditions with one id (D193); the runtime keeps the same invariant honest. */\nfunction usedConditions(plan: CompiledApplicationPlanIR): ReadonlyMap<string, Condition> {\n const used = new Map<string, Condition>();\n\n for (const group of plan.groups) {\n for (const condition of group.conditions) {\n const known = used.get(condition.id);\n\n if (known !== undefined && known !== condition) {\n throw new TypeError(`Condition ${condition.id} is declared twice: one id names one condition, not two.`);\n }\n\n used.set(condition.id, condition);\n }\n }\n\n return used;\n}\n\nfunction indexConditionSources(\n context: ConditionRuntimeContext,\n conditions: Readonly<Record<string, Readable<boolean>>>,\n): Readonly<{\n controls: ReadonlyMap<string, ControllableCondition>;\n readables: ReadonlyMap<object, Readable<boolean>>;\n sources: ReadonlyMap<object, ConditionSource>;\n}> {\n const used = usedConditions(context.plan);\n const index: ConditionIndex = { controls: new Map(), readables: new Map(), sources: new Map() };\n\n for (const [id, readable] of Object.entries(conditions)) bindHostCondition(used, index, id, readable);\n\n for (const condition of used.values()) {\n const record = requireConditionRecord(condition);\n\n if (record.from === undefined) {\n if (!index.controls.has(condition.id)) {\n throw new TypeError(\n `Condition ${condition.id} requires a binding: pass it in the conditions record of openApplication.`,\n );\n }\n\n continue;\n }\n\n const featureIndex = requireConditionFeatureIndex(context.plan, condition, record.from);\n bindControllableCondition(index, condition, featureConditionReadable(context, featureIndex, record.select!));\n }\n\n return Object.freeze({ controls: index.controls, readables: index.readables, sources: index.sources });\n}\n\nexport { indexConditionSources };\n"],"names":["featureConditionReadable","context","featureIndex","select","read","ready","_a","listener","active","revision","inner","isCurrent","current","adopt","subscribed","notify","bind","previous","source","release","close","error","bindControllableCondition","index","condition","controllable","createControllableCondition","conditionFrom","requireConditionFeatureIndex","plan","feature","bindHostCondition","used","id","readable","requireConditionRecord","usedConditions","group","known","indexConditionSources","conditions","record"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { DeclarationId } from '@opetope/core';
2
+ import type { CompiledApplicationPlanIR } from './application-compiler.js';
3
+ import type { HostConditionId } from './condition.js';
4
+ import type { AnyFeature, FeatureConditionsOf } from './feature-authoring.js';
5
+ import type { ExactInput } from './feature-record.js';
6
+ import type { ErrorReporter } from './public-module.js';
7
+ declare const applicationBrand: unique symbol;
8
+ interface Application<Features extends readonly AnyFeature[] = readonly AnyFeature[]> {
9
+ readonly [applicationBrand]: () => Features;
10
+ readonly id: DeclarationId;
11
+ }
12
+ /** Which conditions the host binds at `openApplication`: every `from`-less condition an enabled feature waits for. */
13
+ type ApplicationConditionId<Features extends readonly AnyFeature[]> = HostConditionId<FeatureConditionsOf<Features[number]>[number]>;
14
+ interface ApplicationOptions<Features extends readonly AnyFeature[]> {
15
+ readonly features: Features;
16
+ readonly id: string;
17
+ readonly reporter: ErrorReporter;
18
+ }
19
+ interface ApplicationRecord {
20
+ readonly enabled: readonly AnyFeature[];
21
+ readonly features: readonly AnyFeature[];
22
+ readonly id: DeclarationId;
23
+ readonly plan: CompiledApplicationPlanIR;
24
+ readonly reporter: ErrorReporter;
25
+ }
26
+ declare function defineApplication<const Features extends readonly AnyFeature[], const Options extends ApplicationOptions<Features> = ApplicationOptions<Features>>(options: ExactInput<Options, ApplicationOptions<Features>>): Application<Features>;
27
+ declare function requireApplicationRecord(application: Application): ApplicationRecord;
28
+ declare function getApplicationPlan(application: Application): CompiledApplicationPlanIR;
29
+ export { defineApplication, getApplicationPlan, requireApplicationRecord };
30
+ export type { Application, ApplicationConditionId };
@@ -0,0 +1,2 @@
1
+ import{declarationId as A}from"@opetope/core";import{compileApplicationTopology as y}from"./application-compiler.js";import{getFeatureByExportContract as g}from"./feature-authoring.js";import{isHostContractOptional as b}from"./feature-contract.js";import{assertPlainRecord as E,dataEntries as T,assertExactDataKeys as R}from"./feature-record.js";import{isFeature as I}from"./feature-definition-support.js";const w=new WeakMap,p=Reflect.get(globalThis,"WeakRef");function x(e){return p===void 0?{deref:()=>e}:new p(e)}const c=new Map;function l(e){const t=c.get(e);t!==void 0&&t.deref()===void 0&&c.delete(e)}const m=Reflect.get(globalThis,"FinalizationRegistry"),u=p===void 0||m===void 0?void 0:new m(l);let d;function F(){if(!(p===void 0||u!==void 0)){d??(d=c.entries());for(let e=4;e>0;e-=1){const t=d.next();if(t.done){d=void 0;break}const[r]=t.value;l(r)}}}function O(e){if(F(),l(e),!!c.has(e))throw new TypeError(`Application id ${e} is declared more than once.`)}function j(e,t){if(!Array.isArray(e))throw new TypeError(`${t} must be an array.`);const r=Array.from(e);for(const n of r)if(!I(n))throw new TypeError(`${t} contains a non-authentic Feature.`);return Object.freeze(r)}function k(e){const t=[],r=new Set,n=[...e];for(let i=0;i<n.length;i+=1){const o=n[i];if(!r.has(o)){r.add(o),t.push(o);for(const a of Object.values(o.imports)){if(b(a))continue;const s=g(a);s!==void 0&&n.push(s)}}}return Object.freeze(t)}function z(e){E(e,"Application options");const t=T(e,"Application options");R(e,t,["features","id","reporter"],"Application options");const r=e.id;if(typeof r!="string")throw new TypeError("Application id must be a string.");const n=A(r);O(n);const i=j(e.features,"Application features"),o=k(i),a=e.reporter;if(typeof a!="function")throw new TypeError("Application reporter must be a function.");const s=y({features:o}),f=Object.freeze({id:n});return c.set(n,x(f)),u==null||u.register(f,n),w.set(f,{enabled:i,features:o,id:n,plan:s,reporter:a}),f}function h(e){const t=w.get(e);if(t===void 0)throw new TypeError("Application is not authentic.");return t}function C(e){return h(e).plan}export{z as defineApplication,C as getApplicationPlan,h as requireApplicationRecord};
2
+ //# sourceMappingURL=application-definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-definition.js","sources":["../src/application-definition.ts"],"sourcesContent":["import { declarationId } from '@opetope/core';\nimport type { DeclarationId } from '@opetope/core';\n\nimport { compileApplicationTopology } from './application-compiler';\nimport type { CompiledApplicationPlanIR } from './application-compiler';\nimport type { HostConditionId } from './condition';\nimport type { AnyFeature, FeatureConditionsOf } from './feature-authoring';\nimport { getFeatureByExportContract, isFeature } from './feature-authoring';\nimport { isHostContractOptional } from './feature-contract';\nimport { assertExactDataKeys, assertPlainRecord, dataEntries } from './feature-record';\nimport type { ExactInput } from './feature-record';\nimport type { ErrorReporter } from './public-module';\n\ndeclare const applicationBrand: unique symbol;\n\ninterface Application<Features extends readonly AnyFeature[] = readonly AnyFeature[]> {\n readonly [applicationBrand]: () => Features;\n readonly id: DeclarationId;\n}\n\n/** Which conditions the host binds at `openApplication`: every `from`-less condition an enabled feature waits for. */\ntype ApplicationConditionId<Features extends readonly AnyFeature[]> = HostConditionId<\n FeatureConditionsOf<Features[number]>[number]\n>;\n\ninterface ApplicationOptions<Features extends readonly AnyFeature[]> {\n readonly features: Features;\n readonly id: string;\n readonly reporter: ErrorReporter;\n}\n\ninterface ApplicationRecord {\n readonly enabled: readonly AnyFeature[];\n readonly features: readonly AnyFeature[];\n readonly id: DeclarationId;\n readonly plan: CompiledApplicationPlanIR;\n readonly reporter: ErrorReporter;\n}\n\nconst applicationRecords = new WeakMap<object, ApplicationRecord>();\n\n/**\n * One live definition per id. The registry holds the definition weakly where the engine can: a module that is\n * re-evaluated — HMR, a second import of a manifest in a test — drops the previous definition, and an id nobody\n * holds any more must not keep refusing new ones. A definition that is still reachable does refuse: two live\n * Applications with one id would compile two plans nobody can tell apart.\n *\n * `WeakRef` sits above the supported browser floor, so it is read through the global instead of named directly; on\n * an engine without it the registry holds ids strongly and refuses a redeclaration, which is the old behaviour.\n */\ntype WeakHold = { readonly deref: () => object | undefined };\ntype WeakHoldConstructorLike = new (value: object) => WeakHold;\ntype DefinitionFinalizer = { readonly register: (definition: object, id: DeclarationId) => void };\ntype DefinitionFinalizerConstructor = new (release: (id: DeclarationId) => void) => DefinitionFinalizer;\n\nconst weakHoldConstructor = Reflect.get(globalThis, 'WeakRef') as WeakHoldConstructorLike | undefined;\n\nfunction holdDefinition(definition: object): WeakHold {\n if (weakHoldConstructor === undefined) return { deref: () => definition };\n\n return new weakHoldConstructor(definition);\n}\n\nconst applicationIds = new Map<DeclarationId, WeakHold>();\n/** A late finalizer may name a reused id: only the currently dead registration is removable. */\nfunction releaseApplicationId(id: DeclarationId): void {\n const held = applicationIds.get(id);\n\n if (held !== undefined && held.deref() === undefined) applicationIds.delete(id);\n}\n\nconst finalizerConstructor = Reflect.get(globalThis, 'FinalizationRegistry') as\n | DefinitionFinalizerConstructor\n | undefined;\nconst applicationIdFinalizer =\n weakHoldConstructor === undefined || finalizerConstructor === undefined\n ? undefined\n : new finalizerConstructor(releaseApplicationId);\nlet applicationIdSweep: IterableIterator<[DeclarationId, WeakHold]> | undefined;\n\nfunction sweepApplicationIds(): void {\n if (weakHoldConstructor === undefined || applicationIdFinalizer !== undefined) return;\n\n applicationIdSweep ??= applicationIds.entries();\n\n // Engines with WeakRef but no finalizer still make bounded progress without a full scan per declaration.\n for (let remaining = 4; remaining > 0; remaining -= 1) {\n const entry = applicationIdSweep.next();\n\n if (entry.done) {\n applicationIdSweep = undefined;\n\n break;\n }\n\n const [id] = entry.value;\n releaseApplicationId(id);\n }\n}\n\nfunction assertApplicationIdIsFree(id: DeclarationId): void {\n sweepApplicationIds();\n releaseApplicationId(id);\n\n if (!applicationIds.has(id)) return;\n\n throw new TypeError(`Application id ${id} is declared more than once.`);\n}\n\nfunction snapshotFeatures(value: unknown, label: string): readonly AnyFeature[] {\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array.`);\n\n const features = Array.from(value as readonly unknown[]);\n\n for (const feature of features) {\n if (!isFeature(feature)) throw new TypeError(`${label} contains a non-authentic Feature.`);\n }\n\n return Object.freeze(features as AnyFeature[]);\n}\n\n/**\n * The world is what the application enabled plus what those features hard-import: `features` is the declaration and\n * the hard import closure is derived (D68, D93). A port provider that is not enabled stays out, so a missing\n * port is still reported as a missing provider, and a weak edge never pulls its provider in (D105).\n */\nfunction collectApplicationWorld(enabled: readonly AnyFeature[]): readonly AnyFeature[] {\n const world: AnyFeature[] = [];\n const seen = new Set<AnyFeature>();\n const queue = [...enabled];\n\n for (let next = 0; next < queue.length; next += 1) {\n const feature = queue[next]!;\n\n if (seen.has(feature)) continue;\n\n seen.add(feature);\n world.push(feature);\n\n for (const declaration of Object.values(feature.imports)) {\n // A weak edge never enlarges the world: an absent provider is just a permanently missing one (D105).\n if (isHostContractOptional(declaration)) continue;\n\n const provider = getFeatureByExportContract(declaration);\n\n if (provider !== undefined) queue.push(provider);\n }\n }\n\n return Object.freeze(world);\n}\n\nfunction defineApplication<\n const Features extends readonly AnyFeature[],\n const Options extends ApplicationOptions<Features> = ApplicationOptions<Features>,\n>(options: ExactInput<Options, ApplicationOptions<Features>>): Application<Features>;\nfunction defineApplication(options: unknown): Application {\n assertPlainRecord(options, 'Application options');\n const entries = dataEntries(options, 'Application options');\n\n assertExactDataKeys(options, entries, ['features', 'id', 'reporter'], 'Application options');\n const rawId = options['id'];\n\n if (typeof rawId !== 'string') throw new TypeError('Application id must be a string.');\n\n const id = declarationId(rawId);\n\n assertApplicationIdIsFree(id);\n\n const enabled = snapshotFeatures(options['features'], 'Application features');\n const features = collectApplicationWorld(enabled);\n const reporter = options['reporter'];\n\n if (typeof reporter !== 'function') throw new TypeError('Application reporter must be a function.');\n\n const plan = compileApplicationTopology({ features });\n const definition = Object.freeze({ id }) as unknown as Application;\n applicationIds.set(id, holdDefinition(definition));\n applicationIdFinalizer?.register(definition, id);\n\n applicationRecords.set(definition, {\n enabled,\n features,\n id,\n plan,\n reporter: reporter as ErrorReporter,\n });\n\n return definition;\n}\n\nfunction requireApplicationRecord(application: Application): ApplicationRecord {\n const record = applicationRecords.get(application);\n\n if (record === undefined) throw new TypeError('Application is not authentic.');\n\n return record;\n}\n\nfunction getApplicationPlan(application: Application): CompiledApplicationPlanIR {\n return requireApplicationRecord(application).plan;\n}\n\nexport { defineApplication, getApplicationPlan, requireApplicationRecord };\nexport type { Application, ApplicationConditionId };\n"],"names":["applicationRecords","weakHoldConstructor","holdDefinition","definition","applicationIds","releaseApplicationId","id","held","finalizerConstructor","applicationIdFinalizer","applicationIdSweep","sweepApplicationIds","remaining","entry","assertApplicationIdIsFree","snapshotFeatures","value","label","features","feature","isFeature","collectApplicationWorld","enabled","world","seen","queue","next","declaration","isHostContractOptional","provider","getFeatureByExportContract","defineApplication","options","assertPlainRecord","entries","dataEntries","assertExactDataKeys","rawId","declarationId","reporter","plan","compileApplicationTopology","requireApplicationRecord","application","record","getApplicationPlan"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * One class per subject with the state in `code` (`spec.md` §4, D69). It lives apart from `application-execution.ts`
3
+ * so the observation port can name it without importing the execution it observes.
4
+ */
5
+ declare class ApplicationError extends Error {
6
+ readonly code: 'closed';
7
+ readonly name = "ApplicationError";
8
+ constructor(code: 'closed', message: string);
9
+ }
10
+ export { ApplicationError };
@@ -0,0 +1,2 @@
1
+ class c extends Error{code;name="ApplicationError";constructor(r,o){super(o),this.code=r}}export{c as ApplicationError};
2
+ //# sourceMappingURL=application-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-error.js","sources":["../src/application-error.ts"],"sourcesContent":["/**\n * One class per subject with the state in `code` (`spec.md` §4, D69). It lives apart from `application-execution.ts`\n * so the observation port can name it without importing the execution it observes.\n */\nclass ApplicationError extends Error {\n override readonly name = 'ApplicationError';\n\n constructor(\n readonly code: 'closed',\n message: string,\n ) {\n super(message);\n }\n}\n\nexport { ApplicationError };\n"],"names":["ApplicationError","code","message"],"mappings":"AAIA,MAAMA,UAAyB,KAAK,CAIvB,KAHO,KAAO,mBAEzB,YACWC,EACTC,EAAe,CAEf,MAAMA,CAAO,EAHJ,KAAA,KAAAD,CAIX,CACD"}
@@ -0,0 +1,43 @@
1
+ import type { Readable } from '@opetope/core';
2
+ import type { CompiledApplicationPlanIR } from './application-compiler.js';
3
+ import type { Application } from './application-definition.js';
4
+ import { ApplicationError } from './application-error.js';
5
+ import type { ReadyListeners } from './application-instance-retirement.js';
6
+ import { bind } from './application-open-options.js';
7
+ import type { ApplicationImportBinding, OpenApplicationOptions } from './application-open-options.js';
8
+ import type { Condition } from './condition.js';
9
+ import type { GroupMemberInstance } from './condition-group-execution.js';
10
+ import type { AnyFeature } from './feature-authoring.js';
11
+ import type { FeatureReady } from './feature-generation.js';
12
+ import { ApplicationActivity } from './inspection-activity.js';
13
+ import type { ObservationSlot } from './inspection-observer.js';
14
+ import type { CleanupRetryError } from './instance-demand.js';
15
+ import type { CleanupFailurePolicy } from './public-module.js';
16
+ import type { RuntimeErrorReporter } from './runtime-error-reporting.js';
17
+ interface ApplicationExecution {
18
+ readonly close: () => Promise<void>;
19
+ readonly ready: Promise<void>;
20
+ }
21
+ type FeatureRuntimeContext = {
22
+ readonly activity: ApplicationActivity;
23
+ /** The host's answer to a failed cleanup, passed to every instance the application opens (D182). */
24
+ readonly cleanupFailure: CleanupFailurePolicy;
25
+ readonly conditionReadables: ReadonlyMap<Condition, Readable<boolean>>;
26
+ readonly generations: Map<number, GroupMemberInstance>;
27
+ readonly importValues: ReadonlyMap<object, unknown>;
28
+ /** Empty until a session attaches: an unobserved application allocates no observation record (`docs/devtools.md` §3.2). */
29
+ readonly observation: ObservationSlot;
30
+ readonly plan: CompiledApplicationPlanIR;
31
+ /** Quarantined cleanups, written on the failure path only, so the control port can retry an exact frontier. */
32
+ readonly quarantines: Map<number, CleanupRetryError>;
33
+ /** A `from` condition rebinds when its source instance appears or retires, so ready changes are observable. */
34
+ readonly readyListeners: ReadyListeners;
35
+ readonly readyValues: Map<number, FeatureReady<unknown>>;
36
+ readonly reporter: RuntimeErrorReporter;
37
+ /** Instances whose retire has started and not settled: a consumer is only gone once its close has finished. */
38
+ readonly retiring: Map<number, Promise<void>>;
39
+ };
40
+ declare function openApplication<const Features extends readonly AnyFeature[]>(application: Application<Features>, options: OpenApplicationOptions<Features>): ApplicationExecution;
41
+ export { ApplicationError, bind, openApplication };
42
+ export type { ApplicationExecution, ApplicationImportBinding, OpenApplicationOptions };
43
+ export type { FeatureRuntimeContext };
@@ -0,0 +1,2 @@
1
+ import{isCancellation as v,createAggregateError as A}from"@opetope/core/internal";import{indexConditionSources as F}from"./application-conditions.js";import{requireApplicationRecord as x}from"./application-definition.js";import{ApplicationError as u}from"./application-error.js";import{openFeatureInstance as C}from"./application-feature-instance.js";import{createGroupOrder as O}from"./application-group-order.js";import{createReadyListeners as E,closeObservation as G}from"./application-instance-retirement.js";import{snapshotOpenOptions as S}from"./application-open-options.js";import{bind as rt}from"./application-open-options.js";import{retireGroupMembersReverse as h,createConditionGroupExecution as k}from"./condition-group-execution.js";import{combineConditionSources as I}from"./condition-source.js";import{registerControllableApplication as M,controllableInstances as T,groupsOfCondition as j}from"./control-registry.js";import{ApplicationActivity as V}from"./inspection-activity.js";import{createObservationSlot as q}from"./inspection-observer.js";import{registerObservableApplication as R}from"./inspection-registry.js";import{reportRuntimeFailure as z}from"./runtime-error-reporting.js";function f(r,e){return r.length===1?r[0]:A(r,e)}function $(r,e){const t=r.imports.filter(s=>s.provider.kind==="application-input"),o=new Set(t.map(s=>s.contract)),a=new Set(t.flatMap(s=>s.optional?[]:[s.contract])),n=new Map;for(const s of e){if(!o.has(s.contract))throw new TypeError("openApplication received an unknown import contract.");if(n.has(s.contract))throw new TypeError(`Application input ${s.contract.id} is bound twice.`);n.set(s.contract,s.value)}const i=[...a].find(s=>!n.has(s));if(i!==void 0)throw new TypeError(`${i.id} requires bind(contract, value) in openApplication.`);return n}function m(r,e){return Object.freeze(r.map(t=>{const o=e.plan.features[t];if(o===void 0)throw new TypeError(`Application plan feature ${String(t)} is missing.`);return Object.freeze({id:o.featureId,open:()=>C(o,e)})}))}function L(r,e,t){let o=!0,a=!1,n=0,i=!0;const s=c=>{if(i){if(typeof c!="boolean"){const p=new TypeError("Condition source must publish a boolean.");if(o)throw p;z(p,t),n+=1,e({active:!1,revision:n});return}a=!0,n+=1,e({active:c,revision:n})}},l=r.readAndSubscribe(s);if(o=!1,typeof l!="function")throw i=!1,new TypeError("Condition source must return a disposer.");if(!a)throw i=!1,l(),new TypeError("Condition source must publish its initial value synchronously.");return()=>{i&&(i=!1,l())}}async function w(r){return(await Promise.allSettled([...r].reverse().map(t=>t.close()))).flatMap(t=>t.status==="rejected"?[t.reason]:[])}async function P(r,e,t){try{for(const o of r){if(e.closing)return{kind:"closed"};const a=o.open();e.opening.push(a),e.closing&&t(),await a.ready}}catch(o){return{error:o,kind:"failed"}}return e.closing?{kind:"closed"}:{kind:"opened"}}function g(r){const e=[];for(const t of[...r].reverse())try{t()}catch(o){e.push(o)}return e}class B{context;sources;state;ready;closeAttempt;constructor(e,t,o){this.context=e,this.sources=t,this.state=o,this.ready=this.start(),this.ready.catch(()=>{})}close=()=>{if(this.closeAttempt!==void 0)return this.closeAttempt;let e,t;const o=new Promise((n,i)=>{e=n,t=i});this.closeAttempt=o,this.state.closing=!0,this.state.closeFailures.push(...g(this.state.releases.splice(0))),this.fenceOpening();for(const n of this.state.applicationGenerations??[])n.fence();for(const n of this.state.opening)n.fence();const a=w(this.state.groups);return(async()=>{try{await this.ready.catch(()=>{}),this.state.closeFailures.push(...await a);const n=this.state.applicationGenerations;this.state.applicationGenerations=void 0,n!==void 0&&this.state.closeFailures.push(...await h(n)),this.state.closeFailures.length===0&&this.context.activity.close()}finally{G(this.context.observation,this.context.reporter)}if(this.state.closeFailures.length>0)throw f(this.state.closeFailures,"Application failed to close.")})().then(e,t),o};fenceOpening(){const e=this.state.opening[this.state.opening.length-1];if(e!==void 0)try{e.retire().catch(()=>{})}catch{}}installConditionGroups(){for(const e of this.context.plan.groups){const{observation:t}=this.context,o=O(this.context,e.groupId),a=k({entries:m(e.activationOrder,this.context),id:e.groupId,observation:t,...o===void 0?{}:{order:o},reporter:this.context.reporter});this.state.groups.push(a);const n=e.conditions.map(c=>{const p=this.sources.get(c);if(p===void 0)throw new TypeError(`Condition ${c.id} is unavailable.`);return p}),i=c=>{var p;a.publish(c),(p=t.observer)==null||p.conditionsChanged(e.groupId)},s=I(n),l=L(s,i,this.context.reporter);if(this.state.closing){this.state.closeFailures.push(...g([l]));return}this.state.releases.push(l)}}async openApplicationFeatures(){const e=m(this.context.plan.applicationOrder,this.context),t=await P(e,this.state,()=>this.fenceOpening());if(t.kind==="opened"){this.state.applicationGenerations=Object.freeze([...this.state.opening]),this.state.opening.length=0;return}const o=await h(this.state.opening);throw this.state.opening.length=0,this.state.closing&&this.state.closeFailures.push(...o),t.kind==="closed"?new u("closed","Application closed before it was ready."):this.state.closing&&v(t.error)?t.error:f([t.error,...o],"Application failed to open.")}async start(){if(await this.openApplicationFeatures(),this.state.closing)throw new u("closed","Application closed before it was ready.");try{this.installConditionGroups()}catch(e){if(this.state.closing)throw this.state.closeFailures.push(e),e;this.state.closeFailures.push(...g(this.state.releases.splice(0)));const t=await w(this.state.groups),o=this.state.applicationGenerations??[];this.state.applicationGenerations=void 0;const a=await h(o);throw f([e,...t,...a],"Application condition controls failed to open.")}if(this.state.closing)throw new u("closed","Application closed before it was ready.")}}function D(r,e){const t=x(r),o=S(e),a=new Map,n=q(),i={activity:new V(n),cleanupFailure:o.cleanupFailure,conditionReadables:a,generations:new Map,importValues:$(t.plan,o.imports),observation:n,plan:t.plan,quarantines:new Map,readyListeners:E(),readyValues:new Map,reporter:t.reporter,retiring:new Map},s=F(i,o.conditions),l=s.sources;for(const[y,b]of s.readables)a.set(y,b);const c={applicationGenerations:void 0,closeFailures:[],closing:!1,groups:[],opening:[],releases:[]},p=new B(i,l,c),d=Object.freeze({close:p.close,ready:p.ready});return M(d,{activity:i.activity,closed:()=>c.closing,conditions:s.controls,groups:c.groups,groupsOfCondition:j(t.plan),instances:T(t.plan),observation:i.observation,quarantines:i.quarantines}),R(d,{activity:i.activity,applicationId:t.id,conditionControls:s.controls,conditionReadables:a,importValues:i.importValues,liveInstances:i.generations,observation:i.observation,plan:t.plan,readyInstances:i.readyValues,reporter:t.reporter}),d}export{u as ApplicationError,rt as bind,D as openApplication};
2
+ //# sourceMappingURL=application-execution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-execution.js","sources":["../src/application-execution.ts"],"sourcesContent":["import type { Readable } from '@opetope/core';\nimport { createAggregateError, isCancellation } from '@opetope/core/internal';\n\nimport type { CompiledApplicationPlanIR } from './application-compiler';\nimport { indexConditionSources } from './application-conditions';\nimport { requireApplicationRecord } from './application-definition';\nimport type { Application } from './application-definition';\nimport { ApplicationError } from './application-error';\nimport { openFeatureInstance } from './application-feature-instance';\nimport { createGroupOrder } from './application-group-order';\nimport { closeObservation, createReadyListeners } from './application-instance-retirement';\nimport type { ReadyListeners } from './application-instance-retirement';\nimport { bind, snapshotOpenOptions } from './application-open-options';\nimport type { ApplicationImportBinding, OpenApplicationOptions } from './application-open-options';\nimport type { Condition } from './condition';\nimport { createConditionGroupExecution, retireGroupMembersReverse } from './condition-group-execution';\nimport type {\n ConditionGroupEntry,\n ConditionGroupExecution,\n ConditionSnapshot,\n GroupMemberInstance,\n} from './condition-group-execution';\nimport { combineConditionSources } from './condition-source';\nimport type { ConditionSource } from './condition-source';\nimport { controllableInstances, groupsOfCondition, registerControllableApplication } from './control-registry';\nimport type { AnyFeature } from './feature-authoring';\nimport type { FeatureReady } from './feature-generation';\nimport { ApplicationActivity } from './inspection-activity';\nimport { createObservationSlot } from './inspection-observer';\nimport type { ObservationSlot } from './inspection-observer';\nimport { registerObservableApplication } from './inspection-registry';\nimport type { CleanupRetryError } from './instance-demand';\nimport type { CleanupFailurePolicy } from './public-module';\nimport { reportRuntimeFailure } from './runtime-error-reporting';\nimport type { RuntimeErrorReporter } from './runtime-error-reporting';\n\ninterface ApplicationExecution {\n readonly close: () => Promise<void>;\n readonly ready: Promise<void>;\n}\n\ntype FeatureRuntimeContext = {\n readonly activity: ApplicationActivity;\n /** The host's answer to a failed cleanup, passed to every instance the application opens (D182). */\n readonly cleanupFailure: CleanupFailurePolicy;\n readonly conditionReadables: ReadonlyMap<Condition, Readable<boolean>>;\n readonly generations: Map<number, GroupMemberInstance>;\n readonly importValues: ReadonlyMap<object, unknown>;\n /** Empty until a session attaches: an unobserved application allocates no observation record (`docs/devtools.md` §3.2). */\n readonly observation: ObservationSlot;\n readonly plan: CompiledApplicationPlanIR;\n /** Quarantined cleanups, written on the failure path only, so the control port can retry an exact frontier. */\n readonly quarantines: Map<number, CleanupRetryError>;\n /** A `from` condition rebinds when its source instance appears or retires, so ready changes are observable. */\n readonly readyListeners: ReadyListeners;\n readonly readyValues: Map<number, FeatureReady<unknown>>;\n readonly reporter: RuntimeErrorReporter;\n /** Instances whose retire has started and not settled: a consumer is only gone once its close has finished. */\n readonly retiring: Map<number, Promise<void>>;\n};\n\ntype ApplicationExecutionState = {\n applicationGenerations: readonly GroupMemberInstance[] | undefined;\n readonly closeFailures: unknown[];\n closing: boolean;\n readonly groups: ConditionGroupExecution[];\n readonly opening: GroupMemberInstance[];\n readonly releases: (() => void)[];\n};\n\ntype ApplicationOpeningOutcome =\n | { readonly error: unknown; readonly kind: 'failed' }\n | { readonly kind: 'closed' }\n | { readonly kind: 'opened' };\n\nfunction combinedFailure(failures: readonly unknown[], message: string): unknown {\n if (failures.length === 1) return failures[0];\n\n return createAggregateError(failures, message);\n}\n\nfunction indexImportValues(\n plan: CompiledApplicationPlanIR,\n bindings: readonly ApplicationImportBinding[],\n): ReadonlyMap<object, unknown> {\n const inputs = plan.imports.filter(binding => binding.provider.kind === 'application-input');\n const known = new Set(inputs.map(binding => binding.contract));\n const expected = new Set(inputs.flatMap(binding => (binding.optional ? [] : [binding.contract])));\n const values = new Map<object, unknown>();\n\n for (const binding of bindings) {\n if (!known.has(binding.contract)) throw new TypeError('openApplication received an unknown import contract.');\n\n if (values.has(binding.contract)) throw new TypeError(`Application input ${binding.contract.id} is bound twice.`);\n\n values.set(binding.contract, binding.value);\n }\n\n const missing = [...expected].find(contract => !values.has(contract));\n\n if (missing !== undefined) throw new TypeError(`${missing.id} requires bind(contract, value) in openApplication.`);\n\n return values;\n}\n\nfunction createFeatureEntries(\n indexes: readonly number[],\n context: FeatureRuntimeContext,\n): readonly ConditionGroupEntry[] {\n return Object.freeze(\n indexes.map(featureIndex => {\n const feature = context.plan.features[featureIndex];\n\n if (feature === undefined) throw new TypeError(`Application plan feature ${String(featureIndex)} is missing.`);\n\n return Object.freeze({\n id: feature.featureId,\n open: () => openFeatureInstance(feature, context),\n });\n }),\n );\n}\n\nfunction installConditionSource(\n source: ConditionSource,\n publish: (snapshot: ConditionSnapshot) => void,\n reporter: RuntimeErrorReporter,\n): () => void {\n let installing = true;\n let published = false;\n let revision = 0;\n let subscribed = true;\n const emit = (active: boolean): void => {\n if (!subscribed) return;\n\n if (typeof active !== 'boolean') {\n const error = new TypeError('Condition source must publish a boolean.');\n\n if (installing) throw error;\n\n reportRuntimeFailure(error, reporter);\n revision += 1;\n publish({ active: false, revision });\n\n return;\n }\n\n published = true;\n revision += 1;\n publish({ active, revision });\n };\n const release = source.readAndSubscribe(emit);\n installing = false;\n\n if (typeof release !== 'function') {\n subscribed = false;\n throw new TypeError('Condition source must return a disposer.');\n }\n\n if (!published) {\n subscribed = false;\n release();\n throw new TypeError('Condition source must publish its initial value synchronously.');\n }\n\n return (): void => {\n if (!subscribed) return;\n\n subscribed = false;\n release();\n };\n}\n\n/**\n * Every group is fenced at once and observed at once. Awaiting the attempts one after another left the failure of a\n * later group unobserved while an earlier one still drained, so the runtime produced an unhandled rejection of its\n * own making even when the caller handled `close()` (D182, D189). The order of the failures is the order of close.\n */\nasync function closeConditionGroups(groups: readonly ConditionGroupExecution[]): Promise<readonly unknown[]> {\n const attempts = await Promise.allSettled([...groups].reverse().map(group => group.close()));\n\n return attempts.flatMap(attempt => (attempt.status === 'rejected' ? [attempt.reason as unknown] : []));\n}\n\nasync function openApplicationEntries(\n entries: readonly ConditionGroupEntry[],\n state: ApplicationExecutionState,\n fence: () => void,\n): Promise<ApplicationOpeningOutcome> {\n try {\n for (const entry of entries) {\n if (state.closing) return { kind: 'closed' };\n\n const generation = entry.open();\n state.opening.push(generation);\n\n if (state.closing) fence();\n\n await generation.ready;\n }\n } catch (error) {\n return { error, kind: 'failed' };\n }\n\n return state.closing ? { kind: 'closed' } : { kind: 'opened' };\n}\n\nfunction releaseSources(releases: readonly (() => void)[]): readonly unknown[] {\n const failures: unknown[] = [];\n\n for (const release of [...releases].reverse()) {\n try {\n release();\n } catch (error) {\n failures.push(error);\n }\n }\n\n return failures;\n}\n\nclass ApplicationExecutionController {\n readonly ready: Promise<void>;\n\n private closeAttempt: Promise<void> | undefined;\n\n constructor(\n private readonly context: FeatureRuntimeContext,\n private readonly sources: ReadonlyMap<object, ConditionSource>,\n private readonly state: ApplicationExecutionState,\n ) {\n this.ready = this.start();\n void this.ready.catch(() => undefined);\n }\n\n readonly close = (): Promise<void> => {\n if (this.closeAttempt !== undefined) return this.closeAttempt;\n\n // D210: source disposers and fences may join close synchronously, before the drain starts.\n let resolveClose!: () => void;\n let rejectClose!: (error: unknown) => void;\n const closeAttempt = new Promise<void>((resolve, reject) => {\n resolveClose = resolve;\n rejectClose = reject;\n });\n this.closeAttempt = closeAttempt;\n this.state.closing = true;\n this.state.closeFailures.push(...releaseSources(this.state.releases.splice(0)));\n this.fenceOpening();\n // D189: admission of every live instance stops in this turn, before the ordered physical drain starts.\n for (const generation of this.state.applicationGenerations ?? []) generation.fence();\n\n for (const generation of this.state.opening) generation.fence();\n const groups = closeConditionGroups(this.state.groups);\n\n void (async () => {\n try {\n await this.ready.catch(() => undefined);\n this.state.closeFailures.push(...(await groups));\n const generations = this.state.applicationGenerations;\n this.state.applicationGenerations = undefined;\n\n if (generations !== undefined) {\n this.state.closeFailures.push(...(await retireGroupMembersReverse(generations)));\n }\n\n if (this.state.closeFailures.length === 0) this.context.activity.close();\n } finally {\n // D167: the observer sees the fence it is watching for — group close, instance retire, contribution\n // Withdraw — and only then does the application take its session down with it (D167).\n closeObservation(this.context.observation, this.context.reporter);\n }\n\n if (this.state.closeFailures.length > 0) {\n throw combinedFailure(this.state.closeFailures, 'Application failed to close.');\n }\n })().then(resolveClose, rejectClose);\n\n return closeAttempt;\n };\n\n private fenceOpening(): void {\n const generation = this.state.opening[this.state.opening.length - 1];\n\n if (generation === undefined) return;\n\n try {\n void generation.retire().catch(() => undefined);\n } catch {\n // The serialized opening rollback below owns failure delivery.\n }\n }\n\n private installConditionGroups(): void {\n for (const group of this.context.plan.groups) {\n const { observation } = this.context;\n const order = createGroupOrder(this.context, group.groupId);\n const controller = createConditionGroupExecution({\n entries: createFeatureEntries(group.activationOrder, this.context),\n id: group.groupId,\n observation,\n ...(order === undefined ? {} : { order }),\n reporter: this.context.reporter,\n });\n this.state.groups.push(controller);\n const sources = group.conditions.map(condition => {\n const source = this.sources.get(condition);\n\n if (source === undefined) throw new TypeError(`Condition ${condition.id} is unavailable.`);\n\n return source;\n });\n const publish = (snapshot: ConditionSnapshot): void => {\n controller.publish(snapshot);\n observation.observer?.conditionsChanged(group.groupId);\n };\n const source = combineConditionSources(sources);\n const release = installConditionSource(source, publish, this.context.reporter);\n\n // D210: close may have run inside subscribe, before this disposer existed. It already fenced this group;\n // release the late subscription now, and never register another group after that close took its snapshot.\n if (this.state.closing) {\n this.state.closeFailures.push(...releaseSources([release]));\n\n return;\n }\n\n this.state.releases.push(release);\n }\n }\n\n private async openApplicationFeatures(): Promise<void> {\n const entries = createFeatureEntries(this.context.plan.applicationOrder, this.context);\n const outcome = await openApplicationEntries(entries, this.state, () => this.fenceOpening());\n\n if (outcome.kind === 'opened') {\n this.state.applicationGenerations = Object.freeze([...this.state.opening]);\n this.state.opening.length = 0;\n\n return;\n }\n\n const cleanupFailures = await retireGroupMembersReverse(this.state.opening);\n this.state.opening.length = 0;\n\n if (this.state.closing) this.state.closeFailures.push(...cleanupFailures);\n\n if (outcome.kind === 'closed') {\n throw new ApplicationError('closed', 'Application closed before it was ready.');\n }\n\n if (this.state.closing && isCancellation(outcome.error)) throw outcome.error;\n\n throw combinedFailure([outcome.error, ...cleanupFailures], 'Application failed to open.');\n }\n\n private async start(): Promise<void> {\n await this.openApplicationFeatures();\n\n if (this.state.closing) throw new ApplicationError('closed', 'Application closed before it was ready.');\n\n try {\n this.installConditionGroups();\n } catch (error) {\n if (this.state.closing) {\n this.state.closeFailures.push(error);\n\n throw error;\n }\n\n this.state.closeFailures.push(...releaseSources(this.state.releases.splice(0)));\n const groupFailures = await closeConditionGroups(this.state.groups);\n const generations = this.state.applicationGenerations ?? [];\n this.state.applicationGenerations = undefined;\n\n const cleanupFailures = await retireGroupMembersReverse(generations);\n\n throw combinedFailure(\n [error, ...groupFailures, ...cleanupFailures],\n 'Application condition controls failed to open.',\n );\n }\n\n if (this.state.closing) throw new ApplicationError('closed', 'Application closed before it was ready.');\n }\n}\n\nfunction openApplication<const Features extends readonly AnyFeature[]>(\n application: Application<Features>,\n options: OpenApplicationOptions<Features>,\n): ApplicationExecution;\nfunction openApplication(application: Application, options: unknown): ApplicationExecution {\n const record = requireApplicationRecord(application);\n const snapshot = snapshotOpenOptions(options);\n const conditionReadables = new Map<Condition, Readable<boolean>>();\n const observation = createObservationSlot();\n const context: FeatureRuntimeContext = {\n activity: new ApplicationActivity(observation),\n cleanupFailure: snapshot.cleanupFailure,\n conditionReadables,\n generations: new Map(),\n importValues: indexImportValues(record.plan, snapshot.imports),\n observation,\n plan: record.plan,\n quarantines: new Map(),\n readyListeners: createReadyListeners(),\n readyValues: new Map(),\n reporter: record.reporter,\n retiring: new Map(),\n };\n const index = indexConditionSources(context, snapshot.conditions);\n const sources = index.sources;\n\n for (const [condition, readable] of index.readables) conditionReadables.set(condition as Condition, readable);\n const state: ApplicationExecutionState = {\n applicationGenerations: undefined,\n closeFailures: [],\n closing: false,\n groups: [],\n opening: [],\n releases: [],\n };\n const controller = new ApplicationExecutionController(context, sources, state);\n const execution = Object.freeze({ close: controller.close, ready: controller.ready });\n registerControllableApplication(execution, {\n activity: context.activity,\n closed: () => state.closing,\n conditions: index.controls,\n groups: state.groups,\n groupsOfCondition: groupsOfCondition(record.plan),\n instances: controllableInstances(record.plan),\n observation: context.observation,\n quarantines: context.quarantines,\n });\n registerObservableApplication(execution, {\n activity: context.activity,\n applicationId: record.id,\n conditionControls: index.controls,\n conditionReadables,\n importValues: context.importValues,\n liveInstances: context.generations,\n observation: context.observation,\n plan: record.plan,\n readyInstances: context.readyValues,\n reporter: record.reporter,\n });\n\n return execution;\n}\n\nexport { ApplicationError, bind, openApplication };\nexport type { ApplicationExecution, ApplicationImportBinding, OpenApplicationOptions };\n\nexport type { FeatureRuntimeContext };\n"],"names":["combinedFailure","failures","message","createAggregateError","indexImportValues","plan","bindings","inputs","binding","known","expected","values","missing","contract","createFeatureEntries","indexes","context","featureIndex","feature","openFeatureInstance","installConditionSource","source","publish","reporter","installing","published","revision","subscribed","emit","active","error","reportRuntimeFailure","release","closeConditionGroups","groups","group","attempt","openApplicationEntries","entries","state","fence","entry","generation","releaseSources","releases","ApplicationExecutionController","sources","resolveClose","rejectClose","closeAttempt","resolve","reject","generations","retireGroupMembersReverse","closeObservation","observation","order","createGroupOrder","controller","createConditionGroupExecution","condition","snapshot","_a","combineConditionSources","outcome","cleanupFailures","ApplicationError","isCancellation","groupFailures","openApplication","application","options","record","requireApplicationRecord","snapshotOpenOptions","conditionReadables","createObservationSlot","ApplicationActivity","createReadyListeners","index","indexConditionSources","readable","execution","registerControllableApplication","groupsOfCondition","controllableInstances","registerObservableApplication"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { CompiledApplicationFeatureIR } from './application-compiler.js';
2
+ import type { ReadyListeners } from './application-instance-retirement.js';
3
+ import type { FeatureReady } from './feature-generation.js';
4
+ /** What a weak edge reads at any moment: the live instance while it is open, nothing while it is not (D105). */
5
+ type WeakEdgeContext = {
6
+ readonly readyListeners: ReadyListeners;
7
+ readonly readyValues: ReadonlyMap<number, FeatureReady<unknown>>;
8
+ };
9
+ declare function resolveFeatureImports(feature: CompiledApplicationFeatureIR, importValues: ReadonlyMap<object, unknown>, context: WeakEdgeContext): Readonly<Record<string, unknown>>;
10
+ declare function resolveFeatureRequirements(feature: CompiledApplicationFeatureIR, context: WeakEdgeContext): Readonly<Record<string, unknown>>;
11
+ export { resolveFeatureImports, resolveFeatureRequirements };
@@ -0,0 +1,2 @@
1
+ import{CallError as m}from"@opetope/core";import{defineCallTarget as y,invokeCallTarget as v,bindPortRef as k}from"@opetope/core/internal";import{resolveFeatureReadyPort as f}from"./feature-port-binding.js";const a=Object.freeze({kind:"missing"});function c(i,e,t,n){const r=n.get(e.featureIndex);if(r===void 0)throw new TypeError(`Feature ${i.featureId} dependency ${t} provider ${e.featureId} is not ready.`);return r}function p(i,e){return i.kind==="missing"?e.kind==="missing":e.kind==="found"&&Object.is(i.value,e.value)}function b(i,e){let t=a,n=a,r;const o=new Set,s=()=>{const u=e===void 0?void 0:i.readyValues.get(e),d=u===void 0?a:{kind:"found",value:u.exports};return p(t,d)||(t=Object.freeze(d)),t},l=()=>{const u=s();if(!p(n,u)){n=u;for(const d of[...o])d()}};return Object.freeze({getSnapshot:s,subscribe:u=>{o.add(u),n=s(),o.size===1&&(r=i.readyListeners.add(e,l));let d=!0;return()=>{d&&(d=!1,o.delete(u),!(o.size>0)&&(r==null||r(),r=void 0))}}})}function q(i,e){const t=e.provider,n=y({id:`${e.consumerFeatureId}.${e.requirementKey}.weak`,run:(r,o)=>{const s=t===void 0?void 0:i.readyValues.get(t.featureIndex);if(s===void 0)throw new m("unavailable",`Feature requirement ${e.requirementKey} has no live provider.`);return v(f(s,e.port),r,o)}});return k(e.port,n)}function j(i,e){const t=i.has(e)?Object.freeze({kind:"found",value:i.get(e)}):a;return Object.freeze({getSnapshot:()=>t,subscribe:()=>()=>{}})}function K(i,e,t){const n={};for(const r of i.imports){if(r.optional){const o=r.provider;n[r.importKey]=r.provider.kind==="application-input"?j(e,r.contract):b(t,o.kind==="feature"?o.featureIndex:void 0);continue}if(r.provider.kind!=="feature"){n[r.importKey]=e.get(r.contract);continue}n[r.importKey]=c(i,r.provider,r.importKey,t.readyValues).exports}return Object.freeze(n)}function g(i,e){const t={};for(const n of i.requirements){if(n.optional){t[n.requirementKey]=q(e,n);continue}if(n.provider===void 0){t[n.requirementKey]=void 0;continue}const r=c(i,n.provider,n.requirementKey,e.readyValues);t[n.requirementKey]=f(r,n.port)}return Object.freeze(t)}export{K as resolveFeatureImports,g as resolveFeatureRequirements};
2
+ //# sourceMappingURL=application-feature-bindings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-feature-bindings.js","sources":["../src/application-feature-bindings.ts"],"sourcesContent":["import { CallError } from '@opetope/core';\nimport type { Lookup, Readable } from '@opetope/core';\nimport { bindPortRef, defineCallTarget, invokeCallTarget } from '@opetope/core/internal';\n\nimport type { CompiledApplicationFeatureIR } from './application-compiler';\nimport type { ReadyListeners } from './application-instance-retirement';\nimport type { FeatureReady } from './feature-generation';\nimport { resolveFeatureReadyPort } from './feature-port-binding';\n\n/** What a weak edge reads at any moment: the live instance while it is open, nothing while it is not (D105). */\ntype WeakEdgeContext = {\n readonly readyListeners: ReadyListeners;\n readonly readyValues: ReadonlyMap<number, FeatureReady<unknown>>;\n};\n\nconst missingLookup = Object.freeze({ kind: 'missing' } as const);\n\nfunction requireProviderReady(\n consumer: CompiledApplicationFeatureIR,\n provider: Readonly<{ featureId: string; featureIndex: number }>,\n key: string,\n readyValues: ReadonlyMap<number, FeatureReady<unknown>>,\n): FeatureReady<unknown> {\n const ready = readyValues.get(provider.featureIndex);\n\n if (ready === undefined) {\n throw new TypeError(`Feature ${consumer.featureId} dependency ${key} provider ${provider.featureId} is not ready.`);\n }\n\n return ready;\n}\n\nfunction sameLookup(left: Lookup<unknown>, right: Lookup<unknown>): boolean {\n if (left.kind === 'missing') return right.kind === 'missing';\n\n return right.kind === 'found' && Object.is(left.value, right.value);\n}\n\n/**\n * The projection of a weak import: one `Readable<Lookup<Exports>>` that reports `found` while the provider instance\n * is open and `missing` while it is closed or absent from the application. The consumer holds one subscription, the\n * proxy rebinds when the provider reopens and dies with the consumer (D105).\n *\n * One recompute per transaction, one notification to every subscriber. The projection keeps two facts apart on\n * purpose: `snapshot` is the identity `getSnapshot` hands out, and `published` is the value the subscribers were last\n * told about. Deriving \"did it change\" from `snapshot` alone would let the first reader — a listener or any\n * `getSnapshot` between transactions — consume the change and leave the rest of the subscribers silent.\n */\nfunction weakImportProjection(context: WeakEdgeContext, featureIndex: number | undefined): Readable<Lookup<unknown>> {\n let snapshot: Lookup<unknown> = missingLookup;\n let published: Lookup<unknown> = missingLookup;\n let release: (() => void) | undefined;\n const listeners = new Set<() => void>();\n const read = (): Lookup<unknown> => {\n const ready = featureIndex === undefined ? undefined : context.readyValues.get(featureIndex);\n const next: Lookup<unknown> = ready === undefined ? missingLookup : { kind: 'found', value: ready.exports };\n\n if (sameLookup(snapshot, next)) return snapshot;\n\n snapshot = Object.freeze(next);\n\n return snapshot;\n };\n const publish = (): void => {\n const next = read();\n\n if (sameLookup(published, next)) return;\n\n published = next;\n\n for (const listener of [...listeners]) listener();\n };\n\n return Object.freeze({\n getSnapshot: read,\n subscribe: (listener: () => void): (() => void) => {\n listeners.add(listener);\n published = read();\n\n // One registration per projection, on the provider's index: a transition of another feature is not news here.\n if (listeners.size === 1) release = context.readyListeners.add(featureIndex, publish);\n\n let subscribed = true;\n\n return (): void => {\n if (!subscribed) return;\n\n subscribed = false;\n listeners.delete(listener);\n\n if (listeners.size > 0) return;\n\n release?.();\n release = undefined;\n };\n },\n });\n}\n\n/** The projection of a weak port: the call settles `unavailable` while no provider instance is open (D105). */\nfunction weakRequirementCall(\n context: WeakEdgeContext,\n binding: CompiledApplicationFeatureIR['requirements'][number],\n): unknown {\n const provider = binding.provider;\n\n const target = defineCallTarget({\n id: `${binding.consumerFeatureId}.${binding.requirementKey}.weak`,\n run: (input: never, callContext) => {\n const ready = provider === undefined ? undefined : context.readyValues.get(provider.featureIndex);\n\n if (ready === undefined) {\n throw new CallError('unavailable', `Feature requirement ${binding.requirementKey} has no live provider.`);\n }\n\n return invokeCallTarget(resolveFeatureReadyPort(ready, binding.port), input, callContext);\n },\n });\n\n // The consumer still receives a `PortRef`: only the provider behind it may come and go (D105).\n return bindPortRef(binding.port as never, target);\n}\n\n/** A weak host input is either bound for the whole application lifetime or permanently missing (D105). */\nfunction weakInputProjection(importValues: ReadonlyMap<object, unknown>, contract: object): Readable<Lookup<unknown>> {\n const snapshot: Lookup<unknown> = importValues.has(contract)\n ? Object.freeze({ kind: 'found' as const, value: importValues.get(contract) })\n : missingLookup;\n\n return Object.freeze({ getSnapshot: () => snapshot, subscribe: () => () => undefined });\n}\n\nfunction resolveFeatureImports(\n feature: CompiledApplicationFeatureIR,\n importValues: ReadonlyMap<object, unknown>,\n context: WeakEdgeContext,\n): Readonly<Record<string, unknown>> {\n const imports: Record<string, unknown> = {};\n\n for (const binding of feature.imports) {\n if (binding.optional) {\n const provider = binding.provider;\n\n imports[binding.importKey] =\n binding.provider.kind === 'application-input'\n ? weakInputProjection(importValues, binding.contract)\n : weakImportProjection(context, provider.kind === 'feature' ? provider.featureIndex : undefined);\n\n continue;\n }\n\n if (binding.provider.kind !== 'feature') {\n imports[binding.importKey] = importValues.get(binding.contract);\n\n continue;\n }\n\n imports[binding.importKey] = requireProviderReady(\n feature,\n binding.provider,\n binding.importKey,\n context.readyValues,\n ).exports;\n }\n\n return Object.freeze(imports);\n}\n\nfunction resolveFeatureRequirements(\n feature: CompiledApplicationFeatureIR,\n context: WeakEdgeContext,\n): Readonly<Record<string, unknown>> {\n const requirements: Record<string, unknown> = {};\n\n for (const binding of feature.requirements) {\n if (binding.optional) {\n requirements[binding.requirementKey] = weakRequirementCall(context, binding);\n\n continue;\n }\n\n if (binding.provider === undefined) {\n requirements[binding.requirementKey] = undefined;\n\n continue;\n }\n\n const ready = requireProviderReady(feature, binding.provider, binding.requirementKey, context.readyValues);\n requirements[binding.requirementKey] = resolveFeatureReadyPort(ready, binding.port);\n }\n\n return Object.freeze(requirements);\n}\n\nexport { resolveFeatureImports, resolveFeatureRequirements };\n"],"names":["missingLookup","requireProviderReady","consumer","provider","key","readyValues","ready","sameLookup","left","right","weakImportProjection","context","featureIndex","snapshot","published","release","listeners","read","next","publish","listener","subscribed","weakRequirementCall","binding","target","defineCallTarget","input","callContext","CallError","invokeCallTarget","resolveFeatureReadyPort","bindPortRef","weakInputProjection","importValues","contract","resolveFeatureImports","feature","imports","resolveFeatureRequirements","requirements"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { CompiledApplicationFeatureIR } from './application-compiler.js';
2
+ import type { FeatureRuntimeContext } from './application-execution.js';
3
+ import type { GroupMemberInstance } from './condition-group-execution.js';
4
+ declare function openFeatureInstance(feature: CompiledApplicationFeatureIR, context: FeatureRuntimeContext): GroupMemberInstance;
5
+ export { openFeatureInstance };
@@ -0,0 +1,2 @@
1
+ import{resolveFeatureImports as l,resolveFeatureRequirements as u}from"./application-feature-bindings.js";import{notifyReadyListeners as h,trackRetirement as y}from"./application-instance-retirement.js";import{openFeatureWithActivity as f}from"./feature-generation.js";import{getLazyFeature as v}from"./feature-lazy.js";import{isCleanupRetryError as p}from"./instance-demand.js";import{fenceFeatureGeneration as g}from"./module-generation.js";class m{context;feature;generation;activity;ready;constructor(e,t,s,a){this.context=e,this.feature=t,this.generation=s,this.activity=a,this.ready=s.ready.then(r=>{var i;return e.generations.get(t.featureIndex)===this&&(a.phase="ready",e.readyValues.set(t.featureIndex,r),h(e,t.featureIndex),(i=e.observation.observer)==null||i.instanceReady(t.featureIndex)),r},r=>{var i;throw e.readyValues.delete(t.featureIndex),h(e,t.featureIndex),a.phase==="opening"&&(a.phase="failed"),(i=e.observation.observer)==null||i.instanceFailed(t.featureIndex,r,"open"),r})}fence=()=>{var e,t;this.activity.phase="fenced",g(this.generation),(t=(e=this.activity).changed)==null||t.call(e)};retire=()=>{var r;const{observation:e}=this.context,{featureIndex:t}=this.feature,s=this.context.generations.get(t)===this;s&&(this.context.generations.delete(t),this.context.readyValues.delete(t),h(this.context,t)),this.activity.phase="draining",(r=e.observer)==null||r.instanceRetiring(t);const a=this.generation.close().then(()=>{var i;this.context.activity.retire(t,this.activity),this.context.quarantines.delete(t),(i=e.observer)==null||i.instanceRetired(t)},i=>{var o;throw p(i)&&this.context.quarantines.set(t,i),this.activity.phase=p(i)?"quarantined":"failed",(o=e.observer)==null||o.instanceFailed(t,i,"close"),i});return s&&y(this.context,t,a),a}}function I(n,e){var c;if(e.generations.has(n.featureIndex))throw new TypeError(`Feature ${n.featureId} already has a live instance.`);const t=l(n,e.importValues,e),s=u(n,e),a=f,r=e.activity.open(n.featureIndex,n.featureId,()=>{const d=v(n.definition);return d===void 0?"eager":d.materialized!==void 0?"loaded":d.loading!==void 0?"loading":"not-loaded"});let i;try{i=a(n.definition,{cleanupFailure:e.cleanupFailure,imports:t,reporter:e.reporter,requirements:s},r)}catch(d){throw e.activity.retire(n.featureIndex,r),d}const o=new m(e,n,i,r);return e.generations.set(n.featureIndex,o),(c=e.observation.observer)==null||c.instanceOpening(n.featureIndex),o}export{I as openFeatureInstance};
2
+ //# sourceMappingURL=application-feature-instance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-feature-instance.js","sources":["../src/application-feature-instance.ts"],"sourcesContent":["import type { CompiledApplicationFeatureIR } from './application-compiler';\nimport type { FeatureRuntimeContext } from './application-execution';\nimport { resolveFeatureImports, resolveFeatureRequirements } from './application-feature-bindings';\nimport { notifyReadyListeners, trackRetirement } from './application-instance-retirement';\nimport type { GroupMemberInstance } from './condition-group-execution';\nimport type { AnyFeature } from './feature-authoring';\nimport { openFeatureWithActivity } from './feature-generation';\nimport type { FeatureReady } from './feature-generation';\nimport { getLazyFeature } from './feature-lazy';\nimport type { FeatureActivityOwner } from './inspection-activity';\nimport { isCleanupRetryError } from './instance-demand';\nimport { fenceFeatureGeneration } from './module-generation';\nimport type { FeatureInstance } from './module-generation';\n\nclass ApplicationFeatureInstance implements GroupMemberInstance {\n readonly ready: Promise<unknown>;\n\n constructor(\n private readonly context: FeatureRuntimeContext,\n private readonly feature: CompiledApplicationFeatureIR,\n private readonly generation: FeatureInstance<string, FeatureReady<unknown>>,\n private readonly activity: FeatureActivityOwner,\n ) {\n this.ready = generation.ready.then(\n value => {\n // Only the instance the application still holds is ready; a superseded one must not report `ready` after\n // Its own `fencing`, or the graph would regress.\n if (context.generations.get(feature.featureIndex) === this) {\n activity.phase = 'ready';\n context.readyValues.set(feature.featureIndex, value);\n notifyReadyListeners(context, feature.featureIndex);\n context.observation.observer?.instanceReady(feature.featureIndex);\n }\n\n return value;\n },\n (error: unknown) => {\n context.readyValues.delete(feature.featureIndex);\n notifyReadyListeners(context, feature.featureIndex);\n\n if (activity.phase === 'opening') activity.phase = 'failed';\n\n context.observation.observer?.instanceFailed(feature.featureIndex, error, 'open');\n\n throw error;\n },\n );\n }\n\n readonly fence = (): void => {\n this.activity.phase = 'fenced';\n fenceFeatureGeneration(this.generation);\n this.activity.changed?.();\n };\n\n readonly retire = (): Promise<void> => {\n const { observation } = this.context;\n const { featureIndex } = this.feature;\n const owned = this.context.generations.get(featureIndex) === this;\n\n if (owned) {\n this.context.generations.delete(featureIndex);\n this.context.readyValues.delete(featureIndex);\n notifyReadyListeners(this.context, featureIndex);\n }\n\n this.activity.phase = 'draining';\n observation.observer?.instanceRetiring(featureIndex);\n\n const attempt = this.generation.close().then(\n () => {\n this.context.activity.retire(featureIndex, this.activity);\n this.context.quarantines.delete(featureIndex);\n observation.observer?.instanceRetired(featureIndex);\n },\n (error: unknown) => {\n // A quarantined frontier is the one thing devtools may retry, so the exact error is kept (D176).\n if (isCleanupRetryError(error)) this.context.quarantines.set(featureIndex, error);\n\n this.activity.phase = isCleanupRetryError(error) ? 'quarantined' : 'failed';\n\n observation.observer?.instanceFailed(featureIndex, error, 'close');\n\n throw error;\n },\n );\n\n // §4: the retire has only started here, and a provider in another group waits for it to settle (D123).\n if (owned) trackRetirement(this.context, featureIndex, attempt);\n\n return attempt;\n };\n}\n\nfunction openFeatureInstance(\n feature: CompiledApplicationFeatureIR,\n context: FeatureRuntimeContext,\n): GroupMemberInstance {\n if (context.generations.has(feature.featureIndex)) {\n throw new TypeError(`Feature ${feature.featureId} already has a live instance.`);\n }\n\n const imports = resolveFeatureImports(feature, context.importValues, context);\n const requirements = resolveFeatureRequirements(feature, context);\n const open = openFeatureWithActivity as unknown as (\n definition: AnyFeature,\n options: Readonly<Record<string, unknown>>,\n activity: FeatureActivityOwner,\n ) => FeatureInstance<string, FeatureReady<unknown>>;\n const activity = context.activity.open(feature.featureIndex, feature.featureId, () => {\n const lazy = getLazyFeature(feature.definition);\n\n return lazy === undefined\n ? 'eager'\n : lazy.materialized !== undefined\n ? 'loaded'\n : lazy.loading !== undefined\n ? 'loading'\n : 'not-loaded';\n });\n let generation: FeatureInstance<string, FeatureReady<unknown>>;\n try {\n generation = open(\n feature.definition,\n {\n cleanupFailure: context.cleanupFailure,\n imports,\n reporter: context.reporter,\n requirements,\n },\n activity,\n );\n } catch (error) {\n context.activity.retire(feature.featureIndex, activity);\n throw error;\n }\n const execution = new ApplicationFeatureInstance(context, feature, generation, activity);\n context.generations.set(feature.featureIndex, execution);\n context.observation.observer?.instanceOpening(feature.featureIndex);\n\n return execution;\n}\n\nexport { openFeatureInstance };\n"],"names":["ApplicationFeatureInstance","context","feature","generation","activity","value","notifyReadyListeners","_a","error","fenceFeatureGeneration","_b","observation","featureIndex","owned","attempt","isCleanupRetryError","trackRetirement","openFeatureInstance","imports","resolveFeatureImports","requirements","resolveFeatureRequirements","open","openFeatureWithActivity","lazy","getLazyFeature","execution"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Order between condition groups. Inside one group the canonical order of `activationOrder` already opens a hard
3
+ * provider before its consumer, but two groups are two independent controllers, and the inclusion law of D93 lets a
4
+ * hard edge cross from a smaller condition set into a larger one. Without an order between the groups both wake on
5
+ * the same condition change and the consumer group opens its feature while the provider group is still opening its
6
+ * own — which is the law of §4 «провайдер открывается раньше потребителя» broken by a race, not by a design (D123).
7
+ *
8
+ * This module answers two questions per group and nothing else: whose readiness must this group wait for before it
9
+ * opens, and whose instances must be gone before it retires.
10
+ */
11
+ import type { CompiledApplicationPlanIR } from './application-compiler.js';
12
+ import type { ReadyListeners } from './application-instance-retirement.js';
13
+ import type { ConditionGroupOrder } from './condition-group-execution.js';
14
+ /** What the order needs from the running application: who is ready, who is still live, and when that changes. */
15
+ type GroupOrderContext = {
16
+ readonly generations: ReadonlyMap<number, unknown>;
17
+ readonly plan: CompiledApplicationPlanIR;
18
+ readonly readyListeners: ReadyListeners;
19
+ readonly readyValues: ReadonlyMap<number, unknown>;
20
+ readonly retiring: ReadonlyMap<number, unknown>;
21
+ };
22
+ /**
23
+ * The cross-group half of the activation order (D123). A group opens its members only after every hard provider it
24
+ * imports from another group is ready, and retires them only after every consumer in another group has finished
25
+ * closing — the start of a consumer's retire is not the end of it. A group with no crossing edge gets no order
26
+ * object at all, so it cannot pay for one.
27
+ */
28
+ declare function createGroupOrder(context: GroupOrderContext, groupId: string): ConditionGroupOrder | undefined;
29
+ export { createGroupOrder };
@@ -0,0 +1,2 @@
1
+ function w(r){const n=[];for(const e of r.imports)e.optional||e.provider.kind!=="feature"||n.push([e.provider.featureIndex,e.consumerFeatureIndex]);for(const e of r.requirements)e.optional||e.provider===void 0||n.push([e.provider.featureIndex,e.consumerFeatureIndex]);return n}function l(r){const n=new Map;for(const e of r.features)e.owner.kind==="group"&&n.set(e.featureIndex,e.owner.groupId);return n}function y(r,n,e){var d,i;const[o,t]=e,u=n.get(o),s=n.get(t);u===void 0||s===void 0||u===s||((d=r.get(s))==null||d.providers.add(o),(i=r.get(u))==null||i.consumers.add(t))}function I(r){const n=l(r),e=new Map;for(const o of r.groups)e.set(o.groupId,{consumers:new Set,providers:new Set});for(const o of w(r))y(e,n,o);return new Map([...e].map(([o,t])=>[o,Object.freeze({consumers:Object.freeze([...t.consumers]),providers:Object.freeze([...t.providers])})]))}const g=new WeakMap;function O(r){const n=g.get(r);if(n!==void 0)return n;const e=I(r);return g.set(r,e),e}function m(r,n,e,o){return n()||!e()?Promise.resolve():new Promise((t,u)=>{let s=!0,d=!1,i=()=>{},c=()=>{};const f=()=>{s=!1,i(),c()},a=()=>{s&&(f(),t())},p=()=>{if(d=!1,!!s)try{(n()||!e())&&a()}catch(v){f(),u(v)}},h=()=>{!s||d||(d=!0,queueMicrotask(p))};i=r.readyListeners.add(void 0,h),c=(o==null?void 0:o(a))??c,p()})}function E(r,n){const e=O(r.plan).get(n);if(!(e===void 0||e.consumers.length===0&&e.providers.length===0))return Object.freeze({awaitConsumersClosed:()=>m(r,()=>e.consumers.every(o=>!r.generations.has(o)&&!r.retiring.has(o)),()=>!0,void 0),awaitProvidersReady:(o,t)=>m(r,()=>e.providers.every(u=>r.readyValues.has(u)),o,t)})}export{E as createGroupOrder};
2
+ //# sourceMappingURL=application-group-order.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-group-order.js","sources":["../src/application-group-order.ts"],"sourcesContent":["/**\n * Order between condition groups. Inside one group the canonical order of `activationOrder` already opens a hard\n * provider before its consumer, but two groups are two independent controllers, and the inclusion law of D93 lets a\n * hard edge cross from a smaller condition set into a larger one. Without an order between the groups both wake on\n * the same condition change and the consumer group opens its feature while the provider group is still opening its\n * own — which is the law of §4 «провайдер открывается раньше потребителя» broken by a race, not by a design (D123).\n *\n * This module answers two questions per group and nothing else: whose readiness must this group wait for before it\n * opens, and whose instances must be gone before it retires.\n */\n\nimport type { CompiledApplicationPlanIR } from './application-compiler';\nimport type { ReadyListeners } from './application-instance-retirement';\nimport type { ConditionGroupOrder } from './condition-group-execution';\n\n/** What the order needs from the running application: who is ready, who is still live, and when that changes. */\ntype GroupOrderContext = {\n readonly generations: ReadonlyMap<number, unknown>;\n readonly plan: CompiledApplicationPlanIR;\n readonly readyListeners: ReadyListeners;\n readonly readyValues: ReadonlyMap<number, unknown>;\n readonly retiring: ReadonlyMap<number, unknown>;\n};\n\n/** What a group must wait for. Feature indexes, because that is what the ready and live maps are keyed by. */\ninterface GroupHardEdges {\n /** Consumers in other groups: their instances must be gone before this group retires its providers. */\n readonly consumers: readonly number[];\n /** Providers in other groups: they must be ready before this group opens its members. */\n readonly providers: readonly number[];\n}\n\n/**\n * Every hard edge of the plan as a provider → consumer pair of feature indexes. The predicate is deliberately the\n * same one `resolveFeatureImports` and `resolveFeatureRequirements` use to demand a ready provider: an edge that\n * needs a live provider at open is exactly an edge that needs an order.\n */\nfunction hardEdgePairs(plan: CompiledApplicationPlanIR): readonly (readonly [number, number])[] {\n const pairs: (readonly [number, number])[] = [];\n\n for (const binding of plan.imports) {\n if (binding.optional || binding.provider.kind !== 'feature') continue;\n\n pairs.push([binding.provider.featureIndex, binding.consumerFeatureIndex]);\n }\n\n for (const requirement of plan.requirements) {\n if (requirement.optional || requirement.provider === undefined) continue;\n\n pairs.push([requirement.provider.featureIndex, requirement.consumerFeatureIndex]);\n }\n\n return pairs;\n}\n\n/**\n * Which group owns each feature. An application-owned feature has no group: it opens before any group is installed\n * and retires after every group is closed, so no cross-group order can involve it.\n */\nfunction groupOfFeature(plan: CompiledApplicationPlanIR): ReadonlyMap<number, string> {\n const owners = new Map<number, string>();\n\n for (const feature of plan.features) {\n if (feature.owner.kind === 'group') owners.set(feature.featureIndex, feature.owner.groupId);\n }\n\n return owners;\n}\n\nfunction addCrossGroupEdge(\n edges: Map<string, { consumers: Set<number>; providers: Set<number> }>,\n owners: ReadonlyMap<number, string>,\n pair: readonly [number, number],\n): void {\n const [provider, consumer] = pair;\n const providerGroup = owners.get(provider);\n const consumerGroup = owners.get(consumer);\n\n if (providerGroup === undefined || consumerGroup === undefined || providerGroup === consumerGroup) return;\n\n edges.get(consumerGroup)?.providers.add(provider);\n edges.get(providerGroup)?.consumers.add(consumer);\n}\n\nfunction computeGroupHardEdges(plan: CompiledApplicationPlanIR): ReadonlyMap<string, GroupHardEdges> {\n const owners = groupOfFeature(plan);\n const edges = new Map<string, { consumers: Set<number>; providers: Set<number> }>();\n\n for (const group of plan.groups) edges.set(group.groupId, { consumers: new Set(), providers: new Set() });\n\n for (const pair of hardEdgePairs(plan)) addCrossGroupEdge(edges, owners, pair);\n\n return new Map(\n [...edges].map(([groupId, sets]) => [\n groupId,\n Object.freeze({ consumers: Object.freeze([...sets.consumers]), providers: Object.freeze([...sets.providers]) }),\n ]),\n );\n}\n\n/** One computation per compiled plan: the sets are derived from immutable topology, so they never change. */\nconst hardEdgesByPlan = new WeakMap<CompiledApplicationPlanIR, ReadonlyMap<string, GroupHardEdges>>();\n\nfunction groupHardEdges(plan: CompiledApplicationPlanIR): ReadonlyMap<string, GroupHardEdges> {\n const cached = hardEdgesByPlan.get(plan);\n\n if (cached !== undefined) return cached;\n\n const computed = computeGroupHardEdges(plan);\n hardEdgesByPlan.set(plan, computed);\n\n return computed;\n}\n\n/**\n * Waits for a fact the application republishes through `readyListeners`: an instance became ready, or an instance\n * retired. One registration per entire wait: unrelated transitions allocate no new promises or cancellation reactions.\n */\nfunction awaitReadyFact(\n context: GroupOrderContext,\n holds: () => boolean,\n keepWaiting: () => boolean,\n observeStale: ((wake: () => void) => () => void) | undefined,\n): Promise<void> {\n if (holds() || !keepWaiting()) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n let active = true;\n let scheduled = false;\n let releaseReady = (): void => undefined;\n let releaseStale = (): void => undefined;\n const stop = (): void => {\n active = false;\n releaseReady();\n releaseStale();\n };\n const finish = (): void => {\n if (!active) return;\n\n stop();\n resolve();\n };\n const check = (): void => {\n scheduled = false;\n\n if (!active) return;\n\n try {\n if (holds() || !keepWaiting()) finish();\n } catch (error) {\n stop();\n reject(error);\n }\n };\n const changed = (): void => {\n if (!active || scheduled) return;\n\n scheduled = true;\n // A retire publishes withdrawal before reserving its physical drain; read the settled transition.\n queueMicrotask(check);\n };\n releaseReady = context.readyListeners.add(undefined, changed);\n releaseStale = observeStale?.(finish) ?? releaseStale;\n check();\n });\n}\n\n/**\n * The cross-group half of the activation order (D123). A group opens its members only after every hard provider it\n * imports from another group is ready, and retires them only after every consumer in another group has finished\n * closing — the start of a consumer's retire is not the end of it. A group with no crossing edge gets no order\n * object at all, so it cannot pay for one.\n */\nfunction createGroupOrder(context: GroupOrderContext, groupId: string): ConditionGroupOrder | undefined {\n const edges = groupHardEdges(context.plan).get(groupId);\n\n if (edges === undefined || (edges.consumers.length === 0 && edges.providers.length === 0)) return undefined;\n\n return Object.freeze({\n awaitConsumersClosed: (): Promise<void> =>\n awaitReadyFact(\n context,\n () =>\n edges.consumers.every(\n featureIndex => !context.generations.has(featureIndex) && !context.retiring.has(featureIndex),\n ),\n () => true,\n undefined,\n ),\n awaitProvidersReady: (isCurrent: () => boolean, observeStale: (wake: () => void) => () => void): Promise<void> =>\n awaitReadyFact(\n context,\n () => edges.providers.every(featureIndex => context.readyValues.has(featureIndex)),\n isCurrent,\n observeStale,\n ),\n });\n}\n\nexport { createGroupOrder };\n"],"names":["hardEdgePairs","plan","pairs","binding","requirement","groupOfFeature","owners","feature","addCrossGroupEdge","edges","pair","_a","_b","provider","consumer","providerGroup","consumerGroup","computeGroupHardEdges","group","groupId","sets","hardEdgesByPlan","groupHardEdges","cached","computed","awaitReadyFact","context","holds","keepWaiting","observeStale","resolve","reject","active","scheduled","releaseReady","releaseStale","stop","finish","check","error","changed","createGroupOrder","featureIndex","isCurrent"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The two facts an application publishes about its live instances: that one became ready or went away, and that a
3
+ * retire has started and not settled yet.
4
+ *
5
+ * They live together because they are read together: §4 says a consumer closes before its provider, and leaving the
6
+ * live map marks the *start* of a retire, not its end. A group in another group's shadow therefore waits for the
7
+ * attempt to settle, and both edges of the attempt wake whoever is waiting (D123).
8
+ */
9
+ import type { ObservationSlot } from './inspection-observer.js';
10
+ import type { RuntimeErrorReporter } from './runtime-error-reporting.js';
11
+ /**
12
+ * Who listens for what. A weak projection and a `from` condition each care about one provider, so a ready or a
13
+ * retire notifies that provider's listeners and the few that asked for any change — instead of every listener in
14
+ * The application on every transition.
15
+ */
16
+ interface ReadyListeners {
17
+ readonly add: (featureIndex: number | undefined, listener: () => void) => () => void;
18
+ readonly notify: (featureIndex: number, reporter: RuntimeErrorReporter) => void;
19
+ }
20
+ type ReadyNotifier = {
21
+ readonly readyListeners: ReadyListeners;
22
+ readonly reporter: RuntimeErrorReporter;
23
+ };
24
+ type RetirementContext = ReadyNotifier & {
25
+ readonly retiring: Map<number, Promise<void>>;
26
+ };
27
+ declare function createReadyListeners(): ReadyListeners;
28
+ declare function notifyReadyListeners(context: ReadyNotifier, featureIndex: number): void;
29
+ declare function trackRetirement(context: RetirementContext, featureIndex: number, attempt: Promise<void>): void;
30
+ /**
31
+ * An application that closes takes its inspection session with it: the ring, the readables it pulled and the slot
32
+ * Entry all go, and a fresh session may attach to a closed application without tripping over the old one (D167).
33
+ */
34
+ declare function closeObservation(slot: ObservationSlot, reporter: RuntimeErrorReporter): void;
35
+ export { closeObservation, createReadyListeners, notifyReadyListeners, trackRetirement };
36
+ export type { ReadyListeners };
@@ -0,0 +1,2 @@
1
+ import{reportRuntimeFailure as s}from"./runtime-error-reporting.js";function f(){const e=new Set,r=new Map,i=(n,t)=>{if(n!==void 0)for(const o of[...n])try{o()}catch(d){s(d,t)}};return Object.freeze({add:(n,t)=>{if(n===void 0)return e.add(t),()=>{e.delete(t)};const o=r.get(n)??new Set;return o.add(t),r.set(n,o),()=>{o.delete(t),o.size===0&&r.delete(n)}},notify:(n,t)=>{i(r.get(n),t),i(e,t)}})}function c(e,r){e.readyListeners.notify(r,e.reporter)}function u(e,r,i){e.retiring.set(r,i);const n=()=>{e.retiring.get(r)===i&&e.retiring.delete(r),c(e,r)};i.then(n,n)}function y(e,r){e.completed=!0;const i=e.closeSession;if(i!==void 0){e.closeSession=void 0;try{i()}catch(n){s(n,r)}}}export{y as closeObservation,f as createReadyListeners,c as notifyReadyListeners,u as trackRetirement};
2
+ //# sourceMappingURL=application-instance-retirement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-instance-retirement.js","sources":["../src/application-instance-retirement.ts"],"sourcesContent":["/**\n * The two facts an application publishes about its live instances: that one became ready or went away, and that a\n * retire has started and not settled yet.\n *\n * They live together because they are read together: §4 says a consumer closes before its provider, and leaving the\n * live map marks the *start* of a retire, not its end. A group in another group's shadow therefore waits for the\n * attempt to settle, and both edges of the attempt wake whoever is waiting (D123).\n */\n\nimport type { ObservationSlot } from './inspection-observer';\nimport { reportRuntimeFailure } from './runtime-error-reporting';\nimport type { RuntimeErrorReporter } from './runtime-error-reporting';\n\n/**\n * Who listens for what. A weak projection and a `from` condition each care about one provider, so a ready or a\n * retire notifies that provider's listeners and the few that asked for any change — instead of every listener in\n * The application on every transition.\n */\ninterface ReadyListeners {\n readonly add: (featureIndex: number | undefined, listener: () => void) => () => void;\n readonly notify: (featureIndex: number, reporter: RuntimeErrorReporter) => void;\n}\n\ntype ReadyNotifier = {\n readonly readyListeners: ReadyListeners;\n readonly reporter: RuntimeErrorReporter;\n};\n\ntype RetirementContext = ReadyNotifier & { readonly retiring: Map<number, Promise<void>> };\n\nfunction createReadyListeners(): ReadyListeners {\n const anyFeature = new Set<() => void>();\n const byFeature = new Map<number, Set<() => void>>();\n\n /**\n * A consumer of a weak edge listens here, and D105 says a consumer's failure never alters the provider's\n * lifetime. A listener that throws is therefore reported and skipped instead of rejecting the provider's `ready`\n * Or its `retire()`.\n */\n const run = (listeners: Set<() => void> | undefined, reporter: RuntimeErrorReporter): void => {\n if (listeners === undefined) return;\n\n for (const listener of [...listeners]) {\n try {\n listener();\n } catch (error) {\n reportRuntimeFailure(error, reporter);\n }\n }\n };\n\n return Object.freeze({\n add: (featureIndex: number | undefined, listener: () => void): (() => void) => {\n if (featureIndex === undefined) {\n anyFeature.add(listener);\n\n return () => void anyFeature.delete(listener);\n }\n\n const listeners = byFeature.get(featureIndex) ?? new Set<() => void>();\n listeners.add(listener);\n byFeature.set(featureIndex, listeners);\n\n return () => {\n listeners.delete(listener);\n\n if (listeners.size === 0) byFeature.delete(featureIndex);\n };\n },\n notify: (featureIndex: number, reporter: RuntimeErrorReporter): void => {\n run(byFeature.get(featureIndex), reporter);\n run(anyFeature, reporter);\n },\n });\n}\n\nfunction notifyReadyListeners(context: ReadyNotifier, featureIndex: number): void {\n context.readyListeners.notify(featureIndex, context.reporter);\n}\n\nfunction trackRetirement(context: RetirementContext, featureIndex: number, attempt: Promise<void>): void {\n context.retiring.set(featureIndex, attempt);\n\n const settle = (): void => {\n if (context.retiring.get(featureIndex) === attempt) context.retiring.delete(featureIndex);\n\n notifyReadyListeners(context, featureIndex);\n };\n\n void attempt.then(settle, settle);\n}\n\n/**\n * An application that closes takes its inspection session with it: the ring, the readables it pulled and the slot\n * Entry all go, and a fresh session may attach to a closed application without tripping over the old one (D167).\n */\nfunction closeObservation(slot: ObservationSlot, reporter: RuntimeErrorReporter): void {\n slot.completed = true;\n const closeSession = slot.closeSession;\n\n if (closeSession === undefined) return;\n\n slot.closeSession = undefined;\n\n try {\n closeSession();\n } catch (error) {\n reportRuntimeFailure(error, reporter);\n }\n}\n\nexport { closeObservation, createReadyListeners, notifyReadyListeners, trackRetirement };\nexport type { ReadyListeners };\n"],"names":["createReadyListeners","anyFeature","byFeature","run","listeners","reporter","listener","error","reportRuntimeFailure","featureIndex","notifyReadyListeners","context","trackRetirement","attempt","settle","closeObservation","slot","closeSession"],"mappings":"oEA8BA,SAASA,GAAoB,CAC3B,MAAMC,EAAa,IAAI,IACjBC,EAAY,IAAI,IAOhBC,EAAM,CAACC,EAAwCC,IAAwC,CAC3F,GAAID,IAAc,OAElB,UAAWE,IAAY,CAAC,GAAGF,CAAS,EAClC,GAAI,CACFE,EAAQ,CACV,OAASC,EAAO,CACdC,EAAqBD,EAAOF,CAAQ,CACtC,CAEJ,EAEA,OAAO,OAAO,OAAO,CACnB,IAAK,CAACI,EAAkCH,IAAsC,CAC5E,GAAIG,IAAiB,OACnB,OAAAR,EAAW,IAAIK,CAAQ,EAEhB,KAAWL,EAAW,OAAOK,CAAQ,GAG9C,MAAMF,EAAYF,EAAU,IAAIO,CAAY,GAAK,IAAI,IACrD,OAAAL,EAAU,IAAIE,CAAQ,EACtBJ,EAAU,IAAIO,EAAcL,CAAS,EAE9B,IAAK,CACVA,EAAU,OAAOE,CAAQ,EAErBF,EAAU,OAAS,GAAGF,EAAU,OAAOO,CAAY,CACzD,CACF,EACA,OAAQ,CAACA,EAAsBJ,IAAwC,CACrEF,EAAID,EAAU,IAAIO,CAAY,EAAGJ,CAAQ,EACzCF,EAAIF,EAAYI,CAAQ,CAC1B,CACD,CAAA,CACH,CAEA,SAASK,EAAqBC,EAAwBF,EAAoB,CACxEE,EAAQ,eAAe,OAAOF,EAAcE,EAAQ,QAAQ,CAC9D,CAEA,SAASC,EAAgBD,EAA4BF,EAAsBI,EAAsB,CAC/FF,EAAQ,SAAS,IAAIF,EAAcI,CAAO,EAE1C,MAAMC,EAAS,IAAW,CACpBH,EAAQ,SAAS,IAAIF,CAAY,IAAMI,GAASF,EAAQ,SAAS,OAAOF,CAAY,EAExFC,EAAqBC,EAASF,CAAY,CAC5C,EAEKI,EAAQ,KAAKC,EAAQA,CAAM,CAClC,CAMA,SAASC,EAAiBC,EAAuBX,EAA8B,CAC7EW,EAAK,UAAY,GACjB,MAAMC,EAAeD,EAAK,aAE1B,GAAIC,IAAiB,OAErB,CAAAD,EAAK,aAAe,OAEpB,GAAI,CACFC,EAAY,CACd,OAASV,EAAO,CACdC,EAAqBD,EAAOF,CAAQ,CACtC,EACF"}