@stackframe/stack-shared 2.5.8 → 2.5.10

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,19 @@
1
1
  # @stackframe/stack-shared
2
2
 
3
+ ## 2.5.10
4
+
5
+ ### Patch Changes
6
+
7
+ - Facebook Business support
8
+ - @stackframe/stack-sc@2.5.10
9
+
10
+ ## 2.5.9
11
+
12
+ ### Patch Changes
13
+
14
+ - Impersonation
15
+ - @stackframe/stack-sc@2.5.9
16
+
3
17
  ## 2.5.8
4
18
 
5
19
  ### Patch Changes
@@ -3,6 +3,7 @@ import { AccessToken, InternalSession, RefreshToken } from '../sessions';
3
3
  import { ReadonlyJson } from '../utils/json';
4
4
  import { Result } from "../utils/results";
5
5
  import { CurrentUserCrud } from './crud/current-user';
6
+ import { ProviderAccessTokenCrud } from './crud/oauth';
6
7
  import { InternalProjectsCrud, ProjectsCrud } from './crud/projects';
7
8
  import { TeamPermissionsCrud } from './crud/team-permissions';
8
9
  import { TeamsCrud } from './crud/teams';
@@ -15,17 +16,18 @@ export type ClientInterfaceOptions = {
15
16
  } | {
16
17
  projectOwnerSession: InternalSession;
17
18
  });
