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

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/dist/vite.js CHANGED
@@ -1,14 +1,25 @@
1
+ import {
2
+ createCapabilityCompositionPolicy,
3
+ defaultCapabilityRelationDescriptors
4
+ } from "./chunk-5FUI4JPB.js";
5
+
1
6
  // src/vite.ts
2
7
  import { existsSync, readFileSync } from "fs";
3
8
  import { dirname, join, relative, resolve, sep } from "path";
9
+ import process from "process";
4
10
  import {
5
11
  createCompositionSelection,
6
- createDescriptorCatalog
12
+ createDescriptorCatalog,
13
+ resolveDescriptorSelectionSeed
7
14
  } from "@lorion-org/composition-graph";
8
15
  import {
9
16
  descriptorSchema,
10
17
  discoverDescriptors
11
18
  } from "@lorion-org/descriptor-discovery";
19
+ import {
20
+ collectSelectedProviderPreferences,
21
+ resolveSelectedProviderRelationPreferences
22
+ } from "@lorion-org/provider-selection";
12
23
  var virtualModuleId = "virtual:capabilities";
13
24
  var resolvedVirtualModuleId = `\0${virtualModuleId}`;
14
25
  function capabilityLoader(options = {}) {
@@ -30,7 +41,7 @@ function capabilityLoader(options = {}) {
30
41
  },
31
42
  load(id) {
32
43
  if (id !== resolvedVirtualModuleId) return null;
33
- return renderCapabilityModule(capabilities);
44
+ return renderCapabilityModule(capabilities, resolveSelectionSeed(options));
34
45
  }
35
46
  };
36
47
  }
@@ -52,28 +63,85 @@ function discoverSelectedCapabilities(workspaceRoot, options = {}) {
52
63
  }
