@opetope/runtime 0.1.0 → 0.2.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 (40) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +24 -13
  3. package/README.ru.md +26 -13
  4. package/dist/feature-authoring-types.d.ts +6 -10
  5. package/dist/feature-authoring.js +1 -1
  6. package/dist/feature-authoring.js.map +1 -1
  7. package/dist/feature-body.d.ts +1 -1
  8. package/dist/feature-body.js.map +1 -1
  9. package/dist/feature-contribution.d.ts +5 -3
  10. package/dist/feature-contribution.js +1 -1
  11. package/dist/feature-contribution.js.map +1 -1
  12. package/dist/feature-definition-api.d.ts +1 -1
  13. package/dist/feature-generation.js +1 -1
  14. package/dist/feature-generation.js.map +1 -1
  15. package/dist/feature-lazy-generation.d.ts +1 -1
  16. package/dist/feature-lazy-generation.js +1 -1
  17. package/dist/feature-lazy-generation.js.map +1 -1
  18. package/dist/feature-own-values.d.ts +10 -0
  19. package/dist/feature-own-values.js +2 -0
  20. package/dist/feature-own-values.js.map +1 -0
  21. package/dist/feature-port-binding.d.ts +2 -1
  22. package/dist/feature-port-binding.js +1 -1
  23. package/dist/feature-port-binding.js.map +1 -1
  24. package/dist/feature-port.d.ts +13 -23
  25. package/dist/feature-port.js +1 -1
  26. package/dist/feature-port.js.map +1 -1
  27. package/dist/public-module-types.d.ts +1 -1
  28. package/dist/public-module.d.ts +1 -1
  29. package/docs/agent-guide.md +11 -4
  30. package/docs/agent-guide.ru.md +11 -4
  31. package/docs/cookbook.md +9 -7
  32. package/docs/cookbook.ru.md +9 -7
  33. package/docs/decisions.md +15 -1
  34. package/docs/how-it-works.md +20 -5
  35. package/docs/how-it-works.ru.md +19 -5
  36. package/docs/releases.md +38 -17
  37. package/docs/releases.ru.md +37 -17
  38. package/docs/spec.md +31 -17
  39. package/docs/spec.ru.md +32 -18
  40. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @opetope/runtime
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2d41c56: Give `provides` only its declaration builders and let nested callbacks select the current instance's live values:
8
+
9
+ - The outer `provides` receives exactly `{ port, register, slot, pipe }`, with no `own`.
10
+ - `port(Port, ({ own }) => own.model.call)` or `port(Port, ({ own }) => own.call)` selects an authentic Call during instance preparation, before readiness and contribution publication. Its callback receives only `{ own }`.
11
+ - `register` factories receive `{ own, imports }`.
12
+ - `slot` factories receive `{ own, imports, model }` for per-mount UI models.
13
+ - `when` predicates and `pipe.fold` handlers receive `{ own, imports, read }`.
14
+
15
+ This is a breaking authoring API change. Remove `own` from the outer `provides` context. Replace both `port(Port, ref)` and `port(Port, { from, select })` with a selector of the ready value, such as `port(Port, ({ own }) => own.order.submit)`. Port targets remain known at declaration; selecting the Call waits for the concrete instance.
16
+
17
+ Contribution contexts no longer expose `exports` or `instance`, and `register` no longer exposes `model`. Replace `context.exports.submit` with the source value, such as `context.own.order.submit`; remove exports needed only for internal composition. Replace instance-based binding with the ready value from `own`. Private model calls can be registered without adding them to the feature's public API. There are no compatibility aliases.
18
+
19
+ ### Patch Changes
20
+
21
+ - @opetope/core@0.2.0
22
+
23
+ ## 0.1.1
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [0de624b]
28
+ - @opetope/core@0.1.1
29
+
3
30
  ## 0.1.0
4
31
 
5
32
  Initial release. See the package README for the supported API and usage.
package/README.md CHANGED
@@ -64,8 +64,9 @@ The data sugar lives in that same `own` section and is lowered into ordinary att
64
64
  - `effect({ from, when?, run, onDispose? })` takes an imported `Readable`, runs for the
65
65
  initial snapshot and then latest-wins; the signal, the per-run timers and the returned disposer belong to that
66
66
  exact run;
67
- - `event(from, subscribe, { run })` gives the `subscribe` context a non-replayed `emit`, serialises the handler and
68
- limits the queue to one pending payload; a failure and an overflow go to the application reporter.
67
+ - `event(from, subscribe, { run, backpressure? })` gives the `subscribe` context a non-replayed `emit`, serialises
68
+ the handler and limits the queue to one pending payload; `latest()` replaces that waiting payload, while the
69
+ default keeps it and reports a newer emit as an overflow. A failure and an overflow go to the application reporter.
69
70
  Retirement aborts the handler, drops the pending payload and drains work already started before the source is released;
70
71
  - `resource(from, target, { key, load, retry?, retention? })` watches a
71
72
  `Readable<T | null | undefined>` returned by the target selector:
@@ -97,9 +98,10 @@ its own context created (D139). The refs of `model()` take no part in the loweri
97
98
  cannot be used in `within`. A contribution component reads the models of its feature through `useModel` inside the
98
99
  contribution mount, which binds them automatically. A single field goes outward through
99
100
  `exports: ({ own }) => ({ x: own.model.field })`. A model call can also provide a port:
100
- `port(SubmitPort, { from: own.order, select: order => order.submit })`. The selector runs once on the materialized
101
- model before publication; the selected authentic `Call` is fenced by the provider's lifetime even if it passes
102
- through a dependency call. The direct `port(SubmitPort, own.submit)` form remains for a declared call ref (D169).
101
+ `port(SubmitPort, ({ own }) => own.order.submit)`. The selector receives only `{ own }` and runs once during instance
102
+ preparation, before readiness and publication; the selected authentic `Call` is fenced by the provider's lifetime
103
+ even if it passes through a dependency call. Use `port(SubmitPort, ({ own }) => own.submit)` for a call declared
104
+ directly in `own`. Both forms select ready Calls; direct refs and `{ from, select }` descriptors are not accepted (D255).
103
105
 
104
106
  `target` is a selector `(source) => Readable<T | null | undefined>` in both feature and model APIs.
105
107
  TypeScript infers the payload of the returned `Readable`, whose shape is checked before opening; a scalar
@@ -111,11 +113,11 @@ Both APIs publish one atomic snapshot `idle | opening | ready | refreshing | err
111
113
  work; demand is set by a separate `retain()` lease. Retention by default merges the StrictMode
112
114
  release/reacquire into one microtask and aborts unfinished work after the last lease. An explicit
113
115
  `scoped({ capacity })` holds the materialization until retirement and bounds the sticky
114
- cache. The UI of a feature gets an instance-bound facade through `bindFeatureResource(instance, own.x)`,
115
- and the React `useResource` holds one lease per mounted consumer.
116
+ cache. A nested contribution factory reads the instance-bound resource as `own.x` and can pass it to the UI;
117
+ the React `useResource` holds one lease per mounted consumer.
116
118
 
117
- There are exactly two policies, and both have a production pilot: `scoped({ capacity })` for retention and
118
- `latest()` for stream backpressure. The former `drop`, `sample` and `queue` are removed together with their branches:
119
+ There are exactly two policy factories, and both have a production pilot: `scoped({ capacity })` for retention and
120
+ `latest()` for stream and event backpressure. The former `drop`, `sample` and `queue` are removed together with their branches:
119
121
  they could not be named from the author vocabulary, so they measured only themselves (D160).
120
122
 
121
123
  ## Calls and lanes
@@ -206,7 +208,7 @@ explicit `key` keeps the current generation. `keyed` holds an independent child
206
208
  unique `string | number` key. A repeated key rejects the whole new snapshot before any change to the children that are
207
209
  already active. Retire fences the children synchronously and waits for their physical drain.
208
210
 
209
- The `provides` section declares both ports and contributions: `port` binds a call ref to a `Port`, while `slot`, `pipe` and
211
+ The `provides` section declares both ports and contributions: `port` selects an owned Call for a `Port`, while `slot`, `pipe` and
210
212
  `register` declare a contribution by the kind of its target:
211
213
 
212
214
  ```ts
@@ -222,16 +224,25 @@ const titleFeature = defineFeature({
222
224
  });
223
225
  ```
224
226
 
225
- The outer factory is synchronous and runs at `defineFeature`, so the target/id/priority are available
226
- to the application compiler before an instance opens. The second argument of `pipe(...)` is a `{ fold }` descriptor:
227
+ The outer factory receives only `{ port, register, slot, pipe }`, runs synchronously at `defineFeature`, and records
228
+ targets, ids and priorities for the application compiler before an instance opens. A `port` selector runs later,
229
+ with the instance's `{ own }`, during preparation. The second argument of `pipe(...)` is a `{ fold }` descriptor:
227
230
  the runtime binds it to the concrete instance after the critical barrier and calls the handler only on a fold (D223).
228
- `slot` and `register` take either a value factory with a concrete `FeatureOpenContext`, which also runs only after
231
+ `slot` and `register` take either a value factory for the concrete instance, which also runs only after
229
232
  that barrier, or a ready contribution value. An exact plain record must
230
233
  return every receipt exactly once; the id of an entry is derived as `featureId.key`. The whole batch goes through
231
234
  preflight and is published atomically. The start of retirement removes the entries synchronously before the async cleanup, including
232
235
  a reentrant retirement from a target listener; a failed opening or an error in the value factory leaves no partially
233
236
  published contributions.
234
237
 
238
+ The `register` value factory receives `{ own, imports }`; `slot` receives `{ own, imports, model }`, where `model`
239
+ declares a UI model created for each mount. `when` predicates and `pipe.fold` handlers receive `{ own, imports, read }`.
240
+ Their `own` contains the live models, calls and resources of that instance (D255). For example,
241
+ `register(target, ({ own }) => ({ key: 'submit', value: own.order.submit }))` registers a private model call without
242
+ exporting it. These contexts have no `exports` or `instance` field; `register` also has no `model` builder.
243
+ The outer `provides` has no `own`; each nested callback receives its own context of ready values. Model calls keep
244
+ their existing lifetime and owned state is read-only outside its model.
245
+
235
246
  The feature API is declared by the `exports: ({ own }) => ({ … })` section: the contract is created from the feature
236
247
  definition under the id `<feature>.exports`, and a consumer imports the definition itself — `imports: { provider: providerFeature }`.
237
248
  Assigned missing/extra keys and incompatible `Call` signatures are rejected at compile time. Host-only values
package/README.ru.md CHANGED
@@ -64,8 +64,9 @@ Cleanup failure по умолчанию best-effort `report`; retryable `quarant
64
64
  - `effect({ from, when?, run, onDispose? })` принимает imported `Readable`, запускается для
65
65
  начального snapshot и затем latest-wins; signal, per-run timers и возвращённый disposer принадлежат
66
66
  exact запуску;
67
- - `event(from, subscribe, { run })` даёт `subscribe`-контексту non-replayed `emit`, сериализует обработчик и
68
- ограничивает очередь одним ожидающим payload; отказ и переполнение уходят reporter-у приложения.
67
+ - `event(from, subscribe, { run, backpressure? })` даёт `subscribe`-контексту non-replayed `emit`, сериализует
68
+ обработчик и ограничивает очередь одним ожидающим payload; `latest()` заменяет этот ожидающий payload, а поведение
69
+ по умолчанию сохраняет его и репортит новый emit как переполнение. Отказ и переполнение уходят reporter-у приложения.
69
70
  Retirement abort-ит handler, удаляет ожидающий payload и drain-ит уже запущенную работу до release source;
