@scality/data-browser-library 1.0.0-preview.16 → 1.0.0-preview.17

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.
@@ -6,7 +6,7 @@ import { applyBucketMocks, createTestWrapper } from "../../test/testUtils.js";
6
6
  import { useGetBucketAcl, useGetBucketCors, useGetBucketLifecycle, useGetBucketLocation, useGetBucketObjectLockConfiguration, useGetBucketPolicy, useGetBucketReplication, useGetBucketTagging, useGetBucketVersioning } from "../../hooks/index.js";
7
7
  import { useISVBucketStatus } from "../../hooks/useISVBucketDetection.js";
8
8
  import { useFeatures } from "../../hooks/useFeatures.js";
9
- import * as __WEBPACK_EXTERNAL_MODULE__contexts_DataBrowserUICustomizationContext_js_f267b01c__ from "../../contexts/DataBrowserUICustomizationContext.js";
9
+ import * as __rspack_external__contexts_DataBrowserUICustomizationContext_js_f267b01c from "../../contexts/DataBrowserUICustomizationContext.js";
10
10
  jest.mock('../../hooks');
11
11
  jest.mock('../../hooks/useISVBucketDetection');
12
12
  jest.mock('../../hooks/useFeatures');
@@ -29,7 +29,7 @@ const mockUseGetBucketTagging = jest.mocked(useGetBucketTagging);
29
29
  const mockUseISVBucketStatus = jest.mocked(useISVBucketStatus);
30
30
  const mockUseFeatures = jest.mocked(useFeatures);
31
31
  const mockUseDataBrowserUICustomization = (config = {})=>{
32
- jest.spyOn(__WEBPACK_EXTERNAL_MODULE__contexts_DataBrowserUICustomizationContext_js_f267b01c__, 'useDataBrowserUICustomization').mockReturnValue(config);
32
+ jest.spyOn(__rspack_external__contexts_DataBrowserUICustomizationContext_js_f267b01c, 'useDataBrowserUICustomization').mockReturnValue(config);
33
33
  };