53
64
  function selectCapabilities(capabilities, options = {}) {
54
65
  const enabledCapabilities = capabilities.filter((capability) => capability.disabled !== true);
55
- if (!options.selected?.length && !options.baseDescriptors?.length) {
66
+ const selected = resolveCapabilitySelectionSeed(options);
67
+ if (!selected.length && !options.baseDescriptors?.length) {
56
68
  return [...enabledCapabilities];
57
69
  }
70
+ const selectedProviders = collectSelectedProviderPreferences({
71
+ items: enabledCapabilities,
72
+ getCapabilityId: (capability) => capability.manifest.providesFor,
73
+ getProviderId: (capability) => capability.id,
74
+ selectedProviderIds: selected
75
+ });
76
+ const selectionCapabilities = createProviderSelectionAwareCapabilities(
77
+ enabledCapabilities,
78
+ selectedProviders
79
+ );
58
80
  const catalog = createDescriptorCatalog({
59
- descriptors: enabledCapabilities.map((capability) => capability.manifest)
81
+ descriptors: selectionCapabilities.map((capability) => capability.manifest),
82
+ relationDescriptors: [
83
+ ...defaultCapabilityRelationDescriptors,
84
+ ...options.relationDescriptors ?? []
85
+ ]
60
86
  });
61
87
  const selection = createCompositionSelection({
62
88
  catalog,
63
- selected: [...options.selected ?? []],
89
+ selected: [...selected],
64
90
  baseDescriptors: [...options.baseDescriptors ?? []],
65
- ...options.policy ? { policy: options.policy } : {}
91
+ policy: createCapabilityCompositionPolicy(options.policy)
66
92
  });
67
93
  const selectedIds = new Set(selection.getResolved());
68
- return enabledCapabilities.filter((capability) => selectedIds.has(capability.id));
94
+ return selectionCapabilities.filter((capability) => selectedIds.has(capability.id));
95
+ }
96
+ function createProviderSelectionAwareCapabilities(capabilities, selectedProviders) {
97
+ if (!Object.keys(selectedProviders).length) return [...capabilities];
98
+ return capabilities.map((capability) => {
99
+ const manifest = { ...capability.manifest };
100
+ const preferences = resolveSelectedProviderRelationPreferences({
101
+ providerId: capability.id,
102
+ defaultFor: manifest.defaultFor,
103
+ providerPreferences: manifest.providerPreferences,
104
+ selectedProviders
105
+ });
106
+ delete manifest.defaultFor;
107
+ delete manifest.providerPreferences;
108
+ return {
109
+ ...capability,
110
+ manifest: {
111
+ ...manifest,
112
+ ...preferences
113
+ }
114
+ };
115
+ });
116
+ }
117
+ function resolveSelectionSeed(options) {
118
+ return resolveCapabilitySelectionSeed(options);
119
+ }
120
+ function resolveCapabilitySelectionSeed(options) {
121
+ if (options.selected?.length) return [...options.selected];
122
+ if (options.selectionSeed === false) return [...options.defaultSelection ?? []];
123
+ const seedOptions = options.selectionSeed ?? {};
124
+ const selected = resolveDescriptorSelectionSeed({
125
+ argv: seedOptions.argv ?? process.argv,
126
+ env: seedOptions.env ?? process.env,
127
+ key: seedOptions.key ?? "capability",
128
+ ...seedOptions.cliKeys ? { cliKeys: seedOptions.cliKeys } : {},
129
+ ...seedOptions.envKeys ? { envKeys: seedOptions.envKeys } : {}
130
+ });
131
+ return selected.length ? selected : [...options.defaultSelection ?? []];
69
132
  }
70
- function renderCapabilityModule(capabilities) {
133
+ function renderCapabilityModule(capabilities, selected = []) {
71
134
  const imports = capabilities.map(
72
135
  (capability) => `import { capability as ${capability.variableName} } from '${capability.importSpecifier}'`
73
136
  ).join("\n");
74
137
  const variables = capabilities.map((capability) => ` ${capability.variableName},`).join("\n");
138
+ const capabilityIds = capabilities.map((capability) => capability.id);
75
139
  return `${imports}
76
140
 
141
+ export const selectedCapabilityIds = ${JSON.stringify([...selected])}
142
+
143
+ export const resolvedCapabilityIds = ${JSON.stringify(capabilityIds)}
144
+
77
145
  export const capabilityModules = [
78
146
  ${variables}
79
147
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lorion-org/react",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "description": "React capability runtime and Vite helpers for LORION descriptor-based applications.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,6 +22,10 @@
22
22
  },
23
23
  "exports": {
24
24
  ".": {
25
+ "lorion-source": {
26
+ "types": "./src/index.ts",
27
+ "default": "./src/index.ts"
28
+ },
25
29
  "import": {
26
30
  "types": "./dist/index.d.ts",
27
31
  "default": "./dist/index.js"
@@ -32,6 +36,10 @@
32
36
  }
33
37
  },
34
38
  "./vite": {
39
+ "lorion-source": {
40
+ "types": "./src/vite.ts",
41
+ "default": "./src/vite.ts"
42
+ },
35
43
  "import": {
36
44
  "types": "./dist/vite.d.ts",
37
45
  "default": "./dist/vite.js"
@@ -44,12 +52,14 @@
44
52
  },
45
53
  "files": [
46
54
  "dist",
47
- "LICENSE"
55
+ "LICENSE",
56
+ "src/**/*.ts",
57
+ "!src/**/*.spec.ts"
48
58
  ],
49
59
  "dependencies": {
50
- "@lorion-org/composition-graph": "^1.0.0-beta.1",
51
- "@lorion-org/provider-selection": "^1.0.0-beta.1",
52
- "@lorion-org/descriptor-discovery": "^1.0.0-beta.1"
60
+ "@lorion-org/composition-graph": "^1.0.0-beta.2",
61
+ "@lorion-org/descriptor-discovery": "^1.0.0-beta.2",
62
+ "@lorion-org/provider-selection": "^1.0.0-beta.2"
53
63
  },
54
64
  "peerDependencies": {
55
65
  "react": "^19.0.0"
@@ -80,8 +90,8 @@
80
90
  "clean": "rimraf coverage dist tsconfig.tsbuildinfo",
81
91
  "lint": "eslint src --ext .ts",
82
92
  "package:check": "pnpm build && pnpm pack --dry-run && publint",
83
- "build:playground": "vite build --config playground/vite.config.ts",
84
- "dev:playground": "vite dev --config playground/vite.config.ts --host 0.0.0.0",
93
+ "build:playground": "node ../../tools/run-source-condition.mjs vite build --config playground/vite.config.ts --",
94
+ "dev:playground": "node ../../tools/run-source-condition.mjs vite dev --config playground/vite.config.ts --host 0.0.0.0 --",
85
95
  "test": "vitest run --root ../.. --config vitest.config.mts packages/react/src/index.spec.ts packages/react/src/vite.spec.ts",
86
96
  "typecheck": "tsc -p tsconfig.json --noEmit",
87
97
  "typecheck:playground": "tsc -p playground/tsconfig.json --noEmit"
package/src/index.ts ADDED
@@ -0,0 +1,222 @@
1
+ import { createContext, createElement, useContext, type ReactElement, type ReactNode } from 'react';
2
+ import {
3
+ assertKnownDescriptorIds,
4
+ createDescriptorCatalog,
5
+ type Descriptor,
6
+ type DescriptorCatalog,
7
+ } from '@lorion-org/composition-graph';
8
+ import {
9
+ collectProviderDefaults,
10
+ collectProviderPreferences,
11
+ resolveItemProviderSelection,
12
+ type ProviderPreferenceMap,
13
+ type ProviderSelectionResolution,
14
+ } from '@lorion-org/provider-selection';
15
+ import { defaultCapabilityRelationDescriptors } from './relations';
16
+
17
+ export {
18
+ createCapabilityCompositionPolicy,
19
+ defaultCapabilityRelationDescriptors,
20
+ defaultCapabilityResolutionRelations,
21
+ } from './relations';
22
+
23
+ export type CapabilityManifest = Descriptor & {
24
+ defaultFor?: string | string[];
25
+ description?: string;
26
+ providerPreferences?: ProviderPreferenceMap;
27
+ };
28
+
29
+ export type ExtensionPoint<T> = {
30
+ id: string;
31
+ readonly value?: T;
32
+ };
33
+
34
+ export type CapabilityContribution<T = unknown> = {
35
+ extensionPoint: ExtensionPoint<T>;
36
+ values: readonly T[];
37
+ };
38
+
39
+ export type RuntimeCapability = {
40
+ id: string;
41
+ manifest: CapabilityManifest;
42
+ contributions?: readonly CapabilityContribution[];
43
+ };
44
+
45
+ export type CapabilityRuntime = {
46
+ capabilities: readonly RuntimeCapability[];
47
+ catalog: DescriptorCatalog;
48
+ getContributions: <T>(extensionPoint: ExtensionPoint<T>) => T[];
49
+ };
50
+
51
+ export type ContributionContract<T> = {
52
+ define: (values: readonly T[]) => CapabilityContribution<T>;
53
+ extensionPoint: ExtensionPoint<T>;
54
+ get: (runtime: CapabilityRuntime) => T[];
55
+ use: () => T[];
56
+ };
57
+
58
+ export function defineExtensionPoint<T>(id: string): ExtensionPoint<T> {
59
+ return { id };
60
+ }
61
+
62
+ export function defineContribution<T>(
63
+ extensionPoint: ExtensionPoint<T>,
64
+ values: readonly T[],
65
+ ): CapabilityContribution<T> {
66
+ return { extensionPoint, values };
67
+ }
68
+
69
+ export function createContributionContract<T>(id: string): ContributionContract<T> {
70
+ const extensionPoint = defineExtensionPoint<T>(id);
71
+
72
+ return {
73
+ extensionPoint,
74
+ define: (values) => defineContribution(extensionPoint, values),
75
+ get: (runtime) => runtime.getContributions(extensionPoint),
76
+ use: () => useCapabilityRuntime().getContributions(extensionPoint),
77
+ };
78
+ }
79
+
80
+ export type CapabilityProviderSelectionOptions = {
81
+ configuredProviders?: ProviderPreferenceMap;
82
+ fallbackProviders?: ProviderPreferenceMap;
83
+ selectedProviders?: ProviderPreferenceMap;
84
+ };
85
+
86
+ export function getCapabilityProviderSelection(
87
+ runtime: CapabilityRuntime,
88
+ options: CapabilityProviderSelectionOptions = {},
89
+ ): ProviderSelectionResolution {
90
+ const descriptors = runtime.catalog.getAllDescriptors();
91
+ const descriptorPreferences = collectProviderPreferences({
92
+ items: descriptors,
93
+ getProviderPreferences: (descriptor) => descriptor.providerPreferences,
94
+ });
95
+ const providerDefaults = collectProviderDefaults({
96
+ items: descriptors,
97
+ getDefaultFor: (descriptor) => descriptor.defaultFor,
98
+ getProviderId: (descriptor) => descriptor.id,
99
+ });
100
+ const configuredProviders = options.configuredProviders ?? {};
101
+ const selectedProviders = options.selectedProviders ?? {};
102
+ const fallbackProviders = {
103
+ ...providerDefaults,
104
+ ...descriptorPreferences,
105
+ ...(options.fallbackProviders ?? {}),
106
+ };
107
+ const resolution = resolveItemProviderSelection({
108
+ items: descriptors,
109
+ getCapabilityId: (descriptor) => descriptor.providesFor,
110
+ getProviderId: (descriptor) => descriptor.id,
111
+ configuredProviders,
112
+ fallbackProviders,
113
+ selectedProviders,
114
+ });
115
+
116
+ return {
117
+ excludedProviderIds: resolution.excludedProviderIds,
118
+ mismatches: resolution.mismatches,
119
+ selections: resolution.selections,
120
+ };
121
+ }
122
+
123
+ export function defineCapability(capability: RuntimeCapability): RuntimeCapability {
124
+ if (capability.id !== capability.manifest.id) {
125
+ throw new Error(
126
+ `Capability "${capability.id}" must match capability.json id "${capability.manifest.id}".`,
127
+ );
128
+ }
129
+
130
+ return capability;
131
+ }
132
+
133
+ export function createCapabilityRuntime(
134
+ capabilities: readonly RuntimeCapability[],
135
+ ): CapabilityRuntime {
136
+ const enabledCapabilities = capabilities.filter(
137
+ (capability) => capability.manifest.disabled !== true,
138
+ );
139
+ const catalog = createCapabilityCatalog(enabledCapabilities);
140
+
141
+ assertUniqueCapabilities(enabledCapabilities);
142
+ assertKnownCapabilityDependencies(enabledCapabilities, catalog);
143
+
144
+ const contributions = collectContributions(enabledCapabilities);
145
+
146
+ return {
147
+ capabilities: Object.freeze([...enabledCapabilities]),
148
+ catalog,
149
+ getContributions: <T>(extensionPoint: ExtensionPoint<T>) =>
150
+ [...(contributions.get(extensionPoint.id) ?? [])] as T[],
151
+ };
152
+ }
153
+
154
+ const CapabilityRuntimeContext = createContext<CapabilityRuntime | null>(null);
155
+
156
+ export type CapabilityRuntimeProviderProps = {
157
+ children: ReactNode;
158
+ runtime: CapabilityRuntime;
159
+ };
160
+
161
+ export function CapabilityRuntimeProvider({
162
+ children,
163
+ runtime,
164
+ }: CapabilityRuntimeProviderProps): ReactElement {
165
+ return createElement(CapabilityRuntimeContext.Provider, { value: runtime }, children);
166
+ }
167
+
168
+ export function useCapabilityRuntime(): CapabilityRuntime {
169
+ const runtime = useContext(CapabilityRuntimeContext);
170
+
171
+ if (!runtime) {
172
+ throw new Error('useCapabilityRuntime must be used inside CapabilityRuntimeProvider.');
173
+ }
174
+
175
+ return runtime;
176
+ }
177
+
178
+ function assertUniqueCapabilities(capabilities: readonly RuntimeCapability[]): void {
179
+ const seen = new Set<string>();
180
+
181
+ for (const capability of capabilities) {
182
+ if (seen.has(capability.id)) {
183
+ throw new Error(`Duplicate capability id "${capability.id}".`);
184
+ }
185
+
186
+ seen.add(capability.id);
187
+ }
188
+ }
189
+
190
+ function assertKnownCapabilityDependencies(
191
+ capabilities: readonly RuntimeCapability[],
192
+ catalog: DescriptorCatalog,
193
+ ): void {
194
+ for (const capability of capabilities) {
195
+ assertKnownDescriptorIds(
196
+ catalog.getDescriptorMap(),
197
+ Object.keys(capability.manifest.dependencies ?? {}),
198
+ `capability "${capability.id}" dependencies`,
199
+ );
200
+ }
201
+ }
202
+
203
+ function collectContributions(capabilities: readonly RuntimeCapability[]): Map<string, unknown[]> {
204
+ const contributions = new Map<string, unknown[]>();
205
+
206
+ for (const capability of capabilities) {
207
+ for (const contribution of capability.contributions ?? []) {
208
+ const bucket = contributions.get(contribution.extensionPoint.id) ?? [];
209
+ bucket.push(...contribution.values);
210
+ contributions.set(contribution.extensionPoint.id, bucket);
211
+ }
212
+ }
213
+
214
+ return contributions;
215
+ }
216
+
217
+ function createCapabilityCatalog(capabilities: readonly RuntimeCapability[]): DescriptorCatalog {
218
+ return createDescriptorCatalog({
219
+ descriptors: capabilities.map((capability) => capability.manifest),
220
+ relationDescriptors: defaultCapabilityRelationDescriptors,
221
+ });
222
+ }
@@ -0,0 +1,37 @@
1
+ import type { CompositionPolicy, RelationDescriptor } from '@lorion-org/composition-graph';
2
+
3
+ export const defaultCapabilityResolutionRelations = [
4
+ 'dependencies',
5
+ 'defaultProviders',
6
+ 'providerPreferences',
7
+ ] as const;
8
+
9
+ export const defaultCapabilityRelationDescriptors: RelationDescriptor[] = [
10
+ {
11
+ direction: 'incoming',
12
+ field: 'defaultFor',
13
+ id: 'defaultProviders',
14
+ },
15
+ {
16
+ field: 'providerPreferences',
17
+ id: 'providerPreferences',
18
+ targetMode: 'values',
19
+ },
20
+ ];
21
+
22
+ export function createCapabilityCompositionPolicy(
23
+ policy?: Partial<CompositionPolicy>,
24
+ ): Partial<CompositionPolicy> {
25
+ return {
26
+ ...policy,
27
+ inspectionRelationIds: policy?.inspectionRelationIds ?? [
28
+ ...defaultCapabilityResolutionRelations,
29
+ ],
30
+ provenanceRelationIds: policy?.provenanceRelationIds ?? [
31
+ ...defaultCapabilityResolutionRelations,
32
+ ],
33
+ resolutionRelationIds: policy?.resolutionRelationIds ?? [
34
+ ...defaultCapabilityResolutionRelations,
35
+ ],
36
+ };
37
+ }