@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":"feature-definition-support.js","sources":["../src/feature-definition-support.ts"],"sourcesContent":["import { hasOwn } from '@opetope/core/internal';\n\nimport type { AnyFeature, FeatureExportDeclarationSnapshot, FeatureRecord } from './feature-authoring-types';\nimport { getDirectHostContract, importedContract } from './feature-contract';\nimport type { FeatureContractIdentity, FeatureImportRecord } from './feature-contract';\nimport { getRequiredPort } from './feature-port';\nimport type { PortRequirementRecord } from './feature-port';\nimport { assertPlainRecord, dataEntries } from './feature-record';\n\nconst featureDefinitions = new WeakMap<object, FeatureRecord>();\nconst featureExportFacadeContracts = new WeakMap<object, FeatureContractIdentity>();\n// D186: `body` is the loader of a split feature; it stands in place of `own`, `exports` and the provides factory.\nconst featureOptionKeys = new Set(['body', 'exports', 'id', 'imports', 'own', 'provides', 'requires', 'when']);\nconst requiredFeatureOptionKeys = new Set(['id']);\nconst featureIdSuffix = '.feature';\n\nfunction snapshotFeatureOptions(value: unknown): Readonly<Record<string, unknown>> {\n assertPlainRecord(value, 'defineFeature options');\n const entries = dataEntries(value, 'defineFeature options');\n\n for (const [key] of entries) {\n if (!featureOptionKeys.has(key)) throw new TypeError(`Unknown defineFeature option ${key}.`);\n }\n\n for (const key of requiredFeatureOptionKeys) {\n if (!hasOwn(value, key)) throw new TypeError(`defineFeature requires own ${key}.`);\n }\n\n const id = value['id'];\n\n if (typeof id === 'string' && id.endsWith(featureIdSuffix)) {\n throw new TypeError(`defineFeature id ${id} must not repeat the ${featureIdSuffix} suffix.`);\n }\n\n return Object.freeze(Object.fromEntries(entries));\n}\n\nfunction snapshotFeatureImports<Imports extends FeatureImportRecord>(value: Imports | undefined): Imports {\n const source = value ?? ({} as Imports);\n assertPlainRecord(source, 'defineFeature imports');\n const entries = dataEntries(source, 'defineFeature imports');\n\n const normalized = entries.map(([key, token]) => {\n const contract = importedContract(token);\n\n if (contract === undefined) {\n throw new TypeError(`defineFeature import ${key} is not a host import or a feature definition.`);\n }\n\n return [key, contract] as const;\n });\n\n return Object.freeze(Object.fromEntries(normalized)) as Imports;\n}\n\nfunction snapshotFeatureRequirements<Requires extends PortRequirementRecord>(value: Requires | undefined): Requires {\n const source = value ?? {};\n assertPlainRecord(source, 'defineFeature requires');\n const entries = dataEntries(source, 'defineFeature requires');\n\n for (const [key, requirement] of entries) {\n try {\n getRequiredPort(requirement);\n } catch {\n throw new TypeError(`defineFeature requirement ${key} is not an authentic required Port.`);\n }\n }\n\n return Object.freeze(Object.fromEntries(entries)) as Requires;\n}\n\nfunction requireFunction<Callable>(value: unknown, label: string): Callable {\n if (typeof value !== 'function') throw new TypeError(`${label} must be a function.`);\n\n return value as Callable;\n}\n\nfunction snapshotFeatureExportDeclaration(value: unknown): FeatureExportDeclarationSnapshot {\n if (value === undefined) return Object.freeze({ from: () => ({}) });\n\n return Object.freeze({\n from: requireFunction<(context: { readonly own: unknown }) => unknown>(value, 'defineFeature exports'),\n });\n}\n\nfunction requireFeatureRecord(feature: object): FeatureRecord {\n const record = featureDefinitions.get(feature);\n\n if (record === undefined) throw new TypeError('Feature is not authentic.');\n\n return record;\n}\n\nfunction registerFeatureRecord(feature: object, record: FeatureRecord): void {\n featureDefinitions.set(feature, record);\n}\n\nfunction isFeature(value: unknown): value is AnyFeature {\n return typeof value === 'object' && value !== null && featureDefinitions.has(value);\n}\n\nfunction registerFeatureExportFacade(\n contract: FeatureContractIdentity,\n facade: Readonly<Record<string, unknown>>,\n): void {\n featureExportFacadeContracts.set(facade, contract);\n}\n\nfunction isFeatureExportFacade(contract: FeatureContractIdentity, facade: unknown): boolean {\n return (\n typeof facade === 'object' &&\n facade !== null &&\n featureExportFacadeContracts.get(facade) === getDirectHostContract(contract)\n );\n}\n\nfunction allocateHiddenKey(keys: Set<string>, preferred: string): string {\n let candidate = preferred;\n let suffix = 2;\n\n while (keys.has(candidate)) candidate = `${preferred}${String(suffix++)}`;\n\n keys.add(candidate);\n\n return candidate;\n}\n\ntype RuntimeIdentityEntry = { count: number; readonly key: string };\n\nfunction indexRuntimeIdentities(\n runtimeEntries: readonly (readonly [string, unknown])[],\n): Readonly<WeakMap<object, RuntimeIdentityEntry>> {\n const identities = new WeakMap<object, RuntimeIdentityEntry>();\n\n for (const [key, value] of runtimeEntries) {\n if ((typeof value !== 'object' || value === null) && typeof value !== 'function') continue;\n\n const existing = identities.get(value);\n\n if (existing === undefined) identities.set(value, { count: 1, key });\n else existing.count += 1;\n }\n\n return identities;\n}\n\nfunction requireString<Literal extends string>(value: unknown, label: string): Literal {\n if (typeof value !== 'string') throw new TypeError(`${label} must be a string.`);\n\n return value as Literal;\n}\n\nfunction optionalFunction<Callable>(value: unknown, label: string): Callable | undefined {\n if (value === undefined) return undefined;\n\n return requireFunction<Callable>(value, label);\n}\n\nexport {\n allocateHiddenKey,\n indexRuntimeIdentities,\n isFeature,\n isFeatureExportFacade,\n optionalFunction,\n registerFeatureExportFacade,\n registerFeatureRecord,\n requireFeatureRecord,\n requireString,\n snapshotFeatureExportDeclaration,\n snapshotFeatureImports,\n snapshotFeatureOptions,\n snapshotFeatureRequirements,\n};\n"],"names":["featureDefinitions","featureExportFacadeContracts","featureOptionKeys","requiredFeatureOptionKeys","featureIdSuffix","snapshotFeatureOptions","value","assertPlainRecord","entries","dataEntries","key","hasOwn","id","snapshotFeatureImports","source","normalized","token","contract","importedContract","snapshotFeatureRequirements","requirement","getRequiredPort","requireFunction","label","snapshotFeatureExportDeclaration","requireFeatureRecord","feature","record","registerFeatureRecord","isFeature","registerFeatureExportFacade","facade","isFeatureExportFacade","getDirectHostContract","allocateHiddenKey","keys","preferred","candidate","suffix","indexRuntimeIdentities","runtimeEntries","identities","existing","requireString","optionalFunction"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Awaitable } from '@opetope/core/internal';
2
+ import type { FeatureAttachBuilder, FeatureEffectBuilder, FeatureImportRecord } from './feature-authoring-types.js';
3
+ import type { FeatureTimers } from './feature-timers.js';
4
+ import type { RuntimeErrorReporter } from './runtime-error-reporting.js';
5
+ type Disposer = () => Awaitable<void>;
6
+ type EffectRunContext = {
7
+ readonly current: unknown;
8
+ readonly previous: unknown;
9
+ readonly signal: AbortSignal;
10
+ readonly source: unknown;
11
+ readonly timers: FeatureTimers;
12
+ };
13
+ type EffectOptions = {
14
+ readonly from: object;
15
+ readonly onDispose: ((context: {
16
+ readonly source: unknown;
17
+ }) => Awaitable<void>) | undefined;
18
+ readonly run: (context: EffectRunContext) => Awaitable<Disposer | void>;
19
+ readonly when: ((current: unknown, previous: unknown) => boolean) | undefined;
20
+ };
21
+ declare function snapshotEffectOptions(value: unknown): EffectOptions;
22
+ declare function openEffect(options: EffectOptions, source: unknown, ownerSignal: AbortSignal, reporter: RuntimeErrorReporter | undefined): Disposer & {
23
+ readonly fence: () => void;
24
+ };
25
+ declare function createFeatureEffectBuilder<Id extends string, Imports extends FeatureImportRecord>(attach: FeatureAttachBuilder<Id, Imports>): FeatureEffectBuilder<Id, Imports>;
26
+ export { createFeatureEffectBuilder, openEffect, snapshotEffectOptions };
@@ -0,0 +1,2 @@
1
+ import{isReadable as A}from"@opetope/core/internal";import{getFeatureAttachmentReporter as L}from"./feature-attachment.js";import{assertPlainRecord as U,dataEntries as z}from"./feature-record.js";import{createTimerRegistry as B}from"./feature-timers.js";import{reportRuntimeFailure as K}from"./runtime-error-reporting.js";const S=new Set(["from","onDispose","run","when"]);function g(r,o){if(r!==void 0){if(typeof r!="function")throw new TypeError(`${o} must be a function.`);return r}}function G(r,o){const s=g(r,o);if(s===void 0)throw new TypeError(`${o} is required.`);return s}function O(r){U(r,"Feature effect options");const o=z(r,"Feature effect options"),s=new Set(o.map(([t])=>t));for(const t of s)if(!S.has(t))throw new TypeError(`Unknown feature effect option ${t}.`);for(const t of["from","run"])if(!s.has(t))throw new TypeError(`Feature effect requires own ${t}.`);const i=Object.fromEntries(o),f=i.from;if(typeof f!="object"||f===null)throw new TypeError("Feature effect from must be an import from this definition.");return Object.freeze({from:f,onDispose:g(i.onDispose,"Feature effect onDispose"),run:G(i.run,"Feature effect run"),when:g(i.when,"Feature effect when")})}function H(r){if(!A(r))throw new TypeError("Feature effect from must resolve to a Readable.")}function I(r,o,s){var i;try{return((i=r.when)==null?void 0:i.call(r,o.current,o.previous))??!0}catch(f){return s(f),!1}}async function J(r,o,s,i,f){try{const t=await r.run({current:o.current,previous:o.previous,signal:i.abort.signal,source:s,timers:i.timerRegistry.timers});if(t!==void 0&&typeof t!="function")throw new TypeError("Feature effect run must return void or a disposer function.");i.disposer=t===void 0?void 0:t}catch(t){i.abort.signal.aborted||f(t)}}function C(r,o,s,i){H(o);const f=o;let t=!0,a,m=Promise.resolve(),w=!1,h,d,b=!1;const u=e=>K(e,i),E=()=>{a==null||a.abort.abort()},F=async e=>{var c;const n=a;if(n!==void 0){n.abort.abort(),n.timerRegistry.close();try{await((c=n.disposer)==null?void 0:c.call(n))}catch(p){if(e)throw p;u(p)}a===n&&(a=void 0)}},D=e=>I(r,e,u)&&t;async function R(){try{for(;t&&d!==void 0;){const e=d;if(d=void 0,await F(!1),!t)break;if(!D(e))continue;const n=new AbortController,c=B(n.signal,i),p={abort:n,disposer:void 0,timerRegistry:c};a=p,await J(r,e,o,p,u)}}finally{b=!1,t&&d!==void 0&&(b=!0,m=R().catch(u))}}function j(){b||!t||(b=!0,m=R().catch(u))}const v=()=>{if(!t)return;let e;try{e=f.getSnapshot()}catch(c){u(c);return}if(w&&Object.is(e,h))return;const n=w?h:void 0;w=!0,h=e,d={current:e,previous:n},E(),j()},k=f.subscribe(v);if(typeof k!="function")throw new TypeError("Readable subscribe must return an unsubscribe function.");let T=!0,y=k;const P=async e=>{var n;if(T)try{await((n=r.onDispose)==null?void 0:n.call(r,{source:o})),T=!1}catch(c){e.push(c)}},$=async e=>{try{await F(!0)}catch(n){e.push(n)}},q=e=>{if(y!==void 0)try{y(),y=void 0}catch(n){e.push(n)}},l=()=>{t&&(t=!1,d=void 0,s.removeEventListener("abort",l),E())};return s.addEventListener("abort",l,{once:!0}),s.aborted&&l(),v(),Object.assign(async()=>{const e=[];l(),q(e),await m,await $(e),await P(e);for(const n of e.slice(1))u(n);if(e.length>0)throw e[0]},{fence:l})}function M(r){return(o=>{const s=O(o);return r(s.from,{open:i=>C(s,i.source,i.signal,L(i))})})}export{M as createFeatureEffectBuilder,C as openEffect,O as snapshotEffectOptions};
2
+ //# sourceMappingURL=feature-effect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-effect.js","sources":["../src/feature-effect.ts"],"sourcesContent":["import type { Readable } from '@opetope/core';\nimport type { Awaitable } from '@opetope/core/internal';\nimport { isReadable } from '@opetope/core/internal';\n\nimport { getFeatureAttachmentReporter } from './feature-attachment';\nimport type {\n FeatureAttachBuilder,\n FeatureEffectBuilder,\n FeatureImportRecord,\n FeatureImportRef,\n} from './feature-authoring-types';\nimport { assertPlainRecord, dataEntries } from './feature-record';\nimport { createTimerRegistry } from './feature-timers';\nimport type { FeatureTimers } from './feature-timers';\nimport { reportRuntimeFailure } from './runtime-error-reporting';\nimport type { RuntimeErrorReporter } from './runtime-error-reporting';\n\ntype Disposer = () => Awaitable<void>;\n\ntype EffectRunContext = {\n readonly current: unknown;\n readonly previous: unknown;\n readonly signal: AbortSignal;\n readonly source: unknown;\n readonly timers: FeatureTimers;\n};\n\ntype EffectOptions = {\n readonly from: object;\n readonly onDispose: ((context: { readonly source: unknown }) => Awaitable<void>) | undefined;\n readonly run: (context: EffectRunContext) => Awaitable<Disposer | void>;\n readonly when: ((current: unknown, previous: unknown) => boolean) | undefined;\n};\n\ntype EffectInput = { readonly current: unknown; readonly previous: unknown };\n\ntype EffectExecution = {\n readonly abort: AbortController;\n disposer: Disposer | undefined;\n readonly timerRegistry: ReturnType<typeof createTimerRegistry>;\n};\n\nconst effectOptionKeys = new Set(['from', 'onDispose', 'run', 'when']);\n\nfunction optionalCallback<Callback>(value: unknown, label: string): Callback | undefined {\n if (value === undefined) return undefined;\n\n if (typeof value !== 'function') throw new TypeError(`${label} must be a function.`);\n\n return value as Callback;\n}\n\nfunction requiredCallback<Callback>(value: unknown, label: string): Callback {\n const callback = optionalCallback<Callback>(value, label);\n\n if (callback === undefined) throw new TypeError(`${label} is required.`);\n\n return callback;\n}\n\nfunction snapshotEffectOptions(value: unknown): EffectOptions {\n assertPlainRecord(value, 'Feature effect options');\n const entries = dataEntries(value, 'Feature effect options');\n const keys = new Set(entries.map(([key]) => key));\n\n for (const key of keys) {\n if (!effectOptionKeys.has(key)) throw new TypeError(`Unknown feature effect option ${key}.`);\n }\n\n for (const key of ['from', 'run'] as const) {\n if (!keys.has(key)) throw new TypeError(`Feature effect requires own ${key}.`);\n }\n\n const source = Object.fromEntries(entries);\n const from = source['from'];\n\n if (typeof from !== 'object' || from === null) {\n throw new TypeError('Feature effect from must be an import from this definition.');\n }\n\n return Object.freeze({\n from,\n onDispose: optionalCallback<NonNullable<EffectOptions['onDispose']>>(\n source['onDispose'],\n 'Feature effect onDispose',\n ),\n run: requiredCallback<EffectOptions['run']>(source['run'], 'Feature effect run'),\n when: optionalCallback<NonNullable<EffectOptions['when']>>(source['when'], 'Feature effect when'),\n });\n}\n\nfunction assertReadable(value: unknown): asserts value is Readable<unknown> {\n if (!isReadable(value)) throw new TypeError('Feature effect from must resolve to a Readable.');\n}\n\nfunction effectConditionAllows(options: EffectOptions, input: EffectInput, report: (error: unknown) => void): boolean {\n try {\n return options.when?.(input.current, input.previous) ?? true;\n } catch (error) {\n report(error);\n\n return false;\n }\n}\n\nasync function invokeEffectRun(\n options: EffectOptions,\n input: EffectInput,\n source: unknown,\n execution: EffectExecution,\n report: (error: unknown) => void,\n): Promise<void> {\n try {\n const disposer = await options.run({\n current: input.current,\n previous: input.previous,\n signal: execution.abort.signal,\n source,\n timers: execution.timerRegistry.timers,\n });\n\n if (disposer !== undefined && typeof disposer !== 'function') {\n throw new TypeError('Feature effect run must return void or a disposer function.');\n }\n\n execution.disposer = disposer === undefined ? undefined : disposer;\n } catch (error) {\n if (!execution.abort.signal.aborted) report(error);\n }\n}\n\nfunction openEffect(\n options: EffectOptions,\n source: unknown,\n ownerSignal: AbortSignal,\n reporter: RuntimeErrorReporter | undefined,\n): Disposer & { readonly fence: () => void } {\n assertReadable(source);\n const readable = source;\n\n let active = true;\n let currentExecution: EffectExecution | undefined;\n let drain = Promise.resolve();\n let hasObserved = false;\n let lastObserved: unknown;\n let pending: EffectInput | undefined;\n let processing = false;\n\n const report = (error: unknown): void => reportRuntimeFailure(error, reporter);\n const abortCurrent = (): void => {\n currentExecution?.abort.abort();\n };\n const closeCurrent = async (workspace: boolean): Promise<void> => {\n const execution = currentExecution;\n\n if (execution === undefined) return;\n\n execution.abort.abort();\n execution.timerRegistry.close();\n\n try {\n await execution.disposer?.();\n } catch (error) {\n if (workspace) throw error;\n\n report(error);\n }\n\n if (currentExecution === execution) currentExecution = undefined;\n };\n /**\n * D194: `when` is foreign code, and the predicate of this very input may close the owner. A run started after that\n * fence would carry a signal nobody aborts, and the drain of the close would wait for it.\n */\n const admitsRun = (input: EffectInput): boolean => effectConditionAllows(options, input, report) && active;\n async function runLoop(): Promise<void> {\n try {\n while (active && pending !== undefined) {\n const input = pending;\n pending = undefined;\n await closeCurrent(false);\n\n if (!active) break;\n\n if (!admitsRun(input)) continue;\n\n const abort = new AbortController();\n const timerRegistry = createTimerRegistry(abort.signal, reporter);\n const execution: EffectExecution = { abort, disposer: undefined, timerRegistry };\n currentExecution = execution;\n await invokeEffectRun(options, input, source, execution, report);\n }\n } finally {\n processing = false;\n\n if (active && pending !== undefined) {\n processing = true;\n drain = runLoop().catch(report);\n }\n }\n }\n function startProcessing(): void {\n if (processing || !active) return;\n\n processing = true;\n drain = runLoop().catch(report);\n }\n const observe = (): void => {\n if (!active) return;\n\n let current: unknown;\n\n try {\n current = readable.getSnapshot();\n } catch (error) {\n report(error);\n\n return;\n }\n\n if (hasObserved && Object.is(current, lastObserved)) return;\n\n const previous = hasObserved ? lastObserved : undefined;\n hasObserved = true;\n lastObserved = current;\n pending = { current, previous };\n abortCurrent();\n startProcessing();\n };\n const unsubscribe = readable.subscribe(observe);\n\n if (typeof unsubscribe !== 'function') throw new TypeError('Readable subscribe must return an unsubscribe function.');\n\n let disposePending = true;\n let unsubscribePending: (() => void) | undefined = unsubscribe;\n const collectDisposeFailure = async (errors: unknown[]): Promise<void> => {\n if (!disposePending) return;\n\n try {\n await options.onDispose?.({ source });\n disposePending = false;\n } catch (error) {\n errors.push(error);\n }\n };\n const collectExecutionCleanupFailure = async (errors: unknown[]): Promise<void> => {\n try {\n await closeCurrent(true);\n } catch (error) {\n errors.push(error);\n }\n };\n const collectUnsubscribeFailure = (errors: unknown[]): void => {\n if (unsubscribePending === undefined) return;\n\n try {\n unsubscribePending();\n unsubscribePending = undefined;\n } catch (error) {\n errors.push(error);\n }\n };\n const fence = (): void => {\n if (!active) return;\n\n active = false;\n pending = undefined;\n ownerSignal.removeEventListener('abort', fence);\n abortCurrent();\n };\n ownerSignal.addEventListener('abort', fence, { once: true });\n\n if (ownerSignal.aborted) fence();\n\n observe();\n\n const dispose = async (): Promise<void> => {\n const errors: unknown[] = [];\n fence();\n\n collectUnsubscribeFailure(errors);\n await drain;\n await collectExecutionCleanupFailure(errors);\n await collectDisposeFailure(errors);\n\n for (const error of errors.slice(1)) report(error);\n\n if (errors.length > 0) throw errors[0];\n };\n\n return Object.assign(dispose, { fence });\n}\n\nfunction createFeatureEffectBuilder<Id extends string, Imports extends FeatureImportRecord>(\n attach: FeatureAttachBuilder<Id, Imports>,\n): FeatureEffectBuilder<Id, Imports> {\n return ((value: unknown) => {\n const options = snapshotEffectOptions(value);\n\n return attach(options.from as FeatureImportRef<unknown, Id, 'adapter-method'>, {\n open: context =>\n openEffect(options, context.source, context.signal, getFeatureAttachmentReporter(context as object)),\n });\n }) as FeatureEffectBuilder<Id, Imports>;\n}\n\nexport { createFeatureEffectBuilder, openEffect, snapshotEffectOptions };\n"],"names":["effectOptionKeys","optionalCallback","value","label","requiredCallback","callback","snapshotEffectOptions","assertPlainRecord","entries","dataEntries","keys","key","source","from","assertReadable","isReadable","effectConditionAllows","options","input","report","_a","error","invokeEffectRun","execution","disposer","openEffect","ownerSignal","reporter","readable","active","currentExecution","drain","hasObserved","lastObserved","pending","processing","reportRuntimeFailure","abortCurrent","closeCurrent","workspace","admitsRun","runLoop","abort","timerRegistry","createTimerRegistry","startProcessing","observe","current","previous","unsubscribe","disposePending","unsubscribePending","collectDisposeFailure","errors","collectExecutionCleanupFailure","collectUnsubscribeFailure","fence","createFeatureEffectBuilder","attach","context","getFeatureAttachmentReporter"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { Awaitable } from '@opetope/core/internal';
2
+ import type { FeatureAttachBuilder, FeatureEventBuilder, FeatureImportRecord } from './feature-authoring-types.js';
3
+ import type { FeatureTimers } from './feature-timers.js';
4
+ import type { LatestBackpressure } from './resource-types.js';
5
+ import type { RuntimeErrorReporter } from './runtime-error-reporting.js';
6
+ type Disposer = () => Awaitable<void>;
7
+ type EventOpenContext = {
8
+ readonly emit: (payload: unknown) => void;
9
+ readonly signal: AbortSignal;
10
+ readonly source: unknown;
11
+ readonly timers: FeatureTimers;
12
+ };
13
+ type EventRunContext = {
14
+ readonly payload: unknown;
15
+ readonly signal: AbortSignal;
16
+ readonly source: unknown;
17
+ };
18
+ type EventOptions = {
19
+ /** Absent is the default single slot; `latest()` replaces the payload that waits instead of dropping the new one. */
20
+ readonly backpressure: LatestBackpressure | undefined;
21
+ readonly from: object;
22
+ readonly run: (context: EventRunContext) => Awaitable<void>;
23
+ readonly subscribe: (context: EventOpenContext) => Awaitable<Disposer | void>;
24
+ };
25
+ type EventQueue = {
26
+ readonly close: () => Promise<void>;
27
+ readonly emit: (payload: unknown) => void;
28
+ };
29
+ declare function snapshotEventOptions(from: unknown, subscribe: unknown, value: unknown): EventOptions;
30
+ declare function createEventQueue(options: EventOptions, signal: AbortSignal, source: unknown, reporter: RuntimeErrorReporter | undefined): EventQueue;
31
+ declare function createFeatureEventBuilder<Id extends string, Imports extends FeatureImportRecord>(attach: FeatureAttachBuilder<Id, Imports>): FeatureEventBuilder<Id, Imports>;
32
+ export { createEventQueue, createFeatureEventBuilder, snapshotEventOptions };
@@ -0,0 +1,2 @@
1
+ import{getFeatureAttachmentReporter as b}from"./feature-attachment.js";import{assertPlainRecord as w,dataEntries as h}from"./feature-record.js";import{snapshotStreamBackpressure as E}from"./resource-policy.js";import{reportRuntimeFailure as F}from"./runtime-error-reporting.js";const g=new Set(["backpressure","run"]);async function k(t,o,i,r,a){try{if(await t({payload:o,signal:r,source:i})!==void 0)throw new TypeError("Feature event run must resolve to undefined.")}catch(e){r.aborted||a(e)}}function O(t){w(t,"Feature event options");const o=h(t,"Feature event options"),i=new Set(o.map(([r])=>r));if(!i.has("run")||[...i].some(r=>!g.has(r)))throw new TypeError("Feature event options require the run field and admit backpressure.");return Object.fromEntries(o)}function m(t,o,i){const r=O(i),a=r.run;if(typeof t!="object"||t===null)throw new TypeError("Feature event from must be an import from this definition.");if(typeof o!="function"||typeof a!="function")throw new TypeError("Feature event subscribe and run must be functions.");const e=r.backpressure===void 0?void 0:E(r.backpressure,"Feature event");return Object.freeze({backpressure:e,from:t,run:a,subscribe:o})}const j=Object.freeze({code:"queue-capacity",message:"Feature event queue capacity exceeded: the previous payload is still being processed.",name:"FeatureEventDrop"});function v(t,o,i,r){const{run:a}=t,e=t.backpressure!==void 0;let s=!0,u=!1,c,n=!1,f=Promise.resolve();const d=p=>F(p,r);async function l(){try{for(;s&&u;){const p=c;u=!1,c=void 0,await k(a,p,i,o,d)}}finally{n=!1,s&&u&&(n=!0,f=l().catch(d))}}function y(){n||!s||(n=!0,f=l().catch(d))}return Object.freeze({async close(){if(!s)return f;s=!1,u=!1,c=void 0,await f},emit:p=>{if(s){if(u&&!e){d(j);return}u=!0,c=p,y()}}})}function q(t){return((o,i,r)=>{const a=m(o,i,r);return t(a.from,{open:async e=>{const s=v(a,e.signal,e.source,b(e)),u=()=>{s.close()};let c;e.signal.addEventListener("abort",u,{once:!0});try{const n=await a.subscribe({emit:s.emit,signal:e.signal,source:e.source,timers:e.timers});if(n!==void 0&&typeof n!="function")throw new TypeError("Feature event subscribe must return void or a disposer function.");c=n===void 0?void 0:n}catch(n){throw e.signal.removeEventListener("abort",u),await s.close(),n}return async()=>{e.signal.removeEventListener("abort",u);const n=s.close();let f;try{await(c==null?void 0:c())}catch(d){f=[d]}if(await n,f!==void 0)throw f[0]}}})})}export{v as createEventQueue,q as createFeatureEventBuilder,m as snapshotEventOptions};
2
+ //# sourceMappingURL=feature-event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-event.js","sources":["../src/feature-event.ts"],"sourcesContent":["import type { Awaitable } from '@opetope/core/internal';\n\nimport { getFeatureAttachmentReporter } from './feature-attachment';\nimport type {\n FeatureAttachBuilder,\n FeatureEventBuilder,\n FeatureImportRecord,\n FeatureImportRef,\n} from './feature-authoring-types';\nimport { assertPlainRecord, dataEntries } from './feature-record';\nimport type { FeatureTimers } from './feature-timers';\nimport { snapshotStreamBackpressure } from './resource-policy';\nimport type { LatestBackpressure } from './resource-types';\nimport { reportRuntimeFailure } from './runtime-error-reporting';\nimport type { RuntimeErrorReporter } from './runtime-error-reporting';\n\ntype Disposer = () => Awaitable<void>;\n\ntype EventOpenContext = {\n readonly emit: (payload: unknown) => void;\n readonly signal: AbortSignal;\n readonly source: unknown;\n readonly timers: FeatureTimers;\n};\n\ntype EventRunContext = { readonly payload: unknown; readonly signal: AbortSignal; readonly source: unknown };\n\ntype EventOptions = {\n /** Absent is the default single slot; `latest()` replaces the payload that waits instead of dropping the new one. */\n readonly backpressure: LatestBackpressure | undefined;\n readonly from: object;\n readonly run: (context: EventRunContext) => Awaitable<void>;\n readonly subscribe: (context: EventOpenContext) => Awaitable<Disposer | void>;\n};\n\ntype EventQueue = { readonly close: () => Promise<void>; readonly emit: (payload: unknown) => void };\n\nconst eventOptionKeys = new Set(['backpressure', 'run']);\n\nasync function invokeEventRun(\n run: EventOptions['run'],\n payload: unknown,\n source: unknown,\n signal: AbortSignal,\n report: (error: unknown) => void,\n): Promise<void> {\n try {\n const result = await run({ payload, signal, source });\n\n if (result !== undefined) throw new TypeError('Feature event run must resolve to undefined.');\n } catch (error) {\n if (!signal.aborted) report(error);\n }\n}\n\n/** `run` is the option an event cannot do without; `backpressure` is the only other key it admits (D247). */\nfunction eventOptionRecord(value: unknown): Readonly<Record<string, unknown>> {\n assertPlainRecord(value, 'Feature event options');\n const entries = dataEntries(value, 'Feature event options');\n const keys = new Set(entries.map(([key]) => key));\n\n if (!keys.has('run') || [...keys].some(key => !eventOptionKeys.has(key))) {\n throw new TypeError('Feature event options require the run field and admit backpressure.');\n }\n\n return Object.fromEntries(entries);\n}\n\nfunction snapshotEventOptions(from: unknown, subscribe: unknown, value: unknown): EventOptions {\n const record = eventOptionRecord(value);\n const run = record['run'];\n\n if (typeof from !== 'object' || from === null) {\n throw new TypeError('Feature event from must be an import from this definition.');\n }\n\n if (typeof subscribe !== 'function' || typeof run !== 'function') {\n throw new TypeError('Feature event subscribe and run must be functions.');\n }\n\n const backpressure =\n record['backpressure'] === undefined\n ? undefined\n : snapshotStreamBackpressure(record['backpressure'], 'Feature event');\n\n return Object.freeze({ backpressure, from, run, subscribe }) as EventOptions;\n}\n\n/**\n * One record for every dropped emit of every queue: it carries no per-drop detail, so sharing it costs nothing and\n * Saves a stack capture on the hot path.\n */\nconst queueCapacityFailure = Object.freeze({\n code: 'queue-capacity' as const,\n message: 'Feature event queue capacity exceeded: the previous payload is still being processed.',\n name: 'FeatureEventDrop' as const,\n});\n\nfunction createEventQueue(\n options: EventOptions,\n signal: AbortSignal,\n source: unknown,\n reporter: RuntimeErrorReporter | undefined,\n): EventQueue {\n const { run } = options;\n // D247: `latest()` gives the waiting slot to the newest payload; without it the slot belongs to the payload that\n // reached it first, and the newer emit is dropped with a failure record.\n const replacesPending = options.backpressure !== undefined;\n let active = true;\n let pending = false;\n let pendingPayload: unknown;\n let processing = false;\n let drain = Promise.resolve();\n const report = (error: unknown): void => reportRuntimeFailure(error, reporter);\n async function process(): Promise<void> {\n try {\n while (active && pending) {\n const payload = pendingPayload;\n pending = false;\n pendingPayload = undefined;\n await invokeEventRun(run, payload, source, signal, report);\n }\n } finally {\n processing = false;\n\n if (active && pending) {\n processing = true;\n drain = process().catch(report);\n }\n }\n }\n function start(): void {\n if (processing || !active) return;\n\n processing = true;\n drain = process().catch(report);\n }\n const emit = (payload: unknown): void => {\n if (!active) return;\n\n if (pending && !replacesPending) {\n // A dropped emit is a failure record, not an exception: manufacturing an `Error` per drop allocates a stack\n // On the hot path, and a burst is exactly when that hurts.\n report(queueCapacityFailure);\n\n return;\n }\n\n pending = true;\n pendingPayload = payload;\n start();\n };\n\n return Object.freeze({\n async close(): Promise<void> {\n if (!active) return drain;\n\n active = false;\n pending = false;\n pendingPayload = undefined;\n\n await drain;\n },\n emit,\n });\n}\n\nfunction createFeatureEventBuilder<Id extends string, Imports extends FeatureImportRecord>(\n attach: FeatureAttachBuilder<Id, Imports>,\n): FeatureEventBuilder<Id, Imports> {\n return ((from: unknown, subscribe: unknown, value: unknown) => {\n const options = snapshotEventOptions(from, subscribe, value);\n\n return attach(options.from as FeatureImportRef<unknown, Id, 'adapter-method'>, {\n open: async context => {\n const queue = createEventQueue(options, context.signal, context.source, getFeatureAttachmentReporter(context));\n const abort = (): void => void queue.close();\n let disposer: Disposer | undefined;\n context.signal.addEventListener('abort', abort, { once: true });\n\n try {\n const opened = await options.subscribe({\n emit: queue.emit,\n signal: context.signal,\n source: context.source,\n timers: context.timers,\n });\n\n if (opened !== undefined && typeof opened !== 'function') {\n throw new TypeError('Feature event subscribe must return void or a disposer function.');\n }\n\n disposer = opened === undefined ? undefined : opened;\n } catch (error) {\n context.signal.removeEventListener('abort', abort);\n await queue.close();\n throw error;\n }\n\n return async () => {\n context.signal.removeEventListener('abort', abort);\n const closing = queue.close();\n let failure: readonly [unknown] | undefined;\n\n try {\n await disposer?.();\n } catch (error) {\n failure = [error];\n }\n\n await closing;\n\n if (failure !== undefined) throw failure[0];\n };\n },\n });\n }) as FeatureEventBuilder<Id, Imports>;\n}\n\nexport { createEventQueue, createFeatureEventBuilder, snapshotEventOptions };\n"],"names":["eventOptionKeys","invokeEventRun","run","payload","source","signal","report","error","eventOptionRecord","value","assertPlainRecord","entries","dataEntries","keys","key","snapshotEventOptions","from","subscribe","record","backpressure","snapshotStreamBackpressure","queueCapacityFailure","createEventQueue","options","reporter","replacesPending","active","pending","pendingPayload","processing","drain","reportRuntimeFailure","process","start","createFeatureEventBuilder","attach","context","queue","getFeatureAttachmentReporter","abort","disposer","opened","closing","failure"],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Feature, FeatureExportRecord, FeatureExportsOf, FeatureImportRecord, FeatureImportValues, FeatureOwnRecord } from './feature-authoring.js';
2
+ import type { PortRequirementRecord, RequiredPortValues } from './feature-port.js';
3
+ import type { ExactKeys } from './feature-record.js';
4
+ import type { FeatureActivityOwner } from './inspection-activity.js';
5
+ import type { FeatureInstance } from './module-generation.js';
6
+ import type { CleanupFailurePolicy, ErrorReporter } from './public-module.js';
7
+ interface FeatureReady<Exports> {
8
+ readonly exports: Exports;
9
+ }
10
+ type FeatureInstanceOf<Id extends string, Exports> = FeatureInstance<Id, FeatureReady<Exports>>;
11
+ type FeatureReadyOf<Definition> = FeatureReady<FeatureExportsOf<Definition>>;
12
+ type FeatureOpenBaseOptions<Imports> = {
13
+ readonly cleanupFailure?: CleanupFailurePolicy;
14
+ readonly imports: Imports;
15
+ readonly reporter: ErrorReporter;
16
+ };
17
+ type FeatureRequirementOpenOptions<Requirements, Keys extends PropertyKey> = keyof Requirements extends never ? {
18
+ readonly requirements?: ExactKeys<Keys, Requirements> & Requirements;
19
+ } : {
20
+ readonly requirements: ExactKeys<Keys, Requirements> & Requirements;
21
+ };
22
+ type ExactExpectedObjectMember<Actual, Expected> = Expected extends unknown ? Actual extends Expected ? keyof Actual extends keyof Expected ? true : never : never : never;
23
+ type ExactObjectUnionMember<Actual, Expected> = Actual extends unknown ? Actual extends readonly unknown[] ? true : Actual extends object ? true extends ExactExpectedObjectMember<Actual, Expected> ? true : false : true : never;
24
+ type NoExtraObjectKeys<Actual, Expected> = false extends ExactObjectUnionMember<Actual, Expected> ? never : unknown;
25
+ type ExactImportValueFields<Actual, Expected> = {
26
+ readonly [Key in keyof Expected]: Key extends keyof Actual ? NoExtraObjectKeys<Actual[Key], Expected[Key]> : never;
27
+ };
28
+ declare function openFeatureWithActivity(feature: Feature, options: unknown, activity?: FeatureActivityOwner): FeatureInstanceOf<string, unknown>;
29
+ declare function openFeatureInstance<const Id extends string, const Imports extends FeatureImportRecord, const Runtime extends FeatureOwnRecord<Id>, const Exports extends FeatureExportRecord, const Requires extends PortRequirementRecord, const ImportValues extends FeatureImportValues<Imports>, const OptionKeys extends PropertyKey, const ImportKeys extends PropertyKey, const RequirementKeys extends PropertyKey>(feature: Feature<Id, Imports, Runtime, Exports, Requires>, options: ExactKeys<OptionKeys, FeatureOpenBaseOptions<FeatureImportValues<Imports>> & FeatureRequirementOpenOptions<RequiredPortValues<Requires>, keyof RequiredPortValues<Requires>>> & FeatureOpenBaseOptions<ImportValues> & FeatureRequirementOpenOptions<RequiredPortValues<Requires>, RequirementKeys> & {
30
+ readonly imports: ExactImportValueFields<ImportValues, FeatureImportValues<Imports>> & ExactKeys<ImportKeys, FeatureImportValues<Imports>>;
31
+ }): FeatureInstanceOf<Id, Exports>;
32
+ declare const openFeature: typeof openFeatureInstance;
33
+ export { openFeature, openFeatureWithActivity };
34
+ export type { FeatureReady, FeatureReadyOf };
@@ -0,0 +1,2 @@
1
+ import{FeatureError as m}from"@opetope/core";import{hasOwn as x,withdrawContributions as T,isCallTarget as M}from"@opetope/core/internal";import{isLifetimeAttachmentRef as k}from"./attachment-declaration.js";import{registerFeatureInstanceImports as D,registerFeatureInstanceCallTargets as G}from"./feature-call-authority.js";import{createFeatureContributionPublication as K}from"./feature-contribution.js";import{contributionModel as $}from"./feature-contribution-model.js";import{requireFeatureRecord as V,registerFeatureExportFacade as A}from"./feature-definition-support.js";import{getLazyFeature as L}from"./feature-lazy.js";import{openLazyFeatureInstance as Q}from"./feature-lazy-generation.js";import{isFeatureMaterializationRef as U,bindFeatureResource as W}from"./feature-materialization-binding.js";import{materializeFeatureData as B,readInstanceModels as H,registerFeatureDataScope as J,isFeatureDataRef as N,requireFeatureDataScope as X}from"./feature-model.js";import{assertRequiredPortValue as Y,isOptionalPortRequirement as Z,getRequiredPort as _}from"./feature-port.js";import{bindFeatureReadyPorts as v}from"./feature-port-binding.js";import{assertPlainRecord as l,dataEntries as F}from"./feature-record.js";import{registerModuleActivity as ee}from"./inspection-module-activity.js";import{carryFeatureGenerationFence as re,openFeature as ne,notReadyGeneration as te,quarantinedGeneration as oe}from"./module-generation.js";import{registerModuleRetirementParticipant as g,resolveModuleCallTarget as ie}from"./public-module-instance.js";const ue=new Set(["cleanupFailure","imports","reporter","requirements"]);function ae(e,r){return{authority:void 0,context:void 0,contributions:r,evaluation:void 0,featureId:e,inactive:!1,inactiveReason:void 0,publication:void 0,retirement:void 0,retirementCause:void 0}}function R(e){return e.inactiveReason??(e.inactiveReason=new m("retired",e.featureId))}function p(e){e.context=void 0,e.evaluation=void 0}function E(e){if(e.inactive=!0,p(e),e.authority=void 0,e.publication===void 0)return;const r=e.publication;T(r),e.publication=void 0}function ce(e,r,n,t){e.context=r,e.evaluation=t,g(n,{fence:()=>E(e)})}function b(e){return e instanceof m&&e.code==="quarantined"&&e.retryCleanup!==void 0}function w(e,r){let n;return oe(e.declarationId,e.failures,r,()=>(n!==void 0||(n=(async()=>{try{await e.retryCleanup()}catch(t){throw b(t)?w(t,r):t}})()),n))}function j(e,r,n){if(e.retirementCause??(e.retirementCause=n),e.retirement!==void 0)return e.retirement;let t=()=>{},o=()=>{};const u=new Promise((i,a)=>{t=a,o=i});e.retirement=u;try{r.close().then(o,i=>{e.retirement===u&&(e.retirement=void 0),t(e.retirementCause!==void 0&&b(i)?w(i,e.retirementCause):i)})}catch(i){e.retirement=void 0,t(e.retirementCause!==void 0&&b(i)?w(i,e.retirementCause):i)}return u}function pe(e){const r=e.context;if(r===void 0)throw new TypeError("Feature contribution context is unavailable.");return{context:r}}function fe(e,r){e.publication=r,e.inactive&&E(e)}async function se(e,r,n){try{if(e.inactive)throw R(e);const{context:t}=pe(e);if(K(e.contributions,t,o=>{fe(e,o)},e.authority,e.evaluation),p(e),e.inactive)throw R(e);return n}catch(t){p(e);const o=t instanceof m&&t.code==="retired"?t:te(e.featureId,Object.freeze({cause:t,phase:"open",stage:"prepare"}));throw await j(e,r,o),o}}function de(e,r){const n=r.ready.then(t=>se(e,r,t),t=>{throw p(e),t});return{close:()=>j(e,r),instance:r.instance,ready:n}}function z(e,r,n){l(e,n);const t=F(e,n);if(t.length!==r.length||r.some(o=>!x(e,o)))throw new TypeError(`${n} must contain every exact field once.`);return Object.freeze(Object.fromEntries(t))}function me(e){if(e!==void 0&&e!=="report"&&e!=="quarantine")throw new TypeError('openFeature cleanupFailure must be "report" or "quarantine".')}function le(e,r,n){var t;for(const o of r.requirementKeys){const u=(t=e.requires)==null?void 0:t[o];Y(_(u),n[o],o,Z(u))}}function Fe(e,r,n){l(n,"openFeature options");const t=F(n,"openFeature options");for(const[i]of t)if(!ue.has(i))throw new TypeError(`Unknown openFeature option ${i}.`);for(const i of["imports","reporter"])if(!x(n,i))throw new TypeError(`openFeature requires ${i}.`);if(typeof n.reporter!="function")throw new TypeError("openFeature reporter must be a function.");me(n.cleanupFailure);const o=z(n.imports,r.importKeys,"openFeature imports"),u=z(n.requirements??{},r.requirementKeys,"openFeature requirements");return le(e,r,u),Object.freeze({...Object.fromEntries(t),imports:o,requirements:u})}function be(e,r,n){if(N(n))return X(e).read(n);if(U(n))return W(e,n);if(k(n))throw new TypeError(`own.${r} is a lifecycle binding, so exports has no value to take from it.`);return ie(e,n)}function we(e,r){const n=new Map,t={};for(const[o,u]of Object.entries(r))Object.defineProperty(t,o,{enumerable:!0,get:()=>(n.has(o)||n.set(o,be(e,o,u)),n.get(o))});return Object.freeze(t)}function he(e,r){if(M(r))return r;const n=r;if(typeof(n==null?void 0:n.getSnapshot)=="function"&&typeof n.subscribe=="function")return r;throw new TypeError(`Feature export ${e} must be a Call, a Readable or a Resource.`)}function ye(e,r,n){const t=n({own:r}),o=t===r?Object.fromEntries(Object.keys(r).map(i=>[i,r[i]])):t;l(o,"defineFeature exports result");const u=Object.freeze(Object.fromEntries(F(o,"defineFeature exports result").map(([i,a])=>[i,he(i,a)])));return A(e,u),u}function Ce(e,r,n){const t=n.imports;D(e.imports,t);const o=n.requirements;G(o);const u={};return r.importScopeKey!==void 0&&(u[r.importScopeKey]=t),r.requirementScopeKey!==void 0&&(u[r.requirementScopeKey]=o),Object.freeze({imports:t,lowScopes:Object.freeze(u)})}function I(e,r,n,t){var y;(y=t==null?void 0:t.changed)==null||y.call(t);const{imports:o,lowScopes:u}=Ce(e,r,n),i=n.reporter,a=r.contributions.length===0?void 0:ae(e.id,r.contributions),f=ne(r.module,{...n.cleanupFailure===void 0?{}:{cleanupFailure:n.cleanupFailure},prepare:c=>{t!==void 0&&ee(c,t),r.dataDescriptors.length>0&&B(e.id,r.dataDescriptors,o,c,i,d=>{J(c,d),g(c,{fence:()=>{d.fence();const O=d.drain();return{drain:()=>O,retry:()=>O}}})},t);const C=we(c,e.own),s=ye(e.exports,C,r.exports),S=Object.freeze({exports:s,imports:o,instance:c,model:$,own:e.own});a!==void 0&&(ce(a,S,c,{exports:s,imports:o,own:C}),a.authority=Object.freeze({models:H(c,r.dataDescriptors)}));const q=Object.freeze({exports:s});return v(q,r.providers,c),q},reporter:i,scopes:u});if(a===void 0)return f;const h=de(a,f);return re(f,h),h}function P(e,r,n){const t=V(e),o=Fe(e,t,r);return L(e)===void 0?I(e,t,o,n):Q(e,u=>I(e,u,o,n))}function qe(e,r){return P(e,r)}const Oe=qe;export{Oe as openFeature,P as openFeatureWithActivity};
2
+ //# sourceMappingURL=feature-generation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-generation.js","sources":["../src/feature-generation.ts"],"sourcesContent":["import { FeatureError } from '@opetope/core';\nimport type { DeclarationId, Readable } from '@opetope/core';\nimport { hasOwn, isCallTarget, withdrawContributions } from '@opetope/core/internal';\nimport type { ContributionPublication } from '@opetope/core/internal';\n\nimport { isLifetimeAttachmentRef } from './attachment-declaration';\nimport type {\n AnyFeature,\n Feature,\n FeatureExportRecord,\n FeatureExportsOf,\n FeatureImportRecord,\n FeatureImportValues,\n FeatureOwnRecord,\n} from './feature-authoring';\nimport { registerFeatureInstanceCallTargets, registerFeatureInstanceImports } from './feature-call-authority';\nimport type { FeatureContractIdentity } from './feature-contract';\nimport { createFeatureContributionPublication } from './feature-contribution';\nimport type { FeatureContributionDescriptor, FeatureEvaluationValues } from './feature-contribution';\nimport { contributionModel } from './feature-contribution-model';\nimport { registerFeatureExportFacade, requireFeatureRecord } from './feature-definition-support';\nimport { getLazyFeature } from './feature-lazy';\nimport { openLazyFeatureInstance } from './feature-lazy-generation';\nimport { bindFeatureResource, isFeatureMaterializationRef } from './feature-materialization-binding';\nimport {\n isFeatureDataRef,\n materializeFeatureData,\n readInstanceModels,\n registerFeatureDataScope,\n requireFeatureDataScope,\n} from './feature-model';\nimport { assertRequiredPortValue, getRequiredPort, isOptionalPortRequirement } from './feature-port';\nimport type { PortRequirementRecord, RequiredPortValues } from './feature-port';\nimport { bindFeatureReadyPorts } from './feature-port-binding';\nimport { assertPlainRecord, dataEntries } from './feature-record';\nimport type { ExactKeys } from './feature-record';\nimport type { FeatureActivityOwner } from './inspection-activity';\nimport { registerModuleActivity } from './inspection-module-activity';\nimport {\n carryFeatureGenerationFence,\n notReadyGeneration,\n openFeature as openLoweredModuleInstance,\n quarantinedGeneration,\n} from './module-generation';\nimport type { FeatureInstance } from './module-generation';\nimport { registerModuleRetirementParticipant, resolveModuleCallTarget } from './public-module';\nimport type { CleanupFailurePolicy, ErrorReporter, ModuleCallRef, ModuleInstanceRef } from './public-module';\n\ninterface FeatureReady<Exports> {\n readonly exports: Exports;\n}\n\ntype FeatureInstanceOf<Id extends string, Exports> = FeatureInstance<Id, FeatureReady<Exports>>;\n\ntype FeatureReadyOf<Definition> = FeatureReady<FeatureExportsOf<Definition>>;\n\ntype FeatureContributionInstanceState = {\n authority: unknown;\n context: unknown | undefined;\n readonly contributions: readonly FeatureContributionDescriptor[];\n evaluation: FeatureEvaluationValues | undefined;\n readonly featureId: DeclarationId;\n inactive: boolean;\n inactiveReason: FeatureError | undefined;\n publication: ContributionPublication | undefined;\n retirement: Promise<void> | undefined;\n retirementCause: FeatureError | undefined;\n};\n\ntype FeatureOpenBaseOptions<Imports> = {\n readonly cleanupFailure?: CleanupFailurePolicy;\n readonly imports: Imports;\n readonly reporter: ErrorReporter;\n};\n\ntype FeatureRequirementOpenOptions<Requirements, Keys extends PropertyKey> = keyof Requirements extends never\n ? { readonly requirements?: ExactKeys<Keys, Requirements> & Requirements }\n : { readonly requirements: ExactKeys<Keys, Requirements> & Requirements };\n\ntype ExactExpectedObjectMember<Actual, Expected> = Expected extends unknown\n ? Actual extends Expected\n ? keyof Actual extends keyof Expected\n ? true\n : never\n : never\n : never;\ntype ExactObjectUnionMember<Actual, Expected> = Actual extends unknown\n ? Actual extends readonly unknown[]\n ? true\n : Actual extends object\n ? true extends ExactExpectedObjectMember<Actual, Expected>\n ? true\n : false\n : true\n : never;\ntype NoExtraObjectKeys<Actual, Expected> = false extends ExactObjectUnionMember<Actual, Expected> ? never : unknown;\ntype ExactImportValueFields<Actual, Expected> = {\n readonly [Key in keyof Expected]: Key extends keyof Actual ? NoExtraObjectKeys<Actual[Key], Expected[Key]> : never;\n};\n\nconst featureOpenOptionKeys = new Set(['cleanupFailure', 'imports', 'reporter', 'requirements']);\n\nfunction createFeatureContributionInstanceState(\n featureId: DeclarationId,\n contributions: readonly FeatureContributionDescriptor[],\n): FeatureContributionInstanceState {\n return {\n authority: undefined,\n context: undefined,\n contributions,\n evaluation: undefined,\n featureId,\n inactive: false,\n inactiveReason: undefined,\n publication: undefined,\n retirement: undefined,\n retirementCause: undefined,\n };\n}\n\nfunction featureContributionInactiveReason(state: FeatureContributionInstanceState): FeatureError {\n return (state.inactiveReason ??= new FeatureError('retired', state.featureId));\n}\n\nfunction releaseFeatureContributionPreparation(state: FeatureContributionInstanceState): void {\n state.context = undefined;\n state.evaluation = undefined;\n}\n\nfunction fenceFeatureContributions(state: FeatureContributionInstanceState): void {\n state.inactive = true;\n releaseFeatureContributionPreparation(state);\n // The authority a mount was granted dies with the fence: keeping it alive would outlive the models it names.\n state.authority = undefined;\n\n if (state.publication === undefined) return;\n\n const publication = state.publication;\n withdrawContributions(publication);\n state.publication = undefined;\n}\n\nfunction prepareFeatureContributions(\n state: FeatureContributionInstanceState,\n context: unknown,\n instance: ModuleInstanceRef,\n evaluation: FeatureEvaluationValues,\n): void {\n state.context = context;\n state.evaluation = evaluation;\n registerModuleRetirementParticipant(instance, { fence: () => fenceFeatureContributions(state) });\n}\n\nfunction isQuarantinedGeneration(\n error: unknown,\n): error is FeatureError & { readonly retryCleanup: () => Promise<void> } {\n return error instanceof FeatureError && error.code === 'quarantined' && error.retryCleanup !== undefined;\n}\n\nfunction preserveFeatureContributionQuarantine(\n error: FeatureError & { readonly retryCleanup: () => Promise<void> },\n cause: FeatureError,\n): FeatureError {\n let recovery: Promise<void> | undefined;\n\n return quarantinedGeneration(error.declarationId, error.failures, cause, () => {\n if (recovery !== undefined) return recovery;\n\n recovery = (async () => {\n try {\n await error.retryCleanup();\n } catch (nextError) {\n if (isQuarantinedGeneration(nextError)) {\n throw preserveFeatureContributionQuarantine(nextError, cause);\n }\n\n throw nextError;\n }\n })();\n\n return recovery;\n });\n}\n\nfunction retireFeatureContributionGeneration(\n state: FeatureContributionInstanceState,\n generation: FeatureInstance<string, unknown>,\n cause?: FeatureError,\n): Promise<void> {\n state.retirementCause ??= cause;\n\n if (state.retirement !== undefined) return state.retirement;\n\n let rejectRetirement: (cause: unknown) => void = () => undefined;\n let resolveRetirement: () => void = () => undefined;\n const pending = new Promise<void>((resolve, reject) => {\n rejectRetirement = reject;\n resolveRetirement = resolve;\n });\n state.retirement = pending;\n\n try {\n void generation.close().then(resolveRetirement, (retirementCause: unknown) => {\n if (state.retirement === pending) state.retirement = undefined;\n\n rejectRetirement(\n state.retirementCause !== undefined && isQuarantinedGeneration(retirementCause)\n ? preserveFeatureContributionQuarantine(retirementCause, state.retirementCause)\n : retirementCause,\n );\n });\n } catch (retirementCause) {\n state.retirement = undefined;\n rejectRetirement(\n state.retirementCause !== undefined && isQuarantinedGeneration(retirementCause)\n ? preserveFeatureContributionQuarantine(retirementCause, state.retirementCause)\n : retirementCause,\n );\n }\n\n return pending;\n}\n\nfunction requireFeatureContributionPreparation(state: FeatureContributionInstanceState): {\n readonly context: unknown;\n} {\n const context = state.context;\n\n if (context === undefined) throw new TypeError('Feature contribution context is unavailable.');\n\n return { context };\n}\n\nfunction captureFeatureContributionPublication(\n state: FeatureContributionInstanceState,\n publication: ContributionPublication,\n): void {\n state.publication = publication;\n\n if (state.inactive) fenceFeatureContributions(state);\n}\n\nasync function publishFeatureContributions<Prepared>(\n state: FeatureContributionInstanceState,\n generation: FeatureInstance<string, unknown>,\n value: Prepared,\n): Promise<Prepared> {\n try {\n if (state.inactive) throw featureContributionInactiveReason(state);\n\n const { context } = requireFeatureContributionPreparation(state);\n createFeatureContributionPublication(\n state.contributions,\n context,\n publication => {\n captureFeatureContributionPublication(state, publication);\n },\n state.authority,\n state.evaluation,\n );\n releaseFeatureContributionPreparation(state);\n\n if (state.inactive) throw featureContributionInactiveReason(state);\n\n return value;\n } catch (cause) {\n releaseFeatureContributionPreparation(state);\n const failure =\n cause instanceof FeatureError && cause.code === 'retired'\n ? cause\n : notReadyGeneration(\n state.featureId,\n Object.freeze({ cause, phase: 'open' as const, stage: 'prepare' as const }),\n );\n await retireFeatureContributionGeneration(state, generation, failure);\n\n throw failure;\n }\n}\n\nfunction wrapFeatureContributionInstance<Prepared>(\n state: FeatureContributionInstanceState,\n generation: FeatureInstance<string, Prepared>,\n): FeatureInstance<string, Prepared> {\n const ready = generation.ready.then(\n value => publishFeatureContributions(state, generation, value),\n (cause: unknown) => {\n releaseFeatureContributionPreparation(state);\n\n throw cause;\n },\n );\n\n return {\n close: () => retireFeatureContributionGeneration(state, generation),\n instance: generation.instance,\n ready,\n };\n}\n\nfunction snapshotExactValues(\n value: unknown,\n expectedKeys: readonly string[],\n label: string,\n): Readonly<Record<string, unknown>> {\n assertPlainRecord(value, label);\n const entries = dataEntries(value, label);\n\n if (entries.length !== expectedKeys.length || expectedKeys.some(key => !hasOwn(value, key))) {\n throw new TypeError(`${label} must contain every exact field once.`);\n }\n\n return Object.freeze(Object.fromEntries(entries));\n}\n\nfunction checkFeatureCleanupPolicy(value: unknown): void {\n if (value !== undefined && value !== 'report' && value !== 'quarantine') {\n throw new TypeError('openFeature cleanupFailure must be \"report\" or \"quarantine\".');\n }\n}\n\n/** Validates every declared requirement binding; an `optional` port may be left unbound (D105). */\nfunction checkFeatureRequirements(\n feature: AnyFeature,\n record: ReturnType<typeof requireFeatureRecord>,\n requirements: Readonly<Record<string, unknown>>,\n): void {\n for (const key of record.requirementKeys) {\n const requirement = feature.requires?.[key];\n assertRequiredPortValue(\n getRequiredPort(requirement),\n requirements[key],\n key,\n isOptionalPortRequirement(requirement),\n );\n }\n}\n\nfunction snapshotFeatureOpenOptions(\n feature: Feature,\n record: ReturnType<typeof requireFeatureRecord>,\n value: unknown,\n): Readonly<Record<string, unknown>> {\n assertPlainRecord(value, 'openFeature options');\n const entries = dataEntries(value, 'openFeature options');\n\n for (const [key] of entries) {\n if (!featureOpenOptionKeys.has(key)) throw new TypeError(`Unknown openFeature option ${key}.`);\n }\n\n for (const key of ['imports', 'reporter'] as const) {\n if (!hasOwn(value, key)) throw new TypeError(`openFeature requires ${key}.`);\n }\n\n if (typeof value['reporter'] !== 'function') {\n throw new TypeError('openFeature reporter must be a function.');\n }\n\n checkFeatureCleanupPolicy(value['cleanupFailure']);\n const imports = snapshotExactValues(value['imports'], record.importKeys, 'openFeature imports');\n const requirements = snapshotExactValues(\n value['requirements'] ?? {},\n record.requirementKeys,\n 'openFeature requirements',\n );\n\n checkFeatureRequirements(feature, record, requirements);\n\n return Object.freeze({ ...Object.fromEntries(entries), imports, requirements });\n}\n\n/**\n * One `own` ref as the live value the instance holds: `exports` runs at open, so a call ref is already a `Call`, a\n * model or a state ref is already materialized data and a resource or a stream ref is already a `Resource` (D88).\n */\nfunction readFeatureOwnValue(instance: ModuleInstanceRef, key: string, ref: object): unknown {\n if (isFeatureDataRef(ref)) return requireFeatureDataScope(instance).read(ref);\n\n if (isFeatureMaterializationRef(ref)) return bindFeatureResource(instance, ref as never);\n\n if (isLifetimeAttachmentRef(ref)) {\n throw new TypeError(`own.${key} is a lifecycle binding, so exports has no value to take from it.`);\n }\n\n return resolveModuleCallTarget(instance, ref as ModuleCallRef<unknown, unknown>);\n}\n\n/** Lazy so a feature that owns attachments still runs `exports`: only the selected keys are materialized. */\nfunction materializeFeatureOwn(\n instance: ModuleInstanceRef,\n refs: Readonly<Record<string, object>>,\n): Readonly<Record<string, unknown>> {\n const values = new Map<string, unknown>();\n const own: Record<string, unknown> = {};\n\n for (const [key, ref] of Object.entries(refs)) {\n Object.defineProperty(own, key, {\n enumerable: true,\n get: (): unknown => {\n if (!values.has(key)) values.set(key, readFeatureOwnValue(instance, key, ref));\n\n return values.get(key);\n },\n });\n }\n\n return Object.freeze(own);\n}\n\n/** The export record carries live values, so a ref, a model record or a plain object never reaches an importer. */\nfunction requireFeatureExportValue(key: string, value: unknown): unknown {\n if (isCallTarget(value)) return value;\n\n const readable = value as Partial<Readable<unknown>>;\n\n if (typeof readable?.getSnapshot === 'function' && typeof readable.subscribe === 'function') return value;\n\n throw new TypeError(`Feature export ${key} must be a Call, a Readable or a Resource.`);\n}\n\nfunction buildFeatureExports(\n contract: FeatureContractIdentity,\n values: Readonly<Record<string, unknown>>,\n select: (context: { readonly own: Readonly<Record<string, unknown>> }) => unknown,\n): Readonly<Record<string, unknown>> {\n const returned = select({ own: values });\n // `({ own }) => own` means «export every value this instance owns» (D127). The record the runtime handed to the\n // author reads its members lazily, so returning it materializes every one of them here, before the shared check\n // that a result carries data fields. A member with no value form still fails on its Own key, where D88 belongs.\n const selected =\n returned === values ? Object.fromEntries(Object.keys(values).map(key => [key, values[key]])) : returned;\n\n assertPlainRecord(selected, 'defineFeature exports result');\n const facade = Object.freeze(\n Object.fromEntries(\n dataEntries(selected, 'defineFeature exports result').map(([key, value]) => [\n key,\n requireFeatureExportValue(key, value),\n ]),\n ),\n );\n\n registerFeatureExportFacade(contract, facade);\n\n return facade;\n}\n\nfunction prepareFeatureInstanceInputs(\n feature: AnyFeature,\n record: ReturnType<typeof requireFeatureRecord>,\n snapshot: Readonly<Record<string, unknown>>,\n): Readonly<{ imports: Readonly<Record<string, unknown>>; lowScopes: Readonly<Record<string, unknown>> }> {\n const imports = snapshot['imports'] as Readonly<Record<string, unknown>>;\n registerFeatureInstanceImports(feature.imports, imports);\n const requirementCalls = snapshot['requirements'] as Readonly<Record<string, unknown>>;\n registerFeatureInstanceCallTargets(requirementCalls);\n const lowScopes: Record<string, unknown> = {};\n\n if (record.importScopeKey !== undefined) lowScopes[record.importScopeKey] = imports;\n\n if (record.requirementScopeKey !== undefined) lowScopes[record.requirementScopeKey] = requirementCalls;\n\n return Object.freeze({ imports, lowScopes: Object.freeze(lowScopes) });\n}\n\nfunction openMaterializedFeature(\n feature: Feature,\n record: ReturnType<typeof requireFeatureRecord>,\n snapshot: Readonly<Record<string, unknown>>,\n activity?: FeatureActivityOwner,\n): FeatureInstanceOf<string, unknown> {\n activity?.changed?.();\n const { imports, lowScopes } = prepareFeatureInstanceInputs(feature, record, snapshot);\n const reporter = snapshot['reporter'] as ErrorReporter;\n const contributionState =\n record.contributions.length === 0\n ? undefined\n : createFeatureContributionInstanceState(feature.id, record.contributions);\n\n const openGeneration = openLoweredModuleInstance as unknown as (\n definition: object,\n generationOptions: unknown,\n ) => FeatureInstance<string, FeatureReady<unknown>>;\n const generation = openGeneration(record.module, {\n ...(snapshot['cleanupFailure'] === undefined\n ? {}\n : { cleanupFailure: snapshot['cleanupFailure'] as CleanupFailurePolicy }),\n prepare: (instance: ModuleInstanceRef) => {\n if (activity !== undefined) registerModuleActivity(instance, activity);\n\n if (record.dataDescriptors.length > 0) {\n materializeFeatureData(\n feature.id,\n record.dataDescriptors,\n imports,\n instance,\n reporter,\n data => {\n registerFeatureDataScope(instance, data);\n registerModuleRetirementParticipant(instance, {\n fence: () => {\n data.fence();\n const drain = data.drain();\n\n return { drain: () => drain, retry: () => drain };\n },\n });\n },\n activity,\n );\n }\n\n // D220: one materialized `own` for this instance serves both `exports` and every reactive calculation the\n // instance declared, so a predicate reads the same live values an importer sees.\n const ownValues = materializeFeatureOwn(instance, feature.own);\n const bound = buildFeatureExports(feature.exports, ownValues, record.exports);\n const context = Object.freeze({\n exports: bound,\n imports,\n instance,\n model: contributionModel,\n own: feature.own,\n });\n\n if (contributionState !== undefined) {\n prepareFeatureContributions(contributionState, context, instance, {\n exports: bound,\n imports,\n own: ownValues,\n });\n contributionState.authority = Object.freeze({\n models: readInstanceModels(instance, record.dataDescriptors),\n });\n }\n\n const ready = Object.freeze({ exports: bound });\n bindFeatureReadyPorts(ready, record.providers, instance);\n\n return ready;\n },\n reporter,\n scopes: lowScopes,\n });\n\n if (contributionState === undefined) return generation;\n\n const wrapped = wrapFeatureContributionInstance(contributionState, generation);\n carryFeatureGenerationFence(generation, wrapped);\n\n return wrapped;\n}\n\nfunction openFeatureWithActivity(\n feature: Feature,\n options: unknown,\n activity?: FeatureActivityOwner,\n): FeatureInstanceOf<string, unknown> {\n const record = requireFeatureRecord(feature);\n // D211: exact bindings and base options belong to the call, never to the eventual body-load completion.\n const snapshot = snapshotFeatureOpenOptions(feature, record, options);\n\n return getLazyFeature(feature) === undefined\n ? openMaterializedFeature(feature, record, snapshot, activity)\n : openLazyFeatureInstance(feature, body => openMaterializedFeature(feature, body, snapshot, activity));\n}\n\nfunction openFeatureInstance<\n const Id extends string,\n const Imports extends FeatureImportRecord,\n const Runtime extends FeatureOwnRecord<Id>,\n const Exports extends FeatureExportRecord,\n const Requires extends PortRequirementRecord,\n const ImportValues extends FeatureImportValues<Imports>,\n const OptionKeys extends PropertyKey,\n const ImportKeys extends PropertyKey,\n const RequirementKeys extends PropertyKey,\n>(\n feature: Feature<Id, Imports, Runtime, Exports, Requires>,\n options: ExactKeys<\n OptionKeys,\n FeatureOpenBaseOptions<FeatureImportValues<Imports>> &\n FeatureRequirementOpenOptions<RequiredPortValues<Requires>, keyof RequiredPortValues<Requires>>\n > &\n FeatureOpenBaseOptions<ImportValues> &\n FeatureRequirementOpenOptions<RequiredPortValues<Requires>, RequirementKeys> & {\n readonly imports: ExactImportValueFields<ImportValues, FeatureImportValues<Imports>> &\n ExactKeys<ImportKeys, FeatureImportValues<Imports>>;\n },\n): FeatureInstanceOf<Id, Exports>;\nfunction openFeatureInstance(feature: Feature, options: unknown): FeatureInstanceOf<string, unknown> {\n return openFeatureWithActivity(feature, options);\n}\n\nconst openFeature = openFeatureInstance;\n\nexport { openFeature, openFeatureWithActivity };\nexport type { FeatureReady, FeatureReadyOf };\n"],"names":["featureOpenOptionKeys","createFeatureContributionInstanceState","featureId","contributions","featureContributionInactiveReason","state","FeatureError","releaseFeatureContributionPreparation","fenceFeatureContributions","publication","withdrawContributions","prepareFeatureContributions","context","instance","evaluation","registerModuleRetirementParticipant","isQuarantinedGeneration","error","preserveFeatureContributionQuarantine","cause","recovery","quarantinedGeneration","nextError","retireFeatureContributionGeneration","generation","rejectRetirement","resolveRetirement","pending","resolve","reject","retirementCause","requireFeatureContributionPreparation","captureFeatureContributionPublication","publishFeatureContributions","value","createFeatureContributionPublication","failure","notReadyGeneration","wrapFeatureContributionInstance","ready","snapshotExactValues","expectedKeys","label","assertPlainRecord","entries","dataEntries","key","hasOwn","checkFeatureCleanupPolicy","checkFeatureRequirements","feature","record","requirements","requirement","_a","assertRequiredPortValue","getRequiredPort","isOptionalPortRequirement","snapshotFeatureOpenOptions","imports","readFeatureOwnValue","ref","isFeatureDataRef","requireFeatureDataScope","isFeatureMaterializationRef","bindFeatureResource","isLifetimeAttachmentRef","resolveModuleCallTarget","materializeFeatureOwn","refs","values","own","requireFeatureExportValue","isCallTarget","readable","buildFeatureExports","contract","select","returned","selected","facade","registerFeatureExportFacade","prepareFeatureInstanceInputs","snapshot","registerFeatureInstanceImports","requirementCalls","registerFeatureInstanceCallTargets","lowScopes","openMaterializedFeature","activity","reporter","contributionState","openLoweredModuleInstance","registerModuleActivity","materializeFeatureData","data","registerFeatureDataScope","drain","ownValues","bound","contributionModel","readInstanceModels","bindFeatureReadyPorts","wrapped","carryFeatureGenerationFence","openFeatureWithActivity","options","requireFeatureRecord","getLazyFeature","openLazyFeatureInstance","body","openFeatureInstance","openFeature"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Feature } from './feature-authoring.js';
2
+ import type { FeatureRecord } from './feature-authoring-types.js';
3
+ import type { FeatureInstance } from './module-generation.js';
4
+ type Opening<Prepared> = (record: FeatureRecord) => FeatureInstance<string, Prepared>;
5
+ declare function openLazyFeatureInstance<Prepared>(feature: Feature, opening: Opening<Prepared>): FeatureInstance<string, Prepared>;
6
+ export { openLazyFeatureInstance };
@@ -0,0 +1,2 @@
1
+ import{FeatureError as d}from"@opetope/core";import{observeFeatureBody as a}from"./feature-lazy.js";import{registerFeatureGenerationFence as f,fenceFeatureGeneration as o}from"./module-generation.js";class c{ready;get instance(){if(this.opened===void 0)throw new d("not-ready",this.id);return this.opened.instance}id;opened;opening;readiness;releaseBody;retired=!1;retirement;starting=!1;constructor(e,i){this.id=e.id,this.opening=i,this.ready=new Promise((t,n)=>{this.readiness={reject:n,resolve:t}}),f(this,this.fence),this.releaseBody=a(e,t=>this.start(t),this.fail)}close=()=>{if(this.retirement!==void 0)return this.retirement.promise;let e,i;const t=new Promise((s,h)=>{i=s,e=h}),n={promise:t,reject:e,resolve:i};this.retirement=n;try{this.fence(),this.starting||this.drain(n)}catch(s){this.failRetirement(n,s)}return t};drain(e){const i=this.opened;if(i===void 0){e.resolve();return}try{i.close().then(e.resolve,t=>this.failRetirement(e,t))}catch(t){this.failRetirement(e,t)}}fail=e=>{this.stopWaitingForBody();const i=this.readiness;this.readiness=void 0,this.opening=void 0,i==null||i.reject(e)};failRetirement(e,i){this.retirement===e&&(this.retirement=void 0),e.reject(i)}fence=()=>{this.retired||(this.retired=!0,this.stopWaitingForBody(),this.opening=void 0,this.readiness!==void 0&&this.fail(new d("retired",this.id)),this.opened!==void 0&&o(this.opened))};start(e){this.stopWaitingForBody();const i=this.opening;if(this.opening=void 0,i!==void 0){this.starting=!0;try{this.opened=i(e),this.opened.ready.then(t=>{const n=this.readiness;this.readiness=void 0,n==null||n.resolve(t)},this.fail),this.retired&&o(this.opened)}catch(t){this.fail(t)}finally{this.starting=!1,this.retirement!==void 0&&this.drain(this.retirement)}}}stopWaitingForBody(){const e=this.releaseBody;this.releaseBody=void 0,e==null||e()}}function u(r,e){return new c(r,e)}export{u as openLazyFeatureInstance};
2
+ //# sourceMappingURL=feature-lazy-generation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-lazy-generation.js","sources":["../src/feature-lazy-generation.ts"],"sourcesContent":["import { FeatureError } from '@opetope/core';\nimport type { DeclarationId } from '@opetope/core';\n\nimport type { Feature } from './feature-authoring';\nimport type { FeatureRecord } from './feature-authoring-types';\nimport { observeFeatureBody } from './feature-lazy';\nimport { fenceFeatureGeneration, registerFeatureGenerationFence } from './module-generation';\nimport type { FeatureInstance } from './module-generation';\n\ntype Opening<Prepared> = (record: FeatureRecord) => FeatureInstance<string, Prepared>;\ntype Completion<Value> = {\n readonly reject: (cause: unknown) => void;\n readonly resolve: (value: Value) => void;\n};\ntype Retirement = Completion<void> & { readonly promise: Promise<void> };\n\n/**\n * D211: the shared code load is independent of this opening attempt. A retired attempt drops its inputs and\n * rejects readiness immediately; only a materialization already started owns work that close must drain.\n */\nclass LazyFeatureGeneration<Prepared> implements FeatureInstance<string, Prepared> {\n readonly ready: Promise<Prepared>;\n get instance(): FeatureInstance<string, Prepared>['instance'] {\n if (this.opened === undefined) throw new FeatureError('not-ready', this.id);\n\n return this.opened.instance;\n }\n private readonly id: DeclarationId;\n private opened: FeatureInstance<string, Prepared> | undefined;\n private opening: Opening<Prepared> | undefined;\n private readiness: Completion<Prepared> | undefined;\n private releaseBody: (() => void) | undefined;\n private retired = false;\n private retirement: Retirement | undefined;\n\n private starting = false;\n\n constructor(feature: Feature, opening: Opening<Prepared>) {\n this.id = feature.id;\n this.opening = opening;\n this.ready = new Promise((resolve, reject) => {\n this.readiness = { reject, resolve };\n });\n registerFeatureGenerationFence(this, this.fence);\n this.releaseBody = observeFeatureBody(feature, record => this.start(record), this.fail);\n }\n\n readonly close = (): Promise<void> => {\n if (this.retirement !== undefined) return this.retirement.promise;\n\n let reject!: (cause: unknown) => void;\n let resolve!: () => void;\n const promise = new Promise<void>((accept, refuse) => {\n resolve = accept;\n reject = refuse;\n });\n const retirement = { promise, reject, resolve };\n this.retirement = retirement;\n\n try {\n // Reserve the shared completion before fencing: observers may call close again from their notification.\n this.fence();\n\n if (!this.starting) this.drain(retirement);\n } catch (cause) {\n this.failRetirement(retirement, cause);\n }\n\n return promise;\n };\n\n private drain(retirement: Retirement): void {\n const opened = this.opened;\n\n if (opened === undefined) {\n retirement.resolve();\n\n return;\n }\n\n try {\n void opened.close().then(retirement.resolve, (cause: unknown) => this.failRetirement(retirement, cause));\n } catch (cause) {\n this.failRetirement(retirement, cause);\n }\n }\n\n private readonly fail = (cause: unknown): void => {\n this.stopWaitingForBody();\n const readiness = this.readiness;\n this.readiness = undefined;\n this.opening = undefined;\n readiness?.reject(cause);\n };\n\n private failRetirement(retirement: Retirement, cause: unknown): void {\n // Like the eager contribution wrapper, a failed cleanup can be retried through its error capability.\n if (this.retirement === retirement) this.retirement = undefined;\n\n retirement.reject(cause);\n }\n\n private readonly fence = (): void => {\n if (this.retired) return;\n\n this.retired = true;\n this.stopWaitingForBody();\n this.opening = undefined;\n\n if (this.readiness !== undefined) this.fail(new FeatureError('retired', this.id));\n\n if (this.opened !== undefined) fenceFeatureGeneration(this.opened);\n };\n\n private start(record: FeatureRecord): void {\n this.stopWaitingForBody();\n const opening = this.opening;\n this.opening = undefined;\n\n if (opening === undefined) return;\n\n this.starting = true;\n\n try {\n this.opened = opening(record);\n void this.opened.ready.then(value => {\n const readiness = this.readiness;\n this.readiness = undefined;\n readiness?.resolve(value);\n }, this.fail);\n\n // A factory or source callback may close the facade before the materialized instance has been returned.\n if (this.retired) fenceFeatureGeneration(this.opened);\n } catch (cause) {\n this.fail(cause);\n } finally {\n this.starting = false;\n\n if (this.retirement !== undefined) this.drain(this.retirement);\n }\n }\n\n private stopWaitingForBody(): void {\n const release = this.releaseBody;\n this.releaseBody = undefined;\n release?.();\n }\n}\n\nfunction openLazyFeatureInstance<Prepared>(\n feature: Feature,\n opening: Opening<Prepared>,\n): FeatureInstance<string, Prepared> {\n return new LazyFeatureGeneration(feature, opening);\n}\n\nexport { openLazyFeatureInstance };\n"],"names":["LazyFeatureGeneration","FeatureError","feature","opening","resolve","reject","registerFeatureGenerationFence","observeFeatureBody","record","promise","accept","refuse","retirement","cause","opened","readiness","fenceFeatureGeneration","value","release","openLazyFeatureInstance"],"mappings":"wMAoBA,MAAMA,CAAqB,CAChB,MACT,IAAI,UAAQ,CACV,GAAI,KAAK,SAAW,OAAW,MAAM,IAAIC,EAAa,YAAa,KAAK,EAAE,EAE1E,OAAO,KAAK,OAAO,QACrB,CACiB,GACT,OACA,QACA,UACA,YACA,QAAU,GACV,WAEA,SAAW,GAEnB,YAAYC,EAAkBC,EAA0B,CACtD,KAAK,GAAKD,EAAQ,GAClB,KAAK,QAAUC,EACf,KAAK,MAAQ,IAAI,QAAQ,CAACC,EAASC,IAAU,CAC3C,KAAK,UAAY,CAAE,OAAAA,EAAQ,QAAAD,CAAO,CACpC,CAAC,EACDE,EAA+B,KAAM,KAAK,KAAK,EAC/C,KAAK,YAAcC,EAAmBL,EAASM,GAAU,KAAK,MAAMA,CAAM,EAAG,KAAK,IAAI,CACxF,CAES,MAAQ,IAAoB,CACnC,GAAI,KAAK,aAAe,OAAW,OAAO,KAAK,WAAW,QAE1D,IAAIH,EACAD,EACJ,MAAMK,EAAU,IAAI,QAAc,CAACC,EAAQC,IAAU,CACnDP,EAAUM,EACVL,EAASM,CACX,CAAC,EACKC,EAAa,CAAE,QAAAH,EAAS,OAAAJ,EAAQ,QAAAD,CAAO,EAC7C,KAAK,WAAaQ,EAElB,GAAI,CAEF,KAAK,MAAK,EAEL,KAAK,UAAU,KAAK,MAAMA,CAAU,CAC3C,OAASC,EAAO,CACd,KAAK,eAAeD,EAAYC,CAAK,CACvC,CAEA,OAAOJ,CACT,EAEQ,MAAMG,EAAsB,CAClC,MAAME,EAAS,KAAK,OAEpB,GAAIA,IAAW,OAAW,CACxBF,EAAW,QAAO,EAElB,MACF,CAEA,GAAI,CACGE,EAAO,QAAQ,KAAKF,EAAW,QAAUC,GAAmB,KAAK,eAAeD,EAAYC,CAAK,CAAC,CACzG,OAASA,EAAO,CACd,KAAK,eAAeD,EAAYC,CAAK,CACvC,CACF,CAEiB,KAAQA,GAAwB,CAC/C,KAAK,mBAAkB,EACvB,MAAME,EAAY,KAAK,UACvB,KAAK,UAAY,OACjB,KAAK,QAAU,OACfA,GAAA,MAAAA,EAAW,OAAOF,EACpB,EAEQ,eAAeD,EAAwBC,EAAc,CAEvD,KAAK,aAAeD,IAAY,KAAK,WAAa,QAEtDA,EAAW,OAAOC,CAAK,CACzB,CAEiB,MAAQ,IAAW,CAC9B,KAAK,UAET,KAAK,QAAU,GACf,KAAK,mBAAkB,EACvB,KAAK,QAAU,OAEX,KAAK,YAAc,QAAW,KAAK,KAAK,IAAIZ,EAAa,UAAW,KAAK,EAAE,CAAC,EAE5E,KAAK,SAAW,QAAWe,EAAuB,KAAK,MAAM,EACnE,EAEQ,MAAMR,EAAqB,CACjC,KAAK,mBAAkB,EACvB,MAAML,EAAU,KAAK,QAGrB,GAFA,KAAK,QAAU,OAEXA,IAAY,OAEhB,MAAK,SAAW,GAEhB,GAAI,CACF,KAAK,OAASA,EAAQK,CAAM,EACvB,KAAK,OAAO,MAAM,KAAKS,GAAQ,CAClC,MAAMF,EAAY,KAAK,UACvB,KAAK,UAAY,OACjBA,GAAA,MAAAA,EAAW,QAAQE,EACrB,EAAG,KAAK,IAAI,EAGR,KAAK,SAASD,EAAuB,KAAK,MAAM,CACtD,OAASH,EAAO,CACd,KAAK,KAAKA,CAAK,CACjB,SACE,KAAK,SAAW,GAEZ,KAAK,aAAe,QAAW,KAAK,MAAM,KAAK,UAAU,CAC/D,EACF,CAEQ,oBAAkB,CACxB,MAAMK,EAAU,KAAK,YACrB,KAAK,YAAc,OACnBA,GAAA,MAAAA,GACF,CACD,CAED,SAASC,EACPjB,EACAC,EAA0B,CAE1B,OAAO,IAAIH,EAAsBE,EAASC,CAAO,CACnD"}
@@ -0,0 +1,44 @@
1
+ import type { DeclarationId } from '@opetope/core';
2
+ import type { FeatureRecord } from './feature-authoring-types.js';
3
+ import type { LoadedFeatureBody } from './feature-body.js';
4
+ import type { FeatureContributionDescriptor } from './feature-contribution.js';
5
+ import type { PortProviderDescriptor } from './feature-port.js';
6
+ /** What the header declares of one contribution: the compiler needs its target and the priority it orders by. */
7
+ type DeclaredContribution = {
8
+ readonly id: DeclarationId;
9
+ readonly priority: number;
10
+ readonly target: FeatureContributionDescriptor['target'];
11
+ };
12
+ type DeclaredProvided = {
13
+ readonly contributions: readonly DeclaredContribution[];
14
+ readonly providers: readonly Omit<PortProviderDescriptor, 'target'>[];
15
+ };
16
+ type LazyFeatureDefinition = {
17
+ readonly load: () => Promise<LoadedFeatureBody>;
18
+ readonly materialize: (body: LoadedFeatureBody) => FeatureRecord;
19
+ };
20
+ type LazyFeatureState = {
21
+ loading: Promise<FeatureRecord> | undefined;
22
+ materialized: FeatureRecord | undefined;
23
+ };
24
+ type BodyWaiter = {
25
+ readonly failed: (cause: unknown) => void;
26
+ readonly loaded: (record: FeatureRecord) => void;
27
+ };
28
+ declare function registerLazyFeature(feature: object, definition: LazyFeatureDefinition): void;
29
+ /** A header that has not loaded yet answers `undefined`: only the open path is allowed to start the loader. */
30
+ declare function getLazyFeature(feature: object): (LazyFeatureDefinition & LazyFeatureState) | undefined;
31
+ /**
32
+ * One load per feature, shared by every consumer that asks while it runs. A failed attempt is not cached: the next
33
+ * open tries the loader again, because a chunk that failed to arrive may arrive later (D186).
34
+ */
35
+ declare function loadFeatureBody(feature: object): Promise<FeatureRecord>;
36
+ /** One observer of the shared load, with detachable registrations for the instances still waiting for code. */
37
+ declare function observeFeatureBody(feature: object, loaded: BodyWaiter['loaded'], failed: BodyWaiter['failed']): () => void;
38
+ /**
39
+ * D186: the header carries the metadata the compiler needs — which target every contribution publishes into and
40
+ * which port every provider claims — so the whole topology compiles before a byte of the body is loaded.
41
+ */
42
+ declare function snapshotDeclaredProvided(featureId: string, value: unknown): DeclaredProvided;
43
+ declare function assertBodyMatchesHeader(featureId: string, declared: DeclaredProvided, record: FeatureRecord): void;
44
+ export { assertBodyMatchesHeader, getLazyFeature, loadFeatureBody, observeFeatureBody, registerLazyFeature, snapshotDeclaredProvided, };
@@ -0,0 +1,2 @@
1
+ import{declarationId as u}from"@opetope/core";import{isPort as p}from"@opetope/core/internal";import{assertPlainRecord as s,dataEntries as a}from"./feature-record.js";const d=new WeakMap,c=new WeakMap;function l(o,e){d.set(o,{...e,loading:void 0,materialized:void 0})}function y(o){return d.get(o)}function f(o){const e=d.get(o);if(e===void 0)throw new TypeError("This feature has no body to load.");if(e.materialized!==void 0)return Promise.resolve(e.materialized);if(e.loading!==void 0)return e.loading;const i=Promise.resolve().then(()=>e.load()).then(r=>{const t=e.materialize(r);return e.materialized=t,e.loading=void 0,t}).catch(r=>{throw e.loading=void 0,r});return e.loading=i,i}function h(o){const e=new Set;return o.then(i=>{for(const r of e)e.delete(r),r.loaded(i)},i=>{for(const r of e)e.delete(r),r.failed(i)}),e}function w(o,e,i){const r=f(o);let t=c.get(r);t===void 0&&(t=h(r),c.set(r,t));const n={failed:i,loaded:e};return t.add(n),()=>{t.delete(n)}}const b=new Set(["priority","target"]);function g(o,e,i){if(typeof i!="object"||i===null||typeof i.id!="string")throw new TypeError(`Feature ${o} declares provides.${e} without an authentic target.`);return i}function $(o,e,i){s(i,`defineFeature provides.${e}`);for(const[r]of a(i,`defineFeature provides.${e}`))if(!b.has(r))throw new TypeError(`Feature ${o} declares provides.${e} with unknown field ${r}.`)}function z(o,e,i){const r=i??0;if(typeof r!="number"||!Number.isFinite(r))throw new TypeError(`Feature ${o} declares provides.${e} with a non-numeric priority.`);return r}function m(o,e,i){const r=i,t=typeof r.target=="object";return t&&$(o,e,r),Object.freeze({id:u(`${o}.${e}`),priority:z(o,e,t?r.priority:0),target:g(o,e,t?r.target:i)})}function F(o,e){if(e===void 0)return Object.freeze({contributions:Object.freeze([]),providers:Object.freeze([])});s(e,"defineFeature provides");const i=[],r=[];for(const[t,n]of a(e,"defineFeature provides"))p(n)?r.push(Object.freeze({key:t,port:n})):i.push(m(o,t,n));return Object.freeze({contributions:Object.freeze(i),providers:Object.freeze(r)})}function v(o,e,i){const r=new Map(e.contributions.map(t=>[t.id,t]));for(const t of i.contributions){const n=r.get(t.id);if(n===void 0)throw new TypeError(`Feature ${o} body publishes ${t.id}, which its header does not declare.`);if(n.target!==t.target||n.priority!==t.priority)throw new TypeError(`Feature ${o} body publishes ${t.id} with another target or priority.`);r.delete(t.id)}if(r.size>0)throw new TypeError(`Feature ${o} body does not publish the declared contributions ${[...r.keys()].join(", ")}.`)}function j(o,e,i){const r=new Map(e.providers.map(t=>[t.key,t.port]));for(const t of i.providers){if(r.get(t.key)!==t.port)throw new TypeError(`Feature ${o} body provides ${t.key}, which its header does not declare.`);r.delete(t.key)}if(r.size>0)throw new TypeError(`Feature ${o} body does not provide the declared ports ${[...r.keys()].join(", ")}.`)}function T(o,e,i){v(o,e,i),j(o,e,i)}export{T as assertBodyMatchesHeader,y as getLazyFeature,f as loadFeatureBody,w as observeFeatureBody,l as registerLazyFeature,F as snapshotDeclaredProvided};
2
+ //# sourceMappingURL=feature-lazy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-lazy.js","sources":["../src/feature-lazy.ts"],"sourcesContent":["import { declarationId } from '@opetope/core';\nimport type { DeclarationId } from '@opetope/core';\nimport { isPort } from '@opetope/core/internal';\n\nimport type { FeatureRecord } from './feature-authoring-types';\nimport type { LoadedFeatureBody } from './feature-body';\nimport type { FeatureContributionDescriptor } from './feature-contribution';\nimport type { PortProviderDescriptor } from './feature-port';\nimport { assertPlainRecord, dataEntries } from './feature-record';\n\n/** What the header declares of one contribution: the compiler needs its target and the priority it orders by. */\ntype DeclaredContribution = {\n readonly id: DeclarationId;\n readonly priority: number;\n readonly target: FeatureContributionDescriptor['target'];\n};\n\ntype DeclaredProvided = {\n readonly contributions: readonly DeclaredContribution[];\n readonly providers: readonly Omit<PortProviderDescriptor, 'target'>[];\n};\n\ntype LazyFeatureDefinition = {\n readonly load: () => Promise<LoadedFeatureBody>;\n readonly materialize: (body: LoadedFeatureBody) => FeatureRecord;\n};\n\ntype LazyFeatureState = {\n loading: Promise<FeatureRecord> | undefined;\n materialized: FeatureRecord | undefined;\n};\n\nconst lazyFeatures = new WeakMap<object, LazyFeatureDefinition & LazyFeatureState>();\n\ntype BodyWaiter = {\n readonly failed: (cause: unknown) => void;\n readonly loaded: (record: FeatureRecord) => void;\n};\nconst bodyWaiters = new WeakMap<Promise<FeatureRecord>, Set<BodyWaiter>>();\n\nfunction registerLazyFeature(feature: object, definition: LazyFeatureDefinition): void {\n lazyFeatures.set(feature, { ...definition, loading: undefined, materialized: undefined });\n}\n\n/** A header that has not loaded yet answers `undefined`: only the open path is allowed to start the loader. */\nfunction getLazyFeature(feature: object): (LazyFeatureDefinition & LazyFeatureState) | undefined {\n return lazyFeatures.get(feature);\n}\n\n/**\n * One load per feature, shared by every consumer that asks while it runs. A failed attempt is not cached: the next\n * open tries the loader again, because a chunk that failed to arrive may arrive later (D186).\n */\nfunction loadFeatureBody(feature: object): Promise<FeatureRecord> {\n const lazy = lazyFeatures.get(feature);\n\n if (lazy === undefined) throw new TypeError('This feature has no body to load.');\n\n if (lazy.materialized !== undefined) return Promise.resolve(lazy.materialized);\n\n if (lazy.loading !== undefined) return lazy.loading;\n\n // D211: publish the shared attempt before invoking author code, including a synchronously throwing loader.\n const loading = Promise.resolve()\n .then(() => lazy.load())\n .then(body => {\n const record = lazy.materialize(body);\n lazy.materialized = record;\n lazy.loading = undefined;\n\n return record;\n })\n .catch((error: unknown) => {\n lazy.loading = undefined;\n\n throw error;\n });\n lazy.loading = loading;\n\n return loading;\n}\n\n/** Keep the shared Promise reactions outside the lexical scope of any individual instance registration. */\nfunction createBodyWaiters(loading: Promise<FeatureRecord>): Set<BodyWaiter> {\n const pending = new Set<BodyWaiter>();\n void loading.then(\n record => {\n for (const waiter of pending) {\n pending.delete(waiter);\n waiter.loaded(record);\n }\n },\n (cause: unknown) => {\n for (const waiter of pending) {\n pending.delete(waiter);\n waiter.failed(cause);\n }\n },\n );\n\n return pending;\n}\n\n/** One observer of the shared load, with detachable registrations for the instances still waiting for code. */\nfunction observeFeatureBody(feature: object, loaded: BodyWaiter['loaded'], failed: BodyWaiter['failed']): () => void {\n const loading = loadFeatureBody(feature);\n let waiters = bodyWaiters.get(loading);\n\n if (waiters === undefined) {\n waiters = createBodyWaiters(loading);\n bodyWaiters.set(loading, waiters);\n }\n\n const waiter = { failed, loaded };\n waiters.add(waiter);\n\n return () => void waiters.delete(waiter);\n}\n\nconst contributionKeys = new Set(['priority', 'target']);\n\nfunction requireTarget(featureId: string, key: string, value: unknown): FeatureContributionDescriptor['target'] {\n if (typeof value !== 'object' || value === null || typeof (value as { id?: unknown }).id !== 'string') {\n throw new TypeError(`Feature ${featureId} declares provides.${key} without an authentic target.`);\n }\n\n return value as FeatureContributionDescriptor['target'];\n}\n\nfunction assertDeclaredContributionKeys(featureId: string, key: string, record: Readonly<Record<string, unknown>>) {\n assertPlainRecord(record, `defineFeature provides.${key}`);\n\n for (const [field] of dataEntries(record, `defineFeature provides.${key}`)) {\n if (!contributionKeys.has(field)) {\n throw new TypeError(`Feature ${featureId} declares provides.${key} with unknown field ${field}.`);\n }\n }\n}\n\nfunction requirePriority(featureId: string, key: string, value: unknown): number {\n const priority = value ?? 0;\n\n if (typeof priority !== 'number' || !Number.isFinite(priority)) {\n throw new TypeError(`Feature ${featureId} declares provides.${key} with a non-numeric priority.`);\n }\n\n return priority;\n}\n\nfunction snapshotDeclaredContribution(featureId: string, key: string, value: unknown): DeclaredContribution {\n const record = value as Readonly<Record<string, unknown>>;\n const declared = typeof record['target'] === 'object';\n\n if (declared) assertDeclaredContributionKeys(featureId, key, record);\n\n return Object.freeze({\n id: declarationId(`${featureId}.${key}`),\n priority: requirePriority(featureId, key, declared ? record['priority'] : 0),\n target: requireTarget(featureId, key, declared ? record['target'] : value),\n });\n}\n\n/**\n * D186: the header carries the metadata the compiler needs — which target every contribution publishes into and\n * which port every provider claims — so the whole topology compiles before a byte of the body is loaded.\n */\nfunction snapshotDeclaredProvided(featureId: string, value: unknown): DeclaredProvided {\n if (value === undefined) return Object.freeze({ contributions: Object.freeze([]), providers: Object.freeze([]) });\n\n assertPlainRecord(value, 'defineFeature provides');\n const contributions: DeclaredContribution[] = [];\n const providers: Omit<PortProviderDescriptor, 'target'>[] = [];\n\n for (const [key, entry] of dataEntries(value, 'defineFeature provides')) {\n if (isPort(entry)) providers.push(Object.freeze({ key, port: entry }));\n else contributions.push(snapshotDeclaredContribution(featureId, key, entry));\n }\n\n return Object.freeze({ contributions: Object.freeze(contributions), providers: Object.freeze(providers) });\n}\n\n/**\n * The body implements the border the header declared. A body that publishes into another target, claims another\n * port or reorders a contribution is refused before anything opens: the compiled plan is already public by then.\n */\nfunction assertDeclaredContributions(featureId: string, declared: DeclaredProvided, record: FeatureRecord): void {\n const expected = new Map(declared.contributions.map(entry => [entry.id, entry]));\n\n for (const contribution of record.contributions) {\n const declaration = expected.get(contribution.id);\n\n if (declaration === undefined) {\n throw new TypeError(`Feature ${featureId} body publishes ${contribution.id}, which its header does not declare.`);\n }\n\n if (declaration.target !== contribution.target || declaration.priority !== contribution.priority) {\n throw new TypeError(`Feature ${featureId} body publishes ${contribution.id} with another target or priority.`);\n }\n\n expected.delete(contribution.id);\n }\n\n if (expected.size > 0) {\n throw new TypeError(\n `Feature ${featureId} body does not publish the declared contributions ${[...expected.keys()].join(', ')}.`,\n );\n }\n}\n\nfunction assertDeclaredProviders(featureId: string, declared: DeclaredProvided, record: FeatureRecord): void {\n const expected = new Map(declared.providers.map(entry => [entry.key, entry.port]));\n\n for (const provider of record.providers) {\n if (expected.get(provider.key) !== provider.port) {\n throw new TypeError(`Feature ${featureId} body provides ${provider.key}, which its header does not declare.`);\n }\n\n expected.delete(provider.key);\n }\n\n if (expected.size > 0) {\n throw new TypeError(\n `Feature ${featureId} body does not provide the declared ports ${[...expected.keys()].join(', ')}.`,\n );\n }\n}\n\nfunction assertBodyMatchesHeader(featureId: string, declared: DeclaredProvided, record: FeatureRecord): void {\n assertDeclaredContributions(featureId, declared, record);\n assertDeclaredProviders(featureId, declared, record);\n}\n\nexport {\n assertBodyMatchesHeader,\n getLazyFeature,\n loadFeatureBody,\n observeFeatureBody,\n registerLazyFeature,\n snapshotDeclaredProvided,\n};\n"],"names":["lazyFeatures","bodyWaiters","registerLazyFeature","feature","definition","getLazyFeature","loadFeatureBody","lazy","loading","body","record","error","createBodyWaiters","pending","waiter","cause","observeFeatureBody","loaded","failed","waiters","contributionKeys","requireTarget","featureId","key","value","assertDeclaredContributionKeys","assertPlainRecord","field","dataEntries","requirePriority","priority","snapshotDeclaredContribution","declared","declarationId","snapshotDeclaredProvided","contributions","providers","entry","isPort","assertDeclaredContributions","expected","contribution","declaration","assertDeclaredProviders","provider","assertBodyMatchesHeader"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { Awaitable } from '@opetope/core/internal';
2
+ import type { CompiledLifetimeAttachmentRecipe, LifetimeAttachmentRefIdentity } from './attachment-declaration.js';
3
+ import type { FeatureResourceRef, FeatureStreamRef } from './feature-materialization-types.js';
4
+ import type { FeatureActivityOwner } from './inspection-activity.js';
5
+ import type { ModuleInstanceRef } from './public-module.js';
6
+ import type { Resource, ResourceRequestKey } from './resource-types.js';
7
+ type ManagedMaterialization = Resource<unknown, ResourceRequestKey> & {
8
+ readonly close: () => Awaitable<void>;
9
+ };
10
+ type MaterializationKind = 'resource' | 'stream';
11
+ type MaterializationToken = {
12
+ readonly kind: MaterializationKind;
13
+ };
14
+ interface MaterializationBinding {
15
+ readonly allocateGeneration: () => number;
16
+ readonly attach: (token: MaterializationToken, controller: ManagedMaterialization) => (physicallyClosed?: boolean) => void;
17
+ readonly kind: MaterializationKind;
18
+ readonly observe: (owner: FeatureActivityOwner) => void;
19
+ readonly ref: Resource<unknown, ResourceRequestKey>;
20
+ readonly token: MaterializationToken;
21
+ }
22
+ declare function registerFeatureMaterialization(attachment: LifetimeAttachmentRefIdentity, kind: MaterializationKind): MaterializationToken;
23
+ declare function createMaterializationBinding(recipe: CompiledLifetimeAttachmentRecipe<unknown>): MaterializationBinding | undefined;
24
+ declare function withMaterializationBinding<Result>(binding: MaterializationBinding | undefined, operation: () => Result): Result;
25
+ declare function attachOpeningMaterialization(token: MaterializationToken, controller: ManagedMaterialization): (physicallyClosed?: boolean) => void;
26
+ declare function openingMaterializationGenerationAllocator(token: MaterializationToken): () => number;
27
+ /** Whether an `own` ref came from `resource()` or `stream()`: the open-time `own` record binds those to `Resource`. */
28
+ declare function isFeatureMaterializationRef(ref: object): boolean;
29
+ declare function bindFeatureResource<const Id extends string, Data, Key extends ResourceRequestKey>(instance: ModuleInstanceRef<Id>, resource: FeatureResourceRef<Data, Key, Id> | FeatureStreamRef<Data, Key, Id>): Resource<Data, Key>;
30
+ export { attachOpeningMaterialization, bindFeatureResource, createMaterializationBinding, isFeatureMaterializationRef, openingMaterializationGenerationAllocator, registerFeatureMaterialization, withMaterializationBinding, };
31
+ export type { MaterializationBinding };
@@ -0,0 +1,2 @@
1
+ import{reportDetachedError as z}from"@opetope/core/internal";import{getAttachmentDefinition as m,isLifetimeAttachmentRef as y}from"./attachment-declaration.js";import{getCompiledAttachmentSlot as k}from"./compile-module-template.js";import{requireModuleInstance as T,requireActiveDefinition as M,requireActiveLocal as E}from"./public-module-state.js";import{idleSnapshot as b}from"./resource-snapshot.js";const p=new WeakMap;let u;function A(i,n){const o=m(i).recipe,s=Object.freeze({kind:n});return p.set(o,s),s}function S(i){const n=p.get(i);if(n===void 0)return;const o=new Set,s=new Set;let e,a,f=b,d=0,c;const l=()=>{for(const t of[...s])try{t()}catch(r){z(r)}},w=()=>{var t;for(const r of o)(t=r.controllerRelease)==null||t.call(r),r.controllerRelease=void 0},R=Object.freeze({getSnapshot:()=>(e==null?void 0:e.getSnapshot())??f,invalidate:()=>e==null?void 0:e.invalidate(),refresh:()=>e==null?void 0:e.refresh(),retain(){const t={controllerRelease:e==null?void 0:e.retain(),released:!1};return o.add(t),()=>{var r;t.released||(t.released=!0,o.delete(t),(r=t.controllerRelease)==null||r.call(t),t.controllerRelease=void 0)}},retry:()=>e==null?void 0:e.retry(),subscribe(t){if(typeof t!="function")throw new TypeError("Resource listener must be a function.");s.add(t);let r=!0;return()=>{r&&(r=!1,s.delete(t))}}});return Object.freeze({allocateGeneration:()=>(d+=1,f=Object.freeze({...b,generation:d}),d),attach(t,r){if(t!==n)throw new TypeError("Feature materialization token does not match its slot.");if(e!==void 0)throw new TypeError("This resource is already bound to a live instance of the feature.");e=r,a==null||a.registerResource(n.kind,r),c=r.subscribe(l);for(const h of o)h.controllerRelease=r.retain();l();let g=!0;return(h=!0)=>{g&&(g=!1,c==null||c(),c=void 0,w(),e=void 0,h&&(a==null||a.releaseResource(r)),l())}},kind:n.kind,observe(t){a=t,e!==void 0&&t.registerResource(n.kind,e)},ref:R,token:n})}function O(i,n){if(i===void 0)return n();const o=u;u=i;try{return n()}finally{u=o}}function j(i,n){const o=u;if(o===void 0||o.token!==i)throw new TypeError("This resource belongs to another instance of the feature.");return o.attach(i,n)}function v(i){const n=u;if(n===void 0||n.token!==i)throw new TypeError("This resource belongs to another instance of the feature.");return n.allocateGeneration}function B(i){return y(i)?p.has(m(i).recipe):!1}function F(i,n){const o=T(i),s=M(o),e=E(o),a=k(s.plan,n),f=e.materializationBindings[a];if(f===void 0)throw new TypeError("Runtime ref is not a feature resource or stream.");return f.ref}export{j as attachOpeningMaterialization,F as bindFeatureResource,S as createMaterializationBinding,B as isFeatureMaterializationRef,v as openingMaterializationGenerationAllocator,A as registerFeatureMaterialization,O as withMaterializationBinding};
2
+ //# sourceMappingURL=feature-materialization-binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-materialization-binding.js","sources":["../src/feature-materialization-binding.ts"],"sourcesContent":["import type { Awaitable } from '@opetope/core/internal';\nimport { reportDetachedError } from '@opetope/core/internal';\n\nimport { getAttachmentDefinition, isLifetimeAttachmentRef } from './attachment-declaration';\nimport type { CompiledLifetimeAttachmentRecipe, LifetimeAttachmentRefIdentity } from './attachment-declaration';\nimport { getCompiledAttachmentSlot } from './compile-module-template';\nimport type { FeatureResourceRef, FeatureStreamRef } from './feature-materialization-types';\nimport type { FeatureActivityOwner } from './inspection-activity';\nimport type { ModuleInstanceRef } from './public-module';\nimport { requireActiveDefinition, requireActiveLocal, requireModuleInstance } from './public-module-state';\nimport { idleSnapshot } from './resource-snapshot';\nimport type { Resource, ResourceRequestKey, ResourceSnapshot } from './resource-types';\n\ntype ManagedMaterialization = Resource<unknown, ResourceRequestKey> & {\n readonly close: () => Awaitable<void>;\n};\n\ntype MaterializationKind = 'resource' | 'stream';\ntype MaterializationToken = { readonly kind: MaterializationKind };\ntype MaterializationLease = {\n controllerRelease: (() => void) | undefined;\n released: boolean;\n};\n\ninterface MaterializationBinding {\n readonly allocateGeneration: () => number;\n readonly attach: (\n token: MaterializationToken,\n controller: ManagedMaterialization,\n ) => (physicallyClosed?: boolean) => void;\n readonly kind: MaterializationKind;\n readonly observe: (owner: FeatureActivityOwner) => void;\n readonly ref: Resource<unknown, ResourceRequestKey>;\n readonly token: MaterializationToken;\n}\n\nconst materializationRecipes = new WeakMap<CompiledLifetimeAttachmentRecipe<unknown>, MaterializationToken>();\nlet openingBinding: MaterializationBinding | undefined;\n\nfunction registerFeatureMaterialization(\n attachment: LifetimeAttachmentRefIdentity,\n kind: MaterializationKind,\n): MaterializationToken {\n const recipe = getAttachmentDefinition(attachment).recipe;\n const token = Object.freeze({ kind });\n materializationRecipes.set(recipe, token);\n\n return token;\n}\n\nfunction createMaterializationBinding(\n recipe: CompiledLifetimeAttachmentRecipe<unknown>,\n): MaterializationBinding | undefined {\n const token = materializationRecipes.get(recipe);\n\n if (token === undefined) return undefined;\n\n const leases = new Set<MaterializationLease>();\n const listeners = new Set<() => void>();\n let controller: ManagedMaterialization | undefined;\n let activity: FeatureActivityOwner | undefined;\n let detached = idleSnapshot as ResourceSnapshot<unknown, ResourceRequestKey>;\n let generation = 0;\n let unsubscribe: (() => void) | undefined;\n const notify = (): void => {\n for (const listener of [...listeners]) {\n try {\n listener();\n } catch (error) {\n reportDetachedError(error);\n }\n }\n };\n const releaseLeases = (): void => {\n for (const lease of leases) {\n lease.controllerRelease?.();\n lease.controllerRelease = undefined;\n }\n };\n const ref: Resource<unknown, ResourceRequestKey> = Object.freeze({\n getSnapshot: () => controller?.getSnapshot() ?? detached,\n invalidate: () => controller?.invalidate(),\n refresh: () => controller?.refresh(),\n retain(): () => void {\n const lease: MaterializationLease = {\n controllerRelease: controller?.retain(),\n released: false,\n };\n leases.add(lease);\n\n return (): void => {\n if (lease.released) return;\n\n lease.released = true;\n leases.delete(lease);\n lease.controllerRelease?.();\n lease.controllerRelease = undefined;\n };\n },\n retry: () => controller?.retry(),\n subscribe(listener: () => void): () => void {\n if (typeof listener !== 'function') throw new TypeError('Resource listener must be a function.');\n\n listeners.add(listener);\n let subscribed = true;\n\n return (): void => {\n if (!subscribed) return;\n\n subscribed = false;\n listeners.delete(listener);\n };\n },\n });\n const binding: MaterializationBinding = Object.freeze({\n allocateGeneration: (): number => {\n generation += 1;\n detached = Object.freeze({ ...idleSnapshot, generation });\n\n return generation;\n },\n attach(expectedToken: MaterializationToken, next: ManagedMaterialization): (physicallyClosed?: boolean) => void {\n if (expectedToken !== token) throw new TypeError('Feature materialization token does not match its slot.');\n\n if (controller !== undefined)\n throw new TypeError('This resource is already bound to a live instance of the feature.');\n\n controller = next;\n activity?.registerResource(token.kind, next);\n unsubscribe = next.subscribe(notify);\n\n for (const lease of leases) lease.controllerRelease = next.retain();\n\n notify();\n let attached = true;\n\n return (physicallyClosed = true): void => {\n if (!attached) return;\n\n attached = false;\n unsubscribe?.();\n unsubscribe = undefined;\n\n releaseLeases();\n\n controller = undefined;\n\n if (physicallyClosed) activity?.releaseResource(next);\n\n notify();\n };\n },\n kind: token.kind,\n observe(owner: FeatureActivityOwner): void {\n activity = owner;\n\n if (controller !== undefined) owner.registerResource(token.kind, controller);\n },\n ref,\n token,\n });\n\n return binding;\n}\n\nfunction withMaterializationBinding<Result>(\n binding: MaterializationBinding | undefined,\n operation: () => Result,\n): Result {\n if (binding === undefined) return operation();\n\n const previous = openingBinding;\n openingBinding = binding;\n\n try {\n return operation();\n } finally {\n openingBinding = previous;\n }\n}\n\nfunction attachOpeningMaterialization(\n token: MaterializationToken,\n controller: ManagedMaterialization,\n): (physicallyClosed?: boolean) => void {\n const binding = openingBinding;\n\n if (binding === undefined || binding.token !== token) {\n throw new TypeError('This resource belongs to another instance of the feature.');\n }\n\n return binding.attach(token, controller);\n}\n\nfunction openingMaterializationGenerationAllocator(token: MaterializationToken): () => number {\n const binding = openingBinding;\n\n if (binding === undefined || binding.token !== token) {\n throw new TypeError('This resource belongs to another instance of the feature.');\n }\n\n return binding.allocateGeneration;\n}\n\n/** Whether an `own` ref came from `resource()` or `stream()`: the open-time `own` record binds those to `Resource`. */\nfunction isFeatureMaterializationRef(ref: object): boolean {\n if (!isLifetimeAttachmentRef(ref)) return false;\n\n return materializationRecipes.has(getAttachmentDefinition(ref).recipe);\n}\n\nfunction bindFeatureResource<const Id extends string, Data, Key extends ResourceRequestKey>(\n instance: ModuleInstanceRef<Id>,\n resource: FeatureResourceRef<Data, Key, Id> | FeatureStreamRef<Data, Key, Id>,\n): Resource<Data, Key>;\nfunction bindFeatureResource(instance: object, resource: object): Resource<unknown, ResourceRequestKey> {\n const record = requireModuleInstance(instance);\n const definition = requireActiveDefinition(record);\n const local = requireActiveLocal(record);\n const slot = getCompiledAttachmentSlot(definition.plan, resource as never);\n const binding = local.materializationBindings[slot];\n\n if (binding === undefined) throw new TypeError('Runtime ref is not a feature resource or stream.');\n\n return binding.ref;\n}\n\nexport {\n attachOpeningMaterialization,\n bindFeatureResource,\n createMaterializationBinding,\n isFeatureMaterializationRef,\n openingMaterializationGenerationAllocator,\n registerFeatureMaterialization,\n withMaterializationBinding,\n};\nexport type { MaterializationBinding };\n"],"names":["materializationRecipes","openingBinding","registerFeatureMaterialization","attachment","kind","recipe","getAttachmentDefinition","token","createMaterializationBinding","leases","listeners","controller","activity","detached","idleSnapshot","generation","unsubscribe","notify","listener","error","reportDetachedError","releaseLeases","lease","_a","ref","subscribed","expectedToken","next","attached","physicallyClosed","owner","withMaterializationBinding","binding","operation","previous","attachOpeningMaterialization","openingMaterializationGenerationAllocator","isFeatureMaterializationRef","isLifetimeAttachmentRef","bindFeatureResource","instance","resource","record","requireModuleInstance","definition","requireActiveDefinition","local","requireActiveLocal","slot","getCompiledAttachmentSlot"],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { Readable } from '@opetope/core';
2
+ import type { Awaitable } from '@opetope/core/internal';
3
+ import type { ModuleAttachmentRef } from './public-module.js';
4
+ import type { LatestBackpressure, ResourceRequestKey, ResourceRetry, ScopedRetention } from './resource-types.js';
5
+ declare const featureResourceRefBrand: unique symbol;
6
+ declare const featureStreamRefBrand: unique symbol;
7
+ type FeatureStreamDisposer = () => Awaitable<void>;
8
+ interface FeatureResourceRef<Data, Key extends ResourceRequestKey, Id extends string> extends ModuleAttachmentRef<unknown, Id> {
9
+ readonly [featureResourceRefBrand]: (data: Data, key: Key) => readonly [Data, Key];
10
+ }
11
+ interface FeatureStreamRef<Data, Key extends ResourceRequestKey, Id extends string> extends ModuleAttachmentRef<unknown, Id> {
12
+ readonly [featureStreamRefBrand]: (data: Data, key: Key) => readonly [Data, Key];
13
+ }
14
+ type FeatureResourceLoadContext<Source> = {
15
+ readonly signal: AbortSignal;
16
+ readonly source: Source;
17
+ };
18
+ type FeatureStreamConnectContext<Source, Data> = {
19
+ readonly emit: (data: Data) => void;
20
+ readonly signal: AbortSignal;
21
+ readonly source: Source;
22
+ };
23
+ type FeatureStreamConsumeContext<Source, Target, Data> = {
24
+ readonly data: Data;
25
+ readonly signal: AbortSignal;
26
+ readonly source: Source;
27
+ readonly target: Target;
28
+ };
29
+ /** The Readable that drives the request, selected from the attached source. */
30
+ type FeatureMaterializationTarget<Source, Target> = (source: Source) => Readable<Target>;
31
+ interface FeatureResourceOptions<Source, Target, Data, Key extends ResourceRequestKey> {
32
+ readonly key: (target: NonNullable<Target>) => Key;
33
+ readonly load: (target: NonNullable<Target>, context: FeatureResourceLoadContext<Source>) => Awaitable<Data>;
34
+ readonly retention?: ScopedRetention;
35
+ readonly retry?: ResourceRetry;
36
+ }
37
+ interface FeatureStreamOptions<Source, Target, Data, Key extends ResourceRequestKey> {
38
+ readonly backpressure: LatestBackpressure;
39
+ readonly connect: (target: NonNullable<Target>, context: FeatureStreamConnectContext<Source, Data>) => Awaitable<FeatureStreamDisposer | void>;
40
+ readonly consume?: (context: FeatureStreamConsumeContext<Source, NonNullable<Target>, Data>) => Awaitable<void>;
41
+ readonly key: (target: NonNullable<Target>) => Key;
42
+ readonly retention?: ScopedRetention;
43
+ readonly retry?: ResourceRetry;
44
+ }
45
+ export type { FeatureMaterializationTarget, FeatureResourceOptions, FeatureResourceRef, FeatureStreamOptions, FeatureStreamRef, };
@@ -0,0 +1,23 @@
1
+ import type { Call } from '@opetope/core';
2
+ import type { FeatureImportRef } from './feature-attachment-authoring-types.js';
3
+ import type { ModuleCallRef, ModuleInstanceRef } from './public-module.js';
4
+ type ModelDependencyMap = Readonly<Record<string, object>>;
5
+ type ModelDependencyRefs<Refs extends ModelDependencyMap, Id extends string> = {
6
+ readonly [Key in keyof Refs]: Refs[Key] extends FeatureImportRef<infer _Value, Id, infer _Mode> ? Refs[Key] : Refs[Key] extends ModuleCallRef<infer _Input, infer _Output, Id> ? Refs[Key] : never;
7
+ };
8
+ type ModelDependencyValues<Refs extends ModelDependencyMap> = {
9
+ readonly [Key in keyof Refs]: Refs[Key] extends FeatureImportRef<infer Value, infer _Id, infer _Mode> ? Value : Refs[Key] extends ModuleCallRef<infer Input, infer Output, infer _Owner> ? Call<Input, Output> : never;
10
+ };
11
+ type ModelDependency = {
12
+ readonly importKey: string;
13
+ readonly key: string;
14
+ readonly kind: 'import';
15
+ } | {
16
+ readonly key: string;
17
+ readonly kind: 'call';
18
+ readonly ref: object;
19
+ };
20
+ declare function snapshotModelDependencies(value: unknown, importKeyOf: (source: object) => string | undefined, isLocalCall: (source: object) => boolean): readonly ModelDependency[];
21
+ declare function resolveModelDependencies(dependencies: readonly ModelDependency[] | undefined, imports: Readonly<Record<string, unknown>>, instance: ModuleInstanceRef): unknown;
22
+ export { resolveModelDependencies, snapshotModelDependencies };
23
+ export type { ModelDependency, ModelDependencyMap, ModelDependencyRefs, ModelDependencyValues };
@@ -0,0 +1,2 @@
1
+ import{assertPlainRecord as f,dataEntries as l}from"./feature-record.js";import{resolveModuleCallTarget as m}from"./public-module-instance.js";function a(r,n,i){f(r,"Feature model dependencies");const e=l(r,"Feature model dependencies");if(e.length===0)throw new TypeError("Feature model dependencies must name at least one dependency.");return Object.freeze(e.map(([o,t])=>{if(typeof t=="object"&&t!==null){const d=n(t);if(d!==void 0)return Object.freeze({importKey:d,key:o,kind:"import"});if(i(t))return Object.freeze({key:o,kind:"call",ref:t})}throw new TypeError(`Feature model dependency ${o} must be an import or Call ref of this definition.`)}))}function p(r,n,i){if(r!==void 0)return Object.freeze(Object.fromEntries(r.map(e=>[e.key,e.kind==="import"?n[e.importKey]:m(i,e.ref)])))}export{p as resolveModelDependencies,a as snapshotModelDependencies};
2
+ //# sourceMappingURL=feature-model-dependencies.js.map