@lorion-org/react 1.0.0-beta.1 → 1.0.0-beta.3

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/README.md CHANGED
@@ -1,8 +1,15 @@
1
1
  # @lorion-org/react
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@lorion-org/react)](https://www.npmjs.com/package/@lorion-org/react)
4
+ [![CI](https://github.com/lorion-org/lorion/actions/workflows/ci.yml/badge.svg)](https://github.com/lorion-org/lorion/actions/workflows/ci.yml)
5
+
3
6
  React capability runtime and Vite helpers for LORION descriptor-based applications.
4
7
 
5
- Use this package when a React application is assembled from local capability packages that expose a `capability.json` descriptor and a `./capability` activation export.
8
+ Use this package when a React application is assembled from local capability
9
+ packages that each expose a `capability.json` descriptor. It supports two
10
+ deliberate consumption models over the same LORION discovery and composition
11
+ graph, and both are first-class: pick one per product (see Two Composition
12
+ Models).
6
13
 
7
14
  ## Install
8
15
 
@@ -10,15 +17,42 @@ Use this package when a React application is assembled from local capability pac
10
17
  pnpm add @lorion-org/react react
11
18
  ```
12
19
 
20
+ Host configs that import selection helpers from `@lorion-org/composition-graph`
21
+ should declare that package directly too.
22
+
13
23
  Add Vite, TanStack Router, or another router in the host application as needed.
14
24
  The runtime helpers do not own routing; the Vite entry point only prepares
15
25
  capability discovery and TanStack-compatible virtual route config.
16
26
 
27
+ ## Quick start
28
+
29
+ Add the Vite capability loader, then consume the resolved capabilities with your
30
+ own runtime (this is the loader-only path, Model B):
31
+
32
+ ```ts
33
+ // vite.config.ts
34
+ import { capabilityLoader } from '@lorion-org/react/vite';
35
+
36
+ export default defineConfig({
37
+ plugins: [capabilityLoader({ workspaceRoot: import.meta.dirname })],
38
+ });
39
+ ```
40
+
41
+ ```ts
42
+ // main.ts
43
+ import { capabilityModules } from 'virtual:capabilities';
44
+ // register the pre-resolved capabilities with your own plugin runtime
45
+ ```
46
+
47
+ For a batteries-included React runtime and file-based routing instead, use
48
+ `lorionReact()` (Model A). Both are described under Two Composition Models.
49
+
17
50
  ## What It Is
18
51
 
19
52
  - a small React binding for immutable capability contributions
20
53
  - a Vite virtual module helper for active capability activation exports
21
54
  - a TanStack virtual route config helper for capability-owned route folders
55
+ - scoped public runtime config for active capabilities
22
56
  - a React adapter over LORION descriptor discovery and composition graph packages
23
57
 
24
58
  ## What It Is Not
@@ -28,7 +62,41 @@ capability discovery and TanStack-compatible virtual route config.
28
62
  - not a package manager
29
63
  - not an application naming convention
30
64
 
31
- ## Basic Runtime
65
+ ## Two Composition Models
66
+
67
+ Both models build on the same LORION discovery, composition graph, provider
68
+ selection, and `virtual:capabilities` build output. They differ only in how much
69
+ of the React side the host delegates to this package. Neither is a special case;
70
+ choose one per product.
71
+
72
+ ### Model A: React capability runtime and routing
73
+
74
+ `lorionReact()` wires the Vite capability loader, the React capability runtime
75
+ (`createCapabilityRuntime`), and TanStack file-based route composition together.
76
+ Capabilities activate through the `./capability` convention and own route
77
+ folders. Use this when the product wants a batteries-included React runtime and
78
+ file-based routing from LORION.
79
+
80
+ ### Model B: capability loader with your own runtime
81
+
82
+ `capabilityLoader()` on its own resolves the descriptor graph at build time and
83
+ emits `virtual:capabilities`; the host consumes that pre-resolved module list
84
+ with its own plugin registry, its own routing, and its own lifecycle. Nothing
85
+ from the React runtime or route config is required. Capabilities activate through
86
+ an explicit `activation` resolver against their existing package exports, and
87
+ dependency-only libraries stay graph-only. Use this when the product already owns
88
+ a plugin system, or when one package set ships as several product distributions,
89
+ and only needs LORION for selection and activation.
90
+
91
+ | | Model A | Model B |
92
+ | ------------- | ---------------------------------------- | ---------------------------------------------------- |
93
+ | Vite entry | `lorionReact()` | `capabilityLoader()` |
94
+ | React runtime | `createCapabilityRuntime` (this package) | host-owned |
95
+ | Routing | TanStack file-based via `routeConfig` | host-owned (for example code-based) |
96
+ | Activation | `./capability` convention | explicit `activation` resolver, graph-only otherwise |
97
+ | Host consumes | provider and contribution contracts | `capabilityModules` from `virtual:capabilities` |
98
+
99
+ ## React Capability Runtime (Model A)
32
100
 
33
101
  ```ts
34
102
  import { CapabilityRuntimeProvider, createCapabilityRuntime } from '@lorion-org/react';
@@ -111,19 +179,19 @@ to TanStack Router as a capability-owned route subtree.
111
179
  ## Vite
112
180
 
113
181
  ```ts
114
- import { lorionReact } from '@lorion-org/react/vite';
182
+ import { capabilityLoader, lorionReact } from '@lorion-org/react/vite';
115
183
  ```
116
184
 
117
- The Vite helper discovers `capabilities/*/capability.json`, validates the descriptor shape with LORION, resolves selected descriptors through the LORION composition graph, resolves each package `./capability` export, and exposes `virtual:capabilities`.
185
+ `capabilityLoader` is the standalone loader used by Model B. `lorionReact()`
186
+ bundles that loader with the Model A route config.
187
+
188
+ The Vite helper discovers `capabilities/*/capability.json`, validates the descriptor shape with LORION, resolves selected descriptors through the LORION composition graph, resolves each active capability's activation entry, and exposes `virtual:capabilities`.
118
189
 
119
190
  ```ts
120
- const capabilityComposition = {
121
- selected: ['default'],
122
- };
123
191
  const lorion = lorionReact({
124
192
  workspaceRoot,
125
193
  routesDirectory,
126
- ...capabilityComposition,
194
+ defaultSelection: ['default'],
127
195
  });
128
196
 
129
197
  export default defineConfig({
@@ -138,32 +206,260 @@ export default defineConfig({
138
206
  });
139
207
  ```
140
208
 
141
- Route config generation stays TanStack-focused and only includes enabled, selected capability route directories. If no `selected` or `baseDescriptors` are provided, every enabled local capability remains active. Use `indexRouteFile: false` when `/` is owned by a capability route.
209
+ By default the Vite helper reads the shared capability seed from
210
+ `--capabilities`, `npm_config_capabilities`, and `LORION_CAPABILITIES` before it
211
+ falls back to `defaultSelection`. No `selectionSeed.key` option is required for
212
+ that default. Pass `selectionSeed` only to override the seed names, inject custom
213
+ `argv`/`env` for tests, or set `selectionSeed: false` to disable CLI/env lookup.
214
+
215
+ Route config generation stays TanStack-focused and only includes enabled,
216
+ selected capability route directories. If no `selected`, seed value,
217
+ `defaultSelection`, or `baseDescriptors` are provided, every enabled local
218
+ capability remains active.
219
+ Use `indexRouteFile: false` when `/` is owned by a capability route.
220
+
221
+ The virtual module exports `capabilityModules`, `selectedCapabilityIds`, and
222
+ `resolvedCapabilityIds` so host code can distinguish the seed from the final
223
+ graph resolution.
224
+
225
+ ### Activation
226
+
227
+ Activation binds a resolved descriptor to the module the host imports. LORION
228
+ supports two models, chosen by the host.
229
+
230
+ Convention (default): each capability activates through a `./capability` package
231
+ export with a `capability` named export. No option is required.
232
+
233
+ Explicit resolver: pass an `activation` resolver to bind against an existing
234
+ package export, so capabilities that already expose their contribution from
235
+ another entry point need no dedicated activation file:
236
+
237
+ ```ts
238
+ capabilityLoader({
239
+ workspaceRoot,
240
+ activation: ({ descriptor }) => ({
241
+ exportSubpath: './web',
242
+ exportName: `${descriptor.id}WebPlugin`,
243
+ }),
244
+ });
245
+ ```
246
+
247
+ The generated import then uses the resolved subpath and export name (for example
248
+ `import { homeWebPlugin as homeCapability } from '@scope/home/web'`), and
249
+ specifier resolution is left to the host bundler rather than self-resolved by
250
+ LORION.
251
+
252
+ Graph-only: when the resolver returns a nullish activation for a descriptor, that
253
+ capability still takes part in dependency resolution but activates nothing. No
254
+ import is emitted and it never reaches `capabilityModules`. Use this for
255
+ dependency-only libraries that shape the graph without contributing a runtime
256
+ plugin.
257
+
258
+ ## Bring Your Own Runtime (Model B)
259
+
260
+ In Model B the host uses only the Vite capability loader and composes the
261
+ resolved modules with its own runtime. The build resolves the descriptor graph
262
+ (base, selected features, transitive dependencies, and exactly one provider per
263
+ capability) and emits `capabilityModules` already ordered and filtered.
264
+
265
+ ```ts
266
+ // vite.config.ts
267
+ capabilityLoader({
268
+ workspaceRoot,
269
+ capabilitiesDir: 'packages',
270
+ baseDescriptors: ['shell', 'auth'], // always-on platform base
271
+ defaultSelection: ['home', 'reports'], // default feature set
272
+ selectionSeed: { cliKeys: ['features'], envKeys: ['APP_FEATURES'] },
273
+ // Read a host-defined descriptor field; return undefined to keep a package
274
+ // graph-only. LORION descriptors carry host keys unchanged, so annotate the read.
275
+ activation: ({ descriptor }) =>
276
+ (descriptor as { surfaces?: { web?: { exportName?: string; exportSubpath?: string } } })
277
+ .surfaces?.web,
278
+ });
279
+ ```
280
+
281
+ To reuse the framework-free surface convention from
282
+ [`@lorion-org/surface-activation`](../surface-activation) directly — without a
283
+ per-host adapter — pass `surface` instead of `activation`. Pass exactly one of the
284
+ two (passing both throws). The `fileSurfaceConvention` preset detects a surface by a
285
+ file marker and derives its export name and import subpath (here the canonical
286
+ `@scope/<id>/web/plugin` entry); the raw `SurfaceConvention` object stays available
287
+ for cases the preset does not cover:
288
+
289
+ ```ts
290
+ import { existsSync } from 'node:fs';
291
+ import { join } from 'node:path';
292
+ import { conventionActivation, fileSurfaceConvention } from '@lorion-org/surface-activation';
293
+
294
+ capabilityLoader({
295
+ workspaceRoot,
296
+ capabilitiesDir: 'packages',
297
+ baseDescriptors: ['shell', 'auth'],
298
+ defaultSelection: ['home', 'reports'],
299
+ surface: {
300
+ name: 'web',
301
+ resolver: conventionActivation({
302
+ web: fileSurfaceConvention({
303
+ files: ['src/web/plugin.ts'],
304
+ exportSuffix: 'WebPlugin',
305
+ exportSubpath: './web/plugin',
306
+ exists: existsSync, // injected — surface-activation itself touches no filesystem
307
+ join,
308
+ }),
309
+ }),
310
+ },
311
+ });
312
+ ```
313
+
314
+ Migrating from a hand-written adapter: replace
315
+ `activation: ({ capabilityDir, descriptor }) => resolver('web', { directory: capabilityDir, id: descriptor.id })`
316
+ with `surface: { name: 'web', resolver }`.
317
+
318
+ ```ts
319
+ // main.ts: consume the pre-resolved list with your own registry
320
+ import { capabilityModules } from 'virtual:capabilities';
321
+ import { createRegistry } from './my-plugin-system';
322
+
323
+ const registry = createRegistry();
324
+ for (const plugin of capabilityModules) registry.register(plugin);
325
+ await registry.setup();
326
+ ```
327
+
328
+ The host runtime lists no capability by hand and makes no provider decision.
329
+ Adding or removing a package changes only the descriptor graph, not the runtime
330
+ wiring. Route ownership, i18n merging, and lifecycle hooks stay in the host's own
331
+ plugin system.
332
+
333
+ ## Runtime Config
334
+
335
+ React runtime config follows the same LORION ownership model as other
336
+ capability data: a capability owns its config contract, deployment inputs provide
337
+ values, and the framework adapter exposes only the safe runtime view.
338
+
339
+ By default the React Vite adapter looks for:
340
+
341
+ ```text
342
+ capabilities/<capability>/capability.schema.json
343
+ .data/runtime-config/<capability>/capability.runtime.json
344
+ ```
345
+
346
+ Hosts can configure the convention once:
347
+
348
+ ```ts
349
+ const lorion = lorionReact({
350
+ workspaceRoot,
351
+ routesDirectory,
352
+ runtimeConfig: {
353
+ configFileName: 'capability.runtime.json',
354
+ schemaFileName: 'capability.schema.json',
355
+ },
356
+ });
357
+ ```
358
+
359
+ By default, file-backed config is read from `<workspaceRoot>/.data`. Hosts that
360
+ need a deployment-controlled var dir can configure an env key:
361
+
362
+ ```ts
363
+ const lorion = lorionReact({
364
+ workspaceRoot,
365
+ routesDirectory,
366
+ runtimeConfig: {
367
+ varDir: {
368
+ envKey: 'REACT_VAR_DIR',
369
+ },
370
+ },
371
+ });
372
+ ```
373
+
374
+ Runtime files use unprefixed capability-local sections:
375
+
376
+ ```json
377
+ {
378
+ "public": {
379
+ "url": "https://id.example.test",
380
+ "realm": "demo",
381
+ "clientId": "web"
382
+ },
383
+ "private": {
384
+ "clientSecret": "server-only"
385
+ }
386
+ }
387
+ ```
388
+
389
+ The adapter also reads Vite env files and process env. Public keys use the
390
+ `VITE_<CAPABILITY>_<KEY>` convention, while private keys use
391
+ `<CAPABILITY>_<KEY>`:
392
+
393
+ ```text
394
+ VITE_AUTH_OIDC_URL=https://id.example.test
395
+ VITE_AUTH_OIDC_REALM=demo
396
+ VITE_AUTH_OIDC_CLIENT_ID=web
397
+ AUTH_OIDC_CLIENT_SECRET=server-only
398
+ ```
399
+
400
+ Env values override runtime files. Only `public` config is emitted through
401
+ `virtual:capability-runtime-config`; server code can opt into
402
+ `virtual:capability-runtime-config/server`. The server virtual module is
403
+ SSR-only and fails during client builds to prevent private config from being
404
+ bundled.
405
+
406
+ Render the config provider near the capability runtime provider:
407
+
408
+ ```tsx
409
+ import { CapabilityRuntimeConfigProvider } from '@lorion-org/react';
410
+ import { capabilityRuntimeConfig } from 'virtual:capability-runtime-config';
411
+
412
+ <CapabilityRuntimeConfigProvider runtimeConfig={capabilityRuntimeConfig}>
413
+ <App />
414
+ </CapabilityRuntimeConfigProvider>;
415
+ ```
416
+
417
+ Capability code reads scoped public config:
418
+
419
+ ```ts
420
+ import { useCapabilityRuntimeConfig } from '@lorion-org/react';
421
+
422
+ const authOidc = useCapabilityRuntimeConfig('auth-oidc');
423
+ console.log(authOidc.public.url);
424
+ ```
142
425
 
143
426
  ## Provider Selection
144
427
 
145
- Capabilities that implement another capability can declare `providesFor`:
428
+ Capabilities that implement another capability can declare `providesFor`.
429
+ Provider-owned defaults use `defaultFor` on the provider descriptor:
146
430
 
147
431
  ```json
148
432
  {
149
433
  "id": "payment-provider-stripe",
150
434
  "version": "1.0.0",
151
- "providesFor": "payment-checkout"
435
+ "providesFor": "checkout",
436
+ "defaultFor": "checkout"
152
437
  }
153
438
  ```
154
439
 
155
- Any active descriptor can declare preferences with `providerPreferences`:
440
+ `providesFor` and `defaultFor` both accept a string or string array. If a
441
+ capability descriptor exists, `defaultFor` also creates the composition relation
442
+ from that capability to the default provider.
443
+
444
+ Profiles can still declare descriptor preferences with `providerPreferences`.
445
+ Use this when the profile, not the provider package, owns the default choice:
156
446
 
157
447
  ```json
158
448
  {
159
449
  "id": "web",
160
450
  "version": "1.0.0",
161
451
  "providerPreferences": {
162
- "payment-checkout": "payment-provider-stripe"
452
+ "checkout": "payment-provider-stripe"
163
453
  }
164
454
  }
165
455
  ```
166
456
 
457
+ When a provider capability is explicitly selected through the normal selection
458
+ seed, the Vite helper removes lower-priority `defaultFor` and
459
+ `providerPreferences` relations for that capability before graph resolution.
460
+ That selected provider wins over descriptor defaults and preferences. A losing
461
+ provider is only present if another hard dependency still requires it.
462
+
167
463
  Read the resolved provider selection from the runtime:
168
464
 
169
465
  ```ts
@@ -172,24 +468,57 @@ import { getCapabilityProviderSelection } from '@lorion-org/react';
172
468
  const selection = getCapabilityProviderSelection(capabilityRuntime);
173
469
  ```
174
470
 
175
- Explicit `configuredProviders` passed to `getCapabilityProviderSelection()` override descriptor preferences. `fallbackProviders` are only used when no configured provider exists.
471
+ Explicit `configuredProviders` passed to `getCapabilityProviderSelection()`
472
+ override selected providers, provider-owned defaults, and descriptor
473
+ preferences. `selectedProviders` can mirror the descriptor seed at runtime, and
474
+ `fallbackProviders` are merged with descriptor defaults and only used when no
475
+ configured or selected provider exists.
476
+
477
+ The React example uses the first variant by default: Stripe declares
478
+ `defaultFor: "checkout"` and is selected as the fallback provider. Selecting
479
+ `web payment-provider-invoice` through the seed switches checkout to Invoice and
480
+ leaves Stripe out of the resolved capabilities.
176
481
 
177
482
  ## API
178
483
 
179
484
  The package exposes two public entry points:
180
485
 
181
- - `@lorion-org/react` for runtime, contribution contracts, provider selection, and React context helpers
182
- - `@lorion-org/react/vite` for capability discovery, the Vite virtual module, and TanStack-compatible route config
486
+ - `@lorion-org/react` for runtime, contribution contracts, provider selection, runtime config, and React context helpers
487
+ - `@lorion-org/react/vite` for capability discovery, runtime-config virtual modules, and TanStack-compatible route config
488
+
489
+ ## Example apps
490
+
491
+ Two runnable examples (at the repo root under `examples/`) demonstrate the two
492
+ models. Both run with Lorion's `lorion-source` export condition so local
493
+ workspace imports resolve to `src` instead of stale `dist` output.
494
+
495
+ Model A, `examples/react-runtime`, mirrors the Nuxt example with a demo shop,
496
+ checkout providers, and a tech monitor (composition runtime and file-based
497
+ routing):
498
+
499
+ ```sh
500
+ pnpm --filter @lorion-examples/react-runtime dev
501
+ ```
183
502
 
184
- ## Playground
503
+ It runs on `http://localhost:3200` with capabilities under
504
+ `examples/react-runtime/capabilities`. Select a different profile or provider with
505
+ `--capabilities=admin`, `--capabilities=web,payment-provider-invoice`, or
506
+ `LORION_CAPABILITIES="web payment-provider-invoice"`.
185
507
 
186
- The package includes a React playground that mirrors the Nuxt package playground with a demo shop, checkout providers, and a tech monitor.
508
+ Model B, `examples/react-loader`, shows the capability-loader-only path: explicit
509
+ activation, a graph-only library, a base plus seed selection, provider selection,
510
+ and a small hand-written registry that consumes `virtual:capabilities` with no
511
+ LORION React runtime and no route config:
187
512
 
188
513
  ```sh
189
- pnpm --filter @lorion-org/react dev:playground
514
+ pnpm --filter @lorion-examples/react-loader dev
190
515
  ```
191
516
 
192
- The playground runs on `http://localhost:3200` and uses local demo capabilities under `playground/capabilities`.
517
+ It runs on `http://localhost:3201` with capabilities under
518
+ `examples/react-loader/capabilities`. The seed replaces the default selection, while
519
+ the base and providers resolve through the graph: switch the auth provider with
520
+ `--features=dashboard,auth-oidc`, or change the feature set with
521
+ `LORION_FEATURES="dashboard reports"`.
193
522
 
194
523
  ## Local Commands
195
524
 
package/dist/index.cjs CHANGED
@@ -20,19 +20,62 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ CapabilityRuntimeConfigProvider: () => CapabilityRuntimeConfigProvider,
23
24
  CapabilityRuntimeProvider: () => CapabilityRuntimeProvider,
25
+ createCapabilityCompositionPolicy: () => import_descriptor_selection.descriptorSelectionPolicy,
24
26
  createCapabilityRuntime: () => createCapabilityRuntime,
25
27
  createContributionContract: () => createContributionContract,
28
+ defaultCapabilityRelationDescriptors: () => import_descriptor_selection.providerRelationDescriptors,
29
+ defaultCapabilityResolutionRelations: () => import_descriptor_selection.defaultResolutionRelations,
26
30
  defineCapability: () => defineCapability,
27
31
  defineContribution: () => defineContribution,
28
32
  defineExtensionPoint: () => defineExtensionPoint,
29
33
  getCapabilityProviderSelection: () => getCapabilityProviderSelection,
30
- useCapabilityRuntime: () => useCapabilityRuntime
34
+ getCapabilityRuntimeConfig: () => getCapabilityRuntimeConfig,
35
+ getCapabilityRuntimeConfigScope: () => getCapabilityRuntimeConfigScope,
36
+ useCapabilityRuntime: () => useCapabilityRuntime,
37
+ useCapabilityRuntimeConfig: () => useCapabilityRuntimeConfig,
38
+ useCapabilityRuntimeConfigRoot: () => useCapabilityRuntimeConfigRoot,
39
+ useCapabilityRuntimeConfigScope: () => useCapabilityRuntimeConfigScope
31
40
  });
32
41
  module.exports = __toCommonJS(index_exports);
33
- var import_react = require("react");
42
+ var import_react2 = require("react");
34
43
  var import_composition_graph = require("@lorion-org/composition-graph");
35
44
  var import_provider_selection = require("@lorion-org/provider-selection");
45
+
46
+ // src/relations.ts
47
+ var import_descriptor_selection = require("@lorion-org/descriptor-selection");
48
+
49
+ // src/runtime-config.ts
50
+ var import_react = require("react");
51
+ var emptyRuntimeConfig = Object.freeze({ public: Object.freeze({}) });
52
+ var CapabilityRuntimeConfigContext = (0, import_react.createContext)(emptyRuntimeConfig);
53
+ function CapabilityRuntimeConfigProvider({
54
+ children,
55
+ runtimeConfig
56
+ }) {
57
+ return (0, import_react.createElement)(CapabilityRuntimeConfigContext.Provider, { value: runtimeConfig }, children);
58
+ }
59
+ function useCapabilityRuntimeConfigScope(capabilityId) {
60
+ return getCapabilityRuntimeConfigScope(useCapabilityRuntimeConfigRoot(), capabilityId);
61
+ }
62
+ function useCapabilityRuntimeConfig(capabilityId) {
63
+ return getCapabilityRuntimeConfig(useCapabilityRuntimeConfigRoot(), capabilityId);
64
+ }
65
+ function useCapabilityRuntimeConfigRoot() {
66
+ return (0, import_react.useContext)(CapabilityRuntimeConfigContext);
67
+ }
68
+ function getCapabilityRuntimeConfigScope(runtimeConfig, capabilityId) {
69
+ const publicConfig = runtimeConfig.public[capabilityId];
70
+ return publicConfig && typeof publicConfig === "object" && !Array.isArray(publicConfig) ? publicConfig : {};
71
+ }
72
+ function getCapabilityRuntimeConfig(runtimeConfig, capabilityId) {
73
+ return {
74
+ public: getCapabilityRuntimeConfigScope(runtimeConfig, capabilityId)
75
+ };
76
+ }
77
+
78
+ // src/index.ts
36
79
  function defineExtensionPoint(id) {
37
80
  return { id };
38
81
  }
@@ -54,16 +97,25 @@ function getCapabilityProviderSelection(runtime, options = {}) {
54
97
  items: descriptors,
55
98
  getProviderPreferences: (descriptor) => descriptor.providerPreferences
56
99
  });
