@stackframe/stack-shared 2.7.25 → 2.7.27

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @stackframe/stack-shared
2
2
 
3
+ ## 2.7.27
4
+
5
+ ### Patch Changes
6
+
7
+ - Various changes
8
+
9
+ ## 2.7.26
10
+
11
+ ### Patch Changes
12
+
13
+ - Various changes
14
+
3
15
  ## 2.7.25
4
16
 
5
17
  ## 2.7.24
@@ -1,6 +1,7 @@
1
1
  import { InternalSession } from "../sessions";
2
2
  import { ApiKeysCrud } from "./crud/api-keys";
3
3
  import { EmailTemplateCrud, EmailTemplateType } from "./crud/email-templates";
4
+ import { InternalEmailsCrud } from "./crud/emails";
4
5
  import { ProjectsCrud } from "./crud/projects";
5
6
  import { SvixTokenCrud } from "./crud/svix-token";
6
7
  import { TeamPermissionDefinitionsCrud } from "./crud/team-permissions";
@@ -61,4 +62,5 @@ export declare class StackAdminInterface extends StackServerInterface {
61
62
  success: boolean;
62
63
  error_message?: string;
63
64
  }>;
65
+ listSentEmails(): Promise<InternalEmailsCrud["Admin"]["List"]>;
64
66
  }
@@ -136,4 +136,10 @@ export class StackAdminInterface extends StackServerInterface {
136
136
  }, null);
137
137
  return await response.json();
138
138
  }
139
+ async listSentEmails() {
140
+ const response = await this.sendAdminRequest("/internal/emails", {
141
+ method: "GET",
142
+ }, null);
143
+ return await response.json();
144
+ }
139
145
  }
@@ -7,6 +7,7 @@ import { ContactChannelsCrud } from './crud/contact-channels';
7
7
  import { CurrentUserCrud } from './crud/current-user';
8
8
  import { ConnectedAccountAccessTokenCrud } from './crud/oauth';
9
9
  import { InternalProjectsCrud, ProjectsCrud } from './crud/projects';
10
+ import { SessionsCrud } from './crud/sessions';
10
11
  import { TeamInvitationCrud } from './crud/team-invitation';
11
12
  import { TeamMemberProfilesCrud } from './crud/team-member-profiles';
12
13
  import { TeamPermissionsCrud } from './crud/team-permissions';
@@ -193,6 +194,9 @@ export declare class StackClientInterface {
193
194
  createClientContactChannel(data: ContactChannelsCrud['Client']['Create'], session: InternalSession): Promise<ContactChannelsCrud['Client']['Read']>;
194
195
  updateClientContactChannel(id: string, data: ContactChannelsCrud['Client']['Update'], session: InternalSession): Promise<ContactChannelsCrud['Client']['Read']>;
195
196
  deleteClientContactChannel(id: string, session: InternalSession): Promise<void>;
197
+ deleteSession(sessionId: string, session: InternalSession): Promise<void>;
198
+ listSessions(session: InternalSession): Promise<SessionsCrud['Client']['List']>;
196
199
  listClientContactChannels(session: InternalSession): Promise<ContactChannelsCrud['Client']['Read'][]>;
197
200
  sendCurrentUserContactChannelVerificationEmail(contactChannelId: string, callbackUrl: string, session: InternalSession): Promise<Result<undefined, KnownErrors["EmailAlreadyVerified"]>>;
201
+ cliLogin(loginCode: string, refreshToken: string, session: InternalSession): Promise<Result<undefined, KnownErrors["SchemaError"]>>;
198
202
  }
@@ -876,6 +876,17 @@ export class StackClientInterface {
876
876
  method: "DELETE",
877
877
  }, session);
878
878
  }
