@stackframe/stack-shared 2.3.6 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { StackClientInterface, UserJson as UserJson, UserCustomizableJson, ClientProjectJson, ProjectJson, OAuthProviderConfigJson, getProductionModeErrors, } from "./interface/clientInterface";
2
- export { StackServerInterface, ServerUserJson, ServerUserCustomizableJson, } from "./interface/serverInterface";
1
+ export { StackClientInterface, UserJson as UserJson, ClientProjectJson, ProjectJson, OAuthProviderConfigJson, getProductionModeErrors, } from "./interface/clientInterface";
2
+ export { StackServerInterface, ServerUserJson, } from "./interface/serverInterface";
3
3
  export { StackAdminInterface, ApiKeySetBaseJson, ApiKeySetFirstViewJson, ApiKeySetJson, } from "./interface/adminInterface";
4
4
  export { KnownError, KnownErrors, } from "./known-errors";
@@ -27,6 +27,7 @@ export type ProjectUpdateOptions = {
27
27
  credentialEnabled?: boolean;
28
28
  magicLinkEnabled?: boolean;
29
29
  allowLocalhost?: boolean;
30
+ createTeamOnSignUp?: boolean;
30
31
  };
31
32
  };
32
33
  export type ApiKeySetBaseJson = {
@@ -62,7 +63,7 @@ export type ApiKeySetCreateOptions = {
62
63
  export declare class StackAdminInterface extends StackServerInterface {
63
64
  readonly options: AdminAuthApplicationOptions;
64
65
  constructor(options: AdminAuthApplicationOptions);
65
- protected sendAdminRequest(path: string, options: RequestInit, tokenStore: TokenStore | null): Promise<Response & {
66
+ protected sendAdminRequest(path: string, options: RequestInit, tokenStore: TokenStore | null, requestType?: "admin"): Promise<Response & {
66
67
  usedTokens: Readonly<{
67
68
  refreshToken: string | null;
68
69
  accessToken: string | null;
@@ -5,14 +5,14 @@ export class StackAdminInterface extends StackServerInterface {
5
5
  super(options);
6
6
  this.options = options;
7
7
  }
8
- async sendAdminRequest(path, options, tokenStore) {
8
+ async sendAdminRequest(path, options, tokenStore, requestType = "admin") {
9
9
  return await this.sendServerRequest(path, {
10
10
  ...options,
11
11
  headers: {
12
12
  "x-stack-super-secret-admin-key": "superSecretAdminKey" in this.options ? this.options.superSecretAdminKey : "",
13
13
  ...options.headers,
14
14
  },
15
- }, tokenStore);
15
+ }, tokenStore, requestType);
16
16
  }
17
17
  async getProject(options) {
18
18
  const response = await this.sendAdminRequest("/projects/" + encodeURIComponent(this.projectId), {
@@ -3,12 +3,12 @@ import { Result } from "../utils/results";
3
3
  import { ReadonlyJson } from '../utils/json';
4
4
  import { AsyncStore, ReadonlyAsyncStore } from '../utils/stores';
5
5
  import { KnownErrors } from '../known-errors';
6
- export type UserCustomizableJson = {
7
- readonly projectId: string;
6
+ type UserCustomizableJson = {
8
7
  readonly displayName: string | null;
9
8
  readonly clientMetadata: ReadonlyJson;
10
9
  };
11
10
  export type UserJson = UserCustomizableJson & {
11
+ readonly projectId: string;
12
12
  readonly id: string;
13
13
  readonly primaryEmail: string | null;
14
14
  readonly primaryEmailVerified: boolean;
@@ -16,11 +16,15 @@ export type UserJson = UserCustomizableJson & {
16
16
  readonly clientMetadata: ReadonlyJson;
17
17
  readonly profileImageUrl: string | null;
18
18
  readonly signedUpAtMillis: number;
19
+ /**
20
+ * not used anymore, for backwards compatibility
21
+ */
19
22
  readonly authMethod: "credential" | "oauth";
20
23
  readonly hasPassword: boolean;
21
24
  readonly authWithEmail: boolean;
22
25
  readonly oauthProviders: readonly string[];
23
26
  };
27
+ export type UserUpdateJson = Partial<UserCustomizableJson>;
24
28
  export type ClientProjectJson = {
25
29
  readonly id: string;
26
30
  readonly credentialEnabled: boolean;
@@ -65,6 +69,7 @@ export type ProjectJson = {
65
69
  oauthProviders: OAuthProviderConfigJson[];
66
70
  emailConfig?: EmailConfigJson;
67
71
  domains: DomainConfigJson[];
72
+ createTeamOnSignUp: boolean;
68
73
  };
69
74
  };
70
75
  export type OAuthProviderConfigJson = {
@@ -98,6 +103,30 @@ export type ProductionModeError = {
98
103
  errorMessage: string;
99
104
  fixUrlRelative: string;
100
105
  };
106
+ export type OrglikeJson = {
107
+ id: string;
108
+ displayName: string;
109
+ createdAtMillis: number;
110
+ };
111
+ export type TeamJson = OrglikeJson;
112
+ export type OrganizationJson = OrglikeJson;
113
+ export type TeamMemberJson = {
114
+ userId: string;
115
+ teamId: string;
116
+ displayName: string | null;
117
+ };
118
+ export type PermissionDefinitionScopeJson = {
119
+ type: "global";
120
+ } | {
121
+ type: "any-team";
122
+ } | {
123
+ type: "specific-team";
124
+ teamId: string;
125
+ };
126
+ export type PermissionDefinitionJson = {
127
+ id: string;
128
+ scope: PermissionDefinitionScopeJson;
129
+ };
101
130
  export declare class StackClientInterface {
102
131
  readonly options: ClientInterfaceOptions;
103
132
  constructor(options: ClientInterfaceOptions);
@@ -105,7 +134,7 @@ export declare class StackClientInterface {
105
134
  getSessionCookieName(): string;
106
135
  getApiUrl(): string;
107
136
  protected refreshAccessToken(tokenStore: TokenStore): Promise<void>;
108
- protected sendClientRequest(path: string, requestOptions: RequestInit, tokenStoreOrNull: TokenStore | null): Promise<Response & {
137
+ protected sendClientRequest(path: string, requestOptions: RequestInit, tokenStoreOrNull: TokenStore | null, requestType?: "client" | "server" | "admin"): Promise<Response & {
109
138
  usedTokens: Readonly<{
110
139
  refreshToken: string | null;
111
140
  accessToken: string | null;
@@ -141,9 +170,16 @@ export declare class StackClientInterface {
141
170
  callOAuthCallback(oauthParams: URLSearchParams, redirectUri: string, codeVerifier: string, state: string, tokenStore: TokenStore): Promise<oauth.OAuth2TokenEndpointResponse>;
142
171
  signOut(tokenStore: TokenStore): Promise<void>;
143
172
  getClientUserByToken(tokenStore: TokenStore): Promise<Result<UserJson>>;
173
+ listClientUserTeamPermissions(options: {
174
+ teamId: string;
175
+ type: 'global' | 'team';
176
+ direct: boolean;
177
+ }, tokenStore: TokenStore): Promise<PermissionDefinitionJson[]>;
178
+ listClientUserTeams(tokenStore: TokenStore): Promise<TeamJson[]>;
144
179
  getClientProject(): Promise<Result<ClientProjectJson>>;
145
- setClientUserCustomizableData(update: Partial<UserCustomizableJson>, tokenStore: TokenStore): Promise<void>;
180
+ setClientUserCustomizableData(update: UserUpdateJson, tokenStore: TokenStore): Promise<void>;
146
181
  listProjects(tokenStore: TokenStore): Promise<ProjectJson[]>;
147
182
  createProject(project: Pick<ProjectJson, "displayName" | "description">, tokenStore: TokenStore): Promise<ProjectJson>;
148
183
  }
149
184
  export declare function getProductionModeErrors(project: ProjectJson): ProductionModeError[];
185
+ export {};
@@ -4,6 +4,8 @@ import { Result } from "../utils/results";
4
4
  import { AsyncStore } from '../utils/stores';
5
5
  import { KnownError, KnownErrors } from '../known-errors';
6
6
  import { StackAssertionError } from '../utils/errors';
7
+ import { cookies } from '@stackframe/stack-sc';
8
+ import { generateSecureRandomString } from '../utils/crypto';
7
9
  export const sharedProviders = [
8
10
  "shared-github",
9
11
  "shared-google",
@@ -94,12 +96,12 @@ export class StackClientInterface {
94
96
  refreshToken: result.refresh_token ?? old?.refreshToken ?? null,
95
97
  }));
96
98
  }
97
- async sendClientRequest(path, requestOptions, tokenStoreOrNull) {
99
+ async sendClientRequest(path, requestOptions, tokenStoreOrNull, requestType = "client") {
98
100
  const tokenStore = tokenStoreOrNull ?? new AsyncStore({
99
101
  accessToken: null,
100
102
  refreshToken: null,
101
103
  });
102
- return await Result.orThrowAsync(Result.retry(() => this.sendClientRequestInner(path, requestOptions, tokenStore), 5, { exponentialDelayBase: 1000 }));
104
+ return await Result.orThrowAsync(Result.retry(() => this.sendClientRequestInner(path, requestOptions, tokenStore, requestType), 5, { exponentialDelayBase: 1000 }));
103
105
  }
104
106
  async sendClientRequestAndCatchKnownError(path, requestOptions, tokenStoreOrNull, errorsToCatch) {
105
107
  try {
@@ -118,12 +120,14 @@ export class StackClientInterface {
118
120
  /**
119
121
  * This object will be modified for future retries, so it should be passed by reference.
120
122
  */
121
- tokenStore) {
123
+ tokenStore, requestType) {
122
124
  let tokenObj = await tokenStore.getOrWait();
123
125
  if (!tokenObj.accessToken && tokenObj.refreshToken) {
124
126
  await this.refreshAccessToken(tokenStore);
125
127
  tokenObj = await tokenStore.getOrWait();
126
128
  }
129
+ // all requests should be dynamic to prevent Next.js caching
130
+ cookies?.();
127
131
  const url = this.getApiUrl() + path;
128
132
  const params = {
129
133
  /**
@@ -138,6 +142,7 @@ export class StackClientInterface {
138
142
  headers: {
139
143
  "X-Stack-Override-Error-Status": "true",
140
144
  "X-Stack-Project-Id": this.projectId,
145
+ "X-Stack-Request-Type": requestType,
141
146
  ...tokenObj.accessToken ? {
142
147
  "Authorization": "StackSession " + tokenObj.accessToken,
143
148
  } : {},
@@ -147,8 +152,10 @@ export class StackClientInterface {
147
152
  ...'projectOwnerTokens' in this.options ? {
148
153
  "X-Stack-Admin-Access-Token": (await this.options.projectOwnerTokens?.getOrWait())?.accessToken ?? "",
149
154
  } : {},
155
+ "X-Stack-Random-Nonce": generateSecureRandomString(),
150
156
  ...options.headers,
151
157
  },
158
+ cache: "no-store",
152
159
  };
153
160
  const rawRes = await fetch(url, params);
154
161
  const processedRes = await this._processResponse(rawRes);
@@ -432,6 +439,16 @@ export class StackClientInterface {
432
439
  return Result.error(new Error("Failed to get user"));
433
440
  return Result.ok(user);
434
441
  }
442
+ async listClientUserTeamPermissions(options, tokenStore) {
443
+ const response = await this.sendClientRequest(`/current-user/teams/${options.teamId}/permissions?type=${options.type}&direct=${options.direct}`, {}, tokenStore);
444
+ const permissions = await response.json();
445
+ return permissions;
446
+ }
447
+ async listClientUserTeams(tokenStore) {
448
+ const response = await this.sendClientRequest("/current-user/teams", {}, tokenStore);
449
+ const teams = await response.json();
450
+ return teams;
451
+ }
435
452
  async getClientProject() {
436
453
  const response = await this.sendClientRequest("/projects/" + this.options.projectId, {}, null);
437
454
  const project = await response.json();
@@ -473,7 +490,7 @@ export class StackClientInterface {
473
490
  }
474
491
  export function getProductionModeErrors(project) {
475
492
  const errors = [];
476
- const fixUrlRelative = `/projects/${encodeURIComponent(project.id)}/auth/urls-and-callbacks`;
493
+ const fixUrlRelative = `/projects/${project.id}/auth/urls-and-callbacks`;
477
494
  if (project.evaluatedConfig.allowLocalhost) {
478
495
  errors.push({
479
496
  errorMessage: "Localhost is not allowed in production mode, turn off 'Allow localhost' in project settings",
@@ -1,13 +1,28 @@
1
- import { ClientInterfaceOptions, UserCustomizableJson, UserJson, TokenStore, StackClientInterface, ReadonlyTokenStore } from "./clientInterface";
1
+ import { ClientInterfaceOptions, UserJson, TokenStore, StackClientInterface, ReadonlyTokenStore, OrglikeJson, UserUpdateJson, PermissionDefinitionJson, PermissionDefinitionScopeJson as PermissionDefinitionScopeJson, TeamMemberJson } from "./clientInterface";
2
2
  import { Result } from "../utils/results";
3
3
  import { ReadonlyJson } from "../utils/json";
4
4
  export type ServerUserJson = UserJson & {
5
5
  readonly serverMetadata: ReadonlyJson;
6
6
  };
7
- export type ServerUserCustomizableJson = UserCustomizableJson & {
8
- readonly serverMetadata: ReadonlyJson;
9
- readonly primaryEmail: string | null;
10
- readonly primaryEmailVerified: boolean;
7
+ export type ServerUserUpdateJson = UserUpdateJson & {
8
+ readonly serverMetadata?: ReadonlyJson;
9
+ readonly primaryEmail?: string | null;
10
+ readonly primaryEmailVerified?: boolean;
11
+ };
12
+ export type ServerOrglikeCustomizableJson = Pick<ServerOrglikeJson, "displayName">;
13
+ export type ServerOrglikeJson = OrglikeJson & {};
14
+ export type ServerTeamCustomizableJson = ServerOrglikeCustomizableJson;
15
+ export type ServerTeamJson = ServerOrglikeJson;
16
+ export type ServerTeamMemberJson = TeamMemberJson;
17
+ export type ServerPermissionDefinitionCustomizableJson = {
18
+ readonly id: string;
19
+ readonly description?: string;
20
+ readonly scope: PermissionDefinitionScopeJson;
21
+ readonly containPermissionIds: string[];
22
+ };
23
+ export type ServerPermissionDefinitionJson = PermissionDefinitionJson & ServerPermissionDefinitionCustomizableJson & {
24
+ readonly __databaseUniqueId: string;
25
+ readonly scope: PermissionDefinitionScopeJson;
11
26
  };
12
27
  export type ServerAuthApplicationOptions = (ClientInterfaceOptions & ({
13
28
  readonly secretServerKey: string;
@@ -17,14 +32,46 @@ export type ServerAuthApplicationOptions = (ClientInterfaceOptions & ({
17
32
  export declare class StackServerInterface extends StackClientInterface {
18
33
  options: ServerAuthApplicationOptions;
19
34
  constructor(options: ServerAuthApplicationOptions);
20
- protected sendServerRequest(path: string, options: RequestInit, tokenStore: TokenStore | null): Promise<Response & {
35
+ protected sendServerRequest(path: string, options: RequestInit, tokenStore: TokenStore | null, requestType?: "server" | "admin"): Promise<Response & {
21
36
  usedTokens: Readonly<{
22
37
  refreshToken: string | null;
23
38
  accessToken: string | null;
24
39
  }>;
25
40
  }>;
26
41
  getServerUserByToken(tokenStore: TokenStore): Promise<Result<ServerUserJson>>;
42
+ getServerUserById(userId: string): Promise<Result<ServerUserJson>>;
43
+ listServerUserTeamPermissions(options: {
44
+ teamId: string;
45
+ type: 'global' | 'team';
46
+ direct: boolean;
47
+ }, tokenStore: TokenStore): Promise<ServerPermissionDefinitionJson[]>;
48
+ listServerUserTeams(tokenStore: TokenStore): Promise<ServerTeamJson[]>;
49
+ listPermissionDefinitions(): Promise<ServerPermissionDefinitionJson[]>;
50
+ createPermissionDefinition(data: ServerPermissionDefinitionCustomizableJson): Promise<ServerPermissionDefinitionJson>;
51
+ updatePermissionDefinition(permissionId: string, data: Partial<ServerPermissionDefinitionCustomizableJson>): Promise<void>;
52
+ deletePermissionDefinition(permissionId: string): Promise<void>;
27
53
  listUsers(): Promise<ServerUserJson[]>;
28
- setServerUserCustomizableData(userId: string, update: Partial<ServerUserCustomizableJson>): Promise<void>;
54
+ listTeams(): Promise<ServerTeamJson[]>;
55
+ listTeamMembers(teamId: string): Promise<ServerTeamMemberJson[]>;
56
+ createTeam(data: ServerTeamCustomizableJson): Promise<ServerTeamJson>;
57
+ updateTeam(teamId: string, data: Partial<ServerTeamCustomizableJson>): Promise<void>;
58
+ deleteTeam(teamId: string): Promise<void>;
59
+ addUserToTeam(options: {
60
+ userId: string;
61
+ teamId: string;
62
+ }): Promise<void>;
63
+ removeUserFromTeam(options: {
64
+ userId: string;
65
+ teamId: string;
66
+ }): Promise<void>;
67
+ setServerUserCustomizableData(userId: string, update: ServerUserUpdateJson): Promise<void>;
68
+ listTeamMemberPermissions(options: {
69
+ teamId: string;
70
+ userId: string;
71
+ type: 'global' | 'team';
72
+ direct: boolean;
73
+ }): Promise<ServerPermissionDefinitionJson[]>;
74
+ grantTeamUserPermission(teamId: string, userId: string, permissionId: string, type: 'global' | 'team'): Promise<void>;
75
+ revokeTeamUserPermission(teamId: string, userId: string, permissionId: string, type: 'global' | 'team'): Promise<void>;
29
76
  deleteServerUser(userId: string): Promise<void>;
30
77
  }
@@ -6,14 +6,14 @@ export class StackServerInterface extends StackClientInterface {
6
6
  super(options);
7
7
  this.options = options;
8
8
  }
9
- async sendServerRequest(path, options, tokenStore) {
9
+ async sendServerRequest(path, options, tokenStore, requestType = "server") {
10
10
  return await this.sendClientRequest(path, {
11
11
  ...options,
12
12
  headers: {
13
13
  "x-stack-secret-server-key": "secretServerKey" in this.options ? this.options.secretServerKey : "",
14
14
  ...options.headers,
15
15
  },
16
- }, tokenStore);
16
+ }, tokenStore, requestType);
17
17
  }
18
18
  async getServerUserByToken(tokenStore) {
19
19
  const response = await this.sendServerRequest("/current-user?server=true", {}, tokenStore);
@@ -22,10 +22,107 @@ export class StackServerInterface extends StackClientInterface {
22
22
  return Result.error(new Error("Failed to get user"));
23
23
  return Result.ok(user);
24
24
  }
25
+ async getServerUserById(userId) {
26
+ const response = await this.sendServerRequest(`/users/${userId}?server=true`, {}, null);
27
+ const user = await response.json();
28
+ if (!user)
29
+ return Result.error(new Error("Failed to get user"));
30
+ return Result.ok(user);
31
+ }
32
+ async listServerUserTeamPermissions(options, tokenStore) {
33
+ const response = await this.sendServerRequest(`/current-user/teams/${options.teamId}/permissions?type=${options.type}&direct=${options.direct}&server=true`, {}, tokenStore);
34
+ const permissions = await response.json();
35
+ return permissions;
36
+ }
37
+ async listServerUserTeams(tokenStore) {
38
+ const response = await this.sendServerRequest("/current-user/teams?server=true", {}, tokenStore);
39
+ const teams = await response.json();
40
+ return teams;
41
+ }
42
+ async listPermissionDefinitions() {
43
+ const response = await this.sendServerRequest(`/permission-definitions?server=true`, {}, null);
44
+ return await response.json();
45
+ }
46
+ async createPermissionDefinition(data) {
47
+ const response = await this.sendServerRequest("/permission-definitions?server=true", {
48
+ method: "POST",
49
+ headers: {
50
+ "content-type": "application/json",
51
+ },
52
+ body: JSON.stringify({
53
+ ...data,
54
+ scope: {
55
+ type: "any-team",
56
+ }
57
+ }),
58
+ }, null);
59
+ return await response.json();
60
+ }
61
+ async updatePermissionDefinition(permissionId, data) {
62
+ await this.sendServerRequest(`/permission-definitions/${permissionId}?server=true`, {
63
+ method: "PUT",
64
+ headers: {
65
+ "content-type": "application/json",
66
+ },
67
+ body: JSON.stringify(data),
68
+ }, null);
69
+ }
70
+ async deletePermissionDefinition(permissionId) {
71
+ await this.sendServerRequest(`/permission-definitions/${permissionId}?server=true`, { method: "DELETE" }, null);
72
+ }
25
73
  async listUsers() {
26
74
  const response = await this.sendServerRequest("/users?server=true", {}, null);
27
75
  return await response.json();
28
76
  }
77
+ async listTeams() {
78
+ const response = await this.sendServerRequest("/teams?server=true", {}, null);
79
+ const json = await response.json();
80
+ return json;
81
+ }
82
+ async listTeamMembers(teamId) {
83
+ const response = await this.sendServerRequest(`/teams/${teamId}/users?server=true`, {}, null);
84
+ return await response.json();
85
+ }
86
+ async createTeam(data) {
87
+ const response = await this.sendServerRequest("/teams?server=true", {
88
+ method: "POST",
89
+ headers: {
90
+ "content-type": "application/json",
91
+ },
92
+ body: JSON.stringify(data),
93
+ }, null);
94
+ return await response.json();
95
+ }
96
+ async updateTeam(teamId, data) {
97
+ await this.sendServerRequest(`/teams/${teamId}?server=true`, {
98
+ method: "PUT",
99
+ headers: {
100
+ "content-type": "application/json",
101
+ },
102
+ body: JSON.stringify(data),
103
+ }, null);
104
+ }
105
+ async deleteTeam(teamId) {
106
+ await this.sendServerRequest(`/teams/${teamId}?server=true`, { method: "DELETE" }, null);
107
+ }
108
+ async addUserToTeam(options) {
109
+ await this.sendServerRequest(`/teams/${options.teamId}/users/${options.userId}?server=true`, {
110
+ method: "POST",
111
+ headers: {
112
+ "content-type": "application/json",
113
+ },
114
+ body: JSON.stringify({}),
115
+ }, null);
116
+ }
117
+ async removeUserFromTeam(options) {
118
+ await this.sendServerRequest(`/teams/${options.teamId}/users/${options.userId}?server=true`, {
119
+ method: "DELETE",
120
+ headers: {
121
+ "content-type": "application/json",
122
+ },
123
+ body: JSON.stringify({}),
124
+ }, null);
125
+ }
29
126
  async setServerUserCustomizableData(userId, update) {
30
127
  await this.sendServerRequest(`/users/${userId}?server=true`, {
31
128
  method: "PUT",
@@ -35,6 +132,28 @@ export class StackServerInterface extends StackClientInterface {
35
132
  body: JSON.stringify(update),
36
133
  }, null);
37
134
  }
135
+ async listTeamMemberPermissions(options) {
136
+ const response = await this.sendServerRequest(`/teams/${options.teamId}/users/${options.userId}/permissions?server=true&type=${options.type}&direct=${options.direct}`, {}, null);
137
+ return await response.json();
138
+ }
139
+ async grantTeamUserPermission(teamId, userId, permissionId, type) {
140
+ await this.sendServerRequest(`/teams/${teamId}/users/${userId}/permissions/${permissionId}?server=true`, {
141
+ method: "POST",
142
+ headers: {
143
+ "content-type": "application/json",
144
+ },
145
+ body: JSON.stringify({ type }),
146
+ }, null);
147
+ }
148
+ async revokeTeamUserPermission(teamId, userId, permissionId, type) {
149
+ await this.sendServerRequest(`/teams/${teamId}/users/${userId}/permissions/${permissionId}?server=true`, {
150
+ method: "DELETE",
151
+ headers: {
152
+ "content-type": "application/json",
153
+ },
154
+ body: JSON.stringify({ type }),
155
+ }, null);
156
+ }
38
157
  async deleteServerUser(userId) {
39
158
  await this.sendServerRequest(`/users/${userId}?server=true`, {
40
159
  method: "DELETE",
@@ -1,3 +1,4 @@
1
+ import { PermissionDefinitionScopeJson } from "./interface/clientInterface";
1
2
  import { StatusError } from "./utils/errors";
2
3
  import { Json } from "./utils/json";
3
4
  export type KnownErrorJson = {
@@ -58,6 +59,15 @@ export declare const KnownErrors: {
58
59
  InvalidProjectAuthentication: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION">, [statusCode: number, humanReadableMessage: string, details?: Json | undefined]> & {
59
60
  errorCode: "INVALID_PROJECT_AUTHENTICATION";
60
61
  };
62
+ ProjectKeyWithoutRequestType: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"PROJECT_KEY_WITHOUT_REQUEST_TYPE">, []> & {
63
+ errorCode: "PROJECT_KEY_WITHOUT_REQUEST_TYPE";
64
+ };
65
+ InvalidRequestType: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"INVALID_REQUEST_TYPE">, [requestType: string]> & {
66
+ errorCode: "INVALID_REQUEST_TYPE";
67
+ };
68
+ RequestTypeWithoutProjectId: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"REQUEST_TYPE_WITHOUT_PROJECT_ID">, [requestType: "client" | "server" | "admin"]> & {
69
+ errorCode: "REQUEST_TYPE_WITHOUT_PROJECT_ID";
70
+ };
61
71
  InvalidPublishableClientKey: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"INVALID_PUBLISHABLE_CLIENT_KEY">, []> & {
62
72
  errorCode: "INVALID_PUBLISHABLE_CLIENT_KEY";
63
73
  };
@@ -79,6 +89,9 @@ export declare const KnownErrors: {
79
89
  InvalidProjectForAdminAccessToken: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"INVALID_ADMIN_ACCESS_TOKEN"> & KnownErrorBrand<"INVALID_PROJECT_FOR_ADMIN_ACCESS_TOKEN">, []> & {
80
90
  errorCode: "INVALID_PROJECT_FOR_ADMIN_ACCESS_TOKEN";
81
91
  };
92
+ AdminAccessTokenIsNotAdmin: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"INVALID_ADMIN_ACCESS_TOKEN"> & KnownErrorBrand<"ADMIN_ACCESS_TOKEN_IS_NOT_ADMIN">, []> & {
93
+ errorCode: "ADMIN_ACCESS_TOKEN_IS_NOT_ADMIN";
94
+ };
82
95
  ProjectAuthenticationRequired: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & KnownErrorBrand<"PROJECT_AUTHENTICATION_REQUIRED">, [statusCode: number, humanReadableMessage: string, details?: Json | undefined]> & {
83
96
  errorCode: "PROJECT_AUTHENTICATION_REQUIRED";
84
97
  };
@@ -214,5 +227,14 @@ export declare const KnownErrors: {
214
227
  EmailAlreadyVerified: KnownErrorConstructor<KnownError & KnownErrorBrand<"EMAIL_ALREADY_VERIFIED">, []> & {
215
228
  errorCode: "EMAIL_ALREADY_VERIFIED";
216
229
  };
230
+ PermissionNotFound: KnownErrorConstructor<KnownError & KnownErrorBrand<"PERMISSION_NOT_FOUND">, [permissionId: string]> & {
231
+ errorCode: "PERMISSION_NOT_FOUND";
232
+ };
233
+ PermissionScopeMismatch: KnownErrorConstructor<KnownError & KnownErrorBrand<"PERMISSION_SCOPE_MISMATCH">, [permissionId: string, permissionScope: PermissionDefinitionScopeJson, testScope: PermissionDefinitionScopeJson]> & {
234
+ errorCode: "PERMISSION_SCOPE_MISMATCH";
235
+ };
236
+ TeamNotFound: KnownErrorConstructor<KnownError & KnownErrorBrand<"TEAM_NOT_FOUND">, [teamId: string]> & {
237
+ errorCode: "TEAM_NOT_FOUND";
238
+ };
217
239
  };
218
240
  export {};
@@ -1,5 +1,6 @@
1
1
  import { StatusError, throwErr, throwStackErr } from "./utils/errors";
2
2
  import { identityArgs } from "./utils/functions";
3
+ import { deindent } from "./utils/strings";
3
4
  export class KnownError extends StatusError {
4
5
  statusCode;
5
6
  humanReadableMessage;
@@ -80,7 +81,13 @@ const SchemaError = createKnownErrorConstructor(KnownError, "SCHEMA_ERROR", (mes
80
81
  ], (json) => [json.message]);
81
82
  const AllOverloadsFailed = createKnownErrorConstructor(KnownError, "ALL_OVERLOADS_FAILED", (overloadErrors) => [
82
83
  400,
83
- "This endpoint has multiple overloads, but they all failed to process the request.",
84
+ deindent `
85
+ This endpoint has multiple overloads, but they all failed to process the request.
86
+
87
+ ${overloadErrors.map((e, i) => deindent `
88
+ Overload ${i + 1}: ${JSON.stringify(e, undefined, 2)}
89
+ `).join("\n\n")}
90
+ `,
84
91
  {
85
92
  overloads: overloadErrors,
86
93
  },
@@ -89,6 +96,23 @@ const AllOverloadsFailed = createKnownErrorConstructor(KnownError, "ALL_OVERLOAD
89
96
  ]);
90
97
  const ProjectAuthenticationError = createKnownErrorConstructor(KnownError, "PROJECT_AUTHENTICATION_ERROR", "inherit", "inherit");
91
98
  const InvalidProjectAuthentication = createKnownErrorConstructor(ProjectAuthenticationError, "INVALID_PROJECT_AUTHENTICATION", "inherit", "inherit");
99
+ const ProjectKeyWithoutRequestType = createKnownErrorConstructor(InvalidProjectAuthentication, "PROJECT_KEY_WITHOUT_REQUEST_TYPE", () => [
100
+ 400,
101
+ "Either an API key or an admin access token was provided, but the x-stack-request-type header is missing. Set it to 'client', 'server', or 'admin' as appropriate.",
102
+ ], () => []);
103
+ const InvalidRequestType = createKnownErrorConstructor(InvalidProjectAuthentication, "INVALID_REQUEST_TYPE", (requestType) => [
104
+ 400,
105
+ `The x-stack-request-type header must be 'client', 'server', or 'admin', but was '${requestType}'.`,
106
+ ], (json) => [
107
+ json.details?.requestType ?? throwErr("requestType not found in InvalidRequestType details"),
108
+ ]);
109
+ const RequestTypeWithoutProjectId = createKnownErrorConstructor(InvalidProjectAuthentication, "REQUEST_TYPE_WITHOUT_PROJECT_ID", (requestType) => [
110
+ 400,
111
+ `The x-stack-request-type header was '${requestType}', but the x-stack-project-id header was not provided.`,
112
+ {
113
+ requestType,
114
+ },
115
+ ], (json) => [json.requestType]);
92
116
  const InvalidPublishableClientKey = createKnownErrorConstructor(InvalidProjectAuthentication, "INVALID_PUBLISHABLE_CLIENT_KEY", () => [
93
117
  401,
94
118
  "The publishable key is not valid for the given project. Does the project and/or the key exist?",
@@ -114,6 +138,10 @@ const InvalidProjectForAdminAccessToken = createKnownErrorConstructor(InvalidAdm
114
138
  401,
115
139
  "Admin access token not valid for this project.",
116
140
  ], () => []);
141
+ const AdminAccessTokenIsNotAdmin = createKnownErrorConstructor(InvalidAdminAccessToken, "ADMIN_ACCESS_TOKEN_IS_NOT_ADMIN", () => [
142
+ 401,
143
+ "Admin access token does not have the required permissions to access this project.",
144
+ ], () => []);
117
145
  const ProjectAuthenticationRequired = createKnownErrorConstructor(ProjectAuthenticationError, "PROJECT_AUTHENTICATION_REQUIRED", "inherit", "inherit");
118
146
  const ClientAuthenticationRequired = createKnownErrorConstructor(ProjectAuthenticationRequired, "CLIENT_AUTHENTICATION_REQUIRED", () => [
119
147
  401,
@@ -268,12 +296,52 @@ const EmailAlreadyVerified = createKnownErrorConstructor(KnownError, "EMAIL_ALRE
268
296
  400,
269
297
  "The e-mail is already verified.",
270
298
  ], () => []);
299
+ const PermissionNotFound = createKnownErrorConstructor(KnownError, "PERMISSION_NOT_FOUND", (permissionId) => [
300
+ 404,
301
+ `Permission ${permissionId} not found. Make sure you created it on the dashboard.`,
302
+ {
303
+ permissionId,
304
+ },
305
+ ], (json) => [json.details.permissionId]);
306
+ const PermissionScopeMismatch = createKnownErrorConstructor(KnownError, "PERMISSION_SCOPE_MISMATCH", (permissionId, permissionScope, testScope) => {
307
+ return [
308
+ 400,
309
+ `The scope of the permission with ID ${permissionId} is \`${permissionScope.type}\` but you tested against permissions of scope \`${testScope.type}\`. ${{
310
+ "global": `Please don't specify any teams when using global permissions. For example: \`user.hasPermission(${JSON.stringify(permissionId)})\`.`,
311
+ "any-team": `Please specify the team. For example: \`user.hasPermission(team, ${JSON.stringify(permissionId)})\`.`,
312
+ "specific-team": `Please specify the team. For example: \`user.hasPermission(team, ${JSON.stringify(permissionId)})\`.`,
313
+ }[permissionScope.type]}`,
314
+ {
315
+ permissionId,
316
+ permissionScope,
317
+ testScope,
318
+ },
319
+ ];
320
+ }, (json) => [json.details.permissionId, json.details.permissionScope, json.details.testScope]);
321
+ const UserNotInTeam = createKnownErrorConstructor(KnownError, "USER_NOT_IN_TEAM", (userId, teamId) => [
322
+ 400,
323
+ `User ${userId} is not in team ${teamId}.`,
324
+ {
325
+ userId,
326
+ teamId,
327
+ },
328
+ ], (json) => [json.details.userId, json.details.teamId]);
329
+ const TeamNotFound = createKnownErrorConstructor(KnownError, "TEAM_NOT_FOUND", (teamId) => [
330
+ 404,
331
+ `Team ${teamId} not found.`,
332
+ {
333
+ teamId,
334
+ },
335
+ ], (json) => [json.details.teamId]);
271
336
  export const KnownErrors = {
272
337
  UnsupportedError,
273
338
  SchemaError,
274
339
  AllOverloadsFailed,
275
340
  ProjectAuthenticationError,
276
341
  InvalidProjectAuthentication,
342
+ ProjectKeyWithoutRequestType,
343
+ InvalidRequestType,
344
+ RequestTypeWithoutProjectId,
277
345
  InvalidPublishableClientKey,
278
346
  InvalidSecretServerKey,
279
347
  InvalidSuperSecretAdminKey,
@@ -281,6 +349,7 @@ export const KnownErrors = {
281
349
  UnparsableAdminAccessToken,
282
350
  AdminAccessTokenExpired,
283
351
  InvalidProjectForAdminAccessToken,
352
+ AdminAccessTokenIsNotAdmin,
284
353
  ProjectAuthenticationRequired,
285
354
  ClientAuthenticationRequired,
286
355
  ServerAuthenticationRequired,
@@ -326,6 +395,9 @@ export const KnownErrors = {
326
395
  PasswordResetCodeAlreadyUsed,
327
396
  PasswordMismatch,
328
397
  EmailAlreadyVerified,
398
+ PermissionNotFound,
399
+ PermissionScopeMismatch,
400
+ TeamNotFound,
329
401
  };
330
402
  // ensure that all known error codes are unique
331
403
  const knownErrorCodes = new Set();
@@ -1,6 +1,6 @@
1
1
  export function throwErr(...args) {
2
2
  if (typeof args[0] === "string") {
3
- throw new Error(args[0]);
3
+ throw new StackAssertionError(args[0]);
4
4
  }
5
5
  else if (args[0] instanceof Error) {
6
6
  throw args[0];
@@ -28,7 +28,12 @@ export function registerErrorSink(sink) {
28
28
  }
29
29
  errorSinks.add(sink);
30
30
  }
31
- registerErrorSink((location, ...args) => console.error(`Error in ${location}:`, ...args));
31
+ registerErrorSink((location, ...args) => {
32
+ console.error(`Error in ${location}:`, ...args);
33
+ if (process.env.NODE_ENV === "development") {
34
+ debugger;
35
+ }
36
+ });
32
37
  export function captureError(location, error) {
33
38
  for (const sink of errorSinks) {
34
39
  sink(location, error);
@@ -1,5 +1,6 @@
1
1
  export declare function typedToLowercase<S extends string>(s: S): Lowercase<S>;
2
2
  export declare function typedToUppercase<S extends string>(s: S): Uppercase<S>;
3
+ export declare function typedCapitalize<S extends string>(s: S): Capitalize<S>;
3
4
  /**
4
5
  * Returns all whitespace character at the start of the string.
5
6
  *
@@ -4,6 +4,9 @@ export function typedToLowercase(s) {
4
4
  export function typedToUppercase(s) {
5
5
  return s.toUpperCase();
6
6
  }
7
+ export function typedCapitalize(s) {
8
+ return s.charAt(0).toUpperCase() + s.slice(1);
9
+ }
7
10
  /**
8
11
  * Returns all whitespace character at the start of the string.
9
12
  *
@@ -0,0 +1 @@
1
+ export type IsAny<T> = 0 extends (1 & T) ? true : false;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackframe/stack-shared",
3
- "version": "2.3.6",
3
+ "version": "2.4.1",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -8,13 +8,23 @@
8
8
  "dist"
9
9
  ],
10
10
  "peerDependencies": {
11
- "react": "^18.2"
11
+ "react": "^18.2",
12
+ "yup": "^1.4.0"
13
+ },
14
+ "peerDependenciesMeta": {
15
+ "react": {
16
+ "optional": true
17
+ },
18
+ "yup": {
19
+ "optional": true
20
+ }
12
21
  },
13
22
  "dependencies": {
14
23
  "bcrypt": "^5.1.1",
15
24
  "jose": "^5.2.2",
16
25
  "oauth4webapi": "^2.10.3",
17
- "uuid": "^9.0.1"
26
+ "uuid": "^9.0.1",
27
+ "@stackframe/stack-sc": "1.5.1"
18
28
  },
19
29
  "devDependencies": {
20
30
  "@types/bcrypt": "^5.0.2",