@stackframe/stack-shared 2.4.0 → 2.4.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/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";
@@ -17,6 +17,8 @@ export type OAuthProviderUpdateOptions = {
17
17
  tenantId?: string;
18
18
  });
19
19
  export type ProjectUpdateOptions = {
20
+ displayName?: string;
21
+ description?: string;
20
22
  isProductionMode?: boolean;
21
23
  config?: {
22
24
  domains?: {
@@ -27,6 +29,7 @@ export type ProjectUpdateOptions = {
27
29
  credentialEnabled?: boolean;
28
30
  magicLinkEnabled?: boolean;
29
31
  allowLocalhost?: boolean;
32
+ createTeamOnSignUp?: boolean;
30
33
  };
31
34
  };
32
35
  export type ApiKeySetBaseJson = {
@@ -3,12 +3,13 @@ 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
+ import { ProjectUpdateOptions } from './adminInterface';
7
+ type UserCustomizableJson = {
8
8
  readonly displayName: string | null;
9
9
  readonly clientMetadata: ReadonlyJson;
10
10
  };
11
11
  export type UserJson = UserCustomizableJson & {
12
+ readonly projectId: string;
12
13
  readonly id: string;
13
14
  readonly primaryEmail: string | null;
14
15
  readonly primaryEmailVerified: boolean;
@@ -16,11 +17,15 @@ export type UserJson = UserCustomizableJson & {
16
17
  readonly clientMetadata: ReadonlyJson;
17
18
  readonly profileImageUrl: string | null;
18
19
  readonly signedUpAtMillis: number;
20
+ /**
21
+ * not used anymore, for backwards compatibility
22
+ */
19
23
  readonly authMethod: "credential" | "oauth";
20
24
  readonly hasPassword: boolean;
21
25
  readonly authWithEmail: boolean;
22
26
  readonly oauthProviders: readonly string[];
23
27
  };
28
+ export type UserUpdateJson = Partial<UserCustomizableJson>;
24
29
  export type ClientProjectJson = {
25
30
  readonly id: string;
26
31
  readonly credentialEnabled: boolean;
@@ -65,6 +70,7 @@ export type ProjectJson = {
65
70
  oauthProviders: OAuthProviderConfigJson[];
66
71
  emailConfig?: EmailConfigJson;
67
72
  domains: DomainConfigJson[];
73
+ createTeamOnSignUp: boolean;
68
74
  };
69
75
  };
70
76
  export type OAuthProviderConfigJson = {
@@ -98,6 +104,30 @@ export type ProductionModeError = {
98
104
  errorMessage: string;
99
105
  fixUrlRelative: string;
100
106
  };
107
+ export type OrglikeJson = {
108
+ id: string;
109
+ displayName: string;
110
+ createdAtMillis: number;
111
+ };
112
+ export type TeamJson = OrglikeJson;
113
+ export type OrganizationJson = OrglikeJson;
114
+ export type TeamMemberJson = {
115
+ userId: string;
116
+ teamId: string;
117
+ displayName: string | null;
118
+ };
119
+ export type PermissionDefinitionScopeJson = {
120
+ type: "global";
121
+ } | {
122
+ type: "any-team";
123
+ } | {
124
+ type: "specific-team";
125
+ teamId: string;
126
+ };
127
+ export type PermissionDefinitionJson = {
128
+ id: string;
129
+ scope: PermissionDefinitionScopeJson;
130
+ };
101
131
  export declare class StackClientInterface {
102
132
  readonly options: ClientInterfaceOptions;
103
133
  constructor(options: ClientInterfaceOptions);
@@ -141,9 +171,18 @@ export declare class StackClientInterface {
141
171
  callOAuthCallback(oauthParams: URLSearchParams, redirectUri: string, codeVerifier: string, state: string, tokenStore: TokenStore): Promise<oauth.OAuth2TokenEndpointResponse>;
142
172
  signOut(tokenStore: TokenStore): Promise<void>;
143
173
  getClientUserByToken(tokenStore: TokenStore): Promise<Result<UserJson>>;
174
+ listClientUserTeamPermissions(options: {
175
+ teamId: string;
176
+ type: 'global' | 'team';
177
+ direct: boolean;
178
+ }, tokenStore: TokenStore): Promise<PermissionDefinitionJson[]>;
179
+ listClientUserTeams(tokenStore: TokenStore): Promise<TeamJson[]>;
144
180
  getClientProject(): Promise<Result<ClientProjectJson>>;
145
- setClientUserCustomizableData(update: Partial<UserCustomizableJson>, tokenStore: TokenStore): Promise<void>;
181
+ setClientUserCustomizableData(update: UserUpdateJson, tokenStore: TokenStore): Promise<void>;
146
182
  listProjects(tokenStore: TokenStore): Promise<ProjectJson[]>;
147
- createProject(project: Pick<ProjectJson, "displayName" | "description">, tokenStore: TokenStore): Promise<ProjectJson>;
183
+ createProject(project: ProjectUpdateOptions & {
184
+ displayName: string;
185
+ }, tokenStore: TokenStore): Promise<ProjectJson>;
148
186
  }
149
187
  export declare function getProductionModeErrors(project: ProjectJson): ProductionModeError[];
188
+ 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",
@@ -124,6 +126,8 @@ export class StackClientInterface {
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
  /**
@@ -148,8 +152,10 @@ export class StackClientInterface {
148
152
  ...'projectOwnerTokens' in this.options ? {
149
153
  "X-Stack-Admin-Access-Token": (await this.options.projectOwnerTokens?.getOrWait())?.accessToken ?? "",
150
154
  } : {},
155
+ "X-Stack-Random-Nonce": generateSecureRandomString(),
151
156
  ...options.headers,
152
157
  },
158
+ cache: "no-store",
153
159
  };
154
160
  const rawRes = await fetch(url, params);
155
161
  const processedRes = await this._processResponse(rawRes);
@@ -433,6 +439,16 @@ export class StackClientInterface {
433
439
  return Result.error(new Error("Failed to get user"));
434
440
  return Result.ok(user);
435
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
+ }
436
452
  async getClientProject() {
437
453
  const response = await this.sendClientRequest("/projects/" + this.options.projectId, {}, null);
438
454
  const project = await response.json();
@@ -474,7 +490,7 @@ export class StackClientInterface {
474
490
  }
475
491
  export function getProductionModeErrors(project) {
476
492
  const errors = [];
477
- const fixUrlRelative = `/projects/${encodeURIComponent(project.id)}/auth/urls-and-callbacks`;
493
+ const fixUrlRelative = `/projects/${project.id}/auth/urls-and-callbacks`;
478
494
  if (project.evaluatedConfig.allowLocalhost) {
479
495
  errors.push({
480
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;
@@ -24,7 +39,39 @@ export declare class StackServerInterface extends StackClientInterface {
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
  }
@@ -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 = {
@@ -226,5 +227,14 @@ export declare const KnownErrors: {
226
227
  EmailAlreadyVerified: KnownErrorConstructor<KnownError & KnownErrorBrand<"EMAIL_ALREADY_VERIFIED">, []> & {
227
228
  errorCode: "EMAIL_ALREADY_VERIFIED";
228
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
+ };
229
239
  };
230
240
  export {};
@@ -296,6 +296,43 @@ const EmailAlreadyVerified = createKnownErrorConstructor(KnownError, "EMAIL_ALRE
296
296
  400,
297
297
  "The e-mail is already verified.",
298
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]);
299
336
  export const KnownErrors = {
300
337
  UnsupportedError,
301
338
  SchemaError,
@@ -358,6 +395,9 @@ export const KnownErrors = {
358
395
  PasswordResetCodeAlreadyUsed,
359
396
  PasswordMismatch,
360
397
  EmailAlreadyVerified,
398
+ PermissionNotFound,
399
+ PermissionScopeMismatch,
400
+ TeamNotFound,
361
401
  };
362
402
  // ensure that all known error codes are unique
363
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];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackframe/stack-shared",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -23,7 +23,8 @@
23
23
  "bcrypt": "^5.1.1",
24
24
  "jose": "^5.2.2",
25
25
  "oauth4webapi": "^2.10.3",
26
- "uuid": "^9.0.1"
26
+ "uuid": "^9.0.1",
27
+ "@stackframe/stack-sc": "1.5.1"
27
28
  },
28
29
  "devDependencies": {
29
30
  "@types/bcrypt": "^5.0.2",