879
+ async deleteSession(sessionId, session) {
880
+ await this.sendClientRequest(`/auth/sessions/${sessionId}?user_id=me`, {
881
+ method: "DELETE",
882
+ }, session);
883
+ }
884
+ async listSessions(session) {
885
+ const response = await this.sendClientRequest("/auth/sessions?user_id=me", {
886
+ method: "GET",
887
+ }, session);
888
+ return await response.json();
889
+ }
879
890
  async listClientContactChannels(session) {
880
891
  const response = await this.sendClientRequest("/contact-channels?user_id=me", {
881
892
  method: "GET",
@@ -896,4 +907,20 @@ export class StackClientInterface {
896
907
  }
897
908
  return Result.ok(undefined);
898
909
  }
910
+ async cliLogin(loginCode, refreshToken, session) {
911
+ const responseOrError = await this.sendClientRequestAndCatchKnownError("/auth/cli/complete", {
912
+ method: "POST",
913
+ headers: {
914
+ "Content-Type": "application/json"
915
+ },
916
+ body: JSON.stringify({
917
+ login_code: loginCode,
918
+ refresh_token: refreshToken,
919
+ }),
920
+ }, session, [KnownErrors.SchemaError]);
921
+ if (responseOrError.status === "error") {
922
+ return Result.error(responseOrError.error);
923
+ }
924
+ return Result.ok(undefined);
925
+ }
899
926
  }
@@ -0,0 +1,34 @@
1
+ import { CrudTypeOf } from "../../crud";
2
+ export declare const sentEmailReadSchema: import("yup").ObjectSchema<{
3
+ id: string;
4
+ subject: string;
5
+ sent_at_millis: number;
6
+ to: string[] | undefined;
7
+ sender_config: {} | null;
8
+ error: {} | null | undefined;
9
+ }, import("yup").AnyObject, {
10
+ id: undefined;
11
+ subject: undefined;
12
+ sent_at_millis: undefined;
13
+ to: undefined;
14
+ sender_config: undefined;
15
+ error: undefined;
16
+ }, "">;
17
+ export declare const internalEmailsCrud: import("../../crud").CrudSchemaFromOptions<{
18
+ clientReadSchema: import("yup").ObjectSchema<{
19
+ id: string;
20
+ subject: string;
21
+ sent_at_millis: number;
22
+ to: string[] | undefined;
23
+ sender_config: {} | null;
24
+ error: {} | null | undefined;
25
+ }, import("yup").AnyObject, {
26
+ id: undefined;
27
+ subject: undefined;
28
+ sent_at_millis: undefined;
29
+ to: undefined;
30
+ sender_config: undefined;
31
+ error: undefined;
32
+ }, "">;
33
+ }>;
34
+ export type InternalEmailsCrud = CrudTypeOf<typeof internalEmailsCrud>;
@@ -0,0 +1,13 @@
1
+ import { createCrud } from "../../crud";
2
+ import * as fieldSchema from "../../schema-fields";
3
+ export const sentEmailReadSchema = fieldSchema.yupObject({
4
+ id: fieldSchema.yupString().defined(),
5
+ subject: fieldSchema.yupString().defined(),
6
+ sent_at_millis: fieldSchema.yupNumber().defined(),
7
+ to: fieldSchema.yupArray(fieldSchema.yupString().defined()),
8
+ sender_config: fieldSchema.yupMixed().nullable().defined(),
9
+ error: fieldSchema.yupMixed().nullable().optional(),
10
+ }).defined();
11
+ export const internalEmailsCrud = createCrud({
12
+ clientReadSchema: sentEmailReadSchema,
13
+ });
@@ -64,6 +64,7 @@ export declare const projectsCrudAdminReadSchema: import("yup").ObjectSchema<{
64
64
  team_member_default_permissions: {
65
65
  id: string;
66
66
  }[];
67
+ oauth_account_merge_strategy: "link_method" | "raise_error" | "allow_duplicates";
67
68
  };
68
69
  }, import("yup").AnyObject, {
69
70
  id: undefined;
@@ -96,6 +97,7 @@ export declare const projectsCrudAdminReadSchema: import("yup").ObjectSchema<{
96
97
  create_team_on_sign_up: undefined;
97
98
  team_creator_default_permissions: undefined;
98
99
  team_member_default_permissions: undefined;
100
+ oauth_account_merge_strategy: undefined;
99
101
  };
100
102
  }, "">;
101
103
  export declare const projectsCrudClientReadSchema: import("yup").ObjectSchema<{
@@ -166,6 +168,7 @@ export declare const projectsCrudAdminUpdateSchema: import("yup").ObjectSchema<{
166
168
  team_member_default_permissions?: {
167
169
  id: string;
168
170
  }[] | undefined;
171
+ oauth_account_merge_strategy?: "link_method" | "raise_error" | "allow_duplicates" | undefined;
169
172
  } | undefined;
170
173
  }, import("yup").AnyObject, {
171
174
  display_name: undefined;
@@ -214,6 +217,7 @@ export declare const projectsCrudAdminCreateSchema: import("yup").ObjectSchema<{
214
217
  team_member_default_permissions?: {
215
218
  id: string;
216
219
  }[] | undefined;
220
+ oauth_account_merge_strategy?: "link_method" | "raise_error" | "allow_duplicates" | undefined;
217
221
  } | undefined;
218
222
  } & {
219
223
  display_name: string;
@@ -300,6 +304,7 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
300
304
  team_member_default_permissions: {
301
305
  id: string;
302
306
  }[];
307
+ oauth_account_merge_strategy: "link_method" | "raise_error" | "allow_duplicates";
303
308
  };
304
309
  }, import("yup").AnyObject, {
305
310
  id: undefined;
@@ -332,6 +337,7 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
332
337
  create_team_on_sign_up: undefined;
333
338
  team_creator_default_permissions: undefined;
334
339
  team_member_default_permissions: undefined;
340
+ oauth_account_merge_strategy: undefined;
335
341
  };
336
342
  }, "">;
337
343
  adminUpdateSchema: import("yup").ObjectSchema<{
@@ -375,6 +381,7 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
375
381
  team_member_default_permissions?: {
376
382
  id: string;
377
383
  }[] | undefined;
384
+ oauth_account_merge_strategy?: "link_method" | "raise_error" | "allow_duplicates" | undefined;
378
385
  } | undefined;
379
386
  }, import("yup").AnyObject, {
380
387
  display_name: undefined;
@@ -456,6 +463,7 @@ export declare const internalProjectsCrud: import("../../crud").CrudSchemaFromOp
456
463
  team_member_default_permissions: {
457
464
  id: string;
458
465
  }[];
466
+ oauth_account_merge_strategy: "link_method" | "raise_error" | "allow_duplicates";
459
467
  };
