@friggframework/core 2.0.0--canary.658.a5177aa.0 → 2.0.0--canary.656.4fb07b4.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.
@@ -44,21 +44,12 @@ declare module "@friggframework/module-plugin" {
44
44
  };
45
45
 
46
46
  export type RateLimitPolicy = {
47
- /** The key a limit counts against. Default `entity`. */
48
47
  scope?: RateLimitScope;
49
48
  windows?: RateLimitWindow[];
50
49
  maxConcurrency?: number;
51
- /** A wait never shorter than this, for every throttled response. */
52
50
  minRetryAfterMs?: number;
53
- /** The most a request sleeps in process, in total. Default 300000. */
54
51
  maxInProcessWaitMs?: number;
55
- /** Header parsers, in order. Default all three. */
56
52
  parsers?: Array<"retryAfter" | "resetHeaders" | "ietf">;
57
- /**
58
- * Recognises a limit that is not a plain 429, or names its reason. Returns
59
- * null when the response is not a limit. A hint with no time takes its
60
- * time from the parsers or the policy.
61
- */
62
53
  classify?(
63
54
  signal: RateLimitSignal
64
55
  ): Partial<Omit<RateLimitHint, "source">> & {
@@ -75,8 +66,6 @@ declare module "@friggframework/module-plugin" {
75
66
  static requestTimeoutMs?: number;
76
67
 
77
68
  DLGT_INVALID_AUTH: string;
78
- /** Notified with the `RateLimitError` right before a wait that is too long throws. */
79
- DLGT_RATE_LIMITED: string;
80
69
  requestTimeoutMs: number;
81
70
  backOff: number[];
82
71
  fetch: any;
@@ -113,7 +102,6 @@ declare module "@friggframework/module-plugin" {
113
102
  isRefreshable: boolean;
114
103
  refreshCount: number;
115
104
  DLGT_INVALID_AUTH: string;
116
- DLGT_RATE_LIMITED: string;
117
105
  fetch: any;
118
106
 
119
107
  parseBody(response: any): Promise<any>;
@@ -142,6 +130,7 @@ declare module "@friggframework/module-plugin" {
142
130
  type RequesterConstructor = {
143
131
  backOff?: number[];
144
132
  fetch?: any;
133
+ random?: () => number;
145
134
  };
146
135
 
147
136
  export class ApiKeyRequester
@@ -1,24 +0,0 @@
1
- const isItem = (value) =>
2
- value !== null && typeof value === 'object' && !Array.isArray(value);
3
-
4
- /**
5
- * The stored shape of a message, for IntegrationRepository
6
- * .updateIntegrationMessages(). The method takes the positional form (title,
7
- * body, timestamp) or one item object. The keys of an item object are stored
8
- * as they are, so a caller can attach what a client acts on, like a code or
9
- * a list of actions.
10
- * @param {string|Object} titleOrItem - The title, or the whole item.
11
- * @param {string} [messageBody] - Ignored when an item is given.
12
- * @param {number|Date} [messageTimestamp] - Ignored when an item is given.
13
- * @returns {Object} A new object; the given item is never returned itself.
14
- */
15
- function toMessageItem(titleOrItem, messageBody, messageTimestamp) {
16
- if (isItem(titleOrItem)) return { ...titleOrItem };
17
- return {
18
- title: titleOrItem,
19
- message: messageBody,
20
- timestamp: messageTimestamp,
21
- };
22
- }
23
-
24
- module.exports = { toMessageItem };
@@ -1,85 +0,0 @@
1
- const RATE_LIMITED = 'RATE_LIMITED';
2
- const SAME_LIMIT_WINDOW_MS = 60_000;
3
- const MINUTE_MS = 60_000;
4
-
5
- const isLink = (link) =>
6
- typeof link?.label === 'string' && typeof link?.url === 'string';
7
-
8
- const linkActions = (links) =>
9
- (Array.isArray(links) ? links : [])
10
- .filter(isLink)
11
- .map(({ label, url }) => ({ type: 'LINK', label, url }));
12
-
13
- function utcMinuteAfter(date) {
14
- const minute = new Date(Math.ceil(date.getTime() / MINUTE_MS) * MINUTE_MS);
15
- return `${minute.toISOString().slice(0, 16).replace('T', ' ')} UTC`;
16
- }
17
-
18
- /**
19
- * Use case that records one user-facing warning when a module reports a rate
20
- * limit that is too long to wait for. The text is fixed and the payload's
21
- * message, url, body and headers are never read, because the message reaches
22
- * end users. It changes no integration status: a rate limit is not an error.
23
- * @class RecordRateLimitMessage
24
- */
25
- class RecordRateLimitMessage {
26
- /**
27
- * @param {Object} params - Configuration parameters.
28
- * @param {import('../repositories/integration-repository-interface').IntegrationRepositoryInterface} params.integrationRepository - Repository for integration data operations.
29
- */
30
- constructor({ integrationRepository }) {
31
- this.integrationRepository = integrationRepository;
32
- }
33
-
34
- /**
35
- * Writes the warning, unless the stored warnings already hold one for the
36
- * same module whose reset time is within a minute of this one. Many workers
37
- * hit one limit together and each reports it, so the check reads the
38
- * stored warnings and not the messages of this instance.
39
- * @async
40
- * @param {string} integrationId - ID of the integration to warn on.
41
- * @param {Object} payload - The RATE_LIMITED payload of the Module.
42
- * @param {string} payload.moduleName
43
- * @param {string} payload.reason
44
- * @param {Date|string|number} payload.retryAt
45
- * @param {Array<{label: string, url: string}>} [payload.links]
46
- * @returns {Promise<boolean>} True when it wrote a warning.
47
- * @throws When the read or the write fails; the caller decides what a
48
- * failed warning is worth.
49
- */
50
- async execute(integrationId, { moduleName, reason, retryAt, links }) {
51
- const resetAt = new Date(retryAt);
52
- const warnings =
53
- await this.integrationRepository.findIntegrationMessages(
54
- integrationId,
55
- 'warnings'
56
- );
57
- const alreadyRecorded = warnings.some(
58
- (warning) =>
59
- warning?.code === RATE_LIMITED &&
60
- warning.module === moduleName &&
61
- Math.abs(Date.parse(warning.retryAt) - resetAt.getTime()) <=
62
- SAME_LIMIT_WINDOW_MS
63
- );
64
- if (alreadyRecorded) return false;
65
-
66
- const resetsAt = utcMinuteAfter(resetAt);
67
- await this.integrationRepository.updateIntegrationMessages(
68
- integrationId,
69
- 'warnings',
70
- {
71
- title: 'Rate limit reached',
72
- message: `The ${moduleName} API rate limit was reached and resets at ${resetsAt}.`,
73
- timestamp: Date.now(),
74
- code: RATE_LIMITED,
75
- module: moduleName,
76
- reason,
77
- retryAt: resetAt.toISOString(),
78
- actions: [{ type: 'RETRY_WHEN_READY' }, ...linkActions(links)],
79
- }
80
- );
81
- return true;
82
- }
83
- }
84
-
85
- module.exports = { RecordRateLimitMessage };