s3kit 0.1.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/README.md +398 -0
- package/dist/adapters/express.cjs +305 -0
- package/dist/adapters/express.cjs.map +1 -0
- package/dist/adapters/express.d.cts +10 -0
- package/dist/adapters/express.d.ts +10 -0
- package/dist/adapters/express.js +278 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/fetch.cjs +298 -0
- package/dist/adapters/fetch.cjs.map +1 -0
- package/dist/adapters/fetch.d.cts +9 -0
- package/dist/adapters/fetch.d.ts +9 -0
- package/dist/adapters/fetch.js +271 -0
- package/dist/adapters/fetch.js.map +1 -0
- package/dist/adapters/next.cjs +796 -0
- package/dist/adapters/next.cjs.map +1 -0
- package/dist/adapters/next.d.cts +28 -0
- package/dist/adapters/next.d.ts +28 -0
- package/dist/adapters/next.js +775 -0
- package/dist/adapters/next.js.map +1 -0
- package/dist/client/index.cjs +153 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +59 -0
- package/dist/client/index.d.ts +59 -0
- package/dist/client/index.js +126 -0
- package/dist/client/index.js.map +1 -0
- package/dist/core/index.cjs +452 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +11 -0
- package/dist/core/index.d.ts +11 -0
- package/dist/core/index.js +430 -0
- package/dist/core/index.js.map +1 -0
- package/dist/http/index.cjs +270 -0
- package/dist/http/index.cjs.map +1 -0
- package/dist/http/index.d.cts +49 -0
- package/dist/http/index.d.ts +49 -0
- package/dist/http/index.js +243 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index.cjs +808 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +784 -0
- package/dist/index.js.map +1 -0
- package/dist/manager-BbmXpgXN.d.ts +29 -0
- package/dist/manager-gIjo-t8h.d.cts +29 -0
- package/dist/react/index.cjs +4320 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.css +155 -0
- package/dist/react/index.css.map +1 -0
- package/dist/react/index.d.cts +79 -0
- package/dist/react/index.d.ts +79 -0
- package/dist/react/index.js +4315 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-g2IYvH3O.d.cts +123 -0
- package/dist/types-g2IYvH3O.d.ts +123 -0
- package/package.json +100 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/express.ts","../../src/core/errors.ts","../../src/http/handler.ts"],"sourcesContent":["import type { Request, Response } from 'express'\n\nimport type { HttpRequest } from '../http/types'\nimport { createS3FileManagerHttpHandler } from '../http/handler'\n\nexport type ExpressS3FileManagerHandlerOptions<FileExtra, FolderExtra> = Parameters<\n typeof createS3FileManagerHttpHandler<FileExtra, FolderExtra>\n>[0]\n\nfunction toSingleHeaderValue(val: string | string[] | undefined): string | undefined {\n if (typeof val === 'string') return val\n if (Array.isArray(val)) return val.join(',')\n return undefined\n}\n\nexport function createExpressS3FileManagerHandler<FileExtra = unknown, FolderExtra = unknown>(\n options: ExpressS3FileManagerHandlerOptions<FileExtra, FolderExtra>,\n): (req: Request, res: Response) => Promise<void> {\n const handler = createS3FileManagerHttpHandler(options)\n\n return async (req: Request, res: Response) => {\n const httpReq: HttpRequest = {\n method: req.method,\n path: `${req.baseUrl ?? ''}${req.path}`,\n query: req.query as any,\n headers: req.headers as any,\n body: req.body,\n }\n\n const out = await handler(httpReq)\n if (out.headers) {\n for (const [k, v] of Object.entries(out.headers)) {\n res.setHeader(k, v)\n }\n }\n\n if (out.status === 204) {\n res.status(204).end()\n return\n }\n\n const ct = toSingleHeaderValue((out.headers ?? {})['content-type'])\n if (ct?.includes('application/json')) {\n res.status(out.status).json(out.body ?? null)\n return\n }\n\n res.status(out.status).send(out.body ?? null)\n }\n}\n","export class S3FileManagerAuthorizationError extends Error {\n readonly status: number\n readonly code: string\n\n constructor(message: string, status: number, code: string) {\n super(message)\n this.status = status\n this.code = code\n }\n}\n","import type { S3FileManager } from '../core/manager'\nimport { S3FileManagerAuthorizationError } from '../core/errors'\nimport type { S3FileManagerAuthContext } from '../core/types'\nimport type { HttpRequest, HttpResponse, S3FileManagerApiOptions } from './types'\n\nclass S3FileManagerHttpError extends Error {\n readonly status: number\n readonly code: string\n\n constructor(status: number, code: string, message: string) {\n super(message)\n this.status = status\n this.code = code\n }\n}\n\nfunction normalizeBasePath(basePath?: string): string {\n if (!basePath) return ''\n if (basePath === '/') return ''\n return basePath.startsWith('/')\n ? basePath.replace(/\\/+$/, '')\n : `/${basePath.replace(/\\/+$/, '')}`\n}\n\nfunction jsonError(status: number, code: string, message: string): HttpResponse {\n return {\n status,\n headers: { 'content-type': 'application/json' },\n body: {\n error: {\n code,\n message,\n },\n },\n }\n}\n\nfunction ensureObject(body: unknown): Record<string, unknown> {\n if (body && typeof body === 'object' && !Array.isArray(body))\n return body as Record<string, unknown>\n throw new S3FileManagerHttpError(400, 'invalid_body', 'Expected JSON object body')\n}\n\nfunction optionalString(value: unknown, key: string): string | undefined {\n if (value === undefined) return undefined\n if (typeof value === 'string') return value\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}' to be a string`)\n}\n\nfunction requiredString(value: unknown, key: string): string {\n if (typeof value === 'string') return value\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}' to be a string`)\n}\n\nfunction optionalNumber(value: unknown, key: string): number | undefined {\n if (value === undefined) return undefined\n if (typeof value === 'number' && Number.isFinite(value)) return value\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}' to be a finite number`)\n}\n\nfunction optionalBoolean(value: unknown, key: string): boolean | undefined {\n if (value === undefined) return undefined\n if (typeof value === 'boolean') return value\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}' to be a boolean`)\n}\n\nfunction requiredStringArray(value: unknown, key: string): string[] {\n if (!Array.isArray(value)) {\n throw new S3FileManagerHttpError(\n 400,\n 'invalid_body',\n `Expected '${key}' to be an array of strings`,\n )\n }\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new S3FileManagerHttpError(\n 400,\n 'invalid_body',\n `Expected '${key}' to be an array of strings`,\n )\n }\n }\n return value\n}\n\nfunction optionalStringRecord(value: unknown, key: string): Record<string, string> | undefined {\n if (value === undefined) return undefined\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new S3FileManagerHttpError(\n 400,\n 'invalid_body',\n `Expected '${key}' to be an object of strings`,\n )\n }\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(value)) {\n if (typeof v !== 'string') {\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}.${k}' to be a string`)\n }\n out[k] = v\n }\n return out\n}\n\nfunction parseListOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n path: requiredString(obj.path, 'path'),\n cursor: optionalString(obj.cursor, 'cursor'),\n limit: optionalNumber(obj.limit, 'limit'),\n }\n}\n\nfunction parseSearchOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n query: requiredString(obj.query, 'query'),\n path: optionalString(obj.path, 'path'),\n recursive: optionalBoolean(obj.recursive, 'recursive'),\n limit: optionalNumber(obj.limit, 'limit'),\n cursor: optionalString(obj.cursor, 'cursor'),\n }\n}\n\nfunction parseCreateFolderOptions(body: unknown) {\n const obj = ensureObject(body)\n return { path: requiredString(obj.path, 'path') }\n}\n\nfunction parseDeleteFolderOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n path: requiredString(obj.path, 'path'),\n recursive: optionalBoolean(obj.recursive, 'recursive'),\n }\n}\n\nfunction parseDeleteFilesOptions(body: unknown) {\n const obj = ensureObject(body)\n return { paths: requiredStringArray(obj.paths, 'paths') }\n}\n\nfunction parseCopyMoveOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n fromPath: requiredString(obj.fromPath, 'fromPath'),\n toPath: requiredString(obj.toPath, 'toPath'),\n }\n}\n\nfunction parsePrepareUploadsOptions(body: unknown) {\n const obj = ensureObject(body)\n const itemsValue = obj.items\n if (!Array.isArray(itemsValue)) {\n throw new S3FileManagerHttpError(400, 'invalid_body', \"Expected 'items' to be an array\")\n }\n\n const items = itemsValue.map((raw, idx) => {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new S3FileManagerHttpError(\n 400,\n 'invalid_body',\n `Expected 'items[${idx}]' to be an object`,\n )\n }\n const item = raw as Record<string, unknown>\n return {\n path: requiredString(item.path, `items[${idx}].path`),\n contentType: optionalString(item.contentType, `items[${idx}].contentType`),\n cacheControl: optionalString(item.cacheControl, `items[${idx}].cacheControl`),\n contentDisposition: optionalString(\n item.contentDisposition,\n `items[${idx}].contentDisposition`,\n ),\n metadata: optionalStringRecord(item.metadata, `items[${idx}].metadata`),\n }\n })\n\n return {\n items,\n expiresInSeconds: optionalNumber(obj.expiresInSeconds, 'expiresInSeconds'),\n }\n}\n\nfunction parsePreviewOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n path: requiredString(obj.path, 'path'),\n expiresInSeconds: optionalNumber(obj.expiresInSeconds, 'expiresInSeconds'),\n inline: optionalBoolean(obj.inline, 'inline'),\n }\n}\n\nexport interface CreateS3FileManagerHttpHandlerOptions<FileExtra, FolderExtra> {\n manager?: S3FileManager<FileExtra, FolderExtra>\n getManager?: (\n req: HttpRequest,\n ctx: S3FileManagerAuthContext,\n ) => S3FileManager<FileExtra, FolderExtra> | Promise<S3FileManager<FileExtra, FolderExtra>>\n getContext?: (req: HttpRequest) => Promise<S3FileManagerAuthContext> | S3FileManagerAuthContext\n api?: S3FileManagerApiOptions\n}\n\nexport function createS3FileManagerHttpHandler<FileExtra = unknown, FolderExtra = unknown>(\n options: CreateS3FileManagerHttpHandlerOptions<FileExtra, FolderExtra>,\n): (req: HttpRequest) => Promise<HttpResponse> {\n const basePath = normalizeBasePath(options.api?.basePath)\n\n if (!options.manager && !options.getManager) {\n throw new Error('createS3FileManagerHttpHandler requires either manager or getManager')\n }\n\n return async (req: HttpRequest): Promise<HttpResponse> => {\n try {\n const ctx = (await options.getContext?.(req)) ?? {}\n const manager = options.getManager ? await options.getManager(req, ctx) : options.manager!\n const method = req.method.toUpperCase()\n const path = req.path.startsWith(basePath) ? req.path.slice(basePath.length) || '/' : req.path\n\n if (method === 'POST' && path === '/list') {\n const out = await manager.list(parseListOptions(req.body) as any, ctx)\n return { status: 200, headers: { 'content-type': 'application/json' }, body: out as any }\n }\n\n if (method === 'POST' && path === '/search') {\n const out = await manager.search(parseSearchOptions(req.body) as any, ctx)\n return { status: 200, headers: { 'content-type': 'application/json' }, body: out as any }\n }\n\n if (method === 'POST' && path === '/folder/create') {\n await manager.createFolder(parseCreateFolderOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/folder/delete') {\n await manager.deleteFolder(parseDeleteFolderOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/files/delete') {\n await manager.deleteFiles(parseDeleteFilesOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/files/copy') {\n await manager.copy(parseCopyMoveOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/files/move') {\n await manager.move(parseCopyMoveOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/upload/prepare') {\n const out = await manager.prepareUploads(parsePrepareUploadsOptions(req.body) as any, ctx)\n return { status: 200, headers: { 'content-type': 'application/json' }, body: out as any }\n }\n\n if (method === 'POST' && path === '/preview') {\n const out = await manager.getPreviewUrl(parsePreviewOptions(req.body) as any, ctx)\n return { status: 200, headers: { 'content-type': 'application/json' }, body: out as any }\n }\n\n return jsonError(404, 'not_found', 'Route not found')\n } catch (err) {\n if (err instanceof S3FileManagerHttpError) {\n return jsonError(err.status, err.code, err.message)\n }\n if (err instanceof S3FileManagerAuthorizationError) {\n return jsonError(err.status, err.code, err.message)\n }\n\n console.error('[S3FileManager Error]', err)\n const message = err instanceof Error ? err.message : 'Unknown error'\n return jsonError(500, 'internal_error', message)\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import { createS3FileManagerHttpHandler } from '../http/index.cjs';
|
|
3
|
+
import '../types-g2IYvH3O.cjs';
|
|
4
|
+
import '../manager-gIjo-t8h.cjs';
|
|
5
|
+
import '@aws-sdk/client-s3';
|
|
6
|
+
|
|
7
|
+
type ExpressS3FileManagerHandlerOptions<FileExtra, FolderExtra> = Parameters<typeof createS3FileManagerHttpHandler<FileExtra, FolderExtra>>[0];
|
|
8
|
+
declare function createExpressS3FileManagerHandler<FileExtra = unknown, FolderExtra = unknown>(options: ExpressS3FileManagerHandlerOptions<FileExtra, FolderExtra>): (req: Request, res: Response) => Promise<void>;
|
|
9
|
+
|
|
10
|
+
export { type ExpressS3FileManagerHandlerOptions, createExpressS3FileManagerHandler };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import { createS3FileManagerHttpHandler } from '../http/index.js';
|
|
3
|
+
import '../types-g2IYvH3O.js';
|
|
4
|
+
import '../manager-BbmXpgXN.js';
|
|
5
|
+
import '@aws-sdk/client-s3';
|
|
6
|
+
|
|
7
|
+
type ExpressS3FileManagerHandlerOptions<FileExtra, FolderExtra> = Parameters<typeof createS3FileManagerHttpHandler<FileExtra, FolderExtra>>[0];
|
|
8
|
+
declare function createExpressS3FileManagerHandler<FileExtra = unknown, FolderExtra = unknown>(options: ExpressS3FileManagerHandlerOptions<FileExtra, FolderExtra>): (req: Request, res: Response) => Promise<void>;
|
|
9
|
+
|
|
10
|
+
export { type ExpressS3FileManagerHandlerOptions, createExpressS3FileManagerHandler };
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// src/core/errors.ts
|
|
2
|
+
var S3FileManagerAuthorizationError = class extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
constructor(message, status, code) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.code = code;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/http/handler.ts
|
|
13
|
+
var S3FileManagerHttpError = class extends Error {
|
|
14
|
+
status;
|
|
15
|
+
code;
|
|
16
|
+
constructor(status, code, message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.status = status;
|
|
19
|
+
this.code = code;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function normalizeBasePath(basePath) {
|
|
23
|
+
if (!basePath) return "";
|
|
24
|
+
if (basePath === "/") return "";
|
|
25
|
+
return basePath.startsWith("/") ? basePath.replace(/\/+$/, "") : `/${basePath.replace(/\/+$/, "")}`;
|
|
26
|
+
}
|
|
27
|
+
function jsonError(status, code, message) {
|
|
28
|
+
return {
|
|
29
|
+
status,
|
|
30
|
+
headers: { "content-type": "application/json" },
|
|
31
|
+
body: {
|
|
32
|
+
error: {
|
|
33
|
+
code,
|
|
34
|
+
message
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function ensureObject(body) {
|
|
40
|
+
if (body && typeof body === "object" && !Array.isArray(body))
|
|
41
|
+
return body;
|
|
42
|
+
throw new S3FileManagerHttpError(400, "invalid_body", "Expected JSON object body");
|
|
43
|
+
}
|
|
44
|
+
function optionalString(value, key) {
|
|
45
|
+
if (value === void 0) return void 0;
|
|
46
|
+
if (typeof value === "string") return value;
|
|
47
|
+
throw new S3FileManagerHttpError(400, "invalid_body", `Expected '${key}' to be a string`);
|
|
48
|
+
}
|
|
49
|
+
function requiredString(value, key) {
|
|
50
|
+
if (typeof value === "string") return value;
|
|
51
|
+
throw new S3FileManagerHttpError(400, "invalid_body", `Expected '${key}' to be a string`);
|
|
52
|
+
}
|
|
53
|
+
function optionalNumber(value, key) {
|
|
54
|
+
if (value === void 0) return void 0;
|
|
55
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
56
|
+
throw new S3FileManagerHttpError(400, "invalid_body", `Expected '${key}' to be a finite number`);
|
|
57
|
+
}
|
|
58
|
+
function optionalBoolean(value, key) {
|
|
59
|
+
if (value === void 0) return void 0;
|
|
60
|
+
if (typeof value === "boolean") return value;
|
|
61
|
+
throw new S3FileManagerHttpError(400, "invalid_body", `Expected '${key}' to be a boolean`);
|
|
62
|
+
}
|
|
63
|
+
function requiredStringArray(value, key) {
|
|
64
|
+
if (!Array.isArray(value)) {
|
|
65
|
+
throw new S3FileManagerHttpError(
|
|
66
|
+
400,
|
|
67
|
+
"invalid_body",
|
|
68
|
+
`Expected '${key}' to be an array of strings`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
for (const item of value) {
|
|
72
|
+
if (typeof item !== "string") {
|
|
73
|
+
throw new S3FileManagerHttpError(
|
|
74
|
+
400,
|
|
75
|
+
"invalid_body",
|
|
76
|
+
`Expected '${key}' to be an array of strings`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
function optionalStringRecord(value, key) {
|
|
83
|
+
if (value === void 0) return void 0;
|
|
84
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
85
|
+
throw new S3FileManagerHttpError(
|
|
86
|
+
400,
|
|
87
|
+
"invalid_body",
|
|
88
|
+
`Expected '${key}' to be an object of strings`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const out = {};
|
|
92
|
+
for (const [k, v] of Object.entries(value)) {
|
|
93
|
+
if (typeof v !== "string") {
|
|
94
|
+
throw new S3FileManagerHttpError(400, "invalid_body", `Expected '${key}.${k}' to be a string`);
|
|
95
|
+
}
|
|
96
|
+
out[k] = v;
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
function parseListOptions(body) {
|
|
101
|
+
const obj = ensureObject(body);
|
|
102
|
+
return {
|
|
103
|
+
path: requiredString(obj.path, "path"),
|
|
104
|
+
cursor: optionalString(obj.cursor, "cursor"),
|
|
105
|
+
limit: optionalNumber(obj.limit, "limit")
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function parseSearchOptions(body) {
|
|
109
|
+
const obj = ensureObject(body);
|
|
110
|
+
return {
|
|
111
|
+
query: requiredString(obj.query, "query"),
|
|
112
|
+
path: optionalString(obj.path, "path"),
|
|
113
|
+
recursive: optionalBoolean(obj.recursive, "recursive"),
|
|
114
|
+
limit: optionalNumber(obj.limit, "limit"),
|
|
115
|
+
cursor: optionalString(obj.cursor, "cursor")
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function parseCreateFolderOptions(body) {
|
|
119
|
+
const obj = ensureObject(body);
|
|
120
|
+
return { path: requiredString(obj.path, "path") };
|
|
121
|
+
}
|
|
122
|
+
function parseDeleteFolderOptions(body) {
|
|
123
|
+
const obj = ensureObject(body);
|
|
124
|
+
return {
|
|
125
|
+
path: requiredString(obj.path, "path"),
|
|
126
|
+
recursive: optionalBoolean(obj.recursive, "recursive")
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function parseDeleteFilesOptions(body) {
|
|
130
|
+
const obj = ensureObject(body);
|
|
131
|
+
return { paths: requiredStringArray(obj.paths, "paths") };
|
|
132
|
+
}
|
|
133
|
+
function parseCopyMoveOptions(body) {
|
|
134
|
+
const obj = ensureObject(body);
|
|
135
|
+
return {
|
|
136
|
+
fromPath: requiredString(obj.fromPath, "fromPath"),
|
|
137
|
+
toPath: requiredString(obj.toPath, "toPath")
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function parsePrepareUploadsOptions(body) {
|
|
141
|
+
const obj = ensureObject(body);
|
|
142
|
+
const itemsValue = obj.items;
|
|
143
|
+
if (!Array.isArray(itemsValue)) {
|
|
144
|
+
throw new S3FileManagerHttpError(400, "invalid_body", "Expected 'items' to be an array");
|
|
145
|
+
}
|
|
146
|
+
const items = itemsValue.map((raw, idx) => {
|
|
147
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
148
|
+
throw new S3FileManagerHttpError(
|
|
149
|
+
400,
|
|
150
|
+
"invalid_body",
|
|
151
|
+
`Expected 'items[${idx}]' to be an object`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const item = raw;
|
|
155
|
+
return {
|
|
156
|
+
path: requiredString(item.path, `items[${idx}].path`),
|
|
157
|
+
contentType: optionalString(item.contentType, `items[${idx}].contentType`),
|
|
158
|
+
cacheControl: optionalString(item.cacheControl, `items[${idx}].cacheControl`),
|
|
159
|
+
contentDisposition: optionalString(
|
|
160
|
+
item.contentDisposition,
|
|
161
|
+
`items[${idx}].contentDisposition`
|
|
162
|
+
),
|
|
163
|
+
metadata: optionalStringRecord(item.metadata, `items[${idx}].metadata`)
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
items,
|
|
168
|
+
expiresInSeconds: optionalNumber(obj.expiresInSeconds, "expiresInSeconds")
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function parsePreviewOptions(body) {
|
|
172
|
+
const obj = ensureObject(body);
|
|
173
|
+
return {
|
|
174
|
+
path: requiredString(obj.path, "path"),
|
|
175
|
+
expiresInSeconds: optionalNumber(obj.expiresInSeconds, "expiresInSeconds"),
|
|
176
|
+
inline: optionalBoolean(obj.inline, "inline")
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function createS3FileManagerHttpHandler(options) {
|
|
180
|
+
const basePath = normalizeBasePath(options.api?.basePath);
|
|
181
|
+
if (!options.manager && !options.getManager) {
|
|
182
|
+
throw new Error("createS3FileManagerHttpHandler requires either manager or getManager");
|
|
183
|
+
}
|
|
184
|
+
return async (req) => {
|
|
185
|
+
try {
|
|
186
|
+
const ctx = await options.getContext?.(req) ?? {};
|
|
187
|
+
const manager = options.getManager ? await options.getManager(req, ctx) : options.manager;
|
|
188
|
+
const method = req.method.toUpperCase();
|
|
189
|
+
const path = req.path.startsWith(basePath) ? req.path.slice(basePath.length) || "/" : req.path;
|
|
190
|
+
if (method === "POST" && path === "/list") {
|
|
191
|
+
const out = await manager.list(parseListOptions(req.body), ctx);
|
|
192
|
+
return { status: 200, headers: { "content-type": "application/json" }, body: out };
|
|
193
|
+
}
|
|
194
|
+
if (method === "POST" && path === "/search") {
|
|
195
|
+
const out = await manager.search(parseSearchOptions(req.body), ctx);
|
|
196
|
+
return { status: 200, headers: { "content-type": "application/json" }, body: out };
|
|
197
|
+
}
|
|
198
|
+
if (method === "POST" && path === "/folder/create") {
|
|
199
|
+
await manager.createFolder(parseCreateFolderOptions(req.body), ctx);
|
|
200
|
+
return { status: 204 };
|
|
201
|
+
}
|
|
202
|
+
if (method === "POST" && path === "/folder/delete") {
|
|
203
|
+
await manager.deleteFolder(parseDeleteFolderOptions(req.body), ctx);
|
|
204
|
+
return { status: 204 };
|
|
205
|
+
}
|
|
206
|
+
if (method === "POST" && path === "/files/delete") {
|
|
207
|
+
await manager.deleteFiles(parseDeleteFilesOptions(req.body), ctx);
|
|
208
|
+
return { status: 204 };
|
|
209
|
+
}
|
|
210
|
+
if (method === "POST" && path === "/files/copy") {
|
|
211
|
+
await manager.copy(parseCopyMoveOptions(req.body), ctx);
|
|
212
|
+
return { status: 204 };
|
|
213
|
+
}
|
|
214
|
+
if (method === "POST" && path === "/files/move") {
|
|
215
|
+
await manager.move(parseCopyMoveOptions(req.body), ctx);
|
|
216
|
+
return { status: 204 };
|
|
217
|
+
}
|
|
218
|
+
if (method === "POST" && path === "/upload/prepare") {
|
|
219
|
+
const out = await manager.prepareUploads(parsePrepareUploadsOptions(req.body), ctx);
|
|
220
|
+
return { status: 200, headers: { "content-type": "application/json" }, body: out };
|
|
221
|
+
}
|
|
222
|
+
if (method === "POST" && path === "/preview") {
|
|
223
|
+
const out = await manager.getPreviewUrl(parsePreviewOptions(req.body), ctx);
|
|
224
|
+
return { status: 200, headers: { "content-type": "application/json" }, body: out };
|
|
225
|
+
}
|
|
226
|
+
return jsonError(404, "not_found", "Route not found");
|
|
227
|
+
} catch (err) {
|
|
228
|
+
if (err instanceof S3FileManagerHttpError) {
|
|
229
|
+
return jsonError(err.status, err.code, err.message);
|
|
230
|
+
}
|
|
231
|
+
if (err instanceof S3FileManagerAuthorizationError) {
|
|
232
|
+
return jsonError(err.status, err.code, err.message);
|
|
233
|
+
}
|
|
234
|
+
console.error("[S3FileManager Error]", err);
|
|
235
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
236
|
+
return jsonError(500, "internal_error", message);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/adapters/express.ts
|
|
242
|
+
function toSingleHeaderValue(val) {
|
|
243
|
+
if (typeof val === "string") return val;
|
|
244
|
+
if (Array.isArray(val)) return val.join(",");
|
|
245
|
+
return void 0;
|
|
246
|
+
}
|
|
247
|
+
function createExpressS3FileManagerHandler(options) {
|
|
248
|
+
const handler = createS3FileManagerHttpHandler(options);
|
|
249
|
+
return async (req, res) => {
|
|
250
|
+
const httpReq = {
|
|
251
|
+
method: req.method,
|
|
252
|
+
path: `${req.baseUrl ?? ""}${req.path}`,
|
|
253
|
+
query: req.query,
|
|
254
|
+
headers: req.headers,
|
|
255
|
+
body: req.body
|
|
256
|
+
};
|
|
257
|
+
const out = await handler(httpReq);
|
|
258
|
+
if (out.headers) {
|
|
259
|
+
for (const [k, v] of Object.entries(out.headers)) {
|
|
260
|
+
res.setHeader(k, v);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (out.status === 204) {
|
|
264
|
+
res.status(204).end();
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const ct = toSingleHeaderValue((out.headers ?? {})["content-type"]);
|
|
268
|
+
if (ct?.includes("application/json")) {
|
|
269
|
+
res.status(out.status).json(out.body ?? null);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
res.status(out.status).send(out.body ?? null);
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
export {
|
|
276
|
+
createExpressS3FileManagerHandler
|
|
277
|
+
};
|
|
278
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/errors.ts","../../src/http/handler.ts","../../src/adapters/express.ts"],"sourcesContent":["export class S3FileManagerAuthorizationError extends Error {\n readonly status: number\n readonly code: string\n\n constructor(message: string, status: number, code: string) {\n super(message)\n this.status = status\n this.code = code\n }\n}\n","import type { S3FileManager } from '../core/manager'\nimport { S3FileManagerAuthorizationError } from '../core/errors'\nimport type { S3FileManagerAuthContext } from '../core/types'\nimport type { HttpRequest, HttpResponse, S3FileManagerApiOptions } from './types'\n\nclass S3FileManagerHttpError extends Error {\n readonly status: number\n readonly code: string\n\n constructor(status: number, code: string, message: string) {\n super(message)\n this.status = status\n this.code = code\n }\n}\n\nfunction normalizeBasePath(basePath?: string): string {\n if (!basePath) return ''\n if (basePath === '/') return ''\n return basePath.startsWith('/')\n ? basePath.replace(/\\/+$/, '')\n : `/${basePath.replace(/\\/+$/, '')}`\n}\n\nfunction jsonError(status: number, code: string, message: string): HttpResponse {\n return {\n status,\n headers: { 'content-type': 'application/json' },\n body: {\n error: {\n code,\n message,\n },\n },\n }\n}\n\nfunction ensureObject(body: unknown): Record<string, unknown> {\n if (body && typeof body === 'object' && !Array.isArray(body))\n return body as Record<string, unknown>\n throw new S3FileManagerHttpError(400, 'invalid_body', 'Expected JSON object body')\n}\n\nfunction optionalString(value: unknown, key: string): string | undefined {\n if (value === undefined) return undefined\n if (typeof value === 'string') return value\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}' to be a string`)\n}\n\nfunction requiredString(value: unknown, key: string): string {\n if (typeof value === 'string') return value\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}' to be a string`)\n}\n\nfunction optionalNumber(value: unknown, key: string): number | undefined {\n if (value === undefined) return undefined\n if (typeof value === 'number' && Number.isFinite(value)) return value\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}' to be a finite number`)\n}\n\nfunction optionalBoolean(value: unknown, key: string): boolean | undefined {\n if (value === undefined) return undefined\n if (typeof value === 'boolean') return value\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}' to be a boolean`)\n}\n\nfunction requiredStringArray(value: unknown, key: string): string[] {\n if (!Array.isArray(value)) {\n throw new S3FileManagerHttpError(\n 400,\n 'invalid_body',\n `Expected '${key}' to be an array of strings`,\n )\n }\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new S3FileManagerHttpError(\n 400,\n 'invalid_body',\n `Expected '${key}' to be an array of strings`,\n )\n }\n }\n return value\n}\n\nfunction optionalStringRecord(value: unknown, key: string): Record<string, string> | undefined {\n if (value === undefined) return undefined\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new S3FileManagerHttpError(\n 400,\n 'invalid_body',\n `Expected '${key}' to be an object of strings`,\n )\n }\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(value)) {\n if (typeof v !== 'string') {\n throw new S3FileManagerHttpError(400, 'invalid_body', `Expected '${key}.${k}' to be a string`)\n }\n out[k] = v\n }\n return out\n}\n\nfunction parseListOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n path: requiredString(obj.path, 'path'),\n cursor: optionalString(obj.cursor, 'cursor'),\n limit: optionalNumber(obj.limit, 'limit'),\n }\n}\n\nfunction parseSearchOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n query: requiredString(obj.query, 'query'),\n path: optionalString(obj.path, 'path'),\n recursive: optionalBoolean(obj.recursive, 'recursive'),\n limit: optionalNumber(obj.limit, 'limit'),\n cursor: optionalString(obj.cursor, 'cursor'),\n }\n}\n\nfunction parseCreateFolderOptions(body: unknown) {\n const obj = ensureObject(body)\n return { path: requiredString(obj.path, 'path') }\n}\n\nfunction parseDeleteFolderOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n path: requiredString(obj.path, 'path'),\n recursive: optionalBoolean(obj.recursive, 'recursive'),\n }\n}\n\nfunction parseDeleteFilesOptions(body: unknown) {\n const obj = ensureObject(body)\n return { paths: requiredStringArray(obj.paths, 'paths') }\n}\n\nfunction parseCopyMoveOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n fromPath: requiredString(obj.fromPath, 'fromPath'),\n toPath: requiredString(obj.toPath, 'toPath'),\n }\n}\n\nfunction parsePrepareUploadsOptions(body: unknown) {\n const obj = ensureObject(body)\n const itemsValue = obj.items\n if (!Array.isArray(itemsValue)) {\n throw new S3FileManagerHttpError(400, 'invalid_body', \"Expected 'items' to be an array\")\n }\n\n const items = itemsValue.map((raw, idx) => {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new S3FileManagerHttpError(\n 400,\n 'invalid_body',\n `Expected 'items[${idx}]' to be an object`,\n )\n }\n const item = raw as Record<string, unknown>\n return {\n path: requiredString(item.path, `items[${idx}].path`),\n contentType: optionalString(item.contentType, `items[${idx}].contentType`),\n cacheControl: optionalString(item.cacheControl, `items[${idx}].cacheControl`),\n contentDisposition: optionalString(\n item.contentDisposition,\n `items[${idx}].contentDisposition`,\n ),\n metadata: optionalStringRecord(item.metadata, `items[${idx}].metadata`),\n }\n })\n\n return {\n items,\n expiresInSeconds: optionalNumber(obj.expiresInSeconds, 'expiresInSeconds'),\n }\n}\n\nfunction parsePreviewOptions(body: unknown) {\n const obj = ensureObject(body)\n return {\n path: requiredString(obj.path, 'path'),\n expiresInSeconds: optionalNumber(obj.expiresInSeconds, 'expiresInSeconds'),\n inline: optionalBoolean(obj.inline, 'inline'),\n }\n}\n\nexport interface CreateS3FileManagerHttpHandlerOptions<FileExtra, FolderExtra> {\n manager?: S3FileManager<FileExtra, FolderExtra>\n getManager?: (\n req: HttpRequest,\n ctx: S3FileManagerAuthContext,\n ) => S3FileManager<FileExtra, FolderExtra> | Promise<S3FileManager<FileExtra, FolderExtra>>\n getContext?: (req: HttpRequest) => Promise<S3FileManagerAuthContext> | S3FileManagerAuthContext\n api?: S3FileManagerApiOptions\n}\n\nexport function createS3FileManagerHttpHandler<FileExtra = unknown, FolderExtra = unknown>(\n options: CreateS3FileManagerHttpHandlerOptions<FileExtra, FolderExtra>,\n): (req: HttpRequest) => Promise<HttpResponse> {\n const basePath = normalizeBasePath(options.api?.basePath)\n\n if (!options.manager && !options.getManager) {\n throw new Error('createS3FileManagerHttpHandler requires either manager or getManager')\n }\n\n return async (req: HttpRequest): Promise<HttpResponse> => {\n try {\n const ctx = (await options.getContext?.(req)) ?? {}\n const manager = options.getManager ? await options.getManager(req, ctx) : options.manager!\n const method = req.method.toUpperCase()\n const path = req.path.startsWith(basePath) ? req.path.slice(basePath.length) || '/' : req.path\n\n if (method === 'POST' && path === '/list') {\n const out = await manager.list(parseListOptions(req.body) as any, ctx)\n return { status: 200, headers: { 'content-type': 'application/json' }, body: out as any }\n }\n\n if (method === 'POST' && path === '/search') {\n const out = await manager.search(parseSearchOptions(req.body) as any, ctx)\n return { status: 200, headers: { 'content-type': 'application/json' }, body: out as any }\n }\n\n if (method === 'POST' && path === '/folder/create') {\n await manager.createFolder(parseCreateFolderOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/folder/delete') {\n await manager.deleteFolder(parseDeleteFolderOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/files/delete') {\n await manager.deleteFiles(parseDeleteFilesOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/files/copy') {\n await manager.copy(parseCopyMoveOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/files/move') {\n await manager.move(parseCopyMoveOptions(req.body) as any, ctx)\n return { status: 204 }\n }\n\n if (method === 'POST' && path === '/upload/prepare') {\n const out = await manager.prepareUploads(parsePrepareUploadsOptions(req.body) as any, ctx)\n return { status: 200, headers: { 'content-type': 'application/json' }, body: out as any }\n }\n\n if (method === 'POST' && path === '/preview') {\n const out = await manager.getPreviewUrl(parsePreviewOptions(req.body) as any, ctx)\n return { status: 200, headers: { 'content-type': 'application/json' }, body: out as any }\n }\n\n return jsonError(404, 'not_found', 'Route not found')\n } catch (err) {\n if (err instanceof S3FileManagerHttpError) {\n return jsonError(err.status, err.code, err.message)\n }\n if (err instanceof S3FileManagerAuthorizationError) {\n return jsonError(err.status, err.code, err.message)\n }\n\n console.error('[S3FileManager Error]', err)\n const message = err instanceof Error ? err.message : 'Unknown error'\n return jsonError(500, 'internal_error', message)\n }\n }\n}\n","import type { Request, Response } from 'express'\n\nimport type { HttpRequest } from '../http/types'\nimport { createS3FileManagerHttpHandler } from '../http/handler'\n\nexport type ExpressS3FileManagerHandlerOptions<FileExtra, FolderExtra> = Parameters<\n typeof createS3FileManagerHttpHandler<FileExtra, FolderExtra>\n>[0]\n\nfunction toSingleHeaderValue(val: string | string[] | undefined): string | undefined {\n if (typeof val === 'string') return val\n if (Array.isArray(val)) return val.join(',')\n return undefined\n}\n\nexport function createExpressS3FileManagerHandler<FileExtra = unknown, FolderExtra = unknown>(\n options: ExpressS3FileManagerHandlerOptions<FileExtra, FolderExtra>,\n): (req: Request, res: Response) => Promise<void> {\n const handler = createS3FileManagerHttpHandler(options)\n\n return async (req: Request, res: Response) => {\n const httpReq: HttpRequest = {\n method: req.method,\n path: `${req.baseUrl ?? ''}${req.path}`,\n query: req.query as any,\n headers: req.headers as any,\n body: req.body,\n }\n\n const out = await handler(httpReq)\n if (out.headers) {\n for (const [k, v] of Object.entries(out.headers)) {\n res.setHeader(k, v)\n }\n }\n\n if (out.status === 204) {\n res.status(204).end()\n return\n }\n\n const ct = toSingleHeaderValue((out.headers ?? {})['content-type'])\n if (ct?.includes('application/json')) {\n res.status(out.status).json(out.body ?? null)\n return\n }\n\n res.status(out.status).send(out.body ?? null)\n }\n}\n"],"mappings":"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","names":[]}
|