@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.
Files changed (132) hide show
  1. package/lib/api.d.ts +165 -0
  2. package/lib/api.d.ts.map +1 -0
  3. package/lib/api.js +178 -0
  4. package/lib/api.js.map +1 -0
  5. package/lib/codegen/cli.d.ts +29 -0
  6. package/lib/codegen/cli.d.ts.map +1 -0
  7. package/lib/codegen/cli.js +165 -0
  8. package/lib/codegen/cli.js.map +1 -0
  9. package/lib/codegen/emit.d.ts +129 -0
  10. package/lib/codegen/emit.d.ts.map +1 -0
  11. package/lib/codegen/emit.js +105 -0
  12. package/lib/codegen/emit.js.map +1 -0
  13. package/lib/codegen/format.d.ts +23 -0
  14. package/lib/codegen/format.d.ts.map +1 -0
  15. package/lib/codegen/format.js +28 -0
  16. package/lib/codegen/format.js.map +1 -0
  17. package/lib/codegen/generator.d.ts +334 -0
  18. package/lib/codegen/generator.d.ts.map +1 -0
  19. package/lib/codegen/generator.js +691 -0
  20. package/lib/codegen/generator.js.map +1 -0
  21. package/lib/codegen/graph.d.ts +36 -0
  22. package/lib/codegen/graph.d.ts.map +1 -0
  23. package/lib/codegen/graph.js +136 -0
  24. package/lib/codegen/graph.js.map +1 -0
  25. package/lib/codegen/members.d.ts +25 -0
  26. package/lib/codegen/members.d.ts.map +1 -0
  27. package/lib/codegen/members.js +55 -0
  28. package/lib/codegen/members.js.map +1 -0
  29. package/lib/codegen/naming.d.ts +29 -0
  30. package/lib/codegen/naming.d.ts.map +1 -0
  31. package/lib/codegen/naming.js +74 -0
  32. package/lib/codegen/naming.js.map +1 -0
  33. package/lib/codegen/openapi-cli.d.ts +38 -0
  34. package/lib/codegen/openapi-cli.d.ts.map +1 -0
  35. package/lib/codegen/openapi-cli.js +107 -0
  36. package/lib/codegen/openapi-cli.js.map +1 -0
  37. package/lib/codegen/openapi.d.ts +115 -0
  38. package/lib/codegen/openapi.d.ts.map +1 -0
  39. package/lib/codegen/openapi.js +1220 -0
  40. package/lib/codegen/openapi.js.map +1 -0
  41. package/lib/codegen/operations.d.ts +24 -0
  42. package/lib/codegen/operations.d.ts.map +1 -0
  43. package/lib/codegen/operations.js +56 -0
  44. package/lib/codegen/operations.js.map +1 -0
  45. package/lib/codegen/pagination.d.ts +39 -0
  46. package/lib/codegen/pagination.d.ts.map +1 -0
  47. package/lib/codegen/pagination.js +33 -0
  48. package/lib/codegen/pagination.js.map +1 -0
  49. package/lib/codegen/prelude.d.ts +15 -0
  50. package/lib/codegen/prelude.d.ts.map +1 -0
  51. package/lib/codegen/prelude.js +60 -0
  52. package/lib/codegen/prelude.js.map +1 -0
  53. package/lib/error-category.d.ts +28 -0
  54. package/lib/error-category.d.ts.map +1 -0
  55. package/lib/error-category.js +46 -0
  56. package/lib/error-category.js.map +1 -0
  57. package/lib/errors.d.ts +1 -0
  58. package/lib/errors.d.ts.map +1 -1
  59. package/lib/errors.js +1 -0
  60. package/lib/errors.js.map +1 -1
  61. package/lib/json-patch.d.ts +25 -32
  62. package/lib/json-patch.d.ts.map +1 -1
  63. package/lib/json-patch.js +23 -95
  64. package/lib/json-patch.js.map +1 -1
  65. package/lib/pagination.d.ts +37 -51
  66. package/lib/pagination.d.ts.map +1 -1
  67. package/lib/pagination.js +72 -90
  68. package/lib/pagination.js.map +1 -1
  69. package/lib/protocol-http.d.ts +74 -0
  70. package/lib/protocol-http.d.ts.map +1 -0
  71. package/lib/protocol-http.js +554 -0
  72. package/lib/protocol-http.js.map +1 -0
  73. package/lib/protocol-rest.d.ts +124 -0
  74. package/lib/protocol-rest.d.ts.map +1 -0
  75. package/lib/protocol-rest.js +242 -0
  76. package/lib/protocol-rest.js.map +1 -0
  77. package/lib/retry.d.ts +8 -2
  78. package/lib/retry.d.ts.map +1 -1
  79. package/lib/retry.js +21 -15
  80. package/lib/retry.js.map +1 -1
  81. package/lib/schema.d.ts +7 -8
  82. package/lib/schema.d.ts.map +1 -1
  83. package/lib/schema.js +7 -8
  84. package/lib/schema.js.map +1 -1
  85. package/lib/trait.d.ts +150 -0
  86. package/lib/trait.d.ts.map +1 -0
  87. package/lib/trait.js +107 -0
  88. package/lib/trait.js.map +1 -0
  89. package/package.json +18 -75
  90. package/src/api.ts +446 -0
  91. package/src/codegen/cli.ts +268 -0
  92. package/src/codegen/emit.ts +207 -0
  93. package/src/codegen/format.ts +47 -0
  94. package/src/codegen/generator.ts +1153 -0
  95. package/src/codegen/graph.ts +151 -0
  96. package/src/codegen/members.ts +71 -0
  97. package/src/codegen/naming.ts +86 -0
  98. package/src/codegen/openapi-cli.ts +166 -0
  99. package/src/codegen/openapi.ts +1450 -0
  100. package/src/codegen/operations.ts +76 -0
  101. package/src/codegen/pagination.ts +71 -0
  102. package/src/codegen/prelude.ts +70 -0
  103. package/src/error-category.ts +84 -0
  104. package/src/errors.ts +2 -0
  105. package/src/json-patch.ts +26 -110
  106. package/src/pagination.ts +86 -142
  107. package/src/protocol-http.ts +699 -0
  108. package/src/protocol-rest.ts +367 -0
  109. package/src/retry.ts +20 -21
  110. package/src/schema.ts +7 -8
  111. package/src/trait.ts +238 -0
  112. package/README.md +0 -30
  113. package/lib/client.d.ts +0 -167
  114. package/lib/client.d.ts.map +0 -1
  115. package/lib/client.js +0 -659
  116. package/lib/client.js.map +0 -1
  117. package/lib/schemas.d.ts +0 -60
  118. package/lib/schemas.d.ts.map +0 -1
  119. package/lib/schemas.js +0 -79
  120. package/lib/schemas.js.map +0 -1
  121. package/lib/sensitive.d.ts +0 -71
  122. package/lib/sensitive.d.ts.map +0 -1
  123. package/lib/sensitive.js +0 -96
  124. package/lib/sensitive.js.map +0 -1
  125. package/lib/traits.d.ts +0 -421
  126. package/lib/traits.d.ts.map +0 -1
  127. package/lib/traits.js +0 -737
  128. package/lib/traits.js.map +0 -1
  129. package/src/client.ts +0 -1177
  130. package/src/schemas.ts +0 -128
  131. package/src/sensitive.ts +0 -119
  132. package/src/traits.ts +0 -996
