@stackframe/stack-shared 2.6.39 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/interface/adminInterface.d.ts +16 -1
- package/dist/interface/adminInterface.js +16 -0
- package/dist/interface/crud/current-user.d.ts +3 -3
- package/dist/interface/crud/current-user.js +2 -3
- package/dist/interface/crud/projects.d.ts +17 -0
- package/dist/interface/crud/projects.js +28 -10
- package/dist/known-errors.d.ts +0 -15
- package/dist/known-errors.js +1 -33
- package/dist/schema-fields.d.ts +3 -1
- package/dist/schema-fields.js +13 -4
- package/dist/utils/dates.d.ts +1 -0
- package/dist/utils/dates.js +3 -0
- package/dist/utils/locks.d.ts +14 -0
- package/dist/utils/locks.js +62 -0
- package/dist/utils/objects.d.ts +0 -2
- package/dist/utils/objects.js +0 -19
- package/dist/utils/stores.d.ts +3 -1
- package/dist/utils/stores.js +8 -4
- package/dist/utils/strings.d.ts +0 -2
- package/dist/utils/strings.js +0 -10
- package/dist/utils/unicode.d.ts +1 -0
- package/dist/utils/unicode.js +10 -0
- package/package.json +9 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @stackframe/stack-shared
|
|
2
2
|
|
|
3
|
+
## 2.7.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Various changes
|
|
8
|
+
- @stackframe/stack-sc@2.7.1
|
|
9
|
+
|
|
10
|
+
## 2.7.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- Various changes
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- @stackframe/stack-sc@2.7.0
|
|
19
|
+
|
|
3
20
|
## 2.6.39
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
|
@@ -25,7 +25,7 @@ export type ApiKeyCreateCrudResponse = ApiKeysCrud["Admin"]["Read"] & {
|
|
|
25
25
|
export declare class StackAdminInterface extends StackServerInterface {
|
|
26
26
|
readonly options: AdminAuthApplicationOptions;
|
|
27
27
|
constructor(options: AdminAuthApplicationOptions);
|
|
28
|
-
|
|
28
|
+
sendAdminRequest(path: string, options: RequestInit, session: InternalSession | null, requestType?: "admin"): Promise<Response & {
|
|
29
29
|
usedTokens: {
|
|
30
30
|
accessToken: import("../sessions").AccessToken;
|
|
31
31
|
refreshToken: import("../sessions").RefreshToken | null;
|
|
@@ -46,4 +46,19 @@ export declare class StackAdminInterface extends StackServerInterface {
|
|
|
46
46
|
deletePermissionDefinition(permissionId: string): Promise<void>;
|
|
47
47
|
getSvixToken(): Promise<SvixTokenCrud["Admin"]["Read"]>;
|
|
48
48
|
deleteProject(): Promise<void>;
|
|
49
|
+
getMetrics(): Promise<any>;
|
|
50
|
+
sendTestEmail(data: {
|
|
51
|
+
recipient_email: string;
|
|
52
|
+
email_config: {
|
|
53
|
+
host: string;
|
|
54
|
+
port: number;
|
|
55
|
+
username: string;
|
|
56
|
+
password: string;
|
|
57
|
+
sender_email: string;
|
|
58
|
+
sender_name: string;
|
|
59
|
+
};
|
|
60
|
+
}): Promise<{
|
|
61
|
+
success: boolean;
|
|
62
|
+
error_message?: string;
|
|
63
|
+
}>;
|
|
49
64
|
}
|
|
@@ -120,4 +120,20 @@ export class StackAdminInterface extends StackServerInterface {
|
|
|
120
120
|
method: "DELETE",
|
|
121
121
|
}, null);
|
|
122
122
|
}
|
|
123
|
+
async getMetrics() {
|
|
124
|
+
const response = await this.sendAdminRequest("/internal/metrics", {
|
|
125
|
+
method: "GET",
|
|
126
|
+
}, null);
|
|
127
|
+
return await response.json();
|
|
128
|
+
}
|
|
129
|
+
async sendTestEmail(data) {
|
|
130
|
+
const response = await this.sendAdminRequest(`/internal/send-test-email`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: {
|
|
133
|
+
"content-type": "application/json",
|
|
134
|
+
},
|
|
135
|
+
body: JSON.stringify(data),
|
|
136
|
+
}, null);
|
|
137
|
+
return await response.json();
|
|
138
|
+
}
|
|
123
139
|
}
|
|
@@ -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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
})).
|
|
34
|
-
|
|
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,4 +1,21 @@
|
|
|
1
1
|
import { CrudTypeOf } from "../../crud";
|
|
2
|
+
export declare const emailConfigSchema: import("yup").ObjectSchema<{
|
|
3
|
+
type: "shared" | "standard";
|
|
4
|
+
host: string | undefined;
|
|
5
|
+
port: number | undefined;
|
|
6
|
+
username: string | undefined;
|
|
7
|
+
password: string | undefined;
|
|
8
|
+
sender_name: string | undefined;
|
|
9
|
+
sender_email: string | undefined;
|
|
10
|
+
}, import("yup").AnyObject, {
|
|
11
|
+
type: undefined;
|
|
12
|
+
host: undefined;
|
|
13
|
+
port: undefined;
|
|
14
|
+
username: undefined;
|
|
15
|
+
password: undefined;
|
|
16
|
+
sender_name: undefined;
|
|
17
|
+
sender_email: undefined;
|
|
18
|
+
}, "">;
|
|
2
19
|
export declare const projectsCrudAdminReadSchema: import("yup").ObjectSchema<{
|
|
3
20
|
id: string;
|
|
4
21
|
display_name: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createCrud } from "../../crud";
|
|
2
2
|
import * as schemaFields from "../../schema-fields";
|
|
3
|
-
import { yupArray,
|
|
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:
|
|
12
|
-
|
|
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(),
|
|
@@ -17,14 +23,26 @@ const oauthProviderSchema = yupObject({
|
|
|
17
23
|
const enabledOAuthProviderSchema = yupObject({
|
|
18
24
|
id: schemaFields.oauthIdSchema.defined(),
|
|
19
25
|
});
|
|
20
|
-
const emailConfigSchema = yupObject({
|
|
26
|
+
export const emailConfigSchema = yupObject({
|
|
21
27
|
type: schemaFields.emailTypeSchema.defined(),
|
|
22
|
-
host:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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(),
|
package/dist/known-errors.d.ts
CHANGED
|
@@ -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">, []> & {
|
package/dist/known-errors.js
CHANGED
|
@@ -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,
|
package/dist/schema-fields.d.ts
CHANGED
|
@@ -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,
|
|
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 {};
|
package/dist/schema-fields.js
CHANGED
|
@@ -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 ??
|
|
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,
|
|
375
|
-
|
|
376
|
-
|
|
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
|
+
}
|
package/dist/utils/dates.d.ts
CHANGED
package/dist/utils/dates.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type LockCallback<T> = () => Promise<T>;
|
|
2
|
+
export declare class ReadWriteLock {
|
|
3
|
+
private semaphore;
|
|
4
|
+
private readers;
|
|
5
|
+
private readersMutex;
|
|
6
|
+
constructor();
|
|
7
|
+
withReadLock<T>(callback: LockCallback<T>): Promise<T>;
|
|
8
|
+
withWriteLock<T>(callback: LockCallback<T>): Promise<T>;
|
|
9
|
+
private _acquireReadLock;
|
|
10
|
+
private _releaseReadLock;
|
|
11
|
+
private _acquireWriteLock;
|
|
12
|
+
private _releaseWriteLock;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Semaphore } from 'async-mutex';
|
|
2
|
+
export class ReadWriteLock {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.semaphore = new Semaphore(1); // Semaphore with 1 permit
|
|
5
|
+
this.readers = 0; // Track the number of readers
|
|
6
|
+
this.readersMutex = new Semaphore(1); // Protect access to `readers` count
|
|
7
|
+
}
|
|
8
|
+
async withReadLock(callback) {
|
|
9
|
+
await this._acquireReadLock();
|
|
10
|
+
try {
|
|
11
|
+
return await callback();
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
await this._releaseReadLock();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async withWriteLock(callback) {
|
|
18
|
+
await this._acquireWriteLock();
|
|
19
|
+
try {
|
|
20
|
+
return await callback();
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
await this._releaseWriteLock();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async _acquireReadLock() {
|
|
27
|
+
// Increment the readers count
|
|
28
|
+
await this.readersMutex.acquire();
|
|
29
|
+
try {
|
|
30
|
+
this.readers += 1;
|
|
31
|
+
// If this is the first reader, block writers
|
|
32
|
+
if (this.readers === 1) {
|
|
33
|
+
await this.semaphore.acquire();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
this.readersMutex.release();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async _releaseReadLock() {
|
|
41
|
+
// Decrement the readers count
|
|
42
|
+
await this.readersMutex.acquire();
|
|
43
|
+
try {
|
|
44
|
+
this.readers -= 1;
|
|
45
|
+
// If this was the last reader, release the writer block
|
|
46
|
+
if (this.readers === 0) {
|
|
47
|
+
this.semaphore.release();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
this.readersMutex.release();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async _acquireWriteLock() {
|
|
55
|
+
// Writers acquire the main semaphore exclusively
|
|
56
|
+
await this.semaphore.acquire();
|
|
57
|
+
}
|
|
58
|
+
async _releaseWriteLock() {
|
|
59
|
+
// Writers release the main semaphore
|
|
60
|
+
this.semaphore.release();
|
|
61
|
+
}
|
|
62
|
+
}
|
package/dist/utils/objects.d.ts
CHANGED
|
@@ -10,8 +10,6 @@ export declare function deepPlainEquals<T>(obj1: T, obj2: unknown, options?: {
|
|
|
10
10
|
ignoreUndefinedValues?: boolean;
|
|
11
11
|
}): obj2 is T;
|
|
12
12
|
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
13
|
export declare function typedEntries<T extends {}>(obj: T): [keyof T, T[keyof T]][];
|
|
16
14
|
export declare function typedFromEntries<K extends PropertyKey, V>(entries: [K, V][]): Record<K, V>;
|
|
17
15
|
export declare function typedKeys<T extends {}>(obj: T): (keyof T)[];
|
package/dist/utils/objects.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { StackAssertionError } from "./errors";
|
|
2
|
-
import { camelCaseToSnakeCase, snakeCaseToCamelCase } from "./strings";
|
|
3
2
|
/**
|
|
4
3
|
* Assumes both objects are primitives, arrays, or non-function plain objects, and compares them deeply.
|
|
5
4
|
*
|
|
@@ -57,24 +56,6 @@ export function deepPlainClone(obj) {
|
|
|
57
56
|
return obj.map(deepPlainClone);
|
|
58
57
|
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, deepPlainClone(v)]));
|
|
59
58
|
}
|
|
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
59
|
export function typedEntries(obj) {
|
|
79
60
|
return Object.entries(obj);
|
|
80
61
|
}
|
package/dist/utils/stores.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReadWriteLock } from "./locks";
|
|
2
2
|
import { ReactPromise } from "./promises";
|
|
3
|
+
import { AsyncResult, Result } from "./results";
|
|
3
4
|
export type ReadonlyStore<T> = {
|
|
4
5
|
get(): T;
|
|
5
6
|
onChange(callback: (value: T, oldValue: T | undefined) => void): {
|
|
@@ -45,6 +46,7 @@ export declare class Store<T> implements ReadonlyStore<T> {
|
|
|
45
46
|
unsubscribe: () => void;
|
|
46
47
|
};
|
|
47
48
|
}
|
|
49
|
+
export declare const storeLock: ReadWriteLock;
|
|
48
50
|
export declare class AsyncStore<T> implements ReadonlyAsyncStore<T> {
|
|
49
51
|
private _isAvailable;
|
|
50
52
|
private _mostRecentOkValue;
|
package/dist/utils/stores.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { ReadWriteLock } from "./locks";
|
|
2
|
+
import { pending, rejected, resolved } from "./promises";
|
|
1
3
|
import { AsyncResult, Result } from "./results";
|
|
2
4
|
import { generateUuid } from "./uuids";
|
|
3
|
-
import { pending, rejected, resolved } from "./promises";
|
|
4
5
|
export class Store {
|
|
5
6
|
constructor(_value) {
|
|
6
7
|
this._value = _value;
|
|
@@ -36,6 +37,7 @@ export class Store {
|
|
|
36
37
|
return { unsubscribe };
|
|
37
38
|
}
|
|
38
39
|
}
|
|
40
|
+
export const storeLock = new ReadWriteLock();
|
|
39
41
|
export class AsyncStore {
|
|
40
42
|
constructor(...args) {
|
|
41
43
|
this._mostRecentOkValue = undefined;
|
|
@@ -135,9 +137,11 @@ export class AsyncStore {
|
|
|
135
137
|
return value;
|
|
136
138
|
}
|
|
137
139
|
async setAsync(promise) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
return await storeLock.withReadLock(async () => {
|
|
141
|
+
const curCounter = ++this._updateCounter;
|
|
142
|
+
const result = await Result.fromPromise(promise);
|
|
143
|
+
return this._setIfLatest(result, curCounter);
|
|
144
|
+
});
|
|
141
145
|
}
|
|
142
146
|
setUnavailable() {
|
|
143
147
|
this._lastSuccessfulUpdate = ++this._updateCounter;
|
package/dist/utils/strings.d.ts
CHANGED
|
@@ -41,8 +41,6 @@ export declare function deindent(code: string): string;
|
|
|
41
41
|
export declare function deindent(strings: TemplateStringsArray | readonly string[], ...values: any[]): string;
|
|
42
42
|
export declare function extractScopes(scope: string, removeDuplicates?: boolean): string[];
|
|
43
43
|
export declare function mergeScopeStrings(...scopes: string[]): string;
|
|
44
|
-
export declare function snakeCaseToCamelCase(snakeCase: string): string;
|
|
45
|
-
export declare function camelCaseToSnakeCase(camelCase: string): string;
|
|
46
44
|
export type Nicifiable = {
|
|
47
45
|
getNicifiableKeys?(): PropertyKey[];
|
|
48
46
|
getNicifiedObjectExtraLines?(): string[];
|
package/dist/utils/strings.js
CHANGED
|
@@ -105,16 +105,6 @@ export function mergeScopeStrings(...scopes) {
|
|
|
105
105
|
const allScope = scopes.map((s) => extractScopes(s)).flat().join(" ");
|
|
106
106
|
return extractScopes(allScope).join(" ");
|
|
107
107
|
}
|
|
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
108
|
/**
|
|
119
109
|
* Some classes have different constructor names in different environments (eg. `Headers` is sometimes called `_Headers`,
|
|
120
110
|
* so we create an object of overrides to handle these cases.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getFlagEmoji(twoLetterCountryCode: string): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { StackAssertionError } from "./errors";
|
|
2
|
+
export function getFlagEmoji(twoLetterCountryCode) {
|
|
3
|
+
if (!/^[a-zA-Z][a-zA-Z]$/.test(twoLetterCountryCode))
|
|
4
|
+
throw new StackAssertionError("Country code must be two alphabetical letters");
|
|
5
|
+
const codePoints = twoLetterCountryCode
|
|
6
|
+
.toUpperCase()
|
|
7
|
+
.split('')
|
|
8
|
+
.map(char => 127397 + char.charCodeAt(0));
|
|
9
|
+
return String.fromCodePoint(...codePoints);
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackframe/stack-shared",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"react": ">=18.2 || >=19.0.0-rc.0",
|
|
24
|
-
"react-dom": ">=18.2 || >=19.0.0-rc.0",
|
|
25
23
|
"@types/react": ">=18.2 || >=19.0.0-rc.0",
|
|
26
24
|
"@types/react-dom": ">=18.2 || >=19.0.0-rc.0",
|
|
27
25
|
"next": ">=14.1.0 || >=15.0.0-rc.0",
|
|
26
|
+
"react": ">=18.2 || >=19.0.0-rc.0",
|
|
27
|
+
"react-dom": ">=18.2 || >=19.0.0-rc.0",
|
|
28
28
|
"yup": "^1.4.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependenciesMeta": {
|
|
@@ -43,26 +43,27 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@simplewebauthn/browser": "^11.0.0",
|
|
46
|
+
"async-mutex": "^0.5.0",
|
|
46
47
|
"bcrypt": "^5.1.1",
|
|
47
48
|
"elliptic": "^6.5.7",
|
|
48
|
-
"jose": "^5.2.2",
|
|
49
49
|
"ip-regex": "^5.0.0",
|
|
50
|
+
"jose": "^5.2.2",
|
|
50
51
|
"oauth4webapi": "^2.10.3",
|
|
51
52
|
"semver": "^7.6.3",
|
|
52
53
|
"uuid": "^9.0.1",
|
|
53
|
-
"@stackframe/stack-sc": "2.
|
|
54
|
+
"@stackframe/stack-sc": "2.7.1"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|
|
57
|
+
"@sentry/nextjs": "^8.40.0",
|
|
56
58
|
"@simplewebauthn/types": "^11.0.0",
|
|
57
59
|
"@types/bcrypt": "^5.0.2",
|
|
58
60
|
"@types/elliptic": "^6.4.18",
|
|
59
61
|
"@types/semver": "^7.5.8",
|
|
60
62
|
"@types/uuid": "^9.0.8",
|
|
61
|
-
"
|
|
63
|
+
"next": "^14.1.0",
|
|
62
64
|
"react": "^18.2",
|
|
63
65
|
"react-dom": "^18.2",
|
|
64
|
-
"
|
|
65
|
-
"@sentry/nextjs": "^8.40.0"
|
|
66
|
+
"rimraf": "^5.0.5"
|
|
66
67
|
},
|
|
67
68
|
"scripts": {
|
|
68
69
|
"build": "tsc",
|