@human-gateway/sdk 0.0.1 → 0.1.0-alpha.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/README.md CHANGED
@@ -1,21 +1,642 @@
1
1
  # Human Gateway SDK
2
2
 
3
- Official Human Gateway SDK placeholder.
3
+ Human Gateway SDK lets partners add privacy-first human verification and human authentication to their apps without handling identity, biometrics, World ID internals, nullifiers, or proof verification logic.
4
4
 
5
- This package reserves the official Human Gateway SDK namespace.
5
+ Human Gateway is designed around two identifiers:
6
6
 
7
- The production SDK is under active development and will be released in a future version.
7
+ ```txt
8
+ projectId -> identifies your Human Gateway project
9
+ actionKey -> identifies the specific verify/auth flow inside that project
10
+ ```
8
11
 
9
- ## Status
12
+ For production integrations, pass an `actionKey` explicitly.
10
13
 
11
- Placeholder release (`v0.0.1`)
14
+ ---
12
15
 
13
- ## Package
16
+ ## Installation
14
17
 
15
18
  ```bash
16
19
  npm install @human-gateway/sdk
17
20
  ```
18
21
 
19
- ## Purpose
22
+ > The SDK is currently in alpha. Package publication should be handled as a controlled release step. For local development, use the package from the Human Gateway monorepo.
23
+
24
+ ---
25
+
26
+ ## Quickstart: Verify a human
27
+
28
+ ```ts
29
+ import { HumanGateway } from "@human-gateway/sdk";
30
+
31
+ const hg = new HumanGateway({
32
+ projectId: "project_xxx",
33
+ });
34
+
35
+ const result = await hg.verify({
36
+ actionKey: "hg_verify_action_key_xxx",
37
+ });
38
+
39
+ if (result.verified_human) {
40
+ // Hide verify button
41
+ // Show verified badge
42
+ // Optionally wait for the signed backend callback before persisting state
43
+ }
44
+ ```
45
+
46
+ Example verification result:
47
+
48
+ ```json
49
+ {
50
+ "success": true,
51
+ "verified_human": true,
52
+ "unique_for_partner": true,
53
+ "already_verified": false,
54
+ "project_id": "project_xxx"
55
+ }
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Quickstart: Sign in a verified human
61
+
62
+ ```ts
63
+ import { HumanGateway } from "@human-gateway/sdk";
64
+
65
+ const hg = new HumanGateway({
66
+ projectId: "project_xxx",
67
+ });
68
+
69
+ const result = await hg.auth({
70
+ actionKey: "hg_auth_action_key_xxx",
71
+ });
72
+
73
+ if (result.success && result.verified_human) {
74
+ // Update your UI
75
+ // Store the returned partner-scoped session only according to your own app flow
76
+ }
77
+ ```
78
+
79
+ Example auth result:
80
+
81
+ ```json
82
+ {
83
+ "success": true,
84
+ "event": "auth.completed",
85
+ "verified_human": true,
86
+ "partner_user_id": "partner_user_xxx",
87
+ "auth_session_id": "auth_session_xxx",
88
+ "project_id": "project_xxx",
89
+ "expires_at": "2026-06-25T00:00:00.000Z"
90
+ }
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Project vs Action
96
+
97
+ A Human Gateway project represents one partner integration.
98
+
99
+ An action represents a specific flow inside that project.
100
+
101
+ Example:
102
+
103
+ ```txt
104
+ Project: My SaaS App
105
+
106
+ Actions:
107
+ - Verify signup
108
+ - Auth login
109
+ - Verify voting
110
+ - Auth dashboard access
111
+ ```
112
+
113
+ Use the same `projectId` for the project and pass the correct `actionKey` for the flow you want to run.
114
+
115
+ ```ts
116
+ await hg.verify({
117
+ actionKey: "hg_verify_signup_xxx",
118
+ });
119
+
120
+ await hg.auth({
121
+ actionKey: "hg_auth_login_xxx",
122
+ });
123
+ ```
124
+
125
+ If `actionKey` is omitted, Human Gateway may use a fallback active action for compatibility. Explicit `actionKey` usage is recommended for production integrations.
126
+
127
+ ---
128
+
129
+ ## What Human Gateway does
130
+
131
+ Human Gateway verifies that a real human completed a World ID proof inside a specific project/action context.
132
+
133
+ Human Gateway does **not** expose:
134
+
135
+ * personal identity
136
+ * emails
137
+ * names
138
+ * biometrics
139
+ * World ID nullifiers
140
+ * raw World ID proof data
141
+ * internal Human Gateway user records
142
+
143
+ The partner receives only scoped results needed to update product behavior.
144
+
145
+ ---
146
+
147
+ ## Frontend options
148
+
149
+ ```ts
150
+ const result = await hg.verify({
151
+ actionKey: "hg_verify_action_key_xxx",
152
+ onOpen: () => {
153
+ console.log("Verification popup opened");
154
+ },
155
+ onSuccess: (result) => {
156
+ console.log("Verification completed:", result);
157
+ },
158
+ onError: (error) => {
159
+ console.error("Verification failed:", error.code);
160
+ },
161
+ });
162
+ ```
163
+
164
+ ```ts
165
+ const result = await hg.auth({
166
+ actionKey: "hg_auth_action_key_xxx",
167
+ onOpen: () => {
168
+ console.log("Auth popup opened");
169
+ },
170
+ onSuccess: (result) => {
171
+ console.log("Auth completed:", result);
172
+ },
173
+ onError: (error) => {
174
+ console.error("Auth failed:", error.code);
175
+ },
176
+ });
177
+ ```
178
+
179
+ ---
180
+
181
+ ## SDK configuration
182
+
183
+ ```ts
184
+ const hg = new HumanGateway({
185
+ projectId: "project_xxx",
186
+ environment: "production",
187
+ mode: "auto",
188
+ timeoutMs: 300000,
189
+ });
190
+ ```
191
+
192
+ Available options:
193
+
194
+ ```ts
195
+ type HumanGatewayOptions = {
196
+ projectId: string;
197
+ environment?: "production" | "staging" | "development";
198
+ mode?: "auto" | "popup" | "modal" | "redirect";
199
+ baseUrl?: string;
200
+ timeoutMs?: number;
201
+ };
202
+ ```
203
+
204
+ Alpha behavior:
205
+
206
+ * `popup` is supported.
207
+ * `auto` currently uses popup behavior.
208
+ * `modal` is planned.
209
+ * `redirect` is planned.
210
+
211
+ ---
212
+
213
+ ## Verify options
214
+
215
+ ```ts
216
+ type HumanGatewayVerifyOptions = {
217
+ actionKey?: string;
218
+ onOpen?: () => void;
219
+ onClose?: () => void;
220
+ onSuccess?: (result: HumanGatewayResult) => void;
221
+ onError?: (error: HumanGatewayError) => void;
222
+ };
223
+ ```
224
+
225
+ Verify result:
226
+
227
+ ```ts
228
+ type HumanGatewayResult = {
229
+ success: boolean;
230
+ verified_human: boolean;
231
+ unique_for_partner: boolean;
232
+ already_verified: boolean;
233
+ project_id: string;
234
+ };
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Auth options
240
+
241
+ ```ts
242
+ type HumanGatewayAuthOptions = {
243
+ actionKey?: string;
244
+ onOpen?: () => void;
245
+ onClose?: () => void;
246
+ onSuccess?: (result: HumanGatewayAuthResult) => void;
247
+ onError?: (error: HumanGatewayError) => void;
248
+ };
249
+ ```
250
+
251
+ Auth result:
252
+
253
+ ```ts
254
+ type HumanGatewayAuthResult = {
255
+ success: boolean;
256
+ event?: "auth.completed";
257
+ verified_human: boolean;
258
+ partner_user_id: string;
259
+ auth_session_id: string;
260
+ project_id: string;
261
+ expires_at: string;
262
+ };
263
+ ```
264
+
265
+ ---
266
+
267
+ ## Popup behavior
268
+
269
+ The SDK opens a Human Gateway hosted flow in a centered browser popup.
270
+
271
+ This is expected behavior:
272
+
273
+ * The popup may show the browser address bar.
274
+ * The popup may be resizable or maximizable depending on the browser.
275
+ * The popup attempts to close itself after a successful flow.
276
+ * Some browsers may prevent programmatic closing; in that case, the user can close it manually.
277
+
278
+ The visible address bar is intentional and useful: it lets users confirm they are interacting with a legitimate Human Gateway domain.
279
+
280
+ ### User gesture requirement
281
+
282
+ Call `hg.verify()` or `hg.auth()` directly from a user action such as a button click.
283
+
284
+ Modern browsers may block popups if they are opened after a long asynchronous operation, delayed promise, timer, or background task. For the best success rate, start the Human Gateway flow immediately inside the user interaction handler.
285
+
286
+ Recommended:
287
+
288
+ ```ts
289
+ button.addEventListener("click", async () => {
290
+ const result = await hg.verify({
291
+ actionKey: "hg_verify_action_key_xxx",
292
+ });
293
+
294
+ console.log(result);
295
+ });
296
+ ```
297
+
298
+ Avoid delaying the popup:
299
+
300
+ ```ts
301
+ button.addEventListener("click", async () => {
302
+ await doLongAsyncWork();
303
+
304
+ const result = await hg.verify({
305
+ actionKey: "hg_verify_action_key_xxx",
306
+ });
307
+
308
+ console.log(result);
309
+ });
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Frontend result vs backend callback
315
+
316
+ The frontend SDK result is useful for immediate UI updates.
317
+
318
+ The signed backend callback should be treated as the durable source of truth.
319
+
320
+ ```txt
321
+ Frontend result -> update UI
322
+ Signed callback -> persist verified/auth state in partner backend
323
+ ```
324
+
325
+ A malicious user can manipulate frontend state. They cannot forge a valid Human Gateway callback without the callback secret.
326
+
327
+ ---
328
+
329
+ ## Backend callback verification
330
+
331
+ Human Gateway sends signed callbacks to the partner backend.
332
+
333
+ Server-side helpers are exported from `@human-gateway/sdk/server`.
334
+
335
+ You do not need to create a `HumanGateway` instance on your backend. The server helpers are pure functions designed to validate callback requests using the raw request body, signed headers, and your project callback secret.
336
+
337
+ Callback headers:
338
+
339
+ ```txt
340
+ x-hg-event-id
341
+ x-hg-event
342
+ x-hg-project-id
343
+ x-hg-timestamp
344
+ x-hg-signature
345
+ ```
346
+
347
+ Example callback payload:
348
+
349
+ ```json
350
+ {
351
+ "event_id": "callback_event_xxx",
352
+ "event": "verification.completed",
353
+ "success": true,
354
+ "verified_human": true,
355
+ "unique_for_partner": false,
356
+ "already_verified": true,
357
+ "project_id": "project_xxx",
358
+ "created_at": "2026-06-25T00:00:00.000Z"
359
+ }
360
+ ```
361
+
362
+ ---
363
+
364
+ ## Recommended callback verification
365
+
366
+ Use `verifyCallback` when you want Human Gateway to validate the signature, timestamp, required headers, JSON payload, event id, event type, and project id consistency.
367
+
368
+ ```ts
369
+ import { verifyCallback } from "@human-gateway/sdk/server";
370
+
371
+ export async function POST(request: Request) {
372
+ const body = await request.text();
373
+
374
+ try {
375
+ const callback = verifyCallback({
376
+ body,
377
+ headers: request.headers,
378
+ // Use the callback secret configured for this project in Partner Console.
379
+ // You can generate or rotate it from the project callback settings.
380
+ secret: process.env.HG_CALLBACK_SECRET!,
381
+ expectedProjectId: "project_xxx",
382
+ });
383
+
384
+ if (callback.event === "verification.completed") {
385
+ const payload = callback.payload;
386
+
387
+ if (payload.verified_human === true) {
388
+ // Persist verified state in your backend
389
+ }
390
+ }
391
+
392
+ if (callback.event === "auth.completed") {
393
+ const payload = callback.payload;
394
+
395
+ if (payload.verified_human === true) {
396
+ // Persist or reconcile auth state in your backend
397
+ }
398
+ }
399
+
400
+ return Response.json({ success: true });
401
+ } catch {
402
+ return new Response("Invalid Human Gateway callback", {
403
+ status: 401,
404
+ });
405
+ }
406
+ }
407
+ ```
408
+
409
+ ---
410
+
411
+ ## Low-level signature verification
412
+
413
+ Use `verifyCallbackSignature` only if you want to verify the signature yourself and handle payload/header validation manually.
414
+
415
+ ```ts
416
+ import { verifyCallbackSignature } from "@human-gateway/sdk/server";
417
+
418
+ export async function POST(request: Request) {
419
+ const rawBody = await request.text();
420
+
421
+ const isValid = verifyCallbackSignature({
422
+ rawBody,
423
+ timestamp: request.headers.get("x-hg-timestamp"),
424
+ signature: request.headers.get("x-hg-signature"),
425
+ // Use the callback secret configured for this project in Partner Console.
426
+ // You can generate or rotate it from the project callback settings.
427
+ secret: process.env.HG_CALLBACK_SECRET!,
428
+ });
429
+
430
+ if (!isValid) {
431
+ return new Response("Invalid Human Gateway signature", {
432
+ status: 401,
433
+ });
434
+ }
435
+
436
+ const payload = JSON.parse(rawBody);
437
+
438
+ return Response.json({
439
+ success: true,
440
+ event: payload.event,
441
+ });
442
+ }
443
+ ```
444
+
445
+ ---
446
+
447
+ ## Callback verification errors
448
+
449
+ ```ts
450
+ type HumanGatewayCallbackErrorCode =
451
+ | "callback_missing_body"
452
+ | "callback_missing_secret"
453
+ | "callback_missing_header"
454
+ | "callback_invalid_timestamp"
455
+ | "callback_expired_timestamp"
456
+ | "callback_invalid_signature_format"
457
+ | "callback_invalid_signature"
458
+ | "callback_invalid_json"
459
+ | "callback_invalid_payload"
460
+ | "callback_event_id_mismatch"
461
+ | "callback_event_type_mismatch"
462
+ | "callback_project_id_mismatch";
463
+ ```
464
+
465
+ Example:
466
+
467
+ ```ts
468
+ import {
469
+ HumanGatewayCallbackError,
470
+ verifyCallback,
471
+ } from "@human-gateway/sdk/server";
472
+
473
+ try {
474
+ const callback = verifyCallback({
475
+ body,
476
+ headers,
477
+ secret: process.env.HG_CALLBACK_SECRET!,
478
+ });
479
+ } catch (error) {
480
+ if (error instanceof HumanGatewayCallbackError) {
481
+ console.error("Invalid Human Gateway callback:", error.code);
482
+ }
483
+ }
484
+ ```
485
+
486
+ ---
487
+
488
+ ## SDK error codes
489
+
490
+ ```ts
491
+ type HumanGatewayErrorCode =
492
+ | "popup_blocked"
493
+ | "user_closed"
494
+ | "timeout"
495
+ | "invalid_message"
496
+ | "invalid_origin"
497
+ | "verification_failed"
498
+ | "auth_failed"
499
+ | "unknown_error";
500
+ ```
501
+
502
+ Common handling:
503
+
504
+ ```ts
505
+ import { HumanGateway, HumanGatewayError } from "@human-gateway/sdk";
506
+
507
+ const hg = new HumanGateway({
508
+ projectId: "project_xxx",
509
+ });
510
+
511
+ try {
512
+ const result = await hg.verify({
513
+ actionKey: "hg_verify_action_key_xxx",
514
+ });
515
+ } catch (error) {
516
+ if (error instanceof HumanGatewayError) {
517
+ if (error.code === "user_closed") {
518
+ // User cancelled the flow
519
+ }
520
+
521
+ if (error.code === "popup_blocked") {
522
+ // Ask the user to allow popups
523
+ }
524
+
525
+ if (error.code === "verification_failed") {
526
+ // The hosted verification flow did not complete successfully
527
+ }
528
+ }
529
+ }
530
+ ```
531
+
532
+ ---
533
+
534
+ ## Local development
535
+
536
+ ```ts
537
+ const hg = new HumanGateway({
538
+ projectId: "project_1a7f21ff5e261645",
539
+ environment: "development",
540
+ baseUrl: "http://localhost:3000",
541
+ });
542
+
543
+ await hg.verify({
544
+ actionKey: "hg_verify_action_key_xxx",
545
+ });
546
+
547
+ await hg.auth({
548
+ actionKey: "hg_auth_action_key_xxx",
549
+ });
550
+ ```
551
+
552
+ ---
553
+
554
+ ## Privacy-first guarantees
555
+
556
+ Human Gateway does not identify the user.
557
+
558
+ It verifies humanity inside a project/action context.
559
+
560
+ The partner may receive:
561
+
562
+ ```txt
563
+ success
564
+ verified_human
565
+ unique_for_partner
566
+ already_verified
567
+ partner_user_id
568
+ auth_session_id
569
+ project_id
570
+ expires_at
571
+ ```
572
+
573
+ The partner does not receive:
574
+
575
+ ```txt
576
+ World ID nullifier
577
+ biometric data
578
+ personal identity
579
+ World proof internals
580
+ Human Gateway internal user object
581
+ raw World ID proof data
582
+ ```
583
+
584
+ ---
585
+
586
+ ## Recommended partner verification flow
587
+
588
+ ```txt
589
+ 1. User clicks "Verify with Human Gateway"
590
+ 2. SDK opens Human Gateway popup
591
+ 3. User completes World ID proof
592
+ 4. Partner frontend receives UX result
593
+ 5. Human Gateway sends signed callback to partner backend
594
+ 6. Partner backend verifies callback
595
+ 7. Partner persists verified status
596
+ 8. Partner hides verification button
597
+ ```
598
+
599
+ ---
600
+
601
+ ## Recommended partner auth flow
602
+
603
+ ```txt
604
+ 1. User clicks "Sign in with Human Gateway"
605
+ 2. SDK opens Human Gateway Auth popup
606
+ 3. User completes World ID proof
607
+ 4. Human Gateway creates a partner-scoped auth session
608
+ 5. Partner frontend receives UX result
609
+ 6. Human Gateway sends signed callback to partner backend
610
+ 7. Partner backend verifies callback
611
+ 8. Partner grants or reconciles app access according to its own session model
612
+ ```
613
+
614
+ ---
615
+
616
+ ## Current alpha status
617
+
618
+ Implemented:
619
+
620
+ * popup verification flow
621
+ * popup auth flow
622
+ * action-specific `hg.verify({ actionKey })`
623
+ * action-specific `hg.auth({ actionKey })`
624
+ * fallback behavior without `actionKey`
625
+ * postMessage result delivery
626
+ * timeout handling
627
+ * user close detection
628
+ * duplicate verify/auth call prevention
629
+ * signed callback verification helper
630
+ * full callback validation helper
631
+ * ESM/CJS builds
632
+ * TypeScript types
633
+ * prepack build protection
634
+ * prepublish typecheck/build protection
635
+
636
+ Planned:
20
637
 
