@cubist-labs/cubesigner-sdk 0.2.28 → 0.3.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/README.md +30 -28
- package/dist/cjs/package.json +41 -0
- package/dist/cjs/spec/env/beta.json +9 -0
- package/dist/cjs/spec/env/gamma.json +9 -0
- package/dist/cjs/spec/env/prod.json +9 -0
- package/dist/cjs/src/api.d.ts +634 -0
- package/dist/cjs/src/api.js +1309 -0
- package/dist/cjs/src/client.d.ts +575 -0
- package/dist/cjs/src/client.js +378 -0
- package/dist/cjs/src/env.d.ts +15 -0
- package/dist/cjs/src/env.js +35 -0
- package/dist/cjs/src/error.d.ts +29 -0
- package/dist/cjs/src/error.js +36 -0
- package/dist/cjs/src/events.d.ts +84 -0
- package/dist/cjs/src/events.js +195 -0
- package/dist/cjs/src/index.d.ts +203 -0
- package/dist/cjs/src/index.js +298 -0
- package/dist/cjs/src/key.d.ts +152 -0
- package/dist/cjs/src/key.js +242 -0
- package/dist/{src/fido.d.ts → cjs/src/mfa.d.ts} +33 -15
- package/dist/cjs/src/mfa.js +169 -0
- package/dist/cjs/src/org.d.ts +99 -0
- package/dist/cjs/src/org.js +95 -0
- package/dist/cjs/src/paginator.d.ts +76 -0
- package/dist/cjs/src/paginator.js +99 -0
- package/dist/cjs/src/response.d.ts +101 -0
- package/dist/cjs/src/response.js +164 -0
- package/dist/cjs/src/role.d.ts +283 -0
- package/dist/cjs/src/role.js +253 -0
- package/dist/cjs/src/schema.d.ts +6209 -0
- package/dist/cjs/src/schema.js +7 -0
- package/dist/cjs/src/schema_types.d.ts +113 -0
- package/dist/cjs/src/schema_types.js +3 -0
- package/dist/cjs/src/session/session_storage.d.ts +27 -0
- package/dist/cjs/src/session/session_storage.js +47 -0
- package/dist/cjs/src/session/signer_session_manager.d.ts +125 -0
- package/dist/cjs/src/session/signer_session_manager.js +239 -0
- package/dist/cjs/src/signer_session.d.ts +41 -0
- package/dist/cjs/src/signer_session.js +77 -0
- package/dist/cjs/src/user_export.d.ts +52 -0
- package/dist/cjs/src/user_export.js +129 -0
- package/dist/cjs/src/util.d.ts +56 -0
- package/dist/cjs/src/util.js +86 -0
- package/dist/esm/package.json +41 -0
- package/dist/esm/spec/env/beta.json +9 -0
- package/dist/esm/spec/env/gamma.json +9 -0
- package/dist/esm/spec/env/prod.json +9 -0
- package/dist/esm/src/api.d.ts +634 -0
- package/dist/esm/src/api.js +1299 -0
- package/dist/esm/src/client.d.ts +575 -0
- package/dist/esm/src/client.js +374 -0
- package/dist/esm/src/env.d.ts +15 -0
- package/dist/esm/src/env.js +9 -0
- package/dist/esm/src/error.d.ts +29 -0
- package/dist/esm/src/error.js +31 -0
- package/dist/esm/src/events.d.ts +84 -0
- package/dist/esm/src/events.js +189 -0
- package/dist/esm/src/index.d.ts +203 -0
- package/dist/esm/src/index.js +276 -0
- package/dist/esm/src/key.d.ts +152 -0
- package/dist/esm/src/key.js +236 -0
- package/dist/esm/src/mfa.d.ts +94 -0
- package/dist/esm/src/mfa.js +163 -0
- package/dist/esm/src/org.d.ts +99 -0
- package/dist/esm/src/org.js +91 -0
- package/dist/esm/src/paginator.d.ts +76 -0
- package/dist/esm/src/paginator.js +94 -0
- package/dist/esm/src/response.d.ts +101 -0
- package/dist/esm/src/response.js +159 -0
- package/dist/esm/src/role.d.ts +283 -0
- package/dist/esm/src/role.js +248 -0
- package/dist/esm/src/schema.d.ts +6209 -0
- package/dist/esm/src/schema.js +6 -0
- package/dist/esm/src/schema_types.d.ts +113 -0
- package/dist/esm/src/schema_types.js +2 -0
- package/dist/esm/src/session/session_storage.d.ts +27 -0
- package/dist/esm/src/session/session_storage.js +43 -0
- package/dist/esm/src/session/signer_session_manager.d.ts +125 -0
- package/dist/esm/src/session/signer_session_manager.js +235 -0
- package/dist/esm/src/signer_session.d.ts +41 -0
- package/dist/esm/src/signer_session.js +72 -0
- package/dist/esm/src/user_export.d.ts +52 -0
- package/dist/esm/src/user_export.js +99 -0
- package/dist/esm/src/util.d.ts +56 -0
- package/dist/esm/src/util.js +76 -0
- package/dist/package.json +13 -45
- package/dist/src/api.d.ts +29 -1
- package/dist/src/api.js +66 -1
- package/dist/src/client.d.ts +35 -14
- package/dist/src/client.js +12 -8
- package/dist/src/events.js +1 -1
- package/dist/src/index.d.ts +6 -11
- package/dist/src/index.js +9 -25
- package/dist/src/key.d.ts +18 -7
- package/dist/src/key.js +52 -19
- package/dist/src/role.d.ts +46 -3
- package/dist/src/role.js +60 -8
- package/dist/src/schema.d.ts +206 -72
- package/dist/src/schema.js +1 -1
- package/dist/src/schema_types.d.ts +3 -0
- package/dist/src/schema_types.js +1 -1
- package/dist/src/session/signer_session_manager.d.ts +38 -14
- package/dist/src/session/signer_session_manager.js +93 -33
- package/dist/src/util.d.ts +14 -0
- package/dist/src/util.js +24 -27
- package/package.json +19 -46
- package/src/api.ts +79 -0
- package/src/client.ts +12 -8
- package/src/events.ts +2 -0
- package/src/index.ts +10 -24
- package/src/key.ts +36 -18
- package/src/role.ts +78 -7
- package/src/schema.ts +269 -110
- package/src/schema_types.ts +3 -0
- package/src/session/session_storage.ts +0 -32
- package/src/session/signer_session_manager.ts +124 -36
- package/src/util.ts +19 -10
- package/tsconfig.json +1 -21
- package/LICENSE-APACHE +0 -177
- package/LICENSE-MIT +0 -25
- package/NOTICE +0 -13
- package/dist/examples/ethers.d.ts +0 -1
- package/dist/examples/ethers.js +0 -142
- package/dist/src/ethers/index.d.ts +0 -95
- package/dist/src/ethers/index.js +0 -215
- package/dist/src/fido.js +0 -148
- package/dist/src/session/cognito_manager.d.ts +0 -71
- package/dist/src/session/cognito_manager.js +0 -129
- package/dist/src/session/generic.d.ts +0 -47
- package/dist/src/session/generic.js +0 -3
- package/dist/src/session/management_session_manager.d.ts +0 -59
- package/dist/src/session/management_session_manager.js +0 -111
- package/dist/src/session/oidc_session_manager.d.ts +0 -78
- package/dist/src/session/oidc_session_manager.js +0 -142
- package/dist/src/session/session_manager.d.ts +0 -99
- package/dist/src/session/session_manager.js +0 -136
- package/dist/src/sign.d.ts +0 -114
- package/dist/src/sign.js +0 -248
- package/dist/test/sessions.d.ts +0 -35
- package/dist/test/sessions.js +0 -56
- package/src/ethers/index.ts +0 -253
- package/src/session/cognito_manager.ts +0 -161
- package/src/session/session_manager.ts +0 -165
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { Client } from "../api";
|
|
3
|
-
import { EnvInterface } from "../env";
|
|
4
|
-
import { HasEnv, OrgSessionManager, SessionManager } from "./session_manager";
|
|
5
|
-
import { JsonFileSessionStorage, SessionStorage } from "./session_storage";
|
|
6
|
-
import { configDir } from "../util";
|
|
7
|
-
|
|
8
|
-
/** JSON representation of our "management session" file format */
|
|
9
|
-
export interface CognitoSessionObject {
|
|
10
|
-
/** The organization ID */
|
|
11
|
-
org_id: string;
|
|
12
|
-
/** The email address of the user */
|
|
13
|
-
email: string;
|
|
14
|
-
/** The ID token */
|
|
15
|
-
id_token: string;
|
|
16
|
-
/** The access token */
|
|
17
|
-
access_token: string;
|
|
18
|
-
/** The refresh token */
|
|
19
|
-
refresh_token: string;
|
|
20
|
-
/** The expiration time of the access token */
|
|
21
|
-
expiration: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface CognitoSessionInfo extends CognitoSessionObject, HasEnv {}
|
|
25
|
-
|
|
26
|
-
/** Type of storage required for cognito (management) sessions */
|
|
27
|
-
export type CognitoSessionStorage = SessionStorage<CognitoSessionInfo>;
|
|
28
|
-
|
|
29
|
-
/** The session manager for cognito (management) sessions */
|
|
30
|
-
export class CognitoSessionManager extends OrgSessionManager<CognitoSessionInfo> {
|
|
31
|
-
#client: Client;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @return {string} The current auth token.
|
|
35
|
-
* @internal
|
|
36
|
-
*/
|
|
37
|
-
async token(): Promise<string> {
|
|
38
|
-
const session = await this.storage.retrieve();
|
|
39
|
-
return session.id_token;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Returns a client with the current session and refreshes the current
|
|
44
|
-
* session.
|
|
45
|
-
*/
|
|
46
|
-
async client(): Promise<Client> {
|
|
47
|
-
this.refreshIfNeeded();
|
|
48
|
-
return this.#client;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Revokes the session. */
|
|
52
|
-
async revoke(): Promise<void> {
|
|
53
|
-
const idp = require("@aws-sdk/client-cognito-identity-provider"); // eslint-disable-line @typescript-eslint/no-var-requires
|
|
54
|
-
const session = await this.storage.retrieve();
|
|
55
|
-
const client = new idp.CognitoIdentityProviderClient({
|
|
56
|
-
region: this.env.Region,
|
|
57
|
-
signer: { sign: async (request: any) => request }, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
58
|
-
});
|
|
59
|
-
const input = {
|
|
60
|
-
Token: session.refresh_token,
|
|
61
|
-
ClientId: this.env.ClientId,
|
|
62
|
-
};
|
|
63
|
-
await client.send(new idp.RevokeTokenCommand(input));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Returns whether it's time to refresh this token.
|
|
68
|
-
* @return {boolean} Whether it's time to refresh this token.
|
|
69
|
-
* @internal
|
|
70
|
-
*/
|
|
71
|
-
async isStale(): Promise<boolean> {
|
|
72
|
-
const session = await this.storage.retrieve();
|
|
73
|
-
return SessionManager.isStale(new Date(session.expiration));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Refreshes the session and **UPDATES/MUTATES** self.
|
|
78
|
-
*/
|
|
79
|
-
async refresh(): Promise<void> {
|
|
80
|
-
const idp = require("@aws-sdk/client-cognito-identity-provider"); // eslint-disable-line @typescript-eslint/no-var-requires
|
|
81
|
-
const session = await this.storage.retrieve();
|
|
82
|
-
const client = new idp.CognitoIdentityProviderClient({ region: this.env.Region });
|
|
83
|
-
const resp = await client.send(
|
|
84
|
-
new idp.InitiateAuthCommand({
|
|
85
|
-
AuthFlow: "REFRESH_TOKEN_AUTH",
|
|
86
|
-
AuthParameters: {
|
|
87
|
-
REFRESH_TOKEN: session.refresh_token,
|
|
88
|
-
},
|
|
89
|
-
ClientId: this.env.ClientId,
|
|
90
|
-
}),
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
!resp.AuthenticationResult ||
|
|
95
|
-
!resp.AuthenticationResult.ExpiresIn ||
|
|
96
|
-
!resp.AuthenticationResult.IdToken
|
|
97
|
-
) {
|
|
98
|
-
throw new Error("Refresh failed");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const expiresInMs = resp.AuthenticationResult.ExpiresIn * 1000;
|
|
102
|
-
const expiration = new Date(new Date().getTime() + expiresInMs).toISOString();
|
|
103
|
-
const idToken = resp.AuthenticationResult.IdToken;
|
|
104
|
-
|
|
105
|
-
await this.storage.save(<CognitoSessionInfo>{
|
|
106
|
-
...session,
|
|
107
|
-
id_token: idToken,
|
|
108
|
-
access_token: resp.AuthenticationResult.AccessToken,
|
|
109
|
-
expiration,
|
|
110
|
-
});
|
|
111
|
-
this.#client = this.createClient(idToken);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Loads an existing cognito (management) session from storage.
|
|
116
|
-
* @param {CognitoSessionStorage} storage The storage back end to use
|
|
117
|
-
* @return {Promise<SingerSession>} New token
|
|
118
|
-
*/
|
|
119
|
-
static async loadFromStorage(storage: CognitoSessionStorage): Promise<CognitoSessionManager> {
|
|
120
|
-
const sessionInfo = await storage.retrieve();
|
|
121
|
-
return new CognitoSessionManager(
|
|
122
|
-
sessionInfo.env["Dev-CubeSignerStack"],
|
|
123
|
-
sessionInfo.org_id,
|
|
124
|
-
sessionInfo.id_token,
|
|
125
|
-
storage,
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Loads an existing management session and creates a Cognito session manager for it.
|
|
131
|
-
*
|
|
132
|
-
* @param {CognitoSessionStorage} storage Optional session storage to load
|
|
133
|
-
* the session from. If not specified, the management session from the config
|
|
134
|
-
* directory will be loaded.
|
|
135
|
-
* @return {Promise<CognitoSessionManager>} Cognito session manager
|
|
136
|
-
*/
|
|
137
|
-
static async loadManagementSession(
|
|
138
|
-
storage?: CognitoSessionStorage,
|
|
139
|
-
): Promise<CognitoSessionManager> {
|
|
140
|
-
return await CognitoSessionManager.loadFromStorage(
|
|
141
|
-
storage ?? new JsonFileSessionStorage(path.join(configDir(), "management-session.json")),
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Constructor.
|
|
147
|
-
* @param {EnvInterface} env The environment of the session
|
|
148
|
-
* @param {string} orgId The id of the org associated with this session
|
|
149
|
-
* @param {string} token The current token of the session
|
|
150
|
-
* @param {CognitoSessionStorage} storage The storage back end to use
|
|
151
|
-
*/
|
|
152
|
-
private constructor(
|
|
153
|
-
env: EnvInterface,
|
|
154
|
-
orgId: string,
|
|
155
|
-
token: string,
|
|
156
|
-
storage: CognitoSessionStorage,
|
|
157
|
-
) {
|
|
158
|
-
super(env, orgId, storage);
|
|
159
|
-
this.#client = this.createClient(token);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { Events } from "../events";
|
|
2
|
-
import { EnvInterface } from "../env";
|
|
3
|
-
import { Client, createHttpClient } from "../api";
|
|
4
|
-
import { SessionStorage } from "./session_storage";
|
|
5
|
-
import { delay } from "../util";
|
|
6
|
-
|
|
7
|
-
const DEFAULT_EXPIRATION_BUFFER_SECS = 30;
|
|
8
|
-
|
|
9
|
-
/** Generic session manager interface. */
|
|
10
|
-
export abstract class SessionManager<U> {
|
|
11
|
-
readonly env: EnvInterface;
|
|
12
|
-
readonly storage: SessionStorage<U>;
|
|
13
|
-
readonly events = new Events();
|
|
14
|
-
#refreshing: boolean = false;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @return {string} The current auth token.
|
|
18
|
-
* @internal
|
|
19
|
-
*/
|
|
20
|
-
abstract token(): Promise<string>;
|
|
21
|
-
|
|
22
|
-
/** Returns a client instance that uses the token. */
|
|
23
|
-
abstract client(): Promise<Client>;
|
|
24
|
-
|
|
25
|
-
/** Revokes the session. */
|
|
26
|
-
abstract revoke(): Promise<void>;
|
|
27
|
-
|
|
28
|
-
/** Refreshes the session. */
|
|
29
|
-
abstract refresh(): Promise<void>;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Returns whether it's time to refresh this token.
|
|
33
|
-
* @return {boolean} Whether it's time to refresh this token.
|
|
34
|
-
* @internal
|
|
35
|
-
*/
|
|
36
|
-
abstract isStale(): Promise<boolean>;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Refreshes the session if it is about to expire.
|
|
40
|
-
* @return {boolean} Whether the session token was refreshed.
|
|
41
|
-
* @internal
|
|
42
|
-
*/
|
|
43
|
-
async refreshIfNeeded(): Promise<boolean> {
|
|
44
|
-
if (await this.isStale()) {
|
|
45
|
-
if (this.#refreshing) {
|
|
46
|
-
// wait until done refreshing
|
|
47
|
-
while (this.#refreshing) {
|
|
48
|
-
await delay(100);
|
|
49
|
-
}
|
|
50
|
-
return false;
|
|
51
|
-
} else {
|
|
52
|
-
// refresh
|
|
53
|
-
this.#refreshing = true;
|
|
54
|
-
try {
|
|
55
|
-
await this.refresh();
|
|
56
|
-
return true;
|
|
57
|
-
} finally {
|
|
58
|
-
this.#refreshing = false;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Automatically refreshes the session in the background.
|
|
68
|
-
* The default implementation refreshes (if needed) every minute.
|
|
69
|
-
* Base implementations can, instead use the token expirations timestamps
|
|
70
|
-
* to refresh less often. This is a simple wrapper around `setInterval`.
|
|
71
|
-
* @return {number} The interval ID of the refresh timer.
|
|
72
|
-
*/
|
|
73
|
-
autoRefresh(): RefreshId {
|
|
74
|
-
return setInterval(async () => {
|
|
75
|
-
await this.refreshIfNeeded();
|
|
76
|
-
}, 60 * 1000);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Clears the auto refresh timer.
|
|
81
|
-
* @param {number} timer The timer ID to clear.
|
|
82
|
-
*/
|
|
83
|
-
clearAutoRefresh(timer: RefreshId): void {
|
|
84
|
-
clearInterval(timer);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Constructor.
|
|
89
|
-
* @param {EnvInterface} env The environment of the session
|
|
90
|
-
* @param {SessionStorage<U>} storage The storage back end to use for storing
|
|
91
|
-
* session information
|
|
92
|
-
*/
|
|
93
|
-
constructor(env: EnvInterface, storage: SessionStorage<U>) {
|
|
94
|
-
this.env = env;
|
|
95
|
-
this.storage = storage;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Creates a new REST client with a given token
|
|
100
|
-
* @param {string} token The authorization token to use for the client
|
|
101
|
-
* @return {Client} The new REST client
|
|
102
|
-
*/
|
|
103
|
-
protected createClient(token: string): Client {
|
|
104
|
-
return createHttpClient(this.env.SignerApiRoot, token);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Check if a timestamp is within {@link bufferSeconds} seconds from expiration.
|
|
109
|
-
* @param {Date} exp The timestamp to check
|
|
110
|
-
* @param {number} bufferSeconds Time buffer in seconds (defaults to 0s)
|
|
111
|
-
* @return {boolean} True if the timestamp has expired
|
|
112
|
-
*/
|
|
113
|
-
protected static hasExpired(exp: Date, bufferSeconds?: number): boolean {
|
|
114
|
-
bufferSeconds ??= 0;
|
|
115
|
-
const expMsSinceEpoch = exp.getTime();
|
|
116
|
-
const nowMsSinceEpoch = new Date().getTime();
|
|
117
|
-
const bufferMs = bufferSeconds * 1000;
|
|
118
|
-
return expMsSinceEpoch < nowMsSinceEpoch + bufferMs;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Check if a timestamp is stale, i.e., it's within {@link bufferSeconds} seconds from expiration.
|
|
123
|
-
* @param {Date} exp The timestamp to check
|
|
124
|
-
* @param {number} bufferSeconds Time buffer in seconds (defaults to 30s)
|
|
125
|
-
* @return {boolean} True if the timestamp is stale
|
|
126
|
-
*/
|
|
127
|
-
protected static isStale(exp: Date, bufferSeconds?: number): boolean {
|
|
128
|
-
return this.hasExpired(exp, bufferSeconds ?? DEFAULT_EXPIRATION_BUFFER_SECS);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Throws an error that says that some feature is unsupported.
|
|
133
|
-
* @param {string} name The name of the feature that is not supported
|
|
134
|
-
*/
|
|
135
|
-
protected unsupported(name: string): never {
|
|
136
|
-
throw new Error(`'${name}' not supported`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** Interface for a session manager that knows about the org that the session is in. */
|
|
141
|
-
export abstract class OrgSessionManager<U> extends SessionManager<U> {
|
|
142
|
-
readonly orgId: string;
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Constructor.
|
|
146
|
-
* @param {EnvInterface} env The environment of the session
|
|
147
|
-
* @param {string} orgId The id of the org associated with this session
|
|
148
|
-
* @param {SessionStorage<U>} storage The storage back end to use for storing
|
|
149
|
-
* session information
|
|
150
|
-
*/
|
|
151
|
-
constructor(env: EnvInterface, orgId: string, storage: SessionStorage<U>) {
|
|
152
|
-
super(env, storage);
|
|
153
|
-
this.orgId = orgId;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export interface HasEnv {
|
|
158
|
-
/** The environment */
|
|
159
|
-
env: {
|
|
160
|
-
["Dev-CubeSignerStack"]: EnvInterface;
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** Type of the refresh timer ID. */
|
|
165
|
-
export type RefreshId = ReturnType<typeof setInterval>;
|