@protobase/client 0.22.0 → 0.22.2

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/auth.d.ts CHANGED
@@ -2,8 +2,9 @@ import { SignInPolicy, SignInPolicyState } from "./sign-in-policy.js";
2
2
  import { AccountSignIn, AuthenticatorSetup, Passkey } from "./account-security.js";
3
3
  import { CurrentOrganization, InvitationPreview, InvitationSent, Organization, OrganizationInvitation, OrganizationMember, OrganizationRole, OrganizationsFound } from "./organizations.js";
4
4
  import { ActiveRole } from "./roles.js";
5
+ import { AuthUser, RoleLabel, SetupStatus, SignInResult, TwoFactorMethod } from "./auth-types.js";
6
+ import { AppUser, UserInvitation, UserInvitationSent, UsersFound } from "./users.js";
5
7
  import { StaffSignIn } from "./staff-sign-in.js";
6
- import { AuthUser, SetupStatus, SignInResult, TwoFactorMethod } from "./auth-types.js";
7
8
  import { AuthError } from "./auth-error.js";
8
9
  //#region src/auth.d.ts
9
10
  export type AuthSessionOptions = {
@@ -22,8 +23,8 @@ export declare const tokenExpiry: (token: string) => number;
22
23
  /**
23
24
  * The browser side of Better Auth: sign in with a password, an emailed code or a passkey (with a second step for an
24
25
  * account with two-factor authentication), the account's passkeys and two-factor authentication (`account`), the
25
- * sign-in policy for admins (`signInPolicy`), staff of the operator signing in as people (`staff`), organizations
26
- * (`organizations`), the role the session acts with (`roles`), and a short-lived
26
+ * sign-in policy for admins (`signInPolicy`), the people who can sign in for admins (`users`), staff of the operator
27
+ * signing in as people (`staff`), organizations (`organizations`), the role the session acts with (`roles`), and a short-lived
27
28
  * JWT held in memory only (never in storage), fetched with the session cookie and refreshed before it expires.
28
29
  */
29
30
  export declare const createAuthSession: (options?: AuthSessionOptions) => {
@@ -107,6 +108,25 @@ export declare const createAuthSession: (options?: AuthSessionOptions) => {
107
108
  read: () => Promise<SignInPolicyState>;
108
109
  save: (policy: SignInPolicy) => Promise<SignInPolicyState>;
109
110
  };
111
+ users: {
112
+ search: (query: string, offset?: number) => Promise<UsersFound>;
113
+ create: (input: {
114
+ email: string;
115
+ name: string;
116
+ password?: string;
117
+ roles: string[];
118
+ }) => Promise<AppUser>;
119
+ setRoles: (userId: string, roles: string[]) => Promise<unknown>;
120
+ block: (userId: string) => Promise<unknown>;
121
+ unblock: (userId: string) => Promise<unknown>;
122
+ invite: (input: {
123
+ email: string;
124
+ roles: string[];
125
+ }) => Promise<UserInvitationSent>;
126
+ invitations: () => Promise<UserInvitation[]>;
127
+ revoke: (invitationId: string) => Promise<unknown>;
128
+ roles: () => Promise<RoleLabel[]>;
129
+ };
110
130
  staff: {
111
131
  stop: () => Promise<void>;
112
132
  start: (input: {
package/dist/auth.js CHANGED
@@ -3,6 +3,7 @@ import { accountSecurity } from "./account-security.js";
3
3
  import { betterAuthClient } from "./better-auth-client.js";
4
4
  import { organizationsClient } from "./organizations.js";
5
5
  import { rolesClient } from "./roles.js";
6
+ import { usersClient } from "./users.js";
6
7
  import { signInFlows } from "./sign-in-flows.js";
7
8
  import { signInPolicyClient } from "./sign-in-policy.js";
8
9
  import { staffSignInClient } from "./staff-sign-in.js";
@@ -18,8 +19,8 @@ const tokenExpiry = (token) => {
18
19
  /**
19
20
  * The browser side of Better Auth: sign in with a password, an emailed code or a passkey (with a second step for an
20
21
  * account with two-factor authentication), the account's passkeys and two-factor authentication (`account`), the
21
- * sign-in policy for admins (`signInPolicy`), staff of the operator signing in as people (`staff`), organizations
22
- * (`organizations`), the role the session acts with (`roles`), and a short-lived
22
+ * sign-in policy for admins (`signInPolicy`), the people who can sign in for admins (`users`), staff of the operator
23
+ * signing in as people (`staff`), organizations (`organizations`), the role the session acts with (`roles`), and a short-lived
23
24
  * JWT held in memory only (never in storage), fetched with the session cookie and refreshed before it expires.
24
25
  */
25
26
  const createAuthSession = (options = {}) => {
@@ -149,6 +150,7 @@ const createAuthSession = (options = {}) => {
149
150
  organizations: organizationsClient(client, () => token({ refresh: true })),
150
151
  roles: rolesClient(client, () => token({ refresh: true })),
151
152
  signInPolicy: signInPolicyClient(client),
153
+ users: usersClient(client),
152
154
  staff: {
153
155
  ...staff,
154
156
  stop: async () => {
package/dist/index.d.ts CHANGED
@@ -11,11 +11,12 @@ import { SignInPolicy, SignInPolicyClient, SignInPolicyState, SignInRule, StaffA
11
11
  import { AccountSecurity, AccountSignIn, AuthenticatorSetup, Passkey, RequiredSetup } from "./account-security.js";
12
12
  import { CurrentOrganization, InvitationPreview, InvitationSent, Organization, OrganizationInvitation, OrganizationMember, OrganizationRole, OrganizationsClient, OrganizationsFound } from "./organizations.js";
13
13
  import { ActiveRole, RoleOption, RolesClient } from "./roles.js";
14
- import { StaffSignIn, StaffSignInClient } from "./staff-sign-in.js";
15
14
  import { AuthUser, PlatformSignIn, RoleLabel, SamlSignIn, SetupStatus, SignInMethod, SignInResult, TwoFactorMethod } from "./auth-types.js";
15
+ import { AppUser, UserInvitation, UserInvitationSent, UsersClient, UsersFound } from "./users.js";
16
+ import { StaffSignIn, StaffSignInClient } from "./staff-sign-in.js";
16
17
  import { AuthError } from "./auth-error.js";
17
18
  import { AuthSession, AuthSessionOptions, createAuthSession, tokenExpiry } from "./auth.js";
18
19
  import { createStaticSession } from "./static-session.js";
19
20
  import { AssistantClient, AssistantClientOptions, AssistantConnection, createAssistantClient } from "./assistant.js";
20
21
  import { RuntimeClient, RuntimeClientOptions, RuntimeLastUpdate, RuntimePolicy, RuntimeStatus, RuntimeUpdateStage, createRuntimeClient } from "./runtime.js";
21
- export { type AccountSecurity, type AccountSignIn, type ActiveRole, type Aggregate, type AggregateGroup, type AggregateParams, ApiError, type AssistantClient, type AssistantClientOptions, type AssistantConnection, AuthError, type AuthSession, type AuthSessionOptions, type AuthUser, type AuthenticatorSetup, type BatchOp, type BatchRef, type BatchResult, type Client, type ClientOptions, type CommentPage, type CommentPerson, type CurrentOrganization, type Facet, type FilterInput, type FormChallenge, type FormProof, type Histogram, type HistogramBucket, type HistoryChange, type HistoryPage, type InvitationPreview, type InvitationSent, type InvokeOptions, type ListPage, type ListParams, type MapBounds, type MapBucket, type MapLayerModel, type MapParams, type MapResult, type MentionableUser, type Meta, type MetaResult, type Organization, type OrganizationInvitation, type OrganizationMember, type OrganizationRole, type OrganizationsClient, type OrganizationsFound, type Passkey, type PlatformSignIn, type PluginRef, PreconditionFailedError, type ProblemDetails, type ProblemError, type PublicForm, type RecordComment, type RecordOf, type RecordPermissions, type RecordPhotos, type RelatedLabels, type RequiredSetup, type ResourcePermissions, type ResourceRef, type RoleLabel, type RoleOption, type RolesClient, type RuntimeClient, type RuntimeClientOptions, type RuntimeLastUpdate, type RuntimePolicy, type RuntimeStatus, type RuntimeUpdateStage, type SamlSignIn, type Series, type SeriesParams, type SeriesPoint, type SetupStatus, type SignInMethod, type SignInPolicy, type SignInPolicyClient, type SignInPolicyState, type SignInResult, type SignInRule, type SourceMeta, type SourceRef, type StaffAccessRule, type StaffSignIn, type StaffSignInClient, type Stored, type StoredFile, type StoredFileVariant, type TwoFactorMethod, type UploadOptions, type UploadProgress, type UploadResult, createAssistantClient, createAuthSession, createClient, createRuntimeClient, createStaticSession, fileUrl, filterText, isApiError, listWire, routedClient, sourceMeta, tokenExpiry };
22
+ export { type AccountSecurity, type AccountSignIn, type ActiveRole, type Aggregate, type AggregateGroup, type AggregateParams, ApiError, type AppUser, type AssistantClient, type AssistantClientOptions, type AssistantConnection, AuthError, type AuthSession, type AuthSessionOptions, type AuthUser, type AuthenticatorSetup, type BatchOp, type BatchRef, type BatchResult, type Client, type ClientOptions, type CommentPage, type CommentPerson, type CurrentOrganization, type Facet, type FilterInput, type FormChallenge, type FormProof, type Histogram, type HistogramBucket, type HistoryChange, type HistoryPage, type InvitationPreview, type InvitationSent, type InvokeOptions, type ListPage, type ListParams, type MapBounds, type MapBucket, type MapLayerModel, type MapParams, type MapResult, type MentionableUser, type Meta, type MetaResult, type Organization, type OrganizationInvitation, type OrganizationMember, type OrganizationRole, type OrganizationsClient, type OrganizationsFound, type Passkey, type PlatformSignIn, type PluginRef, PreconditionFailedError, type ProblemDetails, type ProblemError, type PublicForm, type RecordComment, type RecordOf, type RecordPermissions, type RecordPhotos, type RelatedLabels, type RequiredSetup, type ResourcePermissions, type ResourceRef, type RoleLabel, type RoleOption, type RolesClient, type RuntimeClient, type RuntimeClientOptions, type RuntimeLastUpdate, type RuntimePolicy, type RuntimeStatus, type RuntimeUpdateStage, type SamlSignIn, type Series, type SeriesParams, type SeriesPoint, type SetupStatus, type SignInMethod, type SignInPolicy, type SignInPolicyClient, type SignInPolicyState, type SignInResult, type SignInRule, type SourceMeta, type SourceRef, type StaffAccessRule, type StaffSignIn, type StaffSignInClient, type Stored, type StoredFile, type StoredFileVariant, type TwoFactorMethod, type UploadOptions, type UploadProgress, type UploadResult, type UserInvitation, type UserInvitationSent, type UsersClient, type UsersFound, createAssistantClient, createAuthSession, createClient, createRuntimeClient, createStaticSession, fileUrl, filterText, isApiError, listWire, routedClient, sourceMeta, tokenExpiry };
@@ -44,9 +44,15 @@ export type OrganizationInvitation = {
44
44
  expiresAt: string;
45
45
  organizationName?: string;
46
46
  };
47
- /** An invitation as its link shows it, before it is accepted. */
47
+ /**
48
+ * An invitation as its link shows it, before it is accepted. One to the app itself, rather than to an organization, has
49
+ * `app` in place of `organization` and `role`, and `appRoles` are the roles its account gets.
50
+ */
48
51
  export type InvitationPreview = {
49
- organization: {
52
+ app?: {
53
+ name: string;
54
+ };
55
+ organization?: {
50
56
  id: string;
51
57
  name: string;
52
58
  };
@@ -54,7 +60,7 @@ export type InvitationPreview = {
54
60
  name: string;
55
61
  };
56
62
  email: string;
57
- role: OrganizationRole;
63
+ role?: OrganizationRole;
58
64
  appRoles: string[];
59
65
  expiresAt: string;
60
66
  hasAccount: boolean;
@@ -110,11 +116,11 @@ export declare const organizationsClient: (client: BetterAuthClient, refreshToke
110
116
  revoke: (invitationId: string) => Promise<unknown>;
111
117
  /** The pending invitations for the person's own address. */
112
118
  myInvitations: () => Promise<OrganizationInvitation[]>;
113
- /** What an invitation link is for; works signed out. */
119
+ /** What an invitation link is for, to an organization or to the app; works signed out. */
114
120
  invitation: (token: string) => Promise<InvitationPreview>;
115
121
  /** Accepts an invitation signed in as its address, and works in its organization. */
116
122
  accept: (token: string) => Promise<unknown>;
117
- /** Creates an account for an invited address without one, accepts the invitation and signs in. */
123
+ /** Creates an account for an invited address without one, accepts the invitation and signs in; for either kind of invitation. */
118
124
  signUpFromInvitation: (token: string, input: {
119
125
  name: string;
120
126
  password?: string;
@@ -113,11 +113,11 @@ const organizationsClient = (client, refreshToken) => {
113
113
  revoke: (invitationId) => call("/organization/cancel-invitation", "Could not revoke the invitation", { body: { invitationId } }),
114
114
  /** The pending invitations for the person's own address. */
115
115
  myInvitations: async () => (await call("/organization/list-user-invitations", "Could not read your invitations") ?? []).map(invitation),
116
- /** What an invitation link is for; works signed out. */
116
+ /** What an invitation link is for, to an organization or to the app; works signed out. */
117
117
  invitation: (token) => call("/invitation", "This invitation link is not valid", { query: { token } }),
118
118
  /** Accepts an invitation signed in as its address, and works in its organization. */
119
119
  accept: async (token) => thenRefresh(await call("/invitation/accept", "Could not accept the invitation", { body: { token } })),
120
- /** Creates an account for an invited address without one, accepts the invitation and signs in. */
120
+ /** Creates an account for an invited address without one, accepts the invitation and signs in; for either kind of invitation. */
121
121
  signUpFromInvitation: async (token, input) => thenRefresh(await call("/invitation/sign-up", "Could not create your account", { body: {
122
122
  token,
123
123
  ...input
@@ -91,6 +91,20 @@ const createStaticSession = (token, user = {
91
91
  }),
92
92
  switchTo: unsupported("switch roles")
93
93
  },
94
+ users: {
95
+ search: async () => ({
96
+ users: [],
97
+ total: 0
98
+ }),
99
+ create: unsupported("create an account"),
100
+ setRoles: unsupported("change roles"),
101
+ block: unsupported("block an account"),
102
+ unblock: unsupported("unblock an account"),
103
+ invite: unsupported("invite people"),
104
+ invitations: async () => [],
105
+ revoke: unsupported("revoke an invitation"),
106
+ roles: async () => []
107
+ },
94
108
  organizations: {
95
109
  list: async () => [],
96
110
  current: async () => ({
@@ -0,0 +1,59 @@
1
+ import { BetterAuthClient } from "./better-auth-client.js";
2
+ import { RoleLabel } from "./auth-types.js";
3
+ //#region src/users.d.ts
4
+ /** Someone who can sign in, as an admin sees them: their roles, and whether they are blocked from signing in. */
5
+ export type AppUser = {
6
+ id: string;
7
+ name: string;
8
+ email: string;
9
+ roles: string[];
10
+ blocked: boolean;
11
+ createdAt: string;
12
+ };
13
+ /** A page of users and how many there are in all. */
14
+ export type UsersFound = {
15
+ users: AppUser[];
16
+ total: number;
17
+ };
18
+ /** A pending invitation to the app, to make an account with `roles`. */
19
+ export type UserInvitation = {
20
+ id: string;
21
+ email: string;
22
+ roles: string[];
23
+ expiresAt: string;
24
+ };
25
+ /** What inviting gives back: the invitation, and its link when the server cannot email it. */
26
+ export type UserInvitationSent = {
27
+ invitation: UserInvitation;
28
+ link?: string;
29
+ };
30
+ /**
31
+ * Managing the people who can sign in, for admins: Better Auth's admin endpoints to list, create, set roles and block,
32
+ * and Protobase's invitations to the app, which work with or without organizations. Each call rejects with `AuthError`.
33
+ */
34
+ export declare const usersClient: (client: BetterAuthClient) => {
35
+ /** 20 users from `offset`, oldest first, whose address contains `query`, and the total. */
36
+ search: (query: string, offset?: number) => Promise<UsersFound>;
37
+ /** Creates an account; without a password, the person signs in another way the sign-in policy allows, such as an emailed code. */
38
+ create: (input: {
39
+ email: string;
40
+ name: string;
41
+ password?: string;
42
+ roles: string[];
43
+ }) => Promise<AppUser>;
44
+ setRoles: (userId: string, roles: string[]) => Promise<unknown>;
45
+ /** Blocks signing in, which ends the person's sessions. */
46
+ block: (userId: string) => Promise<unknown>;
47
+ unblock: (userId: string) => Promise<unknown>;
48
+ /** Invites an address without an account; the link comes back only when the server cannot email it. Inviting again sends a new link. */
49
+ invite: (input: {
50
+ email: string;
51
+ roles: string[];
52
+ }) => Promise<UserInvitationSent>;
53
+ invitations: () => Promise<UserInvitation[]>;
54
+ revoke: (invitationId: string) => Promise<unknown>;
55
+ /** Every role with its label, to give. */
56
+ roles: () => Promise<RoleLabel[]>;
57
+ };
58
+ export type UsersClient = ReturnType<typeof usersClient>;
59
+ //#endregion
package/dist/users.js ADDED
@@ -0,0 +1,78 @@
1
+ import { authError } from "./auth-error.js";
2
+ //#region src/users.ts
3
+ const pageSize = 20;
4
+ const rolesOf = (role) => Array.isArray(role) ? role : (role ?? "").split(",").map((name) => name.trim()).filter(Boolean);
5
+ const user = (raw) => ({
6
+ id: raw.id,
7
+ name: raw.name || raw.email,
8
+ email: raw.email,
9
+ roles: rolesOf(raw.role),
10
+ blocked: Boolean(raw.banned),
11
+ createdAt: new Date(raw.createdAt).toISOString()
12
+ });
13
+ const invitation = (raw) => ({
14
+ id: raw.id,
15
+ email: raw.email,
16
+ roles: raw.roles,
17
+ expiresAt: new Date(raw.expiresAt).toISOString()
18
+ });
19
+ const sent = (raw) => ({
20
+ invitation: invitation(raw),
21
+ ...raw.link && { link: raw.link }
22
+ });
23
+ /**
24
+ * Managing the people who can sign in, for admins: Better Auth's admin endpoints to list, create, set roles and block,
25
+ * and Protobase's invitations to the app, which work with or without organizations. Each call rejects with `AuthError`.
26
+ */
27
+ const usersClient = (client) => {
28
+ const call = async (path, fallback, init = {}) => {
29
+ const { data, error } = await client.$fetch(path, {
30
+ method: init.body ? "POST" : "GET",
31
+ ...init.body && { body: init.body },
32
+ ...init.query && { query: init.query }
33
+ });
34
+ if (error) throw authError(error, fallback);
35
+ return data;
36
+ };
37
+ return {
38
+ /** 20 users from `offset`, oldest first, whose address contains `query`, and the total. */
39
+ search: async (query, offset = 0) => {
40
+ const found = await call("/admin/list-users", "Could not read the users", { query: {
41
+ limit: String(pageSize),
42
+ sortBy: "createdAt",
43
+ sortDirection: "asc",
44
+ ...offset > 0 && { offset: String(offset) },
45
+ ...query && {
46
+ searchValue: query,
47
+ searchField: "email"
48
+ }
49
+ } });
50
+ return {
51
+ users: found.users.map(user),
52
+ total: found.total
53
+ };
54
+ },
55
+ /** Creates an account; without a password, the person signs in another way the sign-in policy allows, such as an emailed code. */
56
+ create: async (input) => user((await call("/admin/create-user", "Could not create the account", { body: {
57
+ email: input.email,
58
+ name: input.name,
59
+ role: input.roles,
60
+ ...input.password && { password: input.password }
61
+ } })).user),
62
+ setRoles: (userId, roles) => call("/admin/set-role", "Could not change the roles", { body: {
63
+ userId,
64
+ role: roles
65
+ } }),
66
+ /** Blocks signing in, which ends the person's sessions. */
67
+ block: (userId) => call("/admin/ban-user", "Could not block the account", { body: { userId } }),
68
+ unblock: (userId) => call("/admin/unban-user", "Could not unblock the account", { body: { userId } }),
69
+ /** Invites an address without an account; the link comes back only when the server cannot email it. Inviting again sends a new link. */
70
+ invite: async (input) => sent(await call("/users/invite", "Could not send the invitation", { body: input })),
71
+ invitations: async () => (await call("/users/invitations", "Could not read the invitations")).invitations.map(invitation),
72
+ revoke: (invitationId) => call("/users/invitations/cancel", "Could not revoke the invitation", { body: { id: invitationId } }),
73
+ /** Every role with its label, to give. */
74
+ roles: () => call("/users/roles", "Could not read the roles")
75
+ };
76
+ };
77
+ //#endregion
78
+ export { usersClient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/client",
3
- "version": "0.22.0",
3
+ "version": "0.22.2",
4
4
  "description": "Typed fetch client for the Protobase REST API.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -30,8 +30,8 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@better-auth/passkey": "^1.7.7",
33
- "@protobase/layout": "0.22.0",
34
- "@protobase/schema": "0.22.0",
33
+ "@protobase/layout": "0.22.2",
34
+ "@protobase/schema": "0.22.2",
35
35
  "@simplewebauthn/browser": "^13.3.0",
36
36
  "better-auth": "^1.7.7"
37
37
  }