@prodigio-io/sdk 1.0.4 → 2.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
@@ -2,6 +2,14 @@ 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
+ };
12
+ }
5
13
  interface SalaryRange {
6
14
  min: number;
7
15
  max: number;
@@ -102,11 +110,13 @@ declare class Prodigio {
102
110
  readonly applications: {
103
111
  submit: (params: SubmitApplicationParams) => Promise<ApplicationResult>;
104
112
  };
105
- static badge(poweredBy: PoweredBy): BadgeProps;
113
+ static readonly POWERED_BY: PoweredBy;
114
+ static badge(poweredBy?: PoweredBy): BadgeProps;
106
115
  static meta(): {
107
116
  name: string;
108
117
  content: string;
109
118
  };
119
+ static init(config: ProdigioInitConfig): Promise<void>;
110
120
  }
111
121
 
112
- 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 };
122
+ 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,14 @@ 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
+ };
12
+ }
5
13
  interface SalaryRange {
6
14
  min: number;
7
15
  max: number;
@@ -102,11 +110,13 @@ declare class Prodigio {
102
110
  readonly applications: {
103
111
  submit: (params: SubmitApplicationParams) => Promise<ApplicationResult>;
104
112
  };
105
- static badge(poweredBy: PoweredBy): BadgeProps;
113
+ static readonly POWERED_BY: PoweredBy;
114
+ static badge(poweredBy?: PoweredBy): BadgeProps;
106
115
  static meta(): {
107
116
  name: string;
108
117
  content: string;
109
118
  };
119
+ static init(config: ProdigioInitConfig): Promise<void>;
110
120
  }
111
121
 
112
- 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 };
122
+ 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,7 +45,125 @@ var ProdigioError = class extends Error {
34
45
  this.name = "ProdigioError";
35
46
  }
36
47
  };