package/src/pagination.ts CHANGED
@@ -2,47 +2,51 @@
2
2
  * Pagination utilities for streaming through paginated API responses.
3
3
  *
4
4
  * Supports multiple pagination styles:
5
- * - Page-based (e.g., PlanetScale): page/per_page with next_page number
6
- * - Cursor-based (e.g., Neon): cursor/limit with next cursor string
7
- * - Token-based (e.g., AWS): NextToken/MaxResults with continuation tokens
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 defines its own pagination trait configuration, and these
10
- * shared utilities handle the streaming logic.
11
- *
12
- * @example
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
- import { getPath } from "./traits.ts";
17
+
18
+ /**
19
+ * Get a value from an object using a dot-separated path (e.g.
20
+ * `"resultInfo.page"`). Used for pagination traits and nested access.
21
+ */
22
+ export const getPath = (obj: unknown, path: string): unknown => {
23
+ const parts = path.split(".");
24
+ let current: unknown = obj;
25
+ for (const part of parts) {
26
+ if (current == null || typeof current !== "object") {
27
+ return undefined;
28
+ }
29
+ current = (current as Record<string, unknown>)[part];
30
+ }
31
+ return current;
32
+ };
27
33
 
28
34
  // ============================================================================
29
35
  // Pagination Trait
