@stackframe/stack-shared 2.7.0 → 2.7.3

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,26 @@
1
1
  # @stackframe/stack-shared
2
2
 
3
+ ## 2.7.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Various changes
8
+ - @stackframe/stack-sc@2.7.3
9
+
10
+ ## 2.7.2
11
+
12
+ ### Patch Changes
13
+
14
+ - Various changes
15
+ - @stackframe/stack-sc@2.7.2
16
+
17
+ ## 2.7.1
18
+
19
+ ### Patch Changes
20
+
21
+ - Various changes
22
+ - @stackframe/stack-sc@2.7.1
23
+
3
24
  ## 2.7.0
4
25
 
5
26
  ### Minor Changes
@@ -268,9 +268,11 @@ export class StackClientInterface {
268
268
  // Rate limited, so retry if we can
269
269
  const retryAfter = res.headers.get("Retry-After");
270
270
  if (retryAfter !== null) {
271
+ console.log(`Rate limited while sending request to ${url}. Will retry after ${retryAfter} seconds...`);
271
272
  await wait(Number(retryAfter) * 1000);
272
273
  return Result.error(new Error(`Rate limited, retrying after ${retryAfter} seconds`));
273
274
  }
275
+ console.log(`Rate limited while sending request to ${url}, no retry-after header received. Retrying...`);
274
276
  return Result.error(new Error("Rate limited, no retry-after header received"));
275
277
  }
276
278
  else {
@@ -1,6 +1,6 @@
1
1
  import { CrudTypeOf } from "../../crud";
2
2
  export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions<{
3
- clientReadSchema: import("yup").ObjectSchema<({
3
+ clientReadSchema: import("yup").ObjectSchema<{
4
4
  primary_email: string | null;
5
5
  id: string;
6
6
  display_name: string | null;
@@ -28,7 +28,7 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
28
28
  display_name: string;
29
29
  profile_image_url: string | null;
30
30
  } | null;
31
- }) | null, import("yup").AnyObject, {
31
+ }, import("yup").AnyObject, {
32
32
  id: undefined;
33
33
  primary_email: undefined;
34
34
  primary_email_verified: undefined;
@@ -87,7 +87,7 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
87
87
  }[];
88
88
  auth_with_email: boolean;
89
89
  requires_totp_mfa: boolean;
90
- } | null, import("yup").AnyObject, {
90
+ }, import("yup").AnyObject, {
91
91
  id: undefined;
92
92
  primary_email: undefined;
93
93
  primary_email_verified: undefined;
@@ -30,9 +30,8 @@ const clientReadSchema = usersCrudServerReadSchema.pick([
30
30
  "passkey_auth_enabled",
31
31
  ]).concat(yupObject({
32
32
  selected_team: teamsCrudClientReadSchema.nullable().defined(),
33
- })).nullable().defined(); // TODO: next-release: make required
34
- // TODO: next-release: make required
35
- const serverReadSchema = usersCrudServerReadSchema.nullable().defined();
33
+ })).defined();
34
+ const serverReadSchema = usersCrudServerReadSchema.defined();
36
35
  const clientDeleteSchema = usersCrudServerDeleteSchema;
