@prodigio-io/sdk 2.1.4 → 2.1.7

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/index.d.mts CHANGED
@@ -6,6 +6,9 @@ interface ProdigioInitConfig {
6
6
  key: string;
7
7
  badgeToken?: string;
8
8
  baseUrl?: string;
9
+ /** CSS selector for the element the badge should be appended into.
10
+ * e.g. '#careers-footer'. Overrides automatic container detection. */
11
+ badgeContainer?: string;
9
12
  badge?: {
10
13
  position?: 'bottom-right' | 'bottom-left';
11
14
  };
@@ -116,8 +119,8 @@ declare class Prodigio {
116
119
  name: string;
117
120
  content: string;
118
121
  };
119
- static destroy(): void;
120
122
  static init(config: ProdigioInitConfig): Promise<void>;
123
+ static destroy(): void;
121
124
  }
122
125
 
123
126
  export { type Achievement, type Applicant, type ApplicationResult, type BadgeProps, type CVInfo, type Job, type ListJobsParams, type PoweredBy, Prodigio, type ProdigioConfig, ProdigioError, type ProdigioInitConfig, type SalaryRange, type SubmitApplicationParams, type UploadResult, Prodigio as default };
package/dist/index.d.ts CHANGED
@@ -6,6 +6,9 @@ interface ProdigioInitConfig {
6
6
  key: string;
7
7
  badgeToken?: string;
8
8
  baseUrl?: string;
9
+ /** CSS selector for the element the badge should be appended into.
10
+ * e.g. '#careers-footer'. Overrides automatic container detection. */
11
+ badgeContainer?: string;
9
12
  badge?: {
10
13
  position?: 'bottom-right' | 'bottom-left';
11
14
  };
@@ -116,8 +119,8 @@ declare class Prodigio {
116
119
  name: string;
117
120
  content: string;
118
121
  };
119
- static destroy(): void;
120
122
  static init(config: ProdigioInitConfig): Promise<void>;
123
+ static destroy(): void;
121
124
  }
122
125
 
123
126
  export { type Achievement, type Applicant, type ApplicationResult, type BadgeProps, type CVInfo, type Job, type ListJobsParams, type PoweredBy, Prodigio, type ProdigioConfig, ProdigioError, type ProdigioInitConfig, type SalaryRange, type SubmitApplicationParams, type UploadResult, Prodigio as default };
package/dist/index.js CHANGED
@@ -92,7 +92,7 @@ async function verifyBadgeToken(token) {
92
92
  }
93
93
  }
94
94
  var BADGE_ELEMENT_ID = "prodigio-badge-widget";
95
- function injectBadge(_position) {
95
+ function injectBadge(_position, badgeContainer) {
96
96
  if (typeof document === "undefined") return;
97
97
  if (document.getElementById(BADGE_ELEMENT_ID)) return;
98
98
  const wrapper = document.createElement("div");
@@ -134,7 +134,7 @@ function injectBadge(_position) {
134
134
  el.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
135
135
  });
136
136
  wrapper.appendChild(el);
137
- document.body.appendChild(wrapper);
137
+ findBadgeContainer(badgeContainer).appendChild(wrapper);
138
138
  }
139
139
  function removeBadge() {
140
140
  var _a;
@@ -155,16 +155,78 @@ function getCachedToken(apiKey) {
155
155
  return null;
156
156
  }
157
157
  }
158
+ var _initialized = false;
158
159
  var _routeWatcherInstalled = false;
