@edkstack/files 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 +15 -0
- package/dist/backend/index.d.ts +17 -0
- package/dist/backend/index.js +204 -0
- package/dist/client/index.d.ts +28 -0
- package/dist/client/index.js +41 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @bunstack/core
|
|
2
|
+
|
|
3
|
+
To install dependencies:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun install
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
To run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun run src/index.ts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This project was created using `bun init` in bun v1.3.8. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BunSQLDatabase as BunSQLDatabase2 } from "drizzle-orm/bun-sql";
|
|
2
|
+
type Visibility = "private" | "public";
|
|
3
|
+
interface PurposePolicy {
|
|
4
|
+
maxSize?: number;
|
|
5
|
+
allowedMimeTypes?: string[];
|
|
6
|
+
visibility?: Visibility;
|
|
7
|
+
}
|
|
8
|
+
import { S3Client as S3Client2 } from "bun";
|
|
9
|
+
interface FilesBackendOptions<TPurposes extends string> {
|
|
10
|
+
db: BunSQLDatabase2;
|
|
11
|
+
client: S3Client2;
|
|
12
|
+
policies: Record<TPurposes, PurposePolicy>;
|
|
13
|
+
publicBaseUrl: string;
|
|
14
|
+
presignExpiresIn?: number;
|
|
15
|
+
}
|
|
16
|
+
declare function createFilesBackend<const TPurpose extends string>(options: FilesBackendOptions<TPurpose>): {};
|
|
17
|
+
export { createFilesBackend, Visibility, PurposePolicy, FilesBackendOptions };
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// src/backend/routes.ts
|
|
2
|
+
import { Elysia, t } from "elysia";
|
|
3
|
+
function createRoutes(options) {
|
|
4
|
+
const {
|
|
5
|
+
services,
|
|
6
|
+
policies
|
|
7
|
+
} = options;
|
|
8
|
+
return new Elysia({
|
|
9
|
+
prefix: "/api"
|
|
10
|
+
}).post("/files/upload", async ({ status, body }) => {
|
|
11
|
+
const { file, purpose } = body;
|
|
12
|
+
const policy = policies[purpose];
|
|
13
|
+
if (!policy) {
|
|
14
|
+
return status(400, {
|
|
15
|
+
message: "Purpose not supported"
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
if (policy.maxSize !== undefined && file.size > policy.maxSize) {
|
|
19
|
+
return status(400, {
|
|
20
|
+
message: "File size exceeds the maximum allowed size"
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
if (policy.allowedMimeTypes !== undefined && !policy.allowedMimeTypes.includes(file.type)) {
|
|
24
|
+
return status(400, {
|
|
25
|
+
message: "File type not allowed"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const uploaded = await services.uploadFile({
|
|
29
|
+
file,
|
|
30
|
+
purpose,
|
|
31
|
+
visibility: policy.visibility ?? "private"
|
|
32
|
+
});
|
|
33
|
+
return status(200, {
|
|
34
|
+
id: uploaded.id,
|
|
35
|
+
name: uploaded.name,
|
|
36
|
+
key: uploaded.key,
|
|
37
|
+
size: uploaded.size,
|
|
38
|
+
mimeType: uploaded.mimeType,
|
|
39
|
+
createdAt: uploaded.createdAt
|
|
40
|
+
});
|
|
41
|
+
}, {
|
|
42
|
+
body: UploadRequest(Object.keys(policies)),
|
|
43
|
+
response: {
|
|
44
|
+
200: FileResponse,
|
|
45
|
+
400: ErrorResponse,
|
|
46
|
+
500: ErrorResponse
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function UploadRequest(purposes) {
|
|
51
|
+
return t.Object({
|
|
52
|
+
file: t.File(),
|
|
53
|
+
purpose: t.Union(purposes.map((p) => t.Literal(p)))
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
var ErrorResponse = t.Object({
|
|
57
|
+
message: t.String()
|
|
58
|
+
});
|
|
59
|
+
var FileResponse = t.Object({
|
|
60
|
+
id: t.String(),
|
|
61
|
+
name: t.Nullable(t.String()),
|
|
62
|
+
key: t.String(),
|
|
63
|
+
size: t.Number(),
|
|
64
|
+
mimeType: t.String(),
|
|
65
|
+
createdAt: t.Date()
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// src/backend/schemas.ts
|
|
69
|
+
import { nanoid } from "nanoid";
|
|
70
|
+
import { index, integer, pgTable, text, timestamp, pgEnum } from "drizzle-orm/pg-core";
|
|
71
|
+
function createSchemas() {
|
|
72
|
+
const files = pgTable("files", {
|
|
73
|
+
id: text("id").primaryKey().$defaultFn(() => `file_${nanoid()}`),
|
|
74
|
+
purpose: text("purpose").notNull(),
|
|
75
|
+
name: text("name"),
|
|
76
|
+
key: text("key").notNull().unique(),
|
|
77
|
+
size: integer("size").notNull(),
|
|
78
|
+
mimeType: text("mime_type").notNull().default("application/octet-stream"),
|
|
79
|
+
refCount: integer("ref_count").notNull().default(0),
|
|
80
|
+
visibility: pgEnum("visibility", ["private", "public"])().notNull().default("private"),
|
|
81
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
82
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow()
|
|
83
|
+
}, (table) => [
|
|
84
|
+
index("files_key_idx").on(table.key),
|
|
85
|
+
index("files_created_at_idx").on(table.createdAt)
|
|
86
|
+
]);
|
|
87
|
+
return {
|
|
88
|
+
files
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/backend/services.ts
|
|
93
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
94
|
+
import { extname } from "path";
|
|
95
|
+
import { eq, sql, and } from "drizzle-orm";
|
|
96
|
+
function createServices(options) {
|
|
97
|
+
const {
|
|
98
|
+
db,
|
|
99
|
+
schemas,
|
|
100
|
+
s3Client,
|
|
101
|
+
publicBaseUrl,
|
|
102
|
+
keyPrefix = "files",
|
|
103
|
+
presignExpiresIn = 3600
|
|
104
|
+
} = options;
|
|
105
|
+
return {
|
|
106
|
+
s3Client,
|
|
107
|
+
async getFile(params) {
|
|
108
|
+
const [found] = await db.select().from(schemas.files).where(eq(schemas.files.id, params.id)).limit(1);
|
|
109
|
+
if (!found) {
|
|
110
|
+
throw new Error("File not found");
|
|
111
|
+
}
|
|
112
|
+
return found;
|
|
113
|
+
},
|
|
114
|
+
async getUrl(params) {
|
|
115
|
+
const [found] = await db.select({
|
|
116
|
+
key: schemas.files.key,
|
|
117
|
+
visibility: schemas.files.visibility
|
|
118
|
+
}).from(schemas.files).where(eq(schemas.files.id, params.id)).limit(1);
|
|
119
|
+
if (!found) {
|
|
120
|
+
throw new Error("File not found");
|
|
121
|
+
}
|
|
122
|
+
if (found.visibility === "public") {
|
|
123
|
+
return `${publicBaseUrl}/${found.key}`;
|
|
124
|
+
}
|
|
125
|
+
return s3Client.presign(found.key, { expiresIn: presignExpiresIn });
|
|
126
|
+
},
|
|
127
|
+
async uploadFile(params) {
|
|
128
|
+
const id = nanoid2();
|
|
129
|
+
const ext = extname(params.file.name);
|
|
130
|
+
const key = [keyPrefix, params.visibility, params.purpose, `${id}${ext}`].join("/");
|
|
131
|
+
const s3file = s3Client.file(key);
|
|
132
|
+
await s3file.write(params.file, {
|
|
133
|
+
type: params.file.type,
|
|
134
|
+
acl: params.visibility === "public" ? "public-read" : "private"
|
|
135
|
+
});
|
|
136
|
+
try {
|
|
137
|
+
const [created] = await db.insert(schemas.files).values({
|
|
138
|
+
purpose: params.purpose,
|
|
139
|
+
key,
|
|
140
|
+
size: s3file.size,
|
|
141
|
+
name: s3file.name ?? null,
|
|
142
|
+
mimeType: s3file.type,
|
|
143
|
+
visibility: params.visibility
|
|
144
|
+
}).returning();
|
|
145
|
+
if (!created) {
|
|
146
|
+
throw new Error("Failed to create file record");
|
|
147
|
+
}
|
|
148
|
+
return created;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
await s3file.delete().catch();
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
async deleteFile(params) {
|
|
155
|
+
const [deleted] = await db.delete(schemas.files).where(eq(schemas.files.id, params.id)).returning();
|
|
156
|
+
if (!deleted)
|
|
157
|
+
return;
|
|
158
|
+
await s3Client.delete(deleted.key);
|
|
159
|
+
},
|
|
160
|
+
async acquireFile(params) {
|
|
161
|
+
const [updated] = await db.update(schemas.files).set({ refCount: sql`${schemas.files.refCount} + 1` }).where(and(eq(schemas.files.id, params.id), params.purpose ? eq(schemas.files.purpose, params.purpose) : undefined)).returning();
|
|
162
|
+
if (!updated) {
|
|
163
|
+
throw new Error("File not found");
|
|
164
|
+
}
|
|
165
|
+
return updated;
|
|
166
|
+
},
|
|
167
|
+
async releaseFile(params) {
|
|
168
|
+
const [updated] = await db.update(schemas.files).set({ refCount: sql`${schemas.files.refCount} - 1` }).where(eq(schemas.files.id, params.id)).returning();
|
|
169
|
+
if (!updated) {
|
|
170
|
+
throw new Error("File not found");
|
|
171
|
+
}
|
|
172
|
+
if (updated.refCount < 1) {
|
|
173
|
+
await this.deleteFile({ id: params.id });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/backend/backend.ts
|
|
180
|
+
function createFilesBackend(options) {
|
|
181
|
+
const schemas = createSchemas();
|
|
182
|
+
const services = createServices({
|
|
183
|
+
db: options.db,
|
|
184
|
+
schemas,
|
|
185
|
+
s3Client: options.client,
|
|
186
|
+
publicBaseUrl: options.publicBaseUrl,
|
|
187
|
+
presignExpiresIn: options.presignExpiresIn
|
|
188
|
+
});
|
|
189
|
+
const routes = createRoutes({
|
|
190
|
+
services,
|
|
191
|
+
policies: options.policies
|
|
192
|
+
});
|
|
193
|
+
return {
|
|
194
|
+
schemas,
|
|
195
|
+
routes,
|
|
196
|
+
services
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export {
|
|
200
|
+
createFilesBackend
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
//# debugId=6563BBC77836F87F64756E2164756E21
|
|
204
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjXFxiYWNrZW5kXFxyb3V0ZXMudHMiLCAic3JjXFxiYWNrZW5kXFxzY2hlbWFzLnRzIiwgInNyY1xcYmFja2VuZFxcc2VydmljZXMudHMiLCAic3JjXFxiYWNrZW5kXFxiYWNrZW5kLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWwogICAgImltcG9ydCB7IEVseXNpYSwgdCB9IGZyb20gXCJlbHlzaWFcIjtcclxuaW1wb3J0IHR5cGUgeyBTZXJ2aWNlcyB9IGZyb20gXCIuL3NlcnZpY2VzXCI7XHJcblxyXG5leHBvcnQgdHlwZSBWaXNpYmlsaXR5ID0gXCJwcml2YXRlXCIgfCBcInB1YmxpY1wiO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBQdXJwb3NlUG9saWN5IHtcclxuICBtYXhTaXplPzogbnVtYmVyO1xyXG4gIGFsbG93ZWRNaW1lVHlwZXM/OiBzdHJpbmdbXTtcclxuICB2aXNpYmlsaXR5PzogVmlzaWJpbGl0eTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVJvdXRlczxjb25zdCBUUHVycG9zZSBleHRlbmRzIHN0cmluZz4oXHJcbiAgb3B0aW9uczoge1xyXG4gICAgc2VydmljZXM6IFNlcnZpY2VzO1xyXG4gICAgcG9saWNpZXM6IFJlY29yZDxUUHVycG9zZSwgUHVycG9zZVBvbGljeT5cclxuICB9XHJcbikge1xyXG4gIFxyXG4gIGNvbnN0IHsgXHJcbiAgICBzZXJ2aWNlcywgXHJcbiAgICBwb2xpY2llcyxcclxuICB9ID0gb3B0aW9ucztcclxuXHJcbiAgcmV0dXJuIG5ldyBFbHlzaWEoe1xyXG4gICAgcHJlZml4OiBcIi9hcGlcIixcclxuICB9KS5wb3N0KFwiL2ZpbGVzL3VwbG9hZFwiLCBhc3luYyAoeyBzdGF0dXMsIGJvZHkgfSkgPT4ge1xyXG4gICAgY29uc3QgeyBmaWxlLCBwdXJwb3NlIH0gPSBib2R5O1xyXG4gICAgY29uc3QgcG9saWN5ID0gcG9saWNpZXNbcHVycG9zZSBhcyBUUHVycG9zZV07XHJcbiAgICBpZiAoIXBvbGljeSkge1xyXG4gICAgICByZXR1cm4gc3RhdHVzKDQwMCwge1xyXG4gICAgICAgIG1lc3NhZ2U6IFwiUHVycG9zZSBub3Qgc3VwcG9ydGVkXCIsXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgaWYgKHBvbGljeS5tYXhTaXplICE9PSB1bmRlZmluZWQgJiYgZmlsZS5zaXplID4gcG9saWN5Lm1heFNpemUpIHtcclxuICAgICAgcmV0dXJuIHN0YXR1cyg0MDAsIHtcclxuICAgICAgICBtZXNzYWdlOiBcIkZpbGUgc2l6ZSBleGNlZWRzIHRoZSBtYXhpbXVtIGFsbG93ZWQgc2l6ZVwiLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuICAgIGlmIChwb2xpY3kuYWxsb3dlZE1pbWVUeXBlcyAhPT0gdW5kZWZpbmVkICYmICFwb2xpY3kuYWxsb3dlZE1pbWVUeXBlcy5pbmNsdWRlcyhmaWxlLnR5cGUpKSB7XHJcbiAgICAgIHJldHVybiBzdGF0dXMoNDAwLCB7XHJcbiAgICAgICAgbWVzc2FnZTogXCJGaWxlIHR5cGUgbm90IGFsbG93ZWRcIixcclxuICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBjb25zdCB1cGxvYWRlZCA9IGF3YWl0IHNlcnZpY2VzLnVwbG9hZEZpbGUoeyBcclxuICAgICAgZmlsZSwgXHJcbiAgICAgIHB1cnBvc2UsIFxyXG4gICAgICB2aXNpYmlsaXR5OiBwb2xpY3kudmlzaWJpbGl0eSA/PyBcInByaXZhdGVcIiBcclxuICAgIH0pO1xyXG4gICAgcmV0dXJuIHN0YXR1cygyMDAsIHtcclxuICAgICAgaWQ6IHVwbG9hZGVkLmlkLFxyXG4gICAgICBuYW1lOiB1cGxvYWRlZC5uYW1lLFxyXG4gICAgICBrZXk6IHVwbG9hZGVkLmtleSxcclxuICAgICAgc2l6ZTogdXBsb2FkZWQuc2l6ZSxcclxuICAgICAgbWltZVR5cGU6IHVwbG9hZGVkLm1pbWVUeXBlLFxyXG4gICAgICBjcmVhdGVkQXQ6IHVwbG9hZGVkLmNyZWF0ZWRBdCxcclxuICAgIH0pO1xyXG4gIH0sIHtcclxuICAgIGJvZHk6IFVwbG9hZFJlcXVlc3QoT2JqZWN0LmtleXMocG9saWNpZXMpIGFzIFRQdXJwb3NlW10pLFxyXG4gICAgcmVzcG9uc2U6IHtcclxuICAgICAgMjAwOiBGaWxlUmVzcG9uc2UsXHJcbiAgICAgIDQwMDogRXJyb3JSZXNwb25zZSxcclxuICAgICAgNTAwOiBFcnJvclJlc3BvbnNlLFxyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcblxyXG5mdW5jdGlvbiBVcGxvYWRSZXF1ZXN0PGNvbnN0IFRQdXJwb3NlIGV4dGVuZHMgc3RyaW5nPihcclxuICBwdXJwb3NlczogVFB1cnBvc2VbXVxyXG4pIHtcclxuICByZXR1cm4gdC5PYmplY3Qoe1xyXG4gICAgZmlsZTogdC5GaWxlKCksXHJcbiAgICBwdXJwb3NlOiB0LlVuaW9uKFxyXG4gICAgICBwdXJwb3Nlcy5tYXAoKHApID0+IHQuTGl0ZXJhbChwKSkgYXMgW1xyXG4gICAgICAgIFJldHVyblR5cGU8dHlwZW9mIHQuTGl0ZXJhbDxUUHVycG9zZT4+LFxyXG4gICAgICAgIC4uLlJldHVyblR5cGU8dHlwZW9mIHQuTGl0ZXJhbDxUUHVycG9zZT4+W10sXHJcbiAgICAgIF1cclxuICAgIClcclxuICB9KTtcclxufVxyXG5cclxuY29uc3QgRXJyb3JSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBtZXNzYWdlOiB0LlN0cmluZygpLFxyXG59KTtcclxuXHJcbmNvbnN0IEZpbGVSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBpZDogdC5TdHJpbmcoKSxcclxuICBuYW1lOiB0Lk51bGxhYmxlKHQuU3RyaW5nKCkpLFxyXG4gIGtleTogdC5TdHJpbmcoKSxcclxuICBzaXplOiB0Lk51bWJlcigpLFxyXG4gIG1pbWVUeXBlOiB0LlN0cmluZygpLFxyXG4gIGNyZWF0ZWRBdDogdC5EYXRlKCksXHJcbn0pOyIsCiAgICAiaW1wb3J0IHsgbmFub2lkIH0gZnJvbSBcIm5hbm9pZFwiO1xyXG5pbXBvcnQgeyBpbmRleCwgaW50ZWdlciwgcGdUYWJsZSwgdGV4dCwgdGltZXN0YW1wLCBwZ0VudW0gfSBmcm9tIFwiZHJpenpsZS1vcm0vcGctY29yZVwiO1xyXG5cclxuXHJcbmV4cG9ydCB0eXBlIEFsbFNjaGVtYXMgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVTY2hlbWFzPjtcclxuZXhwb3J0IHR5cGUgRmlsZVNjaGVtYSA9IEFsbFNjaGVtYXNbXCJmaWxlc1wiXTtcclxuZXhwb3J0IHR5cGUgRmlsZVNlbGVjdCA9IEZpbGVTY2hlbWFbXCIkaW5mZXJTZWxlY3RcIl07XHJcbmV4cG9ydCB0eXBlIEZpbGVJbnNlcnQgPSBGaWxlU2NoZW1hW1wiJGluZmVySW5zZXJ0XCJdO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVNjaGVtYXMoKSB7XHJcblxyXG4gIGNvbnN0IGZpbGVzID0gcGdUYWJsZShcImZpbGVzXCIsIHtcclxuICAgIGlkOiB0ZXh0KFwiaWRcIikucHJpbWFyeUtleSgpLiRkZWZhdWx0Rm4oKCkgPT4gYGZpbGVfJHtuYW5vaWQoKX1gKSxcclxuICAgIHB1cnBvc2U6IHRleHQoXCJwdXJwb3NlXCIpLm5vdE51bGwoKSxcclxuICAgIG5hbWU6IHRleHQoXCJuYW1lXCIpLFxyXG4gICAga2V5OiB0ZXh0KFwia2V5XCIpLm5vdE51bGwoKS51bmlxdWUoKSxcclxuICAgIHNpemU6IGludGVnZXIoXCJzaXplXCIpLm5vdE51bGwoKSxcclxuICAgIG1pbWVUeXBlOiB0ZXh0KFwibWltZV90eXBlXCIpLm5vdE51bGwoKS5kZWZhdWx0KFwiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtXCIpLFxyXG4gICAgcmVmQ291bnQ6IGludGVnZXIoXCJyZWZfY291bnRcIikubm90TnVsbCgpLmRlZmF1bHQoMCksXHJcbiAgICB2aXNpYmlsaXR5OiBwZ0VudW0oXCJ2aXNpYmlsaXR5XCIsIFtcInByaXZhdGVcIiwgXCJwdWJsaWNcIl0pKCkubm90TnVsbCgpLmRlZmF1bHQoXCJwcml2YXRlXCIpLFxyXG4gICAgY3JlYXRlZEF0OiB0aW1lc3RhbXAoXCJjcmVhdGVkX2F0XCIpLm5vdE51bGwoKS5kZWZhdWx0Tm93KCksXHJcbiAgICB1cGRhdGVkQXQ6IHRpbWVzdGFtcChcInVwZGF0ZWRfYXRcIikubm90TnVsbCgpLmRlZmF1bHROb3coKSxcclxuICB9LCAodGFibGUpID0+IFtcclxuICAgIGluZGV4KFwiZmlsZXNfa2V5X2lkeFwiKS5vbih0YWJsZS5rZXkpLFxyXG4gICAgaW5kZXgoXCJmaWxlc19jcmVhdGVkX2F0X2lkeFwiKS5vbih0YWJsZS5jcmVhdGVkQXQpLFxyXG4gIF0pO1xyXG5cclxuICByZXR1cm4ge1xyXG4gICAgZmlsZXMsXHJcbiAgfVxyXG59IiwKICAgICJpbXBvcnQgdHlwZSB7IFMzQ2xpZW50LCBTM0ZpbGUgfSBmcm9tIFwiYnVuXCI7XHJcbmltcG9ydCB7IG5hbm9pZCB9IGZyb20gXCJuYW5vaWRcIjtcclxuaW1wb3J0IHsgZXh0bmFtZSB9IGZyb20gXCJwYXRoXCI7XHJcbmltcG9ydCB7IGVxLCBzcWwsIGx0LCBhbmQgfSBmcm9tIFwiZHJpenpsZS1vcm1cIjtcclxuaW1wb3J0IHR5cGUgeyBCdW5TUUxEYXRhYmFzZSB9IGZyb20gXCJkcml6emxlLW9ybS9idW4tc3FsXCI7XHJcbmltcG9ydCB0eXBlIHsgQWxsU2NoZW1hcywgRmlsZVNlbGVjdCB9IGZyb20gXCIuL3NjaGVtYXNcIjtcclxuXHJcbmV4cG9ydCB0eXBlIFNlcnZpY2VzID0gUmV0dXJuVHlwZTx0eXBlb2YgY3JlYXRlU2VydmljZXM+O1xyXG5cclxudHlwZSBCeUlkID0geyBpZDogc3RyaW5nIH07XHJcbnR5cGUgQnlLZXkgPSB7IGtleTogc3RyaW5nIH07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU2VydmljZXMoXHJcbiAgb3B0aW9uczoge1xyXG4gICAgZGI6IEJ1blNRTERhdGFiYXNlO1xyXG4gICAgc2NoZW1hczogQWxsU2NoZW1hcztcclxuICAgIHMzQ2xpZW50OiBTM0NsaWVudDtcclxuICAgIHB1YmxpY0Jhc2VVcmw6IHN0cmluZztcclxuICAgIGtleVByZWZpeD86IHN0cmluZztcclxuICAgIHByZXNpZ25FeHBpcmVzSW4/OiBudW1iZXI7XHJcbiAgfVxyXG4pIHtcclxuICBjb25zdCB7IFxyXG4gICAgZGIsIFxyXG4gICAgc2NoZW1hcywgXHJcbiAgICBzM0NsaWVudCwgXHJcbiAgICBwdWJsaWNCYXNlVXJsLCBcclxuICAgIGtleVByZWZpeCA9IFwiZmlsZXNcIixcclxuICAgIHByZXNpZ25FeHBpcmVzSW4gPSAzNjAwXHJcbiAgfSA9IG9wdGlvbnM7XHJcbiAgXHJcbiAgcmV0dXJuIHtcclxuXHJcbiAgICBzM0NsaWVudCxcclxuXHJcbiAgICBhc3luYyBnZXRGaWxlKHBhcmFtczogQnlJZCk6IFByb21pc2U8RmlsZVNlbGVjdD4ge1xyXG4gICAgICBjb25zdCBbZm91bmRdID0gYXdhaXQgZGJcclxuICAgICAgICAuc2VsZWN0KClcclxuICAgICAgICAuZnJvbShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5saW1pdCgxKTtcclxuICAgICAgaWYgKCFmb3VuZCkge1xyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZpbGUgbm90IGZvdW5kXCIpO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBmb3VuZDtcclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgZ2V0VXJsKHBhcmFtczogQnlJZCk6IFByb21pc2U8c3RyaW5nPiB7XHJcbiAgICAgIGNvbnN0IFtmb3VuZF0gPSBhd2FpdCBkYlxyXG4gICAgICAgIC5zZWxlY3QoeyBcclxuICAgICAgICAgIGtleTogc2NoZW1hcy5maWxlcy5rZXksXHJcbiAgICAgICAgICB2aXNpYmlsaXR5OiBzY2hlbWFzLmZpbGVzLnZpc2liaWxpdHksXHJcbiAgICAgICAgfSlcclxuICAgICAgICAuZnJvbShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5saW1pdCgxKTtcclxuICAgICAgaWYgKCFmb3VuZCkge1xyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZpbGUgbm90IGZvdW5kXCIpO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChmb3VuZC52aXNpYmlsaXR5ID09PSBcInB1YmxpY1wiKSB7XHJcbiAgICAgICAgcmV0dXJuIGAke3B1YmxpY0Jhc2VVcmx9LyR7Zm91bmQua2V5fWA7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHMzQ2xpZW50LnByZXNpZ24oZm91bmQua2V5LCB7IGV4cGlyZXNJbjogcHJlc2lnbkV4cGlyZXNJbiB9KTtcclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgdXBsb2FkRmlsZShwYXJhbXM6IHtcclxuICAgICAgZmlsZTogRmlsZTtcclxuICAgICAgcHVycG9zZTogc3RyaW5nO1xyXG4gICAgICB2aXNpYmlsaXR5OiBcInByaXZhdGVcIiB8IFwicHVibGljXCI7XHJcbiAgICB9KTogUHJvbWlzZTxGaWxlU2VsZWN0PiB7XHJcbiAgICAgIGNvbnN0IGlkID0gbmFub2lkKCk7XHJcbiAgICAgIGNvbnN0IGV4dCA9IGV4dG5hbWUocGFyYW1zLmZpbGUubmFtZSk7XHJcbiAgICAgIGNvbnN0IGtleSA9IFtrZXlQcmVmaXgsIHBhcmFtcy52aXNpYmlsaXR5LCBwYXJhbXMucHVycG9zZSwgYCR7aWR9JHtleHR9YF0uam9pbihcIi9cIik7XHJcbiAgICAgIGNvbnN0IHMzZmlsZSA9IHMzQ2xpZW50LmZpbGUoa2V5KTtcclxuICAgICAgYXdhaXQgczNmaWxlLndyaXRlKHBhcmFtcy5maWxlLCB7XHJcbiAgICAgICAgdHlwZTogcGFyYW1zLmZpbGUudHlwZSxcclxuICAgICAgICBhY2w6IHBhcmFtcy52aXNpYmlsaXR5ID09PSBcInB1YmxpY1wiID8gXCJwdWJsaWMtcmVhZFwiIDogXCJwcml2YXRlXCIsXHJcbiAgICAgIH0pO1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IFtjcmVhdGVkXSA9IGF3YWl0IGRiLmluc2VydChzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgICAgLnZhbHVlcyh7XHJcbiAgICAgICAgICAgIHB1cnBvc2U6IHBhcmFtcy5wdXJwb3NlLFxyXG4gICAgICAgICAgICBrZXksXHJcbiAgICAgICAgICAgIHNpemU6IHMzZmlsZS5zaXplLFxyXG4gICAgICAgICAgICBuYW1lOiBzM2ZpbGUubmFtZSA/PyBudWxsLFxyXG4gICAgICAgICAgICBtaW1lVHlwZTogczNmaWxlLnR5cGUsXHJcbiAgICAgICAgICAgIHZpc2liaWxpdHk6IHBhcmFtcy52aXNpYmlsaXR5LFxyXG4gICAgICAgICAgfSlcclxuICAgICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgICBpZiAoIWNyZWF0ZWQpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZhaWxlZCB0byBjcmVhdGUgZmlsZSByZWNvcmRcIik7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBjcmVhdGVkO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGF3YWl0IHMzZmlsZS5kZWxldGUoKS5jYXRjaCgpO1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIGFzeW5jIGRlbGV0ZUZpbGUocGFyYW1zOiB7IGlkOiBzdHJpbmcgfSk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICBjb25zdCBbZGVsZXRlZF0gPSBhd2FpdCBkYlxyXG4gICAgICAgIC5kZWxldGUoc2NoZW1hcy5maWxlcylcclxuICAgICAgICAud2hlcmUoZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSlcclxuICAgICAgICAucmV0dXJuaW5nKCk7XHJcbiAgICAgIGlmICghZGVsZXRlZCkgcmV0dXJuO1xyXG4gICAgICBhd2FpdCBzM0NsaWVudC5kZWxldGUoZGVsZXRlZC5rZXkpO1xyXG4gICAgfSxcclxuXHJcbiAgICBhc3luYyBhY3F1aXJlRmlsZShwYXJhbXM6IEJ5SWQgJiB7IFxyXG4gICAgICBwdXJwb3NlPzogc3RyaW5nIFxyXG4gICAgfSk6IFByb21pc2U8RmlsZVNlbGVjdD4ge1xyXG4gICAgICBjb25zdCBbdXBkYXRlZF0gPSBhd2FpdCBkYlxyXG4gICAgICAgIC51cGRhdGUoc2NoZW1hcy5maWxlcylcclxuICAgICAgICAuc2V0KHsgcmVmQ291bnQ6IHNxbGAke3NjaGVtYXMuZmlsZXMucmVmQ291bnR9ICsgMWAgfSlcclxuICAgICAgICAud2hlcmUoXHJcbiAgICAgICAgICBhbmQoXHJcbiAgICAgICAgICAgIGVxKHNjaGVtYXMuZmlsZXMuaWQsIHBhcmFtcy5pZCksXHJcbiAgICAgICAgICAgIHBhcmFtcy5wdXJwb3NlID8gZXEoc2NoZW1hcy5maWxlcy5wdXJwb3NlLCBwYXJhbXMucHVycG9zZSkgOiB1bmRlZmluZWQsXHJcbiAgICAgICAgICApXHJcbiAgICAgICAgKVxyXG4gICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgaWYgKCF1cGRhdGVkKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmlsZSBub3QgZm91bmRcIik7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHVwZGF0ZWQ7XHJcbiAgICB9LFxyXG5cclxuICAgIGFzeW5jIHJlbGVhc2VGaWxlKHBhcmFtczogQnlJZCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICBjb25zdCBbdXBkYXRlZF0gPSBhd2FpdCBkYlxyXG4gICAgICAgIC51cGRhdGUoc2NoZW1hcy5maWxlcylcclxuICAgICAgICAuc2V0KHsgcmVmQ291bnQ6IHNxbGAke3NjaGVtYXMuZmlsZXMucmVmQ291bnR9IC0gMWAgfSlcclxuICAgICAgICAud2hlcmUoZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSlcclxuICAgICAgICAucmV0dXJuaW5nKCk7XHJcbiAgICAgIGlmICghdXBkYXRlZCkge1xyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZpbGUgbm90IGZvdW5kXCIpO1xyXG4gICAgICB9XHJcbiAgICAgIGlmICh1cGRhdGVkLnJlZkNvdW50IDwgMSkge1xyXG4gICAgICAgIGF3YWl0IHRoaXMuZGVsZXRlRmlsZSh7IGlkOiBwYXJhbXMuaWQgfSk7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgfTtcclxufSIsCiAgICAiaW1wb3J0IHR5cGUgeyBCdW5TUUxEYXRhYmFzZSB9IGZyb20gXCJkcml6emxlLW9ybS9idW4tc3FsXCI7XHJcbmltcG9ydCB7IGNyZWF0ZVJvdXRlcywgdHlwZSBQdXJwb3NlUG9saWN5IH0gZnJvbSBcIi4vcm91dGVzXCI7XHJcbmltcG9ydCB7IGNyZWF0ZVNjaGVtYXMgfSBmcm9tIFwiLi9zY2hlbWFzXCI7XHJcbmltcG9ydCB7IGNyZWF0ZVNlcnZpY2VzIH0gZnJvbSBcIi4vc2VydmljZXNcIjtcclxuaW1wb3J0IHR5cGUgeyBTM0NsaWVudCB9IGZyb20gXCJidW5cIjtcclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgRmlsZXNCYWNrZW5kT3B0aW9uczxUUHVycG9zZXMgZXh0ZW5kcyBzdHJpbmc+IHtcclxuICBkYjogQnVuU1FMRGF0YWJhc2U7XHJcbiAgY2xpZW50OiBTM0NsaWVudDtcclxuICBwb2xpY2llczogUmVjb3JkPFRQdXJwb3NlcywgUHVycG9zZVBvbGljeT47XHJcbiAgcHVibGljQmFzZVVybDogc3RyaW5nO1xyXG4gIHByZXNpZ25FeHBpcmVzSW4/OiBudW1iZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVGaWxlc0JhY2tlbmQ8Y29uc3QgVFB1cnBvc2UgZXh0ZW5kcyBzdHJpbmc+KFxyXG4gIG9wdGlvbnM6IEZpbGVzQmFja2VuZE9wdGlvbnM8VFB1cnBvc2U+XHJcbikge1xyXG4gIGNvbnN0IHNjaGVtYXMgPSBjcmVhdGVTY2hlbWFzKCk7XHJcbiAgY29uc3Qgc2VydmljZXMgPSBjcmVhdGVTZXJ2aWNlcyh7XHJcbiAgICBkYjogb3B0aW9ucy5kYixcclxuICAgIHNjaGVtYXMsXHJcbiAgICBzM0NsaWVudDogb3B0aW9ucy5jbGllbnQsXHJcbiAgICBwdWJsaWNCYXNlVXJsOiBvcHRpb25zLnB1YmxpY0Jhc2VVcmwsXHJcbiAgICBwcmVzaWduRXhwaXJlc0luOiBvcHRpb25zLnByZXNpZ25FeHBpcmVzSW4sXHJcbiAgfSk7XHJcbiAgY29uc3Qgcm91dGVzID0gY3JlYXRlUm91dGVzKHtcclxuICAgIHNlcnZpY2VzLFxyXG4gICAgcG9saWNpZXM6IG9wdGlvbnMucG9saWNpZXMsXHJcbiAgfSk7XHJcbiAgcmV0dXJuIHtcclxuICAgIHNjaGVtYXMsXHJcbiAgICByb3V0ZXMsXHJcbiAgICBzZXJ2aWNlcyxcclxuICB9IGFzIGNvbnN0O1xyXG59IgogIF0sCiAgIm1hcHBpbmdzIjogIjtBQUFBO0FBV08sU0FBUyxZQUEyQyxDQUN6RCxTQUlBO0FBQUEsRUFFQTtBQUFBLElBQ0U7QUFBQSxJQUNBO0FBQUEsTUFDRTtBQUFBLEVBRUosT0FBTyxJQUFJLE9BQU87QUFBQSxJQUNoQixRQUFRO0FBQUEsRUFDVixDQUFDLEVBQUUsS0FBSyxpQkFBaUIsU0FBUyxRQUFRLFdBQVc7QUFBQSxJQUNuRCxRQUFRLE1BQU0sWUFBWTtBQUFBLElBQzFCLE1BQU0sU0FBUyxTQUFTO0FBQUEsSUFDeEIsSUFBSSxDQUFDLFFBQVE7QUFBQSxNQUNYLE9BQU8sT0FBTyxLQUFLO0FBQUEsUUFDakIsU0FBUztBQUFBLE1BQ1gsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLElBQUksT0FBTyxZQUFZLGFBQWEsS0FBSyxPQUFPLE9BQU8sU0FBUztBQUFBLE1BQzlELE9BQU8sT0FBTyxLQUFLO0FBQUEsUUFDakIsU0FBUztBQUFBLE1BQ1gsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLElBQUksT0FBTyxxQkFBcUIsYUFBYSxDQUFDLE9BQU8saUJBQWlCLFNBQVMsS0FBSyxJQUFJLEdBQUc7QUFBQSxNQUN6RixPQUFPLE9BQU8sS0FBSztBQUFBLFFBQ2pCLFNBQVM7QUFBQSxNQUNYLENBQUM7QUFBQSxJQUNIO0FBQUEs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WUFBVyxDQUFDLFFBQTZCO0FBQUEsTUFDN0MsT0FBTyxXQUFXLE1BQU0sR0FDckIsT0FBTyxRQUFRLEtBQUssRUFDcEIsSUFBSSxFQUFFLFVBQVUsTUFBTSxRQUFRLE1BQU0sZUFBZSxDQUFDLEVBQ3BELE1BQU0sR0FBRyxRQUFRLE1BQU0sSUFBSSxPQUFPLEVBQUUsQ0FBQyxFQUNyQyxVQUFVO0FBQUEsTUFDYixJQUFJLENBQUMsU0FBUztBQUFBLFFBQ1osTUFBTSxJQUFJLE1BQU0sZ0JBQWdCO0FBQUEsTUFDbEM7QUFBQSxNQUNBLElBQUksUUFBUSxXQUFXLEdBQUc7QUFBQSxRQUN4QixNQUFNLEtBQUssV0FBVyxFQUFFLElBQUksT0FBTyxHQUFHLENBQUM7QUFBQSxNQUN6QztBQUFBO0FBQUEsRUFFSjtBQUFBOzs7QUM5SEssU0FBUyxrQkFBaUQsQ0FDL0QsU0FDQTtBQUFBLEVBQ0EsTUFBTSxVQUFVLGNBQWM7QUFBQSxFQUM5QixNQUFNLFdBQVcsZUFBZTtBQUFBLElBQzlCLElBQUksUUFBUTtBQUFBLElBQ1o7QUFBQSxJQUNBLFVBQVUsUUFBUTtBQUFBLElBQ2xCLGVBQWUsUUFBUTtBQUFBLElBQ3ZCLGtCQUFrQixRQUFRO0FBQUEsRUFDNUIsQ0FBQztBQUFBLEVBQ0QsTUFBTSxTQUFTLGFBQWE7QUFBQSxJQUMxQjtBQUFBLElBQ0EsVUFBVSxRQUFRO0FBQUEsRUFDcEIsQ0FBQztBQUFBLEVBQ0QsT0FBTztBQUFBLElBQ0w7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLEVBQ0Y7QUFBQTsiLAogICJkZWJ1Z0lkIjogIjY1NjNCQkM3NzgzNkY4N0Y2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { Treaty } from "@elysiajs/eden";
|
|
3
|
+
import { BunSQLDatabase as BunSQLDatabase2 } from "drizzle-orm/bun-sql";
|
|
4
|
+
type Visibility = "private" | "public";
|
|
5
|
+
interface PurposePolicy {
|
|
6
|
+
maxSize?: number;
|
|
7
|
+
allowedMimeTypes?: string[];
|
|
8
|
+
visibility?: Visibility;
|
|
9
|
+
}
|
|
10
|
+
import { S3Client as S3Client2 } from "bun";
|
|
11
|
+
interface FilesBackendOptions<TPurposes extends string> {
|
|
12
|
+
db: BunSQLDatabase2;
|
|
13
|
+
client: S3Client2;
|
|
14
|
+
policies: Record<TPurposes, PurposePolicy>;
|
|
15
|
+
publicBaseUrl: string;
|
|
16
|
+
presignExpiresIn?: number;
|
|
17
|
+
}
|
|
18
|
+
declare function createFilesBackend<const TPurpose extends string>(options: FilesBackendOptions<TPurpose>): {};
|
|
19
|
+
declare function createFilesClient<TBackend extends ReturnType<typeof createFilesBackend<any>>>(domain: string, config?: Treaty.Config);
|
|
20
|
+
type FilesClient = ReturnType<typeof createFilesClient>;
|
|
21
|
+
declare function FilesClientProvider(props: {
|
|
22
|
+
filesClient: FilesClient;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
});
|
|
25
|
+
declare function useFilesClient();
|
|
26
|
+
import { InferMutationOptions } from "eden2query";
|
|
27
|
+
declare function useUpload(options: InferMutationOptions<FilesClient["api"]["files"]["upload"]["post"]>);
|
|
28
|
+
export { useUpload, useFilesClient, createFilesClient, FilesClientProvider, FilesClient };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/client/provider.tsx
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
4
|
+
var FilesClientContext = createContext(null);
|
|
5
|
+
function FilesClientProvider(props) {
|
|
6
|
+
return /* @__PURE__ */ jsxDEV(FilesClientContext.Provider, {
|
|
7
|
+
value: props.filesClient,
|
|
8
|
+
children: props.children
|
|
9
|
+
}, undefined, false, undefined, this);
|
|
10
|
+
}
|
|
11
|
+
function useFilesClient() {
|
|
12
|
+
const context = useContext(FilesClientContext);
|
|
13
|
+
if (!context) {
|
|
14
|
+
throw new Error("useFilesClient must be used within a FilesClientProvider");
|
|
15
|
+
}
|
|
16
|
+
return context;
|
|
17
|
+
}
|
|
18
|
+
// src/client/client.ts
|
|
19
|
+
import { treaty } from "@elysiajs/eden";
|
|
20
|
+
function createFilesClient(domain, config) {
|
|
21
|
+
return treaty(domain, config);
|
|
22
|
+
}
|
|
23
|
+
// src/client/hooks.ts
|
|
24
|
+
import { useMutation } from "@tanstack/react-query";
|
|
25
|
+
import { edenMutationOptions } from "eden2query";
|
|
26
|
+
function useUpload(options) {
|
|
27
|
+
const filesClient = useFilesClient();
|
|
28
|
+
return useMutation({
|
|
29
|
+
...options,
|
|
30
|
+
...edenMutationOptions(filesClient.api.files.upload.post)
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
useUpload,
|
|
35
|
+
useFilesClient,
|
|
36
|
+
createFilesClient,
|
|
37
|
+
FilesClientProvider
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
//# debugId=A2BBBD5C97CF307B64756E2164756E21
|
|
41
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjXFxjbGllbnRcXHByb3ZpZGVyLnRzeCIsICJzcmNcXGNsaWVudFxcY2xpZW50LnRzIiwgInNyY1xcY2xpZW50XFxob29rcy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICAgICJpbXBvcnQgeyBjcmVhdGVDb250ZXh0LCB1c2VDb250ZXh0LCB0eXBlIFJlYWN0Tm9kZSB9IGZyb20gXCJyZWFjdFwiO1xyXG5pbXBvcnQgdHlwZSB7IEZpbGVzQ2xpZW50IH0gZnJvbSBcIi4vY2xpZW50XCI7XHJcblxyXG5cclxuY29uc3QgRmlsZXNDbGllbnRDb250ZXh0ID0gY3JlYXRlQ29udGV4dDxGaWxlc0NsaWVudCB8IG51bGw+KG51bGwpO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIEZpbGVzQ2xpZW50UHJvdmlkZXIocHJvcHM6IHtcclxuICBmaWxlc0NsaWVudDogRmlsZXNDbGllbnQ7XHJcbiAgY2hpbGRyZW46IFJlYWN0Tm9kZTtcclxufSkge1xyXG4gIHJldHVybiAoXHJcbiAgICA8RmlsZXNDbGllbnRDb250ZXh0LlByb3ZpZGVyIHZhbHVlPXtwcm9wcy5maWxlc0NsaWVudH0+XHJcbiAgICAgIHtwcm9wcy5jaGlsZHJlbn1cclxuICAgIDwvRmlsZXNDbGllbnRDb250ZXh0LlByb3ZpZGVyPlxyXG4gICk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2VGaWxlc0NsaWVudCgpIHtcclxuICBjb25zdCBjb250ZXh0ID0gdXNlQ29udGV4dChGaWxlc0NsaWVudENvbnRleHQpO1xyXG4gIGlmICghY29udGV4dCkge1xyXG4gICAgdGhyb3cgbmV3IEVycm9yKFwidXNlRmlsZXNDbGllbnQgbXVzdCBiZSB1c2VkIHdpdGhpbiBhIEZpbGVzQ2xpZW50UHJvdmlkZXJcIik7XHJcbiAgfVxyXG4gIHJldHVybiBjb250ZXh0O1xyXG59XHJcbiIsCiAgICAiaW1wb3J0IHsgdHJlYXR5LCB0eXBlIFRyZWF0eSB9IGZyb20gXCJAZWx5c2lhanMvZWRlblwiO1xyXG5pbXBvcnQgdHlwZSB7IGNyZWF0ZUZpbGVzQmFja2VuZCB9IGZyb20gXCIuLi9iYWNrZW5kXCI7XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRmlsZXNDbGllbnQ8XHJcbiAgVEJhY2tlbmQgZXh0ZW5kcyBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVGaWxlc0JhY2tlbmQ8YW55Pj5cclxuPihcclxuICBkb21haW46IHN0cmluZyxcclxuICBjb25maWc/OiBUcmVhdHkuQ29uZmlnXHJcbikge1xyXG4gIHJldHVybiB0cmVhdHk8VEJhY2tlbmRbXCJyb3V0ZXNcIl0+KGRvbWFpbiwgY29uZmlnKTtcclxufVxyXG5cclxuZXhwb3J0IHR5cGUgRmlsZXNDbGllbnQgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVGaWxlc0NsaWVudD47IiwKICAgICJpbXBvcnQgeyB1c2VNdXRhdGlvbiwgdHlwZSBVc2VNdXRhdGlvbk9wdGlvbnMgfSBmcm9tIFwiQHRhbnN0YWNrL3JlYWN0LXF1ZXJ5XCI7XHJcbmltcG9ydCB0eXBlIHsgRmlsZXNDbGllbnQgfSBmcm9tIFwiLi9jbGllbnRcIjtcclxuaW1wb3J0IHsgZWRlbk11dGF0aW9uT3B0aW9ucywgdHlwZSBJbmZlck11dGF0aW9uT3B0aW9ucyB9IGZyb20gXCJlZGVuMnF1ZXJ5XCI7XHJcbmltcG9ydCB7IHVzZUZpbGVzQ2xpZW50IH0gZnJvbSBcIi4vcHJvdmlkZXJcIjtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2VVcGxvYWQoXHJcbiAgb3B0aW9uczogSW5mZXJNdXRhdGlvbk9wdGlvbnM8RmlsZXNDbGllbnRbXCJhcGlcIl1bXCJmaWxlc1wiXVtcInVwbG9hZFwiXVtcInBvc3RcIl0+XHJcbikge1xyXG4gIGNvbnN0IGZpbGVzQ2xpZW50ID0gdXNlRmlsZXNDbGllbnQoKSA7XHJcbiAgcmV0dXJuIHVzZU11dGF0aW9uKHtcclxuICAgIC4uLm9wdGlvbnMsXHJcbiAgICAuLi5lZGVuTXV0YXRpb25PcHRpb25zKFxyXG4gICAgICBmaWxlc0NsaWVudC5hcGkuZmlsZXMudXBsb2FkLnBvc3QsXHJcbiAgICApLFxyXG4gIH0pO1xyXG59IgogIF0sCiAgIm1hcHBpbmdzIjogIjtBQUFBO0FBQUE7QUFJQSxJQUFNLHFCQUFxQixjQUFrQyxJQUFJO0FBRTFELFNBQVMsbUJBQW1CLENBQUMsT0FHakM7QUFBQSxFQUNELHVCQUNFLE9BRUUsbUJBQW1CLFVBRnJCO0FBQUEsSUFBNkIsT0FBTyxNQUFNO0FBQUEsSUFBMUMsVUFDRyxNQUFNO0FBQUEsS0FEVCxpQ0FFRTtBQUFBO0FBSUMsU0FBUyxjQUFjLEdBQUc7QUFBQSxFQUMvQixNQUFNLFVBQVUsV0FBVyxrQkFBa0I7QUFBQSxFQUM3QyxJQUFJLENBQUMsU0FBUztBQUFBLElBQ1osTUFBTSxJQUFJLE1BQU0sMERBQTBEO0FBQUEsRUFDNUU7QUFBQSxFQUNBLE9BQU87QUFBQTs7QUN0QlQ7QUFHTyxTQUFTLGlCQUVmLENBQ0MsUUFDQSxRQUNBO0FBQUEsRUFDQSxPQUFPLE9BQTJCLFFBQVEsTUFBTTtBQUFBOztBQ1RsRDtBQUVBO0FBR08sU0FBUyxTQUFTLENBQ3ZCLFNBQ0E7QUFBQSxFQUNBLE1BQU0sY0FBYyxlQUFlO0FBQUEsRUFDbkMsT0FBTyxZQUFZO0FBQUEsT0FDZDtBQUFBLE9BQ0Esb0JBQ0QsWUFBWSxJQUFJLE1BQU0sT0FBTyxJQUMvQjtBQUFBLEVBQ0YsQ0FBQztBQUFBOyIsCiAgImRlYnVnSWQiOiAiQTJCQkJENUM5N0NGMzA3QjY0NzU2RTIxNjQ3NTZFMjEiLAogICJuYW1lcyI6IFtdCn0=
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@edkstack/files",
|
|
3
|
+
"description": "File management utilities for EDK Stack",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"README.md"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
"./backend": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/backend/index.d.ts",
|
|
14
|
+
"default": "./dist/backend/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"./client": {
|
|
18
|
+
"import": {
|
|
19
|
+
"types": "./dist/client/index.d.ts",
|
|
20
|
+
"default": "./dist/client/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "bunup",
|
|
27
|
+
"prepublishOnly": "bun run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"files",
|
|
31
|
+
"file-management",
|
|
32
|
+
"edkstack",
|
|
33
|
+
"elysia",
|
|
34
|
+
"drizzle"
|
|
35
|
+
],
|
|
36
|
+
"author": "",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": ""
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": ""
|
|
44
|
+
},
|
|
45
|
+
"homepage": "",
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/bun": "latest",
|
|
51
|
+
"@types/react": "^19.2.13",
|
|
52
|
+
"bunup": "^0.16.25"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@elysiajs/eden": "^1.4.6",
|
|
56
|
+
"@tanstack/react-query": "^5.90.20",
|
|
57
|
+
"drizzle-orm": "^0.45.1",
|
|
58
|
+
"elysia": "^1.4.22",
|
|
59
|
+
"react": "^19.2.4",
|
|
60
|
+
"typescript": "^5"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"eden2query": "^0.2.0",
|
|
64
|
+
"nanoid": "^5.1.6"
|
|
65
|
+
}
|
|
66
|
+
}
|