@prodigio-io/sdk 1.0.5 → 2.0.1

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
@@ -2,6 +2,17 @@ interface ProdigioConfig {
2
2
  apiKey: string;
3
3
  baseUrl?: string;
4
4
  }
5
+ interface ProdigioInitConfig {
6
+ key: string;
7
+ badgeToken?: string;
8
+ baseUrl?: string;
9
+ badge?: {
10
+ position?: 'bottom-right' | 'bottom-left';
11
+ /** Only inject the badge when the current page path starts with one of these strings.
12
+ * e.g. ['/careers'] — badge appears on /careers and /careers/job-id but not on / or /about */
13
+ paths?: string[];
14
+ };
15
+ }
5
16
  interface SalaryRange {
6
17
  min: number;
7
18
  max: number;
@@ -108,6 +119,7 @@ declare class Prodigio {
108
119
  name: string;
109
120
  content: string;
110
121
  };
122
+ static init(config: ProdigioInitConfig): Promise<void>;
111
123
  }
112
124
 
113
- export { type Achievement, type Applicant, type ApplicationResult, type BadgeProps, type CVInfo, type Job, type ListJobsParams, type PoweredBy, Prodigio, type ProdigioConfig, ProdigioError, type SalaryRange, type SubmitApplicationParams, type UploadResult, Prodigio as default };
125
+ 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
@@ -2,6 +2,17 @@ interface ProdigioConfig {
2
2
  apiKey: string;
3
3
  baseUrl?: string;
4
4
  }
5
+ interface ProdigioInitConfig {
6
+ key: string;
7
+ badgeToken?: string;
8
+ baseUrl?: string;
9
+ badge?: {
10
+ position?: 'bottom-right' | 'bottom-left';
11
+ /** Only inject the badge when the current page path starts with one of these strings.
12
+ * e.g. ['/careers'] — badge appears on /careers and /careers/job-id but not on / or /about */
13
+ paths?: string[];
14
+ };
15
+ }
5
16
  interface SalaryRange {
6
17
  min: number;
7
18
  max: number;
@@ -108,6 +119,7 @@ declare class Prodigio {
108
119
  name: string;
109
120
  content: string;
110
121
  };
122
+ static init(config: ProdigioInitConfig): Promise<void>;
111
123
  }
112
124
 
