@depup/uploadthing 7.7.4-depup.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.
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/changes.json +18 -0
- package/client/index.cjs +331 -0
- package/client/index.d.cts +36 -0
- package/client/index.d.cts.map +1 -0
- package/client/index.d.ts +36 -0
- package/client/index.d.ts.map +1 -0
- package/client/index.js +286 -0
- package/client/index.js.map +1 -0
- package/client-future/index.cjs +426 -0
- package/client-future/index.d.cts +373 -0
- package/client-future/index.d.cts.map +1 -0
- package/client-future/index.d.ts +373 -0
- package/client-future/index.d.ts.map +1 -0
- package/client-future/index.js +383 -0
- package/client-future/index.js.map +1 -0
- package/dist/chunk-CUT6urMc.cjs +30 -0
- package/dist/deprecations-DPGpmqha.cjs +13 -0
- package/dist/deprecations-pLmw6Ytd.js +8 -0
- package/dist/deprecations-pLmw6Ytd.js.map +1 -0
- package/dist/package-BQ_k22T9.cjs +11 -0
- package/dist/package-DpScpvTA.js +6 -0
- package/dist/package-DpScpvTA.js.map +1 -0
- package/dist/shared-schemas-BmG5ARoX.js +82 -0
- package/dist/shared-schemas-BmG5ARoX.js.map +1 -0
- package/dist/shared-schemas-CG9VaBtT.cjs +129 -0
- package/dist/to-web-request-BQtxSXgE.cjs +98 -0
- package/dist/to-web-request-DhP0wXG-.js +87 -0
- package/dist/to-web-request-DhP0wXG-.js.map +1 -0
- package/dist/types-Bs3w2d_3.d.ts +627 -0
- package/dist/types-Bs3w2d_3.d.ts.map +1 -0
- package/dist/types-DiVC1t2V.d.cts +625 -0
- package/dist/types-DiVC1t2V.d.cts.map +1 -0
- package/dist/upload-builder-BUa7tovh.d.cts +32 -0
- package/dist/upload-builder-BUa7tovh.d.cts.map +1 -0
- package/dist/upload-builder-BcFawEj0.d.ts +32 -0
- package/dist/upload-builder-BcFawEj0.d.ts.map +1 -0
- package/dist/upload-builder-BlFOAnsv.js +699 -0
- package/dist/upload-builder-BlFOAnsv.js.map +1 -0
- package/dist/upload-builder-D6Ken9H0.cjs +794 -0
- package/dist/ut-reporter-BHoyNnzW.cjs +120 -0
- package/dist/ut-reporter-Dlppchbx.js +103 -0
- package/dist/ut-reporter-Dlppchbx.js.map +1 -0
- package/effect-platform/index.cjs +22 -0
- package/effect-platform/index.d.cts +54 -0
- package/effect-platform/index.d.cts.map +1 -0
- package/effect-platform/index.d.ts +54 -0
- package/effect-platform/index.d.ts.map +1 -0
- package/effect-platform/index.js +19 -0
- package/effect-platform/index.js.map +1 -0
- package/express/index.cjs +30 -0
- package/express/index.d.cts +28 -0
- package/express/index.d.cts.map +1 -0
- package/express/index.d.ts +28 -0
- package/express/index.d.ts.map +1 -0
- package/express/index.js +27 -0
- package/express/index.js.map +1 -0
- package/fastify/index.cjs +27 -0
- package/fastify/index.d.cts +28 -0
- package/fastify/index.d.cts.map +1 -0
- package/fastify/index.d.ts +28 -0
- package/fastify/index.d.ts.map +1 -0
- package/fastify/index.js +24 -0
- package/fastify/index.js.map +1 -0
- package/h3/index.cjs +20 -0
- package/h3/index.d.cts +28 -0
- package/h3/index.d.cts.map +1 -0
- package/h3/index.d.ts +28 -0
- package/h3/index.d.ts.map +1 -0
- package/h3/index.js +17 -0
- package/h3/index.js.map +1 -0
- package/next/index.cjs +22 -0
- package/next/index.d.cts +30 -0
- package/next/index.d.cts.map +1 -0
- package/next/index.d.ts +30 -0
- package/next/index.d.ts.map +1 -0
- package/next/index.js +19 -0
- package/next/index.js.map +1 -0
- package/next-legacy/index.cjs +28 -0
- package/next-legacy/index.d.cts +28 -0
- package/next-legacy/index.d.cts.map +1 -0
- package/next-legacy/index.d.ts +28 -0
- package/next-legacy/index.d.ts.map +1 -0
- package/next-legacy/index.js +25 -0
- package/next-legacy/index.js.map +1 -0
- package/package.json +210 -0
- package/remix/index.cjs +22 -0
- package/remix/index.d.cts +30 -0
- package/remix/index.d.cts.map +1 -0
- package/remix/index.d.ts +30 -0
- package/remix/index.d.ts.map +1 -0
- package/remix/index.js +19 -0
- package/remix/index.js.map +1 -0
- package/server/index.cjs +414 -0
- package/server/index.d.cts +211 -0
- package/server/index.d.cts.map +1 -0
- package/server/index.d.ts +213 -0
- package/server/index.d.ts.map +1 -0
- package/server/index.js +405 -0
- package/server/index.js.map +1 -0
- package/tw/index.cjs +70 -0
- package/tw/index.d.cts +29 -0
- package/tw/index.d.cts.map +1 -0
- package/tw/index.d.ts +29 -0
- package/tw/index.d.ts.map +1 -0
- package/tw/index.js +74 -0
- package/tw/index.js.map +1 -0
- package/tw/v4.css +11 -0
- package/types/index.cjs +3 -0
- package/types/index.d.cts +2 -0
- package/types/index.d.ts +2 -0
- package/types/index.js +3 -0
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
2
|
+
const require_package = require('./package-BQ_k22T9.cjs');
|
|
3
|
+
const require_deprecations = require('./deprecations-DPGpmqha.cjs');
|
|
4
|
+
const require_shared_schemas = require('./shared-schemas-CG9VaBtT.cjs');
|
|
5
|
+
const __uploadthing_shared = require_chunk.__toESM(require("@uploadthing/shared"));
|
|
6
|
+
const effect_Effect = require_chunk.__toESM(require("effect/Effect"));
|
|
7
|
+
const __effect_platform_HttpApp = require_chunk.__toESM(require("@effect/platform/HttpApp"));
|
|
8
|
+
const __effect_platform_HttpBody = require_chunk.__toESM(require("@effect/platform/HttpBody"));
|
|
9
|
+
const __effect_platform_HttpClient = require_chunk.__toESM(require("@effect/platform/HttpClient"));
|
|
10
|
+
const __effect_platform_HttpClientRequest = require_chunk.__toESM(require("@effect/platform/HttpClientRequest"));
|
|
11
|
+
const __effect_platform_HttpClientResponse = require_chunk.__toESM(require("@effect/platform/HttpClientResponse"));
|
|
12
|
+
const __effect_platform_HttpRouter = require_chunk.__toESM(require("@effect/platform/HttpRouter"));
|
|
13
|
+
const __effect_platform_HttpServerRequest = require_chunk.__toESM(require("@effect/platform/HttpServerRequest"));
|
|
14
|
+
const __effect_platform_HttpServerResponse = require_chunk.__toESM(require("@effect/platform/HttpServerResponse"));
|
|
15
|
+
const effect_Config = require_chunk.__toESM(require("effect/Config"));
|
|
16
|
+
const effect_Context = require_chunk.__toESM(require("effect/Context"));
|
|
17
|
+
const effect_Match = require_chunk.__toESM(require("effect/Match"));
|
|
18
|
+
const effect_Redacted = require_chunk.__toESM(require("effect/Redacted"));
|
|
19
|
+
const effect_Schema = require_chunk.__toESM(require("effect/Schema"));
|
|
20
|
+
const effect_ConfigProvider = require_chunk.__toESM(require("effect/ConfigProvider"));
|
|
21
|
+
const effect_Stream = require_chunk.__toESM(require("effect/Stream"));
|
|
22
|
+
const effect_ConfigError = require_chunk.__toESM(require("effect/ConfigError"));
|
|
23
|
+
const effect_Either = require_chunk.__toESM(require("effect/Either"));
|
|
24
|
+
const effect_Layer = require_chunk.__toESM(require("effect/Layer"));
|
|
25
|
+
const effect_Logger = require_chunk.__toESM(require("effect/Logger"));
|
|
26
|
+
const effect_LogLevel = require_chunk.__toESM(require("effect/LogLevel"));
|
|
27
|
+
const effect_Cause = require_chunk.__toESM(require("effect/Cause"));
|
|
28
|
+
const effect_Data = require_chunk.__toESM(require("effect/Data"));
|
|
29
|
+
const effect_Runtime = require_chunk.__toESM(require("effect/Runtime"));
|
|
30
|
+
const __effect_platform_FetchHttpClient = require_chunk.__toESM(require("@effect/platform/FetchHttpClient"));
|
|
31
|
+
const __effect_platform_Headers = require_chunk.__toESM(require("@effect/platform/Headers"));
|
|
32
|
+
const effect_FiberRef = require_chunk.__toESM(require("effect/FiberRef"));
|
|
33
|
+
const effect_ManagedRuntime = require_chunk.__toESM(require("effect/ManagedRuntime"));
|
|
34
|
+
|
|
35
|
+
//#region src/_internal/config.ts
|
|
36
|
+
/**
|
|
37
|
+
* Merge in `import.meta.env` to the built-in `process.env` provider
|
|
38
|
+
* Prefix keys with `UPLOADTHING_` so we can reference just the name.
|
|
39
|
+
* @example
|
|
40
|
+
* process.env.UPLOADTHING_TOKEN = "foo"
|
|
41
|
+
* Config.string("token"); // Config<"foo">
|
|
42
|
+
*/
|
|
43
|
+
const envProvider = effect_ConfigProvider.fromEnv().pipe(effect_ConfigProvider.orElse(() => effect_ConfigProvider.fromMap(new Map(Object.entries((0, __uploadthing_shared.filterDefinedObjectValues)({}?.env ?? {}))), { pathDelim: "_" })), effect_ConfigProvider.nested("uploadthing"), effect_ConfigProvider.constantCase);
|
|
44
|
+
/**
|
|
45
|
+
* Config provider that merges the options from the object
|
|
46
|
+
* and environment variables prefixed with `UPLOADTHING_`.
|
|
47
|
+
* @remarks Options take precedence over environment variables.
|
|
48
|
+
*/
|
|
49
|
+
const configProvider = (options) => effect_ConfigProvider.fromJson(options ?? {}).pipe(effect_ConfigProvider.orElse(() => envProvider));
|
|
50
|
+
const IsDevelopment = effect_Config.boolean("isDev").pipe(effect_Config.orElse(() => effect_Config.succeed(typeof process !== "undefined" ? process.env.NODE_ENV : void 0).pipe(effect_Config.map((_) => _ === "development"))), effect_Config.withDefault(false));
|
|
51
|
+
const UTToken = effect_Schema.Config("token", require_shared_schemas.UploadThingToken).pipe(effect_Effect.catchTags({ ConfigError: (e) => new __uploadthing_shared.UploadThingError({
|
|
52
|
+
code: e._op === "InvalidData" ? "INVALID_SERVER_CONFIG" : "MISSING_ENV",
|
|
53
|
+
message: e._op === "InvalidData" ? "Invalid token. A token is a base64 encoded JSON object matching { apiKey: string, appId: string, regions: string[] }." : "Missing token. Please set the `UPLOADTHING_TOKEN` environment variable or provide a token manually through config.",
|
|
54
|
+
cause: e
|
|
55
|
+
}) }));
|
|
56
|
+
const ApiUrl = effect_Config.string("apiUrl").pipe(effect_Config.withDefault("https://api.uploadthing.com"), effect_Config.mapAttempt((_) => new URL(_)), effect_Config.map((url) => url.href.replace(/\/$/, "")));
|
|
57
|
+
const IngestUrl = effect_Effect.fn(function* (preferredRegion) {
|
|
58
|
+
const { regions, ingestHost } = yield* UTToken;
|
|
59
|
+
const region = preferredRegion ? regions.find((r) => r === preferredRegion) ?? regions[0] : regions[0];
|
|
60
|
+
return yield* effect_Config.string("ingestUrl").pipe(effect_Config.withDefault(`https://${region}.${ingestHost}`), effect_Config.mapAttempt((_) => new URL(_)), effect_Config.map((url) => url.href.replace(/\/$/, "")));
|
|
61
|
+
});
|
|
62
|
+
const UtfsHost = effect_Config.string("utfsHost").pipe(effect_Config.withDefault("utfs.io"));
|
|
63
|
+
const UfsHost = effect_Config.string("ufsHost").pipe(effect_Config.withDefault("ufs.sh"));
|
|
64
|
+
const UfsAppIdLocation = effect_Config.literal("subdomain", "path")("ufsAppIdLocation").pipe(effect_Config.withDefault("subdomain"));
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/_internal/error-formatter.ts
|
|
68
|
+
function defaultErrorFormatter(error) {
|
|
69
|
+
return { message: error.message };
|
|
70
|
+
}
|
|
71
|
+
function formatError(error, router) {
|
|
72
|
+
const firstSlug = Object.keys(router)[0];
|
|
73
|
+
const errorFormatter = firstSlug ? router[firstSlug]?.errorFormatter ?? defaultErrorFormatter : defaultErrorFormatter;
|
|
74
|
+
return errorFormatter(error);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/_internal/jsonl.ts
|
|
79
|
+
const handleJsonLineStream = (schema, onChunk) => (stream) => {
|
|
80
|
+
let buf = "";
|
|
81
|
+
return stream.pipe(effect_Stream.decodeText(), effect_Stream.mapEffect((chunk) => effect_Effect.gen(function* () {
|
|
82
|
+
buf += chunk;
|
|
83
|
+
const parts = buf.split("\n");
|
|
84
|
+
const validChunks = [];
|
|
85
|
+
for (const part of parts) try {
|
|
86
|
+
validChunks.push(JSON.parse(part));
|
|
87
|
+
buf = buf.slice(part.length + 1);
|
|
88
|
+
} catch {}
|
|
89
|
+
yield* effect_Effect.logDebug("Received chunks").pipe(effect_Effect.annotateLogs("chunk", chunk), effect_Effect.annotateLogs("parsedChunks", validChunks), effect_Effect.annotateLogs("buf", buf));
|
|
90
|
+
return validChunks;
|
|
91
|
+
})), effect_Stream.mapEffect(effect_Schema.decodeUnknown(effect_Schema.Array(schema))), effect_Stream.mapEffect(effect_Effect.forEach((part) => onChunk(part))), effect_Stream.runDrain, effect_Effect.withLogSpan("handleJsonLineStream"));
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/_internal/logger.ts
|
|
96
|
+
/**
|
|
97
|
+
* Config.logLevel counter-intuitively accepts LogLevel["label"]
|
|
98
|
+
* instead of a literal, ripping it and changing to accept literal
|
|
99
|
+
* Effect 4.0 will change this to accept a literal and then we can
|
|
100
|
+
* remove this and go back to the built-in validator.
|
|
101
|
+
*/
|
|
102
|
+
const ConfigLogLevel = (name) => {
|
|
103
|
+
const config = effect_Config.mapOrFail(effect_Config.string(), (literal) => {
|
|
104
|
+
const level = effect_LogLevel.allLevels.find((level$1) => level$1._tag === literal);
|
|
105
|
+
return level === void 0 ? effect_Either.left(effect_ConfigError.InvalidData([], `Expected a log level but received ${literal}`)) : effect_Either.right(level);
|
|
106
|
+
});
|
|
107
|
+
return name === void 0 ? config : effect_Config.nested(config, name);
|
|
108
|
+
};
|
|
109
|
+
const withMinimalLogLevel = ConfigLogLevel("logLevel").pipe(effect_Config.withDefault(effect_LogLevel.Info), effect_Effect.andThen((level) => effect_Logger.minimumLogLevel(level)), effect_Effect.tapError((e) => effect_Effect.logError("Invalid log level").pipe(effect_Effect.annotateLogs("error", e))), effect_Effect.catchTag("ConfigError", (e) => new __uploadthing_shared.UploadThingError({
|
|
110
|
+
code: "INVALID_SERVER_CONFIG",
|
|
111
|
+
message: "Invalid server configuration",
|
|
112
|
+
cause: e
|
|
113
|
+
})), effect_Layer.unwrapEffect);
|
|
114
|
+
const LogFormat = effect_Config.literal("json", "logFmt", "structured", "pretty")("logFormat");
|
|
115
|
+
const withLogFormat = effect_Effect.gen(function* () {
|
|
116
|
+
const isDev = yield* IsDevelopment;
|
|
117
|
+
const logFormat = yield* LogFormat.pipe(effect_Config.withDefault(isDev ? "pretty" : "json"));
|
|
118
|
+
return effect_Logger[logFormat];
|
|
119
|
+
}).pipe(effect_Effect.catchTag("ConfigError", (e) => new __uploadthing_shared.UploadThingError({
|
|
120
|
+
code: "INVALID_SERVER_CONFIG",
|
|
121
|
+
message: "Invalid server configuration",
|
|
122
|
+
cause: e
|
|
123
|
+
})), effect_Layer.unwrapEffect);
|
|
124
|
+
const logHttpClientResponse = (message, opts) => {
|
|
125
|
+
const mixin = opts?.mixin ?? "json";
|
|
126
|
+
const level = effect_LogLevel.fromLiteral(opts?.level ?? "Debug");
|
|
127
|
+
return (response) => effect_Effect.flatMap(mixin !== "None" ? response[mixin] : effect_Effect.void, () => effect_Effect.logWithLevel(level, `${message} (${response.status})`).pipe(effect_Effect.annotateLogs("response", response)));
|
|
128
|
+
};
|
|
129
|
+
const logHttpClientError = (message) => (err) => err._tag === "ResponseError" ? logHttpClientResponse(message, { level: "Error" })(err.response) : effect_Effect.logError(message).pipe(effect_Effect.annotateLogs("error", err));
|
|
130
|
+
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/_internal/parser.ts
|
|
133
|
+
var ParserError = class extends effect_Data.TaggedError("ParserError") {
|
|
134
|
+
message = "Input validation failed. The original error with it's validation issues is in the error cause.";
|
|
135
|
+
};
|
|
136
|
+
function getParseFn(parser) {
|
|
137
|
+
if ("parseAsync" in parser && typeof parser.parseAsync === "function")
|
|
138
|
+
/**
|
|
139
|
+
* Zod
|
|
140
|
+
* TODO (next major): Consider wrapping ZodError in ParserError
|
|
141
|
+
*/
|
|
142
|
+
return parser.parseAsync;
|
|
143
|
+
if (effect_Schema.isSchema(parser))
|
|
144
|
+
/**
|
|
145
|
+
* Effect Schema
|
|
146
|
+
*/
|
|
147
|
+
return (value) => effect_Schema.decodeUnknownPromise(parser)(value).catch((error) => {
|
|
148
|
+
throw new ParserError({ cause: effect_Cause.squash(error[effect_Runtime.FiberFailureCauseId]) });
|
|
149
|
+
});
|
|
150
|
+
if ("~standard" in parser)
|
|
151
|
+
/**
|
|
152
|
+
* Standard Schema
|
|
153
|
+
* TODO (next major): Consider moving this to the top of the function
|
|
154
|
+
*/
|
|
155
|
+
return async (value) => {
|
|
156
|
+
const result = await parser["~standard"].validate(value);
|
|
157
|
+
if (result.issues) throw new ParserError({ cause: result.issues });
|
|
158
|
+
return result.value;
|
|
159
|
+
};
|
|
160
|
+
throw new Error("Invalid parser");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/_internal/route-config.ts
|
|
165
|
+
var FileSizeMismatch = class extends effect_Data.Error {
|
|
166
|
+
_tag = "FileSizeMismatch";
|
|
167
|
+
name = "FileSizeMismatchError";
|
|
168
|
+
constructor(type, max, actual) {
|
|
169
|
+
const reason = `You uploaded a ${type} file that was ${(0, __uploadthing_shared.bytesToFileSize)(actual)}, but the limit for that type is ${max}`;
|
|
170
|
+
super({ reason });
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
var FileCountMismatch = class extends effect_Data.Error {
|
|
174
|
+
_tag = "FileCountMismatch";
|
|
175
|
+
name = "FileCountMismatchError";
|
|
176
|
+
constructor(type, boundtype, bound, actual) {
|
|
177
|
+
const reason = `You uploaded ${actual} file(s) of type '${type}', but the ${boundtype} for that type is ${bound}`;
|
|
178
|
+
super({ reason });
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const assertFilesMeetConfig = (files, routeConfig) => effect_Effect.gen(function* () {
|
|
182
|
+
const counts = {};
|
|
183
|
+
for (const file of files) {
|
|
184
|
+
const type = yield* (0, __uploadthing_shared.matchFileType)(file, (0, __uploadthing_shared.objectKeys)(routeConfig));
|
|
185
|
+
counts[type] = (counts[type] ?? 0) + 1;
|
|
186
|
+
const sizeLimit = routeConfig[type]?.maxFileSize;
|
|
187
|
+
if (!sizeLimit) return yield* new __uploadthing_shared.InvalidRouteConfigError(type, "maxFileSize");
|
|
188
|
+
const sizeLimitBytes = yield* (0, __uploadthing_shared.fileSizeToBytes)(sizeLimit);
|
|
189
|
+
if (file.size > sizeLimitBytes) return yield* new FileSizeMismatch(type, sizeLimit, file.size);
|
|
190
|
+
}
|
|
191
|
+
for (const _key in counts) {
|
|
192
|
+
const key = _key;
|
|
193
|
+
const config = routeConfig[key];
|
|
194
|
+
if (!config) return yield* new __uploadthing_shared.InvalidRouteConfigError(key);
|
|
195
|
+
const count = counts[key];
|
|
196
|
+
const min = config.minFileCount;
|
|
197
|
+
const max = config.maxFileCount;
|
|
198
|
+
if (min > max) return yield* new __uploadthing_shared.UploadThingError({
|
|
199
|
+
code: "BAD_REQUEST",
|
|
200
|
+
message: "Invalid config during file count - minFileCount > maxFileCount",
|
|
201
|
+
cause: `minFileCount must be less than maxFileCount for key ${key}. got: ${min} > ${max}`
|
|
202
|
+
});
|
|
203
|
+
if (count != null && count < min) return yield* new FileCountMismatch(key, "minimum", min, count);
|
|
204
|
+
if (count != null && count > max) return yield* new FileCountMismatch(key, "maximum", max, count);
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
});
|
|
208
|
+
const extractRouterConfig = (router) => effect_Effect.forEach((0, __uploadthing_shared.objectKeys)(router), (slug) => effect_Effect.map((0, __uploadthing_shared.fillInputRouteConfig)(router[slug].routerConfig), (config) => ({
|
|
209
|
+
slug,
|
|
210
|
+
config
|
|
211
|
+
})));
|
|
212
|
+
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/_internal/runtime.ts
|
|
215
|
+
const makeRuntime = (fetch, config) => {
|
|
216
|
+
const fetchHttpClient = effect_Layer.provideMerge(__effect_platform_FetchHttpClient.layer, effect_Layer.succeed(__effect_platform_FetchHttpClient.Fetch, fetch));
|
|
217
|
+
const withRedactedHeaders = effect_Layer.effectDiscard(effect_FiberRef.update(__effect_platform_Headers.currentRedactedNames, (_) => _.concat(["x-uploadthing-api-key"])));
|
|
218
|
+
const layer = effect_Layer.provide(effect_Layer.mergeAll(withLogFormat, withMinimalLogLevel, fetchHttpClient, withRedactedHeaders), effect_Layer.setConfigProvider(configProvider(config)));
|
|
219
|
+
return effect_ManagedRuntime.make(layer);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/_internal/types.ts
|
|
224
|
+
/**
|
|
225
|
+
* Marker used to select the region based on the incoming request
|
|
226
|
+
*/
|
|
227
|
+
const UTRegion = Symbol("uploadthing-region-symbol");
|
|
228
|
+
/**
|
|
229
|
+
* Marker used to append a `customId` to the incoming file data in `.middleware()`
|
|
230
|
+
* @example
|
|
231
|
+
* ```ts
|
|
232
|
+
* .middleware((opts) => {
|
|
233
|
+
* return {
|
|
234
|
+
* [UTFiles]: opts.files.map((file) => ({
|
|
235
|
+
* ...file,
|
|
236
|
+
* customId: generateId(),
|
|
237
|
+
* }))
|
|
238
|
+
* };
|
|
239
|
+
* })
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
const UTFiles = Symbol("uploadthing-custom-id-symbol");
|
|
243
|
+
|
|
244
|
+
//#endregion
|
|
245
|
+
//#region src/_internal/handler.ts
|
|
246
|
+
var AdapterArguments = class extends effect_Context.Tag("uploadthing/AdapterArguments")() {};
|
|
247
|
+
/**
|
|
248
|
+
* Create a request handler adapter for any framework or server library.
|
|
249
|
+
* Refer to the existing adapters for examples on how to use this function.
|
|
250
|
+
* @public
|
|
251
|
+
*
|
|
252
|
+
* @param makeAdapterArgs - Function that takes the args from your framework and returns an Effect that resolves to the adapter args.
|
|
253
|
+
* These args are passed to the `.middleware`, `.onUploadComplete`, and `.onUploadError` hooks.
|
|
254
|
+
* @param toRequest - Function that takes the args from your framework and returns an Effect that resolves to a web Request object.
|
|
255
|
+
* @param opts - The router config and other options that are normally passed to `createRequestHandler` of official adapters
|
|
256
|
+
* @param beAdapter - [Optional] The adapter name of the adapter, used for telemetry purposes
|
|
257
|
+
* @returns A function that takes the args from your framework and returns a promise that resolves to a Response object.
|
|
258
|
+
*/
|
|
259
|
+
const makeAdapterHandler = (makeAdapterArgs, toRequest, opts, beAdapter) => {
|
|
260
|
+
const managed = makeRuntime(opts.config?.fetch, opts.config);
|
|
261
|
+
const handle = effect_Effect.promise(() => managed.runtime().then(__effect_platform_HttpApp.toWebHandlerRuntime));
|
|
262
|
+
const app = (...args) => effect_Effect.map(effect_Effect.promise(() => managed.runPromise(createRequestHandler(opts, beAdapter ?? "custom"))), effect_Effect.provideServiceEffect(AdapterArguments, makeAdapterArgs(...args)));
|
|
263
|
+
return async (...args) => {
|
|
264
|
+
const result = await handle.pipe(effect_Effect.ap(app(...args)), effect_Effect.ap(toRequest(...args)), effect_Effect.withLogSpan("requestHandler"), managed.runPromise);
|
|
265
|
+
return result;
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
const createRequestHandler = (opts, beAdapter) => effect_Effect.gen(function* () {
|
|
269
|
+
const isDevelopment = yield* IsDevelopment;
|
|
270
|
+
const routerConfig = yield* extractRouterConfig(opts.router);
|
|
271
|
+
const handleDaemon = (() => {
|
|
272
|
+
if (opts.config?.handleDaemonPromise) return opts.config.handleDaemonPromise;
|
|
273
|
+
return isDevelopment ? "void" : "await";
|
|
274
|
+
})();
|
|
275
|
+
if (isDevelopment && handleDaemon === "await") return yield* new __uploadthing_shared.UploadThingError({
|
|
276
|
+
code: "INVALID_SERVER_CONFIG",
|
|
277
|
+
message: "handleDaemonPromise: \"await\" is forbidden in development."
|
|
278
|
+
});
|
|
279
|
+
const GET = effect_Effect.gen(function* () {
|
|
280
|
+
return yield* __effect_platform_HttpServerResponse.json(routerConfig);
|
|
281
|
+
});
|
|
282
|
+
const POST = effect_Effect.gen(function* () {
|
|
283
|
+
const { "uploadthing-hook": uploadthingHook, "x-uploadthing-package": fePackage, "x-uploadthing-version": clientVersion } = yield* __effect_platform_HttpServerRequest.schemaHeaders(effect_Schema.Struct({
|
|
284
|
+
"uploadthing-hook": require_shared_schemas.UploadThingHook.pipe(effect_Schema.optional),
|
|
285
|
+
"x-uploadthing-package": effect_Schema.String.pipe(effect_Schema.optionalWith({ default: () => "unknown" })),
|
|
286
|
+
"x-uploadthing-version": effect_Schema.String.pipe(effect_Schema.optionalWith({ default: () => require_package.version }))
|
|
287
|
+
}));
|
|
288
|
+
if (clientVersion !== require_package.version) {
|
|
289
|
+
const serverVersion = require_package.version;
|
|
290
|
+
yield* effect_Effect.logWarning("Client version mismatch. Things may not work as expected, please sync your versions to ensure compatibility.").pipe(effect_Effect.annotateLogs({
|
|
291
|
+
clientVersion,
|
|
292
|
+
serverVersion
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
const { slug, actionType } = yield* __effect_platform_HttpRouter.schemaParams(effect_Schema.Struct({
|
|
296
|
+
actionType: require_shared_schemas.ActionType.pipe(effect_Schema.optional),
|
|
297
|
+
slug: effect_Schema.String
|
|
298
|
+
}));
|
|
299
|
+
const uploadable = opts.router[slug];
|
|
300
|
+
if (!uploadable) {
|
|
301
|
+
const msg = `No file route found for slug ${slug}`;
|
|
302
|
+
yield* effect_Effect.logError(msg);
|
|
303
|
+
return yield* new __uploadthing_shared.UploadThingError({
|
|
304
|
+
code: "NOT_FOUND",
|
|
305
|
+
message: msg
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
const { body, fiber } = yield* effect_Match.value({
|
|
309
|
+
actionType,
|
|
310
|
+
uploadthingHook
|
|
311
|
+
}).pipe(effect_Match.when({
|
|
312
|
+
actionType: "upload",
|
|
313
|
+
uploadthingHook: void 0
|
|
314
|
+
}, () => handleUploadAction({
|
|
315
|
+
uploadable,
|
|
316
|
+
fePackage,
|
|
317
|
+
beAdapter,
|
|
318
|
+
slug
|
|
319
|
+
})), effect_Match.when({
|
|
320
|
+
actionType: void 0,
|
|
321
|
+
uploadthingHook: "callback"
|
|
322
|
+
}, () => handleCallbackRequest({
|
|
323
|
+
uploadable,
|
|
324
|
+
fePackage,
|
|
325
|
+
beAdapter
|
|
326
|
+
})), effect_Match.when({
|
|
327
|
+
actionType: void 0,
|
|
328
|
+
uploadthingHook: "error"
|
|
329
|
+
}, () => handleErrorRequest({ uploadable })), effect_Match.orElse(() => effect_Effect.succeed({
|
|
330
|
+
body: null,
|
|
331
|
+
fiber: null
|
|
332
|
+
})));
|
|
333
|
+
if (fiber) {
|
|
334
|
+
yield* effect_Effect.logDebug("Running fiber as daemon").pipe(effect_Effect.annotateLogs("handleDaemon", handleDaemon));
|
|
335
|
+
if (handleDaemon === "void") {} else if (handleDaemon === "await") yield* fiber.await;
|
|
336
|
+
else if (typeof handleDaemon === "function") handleDaemon(effect_Effect.runPromise(fiber.await));
|
|
337
|
+
}
|
|
338
|
+
yield* effect_Effect.logDebug("Sending response").pipe(effect_Effect.annotateLogs("body", body));
|
|
339
|
+
return yield* __effect_platform_HttpServerResponse.json(body);
|
|
340
|
+
}).pipe(effect_Effect.catchTags({
|
|
341
|
+
ParseError: (e) => __effect_platform_HttpServerResponse.json(formatError(new __uploadthing_shared.UploadThingError({
|
|
342
|
+
code: "BAD_REQUEST",
|
|
343
|
+
message: "Invalid input",
|
|
344
|
+
cause: e.message
|
|
345
|
+
}), opts.router), { status: 400 }),
|
|
346
|
+
UploadThingError: (e) => __effect_platform_HttpServerResponse.json(formatError(e, opts.router), { status: (0, __uploadthing_shared.getStatusCodeFromError)(e) })
|
|
347
|
+
}));
|
|
348
|
+
const appendResponseHeaders = effect_Effect.map(__effect_platform_HttpServerResponse.setHeader("x-uploadthing-version", require_package.version));
|
|
349
|
+
return __effect_platform_HttpRouter.empty.pipe(__effect_platform_HttpRouter.get("*", GET), __effect_platform_HttpRouter.post("*", POST), __effect_platform_HttpRouter.use(appendResponseHeaders));
|
|
350
|
+
}).pipe(effect_Effect.withLogSpan("createRequestHandler"));
|
|
351
|
+
const handleErrorRequest = (opts) => effect_Effect.gen(function* () {
|
|
352
|
+
const { uploadable } = opts;
|
|
353
|
+
const request = yield* __effect_platform_HttpServerRequest.HttpServerRequest;
|
|
354
|
+
const { apiKey } = yield* UTToken;
|
|
355
|
+
const verified = yield* (0, __uploadthing_shared.verifySignature)(yield* request.text, request.headers["x-uploadthing-signature"] ?? null, apiKey);
|
|
356
|
+
yield* effect_Effect.logDebug(`Signature verified: ${verified}`);
|
|
357
|
+
if (!verified) {
|
|
358
|
+
yield* effect_Effect.logError("Invalid signature");
|
|
359
|
+
return yield* new __uploadthing_shared.UploadThingError({
|
|
360
|
+
code: "BAD_REQUEST",
|
|
361
|
+
message: "Invalid signature"
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
const requestInput = yield* __effect_platform_HttpServerRequest.schemaBodyJson(effect_Schema.Struct({
|
|
365
|
+
fileKey: effect_Schema.String,
|
|
366
|
+
error: effect_Schema.String
|
|
367
|
+
}));
|
|
368
|
+
yield* effect_Effect.logDebug("Handling error callback request with input:").pipe(effect_Effect.annotateLogs("json", requestInput));
|
|
369
|
+
const adapterArgs = yield* AdapterArguments;
|
|
370
|
+
const fiber = yield* effect_Effect.tryPromise({
|
|
371
|
+
try: async () => uploadable.onUploadError({
|
|
372
|
+
...adapterArgs,
|
|
373
|
+
error: new __uploadthing_shared.UploadThingError({
|
|
374
|
+
code: "UPLOAD_FAILED",
|
|
375
|
+
message: `Upload failed for ${requestInput.fileKey}: ${requestInput.error}`
|
|
376
|
+
}),
|
|
377
|
+
fileKey: requestInput.fileKey
|
|
378
|
+
}),
|
|
379
|
+
catch: (error) => new __uploadthing_shared.UploadThingError({
|
|
380
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
381
|
+
message: "Failed to run onUploadError",
|
|
382
|
+
cause: error
|
|
383
|
+
})
|
|
384
|
+
}).pipe(effect_Effect.tapError((error) => effect_Effect.logError("Failed to run onUploadError. You probably shouldn't be throwing errors here.").pipe(effect_Effect.annotateLogs("error", error)))).pipe(effect_Effect.ignoreLogged, effect_Effect.forkDaemon);
|
|
385
|
+
return {
|
|
386
|
+
body: null,
|
|
387
|
+
fiber
|
|
388
|
+
};
|
|
389
|
+
}).pipe(effect_Effect.withLogSpan("handleErrorRequest"));
|
|
390
|
+
const handleCallbackRequest = (opts) => effect_Effect.gen(function* () {
|
|
391
|
+
const { uploadable, fePackage, beAdapter } = opts;
|
|
392
|
+
const request = yield* __effect_platform_HttpServerRequest.HttpServerRequest;
|
|
393
|
+
const { apiKey } = yield* UTToken;
|
|
394
|
+
const verified = yield* (0, __uploadthing_shared.verifySignature)(yield* request.text, request.headers["x-uploadthing-signature"] ?? null, apiKey);
|
|
395
|
+
yield* effect_Effect.logDebug(`Signature verified: ${verified}`);
|
|
396
|
+
if (!verified) {
|
|
397
|
+
yield* effect_Effect.logError("Invalid signature");
|
|
398
|
+
return yield* new __uploadthing_shared.UploadThingError({
|
|
399
|
+
code: "BAD_REQUEST",
|
|
400
|
+
message: "Invalid signature"
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
const requestInput = yield* __effect_platform_HttpServerRequest.schemaBodyJson(effect_Schema.Struct({
|
|
404
|
+
status: effect_Schema.String,
|
|
405
|
+
file: require_shared_schemas.UploadedFileData,
|
|
406
|
+
origin: effect_Schema.String,
|
|
407
|
+
metadata: effect_Schema.Record({
|
|
408
|
+
key: effect_Schema.String,
|
|
409
|
+
value: effect_Schema.Unknown
|
|
410
|
+
})
|
|
411
|
+
}));
|
|
412
|
+
yield* effect_Effect.logDebug("Handling callback request with input:").pipe(effect_Effect.annotateLogs("json", requestInput));
|
|
413
|
+
/**
|
|
414
|
+
* Run `.onUploadComplete` as a daemon to prevent the
|
|
415
|
+
* request from UT to potentially timeout.
|
|
416
|
+
*/
|
|
417
|
+
const fiber = yield* effect_Effect.gen(function* () {
|
|
418
|
+
const adapterArgs = yield* AdapterArguments;
|
|
419
|
+
const serverData = yield* effect_Effect.tryPromise({
|
|
420
|
+
try: async () => uploadable.onUploadComplete({
|
|
421
|
+
...adapterArgs,
|
|
422
|
+
file: {
|
|
423
|
+
...requestInput.file,
|
|
424
|
+
get url() {
|
|
425
|
+
require_deprecations.logDeprecationWarning("`file.url` is deprecated and will be removed in uploadthing v9. Use `file.ufsUrl` instead.");
|
|
426
|
+
return requestInput.file.url;
|
|
427
|
+
},
|
|
428
|
+
get appUrl() {
|
|
429
|
+
require_deprecations.logDeprecationWarning("`file.appUrl` is deprecated and will be removed in uploadthing v9. Use `file.ufsUrl` instead.");
|
|
430
|
+
return requestInput.file.appUrl;
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
metadata: requestInput.metadata
|
|
434
|
+
}),
|
|
435
|
+
catch: (error) => new __uploadthing_shared.UploadThingError({
|
|
436
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
437
|
+
message: "Failed to run onUploadComplete. You probably shouldn't be throwing errors here.",
|
|
438
|
+
cause: error
|
|
439
|
+
})
|
|
440
|
+
});
|
|
441
|
+
const payload = {
|
|
442
|
+
fileKey: requestInput.file.key,
|
|
443
|
+
callbackData: serverData ?? null
|
|
444
|
+
};
|
|
445
|
+
yield* effect_Effect.logDebug("'onUploadComplete' callback finished. Sending response to UploadThing:").pipe(effect_Effect.annotateLogs("callbackData", payload));
|
|
446
|
+
const httpClient = (yield* __effect_platform_HttpClient.HttpClient).pipe(__effect_platform_HttpClient.filterStatusOk);
|
|
447
|
+
yield* __effect_platform_HttpClientRequest.post(`/callback-result`).pipe(__effect_platform_HttpClientRequest.prependUrl(requestInput.origin), __effect_platform_HttpClientRequest.setHeaders({
|
|
448
|
+
"x-uploadthing-api-key": effect_Redacted.value(apiKey),
|
|
449
|
+
"x-uploadthing-version": require_package.version,
|
|
450
|
+
"x-uploadthing-be-adapter": beAdapter,
|
|
451
|
+
"x-uploadthing-fe-package": fePackage
|
|
452
|
+
}), __effect_platform_HttpClientRequest.bodyJson(payload), effect_Effect.flatMap(httpClient.execute), effect_Effect.tapError(logHttpClientError("Failed to register callback result")), effect_Effect.flatMap(__effect_platform_HttpClientResponse.schemaBodyJson(require_shared_schemas.CallbackResultResponse)), effect_Effect.tap(effect_Effect.log("Sent callback result to UploadThing")), effect_Effect.scoped);
|
|
453
|
+
}).pipe(effect_Effect.ignoreLogged, effect_Effect.forkDaemon);
|
|
454
|
+
return {
|
|
455
|
+
body: null,
|
|
456
|
+
fiber
|
|
457
|
+
};
|
|
458
|
+
}).pipe(effect_Effect.withLogSpan("handleCallbackRequest"));
|
|
459
|
+
const runRouteMiddleware = (opts) => effect_Effect.gen(function* () {
|
|
460
|
+
const { json: { files, input }, uploadable } = opts;
|
|
461
|
+
yield* effect_Effect.logDebug("Running middleware");
|
|
462
|
+
const adapterArgs = yield* AdapterArguments;
|
|
463
|
+
const metadata = yield* effect_Effect.tryPromise({
|
|
464
|
+
try: async () => uploadable.middleware({
|
|
465
|
+
...adapterArgs,
|
|
466
|
+
input,
|
|
467
|
+
files
|
|
468
|
+
}),
|
|
469
|
+
catch: (error) => error instanceof __uploadthing_shared.UploadThingError ? error : new __uploadthing_shared.UploadThingError({
|
|
470
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
471
|
+
message: "Failed to run middleware",
|
|
472
|
+
cause: error
|
|
473
|
+
})
|
|
474
|
+
});
|
|
475
|
+
if (metadata[UTFiles] && metadata[UTFiles].length !== files.length) {
|
|
476
|
+
const msg = `Expected files override to have the same length as original files, got ${metadata[UTFiles].length} but expected ${files.length}`;
|
|
477
|
+
yield* effect_Effect.logError(msg);
|
|
478
|
+
return yield* new __uploadthing_shared.UploadThingError({
|
|
479
|
+
code: "BAD_REQUEST",
|
|
480
|
+
message: "Files override must have the same length as files",
|
|
481
|
+
cause: msg
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
const filesWithCustomIds = yield* effect_Effect.forEach(files, (file, idx) => effect_Effect.gen(function* () {
|
|
485
|
+
const theirs = metadata[UTFiles]?.[idx];
|
|
486
|
+
if (theirs && theirs.size !== file.size) yield* effect_Effect.logWarning("File size mismatch. Reverting to original size");
|
|
487
|
+
return {
|
|
488
|
+
name: theirs?.name ?? file.name,
|
|
489
|
+
size: file.size,
|
|
490
|
+
type: file.type,
|
|
491
|
+
customId: theirs?.customId,
|
|
492
|
+
lastModified: theirs?.lastModified ?? Date.now()
|
|
493
|
+
};
|
|
494
|
+
}));
|
|
495
|
+
return {
|
|
496
|
+
metadata,
|
|
497
|
+
filesWithCustomIds,
|
|
498
|
+
preferredRegion: metadata[UTRegion]
|
|
499
|
+
};
|
|
500
|
+
}).pipe(effect_Effect.withLogSpan("runRouteMiddleware"));
|
|
501
|
+
const handleUploadAction = (opts) => effect_Effect.gen(function* () {
|
|
502
|
+
const httpClient = (yield* __effect_platform_HttpClient.HttpClient).pipe(__effect_platform_HttpClient.filterStatusOk);
|
|
503
|
+
const { uploadable, fePackage, beAdapter, slug } = opts;
|
|
504
|
+
const json = yield* __effect_platform_HttpServerRequest.schemaBodyJson(require_shared_schemas.UploadActionPayload);
|
|
505
|
+
yield* effect_Effect.logDebug("Handling upload request").pipe(effect_Effect.annotateLogs("json", json));
|
|
506
|
+
yield* effect_Effect.logDebug("Parsing user input");
|
|
507
|
+
const parsedInput = yield* effect_Effect.tryPromise({
|
|
508
|
+
try: () => getParseFn(uploadable.inputParser)(json.input),
|
|
509
|
+
catch: (error) => new __uploadthing_shared.UploadThingError({
|
|
510
|
+
code: "BAD_REQUEST",
|
|
511
|
+
message: "Invalid input",
|
|
512
|
+
cause: error
|
|
513
|
+
})
|
|
514
|
+
});
|
|
515
|
+
yield* effect_Effect.logDebug("Input parsed successfully").pipe(effect_Effect.annotateLogs("input", parsedInput));
|
|
516
|
+
const { metadata, filesWithCustomIds, preferredRegion } = yield* runRouteMiddleware({
|
|
517
|
+
json: {
|
|
518
|
+
input: parsedInput,
|
|
519
|
+
files: json.files
|
|
520
|
+
},
|
|
521
|
+
uploadable
|
|
522
|
+
});
|
|
523
|
+
yield* effect_Effect.logDebug("Parsing route config").pipe(effect_Effect.annotateLogs("routerConfig", uploadable.routerConfig));
|
|
524
|
+
const parsedConfig = yield* (0, __uploadthing_shared.fillInputRouteConfig)(uploadable.routerConfig).pipe(effect_Effect.catchTag("InvalidRouteConfig", (err) => new __uploadthing_shared.UploadThingError({
|
|
525
|
+
code: "BAD_REQUEST",
|
|
526
|
+
message: "Invalid route config",
|
|
527
|
+
cause: err
|
|
528
|
+
})));
|
|
529
|
+
yield* effect_Effect.logDebug("Route config parsed successfully").pipe(effect_Effect.annotateLogs("routeConfig", parsedConfig));
|
|
530
|
+
yield* effect_Effect.logDebug("Validating files meet the config requirements").pipe(effect_Effect.annotateLogs("files", json.files));
|
|
531
|
+
yield* assertFilesMeetConfig(json.files, parsedConfig).pipe(effect_Effect.mapError((e) => new __uploadthing_shared.UploadThingError({
|
|
532
|
+
code: "BAD_REQUEST",
|
|
533
|
+
message: `Invalid config: ${e._tag}`,
|
|
534
|
+
cause: "reason" in e ? e.reason : e.message
|
|
535
|
+
})));
|
|
536
|
+
yield* effect_Effect.logDebug("Files validated.");
|
|
537
|
+
const fileUploadRequests = yield* effect_Effect.forEach(filesWithCustomIds, (file) => effect_Effect.map((0, __uploadthing_shared.matchFileType)(file, (0, __uploadthing_shared.objectKeys)(parsedConfig)), (type) => ({
|
|
538
|
+
name: file.name,
|
|
539
|
+
size: file.size,
|
|
540
|
+
type: file.type || type,
|
|
541
|
+
lastModified: file.lastModified,
|
|
542
|
+
customId: file.customId,
|
|
543
|
+
contentDisposition: parsedConfig[type]?.contentDisposition ?? "inline",
|
|
544
|
+
acl: parsedConfig[type]?.acl
|
|
545
|
+
}))).pipe(effect_Effect.catchTags({
|
|
546
|
+
InvalidFileType: (e) => effect_Effect.die(e),
|
|
547
|
+
UnknownFileType: (e) => effect_Effect.die(e)
|
|
548
|
+
}));
|
|
549
|
+
const routeOptions = uploadable.routeOptions;
|
|
550
|
+
const { apiKey, appId } = yield* UTToken;
|
|
551
|
+
const ingestUrl = yield* IngestUrl(preferredRegion);
|
|
552
|
+
const isDev = yield* IsDevelopment;
|
|
553
|
+
yield* effect_Effect.logDebug("Generating presigned URLs").pipe(effect_Effect.annotateLogs("fileUploadRequests", fileUploadRequests), effect_Effect.annotateLogs("ingestUrl", ingestUrl));
|
|
554
|
+
const presignedUrls = yield* effect_Effect.forEach(fileUploadRequests, (file) => effect_Effect.gen(function* () {
|
|
555
|
+
const key = yield* (0, __uploadthing_shared.generateKey)(file, appId, routeOptions.getFileHashParts);
|
|
556
|
+
const url = yield* (0, __uploadthing_shared.generateSignedURL)(`${ingestUrl}/${key}`, apiKey, {
|
|
557
|
+
ttlInSeconds: routeOptions.presignedURLTTL,
|
|
558
|
+
data: {
|
|
559
|
+
"x-ut-identifier": appId,
|
|
560
|
+
"x-ut-file-name": file.name,
|
|
561
|
+
"x-ut-file-size": file.size,
|
|
562
|
+
"x-ut-file-type": file.type,
|
|
563
|
+
"x-ut-slug": slug,
|
|
564
|
+
"x-ut-custom-id": file.customId,
|
|
565
|
+
"x-ut-content-disposition": file.contentDisposition,
|
|
566
|
+
"x-ut-acl": file.acl
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
return {
|
|
570
|
+
url,
|
|
571
|
+
key
|
|
572
|
+
};
|
|
573
|
+
}), { concurrency: "unbounded" });
|
|
574
|
+
const serverReq = yield* __effect_platform_HttpServerRequest.HttpServerRequest;
|
|
575
|
+
const requestUrl = yield* __effect_platform_HttpServerRequest.toURL(serverReq);
|
|
576
|
+
const devHookRequest = yield* effect_Config.string("callbackUrl").pipe(effect_Config.withDefault(requestUrl.origin + requestUrl.pathname), effect_Effect.map((url) => __effect_platform_HttpClientRequest.post(url).pipe(__effect_platform_HttpClientRequest.appendUrlParam("slug", slug))));
|
|
577
|
+
const metadataRequest = __effect_platform_HttpClientRequest.post("/route-metadata").pipe(__effect_platform_HttpClientRequest.prependUrl(ingestUrl), __effect_platform_HttpClientRequest.setHeaders({
|
|
578
|
+
"x-uploadthing-api-key": effect_Redacted.value(apiKey),
|
|
579
|
+
"x-uploadthing-version": require_package.version,
|
|
580
|
+
"x-uploadthing-be-adapter": beAdapter,
|
|
581
|
+
"x-uploadthing-fe-package": fePackage
|
|
582
|
+
}), __effect_platform_HttpClientRequest.bodyJson({
|
|
583
|
+
fileKeys: presignedUrls.map(({ key }) => key),
|
|
584
|
+
metadata,
|
|
585
|
+
isDev,
|
|
586
|
+
callbackUrl: devHookRequest.url,
|
|
587
|
+
callbackSlug: slug,
|
|
588
|
+
awaitServerData: routeOptions.awaitServerData ?? true
|
|
589
|
+
}), effect_Effect.flatMap(httpClient.execute));
|
|
590
|
+
const handleDevStreamError = effect_Effect.fn("handleDevStreamError")(function* (err, chunk) {
|
|
591
|
+
const schema = effect_Schema.parseJson(effect_Schema.Struct({ file: require_shared_schemas.UploadedFileData }));
|
|
592
|
+
const parsedChunk = yield* effect_Schema.decodeUnknown(schema)(chunk);
|
|
593
|
+
const key = parsedChunk.file.key;
|
|
594
|
+
yield* effect_Effect.logError("Failed to forward callback request from dev stream").pipe(effect_Effect.annotateLogs({
|
|
595
|
+
fileKey: key,
|
|
596
|
+
error: err.message
|
|
597
|
+
}));
|
|
598
|
+
const httpResponse = yield* __effect_platform_HttpClientRequest.post("/callback-result").pipe(__effect_platform_HttpClientRequest.prependUrl(ingestUrl), __effect_platform_HttpClientRequest.setHeaders({
|
|
599
|
+
"x-uploadthing-api-key": effect_Redacted.value(apiKey),
|
|
600
|
+
"x-uploadthing-version": require_package.version,
|
|
601
|
+
"x-uploadthing-be-adapter": beAdapter,
|
|
602
|
+
"x-uploadthing-fe-package": fePackage
|
|
603
|
+
}), __effect_platform_HttpClientRequest.bodyJson({
|
|
604
|
+
fileKey: key,
|
|
605
|
+
error: `Failed to forward callback request from dev stream: ${err.message}`
|
|
606
|
+
}), effect_Effect.flatMap(httpClient.execute));
|
|
607
|
+
yield* logHttpClientResponse("Reported callback error to UploadThing")(httpResponse);
|
|
608
|
+
});
|
|
609
|
+
const fiber = yield* effect_Effect.if(isDev, {
|
|
610
|
+
onTrue: () => metadataRequest.pipe(effect_Effect.tapBoth({
|
|
611
|
+
onSuccess: logHttpClientResponse("Registered metadata", { mixin: "None" }),
|
|
612
|
+
onFailure: logHttpClientError("Failed to register metadata")
|
|
613
|
+
}), __effect_platform_HttpClientResponse.stream, handleJsonLineStream(require_shared_schemas.MetadataFetchStreamPart, (chunk) => devHookRequest.pipe(__effect_platform_HttpClientRequest.setHeaders({
|
|
614
|
+
"uploadthing-hook": chunk.hook,
|
|
615
|
+
"x-uploadthing-signature": chunk.signature
|
|
616
|
+
}), __effect_platform_HttpClientRequest.setBody(__effect_platform_HttpBody.text(chunk.payload, "application/json")), httpClient.execute, effect_Effect.tap(logHttpClientResponse("Successfully forwarded callback request from dev stream")), effect_Effect.catchTag("ResponseError", (err) => handleDevStreamError(err, chunk.payload)), effect_Effect.annotateLogs(chunk), effect_Effect.asVoid, effect_Effect.ignoreLogged, effect_Effect.scoped))),
|
|
617
|
+
onFalse: () => metadataRequest.pipe(effect_Effect.tapBoth({
|
|
618
|
+
onSuccess: logHttpClientResponse("Registered metadata"),
|
|
619
|
+
onFailure: logHttpClientError("Failed to register metadata")
|
|
620
|
+
}), effect_Effect.flatMap(__effect_platform_HttpClientResponse.schemaBodyJson(require_shared_schemas.MetadataFetchResponse)), effect_Effect.scoped)
|
|
621
|
+
}).pipe(effect_Effect.forkDaemon);
|
|
622
|
+
const presigneds = presignedUrls.map((p, i) => ({
|
|
623
|
+
url: p.url,
|
|
624
|
+
key: p.key,
|
|
625
|
+
name: fileUploadRequests[i].name,
|
|
626
|
+
customId: fileUploadRequests[i].customId ?? null
|
|
627
|
+
}));
|
|
628
|
+
yield* effect_Effect.logInfo("Sending presigned URLs to client").pipe(effect_Effect.annotateLogs("presignedUrls", presigneds));
|
|
629
|
+
return {
|
|
630
|
+
body: presigneds,
|
|
631
|
+
fiber
|
|
632
|
+
};
|
|
633
|
+
}).pipe(effect_Effect.withLogSpan("handleUploadAction"));
|
|
634
|
+
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region src/_internal/upload-builder.ts
|
|
637
|
+
function internalCreateBuilder(initDef = {}) {
|
|
638
|
+
const _def = {
|
|
639
|
+
$types: {},
|
|
640
|
+
routerConfig: { image: { maxFileSize: "4MB" } },
|
|
641
|
+
routeOptions: { awaitServerData: true },
|
|
642
|
+
inputParser: {
|
|
643
|
+
parseAsync: () => Promise.resolve(void 0),
|
|
644
|
+
_input: void 0,
|
|
645
|
+
_output: void 0
|
|
646
|
+
},
|
|
647
|
+
middleware: () => ({}),
|
|
648
|
+
onUploadError: () => {},
|
|
649
|
+
onUploadComplete: () => void 0,
|
|
650
|
+
errorFormatter: initDef.errorFormatter ?? defaultErrorFormatter,
|
|
651
|
+
...initDef
|
|
652
|
+
};
|
|
653
|
+
return {
|
|
654
|
+
input(userParser) {
|
|
655
|
+
return internalCreateBuilder({
|
|
656
|
+
..._def,
|
|
657
|
+
inputParser: userParser
|
|
658
|
+
});
|
|
659
|
+
},
|
|
660
|
+
middleware(userMiddleware) {
|
|
661
|
+
return internalCreateBuilder({
|
|
662
|
+
..._def,
|
|
663
|
+
middleware: userMiddleware
|
|
664
|
+
});
|
|
665
|
+
},
|
|
666
|
+
onUploadComplete(userUploadComplete) {
|
|
667
|
+
return {
|
|
668
|
+
..._def,
|
|
669
|
+
onUploadComplete: userUploadComplete
|
|
670
|
+
};
|
|
671
|
+
},
|
|
672
|
+
onUploadError(userOnUploadError) {
|
|
673
|
+
return internalCreateBuilder({
|
|
674
|
+
..._def,
|
|
675
|
+
onUploadError: userOnUploadError
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Create a builder for your backend adapter.
|
|
682
|
+
* Refer to the existing adapters for examples on how to use this function.
|
|
683
|
+
* @public
|
|
684
|
+
*
|
|
685
|
+
* @param opts - Options for the builder
|
|
686
|
+
* @returns A file route builder for making UploadThing file routes
|
|
687
|
+
*/
|
|
688
|
+
function createBuilder(opts) {
|
|
689
|
+
return (input, config) => {
|
|
690
|
+
return internalCreateBuilder({
|
|
691
|
+
routerConfig: input,
|
|
692
|
+
routeOptions: config ?? {},
|
|
693
|
+
...opts
|
|
694
|
+
});
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
//#endregion
|
|
699
|
+
Object.defineProperty(exports, 'AdapterArguments', {
|
|
700
|
+
enumerable: true,
|
|
701
|
+
get: function () {
|
|
702
|
+
return AdapterArguments;
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
Object.defineProperty(exports, 'ApiUrl', {
|
|
706
|
+
enumerable: true,
|
|
707
|
+
get: function () {
|
|
708
|
+
return ApiUrl;
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
Object.defineProperty(exports, 'IngestUrl', {
|
|
712
|
+
enumerable: true,
|
|
713
|
+
get: function () {
|
|
714
|
+
return IngestUrl;
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
Object.defineProperty(exports, 'UTFiles', {
|
|
718
|
+
enumerable: true,
|
|
719
|
+
get: function () {
|
|
720
|
+
return UTFiles;
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
Object.defineProperty(exports, 'UTRegion', {
|
|
724
|
+
enumerable: true,
|
|
725
|
+
get: function () {
|
|
726
|
+
return UTRegion;
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
Object.defineProperty(exports, 'UTToken', {
|
|
730
|
+
enumerable: true,
|
|
731
|
+
get: function () {
|
|
732
|
+
return UTToken;
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
Object.defineProperty(exports, 'UfsAppIdLocation', {
|
|
736
|
+
enumerable: true,
|
|
737
|
+
get: function () {
|
|
738
|
+
return UfsAppIdLocation;
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
Object.defineProperty(exports, 'UfsHost', {
|
|
742
|
+
enumerable: true,
|
|
743
|
+
get: function () {
|
|
744
|
+
return UfsHost;
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
Object.defineProperty(exports, 'configProvider', {
|
|
748
|
+
enumerable: true,
|
|
749
|
+
get: function () {
|
|
750
|
+
return configProvider;
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
Object.defineProperty(exports, 'createBuilder', {
|
|
754
|
+
enumerable: true,
|
|
755
|
+
get: function () {
|
|
756
|
+
return createBuilder;
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
Object.defineProperty(exports, 'createRequestHandler', {
|
|
760
|
+
enumerable: true,
|
|
761
|
+
get: function () {
|
|
762
|
+
return createRequestHandler;
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
Object.defineProperty(exports, 'extractRouterConfig', {
|
|
766
|
+
enumerable: true,
|
|
767
|
+
get: function () {
|
|
768
|
+
return extractRouterConfig;
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
Object.defineProperty(exports, 'logHttpClientError', {
|
|
772
|
+
enumerable: true,
|
|
773
|
+
get: function () {
|
|
774
|
+
return logHttpClientError;
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
Object.defineProperty(exports, 'logHttpClientResponse', {
|
|
778
|
+
enumerable: true,
|
|
779
|
+
get: function () {
|
|
780
|
+
return logHttpClientResponse;
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
Object.defineProperty(exports, 'makeAdapterHandler', {
|
|
784
|
+
enumerable: true,
|
|
785
|
+
get: function () {
|
|
786
|
+
return makeAdapterHandler;
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
Object.defineProperty(exports, 'makeRuntime', {
|
|
790
|
+
enumerable: true,
|
|
791
|
+
get: function () {
|
|
792
|
+
return makeRuntime;
|
|
793
|
+
}
|
|
794
|
+
});
|