@kedaruma/revlm-client 1.0.45 → 1.0.48
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.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +86 -5
- package/dist/index.mjs +86 -5
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -181,7 +181,6 @@ type RevlmOptions = {
|
|
|
181
181
|
provisionalAuthDomain?: string;
|
|
182
182
|
autoSetToken?: boolean;
|
|
183
183
|
autoRefreshOn401?: boolean;
|
|
184
|
-
strictRefreshCookie?: boolean;
|
|
185
184
|
};
|
|
186
185
|
type RevlmResponse<T = any> = {
|
|
187
186
|
ok: boolean;
|
|
@@ -201,7 +200,7 @@ declare class Revlm {
|
|
|
201
200
|
private provisionalAuthDomain;
|
|
202
201
|
private autoSetToken;
|
|
203
202
|
private autoRefreshOn401;
|
|
204
|
-
private
|
|
203
|
+
private cookieCheckPromise?;
|
|
205
204
|
constructor(baseUrl: string, opts?: RevlmOptions);
|
|
206
205
|
setToken(token: string): void;
|
|
207
206
|
getToken(): string | undefined;
|
|
@@ -213,6 +212,9 @@ declare class Revlm {
|
|
|
213
212
|
private parseResponse;
|
|
214
213
|
private request;
|
|
215
214
|
private shouldSkipAuthRetry;
|
|
215
|
+
private shouldSkipCookieCheck;
|
|
216
|
+
private decodeJwtPayload;
|
|
217
|
+
private logTokenTtl;
|
|
216
218
|
private signIfNeeded;
|
|
217
219
|
private requestWithRetry;
|
|
218
220
|
login(authId: string, password: string): Promise<LoginResponse>;
|
|
@@ -224,6 +226,7 @@ declare class Revlm {
|
|
|
224
226
|
}): Promise<RevlmResponse<any>>;
|
|
225
227
|
revlmGate(payload: any): Promise<RevlmResponse<any>>;
|
|
226
228
|
db(dbName: string): RevlmDBDatabase;
|
|
229
|
+
private ensureCookieSupport;
|
|
227
230
|
}
|
|
228
231
|
|
|
229
232
|
declare class MongoDBService {
|
package/dist/index.d.ts
CHANGED
|
@@ -181,7 +181,6 @@ type RevlmOptions = {
|
|
|
181
181
|
provisionalAuthDomain?: string;
|
|
182
182
|
autoSetToken?: boolean;
|
|
183
183
|
autoRefreshOn401?: boolean;
|
|
184
|
-
strictRefreshCookie?: boolean;
|
|
185
184
|
};
|
|
186
185
|
type RevlmResponse<T = any> = {
|
|
187
186
|
ok: boolean;
|
|
@@ -201,7 +200,7 @@ declare class Revlm {
|
|
|
201
200
|
private provisionalAuthDomain;
|
|
202
201
|
private autoSetToken;
|
|
203
202
|
private autoRefreshOn401;
|
|
204
|
-
private
|
|
203
|
+
private cookieCheckPromise?;
|
|
205
204
|
constructor(baseUrl: string, opts?: RevlmOptions);
|
|
206
205
|
setToken(token: string): void;
|
|
207
206
|
getToken(): string | undefined;
|
|
@@ -213,6 +212,9 @@ declare class Revlm {
|
|
|
213
212
|
private parseResponse;
|
|
214
213
|
private request;
|
|
215
214
|
private shouldSkipAuthRetry;
|
|
215
|
+
private shouldSkipCookieCheck;
|
|
216
|
+
private decodeJwtPayload;
|
|
217
|
+
private logTokenTtl;
|
|
216
218
|
private signIfNeeded;
|
|
217
219
|
private requestWithRetry;
|
|
218
220
|
login(authId: string, password: string): Promise<LoginResponse>;
|
|
@@ -224,6 +226,7 @@ declare class Revlm {
|
|
|
224
226
|
}): Promise<RevlmResponse<any>>;
|
|
225
227
|
revlmGate(payload: any): Promise<RevlmResponse<any>>;
|
|
226
228
|
db(dbName: string): RevlmDBDatabase;
|
|
229
|
+
private ensureCookieSupport;
|
|
227
230
|
}
|
|
228
231
|
|
|
229
232
|
declare class MongoDBService {
|
package/dist/index.js
CHANGED
|
@@ -156,7 +156,7 @@ var Revlm = class {
|
|
|
156
156
|
provisionalAuthDomain;
|
|
157
157
|
autoSetToken;
|
|
158
158
|
autoRefreshOn401;
|
|
159
|
-
|
|
159
|
+
cookieCheckPromise;
|
|
160
160
|
constructor(baseUrl, opts = {}) {
|
|
161
161
|
if (!baseUrl) throw new Error("baseUrl is required");
|
|
162
162
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
@@ -167,7 +167,6 @@ var Revlm = class {
|
|
|
167
167
|
this.provisionalAuthDomain = opts.provisionalAuthDomain || "";
|
|
168
168
|
this.autoSetToken = opts.autoSetToken ?? true;
|
|
169
169
|
this.autoRefreshOn401 = opts.autoRefreshOn401 || false;
|
|
170
|
-
this.strictRefreshCookie = opts.strictRefreshCookie || false;
|
|
171
170
|
if (!this.fetchImpl) {
|
|
172
171
|
throw new Error("No fetch implementation available. Provide fetchImpl in options or run in Node 18+ with global fetch.");
|
|
173
172
|
}
|
|
@@ -237,11 +236,55 @@ var Revlm = class {
|
|
|
237
236
|
const pathname = path.startsWith("http") ? new URL(path).pathname : path;
|
|
238
237
|
return pathname.includes("/login") || pathname.includes("/provisional-login") || pathname.includes("/refresh-token") || pathname.includes("/verify-token");
|
|
239
238
|
}
|
|
239
|
+
shouldSkipCookieCheck(path) {
|
|
240
|
+
const pathname = path.startsWith("http") ? new URL(path).pathname : path;
|
|
241
|
+
return pathname.includes("/cookie-check");
|
|
242
|
+
}
|
|
243
|
+
decodeJwtPayload(token) {
|
|
244
|
+
if (!token) return null;
|
|
245
|
+
const parts = token.split(".");
|
|
246
|
+
const payloadPart = parts[1];
|
|
247
|
+
if (!payloadPart) return null;
|
|
248
|
+
const raw = payloadPart.replace(/-/g, "+").replace(/_/g, "/");
|
|
249
|
+
const pad = raw.length % 4 ? "=".repeat(4 - raw.length % 4) : "";
|
|
250
|
+
const base64 = raw + pad;
|
|
251
|
+
let jsonText = null;
|
|
252
|
+
if (typeof atob === "function") {
|
|
253
|
+
jsonText = atob(base64);
|
|
254
|
+
} else if (typeof Buffer !== "undefined") {
|
|
255
|
+
jsonText = Buffer.from(base64, "base64").toString("utf8");
|
|
256
|
+
}
|
|
257
|
+
if (!jsonText) return null;
|
|
258
|
+
try {
|
|
259
|
+
const payload = JSON.parse(jsonText);
|
|
260
|
+
return { exp: payload?.exp, iat: payload?.iat };
|
|
261
|
+
} catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
logTokenTtl(event, path, tokenOverride) {
|
|
266
|
+
const token = tokenOverride || this._token;
|
|
267
|
+
if (!token) return;
|
|
268
|
+
const payload = this.decodeJwtPayload(token);
|
|
269
|
+
if (!payload || typeof payload.exp !== "number") return;
|
|
270
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
271
|
+
const ttlSec = payload.exp - now;
|
|
272
|
+
console.log("### token ttl", {
|
|
273
|
+
event,
|
|
274
|
+
path,
|
|
275
|
+
ttlSec,
|
|
276
|
+
exp: payload.exp,
|
|
277
|
+
iat: payload.iat
|
|
278
|
+
});
|
|
279
|
+
}
|
|
240
280
|
async signIfNeeded(_url, _method, headers, _body) {
|
|
241
281
|
return { signedUrl: _url, signedHeaders: headers };
|
|
242
282
|
}
|
|
243
283
|
async requestWithRetry(path, method = "POST", body, opts = { allowAuthRetry: false, retrying: false }) {
|
|
244
284
|
const { allowAuthRetry, retrying } = opts;
|
|
285
|
+
if (!this.shouldSkipCookieCheck(path)) {
|
|
286
|
+
await this.ensureCookieSupport();
|
|
287
|
+
}
|
|
245
288
|
const url = path.startsWith("http") ? path : `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
246
289
|
const hasBody = body !== void 0;
|
|
247
290
|
const headers = this.makeHeaders(hasBody);
|
|
@@ -263,6 +306,7 @@ var Revlm = class {
|
|
|
263
306
|
out.error = parsed?.reason || parsed?.message || "Unknown error";
|
|
264
307
|
}
|
|
265
308
|
if (allowAuthRetry && !retrying && res.status === 401 && !this.shouldSkipAuthRetry(path)) {
|
|
309
|
+
const beforePayload = this.decodeJwtPayload(this._token || "");
|
|
266
310
|
const refreshRes = await this.refreshToken();
|
|
267
311
|
if (!refreshRes.ok) {
|
|
268
312
|
console.warn("### refresh failed:", {
|
|
@@ -270,16 +314,35 @@ var Revlm = class {
|
|
|
270
314
|
status: refreshRes.status,
|
|
271
315
|
error: refreshRes.error
|
|
272
316
|
});
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
317
|
+
if (refreshRes.reason === "no_refresh_secret") {
|
|
318
|
+
const missingError = new Error("Refresh cookie missing. Provide a cookie-aware fetch implementation for Node/RN.");
|
|
319
|
+
missingError.revlmReason = "no_refresh_secret";
|
|
320
|
+
throw missingError;
|
|
321
|
+
}
|
|
276
322
|
}
|
|
277
323
|
if (refreshRes && refreshRes.ok && refreshRes.token) {
|
|
324
|
+
const afterPayload = this.decodeJwtPayload(refreshRes.token);
|
|
325
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
326
|
+
const oldExp = beforePayload?.exp;
|
|
327
|
+
const newExp = afterPayload?.exp;
|
|
328
|
+
console.log("### refresh success", {
|
|
329
|
+
path,
|
|
330
|
+
oldExp,
|
|
331
|
+
newExp,
|
|
332
|
+
oldTtlSec: typeof oldExp === "number" ? oldExp - now : void 0,
|
|
333
|
+
newTtlSec: typeof newExp === "number" ? newExp - now : void 0
|
|
334
|
+
});
|
|
278
335
|
return this.requestWithRetry(path, method, body, { allowAuthRetry: false, retrying: true });
|
|
279
336
|
}
|
|
280
337
|
}
|
|
338
|
+
if (out.ok && !this.shouldSkipCookieCheck(path)) {
|
|
339
|
+
this.logTokenTtl("request_ok", path);
|
|
340
|
+
}
|
|
281
341
|
return out;
|
|
282
342
|
} catch (err) {
|
|
343
|
+
if (err && err.revlmReason === "no_refresh_secret") {
|
|
344
|
+
throw err;
|
|
345
|
+
}
|
|
283
346
|
return { ok: false, error: err?.message || String(err) };
|
|
284
347
|
}
|
|
285
348
|
}
|
|
@@ -295,6 +358,7 @@ var Revlm = class {
|
|
|
295
358
|
if (!this.provisionalEnabled) {
|
|
296
359
|
throw new Error("provisional login is disabled by client configuration");
|
|
297
360
|
}
|
|
361
|
+
await this.ensureCookieSupport();
|
|
298
362
|
if (!authId) throw new Error("authId is required");
|
|
299
363
|
const provisionalClient = new import_revlm_shared.AuthClient({ secretMaster: this.provisionalAuthSecretMaster, authDomain: this.provisionalAuthDomain });
|
|
300
364
|
const provisionalPassword = await provisionalClient.producePassword(String(Date.now() * 1e3));
|
|
@@ -321,6 +385,23 @@ var Revlm = class {
|
|
|
321
385
|
db(dbName) {
|
|
322
386
|
return new RevlmDBDatabase(dbName, this);
|
|
323
387
|
}
|
|
388
|
+
async ensureCookieSupport() {
|
|
389
|
+
if (this.cookieCheckPromise) return this.cookieCheckPromise;
|
|
390
|
+
this.cookieCheckPromise = (async () => {
|
|
391
|
+
const first = await this.requestWithRetry("/cookie-check", "POST", void 0, { allowAuthRetry: false, retrying: false });
|
|
392
|
+
console.log("### cookie check", { step: "first", ok: first.ok, reason: first.reason, status: first.status });
|
|
393
|
+
if (first.ok) return;
|
|
394
|
+
if (first.reason !== "cookie_missing") {
|
|
395
|
+
throw new Error(`Cookie check failed: ${first.reason || first.error || "unknown_error"}`);
|
|
396
|
+
}
|
|
397
|
+
const second = await this.requestWithRetry("/cookie-check", "POST", void 0, { allowAuthRetry: false, retrying: false });
|
|
398
|
+
console.log("### cookie check", { step: "second", ok: second.ok, reason: second.reason, status: second.status });
|
|
399
|
+
if (!second.ok) {
|
|
400
|
+
throw new Error("Cookie support missing. Provide a cookie-aware fetch implementation for Node/RN.");
|
|
401
|
+
}
|
|
402
|
+
})();
|
|
403
|
+
return this.cookieCheckPromise;
|
|
404
|
+
}
|
|
324
405
|
};
|
|
325
406
|
var MongoDBService = class {
|
|
326
407
|
_revlm;
|
package/dist/index.mjs
CHANGED
|
@@ -113,7 +113,7 @@ var Revlm = class {
|
|
|
113
113
|
provisionalAuthDomain;
|
|
114
114
|
autoSetToken;
|
|
115
115
|
autoRefreshOn401;
|
|
116
|
-
|
|
116
|
+
cookieCheckPromise;
|
|
117
117
|
constructor(baseUrl, opts = {}) {
|
|
118
118
|
if (!baseUrl) throw new Error("baseUrl is required");
|
|
119
119
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
@@ -124,7 +124,6 @@ var Revlm = class {
|
|
|
124
124
|
this.provisionalAuthDomain = opts.provisionalAuthDomain || "";
|
|
125
125
|
this.autoSetToken = opts.autoSetToken ?? true;
|
|
126
126
|
this.autoRefreshOn401 = opts.autoRefreshOn401 || false;
|
|
127
|
-
this.strictRefreshCookie = opts.strictRefreshCookie || false;
|
|
128
127
|
if (!this.fetchImpl) {
|
|
129
128
|
throw new Error("No fetch implementation available. Provide fetchImpl in options or run in Node 18+ with global fetch.");
|
|
130
129
|
}
|
|
@@ -194,11 +193,55 @@ var Revlm = class {
|
|
|
194
193
|
const pathname = path.startsWith("http") ? new URL(path).pathname : path;
|
|
195
194
|
return pathname.includes("/login") || pathname.includes("/provisional-login") || pathname.includes("/refresh-token") || pathname.includes("/verify-token");
|
|
196
195
|
}
|
|
196
|
+
shouldSkipCookieCheck(path) {
|
|
197
|
+
const pathname = path.startsWith("http") ? new URL(path).pathname : path;
|
|
198
|
+
return pathname.includes("/cookie-check");
|
|
199
|
+
}
|
|
200
|
+
decodeJwtPayload(token) {
|
|
201
|
+
if (!token) return null;
|
|
202
|
+
const parts = token.split(".");
|
|
203
|
+
const payloadPart = parts[1];
|
|
204
|
+
if (!payloadPart) return null;
|
|
205
|
+
const raw = payloadPart.replace(/-/g, "+").replace(/_/g, "/");
|
|
206
|
+
const pad = raw.length % 4 ? "=".repeat(4 - raw.length % 4) : "";
|
|
207
|
+
const base64 = raw + pad;
|
|
208
|
+
let jsonText = null;
|
|
209
|
+
if (typeof atob === "function") {
|
|
210
|
+
jsonText = atob(base64);
|
|
211
|
+
} else if (typeof Buffer !== "undefined") {
|
|
212
|
+
jsonText = Buffer.from(base64, "base64").toString("utf8");
|
|
213
|
+
}
|
|
214
|
+
if (!jsonText) return null;
|
|
215
|
+
try {
|
|
216
|
+
const payload = JSON.parse(jsonText);
|
|
217
|
+
return { exp: payload?.exp, iat: payload?.iat };
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
logTokenTtl(event, path, tokenOverride) {
|
|
223
|
+
const token = tokenOverride || this._token;
|
|
224
|
+
if (!token) return;
|
|
225
|
+
const payload = this.decodeJwtPayload(token);
|
|
226
|
+
if (!payload || typeof payload.exp !== "number") return;
|
|
227
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
228
|
+
const ttlSec = payload.exp - now;
|
|
229
|
+
console.log("### token ttl", {
|
|
230
|
+
event,
|
|
231
|
+
path,
|
|
232
|
+
ttlSec,
|
|
233
|
+
exp: payload.exp,
|
|
234
|
+
iat: payload.iat
|
|
235
|
+
});
|
|
236
|
+
}
|
|
197
237
|
async signIfNeeded(_url, _method, headers, _body) {
|
|
198
238
|
return { signedUrl: _url, signedHeaders: headers };
|
|
199
239
|
}
|
|
200
240
|
async requestWithRetry(path, method = "POST", body, opts = { allowAuthRetry: false, retrying: false }) {
|
|
201
241
|
const { allowAuthRetry, retrying } = opts;
|
|
242
|
+
if (!this.shouldSkipCookieCheck(path)) {
|
|
243
|
+
await this.ensureCookieSupport();
|
|
244
|
+
}
|
|
202
245
|
const url = path.startsWith("http") ? path : `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
203
246
|
const hasBody = body !== void 0;
|
|
204
247
|
const headers = this.makeHeaders(hasBody);
|
|
@@ -220,6 +263,7 @@ var Revlm = class {
|
|
|
220
263
|
out.error = parsed?.reason || parsed?.message || "Unknown error";
|
|
221
264
|
}
|
|
222
265
|
if (allowAuthRetry && !retrying && res.status === 401 && !this.shouldSkipAuthRetry(path)) {
|
|
266
|
+
const beforePayload = this.decodeJwtPayload(this._token || "");
|
|
223
267
|
const refreshRes = await this.refreshToken();
|
|
224
268
|
if (!refreshRes.ok) {
|
|
225
269
|
console.warn("### refresh failed:", {
|
|
@@ -227,16 +271,35 @@ var Revlm = class {
|
|
|
227
271
|
status: refreshRes.status,
|
|
228
272
|
error: refreshRes.error
|
|
229
273
|
});
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
274
|
+
if (refreshRes.reason === "no_refresh_secret") {
|
|
275
|
+
const missingError = new Error("Refresh cookie missing. Provide a cookie-aware fetch implementation for Node/RN.");
|
|
276
|
+
missingError.revlmReason = "no_refresh_secret";
|
|
277
|
+
throw missingError;
|
|
278
|
+
}
|
|
233
279
|
}
|
|
234
280
|
if (refreshRes && refreshRes.ok && refreshRes.token) {
|
|
281
|
+
const afterPayload = this.decodeJwtPayload(refreshRes.token);
|
|
282
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
283
|
+
const oldExp = beforePayload?.exp;
|
|
284
|
+
const newExp = afterPayload?.exp;
|
|
285
|
+
console.log("### refresh success", {
|
|
286
|
+
path,
|
|
287
|
+
oldExp,
|
|
288
|
+
newExp,
|
|
289
|
+
oldTtlSec: typeof oldExp === "number" ? oldExp - now : void 0,
|
|
290
|
+
newTtlSec: typeof newExp === "number" ? newExp - now : void 0
|
|
291
|
+
});
|
|
235
292
|
return this.requestWithRetry(path, method, body, { allowAuthRetry: false, retrying: true });
|
|
236
293
|
}
|
|
237
294
|
}
|
|
295
|
+
if (out.ok && !this.shouldSkipCookieCheck(path)) {
|
|
296
|
+
this.logTokenTtl("request_ok", path);
|
|
297
|
+
}
|
|
238
298
|
return out;
|
|
239
299
|
} catch (err) {
|
|
300
|
+
if (err && err.revlmReason === "no_refresh_secret") {
|
|
301
|
+
throw err;
|
|
302
|
+
}
|
|
240
303
|
return { ok: false, error: err?.message || String(err) };
|
|
241
304
|
}
|
|
242
305
|
}
|
|
@@ -252,6 +315,7 @@ var Revlm = class {
|
|
|
252
315
|
if (!this.provisionalEnabled) {
|
|
253
316
|
throw new Error("provisional login is disabled by client configuration");
|
|
254
317
|
}
|
|
318
|
+
await this.ensureCookieSupport();
|
|
255
319
|
if (!authId) throw new Error("authId is required");
|
|
256
320
|
const provisionalClient = new AuthClient({ secretMaster: this.provisionalAuthSecretMaster, authDomain: this.provisionalAuthDomain });
|
|
257
321
|
const provisionalPassword = await provisionalClient.producePassword(String(Date.now() * 1e3));
|
|
@@ -278,6 +342,23 @@ var Revlm = class {
|
|
|
278
342
|
db(dbName) {
|
|
279
343
|
return new RevlmDBDatabase(dbName, this);
|
|
280
344
|
}
|
|
345
|
+
async ensureCookieSupport() {
|
|
346
|
+
if (this.cookieCheckPromise) return this.cookieCheckPromise;
|
|
347
|
+
this.cookieCheckPromise = (async () => {
|
|
348
|
+
const first = await this.requestWithRetry("/cookie-check", "POST", void 0, { allowAuthRetry: false, retrying: false });
|
|
349
|
+
console.log("### cookie check", { step: "first", ok: first.ok, reason: first.reason, status: first.status });
|
|
350
|
+
if (first.ok) return;
|
|
351
|
+
if (first.reason !== "cookie_missing") {
|
|
352
|
+
throw new Error(`Cookie check failed: ${first.reason || first.error || "unknown_error"}`);
|
|
353
|
+
}
|
|
354
|
+
const second = await this.requestWithRetry("/cookie-check", "POST", void 0, { allowAuthRetry: false, retrying: false });
|
|
355
|
+
console.log("### cookie check", { step: "second", ok: second.ok, reason: second.reason, status: second.status });
|
|
356
|
+
if (!second.ok) {
|
|
357
|
+
throw new Error("Cookie support missing. Provide a cookie-aware fetch implementation for Node/RN.");
|
|
358
|
+
}
|
|
359
|
+
})();
|
|
360
|
+
return this.cookieCheckPromise;
|
|
361
|
+
}
|
|
281
362
|
};
|
|
282
363
|
var MongoDBService = class {
|
|
283
364
|
_revlm;
|