113
- export { type Achievement, type Applicant, type ApplicationResult, type BadgeProps, type CVInfo, type Job, type ListJobsParams, type PoweredBy, Prodigio, type ProdigioConfig, ProdigioError, type SalaryRange, type SubmitApplicationParams, type UploadResult, Prodigio as default };
125
+ 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,6 +26,17 @@ __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
40
  var ProdigioError = class extends Error {
30
41
  constructor(message, status, code) {
31
42
  super(message);
@@ -34,6 +45,124 @@ var ProdigioError = class extends Error {
34
45
  this.name = "ProdigioError";
35
46
  }
36
47
  };
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 signingInput = `${headerB64}.${payloadB64}`;
72
+ const signature = base64urlToArrayBuffer(sigB64);
73
+ const keyData = pemToArrayBuffer(PRODIGIO_PUBLIC_KEY_PEM);
74
+ const publicKey = await crypto.subtle.importKey(
75
+ "spki",
76
+ keyData,
77
+ { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
78
+ false,
79
+ ["verify"]
80
+ );
81
+ const encoder = new TextEncoder();
82
+ const valid = await crypto.subtle.verify(
83
+ "RSASSA-PKCS1-v1_5",
84
+ publicKey,
85
+ signature,
86
+ encoder.encode(signingInput)
87
+ );
88
+ if (!valid) return null;
89
+ const claims = JSON.parse(atob(payloadB64.replace(/-/g, "+").replace(/_/g, "/")));
90
+ if (claims.exp < Math.floor(Date.now() / 1e3)) return null;
91
+ return claims;
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+ var BADGE_ELEMENT_ID = "prodigio-badge-widget";
97
+ function injectBadge(position = "bottom-right") {
98
+ if (typeof document === "undefined") return;
99
+ if (document.getElementById(BADGE_ELEMENT_ID)) return;
100
+ const side = position === "bottom-left" ? "left: 20px;" : "right: 20px;";
101
+ const el = document.createElement("a");
102
+ el.id = BADGE_ELEMENT_ID;
103
+ el.href = "https://prodigio.io";
104
+ el.target = "_blank";
105
+ el.rel = "noopener noreferrer";
106
+ el.setAttribute("data-prodigio-badge", "true");
107
+ el.setAttribute("aria-label", "Hiring powered by Prodigio");
108
+ el.style.cssText = `
109
+ position: fixed;
110
+ bottom: 20px;
111
+ ${side}
112
+ z-index: 2147483647;
113
+ display: inline-flex;
114
+ align-items: center;
115
+ gap: 6px;
116
+ padding: 6px 10px;
117
+ background: #ffffff;
118
+ border: 1px solid #e5e5e5;
119
+ border-radius: 20px;
120
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
121
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
122
+ font-size: 11px;
123
+ font-weight: 500;
124
+ color: #666666;
125
+ text-decoration: none;
126
+ cursor: pointer;
127
+ transition: box-shadow 0.15s ease;
128
+ `;
129
+ el.innerHTML = `
130
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
131
+ <rect width="24" height="24" rx="6" fill="#3730A3"/>
132
+ <path d="M7 7h5.5a3.5 3.5 0 0 1 0 7H7V7z" fill="#fff"/>
133
+ <path d="M7 14h6a3 3 0 0 1 0 6H7v-6z" fill="#fff" opacity="0.6"/>
134
+ </svg>
135
+ <span>Hiring powered by Prodigio</span>
136
+ `;
137
+ el.addEventListener("mouseenter", () => {
138
+ el.style.boxShadow = "0 4px 12px rgba(0,0,0,0.12)";
139
+ });
140
+ el.addEventListener("mouseleave", () => {
141
+ el.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
142
+ });
143
+ document.body.appendChild(el);
144
+ }
145
+ function removeBadge() {
146
+ var _a;
147
+ if (typeof document === "undefined") return;
148
+ (_a = document.getElementById(BADGE_ELEMENT_ID)) == null ? void 0 : _a.remove();
149
+ }
150
+ function cacheToken(apiKey, token, badgeExemptUntil, configVersion) {
151
+ try {
152
+ const entry = { token, badgeExemptUntil, configVersion, cachedAt: Date.now() };
153
+ localStorage.setItem(`${BADGE_CACHE_KEY}_${apiKey}`, JSON.stringify(entry));
154
+ } catch {
155
+ }
156
+ }
157
+ function getCachedToken(apiKey) {
158
+ try {
159
+ const raw = localStorage.getItem(`${BADGE_CACHE_KEY}_${apiKey}`);
160
+ if (!raw) return null;
161
+ return JSON.parse(raw);
162
+ } catch {
163
+ return null;
164
+ }
165
+ }
37
166
  var _Prodigio = class _Prodigio {
38
167
  constructor(config) {
39
168
  // ── Jobs ──────────────────────────────────────────────────────────────────
@@ -44,9 +173,7 @@ var _Prodigio = class _Prodigio {
44
173
  if (params == null ? void 0 : params.department) p.department = params.department;
45
174
  return this.get("/api/jobs", p);
46
175
  },
47
- get: (jobId) => {
48
- return this.get(`/api/jobs/${jobId}`);
49
- }
176
+ get: (jobId) => this.get(`/api/jobs/${jobId}`)
50
177
  };
51
178
  // ── Uploads ───────────────────────────────────────────────────────────────
52
179
  this.upload = {
@@ -63,9 +190,7 @@ var _Prodigio = class _Prodigio {
63
190
  };
64
191
  // ── Applications ──────────────────────────────────────────────────────────
65
192
  this.applications = {
66
- submit: (params) => {
67
- return this.post("/api/applications", params);
68
- }
193
+ submit: (params) => this.post("/api/applications", params)
69
194
  };
70
195
  var _a;
71
196
  if (typeof config === "string") {
@@ -96,10 +221,7 @@ var _Prodigio = class _Prodigio {
96
221
  var _a;
97
222
  const res = await fetch(`${this.baseUrl}${path}`, {
98
223
  method: "POST",
99
- headers: {
100
- "Content-Type": "application/json",
101
- "x-api-key": this.apiKey
102
- },
224
+ headers: { "Content-Type": "application/json", "x-api-key": this.apiKey },
103
225
  body: JSON.stringify(body)
104
226
  });
105
227
  if (!res.ok) {
@@ -112,10 +234,7 @@ var _Prodigio = class _Prodigio {
112
234
  var _a;
113
235
  const url = new URL(`${this.baseUrl}${path}`);
114
236
  url.searchParams.set("key", this.apiKey);
115
- const res = await fetch(url.toString(), {
116
- method: "POST",
117
- body: formData
118
- });
237
+ const res = await fetch(url.toString(), { method: "POST", body: formData });
119
238
  if (!res.ok) {
120
239
  const body = await res.json().catch(() => ({}));
121
240
  throw new ProdigioError((_a = body.error) != null ? _a : `Upload failed: ${res.status}`, res.status);
@@ -132,16 +251,82 @@ var _Prodigio = class _Prodigio {
132
251
  text: pb.text
133
252
  };
134
253
  }
135
- // Returns the <meta> tag props for server-rendered compliance verification.
136
- // Add to your page <head> — this is the most reliable detection method.
137
- // Next.js: export const metadata = { other: { 'prodigio-badge': 'true' } }
138
254
  static meta() {
139
255
  return { name: "prodigio-badge", content: "true" };
140
256
  }
257
+ // ── init() — v2 badge auto-injection ─────────────────────────────────────
258
+ static async init(config) {
259
+ var _a;
260
+ if (typeof window === "undefined") return;
261
+ const { key, badgeToken, baseUrl = DEFAULT_BASE_URL, badge: badgeConfig } = config;
262
+ const position = (_a = badgeConfig == null ? void 0 : badgeConfig.position) != null ? _a : "bottom-right";
263
+ const paths = badgeConfig == null ? void 0 : badgeConfig.paths;
264
+ if (paths && paths.length > 0) {
265
+ const currentPath = window.location.pathname;
266
+ if (!paths.some((p) => currentPath === p || currentPath.startsWith(p + "/"))) {
267
+ return;
268
+ }
269
+ }
270
+ const hostname = window.location.hostname;
271
+ const cached = getCachedToken(key);
272
+ let bestToken = null;
273
+ let bestTokenRaw = null;
274
+ const [initClaims, cachedClaims] = await Promise.all([
275
+ badgeToken ? verifyBadgeToken(badgeToken) : Promise.resolve(null),
276
+ cached ? verifyBadgeToken(cached.token) : Promise.resolve(null)
277
+ ]);
278
+ if (initClaims && cachedClaims) {
279
+ if (new Date(initClaims.badgeExemptUntil) >= new Date(cachedClaims.badgeExemptUntil)) {
280
+ bestToken = initClaims;
281
+ bestTokenRaw = badgeToken;
282
+ } else {
283
+ bestToken = cachedClaims;
284
+ bestTokenRaw = cached.token;
285
+ }
286
+ } else if (initClaims) {
287
+ bestToken = initClaims;
288
+ bestTokenRaw = badgeToken;
289
+ } else if (cachedClaims) {
290
+ bestToken = cachedClaims;
291
+ bestTokenRaw = cached.token;
292
+ }
293
+ const domainAllowed = bestToken ? bestToken.allowedDomains.length === 0 || bestToken.allowedDomains.includes(hostname) : true;
294
+ const isExempt = bestToken !== null && !bestToken.badgeRequired && domainAllowed;
295
+ if (isExempt) {
296
+ removeBadge();
297
+ } else if (!bestToken && cached) {
298
+ const cacheAge = Date.now() - cached.cachedAt;
299
+ if (cacheAge < GRACE_PERIOD_MS) {
300
+ removeBadge();
301
+ } else {
302
+ injectBadge(position);
303
+ }
304
+ } else {
305
+ injectBadge(position);
306
+ }
307
+ setTimeout(async () => {
308
+ var _a2, _b;
309
+ try {
310
+ const res = await fetch(`${baseUrl}/api/sdk/config?key=${encodeURIComponent(key)}`);
311
+ if (!res.ok) return;
312
+ const data = await res.json();
313
+ if (data.badgeRequired) {
314
+ injectBadge(position);
315
+ return;
316
+ }
317
+ if (data.badgeToken) {
318
+ const refreshedClaims = await verifyBadgeToken(data.badgeToken);
319
+ if (refreshedClaims && !refreshedClaims.badgeRequired) {
320
+ cacheToken(key, data.badgeToken, (_a2 = data.badgeExemptUntil) != null ? _a2 : "", (_b = data.configVersion) != null ? _b : 1);
321
+ removeBadge();
322
+ }
323
+ }
324
+ } catch {
325
+ }
326
+ }, 0);
327
+ }
141
328
  };
142
- // ── Badge ─────────────────────────────────────────────────────────────────
143
- // Returns props to spread onto your <a> element.
144
- // poweredBy is optional — defaults to Prodigio's built-in attribution.
329
+ // ── Badge (static helpers) ────────────────────────────────────────────────
145
330
  _Prodigio.POWERED_BY = {
146
331
  text: "Hiring powered by Prodigio",
147
332
  url: "https://prodigio.io"
package/dist/index.mjs CHANGED
@@ -1,5 +1,16 @@
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
14
  var ProdigioError = class extends Error {
4
15
  constructor(message, status, code) {
5
16
  super(message);
@@ -8,6 +19,124 @@ var ProdigioError = class extends Error {
8
19
  this.name = "ProdigioError";
9
20
  }
10
21
  };
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 signingInput = `${headerB64}.${payloadB64}`;
46
+ const signature = base64urlToArrayBuffer(sigB64);
47
+ const keyData = pemToArrayBuffer(PRODIGIO_PUBLIC_KEY_PEM);
48
+ const publicKey = await crypto.subtle.importKey(
49
+ "spki",
50
+ keyData,
51
+ { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
52
+ false,
53
+ ["verify"]
54
+ );
55
+ const encoder = new TextEncoder();
56
+ const valid = await crypto.subtle.verify(
57
+ "RSASSA-PKCS1-v1_5",
58
+ publicKey,
59
+ signature,
60
+ encoder.encode(signingInput)
61
+ );
62
+ if (!valid) return null;
63
+ const claims = JSON.parse(atob(payloadB64.replace(/-/g, "+").replace(/_/g, "/")));
64
+ if (claims.exp < Math.floor(Date.now() / 1e3)) return null;
65
+ return claims;
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+ var BADGE_ELEMENT_ID = "prodigio-badge-widget";
71
+ function injectBadge(position = "bottom-right") {
72
+ if (typeof document === "undefined") return;
73
+ if (document.getElementById(BADGE_ELEMENT_ID)) return;
74
+ const side = position === "bottom-left" ? "left: 20px;" : "right: 20px;";
75
+ const el = document.createElement("a");
76
+ el.id = BADGE_ELEMENT_ID;
77
+ el.href = "https://prodigio.io";
78
+ el.target = "_blank";
79
+ el.rel = "noopener noreferrer";
80
+ el.setAttribute("data-prodigio-badge", "true");
81
+ el.setAttribute("aria-label", "Hiring powered by Prodigio");
82
+ el.style.cssText = `
83
+ position: fixed;
84
+ bottom: 20px;
85
+ ${side}
86
+ z-index: 2147483647;
87
+ display: inline-flex;
88
+ align-items: center;
89
+ gap: 6px;
90
+ padding: 6px 10px;
91
+ background: #ffffff;
92
+ border: 1px solid #e5e5e5;
93
+ border-radius: 20px;
94
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
95
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
96
+ font-size: 11px;
97
+ font-weight: 500;
98
+ color: #666666;
99
+ text-decoration: none;
100
+ cursor: pointer;
101
+ transition: box-shadow 0.15s ease;
102
+ `;
103
+ el.innerHTML = `
104
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
105
+ <rect width="24" height="24" rx="6" fill="#3730A3"/>
106
+ <path d="M7 7h5.5a3.5 3.5 0 0 1 0 7H7V7z" fill="#fff"/>
107
+ <path d="M7 14h6a3 3 0 0 1 0 6H7v-6z" fill="#fff" opacity="0.6"/>
108
+ </svg>
109
+ <span>Hiring powered by Prodigio</span>
110
+ `;
111
+ el.addEventListener("mouseenter", () => {
112
+ el.style.boxShadow = "0 4px 12px rgba(0,0,0,0.12)";
113
+ });
114
+ el.addEventListener("mouseleave", () => {
115
+ el.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
116
+ });
117
+ document.body.appendChild(el);
118
+ }
119
+ function removeBadge() {
120
+ var _a;
121
+ if (typeof document === "undefined") return;
122
+ (_a = document.getElementById(BADGE_ELEMENT_ID)) == null ? void 0 : _a.remove();
123
+ }
124
+ function cacheToken(apiKey, token, badgeExemptUntil, configVersion) {
125
+ try {
126
+ const entry = { token, badgeExemptUntil, configVersion, cachedAt: Date.now() };
127
+ localStorage.setItem(`${BADGE_CACHE_KEY}_${apiKey}`, JSON.stringify(entry));
128
+ } catch {
129
+ }
130
+ }
131
+ function getCachedToken(apiKey) {
132
+ try {
133
+ const raw = localStorage.getItem(`${BADGE_CACHE_KEY}_${apiKey}`);
134
+ if (!raw) return null;
135
+ return JSON.parse(raw);
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
11
140
  var _Prodigio = class _Prodigio {
12
141
  constructor(config) {
13
142
  // ── Jobs ──────────────────────────────────────────────────────────────────
@@ -18,9 +147,7 @@ var _Prodigio = class _Prodigio {
18
147
  if (params == null ? void 0 : params.department) p.department = params.department;
19
148
  return this.get("/api/jobs", p);
20
149
  },
21
- get: (jobId) => {
22
- return this.get(`/api/jobs/${jobId}`);
23
- }
150
+ get: (jobId) => this.get(`/api/jobs/${jobId}`)
24
151
  };
25
152
  // ── Uploads ───────────────────────────────────────────────────────────────
26
153
  this.upload = {
@@ -37,9 +164,7 @@ var _Prodigio = class _Prodigio {
37
164
  };
38
165
  // ── Applications ──────────────────────────────────────────────────────────
39
166
  this.applications = {
40
- submit: (params) => {
41
- return this.post("/api/applications", params);
42
- }
167
+ submit: (params) => this.post("/api/applications", params)
43
168
  };
44
169
  var _a;
45
170
  if (typeof config === "string") {
@@ -70,10 +195,7 @@ var _Prodigio = class _Prodigio {
70
195
  var _a;
71
196
  const res = await fetch(`${this.baseUrl}${path}`, {
72
197
  method: "POST",
73
- headers: {
74
- "Content-Type": "application/json",
75
- "x-api-key": this.apiKey
76
- },
198
+ headers: { "Content-Type": "application/json", "x-api-key": this.apiKey },
77
199
  body: JSON.stringify(body)
78
200
  });
79
201
  if (!res.ok) {
@@ -86,10 +208,7 @@ var _Prodigio = class _Prodigio {
86
208
  var _a;
87
209
  const url = new URL(`${this.baseUrl}${path}`);
88
210
  url.searchParams.set("key", this.apiKey);
89
- const res = await fetch(url.toString(), {
90
- method: "POST",
91
- body: formData
92
- });
211
+ const res = await fetch(url.toString(), { method: "POST", body: formData });
93
212
  if (!res.ok) {
94
213
  const body = await res.json().catch(() => ({}));
95
214
  throw new ProdigioError((_a = body.error) != null ? _a : `Upload failed: ${res.status}`, res.status);
@@ -106,16 +225,82 @@ var _Prodigio = class _Prodigio {
106
225
  text: pb.text
107
226
  };
108
227
  }
109
- // Returns the <meta> tag props for server-rendered compliance verification.
110
- // Add to your page <head> — this is the most reliable detection method.
111
- // Next.js: export const metadata = { other: { 'prodigio-badge': 'true' } }
112
228
  static meta() {
113
229
  return { name: "prodigio-badge", content: "true" };
114
230
  }
231
+ // ── init() — v2 badge auto-injection ─────────────────────────────────────
232
+ static async init(config) {
233
+ var _a;
234
+ if (typeof window === "undefined") return;
235
+ const { key, badgeToken, baseUrl = DEFAULT_BASE_URL, badge: badgeConfig } = config;
236
+ const position = (_a = badgeConfig == null ? void 0 : badgeConfig.position) != null ? _a : "bottom-right";
237
+ const paths = badgeConfig == null ? void 0 : badgeConfig.paths;
238
+ if (paths && paths.length > 0) {
239
+ const currentPath = window.location.pathname;
240
+ if (!paths.some((p) => currentPath === p || currentPath.startsWith(p + "/"))) {
241
+ return;
242
+ }
243
+ }
244
+ const hostname = window.location.hostname;
245
+ const cached = getCachedToken(key);
246
+ let bestToken = null;
247
+ let bestTokenRaw = null;
248
+ const [initClaims, cachedClaims] = await Promise.all([
249
+ badgeToken ? verifyBadgeToken(badgeToken) : Promise.resolve(null),
250
+ cached ? verifyBadgeToken(cached.token) : Promise.resolve(null)
251
+ ]);
252
+ if (initClaims && cachedClaims) {
253
+ if (new Date(initClaims.badgeExemptUntil) >= new Date(cachedClaims.badgeExemptUntil)) {
254
+ bestToken = initClaims;
255
+ bestTokenRaw = badgeToken;
256
+ } else {
257
+ bestToken = cachedClaims;
258
+ bestTokenRaw = cached.token;
259
+ }
260
+ } else if (initClaims) {
261
+ bestToken = initClaims;
262
+ bestTokenRaw = badgeToken;
263
+ } else if (cachedClaims) {
264
+ bestToken = cachedClaims;
265
+ bestTokenRaw = cached.token;
266
+ }
267
+ const domainAllowed = bestToken ? bestToken.allowedDomains.length === 0 || bestToken.allowedDomains.includes(hostname) : true;
268
+ const isExempt = bestToken !== null && !bestToken.badgeRequired && domainAllowed;
269
+ if (isExempt) {
270
+ removeBadge();
271
+ } else if (!bestToken && cached) {
272
+ const cacheAge = Date.now() - cached.cachedAt;
273
+ if (cacheAge < GRACE_PERIOD_MS) {
274
+ removeBadge();
275
+ } else {
276
+ injectBadge(position);
277
+ }
278
+ } else {
279
+ injectBadge(position);
280
+ }
281
+ setTimeout(async () => {
282
+ var _a2, _b;
283
+ try {
284
+ const res = await fetch(`${baseUrl}/api/sdk/config?key=${encodeURIComponent(key)}`);
285
+ if (!res.ok) return;
286
+ const data = await res.json();
287
+ if (data.badgeRequired) {
288
+ injectBadge(position);
289
+ return;
290
+ }
291
+ if (data.badgeToken) {
292
+ const refreshedClaims = await verifyBadgeToken(data.badgeToken);
293
+ if (refreshedClaims && !refreshedClaims.badgeRequired) {
294
+ cacheToken(key, data.badgeToken, (_a2 = data.badgeExemptUntil) != null ? _a2 : "", (_b = data.configVersion) != null ? _b : 1);
295
+ removeBadge();
296
+ }
297
+ }
298
+ } catch {
299
+ }
300
+ }, 0);
301
+ }
115
302
  };
116
- // ── Badge ─────────────────────────────────────────────────────────────────
117
- // Returns props to spread onto your <a> element.
118
- // poweredBy is optional — defaults to Prodigio's built-in attribution.
303
+ // ── Badge (static helpers) ────────────────────────────────────────────────
119
304
  _Prodigio.POWERED_BY = {
120
305
  text: "Hiring powered by Prodigio",
121
306
  url: "https://prodigio.io"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prodigio-io/sdk",
3
- "version": "1.0.5",
3
+ "version": "2.0.1",
4
4
  "description": "Official JavaScript SDK for the Prodigio hiring API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",