34
34
  const renderBucketDetails = ()=>{
35
35
  const Wrapper = createTestWrapper();
@@ -3,9 +3,9 @@ import { fireEvent, render, screen } from "@testing-library/react";
3
3
  import { MemoryRouter } from "react-router";
4
4
  import { createTestWrapper, mockOffsetSize } from "../../test/testUtils.js";
5
5
  import { BucketList } from "../buckets/BucketList.js";
6
- import * as __WEBPACK_EXTERNAL_MODULE__contexts_DataBrowserUICustomizationContext_js_f267b01c__ from "../../contexts/DataBrowserUICustomizationContext.js";
6
+ import * as __rspack_external__contexts_DataBrowserUICustomizationContext_js_f267b01c from "../../contexts/DataBrowserUICustomizationContext.js";
7
7
  const mockUseDataBrowserUICustomization = (config = {})=>{
8
- jest.spyOn(__WEBPACK_EXTERNAL_MODULE__contexts_DataBrowserUICustomizationContext_js_f267b01c__, 'useDataBrowserUICustomization').mockReturnValue(config);
8
+ jest.spyOn(__rspack_external__contexts_DataBrowserUICustomizationContext_js_f267b01c, 'useDataBrowserUICustomization').mockReturnValue(config);
9
9
  };
10
10
  const renderBucketList = (props = {})=>{
11
11
  const Wrapper = createTestWrapper();
@@ -6,7 +6,7 @@ import { useGetBucketAcl, useGetBucketCors, useGetBucketLocation, useGetBucketOb
6
6
  import { applyBucketMocks, createTestWrapper } from "../../test/testUtils.js";
7
7
  import { BucketOverview, useBucketOverviewContext } from "../buckets/BucketOverview.js";
8
8
  import { useFeatures } from "../../hooks/useFeatures.js";
9
- import * as __WEBPACK_EXTERNAL_MODULE__contexts_DataBrowserUICustomizationContext_js_f267b01c__ from "../../contexts/DataBrowserUICustomizationContext.js";
9
+ import * as __rspack_external__contexts_DataBrowserUICustomizationContext_js_f267b01c from "../../contexts/DataBrowserUICustomizationContext.js";
10
10
  jest.mock('../../hooks');
11
11
  jest.mock('../../hooks/useFeatures');
12
12
  const mockUseGetBucketVersioning = jest.mocked(useGetBucketVersioning);
@@ -19,7 +19,7 @@ const mockUseGetBucketTagging = jest.mocked(useGetBucketTagging);
19
19
  const mockUseISVBucketStatus = jest.mocked(useISVBucketStatus);
20
20
  const mockUseFeatures = jest.mocked(useFeatures);
21
21
  const mockUseDataBrowserUICustomization = (config = {})=>{
22
- jest.spyOn(__WEBPACK_EXTERNAL_MODULE__contexts_DataBrowserUICustomizationContext_js_f267b01c__, 'useDataBrowserUICustomization').mockReturnValue(config);
22
+ jest.spyOn(__rspack_external__contexts_DataBrowserUICustomizationContext_js_f267b01c, 'useDataBrowserUICustomization').mockReturnValue(config);
23
23
  };
24
24
  const renderBucketOverview = (props = {})=>{
25
25
  const { bucketName = 'test-bucket', onEmptyBucket, onDeleteBucket, onEditPolicy, renderEmptyButton, renderDeleteButton, isEmptyBucketDisabled, isDeleteBucketDisabled } = props;
@@ -5,7 +5,7 @@ import { createTestWrapper, mockOffsetSize, setupMswServer } from "../../test/te
5
5
  import { useListObjectVersions, useListObjects } from "../../hooks/index.js";
6
6
  import { ObjectList } from "../objects/ObjectList.js";
7
7
  import { DataBrowserUICustomizationProvider } from "../../contexts/DataBrowserUICustomizationContext.js";
8
- import * as __WEBPACK_EXTERNAL_MODULE__hooks_useFeatures_js_a6a84786__ from "../../hooks/useFeatures.js";
8
+ import * as __rspack_external__hooks_useFeatures_js_a6a84786 from "../../hooks/useFeatures.js";
9
9
  setupMswServer();
10
10
  const renderObjectList = (props = {}, customization)=>{
11
11
  const Wrapper = createTestWrapper();
@@ -32,7 +32,7 @@ describe('ObjectList', ()=>{
32
32
  beforeEach(()=>{
33
33
  jest.clearAllMocks();
34
34
  mockOffsetSize(800, 600);
35
- jest.spyOn(__WEBPACK_EXTERNAL_MODULE__hooks_useFeatures_js_a6a84786__, 'useFeatures').mockReturnValue(false);
35
+ jest.spyOn(__rspack_external__hooks_useFeatures_js_a6a84786, 'useFeatures').mockReturnValue(false);
36
36
  });
37
37
  afterEach(()=>{
38
38
  jest.restoreAllMocks();
@@ -284,7 +284,7 @@ describe('ObjectList', ()=>{
284
284
  });
285
285
  describe('Search Features', ()=>{
286
286
  it('renders table search when metadata-search feature is disabled', async ()=>{
287
- jest.spyOn(__WEBPACK_EXTERNAL_MODULE__hooks_useFeatures_js_a6a84786__, 'useFeatures').mockReturnValue(false);
287
+ jest.spyOn(__rspack_external__hooks_useFeatures_js_a6a84786, 'useFeatures').mockReturnValue(false);
288
288
  renderObjectList();
289
289
  await waitFor(()=>{
290
290
  expect(screen.getByRole('grid')).toBeInTheDocument();
@@ -292,7 +292,7 @@ describe('ObjectList', ()=>{
292
292
  expect(screen.queryByPlaceholderText(/Metadata Search/i)).not.toBeInTheDocument();
293
293
  });
294
294
  it('renders MetadataSearch component when metadata-search feature is enabled', async ()=>{
295
- jest.spyOn(__WEBPACK_EXTERNAL_MODULE__hooks_useFeatures_js_a6a84786__, 'useFeatures').mockReturnValue(true);
295
+ jest.spyOn(__rspack_external__hooks_useFeatures_js_a6a84786, 'useFeatures').mockReturnValue(true);
296
296
  renderObjectList();
297
297
  await waitFor(()=>{
298
298
  expect(screen.getByRole('grid')).toBeInTheDocument();
@@ -7,7 +7,7 @@ import { BucketDetails } from "./BucketDetails.js";
7
7
  import { BucketList } from "./BucketList.js";
8
8
  import { useDataBrowserNavigate } from "../../hooks/useDataBrowserNavigate.js";
9
9
  import { useEffect } from "react";
10
- const BucketPage_BucketPage = ()=>{
10
+ const BucketPage = ()=>{
11
11
  const { data, status } = useBuckets();
12
12
  const { bucketName } = useParams();
13
13
  const navigate = useDataBrowserNavigate();
@@ -47,5 +47,5 @@ const BucketPage_BucketPage = ()=>{
47
47
  rightPanel: /*#__PURE__*/ jsx(BucketDetails, {})
48
48
  });
49
49
  };
50
- const BucketPage = BucketPage_BucketPage;
51
- export { BucketPage_BucketPage as BucketPage, BucketPage as default };
50
+ const buckets_BucketPage = BucketPage;
51
+ export { BucketPage, buckets_BucketPage as default };
@@ -1,12 +1,12 @@
1
- import * as __WEBPACK_EXTERNAL_MODULE__index_js_95fdb65a__ from "../index.js";
1
+ import * as __rspack_external__index_js_95fdb65a from "../index.js";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { cleanup, render, screen } from "@testing-library/react";
4
4
  import { MemoryRouter, Route, Routes } from "react-router";
5
5
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
6
6
  import { DataBrowserUICustomizationProvider } from "../../../../contexts/DataBrowserUICustomizationContext.js";
7
7
  var __webpack_modules__ = {
8
- "..": function(module) {
9
- module.exports = __WEBPACK_EXTERNAL_MODULE__index_js_95fdb65a__;
8
+ ".." (module) {
9
+ module.exports = __rspack_external__index_js_95fdb65a;
10
10
  }
11
11
  };
12
12
  var __webpack_module_cache__ = {};
@@ -111,7 +111,7 @@ const NoFile = ({ open })=>/*#__PURE__*/ jsxs(EmptyFile, {
111
111
  })
112
112
  ]
113
113
  });
114
- const UploadButton_UploadButton = ({ bucket, prefix = '', uploadOptions = {}, onUploadSuccess, onUploadError })=>{
114
+ const UploadButton = ({ bucket, prefix = '', uploadOptions = {}, onUploadSuccess, onUploadError })=>{
115
115
  const [isModalOpen, setIsModalOpen] = useState(false);
116
116
  const [acceptedFiles, setAcceptedFiles] = useState([]);
117
117
  const uploadMutation = useUploadObjects();
@@ -225,5 +225,5 @@ const UploadButton_UploadButton = ({ bucket, prefix = '', uploadOptions = {}, on
225
225
  ]
226
226
  });
227
227
  };
228
- const UploadButton = UploadButton_UploadButton;
229
- export { UploadButton_UploadButton as UploadButton, UploadButton as default };
228
+ const objects_UploadButton = UploadButton;
229
+ export { UploadButton, objects_UploadButton as default };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,311 @@
1
+ import { HttpResponse, http } from "msw";
2
+ import { setupServer } from "msw/node";
3
+ import { loadRuntimeConfig, s3RuntimeConfigSchema } from "../factory.js";
4
+ const server = setupServer();
5
+ const CONFIG_URL = 'http://localhost/config.json';
6
+ describe('loadRuntimeConfig', ()=>{
7
+ beforeAll(()=>server.listen({
8
+ onUnhandledRequest: 'error'
9
+ }));
10
+ afterEach(()=>server.resetHandlers());
11
+ afterAll(()=>server.close());
12
+ describe('Successful configuration loading', ()=>{
13
+ it('should load and return valid runtime configuration', async ()=>{
14
+ const mockConfig = {
15
+ s3: {
16
+ endpoint: 'https://s3.us-west-2.amazonaws.com',
17
+ region: 'us-west-2',
18
+ forcePathStyle: true
19
+ }
20
+ };
21
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(mockConfig)));
22
+ const config = await loadRuntimeConfig(CONFIG_URL);
23
+ expect(config).toEqual(mockConfig);
24
+ expect(config.s3.endpoint).toBe('https://s3.us-west-2.amazonaws.com');
25
+ expect(config.s3.region).toBe('us-west-2');
26
+ expect(config.s3.forcePathStyle).toBe(true);
27
+ });
28
+ it('should support generic type for extended configuration', async ()=>{
29
+ const mockConfig = {
30
+ s3: {
31
+ endpoint: 'https://s3.amazonaws.com',
32
+ region: 'us-east-1'
33
+ },
34
+ basePath: '/app',
35
+ theme: 'dark',
36
+ customSettings: {
37
+ feature1: true,
38
+ feature2: 42
39
+ }
40
+ };
41
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(mockConfig)));
42
+ const config = await loadRuntimeConfig(CONFIG_URL);
43
+ expect(config.basePath).toBe('/app');
44
+ expect(config.theme).toBe('dark');
45
+ expect(config.customSettings).toEqual({
46
+ feature1: true,
47
+ feature2: 42
48
+ });
49
+ });
50
+ it('should replace "origin" endpoint with window.location.origin', async ()=>{
51
+ const mockConfig = {
52
+ s3: {
53
+ endpoint: 'origin',
54
+ region: 'us-east-1'
55
+ }
56
+ };
57
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(mockConfig)));
58
+ const config = await loadRuntimeConfig(CONFIG_URL);
59
+ expect(config.s3.endpoint).not.toBe('origin');
60
+ expect(config.s3.endpoint).toBe(window.location.origin);
61
+ expect(config.s3.endpoint).toMatch(/^http/);
62
+ });
63
+ it('should handle configuration without optional forcePathStyle', async ()=>{
64
+ const mockConfig = {
65
+ s3: {
66
+ endpoint: 'https://s3.ap-northeast-1.amazonaws.com',
67
+ region: 'ap-northeast-1'
68
+ }
69
+ };
70
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(mockConfig)));
71
+ const config = await loadRuntimeConfig(CONFIG_URL);
72
+ expect(config.s3.forcePathStyle).toBeUndefined();
73
+ expect(config.s3.endpoint).toBe('https://s3.ap-northeast-1.amazonaws.com');
74
+ });
75
+ it('should handle configuration without endpoint', async ()=>{
76
+ const mockConfig = {
77
+ s3: {
78
+ region: 'us-east-1'
79
+ }
80
+ };
81
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(mockConfig)));
82
+ const config = await loadRuntimeConfig(CONFIG_URL);
83
+ expect(config.s3.endpoint).toBeUndefined();
84
+ expect(config.s3.region).toBe('us-east-1');
85
+ });
86
+ });
87
+ describe('HTTP errors', ()=>{
88
+ it("should throw descriptive error when fetch returns 404", async ()=>{
89
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(null, {
90
+ status: 404
91
+ })));
92
+ await expect(loadRuntimeConfig(CONFIG_URL)).rejects.toThrow('Failed to load config: Not Found');
93
+ });
94
+ it("should throw descriptive error when fetch returns 500", async ()=>{
95
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(null, {
96
+ status: 500
97
+ })));
98
+ await expect(loadRuntimeConfig(CONFIG_URL)).rejects.toThrow('Failed to load config: Internal Server Error');
99
+ });
100
+ it('should throw error when fetch returns 403', async ()=>{
101
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(null, {
102
+ status: 403
103
+ })));
104
+ await expect(loadRuntimeConfig(CONFIG_URL)).rejects.toThrow('Failed to load config: Forbidden');
105
+ });
106
+ it('should throw error when response is network error', async ()=>{
107
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.error()));
108
+ await expect(loadRuntimeConfig(CONFIG_URL)).rejects.toThrow();
109
+ });
110
+ });
111
+ describe('Validation errors', ()=>{
112
+ it('should reject config missing required s3.region field', async ()=>{
113
+ const invalidConfig = {
114
+ s3: {
115
+ endpoint: 'https://s3.amazonaws.com'
116
+ }
117
+ };
118
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(invalidConfig)));
119
+ const error = await loadRuntimeConfig(CONFIG_URL).catch((e)=>e);
120
+ expect(error).toBeInstanceOf(Error);
121
+ expect(error.message).toContain('Invalid config');
122
+ expect(error.message).toContain('region');
123
+ });
124
+ it('should reject config completely missing s3 section', async ()=>{
125
+ const invalidConfig = {
126
+ someOtherField: 'value',
127
+ anotherField: 123
128
+ };
129
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(invalidConfig)));
130
+ const error = await loadRuntimeConfig(CONFIG_URL).catch((e)=>e);
131
+ expect(error).toBeInstanceOf(Error);
132
+ expect(error.message).toContain('Invalid config');
133
+ expect(error.message).toContain('s3');
134
+ });
135
+ it('should reject config with invalid region type', async ()=>{
136
+ const invalidConfig = {
137
+ s3: {
138
+ endpoint: 'https://s3.amazonaws.com',
139
+ region: 123
140
+ }
141
+ };
142
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(invalidConfig)));
143
+ const error = await loadRuntimeConfig(CONFIG_URL).catch((e)=>e);
144
+ expect(error).toBeInstanceOf(Error);
145
+ expect(error.message).toContain('Invalid config');
146
+ });
147
+ it('should reject config with invalid forcePathStyle type', async ()=>{
148
+ const invalidConfig = {
149
+ s3: {
150
+ endpoint: 'https://s3.amazonaws.com',
151
+ region: 'us-east-1',
152
+ forcePathStyle: 'yes'
153
+ }
154
+ };
155
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(invalidConfig)));
156
+ const error = await loadRuntimeConfig(CONFIG_URL).catch((e)=>e);
157
+ expect(error).toBeInstanceOf(Error);
158
+ expect(error.message).toContain('Invalid config');
159
+ });
160
+ });
161
+ describe('Edge cases', ()=>{
162
+ it('should handle empty s3 object with only region', async ()=>{
163
+ const mockConfig = {
164
+ s3: {
165
+ region: 'eu-west-1'
166
+ }
167
+ };
168
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(mockConfig)));
169
+ const config = await loadRuntimeConfig(CONFIG_URL);
170
+ expect(config.s3.region).toBe('eu-west-1');
171
+ expect(config.s3.endpoint).toBeUndefined();
172
+ expect(config.s3.forcePathStyle).toBeUndefined();
173
+ });
174
+ it('should preserve all unknown fields for extended configs', async ()=>{
175
+ const mockConfig = {
176
+ s3: {
177
+ region: 'us-east-1'
178
+ },
179
+ customField1: 'value1',
180
+ customField2: 42,
181
+ nested: {
182
+ field: true
183
+ }
184
+ };
185
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(mockConfig)));
186
+ const config = await loadRuntimeConfig(CONFIG_URL);
187
+ expect(config.customField1).toBe('value1');
188
+ expect(config.customField2).toBe(42);
189
+ expect(config.nested).toEqual({
190
+ field: true
191
+ });
192
+ });
193
+ it('should not modify original endpoint when not "origin"', async ()=>{
194
+ const originalEndpoint = 'https://custom-s3.example.com:9000';
195
+ const mockConfig = {
196
+ s3: {
197
+ endpoint: originalEndpoint,
198
+ region: 'us-east-1'
199
+ }
200
+ };
201
+ server.use(http.get(CONFIG_URL, ()=>HttpResponse.json(mockConfig)));
202
+ const config = await loadRuntimeConfig(CONFIG_URL);
203
+ expect(config.s3.endpoint).toBe(originalEndpoint);
204
+ });
205
+ });
206
+ });
207
+ describe('s3RuntimeConfigSchema', ()=>{
208
+ describe('Valid configurations', ()=>{
209
+ it('should validate complete configuration with all fields', ()=>{
210
+ const config = {
211
+ s3: {
212
+ endpoint: 'https://s3.amazonaws.com',
213
+ region: 'us-east-1',
214
+ forcePathStyle: true
215
+ }
216
+ };
217
+ const { error } = s3RuntimeConfigSchema.validate(config);
218
+ expect(error).toBeUndefined();
219
+ });
220
+ it('should validate minimal configuration with only required fields', ()=>{
221
+ const config = {
222
+ s3: {
223
+ region: 'eu-west-1'
224
+ }
225
+ };
226
+ const { error } = s3RuntimeConfigSchema.validate(config);
227
+ expect(error).toBeUndefined();
228
+ });
229
+ it('should allow unknown fields when allowUnknown is true', ()=>{
230
+ const config = {
231
+ s3: {
232
+ endpoint: 'https://s3.amazonaws.com',
233
+ region: 'us-east-1'
234
+ },
235
+ customField: 'value',
236
+ anotherField: 123
237
+ };
238
+ const { error } = s3RuntimeConfigSchema.validate(config, {
239
+ allowUnknown: true
240
+ });
241
+ expect(error).toBeUndefined();
242
+ });
243
+ });
244
+ describe('Invalid configurations', ()=>{
245
+ it('should reject config without s3.region', ()=>{
246
+ const config = {
247
+ s3: {
248
+ endpoint: 'https://s3.amazonaws.com'
249
+ }
250
+ };
251
+ const { error } = s3RuntimeConfigSchema.validate(config);
252
+ expect(error).toBeDefined();
253
+ expect(error?.details[0].path).toContain('region');
254
+ expect(error?.message).toContain('region');
255
+ });
256
+ it('should reject config without s3 object', ()=>{
257
+ const config = {};
258
+ const { error } = s3RuntimeConfigSchema.validate(config);
259
+ expect(error).toBeDefined();
260
+ expect(error?.details[0].path).toContain('s3');
261
+ });
262
+ it('should reject config with wrong type for region', ()=>{
263
+ const config = {
264
+ s3: {
265
+ region: 123
266
+ }
267
+ };
268
+ const { error } = s3RuntimeConfigSchema.validate(config);
269
+ expect(error).toBeDefined();
270
+ expect(error?.message).toContain('string');
271
+ });
272
+ it('should reject config with wrong type for forcePathStyle', ()=>{
273
+ const config = {
274
+ s3: {
275
+ region: 'us-east-1',
276
+ forcePathStyle: 'not-a-boolean'
277
+ }
278
+ };
279
+ const { error } = s3RuntimeConfigSchema.validate(config, {
280
+ convert: false
281
+ });
282
+ expect(error).toBeDefined();
283
+ expect(error?.message).toContain('boolean');
284
+ });
285
+ });
286
+ describe('Schema behavior', ()=>{
287
+ it('should return all validation errors when abortEarly is false', ()=>{
288
+ const config = {
289
+ s3: {}
290
+ };
291
+ const { error } = s3RuntimeConfigSchema.validate(config, {
292
+ abortEarly: false
293
+ });
294
+ expect(error).toBeDefined();
295
+ expect(error?.details.length).toBeGreaterThanOrEqual(1);
296
+ });
297
+ it('should preserve unknown fields when stripUnknown is false', ()=>{
298
+ const config = {
299
+ s3: {
300
+ region: 'us-east-1'
301
+ },
302
+ customField: 'should-be-kept'
303
+ };
304
+ const { value } = s3RuntimeConfigSchema.validate(config, {
305
+ allowUnknown: true,
306
+ stripUnknown: false
307
+ });
308
+ expect(value.customField).toBe('should-be-kept');
309
+ });
310
+ });
311
+ });
@@ -1,6 +1,4 @@
1
1
  import Joi from 'joi';
