@fgv/ts-res 5.0.0-23 → 5.0.0-25

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.
Files changed (111) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/ts-res.d.ts +154 -0
  3. package/lib/packlets/runtime/index.d.ts +1 -0
  4. package/lib/packlets/runtime/index.js +1 -0
  5. package/lib/packlets/runtime/resourceTreeResolver.d.ts +152 -0
  6. package/lib/packlets/runtime/resourceTreeResolver.js +222 -0
  7. package/package.json +12 -12
  8. package/.vscode/4.json +0 -3
  9. package/.vscode/launch.json +0 -16
  10. package/.vscode/settings.json +0 -3
  11. package/config/api-extractor.json +0 -343
  12. package/config/rig.json +0 -16
  13. package/lib/test/data/sample.json +0 -32
  14. package/lib/test/unit/bundle/bundleBuilder.test.d.ts +0 -2
  15. package/lib/test/unit/bundle/bundleLoader.test.d.ts +0 -2
  16. package/lib/test/unit/bundle/bundleUtils.test.d.ts +0 -2
  17. package/lib/test/unit/bundle/convert.test.d.ts +0 -2
  18. package/lib/test/unit/bundle/integration.test.d.ts +0 -2
  19. package/lib/test/unit/bundle/model.test.d.ts +0 -2
  20. package/lib/test/unit/bundle/normalization.test.d.ts +0 -2
  21. package/lib/test/unit/common/conditions.test.d.ts +0 -2
  22. package/lib/test/unit/common/helpers/conditions.test.d.ts +0 -2
  23. package/lib/test/unit/common/helpers/contexts.test.d.ts +0 -2
  24. package/lib/test/unit/common/helpers/qualifierDefaultValues.test.d.ts +0 -2
  25. package/lib/test/unit/common/resources.test.d.ts +0 -2
  26. package/lib/test/unit/conditions/condition.test.d.ts +0 -2
  27. package/lib/test/unit/conditions/conditionCollector.test.d.ts +0 -2
  28. package/lib/test/unit/conditions/conditionScoreAsDefault.test.d.ts +0 -2
  29. package/lib/test/unit/conditions/conditionSet.test.d.ts +0 -2
  30. package/lib/test/unit/conditions/conditionSetCollector.test.d.ts +0 -2
  31. package/lib/test/unit/conditions/conditionToken.test.d.ts +0 -2
  32. package/lib/test/unit/conditions/convert.test.d.ts +0 -2
  33. package/lib/test/unit/config/common.test.d.ts +0 -2
  34. package/lib/test/unit/config/convert.test.d.ts +0 -2
  35. package/lib/test/unit/config/predefined/default.test.d.ts +0 -2
  36. package/lib/test/unit/config/predefined/extended.test.d.ts +0 -2
  37. package/lib/test/unit/config/systemConfiguration.test.d.ts +0 -2
  38. package/lib/test/unit/config/systemConfigurationInitParams.test.d.ts +0 -2
  39. package/lib/test/unit/context/contextToken.test.d.ts +0 -2
  40. package/lib/test/unit/context/convert.test.d.ts +0 -2
  41. package/lib/test/unit/decisions/abstractDecision.test.d.ts +0 -2
  42. package/lib/test/unit/decisions/abstractDecisionCollector.test.d.ts +0 -2
  43. package/lib/test/unit/decisions/candidate.test.d.ts +0 -2
  44. package/lib/test/unit/decisions/concreteDecision.test.d.ts +0 -2
  45. package/lib/test/unit/decisions/decision.test.d.ts +0 -2
  46. package/lib/test/unit/import/collectionImporter.test.d.ts +0 -2
  47. package/lib/test/unit/import/fsItem.test.d.ts +0 -2
  48. package/lib/test/unit/import/fsItemImporter.test.d.ts +0 -2
  49. package/lib/test/unit/import/importContext.test.d.ts +0 -2
  50. package/lib/test/unit/import/importManager.test.d.ts +0 -2
  51. package/lib/test/unit/import/jsonImporter.test.d.ts +0 -2
  52. package/lib/test/unit/import/pathImporter.test.d.ts +0 -2
  53. package/lib/test/unit/qualifier-types/convert.test.d.ts +0 -2
  54. package/lib/test/unit/qualifier-types/helpers.test.d.ts +0 -2
  55. package/lib/test/unit/qualifier-types/languageQualifierType.test.d.ts +0 -2
  56. package/lib/test/unit/qualifier-types/literalQualifierType.test.d.ts +0 -2
  57. package/lib/test/unit/qualifier-types/literalValueHierarchy.test.d.ts +0 -2
  58. package/lib/test/unit/qualifier-types/qualifierType.test.d.ts +0 -2
  59. package/lib/test/unit/qualifier-types/qualifierTypeCollector.test.d.ts +0 -2
  60. package/lib/test/unit/qualifier-types/territoryQualifierType.test.d.ts +0 -2
  61. package/lib/test/unit/qualifier-types/testQualifierType.d.ts +0 -19
  62. package/lib/test/unit/qualifier-types/testQualifierType.js +0 -113
  63. package/lib/test/unit/qualifiers/convert.test.d.ts +0 -2
  64. package/lib/test/unit/qualifiers/qualifierCollector.test.d.ts +0 -2
  65. package/lib/test/unit/qualifiers/qualifierDefaultValue.test.d.ts +0 -2
  66. package/lib/test/unit/qualifiers/qualifierDefaultValueToken.test.d.ts +0 -2
  67. package/lib/test/unit/resource-json/convert.test.d.ts +0 -2
  68. package/lib/test/unit/resource-json/nameInheritance.test.d.ts +0 -2
  69. package/lib/test/unit/resource-json/resourceDeclCollection.test.d.ts +0 -2
  70. package/lib/test/unit/resource-json/resourceDeclTree.test.d.ts +0 -2
  71. package/lib/test/unit/resource-types/derivedResourceType.test.d.ts +0 -2
  72. package/lib/test/unit/resource-types/jsonResourceType.test.d.ts +0 -2
  73. package/lib/test/unit/resource-types/resourceTypeCollector.test.d.ts +0 -2
  74. package/lib/test/unit/resource-types/testDerivedResourceType.d.ts +0 -79
  75. package/lib/test/unit/resource-types/testDerivedResourceType.js +0 -113
  76. package/lib/test/unit/resources/candidateReducer.integration.test.d.ts +0 -2
  77. package/lib/test/unit/resources/candidateReducer.test.d.ts +0 -2
  78. package/lib/test/unit/resources/candidateValue.test.d.ts +0 -2
  79. package/lib/test/unit/resources/candidateValueCollector.test.d.ts +0 -2
  80. package/lib/test/unit/resources/reduceCandidates.test.d.ts +0 -2
  81. package/lib/test/unit/resources/reduceQualifiers.comprehensive.test.d.ts +0 -2
  82. package/lib/test/unit/resources/reduceQualifiers.test.d.ts +0 -2
  83. package/lib/test/unit/resources/resource.test.d.ts +0 -2
  84. package/lib/test/unit/resources/resourceBuilder.test.d.ts +0 -2
  85. package/lib/test/unit/resources/resourceCandidate.test.d.ts +0 -2
  86. package/lib/test/unit/resources/resourceManagerBuilder.clone.test.d.ts +0 -2
  87. package/lib/test/unit/resources/resourceManagerBuilder.getBuiltResourceTree.test.d.ts +0 -2
  88. package/lib/test/unit/resources/resourceManagerBuilder.test.d.ts +0 -2
  89. package/lib/test/unit/runtime/cacheListener.test.d.ts +0 -2
  90. package/lib/test/unit/runtime/cacheMetrics.test.d.ts +0 -2
  91. package/lib/test/unit/runtime/compiledResourceCollection.test.d.ts +0 -2
  92. package/lib/test/unit/runtime/conditionSetResolutionResult.comprehensive.test.d.ts +0 -2
  93. package/lib/test/unit/runtime/conditionSetResolutionResult.test.d.ts +0 -2
  94. package/lib/test/unit/runtime/context/contextQualifierProvider.test.d.ts +0 -2
  95. package/lib/test/unit/runtime/context/contextQualifierProviderValidator.test.d.ts +0 -2
  96. package/lib/test/unit/runtime/context/simpleContextQualifierProvider.test.d.ts +0 -2
  97. package/lib/test/unit/runtime/context/validatingSimpleContextQualifierProvider.test.d.ts +0 -2
  98. package/lib/test/unit/runtime/resource-tree/readOnlyResourceTree.test.d.ts +0 -2
  99. package/lib/test/unit/runtime/resource-tree/resourceTreeChildren.test.d.ts +0 -2
  100. package/lib/test/unit/runtime/resource-tree/resourceTreeChildrenValidator.test.d.ts +0 -2
  101. package/lib/test/unit/runtime/resourceResolver.coverage.test.d.ts +0 -2
  102. package/lib/test/unit/runtime/resourceResolver.defaultResolution.test.d.ts +0 -2
  103. package/lib/test/unit/runtime/resourceResolver.newFeatures.test.d.ts +0 -2
  104. package/lib/test/unit/runtime/resourceResolver.test.d.ts +0 -2
  105. package/lib/test/unit/runtime/validate.test.d.ts +0 -2
  106. package/lib/test/unit/zip-archive/convert.test.d.ts +0 -2
  107. package/lib/test/unit/zip-archive/json.test.d.ts +0 -2
  108. package/lib/test/unit/zip-archive/zipArchiveCreator.test.d.ts +0 -2
  109. package/lib/test/unit/zip-archive/zipArchiveFormat.test.d.ts +0 -2
  110. package/lib/test/unit/zip-archive/zipArchiveIdempotency.test.d.ts +0 -2
  111. package/lib/test/unit/zip-archive/zipArchiveLoader.test.d.ts +0 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # Change Log - @fgv/ts-res
