ecwt 0.2.0-beta.2 → 0.2.0-beta.4

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 CHANGED
@@ -76,7 +76,8 @@ In our example, we use [valibot](https://valibot.dev) library.
76
76
  ```javascript
77
77
  import * as v from 'valibot';
78
78
 
79
- const schema = (value) => v.parse(
79
+ const schema = v.parse.bind(
80
+ null,
80
81
  v.object({
81
82
  user_id: v.number([
82
83
  v.maxValue(10),
@@ -85,7 +86,6 @@ const schema = (value) => v.parse(
85
86
  v.maxLength(10),
86
87
  ]),
87
88
  }),
88
- value,
89
89
  );
90
90
  ```
91
91
 
@@ -197,6 +197,30 @@ If the token is invalid, throws `EcwtInvalidError` which contains `Ecwt` instanc
197
197
  const ecwt = await ecwtFactory.verify(token);
198
198
  ```
199
199
 
200
+ #### Class method `safeVerify`
201
+
202
+ ```typescript
203
+ safeVerify(
204
+ token: string,
205
+ ): Promise<{
206
+ success: boolean,
207
+ ecwt: Ecwt | null,
208
+ }>
209
+ ```
210
+
211
+ The same method as `verify`, but does not throw an error if the token is invalid, expired or revoked.
212
+
213
+ Property `success` is `true` if the token is valid.
214
+
215
+ Property `ecwt` is `null` if the token cannot be parsed, otherwise it contains `Ecwt` instance.
216
+
217
+ ```javascript
218
+ const {
219
+ success,
220
+ ecwt,
221
+ } = await ecwtFactory.safeVerify(token);
222
+ ```
223
+
200
224
  ### `Ecwt`
201
225
 
202
226
  Represents the token. Its counstructor cannot be called by the user.
package/dist/ecwt.cjs CHANGED
@@ -229,17 +229,17 @@ var EcwtFactory = class {
229
229
  * @param {{ [key: string]: number }} [param0.options.senml_key_map] Payload object keys mapped for their SenML keys.
230
230
  */
231
231
  constructor({
232
- redisClient: redisClient2 = null,
233
- lruCache = null,
232
+ redisClient: redisClient2,
233
+ lruCache,
234
234
  snowflakeFactory,
235
235
  options: {
236
- namespace = null,
236
+ namespace,
237
237
  key,
238
238
  validator,
239
239
  senml_key_map
240
240
  }
241
241
  }) {
242
- if (redisClient2 !== null && (redisClient2.constructor.name !== redis_client_constructor_name || getAllKeysList(redisClient2) !== redis_client_keys)) {
242
+ if (redisClient2 !== void 0 && (redisClient2.constructor.name !== redis_client_constructor_name || getAllKeysList(redisClient2) !== redis_client_keys)) {
243
243
  throw new InvalidPackageInstanceError(
244
244
  "redisClient",
245
245
  "Commander extends RedisClient",
@@ -247,7 +247,7 @@ var EcwtFactory = class {
247
247
  );
248
248
  }
249
249
  this.#redisClient = redisClient2;
250
- if (lruCache !== null && lruCache instanceof import_lru_cache.LRUCache !== true) {
250
+ if (lruCache !== void 0 && lruCache instanceof import_lru_cache.LRUCache !== true) {
251
251
  throw new InvalidPackageInstanceError(
252
252
  "lruCache",
253
253
  "LRUCache",
@@ -277,11 +277,11 @@ var EcwtFactory = class {
277
277
  * @async
278
278
  * @param {object} data Data to be stored in token.
279
279
  * @param {object} [options] -
280
- * @param {number} [options.ttl] Time to live in seconds. By default, token will never expire.
280
+ * @param {number | null} [options.ttl] Time to live in seconds. By default, token will never expire.
281
281
  * @returns {Promise<Ecwt>} -
282
282
  */
283
283
  async create(data, {
284
- ttl
284
+ ttl = null
285
285
  } = {}) {
286
286
  if (typeof this.#validator === "function") {
287
287
  data = this.#validator(data);
@@ -319,6 +319,11 @@ var EcwtFactory = class {
319
319
  }
320
320
  );
321
321
  }
322
+ /**
323
+ * Sets data to cache.
324
+ * @param {string} token String representation of token.
325
+ * @param {object} data Data to be stored in cache.
326
+ */
322
327
  #setCache(token, data) {
323
328
  this.#lruCache?.set(
324
329
  token,
@@ -410,6 +415,36 @@ var EcwtFactory = class {
410
415
  }
411
416
  return ecwt;
412
417
  }
418
+ /**
419
+ * Parses token without throwing errors.
420
+ * @async
421
+ * @param {string} token String representation of token.
422
+ * @returns {Promise<{ success: boolean, ecwt: Ecwt | null }>} Returns whether token was parsed and verified successfully and Ecwt if parsed.
423
+ */
424
+ async safeVerify(token) {
425
+ let ecwt = null;
426
+ try {
427
+ ecwt = await this.verify(token);
428
+ return {
429
+ success: true,
430
+ ecwt
431
+ };
432
+ } catch (error) {
433
+ if (error instanceof EcwtParseError) {
434
+ return {
435
+ success: false,
436
+ ecwt: null
437
+ };
438
+ }
439
+ if (error instanceof EcwtInvalidError) {
440
+ return {
441
+ success: false,
442
+ ecwt
443
+ };
444
+ }
445
+ throw error;
446
+ }
447
+ }
413
448
  /**
414
449
  * Revokes token.
415
450
  * @async
@@ -428,12 +463,17 @@ var EcwtFactory = class {
428
463
  ttl_initial = ttl_initial ?? Number.MAX_SAFE_INTEGER;
429
464
  const ts_ms_expired = ts_ms_created + ttl_initial * 1e3;
430
465
  if (ts_ms_expired > Date.now()) {
431
- await this.#redisClient.sendCommand([
466
+ await this.#redisClient.MULTI().addCommand([
432
467
  "ZADD",
433
468
  this.#redis_keys.revoked,
434
469
  String(ts_ms_expired),
435
470
  token_id
436
- ]);
471
+ ]).addCommand([
472
+ "ZREMRANGEBYSCORE",
473
+ this.#redis_keys.revoked,
474
+ "-inf",
475
+ String(Date.now())
476
+ ]).EXEC();
437
477
  }
438
478
  } else {
439
479
  console.warn("[ecwt] Redis client is not provided. Tokens cannot be revoked.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecwt",
3
- "version": "0.2.0-beta.2",
3
+ "version": "0.2.0-beta.4",
4
4
  "description": "Encrypted CBOR-encoded Web Token",
5
5
  "main": "src/main.js",
6
6
  "type": "module",
@@ -32,8 +32,8 @@
32
32
  "eslint-plugin-node": "11.1.0",
33
33
  "eslint-plugin-promise": "6.1.1",
34
34
  "eslint-plugin-unicorn": "47.0.0",
35
- "valibot": "^0.24.1",
36
- "vitest": "1.3.1"
35
+ "valibot": "0.33",
36
+ "vitest": "1.6"
37
37
  },
38
38
  "scripts": {
39
39
  "test": "bun run redis-up && bun test && npm run test-node",
package/src/factory.js CHANGED
@@ -1,4 +1,6 @@
1
1
 
2
+ // @ts-check
3
+
2
4
  import { SnowflakeFactory } from '@kirick/snowflake';
3
5
  import {
4
6
  Encoder as CborEncoder,
@@ -13,6 +15,7 @@ import { Ecwt } from './token.js';
13
15
  import { base62 } from './utils/base62.js';
14
16
  import {
15
17
  InvalidPackageInstanceError,
18
+ EcwtInvalidError,
16
19
  EcwtExpiredError,
17
20
  EcwtRevokedError,
18
21
  EcwtParseError } from './utils/errors.js';
@@ -57,18 +60,18 @@ export class EcwtFactory {
57
60
  * @param {{ [key: string]: number }} [param0.options.senml_key_map] Payload object keys mapped for their SenML keys.
58
61
  */
59
62
  constructor({
60
- redisClient = null,
61
- lruCache = null,
63
+ redisClient,
64
+ lruCache,
62
65
  snowflakeFactory,
63
66
  options: {
64
- namespace = null,
67
+ namespace,
65
68
  key,
66
69
  validator,
67
70
  senml_key_map,
68
71
  },
69
72
  }) {
70
73
  if (
71
- redisClient !== null
74
+ redisClient !== undefined
72
75
  && (
73
76
  redisClient.constructor.name !== redis_client_constructor_name
74
77
  || getAllKeysList(redisClient) !== redis_client_keys
@@ -83,7 +86,7 @@ export class EcwtFactory {
83
86
  this.#redisClient = redisClient;
84
87
 
85
88
  if (
86
- lruCache !== null
89
+ lruCache !== undefined
87
90
  && lruCache instanceof LRUCache !== true
88
91
  ) {
89
92
  throw new InvalidPackageInstanceError(
@@ -121,13 +124,13 @@ export class EcwtFactory {
121
124
  * @async
122
125
  * @param {object} data Data to be stored in token.
123
126
  * @param {object} [options] -
124
- * @param {number} [options.ttl] Time to live in seconds. By default, token will never expire.
127
+ * @param {number | null} [options.ttl] Time to live in seconds. By default, token will never expire.
125
128
  * @returns {Promise<Ecwt>} -
126
129
  */
127
130
  async create(
128
131
  data,
129
132
  {
130
- ttl,
133
+ ttl = null,
131
134
  } = {},
132
135
  ) {
133
136
  if (typeof this.#validator === 'function') {
@@ -180,6 +183,11 @@ export class EcwtFactory {
180
183
  );
181
184
  }
182
185
 
186
+ /**
187
+ * Sets data to cache.
188
+ * @param {string} token String representation of token.
189
+ * @param {object} data Data to be stored in cache.
190
+ */
183
191
  #setCache(token, data) {
184
192
  this.#lruCache?.set(
185
193
  token,
@@ -299,6 +307,41 @@ export class EcwtFactory {
299
307
  return ecwt;
300
308
  }
301
309
 
310
+ /**
311
+ * Parses token without throwing errors.
312
+ * @async
313
+ * @param {string} token String representation of token.
314
+ * @returns {Promise<{ success: boolean, ecwt: Ecwt | null }>} Returns whether token was parsed and verified successfully and Ecwt if parsed.
315
+ */
316
+ async safeVerify(token) {
317
+ let ecwt = null;
318
+ try {
319
+ ecwt = await this.verify(token);
320
+
321
+ return {
322
+ success: true,
323
+ ecwt,
324
+ };
325
+ }
326
+ catch (error) {
327
+ if (error instanceof EcwtParseError) {
328
+ return {
329
+ success: false,
330
+ ecwt: null,
331
+ };
332
+ }
333
+
334
+ if (error instanceof EcwtInvalidError) {
335
+ return {
336
+ success: false,
337
+ ecwt,
338
+ };
339
+ }
340
+
341
+ throw error;
342
+ }
343
+ }
344
+
302
345
  /**
303
346
  * Revokes token.
304
347
  * @async
@@ -318,12 +361,26 @@ export class EcwtFactory {
318
361
 
319
362
  const ts_ms_expired = ts_ms_created + (ttl_initial * 1000);
320
363
  if (ts_ms_expired > Date.now()) {
321
- await this.#redisClient.sendCommand([
322
- 'ZADD',
323
- this.#redis_keys.revoked,
324
- String(ts_ms_expired),
325
- token_id,
326
- ]);
364
+ // await this.#redisClient.sendCommand([
365
+ // 'ZADD',
366
+ // this.#redis_keys.revoked,
367
+ // String(ts_ms_expired),
368
+ // token_id,
369
+ // ]);
370
+ await this.#redisClient.MULTI()
371
+ .addCommand([
372
+ 'ZADD',
373
+ this.#redis_keys.revoked,
374
+ String(ts_ms_expired),
375
+ token_id,
376
+ ])
377
+ .addCommand([
378
+ 'ZREMRANGEBYSCORE',
379
+ this.#redis_keys.revoked,
380
+ '-inf',
381
+ String(Date.now()),
382
+ ])
383
+ .EXEC();
327
384
  }
328
385
  }
329
386
  else {
@@ -5,12 +5,18 @@ export class InvalidPackageInstanceError extends TypeError {
5
5
  }
6
6
  }
7
7
 
8
+ /**
9
+ * Error thrown when string token cannot be parsed to Ecwt.
10
+ */
8
11
  export class EcwtParseError extends Error {
9
12
  constructor() {
10
13
  super('Cannot parse data to Ecwt token.');
11
14
  }
12
15
  }
13
16
 
17
+ /**
18
+ * Error thrown when parsed Ecwt is invalid.
19
+ */
14
20
  export class EcwtInvalidError extends Error {
15
21
  constructor(ecwt) {
16
22
  super('Ecwt token is invalid.');
@@ -19,6 +25,9 @@ export class EcwtInvalidError extends Error {
19
25
  }
20
26
  }
21
27
 
28
+ /**
29
+ * Error thrown when parsed Ecwt is expired.
30
+ */
22
31
  export class EcwtExpiredError extends EcwtInvalidError {
23
32
  constructor(ecwt) {
24
33
  super();
@@ -28,6 +37,9 @@ export class EcwtExpiredError extends EcwtInvalidError {
28
37
  }
29
38
  }
30
39
 
40
+ /**
41
+ * Error thrown when parsed Ecwt is revoked.
42
+ */
31
43
  export class EcwtRevokedError extends EcwtInvalidError {
32
44
  constructor(ecwt) {
33
45
  super();