@prodigio-io/sdk 2.1.7 → 3.0.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/index.d.mts CHANGED
@@ -90,6 +90,7 @@ interface BadgeProps {
90
90
  rel: string;
91
91
  text: string;
92
92
  }
93
+
93
94
  declare class ProdigioError extends Error {
94
95
  readonly status: number;
95
96
  readonly code?: string | undefined;
@@ -113,14 +114,17 @@ declare class Prodigio {
113
114
  readonly applications: {
114
115
  submit: (params: SubmitApplicationParams) => Promise<ApplicationResult>;
115
116
  };
116
- static readonly POWERED_BY: PoweredBy;
117
+ static init(config: ProdigioInitConfig): Promise<void>;
118
+ static destroy(): void;
117
119
  static badge(poweredBy?: PoweredBy): BadgeProps;
118
120
  static meta(): {
119
121
  name: string;
120
122
  content: string;
121
123
  };
122
- static init(config: ProdigioInitConfig): Promise<void>;
123
- static destroy(): void;
124
+ static readonly POWERED_BY: {
125
+ text: string;
126
+ url: string;
127
+ };
124
128
  }
125
129
 
126
130
  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
@@ -90,6 +90,7 @@ interface BadgeProps {
90
90
  rel: string;
91
91
  text: string;
92
92
  }
93
+
93
94
  declare class ProdigioError extends Error {
94
95
  readonly status: number;
95
96
  readonly code?: string | undefined;
@@ -113,14 +114,17 @@ declare class Prodigio {
113
114
  readonly applications: {
114
115
  submit: (params: SubmitApplicationParams) => Promise<ApplicationResult>;
115
116
  };
116
- static readonly POWERED_BY: PoweredBy;
117
+ static init(config: ProdigioInitConfig): Promise<void>;
118
+ static destroy(): void;
117
119
  static badge(poweredBy?: PoweredBy): BadgeProps;
118
120
  static meta(): {
119
121
  name: string;
120
122
  content: string;
121
123
  };
122
- static init(config: ProdigioInitConfig): Promise<void>;
123
- static destroy(): void;
124
+ static readonly POWERED_BY: {
125
+ text: string;
126
+ url: string;
127
+ };
124
128
  }
125
129
 
126
130
  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
@@ -26,17 +26,7 @@ __export(index_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(index_exports);
28
28
  var DEFAULT_BASE_URL = "https://hire.prodigio.io";
29
- var BADGE_CACHE_KEY = "prodigio_badge_token";
30
- var GRACE_PERIOD_MS = 5 * 60 * 1e3;
31
- var PRODIGIO_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
32
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy85BNWaJVQQA/AFsz612
33
- I+3K94s6P7r9zoSUvxDhn2J1INyuc27EIcMOHHPDxChrzJGv6bozGbpexYSIIgQd
34
- ge9Ge9P/J+THQMdUmAbidfp/mR1i0vISaaBvKfFQfIGkuXpzbQB8eEV1s0jACMSd
35
- qRSKeJEKSKPf3OkWVhaWi4KxT9H14W6lJMBrE0ZaGbAog2HmG69f5+5JjvxU6TWR
36
- BojS5CYGvSvAIQGnsWm3Rz/HfZoPg1VRMRkGYCbLIVyV3XjLPo/VqBGuvjZSRTk+
37
- HW72H1ZvBsBnNSEKkOSp5r43Du3x2KUQdYyV7p0iIwMRnMq2enBEEb2qrrj7huln
38
- BQIDAQAB
39
- -----END PUBLIC KEY-----`;
29
+ var CDN_URL = "https://cdn.prodigio.io/sdk/v2/sdk.js";
40
30
  var ProdigioError = class extends Error {
41
31
  constructor(message, status, code) {
42
32
  super(message);
@@ -45,203 +35,30 @@ var ProdigioError = class extends Error {
45
35
  this.name = "ProdigioError";
46
36
  }
47
37
  };
48
- function pemToArrayBuffer(pem) {
49
- const base64 = pem.replace(/-----BEGIN PUBLIC KEY-----/, "").replace(/-----END PUBLIC KEY-----/, "").replace(/\s/g, "");
50
- const binary = atob(base64);
51
- const buf = new Uint8Array(binary.length);
52
- for (let i = 0; i < binary.length; i++) buf[i] = binary.charCodeAt(i);
53
- return buf.buffer;
54
- }
55
- function base64urlToArrayBuffer(b64url) {
56
- const b64 = b64url.replace(/-/g, "+").replace(/_/g, "/").padEnd(
57
- b64url.length + (4 - b64url.length % 4) % 4,
58
- "="
59
- );
60
- const binary = atob(b64);
61
- const buf = new Uint8Array(binary.length);
62
- for (let i = 0; i < binary.length; i++) buf[i] = binary.charCodeAt(i);
63
- return buf.buffer;
64
- }
65
- async function verifyBadgeToken(token) {
66
- try {
67
- if (typeof crypto === "undefined" || !crypto.subtle) return null;
68
- const parts = token.split(".");
69
- if (parts.length !== 3) return null;
70
- const [headerB64, payloadB64, sigB64] = parts;
71
- const signature = base64urlToArrayBuffer(sigB64);
72
- const keyData = pemToArrayBuffer(PRODIGIO_PUBLIC_KEY_PEM);
73
- const publicKey = await crypto.subtle.importKey(
74
- "spki",
75
- keyData,
76
- { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
77
- false,
78
- ["verify"]
79
- );
80
- const valid = await crypto.subtle.verify(
81
- "RSASSA-PKCS1-v1_5",
82
- publicKey,
83
- signature,
84
- new TextEncoder().encode(`${headerB64}.${payloadB64}`)
85
- );
86
- if (!valid) return null;
87
- const claims = JSON.parse(atob(payloadB64.replace(/-/g, "+").replace(/_/g, "/")));
88
- if (claims.exp < Math.floor(Date.now() / 1e3)) return null;
89
- return claims;
90
- } catch {
91
- return null;
92
- }
93
- }
94
- var BADGE_ELEMENT_ID = "prodigio-badge-widget";
95
- function injectBadge(_position, badgeContainer) {
96
- if (typeof document === "undefined") return;
97
- if (document.getElementById(BADGE_ELEMENT_ID)) return;
98
- const wrapper = document.createElement("div");
99
- wrapper.id = BADGE_ELEMENT_ID;
100
- wrapper.style.cssText = `
101
- width: 100%; display: flex; justify-content: flex-end;
102
- padding: 16px 20px; box-sizing: border-box;
103
- `;
104
- const el = document.createElement("a");
105
- el.href = "https://prodigio.io";
106
- el.target = "_blank";
107
- el.rel = "noopener noreferrer";
108
- el.setAttribute("data-prodigio-badge", "true");
109
- el.setAttribute("aria-label", "Hiring powered by Prodigio");
110
- el.style.cssText = `
111
- display: inline-flex; align-items: center; gap: 7px;
112
- padding: 7px 14px; background: #ffffff; border: 1px solid #e5e5e5;
113
- border-radius: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.08);
114
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
115
- font-size: 12px; font-weight: 400; color: #555555;
116
- text-decoration: none; cursor: pointer; transition: box-shadow 0.15s ease;
117
- white-space: nowrap;
118
- `;
119
- el.innerHTML = `
120
- <span style="color:#555555;font-weight:400;">Hiring powered by</span>
121
- <span style="display:inline-flex;align-items:center;gap:5px;">
122
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
123
- <rect width="24" height="24" rx="6" fill="#3730A3"/>
124
- <path d="M7 7h5.5a3.5 3.5 0 0 1 0 7H7V7z" fill="#fff"/>
125
- <path d="M7 14h6a3 3 0 0 1 0 6H7v-6z" fill="#fff" opacity="0.6"/>
126
- </svg>
127
- <span style="color:#111111;font-weight:600;font-size:12px;">Prodigio</span>
128
- </span>
129
- `;
130
- el.addEventListener("mouseenter", () => {
131
- el.style.boxShadow = "0 4px 12px rgba(0,0,0,0.12)";
132
- });
133
- el.addEventListener("mouseleave", () => {
134
- el.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
135
- });
136
- wrapper.appendChild(el);
137
- findBadgeContainer(badgeContainer).appendChild(wrapper);
138
- }
139
- function removeBadge() {
140
- var _a;
141
- if (typeof document === "undefined") return;
142
- (_a = document.getElementById(BADGE_ELEMENT_ID)) == null ? void 0 : _a.remove();
143
- }
144
- function cacheToken(apiKey, token, badgeExemptUntil, configVersion) {
145
- try {
146
- localStorage.setItem(`${BADGE_CACHE_KEY}_${apiKey}`, JSON.stringify({ token, badgeExemptUntil, configVersion, cachedAt: Date.now() }));
147
- } catch {
148
- }
149
- }
150
- function getCachedToken(apiKey) {
151
- try {
152
- const raw = localStorage.getItem(`${BADGE_CACHE_KEY}_${apiKey}`);
153
- return raw ? JSON.parse(raw) : null;
154
- } catch {
155
- return null;
156
- }
157
- }
158
- var _initialized = false;
159
- var _routeWatcherInstalled = false;
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();
38
+ var _runtimePromise = null;
39
+ function ensureRuntime() {
40
+ if (_runtimePromise) return _runtimePromise;
41
+ _runtimePromise = new Promise((resolve, reject) => {
42
+ var _a;
43
+ if (typeof window === "undefined") {
44
+ resolve();
45
+ return;
191
46
  }
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.");
47
+ if ((_a = window.Prodigio) == null ? void 0 : _a.__cdnLoaded) {
48
+ resolve();
49
+ return;
198
50
  }
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
- }
220
- function installRouteWatcher() {
221
- if (_routeWatcherInstalled || typeof window === "undefined") return;
222
- _routeWatcherInstalled = true;
223
- const orig = {
224
- push: history.pushState.bind(history),
225
- replace: history.replaceState.bind(history)
226
- };
227
- const dispatch = () => {
228
- _routeChangeCallbacks.forEach((cb) => cb());
229
- };
230
- history.pushState = (...args) => {
231
- orig.push(...args);
232
- dispatch();
233
- };
234
- history.replaceState = (...args) => {
235
- orig.replace(...args);
236
- dispatch();
237
- };
238
- window.addEventListener("popstate", dispatch);
239
- window.addEventListener("hashchange", dispatch);
51
+ const script = document.createElement("script");
52
+ script.src = CDN_URL;
53
+ script.async = true;
54
+ script.onload = () => resolve();
55
+ script.onerror = () => reject(new Error("[Prodigio] Failed to load SDK runtime from CDN"));
56
+ document.head.appendChild(script);
57
+ });
58
+ return _runtimePromise;
240
59
  }
241
- var _routeChangeCallbacks = [];
242
60
  var _Prodigio = class _Prodigio {
243
61
  constructor(config) {
244
- // ── Jobs ──────────────────────────────────────────────────────────────────
245
62
  this.jobs = {
246
63
  list: (params) => {
247
64
  const p = {};
@@ -251,7 +68,6 @@ var _Prodigio = class _Prodigio {
251
68
  },
252
69
  get: (jobId) => this.get(`/api/jobs/${jobId}`)
253
70
  };
254
- // ── Uploads ───────────────────────────────────────────────────────────────
255
71
  this.upload = {
256
72
  cv: (file, fileName) => {
257
73
  const form = new FormData();
@@ -264,7 +80,6 @@ var _Prodigio = class _Prodigio {
264
80
  return this.postForm("/api/upload/achievement", form);
265
81
  }
266
82
  };
267
- // ── Applications ──────────────────────────────────────────────────────────
268
83
  this.applications = {
269
84
  submit: (params) => this.post("/api/applications", params)
270
85
  };
@@ -315,111 +130,27 @@ var _Prodigio = class _Prodigio {
315
130
  }
316
131
  return res.json();
317
132
  }
133
+ // ── Static methods — delegate to CDN runtime ──────────────────────────────
134
+ static async init(config) {
135
+ await ensureRuntime();
136
+ return window.Prodigio.init(config);
137
+ }
138
+ static destroy() {
139
+ var _a;
140
+ (_a = window.Prodigio) == null ? void 0 : _a.destroy();
141
+ }
318
142
  static badge(poweredBy) {
143
+ var _a;
144
+ if ((_a = window.Prodigio) == null ? void 0 : _a.badge) {
145
+ return window.Prodigio.badge(poweredBy);
146
+ }
319
147
  const pb = poweredBy != null ? poweredBy : _Prodigio.POWERED_BY;
320
148
  return { href: pb.url, "data-prodigio-badge": "true", target: "_blank", rel: "noopener noreferrer", text: pb.text };
321
149
  }
322
150
  static meta() {
323
151
  return { name: "prodigio-badge", content: "true" };
324
152
  }
325
- // ── init() ────────────────────────────────────────────────────────────────
326
- static async init(config) {
327
- var _a;
328
- if (typeof window === "undefined") return;
329
- if (_initialized) return;
330
- _initialized = true;
331
- if (process.env.NODE_ENV !== "production" && !document.querySelector('meta[name="prodigio-badge"]')) {
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');
333
- }
334
- const { key, badgeToken, baseUrl = DEFAULT_BASE_URL, badge: badgeConfig, badgeContainer } = config;
335
- const position = (_a = badgeConfig == null ? void 0 : badgeConfig.position) != null ? _a : "bottom-right";
336
- const cached = getCachedToken(key);
337
- const [initClaims, cachedClaims] = await Promise.all([
338
- badgeToken ? verifyBadgeToken(badgeToken) : Promise.resolve(null),
339
- cached ? verifyBadgeToken(cached.token) : Promise.resolve(null)
340
- ]);
341
- let bestToken = null;
342
- if (initClaims && cachedClaims) {
343
- bestToken = new Date(initClaims.badgeExemptUntil) >= new Date(cachedClaims.badgeExemptUntil) ? initClaims : cachedClaims;
344
- } else {
345
- bestToken = initClaims != null ? initClaims : cachedClaims;
346
- }
347
- const hostname = window.location.hostname;
348
- const domainAllowed = bestToken ? bestToken.allowedDomains.length === 0 || bestToken.allowedDomains.includes(hostname) : true;
349
- const isExempt = bestToken !== null && !bestToken.badgeRequired && domainAllowed;
350
- let careersPath = null;
351
- function reconcile() {
352
- cancelPending();
353
- if (isExempt) {
354
- removeBadge();
355
- return;
356
- }
357
- if (!careersPath) {
358
- if (bestToken && cached) {
359
- const cacheAge = Date.now() - cached.cachedAt;
360
- if (cacheAge < GRACE_PERIOD_MS) {
361
- removeBadge();
362
- return;
363
- }
364
- }
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;
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
- });
388
- }
389
- reconcile();
390
- installRouteWatcher();
391
- _routeChangeCallbacks.push(reconcile);
392
- setTimeout(async () => {
393
- var _a2, _b;
394
- try {
395
- const res = await fetch(`${baseUrl}/api/sdk/config?key=${encodeURIComponent(key)}`);
396
- if (!res.ok) return;
397
- const data = await res.json();
398
- if (data.careersPath !== void 0) careersPath = data.careersPath;
399
- if (data.badgeRequired) {
400
- reconcile();
401
- return;
402
- }
403
- if (data.badgeToken) {
404
- const refreshedClaims = await verifyBadgeToken(data.badgeToken);
405
- if (refreshedClaims && !refreshedClaims.badgeRequired) {
406
- cacheToken(key, data.badgeToken, (_a2 = data.badgeExemptUntil) != null ? _a2 : "", (_b = data.configVersion) != null ? _b : 1);
407
- removeBadge();
408
- return;
409
- }
410
- }
411
- reconcile();
412
- } catch {
413
- }
414
- }, 0);
415
- }
416
- static destroy() {
417
- cancelPending();
418
- removeBadge();
419
- _initialized = false;
420
- }
421
153
  };
422
- // ── Badge static helpers ──────────────────────────────────────────────────
423
154
  _Prodigio.POWERED_BY = {
424
155
  text: "Hiring powered by Prodigio",
425
156
  url: "https://prodigio.io"
package/dist/index.mjs CHANGED
@@ -1,16 +1,6 @@
1
1
  // src/index.ts
2
2
  var DEFAULT_BASE_URL = "https://hire.prodigio.io";
3
- var BADGE_CACHE_KEY = "prodigio_badge_token";
4
- var GRACE_PERIOD_MS = 5 * 60 * 1e3;
5
- var PRODIGIO_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
6
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy85BNWaJVQQA/AFsz612
7
- I+3K94s6P7r9zoSUvxDhn2J1INyuc27EIcMOHHPDxChrzJGv6bozGbpexYSIIgQd
8
- ge9Ge9P/J+THQMdUmAbidfp/mR1i0vISaaBvKfFQfIGkuXpzbQB8eEV1s0jACMSd
9
- qRSKeJEKSKPf3OkWVhaWi4KxT9H14W6lJMBrE0ZaGbAog2HmG69f5+5JjvxU6TWR
10
- BojS5CYGvSvAIQGnsWm3Rz/HfZoPg1VRMRkGYCbLIVyV3XjLPo/VqBGuvjZSRTk+
11
- HW72H1ZvBsBnNSEKkOSp5r43Du3x2KUQdYyV7p0iIwMRnMq2enBEEb2qrrj7huln
12
- BQIDAQAB
13
- -----END PUBLIC KEY-----`;
3
+ var CDN_URL = "https://cdn.prodigio.io/sdk/v2/sdk.js";
14
4
  var ProdigioError = class extends Error {
15
5
  constructor(message, status, code) {
16
6
  super(message);
@@ -19,203 +9,30 @@ var ProdigioError = class extends Error {
19
9
  this.name = "ProdigioError";
20
10
  }
21
11
  };
22
- function pemToArrayBuffer(pem) {
23
- const base64 = pem.replace(/-----BEGIN PUBLIC KEY-----/, "").replace(/-----END PUBLIC KEY-----/, "").replace(/\s/g, "");
24
- const binary = atob(base64);
25
- const buf = new Uint8Array(binary.length);
26
- for (let i = 0; i < binary.length; i++) buf[i] = binary.charCodeAt(i);
27
- return buf.buffer;
28
- }
29
- function base64urlToArrayBuffer(b64url) {
30
- const b64 = b64url.replace(/-/g, "+").replace(/_/g, "/").padEnd(
31
- b64url.length + (4 - b64url.length % 4) % 4,
32
- "="
33
- );
34
- const binary = atob(b64);
35
- const buf = new Uint8Array(binary.length);
36
- for (let i = 0; i < binary.length; i++) buf[i] = binary.charCodeAt(i);
37
- return buf.buffer;
38
- }
39
- async function verifyBadgeToken(token) {
40
- try {
41
- if (typeof crypto === "undefined" || !crypto.subtle) return null;
42
- const parts = token.split(".");
43
- if (parts.length !== 3) return null;
44
- const [headerB64, payloadB64, sigB64] = parts;
45
- const signature = base64urlToArrayBuffer(sigB64);
46
- const keyData = pemToArrayBuffer(PRODIGIO_PUBLIC_KEY_PEM);
47
- const publicKey = await crypto.subtle.importKey(
48
- "spki",
49
- keyData,
50
- { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
51
- false,
52
- ["verify"]
53
- );
54
- const valid = await crypto.subtle.verify(
55
- "RSASSA-PKCS1-v1_5",
56
- publicKey,
57
- signature,
58
- new TextEncoder().encode(`${headerB64}.${payloadB64}`)
59
- );
60
- if (!valid) return null;
61
- const claims = JSON.parse(atob(payloadB64.replace(/-/g, "+").replace(/_/g, "/")));
62
- if (claims.exp < Math.floor(Date.now() / 1e3)) return null;
63
- return claims;
64
- } catch {
65
- return null;
66
- }
67
- }
68
- var BADGE_ELEMENT_ID = "prodigio-badge-widget";
69
- function injectBadge(_position, badgeContainer) {
70
- if (typeof document === "undefined") return;
71
- if (document.getElementById(BADGE_ELEMENT_ID)) return;
72
- const wrapper = document.createElement("div");
73
- wrapper.id = BADGE_ELEMENT_ID;
74
- wrapper.style.cssText = `
75
- width: 100%; display: flex; justify-content: flex-end;
76
- padding: 16px 20px; box-sizing: border-box;
77
- `;
78
- const el = document.createElement("a");
79
- el.href = "https://prodigio.io";
80
- el.target = "_blank";
81
- el.rel = "noopener noreferrer";
82
- el.setAttribute("data-prodigio-badge", "true");
83
- el.setAttribute("aria-label", "Hiring powered by Prodigio");
84
- el.style.cssText = `
85
- display: inline-flex; align-items: center; gap: 7px;
86
- padding: 7px 14px; background: #ffffff; border: 1px solid #e5e5e5;
87
- border-radius: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.08);
88
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
89
- font-size: 12px; font-weight: 400; color: #555555;
90
- text-decoration: none; cursor: pointer; transition: box-shadow 0.15s ease;
91
- white-space: nowrap;
92
- `;
93
- el.innerHTML = `
94
- <span style="color:#555555;font-weight:400;">Hiring powered by</span>
95
- <span style="display:inline-flex;align-items:center;gap:5px;">
96
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
97
- <rect width="24" height="24" rx="6" fill="#3730A3"/>
98
- <path d="M7 7h5.5a3.5 3.5 0 0 1 0 7H7V7z" fill="#fff"/>
99
- <path d="M7 14h6a3 3 0 0 1 0 6H7v-6z" fill="#fff" opacity="0.6"/>
100
- </svg>
101
- <span style="color:#111111;font-weight:600;font-size:12px;">Prodigio</span>
102
- </span>
103
- `;
104
- el.addEventListener("mouseenter", () => {
105
- el.style.boxShadow = "0 4px 12px rgba(0,0,0,0.12)";
106
- });
107
- el.addEventListener("mouseleave", () => {
108
- el.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
109
- });
110
- wrapper.appendChild(el);
111
- findBadgeContainer(badgeContainer).appendChild(wrapper);
112
- }
113
- function removeBadge() {
114
- var _a;
115
- if (typeof document === "undefined") return;
116
- (_a = document.getElementById(BADGE_ELEMENT_ID)) == null ? void 0 : _a.remove();
117
- }
118
- function cacheToken(apiKey, token, badgeExemptUntil, configVersion) {
119
- try {
120
- localStorage.setItem(`${BADGE_CACHE_KEY}_${apiKey}`, JSON.stringify({ token, badgeExemptUntil, configVersion, cachedAt: Date.now() }));
121
- } catch {
122
- }
123
- }
124
- function getCachedToken(apiKey) {
125
- try {
126
- const raw = localStorage.getItem(`${BADGE_CACHE_KEY}_${apiKey}`);
127
- return raw ? JSON.parse(raw) : null;
128
- } catch {
129
- return null;
130
- }
131
- }
132
- var _initialized = false;
133
- var _routeWatcherInstalled = false;
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();
12
+ var _runtimePromise = null;
13
+ function ensureRuntime() {
14
+ if (_runtimePromise) return _runtimePromise;
15
+ _runtimePromise = new Promise((resolve, reject) => {
16
+ var _a;
17
+ if (typeof window === "undefined") {
18
+ resolve();
19
+ return;
165
20
  }
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.");
21
+ if ((_a = window.Prodigio) == null ? void 0 : _a.__cdnLoaded) {
22
+ resolve();
23
+ return;
172
24
  }
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
- }
194
- function installRouteWatcher() {
195
- if (_routeWatcherInstalled || typeof window === "undefined") return;
196
- _routeWatcherInstalled = true;
197
- const orig = {
198
- push: history.pushState.bind(history),
199
- replace: history.replaceState.bind(history)
200
- };
201
- const dispatch = () => {
202
- _routeChangeCallbacks.forEach((cb) => cb());
203
- };
204
- history.pushState = (...args) => {
205
- orig.push(...args);
206
- dispatch();
207
- };
208
- history.replaceState = (...args) => {
209
- orig.replace(...args);
210
- dispatch();
211
- };
212
- window.addEventListener("popstate", dispatch);
213
- window.addEventListener("hashchange", dispatch);
25
+ const script = document.createElement("script");
26
+ script.src = CDN_URL;
27
+ script.async = true;
28
+ script.onload = () => resolve();
29
+ script.onerror = () => reject(new Error("[Prodigio] Failed to load SDK runtime from CDN"));
30
+ document.head.appendChild(script);
31
+ });
32
+ return _runtimePromise;
214
33
  }
215
- var _routeChangeCallbacks = [];
216
34
  var _Prodigio = class _Prodigio {
217
35
  constructor(config) {
218
- // ── Jobs ──────────────────────────────────────────────────────────────────
219
36
  this.jobs = {
220
37
  list: (params) => {
221
38
  const p = {};
@@ -225,7 +42,6 @@ var _Prodigio = class _Prodigio {
225
42
  },
226
43
  get: (jobId) => this.get(`/api/jobs/${jobId}`)
227
44
  };
228
- // ── Uploads ───────────────────────────────────────────────────────────────
229
45
  this.upload = {
230
46
  cv: (file, fileName) => {
231
47
  const form = new FormData();
@@ -238,7 +54,6 @@ var _Prodigio = class _Prodigio {
238
54
  return this.postForm("/api/upload/achievement", form);
239
55
  }
240
56
  };
241
- // ── Applications ──────────────────────────────────────────────────────────
242
57
  this.applications = {
243
58
  submit: (params) => this.post("/api/applications", params)
244
59
  };
@@ -289,111 +104,27 @@ var _Prodigio = class _Prodigio {
289
104
  }
290
105
  return res.json();
291
106
  }
107
+ // ── Static methods — delegate to CDN runtime ──────────────────────────────
108
+ static async init(config) {
109
+ await ensureRuntime();
110
+ return window.Prodigio.init(config);
111
+ }
112
+ static destroy() {
113
+ var _a;
114
+ (_a = window.Prodigio) == null ? void 0 : _a.destroy();
115
+ }
292
116
  static badge(poweredBy) {
117
+ var _a;
118
+ if ((_a = window.Prodigio) == null ? void 0 : _a.badge) {
119
+ return window.Prodigio.badge(poweredBy);
120
+ }
293
121
  const pb = poweredBy != null ? poweredBy : _Prodigio.POWERED_BY;
294
122
  return { href: pb.url, "data-prodigio-badge": "true", target: "_blank", rel: "noopener noreferrer", text: pb.text };
295
123
  }
296
124
  static meta() {
297
125
  return { name: "prodigio-badge", content: "true" };
298
126
  }
299
- // ── init() ────────────────────────────────────────────────────────────────
300
- static async init(config) {
301
- var _a;
302
- if (typeof window === "undefined") return;
303
- if (_initialized) return;
304
- _initialized = true;
305
- if (process.env.NODE_ENV !== "production" && !document.querySelector('meta[name="prodigio-badge"]')) {
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');
307
- }
308
- const { key, badgeToken, baseUrl = DEFAULT_BASE_URL, badge: badgeConfig, badgeContainer } = config;
309
- const position = (_a = badgeConfig == null ? void 0 : badgeConfig.position) != null ? _a : "bottom-right";
310
- const cached = getCachedToken(key);
311
- const [initClaims, cachedClaims] = await Promise.all([
312
- badgeToken ? verifyBadgeToken(badgeToken) : Promise.resolve(null),
313
- cached ? verifyBadgeToken(cached.token) : Promise.resolve(null)
314
- ]);
315
- let bestToken = null;
316
- if (initClaims && cachedClaims) {
317
- bestToken = new Date(initClaims.badgeExemptUntil) >= new Date(cachedClaims.badgeExemptUntil) ? initClaims : cachedClaims;
318
- } else {
319
- bestToken = initClaims != null ? initClaims : cachedClaims;
320
- }
321
- const hostname = window.location.hostname;
322
- const domainAllowed = bestToken ? bestToken.allowedDomains.length === 0 || bestToken.allowedDomains.includes(hostname) : true;
323
- const isExempt = bestToken !== null && !bestToken.badgeRequired && domainAllowed;
324
- let careersPath = null;
325
- function reconcile() {
326
- cancelPending();
327
- if (isExempt) {
328
- removeBadge();
329
- return;
330
- }
331
- if (!careersPath) {
332
- if (bestToken && cached) {
333
- const cacheAge = Date.now() - cached.cachedAt;
334
- if (cacheAge < GRACE_PERIOD_MS) {
335
- removeBadge();
336
- return;
337
- }
338
- }
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;
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
- });
362
- }
363
- reconcile();
364
- installRouteWatcher();
365
- _routeChangeCallbacks.push(reconcile);
366
- setTimeout(async () => {
367
- var _a2, _b;
368
- try {
369
- const res = await fetch(`${baseUrl}/api/sdk/config?key=${encodeURIComponent(key)}`);
370
- if (!res.ok) return;
371
- const data = await res.json();
372
- if (data.careersPath !== void 0) careersPath = data.careersPath;
373
- if (data.badgeRequired) {
374
- reconcile();
375
- return;
376
- }
377
- if (data.badgeToken) {
378
- const refreshedClaims = await verifyBadgeToken(data.badgeToken);
379
- if (refreshedClaims && !refreshedClaims.badgeRequired) {
380
- cacheToken(key, data.badgeToken, (_a2 = data.badgeExemptUntil) != null ? _a2 : "", (_b = data.configVersion) != null ? _b : 1);
381
- removeBadge();
382
- return;
383
- }
384
- }
385
- reconcile();
386
- } catch {
387
- }
388
- }, 0);
389
- }
390
- static destroy() {
391
- cancelPending();
392
- removeBadge();
393
- _initialized = false;
394
- }
395
127
  };
396
- // ── Badge static helpers ──────────────────────────────────────────────────
397
128
  _Prodigio.POWERED_BY = {
398
129
  text: "Hiring powered by Prodigio",
399
130
  url: "https://prodigio.io"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prodigio-io/sdk",
3
- "version": "2.1.7",
3
+ "version": "3.0.0",
4
4
  "description": "Official JavaScript SDK for the Prodigio hiring API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -17,7 +17,9 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "build": "tsup src/index.ts --format esm,cjs --dts --clean",
20
- "dev": "tsup src/index.ts --format esm,cjs --dts --watch"
20
+ "build:cdn": "tsup src/cdn.ts --format iife --no-dts --minify --out-dir dist-cdn --clean",
21
+ "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
22
+ "publish:cdn": "node scripts/publish-cdn.js"
21
23
  },
22
24
  "keywords": [
23
25
  "prodigio",