@distilled.cloud/fly-io 1.0.0-rc.16 → 1.0.0-rc.17

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 (47) hide show
  1. package/lib/.lazy.json +1 -0
  2. package/lib/_bundle/lazy.js +4 -0
  3. package/lib/_bundle/sdk.js +2 -0
  4. package/lib/_bundle/sdk.js.map +1 -0
  5. package/lib/credentials.js +1 -70
  6. package/lib/errors.d.ts +15 -0
  7. package/lib/errors.d.ts.map +1 -1
  8. package/lib/errors.js +1 -62
  9. package/lib/index.js +1 -33
  10. package/lib/index.lazy.js +7 -0
  11. package/lib/pagination.js +1 -5
  12. package/lib/protocol.js +1 -564
  13. package/lib/retry.js +1 -34
  14. package/lib/services/addons.js +23 -61
  15. package/lib/services/addons.js.map +1 -0
  16. package/lib/services/builders.d.ts +31 -0
  17. package/lib/services/builders.d.ts.map +1 -0
  18. package/lib/services/builders.js +2 -0
  19. package/lib/services/builders.js.map +1 -0
  20. package/lib/services/index.d.ts +1 -0
  21. package/lib/services/index.d.ts.map +1 -1
  22. package/lib/services/index.js +1 -2
  23. package/lib/services/machines.js +138 -328
  24. package/lib/services/machines.js.map +1 -0
  25. package/lib/services/mpg.js +32 -81
  26. package/lib/services/mpg.js.map +1 -0
  27. package/lib/services/sprites.js +63 -153
  28. package/lib/services/sprites.js.map +1 -0
  29. package/lib/services.lazy/addons.js +3 -0
  30. package/lib/services.lazy/builders.js +1 -0
  31. package/lib/services.lazy/index.js +3 -0
  32. package/lib/services.lazy/machines.js +4 -0
  33. package/lib/services.lazy/mpg.js +3 -0
  34. package/lib/services.lazy/sprites.js +3 -0
  35. package/lib/traits.js +1 -21
  36. package/package.json +18 -9
  37. package/src/errors.ts +14 -0
  38. package/src/services/builders.ts +93 -0
  39. package/src/services/index.ts +1 -0
  40. package/lib/credentials.js.map +0 -1
  41. package/lib/errors.js.map +0 -1
  42. package/lib/pagination.js.map +0 -1
  43. package/lib/protocol.js.map +0 -1
  44. package/lib/retry.js.map +0 -1
  45. package/lib/services/index.js.map +0 -1
  46. package/lib/traits.js.map +0 -1
  47. /package/lib/{index.js.map → index.lazy.js.map} +0 -0
