@expandai/sdk 0.19.0 → 0.20.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.
Files changed (72) hide show
  1. package/README.md +130 -4
  2. package/build/dts/AppendixMarkers.d.ts +30 -0
  3. package/build/dts/AppendixMarkers.d.ts.map +1 -0
  4. package/build/dts/Client.d.ts +41 -6
  5. package/build/dts/Client.d.ts.map +1 -1
  6. package/build/dts/Crawls.d.ts +568 -0
  7. package/build/dts/Crawls.d.ts.map +1 -0
  8. package/build/dts/EnvProxyHttpClient.d.ts +28 -0
  9. package/build/dts/EnvProxyHttpClient.d.ts.map +1 -0
  10. package/build/dts/FetchJson.d.ts +444 -37
  11. package/build/dts/FetchJson.d.ts.map +1 -1
  12. package/build/dts/Generated.d.ts +1545 -788
  13. package/build/dts/Generated.d.ts.map +1 -1
  14. package/build/dts/HttpClientHelpers.d.ts +13 -0
  15. package/build/dts/HttpClientHelpers.d.ts.map +1 -1
  16. package/build/dts/Internal.d.ts +6 -4
  17. package/build/dts/Internal.d.ts.map +1 -1
  18. package/build/dts/ParseErrorBody.d.ts +26 -0
  19. package/build/dts/ParseErrorBody.d.ts.map +1 -0
  20. package/build/dts/Playground.d.ts +20 -0
  21. package/build/dts/Playground.d.ts.map +1 -1
  22. package/build/dts/RetryAfter.d.ts +40 -0
  23. package/build/dts/RetryAfter.d.ts.map +1 -0
  24. package/build/dts/Service.d.ts +94 -45
  25. package/build/dts/Service.d.ts.map +1 -1
  26. package/build/dts/StreamPage.d.ts +10 -0
  27. package/build/dts/StreamPage.d.ts.map +1 -0
  28. package/build/dts/index.d.ts +9 -2
  29. package/build/dts/index.d.ts.map +1 -1
  30. package/build/esm/AppendixMarkers.js +307 -0
  31. package/build/esm/AppendixMarkers.js.map +1 -0
  32. package/build/esm/Client.js +96 -15
  33. package/build/esm/Client.js.map +1 -1
  34. package/build/esm/Crawls.js +786 -0
  35. package/build/esm/Crawls.js.map +1 -0
  36. package/build/esm/EnvProxyHttpClient.js +55 -0
  37. package/build/esm/EnvProxyHttpClient.js.map +1 -0
  38. package/build/esm/FetchJson.js +48 -22
  39. package/build/esm/FetchJson.js.map +1 -1
  40. package/build/esm/Generated.js +540 -112
  41. package/build/esm/Generated.js.map +1 -1
  42. package/build/esm/HttpClientHelpers.js +36 -1
  43. package/build/esm/HttpClientHelpers.js.map +1 -1
  44. package/build/esm/Internal.js +140 -33
  45. package/build/esm/Internal.js.map +1 -1
  46. package/build/esm/ParseErrorBody.js +41 -0
  47. package/build/esm/ParseErrorBody.js.map +1 -0
  48. package/build/esm/Playground.js +38 -0
  49. package/build/esm/Playground.js.map +1 -1
  50. package/build/esm/RetryAfter.js +104 -0
  51. package/build/esm/RetryAfter.js.map +1 -0
  52. package/build/esm/Service.js +96 -17
  53. package/build/esm/Service.js.map +1 -1
  54. package/build/esm/StreamPage.js +46 -0
  55. package/build/esm/StreamPage.js.map +1 -0
  56. package/build/esm/index.js +5 -1
  57. package/build/esm/index.js.map +1 -1
  58. package/package.json +8 -2
  59. package/src/AppendixMarkers.ts +370 -0
  60. package/src/Client.ts +131 -16
  61. package/src/Crawls.ts +1387 -0
  62. package/src/EnvProxyHttpClient.ts +71 -0
  63. package/src/FetchJson.ts +63 -25
  64. package/src/Generated.ts +738 -162
  65. package/src/HttpClientHelpers.ts +48 -1
  66. package/src/Internal.ts +163 -105
  67. package/src/ParseErrorBody.ts +42 -0
  68. package/src/Playground.ts +48 -0
  69. package/src/RetryAfter.ts +138 -0
  70. package/src/Service.ts +208 -25
  71. package/src/StreamPage.ts +47 -0
  72. package/src/index.ts +29 -3