460
468
  }, import("yup").AnyObject, {
461
469
  id: undefined;
@@ -488,6 +496,7 @@ export declare const internalProjectsCrud: import("../../crud").CrudSchemaFromOp
488
496
  create_team_on_sign_up: undefined;
489
497
  team_creator_default_permissions: undefined;
490
498
  team_member_default_permissions: undefined;
499
+ oauth_account_merge_strategy: undefined;
491
500
  };
492
501
  }, "">;
493
502
  clientCreateSchema: import("yup").ObjectSchema<{
@@ -531,6 +540,7 @@ export declare const internalProjectsCrud: import("../../crud").CrudSchemaFromOp
531
540
  team_member_default_permissions?: {
532
541
  id: string;
533
542
  }[] | undefined;
543
+ oauth_account_merge_strategy?: "link_method" | "raise_error" | "allow_duplicates" | undefined;
534
544
  } | undefined;
535
545
  } & {
536
546
  display_name: string;
@@ -74,6 +74,7 @@ export const projectsCrudAdminReadSchema = yupObject({
74
74
  create_team_on_sign_up: schemaFields.projectCreateTeamOnSignUpSchema.defined(),
75
75
  team_creator_default_permissions: yupArray(teamPermissionSchema.defined()).defined(),
76
76
  team_member_default_permissions: yupArray(teamPermissionSchema.defined()).defined(),
77
+ oauth_account_merge_strategy: schemaFields.oauthAccountMergeStrategySchema.defined(),
77
78
  }).defined(),
78
79
  }).defined();
79
80
  export const projectsCrudClientReadSchema = yupObject({
@@ -107,6 +108,7 @@ export const projectsCrudAdminUpdateSchema = yupObject({
107
108
  create_team_on_sign_up: schemaFields.projectCreateTeamOnSignUpSchema.optional(),
108
109
  team_creator_default_permissions: yupArray(teamPermissionSchema.defined()).optional(),
109
110
  team_member_default_permissions: yupArray(teamPermissionSchema.defined()).optional(),
111
+ oauth_account_merge_strategy: schemaFields.oauthAccountMergeStrategySchema.optional(),
110
112
  }).optional().default(undefined),
111
113
  }).defined();