package/lib/protocol.js CHANGED
@@ -1,564 +1 @@
1
- import * as API from "@distilled.cloud/core/api";
2
- import { HTTP_STATUS_MAP, InternalServerError, } from "@distilled.cloud/core/errors";
3
- import { buildRequest, getAnn, mapKeys, matchTypedError, } from "@distilled.cloud/core/protocol-http";
4
- import { hasSensitiveMember, makeRestProtocol, unwrapRedactedDeep, wrapSensitive, } from "@distilled.cloud/core/protocol-rest";
5
- import { failUnlessLenient, validateResponse, withholdActual, } from "@distilled.cloud/core/response-validation";
6
- import { parseRetryAfterForStatus } from "@distilled.cloud/core/retry-after";
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
- import * as Context from "effect/Context";
28
- import * as Deferred from "effect/Deferred";
29
- import * as Effect from "effect/Effect";
30
- import * as Exit from "effect/Exit";
31
- import * as HttpClient from "effect/http/HttpClient";
32
- import * as HttpClientRequest from "effect/http/HttpClientRequest";
33
- import * as Layer from "effect/Layer";
34
- import * as MutableHashMap from "effect/MutableHashMap";
35
- import * as Option from "effect/Option";
36
- import * as Redacted from "effect/Redacted";
37
- import * as Schema from "effect/Schema";
38
- import { Credentials, DEFAULT_FLY_API_BASE_URL, DEFAULT_SPRITES_API_BASE_URL, } from "./credentials.js";
39
- import { ConfigError as FlyConfigError, FlyIoParseError, CreateExtensionTosAgreementNotAuthorized, SpritesNotEnabled, UnknownFlyIoError, } from "./errors.js";
40
- import { graphqlOpSymbol, httpSymbol, responsePathSymbol, } from "./traits.js";
41
- const flyErrorEnvelope = (body) => {
42
- if (body === null || typeof body !== "object")
43
- return undefined;
44
- const b = body;
45
- const errors = b.errors;
46
- const detail = errors && typeof errors.detail === "string" ? errors.detail : undefined;
47
- return {
48
- message: typeof b.error === "string" ? b.error : typeof b.message === "string" ? b.message : detail,
49
- };
50
- };
51
-
52
- const spritesErrorEnvelope = (body) => {
53
- if (body === null || typeof body !== "object")
54
- return undefined;
55
- const b = body;
56
- const message = typeof b.message === "string" ? b.message : typeof b.error === "string" ? b.error : undefined;
57
- const code = typeof b.error === "string" && typeof b.message === "string" ? b.error : undefined;
58
- return { message, code };
59
- };
60
- const resolveFlyCreds = /*@__PURE__*/ Effect.gen(function* () {
61
- const resolve = yield* Credentials;
62
- return yield* resolve;
63
- });
64
-
65
-
66
-
67
-
68
-
69
-
70
- export class MachineIdentity extends /*@__PURE__*/ /*@__PURE__*/ Context.Service()("FlyIoMachineIdentity") {
71
- }
72
- const FlyIoProtocolRest = /*@__PURE__*/ makeRestProtocol({
73
- credentials: resolveFlyCreds,
74
- baseUrl: (creds) => creds.apiBaseUrl,
75
- headers: (creds) => ({
76
- Authorization: `Bearer ${Redacted.value(creds.apiKey)}`,
77
- }),
78
- errorEnvelope: flyErrorEnvelope,
79
- unknownError: ({ message, body }) => new UnknownFlyIoError({ message, body }),
80
- parseError: ({ body, cause }) => new FlyIoParseError({ body, cause }),
81
- });
82
- export const FlyIoProtocol = /*@__PURE__*/ Layer.effect(API.Protocol,
83
- /*@__PURE__*/ Effect.gen(function* () {
84
- const rest = yield* API.Protocol;
85
- return API.Protocol.of({
86
- encode: (args) => Effect.gen(function* () {
87
- const request = yield* rest.encode(args);
88
- const machine = yield* Effect.serviceOption(MachineIdentity);
89
- if (Option.isNone(machine))
90
- return request;
91
- return request.pipe(HttpClientRequest.removeHeader("authorization"), HttpClientRequest.setHeader("connection", "close"));
92
- }),
93
- decode: rest.decode,
94
- });
95
- })).pipe(/*@__PURE__*/ Layer.provide(FlyIoProtocolRest));
96
-
97
- export const FlyApiProtocol = /*@__PURE__*/ makeRestProtocol({
98
- credentials: resolveFlyCreds,
99
- baseUrl: () => DEFAULT_FLY_API_BASE_URL,
100
- headers: (creds) => ({
101
- Authorization: `Bearer ${Redacted.value(creds.apiKey)}`,
102
- }),
103
- errorEnvelope: flyErrorEnvelope,
104
- unknownError: ({ message, body }) => new UnknownFlyIoError({ message, body }),
105
- parseError: ({ body, cause }) => new FlyIoParseError({ body, cause }),
106
- });
107
- const parseMaybeNdjson = (body) => {
108
- if (typeof body !== "string")
109
- return body;
110
- const lines = body
111
- .split(/\r?\n/)
112
- .map((l) => l.trim())
113
- .filter((l) => l.length > 0);
114
- if (lines.length === 0)
115
- return body;
116
- try {
117
- return lines.map((l) => JSON.parse(l));
118
- }
119
- catch {
120
- return body;
121
- }
122
- };
123
-
124
-
125
-
126
-
127
-
128
-
129
- const parseSpritesExecFrames = (bytes) => {
130
- if (bytes.length === 0)
131
- return undefined;
132
- const first = bytes[0];
133
- if (first > 3)
134
- return undefined;
135
- let end = bytes.length;
136
- let exit_code = 0;
137
- if (end >= 2 && bytes[end - 2] === 3) {
138
- exit_code = bytes[end - 1];
139
- end -= 2;
140
- }
141
- let stdout = "";
142
- let stderr = "";
143
- if (end > 0) {
144
- const decoder = new TextDecoder();
145
- const id = bytes[0];
146
- const payload = decoder.decode(bytes.subarray(1, end));
147
- if (id === 2)
148
- stderr = payload;
149
- else
150
- stdout = payload;
151
- }
152
- return { stdout, stderr, exit_code };
153
- };
154
- const fail = (e) => Effect.fail(e);
155
-
156
- const stripFlyAuthScheme = (token) => {
157
- const trimmed = token.trim();
158
- const space = trimmed.indexOf(" ");
159
- if (space === -1)
160
- return trimmed;
161
- const scheme = trimmed.slice(0, space).toLowerCase();
162
- if (scheme === "flyv1" || scheme === "bearer") {
163
- return trimmed.slice(space + 1).trim();
164
- }
165
- return trimmed;
166
- };
167
- const isMintOp = (http) => http !== undefined &&
168
- http.method === "POST" &&
169
- /\/organizations\/\{[^}]+}\/tokens$/.test(http.uri);
170
- const SPRITES_NOT_ENABLED = /sprites not enabled/i;
171
- const readResponseText = (response) => response.text.pipe(Effect.orDie, Effect.map((text) => text ?? ""));
172
- const parseJson = (text) => {
173
- if (text.trim().length === 0)
174
- return undefined;
175
- try {
176
- return JSON.parse(text);
177
- }
178
- catch {
179
- return undefined;
180
- }
181
- };
182
- const haystackOf = (body, text) => {
183
- const parts = [text];
184
- if (body !== null && typeof body === "object") {
185
- const b = body;
186
- if (typeof b.message === "string")
187
- parts.push(b.message);
188
- if (typeof b.error === "string")
189
- parts.push(b.error);
190
- }
191
- return parts.join(" ");
192
- };
193
- const errorEnv = (body) => spritesErrorEnvelope(body) ?? flyErrorEnvelope(body) ?? {};
194
- const failHttp = (status, body, text, headers) => {
195
- const env = errorEnv(body);
196
- const message = env.message ?? (text.trim().length > 0 ? text.trim() : `HTTP ${status}`);
197
- if (status === 401 && SPRITES_NOT_ENABLED.test(haystackOf(body, message))) {
198
- return fail(new SpritesNotEnabled({ message }));
199
- }
200
- const StatusClass = HTTP_STATUS_MAP[status];
201
- if (StatusClass && status >= 400) {
202
- return fail(new StatusClass({
203
- message,
204
- retryAfter: parseRetryAfterForStatus(status, headers),
205
- }));
206
- }
207
- return fail(new UnknownFlyIoError({ message, body: body ?? text }));
208
- };
209
- const orgFromTokensCurrent = (body) => {
210
- if (body === null || typeof body !== "object")
211
- return undefined;
212
- const tokens = body.tokens;
213
- if (!Array.isArray(tokens) || tokens.length === 0)
214
- return undefined;
215
- const first = tokens[0];
216
- if (typeof first.org_slug === "string" && first.org_slug.length > 0) {
217
- return first.org_slug;
218
- }
219
- if (typeof first.organization === "string" && first.organization.length > 0) {
220
- return first.organization;
221
- }
222
- return undefined;
223
- };
224
- const orgFromGraphql = (body) => {
225
- if (body === null || typeof body !== "object")
226
- return undefined;
227
- const data = body.data;
228
- if (data === null || typeof data !== "object")
229
- return undefined;
230
- const viewer = data.viewer;
231
- const root = viewer !== null && typeof viewer === "object" ? viewer : data;
232
- const personal = root.personalOrganization;
233
- if (typeof personal?.slug === "string" && personal.slug.length > 0) {
234
- return personal.slug;
235
- }
236
- const nodes = root.organizations?.nodes;
237
- if (Array.isArray(nodes)) {
238
- for (const node of nodes) {
239
- if (node !== null &&
240
- typeof node === "object" &&
241
- typeof node.slug === "string" &&
242
- node.slug.length > 0) {
243
- return node.slug;
244
- }
245
- }
246
- }
247
- return undefined;
248
- };
249
- const discoverOrgSlug = (fly) => Effect.gen(function* () {
250
- const http = yield* HttpClient.HttpClient;
251
- const machinesRoot = fly.apiBaseUrl.replace(/\/+$/, "");
252
- const currentReq = HttpClientRequest.make("GET")(`${machinesRoot}/v1/tokens/current`).pipe(HttpClientRequest.setHeaders({
253
- Authorization: `Bearer ${Redacted.value(fly.apiKey)}`,
254
- Accept: "application/json",
255
- }));
256
- const currentRes = yield* http.execute(currentReq);
257
- const currentText = yield* readResponseText(currentRes);
258
- const currentJson = parseJson(currentText);
259
- if (currentRes.status < 400) {
260
- const slug = orgFromTokensCurrent(currentJson);
261
- if (slug !== undefined)
262
- return slug;
263
- }
264
- const gqlReq = HttpClientRequest.make("POST")(`${DEFAULT_FLY_API_BASE_URL}/graphql`).pipe(HttpClientRequest.setHeaders({
265
- Authorization: `Bearer ${Redacted.value(fly.apiKey)}`,
266
- Accept: "application/json",
267
- }), HttpClientRequest.bodyJsonUnsafe({
268
- query: "{ viewer { personalOrganization { slug } organizations { nodes { slug } } } }",
269
- }));
270
- const gqlRes = yield* http.execute(gqlReq);
271
- const gqlText = yield* readResponseText(gqlRes);
272
- const gqlJson = parseJson(gqlText);
273
- if (gqlRes.status < 400) {
274
- const slug = orgFromGraphql(gqlJson);
275
- if (slug !== undefined)
276
- return slug;
277
- }
278
- return yield* new FlyConfigError({
279
- message: "Could not discover a Fly organization slug from /tokens/current or GraphQL",
280
- });
281
- });
282
- const mintOnce = (fly) => Effect.gen(function* () {
283
- const http = yield* HttpClient.HttpClient;
284
- const orgSlug = yield* discoverOrgSlug(fly);
285
- const macaroon = stripFlyAuthScheme(Redacted.value(fly.apiKey));
286
- const request = HttpClientRequest.make("POST")(`${DEFAULT_SPRITES_API_BASE_URL}/organizations/${encodeURIComponent(orgSlug)}/tokens`).pipe(HttpClientRequest.setHeaders({
287
- Authorization: `FlyV1 ${macaroon}`,
288
- Accept: "application/json",
289
- "Content-Type": "application/json",
290
- }), HttpClientRequest.bodyJsonUnsafe({
291
- description: "Alchemy Sprites token",
292
- }));
293
- const response = yield* http.execute(request);
294
- const text = yield* readResponseText(response);
295
- const json = parseJson(text);
296
- const headers = response.headers;
297
- if (response.status >= 400) {
298
- return yield* failHttp(response.status, json, text, headers);
299
- }
300
- const token = json !== null && typeof json === "object" ? json.token : undefined;
301
- if (typeof token !== "string" || token.length === 0) {
302
- return yield* fail(new FlyIoParseError({
303
- body: "[REDACTED]",
304
- cause: "mint response missing token",
305
- }));
306
- }
307
- return Redacted.make(token);
308
- });
309
- // Keyed by the Fly token itself: `Redacted` hashes and compares by value.
310
- const mintedTokens = /*@__PURE__*/ MutableHashMap.empty();
311
- const mintInFlight = /*@__PURE__*/ MutableHashMap.empty();
312
-
313
- const mintSpritesToken = (fly) => Effect.uninterruptibleMask((restore) => Effect.suspend(() => {
314
- const key = fly.apiKey;
315
- const hit = MutableHashMap.get(mintedTokens, key);
316
- if (Option.isSome(hit))
317
- return Effect.succeed(hit.value);
318
- const pending = MutableHashMap.get(mintInFlight, key);
319
- if (Option.isSome(pending)) {
320
- return restore(Deferred.await(pending.value));
321
- }
322
- const result = Deferred.makeUnsafe();
323
- MutableHashMap.set(mintInFlight, key, result);
324
- return restore(mintOnce(fly)).pipe(Effect.onExit((exit) => Effect.gen(function* () {
325
- if (Exit.isSuccess(exit)) {
326
- MutableHashMap.set(mintedTokens, key, exit.value);
327
- }
328
- // Release all waiters, including on defects and owner interruption.
329
- MutableHashMap.remove(mintInFlight, key);
330
- yield* Deferred.done(result, exit);
331
- })));
332
- }));
333
- const encodeSpritesRequest = ({ input, inputAst, }) => Effect.gen(function* () {
334
- const fly = yield* resolveFlyCreds;
335
- const http = getAnn(inputAst, httpSymbol);
336
- const mint = isMintOp(http);
337
- let authorization;
338
- if (mint) {
339
- authorization = `FlyV1 ${stripFlyAuthScheme(Redacted.value(fly.apiKey))}`;
340
- }
341
- else {
342
- const token = yield* mintSpritesToken(fly);
343
- authorization = `Bearer ${Redacted.value(token)}`;
344
- }
345
- return buildRequest({
346
- input: unwrapRedactedDeep(input),
347
- inputAst,
348
- baseUrl: DEFAULT_SPRITES_API_BASE_URL,
349
- headers: {
350
- Authorization: authorization,
351
- Accept: "application/json",
352
- },
353
- });
354
- });
355
- const decodeSpritesResponse = ({ response, outputAst, errors: errorClasses, }) => Effect.gen(function* () {
356
- const buffer = yield* response.arrayBuffer.pipe(Effect.map((bytes) => new Uint8Array(bytes)));
357
- const text = new TextDecoder().decode(buffer);
358
- const execFrames = parseSpritesExecFrames(buffer);
359
- let json;
360
- let nonJson = false;
361
- if (execFrames === undefined && text.trim().length > 0) {
362
- try {
363
- json = JSON.parse(text);
364
- }
365
- catch {
366
- nonJson = true;
367
- }
368
- }
369
- const status = response.status;
370
- const headers = response.headers;
371
- if (status >= 400) {
372
- const env = nonJson ? {} : errorEnv(json);
373
- const message = env.message ?? (nonJson && text.trim() ? text.trim() : `HTTP ${status}`);
374
- if (SPRITES_NOT_ENABLED.test(haystackOf(nonJson ? text : json, message))) {
375
- return yield* fail(new SpritesNotEnabled({ message }));
376
- }
377
- const typed = matchTypedError(errorClasses, status, [
378
- {
379
- code: typeof env.code === "number" ? env.code : undefined,
380
- message,
381
- },
382
- ], { body: nonJson ? text : json, headers });
383
- if (typed !== undefined)
384
- return yield* fail(typed);
385
- const StatusErrorClass = HTTP_STATUS_MAP[status];
386
- if (StatusErrorClass) {
387
- return yield* fail(new StatusErrorClass({
388
- message,
389
- retryAfter: parseRetryAfterForStatus(status, headers),
390
- }));
391
- }
392
- if (status >= 500) {
393
- return yield* fail(new InternalServerError({
394
- message,
395
- retryAfter: parseRetryAfterForStatus(status, headers),
396
- }));
397
- }
398
- return yield* fail(new UnknownFlyIoError({
399
- message,
400
- body: nonJson ? text : json,
401
- }));
402
- }
403
- // Strict mode checks the mapped body (or parsed exec frames) against the
404
- // output schema. For an output with sensitive members, a non-JSON body
405
- // fails in every mode and parse errors withhold the body.
406
- const sensitive = hasSensitiveMember(outputAst);
407
- const reported = sensitive ? "[REDACTED]" : (execFrames ?? (nonJson ? text : json));
408
- if (nonJson && sensitive) {
409
- return yield* fail(new FlyIoParseError({
410
- body: "[REDACTED]",
411
- cause: "Invalid JSON response",
412
- }));
413
- }
414
- const body = execFrames ?? parseMaybeNdjson(nonJson ? text : (json ?? {}));
415
- const mapped = yield* validateResponse(outputAst, mapKeys(outputAst, body, "decode"), (cause) => new FlyIoParseError({ body: reported, cause: sensitive ? withholdActual(cause) : cause })).pipe(Effect.catch(fail));
416
- return wrapSensitive(outputAst, mapped);
417
- });
418
-
419
- export const SpritesProtocol = /*@__PURE__*/ Layer.succeed(API.Protocol,
420
- /*@__PURE__*/ API.Protocol.of({
421
- encode: (args) => encodeSpritesRequest(args),
422
- decode: (args) => decodeSpritesResponse(args),
423
- }));
424
- // =============================================================================
425
- // GraphQL add-ons (Tigris, Redis) -- POST https://api.fly.io/graphql
426
- // =============================================================================
427
- const GraphQLError = /*@__PURE__*/ Schema.Struct({
428
- message: Schema.String,
429
- path: /*@__PURE__*/ Schema.optional(/*@__PURE__*/ Schema.Array(Schema.Unknown)),
430
- extensions: /*@__PURE__*/ Schema.optional(
431
- /*@__PURE__*/ Schema.Struct({
432
- code: /*@__PURE__*/ Schema.optional(Schema.String),
433
- })),
434
- });
435
- const GraphQLEnvelope = /*@__PURE__*/ Schema.Struct({
436
- data: /*@__PURE__*/ Schema.optional(/*@__PURE__*/ Schema.NullOr(Schema.Unknown)),
437
- errors: /*@__PURE__*/ Schema.optional(/*@__PURE__*/ Schema.Array(GraphQLError)),
438
- });
439
- const decodeEnvelope = /*@__PURE__*/ Schema.decodeUnknownOption(GraphQLEnvelope);
440
- const matchGraphqlError = (status, errorBody, headers, errorClasses) => {
441
- const envelope = decodeEnvelope(errorBody);
442
- if (envelope._tag === "Some" && envelope.value.errors?.length) {
443
- const first = envelope.value.errors[0];
444
- const message = first.message;
445
- const typed = matchTypedError(errorClasses, status, [{ message }], {
446
- body: errorBody,
447
- headers,
448
- });
449
- if (typed !== undefined)
450
- return fail(typed);
451
- if (/not authorized to access this createextensiontosagreement/i.test(message)) {
452
- return fail(new CreateExtensionTosAgreementNotAuthorized({ message }));
453
- }
454
- const StatusClass = HTTP_STATUS_MAP[status];
455
- if (StatusClass && status >= 400) {
456
- return fail(new StatusClass({
457
- message,
458
- retryAfter: parseRetryAfterForStatus(status, headers),
459
- }));
460
- }
461
- return fail(new UnknownFlyIoError({
462
- code: first.extensions?.code,
463
- message,
464
- body: errorBody,
465
- }));
466
- }
467
- const StatusClass = HTTP_STATUS_MAP[status];
468
- if (StatusClass && status >= 400) {
469
- return fail(new StatusClass({
470
- message: `HTTP ${status}`,
471
- retryAfter: parseRetryAfterForStatus(status, headers),
472
- }));
473
- }
474
- return fail(new UnknownFlyIoError({ body: errorBody }));
475
- };
476
- const graphqlEncode = ({ input, inputAst, }) => Effect.gen(function* () {
477
- const resolveCredentials = yield* Credentials;
478
- const creds = yield* resolveCredentials;
479
- const op = getAnn(inputAst, graphqlOpSymbol);
480
- if (!op) {
481
- throw new Error("operation input is missing the GraphQLOp() trait");
482
- }
483
- const http = getAnn(inputAst, httpSymbol);
484
- const variables = {};
485
- for (const [k, v] of Object.entries((input ?? {}))) {
486
- if (v !== undefined)
487
- variables[k] = v;
488
- }
489
- const url = `${DEFAULT_FLY_API_BASE_URL}${http?.uri ?? "/graphql"}`;
490
- return HttpClientRequest.make("POST")(url).pipe(HttpClientRequest.setHeaders({
491
- Authorization: `Bearer ${Redacted.value(creds.apiKey)}`,
492
- Accept: "application/json",
493
- }), HttpClientRequest.bodyJsonUnsafe({
494
- query: op.query,
495
- operationName: op.operationName,
496
- variables,
497
- }));
498
- });
499
- const graphqlDecode = ({ response, outputAst, errors, }) => Effect.gen(function* () {
500
- const text = (yield* response.text.pipe(Effect.orDie)) ?? "";
501
- const status = response.status;
502
- const headers = response.headers;
503
- let json;
504
- let parsed = false;
505
- if (text.trim().length > 0) {
506
- try {
507
- json = JSON.parse(text);
508
- parsed = true;
509
- }
510
- catch {
511
- parsed = false;
512
- }
513
- }
514
- // For an output with sensitive members, a non-JSON body fails in every
515
- // mode and parse errors withhold the body.
516
- const sensitive = hasSensitiveMember(outputAst);
517
- if (!parsed) {
518
- if (status >= 400) {
519
- return yield* matchGraphqlError(status, text, headers, errors);
520
- }
521
- if (sensitive) {
522
- return yield* fail(new FlyIoParseError({
523
- body: "[REDACTED]",
524
- cause: "response body is not valid JSON",
525
- }));
526
- }
527
- // Lenient mode returns the body as read.
528
- return yield* failUnlessLenient(new FlyIoParseError({
529
- body: text,
530
- cause: "response body is not valid JSON",
531
- }), text).pipe(Effect.catch(fail));
532
- }
533
- const envelope = json;
534
- const hasGraphQLErrors = envelope !== null &&
535
- typeof envelope === "object" &&
536
- Array.isArray(envelope.errors) &&
537
- envelope.errors.length > 0;
538
- if (hasGraphQLErrors || status >= 400) {
539
- return yield* matchGraphqlError(status, json, headers, errors);
540
- }
541
- const path = getAnn(outputAst, responsePathSymbol);
542
- let payload = envelope !== null && typeof envelope === "object" ? envelope.data : undefined;
543
- if (path !== undefined) {
544
- for (const seg of path.split(".")) {
545
- payload =
546
- payload !== null && typeof payload === "object"
547
- ? payload[seg]
548
- : undefined;
549
- }
550
- }
551
- // Strict mode checks the payload at the response path against the
552
- // output schema.
553
- const validated = yield* validateResponse(outputAst, payload === undefined ? null : payload, (cause) => new FlyIoParseError({
554
- body: sensitive ? "[REDACTED]" : json,
555
- cause: sensitive ? withholdActual(cause) : cause,
556
- })).pipe(Effect.catch(fail));
557
- return wrapSensitive(outputAst, validated);
558
- });
559
- export const FlyGraphqlProtocol = /*@__PURE__*/ Layer.succeed(API.Protocol,
560
- /*@__PURE__*/ API.Protocol.of({
561
- encode: (args) => graphqlEncode(args),
562
- decode: graphqlDecode,
563
- }));
564
- //# sourceMappingURL=protocol.js.map
1
+ export { protocol$FlyApiProtocol as FlyApiProtocol, protocol$FlyGraphqlProtocol as FlyGraphqlProtocol, protocol$FlyIoProtocol as FlyIoProtocol, protocol$MachineIdentity as MachineIdentity, protocol$SpritesProtocol as SpritesProtocol } from "./_bundle/sdk.js";
package/lib/retry.js CHANGED
@@ -1,34 +1 @@
1
- import { throttlingFactory, transientFactory } from "@distilled.cloud/core/retry";
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
- import * as Context from "effect/Context";
19
- import * as Effect from "effect/Effect";
20
- import * as Layer from "effect/Layer";
21
- export { makeDefault, jittered, capped, throttlingOptions, transientOptions, throttlingFactory, transientFactory, } from "@distilled.cloud/core/retry";
22
-
23
- export class Retry extends /*@__PURE__*/ /*@__PURE__*/ Context.Service()("FlyIoRetry") {
24
- }
25
-
26
- export const policy = (optionsOrFactory) => Effect.provide(Layer.succeed(Retry, optionsOrFactory));
27
-
28
- export const none = /*@__PURE__*/ Effect.provide(
29
- /*@__PURE__*/ Layer.succeed(Retry, { while: () => false }));
30
-
31
- export const throttling = /*@__PURE__*/ policy(throttlingFactory);
32
-
33
- export const transient = /*@__PURE__*/ policy(transientFactory);
34
- //# sourceMappingURL=retry.js.map
1
+ export { retry$Retry as Retry, retry$capped as capped, retry$jittered as jittered, retry$makeDefault as makeDefault, retry$none as none, retry$policy as policy, retry$throttling as throttling, retry$throttlingFactory as throttlingFactory, retry$throttlingOptions as throttlingOptions, retry$transient as transient, retry$transientFactory as transientFactory, retry$transientOptions as transientOptions } from "./_bundle/sdk.js";