@bernierllc/email-sendgrid-plugin 1.4.4 → 1.6.0

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.
Files changed (43) hide show
  1. package/dist/api/errors.js +5 -0
  2. package/dist/api/rate-limiter.js +3 -1
  3. package/dist/api/sendgrid-client.js +4 -1
  4. package/dist/converters/template-converter.js +1 -0
  5. package/dist/errors.d.ts +11 -0
  6. package/dist/errors.js +24 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +4 -1
  9. package/dist/plugin/sender-manager.js +6 -1
  10. package/dist/plugin/sendgrid-plugin.js +15 -4
  11. package/dist/plugin/sync-manager.js +4 -0
  12. package/dist/plugin/template-manager.js +5 -0
  13. package/dist/safety/ownership-validator.js +3 -1
  14. package/dist/safety/prefix-enforcer.js +1 -0
  15. package/package.json +8 -4
  16. package/.eslintrc.js +0 -27
  17. package/CHANGELOG.md +0 -109
  18. package/__tests__/api/rate-limiter.test.ts +0 -125
  19. package/__tests__/converters/attachment-header-mapping.test.ts +0 -337
  20. package/__tests__/converters/sender-converter.test.ts +0 -148
  21. package/__tests__/converters/template-converter.test.ts +0 -230
  22. package/__tests__/plugin/sender-manager.test.ts +0 -135
  23. package/__tests__/plugin/sendgrid-plugin.test.ts +0 -195
  24. package/__tests__/plugin/template-manager.test.ts +0 -110
  25. package/__tests__/safety/ownership-validator.test.ts +0 -130
  26. package/__tests__/safety/prefix-enforcer.test.ts +0 -136
  27. package/jest.config.js +0 -26
  28. package/src/api/errors.ts +0 -49
  29. package/src/api/rate-limiter.ts +0 -79
  30. package/src/api/sendgrid-client.ts +0 -86
  31. package/src/converters/sender-converter.ts +0 -138
  32. package/src/converters/template-converter.ts +0 -97
  33. package/src/index.ts +0 -61
  34. package/src/plugin/sender-manager.ts +0 -166
  35. package/src/plugin/sendgrid-plugin.ts +0 -316
  36. package/src/plugin/sync-manager.ts +0 -115
  37. package/src/plugin/template-manager.ts +0 -240
  38. package/src/safety/ownership-validator.ts +0 -55
  39. package/src/safety/prefix-enforcer.ts +0 -37
  40. package/src/types/config-types.ts +0 -34
  41. package/src/types/plugin-types.ts +0 -97
  42. package/src/types/sendgrid-types.ts +0 -98
  43. package/tsconfig.json +0 -18
