@codecademy/brand 3.36.2-alpha.4249fc7d9c.0 → 3.36.2-alpha.89d0f59c01.0
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/dist/AppHeader/Search/SearchButton.js +0 -29
- package/dist/AppHeader/Search/SearchPane.js +9 -42
- package/dist/AppHeader/Search/SearchWorker/index.js +7 -55
- package/dist/AppHeader/Search/SearchWorker/src.js +7 -57
- package/dist/AppHeader/Search/SearchWorker/worker.js +23 -70
- package/dist/AppHeader/Search/safelyRedirect.js +11 -50
- package/package.json +1 -1
|
@@ -3,70 +3,22 @@ let workerPromise;
|
|
|
3
3
|
const initErr = 'Search worker not initialized';
|
|
4
4
|
export const searchWorker = {
|
|
5
5
|
init() {
|
|
6
|
-
// eslint-disable-next-line no-console
|
|
7
|
-
console.log('[searchWorker.init] Initializing search worker');
|
|
8
6
|
if (!workerPromise) {
|
|
9
|
-
// eslint-disable-next-line no-console
|
|
10
|
-
console.log('[searchWorker.init] Creating new worker promise');
|
|
11
7
|
workerPromise = (async () => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
createSearchWorker
|
|
18
|
-
} = await import('./worker');
|
|
19
|
-
// eslint-disable-next-line no-console
|
|
20
|
-
console.log('[searchWorker.init] Worker module imported, creating worker');
|
|
21
|
-
const worker = createSearchWorker();
|
|
22
|
-
// eslint-disable-next-line no-console
|
|
23
|
-
console.log('[searchWorker.init] Worker created successfully');
|
|
24
|
-
return worker;
|
|
25
|
-
} catch (error) {
|
|
26
|
-
// eslint-disable-next-line no-console
|
|
27
|
-
console.error('[searchWorker.init] Error creating worker:', error);
|
|
28
|
-
throw error;
|
|
29
|
-
}
|
|
8
|
+
// lazy load worker
|
|
9
|
+
const {
|
|
10
|
+
createSearchWorker
|
|
11
|
+
} = await import('./worker');
|
|
12
|
+
return createSearchWorker();
|
|
30
13
|
})();
|
|
31
|
-
} else {
|
|
32
|
-
// eslint-disable-next-line no-console
|
|
33
|
-
console.log('[searchWorker.init] Worker already initialized');
|
|
34
14
|
}
|
|
35
15
|
},
|
|
36
16
|
async autocomplete(query) {
|
|
37
|
-
// eslint-disable-next-line no-console
|
|
38
|
-
console.log('[searchWorker.autocomplete] Query:', query);
|
|
39
17
|
if (!workerPromise) throw new Error(initErr);
|
|
40
|
-
|
|
41
|
-
const worker = await workerPromise;
|
|
42
|
-
// eslint-disable-next-line no-console
|
|
43
|
-
console.log('[searchWorker.autocomplete] Worker ready, calling with query:', query);
|
|
44
|
-
const result = await worker(SearchAction.Autocomplete, query);
|
|
45
|
-
// eslint-disable-next-line no-console
|
|
46
|
-
console.log('[searchWorker.autocomplete] Result received:', result);
|
|
47
|
-
return result;
|
|
48
|
-
} catch (error) {
|
|
49
|
-
// eslint-disable-next-line no-console
|
|
50
|
-
console.error('[searchWorker.autocomplete] Error:', error);
|
|
51
|
-
throw error;
|
|
52
|
-
}
|
|
18
|
+
return (await workerPromise)(SearchAction.Autocomplete, query);
|
|
53
19
|
},
|
|
54
20
|
async searchAsYouType(query) {
|
|
55
|
-
// eslint-disable-next-line no-console
|
|
56
|
-
console.log('[searchWorker.searchAsYouType] Query:', query);
|
|
57
21
|
if (!workerPromise) throw new Error(initErr);
|
|
58
|
-
|
|
59
|
-
const worker = await workerPromise;
|
|
60
|
-
// eslint-disable-next-line no-console
|
|
61
|
-
console.log('[searchWorker.searchAsYouType] Worker ready, calling with query:', query);
|
|
62
|
-
const result = await worker(SearchAction.SearchAsYouType, query);
|
|
63
|
-
// eslint-disable-next-line no-console
|
|
64
|
-
console.log('[searchWorker.searchAsYouType] Result received:', result);
|
|
65
|
-
return result;
|
|
66
|
-
} catch (error) {
|
|
67
|
-
// eslint-disable-next-line no-console
|
|
68
|
-
console.error('[searchWorker.searchAsYouType] Error:', error);
|
|
69
|
-
throw error;
|
|
70
|
-
}
|
|
22
|
+
return (await workerPromise)(SearchAction.SearchAsYouType, query);
|
|
71
23
|
}
|
|
72
24
|
};
|
|
@@ -38,65 +38,15 @@ function getPortalOrigin() {
|
|
|
38
38
|
return 'https://staging.codecademy.com';
|
|
39
39
|
}
|
|
40
40
|
export function serializeSearchWorkerSrc() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
// eslint-disable-next-line no-console
|
|
45
|
-
console.log('[serializeSearchWorkerSrc] Getting portal origin');
|
|
46
|
-
const BASE_URL = `${getPortalOrigin()}/api/portal/search`;
|
|
47
|
-
// eslint-disable-next-line no-console
|
|
48
|
-
console.log('[serializeSearchWorkerSrc] BASE_URL:', BASE_URL);
|
|
41
|
+
const BASE_URL = `${getPortalOrigin()}/api/portal/search`;
|
|
42
|
+
const fnAsStr = worker.toString().trim();
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
console.log('[serializeSearchWorkerSrc] Converting worker function to string');
|
|
52
|
-
const fnAsStr = worker.toString().trim();
|
|
53
|
-
// eslint-disable-next-line no-console
|
|
54
|
-
console.log('[serializeSearchWorkerSrc] Worker function string length:', fnAsStr.length);
|
|
55
|
-
// eslint-disable-next-line no-console
|
|
56
|
-
console.log('[serializeSearchWorkerSrc] Full worker function:', fnAsStr);
|
|
44
|
+
// in a prod build, webpack will handle removing comments
|
|
57
45
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
console.log('[serializeSearchWorkerSrc] Start index of first {:', startIndex);
|
|
63
|
-
const result = fnAsStr.slice(fnAsStr.indexOf('{') + 1, -1) // remove wrapping function, which may have been renamed by minification
|
|
64
|
-
.replaceAll(' ', '') // remove indentation
|
|
65
|
-
.replaceAll('{BASE_URL}', BASE_URL) // interpolate base url
|
|
66
|
-
.replaceAll('{SEARCH}', window.location.search); // interpolate search query
|
|
67
|
-
|
|
68
|
-
// Check for external dependencies that would break the worker
|
|
69
|
-
const externalDeps = [];
|
|
70
|
-
if (result.includes('Object(h.a)') || result.includes('h.a')) {
|
|
71
|
-
externalDeps.push('h (Babel async helper)');
|
|
72
|
-
}
|
|
73
|
-
if (result.includes('p.a.mark') || result.includes('p.a.wrap')) {
|
|
74
|
-
externalDeps.push('p (Regenerator runtime)');
|
|
75
|
-
}
|
|
76
|
-
if (result.includes('_createForOfIteratorHelper')) {
|
|
77
|
-
externalDeps.push('_createForOfIteratorHelper (Babel helper)');
|
|
78
|
-
}
|
|
79
|
-
if (externalDeps.length > 0) {
|
|
80
|
-
// eslint-disable-next-line no-console
|
|
81
|
-
console.error('[serializeSearchWorkerSrc] ERROR: Worker code contains external dependencies that will not be available in worker context:', externalDeps);
|
|
82
|
-
// eslint-disable-next-line no-console
|
|
83
|
-
console.error('[serializeSearchWorkerSrc] This is likely because the worker function uses async/await or modern JS features that Babel transpiles with external helpers.');
|
|
84
|
-
// eslint-disable-next-line no-console
|
|
85
|
-
console.error('[serializeSearchWorkerSrc] The worker function needs to be self-contained or the build configuration needs to be adjusted to inline helpers.');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// eslint-disable-next-line no-console
|
|
89
|
-
console.log('[serializeSearchWorkerSrc] Serialization complete, result length:', result.length);
|
|
90
|
-
// eslint-disable-next-line no-console
|
|
91
|
-
console.log('[serializeSearchWorkerSrc] First 500 chars of result:', result.substring(0, 500));
|
|
92
|
-
// eslint-disable-next-line no-console
|
|
93
|
-
console.log('[serializeSearchWorkerSrc] Full result:', result);
|
|
94
|
-
return result;
|
|
95
|
-
} catch (error) {
|
|
96
|
-
// eslint-disable-next-line no-console
|
|
97
|
-
console.error('[serializeSearchWorkerSrc] Error during serialization:', error);
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
46
|
+
return fnAsStr.slice(fnAsStr.indexOf('{') + 1, -1) // remove wrapping function, which may have been renamed by minification
|
|
47
|
+
.replaceAll(' ', '') // remove indentation
|
|
48
|
+
.replaceAll('{BASE_URL}', BASE_URL) // interpolate base url
|
|
49
|
+
.replaceAll('{SEARCH}', window.location.search); // interpolate search query
|
|
100
50
|
}
|
|
101
51
|
|
|
102
52
|
/*
|
|
@@ -5,74 +5,27 @@ const mockWorker = () => ({
|
|
|
5
5
|
postMessage: () => null
|
|
6
6
|
});
|
|
7
7
|
export function createSearchWorker() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// eslint-disable-next-line no-console
|
|
32
|
-
console.log('[createSearchWorker] Checking Worker availability');
|
|
33
|
-
const w = typeof Worker === 'undefined' ? mockWorker() : new Worker(dataUrl);
|
|
34
|
-
// eslint-disable-next-line no-console
|
|
35
|
-
console.log('[createSearchWorker] Worker instance created');
|
|
36
|
-
const results = new PromiseLookup({
|
|
37
|
-
onKeyInit: (action, query) => {
|
|
38
|
-
// eslint-disable-next-line no-console
|
|
39
|
-
console.log('[createSearchWorker.onKeyInit] Posting message to worker:', {
|
|
40
|
-
action,
|
|
41
|
-
query
|
|
42
|
-
});
|
|
43
|
-
w.postMessage({
|
|
44
|
-
action,
|
|
45
|
-
query
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
w.onmessage = ({
|
|
50
|
-
data
|
|
51
|
-
}) => {
|
|
52
|
-
// eslint-disable-next-line no-console
|
|
53
|
-
console.log('[createSearchWorker.onmessage] Received message from worker:', data);
|
|
54
|
-
const {
|
|
55
|
-
query,
|
|
56
|
-
result,
|
|
57
|
-
action
|
|
58
|
-
} = data;
|
|
59
|
-
results.resolve(action, query, result);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// Add error handler for the worker
|
|
63
|
-
if ('onerror' in w) {
|
|
64
|
-
w.onerror = error => {
|
|
65
|
-
// eslint-disable-next-line no-console
|
|
66
|
-
console.error('[createSearchWorker] Worker error:', error);
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// eslint-disable-next-line no-console
|
|
71
|
-
console.log('[createSearchWorker] Worker fully configured');
|
|
72
|
-
return (action, query) => results.get(action, query);
|
|
73
|
-
} catch (error) {
|
|
74
|
-
// eslint-disable-next-line no-console
|
|
75
|
-
console.error('[createSearchWorker] Error creating worker:', error);
|
|
76
|
-
throw error;
|
|
77
|
-
}
|
|
8
|
+
const src = serializeSearchWorkerSrc();
|
|
9
|
+
const blob = new Blob([src], {
|
|
10
|
+
type: 'text/javascript'
|
|
11
|
+
});
|
|
12
|
+
const dataUrl = URL.createObjectURL?.(blob);
|
|
13
|
+
const w = typeof Worker === 'undefined' ? mockWorker() : new Worker(dataUrl);
|
|
14
|
+
const results = new PromiseLookup({
|
|
15
|
+
onKeyInit: (action, query) => w.postMessage({
|
|
16
|
+
action,
|
|
17
|
+
query
|
|
18
|
+
})
|
|
19
|
+
});
|
|
20
|
+
w.onmessage = ({
|
|
21
|
+
data
|
|
22
|
+
}) => {
|
|
23
|
+
const {
|
|
24
|
+
query,
|
|
25
|
+
result,
|
|
26
|
+
action
|
|
27
|
+
} = data;
|
|
28
|
+
results.resolve(action, query, result);
|
|
29
|
+
};
|
|
30
|
+
return (action, query) => results.get(action, query);
|
|
78
31
|
}
|
|
@@ -5,31 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
import { ALLOWED_HOSTS } from './consts';
|
|
7
7
|
export function safelyRedirect(url) {
|
|
8
|
-
// eslint-disable-next-line no-console
|
|
9
|
-
console.log('[safelyRedirect] Attempting to redirect to:', url);
|
|
10
8
|
try {
|
|
11
|
-
// eslint-disable-next-line no-console
|
|
12
|
-
console.log('[safelyRedirect] Sanitizing URL');
|
|
13
9
|
const sanitizedURL = sanitizeURL(url);
|
|
14
|
-
|
|
15
|
-
console.log('[safelyRedirect] Sanitized URL:', sanitizedURL);
|
|
16
|
-
const isRelative = isRelativeUrl(sanitizedURL);
|
|
17
|
-
// eslint-disable-next-line no-console
|
|
18
|
-
console.log('[safelyRedirect] Is relative URL:', isRelative);
|
|
19
|
-
const hasAllowed = hasAllowedHost(sanitizedURL);
|
|
20
|
-
// eslint-disable-next-line no-console
|
|
21
|
-
console.log('[safelyRedirect] Has allowed host:', hasAllowed);
|
|
22
|
-
if (isRelative || hasAllowed) {
|
|
23
|
-
// eslint-disable-next-line no-console
|
|
24
|
-
console.log('[safelyRedirect] Redirecting to:', sanitizedURL);
|
|
10
|
+
if (isRelativeUrl(sanitizedURL) || hasAllowedHost(sanitizedURL)) {
|
|
25
11
|
window.location.assign(sanitizedURL);
|
|
26
12
|
} else {
|
|
27
13
|
throw new Error(`Invalid redirect url: ${sanitizedURL}`);
|
|
28
14
|
}
|
|
29
|
-
} catch (error) {
|
|
30
|
-
// eslint-disable-next-line no-console
|
|
31
|
-
console.error('[safelyRedirect] Error during redirect:', error);
|
|
32
|
-
}
|
|
15
|
+
} catch (error) {}
|
|
33
16
|
}
|
|
34
17
|
function hasAllowedHost(url) {
|
|
35
18
|
const parsedUrl = new URL(url);
|
|
@@ -54,35 +37,13 @@ function isRelativeUrl(url) {
|
|
|
54
37
|
// supports already encoded urls by decoding and re-encoding.
|
|
55
38
|
// params are handled separately to allow for the encoding of / characters
|
|
56
39
|
function sanitizeURL(url) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!params) return sanitizedPath;
|
|
67
|
-
|
|
68
|
-
// eslint-disable-next-line no-console
|
|
69
|
-
console.log('[sanitizeURL] Creating URLSearchParams with:', params);
|
|
70
|
-
const urlParams = new URLSearchParams(params);
|
|
71
|
-
// eslint-disable-next-line no-console
|
|
72
|
-
console.log('[sanitizeURL] URLSearchParams created');
|
|
73
|
-
urlParams.forEach((value, key) => {
|
|
74
|
-
// eslint-disable-next-line no-console
|
|
75
|
-
console.log('[sanitizeURL] Processing param:', key, '=', value);
|
|
76
|
-
const decoded = decodeURIComponent(value);
|
|
77
|
-
urlParams.set(key, decoded);
|
|
78
|
-
});
|
|
79
|
-
const result = `${sanitizedPath}?${urlParams.toString()}`;
|
|
80
|
-
// eslint-disable-next-line no-console
|
|
81
|
-
console.log('[sanitizeURL] Final result:', result);
|
|
82
|
-
return result;
|
|
83
|
-
} catch (error) {
|
|
84
|
-
// eslint-disable-next-line no-console
|
|
85
|
-
console.error('[sanitizeURL] Error sanitizing URL:', error);
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
40
|
+
const [path, params] = url.split('?', 2);
|
|
41
|
+
const sanitizedPath = encodeURI(decodeURI(path));
|
|
42
|
+
if (!params) return sanitizedPath;
|
|
43
|
+
const urlParams = new URLSearchParams(params);
|
|
44
|
+
urlParams.forEach((value, key) => {
|
|
45
|
+
const decoded = decodeURIComponent(value);
|
|
46
|
+
urlParams.set(key, decoded);
|
|
47
|
+
});
|
|
48
|
+
return `${sanitizedPath}?${urlParams.toString()}`;
|
|
88
49
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codecademy/brand",
|
|
3
3
|
"description": "Brand component library for Codecademy",
|
|
4
|
-
"version": "3.36.2-alpha.
|
|
4
|
+
"version": "3.36.2-alpha.89d0f59c01.0",
|
|
5
5
|
"author": "Codecademy Engineering <dev@codecademy.com>",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@emotion/is-prop-valid": "^1.2.1",
|