@fgv/ts-res-ui-components 5.0.0-10

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 (231) hide show
  1. package/.rush/temp/03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log +271 -0
  2. package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +9 -0
  3. package/.rush/temp/operation/build/all.log +9 -0
  4. package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
  5. package/.rush/temp/operation/build/state.json +3 -0
  6. package/.rush/temp/shrinkwrap-deps.json +1111 -0
  7. package/README.md +18 -0
  8. package/REFACTORING_PLAN.md +171 -0
  9. package/config/jest.config.json +16 -0
  10. package/config/jest.setup.js +64 -0
  11. package/config/rig.json +16 -0
  12. package/lib/components/common/QualifierContextControl.d.ts +14 -0
  13. package/lib/components/common/QualifierContextControl.d.ts.map +1 -0
  14. package/lib/components/common/QualifierContextControl.js +78 -0
  15. package/lib/components/common/QualifierContextControl.js.map +1 -0
  16. package/lib/components/common/ResourceListView.d.ts +11 -0
  17. package/lib/components/common/ResourceListView.d.ts.map +1 -0
  18. package/lib/components/common/ResourceListView.js +20 -0
  19. package/lib/components/common/ResourceListView.js.map +1 -0
  20. package/lib/components/common/ResourceTreeView.d.ts +12 -0
  21. package/lib/components/common/ResourceTreeView.d.ts.map +1 -0
  22. package/lib/components/common/ResourceTreeView.js +162 -0
  23. package/lib/components/common/ResourceTreeView.js.map +1 -0
  24. package/lib/components/forms/HierarchyEditor.d.ts +10 -0
  25. package/lib/components/forms/HierarchyEditor.d.ts.map +1 -0
  26. package/lib/components/forms/HierarchyEditor.js +106 -0
  27. package/lib/components/forms/HierarchyEditor.js.map +1 -0
  28. package/lib/components/forms/QualifierEditForm.d.ts +11 -0
  29. package/lib/components/forms/QualifierEditForm.d.ts.map +1 -0
  30. package/lib/components/forms/QualifierEditForm.js +181 -0
  31. package/lib/components/forms/QualifierEditForm.js.map +1 -0
  32. package/lib/components/forms/QualifierTypeEditForm.d.ts +10 -0
  33. package/lib/components/forms/QualifierTypeEditForm.d.ts.map +1 -0
  34. package/lib/components/forms/QualifierTypeEditForm.js +172 -0
  35. package/lib/components/forms/QualifierTypeEditForm.js.map +1 -0
  36. package/lib/components/forms/ResourceTypeEditForm.d.ts +10 -0
  37. package/lib/components/forms/ResourceTypeEditForm.d.ts.map +1 -0
  38. package/lib/components/forms/ResourceTypeEditForm.js +188 -0
  39. package/lib/components/forms/ResourceTypeEditForm.js.map +1 -0
  40. package/lib/components/forms/index.d.ts +9 -0
  41. package/lib/components/forms/index.d.ts.map +1 -0
  42. package/lib/components/forms/index.js +5 -0
  43. package/lib/components/forms/index.js.map +1 -0
  44. package/lib/components/orchestrator/ResourceOrchestrator.d.ts +14 -0
  45. package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -0
  46. package/lib/components/orchestrator/ResourceOrchestrator.js +278 -0
  47. package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -0
  48. package/lib/components/views/CompiledView/index.d.ts +5 -0
  49. package/lib/components/views/CompiledView/index.d.ts.map +1 -0
  50. package/lib/components/views/CompiledView/index.js +595 -0
  51. package/lib/components/views/CompiledView/index.js.map +1 -0
  52. package/lib/components/views/ConfigurationView/index.d.ts +5 -0
  53. package/lib/components/views/ConfigurationView/index.d.ts.map +1 -0
  54. package/lib/components/views/ConfigurationView/index.js +363 -0
  55. package/lib/components/views/ConfigurationView/index.js.map +1 -0
  56. package/lib/components/views/FilterView/index.d.ts +5 -0
  57. package/lib/components/views/FilterView/index.d.ts.map +1 -0
  58. package/lib/components/views/FilterView/index.js +463 -0
  59. package/lib/components/views/FilterView/index.js.map +1 -0
  60. package/lib/components/views/ImportView/index.d.ts +5 -0
  61. package/lib/components/views/ImportView/index.d.ts.map +1 -0
  62. package/lib/components/views/ImportView/index.js +514 -0
  63. package/lib/components/views/ImportView/index.js.map +1 -0
  64. package/lib/components/views/ResolutionView/EditableJsonView.d.ts +21 -0
  65. package/lib/components/views/ResolutionView/EditableJsonView.d.ts.map +1 -0
  66. package/lib/components/views/ResolutionView/EditableJsonView.js +109 -0
  67. package/lib/components/views/ResolutionView/EditableJsonView.js.map +1 -0
  68. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts +19 -0
  69. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts.map +1 -0
  70. package/lib/components/views/ResolutionView/ResolutionEditControls.js +82 -0
  71. package/lib/components/views/ResolutionView/ResolutionEditControls.js.map +1 -0
  72. package/lib/components/views/ResolutionView/index.d.ts +5 -0
  73. package/lib/components/views/ResolutionView/index.d.ts.map +1 -0
  74. package/lib/components/views/ResolutionView/index.js +255 -0
  75. package/lib/components/views/ResolutionView/index.js.map +1 -0
  76. package/lib/components/views/SourceView/index.d.ts +5 -0
  77. package/lib/components/views/SourceView/index.d.ts.map +1 -0
  78. package/lib/components/views/SourceView/index.js +316 -0
  79. package/lib/components/views/SourceView/index.js.map +1 -0
  80. package/lib/components/views/ZipLoaderView/index.d.ts +5 -0
  81. package/lib/components/views/ZipLoaderView/index.d.ts.map +1 -0
  82. package/lib/components/views/ZipLoaderView/index.js +313 -0
  83. package/lib/components/views/ZipLoaderView/index.js.map +1 -0
  84. package/lib/hooks/useConfigurationState.d.ts +46 -0
  85. package/lib/hooks/useConfigurationState.d.ts.map +1 -0
  86. package/lib/hooks/useConfigurationState.js +239 -0
  87. package/lib/hooks/useConfigurationState.js.map +1 -0
  88. package/lib/hooks/useFilterState.d.ts +7 -0
  89. package/lib/hooks/useFilterState.d.ts.map +1 -0
  90. package/lib/hooks/useFilterState.js +80 -0
  91. package/lib/hooks/useFilterState.js.map +1 -0
  92. package/lib/hooks/useResolutionState.d.ts +8 -0
  93. package/lib/hooks/useResolutionState.d.ts.map +1 -0
  94. package/lib/hooks/useResolutionState.js +253 -0
  95. package/lib/hooks/useResolutionState.js.map +1 -0
  96. package/lib/hooks/useResourceData.d.ts +19 -0
  97. package/lib/hooks/useResourceData.d.ts.map +1 -0
  98. package/lib/hooks/useResourceData.js +368 -0
  99. package/lib/hooks/useResourceData.js.map +1 -0
  100. package/lib/hooks/useViewState.d.ts +10 -0
  101. package/lib/hooks/useViewState.d.ts.map +1 -0
  102. package/lib/hooks/useViewState.js +29 -0
  103. package/lib/hooks/useViewState.js.map +1 -0
  104. package/lib/index.d.ts +27 -0
  105. package/lib/index.d.ts.map +1 -0
  106. package/lib/index.js +34 -0
  107. package/lib/index.js.map +1 -0
  108. package/lib/test/helpers/testDataLoader.d.ts +37 -0
  109. package/lib/test/helpers/testDataLoader.d.ts.map +1 -0
  110. package/lib/test/helpers/testDataLoader.js +171 -0
  111. package/lib/test/helpers/testDataLoader.js.map +1 -0
  112. package/lib/test/unit/utils/configurationUtils.test.d.ts +2 -0
  113. package/lib/test/unit/utils/configurationUtils.test.d.ts.map +1 -0
  114. package/lib/test/unit/utils/configurationUtils.test.js +497 -0
  115. package/lib/test/unit/utils/configurationUtils.test.js.map +1 -0
  116. package/lib/test/unit/utils/fileProcessing.test.d.ts +2 -0
  117. package/lib/test/unit/utils/fileProcessing.test.d.ts.map +1 -0
  118. package/lib/test/unit/utils/fileProcessing.test.js +321 -0
  119. package/lib/test/unit/utils/fileProcessing.test.js.map +1 -0
  120. package/lib/test/unit/utils/filterResources.test.d.ts +2 -0
  121. package/lib/test/unit/utils/filterResources.test.d.ts.map +1 -0
  122. package/lib/test/unit/utils/filterResources.test.js +403 -0
  123. package/lib/test/unit/utils/filterResources.test.js.map +1 -0
  124. package/lib/test/unit/utils/resolutionEditing.test.d.ts +2 -0
  125. package/lib/test/unit/utils/resolutionEditing.test.d.ts.map +1 -0
  126. package/lib/test/unit/utils/resolutionEditing.test.js +439 -0
  127. package/lib/test/unit/utils/resolutionEditing.test.js.map +1 -0
  128. package/lib/test/unit/utils/resolutionUtils.test.d.ts +2 -0
  129. package/lib/test/unit/utils/resolutionUtils.test.d.ts.map +1 -0
  130. package/lib/test/unit/utils/resolutionUtils.test.js +397 -0
  131. package/lib/test/unit/utils/resolutionUtils.test.js.map +1 -0
  132. package/lib/test/unit/utils/tsResIntegration.test.d.ts +2 -0
  133. package/lib/test/unit/utils/tsResIntegration.test.d.ts.map +1 -0
  134. package/lib/test/unit/utils/tsResIntegration.test.js +376 -0
  135. package/lib/test/unit/utils/tsResIntegration.test.js.map +1 -0
  136. package/lib/types/index.d.ts +251 -0
  137. package/lib/types/index.d.ts.map +1 -0
  138. package/lib/types/index.js +2 -0
  139. package/lib/types/index.js.map +1 -0
  140. package/lib/utils/configurationUtils.d.ts +74 -0
  141. package/lib/utils/configurationUtils.d.ts.map +1 -0
  142. package/lib/utils/configurationUtils.js +359 -0
  143. package/lib/utils/configurationUtils.js.map +1 -0
  144. package/lib/utils/fileProcessing.d.ts +18 -0
  145. package/lib/utils/fileProcessing.d.ts.map +1 -0
  146. package/lib/utils/fileProcessing.js +142 -0
  147. package/lib/utils/fileProcessing.js.map +1 -0
  148. package/lib/utils/filterResources.d.ts +38 -0
  149. package/lib/utils/filterResources.d.ts.map +1 -0
  150. package/lib/utils/filterResources.js +153 -0
  151. package/lib/utils/filterResources.js.map +1 -0
  152. package/lib/utils/resolutionEditing.d.ts +58 -0
  153. package/lib/utils/resolutionEditing.d.ts.map +1 -0
  154. package/lib/utils/resolutionEditing.js +246 -0
  155. package/lib/utils/resolutionEditing.js.map +1 -0
  156. package/lib/utils/resolutionUtils.d.ts +28 -0
  157. package/lib/utils/resolutionUtils.d.ts.map +1 -0
  158. package/lib/utils/resolutionUtils.js +216 -0
  159. package/lib/utils/resolutionUtils.js.map +1 -0
  160. package/lib/utils/tsResIntegration.d.ts +71 -0
  161. package/lib/utils/tsResIntegration.d.ts.map +1 -0
  162. package/lib/utils/tsResIntegration.js +294 -0
  163. package/lib/utils/tsResIntegration.js.map +1 -0
  164. package/lib/utils/zipLoader/browserZipLoader.d.ts +48 -0
  165. package/lib/utils/zipLoader/browserZipLoader.d.ts.map +1 -0
  166. package/lib/utils/zipLoader/browserZipLoader.js +247 -0
  167. package/lib/utils/zipLoader/browserZipLoader.js.map +1 -0
  168. package/lib/utils/zipLoader/index.d.ts +8 -0
  169. package/lib/utils/zipLoader/index.d.ts.map +1 -0
  170. package/lib/utils/zipLoader/index.js +13 -0
  171. package/lib/utils/zipLoader/index.js.map +1 -0
  172. package/lib/utils/zipLoader/nodeZipBuilder.d.ts +55 -0
  173. package/lib/utils/zipLoader/nodeZipBuilder.d.ts.map +1 -0
  174. package/lib/utils/zipLoader/nodeZipBuilder.js +98 -0
  175. package/lib/utils/zipLoader/nodeZipBuilder.js.map +1 -0
  176. package/lib/utils/zipLoader/types.d.ts +139 -0
  177. package/lib/utils/zipLoader/types.d.ts.map +1 -0
  178. package/lib/utils/zipLoader/types.js +2 -0
  179. package/lib/utils/zipLoader/types.js.map +1 -0
  180. package/lib/utils/zipLoader/zipUtils.d.ts +53 -0
  181. package/lib/utils/zipLoader/zipUtils.d.ts.map +1 -0
  182. package/lib/utils/zipLoader/zipUtils.js +229 -0
  183. package/lib/utils/zipLoader/zipUtils.js.map +1 -0
  184. package/package.json +69 -0
  185. package/rush-logs/ts-res-ui-components.build.cache.log +3 -0
  186. package/rush-logs/ts-res-ui-components.build.log +9 -0
  187. package/src/components/common/QualifierContextControl.tsx +151 -0
  188. package/src/components/common/ResourceListView.tsx +63 -0
  189. package/src/components/common/ResourceTreeView.tsx +271 -0
  190. package/src/components/forms/HierarchyEditor.tsx +204 -0
  191. package/src/components/forms/QualifierEditForm.tsx +355 -0
  192. package/src/components/forms/QualifierTypeEditForm.tsx +347 -0
  193. package/src/components/forms/ResourceTypeEditForm.tsx +331 -0
  194. package/src/components/forms/index.ts +11 -0
  195. package/src/components/orchestrator/ResourceOrchestrator.tsx +372 -0
  196. package/src/components/views/CompiledView/index.tsx +922 -0
  197. package/src/components/views/ConfigurationView/index.tsx +800 -0
  198. package/src/components/views/FilterView/index.tsx +825 -0
  199. package/src/components/views/ImportView/index.tsx +717 -0
  200. package/src/components/views/ResolutionView/EditableJsonView.tsx +214 -0
  201. package/src/components/views/ResolutionView/ResolutionEditControls.tsx +170 -0
  202. package/src/components/views/ResolutionView/index.tsx +591 -0
  203. package/src/components/views/SourceView/index.tsx +536 -0
  204. package/src/components/views/ZipLoaderView/index.tsx +485 -0
  205. package/src/hooks/useConfigurationState.ts +374 -0
  206. package/src/hooks/useFilterState.ts +97 -0
  207. package/src/hooks/useResolutionState.ts +355 -0
  208. package/src/hooks/useResourceData.ts +467 -0
  209. package/src/hooks/useViewState.ts +44 -0
  210. package/src/index.ts +45 -0
  211. package/src/test/helpers/testDataLoader.ts +195 -0
  212. package/src/test/unit/utils/configurationUtils.test.ts +630 -0
  213. package/src/test/unit/utils/fileProcessing.test.ts +391 -0
  214. package/src/test/unit/utils/filterResources.test.ts +574 -0
  215. package/src/test/unit/utils/resolutionEditing.test.ts +556 -0
  216. package/src/test/unit/utils/resolutionUtils.test.ts +521 -0
  217. package/src/test/unit/utils/tsResIntegration.test.ts +433 -0
  218. package/src/types/index.ts +322 -0
  219. package/src/utils/configurationUtils.ts +424 -0
  220. package/src/utils/fileProcessing.ts +160 -0
  221. package/src/utils/filterResources.ts +206 -0
  222. package/src/utils/resolutionEditing.ts +319 -0
  223. package/src/utils/resolutionUtils.ts +289 -0
  224. package/src/utils/tsResIntegration.ts +440 -0
  225. package/src/utils/zipLoader/browserZipLoader.ts +319 -0
  226. package/src/utils/zipLoader/index.ts +26 -0
  227. package/src/utils/zipLoader/nodeZipBuilder.ts +153 -0
  228. package/src/utils/zipLoader/types.ts +175 -0
  229. package/src/utils/zipLoader/zipUtils.ts +266 -0
  230. package/temp/build/typescript/ts_gZid87Hu.json +1 -0
  231. package/tsconfig.json +15 -0