@@ -9,6 +9,8 @@ Redistribution or use in other products or commercial offerings is not permitted
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.RateLimitError = exports.TemplateConflictError = exports.UnauthorizedTemplateAccessError = exports.TemplateNotFoundError = exports.SendGridApiError = void 0;
11
11
  class SendGridApiError extends Error {
12
+ statusCode;
13
+ response;
12
14
  constructor(message, statusCode, response) {
13
15
  super(message);
14
16
  this.statusCode = statusCode;
@@ -18,6 +20,7 @@ class SendGridApiError extends Error {
18
20
  }
19
21
  exports.SendGridApiError = SendGridApiError;
20
22
  class TemplateNotFoundError extends Error {
23
+ templateId;
21
24
  constructor(templateId) {
22
25
  super(`Template not found: ${templateId}`);
23
26
  this.templateId = templateId;
@@ -33,6 +36,8 @@ class UnauthorizedTemplateAccessError extends Error {
33
36
  }
34
37
  exports.UnauthorizedTemplateAccessError = UnauthorizedTemplateAccessError;
35
38
  class TemplateConflictError extends Error {
39
+ templateId;
40
+ conflicts;
36
41
  constructor(templateId, conflicts) {
37
42
  super(`Template conflicts detected: ${conflicts.join(', ')}`);
38
43
  this.templateId = templateId;
@@ -10,8 +10,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.SimpleRateLimiter = void 0;
11
11
  const errors_1 = require("./errors");
12
12
  class SimpleRateLimiter {
13
+ requestTimestamps = [];
14
+ maxRequestsPerSecond;
15
+ maxRequestsPerMinute;
13
16
  constructor(config) {
14
- this.requestTimestamps = [];
15
17
  this.maxRequestsPerSecond = config.maxRequestsPerSecond;
16
18
  this.maxRequestsPerMinute = config.maxRequestsPerMinute;
17
19
  }
@@ -10,8 +10,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.SendGridClient = void 0;
11
11
  const errors_1 = require("./errors");
12
12
  class SendGridClient {
13
+ apiKey;
14
+ rateLimiter;
15
+ logger;
16
+ baseUrl = 'https://api.sendgrid.com/v3';
13
17
  constructor(config) {
14
- this.baseUrl = 'https://api.sendgrid.com/v3';
15
18
  this.apiKey = config.apiKey;
16
19
  this.rateLimiter = config.rateLimiter;
17
20
  this.logger = config.logger;
@@ -9,6 +9,7 @@ Redistribution or use in other products or commercial offerings is not permitted
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.TemplateConverter = void 0;
11
11
  class TemplateConverter {
12
+ prefixEnforcer;
12
13
  constructor(prefixEnforcer) {
13
14
  this.prefixEnforcer = prefixEnforcer;
14
15
  }
@@ -0,0 +1,11 @@
1
+ export type EmailSendgridPluginErrorCode = 'EMAIL_SENDGRID_PLUGIN_ERROR' | 'INVALID_INPUT' | 'OPERATION_FAILED';
2
+ export interface EmailSendgridPluginErrorOptions {
3
+ cause?: Error;
4
+ code?: EmailSendgridPluginErrorCode;
5
+ context?: Record<string, unknown>;
6
+ }
7
+ export declare class EmailSendgridPluginError extends Error {
8
+ readonly code: EmailSendgridPluginErrorCode;
9
+ readonly context?: Record<string, unknown>;
10
+ constructor(message: string, options?: EmailSendgridPluginErrorOptions);
11
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code only within the scope of the project it was delivered for.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.EmailSendgridPluginError = void 0;
11
+ class EmailSendgridPluginError extends Error {
12
+ code;
13
+ context;
14
+ constructor(message, options) {
15
+ super(message, options?.cause ? { cause: options.cause } : undefined);
16
+ this.name = 'EmailSendgridPluginError';
17
+ this.code = options?.code ?? 'EMAIL_SENDGRID_PLUGIN_ERROR';
18
+ if (options?.context !== undefined) {
19
+ this.context = options.context;
20
+ }
21
+ Error.captureStackTrace?.(this, this.constructor);
22
+ }
23
+ }
24
+ exports.EmailSendgridPluginError = EmailSendgridPluginError;
package/dist/index.d.ts CHANGED
@@ -12,3 +12,4 @@ export { SendGridApiError, TemplateNotFoundError, UnauthorizedTemplateAccessErro
12
12
  export type { SendGridPluginConfig, ApiRequestOptions, ApiResponse } from './types/config-types';
13
13
  export type { PluginConfig, ProviderCapability, Template, ProviderTemplate, TemplateVersion, SyncResult, ValidationResult, DateRange, TemplateAnalytics, FeatureConfig } from './types/plugin-types';
14
14
  export type { VerifiedSender, SenderValidationResult, SendGridTemplateData, SendGridTemplateVersion, SendGridVerifiedSenderData, EmailAttachment, SendGridAttachment, SendGridMailPayload } from './types/sendgrid-types';
15
+ export { EmailSendgridPluginError, type EmailSendgridPluginErrorCode, type EmailSendgridPluginErrorOptions } from './errors';
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ The client may use and modify this code *only within the scope of the project it
7
7
  Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.RateLimitError = exports.TemplateConflictError = exports.UnauthorizedTemplateAccessError = exports.TemplateNotFoundError = exports.SendGridApiError = exports.SenderConverter = exports.TemplateConverter = exports.PrefixEnforcer = exports.OwnershipValidator = exports.SimpleRateLimiter = exports.SendGridClient = exports.SyncManager = exports.SenderManager = exports.TemplateManager = exports.SendGridTemplatePlugin = void 0;
10
+ exports.EmailSendgridPluginError = exports.RateLimitError = exports.TemplateConflictError = exports.UnauthorizedTemplateAccessError = exports.TemplateNotFoundError = exports.SendGridApiError = exports.SenderConverter = exports.TemplateConverter = exports.PrefixEnforcer = exports.OwnershipValidator = exports.SimpleRateLimiter = exports.SendGridClient = exports.SyncManager = exports.SenderManager = exports.TemplateManager = exports.SendGridTemplatePlugin = void 0;
11
11
  // Main plugin class
12
12
  var sendgrid_plugin_1 = require("./plugin/sendgrid-plugin");
13
13
  Object.defineProperty(exports, "SendGridTemplatePlugin", { enumerable: true, get: function () { return sendgrid_plugin_1.SendGridTemplatePlugin; } });
@@ -40,3 +40,6 @@ Object.defineProperty(exports, "TemplateNotFoundError", { enumerable: true, get:
40
40
  Object.defineProperty(exports, "UnauthorizedTemplateAccessError", { enumerable: true, get: function () { return errors_1.UnauthorizedTemplateAccessError; } });
41
41
  Object.defineProperty(exports, "TemplateConflictError", { enumerable: true, get: function () { return errors_1.TemplateConflictError; } });
42
42
  Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return errors_1.RateLimitError; } });
43
+ // Standard error type
44
+ var errors_2 = require("./errors");
45
+ Object.defineProperty(exports, "EmailSendgridPluginError", { enumerable: true, get: function () { return errors_2.EmailSendgridPluginError; } });
@@ -10,11 +10,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.SenderManager = void 0;
11
11
  const errors_1 = require("../api/errors");
12
12
  class SenderManager {
13
+ client;
14
+ converter;
15
+ logger;
16
+ sendersCache;
17
+ cacheTimestamp;
18
+ cacheTTL = 300000; // 5 minutes
13
19
  constructor(client, converter, logger) {
14
20
  this.client = client;
15
21
  this.converter = converter;
16
22
  this.logger = logger;
17
- this.cacheTTL = 300000; // 5 minutes
18
23
  }
19
24
  async getVerifiedSenders(useCache = true) {
20
25
  // Return cached data if valid
@@ -18,11 +18,22 @@ const template_manager_1 = require("./template-manager");
18
18
  const sender_manager_1 = require("./sender-manager");
19
19
  const sync_manager_1 = require("./sync-manager");
20
20
  class SendGridTemplatePlugin {
21
+ name = 'sendgrid-templates';
22
+ version = '1.0.0';
23
+ providerId = 'sendgrid';
24
+ client;
25
+ rateLimiter;
26
+ ownershipValidator;
27
+ prefixEnforcer;
28
+ templateConverter;
29
+ senderConverter;
30
+ templateManager;
31
+ senderManager;
32
+ syncManager;
33
+ config;
34
+ logger;
35
+ initialized = false;
21
36
  constructor(config) {
22
- this.name = 'sendgrid-templates';
23
- this.version = '1.0.0';
24
- this.providerId = 'sendgrid';
25
- this.initialized = false;
26
37
  this.config = config;
27
38
  }
28
39
  async initialize(__pluginConfig) {
@@ -9,6 +9,10 @@ Redistribution or use in other products or commercial offerings is not permitted
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.SyncManager = void 0;
11
11
  class SyncManager {
12
+ templateManager;
13
+ converter;
14
+ client;
15
+ logger;
12
16
  constructor(templateManager, converter, client, logger) {
13
17
  this.templateManager = templateManager;
14
18
  this.converter = converter;
@@ -10,6 +10,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.TemplateManager = void 0;
11
11
  const errors_1 = require("../api/errors");
12
12
  class TemplateManager {
13
+ client;
14
+ ownershipValidator;
15
+ prefixEnforcer;
16
+ converter;
17
+ logger;
13
18
  constructor(client, ownershipValidator, prefixEnforcer, converter, logger) {
14
19
  this.client = client;
15
20
  this.ownershipValidator = ownershipValidator;
@@ -10,8 +10,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.OwnershipValidator = void 0;
11
11
  const errors_1 = require("../api/errors");
12
12
  class OwnershipValidator {
13
+ appTemplateIds = new Set();
14
+ safetyMode;
15
+ logger;
13
16
  constructor(safetyMode, logger) {
14
- this.appTemplateIds = new Set();
15
17
  this.safetyMode = safetyMode;
16
18
  this.logger = logger;
17
19
  }
@@ -9,6 +9,7 @@ Redistribution or use in other products or commercial offerings is not permitted
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.PrefixEnforcer = void 0;
11
11
  class PrefixEnforcer {
12
+ prefix;
12
13
  constructor(prefix) {
13
14
  this.prefix = prefix;
14
15
  }
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@bernierllc/email-sendgrid-plugin",
3
- "version": "1.4.4",
3
+ "version": "1.6.0",
4
4
  "deprecated": "This package is deprecated. SendGrid functionality has been merged into @bernierllc/email-sender@3.0.0+. See https://github.com/BernierLLC/tools/blob/main/packages/core/email-sender/MIGRATION.md for migration guide.",
5
5
  "description": "SendGrid-specific template management, sender verification, and advanced features plugin for email template service",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist/**/*",
10
+ "README.md"
11
+ ],
8
12
  "keywords": [
9
13
  "sendgrid",
10
14
  "email",
@@ -17,9 +21,9 @@
17
21
  "license": "SEE LICENSE IN LICENSE",
18
22
  "dependencies": {
19
23
  "@sendgrid/mail": "^8.1.3",
20
- "@bernierllc/logger": "1.4.3",
21
- "@bernierllc/rate-limiter": "0.4.0",
22
- "@bernierllc/retry-policy": "0.5.1"
24
+ "@bernierllc/logger": "1.6.0",
25
+ "@bernierllc/rate-limiter": "0.5.0",
26
+ "@bernierllc/retry-policy": "0.6.0"
23
27
  },
24
28
  "devDependencies": {
25
29
  "@types/jest": "^29.5.12",
package/.eslintrc.js DELETED
@@ -1,27 +0,0 @@
1
- /*
2
- Copyright (c) 2025 Bernier LLC
3
-
4
- This file is licensed to the client under a limited-use license.
5
- The client may use and modify this code *only within the scope of the project it was delivered for*.
6
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
- */
8
-
9
- module.exports = {
10
- parser: '@typescript-eslint/parser',
11
- extends: [
12
- 'eslint:recommended',
13
- 'plugin:@typescript-eslint/recommended',
14
- ],
15
- parserOptions: {
16
- ecmaVersion: 2020,
17
- sourceType: 'module',
18
- },
19
- rules: {
20
- '@typescript-eslint/no-explicit-any': 'warn',
21
- '@typescript-eslint/explicit-module-boundary-types': 'off',
22
- '@typescript-eslint/no-unused-vars': ['error', {
23
- 'argsIgnorePattern': '^_+',
24
- 'varsIgnorePattern': '^_+'
25
- }],
26
- },
27
- };
package/CHANGELOG.md DELETED
@@ -1,109 +0,0 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- ## [1.4.4](https://github.com/bernierllc/tools/compare/@bernierllc/email-sendgrid-plugin@1.4.3...@bernierllc/email-sendgrid-plugin@1.4.4) (2026-05-19)
7
-
8
- **Note:** Version bump only for package @bernierllc/email-sendgrid-plugin
9
-
10
-
11
-
12
-
13
-
14
- ## [1.4.3](https://github.com/bernierllc/tools/compare/@bernierllc/email-sendgrid-plugin@1.4.2...@bernierllc/email-sendgrid-plugin@1.4.3) (2026-05-19)
15
-
16
- **Note:** Version bump only for package @bernierllc/email-sendgrid-plugin
17
-
18
-
19
-
20
-
21
-
22
- ## [1.4.2](https://github.com/bernierllc/tools/compare/@bernierllc/email-sendgrid-plugin@1.4.1...@bernierllc/email-sendgrid-plugin@1.4.2) (2026-03-30)
23
-
24
- **Note:** Version bump only for package @bernierllc/email-sendgrid-plugin
25
-
26
-
27
-
28
-
29
-
30
- ## [1.4.1](https://github.com/bernierllc/tools/compare/@bernierllc/email-sendgrid-plugin@1.4.0...@bernierllc/email-sendgrid-plugin@1.4.1) (2026-03-09)
31
-
32
- **Note:** Version bump only for package @bernierllc/email-sendgrid-plugin
33
-
34
-
35
-
36
-
37
-
38
- # [1.4.0](https://github.com/bernierllc/tools/compare/@bernierllc/email-sendgrid-plugin@1.3.0...@bernierllc/email-sendgrid-plugin@1.4.0) (2026-03-03)
39
-
40
-
41
- ### Bug Fixes
42
-
43
- * **email-sendgrid-plugin:** use workspace:* for internal dependencies ([5846df1](https://github.com/bernierllc/tools/commit/5846df165069987cac4f61dc44c2e09e403e8b66))
44
-
45
-
46
- ### Features
47
-
48
- * **email:** add multi-provider capability matrix with smart routing ([#29](https://github.com/bernierllc/tools/issues/29)) ([b9c38c0](https://github.com/bernierllc/tools/commit/b9c38c0a93985dc1675681657cc2ec3a209a9435))
49
-
50
-
51
-
52
-
53
-
54
- # 1.3.0 (2025-12-25)
55
-
56
-
57
- ### Bug Fixes
58
-
59
- * **ci:** add lerna version step before publish ([3d20300](https://github.com/bernierllc/tools/commit/3d203002143bf353fffafe4f8a78a99009567347))
60
- * **ci:** correct jest testEnvironment paths for CI ([c47144c](https://github.com/bernierllc/tools/commit/c47144c3d78aec2be1e1454066c235c2227823fd))
61
- * **ci:** fix release workflow + convert internal deps to workspace:* ([01c078b](https://github.com/bernierllc/tools/commit/01c078b49d6025f7eef750f79207a1c71c8d85dc))
62
- * **jest:** resolve Node.js v25+ localStorage SecurityError ([8ffc512](https://github.com/bernierllc/tools/commit/8ffc512805519bc5ba523bdbcd1c457e84998c9e))
63
- * update neverhub-adapter deps to workspace:* and publish 0.1.2 ([f0e3d04](https://github.com/bernierllc/tools/commit/f0e3d04d8d4f094e3bb899ddf81e93243d16e2c2))
64
-
65
-
66
- ### Features
67
-
68
- * **email-sendgrid-plugin:** Implement SendGrid template management and sender verification plugin ([5368de9](https://github.com/bernierllc/tools/commit/5368de95d03888e5561fa0d01281f603b3f4a6f1))
69
- * **email:** deprecate email-sendgrid-plugin and document consolidation status ([724e37c](https://github.com/bernierllc/tools/commit/724e37ca80dac6ddea112fdc1129fb78018c1c82))
70
-
71
-
72
-
73
-
74
-
75
- # 1.2.0 (2025-12-25)
76
-
77
-
78
- ### Bug Fixes
79
-
80
- * **ci:** correct jest testEnvironment paths for CI ([c47144c](https://github.com/bernierllc/tools/commit/c47144c3d78aec2be1e1454066c235c2227823fd))
81
- * **ci:** fix release workflow + convert internal deps to workspace:* ([01c078b](https://github.com/bernierllc/tools/commit/01c078b49d6025f7eef750f79207a1c71c8d85dc))
82
- * **jest:** resolve Node.js v25+ localStorage SecurityError ([8ffc512](https://github.com/bernierllc/tools/commit/8ffc512805519bc5ba523bdbcd1c457e84998c9e))
83
- * update neverhub-adapter deps to workspace:* and publish 0.1.2 ([f0e3d04](https://github.com/bernierllc/tools/commit/f0e3d04d8d4f094e3bb899ddf81e93243d16e2c2))
84
-
85
-
86
- ### Features
87
-
88
- * **email-sendgrid-plugin:** Implement SendGrid template management and sender verification plugin ([5368de9](https://github.com/bernierllc/tools/commit/5368de95d03888e5561fa0d01281f603b3f4a6f1))
89
- * **email:** deprecate email-sendgrid-plugin and document consolidation status ([724e37c](https://github.com/bernierllc/tools/commit/724e37ca80dac6ddea112fdc1129fb78018c1c82))
90
-
91
-
92
-
93
-
94
-
95
- # @bernierllc/email-sendgrid-plugin
96
-
97
- ## 1.1.2
98
-
99
- ### Patch Changes
100
-
101
- - Updated dependencies [[`73c1d1e`](https://github.com/bernierllc/tools/commit/73c1d1e46114cf2ccfd5b74b83743361247f4502)]:
102
- - @bernierllc/logger@1.1.0
103
-
104
- ## 1.1.1
105
-
106
- ### Patch Changes
107
-
108
- - Updated dependencies [[`24899fa`](https://github.com/bernierllc/tools/commit/24899fa4eaf80ae329f7bd74483cf97a132f5d4c)]:
109
- - @bernierllc/logger@1.0.4
@@ -1,125 +0,0 @@
1
- /*
2
- Copyright (c) 2025 Bernier LLC
3
-
4
- This file is licensed to the client under a limited-use license.
5
- The client may use and modify this code *only within the scope of the project it was delivered for*.
6
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
- */
8
-
9
- import { SimpleRateLimiter } from '../../src/api/rate-limiter';
10
- import { RateLimitError } from '../../src/api/errors';
11
-
12
- describe('SimpleRateLimiter', () => {
13
- describe('constructor', () => {
14
- it('should create rate limiter with specified limits', () => {
15
- const limiter = new SimpleRateLimiter({
16
- maxRequestsPerSecond: 10,
17
- maxRequestsPerMinute: 100
18
- });
19
-
20
- expect(limiter).toBeDefined();
21
- expect(limiter.getRemainingCapacity()).toEqual({
22
- perSecond: 10,
23
- perMinute: 100
24
- });
25
- });
26
- });
27
-
28
- describe('acquire', () => {
29
- it('should allow requests within per-second limit', async () => {
30
- const limiter = new SimpleRateLimiter({
31
- maxRequestsPerSecond: 5,
32
- maxRequestsPerMinute: 100
33
- });
34
-
35
- // Should allow 5 requests without delay
36
- for (let i = 0; i < 5; i++) {
37
- await expect(limiter.acquire()).resolves.toBeUndefined();
38
- }
39
- });
40
-
41
- it('should enforce per-second rate limit', async () => {
42
- const limiter = new SimpleRateLimiter({
43
- maxRequestsPerSecond: 2,
44
- maxRequestsPerMinute: 100
45
- });
46
-
47
- // Use up per-second quota
48
- await limiter.acquire();
49
- await limiter.acquire();
50
-
51
- const start = Date.now();
52
- await limiter.acquire(); // Should wait
53
- const elapsed = Date.now() - start;
54
-
55
- expect(elapsed).toBeGreaterThanOrEqual(900); // Allow some margin
56
- });
57
-
58
- it('should throw error if per-minute limit wait time exceeds threshold', async () => {
59
- const limiter = new SimpleRateLimiter({
60
- maxRequestsPerSecond: 100,
61
- maxRequestsPerMinute: 2
62
- });
63
-
64
- // Use up per-minute quota
65
- await limiter.acquire();
66
- await limiter.acquire();
67
-
68
- // Next request should throw since wait > 5s
69
- await expect(limiter.acquire()).rejects.toThrow(RateLimitError);
70
- });
71
-
72
- it.skip('should clean up old timestamps (test too slow)', async () => {
73
- // This test would take >60s to run, skipping for CI/CD speed
74
- // Manual verification: timestamps older than 60s are filtered out
75
- });
76
- });
77
-
78
- describe('getRemainingCapacity', () => {
79
- it('should return correct remaining capacity', async () => {
80
- const limiter = new SimpleRateLimiter({
81
- maxRequestsPerSecond: 5,
82
- maxRequestsPerMinute: 50
83
- });
84
-
85
- // Initial capacity
86
- expect(limiter.getRemainingCapacity()).toEqual({
87
- perSecond: 5,
88
- perMinute: 50
89
- });
90
-
91
- // After 2 requests
92
- await limiter.acquire();
93
- await limiter.acquire();
94
-
95
- expect(limiter.getRemainingCapacity()).toEqual({
96
- perSecond: 3,
97
- perMinute: 48
98
- });
99
- });
100
-
101
- it('should never return negative capacity', async () => {
102
- const limiter = new SimpleRateLimiter({
103
- maxRequestsPerSecond: 1,
104
- maxRequestsPerMinute: 10
105
- });
106
-
107
- await limiter.acquire();
108
-
109
- const capacity = limiter.getRemainingCapacity();
110
- expect(capacity.perSecond).toBeGreaterThanOrEqual(0);
111
- expect(capacity.perMinute).toBeGreaterThanOrEqual(0);
112
- });
113
- });
114
-
115
- describe('release', () => {
116
- it('should be a no-op', () => {
117
- const limiter = new SimpleRateLimiter({
118
- maxRequestsPerSecond: 10,
119
- maxRequestsPerMinute: 100
120
- });
121
-
122
- expect(() => limiter.release()).not.toThrow();
123
- });
124
- });
125
- });