@meistrari/auth-core 1.15.0 → 1.17.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.mjs CHANGED
@@ -8,7 +8,7 @@ import { defaultStatements } from 'better-auth/plugins/organization/access';
8
8
  import { z } from 'zod';
9
9
  export { APIError } from 'better-auth';
10
10
 
11
- const version = "1.15.0";
11
+ const version = "1.17.0";
12
12
 
13
13
  const statements = {
14
14
  ...defaultStatements,
@@ -61,6 +61,13 @@ const memberAdditionalFields = {
61
61
  defaultValue: null
62
62
  }
63
63
  };
64
+ const invitationAdditionalFields = {
65
+ applicationId: {
66
+ type: "string",
67
+ input: true,
68
+ required: false
69
+ }
70
+ };
64
71
  const JWTPayloadUser = z.object({
65
72
  id: z.string(),
66
73
  name: z.string(),
@@ -100,6 +107,12 @@ function applicationsPluginClient() {
100
107
  }
101
108
  };
102
109
  },
110
+ inviteUserToApplication: async (options) => {
111
+ return await $fetch("/applications/invitations", {
112
+ method: "POST",
113
+ body: options
114
+ });
115
+ },
103
116
  startAuthorizationFlow: async (applicationId, redirectUri, codeChallenge, organizationId) => {
104
117
  return await $fetch("/applications/authorize", {
105
118
  method: "POST",
@@ -171,12 +184,17 @@ function applicationsPluginClient() {
171
184
  headers
172
185
  });
173
186
  },
174
- whoAmI: async (accessToken) => {
187
+ whoAmI: async (accessToken, options = {}) => {
175
188
  const headers = new Headers();
176
189
  headers.set("x-tela-access-token", accessToken);
190
+ const query = {};
191
+ if (options.include && options.include.length > 0) {
192
+ query.include = options.include.join(",");
193
+ }
177
194
  return await $fetch("/applications/whoami", {
178
195
  method: "GET",
179
- headers
196
+ headers,
197
+ query
180
198
  });
181
199
  },
182
200
  switchOrganization: async (organizationId, accessToken) => {
@@ -255,7 +273,13 @@ function createAPIClient(apiUrl, fetchOptions = {}) {
255
273
  }
256
274
 
257
275
  class BaseError extends Error {
276
+ /** Machine-readable error code (e.g. `"INVALID_SOCIAL_PROVIDER"`). */
258
277
  code;
278
+ /**
279
+ * @param code - A machine-readable error code
280
+ * @param message - A human-readable error message
281
+ * @param options - Standard `ErrorOptions` (e.g. `cause`)
282
+ */
259
283
  constructor(code, message, options) {
260
284
  super(message, options);
261
285
  this.code = code;
@@ -374,6 +398,20 @@ class ApplicationService {
374
398
  }
375
399
  return response.data;
376
400
  }
401
+ /**
402
+ * Invites a user to an application through an organization entitlement.
403
+ *
404
+ * @param options - Invitation details including organization, application, email, and role
405
+ * @returns The created invitation
406
+ */
407
+ async inviteUserToApplication(options) {
408
+ const response = await this.client.applications.inviteUserToApplication(options);
409
+ const invitation = response.data;
410
+ if (!invitation) {
411
+ throw new Error("No invitation returned from application invitation endpoint");
412
+ }
413
+ return invitation;
414
+ }
377
415
  /**
378
416
  * Starts an authorization flow for a specific application.
379
417
  *
@@ -389,6 +427,16 @@ class ApplicationService {
389
427
  }
390
428
  return response.data;
391
429
  }
430
+ /**
431
+ * Starts a device authorization flow (RFC 8628).
432
+ *
433
+ * Returns a device code and user code that the end user must enter
434
+ * on the verification URI to authorize the device.
435
+ *
436
+ * @param requesterApplicationId - The application requesting authorization
437
+ * @param targetApplicationId - The target application to gain access to
438
+ * @returns Device authorization details including codes and verification URI
439
+ */
392
440
  async startDeviceAuthorizationFlow(requesterApplicationId, targetApplicationId) {
393
441
  const response = await this.client.applications.startDeviceAuthorizationFlow(requesterApplicationId, targetApplicationId);
394
442
  if (!response.data) {
@@ -396,6 +444,15 @@ class ApplicationService {
396
444
  }
397
445
  return response.data;
398
446
  }
447
+ /**
448
+ * Retrieves the context for a pending device authorization request.
449
+ *
450
+ * Used by the authorization page to display details about the requesting
451
+ * and target applications before the user approves or denies.
452
+ *
453
+ * @param userCode - The user code from the device authorization response
454
+ * @returns Context including application info, available organizations, and status
455
+ */
399
456
  async getDeviceAuthorizationContext(userCode) {
400
457
  const response = await this.client.applications.getDeviceAuthorizationContext(userCode);
401
458
  if (!response.data) {
@@ -403,6 +460,13 @@ class ApplicationService {
403
460
  }
404
461
  return response.data;
405
462
  }
463
+ /**
464
+ * Approves a pending device authorization request.
465
+ *
466
+ * @param userCode - The user code identifying the device authorization request
467
+ * @param organizationId - The organization to authorize the device for
468
+ * @returns Confirmation of the approval action
469
+ */
406
470
  async approveDeviceAuthorizationFlow(userCode, organizationId) {
407
471
  const response = await this.client.applications.approveDeviceAuthorizationFlow(userCode, organizationId);
408
472
  if (!response.data) {
@@ -410,6 +474,12 @@ class ApplicationService {
410
474
  }
411
475
  return response.data;
412
476
  }
477
+ /**
478
+ * Denies a pending device authorization request.
479
+ *
480
+ * @param userCode - The user code identifying the device authorization request
481
+ * @returns Confirmation of the denial action
482
+ */
413
483
  async denyDeviceAuthorizationFlow(userCode) {
414
484
  const response = await this.client.applications.denyDeviceAuthorizationFlow(userCode);
415
485
  if (!response.data) {
@@ -417,6 +487,21 @@ class ApplicationService {
417
487
  }
418
488
  return response.data;
419
489
  }
490
+ /**
491
+ * Exchanges a device code for access and refresh tokens.
492
+ *
493
+ * This should be called by the device client while polling. It will throw
494
+ * typed errors to indicate the polling state (pending, slow down, denied, expired).
495
+ *
496
+ * @param deviceCode - The device code obtained from {@link startDeviceAuthorizationFlow}
497
+ * @returns Access and refresh tokens along with user and organization info
498
+ *
499
+ * @throws {DeviceAuthorizationPendingError} The user hasn't authorized yet — keep polling
500
+ * @throws {DeviceAuthorizationSlowDownError} Polling too fast — increase the interval
501
+ * @throws {DeviceAccessDeniedError} The user denied the request
502
+ * @throws {DeviceCodeExpiredError} The device code has expired
503
+ * @throws {DeviceTransientServerError} Transient server error — safe to retry
504
+ */
420
505
  async exchangeDeviceCodeForTokens(deviceCode) {
421
506
  try {
422
507
  const response = await this.client.applications.exchangeDeviceCodeForTokens(deviceCode);
@@ -474,11 +559,16 @@ class ApplicationService {
474
559
  /**
475
560
  * Gets the current user and organization for a specific application.
476
561
  *
562
+ * By default, the returned organization does not include its `members`,
563
+ * `teams`, or `invitations` collections. Pass `options.include` to request
564
+ * any of them explicitly.
565
+ *
477
566
  * @param accessToken - The access token to use for the who am I request
567
+ * @param options - Optional include list controlling which relations are loaded
478
568
  * @returns The current user and organization
479
569
  */
480
- async whoAmI(accessToken) {
481
- const response = await this.client.applications.whoAmI(accessToken);
570
+ async whoAmI(accessToken, options = {}) {
571
+ const response = await this.client.applications.whoAmI(accessToken, options);
482
572
  if (!response.data) {
483
573
  throw new Error("No data returned from the API", { cause: response.error });
484
574
  }
@@ -560,10 +650,7 @@ class OrganizationService {
560
650
  /**
561
651
  * Updates an organization's details.
562
652
  *
563
- * @param payload - The organization fields to update
564
- * @param payload.name - New organization name
565
- * @param payload.logo - New organization logo URL
566
- * @param payload.settings - New organization settings
653
+ * @param payload - The organization fields to update (name, logo, settings)
567
654
  * @returns The updated organization
568
655
  */
569
656
  async updateOrganization(payload) {
@@ -604,16 +691,19 @@ class OrganizationService {
604
691
  * @param options.userEmail - Email address of the user to invite
605
692
  * @param options.role - Role to assign to the invited user
606
693
  * @param options.teamId - Team ID to add the user to
694
+ * @param options.applicationId - Legacy application scope; prefer applications.inviteUserToApplication for new app-scoped invites
607
695
  * @param options.resend - Whether to resend if invitation already exists
608
696
  * @returns The created invitation
609
697
  */
610
- async inviteUserToOrganization({ userEmail, role, teamId, resend }) {
611
- return await this.client.organization.inviteMember({
698
+ async inviteUserToOrganization({ userEmail, role, teamId, resend, applicationId }) {
699
+ const invitation = {
612
700
  email: userEmail,
613
701
  role,
614
702
  teamId,
615
- resend: resend ?? false
616
- });
703
+ resend: resend ?? false,
704
+ applicationId
705
+ };
706
+ return await this.client.organization.inviteMember(invitation);
617
707
  }
618
708
  /**
619
709
  * Cancels a pending organization invitation.
@@ -629,18 +719,19 @@ class OrganizationService {
629
719
  * Accepts an organization invitation.
630
720
  *
631
721
  * @param id - The invitation ID to accept
722
+ * @returns Object containing the application's `homeUrl` when the invitation is
723
+ * scoped to an application that defines one; otherwise `null`.
632
724
  */
633
725
  async acceptInvitation(id) {
634
- await this.client.organization.acceptInvitation({
726
+ const response = await this.client.organization.acceptInvitation({
635
727
  invitationId: id
636
728
  });
729
+ return { homeUrl: response?.homeUrl ?? null };
637
730
  }
638
731
  /**
639
732
  * Removes a user from the active organization.
640
733
  *
641
- * @param options - User identifier (either memberId or userEmail must be provided)
642
- * @param options.memberId - The member ID to remove
643
- * @param options.userEmail - The user email to remove
734
+ * @param options - Provide either `memberId` or `userEmail` to identify the user
644
735
  */
645
736
  async removeUserFromOrganization({ memberId, userEmail }) {
646
737
  await this.client.organization.removeMember({
@@ -663,8 +754,7 @@ class OrganizationService {
663
754
  /**
664
755
  * Creates a new team within the active organization.
665
756
  *
666
- * @param payload - Team configuration
667
- * @param payload.name - The name of the team
757
+ * @param payload - Team configuration with a `name` field
668
758
  * @returns The created team
669
759
  */
670
760
  async createTeam(payload) {
@@ -674,8 +764,7 @@ class OrganizationService {
674
764
  * Updates an existing team's details.
675
765
  *
676
766
  * @param id - The team ID to update
677
- * @param payload - Team fields to update
678
- * @param payload.name - The new team name
767
+ * @param payload - Team fields to update (currently supports `name`)
679
768
  * @returns The updated team
680
769
  */
681
770
  async updateTeam(id, payload) {
@@ -934,6 +1023,15 @@ class ApiKeyService {
934
1023
  constructor(client) {
935
1024
  this.client = client;
936
1025
  }
1026
+ /**
1027
+ * Creates a new API key.
1028
+ *
1029
+ * The returned object includes the secret `key` value, which is only
1030
+ * available at creation time and cannot be retrieved later.
1031
+ *
1032
+ * @param payload - The API key configuration
1033
+ * @returns The created API key including the secret key value
1034
+ */
937
1035
  async createApiKey(payload) {
938
1036
  return await this.client.apiKey.create({
939
1037
  name: payload.name,
@@ -942,6 +1040,12 @@ class ApiKeyService {
942
1040
  metadata: payload.metadata
943
1041
  });
944
1042
  }
1043
+ /**
1044
+ * Retrieves an API key by its ID.
1045
+ *
1046
+ * @param id - The API key ID
1047
+ * @returns The API key (without the secret key value)
1048
+ */
945
1049
  async getApiKey(id) {
946
1050
  return await this.client.apiKey.get({
947
1051
  query: {
@@ -949,18 +1053,39 @@ class ApiKeyService {
949
1053
  }
950
1054
  });
951
1055
  }
1056
+ /**
1057
+ * Lists all API keys owned by the authenticated user.
1058
+ *
1059
+ * @returns An object containing the API keys array and pagination metadata
1060
+ */
952
1061
  async listApiKeys() {
953
1062
  return await this.client.apiKey.list();
954
1063
  }
1064
+ /**
1065
+ * Lists all API keys belonging to the active organization.
1066
+ *
1067
+ * @returns An array of API keys (without secret key values)
1068
+ */
955
1069
  async listOrganizationApiKeys() {
956
1070
  return await this.client.customEndpoints.organizations.apiKeys();
957
1071
  }
1072
+ /**
1073
+ * Updates an existing API key.
1074
+ *
1075
+ * @param payload - The fields to update
1076
+ * @returns The updated API key (without the secret key value)
1077
+ */
958
1078
  async updateApiKey(payload) {
959
1079
  return await this.client.apiKey.update({
960
1080
  keyId: payload.id,
961
1081
  name: payload.name
962
1082
  });
963
1083
  }
1084
+ /**
1085
+ * Permanently deletes an API key.
1086
+ *
1087
+ * @param id - The API key ID to delete
1088
+ */
964
1089
  async deleteApiKey(id) {
965
1090
  return await this.client.apiKey.delete({
966
1091
  keyId: id
@@ -1034,4 +1159,4 @@ function extractTokenPayload(token) {
1034
1159
  return payload;
1035
1160
  }
1036
1161
 
1037
- export { ApplicationError, AuthClient, AuthorizationFlowError, DeviceAccessDeniedError, DeviceAuthorizationPendingError, DeviceAuthorizationSlowDownError, DeviceCodeExpiredError, DeviceTransientServerError, EmailRequired, InvalidCallbackURL, InvalidSocialProvider, JWTPayload, JWTPayloadUser, JWTPayloadWorkspace, RefreshTokenExpiredError, Roles, UserNotLoggedInError, ac, extractTokenPayload, isTokenExpired, memberAdditionalFields, organizationAdditionalFields, rolesAccessControl, userAdditionalFields, validateToken };
1162
+ export { ApplicationError, AuthClient, AuthorizationFlowError, DeviceAccessDeniedError, DeviceAuthorizationPendingError, DeviceAuthorizationSlowDownError, DeviceCodeExpiredError, DeviceTransientServerError, EmailRequired, InvalidCallbackURL, InvalidSocialProvider, JWTPayload, JWTPayloadUser, JWTPayloadWorkspace, RefreshTokenExpiredError, Roles, UserNotLoggedInError, ac, extractTokenPayload, invitationAdditionalFields, isTokenExpired, memberAdditionalFields, organizationAdditionalFields, rolesAccessControl, userAdditionalFields, validateToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/auth-core",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -14,7 +14,8 @@
14
14
  "dist"
15
15
  ],
16
16
  "scripts": {
17
- "build": "unbuild"
17
+ "build": "unbuild",
18
+ "docs": "typedoc"
18
19
  },
19
20
  "dependencies": {
20
21
  "@better-auth/api-key": "1.5.4",
@@ -25,6 +26,8 @@
25
26
  },
26
27
  "devDependencies": {
27
28
  "@types/node": "latest",
29
+ "typedoc": "0.28.18",
30
+ "typedoc-plugin-markdown": "4.11.0",
28
31
  "typescript": "5.9.2",
29
32
  "unbuild": "3.6.1"
30
33
  }