37
- var Prodigio = class {
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 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
+ }
166
+ var _Prodigio = class _Prodigio {
38
167
  constructor(config) {
39
168
  // ── Jobs ──────────────────────────────────────────────────────────────────
40
169
  this.jobs = {
@@ -44,9 +173,7 @@ var Prodigio = class {
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 {
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 {
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,34 +234,97 @@ var Prodigio = class {
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);
122
241
  }
123
242
  return res.json();
124
243
  }
125
- // ── Badge ─────────────────────────────────────────────────────────────────
126
- // Returns props to spread onto your <a> element.
127
244
  static badge(poweredBy) {
245
+ const pb = poweredBy != null ? poweredBy : _Prodigio.POWERED_BY;
128
246
  return {
129
- href: poweredBy.url,
247
+ href: pb.url,
130
248
  "data-prodigio-badge": "true",
131
249
  target: "_blank",
132
250
  rel: "noopener noreferrer",
133
- text: poweredBy.text
251
+ text: pb.text
134
252
  };
135
253
  }
136
- // Returns the <meta> tag props for server-rendered compliance verification.
137
- // Add to your page <head> — this is the most reliable detection method.
138
- // Next.js: export const metadata = { other: { 'prodigio-badge': 'true' } }
139
254
  static meta() {
140
255
  return { name: "prodigio-badge", content: "true" };
141
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 hostname = window.location.hostname;
264
+ const cached = getCachedToken(key);
265
+ let bestToken = null;
266
+ let bestTokenRaw = null;
267
+ const [initClaims, cachedClaims] = await Promise.all([
268
+ badgeToken ? verifyBadgeToken(badgeToken) : Promise.resolve(null),
269
+ cached ? verifyBadgeToken(cached.token) : Promise.resolve(null)
270
+ ]);
271
+ if (initClaims && cachedClaims) {
272
+ if (new Date(initClaims.badgeExemptUntil) >= new Date(cachedClaims.badgeExemptUntil)) {
273
+ bestToken = initClaims;
274
+ bestTokenRaw = badgeToken;
275
+ } else {
276
+ bestToken = cachedClaims;
277
+ bestTokenRaw = cached.token;
278
+ }
279
+ } else if (initClaims) {
280
+ bestToken = initClaims;
281
+ bestTokenRaw = badgeToken;
282
+ } else if (cachedClaims) {
283
+ bestToken = cachedClaims;
284
+ bestTokenRaw = cached.token;
285
+ }
286
+ const domainAllowed = bestToken ? bestToken.allowedDomains.length === 0 || bestToken.allowedDomains.includes(hostname) : true;
287
+ const isExempt = bestToken !== null && !bestToken.badgeRequired && domainAllowed;
288
+ if (isExempt) {
289
+ removeBadge();
290
+ } else if (!bestToken && cached) {
291
+ const cacheAge = Date.now() - cached.cachedAt;
292
+ if (cacheAge < GRACE_PERIOD_MS) {
293
+ removeBadge();
294
+ } else {
295
+ injectBadge(position);
296
+ }
297
+ } else {
298
+ injectBadge(position);
299
+ }
300
+ setTimeout(async () => {
301
+ var _a2, _b;
302
+ try {
303
+ const res = await fetch(`${baseUrl}/api/sdk/config?key=${encodeURIComponent(key)}`);
304
+ if (!res.ok) return;
305
+ const data = await res.json();
306
+ if (data.badgeRequired) {
307
+ injectBadge(position);
308
+ return;
309
+ }
310
+ if (data.badgeToken) {
311
+ const refreshedClaims = await verifyBadgeToken(data.badgeToken);
312
+ if (refreshedClaims && !refreshedClaims.badgeRequired) {
313
+ cacheToken(key, data.badgeToken, (_a2 = data.badgeExemptUntil) != null ? _a2 : "", (_b = data.configVersion) != null ? _b : 1);
314
+ removeBadge();
315
+ }
316
+ }
317
+ } catch {
318
+ }
319
+ }, 0);
320
+ }
321
+ };
322
+ // ── Badge (static helpers) ────────────────────────────────────────────────
323
+ _Prodigio.POWERED_BY = {
324
+ text: "Hiring powered by Prodigio",
325
+ url: "https://prodigio.io"
142
326
  };
327
+ var Prodigio = _Prodigio;
143
328
  var index_default = Prodigio;
144
329
  // Annotate the CommonJS export names for ESM import in node:
145
330
  0 && (module.exports = {
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,7 +19,125 @@ var ProdigioError = class extends Error {
8
19
  this.name = "ProdigioError";
9
20
  }
10
21
  };
11
- var Prodigio = class {
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 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
+ }
140
+ var _Prodigio = class _Prodigio {
12
141
  constructor(config) {
13
142
  // ── Jobs ──────────────────────────────────────────────────────────────────
14
143
  this.jobs = {
@@ -18,9 +147,7 @@ var Prodigio = class {
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 {
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 {
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,34 +208,97 @@ var Prodigio = class {
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);
96
215
  }
97
216
  return res.json();
98
217
  }
99
- // ── Badge ─────────────────────────────────────────────────────────────────
100
- // Returns props to spread onto your <a> element.
101
218
  static badge(poweredBy) {
219
+ const pb = poweredBy != null ? poweredBy : _Prodigio.POWERED_BY;
102
220
  return {
103
- href: poweredBy.url,
221
+ href: pb.url,
104
222
  "data-prodigio-badge": "true",
105
223
  target: "_blank",
106
224
  rel: "noopener noreferrer",
107
- text: poweredBy.text
225
+ text: pb.text
108
226
  };
109
227
  }
110
- // Returns the <meta> tag props for server-rendered compliance verification.
111
- // Add to your page <head> — this is the most reliable detection method.
112
- // Next.js: export const metadata = { other: { 'prodigio-badge': 'true' } }
113
228
  static meta() {
114
229
  return { name: "prodigio-badge", content: "true" };
115
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 hostname = window.location.hostname;
238
+ const cached = getCachedToken(key);
239
+ let bestToken = null;
240
+ let bestTokenRaw = null;
241
+ const [initClaims, cachedClaims] = await Promise.all([
242
+ badgeToken ? verifyBadgeToken(badgeToken) : Promise.resolve(null),
243
+ cached ? verifyBadgeToken(cached.token) : Promise.resolve(null)
244
+ ]);
245
+ if (initClaims && cachedClaims) {
246
+ if (new Date(initClaims.badgeExemptUntil) >= new Date(cachedClaims.badgeExemptUntil)) {
247
+ bestToken = initClaims;
248
+ bestTokenRaw = badgeToken;
249
+ } else {
250
+ bestToken = cachedClaims;
251
+ bestTokenRaw = cached.token;
252
+ }
253
+ } else if (initClaims) {
254
+ bestToken = initClaims;
255
+ bestTokenRaw = badgeToken;
256
+ } else if (cachedClaims) {
257
+ bestToken = cachedClaims;
258
+ bestTokenRaw = cached.token;
259
+ }
260
+ const domainAllowed = bestToken ? bestToken.allowedDomains.length === 0 || bestToken.allowedDomains.includes(hostname) : true;
261
+ const isExempt = bestToken !== null && !bestToken.badgeRequired && domainAllowed;
262
+ if (isExempt) {
263
+ removeBadge();
264
+ } else if (!bestToken && cached) {
265
+ const cacheAge = Date.now() - cached.cachedAt;
266
+ if (cacheAge < GRACE_PERIOD_MS) {
267
+ removeBadge();
268
+ } else {
269
+ injectBadge(position);
270
+ }
271
+ } else {
272
+ injectBadge(position);
273
+ }
274
+ setTimeout(async () => {
275
+ var _a2, _b;
276
+ try {
277
+ const res = await fetch(`${baseUrl}/api/sdk/config?key=${encodeURIComponent(key)}`);
278
+ if (!res.ok) return;
279
+ const data = await res.json();
280
+ if (data.badgeRequired) {
281
+ injectBadge(position);
282
+ return;
283
+ }
284
+ if (data.badgeToken) {
285
+ const refreshedClaims = await verifyBadgeToken(data.badgeToken);
286
+ if (refreshedClaims && !refreshedClaims.badgeRequired) {
287
+ cacheToken(key, data.badgeToken, (_a2 = data.badgeExemptUntil) != null ? _a2 : "", (_b = data.configVersion) != null ? _b : 1);
288
+ removeBadge();
289
+ }
290
+ }
291
+ } catch {
292
+ }
293
+ }, 0);
294
+ }
295
+ };
296
+ // ── Badge (static helpers) ────────────────────────────────────────────────
297
+ _Prodigio.POWERED_BY = {
298
+ text: "Hiring powered by Prodigio",
299
+ url: "https://prodigio.io"
116
300
  };
301
+ var Prodigio = _Prodigio;
117
302
  var index_default = Prodigio;
118
303
  export {
119
304
  Prodigio,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prodigio-io/sdk",
3
- "version": "1.0.4",
3
+ "version": "2.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",