112
114
  export const projectsCrudAdminCreateSchema = projectsCrudAdminUpdateSchema.concat(yupObject({
@@ -0,0 +1,144 @@
1
+ import { CrudTypeOf } from "../../crud";
2
+ export declare const sessionsCrudServerCreateSchema: import("yup").ObjectSchema<{
3
+ user_id: string;
4
+ expires_in_millis: number;
5
+ is_impersonation: boolean;
6
+ }, import("yup").AnyObject, {
7
+ user_id: undefined;
8
+ expires_in_millis: number;
9
+ is_impersonation: false;
10
+ }, "">;
11
+ export declare const sessionsCreateOutputSchema: import("yup").ObjectSchema<{
12
+ refresh_token: string;
13
+ access_token: string;
14
+ }, import("yup").AnyObject, {
15
+ refresh_token: undefined;
16
+ access_token: undefined;
17
+ }, "">;
18
+ export declare const sessionsCrudReadSchema: import("yup").ObjectSchema<{
19
+ id: string;
20
+ user_id: string;
21
+ created_at: number;
22
+ is_impersonation: boolean;
23
+ last_used_at: number | undefined;
24
+ is_current_session: boolean | undefined;
25
+ last_used_at_end_user_ip_info: {
26
+ countryCode?: string | null | undefined;
27
+ regionCode?: string | null | undefined;
28
+ cityName?: string | null | undefined;
29
+ latitude?: number | null | undefined;
30
+ longitude?: number | null | undefined;
31
+ tzIdentifier?: string | null | undefined;
32
+ ip: string;
33
+ } | undefined;
34
+ }, import("yup").AnyObject, {
35
+ id: undefined;
36
+ user_id: undefined;
37
+ created_at: undefined;
38
+ is_impersonation: undefined;
39
+ last_used_at: undefined;
40
+ is_current_session: undefined;
41
+ last_used_at_end_user_ip_info: {
42
+ ip: undefined;
43
+ countryCode: undefined;
44
+ regionCode: undefined;
45
+ cityName: undefined;
46
+ latitude: undefined;
47
+ longitude: undefined;
48
+ tzIdentifier: undefined;
49
+ };
50
+ }, "">;
51
+ export declare const sessionsCrudDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
52
+ export declare const sessionsCrud: import("../../crud").CrudSchemaFromOptions<{
53
+ serverReadSchema: import("yup").ObjectSchema<{
54
+ id: string;
55
+ user_id: string;
56
+ created_at: number;
57
+ is_impersonation: boolean;
58
+ last_used_at: number | undefined;
59
+ is_current_session: boolean | undefined;
60
+ last_used_at_end_user_ip_info: {
61
+ countryCode?: string | null | undefined;
62
+ regionCode?: string | null | undefined;
63
+ cityName?: string | null | undefined;
64
+ latitude?: number | null | undefined;
65
+ longitude?: number | null | undefined;
66
+ tzIdentifier?: string | null | undefined;
67
+ ip: string;
68
+ } | undefined;
69
+ }, import("yup").AnyObject, {
70
+ id: undefined;
71
+ user_id: undefined;
72
+ created_at: undefined;
73
+ is_impersonation: undefined;
74
+ last_used_at: undefined;
75
+ is_current_session: undefined;
76
+ last_used_at_end_user_ip_info: {
77
+ ip: undefined;
78
+ countryCode: undefined;
79
+ regionCode: undefined;
80
+ cityName: undefined;
81
+ latitude: undefined;
82
+ longitude: undefined;
83
+ tzIdentifier: undefined;
84
+ };
85
+ }, "">;
86
+ serverDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
87
+ clientReadSchema: import("yup").ObjectSchema<{
88
+ id: string;
89
+ user_id: string;
90
+ created_at: number;
91
+ is_impersonation: boolean;
92
+ last_used_at: number | undefined;
93
+ is_current_session: boolean | undefined;
94
+ last_used_at_end_user_ip_info: {
95
+ countryCode?: string | null | undefined;
96
+ regionCode?: string | null | undefined;
97
+ cityName?: string | null | undefined;
98
+ latitude?: number | null | undefined;
99
+ longitude?: number | null | undefined;
100
+ tzIdentifier?: string | null | undefined;
101
+ ip: string;
102
+ } | undefined;
103
+ }, import("yup").AnyObject, {
104
+ id: undefined;
105
+ user_id: undefined;
106
+ created_at: undefined;
107
+ is_impersonation: undefined;
108
+ last_used_at: undefined;
109
+ is_current_session: undefined;
110
+ last_used_at_end_user_ip_info: {
111
+ ip: undefined;
112
+ countryCode: undefined;
113
+ regionCode: undefined;
114
+ cityName: undefined;
115
+ latitude: undefined;
116
+ longitude: undefined;
117
+ tzIdentifier: undefined;
118
+ };
119
+ }, "">;
120
+ clientDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
121
+ docs: {
122
+ serverList: {
123
+ summary: string;
124
+ description: string;
125
+ tags: string[];
126
+ };
127
+ serverDelete: {
128
+ summary: string;
129
+ description: string;
130
+ tags: string[];
131
+ };
132
+ clientList: {
133
+ summary: string;
134
+ description: string;
135
+ tags: string[];
136
+ };
137
+ clientDelete: {
138
+ summary: string;
139
+ description: string;
140
+ tags: string[];
141
+ };
142
+ };
143
+ }>;
144
+ export type SessionsCrud = CrudTypeOf<typeof sessionsCrud>;
@@ -0,0 +1,55 @@
1
+ import { createCrud } from "../../crud";
2
+ import { yupBoolean, yupMixed, yupNumber, yupObject, yupString } from "../../schema-fields";
3
+ import { geoInfoSchema } from "../../utils/geo";
4
+ // Create
5
+ export const sessionsCrudServerCreateSchema = yupObject({
6
+ user_id: yupString().uuid().defined(),
7
+ expires_in_millis: yupNumber().max(1000 * 60 * 60 * 24 * 367).default(1000 * 60 * 60 * 24 * 365),
8
+ is_impersonation: yupBoolean().default(false),
9
+ }).defined();
10
+ export const sessionsCreateOutputSchema = yupObject({
11
+ refresh_token: yupString().defined(),
12
+ access_token: yupString().defined(),
13
+ }).defined();
14
+ export const sessionsCrudReadSchema = yupObject({
15
+ id: yupString().defined(),
16
+ user_id: yupString().uuid().defined(),
17
+ created_at: yupNumber().defined(),
18
+ is_impersonation: yupBoolean().defined(),
19
+ last_used_at: yupNumber().optional(),
20
+ is_current_session: yupBoolean(),
21
+ // TODO move this to a shared type
22
+ // TODO: what about if not trusted?
23
+ last_used_at_end_user_ip_info: geoInfoSchema.optional(),
24
+ }).defined();
25
+ // Delete
26
+ export const sessionsCrudDeleteSchema = yupMixed();
27
+ export const sessionsCrud = createCrud({
28
+ // serverCreateSchema: sessionsCrudServerCreateSchema,
29
+ serverReadSchema: sessionsCrudReadSchema,
30
+ serverDeleteSchema: sessionsCrudDeleteSchema,
31
+ clientReadSchema: sessionsCrudReadSchema,
32
+ clientDeleteSchema: sessionsCrudDeleteSchema,
33
+ docs: {
34
+ serverList: {
35
+ summary: "List sessions",
36
+ description: "List all sessions for the current user.",
37
+ tags: ["Sessions"],
38
+ },
39
+ serverDelete: {
40
+ summary: "Delete session",
41
+ description: "Delete a session by ID.",
42
+ tags: ["Sessions"],
43
+ },
44
+ clientList: {
45
+ summary: "List sessions",
46
+ description: "List all sessions for the current user.",
47
+ tags: ["Sessions"],
48
+ },
49
+ clientDelete: {
50
+ summary: "Delete session",
51
+ description: "Delete a session by ID.",
52
+ tags: ["Sessions"],
53
+ },
54
+ },
55
+ });
@@ -46,6 +46,40 @@ export declare const teamPermissionsCrud: import("../../crud").CrudSchemaFromOpt
46
46
  };
47
47
  }>;
48
48
  export type TeamPermissionsCrud = CrudTypeOf<typeof teamPermissionsCrud>;
49
+ export declare const teamPermissionCreatedWebhookEvent: {
50
+ type: string;
51
+ schema: import("yup").ObjectSchema<{
52
+ id: string;
53
+ user_id: string;
54
+ team_id: string;
55
+ }, import("yup").AnyObject, {
56
+ id: undefined;
57
+ user_id: undefined;
58
+ team_id: undefined;
59
+ }, "">;
60
+ metadata: {
61
+ summary: string;
62
+ description: string;
63
+ tags: string[];
64
+ };
65
+ };
66
+ export declare const teamPermissionDeletedWebhookEvent: {
67
+ type: string;
68
+ schema: import("yup").ObjectSchema<{
69
+ id: string;
70
+ user_id: string;
71
+ team_id: string;
72
+ }, import("yup").AnyObject, {
73
+ id: undefined;
74
+ user_id: undefined;
75
+ team_id: undefined;
76
+ }, "">;
77
+ metadata: {
78
+ summary: string;
79
+ description: string;
80
+ tags: string[];
81
+ };
82
+ };
49
83
  export declare const teamPermissionDefinitionsCrudAdminReadSchema: import("yup").ObjectSchema<{
50
84
  id: string;
51
85
  description: string | undefined;
@@ -36,6 +36,24 @@ export const teamPermissionsCrud = createCrud({
36
36
  },
37
37
  },
38
38
  });