30
36
  // ============================================================================
31
37
 
32
- /**
33
- * Pagination trait describing how to navigate between pages.
34
- */
38
+ /** Pagination trait describing how to navigate between pages. */
35
39
  export interface PaginatedTrait {
36
40
  /** Pagination strategy */
37
- mode?: "token" | "page" | "cursor" | "single";
41
+ readonly mode?: "token" | "page" | "cursor" | "single";
38
42
  /** The name of the input member containing the page/cursor token */
39
- inputToken?: string;
43
+ readonly inputToken?: string;
40
44
  /** The path to the output member containing the next page/cursor token */
41
- outputToken?: string;
45
+ readonly outputToken?: string;
42
46
  /** The path to the output member containing the paginated items */
43
- items?: string;
47
+ readonly items?: string;
44
48
  /** The name of the input member that limits page size */
45
- pageSize?: string;
49
+ readonly pageSize?: string;
46
50
  }
47
51
 
48
52
  export type PaginationStrategy = <
@@ -50,15 +54,10 @@ export type PaginationStrategy = <
50
54
  Output,
51
55
  E,
52
56
  R,
53
- RequestOptions = never,
54
57
  >(
55
- operation: (
56
- input: Input,
57
- requestOptions?: RequestOptions,
58
- ) => Effect.Effect<Output, E, R>,
58
+ operation: (input: Input) => Effect.Effect<Output, E, R>,
59
59
  input: Input,
60
60
  pagination: PaginatedTrait,
61
- requestOptions?: RequestOptions,
62
61
  ) => Stream.Stream<Output, E, R>;
63
62
 
64
63
  const missingPaginationConfig = (kind: string) => Stream.die(new Error(kind));
@@ -68,58 +67,44 @@ const missingPaginationConfig = (kind: string) => Stream.die(new Error(kind));
68
67
  * means "no more pages".
69
68
  *
70
69
  * APIs mark the terminal page by omitting the output token, returning it