37
36
  export const currentUserCrud = createCrud({
38
37
  clientReadSchema,
@@ -1,6 +1,6 @@
1
1
  import { createCrud } from "../../crud";
2
2
  import * as schemaFields from "../../schema-fields";
3
- import { yupArray, yupDefinedWhen, yupObject, yupString } from "../../schema-fields";
3
+ import { yupArray, yupObject, yupString } from "../../schema-fields";
4
4
  const teamPermissionSchema = yupObject({
5
5
  id: yupString().defined(),
6
6
  }).defined();
@@ -8,8 +8,14 @@ const oauthProviderSchema = yupObject({
8
8
  id: schemaFields.oauthIdSchema.defined(),
9
9
  enabled: schemaFields.oauthEnabledSchema.defined(),
10
10
  type: schemaFields.oauthTypeSchema.defined(),
11
- client_id: yupDefinedWhen(schemaFields.oauthClientIdSchema, 'type', 'standard'),
12
- client_secret: yupDefinedWhen(schemaFields.oauthClientSecretSchema, 'type', 'standard'),
11
+ client_id: schemaFields.yupDefinedAndNonEmptyWhen(schemaFields.oauthClientIdSchema, {
12
+ type: 'standard',
13
+ enabled: true,
14
+ }),
15
+ client_secret: schemaFields.yupDefinedAndNonEmptyWhen(schemaFields.oauthClientSecretSchema, {
16
+ type: 'standard',
17
+ enabled: true,
18
+ }),
13
19
  // extra params
14
20
  facebook_config_id: schemaFields.oauthFacebookConfigIdSchema.optional(),
15
21
  microsoft_tenant_id: schemaFields.oauthMicrosoftTenantIdSchema.optional(),
@@ -19,12 +25,24 @@ const enabledOAuthProviderSchema = yupObject({
19
25
  });
20
26
  export const emailConfigSchema = yupObject({
21
27
  type: schemaFields.emailTypeSchema.defined(),
22
- host: yupDefinedWhen(schemaFields.emailHostSchema, 'type', 'standard'),
23
- port: yupDefinedWhen(schemaFields.emailPortSchema, 'type', 'standard'),
24
- username: yupDefinedWhen(schemaFields.emailUsernameSchema, 'type', 'standard'),
25
- password: yupDefinedWhen(schemaFields.emailPasswordSchema, 'type', 'standard'),
26
- sender_name: yupDefinedWhen(schemaFields.emailSenderNameSchema, 'type', 'standard'),
27
- sender_email: yupDefinedWhen(schemaFields.emailSenderEmailSchema, 'type', 'standard'),
28
+ host: schemaFields.yupDefinedAndNonEmptyWhen(schemaFields.emailHostSchema, {
29
+ type: 'standard',
30
+ }),
31
+ port: schemaFields.yupDefinedWhen(schemaFields.emailPortSchema, {
32
+ type: 'standard',
33
+ }),
34
+ username: schemaFields.yupDefinedAndNonEmptyWhen(schemaFields.emailUsernameSchema, {
35
+ type: 'standard',
36
+ }),
37
+ password: schemaFields.yupDefinedAndNonEmptyWhen(schemaFields.emailPasswordSchema, {
38
+ type: 'standard',
39
+ }),
40
+ sender_name: schemaFields.yupDefinedAndNonEmptyWhen(schemaFields.emailSenderNameSchema, {
41
+ type: 'standard',
42
+ }),
43
+ sender_email: schemaFields.yupDefinedAndNonEmptyWhen(schemaFields.emailSenderEmailSchema, {
44
+ type: 'standard',
45
+ }),
28
46
  });
29
47
  const domainSchema = yupObject({
30
48
  domain: schemaFields.projectTrustedDomainSchema.defined(),
@@ -103,17 +121,17 @@ export const projectsCrud = createCrud({
103
121
  docs: {
104
122
  clientRead: {
105
123
  summary: 'Get the current project',
106
- description: 'Get the current project information including display name, oauth providers and authentication methods. Useful for display the available login options to the user.',
124
+ description: 'Get the current project information including display name, OAuth providers and authentication methods. Useful for display the available login options to the user.',
107
125
  tags: ['Projects'],
108
126
  },
109
127
  adminRead: {
110
128
  summary: 'Get the current project',
111
- description: 'Get the current project information and configuration including display name, oauth providers, email configuration, etc.',
129
+ description: 'Get the current project information and configuration including display name, OAuth providers, email configuration, etc.',
112
130
  tags: ['Projects'],
113
131
  },
114
132
  adminUpdate: {
115
133
  summary: 'Update the current project',
116
- description: 'Update the current project information and configuration including display name, oauth providers, email configuration, etc.',
134
+ description: 'Update the current project information and configuration including display name, OAuth providers, email configuration, etc.',
117
135
  tags: ['Projects'],
118
136
  },
119
137
  adminDelete: {
@@ -66,21 +66,6 @@ export declare const KnownErrors: {
66
66
  } & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION">, [statusCode: number, humanReadableMessage: string, details?: Json | undefined]> & {
67
67
  errorCode: "INVALID_PROJECT_AUTHENTICATION";
68
68
  };
69
- ProjectKeyWithoutRequestType: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & {
70
- constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
71
- } & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"PROJECT_KEY_WITHOUT_REQUEST_TYPE">, []> & {
72
- errorCode: "PROJECT_KEY_WITHOUT_REQUEST_TYPE";
73
- };
74
- InvalidRequestType: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & {
75
- constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
76
- } & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"INVALID_REQUEST_TYPE">, [requestType: string]> & {
77
- errorCode: "INVALID_REQUEST_TYPE";
78
- };
79
- RequestTypeWithoutProjectId: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & {
80
- constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
81
- } & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"REQUEST_TYPE_WITHOUT_PROJECT_ID">, [requestType: "client" | "server" | "admin"]> & {
82
- errorCode: "REQUEST_TYPE_WITHOUT_PROJECT_ID";
83
- };
84
69
  ProjectKeyWithoutAccessType: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & {
85
70
  constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
86
71
  } & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"PROJECT_KEY_WITHOUT_ACCESS_TYPE">, []> & {
@@ -105,35 +105,6 @@ const AllOverloadsFailed = createKnownErrorConstructor(KnownError, "ALL_OVERLOAD
105
105
  ]);
106
106
  const ProjectAuthenticationError = createKnownErrorConstructor(KnownError, "PROJECT_AUTHENTICATION_ERROR", "inherit", "inherit");
107
107
  const InvalidProjectAuthentication = createKnownErrorConstructor(ProjectAuthenticationError, "INVALID_PROJECT_AUTHENTICATION", "inherit", "inherit");
108
- // TODO next-release: delete deprecated error type
109
- /**
110
- * @deprecated Use ProjectKeyWithoutAccessType instead
111
- */
112
- const ProjectKeyWithoutRequestType = createKnownErrorConstructor(InvalidProjectAuthentication, "PROJECT_KEY_WITHOUT_REQUEST_TYPE", () => [
113
- 400,
114
- "Either an API key or an admin access token was provided, but the x-stack-access-type header is missing. Set it to 'client', 'server', or 'admin' as appropriate.",
115
- ], () => []);
116
- // TODO next-release: delete deprecated error type
117
- /**
118
- * @deprecated Use InvalidAccessType instead
119
- */
120
- const InvalidRequestType = createKnownErrorConstructor(InvalidProjectAuthentication, "INVALID_REQUEST_TYPE", (requestType) => [
121
- 400,
122
- `The x-stack-access-type header must be 'client', 'server', or 'admin', but was '${requestType}'.`,
123
- ], (json) => [
124
- json?.requestType ?? throwErr("requestType not found in InvalidRequestType details"),
125
- ]);
126
- // TODO next-release: delete deprecated error type
127
- /**
128
- * @deprecated Use AccessTypeWithoutProjectId instead
129
- */
130
- const RequestTypeWithoutProjectId = createKnownErrorConstructor(InvalidProjectAuthentication, "REQUEST_TYPE_WITHOUT_PROJECT_ID", (requestType) => [
131
- 400,
132
- `The x-stack-access-type header was '${requestType}', but the x-stack-project-id header was not provided.`,
133
- {
134
- request_type: requestType,
135
- },
136
- ], (json) => [json.request_type]);
137
108
  const ProjectKeyWithoutAccessType = createKnownErrorConstructor(InvalidProjectAuthentication, "PROJECT_KEY_WITHOUT_ACCESS_TYPE", () => [
138
109
  400,
139
110
  "Either an API key or an admin access token was provided, but the x-stack-access-type header is missing. Set it to 'client', 'server', or 'admin' as appropriate.",
@@ -288,7 +259,7 @@ const ProviderRejected = createKnownErrorConstructor(RefreshTokenError, "PROVIDE
288
259
  ], () => []);
289
260
  const UserEmailAlreadyExists = createKnownErrorConstructor(KnownError, "USER_EMAIL_ALREADY_EXISTS", () => [
290
261
  400,
291
- "User already exists.",
262
+ "User email already exists.",
292
263
  ], () => []);
293
264
  const CannotGetOwnUserWithoutUser = createKnownErrorConstructor(KnownError, "CANNOT_GET_OWN_USER_WITHOUT_USER", () => [
294
265
  400,
@@ -570,9 +541,6 @@ export const KnownErrors = {
570
541
  AllOverloadsFailed,
571
542
  ProjectAuthenticationError,
572
543
  InvalidProjectAuthentication,
573
- ProjectKeyWithoutRequestType,
574
- InvalidRequestType,
575
- RequestTypeWithoutProjectId,
576
544
  ProjectKeyWithoutAccessType,
577
545
  InvalidAccessType,
578
546
  AccessTypeWithoutProjectId,
@@ -2,6 +2,7 @@ import * as yup from "yup";
2
2
  declare module "yup" {
3
3
  interface StringSchema<TType, TContext, TDefault, TFlags> {
4
4
  nonEmpty(message?: string): StringSchema<TType, TContext, TDefault, TFlags>;
5
+ empty(): StringSchema<TType, TContext, TDefault, TFlags>;
5
6
  }
6
7
  }
7
8
  export declare function yupValidate<S extends yup.ISchema<any>>(schema: S, obj: unknown, options?: yup.ValidateOptions & {
@@ -149,5 +150,6 @@ export declare const contactChannelIsVerifiedSchema: yup.BooleanSchema<boolean |
149
150
  export declare const contactChannelIsPrimarySchema: yup.BooleanSchema<boolean | undefined, yup.AnyObject, undefined, "">;
150
151
  export declare const basicAuthorizationHeaderSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
151
152
  export declare const neonAuthorizationHeaderSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
152
- export declare function yupDefinedWhen<S extends yup.AnyObject>(schema: S, triggerName: string, isValue: any): S;
153
+ export declare function yupDefinedWhen<S extends yup.AnyObject>(schema: S, triggers: Record<string, any>): S;
154
+ export declare function yupDefinedAndNonEmptyWhen<S extends yup.StringSchema>(schema: S, triggers: Record<string, any>): S;
153
155
  export {};
@@ -9,7 +9,7 @@ import { isValidUrl } from "./utils/urls";
9
9
  import { isUuid } from "./utils/uuids";
10
10
  // eslint-disable-next-line no-restricted-syntax
11
11
  yup.addMethod(yup.string, "nonEmpty", function (message) {
12
- return this.test("non-empty", message ?? "String must not be empty", (value) => {
12
+ return this.test("non-empty", message ?? (({ path }) => `${path} must not be empty`), (value) => {
13
13
  return value !== "";
14
14
  });
15
15
  });
@@ -371,10 +371,19 @@ export const neonAuthorizationHeaderSchema = basicAuthorizationHeaderSchema.test
371
371
  return false;
372
372
  });
373
373
  // Utils
374
- export function yupDefinedWhen(schema, triggerName, isValue) {
375
- return schema.when(triggerName, {
376
- is: isValue,
374
+ export function yupDefinedWhen(schema, triggers) {
375
+ const entries = Object.entries(triggers);
376
+ return schema.when(entries.map(([key]) => key), {
377
+ is: (...values) => entries.every(([key, value], index) => value === values[index]),
377
378
  then: (schema) => schema.defined(),
378
379
  otherwise: (schema) => schema.optional()
379
380
  });
380
381
  }
382
+ export function yupDefinedAndNonEmptyWhen(schema, triggers) {
383
+ const entries = Object.entries(triggers);
384
+ return schema.when(entries.map(([key]) => key), {
385
+ is: (...values) => entries.every(([key, value], index) => value === values[index]),
386
+ then: (schema) => schema.defined().nonEmpty(),
387
+ otherwise: (schema) => schema.optional()
388
+ });
389
+ }
@@ -1,5 +1,6 @@
1
1
  import { globalVar } from "./globals";
2
2
  import { pick } from "./objects";
3
+ import { nicify } from "./strings";
3
4
  export function throwErr(...args) {
4
5
  if (typeof args[0] === "string") {
5
6
  throw new StackAssertionError(args[0], args[1]);
@@ -67,11 +68,11 @@ StackAssertionError.prototype.name = "StackAssertionError";
67
68
  export function errorToNiceString(error) {
68
69
  if (!(error instanceof Error))
69
70
  return `${typeof error}<${error}>`;
70
- const stack = error.stack ?? "";
71
+ let stack = error.stack ?? "";
71
72
  const toString = error.toString();
72
- if (stack.startsWith(toString))
73
- return stack;
74
- return `${toString}\n${stack}`;
73
+ if (!stack.startsWith(toString))
74
+ stack = `${toString}\n${stack}`; // some browsers don't include the error message in the stack, some do
75
+ return `${stack} ${nicify(Object.fromEntries(Object.entries(error)), { maxDepth: 8 })}`;
75
76
  }
76
77
  const errorSinks = new Set();
77
78
  export function registerErrorSink(sink) {
@@ -1,3 +1,4 @@
1
+ export declare function isNotNull<T>(value: T): value is NonNullable<T>;
1
2
  export type DeepPartial<T> = T extends object ? {
2
3
  [P in keyof T]?: DeepPartial<T[P]>;
3
4
  } : T;
@@ -10,8 +11,6 @@ export declare function deepPlainEquals<T>(obj1: T, obj2: unknown, options?: {
10
11
  ignoreUndefinedValues?: boolean;
11
12
  }): obj2 is T;
12
13
  export declare function deepPlainClone<T>(obj: T): T;
13
- export declare function deepPlainSnakeCaseToCamelCase(snakeCaseObj: any): any;
14
- export declare function deepPlainCamelCaseToSnakeCase(camelCaseObj: any): any;
15
14
  export declare function typedEntries<T extends {}>(obj: T): [keyof T, T[keyof T]][];
16
15
  export declare function typedFromEntries<K extends PropertyKey, V>(entries: [K, V][]): Record<K, V>;
17
16
  export declare function typedKeys<T extends {}>(obj: T): (keyof T)[];
@@ -1,5 +1,7 @@
1
1
  import { StackAssertionError } from "./errors";
2
- import { camelCaseToSnakeCase, snakeCaseToCamelCase } from "./strings";
2
+ export function isNotNull(value) {
3
+ return value !== null && value !== undefined;
4
+ }
3
5
  /**
4
6
  * Assumes both objects are primitives, arrays, or non-function plain objects, and compares them deeply.
5
7
  *
@@ -57,24 +59,6 @@ export function deepPlainClone(obj) {
57
59
  return obj.map(deepPlainClone);
58
60
  return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, deepPlainClone(v)]));
59
61
  }
60
- export function deepPlainSnakeCaseToCamelCase(snakeCaseObj) {
61
- if (typeof snakeCaseObj === 'function')
62
- throw new StackAssertionError("deepPlainSnakeCaseToCamelCase does not support functions");
63
- if (typeof snakeCaseObj !== 'object' || !snakeCaseObj)
64
- return snakeCaseObj;
65
- if (Array.isArray(snakeCaseObj))
66
- return snakeCaseObj.map(o => deepPlainSnakeCaseToCamelCase(o));
67
- return Object.fromEntries(Object.entries(snakeCaseObj).map(([k, v]) => [snakeCaseToCamelCase(k), deepPlainSnakeCaseToCamelCase(v)]));
68
- }
69
- export function deepPlainCamelCaseToSnakeCase(camelCaseObj) {
70
- if (typeof camelCaseObj === 'function')
71
- throw new StackAssertionError("deepPlainCamelCaseToSnakeCase does not support functions");
72
- if (typeof camelCaseObj !== 'object' || !camelCaseObj)
73
- return camelCaseObj;
74
- if (Array.isArray(camelCaseObj))
75
- return camelCaseObj.map(o => deepPlainCamelCaseToSnakeCase(o));
76
- return Object.fromEntries(Object.entries(camelCaseObj).map(([k, v]) => [camelCaseToSnakeCase(k), deepPlainCamelCaseToSnakeCase(v)]));
77
- }
78
62
  export function typedEntries(obj) {
79
63
  return Object.entries(obj);
80
64
  }
@@ -1,6 +1,10 @@
1
1
  export declare function typedToLowercase<S extends string>(s: S): Lowercase<S>;
2
2
  export declare function typedToUppercase<S extends string>(s: S): Uppercase<S>;
3
3
  export declare function typedCapitalize<S extends string>(s: S): Capitalize<S>;
4
+ /**
5
+ * Compares two strings in a way that is not dependent on the current locale.
6
+ */
7
+ export declare function stringCompare(a: string, b: string): number;
4
8
  /**
5
9
  * Returns all whitespace character at the start of the string.
6
10
  *
@@ -41,8 +45,6 @@ export declare function deindent(code: string): string;
41
45
  export declare function deindent(strings: TemplateStringsArray | readonly string[], ...values: any[]): string;
42
46
  export declare function extractScopes(scope: string, removeDuplicates?: boolean): string[];
43
47
  export declare function mergeScopeStrings(...scopes: string[]): string;
44
- export declare function snakeCaseToCamelCase(snakeCase: string): string;
45
- export declare function camelCaseToSnakeCase(camelCase: string): string;
46
48
  export type Nicifiable = {
47
49
  getNicifiableKeys?(): PropertyKey[];
48
50
  getNicifiedObjectExtraLines?(): string[];
@@ -10,6 +10,13 @@ export function typedToUppercase(s) {
10
10
  export function typedCapitalize(s) {
11
11
  return s.charAt(0).toUpperCase() + s.slice(1);
12
12
  }
13
+ /**
14
+ * Compares two strings in a way that is not dependent on the current locale.
15
+ */
16
+ export function stringCompare(a, b) {
17
+ const cmp = (a, b) => a < b ? -1 : a > b ? 1 : 0;
18
+ return cmp(a.toUpperCase(), b.toUpperCase()) || cmp(b, a);
19
+ }
13
20
  /**
14
21
  * Returns all whitespace character at the start of the string.
15
22
  *
@@ -105,16 +112,6 @@ export function mergeScopeStrings(...scopes) {
105
112
  const allScope = scopes.map((s) => extractScopes(s)).flat().join(" ");
106
113
  return extractScopes(allScope).join(" ");
107
114
  }
108
- export function snakeCaseToCamelCase(snakeCase) {
109
- if (snakeCase.match(/[A-Z]/))
110
- return snakeCase; // TODO next-release: this is a hack for fixing the email templates, remove this after v2 migration
111
- return snakeCase.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
112
- }
113
- export function camelCaseToSnakeCase(camelCase) {
114
- if (camelCase.match(/_/))
115
- return camelCase; // TODO next-release: this is a hack for fixing the email templates, remove this after v2 migration
116
- return camelCase.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
117
- }
118
115
  /**
119
116
  * Some classes have different constructor names in different environments (eg. `Headers` is sometimes called `_Headers`,
120
117
  * so we create an object of overrides to handle these cases.
@@ -279,7 +276,7 @@ function getNicifiableEntries(value) {
279
276
  return recordLikes.some(x => value instanceof x);
280
277
  }
281
278
  if (isRecordLike(value)) {
282
- return [...value.entries()].sort(([a], [b]) => String(a).localeCompare(String(b)));
279
+ return [...value.entries()].sort(([a], [b]) => stringCompare(`${a}`, `${b}`));
283
280
  }
284
281
  const keys = getNicifiableKeys(value);
285
282
  return keys.map((k) => [k, value[k]]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackframe/stack-shared",
3
- "version": "2.7.0",
3
+ "version": "2.7.3",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -51,7 +51,7 @@
51
51
  "oauth4webapi": "^2.10.3",
52
52
  "semver": "^7.6.3",
53
53
  "uuid": "^9.0.1",
54
- "@stackframe/stack-sc": "2.7.0"
54
+ "@stackframe/stack-sc": "2.7.3"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@sentry/nextjs": "^8.40.0",