@distilled.cloud/core 0.30.3 → 1.0.0-rc.2
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/lib/api.d.ts +165 -0
- package/lib/api.d.ts.map +1 -0
- package/lib/api.js +178 -0
- package/lib/api.js.map +1 -0
- package/lib/codegen/cli.d.ts +29 -0
- package/lib/codegen/cli.d.ts.map +1 -0
- package/lib/codegen/cli.js +165 -0
- package/lib/codegen/cli.js.map +1 -0
- package/lib/codegen/emit.d.ts +129 -0
- package/lib/codegen/emit.d.ts.map +1 -0
- package/lib/codegen/emit.js +105 -0
- package/lib/codegen/emit.js.map +1 -0
- package/lib/codegen/format.d.ts +23 -0
- package/lib/codegen/format.d.ts.map +1 -0
- package/lib/codegen/format.js +28 -0
- package/lib/codegen/format.js.map +1 -0
- package/lib/codegen/generator.d.ts +334 -0
- package/lib/codegen/generator.d.ts.map +1 -0
- package/lib/codegen/generator.js +691 -0
- package/lib/codegen/generator.js.map +1 -0
- package/lib/codegen/graph.d.ts +36 -0
- package/lib/codegen/graph.d.ts.map +1 -0
- package/lib/codegen/graph.js +136 -0
- package/lib/codegen/graph.js.map +1 -0
- package/lib/codegen/members.d.ts +25 -0
- package/lib/codegen/members.d.ts.map +1 -0
- package/lib/codegen/members.js +55 -0
- package/lib/codegen/members.js.map +1 -0
- package/lib/codegen/naming.d.ts +29 -0
- package/lib/codegen/naming.d.ts.map +1 -0
- package/lib/codegen/naming.js +74 -0
- package/lib/codegen/naming.js.map +1 -0
- package/lib/codegen/openapi-cli.d.ts +38 -0
- package/lib/codegen/openapi-cli.d.ts.map +1 -0
- package/lib/codegen/openapi-cli.js +107 -0
- package/lib/codegen/openapi-cli.js.map +1 -0
- package/lib/codegen/openapi.d.ts +115 -0
- package/lib/codegen/openapi.d.ts.map +1 -0
- package/lib/codegen/openapi.js +1220 -0
- package/lib/codegen/openapi.js.map +1 -0
- package/lib/codegen/operations.d.ts +24 -0
- package/lib/codegen/operations.d.ts.map +1 -0
- package/lib/codegen/operations.js +56 -0
- package/lib/codegen/operations.js.map +1 -0
- package/lib/codegen/pagination.d.ts +39 -0
- package/lib/codegen/pagination.d.ts.map +1 -0
- package/lib/codegen/pagination.js +33 -0
- package/lib/codegen/pagination.js.map +1 -0
- package/lib/codegen/prelude.d.ts +15 -0
- package/lib/codegen/prelude.d.ts.map +1 -0
- package/lib/codegen/prelude.js +60 -0
- package/lib/codegen/prelude.js.map +1 -0
- package/lib/error-category.d.ts +28 -0
- package/lib/error-category.d.ts.map +1 -0
- package/lib/error-category.js +46 -0
- package/lib/error-category.js.map +1 -0
- package/lib/errors.d.ts +1 -0
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +1 -0
- package/lib/errors.js.map +1 -1
- package/lib/json-patch.d.ts +25 -32
- package/lib/json-patch.d.ts.map +1 -1
- package/lib/json-patch.js +23 -95
- package/lib/json-patch.js.map +1 -1
- package/lib/pagination.d.ts +37 -51
- package/lib/pagination.d.ts.map +1 -1
- package/lib/pagination.js +72 -90
- package/lib/pagination.js.map +1 -1
- package/lib/protocol-http.d.ts +74 -0
- package/lib/protocol-http.d.ts.map +1 -0
- package/lib/protocol-http.js +554 -0
- package/lib/protocol-http.js.map +1 -0
- package/lib/protocol-rest.d.ts +124 -0
- package/lib/protocol-rest.d.ts.map +1 -0
- package/lib/protocol-rest.js +242 -0
- package/lib/protocol-rest.js.map +1 -0
- package/lib/retry.d.ts +8 -2
- package/lib/retry.d.ts.map +1 -1
- package/lib/retry.js +21 -15
- package/lib/retry.js.map +1 -1
- package/lib/schema.d.ts +7 -8
- package/lib/schema.d.ts.map +1 -1
- package/lib/schema.js +7 -8
- package/lib/schema.js.map +1 -1
- package/lib/trait.d.ts +150 -0
- package/lib/trait.d.ts.map +1 -0
- package/lib/trait.js +107 -0
- package/lib/trait.js.map +1 -0
- package/package.json +18 -75
- package/src/api.ts +446 -0
- package/src/codegen/cli.ts +268 -0
- package/src/codegen/emit.ts +207 -0
- package/src/codegen/format.ts +47 -0
- package/src/codegen/generator.ts +1153 -0
- package/src/codegen/graph.ts +151 -0
- package/src/codegen/members.ts +71 -0
- package/src/codegen/naming.ts +86 -0
- package/src/codegen/openapi-cli.ts +166 -0
- package/src/codegen/openapi.ts +1450 -0
- package/src/codegen/operations.ts +76 -0
- package/src/codegen/pagination.ts +71 -0
- package/src/codegen/prelude.ts +70 -0
- package/src/error-category.ts +84 -0
- package/src/errors.ts +2 -0
- package/src/json-patch.ts +26 -110
- package/src/pagination.ts +86 -142
- package/src/protocol-http.ts +699 -0
- package/src/protocol-rest.ts +367 -0
- package/src/retry.ts +20 -21
- package/src/schema.ts +7 -8
- package/src/trait.ts +238 -0
- package/README.md +0 -30
- package/lib/client.d.ts +0 -167
- package/lib/client.d.ts.map +0 -1
- package/lib/client.js +0 -659
- package/lib/client.js.map +0 -1
- package/lib/schemas.d.ts +0 -60
- package/lib/schemas.d.ts.map +0 -1
- package/lib/schemas.js +0 -79
- package/lib/schemas.js.map +0 -1
- package/lib/sensitive.d.ts +0 -71
- package/lib/sensitive.d.ts.map +0 -1
- package/lib/sensitive.js +0 -96
- package/lib/sensitive.js.map +0 -1
- package/lib/traits.d.ts +0 -421
- package/lib/traits.d.ts.map +0 -1
- package/lib/traits.js +0 -737
- package/lib/traits.js.map +0 -1
- package/src/client.ts +0 -1177
- package/src/schemas.ts +0 -128
- package/src/sensitive.ts +0 -119
- package/src/traits.ts +0 -996
package/lib/json-patch.js
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JSON Patch (RFC 6902) Implementation
|
|
3
3
|
*
|
|
4
|
-
* Provides a unified spec patching system for all SDKs.
|
|
5
|
-
*
|
|
6
|
-
* to add error
|
|
4
|
+
* Provides a unified spec patching system for all SDKs. Patches are applied to
|
|
5
|
+
* generated intermediary specs (Smithy models, OpenAPI docs, …) before code
|
|
6
|
+
* generation to add typed error shapes, rename operations, mark nullable
|
|
7
|
+
* fields, etc. Ported from the distilled repo's `core/json-patch`.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* import { applyAllPatches } from "@distilled.cloud/core/json-patch";
|
|
11
|
-
*
|
|
12
|
-
* const spec = JSON.parse(fs.readFileSync("openapi.json", "utf-8"));
|
|
13
|
-
* const { applied, errors } = applyAllPatches(spec, "./patches");
|
|
14
|
-
* ```
|
|
9
|
+
* Pure functions only — callers load patch files themselves (the generators
|
|
10
|
+
* use Effect's FileSystem) and hand the parsed operations to `applyPatch`.
|
|
15
11
|
*/
|
|
16
|
-
import * as fs from "fs";
|
|
17
|
-
import * as path from "path";
|
|
18
12
|
// ============================================================================
|
|
19
13
|
// JSON Pointer (RFC 6901)
|
|
20
14
|
// ============================================================================
|
|
21
|
-
/**
|
|
22
|
-
* Parse a JSON Pointer (RFC 6901) path into segments.
|
|
23
|
-
*/
|
|
15
|
+
/** Parse a JSON Pointer (RFC 6901) path into segments. */
|
|
24
16
|
export function parseJsonPointer(pointer) {
|
|
25
17
|
if (pointer === "")
|
|
26
18
|
return [];
|
|
@@ -32,9 +24,7 @@ export function parseJsonPointer(pointer) {
|
|
|
32
24
|
.split("/")
|
|
33
25
|
.map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
34
26
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Get a value at a JSON Pointer path.
|
|
37
|
-
*/
|
|
27
|
+
/** Get a value at a JSON Pointer path. */
|
|
38
28
|
export function getValueAtPath(obj, pointer) {
|
|
39
29
|
const segments = parseJsonPointer(pointer);
|
|
40
30
|
let current = obj;
|
|
@@ -52,9 +42,7 @@ export function getValueAtPath(obj, pointer) {
|
|
|
52
42
|
}
|
|
53
43
|
return current;
|
|
54
44
|
}
|
|
55
|
-
/**
|
|
56
|
-
* Set a value at a JSON Pointer path.
|
|
57
|
-
*/
|
|
45
|
+
/** Set a value at a JSON Pointer path. */
|
|
58
46
|
export function setValueAtPath(obj, pointer, value) {
|
|
59
47
|
const segments = parseJsonPointer(pointer);
|
|
60
48
|
if (segments.length === 0) {
|
|
@@ -67,8 +55,7 @@ export function setValueAtPath(obj, pointer, value) {
|
|
|
67
55
|
throw new Error(`Cannot traverse path ${pointer}: not an object`);
|
|
68
56
|
}
|
|
69
57
|
if (Array.isArray(current)) {
|
|
70
|
-
|
|
71
|
-
current = current[index];
|
|
58
|
+
current = current[parseInt(segment, 10)];
|
|
72
59
|
}
|
|
73
60
|
else {
|
|
74
61
|
current = current[segment];
|
|
@@ -79,21 +66,18 @@ export function setValueAtPath(obj, pointer, value) {
|
|
|
79
66
|
throw new Error(`Cannot set value at path ${pointer}: parent is not an object`);
|
|
80
67
|
}
|
|
81
68
|
if (Array.isArray(current)) {
|
|
82
|
-
const index = lastSegment === "-" ? current.length : parseInt(lastSegment, 10);
|
|
83
69
|
if (lastSegment === "-") {
|
|
84
70
|
current.push(value);
|
|
85
71
|
}
|
|
86
72
|
else {
|
|
87
|
-
current[
|
|
73
|
+
current[parseInt(lastSegment, 10)] = value;
|
|
88
74
|
}
|
|
89
75
|
}
|
|
90
76
|
else {
|
|
91
77
|
current[lastSegment] = value;
|
|
92
78
|
}
|
|
93
79
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Remove a value at a JSON Pointer path.
|
|
96
|
-
*/
|
|
80
|
+
/** Remove a value at a JSON Pointer path. */
|
|
97
81
|
export function removeValueAtPath(obj, pointer) {
|
|
98
82
|
const segments = parseJsonPointer(pointer);
|
|
99
83
|
if (segments.length === 0) {
|
|
@@ -126,9 +110,7 @@ export function removeValueAtPath(obj, pointer) {
|
|
|
126
110
|
// ============================================================================
|
|
127
111
|
// Patch Operations
|
|
128
112
|
// ============================================================================
|
|
129
|
-
/**
|
|
130
|
-
* Apply a single JSON Patch operation.
|
|
131
|
-
*/
|
|
113
|
+
/** Apply a single JSON Patch operation (mutates `obj` in place). */
|
|
132
114
|
export function applyOperation(obj, operation) {
|
|
133
115
|
switch (operation.op) {
|
|
134
116
|
case "add":
|
|
@@ -146,6 +128,9 @@ export function applyOperation(obj, operation) {
|
|
|
146
128
|
if (!operation.from)
|
|
147
129
|
throw new Error("move operation requires 'from'");
|
|
148
130
|
const moveValue = getValueAtPath(obj, operation.from);
|
|
131
|
+
if (moveValue === undefined) {
|
|
132
|
+
throw new Error(`Cannot move from path ${operation.from}: not an object`);
|
|
133
|
+
}
|
|
149
134
|
removeValueAtPath(obj, operation.from);
|
|
150
135
|
setValueAtPath(obj, operation.path, moveValue);
|
|
151
136
|
break;
|
|
@@ -168,9 +153,7 @@ export function applyOperation(obj, operation) {
|
|
|
168
153
|
throw new Error(`Unknown operation: ${operation.op}`);
|
|
169
154
|
}
|
|
170
155
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Apply a JSON Patch to an object (mutates in place).
|
|
173
|
-
*/
|
|
156
|
+
/** Apply a JSON Patch to an object (mutates in place). */
|
|
174
157
|
export function applyPatch(obj, patch) {
|
|
175
158
|
for (const operation of patch) {
|
|
176
159
|
applyOperation(obj, operation);
|
|
@@ -178,69 +161,14 @@ export function applyPatch(obj, patch) {
|
|
|
178
161
|
}
|
|
179
162
|
/**
|
|
180
163
|
* Whether a per-operation failure is caused by the target location being
|
|
181
|
-
* absent from the spec (i.e.
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* whole generate; a patch that only adds an error
|
|
185
|
-
*
|
|
164
|
+
* absent from the spec (i.e. spec drift — the operation/shape the patch
|
|
165
|
+
* targets was renamed or removed upstream) as opposed to a malformed patch.
|
|
166
|
+
* Stale targets should be skipped with a warning rather than aborting the
|
|
167
|
+
* whole generate; a patch that only adds an error to an operation that no
|
|
168
|
+
* longer exists is harmless to drop.
|
|
186
169
|
*/
|
|
187
|
-
function isStaleTargetError(message) {
|
|
170
|
+
export function isStaleTargetError(message) {
|
|
188
171
|
return (message.includes("not an object") ||
|
|
189
172
|
message.includes("parent is not an object"));
|
|
190
173
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Load and apply all patches from a directory.
|
|
193
|
-
* Finds all *.patch.json files and applies them.
|
|
194
|
-
*
|
|
195
|
-
* Each operation within a patch file is applied independently. Operations
|
|
196
|
-
* whose target path no longer exists in the spec (vendor spec drift) are
|
|
197
|
-
* collected as `skipped` warnings and do not abort generation. Only genuine
|
|
198
|
-
* failures (malformed JSON, invalid pointers, failed `test` ops) are reported
|
|
199
|
-
* as `errors`.
|
|
200
|
-
*/
|
|
201
|
-
export function applyAllPatches(spec, patchDir) {
|
|
202
|
-
const applied = [];
|
|
203
|
-
const skipped = [];
|
|
204
|
-
const errors = [];
|
|
205
|
-
if (!fs.existsSync(patchDir)) {
|
|
206
|
-
return { applied, skipped, errors };
|
|
207
|
-
}
|
|
208
|
-
// Find all .patch.json files
|
|
209
|
-
const files = fs
|
|
210
|
-
.readdirSync(patchDir)
|
|
211
|
-
.filter((f) => f.endsWith(".patch.json"))
|
|
212
|
-
.sort(); // Sort for deterministic application order
|
|
213
|
-
for (const file of files) {
|
|
214
|
-
const filePath = path.join(patchDir, file);
|
|
215
|
-
let patchFile;
|
|
216
|
-
try {
|
|
217
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
218
|
-
patchFile = JSON.parse(content);
|
|
219
|
-
}
|
|
220
|
-
catch (error) {
|
|
221
|
-
errors.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
let appliedAny = false;
|
|
225
|
-
for (const operation of patchFile.patches) {
|
|
226
|
-
try {
|
|
227
|
-
applyOperation(spec, operation);
|
|
228
|
-
appliedAny = true;
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
232
|
-
if (isStaleTargetError(message)) {
|
|
233
|
-
skipped.push(`${file} [${operation.op} ${operation.path}]: ${message}`);
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
errors.push(`${file} [${operation.op} ${operation.path}]: ${message}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
if (appliedAny) {
|
|
241
|
-
applied.push(`${file}: ${patchFile.description}`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return { applied, skipped, errors };
|
|
245
|
-
}
|
|
246
174
|
//# sourceMappingURL=json-patch.js.map
|
package/lib/json-patch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-patch.js","sourceRoot":"","sources":["../src/json-patch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"json-patch.js","sourceRoot":"","sources":["../src/json-patch.ts"],"names":[],"mappings":"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"}
|
package/lib/pagination.d.ts
CHANGED
|
@@ -2,93 +2,79 @@
|
|
|
2
2
|
* Pagination utilities for streaming through paginated API responses.
|
|
3
3
|
*
|
|
4
4
|
* Supports multiple pagination styles:
|
|
5
|
-
* - Page-based
|
|
6
|
-
* - Cursor-based
|
|
7
|
-
* - Token-based (
|
|
5
|
+
* - Page-based: page/per_page with a page number that advances
|
|
6
|
+
* - Cursor-based: cursor/limit with an opaque next-cursor string
|
|
7
|
+
* - Token-based (AWS style): NextToken/MaxResults continuation tokens
|
|
8
|
+
* - Single: one-shot list endpoints that still expose the paginated surface
|
|
8
9
|
*
|
|
9
|
-
* Each SDK
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* ```ts
|
|
14
|
-
* import * as Pagination from "@distilled.cloud/core/pagination";
|
|
15
|
-
*
|
|
16
|
-
* // Page-based pagination
|
|
17
|
-
* const allPages = Pagination.paginatePages(listDatabases, { organization: "my-org" }, {
|
|
18
|
-
* inputToken: "page",
|
|
19
|
-
* outputToken: "next_page",
|
|
20
|
-
* items: "data",
|
|
21
|
-
* });
|
|
22
|
-
* ```
|
|
10
|
+
* Each SDK stores a {@link PaginatedTrait} on its operations (sourced from the
|
|
11
|
+
* `smithy.api#paginated` trait in its models) and picks a
|
|
12
|
+
* {@link PaginationStrategy}; these shared utilities handle the streaming.
|
|
13
|
+
* Ported from the distilled repo's `core/pagination`.
|
|
23
14
|
*/
|
|
24
15
|
import * as Effect from "effect/Effect";
|
|
25
16
|
import * as Stream from "effect/Stream";
|
|
26
17
|
/**
|
|
27
|
-
*
|
|
18
|
+
* Get a value from an object using a dot-separated path (e.g.
|
|
19
|
+
* `"resultInfo.page"`). Used for pagination traits and nested access.
|
|
28
20
|
*/
|
|
21
|
+
export declare const getPath: (obj: unknown, path: string) => unknown;
|
|
22
|
+
/** Pagination trait describing how to navigate between pages. */
|
|
29
23
|
export interface PaginatedTrait {
|
|
30
24
|
/** Pagination strategy */
|
|
31
|
-
mode?: "token" | "page" | "cursor" | "single";
|
|
25
|
+
readonly mode?: "token" | "page" | "cursor" | "single";
|
|
32
26
|
/** The name of the input member containing the page/cursor token */
|
|
33
|
-
inputToken?: string;
|
|
27
|
+
readonly inputToken?: string;
|
|
34
28
|
/** The path to the output member containing the next page/cursor token */
|
|
35
|
-
outputToken?: string;
|
|
29
|
+
readonly outputToken?: string;
|
|
36
30
|
/** The path to the output member containing the paginated items */
|
|
37
|
-
items?: string;
|
|
31
|
+
readonly items?: string;
|
|
38
32
|
/** The name of the input member that limits page size */
|
|
39
|
-
pageSize?: string;
|
|
33
|
+
readonly pageSize?: string;
|
|
40
34
|
}
|
|
41
|
-
export type PaginationStrategy = <Input extends Record<string, unknown>, Output, E, R
|
|
35
|
+
export type PaginationStrategy = <Input extends Record<string, unknown>, Output, E, R>(operation: (input: Input) => Effect.Effect<Output, E, R>, input: Input, pagination: PaginatedTrait) => Stream.Stream<Output, E, R>;
|
|
42
36
|
/**
|
|
43
37
|
* Whether a continuation token/cursor returned by a paginated operation
|
|
44
38
|
* means "no more pages".
|
|
45
39
|
*
|
|
46
40
|
* APIs mark the terminal page by omitting the output token, returning it
|
|
47
|
-
* as `null`, or
|
|
48
|
-
* token re-requests the first page
|
|
49
|
-
*
|
|
41
|
+
* as `null`, or — for several services (e.g. SSM, CloudWatch Logs) — as an
|
|
42
|
+
* empty string. Treating `""` as a live token re-requests the first page
|
|
43
|
+
* forever (or fails with a ValidationException). Object tokens (e.g.
|
|
44
|
+
* DynamoDB's `LastEvaluatedKey`) are always truthy and unaffected.
|
|
50
45
|
*/
|
|
51
46
|
export declare const isTerminalToken: (token: unknown) => boolean;
|
|
52
47
|
/**
|
|
53
|
-
*
|
|
48
|
+
* Stream for single-shot list endpoints that still expose the paginated
|
|
49
|
+
* surface — emits exactly one page.
|
|
54
50
|
*/
|
|
55
51
|
export declare const paginateSingle: PaginationStrategy;
|
|
56
52
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* @param input - The initial input (without page parameter)
|
|
61
|
-
* @param pagination - The pagination trait configuration
|
|
62
|
-
* @returns A Stream of full page responses
|
|
53
|
+
* Stream of pages using page-number pagination. The next page is taken from
|
|
54
|
+
* `outputToken` when it advances; otherwise the page number is incremented,
|
|
55
|
+
* terminating when a page comes back with no items (or no token).
|
|
63
56
|
*/
|
|
64
|
-
export declare const paginatePageNumber: <Input extends Record<string, unknown>, Output, E, R
|
|
57
|
+
export declare const paginatePageNumber: <Input extends Record<string, unknown>, Output, E, R>(operation: (input: Input) => Effect.Effect<Output, E, R>, input: Input, pagination: PaginatedTrait) => Stream.Stream<Output, E, R>;
|
|
65
58
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* @param operation - The paginated operation to call
|
|
69
|
-
* @param input - The initial input (without cursor parameter)
|
|
70
|
-
* @param pagination - The pagination trait configuration
|
|
71
|
-
* @returns A Stream of full page responses
|
|
59
|
+
* Stream of pages using cursor-based pagination — follow `outputToken`
|
|
60
|
+
* cursors until one comes back absent.
|
|
72
61
|
*/
|
|
73
|
-
export declare const paginateCursor: <Input extends Record<string, unknown>, Output, E, R
|
|
62
|
+
export declare const paginateCursor: <Input extends Record<string, unknown>, Output, E, R>(operation: (input: Input) => Effect.Effect<Output, E, R>, input: Input, pagination: PaginatedTrait) => Stream.Stream<Output, E, R>;
|
|
74
63
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* @param operation - The paginated operation to call
|
|
78
|
-
* @param input - The initial input
|
|
79
|
-
* @param pagination - The pagination trait configuration
|
|
80
|
-
* @returns A Stream of full page responses
|
|
64
|
+
* Stream of pages using token-based pagination — pass `outputToken` back as
|
|
65
|
+
* `inputToken` until it comes back absent.
|
|
81
66
|
*/
|
|
82
|
-
export declare const paginateToken: <Input extends Record<string, unknown>, Output, E, R
|
|
67
|
+
export declare const paginateToken: <Input extends Record<string, unknown>, Output, E, R>(operation: (input: Input) => Effect.Effect<Output, E, R>, input: Input, pagination: PaginatedTrait) => Stream.Stream<Output, E, R>;
|
|
83
68
|
/**
|
|
84
|
-
* Shared default pagination dispatcher for SDKs that use generic
|
|
69
|
+
* Shared default pagination dispatcher for SDKs that use generic
|
|
70
|
+
* token/cursor/page traversal.
|
|
85
71
|
*/
|
|
86
72
|
export declare const paginateWithDefaults: PaginationStrategy;
|
|
87
73
|
/**
|
|
88
74
|
* Extracts individual items from a page stream.
|
|
89
75
|
*
|
|
90
76
|
* @param pages - A stream of page responses
|
|
91
|
-
* @param itemsPath - Dot-separated path to the items array in the page
|
|
77
|
+
* @param itemsPath - Dot-separated path to the items array in the page
|
|
92
78
|
* @returns A Stream of individual items
|
|
93
79
|
*/
|
|
94
80
|
export declare const extractItems: <Output, Item, E, R>(pages: Stream.Stream<Output, E, R>, itemsPath: string) => Stream.Stream<Item, E, R>;
|
package/lib/pagination.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,OAAO,QAAS,OAAO,QAAQ,MAAM,KAAG,OAUpD,CAAC;AAMF,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACvD,oEAAoE;IACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,kBAAkB,GAAG,CAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,MAAM,EACN,CAAC,EACD,CAAC,EAED,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,EACxD,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,cAAc,KACvB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AAIjC;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,UAAW,OAAO,KAAG,OACM,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,kBAG1B,CAAC;AAMJ;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,MAAM,EACN,CAAC,EACD,CAAC,aAEU,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,SACjD,KAAK,cACA,cAAc,KACzB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAiD5B,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,MAAM,EACN,CAAC,EACD,CAAC,aAEU,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,SACjD,KAAK,cACA,cAAc,KACzB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAsC5B,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,MAAM,EACN,CAAC,EACD,CAAC,aAEU,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,SACjD,KAAK,cACA,cAAc,KACzB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAgC5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,kBAkBlC,CAAC;AAMF;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,SACtC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,aACvB,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAMxB,CAAC"}
|
package/lib/pagination.js
CHANGED
|
@@ -2,80 +2,76 @@
|
|
|
2
2
|
* Pagination utilities for streaming through paginated API responses.
|
|
3
3
|
*
|
|
4
4
|
* Supports multiple pagination styles:
|
|
5
|
-
* - Page-based
|
|
6
|
-
* - Cursor-based
|
|
7
|
-
* - Token-based (
|
|
5
|
+
* - Page-based: page/per_page with a page number that advances
|
|
6
|
+
* - Cursor-based: cursor/limit with an opaque next-cursor string
|
|
7
|
+
* - Token-based (AWS style): NextToken/MaxResults continuation tokens
|
|
8
|
+
* - Single: one-shot list endpoints that still expose the paginated surface
|
|
8
9
|
*
|
|
9
|
-
* Each SDK
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* ```ts
|
|
14
|
-
* import * as Pagination from "@distilled.cloud/core/pagination";
|
|
15
|
-
*
|
|
16
|
-
* // Page-based pagination
|
|
17
|
-
* const allPages = Pagination.paginatePages(listDatabases, { organization: "my-org" }, {
|
|
18
|
-
* inputToken: "page",
|
|
19
|
-
* outputToken: "next_page",
|
|
20
|
-
* items: "data",
|
|
21
|
-
* });
|
|
22
|
-
* ```
|
|
10
|
+
* Each SDK stores a {@link PaginatedTrait} on its operations (sourced from the
|
|
11
|
+
* `smithy.api#paginated` trait in its models) and picks a
|
|
12
|
+
* {@link PaginationStrategy}; these shared utilities handle the streaming.
|
|
13
|
+
* Ported from the distilled repo's `core/pagination`.
|
|
23
14
|
*/
|
|
24
15
|
import * as Effect from "effect/Effect";
|
|
25
16
|
import * as Stream from "effect/Stream";
|
|
26
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Get a value from an object using a dot-separated path (e.g.
|
|
19
|
+
* `"resultInfo.page"`). Used for pagination traits and nested access.
|
|
20
|
+
*/
|
|
21
|
+
export const getPath = (obj, path) => {
|
|
22
|
+
const parts = path.split(".");
|
|
23
|
+
let current = obj;
|
|
24
|
+
for (const part of parts) {
|
|
25
|
+
if (current == null || typeof current !== "object") {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
current = current[part];
|
|
29
|
+
}
|
|
30
|
+
return current;
|
|
31
|
+
};
|
|
27
32
|
const missingPaginationConfig = (kind) => Stream.die(new Error(kind));
|
|
28
33
|
/**
|
|
29
34
|
* Whether a continuation token/cursor returned by a paginated operation
|
|
30
35
|
* means "no more pages".
|
|
31
36
|
*
|
|
32
37
|
* APIs mark the terminal page by omitting the output token, returning it
|
|
33
|
-
* as `null`, or
|
|
34
|
-
* token re-requests the first page
|
|
35
|
-
*
|
|
38
|
+
* as `null`, or — for several services (e.g. SSM, CloudWatch Logs) — as an
|
|
39
|
+
* empty string. Treating `""` as a live token re-requests the first page
|
|
40
|
+
* forever (or fails with a ValidationException). Object tokens (e.g.
|
|
41
|
+
* DynamoDB's `LastEvaluatedKey`) are always truthy and unaffected.
|
|
36
42
|
*/
|
|
37
43
|
export const isTerminalToken = (token) => token === undefined || token === null || token === "";
|
|
38
44
|
/**
|
|
39
|
-
*
|
|
45
|
+
* Stream for single-shot list endpoints that still expose the paginated
|
|
46
|
+
* surface — emits exactly one page.
|
|
40
47
|
*/
|
|
41
|
-
export const paginateSingle = (operation, input
|
|
48
|
+
export const paginateSingle = (operation, input) => Stream.make(input).pipe(Stream.mapEffect((requestPayload) => operation(requestPayload)));
|
|
42
49
|
// ============================================================================
|
|
43
|
-
// Page-based Pagination
|
|
50
|
+
// Page-based Pagination
|
|
44
51
|
// ============================================================================
|
|
45
52
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* @param input - The initial input (without page parameter)
|
|
50
|
-
* @param pagination - The pagination trait configuration
|
|
51
|
-
* @returns A Stream of full page responses
|
|
53
|
+
* Stream of pages using page-number pagination. The next page is taken from
|
|
54
|
+
* `outputToken` when it advances; otherwise the page number is incremented,
|
|
55
|
+
* terminating when a page comes back with no items (or no token).
|
|
52
56
|
*/
|
|
53
|
-
export const paginatePageNumber = (operation, input, pagination
|
|
57
|
+
export const paginatePageNumber = (operation, input, pagination) => {
|
|
54
58
|
const inputToken = pagination.inputToken;
|
|
55
59
|
const outputToken = pagination.outputToken;
|
|
56
|
-
const inputRecord = input;
|
|
57
60
|
if (!inputToken || !outputToken) {
|
|
58
61
|
return missingPaginationConfig("Page-number pagination requires inputToken and outputToken");
|
|
59
62
|
}
|
|
60
|
-
const startPage = typeof
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const unfoldFn = (state) => Effect.gen(function* () {
|
|
64
|
-
if (state.done) {
|
|
63
|
+
const startPage = typeof input[inputToken] === "number" ? input[inputToken] : 1;
|
|
64
|
+
return Stream.unfold({ page: startPage, done: false }, (state) => Effect.gen(function* () {
|
|
65
|
+
if (state.done)
|
|
65
66
|
return undefined;
|
|
66
|
-
}
|
|
67
|
-
const
|
|
68
|
-
...input,
|
|
69
|
-
[inputToken]: state.page,
|
|
70
|
-
};
|
|
71
|
-
const response = yield* operation(requestPayload, requestOptions);
|
|
67
|
+
const requestPayload = { ...input, [inputToken]: state.page };
|
|
68
|
+
const response = yield* operation(requestPayload);
|
|
72
69
|
const nextPage = getPath(response, outputToken);
|
|
73
|
-
// Some APIs report the CURRENT page at `outputToken` rather than
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
// absent).
|
|
70
|
+
// Some APIs report the CURRENT page at `outputToken` rather than the
|
|
71
|
+
// next one (e.g. Cloudflare's `result_info.page`). Taking that value as
|
|
72
|
+
// the next page re-requests the same page forever. Only accept an
|
|
73
|
+
// *advancing* page number; otherwise advance by one and terminate when
|
|
74
|
+
// a page comes back with no items (or the token is absent).
|
|
79
75
|
const items = pagination.items
|
|
80
76
|
? getPath(response, pagination.items)
|
|
81
77
|
: undefined;
|
|
@@ -88,99 +84,85 @@ export const paginatePageNumber = (operation, input, pagination, requestOptions)
|
|
|
88
84
|
(items !== undefined && items.length === 0),
|
|
89
85
|
};
|
|
90
86
|
return [response, nextState];
|
|
91
|
-
});
|
|
92
|
-
return Stream.unfold({ page: startPage, done: false }, unfoldFn);
|
|
87
|
+
}));
|
|
93
88
|
};
|
|
94
89
|
// ============================================================================
|
|
95
|
-
// Cursor-based Pagination
|
|
90
|
+
// Cursor-based Pagination
|
|
96
91
|
// ============================================================================
|
|
97
92
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* @param operation - The paginated operation to call
|
|
101
|
-
* @param input - The initial input (without cursor parameter)
|
|
102
|
-
* @param pagination - The pagination trait configuration
|
|
103
|
-
* @returns A Stream of full page responses
|
|
93
|
+
* Stream of pages using cursor-based pagination — follow `outputToken`
|
|
94
|
+
* cursors until one comes back absent.
|
|
104
95
|
*/
|
|
105
|
-
export const paginateCursor = (operation, input, pagination
|
|
96
|
+
export const paginateCursor = (operation, input, pagination) => {
|
|
106
97
|
const inputToken = pagination.inputToken;
|
|
107
98
|
const outputToken = pagination.outputToken;
|
|
108
|
-
const inputRecord = input;
|
|
109
99
|
if (!inputToken || !outputToken) {
|
|
110
100
|
return missingPaginationConfig("Cursor pagination requires inputToken and outputToken");
|
|
111
101
|
}
|
|
112
|
-
const startCursor = typeof
|
|
113
|
-
?
|
|
102
|
+
const startCursor = typeof input[inputToken] === "string"
|
|
103
|
+
? input[inputToken]
|
|
114
104
|
: undefined;
|
|
115
|
-
|
|
116
|
-
if (state.done)
|
|
105
|
+
return Stream.unfold({ cursor: startCursor, done: false }, (state) => Effect.gen(function* () {
|
|
106
|
+
if (state.done)
|
|
117
107
|
return undefined;
|
|
118
|
-
}
|
|
119
108
|
const requestPayload = {
|
|
120
109
|
...input,
|
|
121
110
|
...(state.cursor ? { [inputToken]: state.cursor } : {}),
|
|
122
111
|
};
|
|
123
|
-
const response = yield* operation(requestPayload
|
|
112
|
+
const response = yield* operation(requestPayload);
|
|
124
113
|
const nextCursor = getPath(response, outputToken);
|
|
125
114
|
const nextState = {
|
|
126
115
|
cursor: nextCursor ?? undefined,
|
|
127
116
|
done: isTerminalToken(nextCursor),
|
|
128
117
|
};
|
|
129
118
|
return [response, nextState];
|
|
130
|
-
});
|
|
131
|
-
return Stream.unfold({ cursor: startCursor, done: false }, unfoldFn);
|
|
119
|
+
}));
|
|
132
120
|
};
|
|
133
121
|
// ============================================================================
|
|
134
122
|
// Token-based Pagination (AWS style)
|
|
135
123
|
// ============================================================================
|
|
136
124
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* @param operation - The paginated operation to call
|
|
140
|
-
* @param input - The initial input
|
|
141
|
-
* @param pagination - The pagination trait configuration
|
|
142
|
-
* @returns A Stream of full page responses
|
|
125
|
+
* Stream of pages using token-based pagination — pass `outputToken` back as
|
|
126
|
+
* `inputToken` until it comes back absent.
|
|
143
127
|
*/
|
|
144
|
-
export const paginateToken = (operation, input, pagination
|
|
128
|
+
export const paginateToken = (operation, input, pagination) => {
|
|
145
129
|
const inputToken = pagination.inputToken;
|
|
146
130
|
const outputToken = pagination.outputToken;
|
|
147
|
-
const inputRecord = input;
|
|
148
131
|
if (!inputToken || !outputToken) {
|
|
149
132
|
return missingPaginationConfig("Token pagination requires inputToken and outputToken");
|
|
150
133
|
}
|
|
151
|
-
const startToken =
|
|
152
|
-
|
|
153
|
-
if (state.done)
|
|
134
|
+
const startToken = input[inputToken];
|
|
135
|
+
return Stream.unfold({ token: startToken, done: false }, (state) => Effect.gen(function* () {
|
|
136
|
+
if (state.done)
|
|
154
137
|
return undefined;
|
|
155
|
-
}
|
|
156
138
|
const requestPayload = state.token !== undefined
|
|
157
139
|
? { ...input, [inputToken]: state.token }
|
|
158
140
|
: input;
|
|
159
|
-
const response = yield* operation(requestPayload
|
|
141
|
+
const response = yield* operation(requestPayload);
|
|
160
142
|
const nextToken = getPath(response, outputToken);
|
|
161
143
|
const nextState = {
|
|
162
144
|
token: nextToken,
|
|
163
145
|
done: isTerminalToken(nextToken),
|
|
164
146
|
};
|
|
165
147
|
return [response, nextState];
|
|
166
|
-
});
|
|
167
|
-
return Stream.unfold({ token: startToken, done: false }, unfoldFn);
|
|
148
|
+
}));
|
|
168
149
|
};
|
|
169
150
|
/**
|
|
170
|
-
* Shared default pagination dispatcher for SDKs that use generic
|
|
151
|
+
* Shared default pagination dispatcher for SDKs that use generic
|
|
152
|
+
* token/cursor/page traversal.
|
|
171
153
|
*/
|
|
172
|
-
export const paginateWithDefaults = (operation, input, pagination
|
|
154
|
+
export const paginateWithDefaults = (operation, input, pagination) => {
|
|
173
155
|
const mode = pagination.mode ?? "token";
|
|
174
156
|
switch (mode) {
|
|
175
157
|
case "page":
|
|
176
|
-
return paginatePageNumber(operation, input, pagination
|
|
158
|
+
return paginatePageNumber(operation, input, pagination);
|
|
177
159
|
case "cursor":
|
|
178
|
-
return paginateCursor(operation, input, pagination
|
|
160
|
+
return paginateCursor(operation, input, pagination);
|
|
179
161
|
case "single":
|
|
180
|
-
return
|
|
162
|
+
return paginateSingle(operation, input, pagination);
|
|
181
163
|
case "token":
|
|
182
164
|
default:
|
|
183
|
-
return paginateToken(operation, input, pagination
|
|
165
|
+
return paginateToken(operation, input, pagination);
|
|
184
166
|
}
|
|
185
167
|
};
|
|
186
168
|
// ============================================================================
|
|
@@ -190,7 +172,7 @@ export const paginateWithDefaults = (operation, input, pagination, requestOption
|
|
|
190
172
|
* Extracts individual items from a page stream.
|
|
191
173
|
*
|
|
192
174
|
* @param pages - A stream of page responses
|
|
193
|
-
* @param itemsPath - Dot-separated path to the items array in the page
|
|
175
|
+
* @param itemsPath - Dot-separated path to the items array in the page
|
|
194
176
|
* @returns A Stream of individual items
|
|
195
177
|
*/
|
|
196
178
|
export const extractItems = (pages, itemsPath) => pages.pipe(Stream.flatMap((page) => {
|