18
- export type SharedProvider = "shared-github" | "shared-google" | "shared-facebook" | "shared-microsoft" | "shared-spotify";
19
- export declare const sharedProviders: readonly ["shared-github", "shared-google", "shared-facebook", "shared-microsoft", "shared-spotify"];
20
- export type StandardProvider = "github" | "facebook" | "google" | "microsoft" | "spotify";
21
- export declare const standardProviders: readonly ["github", "facebook", "google", "microsoft", "spotify"];
22
- export declare function toStandardProvider(provider: SharedProvider | StandardProvider): StandardProvider;
23
- export declare function toSharedProvider(provider: SharedProvider | StandardProvider): SharedProvider;
24
19
  export declare class StackClientInterface {
25
20
  readonly options: ClientInterfaceOptions;
26
21
  constructor(options: ClientInterfaceOptions);
27
22
  get projectId(): string;
28
23
  getApiUrl(): string;
24
+ runNetworkDiagnostics(session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<{
25
+ cfTrace: string;
26
+ apiRoot: string;
27
+ baseUrlBackend: string;
28
+ prodDashboard: string;
29
+ prodBackend: string;
30
+ }>;
29
31
  fetchNewAccessToken(refreshToken: RefreshToken): Promise<AccessToken | null>;
30
32
  protected sendClientRequest(path: string, requestOptions: RequestInit, session: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<Response & {
31
33
  usedTokens: {
@@ -46,7 +48,7 @@ export declare class StackClientInterface {
46
48
  featureName?: string;
47
49
  } & ReadonlyJson): Promise<never>;
48
50
  sendForgotPasswordEmail(email: string, callbackUrl: string): Promise<KnownErrors["UserNotFound"] | undefined>;
49
- sendVerificationEmail(emailVerificationRedirectUrl: string, session: InternalSession): Promise<KnownErrors["EmailAlreadyVerified"] | undefined>;
51
+ sendVerificationEmail(email: string, callbackUrl: string, session: InternalSession): Promise<KnownErrors["EmailAlreadyVerified"] | undefined>;
50
52
  sendMagicLinkEmail(email: string, callbackUrl: string): Promise<KnownErrors["RedirectUrlNotWhitelisted"] | undefined>;
51
53
  resetPassword(options: {
52
54
  code: string;
@@ -111,8 +113,6 @@ export declare class StackClientInterface {
111
113
  updateClientUser(update: CurrentUserCrud["Client"]["Update"], session: InternalSession): Promise<void>;
112
114
  listProjects(session: InternalSession): Promise<InternalProjectsCrud['Client']['Read'][]>;
113
115
  createProject(project: InternalProjectsCrud['Client']['Create'], session: InternalSession): Promise<InternalProjectsCrud['Client']['Read']>;
114
- getAccessToken(provider: string, scope: string, session: InternalSession): Promise<{
115
- accessToken: string;
116
- }>;
116
+ createProviderAccessToken(provider: string, scope: string, session: InternalSession): Promise<ProviderAccessTokenCrud['Client']['Read']>;
117
117
  createTeamForCurrentUser(data: TeamsCrud['Client']['Create'], session: InternalSession): Promise<TeamsCrud['Client']['Read']>;
118
118
  }
@@ -6,26 +6,7 @@ import { generateSecureRandomString } from '../utils/crypto';
6
6
  import { StackAssertionError, throwErr } from '../utils/errors';
7
7
  import { globalVar } from '../utils/globals';
8
8
  import { Result } from "../utils/results";
9
- export const sharedProviders = [
10
- "shared-github",
11
- "shared-google",
12
- "shared-facebook",
13
- "shared-microsoft",
14
- "shared-spotify",
15
- ];
16
- export const standardProviders = [
17
- "github",
18
- "facebook",
19
- "google",
20
- "microsoft",
21
- "spotify",
22
- ];
23
- export function toStandardProvider(provider) {
24
- return provider.replace("shared-", "");
25
- }
26
- export function toSharedProvider(provider) {
27
- return "shared-" + provider;
28
- }
9
+ import { deindent } from '../utils/strings';
29
10
  export class StackClientInterface {
30
11
  constructor(options) {
31
12
  this.options = options;
@@ -37,6 +18,54 @@ export class StackClientInterface {
37
18
  getApiUrl() {
38
19
  return this.options.baseUrl + "/api/v1";
39
20
  }
21
+ async runNetworkDiagnostics(session, requestType) {
22
+ const tryRequest = async (cb) => {
23
+ try {
24
+ await cb();
25
+ return "OK";
26
+ }
27
+ catch (e) {
28
+ return `${e}`;
29
+ }
30
+ };
31
+ const cfTrace = await tryRequest(async () => {
32
+ const res = await fetch("https://1.1.1.1/cdn-cgi/trace");
33
+ if (!res.ok) {
34
+ throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`);
35
+ }
36
+ });
37
+ const apiRoot = session !== undefined && requestType !== undefined ? await tryRequest(async () => {
38
+ const res = await this.sendClientRequestInner("/", {}, session, requestType);
39
+ if (res.status === "error") {
40
+ throw res.error;
41
+ }
42
+ }) : "Not tested";
43
+ const baseUrlBackend = await tryRequest(async () => {
44
+ const res = await fetch(new URL("/health", this.getApiUrl()));
45
+ if (!res.ok) {
46
+ throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`);
47
+ }
48
+ });
49
+ const prodDashboard = await tryRequest(async () => {
50
+ const res = await fetch("https://app.stackframe.com/health");
51
+ if (!res.ok) {
52
+ throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`);
53
+ }
54
+ });
55
+ const prodBackend = await tryRequest(async () => {
56
+ const res = await fetch("https://api.stackframe.com/health");
57
+ if (!res.ok) {
58
+ throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`);
59
+ }
60
+ });
61
+ return {
62
+ cfTrace,
63
+ apiRoot,
64
+ baseUrlBackend,
65
+ prodDashboard,
66
+ prodBackend,
67
+ };
68
+ }
40
69
  async fetchNewAccessToken(refreshToken) {
41
70
  if (!('publishableClientKey' in this.options)) {
42
71
  // TODO support it
@@ -79,7 +108,23 @@ export class StackClientInterface {
79
108
  session ??= this.createSession({
80
109
  refreshToken: null,
81
110
  });
82
- return await Result.orThrowAsync(Result.retry(() => this.sendClientRequestInner(path, requestOptions, session, requestType), 5, { exponentialDelayBase: 1000 }));
111
+ const retriedResult = await Result.retry(() => this.sendClientRequestInner(path, requestOptions, session, requestType), 5, { exponentialDelayBase: 1000 });
112
+ // try to diagnose the error for the user
113
+ if (retriedResult.status === "error") {
114
+ if (!navigator.onLine) {
115
+ throw new Error("Failed to send Stack request. It seems like you are offline. (window.navigator.onLine is falsy)", { cause: retriedResult.error });
116
+ }
117
+ throw new Error(deindent `
118
+ Stack is unable to connect to the server. Please check your internet connection and try again.
119
+
120
+ If the problem persists, please contact Stack support and provide a screenshot of your entire browser console.
121
+
122
+ ${retriedResult.error}
123
+
124
+ ${JSON.stringify(await this.runNetworkDiagnostics(session, requestType), null, 2)}
125
+ `, { cause: retriedResult.error });
126
+ }
127
+ return retriedResult.data;
83
128
  }
84
129
  createSession(options) {
85
130
  const session = new InternalSession({
@@ -254,14 +299,15 @@ export class StackClientInterface {
254
299
  return res.error;
255
300
  }
256
301
  }
257
- async sendVerificationEmail(emailVerificationRedirectUrl, session) {
302
+ async sendVerificationEmail(email, callbackUrl, session) {
258
303
  const res = await this.sendClientRequestAndCatchKnownError("/contact-channels/send-verification-code", {
259
304
  method: "POST",
260
305
  headers: {
261
306
  "Content-Type": "application/json"
262
307
  },
263
308
  body: JSON.stringify({
264
- emailVerificationRedirectUrl,
309
+ email,
310
+ callback_url: callbackUrl,
265
311
  }),
266
312
  }, session, [KnownErrors.EmailAlreadyVerified]);
267
313
  if (res.status === "error") {
@@ -417,7 +463,7 @@ export class StackClientInterface {
417
463
  url.searchParams.set("type", options.type);
418
464
  url.searchParams.set("error_redirect_url", options.errorRedirectUrl);
419
465
  if (options.afterCallbackRedirectUrl) {
420
- url.searchParams.set("after_callback_redirect_rrl", options.afterCallbackRedirectUrl);
466
+ url.searchParams.set("after_callback_redirect_url", options.afterCallbackRedirectUrl);
421
467
  }
422
468
  if (options.type === "link") {
423
469
  const tokens = await options.session.getPotentiallyExpiredTokens();
@@ -551,18 +597,15 @@ export class StackClientInterface {
551
597
  const json = await fetchResponse.json();
552
598
  return json;
553
599
  }
554
- async getAccessToken(provider, scope, session) {
555
- const response = await this.sendClientRequest(`/auth/oauth/connected-account/${provider}/access-token`, {
600
+ async createProviderAccessToken(provider, scope, session) {
601
+ const response = await this.sendClientRequest(`/auth/oauth/connected-accounts/${provider}/access-token`, {
556
602
  method: "POST",
557
603
  headers: {
558
604
  "content-type": "application/json",
559
605
  },
560
606
  body: JSON.stringify({ scope }),
561
607
  }, session);
562
- const json = await response.json();
563
- return {
564
- accessToken: json.accessToken,
565
- };
608
+ return await response.json();
566
609
  }
567
610
  async createTeamForCurrentUser(data, session) {
568
611
  const response = await this.sendClientRequest("/teams?add_current_user=true", {
@@ -1,15 +1,15 @@
1
1
  import { CrudTypeOf } from "../../crud";
2
- export declare const accessTokenReadSchema: import("yup").ObjectSchema<{
2
+ export declare const providerAccessTokenReadSchema: import("yup").ObjectSchema<{
3
3
  access_token: string;
4
4
  }, import("yup").AnyObject, {
5
5
  access_token: undefined;
6
6
  }, "">;
7
- export declare const accessTokenCreateSchema: import("yup").ObjectSchema<{
7
+ export declare const providerAccessTokenCreateSchema: import("yup").ObjectSchema<{
8
8
  scope: string | undefined;
9
9
  }, import("yup").AnyObject, {
10
10
  scope: undefined;
11
11
  }, "">;
12
- export declare const accessTokenCrud: import("../../crud").CrudSchemaFromOptions<{
12
+ export declare const providerAccessTokenCrud: import("../../crud").CrudSchemaFromOptions<{
13
13
  clientReadSchema: import("yup").ObjectSchema<{
14
14
  access_token: string;
15
15
  }, import("yup").AnyObject, {
@@ -21,4 +21,4 @@ export declare const accessTokenCrud: import("../../crud").CrudSchemaFromOptions
21
21
  scope: undefined;
22
22
  }, "">;
23
23
  }>;
24
- export type AccessTokenCrud = CrudTypeOf<typeof accessTokenCrud>;
24
+ export type ProviderAccessTokenCrud = CrudTypeOf<typeof providerAccessTokenCrud>;
@@ -1,12 +1,12 @@
1
1
  import { createCrud } from "../../crud";
2
2
  import { yupObject, yupString } from "../../schema-fields";
3
- export const accessTokenReadSchema = yupObject({
3
+ export const providerAccessTokenReadSchema = yupObject({
4
4
  access_token: yupString().required(),
5
5
  }).required();
6
- export const accessTokenCreateSchema = yupObject({
6
+ export const providerAccessTokenCreateSchema = yupObject({
7
7
  scope: yupString().optional(),
8
8
  }).required();
9
- export const accessTokenCrud = createCrud({
10
- clientReadSchema: accessTokenReadSchema,
11
- clientCreateSchema: accessTokenCreateSchema,
9
+ export const providerAccessTokenCrud = createCrud({
10
+ clientReadSchema: providerAccessTokenReadSchema,
11
+ clientCreateSchema: providerAccessTokenCreateSchema,
12
12
  });
@@ -14,6 +14,7 @@ export declare const projectsCrudServerReadSchema: import("yup").ObjectSchema<{
14
14
  oauth_providers: {
15
15
  client_id?: string | undefined;
16
16
  client_secret?: string | undefined;
17
+ facebook_config_id?: string | undefined;
17
18
  type: NonNullable<"shared" | "standard" | undefined>;
18
19
  id: NonNullable<"google" | "github" | "facebook" | "microsoft" | "spotify" | undefined>;
19
20
  enabled: NonNullable<boolean | undefined>;
@@ -101,6 +102,7 @@ export declare const projectsCrudServerUpdateSchema: import("yup").ObjectSchema<
101
102
  oauth_providers?: {
102
103
  client_id?: string | undefined;
103
104
  client_secret?: string | undefined;
105
+ facebook_config_id?: string | undefined;
104
106
  type: NonNullable<"shared" | "standard" | undefined>;
105
107
  id: NonNullable<"google" | "github" | "facebook" | "microsoft" | "spotify" | undefined>;
106
108
  enabled: NonNullable<boolean | undefined>;
@@ -143,6 +145,7 @@ export declare const projectsCrudServerCreateSchema: import("yup").ObjectSchema<
143
145
  oauth_providers?: {
144
146
  client_id?: string | undefined;
145
147
  client_secret?: string | undefined;
148
+ facebook_config_id?: string | undefined;
146
149
  type: NonNullable<"shared" | "standard" | undefined>;
147
150
  id: NonNullable<"google" | "github" | "facebook" | "microsoft" | "spotify" | undefined>;
148
151
  enabled: NonNullable<boolean | undefined>;
@@ -211,6 +214,7 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
211
214
  oauth_providers: {
212
215
  client_id?: string | undefined;
213
216
  client_secret?: string | undefined;
217
+ facebook_config_id?: string | undefined;
214
218
  type: NonNullable<"shared" | "standard" | undefined>;
215
219
  id: NonNullable<"google" | "github" | "facebook" | "microsoft" | "spotify" | undefined>;
216
220
  enabled: NonNullable<boolean | undefined>;
@@ -279,6 +283,7 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
279
283
  oauth_providers?: {
280
284
  client_id?: string | undefined;
281
285
  client_secret?: string | undefined;
286
+ facebook_config_id?: string | undefined;
282
287
  type: NonNullable<"shared" | "standard" | undefined>;
283
288
  id: NonNullable<"google" | "github" | "facebook" | "microsoft" | "spotify" | undefined>;
284
289
  enabled: NonNullable<boolean | undefined>;
@@ -345,6 +350,7 @@ export declare const internalProjectsCrud: import("../../crud").CrudSchemaFromOp
345
350
  oauth_providers: {
346
351
  client_id?: string | undefined;
347
352
  client_secret?: string | undefined;
353
+ facebook_config_id?: string | undefined;
348
354
  type: NonNullable<"shared" | "standard" | undefined>;
349
355
  id: NonNullable<"google" | "github" | "facebook" | "microsoft" | "spotify" | undefined>;
350
356
  enabled: NonNullable<boolean | undefined>;
@@ -413,6 +419,7 @@ export declare const internalProjectsCrud: import("../../crud").CrudSchemaFromOp
413
419
  oauth_providers?: {
414
420
  client_id?: string | undefined;
415
421
  client_secret?: string | undefined;
422
+ facebook_config_id?: string | undefined;
416
423
  type: NonNullable<"shared" | "standard" | undefined>;
417
424
  id: NonNullable<"google" | "github" | "facebook" | "microsoft" | "spotify" | undefined>;
418
425
  enabled: NonNullable<boolean | undefined>;
@@ -10,6 +10,8 @@ const oauthProviderSchema = yupObject({
10
10
  type: schemaFields.oauthTypeSchema.required(),
11
11
  client_id: yupRequiredWhen(schemaFields.oauthClientIdSchema, 'type', 'standard'),
12
12
  client_secret: yupRequiredWhen(schemaFields.oauthClientSecretSchema, 'type', 'standard'),
13
+ // extra params
14
+ facebook_config_id: yupString().optional().meta({ openapiField: { description: 'This parameter is the configuration id for Facebook business login (for things like ads and marketing).' } }),
13
15
  });
14
16
  const enabledOAuthProviderSchema = yupObject({
15
17
  id: schemaFields.oauthIdSchema.required(),
@@ -0,0 +1,72 @@
1
+ import { CrudTypeOf } from "../../crud";
2
+ export declare const teamMemberProfilesCrudClientReadSchema: import("yup").ObjectSchema<{
3
+ team_id: string;
4
+ user_id: string;
5
+ display_name: string | null;
6
+ profile_image_url: string | null;
7
+ }, import("yup").AnyObject, {
8
+ team_id: undefined;
9
+ user_id: undefined;
10
+ display_name: undefined;
11
+ profile_image_url: undefined;
12
+ }, "">;
13
+ export declare const teamMemberProfilesCrudClientUpdateSchema: import("yup").ObjectSchema<{
14
+ display_name: string | undefined;
15
+ profile_image_url: string | null | undefined;
16
+ }, import("yup").AnyObject, {
17
+ display_name: undefined;
18
+ profile_image_url: undefined;
19
+ }, "">;
20
+ export declare const teamMemberProfilesCrud: import("../../crud").CrudSchemaFromOptions<{
21
+ clientReadSchema: import("yup").ObjectSchema<{
22
+ team_id: string;
23
+ user_id: string;
24
+ display_name: string | null;
25
+ profile_image_url: string | null;
26
+ }, import("yup").AnyObject, {
27
+ team_id: undefined;
28
+ user_id: undefined;
29
+ display_name: undefined;
30
+ profile_image_url: undefined;
31
+ }, "">;
32
+ clientUpdateSchema: import("yup").ObjectSchema<{
33
+ display_name: string | undefined;
34
+ profile_image_url: string | null | undefined;
35
+ }, import("yup").AnyObject, {
36
+ display_name: undefined;
37
+ profile_image_url: undefined;
38
+ }, "">;
39
+ docs: {
40
+ clientList: {
41
+ summary: string;
42
+ description: string;
43
+ tags: string[];
44
+ };
45
+ serverList: {
46
+ summary: string;
47
+ description: string;
48
+ tags: string[];
49
+ };
50
+ clientRead: {
51
+ summary: string;
52
+ description: string;
53
+ tags: string[];
54
+ };
55
+ serverRead: {
56
+ summary: string;
57
+ description: string;
58
+ tags: string[];
59
+ };
60
+ clientUpdate: {
61
+ summary: string;
62
+ description: string;
63
+ tags: string[];
64
+ };
65
+ serverUpdate: {
66
+ summary: string;
67
+ description: string;
68
+ tags: string[];
69
+ };
70
+ };
71
+ }>;
72
+ export type TeamMemberProfilesCrud = CrudTypeOf<typeof teamMemberProfilesCrud>;
@@ -0,0 +1,49 @@
1
+ import { createCrud } from "../../crud";
2
+ import * as schemaFields from "../../schema-fields";
3
+ import { yupObject } from "../../schema-fields";
4
+ export const teamMemberProfilesCrudClientReadSchema = yupObject({
5
+ team_id: schemaFields.teamIdSchema.required(),
6
+ user_id: schemaFields.userIdSchema.required(),
7
+ display_name: schemaFields.teamMemberDisplayNameSchema.nullable().defined(),
8
+ profile_image_url: schemaFields.teamMemberProfileImageUrlSchema.nullable().defined(),
9
+ }).required();
10
+ export const teamMemberProfilesCrudClientUpdateSchema = yupObject({
11
+ display_name: schemaFields.teamMemberDisplayNameSchema.optional(),
12
+ profile_image_url: schemaFields.teamMemberProfileImageUrlSchema.nullable().optional(),
13
+ }).required();
14
+ export const teamMemberProfilesCrud = createCrud({
15
+ clientReadSchema: teamMemberProfilesCrudClientReadSchema,
16
+ clientUpdateSchema: teamMemberProfilesCrudClientUpdateSchema,
17
+ docs: {
18
+ clientList: {
19
+ summary: "List team members profiles",
20
+ description: "List team members profiles. You always need to specify a `team_id` that your are a member of on the client. You can always filter for your own profile by setting `me` as the `user_id` in the path parameters. If you want list all the profiles in a team, you need to have the `$read_members` permission in that team.",
21
+ tags: ["Teams"],
22
+ },
23
+ serverList: {
24
+ summary: "List team members profiles",
25
+ description: "List team members profiles and filter by team ID and user ID",
26
+ tags: ["Teams"],
27
+ },
28
+ clientRead: {
29
+ summary: "Get a team member profile",
30
+ description: "Get a team member profile. you can always get your own profile by setting `me` as the `user_id` in the path parameters on the client. If you want to get someone else's profile in a team, you need to have the `$read_members` permission in that team.",
31
+ tags: ["Teams"],
32
+ },
33
+ serverRead: {
34
+ summary: "Get a team member profile",
35
+ description: "Get a team member profile by user ID",
36
+ tags: ["Teams"],
37
+ },
38
+ clientUpdate: {
39
+ summary: "Update your team member profile",
40
+ description: "Update your own team member profile. `user_id` must be `me` in the path parameters on the client.",
41
+ tags: ["Teams"],
42
+ },
43
+ serverUpdate: {
44
+ summary: "Update a team member profile",
45
+ description: "Update a team member profile by user ID",
46
+ tags: ["Teams"],
47
+ },
48
+ },
49
+ });
@@ -1,11 +1,11 @@
1
1
  import { CrudTypeOf } from "../../crud";
2
- export declare const teamMembershipsCrudServerReadSchema: import("yup").ObjectSchema<{}, import("yup").AnyObject, {}, "">;
2
+ export declare const teamMembershipsCrudClientReadSchema: import("yup").ObjectSchema<{}, import("yup").AnyObject, {}, "">;
3
3
  export declare const teamMembershipsCrudServerCreateSchema: import("yup").ObjectSchema<{}, import("yup").AnyObject, {}, "">;
4
- export declare const teamMembershipsCrudServerDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
4
+ export declare const teamMembershipsCrudClientDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
5
5
  export declare const teamMembershipsCrud: import("../../crud").CrudSchemaFromOptions<{
6
- serverReadSchema: import("yup").ObjectSchema<{}, import("yup").AnyObject, {}, "">;
6
+ clientReadSchema: import("yup").ObjectSchema<{}, import("yup").AnyObject, {}, "">;
7
+ clientDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
7
8
  serverCreateSchema: import("yup").ObjectSchema<{}, import("yup").AnyObject, {}, "">;
8
- serverDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
9
9
  docs: {
10
10
  serverCreate: {
11
11
  summary: string;
@@ -1,12 +1,14 @@
1
1
  import { createCrud } from "../../crud";
2
2
  import { yupMixed, yupObject } from "../../schema-fields";
3
- export const teamMembershipsCrudServerReadSchema = yupObject({}).required();
3
+ export const teamMembershipsCrudClientReadSchema = yupObject({}).required();
4
4
  export const teamMembershipsCrudServerCreateSchema = yupObject({}).required();
5
- export const teamMembershipsCrudServerDeleteSchema = yupMixed();
5
+ export const teamMembershipsCrudClientDeleteSchema = yupMixed();
6
6
  export const teamMembershipsCrud = createCrud({
7
- serverReadSchema: teamMembershipsCrudServerReadSchema,
7
+ // Client
8
+ clientReadSchema: teamMembershipsCrudClientReadSchema,
9
+ clientDeleteSchema: teamMembershipsCrudClientDeleteSchema,
10
+ // Server
8
11
  serverCreateSchema: teamMembershipsCrudServerCreateSchema,
9
- serverDeleteSchema: teamMembershipsCrudServerDeleteSchema,
10
12
  docs: {
11
13
  serverCreate: {
12
14
  summary: "Add a user to a team",
@@ -64,6 +64,13 @@ export declare const teamsCrud: import("../../crud").CrudSchemaFromOptions<{
64
64
  display_name: undefined;
65
65
  profile_image_url: undefined;
66
66
  }, "">;
67
+ clientUpdateSchema: import("yup").ObjectSchema<{
68
+ display_name: string | undefined;
69
+ profile_image_url: string | null | undefined;
70
+ }, import("yup").AnyObject, {
71
+ display_name: undefined;
72
+ profile_image_url: undefined;
73
+ }, "">;
67
74
  clientCreateSchema: import("yup").ObjectSchema<{
68
75
  display_name: string;
69
76
  profile_image_url: string | null | undefined;
@@ -73,6 +80,7 @@ export declare const teamsCrud: import("../../crud").CrudSchemaFromOptions<{
73
80
  display_name: undefined;
74
81
  profile_image_url: undefined;
75
82
  }, "">;
83
+ clientDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
76
84
  serverReadSchema: import("yup").ObjectSchema<{
77
85
  id: string;
78
86
  display_name: string;
@@ -5,7 +5,7 @@ import { yupMixed, yupObject } from "../../schema-fields";
5
5
  export const teamsCrudClientReadSchema = yupObject({
6
6
  id: fieldSchema.teamIdSchema.required(),
7
7
  display_name: fieldSchema.teamDisplayNameSchema.required(),
8
- profile_image_url: fieldSchema.profileImageUrlSchema.nullable().defined(),
8
+ profile_image_url: fieldSchema.teamProfileImageUrlSchema.nullable().defined(),
9
9
  }).required();
10
10
  export const teamsCrudServerReadSchema = teamsCrudClientReadSchema.concat(yupObject({
11
11
  created_at_millis: fieldSchema.teamCreatedAtMillisSchema.required(),
@@ -13,7 +13,7 @@ export const teamsCrudServerReadSchema = teamsCrudClientReadSchema.concat(yupObj
13
13
  // Update
14
14
  export const teamsCrudClientUpdateSchema = yupObject({
15
15
  display_name: fieldSchema.teamDisplayNameSchema.optional(),
16
- profile_image_url: fieldSchema.profileImageUrlSchema.nullable().optional(),
16
+ profile_image_url: fieldSchema.teamProfileImageUrlSchema.nullable().optional(),
17
17
  }).required();
18
18
  export const teamsCrudServerUpdateSchema = teamsCrudClientUpdateSchema.concat(yupObject({}).required());
19
19
  // Create
@@ -27,10 +27,12 @@ export const teamsCrudServerCreateSchema = teamsCrudServerUpdateSchema.concat(yu
27
27
  export const teamsCrudClientDeleteSchema = yupMixed();
28
28
  export const teamsCrudServerDeleteSchema = teamsCrudClientDeleteSchema;
29
29
  export const teamsCrud = createCrud({
30
+ // Client
30
31
  clientReadSchema: teamsCrudClientReadSchema,
31
- // clientUpdateSchema: teamsCrudClientUpdateSchema,
32
+ clientUpdateSchema: teamsCrudClientUpdateSchema,
32
33
  clientCreateSchema: teamsCrudClientCreateSchema,
33
- // clientDeleteSchema: teamsCrudClientDeleteSchema,
34
+ clientDeleteSchema: teamsCrudClientDeleteSchema,
35
+ // Server
34
36
  serverReadSchema: teamsCrudServerReadSchema,
35
37
  serverUpdateSchema: teamsCrudServerUpdateSchema,
36
38
  serverCreateSchema: teamsCrudServerCreateSchema,
@@ -5,8 +5,8 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
5
5
  display_name: string | null;
6
6
  oauth_providers: {
7
7
  email?: string | null | undefined;
8
- account_id: string;
9
8
  provider_id: string;
9
+ account_id: string;
10
10
  }[];
11
11
  project_id: string;
12
12
  primary_email: string | null;
@@ -48,8 +48,8 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
48
48
  auth_with_email: NonNullable<boolean | undefined>;
49
49
  oauth_providers: {
50
50
  email?: string | null | undefined;
51
- account_id: string;
52
51
  provider_id: string;
52
+ account_id: string;
53
53
  }[];
54
54
  client_metadata: {} | null;
55
55
  server_metadata: {} | null;
@@ -34,8 +34,8 @@ export declare const usersCrudServerReadSchema: import("yup").ObjectSchema<{
34
34
  auth_with_email: NonNullable<boolean | undefined>;
35
35
  oauth_providers: {
36
36
  email?: string | null | undefined;
37
- account_id: string;
38
37
  provider_id: string;
38
+ account_id: string;
39
39
  }[];
40
40
  client_metadata: {} | null;
41
41
  server_metadata: {} | null;
@@ -68,8 +68,8 @@ export declare const usersCrudServerCreateSchema: import("yup").ObjectSchema<{
68
68
  } & {
69
69
  oauth_providers: {
70
70
  email: string | null;
71
- account_id: string;
72
71
  provider_id: string;
72
+ account_id: string;
73
73
  }[] | undefined;
74
74
  }, import("yup").AnyObject, {
75
75
  display_name: undefined;
@@ -99,8 +99,8 @@ export declare const usersCrud: import("../../crud").CrudSchemaFromOptions<{
99
99
  auth_with_email: NonNullable<boolean | undefined>;
100
100
  oauth_providers: {
101
101
  email?: string | null | undefined;
102
- account_id: string;
103
102
  provider_id: string;
103
+ account_id: string;
104
104
  }[];
105
105
  client_metadata: {} | null;
106
106
  server_metadata: {} | null;
@@ -154,8 +154,8 @@ export declare const usersCrud: import("../../crud").CrudSchemaFromOptions<{
154
154
  } & {
155
155
  oauth_providers: {
156
156
  email: string | null;
157
- account_id: string;
158
157
  provider_id: string;
158
+ account_id: string;
159
159
  }[] | undefined;
160
160
  }, import("yup").AnyObject, {
161
161
  display_name: undefined;
@@ -49,6 +49,10 @@ export declare class StackServerInterface extends StackClientInterface {
49
49
  teamId: string;
50
50
  }): Promise<void>;
51
51
  updateServerUser(userId: string, update: UsersCrud['Server']['Update']): Promise<UsersCrud['Server']['Read']>;
52
+ createServerUserSession(userId: string, expiresInMillis: number): Promise<{
53
+ accessToken: string;
54
+ refreshToken: string;
55
+ }>;
52
56
  listServerTeamMemberPermissions(options: {
53
57
  teamId: string;
54
58
  userId: string;
@@ -130,6 +130,23 @@ export class StackServerInterface extends StackClientInterface {
130
130
  }, null);
131
131
  return await response.json();
132
132
  }
133
+ async createServerUserSession(userId, expiresInMillis) {
134
+ const response = await this.sendServerRequest("/auth/sessions", {
135
+ method: "POST",
136
+ headers: {
137
+ "content-type": "application/json",
138
+ },
139
+ body: JSON.stringify({
140
+ user_id: userId,
141
+ expires_in_millis: expiresInMillis,
142
+ }),
143
+ }, null);
144
+ const result = await response.json();
145
+ return {
146
+ accessToken: result.access_token,
147
+ refreshToken: result.refresh_token,
148
+ };
149
+ }
133
150
  async listServerTeamMemberPermissions(options) {
134
151
  const response = await this.sendServerRequest(`/team-permissions?team_id=${options.teamId}&user_id=${options.userId}&recursive=${options.recursive}`, {}, null);
135
152
  const result = await response.json();
@@ -233,6 +233,9 @@ export declare const KnownErrors: {
233
233
  UserEmailAlreadyExists: KnownErrorConstructor<KnownError & KnownErrorBrand<"USER_EMAIL_ALREADY_EXISTS">, []> & {
234
234
  errorCode: "USER_EMAIL_ALREADY_EXISTS";
235
235
  };
236
+ UserIdDoesNotExist: KnownErrorConstructor<KnownError & KnownErrorBrand<"USER_ID_DOES_NOT_EXIST">, [userId: string]> & {
237
+ errorCode: "USER_ID_DOES_NOT_EXIST";
238
+ };
236
239
  UserNotFound: KnownErrorConstructor<KnownError & KnownErrorBrand<"USER_NOT_FOUND">, []> & {
237
240
  errorCode: "USER_NOT_FOUND";
238
241
  };
@@ -348,5 +351,14 @@ export declare const KnownErrors: {
348
351
  TeamMembershipAlreadyExists: KnownErrorConstructor<KnownError & KnownErrorBrand<"TEAM_MEMBERSHIP_ALREADY_EXISTS">, []> & {
349
352
  errorCode: "TEAM_MEMBERSHIP_ALREADY_EXISTS";
350
353
  };
354
+ TeamPermissionRequired: KnownErrorConstructor<KnownError & KnownErrorBrand<"TEAM_PERMISSION_REQUIRED">, [any, any, any]> & {
355
+ errorCode: "TEAM_PERMISSION_REQUIRED";
356
+ };
357
+ InvalidSharedOAuthProviderId: KnownErrorConstructor<KnownError & KnownErrorBrand<"INVALID_SHARED_OAUTH_PROVIDER_ID">, [any]> & {
358
+ errorCode: "INVALID_SHARED_OAUTH_PROVIDER_ID";
359
+ };
360
+ InvalidStandardOAuthProviderId: KnownErrorConstructor<KnownError & KnownErrorBrand<"INVALID_STANDARD_OAUTH_PROVIDER_ID">, [any]> & {
361
+ errorCode: "INVALID_STANDARD_OAUTH_PROVIDER_ID";
362
+ };
351
363
  };
352
364
  export {};
@@ -146,14 +146,22 @@ const InvalidAccessType = createKnownErrorConstructor(InvalidProjectAuthenticati
146
146
  ]);
147
147
  const AccessTypeWithoutProjectId = createKnownErrorConstructor(InvalidProjectAuthentication, "ACCESS_TYPE_WITHOUT_PROJECT_ID", (accessType) => [
148
148
  400,
149
- `The x-stack-access-type header was '${accessType}', but the x-stack-project-id header was not provided.`,
149
+ deindent `
150
+ The x-stack-access-type header was '${accessType}', but the x-stack-project-id header was not provided.
151
+
152
+ For more information, see the docs on REST API authentication: https://docs.stack-auth.com/rest-api/auth#authentication
153
+ `,
150
154
  {
151
155
  request_type: accessType,
152
156
  },
153
157
  ], (json) => [json.request_type]);
154
158
  const AccessTypeRequired = createKnownErrorConstructor(InvalidProjectAuthentication, "ACCESS_TYPE_REQUIRED", () => [
155
159
  400,
156
- `You must specify an access level for this Stack project. Make sure project API keys are provided (eg. x-stack-publishable-client-key) and you set the x-stack-access-type header to 'client', 'server', or 'admin'.`,
160
+ deindent `
161
+ You must specify an access level for this Stack project. Make sure project API keys are provided (eg. x-stack-publishable-client-key) and you set the x-stack-access-type header to 'client', 'server', or 'admin'.
162
+
163
+ For more information, see the docs on REST API authentication: https://docs.stack-auth.com/rest-api/auth#authentication
164
+ `,
157
165
  ], () => []);
158
166
  const InsufficientAccessType = createKnownErrorConstructor(InvalidProjectAuthentication, "INSUFFICIENT_ACCESS_TYPE", (actualAccessType, allowedAccessTypes) => [
159
167
  401,
@@ -286,6 +294,13 @@ const CannotGetOwnUserWithoutUser = createKnownErrorConstructor(KnownError, "CAN
286
294
  400,
287
295
  "You have specified 'me' as a userId, but did not provide authentication for a user.",
288
296
  ], () => []);
297
+ const UserIdDoesNotExist = createKnownErrorConstructor(KnownError, "USER_ID_DOES_NOT_EXIST", (userId) => [
298
+ 400,
299
+ `The given user with the ID ${userId} does not exist.`,
300
+ {
301
+ user_id: userId,
302
+ },
303
+ ], (json) => [json.user_id]);
289
304
  const UserNotFound = createKnownErrorConstructor(KnownError, "USER_NOT_FOUND", () => [
290
305
  404,
291
306
  "User not found.",
@@ -464,6 +479,29 @@ const TeamMembershipAlreadyExists = createKnownErrorConstructor(KnownError, "TEA
464
479
  400,
465
480
  "Team membership already exists.",
466
481
  ], () => []);
482
+ const TeamPermissionRequired = createKnownErrorConstructor(KnownError, "TEAM_PERMISSION_REQUIRED", (teamId, userId, permissionId) => [
483
+ 401,
484
+ `User ${userId} does not have permission ${permissionId} in team ${teamId}.`,
485
+ {
486
+ team_id: teamId,
487
+ user_id: userId,
488
+ permission_id: permissionId,
489
+ },
490
+ ], (json) => [json.team_id, json.user_id, json.permission_id]);
491
+ const InvalidSharedOAuthProviderId = createKnownErrorConstructor(KnownError, "INVALID_SHARED_OAUTH_PROVIDER_ID", (providerId) => [
492
+ 400,
493
+ `The shared OAuth provider with ID ${providerId} is not valid.`,
494
+ {
495
+ provider_id: providerId,
496
+ },
497
+ ], (json) => [json.provider_id]);
498
+ const InvalidStandardOAuthProviderId = createKnownErrorConstructor(KnownError, "INVALID_STANDARD_OAUTH_PROVIDER_ID", (providerId) => [
499
+ 400,
500
+ `The standard OAuth provider with ID ${providerId} is not valid.`,
501
+ {
502
+ provider_id: providerId,
503
+ },
504
+ ], (json) => [json.provider_id]);
467
505
  export const KnownErrors = {
468
506
  UnsupportedError,
469
507
  BodyParsingError,
@@ -506,6 +544,7 @@ export const KnownErrors = {
506
544
  ProviderRejected,
507
545
  RefreshTokenNotFoundOrExpired,
508
546
  UserEmailAlreadyExists,
547
+ UserIdDoesNotExist,
509
548
  UserNotFound,
510
549
  ApiKeyNotFound,
511
550
  ProjectNotFound,
@@ -541,6 +580,9 @@ export const KnownErrors = {
541
580
  OAuthProviderNotFoundOrNotEnabled,
542
581
  UserAuthenticationRequired,
543
582
  TeamMembershipAlreadyExists,
583
+ TeamPermissionRequired,
584
+ InvalidSharedOAuthProviderId,
585
+ InvalidStandardOAuthProviderId,
544
586
  };
545
587
  // ensure that all known error codes are unique
546
588
  const knownErrorCodes = new Set();
@@ -19,6 +19,7 @@ export declare const adaptSchema: yup.MixedSchema<typeof StackAdaptSentinel | un
19
19
  export declare const urlSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
20
20
  export declare const jsonSchema: yup.MixedSchema<{} | null, yup.AnyObject, undefined, "">;
21
21
  export declare const jsonStringSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
22
+ export declare const jsonStringOrEmptySchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
22
23
  export declare const emailSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
23
24
  export declare const clientOrHigherAuthTypeSchema: yup.StringSchema<"client" | "server" | "admin" | undefined, yup.AnyObject, undefined, "">;
24
25
  export declare const serverOrHigherAuthTypeSchema: yup.StringSchema<"server" | "admin" | undefined, yup.AnyObject, undefined, "">;
@@ -85,8 +86,11 @@ export declare const teamPermissionDescriptionSchema: yup.StringSchema<string |
85
86
  export declare const containedPermissionIdsSchema: yup.ArraySchema<string[] | undefined, yup.AnyObject, undefined, "">;
86
87
  export declare const teamIdSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
87
88
  export declare const teamDisplayNameSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
89
+ export declare const teamProfileImageUrlSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
88
90
  export declare const teamClientMetadataSchema: yup.MixedSchema<{} | null, yup.AnyObject, undefined, "">;
89
91
  export declare const teamServerMetadataSchema: yup.MixedSchema<{} | null, yup.AnyObject, undefined, "">;
90
92
  export declare const teamCreatedAtMillisSchema: yup.NumberSchema<number | undefined, yup.AnyObject, undefined, "">;
93
+ export declare const teamMemberDisplayNameSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
94
+ export declare const teamMemberProfileImageUrlSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
91
95
  export declare function yupRequiredWhen<S extends yup.AnyObject>(schema: S, triggerName: string, isValue: any): S;
92
96
  export {};
@@ -1,12 +1,13 @@
1
1
  import * as yup from "yup";
2
+ import { allProviders } from "./utils/oauth";
2
3
  import { isUuid } from "./utils/uuids";
3
4
  const _idDescription = (identify) => `The unique identifier of this ${identify}`;
4
5
  const _displayNameDescription = (identify) => `Human-readable ${identify} display name. This is not a unique identifier.`;
5
6
  const _clientMetaDataDescription = (identify) => `Client metadata. Used as a data store, accessible from the client side. Do not store information that should not be exposed to the client.`;
7
+ const _profileImageUrlDescription = (identify) => `URL of the profile image for ${identify}. Can be a Base64 encoded image. Please compress and crop to a square before passing in.`;
6
8
  const _serverMetaDataDescription = (identify) => `Server metadata. Used as a data store, only accessible from the server side. You can store secret information related to the ${identify} here.`;
7
9
  const _atMillisDescription = (identify) => `(the number of milliseconds since epoch, January 1, 1970, UTC)`;
8
10
  const _createdAtMillisDescription = (identify) => `The time the ${identify} was created ${_atMillisDescription(identify)}`;
9
- const _updatedAtMillisDescription = (identify) => `The time the ${identify} was last updated ${_atMillisDescription(identify)}`;
10
11
  const _signedUpAtMillisDescription = `The time the user signed up ${_atMillisDescription}`;
11
12
  // Built-in replacements
12
13
  /* eslint-disable no-restricted-syntax */
@@ -86,6 +87,17 @@ export const jsonStringSchema = yupString().test("json", "Invalid JSON format",
86
87
  return false;
87
88
  }
88
89
  });
90
+ export const jsonStringOrEmptySchema = yupString().test("json", "Invalid JSON format", (value) => {
91
+ if (!value)
92
+ return true;
93
+ try {
94
+ JSON.parse(value);
95
+ return true;
96
+ }
97
+ catch (error) {
98
+ return false;
99
+ }
100
+ });
89
101
  export const emailSchema = yupString().email();
90
102
  // Request auth
91
103
  export const clientOrHigherAuthTypeSchema = yupString().oneOf(['client', 'server', 'admin']);
@@ -105,7 +117,7 @@ export const projectCreateTeamOnSignUpSchema = yupBoolean().meta({ openapiField:
105
117
  export const projectMagicLinkEnabledSchema = yupBoolean().meta({ openapiField: { description: 'Whether magic link authentication is enabled for this project', exampleValue: true } });
106
118
  export const projectCredentialEnabledSchema = yupBoolean().meta({ openapiField: { description: 'Whether email password authentication is enabled for this project', exampleValue: true } });
107
119
  // Project OAuth config
108
- export const oauthIdSchema = yupString().oneOf(['google', 'github', 'facebook', 'microsoft', 'spotify']).meta({ openapiField: { description: 'OAuth provider ID, one of google, github, facebook, microsoft, spotify', exampleValue: 'google' } });
120
+ export const oauthIdSchema = yupString().oneOf(allProviders).meta({ openapiField: { description: `OAuth provider ID, one of ${allProviders.map(x => `\`${x}\``).join(', ')}`, exampleValue: 'google' } });
109
121
  export const oauthEnabledSchema = yupBoolean().meta({ openapiField: { description: 'Whether the OAuth provider is enabled. If an provider is first enabled, then disabled, it will be shown in the list but with enabled=false', exampleValue: true } });
110
122
  export const oauthTypeSchema = yupString().oneOf(['shared', 'standard']).meta({ openapiField: { description: 'OAuth provider type, one of shared, standard. "shared" uses Stack shared OAuth keys and it is only meant for development. "standard" uses your own OAuth keys and will show your logo and company name when signing in with the provider.', exampleValue: 'standard' } });
111
123
  export const oauthClientIdSchema = yupString().meta({ openapiField: { description: 'OAuth client ID. Needs to be specified when using type="standard"', exampleValue: 'google-oauth-client-id' } });
@@ -144,7 +156,7 @@ export const primaryEmailSchema = emailSchema.meta({ openapiField: { description
144
156
  export const primaryEmailVerifiedSchema = yupBoolean().meta({ openapiField: { description: 'Whether the primary email has been verified to belong to this user', exampleValue: true } });
145
157
  export const userDisplayNameSchema = yupString().nullable().meta({ openapiField: { description: _displayNameDescription('user'), exampleValue: 'John Doe' } });
146
158
  export const selectedTeamIdSchema = yupString().meta({ openapiField: { description: 'ID of the team currently selected by the user', exampleValue: 'team-id' } });
147
- export const profileImageUrlSchema = yupString().meta({ openapiField: { description: 'Profile image URL. Can be a Base64 encoded image. Please compress and crop to a square before passing in.', exampleValue: 'https://example.com/image.jpg' } });
159
+ export const profileImageUrlSchema = yupString().meta({ openapiField: { description: _profileImageUrlDescription('user'), exampleValue: 'https://example.com/image.jpg' } });
148
160
  export const signedUpAtMillisSchema = yupNumber().meta({ openapiField: { description: _signedUpAtMillisDescription, exampleValue: 1630000000000 } });
149
161
  export const userClientMetadataSchema = jsonSchema.meta({ openapiField: { description: _clientMetaDataDescription('user'), exampleValue: { key: 'value' } } });
150
162
  export const userServerMetadataSchema = jsonSchema.meta({ openapiField: { description: _serverMetaDataDescription('user'), exampleValue: { key: 'value' } } });
@@ -187,9 +199,13 @@ export const containedPermissionIdsSchema = yupArray(teamPermissionDefinitionIdS
187
199
  // Teams
188
200
  export const teamIdSchema = yupString().uuid().meta({ openapiField: { description: _idDescription('team'), exampleValue: 'ad962777-8244-496a-b6a2-e0c6a449c79e' } });
189
201
  export const teamDisplayNameSchema = yupString().meta({ openapiField: { description: _displayNameDescription('team'), exampleValue: 'My Team' } });
202
+ export const teamProfileImageUrlSchema = yupString().meta({ openapiField: { description: _profileImageUrlDescription('team'), exampleValue: 'https://example.com/image.jpg' } });
190
203
  export const teamClientMetadataSchema = jsonSchema.meta({ openapiField: { description: _clientMetaDataDescription('team'), exampleValue: { key: 'value' } } });
191
204
  export const teamServerMetadataSchema = jsonSchema.meta({ openapiField: { description: _serverMetaDataDescription('team'), exampleValue: { key: 'value' } } });
192
205
  export const teamCreatedAtMillisSchema = yupNumber().meta({ openapiField: { description: _createdAtMillisDescription('team'), exampleValue: 1630000000000 } });
206
+ // Team member profiles
207
+ export const teamMemberDisplayNameSchema = yupString().meta({ openapiField: { description: _displayNameDescription('team member') + ' Note that this is separate from the display_name of the user.', exampleValue: 'John Doe' } });
208
+ export const teamMemberProfileImageUrlSchema = yupString().meta({ openapiField: { description: _profileImageUrlDescription('team member'), exampleValue: 'https://example.com/image.jpg' } });
193
209
  // Utils
194
210
  export function yupRequiredWhen(schema, triggerName, isValue) {
195
211
  return schema.when(triggerName, {
@@ -13,7 +13,7 @@ export function throwErr(...args) {
13
13
  }
14
14
  export class StackAssertionError extends Error {
15
15
  constructor(message, extraData, options) {
16
- const disclaimer = `\n\nThis is likely an error in Stack. Please report it.`;
16
+ const disclaimer = `\n\nThis is likely an error in Stack. Please make sure you are running the newest version and report it.`;
17
17
  super(`${message}${message.endsWith(disclaimer) ? "" : disclaimer}`, options);
18
18
  this.extraData = extraData;
19
19
  }
@@ -0,0 +1,6 @@
1
+ export declare const standardProviders: readonly ["google", "github", "facebook", "microsoft", "spotify"];
2
+ export declare const sharedProviders: readonly ["google", "github", "facebook", "microsoft", "spotify"];
3
+ export declare const allProviders: readonly ["google", "github", "facebook", "microsoft", "spotify"];
4
+ export type ProviderType = typeof allProviders[number];
5
+ export type StandardProviderType = typeof standardProviders[number];
6
+ export type SharedProviderType = typeof sharedProviders[number];
@@ -0,0 +1,3 @@
1
+ export const standardProviders = ["google", "github", "facebook", "microsoft", "spotify"];
2
+ export const sharedProviders = ["google", "github", "facebook", "microsoft", "spotify"];
3
+ export const allProviders = ["google", "github", "facebook", "microsoft", "spotify"];
@@ -86,13 +86,18 @@ function mapResult(result, fn) {
86
86
  }
87
87
  class RetryError extends AggregateError {
88
88
  constructor(errors) {
89
+ const strings = errors.map(e => String(e));
90
+ const isAllSame = strings.length > 1 && strings.every(s => s === strings[0]);
89
91
  super(errors, deindent `
90
92
  Error after retrying ${errors.length} times.
91
93
 
92
- ${errors.map((e, i) => deindent `
93
- Attempt ${i + 1}:
94
- ${e}
95
- `).join("\n\n")}
94
+ ${isAllSame ? deindent `
95
+ Attempts 1-${errors.length}:
96
+ ${errors[0]}
97
+ ` : errors.map((e, i) => deindent `
98
+ Attempt ${i + 1}:
99
+ ${e}
100
+ `).join("\n\n")}
96
101
  `, { cause: errors[errors.length - 1] });
97
102
  this.errors = errors;
98
103
  this.name = "RetryError";
@@ -1 +1,3 @@
1
1
  export declare function isLocalhost(urlOrString: string | URL): boolean;
2
+ export declare function isRelative(url: string): boolean;
3
+ export declare function getRelativePart(url: URL): string;
@@ -1,3 +1,4 @@
1
+ import { generateSecureRandomString } from "./crypto";
1
2
  export function isLocalhost(urlOrString) {
2
3
  const url = new URL(urlOrString);
3
4
  if (url.hostname === "localhost" || url.hostname.endsWith(".localhost"))
@@ -6,3 +7,15 @@ export function isLocalhost(urlOrString) {
6
7
  return true;
7
8
  return false;
8
9
  }
10
+ export function isRelative(url) {
11
+ const randomDomain = `${generateSecureRandomString()}.stack-auth.example.com`;
12
+ const u = new URL(url, `https://${randomDomain}`);
13
+ if (u.host !== randomDomain)
14
+ return false;
15
+ if (u.protocol !== "https:")
16
+ return false;
17
+ return true;
18
+ }
19
+ export function getRelativePart(url) {
20
+ return url.pathname + url.search + url.hash;
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackframe/stack-shared",
3
- "version": "2.5.8",
3
+ "version": "2.5.10",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -36,7 +36,7 @@
36
36
  "jose": "^5.2.2",
37
37
  "oauth4webapi": "^2.10.3",
38
38
  "uuid": "^9.0.1",
39
- "@stackframe/stack-sc": "2.5.8"
39
+ "@stackframe/stack-sc": "2.5.10"
40
40
  },
41
41
  "devDependencies": {
42
42
  "rimraf": "^5.0.5",