70
71
  - `resource(from, target, { key, load, retry?, retention? })` следит за
71
72
  `Readable<T | null | undefined>`, возвращённым target-селектором:
@@ -97,9 +98,11 @@ lookup: requires.lookup }, create)` передаёт readonly-запись ра
97
98
  нельзя использовать в `within`. Компонент вклада читает модели своей фичи через `useModel` внутри монтирования
98
99
  вклада, которое связывает их автоматически. Отдельное поле уходит наружу через
99
100
  `exports: ({ own }) => ({ x: own.model.field })`. Вызов модели также может предоставлять порт:
100
- `port(SubmitPort, { from: own.order, select: order => order.submit })`. Селектор вызывается один раз на готовой
101
- модели до публикации; выбранный подлинный `Call` ограничен временем жизни провайдера, даже если он напрямую
102
- передан из зависимости. Для объявленного call ref остаётся форма `port(SubmitPort, own.submit)` (D169).
101
+ `port(SubmitPort, ({ own }) => own.order.submit)`. Селектор получает только `{ own }` и вызывается один раз при
102
+ подготовке экземпляра, до readiness и публикации; выбранный подлинный `Call` ограничен временем жизни провайдера,
103
+ даже когда он напрямую передан из зависимости. Для вызова, объявленного прямо в `own`, используется
104
+ `port(SubmitPort, ({ own }) => own.submit)`. Обе формы выбирают готовые Calls; прямые refs и дескрипторы
105
+ `{ from, select }` не принимаются (D255).
103
106
 
104
107
  `target` — селектор `(source) => Readable<T | null | undefined>` и у фичи, и у модели.
105
108
  TypeScript выводит payload возвращённого `Readable`, форма которого проверяется перед открытием;
@@ -111,11 +114,11 @@ TypeScript выводит payload возвращённого `Readable`, фор
111
114
  работу; спрос задаёт отдельный lease `retain()`. Удержание по умолчанию объединяет StrictMode
112
115
  release/reacquire в одну микрозадачу и abort-ит незавершённую работу после последнего lease. Явный
113
116
  `scoped({ capacity })` удерживает materialization до retirement и ограничивает sticky
114
- кэша. UI фичи получает привязанный к экземпляру фасад через `bindFeatureResource(instance, own.x)`,
115
- а React `useResource` держит один lease на смонтированного потребителя.
117
+ кэша. Вложенная фабрика вклада получает привязанный к экземпляру ресурс как `own.x` и может передать его в UI;
118
+ React `useResource` держит один lease на смонтированного потребителя.
116
119
 
117
- Политик ровно две, и обе с production-пилотом: `scoped({ capacity })` для удержания и `latest()` для
118
- backpressure стрима. Прежние `drop`, `sample` и `queue` удалены вместе с их ветками: назвать их из авторского
120
+ Фабрик политик ровно две, и обе с production-пилотом: `scoped({ capacity })` для удержания и `latest()` для
121
+ backpressure стрима и события. Прежние `drop`, `sample` и `queue` удалены вместе с их ветками: назвать их из авторского
119
122
  словаря было нельзя, поэтому они измеряли только сами себя (D160).
120
123
 
121
124
  ## Calls and lanes
@@ -205,7 +208,7 @@ own: ({ imports, scope }) => ({
205
208
  уникальный `string | number` key. Повтор ключа отклоняет весь новый snapshot до любых изменений уже
206
209
  активных детей. Retire синхронно фенсит детей и ждёт их физический дренаж.
207
210
 
208
- Секция `provides` объявляет и порты, и вклады: `port` связывает call-ref с `Port`, `slot`, `pipe` и
211
+ Секция `provides` объявляет и порты, и вклады: `port` выбирает владеемый Call для `Port`, `slot`, `pipe` и
209
212
  `register` объявляют вклад по виду цели:
210
213
 
211
214
  ```ts
@@ -221,16 +224,26 @@ const titleFeature = defineFeature({
221
224
  });