71
- * as `null`, or returning it as an empty string. Treating `""` as a live
72
- * token re-requests the first page forever (the request builders skip
73
- * falsy cursors, so the same page is fetched in an infinite loop).
70
+ * as `null`, or — for several services (e.g. SSM, CloudWatch Logs) — as an
71
+ * empty string. Treating `""` as a live token re-requests the first page
72
+ * forever (or fails with a ValidationException). Object tokens (e.g.
73
+ * DynamoDB's `LastEvaluatedKey`) are always truthy and unaffected.
74
74
  */
75
75
  export const isTerminalToken = (token: unknown): boolean =>
76
76
  token === undefined || token === null || token === "";
77
77
 
78
78
  /**
79
- * Creates a stream for single-shot list endpoints that still expose the paginated API surface.
79
+ * Stream for single-shot list endpoints that still expose the paginated
80
+ * surface — emits exactly one page.
80
81
  */
81
- export const paginateSingle: PaginationStrategy = (
82
- operation,
83
- input,
84
- _pagination,
85
- requestOptions,
86
- ) =>
82
+ export const paginateSingle: PaginationStrategy = (operation, input) =>
87
83
  Stream.make(input).pipe(
88
- Stream.mapEffect((requestPayload) =>
89
- operation(requestPayload, requestOptions),
90
- ),
84
+ Stream.mapEffect((requestPayload) => operation(requestPayload)),
91
85
  );
92
86
 
93
87
  // ============================================================================
94
- // Page-based Pagination (PlanetScale style)
88
+ // Page-based Pagination
95
89
  // ============================================================================
96
90
 
97
91
  /**
98
- * Creates a stream of pages using page-number pagination.
99
- *
100
- * @param operation - The paginated operation to call
101
- * @param input - The initial input (without page parameter)
102
- * @param pagination - The pagination trait configuration
103
- * @returns A Stream of full page responses
92
+ * Stream of pages using page-number pagination. The next page is taken from
93
+ * `outputToken` when it advances; otherwise the page number is incremented,
94
+ * terminating when a page comes back with no items (or no token).
104
95
  */
105
96
  export const paginatePageNumber = <
106
97
  Input extends Record<string, unknown>,
107
98
  Output,
108
99
  E,
109
100
  R,
110
- RequestOptions = never,
111
101
  >(
112
- operation: (
113
- input: Input,
114
- requestOptions?: RequestOptions,
115
- ) => Effect.Effect<Output, E, R>,
116
- input: Omit<Input, string>,
102
+ operation: (input: Input) => Effect.Effect<Output, E, R>,
103
+ input: Input,
117
104
  pagination: PaginatedTrait,
118
- requestOptions?: RequestOptions,
119
105
  ): Stream.Stream<Output, E, R> => {
120
106
  const inputToken = pagination.inputToken;
121
107
  const outputToken = pagination.outputToken;
122
- const inputRecord = input as Record<string, unknown>;
123
108
  if (!inputToken || !outputToken) {
124
109
  return missingPaginationConfig(
125
110
  "Page-number pagination requires inputToken and outputToken",
@@ -127,34 +112,25 @@ export const paginatePageNumber = <
127
112
  }
128
113
  type State = { page: number; done: boolean };
129
114
  const startPage =
130
- typeof inputRecord[inputToken] === "number"
131
- ? (inputRecord[inputToken] as number)
132
- : 1;
115
+ typeof input[inputToken] === "number" ? (input[inputToken] as number) : 1;
133
116
 
134
- const unfoldFn = (state: State) =>
117
+ return Stream.unfold({ page: startPage, done: false } as State, (state) =>
135
118
  Effect.gen(function* () {
136
- if (state.done) {
137
- return undefined;
138
- }
119
+ if (state.done) return undefined;
139
120
 
140
- const requestPayload = {
141
- ...input,
142
- [inputToken]: state.page,
143
- } as Input;
144
-
145
- const response = yield* operation(requestPayload, requestOptions);
121
+ const requestPayload = { ...input, [inputToken]: state.page } as Input;
122
+ const response = yield* operation(requestPayload);
146
123
 
147
124
  const nextPage = getPath(response, outputToken) as
148
125
  | number
149
126
  | null
150
127
  | undefined;
151
128
 
152
- // Some APIs report the CURRENT page at `outputToken` rather than
153
- // the next one (e.g. Cloudflare's `result_info.page`). Taking that
154
- // value as the next page re-requests the same page forever. Only
155
- // accept an *advancing* page number; otherwise advance by one and
156
- // terminate when a page comes back with no items (or the token is
157
- // absent).
129
+ // Some APIs report the CURRENT page at `outputToken` rather than the
130
+ // next one (e.g. Cloudflare's `result_info.page`). Taking that value as
131
+ // the next page re-requests the same page forever. Only accept an
132
+ // *advancing* page number; otherwise advance by one and terminate when
133
+ // a page comes back with no items (or the token is absent).
158
134
  const items = pagination.items
159
135
  ? (getPath(response, pagination.items) as
160
136
  | readonly unknown[]
@@ -173,41 +149,30 @@ export const paginatePageNumber = <
173
149
  };
174
150
 
175
151
  return [response, nextState] as const;
176
- });
177
-
178
- return Stream.unfold({ page: startPage, done: false } as State, unfoldFn);
152
+ }),
153
+ );
179
154
  };