57
- const configuredProviders = {
100
+ const providerDefaults = (0, import_provider_selection.collectProviderDefaults)({
101
+ items: descriptors,
102
+ getDefaultFor: (descriptor) => descriptor.defaultFor,
103
+ getProviderId: (descriptor) => descriptor.id
104
+ });
105
+ const configuredProviders = options.configuredProviders ?? {};
106
+ const selectedProviders = options.selectedProviders ?? {};
107
+ const fallbackProviders = {
108
+ ...providerDefaults,
58
109
  ...descriptorPreferences,
59
- ...options.configuredProviders ?? {}
110
+ ...options.fallbackProviders ?? {}
60
111
  };
61
112
  const resolution = (0, import_provider_selection.resolveItemProviderSelection)({
62
113
  items: descriptors,
63
114
  getCapabilityId: (descriptor) => descriptor.providesFor,
64
115
  getProviderId: (descriptor) => descriptor.id,
65
116
  configuredProviders,
66
- ...options.fallbackProviders ? { fallbackProviders: options.fallbackProviders } : {}
117
+ fallbackProviders,
118
+ selectedProviders
67
119
  });
68
120
  return {
69
121
  excludedProviderIds: resolution.excludedProviderIds,
@@ -93,15 +145,15 @@ function createCapabilityRuntime(capabilities) {
93
145
  getContributions: (extensionPoint) => [...contributions.get(extensionPoint.id) ?? []]
94
146
  };
95
147
  }
96
- var CapabilityRuntimeContext = (0, import_react.createContext)(null);
148
+ var CapabilityRuntimeContext = (0, import_react2.createContext)(null);
97
149
  function CapabilityRuntimeProvider({
98
150
  children,
99
151
  runtime
100
152
  }) {
101
- return (0, import_react.createElement)(CapabilityRuntimeContext.Provider, { value: runtime }, children);
153
+ return (0, import_react2.createElement)(CapabilityRuntimeContext.Provider, { value: runtime }, children);
102
154
  }
103
155
  function useCapabilityRuntime() {
104
- const runtime = (0, import_react.useContext)(CapabilityRuntimeContext);
156
+ const runtime = (0, import_react2.useContext)(CapabilityRuntimeContext);
105
157
  if (!runtime) {
106
158
  throw new Error("useCapabilityRuntime must be used inside CapabilityRuntimeProvider.");
107
159
  }
@@ -138,17 +190,27 @@ function collectContributions(capabilities) {
138
190
  }
139
191
  function createCapabilityCatalog(capabilities) {
140
192
  return (0, import_composition_graph.createDescriptorCatalog)({
141
- descriptors: capabilities.map((capability) => capability.manifest)
193
+ descriptors: capabilities.map((capability) => capability.manifest),
194
+ relationDescriptors: import_descriptor_selection.providerRelationDescriptors
142
195
  });
143
196
  }
144
197
  // Annotate the CommonJS export names for ESM import in node:
145
198
  0 && (module.exports = {
199
+ CapabilityRuntimeConfigProvider,
146
200
  CapabilityRuntimeProvider,
201
+ createCapabilityCompositionPolicy,
147
202
  createCapabilityRuntime,
148
203
  createContributionContract,
204
+ defaultCapabilityRelationDescriptors,
205
+ defaultCapabilityResolutionRelations,
149
206
  defineCapability,
150
207
  defineContribution,
151
208
  defineExtensionPoint,
152
209
  getCapabilityProviderSelection,
153
- useCapabilityRuntime
210
+ getCapabilityRuntimeConfig,
211
+ getCapabilityRuntimeConfigScope,
212
+ useCapabilityRuntime,
213
+ useCapabilityRuntimeConfig,
214
+ useCapabilityRuntimeConfigRoot,
215
+ useCapabilityRuntimeConfigScope
154
216
  });
package/dist/index.d.cts CHANGED
@@ -1,10 +1,31 @@
1
1
  import { ReactNode, ReactElement } from 'react';
2
2
  import { Descriptor, DescriptorCatalog } from '@lorion-org/composition-graph';
3
3
  import { ProviderPreferenceMap, ProviderSelectionResolution } from '@lorion-org/provider-selection';
4
+ import { RuntimeConfigSection, RuntimeConfigValidationPolicy } from '@lorion-org/runtime-config';
5
+ export { descriptorSelectionPolicy as createCapabilityCompositionPolicy, providerRelationDescriptors as defaultCapabilityRelationDescriptors, defaultResolutionRelations as defaultCapabilityResolutionRelations } from '@lorion-org/descriptor-selection';
6
+
7
+ type CapabilityRuntimeConfig = {
8
+ public: Record<string, RuntimeConfigSection>;
9
+ };
10
+ type CapabilityRuntimeConfigFragment<T extends RuntimeConfigSection = RuntimeConfigSection> = {
11
+ public: T;
12
+ };
13
+ type CapabilityRuntimeConfigProviderProps = {
14
+ children: ReactNode;
15
+ runtimeConfig: CapabilityRuntimeConfig;
16
+ };
17
+ declare function CapabilityRuntimeConfigProvider({ children, runtimeConfig, }: CapabilityRuntimeConfigProviderProps): ReactElement;
18
+ declare function useCapabilityRuntimeConfigScope<T extends RuntimeConfigSection = RuntimeConfigSection>(capabilityId: string): T;
19
+ declare function useCapabilityRuntimeConfig<T extends RuntimeConfigSection = RuntimeConfigSection>(capabilityId: string): CapabilityRuntimeConfigFragment<T>;
20
+ declare function useCapabilityRuntimeConfigRoot(): CapabilityRuntimeConfig;
21
+ declare function getCapabilityRuntimeConfigScope<T extends RuntimeConfigSection = RuntimeConfigSection>(runtimeConfig: CapabilityRuntimeConfig, capabilityId: string): T;
22
+ declare function getCapabilityRuntimeConfig<T extends RuntimeConfigSection = RuntimeConfigSection>(runtimeConfig: CapabilityRuntimeConfig, capabilityId: string): CapabilityRuntimeConfigFragment<T>;
4
23
 
5
24
  type CapabilityManifest = Descriptor & {
25
+ defaultFor?: string | string[];
6
26
  description?: string;
7
27
  providerPreferences?: ProviderPreferenceMap;
28
+ runtimeConfig?: RuntimeConfigValidationPolicy;
8
29
  };
9
30
  type ExtensionPoint<T> = {
10
31
  id: string;
@@ -36,6 +57,7 @@ declare function createContributionContract<T>(id: string): ContributionContract
36
57
  type CapabilityProviderSelectionOptions = {
37
58
  configuredProviders?: ProviderPreferenceMap;
38
59
  fallbackProviders?: ProviderPreferenceMap;
60
+ selectedProviders?: ProviderPreferenceMap;
39
61
  };
40
62
  declare function getCapabilityProviderSelection(runtime: CapabilityRuntime, options?: CapabilityProviderSelectionOptions): ProviderSelectionResolution;
41
63
  declare function defineCapability(capability: RuntimeCapability): RuntimeCapability;
@@ -47,4 +69,4 @@ type CapabilityRuntimeProviderProps = {
47
69
  declare function CapabilityRuntimeProvider({ children, runtime, }: CapabilityRuntimeProviderProps): ReactElement;
48
70
  declare function useCapabilityRuntime(): CapabilityRuntime;
49
71
 
50
- export { type CapabilityContribution, type CapabilityManifest, type CapabilityProviderSelectionOptions, type CapabilityRuntime, CapabilityRuntimeProvider, type CapabilityRuntimeProviderProps, type ContributionContract, type ExtensionPoint, type RuntimeCapability, createCapabilityRuntime, createContributionContract, defineCapability, defineContribution, defineExtensionPoint, getCapabilityProviderSelection, useCapabilityRuntime };
72
+ export { type CapabilityContribution, type CapabilityManifest, type CapabilityProviderSelectionOptions, type CapabilityRuntime, type CapabilityRuntimeConfig, type CapabilityRuntimeConfigFragment, CapabilityRuntimeConfigProvider, type CapabilityRuntimeConfigProviderProps, CapabilityRuntimeProvider, type CapabilityRuntimeProviderProps, type ContributionContract, type ExtensionPoint, type RuntimeCapability, createCapabilityRuntime, createContributionContract, defineCapability, defineContribution, defineExtensionPoint, getCapabilityProviderSelection, getCapabilityRuntimeConfig, getCapabilityRuntimeConfigScope, useCapabilityRuntime, useCapabilityRuntimeConfig, useCapabilityRuntimeConfigRoot, useCapabilityRuntimeConfigScope };