@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,39 @@
1
+ import type { Call } from '@opetope/core';
2
+ import type { CallOnceOption, CallSingleFlightOption } from './call-option-snapshot.js';
3
+ import type { ExactInput } from './feature-record.js';
4
+ import type { ModuleAttachmentRef, ModuleAttachmentRefIdentity, ModuleCallContext, ModuleCallRef } from './public-module.js';
5
+ declare const featureImportedAttachmentBrand: unique symbol;
6
+ declare const featureCallLaneBrand: unique symbol;
7
+ type FeatureExposureMode = 'adapter-method' | 'feature-call-target' | 'optional-feature-call-target';
8
+ interface FeatureImportedAttachmentRef<Value, Id extends string, ImportValues extends object, Mode extends FeatureExposureMode> extends ModuleAttachmentRef<Value, Id> {
9
+ readonly [featureImportedAttachmentBrand]: (imports: ImportValues, mode: Mode) => readonly [ImportValues, Mode];
10
+ }
11
+ interface FeatureCallLaneRef<Id extends string, Within extends ModuleAttachmentRefIdentity<Id> = ModuleAttachmentRefIdentity<Id>> {
12
+ readonly [featureCallLaneBrand]: (within: Within) => Within;
13
+ }
14
+ interface FeatureCallLaneOptions<Within> {
15
+ readonly within: Within;
16
+ }
17
+ type FeatureCallLaneBuilder<Id extends string> = <Within extends ModuleAttachmentRefIdentity<Id>, const Options extends FeatureCallLaneOptions<Within>>(options: ExactInput<Options, FeatureCallLaneOptions<Within>>) => FeatureCallLaneRef<Id, Within>;
18
+ type FeatureNestedCall<Id extends string> = <Input, Output>(target: Call<Input, Output> | ModuleCallRef<Input, Output, Id>, input: Input) => Promise<Output>;
19
+ type FeatureCallContext<Within, Id extends string> = Within extends FeatureImportedAttachmentRef<infer Value, infer _Id, infer BoundImportValues, infer _Mode> ? ModuleCallContext<Value> & {
20
+ readonly imports: BoundImportValues;
21
+ readonly invoke: FeatureNestedCall<Id>;
22
+ } : Within extends ModuleAttachmentRef<infer Value, infer _Id> ? ModuleCallContext<Value> & {
23
+ readonly invoke: FeatureNestedCall<Id>;
24
+ } : never;
25
+ type FeatureCallScheduling<Id extends string, Within extends ModuleAttachmentRefIdentity<Id>> = {
26
+ readonly lane?: FeatureCallLaneRef<Id, Within>;
27
+ readonly policy?: 'latest' | 'queue';
28
+ } | {
29
+ readonly lane?: never;
30
+ readonly policy: 'parallel';
31
+ };
32
+ type FeatureCallOptions<Input, Output, Within extends ModuleAttachmentRefIdentity<Id>, Id extends string> = {
33
+ readonly once?: CallOnceOption<Input>;
34
+ readonly run: (input: Input, context: FeatureCallContext<Within, Id>) => Output | PromiseLike<Output>;
35
+ readonly singleFlight?: CallSingleFlightOption<Input>;
36
+ readonly within: Within;
37
+ } & FeatureCallScheduling<Id, Within>;
38
+ type FeatureCallBuilder<Id extends string> = <Input, Output, Within extends ModuleAttachmentRefIdentity<Id>, const Options extends FeatureCallOptions<Input, Output, Within, Id>>(options: ExactInput<Options, FeatureCallOptions<Input, Output, Within, Id>>) => ModuleCallRef<Input, Output, Id>;
39
+ export type { FeatureCallBuilder, FeatureCallLaneBuilder, FeatureExposureMode, FeatureImportedAttachmentRef };
@@ -0,0 +1,18 @@
1
+ import type { CompiledAttachmentCallPolicy } from './attachment-call-declaration.js';
2
+ type AttachmentOwnershipCheck = (value: object) => boolean;
3
+ type FeatureCallLaneRecord = Readonly<{
4
+ id: string;
5
+ within: object;
6
+ }>;
7
+ type FeatureCallOperation = Readonly<{
8
+ policy: CompiledAttachmentCallPolicy<unknown>;
9
+ run: (input: unknown, context: unknown) => unknown;
10
+ within: object;
11
+ }>;
12
+ type FeatureCallAuthoring = Readonly<{
13
+ lane: (options: unknown) => object;
14
+ snapshotCall: (options: unknown) => FeatureCallOperation;
15
+ }>;
16
+ declare function createFeatureCallAuthoring(featureId: string, ownsAttachment: AttachmentOwnershipCheck): FeatureCallAuthoring;
17
+ declare function getFeatureCallLaneRecord(lane: object): FeatureCallLaneRecord;
18
+ export { createFeatureCallAuthoring, getFeatureCallLaneRecord };
@@ -0,0 +1,2 @@
1
+ import{snapshotCallPolicy as p,snapshotCallOnce as w,snapshotCallSingleFlight as d}from"./call-option-snapshot.js";import{assertPlainRecord as f,dataEntries as h}from"./feature-record.js";const y=new Set(["lane","once","policy","run","singleFlight","within"]),s=new WeakMap;function F(e,n,r){for(const[t]of e)if(!n.has(t))throw new TypeError(`${r} contains unknown field ${t}.`)}function g(e,n){if(typeof e!="function")throw new TypeError(`${n} must be a function.`);return e}function b(e,n){if(typeof e!="object"||e===null||!n(e))throw new TypeError("own.call needs within from the same feature: pass what own.attach returned here.");return e}function m(e,n,r){if(e===void 0)return;const t=typeof e=="object"&&e!==null?s.get(e):void 0;if(t===void 0||t.within!==n)throw new TypeError("own.call takes a lane from own.lane declared on the same within.");if(r==="parallel")throw new TypeError("Parallel feature calls cannot declare a call lane.");return e}function E(e,n,r,t){const o={policy:e};return r!==void 0&&(o.once=r),n!==void 0&&(o.lane=n),t!==void 0&&(o.singleFlight=t),Object.freeze(o)}function O(e,n){f(e,"Feature call options");const r=h(e,"Feature call options");F(r,y,"Feature call options");const t=Object.fromEntries(r),o=b(t.within,n),i=p(t.policy,"Feature call"),l=m(t.lane,o,i),a=w(t.once,"Feature call"),c=d(t.singleFlight,"Feature call");return Object.freeze({policy:E(i,l,a,c),run:g(t.run,"Feature call run"),within:o})}function j(e,n){let r=0;return Object.freeze({lane:i=>{var u;f(i,"Feature lane options");const l=h(i,"Feature lane options");if(l.length!==1||((u=l[0])==null?void 0:u[0])!=="within")throw new TypeError("Feature lane requires the exact within field.");const a=l[0][1];if(typeof a!="object"||a===null||!n(a))throw new TypeError("Feature lane must bind within a source this feature attached.");r+=1;const c=Object.freeze({});return s.set(c,Object.freeze({id:`${e}.lane.${String(r)}`,within:a})),c},snapshotCall:i=>O(i,n)})}function C(e){const n=s.get(e);if(n===void 0)throw new TypeError("Feature lane is not authentic.");return n}export{j as createFeatureCallAuthoring,C as getFeatureCallLaneRecord};
2
+ //# sourceMappingURL=feature-call.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-call.js","sources":["../src/feature-call.ts"],"sourcesContent":["import type { CompiledAttachmentCallPolicy } from './attachment-call-declaration';\nimport { snapshotCallOnce, snapshotCallPolicy, snapshotCallSingleFlight } from './call-option-snapshot';\nimport type { CallPolicy } from './call-option-snapshot';\nimport { assertPlainRecord, dataEntries } from './feature-record';\n\ntype AttachmentOwnershipCheck = (value: object) => boolean;\n\ntype FeatureCallLaneRecord = Readonly<{\n id: string;\n within: object;\n}>;\n\ntype FeatureCallOperation = Readonly<{\n policy: CompiledAttachmentCallPolicy<unknown>;\n run: (input: unknown, context: unknown) => unknown;\n within: object;\n}>;\n\ntype MutableCallPolicy = {\n lane?: object;\n once?: NonNullable<CompiledAttachmentCallPolicy<unknown>['once']>;\n policy: CompiledAttachmentCallPolicy<unknown>['policy'];\n singleFlight?: NonNullable<CompiledAttachmentCallPolicy<unknown>['singleFlight']>;\n};\n\ntype FeatureCallAuthoring = Readonly<{\n lane: (options: unknown) => object;\n snapshotCall: (options: unknown) => FeatureCallOperation;\n}>;\n\nconst callOptionKeys = new Set<PropertyKey>(['lane', 'once', 'policy', 'run', 'singleFlight', 'within']);\nconst laneRecords = new WeakMap<object, FeatureCallLaneRecord>();\n\nfunction assertAllowedKeys(\n entries: readonly (readonly [string, unknown])[],\n allowed: ReadonlySet<PropertyKey>,\n label: string,\n) {\n for (const [key] of entries) {\n if (!allowed.has(key)) throw new TypeError(`${label} contains unknown field ${key}.`);\n }\n}\n\nfunction requireFunction<Callback>(value: unknown, label: string): Callback {\n if (typeof value !== 'function') throw new TypeError(`${label} must be a function.`);\n\n return value as Callback;\n}\n\nfunction requireOwnedAttachment(value: unknown, ownsAttachment: AttachmentOwnershipCheck): object {\n if (typeof value !== 'object' || value === null || !ownsAttachment(value)) {\n throw new TypeError('own.call needs within from the same feature: pass what own.attach returned here.');\n }\n\n return value;\n}\n\nfunction snapshotLane(value: unknown, within: object, policy: CallPolicy): object | undefined {\n if (value === undefined) return undefined;\n\n const record = typeof value === 'object' && value !== null ? laneRecords.get(value) : undefined;\n\n if (record === undefined || record.within !== within) {\n throw new TypeError('own.call takes a lane from own.lane declared on the same within.');\n }\n\n if (policy === 'parallel') throw new TypeError('Parallel feature calls cannot declare a call lane.');\n\n return value as object;\n}\n\nfunction createCallPolicyRecipe(\n policy: CallPolicy,\n lane: object | undefined,\n once: CompiledAttachmentCallPolicy<unknown>['once'],\n singleFlight: CompiledAttachmentCallPolicy<unknown>['singleFlight'],\n): CompiledAttachmentCallPolicy<unknown> {\n const callPolicy: MutableCallPolicy = { policy };\n\n if (once !== undefined) callPolicy.once = once;\n\n if (lane !== undefined) callPolicy.lane = lane;\n\n if (singleFlight !== undefined) callPolicy.singleFlight = singleFlight;\n\n return Object.freeze(callPolicy);\n}\n\nfunction snapshotFeatureCallOperation(\n options: unknown,\n ownsAttachment: AttachmentOwnershipCheck,\n): FeatureCallOperation {\n assertPlainRecord(options, 'Feature call options');\n const entries = dataEntries(options, 'Feature call options');\n assertAllowedKeys(entries, callOptionKeys, 'Feature call options');\n const source = Object.fromEntries(entries);\n const within = requireOwnedAttachment(source['within'], ownsAttachment);\n const policy = snapshotCallPolicy(source['policy'], 'Feature call');\n const lane = snapshotLane(source['lane'], within, policy);\n const once = snapshotCallOnce(source['once'], 'Feature call');\n const singleFlight = snapshotCallSingleFlight(source['singleFlight'], 'Feature call');\n\n return Object.freeze({\n policy: createCallPolicyRecipe(policy, lane, once, singleFlight),\n run: requireFunction<FeatureCallOperation['run']>(source['run'], 'Feature call run'),\n within,\n });\n}\n\nfunction createFeatureCallAuthoring(featureId: string, ownsAttachment: AttachmentOwnershipCheck): FeatureCallAuthoring {\n let nextLane = 0;\n const lane = (options: unknown): object => {\n assertPlainRecord(options, 'Feature lane options');\n const entries = dataEntries(options, 'Feature lane options');\n\n if (entries.length !== 1 || entries[0]?.[0] !== 'within') {\n throw new TypeError('Feature lane requires the exact within field.');\n }\n\n const within = entries[0][1];\n\n if (typeof within !== 'object' || within === null || !ownsAttachment(within)) {\n throw new TypeError('Feature lane must bind within a source this feature attached.');\n }\n\n nextLane += 1;\n const ref = Object.freeze({});\n laneRecords.set(ref, Object.freeze({ id: `${featureId}.lane.${String(nextLane)}`, within }));\n\n return ref;\n };\n const snapshotCall = (options: unknown): FeatureCallOperation =>\n snapshotFeatureCallOperation(options, ownsAttachment);\n\n return Object.freeze({ lane, snapshotCall });\n}\n\nfunction getFeatureCallLaneRecord(lane: object): FeatureCallLaneRecord {\n const record = laneRecords.get(lane);\n\n if (record === undefined) throw new TypeError('Feature lane is not authentic.');\n\n return record;\n}\n\nexport { createFeatureCallAuthoring, getFeatureCallLaneRecord };\n"],"names":["callOptionKeys","laneRecords","assertAllowedKeys","entries","allowed","label","key","requireFunction","value","requireOwnedAttachment","ownsAttachment","snapshotLane","within","policy","record","createCallPolicyRecipe","lane","once","singleFlight","callPolicy","snapshotFeatureCallOperation","options","assertPlainRecord","dataEntries","source","snapshotCallPolicy","snapshotCallOnce","snapshotCallSingleFlight","createFeatureCallAuthoring","featureId","nextLane","_a","ref","getFeatureCallLaneRecord"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { FeatureCallOperation, FeatureExposureMode } from './feature-authoring-types.js';
2
+ type CallsOperationEntry = readonly [key: string, operation: FeatureCallOperation];
3
+ /** `own.calls` and `ctx.calls` select keys by the same rule, so the label is the only thing they do not share (D143). */
4
+ declare function snapshotCallsKeys(value: unknown, label: string): readonly string[];
5
+ declare function createCallsOperationEntries(within: object, keysValue: unknown, mode: FeatureExposureMode): readonly CallsOperationEntry[];
6
+ export { createCallsOperationEntries, snapshotCallsKeys };
@@ -0,0 +1,2 @@
1
+ import{CallError as i}from"@opetope/core";import{isCallTarget as l}from"@opetope/core/internal";const u=/^[A-Za-z][A-Za-z0-9]*$/;function c(r,e){if(!Array.isArray(r)||r.length===0)throw new TypeError(`${e} keys must be a non-empty array.`);const n=[],o=new Set;for(const t of r){if(typeof t!="string"||!u.test(t))throw new TypeError(`${e} key ${String(t)} must be one identifier segment.`);if(o.has(t))throw new TypeError(`${e} key ${t} is duplicated.`);o.add(t),n.push(t)}return Object.freeze(n)}function f(r,e){return Object.freeze({run:(n,o)=>{const t=o,s=t.source;if(typeof s[e]!="function")throw new TypeError(`Feature calls adapter field ${e} is not a method.`);return s[e](n,t.signal)},within:r})}function p(r,e){return Object.freeze({run:(n,o)=>{const t=o,a=t.source[e];return t.invoke(a,n)},within:r})}function d(r,e){return Object.freeze({run:(n,o)=>{const t=o,a=t.source.getSnapshot();if(a.kind!=="found")throw new i("unavailable",`Optional feature call ${e} has no live provider.`);const s=a.value[e];if(!l(s))throw new i("unavailable",`Optional feature call ${e} is not exported by its provider.`);return t.invoke(s,n)},within:r})}function g(r,e,n){return n==="optional-feature-call-target"?d(r,e):n==="feature-call-target"?p(r,e):f(r,e)}function h(r,e,n){const t=c(e,"Feature calls").map(a=>[a,g(r,a,n)]);return Object.freeze(t)}export{h as createCallsOperationEntries,c as snapshotCallsKeys};
2
+ //# sourceMappingURL=feature-calls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-calls.js","sources":["../src/feature-calls.ts"],"sourcesContent":["import { CallError } from '@opetope/core';\nimport type { Call, Lookup, Readable } from '@opetope/core';\nimport { isCallTarget } from '@opetope/core/internal';\n\nimport type { FeatureCallOperation, FeatureExposureMode } from './feature-authoring-types';\n\ntype CallsOperationEntry = readonly [key: string, operation: FeatureCallOperation];\n\nconst identifierKeyPattern = /^[A-Za-z][A-Za-z0-9]*$/;\n\n/** `own.calls` and `ctx.calls` select keys by the same rule, so the label is the only thing they do not share (D143). */\nfunction snapshotCallsKeys(value: unknown, label: string): readonly string[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new TypeError(`${label} keys must be a non-empty array.`);\n }\n\n const keys: string[] = [];\n const seen = new Set<string>();\n\n for (const key of value) {\n if (typeof key !== 'string' || !identifierKeyPattern.test(key)) {\n throw new TypeError(`${label} key ${String(key)} must be one identifier segment.`);\n }\n\n if (seen.has(key)) throw new TypeError(`${label} key ${key} is duplicated.`);\n\n seen.add(key);\n keys.push(key);\n }\n\n return Object.freeze(keys);\n}\n\nfunction createAdapterMethodOperation(within: object, key: string): FeatureCallOperation {\n return Object.freeze({\n run: (input: unknown, context: unknown): unknown => {\n const callContext = context as {\n readonly signal: AbortSignal;\n readonly source: Readonly<Record<string, unknown>>;\n };\n const adapter = callContext.source;\n const methods = adapter as Readonly<Record<string, (input: unknown, signal: AbortSignal) => unknown>>;\n\n if (typeof methods[key] !== 'function') {\n throw new TypeError(`Feature calls adapter field ${key} is not a method.`);\n }\n\n return methods[key](input, callContext.signal);\n },\n within,\n });\n}\n\nfunction createFeatureCallTargetOperation(within: object, key: string): FeatureCallOperation {\n return Object.freeze({\n run: (input: unknown, context: unknown): unknown => {\n const callContext = context as {\n readonly invoke: (target: Call<unknown, unknown>, input: unknown) => Promise<unknown>;\n readonly source: Readonly<Record<string, unknown>>;\n };\n const target = callContext.source[key] as Call<unknown, unknown>;\n\n return callContext.invoke(target, input);\n },\n within,\n });\n}\n\n/**\n * D187: the call half of a weak edge. The provider is read at invocation, so the call reaches the instance that is\n * there now and travels with the provider's own fence; while nothing provides it, the call answers `unavailable`\n * exactly as a weak port does (D105, D138).\n */\nfunction createOptionalFeatureCallTargetOperation(within: object, key: string): FeatureCallOperation {\n return Object.freeze({\n run: (input: unknown, context: unknown): unknown => {\n const callContext = context as {\n readonly invoke: (target: Call<unknown, unknown>, input: unknown) => Promise<unknown>;\n readonly source: Readable<Lookup<Readonly<Record<string, unknown>>>>;\n };\n const lookup = callContext.source.getSnapshot();\n\n if (lookup.kind !== 'found') {\n throw new CallError('unavailable', `Optional feature call ${key} has no live provider.`);\n }\n\n const target = lookup.value[key];\n\n if (!isCallTarget(target)) {\n throw new CallError('unavailable', `Optional feature call ${key} is not exported by its provider.`);\n }\n\n return callContext.invoke(target, input);\n },\n within,\n });\n}\n\nfunction selectCallsOperation(within: object, key: string, mode: FeatureExposureMode): FeatureCallOperation {\n if (mode === 'optional-feature-call-target') return createOptionalFeatureCallTargetOperation(within, key);\n\n return mode === 'feature-call-target'\n ? createFeatureCallTargetOperation(within, key)\n : createAdapterMethodOperation(within, key);\n}\n\nfunction createCallsOperationEntries(\n within: object,\n keysValue: unknown,\n mode: FeatureExposureMode,\n): readonly CallsOperationEntry[] {\n const keys = snapshotCallsKeys(keysValue, 'Feature calls');\n const entries = keys.map((key): CallsOperationEntry => [key, selectCallsOperation(within, key, mode)]);\n\n return Object.freeze(entries);\n}\n\nexport { createCallsOperationEntries, snapshotCallsKeys };\n"],"names":["identifierKeyPattern","snapshotCallsKeys","value","label","keys","seen","key","createAdapterMethodOperation","within","input","context","callContext","methods","createFeatureCallTargetOperation","target","createOptionalFeatureCallTargetOperation","lookup","CallError","isCallTarget","selectCallsOperation","mode","createCallsOperationEntries","keysValue","entries"],"mappings":"gGAQA,MAAMA,EAAuB,yBAG7B,SAASC,EAAkBC,EAAgBC,EAAa,CACtD,GAAI,CAAC,MAAM,QAAQD,CAAK,GAAKA,EAAM,SAAW,EAC5C,MAAM,IAAI,UAAU,GAAGC,CAAK,kCAAkC,EAGhE,MAAMC,EAAiB,CAAA,EACjBC,EAAO,IAAI,IAEjB,UAAWC,KAAOJ,EAAO,CACvB,GAAI,OAAOI,GAAQ,UAAY,CAACN,EAAqB,KAAKM,CAAG,EAC3D,MAAM,IAAI,UAAU,GAAGH,CAAK,QAAQ,OAAOG,CAAG,CAAC,kCAAkC,EAGnF,GAAID,EAAK,IAAIC,CAAG,EAAG,MAAM,IAAI,UAAU,GAAGH,CAAK,QAAQG,CAAG,iBAAiB,EAE3ED,EAAK,IAAIC,CAAG,EACZF,EAAK,KAAKE,CAAG,CACf,CAEA,OAAO,OAAO,OAAOF,CAAI,CAC3B,CAEA,SAASG,EAA6BC,EAAgBF,EAAW,CAC/D,OAAO,OAAO,OAAO,CACnB,IAAK,CAACG,EAAgBC,IAA6B,CACjD,MAAMC,EAAcD,EAKdE,EADUD,EAAY,OAG5B,GAAI,OAAOC,EAAQN,CAAG,GAAM,WAC1B,MAAM,IAAI,UAAU,+BAA+BA,CAAG,mBAAmB,EAG3E,OAAOM,EAAQN,CAAG,EAAEG,EAAOE,EAAY,MAAM,CAC/C,EACA,OAAAH,CACD,CAAA,CACH,CAEA,SAASK,EAAiCL,EAAgBF,EAAW,CACnE,OAAO,OAAO,OAAO,CACnB,IAAK,CAACG,EAAgBC,IAA6B,CACjD,MAAMC,EAAcD,EAIdI,EAASH,EAAY,OAAOL,CAAG,EAErC,OAAOK,EAAY,OAAOG,EAAQL,CAAK,CACzC,EACA,OAAAD,CACD,CAAA,CACH,CAOA,SAASO,EAAyCP,EAAgBF,EAAW,CAC3E,OAAO,OAAO,OAAO,CACnB,IAAK,CAACG,EAAgBC,IAA6B,CACjD,MAAMC,EAAcD,EAIdM,EAASL,EAAY,OAAO,YAAW,EAE7C,GAAIK,EAAO,OAAS,QAClB,MAAM,IAAIC,EAAU,cAAe,yBAAyBX,CAAG,wBAAwB,EAGzF,MAAMQ,EAASE,EAAO,MAAMV,CAAG,EAE/B,GAAI,CAACY,EAAaJ,CAAM,EACtB,MAAM,IAAIG,EAAU,cAAe,yBAAyBX,CAAG,mCAAmC,EAGpG,OAAOK,EAAY,OAAOG,EAAQL,CAAK,CACzC,EACA,OAAAD,CACD,CAAA,CACH,CAEA,SAASW,EAAqBX,EAAgBF,EAAac,EAAyB,CAClF,OAAIA,IAAS,+BAAuCL,EAAyCP,EAAQF,CAAG,EAEjGc,IAAS,sBACZP,EAAiCL,EAAQF,CAAG,EAC5CC,EAA6BC,EAAQF,CAAG,CAC9C,CAEA,SAASe,EACPb,EACAc,EACAF,EAAyB,CAGzB,MAAMG,EADOtB,EAAkBqB,EAAW,eAAe,EACpC,IAAKhB,GAA6B,CAACA,EAAKa,EAAqBX,EAAQF,EAAKc,CAAI,CAAC,CAAC,EAErG,OAAO,OAAO,OAAOG,CAAO,CAC9B"}
@@ -0,0 +1,70 @@
1
+ import type { Call, DeclarationId, Lookup, Readable } from '@opetope/core';
2
+ import type { IsAny } from '@opetope/core/internal';
3
+ declare const featureContractBrand: unique symbol;
4
+ declare const featureContractValueBrand: unique symbol;
5
+ declare const hostContractBrand: unique symbol;
6
+ declare const onDemandHostContractBrand: unique symbol;
7
+ declare const optionalHostContractBrand: unique symbol;
8
+ interface HostContractIdentity {
9
+ readonly [hostContractBrand]: true;
10
+ readonly id: DeclarationId;
11
+ }
12
+ interface HostContract<Value> extends HostContractIdentity {
13
+ readonly [featureContractValueBrand]: (value: Value) => Value;
14
+ }
15
+ interface FeatureContractIdentity extends HostContractIdentity {
16
+ readonly [featureContractBrand]: true;
17
+ }
18
+ interface FeatureContract<Value> extends FeatureContractIdentity, HostContract<Value> {
19
+ }
20
+ interface OnDemandHostContract<Contract> extends HostContractIdentity {
21
+ readonly [onDemandHostContractBrand]: (contract: Contract) => Contract;
22
+ }
23
+ /**
24
+ * A weak edge: the consumer may outlive the provider, so the value arrives as a projection that reports `missing`
25
+ * while the provider is closed or absent from the application (D105).
26
+ */
27
+ interface OptionalHostContract<Contract> extends HostContractIdentity {
28
+ readonly [optionalHostContractBrand]: (contract: Contract) => Contract;
29
+ }
30
+ /** A feature definition is its own import declaration: the importer names the definition, not a third object. */
31
+ interface FeatureImportSource {
32
+ readonly exports: FeatureContractIdentity;
33
+ }
34
+ type FeatureImportDeclaration = FeatureImportSource | HostContractIdentity;
35
+ /** One wrapper per declaration: `optional` is always the outer word, so it never accepts its own result (D91). */
36
+ type DirectImportDeclaration<Declaration> = Declaration & {
37
+ readonly [optionalHostContractBrand]?: never;
38
+ };
39
+ type FeatureImportRecord = Readonly<Record<string, FeatureImportDeclaration>>;
40
+ /** How an import value is bound: with the instance that owns it, or lazily on first use (D110). */
41
+ type ImportDemand = 'instance' | 'onDemand';
42
+ type FeatureImportValue<Token> = Token extends OptionalHostContract<infer Optional> ? Readable<Lookup<FeatureImportValue<Optional>>> : Token extends OnDemandHostContract<infer Contract> ? FeatureImportValue<Contract> : Token extends {
43
+ readonly exports: FeatureContract<infer Value>;
44
+ } ? Readonly<Value> : Token extends HostContract<infer Value> ? Readonly<Value> : never;
45
+ type FeatureImportValues<Imports extends FeatureImportRecord> = {
46
+ readonly [Key in keyof Imports]: FeatureImportValue<Imports[Key]>;
47
+ };
48
+ type IsUnion<Value, Whole = Value> = IsAny<Value> extends true ? false : Value extends Whole ? ([Whole] extends [Value] ? false : true) : never;
49
+ type ContractId<Value> = IsAny<Value> extends true ? never : unknown extends Value ? never : Value extends readonly unknown[] | ((...arguments_: never[]) => unknown) ? never : Value extends object ? string extends keyof Value ? never : number extends keyof Value ? never : symbol extends keyof Value ? never : string : never;
50
+ type ExactCallTarget<Value> = IsAny<Value> extends true ? never : Value extends Call<infer Input, infer Output> ? IsAny<Input> extends true ? never : IsAny<Output> extends true ? never : Value : never;
51
+ type FeatureContractShape<Value> = {
52
+ readonly [Key in keyof Value]-?: ExactCallTarget<Value[Key]>;
53
+ };
54
+ type FeatureContractId<Value> = true extends IsUnion<Value> ? never : ([Value] extends [FeatureContractShape<Value>] ? string : never) & ContractId<Value>;
55
+ declare function defineHostContract<Value>(id: ContractId<Value>): HostContract<Value>;
56
+ declare function defineFeatureContract<Value>(id: FeatureContractId<Value>): FeatureContract<Value>;
57
+ declare function isFeatureContract(value: unknown): value is FeatureContractIdentity;
58
+ declare function importedContract(source: unknown): HostContractIdentity | undefined;
59
+ /** `onDemand` is host-only: a feature edge is either hard or weak, and weakness is spelled `optional` (D105). */
60
+ declare function onDemand<Value>(source: HostContract<Value> & {
61
+ readonly [onDemandHostContractBrand]?: never;
62
+ }): OnDemandHostContract<HostContract<Value>>;
63
+ /** `optional` is the weak edge: the provider may be closed or absent, and the consumer reads a projection (D105). */
64
+ declare function optionalImport<Declaration extends FeatureImportDeclaration>(source: DirectImportDeclaration<Declaration>): OptionalHostContract<Declaration>;
65
+ declare function getDirectHostContract(contract: HostContractIdentity): HostContractIdentity;
66
+ declare function getHostContractDemand(contract: HostContractIdentity): ImportDemand;
67
+ /** Tolerant on purpose: the caller may hold any import declaration, including a feature definition itself. */
68
+ declare function isHostContractOptional(declaration: unknown): boolean;
69
+ export { defineFeatureContract, defineHostContract, getDirectHostContract, getHostContractDemand, importedContract, isFeatureContract, isHostContractOptional, onDemand, optionalImport, };
70
+ export type { DirectImportDeclaration, FeatureContract, FeatureContractIdentity, FeatureImportDeclaration, FeatureImportRecord, FeatureImportValues, HostContract, ImportDemand, OnDemandHostContract, OptionalHostContract, };
@@ -0,0 +1,2 @@
1
+ import{declarationId as p}from"@opetope/core";const d=new WeakSet,r=new WeakSet,c=new WeakMap,o=new WeakMap;function f(t){const n=Object.freeze({id:p(t)});return r.add(n),n}function h(t){const n=f(t);return d.add(n),n}function a(t){return typeof t=="object"&&t!==null&&r.has(t)}function u(t){const n=o.get(t)??t;return c.get(n)??n}function i(t){return typeof t!="object"||t===null?!1:d.has(u(t))}function s(t){if(a(t))return t;if(typeof t!="object"||t===null)return;const n=Reflect.get(t,"exports");return i(n)?n:void 0}function w(t){const n=s(t);if(n===void 0||c.has(n)||o.has(n))throw new TypeError("onDemand requires one direct authentic host contract.");if(i(n))throw new TypeError("onDemand wraps a host contract; a feature edge is hard or optional.");const e=Object.freeze({id:n.id});return r.add(e),c.set(e,n),e}function l(t){const n=s(t);if(n===void 0||o.has(n))throw new TypeError("optional requires one direct authentic import declaration.");const e=Object.freeze({id:n.id});return r.add(e),o.set(e,n),e}function m(t){if(!a(t))throw new TypeError("Import contract is not authentic.");return u(t)}function C(t){if(!a(t))throw new TypeError("Import contract is not authentic.");return c.has(o.get(t)??t)?"onDemand":"instance"}function y(t){return typeof t=="object"&&t!==null&&o.has(t)}export{h as defineFeatureContract,f as defineHostContract,m as getDirectHostContract,C as getHostContractDemand,s as importedContract,i as isFeatureContract,y as isHostContractOptional,w as onDemand,l as optionalImport};
2
+ //# sourceMappingURL=feature-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-contract.js","sources":["../src/feature-contract.ts"],"sourcesContent":["import { declarationId } from '@opetope/core';\nimport type { Call, DeclarationId, Lookup, Readable } from '@opetope/core';\nimport type { IsAny } from '@opetope/core/internal';\n\ndeclare const featureContractBrand: unique symbol;\ndeclare const featureContractValueBrand: unique symbol;\ndeclare const hostContractBrand: unique symbol;\ndeclare const onDemandHostContractBrand: unique symbol;\ndeclare const optionalHostContractBrand: unique symbol;\n\ninterface HostContractIdentity {\n readonly [hostContractBrand]: true;\n readonly id: DeclarationId;\n}\n\ninterface HostContract<Value> extends HostContractIdentity {\n readonly [featureContractValueBrand]: (value: Value) => Value;\n}\n\ninterface FeatureContractIdentity extends HostContractIdentity {\n readonly [featureContractBrand]: true;\n}\n\ninterface FeatureContract<Value> extends FeatureContractIdentity, HostContract<Value> {}\n\ninterface OnDemandHostContract<Contract> extends HostContractIdentity {\n readonly [onDemandHostContractBrand]: (contract: Contract) => Contract;\n}\n\n/**\n * A weak edge: the consumer may outlive the provider, so the value arrives as a projection that reports `missing`\n * while the provider is closed or absent from the application (D105).\n */\ninterface OptionalHostContract<Contract> extends HostContractIdentity {\n readonly [optionalHostContractBrand]: (contract: Contract) => Contract;\n}\n\n/** A feature definition is its own import declaration: the importer names the definition, not a third object. */\ninterface FeatureImportSource {\n readonly exports: FeatureContractIdentity;\n}\n\ntype FeatureImportDeclaration = FeatureImportSource | HostContractIdentity;\n/** One wrapper per declaration: `optional` is always the outer word, so it never accepts its own result (D91). */\ntype DirectImportDeclaration<Declaration> = Declaration & { readonly [optionalHostContractBrand]?: never };\ntype FeatureImportRecord = Readonly<Record<string, FeatureImportDeclaration>>;\n/** How an import value is bound: with the instance that owns it, or lazily on first use (D110). */\ntype ImportDemand = 'instance' | 'onDemand';\ntype FeatureImportValue<Token> =\n Token extends OptionalHostContract<infer Optional>\n ? Readable<Lookup<FeatureImportValue<Optional>>>\n : Token extends OnDemandHostContract<infer Contract>\n ? FeatureImportValue<Contract>\n : Token extends { readonly exports: FeatureContract<infer Value> }\n ? Readonly<Value>\n : Token extends HostContract<infer Value>\n ? Readonly<Value>\n : never;\ntype FeatureImportValues<Imports extends FeatureImportRecord> = {\n readonly [Key in keyof Imports]: FeatureImportValue<Imports[Key]>;\n};\n\nconst featureContracts = new WeakSet<object>();\nconst hostContracts = new WeakSet<object>();\nconst onDemandHostContracts = new WeakMap<object, HostContractIdentity>();\nconst optionalHostContracts = new WeakMap<object, HostContractIdentity>();\n\ntype IsUnion<Value, Whole = Value> =\n IsAny<Value> extends true ? false : Value extends Whole ? ([Whole] extends [Value] ? false : true) : never;\ntype ContractId<Value> =\n IsAny<Value> extends true\n ? never\n : unknown extends Value\n ? never\n : Value extends readonly unknown[] | ((...arguments_: never[]) => unknown)\n ? never\n : Value extends object\n ? string extends keyof Value\n ? never\n : number extends keyof Value\n ? never\n : symbol extends keyof Value\n ? never\n : string\n : never;\ntype ExactCallTarget<Value> =\n IsAny<Value> extends true\n ? never\n : Value extends Call<infer Input, infer Output>\n ? IsAny<Input> extends true\n ? never\n : IsAny<Output> extends true\n ? never\n : Value\n : never;\ntype FeatureContractShape<Value> = { readonly [Key in keyof Value]-?: ExactCallTarget<Value[Key]> };\ntype FeatureContractId<Value> =\n true extends IsUnion<Value>\n ? never\n : ([Value] extends [FeatureContractShape<Value>] ? string : never) & ContractId<Value>;\n\nfunction defineHostContract<Value>(id: ContractId<Value>): HostContract<Value> {\n const contract = Object.freeze({ id: declarationId(id) }) as HostContract<Value>;\n hostContracts.add(contract);\n\n return contract;\n}\n\nfunction defineFeatureContract<Value>(id: FeatureContractId<Value>): FeatureContract<Value> {\n const contract = defineHostContract<Value>(id) as FeatureContract<Value>;\n featureContracts.add(contract);\n\n return contract;\n}\n\nfunction isHostContract(value: unknown): value is HostContract<unknown> {\n return typeof value === 'object' && value !== null && hostContracts.has(value);\n}\n\n/** One declaration may wrap another — `optional(onDemand(x))` — so resolution unwraps both markers in that order. */\nfunction declaredContract(value: object): object {\n const declared = optionalHostContracts.get(value) ?? value;\n\n return onDemandHostContracts.get(declared) ?? declared;\n}\n\nfunction isFeatureContract(value: unknown): value is FeatureContractIdentity {\n if (typeof value !== 'object' || value === null) return false;\n\n return featureContracts.has(declaredContract(value));\n}\n\nfunction importedContract(source: unknown): HostContractIdentity | undefined {\n if (isHostContract(source)) return source;\n\n if (typeof source !== 'object' || source === null) return undefined;\n\n const exported: unknown = Reflect.get(source, 'exports');\n\n return isFeatureContract(exported) ? exported : undefined;\n}\n\n/** `onDemand` is host-only: a feature edge is either hard or weak, and weakness is spelled `optional` (D105). */\nfunction onDemand<Value>(\n source: HostContract<Value> & { readonly [onDemandHostContractBrand]?: never },\n): OnDemandHostContract<HostContract<Value>> {\n const contract = importedContract(source);\n\n if (contract === undefined || onDemandHostContracts.has(contract) || optionalHostContracts.has(contract)) {\n throw new TypeError('onDemand requires one direct authentic host contract.');\n }\n\n if (isFeatureContract(contract)) {\n throw new TypeError('onDemand wraps a host contract; a feature edge is hard or optional.');\n }\n\n const declaration = Object.freeze({ id: contract.id }) as OnDemandHostContract<HostContract<Value>>;\n hostContracts.add(declaration);\n onDemandHostContracts.set(declaration, contract);\n\n return declaration;\n}\n\n/** `optional` is the weak edge: the provider may be closed or absent, and the consumer reads a projection (D105). */\nfunction optionalImport<Declaration extends FeatureImportDeclaration>(\n source: DirectImportDeclaration<Declaration>,\n): OptionalHostContract<Declaration> {\n const contract = importedContract(source);\n\n if (contract === undefined || optionalHostContracts.has(contract)) {\n throw new TypeError('optional requires one direct authentic import declaration.');\n }\n\n const declaration = Object.freeze({ id: contract.id }) as OptionalHostContract<Declaration>;\n hostContracts.add(declaration);\n optionalHostContracts.set(declaration, contract);\n\n return declaration;\n}\n\nfunction getDirectHostContract(contract: HostContractIdentity): HostContractIdentity {\n if (!isHostContract(contract)) throw new TypeError('Import contract is not authentic.');\n\n return declaredContract(contract) as HostContractIdentity;\n}\n\nfunction getHostContractDemand(contract: HostContractIdentity): ImportDemand {\n if (!isHostContract(contract)) throw new TypeError('Import contract is not authentic.');\n\n return onDemandHostContracts.has(optionalHostContracts.get(contract) ?? contract) ? 'onDemand' : 'instance';\n}\n\n/** Tolerant on purpose: the caller may hold any import declaration, including a feature definition itself. */\nfunction isHostContractOptional(declaration: unknown): boolean {\n return typeof declaration === 'object' && declaration !== null && optionalHostContracts.has(declaration);\n}\n\nexport {\n defineFeatureContract,\n defineHostContract,\n getDirectHostContract,\n getHostContractDemand,\n importedContract,\n isFeatureContract,\n isHostContractOptional,\n onDemand,\n optionalImport,\n};\nexport type {\n DirectImportDeclaration,\n FeatureContract,\n FeatureContractIdentity,\n FeatureImportDeclaration,\n FeatureImportRecord,\n FeatureImportValues,\n HostContract,\n ImportDemand,\n OnDemandHostContract,\n OptionalHostContract,\n};\n"],"names":["featureContracts","hostContracts","onDemandHostContracts","optionalHostContracts","defineHostContract","id","contract","declarationId","defineFeatureContract","isHostContract","value","declaredContract","declared","isFeatureContract","importedContract","source","exported","onDemand","declaration","optionalImport","getDirectHostContract","getHostContractDemand","isHostContractOptional"],"mappings":"8CA8DA,MAAMA,EAAmB,IAAI,QACvBC,EAAgB,IAAI,QACpBC,EAAwB,IAAI,QAC5BC,EAAwB,IAAI,QAoClC,SAASC,EAA0BC,EAAqB,CACtD,MAAMC,EAAW,OAAO,OAAO,CAAE,GAAIC,EAAcF,CAAE,EAAG,EACxD,OAAAJ,EAAc,IAAIK,CAAQ,EAEnBA,CACT,CAEA,SAASE,EAA6BH,EAA4B,CAChE,MAAMC,EAAWF,EAA0BC,CAAE,EAC7C,OAAAL,EAAiB,IAAIM,CAAQ,EAEtBA,CACT,CAEA,SAASG,EAAeC,EAAc,CACpC,OAAO,OAAOA,GAAU,UAAYA,IAAU,MAAQT,EAAc,IAAIS,CAAK,CAC/E,CAGA,SAASC,EAAiBD,EAAa,CACrC,MAAME,EAAWT,EAAsB,IAAIO,CAAK,GAAKA,EAErD,OAAOR,EAAsB,IAAIU,CAAQ,GAAKA,CAChD,CAEA,SAASC,EAAkBH,EAAc,CACvC,OAAI,OAAOA,GAAU,UAAYA,IAAU,KAAa,GAEjDV,EAAiB,IAAIW,EAAiBD,CAAK,CAAC,CACrD,CAEA,SAASI,EAAiBC,EAAe,CACvC,GAAIN,EAAeM,CAAM,EAAG,OAAOA,EAEnC,GAAI,OAAOA,GAAW,UAAYA,IAAW,KAAM,OAEnD,MAAMC,EAAoB,QAAQ,IAAID,EAAQ,SAAS,EAEvD,OAAOF,EAAkBG,CAAQ,EAAIA,EAAW,MAClD,CAGA,SAASC,EACPF,EAA8E,CAE9E,MAAMT,EAAWQ,EAAiBC,CAAM,EAExC,GAAIT,IAAa,QAAaJ,EAAsB,IAAII,CAAQ,GAAKH,EAAsB,IAAIG,CAAQ,EACrG,MAAM,IAAI,UAAU,uDAAuD,EAG7E,GAAIO,EAAkBP,CAAQ,EAC5B,MAAM,IAAI,UAAU,qEAAqE,EAG3F,MAAMY,EAAc,OAAO,OAAO,CAAE,GAAIZ,EAAS,GAAI,EACrD,OAAAL,EAAc,IAAIiB,CAAW,EAC7BhB,EAAsB,IAAIgB,EAAaZ,CAAQ,EAExCY,CACT,CAGA,SAASC,EACPJ,EAA4C,CAE5C,MAAMT,EAAWQ,EAAiBC,CAAM,EAExC,GAAIT,IAAa,QAAaH,EAAsB,IAAIG,CAAQ,EAC9D,MAAM,IAAI,UAAU,4DAA4D,EAGlF,MAAMY,EAAc,OAAO,OAAO,CAAE,GAAIZ,EAAS,GAAI,EACrD,OAAAL,EAAc,IAAIiB,CAAW,EAC7Bf,EAAsB,IAAIe,EAAaZ,CAAQ,EAExCY,CACT,CAEA,SAASE,EAAsBd,EAA8B,CAC3D,GAAI,CAACG,EAAeH,CAAQ,EAAG,MAAM,IAAI,UAAU,mCAAmC,EAEtF,OAAOK,EAAiBL,CAAQ,CAClC,CAEA,SAASe,EAAsBf,EAA8B,CAC3D,GAAI,CAACG,EAAeH,CAAQ,EAAG,MAAM,IAAI,UAAU,mCAAmC,EAEtF,OAAOJ,EAAsB,IAAIC,EAAsB,IAAIG,CAAQ,GAAKA,CAAQ,EAAI,WAAa,UACnG,CAGA,SAASgB,EAAuBJ,EAAoB,CAClD,OAAO,OAAOA,GAAgB,UAAYA,IAAgB,MAAQf,EAAsB,IAAIe,CAAW,CACzG"}
@@ -0,0 +1,38 @@
1
+ import type { ModelContext, ModelOf, Readable } from '@opetope/core';
2
+ import type { ModelIdentity } from '@opetope/core/internal';
3
+ import type { RuntimeErrorReporter } from './runtime-error-reporting.js';
4
+ declare const contributionModelBrand: unique symbol;
5
+ type AnyModel = ModelIdentity;
6
+ /**
7
+ * A UI model of a contribution: the plan is declared once per generation, the instance is created on every mount and
8
+ * closed on unmount (D85). `props` is the readable of the slot props of that one mount.
9
+ */
10
+ interface ContributionModel<Declaration extends AnyModel = AnyModel, Props = unknown> {
11
+ /** A record, not a function: the props of a plan must stay covariant so a plan fits `readonly ContributionModel[]`. */
12
+ readonly [contributionModelBrand]: {
13
+ readonly declaration: Declaration;
14
+ readonly props: Props;
15
+ };
16
+ }
17
+ type ContributionModelDeclaration<Plan> = Plan extends ContributionModel<infer Declaration, unknown> ? Declaration : never;
18
+ /** What a plan's factory reads as the props of its mount. Checked against the target at the contribution . */
19
+ type ContributionModelProps<Plan> = Plan extends ContributionModel<AnyModel, infer Props> ? Props : never;
20
+ type ContributionModelFactory<Declaration extends AnyModel, Props> = (context: ModelContext, props: Readable<Props>) => ModelOf<Declaration>;
21
+ type ContributionModelBuilder = <Declaration extends AnyModel, Props = unknown>(declaration: Declaration, create: ContributionModelFactory<Declaration, Props>) => ContributionModel<Declaration, Props>;
22
+ type ContributionModelPlan = {
23
+ readonly create: (context: ModelContext, props: Readable<unknown>) => unknown;
24
+ readonly declaration: AnyModel;
25
+ };
26
+ interface ContributionModelBundle {
27
+ readonly close: () => Promise<void>;
28
+ readonly models: readonly (readonly [AnyModel, unknown])[];
29
+ }
30
+ declare const contributionModel: ContributionModelBuilder;
31
+ declare function requireContributionModelPlans(value: unknown): readonly ContributionModelPlan[];
32
+ /**
33
+ * One mount, one bundle: every plan gets its own kernel, and `close` fences the calls before it drains the nodes, so a
34
+ * command that arrives after unmount is a cancellation and never a product failure (D85).
35
+ */
36
+ declare function openContributionModels(plans: readonly ContributionModelPlan[], props: Readable<unknown>, reporter: RuntimeErrorReporter | undefined): ContributionModelBundle;
37
+ export { contributionModel, openContributionModels, requireContributionModelPlans };
38
+ export type { ContributionModel, ContributionModelBuilder, ContributionModelBundle, ContributionModelDeclaration, ContributionModelPlan, ContributionModelProps, };
@@ -0,0 +1,2 @@
1
+ import{createFeatureModelOwner as l}from"./feature-model.js";import{assertPlainRecord as s}from"./feature-record.js";import{reportRuntimeFailure as m}from"./runtime-error-reporting.js";const u=new WeakMap;function p(e){if(typeof e!="object"||e===null)throw new TypeError("Contribution model requires a model declaration.");return e}function b(e){if(typeof e!="function")throw new TypeError("Contribution model requires a factory function.");return e}const y=((e,n)=>{const r=Object.freeze({});return u.set(r,{create:b(n),declaration:p(e)}),r});function w(e){return typeof e=="object"&&e!==null?u.get(e):void 0}function h(e){if(e===void 0)return Object.freeze([]);if(!Array.isArray(e))throw new TypeError("Contribution models must be an array of model plans.");return Object.freeze(e.map(n=>{const r=w(n);if(r===void 0)throw new TypeError("Contribution model is not authentic.");return r}))}function j(e,n,r){const o=l(r),c=[],a=()=>(o.fence(),o.drain());try{for(const[i,t]of e.entries()){const f=`${String(t.declaration.id)}.mount.${String(i+1)}`,d=t.create(o.create(t.declaration.id,f),n);s(d,`Contribution model ${String(t.declaration.id)}`),c.push([t.declaration,Object.freeze({...d})])}}catch(i){throw a().catch(t=>m(t,r)),i}return Object.freeze({close:a,models:Object.freeze(c)})}export{y as contributionModel,j as openContributionModels,h as requireContributionModelPlans};
2
+ //# sourceMappingURL=feature-contribution-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-contribution-model.js","sources":["../src/feature-contribution-model.ts"],"sourcesContent":["import type { ModelContext, ModelOf, Readable } from '@opetope/core';\nimport type { ModelIdentity } from '@opetope/core/internal';\n\nimport { createFeatureModelOwner } from './feature-model';\nimport { assertPlainRecord } from './feature-record';\nimport { reportRuntimeFailure } from './runtime-error-reporting';\nimport type { RuntimeErrorReporter } from './runtime-error-reporting';\n\ndeclare const contributionModelBrand: unique symbol;\n\ntype AnyModel = ModelIdentity;\n\n/**\n * A UI model of a contribution: the plan is declared once per generation, the instance is created on every mount and\n * closed on unmount (D85). `props` is the readable of the slot props of that one mount.\n */\ninterface ContributionModel<Declaration extends AnyModel = AnyModel, Props = unknown> {\n /** A record, not a function: the props of a plan must stay covariant so a plan fits `readonly ContributionModel[]`. */\n readonly [contributionModelBrand]: { readonly declaration: Declaration; readonly props: Props };\n}\n\ntype ContributionModelDeclaration<Plan> =\n Plan extends ContributionModel<infer Declaration, unknown> ? Declaration : never;\n\n/** What a plan's factory reads as the props of its mount. Checked against the target at the contribution . */\ntype ContributionModelProps<Plan> = Plan extends ContributionModel<AnyModel, infer Props> ? Props : never;\n\ntype ContributionModelFactory<Declaration extends AnyModel, Props> = (\n context: ModelContext,\n props: Readable<Props>,\n) => ModelOf<Declaration>;\n\ntype ContributionModelBuilder = <Declaration extends AnyModel, Props = unknown>(\n declaration: Declaration,\n create: ContributionModelFactory<Declaration, Props>,\n) => ContributionModel<Declaration, Props>;\n\ntype ContributionModelPlan = {\n readonly create: (context: ModelContext, props: Readable<unknown>) => unknown;\n readonly declaration: AnyModel;\n};\n\ninterface ContributionModelBundle {\n readonly close: () => Promise<void>;\n readonly models: readonly (readonly [AnyModel, unknown])[];\n}\n\nconst plans = new WeakMap<object, ContributionModelPlan>();\n\nfunction requireModelDeclaration(value: unknown): AnyModel {\n if (typeof value !== 'object' || value === null) {\n throw new TypeError('Contribution model requires a model declaration.');\n }\n\n return value as AnyModel;\n}\n\nfunction requireFactory(value: unknown): ContributionModelPlan['create'] {\n if (typeof value !== 'function') throw new TypeError('Contribution model requires a factory function.');\n\n return value as ContributionModelPlan['create'];\n}\n\nconst contributionModel = ((declaration: unknown, create: unknown) => {\n const plan = Object.freeze({});\n plans.set(plan, { create: requireFactory(create), declaration: requireModelDeclaration(declaration) });\n\n return plan;\n}) as unknown as ContributionModelBuilder;\n\nfunction getContributionModelPlan(value: unknown): ContributionModelPlan | undefined {\n return typeof value === 'object' && value !== null ? plans.get(value) : undefined;\n}\n\nfunction requireContributionModelPlans(value: unknown): readonly ContributionModelPlan[] {\n if (value === undefined) return Object.freeze([]);\n\n if (!Array.isArray(value)) throw new TypeError('Contribution models must be an array of model plans.');\n\n return Object.freeze(\n value.map(entry => {\n const plan = getContributionModelPlan(entry);\n\n if (plan === undefined) throw new TypeError('Contribution model is not authentic.');\n\n return plan;\n }),\n );\n}\n\n/**\n * One mount, one bundle: every plan gets its own kernel, and `close` fences the calls before it drains the nodes, so a\n * command that arrives after unmount is a cancellation and never a product failure (D85).\n */\nfunction openContributionModels(\n plans: readonly ContributionModelPlan[],\n props: Readable<unknown>,\n reporter: RuntimeErrorReporter | undefined,\n): ContributionModelBundle {\n const owner = createFeatureModelOwner(reporter);\n const models: (readonly [AnyModel, unknown])[] = [];\n const close = (): Promise<void> => {\n owner.fence();\n\n return owner.drain();\n };\n\n try {\n for (const [index, plan] of plans.entries()) {\n const modelId = `${String(plan.declaration.id)}.mount.${String(index + 1)}`;\n const model = plan.create(owner.create(plan.declaration.id, modelId), props);\n assertPlainRecord(model, `Contribution model ${String(plan.declaration.id)}`);\n models.push([plan.declaration, Object.freeze({ ...model })] as const);\n }\n } catch (error) {\n void close().catch((failure: unknown) => reportRuntimeFailure(failure, reporter));\n throw error;\n }\n\n return Object.freeze({\n close,\n models: Object.freeze(models),\n });\n}\n\nexport { contributionModel, openContributionModels, requireContributionModelPlans };\nexport type {\n ContributionModel,\n ContributionModelBuilder,\n ContributionModelBundle,\n ContributionModelDeclaration,\n ContributionModelPlan,\n ContributionModelProps,\n};\n"],"names":["plans","requireModelDeclaration","value","requireFactory","contributionModel","declaration","create","plan","getContributionModelPlan","requireContributionModelPlans","entry","openContributionModels","props","reporter","owner","createFeatureModelOwner","models","close","index","modelId","model","assertPlainRecord","error","failure","reportRuntimeFailure"],"mappings":"yLA+CA,MAAMA,EAAQ,IAAI,QAElB,SAASC,EAAwBC,EAAc,CAC7C,GAAI,OAAOA,GAAU,UAAYA,IAAU,KACzC,MAAM,IAAI,UAAU,kDAAkD,EAGxE,OAAOA,CACT,CAEA,SAASC,EAAeD,EAAc,CACpC,GAAI,OAAOA,GAAU,WAAY,MAAM,IAAI,UAAU,iDAAiD,EAEtG,OAAOA,CACT,CAEA,MAAME,GAAqB,CAACC,EAAsBC,IAAmB,CACnE,MAAMC,EAAO,OAAO,OAAO,EAAE,EAC7B,OAAAP,EAAM,IAAIO,EAAM,CAAE,OAAQJ,EAAeG,CAAM,EAAG,YAAaL,EAAwBI,CAAW,CAAC,CAAE,EAE9FE,CACT,GAEA,SAASC,EAAyBN,EAAc,CAC9C,OAAO,OAAOA,GAAU,UAAYA,IAAU,KAAOF,EAAM,IAAIE,CAAK,EAAI,MAC1E,CAEA,SAASO,EAA8BP,EAAc,CACnD,GAAIA,IAAU,OAAW,OAAO,OAAO,OAAO,EAAE,EAEhD,GAAI,CAAC,MAAM,QAAQA,CAAK,EAAG,MAAM,IAAI,UAAU,sDAAsD,EAErG,OAAO,OAAO,OACZA,EAAM,IAAIQ,GAAQ,CAChB,MAAMH,EAAOC,EAAyBE,CAAK,EAE3C,GAAIH,IAAS,OAAW,MAAM,IAAI,UAAU,sCAAsC,EAElF,OAAOA,CACT,CAAC,CAAC,CAEN,CAMA,SAASI,EACPX,EACAY,EACAC,EAA0C,CAE1C,MAAMC,EAAQC,EAAwBF,CAAQ,EACxCG,EAA2C,CAAA,EAC3CC,EAAQ,KACZH,EAAM,MAAK,EAEJA,EAAM,MAAK,GAGpB,GAAI,CACF,SAAW,CAACI,EAAOX,CAAI,IAAKP,EAAM,QAAO,EAAI,CAC3C,MAAMmB,EAAU,GAAG,OAAOZ,EAAK,YAAY,EAAE,CAAC,UAAU,OAAOW,EAAQ,CAAC,CAAC,GACnEE,EAAQb,EAAK,OAAOO,EAAM,OAAOP,EAAK,YAAY,GAAIY,CAAO,EAAGP,CAAK,EAC3ES,EAAkBD,EAAO,sBAAsB,OAAOb,EAAK,YAAY,EAAE,CAAC,EAAE,EAC5ES,EAAO,KAAK,CAACT,EAAK,YAAa,OAAO,OAAO,CAAE,GAAGa,CAAK,CAAE,CAAC,CAAU,CACtE,CACF,OAASE,EAAO,CACd,MAAKL,EAAK,EAAG,MAAOM,GAAqBC,EAAqBD,EAASV,CAAQ,CAAC,EAC1ES,CACR,CAEA,OAAO,OAAO,OAAO,CACnB,MAAAL,EACA,OAAQ,OAAO,OAAOD,CAAM,CAC7B,CAAA,CACH"}
@@ -0,0 +1,126 @@
1
+ import type { DeclarationId, Readable } from '@opetope/core';
2
+ import type { ContributionPublication, ContributionPublicationInput, ContributionTarget, PipeHandler } from '@opetope/core/internal';
3
+ import type { ContributionModelDeclaration, ContributionModelProps } from './feature-contribution-model.js';
4
+ import type { ExactInput } from './feature-record.js';
5
+ declare const featureContributionBrand: unique symbol;
6
+ interface FeatureContribution {
7
+ readonly [featureContributionBrand]: true;
8
+ }
9
+ type FeatureContributionValueFactory<Context, Value> = (context: Context) => Value;
10
+ /**
11
+ * `when` withholds a contribution while it reads `false`, so the target never lists it and `Slot`, `fold`, `select`
12
+ * and emptiness checks skip it (D79). It is either a `Readable<boolean>` or a predicate of the instance: the
13
+ * predicate answers a boolean and its `read` records what the answer depends on, so the runtime lowers it to one
14
+ * computed readable of that instance and publishes it the way it always did (D220).
15
+ */
16
+ interface FeatureContributionOptions<Evaluation = never> {
17
+ readonly priority?: number;
18
+ readonly when?: ((context: Evaluation) => boolean) | Readable<boolean>;
19
+ }
20
+ /**
21
+ * Structural and renderer-free: with a props adapter the component must accept exactly what the adapter returns,
22
+ * without one it must accept the props of the target. Slot props, adapter and component stay one chain (D85).
23
+ */
24
+ type ContributionComponentProps<Component> = Component extends (props: infer Props, ...rest: never[]) => unknown ? Props : Component extends abstract new (props: infer Props, ...rest: never[]) => unknown ? Props : never;
25
+ type ContributionTargetProps<Value> = Value extends {
26
+ readonly props?: (slotProps: infer Props) => unknown;
27
+ } ? Props : never;
28
+ type ContributionPropsMismatch = {
29
+ readonly Component: 'contribution component must accept the props the adapter or the target supplies';
30
+ };
31
+ /** An erased component type states no props, so there is nothing left to check on that spec. */
32
+ type AcceptsProps<Supplied, Accepted> = [Accepted] extends [never] ? unknown : [Supplied] extends [Accepted] ? unknown : ContributionPropsMismatch;
33
+ type ExactContributionProps<Spec, Value> = Spec extends {
34
+ readonly Component: infer Component;
35
+ } ? Spec extends {
36
+ readonly props: (input: never) => infer Adapted;
37
+ } ? AcceptsProps<Adapted, ContributionComponentProps<Component>> : AcceptsProps<ContributionTargetProps<Value>, ContributionComponentProps<Component>> : unknown;
38
+ /**
39
+ * The check is a return type, not a parameter type: a conditional over `Spec` inside the parameter would make the
40
+ * inference circular and TypeScript would fall back to the constraint, leaving the props chain unchecked.
41
+ */
42
+ type ContributionModelsMissing = {
43
+ readonly models: 'contribution must list every model its component requires';
44
+ };
45
+ type ContributionModelPropsMismatch = {
46
+ readonly models: 'a contribution model reads props this target does not publish';
47
+ };
48
+ /**
49
+ * D118: a UI model of a contribution is created per mount and reads the props of that mount, so the props its
50
+ * factory declares must be the props the target publishes. Without this the annotation was free and a wrong shape
51
+ * Only failed when a component read a field the mount never had.
52
+ */
53
+ type ContributionModelPropsGap<Spec, Value> = Spec extends {
54
+ readonly models: infer Plans extends readonly unknown[];
55
+ } ? {
56
+ readonly [Index in keyof Plans]: [ContributionTargetProps<Value>] extends [ContributionModelProps<Plans[Index]>] ? never : Plans[Index];
57
+ }[number] : never;
58
+ type ContributionModelDeclarations<Spec> = Spec extends {
59
+ readonly models: readonly (infer Plan)[];
60
+ } ? ContributionModelDeclaration<Plan> : never;
61
+ type ContributionRequiredModels<Spec> = Spec extends {
62
+ readonly Component: {
63
+ readonly requires: readonly (infer Required)[];
64
+ };
65
+ } ? Required : never;
66
+ type CheckedContribution<Spec, Value> = ExactContributionProps<Spec, Value> extends ContributionPropsMismatch ? ContributionPropsMismatch : [Exclude<ContributionRequiredModels<Spec>, ContributionModelDeclarations<Spec>>] extends [never] ? [ContributionModelPropsGap<Spec, Value>] extends [never] ? FeatureContribution : ContributionModelPropsMismatch : ContributionModelsMissing;
67
+ /**
68
+ * A slot contribution is a record, so the static form and the generation-scoped factory never collide. Each form is
69
+ * its own signature and the props check is a return type: a conditional over `Spec` in the parameter would make the
70
+ * inference circular and TypeScript would fall back to the constraint.
71
+ */
72
+ interface FeatureSlotBuilder<Context, Evaluation> {
73
+ <Value extends object, const Spec extends NoInfer<Value>, const Options extends FeatureContributionOptions<Evaluation>>(target: ContributionTarget<Value>, contribution: (context: Context) => Spec, options?: ExactInput<Options, FeatureContributionOptions<Evaluation>>): CheckedContribution<Spec, Value>;
74
+ <Value extends object, const Spec extends NoInfer<Value>, const Options extends FeatureContributionOptions<Evaluation>>(target: ContributionTarget<Value>, contribution: Spec, options?: ExactInput<Options, FeatureContributionOptions<Evaluation>>): CheckedContribution<Spec, Value>;
75
+ }
76
+ /**
77
+ * D223: the second argument of `pipe` is a descriptor with one key. A handler is itself a function, so a bare
78
+ * function could be read as either the handler or a factory of it; a record names which one it is, and the handler
79
+ * receives the instance it belongs to together with the reader of the fold that is running.
80
+ */
81
+ type FeaturePipeDescriptor<Value, Meta, Evaluation> = {
82
+ readonly fold: (value: Value, meta: Meta, context: Evaluation) => Value;
83
+ };
84
+ type FeaturePipeBuilder<Evaluation> = <Value, Meta, const Options extends FeatureContributionOptions<Evaluation> = FeatureContributionOptions<Evaluation>>(target: ContributionTarget<PipeHandler<Value, Meta>>, descriptor: FeaturePipeDescriptor<Value, Meta, Evaluation>, options?: ExactInput<Options, FeatureContributionOptions<Evaluation>>) => FeatureContribution;
85
+ type FeatureRegisterBuilder<Context, Evaluation> = <Value extends object, const Options extends FeatureContributionOptions<Evaluation>>(target: ContributionTarget<Value>, entry: FeatureContributionValueFactory<Context, NoInfer<Value>> | NoInfer<Value>, options?: ExactInput<Options, FeatureContributionOptions<Evaluation>>) => FeatureContribution;
86
+ interface FeatureContributionDescriptor {
87
+ readonly id: DeclarationId;
88
+ readonly priority: number;
89
+ readonly target: ContributionPublicationInput['target'];
90
+ }
91
+ /** The live values of one instance, read by every reactive calculation that instance declared (D220). */
92
+ type FeatureEvaluationValues = Readonly<{
93
+ exports: unknown;
94
+ imports: unknown;
95
+ own: unknown;
96
+ }>;
97
+ /** What a declaration keeps: the form was decided when it was written, so publication only asks for the readable. */
98
+ type FeatureContributionVisibility = (evaluation: FeatureEvaluationValues | undefined) => Readable<boolean>;
99
+ type SnapshotContributionOptions = {
100
+ readonly priority: number;
101
+ readonly when: FeatureContributionVisibility | undefined;
102
+ };
103
+ /** The handler an author wrote for a pipe: it runs on `fold`, never at declaration, preload or opening (D223). */
104
+ type FeaturePipeFold = (value: unknown, meta: unknown, context: unknown) => unknown;
105
+ type PendingFeatureContribution = SnapshotContributionOptions & {
106
+ readonly fold: FeaturePipeFold | undefined;
107
+ readonly target: ContributionPublicationInput['target'];
108
+ readonly value: FeatureContributionValueFactory<unknown, unknown>;
109
+ };
110
+ type FeatureContributionBuilders<Context, Evaluation> = {
111
+ readonly declarations: WeakMap<object, PendingFeatureContribution>;
112
+ readonly declared: Set<object>;
113
+ readonly pipe: FeaturePipeBuilder<Evaluation>;
114
+ readonly register: FeatureRegisterBuilder<Context, Evaluation>;
115
+ readonly slot: FeatureSlotBuilder<Context, Evaluation>;
116
+ };
117
+ declare function createFeatureContributionBuilders<Context, Evaluation>(): FeatureContributionBuilders<Context, Evaluation>;
118
+ declare function isFeatureContributionDeclaration(builders: FeatureContributionBuilders<never, never>, value: unknown): boolean;
119
+ declare function snapshotFeatureContributions<Context, Evaluation>(featureId: string, builders: FeatureContributionBuilders<Context, Evaluation>, entries: readonly (readonly [string, unknown])[]): readonly FeatureContributionDescriptor[];
120
+ /**
121
+ * Every published value carries the authority of the generation that produced it, so a renderer can give a
122
+ * contribution the models of its own feature and nothing else (D70).
123
+ */
124
+ declare function createFeatureContributionPublication<Context>(descriptors: readonly FeatureContributionDescriptor[], context: Context, capture: (publication: ContributionPublication) => void, authority?: unknown, evaluation?: FeatureEvaluationValues): ContributionPublication;
125
+ export { createFeatureContributionBuilders, createFeatureContributionPublication, isFeatureContributionDeclaration, snapshotFeatureContributions, };
126
+ export type { FeatureContribution, FeatureContributionDescriptor, FeatureEvaluationValues, FeaturePipeBuilder, FeatureRegisterBuilder, FeatureSlotBuilder, };
@@ -0,0 +1,2 @@
1
+ import{declarationId as h,computed as l}from"@opetope/core";import{isContributionTarget as F,publishContributions as m}from"@opetope/core/internal";import{assertPlainRecord as a,dataEntries as d,assertExactDataKeys as E}from"./feature-record.js";const b=new WeakMap,y=new WeakMap,w=new WeakMap,g=new Set(["priority","when"]);function T(e){if(e===void 0)return 0;if(typeof e!="number"||!Number.isFinite(e))throw new TypeError("Feature contribution priority must be a finite number.");return e}function j(e,t){if(t===void 0)throw new TypeError("Feature contribution when predicate has no instance to read.");return l({read:r=>{const n=e({...t,read:r});if(typeof n!="boolean")throw new TypeError("Feature contribution when predicate must return a boolean.");return n}})}function C(e){if(e===void 0)return;if(typeof e=="function"){const r=e;return n=>j(r,n)}if(typeof e!="object"||e===null||typeof e.getSnapshot!="function"||typeof e.subscribe!="function")throw new TypeError("Feature contribution when must be a readable or a predicate of the instance.");const t=e;return()=>t}function x(e){if(e===void 0)return{priority:0,when:void 0};a(e,"Feature contribution options");const t=d(e,"Feature contribution options");if(t.some(([n])=>!g.has(n)))throw new TypeError("Feature contribution options accept only priority and when.");const r=Object.fromEntries(t);return{priority:T(r.priority),when:C(r.when)}}function O(e){if(typeof e=="function"||typeof e!="object"||e===null)throw new TypeError("Feature pipe expects a descriptor: pipe(target, { fold: (value, meta, context) => next }).");a(e,"Feature pipe descriptor");const t=d(e,"Feature pipe descriptor");E(e,t,["fold"],"Feature pipe descriptor");const r=e.fold;if(typeof r!="function")throw new TypeError("Feature pipe fold must be a function.");return r}function z(){const e=new WeakMap,t=new Set,r=(n,o,s,i)=>{if(!F(n))throw new TypeError(`Feature ${i} target is not authentic.`);const u=i==="pipe"?O(o):void 0,c=typeof o=="function"?o:()=>o,p=Object.freeze({});return t.add(p),e.set(p,{...x(s),fold:u,target:n,value:c}),p};return Object.freeze({declarations:e,declared:t,pipe:(n,o,s)=>r(n,o,s,"pipe"),register:(n,o,s)=>r(n,o,s,"register"),slot:((n,o,s)=>r(n,o,s,"slot"))})}function k(e,t){return typeof t=="object"&&t!==null&&e.declarations.has(t)}function P(e,t,r){const n=new Set,o=r.map(([s,i])=>{const u=typeof i=="object"&&i!==null?t.declarations.get(i):void 0;if(u===void 0)throw new TypeError(`Feature contribution ${s} is not authentic.`);if(n.has(i))throw new TypeError("Every feature contribution must be returned under exactly one key.");n.add(i);const c=Object.freeze({id:h(`${e}.${s}`),priority:u.priority,target:u.target});return b.set(c,u.value),u.when!==void 0&&y.set(c,u.when),u.fold!==void 0&&w.set(c,u.fold),c});if(n.size!==t.declared.size)throw new TypeError("Every feature contribution must be returned under exactly one key.");return Object.freeze(o)}function S(e,t){if(t===void 0)throw new TypeError("Feature pipe fold has no instance to read.");const r=[],n=Object.freeze({...t,read:o=>{const s=r[r.length-1];return s===void 0?o.getSnapshot():s(o)}});return(o,s,i)=>{r.push(i.read);try{return e(o,s,n)}finally{r.pop()}}}function V(e,t,r,n,o){const s=e.map(i=>{const u=b.get(i);if(u===void 0)throw new TypeError(`Feature contribution ${i.id} is not authentic.`);const c=w.get(i),p=c===void 0?u(t):S(c,o),f=y.get(i);return{id:i.id,...n===void 0?{}:{owner:n},priority:i.priority,target:i.target,value:p,when:f==null?void 0:f(o)}});return m(s,r)}export{z as createFeatureContributionBuilders,V as createFeatureContributionPublication,k as isFeatureContributionDeclaration,P as snapshotFeatureContributions};
2
+ //# sourceMappingURL=feature-contribution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-contribution.js","sources":["../src/feature-contribution.ts"],"sourcesContent":["import { computed, declarationId } from '@opetope/core';\nimport type { DeclarationId, Readable } from '@opetope/core';\nimport type {\n ContributionPublication,\n ContributionPublicationInput,\n ContributionTarget,\n PipeHandler,\n PipeRead,\n PipeReadContext,\n} from '@opetope/core/internal';\nimport { isContributionTarget, publishContributions } from '@opetope/core/internal';\n\nimport type { ContributionModelDeclaration, ContributionModelProps } from './feature-contribution-model';\nimport { assertExactDataKeys, assertPlainRecord, dataEntries } from './feature-record';\nimport type { ExactInput } from './feature-record';\n\ndeclare const featureContributionBrand: unique symbol;\n\ninterface FeatureContribution {\n readonly [featureContributionBrand]: true;\n}\n\ntype FeatureContributionValueFactory<Context, Value> = (context: Context) => Value;\n\n/**\n * `when` withholds a contribution while it reads `false`, so the target never lists it and `Slot`, `fold`, `select`\n * and emptiness checks skip it (D79). It is either a `Readable<boolean>` or a predicate of the instance: the\n * predicate answers a boolean and its `read` records what the answer depends on, so the runtime lowers it to one\n * computed readable of that instance and publishes it the way it always did (D220).\n */\ninterface FeatureContributionOptions<Evaluation = never> {\n readonly priority?: number;\n readonly when?: ((context: Evaluation) => boolean) | Readable<boolean>;\n}\n\n/**\n * Structural and renderer-free: with a props adapter the component must accept exactly what the adapter returns,\n * without one it must accept the props of the target. Slot props, adapter and component stay one chain (D85).\n */\ntype ContributionComponentProps<Component> = Component extends (props: infer Props, ...rest: never[]) => unknown\n ? Props\n : Component extends abstract new (props: infer Props, ...rest: never[]) => unknown\n ? Props\n : never;\n\ntype ContributionTargetProps<Value> = Value extends { readonly props?: (slotProps: infer Props) => unknown }\n ? Props\n : never;\n\ntype ContributionPropsMismatch = {\n readonly Component: 'contribution component must accept the props the adapter or the target supplies';\n};\n\n/** An erased component type states no props, so there is nothing left to check on that spec. */\ntype AcceptsProps<Supplied, Accepted> = [Accepted] extends [never]\n ? unknown\n : [Supplied] extends [Accepted]\n ? unknown\n : ContributionPropsMismatch;\n\ntype ExactContributionProps<Spec, Value> = Spec extends { readonly Component: infer Component }\n ? Spec extends { readonly props: (input: never) => infer Adapted }\n ? AcceptsProps<Adapted, ContributionComponentProps<Component>>\n : AcceptsProps<ContributionTargetProps<Value>, ContributionComponentProps<Component>>\n : unknown;\n\n/**\n * The check is a return type, not a parameter type: a conditional over `Spec` inside the parameter would make the\n * inference circular and TypeScript would fall back to the constraint, leaving the props chain unchecked.\n */\ntype ContributionModelsMissing = {\n readonly models: 'contribution must list every model its component requires';\n};\n\ntype ContributionModelPropsMismatch = {\n readonly models: 'a contribution model reads props this target does not publish';\n};\n\n/**\n * D118: a UI model of a contribution is created per mount and reads the props of that mount, so the props its\n * factory declares must be the props the target publishes. Without this the annotation was free and a wrong shape\n * Only failed when a component read a field the mount never had.\n */\ntype ContributionModelPropsGap<Spec, Value> = Spec extends {\n readonly models: infer Plans extends readonly unknown[];\n}\n ? {\n readonly [Index in keyof Plans]: [ContributionTargetProps<Value>] extends [ContributionModelProps<Plans[Index]>]\n ? never\n : Plans[Index];\n }[number]\n : never;\n\ntype ContributionModelDeclarations<Spec> = Spec extends { readonly models: readonly (infer Plan)[] }\n ? ContributionModelDeclaration<Plan>\n : never;\n\ntype ContributionRequiredModels<Spec> = Spec extends {\n readonly Component: { readonly requires: readonly (infer Required)[] };\n}\n ? Required\n : never;\n\ntype CheckedContribution<Spec, Value> =\n ExactContributionProps<Spec, Value> extends ContributionPropsMismatch\n ? ContributionPropsMismatch\n : [Exclude<ContributionRequiredModels<Spec>, ContributionModelDeclarations<Spec>>] extends [never]\n ? [ContributionModelPropsGap<Spec, Value>] extends [never]\n ? FeatureContribution\n : ContributionModelPropsMismatch\n : ContributionModelsMissing;\n\n/**\n * A slot contribution is a record, so the static form and the generation-scoped factory never collide. Each form is\n * its own signature and the props check is a return type: a conditional over `Spec` in the parameter would make the\n * inference circular and TypeScript would fall back to the constraint.\n */\ninterface FeatureSlotBuilder<Context, Evaluation> {\n <\n Value extends object,\n const Spec extends NoInfer<Value>,\n const Options extends FeatureContributionOptions<Evaluation>,\n >(\n target: ContributionTarget<Value>,\n contribution: (context: Context) => Spec,\n options?: ExactInput<Options, FeatureContributionOptions<Evaluation>>,\n ): CheckedContribution<Spec, Value>;\n <\n Value extends object,\n const Spec extends NoInfer<Value>,\n const Options extends FeatureContributionOptions<Evaluation>,\n >(\n target: ContributionTarget<Value>,\n // oxlint-disable-next-line typescript/unified-signatures -- a union parameter leaves `Spec` uninferred (D85)\n contribution: Spec,\n options?: ExactInput<Options, FeatureContributionOptions<Evaluation>>,\n ): CheckedContribution<Spec, Value>;\n}\n\n/**\n * D223: the second argument of `pipe` is a descriptor with one key. A handler is itself a function, so a bare\n * function could be read as either the handler or a factory of it; a record names which one it is, and the handler\n * receives the instance it belongs to together with the reader of the fold that is running.\n */\ntype FeaturePipeDescriptor<Value, Meta, Evaluation> = {\n readonly fold: (value: Value, meta: Meta, context: Evaluation) => Value;\n};\n\ntype FeaturePipeBuilder<Evaluation> = <\n Value,\n Meta,\n const Options extends FeatureContributionOptions<Evaluation> = FeatureContributionOptions<Evaluation>,\n>(\n target: ContributionTarget<PipeHandler<Value, Meta>>,\n descriptor: FeaturePipeDescriptor<Value, Meta, Evaluation>,\n options?: ExactInput<Options, FeatureContributionOptions<Evaluation>>,\n) => FeatureContribution;\n\ntype FeatureRegisterBuilder<Context, Evaluation> = <\n Value extends object,\n const Options extends FeatureContributionOptions<Evaluation>,\n>(\n target: ContributionTarget<Value>,\n entry: FeatureContributionValueFactory<Context, NoInfer<Value>> | NoInfer<Value>,\n options?: ExactInput<Options, FeatureContributionOptions<Evaluation>>,\n) => FeatureContribution;\n\ninterface FeatureContributionDescriptor {\n readonly id: DeclarationId;\n readonly priority: number;\n readonly target: ContributionPublicationInput['target'];\n}\n\n/** The live values of one instance, read by every reactive calculation that instance declared (D220). */\ntype FeatureEvaluationValues = Readonly<{\n exports: unknown;\n imports: unknown;\n own: unknown;\n}>;\n\ntype FeatureContributionPredicate = (context: unknown) => boolean;\n\n/** What a declaration keeps: the form was decided when it was written, so publication only asks for the readable. */\ntype FeatureContributionVisibility = (evaluation: FeatureEvaluationValues | undefined) => Readable<boolean>;\n\ntype SnapshotContributionOptions = {\n readonly priority: number;\n readonly when: FeatureContributionVisibility | undefined;\n};\n\n/** The handler an author wrote for a pipe: it runs on `fold`, never at declaration, preload or opening (D223). */\ntype FeaturePipeFold = (value: unknown, meta: unknown, context: unknown) => unknown;\n\ntype PendingFeatureContribution = SnapshotContributionOptions & {\n readonly fold: FeaturePipeFold | undefined;\n readonly target: ContributionPublicationInput['target'];\n readonly value: FeatureContributionValueFactory<unknown, unknown>;\n};\n\nconst contributionValueFactories = new WeakMap<object, FeatureContributionValueFactory<unknown, unknown>>();\nconst contributionVisibilities = new WeakMap<object, FeatureContributionVisibility>();\nconst contributionFolds = new WeakMap<object, FeaturePipeFold>();\n\nconst contributionOptionKeys = new Set(['priority', 'when']);\n\nfunction snapshotContributionPriority(value: unknown): number {\n if (value === undefined) return 0;\n\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new TypeError('Feature contribution priority must be a finite number.');\n }\n\n return value;\n}\n\n/**\n * D220: a predicate becomes one computed readable of this instance, and the publication path below stays the one\n * D79 and D196 already describe. `read` is the tracking reader of that computation, so a dynamic branch replaces\n * dependencies through the reactive graph, and a predicate that answers anything but a boolean fails its own read\n * instead of publishing a truthy object as visibility.\n */\nfunction predicateVisibility(\n predicate: FeatureContributionPredicate,\n evaluation: FeatureEvaluationValues | undefined,\n): Readable<boolean> {\n if (evaluation === undefined) throw new TypeError('Feature contribution when predicate has no instance to read.');\n\n return computed({\n read: read => {\n const answer = predicate({ ...evaluation, read });\n\n if (typeof answer !== 'boolean') {\n throw new TypeError('Feature contribution when predicate must return a boolean.');\n }\n\n return answer;\n },\n });\n}\n\n/**\n * D220: `when` is a readable or a predicate of the instance. The form is decided by what the author wrote, never by\n * what a call returns: the runtime does not invoke a predicate to classify it, and it never calls it twice.\n */\nfunction snapshotContributionVisibility(value: unknown): FeatureContributionVisibility | undefined {\n if (value === undefined) return undefined;\n\n if (typeof value === 'function') {\n const predicate = value as FeatureContributionPredicate;\n\n return evaluation => predicateVisibility(predicate, evaluation);\n }\n\n if (\n typeof value !== 'object' ||\n value === null ||\n typeof (value as Readable<boolean>).getSnapshot !== 'function' ||\n typeof (value as Readable<boolean>).subscribe !== 'function'\n ) {\n throw new TypeError('Feature contribution when must be a readable or a predicate of the instance.');\n }\n\n const readable = value as Readable<boolean>;\n\n return () => readable;\n}\n\nfunction snapshotContributionOptions(value: unknown): SnapshotContributionOptions {\n if (value === undefined) return { priority: 0, when: undefined };\n\n assertPlainRecord(value, 'Feature contribution options');\n const entries = dataEntries(value, 'Feature contribution options');\n\n if (entries.some(([key]) => !contributionOptionKeys.has(key))) {\n throw new TypeError('Feature contribution options accept only priority and when.');\n }\n\n const options = Object.fromEntries(entries);\n\n return {\n priority: snapshotContributionPriority(options['priority']),\n when: snapshotContributionVisibility(options['when']),\n };\n}\n\ntype FeatureContributionBuilders<Context, Evaluation> = {\n readonly declarations: WeakMap<object, PendingFeatureContribution>;\n readonly declared: Set<object>;\n readonly pipe: FeaturePipeBuilder<Evaluation>;\n readonly register: FeatureRegisterBuilder<Context, Evaluation>;\n readonly slot: FeatureSlotBuilder<Context, Evaluation>;\n};\n\n/** D223: a pipe declares a descriptor with exactly one key, so a bare function is named at the call site. */\nfunction snapshotPipeFold(value: unknown): FeaturePipeFold {\n if (typeof value === 'function' || typeof value !== 'object' || value === null) {\n throw new TypeError('Feature pipe expects a descriptor: pipe(target, { fold: (value, meta, context) => next }).');\n }\n\n assertPlainRecord(value, 'Feature pipe descriptor');\n const entries = dataEntries(value, 'Feature pipe descriptor');\n assertExactDataKeys(value, entries, ['fold'], 'Feature pipe descriptor');\n const fold = (value as { readonly fold: unknown }).fold;\n\n if (typeof fold !== 'function') throw new TypeError('Feature pipe fold must be a function.');\n\n return fold as FeaturePipeFold;\n}\n\nfunction createFeatureContributionBuilders<Context, Evaluation>(): FeatureContributionBuilders<Context, Evaluation> {\n const declarations = new WeakMap<object, PendingFeatureContribution>();\n const declared = new Set<object>();\n const declare = (target: unknown, value: unknown, options: unknown, label: string): FeatureContribution => {\n if (!isContributionTarget(target)) throw new TypeError(`Feature ${label} target is not authentic.`);\n\n const fold = label === 'pipe' ? snapshotPipeFold(value) : undefined;\n const factory: FeatureContributionValueFactory<unknown, unknown> =\n typeof value === 'function' ? (value as FeatureContributionValueFactory<unknown, unknown>) : () => value;\n\n const declaration = Object.freeze({});\n declared.add(declaration);\n declarations.set(declaration, { ...snapshotContributionOptions(options), fold, target, value: factory });\n\n return declaration as FeatureContribution;\n };\n\n return Object.freeze({\n declarations,\n declared,\n pipe: (target: unknown, value: unknown, options?: unknown) => declare(target, value, options, 'pipe'),\n register: (target: unknown, value: unknown, options?: unknown) => declare(target, value, options, 'register'),\n slot: ((target: unknown, value: unknown, options?: unknown) =>\n declare(target, value, options, 'slot')) as unknown as FeatureSlotBuilder<Context, Evaluation>,\n });\n}\n\nfunction isFeatureContributionDeclaration(\n builders: FeatureContributionBuilders<never, never>,\n value: unknown,\n): boolean {\n return typeof value === 'object' && value !== null && builders.declarations.has(value);\n}\n\nfunction snapshotFeatureContributions<Context, Evaluation>(\n featureId: string,\n builders: FeatureContributionBuilders<Context, Evaluation>,\n entries: readonly (readonly [string, unknown])[],\n): readonly FeatureContributionDescriptor[] {\n const seen = new Set<object>();\n const descriptors = entries.map(([key, candidate]): FeatureContributionDescriptor => {\n const declaration =\n typeof candidate === 'object' && candidate !== null ? builders.declarations.get(candidate) : undefined;\n\n if (declaration === undefined) throw new TypeError(`Feature contribution ${key} is not authentic.`);\n\n if (seen.has(candidate as object)) {\n throw new TypeError('Every feature contribution must be returned under exactly one key.');\n }\n\n seen.add(candidate as object);\n const descriptor: FeatureContributionDescriptor = Object.freeze({\n id: declarationId(`${featureId}.${key}`),\n priority: declaration.priority,\n target: declaration.target,\n });\n contributionValueFactories.set(descriptor, declaration.value);\n\n if (declaration.when !== undefined) contributionVisibilities.set(descriptor, declaration.when);\n\n if (declaration.fold !== undefined) contributionFolds.set(descriptor, declaration.fold);\n\n return descriptor;\n });\n\n if (seen.size !== builders.declared.size) {\n throw new TypeError('Every feature contribution must be returned under exactly one key.');\n }\n\n return Object.freeze(descriptors);\n}\n\n/**\n * D223: the descriptor is bound to its instance once, and the handler it publishes allocates nothing per call. The\n * context it hands to the author is frozen and built here; only the reader of the fold that is running changes, so\n * a nested fold with another reader pushes its own and the outer one continues with the reader it started with.\n */\nfunction boundPipeHandler(\n fold: FeaturePipeFold,\n evaluation: FeatureEvaluationValues | undefined,\n): PipeHandler<unknown, unknown> {\n if (evaluation === undefined) throw new TypeError('Feature pipe fold has no instance to read.');\n\n const readers: PipeRead[] = [];\n const context = Object.freeze({\n ...evaluation,\n read: <Value>(source: Readable<Value>): Value => {\n const reader = readers[readers.length - 1];\n\n return reader === undefined ? source.getSnapshot() : reader(source);\n },\n });\n\n return (value: unknown, meta: unknown, pipeContext: PipeReadContext): unknown => {\n readers.push(pipeContext.read);\n\n try {\n return fold(value, meta, context);\n } finally {\n readers.pop();\n }\n };\n}\n\n/**\n * Every published value carries the authority of the generation that produced it, so a renderer can give a\n * contribution the models of its own feature and nothing else (D70).\n */\nfunction createFeatureContributionPublication<Context>(\n descriptors: readonly FeatureContributionDescriptor[],\n context: Context,\n capture: (publication: ContributionPublication) => void,\n authority?: unknown,\n evaluation?: FeatureEvaluationValues,\n): ContributionPublication {\n const inputs = descriptors.map((descriptor): ContributionPublicationInput => {\n const createValue = contributionValueFactories.get(descriptor);\n\n if (createValue === undefined) throw new TypeError(`Feature contribution ${descriptor.id} is not authentic.`);\n\n const fold = contributionFolds.get(descriptor);\n const value = fold === undefined ? createValue(context) : boundPipeHandler(fold, evaluation);\n const visibility = contributionVisibilities.get(descriptor);\n\n return {\n id: descriptor.id,\n // The authority belongs to this publication: the same static value may be published by another feature (D70).\n ...(authority === undefined ? {} : { owner: authority }),\n priority: descriptor.priority,\n target: descriptor.target,\n value,\n when: visibility?.(evaluation),\n };\n });\n\n return publishContributions(inputs, capture);\n}\n\nexport {\n createFeatureContributionBuilders,\n createFeatureContributionPublication,\n isFeatureContributionDeclaration,\n snapshotFeatureContributions,\n};\nexport type {\n FeatureContribution,\n FeatureContributionDescriptor,\n FeatureEvaluationValues,\n FeaturePipeBuilder,\n FeatureRegisterBuilder,\n FeatureSlotBuilder,\n};\n"],"names":["contributionValueFactories","contributionVisibilities","contributionFolds","contributionOptionKeys","snapshotContributionPriority","value","predicateVisibility","predicate","evaluation","computed","read","answer","snapshotContributionVisibility","readable","snapshotContributionOptions","assertPlainRecord","entries","dataEntries","key","options","snapshotPipeFold","assertExactDataKeys","fold","createFeatureContributionBuilders","declarations","declared","declare","target","label","isContributionTarget","factory","declaration","isFeatureContributionDeclaration","builders","snapshotFeatureContributions","featureId","seen","descriptors","candidate","descriptor","declarationId","boundPipeHandler","readers","context","source","reader","meta","pipeContext","createFeatureContributionPublication","capture","authority","inputs","createValue","visibility","publishContributions"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { Condition } from './condition.js';
2
+ import type { Feature, FeatureEvaluationContext, FeatureExportRecord, FeatureExportsFactory, FeatureOpenContext, FeatureOwnBuilder, FeatureOwnRecord } from './feature-authoring-types.js';
3
+ import type { FeatureBody } from './feature-body.js';
4
+ import type { FeatureImportRecord } from './feature-contract.js';
5
+ import type { AnyPort, FeatureProvidedRecord, FeatureProvidesFactory, PortRequirementRecord } from './feature-port.js';
6
+ import type { ExactKeys } from './feature-record.js';
7
+ import type { ExactNonUnionRuntime } from './public-module.js';
8
+ /**
9
+ * One input shape for every feature: only `id` is required and every section is optional. Sections are declared in
10
+ * stage order so TypeScript resolves `own` before it contextually types `provides` and `exports`.
11
+ */
12
+ type FeatureInput<Id extends string, Imports extends FeatureImportRecord, Requires extends PortRequirementRecord, Own extends FeatureOwnRecord<Id>, Exports extends FeatureExportRecord, Provided extends FeatureProvidedRecord<Id>, When extends readonly Condition[]> = {
13
+ readonly exports?: FeatureExportsFactory<Id, Own, Exports>;
14
+ readonly id: Id;
15
+ readonly imports?: Imports;
16
+ readonly own?: ExactNonUnionRuntime<Own> & ((builder: FeatureOwnBuilder<Id, Imports, NoInfer<Requires>>) => Own);
17
+ readonly provides?: FeatureProvidesFactory<Id, FeatureOpenContext<Id, Imports, Own, Exports>, FeatureEvaluationContext<Id, Imports, Own, Exports>, Own, Provided>;
18
+ readonly requires?: Requires;
19
+ /** The feature lives while every condition holds; an empty or absent list is a permanent feature (D93). */
20
+ readonly when?: When;
21
+ };
22
+ /**
23
+ * D186: what a lazy header declares of its `provides` — the target of every contribution and the port of every
24
+ * provider, with the priority the compiler orders by. The values behind them arrive with the body.
25
+ */
26
+ type FeatureDeclaredContribution = {
27
+ readonly priority?: number;
28
+ readonly target: {
29
+ readonly id: string;
30
+ };
31
+ };
32
+ type FeatureDeclaredProvided = Readonly<Record<string, AnyPort | FeatureDeclaredContribution | {
33
+ readonly id: string;
34
+ }>>;
35
+ /**
36
+ * The header of a split feature: identity, edges and the metadata the compiler needs, plus the loader of the body.
37
+ * `own`, `exports` and `provides` as functions belong to the body, so they are absent here (D186).
38
+ */
39
+ type FeatureHeaderInput<Id extends string, Imports extends FeatureImportRecord, Requires extends PortRequirementRecord, Exports extends FeatureExportRecord, When extends readonly Condition[]> = {
40
+ readonly body: () => PromiseLike<FeatureBody<Exports>>;
41
+ readonly id: Id;
42
+ readonly imports?: Imports;
43
+ readonly provides?: FeatureDeclaredProvided;
44
+ readonly requires?: Requires;
45
+ /** The feature lives while every condition holds; an empty or absent list is a permanent feature (D93). */
46
+ readonly when?: When;
47
+ };
48
+ /** One word, two forms: the header of a split feature and the eager triple it replaces (D186). */
49
+ interface DefineFeature {
50
+ <const Id extends string, const Imports extends FeatureImportRecord = {}, const Requires extends PortRequirementRecord = {}, const Exports extends FeatureExportRecord = {}, When extends readonly Condition[] = [], const Keys extends PropertyKey = keyof FeatureHeaderInput<Id, Imports, Requires, Exports, When>>(options: ExactKeys<Keys, FeatureHeaderInput<Id, Imports, Requires, Exports, When>> & FeatureHeaderInput<Id, Imports, Requires, Exports, When>): Feature<Id, Imports, {}, Exports, Requires, When>;
51
+ <const Id extends string, const Imports extends FeatureImportRecord = {}, const Requires extends PortRequirementRecord = {}, const Own extends FeatureOwnRecord<Id> = {}, const Exports extends FeatureExportRecord = {}, const Provided extends FeatureProvidedRecord<Id> = {}, When extends readonly Condition[] = [], const Keys extends PropertyKey = keyof FeatureInput<Id, Imports, Requires, Own, Exports, Provided, When>>(options: ExactKeys<Keys, FeatureInput<Id, Imports, Requires, Own, Exports, Provided, When>> & FeatureInput<Id, Imports, Requires, Own, Exports, Provided, When>): Feature<Id, Imports, Own, Exports, Requires, When>;
52
+ }
53
+ export type { DefineFeature, FeatureInput };
@@ -0,0 +1,21 @@
1
+ import type { AnyFeature, FeatureExportDeclarationSnapshot, FeatureRecord } from './feature-authoring-types.js';
2
+ import type { FeatureContractIdentity, FeatureImportRecord } from './feature-contract.js';
3
+ import type { PortRequirementRecord } from './feature-port.js';
4
+ declare function snapshotFeatureOptions(value: unknown): Readonly<Record<string, unknown>>;
5
+ declare function snapshotFeatureImports<Imports extends FeatureImportRecord>(value: Imports | undefined): Imports;
6
+ declare function snapshotFeatureRequirements<Requires extends PortRequirementRecord>(value: Requires | undefined): Requires;
7
+ declare function snapshotFeatureExportDeclaration(value: unknown): FeatureExportDeclarationSnapshot;
8
+ declare function requireFeatureRecord(feature: object): FeatureRecord;
9
+ declare function registerFeatureRecord(feature: object, record: FeatureRecord): void;
10
+ declare function isFeature(value: unknown): value is AnyFeature;
11
+ declare function registerFeatureExportFacade(contract: FeatureContractIdentity, facade: Readonly<Record<string, unknown>>): void;
12
+ declare function isFeatureExportFacade(contract: FeatureContractIdentity, facade: unknown): boolean;
13
+ declare function allocateHiddenKey(keys: Set<string>, preferred: string): string;
14
+ type RuntimeIdentityEntry = {
15
+ count: number;
16
+ readonly key: string;
17
+ };
18
+ declare function indexRuntimeIdentities(runtimeEntries: readonly (readonly [string, unknown])[]): Readonly<WeakMap<object, RuntimeIdentityEntry>>;
19
+ declare function requireString<Literal extends string>(value: unknown, label: string): Literal;
20
+ declare function optionalFunction<Callable>(value: unknown, label: string): Callable | undefined;
21
+ export { allocateHiddenKey, indexRuntimeIdentities, isFeature, isFeatureExportFacade, optionalFunction, registerFeatureExportFacade, registerFeatureRecord, requireFeatureRecord, requireString, snapshotFeatureExportDeclaration, snapshotFeatureImports, snapshotFeatureOptions, snapshotFeatureRequirements, };
@@ -0,0 +1,2 @@
1
+ import{hasOwn as m}from"@opetope/core/internal";import{importedContract as F,getDirectHostContract as w}from"./feature-contract.js";import{getRequiredPort as h}from"./feature-port.js";import{assertPlainRecord as i,dataEntries as u}from"./feature-record.js";const s=new WeakMap,c=new WeakMap,y=new Set(["body","exports","id","imports","own","provides","requires","when"]),E=new Set(["id"]),a=".feature";function b(e){i(e,"defineFeature options");const t=u(e,"defineFeature options");for(const[r]of t)if(!y.has(r))throw new TypeError(`Unknown defineFeature option ${r}.`);for(const r of E)if(!m(e,r))throw new TypeError(`defineFeature requires own ${r}.`);const n=e.id;if(typeof n=="string"&&n.endsWith(a))throw new TypeError(`defineFeature id ${n} must not repeat the ${a} suffix.`);return Object.freeze(Object.fromEntries(t))}function g(e){const t=e??{};i(t,"defineFeature imports");const r=u(t,"defineFeature imports").map(([o,p])=>{const f=F(p);if(f===void 0)throw new TypeError(`defineFeature import ${o} is not a host import or a feature definition.`);return[o,f]});return Object.freeze(Object.fromEntries(r))}function q(e){const t=e??{};i(t,"defineFeature requires");const n=u(t,"defineFeature requires");for(const[r,o]of n)try{h(o)}catch{throw new TypeError(`defineFeature requirement ${r} is not an authentic required Port.`)}return Object.freeze(Object.fromEntries(n))}function d(e,t){if(typeof e!="function")throw new TypeError(`${t} must be a function.`);return e}function x(e){return Object.freeze(e===void 0?{from:()=>({})}:{from:d(e,"defineFeature exports")})}function O(e){const t=s.get(e);if(t===void 0)throw new TypeError("Feature is not authentic.");return t}function j(e,t){s.set(e,t)}function $(e){return typeof e=="object"&&e!==null&&s.has(e)}function l(e,t){c.set(t,e)}function T(e,t){return typeof t=="object"&&t!==null&&c.get(t)===w(e)}function k(e,t){let n=t,r=2;for(;e.has(n);)n=`${t}${String(r++)}`;return e.add(n),n}function z(e){const t=new WeakMap;for(const[n,r]of e){if((typeof r!="object"||r===null)&&typeof r!="function")continue;const o=t.get(r);o===void 0?t.set(r,{count:1,key:n}):o.count+=1}return t}function R(e,t){if(typeof e!="string")throw new TypeError(`${t} must be a string.`);return e}function S(e,t){if(e!==void 0)return d(e,t)}export{k as allocateHiddenKey,z as indexRuntimeIdentities,$ as isFeature,T as isFeatureExportFacade,S as optionalFunction,l as registerFeatureExportFacade,j as registerFeatureRecord,O as requireFeatureRecord,R as requireString,x as snapshotFeatureExportDeclaration,g as snapshotFeatureImports,b as snapshotFeatureOptions,q as snapshotFeatureRequirements};
2
+ //# sourceMappingURL=feature-definition-support.js.map