@@ -0,0 +1,153 @@
1
+ import { succeed, fail } from '@fgv/ts-utils';
2
+ import { Runtime, Import } from '@fgv/ts-res';
3
+ // Helper function for conditional debug logging
4
+ const debugLog = (enableDebug, ...args) => {
5
+ if (enableDebug) {
6
+ console.log(...args);
7
+ }
8
+ };
9
+ /**
10
+ * Check if filter values object has any meaningful values
11
+ */
12
+ export function hasFilterValues(values) {
13
+ return Object.values(values).some((value) => value !== undefined && value !== '');
14
+ }
15
+ /**
16
+ * Get a summary string of active filter values
17
+ */
18
+ export function getFilterSummary(values) {
19
+ const activeFilters = Object.entries(values)
20
+ .filter(([, value]) => value !== undefined && value !== '')
21
+ .map(([key, value]) => `${key}=${value}`);
22
+ return activeFilters.length > 0 ? activeFilters.join(', ') : 'No filters';
23
+ }
24
+ /**
25
+ * Creates a filtered resource manager using the ResourceManagerBuilder.clone() method.
26
+ * This is a simplified implementation that leverages the built-in filtering functionality.
27
+ */
28
+ export const createFilteredResourceManagerSimple = async (originalSystem, partialContext, options = { partialContextMatch: true }) => {
29
+ const enableDebug = options.enableDebugLogging === true;
30
+ debugLog(enableDebug, '=== SIMPLE FILTER CREATION ===');
31
+ debugLog(enableDebug, 'Original system:', originalSystem);
32
+ debugLog(enableDebug, 'Partial context:', partialContext);
33
+ // Validate the original system
34
+ if (!originalSystem?.resourceManager) {
35
+ return fail('Original system or resourceManager is undefined');
36
+ }
37
+ // Filter out undefined values from the context before processing
38
+ const filteredContext = Object.fromEntries(Object.entries(partialContext).filter(([, value]) => value !== undefined));
39
+ // Try to use ResourceManagerBuilder.clone() for proper filtering first
40
+ debugLog(enableDebug, 'Using ResourceManagerBuilder for proper filtering');
41
+ debugLog(enableDebug, 'Validating context and cloning manager:', filteredContext);
42
+ const resourceManagerBuilder = originalSystem.resourceManager;
43
+ return resourceManagerBuilder
44
+ .validateContext(filteredContext)
45
+ .onSuccess((validatedContext) => {
46
+ debugLog(enableDebug, 'Context validated, creating clone with context:', validatedContext);
47
+ return resourceManagerBuilder.clone({
48
+ filterForContext: validatedContext,
49
+ reduceQualifiers: options.reduceQualifiers
50
+ });
51
+ })
52
+ .withErrorFormat((e) => `Failed to validate context or clone: ${e}`)
53
+ .onSuccess((filteredManager) => {
54
+ debugLog(enableDebug, 'Filtered manager created:', filteredManager);
55
+ // Create new ImportManager for the filtered system
56
+ return Import.ImportManager.create({
57
+ resources: filteredManager
58
+ })
59
+ .withErrorFormat((e) => `Failed to create filtered import manager: ${e}`)
60
+ .onSuccess((newImportManager) => {
61
+ // Create new ContextQualifierProvider for the filtered system
62
+ return Runtime.ValidatingSimpleContextQualifierProvider.create({
63
+ qualifiers: originalSystem.qualifiers
64
+ })
65
+ .withErrorFormat((e) => `Failed to create filtered context provider: ${e}`)
66
+ .onSuccess((newContextQualifierProvider) => {
67
+ // Build the new system object
68
+ const newSystem = {
69
+ qualifierTypes: originalSystem.qualifierTypes,
70
+ qualifiers: originalSystem.qualifiers,
71
+ resourceTypes: originalSystem.resourceTypes,
72
+ resourceManager: filteredManager,
73
+ importManager: newImportManager,
74
+ contextQualifierProvider: newContextQualifierProvider
75
+ };
76
+ // Get compiled collection from the filtered manager
77
+ return filteredManager
78
+ .getCompiledResourceCollection({ includeMetadata: true })
79
+ .withErrorFormat((e) => `Failed to get compiled collection: ${e}`)
80
+ .onSuccess((compiledCollection) => {
81
+ // Create resolver for the filtered system
82
+ return Runtime.ResourceResolver.create({
83
+ resourceManager: filteredManager,
84
+ qualifierTypes: originalSystem.qualifierTypes,
85
+ contextQualifierProvider: newContextQualifierProvider
86
+ })
87
+ .withErrorFormat((e) => `Failed to create resolver: ${e}`)
88
+ .onSuccess((resolver) => {
89
+ // Create summary
90
+ const resourceIds = Array.from(filteredManager.resources.keys());
91
+ const summary = {
92
+ totalResources: resourceIds.length,
93
+ resourceIds,
94
+ errorCount: 0,
95
+ warnings: []
96
+ };
97
+ const processedResources = {
98
+ system: newSystem,
99
+ compiledCollection,
100
+ resolver,
101
+ resourceCount: resourceIds.length,
102
+ summary
103
+ };
104
+ debugLog(enableDebug, '=== FILTERED PROCESSING COMPLETE ===');
105
+ debugLog(enableDebug, 'Filtered resource count:', resourceIds.length);
106
+ debugLog(enableDebug, 'Filtered resource IDs:', resourceIds);
107
+ return succeed(processedResources);
108
+ });
109
+ });
110
+ });
111
+ });
112
+ })
113
+ .onFailure((error) => {
114
+ debugLog(enableDebug, 'Failed to create filtered resource manager:', error);
115
+ return fail(`Failed to create filtered resource manager: ${error}`);
116
+ });
117
+ };
118
+ /**
119
+ * Analyze filtered resources compared to original resources
120
+ */
121
+ export function analyzeFilteredResources(originalResourceIds, filteredProcessedResources, originalProcessedResources) {
122
+ const filteredResources = [];
123
+ const warnings = [];
124
+ for (const resourceId of originalResourceIds) {
125
+ // Get original resource info
126
+ const originalResourceResult = originalProcessedResources.system.resourceManager.getBuiltResource(resourceId);
127
+ const originalCandidateCount = originalResourceResult.isSuccess()
128
+ ? originalResourceResult.value.candidates.length
129
+ : 0;
130
+ // Get filtered resource info
131
+ const filteredResourceResult = filteredProcessedResources.system.resourceManager.getBuiltResource(resourceId);
132
+ const filteredCandidateCount = filteredResourceResult.isSuccess()
133
+ ? filteredResourceResult.value.candidates.length
134
+ : 0;
135
+ const hasWarning = filteredCandidateCount === 0 && originalCandidateCount > 0;
136
+ if (hasWarning) {
137
+ warnings.push(`Resource ${resourceId} has no matching candidates after filtering`);
138
+ }
139
+ filteredResources.push({
140
+ id: resourceId,
141
+ originalCandidateCount,
142
+ filteredCandidateCount,
143
+ hasWarning
144
+ });
145
+ }
146
+ return {
147
+ success: true,
148
+ filteredResources,
149
+ processedResources: filteredProcessedResources,
150
+ warnings
151
+ };
152
+ }
153
+ //# sourceMappingURL=filterResources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterResources.js","sourceRoot":"","sources":["../../src/utils/filterResources.ts"],"names":[],"mappings":"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","sourcesContent":["import { Result, succeed, fail } from '@fgv/ts-utils';\nimport { Runtime, Import, Resources } from '@fgv/ts-res';\nimport { ProcessedResources } from '../types';\n\nexport interface FilterOptions {\n partialContextMatch?: boolean;\n enableDebugLogging?: boolean;\n reduceQualifiers?: boolean;\n}\n\nexport interface FilteredResource {\n id: string;\n originalCandidateCount: number;\n filteredCandidateCount: number;\n hasWarning: boolean;\n}\n\nexport interface FilterResult {\n success: boolean;\n filteredResources: FilteredResource[];\n processedResources?: ProcessedResources;\n error?: string;\n warnings: string[];\n}\n\n// Helper function for conditional debug logging\nconst debugLog = (enableDebug: boolean, ...args: unknown[]) => {\n if (enableDebug) {\n console.log(...args);\n }\n};\n\n/**\n * Check if filter values object has any meaningful values\n */\nexport function hasFilterValues(values: Record<string, string | undefined>): boolean {\n return Object.values(values).some((value) => value !== undefined && value !== '');\n}\n\n/**\n * Get a summary string of active filter values\n */\nexport function getFilterSummary(values: Record<string, string | undefined>): string {\n const activeFilters = Object.entries(values)\n .filter(([, value]) => value !== undefined && value !== '')\n .map(([key, value]) => `${key}=${value}`);\n return activeFilters.length > 0 ? activeFilters.join(', ') : 'No filters';\n}\n\n/**\n * Creates a filtered resource manager using the ResourceManagerBuilder.clone() method.\n * This is a simplified implementation that leverages the built-in filtering functionality.\n */\nexport const createFilteredResourceManagerSimple = async (\n originalSystem: ProcessedResources['system'],\n partialContext: Record<string, string | undefined>,\n options: FilterOptions = { partialContextMatch: true }\n): Promise<Result<ProcessedResources>> => {\n const enableDebug = options.enableDebugLogging === true;\n\n debugLog(enableDebug, '=== SIMPLE FILTER CREATION ===');\n debugLog(enableDebug, 'Original system:', originalSystem);\n debugLog(enableDebug, 'Partial context:', partialContext);\n\n // Validate the original system\n if (!originalSystem?.resourceManager) {\n return fail('Original system or resourceManager is undefined');\n }\n\n // Filter out undefined values from the context before processing\n const filteredContext = Object.fromEntries(\n Object.entries(partialContext).filter(([, value]) => value !== undefined)\n ) as Record<string, string>;\n\n // Try to use ResourceManagerBuilder.clone() for proper filtering first\n debugLog(enableDebug, 'Using ResourceManagerBuilder for proper filtering');\n debugLog(enableDebug, 'Validating context and cloning manager:', filteredContext);\n const resourceManagerBuilder = originalSystem.resourceManager;\n\n return resourceManagerBuilder\n .validateContext(filteredContext)\n .onSuccess((validatedContext) => {\n debugLog(enableDebug, 'Context validated, creating clone with context:', validatedContext);\n return resourceManagerBuilder.clone({\n filterForContext: validatedContext,\n reduceQualifiers: options.reduceQualifiers\n });\n })\n .withErrorFormat((e) => `Failed to validate context or clone: ${e}`)\n .onSuccess((filteredManager) => {\n debugLog(enableDebug, 'Filtered manager created:', filteredManager);\n\n // Create new ImportManager for the filtered system\n return Import.ImportManager.create({\n resources: filteredManager\n })\n .withErrorFormat((e) => `Failed to create filtered import manager: ${e}`)\n .onSuccess((newImportManager) => {\n // Create new ContextQualifierProvider for the filtered system\n return Runtime.ValidatingSimpleContextQualifierProvider.create({\n qualifiers: originalSystem.qualifiers\n })\n .withErrorFormat((e) => `Failed to create filtered context provider: ${e}`)\n .onSuccess((newContextQualifierProvider) => {\n // Build the new system object\n const newSystem = {\n qualifierTypes: originalSystem.qualifierTypes,\n qualifiers: originalSystem.qualifiers,\n resourceTypes: originalSystem.resourceTypes,\n resourceManager: filteredManager,\n importManager: newImportManager,\n contextQualifierProvider: newContextQualifierProvider\n };\n\n // Get compiled collection from the filtered manager\n return filteredManager\n .getCompiledResourceCollection({ includeMetadata: true })\n .withErrorFormat((e) => `Failed to get compiled collection: ${e}`)\n .onSuccess((compiledCollection) => {\n // Create resolver for the filtered system\n return Runtime.ResourceResolver.create({\n resourceManager: filteredManager,\n qualifierTypes: originalSystem.qualifierTypes,\n contextQualifierProvider: newContextQualifierProvider\n })\n .withErrorFormat((e) => `Failed to create resolver: ${e}`)\n .onSuccess((resolver) => {\n // Create summary\n const resourceIds = Array.from(filteredManager.resources.keys());\n const summary = {\n totalResources: resourceIds.length,\n resourceIds,\n errorCount: 0,\n warnings: [] as string[]\n };\n\n const processedResources: ProcessedResources = {\n system: newSystem,\n compiledCollection,\n resolver,\n resourceCount: resourceIds.length,\n summary\n };\n\n debugLog(enableDebug, '=== FILTERED PROCESSING COMPLETE ===');\n debugLog(enableDebug, 'Filtered resource count:', resourceIds.length);\n debugLog(enableDebug, 'Filtered resource IDs:', resourceIds);\n\n return succeed(processedResources);\n });\n });\n });\n });\n })\n .onFailure((error) => {\n debugLog(enableDebug, 'Failed to create filtered resource manager:', error);\n return fail(`Failed to create filtered resource manager: ${error}`);\n });\n};\n\n/**\n * Analyze filtered resources compared to original resources\n */\nexport function analyzeFilteredResources(\n originalResourceIds: string[],\n filteredProcessedResources: ProcessedResources,\n originalProcessedResources: ProcessedResources\n): FilterResult {\n const filteredResources: FilteredResource[] = [];\n const warnings: string[] = [];\n\n for (const resourceId of originalResourceIds) {\n // Get original resource info\n const originalResourceResult =\n originalProcessedResources.system.resourceManager.getBuiltResource(resourceId);\n const originalCandidateCount = originalResourceResult.isSuccess()\n ? originalResourceResult.value.candidates.length\n : 0;\n\n // Get filtered resource info\n const filteredResourceResult =\n filteredProcessedResources.system.resourceManager.getBuiltResource(resourceId);\n const filteredCandidateCount = filteredResourceResult.isSuccess()\n ? filteredResourceResult.value.candidates.length\n : 0;\n\n const hasWarning = filteredCandidateCount === 0 && originalCandidateCount > 0;\n if (hasWarning) {\n warnings.push(`Resource ${resourceId} has no matching candidates after filtering`);\n }\n\n filteredResources.push({\n id: resourceId,\n originalCandidateCount,\n filteredCandidateCount,\n hasWarning\n });\n }\n\n return {\n success: true,\n filteredResources,\n processedResources: filteredProcessedResources,\n warnings\n };\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { ResourceJson, Resources, Runtime } from '@fgv/ts-res';
3
+ import { ProcessedResources, JsonValue } from '../types';
4
+ export interface EditedResourceInfo {
5
+ resourceId: string;
6
+ originalValue: JsonValue;
7
+ editedValue: JsonValue;
8
+ timestamp: Date;
9
+ }
10
+ export interface EditValidationResult {
11
+ isValid: boolean;
12
+ errors: string[];
13
+ warnings: string[];
14
+ }
15
+ /**
16
+ * Validates an edited resource JSON value
17
+ */
18
+ export declare function validateEditedResource(editedValue: JsonValue): EditValidationResult;
19
+ /**
20
+ * Computes a 3-way diff between base, resolved, and edited values to create minimal delta
21
+ * @param baseValue - The base/original value before resolution (if available)
22
+ * @param resolvedValue - The fully resolved/composed value shown to user
23
+ * @param editedValue - The value after user edits
24
+ * @returns A minimal delta object with only the changes, or null if no changes
25
+ */
26
+ export declare function computeResourceDelta(baseValue: JsonValue | undefined, resolvedValue: JsonValue, editedValue: JsonValue): Result<JsonValue>;
27
+ /**
28
+ * Creates candidate declarations for edited resources with proper delta handling
29
+ */
30
+ export declare function createCandidateDeclarations(editedResources: Map<string, {
31
+ originalValue: JsonValue;
32
+ editedValue: JsonValue;
33
+ delta: JsonValue;
34
+ }>, currentContext: Record<string, string>): ResourceJson.Json.ILooseResourceCandidateDecl[];
35
+ /**
36
+ * Rebuilds the resource system with edited candidates using deltas
37
+ */
38
+ export declare function rebuildSystemWithEdits(originalSystem: ProcessedResources['system'], editedResources: Map<string, {
39
+ originalValue: JsonValue;
40
+ editedValue: JsonValue;
41
+ delta: JsonValue;
42
+ }>, currentContext: Record<string, string>): Promise<Result<ProcessedResources>>;
43
+ /**
44
+ * Extracts the current resolution context from resolver state
45
+ */
46
+ export declare function extractResolutionContext(resolver: Runtime.ResourceResolver, contextValues: Record<string, string>): Record<string, string>;
47
+ /**
48
+ * Creates a collision detection key for tracking edit conflicts
49
+ */
50
+ export declare function createEditCollisionKey(resourceId: string, context: Record<string, string>): string;
51
+ /**
52
+ * Checks for potential edit conflicts with existing candidates
53
+ */
54
+ export declare function checkEditConflicts(resourceManager: Resources.ResourceManagerBuilder | Runtime.IResourceManager, editedResources: Map<string, JsonValue>, currentContext: Record<string, string>): {
55
+ conflicts: string[];
56
+ warnings: string[];
57
+ };
58
+ //# sourceMappingURL=resolutionEditing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolutionEditing.d.ts","sourceRoot":"","sources":["../../src/utils/resolutionEditing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAiB,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAG/D,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,SAAS,CAAC;IACzB,WAAW,EAAE,SAAS,CAAC;IACvB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,SAAS,GAAG,oBAAoB,CA6CnF;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,aAAa,EAAE,SAAS,EACxB,WAAW,EAAE,SAAS,GACrB,MAAM,CAAC,SAAS,CAAC,CAsCnB;AAoBD;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,aAAa,EAAE,SAAS,CAAC;IAAC,WAAW,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC,EACpG,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,YAAY,CAAC,IAAI,CAAC,2BAA2B,EAAE,CAuCjD;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,cAAc,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EAC5C,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,aAAa,EAAE,SAAS,CAAC;IAAC,WAAW,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC,EACpG,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CA2DrC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,OAAO,CAAC,gBAAgB,EAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWxB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAOlG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,eAAe,EAAE,SAAS,CAAC,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,EAC5E,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EACvC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA2B7C"}
@@ -0,0 +1,246 @@
1
+ import { succeed, fail } from '@fgv/ts-utils';
2
+ import { Runtime } from '@fgv/ts-res';
3
+ import { Diff } from '@fgv/ts-json';
4
+ /**
5
+ * Validates an edited resource JSON value
6
+ */
7
+ export function validateEditedResource(editedValue) {
8
+ const errors = [];
9
+ const warnings = [];
10
+ // Basic JSON validation
11
+ if (editedValue === null || editedValue === undefined) {
12
+ errors.push('Resource value cannot be null or undefined');
13
+ }
14
+ // Check if it's valid JSON-serializable
15
+ try {
16
+ JSON.stringify(editedValue);
17
+ }
18
+ catch (error) {
19
+ errors.push(`Invalid JSON: ${error instanceof Error ? error.message : 'Unknown error'}`);
20
+ }
21
+ // Type-specific validation
22
+ if (typeof editedValue === 'object' && editedValue !== null) {
23
+ // Object validation - check for circular references
24
+ const seen = new Set();
25
+ const checkCircular = (obj) => {
26
+ if (seen.has(obj))
27
+ return true;
28
+ seen.add(obj);
29
+ if (typeof obj === 'object' && obj !== null) {
30
+ for (const key in obj) {
31
+ const objTyped = obj;
32
+ if (typeof objTyped[key] === 'object' && objTyped[key] !== null) {
33
+ if (checkCircular(objTyped[key]))
34
+ return true;
35
+ }
36
+ }
37
+ }
38
+ seen.delete(obj);
39
+ return false;
40
+ };
41
+ if (checkCircular(editedValue)) {
42
+ errors.push('Resource contains circular references');
43
+ }
44
+ }
45
+ return {
46
+ isValid: errors.length === 0,
47
+ errors,
48
+ warnings
49
+ };
50
+ }
51
+ /**
52
+ * Computes a 3-way diff between base, resolved, and edited values to create minimal delta
53
+ * @param baseValue - The base/original value before resolution (if available)
54
+ * @param resolvedValue - The fully resolved/composed value shown to user
55
+ * @param editedValue - The value after user edits
56
+ * @returns A minimal delta object with only the changes, or null if no changes
57
+ */
58
+ export function computeResourceDelta(baseValue, resolvedValue, editedValue) {
59
+ // Use ts-json's three-way diff for proper delta computation
60
+ const diffResult = Diff.jsonThreeWayDiff(resolvedValue, editedValue);
61
+ if (diffResult.isFailure()) {
62
+ // Fall back to full replacement on diff failure
63
+ console.error('Failed to compute three-way diff:', diffResult.message);
64
+ return succeed(editedValue);
65
+ }
66
+ const diff = diffResult.value;
67
+ // If identical, no changes needed
68
+ if (diff.identical) {
69
+ return succeed(null);
70
+ }
71
+ // Build a proper delta that includes deletions as null values
72
+ const delta = {};
73
+ // Add all changes/additions from onlyInB
74
+ if (diff.onlyInB !== null) {
75
+ Object.assign(delta, diff.onlyInB);
76
+ }
77
+ // Add deletions as null values from onlyInA
78
+ // onlyInA contains properties that existed in resolved but not in edited
79
+ if (diff.onlyInA !== null) {
80
+ // Add null entries for all deleted properties
81
+ addDeletionsAsNull(diff.onlyInA, delta);
82
+ }
83
+ // If delta is empty, no changes
84
+ if (Object.keys(delta).length === 0) {
85
+ return succeed(null);
86
+ }
87
+ return succeed(delta);
88
+ }
89
+ /**
90
+ * Recursively adds null values to delta for all properties in the deleted object
91
+ */
92
+ function addDeletionsAsNull(deleted, delta) {
93
+ if (typeof deleted === 'object' && deleted !== null && !Array.isArray(deleted)) {
94
+ const deletedObj = deleted;
95
+ for (const key in deletedObj) {
96
+ if (deletedObj.hasOwnProperty(key)) {
97
+ // If this key already exists in delta (from onlyInB), it means the property
98
+ // was modified, not deleted, so don't override with null
99
+ if (!(key in delta)) {
100
+ delta[key] = null;
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ /**
107
+ * Creates candidate declarations for edited resources with proper delta handling
108
+ */
109
+ export function createCandidateDeclarations(editedResources, currentContext) {
110
+ const declarations = [];
111
+ for (const [resourceId, resourceEdit] of editedResources.entries()) {
112
+ // Create conditions from current context (using array format)
113
+ const conditions = [];
114
+ for (const [qualifierName, qualifierValue] of Object.entries(currentContext)) {
115
+ if (qualifierValue && qualifierValue.trim() !== '') {
116
+ conditions.push({
117
+ qualifierName,
118
+ operator: 'matches',
119
+ value: qualifierValue,
120
+ priority: 900 // High priority for user edits
121
+ });
122
+ }
123
+ }
124
+ // Always use the delta if we have one (which should be the minimal changes)
125
+ // The delta will be null if there are no changes, or the delta itself if there are changes
126
+ const hasChanges = resourceEdit.delta !== null && resourceEdit.delta !== undefined;
127
+ if (!hasChanges) {
128
+ // No changes, skip this resource
129
+ continue;
130
+ }
131
+ // Always save as partial with just the delta when we have changes
132
+ // This ensures minimal, clean resource files
133
+ declarations.push({
134
+ id: resourceId,
135
+ conditions: conditions.length > 0 ? conditions : undefined,
136
+ json: resourceEdit.delta, // Always use the delta (minimal changes only)
137
+ isPartial: true, // Always partial when saving deltas
138
+ mergeMethod: 'augment' // Always augment to merge the delta with base
139
+ });
140
+ }
141
+ return declarations;
142
+ }
143
+ /**
144
+ * Rebuilds the resource system with edited candidates using deltas
145
+ */
146
+ export async function rebuildSystemWithEdits(originalSystem, editedResources, currentContext) {
147
+ try {
148
+ const candidateDeclarations = createCandidateDeclarations(editedResources, currentContext);
149
+ const clonedManager = originalSystem.resourceManager.clone({
150
+ candidates: candidateDeclarations
151
+ });
152
+ if (clonedManager.isFailure()) {
153
+ return fail(`Failed to clone manager: ${clonedManager.message}`);
154
+ }
155
+ // Get compiled collection from the updated manager
156
+ const compiledResult = clonedManager.value.getCompiledResourceCollection({ includeMetadata: true });
157
+ if (compiledResult.isFailure()) {
158
+ return fail(`Failed to get compiled collection: ${compiledResult.message}`);
159
+ }
160
+ // Create resolver for the updated system
161
+ const resolverResult = Runtime.ResourceResolver.create({
162
+ resourceManager: clonedManager.value,
163
+ qualifierTypes: originalSystem.qualifierTypes,
164
+ contextQualifierProvider: originalSystem.contextQualifierProvider
165
+ });
166
+ if (resolverResult.isFailure()) {
167
+ return fail(`Failed to create resolver: ${resolverResult.message}`);
168
+ }
169
+ // Create summary
170
+ const resourceIds = Array.from(clonedManager.value.resources.keys());
171
+ const summary = {
172
+ totalResources: resourceIds.length,
173
+ resourceIds,
174
+ errorCount: 0,
175
+ warnings: []
176
+ };
177
+ const updatedSystem = {
178
+ system: {
179
+ qualifierTypes: originalSystem.qualifierTypes,
180
+ qualifiers: originalSystem.qualifiers,
181
+ resourceTypes: originalSystem.resourceTypes,
182
+ resourceManager: clonedManager.value,
183
+ importManager: originalSystem.importManager,
184
+ contextQualifierProvider: originalSystem.contextQualifierProvider
185
+ },
186
+ compiledCollection: compiledResult.value,
187
+ resolver: resolverResult.value,
188
+ resourceCount: resourceIds.length,
189
+ summary
190
+ };
191
+ return succeed(updatedSystem);
192
+ }
193
+ catch (error) {
194
+ return fail(`Failed to rebuild system with edits: ${error instanceof Error ? error.message : String(error)}`);
195
+ }
196
+ }
197
+ /**
198
+ * Extracts the current resolution context from resolver state
199
+ */
200
+ export function extractResolutionContext(resolver, contextValues) {
201
+ // Filter out empty/undefined context values
202
+ const cleanContext = {};
203
+ for (const [key, value] of Object.entries(contextValues)) {
204
+ if (value && value.trim() !== '') {
205
+ cleanContext[key] = value.trim();
206
+ }
207
+ }
208
+ return cleanContext;
209
+ }
210
+ /**
211
+ * Creates a collision detection key for tracking edit conflicts
212
+ */
213
+ export function createEditCollisionKey(resourceId, context) {
214
+ const contextEntries = Object.entries(context)
215
+ .sort(([a], [b]) => a.localeCompare(b))
216
+ .map(([key, value]) => `${key}=${value}`)
217
+ .join('&');
218
+ return `${resourceId}?${contextEntries}`;
219
+ }
220
+ /**
221
+ * Checks for potential edit conflicts with existing candidates
222
+ */
223
+ export function checkEditConflicts(resourceManager, editedResources, currentContext) {
224
+ const conflicts = [];
225
+ const warnings = [];
226
+ for (const [resourceId] of editedResources) {
227
+ try {
228
+ // Get the current resource to check for conflicts
229
+ const resourceResult = resourceManager.getBuiltResource(resourceId);
230
+ if (resourceResult.isSuccess()) {
231
+ const resource = resourceResult.value;
232
+ // Check if we're likely to create a conflict
233
+ if (resource.candidates.length > 1) {
234
+ warnings.push(`Resource ${resourceId} has ${resource.candidates.length} candidates - edits may create conflicts`);
235
+ }
236
+ // Could add more sophisticated conflict detection here
237
+ // based on condition overlap analysis
238
+ }
239
+ }
240
+ catch (error) {
241
+ // Ignore errors in conflict checking
242
+ }
243
+ }
244
+ return { conflicts, warnings };
245
+ }
246
+ //# sourceMappingURL=resolutionEditing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolutionEditing.js","sourceRoot":"","sources":["../../src/utils/resolutionEditing.ts"],"names":[],"mappings":"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","sourcesContent":["import { Result, succeed, fail } from '@fgv/ts-utils';\nimport { ResourceJson, Resources, Runtime } from '@fgv/ts-res';\nimport { Diff } from '@fgv/ts-json';\nimport { JsonObject } from '@fgv/ts-json-base';\nimport { ProcessedResources, JsonValue } from '../types';\n\nexport interface EditedResourceInfo {\n resourceId: string;\n originalValue: JsonValue;\n editedValue: JsonValue;\n timestamp: Date;\n}\n\nexport interface EditValidationResult {\n isValid: boolean;\n errors: string[];\n warnings: string[];\n}\n\n/**\n * Validates an edited resource JSON value\n */\nexport function validateEditedResource(editedValue: JsonValue): EditValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Basic JSON validation\n if (editedValue === null || editedValue === undefined) {\n errors.push('Resource value cannot be null or undefined');\n }\n\n // Check if it's valid JSON-serializable\n try {\n JSON.stringify(editedValue);\n } catch (error) {\n errors.push(`Invalid JSON: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n\n // Type-specific validation\n if (typeof editedValue === 'object' && editedValue !== null) {\n // Object validation - check for circular references\n const seen = new Set<unknown>();\n const checkCircular = (obj: unknown): boolean => {\n if (seen.has(obj)) return true;\n seen.add(obj);\n if (typeof obj === 'object' && obj !== null) {\n for (const key in obj as Record<string, unknown>) {\n const objTyped = obj as Record<string, unknown>;\n if (typeof objTyped[key] === 'object' && objTyped[key] !== null) {\n if (checkCircular(objTyped[key])) return true;\n }\n }\n }\n seen.delete(obj);\n return false;\n };\n\n if (checkCircular(editedValue)) {\n errors.push('Resource contains circular references');\n }\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings\n };\n}\n\n/**\n * Computes a 3-way diff between base, resolved, and edited values to create minimal delta\n * @param baseValue - The base/original value before resolution (if available)\n * @param resolvedValue - The fully resolved/composed value shown to user\n * @param editedValue - The value after user edits\n * @returns A minimal delta object with only the changes, or null if no changes\n */\nexport function computeResourceDelta(\n baseValue: JsonValue | undefined,\n resolvedValue: JsonValue,\n editedValue: JsonValue\n): Result<JsonValue> {\n // Use ts-json's three-way diff for proper delta computation\n const diffResult = Diff.jsonThreeWayDiff(resolvedValue, editedValue);\n\n if (diffResult.isFailure()) {\n // Fall back to full replacement on diff failure\n console.error('Failed to compute three-way diff:', diffResult.message);\n return succeed(editedValue);\n }\n\n const diff = diffResult.value;\n\n // If identical, no changes needed\n if (diff.identical) {\n return succeed(null);\n }\n\n // Build a proper delta that includes deletions as null values\n const delta: Record<string, JsonValue> = {};\n\n // Add all changes/additions from onlyInB\n if (diff.onlyInB !== null) {\n Object.assign(delta, diff.onlyInB);\n }\n\n // Add deletions as null values from onlyInA\n // onlyInA contains properties that existed in resolved but not in edited\n if (diff.onlyInA !== null) {\n // Add null entries for all deleted properties\n addDeletionsAsNull(diff.onlyInA, delta);\n }\n\n // If delta is empty, no changes\n if (Object.keys(delta).length === 0) {\n return succeed(null);\n }\n\n return succeed(delta);\n}\n\n/**\n * Recursively adds null values to delta for all properties in the deleted object\n */\nfunction addDeletionsAsNull(deleted: JsonValue, delta: Record<string, JsonValue>): void {\n if (typeof deleted === 'object' && deleted !== null && !Array.isArray(deleted)) {\n const deletedObj = deleted as Record<string, JsonValue>;\n for (const key in deletedObj) {\n if (deletedObj.hasOwnProperty(key)) {\n // If this key already exists in delta (from onlyInB), it means the property\n // was modified, not deleted, so don't override with null\n if (!(key in delta)) {\n delta[key] = null;\n }\n }\n }\n }\n}\n\n/**\n * Creates candidate declarations for edited resources with proper delta handling\n */\nexport function createCandidateDeclarations(\n editedResources: Map<string, { originalValue: JsonValue; editedValue: JsonValue; delta: JsonValue }>,\n currentContext: Record<string, string>\n): ResourceJson.Json.ILooseResourceCandidateDecl[] {\n const declarations: ResourceJson.Json.ILooseResourceCandidateDecl[] = [];\n\n for (const [resourceId, resourceEdit] of editedResources.entries()) {\n // Create conditions from current context (using array format)\n const conditions: ResourceJson.Json.ILooseConditionDecl[] = [];\n\n for (const [qualifierName, qualifierValue] of Object.entries(currentContext)) {\n if (qualifierValue && qualifierValue.trim() !== '') {\n conditions.push({\n qualifierName,\n operator: 'matches',\n value: qualifierValue,\n priority: 900 // High priority for user edits\n });\n }\n }\n\n // Always use the delta if we have one (which should be the minimal changes)\n // The delta will be null if there are no changes, or the delta itself if there are changes\n const hasChanges = resourceEdit.delta !== null && resourceEdit.delta !== undefined;\n\n if (!hasChanges) {\n // No changes, skip this resource\n continue;\n }\n\n // Always save as partial with just the delta when we have changes\n // This ensures minimal, clean resource files\n declarations.push({\n id: resourceId,\n conditions: conditions.length > 0 ? conditions : undefined,\n json: resourceEdit.delta as JsonObject, // Always use the delta (minimal changes only)\n isPartial: true, // Always partial when saving deltas\n mergeMethod: 'augment' // Always augment to merge the delta with base\n });\n }\n\n return declarations;\n}\n\n/**\n * Rebuilds the resource system with edited candidates using deltas\n */\nexport async function rebuildSystemWithEdits(\n originalSystem: ProcessedResources['system'],\n editedResources: Map<string, { originalValue: JsonValue; editedValue: JsonValue; delta: JsonValue }>,\n currentContext: Record<string, string>\n): Promise<Result<ProcessedResources>> {\n try {\n const candidateDeclarations = createCandidateDeclarations(editedResources, currentContext);\n\n const clonedManager = originalSystem.resourceManager.clone({\n candidates: candidateDeclarations\n });\n\n if (clonedManager.isFailure()) {\n return fail(`Failed to clone manager: ${clonedManager.message}`);\n }\n\n // Get compiled collection from the updated manager\n const compiledResult = clonedManager.value.getCompiledResourceCollection({ includeMetadata: true });\n if (compiledResult.isFailure()) {\n return fail(`Failed to get compiled collection: ${compiledResult.message}`);\n }\n\n // Create resolver for the updated system\n const resolverResult = Runtime.ResourceResolver.create({\n resourceManager: clonedManager.value,\n qualifierTypes: originalSystem.qualifierTypes,\n contextQualifierProvider: originalSystem.contextQualifierProvider\n });\n\n if (resolverResult.isFailure()) {\n return fail(`Failed to create resolver: ${resolverResult.message}`);\n }\n\n // Create summary\n const resourceIds = Array.from(clonedManager.value.resources.keys());\n const summary = {\n totalResources: resourceIds.length,\n resourceIds,\n errorCount: 0,\n warnings: []\n };\n\n const updatedSystem: ProcessedResources = {\n system: {\n qualifierTypes: originalSystem.qualifierTypes,\n qualifiers: originalSystem.qualifiers,\n resourceTypes: originalSystem.resourceTypes,\n resourceManager: clonedManager.value,\n importManager: originalSystem.importManager,\n contextQualifierProvider: originalSystem.contextQualifierProvider\n },\n compiledCollection: compiledResult.value,\n resolver: resolverResult.value,\n resourceCount: resourceIds.length,\n summary\n };\n\n return succeed(updatedSystem);\n } catch (error) {\n return fail(\n `Failed to rebuild system with edits: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Extracts the current resolution context from resolver state\n */\nexport function extractResolutionContext(\n resolver: Runtime.ResourceResolver,\n contextValues: Record<string, string>\n): Record<string, string> {\n // Filter out empty/undefined context values\n const cleanContext: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(contextValues)) {\n if (value && value.trim() !== '') {\n cleanContext[key] = value.trim();\n }\n }\n\n return cleanContext;\n}\n\n/**\n * Creates a collision detection key for tracking edit conflicts\n */\nexport function createEditCollisionKey(resourceId: string, context: Record<string, string>): string {\n const contextEntries = Object.entries(context)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => `${key}=${value}`)\n .join('&');\n\n return `${resourceId}?${contextEntries}`;\n}\n\n/**\n * Checks for potential edit conflicts with existing candidates\n */\nexport function checkEditConflicts(\n resourceManager: Resources.ResourceManagerBuilder | Runtime.IResourceManager,\n editedResources: Map<string, JsonValue>,\n currentContext: Record<string, string>\n): { conflicts: string[]; warnings: string[] } {\n const conflicts: string[] = [];\n const warnings: string[] = [];\n\n for (const [resourceId] of editedResources) {\n try {\n // Get the current resource to check for conflicts\n const resourceResult = resourceManager.getBuiltResource(resourceId);\n if (resourceResult.isSuccess()) {\n const resource = resourceResult.value;\n\n // Check if we're likely to create a conflict\n if (resource.candidates.length > 1) {\n warnings.push(\n `Resource ${resourceId} has ${resource.candidates.length} candidates - edits may create conflicts`\n );\n }\n\n // Could add more sophisticated conflict detection here\n // based on condition overlap analysis\n }\n } catch (error) {\n // Ignore errors in conflict checking\n }\n }\n\n return { conflicts, warnings };\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { Runtime } from '@fgv/ts-res';
3
+ import { ProcessedResources, ResolutionResult, ConditionEvaluationResult } from '../types';
4
+ export interface ResolutionOptions {
5
+ enableCaching?: boolean;
6
+ enableDebugLogging?: boolean;
7
+ }
8
+ /**
9
+ * Create a resolver with context for resource resolution
10
+ */
11
+ export declare function createResolverWithContext(processedResources: ProcessedResources, contextValues: Record<string, string | undefined>, options?: ResolutionOptions): Result<Runtime.ResourceResolver>;
12
+ /**
13
+ * Evaluate conditions for a specific candidate
14
+ */
15
+ export declare function evaluateConditionsForCandidate(resolver: Runtime.ResourceResolver, candidateIndex: number, compiledResource: any, compiledCollection: any): ConditionEvaluationResult[];
16
+ /**
17
+ * Resolve a resource and create detailed resolution result
18
+ */
19
+ export declare function resolveResourceDetailed(resolver: Runtime.ResourceResolver, resourceId: string, processedResources: ProcessedResources, options?: ResolutionOptions): Result<ResolutionResult>;
20
+ /**
21
+ * Get available qualifiers from processed resources
22
+ */
23
+ export declare function getAvailableQualifiers(processedResources: ProcessedResources): string[];
24
+ /**
25
+ * Check if context has any pending changes
26
+ */
27
+ export declare function hasPendingContextChanges(contextValues: Record<string, string | undefined>, pendingContextValues: Record<string, string | undefined>): boolean;
28
+ //# sourceMappingURL=resolutionUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolutionUtils.d.ts","sourceRoot":"","sources":["../../src/utils/resolutionUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAoC,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAiB,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAE1G,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AASD;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,kBAAkB,EAAE,kBAAkB,EACtC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACjD,OAAO,GAAE,iBAAsB,GAC9B,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAmDlC;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,OAAO,CAAC,gBAAgB,EAClC,cAAc,EAAE,MAAM,EACtB,gBAAgB,EAAE,GAAG,EACrB,kBAAkB,EAAE,GAAG,GACtB,yBAAyB,EAAE,CAkD7B;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,OAAO,CAAC,gBAAgB,EAClC,UAAU,EAAE,MAAM,EAClB,kBAAkB,EAAE,kBAAkB,EACtC,OAAO,GAAE,iBAAsB,GAC9B,MAAM,CAAC,gBAAgB,CAAC,CA4H1B;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAKvF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACjD,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACvD,OAAO,CAET"}