@verne-software/sdk 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12,31 +12,89 @@ var VerneAPIError = class _VerneAPIError extends VerneError {
12
12
  code,
13
13
  message,
14
14
  status,
15
- requestId
15
+ requestId,
16
+ details,
17
+ retryAfter,
18
+ body
16
19
  }) {
17
20
  super(message);
18
21
  this.name = "VerneAPIError";
19
22
  this.code = code;
20
23
  this.status = status;
21
24
  this.requestId = requestId;
25
+ this.details = details ?? {};
26
+ this.retryAfter = retryAfter;
27
+ this.body = body;
22
28
  }
23
- static fromResponse(status, body) {
29
+ /**
30
+ * Builds an error from a parsed response body of *unknown* shape.
31
+ *
32
+ * Every API route answers with the envelope `{ error: { code, message, request_id,
33
+ * ...extra } }`. Other shapes are still read, as a fallback that costs nothing: the
34
+ * string form `{ error: "<sentence>" }` Clockwork used before October 2026, and an
35
+ * identity-service body whose `code` is a number (still what Kratos' own `/auth/*`
36
+ * self-service routes answer). Anything unrecognised degrades — to code `'unknown'`,
37
+ * to `HTTP <status>` — rather than throwing: an error path must never be the thing
38
+ * that fails.
39
+ */
40
+ static fromResponse(status, body, retryAfter) {
41
+ const envelope = isRecord(body) && isRecord(body.error) ? body.error : void 0;
42
+ const details = {};
43
+ if (envelope) {
44
+ for (const [key, value] of Object.entries(envelope)) {
45
+ if (key !== "code" && key !== "message" && key !== "request_id") details[key] = value;
46
+ }
47
+ }
24
48
  return new _VerneAPIError({
25
- code: body.error.code,
26
- message: body.error.message,
49
+ code: typeof envelope?.code === "string" ? envelope.code : "unknown",
50
+ message: extractMessage(body, status),
27
51
  status,
28
- requestId: body.error.request_id
52
+ requestId: typeof envelope?.request_id === "string" ? envelope.request_id : void 0,
53
+ details,
54
+ retryAfter,
55
+ body
29
56
  });
30
57
  }
31
58
  };
59
+ var VerneTokenError = class extends VerneError {
60
+ constructor(reason, message) {
61
+ super(message);
62
+ this.name = "VerneTokenError";
63
+ this.reason = reason;
64
+ }
65
+ };
66
+ function isRecord(value) {
67
+ return typeof value === "object" && value !== null && !Array.isArray(value);
68
+ }
69
+ function extractMessage(body, status) {
70
+ if (isRecord(body)) {
71
+ const envelope = body.error;
72
+ if (isRecord(envelope) && typeof envelope.message === "string") {
73
+ return envelope.message;
74
+ }
75
+ if (typeof envelope === "string" && envelope.trim() !== "") {
76
+ return envelope;
77
+ }
78
+ const { detail } = body;
79
+ if (typeof detail === "string") return detail;
80
+ if (Array.isArray(detail)) {
81
+ const messages = detail.map((item) => isRecord(item) && typeof item.msg === "string" ? item.msg : null).filter((msg) => msg !== null);
82
+ if (messages.length > 0) return messages.join("; ");
83
+ }
84
+ if (typeof body.message === "string") return body.message;
85
+ }
86
+ return `HTTP ${status}`;
87
+ }
32
88
 
33
89
  // src/core/http.ts
34
90
  var DEFAULT_BASE_URL = "https://api.vernesoft.com";
35
91
  var DEFAULT_TIMEOUT_MS = 3e4;
