@project-ajax/sdk 0.0.76 → 0.0.77
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/capabilities/sync.d.ts +0 -4
- package/dist/capabilities/sync.d.ts.map +1 -1
- package/dist/capabilities/sync.js +1 -11
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/package.json +1 -5
- package/src/capabilities/sync.ts +0 -19
- package/src/index.ts +0 -2
- package/dist/pacer.d.ts +0 -50
- package/dist/pacer.d.ts.map +0 -1
- package/dist/pacer.js +0 -48
- package/dist/pacer.test.d.ts +0 -2
- package/dist/pacer.test.d.ts.map +0 -1
- package/dist/pacer_internal.d.ts +0 -6
- package/dist/pacer_internal.d.ts.map +0 -1
- package/dist/pacer_internal.js +0 -11
- package/src/pacer.test.ts +0 -41
- package/src/pacer.ts +0 -80
- package/src/pacer_internal.ts +0 -21
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type PacerEntry } from "../pacer_internal.js";
|
|
2
1
|
import type { PropertyConfiguration, PropertySchema, Schema } from "../schema.js";
|
|
3
2
|
import type { Icon, PeopleValue, PlaceValue, RelationValue, Schedule, SyncSchedule, TextValue } from "../types.js";
|
|
4
3
|
import type { CapabilityContext } from "./context.js";
|
|
@@ -154,8 +153,6 @@ type RuntimeContext<UserContext = unknown> = {
|
|
|
154
153
|
state?: UserContext;
|
|
155
154
|
/** Legacy field for user-defined/-controlled state. */
|
|
156
155
|
userContext?: UserContext;
|
|
157
|
-
/** Pacer state from the server for rate limiting. */
|
|
158
|
-
pacers?: Record<string, PacerEntry>;
|
|
159
156
|
};
|
|
160
157
|
/**
|
|
161
158
|
* Creates a special handler for syncing third-party data to a collection.
|
|
@@ -177,7 +174,6 @@ export declare function createSyncCapability<PK extends string, S extends Schema
|
|
|
177
174
|
changes: SyncChange<PK, PropertySchema<PK>>[];
|
|
178
175
|
hasMore: boolean;
|
|
179
176
|
nextUserContext: Context | undefined;
|
|
180
|
-
nextPacerState: Record<string, PacerEntry>;
|
|
181
177
|
}>;
|
|
182
178
|
};
|
|
183
179
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/capabilities/sync.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/capabilities/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { ExecutionError, unreachable } from "../error.js";
|
|
2
|
-
import {
|
|
3
|
-
getPacerState,
|
|
4
|
-
setPacerState
|
|
5
|
-
} from "../pacer_internal.js";
|
|
6
2
|
import { createCapabilityContext } from "./context.js";
|
|
7
3
|
function createSyncCapability(key, syncConfiguration) {
|
|
8
4
|
return {
|
|
@@ -17,19 +13,13 @@ function createSyncCapability(key, syncConfiguration) {
|
|
|
17
13
|
async handler(runtimeContext) {
|
|
18
14
|
const capabilityContext = createCapabilityContext();
|
|
19
15
|
const state = runtimeContext?.state ?? runtimeContext?.userContext;
|
|
20
|
-
const pacerState = {
|
|
21
|
-
pacers: runtimeContext?.pacers ?? {}
|
|
22
|
-
};
|
|
23
|
-
setPacerState(pacerState);
|
|
24
16
|
const executionResult = await syncConfiguration.execute(state, capabilityContext).catch((err) => {
|
|
25
17
|
throw new ExecutionError(err);
|
|
26
18
|
});
|
|
27
|
-
const updatedPacerState = getPacerState();
|
|
28
19
|
const result = {
|
|
29
20
|
changes: executionResult.changes,
|
|
30
21
|
hasMore: executionResult.hasMore,
|
|
31
|
-
nextUserContext: executionResult.nextState
|
|
32
|
-
nextPacerState: updatedPacerState.pacers
|
|
22
|
+
nextUserContext: executionResult.nextState
|
|
33
23
|
};
|
|
34
24
|
process.stdout.write(`
|
|
35
25
|
<output>${JSON.stringify(result)}</output>
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ export type { CapabilityContext } from "./capabilities/context.js";
|
|
|
4
4
|
export type { NotionManagedOAuthConfiguration, OAuthCapability, OAuthConfiguration, UserManagedOAuthConfiguration, } from "./capabilities/oauth.js";
|
|
5
5
|
export type { SyncCapability, SyncChange, SyncChangeDelete, SyncChangeUpsert, SyncConfiguration, SyncExecutionResult, SyncMode, } from "./capabilities/sync.js";
|
|
6
6
|
export type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
|
|
7
|
-
export { Pacer } from "./pacer.js";
|
|
8
7
|
export type { Icon, ImageIcon, NoticonColor, NoticonName, PlaceValue, Schedule, } from "./types.js";
|
|
9
8
|
export { Worker } from "./worker.js";
|
|
10
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACvE,YAAY,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACvE,YAAY,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChF,YAAY,EACX,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,EACV,QAAQ,GACR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@project-ajax/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.77",
|
|
4
4
|
"description": "An SDK for building workers for the Project Ajax platform",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -28,10 +28,6 @@
|
|
|
28
28
|
"./types": {
|
|
29
29
|
"types": "./dist/types.d.ts",
|
|
30
30
|
"default": "./dist/types.js"
|
|
31
|
-
},
|
|
32
|
-
"./pacer": {
|
|
33
|
-
"types": "./dist/pacer.d.ts",
|
|
34
|
-
"default": "./dist/pacer.js"
|
|
35
31
|
}
|
|
36
32
|
},
|
|
37
33
|
"engines": {
|
package/src/capabilities/sync.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { ExecutionError, unreachable } from "../error.js";
|
|
2
|
-
import {
|
|
3
|
-
getPacerState,
|
|
4
|
-
type PacerEntry,
|
|
5
|
-
type PacerState,
|
|
6
|
-
setPacerState,
|
|
7
|
-
} from "../pacer_internal.js";
|
|
8
2
|
import type {
|
|
9
3
|
PropertyConfiguration,
|
|
10
4
|
PropertySchema,
|
|
@@ -203,8 +197,6 @@ type RuntimeContext<UserContext = unknown> = {
|
|
|
203
197
|
state?: UserContext;
|
|
204
198
|
/** Legacy field for user-defined/-controlled state. */
|
|
205
199
|
userContext?: UserContext;
|
|
206
|
-
/** Pacer state from the server for rate limiting. */
|
|
207
|
-
pacers?: Record<string, PacerEntry>;
|
|
208
200
|
};
|
|
209
201
|
|
|
210
202
|
/**
|
|
@@ -231,27 +223,16 @@ export function createSyncCapability<
|
|
|
231
223
|
async handler(runtimeContext?: RuntimeContext<Context>) {
|
|
232
224
|
const capabilityContext = createCapabilityContext();
|
|
233
225
|
const state = runtimeContext?.state ?? runtimeContext?.userContext;
|
|
234
|
-
|
|
235
|
-
// Initialize pacer state from runtime context
|
|
236
|
-
const pacerState: PacerState = {
|
|
237
|
-
pacers: runtimeContext?.pacers ?? {},
|
|
238
|
-
};
|
|
239
|
-
setPacerState(pacerState);
|
|
240
|
-
|
|
241
226
|
const executionResult = await syncConfiguration
|
|
242
227
|
.execute(state, capabilityContext)
|
|
243
228
|
.catch((err) => {
|
|
244
229
|
throw new ExecutionError(err);
|
|
245
230
|
});
|
|
246
231
|
|
|
247
|
-
// Get updated pacer state after execution
|
|
248
|
-
const updatedPacerState = getPacerState();
|
|
249
|
-
|
|
250
232
|
const result = {
|
|
251
233
|
changes: executionResult.changes,
|
|
252
234
|
hasMore: executionResult.hasMore,
|
|
253
235
|
nextUserContext: executionResult.nextState,
|
|
254
|
-
nextPacerState: updatedPacerState.pacers,
|
|
255
236
|
};
|
|
256
237
|
|
|
257
238
|
process.stdout.write(`\n<output>${JSON.stringify(result)}</output>\n`);
|
package/src/index.ts
CHANGED
|
@@ -22,8 +22,6 @@ export type {
|
|
|
22
22
|
SyncMode,
|
|
23
23
|
} from "./capabilities/sync.js";
|
|
24
24
|
export type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
|
|
25
|
-
// Pacer module re-exported for convenience (users can also import from "@project-ajax/sdk/pacer")
|
|
26
|
-
export { Pacer } from "./pacer.js";
|
|
27
25
|
export type {
|
|
28
26
|
Icon,
|
|
29
27
|
ImageIcon,
|
package/dist/pacer.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pacer module for rate limiting API requests.
|
|
3
|
-
*
|
|
4
|
-
* The Pacer ensures requests are evenly spaced over time to respect third-party
|
|
5
|
-
* API rate limits. Instead of making all requests immediately until hitting a 429,
|
|
6
|
-
* Pacer paces requests throughout the rate limit window.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* import { Pacer } from "@project-ajax/sdk/pacer"
|
|
11
|
-
*
|
|
12
|
-
* // Rate limit: 10 requests per minute
|
|
13
|
-
* await Pacer.wait("salesforce-api", { requests: 10, intervalMs: 60 * 1000 })
|
|
14
|
-
*
|
|
15
|
-
* // Now make your API call
|
|
16
|
-
* const data = await fetchFromSalesforce()
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* @module
|
|
20
|
-
*/
|
|
21
|
-
export type PacerLimit = {
|
|
22
|
-
requests: number;
|
|
23
|
-
intervalMs: number;
|
|
24
|
-
};
|
|
25
|
-
export declare const Pacer: {
|
|
26
|
-
/**
|
|
27
|
-
* Wait until a request can proceed under the specified rate limit.
|
|
28
|
-
*
|
|
29
|
-
* This function paces requests evenly across the rate limit interval. For example,
|
|
30
|
-
* with 60 requests allowed per hour, requests are spaced approximately 1 minute apart.
|
|
31
|
-
*
|
|
32
|
-
* @param key - Unique identifier for this rate limit scope (e.g., "salesforce-api", "jira:token123"). If you're not sure, use any string.
|
|
33
|
-
* @param limit - Rate limit configuration: `requests` per `intervalMs`.
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```ts
|
|
37
|
-
* // Salesforce: 30,000 requests per 24 hours
|
|
38
|
-
* await Pacer.wait("salesforce", { requests: 30_000, intervalMs: 24 * 60 * 60 * 1000 })
|
|
39
|
-
*
|
|
40
|
-
* // Jira: 100 requests per minute
|
|
41
|
-
* await Pacer.wait("jira", { requests: 100, intervalMs: 60 * 1000 })
|
|
42
|
-
*
|
|
43
|
-
* // Multiple rate limits for different endpoints
|
|
44
|
-
* await Pacer.wait("api-read", { requests: 1000, intervalMs: 60 * 1000 })
|
|
45
|
-
* await Pacer.wait("api-write", { requests: 100, intervalMs: 60 * 1000 })
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
wait(key: string, limit: PacerLimit): Promise<void>;
|
|
49
|
-
};
|
|
50
|
-
//# sourceMappingURL=pacer.d.ts.map
|
package/dist/pacer.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pacer.d.ts","sourceRoot":"","sources":["../src/pacer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,MAAM,MAAM,UAAU,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,KAAK;IACjB;;;;;;;;;;;;;;;;;;;;;OAqBG;cACa,MAAM,SAAS,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CA4BzD,CAAC"}
|
package/dist/pacer.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { getPacerState, setPacerState } from "./pacer_internal.js";
|
|
2
|
-
const Pacer = {
|
|
3
|
-
/**
|
|
4
|
-
* Wait until a request can proceed under the specified rate limit.
|
|
5
|
-
*
|
|
6
|
-
* This function paces requests evenly across the rate limit interval. For example,
|
|
7
|
-
* with 60 requests allowed per hour, requests are spaced approximately 1 minute apart.
|
|
8
|
-
*
|
|
9
|
-
* @param key - Unique identifier for this rate limit scope (e.g., "salesforce-api", "jira:token123"). If you're not sure, use any string.
|
|
10
|
-
* @param limit - Rate limit configuration: `requests` per `intervalMs`.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* // Salesforce: 30,000 requests per 24 hours
|
|
15
|
-
* await Pacer.wait("salesforce", { requests: 30_000, intervalMs: 24 * 60 * 60 * 1000 })
|
|
16
|
-
*
|
|
17
|
-
* // Jira: 100 requests per minute
|
|
18
|
-
* await Pacer.wait("jira", { requests: 100, intervalMs: 60 * 1000 })
|
|
19
|
-
*
|
|
20
|
-
* // Multiple rate limits for different endpoints
|
|
21
|
-
* await Pacer.wait("api-read", { requests: 1000, intervalMs: 60 * 1000 })
|
|
22
|
-
* await Pacer.wait("api-write", { requests: 100, intervalMs: 60 * 1000 })
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
async wait(key, limit) {
|
|
26
|
-
if (limit.requests <= 0) {
|
|
27
|
-
throw new Error("requests must be greater than 0");
|
|
28
|
-
}
|
|
29
|
-
if (limit.intervalMs <= 0) {
|
|
30
|
-
throw new Error("intervalMs must be greater than 0");
|
|
31
|
-
}
|
|
32
|
-
const now = Date.now();
|
|
33
|
-
const paceMs = Math.ceil(limit.intervalMs / limit.requests);
|
|
34
|
-
const state = getPacerState();
|
|
35
|
-
const existing = state.pacers[key];
|
|
36
|
-
const lastScheduledAtMs = existing?.lastScheduledAtMs ?? 0;
|
|
37
|
-
const scheduledAtMs = Math.max(lastScheduledAtMs + paceMs, now);
|
|
38
|
-
const delayMs = scheduledAtMs - now;
|
|
39
|
-
state.pacers[key] = { lastScheduledAtMs: scheduledAtMs };
|
|
40
|
-
setPacerState(state);
|
|
41
|
-
if (delayMs > 0) {
|
|
42
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
export {
|
|
47
|
-
Pacer
|
|
48
|
-
};
|
package/dist/pacer.test.d.ts
DELETED
package/dist/pacer.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pacer.test.d.ts","sourceRoot":"","sources":["../src/pacer.test.ts"],"names":[],"mappings":""}
|
package/dist/pacer_internal.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pacer_internal.d.ts","sourceRoot":"","sources":["../src/pacer_internal.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,UAAU,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACnC,CAAC;AAIF,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAErD;AAED,wBAAgB,aAAa,IAAI,UAAU,CAE1C"}
|
package/dist/pacer_internal.js
DELETED
package/src/pacer.test.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { Pacer } from "./pacer.js";
|
|
4
|
-
import { getPacerState, setPacerState } from "./pacer_internal.js";
|
|
5
|
-
|
|
6
|
-
describe("Pacer.wait", () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
vi.useFakeTimers();
|
|
9
|
-
vi.setSystemTime(0);
|
|
10
|
-
setPacerState({ pacers: {} });
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
vi.useRealTimers();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("paces requests based on the rate limit", async () => {
|
|
18
|
-
const first = Pacer.wait("api", { requests: 10, intervalMs: 60_000 });
|
|
19
|
-
|
|
20
|
-
expect(getPacerState().pacers.api?.lastScheduledAtMs).toBe(6000);
|
|
21
|
-
await vi.advanceTimersByTimeAsync(6000);
|
|
22
|
-
await first;
|
|
23
|
-
|
|
24
|
-
const second = Pacer.wait("api", { requests: 10, intervalMs: 60_000 });
|
|
25
|
-
expect(getPacerState().pacers.api?.lastScheduledAtMs).toBe(12_000);
|
|
26
|
-
await vi.advanceTimersByTimeAsync(6000);
|
|
27
|
-
await second;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("does not wait when the next slot is already in the past", async () => {
|
|
31
|
-
const first = Pacer.wait("api", { requests: 10, intervalMs: 60_000 });
|
|
32
|
-
await vi.advanceTimersByTimeAsync(6000);
|
|
33
|
-
await first;
|
|
34
|
-
|
|
35
|
-
vi.advanceTimersByTime(20_000);
|
|
36
|
-
|
|
37
|
-
const second = Pacer.wait("api", { requests: 10, intervalMs: 60_000 });
|
|
38
|
-
expect(getPacerState().pacers.api?.lastScheduledAtMs).toBe(26_000);
|
|
39
|
-
await second;
|
|
40
|
-
});
|
|
41
|
-
});
|
package/src/pacer.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pacer module for rate limiting API requests.
|
|
3
|
-
*
|
|
4
|
-
* The Pacer ensures requests are evenly spaced over time to respect third-party
|
|
5
|
-
* API rate limits. Instead of making all requests immediately until hitting a 429,
|
|
6
|
-
* Pacer paces requests throughout the rate limit window.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* import { Pacer } from "@project-ajax/sdk/pacer"
|
|
11
|
-
*
|
|
12
|
-
* // Rate limit: 10 requests per minute
|
|
13
|
-
* await Pacer.wait("salesforce-api", { requests: 10, intervalMs: 60 * 1000 })
|
|
14
|
-
*
|
|
15
|
-
* // Now make your API call
|
|
16
|
-
* const data = await fetchFromSalesforce()
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* @module
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { getPacerState, setPacerState } from "./pacer_internal.js";
|
|
23
|
-
|
|
24
|
-
export type PacerLimit = {
|
|
25
|
-
requests: number;
|
|
26
|
-
intervalMs: number;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export const Pacer = {
|
|
30
|
-
/**
|
|
31
|
-
* Wait until a request can proceed under the specified rate limit.
|
|
32
|
-
*
|
|
33
|
-
* This function paces requests evenly across the rate limit interval. For example,
|
|
34
|
-
* with 60 requests allowed per hour, requests are spaced approximately 1 minute apart.
|
|
35
|
-
*
|
|
36
|
-
* @param key - Unique identifier for this rate limit scope (e.g., "salesforce-api", "jira:token123"). If you're not sure, use any string.
|
|
37
|
-
* @param limit - Rate limit configuration: `requests` per `intervalMs`.
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```ts
|
|
41
|
-
* // Salesforce: 30,000 requests per 24 hours
|
|
42
|
-
* await Pacer.wait("salesforce", { requests: 30_000, intervalMs: 24 * 60 * 60 * 1000 })
|
|
43
|
-
*
|
|
44
|
-
* // Jira: 100 requests per minute
|
|
45
|
-
* await Pacer.wait("jira", { requests: 100, intervalMs: 60 * 1000 })
|
|
46
|
-
*
|
|
47
|
-
* // Multiple rate limits for different endpoints
|
|
48
|
-
* await Pacer.wait("api-read", { requests: 1000, intervalMs: 60 * 1000 })
|
|
49
|
-
* await Pacer.wait("api-write", { requests: 100, intervalMs: 60 * 1000 })
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
async wait(key: string, limit: PacerLimit): Promise<void> {
|
|
53
|
-
if (limit.requests <= 0) {
|
|
54
|
-
throw new Error("requests must be greater than 0");
|
|
55
|
-
}
|
|
56
|
-
if (limit.intervalMs <= 0) {
|
|
57
|
-
throw new Error("intervalMs must be greater than 0");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const now = Date.now();
|
|
61
|
-
const paceMs = Math.ceil(limit.intervalMs / limit.requests);
|
|
62
|
-
|
|
63
|
-
const state = getPacerState();
|
|
64
|
-
const existing = state.pacers[key];
|
|
65
|
-
const lastScheduledAtMs = existing?.lastScheduledAtMs ?? 0;
|
|
66
|
-
|
|
67
|
-
// Schedule at the later of: (last scheduled + pace) or now
|
|
68
|
-
const scheduledAtMs = Math.max(lastScheduledAtMs + paceMs, now);
|
|
69
|
-
const delayMs = scheduledAtMs - now;
|
|
70
|
-
|
|
71
|
-
// Update state with new scheduled timestamp
|
|
72
|
-
state.pacers[key] = { lastScheduledAtMs: scheduledAtMs };
|
|
73
|
-
setPacerState(state);
|
|
74
|
-
|
|
75
|
-
// Sleep if needed
|
|
76
|
-
if (delayMs > 0) {
|
|
77
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
};
|
package/src/pacer_internal.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal pacer state helpers used by the runtime.
|
|
3
|
-
* @internal
|
|
4
|
-
*/
|
|
5
|
-
export type PacerEntry = {
|
|
6
|
-
lastScheduledAtMs: number;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export type PacerState = {
|
|
10
|
-
pacers: Record<string, PacerEntry>;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
let pacerState: PacerState = { pacers: {} };
|
|
14
|
-
|
|
15
|
-
export function setPacerState(state: PacerState): void {
|
|
16
|
-
pacerState = state;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function getPacerState(): PacerState {
|
|
20
|
-
return pacerState;
|
|
21
|
-
}
|