2
- import type { S3BrowserConfig, S3Credentials } from '../types';
3
- import type { S3ClientConfiguration } from './types';
4
2
  export type S3RuntimeConfig = {
5
3
  s3: {
6
4
  endpoint?: string;
@@ -10,67 +8,10 @@ export type S3RuntimeConfig = {
10
8
  };
11
9
  export declare const s3RuntimeConfigSchema: Joi.ObjectSchema<any>;
12
10
  /**
13
- * Configuration factory that uses build-time constants
14
- * No runtime environment detection or hardcoded values
11
+ * Load runtime configuration from config.json
12
+ *
13
+ * @template T - Extended runtime config type that must include S3RuntimeConfig
14
+ * @param configUrl - URL to fetch the config.json from
15
+ * @returns Promise resolving to the loaded config
15
16
  */
16
- export declare class S3ConfigurationFactory {
17
- /**
18
- * Load runtime configuration from config.json
19
- * Should be called at app startup
20
- *
21
- * @template T - Extended runtime config type that must include S3RuntimeConfig
22
- * @param configUrl - URL to fetch the config.json from
23
- * @returns Promise resolving to the loaded config or null if loading failed
24
- *
25
- * @example
26
- * ```typescript
27
- * type MyConfig = S3RuntimeConfig & {
28
- * theme?: string;
29
- * basePath?: string;
30
- * more: "type";
31
- * };
32
- *
33
- * const config = await loadRuntimeConfig<MyConfig>("/config.json");
34
- * ```
35
- */
36
- static loadRuntimeConfig<T extends S3RuntimeConfig = S3RuntimeConfig>(configUrl: string): Promise<T>;
37
- private static getBuildTimeConfig;
38
- /**
39
- * Create S3 client configuration based on build-time settings
40
- */
41
- static createClientConfiguration(credentials: S3Credentials): S3BrowserConfig & {
42
- credentials: S3Credentials;
43
- };
44
- /**
45
- * Create proxy middleware configuration
46
- */
47
- static createProxyConfiguration(): S3ClientConfiguration;
48
- /**
49
- * Check if proxy middleware should be enabled
50
- */
51
- static shouldUseProxyMiddleware(): boolean;
52
- /**
53
- * Get build-time environment info for debugging
54
- */
55
- static getBuildInfo(): {
56
- environment: string;
57
- useProxy: boolean;
58
- s3Endpoint: string;
59
- proxyEndpoint: string | undefined;
60
- };
61
- }
62
- /**
63
- * Convenience functions for common use cases
64
- */
65
- export declare const createS3Config: (credentials: S3Credentials) => S3BrowserConfig & {
66
- credentials: S3Credentials;
67
- };
68
- export declare const shouldUseProxy: () => boolean;
69
- export declare const getProxyConfig: () => S3ClientConfiguration;
70
- export declare const getBuildInfo: () => {
71
- environment: string;
72
- useProxy: boolean;
73
- s3Endpoint: string;
74
- proxyEndpoint: string | undefined;
75
- };
76
- export declare const loadRuntimeConfig: <T extends S3RuntimeConfig = S3RuntimeConfig>(configUrl: string) => Promise<T>;
17
+ export declare function loadRuntimeConfig<T extends S3RuntimeConfig = S3RuntimeConfig>(configUrl: string): Promise<T>;
@@ -1,89 +1,25 @@
1
1
  import joi from "joi";
2
2
  const s3RuntimeConfigSchema = joi.object({
3
3
  s3: joi.object({
4
- endpoint: joi.string().optional(),
5
- region: joi.string().required(),
4
+ endpoint: joi.string().optional().allow('origin'),
5
+ region: joi.string().min(1).required(),
6
6
  forcePathStyle: joi.boolean().optional()
7
- })
7
+ }).required()
8
8
  });
