@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 +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +207 -22
- package/dist/index.mjs +207 -22
- package/package.json +1 -1
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
247
|
+
href: pb.url,
|
|
130
248
|
"data-prodigio-badge": "true",
|
|
131
249
|
target: "_blank",
|
|
132
250
|
rel: "noopener noreferrer",
|
|
133
|
-
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
|
-
|
|
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:
|
|
221
|
+
href: pb.url,
|
|
104
222
|
"data-prodigio-badge": "true",
|
|
105
223
|
target: "_blank",
|
|
106
224
|
rel: "noopener noreferrer",
|
|
107
|
-
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,
|