159
- var _routeChangeCallbacks = [];
160
+ var _rafHandle = null;
161
+ var _anchorObserver = null;
162
+ var _anchorTimeout = null;
163
+ var ANCHOR_WAIT_MS = 3e3;
164
+ function normalizePath(p) {
165
+ return p.replace(/\/$/, "").split("?")[0];
166
+ }
167
+ function pathMatches(current, careersPath) {
168
+ const c = normalizePath(current);
169
+ const target = normalizePath(careersPath);
170
+ return c === target || c.startsWith(target + "/");
171
+ }
172
+ function cancelPending() {
173
+ if (_rafHandle !== null) {
174
+ cancelAnimationFrame(_rafHandle);
175
+ _rafHandle = null;
176
+ }
177
+ if (_anchorObserver !== null) {
178
+ _anchorObserver.disconnect();
179
+ _anchorObserver = null;
180
+ }
181
+ if (_anchorTimeout !== null) {
182
+ clearTimeout(_anchorTimeout);
183
+ _anchorTimeout = null;
184
+ }
185
+ }
186
+ function waitForAnchor(badgeContainer, onFound) {
187
+ _anchorObserver = new MutationObserver(() => {
188
+ if (findBadgeAnchor(badgeContainer)) {
189
+ cancelPending();
190
+ onFound();
191
+ }
192
+ });
193
+ _anchorObserver.observe(document.body, { childList: true, subtree: true });
194
+ _anchorTimeout = setTimeout(() => {
195
+ cancelPending();
196
+ if (process.env.NODE_ENV !== "production") {
197
+ console.warn("[Prodigio] Badge anchor not found within 3s. Add <div data-prodigio-badge-anchor></div> to your page.");
198
+ }
199
+ }, ANCHOR_WAIT_MS);
200
+ }
201
+ function findBadgeAnchor(badgeContainer) {
202
+ if (badgeContainer) {
203
+ const el = document.querySelector(badgeContainer);
204
+ if (el) return el;
205
+ }
206
+ const anchor = document.querySelector("[data-prodigio-badge-anchor]");
207
+ if (anchor) return anchor;
208
+ return null;
209
+ }
210
+ function findBadgeContainer(badgeContainer) {
211
+ const anchor = findBadgeAnchor(badgeContainer);
212
+ if (anchor) return anchor;
213
+ const selectors = ["main", '[role="main"]', "#root", "#__next", "#app"];
214
+ for (const sel of selectors) {
215
+ const el = document.querySelector(sel);
216
+ if (el) return el;
217
+ }
218
+ return document.body;
219
+ }
160
220
  function installRouteWatcher() {
161
221
  if (_routeWatcherInstalled || typeof window === "undefined") return;
162
222
  _routeWatcherInstalled = true;
163
- const dispatch = () => _routeChangeCallbacks.forEach((cb) => cb());
164
223
  const orig = {
165
224
  push: history.pushState.bind(history),
166
225
  replace: history.replaceState.bind(history)
167
226
  };
227
+ const dispatch = () => {
228
+ _routeChangeCallbacks.forEach((cb) => cb());
229
+ };
168
230
  history.pushState = (...args) => {
169
231
  orig.push(...args);
170
232
  dispatch();
@@ -176,15 +238,7 @@ function installRouteWatcher() {
176
238
  window.addEventListener("popstate", dispatch);
177
239
  window.addEventListener("hashchange", dispatch);
178
240
  }
179
- var _initialized = false;
180
- function normalizePath(p) {
181
- return p.replace(/\/$/, "").split("?")[0];
182
- }
183
- function pathMatches(current, careersPath) {
184
- const c = normalizePath(current);
185
- const target = normalizePath(careersPath);
186
- return c === target || c.startsWith(target + "/");
187
- }
241
+ var _routeChangeCallbacks = [];
188
242
  var _Prodigio = class _Prodigio {
189
243
  constructor(config) {
190
244
  // ── Jobs ──────────────────────────────────────────────────────────────────
@@ -268,11 +322,6 @@ var _Prodigio = class _Prodigio {
268
322
  static meta() {
269
323
  return { name: "prodigio-badge", content: "true" };
270
324
  }
271
- // ── destroy() ─────────────────────────────────────────────────────────────
272
- static destroy() {
273
- removeBadge();
274
- _initialized = false;
275
- }
276
325
  // ── init() ────────────────────────────────────────────────────────────────
277
326
  static async init(config) {
278
327
  var _a;
@@ -282,7 +331,7 @@ var _Prodigio = class _Prodigio {
282
331
  if (process.env.NODE_ENV !== "production" && !document.querySelector('meta[name="prodigio-badge"]')) {
283
332
  console.warn('[Prodigio] Missing <meta name="prodigio-badge" content="true"> in your page <head>. This is required for compliance detection. See docs.prodigio.io');
284
333
  }
285
- const { key, badgeToken, baseUrl = DEFAULT_BASE_URL, badge: badgeConfig } = config;
334
+ const { key, badgeToken, baseUrl = DEFAULT_BASE_URL, badge: badgeConfig, badgeContainer } = config;
286
335
  const position = (_a = badgeConfig == null ? void 0 : badgeConfig.position) != null ? _a : "bottom-right";
287
336
  const cached = getCachedToken(key);
288
337
  const [initClaims, cachedClaims] = await Promise.all([
@@ -299,42 +348,56 @@ var _Prodigio = class _Prodigio {
299
348
  const domainAllowed = bestToken ? bestToken.allowedDomains.length === 0 || bestToken.allowedDomains.includes(hostname) : true;
300
349
  const isExempt = bestToken !== null && !bestToken.badgeRequired && domainAllowed;
301
350
  let careersPath = null;
302
- function evaluateBadge() {
351
+ function reconcile() {
352
+ cancelPending();
303
353
  if (isExempt) {
304
354
  removeBadge();
305
355
  return;
306
356
  }
307
- if (careersPath) {
308
- if (pathMatches(window.location.pathname, careersPath)) {
309
- injectBadge(position);
310
- } else {
311
- removeBadge();
312
- }
313
- } else {
314
- if (!bestToken && cached) {
357
+ if (!careersPath) {
358
+ if (bestToken && cached) {
315
359
  const cacheAge = Date.now() - cached.cachedAt;
316
360
  if (cacheAge < GRACE_PERIOD_MS) {
317
361
  removeBadge();
318
362
  return;
319
363
  }
320
364
  }
321
- injectBadge(position);
365
+ if (!document.getElementById(BADGE_ELEMENT_ID)) {
366
+ injectBadge(position, badgeContainer);
367
+ }
368
+ return;
369
+ }
370
+ const routeMatches = pathMatches(window.location.pathname, careersPath);
371
+ if (!routeMatches) {
372
+ removeBadge();
373
+ return;
322
374
  }
375
+ queueMicrotask(() => {
376
+ _rafHandle = requestAnimationFrame(() => {
377
+ _rafHandle = null;
378
+ const anchor = findBadgeAnchor(badgeContainer);
379
+ if (anchor) {
380
+ if (!document.getElementById(BADGE_ELEMENT_ID)) injectBadge(position, badgeContainer);
381
+ return;
382
+ }
383
+ waitForAnchor(badgeContainer, () => {
384
+ if (!document.getElementById(BADGE_ELEMENT_ID)) injectBadge(position, badgeContainer);
385
+ });
386
+ });
387
+ });
323
388
  }
324
- evaluateBadge();
389
+ reconcile();
325
390
  installRouteWatcher();
326
- _routeChangeCallbacks.push(evaluateBadge);
391
+ _routeChangeCallbacks.push(reconcile);
327
392
  setTimeout(async () => {
328
393
  var _a2, _b;
329
394
  try {
330
395
  const res = await fetch(`${baseUrl}/api/sdk/config?key=${encodeURIComponent(key)}`);
331
396
  if (!res.ok) return;
332
397
  const data = await res.json();
333
- if (data.careersPath !== void 0) {
334
- careersPath = data.careersPath;
335
- }
398
+ if (data.careersPath !== void 0) careersPath = data.careersPath;
336
399
  if (data.badgeRequired) {
337
- evaluateBadge();
400
+ reconcile();
338
401
  return;
339
402
  }
340
403
  if (data.badgeToken) {
@@ -342,13 +405,19 @@ var _Prodigio = class _Prodigio {
342
405
  if (refreshedClaims && !refreshedClaims.badgeRequired) {
343
406
  cacheToken(key, data.badgeToken, (_a2 = data.badgeExemptUntil) != null ? _a2 : "", (_b = data.configVersion) != null ? _b : 1);
344
407
  removeBadge();
408
+ return;
345
409
  }
346
410
  }
347
- evaluateBadge();
411
+ reconcile();
348
412
  } catch {
349
413
  }
350
414
  }, 0);
351
415
  }
416
+ static destroy() {
417
+ cancelPending();
418
+ removeBadge();
419
+ _initialized = false;
420
+ }
352
421
  };
353
422
  // ── Badge static helpers ──────────────────────────────────────────────────
354
423
  _Prodigio.POWERED_BY = {
package/dist/index.mjs CHANGED
@@ -66,7 +66,7 @@ async function verifyBadgeToken(token) {
66
66
  }
67
67
  }
68
68
  var BADGE_ELEMENT_ID = "prodigio-badge-widget";
69
- function injectBadge(_position) {
69
+ function injectBadge(_position, badgeContainer) {
70
70
  if (typeof document === "undefined") return;
71
71
  if (document.getElementById(BADGE_ELEMENT_ID)) return;
72
72
  const wrapper = document.createElement("div");
@@ -108,7 +108,7 @@ function injectBadge(_position) {
108
108
  el.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
109
109
  });
110
110
  wrapper.appendChild(el);
111
- document.body.appendChild(wrapper);
111
+ findBadgeContainer(badgeContainer).appendChild(wrapper);
112
112
  }
113
113
  function removeBadge() {
114
114
  var _a;
@@ -129,16 +129,78 @@ function getCachedToken(apiKey) {
129
129
  return null;
130
130
  }
131
131
  }
132
+ var _initialized = false;
132
133
  var _routeWatcherInstalled = false;
133
- var _routeChangeCallbacks = [];
134
+ var _rafHandle = null;
135
+ var _anchorObserver = null;
136
+ var _anchorTimeout = null;
137
+ var ANCHOR_WAIT_MS = 3e3;
138
+ function normalizePath(p) {
139
+ return p.replace(/\/$/, "").split("?")[0];
140
+ }
141
+ function pathMatches(current, careersPath) {
142
+ const c = normalizePath(current);
143
+ const target = normalizePath(careersPath);
144
+ return c === target || c.startsWith(target + "/");
145
+ }
146
+ function cancelPending() {
147
+ if (_rafHandle !== null) {
148
+ cancelAnimationFrame(_rafHandle);
149
+ _rafHandle = null;
150
+ }
151
+ if (_anchorObserver !== null) {
152
+ _anchorObserver.disconnect();
153
+ _anchorObserver = null;
154
+ }
155
+ if (_anchorTimeout !== null) {
156
+ clearTimeout(_anchorTimeout);
157
+ _anchorTimeout = null;
158
+ }
159
+ }
160
+ function waitForAnchor(badgeContainer, onFound) {
161
+ _anchorObserver = new MutationObserver(() => {
162
+ if (findBadgeAnchor(badgeContainer)) {
163
+ cancelPending();
164
+ onFound();
165
+ }
166
+ });
167
+ _anchorObserver.observe(document.body, { childList: true, subtree: true });
168
+ _anchorTimeout = setTimeout(() => {
169
+ cancelPending();
170
+ if (process.env.NODE_ENV !== "production") {
171
+ console.warn("[Prodigio] Badge anchor not found within 3s. Add <div data-prodigio-badge-anchor></div> to your page.");
172
+ }
173
+ }, ANCHOR_WAIT_MS);
174
+ }
175
+ function findBadgeAnchor(badgeContainer) {
176
+ if (badgeContainer) {
177
+ const el = document.querySelector(badgeContainer);
178
+ if (el) return el;
179
+ }
180
+ const anchor = document.querySelector("[data-prodigio-badge-anchor]");
181
+ if (anchor) return anchor;
182
+ return null;
183
+ }
184
+ function findBadgeContainer(badgeContainer) {
185
+ const anchor = findBadgeAnchor(badgeContainer);
186
+ if (anchor) return anchor;
187
+ const selectors = ["main", '[role="main"]', "#root", "#__next", "#app"];
188
+ for (const sel of selectors) {
189
+ const el = document.querySelector(sel);
190
+ if (el) return el;
191
+ }
192
+ return document.body;
193
+ }
134
194
  function installRouteWatcher() {
135
195
  if (_routeWatcherInstalled || typeof window === "undefined") return;
136
196
  _routeWatcherInstalled = true;
137
- const dispatch = () => _routeChangeCallbacks.forEach((cb) => cb());
138
197
  const orig = {
139
198
  push: history.pushState.bind(history),
140
199
  replace: history.replaceState.bind(history)
141
200
  };
201
+ const dispatch = () => {
202
+ _routeChangeCallbacks.forEach((cb) => cb());
203
+ };
142
204
  history.pushState = (...args) => {
143
205
  orig.push(...args);
144
206
  dispatch();
@@ -150,15 +212,7 @@ function installRouteWatcher() {
150
212
  window.addEventListener("popstate", dispatch);
151
213
  window.addEventListener("hashchange", dispatch);
152
214
  }
153
- var _initialized = false;
154
- function normalizePath(p) {
155
- return p.replace(/\/$/, "").split("?")[0];
156
- }
157
- function pathMatches(current, careersPath) {
158
- const c = normalizePath(current);
159
- const target = normalizePath(careersPath);
160
- return c === target || c.startsWith(target + "/");
161
- }
215
+ var _routeChangeCallbacks = [];
162
216
  var _Prodigio = class _Prodigio {
163
217
  constructor(config) {
164
218
  // ── Jobs ──────────────────────────────────────────────────────────────────
@@ -242,11 +296,6 @@ var _Prodigio = class _Prodigio {
242
296
  static meta() {
243
297
  return { name: "prodigio-badge", content: "true" };
244
298
  }
245
- // ── destroy() ─────────────────────────────────────────────────────────────
246
- static destroy() {
247
- removeBadge();
248
- _initialized = false;
249
- }
250
299
  // ── init() ────────────────────────────────────────────────────────────────
251
300
  static async init(config) {
252
301
  var _a;
@@ -256,7 +305,7 @@ var _Prodigio = class _Prodigio {
256
305
  if (process.env.NODE_ENV !== "production" && !document.querySelector('meta[name="prodigio-badge"]')) {
257
306
  console.warn('[Prodigio] Missing <meta name="prodigio-badge" content="true"> in your page <head>. This is required for compliance detection. See docs.prodigio.io');
258
307
  }
259
- const { key, badgeToken, baseUrl = DEFAULT_BASE_URL, badge: badgeConfig } = config;
308
+ const { key, badgeToken, baseUrl = DEFAULT_BASE_URL, badge: badgeConfig, badgeContainer } = config;
260
309
  const position = (_a = badgeConfig == null ? void 0 : badgeConfig.position) != null ? _a : "bottom-right";
261
310
  const cached = getCachedToken(key);
262
311
  const [initClaims, cachedClaims] = await Promise.all([
@@ -273,42 +322,56 @@ var _Prodigio = class _Prodigio {
273
322
  const domainAllowed = bestToken ? bestToken.allowedDomains.length === 0 || bestToken.allowedDomains.includes(hostname) : true;
274
323
  const isExempt = bestToken !== null && !bestToken.badgeRequired && domainAllowed;
275
324
  let careersPath = null;
276
- function evaluateBadge() {
325
+ function reconcile() {
326
+ cancelPending();
277
327
  if (isExempt) {
278
328
  removeBadge();
279
329
  return;
280
330
  }
281
- if (careersPath) {
282
- if (pathMatches(window.location.pathname, careersPath)) {
283
- injectBadge(position);
284
- } else {
285
- removeBadge();
286
- }
287
- } else {
288
- if (!bestToken && cached) {
331
+ if (!careersPath) {
332
+ if (bestToken && cached) {
289
333
  const cacheAge = Date.now() - cached.cachedAt;
290
334
  if (cacheAge < GRACE_PERIOD_MS) {
291
335
  removeBadge();
292
336
  return;
293
337
  }
294
338
  }
295
- injectBadge(position);
339
+ if (!document.getElementById(BADGE_ELEMENT_ID)) {
340
+ injectBadge(position, badgeContainer);
341
+ }
342
+ return;
343
+ }
344
+ const routeMatches = pathMatches(window.location.pathname, careersPath);
345
+ if (!routeMatches) {
346
+ removeBadge();
347
+ return;
296
348
  }
349
+ queueMicrotask(() => {
350
+ _rafHandle = requestAnimationFrame(() => {
351
+ _rafHandle = null;
352
+ const anchor = findBadgeAnchor(badgeContainer);
353
+ if (anchor) {
354
+ if (!document.getElementById(BADGE_ELEMENT_ID)) injectBadge(position, badgeContainer);
355
+ return;
356
+ }
357
+ waitForAnchor(badgeContainer, () => {
358
+ if (!document.getElementById(BADGE_ELEMENT_ID)) injectBadge(position, badgeContainer);
359
+ });
360
+ });
361
+ });
297
362
  }
298
- evaluateBadge();
363
+ reconcile();
299
364
  installRouteWatcher();
300
- _routeChangeCallbacks.push(evaluateBadge);
365
+ _routeChangeCallbacks.push(reconcile);
301
366
  setTimeout(async () => {
302
367
  var _a2, _b;
303
368
  try {
304
369
  const res = await fetch(`${baseUrl}/api/sdk/config?key=${encodeURIComponent(key)}`);
305
370
  if (!res.ok) return;
306
371
  const data = await res.json();
307
- if (data.careersPath !== void 0) {
308
- careersPath = data.careersPath;
309
- }
372
+ if (data.careersPath !== void 0) careersPath = data.careersPath;
310
373
  if (data.badgeRequired) {
311
- evaluateBadge();
374
+ reconcile();
312
375
  return;
313
376
  }
314
377
  if (data.badgeToken) {
@@ -316,13 +379,19 @@ var _Prodigio = class _Prodigio {
316
379
  if (refreshedClaims && !refreshedClaims.badgeRequired) {
317
380
  cacheToken(key, data.badgeToken, (_a2 = data.badgeExemptUntil) != null ? _a2 : "", (_b = data.configVersion) != null ? _b : 1);
318
381
  removeBadge();
382
+ return;
319
383
  }
320
384
  }
321
- evaluateBadge();
385
+ reconcile();
322
386
  } catch {
323
387
  }
324
388
  }, 0);
325
389
  }
390
+ static destroy() {
391
+ cancelPending();
392
+ removeBadge();
393
+ _initialized = false;
394
+ }
326
395
  };
327
396
  // ── Badge static helpers ──────────────────────────────────────────────────
328
397
  _Prodigio.POWERED_BY = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prodigio-io/sdk",
3
- "version": "2.1.4",
3
+ "version": "2.1.7",
4
4
  "description": "Official JavaScript SDK for the Prodigio hiring API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",