package/src/Service.ts CHANGED
@@ -1,16 +1,26 @@
1
1
  import { HttpClient, HttpClientRequest, HttpClientResponse } from '@effect/platform'
2
- import { NodeHttpClient } from '@effect/platform-node'
3
- import { Context, Duration, Effect, flow, Layer, Option, Random, Schedule, Schema } from 'effect'
4
- import { ExpandSdkError } from './Error.js'
2
+ import { Cause, Context, Duration, Effect, flow, Layer, MutableRef, Option, Random, Schedule, Schema } from 'effect'
3
+ import type {
4
+ CrawlChangePolicy,
5
+ CrawlCreateOptions,
6
+ CrawlListResponse,
7
+ CrawlRequest,
8
+ CrawlRunOptions,
9
+ CrawlStatus,
10
+ CrawlSyncPolicy,
11
+ } from './Crawls.js'
12
+ import * as EnvProxyHttpClient from './EnvProxyHttpClient.js'
13
+ import { ExpandApiError, type ExpandError, ExpandSdkError } from './Error.js'
5
14
  import {
6
- type FetchObjectModeResultWithJson,
7
- type FetchSnapshotSearchResultWithJson,
15
+ type FetchObjectModeJsonResponse,
16
+ type FetchSnapshotHighlightsResultWithJson,
8
17
  fetchObjectModeJson,
9
- fetchSnapshotSearch,
18
+ fetchSnapshotHighlights,
10
19
  } from './FetchJson.js'
11
20
  import * as Generated from './Generated.js'
12
21
  import { decodeError, unexpectedStatus } from './HttpClientHelpers.js'
13
22
  import { isRetryable, toExpandError } from './Internal.js'
23
+ import { nextRetryDelay, recordRetryAfter } from './RetryAfter.js'
14
24
 
15
25
  const DEFAULT_BASE_URL = 'https://api.expand.ai'
16
26
  const DEFAULT_TIMEOUT_MS = 60_000
@@ -74,11 +84,24 @@ export type FetchResponse = string
74
84
  export type FetchRequestOptions = RequestOptions & typeof Generated.FetchFetchParams.Encoded
75
85
  export type FetchJsonParams = typeof Generated.FetchFetchJsonRequest.Encoded
76
86
  // `*WithJson` rather than the generated result: the codegen drops the statejson snippet `json`, so
77
- // the service decodes the body with the recovering schemas (see `FetchJson.ts`).
78
- export type FetchJsonResponse = FetchObjectModeResultWithJson
87
+ // the service decodes the body with the recovering schemas (see `FetchJson.ts`). A union because an
88
+ // origin that never answered has no content to describe — narrow on `_tag`.
89
+ export type FetchJsonResponse = FetchObjectModeJsonResponse
79
90
  export type FetchJsonRequestOptions = RequestOptions & typeof Generated.FetchFetchJsonParams.Encoded
80
- export type FetchSearchParams = typeof Generated.FetchFetchSearchRequest.Encoded
81
- export type FetchSearchResponse = FetchSnapshotSearchResultWithJson
91
+ export interface SnapshotHighlightsParams extends Schema.Schema.Encoded<typeof Generated.SnapshotHighlightsInput> {
92
+ readonly snapshotId: string
93
+ }
94
+ export interface SnapshotCatalogParams {
95
+ readonly snapshotId: string
96
+ readonly highlights?: undefined
97
+ }
98
+ export interface SnapshotGetParams {
99
+ readonly snapshotId: string
100
+ readonly highlights?: SnapshotHighlightsParams['highlights']
101
+ readonly include?: SnapshotHighlightsParams['include']
102
+ }
103
+ export type SnapshotCatalog = typeof Generated.SnapshotCatalog.Type
104
+ export type SnapshotHighlightsResponse = FetchSnapshotHighlightsResultWithJson
82
105
  export type BatchedParams = typeof Generated.FetchBatchedRequest.Encoded
83
106
  export type BatchedResponse = typeof Generated.BatchedRun.Type
