@stackframe/stack-shared 2.7.7 → 2.7.9
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 +15 -0
- package/dist/known-errors.d.ts +2 -2
- package/dist/known-errors.js +8 -6
- package/dist/schema-fields.d.ts +5 -0
- package/dist/schema-fields.js +10 -0
- package/dist/sessions.d.ts +3 -7
- package/dist/sessions.js +11 -22
- package/dist/utils/booleans.d.ts +4 -0
- package/dist/utils/booleans.js +6 -0
- package/dist/utils/fs.d.ts +4 -0
- package/dist/utils/fs.js +22 -0
- package/dist/utils/numbers.d.ts +1 -0
- package/dist/utils/numbers.js +3 -0
- package/dist/utils/strings.d.ts +1 -0
- package/dist/utils/strings.js +23 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @stackframe/stack-shared
|
|
2
2
|
|
|
3
|
+
## 2.7.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Various changes
|
|
8
|
+
- @stackframe/stack-sc@2.7.9
|
|
9
|
+
|
|
10
|
+
## 2.7.8
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Various changes
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
- @stackframe/stack-sc@2.7.8
|
|
17
|
+
|
|
3
18
|
## 2.7.7
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/known-errors.d.ts
CHANGED
|
@@ -121,7 +121,7 @@ export declare const KnownErrors: {
|
|
|
121
121
|
};
|
|
122
122
|
AdminAccessTokenExpired: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & {
|
|
123
123
|
constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
|
|
124
|
-
} & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"INVALID_ADMIN_ACCESS_TOKEN"> & KnownErrorBrand<"ADMIN_ACCESS_TOKEN_EXPIRED">, []> & {
|
|
124
|
+
} & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION"> & KnownErrorBrand<"INVALID_ADMIN_ACCESS_TOKEN"> & KnownErrorBrand<"ADMIN_ACCESS_TOKEN_EXPIRED">, [expiredAt: Date | undefined]> & {
|
|
125
125
|
errorCode: "ADMIN_ACCESS_TOKEN_EXPIRED";
|
|
126
126
|
};
|
|
127
127
|
InvalidProjectForAdminAccessToken: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & {
|
|
@@ -194,7 +194,7 @@ export declare const KnownErrors: {
|
|
|
194
194
|
};
|
|
195
195
|
AccessTokenExpired: KnownErrorConstructor<KnownError & KnownErrorBrand<"SESSION_AUTHENTICATION_ERROR"> & {
|
|
196
196
|
constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
|
|
197
|
-
} & KnownErrorBrand<"INVALID_SESSION_AUTHENTICATION"> & KnownErrorBrand<"INVALID_ACCESS_TOKEN"> & KnownErrorBrand<"ACCESS_TOKEN_EXPIRED">, []> & {
|
|
197
|
+
} & KnownErrorBrand<"INVALID_SESSION_AUTHENTICATION"> & KnownErrorBrand<"INVALID_ACCESS_TOKEN"> & KnownErrorBrand<"ACCESS_TOKEN_EXPIRED">, [expiredAt: Date | undefined]> & {
|
|
198
198
|
errorCode: "ACCESS_TOKEN_EXPIRED";
|
|
199
199
|
};
|
|
200
200
|
InvalidProjectForAccessToken: KnownErrorConstructor<KnownError & KnownErrorBrand<"SESSION_AUTHENTICATION_ERROR"> & {
|
package/dist/known-errors.js
CHANGED
|
@@ -171,10 +171,11 @@ const UnparsableAdminAccessToken = createKnownErrorConstructor(InvalidAdminAcces
|
|
|
171
171
|
401,
|
|
172
172
|
"Admin access token is not parsable.",
|
|
173
173
|
], () => []);
|
|
174
|
-
const AdminAccessTokenExpired = createKnownErrorConstructor(InvalidAdminAccessToken, "ADMIN_ACCESS_TOKEN_EXPIRED", () => [
|
|
174
|
+
const AdminAccessTokenExpired = createKnownErrorConstructor(InvalidAdminAccessToken, "ADMIN_ACCESS_TOKEN_EXPIRED", (expiredAt) => [
|
|
175
175
|
401,
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
`Admin access token has expired. Please refresh it and try again.${expiredAt ? ` (The access token expired at ${expiredAt.toISOString()}.)` : ""}`,
|
|
177
|
+
{ expired_at_millis: expiredAt?.getTime() ?? null },
|
|
178
|
+
], (json) => [json.expired_at_millis ?? undefined]);
|
|
178
179
|
const InvalidProjectForAdminAccessToken = createKnownErrorConstructor(InvalidAdminAccessToken, "INVALID_PROJECT_FOR_ADMIN_ACCESS_TOKEN", () => [
|
|
179
180
|
401,
|
|
180
181
|
"Admin access tokens must be created on the internal project.",
|
|
@@ -240,10 +241,11 @@ const UnparsableAccessToken = createKnownErrorConstructor(InvalidAccessToken, "U
|
|
|
240
241
|
401,
|
|
241
242
|
"Access token is not parsable.",
|
|
242
243
|
], () => []);
|
|
243
|
-
const AccessTokenExpired = createKnownErrorConstructor(InvalidAccessToken, "ACCESS_TOKEN_EXPIRED", () => [
|
|
244
|
+
const AccessTokenExpired = createKnownErrorConstructor(InvalidAccessToken, "ACCESS_TOKEN_EXPIRED", (expiredAt) => [
|
|
244
245
|
401,
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
`Access token has expired. Please refresh it and try again.${expiredAt ? ` (The access token expired at ${expiredAt.toISOString()}.)` : ""}`,
|
|
247
|
+
{ expired_at_millis: expiredAt?.getTime() ?? null },
|
|
248
|
+
], (json) => [json.expired_at_millis ?? undefined]);
|
|
247
249
|
const InvalidProjectForAccessToken = createKnownErrorConstructor(InvalidAccessToken, "INVALID_PROJECT_FOR_ACCESS_TOKEN", () => [
|
|
248
250
|
401,
|
|
249
251
|
"Access token not valid for this project.",
|
package/dist/schema-fields.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ declare module "yup" {
|
|
|
4
4
|
nonEmpty(message?: string): StringSchema<TType, TContext, TDefault, TFlags>;
|
|
5
5
|
empty(): StringSchema<TType, TContext, TDefault, TFlags>;
|
|
6
6
|
}
|
|
7
|
+
interface Schema<TType, TContext, TDefault, TFlags> {
|
|
8
|
+
getNested<K extends keyof TType>(path: K): yup.Schema<TType[K], TContext, TDefault, TFlags>;
|
|
9
|
+
concat<U extends yup.AnySchema>(schema: U): yup.Schema<Omit<TType, keyof yup.InferType<U>> & yup.InferType<U>, TContext, TDefault, TFlags>;
|
|
10
|
+
}
|
|
7
11
|
}
|
|
8
12
|
export declare function yupValidate<S extends yup.ISchema<any>>(schema: S, obj: unknown, options?: yup.ValidateOptions & {
|
|
9
13
|
currentUserId?: string | null;
|
|
@@ -23,6 +27,7 @@ export declare function yupTuple<T extends [unknown, ...unknown[]]>(...args: Par
|
|
|
23
27
|
export declare function yupObject<A extends yup.Maybe<yup.AnyObject>, B extends yup.ObjectShape>(...args: Parameters<typeof yup.object<A, B>>): yup.ObjectSchema<yup.TypeFromShape<B, yup.AnyObject> extends infer T ? T extends yup.TypeFromShape<B, yup.AnyObject> ? T extends {} ? { [k in keyof T]: T[k]; } : T : never : never, yup.AnyObject, yup.DefaultFromShape<B> extends infer T_1 ? T_1 extends yup.DefaultFromShape<B> ? T_1 extends {} ? { [k_1 in keyof T_1]: T_1[k_1]; } : T_1 : never : never, "">;
|
|
24
28
|
export declare function yupNever(): yup.MixedSchema<never>;
|
|
25
29
|
export declare function yupUnion<T extends yup.ISchema<any>[]>(...args: T): yup.MixedSchema<yup.InferType<T[number]>>;
|
|
30
|
+
export declare function ensureObjectSchema<T extends yup.AnyObject>(schema: yup.Schema<T>): yup.ObjectSchema<T> & typeof schema;
|
|
26
31
|
export declare const adaptSchema: yup.MixedSchema<typeof StackAdaptSentinel | undefined, yup.AnyObject, undefined, "">;
|
|
27
32
|
/**
|
|
28
33
|
* Yup's URL schema does not recognize some URLs (including `http://localhost`) as a valid URL. This schema is a workaround for that.
|
package/dist/schema-fields.js
CHANGED
|
@@ -13,6 +13,11 @@ yup.addMethod(yup.string, "nonEmpty", function (message) {
|
|
|
13
13
|
return value !== "";
|
|
14
14
|
});
|
|
15
15
|
});
|
|
16
|
+
yup.addMethod(yup.Schema, "getNested", function (path) {
|
|
17
|
+
if (!path.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/))
|
|
18
|
+
throw new StackAssertionError(`yupSchema.getNested can currently only be used with alphanumeric keys. Fix this in the future. Provided key: ${path}`);
|
|
19
|
+
return yup.reach(this, path);
|
|
20
|
+
});
|
|
16
21
|
export async function yupValidate(schema, obj, options) {
|
|
17
22
|
try {
|
|
18
23
|
return await schema.validate(obj, {
|
|
@@ -155,6 +160,11 @@ export function yupUnion(...args) {
|
|
|
155
160
|
});
|
|
156
161
|
});
|
|
157
162
|
}
|
|
163
|
+
export function ensureObjectSchema(schema) {
|
|
164
|
+
if (!(schema instanceof yup.ObjectSchema))
|
|
165
|
+
throw new StackAssertionError(`assertObjectSchema: schema is not an ObjectSchema: ${schema.describe().type}`);
|
|
166
|
+
return schema;
|
|
167
|
+
}
|
|
158
168
|
// Common
|
|
159
169
|
export const adaptSchema = yupMixed();
|
|
160
170
|
/**
|
package/dist/sessions.d.ts
CHANGED
|
@@ -73,9 +73,9 @@ export declare class InternalSession {
|
|
|
73
73
|
/**
|
|
74
74
|
* Fetches new tokens that are, at the time of fetching, guaranteed to be valid.
|
|
75
75
|
*
|
|
76
|
-
* The newly generated tokens are
|
|
76
|
+
* The newly generated tokens are short-lived, so it's good practice not to rely on their validity (if possible). However, this function is useful in some cases where you only want to pass access tokens to a service, and you want to make sure said access token has the longest possible lifetime.
|
|
77
77
|
*
|
|
78
|
-
* In most cases, you should prefer `getOrFetchLikelyValidTokens
|
|
78
|
+
* In most cases, you should prefer `getOrFetchLikelyValidTokens`.
|
|
79
79
|
*
|
|
80
80
|
* @returns null if the session is known to be invalid, or new tokens otherwise (which, at the time of fetching, are guaranteed to be valid).
|
|
81
81
|
*/
|
|
@@ -95,11 +95,7 @@ export declare class InternalSession {
|
|
|
95
95
|
*/
|
|
96
96
|
private _getPotentiallyInvalidAccessTokenIfAvailable;
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
*/
|
|
100
|
-
private _getOrFetchPotentiallyInvalidAccessToken;
|
|
101
|
-
/**
|
|
102
|
-
* You should prefer `_getOrFetchAccessToken` in almost all cases.
|
|
98
|
+
* You should prefer `_getOrFetchPotentiallyInvalidAccessToken` in almost all cases.
|
|
103
99
|
*
|
|
104
100
|
* @returns A newly fetched access token (never read from cache), or null if the session either does not represent a user or the session is invalid.
|
|
105
101
|
*/
|
package/dist/sessions.js
CHANGED
|
@@ -10,7 +10,7 @@ export class AccessToken {
|
|
|
10
10
|
}
|
|
11
11
|
get expiresAt() {
|
|
12
12
|
const { exp } = jose.decodeJwt(this.token);
|
|
13
|
-
if (
|
|
13
|
+
if (exp === undefined)
|
|
14
14
|
return new Date(8640000000000000); // max date value
|
|
15
15
|
return new Date(exp * 1000);
|
|
16
16
|
}
|
|
@@ -107,9 +107,9 @@ export class InternalSession {
|
|
|
107
107
|
/**
|
|
108
108
|
* Fetches new tokens that are, at the time of fetching, guaranteed to be valid.
|
|
109
109
|
*
|
|
110
|
-
* The newly generated tokens are
|
|
110
|
+
* The newly generated tokens are short-lived, so it's good practice not to rely on their validity (if possible). However, this function is useful in some cases where you only want to pass access tokens to a service, and you want to make sure said access token has the longest possible lifetime.
|
|
111
111
|
*
|
|
112
|
-
* In most cases, you should prefer `getOrFetchLikelyValidTokens
|
|
112
|
+
* In most cases, you should prefer `getOrFetchLikelyValidTokens`.
|
|
113
113
|
*
|
|
114
114
|
* @returns null if the session is known to be invalid, or new tokens otherwise (which, at the time of fetching, are guaranteed to be valid).
|
|
115
115
|
*/
|
|
@@ -133,30 +133,17 @@ export class InternalSession {
|
|
|
133
133
|
* @returns An access token, which may be expired or expire soon, or null if it is known to be invalid.
|
|
134
134
|
*/
|
|
135
135
|
_getPotentiallyInvalidAccessTokenIfAvailable() {
|
|
136
|
-
const accessToken = this._accessToken.get();
|
|
137
|
-
if (accessToken && !accessToken.isExpired())
|
|
138
|
-
return accessToken;
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* @returns An access token (cached if possible), or null if the session either does not represent a user or the session is invalid.
|
|
143
|
-
*/
|
|
144
|
-
async _getOrFetchPotentiallyInvalidAccessToken() {
|
|
145
136
|
if (!this._refreshToken)
|
|
146
137
|
return null;
|
|
147
138
|
if (this.isKnownToBeInvalid())
|
|
148
139
|
return null;
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
return
|
|
152
|
-
|
|
153
|
-
if (!this._refreshPromise) {
|
|
154
|
-
this._refreshAndSetRefreshPromise(this._refreshToken);
|
|
155
|
-
}
|
|
156
|
-
return await this._refreshPromise;
|
|
140
|
+
const accessToken = this._accessToken.get();
|
|
141
|
+
if (accessToken && !accessToken.isExpired())
|
|
142
|
+
return accessToken;
|
|
143
|
+
return null;
|
|
157
144
|
}
|
|
158
145
|
/**
|
|
159
|
-
* You should prefer `
|
|
146
|
+
* You should prefer `_getOrFetchPotentiallyInvalidAccessToken` in almost all cases.
|
|
160
147
|
*
|
|
161
148
|
* @returns A newly fetched access token (never read from cache), or null if the session either does not represent a user or the session is invalid.
|
|
162
149
|
*/
|
|
@@ -165,7 +152,9 @@ export class InternalSession {
|
|
|
165
152
|
return null;
|
|
166
153
|
if (this._knownToBeInvalid.get())
|
|
167
154
|
return null;
|
|
168
|
-
|
|
155
|
+
if (!this._refreshPromise) {
|
|
156
|
+
this._refreshAndSetRefreshPromise(this._refreshToken);
|
|
157
|
+
}
|
|
169
158
|
return await this._refreshPromise;
|
|
170
159
|
}
|
|
171
160
|
_refreshAndSetRefreshPromise(refreshToken) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type Truthy<T> = T extends null | undefined | 0 | "" | false ? false : true;
|
|
2
|
+
export type Falsy<T> = T extends null | undefined | 0 | "" | false ? true : false;
|
|
3
|
+
export declare function isTruthy<T>(value: T): value is T & Truthy<T>;
|
|
4
|
+
export declare function isFalsy<T>(value: T): value is T & Falsy<T>;
|
package/dist/utils/fs.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as stackFs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
export async function list(path) {
|
|
4
|
+
return await stackFs.promises.readdir(path);
|
|
5
|
+
}
|
|
6
|
+
export async function listRecursively(p, options = {}) {
|
|
7
|
+
const files = await list(p);
|
|
8
|
+
return [
|
|
9
|
+
...(await Promise.all(files.map(async (fileName) => {
|
|
10
|
+
const filePath = path.join(p, fileName);
|
|
11
|
+
if ((await stackFs.promises.stat(filePath)).isDirectory()) {
|
|
12
|
+
return [
|
|
13
|
+
...(await listRecursively(filePath, options)),
|
|
14
|
+
...(options.excludeDirectories ? [] : [filePath]),
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return [filePath];
|
|
19
|
+
}
|
|
20
|
+
}))).flat(),
|
|
21
|
+
];
|
|
22
|
+
}
|
package/dist/utils/numbers.d.ts
CHANGED
package/dist/utils/numbers.js
CHANGED
package/dist/utils/strings.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export declare function deindent(code: string): string;
|
|
|
45
45
|
export declare function deindent(strings: TemplateStringsArray | readonly string[], ...values: any[]): string;
|
|
46
46
|
export declare function extractScopes(scope: string, removeDuplicates?: boolean): string[];
|
|
47
47
|
export declare function mergeScopeStrings(...scopes: string[]): string;
|
|
48
|
+
export declare function escapeTemplateLiteral(s: string): string;
|
|
48
49
|
export type Nicifiable = {
|
|
49
50
|
getNicifiableKeys?(): PropertyKey[];
|
|
50
51
|
getNicifiedObjectExtraLines?(): string[];
|
package/dist/utils/strings.js
CHANGED
|
@@ -103,15 +103,20 @@ export function deindent(strings, ...values) {
|
|
|
103
103
|
return templateIdentity(deindentedStrings, ...indentedValues);
|
|
104
104
|
}
|
|
105
105
|
export function extractScopes(scope, removeDuplicates = true) {
|
|
106
|
+
// TODO what is this for? can we move this into the OAuth code in the backend?
|
|
106
107
|
const trimmedString = scope.trim();
|
|
107
108
|
const scopesArray = trimmedString.split(/\s+/);
|
|
108
109
|
const filtered = scopesArray.filter(scope => scope.length > 0);
|
|
109
110
|
return removeDuplicates ? [...new Set(filtered)] : filtered;
|
|
110
111
|
}
|
|
111
112
|
export function mergeScopeStrings(...scopes) {
|
|
113
|
+
// TODO what is this for? can we move this into the OAuth code in the backend?
|
|
112
114
|
const allScope = scopes.map((s) => extractScopes(s)).flat().join(" ");
|
|
113
115
|
return extractScopes(allScope).join(" ");
|
|
114
116
|
}
|
|
117
|
+
export function escapeTemplateLiteral(s) {
|
|
118
|
+
return s.replaceAll("`", "\\`").replaceAll("\\", "\\\\").replaceAll("$", "\\$");
|
|
119
|
+
}
|
|
115
120
|
/**
|
|
116
121
|
* Some classes have different constructor names in different environments (eg. `Headers` is sometimes called `_Headers`,
|
|
117
122
|
* so we create an object of overrides to handle these cases.
|
|
@@ -165,11 +170,27 @@ export function nicify(value, options = {}) {
|
|
|
165
170
|
});
|
|
166
171
|
};
|
|
167
172
|
switch (typeof value) {
|
|
168
|
-
case "string":
|
|
169
173
|
case "boolean":
|
|
170
174
|
case "number": {
|
|
171
175
|
return JSON.stringify(value);
|
|
172
176
|
}
|
|
177
|
+
case "string": {
|
|
178
|
+
const isDeindentable = (v) => deindent(v) === v && v.includes("\n");
|
|
179
|
+
const wrapInDeindent = (v) => deindent `
|
|
180
|
+
deindent\`
|
|
181
|
+
${currentIndent + lineIndent}${escapeTemplateLiteral(value).replaceAll("\n", nl + lineIndent)}
|
|
182
|
+
${currentIndent}\`
|
|
183
|
+
`;
|
|
184
|
+
if (isDeindentable(value)) {
|
|
185
|
+
return wrapInDeindent(value);
|
|
186
|
+
}
|
|
187
|
+
else if (value.endsWith("\n") && isDeindentable(value.slice(0, -1))) {
|
|
188
|
+
return wrapInDeindent(value.slice(0, -1)) + ' + "\\n"';
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
return JSON.stringify(value);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
173
194
|
case "undefined": {
|
|
174
195
|
return "undefined";
|
|
175
196
|
}
|
|
@@ -205,7 +226,7 @@ export function nicify(value, options = {}) {
|
|
|
205
226
|
}
|
|
206
227
|
}
|
|
207
228
|
if (value instanceof URL) {
|
|
208
|
-
return `URL(${
|
|
229
|
+
return `URL(${nicify(value.toString())})`;
|
|
209
230
|
}
|
|
210
231
|
if (ArrayBuffer.isView(value)) {
|
|
211
232
|
return `${value.constructor.name}([${value.toString()}])`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackframe/stack-shared",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.9",
|
|
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.
|
|
54
|
+
"@stackframe/stack-sc": "2.7.9"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@sentry/nextjs": "^8.40.0",
|