21
- This package currently exists to reserve the official namespace for future Human Gateway SDK releases.
638
+ * iframe modal mode
639
+ * redirect fallback
640
+ * React helper
641
+ * expanded usage/session events
642
+ * public package release
@@ -0,0 +1,2 @@
1
+ var t=class extends Error{constructor(e,a){super(a),this.name="HumanGatewayError",this.code=e}},n=class extends Error{constructor(e,a){super(a),this.name="HumanGatewayCallbackError",this.code=e}};export{t as a,n as b};
2
+ //# sourceMappingURL=chunk-3SSCLKQD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type HumanGatewayEnvironment =\r\n | \"production\"\r\n | \"staging\"\r\n | \"development\";\r\n\r\nexport type HumanGatewayMode =\r\n | \"auto\"\r\n | \"popup\"\r\n | \"modal\"\r\n | \"redirect\";\r\n\r\nexport type HumanGatewayOptions = {\r\n projectId: string;\r\n environment?: HumanGatewayEnvironment;\r\n mode?: HumanGatewayMode;\r\n baseUrl?: string;\r\n timeoutMs?: number;\r\n};\r\n\r\nexport type HumanGatewayVerifyOptions = {\r\n actionKey?: string;\r\n onOpen?: () => void;\r\n onClose?: () => void;\r\n onSuccess?: (result: HumanGatewayResult) => void;\r\n onError?: (error: HumanGatewayError) => void;\r\n};\r\n\r\nexport type HumanGatewayAuthOptions = {\r\n actionKey?: string;\r\n onOpen?: () => void;\r\n onClose?: () => void;\r\n onSuccess?: (result: HumanGatewayAuthResult) => void;\r\n onError?: (error: HumanGatewayError) => void;\r\n};\r\n\r\nexport type HumanGatewayResult = {\r\n success: boolean;\r\n verified_human: boolean;\r\n unique_for_partner: boolean;\r\n already_verified: boolean;\r\n project_id: string;\r\n};\r\n\r\nexport type HumanGatewayAuthResult = {\r\n success: boolean;\r\n event?: \"auth.completed\";\r\n verified_human: boolean;\r\n partner_user_id: string;\r\n auth_session_id: string;\r\n project_id: string;\r\n expires_at: string;\r\n};\r\n\r\nexport type HumanGatewayMessage = {\r\n source: \"human-gateway\";\r\n type: \"human_gateway.verification.completed\";\r\n payload: HumanGatewayResult;\r\n};\r\n\r\nexport type HumanGatewayAuthMessage = {\r\n source: \"human-gateway\";\r\n type: \"human_gateway.auth.completed\";\r\n payload: HumanGatewayAuthResult;\r\n};\r\n\r\nexport type HumanGatewayErrorCode =\r\n | \"popup_blocked\"\r\n | \"user_closed\"\r\n | \"timeout\"\r\n | \"invalid_message\"\r\n | \"invalid_origin\"\r\n | \"verification_failed\"\r\n | \"auth_failed\"\r\n | \"unknown_error\";\r\n\r\nexport class HumanGatewayError extends Error {\r\n code: HumanGatewayErrorCode;\r\n\r\n constructor(code: HumanGatewayErrorCode, message: string) {\r\n super(message);\r\n this.name = \"HumanGatewayError\";\r\n this.code = code;\r\n }\r\n}\r\n\r\nexport type HumanGatewayCallbackEventName =\r\n | \"verification.completed\"\r\n | \"auth.completed\"\r\n | \"callback.test\"\r\n | (string & {});\r\n\r\nexport type HumanGatewayCallbackPayload = Record<string, unknown> & {\r\n event_id?: unknown;\r\n event?: unknown;\r\n project_id?: unknown;\r\n};\r\n\r\nexport type HumanGatewayCallbackHeaders =\r\n | Headers\r\n | Record<string, string | string[] | null | undefined>;\r\n\r\nexport type VerifyCallbackInput = {\r\n body: string;\r\n headers: HumanGatewayCallbackHeaders;\r\n secret: string;\r\n toleranceMs?: number;\r\n expectedProjectId?: string;\r\n};\r\n\r\nexport type VerifiedHumanGatewayCallback = {\r\n event_id: string;\r\n event: HumanGatewayCallbackEventName;\r\n project_id: string;\r\n timestamp: number;\r\n payload: HumanGatewayCallbackPayload;\r\n};\r\n\r\nexport type HumanGatewayCallbackErrorCode =\r\n | \"callback_missing_body\"\r\n | \"callback_missing_secret\"\r\n | \"callback_missing_header\"\r\n | \"callback_invalid_timestamp\"\r\n | \"callback_expired_timestamp\"\r\n | \"callback_invalid_signature_format\"\r\n | \"callback_invalid_signature\"\r\n | \"callback_invalid_json\"\r\n | \"callback_invalid_payload\"\r\n | \"callback_event_id_mismatch\"\r\n | \"callback_event_type_mismatch\"\r\n | \"callback_project_id_mismatch\";\r\n\r\nexport class HumanGatewayCallbackError extends Error {\r\n code: HumanGatewayCallbackErrorCode;\r\n\r\n constructor(code: HumanGatewayCallbackErrorCode, message: string) {\r\n super(message);\r\n this.name = \"HumanGatewayCallbackError\";\r\n this.code = code;\r\n }\r\n}"],"mappings":"AA2EO,IAAMA,EAAN,cAAgC,KAAM,CAGzC,YAAYC,EAA6BC,EAAiB,CACtD,MAAMA,CAAO,EACb,KAAK,KAAO,oBACZ,KAAK,KAAOD,CAChB,CACJ,EAgDaE,EAAN,cAAwC,KAAM,CAGjD,YAAYF,EAAqCC,EAAiB,CAC9D,MAAMA,CAAO,EACb,KAAK,KAAO,4BACZ,KAAK,KAAOD,CAChB,CACJ","names":["HumanGatewayError","code","message","HumanGatewayCallbackError"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var g=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var M=(t,e)=>{for(var a in e)g(t,a,{get:e[a],enumerable:!0})},x=(t,e,a,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of b(e))!k.call(t,o)&&o!==a&&g(t,o,{get:()=>e[o],enumerable:!(r=_(e,o))||r.enumerable});return t};var E=t=>x(g({},"__esModule",{value:!0}),t);var V={};M(V,{HumanGateway:()=>w,HumanGatewayError:()=>n});module.exports=E(V);var n=class extends Error{constructor(e,a){super(a),this.name="HumanGatewayError",this.code=e}};function R(t=480,e=720){let a=typeof window<"u"?Math.min(t,Math.floor(window.innerWidth*.92)):t,r=typeof window<"u"?Math.min(e,Math.floor(window.innerHeight*.88)):e,o=typeof window<"u"?Math.max(0,window.screenX+(window.outerWidth-a)/2):0,s=typeof window<"u"?Math.max(0,window.screenY+(window.outerHeight-r)/2):0;return[`width=${a}`,`height=${r}`,`left=${o}`,`top=${s}`,"resizable=yes","scrollbars=yes","status=no","toolbar=no","menubar=no"].join(",")}function H({url:t,width:e,height:a}){if(typeof window>"u")throw new n("unknown_error","HumanGateway popup verification requires a browser environment.");let r=window.open(t,"human_gateway_verification",R(e,a));if(!r)throw new n("popup_blocked","The verification popup was blocked by the browser.");return r.focus(),r}function j(t){if(typeof t!="object"||t===null)return!1;let e=t;return e.source==="human-gateway"&&e.type==="human_gateway.verification.completed"&&typeof e.payload=="object"&&e.payload!==null}function I(t){if(typeof t!="object"||t===null)return!1;let e=t;return e.source==="human-gateway"&&e.type==="human_gateway.auth.completed"&&typeof e.payload=="object"&&e.payload!==null}function v({popup:t,expectedOrigin:e,expectedProjectId:a,timeoutMs:r,onClose:o}){return new Promise((s,p)=>{let l=!1,m=()=>{window.removeEventListener("message",d),window.clearTimeout(h),window.clearInterval(f)},y=i=>{l||(l=!0,m(),s(i))},u=i=>{l||(l=!0,m(),p(i))},d=i=>{if(i.origin!==e)return;if(!j(i.data)){u(new n("invalid_message","Received an invalid Human Gateway verification message."));return}let c=i.data.payload;if(c.project_id!==a){u(new n("invalid_message","Received a verification result for a different project."));return}if(!c.success||!c.verified_human){u(new n("verification_failed","Human Gateway verification did not complete successfully."));return}y(c)},h=window.setTimeout(()=>{u(new n("timeout","Human Gateway verification timed out."))},r),f=window.setInterval(()=>{t.closed&&(o?.(),u(new n("user_closed","The verification window was closed before completion.")))},500);window.addEventListener("message",d)})}function G({popup:t,expectedOrigin:e,expectedProjectId:a,timeoutMs:r,onClose:o}){return new Promise((s,p)=>{let l=!1,m=()=>{window.removeEventListener("message",d),window.clearTimeout(h),window.clearInterval(f)},y=i=>{l||(l=!0,m(),s(i))},u=i=>{l||(l=!0,m(),p(i))},d=i=>{if(i.origin!==e)return;if(!I(i.data)){u(new n("invalid_message","Received an invalid Human Gateway auth message."));return}let c=i.data.payload;if(c.project_id!==a){u(new n("invalid_message","Received an auth result for a different project."));return}if(!c.success||!c.verified_human||!c.partner_user_id||!c.auth_session_id){u(new n("auth_failed","Human Gateway auth did not complete successfully."));return}y(c)},h=window.setTimeout(()=>{u(new n("timeout","Human Gateway auth timed out."))},r),f=window.setInterval(()=>{t.closed&&(o?.(),u(new n("user_closed","The auth window was closed before completion.")))},500);window.addEventListener("message",d)})}var A=300*1e3;function P(t){return t==="development"?"http://localhost:3000":t==="staging"?"https://staging.humangateway.dev":"https://humangateway.dev"}function C(t){let e=new URL(`/verify/${t.projectId}`,t.baseUrl);return e.searchParams.set("sdk","1"),e.searchParams.set("origin",window.location.origin),e.searchParams.set("mode","popup"),t.actionKey&&e.searchParams.set("actionKey",t.actionKey),e.toString()}function U(t){let e=new URL(`/auth/${t.projectId}`,t.baseUrl);return e.searchParams.set("sdk","1"),e.searchParams.set("origin",window.location.origin),e.searchParams.set("mode","popup"),t.actionKey&&e.searchParams.set("actionKey",t.actionKey),e.toString()}var w=class{constructor(e){this.activeVerification=null;this.activeAuth=null;if(!e.projectId)throw new n("unknown_error","HumanGateway requires a projectId.");this.projectId=e.projectId,this.environment=e.environment??"production",this.mode=e.mode??"auto",this.baseUrl=e.baseUrl??P(this.environment),this.timeoutMs=e.timeoutMs??A}async verify(e={}){return this.activeVerification?this.activeVerification:(this.activeVerification=this.runVerification(e).finally(()=>{this.activeVerification=null}),this.activeVerification)}async auth(e={}){return this.activeAuth?this.activeAuth:(this.activeAuth=this.runAuth(e).finally(()=>{this.activeAuth=null}),this.activeAuth)}async runVerification(e){try{if(typeof window>"u")throw new n("unknown_error","HumanGateway.verify() requires a browser environment.");if(this.mode==="redirect")throw new n("unknown_error","Redirect mode is not implemented in this SDK alpha.");let a=C({baseUrl:this.baseUrl,projectId:this.projectId,...e.actionKey?{actionKey:e.actionKey}:{}}),r=new URL(this.baseUrl).origin,o=H({url:a});e.onOpen?.();let s=await v({popup:o,expectedOrigin:r,expectedProjectId:this.projectId,timeoutMs:this.timeoutMs,...e.onClose?{onClose:e.onClose}:{}});e.onSuccess?.(s);try{o.close()}catch{}return s}catch(a){let r=a instanceof n?a:new n("unknown_error",a instanceof Error?a.message:"Unknown Human Gateway SDK error.");throw e.onError?.(r),r}}async runAuth(e){try{if(typeof window>"u")throw new n("unknown_error","HumanGateway.auth() requires a browser environment.");if(this.mode==="redirect")throw new n("unknown_error","Redirect mode is not implemented in this SDK alpha.");let a=U({baseUrl:this.baseUrl,projectId:this.projectId,...e.actionKey?{actionKey:e.actionKey}:{}}),r=new URL(this.baseUrl).origin,o=H({url:a});e.onOpen?.();let s=await G({popup:o,expectedOrigin:r,expectedProjectId:this.projectId,timeoutMs:this.timeoutMs,...e.onClose?{onClose:e.onClose}:{}});e.onSuccess?.(s);try{o.close()}catch{}return s}catch(a){let r=a instanceof n?a:new n("unknown_error",a instanceof Error?a.message:"Unknown Human Gateway SDK auth error.");throw e.onError?.(r),r}}};0&&(module.exports={HumanGateway,HumanGatewayError});
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/popup.ts","../src/post-message.ts","../src/client.ts"],"sourcesContent":["export { HumanGateway } from \"./client\";\r\nexport type {\r\n HumanGatewayAuthMessage,\r\n HumanGatewayAuthOptions,\r\n HumanGatewayAuthResult,\r\n HumanGatewayEnvironment,\r\n HumanGatewayErrorCode,\r\n HumanGatewayMessage,\r\n HumanGatewayMode,\r\n HumanGatewayOptions,\r\n HumanGatewayResult,\r\n HumanGatewayVerifyOptions,\r\n} from \"./types\";\r\nexport { HumanGatewayError } from \"./types\";","export type HumanGatewayEnvironment =\r\n | \"production\"\r\n | \"staging\"\r\n | \"development\";\r\n\r\nexport type HumanGatewayMode =\r\n | \"auto\"\r\n | \"popup\"\r\n | \"modal\"\r\n | \"redirect\";\r\n\r\nexport type HumanGatewayOptions = {\r\n projectId: string;\r\n environment?: HumanGatewayEnvironment;\r\n mode?: HumanGatewayMode;\r\n baseUrl?: string;\r\n timeoutMs?: number;\r\n};\r\n\r\nexport type HumanGatewayVerifyOptions = {\r\n actionKey?: string;\r\n onOpen?: () => void;\r\n onClose?: () => void;\r\n onSuccess?: (result: HumanGatewayResult) => void;\r\n onError?: (error: HumanGatewayError) => void;\r\n};\r\n\r\nexport type HumanGatewayAuthOptions = {\r\n actionKey?: string;\r\n onOpen?: () => void;\r\n onClose?: () => void;\r\n onSuccess?: (result: HumanGatewayAuthResult) => void;\r\n onError?: (error: HumanGatewayError) => void;\r\n};\r\n\r\nexport type HumanGatewayResult = {\r\n success: boolean;\r\n verified_human: boolean;\r\n unique_for_partner: boolean;\r\n already_verified: boolean;\r\n project_id: string;\r\n};\r\n\r\nexport type HumanGatewayAuthResult = {\r\n success: boolean;\r\n event?: \"auth.completed\";\r\n verified_human: boolean;\r\n partner_user_id: string;\r\n auth_session_id: string;\r\n project_id: string;\r\n expires_at: string;\r\n};\r\n\r\nexport type HumanGatewayMessage = {\r\n source: \"human-gateway\";\r\n type: \"human_gateway.verification.completed\";\r\n payload: HumanGatewayResult;\r\n};\r\n\r\nexport type HumanGatewayAuthMessage = {\r\n source: \"human-gateway\";\r\n type: \"human_gateway.auth.completed\";\r\n payload: HumanGatewayAuthResult;\r\n};\r\n\r\nexport type HumanGatewayErrorCode =\r\n | \"popup_blocked\"\r\n | \"user_closed\"\r\n | \"timeout\"\r\n | \"invalid_message\"\r\n | \"invalid_origin\"\r\n | \"verification_failed\"\r\n | \"auth_failed\"\r\n | \"unknown_error\";\r\n\r\nexport class HumanGatewayError extends Error {\r\n code: HumanGatewayErrorCode;\r\n\r\n constructor(code: HumanGatewayErrorCode, message: string) {\r\n super(message);\r\n this.name = \"HumanGatewayError\";\r\n this.code = code;\r\n }\r\n}\r\n\r\nexport type HumanGatewayCallbackEventName =\r\n | \"verification.completed\"\r\n | \"auth.completed\"\r\n | \"callback.test\"\r\n | (string & {});\r\n\r\nexport type HumanGatewayCallbackPayload = Record<string, unknown> & {\r\n event_id?: unknown;\r\n event?: unknown;\r\n project_id?: unknown;\r\n};\r\n\r\nexport type HumanGatewayCallbackHeaders =\r\n | Headers\r\n | Record<string, string | string[] | null | undefined>;\r\n\r\nexport type VerifyCallbackInput = {\r\n body: string;\r\n headers: HumanGatewayCallbackHeaders;\r\n secret: string;\r\n toleranceMs?: number;\r\n expectedProjectId?: string;\r\n};\r\n\r\nexport type VerifiedHumanGatewayCallback = {\r\n event_id: string;\r\n event: HumanGatewayCallbackEventName;\r\n project_id: string;\r\n timestamp: number;\r\n payload: HumanGatewayCallbackPayload;\r\n};\r\n\r\nexport type HumanGatewayCallbackErrorCode =\r\n | \"callback_missing_body\"\r\n | \"callback_missing_secret\"\r\n | \"callback_missing_header\"\r\n | \"callback_invalid_timestamp\"\r\n | \"callback_expired_timestamp\"\r\n | \"callback_invalid_signature_format\"\r\n | \"callback_invalid_signature\"\r\n | \"callback_invalid_json\"\r\n | \"callback_invalid_payload\"\r\n | \"callback_event_id_mismatch\"\r\n | \"callback_event_type_mismatch\"\r\n | \"callback_project_id_mismatch\";\r\n\r\nexport class HumanGatewayCallbackError extends Error {\r\n code: HumanGatewayCallbackErrorCode;\r\n\r\n constructor(code: HumanGatewayCallbackErrorCode, message: string) {\r\n super(message);\r\n this.name = \"HumanGatewayCallbackError\";\r\n this.code = code;\r\n }\r\n}","import { HumanGatewayError } from \"./types\";\r\n\r\ntype OpenPopupInput = {\r\n url: string;\r\n width?: number;\r\n height?: number;\r\n};\r\n\r\nexport function getPopupFeatures(width = 480, height = 720): string {\r\n const safeWidth =\r\n typeof window !== \"undefined\"\r\n ? Math.min(width, Math.floor(window.innerWidth * 0.92))\r\n : width;\r\n\r\n const safeHeight =\r\n typeof window !== \"undefined\"\r\n ? Math.min(height, Math.floor(window.innerHeight * 0.88))\r\n : height;\r\n\r\n const left =\r\n typeof window !== \"undefined\"\r\n ? Math.max(0, window.screenX + (window.outerWidth - safeWidth) / 2)\r\n : 0;\r\n\r\n const top =\r\n typeof window !== \"undefined\"\r\n ? Math.max(0, window.screenY + (window.outerHeight - safeHeight) / 2)\r\n : 0;\r\n\r\n return [\r\n `width=${safeWidth}`,\r\n `height=${safeHeight}`,\r\n `left=${left}`,\r\n `top=${top}`,\r\n \"resizable=yes\",\r\n \"scrollbars=yes\",\r\n \"status=no\",\r\n \"toolbar=no\",\r\n \"menubar=no\",\r\n ].join(\",\");\r\n}\r\n\r\nexport function openVerificationPopup({\r\n url,\r\n width,\r\n height,\r\n}: OpenPopupInput): Window {\r\n if (typeof window === \"undefined\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"HumanGateway popup verification requires a browser environment.\"\r\n );\r\n }\r\n\r\n const popup = window.open(\r\n url,\r\n \"human_gateway_verification\",\r\n getPopupFeatures(width, height)\r\n );\r\n\r\n if (!popup) {\r\n throw new HumanGatewayError(\r\n \"popup_blocked\",\r\n \"The verification popup was blocked by the browser.\"\r\n );\r\n }\r\n\r\n popup.focus();\r\n\r\n return popup;\r\n}","import {\r\n HumanGatewayAuthMessage,\r\n HumanGatewayAuthResult,\r\n HumanGatewayError,\r\n HumanGatewayMessage,\r\n HumanGatewayResult,\r\n} from \"./types\";\r\n\r\ntype WaitForMessageInput = {\r\n popup: Window;\r\n expectedOrigin: string;\r\n expectedProjectId: string;\r\n timeoutMs: number;\r\n onClose?: () => void;\r\n};\r\n\r\nfunction isHumanGatewayVerificationMessage(\r\n value: unknown\r\n): value is HumanGatewayMessage {\r\n if (typeof value !== \"object\" || value === null) {\r\n return false;\r\n }\r\n\r\n const message = value as Partial<HumanGatewayMessage>;\r\n\r\n return (\r\n message.source === \"human-gateway\" &&\r\n message.type === \"human_gateway.verification.completed\" &&\r\n typeof message.payload === \"object\" &&\r\n message.payload !== null\r\n );\r\n}\r\n\r\nfunction isHumanGatewayAuthMessage(\r\n value: unknown\r\n): value is HumanGatewayAuthMessage {\r\n if (typeof value !== \"object\" || value === null) {\r\n return false;\r\n }\r\n\r\n const message = value as Partial<HumanGatewayAuthMessage>;\r\n\r\n return (\r\n message.source === \"human-gateway\" &&\r\n message.type === \"human_gateway.auth.completed\" &&\r\n typeof message.payload === \"object\" &&\r\n message.payload !== null\r\n );\r\n}\r\n\r\nexport function waitForVerificationMessage({\r\n popup,\r\n expectedOrigin,\r\n expectedProjectId,\r\n timeoutMs,\r\n onClose,\r\n}: WaitForMessageInput): Promise<HumanGatewayResult> {\r\n return new Promise((resolve, reject) => {\r\n let settled = false;\r\n\r\n const cleanup = () => {\r\n window.removeEventListener(\"message\", onMessage);\r\n window.clearTimeout(timeoutId);\r\n window.clearInterval(closeCheckId);\r\n };\r\n\r\n const settleResolve = (result: HumanGatewayResult) => {\r\n if (settled) return;\r\n\r\n settled = true;\r\n cleanup();\r\n resolve(result);\r\n };\r\n\r\n const settleReject = (error: HumanGatewayError) => {\r\n if (settled) return;\r\n\r\n settled = true;\r\n cleanup();\r\n reject(error);\r\n };\r\n\r\n const onMessage = (event: MessageEvent) => {\r\n if (event.origin !== expectedOrigin) {\r\n return;\r\n }\r\n\r\n if (!isHumanGatewayVerificationMessage(event.data)) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"invalid_message\",\r\n \"Received an invalid Human Gateway verification message.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n const result = event.data.payload;\r\n\r\n if (result.project_id !== expectedProjectId) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"invalid_message\",\r\n \"Received a verification result for a different project.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n if (!result.success || !result.verified_human) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"verification_failed\",\r\n \"Human Gateway verification did not complete successfully.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n settleResolve(result);\r\n };\r\n\r\n const timeoutId = window.setTimeout(() => {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"timeout\",\r\n \"Human Gateway verification timed out.\"\r\n )\r\n );\r\n }, timeoutMs);\r\n\r\n const closeCheckId = window.setInterval(() => {\r\n if (popup.closed) {\r\n onClose?.();\r\n\r\n settleReject(\r\n new HumanGatewayError(\r\n \"user_closed\",\r\n \"The verification window was closed before completion.\"\r\n )\r\n );\r\n }\r\n }, 500);\r\n\r\n window.addEventListener(\"message\", onMessage);\r\n });\r\n}\r\n\r\nexport function waitForAuthMessage({\r\n popup,\r\n expectedOrigin,\r\n expectedProjectId,\r\n timeoutMs,\r\n onClose,\r\n}: WaitForMessageInput): Promise<HumanGatewayAuthResult> {\r\n return new Promise((resolve, reject) => {\r\n let settled = false;\r\n\r\n const cleanup = () => {\r\n window.removeEventListener(\"message\", onMessage);\r\n window.clearTimeout(timeoutId);\r\n window.clearInterval(closeCheckId);\r\n };\r\n\r\n const settleResolve = (result: HumanGatewayAuthResult) => {\r\n if (settled) return;\r\n\r\n settled = true;\r\n cleanup();\r\n resolve(result);\r\n };\r\n\r\n const settleReject = (error: HumanGatewayError) => {\r\n if (settled) return;\r\n\r\n settled = true;\r\n cleanup();\r\n reject(error);\r\n };\r\n\r\n const onMessage = (event: MessageEvent) => {\r\n if (event.origin !== expectedOrigin) {\r\n return;\r\n }\r\n\r\n if (!isHumanGatewayAuthMessage(event.data)) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"invalid_message\",\r\n \"Received an invalid Human Gateway auth message.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n const result = event.data.payload;\r\n\r\n if (result.project_id !== expectedProjectId) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"invalid_message\",\r\n \"Received an auth result for a different project.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n if (\r\n !result.success ||\r\n !result.verified_human ||\r\n !result.partner_user_id ||\r\n !result.auth_session_id\r\n ) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"auth_failed\",\r\n \"Human Gateway auth did not complete successfully.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n settleResolve(result);\r\n };\r\n\r\n const timeoutId = window.setTimeout(() => {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"timeout\",\r\n \"Human Gateway auth timed out.\"\r\n )\r\n );\r\n }, timeoutMs);\r\n\r\n const closeCheckId = window.setInterval(() => {\r\n if (popup.closed) {\r\n onClose?.();\r\n\r\n settleReject(\r\n new HumanGatewayError(\r\n \"user_closed\",\r\n \"The auth window was closed before completion.\"\r\n )\r\n );\r\n }\r\n }, 500);\r\n\r\n window.addEventListener(\"message\", onMessage);\r\n });\r\n}","import { openVerificationPopup } from \"./popup\";\r\nimport {\r\n waitForAuthMessage,\r\n waitForVerificationMessage,\r\n} from \"./post-message\";\r\nimport {\r\n HumanGatewayAuthOptions,\r\n HumanGatewayAuthResult,\r\n HumanGatewayError,\r\n HumanGatewayOptions,\r\n HumanGatewayResult,\r\n HumanGatewayVerifyOptions,\r\n} from \"./types\";\r\n\r\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;\r\n\r\nfunction getDefaultBaseUrl(environment: HumanGatewayOptions[\"environment\"]) {\r\n if (environment === \"development\") {\r\n return \"http://localhost:3000\";\r\n }\r\n\r\n if (environment === \"staging\") {\r\n return \"https://staging.humangateway.dev\";\r\n }\r\n\r\n return \"https://humangateway.dev\";\r\n}\r\n\r\nfunction buildVerificationUrl(input: {\r\n baseUrl: string;\r\n projectId: string;\r\n actionKey?: string;\r\n}): string {\r\n const url = new URL(`/verify/${input.projectId}`, input.baseUrl);\r\n\r\n url.searchParams.set(\"sdk\", \"1\");\r\n url.searchParams.set(\"origin\", window.location.origin);\r\n url.searchParams.set(\"mode\", \"popup\");\r\n\r\n if (input.actionKey) {\r\n url.searchParams.set(\"actionKey\", input.actionKey);\r\n }\r\n\r\n return url.toString();\r\n}\r\n\r\nfunction buildAuthUrl(input: {\r\n baseUrl: string;\r\n projectId: string;\r\n actionKey?: string;\r\n}): string {\r\n const url = new URL(`/auth/${input.projectId}`, input.baseUrl);\r\n\r\n url.searchParams.set(\"sdk\", \"1\");\r\n url.searchParams.set(\"origin\", window.location.origin);\r\n url.searchParams.set(\"mode\", \"popup\");\r\n\r\n if (input.actionKey) {\r\n url.searchParams.set(\"actionKey\", input.actionKey);\r\n }\r\n\r\n return url.toString();\r\n}\r\n\r\nexport class HumanGateway {\r\n private readonly projectId: string;\r\n private readonly environment: NonNullable<HumanGatewayOptions[\"environment\"]>;\r\n private readonly mode: NonNullable<HumanGatewayOptions[\"mode\"]>;\r\n private readonly baseUrl: string;\r\n private readonly timeoutMs: number;\r\n private activeVerification: Promise<HumanGatewayResult> | null = null;\r\n private activeAuth: Promise<HumanGatewayAuthResult> | null = null;\r\n\r\n constructor(options: HumanGatewayOptions) {\r\n if (!options.projectId) {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"HumanGateway requires a projectId.\"\r\n );\r\n }\r\n\r\n this.projectId = options.projectId;\r\n this.environment = options.environment ?? \"production\";\r\n this.mode = options.mode ?? \"auto\";\r\n this.baseUrl = options.baseUrl ?? getDefaultBaseUrl(this.environment);\r\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\r\n }\r\n\r\n async verify(\r\n verifyOptions: HumanGatewayVerifyOptions = {}\r\n ): Promise<HumanGatewayResult> {\r\n if (this.activeVerification) {\r\n return this.activeVerification;\r\n }\r\n\r\n this.activeVerification = this.runVerification(verifyOptions).finally(() => {\r\n this.activeVerification = null;\r\n });\r\n\r\n return this.activeVerification;\r\n }\r\n\r\n async auth(\r\n authOptions: HumanGatewayAuthOptions = {}\r\n ): Promise<HumanGatewayAuthResult> {\r\n if (this.activeAuth) {\r\n return this.activeAuth;\r\n }\r\n\r\n this.activeAuth = this.runAuth(authOptions).finally(() => {\r\n this.activeAuth = null;\r\n });\r\n\r\n return this.activeAuth;\r\n }\r\n\r\n private async runVerification(\r\n verifyOptions: HumanGatewayVerifyOptions\r\n ): Promise<HumanGatewayResult> {\r\n try {\r\n if (typeof window === \"undefined\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"HumanGateway.verify() requires a browser environment.\"\r\n );\r\n }\r\n\r\n if (this.mode === \"redirect\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"Redirect mode is not implemented in this SDK alpha.\"\r\n );\r\n }\r\n\r\n const verificationUrl = buildVerificationUrl({\r\n baseUrl: this.baseUrl,\r\n projectId: this.projectId,\r\n ...(verifyOptions.actionKey\r\n ? { actionKey: verifyOptions.actionKey }\r\n : {}),\r\n });\r\n\r\n const expectedOrigin = new URL(this.baseUrl).origin;\r\n\r\n const popup = openVerificationPopup({\r\n url: verificationUrl,\r\n });\r\n\r\n verifyOptions.onOpen?.();\r\n\r\n const result = await waitForVerificationMessage({\r\n popup,\r\n expectedOrigin,\r\n expectedProjectId: this.projectId,\r\n timeoutMs: this.timeoutMs,\r\n ...(verifyOptions.onClose\r\n ? { onClose: verifyOptions.onClose }\r\n : {}),\r\n });\r\n\r\n verifyOptions.onSuccess?.(result);\r\n\r\n try {\r\n popup.close();\r\n } catch {\r\n // Ignore close errors. Some browsers may block programmatic close.\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n const normalizedError =\r\n error instanceof HumanGatewayError\r\n ? error\r\n : new HumanGatewayError(\r\n \"unknown_error\",\r\n error instanceof Error\r\n ? error.message\r\n : \"Unknown Human Gateway SDK error.\"\r\n );\r\n\r\n verifyOptions.onError?.(normalizedError);\r\n\r\n throw normalizedError;\r\n }\r\n }\r\n\r\n private async runAuth(\r\n authOptions: HumanGatewayAuthOptions\r\n ): Promise<HumanGatewayAuthResult> {\r\n try {\r\n if (typeof window === \"undefined\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"HumanGateway.auth() requires a browser environment.\"\r\n );\r\n }\r\n\r\n if (this.mode === \"redirect\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"Redirect mode is not implemented in this SDK alpha.\"\r\n );\r\n }\r\n\r\n const authUrl = buildAuthUrl({\r\n baseUrl: this.baseUrl,\r\n projectId: this.projectId,\r\n ...(authOptions.actionKey ? { actionKey: authOptions.actionKey } : {}),\r\n });\r\n\r\n const expectedOrigin = new URL(this.baseUrl).origin;\r\n\r\n const popup = openVerificationPopup({\r\n url: authUrl,\r\n });\r\n\r\n authOptions.onOpen?.();\r\n\r\n const result = await waitForAuthMessage({\r\n popup,\r\n expectedOrigin,\r\n expectedProjectId: this.projectId,\r\n timeoutMs: this.timeoutMs,\r\n ...(authOptions.onClose\r\n ? { onClose: authOptions.onClose }\r\n : {}),\r\n });\r\n\r\n authOptions.onSuccess?.(result);\r\n\r\n try {\r\n popup.close();\r\n } catch {\r\n // Ignore close errors. Some browsers may block programmatic close.\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n const normalizedError =\r\n error instanceof HumanGatewayError\r\n ? error\r\n : new HumanGatewayError(\r\n \"unknown_error\",\r\n error instanceof Error\r\n ? error.message\r\n : \"Unknown Human Gateway SDK auth error.\"\r\n );\r\n\r\n authOptions.onError?.(normalizedError);\r\n\r\n throw normalizedError;\r\n }\r\n }\r\n}"],"mappings":"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","names":["src_exports","__export","HumanGateway","HumanGatewayError","__toCommonJS","HumanGatewayError","code","message","getPopupFeatures","width","height","safeWidth","safeHeight","left","top","openVerificationPopup","url","HumanGatewayError","popup","isHumanGatewayVerificationMessage","value","message","isHumanGatewayAuthMessage","waitForVerificationMessage","popup","expectedOrigin","expectedProjectId","timeoutMs","onClose","resolve","reject","settled","cleanup","onMessage","timeoutId","closeCheckId","settleResolve","result","settleReject","error","event","HumanGatewayError","waitForAuthMessage","DEFAULT_TIMEOUT_MS","getDefaultBaseUrl","environment","buildVerificationUrl","input","url","buildAuthUrl","HumanGateway","options","HumanGatewayError","verifyOptions","authOptions","verificationUrl","expectedOrigin","popup","openVerificationPopup","result","waitForVerificationMessage","error","normalizedError","authUrl","waitForAuthMessage"]}
@@ -0,0 +1,19 @@
1
+ import { m as HumanGatewayOptions, o as HumanGatewayVerifyOptions, n as HumanGatewayResult, a as HumanGatewayAuthOptions, b as HumanGatewayAuthResult } from './types-D8bnQbLL.cjs';
2
+ export { H as HumanGatewayAuthMessage, h as HumanGatewayEnvironment, i as HumanGatewayError, j as HumanGatewayErrorCode, k as HumanGatewayMessage, l as HumanGatewayMode } from './types-D8bnQbLL.cjs';
3
+
4
+ declare class HumanGateway {
5
+ private readonly projectId;
6
+ private readonly environment;
7
+ private readonly mode;
8
+ private readonly baseUrl;
9
+ private readonly timeoutMs;
10
+ private activeVerification;
11
+ private activeAuth;
12
+ constructor(options: HumanGatewayOptions);
13
+ verify(verifyOptions?: HumanGatewayVerifyOptions): Promise<HumanGatewayResult>;
14
+ auth(authOptions?: HumanGatewayAuthOptions): Promise<HumanGatewayAuthResult>;
15
+ private runVerification;
16
+ private runAuth;
17
+ }
18
+
19
+ export { HumanGateway, HumanGatewayAuthOptions, HumanGatewayAuthResult, HumanGatewayOptions, HumanGatewayResult, HumanGatewayVerifyOptions };
@@ -0,0 +1,19 @@
1
+ import { m as HumanGatewayOptions, o as HumanGatewayVerifyOptions, n as HumanGatewayResult, a as HumanGatewayAuthOptions, b as HumanGatewayAuthResult } from './types-D8bnQbLL.js';
2
+ export { H as HumanGatewayAuthMessage, h as HumanGatewayEnvironment, i as HumanGatewayError, j as HumanGatewayErrorCode, k as HumanGatewayMessage, l as HumanGatewayMode } from './types-D8bnQbLL.js';
3
+
4
+ declare class HumanGateway {
5
+ private readonly projectId;
6
+ private readonly environment;
7
+ private readonly mode;
8
+ private readonly baseUrl;
9
+ private readonly timeoutMs;
10
+ private activeVerification;
11
+ private activeAuth;
12
+ constructor(options: HumanGatewayOptions);
13
+ verify(verifyOptions?: HumanGatewayVerifyOptions): Promise<HumanGatewayResult>;
14
+ auth(authOptions?: HumanGatewayAuthOptions): Promise<HumanGatewayAuthResult>;
15
+ private runVerification;
16
+ private runAuth;
17
+ }
18
+
19
+ export { HumanGateway, HumanGatewayAuthOptions, HumanGatewayAuthResult, HumanGatewayOptions, HumanGatewayResult, HumanGatewayVerifyOptions };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import{a}from"./chunk-3SSCLKQD.js";function G(t=480,e=720){let n=typeof window<"u"?Math.min(t,Math.floor(window.innerWidth*.92)):t,r=typeof window<"u"?Math.min(e,Math.floor(window.innerHeight*.88)):e,i=typeof window<"u"?Math.max(0,window.screenX+(window.outerWidth-n)/2):0,s=typeof window<"u"?Math.max(0,window.screenY+(window.outerHeight-r)/2):0;return[`width=${n}`,`height=${r}`,`left=${i}`,`top=${s}`,"resizable=yes","scrollbars=yes","status=no","toolbar=no","menubar=no"].join(",")}function f({url:t,width:e,height:n}){if(typeof window>"u")throw new a("unknown_error","HumanGateway popup verification requires a browser environment.");let r=window.open(t,"human_gateway_verification",G(e,n));if(!r)throw new a("popup_blocked","The verification popup was blocked by the browser.");return r.focus(),r}function b(t){if(typeof t!="object"||t===null)return!1;let e=t;return e.source==="human-gateway"&&e.type==="human_gateway.verification.completed"&&typeof e.payload=="object"&&e.payload!==null}function M(t){if(typeof t!="object"||t===null)return!1;let e=t;return e.source==="human-gateway"&&e.type==="human_gateway.auth.completed"&&typeof e.payload=="object"&&e.payload!==null}function H({popup:t,expectedOrigin:e,expectedProjectId:n,timeoutMs:r,onClose:i}){return new Promise((s,d)=>{let w=!1,m=()=>{window.removeEventListener("message",l),window.clearTimeout(y),window.clearInterval(h)},p=o=>{w||(w=!0,m(),s(o))},u=o=>{w||(w=!0,m(),d(o))},l=o=>{if(o.origin!==e)return;if(!b(o.data)){u(new a("invalid_message","Received an invalid Human Gateway verification message."));return}let c=o.data.payload;if(c.project_id!==n){u(new a("invalid_message","Received a verification result for a different project."));return}if(!c.success||!c.verified_human){u(new a("verification_failed","Human Gateway verification did not complete successfully."));return}p(c)},y=window.setTimeout(()=>{u(new a("timeout","Human Gateway verification timed out."))},r),h=window.setInterval(()=>{t.closed&&(i?.(),u(new a("user_closed","The verification window was closed before completion.")))},500);window.addEventListener("message",l)})}function v({popup:t,expectedOrigin:e,expectedProjectId:n,timeoutMs:r,onClose:i}){return new Promise((s,d)=>{let w=!1,m=()=>{window.removeEventListener("message",l),window.clearTimeout(y),window.clearInterval(h)},p=o=>{w||(w=!0,m(),s(o))},u=o=>{w||(w=!0,m(),d(o))},l=o=>{if(o.origin!==e)return;if(!M(o.data)){u(new a("invalid_message","Received an invalid Human Gateway auth message."));return}let c=o.data.payload;if(c.project_id!==n){u(new a("invalid_message","Received an auth result for a different project."));return}if(!c.success||!c.verified_human||!c.partner_user_id||!c.auth_session_id){u(new a("auth_failed","Human Gateway auth did not complete successfully."));return}p(c)},y=window.setTimeout(()=>{u(new a("timeout","Human Gateway auth timed out."))},r),h=window.setInterval(()=>{t.closed&&(i?.(),u(new a("user_closed","The auth window was closed before completion.")))},500);window.addEventListener("message",l)})}var I=300*1e3;function _(t){return t==="development"?"http://localhost:3000":t==="staging"?"https://staging.humangateway.dev":"https://humangateway.dev"}function R(t){let e=new URL(`/verify/${t.projectId}`,t.baseUrl);return e.searchParams.set("sdk","1"),e.searchParams.set("origin",window.location.origin),e.searchParams.set("mode","popup"),t.actionKey&&e.searchParams.set("actionKey",t.actionKey),e.toString()}function j(t){let e=new URL(`/auth/${t.projectId}`,t.baseUrl);return e.searchParams.set("sdk","1"),e.searchParams.set("origin",window.location.origin),e.searchParams.set("mode","popup"),t.actionKey&&e.searchParams.set("actionKey",t.actionKey),e.toString()}var g=class{constructor(e){this.activeVerification=null;this.activeAuth=null;if(!e.projectId)throw new a("unknown_error","HumanGateway requires a projectId.");this.projectId=e.projectId,this.environment=e.environment??"production",this.mode=e.mode??"auto",this.baseUrl=e.baseUrl??_(this.environment),this.timeoutMs=e.timeoutMs??I}async verify(e={}){return this.activeVerification?this.activeVerification:(this.activeVerification=this.runVerification(e).finally(()=>{this.activeVerification=null}),this.activeVerification)}async auth(e={}){return this.activeAuth?this.activeAuth:(this.activeAuth=this.runAuth(e).finally(()=>{this.activeAuth=null}),this.activeAuth)}async runVerification(e){try{if(typeof window>"u")throw new a("unknown_error","HumanGateway.verify() requires a browser environment.");if(this.mode==="redirect")throw new a("unknown_error","Redirect mode is not implemented in this SDK alpha.");let n=R({baseUrl:this.baseUrl,projectId:this.projectId,...e.actionKey?{actionKey:e.actionKey}:{}}),r=new URL(this.baseUrl).origin,i=f({url:n});e.onOpen?.();let s=await H({popup:i,expectedOrigin:r,expectedProjectId:this.projectId,timeoutMs:this.timeoutMs,...e.onClose?{onClose:e.onClose}:{}});e.onSuccess?.(s);try{i.close()}catch{}return s}catch(n){let r=n instanceof a?n:new a("unknown_error",n instanceof Error?n.message:"Unknown Human Gateway SDK error.");throw e.onError?.(r),r}}async runAuth(e){try{if(typeof window>"u")throw new a("unknown_error","HumanGateway.auth() requires a browser environment.");if(this.mode==="redirect")throw new a("unknown_error","Redirect mode is not implemented in this SDK alpha.");let n=j({baseUrl:this.baseUrl,projectId:this.projectId,...e.actionKey?{actionKey:e.actionKey}:{}}),r=new URL(this.baseUrl).origin,i=f({url:n});e.onOpen?.();let s=await v({popup:i,expectedOrigin:r,expectedProjectId:this.projectId,timeoutMs:this.timeoutMs,...e.onClose?{onClose:e.onClose}:{}});e.onSuccess?.(s);try{i.close()}catch{}return s}catch(n){let r=n instanceof a?n:new a("unknown_error",n instanceof Error?n.message:"Unknown Human Gateway SDK auth error.");throw e.onError?.(r),r}}};export{g as HumanGateway,a as HumanGatewayError};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/popup.ts","../src/post-message.ts","../src/client.ts"],"sourcesContent":["import { HumanGatewayError } from \"./types\";\r\n\r\ntype OpenPopupInput = {\r\n url: string;\r\n width?: number;\r\n height?: number;\r\n};\r\n\r\nexport function getPopupFeatures(width = 480, height = 720): string {\r\n const safeWidth =\r\n typeof window !== \"undefined\"\r\n ? Math.min(width, Math.floor(window.innerWidth * 0.92))\r\n : width;\r\n\r\n const safeHeight =\r\n typeof window !== \"undefined\"\r\n ? Math.min(height, Math.floor(window.innerHeight * 0.88))\r\n : height;\r\n\r\n const left =\r\n typeof window !== \"undefined\"\r\n ? Math.max(0, window.screenX + (window.outerWidth - safeWidth) / 2)\r\n : 0;\r\n\r\n const top =\r\n typeof window !== \"undefined\"\r\n ? Math.max(0, window.screenY + (window.outerHeight - safeHeight) / 2)\r\n : 0;\r\n\r\n return [\r\n `width=${safeWidth}`,\r\n `height=${safeHeight}`,\r\n `left=${left}`,\r\n `top=${top}`,\r\n \"resizable=yes\",\r\n \"scrollbars=yes\",\r\n \"status=no\",\r\n \"toolbar=no\",\r\n \"menubar=no\",\r\n ].join(\",\");\r\n}\r\n\r\nexport function openVerificationPopup({\r\n url,\r\n width,\r\n height,\r\n}: OpenPopupInput): Window {\r\n if (typeof window === \"undefined\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"HumanGateway popup verification requires a browser environment.\"\r\n );\r\n }\r\n\r\n const popup = window.open(\r\n url,\r\n \"human_gateway_verification\",\r\n getPopupFeatures(width, height)\r\n );\r\n\r\n if (!popup) {\r\n throw new HumanGatewayError(\r\n \"popup_blocked\",\r\n \"The verification popup was blocked by the browser.\"\r\n );\r\n }\r\n\r\n popup.focus();\r\n\r\n return popup;\r\n}","import {\r\n HumanGatewayAuthMessage,\r\n HumanGatewayAuthResult,\r\n HumanGatewayError,\r\n HumanGatewayMessage,\r\n HumanGatewayResult,\r\n} from \"./types\";\r\n\r\ntype WaitForMessageInput = {\r\n popup: Window;\r\n expectedOrigin: string;\r\n expectedProjectId: string;\r\n timeoutMs: number;\r\n onClose?: () => void;\r\n};\r\n\r\nfunction isHumanGatewayVerificationMessage(\r\n value: unknown\r\n): value is HumanGatewayMessage {\r\n if (typeof value !== \"object\" || value === null) {\r\n return false;\r\n }\r\n\r\n const message = value as Partial<HumanGatewayMessage>;\r\n\r\n return (\r\n message.source === \"human-gateway\" &&\r\n message.type === \"human_gateway.verification.completed\" &&\r\n typeof message.payload === \"object\" &&\r\n message.payload !== null\r\n );\r\n}\r\n\r\nfunction isHumanGatewayAuthMessage(\r\n value: unknown\r\n): value is HumanGatewayAuthMessage {\r\n if (typeof value !== \"object\" || value === null) {\r\n return false;\r\n }\r\n\r\n const message = value as Partial<HumanGatewayAuthMessage>;\r\n\r\n return (\r\n message.source === \"human-gateway\" &&\r\n message.type === \"human_gateway.auth.completed\" &&\r\n typeof message.payload === \"object\" &&\r\n message.payload !== null\r\n );\r\n}\r\n\r\nexport function waitForVerificationMessage({\r\n popup,\r\n expectedOrigin,\r\n expectedProjectId,\r\n timeoutMs,\r\n onClose,\r\n}: WaitForMessageInput): Promise<HumanGatewayResult> {\r\n return new Promise((resolve, reject) => {\r\n let settled = false;\r\n\r\n const cleanup = () => {\r\n window.removeEventListener(\"message\", onMessage);\r\n window.clearTimeout(timeoutId);\r\n window.clearInterval(closeCheckId);\r\n };\r\n\r\n const settleResolve = (result: HumanGatewayResult) => {\r\n if (settled) return;\r\n\r\n settled = true;\r\n cleanup();\r\n resolve(result);\r\n };\r\n\r\n const settleReject = (error: HumanGatewayError) => {\r\n if (settled) return;\r\n\r\n settled = true;\r\n cleanup();\r\n reject(error);\r\n };\r\n\r\n const onMessage = (event: MessageEvent) => {\r\n if (event.origin !== expectedOrigin) {\r\n return;\r\n }\r\n\r\n if (!isHumanGatewayVerificationMessage(event.data)) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"invalid_message\",\r\n \"Received an invalid Human Gateway verification message.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n const result = event.data.payload;\r\n\r\n if (result.project_id !== expectedProjectId) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"invalid_message\",\r\n \"Received a verification result for a different project.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n if (!result.success || !result.verified_human) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"verification_failed\",\r\n \"Human Gateway verification did not complete successfully.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n settleResolve(result);\r\n };\r\n\r\n const timeoutId = window.setTimeout(() => {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"timeout\",\r\n \"Human Gateway verification timed out.\"\r\n )\r\n );\r\n }, timeoutMs);\r\n\r\n const closeCheckId = window.setInterval(() => {\r\n if (popup.closed) {\r\n onClose?.();\r\n\r\n settleReject(\r\n new HumanGatewayError(\r\n \"user_closed\",\r\n \"The verification window was closed before completion.\"\r\n )\r\n );\r\n }\r\n }, 500);\r\n\r\n window.addEventListener(\"message\", onMessage);\r\n });\r\n}\r\n\r\nexport function waitForAuthMessage({\r\n popup,\r\n expectedOrigin,\r\n expectedProjectId,\r\n timeoutMs,\r\n onClose,\r\n}: WaitForMessageInput): Promise<HumanGatewayAuthResult> {\r\n return new Promise((resolve, reject) => {\r\n let settled = false;\r\n\r\n const cleanup = () => {\r\n window.removeEventListener(\"message\", onMessage);\r\n window.clearTimeout(timeoutId);\r\n window.clearInterval(closeCheckId);\r\n };\r\n\r\n const settleResolve = (result: HumanGatewayAuthResult) => {\r\n if (settled) return;\r\n\r\n settled = true;\r\n cleanup();\r\n resolve(result);\r\n };\r\n\r\n const settleReject = (error: HumanGatewayError) => {\r\n if (settled) return;\r\n\r\n settled = true;\r\n cleanup();\r\n reject(error);\r\n };\r\n\r\n const onMessage = (event: MessageEvent) => {\r\n if (event.origin !== expectedOrigin) {\r\n return;\r\n }\r\n\r\n if (!isHumanGatewayAuthMessage(event.data)) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"invalid_message\",\r\n \"Received an invalid Human Gateway auth message.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n const result = event.data.payload;\r\n\r\n if (result.project_id !== expectedProjectId) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"invalid_message\",\r\n \"Received an auth result for a different project.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n if (\r\n !result.success ||\r\n !result.verified_human ||\r\n !result.partner_user_id ||\r\n !result.auth_session_id\r\n ) {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"auth_failed\",\r\n \"Human Gateway auth did not complete successfully.\"\r\n )\r\n );\r\n return;\r\n }\r\n\r\n settleResolve(result);\r\n };\r\n\r\n const timeoutId = window.setTimeout(() => {\r\n settleReject(\r\n new HumanGatewayError(\r\n \"timeout\",\r\n \"Human Gateway auth timed out.\"\r\n )\r\n );\r\n }, timeoutMs);\r\n\r\n const closeCheckId = window.setInterval(() => {\r\n if (popup.closed) {\r\n onClose?.();\r\n\r\n settleReject(\r\n new HumanGatewayError(\r\n \"user_closed\",\r\n \"The auth window was closed before completion.\"\r\n )\r\n );\r\n }\r\n }, 500);\r\n\r\n window.addEventListener(\"message\", onMessage);\r\n });\r\n}","import { openVerificationPopup } from \"./popup\";\r\nimport {\r\n waitForAuthMessage,\r\n waitForVerificationMessage,\r\n} from \"./post-message\";\r\nimport {\r\n HumanGatewayAuthOptions,\r\n HumanGatewayAuthResult,\r\n HumanGatewayError,\r\n HumanGatewayOptions,\r\n HumanGatewayResult,\r\n HumanGatewayVerifyOptions,\r\n} from \"./types\";\r\n\r\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;\r\n\r\nfunction getDefaultBaseUrl(environment: HumanGatewayOptions[\"environment\"]) {\r\n if (environment === \"development\") {\r\n return \"http://localhost:3000\";\r\n }\r\n\r\n if (environment === \"staging\") {\r\n return \"https://staging.humangateway.dev\";\r\n }\r\n\r\n return \"https://humangateway.dev\";\r\n}\r\n\r\nfunction buildVerificationUrl(input: {\r\n baseUrl: string;\r\n projectId: string;\r\n actionKey?: string;\r\n}): string {\r\n const url = new URL(`/verify/${input.projectId}`, input.baseUrl);\r\n\r\n url.searchParams.set(\"sdk\", \"1\");\r\n url.searchParams.set(\"origin\", window.location.origin);\r\n url.searchParams.set(\"mode\", \"popup\");\r\n\r\n if (input.actionKey) {\r\n url.searchParams.set(\"actionKey\", input.actionKey);\r\n }\r\n\r\n return url.toString();\r\n}\r\n\r\nfunction buildAuthUrl(input: {\r\n baseUrl: string;\r\n projectId: string;\r\n actionKey?: string;\r\n}): string {\r\n const url = new URL(`/auth/${input.projectId}`, input.baseUrl);\r\n\r\n url.searchParams.set(\"sdk\", \"1\");\r\n url.searchParams.set(\"origin\", window.location.origin);\r\n url.searchParams.set(\"mode\", \"popup\");\r\n\r\n if (input.actionKey) {\r\n url.searchParams.set(\"actionKey\", input.actionKey);\r\n }\r\n\r\n return url.toString();\r\n}\r\n\r\nexport class HumanGateway {\r\n private readonly projectId: string;\r\n private readonly environment: NonNullable<HumanGatewayOptions[\"environment\"]>;\r\n private readonly mode: NonNullable<HumanGatewayOptions[\"mode\"]>;\r\n private readonly baseUrl: string;\r\n private readonly timeoutMs: number;\r\n private activeVerification: Promise<HumanGatewayResult> | null = null;\r\n private activeAuth: Promise<HumanGatewayAuthResult> | null = null;\r\n\r\n constructor(options: HumanGatewayOptions) {\r\n if (!options.projectId) {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"HumanGateway requires a projectId.\"\r\n );\r\n }\r\n\r\n this.projectId = options.projectId;\r\n this.environment = options.environment ?? \"production\";\r\n this.mode = options.mode ?? \"auto\";\r\n this.baseUrl = options.baseUrl ?? getDefaultBaseUrl(this.environment);\r\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\r\n }\r\n\r\n async verify(\r\n verifyOptions: HumanGatewayVerifyOptions = {}\r\n ): Promise<HumanGatewayResult> {\r\n if (this.activeVerification) {\r\n return this.activeVerification;\r\n }\r\n\r\n this.activeVerification = this.runVerification(verifyOptions).finally(() => {\r\n this.activeVerification = null;\r\n });\r\n\r\n return this.activeVerification;\r\n }\r\n\r\n async auth(\r\n authOptions: HumanGatewayAuthOptions = {}\r\n ): Promise<HumanGatewayAuthResult> {\r\n if (this.activeAuth) {\r\n return this.activeAuth;\r\n }\r\n\r\n this.activeAuth = this.runAuth(authOptions).finally(() => {\r\n this.activeAuth = null;\r\n });\r\n\r\n return this.activeAuth;\r\n }\r\n\r\n private async runVerification(\r\n verifyOptions: HumanGatewayVerifyOptions\r\n ): Promise<HumanGatewayResult> {\r\n try {\r\n if (typeof window === \"undefined\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"HumanGateway.verify() requires a browser environment.\"\r\n );\r\n }\r\n\r\n if (this.mode === \"redirect\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"Redirect mode is not implemented in this SDK alpha.\"\r\n );\r\n }\r\n\r\n const verificationUrl = buildVerificationUrl({\r\n baseUrl: this.baseUrl,\r\n projectId: this.projectId,\r\n ...(verifyOptions.actionKey\r\n ? { actionKey: verifyOptions.actionKey }\r\n : {}),\r\n });\r\n\r\n const expectedOrigin = new URL(this.baseUrl).origin;\r\n\r\n const popup = openVerificationPopup({\r\n url: verificationUrl,\r\n });\r\n\r\n verifyOptions.onOpen?.();\r\n\r\n const result = await waitForVerificationMessage({\r\n popup,\r\n expectedOrigin,\r\n expectedProjectId: this.projectId,\r\n timeoutMs: this.timeoutMs,\r\n ...(verifyOptions.onClose\r\n ? { onClose: verifyOptions.onClose }\r\n : {}),\r\n });\r\n\r\n verifyOptions.onSuccess?.(result);\r\n\r\n try {\r\n popup.close();\r\n } catch {\r\n // Ignore close errors. Some browsers may block programmatic close.\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n const normalizedError =\r\n error instanceof HumanGatewayError\r\n ? error\r\n : new HumanGatewayError(\r\n \"unknown_error\",\r\n error instanceof Error\r\n ? error.message\r\n : \"Unknown Human Gateway SDK error.\"\r\n );\r\n\r\n verifyOptions.onError?.(normalizedError);\r\n\r\n throw normalizedError;\r\n }\r\n }\r\n\r\n private async runAuth(\r\n authOptions: HumanGatewayAuthOptions\r\n ): Promise<HumanGatewayAuthResult> {\r\n try {\r\n if (typeof window === \"undefined\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"HumanGateway.auth() requires a browser environment.\"\r\n );\r\n }\r\n\r\n if (this.mode === \"redirect\") {\r\n throw new HumanGatewayError(\r\n \"unknown_error\",\r\n \"Redirect mode is not implemented in this SDK alpha.\"\r\n );\r\n }\r\n\r\n const authUrl = buildAuthUrl({\r\n baseUrl: this.baseUrl,\r\n projectId: this.projectId,\r\n ...(authOptions.actionKey ? { actionKey: authOptions.actionKey } : {}),\r\n });\r\n\r\n const expectedOrigin = new URL(this.baseUrl).origin;\r\n\r\n const popup = openVerificationPopup({\r\n url: authUrl,\r\n });\r\n\r\n authOptions.onOpen?.();\r\n\r\n const result = await waitForAuthMessage({\r\n popup,\r\n expectedOrigin,\r\n expectedProjectId: this.projectId,\r\n timeoutMs: this.timeoutMs,\r\n ...(authOptions.onClose\r\n ? { onClose: authOptions.onClose }\r\n : {}),\r\n });\r\n\r\n authOptions.onSuccess?.(result);\r\n\r\n try {\r\n popup.close();\r\n } catch {\r\n // Ignore close errors. Some browsers may block programmatic close.\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n const normalizedError =\r\n error instanceof HumanGatewayError\r\n ? error\r\n : new HumanGatewayError(\r\n \"unknown_error\",\r\n error instanceof Error\r\n ? error.message\r\n : \"Unknown Human Gateway SDK auth error.\"\r\n );\r\n\r\n authOptions.onError?.(normalizedError);\r\n\r\n throw normalizedError;\r\n }\r\n }\r\n}"],"mappings":"mCAQO,SAASA,EAAiBC,EAAQ,IAAKC,EAAS,IAAa,CAChE,IAAMC,EACF,OAAO,OAAW,IACZ,KAAK,IAAIF,EAAO,KAAK,MAAM,OAAO,WAAa,GAAI,CAAC,EACpDA,EAEJG,EACF,OAAO,OAAW,IACZ,KAAK,IAAIF,EAAQ,KAAK,MAAM,OAAO,YAAc,GAAI,CAAC,EACtDA,EAEJG,EACF,OAAO,OAAW,IACZ,KAAK,IAAI,EAAG,OAAO,SAAW,OAAO,WAAaF,GAAa,CAAC,EAChE,EAEJG,EACF,OAAO,OAAW,IACZ,KAAK,IAAI,EAAG,OAAO,SAAW,OAAO,YAAcF,GAAc,CAAC,EAClE,EAEV,MAAO,CACH,SAASD,CAAS,GAClB,UAAUC,CAAU,GACpB,QAAQC,CAAI,GACZ,OAAOC,CAAG,GACV,gBACA,iBACA,YACA,aACA,YACJ,EAAE,KAAK,GAAG,CACd,CAEO,SAASC,EAAsB,CAClC,IAAAC,EACA,MAAAP,EACA,OAAAC,CACJ,EAA2B,CACvB,GAAI,OAAO,OAAW,IAClB,MAAM,IAAIO,EACN,gBACA,iEACJ,EAGJ,IAAMC,EAAQ,OAAO,KACjBF,EACA,6BACAR,EAAiBC,EAAOC,CAAM,CAClC,EAEA,GAAI,CAACQ,EACD,MAAM,IAAID,EACN,gBACA,oDACJ,EAGJ,OAAAC,EAAM,MAAM,EAELA,CACX,CCtDA,SAASC,EACLC,EAC4B,CAC5B,GAAI,OAAOA,GAAU,UAAYA,IAAU,KACvC,MAAO,GAGX,IAAMC,EAAUD,EAEhB,OACIC,EAAQ,SAAW,iBACnBA,EAAQ,OAAS,wCACjB,OAAOA,EAAQ,SAAY,UAC3BA,EAAQ,UAAY,IAE5B,CAEA,SAASC,EACLF,EACgC,CAChC,GAAI,OAAOA,GAAU,UAAYA,IAAU,KACvC,MAAO,GAGX,IAAMC,EAAUD,EAEhB,OACIC,EAAQ,SAAW,iBACnBA,EAAQ,OAAS,gCACjB,OAAOA,EAAQ,SAAY,UAC3BA,EAAQ,UAAY,IAE5B,CAEO,SAASE,EAA2B,CACvC,MAAAC,EACA,eAAAC,EACA,kBAAAC,EACA,UAAAC,EACA,QAAAC,CACJ,EAAqD,CACjD,OAAO,IAAI,QAAQ,CAACC,EAASC,IAAW,CACpC,IAAIC,EAAU,GAERC,EAAU,IAAM,CAClB,OAAO,oBAAoB,UAAWC,CAAS,EAC/C,OAAO,aAAaC,CAAS,EAC7B,OAAO,cAAcC,CAAY,CACrC,EAEMC,EAAiBC,GAA+B,CAC9CN,IAEJA,EAAU,GACVC,EAAQ,EACRH,EAAQQ,CAAM,EAClB,EAEMC,EAAgBC,GAA6B,CAC3CR,IAEJA,EAAU,GACVC,EAAQ,EACRF,EAAOS,CAAK,EAChB,EAEMN,EAAaO,GAAwB,CACvC,GAAIA,EAAM,SAAWf,EACjB,OAGJ,GAAI,CAACN,EAAkCqB,EAAM,IAAI,EAAG,CAChDF,EACI,IAAIG,EACA,kBACA,yDACJ,CACJ,EACA,MACJ,CAEA,IAAMJ,EAASG,EAAM,KAAK,QAE1B,GAAIH,EAAO,aAAeX,EAAmB,CACzCY,EACI,IAAIG,EACA,kBACA,yDACJ,CACJ,EACA,MACJ,CAEA,GAAI,CAACJ,EAAO,SAAW,CAACA,EAAO,eAAgB,CAC3CC,EACI,IAAIG,EACA,sBACA,2DACJ,CACJ,EACA,MACJ,CAEAL,EAAcC,CAAM,CACxB,EAEMH,EAAY,OAAO,WAAW,IAAM,CACtCI,EACI,IAAIG,EACA,UACA,uCACJ,CACJ,CACJ,EAAGd,CAAS,EAENQ,EAAe,OAAO,YAAY,IAAM,CACtCX,EAAM,SACNI,IAAU,EAEVU,EACI,IAAIG,EACA,cACA,uDACJ,CACJ,EAER,EAAG,GAAG,EAEN,OAAO,iBAAiB,UAAWR,CAAS,CAChD,CAAC,CACL,CAEO,SAASS,EAAmB,CAC/B,MAAAlB,EACA,eAAAC,EACA,kBAAAC,EACA,UAAAC,EACA,QAAAC,CACJ,EAAyD,CACrD,OAAO,IAAI,QAAQ,CAACC,EAASC,IAAW,CACpC,IAAIC,EAAU,GAERC,EAAU,IAAM,CAClB,OAAO,oBAAoB,UAAWC,CAAS,EAC/C,OAAO,aAAaC,CAAS,EAC7B,OAAO,cAAcC,CAAY,CACrC,EAEMC,EAAiBC,GAAmC,CAClDN,IAEJA,EAAU,GACVC,EAAQ,EACRH,EAAQQ,CAAM,EAClB,EAEMC,EAAgBC,GAA6B,CAC3CR,IAEJA,EAAU,GACVC,EAAQ,EACRF,EAAOS,CAAK,EAChB,EAEMN,EAAaO,GAAwB,CACvC,GAAIA,EAAM,SAAWf,EACjB,OAGJ,GAAI,CAACH,EAA0BkB,EAAM,IAAI,EAAG,CACxCF,EACI,IAAIG,EACA,kBACA,iDACJ,CACJ,EACA,MACJ,CAEA,IAAMJ,EAASG,EAAM,KAAK,QAE1B,GAAIH,EAAO,aAAeX,EAAmB,CACzCY,EACI,IAAIG,EACA,kBACA,kDACJ,CACJ,EACA,MACJ,CAEA,GACI,CAACJ,EAAO,SACR,CAACA,EAAO,gBACR,CAACA,EAAO,iBACR,CAACA,EAAO,gBACV,CACEC,EACI,IAAIG,EACA,cACA,mDACJ,CACJ,EACA,MACJ,CAEAL,EAAcC,CAAM,CACxB,EAEMH,EAAY,OAAO,WAAW,IAAM,CACtCI,EACI,IAAIG,EACA,UACA,+BACJ,CACJ,CACJ,EAAGd,CAAS,EAENQ,EAAe,OAAO,YAAY,IAAM,CACtCX,EAAM,SACNI,IAAU,EAEVU,EACI,IAAIG,EACA,cACA,+CACJ,CACJ,EAER,EAAG,GAAG,EAEN,OAAO,iBAAiB,UAAWR,CAAS,CAChD,CAAC,CACL,CC3OA,IAAMU,EAAqB,IAAS,IAEpC,SAASC,EAAkBC,EAAiD,CACxE,OAAIA,IAAgB,cACT,wBAGPA,IAAgB,UACT,mCAGJ,0BACX,CAEA,SAASC,EAAqBC,EAInB,CACP,IAAMC,EAAM,IAAI,IAAI,WAAWD,EAAM,SAAS,GAAIA,EAAM,OAAO,EAE/D,OAAAC,EAAI,aAAa,IAAI,MAAO,GAAG,EAC/BA,EAAI,aAAa,IAAI,SAAU,OAAO,SAAS,MAAM,EACrDA,EAAI,aAAa,IAAI,OAAQ,OAAO,EAEhCD,EAAM,WACNC,EAAI,aAAa,IAAI,YAAaD,EAAM,SAAS,EAG9CC,EAAI,SAAS,CACxB,CAEA,SAASC,EAAaF,EAIX,CACP,IAAMC,EAAM,IAAI,IAAI,SAASD,EAAM,SAAS,GAAIA,EAAM,OAAO,EAE7D,OAAAC,EAAI,aAAa,IAAI,MAAO,GAAG,EAC/BA,EAAI,aAAa,IAAI,SAAU,OAAO,SAAS,MAAM,EACrDA,EAAI,aAAa,IAAI,OAAQ,OAAO,EAEhCD,EAAM,WACNC,EAAI,aAAa,IAAI,YAAaD,EAAM,SAAS,EAG9CC,EAAI,SAAS,CACxB,CAEO,IAAME,EAAN,KAAmB,CAStB,YAAYC,EAA8B,CAH1C,KAAQ,mBAAyD,KACjE,KAAQ,WAAqD,KAGzD,GAAI,CAACA,EAAQ,UACT,MAAM,IAAIC,EACN,gBACA,oCACJ,EAGJ,KAAK,UAAYD,EAAQ,UACzB,KAAK,YAAcA,EAAQ,aAAe,aAC1C,KAAK,KAAOA,EAAQ,MAAQ,OAC5B,KAAK,QAAUA,EAAQ,SAAWP,EAAkB,KAAK,WAAW,EACpE,KAAK,UAAYO,EAAQ,WAAaR,CAC1C,CAEA,MAAM,OACFU,EAA2C,CAAC,EACjB,CAC3B,OAAI,KAAK,mBACE,KAAK,oBAGhB,KAAK,mBAAqB,KAAK,gBAAgBA,CAAa,EAAE,QAAQ,IAAM,CACxE,KAAK,mBAAqB,IAC9B,CAAC,EAEM,KAAK,mBAChB,CAEA,MAAM,KACFC,EAAuC,CAAC,EACT,CAC/B,OAAI,KAAK,WACE,KAAK,YAGhB,KAAK,WAAa,KAAK,QAAQA,CAAW,EAAE,QAAQ,IAAM,CACtD,KAAK,WAAa,IACtB,CAAC,EAEM,KAAK,WAChB,CAEA,MAAc,gBACVD,EAC2B,CAC3B,GAAI,CACA,GAAI,OAAO,OAAW,IAClB,MAAM,IAAID,EACN,gBACA,uDACJ,EAGJ,GAAI,KAAK,OAAS,WACd,MAAM,IAAIA,EACN,gBACA,qDACJ,EAGJ,IAAMG,EAAkBT,EAAqB,CACzC,QAAS,KAAK,QACd,UAAW,KAAK,UAChB,GAAIO,EAAc,UACZ,CAAE,UAAWA,EAAc,SAAU,EACrC,CAAC,CACX,CAAC,EAEKG,EAAiB,IAAI,IAAI,KAAK,OAAO,EAAE,OAEvCC,EAAQC,EAAsB,CAChC,IAAKH,CACT,CAAC,EAEDF,EAAc,SAAS,EAEvB,IAAMM,EAAS,MAAMC,EAA2B,CAC5C,MAAAH,EACA,eAAAD,EACA,kBAAmB,KAAK,UACxB,UAAW,KAAK,UAChB,GAAIH,EAAc,QACZ,CAAE,QAASA,EAAc,OAAQ,EACjC,CAAC,CACX,CAAC,EAEDA,EAAc,YAAYM,CAAM,EAEhC,GAAI,CACAF,EAAM,MAAM,CAChB,MAAQ,CAER,CAEA,OAAOE,CACX,OAASE,EAAO,CACZ,IAAMC,EACFD,aAAiBT,EACXS,EACA,IAAIT,EACF,gBACAS,aAAiB,MACXA,EAAM,QACN,kCACV,EAER,MAAAR,EAAc,UAAUS,CAAe,EAEjCA,CACV,CACJ,CAEA,MAAc,QACVR,EAC+B,CAC/B,GAAI,CACA,GAAI,OAAO,OAAW,IAClB,MAAM,IAAIF,EACN,gBACA,qDACJ,EAGJ,GAAI,KAAK,OAAS,WACd,MAAM,IAAIA,EACN,gBACA,qDACJ,EAGJ,IAAMW,EAAUd,EAAa,CACzB,QAAS,KAAK,QACd,UAAW,KAAK,UAChB,GAAIK,EAAY,UAAY,CAAE,UAAWA,EAAY,SAAU,EAAI,CAAC,CACxE,CAAC,EAEKE,EAAiB,IAAI,IAAI,KAAK,OAAO,EAAE,OAEvCC,EAAQC,EAAsB,CAChC,IAAKK,CACT,CAAC,EAEDT,EAAY,SAAS,EAErB,IAAMK,EAAS,MAAMK,EAAmB,CACpC,MAAAP,EACA,eAAAD,EACA,kBAAmB,KAAK,UACxB,UAAW,KAAK,UAChB,GAAIF,EAAY,QACV,CAAE,QAASA,EAAY,OAAQ,EAC/B,CAAC,CACX,CAAC,EAEDA,EAAY,YAAYK,CAAM,EAE9B,GAAI,CACAF,EAAM,MAAM,CAChB,MAAQ,CAER,CAEA,OAAOE,CACX,OAASE,EAAO,CACZ,IAAMC,EACFD,aAAiBT,EACXS,EACA,IAAIT,EACF,gBACAS,aAAiB,MACXA,EAAM,QACN,uCACV,EAER,MAAAP,EAAY,UAAUQ,CAAe,EAE/BA,CACV,CACJ,CACJ","names":["getPopupFeatures","width","height","safeWidth","safeHeight","left","top","openVerificationPopup","url","HumanGatewayError","popup","isHumanGatewayVerificationMessage","value","message","isHumanGatewayAuthMessage","waitForVerificationMessage","popup","expectedOrigin","expectedProjectId","timeoutMs","onClose","resolve","reject","settled","cleanup","onMessage","timeoutId","closeCheckId","settleResolve","result","settleReject","error","event","HumanGatewayError","waitForAuthMessage","DEFAULT_TIMEOUT_MS","getDefaultBaseUrl","environment","buildVerificationUrl","input","url","buildAuthUrl","HumanGateway","options","HumanGatewayError","verifyOptions","authOptions","verificationUrl","expectedOrigin","popup","openVerificationPopup","result","waitForVerificationMessage","error","normalizedError","authUrl","waitForAuthMessage"]}
@@ -0,0 +1,2 @@
1
+ "use strict";var b=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var x=(e,a)=>{for(var t in a)b(e,t,{get:a[t],enumerable:!0})},j=(e,a,t,i)=>{if(a&&typeof a=="object"||typeof a=="function")for(let r of C(a))!E.call(e,r)&&r!==t&&b(e,r,{get:()=>a[r],enumerable:!(i=G(a,r))||i.enumerable});return e};var A=e=>j(b({},"__esModule",{value:!0}),e);var q={};x(q,{HumanGatewayCallbackError:()=>n,verifyCallback:()=>v,verifyCallbackSignature:()=>m});module.exports=A(q);var g=require("buffer"),d=require("crypto"),N=300*1e3;function m({rawBody:e,timestamp:a,signature:t,secret:i,toleranceMs:r=N}){if(!e||!a||!t||!i)return!1;let l=Number(a);if(!Number.isFinite(l)||Math.abs(Date.now()-l)>r||!/^[a-f0-9]{64}$/i.test(t))return!1;let y=(0,d.createHmac)("sha256",i).update(`${a}.${e}`).digest("hex"),o=g.Buffer.from(y,"hex"),c=g.Buffer.from(t,"hex");return o.length!==c.length?!1:(0,d.timingSafeEqual)(o,c)}var n=class extends Error{constructor(a,t){super(t),this.name="HumanGatewayCallbackError",this.code=a}};var S=300*1e3,I="x-hg-event-id",R="x-hg-event",M="x-hg-project-id",V="x-hg-timestamp",P="x-hg-signature";function O(e,a){let t=e;if(typeof t.get=="function")return t.get(a);let i=a.toLowerCase();for(let[r,l]of Object.entries(e))if(r.toLowerCase()===i)return Array.isArray(l)?l[0]??null:l??null;return null}function s(e,a){let t=O(e,a);if(!t)throw new n("callback_missing_header",`Missing required Human Gateway callback header: ${a}.`);return t}function T(e){let a=Number(e.timestamp);if(!Number.isFinite(a))throw new n("callback_invalid_timestamp","Human Gateway callback timestamp is invalid.");if(Math.abs(Date.now()-a)>e.toleranceMs)throw new n("callback_expired_timestamp","Human Gateway callback timestamp is outside the allowed tolerance.");return a}function D(e){let a;try{a=JSON.parse(e)}catch{throw new n("callback_invalid_json","Human Gateway callback body is not valid JSON.")}if(typeof a!="object"||a===null||Array.isArray(a))throw new n("callback_invalid_payload","Human Gateway callback payload must be a JSON object.");return a}function w(e){let a=e.payload[e.field];if(typeof a!="string"||!a)throw new n("callback_invalid_payload",`Human Gateway callback payload is missing a valid ${e.field}.`);return a}function p(e){if(e.actual!==e.expected)throw new n(e.code,e.message)}function v({body:e,headers:a,secret:t,toleranceMs:i=S,expectedProjectId:r}){if(!e)throw new n("callback_missing_body","Human Gateway callback raw body is required.");if(!t)throw new n("callback_missing_secret","Human Gateway callback secret is required.");let l=s(a,I),f=s(a,R),y=s(a,M),o=s(a,V),c=s(a,P);if(!/^[a-f0-9]{64}$/i.test(c))throw new n("callback_invalid_signature_format","Human Gateway callback signature format is invalid.");let h=T({timestamp:o,toleranceMs:i});if(!m({rawBody:e,timestamp:o,signature:c,secret:t,toleranceMs:i}))throw new n("callback_invalid_signature","Human Gateway callback signature is invalid.");let u=D(e),k=w({payload:u,field:"event_id"}),H=w({payload:u,field:"event"}),_=w({payload:u,field:"project_id"});return p({actual:k,expected:l,code:"callback_event_id_mismatch",message:"Human Gateway callback event_id does not match the signed header."}),p({actual:H,expected:f,code:"callback_event_type_mismatch",message:"Human Gateway callback event type does not match the signed header."}),p({actual:_,expected:y,code:"callback_project_id_mismatch",message:"Human Gateway callback project_id does not match the signed header."}),r&&p({actual:_,expected:r,code:"callback_project_id_mismatch",message:"Human Gateway callback project_id does not match the expected project."}),{event_id:k,event:H,project_id:_,timestamp:h,payload:u}}0&&(module.exports={HumanGatewayCallbackError,verifyCallback,verifyCallbackSignature});
2
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/verify-callback-signature.ts","../src/types.ts","../src/verify-callback.ts"],"sourcesContent":["export {\r\n verifyCallbackSignature,\r\n type VerifyCallbackSignatureInput,\r\n} from \"./verify-callback-signature\";\r\n\r\nexport { verifyCallback } from \"./verify-callback\";\r\n\r\nexport type {\r\n HumanGatewayCallbackErrorCode,\r\n HumanGatewayCallbackEventName,\r\n HumanGatewayCallbackHeaders,\r\n HumanGatewayCallbackPayload,\r\n VerifiedHumanGatewayCallback,\r\n VerifyCallbackInput,\r\n} from \"./types\";\r\n\r\nexport { HumanGatewayCallbackError } from \"./types\";","import { Buffer } from \"node:buffer\";\r\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\r\n\r\nexport type VerifyCallbackSignatureInput = {\r\n rawBody: string;\r\n timestamp: string | null | undefined;\r\n signature: string | null | undefined;\r\n secret: string;\r\n toleranceMs?: number;\r\n};\r\n\r\nconst DEFAULT_TOLERANCE_MS = 5 * 60 * 1000;\r\n\r\nexport function verifyCallbackSignature({\r\n rawBody,\r\n timestamp,\r\n signature,\r\n secret,\r\n toleranceMs = DEFAULT_TOLERANCE_MS,\r\n}: VerifyCallbackSignatureInput): boolean {\r\n if (!rawBody || !timestamp || !signature || !secret) {\r\n return false;\r\n }\r\n\r\n const timestampNumber = Number(timestamp);\r\n\r\n if (!Number.isFinite(timestampNumber)) {\r\n return false;\r\n }\r\n\r\n const age = Math.abs(Date.now() - timestampNumber);\r\n\r\n if (age > toleranceMs) {\r\n return false;\r\n }\r\n\r\n if (!/^[a-f0-9]{64}$/i.test(signature)) {\r\n return false;\r\n }\r\n\r\n const expectedSignature = createHmac(\"sha256\", secret)\r\n .update(`${timestamp}.${rawBody}`)\r\n .digest(\"hex\");\r\n\r\n const expectedBuffer = Buffer.from(expectedSignature, \"hex\");\r\n const receivedBuffer = Buffer.from(signature, \"hex\");\r\n\r\n if (expectedBuffer.length !== receivedBuffer.length) {\r\n return false;\r\n }\r\n\r\n return timingSafeEqual(expectedBuffer, receivedBuffer);\r\n}","export type HumanGatewayEnvironment =\r\n | \"production\"\r\n | \"staging\"\r\n | \"development\";\r\n\r\nexport type HumanGatewayMode =\r\n | \"auto\"\r\n | \"popup\"\r\n | \"modal\"\r\n | \"redirect\";\r\n\r\nexport type HumanGatewayOptions = {\r\n projectId: string;\r\n environment?: HumanGatewayEnvironment;\r\n mode?: HumanGatewayMode;\r\n baseUrl?: string;\r\n timeoutMs?: number;\r\n};\r\n\r\nexport type HumanGatewayVerifyOptions = {\r\n actionKey?: string;\r\n onOpen?: () => void;\r\n onClose?: () => void;\r\n onSuccess?: (result: HumanGatewayResult) => void;\r\n onError?: (error: HumanGatewayError) => void;\r\n};\r\n\r\nexport type HumanGatewayAuthOptions = {\r\n actionKey?: string;\r\n onOpen?: () => void;\r\n onClose?: () => void;\r\n onSuccess?: (result: HumanGatewayAuthResult) => void;\r\n onError?: (error: HumanGatewayError) => void;\r\n};\r\n\r\nexport type HumanGatewayResult = {\r\n success: boolean;\r\n verified_human: boolean;\r\n unique_for_partner: boolean;\r\n already_verified: boolean;\r\n project_id: string;\r\n};\r\n\r\nexport type HumanGatewayAuthResult = {\r\n success: boolean;\r\n event?: \"auth.completed\";\r\n verified_human: boolean;\r\n partner_user_id: string;\r\n auth_session_id: string;\r\n project_id: string;\r\n expires_at: string;\r\n};\r\n\r\nexport type HumanGatewayMessage = {\r\n source: \"human-gateway\";\r\n type: \"human_gateway.verification.completed\";\r\n payload: HumanGatewayResult;\r\n};\r\n\r\nexport type HumanGatewayAuthMessage = {\r\n source: \"human-gateway\";\r\n type: \"human_gateway.auth.completed\";\r\n payload: HumanGatewayAuthResult;\r\n};\r\n\r\nexport type HumanGatewayErrorCode =\r\n | \"popup_blocked\"\r\n | \"user_closed\"\r\n | \"timeout\"\r\n | \"invalid_message\"\r\n | \"invalid_origin\"\r\n | \"verification_failed\"\r\n | \"auth_failed\"\r\n | \"unknown_error\";\r\n\r\nexport class HumanGatewayError extends Error {\r\n code: HumanGatewayErrorCode;\r\n\r\n constructor(code: HumanGatewayErrorCode, message: string) {\r\n super(message);\r\n this.name = \"HumanGatewayError\";\r\n this.code = code;\r\n }\r\n}\r\n\r\nexport type HumanGatewayCallbackEventName =\r\n | \"verification.completed\"\r\n | \"auth.completed\"\r\n | \"callback.test\"\r\n | (string & {});\r\n\r\nexport type HumanGatewayCallbackPayload = Record<string, unknown> & {\r\n event_id?: unknown;\r\n event?: unknown;\r\n project_id?: unknown;\r\n};\r\n\r\nexport type HumanGatewayCallbackHeaders =\r\n | Headers\r\n | Record<string, string | string[] | null | undefined>;\r\n\r\nexport type VerifyCallbackInput = {\r\n body: string;\r\n headers: HumanGatewayCallbackHeaders;\r\n secret: string;\r\n toleranceMs?: number;\r\n expectedProjectId?: string;\r\n};\r\n\r\nexport type VerifiedHumanGatewayCallback = {\r\n event_id: string;\r\n event: HumanGatewayCallbackEventName;\r\n project_id: string;\r\n timestamp: number;\r\n payload: HumanGatewayCallbackPayload;\r\n};\r\n\r\nexport type HumanGatewayCallbackErrorCode =\r\n | \"callback_missing_body\"\r\n | \"callback_missing_secret\"\r\n | \"callback_missing_header\"\r\n | \"callback_invalid_timestamp\"\r\n | \"callback_expired_timestamp\"\r\n | \"callback_invalid_signature_format\"\r\n | \"callback_invalid_signature\"\r\n | \"callback_invalid_json\"\r\n | \"callback_invalid_payload\"\r\n | \"callback_event_id_mismatch\"\r\n | \"callback_event_type_mismatch\"\r\n | \"callback_project_id_mismatch\";\r\n\r\nexport class HumanGatewayCallbackError extends Error {\r\n code: HumanGatewayCallbackErrorCode;\r\n\r\n constructor(code: HumanGatewayCallbackErrorCode, message: string) {\r\n super(message);\r\n this.name = \"HumanGatewayCallbackError\";\r\n this.code = code;\r\n }\r\n}","import { verifyCallbackSignature } from \"./verify-callback-signature\";\r\nimport {\r\n HumanGatewayCallbackError,\r\n HumanGatewayCallbackHeaders,\r\n HumanGatewayCallbackPayload,\r\n VerifiedHumanGatewayCallback,\r\n VerifyCallbackInput,\r\n} from \"./types\";\r\n\r\nconst DEFAULT_TOLERANCE_MS = 5 * 60 * 1000;\r\n\r\nconst HEADER_EVENT_ID = \"x-hg-event-id\";\r\nconst HEADER_EVENT = \"x-hg-event\";\r\nconst HEADER_PROJECT_ID = \"x-hg-project-id\";\r\nconst HEADER_TIMESTAMP = \"x-hg-timestamp\";\r\nconst HEADER_SIGNATURE = \"x-hg-signature\";\r\n\r\nfunction getHeader(\r\n headers: HumanGatewayCallbackHeaders,\r\n name: string\r\n): string | null {\r\n const maybeHeaders = headers as {\r\n get?: (headerName: string) => string | null;\r\n };\r\n\r\n if (typeof maybeHeaders.get === \"function\") {\r\n return maybeHeaders.get(name);\r\n }\r\n\r\n const normalizedName = name.toLowerCase();\r\n\r\n for (const [key, value] of Object.entries(headers)) {\r\n if (key.toLowerCase() !== normalizedName) {\r\n continue;\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n return value[0] ?? null;\r\n }\r\n\r\n return value ?? null;\r\n }\r\n\r\n return null;\r\n}\r\n\r\nfunction requireHeader(\r\n headers: HumanGatewayCallbackHeaders,\r\n name: string\r\n): string {\r\n const value = getHeader(headers, name);\r\n\r\n if (!value) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_missing_header\",\r\n `Missing required Human Gateway callback header: ${name}.`\r\n );\r\n }\r\n\r\n return value;\r\n}\r\n\r\nfunction parseTimestamp(input: {\r\n timestamp: string;\r\n toleranceMs: number;\r\n}): number {\r\n const timestampNumber = Number(input.timestamp);\r\n\r\n if (!Number.isFinite(timestampNumber)) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_timestamp\",\r\n \"Human Gateway callback timestamp is invalid.\"\r\n );\r\n }\r\n\r\n const age = Math.abs(Date.now() - timestampNumber);\r\n\r\n if (age > input.toleranceMs) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_expired_timestamp\",\r\n \"Human Gateway callback timestamp is outside the allowed tolerance.\"\r\n );\r\n }\r\n\r\n return timestampNumber;\r\n}\r\n\r\nfunction parsePayload(body: string): HumanGatewayCallbackPayload {\r\n let parsed: unknown;\r\n\r\n try {\r\n parsed = JSON.parse(body);\r\n } catch {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_json\",\r\n \"Human Gateway callback body is not valid JSON.\"\r\n );\r\n }\r\n\r\n if (\r\n typeof parsed !== \"object\" ||\r\n parsed === null ||\r\n Array.isArray(parsed)\r\n ) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_payload\",\r\n \"Human Gateway callback payload must be a JSON object.\"\r\n );\r\n }\r\n\r\n return parsed as HumanGatewayCallbackPayload;\r\n}\r\n\r\nfunction requireStringPayloadField(input: {\r\n payload: HumanGatewayCallbackPayload;\r\n field: \"event_id\" | \"event\" | \"project_id\";\r\n}): string {\r\n const value = input.payload[input.field];\r\n\r\n if (typeof value !== \"string\" || !value) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_payload\",\r\n `Human Gateway callback payload is missing a valid ${input.field}.`\r\n );\r\n }\r\n\r\n return value;\r\n}\r\n\r\nfunction assertMatches(input: {\r\n actual: string;\r\n expected: string;\r\n code:\r\n | \"callback_event_id_mismatch\"\r\n | \"callback_event_type_mismatch\"\r\n | \"callback_project_id_mismatch\";\r\n message: string;\r\n}): void {\r\n if (input.actual !== input.expected) {\r\n throw new HumanGatewayCallbackError(input.code, input.message);\r\n }\r\n}\r\n\r\nexport function verifyCallback({\r\n body,\r\n headers,\r\n secret,\r\n toleranceMs = DEFAULT_TOLERANCE_MS,\r\n expectedProjectId,\r\n}: VerifyCallbackInput): VerifiedHumanGatewayCallback {\r\n if (!body) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_missing_body\",\r\n \"Human Gateway callback raw body is required.\"\r\n );\r\n }\r\n\r\n if (!secret) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_missing_secret\",\r\n \"Human Gateway callback secret is required.\"\r\n );\r\n }\r\n\r\n const headerEventId = requireHeader(headers, HEADER_EVENT_ID);\r\n const headerEvent = requireHeader(headers, HEADER_EVENT);\r\n const headerProjectId = requireHeader(headers, HEADER_PROJECT_ID);\r\n const headerTimestamp = requireHeader(headers, HEADER_TIMESTAMP);\r\n const headerSignature = requireHeader(headers, HEADER_SIGNATURE);\r\n\r\n if (!/^[a-f0-9]{64}$/i.test(headerSignature)) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_signature_format\",\r\n \"Human Gateway callback signature format is invalid.\"\r\n );\r\n }\r\n\r\n const timestamp = parseTimestamp({\r\n timestamp: headerTimestamp,\r\n toleranceMs,\r\n });\r\n\r\n const signatureIsValid = verifyCallbackSignature({\r\n rawBody: body,\r\n timestamp: headerTimestamp,\r\n signature: headerSignature,\r\n secret,\r\n toleranceMs,\r\n });\r\n\r\n if (!signatureIsValid) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_signature\",\r\n \"Human Gateway callback signature is invalid.\"\r\n );\r\n }\r\n\r\n const payload = parsePayload(body);\r\n\r\n const payloadEventId = requireStringPayloadField({\r\n payload,\r\n field: \"event_id\",\r\n });\r\n\r\n const payloadEvent = requireStringPayloadField({\r\n payload,\r\n field: \"event\",\r\n });\r\n\r\n const payloadProjectId = requireStringPayloadField({\r\n payload,\r\n field: \"project_id\",\r\n });\r\n\r\n assertMatches({\r\n actual: payloadEventId,\r\n expected: headerEventId,\r\n code: \"callback_event_id_mismatch\",\r\n message:\r\n \"Human Gateway callback event_id does not match the signed header.\",\r\n });\r\n\r\n assertMatches({\r\n actual: payloadEvent,\r\n expected: headerEvent,\r\n code: \"callback_event_type_mismatch\",\r\n message:\r\n \"Human Gateway callback event type does not match the signed header.\",\r\n });\r\n\r\n assertMatches({\r\n actual: payloadProjectId,\r\n expected: headerProjectId,\r\n code: \"callback_project_id_mismatch\",\r\n message:\r\n \"Human Gateway callback project_id does not match the signed header.\",\r\n });\r\n\r\n if (expectedProjectId) {\r\n assertMatches({\r\n actual: payloadProjectId,\r\n expected: expectedProjectId,\r\n code: \"callback_project_id_mismatch\",\r\n message:\r\n \"Human Gateway callback project_id does not match the expected project.\",\r\n });\r\n }\r\n\r\n return {\r\n event_id: payloadEventId,\r\n event: payloadEvent,\r\n project_id: payloadProjectId,\r\n timestamp,\r\n payload,\r\n };\r\n}"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,+BAAAE,EAAA,mBAAAC,EAAA,4BAAAC,IAAA,eAAAC,EAAAL,GCAA,IAAAM,EAAuB,kBACvBC,EAA4C,kBAUtCC,EAAuB,IAAS,IAE/B,SAASC,EAAwB,CACpC,QAAAC,EACA,UAAAC,EACA,UAAAC,EACA,OAAAC,EACA,YAAAC,EAAcN,CAClB,EAA0C,CACtC,GAAI,CAACE,GAAW,CAACC,GAAa,CAACC,GAAa,CAACC,EACzC,MAAO,GAGX,IAAME,EAAkB,OAAOJ,CAAS,EAYxC,GAVI,CAAC,OAAO,SAASI,CAAe,GAIxB,KAAK,IAAI,KAAK,IAAI,EAAIA,CAAe,EAEvCD,GAIN,CAAC,kBAAkB,KAAKF,CAAS,EACjC,MAAO,GAGX,IAAMI,KAAoB,cAAW,SAAUH,CAAM,EAChD,OAAO,GAAGF,CAAS,IAAID,CAAO,EAAE,EAChC,OAAO,KAAK,EAEXO,EAAiB,SAAO,KAAKD,EAAmB,KAAK,EACrDE,EAAiB,SAAO,KAAKN,EAAW,KAAK,EAEnD,OAAIK,EAAe,SAAWC,EAAe,OAClC,MAGJ,mBAAgBD,EAAgBC,CAAc,CACzD,CC+EO,IAAMC,EAAN,cAAwC,KAAM,CAGjD,YAAYC,EAAqCC,EAAiB,CAC9D,MAAMA,CAAO,EACb,KAAK,KAAO,4BACZ,KAAK,KAAOD,CAChB,CACJ,EClIA,IAAME,EAAuB,IAAS,IAEhCC,EAAkB,gBAClBC,EAAe,aACfC,EAAoB,kBACpBC,EAAmB,iBACnBC,EAAmB,iBAEzB,SAASC,EACLC,EACAC,EACa,CACb,IAAMC,EAAeF,EAIrB,GAAI,OAAOE,EAAa,KAAQ,WAC5B,OAAOA,EAAa,IAAID,CAAI,EAGhC,IAAME,EAAiBF,EAAK,YAAY,EAExC,OAAW,CAACG,EAAKC,CAAK,IAAK,OAAO,QAAQL,CAAO,EAC7C,GAAII,EAAI,YAAY,IAAMD,EAI1B,OAAI,MAAM,QAAQE,CAAK,EACZA,EAAM,CAAC,GAAK,KAGhBA,GAAS,KAGpB,OAAO,IACX,CAEA,SAASC,EACLN,EACAC,EACM,CACN,IAAMI,EAAQN,EAAUC,EAASC,CAAI,EAErC,GAAI,CAACI,EACD,MAAM,IAAIE,EACN,0BACA,mDAAmDN,CAAI,GAC3D,EAGJ,OAAOI,CACX,CAEA,SAASG,EAAeC,EAGb,CACP,IAAMC,EAAkB,OAAOD,EAAM,SAAS,EAE9C,GAAI,CAAC,OAAO,SAASC,CAAe,EAChC,MAAM,IAAIH,EACN,6BACA,8CACJ,EAKJ,GAFY,KAAK,IAAI,KAAK,IAAI,EAAIG,CAAe,EAEvCD,EAAM,YACZ,MAAM,IAAIF,EACN,6BACA,oEACJ,EAGJ,OAAOG,CACX,CAEA,SAASC,EAAaC,EAA2C,CAC7D,IAAIC,EAEJ,GAAI,CACAA,EAAS,KAAK,MAAMD,CAAI,CAC5B,MAAQ,CACJ,MAAM,IAAIL,EACN,wBACA,gDACJ,CACJ,CAEA,GACI,OAAOM,GAAW,UAClBA,IAAW,MACX,MAAM,QAAQA,CAAM,EAEpB,MAAM,IAAIN,EACN,2BACA,uDACJ,EAGJ,OAAOM,CACX,CAEA,SAASC,EAA0BL,EAGxB,CACP,IAAMJ,EAAQI,EAAM,QAAQA,EAAM,KAAK,EAEvC,GAAI,OAAOJ,GAAU,UAAY,CAACA,EAC9B,MAAM,IAAIE,EACN,2BACA,qDAAqDE,EAAM,KAAK,GACpE,EAGJ,OAAOJ,CACX,CAEA,SAASU,EAAcN,EAQd,CACL,GAAIA,EAAM,SAAWA,EAAM,SACvB,MAAM,IAAIF,EAA0BE,EAAM,KAAMA,EAAM,OAAO,CAErE,CAEO,SAASO,EAAe,CAC3B,KAAAJ,EACA,QAAAZ,EACA,OAAAiB,EACA,YAAAC,EAAczB,EACd,kBAAA0B,CACJ,EAAsD,CAClD,GAAI,CAACP,EACD,MAAM,IAAIL,EACN,wBACA,8CACJ,EAGJ,GAAI,CAACU,EACD,MAAM,IAAIV,EACN,0BACA,4CACJ,EAGJ,IAAMa,EAAgBd,EAAcN,EAASN,CAAe,EACtD2B,EAAcf,EAAcN,EAASL,CAAY,EACjD2B,EAAkBhB,EAAcN,EAASJ,CAAiB,EAC1D2B,EAAkBjB,EAAcN,EAASH,CAAgB,EACzD2B,EAAkBlB,EAAcN,EAASF,CAAgB,EAE/D,GAAI,CAAC,kBAAkB,KAAK0B,CAAe,EACvC,MAAM,IAAIjB,EACN,oCACA,qDACJ,EAGJ,IAAMkB,EAAYjB,EAAe,CAC7B,UAAWe,EACX,YAAAL,CACJ,CAAC,EAUD,GAAI,CARqBQ,EAAwB,CAC7C,QAASd,EACT,UAAWW,EACX,UAAWC,EACX,OAAAP,EACA,YAAAC,CACJ,CAAC,EAGG,MAAM,IAAIX,EACN,6BACA,8CACJ,EAGJ,IAAMoB,EAAUhB,EAAaC,CAAI,EAE3BgB,EAAiBd,EAA0B,CAC7C,QAAAa,EACA,MAAO,UACX,CAAC,EAEKE,EAAef,EAA0B,CAC3C,QAAAa,EACA,MAAO,OACX,CAAC,EAEKG,EAAmBhB,EAA0B,CAC/C,QAAAa,EACA,MAAO,YACX,CAAC,EAED,OAAAZ,EAAc,CACV,OAAQa,EACR,SAAUR,EACV,KAAM,6BACN,QACI,mEACR,CAAC,EAEDL,EAAc,CACV,OAAQc,EACR,SAAUR,EACV,KAAM,+BACN,QACI,qEACR,CAAC,EAEDN,EAAc,CACV,OAAQe,EACR,SAAUR,EACV,KAAM,+BACN,QACI,qEACR,CAAC,EAEGH,GACAJ,EAAc,CACV,OAAQe,EACR,SAAUX,EACV,KAAM,+BACN,QACI,wEACR,CAAC,EAGE,CACH,SAAUS,EACV,MAAOC,EACP,WAAYC,EACZ,UAAAL,EACA,QAAAE,CACJ,CACJ","names":["server_exports","__export","HumanGatewayCallbackError","verifyCallback","verifyCallbackSignature","__toCommonJS","import_node_buffer","import_node_crypto","DEFAULT_TOLERANCE_MS","verifyCallbackSignature","rawBody","timestamp","signature","secret","toleranceMs","timestampNumber","expectedSignature","expectedBuffer","receivedBuffer","HumanGatewayCallbackError","code","message","DEFAULT_TOLERANCE_MS","HEADER_EVENT_ID","HEADER_EVENT","HEADER_PROJECT_ID","HEADER_TIMESTAMP","HEADER_SIGNATURE","getHeader","headers","name","maybeHeaders","normalizedName","key","value","requireHeader","HumanGatewayCallbackError","parseTimestamp","input","timestampNumber","parsePayload","body","parsed","requireStringPayloadField","assertMatches","verifyCallback","secret","toleranceMs","expectedProjectId","headerEventId","headerEvent","headerProjectId","headerTimestamp","headerSignature","timestamp","verifyCallbackSignature","payload","payloadEventId","payloadEvent","payloadProjectId"]}
@@ -0,0 +1,15 @@
1
+ import { p as VerifyCallbackInput, V as VerifiedHumanGatewayCallback } from './types-D8bnQbLL.cjs';
2
+ export { c as HumanGatewayCallbackError, d as HumanGatewayCallbackErrorCode, e as HumanGatewayCallbackEventName, f as HumanGatewayCallbackHeaders, g as HumanGatewayCallbackPayload } from './types-D8bnQbLL.cjs';
3
+
4
+ type VerifyCallbackSignatureInput = {
5
+ rawBody: string;
6
+ timestamp: string | null | undefined;
7
+ signature: string | null | undefined;
8
+ secret: string;
9
+ toleranceMs?: number;
10
+ };
11
+ declare function verifyCallbackSignature({ rawBody, timestamp, signature, secret, toleranceMs, }: VerifyCallbackSignatureInput): boolean;
12
+
13
+ declare function verifyCallback({ body, headers, secret, toleranceMs, expectedProjectId, }: VerifyCallbackInput): VerifiedHumanGatewayCallback;
14
+
15
+ export { VerifiedHumanGatewayCallback, VerifyCallbackInput, type VerifyCallbackSignatureInput, verifyCallback, verifyCallbackSignature };
@@ -0,0 +1,15 @@
1
+ import { p as VerifyCallbackInput, V as VerifiedHumanGatewayCallback } from './types-D8bnQbLL.js';
2
+ export { c as HumanGatewayCallbackError, d as HumanGatewayCallbackErrorCode, e as HumanGatewayCallbackEventName, f as HumanGatewayCallbackHeaders, g as HumanGatewayCallbackPayload } from './types-D8bnQbLL.js';
3
+
4
+ type VerifyCallbackSignatureInput = {
5
+ rawBody: string;
6
+ timestamp: string | null | undefined;
7
+ signature: string | null | undefined;
8
+ secret: string;
9
+ toleranceMs?: number;
10
+ };
11
+ declare function verifyCallbackSignature({ rawBody, timestamp, signature, secret, toleranceMs, }: VerifyCallbackSignatureInput): boolean;
12
+
13
+ declare function verifyCallback({ body, headers, secret, toleranceMs, expectedProjectId, }: VerifyCallbackInput): VerifiedHumanGatewayCallback;
14
+
15
+ export { VerifiedHumanGatewayCallback, VerifyCallbackInput, type VerifyCallbackSignatureInput, verifyCallback, verifyCallbackSignature };
package/dist/server.js ADDED
@@ -0,0 +1,2 @@
1
+ import{b as n}from"./chunk-3SSCLKQD.js";import{Buffer as k}from"buffer";import{createHmac as h,timingSafeEqual as H}from"crypto";var v=300*1e3;function y({rawBody:e,timestamp:a,signature:t,secret:r,toleranceMs:i=v}){if(!e||!a||!t||!r)return!1;let l=Number(a);if(!Number.isFinite(l)||Math.abs(Date.now()-l)>i||!/^[a-f0-9]{64}$/i.test(t))return!1;let u=h("sha256",r).update(`${a}.${e}`).digest("hex"),c=k.from(u,"hex"),o=k.from(t,"hex");return c.length!==o.length?!1:H(c,o)}var C=300*1e3,E="x-hg-event-id",G="x-hg-event",x="x-hg-project-id",N="x-hg-timestamp",S="x-hg-signature";function j(e,a){let t=e;if(typeof t.get=="function")return t.get(a);let r=a.toLowerCase();for(let[i,l]of Object.entries(e))if(i.toLowerCase()===r)return Array.isArray(l)?l[0]??null:l??null;return null}function s(e,a){let t=j(e,a);if(!t)throw new n("callback_missing_header",`Missing required Human Gateway callback header: ${a}.`);return t}function A(e){let a=Number(e.timestamp);if(!Number.isFinite(a))throw new n("callback_invalid_timestamp","Human Gateway callback timestamp is invalid.");if(Math.abs(Date.now()-a)>e.toleranceMs)throw new n("callback_expired_timestamp","Human Gateway callback timestamp is outside the allowed tolerance.");return a}function I(e){let a;try{a=JSON.parse(e)}catch{throw new n("callback_invalid_json","Human Gateway callback body is not valid JSON.")}if(typeof a!="object"||a===null||Array.isArray(a))throw new n("callback_invalid_payload","Human Gateway callback payload must be a JSON object.");return a}function b(e){let a=e.payload[e.field];if(typeof a!="string"||!a)throw new n("callback_invalid_payload",`Human Gateway callback payload is missing a valid ${e.field}.`);return a}function d(e){if(e.actual!==e.expected)throw new n(e.code,e.message)}function V({body:e,headers:a,secret:t,toleranceMs:r=C,expectedProjectId:i}){if(!e)throw new n("callback_missing_body","Human Gateway callback raw body is required.");if(!t)throw new n("callback_missing_secret","Human Gateway callback secret is required.");let l=s(a,E),p=s(a,G),u=s(a,x),c=s(a,N),o=s(a,S);if(!/^[a-f0-9]{64}$/i.test(o))throw new n("callback_invalid_signature_format","Human Gateway callback signature format is invalid.");let w=A({timestamp:c,toleranceMs:r});if(!y({rawBody:e,timestamp:c,signature:o,secret:t,toleranceMs:r}))throw new n("callback_invalid_signature","Human Gateway callback signature is invalid.");let m=I(e),g=b({payload:m,field:"event_id"}),_=b({payload:m,field:"event"}),f=b({payload:m,field:"project_id"});return d({actual:g,expected:l,code:"callback_event_id_mismatch",message:"Human Gateway callback event_id does not match the signed header."}),d({actual:_,expected:p,code:"callback_event_type_mismatch",message:"Human Gateway callback event type does not match the signed header."}),d({actual:f,expected:u,code:"callback_project_id_mismatch",message:"Human Gateway callback project_id does not match the signed header."}),i&&d({actual:f,expected:i,code:"callback_project_id_mismatch",message:"Human Gateway callback project_id does not match the expected project."}),{event_id:g,event:_,project_id:f,timestamp:w,payload:m}}export{n as HumanGatewayCallbackError,V as verifyCallback,y as verifyCallbackSignature};
2
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/verify-callback-signature.ts","../src/verify-callback.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\r\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\r\n\r\nexport type VerifyCallbackSignatureInput = {\r\n rawBody: string;\r\n timestamp: string | null | undefined;\r\n signature: string | null | undefined;\r\n secret: string;\r\n toleranceMs?: number;\r\n};\r\n\r\nconst DEFAULT_TOLERANCE_MS = 5 * 60 * 1000;\r\n\r\nexport function verifyCallbackSignature({\r\n rawBody,\r\n timestamp,\r\n signature,\r\n secret,\r\n toleranceMs = DEFAULT_TOLERANCE_MS,\r\n}: VerifyCallbackSignatureInput): boolean {\r\n if (!rawBody || !timestamp || !signature || !secret) {\r\n return false;\r\n }\r\n\r\n const timestampNumber = Number(timestamp);\r\n\r\n if (!Number.isFinite(timestampNumber)) {\r\n return false;\r\n }\r\n\r\n const age = Math.abs(Date.now() - timestampNumber);\r\n\r\n if (age > toleranceMs) {\r\n return false;\r\n }\r\n\r\n if (!/^[a-f0-9]{64}$/i.test(signature)) {\r\n return false;\r\n }\r\n\r\n const expectedSignature = createHmac(\"sha256\", secret)\r\n .update(`${timestamp}.${rawBody}`)\r\n .digest(\"hex\");\r\n\r\n const expectedBuffer = Buffer.from(expectedSignature, \"hex\");\r\n const receivedBuffer = Buffer.from(signature, \"hex\");\r\n\r\n if (expectedBuffer.length !== receivedBuffer.length) {\r\n return false;\r\n }\r\n\r\n return timingSafeEqual(expectedBuffer, receivedBuffer);\r\n}","import { verifyCallbackSignature } from \"./verify-callback-signature\";\r\nimport {\r\n HumanGatewayCallbackError,\r\n HumanGatewayCallbackHeaders,\r\n HumanGatewayCallbackPayload,\r\n VerifiedHumanGatewayCallback,\r\n VerifyCallbackInput,\r\n} from \"./types\";\r\n\r\nconst DEFAULT_TOLERANCE_MS = 5 * 60 * 1000;\r\n\r\nconst HEADER_EVENT_ID = \"x-hg-event-id\";\r\nconst HEADER_EVENT = \"x-hg-event\";\r\nconst HEADER_PROJECT_ID = \"x-hg-project-id\";\r\nconst HEADER_TIMESTAMP = \"x-hg-timestamp\";\r\nconst HEADER_SIGNATURE = \"x-hg-signature\";\r\n\r\nfunction getHeader(\r\n headers: HumanGatewayCallbackHeaders,\r\n name: string\r\n): string | null {\r\n const maybeHeaders = headers as {\r\n get?: (headerName: string) => string | null;\r\n };\r\n\r\n if (typeof maybeHeaders.get === \"function\") {\r\n return maybeHeaders.get(name);\r\n }\r\n\r\n const normalizedName = name.toLowerCase();\r\n\r\n for (const [key, value] of Object.entries(headers)) {\r\n if (key.toLowerCase() !== normalizedName) {\r\n continue;\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n return value[0] ?? null;\r\n }\r\n\r\n return value ?? null;\r\n }\r\n\r\n return null;\r\n}\r\n\r\nfunction requireHeader(\r\n headers: HumanGatewayCallbackHeaders,\r\n name: string\r\n): string {\r\n const value = getHeader(headers, name);\r\n\r\n if (!value) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_missing_header\",\r\n `Missing required Human Gateway callback header: ${name}.`\r\n );\r\n }\r\n\r\n return value;\r\n}\r\n\r\nfunction parseTimestamp(input: {\r\n timestamp: string;\r\n toleranceMs: number;\r\n}): number {\r\n const timestampNumber = Number(input.timestamp);\r\n\r\n if (!Number.isFinite(timestampNumber)) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_timestamp\",\r\n \"Human Gateway callback timestamp is invalid.\"\r\n );\r\n }\r\n\r\n const age = Math.abs(Date.now() - timestampNumber);\r\n\r\n if (age > input.toleranceMs) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_expired_timestamp\",\r\n \"Human Gateway callback timestamp is outside the allowed tolerance.\"\r\n );\r\n }\r\n\r\n return timestampNumber;\r\n}\r\n\r\nfunction parsePayload(body: string): HumanGatewayCallbackPayload {\r\n let parsed: unknown;\r\n\r\n try {\r\n parsed = JSON.parse(body);\r\n } catch {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_json\",\r\n \"Human Gateway callback body is not valid JSON.\"\r\n );\r\n }\r\n\r\n if (\r\n typeof parsed !== \"object\" ||\r\n parsed === null ||\r\n Array.isArray(parsed)\r\n ) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_payload\",\r\n \"Human Gateway callback payload must be a JSON object.\"\r\n );\r\n }\r\n\r\n return parsed as HumanGatewayCallbackPayload;\r\n}\r\n\r\nfunction requireStringPayloadField(input: {\r\n payload: HumanGatewayCallbackPayload;\r\n field: \"event_id\" | \"event\" | \"project_id\";\r\n}): string {\r\n const value = input.payload[input.field];\r\n\r\n if (typeof value !== \"string\" || !value) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_payload\",\r\n `Human Gateway callback payload is missing a valid ${input.field}.`\r\n );\r\n }\r\n\r\n return value;\r\n}\r\n\r\nfunction assertMatches(input: {\r\n actual: string;\r\n expected: string;\r\n code:\r\n | \"callback_event_id_mismatch\"\r\n | \"callback_event_type_mismatch\"\r\n | \"callback_project_id_mismatch\";\r\n message: string;\r\n}): void {\r\n if (input.actual !== input.expected) {\r\n throw new HumanGatewayCallbackError(input.code, input.message);\r\n }\r\n}\r\n\r\nexport function verifyCallback({\r\n body,\r\n headers,\r\n secret,\r\n toleranceMs = DEFAULT_TOLERANCE_MS,\r\n expectedProjectId,\r\n}: VerifyCallbackInput): VerifiedHumanGatewayCallback {\r\n if (!body) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_missing_body\",\r\n \"Human Gateway callback raw body is required.\"\r\n );\r\n }\r\n\r\n if (!secret) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_missing_secret\",\r\n \"Human Gateway callback secret is required.\"\r\n );\r\n }\r\n\r\n const headerEventId = requireHeader(headers, HEADER_EVENT_ID);\r\n const headerEvent = requireHeader(headers, HEADER_EVENT);\r\n const headerProjectId = requireHeader(headers, HEADER_PROJECT_ID);\r\n const headerTimestamp = requireHeader(headers, HEADER_TIMESTAMP);\r\n const headerSignature = requireHeader(headers, HEADER_SIGNATURE);\r\n\r\n if (!/^[a-f0-9]{64}$/i.test(headerSignature)) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_signature_format\",\r\n \"Human Gateway callback signature format is invalid.\"\r\n );\r\n }\r\n\r\n const timestamp = parseTimestamp({\r\n timestamp: headerTimestamp,\r\n toleranceMs,\r\n });\r\n\r\n const signatureIsValid = verifyCallbackSignature({\r\n rawBody: body,\r\n timestamp: headerTimestamp,\r\n signature: headerSignature,\r\n secret,\r\n toleranceMs,\r\n });\r\n\r\n if (!signatureIsValid) {\r\n throw new HumanGatewayCallbackError(\r\n \"callback_invalid_signature\",\r\n \"Human Gateway callback signature is invalid.\"\r\n );\r\n }\r\n\r\n const payload = parsePayload(body);\r\n\r\n const payloadEventId = requireStringPayloadField({\r\n payload,\r\n field: \"event_id\",\r\n });\r\n\r\n const payloadEvent = requireStringPayloadField({\r\n payload,\r\n field: \"event\",\r\n });\r\n\r\n const payloadProjectId = requireStringPayloadField({\r\n payload,\r\n field: \"project_id\",\r\n });\r\n\r\n assertMatches({\r\n actual: payloadEventId,\r\n expected: headerEventId,\r\n code: \"callback_event_id_mismatch\",\r\n message:\r\n \"Human Gateway callback event_id does not match the signed header.\",\r\n });\r\n\r\n assertMatches({\r\n actual: payloadEvent,\r\n expected: headerEvent,\r\n code: \"callback_event_type_mismatch\",\r\n message:\r\n \"Human Gateway callback event type does not match the signed header.\",\r\n });\r\n\r\n assertMatches({\r\n actual: payloadProjectId,\r\n expected: headerProjectId,\r\n code: \"callback_project_id_mismatch\",\r\n message:\r\n \"Human Gateway callback project_id does not match the signed header.\",\r\n });\r\n\r\n if (expectedProjectId) {\r\n assertMatches({\r\n actual: payloadProjectId,\r\n expected: expectedProjectId,\r\n code: \"callback_project_id_mismatch\",\r\n message:\r\n \"Human Gateway callback project_id does not match the expected project.\",\r\n });\r\n }\r\n\r\n return {\r\n event_id: payloadEventId,\r\n event: payloadEvent,\r\n project_id: payloadProjectId,\r\n timestamp,\r\n payload,\r\n };\r\n}"],"mappings":"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","names":["Buffer","createHmac","timingSafeEqual","DEFAULT_TOLERANCE_MS","verifyCallbackSignature","rawBody","timestamp","signature","secret","toleranceMs","timestampNumber","expectedSignature","expectedBuffer","receivedBuffer","DEFAULT_TOLERANCE_MS","HEADER_EVENT_ID","HEADER_EVENT","HEADER_PROJECT_ID","HEADER_TIMESTAMP","HEADER_SIGNATURE","getHeader","headers","name","maybeHeaders","normalizedName","key","value","requireHeader","HumanGatewayCallbackError","parseTimestamp","input","timestampNumber","parsePayload","body","parsed","requireStringPayloadField","assertMatches","verifyCallback","secret","toleranceMs","expectedProjectId","headerEventId","headerEvent","headerProjectId","headerTimestamp","headerSignature","timestamp","verifyCallbackSignature","payload","payloadEventId","payloadEvent","payloadProjectId"]}
@@ -0,0 +1,82 @@
1
+ type HumanGatewayEnvironment = "production" | "staging" | "development";
2
+ type HumanGatewayMode = "auto" | "popup" | "modal" | "redirect";
3
+ type HumanGatewayOptions = {
4
+ projectId: string;
5
+ environment?: HumanGatewayEnvironment;
6
+ mode?: HumanGatewayMode;
7
+ baseUrl?: string;
8
+ timeoutMs?: number;
9
+ };
10
+ type HumanGatewayVerifyOptions = {
11
+ actionKey?: string;
12
+ onOpen?: () => void;
13
+ onClose?: () => void;
14
+ onSuccess?: (result: HumanGatewayResult) => void;
15
+ onError?: (error: HumanGatewayError) => void;
16
+ };
17
+ type HumanGatewayAuthOptions = {
18
+ actionKey?: string;
19
+ onOpen?: () => void;
20
+ onClose?: () => void;
21
+ onSuccess?: (result: HumanGatewayAuthResult) => void;
22
+ onError?: (error: HumanGatewayError) => void;
23
+ };
24
+ type HumanGatewayResult = {
25
+ success: boolean;
26
+ verified_human: boolean;
27
+ unique_for_partner: boolean;
28
+ already_verified: boolean;
29
+ project_id: string;
30
+ };
31
+ type HumanGatewayAuthResult = {
32
+ success: boolean;
33
+ event?: "auth.completed";
34
+ verified_human: boolean;
35
+ partner_user_id: string;
36
+ auth_session_id: string;
37
+ project_id: string;
38
+ expires_at: string;
39
+ };
40
+ type HumanGatewayMessage = {
41
+ source: "human-gateway";
42
+ type: "human_gateway.verification.completed";
43
+ payload: HumanGatewayResult;
44
+ };
45
+ type HumanGatewayAuthMessage = {
46
+ source: "human-gateway";
47
+ type: "human_gateway.auth.completed";
48
+ payload: HumanGatewayAuthResult;
49
+ };
50
+ type HumanGatewayErrorCode = "popup_blocked" | "user_closed" | "timeout" | "invalid_message" | "invalid_origin" | "verification_failed" | "auth_failed" | "unknown_error";
51
+ declare class HumanGatewayError extends Error {
52
+ code: HumanGatewayErrorCode;
53
+ constructor(code: HumanGatewayErrorCode, message: string);
54
+ }
55
+ type HumanGatewayCallbackEventName = "verification.completed" | "auth.completed" | "callback.test" | (string & {});
56
+ type HumanGatewayCallbackPayload = Record<string, unknown> & {
57
+ event_id?: unknown;
58
+ event?: unknown;
59
+ project_id?: unknown;
60
+ };
61
+ type HumanGatewayCallbackHeaders = Headers | Record<string, string | string[] | null | undefined>;
62
+ type VerifyCallbackInput = {
63
+ body: string;
64
+ headers: HumanGatewayCallbackHeaders;
65
+ secret: string;
66
+ toleranceMs?: number;
67
+ expectedProjectId?: string;
68
+ };
69
+ type VerifiedHumanGatewayCallback = {
70
+ event_id: string;
71
+ event: HumanGatewayCallbackEventName;
72
+ project_id: string;
73
+ timestamp: number;
74
+ payload: HumanGatewayCallbackPayload;
75
+ };
76
+ type HumanGatewayCallbackErrorCode = "callback_missing_body" | "callback_missing_secret" | "callback_missing_header" | "callback_invalid_timestamp" | "callback_expired_timestamp" | "callback_invalid_signature_format" | "callback_invalid_signature" | "callback_invalid_json" | "callback_invalid_payload" | "callback_event_id_mismatch" | "callback_event_type_mismatch" | "callback_project_id_mismatch";
77
+ declare class HumanGatewayCallbackError extends Error {
78
+ code: HumanGatewayCallbackErrorCode;
79
+ constructor(code: HumanGatewayCallbackErrorCode, message: string);
80
+ }
81
+
82
+ export { type HumanGatewayAuthMessage as H, type VerifiedHumanGatewayCallback as V, type HumanGatewayAuthOptions as a, type HumanGatewayAuthResult as b, HumanGatewayCallbackError as c, type HumanGatewayCallbackErrorCode as d, type HumanGatewayCallbackEventName as e, type HumanGatewayCallbackHeaders as f, type HumanGatewayCallbackPayload as g, type HumanGatewayEnvironment as h, HumanGatewayError as i, type HumanGatewayErrorCode as j, type HumanGatewayMessage as k, type HumanGatewayMode as l, type HumanGatewayOptions as m, type HumanGatewayResult as n, type HumanGatewayVerifyOptions as o, type VerifyCallbackInput as p };
@@ -0,0 +1,82 @@
1
+ type HumanGatewayEnvironment = "production" | "staging" | "development";
2
+ type HumanGatewayMode = "auto" | "popup" | "modal" | "redirect";
3
+ type HumanGatewayOptions = {
4
+ projectId: string;
5
+ environment?: HumanGatewayEnvironment;
6
+ mode?: HumanGatewayMode;
7
+ baseUrl?: string;
8
+ timeoutMs?: number;
9
+ };
10
+ type HumanGatewayVerifyOptions = {
11
+ actionKey?: string;
12
+ onOpen?: () => void;
13
+ onClose?: () => void;
14
+ onSuccess?: (result: HumanGatewayResult) => void;
15
+ onError?: (error: HumanGatewayError) => void;
16
+ };
17
+ type HumanGatewayAuthOptions = {
18
+ actionKey?: string;
19
+ onOpen?: () => void;
20
+ onClose?: () => void;
21
+ onSuccess?: (result: HumanGatewayAuthResult) => void;
22
+ onError?: (error: HumanGatewayError) => void;
23
+ };
24
+ type HumanGatewayResult = {
25
+ success: boolean;
26
+ verified_human: boolean;
27
+ unique_for_partner: boolean;
28
+ already_verified: boolean;
29
+ project_id: string;
30
+ };
31
+ type HumanGatewayAuthResult = {
32
+ success: boolean;
33
+ event?: "auth.completed";
34
+ verified_human: boolean;
35
+ partner_user_id: string;
36
+ auth_session_id: string;
37
+ project_id: string;
38
+ expires_at: string;
39
+ };
40
+ type HumanGatewayMessage = {
41
+ source: "human-gateway";
42
+ type: "human_gateway.verification.completed";
43
+ payload: HumanGatewayResult;
44
+ };
45
+ type HumanGatewayAuthMessage = {
46
+ source: "human-gateway";
47
+ type: "human_gateway.auth.completed";
48
+ payload: HumanGatewayAuthResult;
49
+ };
50
+ type HumanGatewayErrorCode = "popup_blocked" | "user_closed" | "timeout" | "invalid_message" | "invalid_origin" | "verification_failed" | "auth_failed" | "unknown_error";
51
+ declare class HumanGatewayError extends Error {
52
+ code: HumanGatewayErrorCode;
53
+ constructor(code: HumanGatewayErrorCode, message: string);
54
+ }
55
+ type HumanGatewayCallbackEventName = "verification.completed" | "auth.completed" | "callback.test" | (string & {});
56
+ type HumanGatewayCallbackPayload = Record<string, unknown> & {
57
+ event_id?: unknown;
58
+ event?: unknown;
59
+ project_id?: unknown;
60
+ };
61
+ type HumanGatewayCallbackHeaders = Headers | Record<string, string | string[] | null | undefined>;
62
+ type VerifyCallbackInput = {
63
+ body: string;
64
+ headers: HumanGatewayCallbackHeaders;
65
+ secret: string;
66
+ toleranceMs?: number;
67
+ expectedProjectId?: string;
68
+ };
69
+ type VerifiedHumanGatewayCallback = {
70
+ event_id: string;
71
+ event: HumanGatewayCallbackEventName;
72
+ project_id: string;
73
+ timestamp: number;
74
+ payload: HumanGatewayCallbackPayload;
75
+ };
76
+ type HumanGatewayCallbackErrorCode = "callback_missing_body" | "callback_missing_secret" | "callback_missing_header" | "callback_invalid_timestamp" | "callback_expired_timestamp" | "callback_invalid_signature_format" | "callback_invalid_signature" | "callback_invalid_json" | "callback_invalid_payload" | "callback_event_id_mismatch" | "callback_event_type_mismatch" | "callback_project_id_mismatch";
77
+ declare class HumanGatewayCallbackError extends Error {
78
+ code: HumanGatewayCallbackErrorCode;
79
+ constructor(code: HumanGatewayCallbackErrorCode, message: string);
80
+ }
81
+
82
+ export { type HumanGatewayAuthMessage as H, type VerifiedHumanGatewayCallback as V, type HumanGatewayAuthOptions as a, type HumanGatewayAuthResult as b, HumanGatewayCallbackError as c, type HumanGatewayCallbackErrorCode as d, type HumanGatewayCallbackEventName as e, type HumanGatewayCallbackHeaders as f, type HumanGatewayCallbackPayload as g, type HumanGatewayEnvironment as h, HumanGatewayError as i, type HumanGatewayErrorCode as j, type HumanGatewayMessage as k, type HumanGatewayMode as l, type HumanGatewayOptions as m, type HumanGatewayResult as n, type HumanGatewayVerifyOptions as o, type VerifyCallbackInput as p };
package/package.json CHANGED
@@ -1,16 +1,46 @@
1
- {
2
- "name": "@human-gateway/sdk",
3
- "version": "0.0.1",
4
- "description": "Official Human Gateway SDK placeholder. Public SDK coming soon.",
5
- "main": "index.js",
6
- "type": "module",
7
- "keywords": [
8
- "human-gateway",
9
- "world-id",
10
- "verification",
11
- "authentication",
12
- "sdk"
13
- ],
14
- "author": "Human Gateway",
15
- "license": "MIT"
16
- }
1
+ {
2
+ "name": "@human-gateway/sdk",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "Human verification SDK for Human Gateway.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./server": {
16
+ "types": "./dist/server.d.ts",
17
+ "import": "./dist/server.js",
18
+ "require": "./dist/server.cjs"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "dev": "tsup --watch",
27
+ "typecheck": "tsc --noEmit",
28
+ "prepack": "npm run build",
29
+ "prepublishOnly": "npm run typecheck && npm run build"
30
+ },
31
+ "keywords": [
32
+ "human-gateway",
33
+ "world-id",
34
+ "verification",
35
+ "identity",
36
+ "sdk"
37
+ ],
38
+ "author": "Human Gateway",
39
+ "license": "MIT",
40
+ "sideEffects": false,
41
+ "devDependencies": {
42
+ "@types/node": "^25.9.1",
43
+ "tsup": "^8.5.1",
44
+ "typescript": "^5.9.3"
45
+ }
46
+ }
package/index.js DELETED
@@ -1,5 +0,0 @@
1
- export const version = "0.0.1";
2
-
3
- export function humanGatewaySdkPlaceholder() {
4
- return "Human Gateway SDK placeholder. Public SDK coming soon.";
5
- }