UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

194 lines (192 loc) • 8.16 kB
let effect = require("effect"); let zod = require("zod"); let every_plugin_version_manifest = require("every-plugin/version-manifest"); //#region ../../apps/upload/src/client.ts const BundleFileSchema = zod.z.object({ path: zod.z.string().min(1).max(512), contentBase64: zod.z.string().min(1) }); const UploadRequestSchema = zod.z.object({ account: zod.z.string().min(1), gateway: zod.z.string().min(1), workspace: zod.z.string().min(1), files: zod.z.array(BundleFileSchema).min(1).max(1e4) }); const UploadResponseSchema = zod.z.object({ stored: zod.z.number().int(), /** files recognized as byte-identical to what the store already holds (SRI metadata match) and skipped */ unchanged: zod.z.number().int().optional(), totalBytes: zod.z.number().int(), integrity: zod.z.record(zod.z.string(), zod.z.string()), storage: zod.z.enum(["s3", "memory"]).optional().describe("Resolved bundle-storage backend: `s3` (R2/MinIO, persistent) or `memory` (ephemeral — bytes are lost on restart)") }); const NamespaceQuerySchema = zod.z.object({ account: zod.z.string().min(1), gateway: zod.z.string().min(1) }); const ListBundlesQuerySchema = NamespaceQuerySchema.extend({ workspace: zod.z.string().min(1).optional() }); const BundleObjectSummarySchema = zod.z.object({ key: zod.z.string(), size: zod.z.number().int(), lastModified: zod.z.string().nullable().optional() }); const ListBundlesResultSchema = zod.z.object({ storage: zod.z.enum(["s3", "memory"]), prefix: zod.z.string(), objects: zod.z.array(BundleObjectSummarySchema), truncated: zod.z.boolean() }); const ListVersionsQuerySchema = NamespaceQuerySchema.extend({ workspace: zod.z.string().min(1) }); const VersionSummarySchema = zod.z.object({ version: zod.z.string().regex(/^[0-9a-f]{16}$/), entry: zod.z.string(), entryIntegrity: zod.z.string(), gitSha: zod.z.string().optional(), builtAt: zod.z.string().optional(), fileCount: zod.z.number().int().optional(), lastModified: zod.z.string().nullable().optional() }); const ListVersionsResultSchema = zod.z.object({ storage: zod.z.enum(["s3", "memory"]), versions: zod.z.array(VersionSummarySchema), truncated: zod.z.boolean() }); const IntegrityReportQuerySchema = NamespaceQuerySchema.extend({ workspace: zod.z.string().min(1), version: zod.z.string().regex(/^[0-9a-f]{16}$/) }); const IntegrityReportResultSchema = zod.z.object({ storage: zod.z.enum(["s3", "memory"]), manifest: every_plugin_version_manifest.WorkspaceVersionManifestSchema, /** SRI recompute of the manifest's entry object against `entryIntegrity`; * null when the entry object is missing from the store. */ entryVerified: zod.z.boolean().nullable() }); const STATUS_ATTEMPTS = 3; const TRANSPORT_ATTEMPTS = 5; /** 522/524 are Cloudflare's "connection timed out"/"origin exceeded the proxy * timeout" codes — the origin may still have applied the upload, and the * server-side SRI dedupe makes a retry idempotent. */ const RETRYABLE_STATUSES = /* @__PURE__ */ new Set([ 408, 429, 500, 502, 503, 504, 522, 524 ]); const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const backoffMs = (attempt) => 1e3 * attempt + Math.floor(Math.random() * 500); var UploadTransferError = class UploadTransferError extends effect.Data.TaggedError("UploadTransferError") { constructor(status, detail, note) { super({ status, detail, note }); } get message() { return `${this.detail}${this.note ?? ""}`; } withNote(note) { return new UploadTransferError(this.status, this.detail, `${this.note ?? ""}${note}`); } }; function describeTransferFailure(status, detail) { const trimmed = detail.slice(0, 300); if (status === 408 || /request timeout|bundle upload timed out/i.test(trimmed)) return `${trimmed} — the serving host timed out the upload; raise BOS_STORAGE_UPLOAD_TIMEOUT_MS there or upload smaller batches (BOS_MAX_BUNDLE_UPLOAD_BYTES)`; if (status === 524) return `Cloudflare gave up waiting for the upload service (proxy timeout) — the upload may still have been applied (server-side SRI dedupe makes retries idempotent). Upload smaller batches (BOS_MAX_BUNDLE_UPLOAD_BYTES)`; try { const parsed = JSON.parse(detail); if (parsed?.code === "INTERNAL_SERVER_ERROR" && parsed?.message === "Internal Server Error") return `${trimmed} — the host hid the underlying error; check the host logs for the cause`; } catch {} return trimmed; } async function requestJson(config, label, path, parse, init) { const fetchImpl = config.fetchImpl ?? fetch; const headers = { ...init?.headers }; if (config.apiKey) headers["x-api-key"] = config.apiKey; if (init?.body && !headers["content-type"]) headers["content-type"] = "application/json"; let lastError; let transportFailures = 0; let statusFailures = 0; for (let attempt = 1; attempt <= TRANSPORT_ATTEMPTS; attempt++) { try { const response = await fetchImpl(`${config.origin.replace(/\/$/, "")}${path}`, { ...init, headers: transportFailures > 0 ? { ...headers, connection: "close" } : headers }); if (response.ok) return parse(await response.json()); const detail = await response.text().catch(() => ""); lastError = new UploadTransferError(response.status, `[upload] ${label} failed: ${response.status} ${describeTransferFailure(response.status, detail)}`); if (!RETRYABLE_STATUSES.has(response.status)) break; statusFailures += 1; if (statusFailures >= STATUS_ATTEMPTS) break; } catch (error) { transportFailures += 1; lastError = new UploadTransferError(0, `[upload] ${label} failed: ${error instanceof Error ? error.message : String(error)}`); } if (attempt < TRANSPORT_ATTEMPTS) await sleep(backoffMs(attempt)); } if (lastError instanceof UploadTransferError && lastError.status === 0) lastError = lastError.withNote(" (Bun fetch note: re-run with fetch verbose diagnostics for detail)"); throw lastError ?? /* @__PURE__ */ new Error(`[upload] ${label} failed`); } const parseJsonBody = (label, schema) => { return (response) => { try { return schema.parse(response); } catch (error) { throw new UploadTransferError(0, `[upload] ${label} returned an unexpected payload: ${error instanceof Error ? error.message : String(error)}`); } }; }; function createUploadClient(config) { return { uploadBundle(input) { const payload = UploadRequestSchema.parse(input); return requestJson(config, `bundle upload for ${payload.workspace}`, "/api/storage/bundles", parseJsonBody("bundle upload", UploadResponseSchema), { method: "POST", body: JSON.stringify(payload) }); }, listBundles(input) { const query = new URLSearchParams({ account: input.account, gateway: input.gateway }); if (input.workspace) query.set("workspace", input.workspace); return requestJson(config, "bundle listing", `/api/storage/bundles?${query.toString()}`, parseJsonBody("bundle listing", ListBundlesResultSchema), { method: "GET" }); }, listVersions(input) { return requestJson(config, "version listing", `/api/storage/versions?${new URLSearchParams({ account: input.account, gateway: input.gateway, workspace: input.workspace }).toString()}`, parseJsonBody("version listing", ListVersionsResultSchema), { method: "GET" }); }, integrityReport(input) { return requestJson(config, "integrity report", `/api/storage/integrity?${new URLSearchParams({ account: input.account, gateway: input.gateway, workspace: input.workspace, version: input.version }).toString()}`, parseJsonBody("integrity report", IntegrityReportResultSchema), { method: "GET" }); } }; } //#endregion exports.BundleFileSchema = BundleFileSchema; exports.BundleObjectSummarySchema = BundleObjectSummarySchema; exports.IntegrityReportResultSchema = IntegrityReportResultSchema; exports.ListBundlesResultSchema = ListBundlesResultSchema; exports.ListVersionsResultSchema = ListVersionsResultSchema; exports.UploadRequestSchema = UploadRequestSchema; exports.UploadResponseSchema = UploadResponseSchema; exports.VersionSummarySchema = VersionSummarySchema; exports.createUploadClient = createUploadClient; //# sourceMappingURL=client.cjs.map