84
107
  export interface BatchedRequestOptions extends RequestOptions {
@@ -126,6 +149,12 @@ export const buildServiceConfig = Effect.fn('buildServiceConfig')(function* (opt
126
149
  if (!apiKey) {
127
150
  return yield* new ExpandSdkError({ message: 'Missing Expand API key. Pass apiKey or set EXPAND_API_KEY.' })
128
151
  }
152
+ // Checked here, not only in the HttpClient layer, so `new ExpandClient()` keeps its
153
+ // documented sync-throw contract for bad configuration instead of rejecting later.
154
+ const invalidProxy = EnvProxyHttpClient.invalidProxyEnvMessage()
155
+ if (Option.isSome(invalidProxy)) {
156
+ return yield* new ExpandSdkError({ message: invalidProxy.value })
157
+ }
129
158
  return yield* Schema.decode(ServiceConfig)({
130
159
  apiKey,
131
160
  baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
@@ -148,7 +177,7 @@ export const buildServiceConfig = Effect.fn('buildServiceConfig')(function* (opt
148
177
  * ```
149
178
  */
150
179
  export class ExpandService extends Effect.Service<ExpandService>()('@expandai/sdk/ExpandService', {
151
- dependencies: [NodeHttpClient.layer],
180
+ dependencies: [EnvProxyHttpClient.layer],
152
181
  effect: Effect.gen(function* () {
153
182
  const config = yield* ServiceConfigTag
154
183
  const httpClient = yield* HttpClient.HttpClient
@@ -159,6 +188,9 @@ export class ExpandService extends Effect.Service<ExpandService>()('@expandai/sd
159
188
  HttpClientRequest.setHeader('x-expand-api-key', config.apiKey),
160
189
  ),
161
190
  ),
191
+ // Wraps every path, generated and hand-written alike, so `withRetries` can honour a
192
+ // `Retry-After` no matter which call produced the failure.
193
+ recordRetryAfter,
162
194
  )
163
195
  const generated = Generated.make(authedClient)
164
196
  const fetchMarkdown = (params: FetchParams, queryParams: typeof Generated.FetchFetchParams.Encoded = {}) =>
@@ -175,11 +207,15 @@ export class ExpandService extends Effect.Service<ExpandService>()('@expandai/sd
175
207
  '400': (response) => decodeError(response, Generated.FetchFetch400),
176
208
  '401': (response) => decodeError(response, Generated.AuthFailed),
177
209
  '403': (response) => decodeError(response, Generated.FetchFetch403),
178
- '413': (response) => decodeError(response, Generated.PayloadTooLarge),
179
- '429': (response) => decodeError(response, Generated.TooManyRequests),
210
+ '404': (response) => decodeError(response, Generated.FetchOriginNotFound),
211
+ '410': (response) => decodeError(response, Generated.FetchOriginGone),
212
+ '413': (response) => decodeError(response, Generated.FetchFetch413),
213
+ '415': (response) => decodeError(response, Generated.FetchUnsupportedContentType),
214
+ '422': (response) => decodeError(response, Generated.FetchVideoUnavailable),
215
+ '429': (response) => decodeError(response, Generated.FetchFetch429),
180
216
  '500': (response) => decodeError(response, Generated.InternalError),
181
- '502': (response) => decodeError(response, Generated.FetchNavigationFailed),
182
- '503': (response) => decodeError(response, Generated.ServiceUnavailable),
217
+ '502': (response) => decodeError(response, Generated.FetchFetch502),
218
+ '503': (response) => decodeError(response, Generated.FetchFetch503),
183
219
  '504': (response) => decodeError(response, Generated.FetchCaptureTimeout),
184
220
  '529': (response) => decodeError(response, Generated.FetchCapacityTimeout),
185
221
  orElse: (response) => unexpectedStatus(request, response),
@@ -196,16 +232,38 @@ export class ExpandService extends Effect.Service<ExpandService>()('@expandai/sd
196
232
  ) {
197
233
  const totalTimeoutMs = requestOptions.timeoutMs ?? config.timeoutMs
198
234
  const maxRetries = requestOptions.maxRetries ?? config.maxRetries
235
+ // The failure the schedule last chose to wait on, while the fiber is inside that wait.
236
+ // If the wall-clock budget expires mid-wait, the caller is handed this refusal rather
237
+ // than a timeout: the server said "not before X", the caller said "no later than Y",
238
+ // and a timeout would hide the 429/503 the caller branches on. Cleared before every
239
+ // attempt, so a budget that expires during a live attempt still reports as a timeout.
240
+ const pendingRefusal = MutableRef.make<Option.Option<unknown>>(Option.none())
199
241
  // `timeoutMs` is one wall-clock budget for the operation, including retries and backoff. A slow
200
242
  // valid capture may use most of that budget; only failures that arrive before it expires can retry.
201
243
  const retrySchedule = Schedule.exponential(Duration.millis(250)).pipe(
202
244
  Schedule.jittered,
203
245
  Schedule.compose(Schedule.recurs(maxRetries)),
204
246
  Schedule.whileInput((error: unknown) => isRetryable(error, totalTimeoutMs)),
247
+ // `passthrough` republishes the failure as the schedule's output so the delay function
248
+ // can see which error it is about to wait on.
249
+ Schedule.passthrough,
250
+ // Floors the delay at the server's requested wait (re-jittered) and caps it at
251
+ // `MAX_RETRY_AFTER`; the whole policy lives with its inputs in `RetryAfter.ts`.
252
+ Schedule.modifyDelayEffect((error: unknown, backoff) =>
253
+ Effect.zipLeft(
254
+ nextRetryDelay(error, backoff),
255
+ Effect.sync(() => MutableRef.set(pendingRefusal, Option.some(error))),
256
+ ),
257
+ ),
205
258
  )
206
- return yield* operation.pipe(
259
+ return yield* Effect.sync(() => MutableRef.set(pendingRefusal, Option.none())).pipe(
260
+ Effect.zipRight(operation),
207
261
  Effect.retry(retrySchedule),
208
- Effect.timeout(Duration.millis(totalTimeoutMs)),
262
+ Effect.timeoutFail({
263
+ duration: Duration.millis(totalTimeoutMs),
264
+ onTimeout: (): unknown =>
265
+ Option.getOrElse(MutableRef.get(pendingRefusal), () => new Cause.TimeoutException()),
266
+ }),
209
267
  Effect.mapError((error) => toExpandError(error, totalTimeoutMs)),
210
268
  )
211
269
  })
@@ -231,13 +289,39 @@ export class ExpandService extends Effect.Service<ExpandService>()('@expandai/sd
231
289
  )
232
290
  })
233
291
 
234
- const fetchSearch = Effect.fn('ExpandService.fetchSearch')(function* (
235
- params: FetchSearchParams,
236
- requestOptions: RequestOptions = {},
237
- ) {
238
- // `fetchSnapshotSearch` mirrors the generated call but preserves the statejson snippet `json`.
239
- return yield* withRetries(fetchSnapshotSearch(authedClient, params), requestOptions)
240
- })
292
+ const publicClient = httpClient.pipe(
293
+ HttpClient.mapRequest(HttpClientRequest.prependUrl(config.baseUrl)),
294
+ recordRetryAfter,
295
+ )
296
+ function getSnapshot(
297
+ params: SnapshotHighlightsParams,
298
+ options?: RequestOptions,
299
+ ): Effect.Effect<SnapshotHighlightsResponse, ExpandError>
300
+ function getSnapshot(
301
+ params: SnapshotCatalogParams,
302
+ options?: RequestOptions,
303
+ ): Effect.Effect<SnapshotCatalog, ExpandError>
304
+ function getSnapshot(
305
+ params: SnapshotGetParams,
306
+ options?: RequestOptions,
307
+ ): Effect.Effect<SnapshotCatalog | SnapshotHighlightsResponse, ExpandError>
308
+ function getSnapshot(
309
+ params: SnapshotGetParams,
310
+ options: RequestOptions = {},
311
+ ): Effect.Effect<SnapshotCatalog | SnapshotHighlightsResponse, ExpandError> {
312
+ return params.highlights === undefined
313
+ ? withRetries(
314
+ Generated.make(publicClient).publicSnapshotsGetCatalog(encodeURIComponent(params.snapshotId), {}),
315
+ options,
316
+ )
317
+ : withRetries(
318
+ fetchSnapshotHighlights(authedClient, params.snapshotId, {
319
+ highlights: params.highlights,
320
+ include: params.include,
321
+ }),
322
+ options,
323
+ )
324
+ }
241
325
 
242
326
  const batched = Effect.fn('ExpandService.batched')(function* (
243
327
  params: BatchedParams,
@@ -301,7 +385,106 @@ export class ExpandService extends Effect.Service<ExpandService>()('@expandai/sd
301
385
  return yield* withRetries(generated.fetchCancelBatched(id), requestOptions)
302
386
  })
303
387
 
304
- return { fetch, fetchJson, fetchSearch, batched, getBatched, cancelBatched } as const
388
+ // Crawl control routes intentionally do not use the fetch-only generator. They still
389
+ // share this service's authenticated client, wall-clock budget, and retry policy.
390
+ const crawlRequest = Effect.fn('ExpandService.crawlRequest')(function* <A>(
391
+ path: string,
392
+ method: 'GET' | 'POST' | 'PATCH',
393
+ options: RequestOptions = {},
394
+ body?: unknown,
395
+ idempotencyKey?: string,
396
+ ) {
397
+ const request = HttpClientRequest.make(method)(path)
398
+ const keyed =
399
+ idempotencyKey === undefined
400
+ ? request
401
+ : HttpClientRequest.setHeader(request, 'x-idempotency-key', idempotencyKey)
402
+ const encoded =
403
+ body === undefined
404
+ ? keyed
405
+ : yield* HttpClientRequest.bodyJson(keyed, body).pipe(
406
+ Effect.mapError((cause) => new ExpandSdkError({ message: 'Invalid Crawl request body.', cause })),
407
+ )
408
+ return yield* withRetries(
409
+ authedClient.execute(encoded).pipe(
410
+ Effect.flatMap((response) =>
411
+ (response.status >= 200 && response.status < 300
412
+ ? response.json
413
+ : response.json.pipe(Effect.catchAll(() => Effect.void))
414
+ ).pipe(
415
+ Effect.flatMap((value) => {
416
+ if (response.status >= 200 && response.status < 300) {
417
+ // The hand-written interfaces mirror the versioned Crawl HTTP contract.
418
+ return Effect.succeed(value as unknown as A)
419
+ }
420
+ const error = typeof value === 'object' && value !== null && 'error' in value ? value.error : undefined
421
+ const message =
422
+ typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string'
423
+ ? error.message
424
+ : `HTTP ${response.status}`
425
+ return new ExpandApiError({ message, status: response.status, body: value })
426
+ }),
427
+ ),
428
+ ),
429
+ ),
430
+ options,
431
+ )
432
+ })
433
+ // Mirrors the Promise client: a durable caller key is required before any request is issued,
434
+ // or an unretryable failure could not be replayed by key.
435
+ // Takes `string | undefined`: TypeScript does not stop a JavaScript caller, and
436
+ // reading `.length` off undefined threw where the contract promises a typed failure.
437
+ const requireCrawlIdempotencyKey = (
438
+ value: string | undefined,
439
+ operation: string,
440
+ ): Effect.Effect<string, ExpandSdkError> =>
441
+ value !== undefined && value.length >= 1 && value.length <= 255
442
+ ? Effect.succeed(value)
443
+ : new ExpandSdkError({ message: `${operation} requires an idempotencyKey between 1 and 255 characters.` })
444
+ function crawlById(id: string) {
445
+ const path = `/v1/crawls/${encodeURIComponent(id)}`
446
+ return {
447
+ id,
448
+ status: (options: RequestOptions = {}) => crawlRequest<CrawlStatus>(path, 'GET', options),
449
+ cancel: (options: RequestOptions = {}) => crawlRequest<CrawlStatus>(`${path}/cancel`, 'POST', options),
450
+ update: (
451
+ patch: {
452
+ readonly sync?: CrawlSyncPolicy | null
453
+ readonly changePolicy?: CrawlChangePolicy
454
+ readonly maxPages?: number
455
+ },
456
+ options: RequestOptions = {},
457
+ ) => crawlRequest<CrawlStatus>(path, 'PATCH', options, patch),
458
+ run: (options: CrawlRunOptions & RequestOptions) =>
459
+ requireCrawlIdempotencyKey(options.idempotencyKey, 'Crawl.run').pipe(
460
+ Effect.flatMap((idempotencyKey) =>
461
+ crawlRequest<CrawlStatus>(
462
+ `${path}/runs${options.replayOnly ? '/replay' : ''}`,
463
+ 'POST',
464
+ options,
465
+ undefined,
466
+ idempotencyKey,
467
+ ),
468
+ ),
469
+ ),
470
+ }
471
+ }
472
+ const crawls = Object.assign(crawlById, {
473
+ list: (options: { readonly limit?: number } & RequestOptions = {}) =>
474
+ crawlRequest<CrawlListResponse>(
475
+ options.limit === undefined ? '/v1/crawls' : `/v1/crawls?limit=${encodeURIComponent(options.limit)}`,
476
+ 'GET',
477
+ options,
478
+ ),
479
+ create: (request: CrawlRequest, options: CrawlCreateOptions & RequestOptions) =>
480
+ requireCrawlIdempotencyKey(options.idempotencyKey, 'Crawls.create').pipe(
481
+ Effect.flatMap((idempotencyKey) =>
482
+ crawlRequest<readonly { readonly id: string }[]>('/v1/crawls', 'POST', options, request, idempotencyKey),
483
+ ),
484
+ Effect.map((created) => created.map(({ id }) => crawlById(id))),
485
+ ),
486
+ })
487
+ return { fetch, fetchJson, snapshots: { get: getSnapshot }, batched, getBatched, cancelBatched, crawls } as const
305
488
  }),
306
489
  }) {
307
490
  /**
@@ -0,0 +1,47 @@
1
+ import { BackoffDefaults, type JsonBatch, type StreamResponse, stream } from '@durable-streams/client'
2
+
3
+ /** live:false reads one bounded server response, never the complete retained history. */
4
+ export async function readStreamPage<T>(response: StreamResponse<T>): Promise<JsonBatch<T>> {
5
+ if (response.live !== false) throw new Error('A bounded stream page requires live:false.')
6
+ try {
7
+ const items = await response.json<T>()
8
+ return {
9
+ items,
10
+ offset: response.offset,
11
+ upToDate: response.upToDate,
12
+ streamClosed: response.streamClosed,
13
+ cursor: response.cursor,
14
+ }
15
+ } finally {
16
+ response.cancel()
17
+ }
18
+ }
19
+
20
+ /** Decode bounded responses with the official protocol client while preserving the caller's auth transport. */
21
+ export class DurableStreamReader {
22
+ private readonly cursors = new Map<string, string>()
23
+
24
+ cursor(key: string): string | undefined {
25
+ return this.cursors.get(key)
26
+ }
27
+
28
+ async read<T>(key: string, response: Response, offset: string): Promise<JsonBatch<T>> {
29
+ const page = await readStreamPage(
30
+ await stream<T>({
31
+ url: response.url || 'https://stream.invalid/',
32
+ offset,
33
+ live: false,
34
+ json: true,
35
+ fetch: async () => response,
36
+ backoffOptions: { ...BackoffDefaults, maxRetries: 0 },
37
+ }),
38
+ )
39
+ this.cursors.delete(key)
40
+ if (!page.streamClosed && page.cursor !== undefined) this.cursors.set(key, page.cursor)
41
+ if (this.cursors.size > 128) {
42
+ const oldest = this.cursors.keys().next().value
43
+ if (oldest !== undefined) this.cursors.delete(oldest)
44
+ }
45
+ return page
46
+ }
47
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,13 @@
1
+ export type { AppendixMarker } from './AppendixMarkers.js'
2
+ export {
3
+ APPENDIX_MARKER_PREFIX,
4
+ AppendixMarkerLevel,
5
+ isAppendixMarkerLine,
6
+ parseAppendixMarkerLine,
7
+ parseAppendixMarkers,
8
+ stripAppendixMarkers,
9
+ withoutFencedCode,
10
+ } from './AppendixMarkers.js'
1
11
  export type {
2
12
  BatchedParams,
3
13
  BatchedRequestOptions,
@@ -10,12 +20,16 @@ export type {
10
20
  FetchParams,
11
21
  FetchRequestOptions,
12
22
  FetchResponse,
13
- FetchSearchParams,
14
- FetchSearchResponse,
15
23
  GetBatchedParams,
16
24
  GetBatchedResponse,
25
+ SnapshotCatalog,
26
+ SnapshotCatalogParams,
27
+ SnapshotGetParams,
28
+ SnapshotHighlightsParams,
29
+ SnapshotHighlightsResponse,
17
30
  } from './Client.js'
18
- export { ExpandClient } from './Client.js'
31
+ export { Expand, ExpandClient } from './Client.js'
32
+ export * from './Crawls.js'
19
33
  export type { ExpandError } from './Error.js'
20
34
  export {
21
35
  ExpandApiError,
@@ -27,4 +41,16 @@ export {
27
41
  ExpandSdkError,
28
42
  ExpandTimeoutError,
29
43
  } from './Error.js'
44
+ export type { FetchOriginErrorResponse } from './FetchJson.js'
45
+ export { isFetchOriginError } from './FetchJson.js'
46
+ export type { SnapshotHandle } from './Playground.js'
47
+ export {
48
+ citationUrl,
49
+ DEFAULT_PLAYGROUND_HOST,
50
+ parseSnapshotHandle,
51
+ playgroundAppendixUrl,
52
+ playgroundBase,
53
+ playgroundContentUrl,
54
+ resolvePlaygroundHost,
55
+ } from './Playground.js'
30
56
  export { ExpandService, ExpandServiceOptions, RequestOptions } from './Service.js'