@opetope/runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +345 -0
- package/README.ru.md +344 -0
- package/dist/application-compiler-edges.d.ts +3 -0
- package/dist/application-compiler-edges.js +2 -0
- package/dist/application-compiler-edges.js.map +1 -0
- package/dist/application-compiler-graph.d.ts +8 -0
- package/dist/application-compiler-graph.js +2 -0
- package/dist/application-compiler-graph.js.map +1 -0
- package/dist/application-compiler.d.ts +116 -0
- package/dist/application-compiler.js +2 -0
- package/dist/application-compiler.js.map +1 -0
- package/dist/application-conditions.d.ts +18 -0
- package/dist/application-conditions.js +2 -0
- package/dist/application-conditions.js.map +1 -0
- package/dist/application-definition.d.ts +30 -0
- package/dist/application-definition.js +2 -0
- package/dist/application-definition.js.map +1 -0
- package/dist/application-error.d.ts +10 -0
- package/dist/application-error.js +2 -0
- package/dist/application-error.js.map +1 -0
- package/dist/application-execution.d.ts +43 -0
- package/dist/application-execution.js +2 -0
- package/dist/application-execution.js.map +1 -0
- package/dist/application-feature-bindings.d.ts +11 -0
- package/dist/application-feature-bindings.js +2 -0
- package/dist/application-feature-bindings.js.map +1 -0
- package/dist/application-feature-instance.d.ts +5 -0
- package/dist/application-feature-instance.js +2 -0
- package/dist/application-feature-instance.js.map +1 -0
- package/dist/application-group-order.d.ts +29 -0
- package/dist/application-group-order.js +2 -0
- package/dist/application-group-order.js.map +1 -0
- package/dist/application-instance-retirement.d.ts +36 -0
- package/dist/application-instance-retirement.js +2 -0
- package/dist/application-instance-retirement.js.map +1 -0
- package/dist/application-open-options.d.ts +29 -0
- package/dist/application-open-options.js +2 -0
- package/dist/application-open-options.js.map +1 -0
- package/dist/application-port-compiler.d.ts +28 -0
- package/dist/application-port-compiler.js +2 -0
- package/dist/application-port-compiler.js.map +1 -0
- package/dist/attachment-call-declaration.d.ts +43 -0
- package/dist/attachment-call-declaration.js +2 -0
- package/dist/attachment-call-declaration.js.map +1 -0
- package/dist/attachment-declaration.d.ts +68 -0
- package/dist/attachment-declaration.js +2 -0
- package/dist/attachment-declaration.js.map +1 -0
- package/dist/attachment-execution.d.ts +9 -0
- package/dist/attachment-execution.js +2 -0
- package/dist/attachment-execution.js.map +1 -0
- package/dist/attachment-retirement-scheduler.d.ts +14 -0
- package/dist/attachment-retirement-scheduler.js +2 -0
- package/dist/attachment-retirement-scheduler.js.map +1 -0
- package/dist/call-option-snapshot.d.ts +20 -0
- package/dist/call-option-snapshot.js +2 -0
- package/dist/call-option-snapshot.js.map +1 -0
- package/dist/compile-call-target-bindings.d.ts +14 -0
- package/dist/compile-call-target-bindings.js +2 -0
- package/dist/compile-call-target-bindings.js.map +1 -0
- package/dist/compile-module-template.d.ts +38 -0
- package/dist/compile-module-template.js +2 -0
- package/dist/compile-module-template.js.map +1 -0
- package/dist/condition-group-execution.d.ts +44 -0
- package/dist/condition-group-execution.js +2 -0
- package/dist/condition-group-execution.js.map +1 -0
- package/dist/condition-override.d.ts +28 -0
- package/dist/condition-override.js +2 -0
- package/dist/condition-override.js.map +1 -0
- package/dist/condition-source.d.ts +10 -0
- package/dist/condition-source.js +2 -0
- package/dist/condition-source.js.map +1 -0
- package/dist/condition-types.d.ts +14 -0
- package/dist/condition.d.ts +28 -0
- package/dist/condition.js +2 -0
- package/dist/condition.js.map +1 -0
- package/dist/control-registry.d.ts +56 -0
- package/dist/control-registry.js +2 -0
- package/dist/control-registry.js.map +1 -0
- package/dist/dynamic-scope-child.d.ts +23 -0
- package/dist/dynamic-scope-child.js +2 -0
- package/dist/dynamic-scope-child.js.map +1 -0
- package/dist/dynamic-scope-controller.d.ts +16 -0
- package/dist/dynamic-scope-controller.js +2 -0
- package/dist/dynamic-scope-controller.js.map +1 -0
- package/dist/feature-attachment-authoring-types.d.ts +51 -0
- package/dist/feature-attachment-lowering.d.ts +18 -0
- package/dist/feature-attachment-lowering.js +2 -0
- package/dist/feature-attachment-lowering.js.map +1 -0
- package/dist/feature-attachment.d.ts +45 -0
- package/dist/feature-attachment.js +2 -0
- package/dist/feature-attachment.js.map +1 -0
- package/dist/feature-authoring-types.d.ts +196 -0
- package/dist/feature-authoring.d.ts +15 -0
- package/dist/feature-authoring.js +2 -0
- package/dist/feature-authoring.js.map +1 -0
- package/dist/feature-body.d.ts +53 -0
- package/dist/feature-body.js +2 -0
- package/dist/feature-body.js.map +1 -0
- package/dist/feature-call-authority.d.ts +8 -0
- package/dist/feature-call-authority.js +2 -0
- package/dist/feature-call-authority.js.map +1 -0
- package/dist/feature-call-types.d.ts +39 -0
- package/dist/feature-call.d.ts +18 -0
- package/dist/feature-call.js +2 -0
- package/dist/feature-call.js.map +1 -0
- package/dist/feature-calls.d.ts +6 -0
- package/dist/feature-calls.js +2 -0
- package/dist/feature-calls.js.map +1 -0
- package/dist/feature-contract.d.ts +70 -0
- package/dist/feature-contract.js +2 -0
- package/dist/feature-contract.js.map +1 -0
- package/dist/feature-contribution-model.d.ts +38 -0
- package/dist/feature-contribution-model.js +2 -0
- package/dist/feature-contribution-model.js.map +1 -0
- package/dist/feature-contribution.d.ts +126 -0
- package/dist/feature-contribution.js +2 -0
- package/dist/feature-contribution.js.map +1 -0
- package/dist/feature-definition-api.d.ts +53 -0
- package/dist/feature-definition-support.d.ts +21 -0
- package/dist/feature-definition-support.js +2 -0
- package/dist/feature-definition-support.js.map +1 -0
- package/dist/feature-effect.d.ts +26 -0
- package/dist/feature-effect.js +2 -0
- package/dist/feature-effect.js.map +1 -0
- package/dist/feature-event.d.ts +32 -0
- package/dist/feature-event.js +2 -0
- package/dist/feature-event.js.map +1 -0
- package/dist/feature-generation.d.ts +34 -0
- package/dist/feature-generation.js +2 -0
- package/dist/feature-generation.js.map +1 -0
- package/dist/feature-lazy-generation.d.ts +6 -0
- package/dist/feature-lazy-generation.js +2 -0
- package/dist/feature-lazy-generation.js.map +1 -0
- package/dist/feature-lazy.d.ts +44 -0
- package/dist/feature-lazy.js +2 -0
- package/dist/feature-lazy.js.map +1 -0
- package/dist/feature-materialization-binding.d.ts +31 -0
- package/dist/feature-materialization-binding.js +2 -0
- package/dist/feature-materialization-binding.js.map +1 -0
- package/dist/feature-materialization-types.d.ts +45 -0
- package/dist/feature-model-dependencies.d.ts +23 -0
- package/dist/feature-model-dependencies.js +2 -0
- package/dist/feature-model-dependencies.js.map +1 -0
- package/dist/feature-model.d.ts +60 -0
- package/dist/feature-model.js +2 -0
- package/dist/feature-model.js.map +1 -0
- package/dist/feature-optional.d.ts +15 -0
- package/dist/feature-optional.js +2 -0
- package/dist/feature-optional.js.map +1 -0
- package/dist/feature-own-lowering.d.ts +22 -0
- package/dist/feature-own-lowering.js +2 -0
- package/dist/feature-own-lowering.js.map +1 -0
- package/dist/feature-port-binding.d.ts +6 -0
- package/dist/feature-port-binding.js +2 -0
- package/dist/feature-port-binding.js.map +1 -0
- package/dist/feature-port.d.ts +66 -0
- package/dist/feature-port.js +2 -0
- package/dist/feature-port.js.map +1 -0
- package/dist/feature-record.d.ts +6 -0
- package/dist/feature-record.js +2 -0
- package/dist/feature-record.js.map +1 -0
- package/dist/feature-resource.d.ts +29 -0
- package/dist/feature-resource.js +2 -0
- package/dist/feature-resource.js.map +1 -0
- package/dist/feature-scope-types.d.ts +35 -0
- package/dist/feature-scope.d.ts +29 -0
- package/dist/feature-scope.js +2 -0
- package/dist/feature-scope.js.map +1 -0
- package/dist/feature-stream.d.ts +37 -0
- package/dist/feature-stream.js +2 -0
- package/dist/feature-stream.js.map +1 -0
- package/dist/feature-timers.d.ts +14 -0
- package/dist/feature-timers.js +2 -0
- package/dist/feature-timers.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/inspection-activity-protocol.d.ts +63 -0
- package/dist/inspection-activity.d.ts +54 -0
- package/dist/inspection-activity.js +2 -0
- package/dist/inspection-activity.js.map +1 -0
- package/dist/inspection-diff.d.ts +10 -0
- package/dist/inspection-diff.js +2 -0
- package/dist/inspection-diff.js.map +1 -0
- package/dist/inspection-module-activity.d.ts +4 -0
- package/dist/inspection-module-activity.js +2 -0
- package/dist/inspection-module-activity.js.map +1 -0
- package/dist/inspection-observer.d.ts +38 -0
- package/dist/inspection-observer.js +2 -0
- package/dist/inspection-observer.js.map +1 -0
- package/dist/inspection-plan.d.ts +33 -0
- package/dist/inspection-plan.js +2 -0
- package/dist/inspection-plan.js.map +1 -0
- package/dist/inspection-protocol.d.ts +333 -0
- package/dist/inspection-protocol.js +2 -0
- package/dist/inspection-protocol.js.map +1 -0
- package/dist/inspection-registry.d.ts +41 -0
- package/dist/inspection-registry.js +2 -0
- package/dist/inspection-registry.js.map +1 -0
- package/dist/inspection-session.d.ts +92 -0
- package/dist/inspection-session.js +2 -0
- package/dist/inspection-session.js.map +1 -0
- package/dist/inspection-snapshot.d.ts +4 -0
- package/dist/inspection-snapshot.js +2 -0
- package/dist/inspection-snapshot.js.map +1 -0
- package/dist/inspection-state.d.ts +91 -0
- package/dist/inspection-state.js +2 -0
- package/dist/inspection-state.js.map +1 -0
- package/dist/instance-demand.d.ts +31 -0
- package/dist/instance-demand.js +2 -0
- package/dist/instance-demand.js.map +1 -0
- package/dist/internal.d.ts +41 -0
- package/dist/internal.js +2 -0
- package/dist/internal.js.map +1 -0
- package/dist/keyed-scope-controller.d.ts +11 -0
- package/dist/keyed-scope-controller.js +2 -0
- package/dist/keyed-scope-controller.js.map +1 -0
- package/dist/model-kernel.d.ts +18 -0
- package/dist/model-kernel.js +2 -0
- package/dist/model-kernel.js.map +1 -0
- package/dist/module-call-context.d.ts +7 -0
- package/dist/module-call-context.js +2 -0
- package/dist/module-call-context.js.map +1 -0
- package/dist/module-call-runtime.d.ts +21 -0
- package/dist/module-call-runtime.js +2 -0
- package/dist/module-call-runtime.js.map +1 -0
- package/dist/module-generation.d.ts +57 -0
- package/dist/module-generation.js +2 -0
- package/dist/module-generation.js.map +1 -0
- package/dist/module-instance-types.d.ts +156 -0
- package/dist/module-instance.d.ts +19 -0
- package/dist/module-instance.js +2 -0
- package/dist/module-instance.js.map +1 -0
- package/dist/module-runtime-identity.d.ts +4 -0
- package/dist/module-runtime-identity.js +2 -0
- package/dist/module-runtime-identity.js.map +1 -0
- package/dist/module-scope-open.d.ts +4 -0
- package/dist/module-scope-open.js +2 -0
- package/dist/module-scope-open.js.map +1 -0
- package/dist/module-scope-retirement.d.ts +3 -0
- package/dist/module-scope-retirement.js +2 -0
- package/dist/module-scope-retirement.js.map +1 -0
- package/dist/module-template-ir.d.ts +59 -0
- package/dist/owner-generation-retirement.d.ts +6 -0
- package/dist/owner-generation-retirement.js +2 -0
- package/dist/owner-generation-retirement.js.map +1 -0
- package/dist/owner-generation-state.d.ts +123 -0
- package/dist/owner-generation-state.js +2 -0
- package/dist/owner-generation-state.js.map +1 -0
- package/dist/owner-generation.d.ts +16 -0
- package/dist/owner-generation.js +2 -0
- package/dist/owner-generation.js.map +1 -0
- package/dist/public-module-definition.d.ts +9 -0
- package/dist/public-module-definition.js +2 -0
- package/dist/public-module-definition.js.map +1 -0
- package/dist/public-module-instance.d.ts +6 -0
- package/dist/public-module-instance.js +2 -0
- package/dist/public-module-instance.js.map +1 -0
- package/dist/public-module-retirement-diagnostics.d.ts +5 -0
- package/dist/public-module-retirement-diagnostics.js +2 -0
- package/dist/public-module-retirement-diagnostics.js.map +1 -0
- package/dist/public-module-retirement.d.ts +3 -0
- package/dist/public-module-retirement.js +2 -0
- package/dist/public-module-retirement.js.map +1 -0
- package/dist/public-module-scope.d.ts +5 -0
- package/dist/public-module-scope.js +2 -0
- package/dist/public-module-scope.js.map +1 -0
- package/dist/public-module-state.d.ts +28 -0
- package/dist/public-module-state.js +2 -0
- package/dist/public-module-state.js.map +1 -0
- package/dist/public-module-types.d.ts +295 -0
- package/dist/public-module.d.ts +4 -0
- package/dist/resource-cache.d.ts +12 -0
- package/dist/resource-cache.js +2 -0
- package/dist/resource-cache.js.map +1 -0
- package/dist/resource-controller.d.ts +29 -0
- package/dist/resource-controller.js +2 -0
- package/dist/resource-controller.js.map +1 -0
- package/dist/resource-policy.d.ts +16 -0
- package/dist/resource-policy.js +2 -0
- package/dist/resource-policy.js.map +1 -0
- package/dist/resource-snapshot.d.ts +12 -0
- package/dist/resource-snapshot.js +2 -0
- package/dist/resource-snapshot.js.map +1 -0
- package/dist/resource-types.d.ts +3 -0
- package/dist/runtime-error-reporting.d.ts +4 -0
- package/dist/runtime-error-reporting.js +2 -0
- package/dist/runtime-error-reporting.js.map +1 -0
- package/dist/stream-backpressure.d.ts +18 -0
- package/dist/stream-backpressure.js +2 -0
- package/dist/stream-backpressure.js.map +1 -0
- package/dist/stream-cleanup.d.ts +17 -0
- package/dist/stream-cleanup.js +2 -0
- package/dist/stream-cleanup.js.map +1 -0
- package/dist/stream-controller-types.d.ts +51 -0
- package/dist/stream-controller.d.ts +5 -0
- package/dist/stream-controller.js +2 -0
- package/dist/stream-controller.js.map +1 -0
- package/docs/agent-guide.md +214 -0
- package/docs/agent-guide.ru.md +208 -0
- package/docs/cookbook.md +734 -0
- package/docs/cookbook.ru.md +729 -0
- package/docs/decisions.md +1437 -0
- package/docs/devtools.md +423 -0
- package/docs/devtools.ru.md +419 -0
- package/docs/how-it-works.md +521 -0
- package/docs/how-it-works.ru.md +495 -0
- package/docs/releases.md +78 -0
- package/docs/releases.ru.md +78 -0
- package/docs/spec.md +874 -0
- package/docs/spec.ru.md +884 -0
- package/package.json +72 -0
package/README.ru.md
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# `@opetope/runtime`
|
|
2
|
+
|
|
3
|
+
Рантайм без привязки к рендереру: объявление фичи, открытие её экземпляра, понижение вложений и заранее
|
|
4
|
+
проверенный граф времени жизни. Словарь пакета задаёт §3 [спецификации](./docs/spec.ru.md); всё, что здесь не названо, живёт на
|
|
5
|
+
`@opetope/runtime/internal`.
|
|
6
|
+
|
|
7
|
+
## Установка
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @opetope/core @opetope/runtime
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Используйте согласованные версии Opetope. Для release candidate добавьте `@next` каждому пакету `@opetope/*` в команде.
|
|
14
|
+
API поставляется только в ESM; требуется Node 20.19+. Команды разработки ниже относятся к contributor checkout.
|
|
15
|
+
|
|
16
|
+
Нормативные руководства EN/RU поставляются в `@opetope/runtime`: после его установки откройте
|
|
17
|
+
`node_modules/@opetope/runtime/docs/spec.md` или `spec.ru.md`; рецепты находятся в `cookbook.md` и `cookbook.ru.md`.
|
|
18
|
+
Для чтения установленных руководств доступ к GitHub не нужен.
|
|
19
|
+
|
|
20
|
+
## Hello module
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { defineFeature, defineHostContract, openFeature } from '@opetope/runtime';
|
|
24
|
+
|
|
25
|
+
type Clock = { tick(): PromiseLike<void> | void };
|
|
26
|
+
declare const clockService: Clock;
|
|
27
|
+
|
|
28
|
+
const clockContract = defineHostContract<Clock>('example.clock.service');
|
|
29
|
+
|
|
30
|
+
const clockFeature = defineFeature({
|
|
31
|
+
id: 'example.clock',
|
|
32
|
+
imports: { service: clockContract },
|
|
33
|
+
own: ({ attach, call, imports }) => {
|
|
34
|
+
const clock = attach(imports.service, {
|
|
35
|
+
open: ({ source, timers }) => timers.interval(1_000, () => source.tick()),
|
|
36
|
+
});
|
|
37
|
+
const tick = call({ within: clock, run: (_input: void, { source }) => source.tick() });
|
|
38
|
+
|
|
39
|
+
return { clock, tick };
|
|
40
|
+
},
|
|
41
|
+
exports: ({ own }) => ({ tick: own.tick }),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const instance = openFeature(clockFeature, {
|
|
45
|
+
imports: { service: clockService },
|
|
46
|
+
reporter: error => void error,
|
|
47
|
+
});
|
|
48
|
+
const ready = await instance.ready;
|
|
49
|
+
await instance.close();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`attach` выводит id из ключа `clock` и берёт значение из импорта, поэтому
|
|
53
|
+
lifecycle всегда привязан к тому, что дал хост или другая фича. Disposer от `timers.interval`
|
|
54
|
+
закрывается вместе с экземпляром.
|
|
55
|
+
`timers.delay`/`timers.interval` физически отменяют ещё не начавшийся callback и передают signal текущей
|
|
56
|
+
поколения вложения. Отмена уже начавшейся асинхронной работы кооперативна. Interval не ждёт Promise
|
|
57
|
+
предыдущего callback и поэтому допускает overlap; backpressure и execution policy принадлежат
|
|
58
|
+
`effect`/`CallLane`, а не таймеру.
|
|
59
|
+
Cleanup failure по умолчанию best-effort `report`; retryable `quarantine` включается явно только для
|
|
60
|
+
ресурсов, которым обязателен физический release.
|
|
61
|
+
|
|
62
|
+
Сахар данных живёт в той же секции `own` и понижается в обычные вложения:
|
|
63
|
+
|
|
64
|
+
- `effect({ from, when?, run, onDispose? })` принимает imported `Readable`, запускается для
|
|
65
|
+
начального snapshot и затем latest-wins; signal, per-run timers и возвращённый disposer принадлежат
|
|
66
|
+
exact запуску;
|
|
67
|
+
- `event(from, subscribe, { run })` даёт `subscribe`-контексту non-replayed `emit`, сериализует обработчик и
|
|
68
|
+
ограничивает очередь одним ожидающим payload; отказ и переполнение уходят reporter-у приложения.
|
|
69
|
+
Retirement abort-ит handler, удаляет ожидающий payload и drain-ит уже запущенную работу до release source;
|
|
70
|
+
- `resource(from, target, { key, load, retry?, retention? })` следит за
|
|
71
|
+
`Readable<T | null | undefined>`, возвращённым target-селектором:
|
|
72
|
+
`null` или `undefined` закрывает materialization, а смена `key(target)` fence-ит старый load и открывает новую
|
|
73
|
+
экземпляра. `refresh` сохраняет устаревшие данные только для того же ключа, `invalidate` очищает её, а
|
|
74
|
+
`retry` повторяет отказ, который можно повторить, в текущем экземпляре;
|
|
75
|
+
- `stream(from, target, { backpressure, connect, consume?, key, retention? })` применяет
|
|
76
|
+
backpressure до публикации и `consume`, фенсит emitter старого поколения и физически закрывает
|
|
77
|
+
source до release импортированного значения.
|
|
78
|
+
|
|
79
|
+
`model` даёт той же `own`-секции владеемые данные вместо ещё одного attachment:
|
|
80
|
+
|
|
81
|
+
- `model(Declaration, create)` не требует зависимостей. `model(Declaration, { platform: imports.platform,
|
|
82
|
+
lookup: requires.lookup }, create)` передаёт readonly-запись разрешённых зависимостей на открытии экземпляра.
|
|
83
|
+
Даже для одной зависимости используется запись. Фабрика объявляет свой интерфейс зависимостей и получает
|
|
84
|
+
готовые значения, а не контекст фичи или refs декларации. Принимаются подлинные импорты и call refs текущей
|
|
85
|
+
фичи; optional-импорты сохраняют проекцию `Readable<Lookup<…>>`. Сырые сервисы, декларации портов, чужие refs
|
|
86
|
+
и ref другой модели отвергаются (D169).
|
|
87
|
+
|
|
88
|
+
`ModelContext` не generic и даёт модели собственный kernel (D73): `state` создаёт `OwnedState`, `update(state, value)`
|
|
89
|
+
единственный писатель и принимает только его, `call`, `calls`, `lane`, `effect`, `event`, `resource`, `stream`,
|
|
90
|
+
`scope`, `timers` и `cleanup` открывают узлы модели на открытии экземпляра и закрывают их в его дренаже; `calls(deps, keys, { lane })`
|
|
91
|
+
это тот же выбор методов, что `own.calls`, только источник это готовые зависимости модели (D143); `from` у `resource`/`stream`/`event` это
|
|
92
|
+
готовое значение зависимости, а не import-ref,
|
|
93
|
+
`signal` абортится на retire, а запись после retire уходит reporter-у как
|
|
94
|
+
`FeatureError` с кодом `retired`, а не бросается вызывающему. Владеемое состояние живёт только в модели: у `own`
|
|
95
|
+
метода `state` нет, потому что писать в такое состояние было некому — `ctx.update` принимает лишь то, что создал
|
|
96
|
+
его собственный контекст (D139). Refs `model()` не участвуют в IR понижения: их нельзя экспортировать как call и
|
|
97
|
+
нельзя использовать в `within`. Компонент вклада читает модели своей фичи через `useModel` внутри монтирования
|
|
98
|
+
вклада, которое связывает их автоматически. Отдельное поле уходит наружу через
|
|
99
|
+
`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).
|
|
103
|
+
|
|
104
|
+
`target` — селектор `(source) => Readable<T | null | undefined>` и у фичи, и у модели.
|
|
105
|
+
TypeScript выводит payload возвращённого `Readable`, форма которого проверяется перед открытием;
|
|
106
|
+
скалярный snapshot или строка с именем поля не принимаются. `null` и `undefined` закрывают materialization.
|
|
107
|
+
`retention` необязателен в обоих API; `scoped({ capacity })` использует LRU без дополнительной опции.
|
|
108
|
+
|
|
109
|
+
Оба API публикуют один atomic snapshot `idle | opening | ready | refreshing | error` с
|
|
110
|
+
`key`, поколение, попытку, устаревшие данные и отказ. `subscribe` только наблюдает и не активирует
|
|
111
|
+
работу; спрос задаёт отдельный lease `retain()`. Удержание по умолчанию объединяет StrictMode
|
|
112
|
+
release/reacquire в одну микрозадачу и abort-ит незавершённую работу после последнего lease. Явный
|
|
113
|
+
`scoped({ capacity })` удерживает materialization до retirement и ограничивает sticky
|
|
114
|
+
кэша. UI фичи получает привязанный к экземпляру фасад через `bindFeatureResource(instance, own.x)`,
|
|
115
|
+
а React `useResource` держит один lease на смонтированного потребителя.
|
|
116
|
+
|
|
117
|
+
Политик ровно две, и обе с production-пилотом: `scoped({ capacity })` для удержания и `latest()` для
|
|
118
|
+
backpressure стрима. Прежние `drop`, `sample` и `queue` удалены вместе с их ветками: назвать их из авторского
|
|
119
|
+
словаря было нельзя, поэтому они измеряли только сами себя (D160).
|
|
120
|
+
|
|
121
|
+
## Calls and lanes
|
|
122
|
+
|
|
123
|
+
`call(options)` объявляет команду; `invoke(target, input)` исполняет её. В `run` фичи или модели используйте
|
|
124
|
+
`run: (input, { invoke }) => invoke(target, input)` либо `context.invoke(target, input)`. Доверенные шаги attachment
|
|
125
|
+
используют то же имя в своём контексте исполнения `context`. Алиаса исполнения `call` нет (D243).
|
|
126
|
+
|
|
127
|
+
`call({ within, run })` создаёт вызов экземпляра с приватной FIFO lane по умолчанию. Несколько
|
|
128
|
+
calls можно сериализовать одной `lane({ within })`; lane нельзя перенести на другую attachment.
|
|
129
|
+
Вложенный `context.invoke(localRef, input)` на той же lane выполняется без self-deadlock, а циклы через
|
|
130
|
+
несколько lanes и рекурсивный single-flight завершаются диагностической ошибкой.
|
|
131
|
+
|
|
132
|
+
`policy: 'parallel'` всегда явный. `singleFlight: fn` объединяет только одинаковые ключи:
|
|
133
|
+
отмена одного вызывающего не отменяет остальных, а уход последнего abort-ит физический прогон. Успешный
|
|
134
|
+
`once: true | fn` живёт ровно один экземпляр; отказ не кэшируется. `policy: 'latest'` заменяет вход, ещё ожидающий
|
|
135
|
+
в конце lane, сохраняя исполняющееся тело и порядок относительно других Calls (D185). Retirement сначала fence-ит
|
|
136
|
+
settlement и освобождает lane, затем
|
|
137
|
+
ждёт физический дренаж до уборки вложения.
|
|
138
|
+
|
|
139
|
+
`calls(imports.host, keys)` выбирает методы хоста; `calls(imports.feature, keys)` выбирает экспортированные
|
|
140
|
+
вызовы жёсткого импорта фичи с теми же выведенными входами и выходами. Для optional-импорта фичи он создаёт вызовы,
|
|
141
|
+
которые выбирают текущего провайдера при обращении и отвечают `unavailable`, пока его нет (D187). Сам импорт
|
|
142
|
+
остаётся lookup; `fromOptional` из Core выбирает его данные с явным значением отсутствия.
|
|
143
|
+
|
|
144
|
+
Методы хоста для `calls` могут не иметь параметров, принимать один input либо input и `AbortSignal`;
|
|
145
|
+
поддерживаются необязательные input/signal. Без параметров получается `Call<void, Awaited<Output>>`. Методы сохраняют receiver
|
|
146
|
+
и всегда получают `(input, signal)`; runtime не читает `Function.length` и не угадывает, где клиент ждёт options.
|
|
147
|
+
Для `client.load(input, { signal })` задайте обычный адаптер
|
|
148
|
+
`load: (input: Input, signal: AbortSignal) => client.load(input, { signal })`. Единственный параметр с именем `signal`
|
|
149
|
+
это input; передавайте отмену в signal-only метод явно внутри `call({ run })` (D244).
|
|
150
|
+
|
|
151
|
+
Тело команды получает сигнал своего исполнения, а вложенный `invoke` наследует отмену и стек lane.
|
|
152
|
+
`Resource.load` и `Stream.connect`/`consume` получают сигнал текущего исполнения target: смена target, refresh или
|
|
153
|
+
освобождение ресурса могут отменить работу, пока модель жива. `ModelContext.signal` принадлежит всей модели.
|
|
154
|
+
Логическая отмена не останавливает метод, игнорирующий сигнал, и не откатывает его записи; физический drain ждёт
|
|
155
|
+
допущенную работу. Простым setters и методам без I/O не нужен фиктивный `_signal` (D244).
|
|
156
|
+
|
|
157
|
+
`instance.close()` фенсит синхронно и возвращает один общий promise завершения. Неудачная уборка может дать
|
|
158
|
+
`FeatureError` с `code: 'quarantined'` и необязательной capability `retryCleanup`. Перед вызовом проверьте её
|
|
159
|
+
наличие; у обычных ошибок функции повтора нет. `Resource.retry()` и retry спроса фичи остаются отдельными
|
|
160
|
+
операциями. Внутреннее имя lifecycle и код отмены `retired` сохраняются (D168).
|
|
161
|
+
|
|
162
|
+
## Ленивые тела фич
|
|
163
|
+
|
|
164
|
+
Лёгкий заголовок объявляет topology через `id`, `when`, `imports`, `requires`, метаданные `provides` и
|
|
165
|
+
`body: () => import('./feature.body')`. Тело использует `defineFeature.body(header, { own, exports, provides })`;
|
|
166
|
+
`FeatureBody<Exports>` задаёт результат loader без циклической зависимости, а `FeatureBodyOf<Header>` типизирует
|
|
167
|
+
вход реализации body через imports, requirements и exports заголовка. `defineFeature.preload(header)` загружает код без открытия экземпляра (D186, D207, D208).
|
|
168
|
+
|
|
169
|
+
Eager и lazy `openFeature` сразу проверяют и копируют options, imports и requirements. Throw loader отклоняет
|
|
170
|
+
`ready` или `preload`. Определение делит загрузку кода, но у каждого экземпляра своё время жизни: закрытие до
|
|
171
|
+
прихода кода отклоняет его готовность как `retired` и завершается без ожидания общей загрузки. Другие экземпляры
|
|
172
|
+
и preload продолжают её использовать; поздний успех не открывает закрытый экземпляр, а отказ допускает повтор.
|
|
173
|
+
Когда материализация началась, close дожидается уже владеемой работы, включая реентерабельное закрытие из фабрики
|
|
174
|
+
модели; синхронный фенс, общий Promise закрытия и восстановление quarantine совпадают с eager-экземпляром (D211).
|
|
175
|
+
|
|
176
|
+
## Dynamic scopes
|
|
177
|
+
|
|
178
|
+
`scope.while`, `scope.switch` и `scope.keyed` управляют дочерним поколением от импортированного
|
|
179
|
+
`Readable`; callback `open(value, { signal, timers })` возвращает disposer или `void`. Они компилируют
|
|
180
|
+
ребёнка в то же IR времени жизни, что обычный `attach`, а контроллер только переводит изменения
|
|
181
|
+
источника в открытие и retire уже существующего scope-а.
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
own: ({ imports, scope }) => ({
|
|
185
|
+
authorized: scope.while({
|
|
186
|
+
from: imports.authorized,
|
|
187
|
+
open: (_authorized, { signal }) => connection.open(signal),
|
|
188
|
+
}),
|
|
189
|
+
selected: scope.switch({
|
|
190
|
+
from: imports.selected,
|
|
191
|
+
key: item => item.id,
|
|
192
|
+
open: (item, { signal }) => repository.open(item.id, signal),
|
|
193
|
+
}),
|
|
194
|
+
rows: scope.keyed({
|
|
195
|
+
from: imports.rows,
|
|
196
|
+
key: row => row.id,
|
|
197
|
+
open: (row, { signal }) => cache.retain(row.id, signal),
|
|
198
|
+
}),
|
|
199
|
+
}),
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`while` без `when` использует truthiness. `switch` считает `null`/`undefined` закрытым состоянием,
|
|
203
|
+
закрывает старое поколение до нового и при `A → B → C` открывает только актуальное `C`; равный
|
|
204
|
+
явный `key` сохраняет текущее поколение. `keyed` держит независимое дочернее поколение на каждый
|
|
205
|
+
уникальный `string | number` key. Повтор ключа отклоняет весь новый snapshot до любых изменений уже
|
|
206
|
+
активных детей. Retire синхронно фенсит детей и ждёт их физический дренаж.
|
|
207
|
+
|
|
208
|
+
Секция `provides` объявляет и порты, и вклады: `port` связывает call-ref с `Port`, `slot`, `pipe` и
|
|
209
|
+
`register` объявляют вклад по виду цели:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
import { definePipe } from '@opetope/core';
|
|
213
|
+
import { defineFeature } from '@opetope/runtime';
|
|
214
|
+
|
|
215
|
+
const titlePipe = definePipe<string>({ id: 'example.title' });
|
|
216
|
+
const titleFeature = defineFeature({
|
|
217
|
+
id: 'example.title-contributor',
|
|
218
|
+
provides: ({ pipe }) => ({
|
|
219
|
+
suffix: pipe(titlePipe, { fold: title => `${title} · ready` }, { priority: 20 }),
|
|
220
|
+
}),
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Внешняя factory синхронна и выполняется при `defineFeature`, поэтому target/id/priority доступны
|
|
225
|
+
компилятор приложения до открытия экземпляра. Второй аргумент `pipe(...)` — дескриптор `{ fold }`: рантайм
|
|
226
|
+
связывает его с конкретным экземпляром после critical barrier и вызывает обработчик только на свёртке (D223).
|
|
227
|
+
`slot` и `register` принимают либо value factory с concrete `FeatureOpenContext`, которая тоже выполняется только
|
|
228
|
+
после этого барьера, либо готовое значение вклада. Exact plain record обязан
|
|
229
|
+
вернуть каждый receipt ровно один раз; id записи выводится как `featureId.key`. Весь batch проходит
|
|
230
|
+
preflight и публикуется атомарно. Начало retirement синхронно удаляет записи до async cleanup, включая
|
|
231
|
+
reentrant retirement из target listener; failed opening или ошибка value factory не оставляют частично
|
|
232
|
+
опубликованных вкладов.
|
|
233
|
+
|
|
234
|
+
Feature API объявляется секцией `exports: ({ own }) => ({ … })`: contract создаётся из определения
|
|
235
|
+
фичи под id `<feature>.exports`, а consumer импортирует само определение — `imports: { provider: providerFeature }`.
|
|
236
|
+
Assigned missing/extra keys и несовместимые `Call` signatures отклоняются compile-time. Host-only значения
|
|
237
|
+
используют `defineHostContract<T>()`. `context.invoke`
|
|
238
|
+
принимает локальный вызов той же фичи или target из импортов текущего экземпляра; чужой локальный ref и
|
|
239
|
+
захваченный вне объявленных imports target отклоняются runtime fence-ом.
|
|
240
|
+
|
|
241
|
+
`onDemand(hostContract)` сохраняет в декларации импорта отложенное подключение для скомпилированной топологии. Значение host
|
|
242
|
+
остаётся того же contract-derived типа, а `attach(imports.x)` lower-ится в тот же lifecycle IR, что
|
|
243
|
+
`attach(imports.x, { open: () => undefined })`; wrapper принимает только один direct authentic host contract, не
|
|
244
|
+
допускает повторного оборачивания и не оборачивает ребро фичи.
|
|
245
|
+
|
|
246
|
+
`optional(x)` в `imports` и `optional(port)` в `requires` это слабое ребро: потребитель может жить дольше
|
|
247
|
+
провайдера, поэтому значение приходит проекцией `Readable<Lookup<…>>`, а вызов слабого порта без живого провайдера
|
|
248
|
+
оседает `CallError` с кодом `unavailable`. Это ошибка, а не отмена: `isCancellation` для него ложно, а
|
|
249
|
+
`useCommand` отдаёт исход `failed` с этой ошибкой в `lastError` (D138).
|
|
250
|
+
|
|
251
|
+
## Карта слов
|
|
252
|
+
|
|
253
|
+
Одна страница словаря пакета: RFC §3 задаёт имена, README объясняет, зачем каждое.
|
|
254
|
+
|
|
255
|
+
| Слово | Зачем |
|
|
256
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
257
|
+
| `defineFeature`, `Feature`, `FeatureBody`, `FeatureBodyOf` | объявление фичи; обязателен только `id` |
|
|
258
|
+
| секции `imports`, `requires`, `own`, `exports`, `provides`, `when` | что мне дадут, что мне нужно, чем я владею и когда живу |
|
|
259
|
+
| `defineHostContract`, `HostContract`, `onDemand` | граница с хостом и отложенное подключение |
|
|
260
|
+
| `optional` (проекция типа `Lookup` из `@opetope/core`) | слабое ребро: проекция вместо значения |
|
|
261
|
+
| `attach`, `calls`, `call`, `lane`, `effect`, `event`, `model`, `resource`, `stream`, `scope.while`, `scope.switch`, `scope.keyed` | методы `own`: чем владеет фича |
|
|
262
|
+
| `port`, `slot`, `pipe`, `register` | методы `provides`: что фича предлагает точкам расширения |
|
|
263
|
+
| `openFeature`, `FeatureInstance` | одно открытие фичи: `ready`, `close` |
|
|
264
|
+
| `scoped`, `latest`, `ScopedRetention`, `LatestBackpressure`, `Resource`, `ResourceSnapshot` | политики удержания и снимок ресурса |
|
|
265
|
+
| `defineApplication`, `Application`, `defineCondition`, `Condition`, `bind`, `openApplication`, `ApplicationExecution`, `ApplicationImportBinding` | статическая топология, условия и общее время жизни |
|
|
266
|
+
| `ErrorReporter`, `CleanupFailurePolicy` | куда уходит отказ и что делать с неудачным cleanup |
|
|
267
|
+
| `FeatureError` с кодами `not-ready`, `quarantined`, `cleanup-failed`, `retired`; `ApplicationError` с кодом `closed` | один класс на субъект, состояние в поле `code` |
|
|
268
|
+
|
|
269
|
+
`defineModule`, `instantiateModule` и всё семейство `Module*`, `Call*`, `Attachment*` живут только в
|
|
270
|
+
`@opetope/runtime/internal`: это ABI реализации фреймворка и интеграции с хостом, а не словарь автора фичи.
|
|
271
|
+
|
|
272
|
+
`Model` — типизированный ключ записи из `Readable` и `Call`. Он объявляет доступные потребителю данные и команды;
|
|
273
|
+
фабрика модели создаёт реализацию в пределах принадлежащего ей времени жизни. UI-компоненты используют этот контракт.
|
|
274
|
+
|
|
275
|
+
Eager `defineFeature` lower-ится в immutable reusable `defineModule`; lazy-заголовок откладывает lowering реализации
|
|
276
|
+
до `defineFeature.body`. `openFeature` создаёт
|
|
277
|
+
изолированное поколение с точной картой scope-ов, синхронной подготовкой UI, откатом, single-flight
|
|
278
|
+
retirement и quarantine retry. Advanced `defineModule`/`instantiateModule` остаются доступны для
|
|
279
|
+
framework lowering, но не являются типичным feature-authoring путём.
|
|
280
|
+
`instance.ready` и `close()` — throwing convenience facade с типизированными ошибками.
|
|
281
|
+
Detailed outcome-unions не оборачиваются в `.orThrow()`: они остаются на advanced-пути
|
|
282
|
+
`instantiateModule` → `scope.open`/`retirement.drain`, чтобы не создавать второй lifecycle API.
|
|
283
|
+
|
|
284
|
+
Сценарии consumer size собираются из `size/**`, а пределы в `package.json` служат coarse regression guards с
|
|
285
|
+
запасом, а не ratchet-ами к текущему байту. Preserve-modules inventory намеренно не суммируется по файлам: такой
|
|
286
|
+
счёт растёт от архитектурного split без роста consumer bundle. Проверяйте initial и async-чанки хоста по его
|
|
287
|
+
собственным бюджетам, а startup и retained heap измеряйте на нагрузке приложения. Одни проверки размера пакета
|
|
288
|
+
не подтверждают производительность приложения.
|
|
289
|
+
|
|
290
|
+
Статические scope/attachment/call refs доступны через `definition.own`; raw target, compiler IR
|
|
291
|
+
и контроллер времени жизни не публикуются. Точное значение поколения scope-а передаётся в `open`, `close` и
|
|
292
|
+
attachment-bound `call`, поэтому одна definition безопасно создает несколько instances без shared
|
|
293
|
+
closure state.
|
|
294
|
+
|
|
295
|
+
Первый `scope.open` запечатывает assembly. Module retirement синхронно fence-ит зарегистрированные
|
|
296
|
+
renderer participants и все активные scopes, после чего `drain` возвращает единый typed outcome.
|
|
297
|
+
`retry` проходит только quarantined frontier; workspace retirement handles остаются идемпотентными.
|
|
298
|
+
Штатная отмена поколения представлена `FeatureError` с кодом `retired`; framework adapters используют
|
|
299
|
+
`isCancellation`, а обычный UI получает `CommandOutcome` из `useCommand`. Отмена вызова это коды
|
|
300
|
+
`cancelled` и `closed`; `unavailable` и `publication-rejected` доходят до продукта ошибкой.
|
|
301
|
+
Колбэк вложения не должен вызывать `drain`/`retry` собственного поколения scope-а:
|
|
302
|
+
синхронный self-wait отклоняется до запуска cleanup. Self-wait после собственного `await` остается
|
|
303
|
+
нарушением контракта колбэка — без токена асинхронного контекста его не отличить от законного внешнего
|
|
304
|
+
присоединения, поэтому рантайм сохраняет single-flight для внешних вызывающих.
|
|
305
|
+
`call` выводит вход, выход и значение scope-а из колбэка и `within`; при необходимости автор аннотирует параметр
|
|
306
|
+
`input`. Частичная запись `call<Input, Output>(...)` намеренно не является API: после явного префикса TypeScript
|
|
307
|
+
не выведет скрытое значение владельца без `any`.
|
|
308
|
+
|
|
309
|
+
Контроллер поколения владельца, низкоуровневый компилятор, привязка времени жизни, разрешение target вызова и
|
|
310
|
+
расписки retire остаются на `@opetope/runtime/internal`, а не в словаре автора. Направление зависимости
|
|
311
|
+
одностороннее: рантайм пользуется `@opetope/core/internal`, ядро рантайм не импортирует никогда.
|
|
312
|
+
|
|
313
|
+
Слой понижения даёт неизменяемые шаблоны и независимые экземпляры: он вычисляет критический барьер, запускает
|
|
314
|
+
отложенные вложения только после коммита владельца, сохраняет наблюдаемость причинных отказов и возвращает
|
|
315
|
+
стабильные идентичности вложений в исходах провалившегося открытия и дренажа.
|
|
316
|
+
|
|
317
|
+
Вложения одного scope-а образуют одну волну, а не граф: порядка между ними нет ни на открытии, ни на
|
|
318
|
+
закрытии (D163). Критические вложения стартуют до коммита владельца, отложенные — после `ready`. Retire
|
|
319
|
+
сначала фенсит весь живой набор, затем дренирует его целиком; отказавшее вложение попадает в карантинный
|
|
320
|
+
фронтир в каноническом порядке слотов, остальные закрываются, а `retry` повторяет ровно отказавшие.
|
|
321
|
+
|
|
322
|
+
Откат карантинного открытия восстанавливается только через приватный `retry` владельца. Успешное
|
|
323
|
+
восстановление удаляет мёртвого ребёнка из живого набора; сырой `retry` ядра не входит ни в понижение, ни в
|
|
324
|
+
авторский словарь. Замещающий ребёнок считается доказательством закрытия старого только после осевшего `ready`
|
|
325
|
+
от ядра.
|
|
326
|
+
|
|
327
|
+
Пока экземпляр открывается, его поколение ещё не стало текущим авторитетом вызовов: понижение коммитит его
|
|
328
|
+
только после успешного критического барьера, а retire инвалидирует поколение раньше уведомления родителя и детей.
|
|
329
|
+
Сигнал родителя и сигнал вложения образуют единый фенс исполнения для шагов и вызовов, а закрытие и уборка держат
|
|
330
|
+
отдельный авторитет освобождения.
|
|
331
|
+
|
|
332
|
+
Непрозрачные рецепты вызовов возвращаются под каноническими ключами рантайма и компилируются в замороженные
|
|
333
|
+
привязки без поведения. Вызов можно связать только с критическим вложением; физический target становится текущим
|
|
334
|
+
лишь после коммита всего критического барьера владельца и фенсится retire раньше уборки детей. Рантайм чеканит
|
|
335
|
+
один прямой target на привязку и экземпляр, а публикацию вкладов в React владеет одна транзакция.
|
|
336
|
+
|
|
337
|
+
Временного авторского входа у рантайма нет: разрешение target вызова, регистрация участников рендерера и
|
|
338
|
+
контроллеры владельца остаются внутренним ABI на `@opetope/runtime/internal`.
|
|
339
|
+
|
|
340
|
+
## Compatibility contract
|
|
341
|
+
|
|
342
|
+
Рантайм публикует ESM для Chrome 82+, Firefox 110+, Safari/iOS 15+, Android 82+ и Node 20.19+.
|
|
343
|
+
Обязательные globals и реализации совместимости принадлежат `@opetope/core`; рантайм полифилов не ставит.
|
|
344
|
+
Сырой dist проходит тот же гейт синтаксической цели и тот же smoke импорта по HTTP, что ядро.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ApplicationPortProviderIR, ApplicationTopologyEdgeIR, CompiledApplicationContributionIR, CompiledApplicationFeatureIR, CompiledApplicationImportIR, CompiledApplicationRequirementIR, CompiledConditionGroupIR } from './application-compiler.js';
|
|
2
|
+
declare function compileApplicationEdges(features: readonly CompiledApplicationFeatureIR[], imports: readonly CompiledApplicationImportIR[], providers: readonly ApplicationPortProviderIR[], requirements: readonly CompiledApplicationRequirementIR[], contributions: readonly CompiledApplicationContributionIR[], groups: readonly CompiledConditionGroupIR[]): readonly ApplicationTopologyEdgeIR[];
|
|
3
|
+
export { compileApplicationEdges };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function f(r,e){return r<e?-1:r>e?1:0}function s(r){return r.kind==="feature"?r.featureId:r.contractId}function I(r){return r.kind==="group"?r.groupId:""}function a(r){return r.kind==="provides-port"?`provides-port:${r.featureId}:${r.portId}:${r.providerKey}`:r.kind==="absent-port"?`absent-port:${r.consumerFeatureId}:${r.portId}:${r.requirementKey}`:`requires-port:${r.providerFeatureId}:${r.consumerFeatureId}:${r.portId}:${r.requirementKey}`}function m(r){return r.kind==="absent-port"||r.kind==="provides-port"||r.kind==="requires-port"}function i(r){return r.kind==="condition-control"?`condition-control:${r.conditionId}:${r.groupId}`:r.kind==="contributes-to"?`contributes-to:${r.contributorFeatureId}:${r.targetId}:${r.contributionId}`:m(r)?a(r):r.kind==="owns"?`owns:${r.owner.kind}:${I(r.owner)}:${r.featureId}`:`${r.kind}:${r.provider.kind}:${s(r.provider)}:${r.consumerFeatureId}:${r.importKey}`}function $(r){return r.provider.kind==="absent"?"absent-import":r.optional?"optional-import":r.demand==="instance"?"hard-import":"on-demand-import"}function k(r){return Object.freeze({consumerFeatureId:r.consumerFeatureId,contractId:r.contractId,importKey:r.importKey,kind:$(r),provider:r.provider})}function b(r){return r.provider===void 0?Object.freeze({consumerFeatureId:r.consumerFeatureId,kind:"absent-port",portId:r.portId,requirementKey:r.requirementKey}):Object.freeze({consumerFeatureId:r.consumerFeatureId,kind:"requires-port",portId:r.portId,providerFeatureId:r.provider.featureId,requirementKey:r.requirementKey})}function y(r){return r.flatMap(e=>e.conditions.map(n=>Object.freeze({condition:n,conditionId:n.id,groupId:e.groupId,kind:"condition-control"})))}function K(r,e,n,u,d,p){const o=[];for(const t of r)o.push(Object.freeze({featureId:t.featureId,kind:"owns",owner:t.owner}));for(const t of e)o.push(k(t));for(const t of n)o.push(Object.freeze({featureId:t.featureId,kind:"provides-port",portId:t.portId,providerKey:t.providerKey}));for(const t of u)o.push(b(t));for(const t of d)o.push(Object.freeze({contributionId:t.contributionId,contributorFeatureId:t.contributorFeatureId,kind:"contributes-to",targetId:t.targetId}));return o.push(...y(p)),o.sort((t,c)=>f(i(t),i(c))),Object.freeze(o)}export{K as compileApplicationEdges};
|
|
2
|
+
//# sourceMappingURL=application-compiler-edges.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-compiler-edges.js","sources":["../src/application-compiler-edges.ts"],"sourcesContent":["import type {\n ApplicationImportProviderIR,\n ApplicationLifetimeOwnerIR,\n ApplicationPortProviderIR,\n ApplicationTopologyEdgeIR,\n CompiledApplicationContributionIR,\n CompiledApplicationFeatureIR,\n CompiledApplicationImportIR,\n CompiledApplicationRequirementIR,\n CompiledConditionGroupIR,\n} from './application-compiler';\n\nfunction compareIds(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction providerEdgeKey(provider: ApplicationImportProviderIR): string {\n return provider.kind === 'feature' ? provider.featureId : provider.contractId;\n}\n\nfunction ownerEdgeKey(owner: ApplicationLifetimeOwnerIR): string {\n return owner.kind === 'group' ? owner.groupId : '';\n}\n\nfunction portEdgeKey(\n edge: Extract<ApplicationTopologyEdgeIR, { kind: 'absent-port' | 'provides-port' | 'requires-port' }>,\n): string {\n if (edge.kind === 'provides-port') return `provides-port:${edge.featureId}:${edge.portId}:${edge.providerKey}`;\n\n if (edge.kind === 'absent-port') {\n return `absent-port:${edge.consumerFeatureId}:${edge.portId}:${edge.requirementKey}`;\n }\n\n return `requires-port:${edge.providerFeatureId}:${edge.consumerFeatureId}:${edge.portId}:${edge.requirementKey}`;\n}\n\nfunction isPortEdge(\n edge: ApplicationTopologyEdgeIR,\n): edge is Extract<ApplicationTopologyEdgeIR, { kind: 'absent-port' | 'provides-port' | 'requires-port' }> {\n return edge.kind === 'absent-port' || edge.kind === 'provides-port' || edge.kind === 'requires-port';\n}\n\nfunction topologyEdgeKey(edge: ApplicationTopologyEdgeIR): string {\n if (edge.kind === 'condition-control') return `condition-control:${edge.conditionId}:${edge.groupId}`;\n\n if (edge.kind === 'contributes-to') {\n return `contributes-to:${edge.contributorFeatureId}:${edge.targetId}:${edge.contributionId}`;\n }\n\n if (isPortEdge(edge)) return portEdgeKey(edge);\n\n if (edge.kind === 'owns') return `owns:${edge.owner.kind}:${ownerEdgeKey(edge.owner)}:${edge.featureId}`;\n\n return `${edge.kind}:${edge.provider.kind}:${providerEdgeKey(edge.provider)}:${edge.consumerFeatureId}:${edge.importKey}`;\n}\n\n/** A weak edge is its own plan node, and a weak edge without a provider is the absent one (D91, D105). */\nfunction importEdgeKind(\n binding: CompiledApplicationImportIR,\n): 'absent-import' | 'hard-import' | 'on-demand-import' | 'optional-import' {\n if (binding.provider.kind === 'absent') return 'absent-import';\n\n if (binding.optional) return 'optional-import';\n\n return binding.demand === 'instance' ? 'hard-import' : 'on-demand-import';\n}\n\nfunction compileImportEdge(binding: CompiledApplicationImportIR): ApplicationTopologyEdgeIR {\n return Object.freeze({\n consumerFeatureId: binding.consumerFeatureId,\n contractId: binding.contractId,\n importKey: binding.importKey,\n kind: importEdgeKind(binding),\n provider: binding.provider,\n });\n}\n\nfunction compileRequirementEdge(requirement: CompiledApplicationRequirementIR): ApplicationTopologyEdgeIR {\n return requirement.provider === undefined\n ? Object.freeze({\n consumerFeatureId: requirement.consumerFeatureId,\n kind: 'absent-port' as const,\n portId: requirement.portId,\n requirementKey: requirement.requirementKey,\n })\n : Object.freeze({\n consumerFeatureId: requirement.consumerFeatureId,\n kind: 'requires-port' as const,\n portId: requirement.portId,\n providerFeatureId: requirement.provider.featureId,\n requirementKey: requirement.requirementKey,\n });\n}\n\n/** Every condition of every group is one control edge, so the devtools graph shows what gates each group (D93). */\nfunction compileConditionEdges(groups: readonly CompiledConditionGroupIR[]): readonly ApplicationTopologyEdgeIR[] {\n return groups.flatMap(group =>\n group.conditions.map(condition =>\n Object.freeze({\n condition,\n conditionId: condition.id,\n groupId: group.groupId,\n kind: 'condition-control' as const,\n }),\n ),\n );\n}\n\nfunction compileApplicationEdges(\n features: readonly CompiledApplicationFeatureIR[],\n imports: readonly CompiledApplicationImportIR[],\n providers: readonly ApplicationPortProviderIR[],\n requirements: readonly CompiledApplicationRequirementIR[],\n contributions: readonly CompiledApplicationContributionIR[],\n groups: readonly CompiledConditionGroupIR[],\n): readonly ApplicationTopologyEdgeIR[] {\n const edges: ApplicationTopologyEdgeIR[] = [];\n\n for (const feature of features) {\n edges.push(Object.freeze({ featureId: feature.featureId, kind: 'owns', owner: feature.owner }));\n }\n\n for (const binding of imports) {\n edges.push(compileImportEdge(binding));\n }\n\n for (const provider of providers) {\n edges.push(\n Object.freeze({\n featureId: provider.featureId,\n kind: 'provides-port',\n portId: provider.portId,\n providerKey: provider.providerKey,\n }),\n );\n }\n\n for (const requirement of requirements) {\n edges.push(compileRequirementEdge(requirement));\n }\n\n for (const contribution of contributions) {\n edges.push(\n Object.freeze({\n contributionId: contribution.contributionId,\n contributorFeatureId: contribution.contributorFeatureId,\n kind: 'contributes-to',\n targetId: contribution.targetId,\n }),\n );\n }\n\n edges.push(...compileConditionEdges(groups));\n\n edges.sort((left, right) => compareIds(topologyEdgeKey(left), topologyEdgeKey(right)));\n\n return Object.freeze(edges);\n}\n\nexport { compileApplicationEdges };\n"],"names":["compareIds","left","right","providerEdgeKey","provider","ownerEdgeKey","owner","portEdgeKey","edge","isPortEdge","topologyEdgeKey","importEdgeKind","binding","compileImportEdge","compileRequirementEdge","requirement","compileConditionEdges","groups","group","condition","compileApplicationEdges","features","imports","providers","requirements","contributions","edges","feature","contribution"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DeclarationId } from '@opetope/core';
|
|
2
|
+
interface ApplicationHardImportIR {
|
|
3
|
+
readonly consumerFeatureIndex: number;
|
|
4
|
+
readonly providerFeatureIndex: number;
|
|
5
|
+
}
|
|
6
|
+
declare function compileCanonicalOrder(featureIds: readonly DeclarationId[], edges: readonly ApplicationHardImportIR[]): readonly number[];
|
|
7
|
+
export { compileCanonicalOrder };
|
|
8
|
+
export type { ApplicationHardImportIR };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function c(s,t){return s<t?-1:s>t?1:0}function h(s,t){const n=new Map(Array.from({length:s},(e,o)=>[o,new Set])),i=Array.from({length:s},()=>0);for(const e of t){const o=n.get(e.providerFeatureIndex);(o==null?void 0:o.has(e.consumerFeatureIndex))!==!0&&(o==null||o.add(e.consumerFeatureIndex),i[e.consumerFeatureIndex]=(i[e.consumerFeatureIndex]??0)+1)}return{incoming:i,outgoing:n}}class a{featureIds;indexes=[];constructor(t,n){this.featureIds=t;for(const[i,e]of n.entries())e===0&&this.push(i)}push(t){let n=this.indexes.length;for(this.indexes.push(t);n>0;){const i=n-1>>>1,e=this.indexes[i];if(this.compare(e,t)<=0)break;this.indexes[n]=e,n=i}this.indexes[n]=t}take(){const t=this.indexes[0],n=this.indexes.pop();if(n===void 0||this.indexes.length===0)return t;let i=0;for(;i*2+1<this.indexes.length;){let e=i*2+1;const o=e+1;o<this.indexes.length&&this.compare(this.indexes[o],this.indexes[e])<0&&(e=o);const r=this.indexes[e];if(this.compare(n,r)<=0)break;this.indexes[i]=r,i=e}return this.indexes[i]=n,t}compare(t,n){return c(this.featureIds[t]??"",this.featureIds[n]??"")}}function d(s,t){const n=new a(t,s.incoming),i=[];for(;;){const e=n.take();if(e===void 0)break;i.push(e);for(const o of s.outgoing.get(e)??[])s.incoming[o]=(s.incoming[o]??0)-1,s.incoming[o]===0&&n.push(o)}return i}function u(s,t){const n=h(s.length,t),i=d(n,s);if(i.length!==s.length){const e=s.filter((o,r)=>n.incoming[r]!==0);throw new TypeError(`Application has an unconditional import cycle: ${e.join(", ")}.`)}return Object.freeze(i)}export{u as compileCanonicalOrder};
|
|
2
|
+
//# sourceMappingURL=application-compiler-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-compiler-graph.js","sources":["../src/application-compiler-graph.ts"],"sourcesContent":["import type { DeclarationId } from '@opetope/core';\n\ninterface ApplicationHardImportIR {\n readonly consumerFeatureIndex: number;\n readonly providerFeatureIndex: number;\n}\n\ntype HardImportGraph = {\n readonly incoming: number[];\n readonly outgoing: ReadonlyMap<number, ReadonlySet<number>>;\n};\n\nfunction compareIds(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction createHardImportGraph(featureCount: number, edges: readonly ApplicationHardImportIR[]): HardImportGraph {\n const outgoing = new Map<number, Set<number>>(\n Array.from({ length: featureCount }, (_value, index) => [index, new Set<number>()]),\n );\n const incoming = Array.from({ length: featureCount }, () => 0);\n\n for (const edge of edges) {\n const dependents = outgoing.get(edge.providerFeatureIndex);\n\n if (dependents?.has(edge.consumerFeatureIndex) === true) continue;\n\n dependents?.add(edge.consumerFeatureIndex);\n incoming[edge.consumerFeatureIndex] = (incoming[edge.consumerFeatureIndex] ?? 0) + 1;\n }\n\n return { incoming, outgoing };\n}\n\n/** D213: the smallest available id wins, including a newly unlocked provider; a heap keeps wide graphs O(V log V). */\nclass CanonicalFeatureQueue {\n private readonly indexes: number[] = [];\n\n constructor(\n private readonly featureIds: readonly DeclarationId[],\n incoming: readonly number[],\n ) {\n for (const [index, count] of incoming.entries()) {\n if (count === 0) this.push(index);\n }\n }\n\n push(featureIndex: number): void {\n let position = this.indexes.length;\n this.indexes.push(featureIndex);\n\n while (position > 0) {\n const parent = (position - 1) >>> 1;\n const parentIndex = this.indexes[parent]!;\n\n if (this.compare(parentIndex, featureIndex) <= 0) break;\n\n this.indexes[position] = parentIndex;\n position = parent;\n }\n\n this.indexes[position] = featureIndex;\n }\n\n take(): number | undefined {\n const first = this.indexes[0];\n const last = this.indexes.pop();\n\n if (last === undefined || this.indexes.length === 0) return first;\n\n let position = 0;\n\n while (position * 2 + 1 < this.indexes.length) {\n let child = position * 2 + 1;\n const right = child + 1;\n\n if (right < this.indexes.length && this.compare(this.indexes[right]!, this.indexes[child]!) < 0) child = right;\n\n const childIndex = this.indexes[child]!;\n\n if (this.compare(last, childIndex) <= 0) break;\n\n this.indexes[position] = childIndex;\n position = child;\n }\n\n this.indexes[position] = last;\n\n return first;\n }\n\n private compare(left: number, right: number): number {\n return compareIds(this.featureIds[left] ?? '', this.featureIds[right] ?? '');\n }\n}\n\nfunction drainTopologicalOrder(graph: HardImportGraph, featureIds: readonly DeclarationId[]): readonly number[] {\n const available = new CanonicalFeatureQueue(featureIds, graph.incoming);\n const order: number[] = [];\n\n for (;;) {\n const provider = available.take();\n\n if (provider === undefined) break;\n\n order.push(provider);\n\n for (const consumer of graph.outgoing.get(provider) ?? []) {\n graph.incoming[consumer] = (graph.incoming[consumer] ?? 0) - 1;\n\n if (graph.incoming[consumer] === 0) available.push(consumer);\n }\n }\n\n return order;\n}\n\nfunction compileCanonicalOrder(\n featureIds: readonly DeclarationId[],\n edges: readonly ApplicationHardImportIR[],\n): readonly number[] {\n const graph = createHardImportGraph(featureIds.length, edges);\n const order = drainTopologicalOrder(graph, featureIds);\n\n if (order.length !== featureIds.length) {\n const cyclic = featureIds.filter((_featureId, index) => graph.incoming[index] !== 0);\n\n throw new TypeError(`Application has an unconditional import cycle: ${cyclic.join(', ')}.`);\n }\n\n return Object.freeze(order);\n}\n\nexport { compileCanonicalOrder };\nexport type { ApplicationHardImportIR };\n"],"names":["compareIds","left","right","createHardImportGraph","featureCount","edges","outgoing","_value","index","incoming","edge","dependents","CanonicalFeatureQueue","featureIds","count","featureIndex","position","parent","parentIndex","first","last","child","childIndex","drainTopologicalOrder","graph","available","order","provider","consumer","compileCanonicalOrder","cyclic","_featureId"],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { DeclarationId } from '@opetope/core';
|
|
2
|
+
import type { ApplicationPortProviderIR, CompiledApplicationRequirementIR } from './application-port-compiler.js';
|
|
3
|
+
import type { Condition } from './condition.js';
|
|
4
|
+
import type { AnyFeature } from './feature-authoring.js';
|
|
5
|
+
import { getFeatureContributionDescriptors } from './feature-authoring.js';
|
|
6
|
+
import { getDirectHostContract } from './feature-contract.js';
|
|
7
|
+
import type { ImportDemand } from './feature-contract.js';
|
|
8
|
+
type HostContractIdentity = ReturnType<typeof getDirectHostContract>;
|
|
9
|
+
type ApplicationLifetimeOwnerIR = {
|
|
10
|
+
readonly groupId: string;
|
|
11
|
+
readonly kind: 'group';
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: 'application';
|
|
14
|
+
};
|
|
15
|
+
type ApplicationImportProviderIR = {
|
|
16
|
+
readonly contract: HostContractIdentity;
|
|
17
|
+
readonly contractId: DeclarationId;
|
|
18
|
+
readonly kind: 'absent';
|
|
19
|
+
} | {
|
|
20
|
+
readonly contract: HostContractIdentity;
|
|
21
|
+
readonly contractId: DeclarationId;
|
|
22
|
+
readonly kind: 'application-input';
|
|
23
|
+
} | {
|
|
24
|
+
readonly definition: AnyFeature;
|
|
25
|
+
readonly featureId: DeclarationId;
|
|
26
|
+
readonly featureIndex: number;
|
|
27
|
+
readonly kind: 'feature';
|
|
28
|
+
};
|
|
29
|
+
interface CompiledApplicationImportIR {
|
|
30
|
+
readonly consumerFeatureId: DeclarationId;
|
|
31
|
+
readonly consumerFeatureIndex: number;
|
|
32
|
+
readonly contract: HostContractIdentity;
|
|
33
|
+
readonly contractId: DeclarationId;
|
|
34
|
+
readonly demand: ImportDemand;
|
|
35
|
+
readonly importKey: string;
|
|
36
|
+
/** `optional(x)` in `imports`: the provider may be absent, and the consumer reads a `Lookup` projection (D105). */
|
|
37
|
+
readonly optional: boolean;
|
|
38
|
+
readonly provider: ApplicationImportProviderIR;
|
|
39
|
+
}
|
|
40
|
+
interface CompiledApplicationFeatureIR {
|
|
41
|
+
readonly definition: AnyFeature;
|
|
42
|
+
readonly exportContractId: DeclarationId;
|
|
43
|
+
readonly featureId: DeclarationId;
|
|
44
|
+
readonly featureIndex: number;
|
|
45
|
+
readonly imports: readonly CompiledApplicationImportIR[];
|
|
46
|
+
readonly owner: ApplicationLifetimeOwnerIR;
|
|
47
|
+
readonly requirements: readonly CompiledApplicationRequirementIR[];
|
|
48
|
+
}
|
|
49
|
+
interface CompiledApplicationContributionIR {
|
|
50
|
+
readonly contributionId: DeclarationId;
|
|
51
|
+
readonly contributorFeatureId: DeclarationId;
|
|
52
|
+
readonly contributorFeatureIndex: number;
|
|
53
|
+
readonly priority: number;
|
|
54
|
+
readonly target: ReturnType<typeof getFeatureContributionDescriptors>[number]['target'];
|
|
55
|
+
readonly targetId: DeclarationId;
|
|
56
|
+
}
|
|
57
|
+
/** Features waiting for the same set of conditions form one group: the inferred domain of D93. */
|
|
58
|
+
interface CompiledConditionGroupIR {
|
|
59
|
+
readonly activationOrder: readonly number[];
|
|
60
|
+
readonly conditions: readonly Condition[];
|
|
61
|
+
readonly groupId: string;
|
|
62
|
+
readonly members: readonly number[];
|
|
63
|
+
}
|
|
64
|
+
type ApplicationTopologyEdgeIR = {
|
|
65
|
+
readonly condition: Condition;
|
|
66
|
+
readonly conditionId: DeclarationId;
|
|
67
|
+
readonly groupId: string;
|
|
68
|
+
readonly kind: 'condition-control';
|
|
69
|
+
} | {
|
|
70
|
+
readonly consumerFeatureId: DeclarationId;
|
|
71
|
+
readonly contractId: DeclarationId;
|
|
72
|
+
readonly importKey: string;
|
|
73
|
+
readonly kind: 'absent-import' | 'hard-import' | 'on-demand-import' | 'optional-import';
|
|
74
|
+
readonly provider: ApplicationImportProviderIR;
|
|
75
|
+
} | {
|
|
76
|
+
readonly consumerFeatureId: DeclarationId;
|
|
77
|
+
readonly kind: 'absent-port';
|
|
78
|
+
readonly portId: DeclarationId;
|
|
79
|
+
readonly requirementKey: string;
|
|
80
|
+
} | {
|
|
81
|
+
readonly consumerFeatureId: DeclarationId;
|
|
82
|
+
readonly kind: 'requires-port';
|
|
83
|
+
readonly portId: DeclarationId;
|
|
84
|
+
readonly providerFeatureId: DeclarationId;
|
|
85
|
+
readonly requirementKey: string;
|
|
86
|
+
} | {
|
|
87
|
+
readonly contributionId: DeclarationId;
|
|
88
|
+
readonly contributorFeatureId: DeclarationId;
|
|
89
|
+
readonly kind: 'contributes-to';
|
|
90
|
+
readonly targetId: DeclarationId;
|
|
91
|
+
} | {
|
|
92
|
+
readonly featureId: DeclarationId;
|
|
93
|
+
readonly kind: 'owns';
|
|
94
|
+
readonly owner: ApplicationLifetimeOwnerIR;
|
|
95
|
+
} | {
|
|
96
|
+
readonly featureId: DeclarationId;
|
|
97
|
+
readonly kind: 'provides-port';
|
|
98
|
+
readonly portId: DeclarationId;
|
|
99
|
+
readonly providerKey: string;
|
|
100
|
+
};
|
|
101
|
+
interface CompiledApplicationPlanIR {
|
|
102
|
+
readonly applicationOrder: readonly number[];
|
|
103
|
+
readonly contributions: readonly CompiledApplicationContributionIR[];
|
|
104
|
+
readonly edges: readonly ApplicationTopologyEdgeIR[];
|
|
105
|
+
readonly features: readonly CompiledApplicationFeatureIR[];
|
|
106
|
+
readonly groups: readonly CompiledConditionGroupIR[];
|
|
107
|
+
readonly imports: readonly CompiledApplicationImportIR[];
|
|
108
|
+
readonly providers: readonly ApplicationPortProviderIR[];
|
|
109
|
+
readonly requirements: readonly CompiledApplicationRequirementIR[];
|
|
110
|
+
}
|
|
111
|
+
interface ApplicationCompileInput {
|
|
112
|
+
readonly features: readonly AnyFeature[];
|
|
113
|
+
}
|
|
114
|
+
declare function compileApplicationTopology(input: ApplicationCompileInput): CompiledApplicationPlanIR;
|
|
115
|
+
export { compileApplicationTopology };
|
|
116
|
+
export type { ApplicationCompileInput, ApplicationImportProviderIR, ApplicationLifetimeOwnerIR, ApplicationPortProviderIR, ApplicationTopologyEdgeIR, CompiledApplicationContributionIR, CompiledApplicationFeatureIR, CompiledApplicationImportIR, CompiledApplicationPlanIR, CompiledApplicationRequirementIR, CompiledConditionGroupIR, };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{compileApplicationEdges as v}from"./application-compiler-edges.js";import{compileCanonicalOrder as y}from"./application-compiler-graph.js";import{compileApplicationPorts as $}from"./application-port-compiler.js";import{conditionSetKey as w,requireConditionRecord as j}from"./condition.js";import{getFeatureContributionDescriptors as x}from"./feature-authoring.js";import{getDirectHostContract as F,getHostContractDemand as g,isHostContractOptional as z,isFeatureContract as C}from"./feature-contract.js";import{isFeature as E}from"./feature-definition-support.js";const l=Object.freeze({kind:"application"});function f(r,t){return r<t?-1:r>t?1:0}function T(r){const t=new Set,n=new Set;for(const e of r){if(!E(e))throw new TypeError("Application features contain a non-authentic Feature.");if(t.has(e))throw new TypeError(`Application feature ${e.id} is declared more than once.`);if(n.has(e.id))throw new TypeError(`Application feature id ${e.id} has more than one definition.`);t.add(e),n.add(e.id)}return Object.freeze([...r].sort((e,o)=>f(e.id,o.id)))}function k(r){const t=new Map;for(const n of r)t.set(n,n.when.length===0?l:Object.freeze({groupId:w(n.when),kind:"group"}));return t}function A(r){var e;const t=new Map,n=new Map;for(const[o,i]of r.entries()){const d=i.exports.id,s=n.get(d);if(s!==void 0)throw new TypeError(`Feature export contract ${d} has duplicate providers ${((e=r[s])==null?void 0:e.id)??"<unknown>"} and ${i.id}.`);t.set(i.exports,o),n.set(d,o)}return{byContract:t,byContractId:n}}function h(r,t){const n=new Set(r.map(e=>e.id));return t.every(e=>n.has(e.id))}function I(r,t,n,e="import"){if(!h(t.when,r.when))throw new TypeError(`Feature ${e} ${t.id}.${n} cannot depend on shorter-lived provider ${r.id}; use optional for a weak edge.`)}function q(r,t,n,e,o,i,d){if(i==="onDemand")throw new TypeError(`Feature import ${n.id}.${e} cannot defer a feature provider with onDemand.`);const s=t.byContract.get(o),c=t.byContractId.get(o.id);if(s===void 0){if(c!==void 0)throw new TypeError(`Feature import ${n.id}.${e} uses a different contract identity for ${o.id}.`);if(d)return Object.freeze({contract:o,contractId:o.id,kind:"absent"});throw new TypeError(`Feature import ${n.id}.${e} has no provider for ${o.id}: add the providing feature to defineApplication features, or make the import optional(...).`)}const a=r[s];if(a===void 0)throw new TypeError(`Feature provider ${o.id} is unavailable.`);return d||I(a,n,e),Object.freeze({definition:a,featureId:a.id,featureIndex:s,kind:"feature"})}function M(r,t,n){if(t.byContractId.has(r.id))throw new TypeError(`Application input ${r.id} conflicts with a feature export contract.`);const e=n.get(r.id);if((e==null?void 0:e.contract)!==r){if(e!==void 0)throw new TypeError(`Application input contract id ${r.id} has more than one definition.`);const o=Object.freeze({contract:r,contractId:r.id,kind:"application-input"});return n.set(r.id,o),o}return e}function D(r){const t=A(r),n=new Map,e=[];for(const[o,i]of r.entries()){const d=Object.entries(i.imports).sort(([s],[c])=>f(s,c));for(const[s,c]of d){const a=F(c),p=a.id,u=g(c),m=z(c),O=C(c)?q(r,t,i,s,a,u,m):M(a,t,n);e.push(Object.freeze({consumerFeatureId:i.id,consumerFeatureIndex:o,contract:a,contractId:p,demand:u,importKey:s,optional:m,provider:O}))}}return Object.freeze(e)}function P(r){return Object.freeze(r.flatMap(t=>t.demand==="instance"&&!t.optional&&t.provider.kind==="feature"?[Object.freeze({consumerFeatureIndex:t.consumerFeatureIndex,providerFeatureIndex:t.provider.featureIndex})]:[]))}function b(r,t){const n=Array.from({length:r},()=>[]);for(const e of t)n[e.consumerFeatureIndex].push(e);return n.map(e=>Object.freeze(e))}function S(r,t,n,e){const o=b(r.length,n),i=b(r.length,e);return Object.freeze(r.map((d,s)=>Object.freeze({definition:d,exportContractId:d.exports.id,featureId:d.id,featureIndex:s,imports:o[s],owner:t.get(d)??l,requirements:i[s]})))}function H(r){const t=r.flatMap((n,e)=>x(n).map(o=>Object.freeze({contributionId:o.id,contributorFeatureId:n.id,contributorFeatureIndex:e,priority:o.priority,target:o.target,targetId:o.target.id})));return t.sort((n,e)=>f(n.contributionId,e.contributionId)),Object.freeze(t)}function B(r){const t=new Map;for(const n of r)for(const e of n.when){const o=e.id,i=t.get(o);if(i===void 0){t.set(o,e);continue}if(i!==e)throw new TypeError(`Condition ${o} is declared twice: one id names one condition, not two.`)}}function L(r,t){var o;B(r);const n=new Map,e=[];for(const[i,d]of r.entries()){if(d.when.length===0)continue;const s=w(d.when),c=n.get(s);if(c===void 0){const a={activationOrder:[],conditions:d.when,members:[i]};n.set(s,a),e[i]=a.activationOrder}else c.members.push(i),e[i]=c.activationOrder}for(const i of t)(o=e[i])==null||o.push(i);return Object.freeze([...n.entries()].sort(([i],[d])=>f(i,d)).map(([i,d])=>Object.freeze({activationOrder:Object.freeze(d.activationOrder),conditions:Object.freeze([...d.conditions].sort((s,c)=>f(s.id,c.id))),groupId:i,members:Object.freeze([...d.members].sort((s,c)=>s-c))})))}function G(r){const t=new Set(r);for(const n of r)for(const e of n.when){const o=j(e).from;if(o!==void 0){if(!t.has(o))throw new TypeError(`Condition ${e.id} reads ${o.id}, which the application does not enable.`);if(!h(n.when,o.when))throw new TypeError(`Condition ${e.id} reads shorter-lived ${o.id} than dependent ${n.id}.`)}}}function R(r){const t=T(r.features),n=k(t);G(t);const e=D(t),o=$(t,(p,u,m)=>I(p,u,m,"requirement")),i=Object.freeze([...P(e),...o.requirements.flatMap(p=>p.optional||p.provider===void 0?[]:[Object.freeze({consumerFeatureIndex:p.consumerFeatureIndex,providerFeatureIndex:p.provider.featureIndex})])]),d=y(t.map(p=>p.id),i),s=S(t,n,e,o.requirements),c=H(t),a=L(t,d);return Object.freeze({applicationOrder:Object.freeze(d.filter(p=>{var u;return((u=s[p])==null?void 0:u.owner.kind)==="application"})),contributions:c,edges:v(s,e,o.providers,o.requirements,c,a),features:s,groups:a,imports:e,providers:o.providers,requirements:o.requirements})}export{R as compileApplicationTopology};
|
|
2
|
+
//# sourceMappingURL=application-compiler.js.map
|