@codecademy/brand 3.36.2-alpha.e9c6fd8276.0 → 3.36.3-alpha.2bdd7022f9.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.
@@ -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
- try {
13
- // eslint-disable-next-line no-console
14
- console.log('[searchWorker.init] Importing worker module');
15
- // lazy load worker
16
- const {
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
- try {
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
- try {
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
  };
@@ -28,6 +28,9 @@ function getPortalOrigin() {
28
28
  // for standard envs, use portal app in the same env
29
29
  if (envs.some(s => `https://${s}.codecademy.com` === origin)) return origin;
30
30
 
31
+ // for PR envs (e.g. pr-40229-monolith.dev-eks.codecademy.com)
32
+ if (origin.includes('.dev-eks.codecademy.com')) return origin;
33
+
31
34
  // for local, use local portal-app, replace if origin port is monolith or le
32
35
  if (origin.includes('localhost')) return origin.replace(/:\d{4}/, ':3100');
33
36
 
@@ -35,45 +38,15 @@ function getPortalOrigin() {
35
38
  return 'https://staging.codecademy.com';
36
39
  }
37
40
  export function serializeSearchWorkerSrc() {
38
- // eslint-disable-next-line no-console
39
- console.log('[serializeSearchWorkerSrc] Starting serialization');
40
- try {
41
- // eslint-disable-next-line no-console
42
- console.log('[serializeSearchWorkerSrc] Getting portal origin');
43
- const BASE_URL = `${getPortalOrigin()}/api/portal/search`;
44
- // eslint-disable-next-line no-console
45
- console.log('[serializeSearchWorkerSrc] BASE_URL:', BASE_URL);
46
-
47
- // eslint-disable-next-line no-console
48
- console.log('[serializeSearchWorkerSrc] Converting worker function to string');
49
- const fnAsStr = worker.toString().trim();
50
- // eslint-disable-next-line no-console
51
- console.log('[serializeSearchWorkerSrc] Worker function string length:', fnAsStr.length);
52
- // eslint-disable-next-line no-console
53
- console.log('[serializeSearchWorkerSrc] Full worker function:', fnAsStr);
41
+ const BASE_URL = `${getPortalOrigin()}/api/portal/search`;
42
+ const fnAsStr = worker.toString().trim();
54
43
 
55
- // in a prod build, webpack will handle removing comments
44
+ // in a prod build, webpack will handle removing comments
56
45
 
57
- const startIndex = fnAsStr.indexOf('{');
58
- // eslint-disable-next-line no-console
59
- console.log('[serializeSearchWorkerSrc] Start index of first {:', startIndex);
60
- const result = fnAsStr.slice(fnAsStr.indexOf('{') + 1, -1) // remove wrapping function, which may have been renamed by minification
61
- .replaceAll(' ', '') // remove indentation
62
- .replaceAll('{BASE_URL}', BASE_URL) // interpolate base url
63
- .replaceAll('{SEARCH}', window.location.search); // interpolate search query
64
-
65
- // eslint-disable-next-line no-console
66
- console.log('[serializeSearchWorkerSrc] Serialization complete, result length:', result.length);
67
- // eslint-disable-next-line no-console
68
- console.log('[serializeSearchWorkerSrc] First 500 chars of result:', result.substring(0, 500));
69
- // eslint-disable-next-line no-console
70
- console.log('[serializeSearchWorkerSrc] Full result:', result);
71
- return result;
72
- } catch (error) {
73
- // eslint-disable-next-line no-console
74
- console.error('[serializeSearchWorkerSrc] Error during serialization:', error);
75
- throw error;
76
- }
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
77
50
  }
78
51
 
79
52
  /*
@@ -82,11 +55,7 @@ export function serializeSearchWorkerSrc() {
82
55
  * the worker via onmessage and passed back to the main thread via postMessage.
83
56
  */
84
57
  function worker() {
85
- const preloadTitlesPromise = (async () => {
86
- const f = await fetch('{BASE_URL}/autocomplete-preload{SEARCH}');
87
- const rawTitles = await f.json();
88
- return rawTitles.map(preparseTitle);
89
- })();
58
+ const preloadTitlesPromise = fetch('{BASE_URL}/autocomplete-preload{SEARCH}').then(f => f.json()).then(rawTitles => rawTitles.map(preparseTitle));
90
59
  function preparseTitle({
91
60
  value,
92
61
  popularity
@@ -138,41 +107,43 @@ function worker() {
138
107
  };
139
108
  }
140
109
  const maxResults = 5;
141
- async function autocompleteHandler(q) {
142
- const titles = await preloadTitlesPromise;
143
- const scoredTitles = titles.map(t => getScoredTitle(q, t));
144
- const topAutocompleteTitles = scoredTitles.filter(x => x.score > 0).sort((a, b) => a.score > b.score ? -1 : 1).slice(0, maxResults).map(t => ({
145
- title: t.title,
146
- segments: getHighlightSegments(t.title, t.charScores)
147
- }));
148
- postMessage({
149
- query: q.query,
150
- result: topAutocompleteTitles,
151
- action: 'autocomplete'
110
+ function autocompleteHandler(q) {
111
+ preloadTitlesPromise.then(titles => {
112
+ const scoredTitles = titles.map(t => getScoredTitle(q, t));
113
+ const topAutocompleteTitles = scoredTitles.filter(x => x.score > 0).sort((a, b) => a.score > b.score ? -1 : 1).slice(0, maxResults).map(t => ({
114
+ title: t.title,
115
+ segments: getHighlightSegments(t.title, t.charScores)
116
+ }));
117
+ postMessage({
118
+ query: q.query,
119
+ result: topAutocompleteTitles,
120
+ action: 'autocomplete'
121
+ });
152
122
  });
153
123
  }
154
- async function searchAsYouTypeHandler(q) {
155
- const f = await fetch('{BASE_URL}/search-as-you-type{SEARCH}', {
124
+ function searchAsYouTypeHandler(q) {
125
+ fetch('{BASE_URL}/search-as-you-type{SEARCH}', {
156
126
  body: JSON.stringify({
157
127
  query: q.query,
158
128
  max: maxResults
159
129
  }),
160
130
  method: 'POST'
161
- });
162
- const searchAsYouTypeResults = await f.json();
163
- for (const entry of searchAsYouTypeResults.top) {
164
- const t = preparseTitle({
165
- value: entry.title,
166
- popularity: 0
131
+ }).then(f => f.json()).then(searchAsYouTypeResults => {
132
+ for (let i = 0; i < searchAsYouTypeResults.top.length; i++) {
133
+ const entry = searchAsYouTypeResults.top[i];
134
+ const t = preparseTitle({
135
+ value: entry.title,
136
+ popularity: 0
137
+ });
138
+ const charScores = getCharScores(q, t);
139
+ entry.segments = getHighlightSegments(entry.title, charScores);
140
+ entry.key = q.query + ':' + entry.title;
141
+ }
142
+ postMessage({
143
+ query: q.query,
144
+ result: searchAsYouTypeResults,
145
+ action: 'search-as-you-type'
167
146
  });
168
- const charScores = getCharScores(q, t);
169
- entry.segments = getHighlightSegments(entry.title, charScores);
170
- entry.key = q.query + ':' + entry.title;
171
- }
172
- postMessage({
173
- query: q.query,
174
- result: searchAsYouTypeResults,
175
- action: 'search-as-you-type'
176
147
  });
177
148
  }
178
149
 
@@ -187,17 +158,17 @@ function worker() {
187
158
  // Bonus scores are includes in certain cases
188
159
  function getTotalScore(q, t, charScores) {
189
160
  let fromChars = 0;
190
- for (const s of charScores) {
191
- fromChars += s;
161
+ for (let i = 0; i < charScores.length; i++) {
162
+ fromChars += charScores[i];
192
163
  }
193
164
  const fromPopularity = (t.popularity || 0) * popularityStrength;
194
165
  let bonus = 0;
195
166
  // for each complete word present in both the title and query
196
- for (const qw of q.words) {
167
+ q.words.forEach(qw => {
197
168
  if (t.words.has(qw)) {
198
169
  bonus += 100;
199
170
  }
200
- }
171
+ });
201
172
 
202
173
  // if the title starts with the query
203
174
  if (t.lower.startsWith(q.query)) {
@@ -321,9 +292,11 @@ function worker() {
321
292
  // "authentication" or other words ending in "ion"
322
293
  const minMatchLength = Math.ceil(avgQueryWordLength ** 0.65);
323
294
  const charScores = [];
324
- for (const cmcs of cmcsForTitleChars) {
295
+ for (let i = 0; i < cmcsForTitleChars.length; i++) {
296
+ const cmcs = cmcsForTitleChars[i];
325
297
  let score = 0;
326
- for (const cmc of cmcs) {
298
+ for (let j = 0; j < cmcs.length; j++) {
299
+ const cmc = cmcs[j];
327
300
  // if the string of consecutive matching chars meets the minMatchLength
328
301
  // or if it's a standalone word in the query (e.g "C")
329
302
  if (cmc.value.length >= minMatchLength || q.words.has(cmc.value)) {
@@ -5,74 +5,27 @@ const mockWorker = () => ({
5
5
  postMessage: () => null
6
6
  });
7
7
  export function createSearchWorker() {
8
- // eslint-disable-next-line no-console
9
- console.log('[createSearchWorker] Starting worker creation');
10
- try {
11
- // eslint-disable-next-line no-console
12
- console.log('[createSearchWorker] Serializing worker source');
13
- const src = serializeSearchWorkerSrc();
14
- // eslint-disable-next-line no-console
15
- console.log('[createSearchWorker] Worker source serialized, length:', src.length);
16
-
17
- // eslint-disable-next-line no-console
18
- console.log('[createSearchWorker] Creating blob');
19
- const blob = new Blob([src], {
20
- type: 'text/javascript'
21
- });
22
- // eslint-disable-next-line no-console
23
- console.log('[createSearchWorker] Blob created');
24
-
25
- // eslint-disable-next-line no-console
26
- console.log('[createSearchWorker] Creating object URL');
27
- const dataUrl = URL.createObjectURL?.(blob);
28
- // eslint-disable-next-line no-console
29
- console.log('[createSearchWorker] Object URL created:', dataUrl);
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
- // eslint-disable-next-line no-console
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
- // eslint-disable-next-line no-console
58
- console.log('[sanitizeURL] Input URL:', url);
59
- try {
60
- const [path, params] = url.split('?', 2);
61
- // eslint-disable-next-line no-console
62
- console.log('[sanitizeURL] Path:', path, 'Params:', params);
63
- const sanitizedPath = encodeURI(decodeURI(path));
64
- // eslint-disable-next-line no-console
65
- console.log('[sanitizeURL] Sanitized path:', sanitizedPath);
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
  }
@@ -19,7 +19,9 @@ export const CertificationPathCard = /*#__PURE__*/forwardRef(({
19
19
  }, ref) => {
20
20
  const logoWidthOverrides = {
21
21
  AWS: 28,
22
- Microsoft: 20
22
+ Microsoft: 20,
23
+ HRCI: 28,
24
+ 'Red Hat': 20
23
25
  };
24
26
  return /*#__PURE__*/_jsx(ContentGroupBaseCard, {
25
27
  headerBackgroundColor: "orange-100",
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.e9c6fd8276.0",
4
+ "version": "3.36.3-alpha.2bdd7022f9.0",
5
5
  "author": "Codecademy Engineering <dev@codecademy.com>",
6
6
  "dependencies": {
7
7
  "@emotion/is-prop-valid": "^1.2.1",