9
- let runtimeConfig = null;
10
- class S3ConfigurationFactory {
11
- static async loadRuntimeConfig(configUrl) {
12
- try {
13
- const response = await fetch(configUrl);
14
- if (response.ok) {
15
- const data = await response.json();
16
- runtimeConfig = data;
17
- const { error } = s3RuntimeConfigSchema.validate(data, {
18
- allowUnknown: true,
19
- stripUnknown: false,
20
- abortEarly: false
21
- });
22
- if (error) {
23
- const errorMessages = error.details.map((detail)=>detail.message).join(`\n`);
24
- throw new Error(`Invalid runtime configuration, please check your config.json: \n${errorMessages}`);
25
- }
26
- if (runtimeConfig?.s3?.endpoint === 'origin') runtimeConfig.s3.endpoint = window.location.origin;
27
- return data;
28
- }
29
- throw new Error('Failed to load runtime configuration');
30
- } catch (error) {
31
- throw error;
32
- }
33
- }
34
- static getBuildTimeConfig() {
35
- return {
36
- s3: __S3_CONFIG__,
37
- dev: __DEV_CONFIG__,
38
- isDevelopment: __IS_DEVELOPMENT__,
39
- isProduction: __IS_PRODUCTION__
40
- };
41
- }
42
- static createClientConfiguration(credentials) {
43
- const buildConfig = this.getBuildTimeConfig();
44
- const s3Config = buildConfig.isProduction && runtimeConfig?.s3 ? runtimeConfig.s3 : buildConfig.s3;
45
- const baseConfig = {
46
- credentials,
47
- region: s3Config.region,
48
- forcePathStyle: s3Config.forcePathStyle ?? true,
49
- endpoint: s3Config.endpoint
50
- };
51
- if (buildConfig.isDevelopment && buildConfig.dev.useProxy && buildConfig.dev.proxyEndpoint) baseConfig.endpoint = buildConfig.dev.proxyEndpoint;
52
- return baseConfig;
53
- }
54
- static createProxyConfiguration() {
55
- const buildConfig = this.getBuildTimeConfig();
56
- const config = {
57
- endpoint: buildConfig.s3.endpoint,
58
- region: buildConfig.s3.region,
59
- forcePathStyle: buildConfig.s3.forcePathStyle,
60
- useProxy: buildConfig.isDevelopment && buildConfig.dev.useProxy
61
- };
62
- if (config.useProxy) config.proxyConfig = {
63
- realHost: buildConfig.s3.realHost,
64
- proxyBasePath: buildConfig.dev.proxyBasePath,
65
- proxyHost: buildConfig.dev.proxyHost,
66
- proxyPort: buildConfig.dev.proxyPort
67
- };
68
- return config;
69
- }
70
- static shouldUseProxyMiddleware() {
71
- const buildConfig = this.getBuildTimeConfig();
72
- return buildConfig.isDevelopment && buildConfig.dev.useProxy;
73
- }
74
- static getBuildInfo() {
75
- const buildConfig = this.getBuildTimeConfig();
76
- return {
77
- environment: buildConfig.isDevelopment ? 'development' : 'production',
78
- useProxy: buildConfig.dev.useProxy,
79
- s3Endpoint: buildConfig.s3.endpoint,
80
- proxyEndpoint: buildConfig.dev.proxyEndpoint
81
- };
9
+ async function loadRuntimeConfig(configUrl) {
10
+ const response = await fetch(configUrl);
11
+ if (!response.ok) throw new Error(`Failed to load config: ${response.statusText} (${response.status})`);
12
+ const data = await response.json();
13
+ const { error } = s3RuntimeConfigSchema.validate(data, {
14
+ allowUnknown: true,
15
+ abortEarly: false
16
+ });
17
+ if (error) {
18
+ const messages = error.details.map((d)=>d.message).join('; ');
19
+ throw new Error(`Invalid config: ${messages}`);
82
20
  }
21
+ if (data.s3?.endpoint === 'origin') if ('undefined' != typeof window && window.location?.origin) data.s3.endpoint = window.location.origin;
22
+ else throw new Error('Cannot use "origin" keyword outside browser');
23
+ return data;
83
24
  }
84
- const createS3Config = (credentials)=>S3ConfigurationFactory.createClientConfiguration(credentials);
85
- const shouldUseProxy = ()=>S3ConfigurationFactory.shouldUseProxyMiddleware();
86
- const getProxyConfig = ()=>S3ConfigurationFactory.createProxyConfiguration();
87
- const getBuildInfo = ()=>S3ConfigurationFactory.getBuildInfo();
88
- const loadRuntimeConfig = (configUrl)=>S3ConfigurationFactory.loadRuntimeConfig(configUrl);
89
- export { S3ConfigurationFactory, createS3Config, getBuildInfo, getProxyConfig, loadRuntimeConfig, s3RuntimeConfigSchema, shouldUseProxy };
25
+ export { loadRuntimeConfig, s3RuntimeConfigSchema };