222
225
  ```
223
226
 
224
- Внешняя factory синхронна и выполняется при `defineFeature`, поэтому target/id/priority доступны
225
- компилятор приложения до открытия экземпляра. Второй аргумент `pipe(...)` — дескриптор `{ fold }`: рантайм
227
+ Внешняя factory получает только `{ port, register, slot, pipe }`, синхронно выполняется при `defineFeature` и
228
+ записывает target/id/priority для компилятора приложения до открытия экземпляра. Селектор `port` выполняется
229
+ позже, с `{ own }` конкретного экземпляра, при его подготовке. Второй аргумент `pipe(...)` — дескриптор `{ fold }`: рантайм
226
230
  связывает его с конкретным экземпляром после critical barrier и вызывает обработчик только на свёртке (D223).
227
- `slot` и `register` принимают либо value factory с concrete `FeatureOpenContext`, которая тоже выполняется только
231
+ `slot` и `register` принимают либо value factory конкретного экземпляра, которая тоже выполняется только
228
232
  после этого барьера, либо готовое значение вклада. Exact plain record обязан
229
233
  вернуть каждый receipt ровно один раз; id записи выводится как `featureId.key`. Весь batch проходит
230
234
  preflight и публикуется атомарно. Начало retirement синхронно удаляет записи до async cleanup, включая
231
235
  reentrant retirement из target listener; failed opening или ошибка value factory не оставляют частично
232
236
  опубликованных вкладов.
233
237
 
238
+ Value factory `register` получает `{ own, imports }`, а `slot` — `{ own, imports, model }`, где `model` объявляет
239
+ UI-модель, создаваемую на каждое монтирование. Предикаты `when` и обработчики `pipe.fold` получают `{ own, imports, read }`.
240
+ Их `own` содержит готовые модели, вызовы и ресурсы конкретного экземпляра (D255). Например,
241
+ `register(target, ({ own }) => ({ key: 'submit', value: own.order.submit }))` регистрирует вызов внутренней модели
242
+ без его экспорта. В этих контекстах нет полей `exports` и `instance`; у `register` также нет построителя `model`.
243
+ У внешней `provides` нет `own`; каждый вложенный колбэк получает собственный контекст готовых значений.
244
+ Вызовы модели сохраняют своё время жизни, а owned state за пределами модели доступен
245
+ только для чтения.
246
+
234
247
  Feature API объявляется секцией `exports: ({ own }) => ({ … })`: contract создаётся из определения
235
248
  фичи под id `<feature>.exports`, а consumer импортирует само определение — `imports: { provider: providerFeature }`.
236
249
  Assigned missing/extra keys и несовместимые `Call` signatures отклоняются compile-time. Host-only значения
@@ -6,7 +6,6 @@ import type { FeatureAttachBuilder, FeatureCallsBuilder, FeatureImportRef, Featu
6
6
  import type { FeatureCallBuilder, FeatureCallLaneBuilder, FeatureExposureMode, FeatureImportedAttachmentRef } from './feature-call-types.js';
7
7
  import type { FeatureContract, FeatureContractIdentity, FeatureImportRecord, FeatureImportValues } from './feature-contract.js';
8
8
  import type { FeatureContributionDescriptor } from './feature-contribution.js';
9
- import type { ContributionModelBuilder } from './feature-contribution-model.js';
10
9
  import type { FeatureResourceRef, FeatureStreamRef } from './feature-materialization-types.js';
11
10
  import type { FeatureDataDescriptor, FeatureModelBuilder, FeatureModelRef } from './feature-model.js';
12
11
  import type { FeatureRequiredCalls, PortProviderDescriptor, PortRequirementRecord, RequiredPortValues } from './feature-port.js';
@@ -15,7 +14,7 @@ import type { FeatureResourceBuilder } from './feature-resource.js';
15
14
  import type { FeatureScopeBuilder } from './feature-scope-types.js';
16
15
  import type { FeatureStreamBuilder } from './feature-stream.js';
17
16
  import type { FeatureTimers } from './feature-timers.js';
18
- import type { ModuleAttachmentRefIdentity, ModuleCallRef, ModuleInstanceRef, ModuleRuntimeRefIdentity, ModuleScopeRef } from './public-module.js';
17
+ import type { ModuleAttachmentRefIdentity, ModuleCallRef, ModuleRuntimeRefIdentity, ModuleScopeRef } from './public-module.js';
19
18
  declare const featureBrand: unique symbol;
20
19
  type FeatureLifecycleRef<Source, Id extends string, Imports extends FeatureImportRecord, Mode extends FeatureExposureMode> = FeatureImportedAttachmentRef<Source, Id, FeatureImportValues<Imports>, Mode>;
21
20
  type FeatureEffectRunContext<Source, Value> = {
@@ -144,18 +143,15 @@ type FeatureRead = <Value>(source: Readable<Value>) => Value;
144
143
  * is the materialized form `exports` already uses — a model field is a `Readable`, not the ref the declaration
145
144
  * context carries — because a calculation reads values and never constructs a node (D220).
146
145
  */
147
- type FeatureEvaluationContext<Id extends string, Imports extends FeatureImportRecord, Runtime extends FeatureOwnRecord<Id>, Exports extends FeatureExportRecord> = {
148
- readonly exports: Exports;
146
+ type FeatureEvaluationContext<Id extends string, Imports extends FeatureImportRecord, Runtime extends FeatureOwnRecord<Id>> = {
149
147
  readonly imports: FeatureImportValues<Imports>;
150
148
  readonly own: FeatureOwnValues<Id, Runtime>;
151
149
  readonly read: FeatureRead;
152
150
  };
153
- type FeatureOpenContext<Id extends string, Imports extends FeatureImportRecord, Runtime extends FeatureOwnRecord<Id>, Exports extends FeatureExportRecord> = {
154
- readonly exports: Exports;
151
+ /** Contribution factories select live values; the outer `provides` carries only declaration builders (D255). */
152
+ type FeatureOpenContext<Id extends string, Imports extends FeatureImportRecord, Runtime extends FeatureOwnRecord<Id>> = {
155
153
  readonly imports: FeatureImportValues<Imports>;
156
- readonly instance: ModuleInstanceRef<Id>;
157
- readonly model: ContributionModelBuilder;
158
- readonly own: Runtime;
154
+ readonly own: FeatureOwnValues<Id, Runtime>;
159
155
  };
160
156
  type FeatureRecord = {
161
157
  readonly contributions: readonly FeatureContributionDescriptor[];
@@ -193,4 +189,4 @@ type FeatureExportDeclarationSnapshot = {
193
189
  readonly own: unknown;
194
190
  }) => unknown;
195
191
  };
196
- export type { AnyFeature, AttachmentLowering, AttachmentProjection, AttachmentRecipe, Feature, FeatureAttachBuilder, FeatureCallBuilder, FeatureCallOperation, FeatureConditionsOf, FeatureEffectBuilder, FeatureEvaluationContext, FeatureEventBuilder, FeatureExportDeclarationSnapshot, FeatureExportRecord, FeatureExportsFactory, FeatureExportsOf, FeatureExposureMode, FeatureIdOf, FeatureImportRecord, FeatureImportRef, FeatureImportsOf, FeatureImportValues, FeatureLifecycleRef, FeatureOpenContext, FeatureOwnBuilder, FeatureOwnRecord, FeatureRecord, FeatureRequirementsOf, };
192
+ export type { AnyFeature, AttachmentLowering, AttachmentProjection, AttachmentRecipe, Feature, FeatureAttachBuilder, FeatureCallBuilder, FeatureCallOperation, FeatureConditionsOf, FeatureEffectBuilder, FeatureEvaluationContext, FeatureEventBuilder, FeatureExportDeclarationSnapshot, FeatureExportRecord, FeatureExportsFactory, FeatureExportsOf, FeatureExposureMode, FeatureIdOf, FeatureImportRecord, FeatureImportRef, FeatureImportsOf, FeatureImportValues, FeatureLifecycleRef, FeatureOpenContext, FeatureOwnBuilder, FeatureOwnRecord, FeatureOwnValues, FeatureRecord, FeatureRequirementsOf, };
@@ -1,2 +1,2 @@
1
- import{declarationId as I}from"@opetope/core";import{snapshotFeatureConditions as q}from"./condition.js";import{preloadFeatureBody as M,featureBody as k,requireFeatureBody as L}from"./feature-body.js";import{defineFeatureContract as z}from"./feature-contract.js";import{isFeatureContributionDeclaration as x,snapshotFeatureContributions as V,createFeatureContributionBuilders as G}from"./feature-contribution.js";import{snapshotFeatureOptions as H,requireString as W,snapshotFeatureExportDeclaration as K,optionalFunction as F,snapshotFeatureImports as g,snapshotFeatureRequirements as E,registerFeatureRecord as P,requireFeatureRecord as A}from"./feature-definition-support.js";import{isFeature as ve}from"./feature-definition-support.js";import{snapshotDeclaredProvided as J,registerLazyFeature as N,assertBodyMatchesHeader as Q}from"./feature-lazy.js";import{lowerFeatureOwn as D}from"./feature-own-lowering.js";import{createPortBuilder as U,snapshotPortProviders as X}from"./feature-port.js";import{dataEntries as y,assertPlainRecord as Y}from"./feature-record.js";import{defineModule as Z}from"./public-module-definition.js";const C=Z;function _(r){if((typeof r!="object"||r===null)&&typeof r!="function")return!1;let e=r;for(;e!==null;){const o=Object.getOwnPropertyDescriptor(e,"then");if(o!==void 0)return"value"in o&&typeof o.value=="function";e=Object.getPrototypeOf(e)}return!1}function S(r,e,o,c){const i=G();if(e===void 0)return Object.freeze({contributions:Object.freeze([]),providers:Object.freeze([])});const n=e({own:o,pipe:i.pipe,port:U(),register:i.register,slot:i.slot});if(_(n))throw Promise.resolve(n).catch(()=>{}),new TypeError("defineFeature provides must return synchronously.");Y(n,"defineFeature provides result");const d=y(n,"defineFeature provides result"),s=d.filter(([,u])=>x(i,u)),a=d.filter(([,u])=>!x(i,u));return Object.freeze({contributions:V(r,i,s),providers:X(c,a)})}const h=new WeakMap;function ee(r,e){const o=F(e.own,"defineFeature own")??(()=>({})),c=K(e.exports),i=F(e.provides,"defineFeature provides");let n;const d=C({id:r.id,runtime:l=>(n=D(l,{build:o,id:r.id,importKeys:r.importKeys,imports:r.imports,requirementKeys:r.requirementKeys}),n.loweredRuntime)});if(n===void 0)throw new TypeError("defineFeature own did not produce a public record.");const{dataDescriptors:s,importScopeKey:a,publicRuntime:u,requirementScopeKey:p}=n,m=u,t=new Set(Object.values(m).filter(l=>typeof l=="object")),{contributions:f,providers:w}=S(r.id,i,u,t);return{own:m,record:{contributions:f,dataDescriptors:s,exports:c.from,importKeys:r.importKeys,importScopeKey:a,module:d,providers:w,requirementKeys:r.requirementKeys,requirementScopeKey:p}}}function re(r,e){for(const t of["exports","own"])if(r[t]!==void 0)throw new TypeError(`defineFeature ${e} declares body and ${t}: the implementation belongs to the body.`);if(typeof r.provides=="function")throw new TypeError(`defineFeature ${e} declares body and a provides factory: the header declares targets only.`);const o=r.body;if(typeof o!="function")throw new TypeError("defineFeature body must be a loader function.");const c=g(r.imports),i=E(r.requires),n=Object.freeze(y(c,"defineFeature imports").map(([t])=>t)),d=Object.freeze(y(i,"defineFeature requires").map(([t])=>t)),s=J(e,r.provides),a=z(`${e}.exports`);let u=Object.freeze({});const p=Object.freeze({exports:a,id:I(e),imports:c,get own(){return u},requires:i,when:q(r.when)}),m={id:e,importKeys:n,imports:c,requirementKeys:d};return P(p,{contributions:s.contributions,dataDescriptors:Object.freeze([]),exports:()=>({}),importKeys:n,importScopeKey:void 0,module:Object.freeze({}),providers:s.providers.map(t=>Object.freeze({...t,target:void 0})),requirementKeys:d,requirementScopeKey:void 0}),N(p,{load:()=>Promise.resolve(o()).then(t=>L(e,t)),materialize:t=>{const f=ee(m,t);return Q(e,s,f.record),u=Object.freeze(f.own),f.record}}),h.set(a,p),p}function te(r){const e=H(r),o=W(e.id,"defineFeature id");if(e.body!==void 0)return re(e,o);const c=K(e.exports),i=F(e.own,"defineFeature own")??(()=>({})),n=c.from,d=F(e.provides,"defineFeature provides"),s=g(e.imports),a=E(e.requires),u=y(a,"defineFeature requires"),p=Object.freeze(y(s,"defineFeature imports").map(([b])=>b)),m=Object.freeze(u.map(([b])=>b));let t;const f=C({id:o,runtime:b=>(t=D(b,{build:i,id:o,importKeys:p,imports:s,requirementKeys:m}),t.loweredRuntime)});if(t===void 0)throw new TypeError("defineFeature own did not produce a public record.");const{dataDescriptors:w,importScopeKey:l,publicRuntime:v,requirementScopeKey:B}=t,R=new Set(Object.values(v)),{contributions:T,providers:$}=S(o,d,v,R),j=z(`${o}.exports`),O=Object.freeze({exports:j,id:f.id,imports:s,own:v,requires:a,when:q(e.when)});return P(O,{contributions:T,dataDescriptors:w,exports:n,importKeys:p,importScopeKey:l,module:f,providers:$,requirementKeys:m,requirementScopeKey:B}),h.set(j,O),O}function oe(r){return h.get(r)}const ne=Object.assign(te,{body:k,preload:M});function ie(r){return A(r).contributions}export{ne as defineFeature,oe as getFeatureByExportContract,ie as getFeatureContributionDescriptors,ve as isFeature};
1
+ import{declarationId as $}from"@opetope/core";import{snapshotFeatureConditions as O}from"./condition.js";import{preloadFeatureBody as I,featureBody as M,requireFeatureBody as k}from"./feature-body.js";import{defineFeatureContract as q}from"./feature-contract.js";import{isFeatureContributionDeclaration as z,snapshotFeatureContributions as L,createFeatureContributionBuilders as G}from"./feature-contribution.js";import{snapshotFeatureOptions as H,requireString as W,snapshotFeatureExportDeclaration as x,optionalFunction as b,snapshotFeatureImports as j,snapshotFeatureRequirements as K,registerFeatureRecord as g,requireFeatureRecord as A}from"./feature-definition-support.js";import{isFeature as we}from"./feature-definition-support.js";import{snapshotDeclaredProvided as J,registerLazyFeature as N,assertBodyMatchesHeader as Q}from"./feature-lazy.js";import{lowerFeatureOwn as E}from"./feature-own-lowering.js";import{createPortBuilder as U,snapshotPortProviders as V}from"./feature-port.js";import{dataEntries as y,assertPlainRecord as X}from"./feature-record.js";import{defineModule as Y}from"./public-module-definition.js";const P=Y;function Z(r){if((typeof r!="object"||r===null)&&typeof r!="function")return!1;let e=r;for(;e!==null;){const t=Object.getOwnPropertyDescriptor(e,"then");if(t!==void 0)return"value"in t&&typeof t.value=="function";e=Object.getPrototypeOf(e)}return!1}function D(r,e){const t=G();if(e===void 0)return Object.freeze({contributions:Object.freeze([]),providers:Object.freeze([])});const i=e({pipe:t.pipe,port:U(),register:t.register,slot:t.slot});if(Z(i))throw Promise.resolve(i).catch(()=>{}),new TypeError("defineFeature provides must return synchronously.");X(i,"defineFeature provides result");const u=y(i,"defineFeature provides result"),s=u.filter(([,n])=>z(t,n)),c=u.filter(([,n])=>!z(t,n));return Object.freeze({contributions:L(r,t,s),providers:V(c)})}const v=new WeakMap;function _(r,e){const t=b(e.own,"defineFeature own")??(()=>({})),i=x(e.exports),u=b(e.provides,"defineFeature provides");let s;const c=P({id:r.id,runtime:F=>(s=E(F,{build:t,id:r.id,importKeys:r.importKeys,imports:r.imports,requirementKeys:r.requirementKeys}),s.loweredRuntime)});if(s===void 0)throw new TypeError("defineFeature own did not produce a public record.");const{dataDescriptors:n,importScopeKey:p,publicRuntime:f,requirementScopeKey:d}=s,m=f,{contributions:o,providers:a}=D(r.id,u);return{own:m,record:{contributions:o,dataDescriptors:n,exports:i.from,importKeys:r.importKeys,importScopeKey:p,module:c,providers:a,requirementKeys:r.requirementKeys,requirementScopeKey:d}}}function ee(r,e){for(const o of["exports","own"])if(r[o]!==void 0)throw new TypeError(`defineFeature ${e} declares body and ${o}: the implementation belongs to the body.`);if(typeof r.provides=="function")throw new TypeError(`defineFeature ${e} declares body and a provides factory: the header declares targets only.`);const t=r.body;if(typeof t!="function")throw new TypeError("defineFeature body must be a loader function.");const i=j(r.imports),u=K(r.requires),s=Object.freeze(y(i,"defineFeature imports").map(([o])=>o)),c=Object.freeze(y(u,"defineFeature requires").map(([o])=>o)),n=J(e,r.provides),p=q(`${e}.exports`);let f=Object.freeze({});const d=Object.freeze({exports:p,id:$(e),imports:i,get own(){return f},requires:u,when:O(r.when)}),m={id:e,importKeys:s,imports:i,requirementKeys:c};return g(d,{contributions:n.contributions,dataDescriptors:Object.freeze([]),exports:()=>({}),importKeys:s,importScopeKey:void 0,module:Object.freeze({}),providers:n.providers.map(o=>Object.freeze({...o,target:void 0})),requirementKeys:c,requirementScopeKey:void 0}),N(d,{load:()=>Promise.resolve(t()).then(o=>k(e,o)),materialize:o=>{const a=_(m,o);return Q(e,n,a.record),f=Object.freeze(a.own),a.record}}),v.set(p,d),d}function re(r){const e=H(r),t=W(e.id,"defineFeature id");if(e.body!==void 0)return ee(e,t);const i=x(e.exports),u=b(e.own,"defineFeature own")??(()=>({})),s=i.from,c=b(e.provides,"defineFeature provides"),n=j(e.imports),p=K(e.requires),f=y(p,"defineFeature requires"),d=Object.freeze(y(n,"defineFeature imports").map(([l])=>l)),m=Object.freeze(f.map(([l])=>l));let o;const a=P({id:t,runtime:l=>(o=E(l,{build:u,id:t,importKeys:d,imports:n,requirementKeys:m}),o.loweredRuntime)});if(o===void 0)throw new TypeError("defineFeature own did not produce a public record.");const{dataDescriptors:F,importScopeKey:C,publicRuntime:B,requirementScopeKey:R}=o,{contributions:S,providers:T}=D(t,c),h=q(`${t}.exports`),w=Object.freeze({exports:h,id:a.id,imports:n,own:B,requires:p,when:O(e.when)});return g(w,{contributions:S,dataDescriptors:F,exports:s,importKeys:d,importScopeKey:C,module:a,providers:T,requirementKeys:m,requirementScopeKey:R}),v.set(h,w),w}function te(r){return v.get(r)}const oe=Object.assign(re,{body:M,preload:I});function ne(r){return A(r).contributions}export{oe as defineFeature,te as getFeatureByExportContract,ne as getFeatureContributionDescriptors,we as isFeature};
2
2
  //# sourceMappingURL=feature-authoring.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"feature-authoring.js","sources":["../src/feature-authoring.ts"],"sourcesContent":["import { declarationId } from '@opetope/core';\n\nimport { snapshotFeatureConditions } from './condition';\nimport type { Condition } from './condition';\nimport type {\n AnyFeature,\n Feature,\n FeatureEvaluationContext,\n FeatureExportRecord,\n FeatureImportRecord,\n FeatureOpenContext,\n FeatureOwnBuilder,\n FeatureOwnRecord,\n FeatureRecord,\n} from './feature-authoring-types';\nimport { featureBody, preloadFeatureBody, requireFeatureBody } from './feature-body';\nimport type { LoadedFeatureBody } from './feature-body';\nimport { defineFeatureContract } from './feature-contract';\nimport {\n createFeatureContributionBuilders,\n isFeatureContributionDeclaration,\n snapshotFeatureContributions,\n} from './feature-contribution';\nimport type { FeatureContributionDescriptor } from './feature-contribution';\nimport type { DefineFeature, FeatureInput } from './feature-definition-api';\nimport {\n isFeature,\n optionalFunction,\n registerFeatureRecord,\n requireFeatureRecord,\n requireString,\n snapshotFeatureExportDeclaration,\n snapshotFeatureImports,\n snapshotFeatureOptions,\n snapshotFeatureRequirements,\n} from './feature-definition-support';\nimport { assertBodyMatchesHeader, registerLazyFeature, snapshotDeclaredProvided } from './feature-lazy';\nimport { lowerFeatureOwn } from './feature-own-lowering';\nimport type { FeatureOwnLoweringResult } from './feature-own-lowering';\nimport { createPortBuilder, snapshotPortProviders } from './feature-port';\nimport type { FeatureProvidedRecord, FeatureProvidesFactory, PortRequirementRecord } from './feature-port';\nimport { assertPlainRecord, dataEntries } from './feature-record';\nimport type { ExactKeys } from './feature-record';\nimport { defineModule } from './public-module';\nimport type { ModuleDefinition, ModuleRuntimeBuilder, ModuleRuntimeRecord } from './public-module';\n\ntype DefineGeneratedModule = <Id extends string, Runtime extends ModuleRuntimeRecord<Id>>(options: {\n readonly id: Id;\n readonly runtime: (builder: ModuleRuntimeBuilder<Id>) => Runtime;\n}) => ModuleDefinition<Id, Runtime>;\n\n// The public defineModule guard rejects union author records. Feature lowering has already applied\n// the same guard before it appends collision-free private refs to one concrete `own` record.\nconst defineGeneratedModule = defineModule as DefineGeneratedModule;\n\nfunction hasDataThen(value: unknown): boolean {\n if ((typeof value !== 'object' || value === null) && typeof value !== 'function') return false;\n\n let source: object | null = value;\n\n while (source !== null) {\n const descriptor = Object.getOwnPropertyDescriptor(source, 'then');\n\n if (descriptor !== undefined) return 'value' in descriptor && typeof descriptor.value === 'function';\n\n source = Object.getPrototypeOf(source) as object | null;\n }\n\n return false;\n}\n\n/** `provides` returns ports and contributions in one record; each entry is split by the builder that made it. */\nfunction buildFeatureProvided<\n Id extends string,\n Context,\n Evaluation,\n Runtime extends FeatureOwnRecord<Id>,\n Provided extends FeatureProvidedRecord<Id>,\n>(\n featureId: string,\n build: FeatureProvidesFactory<Id, Context, Evaluation, Runtime, Provided> | undefined,\n own: Runtime,\n runtimeValues: ReadonlySet<object>,\n): Readonly<{\n contributions: readonly FeatureContributionDescriptor[];\n providers: FeatureRecord['providers'];\n}> {\n const builders = createFeatureContributionBuilders<Context, Evaluation>();\n\n if (build === undefined) return Object.freeze({ contributions: Object.freeze([]), providers: Object.freeze([]) });\n\n const value = build({\n own,\n pipe: builders.pipe,\n port: createPortBuilder<Id>(),\n register: builders.register,\n slot: builders.slot,\n });\n\n if (hasDataThen(value)) {\n void Promise.resolve(value).catch(() => undefined);\n\n throw new TypeError('defineFeature provides must return synchronously.');\n }\n\n assertPlainRecord(value, 'defineFeature provides result');\n const entries = dataEntries(value, 'defineFeature provides result');\n const contributionEntries = entries.filter(([, entry]) => isFeatureContributionDeclaration(builders as never, entry));\n const providerEntries = entries.filter(([, entry]) => !isFeatureContributionDeclaration(builders as never, entry));\n\n return Object.freeze({\n contributions: snapshotFeatureContributions(featureId, builders, contributionEntries),\n providers: snapshotPortProviders(runtimeValues, providerEntries),\n });\n}\n\nconst featuresByExportContract = new WeakMap<object, AnyFeature>();\n\ntype FeatureHeader = {\n readonly exportsContract: object;\n readonly id: string;\n readonly importKeys: readonly string[];\n readonly imports: FeatureImportRecord;\n readonly requirementKeys: readonly string[];\n readonly requirements: PortRequirementRecord;\n};\n\n/**\n * The heavy half of a feature: lowering `own`, running `provides` and selecting `exports`. An eager feature runs it\n * at declaration; a lazy one runs the very same code once its body has loaded (D186).\n */\nfunction materializeFeatureImplementation(\n header: FeatureHeader,\n body: LoadedFeatureBody,\n): { readonly own: Readonly<Record<string, unknown>>; readonly record: FeatureRecord } {\n const build =\n optionalFunction<(builder: never) => Readonly<Record<string, unknown>>>(body.own, 'defineFeature own') ??\n (() => ({}));\n const exportDeclaration = snapshotFeatureExportDeclaration(body.exports);\n const buildProvides = optionalFunction<FeatureProvidesFactory<string, never, never, never, never>>(\n body.provides,\n 'defineFeature provides',\n );\n let lowering: FeatureOwnLoweringResult<string, never> | undefined;\n const module = defineGeneratedModule({\n id: header.id,\n runtime: low => {\n lowering = lowerFeatureOwn(low as never, {\n build: build as never,\n id: header.id,\n importKeys: header.importKeys,\n imports: header.imports,\n requirementKeys: header.requirementKeys,\n }) as FeatureOwnLoweringResult<string, never>;\n\n return lowering.loweredRuntime;\n },\n });\n\n if (lowering === undefined) throw new TypeError('defineFeature own did not produce a public record.');\n\n const { dataDescriptors, importScopeKey, publicRuntime, requirementScopeKey } = lowering;\n const own = publicRuntime as unknown as Readonly<Record<string, unknown>>;\n const runtimeValues = new Set<object>(\n Object.values(own).filter((value): value is object => typeof value === 'object'),\n );\n const { contributions, providers } = buildFeatureProvided(\n header.id,\n buildProvides as never,\n publicRuntime,\n runtimeValues,\n );\n\n return {\n own,\n record: {\n contributions,\n dataDescriptors,\n exports: exportDeclaration.from,\n importKeys: header.importKeys,\n importScopeKey,\n module,\n providers,\n requirementKeys: header.requirementKeys,\n requirementScopeKey,\n },\n };\n}\n\n/**\n * D186: a header opens nothing by itself. It registers the identity, the edges and the declared border, and keeps\n * the loader; the body is materialized once, on the first open, and every later open reuses the same code.\n */\nfunction defineLazyFeature(source: Readonly<Record<string, unknown>>, id: string): AnyFeature {\n for (const key of ['exports', 'own'] as const) {\n if (source[key] !== undefined) {\n throw new TypeError(`defineFeature ${id} declares body and ${key}: the implementation belongs to the body.`);\n }\n }\n\n if (typeof source['provides'] === 'function') {\n throw new TypeError(`defineFeature ${id} declares body and a provides factory: the header declares targets only.`);\n }\n\n const load = source['body'];\n\n if (typeof load !== 'function') throw new TypeError('defineFeature body must be a loader function.');\n\n const imports = snapshotFeatureImports(source['imports'] as FeatureImportRecord | undefined);\n const requirements = snapshotFeatureRequirements(source['requires'] as PortRequirementRecord | undefined);\n const importKeys = Object.freeze(dataEntries(imports, 'defineFeature imports').map(([key]) => key));\n const requirementKeys = Object.freeze(dataEntries(requirements, 'defineFeature requires').map(([key]) => key));\n const declared = snapshotDeclaredProvided(id, source['provides']);\n const exportsContract = defineFeatureContract<{}>(`${id}.exports`);\n // The refs of `own` are minted by the body, so the header reads them through the record it materializes later.\n let materializedOwn: Readonly<Record<string, unknown>> = Object.freeze({});\n const feature = Object.freeze({\n exports: exportsContract,\n id: declarationId(id),\n imports,\n get own(): Readonly<Record<string, unknown>> {\n return materializedOwn;\n },\n requires: requirements,\n when: snapshotFeatureConditions(source['when']),\n }) as unknown as AnyFeature;\n const header: FeatureHeader = { exportsContract, id, importKeys, imports, requirementKeys, requirements };\n\n registerFeatureRecord(feature, {\n contributions: declared.contributions,\n dataDescriptors: Object.freeze([]),\n exports: () => ({}),\n importKeys,\n importScopeKey: undefined,\n module: Object.freeze({}),\n providers: declared.providers.map(entry => Object.freeze({ ...entry, target: undefined })) as never,\n requirementKeys,\n requirementScopeKey: undefined,\n });\n registerLazyFeature(feature, {\n load: () => Promise.resolve((load as () => PromiseLike<unknown>)()).then(value => requireFeatureBody(id, value)),\n materialize: (body: LoadedFeatureBody) => {\n const materialized = materializeFeatureImplementation(header, body);\n assertBodyMatchesHeader(id, declared, materialized.record);\n materializedOwn = Object.freeze(materialized.own);\n\n return materialized.record;\n },\n });\n featuresByExportContract.set(exportsContract, feature);\n\n return feature;\n}\n\nfunction defineFeatureImplementation<\n const Id extends string,\n const Imports extends FeatureImportRecord,\n const Runtime extends FeatureOwnRecord<Id>,\n const Exports extends FeatureExportRecord,\n const Requires extends PortRequirementRecord = {},\n const Provided extends FeatureProvidedRecord<Id> = FeatureProvidedRecord<Id>,\n When extends readonly Condition[] = readonly Condition[],\n const Keys extends PropertyKey = keyof FeatureInput<Id, Imports, Requires, Runtime, Exports, Provided, When>,\n>(\n options: ExactKeys<Keys, FeatureInput<Id, Imports, Requires, Runtime, Exports, Provided, When>> &\n FeatureInput<Id, Imports, Requires, Runtime, Exports, Provided, When>,\n): Feature<Id, Imports, Runtime, Exports, Requires, When> {\n const source = snapshotFeatureOptions(options);\n const id = requireString<Id>(source['id'], 'defineFeature id');\n\n if (source['body'] !== undefined) {\n return defineLazyFeature(source, id) as unknown as Feature<Id, Imports, Runtime, Exports, Requires, When>;\n }\n\n const exportDeclaration = snapshotFeatureExportDeclaration(source['exports']);\n const build =\n optionalFunction<(builder: FeatureOwnBuilder<Id, Imports, Requires>) => Runtime>(\n source['own'],\n 'defineFeature own',\n ) ?? ((() => ({})) as unknown as (builder: FeatureOwnBuilder<Id, Imports, Requires>) => Runtime);\n const selectExports = exportDeclaration.from;\n const buildProvides = optionalFunction<\n FeatureProvidesFactory<\n Id,\n FeatureOpenContext<Id, Imports, Runtime, Exports>,\n FeatureEvaluationContext<Id, Imports, Runtime, Exports>,\n Runtime,\n Provided\n >\n >(source['provides'], 'defineFeature provides');\n\n const imports = snapshotFeatureImports(source['imports'] as Imports);\n const requirements = snapshotFeatureRequirements(source['requires'] as Requires | undefined);\n const requirementEntries = dataEntries(requirements, 'defineFeature requires');\n const importKeys = Object.freeze(dataEntries(imports, 'defineFeature imports').map(([key]) => key));\n const requirementKeys = Object.freeze(requirementEntries.map(([key]) => key));\n let lowering: FeatureOwnLoweringResult<Id, Runtime> | undefined;\n const module = defineGeneratedModule({\n id,\n runtime: low => {\n lowering = lowerFeatureOwn(low, {\n build,\n id,\n importKeys,\n imports,\n requirementKeys,\n });\n\n return lowering.loweredRuntime;\n },\n });\n\n if (lowering === undefined) throw new TypeError('defineFeature own did not produce a public record.');\n\n const { dataDescriptors, importScopeKey, publicRuntime, requirementScopeKey } = lowering;\n\n const runtimeValues = new Set(Object.values(publicRuntime));\n const { contributions, providers } = buildFeatureProvided<\n Id,\n FeatureOpenContext<Id, Imports, Runtime, Exports>,\n FeatureEvaluationContext<Id, Imports, Runtime, Exports>,\n Runtime,\n Provided\n >(id, buildProvides, publicRuntime, runtimeValues);\n\n const exportsContract = defineFeatureContract<{}>(`${id}.exports`);\n const feature = Object.freeze({\n exports: exportsContract,\n id: module.id,\n imports,\n own: publicRuntime,\n requires: requirements,\n when: snapshotFeatureConditions(source['when']),\n }) as unknown as Feature<Id, Imports, Runtime, Exports, Requires, When>;\n registerFeatureRecord(feature, {\n contributions,\n dataDescriptors,\n exports: selectExports,\n importKeys,\n importScopeKey,\n module,\n providers,\n requirementKeys,\n requirementScopeKey,\n });\n featuresByExportContract.set(exportsContract, feature);\n\n return feature;\n}\n\n/** `imports` stores the contract, so the application world resolves a feature edge back to the feature (D68). */\nfunction getFeatureByExportContract(contract: object): AnyFeature | undefined {\n return featuresByExportContract.get(contract);\n}\n\n// D186: one word, two forms — the eager triple and the lazy header, so the overload set is the public type.\n// D207: `defineFeature.body` writes the implementation of a header; a member costs no new word of the vocabulary.\nconst defineFeature = Object.assign(defineFeatureImplementation as unknown as DefineFeature, {\n body: featureBody,\n preload: preloadFeatureBody,\n});\n\nfunction getFeatureModule<\n const Id extends string,\n const Imports extends FeatureImportRecord,\n const Runtime extends FeatureOwnRecord<Id>,\n const Exports extends FeatureExportRecord,\n>(feature: Feature<Id, Imports, Runtime, Exports>): ModuleDefinition<Id> {\n return requireFeatureRecord(feature).module as ModuleDefinition<Id>;\n}\n\nfunction getFeatureContributionDescriptors(feature: AnyFeature): FeatureRecord['contributions'] {\n return requireFeatureRecord(feature).contributions;\n}\n\nexport { defineFeature, getFeatureByExportContract, getFeatureContributionDescriptors, getFeatureModule, isFeature };\nexport type {\n AnyFeature,\n Feature,\n FeatureConditionsOf,\n FeatureExportRecord,\n FeatureExportsOf,\n FeatureIdOf,\n FeatureImportRecord,\n FeatureImportsOf,\n FeatureImportValues,\n FeatureOwnRecord,\n FeatureRequirementsOf,\n} from './feature-authoring-types';\n"],"names":["defineGeneratedModule","defineModule","hasDataThen","value","source","descriptor","buildFeatureProvided","featureId","build","own","runtimeValues","builders","createFeatureContributionBuilders","createPortBuilder","assertPlainRecord","entries","dataEntries","contributionEntries","entry","isFeatureContributionDeclaration","providerEntries","snapshotFeatureContributions","snapshotPortProviders","featuresByExportContract","materializeFeatureImplementation","header","body","optionalFunction","exportDeclaration","snapshotFeatureExportDeclaration","buildProvides","lowering","module","low","lowerFeatureOwn","dataDescriptors","importScopeKey","publicRuntime","requirementScopeKey","contributions","providers","defineLazyFeature","id","key","load","imports","snapshotFeatureImports","requirements","snapshotFeatureRequirements","importKeys","requirementKeys","declared","snapshotDeclaredProvided","exportsContract","defineFeatureContract","materializedOwn","feature","declarationId","snapshotFeatureConditions","registerFeatureRecord","registerLazyFeature","requireFeatureBody","materialized","assertBodyMatchesHeader","defineFeatureImplementation","options","snapshotFeatureOptions","requireString","selectExports","requirementEntries","getFeatureByExportContract","contract","defineFeature","featureBody","preloadFeatureBody","getFeatureContributionDescriptors","requireFeatureRecord"],"mappings":"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"}
1
+ {"version":3,"file":"feature-authoring.js","sources":["../src/feature-authoring.ts"],"sourcesContent":["import { declarationId } from '@opetope/core';\n\nimport { snapshotFeatureConditions } from './condition';\nimport type { Condition } from './condition';\nimport type {\n AnyFeature,\n Feature,\n FeatureEvaluationContext,\n FeatureExportRecord,\n FeatureImportRecord,\n FeatureOpenContext,\n FeatureOwnBuilder,\n FeatureOwnRecord,\n FeatureRecord,\n} from './feature-authoring-types';\nimport { featureBody, preloadFeatureBody, requireFeatureBody } from './feature-body';\nimport type { LoadedFeatureBody } from './feature-body';\nimport { defineFeatureContract } from './feature-contract';\nimport {\n createFeatureContributionBuilders,\n isFeatureContributionDeclaration,\n snapshotFeatureContributions,\n} from './feature-contribution';\nimport type { FeatureContributionDescriptor } from './feature-contribution';\nimport type { DefineFeature, FeatureInput } from './feature-definition-api';\nimport {\n isFeature,\n optionalFunction,\n registerFeatureRecord,\n requireFeatureRecord,\n requireString,\n snapshotFeatureExportDeclaration,\n snapshotFeatureImports,\n snapshotFeatureOptions,\n snapshotFeatureRequirements,\n} from './feature-definition-support';\nimport { assertBodyMatchesHeader, registerLazyFeature, snapshotDeclaredProvided } from './feature-lazy';\nimport { lowerFeatureOwn } from './feature-own-lowering';\nimport type { FeatureOwnLoweringResult } from './feature-own-lowering';\nimport { createPortBuilder, snapshotPortProviders } from './feature-port';\nimport type { FeatureProvidedRecord, FeatureProvidesFactory, PortRequirementRecord } from './feature-port';\nimport { assertPlainRecord, dataEntries } from './feature-record';\nimport type { ExactKeys } from './feature-record';\nimport { defineModule } from './public-module';\nimport type { ModuleDefinition, ModuleRuntimeBuilder, ModuleRuntimeRecord } from './public-module';\n\ntype DefineGeneratedModule = <Id extends string, Runtime extends ModuleRuntimeRecord<Id>>(options: {\n readonly id: Id;\n readonly runtime: (builder: ModuleRuntimeBuilder<Id>) => Runtime;\n}) => ModuleDefinition<Id, Runtime>;\n\n// The public defineModule guard rejects union author records. Feature lowering has already applied\n// the same guard before it appends collision-free private refs to one concrete `own` record.\nconst defineGeneratedModule = defineModule as DefineGeneratedModule;\n\nfunction hasDataThen(value: unknown): boolean {\n if ((typeof value !== 'object' || value === null) && typeof value !== 'function') return false;\n\n let source: object | null = value;\n\n while (source !== null) {\n const descriptor = Object.getOwnPropertyDescriptor(source, 'then');\n\n if (descriptor !== undefined) return 'value' in descriptor && typeof descriptor.value === 'function';\n\n source = Object.getPrototypeOf(source) as object | null;\n }\n\n return false;\n}\n\n/** `provides` returns ports and contributions in one record; each entry is split by the builder that made it. */\nfunction buildFeatureProvided<\n Id extends string,\n Context,\n Evaluation,\n Runtime extends FeatureOwnRecord<Id>,\n Provided extends FeatureProvidedRecord<Id>,\n>(\n featureId: string,\n build: FeatureProvidesFactory<Id, Context, Evaluation, Runtime, Provided> | undefined,\n): Readonly<{\n contributions: readonly FeatureContributionDescriptor[];\n providers: FeatureRecord['providers'];\n}> {\n const builders = createFeatureContributionBuilders<Context, Evaluation>();\n\n if (build === undefined) return Object.freeze({ contributions: Object.freeze([]), providers: Object.freeze([]) });\n\n const value = build({\n pipe: builders.pipe,\n port: createPortBuilder(),\n register: builders.register,\n slot: builders.slot,\n });\n\n if (hasDataThen(value)) {\n void Promise.resolve(value).catch(() => undefined);\n\n throw new TypeError('defineFeature provides must return synchronously.');\n }\n\n assertPlainRecord(value, 'defineFeature provides result');\n const entries = dataEntries(value, 'defineFeature provides result');\n const contributionEntries = entries.filter(([, entry]) => isFeatureContributionDeclaration(builders as never, entry));\n const providerEntries = entries.filter(([, entry]) => !isFeatureContributionDeclaration(builders as never, entry));\n\n return Object.freeze({\n contributions: snapshotFeatureContributions(featureId, builders, contributionEntries),\n providers: snapshotPortProviders(providerEntries),\n });\n}\n\nconst featuresByExportContract = new WeakMap<object, AnyFeature>();\n\ntype FeatureHeader = {\n readonly exportsContract: object;\n readonly id: string;\n readonly importKeys: readonly string[];\n readonly imports: FeatureImportRecord;\n readonly requirementKeys: readonly string[];\n readonly requirements: PortRequirementRecord;\n};\n\n/**\n * The heavy half of a feature: lowering `own`, running `provides` and selecting `exports`. An eager feature runs it\n * at declaration; a lazy one runs the very same code once its body has loaded (D186).\n */\nfunction materializeFeatureImplementation(\n header: FeatureHeader,\n body: LoadedFeatureBody,\n): { readonly own: Readonly<Record<string, unknown>>; readonly record: FeatureRecord } {\n const build =\n optionalFunction<(builder: never) => Readonly<Record<string, unknown>>>(body.own, 'defineFeature own') ??\n (() => ({}));\n const exportDeclaration = snapshotFeatureExportDeclaration(body.exports);\n const buildProvides = optionalFunction<FeatureProvidesFactory<string, never, never, never, never>>(\n body.provides,\n 'defineFeature provides',\n );\n let lowering: FeatureOwnLoweringResult<string, never> | undefined;\n const module = defineGeneratedModule({\n id: header.id,\n runtime: low => {\n lowering = lowerFeatureOwn(low as never, {\n build: build as never,\n id: header.id,\n importKeys: header.importKeys,\n imports: header.imports,\n requirementKeys: header.requirementKeys,\n }) as FeatureOwnLoweringResult<string, never>;\n\n return lowering.loweredRuntime;\n },\n });\n\n if (lowering === undefined) throw new TypeError('defineFeature own did not produce a public record.');\n\n const { dataDescriptors, importScopeKey, publicRuntime, requirementScopeKey } = lowering;\n const own = publicRuntime as unknown as Readonly<Record<string, unknown>>;\n const { contributions, providers } = buildFeatureProvided(header.id, buildProvides as never);\n\n return {\n own,\n record: {\n contributions,\n dataDescriptors,\n exports: exportDeclaration.from,\n importKeys: header.importKeys,\n importScopeKey,\n module,\n providers,\n requirementKeys: header.requirementKeys,\n requirementScopeKey,\n },\n };\n}\n\n/**\n * D186: a header opens nothing by itself. It registers the identity, the edges and the declared border, and keeps\n * the loader; the body is materialized once, on the first open, and every later open reuses the same code.\n */\nfunction defineLazyFeature(source: Readonly<Record<string, unknown>>, id: string): AnyFeature {\n for (const key of ['exports', 'own'] as const) {\n if (source[key] !== undefined) {\n throw new TypeError(`defineFeature ${id} declares body and ${key}: the implementation belongs to the body.`);\n }\n }\n\n if (typeof source['provides'] === 'function') {\n throw new TypeError(`defineFeature ${id} declares body and a provides factory: the header declares targets only.`);\n }\n\n const load = source['body'];\n\n if (typeof load !== 'function') throw new TypeError('defineFeature body must be a loader function.');\n\n const imports = snapshotFeatureImports(source['imports'] as FeatureImportRecord | undefined);\n const requirements = snapshotFeatureRequirements(source['requires'] as PortRequirementRecord | undefined);\n const importKeys = Object.freeze(dataEntries(imports, 'defineFeature imports').map(([key]) => key));\n const requirementKeys = Object.freeze(dataEntries(requirements, 'defineFeature requires').map(([key]) => key));\n const declared = snapshotDeclaredProvided(id, source['provides']);\n const exportsContract = defineFeatureContract<{}>(`${id}.exports`);\n // The refs of `own` are minted by the body, so the header reads them through the record it materializes later.\n let materializedOwn: Readonly<Record<string, unknown>> = Object.freeze({});\n const feature = Object.freeze({\n exports: exportsContract,\n id: declarationId(id),\n imports,\n get own(): Readonly<Record<string, unknown>> {\n return materializedOwn;\n },\n requires: requirements,\n when: snapshotFeatureConditions(source['when']),\n }) as unknown as AnyFeature;\n const header: FeatureHeader = { exportsContract, id, importKeys, imports, requirementKeys, requirements };\n\n registerFeatureRecord(feature, {\n contributions: declared.contributions,\n dataDescriptors: Object.freeze([]),\n exports: () => ({}),\n importKeys,\n importScopeKey: undefined,\n module: Object.freeze({}),\n providers: declared.providers.map(entry => Object.freeze({ ...entry, target: undefined })) as never,\n requirementKeys,\n requirementScopeKey: undefined,\n });\n registerLazyFeature(feature, {\n load: () => Promise.resolve((load as () => PromiseLike<unknown>)()).then(value => requireFeatureBody(id, value)),\n materialize: (body: LoadedFeatureBody) => {\n const materialized = materializeFeatureImplementation(header, body);\n assertBodyMatchesHeader(id, declared, materialized.record);\n materializedOwn = Object.freeze(materialized.own);\n\n return materialized.record;\n },\n });\n featuresByExportContract.set(exportsContract, feature);\n\n return feature;\n}\n\nfunction defineFeatureImplementation<\n const Id extends string,\n const Imports extends FeatureImportRecord,\n const Runtime extends FeatureOwnRecord<Id>,\n const Exports extends FeatureExportRecord,\n const Requires extends PortRequirementRecord = {},\n const Provided extends FeatureProvidedRecord<Id> = FeatureProvidedRecord<Id>,\n When extends readonly Condition[] = readonly Condition[],\n const Keys extends PropertyKey = keyof FeatureInput<Id, Imports, Requires, Runtime, Exports, Provided, When>,\n>(\n options: ExactKeys<Keys, FeatureInput<Id, Imports, Requires, Runtime, Exports, Provided, When>> &\n FeatureInput<Id, Imports, Requires, Runtime, Exports, Provided, When>,\n): Feature<Id, Imports, Runtime, Exports, Requires, When> {\n const source = snapshotFeatureOptions(options);\n const id = requireString<Id>(source['id'], 'defineFeature id');\n\n if (source['body'] !== undefined) {\n return defineLazyFeature(source, id) as unknown as Feature<Id, Imports, Runtime, Exports, Requires, When>;\n }\n\n const exportDeclaration = snapshotFeatureExportDeclaration(source['exports']);\n const build =\n optionalFunction<(builder: FeatureOwnBuilder<Id, Imports, Requires>) => Runtime>(\n source['own'],\n 'defineFeature own',\n ) ?? ((() => ({})) as unknown as (builder: FeatureOwnBuilder<Id, Imports, Requires>) => Runtime);\n const selectExports = exportDeclaration.from;\n const buildProvides = optionalFunction<\n FeatureProvidesFactory<\n Id,\n FeatureOpenContext<Id, Imports, Runtime>,\n FeatureEvaluationContext<Id, Imports, Runtime>,\n Runtime,\n Provided\n >\n >(source['provides'], 'defineFeature provides');\n\n const imports = snapshotFeatureImports(source['imports'] as Imports);\n const requirements = snapshotFeatureRequirements(source['requires'] as Requires | undefined);\n const requirementEntries = dataEntries(requirements, 'defineFeature requires');\n const importKeys = Object.freeze(dataEntries(imports, 'defineFeature imports').map(([key]) => key));\n const requirementKeys = Object.freeze(requirementEntries.map(([key]) => key));\n let lowering: FeatureOwnLoweringResult<Id, Runtime> | undefined;\n const module = defineGeneratedModule({\n id,\n runtime: low => {\n lowering = lowerFeatureOwn(low, {\n build,\n id,\n importKeys,\n imports,\n requirementKeys,\n });\n\n return lowering.loweredRuntime;\n },\n });\n\n if (lowering === undefined) throw new TypeError('defineFeature own did not produce a public record.');\n\n const { dataDescriptors, importScopeKey, publicRuntime, requirementScopeKey } = lowering;\n\n const { contributions, providers } = buildFeatureProvided<\n Id,\n FeatureOpenContext<Id, Imports, Runtime>,\n FeatureEvaluationContext<Id, Imports, Runtime>,\n Runtime,\n Provided\n >(id, buildProvides);\n\n const exportsContract = defineFeatureContract<{}>(`${id}.exports`);\n const feature = Object.freeze({\n exports: exportsContract,\n id: module.id,\n imports,\n own: publicRuntime,\n requires: requirements,\n when: snapshotFeatureConditions(source['when']),\n }) as unknown as Feature<Id, Imports, Runtime, Exports, Requires, When>;\n registerFeatureRecord(feature, {\n contributions,\n dataDescriptors,\n exports: selectExports,\n importKeys,\n importScopeKey,\n module,\n providers,\n requirementKeys,\n requirementScopeKey,\n });\n featuresByExportContract.set(exportsContract, feature);\n\n return feature;\n}\n\n/** `imports` stores the contract, so the application world resolves a feature edge back to the feature (D68). */\nfunction getFeatureByExportContract(contract: object): AnyFeature | undefined {\n return featuresByExportContract.get(contract);\n}\n\n// D186: one word, two forms — the eager triple and the lazy header, so the overload set is the public type.\n// D207: `defineFeature.body` writes the implementation of a header; a member costs no new word of the vocabulary.\nconst defineFeature = Object.assign(defineFeatureImplementation as unknown as DefineFeature, {\n body: featureBody,\n preload: preloadFeatureBody,\n});\n\nfunction getFeatureModule<\n const Id extends string,\n const Imports extends FeatureImportRecord,\n const Runtime extends FeatureOwnRecord<Id>,\n const Exports extends FeatureExportRecord,\n>(feature: Feature<Id, Imports, Runtime, Exports>): ModuleDefinition<Id> {\n return requireFeatureRecord(feature).module as ModuleDefinition<Id>;\n}\n\nfunction getFeatureContributionDescriptors(feature: AnyFeature): FeatureRecord['contributions'] {\n return requireFeatureRecord(feature).contributions;\n}\n\nexport { defineFeature, getFeatureByExportContract, getFeatureContributionDescriptors, getFeatureModule, isFeature };\nexport type {\n AnyFeature,\n Feature,\n FeatureConditionsOf,\n FeatureExportRecord,\n FeatureExportsOf,\n FeatureIdOf,\n FeatureImportRecord,\n FeatureImportsOf,\n FeatureImportValues,\n FeatureOwnRecord,\n FeatureRequirementsOf,\n} from './feature-authoring-types';\n"],"names":["defineGeneratedModule","defineModule","hasDataThen","value","source","descriptor","buildFeatureProvided","featureId","build","builders","createFeatureContributionBuilders","createPortBuilder","assertPlainRecord","entries","dataEntries","contributionEntries","entry","isFeatureContributionDeclaration","providerEntries","snapshotFeatureContributions","snapshotPortProviders","featuresByExportContract","materializeFeatureImplementation","header","body","optionalFunction","exportDeclaration","snapshotFeatureExportDeclaration","buildProvides","lowering","module","low","lowerFeatureOwn","dataDescriptors","importScopeKey","publicRuntime","requirementScopeKey","own","contributions","providers","defineLazyFeature","id","key","load","imports","snapshotFeatureImports","requirements","snapshotFeatureRequirements","importKeys","requirementKeys","declared","snapshotDeclaredProvided","exportsContract","defineFeatureContract","materializedOwn","feature","declarationId","snapshotFeatureConditions","registerFeatureRecord","registerLazyFeature","requireFeatureBody","materialized","assertBodyMatchesHeader","defineFeatureImplementation","options","snapshotFeatureOptions","requireString","selectExports","requirementEntries","getFeatureByExportContract","contract","defineFeature","featureBody","preloadFeatureBody","getFeatureContributionDescriptors","requireFeatureRecord"],"mappings":"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"}
@@ -22,7 +22,7 @@ interface FeatureBody<Exports extends FeatureExportRecord = FeatureExportRecord>
22
22
  type FeatureBodyInput<Id extends string, Imports extends FeatureImportRecord, Requires extends PortRequirementRecord, Own extends FeatureOwnRecord<Id>, Exports extends FeatureExportRecord, Provided extends FeatureProvidedRecord<Id>> = {
23
23
  readonly exports?: FeatureExportsFactory<Id, Own, Exports>;
24
24
  readonly own?: ExactNonUnionRuntime<Own> & ((builder: FeatureOwnBuilder<Id, Imports, NoInfer<Requires>>) => Own);
25
- readonly provides?: FeatureProvidesFactory<Id, FeatureOpenContext<Id, Imports, Own, Exports>, FeatureEvaluationContext<Id, Imports, Own, Exports>, Own, Provided>;
25
+ readonly provides?: FeatureProvidesFactory<Id, FeatureOpenContext<Id, Imports, Own>, FeatureEvaluationContext<Id, Imports, Own>, Own, Provided>;
26
26
  };
27
27
  type FeatureImportRecordOf<Header> = Header extends Feature<infer _Id, infer Imports, infer _Own, infer _Exports, infer _Requires, infer _When> ? Imports : never;
28
28
  type FeatureExportRecordOf<Header> = Header extends Feature<infer _Id, infer _Imports, infer _Own, infer Exports, infer _Requires, infer _When> ? Exports : never;
@@ -1 +1 @@
1
- {"version":3,"file":"feature-body.js","sources":["../src/feature-body.ts"],"sourcesContent":["import type { Condition } from './condition';\nimport type {\n Feature,\n FeatureEvaluationContext,\n FeatureExportRecord,\n FeatureExportsFactory,\n FeatureOpenContext,\n FeatureOwnBuilder,\n FeatureOwnRecord,\n} from './feature-authoring-types';\nimport type { FeatureImportRecord } from './feature-contract';\nimport { isFeature } from './feature-definition-support';\nimport { getLazyFeature, loadFeatureBody } from './feature-lazy';\nimport type { FeatureProvidedRecord, FeatureProvidesFactory, PortRequirementRecord } from './feature-port';\nimport type { ExactNonUnionRuntime } from './public-module';\n\n/**\n * D186: the header of a feature and its body are two files. The header is the identity every consumer imports; the\n * body is the implementation the loader brings in later. `FeatureBody<Exports>` is the narrow view the header names\n * in the return type of its loader — it carries the export contract and nothing else, so the header never depends on\n * the body module and the two types cannot form a cycle.\n */\ninterface FeatureBody<Exports extends FeatureExportRecord = FeatureExportRecord> {\n readonly exports?: (context: never) => Exports;\n readonly own?: unknown;\n readonly provides?: unknown;\n}\n\n/**\n * The body as its own file writes it: `own`, `exports` and `provides` of the declared header, with the builders typed\n * from the header's id, imports and requirements. The record is plain data — the header already owns the identity,\n * so a body needs no second authentic declaration.\n */\ntype FeatureBodyInput<\n Id extends string,\n Imports extends FeatureImportRecord,\n Requires extends PortRequirementRecord,\n Own extends FeatureOwnRecord<Id>,\n Exports extends FeatureExportRecord,\n Provided extends FeatureProvidedRecord<Id>,\n> = {\n readonly exports?: FeatureExportsFactory<Id, Own, Exports>;\n readonly own?: ExactNonUnionRuntime<Own> & ((builder: FeatureOwnBuilder<Id, Imports, NoInfer<Requires>>) => Own);\n readonly provides?: FeatureProvidesFactory<\n Id,\n FeatureOpenContext<Id, Imports, Own, Exports>,\n FeatureEvaluationContext<Id, Imports, Own, Exports>,\n Own,\n Provided\n >;\n};\n\ntype FeatureImportRecordOf<Header> =\n Header extends Feature<infer _Id, infer Imports, infer _Own, infer _Exports, infer _Requires, infer _When>\n ? Imports\n : never;\ntype FeatureExportRecordOf<Header> =\n Header extends Feature<infer _Id, infer _Imports, infer _Own, infer Exports, infer _Requires, infer _When>\n ? Exports\n : never;\ntype FeatureRequirementRecordOf<Header> =\n Header extends Feature<infer _Id, infer _Imports, infer _Own, infer _Exports, infer Requires, infer _When>\n ? Requires\n : never;\ntype FeatureHeaderIdOf<Header> =\n Header extends Feature<infer Id, infer _Imports, infer _Own, infer _Exports, infer _Requires, infer _When>\n ? Id\n : never;\n\n/** What the body file annotates itself with: everything the header already fixed, and the own record it invents. */\ntype FeatureBodyOf<\n Header,\n Own extends FeatureOwnRecord<FeatureHeaderIdOf<Header>> = FeatureOwnRecord<FeatureHeaderIdOf<Header>>,\n Provided extends FeatureProvidedRecord<FeatureHeaderIdOf<Header>> = FeatureProvidedRecord<FeatureHeaderIdOf<Header>>,\n> = FeatureBodyInput<\n FeatureHeaderIdOf<Header>,\n FeatureImportRecordOf<Header>,\n FeatureRequirementRecordOf<Header>,\n Own,\n FeatureExportRecordOf<Header>,\n Provided\n>;\n\ntype LoadedFeatureBody = {\n readonly exports?: unknown;\n readonly own?: unknown;\n readonly provides?: unknown;\n};\n\nconst bodyKeys = new Set(['exports', 'own', 'provides']);\n\nfunction assertPlainBodyRecord(featureId: string, value: unknown): asserts value is object {\n if (typeof value !== 'object' || value === null) {\n throw new TypeError(`Feature ${featureId} body must be a record of own, exports and provides.`);\n }\n\n const prototype: unknown = Object.getPrototypeOf(value);\n\n if (prototype !== Object.prototype && prototype !== null) {\n throw new TypeError(`Feature ${featureId} body must be a plain object record.`);\n }\n}\n\n/** The loader is foreign code, so what it returns is checked before anything of the feature is materialized. */\nfunction requireFeatureBody(featureId: string, value: unknown): LoadedFeatureBody {\n assertPlainBodyRecord(featureId, value);\n\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key !== 'string' || !bodyKeys.has(key)) {\n throw new TypeError(`Feature ${featureId} body contains unknown field ${String(key)}.`);\n }\n }\n\n return value;\n}\n\n/**\n * D207: the body is written through `defineFeature.body`, a member of the word that already names a feature. A\n * function is what lets TypeScript infer `own` for `exports` and `provides`, exactly as the eager form does, and a\n * member spends no new word on the author's vocabulary. It mints no identity — the header already is the feature.\n */\nfunction featureBody<\n Id extends string,\n Imports extends FeatureImportRecord,\n Exports extends FeatureExportRecord,\n Requires extends PortRequirementRecord,\n Own extends FeatureOwnRecord<Id> = {},\n Provided extends FeatureProvidedRecord<Id> = {},\n>(\n header: Feature<Id, Imports, FeatureOwnRecord<Id>, Exports, Requires, readonly Condition[]>,\n body: FeatureBodyInput<Id, Imports, Requires, Own, Exports, Provided>,\n): FeatureBody<Exports> {\n if (!isFeature(header)) throw new TypeError('featureBody takes the feature its header declared.');\n\n return requireFeatureBody(header.id, body) as FeatureBody<Exports>;\n}\n\n/**\n * D186 asked for a way to fetch the code without opening anything: a host that knows a feature is about to be\n * needed — a hovered entry point, an idle callback — warms its chunk here. It opens no instance, holds no lease and\n * publishes no state; a failure is the caller's to observe, and the next open tries the loader again.\n */\nfunction preloadFeatureBody(header: unknown): Promise<void> {\n if (!isFeature(header)) throw new TypeError('defineFeature.preload takes a feature declaration.');\n\n if (getLazyFeature(header) === undefined) return Promise.resolve();\n\n return loadFeatureBody(header).then(() => undefined);\n}\n\nexport { featureBody, preloadFeatureBody, requireFeatureBody };\nexport type { FeatureBody, FeatureBodyOf, LoadedFeatureBody };\n"],"names":["bodyKeys","assertPlainBodyRecord","featureId","value","prototype","requireFeatureBody","key","featureBody","header","body","isFeature","preloadFeatureBody","getLazyFeature","loadFeatureBody"],"mappings":"oIAyFA,MAAMA,EAAW,IAAI,IAAI,CAAC,UAAW,MAAO,UAAU,CAAC,EAEvD,SAASC,EAAsBC,EAAmBC,EAAc,CAC9D,GAAI,OAAOA,GAAU,UAAYA,IAAU,KACzC,MAAM,IAAI,UAAU,WAAWD,CAAS,sDAAsD,EAGhG,MAAME,EAAqB,OAAO,eAAeD,CAAK,EAEtD,GAAIC,IAAc,OAAO,WAAaA,IAAc,KAClD,MAAM,IAAI,UAAU,WAAWF,CAAS,sCAAsC,CAElF,CAGA,SAASG,EAAmBH,EAAmBC,EAAc,CAC3DF,EAAsBC,EAAWC,CAAK,EAEtC,UAAWG,KAAO,QAAQ,QAAQH,CAAK,EACrC,GAAI,OAAOG,GAAQ,UAAY,CAACN,EAAS,IAAIM,CAAG,EAC9C,MAAM,IAAI,UAAU,WAAWJ,CAAS,gCAAgC,OAAOI,CAAG,CAAC,GAAG,EAI1F,OAAOH,CACT,CAOA,SAASI,EAQPC,EACAC,EAAqE,CAErE,GAAI,CAACC,EAAUF,CAAM,EAAG,MAAM,IAAI,UAAU,oDAAoD,EAEhG,OAAOH,EAAmBG,EAAO,GAAIC,CAAI,CAC3C,CAOA,SAASE,EAAmBH,EAAe,CACzC,GAAI,CAACE,EAAUF,CAAM,EAAG,MAAM,IAAI,UAAU,oDAAoD,EAEhG,OAAII,EAAeJ,CAAM,IAAM,OAAkB,QAAQ,QAAO,EAEzDK,EAAgBL,CAAM,EAAE,KAAK,IAAA,EAAe,CACrD"}
1
+ {"version":3,"file":"feature-body.js","sources":["../src/feature-body.ts"],"sourcesContent":["import type { Condition } from './condition';\nimport type {\n Feature,\n FeatureEvaluationContext,\n FeatureExportRecord,\n FeatureExportsFactory,\n FeatureOpenContext,\n FeatureOwnBuilder,\n FeatureOwnRecord,\n} from './feature-authoring-types';\nimport type { FeatureImportRecord } from './feature-contract';\nimport { isFeature } from './feature-definition-support';\nimport { getLazyFeature, loadFeatureBody } from './feature-lazy';\nimport type { FeatureProvidedRecord, FeatureProvidesFactory, PortRequirementRecord } from './feature-port';\nimport type { ExactNonUnionRuntime } from './public-module';\n\n/**\n * D186: the header of a feature and its body are two files. The header is the identity every consumer imports; the\n * body is the implementation the loader brings in later. `FeatureBody<Exports>` is the narrow view the header names\n * in the return type of its loader — it carries the export contract and nothing else, so the header never depends on\n * the body module and the two types cannot form a cycle.\n */\ninterface FeatureBody<Exports extends FeatureExportRecord = FeatureExportRecord> {\n readonly exports?: (context: never) => Exports;\n readonly own?: unknown;\n readonly provides?: unknown;\n}\n\n/**\n * The body as its own file writes it: `own`, `exports` and `provides` of the declared header, with the builders typed\n * from the header's id, imports and requirements. The record is plain data — the header already owns the identity,\n * so a body needs no second authentic declaration.\n */\ntype FeatureBodyInput<\n Id extends string,\n Imports extends FeatureImportRecord,\n Requires extends PortRequirementRecord,\n Own extends FeatureOwnRecord<Id>,\n Exports extends FeatureExportRecord,\n Provided extends FeatureProvidedRecord<Id>,\n> = {\n readonly exports?: FeatureExportsFactory<Id, Own, Exports>;\n readonly own?: ExactNonUnionRuntime<Own> & ((builder: FeatureOwnBuilder<Id, Imports, NoInfer<Requires>>) => Own);\n readonly provides?: FeatureProvidesFactory<\n Id,\n FeatureOpenContext<Id, Imports, Own>,\n FeatureEvaluationContext<Id, Imports, Own>,\n Own,\n Provided\n >;\n};\n\ntype FeatureImportRecordOf<Header> =\n Header extends Feature<infer _Id, infer Imports, infer _Own, infer _Exports, infer _Requires, infer _When>\n ? Imports\n : never;\ntype FeatureExportRecordOf<Header> =\n Header extends Feature<infer _Id, infer _Imports, infer _Own, infer Exports, infer _Requires, infer _When>\n ? Exports\n : never;\ntype FeatureRequirementRecordOf<Header> =\n Header extends Feature<infer _Id, infer _Imports, infer _Own, infer _Exports, infer Requires, infer _When>\n ? Requires\n : never;\ntype FeatureHeaderIdOf<Header> =\n Header extends Feature<infer Id, infer _Imports, infer _Own, infer _Exports, infer _Requires, infer _When>\n ? Id\n : never;\n\n/** What the body file annotates itself with: everything the header already fixed, and the own record it invents. */\ntype FeatureBodyOf<\n Header,\n Own extends FeatureOwnRecord<FeatureHeaderIdOf<Header>> = FeatureOwnRecord<FeatureHeaderIdOf<Header>>,\n Provided extends FeatureProvidedRecord<FeatureHeaderIdOf<Header>> = FeatureProvidedRecord<FeatureHeaderIdOf<Header>>,\n> = FeatureBodyInput<\n FeatureHeaderIdOf<Header>,\n FeatureImportRecordOf<Header>,\n FeatureRequirementRecordOf<Header>,\n Own,\n FeatureExportRecordOf<Header>,\n Provided\n>;\n\ntype LoadedFeatureBody = {\n readonly exports?: unknown;\n readonly own?: unknown;\n readonly provides?: unknown;\n};\n\nconst bodyKeys = new Set(['exports', 'own', 'provides']);\n\nfunction assertPlainBodyRecord(featureId: string, value: unknown): asserts value is object {\n if (typeof value !== 'object' || value === null) {\n throw new TypeError(`Feature ${featureId} body must be a record of own, exports and provides.`);\n }\n\n const prototype: unknown = Object.getPrototypeOf(value);\n\n if (prototype !== Object.prototype && prototype !== null) {\n throw new TypeError(`Feature ${featureId} body must be a plain object record.`);\n }\n}\n\n/** The loader is foreign code, so what it returns is checked before anything of the feature is materialized. */\nfunction requireFeatureBody(featureId: string, value: unknown): LoadedFeatureBody {\n assertPlainBodyRecord(featureId, value);\n\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key !== 'string' || !bodyKeys.has(key)) {\n throw new TypeError(`Feature ${featureId} body contains unknown field ${String(key)}.`);\n }\n }\n\n return value;\n}\n\n/**\n * D207: the body is written through `defineFeature.body`, a member of the word that already names a feature. A\n * function is what lets TypeScript infer `own` for `exports` and `provides`, exactly as the eager form does, and a\n * member spends no new word on the author's vocabulary. It mints no identity — the header already is the feature.\n */\nfunction featureBody<\n Id extends string,\n Imports extends FeatureImportRecord,\n Exports extends FeatureExportRecord,\n Requires extends PortRequirementRecord,\n Own extends FeatureOwnRecord<Id> = {},\n Provided extends FeatureProvidedRecord<Id> = {},\n>(\n header: Feature<Id, Imports, FeatureOwnRecord<Id>, Exports, Requires, readonly Condition[]>,\n body: FeatureBodyInput<Id, Imports, Requires, Own, Exports, Provided>,\n): FeatureBody<Exports> {\n if (!isFeature(header)) throw new TypeError('featureBody takes the feature its header declared.');\n\n return requireFeatureBody(header.id, body) as FeatureBody<Exports>;\n}\n\n/**\n * D186 asked for a way to fetch the code without opening anything: a host that knows a feature is about to be\n * needed — a hovered entry point, an idle callback — warms its chunk here. It opens no instance, holds no lease and\n * publishes no state; a failure is the caller's to observe, and the next open tries the loader again.\n */\nfunction preloadFeatureBody(header: unknown): Promise<void> {\n if (!isFeature(header)) throw new TypeError('defineFeature.preload takes a feature declaration.');\n\n if (getLazyFeature(header) === undefined) return Promise.resolve();\n\n return loadFeatureBody(header).then(() => undefined);\n}\n\nexport { featureBody, preloadFeatureBody, requireFeatureBody };\nexport type { FeatureBody, FeatureBodyOf, LoadedFeatureBody };\n"],"names":["bodyKeys","assertPlainBodyRecord","featureId","value","prototype","requireFeatureBody","key","featureBody","header","body","isFeature","preloadFeatureBody","getLazyFeature","loadFeatureBody"],"mappings":"oIAyFA,MAAMA,EAAW,IAAI,IAAI,CAAC,UAAW,MAAO,UAAU,CAAC,EAEvD,SAASC,EAAsBC,EAAmBC,EAAc,CAC9D,GAAI,OAAOA,GAAU,UAAYA,IAAU,KACzC,MAAM,IAAI,UAAU,WAAWD,CAAS,sDAAsD,EAGhG,MAAME,EAAqB,OAAO,eAAeD,CAAK,EAEtD,GAAIC,IAAc,OAAO,WAAaA,IAAc,KAClD,MAAM,IAAI,UAAU,WAAWF,CAAS,sCAAsC,CAElF,CAGA,SAASG,EAAmBH,EAAmBC,EAAc,CAC3DF,EAAsBC,EAAWC,CAAK,EAEtC,UAAWG,KAAO,QAAQ,QAAQH,CAAK,EACrC,GAAI,OAAOG,GAAQ,UAAY,CAACN,EAAS,IAAIM,CAAG,EAC9C,MAAM,IAAI,UAAU,WAAWJ,CAAS,gCAAgC,OAAOI,CAAG,CAAC,GAAG,EAI1F,OAAOH,CACT,CAOA,SAASI,EAQPC,EACAC,EAAqE,CAErE,GAAI,CAACC,EAAUF,CAAM,EAAG,MAAM,IAAI,UAAU,oDAAoD,EAEhG,OAAOH,EAAmBG,EAAO,GAAIC,CAAI,CAC3C,CAOA,SAASE,EAAmBH,EAAe,CACzC,GAAI,CAACE,EAAUF,CAAM,EAAG,MAAM,IAAI,UAAU,oDAAoD,EAEhG,OAAII,EAAeJ,CAAM,IAAM,OAAkB,QAAQ,QAAO,EAEzDK,EAAgBL,CAAM,EAAE,KAAK,IAAA,EAAe,CACrD"}
@@ -1,6 +1,6 @@
1
1
  import type { DeclarationId, Readable } from '@opetope/core';
2
2
  import type { ContributionPublication, ContributionPublicationInput, ContributionTarget, PipeHandler } from '@opetope/core/internal';
3
- import type { ContributionModelDeclaration, ContributionModelProps } from './feature-contribution-model.js';
3
+ import type { ContributionModelBuilder, ContributionModelDeclaration, ContributionModelProps } from './feature-contribution-model.js';
4
4
  import type { ExactInput } from './feature-record.js';
5
5
  declare const featureContributionBrand: unique symbol;
6
6
  interface FeatureContribution {
@@ -70,7 +70,9 @@ type CheckedContribution<Spec, Value> = ExactContributionProps<Spec, Value> exte
70
70
  * inference circular and TypeScript would fall back to the constraint.
71
71
  */
72
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>;
73
+ <Value extends object, const Spec extends NoInfer<Value>, const Options extends FeatureContributionOptions<Evaluation>>(target: ContributionTarget<Value>, contribution: (context: Context & {
74
+ readonly model: ContributionModelBuilder;
75
+ }) => Spec, options?: ExactInput<Options, FeatureContributionOptions<Evaluation>>): CheckedContribution<Spec, Value>;
74
76
  <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
77
  }
76
78
  /**
@@ -90,7 +92,6 @@ interface FeatureContributionDescriptor {
90
92
  }
91
93
  /** The live values of one instance, read by every reactive calculation that instance declared (D220). */
92
94
  type FeatureEvaluationValues = Readonly<{
93
- exports: unknown;
94
95
  imports: unknown;
95
96
  own: unknown;
96
97
  }>;
@@ -104,6 +105,7 @@ type SnapshotContributionOptions = {
104
105
  type FeaturePipeFold = (value: unknown, meta: unknown, context: unknown) => unknown;
105
106
  type PendingFeatureContribution = SnapshotContributionOptions & {
106
107
  readonly fold: FeaturePipeFold | undefined;
108
+ readonly slot: boolean;
107
109
  readonly target: ContributionPublicationInput['target'];
108
110
  readonly value: FeatureContributionValueFactory<unknown, unknown>;
109
111
  };
@@ -1,2 +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};
1
+ import{declarationId as F,computed as E}from"@opetope/core";import{isContributionTarget as g,publishContributions as T}from"@opetope/core/internal";import{contributionModel as j}from"./feature-contribution-model.js";import{assertPlainRecord as a,dataEntries as d,assertExactDataKeys as C}from"./feature-record.js";const b=new WeakMap,y=new WeakMap,w=new WeakMap,h=new WeakSet,O=new Set(["priority","when"]);function x(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 z(e,t){if(t===void 0)throw new TypeError("Feature contribution when predicate has no instance to read.");return E({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 S(e){if(e===void 0)return;if(typeof e=="function"){const r=e;return n=>z(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 k(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])=>!O.has(n)))throw new TypeError("Feature contribution options accept only priority and when.");const r=Object.fromEntries(t);return{priority:x(r.priority),when:S(r.when)}}function M(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");C(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 P(){const e=new WeakMap,t=new Set,r=(n,o,i,s)=>{if(!g(n))throw new TypeError(`Feature ${s} target is not authentic.`);const u=s==="pipe"?M(o):void 0,c=typeof o=="function"?o:()=>o,f=Object.freeze({});return t.add(f),e.set(f,{...k(i),fold:u,slot:s==="slot"&&typeof o=="function",target:n,value:c}),f};return Object.freeze({declarations:e,declared:t,pipe:(n,o,i)=>r(n,o,i,"pipe"),register:(n,o,i)=>r(n,o,i,"register"),slot:((n,o,i)=>r(n,o,i,"slot"))})}function V(e,t){return typeof t=="object"&&t!==null&&e.declarations.has(t)}function W(e,t){b.set(e,t.value),t.slot&&h.add(e),t.when!==void 0&&y.set(e,t.when),t.fold!==void 0&&w.set(e,t.fold)}function $(e,t,r){const n=new Set,o=r.map(([i,s])=>{const u=typeof s=="object"&&s!==null?t.declarations.get(s):void 0;if(u===void 0)throw new TypeError(`Feature contribution ${i} is not authentic.`);if(n.has(s))throw new TypeError("Every feature contribution must be returned under exactly one key.");n.add(s);const c=Object.freeze({id:F(`${e}.${i}`),priority:u.priority,target:u.target});return W(c,u),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 D(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 i=r[r.length-1];return i===void 0?o.getSnapshot():i(o)}});return(o,i,s)=>{r.push(s.read);try{return e(o,i,n)}finally{r.pop()}}}function K(e,t,r,n,o){let i;const s=e.map(u=>{const c=b.get(u);if(c===void 0)throw new TypeError(`Feature contribution ${u.id} is not authentic.`);const f=w.get(u),l=h.has(u)?i??(i=Object.freeze({...t,model:j})):t,m=f===void 0?c(l):D(f,o),p=y.get(u);return{id:u.id,...n===void 0?{}:{owner:n},priority:u.priority,target:u.target,value:m,when:p==null?void 0:p(o)}});return T(s,r)}export{P as createFeatureContributionBuilders,K as createFeatureContributionPublication,V as isFeatureContributionDeclaration,$ as snapshotFeatureContributions};
2
2
  //# sourceMappingURL=feature-contribution.js.map