@fgv/ts-res 5.0.1-9 → 5.0.1
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/index.browser.js +53 -0
- package/dist/index.js +45 -0
- package/dist/packlets/bundle/bundleBuilder.js +130 -0
- package/dist/packlets/bundle/bundleLoader.js +101 -0
- package/dist/packlets/bundle/bundleNormalizer.js +138 -0
- package/dist/packlets/bundle/bundleUtils.js +113 -0
- package/dist/packlets/bundle/convert.js +75 -0
- package/dist/packlets/bundle/index.js +42 -0
- package/dist/packlets/bundle/model.js +23 -0
- package/dist/packlets/common/conditions.js +47 -0
- package/dist/packlets/common/convert.js +172 -0
- package/dist/packlets/common/helpers/conditions.js +66 -0
- package/dist/packlets/common/helpers/context.js +70 -0
- package/dist/packlets/common/helpers/index.js +26 -0
- package/dist/packlets/common/helpers/qualifierDefaultValues.js +66 -0
- package/dist/packlets/common/helpers/resources.js +93 -0
- package/dist/packlets/common/index.js +28 -0
- package/dist/packlets/common/resources.js +27 -0
- package/dist/packlets/common/validate/conditions.js +544 -0
- package/dist/packlets/common/validate/index.js +26 -0
- package/dist/packlets/common/validate/regularExpressions.js +84 -0
- package/dist/packlets/common/validate/resources.js +209 -0
- package/dist/packlets/conditions/condition.js +252 -0
- package/dist/packlets/conditions/conditionCollector.js +69 -0
- package/dist/packlets/conditions/conditionDecls.js +23 -0
- package/dist/packlets/conditions/conditionSet.js +247 -0
- package/dist/packlets/conditions/conditionSetCollector.js +89 -0
- package/dist/packlets/conditions/conditionSetDecls.js +23 -0
- package/dist/packlets/conditions/conditionToken.js +147 -0
- package/dist/packlets/conditions/convert/conditionSetDecls.js +51 -0
- package/dist/packlets/conditions/convert/decls.js +75 -0
- package/dist/packlets/conditions/convert/index.js +24 -0
- package/dist/packlets/conditions/index.js +31 -0
- package/dist/packlets/config/common.js +78 -0
- package/dist/packlets/config/configInitFactory.js +258 -0
- package/dist/packlets/config/convert.js +56 -0
- package/dist/packlets/config/index.browser.js +34 -0
- package/dist/packlets/config/index.js +28 -0
- package/dist/packlets/config/json.js +23 -0
- package/dist/packlets/config/predefined/default.js +138 -0
- package/dist/packlets/config/predefined/extended.js +190 -0
- package/dist/packlets/config/predefined/index.js +25 -0
- package/dist/packlets/config/systemConfiguration.js +147 -0
- package/dist/packlets/context/contextDecls.js +23 -0
- package/dist/packlets/context/contextToken.js +202 -0
- package/dist/packlets/context/convert/decls.js +91 -0
- package/dist/packlets/context/convert/index.js +23 -0
- package/dist/packlets/context/index.js +26 -0
- package/dist/packlets/decisions/abstractDecision.js +68 -0
- package/dist/packlets/decisions/abstractDecisionCollector.js +90 -0
- package/dist/packlets/decisions/candidate.js +76 -0
- package/dist/packlets/decisions/common.js +23 -0
- package/dist/packlets/decisions/concreteDecision.js +111 -0
- package/dist/packlets/decisions/decision.js +126 -0
- package/dist/packlets/decisions/index.js +28 -0
- package/dist/packlets/import/fsItem.js +134 -0
- package/dist/packlets/import/importContext.js +129 -0
- package/dist/packlets/import/importManager.js +129 -0
- package/dist/packlets/import/importable.js +32 -0
- package/dist/packlets/import/importers/collectionImporter.js +118 -0
- package/dist/packlets/import/importers/fsItemImporter.js +116 -0
- package/dist/packlets/import/importers/importer.js +23 -0
- package/dist/packlets/import/importers/index.browser.js +29 -0
- package/dist/packlets/import/importers/index.js +27 -0
- package/dist/packlets/import/importers/jsonImporter.js +94 -0
- package/dist/packlets/import/importers/pathImporter.js +84 -0
- package/dist/packlets/import/index.browser.js +34 -0
- package/dist/packlets/import/index.js +28 -0
- package/dist/packlets/qualifier-types/config/convert.js +124 -0
- package/dist/packlets/qualifier-types/config/index.js +25 -0
- package/dist/packlets/qualifier-types/config/json.js +32 -0
- package/dist/packlets/qualifier-types/convert.js +56 -0
- package/dist/packlets/qualifier-types/helpers.js +82 -0
- package/dist/packlets/qualifier-types/index.js +32 -0
- package/dist/packlets/qualifier-types/languageQualifierType.js +142 -0
- package/dist/packlets/qualifier-types/literalQualifierType.js +200 -0
- package/dist/packlets/qualifier-types/literalValueHierarchy.js +236 -0
- package/dist/packlets/qualifier-types/qualifierType.js +184 -0
- package/dist/packlets/qualifier-types/qualifierTypeCollector.js +66 -0
- package/dist/packlets/qualifier-types/territoryQualifierType.js +200 -0
- package/dist/packlets/qualifiers/convert/decls.js +70 -0
- package/dist/packlets/qualifiers/convert/index.js +24 -0
- package/dist/packlets/qualifiers/convert/qualifier.js +40 -0
- package/dist/packlets/qualifiers/index.js +29 -0
- package/dist/packlets/qualifiers/qualifier.js +108 -0
- package/dist/packlets/qualifiers/qualifierCollector.js +123 -0
- package/dist/packlets/qualifiers/qualifierDecl.js +23 -0
- package/dist/packlets/qualifiers/qualifierDefaultValueDecls.js +23 -0
- package/dist/packlets/qualifiers/qualifierDefaultValueToken.js +175 -0
- package/dist/packlets/resource-json/compiled/common.js +23 -0
- package/dist/packlets/resource-json/compiled/convert.js +132 -0
- package/dist/packlets/resource-json/compiled/index.js +26 -0
- package/dist/packlets/resource-json/compiled/json.js +2 -0
- package/dist/packlets/resource-json/convert.js +171 -0
- package/dist/packlets/resource-json/helpers.js +241 -0
- package/dist/packlets/resource-json/index.js +31 -0
- package/dist/packlets/resource-json/json.js +36 -0
- package/dist/packlets/resource-json/normalized.js +23 -0
- package/dist/packlets/resource-json/resourceDeclCollection.js +88 -0
- package/dist/packlets/resource-json/resourceDeclContainer.js +23 -0
- package/dist/packlets/resource-json/resourceDeclTree.js +94 -0
- package/dist/packlets/resource-types/config/convert.js +35 -0
- package/dist/packlets/resource-types/config/index.js +25 -0
- package/dist/packlets/resource-types/config/json.js +23 -0
- package/dist/packlets/resource-types/helpers.js +42 -0
- package/dist/packlets/resource-types/index.js +28 -0
- package/dist/packlets/resource-types/jsonResourceType.js +66 -0
- package/dist/packlets/resource-types/resourceType.js +95 -0
- package/dist/packlets/resource-types/resourceTypeCollector.js +61 -0
- package/dist/packlets/resources/candidateReducer.js +246 -0
- package/dist/packlets/resources/candidateValue.js +92 -0
- package/dist/packlets/resources/candidateValueCollector.js +94 -0
- package/dist/packlets/resources/common.js +23 -0
- package/dist/packlets/resources/deltaGenerator.js +338 -0
- package/dist/packlets/resources/index.js +31 -0
- package/dist/packlets/resources/resource.js +207 -0
- package/dist/packlets/resources/resourceBuilder.js +183 -0
- package/dist/packlets/resources/resourceCandidate.js +216 -0
- package/dist/packlets/resources/resourceManagerBuilder.js +890 -0
- package/dist/packlets/runtime/cacheListener.js +58 -0
- package/dist/packlets/runtime/cacheMetrics.js +149 -0
- package/dist/packlets/runtime/compiledResourceCollection.js +473 -0
- package/dist/packlets/runtime/conditionSetResolutionResult.js +134 -0
- package/dist/packlets/runtime/context/contextQualifierProvider.js +29 -0
- package/dist/packlets/runtime/context/contextQualifierProviderValidator.js +146 -0
- package/dist/packlets/runtime/context/index.js +26 -0
- package/dist/packlets/runtime/context/simpleContextQualifierProvider.js +196 -0
- package/dist/packlets/runtime/context/validatingSimpleContextQualifierProvider.js +60 -0
- package/dist/packlets/runtime/iResourceManager.js +23 -0
- package/dist/packlets/runtime/index.js +34 -0
- package/dist/packlets/runtime/resource-tree/common.js +23 -0
- package/dist/packlets/runtime/resource-tree/index.js +26 -0
- package/dist/packlets/runtime/resource-tree/readOnlyResourceTree.js +216 -0
- package/dist/packlets/runtime/resource-tree/resourceTreeChildren.js +97 -0
- package/dist/packlets/runtime/resource-tree/resourceTreeChildrenValidator.js +161 -0
- package/dist/packlets/runtime/resourceResolver.js +481 -0
- package/dist/packlets/runtime/resourceTreeResolver.js +220 -0
- package/dist/packlets/runtime/validate.js +47 -0
- package/dist/packlets/zip-archive/convert.js +100 -0
- package/dist/packlets/zip-archive/index.browser.js +54 -0
- package/dist/packlets/zip-archive/index.js +47 -0
- package/dist/packlets/zip-archive/json.js +23 -0
- package/dist/packlets/zip-archive/types.js +36 -0
- package/dist/packlets/zip-archive/zipArchiveCreator.js +191 -0
- package/dist/packlets/zip-archive/zipArchiveFormat.js +140 -0
- package/dist/packlets/zip-archive/zipArchiveLoader.js +282 -0
- package/dist/test/data/sample.json +32 -0
- package/dist/test/unit/qualifier-types/testQualifierType.js +76 -0
- package/dist/test/unit/resource-types/testDerivedResourceType.js +109 -0
- package/dist/test/unit/resources/deltaGenerator.helpers.js +282 -0
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/index.browser.d.ts +24 -0
- package/lib/index.browser.js +116 -0
- package/lib/packlets/config/index.browser.d.ts +7 -0
- package/lib/packlets/config/index.browser.js +74 -0
- package/lib/packlets/import/importers/index.browser.d.ts +4 -0
- package/lib/packlets/import/importers/index.browser.js +45 -0
- package/lib/packlets/import/index.browser.d.ts +7 -0
- package/lib/packlets/import/index.browser.js +74 -0
- package/lib/packlets/zip-archive/index.browser.d.ts +27 -0
- package/lib/packlets/zip-archive/index.browser.js +102 -0
- package/package.json +21 -7
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Erik Fortune
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
import { ResultMap, captureResult, fail, failWithDetail, succeed, succeedWithDetail } from '@fgv/ts-utils';
|
|
23
|
+
import { Validate } from '../common';
|
|
24
|
+
import { ResourceCandidate } from './resourceCandidate';
|
|
25
|
+
import { Resource } from './resource';
|
|
26
|
+
/**
|
|
27
|
+
* Represents a builder for a single logical {@link Resources.Resource | resource}. Collects candidates
|
|
28
|
+
* with a common resource ID, validates them against each other and builds a {@link Resources.Resource | resource}
|
|
29
|
+
* object once all candidates are collected.
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export class ResourceBuilder {
|
|
33
|
+
/**
|
|
34
|
+
* Supplied or inferred {@link ResourceTypes.ResourceType | type} of the resource being built.
|
|
35
|
+
* If no type is supplied, the type will be inferred from the candidates - at least one candidate must
|
|
36
|
+
* define resource type and all candidates must be of the same type.
|
|
37
|
+
*/
|
|
38
|
+
get resourceType() {
|
|
39
|
+
return this._resourceType;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Array of {@link Resources.ResourceCandidate | candidates} for the resource being built.
|
|
43
|
+
*/
|
|
44
|
+
get candidates() {
|
|
45
|
+
return Array.from(this._candidates.values()).sort(ResourceCandidate.compare).reverse();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Constructor for a {@link Resources.ResourceBuilder | ResourceBuilder} object.
|
|
49
|
+
* @param params - Parameters to construct the new {@link Resources.ResourceBuilder | ResourceBuilder}.
|
|
50
|
+
*/
|
|
51
|
+
constructor(params) {
|
|
52
|
+
this.id = Validate.toResourceId(params.id).orThrow();
|
|
53
|
+
this._resourceTypes = params.resourceTypes;
|
|
54
|
+
this._conditionSets = params.conditionSets;
|
|
55
|
+
this._decisions = params.decisions;
|
|
56
|
+
this._candidateValues = params.candidateValues;
|
|
57
|
+
this._candidates = new ResultMap();
|
|
58
|
+
if (params.typeName) {
|
|
59
|
+
this._resourceType = this._resourceTypes.validating.get(params.typeName).orThrow();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Creates a new {@link Resources.ResourceBuilder | ResourceBuilder} object.
|
|
64
|
+
* @param params - Parameters to create a new {@link Resources.ResourceBuilder | ResourceBuilder}.
|
|
65
|
+
* @returns `Success` with the new {@link Resources.ResourceBuilder | ResourceBuilder} object if successful,
|
|
66
|
+
* or `Failure` with an error message if not.
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
static create(params) {
|
|
70
|
+
return captureResult(() => new ResourceBuilder(params));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Gets the {@link Resources.ResourceCandidate | candidates} that match a given {@link Context.IValidatedContextDecl | context}.
|
|
74
|
+
* @param context - The {@link Context.IValidatedContextDecl | context} to get candidates for.
|
|
75
|
+
* @param options - Optional {@link Context.IContextMatchOptions | context match options} to use when matching candidates.
|
|
76
|
+
* @returns An array of {@link Resources.ResourceCandidate | candidates} that match the given context.
|
|
77
|
+
*/
|
|
78
|
+
getCandidatesForContext(context, options) {
|
|
79
|
+
return Array.from(this._candidates.values()).filter((candidate) => candidate.canMatchPartialContext(context, options));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Given a {@link ResourceJson.Json.IChildResourceCandidateDecl | child resource candidate declaration}, creates and adds a
|
|
83
|
+
* {@link Resources.ResourceCandidate | candidate} to the resource being built.
|
|
84
|
+
* @param candidate - The {@link ResourceJson.Json.IChildResourceCandidateDecl | IChildResourceCandidateDecl} to add to the
|
|
85
|
+
* resource being built.
|
|
86
|
+
* @returns `Success` with the added {@link Resources.ResourceCandidate | candidate} if successful,
|
|
87
|
+
* or `Failure` with an error message if not.
|
|
88
|
+
*/
|
|
89
|
+
addChildCandidate(childDecl) {
|
|
90
|
+
return ResourceCandidate.create({
|
|
91
|
+
id: this.id,
|
|
92
|
+
resourceType: this._resourceType,
|
|
93
|
+
decl: childDecl,
|
|
94
|
+
conditionSets: this._conditionSets,
|
|
95
|
+
candidateValues: this._candidateValues
|
|
96
|
+
})
|
|
97
|
+
.withDetail('failure', 'success')
|
|
98
|
+
.onSuccess((candidate) => {
|
|
99
|
+
return this._candidates
|
|
100
|
+
.getOrAdd(candidate.conditions.toString(), candidate)
|
|
101
|
+
.onSuccess((added, detail) => {
|
|
102
|
+
if (detail === 'exists') {
|
|
103
|
+
/* c8 ignore next 5 - defensive coding: conflicting candidates with same conditions should not occur */
|
|
104
|
+
/* c8 ignore next 6 - functional code tested but coverage intermittently missed */
|
|
105
|
+
if (!ResourceCandidate.equal(added, candidate)) {
|
|
106
|
+
return failWithDetail(`${this.id}: conflicting candidates.`, 'exists');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return succeedWithDetail(added, detail);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Given a {@link ResourceJson.Json.ILooseResourceCandidateDecl | resource candidate declaration}, creates and adds a
|
|
115
|
+
* {@link Resources.ResourceCandidate | candidate} to the resource being built.
|
|
116
|
+
* @param candidate - The {@link ResourceJson.Json.ILooseResourceCandidateDecl | IResourceCandidateDecl} to add to the
|
|
117
|
+
* resource being built.
|
|
118
|
+
* @returns `Success` with the added {@link Resources.ResourceCandidate | candidate} if successful,
|
|
119
|
+
* or `Failure` with an error message if not. Fails with error detail 'type-mismatch' if the candidate
|
|
120
|
+
* specifies a different resource type than previously added candidates, or with 'exists' if a candidate
|
|
121
|
+
* already exists with the same conditions but different values. Succeeds with 'exists' and returns the
|
|
122
|
+
* existing candidate if the candidate to be added is identical to an existing candidate.
|
|
123
|
+
*/
|
|
124
|
+
addLooseCandidate(decl) {
|
|
125
|
+
/* c8 ignore next 6 - functional code path tested but coverage intermittently missed */
|
|
126
|
+
if (decl.id !== this.id) {
|
|
127
|
+
return failWithDetail(`${this.id}: mismatched candidate id ${decl.id}.`, 'id-mismatch');
|
|
128
|
+
}
|
|
129
|
+
if (decl.resourceTypeName !== undefined) {
|
|
130
|
+
const rt = this.setResourceType(decl.resourceTypeName);
|
|
131
|
+
/* c8 ignore next 3 - functional code tested but coverage intermittently missed */
|
|
132
|
+
if (rt.isFailure()) {
|
|
133
|
+
return failWithDetail(rt.message, 'type-mismatch');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return this.addChildCandidate(decl);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Sets the resource type for the resource being built. Fails if a resource type has already been set
|
|
140
|
+
* and it does not match the new resource type.
|
|
141
|
+
* @param resourceTypeName - The name of the resource type to set.
|
|
142
|
+
* @returns `Success` with the updated {@link Resources.ResourceBuilder | ResourceBuilder} object if successful,
|
|
143
|
+
* or `Failure` with an error message if not.
|
|
144
|
+
*/
|
|
145
|
+
setResourceType(resourceTypeName) {
|
|
146
|
+
var _a;
|
|
147
|
+
/* c8 ignore next 2 - functional code tested but coverage intermittently missed */
|
|
148
|
+
if (((_a = this._resourceType) === null || _a === void 0 ? void 0 : _a.key) === resourceTypeName) {
|
|
149
|
+
return succeed(this);
|
|
150
|
+
}
|
|
151
|
+
else if (this._resourceType !== undefined) {
|
|
152
|
+
/* c8 ignore next 4 - functional code tested but coverage intermittently missed */
|
|
153
|
+
return fail(`${this.id}: conflicting resource types ${this._resourceType.key} !== ${resourceTypeName}.`);
|
|
154
|
+
}
|
|
155
|
+
return this._resourceTypes.validating.get(resourceTypeName).onSuccess((resourceType) => {
|
|
156
|
+
this._resourceType = resourceType;
|
|
157
|
+
return succeedWithDetail(this, 'success');
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Builds the {@link Resources.Resource | resource} object from this builder.
|
|
162
|
+
* @returns `Success` with the new {@link Resources.Resource | Resource} object if successful,
|
|
163
|
+
* or `Failure` with an error message if not. Fails if no candidates have been added
|
|
164
|
+
* or if the resource type is not defined.
|
|
165
|
+
*/
|
|
166
|
+
build() {
|
|
167
|
+
/* c8 ignore next 3 - functional code path tested but coverage intermittently missed */
|
|
168
|
+
if (this._candidates.size === 0) {
|
|
169
|
+
return fail(`${this.id}: no candidates supplied.`);
|
|
170
|
+
}
|
|
171
|
+
/* c8 ignore next 3 - functional code path tested but coverage intermittently missed */
|
|
172
|
+
if (this._resourceType === undefined) {
|
|
173
|
+
return fail(`${this.id}: no resource type supplied or inferred.`);
|
|
174
|
+
}
|
|
175
|
+
return Resource.create({
|
|
176
|
+
id: this.id,
|
|
177
|
+
resourceType: this._resourceType,
|
|
178
|
+
candidates: this.candidates,
|
|
179
|
+
decisions: this._decisions
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=resourceBuilder.js.map
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Erik Fortune
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
import { PerfectMatch, Validate } from '../common';
|
|
23
|
+
import { ConditionSet } from '../conditions';
|
|
24
|
+
import * as ResourceJson from '../resource-json';
|
|
25
|
+
import { captureResult, mapResults, MessageAggregator, fail, succeed } from '@fgv/ts-utils';
|
|
26
|
+
/**
|
|
27
|
+
* A {@link Resources.ResourceCandidate | resource candidate} represents a single possible
|
|
28
|
+
* instance value for some resource, with the conditions under which it applies
|
|
29
|
+
* and instructions on how to merge it with other instances.
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export class ResourceCandidate {
|
|
33
|
+
/**
|
|
34
|
+
* The JSON representation of the instance data to be applied.
|
|
35
|
+
* @remarks
|
|
36
|
+
* This property provides access to the JSON data from the underlying candidate value.
|
|
37
|
+
*/
|
|
38
|
+
get json() {
|
|
39
|
+
return this.candidateValue.json;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The completeness of the candidate value.
|
|
43
|
+
*/
|
|
44
|
+
get completeness() {
|
|
45
|
+
return this.isPartial ? 'partial' : 'full';
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Constructor for a {@link Resources.ResourceCandidate | ResourceCandidate} object.
|
|
49
|
+
* @param params - Parameters to create a new {@link Resources.ResourceCandidate | ResourceCandidate}.
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
constructor(params) {
|
|
53
|
+
var _a, _b;
|
|
54
|
+
this.id = Validate.toResourceId(params.id).orThrow();
|
|
55
|
+
this.candidateValue = params.candidateValues.validating.getOrAdd(params.decl.json).orThrow();
|
|
56
|
+
this.conditions = ResourceCandidate._mergeConditions(params.conditionSets, params.decl.conditions, params.parentConditions).orThrow();
|
|
57
|
+
this.isPartial = (_a = params.decl.isPartial) !== null && _a !== void 0 ? _a : false;
|
|
58
|
+
this.mergeMethod = (_b = params.decl.mergeMethod) !== null && _b !== void 0 ? _b : 'augment';
|
|
59
|
+
this.resourceType = params.resourceType;
|
|
60
|
+
if (this.resourceType) {
|
|
61
|
+
this.resourceType.validateDeclaration(this).orThrow();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Creates a new {@link Resources.ResourceCandidate | ResourceCandidate} object.
|
|
66
|
+
* @param params - Parameters to create a new {@link Resources.ResourceCandidate | ResourceCandidate}.
|
|
67
|
+
* @returns `Success` with the new {@link Resources.ResourceCandidate | ResourceCandidate} object if successful,
|
|
68
|
+
* or `Failure` with an error message if not.
|
|
69
|
+
* @public
|
|
70
|
+
*/
|
|
71
|
+
static create(params) {
|
|
72
|
+
return captureResult(() => new ResourceCandidate(params));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Determines if this candidate can match the supplied context (possibly partial).
|
|
76
|
+
* @param context - The context to match.
|
|
77
|
+
* @param options - Options to use when matching.
|
|
78
|
+
* @returns `true` if the candidate can match the context, `false` otherwise.
|
|
79
|
+
*/
|
|
80
|
+
canMatchPartialContext(context, options) {
|
|
81
|
+
return this.conditions.canMatchPartialContext(context, options);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Gets the {@link ResourceJson.Json.IChildResourceCandidateDecl | child resource candidate declaration}
|
|
85
|
+
* for this candidate.
|
|
86
|
+
* @param options - {@link ResourceJson.Helpers.IDeclarationOptions | options} to use when creating the declaration.
|
|
87
|
+
* @returns The {@link ResourceJson.Json.IChildResourceCandidateDecl | child resource candidate declaration}.
|
|
88
|
+
*/
|
|
89
|
+
toChildResourceCandidateDecl(options) {
|
|
90
|
+
const showDefaults = (options === null || options === void 0 ? void 0 : options.showDefaults) === true;
|
|
91
|
+
return Object.assign(Object.assign({ json: this.json, conditions: this.conditions.toConditionSetRecordDecl(options) }, (showDefaults || this.isPartial ? { isPartial: this.isPartial } : {})), (showDefaults || this.mergeMethod !== 'augment' ? { mergeMethod: this.mergeMethod } : {}));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Gets the {@link ResourceJson.Json.ILooseResourceCandidateDecl | loose resource candidate declaration}
|
|
95
|
+
* for this candidate.
|
|
96
|
+
* @param options - {@link ResourceJson.Helpers.IDeclarationOptions | options} to use when creating the declaration.
|
|
97
|
+
* @returns The {@link ResourceJson.Json.ILooseResourceCandidateDecl | loose resource candidate declaration}.
|
|
98
|
+
*/
|
|
99
|
+
toLooseResourceCandidateDecl(options) {
|
|
100
|
+
var _a;
|
|
101
|
+
const showDefaults = (options === null || options === void 0 ? void 0 : options.showDefaults) === true;
|
|
102
|
+
const resourceTypeName = (_a = this.resourceType) === null || _a === void 0 ? void 0 : _a.key;
|
|
103
|
+
return Object.assign(Object.assign(Object.assign({ id: this.id.toString(), json: this.json, conditions: this.conditions.toConditionSetRecordDecl(options) }, (showDefaults || this.isPartial ? { isPartial: this.isPartial } : {})), (showDefaults || this.mergeMethod !== 'augment' ? { mergeMethod: this.mergeMethod } : {})), (resourceTypeName ? { resourceTypeName } : {}));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Extracts the {@link ResourceTypes.ResourceType | resource type} from a list of {@link Resources.ResourceCandidate | resource candidates},
|
|
107
|
+
* if present.
|
|
108
|
+
* @param candidates - The list of candidates from which to extract the resource type.
|
|
109
|
+
* @returns `Success` with the resource type if successful, `Success` with `undefined` if none of the candidates
|
|
110
|
+
* specify a resource tap, and `Failure` with an error message if clients specify conflicting resource types.
|
|
111
|
+
* @public
|
|
112
|
+
*/
|
|
113
|
+
static validateResourceTypes(candidates, expectedType) {
|
|
114
|
+
const errors = new MessageAggregator();
|
|
115
|
+
let selectedType = expectedType;
|
|
116
|
+
for (const candidate of candidates) {
|
|
117
|
+
if (selectedType === undefined) {
|
|
118
|
+
/* c8 ignore next 1 - functional code path tested but coverage intermittently missed */
|
|
119
|
+
selectedType = candidate.resourceType;
|
|
120
|
+
}
|
|
121
|
+
else if (candidate.resourceType && selectedType !== candidate.resourceType) {
|
|
122
|
+
/* c8 ignore next 4 - functional code path tested but coverage intermittently missed */
|
|
123
|
+
errors.addMessage(`${candidate.id}: resource type mismatch (${selectedType.key} != ${candidate.resourceType.key})`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return errors.returnOrReport(succeed(selectedType));
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Compares two {@link Resources.ResourceCandidate | ResourceCandidates} for sorting purposes.
|
|
130
|
+
* @param rc1 - The first candidate to compare.
|
|
131
|
+
* @param rc2 - The second candidate to compare.
|
|
132
|
+
* @returns A negative number if `rc1` should come before `rc2`, a positive number if `rc2` should come before `rc1`,
|
|
133
|
+
* or zero if they are equivalent.
|
|
134
|
+
* @public
|
|
135
|
+
*/
|
|
136
|
+
static compare(rc1, rc2) {
|
|
137
|
+
return ConditionSet.compare(rc1.conditions, rc2.conditions);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Compares two {@link Resources.ResourceCandidate | ResourceCandidates} for equality.
|
|
141
|
+
* @param rc1 - The first candidate to compare.
|
|
142
|
+
* @param rc2 - The second candidate to compare.
|
|
143
|
+
* @returns `true` if the candidates are equal, `false` otherwise.
|
|
144
|
+
* @public
|
|
145
|
+
*/
|
|
146
|
+
static equal(rc1, rc2) {
|
|
147
|
+
/* c8 ignore next 3 - functional code path tested but coverage intermittently missed */
|
|
148
|
+
if (rc1 === rc2) {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
let equal = rc1.id === rc2.id &&
|
|
152
|
+
rc1.isPartial === rc2.isPartial &&
|
|
153
|
+
rc1.mergeMethod === rc2.mergeMethod &&
|
|
154
|
+
ConditionSet.compare(rc1.conditions, rc2.conditions) === 0;
|
|
155
|
+
if (equal) {
|
|
156
|
+
// Compare candidate values by their keys for efficient JSON comparison
|
|
157
|
+
equal = rc1.candidateValue.key === rc2.candidateValue.key;
|
|
158
|
+
}
|
|
159
|
+
return equal;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Validates declared conditions and merges them with parent conditions.
|
|
163
|
+
* @param conditionSets - The {@link Conditions.ConditionSetCollector | condition set collector}
|
|
164
|
+
* to use for this candidate.
|
|
165
|
+
* @param declared - The declared conditions for the candidate.
|
|
166
|
+
* @param parent - The parent conditions to merge with the declared conditions.
|
|
167
|
+
* @returns `Success` with the merged conditions if successful, `Failure` otherwise.
|
|
168
|
+
* @internal
|
|
169
|
+
*/
|
|
170
|
+
static _mergeConditions(conditionSets, declared, parent) {
|
|
171
|
+
/* c8 ignore next 2 - code coverage is flaky */
|
|
172
|
+
declared = declared !== null && declared !== void 0 ? declared : {};
|
|
173
|
+
parent = parent !== null && parent !== void 0 ? parent : [];
|
|
174
|
+
const { value: conditionDecls, message } = ResourceJson.Convert.conditionSetDecl.convert(declared);
|
|
175
|
+
/* c8 ignore next 3 - functional code path tested but coverage intermittently missed */
|
|
176
|
+
if (message !== undefined) {
|
|
177
|
+
return fail(message);
|
|
178
|
+
}
|
|
179
|
+
return mapResults(
|
|
180
|
+
// get or add all declared conditions from our condition collector
|
|
181
|
+
conditionDecls.map((decl) => conditionSets.conditions.validating.getOrAdd(decl))).onSuccess((declaredConditions) => {
|
|
182
|
+
// make sure our parent conditions all come from our condition collector too
|
|
183
|
+
return mapResults(parent.map((c) => conditionSets.conditions.getOrAdd(c))).onSuccess((parentConditions) => {
|
|
184
|
+
const conditions = [...parentConditions, ...declaredConditions];
|
|
185
|
+
return conditionSets.validating.getOrAdd(conditions);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Finds the qualifiers that are made irrelevant by the supplied filterForContext.
|
|
191
|
+
* @param candidates - The candidates to find the reducible qualifiers for.
|
|
192
|
+
* @param filterForContext - The filter for context to use.
|
|
193
|
+
* @returns The qualifiers that are made irrelevant by the filterForContext.
|
|
194
|
+
* @remarks
|
|
195
|
+
* For any given set of candidates, qualifiers that match every candidate perfectly
|
|
196
|
+
* are irrelevant to the filtered content and can be removed. Qualifiers that do not
|
|
197
|
+
* match, that are not in the filterForContext, or which partially match at least one
|
|
198
|
+
* candidate remain relevant and must not be removed.
|
|
199
|
+
* @public
|
|
200
|
+
*/
|
|
201
|
+
static findReducibleQualifiers(candidates, filterForContext) {
|
|
202
|
+
const qualifiersToReduce = new Set(Object.keys(filterForContext));
|
|
203
|
+
for (const candidate of candidates) {
|
|
204
|
+
for (const condition of candidate.conditions.conditions) {
|
|
205
|
+
if (qualifiersToReduce.has(condition.qualifier.name)) {
|
|
206
|
+
const match = condition.qualifier.type.matches(condition.value, filterForContext[condition.qualifier.name], 'matches');
|
|
207
|
+
if (match !== PerfectMatch) {
|
|
208
|
+
qualifiersToReduce.delete(condition.qualifier.name);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return qualifiersToReduce.size > 0 ? qualifiersToReduce : undefined;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=resourceCandidate.js.map
|