@hirely/sdk 1.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/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +532 -0
- package/dist/cache/memory-cache.d.ts +1 -0
- package/dist/client.d.ts +151 -0
- package/dist/constants.d.ts +8 -0
- package/dist/errors.d.ts +115 -0
- package/dist/http/request.d.ts +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +1 -0
- package/dist/resources/certificates.d.ts +25 -0
- package/dist/resources/contacts.d.ts +15 -0
- package/dist/resources/cv.d.ts +15 -0
- package/dist/resources/education.d.ts +25 -0
- package/dist/resources/faqs.d.ts +25 -0
- package/dist/resources/projects.d.ts +51 -0
- package/dist/resources/services.d.ts +25 -0
- package/dist/resources/skills.d.ts +22 -0
- package/dist/resources/testimonials.d.ts +26 -0
- package/dist/resources/work.d.ts +31 -0
- package/dist/types.d.ts +464 -0
- package/package.json +84 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@hirely/sdk` are documented here.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2024-09-13
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Initial public release of `@hirely/sdk`
|
|
10
|
+
- `Hirely` client class with `apiKey` validation
|
|
11
|
+
- `hirely.get()` — fetch complete portfolio in one request
|
|
12
|
+
- `hirely.me()` — fetch account info + full profile
|
|
13
|
+
- Resource helpers: `projects`, `work`, `education`, `skills`, `certificates`, `services`, `testimonials`, `faqs`, `contacts`, `cv`
|
|
14
|
+
- Per-resource lookup methods: `.getById()` and `.getBySlug()` (projects only)
|
|
15
|
+
- Typed error hierarchy: `HirelyError`, `HirelyAuthenticationError`, `HirelyNotFoundError`, `HirelyValidationError`, `HirelyRateLimitError`, `HirelyTimeoutError`, `HirelyServerError`
|
|
16
|
+
- SDK-side in-memory caching with configurable TTL
|
|
17
|
+
- Custom cache store interface (`HirelyCache`) for Redis or any external store
|
|
18
|
+
- Per-request cache bypass via `{ cache: false }`
|
|
19
|
+
- Exponential backoff retry logic for transient errors
|
|
20
|
+
- `AbortController`-based request timeout
|
|
21
|
+
- Zero runtime dependencies — uses native `fetch` API
|
|
22
|
+
- Full TypeScript support with declaration maps
|
|
23
|
+
- ESM-only output targeting ES2022
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hirely
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>@hirely/sdk</h1>
|
|
3
|
+
<p>Official JavaScript & TypeScript SDK for <a href="https://hirely.cc">Hirely</a></p>
|
|
4
|
+
<p>
|
|
5
|
+
<a href="https://www.npmjs.com/package/@hirely/sdk"><img src="https://img.shields.io/npm/v/@hirely/sdk.svg" alt="npm version" /></a>
|
|
6
|
+
<a href="https://www.npmjs.com/package/@hirely/sdk"><img src="https://img.shields.io/npm/dm/@hirely/sdk.svg" alt="downloads" /></a>
|
|
7
|
+
<img src="https://img.shields.io/badge/TypeScript-ready-blue" alt="TypeScript" />
|
|
8
|
+
<img src="https://img.shields.io/badge/license-MIT-green" alt="MIT license" />
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What is Hirely?
|
|
15
|
+
|
|
16
|
+
[Hirely](https://hirely.cc) is an **AI-powered career accelerator and professional portfolio platform** built for developers and freelancers. It combines intelligent content management, semantic job discovery, and a conversational AI career coach — all in one platform.
|
|
17
|
+
|
|
18
|
+
### The platform at a glance
|
|
19
|
+
|
|
20
|
+
**AI-First career management**
|
|
21
|
+
Hirely's AI doesn't just store your data — it actively coaches you. It analyzes your skill matrix, identifies knowledge gaps, generates tailored CVs for specific job descriptions, scores your resume against ATS systems, and synthesizes your experience into compelling recruiter-ready biographies. An integrated conversational AI agent lets you build and update your entire profile through natural chat.
|
|
22
|
+
|
|
23
|
+
**Semantic professional discovery**
|
|
24
|
+
Hirely moves beyond keyword matching. Using Google Gemini vector embeddings, the platform understands the true intent behind searches — matching freelancers to clients based on deep contextual similarity, not just exact keyword overlap.
|
|
25
|
+
|
|
26
|
+
**Third-party integrations**
|
|
27
|
+
Connect GitHub to auto-import projects from public repositories (AI-generated descriptions, skill extraction, verified developer badge). Connect Spotify to display a live "Now Playing" widget on your public portfolio. LinkedIn, Discord, Figma, and Notion integrations are on the roadmap.
|
|
28
|
+
|
|
29
|
+
**Enterprise-grade infrastructure**
|
|
30
|
+
Built for scale: Redis SWR caching, background job queues, real-time AI quota management, cryptographic webhook verification, and GDPR/CCPA/EU AI Act compliance — including permanent Right to be Forgotten workflows and data portability exports.
|
|
31
|
+
|
|
32
|
+
### How the SDK fits in
|
|
33
|
+
|
|
34
|
+
Hirely manages all your professional content — projects, work history, education, skills, certificates, testimonials, services, and more — through a rich dashboard. The `@hirely/sdk` is the bridge that lets you pull that content into **any website or app you build**, with full TypeScript types, caching, and error handling included.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Hirely dashboard (content management + AI tools)
|
|
38
|
+
↓ your public portfolio data
|
|
39
|
+
@hirely/sdk (this package)
|
|
40
|
+
↓ typed, cached, ready to render
|
|
41
|
+
Your website / app (Next.js, Astro, Remix, plain HTML — your choice)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**You own the presentation layer completely.** Use your own framework, your own design, your own domain. The SDK handles authentication, data fetching, retries, caching, and TypeScript types — so you can focus on building a great frontend.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Bun (recommended)
|
|
52
|
+
bun add @hirely/sdk
|
|
53
|
+
|
|
54
|
+
# npm
|
|
55
|
+
npm install @hirely/sdk
|
|
56
|
+
|
|
57
|
+
# pnpm
|
|
58
|
+
pnpm add @hirely/sdk
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import Hirely from "@hirely/sdk";
|
|
67
|
+
|
|
68
|
+
const hirely = new Hirely({
|
|
69
|
+
apiKey: process.env.HIRELY_API_KEY!,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const portfolio = await hirely.get();
|
|
73
|
+
|
|
74
|
+
console.log(portfolio.profile?.firstName);
|
|
75
|
+
console.log(portfolio.projects.length);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## API key setup
|
|
81
|
+
|
|
82
|
+
Generate a public API key from your [Hirely dashboard](https://hirely.cc/dashboard/settings/api-keys).
|
|
83
|
+
|
|
84
|
+
```env
|
|
85
|
+
HIRELY_API_KEY=hk_pub_xxxxxxxxxxxxxxxxxxxxxxxxx
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
> **Note:** Your API key starts with `hk_pub_`. It is scoped to your account and provides **read-only** access to your public portfolio data. It cannot access or modify any other user's data.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Usage
|
|
93
|
+
|
|
94
|
+
### Full portfolio
|
|
95
|
+
|
|
96
|
+
The most efficient way to power a portfolio website — fetch everything in a single request:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const portfolio = await hirely.get();
|
|
100
|
+
|
|
101
|
+
portfolio.profile // → HirelyProfile
|
|
102
|
+
portfolio.projects // → HirelyProject[]
|
|
103
|
+
portfolio.work // → HirelyWork[]
|
|
104
|
+
portfolio.education // → HirelyEducation[]
|
|
105
|
+
portfolio.skills // → HirelySkill[]
|
|
106
|
+
portfolio.certificates // → HirelyCertificate[]
|
|
107
|
+
portfolio.services // → HirelyService[]
|
|
108
|
+
portfolio.testimonials // → HirelyTestimonial[]
|
|
109
|
+
portfolio.faqs // → HirelyFaq[]
|
|
110
|
+
portfolio.contact // → HirelyContact | null
|
|
111
|
+
portfolio.cv // → HirelyCV | null
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Account info
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const me = await hirely.me();
|
|
118
|
+
|
|
119
|
+
me.userName // → "mahmoud"
|
|
120
|
+
me.email // → "hello@example.com"
|
|
121
|
+
me.plan // → "pro"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Projects
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
// All public projects
|
|
128
|
+
const projects = await hirely.projects();
|
|
129
|
+
|
|
130
|
+
// By MongoDB ID
|
|
131
|
+
const project = await hirely.projects.getById("665f1a2b3c4d5e6f7a8b9c0d");
|
|
132
|
+
|
|
133
|
+
// By slug (ideal for dynamic routes)
|
|
134
|
+
const project = await hirely.projects.getBySlug("my-awesome-app");
|
|
135
|
+
|
|
136
|
+
project.title // → "My Awesome App"
|
|
137
|
+
project.slug // → "my-awesome-app"
|
|
138
|
+
project.description // → "..."
|
|
139
|
+
project.technologies // → ["React", "TypeScript", "Node.js"]
|
|
140
|
+
project.demoUrl // → "https://example.com"
|
|
141
|
+
project.repositoryUrl // → "https://github.com/..."
|
|
142
|
+
project.featured // → true
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Work experience
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
const jobs = await hirely.work();
|
|
149
|
+
const job = await hirely.work.getById("665...");
|
|
150
|
+
|
|
151
|
+
job.companyName // → "Acme Corp"
|
|
152
|
+
job.position // → "Senior Engineer"
|
|
153
|
+
job.isCurrent // → true
|
|
154
|
+
job.achievements // → ["Led migration to microservices", ...]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Education
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const education = await hirely.education();
|
|
161
|
+
const entry = await hirely.education.getById("665...");
|
|
162
|
+
|
|
163
|
+
entry.institution // → "Cairo University"
|
|
164
|
+
entry.degree // → "Bachelor's"
|
|
165
|
+
entry.fieldOfStudy // → "Computer Science"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Skills
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
const skills = await hirely.skills();
|
|
172
|
+
|
|
173
|
+
// Group by category
|
|
174
|
+
const byCategory = skills.reduce((acc, skill) => {
|
|
175
|
+
const cat = skill.category ?? "Other";
|
|
176
|
+
acc[cat] = [...(acc[cat] ?? []), skill.name ?? ""];
|
|
177
|
+
return acc;
|
|
178
|
+
}, {} as Record<string, string[]>);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Certificates
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
const certs = await hirely.certificates();
|
|
185
|
+
const cert = await hirely.certificates.getById("665...");
|
|
186
|
+
|
|
187
|
+
cert.name // → "AWS Solutions Architect"
|
|
188
|
+
cert.issuer // → "Amazon Web Services"
|
|
189
|
+
cert.credentialUrl // → "https://credly.com/..."
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Services
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
const services = await hirely.services();
|
|
196
|
+
const service = await hirely.services.getById("665...");
|
|
197
|
+
|
|
198
|
+
service.title // → "Full-Stack Development"
|
|
199
|
+
service.pricing?.type // → "fixed"
|
|
200
|
+
service.packages // → [{ name: "Basic", price: 500, ... }]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Testimonials
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
const testimonials = await hirely.testimonials();
|
|
207
|
+
const testimonial = await hirely.testimonials.getById("665...");
|
|
208
|
+
|
|
209
|
+
testimonial.client?.firstName // → "John"
|
|
210
|
+
testimonial.rating // → 5
|
|
211
|
+
testimonial.review // → "Exceptional work..."
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### FAQ
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
const faqs = await hirely.faqs();
|
|
218
|
+
const faq = await hirely.faqs.getById("665...");
|
|
219
|
+
|
|
220
|
+
faq.question // → "What is your availability?"
|
|
221
|
+
faq.answer // → "I'm available for..."
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Contacts & social links
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
const contact = await hirely.contacts();
|
|
228
|
+
|
|
229
|
+
contact?.socialLinks?.forEach(link => {
|
|
230
|
+
console.log(link.platform, link.url);
|
|
231
|
+
// → "github", "https://github.com/..."
|
|
232
|
+
// → "linkedIn", "https://linkedin.com/..."
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### CV
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
const cv = await hirely.cv();
|
|
240
|
+
|
|
241
|
+
cv?.pdfUrl // → "https://cdn.hirely.cc/cvs/my-cv.pdf"
|
|
242
|
+
cv?.docxUrl // → "https://cdn.hirely.cc/cvs/my-cv.docx"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Site info & settings
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
const info = await hirely.info();
|
|
249
|
+
|
|
250
|
+
info?.isActive // → true
|
|
251
|
+
info?.showSkills // → true
|
|
252
|
+
info?.siteSettings?.bgColor // → "#0f0f0f"
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Caching
|
|
258
|
+
|
|
259
|
+
Enable optional in-memory response caching to reduce API calls:
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
const hirely = new Hirely({
|
|
263
|
+
apiKey: process.env.HIRELY_API_KEY!,
|
|
264
|
+
cache: {
|
|
265
|
+
enabled: true,
|
|
266
|
+
ttl: 300, // seconds — cache responses for 5 minutes
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Normal call — uses cache
|
|
271
|
+
const projects = await hirely.projects();
|
|
272
|
+
|
|
273
|
+
// Bypass cache for a fresh request
|
|
274
|
+
const freshProjects = await hirely.projects({ cache: false });
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Custom cache store
|
|
278
|
+
|
|
279
|
+
Bring your own cache backend (Redis, Upstash, etc.):
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import type { HirelyCache } from "@hirely/sdk";
|
|
283
|
+
|
|
284
|
+
const redisStore: HirelyCache = {
|
|
285
|
+
get: async (key) => {
|
|
286
|
+
const raw = await redis.get(key);
|
|
287
|
+
return raw ? JSON.parse(raw) : undefined;
|
|
288
|
+
},
|
|
289
|
+
set: async (key, value, ttl) => {
|
|
290
|
+
await redis.set(key, JSON.stringify(value), "EX", ttl);
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const hirely = new Hirely({
|
|
295
|
+
apiKey: process.env.HIRELY_API_KEY!,
|
|
296
|
+
cache: { enabled: true, ttl: 300, store: redisStore },
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Retries & timeouts
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
const hirely = new Hirely({
|
|
306
|
+
apiKey: process.env.HIRELY_API_KEY!,
|
|
307
|
+
timeout: 10_000, // 10s timeout per request
|
|
308
|
+
retries: 2, // retry up to 2 times on transient errors (5xx, 429)
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The SDK automatically retries on `500`, `502`, `503`, `504`, `408`, `425`, and `429` with exponential backoff. It does **not** retry on `400`, `401`, `403`, `404`, or `422`.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Error handling
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
import Hirely, {
|
|
320
|
+
HirelyError,
|
|
321
|
+
HirelyAuthenticationError,
|
|
322
|
+
HirelyNotFoundError,
|
|
323
|
+
HirelyRateLimitError,
|
|
324
|
+
HirelyTimeoutError,
|
|
325
|
+
HirelyServerError,
|
|
326
|
+
} from "@hirely/sdk";
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
const project = await hirely.projects.getBySlug("my-project");
|
|
330
|
+
} catch (error) {
|
|
331
|
+
if (error instanceof HirelyAuthenticationError) {
|
|
332
|
+
// 401/403 — invalid or missing API key
|
|
333
|
+
} else if (error instanceof HirelyNotFoundError) {
|
|
334
|
+
// 404 — resource does not exist
|
|
335
|
+
} else if (error instanceof HirelyRateLimitError) {
|
|
336
|
+
console.log(`Retry in ${error.retryAfter}s`);
|
|
337
|
+
} else if (error instanceof HirelyTimeoutError) {
|
|
338
|
+
// Request exceeded the configured timeout
|
|
339
|
+
} else if (error instanceof HirelyServerError) {
|
|
340
|
+
// 5xx server error
|
|
341
|
+
} else if (error instanceof HirelyError) {
|
|
342
|
+
// Any other SDK error
|
|
343
|
+
console.error(error.status, error.code, error.message);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Framework examples
|
|
351
|
+
|
|
352
|
+
### Next.js (App Router)
|
|
353
|
+
|
|
354
|
+
```tsx
|
|
355
|
+
// app/page.tsx
|
|
356
|
+
import Hirely from "@hirely/sdk";
|
|
357
|
+
|
|
358
|
+
const hirely = new Hirely({
|
|
359
|
+
apiKey: process.env.HIRELY_API_KEY!, // server-only env var
|
|
360
|
+
cache: { enabled: true, ttl: 300 },
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
export default async function Page() {
|
|
364
|
+
const portfolio = await hirely.get();
|
|
365
|
+
|
|
366
|
+
return (
|
|
367
|
+
<main>
|
|
368
|
+
<h1>
|
|
369
|
+
{portfolio.profile?.firstName} {portfolio.profile?.lastName}
|
|
370
|
+
</h1>
|
|
371
|
+
<p>{portfolio.profile?.about}</p>
|
|
372
|
+
|
|
373
|
+
<section>
|
|
374
|
+
<h2>Projects</h2>
|
|
375
|
+
{portfolio.projects.map((project) => (
|
|
376
|
+
<article key={project._id}>
|
|
377
|
+
<h3>{project.title}</h3>
|
|
378
|
+
<p>{project.shortDescription}</p>
|
|
379
|
+
<ul>
|
|
380
|
+
{project.technologies?.map((tech) => (
|
|
381
|
+
<li key={tech}>{tech}</li>
|
|
382
|
+
))}
|
|
383
|
+
</ul>
|
|
384
|
+
{project.demoUrl && (
|
|
385
|
+
<a href={project.demoUrl}>Live Demo →</a>
|
|
386
|
+
)}
|
|
387
|
+
</article>
|
|
388
|
+
))}
|
|
389
|
+
</section>
|
|
390
|
+
</main>
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Next.js dynamic project page
|
|
396
|
+
|
|
397
|
+
```tsx
|
|
398
|
+
// app/projects/[slug]/page.tsx
|
|
399
|
+
import Hirely, { HirelyNotFoundError } from "@hirely/sdk";
|
|
400
|
+
import { notFound } from "next/navigation";
|
|
401
|
+
|
|
402
|
+
const hirely = new Hirely({ apiKey: process.env.HIRELY_API_KEY! });
|
|
403
|
+
|
|
404
|
+
export default async function ProjectPage({
|
|
405
|
+
params,
|
|
406
|
+
}: {
|
|
407
|
+
params: { slug: string };
|
|
408
|
+
}) {
|
|
409
|
+
try {
|
|
410
|
+
const project = await hirely.projects.getBySlug(params.slug);
|
|
411
|
+
|
|
412
|
+
return (
|
|
413
|
+
<article>
|
|
414
|
+
<h1>{project.title}</h1>
|
|
415
|
+
<p>{project.description}</p>
|
|
416
|
+
</article>
|
|
417
|
+
);
|
|
418
|
+
} catch (error) {
|
|
419
|
+
if (error instanceof HirelyNotFoundError) notFound();
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Astro
|
|
426
|
+
|
|
427
|
+
```astro
|
|
428
|
+
---
|
|
429
|
+
// src/pages/index.astro
|
|
430
|
+
import Hirely from "@hirely/sdk";
|
|
431
|
+
|
|
432
|
+
const hirely = new Hirely({ apiKey: import.meta.env.HIRELY_API_KEY });
|
|
433
|
+
const portfolio = await hirely.get();
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
<main>
|
|
437
|
+
<h1>{portfolio.profile?.firstName} {portfolio.profile?.lastName}</h1>
|
|
438
|
+
{portfolio.projects.map((p) => (
|
|
439
|
+
<article>
|
|
440
|
+
<h2>{p.title}</h2>
|
|
441
|
+
<p>{p.shortDescription}</p>
|
|
442
|
+
</article>
|
|
443
|
+
))}
|
|
444
|
+
</main>
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Remix
|
|
448
|
+
|
|
449
|
+
```tsx
|
|
450
|
+
// app/routes/_index.tsx
|
|
451
|
+
import { json } from "@remix-run/node";
|
|
452
|
+
import { useLoaderData } from "@remix-run/react";
|
|
453
|
+
import Hirely from "@hirely/sdk";
|
|
454
|
+
|
|
455
|
+
const hirely = new Hirely({ apiKey: process.env.HIRELY_API_KEY! });
|
|
456
|
+
|
|
457
|
+
export async function loader() {
|
|
458
|
+
const portfolio = await hirely.get();
|
|
459
|
+
return json({ portfolio });
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export default function Index() {
|
|
463
|
+
const { portfolio } = useLoaderData<typeof loader>();
|
|
464
|
+
return <h1>{portfolio.profile?.firstName}</h1>;
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## Security
|
|
471
|
+
|
|
472
|
+
### API key model
|
|
473
|
+
|
|
474
|
+
- Keys are generated in your Hirely dashboard
|
|
475
|
+
- Each key is scoped to **one Hirely account** — one key cannot access another user's data
|
|
476
|
+
- SDK access is **read-only** — the SDK cannot create, update, or delete any data
|
|
477
|
+
- Keys use the format `hk_pub_xxx` and are safe to use in server-side environments
|
|
478
|
+
|
|
479
|
+
### Public vs. server environments
|
|
480
|
+
|
|
481
|
+
| Environment | Safe to use API key? |
|
|
482
|
+
|---|---|
|
|
483
|
+
| Next.js Server Components | ✅ Yes — runs on server |
|
|
484
|
+
| Astro SSR / SSG | ✅ Yes — runs on server |
|
|
485
|
+
| Node.js / Bun scripts | ✅ Yes |
|
|
486
|
+
| Remix loaders | ✅ Yes — runs on server |
|
|
487
|
+
| Browser (client-side) | ⚠️ Only if your use case explicitly requires it |
|
|
488
|
+
|
|
489
|
+
Because `hk_pub_` keys are read-only and scoped to your portfolio, exposing them in client-side code does not grant write access. However, it does expose your key to anyone who inspects your bundle. Use your judgement based on your application.
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## Configuration reference
|
|
494
|
+
|
|
495
|
+
```ts
|
|
496
|
+
interface HirelyConfig {
|
|
497
|
+
apiKey: string; // Required. Must start with hk_pub_
|
|
498
|
+
timeout?: number; // Request timeout in ms. Default: 30_000
|
|
499
|
+
retries?: number; // Retry attempts for transient errors. Default: 2
|
|
500
|
+
cache?: {
|
|
501
|
+
enabled: boolean; // Enable SDK-side caching
|
|
502
|
+
ttl?: number; // Cache TTL in seconds. Default: 300
|
|
503
|
+
store?: HirelyCache; // Custom cache store (Redis, etc.)
|
|
504
|
+
};
|
|
505
|
+
fetch?: typeof fetch; // Custom fetch implementation
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## Works with
|
|
512
|
+
|
|
513
|
+
| Runtime | Supported |
|
|
514
|
+
|---|---|
|
|
515
|
+
| Node.js 18+ | ✅ |
|
|
516
|
+
| Bun | ✅ |
|
|
517
|
+
| Deno | ✅ |
|
|
518
|
+
| Browser (modern) | ✅ |
|
|
519
|
+
| Next.js | ✅ |
|
|
520
|
+
| Astro | ✅ |
|
|
521
|
+
| Remix | ✅ |
|
|
522
|
+
| React (SSR/SSG) | ✅ |
|
|
523
|
+
| Vue / Nuxt | ✅ |
|
|
524
|
+
| Svelte / SvelteKit | ✅ |
|
|
525
|
+
|
|
526
|
+
No dependencies required. Uses the native `fetch` API available in all modern environments.
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## License
|
|
531
|
+
|
|
532
|
+
MIT © [Hirely](https://hirely.cc)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|