@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,319 @@
1
+ import { Result, succeed, fail } from '@fgv/ts-utils';
2
+ import {
3
+ IZipLoader,
4
+ ZipLoadOptions,
5
+ ZipLoadResult,
6
+ ZipProgressCallback,
7
+ ZipFileTree,
8
+ ZipFileItem,
9
+ ZipLoadingStage
10
+ } from './types';
11
+ import { parseManifest, parseConfiguration, zipTreeToFiles, zipTreeToDirectory, isZipFile } from './zipUtils';
12
+ import { processImportedFiles, processImportedDirectory } from '../tsResIntegration';
13
+ import { ProcessedResources } from '../../types';
14
+
15
+ // Dynamic import for JSZip to support both Node.js and browser environments
16
+ let JSZip: any = null;
17
+
18
+ /**
19
+ * Get JSZip instance (assumes JSZip is available)
20
+ */
21
+ function getJSZip(): any {
22
+ if (JSZip) return JSZip;
23
+
24
+ // Check if JSZip is globally available
25
+ if (typeof window !== 'undefined' && (window as any).JSZip) {
26
+ JSZip = (window as any).JSZip;
27
+ return JSZip;
28
+ }
29
+
30
+ // Try to get JSZip from require/import (will work in bundled environments)
31
+ try {
32
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
33
+ JSZip = require('jszip');
34
+ return JSZip;
35
+ } catch (error) {
36
+ throw new Error('JSZip is not available. Please install jszip as a dependency: npm install jszip');
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Browser-based ZIP loader implementation
42
+ */
43
+ export class BrowserZipLoader implements IZipLoader {
44
+ /**
45
+ * Load ZIP from File object
46
+ */
47
+ async loadFromFile(
48
+ file: File,
49
+ options: ZipLoadOptions = {},
50
+ onProgress?: ZipProgressCallback
51
+ ): Promise<Result<ZipLoadResult>> {
52
+ onProgress?.('reading-file', 0, `Reading file: ${file.name}`);
53
+
54
+ if (!isZipFile(file.name)) {
55
+ return fail(`File ${file.name} is not a ZIP file`);
56
+ }
57
+
58
+ const buffer = await file.arrayBuffer().catch((error) => {
59
+ throw new Error(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`);
60
+ });
61
+ onProgress?.('reading-file', 100, 'File read complete');
62
+
63
+ return this.loadFromBuffer(buffer, options, onProgress);
64
+ }
65
+
66
+ /**
67
+ * Load ZIP from ArrayBuffer
68
+ */
69
+ async loadFromBuffer(
70
+ buffer: ArrayBuffer,
71
+ options: ZipLoadOptions = {},
72
+ onProgress?: ZipProgressCallback
73
+ ): Promise<Result<ZipLoadResult>> {
74
+ onProgress?.('parsing-zip', 0, 'Parsing ZIP archive');
75
+
76
+ const JSZipClass = getJSZip();
77
+ const zip = new JSZipClass();
78
+ const loadedZip = await zip.loadAsync(buffer).catch((error: any) => {
79
+ throw new Error(`Failed to parse ZIP: ${error instanceof Error ? error.message : String(error)}`);
80
+ });
81
+
82
+ onProgress?.('parsing-zip', 100, 'ZIP archive parsed');
83
+
84
+ // Build file tree
85
+ const fileTree = await this.buildFileTree(loadedZip, onProgress);
86
+
87
+ // Load manifest
88
+ onProgress?.('loading-manifest', 0, 'Loading manifest');
89
+ const manifest = await this.loadManifest(loadedZip);
90
+ onProgress?.('loading-manifest', 100, 'Manifest loaded');
91
+
92
+ // Load configuration
93
+ onProgress?.('loading-config', 0, 'Loading configuration');
94
+ const config = await this.loadConfiguration(loadedZip, options);
95
+ onProgress?.('loading-config', 100, 'Configuration loaded');
96
+
97
+ // Extract files and directory structure
98
+ onProgress?.('extracting-files', 0, 'Extracting files');
99
+ const files = zipTreeToFiles(fileTree);
100
+ const directory = zipTreeToDirectory(fileTree);
101
+ onProgress?.('extracting-files', 100, `Extracted ${files.length} files`);
102
+
103
+ // Process resources if requested
104
+ let processedResources: ProcessedResources | null = null;
105
+ if (options.autoProcessResources) {
106
+ onProgress?.('processing-resources', 0, 'Processing resources');
107
+
108
+ const configToUse = options.overrideConfig || config;
109
+
110
+ if (directory) {
111
+ const processResult = await processImportedDirectory(directory, configToUse || undefined);
112
+ if (processResult.isSuccess()) {
113
+ processedResources = processResult.value;
114
+ } else {
115
+ throw new Error(`Failed to process resources from directory: ${processResult.message}`);
116
+ }
117
+ } else if (files.length > 0) {
118
+ const processResult = await processImportedFiles(files, configToUse || undefined);
119
+ if (processResult.isSuccess()) {
120
+ processedResources = processResult.value;
121
+ } else {
122
+ throw new Error(`Failed to process resources from files: ${processResult.message}`);
123
+ }
124
+ }
125
+
126
+ onProgress?.('processing-resources', 100, 'Resources processed');
127
+ }
128
+
129
+ onProgress?.('complete', 100, 'ZIP loading complete');
130
+
131
+ return succeed({
132
+ manifest,
133
+ config: options.overrideConfig || config,
134
+ files,
135
+ directory,
136
+ processedResources
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Load ZIP from URL
142
+ */
143
+ async loadFromUrl(
144
+ url: string,
145
+ options: ZipLoadOptions = {},
146
+ onProgress?: ZipProgressCallback
147
+ ): Promise<Result<ZipLoadResult>> {
148
+ onProgress?.('reading-file', 0, `Fetching from URL: ${url}`);
149
+
150
+ const response = await fetch(url).catch((error) => {
151
+ throw new Error(
152
+ `Failed to fetch ZIP from URL: ${error instanceof Error ? error.message : String(error)}`
153
+ );
154
+ });
155
+
156
+ if (!response.ok) {
157
+ return fail(`Failed to fetch ZIP from URL: ${response.status} ${response.statusText}`);
158
+ }
159
+
160
+ const buffer = await response.arrayBuffer().catch((error) => {
161
+ throw new Error(
162
+ `Failed to read response buffer: ${error instanceof Error ? error.message : String(error)}`
163
+ );
164
+ });
165
+ onProgress?.('reading-file', 100, 'URL fetch complete');
166
+
167
+ return this.loadFromBuffer(buffer, options, onProgress);
168
+ }
169
+
170
+ /**
171
+ * Build file tree from JSZip instance
172
+ */
173
+ private async buildFileTree(zip: any, onProgress?: ZipProgressCallback): Promise<ZipFileTree> {
174
+ const files = new Map<string, ZipFileItem>();
175
+ const directories = new Set<string>();
176
+
177
+ const zipFiles = Object.keys(zip.files);
178
+ let processed = 0;
179
+
180
+ // Pre-load all file contents for performance
181
+ for (const filename of zipFiles) {
182
+ const zipEntry = zip.files[filename];
183
+
184
+ if (zipEntry.dir) {
185
+ directories.add(filename);
186
+ files.set(filename, {
187
+ name:
188
+ filename
189
+ .split('/')
190
+ .filter((p) => p)
191
+ .pop() || filename,
192
+ path: filename,
193
+ size: 0,
194
+ isDirectory: true,
195
+ lastModified: zipEntry.date
196
+ });
197
+ } else {
198
+ // Load file content
199
+ const content = await zipEntry.async('string');
200
+
201
+ files.set(filename, {
202
+ name: filename.split('/').pop() || filename,
203
+ path: filename,
204
+ size: content.length,
205
+ isDirectory: false,
206
+ lastModified: zipEntry.date,
207
+ content
208
+ });
209
+ }
210
+
211
+ processed++;
212
+ const progress = Math.round((processed / zipFiles.length) * 100);
213
+ onProgress?.('extracting-files', progress, `Processing ${filename}`);
214
+ }
215
+
216
+ return {
217
+ files,
218
+ directories,
219
+ root: this.findCommonRoot(Array.from(files.keys()))
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Load manifest from ZIP
225
+ */
226
+ private async loadManifest(zip: any): Promise<any> {
227
+ const manifestFile = zip.files['manifest.json'];
228
+ if (!manifestFile) {
229
+ return null;
230
+ }
231
+
232
+ const manifestData = await manifestFile.async('string').catch((error: any) => {
233
+ console.warn('Failed to read manifest file:', error);
234
+ return null;
235
+ });
236
+
237
+ if (!manifestData) return null;
238
+
239
+ const parseResult = parseManifest(manifestData);
240
+ return parseResult.orDefault() ?? null;
241
+ }
242
+
243
+ /**
244
+ * Load configuration from ZIP
245
+ */
246
+ private async loadConfiguration(zip: any, options: ZipLoadOptions): Promise<any> {
247
+ if (options.overrideConfig) {
248
+ return options.overrideConfig;
249
+ }
250
+
251
+ const configFile = zip.files['config.json'];
252
+ if (!configFile) {
253
+ return null;
254
+ }
255
+
256
+ const configData = await configFile.async('string').catch((error: any) => {
257
+ console.warn('Failed to read config file:', error);
258
+ return null;
259
+ });
260
+
261
+ if (!configData) return null;
262
+
263
+ const parseResult = parseConfiguration(configData);
264
+ return parseResult.orDefault() ?? null;
265
+ }
266
+
267
+ /**
268
+ * Find common root directory from file paths
269
+ */
270
+ private findCommonRoot(paths: string[]): string {
271
+ if (paths.length === 0) return '';
272
+ if (paths.length === 1) return paths[0].split('/')[0] || '';
273
+
274
+ const parts = paths[0].split('/');
275
+ let commonLength = 0;
276
+
277
+ for (let i = 0; i < parts.length; i++) {
278
+ const part = parts[i];
279
+ if (paths.every((path) => path.split('/')[i] === part)) {
280
+ commonLength = i + 1;
281
+ } else {
282
+ break;
283
+ }
284
+ }
285
+
286
+ return parts.slice(0, commonLength).join('/');
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Create a new browser ZIP loader instance
292
+ */
293
+ export function createBrowserZipLoader(): IZipLoader {
294
+ return new BrowserZipLoader();
295
+ }
296
+
297
+ /**
298
+ * Convenience function to load ZIP from File with default options
299
+ */
300
+ export async function loadZipFile(
301
+ file: File,
302
+ options?: ZipLoadOptions,
303
+ onProgress?: ZipProgressCallback
304
+ ): Promise<Result<ZipLoadResult>> {
305
+ const loader = createBrowserZipLoader();
306
+ return loader.loadFromFile(file, options, onProgress);
307
+ }
308
+
309
+ /**
310
+ * Convenience function to load ZIP from URL with default options
311
+ */
312
+ export async function loadZipFromUrl(
313
+ url: string,
314
+ options?: ZipLoadOptions,
315
+ onProgress?: ZipProgressCallback
316
+ ): Promise<Result<ZipLoadResult>> {
317
+ const loader = createBrowserZipLoader();
318
+ return loader.loadFromUrl(url, options, onProgress);
319
+ }
@@ -0,0 +1,26 @@
1
+ // Export types
2
+ export * from './types';
3
+
4
+ // Export utilities
5
+ export * from './zipUtils';
6
+
7
+ // Export browser ZIP loader
8
+ export * from './browserZipLoader';
9
+
10
+ // Export Node.js ZIP builder (placeholder)
11
+ export * from './nodeZipBuilder';
12
+
13
+ // Re-export commonly used functions
14
+ export { loadZipFile, loadZipFromUrl, createBrowserZipLoader } from './browserZipLoader';
15
+
16
+ export { prepareZipData, prepareZipDataFromDirectory, createNodeZipBuilder } from './nodeZipBuilder';
17
+
18
+ export {
19
+ generateZipFilename,
20
+ parseManifest,
21
+ parseConfiguration,
22
+ zipTreeToFiles,
23
+ zipTreeToDirectory,
24
+ formatFileSize,
25
+ isZipFile
26
+ } from './zipUtils';
@@ -0,0 +1,153 @@
1
+ import { Result, succeed, fail } from '@fgv/ts-utils';
2
+ import { IZipBuilder, ZipArchiveOptions, ZipArchiveResult, ZipManifest } from './types';
3
+ import { ImportedDirectory, ImportedFile } from '../../types';
4
+ import { generateZipFilename, createManifest, sanitizeFilename, normalizePath } from './zipUtils';
5
+
6
+ /**
7
+ * Node.js-based ZIP builder implementation
8
+ *
9
+ * Note: This is a placeholder implementation for interface compatibility.
10
+ * The actual Node.js ZIP building functionality should be implemented
11
+ * in a separate Node.js-specific package or tool.
12
+ */
13
+ export class NodeZipBuilder implements IZipBuilder {
14
+ /**
15
+ * Create ZIP from files
16
+ */
17
+ async createFromFiles(
18
+ files: ImportedFile[],
19
+ options: ZipArchiveOptions = {}
20
+ ): Promise<Result<ZipArchiveResult>> {
21
+ return fail(
22
+ 'Node.js ZIP building not implemented in browser library. Use @fgv/ts-res-browser-cli for ZIP creation.'
23
+ );
24
+ }
25
+
26
+ /**
27
+ * Create ZIP from directory
28
+ */
29
+ async createFromDirectory(
30
+ directory: ImportedDirectory,
31
+ options: ZipArchiveOptions = {}
32
+ ): Promise<Result<ZipArchiveResult>> {
33
+ return fail(
34
+ 'Node.js ZIP building not implemented in browser library. Use @fgv/ts-res-browser-cli for ZIP creation.'
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Create ZIP from file system path
40
+ */
41
+ async createFromPath(path: string, options: ZipArchiveOptions = {}): Promise<Result<ZipArchiveResult>> {
42
+ return fail(
43
+ 'Node.js ZIP building not implemented in browser library. Use @fgv/ts-res-browser-cli for ZIP creation.'
44
+ );
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Create a new Node.js ZIP builder instance
50
+ *
51
+ * Note: This returns a placeholder implementation.
52
+ * For actual ZIP building, use the ts-res-browser-cli tool.
53
+ */
54
+ export function createNodeZipBuilder(): IZipBuilder {
55
+ return new NodeZipBuilder();
56
+ }
57
+
58
+ /**
59
+ * Browser-compatible ZIP creation interface
60
+ *
61
+ * This provides a way to prepare ZIP data in the browser,
62
+ * though actual ZIP file creation requires server-side processing
63
+ * or a separate Node.js tool.
64
+ */
65
+ export interface BrowserZipData {
66
+ files: Array<{
67
+ path: string;
68
+ content: string;
69
+ }>;
70
+ manifest: ZipManifest;
71
+ config?: any;
72
+ }
73
+
74
+ /**
75
+ * Prepare ZIP data structure for browser download or server processing
76
+ */
77
+ export function prepareZipData(
78
+ files: ImportedFile[],
79
+ options: ZipArchiveOptions = {}
80
+ ): Result<BrowserZipData> {
81
+ const timestamp = new Date().toISOString();
82
+ const filename = options.filename || 'ts-res-bundle';
83
+
84
+ // Create manifest
85
+ const manifest = createManifest(
86
+ 'file',
87
+ 'browser-files',
88
+ 'files/',
89
+ options.includeConfig ? 'config.json' : undefined
90
+ );
91
+
92
+ // Prepare file data
93
+ const zipFiles = files.map((file) => ({
94
+ path: normalizePath(file.path || file.name),
95
+ content: file.content
96
+ }));
97
+
98
+ // Add manifest
99
+ zipFiles.push({
100
+ path: 'manifest.json',
101
+ content: JSON.stringify(manifest, null, 2)
102
+ });
103
+
104
+ // Add configuration if provided
105
+ if (options.includeConfig && options.config) {
106
+ zipFiles.push({
107
+ path: 'config.json',
108
+ content: JSON.stringify(options.config, null, 2)
109
+ });
110
+ }
111
+
112
+ return succeed({
113
+ files: zipFiles,
114
+ manifest,
115
+ config: options.config
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Prepare ZIP data from directory structure
121
+ */
122
+ export function prepareZipDataFromDirectory(
123
+ directory: ImportedDirectory,
124
+ options: ZipArchiveOptions = {}
125
+ ): Result<BrowserZipData> {
126
+ // Flatten directory to files
127
+ const files: ImportedFile[] = [];
128
+
129
+ const collectFiles = (dir: ImportedDirectory, basePath: string = '') => {
130
+ // Add files from current directory
131
+ dir.files.forEach((file) => {
132
+ files.push({
133
+ ...file,
134
+ path: basePath ? `${basePath}/${file.name}` : file.name
135
+ });
136
+ });
137
+
138
+ // Recursively collect from subdirectories
139
+ if (dir.subdirectories) {
140
+ dir.subdirectories.forEach((subdir) => {
141
+ const subdirPath = basePath ? `${basePath}/${subdir.name}` : subdir.name;
142
+ collectFiles(subdir, subdirPath);
143
+ });
144
+ }
145
+ };
146
+
147
+ collectFiles(directory);
148
+
149
+ return prepareZipData(files, {
150
+ ...options,
151
+ filename: options.filename || sanitizeFilename(directory.name)
152
+ });
153
+ }
@@ -0,0 +1,175 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { Config } from '@fgv/ts-res';
3
+ import { ImportedDirectory, ImportedFile, ProcessedResources } from '../../types';
4
+
5
+ /**
6
+ * ZIP manifest metadata structure
7
+ */
8
+ export interface ZipManifest {
9
+ timestamp: string;
10
+ input?: {
11
+ type: 'file' | 'directory';
12
+ originalPath: string;
13
+ archivePath: string;
14
+ };
15
+ config?: {
16
+ type: 'file';
17
+ originalPath: string;
18
+ archivePath: string;
19
+ };
20
+ }
21
+
22
+ /**
23
+ * Options for creating ZIP archives
24
+ */
25
+ export interface ZipArchiveOptions {
26
+ /** Output directory for the ZIP file */
27
+ outputDir?: string;
28
+ /** Custom filename (without extension) */
29
+ filename?: string;
30
+ /** Compression level (0-9) */
31
+ compressionLevel?: number;
32
+ /** Include configuration file */
33
+ includeConfig?: boolean;
34
+ /** Custom configuration to include */
35
+ config?: Config.Model.ISystemConfiguration;
36
+ }
37
+
38
+ /**
39
+ * Result of ZIP archive creation
40
+ */
41
+ export interface ZipArchiveResult {
42
+ /** Path to the created ZIP file */
43
+ filePath: string;
44
+ /** Size of the ZIP file in bytes */
45
+ fileSize: number;
46
+ /** Generated manifest */
47
+ manifest: ZipManifest;
48
+ /** Timestamp when created */
49
+ timestamp: string;
50
+ }
51
+
52
+ /**
53
+ * Options for loading ZIP archives
54
+ */
55
+ export interface ZipLoadOptions {
56
+ /** Whether to auto-apply configuration found in ZIP */
57
+ autoApplyConfig?: boolean;
58
+ /** Whether to auto-process resources after loading */
59
+ autoProcessResources?: boolean;
60
+ /** Custom configuration to use instead of ZIP config */
61
+ overrideConfig?: Config.Model.ISystemConfiguration;
62
+ }
63
+
64
+ /**
65
+ * Result of ZIP loading operation
66
+ */
67
+ export interface ZipLoadResult {
68
+ /** Manifest from the ZIP */
69
+ manifest: ZipManifest | null;
70
+ /** Configuration found in ZIP */
71
+ config: Config.Model.ISystemConfiguration | null;
72
+ /** Loaded files */
73
+ files: ImportedFile[];
74
+ /** Loaded directory structure */
75
+ directory: ImportedDirectory | null;
76
+ /** File tree for direct processing */
77
+ fileTree?: any; // FileTree.FileTree from ts-utils
78
+ /** Processed resources if auto-processing was enabled */
79
+ processedResources: ProcessedResources | null;
80
+ }
81
+
82
+ /**
83
+ * ZIP loading progress stages
84
+ */
85
+ export type ZipLoadingStage =
86
+ | 'reading-file'
87
+ | 'parsing-zip'
88
+ | 'loading-manifest'
89
+ | 'loading-config'
90
+ | 'extracting-files'
91
+ | 'processing-resources'
92
+ | 'complete';
93
+
94
+ /**
95
+ * Progress callback for ZIP operations
96
+ */
97
+ export interface ZipProgressCallback {
98
+ (stage: ZipLoadingStage, progress: number, message?: string): void;
99
+ }
100
+
101
+ /**
102
+ * ZIP builder interface (Node.js environment)
103
+ */
104
+ export interface IZipBuilder {
105
+ /**
106
+ * Create ZIP from files
107
+ */
108
+ createFromFiles(files: ImportedFile[], options?: ZipArchiveOptions): Promise<Result<ZipArchiveResult>>;
109
+
110
+ /**
111
+ * Create ZIP from directory
112
+ */
113
+ createFromDirectory(
114
+ directory: ImportedDirectory,
115
+ options?: ZipArchiveOptions
116
+ ): Promise<Result<ZipArchiveResult>>;
117
+
118
+ /**
119
+ * Create ZIP from file system path
120
+ */
121
+ createFromPath(path: string, options?: ZipArchiveOptions): Promise<Result<ZipArchiveResult>>;
122
+ }
123
+
124
+ /**
125
+ * ZIP loader interface (Browser environment)
126
+ */
127
+ export interface IZipLoader {
128
+ /**
129
+ * Load ZIP from File object
130
+ */
131
+ loadFromFile(
132
+ file: File,
133
+ options?: ZipLoadOptions,
134
+ onProgress?: ZipProgressCallback
135
+ ): Promise<Result<ZipLoadResult>>;
136
+
137
+ /**
138
+ * Load ZIP from ArrayBuffer
139
+ */
140
+ loadFromBuffer(
141
+ buffer: ArrayBuffer,
142
+ options?: ZipLoadOptions,
143
+ onProgress?: ZipProgressCallback
144
+ ): Promise<Result<ZipLoadResult>>;
145
+
146
+ /**
147
+ * Load ZIP from URL
148
+ */
149
+ loadFromUrl(
150
+ url: string,
151
+ options?: ZipLoadOptions,
152
+ onProgress?: ZipProgressCallback
153
+ ): Promise<Result<ZipLoadResult>>;
154
+ }
155
+
156
+ /**
157
+ * File item within a ZIP archive
158
+ */
159
+ export interface ZipFileItem {
160
+ name: string;
161
+ path: string;
162
+ size: number;
163
+ isDirectory: boolean;
164
+ lastModified?: Date;
165
+ content?: string | ArrayBuffer;
166
+ }
167
+
168
+ /**
169
+ * ZIP file tree representation
170
+ */
171
+ export interface ZipFileTree {
172
+ files: Map<string, ZipFileItem>;
173
+ directories: Set<string>;
174
+ root: string;
175
+ }