@zapier/zapier-sdk 0.18.4 → 0.19.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.
- package/CHANGELOG.md +35 -0
- package/README.md +43 -31
- package/dist/index.cjs +365 -64
- package/dist/index.d.mts +220 -33
- package/dist/index.d.ts +2541 -40
- package/dist/index.mjs +345 -63
- package/package.json +14 -13
- package/dist/api/auth.d.ts +0 -19
- package/dist/api/auth.d.ts.map +0 -1
- package/dist/api/auth.js +0 -70
- package/dist/api/auth.test.d.ts +0 -2
- package/dist/api/auth.test.d.ts.map +0 -1
- package/dist/api/auth.test.js +0 -220
- package/dist/api/client.d.ts +0 -9
- package/dist/api/client.d.ts.map +0 -1
- package/dist/api/client.js +0 -356
- package/dist/api/client.test.d.ts +0 -2
- package/dist/api/client.test.d.ts.map +0 -1
- package/dist/api/client.test.js +0 -96
- package/dist/api/debug.d.ts +0 -14
- package/dist/api/debug.d.ts.map +0 -1
- package/dist/api/debug.js +0 -131
- package/dist/api/debug.test.d.ts +0 -2
- package/dist/api/debug.test.d.ts.map +0 -1
- package/dist/api/debug.test.js +0 -59
- package/dist/api/index.d.ts +0 -30
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/index.js +0 -43
- package/dist/api/polling.d.ts +0 -46
- package/dist/api/polling.d.ts.map +0 -1
- package/dist/api/polling.js +0 -139
- package/dist/api/polling.test.d.ts +0 -2
- package/dist/api/polling.test.d.ts.map +0 -1
- package/dist/api/polling.test.js +0 -318
- package/dist/api/schemas.d.ts +0 -422
- package/dist/api/schemas.d.ts.map +0 -1
- package/dist/api/schemas.js +0 -322
- package/dist/api/types.d.ts +0 -83
- package/dist/api/types.d.ts.map +0 -1
- package/dist/api/types.js +0 -1
- package/dist/auth.d.ts +0 -52
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js +0 -72
- package/dist/auth.test.d.ts +0 -2
- package/dist/auth.test.d.ts.map +0 -1
- package/dist/auth.test.js +0 -102
- package/dist/constants.d.ts +0 -14
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -13
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -42
- package/dist/plugins/api/index.d.ts +0 -12
- package/dist/plugins/api/index.d.ts.map +0 -1
- package/dist/plugins/api/index.js +0 -24
- package/dist/plugins/apps/index.d.ts +0 -15
- package/dist/plugins/apps/index.d.ts.map +0 -1
- package/dist/plugins/apps/index.js +0 -112
- package/dist/plugins/apps/schemas.d.ts +0 -42
- package/dist/plugins/apps/schemas.d.ts.map +0 -1
- package/dist/plugins/apps/schemas.js +0 -14
- package/dist/plugins/eventEmission/builders.d.ts +0 -14
- package/dist/plugins/eventEmission/builders.d.ts.map +0 -1
- package/dist/plugins/eventEmission/builders.js +0 -109
- package/dist/plugins/eventEmission/index.d.ts +0 -38
- package/dist/plugins/eventEmission/index.d.ts.map +0 -1
- package/dist/plugins/eventEmission/index.js +0 -336
- package/dist/plugins/eventEmission/index.test.d.ts +0 -5
- package/dist/plugins/eventEmission/index.test.d.ts.map +0 -1
- package/dist/plugins/eventEmission/index.test.js +0 -581
- package/dist/plugins/eventEmission/transport.d.ts +0 -27
- package/dist/plugins/eventEmission/transport.d.ts.map +0 -1
- package/dist/plugins/eventEmission/transport.js +0 -104
- package/dist/plugins/eventEmission/transport.test.d.ts +0 -5
- package/dist/plugins/eventEmission/transport.test.d.ts.map +0 -1
- package/dist/plugins/eventEmission/transport.test.js +0 -164
- package/dist/plugins/eventEmission/types.d.ts +0 -63
- package/dist/plugins/eventEmission/types.d.ts.map +0 -1
- package/dist/plugins/eventEmission/types.js +0 -1
- package/dist/plugins/eventEmission/utils.d.ts +0 -45
- package/dist/plugins/eventEmission/utils.d.ts.map +0 -1
- package/dist/plugins/eventEmission/utils.js +0 -114
- package/dist/plugins/fetch/index.d.ts +0 -36
- package/dist/plugins/fetch/index.d.ts.map +0 -1
- package/dist/plugins/fetch/index.js +0 -62
- package/dist/plugins/fetch/schemas.d.ts +0 -19
- package/dist/plugins/fetch/schemas.d.ts.map +0 -1
- package/dist/plugins/fetch/schemas.js +0 -31
- package/dist/plugins/findFirstAuthentication/index.d.ts +0 -21
- package/dist/plugins/findFirstAuthentication/index.d.ts.map +0 -1
- package/dist/plugins/findFirstAuthentication/index.js +0 -36
- package/dist/plugins/findFirstAuthentication/index.test.d.ts +0 -2
- package/dist/plugins/findFirstAuthentication/index.test.d.ts.map +0 -1
- package/dist/plugins/findFirstAuthentication/index.test.js +0 -177
- package/dist/plugins/findFirstAuthentication/schemas.d.ts +0 -20
- package/dist/plugins/findFirstAuthentication/schemas.d.ts.map +0 -1
- package/dist/plugins/findFirstAuthentication/schemas.js +0 -18
- package/dist/plugins/findUniqueAuthentication/index.d.ts +0 -21
- package/dist/plugins/findUniqueAuthentication/index.d.ts.map +0 -1
- package/dist/plugins/findUniqueAuthentication/index.js +0 -39
- package/dist/plugins/findUniqueAuthentication/index.test.d.ts +0 -2
- package/dist/plugins/findUniqueAuthentication/index.test.d.ts.map +0 -1
- package/dist/plugins/findUniqueAuthentication/index.test.js +0 -159
- package/dist/plugins/findUniqueAuthentication/schemas.d.ts +0 -20
- package/dist/plugins/findUniqueAuthentication/schemas.d.ts.map +0 -1
- package/dist/plugins/findUniqueAuthentication/schemas.js +0 -18
- package/dist/plugins/getAction/index.d.ts +0 -25
- package/dist/plugins/getAction/index.d.ts.map +0 -1
- package/dist/plugins/getAction/index.js +0 -42
- package/dist/plugins/getAction/index.test.d.ts +0 -2
- package/dist/plugins/getAction/index.test.d.ts.map +0 -1
- package/dist/plugins/getAction/index.test.js +0 -211
- package/dist/plugins/getAction/schemas.d.ts +0 -30
- package/dist/plugins/getAction/schemas.d.ts.map +0 -1
- package/dist/plugins/getAction/schemas.js +0 -12
- package/dist/plugins/getApp/index.d.ts +0 -21
- package/dist/plugins/getApp/index.d.ts.map +0 -1
- package/dist/plugins/getApp/index.js +0 -44
- package/dist/plugins/getApp/index.test.d.ts +0 -2
- package/dist/plugins/getApp/index.test.d.ts.map +0 -1
- package/dist/plugins/getApp/index.test.js +0 -157
- package/dist/plugins/getApp/schemas.d.ts +0 -16
- package/dist/plugins/getApp/schemas.d.ts.map +0 -1
- package/dist/plugins/getApp/schemas.js +0 -8
- package/dist/plugins/getAuthentication/index.d.ts +0 -21
- package/dist/plugins/getAuthentication/index.d.ts.map +0 -1
- package/dist/plugins/getAuthentication/index.js +0 -29
- package/dist/plugins/getAuthentication/index.test.d.ts +0 -2
- package/dist/plugins/getAuthentication/index.test.d.ts.map +0 -1
- package/dist/plugins/getAuthentication/index.test.js +0 -106
- package/dist/plugins/getAuthentication/schemas.d.ts +0 -7
- package/dist/plugins/getAuthentication/schemas.d.ts.map +0 -1
- package/dist/plugins/getAuthentication/schemas.js +0 -1
- package/dist/plugins/getInputFieldsSchema/index.d.ts +0 -23
- package/dist/plugins/getInputFieldsSchema/index.d.ts.map +0 -1
- package/dist/plugins/getInputFieldsSchema/index.js +0 -53
- package/dist/plugins/getInputFieldsSchema/index.test.d.ts +0 -2
- package/dist/plugins/getInputFieldsSchema/index.test.d.ts.map +0 -1
- package/dist/plugins/getInputFieldsSchema/index.test.js +0 -291
- package/dist/plugins/getInputFieldsSchema/schemas.d.ts +0 -28
- package/dist/plugins/getInputFieldsSchema/schemas.d.ts.map +0 -1
- package/dist/plugins/getInputFieldsSchema/schemas.js +0 -13
- package/dist/plugins/getProfile/index.d.ts +0 -25
- package/dist/plugins/getProfile/index.d.ts.map +0 -1
- package/dist/plugins/getProfile/index.js +0 -39
- package/dist/plugins/getProfile/schemas.d.ts +0 -13
- package/dist/plugins/getProfile/schemas.d.ts.map +0 -1
- package/dist/plugins/getProfile/schemas.js +0 -6
- package/dist/plugins/listActions/index.d.ts +0 -30
- package/dist/plugins/listActions/index.d.ts.map +0 -1
- package/dist/plugins/listActions/index.js +0 -75
- package/dist/plugins/listActions/index.test.d.ts +0 -2
- package/dist/plugins/listActions/index.test.d.ts.map +0 -1
- package/dist/plugins/listActions/index.test.js +0 -453
- package/dist/plugins/listActions/schemas.d.ts +0 -35
- package/dist/plugins/listActions/schemas.d.ts.map +0 -1
- package/dist/plugins/listActions/schemas.js +0 -22
- package/dist/plugins/listApps/index.d.ts +0 -19
- package/dist/plugins/listApps/index.d.ts.map +0 -1
- package/dist/plugins/listApps/index.js +0 -67
- package/dist/plugins/listApps/index.test.d.ts +0 -2
- package/dist/plugins/listApps/index.test.d.ts.map +0 -1
- package/dist/plugins/listApps/index.test.js +0 -121
- package/dist/plugins/listApps/schemas.d.ts +0 -47
- package/dist/plugins/listApps/schemas.d.ts.map +0 -1
- package/dist/plugins/listApps/schemas.js +0 -49
- package/dist/plugins/listAuthentications/index.d.ts +0 -24
- package/dist/plugins/listAuthentications/index.d.ts.map +0 -1
- package/dist/plugins/listAuthentications/index.js +0 -77
- package/dist/plugins/listAuthentications/index.test.d.ts +0 -2
- package/dist/plugins/listAuthentications/index.test.d.ts.map +0 -1
- package/dist/plugins/listAuthentications/index.test.js +0 -848
- package/dist/plugins/listAuthentications/schemas.d.ts +0 -30
- package/dist/plugins/listAuthentications/schemas.d.ts.map +0 -1
- package/dist/plugins/listAuthentications/schemas.js +0 -32
- package/dist/plugins/listInputFieldChoices/index.d.ts +0 -33
- package/dist/plugins/listInputFieldChoices/index.d.ts.map +0 -1
- package/dist/plugins/listInputFieldChoices/index.js +0 -115
- package/dist/plugins/listInputFieldChoices/index.test.d.ts +0 -2
- package/dist/plugins/listInputFieldChoices/index.test.d.ts.map +0 -1
- package/dist/plugins/listInputFieldChoices/index.test.js +0 -717
- package/dist/plugins/listInputFieldChoices/schemas.d.ts +0 -43
- package/dist/plugins/listInputFieldChoices/schemas.d.ts.map +0 -1
- package/dist/plugins/listInputFieldChoices/schemas.js +0 -65
- package/dist/plugins/listInputFields/index.d.ts +0 -34
- package/dist/plugins/listInputFields/index.d.ts.map +0 -1
- package/dist/plugins/listInputFields/index.js +0 -204
- package/dist/plugins/listInputFields/index.test.d.ts +0 -2
- package/dist/plugins/listInputFields/index.test.d.ts.map +0 -1
- package/dist/plugins/listInputFields/index.test.js +0 -359
- package/dist/plugins/listInputFields/schemas.d.ts +0 -35
- package/dist/plugins/listInputFields/schemas.d.ts.map +0 -1
- package/dist/plugins/listInputFields/schemas.js +0 -23
- package/dist/plugins/manifest/index.d.ts +0 -85
- package/dist/plugins/manifest/index.d.ts.map +0 -1
- package/dist/plugins/manifest/index.js +0 -376
- package/dist/plugins/manifest/index.test.d.ts +0 -2
- package/dist/plugins/manifest/index.test.d.ts.map +0 -1
- package/dist/plugins/manifest/index.test.js +0 -1139
- package/dist/plugins/manifest/schemas.d.ts +0 -57
- package/dist/plugins/manifest/schemas.d.ts.map +0 -1
- package/dist/plugins/manifest/schemas.js +0 -50
- package/dist/plugins/registry/index.d.ts +0 -21
- package/dist/plugins/registry/index.d.ts.map +0 -1
- package/dist/plugins/registry/index.js +0 -117
- package/dist/plugins/request/index.d.ts +0 -21
- package/dist/plugins/request/index.d.ts.map +0 -1
- package/dist/plugins/request/index.js +0 -76
- package/dist/plugins/request/index.test.d.ts +0 -2
- package/dist/plugins/request/index.test.d.ts.map +0 -1
- package/dist/plugins/request/index.test.js +0 -337
- package/dist/plugins/request/schemas.d.ts +0 -54
- package/dist/plugins/request/schemas.d.ts.map +0 -1
- package/dist/plugins/request/schemas.js +0 -41
- package/dist/plugins/runAction/index.d.ts +0 -33
- package/dist/plugins/runAction/index.d.ts.map +0 -1
- package/dist/plugins/runAction/index.js +0 -108
- package/dist/plugins/runAction/index.test.d.ts +0 -2
- package/dist/plugins/runAction/index.test.d.ts.map +0 -1
- package/dist/plugins/runAction/index.test.js +0 -333
- package/dist/plugins/runAction/schemas.d.ts +0 -34
- package/dist/plugins/runAction/schemas.d.ts.map +0 -1
- package/dist/plugins/runAction/schemas.js +0 -23
- package/dist/resolvers/actionKey.d.ts +0 -15
- package/dist/resolvers/actionKey.d.ts.map +0 -1
- package/dist/resolvers/actionKey.js +0 -19
- package/dist/resolvers/actionType.d.ts +0 -10
- package/dist/resolvers/actionType.d.ts.map +0 -1
- package/dist/resolvers/actionType.js +0 -21
- package/dist/resolvers/appKey.d.ts +0 -3
- package/dist/resolvers/appKey.d.ts.map +0 -1
- package/dist/resolvers/appKey.js +0 -5
- package/dist/resolvers/authenticationId.d.ts +0 -9
- package/dist/resolvers/authenticationId.d.ts.map +0 -1
- package/dist/resolvers/authenticationId.js +0 -42
- package/dist/resolvers/index.d.ts +0 -8
- package/dist/resolvers/index.d.ts.map +0 -1
- package/dist/resolvers/index.js +0 -8
- package/dist/resolvers/inputFieldKey.d.ts +0 -11
- package/dist/resolvers/inputFieldKey.d.ts.map +0 -1
- package/dist/resolvers/inputFieldKey.js +0 -47
- package/dist/resolvers/inputs.d.ts +0 -17
- package/dist/resolvers/inputs.d.ts.map +0 -1
- package/dist/resolvers/inputs.js +0 -50
- package/dist/schemas/Action.d.ts +0 -24
- package/dist/schemas/Action.d.ts.map +0 -1
- package/dist/schemas/Action.js +0 -40
- package/dist/schemas/App.d.ts +0 -57
- package/dist/schemas/App.d.ts.map +0 -1
- package/dist/schemas/App.js +0 -26
- package/dist/schemas/Auth.d.ts +0 -55
- package/dist/schemas/Auth.d.ts.map +0 -1
- package/dist/schemas/Auth.js +0 -33
- package/dist/schemas/Field.d.ts +0 -61
- package/dist/schemas/Field.d.ts.map +0 -1
- package/dist/schemas/Field.js +0 -116
- package/dist/schemas/Run.d.ts +0 -3
- package/dist/schemas/Run.d.ts.map +0 -1
- package/dist/schemas/Run.js +0 -31
- package/dist/schemas/UserProfile.d.ts +0 -13
- package/dist/schemas/UserProfile.d.ts.map +0 -1
- package/dist/schemas/UserProfile.js +0 -32
- package/dist/sdk.d.ts +0 -146
- package/dist/sdk.d.ts.map +0 -1
- package/dist/sdk.js +0 -112
- package/dist/sdk.test.d.ts +0 -2
- package/dist/sdk.test.d.ts.map +0 -1
- package/dist/sdk.test.js +0 -258
- package/dist/services/implementations.d.ts +0 -63
- package/dist/services/implementations.d.ts.map +0 -1
- package/dist/services/implementations.js +0 -80
- package/dist/types/domain.d.ts +0 -52
- package/dist/types/domain.d.ts.map +0 -1
- package/dist/types/domain.js +0 -1
- package/dist/types/domain.test.d.ts +0 -2
- package/dist/types/domain.test.d.ts.map +0 -1
- package/dist/types/domain.test.js +0 -39
- package/dist/types/errors.d.ts +0 -143
- package/dist/types/errors.d.ts.map +0 -1
- package/dist/types/errors.js +0 -187
- package/dist/types/events.d.ts +0 -38
- package/dist/types/events.d.ts.map +0 -1
- package/dist/types/events.js +0 -7
- package/dist/types/functions.d.ts +0 -27
- package/dist/types/functions.d.ts.map +0 -1
- package/dist/types/functions.js +0 -1
- package/dist/types/plugin.d.ts +0 -75
- package/dist/types/plugin.d.ts.map +0 -1
- package/dist/types/plugin.js +0 -9
- package/dist/types/properties.d.ts +0 -33
- package/dist/types/properties.d.ts.map +0 -1
- package/dist/types/properties.js +0 -52
- package/dist/types/sdk.d.ts +0 -69
- package/dist/types/sdk.d.ts.map +0 -1
- package/dist/types/sdk.js +0 -4
- package/dist/types/telemetry-events.d.ts +0 -105
- package/dist/types/telemetry-events.d.ts.map +0 -1
- package/dist/types/telemetry-events.js +0 -8
- package/dist/utils/array-utils.d.ts +0 -31
- package/dist/utils/array-utils.d.ts.map +0 -1
- package/dist/utils/array-utils.js +0 -36
- package/dist/utils/array-utils.test.d.ts +0 -2
- package/dist/utils/array-utils.test.d.ts.map +0 -1
- package/dist/utils/array-utils.test.js +0 -107
- package/dist/utils/batch-utils.d.ts +0 -72
- package/dist/utils/batch-utils.d.ts.map +0 -1
- package/dist/utils/batch-utils.js +0 -162
- package/dist/utils/batch-utils.test.d.ts +0 -2
- package/dist/utils/batch-utils.test.d.ts.map +0 -1
- package/dist/utils/batch-utils.test.js +0 -476
- package/dist/utils/domain-utils.d.ts +0 -66
- package/dist/utils/domain-utils.d.ts.map +0 -1
- package/dist/utils/domain-utils.js +0 -164
- package/dist/utils/domain-utils.test.d.ts +0 -2
- package/dist/utils/domain-utils.test.d.ts.map +0 -1
- package/dist/utils/domain-utils.test.js +0 -346
- package/dist/utils/file-utils.d.ts +0 -4
- package/dist/utils/file-utils.d.ts.map +0 -1
- package/dist/utils/file-utils.js +0 -74
- package/dist/utils/file-utils.test.d.ts +0 -2
- package/dist/utils/file-utils.test.d.ts.map +0 -1
- package/dist/utils/file-utils.test.js +0 -51
- package/dist/utils/function-utils.d.ts +0 -73
- package/dist/utils/function-utils.d.ts.map +0 -1
- package/dist/utils/function-utils.js +0 -245
- package/dist/utils/function-utils.test.d.ts +0 -2
- package/dist/utils/function-utils.test.d.ts.map +0 -1
- package/dist/utils/function-utils.test.js +0 -110
- package/dist/utils/id-utils.d.ts +0 -13
- package/dist/utils/id-utils.d.ts.map +0 -1
- package/dist/utils/id-utils.js +0 -22
- package/dist/utils/id-utils.test.d.ts +0 -2
- package/dist/utils/id-utils.test.d.ts.map +0 -1
- package/dist/utils/id-utils.test.js +0 -22
- package/dist/utils/pagination-utils.d.ts +0 -37
- package/dist/utils/pagination-utils.d.ts.map +0 -1
- package/dist/utils/pagination-utils.js +0 -165
- package/dist/utils/pagination-utils.test.d.ts +0 -17
- package/dist/utils/pagination-utils.test.d.ts.map +0 -1
- package/dist/utils/pagination-utils.test.js +0 -461
- package/dist/utils/retry-utils.d.ts +0 -45
- package/dist/utils/retry-utils.d.ts.map +0 -1
- package/dist/utils/retry-utils.js +0 -51
- package/dist/utils/retry-utils.test.d.ts +0 -2
- package/dist/utils/retry-utils.test.d.ts.map +0 -1
- package/dist/utils/retry-utils.test.js +0 -90
- package/dist/utils/schema-utils.d.ts +0 -69
- package/dist/utils/schema-utils.d.ts.map +0 -1
- package/dist/utils/schema-utils.js +0 -72
- package/dist/utils/string-utils.d.ts +0 -40
- package/dist/utils/string-utils.d.ts.map +0 -1
- package/dist/utils/string-utils.js +0 -69
- package/dist/utils/string-utils.test.d.ts +0 -2
- package/dist/utils/string-utils.test.d.ts.map +0 -1
- package/dist/utils/string-utils.test.js +0 -59
- package/dist/utils/telemetry-utils.d.ts +0 -44
- package/dist/utils/telemetry-utils.d.ts.map +0 -1
- package/dist/utils/telemetry-utils.js +0 -55
- package/dist/utils/telemetry-utils.test.d.ts +0 -2
- package/dist/utils/telemetry-utils.test.d.ts.map +0 -1
- package/dist/utils/telemetry-utils.test.js +0 -94
- package/dist/utils/url-utils.d.ts +0 -19
- package/dist/utils/url-utils.d.ts.map +0 -1
- package/dist/utils/url-utils.js +0 -62
- package/dist/utils/url-utils.test.d.ts +0 -2
- package/dist/utils/url-utils.test.d.ts.map +0 -1
- package/dist/utils/url-utils.test.js +0 -103
- package/dist/utils/validation.d.ts +0 -4
- package/dist/utils/validation.d.ts.map +0 -1
- package/dist/utils/validation.js +0 -30
- package/dist/utils/validation.test.d.ts +0 -2
- package/dist/utils/validation.test.d.ts.map +0 -1
- package/dist/utils/validation.test.js +0 -44
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
const offsetCursorMarker = "$$offset$$";
|
|
2
|
-
function splitOffsetCursor(cursor) {
|
|
3
|
-
if (!cursor) {
|
|
4
|
-
return [0, cursor];
|
|
5
|
-
}
|
|
6
|
-
try {
|
|
7
|
-
const parsedCursor = JSON.parse(cursor);
|
|
8
|
-
if (!Array.isArray(parsedCursor)) {
|
|
9
|
-
return [0, cursor];
|
|
10
|
-
}
|
|
11
|
-
const [marker, offset, currentCursor] = parsedCursor;
|
|
12
|
-
if (marker !== offsetCursorMarker) {
|
|
13
|
-
return [0, cursor];
|
|
14
|
-
}
|
|
15
|
-
if (typeof offset !== "number") {
|
|
16
|
-
return [0, cursor];
|
|
17
|
-
}
|
|
18
|
-
return [offset, currentCursor];
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return [0, cursor];
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function createOffsetCursor(offset, currentCursor) {
|
|
25
|
-
return JSON.stringify([offsetCursorMarker, offset, currentCursor]);
|
|
26
|
-
}
|
|
27
|
-
export function createPrefixedCursor(prefix, cursor) {
|
|
28
|
-
if (!cursor) {
|
|
29
|
-
return `${prefix}::`;
|
|
30
|
-
}
|
|
31
|
-
return `${prefix}::${cursor}`;
|
|
32
|
-
}
|
|
33
|
-
export function splitPrefixedCursor(cursor, prefixes) {
|
|
34
|
-
if (!cursor) {
|
|
35
|
-
return [undefined, undefined];
|
|
36
|
-
}
|
|
37
|
-
const [prefix, ...rest] = cursor.split("::");
|
|
38
|
-
if (prefixes && !prefixes.includes(prefix)) {
|
|
39
|
-
return [undefined, cursor];
|
|
40
|
-
}
|
|
41
|
-
cursor = rest.join("::");
|
|
42
|
-
if (!cursor) {
|
|
43
|
-
return [prefix, undefined];
|
|
44
|
-
}
|
|
45
|
-
return [prefix, cursor];
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Utility for paginating through API endpoints that return cursor-based pages
|
|
49
|
-
*
|
|
50
|
-
* @param pageFunction - Function that fetches a single page with {data, nextCursor} structure
|
|
51
|
-
* @param pageOptions - Options to pass to the page function (cursor will be managed automatically)
|
|
52
|
-
* @returns Async iterator that yields pages
|
|
53
|
-
*/
|
|
54
|
-
export async function* paginateMaxItems(pageFunction, pageOptions) {
|
|
55
|
-
let cursor = pageOptions?.cursor;
|
|
56
|
-
let totalItemsYielded = 0;
|
|
57
|
-
const maxItems = pageOptions?.maxItems;
|
|
58
|
-
const pageSize = pageOptions?.pageSize;
|
|
59
|
-
do {
|
|
60
|
-
const options = {
|
|
61
|
-
...(pageOptions || {}),
|
|
62
|
-
cursor,
|
|
63
|
-
pageSize: maxItems !== undefined && pageSize !== undefined
|
|
64
|
-
? Math.min(pageSize, maxItems)
|
|
65
|
-
: pageSize,
|
|
66
|
-
};
|
|
67
|
-
const page = await pageFunction(options);
|
|
68
|
-
if (maxItems !== undefined) {
|
|
69
|
-
const remainingItems = maxItems - totalItemsYielded;
|
|
70
|
-
if (page.data.length >= remainingItems) {
|
|
71
|
-
const yieldedPage = {
|
|
72
|
-
...page,
|
|
73
|
-
data: page.data.slice(0, remainingItems),
|
|
74
|
-
nextCursor: undefined,
|
|
75
|
-
};
|
|
76
|
-
yield yieldedPage;
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
yield page;
|
|
81
|
-
totalItemsYielded += page.data.length;
|
|
82
|
-
cursor = page.nextCursor;
|
|
83
|
-
} while (cursor);
|
|
84
|
-
}
|
|
85
|
-
export async function* paginateBuffered(pageFunction, pageOptions) {
|
|
86
|
-
const pageSize = pageOptions?.pageSize;
|
|
87
|
-
const [cursorOffset, currentCursor] = splitOffsetCursor(pageOptions?.cursor);
|
|
88
|
-
const options = {
|
|
89
|
-
...(pageOptions || {}),
|
|
90
|
-
cursor: currentCursor,
|
|
91
|
-
};
|
|
92
|
-
const iterator = paginateMaxItems(pageFunction, options);
|
|
93
|
-
let bufferedPages = [];
|
|
94
|
-
let isFirstPage = true;
|
|
95
|
-
let cursor;
|
|
96
|
-
for await (let page of iterator) {
|
|
97
|
-
if (isFirstPage) {
|
|
98
|
-
isFirstPage = false;
|
|
99
|
-
if (cursorOffset) {
|
|
100
|
-
page = {
|
|
101
|
-
...page,
|
|
102
|
-
data: page.data.slice(cursorOffset),
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (!pageSize) {
|
|
107
|
-
yield page;
|
|
108
|
-
cursor = page.nextCursor;
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
const bufferedLength = bufferedPages.reduce((acc, page) => acc + page.data.length, 0);
|
|
112
|
-
// If we don't have enough to fill a page, buffer this page.
|
|
113
|
-
if (bufferedLength + page.data.length < pageSize) {
|
|
114
|
-
bufferedPages.push(page);
|
|
115
|
-
cursor = page.nextCursor;
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
// Let's yield a page from our buffered pages.
|
|
119
|
-
const bufferedItems = bufferedPages.map((p) => p.data).flat();
|
|
120
|
-
const allItems = [...bufferedItems, ...page.data];
|
|
121
|
-
const pageItems = allItems.slice(0, pageSize);
|
|
122
|
-
const remainingItems = allItems.slice(pageItems.length);
|
|
123
|
-
// No extra items to buffer, so we can just yield a normal page with a cursor to the next one.
|
|
124
|
-
if (remainingItems.length === 0) {
|
|
125
|
-
yield {
|
|
126
|
-
...page,
|
|
127
|
-
data: pageItems,
|
|
128
|
-
nextCursor: page.nextCursor,
|
|
129
|
-
};
|
|
130
|
-
bufferedPages = [];
|
|
131
|
-
cursor = page.nextCursor;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
// Yield our items with a cursor to offset into this page.
|
|
135
|
-
yield {
|
|
136
|
-
...page,
|
|
137
|
-
data: pageItems,
|
|
138
|
-
nextCursor: createOffsetCursor(page.data.length - remainingItems.length, cursor),
|
|
139
|
-
};
|
|
140
|
-
while (remainingItems.length > pageSize) {
|
|
141
|
-
const pageItems = remainingItems.splice(0, pageSize);
|
|
142
|
-
yield {
|
|
143
|
-
...page,
|
|
144
|
-
data: pageItems,
|
|
145
|
-
nextCursor: createOffsetCursor(page.data.length - remainingItems.length, cursor),
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
bufferedPages = [
|
|
149
|
-
{
|
|
150
|
-
...page,
|
|
151
|
-
data: remainingItems,
|
|
152
|
-
},
|
|
153
|
-
];
|
|
154
|
-
cursor = page.nextCursor;
|
|
155
|
-
}
|
|
156
|
-
if (bufferedPages.length > 0) {
|
|
157
|
-
const lastBufferedPage = bufferedPages.slice(-1)[0];
|
|
158
|
-
const bufferedItems = bufferedPages.map((p) => p.data).flat();
|
|
159
|
-
yield {
|
|
160
|
-
...lastBufferedPage,
|
|
161
|
-
data: bufferedItems,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
export const paginate = paginateBuffered;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export declare const ALL_ITEMS: {
|
|
2
|
-
id: number;
|
|
3
|
-
name: string;
|
|
4
|
-
}[];
|
|
5
|
-
export declare function listTestItems(options: {
|
|
6
|
-
pageSize?: number;
|
|
7
|
-
cursor?: string;
|
|
8
|
-
filter?: string;
|
|
9
|
-
maxItems?: number;
|
|
10
|
-
}): Promise<{
|
|
11
|
-
data: {
|
|
12
|
-
id: number;
|
|
13
|
-
name: string;
|
|
14
|
-
}[];
|
|
15
|
-
nextCursor: string | undefined;
|
|
16
|
-
}>;
|
|
17
|
-
//# sourceMappingURL=pagination-utils.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pagination-utils.test.d.ts","sourceRoot":"","sources":["../../src/utils/pagination-utils.test.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,SAAS;;;GAGnB,CAAC;AAGJ,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;;;;;;GA6CA"}
|
|
@@ -1,461 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { paginate, paginateMaxItems } from "./pagination-utils";
|
|
3
|
-
// Test data set - simulating a large dataset with cursor-based pagination
|
|
4
|
-
export const ALL_ITEMS = Array.from({ length: 25 }, (_, i) => ({
|
|
5
|
-
id: i + 1,
|
|
6
|
-
name: `Item ${i + 1}`,
|
|
7
|
-
}));
|
|
8
|
-
// Test page function that implements cursor-based pagination logic
|
|
9
|
-
export async function listTestItems(options) {
|
|
10
|
-
const { pageSize = 5, cursor, filter, maxItems } = options;
|
|
11
|
-
// Cap pageSize at 10 to simulate real API limits
|
|
12
|
-
let actualPageSize = Math.min(pageSize, 10);
|
|
13
|
-
// If maxItems hint is provided, further limit the page size for optimization
|
|
14
|
-
if (maxItems !== undefined && maxItems > 0) {
|
|
15
|
-
actualPageSize = Math.min(actualPageSize, maxItems);
|
|
16
|
-
}
|
|
17
|
-
// Filter items if filter is provided
|
|
18
|
-
let items = ALL_ITEMS;
|
|
19
|
-
if (filter) {
|
|
20
|
-
items = items.filter((item) => item.name.toLowerCase().includes(filter.toLowerCase()));
|
|
21
|
-
}
|
|
22
|
-
// Find starting position based on cursor
|
|
23
|
-
let startIndex = 0;
|
|
24
|
-
if (cursor) {
|
|
25
|
-
// Cursor format: "cursor-{itemId}"
|
|
26
|
-
const cursorId = parseInt(cursor.replace("cursor-", ""));
|
|
27
|
-
startIndex = items.findIndex((item) => item.id > cursorId);
|
|
28
|
-
if (startIndex === -1) {
|
|
29
|
-
// No more items after cursor
|
|
30
|
-
return { data: [], nextCursor: undefined };
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
// Get page of items
|
|
34
|
-
const pageItems = items.slice(startIndex, startIndex + actualPageSize);
|
|
35
|
-
// Determine next cursor
|
|
36
|
-
let nextCursor;
|
|
37
|
-
if (startIndex + actualPageSize < items.length && pageItems.length > 0) {
|
|
38
|
-
const lastItem = pageItems[pageItems.length - 1];
|
|
39
|
-
nextCursor = `cursor-${lastItem.id}`;
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
data: pageItems,
|
|
43
|
-
nextCursor,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
describe("paginate", () => {
|
|
47
|
-
it("should paginate through multiple pages", async () => {
|
|
48
|
-
const pageIterator = paginate(listTestItems, { pageSize: 3 });
|
|
49
|
-
const pages = [];
|
|
50
|
-
for await (const page of pageIterator) {
|
|
51
|
-
pages.push(page);
|
|
52
|
-
}
|
|
53
|
-
// With 25 items and pageSize 3, should have 9 pages (8 full + 1 partial)
|
|
54
|
-
expect(pages).toHaveLength(9);
|
|
55
|
-
// Check first page
|
|
56
|
-
expect(pages[0]).toEqual({
|
|
57
|
-
data: ALL_ITEMS.slice(0, 3),
|
|
58
|
-
nextCursor: "cursor-3",
|
|
59
|
-
});
|
|
60
|
-
// Check second page (cursor should be passed correctly)
|
|
61
|
-
expect(pages[1]).toEqual({
|
|
62
|
-
data: ALL_ITEMS.slice(3, 6),
|
|
63
|
-
nextCursor: "cursor-6",
|
|
64
|
-
});
|
|
65
|
-
// Check last page (should have no nextCursor)
|
|
66
|
-
expect(pages[8]).toEqual({
|
|
67
|
-
data: ALL_ITEMS.slice(24, 25),
|
|
68
|
-
nextCursor: undefined,
|
|
69
|
-
});
|
|
70
|
-
// Verify we got all 25 items across all pages
|
|
71
|
-
const allItems = pages.flatMap((page) => page.data);
|
|
72
|
-
expect(allItems).toHaveLength(25);
|
|
73
|
-
});
|
|
74
|
-
it("should cap pageSize at 10 when unbuffered", async () => {
|
|
75
|
-
// Use a page size larger than 10, should get capped
|
|
76
|
-
const pageIterator = paginateMaxItems(listTestItems, { pageSize: 50 });
|
|
77
|
-
const pages = [];
|
|
78
|
-
for await (const page of pageIterator) {
|
|
79
|
-
pages.push(page);
|
|
80
|
-
}
|
|
81
|
-
// Should get 3 pages: 10+10+5=25 (pageSize capped at 10)
|
|
82
|
-
expect(pages).toHaveLength(3);
|
|
83
|
-
expect(pages[0].data).toHaveLength(10); // First page capped at 10
|
|
84
|
-
expect(pages[1].data).toHaveLength(10); // Second page capped at 10
|
|
85
|
-
expect(pages[2].data).toHaveLength(5); // Last page has remaining 5
|
|
86
|
-
expect(pages[2].nextCursor).toBeUndefined(); // Last page has no nextCursor
|
|
87
|
-
});
|
|
88
|
-
it("should not cap pageSize when buffered", async () => {
|
|
89
|
-
const pageIterator = paginate(listTestItems, { pageSize: 50 });
|
|
90
|
-
for await (const page of pageIterator) {
|
|
91
|
-
expect(page.data).toHaveLength(25);
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
it("should not cap pageSize when buffered", async () => {
|
|
96
|
-
const pageIterator = paginate(listTestItems, { pageSize: 15 });
|
|
97
|
-
const pages = [];
|
|
98
|
-
for await (const page of pageIterator) {
|
|
99
|
-
pages.push(page);
|
|
100
|
-
}
|
|
101
|
-
expect(pages).toHaveLength(2);
|
|
102
|
-
expect(pages[0].data).toHaveLength(15);
|
|
103
|
-
expect(pages[1].data).toHaveLength(10);
|
|
104
|
-
});
|
|
105
|
-
it("should continue from pseudo cursor", async () => {
|
|
106
|
-
const pageIterator = paginate(listTestItems, { pageSize: 15 });
|
|
107
|
-
const pages = [];
|
|
108
|
-
for await (const page of pageIterator) {
|
|
109
|
-
pages.push(page);
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
const nextPageIterator = paginate(listTestItems, {
|
|
113
|
-
pageSize: 15,
|
|
114
|
-
cursor: pages[0].nextCursor,
|
|
115
|
-
});
|
|
116
|
-
for await (const page of nextPageIterator) {
|
|
117
|
-
pages.push(page);
|
|
118
|
-
}
|
|
119
|
-
expect(pages).toHaveLength(2);
|
|
120
|
-
expect(pages[0].data).toHaveLength(15);
|
|
121
|
-
expect(pages[1].data).toHaveLength(10);
|
|
122
|
-
});
|
|
123
|
-
it("should handle filtered results", async () => {
|
|
124
|
-
// Filter for items containing "1" (should match Item 1, Item 10-19, Item 21)
|
|
125
|
-
const pageIterator = paginate(listTestItems, {
|
|
126
|
-
pageSize: 4,
|
|
127
|
-
filter: "1",
|
|
128
|
-
});
|
|
129
|
-
const pages = [];
|
|
130
|
-
for await (const page of pageIterator) {
|
|
131
|
-
pages.push(page);
|
|
132
|
-
}
|
|
133
|
-
// Should have multiple pages due to pageSize: 4
|
|
134
|
-
expect(pages.length).toBeGreaterThan(1);
|
|
135
|
-
// All items should contain "1"
|
|
136
|
-
const allItems = pages.flatMap((page) => page.data);
|
|
137
|
-
expect(allItems.every((item) => item.name.includes("1"))).toBe(true);
|
|
138
|
-
// Should include items like Item 1, Item 10, Item 11, etc.
|
|
139
|
-
expect(allItems).toContainEqual(ALL_ITEMS[0]); // Item 1
|
|
140
|
-
expect(allItems).toContainEqual(ALL_ITEMS[9]); // Item 10
|
|
141
|
-
expect(allItems).toContainEqual(ALL_ITEMS[20]); // Item 21
|
|
142
|
-
});
|
|
143
|
-
it("should handle empty results", async () => {
|
|
144
|
-
// Filter that matches no items
|
|
145
|
-
const pageIterator = paginate(listTestItems, {
|
|
146
|
-
pageSize: 10,
|
|
147
|
-
filter: "nonexistent",
|
|
148
|
-
});
|
|
149
|
-
const pages = [];
|
|
150
|
-
for await (const page of pageIterator) {
|
|
151
|
-
pages.push(page);
|
|
152
|
-
}
|
|
153
|
-
expect(pages).toHaveLength(1);
|
|
154
|
-
expect(pages[0]).toEqual({
|
|
155
|
-
data: [],
|
|
156
|
-
nextCursor: undefined,
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
it("should pass through errors from the page function", async () => {
|
|
160
|
-
// Create a page function that throws an error
|
|
161
|
-
const errorPageFunction = async () => {
|
|
162
|
-
throw new Error("Page function failed");
|
|
163
|
-
};
|
|
164
|
-
const pageIterator = paginate(errorPageFunction, {});
|
|
165
|
-
let reachedUnreachableCode = undefined;
|
|
166
|
-
await expect(async () => {
|
|
167
|
-
for await (const page of pageIterator) {
|
|
168
|
-
// Should never reach here due to error
|
|
169
|
-
reachedUnreachableCode = page;
|
|
170
|
-
}
|
|
171
|
-
}).rejects.toThrow("Page function failed");
|
|
172
|
-
// Verify we never reached the unreachable code
|
|
173
|
-
expect(reachedUnreachableCode).toBeUndefined();
|
|
174
|
-
});
|
|
175
|
-
it("should start pagination from provided cursor", async () => {
|
|
176
|
-
// Test that pagination can start from a specific cursor position
|
|
177
|
-
const pageIterator = paginate(listTestItems, {
|
|
178
|
-
pageSize: 3,
|
|
179
|
-
cursor: "cursor-5", // Start after item 5
|
|
180
|
-
});
|
|
181
|
-
const pages = [];
|
|
182
|
-
for await (const page of pageIterator) {
|
|
183
|
-
pages.push(page);
|
|
184
|
-
if (pages.length >= 2)
|
|
185
|
-
break; // Just test first 2 pages
|
|
186
|
-
}
|
|
187
|
-
expect(pages).toHaveLength(2);
|
|
188
|
-
// First page should start after cursor-5 (from item 6)
|
|
189
|
-
expect(pages[0].data[0]).toEqual(ALL_ITEMS[5]); // Item 6
|
|
190
|
-
// Second page should continue properly
|
|
191
|
-
expect(pages[1].data[0]).toEqual(ALL_ITEMS[8]); // Item 9
|
|
192
|
-
});
|
|
193
|
-
it("should allow breaking out of pagination early", async () => {
|
|
194
|
-
// Test that users can break out of the iterator manually
|
|
195
|
-
const pageIterator = paginate(listTestItems, { pageSize: 3 });
|
|
196
|
-
const pages = [];
|
|
197
|
-
for await (const page of pageIterator) {
|
|
198
|
-
pages.push(page);
|
|
199
|
-
// Break after getting 3 pages
|
|
200
|
-
if (pages.length >= 3) {
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
// Should have stopped at exactly 3 pages with 9 items (3+3+3=9)
|
|
205
|
-
const allItems = pages.flatMap((page) => page.data);
|
|
206
|
-
expect(allItems).toHaveLength(9);
|
|
207
|
-
expect(pages).toHaveLength(3);
|
|
208
|
-
// Should be first 9 items
|
|
209
|
-
expect(allItems).toEqual(ALL_ITEMS.slice(0, 9));
|
|
210
|
-
});
|
|
211
|
-
describe("conditional types for optional options", () => {
|
|
212
|
-
it("should work with function that takes no options", async () => {
|
|
213
|
-
async function fetchWithNoOptions(_options) {
|
|
214
|
-
return { data: [1, 2, 3] };
|
|
215
|
-
}
|
|
216
|
-
const pages = [];
|
|
217
|
-
for await (const page of paginate(fetchWithNoOptions)) {
|
|
218
|
-
pages.push(...page.data);
|
|
219
|
-
}
|
|
220
|
-
expect(pages).toEqual([1, 2, 3]);
|
|
221
|
-
});
|
|
222
|
-
it("should work with function that takes optional options", async () => {
|
|
223
|
-
async function fetchWithOptionalOptions(options) {
|
|
224
|
-
return { data: [`page-${options?.cursor || "1"}`] };
|
|
225
|
-
}
|
|
226
|
-
const pages = [];
|
|
227
|
-
for await (const page of paginate(fetchWithOptionalOptions, {
|
|
228
|
-
limit: 5,
|
|
229
|
-
})) {
|
|
230
|
-
pages.push(...page.data);
|
|
231
|
-
}
|
|
232
|
-
expect(pages).toEqual(["page-1"]);
|
|
233
|
-
});
|
|
234
|
-
it("should work with function that takes only cursor", async () => {
|
|
235
|
-
async function fetchWithOnlyCursor(_options) {
|
|
236
|
-
return { data: [true] };
|
|
237
|
-
}
|
|
238
|
-
const pages = [];
|
|
239
|
-
for await (const page of paginate(fetchWithOnlyCursor, {
|
|
240
|
-
cursor: "start",
|
|
241
|
-
})) {
|
|
242
|
-
pages.push(...page.data);
|
|
243
|
-
}
|
|
244
|
-
expect(pages).toEqual([true]);
|
|
245
|
-
});
|
|
246
|
-
it("should work when baseOptions is omitted entirely", async () => {
|
|
247
|
-
async function fetchSimple(_options) {
|
|
248
|
-
return { data: ["simple"] };
|
|
249
|
-
}
|
|
250
|
-
const pages = [];
|
|
251
|
-
for await (const page of paginate(fetchSimple)) {
|
|
252
|
-
pages.push(...page.data);
|
|
253
|
-
}
|
|
254
|
-
expect(pages).toEqual(["simple"]);
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
describe("maxItems functionality", () => {
|
|
258
|
-
it("should limit total items across pages", async () => {
|
|
259
|
-
const items = [];
|
|
260
|
-
for await (const page of paginate(listTestItems, {
|
|
261
|
-
pageSize: 3,
|
|
262
|
-
maxItems: 7,
|
|
263
|
-
})) {
|
|
264
|
-
items.push(...page.data);
|
|
265
|
-
}
|
|
266
|
-
expect(items).toHaveLength(7);
|
|
267
|
-
expect(items).toEqual(ALL_ITEMS.slice(0, 7));
|
|
268
|
-
});
|
|
269
|
-
it("should truncate the final page when maxItems is exceeded", async () => {
|
|
270
|
-
// Create a function that ignores maxItems hint to force truncation
|
|
271
|
-
async function fetchIgnoringMaxItems(options) {
|
|
272
|
-
return listTestItems({
|
|
273
|
-
pageSize: options.pageSize,
|
|
274
|
-
cursor: options.cursor,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
const pageData = [];
|
|
278
|
-
for await (const page of paginate(fetchIgnoringMaxItems, {
|
|
279
|
-
pageSize: 3,
|
|
280
|
-
maxItems: 7,
|
|
281
|
-
})) {
|
|
282
|
-
pageData.push(page);
|
|
283
|
-
}
|
|
284
|
-
// Should have 3 pages: [3 items], [3 items], [1 item (truncated)]
|
|
285
|
-
expect(pageData).toHaveLength(3);
|
|
286
|
-
expect(pageData[0].data).toHaveLength(3);
|
|
287
|
-
expect(pageData[1].data).toHaveLength(3);
|
|
288
|
-
expect(pageData[2].data).toHaveLength(1); // Truncated page
|
|
289
|
-
expect(pageData[2].nextCursor).toBeUndefined(); // Should clear cursor
|
|
290
|
-
});
|
|
291
|
-
it("should stop early when maxItems is exactly a page boundary", async () => {
|
|
292
|
-
const items = [];
|
|
293
|
-
for await (const page of paginate(listTestItems, {
|
|
294
|
-
pageSize: 3,
|
|
295
|
-
maxItems: 6,
|
|
296
|
-
})) {
|
|
297
|
-
items.push(...page.data);
|
|
298
|
-
}
|
|
299
|
-
expect(items).toHaveLength(6);
|
|
300
|
-
expect(items).toEqual(ALL_ITEMS.slice(0, 6));
|
|
301
|
-
});
|
|
302
|
-
it("should handle maxItems of 0", async () => {
|
|
303
|
-
const items = [];
|
|
304
|
-
for await (const page of paginate(listTestItems, {
|
|
305
|
-
pageSize: 3,
|
|
306
|
-
maxItems: 0,
|
|
307
|
-
})) {
|
|
308
|
-
items.push(...page.data);
|
|
309
|
-
}
|
|
310
|
-
expect(items).toHaveLength(0);
|
|
311
|
-
});
|
|
312
|
-
it("should work normally when maxItems is larger than total items", async () => {
|
|
313
|
-
const items = [];
|
|
314
|
-
for await (const page of paginate(listTestItems, {
|
|
315
|
-
pageSize: 3,
|
|
316
|
-
maxItems: 100,
|
|
317
|
-
})) {
|
|
318
|
-
items.push(...page.data);
|
|
319
|
-
}
|
|
320
|
-
// Should get all items since limit is higher than total
|
|
321
|
-
expect(items).toEqual(ALL_ITEMS);
|
|
322
|
-
});
|
|
323
|
-
it("should work with maxItems and other options combined", async () => {
|
|
324
|
-
async function fetchWithLimit(options) {
|
|
325
|
-
const start = parseInt(options.cursor || "0");
|
|
326
|
-
let limit = options.limit;
|
|
327
|
-
// Apply maxItems hint for optimization
|
|
328
|
-
if (options.maxItems !== undefined && options.maxItems > 0) {
|
|
329
|
-
limit = Math.min(limit, options.maxItems);
|
|
330
|
-
}
|
|
331
|
-
const items = ALL_ITEMS.slice(start, start + limit);
|
|
332
|
-
const nextCursor = start + limit < ALL_ITEMS.length
|
|
333
|
-
? (start + limit).toString()
|
|
334
|
-
: undefined;
|
|
335
|
-
return { data: items, nextCursor };
|
|
336
|
-
}
|
|
337
|
-
const items = [];
|
|
338
|
-
for await (const page of paginate(fetchWithLimit, {
|
|
339
|
-
limit: 2,
|
|
340
|
-
maxItems: 5,
|
|
341
|
-
})) {
|
|
342
|
-
items.push(...page.data);
|
|
343
|
-
}
|
|
344
|
-
expect(items).toHaveLength(5);
|
|
345
|
-
expect(items).toEqual(ALL_ITEMS.slice(0, 5));
|
|
346
|
-
});
|
|
347
|
-
it("should preserve original page structure except for truncation", async () => {
|
|
348
|
-
// Create a function that ignores maxItems hint to force truncation
|
|
349
|
-
async function fetchIgnoringMaxItems(options) {
|
|
350
|
-
const pageSize = options.pageSize || 3;
|
|
351
|
-
const start = options.cursor
|
|
352
|
-
? parseInt(options.cursor.replace("cursor-", "")) + 1
|
|
353
|
-
: 0;
|
|
354
|
-
const items = ALL_ITEMS.slice(start, start + pageSize);
|
|
355
|
-
const nextCursor = start + pageSize <= ALL_ITEMS.length
|
|
356
|
-
? `cursor-${ALL_ITEMS[start + pageSize - 1].id}`
|
|
357
|
-
: undefined;
|
|
358
|
-
return { data: items, nextCursor };
|
|
359
|
-
}
|
|
360
|
-
let pageCount = 0;
|
|
361
|
-
for await (const page of paginate(fetchIgnoringMaxItems, {
|
|
362
|
-
pageSize: 3,
|
|
363
|
-
maxItems: 7,
|
|
364
|
-
})) {
|
|
365
|
-
pageCount++;
|
|
366
|
-
// All pages should have the expected structure
|
|
367
|
-
expect(page).toHaveProperty("data");
|
|
368
|
-
expect(Array.isArray(page.data)).toBe(true);
|
|
369
|
-
if (pageCount === 3) {
|
|
370
|
-
// Final truncated page
|
|
371
|
-
expect(page.data).toHaveLength(1);
|
|
372
|
-
expect(page.nextCursor).toBeUndefined();
|
|
373
|
-
}
|
|
374
|
-
else {
|
|
375
|
-
// Regular pages
|
|
376
|
-
expect(page.data).toHaveLength(3);
|
|
377
|
-
expect(typeof page.nextCursor).toBe("string");
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
expect(pageCount).toBe(3);
|
|
381
|
-
});
|
|
382
|
-
it("should handle API that ignores pageSize and returns more items on first page", async () => {
|
|
383
|
-
// Simulate an API that ignores pageSize and always returns 10 items per page
|
|
384
|
-
async function apiIgnoresPageSize(options) {
|
|
385
|
-
return listTestItems({
|
|
386
|
-
cursor: options.cursor,
|
|
387
|
-
pageSize: 10,
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
const pages = [];
|
|
391
|
-
// User requests pageSize: 3, but API returns 10 per page
|
|
392
|
-
for await (const page of paginate(apiIgnoresPageSize, {
|
|
393
|
-
pageSize: 3,
|
|
394
|
-
})) {
|
|
395
|
-
pages.push(page);
|
|
396
|
-
// Only get first page to test the behavior
|
|
397
|
-
break;
|
|
398
|
-
}
|
|
399
|
-
// Should get 1 page with only 3 items (paginate enforces pageSize limit)
|
|
400
|
-
expect(pages).toHaveLength(1);
|
|
401
|
-
expect(pages[0].data).toHaveLength(3);
|
|
402
|
-
expect(pages[0].data).toEqual(ALL_ITEMS.slice(0, 3));
|
|
403
|
-
// Cursor should still be present since there are more items available
|
|
404
|
-
expect(pages[0].nextCursor).toBeDefined();
|
|
405
|
-
});
|
|
406
|
-
it("should truncate oversized API response to pageSize", async () => {
|
|
407
|
-
// API always returns 10 items, but user wants pageSize: 3
|
|
408
|
-
async function apiReturns10Items(options) {
|
|
409
|
-
return listTestItems({
|
|
410
|
-
cursor: options.cursor,
|
|
411
|
-
pageSize: 10,
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
const pages = [];
|
|
415
|
-
for await (const page of paginate(apiReturns10Items, {
|
|
416
|
-
pageSize: 3,
|
|
417
|
-
})) {
|
|
418
|
-
pages.push(page);
|
|
419
|
-
}
|
|
420
|
-
// Should truncate each 10-item API response to 3 items per page
|
|
421
|
-
expect(pages).toHaveLength(9);
|
|
422
|
-
expect(pages[0].data).toHaveLength(3);
|
|
423
|
-
expect(pages[1].data).toHaveLength(3);
|
|
424
|
-
expect(pages[8].data).toHaveLength(1);
|
|
425
|
-
// Verify items are from different API calls
|
|
426
|
-
expect(pages[0].data).toEqual(ALL_ITEMS.slice(0, 3)); // Items 1-3 from first API call
|
|
427
|
-
expect(pages[1].data).toEqual(ALL_ITEMS.slice(3, 6)); // Items 11-13 from second API call
|
|
428
|
-
expect(pages[8].data).toEqual(ALL_ITEMS.slice(24, 25)); // Items 21-23 from third API call
|
|
429
|
-
// All pages should have nextCursor since API has more data
|
|
430
|
-
expect(pages[0].nextCursor).toBe('["$$offset$$",3,null]');
|
|
431
|
-
expect(pages[8].nextCursor).toBeUndefined();
|
|
432
|
-
});
|
|
433
|
-
it("should handle pageSize enforcement with maxItems limit", async () => {
|
|
434
|
-
// API returns 10 items, user wants pageSize: 3, maxItems: 7
|
|
435
|
-
async function apiReturns10Items(options) {
|
|
436
|
-
return listTestItems({
|
|
437
|
-
cursor: options.cursor,
|
|
438
|
-
pageSize: 10,
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
const pages = [];
|
|
442
|
-
for await (const page of paginate(apiReturns10Items, {
|
|
443
|
-
pageSize: 3,
|
|
444
|
-
maxItems: 7,
|
|
445
|
-
})) {
|
|
446
|
-
pages.push(page);
|
|
447
|
-
}
|
|
448
|
-
// Should get: [3, 3, 1] items (total 7, respecting maxItems)
|
|
449
|
-
expect(pages).toHaveLength(3);
|
|
450
|
-
expect(pages[0].data).toHaveLength(3);
|
|
451
|
-
expect(pages[1].data).toHaveLength(3);
|
|
452
|
-
expect(pages[2].data).toHaveLength(1); // Truncated to respect maxItems
|
|
453
|
-
// Last page should have no nextCursor (maxItems reached)
|
|
454
|
-
expect(pages[2].nextCursor).toBeUndefined();
|
|
455
|
-
// Verify total items
|
|
456
|
-
const allItems = pages.flatMap((p) => p.data);
|
|
457
|
-
expect(allItems).toHaveLength(7);
|
|
458
|
-
expect(allItems).toEqual(ALL_ITEMS.slice(0, 7));
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Retry and Backoff Utilities
|
|
3
|
-
*
|
|
4
|
-
* Shared utilities for implementing resilient retry logic with exponential backoff
|
|
5
|
-
* and jitter. Used by both polling and batch operations.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Maximum number of consecutive errors before giving up
|
|
9
|
-
* Prevents infinite retry loops when the API is consistently failing
|
|
10
|
-
*/
|
|
11
|
-
export declare const MAX_CONSECUTIVE_ERRORS = 3;
|
|
12
|
-
/**
|
|
13
|
-
* Base delay for error backoff in milliseconds
|
|
14
|
-
* Each error adds this amount (with scaling) to the wait time
|
|
15
|
-
*/
|
|
16
|
-
export declare const BASE_ERROR_BACKOFF_MS = 1000;
|
|
17
|
-
/**
|
|
18
|
-
* Jitter factor (0.0 to 1.0) for randomizing wait times
|
|
19
|
-
* Prevents thundering herd problem when many clients retry simultaneously
|
|
20
|
-
* A factor of 0.5 means we add 0-50% random variance to the base interval
|
|
21
|
-
*/
|
|
22
|
-
export declare const JITTER_FACTOR = 0.5;
|
|
23
|
-
/**
|
|
24
|
-
* Calculate wait time with jitter and error backoff
|
|
25
|
-
*
|
|
26
|
-
* This implements two key reliability patterns:
|
|
27
|
-
* 1. Jitter - Adds randomness to prevent synchronized retries across clients
|
|
28
|
-
* 2. Error backoff - Increases wait time when errors occur, giving the API time to recover
|
|
29
|
-
*
|
|
30
|
-
* @param baseInterval - The base wait time in milliseconds
|
|
31
|
-
* @param errorCount - Number of consecutive errors (0 if no errors)
|
|
32
|
-
* @returns Wait time in milliseconds with jitter and error backoff applied
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* // No errors: returns 1000-1500ms (1000 + 0-500ms jitter)
|
|
36
|
-
* calculateWaitTime(1000, 0);
|
|
37
|
-
*
|
|
38
|
-
* // 2 errors: returns 1000-1500ms base + up to 1000ms error backoff = 1000-2500ms
|
|
39
|
-
* calculateWaitTime(1000, 2);
|
|
40
|
-
*
|
|
41
|
-
* // 6 errors: returns 1000-1500ms base + 2000ms capped backoff = 3000-3500ms
|
|
42
|
-
* calculateWaitTime(1000, 6);
|
|
43
|
-
*/
|
|
44
|
-
export declare function calculateWaitTime(baseInterval: number, errorCount: number): number;
|
|
45
|
-
//# sourceMappingURL=retry-utils.d.ts.map
|