@expandai/sdk 0.20.1 → 0.20.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/README.md +11 -0
- package/build/dts/Error.d.ts +25 -1
- package/build/dts/Error.d.ts.map +1 -1
- package/build/dts/Internal.d.ts.map +1 -1
- package/build/dts/RetryAfter.d.ts +2 -0
- package/build/dts/RetryAfter.d.ts.map +1 -1
- package/build/dts/Service.d.ts +9 -2
- package/build/dts/Service.d.ts.map +1 -1
- package/build/dts/Tunnel/Headers.d.ts +2 -0
- package/build/dts/Tunnel/Headers.d.ts.map +1 -0
- package/build/dts/Tunnel/LocalTarget.d.ts +122 -0
- package/build/dts/Tunnel/LocalTarget.d.ts.map +1 -0
- package/build/dts/Tunnel/Protocol.d.ts +160 -0
- package/build/dts/Tunnel/Protocol.d.ts.map +1 -0
- package/build/dts/Tunnel/StaticServer.d.ts +10 -0
- package/build/dts/Tunnel/StaticServer.d.ts.map +1 -0
- package/build/dts/Tunnel/Tunnel.d.ts +40 -0
- package/build/dts/Tunnel/Tunnel.d.ts.map +1 -0
- package/build/dts/index.d.ts +2 -2
- package/build/dts/index.d.ts.map +1 -1
- package/build/esm/Error.js +27 -0
- package/build/esm/Error.js.map +1 -1
- package/build/esm/Internal.js +9 -1
- package/build/esm/Internal.js.map +1 -1
- package/build/esm/RetryAfter.js +3 -1
- package/build/esm/RetryAfter.js.map +1 -1
- package/build/esm/Service.js +23 -7
- package/build/esm/Service.js.map +1 -1
- package/build/esm/Tunnel/Headers.js +18 -0
- package/build/esm/Tunnel/Headers.js.map +1 -0
- package/build/esm/Tunnel/LocalTarget.js +125 -0
- package/build/esm/Tunnel/LocalTarget.js.map +1 -0
- package/build/esm/Tunnel/Protocol.js +73 -0
- package/build/esm/Tunnel/Protocol.js.map +1 -0
- package/build/esm/Tunnel/StaticServer.js +69 -0
- package/build/esm/Tunnel/StaticServer.js.map +1 -0
- package/build/esm/Tunnel/Tunnel.js +302 -0
- package/build/esm/Tunnel/Tunnel.js.map +1 -0
- package/build/esm/index.js +1 -1
- package/build/esm/index.js.map +1 -1
- package/package.json +6 -3
- package/src/Error.ts +53 -1
- package/src/Internal.ts +10 -0
- package/src/RetryAfter.ts +4 -1
- package/src/Service.ts +48 -8
- package/src/Tunnel/Headers.ts +22 -0
- package/src/Tunnel/LocalTarget.ts +153 -0
- package/src/Tunnel/Protocol.ts +99 -0
- package/src/Tunnel/StaticServer.ts +85 -0
- package/src/Tunnel/Tunnel.ts +420 -0
- package/src/index.ts +3 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { Cookies, FetchHttpClient, HttpBody, HttpClient, HttpClientRequest, Path, Socket, } from '@effect/platform';
|
|
3
|
+
import { NodeFileSystem, NodeHttpServer, NodePath } from '@effect/platform-node';
|
|
4
|
+
import { Config, Deferred, Duration, Effect, FiberMap, Iterable, Layer, Match, Option, Schema, Stream } from 'effect';
|
|
5
|
+
import { WebSocket } from 'ws';
|
|
6
|
+
import { ExpandLocalTargetError } from '../Error.js';
|
|
7
|
+
import { endToEndHeaders } from './Headers.js';
|
|
8
|
+
import { LocalTarget, resolveLocalTarget, resolveSafeDirectory } from './LocalTarget.js';
|
|
9
|
+
import { decodeBodyFrame, encodeBodyFrame, encodeClientToRelay, MAX_BODY_BYTES, MAX_INFLIGHT_REQUESTS, PROTOCOL_VERSION, RelayToClient, splitBodyChunk, } from './Protocol.js';
|
|
10
|
+
import { serveDirectory, serveFile } from './StaticServer.js';
|
|
11
|
+
export class TunnelError extends Schema.TaggedError()('TunnelError', {
|
|
12
|
+
reason: Schema.Literal('Unauthorized', 'Configuration', 'RelayUnreachable'),
|
|
13
|
+
message: Schema.String,
|
|
14
|
+
cause: Schema.optional(Schema.Defect),
|
|
15
|
+
}) {
|
|
16
|
+
}
|
|
17
|
+
const HELLO_TIMEOUT = Duration.seconds(10);
|
|
18
|
+
const MAX_QUEUED_RELAY_BYTES = 16 * 1024 * 1024;
|
|
19
|
+
// A masked WebSocket frame has at most 14 bytes of framing in addition to its payload.
|
|
20
|
+
const MAX_WEBSOCKET_FRAME_OVERHEAD = 14;
|
|
21
|
+
const RELAY_URLS = new Map([
|
|
22
|
+
['https://api.expand.ai', 'https://expandusercontent.com'],
|
|
23
|
+
['https://api.staging.expand.ai', 'https://staging.expandusercontent.com'],
|
|
24
|
+
]);
|
|
25
|
+
const decodeControlFrame = Schema.decodeUnknownOption(Schema.parseJson(RelayToClient));
|
|
26
|
+
const decodeBody = Option.liftThrowable(decodeBodyFrame);
|
|
27
|
+
// Hop-by-hop or recomputed by `fetch`; forwarding them breaks the local request.
|
|
28
|
+
const DROPPED_REQUEST_HEADERS = new Set(['host', 'content-length', 'accept-encoding']);
|
|
29
|
+
// `fetch` decoded the body; cookies are forwarded separately to preserve repeated fields.
|
|
30
|
+
const DROPPED_RESPONSE_HEADERS = new Set(['content-encoding', 'content-length', 'set-cookie']);
|
|
31
|
+
function rewriteLocalLocation(value, localUrl, publicUrl) {
|
|
32
|
+
if (!URL.canParse(value))
|
|
33
|
+
return value;
|
|
34
|
+
const location = new URL(value);
|
|
35
|
+
return location.origin === new URL(localUrl).origin
|
|
36
|
+
? `${publicUrl}${location.pathname}${location.search}${location.hash}`
|
|
37
|
+
: value;
|
|
38
|
+
}
|
|
39
|
+
// `fetch` reports "fetch failed" and hides the reason in `cause`; a refused connection is an
|
|
40
|
+
// `AggregateError` (one attempt per address) with an empty message and the reason in `code`.
|
|
41
|
+
function describeFetchError(error) {
|
|
42
|
+
const cause = error instanceof Error && error.cause instanceof Error ? error.cause : error;
|
|
43
|
+
if (!(cause instanceof Error))
|
|
44
|
+
return String(cause);
|
|
45
|
+
return cause.message || ('code' in cause && typeof cause.code === 'string' ? cause.code : cause.name);
|
|
46
|
+
}
|
|
47
|
+
/** Uses the relay for the selected API environment, unless `EXPAND_TUNNEL_URL` says otherwise. */
|
|
48
|
+
const relayUrlFor = Effect.fn('Tunnel.relayUrlFor')(function* (baseUrl) {
|
|
49
|
+
const override = yield* Config.option(Config.string('EXPAND_TUNNEL_URL')).pipe(Effect.orDie);
|
|
50
|
+
const configured = Option.orElse(override, () => Option.fromNullable(RELAY_URLS.get(baseUrl.replace(/\/+$/u, ''))));
|
|
51
|
+
if (Option.isNone(configured)) {
|
|
52
|
+
return yield* new TunnelError({
|
|
53
|
+
reason: 'Configuration',
|
|
54
|
+
message: `No tunnel relay is known for ${baseUrl}; set EXPAND_TUNNEL_URL.`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const relayUrl = configured.value;
|
|
58
|
+
if (!URL.canParse(relayUrl) || !['http:', 'https:'].includes(new URL(relayUrl).protocol)) {
|
|
59
|
+
return yield* new TunnelError({
|
|
60
|
+
reason: 'Configuration',
|
|
61
|
+
message: 'EXPAND_TUNNEL_URL must be an http:// or https:// relay address.',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return relayUrl.replace(/\/+$/u, '');
|
|
65
|
+
});
|
|
66
|
+
function tunnelOpenError(relayUrl, status) {
|
|
67
|
+
if (status === 401 || status === 403) {
|
|
68
|
+
return new TunnelError({ reason: 'Unauthorized', message: 'The tunnel relay rejected the Expand credentials.' });
|
|
69
|
+
}
|
|
70
|
+
const retryable = status === 0 || status === 408 || status === 429 || status >= 500;
|
|
71
|
+
return new TunnelError({
|
|
72
|
+
reason: retryable ? 'RelayUnreachable' : 'Configuration',
|
|
73
|
+
message: `Could not open a tunnel at ${relayUrl}${status === 0 ? '' : ` (HTTP ${status})`}.`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// Effect owns receiving and socket lifetime; native send callbacks supply the backpressure its writer lacks.
|
|
77
|
+
const makeRelaySocket = Effect.fn('Tunnel.makeRelaySocket')(function* (relayUrl, headers) {
|
|
78
|
+
const opened = yield* Deferred.make();
|
|
79
|
+
let rejectedStatus = 0;
|
|
80
|
+
const acquire = Effect.acquireRelease(Effect.sync(() => {
|
|
81
|
+
// Disable compression so bufferedAmount plus frame overhead bounds the actual native send queue.
|
|
82
|
+
const ws = new WebSocket(`${relayUrl.replace(/^http/u, 'ws')}/v1/connect`, { headers, perMessageDeflate: false });
|
|
83
|
+
ws.once('open', () => Deferred.unsafeDone(opened, Effect.succeed(ws)));
|
|
84
|
+
ws.once('unexpected-response', (_request, response) => {
|
|
85
|
+
rejectedStatus = response.statusCode ?? 0;
|
|
86
|
+
ws.terminate();
|
|
87
|
+
});
|
|
88
|
+
return ws;
|
|
89
|
+
}),
|
|
90
|
+
// Graceful close queues behind body frames and can retain them for ws's 30-second close timeout.
|
|
91
|
+
(ws) => Effect.sync(() => ws.terminate()));
|
|
92
|
+
const socket = yield* Socket.fromWebSocket(
|
|
93
|
+
// `ws` implements the WHATWG event interface `fromWebSocket` uses.
|
|
94
|
+
acquire.pipe(Effect.map((ws) => ws)));
|
|
95
|
+
const writer = Effect.map(socket.writer, (closeWrite) => Effect.fnUntraced(function* (chunk) {
|
|
96
|
+
if (Socket.isCloseEvent(chunk))
|
|
97
|
+
return yield* closeWrite(chunk);
|
|
98
|
+
const ws = yield* Deferred.await(opened);
|
|
99
|
+
return yield* Effect.async((resume) => {
|
|
100
|
+
const fail = (cause) => Effect.fail(new Socket.SocketGenericError({ reason: 'Write', cause }));
|
|
101
|
+
if (ws.readyState !== WebSocket.OPEN) {
|
|
102
|
+
resume(fail(new Error('Tunnel connection closed.')));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const bytes = typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.byteLength;
|
|
106
|
+
if (ws.bufferedAmount + bytes + MAX_WEBSOCKET_FRAME_OVERHEAD > MAX_QUEUED_RELAY_BYTES) {
|
|
107
|
+
// Cancelled requests leave native writes queued, so overflow must end the whole transport.
|
|
108
|
+
ws.terminate();
|
|
109
|
+
resume(fail(new Error('Tunnel relay stopped reading.')));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
let complete = resume;
|
|
113
|
+
try {
|
|
114
|
+
ws.send(chunk, (error) => {
|
|
115
|
+
complete?.(error ? fail(error) : Effect.void);
|
|
116
|
+
complete = undefined;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
complete = undefined;
|
|
121
|
+
resume(fail(error));
|
|
122
|
+
}
|
|
123
|
+
// ws retains the send callback until flushing or close; don't retain an interrupted request fiber.
|
|
124
|
+
return Effect.sync(() => {
|
|
125
|
+
complete = undefined;
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}));
|
|
129
|
+
return { socket: Socket.Socket.of({ ...socket, writer }), rejectedStatus: () => rejectedStatus };
|
|
130
|
+
});
|
|
131
|
+
// Where the relay's requests go, and the path on the public URL that shows the target.
|
|
132
|
+
const localOrigin = Effect.fn('Tunnel.localOrigin')(function* (target) {
|
|
133
|
+
if (LocalTarget.$is('Port')(target))
|
|
134
|
+
return { host: target.host, port: target.port, path: target.path };
|
|
135
|
+
const path = yield* Path.Path;
|
|
136
|
+
const isFile = LocalTarget.$is('File')(target);
|
|
137
|
+
const directory = isFile ? target.path : yield* resolveSafeDirectory(target.path);
|
|
138
|
+
// Build into the tunnel's scope so the listener stays open after this function returns.
|
|
139
|
+
const port = yield* Layer.build(NodeHttpServer.layer(() => createServer(), { host: '127.0.0.1', port: 0 })).pipe(Effect.flatMap((context) => (isFile ? serveFile(target.path) : serveDirectory(directory)).pipe(Effect.provide(context))), Effect.mapError((cause) => new ExpandLocalTargetError({
|
|
140
|
+
reason: 'Unavailable',
|
|
141
|
+
input: target.path,
|
|
142
|
+
cause,
|
|
143
|
+
message: `Could not start a local file server for "${target.path}". ${cause.message}`,
|
|
144
|
+
})));
|
|
145
|
+
return { host: '127.0.0.1', port, path: isFile ? `/${encodeURIComponent(path.basename(target.path))}` : '/' };
|
|
146
|
+
}, Effect.provide(Layer.merge(NodePath.layer, NodeFileSystem.layer)));
|
|
147
|
+
const makeResponder = Effect.fn('Tunnel.makeResponder')(function* (socket, localUrl, hello) {
|
|
148
|
+
// Loopback requests must not inherit API proxies or trace user paths and forwarded credentials.
|
|
149
|
+
const http = HttpClient.withScope(yield* HttpClient.HttpClient).pipe(HttpClient.withTracerDisabledWhen(() => true), HttpClient.withTracerPropagation(false));
|
|
150
|
+
const write = yield* socket.writer;
|
|
151
|
+
const send = (frame) => write(encodeClientToRelay(frame));
|
|
152
|
+
return Effect.fn('Tunnel.respond')(function* (start, body) {
|
|
153
|
+
const { url: publicUrl } = yield* Deferred.await(hello);
|
|
154
|
+
const response = yield* http
|
|
155
|
+
.execute(
|
|
156
|
+
// Effect's method union is narrower than fetch; preserve custom methods from the relay.
|
|
157
|
+
HttpClientRequest.make(start.method)(`${localUrl}${start.path}`, {
|
|
158
|
+
headers: endToEndHeaders(start.headers).filter(([name]) => !DROPPED_REQUEST_HEADERS.has(name.toLowerCase())),
|
|
159
|
+
body: Option.isSome(body) ? HttpBody.raw(body.value) : undefined,
|
|
160
|
+
}))
|
|
161
|
+
.pipe(Effect.mapError((error) => describeFetchError(error.cause ?? error)));
|
|
162
|
+
yield* send({
|
|
163
|
+
_tag: 'ResponseStart',
|
|
164
|
+
v: PROTOCOL_VERSION,
|
|
165
|
+
id: start.id,
|
|
166
|
+
status: response.status,
|
|
167
|
+
headers: endToEndHeaders(Object.entries(response.headers))
|
|
168
|
+
.filter(([name]) => !DROPPED_RESPONSE_HEADERS.has(name))
|
|
169
|
+
// A dev server redirecting to its own absolute URL must send the fetcher back through the tunnel.
|
|
170
|
+
.map(([name, value]) => [
|
|
171
|
+
name,
|
|
172
|
+
name === 'location' ? rewriteLocalLocation(value, localUrl, publicUrl) : value,
|
|
173
|
+
])
|
|
174
|
+
// Effect's flat Headers record loses repeated Set-Cookie fields.
|
|
175
|
+
.concat(Cookies.toSetCookieHeaders(response.cookies).map((cookie) => ['set-cookie', cookie])),
|
|
176
|
+
});
|
|
177
|
+
let responseBytes = 0;
|
|
178
|
+
yield* response.stream.pipe(Stream.catchTag('ResponseError', (error) => error.reason === 'EmptyBody' ? Stream.empty : Stream.fail(describeFetchError(error.cause ?? error))), Stream.runForEach(Effect.fnUntraced(function* (chunk) {
|
|
179
|
+
responseBytes += chunk.byteLength;
|
|
180
|
+
// Enforce the response bound independently of transport backpressure and relay cancellation.
|
|
181
|
+
if (responseBytes > MAX_BODY_BYTES)
|
|
182
|
+
return yield* Effect.fail('response body too large');
|
|
183
|
+
yield* Effect.forEach(splitBodyChunk(chunk), (slice) => write(encodeBodyFrame(start.id, slice)));
|
|
184
|
+
})));
|
|
185
|
+
yield* send({ _tag: 'ResponseEnd', v: PROTOCOL_VERSION, id: start.id });
|
|
186
|
+
}, (effect, start) => effect.pipe(Effect.scoped, Effect.catchAll((error) => send({
|
|
187
|
+
_tag: 'ResponseError',
|
|
188
|
+
v: PROTOCOL_VERSION,
|
|
189
|
+
id: start.id,
|
|
190
|
+
message: `${localUrl} did not answer: ${String(error)}`,
|
|
191
|
+
})), Effect.ignore));
|
|
192
|
+
});
|
|
193
|
+
const runRequests = Effect.fn('Tunnel.runRequests')(function* (socket, localUrl, hello) {
|
|
194
|
+
const write = yield* socket.writer;
|
|
195
|
+
const send = (frame) => write(encodeClientToRelay(frame));
|
|
196
|
+
const requestFibers = yield* FiberMap.make();
|
|
197
|
+
const run = yield* FiberMap.runtime(requestFibers)();
|
|
198
|
+
const respond = yield* makeResponder(socket, localUrl, hello);
|
|
199
|
+
function runRequest(start, body) {
|
|
200
|
+
run(start.id, respond(start, body));
|
|
201
|
+
}
|
|
202
|
+
// Bookkeeping runs synchronously in frame order so body chunks always land after their
|
|
203
|
+
// `RequestStart` and before their `RequestEnd`; only the returned effects run concurrently.
|
|
204
|
+
const bodies = new Map();
|
|
205
|
+
function handleBodyFrame(data) {
|
|
206
|
+
const body = decodeBody(data);
|
|
207
|
+
if (Option.isNone(body))
|
|
208
|
+
return;
|
|
209
|
+
const pending = bodies.get(body.value.id);
|
|
210
|
+
if (pending === undefined)
|
|
211
|
+
return;
|
|
212
|
+
pending.chunks.push(body.value.chunk);
|
|
213
|
+
pending.bytes += body.value.chunk.byteLength;
|
|
214
|
+
// The public URL is open to anyone who learns it; do not let a request body exhaust memory.
|
|
215
|
+
if (pending.bytes <= MAX_BODY_BYTES)
|
|
216
|
+
return;
|
|
217
|
+
bodies.delete(body.value.id);
|
|
218
|
+
return send({
|
|
219
|
+
_tag: 'ResponseError',
|
|
220
|
+
v: PROTOCOL_VERSION,
|
|
221
|
+
id: body.value.id,
|
|
222
|
+
message: 'request body too large',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
function handleControlFrame(data) {
|
|
226
|
+
return Option.match(decodeControlFrame(data), {
|
|
227
|
+
onNone: () => Effect.logDebug('Ignoring unrecognised tunnel frame'),
|
|
228
|
+
onSome: Match.valueTags({
|
|
229
|
+
Hello: (frame) => Deferred.succeed(hello, frame),
|
|
230
|
+
RequestStart: (start) => {
|
|
231
|
+
if (bodies.has(start.id) || FiberMap.unsafeHas(requestFibers, start.id))
|
|
232
|
+
return;
|
|
233
|
+
if (bodies.size + Iterable.size(requestFibers) >= MAX_INFLIGHT_REQUESTS) {
|
|
234
|
+
return send({
|
|
235
|
+
_tag: 'ResponseError',
|
|
236
|
+
v: PROTOCOL_VERSION,
|
|
237
|
+
id: start.id,
|
|
238
|
+
message: 'too many local requests',
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
if (!start.hasBody) {
|
|
242
|
+
runRequest(start, Option.none());
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
bodies.set(start.id, { start, chunks: [], bytes: 0 });
|
|
246
|
+
return Effect.void;
|
|
247
|
+
},
|
|
248
|
+
RequestCancel: ({ id }) => {
|
|
249
|
+
bodies.delete(id);
|
|
250
|
+
return FiberMap.remove(requestFibers, id);
|
|
251
|
+
},
|
|
252
|
+
RequestEnd: ({ id }) => {
|
|
253
|
+
const pending = Option.fromNullable(bodies.get(id));
|
|
254
|
+
bodies.delete(id);
|
|
255
|
+
return Option.match(pending, {
|
|
256
|
+
onNone: () => Effect.void,
|
|
257
|
+
onSome: ({ start, chunks }) => {
|
|
258
|
+
runRequest(start, Option.some(Buffer.concat(chunks)));
|
|
259
|
+
return Effect.void;
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
},
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return yield* socket
|
|
267
|
+
.runRaw((data) => (typeof data === 'string' ? handleControlFrame(data) : handleBodyFrame(data)))
|
|
268
|
+
.pipe(Effect.ensuring(Effect.sync(() => bodies.clear())));
|
|
269
|
+
});
|
|
270
|
+
/**
|
|
271
|
+
* Opens a tunnel from the relay to `target` for the lifetime of the scope and returns its public URL.
|
|
272
|
+
* Closing the scope closes the socket and, for file targets, the static server.
|
|
273
|
+
*/
|
|
274
|
+
export const openTunnel = Effect.fn('Tunnel.openTunnel')(function* (target, relayUrl, headers) {
|
|
275
|
+
const origin = yield* localOrigin(target);
|
|
276
|
+
const { socket, rejectedStatus } = yield* makeRelaySocket(relayUrl, headers);
|
|
277
|
+
const hello = yield* Deferred.make();
|
|
278
|
+
// If the socket ends before Hello the open fails; after Hello, the API simply sees the origin go away.
|
|
279
|
+
yield* runRequests(socket, `http://${origin.host}:${origin.port}`, hello).pipe(Effect.scoped, Effect.exit, Effect.flatMap(() => Deferred.fail(hello, tunnelOpenError(relayUrl, rejectedStatus()))), Effect.forkScoped);
|
|
280
|
+
const { url } = yield* Deferred.await(hello).pipe(Effect.timeoutFail({
|
|
281
|
+
duration: HELLO_TIMEOUT,
|
|
282
|
+
onTimeout: () => new TunnelError({
|
|
283
|
+
reason: 'RelayUnreachable',
|
|
284
|
+
message: `No answer from the tunnel relay at ${relayUrl}.`,
|
|
285
|
+
}),
|
|
286
|
+
}));
|
|
287
|
+
return `${url}${origin.path}`;
|
|
288
|
+
}, Effect.provide(FetchHttpClient.layer), Effect.provideService(FetchHttpClient.RequestInit, { redirect: 'manual' }));
|
|
289
|
+
/**
|
|
290
|
+
* Runs `use` with a public URL for `input`, which must be a local target (see `resolveLocalTarget`),
|
|
291
|
+
* and closes the tunnel afterwards.
|
|
292
|
+
*/
|
|
293
|
+
export const withLocalTunnel = Effect.fn('Tunnel.withLocalTunnel')(function* (input, credentials, use) {
|
|
294
|
+
const target = yield* resolveLocalTarget(input).pipe(Effect.provide(Layer.merge(NodeFileSystem.layer, NodePath.layer)));
|
|
295
|
+
const { baseUrl, headers } = yield* credentials;
|
|
296
|
+
const relayUrl = yield* relayUrlFor(baseUrl);
|
|
297
|
+
const url = yield* openTunnel(target, relayUrl, headers);
|
|
298
|
+
return yield* use(url);
|
|
299
|
+
}, (effect, input) => effect.pipe(Effect.mapError((error) => error instanceof ExpandLocalTargetError && error.input !== input
|
|
300
|
+
? new ExpandLocalTargetError({ reason: error.reason, input, message: error.message, cause: error.cause })
|
|
301
|
+
: error), Effect.scoped));
|
|
302
|
+
//# sourceMappingURL=Tunnel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tunnel.js","sourceRoot":"","sources":["../../../src/Tunnel/Tunnel.ts"],"names":[],"mappings":"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"}
|
package/build/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { APPENDIX_MARKER_PREFIX, AppendixMarkerLevel, isAppendixMarkerLine, parseAppendixMarkerLine, parseAppendixMarkers, stripAppendixMarkers, withoutFencedCode, } from './AppendixMarkers.js';
|
|
2
2
|
export { Expand, ExpandClient } from './Client.js';
|
|
3
3
|
export * from './Crawls.js';
|
|
4
|
-
export { ExpandApiError, ExpandClientApiError, ExpandClientConnectionError, ExpandClientError, ExpandClientTimeoutError, ExpandConnectionError, ExpandSdkError, ExpandTimeoutError, } from './Error.js';
|
|
4
|
+
export { ExpandApiError, ExpandClientApiError, ExpandClientConnectionError, ExpandClientError, ExpandClientLocalTargetError, ExpandClientTimeoutError, ExpandConnectionError, ExpandLocalTargetError, ExpandSdkError, ExpandTimeoutError, } from './Error.js';
|
|
5
5
|
export { isFetchOriginError } from './FetchJson.js';
|
|
6
6
|
export { citationUrl, DEFAULT_PLAYGROUND_HOST, parseSnapshotHandle, playgroundAppendixUrl, playgroundBase, playgroundContentUrl, resolvePlaygroundHost, } from './Playground.js';
|
|
7
7
|
export { ExpandService, ExpandServiceOptions, RequestOptions } from './Service.js';
|
package/build/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,GACjB,MAAM,sBAAsB,CAAA;AAqB7B,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAClD,cAAc,aAAa,CAAA;AAE3B,OAAO,EACN,cAAc,EACd,oBAAoB,EACpB,2BAA2B,EAC3B,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,EACd,kBAAkB,GAClB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,EACN,WAAW,EACX,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,qBAAqB,GACrB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,GACjB,MAAM,sBAAsB,CAAA;AAqB7B,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAClD,cAAc,aAAa,CAAA;AAE3B,OAAO,EACN,cAAc,EACd,oBAAoB,EACpB,2BAA2B,EAC3B,iBAAiB,EACjB,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,kBAAkB,GAClB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,EACN,WAAW,EACX,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,qBAAqB,GACrB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20.3.0"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.20.
|
|
7
|
+
"version": "0.20.2",
|
|
8
8
|
"private": false,
|
|
9
9
|
"description": "TypeScript SDK for ExpandAI",
|
|
10
10
|
"author": "ExpandAI",
|
|
@@ -67,14 +67,17 @@
|
|
|
67
67
|
"@durable-streams/client": "^0.2.6",
|
|
68
68
|
"@effect/platform": "0.96.1",
|
|
69
69
|
"@effect/platform-node": "0.106.0",
|
|
70
|
-
"effect": "3.21.2"
|
|
70
|
+
"effect": "3.21.2",
|
|
71
|
+
"mime": "^4.1.0",
|
|
72
|
+
"ws": "^8.21.0"
|
|
71
73
|
},
|
|
72
74
|
"devDependencies": {
|
|
73
75
|
"@biomejs/biome": "^2.4.14",
|
|
74
76
|
"@effect/vitest": "0.29.0",
|
|
75
77
|
"@tim-smart/openapi-gen": "0.3.12",
|
|
76
78
|
"@types/node": "^25.5.2",
|
|
79
|
+
"@types/ws": "^8.18.1",
|
|
77
80
|
"vitest": "4.1.4"
|
|
78
81
|
},
|
|
79
|
-
"expandSourceSha": "
|
|
82
|
+
"expandSourceSha": "71e04d6b697d70cdca5566e162dc4b9fdc98dba3"
|
|
80
83
|
}
|
package/src/Error.ts
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import { Schema } from 'effect'
|
|
2
2
|
|
|
3
|
+
const LOCAL_TARGET_ERROR_REASON = Schema.Literal(
|
|
4
|
+
'InvalidInput',
|
|
5
|
+
'NotLocal',
|
|
6
|
+
'UnsupportedProtocol',
|
|
7
|
+
'NotFound',
|
|
8
|
+
'PermissionDenied',
|
|
9
|
+
'UnsupportedFileType',
|
|
10
|
+
'Unavailable',
|
|
11
|
+
'UnsafeDirectory',
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
export type LocalTargetErrorReason = typeof LOCAL_TARGET_ERROR_REASON.Type
|
|
15
|
+
|
|
16
|
+
/** A local input cannot be fetched. Inspect `reason` rather than parsing the message. */
|
|
17
|
+
export class ExpandLocalTargetError extends Schema.TaggedError<ExpandLocalTargetError>()('ExpandLocalTargetError', {
|
|
18
|
+
reason: LOCAL_TARGET_ERROR_REASON,
|
|
19
|
+
input: Schema.String,
|
|
20
|
+
message: Schema.String,
|
|
21
|
+
cause: Schema.optional(Schema.Defect),
|
|
22
|
+
}) {
|
|
23
|
+
toPublicError() {
|
|
24
|
+
return new ExpandClientLocalTargetError(this.message, {
|
|
25
|
+
reason: this.reason,
|
|
26
|
+
input: this.input,
|
|
27
|
+
cause: this.cause,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
3
32
|
/**
|
|
4
33
|
* Effect-side base error. Schema.TaggedError; Effect consumers receive these in the error channel.
|
|
5
34
|
*/
|
|
@@ -54,7 +83,12 @@ export class ExpandTimeoutError extends Schema.TaggedError<ExpandTimeoutError>()
|
|
|
54
83
|
/**
|
|
55
84
|
* Union of every error the Effect-side `ExpandService` can fail with.
|
|
56
85
|
*/
|
|
57
|
-
export type ExpandError =
|
|
86
|
+
export type ExpandError =
|
|
87
|
+
| ExpandSdkError
|
|
88
|
+
| ExpandApiError
|
|
89
|
+
| ExpandConnectionError
|
|
90
|
+
| ExpandTimeoutError
|
|
91
|
+
| ExpandLocalTargetError
|
|
58
92
|
|
|
59
93
|
/**
|
|
60
94
|
* Promise-side base class. Catch this to handle any failure thrown by `ExpandClient`.
|
|
@@ -66,6 +100,24 @@ export class ExpandClientError extends Error {
|
|
|
66
100
|
}
|
|
67
101
|
}
|
|
68
102
|
|
|
103
|
+
interface LocalTargetErrorOptions extends ErrorOptions {
|
|
104
|
+
readonly reason: LocalTargetErrorReason
|
|
105
|
+
readonly input: string
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** A local input cannot be fetched. Includes the input, stable reason, and original filesystem cause. */
|
|
109
|
+
export class ExpandClientLocalTargetError extends ExpandClientError {
|
|
110
|
+
readonly reason: LocalTargetErrorReason
|
|
111
|
+
readonly input: string
|
|
112
|
+
|
|
113
|
+
constructor(message: string, options: LocalTargetErrorOptions) {
|
|
114
|
+
super(message, options)
|
|
115
|
+
this.name = 'ExpandClientLocalTargetError'
|
|
116
|
+
this.reason = options.reason
|
|
117
|
+
this.input = options.input
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
69
121
|
/**
|
|
70
122
|
* Promise-side error for non-2xx API responses. Inspect `status` and `body` for details.
|
|
71
123
|
*/
|
package/src/Internal.ts
CHANGED
|
@@ -5,9 +5,11 @@ import {
|
|
|
5
5
|
ExpandClientApiError,
|
|
6
6
|
ExpandClientConnectionError,
|
|
7
7
|
ExpandClientError,
|
|
8
|
+
ExpandClientLocalTargetError,
|
|
8
9
|
ExpandClientTimeoutError,
|
|
9
10
|
ExpandConnectionError,
|
|
10
11
|
type ExpandError,
|
|
12
|
+
ExpandLocalTargetError,
|
|
11
13
|
ExpandSdkError,
|
|
12
14
|
ExpandTimeoutError,
|
|
13
15
|
} from './Error.js'
|
|
@@ -115,9 +117,17 @@ export function toExpandError(error: unknown, timeoutMs: number): ExpandError {
|
|
|
115
117
|
error instanceof ExpandApiError ||
|
|
116
118
|
error instanceof ExpandConnectionError ||
|
|
117
119
|
error instanceof ExpandTimeoutError ||
|
|
120
|
+
error instanceof ExpandLocalTargetError ||
|
|
118
121
|
error instanceof ExpandSdkError
|
|
119
122
|
)
|
|
120
123
|
return error
|
|
124
|
+
if (error instanceof ExpandClientLocalTargetError)
|
|
125
|
+
return new ExpandLocalTargetError({
|
|
126
|
+
reason: error.reason,
|
|
127
|
+
input: error.input,
|
|
128
|
+
message: error.message,
|
|
129
|
+
cause: error.cause,
|
|
130
|
+
})
|
|
121
131
|
if (error instanceof ExpandClientApiError)
|
|
122
132
|
return new ExpandApiError({ message: error.message, status: error.status, body: error.body, cause: error })
|
|
123
133
|
if (error instanceof ExpandClientTimeoutError)
|
package/src/RetryAfter.ts
CHANGED
|
@@ -25,6 +25,9 @@ export const MAX_RETRY_AFTER = Duration.minutes(2)
|
|
|
25
25
|
*/
|
|
26
26
|
const currentRetryAfter = FiberRef.unsafeMake(Option.none<Duration.Duration>())
|
|
27
27
|
|
|
28
|
+
/** Start an attempt with no previous response's delay, including when setup fails before HTTP. */
|
|
29
|
+
export const clearRetryAfter = FiberRef.set(currentRetryAfter, Option.none())
|
|
30
|
+
|
|
28
31
|
/**
|
|
29
32
|
* Rounded to whole milliseconds rather than kept as fractional seconds.
|
|
30
33
|
*
|
|
@@ -59,7 +62,7 @@ export const recordRetryAfter = <E, R>(client: HttpClient.HttpClient.With<E, R>)
|
|
|
59
62
|
// transport level never produces a response to overwrite the previous one, so without this the
|
|
60
63
|
// next attempt would sleep on a wait some earlier response asked for — a connection reset would
|
|
61
64
|
// inherit a saturated gate's 20s.
|
|
62
|
-
HttpClient.tapRequest(client, () =>
|
|
65
|
+
HttpClient.tapRequest(client, () => clearRetryAfter),
|
|
63
66
|
(response) =>
|
|
64
67
|
FiberRef.set(
|
|
65
68
|
currentRetryAfter,
|
package/src/Service.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
CrawlSyncPolicy,
|
|
11
11
|
} from './Crawls.js'
|
|
12
12
|
import * as EnvProxyHttpClient from './EnvProxyHttpClient.js'
|
|
13
|
-
import { ExpandApiError, type ExpandError, ExpandSdkError } from './Error.js'
|
|
13
|
+
import { ExpandApiError, ExpandConnectionError, type ExpandError, ExpandSdkError } from './Error.js'
|
|
14
14
|
import {
|
|
15
15
|
type FetchObjectModeJsonResponse,
|
|
16
16
|
type FetchSnapshotHighlightsResultWithJson,
|
|
@@ -20,7 +20,8 @@ import {
|
|
|
20
20
|
import * as Generated from './Generated.js'
|
|
21
21
|
import { decodeError, unexpectedStatus } from './HttpClientHelpers.js'
|
|
22
22
|
import { isRetryable, toExpandError } from './Internal.js'
|
|
23
|
-
import { nextRetryDelay, recordRetryAfter } from './RetryAfter.js'
|
|
23
|
+
import { clearRetryAfter, nextRetryDelay, recordRetryAfter } from './RetryAfter.js'
|
|
24
|
+
import { TunnelError, withLocalTunnel } from './Tunnel/Tunnel.js'
|
|
24
25
|
|
|
25
26
|
const DEFAULT_BASE_URL = 'https://api.expand.ai'
|
|
26
27
|
const DEFAULT_TIMEOUT_MS = 60_000
|
|
@@ -79,15 +80,23 @@ export class RequestOptions extends Schema.Class<RequestOptions>('RequestOptions
|
|
|
79
80
|
{ description: 'Per-request overrides for an `ExpandService` operation.' },
|
|
80
81
|
) {}
|
|
81
82
|
|
|
83
|
+
interface LocalOptions {
|
|
84
|
+
/**
|
|
85
|
+
* The URL is a localhost server or a file path on this machine: fetch it through a short-lived tunnel
|
|
86
|
+
* (Node.js only). Never set this for user-supplied URLs; it exposes this machine's loopback and files.
|
|
87
|
+
*/
|
|
88
|
+
readonly local?: boolean
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
export type FetchParams = typeof Generated.FetchFetchRequest.Encoded
|
|
83
92
|
export type FetchResponse = string
|
|
84
|
-
export type FetchRequestOptions = RequestOptions & typeof Generated.FetchFetchParams.Encoded
|
|
93
|
+
export type FetchRequestOptions = RequestOptions & typeof Generated.FetchFetchParams.Encoded & LocalOptions
|
|
85
94
|
export type FetchJsonParams = typeof Generated.FetchFetchJsonRequest.Encoded
|
|
86
95
|
// `*WithJson` rather than the generated result: the codegen drops the statejson snippet `json`, so
|
|
87
96
|
// the service decodes the body with the recovering schemas (see `FetchJson.ts`). A union because an
|
|
88
97
|
// origin that never answered has no content to describe — narrow on `_tag`.
|
|
89
98
|
export type FetchJsonResponse = FetchObjectModeJsonResponse
|
|
90
|
-
export type FetchJsonRequestOptions = RequestOptions & typeof Generated.FetchFetchJsonParams.Encoded
|
|
99
|
+
export type FetchJsonRequestOptions = RequestOptions & typeof Generated.FetchFetchJsonParams.Encoded & LocalOptions
|
|
91
100
|
export interface SnapshotHighlightsParams extends Schema.Schema.Encoded<typeof Generated.SnapshotHighlightsInput> {
|
|
92
101
|
readonly snapshotId: string
|
|
93
102
|
}
|
|
@@ -257,6 +266,7 @@ export class ExpandService extends Effect.Service<ExpandService>()('@expandai/sd
|
|
|
257
266
|
),
|
|
258
267
|
)
|
|
259
268
|
return yield* Effect.sync(() => MutableRef.set(pendingRefusal, Option.none())).pipe(
|
|
269
|
+
Effect.zipRight(clearRetryAfter),
|
|
260
270
|
Effect.zipRight(operation),
|
|
261
271
|
Effect.retry(retrySchedule),
|
|
262
272
|
Effect.timeoutFail({
|
|
@@ -268,23 +278,53 @@ export class ExpandService extends Effect.Service<ExpandService>()('@expandai/sd
|
|
|
268
278
|
)
|
|
269
279
|
})
|
|
270
280
|
|
|
281
|
+
// Tunnel setup and local access belong to the attempt covered by the existing retry/timeout policy.
|
|
282
|
+
function viaLocalTunnel<A, E, R>(
|
|
283
|
+
url: string,
|
|
284
|
+
local: boolean | undefined,
|
|
285
|
+
run: (url: string) => Effect.Effect<A, E, R>,
|
|
286
|
+
) {
|
|
287
|
+
return local === true
|
|
288
|
+
? withLocalTunnel(
|
|
289
|
+
url,
|
|
290
|
+
Effect.succeed({ baseUrl: config.baseUrl, headers: { 'x-expand-api-key': config.apiKey } }),
|
|
291
|
+
run,
|
|
292
|
+
).pipe(
|
|
293
|
+
Effect.mapError((error) => {
|
|
294
|
+
if (!(error instanceof TunnelError)) return error
|
|
295
|
+
return error.reason === 'RelayUnreachable'
|
|
296
|
+
? new ExpandConnectionError({ message: error.message, cause: error })
|
|
297
|
+
: new ExpandSdkError({ message: error.message, cause: error })
|
|
298
|
+
}),
|
|
299
|
+
)
|
|
300
|
+
: run(url)
|
|
301
|
+
}
|
|
302
|
+
|
|
271
303
|
const fetch = Effect.fn('ExpandService.fetch')(function* (
|
|
272
304
|
params: FetchParams,
|
|
273
305
|
requestOptions: FetchRequestOptions = {},
|
|
274
306
|
) {
|
|
275
|
-
const { include, ...retryOptions } = requestOptions
|
|
276
|
-
return yield* withRetries(
|
|
307
|
+
const { include, local, ...retryOptions } = requestOptions
|
|
308
|
+
return yield* withRetries(
|
|
309
|
+
viaLocalTunnel(params.url, local, (url) => fetchMarkdown({ ...params, url }, { include })),
|
|
310
|
+
retryOptions,
|
|
311
|
+
)
|
|
277
312
|
})
|
|
278
313
|
|
|
279
314
|
const fetchJson = Effect.fn('ExpandService.fetchJson')(function* (
|
|
280
315
|
params: FetchJsonParams,
|
|
281
316
|
requestOptions: FetchJsonRequestOptions = {},
|
|
282
317
|
) {
|
|
283
|
-
const { include, ...retryOptions } = requestOptions
|
|
318
|
+
const { include, local, ...retryOptions } = requestOptions
|
|
284
319
|
// `fetchObjectModeJson` mirrors the generated call but preserves the statejson snippet `json`.
|
|
285
320
|
// Coalesce null -> undefined before `setUrlParams`, which throws on null.
|
|
286
321
|
return yield* withRetries(
|
|
287
|
-
|
|
322
|
+
viaLocalTunnel(params.url, local, (url) =>
|
|
323
|
+
fetchObjectModeJson(authedClient, {
|
|
324
|
+
params: { include: include ?? undefined },
|
|
325
|
+
payload: { ...params, url },
|
|
326
|
+
}),
|
|
327
|
+
),
|
|
288
328
|
retryOptions,
|
|
289
329
|
)
|
|
290
330
|
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const HOP_BY_HOP_HEADERS = new Set([
|
|
2
|
+
'connection',
|
|
3
|
+
'keep-alive',
|
|
4
|
+
'proxy-authenticate',
|
|
5
|
+
'proxy-authorization',
|
|
6
|
+
'te',
|
|
7
|
+
'trailer',
|
|
8
|
+
'transfer-encoding',
|
|
9
|
+
'upgrade',
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
export function endToEndHeaders(headers: Iterable<readonly [string, string]>): Array<readonly [string, string]> {
|
|
13
|
+
const entries = [...headers]
|
|
14
|
+
const connectionTokens = new Set(
|
|
15
|
+
entries
|
|
16
|
+
.filter(([name]) => name.toLowerCase() === 'connection')
|
|
17
|
+
.flatMap(([, value]) => value.split(',').map((token) => token.trim().toLowerCase())),
|
|
18
|
+
)
|
|
19
|
+
return entries.filter(
|
|
20
|
+
([name]) => !HOP_BY_HOP_HEADERS.has(name.toLowerCase()) && !connectionTokens.has(name.toLowerCase()),
|
|
21
|
+
)
|
|
22
|
+
}
|