180
155
 
181
156
  // ============================================================================
182
- // Cursor-based Pagination (Neon style)
157
+ // Cursor-based Pagination
183
158
  // ============================================================================
184
159
 
185
160
  /**
186
- * Creates a stream of pages using cursor-based pagination.
187
- *
188
- * @param operation - The paginated operation to call
189
- * @param input - The initial input (without cursor parameter)
190
- * @param pagination - The pagination trait configuration
191
- * @returns A Stream of full page responses
161
+ * Stream of pages using cursor-based pagination — follow `outputToken`
162
+ * cursors until one comes back absent.
192
163
  */
193
164
  export const paginateCursor = <
194
165
  Input extends Record<string, unknown>,
195
166
  Output,
196
167
  E,
197
168
  R,
198
- RequestOptions = never,
199
169
  >(
200
- operation: (
201
- input: Input,
202
- requestOptions?: RequestOptions,
203
- ) => Effect.Effect<Output, E, R>,
204
- input: Omit<Input, string>,
170
+ operation: (input: Input) => Effect.Effect<Output, E, R>,
171
+ input: Input,
205
172
  pagination: PaginatedTrait,
206
- requestOptions?: RequestOptions,
207
173
  ): Stream.Stream<Output, E, R> => {
208
174
  const inputToken = pagination.inputToken;
209
175
  const outputToken = pagination.outputToken;
210
- const inputRecord = input as Record<string, unknown>;
211
176
  if (!inputToken || !outputToken) {
212
177
  return missingPaginationConfig(
213
178
  "Cursor pagination requires inputToken and outputToken",
@@ -215,22 +180,20 @@ export const paginateCursor = <
215
180
  }
216
181
  type State = { cursor: string | undefined; done: boolean };
217
182
  const startCursor =
218
- typeof inputRecord[inputToken] === "string"
219
- ? (inputRecord[inputToken] as string)
183
+ typeof input[inputToken] === "string"
184
+ ? (input[inputToken] as string)
220
185
  : undefined;
221
186
 
222
- const unfoldFn = (state: State) =>
187
+ return Stream.unfold({ cursor: startCursor, done: false } as State, (state) =>
223
188
  Effect.gen(function* () {
224
- if (state.done) {
225
- return undefined;
226
- }
189
+ if (state.done) return undefined;
227
190
 
228
191
  const requestPayload = {
229
192
  ...input,
230
193
  ...(state.cursor ? { [inputToken]: state.cursor } : {}),
231
194
  } as Input;
232
195
 
233
- const response = yield* operation(requestPayload, requestOptions);
196
+ const response = yield* operation(requestPayload);
234
197
 
235
198
  const nextCursor = getPath(response, outputToken) as
236
199
  | string
@@ -243,9 +206,8 @@ export const paginateCursor = <
243
206
  };
244
207
 
245
208
  return [response, nextState] as const;
246
- });
247
-
248
- return Stream.unfold({ cursor: startCursor, done: false } as State, unfoldFn);
209
+ }),
210
+ );
249
211
  };
250
212
 
251
213
  // ============================================================================
@@ -253,54 +215,39 @@ export const paginateCursor = <
253
215
  // ============================================================================
254
216
 
255
217
  /**
256
- * Creates a stream of pages using token-based pagination.
257
- *
258
- * @param operation - The paginated operation to call
259
- * @param input - The initial input
260
- * @param pagination - The pagination trait configuration
261
- * @returns A Stream of full page responses
218
+ * Stream of pages using token-based pagination — pass `outputToken` back as
219
+ * `inputToken` until it comes back absent.
262
220
  */
