@admin-layout/client 12.0.16-alpha.59 → 12.0.16-alpha.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__tests__/ConfigurationModelWrapper.test.d.ts +2 -0
- package/lib/__tests__/ConfigurationModelWrapper.test.d.ts.map +1 -0
- package/lib/components/UpdateSettings/UpdateSettings.server.d.ts +55 -0
- package/lib/components/UpdateSettings/UpdateSettings.server.d.ts.map +1 -1
- package/lib/components/UpdateSettings/UpdateSettings.server.js +128 -70
- package/lib/components/UpdateSettings/UpdateSettings.server.js.map +1 -1
- package/lib/components/UpdateSettings/UpdateSettings.server.test.d.ts +2 -0
- package/lib/components/UpdateSettings/UpdateSettings.server.test.d.ts.map +1 -0
- package/lib/utils/parseEnvUiLayoutSettings.d.ts +6 -0
- package/lib/utils/parseEnvUiLayoutSettings.d.ts.map +1 -0
- package/lib/utils/parseEnvUiLayoutSettings.js +27 -0
- package/lib/utils/parseEnvUiLayoutSettings.js.map +1 -0
- package/lib/utils/parseEnvUiLayoutSettings.test.d.ts +2 -0
- package/lib/utils/parseEnvUiLayoutSettings.test.d.ts.map +1 -0
- package/lib/utils/settingsDifference.d.ts.map +1 -1
- package/lib/utils/settingsDifference.js +29 -100
- package/lib/utils/settingsDifference.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigurationModelWrapper.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ConfigurationModelWrapper.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,7 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie configuration for storing user settings
|
|
3
|
+
* - Max age: 30 days
|
|
4
|
+
* - Client-side accessible for dynamic UI updates
|
|
5
|
+
* - Domain-scoped in production for subdomain sharing
|
|
6
|
+
*/
|
|
1
7
|
export declare const settingsCookie: import("@remix-run/server-runtime").Cookie;
|
|
8
|
+
/**
|
|
9
|
+
* Fetches page settings from GraphQL backend with device/route-specific overrides
|
|
10
|
+
*
|
|
11
|
+
* @param request - The incoming HTTP request (used for forwarding cookies and headers)
|
|
12
|
+
* @param overrideIdentifier - Optional override identifier (e.g., "[mobile]", "[desktop]", "[/dashboard][mobile]")
|
|
13
|
+
* @returns GraphQL query result containing merged settings with overrides applied
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* - The GraphQL backend implements Redis caching for performance
|
|
17
|
+
* - Settings are returned in a flat structure with bracket notation for overrides
|
|
18
|
+
* - Example structure: { logo: '...', theme: '...', '[mobile]': { ... }, '[/dashboard][mobile]': { ... } }
|
|
19
|
+
*/
|
|
2
20
|
export declare function fetchPageSettingsWithFetch(request: Request, overrideIdentifier?: string): Promise<{
|
|
3
21
|
data: any;
|
|
4
22
|
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Universal settings loader utility for both Remix loaders and actions
|
|
25
|
+
*
|
|
26
|
+
* @param params - Object containing request, context, and params
|
|
27
|
+
* @param params.request - The incoming HTTP request
|
|
28
|
+
* @param params.context - Optional Remix load context
|
|
29
|
+
* @param params.params - Optional route parameters
|
|
30
|
+
*
|
|
31
|
+
* @returns Object containing merged settings and cookie string
|
|
32
|
+
* @returns settings - Fully merged settings (GraphQL + env + cookies + request payload)
|
|
33
|
+
* @returns setCookie - Cookie string to set in response headers
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* This utility handles both loader (GET) and action (POST/PUT/DELETE) requests:
|
|
37
|
+
* - **Loaders (GET)**: Fetches and merges settings from GraphQL, env, and cookies
|
|
38
|
+
* - **Actions (POST/PUT)**: Additionally processes request payload and updates cookies
|
|
39
|
+
*
|
|
40
|
+
* Settings merge order (later sources override earlier ones):
|
|
41
|
+
* 1. GraphQL backend settings (with device overrides)
|
|
42
|
+
* 2. Environment variable settings
|
|
43
|
+
* 3. Cookie settings (user preferences)
|
|
44
|
+
* 4. Request payload (only for actions)
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // In a Remix loader
|
|
48
|
+
* export const loader = async ({ request }) => {
|
|
49
|
+
* const { settings, setCookie } = await settingsLoaderUtil({ request });
|
|
50
|
+
* return json({ settings }, { headers: { 'Set-Cookie': setCookie } });
|
|
51
|
+
* };
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // In a Remix action
|
|
55
|
+
* export const action = async ({ request }) => {
|
|
56
|
+
* const { settings, setCookie } = await settingsLoaderUtil({ request });
|
|
57
|
+
* return json(settings, { headers: { 'Set-Cookie': setCookie } });
|
|
58
|
+
* };
|
|
59
|
+
*/
|
|
5
60
|
export declare function settingsLoaderUtil({ request, context, params }: {
|
|
6
61
|
request: any;
|
|
7
62
|
context: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpdateSettings.server.d.ts","sourceRoot":"","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,cAAc,4CAMzB,CAAC;
|
|
1
|
+
{"version":3,"file":"UpdateSettings.server.d.ts","sourceRoot":"","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,eAAO,MAAM,cAAc,4CAMzB,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,kBAAkB,CAAC,EAAE,MAAM;;GAuF7F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE;;;;CAAA;;;GA+GpE"}
|
|
@@ -1,14 +1,31 @@
|
|
|
1
|
-
import {createCookie}from'@remix-run/node';import {ConfigCollectionName,ConfigFragmentName,ConfigurationSchemaId}from'common';import {GetPageSettingsDocument}from'common/graphql';import {merge}from'lodash-es';import {print}from'graphql';import {
|
|
1
|
+
import {createCookie}from'@remix-run/node';import {ConfigCollectionName,ConfigFragmentName,ConfigurationSchemaId}from'common';import {GetPageSettingsDocument}from'common/graphql';import {merge}from'lodash-es';import {print}from'graphql';import {parseEnvLayoutSettings}from'../../utils/parseEnvUiLayoutSettings.js';import {config}from'../../config/env-config.js';import'../../config/defaultSettings.js';import {generateCdecodeUri,DEFAULT_CONTRIBUTION_TENANT_ID}from'@adminide-stack/core';/**
|
|
2
|
+
* Cookie configuration for storing user settings
|
|
3
|
+
* - Max age: 30 days
|
|
4
|
+
* - Client-side accessible for dynamic UI updates
|
|
5
|
+
* - Domain-scoped in production for subdomain sharing
|
|
6
|
+
*/
|
|
7
|
+
const settingsCookie = createCookie('settings', {
|
|
2
8
|
maxAge: 60 * 60 * 24 * 30, // 1 month
|
|
3
9
|
httpOnly: false, // Allow client-side access
|
|
4
10
|
sameSite: 'lax',
|
|
5
11
|
path: '/', // Ensure cookie is available for all paths
|
|
6
12
|
domain: config.isProd ? config.APP_DOMAIN : undefined,
|
|
7
13
|
});
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Fetches page settings from GraphQL backend with device/route-specific overrides
|
|
16
|
+
*
|
|
17
|
+
* @param request - The incoming HTTP request (used for forwarding cookies and headers)
|
|
18
|
+
* @param overrideIdentifier - Optional override identifier (e.g., "[mobile]", "[desktop]", "[/dashboard][mobile]")
|
|
19
|
+
* @returns GraphQL query result containing merged settings with overrides applied
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* - The GraphQL backend implements Redis caching for performance
|
|
23
|
+
* - Settings are returned in a flat structure with bracket notation for overrides
|
|
24
|
+
* - Example structure: { logo: '...', theme: '...', '[mobile]': { ... }, '[/dashboard][mobile]': { ... } }
|
|
25
|
+
*/
|
|
10
26
|
async function fetchPageSettingsWithFetch(request, overrideIdentifier) {
|
|
11
27
|
console.log(`🔍 Fetching settings from GraphQL (Redis-cached backend)...`);
|
|
28
|
+
// Generate resource URI for the application settings
|
|
12
29
|
const resource = generateCdecodeUri(DEFAULT_CONTRIBUTION_TENANT_ID, {
|
|
13
30
|
resourceType: ConfigCollectionName.Applications,
|
|
14
31
|
resourceId: config.APPLICATION_ID,
|
|
@@ -17,27 +34,26 @@ async function fetchPageSettingsWithFetch(request, overrideIdentifier) {
|
|
|
17
34
|
const url = process.env.GRAPHQL_URL || 'http://localhost:8080/graphql';
|
|
18
35
|
// Convert GraphQL DocumentNode to string
|
|
19
36
|
const query = print(GetPageSettingsDocument);
|
|
20
|
-
// Prepare
|
|
37
|
+
// Prepare GraphQL query options
|
|
21
38
|
const options = {
|
|
22
39
|
schemaId: ConfigurationSchemaId.UiLayout,
|
|
23
40
|
target: 2 /* ConfigurationTarget.APPLICATION */,
|
|
24
41
|
configKey: 'uilayout',
|
|
25
42
|
};
|
|
26
|
-
// Add overrides if provided
|
|
43
|
+
// Add device/route overrides if provided
|
|
27
44
|
if (overrideIdentifier) {
|
|
28
45
|
options.overrides = {
|
|
46
|
+
strict: true,
|
|
29
47
|
overrideIdentifier,
|
|
30
48
|
};
|
|
31
49
|
}
|
|
32
|
-
// Add timestamp to force cache invalidation
|
|
33
50
|
const variables = {
|
|
34
51
|
resourceUri: resource,
|
|
35
52
|
options,
|
|
36
|
-
// _timestamp: Date.now(), // Force cache bust
|
|
37
53
|
};
|
|
38
54
|
console.log('🔍 Fetching settings with direct fetch to:', url);
|
|
39
55
|
console.log('🔍 Query variables:', JSON.stringify(variables, null, 2));
|
|
40
|
-
// Forward cookies from the incoming request
|
|
56
|
+
// Forward cookies and headers from the incoming request
|
|
41
57
|
const headers = {
|
|
42
58
|
'Content-Type': 'application/json',
|
|
43
59
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
@@ -78,85 +94,127 @@ async function fetchPageSettingsWithFetch(request, overrideIdentifier) {
|
|
|
78
94
|
throw error;
|
|
79
95
|
}
|
|
80
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Universal settings loader utility for both Remix loaders and actions
|
|
99
|
+
*
|
|
100
|
+
* @param params - Object containing request, context, and params
|
|
101
|
+
* @param params.request - The incoming HTTP request
|
|
102
|
+
* @param params.context - Optional Remix load context
|
|
103
|
+
* @param params.params - Optional route parameters
|
|
104
|
+
*
|
|
105
|
+
* @returns Object containing merged settings and cookie string
|
|
106
|
+
* @returns settings - Fully merged settings (GraphQL + env + cookies + request payload)
|
|
107
|
+
* @returns setCookie - Cookie string to set in response headers
|
|
108
|
+
*
|
|
109
|
+
* @remarks
|
|
110
|
+
* This utility handles both loader (GET) and action (POST/PUT/DELETE) requests:
|
|
111
|
+
* - **Loaders (GET)**: Fetches and merges settings from GraphQL, env, and cookies
|
|
112
|
+
* - **Actions (POST/PUT)**: Additionally processes request payload and updates cookies
|
|
113
|
+
*
|
|
114
|
+
* Settings merge order (later sources override earlier ones):
|
|
115
|
+
* 1. GraphQL backend settings (with device overrides)
|
|
116
|
+
* 2. Environment variable settings
|
|
117
|
+
* 3. Cookie settings (user preferences)
|
|
118
|
+
* 4. Request payload (only for actions)
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* // In a Remix loader
|
|
122
|
+
* export const loader = async ({ request }) => {
|
|
123
|
+
* const { settings, setCookie } = await settingsLoaderUtil({ request });
|
|
124
|
+
* return json({ settings }, { headers: { 'Set-Cookie': setCookie } });
|
|
125
|
+
* };
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* // In a Remix action
|
|
129
|
+
* export const action = async ({ request }) => {
|
|
130
|
+
* const { settings, setCookie } = await settingsLoaderUtil({ request });
|
|
131
|
+
* return json(settings, { headers: { 'Set-Cookie': setCookie } });
|
|
132
|
+
* };
|
|
133
|
+
*/
|
|
81
134
|
async function settingsLoaderUtil({ request, context, params }) {
|
|
82
|
-
|
|
83
|
-
// Detect device type from User-Agent (simplified detection)
|
|
135
|
+
// 1. Detect device type from User-Agent header
|
|
84
136
|
const userAgent = request.headers.get('User-Agent') || '';
|
|
85
137
|
const isMobile = /mobile|android|iphone|ipad|ipod/i.test(userAgent);
|
|
86
138
|
const deviceType = isMobile ? 'mobile' : 'desktop';
|
|
87
|
-
// Fetch
|
|
88
|
-
//
|
|
139
|
+
// 2. Fetch settings from GraphQL backend with device-specific overrides
|
|
140
|
+
// Override identifier format: "[desktop]" or "[mobile]"
|
|
89
141
|
const overrideIdentifier = `[${deviceType}]`;
|
|
90
142
|
const result = await fetchPageSettingsWithFetch(request, overrideIdentifier);
|
|
91
|
-
console.log('
|
|
92
|
-
const
|
|
143
|
+
console.log('ddRESULT=========>', JSON.stringify(result));
|
|
144
|
+
const pageBackendSettings = result?.data?.pageSettings?.settings
|
|
93
145
|
? JSON.parse(JSON.stringify(result?.data?.pageSettings?.settings))
|
|
94
146
|
: null;
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
settingsKeys.includes('hiddenMenuCategories');
|
|
112
|
-
if (hasOnlyDefaultKeys && config.LAYOUT_SETTINGS) {
|
|
113
|
-
const fallbackSettings = typeof config.LAYOUT_SETTINGS === 'string'
|
|
114
|
-
? JSON.parse(config.LAYOUT_SETTINGS)
|
|
115
|
-
: { ...config.LAYOUT_SETTINGS };
|
|
116
|
-
pageDefaultSettings = {
|
|
117
|
-
...fallbackSettings,
|
|
118
|
-
};
|
|
147
|
+
// 3. Parse request payload (only available for POST/PUT requests in actions)
|
|
148
|
+
let requestPayload = {};
|
|
149
|
+
try {
|
|
150
|
+
// Clone request to avoid "body already read" errors
|
|
151
|
+
const clonedRequest = request.clone();
|
|
152
|
+
const contentType = request.headers.get('content-type');
|
|
153
|
+
// Only parse JSON for non-GET requests with JSON content-type
|
|
154
|
+
if (request.method !== 'GET' && contentType?.includes('application/json')) {
|
|
155
|
+
const jsonData = await clonedRequest.json();
|
|
156
|
+
requestPayload = jsonData?.config || {};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
// Expected for GET requests (loaders) - no payload to parse
|
|
161
|
+
console.log('ℹ️ No request payload (likely a loader call)');
|
|
162
|
+
requestPayload = {};
|
|
119
163
|
}
|
|
120
|
-
|
|
121
|
-
|
|
164
|
+
console.log('🔍 DEBUG - pageBackendSettings:', JSON.stringify(pageBackendSettings, null, 2));
|
|
165
|
+
// 4. Parse environment-based layout settings (fallback/default values)
|
|
166
|
+
const envLayoutSettings = parseEnvLayoutSettings();
|
|
167
|
+
// 5. Parse and validate existing cookie settings
|
|
168
|
+
const cookieHeader = request.headers.get('Cookie');
|
|
169
|
+
let existingCookieSettings;
|
|
170
|
+
let cookieUpdatedSettings;
|
|
122
171
|
try {
|
|
123
|
-
|
|
124
|
-
// Remove
|
|
125
|
-
if (
|
|
126
|
-
Object.keys(
|
|
172
|
+
existingCookieSettings = await settingsCookie.parse(cookieHeader);
|
|
173
|
+
// DEFENSIVE: Remove invalid numeric string keys (data corruption prevention)
|
|
174
|
+
if (existingCookieSettings && typeof existingCookieSettings === 'object') {
|
|
175
|
+
Object.keys(existingCookieSettings).forEach((key) => {
|
|
176
|
+
// Remove keys that are numeric strings (like "0", "1", etc.)
|
|
127
177
|
if (/^\d+$/.test(key)) {
|
|
128
|
-
|
|
178
|
+
console.warn(`⚠️ Removing invalid numeric key from cookie settings: "${key}" = ${JSON.stringify(existingCookieSettings[key])}`);
|
|
179
|
+
delete existingCookieSettings[key];
|
|
129
180
|
}
|
|
130
181
|
});
|
|
131
182
|
}
|
|
183
|
+
// 6. Merge existing cookie settings with new request payload
|
|
184
|
+
cookieUpdatedSettings = merge({}, existingCookieSettings, requestPayload);
|
|
185
|
+
// Validate merge result - check for numeric key corruption
|
|
186
|
+
const numericKeysAfterMerge = Object.keys(cookieUpdatedSettings).filter((k) => /^\d+$/.test(k));
|
|
187
|
+
if (numericKeysAfterMerge.length > 0) {
|
|
188
|
+
console.error('❌ NUMERIC KEYS APPEARED AFTER MERGE!', numericKeysAfterMerge);
|
|
189
|
+
console.error('❌ Values:', numericKeysAfterMerge.map((k) => `${k}: ${JSON.stringify(cookieUpdatedSettings[k])}`));
|
|
190
|
+
}
|
|
132
191
|
}
|
|
133
192
|
catch (error) {
|
|
134
|
-
|
|
193
|
+
// If cookie parsing fails, use only the request payload
|
|
194
|
+
console.error('Error parsing settings cookie:', error);
|
|
195
|
+
existingCookieSettings = null;
|
|
196
|
+
cookieUpdatedSettings = requestPayload;
|
|
135
197
|
}
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
198
|
+
// 7. Merge all settings sources in priority order (later sources override earlier)
|
|
199
|
+
// Priority: GraphQL backend → Environment → Cookie + Request payload
|
|
200
|
+
const fullSettings = merge({}, pageBackendSettings, envLayoutSettings, cookieUpdatedSettings);
|
|
201
|
+
// 8. Validate final merged settings - detect and remove numeric key corruption
|
|
202
|
+
const numericKeysAfterFinalMerge = Object.keys(fullSettings).filter((k) => /^\d+$/.test(k));
|
|
203
|
+
if (numericKeysAfterFinalMerge.length > 0) {
|
|
204
|
+
console.error('❌ NUMERIC KEYS APPEARED AFTER FINAL MERGE!', numericKeysAfterFinalMerge);
|
|
205
|
+
console.error('❌ Values:', numericKeysAfterFinalMerge.map((k) => `${k}: ${JSON.stringify(fullSettings[k])}`));
|
|
143
206
|
}
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const mergedBaseSettings = merge({}, baseSettings, config.LAYOUT_SETTINGS, settings);
|
|
157
|
-
const fullSettings = merge({}, mergedBaseSettings, bracketOverrides);
|
|
158
|
-
// Refresh the cookie to keep it active
|
|
159
|
-
const cookie = await settingsCookie.serialize(settings);
|
|
160
|
-
console.log('FULL_SETTINGS', fullSettings);
|
|
207
|
+
// 9. Final cleanup - remove any invalid numeric string keys before returning
|
|
208
|
+
if (fullSettings && typeof fullSettings === 'object') {
|
|
209
|
+
Object.keys(fullSettings).forEach((key) => {
|
|
210
|
+
if (/^\d+$/.test(key)) {
|
|
211
|
+
console.warn(`⚠️ Removing invalid numeric key from fullSettings: "${key}"`);
|
|
212
|
+
delete fullSettings[key];
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
// 10. Serialize cookie settings for response headers
|
|
217
|
+
const cookie = await settingsCookie.serialize(cookieUpdatedSettings);
|
|
218
|
+
console.log('✅ FULL_SETTINGS prepared:', Object.keys(fullSettings).length, 'keys');
|
|
161
219
|
return { settings: fullSettings, setCookie: cookie };
|
|
162
220
|
}export{fetchPageSettingsWithFetch,settingsCookie,settingsLoaderUtil};//# sourceMappingURL=UpdateSettings.server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpdateSettings.server.js","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.ts"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"UpdateSettings.server.js","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;AAyCG,IAAA,IAAA,EAAA,GAAA;AACH,IAAA,MAAA,EAAA,MAAA,CAAA,MAAsB,GAAA,MAAA,CAAA,UAAA,GAAA,SAA2B;;AAuFhD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCG,QAAA,WAAA,EAAA,QAAA;AACH,QAAA,OAAA;;;;AAAqE;;;AA+GpE,QAAA,eAAA,EAAA,qCAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UpdateSettings.server.test.d.ts","sourceRoot":"","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse LAYOUT_SETTINGS from config (which may be a JSON string from env variable)
|
|
3
|
+
* This is the single place where we handle the parsing logic
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseEnvLayoutSettings(): Record<string, any>;
|
|
6
|
+
//# sourceMappingURL=parseEnvUiLayoutSettings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseEnvUiLayoutSettings.d.ts","sourceRoot":"","sources":["../../src/utils/parseEnvUiLayoutSettings.ts"],"names":[],"mappings":"AACA;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAwB5D"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {config}from'../config/env-config.js';import'../config/defaultSettings.js';/**
|
|
2
|
+
* Parse LAYOUT_SETTINGS from config (which may be a JSON string from env variable)
|
|
3
|
+
* This is the single place where we handle the parsing logic
|
|
4
|
+
*/
|
|
5
|
+
function parseEnvLayoutSettings() {
|
|
6
|
+
try {
|
|
7
|
+
if (!config.LAYOUT_SETTINGS) {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
// If it's already an object (parsed by envalid json validator), return it
|
|
11
|
+
if (typeof config.LAYOUT_SETTINGS === 'object' && !Array.isArray(config.LAYOUT_SETTINGS)) {
|
|
12
|
+
return config.LAYOUT_SETTINGS;
|
|
13
|
+
}
|
|
14
|
+
// If it's a string, parse it (this happens when envalid uses str validator)
|
|
15
|
+
if (typeof config.LAYOUT_SETTINGS === 'string') {
|
|
16
|
+
const parsed = JSON.parse(config.LAYOUT_SETTINGS);
|
|
17
|
+
return parsed || {};
|
|
18
|
+
}
|
|
19
|
+
console.warn('⚠️ config.LAYOUT_SETTINGS is unexpected type:', typeof config.LAYOUT_SETTINGS);
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error('❌ Error parsing LAYOUT_SETTINGS:', error);
|
|
24
|
+
console.error('❌ Value was:', config.LAYOUT_SETTINGS);
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
}export{parseEnvLayoutSettings};//# sourceMappingURL=parseEnvUiLayoutSettings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseEnvUiLayoutSettings.js","sources":["../../src/utils/parseEnvUiLayoutSettings.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAIG;AACH;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseEnvUiLayoutSettings.test.d.ts","sourceRoot":"","sources":["../../src/utils/parseEnvUiLayoutSettings.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settingsDifference.d.ts","sourceRoot":"","sources":["../../src/utils/settingsDifference.ts"],"names":[],"mappings":"AAkLA;;;GAGG;AACH,eAAO,MAAM,iCAAiC,GAAI,iBAAiB,GAAG,EAAE,iBAAiB,GAAG,
|
|
1
|
+
{"version":3,"file":"settingsDifference.d.ts","sourceRoot":"","sources":["../../src/utils/settingsDifference.ts"],"names":[],"mappings":"AAkLA;;;GAGG;AACH,eAAO,MAAM,iCAAiC,GAAI,iBAAiB,GAAG,EAAE,iBAAiB,GAAG,QA6D3F,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {cloneDeep,
|
|
1
|
+
import {cloneDeep,isObject,isEqual,transform}from'lodash-es';/**
|
|
2
2
|
* Gets the deep differences between two objects
|
|
3
3
|
* Returns only the values that differ from obj2
|
|
4
4
|
*/
|
|
@@ -63,84 +63,6 @@ const getDifference = (obj1, obj2) => {
|
|
|
63
63
|
// Return undefined if the resulting object is empty
|
|
64
64
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
65
65
|
};
|
|
66
|
-
/**
|
|
67
|
-
* Special handler for route settings differences with improved nested comparison
|
|
68
|
-
*/
|
|
69
|
-
const getRouteSettingsDifferences = (currentRouteSettings, defaultRouteSettings) => {
|
|
70
|
-
const differences = {};
|
|
71
|
-
// Get the default route template (typically from '/')
|
|
72
|
-
const defaultRouteTemplate = defaultRouteSettings['/'] || Object.values(defaultRouteSettings)[0];
|
|
73
|
-
if (!defaultRouteTemplate) {
|
|
74
|
-
console.warn('No default route template found for comparison');
|
|
75
|
-
return currentRouteSettings;
|
|
76
|
-
}
|
|
77
|
-
// Compare each route's settings
|
|
78
|
-
Object.keys(currentRouteSettings).forEach((route) => {
|
|
79
|
-
const currentRouteConfig = currentRouteSettings[route];
|
|
80
|
-
// Use the specific route config if it exists, otherwise use the default template
|
|
81
|
-
const defaultRouteConfig = defaultRouteSettings[route] || defaultRouteTemplate;
|
|
82
|
-
// Compare each section separately (layout, regions, etc)
|
|
83
|
-
const routeDiff = {};
|
|
84
|
-
Object.keys(currentRouteConfig).forEach((section) => {
|
|
85
|
-
if (section === 'layout' || section === 'regions') {
|
|
86
|
-
const sectionDiff = {};
|
|
87
|
-
// Compare desktop and mobile separately
|
|
88
|
-
['desktop', 'mobile'].forEach((device) => {
|
|
89
|
-
if (!currentRouteConfig[section][device]) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
// For regions section with header/footer/background
|
|
93
|
-
if (section === 'regions') {
|
|
94
|
-
const regionsDiff = {};
|
|
95
|
-
['header', 'footer', 'background'].forEach((region) => {
|
|
96
|
-
if (!currentRouteConfig[section][device][region]) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
// Get default path for comparison
|
|
100
|
-
const defaultRegionPath = `${section}.${device}.${region}`;
|
|
101
|
-
const defaultRegion = get(defaultRouteConfig, defaultRegionPath);
|
|
102
|
-
if (defaultRegion) {
|
|
103
|
-
const regionDiff = getDifference(currentRouteConfig[section][device][region], defaultRegion);
|
|
104
|
-
if (regionDiff !== undefined &&
|
|
105
|
-
(Array.isArray(regionDiff) || Object.keys(regionDiff).length > 0)) {
|
|
106
|
-
regionsDiff[region] = regionDiff;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
if (Object.keys(regionsDiff).length > 0) {
|
|
111
|
-
sectionDiff[device] = regionsDiff;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
// For other sections (like layout)
|
|
116
|
-
const defaultDevicePath = `${section}.${device}`;
|
|
117
|
-
const defaultDevice = get(defaultRouteConfig, defaultDevicePath);
|
|
118
|
-
if (defaultDevice) {
|
|
119
|
-
const deviceDiff = getDifference(currentRouteConfig[section][device], defaultDevice);
|
|
120
|
-
if (deviceDiff !== undefined && Object.keys(deviceDiff).length > 0) {
|
|
121
|
-
sectionDiff[device] = deviceDiff;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
if (Object.keys(sectionDiff).length > 0) {
|
|
127
|
-
routeDiff[section] = sectionDiff;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
const sectionDiff = getDifference(currentRouteConfig[section], defaultRouteConfig[section]);
|
|
132
|
-
if (sectionDiff !== undefined && Object.keys(sectionDiff).length > 0) {
|
|
133
|
-
routeDiff[section] = sectionDiff;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
// Only include the route if there are actual differences
|
|
138
|
-
if (Object.keys(routeDiff).length > 0) {
|
|
139
|
-
differences[route] = routeDiff;
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
return Object.keys(differences).length > 0 ? differences : undefined;
|
|
143
|
-
};
|
|
144
66
|
/**
|
|
145
67
|
* Compares settings objects and saves only the differences
|
|
146
68
|
* @returns An object containing only the differences, or an empty object on error
|
|
@@ -154,39 +76,46 @@ const compareAndSaveSettingsDifferences = (currentSettings, defaultSettings) =>
|
|
|
154
76
|
// Make deep clones to avoid modifying the original objects
|
|
155
77
|
const current = cloneDeep(currentSettings);
|
|
156
78
|
const defaults = cloneDeep(defaultSettings);
|
|
157
|
-
//
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
79
|
+
// Extract overrides (bracket keys) from current settings
|
|
80
|
+
const overrides = {};
|
|
81
|
+
Object.keys(current).forEach((key) => {
|
|
82
|
+
if (key.startsWith('[') && key.endsWith(']')) {
|
|
83
|
+
overrides[key] = current[key];
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// Get differences for non-override settings (base settings)
|
|
87
|
+
const currentBase = { ...current };
|
|
88
|
+
const defaultBase = { ...defaults };
|
|
89
|
+
// Remove all bracket keys from both objects
|
|
90
|
+
Object.keys(currentBase).forEach((key) => {
|
|
91
|
+
if (key.startsWith('[') && key.endsWith(']')) {
|
|
92
|
+
delete currentBase[key];
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
Object.keys(defaultBase).forEach((key) => {
|
|
96
|
+
if (key.startsWith('[') && key.endsWith(']')) {
|
|
97
|
+
delete defaultBase[key];
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
const baseSettingsDiff = getDifference(currentBase, defaultBase);
|
|
101
|
+
// Combine base settings differences with overrides
|
|
169
102
|
const diffSettings = {};
|
|
170
|
-
// Add
|
|
171
|
-
if (
|
|
172
|
-
Object.assign(diffSettings,
|
|
173
|
-
}
|
|
174
|
-
// Add route settings differences if any exist
|
|
175
|
-
if (routeSettingsDiff && Object.keys(routeSettingsDiff).length > 0) {
|
|
176
|
-
diffSettings.routeSettings = routeSettingsDiff;
|
|
103
|
+
// Add base settings differences if any exist
|
|
104
|
+
if (baseSettingsDiff && Object.keys(baseSettingsDiff).length > 0) {
|
|
105
|
+
Object.assign(diffSettings, baseSettingsDiff);
|
|
177
106
|
}
|
|
107
|
+
// Add all overrides (they are always different from defaults)
|
|
108
|
+
Object.assign(diffSettings, overrides);
|
|
178
109
|
// If there are no differences at all, return early
|
|
179
110
|
if (Object.keys(diffSettings).length === 0) {
|
|
180
111
|
return {};
|
|
181
112
|
}
|
|
182
113
|
// Remove empty objects from the diff
|
|
183
|
-
// This helps prevent unnecessary properties in the payload
|
|
184
114
|
const cleanDiffSettings = removeEmptyObjects(diffSettings);
|
|
185
115
|
return cleanDiffSettings;
|
|
186
116
|
}
|
|
187
117
|
catch (error) {
|
|
188
118
|
console.error('Error comparing settings differences:', error);
|
|
189
|
-
// Return empty object instead of null to avoid downstream errors
|
|
190
119
|
return {};
|
|
191
120
|
}
|
|
192
121
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settingsDifference.js","sources":["../../src/utils/settingsDifference.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAqLG;AACH
|
|
1
|
+
{"version":3,"file":"settingsDifference.js","sources":["../../src/utils/settingsDifference.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAqLG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@admin-layout/client",
|
|
3
|
-
"version": "12.0.16-alpha.
|
|
3
|
+
"version": "12.0.16-alpha.64",
|
|
4
4
|
"description": "Sample client for higher packages to depend on",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "CDMBase LLC",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"serialize-error": "^8.0.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"common": "12.0.16-alpha.
|
|
29
|
+
"common": "12.0.16-alpha.64",
|
|
30
30
|
"rc-menu": "^9.16.1"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"typescript": {
|
|
45
45
|
"definition": "lib/index.d.ts"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "506bff5baaa896802376e745517c2e8c295a9db8"
|
|
48
48
|
}
|