@lorion-org/provider-selection 1.0.0-beta.0 → 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/LICENSE +21 -21
- package/README.md +123 -51
- package/dist/index.cjs +96 -11
- package/dist/index.d.cts +31 -3
- package/dist/index.d.ts +31 -3
- package/dist/index.js +91 -10
- package/examples/payment-checkout.ts +38 -0
- package/examples/provider-preferences.ts +61 -0
- package/package.json +8 -2
- package/src/index.ts +355 -0
- package/examples/command-handlers.ts +0 -45
- package/examples/storage-drivers.ts +0 -39
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the 'Software'), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the 'Software'), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
`@lorion-org/provider-selection` is a small framework-free core for selecting one
|
|
4
4
|
provider per capability from multiple candidates.
|
|
5
5
|
|
|
6
|
-
It solves
|
|
6
|
+
It solves these things:
|
|
7
7
|
|
|
8
8
|
- collect provider candidates by capability
|
|
9
|
+
- collect provider-owned defaults from descriptors
|
|
10
|
+
- collect provider preferences from descriptors or config-like records
|
|
11
|
+
- collect explicit provider preferences from selected provider ids
|
|
9
12
|
- optionally collect and resolve in one call
|
|
10
13
|
- pick one provider with configured and fallback preferences
|
|
11
14
|
- report misconfigured provider selections
|
|
@@ -14,17 +17,20 @@ It solves four things:
|
|
|
14
17
|
Selection order is always:
|
|
15
18
|
|
|
16
19
|
1. configured provider
|
|
17
|
-
2.
|
|
18
|
-
3.
|
|
20
|
+
2. explicitly selected provider
|
|
21
|
+
3. fallback provider
|
|
22
|
+
4. first provider in deterministic sort order
|
|
19
23
|
|
|
20
24
|
If a configured provider is set but not present among the candidates, the package
|
|
21
25
|
does not silently fall back. It reports a mismatch and leaves that capability
|
|
22
26
|
unselected.
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
Playground examples in this repository:
|
|
25
29
|
|
|
26
|
-
- `
|
|
27
|
-
- `
|
|
30
|
+
- `packages/nuxt/playground/layer-extensions/payment-provider-stripe/extension.json`
|
|
31
|
+
- `packages/nuxt/playground/layer-extensions/payment-provider-invoice/extension.json`
|
|
32
|
+
- `packages/react/playground/capabilities/payment-provider-stripe/capability.json`
|
|
33
|
+
- `packages/react/playground/capabilities/payment-provider-invoice/capability.json`
|
|
28
34
|
|
|
29
35
|
It does not know anything about:
|
|
30
36
|
|
|
@@ -42,23 +48,77 @@ pnpm add @lorion-org/provider-selection
|
|
|
42
48
|
|
|
43
49
|
## Example
|
|
44
50
|
|
|
51
|
+
Use provider-owned defaults when a provider descriptor should decide the normal
|
|
52
|
+
provider for a capability:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import {
|
|
56
|
+
collectProviderDefaults,
|
|
57
|
+
resolveItemProviderSelection,
|
|
58
|
+
} from '@lorion-org/provider-selection';
|
|
59
|
+
|
|
60
|
+
const descriptors = [
|
|
61
|
+
{ id: 'payment-provider-stripe', providesFor: 'checkout', defaultFor: 'checkout' },
|
|
62
|
+
{ id: 'payment-provider-invoice', providesFor: 'checkout' },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const fallbackProviders = collectProviderDefaults({
|
|
66
|
+
items: descriptors,
|
|
67
|
+
getDefaultFor: (item) => item.defaultFor,
|
|
68
|
+
getProviderId: (item) => item.id,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const result = resolveItemProviderSelection({
|
|
72
|
+
items: descriptors,
|
|
73
|
+
getCapabilityId: (item) => item.providesFor,
|
|
74
|
+
getProviderId: (item) => item.id,
|
|
75
|
+
fallbackProviders,
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This selects Stripe with mode `fallback`. `providesFor` and `defaultFor` both
|
|
80
|
+
accept a string or string array, so one provider can serve multiple capabilities.
|
|
81
|
+
|
|
82
|
+
Use explicit selected providers for seed-owned overrides. This is useful when a
|
|
83
|
+
host app lets users select descriptors through a normal feature or profile seed,
|
|
84
|
+
and a selected descriptor is also a provider for a capability:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import {
|
|
88
|
+
collectSelectedProviderPreferences,
|
|
89
|
+
resolveItemProviderSelection,
|
|
90
|
+
} from '@lorion-org/provider-selection';
|
|
91
|
+
|
|
92
|
+
const selectedProviders = collectSelectedProviderPreferences({
|
|
93
|
+
items: descriptors,
|
|
94
|
+
getCapabilityId: (descriptor) => descriptor.providesFor,
|
|
95
|
+
getProviderId: (descriptor) => descriptor.id,
|
|
96
|
+
selectedProviderIds: ['payment-provider-invoice'],
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const result = resolveItemProviderSelection({
|
|
100
|
+
items: descriptors,
|
|
101
|
+
getCapabilityId: (descriptor) => descriptor.providesFor,
|
|
102
|
+
getProviderId: (descriptor) => descriptor.id,
|
|
103
|
+
fallbackProviders,
|
|
104
|
+
selectedProviders,
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This selects Invoice with mode `selected`, even though Stripe owns the fallback
|
|
109
|
+
default. Configured providers still have higher priority.
|
|
110
|
+
|
|
111
|
+
Use configured providers for deployment-owned overrides:
|
|
112
|
+
|
|
45
113
|
```ts
|
|
46
114
|
import { resolveItemProviderSelection } from '@lorion-org/provider-selection';
|
|
47
115
|
|
|
48
116
|
const result = resolveItemProviderSelection({
|
|
49
|
-
items:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
getCapabilityId: (item) => item.capability,
|
|
55
|
-
getProviderId: (item) => item.providerId,
|
|
56
|
-
configuredProviders: {
|
|
57
|
-
auth: 'keycloak',
|
|
58
|
-
},
|
|
59
|
-
fallbackProviders: {
|
|
60
|
-
mailer: 'mailer-postmark',
|
|
61
|
-
},
|
|
117
|
+
items: descriptors,
|
|
118
|
+
getCapabilityId: (item) => item.providesFor,
|
|
119
|
+
getProviderId: (item) => item.id,
|
|
120
|
+
configuredProviders: { checkout: 'payment-provider-invoice' },
|
|
121
|
+
fallbackProviders,
|
|
62
122
|
});
|
|
63
123
|
|
|
64
124
|
result.selections;
|
|
@@ -74,51 +134,46 @@ resolver directly:
|
|
|
74
134
|
import { resolveProviderSelection } from '@lorion-org/provider-selection';
|
|
75
135
|
|
|
76
136
|
const result = resolveProviderSelection({
|
|
77
|
-
providersByCapability: new Map([
|
|
137
|
+
providersByCapability: new Map([
|
|
138
|
+
['checkout', ['payment-provider-invoice', 'payment-provider-stripe']],
|
|
139
|
+
]),
|
|
78
140
|
configuredProviders: {
|
|
79
|
-
|
|
141
|
+
checkout: 'missing-provider',
|
|
80
142
|
},
|
|
81
143
|
});
|
|
82
144
|
|
|
83
145
|
result.selections;
|
|
84
146
|
result.mismatches;
|
|
85
|
-
// => [{ capabilityId: '
|
|
147
|
+
// => [{ capabilityId: 'checkout', configuredProviderId: 'missing-provider' }]
|
|
86
148
|
```
|
|
87
149
|
|
|
88
|
-
|
|
150
|
+
If profile or descriptor preferences are stored on descriptors, collect them as
|
|
151
|
+
fallback preferences before resolving:
|
|
89
152
|
|
|
90
153
|
```ts
|
|
91
|
-
import {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
getProviderId: (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
154
|
+
import {
|
|
155
|
+
collectProviderDefaults,
|
|
156
|
+
collectProviderPreferences,
|
|
157
|
+
resolveItemProviderSelection,
|
|
158
|
+
} from '@lorion-org/provider-selection';
|
|
159
|
+
|
|
160
|
+
const providerDefaults = collectProviderDefaults({
|
|
161
|
+
items: descriptors,
|
|
162
|
+
getDefaultFor: (descriptor) => descriptor.defaultFor,
|
|
163
|
+
getProviderId: (descriptor) => descriptor.id,
|
|
164
|
+
});
|
|
165
|
+
const descriptorPreferences = collectProviderPreferences({
|
|
166
|
+
items: descriptors,
|
|
167
|
+
getProviderPreferences: (descriptor) => descriptor.providerPreferences,
|
|
104
168
|
});
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## Example: storage drivers
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
import { resolveItemProviderSelection } from '@lorion-org/provider-selection';
|
|
111
169
|
|
|
112
170
|
const result = resolveItemProviderSelection({
|
|
113
|
-
items:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
{ storageKind: 'queue', driverId: 'redis-streams' },
|
|
117
|
-
],
|
|
118
|
-
getCapabilityId: (item) => item.storageKind,
|
|
119
|
-
getProviderId: (item) => item.driverId,
|
|
171
|
+
items: descriptors,
|
|
172
|
+
getCapabilityId: (descriptor) => descriptor.providesFor,
|
|
173
|
+
getProviderId: (descriptor) => descriptor.id,
|
|
120
174
|
fallbackProviders: {
|
|
121
|
-
|
|
175
|
+
...providerDefaults,
|
|
176
|
+
...descriptorPreferences,
|
|
122
177
|
},
|
|
123
178
|
});
|
|
124
179
|
```
|
|
@@ -128,9 +183,22 @@ const result = resolveItemProviderSelection({
|
|
|
128
183
|
```ts
|
|
129
184
|
type CapabilityId = string;
|
|
130
185
|
type ProviderId = string;
|
|
131
|
-
type ProviderSelectionMode = 'configured' | 'fallback' | 'first';
|
|
186
|
+
type ProviderSelectionMode = 'configured' | 'selected' | 'fallback' | 'first';
|
|
132
187
|
type ProviderPreferenceMap = Partial<Record<CapabilityId, ProviderId>>;
|
|
133
188
|
type ProvidersByCapability = Map<CapabilityId, ProviderId[]>;
|
|
189
|
+
type ProviderCollectionInput<T> = {
|
|
190
|
+
items: Iterable<T>;
|
|
191
|
+
getCapabilityId: (item: T) => unknown;
|
|
192
|
+
getProviderId: (item: T) => ProviderId;
|
|
193
|
+
};
|
|
194
|
+
type SelectedProviderPreferenceCollectionInput<T> = ProviderCollectionInput<T> & {
|
|
195
|
+
selectedProviderIds: Iterable<ProviderId>;
|
|
196
|
+
};
|
|
197
|
+
type ProviderDefaultCollectionInput<T> = {
|
|
198
|
+
items: Iterable<T>;
|
|
199
|
+
getDefaultFor: (item: T) => unknown;
|
|
200
|
+
getProviderId: (item: T) => ProviderId;
|
|
201
|
+
};
|
|
134
202
|
|
|
135
203
|
type ProviderSelection = {
|
|
136
204
|
capabilityId: CapabilityId;
|
|
@@ -157,8 +225,12 @@ type ItemProviderSelectionResolution = ProviderSelectionResolution & {
|
|
|
157
225
|
|
|
158
226
|
The package exposes:
|
|
159
227
|
|
|
228
|
+
- `collectProviderDefaults()`
|
|
229
|
+
- `collectProviderPreferences()`
|
|
160
230
|
- `collectProvidersByCapability()`
|
|
231
|
+
- `collectSelectedProviderPreferences()`
|
|
161
232
|
- `resolveItemProviderSelection()`
|
|
233
|
+
- `resolveSelectedProviderRelationPreferences()`
|
|
162
234
|
- `resolveProviderSelection()`
|
|
163
235
|
|
|
164
236
|
## Local commands
|
package/dist/index.cjs
CHANGED
|
@@ -20,14 +20,25 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
collectProviderDefaults: () => collectProviderDefaults,
|
|
24
|
+
collectProviderPreferences: () => collectProviderPreferences,
|
|
23
25
|
collectProvidersByCapability: () => collectProvidersByCapability,
|
|
26
|
+
collectSelectedProviderPreferences: () => collectSelectedProviderPreferences,
|
|
24
27
|
resolveItemProviderSelection: () => resolveItemProviderSelection,
|
|
25
|
-
resolveProviderSelection: () => resolveProviderSelection
|
|
28
|
+
resolveProviderSelection: () => resolveProviderSelection,
|
|
29
|
+
resolveSelectedProviderRelationPreferences: () => resolveSelectedProviderRelationPreferences
|
|
26
30
|
});
|
|
27
31
|
module.exports = __toCommonJS(index_exports);
|
|
28
32
|
function toSortedUniqueProviderIds(providerIds) {
|
|
29
33
|
return Array.from(new Set(Array.from(providerIds).filter(Boolean))).sort();
|
|
30
34
|
}
|
|
35
|
+
function toCapabilityIds(value) {
|
|
36
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
37
|
+
return entries.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
38
|
+
}
|
|
39
|
+
function isRecord(value) {
|
|
40
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
|
+
}
|
|
31
42
|
function getSelectedProvider(input) {
|
|
32
43
|
const firstProviderId = input.candidateProviderIds[0];
|
|
33
44
|
if (!firstProviderId) {
|
|
@@ -43,6 +54,13 @@ function getSelectedProvider(input) {
|
|
|
43
54
|
}
|
|
44
55
|
return void 0;
|
|
45
56
|
}
|
|
57
|
+
const selectedProviderId = input.selectedProviders?.[input.capabilityId];
|
|
58
|
+
if (selectedProviderId && input.candidateProviderIds.includes(selectedProviderId)) {
|
|
59
|
+
return {
|
|
60
|
+
selectedProviderId,
|
|
61
|
+
mode: "selected"
|
|
62
|
+
};
|
|
63
|
+
}
|
|
46
64
|
const fallbackProviderId = input.fallbackProviders?.[input.capabilityId];
|
|
47
65
|
if (fallbackProviderId && input.candidateProviderIds.includes(fallbackProviderId)) {
|
|
48
66
|
return {
|
|
@@ -65,7 +83,8 @@ function selectProviders(input) {
|
|
|
65
83
|
capabilityId,
|
|
66
84
|
candidateProviderIds: normalizedCandidateProviderIds,
|
|
67
85
|
...input.configuredProviders ? { configuredProviders: input.configuredProviders } : {},
|
|
68
|
-
...input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {}
|
|
86
|
+
...input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {},
|
|
87
|
+
...input.selectedProviders ? { selectedProviders: input.selectedProviders } : {}
|
|
69
88
|
});
|
|
70
89
|
if (!selected) {
|
|
71
90
|
continue;
|
|
@@ -120,17 +139,78 @@ function getExcludedProviders(selections) {
|
|
|
120
139
|
function collectProvidersByCapability(input) {
|
|
121
140
|
const providersByCapability = /* @__PURE__ */ new Map();
|
|
122
141
|
for (const item of input.items) {
|
|
123
|
-
const capabilityId = input.getCapabilityId(item);
|
|
124
|
-
if (!capabilityId) {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
142
|
const providerId = input.getProviderId(item);
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
const capabilityIds = toCapabilityIds(input.getCapabilityId(item));
|
|
144
|
+
for (const capabilityId of capabilityIds) {
|
|
145
|
+
const currentProviderIds = providersByCapability.get(capabilityId) ?? [];
|
|
146
|
+
currentProviderIds.push(providerId);
|
|
147
|
+
providersByCapability.set(capabilityId, toSortedUniqueProviderIds(currentProviderIds));
|
|
148
|
+
}
|
|
131
149
|
}
|
|
132
150
|
return providersByCapability;
|
|
133
151
|
}
|
|
152
|
+
function collectProviderPreferences(input) {
|
|
153
|
+
let preferences = {};
|
|
154
|
+
for (const item of input.items) {
|
|
155
|
+
const value = input.getProviderPreferences(item);
|
|
156
|
+
if (!isRecord(value)) continue;
|
|
157
|
+
preferences = {
|
|
158
|
+
...preferences,
|
|
159
|
+
...Object.fromEntries(
|
|
160
|
+
Object.entries(value).filter(
|
|
161
|
+
(entry) => typeof entry[1] === "string" && entry[1].length > 0
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return preferences;
|
|
167
|
+
}
|
|
168
|
+
function normalizeDefaultFor(value) {
|
|
169
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
170
|
+
return entries.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
171
|
+
}
|
|
172
|
+
function collectProviderDefaults(input) {
|
|
173
|
+
let defaults = {};
|
|
174
|
+
for (const item of input.items) {
|
|
175
|
+
const providerId = input.getProviderId(item);
|
|
176
|
+
const capabilityIds = normalizeDefaultFor(input.getDefaultFor(item));
|
|
177
|
+
defaults = {
|
|
178
|
+
...defaults,
|
|
179
|
+
...Object.fromEntries(capabilityIds.map((capabilityId) => [capabilityId, providerId]))
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return defaults;
|
|
183
|
+
}
|
|
184
|
+
function collectSelectedProviderPreferences(input) {
|
|
185
|
+
const selectedProviderIds = new Set(input.selectedProviderIds);
|
|
186
|
+
const selectedItems = Array.from(input.items).filter((item) => selectedProviderIds.has(input.getProviderId(item))).sort((left, right) => input.getProviderId(left).localeCompare(input.getProviderId(right)));
|
|
187
|
+
const preferences = {};
|
|
188
|
+
for (const item of selectedItems) {
|
|
189
|
+
const providerId = input.getProviderId(item);
|
|
190
|
+
for (const capabilityId of toCapabilityIds(input.getCapabilityId(item))) {
|
|
191
|
+
if (!preferences[capabilityId]) {
|
|
192
|
+
preferences[capabilityId] = providerId;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return preferences;
|
|
197
|
+
}
|
|
198
|
+
function resolveSelectedProviderRelationPreferences(input) {
|
|
199
|
+
const selectedProviders = input.selectedProviders ?? {};
|
|
200
|
+
const selectedCapabilityIds = new Set(Object.keys(selectedProviders));
|
|
201
|
+
const defaultFor = toCapabilityIds(input.defaultFor).filter(
|
|
202
|
+
(capabilityId) => !selectedProviders[capabilityId] || selectedProviders[capabilityId] === input.providerId
|
|
203
|
+
);
|
|
204
|
+
const providerPreferences = Object.fromEntries(
|
|
205
|
+
Object.entries(input.providerPreferences ?? {}).filter(
|
|
206
|
+
([capabilityId]) => !selectedCapabilityIds.has(capabilityId)
|
|
207
|
+
)
|
|
208
|
+
);
|
|
209
|
+
return {
|
|
210
|
+
...defaultFor.length ? { defaultFor: Array.isArray(input.defaultFor) ? defaultFor : defaultFor[0] } : {},
|
|
211
|
+
...Object.keys(providerPreferences).length ? { providerPreferences } : {}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
134
214
|
function resolveProviderSelection(input) {
|
|
135
215
|
const selections = selectProviders(input);
|
|
136
216
|
const mismatches = findConfiguredProviderMismatches(input);
|
|
@@ -146,7 +226,8 @@ function resolveItemProviderSelection(input) {
|
|
|
146
226
|
const resolution = resolveProviderSelection({
|
|
147
227
|
providersByCapability,
|
|
148
228
|
...input.configuredProviders ? { configuredProviders: input.configuredProviders } : {},
|
|
149
|
-
...input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {}
|
|
229
|
+
...input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {},
|
|
230
|
+
...input.selectedProviders ? { selectedProviders: input.selectedProviders } : {}
|
|
150
231
|
});
|
|
151
232
|
return {
|
|
152
233
|
providersByCapability,
|
|
@@ -155,7 +236,11 @@ function resolveItemProviderSelection(input) {
|
|
|
155
236
|
}
|
|
156
237
|
// Annotate the CommonJS export names for ESM import in node:
|
|
157
238
|
0 && (module.exports = {
|
|
239
|
+
collectProviderDefaults,
|
|
240
|
+
collectProviderPreferences,
|
|
158
241
|
collectProvidersByCapability,
|
|
242
|
+
collectSelectedProviderPreferences,
|
|
159
243
|
resolveItemProviderSelection,
|
|
160
|
-
resolveProviderSelection
|
|
244
|
+
resolveProviderSelection,
|
|
245
|
+
resolveSelectedProviderRelationPreferences
|
|
161
246
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
type CapabilityId = string;
|
|
2
2
|
type ProviderId = string;
|
|
3
|
-
type ProviderSelectionMode = 'configured' | 'fallback' | 'first';
|
|
3
|
+
type ProviderSelectionMode = 'configured' | 'selected' | 'fallback' | 'first';
|
|
4
4
|
type ProviderPreferenceMap = Partial<Record<CapabilityId, ProviderId>>;
|
|
5
|
+
type ProviderPreferenceCollectionInput<T> = {
|
|
6
|
+
items: Iterable<T>;
|
|
7
|
+
getProviderPreferences: (item: T) => unknown;
|
|
8
|
+
};
|
|
9
|
+
type ProviderDefaultCollectionInput<T> = {
|
|
10
|
+
items: Iterable<T>;
|
|
11
|
+
getDefaultFor: (item: T) => unknown;
|
|
12
|
+
getProviderId: (item: T) => ProviderId;
|
|
13
|
+
};
|
|
14
|
+
type SelectedProviderPreferenceCollectionInput<T> = ProviderCollectionInput<T> & {
|
|
15
|
+
selectedProviderIds: Iterable<ProviderId>;
|
|
16
|
+
};
|
|
17
|
+
type SelectedProviderRelationPreferenceInput = {
|
|
18
|
+
defaultFor?: CapabilityId | CapabilityId[] | undefined;
|
|
19
|
+
providerId: ProviderId;
|
|
20
|
+
providerPreferences?: ProviderPreferenceMap | undefined;
|
|
21
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
22
|
+
};
|
|
23
|
+
type SelectedProviderRelationPreferences = {
|
|
24
|
+
defaultFor?: CapabilityId | CapabilityId[];
|
|
25
|
+
providerPreferences?: ProviderPreferenceMap;
|
|
26
|
+
};
|
|
5
27
|
type ProviderSelection = {
|
|
6
28
|
capabilityId: CapabilityId;
|
|
7
29
|
selectedProviderId: ProviderId;
|
|
@@ -23,20 +45,26 @@ type ItemProviderSelectionResolution = ProviderSelectionResolution & {
|
|
|
23
45
|
};
|
|
24
46
|
type ProviderCollectionInput<T> = {
|
|
25
47
|
items: Iterable<T>;
|
|
26
|
-
getCapabilityId: (item: T) => CapabilityId | undefined;
|
|
48
|
+
getCapabilityId: (item: T) => CapabilityId | CapabilityId[] | undefined;
|
|
27
49
|
getProviderId: (item: T) => ProviderId;
|
|
28
50
|
};
|
|
29
51
|
type ResolveProviderSelectionInput = {
|
|
30
52
|
providersByCapability: ProvidersByCapability;
|
|
31
53
|
configuredProviders?: ProviderPreferenceMap;
|
|
32
54
|
fallbackProviders?: ProviderPreferenceMap;
|
|
55
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
33
56
|
};
|
|
34
57
|
type ResolveItemProviderSelectionInput<T> = ProviderCollectionInput<T> & {
|
|
35
58
|
configuredProviders?: ProviderPreferenceMap;
|
|
36
59
|
fallbackProviders?: ProviderPreferenceMap;
|
|
60
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
37
61
|
};
|
|
38
62
|
declare function collectProvidersByCapability<T>(input: ProviderCollectionInput<T>): ProvidersByCapability;
|
|
63
|
+
declare function collectProviderPreferences<T>(input: ProviderPreferenceCollectionInput<T>): ProviderPreferenceMap;
|
|
64
|
+
declare function collectProviderDefaults<T>(input: ProviderDefaultCollectionInput<T>): ProviderPreferenceMap;
|
|
65
|
+
declare function collectSelectedProviderPreferences<T>(input: SelectedProviderPreferenceCollectionInput<T>): ProviderPreferenceMap;
|
|
66
|
+
declare function resolveSelectedProviderRelationPreferences(input: SelectedProviderRelationPreferenceInput): SelectedProviderRelationPreferences;
|
|
39
67
|
declare function resolveProviderSelection(input: ResolveProviderSelectionInput): ProviderSelectionResolution;
|
|
40
68
|
declare function resolveItemProviderSelection<T>(input: ResolveItemProviderSelectionInput<T>): ItemProviderSelectionResolution;
|
|
41
69
|
|
|
42
|
-
export { type CapabilityId, type ItemProviderSelectionResolution, type ProviderId, type ProviderMismatch, type ProviderPreferenceMap, type ProviderSelection, type ProviderSelectionMode, type ProviderSelectionResolution, type ProvidersByCapability, type ResolveItemProviderSelectionInput, type ResolveProviderSelectionInput, collectProvidersByCapability, resolveItemProviderSelection, resolveProviderSelection };
|
|
70
|
+
export { type CapabilityId, type ItemProviderSelectionResolution, type ProviderDefaultCollectionInput, type ProviderId, type ProviderMismatch, type ProviderPreferenceCollectionInput, type ProviderPreferenceMap, type ProviderSelection, type ProviderSelectionMode, type ProviderSelectionResolution, type ProvidersByCapability, type ResolveItemProviderSelectionInput, type ResolveProviderSelectionInput, type SelectedProviderPreferenceCollectionInput, type SelectedProviderRelationPreferenceInput, type SelectedProviderRelationPreferences, collectProviderDefaults, collectProviderPreferences, collectProvidersByCapability, collectSelectedProviderPreferences, resolveItemProviderSelection, resolveProviderSelection, resolveSelectedProviderRelationPreferences };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
type CapabilityId = string;
|
|
2
2
|
type ProviderId = string;
|
|
3
|
-
type ProviderSelectionMode = 'configured' | 'fallback' | 'first';
|
|
3
|
+
type ProviderSelectionMode = 'configured' | 'selected' | 'fallback' | 'first';
|
|
4
4
|
type ProviderPreferenceMap = Partial<Record<CapabilityId, ProviderId>>;
|
|
5
|
+
type ProviderPreferenceCollectionInput<T> = {
|
|
6
|
+
items: Iterable<T>;
|
|
7
|
+
getProviderPreferences: (item: T) => unknown;
|
|
8
|
+
};
|
|
9
|
+
type ProviderDefaultCollectionInput<T> = {
|
|
10
|
+
items: Iterable<T>;
|
|
11
|
+
getDefaultFor: (item: T) => unknown;
|
|
12
|
+
getProviderId: (item: T) => ProviderId;
|
|
13
|
+
};
|
|
14
|
+
type SelectedProviderPreferenceCollectionInput<T> = ProviderCollectionInput<T> & {
|
|
15
|
+
selectedProviderIds: Iterable<ProviderId>;
|
|
16
|
+
};
|
|
17
|
+
type SelectedProviderRelationPreferenceInput = {
|
|
18
|
+
defaultFor?: CapabilityId | CapabilityId[] | undefined;
|
|
19
|
+
providerId: ProviderId;
|
|
20
|
+
providerPreferences?: ProviderPreferenceMap | undefined;
|
|
21
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
22
|
+
};
|
|
23
|
+
type SelectedProviderRelationPreferences = {
|
|
24
|
+
defaultFor?: CapabilityId | CapabilityId[];
|
|
25
|
+
providerPreferences?: ProviderPreferenceMap;
|
|
26
|
+
};
|
|
5
27
|
type ProviderSelection = {
|
|
6
28
|
capabilityId: CapabilityId;
|
|
7
29
|
selectedProviderId: ProviderId;
|
|
@@ -23,20 +45,26 @@ type ItemProviderSelectionResolution = ProviderSelectionResolution & {
|
|
|
23
45
|
};
|
|
24
46
|
type ProviderCollectionInput<T> = {
|
|
25
47
|
items: Iterable<T>;
|
|
26
|
-
getCapabilityId: (item: T) => CapabilityId | undefined;
|
|
48
|
+
getCapabilityId: (item: T) => CapabilityId | CapabilityId[] | undefined;
|
|
27
49
|
getProviderId: (item: T) => ProviderId;
|
|
28
50
|
};
|
|
29
51
|
type ResolveProviderSelectionInput = {
|
|
30
52
|
providersByCapability: ProvidersByCapability;
|
|
31
53
|
configuredProviders?: ProviderPreferenceMap;
|
|
32
54
|
fallbackProviders?: ProviderPreferenceMap;
|
|
55
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
33
56
|
};
|
|
34
57
|
type ResolveItemProviderSelectionInput<T> = ProviderCollectionInput<T> & {
|
|
35
58
|
configuredProviders?: ProviderPreferenceMap;
|
|
36
59
|
fallbackProviders?: ProviderPreferenceMap;
|
|
60
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
37
61
|
};
|
|
38
62
|
declare function collectProvidersByCapability<T>(input: ProviderCollectionInput<T>): ProvidersByCapability;
|
|
63
|
+
declare function collectProviderPreferences<T>(input: ProviderPreferenceCollectionInput<T>): ProviderPreferenceMap;
|
|
64
|
+
declare function collectProviderDefaults<T>(input: ProviderDefaultCollectionInput<T>): ProviderPreferenceMap;
|
|
65
|
+
declare function collectSelectedProviderPreferences<T>(input: SelectedProviderPreferenceCollectionInput<T>): ProviderPreferenceMap;
|
|
66
|
+
declare function resolveSelectedProviderRelationPreferences(input: SelectedProviderRelationPreferenceInput): SelectedProviderRelationPreferences;
|
|
39
67
|
declare function resolveProviderSelection(input: ResolveProviderSelectionInput): ProviderSelectionResolution;
|
|
40
68
|
declare function resolveItemProviderSelection<T>(input: ResolveItemProviderSelectionInput<T>): ItemProviderSelectionResolution;
|
|
41
69
|
|
|
42
|
-
export { type CapabilityId, type ItemProviderSelectionResolution, type ProviderId, type ProviderMismatch, type ProviderPreferenceMap, type ProviderSelection, type ProviderSelectionMode, type ProviderSelectionResolution, type ProvidersByCapability, type ResolveItemProviderSelectionInput, type ResolveProviderSelectionInput, collectProvidersByCapability, resolveItemProviderSelection, resolveProviderSelection };
|
|
70
|
+
export { type CapabilityId, type ItemProviderSelectionResolution, type ProviderDefaultCollectionInput, type ProviderId, type ProviderMismatch, type ProviderPreferenceCollectionInput, type ProviderPreferenceMap, type ProviderSelection, type ProviderSelectionMode, type ProviderSelectionResolution, type ProvidersByCapability, type ResolveItemProviderSelectionInput, type ResolveProviderSelectionInput, type SelectedProviderPreferenceCollectionInput, type SelectedProviderRelationPreferenceInput, type SelectedProviderRelationPreferences, collectProviderDefaults, collectProviderPreferences, collectProvidersByCapability, collectSelectedProviderPreferences, resolveItemProviderSelection, resolveProviderSelection, resolveSelectedProviderRelationPreferences };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
function toSortedUniqueProviderIds(providerIds) {
|
|
3
3
|
return Array.from(new Set(Array.from(providerIds).filter(Boolean))).sort();
|
|
4
4
|
}
|
|
5
|
+
function toCapabilityIds(value) {
|
|
6
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
7
|
+
return entries.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
8
|
+
}
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
5
12
|
function getSelectedProvider(input) {
|
|
6
13
|
const firstProviderId = input.candidateProviderIds[0];
|
|
7
14
|
if (!firstProviderId) {
|
|
@@ -17,6 +24,13 @@ function getSelectedProvider(input) {
|
|
|
17
24
|
}
|
|
18
25
|
return void 0;
|
|
19
26
|
}
|
|
27
|
+
const selectedProviderId = input.selectedProviders?.[input.capabilityId];
|
|
28
|
+
if (selectedProviderId && input.candidateProviderIds.includes(selectedProviderId)) {
|
|
29
|
+
return {
|
|
30
|
+
selectedProviderId,
|
|
31
|
+
mode: "selected"
|
|
32
|
+
};
|
|
33
|
+
}
|
|
20
34
|
const fallbackProviderId = input.fallbackProviders?.[input.capabilityId];
|
|
21
35
|
if (fallbackProviderId && input.candidateProviderIds.includes(fallbackProviderId)) {
|
|
22
36
|
return {
|
|
@@ -39,7 +53,8 @@ function selectProviders(input) {
|
|
|
39
53
|
capabilityId,
|
|
40
54
|
candidateProviderIds: normalizedCandidateProviderIds,
|
|
41
55
|
...input.configuredProviders ? { configuredProviders: input.configuredProviders } : {},
|
|
42
|
-
...input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {}
|
|
56
|
+
...input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {},
|
|
57
|
+
...input.selectedProviders ? { selectedProviders: input.selectedProviders } : {}
|
|
43
58
|
});
|
|
44
59
|
if (!selected) {
|
|
45
60
|
continue;
|
|
@@ -94,17 +109,78 @@ function getExcludedProviders(selections) {
|
|
|
94
109
|
function collectProvidersByCapability(input) {
|
|
95
110
|
const providersByCapability = /* @__PURE__ */ new Map();
|
|
96
111
|
for (const item of input.items) {
|
|
97
|
-
const capabilityId = input.getCapabilityId(item);
|
|
98
|
-
if (!capabilityId) {
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
112
|
const providerId = input.getProviderId(item);
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
const capabilityIds = toCapabilityIds(input.getCapabilityId(item));
|
|
114
|
+
for (const capabilityId of capabilityIds) {
|
|
115
|
+
const currentProviderIds = providersByCapability.get(capabilityId) ?? [];
|
|
116
|
+
currentProviderIds.push(providerId);
|
|
117
|
+
providersByCapability.set(capabilityId, toSortedUniqueProviderIds(currentProviderIds));
|
|
118
|
+
}
|
|
105
119
|
}
|
|
106
120
|
return providersByCapability;
|
|
107
121
|
}
|
|
122
|
+
function collectProviderPreferences(input) {
|
|
123
|
+
let preferences = {};
|
|
124
|
+
for (const item of input.items) {
|
|
125
|
+
const value = input.getProviderPreferences(item);
|
|
126
|
+
if (!isRecord(value)) continue;
|
|
127
|
+
preferences = {
|
|
128
|
+
...preferences,
|
|
129
|
+
...Object.fromEntries(
|
|
130
|
+
Object.entries(value).filter(
|
|
131
|
+
(entry) => typeof entry[1] === "string" && entry[1].length > 0
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return preferences;
|
|
137
|
+
}
|
|
138
|
+
function normalizeDefaultFor(value) {
|
|
139
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
140
|
+
return entries.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
141
|
+
}
|
|
142
|
+
function collectProviderDefaults(input) {
|
|
143
|
+
let defaults = {};
|
|
144
|
+
for (const item of input.items) {
|
|
145
|
+
const providerId = input.getProviderId(item);
|
|
146
|
+
const capabilityIds = normalizeDefaultFor(input.getDefaultFor(item));
|
|
147
|
+
defaults = {
|
|
148
|
+
...defaults,
|
|
149
|
+
...Object.fromEntries(capabilityIds.map((capabilityId) => [capabilityId, providerId]))
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return defaults;
|
|
153
|
+
}
|
|
154
|
+
function collectSelectedProviderPreferences(input) {
|
|
155
|
+
const selectedProviderIds = new Set(input.selectedProviderIds);
|
|
156
|
+
const selectedItems = Array.from(input.items).filter((item) => selectedProviderIds.has(input.getProviderId(item))).sort((left, right) => input.getProviderId(left).localeCompare(input.getProviderId(right)));
|
|
157
|
+
const preferences = {};
|
|
158
|
+
for (const item of selectedItems) {
|
|
159
|
+
const providerId = input.getProviderId(item);
|
|
160
|
+
for (const capabilityId of toCapabilityIds(input.getCapabilityId(item))) {
|
|
161
|
+
if (!preferences[capabilityId]) {
|
|
162
|
+
preferences[capabilityId] = providerId;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return preferences;
|
|
167
|
+
}
|
|
168
|
+
function resolveSelectedProviderRelationPreferences(input) {
|
|
169
|
+
const selectedProviders = input.selectedProviders ?? {};
|
|
170
|
+
const selectedCapabilityIds = new Set(Object.keys(selectedProviders));
|
|
171
|
+
const defaultFor = toCapabilityIds(input.defaultFor).filter(
|
|
172
|
+
(capabilityId) => !selectedProviders[capabilityId] || selectedProviders[capabilityId] === input.providerId
|
|
173
|
+
);
|
|
174
|
+
const providerPreferences = Object.fromEntries(
|
|
175
|
+
Object.entries(input.providerPreferences ?? {}).filter(
|
|
176
|
+
([capabilityId]) => !selectedCapabilityIds.has(capabilityId)
|
|
177
|
+
)
|
|
178
|
+
);
|
|
179
|
+
return {
|
|
180
|
+
...defaultFor.length ? { defaultFor: Array.isArray(input.defaultFor) ? defaultFor : defaultFor[0] } : {},
|
|
181
|
+
...Object.keys(providerPreferences).length ? { providerPreferences } : {}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
108
184
|
function resolveProviderSelection(input) {
|
|
109
185
|
const selections = selectProviders(input);
|
|
110
186
|
const mismatches = findConfiguredProviderMismatches(input);
|
|
@@ -120,7 +196,8 @@ function resolveItemProviderSelection(input) {
|
|
|
120
196
|
const resolution = resolveProviderSelection({
|
|
121
197
|
providersByCapability,
|
|
122
198
|
...input.configuredProviders ? { configuredProviders: input.configuredProviders } : {},
|
|
123
|
-
...input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {}
|
|
199
|
+
...input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {},
|
|
200
|
+
...input.selectedProviders ? { selectedProviders: input.selectedProviders } : {}
|
|
124
201
|
});
|
|
125
202
|
return {
|
|
126
203
|
providersByCapability,
|
|
@@ -128,7 +205,11 @@ function resolveItemProviderSelection(input) {
|
|
|
128
205
|
};
|
|
129
206
|
}
|
|
130
207
|
export {
|
|
208
|
+
collectProviderDefaults,
|
|
209
|
+
collectProviderPreferences,
|
|
131
210
|
collectProvidersByCapability,
|
|
211
|
+
collectSelectedProviderPreferences,
|
|
132
212
|
resolveItemProviderSelection,
|
|
133
|
-
resolveProviderSelection
|
|
213
|
+
resolveProviderSelection,
|
|
214
|
+
resolveSelectedProviderRelationPreferences
|
|
134
215
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { resolveItemProviderSelection } from '@lorion-org/provider-selection';
|
|
2
|
+
|
|
3
|
+
type PaymentProviderDescriptor = {
|
|
4
|
+
id: string;
|
|
5
|
+
providesFor?: string | string[];
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const descriptors: PaymentProviderDescriptor[] = [
|
|
9
|
+
{
|
|
10
|
+
id: 'payment-provider-stripe',
|
|
11
|
+
providesFor: 'checkout',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 'payment-provider-invoice',
|
|
15
|
+
providesFor: 'checkout',
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const result = resolveItemProviderSelection({
|
|
20
|
+
items: descriptors,
|
|
21
|
+
getCapabilityId: (descriptor) => descriptor.providesFor,
|
|
22
|
+
getProviderId: (descriptor) => descriptor.id,
|
|
23
|
+
configuredProviders: {
|
|
24
|
+
checkout: 'payment-provider-stripe',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
console.log(result.providersByCapability);
|
|
29
|
+
// Map { checkout => ['payment-provider-invoice', 'payment-provider-stripe'] }
|
|
30
|
+
|
|
31
|
+
console.log(result.selections);
|
|
32
|
+
// Map { checkout => { selectedProviderId: 'payment-provider-stripe', mode: 'configured', ... } }
|
|
33
|
+
|
|
34
|
+
console.log(result.mismatches);
|
|
35
|
+
// []
|
|
36
|
+
|
|
37
|
+
console.log(result.excludedProviderIds);
|
|
38
|
+
// ['payment-provider-invoice']
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collectProviderDefaults,
|
|
3
|
+
collectProviderPreferences,
|
|
4
|
+
collectSelectedProviderPreferences,
|
|
5
|
+
resolveItemProviderSelection,
|
|
6
|
+
} from '@lorion-org/provider-selection';
|
|
7
|
+
|
|
8
|
+
type PlaygroundDescriptor = {
|
|
9
|
+
defaultFor?: string | string[];
|
|
10
|
+
id: string;
|
|
11
|
+
providerPreferences?: Record<string, string>;
|
|
12
|
+
providesFor?: string | string[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const descriptors: PlaygroundDescriptor[] = [
|
|
16
|
+
{
|
|
17
|
+
id: 'web',
|
|
18
|
+
providerPreferences: {
|
|
19
|
+
checkout: 'payment-provider-stripe',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'payment-provider-stripe',
|
|
24
|
+
defaultFor: 'checkout',
|
|
25
|
+
providesFor: 'checkout',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'payment-provider-invoice',
|
|
29
|
+
providesFor: 'checkout',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const providerDefaults = collectProviderDefaults({
|
|
34
|
+
items: descriptors,
|
|
35
|
+
getDefaultFor: (descriptor) => descriptor.defaultFor,
|
|
36
|
+
getProviderId: (descriptor) => descriptor.id,
|
|
37
|
+
});
|
|
38
|
+
const descriptorPreferences = collectProviderPreferences({
|
|
39
|
+
items: descriptors,
|
|
40
|
+
getProviderPreferences: (descriptor) => descriptor.providerPreferences,
|
|
41
|
+
});
|
|
42
|
+
const selectedProviders = collectSelectedProviderPreferences({
|
|
43
|
+
items: descriptors,
|
|
44
|
+
getCapabilityId: (descriptor) => descriptor.providesFor,
|
|
45
|
+
getProviderId: (descriptor) => descriptor.id,
|
|
46
|
+
selectedProviderIds: ['payment-provider-invoice'],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const result = resolveItemProviderSelection({
|
|
50
|
+
items: descriptors,
|
|
51
|
+
getCapabilityId: (descriptor) => descriptor.providesFor,
|
|
52
|
+
getProviderId: (descriptor) => descriptor.id,
|
|
53
|
+
fallbackProviders: {
|
|
54
|
+
...providerDefaults,
|
|
55
|
+
...descriptorPreferences,
|
|
56
|
+
},
|
|
57
|
+
selectedProviders,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
console.log(Object.fromEntries(result.selections));
|
|
61
|
+
// { checkout: { selectedProviderId: 'payment-provider-invoice', mode: 'selected', ... } }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lorion-org/provider-selection",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.2",
|
|
4
4
|
"description": "Framework-free capability provider selection primitives.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
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"
|
|
@@ -35,7 +39,9 @@
|
|
|
35
39
|
"files": [
|
|
36
40
|
"dist",
|
|
37
41
|
"examples",
|
|
38
|
-
"LICENSE"
|
|
42
|
+
"LICENSE",
|
|
43
|
+
"src/**/*.ts",
|
|
44
|
+
"!src/**/*.spec.ts"
|
|
39
45
|
],
|
|
40
46
|
"keywords": [
|
|
41
47
|
"provider",
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
export type CapabilityId = string;
|
|
2
|
+
export type ProviderId = string;
|
|
3
|
+
|
|
4
|
+
export type ProviderSelectionMode = 'configured' | 'selected' | 'fallback' | 'first';
|
|
5
|
+
export type ProviderPreferenceMap = Partial<Record<CapabilityId, ProviderId>>;
|
|
6
|
+
export type ProviderPreferenceCollectionInput<T> = {
|
|
7
|
+
items: Iterable<T>;
|
|
8
|
+
getProviderPreferences: (item: T) => unknown;
|
|
9
|
+
};
|
|
10
|
+
export type ProviderDefaultCollectionInput<T> = {
|
|
11
|
+
items: Iterable<T>;
|
|
12
|
+
getDefaultFor: (item: T) => unknown;
|
|
13
|
+
getProviderId: (item: T) => ProviderId;
|
|
14
|
+
};
|
|
15
|
+
export type SelectedProviderPreferenceCollectionInput<T> = ProviderCollectionInput<T> & {
|
|
16
|
+
selectedProviderIds: Iterable<ProviderId>;
|
|
17
|
+
};
|
|
18
|
+
export type SelectedProviderRelationPreferenceInput = {
|
|
19
|
+
defaultFor?: CapabilityId | CapabilityId[] | undefined;
|
|
20
|
+
providerId: ProviderId;
|
|
21
|
+
providerPreferences?: ProviderPreferenceMap | undefined;
|
|
22
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type SelectedProviderRelationPreferences = {
|
|
26
|
+
defaultFor?: CapabilityId | CapabilityId[];
|
|
27
|
+
providerPreferences?: ProviderPreferenceMap;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ProviderSelection = {
|
|
31
|
+
capabilityId: CapabilityId;
|
|
32
|
+
selectedProviderId: ProviderId;
|
|
33
|
+
candidateProviderIds: ProviderId[];
|
|
34
|
+
mode: ProviderSelectionMode;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type ProviderMismatch = {
|
|
38
|
+
capabilityId: CapabilityId;
|
|
39
|
+
configuredProviderId: ProviderId;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type ProvidersByCapability = Map<CapabilityId, ProviderId[]>;
|
|
43
|
+
|
|
44
|
+
export type ProviderSelectionResolution = {
|
|
45
|
+
selections: Map<CapabilityId, ProviderSelection>;
|
|
46
|
+
mismatches: ProviderMismatch[];
|
|
47
|
+
excludedProviderIds: ProviderId[];
|
|
48
|
+
};
|
|
49
|
+
export type ItemProviderSelectionResolution = ProviderSelectionResolution & {
|
|
50
|
+
providersByCapability: ProvidersByCapability;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type ProviderCollectionInput<T> = {
|
|
54
|
+
items: Iterable<T>;
|
|
55
|
+
getCapabilityId: (item: T) => CapabilityId | CapabilityId[] | undefined;
|
|
56
|
+
getProviderId: (item: T) => ProviderId;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type ResolveProviderSelectionInput = {
|
|
60
|
+
providersByCapability: ProvidersByCapability;
|
|
61
|
+
configuredProviders?: ProviderPreferenceMap;
|
|
62
|
+
fallbackProviders?: ProviderPreferenceMap;
|
|
63
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type ResolveItemProviderSelectionInput<T> = ProviderCollectionInput<T> & {
|
|
67
|
+
configuredProviders?: ProviderPreferenceMap;
|
|
68
|
+
fallbackProviders?: ProviderPreferenceMap;
|
|
69
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function toSortedUniqueProviderIds(providerIds: Iterable<ProviderId>): ProviderId[] {
|
|
73
|
+
return Array.from(new Set(Array.from(providerIds).filter(Boolean))).sort();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function toCapabilityIds(value: CapabilityId | CapabilityId[] | undefined): CapabilityId[] {
|
|
77
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
78
|
+
|
|
79
|
+
return entries.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
83
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getSelectedProvider(input: {
|
|
87
|
+
capabilityId: CapabilityId;
|
|
88
|
+
candidateProviderIds: ProviderId[];
|
|
89
|
+
configuredProviders?: ProviderPreferenceMap;
|
|
90
|
+
fallbackProviders?: ProviderPreferenceMap;
|
|
91
|
+
selectedProviders?: ProviderPreferenceMap;
|
|
92
|
+
}): { selectedProviderId: ProviderId; mode: ProviderSelectionMode } | undefined {
|
|
93
|
+
const firstProviderId = input.candidateProviderIds[0];
|
|
94
|
+
if (!firstProviderId) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const configuredProviderId = input.configuredProviders?.[input.capabilityId];
|
|
99
|
+
if (configuredProviderId) {
|
|
100
|
+
if (input.candidateProviderIds.includes(configuredProviderId)) {
|
|
101
|
+
return {
|
|
102
|
+
selectedProviderId: configuredProviderId,
|
|
103
|
+
mode: 'configured',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const selectedProviderId = input.selectedProviders?.[input.capabilityId];
|
|
111
|
+
if (selectedProviderId && input.candidateProviderIds.includes(selectedProviderId)) {
|
|
112
|
+
return {
|
|
113
|
+
selectedProviderId,
|
|
114
|
+
mode: 'selected',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const fallbackProviderId = input.fallbackProviders?.[input.capabilityId];
|
|
119
|
+
if (fallbackProviderId && input.candidateProviderIds.includes(fallbackProviderId)) {
|
|
120
|
+
return {
|
|
121
|
+
selectedProviderId: fallbackProviderId,
|
|
122
|
+
mode: 'fallback',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
selectedProviderId: firstProviderId,
|
|
128
|
+
mode: 'first',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function selectProviders(
|
|
133
|
+
input: ResolveProviderSelectionInput,
|
|
134
|
+
): Map<CapabilityId, ProviderSelection> {
|
|
135
|
+
const selections: Map<CapabilityId, ProviderSelection> = new Map();
|
|
136
|
+
|
|
137
|
+
for (const [capabilityId, candidateProviderIds] of Array.from(
|
|
138
|
+
input.providersByCapability.entries(),
|
|
139
|
+
).sort(([left], [right]) => left.localeCompare(right))) {
|
|
140
|
+
const normalizedCandidateProviderIds = toSortedUniqueProviderIds(candidateProviderIds);
|
|
141
|
+
const selected = getSelectedProvider({
|
|
142
|
+
capabilityId,
|
|
143
|
+
candidateProviderIds: normalizedCandidateProviderIds,
|
|
144
|
+
...(input.configuredProviders ? { configuredProviders: input.configuredProviders } : {}),
|
|
145
|
+
...(input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {}),
|
|
146
|
+
...(input.selectedProviders ? { selectedProviders: input.selectedProviders } : {}),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (!selected) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
selections.set(capabilityId, {
|
|
154
|
+
capabilityId,
|
|
155
|
+
selectedProviderId: selected.selectedProviderId,
|
|
156
|
+
candidateProviderIds: normalizedCandidateProviderIds,
|
|
157
|
+
mode: selected.mode,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return selections;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function findConfiguredProviderMismatches(
|
|
165
|
+
input: ResolveProviderSelectionInput,
|
|
166
|
+
): ProviderMismatch[] {
|
|
167
|
+
const mismatches: ProviderMismatch[] = [];
|
|
168
|
+
|
|
169
|
+
for (const [capabilityId, configuredProviderId] of Object.entries(
|
|
170
|
+
input.configuredProviders ?? {},
|
|
171
|
+
).sort(([left], [right]) => left.localeCompare(right))) {
|
|
172
|
+
if (!configuredProviderId) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const candidateProviderIds = toSortedUniqueProviderIds(
|
|
177
|
+
input.providersByCapability.get(capabilityId) ?? [],
|
|
178
|
+
);
|
|
179
|
+
if (!candidateProviderIds.length) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (candidateProviderIds.includes(configuredProviderId)) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
mismatches.push({
|
|
188
|
+
capabilityId,
|
|
189
|
+
configuredProviderId,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return mismatches;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function getExcludedProviders(selections: Iterable<ProviderSelection>): ProviderId[] {
|
|
197
|
+
const excludedProviderIds: ProviderId[] = [];
|
|
198
|
+
|
|
199
|
+
for (const selection of selections) {
|
|
200
|
+
if (selection.candidateProviderIds.length <= 1) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for (const candidateProviderId of selection.candidateProviderIds) {
|
|
205
|
+
if (candidateProviderId !== selection.selectedProviderId) {
|
|
206
|
+
excludedProviderIds.push(candidateProviderId);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return toSortedUniqueProviderIds(excludedProviderIds);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function collectProvidersByCapability<T>(
|
|
215
|
+
input: ProviderCollectionInput<T>,
|
|
216
|
+
): ProvidersByCapability {
|
|
217
|
+
const providersByCapability: ProvidersByCapability = new Map();
|
|
218
|
+
|
|
219
|
+
for (const item of input.items) {
|
|
220
|
+
const providerId = input.getProviderId(item);
|
|
221
|
+
const capabilityIds = toCapabilityIds(input.getCapabilityId(item));
|
|
222
|
+
|
|
223
|
+
for (const capabilityId of capabilityIds) {
|
|
224
|
+
const currentProviderIds = providersByCapability.get(capabilityId) ?? [];
|
|
225
|
+
currentProviderIds.push(providerId);
|
|
226
|
+
providersByCapability.set(capabilityId, toSortedUniqueProviderIds(currentProviderIds));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return providersByCapability;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function collectProviderPreferences<T>(
|
|
234
|
+
input: ProviderPreferenceCollectionInput<T>,
|
|
235
|
+
): ProviderPreferenceMap {
|
|
236
|
+
let preferences: ProviderPreferenceMap = {};
|
|
237
|
+
|
|
238
|
+
for (const item of input.items) {
|
|
239
|
+
const value = input.getProviderPreferences(item);
|
|
240
|
+
|
|
241
|
+
if (!isRecord(value)) continue;
|
|
242
|
+
|
|
243
|
+
preferences = {
|
|
244
|
+
...preferences,
|
|
245
|
+
...Object.fromEntries(
|
|
246
|
+
Object.entries(value).filter(
|
|
247
|
+
(entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].length > 0,
|
|
248
|
+
),
|
|
249
|
+
),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return preferences;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeDefaultFor(value: unknown): CapabilityId[] {
|
|
257
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
258
|
+
|
|
259
|
+
return entries.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function collectProviderDefaults<T>(
|
|
263
|
+
input: ProviderDefaultCollectionInput<T>,
|
|
264
|
+
): ProviderPreferenceMap {
|
|
265
|
+
let defaults: ProviderPreferenceMap = {};
|
|
266
|
+
|
|
267
|
+
for (const item of input.items) {
|
|
268
|
+
const providerId = input.getProviderId(item);
|
|
269
|
+
const capabilityIds = normalizeDefaultFor(input.getDefaultFor(item));
|
|
270
|
+
|
|
271
|
+
defaults = {
|
|
272
|
+
...defaults,
|
|
273
|
+
...Object.fromEntries(capabilityIds.map((capabilityId) => [capabilityId, providerId])),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return defaults;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function collectSelectedProviderPreferences<T>(
|
|
281
|
+
input: SelectedProviderPreferenceCollectionInput<T>,
|
|
282
|
+
): ProviderPreferenceMap {
|
|
283
|
+
const selectedProviderIds = new Set(input.selectedProviderIds);
|
|
284
|
+
const selectedItems = Array.from(input.items)
|
|
285
|
+
.filter((item) => selectedProviderIds.has(input.getProviderId(item)))
|
|
286
|
+
.sort((left, right) => input.getProviderId(left).localeCompare(input.getProviderId(right)));
|
|
287
|
+
const preferences: ProviderPreferenceMap = {};
|
|
288
|
+
|
|
289
|
+
for (const item of selectedItems) {
|
|
290
|
+
const providerId = input.getProviderId(item);
|
|
291
|
+
|
|
292
|
+
for (const capabilityId of toCapabilityIds(input.getCapabilityId(item))) {
|
|
293
|
+
if (!preferences[capabilityId]) {
|
|
294
|
+
preferences[capabilityId] = providerId;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return preferences;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function resolveSelectedProviderRelationPreferences(
|
|
303
|
+
input: SelectedProviderRelationPreferenceInput,
|
|
304
|
+
): SelectedProviderRelationPreferences {
|
|
305
|
+
const selectedProviders = input.selectedProviders ?? {};
|
|
306
|
+
const selectedCapabilityIds = new Set(Object.keys(selectedProviders));
|
|
307
|
+
const defaultFor = toCapabilityIds(input.defaultFor).filter(
|
|
308
|
+
(capabilityId) =>
|
|
309
|
+
!selectedProviders[capabilityId] || selectedProviders[capabilityId] === input.providerId,
|
|
310
|
+
);
|
|
311
|
+
const providerPreferences = Object.fromEntries(
|
|
312
|
+
Object.entries(input.providerPreferences ?? {}).filter(
|
|
313
|
+
([capabilityId]) => !selectedCapabilityIds.has(capabilityId),
|
|
314
|
+
),
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
...(defaultFor.length
|
|
319
|
+
? { defaultFor: Array.isArray(input.defaultFor) ? defaultFor : defaultFor[0] }
|
|
320
|
+
: {}),
|
|
321
|
+
...(Object.keys(providerPreferences).length ? { providerPreferences } : {}),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function resolveProviderSelection(
|
|
326
|
+
input: ResolveProviderSelectionInput,
|
|
327
|
+
): ProviderSelectionResolution {
|
|
328
|
+
const selections = selectProviders(input);
|
|
329
|
+
const mismatches = findConfiguredProviderMismatches(input);
|
|
330
|
+
const excludedProviderIds = getExcludedProviders(selections.values());
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
selections,
|
|
334
|
+
mismatches,
|
|
335
|
+
excludedProviderIds,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export function resolveItemProviderSelection<T>(
|
|
340
|
+
input: ResolveItemProviderSelectionInput<T>,
|
|
341
|
+
): ItemProviderSelectionResolution {
|
|
342
|
+
const providersByCapability = collectProvidersByCapability(input);
|
|
343
|
+
|
|
344
|
+
const resolution = resolveProviderSelection({
|
|
345
|
+
providersByCapability,
|
|
346
|
+
...(input.configuredProviders ? { configuredProviders: input.configuredProviders } : {}),
|
|
347
|
+
...(input.fallbackProviders ? { fallbackProviders: input.fallbackProviders } : {}),
|
|
348
|
+
...(input.selectedProviders ? { selectedProviders: input.selectedProviders } : {}),
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
providersByCapability,
|
|
353
|
+
...resolution,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { resolveItemProviderSelection } from '@lorion-org/provider-selection';
|
|
2
|
-
|
|
3
|
-
type CommandHandler = {
|
|
4
|
-
commandId: string;
|
|
5
|
-
handlerId: string;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const handlers: CommandHandler[] = [
|
|
9
|
-
{
|
|
10
|
-
commandId: 'open',
|
|
11
|
-
handlerId: 'open-native',
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
commandId: 'open',
|
|
15
|
-
handlerId: 'open-web',
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
commandId: 'share',
|
|
19
|
-
handlerId: 'share-link',
|
|
20
|
-
},
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
const result = resolveItemProviderSelection({
|
|
24
|
-
items: handlers,
|
|
25
|
-
getCapabilityId: (handler) => handler.commandId,
|
|
26
|
-
getProviderId: (handler) => handler.handlerId,
|
|
27
|
-
configuredProviders: {
|
|
28
|
-
open: 'open-web',
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
console.log(result.providersByCapability);
|
|
33
|
-
// { open: ['open-native', 'open-web'], share: ['share-link'] }
|
|
34
|
-
|
|
35
|
-
console.log(result.selections);
|
|
36
|
-
// {
|
|
37
|
-
// open: { selectedProviderId: 'open-web', mode: 'configured' },
|
|
38
|
-
// share: { selectedProviderId: 'share-link', mode: 'first' }
|
|
39
|
-
// }
|
|
40
|
-
|
|
41
|
-
console.log(result.mismatches);
|
|
42
|
-
// []
|
|
43
|
-
|
|
44
|
-
console.log(result.excludedProviderIds);
|
|
45
|
-
// ['open-native']
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { resolveItemProviderSelection } from '@lorion-org/provider-selection';
|
|
2
|
-
|
|
3
|
-
type StorageDriver = {
|
|
4
|
-
storageKind: string;
|
|
5
|
-
driverId: string;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const drivers: StorageDriver[] = [
|
|
9
|
-
{
|
|
10
|
-
storageKind: 'blob',
|
|
11
|
-
driverId: 's3',
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
storageKind: 'blob',
|
|
15
|
-
driverId: 'filesystem',
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
storageKind: 'queue',
|
|
19
|
-
driverId: 'redis-streams',
|
|
20
|
-
},
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
const result = resolveItemProviderSelection({
|
|
24
|
-
items: drivers,
|
|
25
|
-
getCapabilityId: (driver) => driver.storageKind,
|
|
26
|
-
getProviderId: (driver) => driver.driverId,
|
|
27
|
-
fallbackProviders: {
|
|
28
|
-
blob: 'filesystem',
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
console.log(result.selections);
|
|
33
|
-
// {
|
|
34
|
-
// blob: { selectedProviderId: 'filesystem', mode: 'fallback' },
|
|
35
|
-
// queue: { selectedProviderId: 'redis-streams', mode: 'first' }
|
|
36
|
-
// }
|
|
37
|
-
|
|
38
|
-
console.log(result.mismatches);
|
|
39
|
-
// []
|