263
221
  export const paginateToken = <
264
222
  Input extends Record<string, unknown>,
265
223
  Output,
266
224
  E,
267
225
  R,
268
- RequestOptions = never,
269
226
  >(
270
- operation: (
271
- input: Input,
272
- requestOptions?: RequestOptions,
273
- ) => Effect.Effect<Output, E, R>,
227
+ operation: (input: Input) => Effect.Effect<Output, E, R>,
274
228
  input: Input,
275
229
  pagination: PaginatedTrait,
276
- requestOptions?: RequestOptions,
277
230
  ): Stream.Stream<Output, E, R> => {
278
231
  const inputToken = pagination.inputToken;
279
232
  const outputToken = pagination.outputToken;
280
- const inputRecord = input as Record<string, unknown>;
281
233
  if (!inputToken || !outputToken) {
282
234
  return missingPaginationConfig(
283
235
  "Token pagination requires inputToken and outputToken",
284
236
  );
285
237
  }
286
238
  type State = { token: unknown; done: boolean };
287
- const startToken = inputRecord[inputToken];
239
+ const startToken = input[inputToken];
288
240
 
289
- const unfoldFn = (state: State) =>
241
+ return Stream.unfold({ token: startToken, done: false } as State, (state) =>
290
242
  Effect.gen(function* () {
291
- if (state.done) {
292
- return undefined;
293
- }
243
+ if (state.done) return undefined;
294
244
 
295
245
  const requestPayload =
296
246
  state.token !== undefined
297
- ? { ...input, [inputToken]: state.token }
247
+ ? ({ ...input, [inputToken]: state.token } as Input)
298
248
  : input;
299
249
 
300
- const response = yield* operation(
301
- requestPayload as Input,
302
- requestOptions,
303
- );
250
+ const response = yield* operation(requestPayload);
304
251
 
305
252
  const nextToken = getPath(response, outputToken);
306
253
 
@@ -310,34 +257,31 @@ export const paginateToken = <
310
257
  };
311
258
 
312
259
  return [response, nextState] as const;
313
- });
314
-
315
- return Stream.unfold({ token: startToken, done: false } as State, unfoldFn);
260
+ }),
261
+ );
316
262
  };
317
263
 
318
264
  /**
319
- * Shared default pagination dispatcher for SDKs that use generic token/cursor/page traversal.
265
+ * Shared default pagination dispatcher for SDKs that use generic
266
+ * token/cursor/page traversal.
320
267
  */
321
268
  export const paginateWithDefaults: PaginationStrategy = (
322
269
  operation,
323
270
  input,
324
271
  pagination,
325
- requestOptions,
326
272
  ) => {
327
273
  const mode = pagination.mode ?? "token";
328
274
 
329
275
  switch (mode) {
330
276
  case "page":
331
- return paginatePageNumber(operation, input, pagination, requestOptions);
277
+ return paginatePageNumber(operation, input, pagination);
332
278
  case "cursor":
333
- return paginateCursor(operation, input, pagination, requestOptions);
279
+ return paginateCursor(operation, input, pagination);
334
280
  case "single":
335
- return missingPaginationConfig(
336
- "Single-page pagination requires a provider-specific pagination strategy",
337
- );
281
+ return paginateSingle(operation, input, pagination);
338
282
  case "token":
339
283
  default:
340
- return paginateToken(operation, input, pagination, requestOptions);
284
+ return paginateToken(operation, input, pagination);
341
285
  }
342
286
  };
343
287
 
@@ -349,7 +293,7 @@ export const paginateWithDefaults: PaginationStrategy = (
349
293
  * Extracts individual items from a page stream.
350
294
  *
351
295
  * @param pages - A stream of page responses
352
- * @param itemsPath - Dot-separated path to the items array in the page response
296
+ * @param itemsPath - Dot-separated path to the items array in the page
353
297
  * @returns A Stream of individual items
354
298
  */
355
299
  export const extractItems = <Output, Item, E, R>(