modal 0.3.24 → 0.3.25
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/dist/index.cjs +138 -19
- package/dist/index.js +138 -19
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -42060,9 +42060,135 @@ function imageBuilderVersion(version) {
|
|
|
42060
42060
|
return version || clientProfile.imageBuilderVersion || "2024.10";
|
|
42061
42061
|
}
|
|
42062
42062
|
|
|
42063
|
+
// src/auth_token_manager.ts
|
|
42064
|
+
var REFRESH_WINDOW = 5 * 60;
|
|
42065
|
+
var DEFAULT_EXPIRY_OFFSET = 20 * 60;
|
|
42066
|
+
var AuthTokenManager = class {
|
|
42067
|
+
client;
|
|
42068
|
+
currentToken = "";
|
|
42069
|
+
tokenExpiry = 0;
|
|
42070
|
+
stopped = false;
|
|
42071
|
+
timeoutId = null;
|
|
42072
|
+
initialTokenPromise = null;
|
|
42073
|
+
constructor(client2) {
|
|
42074
|
+
this.client = client2;
|
|
42075
|
+
}
|
|
42076
|
+
/**
|
|
42077
|
+
* Returns the current cached token.
|
|
42078
|
+
* If the initial token fetch is still in progress, waits for it to complete.
|
|
42079
|
+
*/
|
|
42080
|
+
async getToken() {
|
|
42081
|
+
if (this.initialTokenPromise) {
|
|
42082
|
+
await this.initialTokenPromise;
|
|
42083
|
+
}
|
|
42084
|
+
if (this.currentToken && !this.isExpired()) {
|
|
42085
|
+
return this.currentToken;
|
|
42086
|
+
}
|
|
42087
|
+
throw new Error("No valid auth token available");
|
|
42088
|
+
}
|
|
42089
|
+
/**
|
|
42090
|
+
* Fetches a new auth token from the server and stores it.
|
|
42091
|
+
*/
|
|
42092
|
+
async fetchToken() {
|
|
42093
|
+
const response = await this.client.authTokenGet({});
|
|
42094
|
+
const token = response.token;
|
|
42095
|
+
if (!token) {
|
|
42096
|
+
throw new Error(
|
|
42097
|
+
"Internal error: did not receive auth token from server, please contact Modal support"
|
|
42098
|
+
);
|
|
42099
|
+
}
|
|
42100
|
+
this.currentToken = token;
|
|
42101
|
+
const exp = this.decodeJWT(token);
|
|
42102
|
+
if (exp > 0) {
|
|
42103
|
+
this.tokenExpiry = exp;
|
|
42104
|
+
} else {
|
|
42105
|
+
console.warn("Failed to decode x-modal-auth-token exp field");
|
|
42106
|
+
this.tokenExpiry = Math.floor(Date.now() / 1e3) + DEFAULT_EXPIRY_OFFSET;
|
|
42107
|
+
}
|
|
42108
|
+
}
|
|
42109
|
+
/**
|
|
42110
|
+
* Background loop that refreshes tokens REFRESH_WINDOW seconds before they expire.
|
|
42111
|
+
*/
|
|
42112
|
+
async backgroundRefresh() {
|
|
42113
|
+
while (!this.stopped) {
|
|
42114
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
42115
|
+
const refreshTime = this.tokenExpiry - REFRESH_WINDOW;
|
|
42116
|
+
const delay = Math.max(0, refreshTime - now) * 1e3;
|
|
42117
|
+
await new Promise((resolve) => {
|
|
42118
|
+
this.timeoutId = setTimeout(resolve, delay);
|
|
42119
|
+
this.timeoutId.unref();
|
|
42120
|
+
});
|
|
42121
|
+
if (this.stopped) {
|
|
42122
|
+
return;
|
|
42123
|
+
}
|
|
42124
|
+
try {
|
|
42125
|
+
await this.fetchToken();
|
|
42126
|
+
} catch (error) {
|
|
42127
|
+
console.error("Failed to refresh auth token:", error);
|
|
42128
|
+
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
42129
|
+
}
|
|
42130
|
+
}
|
|
42131
|
+
}
|
|
42132
|
+
/**
|
|
42133
|
+
* Fetches the initial token and starts the refresh loop.
|
|
42134
|
+
* Throws an error if the initial token fetch fails.
|
|
42135
|
+
*/
|
|
42136
|
+
async start() {
|
|
42137
|
+
this.initialTokenPromise = this.fetchToken();
|
|
42138
|
+
try {
|
|
42139
|
+
await this.initialTokenPromise;
|
|
42140
|
+
} finally {
|
|
42141
|
+
this.initialTokenPromise = null;
|
|
42142
|
+
}
|
|
42143
|
+
this.stopped = false;
|
|
42144
|
+
this.backgroundRefresh();
|
|
42145
|
+
}
|
|
42146
|
+
/**
|
|
42147
|
+
* Stops the background refresh.
|
|
42148
|
+
*/
|
|
42149
|
+
stop() {
|
|
42150
|
+
this.stopped = true;
|
|
42151
|
+
if (this.timeoutId) {
|
|
42152
|
+
clearTimeout(this.timeoutId);
|
|
42153
|
+
this.timeoutId = null;
|
|
42154
|
+
}
|
|
42155
|
+
}
|
|
42156
|
+
/**
|
|
42157
|
+
* Extracts the exp claim from a JWT token.
|
|
42158
|
+
*/
|
|
42159
|
+
decodeJWT(token) {
|
|
42160
|
+
try {
|
|
42161
|
+
const parts = token.split(".");
|
|
42162
|
+
if (parts.length !== 3) {
|
|
42163
|
+
return 0;
|
|
42164
|
+
}
|
|
42165
|
+
let payload = parts[1];
|
|
42166
|
+
while (payload.length % 4 !== 0) {
|
|
42167
|
+
payload += "=";
|
|
42168
|
+
}
|
|
42169
|
+
const decoded = atob(payload);
|
|
42170
|
+
const claims = JSON.parse(decoded);
|
|
42171
|
+
return claims.exp || 0;
|
|
42172
|
+
} catch {
|
|
42173
|
+
return 0;
|
|
42174
|
+
}
|
|
42175
|
+
}
|
|
42176
|
+
isExpired() {
|
|
42177
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
42178
|
+
return now >= this.tokenExpiry;
|
|
42179
|
+
}
|
|
42180
|
+
getCurrentToken() {
|
|
42181
|
+
return this.currentToken;
|
|
42182
|
+
}
|
|
42183
|
+
setToken(token, expiry) {
|
|
42184
|
+
this.currentToken = token;
|
|
42185
|
+
this.tokenExpiry = expiry;
|
|
42186
|
+
}
|
|
42187
|
+
};
|
|
42188
|
+
|
|
42063
42189
|
// src/client.ts
|
|
42064
42190
|
var defaultProfile = getProfile(process.env["MODAL_PROFILE"]);
|
|
42065
|
-
var
|
|
42191
|
+
var authTokenManager = null;
|
|
42066
42192
|
function authMiddleware(profile) {
|
|
42067
42193
|
return async function* authMiddleware2(call, options) {
|
|
42068
42194
|
if (!profile.tokenId || !profile.tokenSecret) {
|
|
@@ -42079,25 +42205,16 @@ function authMiddleware(profile) {
|
|
|
42079
42205
|
options.metadata.set("x-modal-client-version", "1.0.0");
|
|
42080
42206
|
options.metadata.set("x-modal-token-id", tokenId);
|
|
42081
42207
|
options.metadata.set("x-modal-token-secret", tokenSecret);
|
|
42082
|
-
if (
|
|
42083
|
-
|
|
42084
|
-
|
|
42085
|
-
|
|
42086
|
-
options.onHeader = (header) => {
|
|
42087
|
-
const token = header.get("x-modal-auth-token");
|
|
42088
|
-
if (token) {
|
|
42089
|
-
modalAuthToken = token;
|
|
42208
|
+
if (call.method.path !== "/modal.client.ModalClient/AuthTokenGet") {
|
|
42209
|
+
if (!authTokenManager) {
|
|
42210
|
+
authTokenManager = new AuthTokenManager(client);
|
|
42211
|
+
authTokenManager.start();
|
|
42090
42212
|
}
|
|
42091
|
-
|
|
42092
|
-
};
|
|
42093
|
-
const prevOnTrailer = options.onTrailer;
|
|
42094
|
-
options.onTrailer = (trailer) => {
|
|
42095
|
-
const token = trailer.get("x-modal-auth-token");
|
|
42213
|
+
const token = await authTokenManager.getToken();
|
|
42096
42214
|
if (token) {
|
|
42097
|
-
|
|
42215
|
+
options.metadata.set("x-modal-auth-token", token);
|
|
42098
42216
|
}
|
|
42099
|
-
|
|
42100
|
-
};
|
|
42217
|
+
}
|
|
42101
42218
|
return yield* call.next(call.request, options);
|
|
42102
42219
|
};
|
|
42103
42220
|
}
|
|
@@ -42233,6 +42350,8 @@ function initializeClient(options) {
|
|
|
42233
42350
|
};
|
|
42234
42351
|
clientProfile = mergedProfile;
|
|
42235
42352
|
client = createClient(mergedProfile);
|
|
42353
|
+
authTokenManager = new AuthTokenManager(client);
|
|
42354
|
+
authTokenManager.start();
|
|
42236
42355
|
}
|
|
42237
42356
|
|
|
42238
42357
|
// src/secret.ts
|
|
@@ -43646,7 +43765,7 @@ function encodeValue(val, w, proto) {
|
|
|
43646
43765
|
if (val >= 0 && val <= 255) {
|
|
43647
43766
|
w.byte(75 /* BININT1 */);
|
|
43648
43767
|
w.byte(val);
|
|
43649
|
-
} else if (val >=
|
|
43768
|
+
} else if (val >= 0 && val <= 65535) {
|
|
43650
43769
|
w.byte(77 /* BININT2 */);
|
|
43651
43770
|
w.byte(val & 255);
|
|
43652
43771
|
w.byte(val >> 8 & 255);
|
|
@@ -43781,7 +43900,7 @@ function loads(buf) {
|
|
|
43781
43900
|
case 77 /* BININT2 */: {
|
|
43782
43901
|
const lo = r.byte(), hi = r.byte();
|
|
43783
43902
|
const n = hi << 8 | lo;
|
|
43784
|
-
push(n
|
|
43903
|
+
push(n);
|
|
43785
43904
|
break;
|
|
43786
43905
|
}
|
|
43787
43906
|
case 74 /* BININT4 */: {
|
package/dist/index.js
CHANGED
|
@@ -42006,9 +42006,135 @@ function imageBuilderVersion(version) {
|
|
|
42006
42006
|
return version || clientProfile.imageBuilderVersion || "2024.10";
|
|
42007
42007
|
}
|
|
42008
42008
|
|
|
42009
|
+
// src/auth_token_manager.ts
|
|
42010
|
+
var REFRESH_WINDOW = 5 * 60;
|
|
42011
|
+
var DEFAULT_EXPIRY_OFFSET = 20 * 60;
|
|
42012
|
+
var AuthTokenManager = class {
|
|
42013
|
+
client;
|
|
42014
|
+
currentToken = "";
|
|
42015
|
+
tokenExpiry = 0;
|
|
42016
|
+
stopped = false;
|
|
42017
|
+
timeoutId = null;
|
|
42018
|
+
initialTokenPromise = null;
|
|
42019
|
+
constructor(client2) {
|
|
42020
|
+
this.client = client2;
|
|
42021
|
+
}
|
|
42022
|
+
/**
|
|
42023
|
+
* Returns the current cached token.
|
|
42024
|
+
* If the initial token fetch is still in progress, waits for it to complete.
|
|
42025
|
+
*/
|
|
42026
|
+
async getToken() {
|
|
42027
|
+
if (this.initialTokenPromise) {
|
|
42028
|
+
await this.initialTokenPromise;
|
|
42029
|
+
}
|
|
42030
|
+
if (this.currentToken && !this.isExpired()) {
|
|
42031
|
+
return this.currentToken;
|
|
42032
|
+
}
|
|
42033
|
+
throw new Error("No valid auth token available");
|
|
42034
|
+
}
|
|
42035
|
+
/**
|
|
42036
|
+
* Fetches a new auth token from the server and stores it.
|
|
42037
|
+
*/
|
|
42038
|
+
async fetchToken() {
|
|
42039
|
+
const response = await this.client.authTokenGet({});
|
|
42040
|
+
const token = response.token;
|
|
42041
|
+
if (!token) {
|
|
42042
|
+
throw new Error(
|
|
42043
|
+
"Internal error: did not receive auth token from server, please contact Modal support"
|
|
42044
|
+
);
|
|
42045
|
+
}
|
|
42046
|
+
this.currentToken = token;
|
|
42047
|
+
const exp = this.decodeJWT(token);
|
|
42048
|
+
if (exp > 0) {
|
|
42049
|
+
this.tokenExpiry = exp;
|
|
42050
|
+
} else {
|
|
42051
|
+
console.warn("Failed to decode x-modal-auth-token exp field");
|
|
42052
|
+
this.tokenExpiry = Math.floor(Date.now() / 1e3) + DEFAULT_EXPIRY_OFFSET;
|
|
42053
|
+
}
|
|
42054
|
+
}
|
|
42055
|
+
/**
|
|
42056
|
+
* Background loop that refreshes tokens REFRESH_WINDOW seconds before they expire.
|
|
42057
|
+
*/
|
|
42058
|
+
async backgroundRefresh() {
|
|
42059
|
+
while (!this.stopped) {
|
|
42060
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
42061
|
+
const refreshTime = this.tokenExpiry - REFRESH_WINDOW;
|
|
42062
|
+
const delay = Math.max(0, refreshTime - now) * 1e3;
|
|
42063
|
+
await new Promise((resolve) => {
|
|
42064
|
+
this.timeoutId = setTimeout(resolve, delay);
|
|
42065
|
+
this.timeoutId.unref();
|
|
42066
|
+
});
|
|
42067
|
+
if (this.stopped) {
|
|
42068
|
+
return;
|
|
42069
|
+
}
|
|
42070
|
+
try {
|
|
42071
|
+
await this.fetchToken();
|
|
42072
|
+
} catch (error) {
|
|
42073
|
+
console.error("Failed to refresh auth token:", error);
|
|
42074
|
+
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
42075
|
+
}
|
|
42076
|
+
}
|
|
42077
|
+
}
|
|
42078
|
+
/**
|
|
42079
|
+
* Fetches the initial token and starts the refresh loop.
|
|
42080
|
+
* Throws an error if the initial token fetch fails.
|
|
42081
|
+
*/
|
|
42082
|
+
async start() {
|
|
42083
|
+
this.initialTokenPromise = this.fetchToken();
|
|
42084
|
+
try {
|
|
42085
|
+
await this.initialTokenPromise;
|
|
42086
|
+
} finally {
|
|
42087
|
+
this.initialTokenPromise = null;
|
|
42088
|
+
}
|
|
42089
|
+
this.stopped = false;
|
|
42090
|
+
this.backgroundRefresh();
|
|
42091
|
+
}
|
|
42092
|
+
/**
|
|
42093
|
+
* Stops the background refresh.
|
|
42094
|
+
*/
|
|
42095
|
+
stop() {
|
|
42096
|
+
this.stopped = true;
|
|
42097
|
+
if (this.timeoutId) {
|
|
42098
|
+
clearTimeout(this.timeoutId);
|
|
42099
|
+
this.timeoutId = null;
|
|
42100
|
+
}
|
|
42101
|
+
}
|
|
42102
|
+
/**
|
|
42103
|
+
* Extracts the exp claim from a JWT token.
|
|
42104
|
+
*/
|
|
42105
|
+
decodeJWT(token) {
|
|
42106
|
+
try {
|
|
42107
|
+
const parts = token.split(".");
|
|
42108
|
+
if (parts.length !== 3) {
|
|
42109
|
+
return 0;
|
|
42110
|
+
}
|
|
42111
|
+
let payload = parts[1];
|
|
42112
|
+
while (payload.length % 4 !== 0) {
|
|
42113
|
+
payload += "=";
|
|
42114
|
+
}
|
|
42115
|
+
const decoded = atob(payload);
|
|
42116
|
+
const claims = JSON.parse(decoded);
|
|
42117
|
+
return claims.exp || 0;
|
|
42118
|
+
} catch {
|
|
42119
|
+
return 0;
|
|
42120
|
+
}
|
|
42121
|
+
}
|
|
42122
|
+
isExpired() {
|
|
42123
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
42124
|
+
return now >= this.tokenExpiry;
|
|
42125
|
+
}
|
|
42126
|
+
getCurrentToken() {
|
|
42127
|
+
return this.currentToken;
|
|
42128
|
+
}
|
|
42129
|
+
setToken(token, expiry) {
|
|
42130
|
+
this.currentToken = token;
|
|
42131
|
+
this.tokenExpiry = expiry;
|
|
42132
|
+
}
|
|
42133
|
+
};
|
|
42134
|
+
|
|
42009
42135
|
// src/client.ts
|
|
42010
42136
|
var defaultProfile = getProfile(process.env["MODAL_PROFILE"]);
|
|
42011
|
-
var
|
|
42137
|
+
var authTokenManager = null;
|
|
42012
42138
|
function authMiddleware(profile) {
|
|
42013
42139
|
return async function* authMiddleware2(call, options) {
|
|
42014
42140
|
if (!profile.tokenId || !profile.tokenSecret) {
|
|
@@ -42025,25 +42151,16 @@ function authMiddleware(profile) {
|
|
|
42025
42151
|
options.metadata.set("x-modal-client-version", "1.0.0");
|
|
42026
42152
|
options.metadata.set("x-modal-token-id", tokenId);
|
|
42027
42153
|
options.metadata.set("x-modal-token-secret", tokenSecret);
|
|
42028
|
-
if (
|
|
42029
|
-
|
|
42030
|
-
|
|
42031
|
-
|
|
42032
|
-
options.onHeader = (header) => {
|
|
42033
|
-
const token = header.get("x-modal-auth-token");
|
|
42034
|
-
if (token) {
|
|
42035
|
-
modalAuthToken = token;
|
|
42154
|
+
if (call.method.path !== "/modal.client.ModalClient/AuthTokenGet") {
|
|
42155
|
+
if (!authTokenManager) {
|
|
42156
|
+
authTokenManager = new AuthTokenManager(client);
|
|
42157
|
+
authTokenManager.start();
|
|
42036
42158
|
}
|
|
42037
|
-
|
|
42038
|
-
};
|
|
42039
|
-
const prevOnTrailer = options.onTrailer;
|
|
42040
|
-
options.onTrailer = (trailer) => {
|
|
42041
|
-
const token = trailer.get("x-modal-auth-token");
|
|
42159
|
+
const token = await authTokenManager.getToken();
|
|
42042
42160
|
if (token) {
|
|
42043
|
-
|
|
42161
|
+
options.metadata.set("x-modal-auth-token", token);
|
|
42044
42162
|
}
|
|
42045
|
-
|
|
42046
|
-
};
|
|
42163
|
+
}
|
|
42047
42164
|
return yield* call.next(call.request, options);
|
|
42048
42165
|
};
|
|
42049
42166
|
}
|
|
@@ -42179,6 +42296,8 @@ function initializeClient(options) {
|
|
|
42179
42296
|
};
|
|
42180
42297
|
clientProfile = mergedProfile;
|
|
42181
42298
|
client = createClient(mergedProfile);
|
|
42299
|
+
authTokenManager = new AuthTokenManager(client);
|
|
42300
|
+
authTokenManager.start();
|
|
42182
42301
|
}
|
|
42183
42302
|
|
|
42184
42303
|
// src/secret.ts
|
|
@@ -43592,7 +43711,7 @@ function encodeValue(val, w, proto) {
|
|
|
43592
43711
|
if (val >= 0 && val <= 255) {
|
|
43593
43712
|
w.byte(75 /* BININT1 */);
|
|
43594
43713
|
w.byte(val);
|
|
43595
|
-
} else if (val >=
|
|
43714
|
+
} else if (val >= 0 && val <= 65535) {
|
|
43596
43715
|
w.byte(77 /* BININT2 */);
|
|
43597
43716
|
w.byte(val & 255);
|
|
43598
43717
|
w.byte(val >> 8 & 255);
|
|
@@ -43727,7 +43846,7 @@ function loads(buf) {
|
|
|
43727
43846
|
case 77 /* BININT2 */: {
|
|
43728
43847
|
const lo = r.byte(), hi = r.byte();
|
|
43729
43848
|
const n = hi << 8 | lo;
|
|
43730
|
-
push(n
|
|
43849
|
+
push(n);
|
|
43731
43850
|
break;
|
|
43732
43851
|
}
|
|
43733
43852
|
case 74 /* BININT4 */: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modal",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.25",
|
|
4
4
|
"description": "Modal client library for JavaScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://modal.com/docs",
|
|
@@ -46,11 +46,13 @@
|
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@eslint/js": "^9.28.0",
|
|
49
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
49
50
|
"@types/node": "^22.15.2",
|
|
50
51
|
"eslint": "^9.28.0",
|
|
51
52
|
"globals": "^16.2.0",
|
|
52
53
|
"grpc-tools": "^1.13.0",
|
|
53
54
|
"http-server": "^14.1.1",
|
|
55
|
+
"jsonwebtoken": "^9.0.2",
|
|
54
56
|
"p-queue": "^8.1.0",
|
|
55
57
|
"prettier": "^3.5.3",
|
|
56
58
|
"ts-proto": "^2.7.0",
|