@kedaruma/revlm-client 1.0.10 → 1.0.13
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 +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +69 -4
- package/dist/index.mjs +69 -4
- package/package.json +4 -2
package/dist/index.d.mts
CHANGED
|
@@ -173,6 +173,12 @@ type RevlmOptions = {
|
|
|
173
173
|
provisionalAuthSecretMaster?: string;
|
|
174
174
|
provisionalAuthDomain?: string;
|
|
175
175
|
autoSetToken?: boolean;
|
|
176
|
+
autoRefreshOn401?: boolean;
|
|
177
|
+
sigv4SecretKey?: string;
|
|
178
|
+
sigv4AccessKey?: string;
|
|
179
|
+
sigv4Region?: string;
|
|
180
|
+
sigv4Service?: string;
|
|
181
|
+
sigv4Enabled?: boolean;
|
|
176
182
|
};
|
|
177
183
|
type RevlmResponse<T = any> = {
|
|
178
184
|
ok: boolean;
|
|
@@ -191,6 +197,8 @@ declare class Revlm {
|
|
|
191
197
|
private provisionalAuthSecretMaster;
|
|
192
198
|
private provisionalAuthDomain;
|
|
193
199
|
private autoSetToken;
|
|
200
|
+
private autoRefreshOn401;
|
|
201
|
+
private sigv4Signer;
|
|
194
202
|
constructor(baseUrl: string, opts?: RevlmOptions);
|
|
195
203
|
setToken(token: string): void;
|
|
196
204
|
getToken(): string | undefined;
|
|
@@ -201,6 +209,9 @@ declare class Revlm {
|
|
|
201
209
|
private makeHeaders;
|
|
202
210
|
private parseResponse;
|
|
203
211
|
private request;
|
|
212
|
+
private shouldSkipAuthRetry;
|
|
213
|
+
private signIfNeeded;
|
|
214
|
+
private requestWithRetry;
|
|
204
215
|
login(authId: string, password: string): Promise<LoginResponse>;
|
|
205
216
|
provisionalLogin(authId: string): Promise<ProvisionalLoginResponse>;
|
|
206
217
|
registerUser(user: UserInput, password: string): Promise<RevlmResponse<any>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -173,6 +173,12 @@ type RevlmOptions = {
|
|
|
173
173
|
provisionalAuthSecretMaster?: string;
|
|
174
174
|
provisionalAuthDomain?: string;
|
|
175
175
|
autoSetToken?: boolean;
|
|
176
|
+
autoRefreshOn401?: boolean;
|
|
177
|
+
sigv4SecretKey?: string;
|
|
178
|
+
sigv4AccessKey?: string;
|
|
179
|
+
sigv4Region?: string;
|
|
180
|
+
sigv4Service?: string;
|
|
181
|
+
sigv4Enabled?: boolean;
|
|
176
182
|
};
|
|
177
183
|
type RevlmResponse<T = any> = {
|
|
178
184
|
ok: boolean;
|
|
@@ -191,6 +197,8 @@ declare class Revlm {
|
|
|
191
197
|
private provisionalAuthSecretMaster;
|
|
192
198
|
private provisionalAuthDomain;
|
|
193
199
|
private autoSetToken;
|
|
200
|
+
private autoRefreshOn401;
|
|
201
|
+
private sigv4Signer;
|
|
194
202
|
constructor(baseUrl: string, opts?: RevlmOptions);
|
|
195
203
|
setToken(token: string): void;
|
|
196
204
|
getToken(): string | undefined;
|
|
@@ -201,6 +209,9 @@ declare class Revlm {
|
|
|
201
209
|
private makeHeaders;
|
|
202
210
|
private parseResponse;
|
|
203
211
|
private request;
|
|
212
|
+
private shouldSkipAuthRetry;
|
|
213
|
+
private signIfNeeded;
|
|
214
|
+
private requestWithRetry;
|
|
204
215
|
login(authId: string, password: string): Promise<LoginResponse>;
|
|
205
216
|
provisionalLogin(authId: string): Promise<ProvisionalLoginResponse>;
|
|
206
217
|
registerUser(user: UserInput, password: string): Promise<RevlmResponse<any>>;
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
46
46
|
// src/Revlm.ts
|
|
47
47
|
var import_bson = require("bson");
|
|
48
48
|
var import_revlm_shared = require("@kedaruma/revlm-shared");
|
|
49
|
+
var import_signature_v4 = require("@aws-sdk/signature-v4");
|
|
50
|
+
var import_sha256_js = require("@aws-crypto/sha256-js");
|
|
49
51
|
|
|
50
52
|
// src/MdbCollection.ts
|
|
51
53
|
var MdbCollection = class {
|
|
@@ -155,6 +157,8 @@ var Revlm = class {
|
|
|
155
157
|
provisionalAuthSecretMaster;
|
|
156
158
|
provisionalAuthDomain;
|
|
157
159
|
autoSetToken;
|
|
160
|
+
autoRefreshOn401;
|
|
161
|
+
sigv4Signer;
|
|
158
162
|
constructor(baseUrl, opts = {}) {
|
|
159
163
|
if (!baseUrl) throw new Error("baseUrl is required");
|
|
160
164
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
@@ -164,6 +168,25 @@ var Revlm = class {
|
|
|
164
168
|
this.provisionalAuthSecretMaster = opts.provisionalAuthSecretMaster || "";
|
|
165
169
|
this.provisionalAuthDomain = opts.provisionalAuthDomain || "";
|
|
166
170
|
this.autoSetToken = opts.autoSetToken ?? true;
|
|
171
|
+
this.autoRefreshOn401 = opts.autoRefreshOn401 || false;
|
|
172
|
+
const sigv4SecretKey = opts.sigv4SecretKey || process.env.REVLM_SIGV4_SECRET_KEY;
|
|
173
|
+
const sigv4AccessKey = opts.sigv4AccessKey || process.env.REVLM_SIGV4_ACCESS_KEY || "revlm-access";
|
|
174
|
+
const sigv4Region = opts.sigv4Region || process.env.REVLM_SIGV4_REGION || "revlm";
|
|
175
|
+
const sigv4Service = opts.sigv4Service || process.env.REVLM_SIGV4_SERVICE || "revlm";
|
|
176
|
+
const sigv4Enabled = opts.sigv4Enabled ?? true;
|
|
177
|
+
if (sigv4Enabled) {
|
|
178
|
+
if (!sigv4SecretKey) {
|
|
179
|
+
throw new Error("SigV4 is enabled but REVLM_SIGV4_SECRET_KEY or opts.sigv4SecretKey is not provided");
|
|
180
|
+
}
|
|
181
|
+
this.sigv4Signer = new import_signature_v4.SignatureV4({
|
|
182
|
+
credentials: { accessKeyId: sigv4AccessKey, secretAccessKey: sigv4SecretKey },
|
|
183
|
+
region: sigv4Region,
|
|
184
|
+
service: sigv4Service,
|
|
185
|
+
sha256: import_sha256_js.Sha256
|
|
186
|
+
});
|
|
187
|
+
} else {
|
|
188
|
+
this.sigv4Signer = null;
|
|
189
|
+
}
|
|
167
190
|
if (!this.fetchImpl) {
|
|
168
191
|
throw new Error("No fetch implementation available. Provide fetchImpl in options or run in Node 18+ with global fetch.");
|
|
169
192
|
}
|
|
@@ -185,7 +208,7 @@ var Revlm = class {
|
|
|
185
208
|
// On success, if autoSetToken is true and res.token is set, update the client token.
|
|
186
209
|
async refreshToken() {
|
|
187
210
|
if (!this._token) return { ok: false, error: "No token set" };
|
|
188
|
-
const res = await this.
|
|
211
|
+
const res = await this.requestWithRetry("/refresh-token", "POST", void 0, { allowAuthRetry: false, retrying: false });
|
|
189
212
|
if (this.autoSetToken && res && res.ok && res.token) {
|
|
190
213
|
this.setToken(res.token);
|
|
191
214
|
}
|
|
@@ -212,7 +235,7 @@ var Revlm = class {
|
|
|
212
235
|
headers["Content-Type"] = "application/ejson";
|
|
213
236
|
}
|
|
214
237
|
if (this._token) {
|
|
215
|
-
headers["
|
|
238
|
+
headers["X-Revlm-JWT"] = `Bearer ${this._token}`;
|
|
216
239
|
}
|
|
217
240
|
return headers;
|
|
218
241
|
}
|
|
@@ -230,6 +253,41 @@ var Revlm = class {
|
|
|
230
253
|
}
|
|
231
254
|
}
|
|
232
255
|
async request(path, method = "POST", body) {
|
|
256
|
+
return this.requestWithRetry(path, method, body, { allowAuthRetry: this.autoRefreshOn401, retrying: false });
|
|
257
|
+
}
|
|
258
|
+
shouldSkipAuthRetry(path) {
|
|
259
|
+
const pathname = path.startsWith("http") ? new URL(path).pathname : path;
|
|
260
|
+
return pathname.includes("/login") || pathname.includes("/provisional-login") || pathname.includes("/refresh-token") || pathname.includes("/verify-token");
|
|
261
|
+
}
|
|
262
|
+
async signIfNeeded(url, method, headers, body) {
|
|
263
|
+
if (!this.sigv4Signer) {
|
|
264
|
+
return { signedUrl: url, signedHeaders: headers };
|
|
265
|
+
}
|
|
266
|
+
const u = new URL(url);
|
|
267
|
+
const signingHeaders = {
|
|
268
|
+
host: u.host,
|
|
269
|
+
...headers
|
|
270
|
+
};
|
|
271
|
+
const reqToSign = {
|
|
272
|
+
method,
|
|
273
|
+
protocol: u.protocol,
|
|
274
|
+
path: u.pathname + (u.search || ""),
|
|
275
|
+
hostname: u.hostname,
|
|
276
|
+
headers: signingHeaders,
|
|
277
|
+
body: body ?? ""
|
|
278
|
+
};
|
|
279
|
+
if (u.port) {
|
|
280
|
+
reqToSign.port = Number(u.port);
|
|
281
|
+
}
|
|
282
|
+
const signed = await this.sigv4Signer.sign(reqToSign);
|
|
283
|
+
const signedHeaders = {};
|
|
284
|
+
Object.entries(signed.headers || {}).forEach(([k, v]) => {
|
|
285
|
+
signedHeaders[k] = Array.isArray(v) ? v.join(",") : String(v);
|
|
286
|
+
});
|
|
287
|
+
return { signedUrl: url, signedHeaders };
|
|
288
|
+
}
|
|
289
|
+
async requestWithRetry(path, method = "POST", body, opts = { allowAuthRetry: false, retrying: false }) {
|
|
290
|
+
const { allowAuthRetry, retrying } = opts;
|
|
233
291
|
const url = path.startsWith("http") ? path : `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
234
292
|
const hasBody = body !== void 0;
|
|
235
293
|
const headers = this.makeHeaders(hasBody);
|
|
@@ -237,10 +295,11 @@ var Revlm = class {
|
|
|
237
295
|
if (hasBody) {
|
|
238
296
|
serializedBody = import_bson.EJSON.stringify(body);
|
|
239
297
|
}
|
|
298
|
+
const { signedUrl, signedHeaders } = await this.signIfNeeded(url, method, headers, serializedBody);
|
|
240
299
|
try {
|
|
241
|
-
const res = await this.fetchImpl(
|
|
300
|
+
const res = await this.fetchImpl(signedUrl, {
|
|
242
301
|
method,
|
|
243
|
-
headers,
|
|
302
|
+
headers: signedHeaders,
|
|
244
303
|
body: serializedBody
|
|
245
304
|
});
|
|
246
305
|
const parsed = await this.parseResponse(res);
|
|
@@ -249,6 +308,12 @@ var Revlm = class {
|
|
|
249
308
|
if (out && out.ok === false && !out.error) {
|
|
250
309
|
out.error = parsed?.reason || parsed?.message || "Unknown error";
|
|
251
310
|
}
|
|
311
|
+
if (allowAuthRetry && !retrying && res.status === 401 && !this.shouldSkipAuthRetry(path)) {
|
|
312
|
+
const refreshRes = await this.refreshToken();
|
|
313
|
+
if (refreshRes && refreshRes.ok && refreshRes.token) {
|
|
314
|
+
return this.requestWithRetry(path, method, body, { allowAuthRetry: false, retrying: true });
|
|
315
|
+
}
|
|
316
|
+
}
|
|
252
317
|
return out;
|
|
253
318
|
} catch (err) {
|
|
254
319
|
return { ok: false, error: err?.message || String(err) };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/Revlm.ts
|
|
2
2
|
import { EJSON } from "bson";
|
|
3
3
|
import { AuthClient } from "@kedaruma/revlm-shared";
|
|
4
|
+
import { SignatureV4 } from "@aws-sdk/signature-v4";
|
|
5
|
+
import { Sha256 } from "@aws-crypto/sha256-js";
|
|
4
6
|
|
|
5
7
|
// src/MdbCollection.ts
|
|
6
8
|
var MdbCollection = class {
|
|
@@ -110,6 +112,8 @@ var Revlm = class {
|
|
|
110
112
|
provisionalAuthSecretMaster;
|
|
111
113
|
provisionalAuthDomain;
|
|
112
114
|
autoSetToken;
|
|
115
|
+
autoRefreshOn401;
|
|
116
|
+
sigv4Signer;
|
|
113
117
|
constructor(baseUrl, opts = {}) {
|
|
114
118
|
if (!baseUrl) throw new Error("baseUrl is required");
|
|
115
119
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
@@ -119,6 +123,25 @@ var Revlm = class {
|
|
|
119
123
|
this.provisionalAuthSecretMaster = opts.provisionalAuthSecretMaster || "";
|
|
120
124
|
this.provisionalAuthDomain = opts.provisionalAuthDomain || "";
|
|
121
125
|
this.autoSetToken = opts.autoSetToken ?? true;
|
|
126
|
+
this.autoRefreshOn401 = opts.autoRefreshOn401 || false;
|
|
127
|
+
const sigv4SecretKey = opts.sigv4SecretKey || process.env.REVLM_SIGV4_SECRET_KEY;
|
|
128
|
+
const sigv4AccessKey = opts.sigv4AccessKey || process.env.REVLM_SIGV4_ACCESS_KEY || "revlm-access";
|
|
129
|
+
const sigv4Region = opts.sigv4Region || process.env.REVLM_SIGV4_REGION || "revlm";
|
|
130
|
+
const sigv4Service = opts.sigv4Service || process.env.REVLM_SIGV4_SERVICE || "revlm";
|
|
131
|
+
const sigv4Enabled = opts.sigv4Enabled ?? true;
|
|
132
|
+
if (sigv4Enabled) {
|
|
133
|
+
if (!sigv4SecretKey) {
|
|
134
|
+
throw new Error("SigV4 is enabled but REVLM_SIGV4_SECRET_KEY or opts.sigv4SecretKey is not provided");
|
|
135
|
+
}
|
|
136
|
+
this.sigv4Signer = new SignatureV4({
|
|
137
|
+
credentials: { accessKeyId: sigv4AccessKey, secretAccessKey: sigv4SecretKey },
|
|
138
|
+
region: sigv4Region,
|
|
139
|
+
service: sigv4Service,
|
|
140
|
+
sha256: Sha256
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
this.sigv4Signer = null;
|
|
144
|
+
}
|
|
122
145
|
if (!this.fetchImpl) {
|
|
123
146
|
throw new Error("No fetch implementation available. Provide fetchImpl in options or run in Node 18+ with global fetch.");
|
|
124
147
|
}
|
|
@@ -140,7 +163,7 @@ var Revlm = class {
|
|
|
140
163
|
// On success, if autoSetToken is true and res.token is set, update the client token.
|
|
141
164
|
async refreshToken() {
|
|
142
165
|
if (!this._token) return { ok: false, error: "No token set" };
|
|
143
|
-
const res = await this.
|
|
166
|
+
const res = await this.requestWithRetry("/refresh-token", "POST", void 0, { allowAuthRetry: false, retrying: false });
|
|
144
167
|
if (this.autoSetToken && res && res.ok && res.token) {
|
|
145
168
|
this.setToken(res.token);
|
|
146
169
|
}
|
|
@@ -167,7 +190,7 @@ var Revlm = class {
|
|
|
167
190
|
headers["Content-Type"] = "application/ejson";
|
|
168
191
|
}
|
|
169
192
|
if (this._token) {
|
|
170
|
-
headers["
|
|
193
|
+
headers["X-Revlm-JWT"] = `Bearer ${this._token}`;
|
|
171
194
|
}
|
|
172
195
|
return headers;
|
|
173
196
|
}
|
|
@@ -185,6 +208,41 @@ var Revlm = class {
|
|
|
185
208
|
}
|
|
186
209
|
}
|
|
187
210
|
async request(path, method = "POST", body) {
|
|
211
|
+
return this.requestWithRetry(path, method, body, { allowAuthRetry: this.autoRefreshOn401, retrying: false });
|
|
212
|
+
}
|
|
213
|
+
shouldSkipAuthRetry(path) {
|
|
214
|
+
const pathname = path.startsWith("http") ? new URL(path).pathname : path;
|
|
215
|
+
return pathname.includes("/login") || pathname.includes("/provisional-login") || pathname.includes("/refresh-token") || pathname.includes("/verify-token");
|
|
216
|
+
}
|
|
217
|
+
async signIfNeeded(url, method, headers, body) {
|
|
218
|
+
if (!this.sigv4Signer) {
|
|
219
|
+
return { signedUrl: url, signedHeaders: headers };
|
|
220
|
+
}
|
|
221
|
+
const u = new URL(url);
|
|
222
|
+
const signingHeaders = {
|
|
223
|
+
host: u.host,
|
|
224
|
+
...headers
|
|
225
|
+
};
|
|
226
|
+
const reqToSign = {
|
|
227
|
+
method,
|
|
228
|
+
protocol: u.protocol,
|
|
229
|
+
path: u.pathname + (u.search || ""),
|
|
230
|
+
hostname: u.hostname,
|
|
231
|
+
headers: signingHeaders,
|
|
232
|
+
body: body ?? ""
|
|
233
|
+
};
|
|
234
|
+
if (u.port) {
|
|
235
|
+
reqToSign.port = Number(u.port);
|
|
236
|
+
}
|
|
237
|
+
const signed = await this.sigv4Signer.sign(reqToSign);
|
|
238
|
+
const signedHeaders = {};
|
|
239
|
+
Object.entries(signed.headers || {}).forEach(([k, v]) => {
|
|
240
|
+
signedHeaders[k] = Array.isArray(v) ? v.join(",") : String(v);
|
|
241
|
+
});
|
|
242
|
+
return { signedUrl: url, signedHeaders };
|
|
243
|
+
}
|
|
244
|
+
async requestWithRetry(path, method = "POST", body, opts = { allowAuthRetry: false, retrying: false }) {
|
|
245
|
+
const { allowAuthRetry, retrying } = opts;
|
|
188
246
|
const url = path.startsWith("http") ? path : `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
189
247
|
const hasBody = body !== void 0;
|
|
190
248
|
const headers = this.makeHeaders(hasBody);
|
|
@@ -192,10 +250,11 @@ var Revlm = class {
|
|
|
192
250
|
if (hasBody) {
|
|
193
251
|
serializedBody = EJSON.stringify(body);
|
|
194
252
|
}
|
|
253
|
+
const { signedUrl, signedHeaders } = await this.signIfNeeded(url, method, headers, serializedBody);
|
|
195
254
|
try {
|
|
196
|
-
const res = await this.fetchImpl(
|
|
255
|
+
const res = await this.fetchImpl(signedUrl, {
|
|
197
256
|
method,
|
|
198
|
-
headers,
|
|
257
|
+
headers: signedHeaders,
|
|
199
258
|
body: serializedBody
|
|
200
259
|
});
|
|
201
260
|
const parsed = await this.parseResponse(res);
|
|
@@ -204,6 +263,12 @@ var Revlm = class {
|
|
|
204
263
|
if (out && out.ok === false && !out.error) {
|
|
205
264
|
out.error = parsed?.reason || parsed?.message || "Unknown error";
|
|
206
265
|
}
|
|
266
|
+
if (allowAuthRetry && !retrying && res.status === 401 && !this.shouldSkipAuthRetry(path)) {
|
|
267
|
+
const refreshRes = await this.refreshToken();
|
|
268
|
+
if (refreshRes && refreshRes.ok && refreshRes.token) {
|
|
269
|
+
return this.requestWithRetry(path, method, body, { allowAuthRetry: false, retrying: true });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
207
272
|
return out;
|
|
208
273
|
} catch (err) {
|
|
209
274
|
return { ok: false, error: err?.message || String(err) };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kedaruma/revlm-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "TypeScript client SDK for talking to the Revlm server replacement for MongoDB Realm.",
|
|
6
6
|
"keywords": [
|
|
@@ -36,9 +36,11 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
+
"@aws-sdk/signature-v4": "^3.374.0",
|
|
40
|
+
"@aws-crypto/sha256-js": "^5.2.0",
|
|
39
41
|
"bson": "^6.10.4",
|
|
40
42
|
"dotenv": "^17.2.3",
|
|
41
|
-
"@kedaruma/revlm-shared": "1.0.
|
|
43
|
+
"@kedaruma/revlm-shared": "1.0.4"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
46
|
"tsup": "^8.5.1"
|