92
+ var MAX_AUTO_RETRY_AFTER_SECONDS = 60;
93
+ var NON_RETRYABLE_429_CODES = /* @__PURE__ */ new Set(["RELAY_MESSAGE_LIMIT_REACHED"]);
36
94
  var HttpClient = class {
37
95
  constructor(config) {
38
96
  this.apiKey = config.apiKey;
39
- this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
97
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
40
98
  this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
41
99
  }
42
100
  async get(path, options) {
@@ -55,16 +113,29 @@ var HttpClient = class {
55
113
  return this.request("DELETE", path, options);
56
114
  }
57
115
  async request(method, path, options = {}, attempt = 1) {
58
- const { skipAuth, body, signal, headers: extraHeaders, timeoutMs } = options;
59
- const url = `${this.baseUrl}${path}`;
116
+ const {
117
+ skipAuth,
118
+ body,
119
+ signal,
120
+ headers: extraHeaders,
121
+ timeoutMs,
122
+ query,
123
+ sessionToken
124
+ } = options;
125
+ const qs = buildQuery(query);
126
+ const url = `${this.baseUrl}${path}${qs ? (path.includes("?") ? "&" : "?") + qs : ""}`;
60
127
  const timeout = timeoutMs ?? this.timeoutMs;
61
128
  const headers = {
62
129
  "Content-Type": "application/json",
130
+ Accept: "application/json",
63
131
  ...extraHeaders
64
132
  };
65
133
  if (!skipAuth) {
66
134
  headers["Authorization"] = `Bearer ${this.apiKey}`;
67
135
  }
136
+ if (sessionToken !== void 0) {
137
+ headers["X-Session-Token"] = sessionToken;
138
+ }
68
139
  const controller = new AbortController();
69
140
  const timeoutId = setTimeout(() => controller.abort(), timeout);
70
141
  const combinedSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
@@ -85,20 +156,21 @@ var HttpClient = class {
85
156
  } finally {
86
157
  clearTimeout(timeoutId);
87
158
  }
88
- if (response.status === 429 && attempt === 1 && method === "POST") {
89
- const retryAfter = response.headers.get("Retry-After");
90
- const waitMs = retryAfter ? parseFloat(retryAfter) * 1e3 : 1e3;
91
- await sleep(waitMs);
92
- return this.request(method, path, options, 2);
93
- }
94
159
  if (!response.ok) {
160
+ const retryAfter = parseRetryAfter(response.headers.get("Retry-After"));
95
161
  let errorBody;
96
162
  try {
97
163
  errorBody = await response.json();
98
164
  } catch {
99
- throw new VerneError(`HTTP ${response.status}: ${method} ${path}`);
165
+ throw VerneAPIError.fromResponse(response.status, void 0, retryAfter);
100
166
  }
101
- throw VerneAPIError.fromResponse(response.status, errorBody);
167
+ const error = VerneAPIError.fromResponse(response.status, errorBody, retryAfter);
168
+ const wait = retryAfter ?? 1;
169
+ if (response.status === 429 && attempt === 1 && method === "POST" && wait <= MAX_AUTO_RETRY_AFTER_SECONDS && !NON_RETRYABLE_429_CODES.has(error.code)) {
170
+ await sleep(wait * 1e3);
171
+ return this.request(method, path, options, 2);
172
+ }
173
+ throw error;
102
174
  }
103
175
  if (response.status === 204) {
104
176
  return void 0;
@@ -106,6 +178,26 @@ var HttpClient = class {
106
178
  return response.json();
107
179
  }
108
180
  };
181
+ function parseRetryAfter(raw) {
182
+ if (raw === null || raw.trim() === "") return void 0;
183
+ const seconds = Number(raw);
184
+ if (Number.isFinite(seconds) && seconds >= 0) return seconds;
185
+ const at = Date.parse(raw);
186
+ if (!Number.isNaN(at)) return Math.max(0, Math.ceil((at - Date.now()) / 1e3));
187
+ return void 0;
188
+ }
189
+ function buildQuery(params) {
190
+ if (!params) return "";
191
+ const search = new URLSearchParams();
192
+ for (const [key, value] of Object.entries(params)) {
193
+ if (value === void 0 || value === null) continue;
194
+ search.set(key, value instanceof Date ? value.toISOString() : String(value));
195
+ }
196
+ return search.toString();
197
+ }
198
+ function seg(value) {
199
+ return encodeURIComponent(value);
200
+ }
109
201
  function sleep(ms) {
110
202
  return new Promise((resolve) => setTimeout(resolve, ms));
111
203
  }
@@ -203,6 +295,34 @@ var Clockwork = class {
203
295
  this.jobs = new ClockworkJobsResource(this.http);
204
296
  this.delayed = new ClockworkDelayedResource(this.http);
205
297
  }
298
+ /**
299
+ * How many jobs exist against the plan's limits. Creating a job over a limit, or
300
+ * resuming a paused one, is refused with `402` (`UPGRADE_REQUIRED` or
301
+ * `JOB_LIMIT_REACHED`) — never retried.
302
+ */
303
+ async usage(options) {
304
+ return this.http.get("/v1/clockwork/usage", options);
305
+ }
306
+ };
307
+
308
+ // src/resources/gate/audit-log.ts
309
+ var AuditLogResource = class {
310
+ constructor(http) {
311
+ this.http = http;
312
+ }
313
+ /**
314
+ * One page of entries, newest first. Filters combine; keep the same filters while
315
+ * paging with `cursor`.
316
+ *
317
+ * @example
318
+ * const page = await gate.auditLog.list({ action: 'identity.login', actor_id: userId });
319
+ */
320
+ async list(params, options) {
321
+ return this.http.get("/v1/gate/audit-log", {
322
+ query: { ...params },
323
+ ...options
324
+ });
325
+ }
206
326
  };
207
327
 
208
328
  // src/resources/gate/identities.ts
@@ -211,50 +331,52 @@ var IdentitiesResource = class {
211
331
  this.http = http;
212
332
  }
213
333
  /**
214
- * Creates a new identity (end-user) bound to your tenant.
215
- * The `traits.tenant_id` is injected automatically by the Edge Gateway.
334
+ * Creates a new identity (end user) bound to your tenant. Answers `201`.
335
+ *
336
+ * A `409 conflict` means you already have an end user with this address; the same
337
+ * address at another tenant is a different person and does not conflict. Traits are
338
+ * `email` and `locale` only — keep your own fields in custom metadata.
216
339
  */
217
340
  async create(params, options) {
218
341
  return this.http.post("/v1/gate/identities", { body: params, ...options });
219
342
  }
220
343
  /**
221
- * Returns an identity by ID.
222
- * The Gateway will reject the request if the identity does not belong to your tenant.
344
+ * Returns an identity by ID. An identity of another tenant is refused.
223
345
  */
224
346
  async get(identityId, options) {
225
- return this.http.get(`/v1/gate/identities/${identityId}`, options);
347
+ return this.http.get(`/v1/gate/identities/${seg(identityId)}`, options);
226
348
  }
227
349
  /**
228
350
  * Partially updates an identity using a JSON Patch document (RFC 6902).
229
351
  *
230
352
  * @example
231
353
  * await gate.identities.patch('identity_123', [
232
- * { op: 'replace', path: '/traits/custom_data/role', value: 'admin' }
354
+ * { op: 'replace', path: '/traits/email', value: 'new@example.com' },
233
355
  * ]);
234
356
  */
235
357
  async patch(identityId, ops, options) {
236
- return this.http.patch(`/v1/gate/identities/${identityId}`, {
358
+ return this.http.patch(`/v1/gate/identities/${seg(identityId)}`, {
237
359
  body: ops,
238
360
  ...options
239
361
  });
240
362
  }
241
363
  /**
242
- * Deletes an identity from Kratos.
243
- * The Gateway will reject the request if the identity does not belong to your tenant.
364
+ * Deletes an identity, its sessions and its memberships. Fires `identity.deleted`.
244
365
  */
245
366
  async delete(identityId, options) {
246
- return this.http.delete(`/v1/gate/identities/${identityId}`, options);
367
+ return this.http.delete(`/v1/gate/identities/${seg(identityId)}`, options);
247
368
  }
248
369
  /**
249
- * Activates or deactivates an identity. An `inactive` identity cannot log in —
250
- * Kratos rejects its credentials automatically — until it is reactivated.
251
- * The identity is not deleted. Fires the `identity.state_changed` webhook event.
370
+ * Activates or deactivates an identity. An `inactive` identity cannot sign in, its
371
+ * sessions stop working (until it is active again), its access tokens introspect as
372
+ * `active: false`, and no signed token is issued for it. Nothing is deleted. Fires the
373
+ * `identity.state_changed` webhook event.
252
374
  *
253
375
  * @example
254
376
  * await gate.identities.setState('identity_123', 'inactive');
255
377
  */
256
378
  async setState(identityId, state, options) {
257
- return this.http.patch(`/v1/gate/identities/${identityId}/state`, {
379
+ return this.http.patch(`/v1/gate/identities/${seg(identityId)}/state`, {
258
380
  body: { state },
259
381
  ...options
260
382
  });
@@ -274,9 +396,272 @@ var IdentitiesResource = class {
274
396
  */
275
397
  async resendVerification(identityId, options) {
276
398
  return this.http.post(
277
- `/v1/gate/identities/${identityId}/resend-verification`,
399
+ `/v1/gate/identities/${seg(identityId)}/resend-verification`,
400
+ options
401
+ );
402
+ }
403
+ /**
404
+ * Reads an identity's custom metadata (`metadata_public`) — the place for your own
405
+ * fields about a user. `{}` when nothing is set.
406
+ */
407
+ async getMetadata(identityId, options) {
408
+ const res = await this.http.get(
409
+ `/v1/gate/identities/${seg(identityId)}/metadata`,
278
410
  options
279
411
  );
412
+ return res.metadata_public;
413
+ }
414
+ /**
415
+ * Merges `patch` into an identity's custom metadata and returns the result. Keys you
416
+ * leave out are kept; a key set to `null` is removed.
417
+ *
418
+ * @example
419
+ * await gate.identities.updateMetadata(id, { plan: 'pro', seats: null });
420
+ */
421
+ async updateMetadata(identityId, patch, options) {
422
+ const res = await this.http.patch(
423
+ `/v1/gate/identities/${seg(identityId)}/metadata`,
424
+ { body: patch, ...options }
425
+ );
426
+ return res.metadata_public;
427
+ }
428
+ /**
429
+ * The organizations of the key's project that an end user belongs to, with the role
430
+ * held in each and its permissions — live, oldest membership first, cursor-paged. This
431
+ * is the complete list that `orgs_truncated: true` (in a token, an introspection or a
432
+ * session check) points to.
433
+ */
434
+ async listOrganizations(identityId, params, options) {
435
+ return this.http.get(
436
+ `/v1/gate/identities/${seg(identityId)}/organizations`,
437
+ { query: { limit: params?.limit, cursor: params?.cursor }, ...options }
438
+ );
439
+ }
440
+ };
441
+
442
+ // src/resources/gate/lifecycle.ts
443
+ var rulePath = (kind) => `/v1/gate/lifecycle/rules/${seg(kind)}`;
444
+ var LifecycleResource = class {
445
+ constructor(http) {
446
+ this.http = http;
447
+ }
448
+ /** All four rules of the key's project, set up or not, and the plan. */
449
+ async listRules(options) {
450
+ return this.http.get("/v1/gate/lifecycle/rules", options);
451
+ }
452
+ /**
453
+ * Creates, changes, or switches a rule on or off. Leave `params` out to keep the saved
454
+ * settings. Switching off or changing settings cancels what the rule had scheduled.
455
+ *
456
+ * @example
457
+ * await gate.lifecycle.setRule('delete_unverified', {
458
+ * enabled: true,
459
+ * params: { delete_after_days: 14, warn_before_days: 3 },
460
+ * });
461
+ */
462
+ async setRule(kind, params, options) {
463
+ const body = { enabled: params.enabled };
464
+ if (params.params !== void 0) body.params = params.params;
465
+ return this.http.put(rulePath(kind), { body, ...options });
466
+ }
467
+ /** Removes a rule and cancels what it scheduled. Succeeds also when there was none. */
468
+ async deleteRule(kind, options) {
469
+ return this.http.delete(rulePath(kind), options);
470
+ }
471
+ /**
472
+ * How many people a rule with these settings would affect right now. Writes nothing;
473
+ * works whether or not the rule is on or in your plan. `expire_roles` takes `{}`.
474
+ */
475
+ async previewRule(kind, params, options) {
476
+ return this.http.post(`${rulePath(kind)}/preview`, {
477
+ body: { params },
478
+ ...options
479
+ });
480
+ }
481
+ /** What the rules have scheduled and done, oldest first, cursor-paged. */
482
+ async listActions(params, options) {
483
+ return this.http.get("/v1/gate/lifecycle/actions", {
484
+ query: { ...params },
485
+ ...options
486
+ });
487
+ }
488
+ };
489
+
490
+ // src/resources/gate/organizations.ts
491
+ var orgPath = (orgId) => `/v1/gate/organizations/${seg(orgId)}`;
492
+ var pageQuery = (params) => ({ limit: params?.limit, cursor: params?.cursor });
493
+ var isoOrNull = (value) => value instanceof Date ? value.toISOString() : value;
494
+ var OrganizationsResource = class {
495
+ constructor(http) {
496
+ this.http = http;
497
+ this.roles = new RolesResource(http);
498
+ this.members = new MembersResource(http);
499
+ }
500
+ /** Creates an organization. `name` is free text, 1–120 characters, not unique. */
501
+ async create(params, options) {
502
+ return this.http.post("/v1/gate/organizations", { body: params, ...options });
503
+ }
504
+ /** Lists the project's organizations, oldest first, cursor-paged. */
505
+ async list(params, options) {
506
+ return this.http.get("/v1/gate/organizations", {
507
+ query: pageQuery(params),
508
+ ...options
509
+ });
510
+ }
511
+ async get(orgId, options) {
512
+ return this.http.get(orgPath(orgId), options);
513
+ }
514
+ /** Renames an organization. */
515
+ async update(orgId, params, options) {
516
+ return this.http.patch(orgPath(orgId), { body: params, ...options });
517
+ }
518
+ /** Deletes an organization with its roles and memberships. */
519
+ async delete(orgId, options) {
520
+ return this.http.delete(orgPath(orgId), options);
521
+ }
522
+ /**
523
+ * *May this end user do this in this organization?* — answered from the role the user
524
+ * holds there and its permissions, as they are now. `permission` must match exactly
525
+ * (no wildcards). An organization of another project looks like a missing one
526
+ * (`no_such_organization`). An invalid permission (upper case, `*`, the `nautilus:`
527
+ * prefix) is a `400` error, never `allowed: false`. Not billed, not audited.
528
+ */
529
+ async authorize(orgId, identityId, permission, options) {
530
+ return this.http.post("/v1/gate/authorize", {
531
+ body: { subject: identityId, action: permission, resource: `organization:${orgId}` },
532
+ ...options
533
+ });
534
+ }
535
+ /**
536
+ * `true` when the end user holds `permission` in the organization — the boolean of
537
+ * {@link authorize}. Errors are thrown, not turned into `false`.
538
+ *
539
+ * @example
540
+ * if (!(await gate.organizations.can(orgId, userId, 'invoices:write'))) {
541
+ * throw new Error('Forbidden');
542
+ * }
543
+ */
544
+ async can(orgId, identityId, permission, options) {
545
+ const decision = await this.authorize(orgId, identityId, permission, options);
546
+ return decision.allowed === true;
547
+ }
548
+ };
549
+ var RolesResource = class {
550
+ constructor(http) {
551
+ this.http = http;
552
+ }
553
+ /**
554
+ * Defines a role. `409 conflict` if the name is taken in the organization,
555
+ * `409 limit_reached` past 100 roles.
556
+ */
557
+ async create(orgId, params, options) {
558
+ return this.http.post(`${orgPath(orgId)}/roles`, { body: params, ...options });
559
+ }
560
+ async list(orgId, params, options) {
561
+ return this.http.get(`${orgPath(orgId)}/roles`, {
562
+ query: pageQuery(params),
563
+ ...options
564
+ });
565
+ }
566
+ async get(orgId, roleId, options) {
567
+ return this.http.get(`${orgPath(orgId)}/roles/${seg(roleId)}`, options);
568
+ }
569
+ /**
570
+ * Renames a role and/or **replaces** its permissions. Everyone who holds it has the new
571
+ * permissions the next time they are read.
572
+ */
573
+ async update(orgId, roleId, params, options) {
574
+ return this.http.patch(`${orgPath(orgId)}/roles/${seg(roleId)}`, {
575
+ body: params,
576
+ ...options
577
+ });
578
+ }
579
+ /** Deletes a role. A role someone holds is refused with `409 conflict`. */
580
+ async delete(orgId, roleId, options) {
581
+ return this.http.delete(`${orgPath(orgId)}/roles/${seg(roleId)}`, options);
582
+ }
583
+ };
584
+ var MembersResource = class {
585
+ constructor(http) {
586
+ this.http = http;
587
+ }
588
+ /**
589
+ * Gives an end user a role in the organization. `404` if the identity is not one of
590
+ * your end users, `409` if they already are a member (change the role with
591
+ * {@link update}).
592
+ */
593
+ async add(orgId, params, options) {
594
+ const body = {
595
+ identity_id: params.identity_id,
596
+ role_id: params.role_id
597
+ };
598
+ if (params.expires_at !== void 0) body.expires_at = isoOrNull(params.expires_at);
599
+ return this.http.post(`${orgPath(orgId)}/members`, { body, ...options });
600
+ }
601
+ async list(orgId, params, options) {
602
+ return this.http.get(`${orgPath(orgId)}/members`, {
603
+ query: pageQuery(params),
604
+ ...options
605
+ });
606
+ }
607
+ /**
608
+ * Changes a member's role and/or deadline. `expires_at`: leave it out to keep the
609
+ * deadline, `null` to remove it, a time to set it.
610
+ */
611
+ async update(orgId, identityId, params, options) {
612
+ const body = {};
613
+ if (params.role_id !== void 0) body.role_id = params.role_id;
614
+ if (params.expires_at !== void 0) body.expires_at = isoOrNull(params.expires_at);
615
+ return this.http.patch(`${orgPath(orgId)}/members/${seg(identityId)}`, {
616
+ body,
617
+ ...options
618
+ });
619
+ }
620
+ /** Removes the membership. The person stays your end user. */
621
+ async remove(orgId, identityId, options) {
622
+ return this.http.delete(`${orgPath(orgId)}/members/${seg(identityId)}`, options);
623
+ }
624
+ };
625
+
626
+ // src/resources/gate/sessions.ts
627
+ var SessionsResource = class {
628
+ constructor(http) {
629
+ this.http = http;
630
+ }
631
+ /** The active sessions of one of your end users (a bare array, not paged). */
632
+ async list(identityId, options) {
633
+ return this.http.get(`/v1/gate/identities/${seg(identityId)}/sessions`, options);
634
+ }
635
+ /**
636
+ * Revokes every session of an end user — "log out everywhere". Signed tokens already
637
+ * issued keep verifying until their own `exp` (at most 15 minutes).
638
+ *
639
+ * Idempotent: a user of yours with no session at all is a success too. An id that is not
640
+ * one of your users — another account's, unknown, or one you deleted — is
641
+ * `403 forbidden`. `404 not_found` is rare: a user removed from the identity service
642
+ * other than through `identities.delete()`, which Gate still remembered as yours.
643
+ */
644
+ async revokeAll(identityId, options) {
645
+ return this.http.delete(`/v1/gate/identities/${seg(identityId)}/sessions`, options);
646
+ }
647
+ /**
648
+ * Revokes one session by its id. Revoking an already revoked session of your user is a
649
+ * success too; an id that is not one of your users' sessions is `404`.
650
+ */
651
+ async revoke(sessionId, options) {
652
+ return this.http.delete(`/v1/gate/sessions/${seg(sessionId)}`, options);
653
+ }
654
+ /**
655
+ * Whether an end user's session meets what your tenant requires — the one call to
656
+ * gate your own routes on. Gate on `mfa_satisfied`. Also lists the user's
657
+ * organizations in the key's project, read now.
658
+ *
659
+ * Throws `VerneAPIError` with `status 401` for a dead, foreign or unknown session, and
660
+ * `code 'mfa_required'` (`403`) when a second factor is owed: start a step-up flow with
661
+ * `gate.createLoginFlow({ aal: 'aal2', session_token })`.
662
+ */
663
+ async check(sessionToken, options) {
664
+ return this.http.get("/v1/gate/auth/session", { sessionToken, ...options });
280
665
  }
281
666
  };
282
667
 
@@ -286,7 +671,8 @@ var SettingsResource = class {
286
671
  this.http = http;
287
672
  }
288
673
  /**
289
- * Returns the tenant's security settings (passwordless / MFA).
674
+ * Returns the tenant's security settings (passwordless / MFA). With `mfa_enabled`,
675
+ * `sessions.check()` refuses a session without a second factor (`403 mfa_required`).
290
676
  */
291
677
  async getSecurity(options) {
292
678
  return this.http.get("/v1/gate/settings/security", options);
@@ -333,29 +719,258 @@ var SettingsResource = class {
333
719
  );
334
720
  return res.providers;
335
721
  }
722
+ /**
723
+ * The project's social-login return URLs — the addresses on your backend that receive
724
+ * the user (with a one-time `code`) after a social login. The first is the default.
725
+ */
726
+ async getReturnUrls(options) {
727
+ const res = await this.http.get(
728
+ "/v1/gate/settings/return-urls",
729
+ options
730
+ );
731
+ return res.return_urls;
732
+ }
733
+ /**
734
+ * Replaces the project's return URLs (at most 10; an empty list switches the feature
735
+ * off) and returns them as they will be matched — exactly, never by prefix. `https`
736
+ * only, except `http://localhost` and `http://127.0.0.1`; no query, fragment, `@` or
737
+ * wildcard (`400 invalid_payload` names the entry).
738
+ */
739
+ async updateReturnUrls(urls, options) {
740
+ const res = await this.http.put("/v1/gate/settings/return-urls", {
741
+ body: { return_urls: urls },
742
+ ...options
743
+ });
744
+ return res.return_urls;
745
+ }
336
746
  };
337
747
 
748
+ // src/resources/gate/verifier.ts
749
+ var DEFAULT_JWKS_MAX_AGE_MS = 3e5;
750
+ var MIN_REFETCH_INTERVAL_MS = 3e4;
751
+ var DEFAULT_CLOCK_TOLERANCE_SECONDS = 10;
752
+ var DEFAULT_TIMEOUT_MS2 = 1e4;
753
+ var GateTokenVerifier = class {
754
+ constructor(config) {
755
+ this.keys = /* @__PURE__ */ new Map();
756
+ this.fetchedAt = 0;
757
+ this.maxAgeMs = DEFAULT_JWKS_MAX_AGE_MS;
758
+ if (!config.tenantId) {
759
+ throw new VerneError("GateTokenVerifier needs your tenantId.");
760
+ }
761
+ const base = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
762
+ this.issuer = `${base}/auth/${config.tenantId}`;
763
+ this.jwksUrl = `${this.issuer}/.well-known/jwks.json`;
764
+ this.toleranceSeconds = config.clockToleranceSeconds ?? DEFAULT_CLOCK_TOLERANCE_SECONDS;
765
+ this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
766
+ }
767
+ /**
768
+ * Verifies `token` and returns its claims. Throws {@link VerneTokenError} (with a
769
+ * `reason`) when the token must not be trusted.
770
+ */
771
+ async verify(token) {
772
+ const parts = typeof token === "string" ? token.split(".") : [];
773
+ if (parts.length !== 3) {
774
+ throw new VerneTokenError("malformed", "Not a compact JWT.");
775
+ }
776
+ const [rawHeader, rawPayload, rawSignature] = parts;
777
+ const header = decodeJson(rawHeader, "header");
778
+ const payload = decodeJson(rawPayload, "payload");
779
+ if (header.alg !== "ES256") {
780
+ throw new VerneTokenError(
781
+ "unsupported_alg",
782
+ `Token algorithm ${JSON.stringify(header.alg)} is not accepted; Gate signs with ES256.`
783
+ );
784
+ }
785
+ if (typeof header.kid !== "string" || header.kid === "") {
786
+ throw new VerneTokenError("malformed", "Token header has no `kid`.");
787
+ }
788
+ const key = await this.keyFor(header.kid);
789
+ const signature = base64UrlDecode(rawSignature);
790
+ if (!signature || signature.length !== 64) {
791
+ throw new VerneTokenError("bad_signature", "Token signature is not a P-256 signature.");
792
+ }
793
+ const subtle = await subtleCrypto();
794
+ const valid = await subtle.verify(
795
+ { name: "ECDSA", hash: "SHA-256" },
796
+ key,
797
+ signature,
798
+ new TextEncoder().encode(`${rawHeader}.${rawPayload}`)
799
+ );
800
+ if (!valid) {
801
+ throw new VerneTokenError("bad_signature", "Token signature does not verify.");
802
+ }
803
+ if (payload.iss !== this.issuer) {
804
+ throw new VerneTokenError(
805
+ "wrong_issuer",
806
+ `Token issuer ${JSON.stringify(payload.iss)} is not ${this.issuer}.`
807
+ );
808
+ }
809
+ const now = Math.floor(Date.now() / 1e3);
810
+ if (typeof payload.exp !== "number") {
811
+ throw new VerneTokenError("missing_claim", "Token has no `exp`.");
812
+ }
813
+ if (now > payload.exp + this.toleranceSeconds) {
814
+ throw new VerneTokenError("expired", "Token has expired.");
815
+ }
816
+ if (typeof payload.nbf === "number" && payload.nbf > now + this.toleranceSeconds) {
817
+ throw new VerneTokenError("not_yet_valid", "Token is not valid yet.");
818
+ }
819
+ if (typeof payload.sub !== "string" || payload.sub === "") {
820
+ throw new VerneTokenError("missing_claim", "Token has no `sub`.");
821
+ }
822
+ return payload;
823
+ }
824
+ async keyFor(kid) {
825
+ const stale = Date.now() - this.fetchedAt > this.maxAgeMs;
826
+ if (stale) {
827
+ await this.refresh(this.keys.size === 0);
828
+ }
829
+ let key = this.keys.get(kid);
830
+ if (!key && Date.now() - this.fetchedAt >= MIN_REFETCH_INTERVAL_MS) {
831
+ await this.refresh(true);
832
+ key = this.keys.get(kid);
833
+ }
834
+ if (!key) {
835
+ throw new VerneTokenError("unknown_kid", `No key ${kid} in ${this.jwksUrl}.`);
836
+ }
837
+ return key;
838
+ }
839
+ /**
840
+ * Fetches the key set. With `required` false a failure keeps the keys already cached
841
+ * (a stale key set is still the right one unless a token says otherwise).
842
+ */
843
+ async refresh(required) {
844
+ this.inflight ?? (this.inflight = this.load().finally(() => {
845
+ this.inflight = void 0;
846
+ }));
847
+ try {
848
+ await this.inflight;
849
+ } catch (err) {
850
+ if (required) throw err;
851
+ }
852
+ }
853
+ async load() {
854
+ let response;
855
+ const controller = new AbortController();
856
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
857
+ try {
858
+ response = await fetch(this.jwksUrl, {
859
+ headers: { Accept: "application/json" },
860
+ signal: controller.signal
861
+ });
862
+ } catch (err) {
863
+ throw new VerneTokenError(
864
+ "jwks_unavailable",
865
+ `Could not fetch ${this.jwksUrl}: ${err instanceof Error ? err.message : String(err)}`
866
+ );
867
+ } finally {
868
+ clearTimeout(timer);
869
+ }
870
+ if (!response.ok) {
871
+ throw new VerneTokenError(
872
+ "jwks_unavailable",
873
+ `${this.jwksUrl} answered HTTP ${response.status}.`
874
+ );
875
+ }
876
+ let body;
877
+ try {
878
+ body = await response.json();
879
+ } catch {
880
+ throw new VerneTokenError("jwks_unavailable", `${this.jwksUrl} did not answer JSON.`);
881
+ }
882
+ const jwks = body.keys;
883
+ if (!Array.isArray(jwks)) {
884
+ throw new VerneTokenError("jwks_unavailable", `${this.jwksUrl} has no \`keys\`.`);
885
+ }
886
+ const subtle = await subtleCrypto();
887
+ const keys = /* @__PURE__ */ new Map();
888
+ for (const jwk of jwks) {
889
+ if (jwk.kty !== "EC" || jwk.crv !== "P-256" || typeof jwk.kid !== "string") continue;
890
+ if (jwk.use !== void 0 && jwk.use !== "sig") continue;
891
+ const { kty, crv, x, y } = jwk;
892
+ try {
893
+ keys.set(
894
+ jwk.kid,
895
+ await subtle.importKey(
896
+ "jwk",
897
+ { kty, crv, x, y },
898
+ { name: "ECDSA", namedCurve: "P-256" },
899
+ false,
900
+ ["verify"]
901
+ )
902
+ );
903
+ } catch {
904
+ }
905
+ }
906
+ this.keys = keys;
907
+ this.fetchedAt = Date.now();
908
+ this.maxAgeMs = maxAgeMs(response.headers.get("Cache-Control"));
909
+ }
910
+ };
911
+ function maxAgeMs(cacheControl) {
912
+ const match = cacheControl ? /max-age=(\d+)/i.exec(cacheControl) : null;
913
+ return match ? Number(match[1]) * 1e3 : DEFAULT_JWKS_MAX_AGE_MS;
914
+ }
915
+ function decodeJson(segment, what) {
916
+ const bytes = base64UrlDecode(segment);
917
+ if (bytes) {
918
+ try {
919
+ const value = JSON.parse(new TextDecoder().decode(bytes));
920
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
921
+ return value;
922
+ }
923
+ } catch {
924
+ }
925
+ }
926
+ throw new VerneTokenError("malformed", `Token ${what} is not base64url JSON.`);
927
+ }
928
+ function base64UrlDecode(segment) {
929
+ if (!/^[A-Za-z0-9_-]*$/.test(segment)) return null;
930
+ const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
931
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
932
+ try {
933
+ const binary = atob(padded);
934
+ const bytes = new Uint8Array(binary.length);
935
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
936
+ return bytes;
937
+ } catch {
938
+ return null;
939
+ }
940
+ }
941
+ var subtlePromise;
942
+ function subtleCrypto() {
943
+ subtlePromise ?? (subtlePromise = (async () => {
944
+ const global = globalThis.crypto;
945
+ if (global?.subtle) return global.subtle;
946
+ const nodeCrypto = await import('crypto');
947
+ return nodeCrypto.webcrypto.subtle;
948
+ })());
949
+ return subtlePromise;
950
+ }
951
+
338
952
  // src/resources/gate/tokens.ts
339
953
  var TokensResource = class {
340
- constructor(http, apiKey) {
954
+ constructor(http) {
341
955
  this.http = http;
342
- this.apiKey = apiKey;
956
+ this.verifiers = /* @__PURE__ */ new Map();
343
957
  }
344
958
  /**
345
- * Exchanges the Gate API key for a short-lived access token.
959
+ * Exchanges the Gate API key for a short-lived access token (`gat_live_…`), for
960
+ * `subject` — a user or a service account. Answers `201`.
346
961
  *
347
- * Note: This endpoint does not use the Authorization header — the API key
348
- * is passed in the request body per the Gate API specification.
962
+ * The key travels in the `Authorization` header like on every other call. (Until 1.4.0
963
+ * this SDK sent it in the body without the header, which the API refuses with `401`.)
349
964
  */
350
965
  async create(params, options) {
351
- return this.http.post("/v1/gate/tokens", {
352
- body: { api_key: this.apiKey, ...params },
353
- skipAuth: true,
354
- ...options
355
- });
966
+ return this.http.post("/v1/gate/tokens", { body: params, ...options });
356
967
  }
357
968
  /**
358
- * Validates a Gate access token and returns its decoded attributes.
969
+ * Validates a Gate access token and returns its attributes. An unknown, expired or
970
+ * foreign token, or one whose user you deactivated or deleted, is `{ active: false }`.
971
+ *
972
+ * When the subject is an end user, `orgs` lists their organizations in the key's
973
+ * project, read at the moment you ask.
359
974
  */
360
975
  async introspect(accessToken, options) {
361
976
  return this.http.post("/v1/gate/tokens/introspect", {
@@ -363,6 +978,51 @@ var TokensResource = class {
363
978
  ...options
364
979
  });
365
980
  }
981
+ /**
982
+ * Gets a **signed JWT** (ES256) for a signed-in end user's session, for a platform that
983
+ * verifies signatures instead of calling Gate (Supabase, an API gateway). The session
984
+ * token is the credential: no API key is sent.
985
+ *
986
+ * The token is valid for 15 minutes and **cannot be revoked**; its `orgs` claim is as old
987
+ * as the token. Every call mints a new one — ask again before the one you hold expires.
988
+ * A revoked or expired session, or a deactivated user, is a `401`.
989
+ *
990
+ * @example
991
+ * const { token } = await gate.tokens.createSigned(sessionToken);
992
+ */
993
+ async createSigned(sessionToken, options) {
994
+ const res = await this.http.get("/auth/sessions/whoami", {
995
+ query: { tokenize_as: "gate_jwt" },
996
+ sessionToken,
997
+ skipAuth: true,
998
+ ...options
999
+ });
1000
+ const { tokenized, ...session } = res;
1001
+ return { token: tokenized, session };
1002
+ }
1003
+ /**
1004
+ * Verifies a Gate signed token offline against your tenant's public keys (fetched once
1005
+ * and cached) and returns its claims. Throws `VerneTokenError` when the token must not
1006
+ * be trusted — wrong issuer (another tenant), bad signature, expired, any algorithm but
1007
+ * ES256. See {@link GateTokenVerifier} to verify without a Gate client.
1008
+ *
1009
+ * @example
1010
+ * const claims = await gate.tokens.verifySigned(token, { tenantId: 'your-tenant-id' });
1011
+ * claims.sub; // the end user's id
1012
+ */
1013
+ async verifySigned(token, options) {
1014
+ const cacheKey = `${options.tenantId}|${options.clockToleranceSeconds ?? ""}`;
1015
+ let verifier = this.verifiers.get(cacheKey);
1016
+ if (!verifier) {
1017
+ verifier = new GateTokenVerifier({
1018
+ tenantId: options.tenantId,
1019
+ baseUrl: this.http.baseUrl,
1020
+ clockToleranceSeconds: options.clockToleranceSeconds
1021
+ });
1022
+ this.verifiers.set(cacheKey, verifier);
1023
+ }
1024
+ return verifier.verify(token);
1025
+ }
366
1026
  };
367
1027
 
368
1028
  // src/resources/gate/gate.ts
@@ -374,20 +1034,31 @@ var Gate = class {
374
1034
  timeoutMs: config.timeoutMs
375
1035
  });
376
1036
  this.identities = new IdentitiesResource(this.http);
377
- this.tokens = new TokensResource(this.http, config.apiKey);
1037
+ this.tokens = new TokensResource(this.http);
378
1038
  this.settings = new SettingsResource(this.http);
1039
+ this.organizations = new OrganizationsResource(this.http);
1040
+ this.sessions = new SessionsResource(this.http);
1041
+ this.auditLog = new AuditLogResource(this.http);
1042
+ this.lifecycle = new LifecycleResource(this.http);
379
1043
  }
380
1044
  /**
381
- * Checks whether a given subject is allowed to perform an action on a resource.
382
- * This is the core building block for enforcing authorization in your services.
1045
+ * Asks whether a subject may perform an action on a resource. The answer is a decision;
1046
+ * your service enforces it.
1047
+ *
1048
+ * - `resource: 'organization:<id>'` — may this end user do `action` (a permission) in
1049
+ * that organization? `reason` is one of a fixed set and `role` is the role they hold.
1050
+ * Prefer `gate.organizations.authorize()` / `can()`, which build the resource.
1051
+ * - any other resource — only *is `subject` one of your active end users?*; `reason` is
1052
+ * `owned_by_tenant` (allowed), `subject_inactive`, `not_owned_by_tenant` or
1053
+ * `not_a_known_identity`, and `message` says it in a sentence for your logs.
383
1054
  *
384
1055
  * @example
385
- * const result = await gate.authorize({
386
- * subject: 'usr_123',
387
- * action: 'relay.messages.read',
388
- * resource: 'tenant:ten_001',
1056
+ * const decision = await gate.authorize({
1057
+ * subject: userId,
1058
+ * action: 'invoices:write',
1059
+ * resource: `organization:${orgId}`,
389
1060
  * });
390
- * if (!result.allowed) throw new Error('Forbidden');
1061
+ * if (!decision.allowed) throw new Error('Forbidden');
391
1062
  */
392
1063
  async authorize(params, options) {
393
1064
  return this.http.post("/v1/gate/authorize", { body: params, ...options });
@@ -403,23 +1074,47 @@ var Gate = class {
403
1074
  */
404
1075
  async getEnabledProviders(tenantId, options) {
405
1076
  const res = await this.http.get(
406
- `/public/gate/providers/${encodeURIComponent(tenantId)}`,
1077
+ `/public/gate/providers/${seg(tenantId)}`,
407
1078
  { skipAuth: true, ...options }
408
1079
  );
409
1080
  return res.providers;
410
1081
  }
411
1082
  /**
412
- * Initializes a Kratos login flow using your Gate API key. Call this from
413
- * your server and pass the returned flow JSON to your browser-side code to
414
- * render social login buttons. The flow already contains only the providers
415
- * your tenant has enabled.
1083
+ * Starts a Kratos login flow with your Gate API key and returns it; render its
1084
+ * `ui.nodes` or post credentials to its `ui.action`. With a return URL registered,
1085
+ * pass `return_to` and always a `state` for social login. A step-up (second factor) is
1086
+ * `{ aal: 'aal2', session_token }`.
416
1087
  *
417
1088
  * @example
418
- * const flow = await gate.createLoginFlow();
419
- * // send `flow` to the browser as JSON
1089
+ * const flow = await gate.createLoginFlow({ state: csrfBoundValue });
420
1090
  */
421
- async createLoginFlow(options) {
422
- return this.http.get("/v1/gate/auth/login", options);
1091
+ async createLoginFlow(params, options) {
1092
+ return this.http.get("/v1/gate/auth/login", {
1093
+ query: { return_to: params?.return_to, state: params?.state, aal: params?.aal },
1094
+ sessionToken: params?.session_token,
1095
+ ...options
1096
+ });
1097
+ }
1098
+ /**
1099
+ * Starts a Kratos registration flow. Refused with `402 ACTIVE_USER_LIMIT_REACHED` when
1100
+ * the account is past its active-user limit and the month to move up has ended.
1101
+ */
1102
+ async createRegistrationFlow(params, options) {
1103
+ return this.http.get("/v1/gate/auth/registration", {
1104
+ query: { return_to: params?.return_to, state: params?.state },
1105
+ ...options
1106
+ });
1107
+ }
1108
+ /**
1109
+ * Exchanges the one-time `code` your return URL received after a social login for the
1110
+ * user's session. The code works once, for about 60 seconds, and only with a key of the
1111
+ * project that started the login (otherwise `404`). Keep the session token server-side.
1112
+ */
1113
+ async exchangeOidcCode(code, options) {
1114
+ return this.http.post("/v1/gate/auth/oidc/exchange", {
1115
+ body: { code },
1116
+ ...options
1117
+ });
423
1118
  }
424
1119
  };
425
1120
 
@@ -448,6 +1143,10 @@ var Passepartout = class {
448
1143
  * Returns the current state of a login attempt. While the user hasn't
449
1144
  * finished, `status` is `pending`; once done it becomes `completed` and
450
1145
  * carries the `access_token` and `user`.
1146
+ *
1147
+ * Throws `VerneAPIError` with `403 account_disabled` when the person is deactivated,
1148
+ * and `402 ACTIVE_USER_LIMIT_REACHED` when a *new* person signs in past the account's
1149
+ * active-user limit (retrying does not help; existing users are unaffected).
451
1150
  */
452
1151
  async loginStatus(nonce, options) {
453
1152
  return this.http.get(
@@ -474,7 +1173,13 @@ var MessagesResource = class {
474
1173
  }
475
1174
  /**
476
1175
  * Publish an event to all endpoints subscribed to the given event_type.
477
- * Automatically retries once on 429 (respects Retry-After header).
1176
+ * Retries once on a short `429` (a `Retry-After` of a minute or less). The monthly
1177
+ * message limit (`429 RELAY_MESSAGE_LIMIT_REACHED`, `Retry-After` until the 1st) is
1178
+ * thrown at once, with `retryAfter` on the error.
1179
+ *
1180
+ * Safe to retry beyond that, if you pass an `idempotency_key`: a repeat
1181
+ * returns the originally accepted message rather than creating a second event
1182
+ * or failing.
478
1183
  *
479
1184
  * @returns The accepted message with its assigned ID and timestamp.
480
1185
  */
@@ -485,12 +1190,12 @@ var MessagesResource = class {
485
1190
  * Returns a paginated list of previously sent events for the current tenant.
486
1191
  */
487
1192
  async list(params, options) {
488
- const query = buildQuery(params);
1193
+ const query = buildQuery2(params);
489
1194
  const path = query ? `/v1/relay/messages?${query}` : "/v1/relay/messages";
490
1195
  return this.http.get(path, options);
491
1196
  }
492
1197
  };
493
- function buildQuery(params) {
1198
+ function buildQuery2(params) {
494
1199
  if (!params) return "";
495
1200
  const searchParams = new URLSearchParams();
496
1201
  for (const [k, v] of Object.entries(params)) {
@@ -512,75 +1217,56 @@ var Relay = class {
512
1217
  };
513
1218
 
514
1219
  // src/verne.ts
1220
+ var SERVICE_LABELS = {
1221
+ relay: "Relay",
1222
+ gate: "Gate",
1223
+ passepartout: "Passepartout",
1224
+ clockwork: "Clockwork"
1225
+ };
515
1226
  var Verne = class {
516
1227
  constructor(config) {
517
- if (!config.relay && !config.gate && !config.passepartout && !config.clockwork) {
1228
+ if (!config.apiKey && !config.relay && !config.gate && !config.passepartout && !config.clockwork) {
518
1229
  throw new VerneError(
519
- "At least one service key (relay, gate, passepartout, or clockwork) must be provided."
1230
+ "No API key provided. Pass `apiKey` (or a per-service key: relay, gate, passepartout, clockwork)."
520
1231
  );
521
1232
  }
522
1233
  this.config = config;
523
1234
  }
524
1235
  get relay() {
525
- if (!this.config.relay) {
526
- throw new VerneError(
527
- "Relay API key not provided. Pass `relay` in the Verne constructor config."
528
- );
529
- }
530
- this._relay ?? (this._relay = new Relay({
531
- apiKey: this.config.relay,
532
- baseUrl: this.config.baseUrl,
533
- timeoutMs: this.config.timeoutMs
534
- }));
1236
+ this._relay ?? (this._relay = new Relay(this.serviceConfig("relay")));
535
1237
  return this._relay;
536
1238
  }
537
1239
  get gate() {
538
- if (!this.config.gate) {
539
- throw new VerneError(
540
- "Gate API key not provided. Pass `gate` in the Verne constructor config."
541
- );
542
- }
543
- this._gate ?? (this._gate = new Gate({
544
- apiKey: this.config.gate,
545
- baseUrl: this.config.baseUrl,
546
- timeoutMs: this.config.timeoutMs
547
- }));
1240
+ this._gate ?? (this._gate = new Gate(this.serviceConfig("gate")));
548
1241
  return this._gate;
549
1242
  }
550
1243
  get passepartout() {
551
- if (!this.config.passepartout) {
552
- throw new VerneError(
553
- "Passepartout API key not provided. Pass `passepartout` in the Verne constructor config."
554
- );
555
- }
556
- this._passepartout ?? (this._passepartout = new Passepartout({
557
- apiKey: this.config.passepartout,
558
- baseUrl: this.config.baseUrl,
559
- timeoutMs: this.config.timeoutMs
560
- }));
1244
+ this._passepartout ?? (this._passepartout = new Passepartout(this.serviceConfig("passepartout")));
561
1245
  return this._passepartout;
562
1246
  }
563
1247
  get clockwork() {
564
- if (!this.config.clockwork) {
1248
+ this._clockwork ?? (this._clockwork = new Clockwork(this.serviceConfig("clockwork")));
1249
+ return this._clockwork;
1250
+ }
1251
+ serviceConfig(service) {
1252
+ const apiKey = this.config[service] ?? this.config.apiKey;
1253
+ if (!apiKey) {
565
1254
  throw new VerneError(
566
- "Clockwork API key not provided. Pass `clockwork` in the Verne constructor config."
1255
+ `${SERVICE_LABELS[service]} API key not provided. Pass \`apiKey\` (or \`${service}\`) in the Verne constructor config.`
567
1256
  );
568
1257
  }
569
- this._clockwork ?? (this._clockwork = new Clockwork({
570
- apiKey: this.config.clockwork,
571
- baseUrl: this.config.baseUrl,
572
- timeoutMs: this.config.timeoutMs
573
- }));
574
- return this._clockwork;
1258
+ return { apiKey, baseUrl: this.config.baseUrl, timeoutMs: this.config.timeoutMs };
575
1259
  }
576
1260
  };
577
1261
 
578
1262
  exports.Clockwork = Clockwork;
579
1263
  exports.Gate = Gate;
1264
+ exports.GateTokenVerifier = GateTokenVerifier;
580
1265
  exports.Passepartout = Passepartout;
581
1266
  exports.Relay = Relay;
582
1267
  exports.Verne = Verne;
583
1268
  exports.VerneAPIError = VerneAPIError;
584
1269
  exports.VerneError = VerneError;
1270
+ exports.VerneTokenError = VerneTokenError;
585
1271
  //# sourceMappingURL=index.js.map
586
1272
  //# sourceMappingURL=index.js.map