@fgv/ts-res 5.0.1-9 → 5.0.2-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +25 -7
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=common.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
export * from './common';
|
|
23
|
+
export * from './resourceTreeChildren';
|
|
24
|
+
export * from './resourceTreeChildrenValidator';
|
|
25
|
+
export * from './readOnlyResourceTree';
|
|
26
|
+
//# sourceMappingURL=index.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 { MessageAggregator, captureResult, mapResults, succeed } from '@fgv/ts-utils';
|
|
23
|
+
import { Helpers } from '../../common';
|
|
24
|
+
import { ReadOnlyResourceTreeChildren } from './resourceTreeChildren';
|
|
25
|
+
// Type guards
|
|
26
|
+
/**
|
|
27
|
+
* Type guard to determine if an init object represents a branch or root with children.
|
|
28
|
+
* @param init - The initialization object to test
|
|
29
|
+
* @returns True if the init object has children property
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export function isResourceTreeRootOrNodeInit(init) {
|
|
33
|
+
return 'children' in init;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Type guard to determine if an init object represents a leaf node with a resource.
|
|
37
|
+
* @param init - The initialization object to test
|
|
38
|
+
* @returns True if the init object has a resource but no children
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
export function isResourceTreeLeafInit(init) {
|
|
42
|
+
return 'resource' in init && !('children' in init);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Implementation of a read-only resource tree leaf node that contains a resource value.
|
|
46
|
+
* Leaf nodes represent the actual resources in the tree and cannot have children.
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
export class ReadOnlyResourceTreeLeaf {
|
|
50
|
+
get isRoot() {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
get isBranch() {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
get isLeaf() {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Creates a new leaf node. Use the static create method instead.
|
|
61
|
+
* @param name - The name of this node (last segment of the path)
|
|
62
|
+
* @param parentPath - The path to the parent node (undefined for root-level nodes)
|
|
63
|
+
* @param resource - The resource value stored in this leaf
|
|
64
|
+
*/
|
|
65
|
+
constructor(name, parentPath, resource) {
|
|
66
|
+
this.name = name;
|
|
67
|
+
this.id = Helpers.joinResourceIds(parentPath, name).orThrow();
|
|
68
|
+
this.resource = resource;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Creates a new ReadOnlyResourceTreeLeaf instance.
|
|
72
|
+
* @param name - The name of this node (last segment of the path)
|
|
73
|
+
* @param parentPath - The path to the parent node (undefined for root-level nodes)
|
|
74
|
+
* @param resource - The resource value to store in this leaf
|
|
75
|
+
* @returns Result containing the new leaf node or failure if construction fails
|
|
76
|
+
*/
|
|
77
|
+
static create(name, parentPath, resource) {
|
|
78
|
+
return captureResult(() => new ReadOnlyResourceTreeLeaf(name, parentPath, resource));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Implementation of a read-only resource tree branch node that contains child nodes.
|
|
83
|
+
* Branch nodes organize other nodes in a hierarchical structure and may optionally contain a resource value.
|
|
84
|
+
* @public
|
|
85
|
+
*/
|
|
86
|
+
export class ReadOnlyResourceTreeBranch {
|
|
87
|
+
get isRoot() {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
get isBranch() {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
get isLeaf() {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Creates a new branch node. Use the static create method instead.
|
|
98
|
+
* @param name - The name of this node (last segment of the path)
|
|
99
|
+
* @param parentPath - The path to the parent node (undefined for root-level nodes)
|
|
100
|
+
* @param init - Initialization data containing child nodes
|
|
101
|
+
*/
|
|
102
|
+
constructor(name, parentPath, init) {
|
|
103
|
+
this.name = name;
|
|
104
|
+
this.id = Helpers.joinResourceIds(parentPath, name).orThrow();
|
|
105
|
+
const children = mapResults(Array.from(Object.entries(init.children)).map(([childName, childInit]) => {
|
|
106
|
+
if (isResourceTreeLeafInit(childInit)) {
|
|
107
|
+
return ReadOnlyResourceTreeLeaf.create(childName, this.id, childInit.resource);
|
|
108
|
+
}
|
|
109
|
+
return ReadOnlyResourceTreeBranch.create(childName, this.id, childInit);
|
|
110
|
+
})).orThrow();
|
|
111
|
+
this.children = new ReadOnlyResourceTreeChildren(this.id, children.map((c) => [c.name, c]));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Creates a new ReadOnlyResourceTreeBranch instance.
|
|
115
|
+
* @param childName - The name of this node (last segment of the path)
|
|
116
|
+
* @param path - The path to the parent node (undefined for root-level nodes)
|
|
117
|
+
* @param childInit - Initialization data containing child nodes
|
|
118
|
+
* @returns Result containing the new branch node or failure if construction fails
|
|
119
|
+
*/
|
|
120
|
+
static create(childName, path, childInit) {
|
|
121
|
+
return captureResult(() => new ReadOnlyResourceTreeBranch(childName, path, childInit));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Implementation of a read-only resource tree root that organizes resources hierarchically.
|
|
126
|
+
* The root provides the entry point for tree navigation and resource access by ResourceId paths.
|
|
127
|
+
* @public
|
|
128
|
+
*/
|
|
129
|
+
export class ReadOnlyResourceTreeRoot {
|
|
130
|
+
get isRoot() {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
get isBranch() {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
get isLeaf() {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Creates a new root node. Use the static create method instead.
|
|
141
|
+
* @param init - Initialization data containing child nodes
|
|
142
|
+
*/
|
|
143
|
+
constructor(init) {
|
|
144
|
+
const children = mapResults(Array.from(Object.entries(init.children)).map(([childName, childInit]) => {
|
|
145
|
+
if (isResourceTreeLeafInit(childInit)) {
|
|
146
|
+
return ReadOnlyResourceTreeLeaf.create(childName, undefined, childInit.resource);
|
|
147
|
+
}
|
|
148
|
+
return ReadOnlyResourceTreeBranch.create(childName, undefined, childInit);
|
|
149
|
+
})).orThrow();
|
|
150
|
+
this.children = new ReadOnlyResourceTreeChildren(undefined, children.map((c) => [c.name, c]));
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Creates a new ReadOnlyResourceTreeRoot from either resources or initialization data.
|
|
154
|
+
* @param init - Either an array of [ResourceId, resource] pairs or tree initialization structure
|
|
155
|
+
* @returns Result containing the new root or failure if construction fails
|
|
156
|
+
*/
|
|
157
|
+
static create(init) {
|
|
158
|
+
if (Array.isArray(init)) {
|
|
159
|
+
return ReadOnlyResourceTreeRoot.createResourceTreeInit(init).onSuccess((treeInit) => captureResult(() => new ReadOnlyResourceTreeRoot(treeInit)));
|
|
160
|
+
}
|
|
161
|
+
return captureResult(() => new ReadOnlyResourceTreeRoot(init));
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Converts an array of resources into tree initialization data.
|
|
165
|
+
* Validates that resource paths do not conflict - if a path has child resources,
|
|
166
|
+
* it cannot itself be a resource (e.g., if 'app.messages.welcome' exists, then 'app' cannot be a resource).
|
|
167
|
+
* @param resources - Array of [ResourceId, resource] pairs to convert
|
|
168
|
+
* @returns Result containing the initialization structure or failure if validation fails
|
|
169
|
+
*/
|
|
170
|
+
static createResourceTreeInit(resources) {
|
|
171
|
+
const errors = new MessageAggregator();
|
|
172
|
+
const root = { children: {} };
|
|
173
|
+
for (const [id, resource] of resources) {
|
|
174
|
+
Helpers.splitResourceId(id)
|
|
175
|
+
.onSuccess((pathComponents) => {
|
|
176
|
+
let currentNode = root;
|
|
177
|
+
const resourceName = pathComponents.pop();
|
|
178
|
+
for (const nodeName of pathComponents) {
|
|
179
|
+
if (isResourceTreeRootOrNodeInit(currentNode)) {
|
|
180
|
+
if (!(nodeName in currentNode.children)) {
|
|
181
|
+
currentNode.children[nodeName] = { children: {} };
|
|
182
|
+
}
|
|
183
|
+
if (isResourceTreeLeafInit(currentNode.children[nodeName])) {
|
|
184
|
+
errors.addMessage(`${id}: Expected a branch but found a leaf at ${nodeName}.`);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
currentNode = currentNode.children[nodeName];
|
|
188
|
+
}
|
|
189
|
+
} /* c8 ignore next 4 - defense in depth, not reachable without chicanery */
|
|
190
|
+
else if (isResourceTreeLeafInit(currentNode)) {
|
|
191
|
+
errors.addMessage(`${id}: Expected a branch but found a leaf.`);
|
|
192
|
+
break;
|
|
193
|
+
} /* c8 ignore next 3 - defense in depth, not reachable without chicanery */
|
|
194
|
+
else {
|
|
195
|
+
errors.addMessage(`${id}: Unexpected structure in resource tree`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (isResourceTreeRootOrNodeInit(currentNode)) {
|
|
199
|
+
if (resourceName in currentNode.children) {
|
|
200
|
+
errors.addMessage(`${id}: Duplicate resource at path.`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
currentNode.children[resourceName] = { resource };
|
|
204
|
+
} /* c8 ignore next 3 - defense in depth, not reachable without chicanery */
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
errors.addMessage(`${id}: Expected a branch but found a leaf.`);
|
|
208
|
+
}
|
|
209
|
+
return succeed(currentNode.children[resourceName]);
|
|
210
|
+
})
|
|
211
|
+
.aggregateError(errors);
|
|
212
|
+
}
|
|
213
|
+
return errors.returnOrReport(succeed(root));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=readOnlyResourceTree.js.map
|
|
@@ -0,0 +1,97 @@
|
|
|
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, fail, succeed } from '@fgv/ts-utils';
|
|
23
|
+
import { Helpers } from '../../common';
|
|
24
|
+
import { ResourceTreeChildrenValidator } from './resourceTreeChildrenValidator';
|
|
25
|
+
/**
|
|
26
|
+
* Implementation of a result-based resource tree that provides hierarchical access to resources.
|
|
27
|
+
* Extends ResultMap to provide collection-like access while adding tree-specific navigation methods.
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export class ReadOnlyResourceTreeChildren extends ResultMap {
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new ReadOnlyResourceTreeChildren instance.
|
|
33
|
+
* @param path - The path to this tree node (undefined for root)
|
|
34
|
+
* @param entries - Array of [name, node] tuples to populate the tree
|
|
35
|
+
*/
|
|
36
|
+
constructor(path, entries) {
|
|
37
|
+
super(entries);
|
|
38
|
+
this.path = path;
|
|
39
|
+
this.validating = new ResourceTreeChildrenValidator(this);
|
|
40
|
+
}
|
|
41
|
+
getResource(name) {
|
|
42
|
+
return this.get(name).onSuccess((node) => {
|
|
43
|
+
if (node.isLeaf) {
|
|
44
|
+
return succeed(node).withDetail('success');
|
|
45
|
+
}
|
|
46
|
+
/* c8 ignore next 3 - functional code tested but coverage intermittently missed */
|
|
47
|
+
return fail(`${name}: not a resource${this.path ? ` in ${this.path}` : ''}.`).withDetail('failure');
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
getBranch(name) {
|
|
51
|
+
return this.get(name).onSuccess((node) => {
|
|
52
|
+
if (node.isBranch) {
|
|
53
|
+
return succeed(node).withDetail('success');
|
|
54
|
+
}
|
|
55
|
+
/* c8 ignore next 3 - functional code tested but coverage intermittently missed */
|
|
56
|
+
return fail(`${name}: not a branch${this.path ? ` in ${this.path}` : ''}.`).withDetail('failure');
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
getById(id) {
|
|
60
|
+
return Helpers.splitResourceId(id).onSuccess((names) => {
|
|
61
|
+
let name = names.shift();
|
|
62
|
+
if (name !== undefined) {
|
|
63
|
+
let node = this.get(name).orDefault();
|
|
64
|
+
while (node) {
|
|
65
|
+
name = names.shift();
|
|
66
|
+
if (name === undefined) {
|
|
67
|
+
return succeed(node);
|
|
68
|
+
}
|
|
69
|
+
if (node.isLeaf) {
|
|
70
|
+
/* c8 ignore next 1 - defense in depth */
|
|
71
|
+
return fail(`${id}: resource not found${this.path ? ` in ${this.path}` : ''}.`);
|
|
72
|
+
}
|
|
73
|
+
node = node.children.get(name).orDefault();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return fail(`${id}: resource not found${this.path ? ` in ${this.path}` : ''}.`);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
getResourceById(id) {
|
|
80
|
+
return this.getById(id).onSuccess((node) => {
|
|
81
|
+
if (node.isLeaf) {
|
|
82
|
+
return succeed(node).withDetail('success');
|
|
83
|
+
}
|
|
84
|
+
return fail(`${id}: not a resource${this.path ? ` in ${this.path}` : ''}.`).withDetail('failure');
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
getBranchById(id) {
|
|
88
|
+
return this.getById(id).onSuccess((node) => {
|
|
89
|
+
if (node.isBranch) {
|
|
90
|
+
return succeed(node).withDetail('success');
|
|
91
|
+
}
|
|
92
|
+
/* c8 ignore next 3 - functional code tested but coverage intermittently missed */
|
|
93
|
+
return fail(`${id}: not a branch${this.path ? ` in ${this.path}` : ''}.`).withDetail('failure');
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=resourceTreeChildren.js.map
|
|
@@ -0,0 +1,161 @@
|
|
|
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 { Validate } from '../../common';
|
|
23
|
+
import { failWithDetail } from '@fgv/ts-utils';
|
|
24
|
+
/**
|
|
25
|
+
* A validator wrapper for resource tree children that validates string inputs before
|
|
26
|
+
* delegating to the underlying tree children collection.
|
|
27
|
+
*
|
|
28
|
+
* This class implements {@link Runtime.ResourceTree.IReadOnlyValidatingResourceTreeChildren | IReadOnlyValidatingResourceTreeChildren}
|
|
29
|
+
* by wrapping an {@link Runtime.ResourceTree.IReadOnlyResourceTreeChildren | IReadOnlyResourceTreeChildren} instance and
|
|
30
|
+
* providing string-based access to all tree operations. All string inputs are validated using the library's
|
|
31
|
+
* validation utilities before being passed to the underlying collection.
|
|
32
|
+
*
|
|
33
|
+
* The validator acts as a bridge between string-based external APIs and the
|
|
34
|
+
* strongly-typed internal tree operations, ensuring type safety and consistent
|
|
35
|
+
* error handling throughout the resource tree navigation.
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
export class ResourceTreeChildrenValidator {
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new validator wrapper for resource tree children.
|
|
42
|
+
* @param inner - The underlying resource tree children collection to wrap with validation
|
|
43
|
+
*/
|
|
44
|
+
constructor(inner) {
|
|
45
|
+
this._inner = inner;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Gets a tree node by its string ResourceId path, validating the input.
|
|
49
|
+
* @param id - The string ResourceId path to validate and look up
|
|
50
|
+
* @returns Result containing the node if found, or failure if validation fails or not found
|
|
51
|
+
*/
|
|
52
|
+
getById(id) {
|
|
53
|
+
return Validate.toResourceId(id).onSuccess((resourceId) => {
|
|
54
|
+
return this._inner.getById(resourceId);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Gets a resource node by its string name (single component), validating the input.
|
|
59
|
+
* @param name - The string ResourceName to validate and look up
|
|
60
|
+
* @returns Result containing the node if it's a resource, or failure if validation fails or not found
|
|
61
|
+
*/
|
|
62
|
+
getResource(name) {
|
|
63
|
+
return Validate.toResourceName(name).onSuccess((resourceName) => {
|
|
64
|
+
return this._inner.getResource(resourceName);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Gets a branch node by its string name (single component), validating the input.
|
|
69
|
+
* @param name - The string ResourceName to validate and look up
|
|
70
|
+
* @returns Result containing the node if it's a branch, or failure if validation fails or not found
|
|
71
|
+
*/
|
|
72
|
+
getBranch(name) {
|
|
73
|
+
return Validate.toResourceName(name).onSuccess((resourceName) => {
|
|
74
|
+
return this._inner.getBranch(resourceName);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Gets a resource leaf node by its string ResourceId path, validating the input.
|
|
79
|
+
* @param id - The string ResourceId path to validate and look up
|
|
80
|
+
* @returns Result containing the leaf if found and is a resource, or failure otherwise
|
|
81
|
+
*/
|
|
82
|
+
getResourceById(id) {
|
|
83
|
+
return Validate.toResourceId(id).onSuccess((resourceId) => {
|
|
84
|
+
return this._inner.getResourceById(resourceId);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Gets a branch node by its string ResourceId path, validating the input.
|
|
89
|
+
* @param id - The string ResourceId path to validate and look up
|
|
90
|
+
* @returns Result containing the branch if found and has children, or failure otherwise
|
|
91
|
+
*/
|
|
92
|
+
getBranchById(id) {
|
|
93
|
+
return Validate.toResourceId(id).onSuccess((resourceId) => {
|
|
94
|
+
return this._inner.getBranchById(resourceId);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The number of direct child nodes in this collection.
|
|
99
|
+
*/
|
|
100
|
+
get size() {
|
|
101
|
+
return this._inner.size;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Returns an iterator of [ResourceName, node] pairs for all child nodes.
|
|
105
|
+
* @returns Map iterator for all child nodes
|
|
106
|
+
*/
|
|
107
|
+
entries() {
|
|
108
|
+
return this._inner.entries();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Executes a callback function for each child node in the collection.
|
|
112
|
+
* @param cb - The callback function to execute for each child node
|
|
113
|
+
* @param arg - Optional argument to pass to the callback
|
|
114
|
+
*/
|
|
115
|
+
forEach(cb, arg) {
|
|
116
|
+
for (const [key, value] of this._inner.entries()) {
|
|
117
|
+
cb(value, key, this, arg);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Gets a child node by its string key with detailed error information.
|
|
122
|
+
* @param key - The string key to look up
|
|
123
|
+
* @returns DetailedResult containing the node if found, or failure with details
|
|
124
|
+
*/
|
|
125
|
+
get(key) {
|
|
126
|
+
if (Validate.isValidResourceName(key)) {
|
|
127
|
+
return this._inner.get(key);
|
|
128
|
+
}
|
|
129
|
+
return failWithDetail(`${key}: invalid resource name.`, 'invalid-key');
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Checks if a child node exists at the given string key.
|
|
133
|
+
* @param key - The string key to check
|
|
134
|
+
* @returns True if a child node exists at the key, false otherwise
|
|
135
|
+
*/
|
|
136
|
+
has(key) {
|
|
137
|
+
return this._inner.has(key);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Returns an iterator of ResourceName keys for all child nodes.
|
|
141
|
+
* @returns Map iterator for all child node keys
|
|
142
|
+
*/
|
|
143
|
+
keys() {
|
|
144
|
+
return this._inner.keys();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Returns an iterator of child node values.
|
|
148
|
+
* @returns Map iterator for all child node values
|
|
149
|
+
*/
|
|
150
|
+
values() {
|
|
151
|
+
return this._inner.values();
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Returns an iterator for [ResourceName, node] pairs, enabling for...of iteration.
|
|
155
|
+
* @returns Iterable iterator for all child nodes
|
|
156
|
+
*/
|
|
157
|
+
[Symbol.iterator]() {
|
|
158
|
+
return this._inner[Symbol.iterator]();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=resourceTreeChildrenValidator.js.map
|