2
+
3
+ This log was last generated on Thu, 17 Jul 2025 00:13:24 GMT and should not be manually modified.
4
+
5
+ ## 5.0.0
6
+ Thu, 17 Jul 2025 00:13:24 GMT
7
+
8
+ ### Updates
9
+
10
+ - add decl generators
11
+ - fix merge errors
12
+ - flesh out tests
13
+ - initial checkin
14
+
package/dist/ts-res.d.ts CHANGED
@@ -2557,6 +2557,17 @@ declare const DefaultResourceTypes: ReadonlyArray<ResourceTypes.Config.IResource
2557
2557
  */
2558
2558
  declare const DefaultSystemConfiguration: ISystemConfiguration;
2559
2559
 
2560
+ /**
2561
+ * Type for handling empty branch nodes during tree composition.
2562
+ * The handler receives the branch node, names of failed children, and the resolver for recovery attempts.
2563
+ * It can return:
2564
+ * - Success(undefined) to omit the branch from the result
2565
+ * - Success(value) to use an alternate value for the branch
2566
+ * - Failure to propagate the error
2567
+ * @public
2568
+ */
2569
+ declare type EmptyBranchHandler = (branchNode: IReadOnlyResourceTreeNode<IResource>, failedChildNames: string[], resolver: ResourceResolver) => Result<JsonValue | undefined>;
2570
+
2560
2571
  declare namespace Example {
2561
2572
  export {
2562
2573
  ExtendedQualifierTypes,
@@ -4892,6 +4903,29 @@ declare interface IReducedCandidate {
4892
4903
  readonly mergeMethod?: ResourceValueMergeMethod;
4893
4904
  }
4894
4905
 
4906
+ /**
4907
+ * Options for configuring resource tree resolution.
4908
+ * @public
4909
+ */
4910
+ declare interface IResolveResourceTreeOptions {
4911
+ /**
4912
+ * Controls how errors are handled when resolving individual resources in the tree.
4913
+ * - 'fail': Aggregate all errors and fail if any resource fails to resolve
4914
+ * - 'ignore': Skip failed resources and omit them from the result
4915
+ * - callback: Custom error handler that can provide alternate values or propagate errors
4916
+ * @defaultValue 'fail'
4917
+ */
4918
+ onResourceError?: 'fail' | 'ignore' | ResourceErrorHandler;
4919
+ /**
4920
+ * Controls how empty branch nodes are handled during tree composition.
4921
+ * - 'allow': Include empty branches as empty objects in the result
4922
+ * - 'omit': Exclude empty branches from the parent object
4923
+ * - callback: Custom handler that can provide alternate values or recovery logic
4924
+ * @defaultValue 'allow'
4925
+ */
4926
+ onEmptyBranch?: 'allow' | 'omit' | EmptyBranchHandler;
4927
+ }
4928
+
4895
4929
  /**
4896
4930
  * Interface for a resource that can be used in the runtime layer.
4897
4931
  * This provides the minimal properties needed from a resource without requiring
@@ -8107,6 +8141,17 @@ declare class ResourceDeclTree implements IResourceDeclContainer {
8107
8141
  private _extract;
8108
8142
  }
8109
8143
 
8144
+ /**
8145
+ * Type for handling resource resolution errors during tree traversal.
8146
+ * The handler receives the resource that failed to resolve, the error message, and the resolver for recovery attempts.
8147
+ * It can return:
8148
+ * - Success(undefined) to omit the property from the result
8149
+ * - Success(value) to use an alternate value
8150
+ * - Failure to propagate the error
8151
+ * @public
8152
+ */
8153
+ declare type ResourceErrorHandler = (resource: IResource, message: string, resolver: ResourceResolver) => Result<JsonValue | undefined>;
8154
+
8110
8155
  /**
8111
8156
  * Branded string representing a validated resource id. A resource ID
8112
8157
  * is a dot-separated sequence of resource names.
@@ -8936,6 +8981,111 @@ declare class ResourceTreeChildrenValidator<T> implements IReadOnlyResourceTreeC
8936
8981
  */
8937
8982
  declare type ResourceTreeNodeInit<T> = IResourceTreeLeafInit<T> | IResourceTreeBranchInit<T>;
8938
8983
 
8984
+ /**
8985
+ * Specialized resolver for resource tree operations, providing enhanced APIs for
8986
+ * resolving entire resource trees from either resource IDs or pre-built tree nodes.
8987
+ *
8988
+ * This class provides a clean separation between individual resource resolution
8989
+ * (handled by ResourceResolver) and tree-based operations, with support for
8990
+ * lazy tree construction and enhanced error handling.
8991
+ *
8992
+ * @public
8993
+ */
8994
+ declare class ResourceTreeResolver {
8995
+ /**
8996
+ * The {@link Runtime.ResourceResolver | ResourceResolver} to use for individual resource resolution
8997
+ */
8998
+ readonly resolver: ResourceResolver;
8999
+ /**
9000
+ * The {@link Runtime.IResourceManager | IResourceManager} to use for lazy tree construction.
9001
+ * @internal
9002
+ */
9003
+ private readonly _resourceManager;
9004
+ /**
9005
+ * The built resource tree, lazily built on first access.
9006
+ * @internal
9007
+ */
9008
+ private _tree?;
9009
+ /**
9010
+ * Creates a {@link Runtime.ResourceTreeResolver | ResourceTreeResolver} instance.
9011
+ * @param resolver - The ResourceResolver to use for individual resource resolution
9012
+ */
9013
+ constructor(resolver: ResourceResolver);
9014
+ /**
9015
+ * Creates a {@link Runtime.ResourceTreeResolver | ResourceTreeResolver} instance.
9016
+ * @param resolver - The ResourceResolver to use for individual resource resolution
9017
+ */
9018
+ static create(resolver: ResourceResolver): Result<ResourceTreeResolver>;
9019
+ /**
9020
+ * Gets the built resource tree, building it lazily on first access.
9021
+ * @returns The resource tree root
9022
+ * @throws Error if no resource manager was provided or tree building fails
9023
+ * @public
9024
+ */
9025
+ get tree(): IReadOnlyResourceTreeRoot<IResource>;
9026
+ /**
9027
+ * Resolves a resource tree from a resource ID, building the tree lazily from the resource manager.
9028
+ * @param resourceId - The ID of the root resource to resolve
9029
+ * @param options - Optional configuration for error handling during resolution
9030
+ * @returns Success with the composed JsonObject or undefined, or Failure with error message
9031
+ * @public
9032
+ */
9033
+ resolveComposedResourceTree(resourceId: string, options?: IResolveResourceTreeOptions): Result<JsonObject | undefined>;
9034
+ /**
9035
+ * Resolves a pre-built resource tree node.
9036
+ * @param node - The resource tree node to resolve
9037
+ * @param options - Optional configuration for error handling during resolution
9038
+ * @returns Success with the composed JsonObject or undefined, or Failure with error message
9039
+ * @public
9040
+ */
9041
+ resolveComposedResourceTree(node: IReadOnlyResourceTreeNode<IResource>, options?: IResolveResourceTreeOptions): Result<JsonObject | undefined>;
9042
+ private _getTree;
9043
+ /**
9044
+ * Resolves a tree from a resource ID by first building the tree from the resource manager.
9045
+ * @internal
9046
+ */
9047
+ private _resolveFromResourceId;
9048
+ /**
9049
+ * Resolves a pre-built tree node using the extracted tree resolution logic.
9050
+ * @internal
9051
+ */
9052
+ private _resolveFromTreeNode;
9053
+ /**
9054
+ * Handles resource resolution errors according to the specified mode.
9055
+ * @param resource - The resource that failed to resolve
9056
+ * @param message - The error message from the failed resolution
9057
+ * @param mode - The error handling mode
9058
+ * @param path - The path to the resource in the tree (for error context)
9059
+ * @internal
9060
+ */
9061
+ private _handleResourceError;
9062
+ /**
9063
+ * Handles empty branch nodes according to the specified mode.
9064
+ * @param node - The empty branch node
9065
+ * @param failedChildren - Names of children that failed to resolve
9066
+ * @param mode - The empty branch handling mode
9067
+ * @param path - The path to the branch in the tree (for error context)
9068
+ * @internal
9069
+ */
9070
+ private _handleEmptyBranch;
9071
+ /**
9072
+ * Processes a leaf node by resolving its resource value.
9073
+ * @param node - The leaf node to process (must be a leaf node)
9074
+ * @param path - The path to the node in the tree
9075
+ * @param resourceErrorMode - How to handle resource resolution errors
9076
+ * @internal
9077
+ */
9078
+ private _processLeafNode;
9079
+ /**
9080
+ * Processes a branch node by recursively resolving all its children.
9081
+ * @param node - The branch node to process (must be a branch node)
9082
+ * @param path - The path to the node in the tree
9083
+ * @param options - Resolution options
9084
+ * @internal
9085
+ */
9086
+ private _processBranchNode;
9087
+ }
9088
+
8939
9089
  /**
8940
9090
  * `Converter` for a normalized {@link ResourceJson.Normalized.IResourceTreeRootDecl | resource tree root declaration}.
8941
9091
  * @public
@@ -9143,6 +9293,10 @@ declare namespace Runtime {
9143
9293
  IResourceResolverOptions,
9144
9294
  IResourceResolverCreateParams,
9145
9295
  ResourceResolver,
9296
+ ResourceErrorHandler,
9297
+ EmptyBranchHandler,
9298
+ IResolveResourceTreeOptions,
9299
+ ResourceTreeResolver,
9146
9300
  ConditionMatchType,
9147
9301
  IConditionMatchResult,
9148
9302
  ConditionSetResolutionResult,
@@ -3,6 +3,7 @@ export * from './iResourceManager';
3
3
  export * from './context/simpleContextQualifierProvider';
4
4
  export * from './context/validatingSimpleContextQualifierProvider';
5
5
  export * from './resourceResolver';
6
+ export * from './resourceTreeResolver';
6
7
  export * from './conditionSetResolutionResult';
7
8
  export * from './cacheListener';
8
9
  export * from './cacheMetrics';
@@ -63,6 +63,7 @@ __exportStar(require("./iResourceManager"), exports);
63
63
  __exportStar(require("./context/simpleContextQualifierProvider"), exports);
64
64
  __exportStar(require("./context/validatingSimpleContextQualifierProvider"), exports);
65
65
  __exportStar(require("./resourceResolver"), exports);
66
+ __exportStar(require("./resourceTreeResolver"), exports);
66
67
  __exportStar(require("./conditionSetResolutionResult"), exports);
67
68
  __exportStar(require("./cacheListener"), exports);
68
69
  __exportStar(require("./cacheMetrics"), exports);
@@ -0,0 +1,152 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { JsonValue, JsonObject } from '@fgv/ts-json-base';
3
+ import { IResource } from './iResourceManager';
4
+ import { IReadOnlyResourceTreeNode, IReadOnlyResourceTreeRoot } from './resource-tree/common';
5
+ import { ResourceResolver } from './resourceResolver';
6
+ /**
7
+ * Type for handling resource resolution errors during tree traversal.
8
+ * The handler receives the resource that failed to resolve, the error message, and the resolver for recovery attempts.
9
+ * It can return:
10
+ * - Success(undefined) to omit the property from the result
11
+ * - Success(value) to use an alternate value
12
+ * - Failure to propagate the error
13
+ * @public
14
+ */
15
+ export type ResourceErrorHandler = (resource: IResource, message: string, resolver: ResourceResolver) => Result<JsonValue | undefined>;
16
+ /**
17
+ * Type for handling empty branch nodes during tree composition.
18
+ * The handler receives the branch node, names of failed children, and the resolver for recovery attempts.
19
+ * It can return:
20
+ * - Success(undefined) to omit the branch from the result
21
+ * - Success(value) to use an alternate value for the branch
22
+ * - Failure to propagate the error
23
+ * @public
24
+ */
25
+ export type EmptyBranchHandler = (branchNode: IReadOnlyResourceTreeNode<IResource>, failedChildNames: string[], resolver: ResourceResolver) => Result<JsonValue | undefined>;
26
+ /**
27
+ * Options for configuring resource tree resolution.
28
+ * @public
29
+ */
30
+ export interface IResolveResourceTreeOptions {
31
+ /**
32
+ * Controls how errors are handled when resolving individual resources in the tree.
33
+ * - 'fail': Aggregate all errors and fail if any resource fails to resolve
34
+ * - 'ignore': Skip failed resources and omit them from the result
35
+ * - callback: Custom error handler that can provide alternate values or propagate errors
36
+ * @defaultValue 'fail'
37
+ */
38
+ onResourceError?: 'fail' | 'ignore' | ResourceErrorHandler;
39
+ /**
40
+ * Controls how empty branch nodes are handled during tree composition.
41
+ * - 'allow': Include empty branches as empty objects in the result
42
+ * - 'omit': Exclude empty branches from the parent object
43
+ * - callback: Custom handler that can provide alternate values or recovery logic
44
+ * @defaultValue 'allow'
45
+ */
46
+ onEmptyBranch?: 'allow' | 'omit' | EmptyBranchHandler;
47
+ }
48
+ /**
49
+ * Specialized resolver for resource tree operations, providing enhanced APIs for
50
+ * resolving entire resource trees from either resource IDs or pre-built tree nodes.
51
+ *
52
+ * This class provides a clean separation between individual resource resolution
53
+ * (handled by ResourceResolver) and tree-based operations, with support for
54
+ * lazy tree construction and enhanced error handling.
55
+ *
56
+ * @public
57
+ */
58
+ export declare class ResourceTreeResolver {
59
+ /**
60
+ * The {@link Runtime.ResourceResolver | ResourceResolver} to use for individual resource resolution
61
+ */
62
+ readonly resolver: ResourceResolver;
63
+ /**
64
+ * The {@link Runtime.IResourceManager | IResourceManager} to use for lazy tree construction.
65
+ * @internal
66
+ */
67
+ private readonly _resourceManager;
68
+ /**
69
+ * The built resource tree, lazily built on first access.
70
+ * @internal
71
+ */
72
+ private _tree?;
73
+ /**
74
+ * Creates a {@link Runtime.ResourceTreeResolver | ResourceTreeResolver} instance.
75
+ * @param resolver - The ResourceResolver to use for individual resource resolution
76
+ */
77
+ constructor(resolver: ResourceResolver);
78
+ /**
79
+ * Creates a {@link Runtime.ResourceTreeResolver | ResourceTreeResolver} instance.
80
+ * @param resolver - The ResourceResolver to use for individual resource resolution
81
+ */
82
+ static create(resolver: ResourceResolver): Result<ResourceTreeResolver>;
83
+ /**
84
+ * Gets the built resource tree, building it lazily on first access.
85
+ * @returns The resource tree root
86
+ * @throws Error if no resource manager was provided or tree building fails
87
+ * @public
88
+ */
89
+ get tree(): IReadOnlyResourceTreeRoot<IResource>;
90
+ /**
91
+ * Resolves a resource tree from a resource ID, building the tree lazily from the resource manager.
92
+ * @param resourceId - The ID of the root resource to resolve
93
+ * @param options - Optional configuration for error handling during resolution
94
+ * @returns Success with the composed JsonObject or undefined, or Failure with error message
95
+ * @public
96
+ */
97
+ resolveComposedResourceTree(resourceId: string, options?: IResolveResourceTreeOptions): Result<JsonObject | undefined>;
98
+ /**
99
+ * Resolves a pre-built resource tree node.
100
+ * @param node - The resource tree node to resolve
101
+ * @param options - Optional configuration for error handling during resolution
102
+ * @returns Success with the composed JsonObject or undefined, or Failure with error message
103
+ * @public
104
+ */
105
+ resolveComposedResourceTree(node: IReadOnlyResourceTreeNode<IResource>, options?: IResolveResourceTreeOptions): Result<JsonObject | undefined>;
106
+ private _getTree;
107
+ /**
108
+ * Resolves a tree from a resource ID by first building the tree from the resource manager.
109
+ * @internal
110
+ */
111
+ private _resolveFromResourceId;
112
+ /**
113
+ * Resolves a pre-built tree node using the extracted tree resolution logic.
114
+ * @internal
115
+ */
116
+ private _resolveFromTreeNode;
117
+ /**
118
+ * Handles resource resolution errors according to the specified mode.
119
+ * @param resource - The resource that failed to resolve
120
+ * @param message - The error message from the failed resolution
121
+ * @param mode - The error handling mode
122
+ * @param path - The path to the resource in the tree (for error context)
123
+ * @internal
124
+ */
125
+ private _handleResourceError;
126
+ /**
127
+ * Handles empty branch nodes according to the specified mode.
128
+ * @param node - The empty branch node
129
+ * @param failedChildren - Names of children that failed to resolve
130
+ * @param mode - The empty branch handling mode
131
+ * @param path - The path to the branch in the tree (for error context)
132
+ * @internal
133
+ */
134
+ private _handleEmptyBranch;
135
+ /**
136
+ * Processes a leaf node by resolving its resource value.
137
+ * @param node - The leaf node to process (must be a leaf node)
138
+ * @param path - The path to the node in the tree
139
+ * @param resourceErrorMode - How to handle resource resolution errors
140
+ * @internal
141
+ */
142
+ private _processLeafNode;
143
+ /**
144
+ * Processes a branch node by recursively resolving all its children.
145
+ * @param node - The branch node to process (must be a branch node)
146
+ * @param path - The path to the node in the tree
147
+ * @param options - Resolution options
148
+ * @internal
149
+ */
150
+ private _processBranchNode;
151
+ }
152
+ //# sourceMappingURL=resourceTreeResolver.d.ts.map
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2025 Erik Fortune
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.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.ResourceTreeResolver = void 0;
25
+ const ts_utils_1 = require("@fgv/ts-utils");
26
+ const ts_json_base_1 = require("@fgv/ts-json-base");
27
+ /**
28
+ * Specialized resolver for resource tree operations, providing enhanced APIs for
29
+ * resolving entire resource trees from either resource IDs or pre-built tree nodes.
30
+ *
31
+ * This class provides a clean separation between individual resource resolution
32
+ * (handled by ResourceResolver) and tree-based operations, with support for
33
+ * lazy tree construction and enhanced error handling.
34
+ *
35
+ * @public
36
+ */
37
+ class ResourceTreeResolver {
38
+ /**
39
+ * Creates a {@link Runtime.ResourceTreeResolver | ResourceTreeResolver} instance.
40
+ * @param resolver - The ResourceResolver to use for individual resource resolution
41
+ */
42
+ constructor(resolver) {
43
+ this.resolver = resolver;
44
+ this._resourceManager = resolver.resourceManager;
45
+ }
46
+ /**
47
+ * Creates a {@link Runtime.ResourceTreeResolver | ResourceTreeResolver} instance.
48
+ * @param resolver - The ResourceResolver to use for individual resource resolution
49
+ */
50
+ static create(resolver) {
51
+ return (0, ts_utils_1.captureResult)(() => new ResourceTreeResolver(resolver));
52
+ }
53
+ /**
54
+ * Gets the built resource tree, building it lazily on first access.
55
+ * @returns The resource tree root
56
+ * @throws Error if no resource manager was provided or tree building fails
57
+ * @public
58
+ */
59
+ get tree() {
60
+ return this._getTree().orThrow();
61
+ }
62
+ /**
63
+ * Implementation for both overloads.
64
+ */
65
+ resolveComposedResourceTree(idOrNode, options) {
66
+ if (typeof idOrNode === 'string') {
67
+ return this._resolveFromResourceId(idOrNode, options);
68
+ }
69
+ return this._resolveFromTreeNode(idOrNode, options);
70
+ }
71
+ _getTree() {
72
+ if (!this._tree) {
73
+ this._tree = this._resourceManager.getBuiltResourceTree();
74
+ }
75
+ return this._tree;
76
+ }
77
+ /**
78
+ * Resolves a tree from a resource ID by first building the tree from the resource manager.
79
+ * @internal
80
+ */
81
+ _resolveFromResourceId(resourceId, options) {
82
+ return this._getTree().onSuccess((tree) => {
83
+ return tree.children.validating
84
+ .getById(resourceId)
85
+ .onFailure((message) => (0, ts_utils_1.fail)(`${resourceId}: Resource not found in resource tree: ${message}`))
86
+ .onSuccess((rootNode) => this._resolveFromTreeNode(rootNode, options));
87
+ });
88
+ }
89
+ /**
90
+ * Resolves a pre-built tree node using the extracted tree resolution logic.
91
+ * @internal
92
+ */
93
+ _resolveFromTreeNode(node, options) {
94
+ var _a, _b;
95
+ const resourceErrorMode = (_a = options === null || options === void 0 ? void 0 : options.onResourceError) !== null && _a !== void 0 ? _a : 'fail';
96
+ const emptyBranchMode = (_b = options === null || options === void 0 ? void 0 : options.onEmptyBranch) !== null && _b !== void 0 ? _b : 'allow';
97
+ // Handle root node with proper Result chaining
98
+ return node.isLeaf
99
+ ? this._processLeafNode(node, '', resourceErrorMode).onSuccess((value) => value !== undefined ? ts_json_base_1.Converters.jsonObject.convert(value) : (0, ts_utils_1.succeed)(undefined))
100
+ : this._processBranchNode(node, '', {
101
+ onResourceError: resourceErrorMode,
102
+ onEmptyBranch: emptyBranchMode
103
+ }).onSuccess((value) => {
104
+ // Only convert undefined to {} if emptyBranchMode is 'allow'
105
+ // For 'omit' mode or custom handlers returning undefined, preserve undefined
106
+ return (0, ts_utils_1.succeed)(value === undefined && emptyBranchMode === 'allow' ? {} : value);
107
+ });
108
+ }
109
+ /**
110
+ * Handles resource resolution errors according to the specified mode.
111
+ * @param resource - The resource that failed to resolve
112
+ * @param message - The error message from the failed resolution
113
+ * @param mode - The error handling mode
114
+ * @param path - The path to the resource in the tree (for error context)
115
+ * @internal
116
+ */
117
+ _handleResourceError(resource, message, mode, path) {
118
+ if (mode === 'ignore') {
119
+ return (0, ts_utils_1.succeed)(undefined);
120
+ }
121
+ if (mode === 'fail') {
122
+ const errorMessage = path ? `${path}: ${message}` : message;
123
+ return (0, ts_utils_1.fail)(errorMessage);
124
+ }
125
+ // Custom handler - pass the resolver for recovery attempts
126
+ return mode(resource, message, this.resolver);
127
+ }
128
+ /**
129
+ * Handles empty branch nodes according to the specified mode.
130
+ * @param node - The empty branch node
131
+ * @param failedChildren - Names of children that failed to resolve
132
+ * @param mode - The empty branch handling mode
133
+ * @param path - The path to the branch in the tree (for error context)
134
+ * @internal
135
+ */
136
+ _handleEmptyBranch(node, failedChildren, mode, path) {
137
+ if (mode === 'omit') {
138
+ return (0, ts_utils_1.succeed)(undefined);
139
+ }
140
+ if (mode === 'allow') {
141
+ return (0, ts_utils_1.succeed)({});
142
+ }
143
+ // Custom handler - pass the resolver for recovery attempts
144
+ return mode(node, failedChildren, this.resolver);
145
+ }
146
+ /**
147
+ * Processes a leaf node by resolving its resource value.
148
+ * @param node - The leaf node to process (must be a leaf node)
149
+ * @param path - The path to the node in the tree
150
+ * @param resourceErrorMode - How to handle resource resolution errors
151
+ * @internal
152
+ */
153
+ _processLeafNode(node, path, resourceErrorMode) {
154
+ /* c8 ignore next 3 - defense in depth */
155
+ if (!node.isLeaf) {
156
+ return (0, ts_utils_1.fail)(`Internal error: processLeafNode called on non-leaf node at ${path}`);
157
+ }
158
+ return this.resolver
159
+ .resolveComposedResourceValue(node.resource)
160
+ .onSuccess((value) => (0, ts_utils_1.succeed)(value))
161
+ .onFailure((message) => this._handleResourceError(node.resource, message, resourceErrorMode, path));
162
+ }
163
+ /**
164
+ * Processes a branch node by recursively resolving all its children.
165
+ * @param node - The branch node to process (must be a branch node)
166
+ * @param path - The path to the node in the tree
167
+ * @param options - Resolution options
168
+ * @internal
169
+ */
170
+ _processBranchNode(node, path, options) {
171
+ var _a, _b;
172
+ /* c8 ignore next 3 - defense in depth */
173
+ if (node.isLeaf) {
174
+ return (0, ts_utils_1.fail)(`Internal error: processBranchNode called on leaf node at ${path}`);
175
+ }
176
+ const resourceErrorMode = (_a = options.onResourceError) !== null && _a !== void 0 ? _a : 'fail';
177
+ const emptyBranchMode = (_b = options.onEmptyBranch) !== null && _b !== void 0 ? _b : 'allow';
178
+ const aggregator = new ts_utils_1.MessageAggregator();
179
+ // Process all children
180
+ const childResults = {};
181
+ const failedChildren = [];
182
+ for (const [childName, childNode] of node.children) {
183
+ const childPath = path ? `${path}.${childName}` : childName;
184
+ const childResult = childNode.isLeaf
185
+ ? this._processLeafNode(childNode, childPath, resourceErrorMode)
186
+ : this._processBranchNode(childNode, childPath, options);
187
+ // Process the child result and update our state
188
+ if (childResult.isSuccess()) {
189
+ const value = childResult.value;
190
+ if (value !== undefined) {
191
+ childResults[childName] = value;
192
+ }
193
+ else {
194
+ failedChildren.push(childName);
195
+ }
196
+ }
197
+ else {
198
+ if (resourceErrorMode === 'fail') {
199
+ aggregator.addMessage(childResult.message);
200
+ }
201
+ failedChildren.push(childName);
202
+ }
203
+ }
204
+ // Check for accumulated errors in 'fail' mode
205
+ if (resourceErrorMode === 'fail' && aggregator.hasMessages) {
206
+ return (0, ts_utils_1.fail)(aggregator.toString('; '));
207
+ }
208
+ // Handle empty branch
209
+ if (Object.keys(childResults).length === 0) {
210
+ return this._handleEmptyBranch(node, failedChildren, emptyBranchMode, path).onSuccess((value) => {
211
+ if (value === undefined) {
212
+ return (0, ts_utils_1.succeed)(undefined);
213
+ }
214
+ // Ensure we return JsonObject or undefined
215
+ return ts_json_base_1.Converters.jsonObject.convert(value);
216
+ });
217
+ }
218
+ return (0, ts_utils_1.succeed)(childResults);
219
+ }
220
+ }
221
+ exports.ResourceTreeResolver = ResourceTreeResolver;
222
+ //# sourceMappingURL=resourceTreeResolver.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fgv/ts-res",
3
- "version": "5.0.0-23",
3
+ "version": "5.0.0-25",
4
4
  "description": "Multi-dimensional Resource Runtime",
5
5
  "main": "lib/index.js",
6
6
  "types": "dist/ts-res.d.ts",
@@ -36,24 +36,24 @@
36
36
  "rimraf": "^5.0.7",
37
37
  "ts-jest": "^29.4.1",
38
38
  "ts-node": "^10.9.2",
39
- "typescript": "^5.8.3",
39
+ "typescript": "5.8.3",
40
40
  "eslint-plugin-n": "^16.6.2",
41
- "@rushstack/eslint-config": "~4.4.0",
42
- "@rushstack/heft": "~0.74.2",
43
- "@rushstack/heft-jest-plugin": "~0.16.11",
44
- "@rushstack/heft-node-rig": "~2.9.3",
41
+ "@rushstack/eslint-config": "4.4.0",
42
+ "@rushstack/heft": "0.74.3",
43
+ "@rushstack/heft-jest-plugin": "0.16.12",
44
+ "@rushstack/heft-node-rig": "2.9.4",
45
45
  "@types/heft-jest": "1.0.6",
46
46
  "eslint-plugin-tsdoc": "~0.4.0",
47
- "@fgv/ts-utils-jest": "5.0.0-23",
48
- "@fgv/ts-extras": "5.0.0-23"
47
+ "@fgv/ts-extras": "5.0.0-25",
48
+ "@fgv/ts-utils-jest": "5.0.0-25"
49
49
  },
50
50
  "dependencies": {
51
51
  "luxon": "^3.7.1",
52
52
  "fflate": "~0.8.2",
53
- "@fgv/ts-utils": "5.0.0-23",
54
- "@fgv/ts-json-base": "5.0.0-23",
55
- "@fgv/ts-json": "5.0.0-23",
56
- "@fgv/ts-bcp47": "5.0.0-23"
53
+ "@fgv/ts-utils": "5.0.0-25",
54
+ "@fgv/ts-json-base": "5.0.0-25",
55
+ "@fgv/ts-json": "5.0.0-25",
56
+ "@fgv/ts-bcp47": "5.0.0-25"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "heft build --clean",
package/.vscode/4.json DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "cSpell.words": ["fixlint", "luxon", "packlets", "rushstack", "rushx", "TINSTANCE", "xyzzy"]
3
- }
@@ -1,16 +0,0 @@
1
- {
2
- // Use IntelliSense to learn about possible attributes.
3
- // Hover to view descriptions of existing attributes.
4
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
- "version": "0.2.0",
6
- "configurations": [
7
- {
8
- "type": "node",
9
- "request": "launch",
10
- "name": "Launch Program",
11
- "skipFiles": ["<node_internals>/**"],
12
- "program": "${workspaceFolder}/lib/index.js",
13
- "outFiles": ["${workspaceFolder}/**/*.js"]
14
- }
15
- ]
16
- }
@@ -1,3 +0,0 @@
1
- {
2
- "cSpell.words": ["importables"]
3
- }