39
+ export const teamPermissionCreatedWebhookEvent = {
40
+ type: "team_permission.created",
41
+ schema: teamPermissionsCrud.server.readSchema,
42
+ metadata: {
43
+ summary: "Team Permission Created",
44
+ description: "This event is triggered when a team permission is created.",
45
+ tags: ["Teams"],
46
+ },
47
+ };
48
+ export const teamPermissionDeletedWebhookEvent = {
49
+ type: "team_permission.deleted",
50
+ schema: teamPermissionsCrud.server.readSchema,
51
+ metadata: {
52
+ summary: "Team Permission Deleted",
53
+ description: "This event is triggered when a team permission is deleted.",
54
+ tags: ["Teams"],
55
+ },
56
+ };
39
57
  // =============== Team permission definitions =================
40
58
  export const teamPermissionDefinitionsCrudAdminReadSchema = yupObject({
41
59
  id: schemaFields.teamPermissionDefinitionIdSchema.defined(),
@@ -5,6 +5,7 @@ import { ClientInterfaceOptions, StackClientInterface } from "./clientInterface"
5
5
  import { ContactChannelsCrud } from "./crud/contact-channels";
6
6
  import { CurrentUserCrud } from "./crud/current-user";
7
7
  import { ConnectedAccountAccessTokenCrud } from "./crud/oauth";
8
+ import { SessionsCrud } from "./crud/sessions";
8
9
  import { TeamInvitationCrud } from "./crud/team-invitation";
9
10
  import { TeamMemberProfilesCrud } from "./crud/team-member-profiles";
10
11
  import { TeamMembershipsCrud } from "./crud/team-memberships";
@@ -74,7 +75,7 @@ export declare class StackServerInterface extends StackClientInterface {
74
75
  }): Promise<void>;
75
76
  updateServerUser(userId: string, update: UsersCrud['Server']['Update']): Promise<UsersCrud['Server']['Read']>;
76
77
  createServerProviderAccessToken(userId: string, provider: string, scope: string): Promise<ConnectedAccountAccessTokenCrud['Server']['Read']>;
77
- createServerUserSession(userId: string, expiresInMillis: number): Promise<{
78
+ createServerUserSession(userId: string, expiresInMillis: number, isImpersonation: boolean): Promise<{
78
79
  accessToken: string;
79
80
  refreshToken: string;
80
81
  }>;
@@ -95,6 +96,8 @@ export declare class StackServerInterface extends StackClientInterface {
95
96
  deleteServerContactChannel(userId: string, contactChannelId: string): Promise<void>;
96
97
  listServerContactChannels(userId: string): Promise<ContactChannelsCrud['Server']['Read'][]>;
97
98
  sendServerContactChannelVerificationEmail(userId: string, contactChannelId: string, callbackUrl: string): Promise<void>;
99
+ listServerSessions(userId: string): Promise<SessionsCrud['Server']['Read'][]>;
100
+ deleteServerSession(sessionId: string): Promise<void>;
98
101
  sendServerTeamInvitation(options: {
99
102
  email: string;
100
103
  teamId: string;
@@ -58,10 +58,11 @@ export class StackServerInterface extends StackClientInterface {
58
58
  return user;
59
59
  }
60
60
  async getServerUserById(userId) {
61
- const response = await this.sendServerRequest(urlString `/users/${userId}`, {}, null);
62
- const user = await response.json();
63
- if (!user)
64
- return Result.error(new Error("Failed to get user"));
61
+ const responseOrError = await this.sendServerRequestAndCatchKnownError(urlString `/users/${userId}`, {}, null, [KnownErrors.UserNotFound]);
62
+ if (responseOrError.status === "error") {
63
+ return Result.error(responseOrError.error);
64
+ }
65
+ const user = await responseOrError.data.json();
65
66
  return Result.ok(user);
66
67
  }
67
68
  async listServerTeamInvitations(options) {
@@ -182,7 +183,7 @@ export class StackServerInterface extends StackClientInterface {
182
183
  }, null);
183
184
  return await response.json();
184
185
  }
185
- async createServerUserSession(userId, expiresInMillis) {
186
+ async createServerUserSession(userId, expiresInMillis, isImpersonation) {
186
187
  const response = await this.sendServerRequest("/auth/sessions", {
187
188
  method: "POST",
188
189
  headers: {
@@ -191,6 +192,7 @@ export class StackServerInterface extends StackClientInterface {
191
192
  body: JSON.stringify({
192
193
  user_id: userId,
193
194
  expires_in_millis: expiresInMillis,
195
+ is_impersonation: isImpersonation,
194
196
  }),
195
197
  }, null);
196
198
  const result = await response.json();
@@ -285,6 +287,17 @@ export class StackServerInterface extends StackClientInterface {
285
287
  body: JSON.stringify({ callback_url: callbackUrl }),
286
288
  }, null);
287
289
  }
290
+ async listServerSessions(userId) {
291
+ const response = await this.sendServerRequest(urlString `/auth/sessions?user_id=${userId}`, {
292
+ method: "GET",
293
+ }, null);
294
+ return await response.json();
295
+ }
296
+ async deleteServerSession(sessionId) {
297
+ await this.sendServerRequest(urlString `/auth/sessions/${sessionId}`, {
298
+ method: "DELETE",
299
+ }, null);
300
+ }
288
301
  async sendServerTeamInvitation(options) {
289
302
  await this.sendServerRequest("/team-invitations/send-code", {
290
303
  method: "POST",
@@ -46,6 +46,11 @@ export type KnownErrors = {
46
46
  [K in keyof typeof KnownErrors]: InstanceType<typeof KnownErrors[K]>;
47
47
  };
48
48
  export declare const KnownErrors: {
49
+ CannotDeleteCurrentSession: KnownErrorConstructor<KnownError & KnownErrorBrand<"REFRESH_TOKEN_ERROR"> & {
50
+ constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
51
+ } & KnownErrorBrand<"CANNOT_DELETE_CURRENT_SESSION">, []> & {
52
+ errorCode: "CANNOT_DELETE_CURRENT_SESSION";
53
+ };
49
54
  UnsupportedError: KnownErrorConstructor<KnownError & KnownErrorBrand<"UNSUPPORTED_ERROR">, [originalErrorCode: string]> & {
50
55
  errorCode: "UNSUPPORTED_ERROR";
51
56
  };
@@ -194,7 +199,7 @@ export declare const KnownErrors: {
194
199
  };
195
200
  AccessTokenExpired: KnownErrorConstructor<KnownError & KnownErrorBrand<"SESSION_AUTHENTICATION_ERROR"> & {
196
201
  constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
197
- } & KnownErrorBrand<"INVALID_SESSION_AUTHENTICATION"> & KnownErrorBrand<"INVALID_ACCESS_TOKEN"> & KnownErrorBrand<"ACCESS_TOKEN_EXPIRED">, [expiredAt: Date | undefined]> & {
202
+ } & KnownErrorBrand<"INVALID_SESSION_AUTHENTICATION"> & KnownErrorBrand<"INVALID_ACCESS_TOKEN"> & KnownErrorBrand<"ACCESS_TOKEN_EXPIRED">, [Date | undefined]> & {
198
203
  errorCode: "ACCESS_TOKEN_EXPIRED";
199
204
  };
200
205
  InvalidProjectForAccessToken: KnownErrorConstructor<KnownError & KnownErrorBrand<"SESSION_AUTHENTICATION_ERROR"> & {
@@ -218,6 +223,9 @@ export declare const KnownErrors: {
218
223
  UserEmailAlreadyExists: KnownErrorConstructor<KnownError & KnownErrorBrand<"USER_EMAIL_ALREADY_EXISTS">, []> & {
219
224
  errorCode: "USER_EMAIL_ALREADY_EXISTS";
220
225
  };
226
+ EmailNotVerified: KnownErrorConstructor<KnownError & KnownErrorBrand<"EMAIL_NOT_VERIFIED">, []> & {
227
+ errorCode: "EMAIL_NOT_VERIFIED";
228
+ };
221
229
  UserIdDoesNotExist: KnownErrorConstructor<KnownError & KnownErrorBrand<"USER_ID_DOES_NOT_EXIST">, [userId: string]> & {
222
230
  errorCode: "USER_ID_DOES_NOT_EXIST";
223
231
  };
@@ -380,8 +388,11 @@ export declare const KnownErrors: {
380
388
  OAuthProviderAccessDenied: KnownErrorConstructor<KnownError & KnownErrorBrand<"OAUTH_PROVIDER_ACCESS_DENIED">, []> & {
381
389
  errorCode: "OAUTH_PROVIDER_ACCESS_DENIED";
382
390
  };
383
- ContactChannelAlreadyUsedForAuthBySomeoneElse: KnownErrorConstructor<KnownError & KnownErrorBrand<"CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE">, [type: "email"]> & {
391
+ ContactChannelAlreadyUsedForAuthBySomeoneElse: KnownErrorConstructor<KnownError & KnownErrorBrand<"CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE">, [type: "email", contactChannelValue?: string | undefined]> & {
384
392
  errorCode: "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE";
385
393
  };
394
+ InvalidPollingCodeError: KnownErrorConstructor<KnownError & KnownErrorBrand<"INVALID_POLLING_CODE">, [details?: Json | undefined]> & {
395
+ errorCode: "INVALID_POLLING_CODE";
396
+ };
386
397
  };
387
398
  export {};
@@ -245,7 +245,7 @@ const AccessTokenExpired = createKnownErrorConstructor(InvalidAccessToken, "ACCE
245
245
  401,
246
246
  `Access token has expired. Please refresh it and try again.${expiredAt ? ` (The access token expired at ${expiredAt.toISOString()}.)` : ""}`,
247
247
  { expired_at_millis: expiredAt?.getTime() ?? null },
248
- ], (json) => [json.expired_at_millis ?? undefined]);
248
+ ], (json) => [json.expired_at_millis ? new Date(json.expired_at_millis) : undefined]);
249
249
  const InvalidProjectForAccessToken = createKnownErrorConstructor(InvalidAccessToken, "INVALID_PROJECT_FOR_ACCESS_TOKEN", () => [
250
250
  401,
251
251
  "Access token not valid for this project.",
@@ -255,6 +255,10 @@ const RefreshTokenNotFoundOrExpired = createKnownErrorConstructor(RefreshTokenEr
255
255
  401,
256
256
  "Refresh token not found for this project, or the session has expired/been revoked.",
257
257
  ], () => []);
258
+ const CannotDeleteCurrentSession = createKnownErrorConstructor(RefreshTokenError, "CANNOT_DELETE_CURRENT_SESSION", () => [
259
+ 400,
260
+ "Cannot delete the current session.",
261
+ ], () => []);
258
262
  const ProviderRejected = createKnownErrorConstructor(RefreshTokenError, "PROVIDER_REJECTED", () => [
259
263
  401,
260
264
  "The provider refused to refresh their token. This usually means that the provider used to authenticate the user no longer regards this session as valid, and the user must re-authenticate.",
@@ -263,6 +267,10 @@ const UserEmailAlreadyExists = createKnownErrorConstructor(KnownError, "USER_EMA
263
267
  409,
264
268
  "User email already exists.",
265
269
  ], () => []);
270
+ const EmailNotVerified = createKnownErrorConstructor(KnownError, "EMAIL_NOT_VERIFIED", () => [
271
+ 400,
272
+ "The email is not verified.",
273
+ ], () => []);
266
274
  const CannotGetOwnUserWithoutUser = createKnownErrorConstructor(KnownError, "CANNOT_GET_OWN_USER_WITHOUT_USER", () => [
267
275
  400,
268
276
  "You have specified 'me' as a userId, but did not provide authentication for a user.",
@@ -531,12 +539,20 @@ const OAuthProviderAccessDenied = createKnownErrorConstructor(KnownError, "OAUTH
531
539
  400,
532
540
  "The OAuth provider denied access to the user.",
533
541
  ], () => []);
534
- const ContactChannelAlreadyUsedForAuthBySomeoneElse = createKnownErrorConstructor(KnownError, "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE", (type) => [
542
+ const ContactChannelAlreadyUsedForAuthBySomeoneElse = createKnownErrorConstructor(KnownError, "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE", (type, contactChannelValue) => [
535
543
  409,
536
- `This ${type} is already used for authentication by another account.`,
537
- { type },
538
- ], (json) => [json.type]);
544
+ contactChannelValue ?
545
+ `The ${type} (${contactChannelValue}) is already used for authentication by another account.` :
546
+ `This ${type} is already used for authentication by another account.`,
547
+ { type, contact_channel_value: contactChannelValue ?? null },
548
+ ], (json) => [json.type, json.contact_channel_value]);
549
+ const InvalidPollingCodeError = createKnownErrorConstructor(KnownError, "INVALID_POLLING_CODE", (details) => [
550
+ 400,
551
+ "The polling code is invalid or does not exist.",
552
+ details,
553
+ ], (json) => [json]);
539
554
  export const KnownErrors = {
555
+ CannotDeleteCurrentSession,
540
556
  UnsupportedError,
541
557
  BodyParsingError,
542
558
  SchemaError,
@@ -575,6 +591,7 @@ export const KnownErrors = {
575
591
  ProviderRejected,
576
592
  RefreshTokenNotFoundOrExpired,
577
593
  UserEmailAlreadyExists,
594
+ EmailNotVerified,
578
595
  UserIdDoesNotExist,
579
596
  UserNotFound,
580
597
  ApiKeyNotFound,
@@ -626,6 +643,7 @@ export const KnownErrors = {
626
643
  TeamPermissionNotFound,
627
644
  OAuthProviderAccessDenied,
628
645
  ContactChannelAlreadyUsedForAuthBySomeoneElse,
646
+ InvalidPollingCodeError,
629
647
  };
630
648
  // ensure that all known error codes are unique
631
649
  const knownErrorCodes = new Set();
@@ -72,6 +72,7 @@ export declare const oauthClientIdSchema: yup.StringSchema<string | undefined, y
72
72
  export declare const oauthClientSecretSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
73
73
  export declare const oauthFacebookConfigIdSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
74
74
  export declare const oauthMicrosoftTenantIdSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
75
+ export declare const oauthAccountMergeStrategySchema: yup.StringSchema<"link_method" | "raise_error" | "allow_duplicates" | undefined, yup.AnyObject, undefined, "">;
75
76
  export declare const emailTypeSchema: yup.StringSchema<"shared" | "standard" | undefined, yup.AnyObject, undefined, "">;
76
77
  export declare const emailSenderNameSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
77
78
  export declare const emailHostSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
@@ -248,6 +248,7 @@ export const oauthClientIdSchema = yupString().meta({ openapiField: { descriptio
248
248
  export const oauthClientSecretSchema = yupString().meta({ openapiField: { description: 'OAuth client secret. Needs to be specified when using type="standard"', exampleValue: 'google-oauth-client-secret' } });
249
249
  export const oauthFacebookConfigIdSchema = yupString().meta({ openapiField: { description: 'The configuration id for Facebook business login (for things like ads and marketing). This is only required if you are using the standard OAuth with Facebook and you are using Facebook business login.' } });
250
250
  export const oauthMicrosoftTenantIdSchema = yupString().meta({ openapiField: { description: 'The Microsoft tenant id for Microsoft directory. This is only required if you are using the standard OAuth with Microsoft and you have an Azure AD tenant.' } });
251
+ export const oauthAccountMergeStrategySchema = yupString().oneOf(['link_method', 'raise_error', 'allow_duplicates']).meta({ openapiField: { description: 'Determines how to handle OAuth logins that match an existing user by email. `link_method` adds the OAuth method to the existing user. `raise_error` rejects the login with an error. `allow_duplicates` creates a new user.', exampleValue: 'link_method' } });
251
252
  // Project email config
252
253
  export const emailTypeSchema = yupString().oneOf(['shared', 'standard']).meta({ openapiField: { description: 'Email provider type, one of shared, standard. "shared" uses Stack shared email provider and it is only meant for development. "standard" uses your own email server and will have your email address as the sender.', exampleValue: 'standard' } });
253
254
  export const emailSenderNameSchema = yupString().meta({ openapiField: { description: 'Email sender name. Needs to be specified when using type="standard"', exampleValue: 'Stack' } });
@@ -0,0 +1,19 @@
1
+ import * as yup from "yup";
2
+ export declare const geoInfoSchema: yup.ObjectSchema<{
3
+ ip: string;
4
+ countryCode: string | null | undefined;
5
+ regionCode: string | null | undefined;
6
+ cityName: string | null | undefined;
7
+ latitude: number | null | undefined;
8
+ longitude: number | null | undefined;
9
+ tzIdentifier: string | null | undefined;
10
+ }, yup.AnyObject, {
11
+ ip: undefined;
12
+ countryCode: undefined;
13
+ regionCode: undefined;
14
+ cityName: undefined;
15
+ latitude: undefined;
16
+ longitude: undefined;
17
+ tzIdentifier: undefined;
18
+ }, "">;
19
+ export type GeoInfo = yup.InferType<typeof geoInfoSchema>;
@@ -0,0 +1,10 @@
1
+ import { yupNumber, yupObject, yupString } from "../schema-fields";
2
+ export const geoInfoSchema = yupObject({
3
+ ip: yupString().defined(),
4
+ countryCode: yupString().nullable(),
5
+ regionCode: yupString().nullable(),
6
+ cityName: yupString().nullable(),
7
+ latitude: yupNumber().nullable(),
8
+ longitude: yupNumber().nullable(),
9
+ tzIdentifier: yupString().nullable(),
10
+ });
@@ -2,6 +2,8 @@ import { findLastIndex, unique } from "./arrays";
2
2
  import { StackAssertionError } from "./errors";
3
3
  import { filterUndefined } from "./objects";
4
4
  export function typedToLowercase(s) {
5
+ if (typeof s !== "string")
6
+ throw new StackAssertionError("Expected a string for typedToLowercase", { s });
5
7
  return s.toLowerCase();
6
8
  }
7
9
  import.meta.vitest?.test("typedToLowercase", ({ expect }) => {
@@ -12,8 +14,11 @@ import.meta.vitest?.test("typedToLowercase", ({ expect }) => {
12
14
  expect(typedToLowercase("123")).toBe("123");
13
15
  expect(typedToLowercase("MIXED123case")).toBe("mixed123case");
14
16
  expect(typedToLowercase("Special@Chars!")).toBe("special@chars!");
17
+ expect(() => typedToLowercase(123)).toThrow("Expected a string for typedToLowercase");
15
18
  });
16
19
  export function typedToUppercase(s) {
20
+ if (typeof s !== "string")
21
+ throw new StackAssertionError("Expected a string for typedToUppercase", { s });
17
22
  return s.toUpperCase();
18
23
  }
19
24
  import.meta.vitest?.test("typedToUppercase", ({ expect }) => {
@@ -24,6 +29,7 @@ import.meta.vitest?.test("typedToUppercase", ({ expect }) => {
24
29
  expect(typedToUppercase("123")).toBe("123");
25
30
  expect(typedToUppercase("mixed123Case")).toBe("MIXED123CASE");
26
31
  expect(typedToUppercase("special@chars!")).toBe("SPECIAL@CHARS!");
32
+ expect(() => typedToUppercase(123)).toThrow("Expected a string for typedToUppercase");
27
33
  });
28
34
  export function typedCapitalize(s) {
29
35
  return s.charAt(0).toUpperCase() + s.slice(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackframe/stack-shared",
3
- "version": "2.7.25",
3
+ "version": "2.7.27",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",