@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 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 strictRefreshCookie;
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 strictRefreshCookie;
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
- strictRefreshCookie;
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
- if (this.strictRefreshCookie && !refreshRes.ok && refreshRes.reason === "no_refresh_secret") {
275
- throw new Error("Refresh cookie missing. Provide a cookie-aware fetch implementation for Node/RN.");
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
- strictRefreshCookie;
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
- if (this.strictRefreshCookie && !refreshRes.ok && refreshRes.reason === "no_refresh_secret") {
232
- throw new Error("Refresh cookie missing. Provide a cookie-aware fetch implementation for Node/RN.");
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kedaruma/revlm-client",
3
- "version": "1.0.45",
3
+ "version": "1.0.48",
4
4
  "private": false,
5
5
  "description": "TypeScript client SDK for talking to the Revlm server replacement for MongoDB Realm.",
6
6
  "keywords": [