@edkstack/files 0.1.7 → 0.1.9

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.
@@ -0,0 +1,2 @@
1
+ import { a as FileRecord, i as Visibility, n as createFilesBackend, r as PurposePolicy, t as FilesBackendOptions } from "../backend-DpyA_n4-.mjs";
2
+ export { type FileRecord, type FilesBackendOptions, type PurposePolicy, type Visibility, createFilesBackend };
@@ -0,0 +1,3 @@
1
+ import { t as createFilesBackend } from "../backend-BOAJlrA-.mjs";
2
+
3
+ export { createFilesBackend };
@@ -0,0 +1,163 @@
1
+ import { Elysia, t } from "elysia";
2
+ import { nanoid } from "nanoid";
3
+ import { index, integer, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core";
4
+ import { S3Client } from "bun";
5
+ import { extname } from "path";
6
+ import { and, eq, sql } from "drizzle-orm";
7
+
8
+ //#region src/backend/routes.ts
9
+ function createRoutes(options) {
10
+ const { services, policies } = options;
11
+ return new Elysia({ prefix: "/api" }).post("/files/upload", async ({ status, body }) => {
12
+ const { file, purpose } = body;
13
+ const policy = policies[purpose];
14
+ if (!policy) return status(400, { message: "Purpose not supported" });
15
+ if (policy.maxSize !== void 0 && file.size > policy.maxSize) return status(400, { message: "File size exceeds the maximum allowed size" });
16
+ if (policy.allowedMimeTypes !== void 0 && !policy.allowedMimeTypes.includes(file.type)) return status(400, { message: "File type not allowed" });
17
+ const uploaded = await services.uploadFile({
18
+ file,
19
+ purpose,
20
+ visibility: policy.visibility ?? "private"
21
+ });
22
+ return status(200, {
23
+ id: uploaded.id,
24
+ name: uploaded.name,
25
+ key: uploaded.key,
26
+ size: uploaded.size,
27
+ mimeType: uploaded.mimeType,
28
+ createdAt: uploaded.createdAt
29
+ });
30
+ }, {
31
+ body: UploadRequest(Object.keys(policies)),
32
+ response: {
33
+ 200: FileResponse,
34
+ 400: ErrorResponse,
35
+ 500: ErrorResponse
36
+ }
37
+ });
38
+ }
39
+ function UploadRequest(purposes) {
40
+ return t.Object({
41
+ file: t.File(),
42
+ purpose: t.Union(purposes.map((p) => t.Literal(p)))
43
+ });
44
+ }
45
+ const ErrorResponse = t.Object({ message: t.String() });
46
+ const FileResponse = t.Object({
47
+ id: t.String(),
48
+ name: t.Nullable(t.String()),
49
+ key: t.String(),
50
+ size: t.Number(),
51
+ mimeType: t.String(),
52
+ createdAt: t.Date()
53
+ });
54
+
55
+ //#endregion
56
+ //#region src/backend/schemas.ts
57
+ function createSchemas() {
58
+ return { files: pgTable("files", {
59
+ id: text("id").primaryKey().$defaultFn(() => `file_${nanoid()}`),
60
+ purpose: text("purpose").notNull(),
61
+ name: text("name"),
62
+ key: text("key").notNull().unique(),
63
+ size: integer("size").notNull(),
64
+ mimeType: text("mime_type").notNull().default("application/octet-stream"),
65
+ refCount: integer("ref_count").notNull().default(0),
66
+ visibility: pgEnum("visibility", ["private", "public"])().notNull().default("private"),
67
+ createdAt: timestamp("created_at").notNull().defaultNow(),
68
+ updatedAt: timestamp("updated_at").notNull().defaultNow()
69
+ }, (table) => [index("files_key_idx").on(table.key), index("files_created_at_idx").on(table.createdAt)]) };
70
+ }
71
+
72
+ //#endregion
73
+ //#region src/backend/services.ts
74
+ function createServices(options) {
75
+ const { db, schemas, s3Options, keyPrefix = "files", presignExpiresIn = 3600 } = options;
76
+ const { endpoint } = s3Options;
77
+ const s3Client = new S3Client(s3Options);
78
+ return {
79
+ async getFile(params) {
80
+ const [found] = await db.select().from(schemas.files).where(eq(schemas.files.id, params.id)).limit(1);
81
+ if (!found) throw new Error("File not found");
82
+ return found;
83
+ },
84
+ async getUrl(params) {
85
+ const [found] = await db.select({
86
+ key: schemas.files.key,
87
+ visibility: schemas.files.visibility
88
+ }).from(schemas.files).where(eq(schemas.files.id, params.id)).limit(1);
89
+ if (!found) throw new Error("File not found");
90
+ if (found.visibility === "public") return `${endpoint}/${found.key}`;
91
+ return s3Client.presign(found.key, { expiresIn: presignExpiresIn });
92
+ },
93
+ async uploadFile(params) {
94
+ const id = nanoid();
95
+ const ext = extname(params.file.name);
96
+ const key = [
97
+ keyPrefix,
98
+ params.visibility,
99
+ params.purpose,
100
+ `${id}${ext}`
101
+ ].join("/");
102
+ const s3file = s3Client.file(key);
103
+ await s3file.write(params.file, {
104
+ type: params.file.type,
105
+ acl: params.visibility === "public" ? "public-read" : "private"
106
+ });
107
+ try {
108
+ const [created] = await db.insert(schemas.files).values({
109
+ purpose: params.purpose,
110
+ key,
111
+ size: s3file.size,
112
+ name: s3file.name ?? null,
113
+ mimeType: s3file.type,
114
+ visibility: params.visibility
115
+ }).returning();
116
+ if (!created) throw new Error("Failed to create file record");
117
+ return created;
118
+ } catch (error) {
119
+ await s3file.delete().catch();
120
+ throw error;
121
+ }
122
+ },
123
+ async deleteFile(params) {
124
+ const [deleted] = await db.delete(schemas.files).where(eq(schemas.files.id, params.id)).returning();
125
+ if (!deleted) return;
126
+ await s3Client.delete(deleted.key);
127
+ },
128
+ async acquireFile(params) {
129
+ 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) : void 0)).returning();
130
+ if (!updated) throw new Error("File not found");
131
+ return updated;
132
+ },
133
+ async releaseFile(params) {
134
+ const [updated] = await db.update(schemas.files).set({ refCount: sql`${schemas.files.refCount} - 1` }).where(eq(schemas.files.id, params.id)).returning();
135
+ if (!updated) throw new Error("File not found");
136
+ if (updated.refCount < 1) await this.deleteFile({ id: params.id });
137
+ }
138
+ };
139
+ }
140
+
141
+ //#endregion
142
+ //#region src/backend/backend.ts
143
+ function createFilesBackend(options) {
144
+ const schemas = createSchemas();
145
+ const services = createServices({
146
+ db: options.db,
147
+ schemas,
148
+ s3Options: options.s3Options,
149
+ presignExpiresIn: options.presignExpiresIn
150
+ });
151
+ return {
152
+ schemas,
153
+ routes: createRoutes({
154
+ services,
155
+ policies: options.policies
156
+ }),
157
+ services
158
+ };
159
+ }
160
+
161
+ //#endregion
162
+ export { createFilesBackend as t };
163
+ //# sourceMappingURL=backend-BOAJlrA-.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend-BOAJlrA-.mjs","names":[],"sources":["../src/backend/routes.ts","../src/backend/schemas.ts","../src/backend/services.ts","../src/backend/backend.ts"],"sourcesContent":["import { Elysia, t } from \"elysia\";\r\nimport type { Services } from \"./services\";\r\n\r\nexport type Visibility = \"private\" | \"public\";\r\n\r\nexport interface PurposePolicy {\r\n maxSize?: number;\r\n allowedMimeTypes?: string[];\r\n visibility?: Visibility;\r\n}\r\n\r\nexport function createRoutes<const TPurpose extends string>(\r\n options: {\r\n services: Services;\r\n policies: Record<TPurpose, PurposePolicy>\r\n }\r\n) {\r\n \r\n const { \r\n services, \r\n policies,\r\n } = options;\r\n\r\n return new Elysia({\r\n prefix: \"/api\",\r\n }).post(\"/files/upload\", async ({ status, body }) => {\r\n const { file, purpose } = body;\r\n const policy = policies[purpose as TPurpose];\r\n if (!policy) {\r\n return status(400, {\r\n message: \"Purpose not supported\",\r\n });\r\n }\r\n if (policy.maxSize !== undefined && file.size > policy.maxSize) {\r\n return status(400, {\r\n message: \"File size exceeds the maximum allowed size\",\r\n });\r\n }\r\n if (policy.allowedMimeTypes !== undefined && !policy.allowedMimeTypes.includes(file.type)) {\r\n return status(400, {\r\n message: \"File type not allowed\",\r\n });\r\n }\r\n const uploaded = await services.uploadFile({ \r\n file, \r\n purpose, \r\n visibility: policy.visibility ?? \"private\" \r\n });\r\n return status(200, {\r\n id: uploaded.id,\r\n name: uploaded.name,\r\n key: uploaded.key,\r\n size: uploaded.size,\r\n mimeType: uploaded.mimeType,\r\n createdAt: uploaded.createdAt,\r\n });\r\n }, {\r\n body: UploadRequest(Object.keys(policies) as TPurpose[]),\r\n response: {\r\n 200: FileResponse,\r\n 400: ErrorResponse,\r\n 500: ErrorResponse,\r\n }\r\n });\r\n}\r\n\r\nfunction UploadRequest<const TPurpose extends string>(\r\n purposes: TPurpose[]\r\n) {\r\n return t.Object({\r\n file: t.File(),\r\n purpose: t.Union(\r\n purposes.map((p) => t.Literal(p)) as [\r\n ReturnType<typeof t.Literal<TPurpose>>,\r\n ...ReturnType<typeof t.Literal<TPurpose>>[],\r\n ]\r\n )\r\n });\r\n}\r\n\r\nconst ErrorResponse = t.Object({\r\n message: t.String(),\r\n});\r\n\r\nconst FileResponse = t.Object({\r\n id: t.String(),\r\n name: t.Nullable(t.String()),\r\n key: t.String(),\r\n size: t.Number(),\r\n mimeType: t.String(),\r\n createdAt: t.Date(),\r\n});","import { nanoid } from \"nanoid\";\r\nimport { index, integer, pgTable, text, timestamp, pgEnum } from \"drizzle-orm/pg-core\";\r\n\r\n\r\nexport type Schemas = ReturnType<typeof createSchemas>;\r\nexport type FileSchema = Schemas[\"files\"];\r\nexport type FileRecord = FileSchema[\"$inferSelect\"];\r\n\r\nexport function createSchemas() {\r\n\r\n const files = pgTable(\"files\", {\r\n id: text(\"id\").primaryKey().$defaultFn(() => `file_${nanoid()}`),\r\n purpose: text(\"purpose\").notNull(),\r\n name: text(\"name\"),\r\n key: text(\"key\").notNull().unique(),\r\n size: integer(\"size\").notNull(),\r\n mimeType: text(\"mime_type\").notNull().default(\"application/octet-stream\"),\r\n refCount: integer(\"ref_count\").notNull().default(0),\r\n visibility: pgEnum(\"visibility\", [\"private\", \"public\"])().notNull().default(\"private\"),\r\n createdAt: timestamp(\"created_at\").notNull().defaultNow(),\r\n updatedAt: timestamp(\"updated_at\").notNull().defaultNow(),\r\n }, (table) => [\r\n index(\"files_key_idx\").on(table.key),\r\n index(\"files_created_at_idx\").on(table.createdAt),\r\n ]);\r\n\r\n return {\r\n files,\r\n }\r\n}","import { S3Client, type S3Options } from \"bun\";\r\nimport { nanoid } from \"nanoid\";\r\nimport { extname } from \"path\";\r\nimport { eq, sql, lt, and } from \"drizzle-orm\";\r\nimport type { Schemas, FileRecord } from \"./schemas\";\r\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\r\n\r\nexport type Services = ReturnType<typeof createServices>;\r\n\r\ntype ById = { id: string };\r\n\r\nexport function createServices(\r\n options: {\r\n db: PgDatabase<any, any, any>;\r\n schemas: Schemas;\r\n s3Options: S3Options;\r\n keyPrefix?: string;\r\n presignExpiresIn?: number;\r\n }\r\n) {\r\n const { \r\n db, \r\n schemas, \r\n s3Options,\r\n keyPrefix = \"files\",\r\n presignExpiresIn = 3600\r\n } = options;\r\n \r\n const { endpoint } = s3Options;\r\n const s3Client = new S3Client(s3Options);\r\n\r\n return {\r\n\r\n async getFile(params: ById): Promise<FileRecord> {\r\n const [found] = await db\r\n .select()\r\n .from(schemas.files)\r\n .where(eq(schemas.files.id, params.id))\r\n .limit(1);\r\n if (!found) {\r\n throw new Error(\"File not found\");\r\n }\r\n return found;\r\n },\r\n\r\n async getUrl(params: ById): Promise<string> {\r\n const [found] = await db\r\n .select({ \r\n key: schemas.files.key,\r\n visibility: schemas.files.visibility,\r\n })\r\n .from(schemas.files)\r\n .where(eq(schemas.files.id, params.id))\r\n .limit(1);\r\n if (!found) {\r\n throw new Error(\"File not found\");\r\n }\r\n if (found.visibility === \"public\") {\r\n return `${endpoint}/${found.key}`;\r\n }\r\n return s3Client.presign(found.key, { expiresIn: presignExpiresIn });\r\n },\r\n\r\n async uploadFile(params: {\r\n file: File;\r\n purpose: string;\r\n visibility: \"private\" | \"public\";\r\n }): Promise<FileRecord> {\r\n const id = nanoid();\r\n const ext = extname(params.file.name);\r\n const key = [keyPrefix, params.visibility, params.purpose, `${id}${ext}`].join(\"/\");\r\n const s3file = s3Client.file(key);\r\n await s3file.write(params.file, {\r\n type: params.file.type,\r\n acl: params.visibility === \"public\" ? \"public-read\" : \"private\",\r\n });\r\n try {\r\n const [created] = await db.insert(schemas.files)\r\n .values({\r\n purpose: params.purpose,\r\n key,\r\n size: s3file.size,\r\n name: s3file.name ?? null,\r\n mimeType: s3file.type,\r\n visibility: params.visibility,\r\n })\r\n .returning();\r\n if (!created) {\r\n throw new Error(\"Failed to create file record\");\r\n }\r\n return created;\r\n } catch (error) {\r\n await s3file.delete().catch();\r\n throw error;\r\n }\r\n },\r\n\r\n async deleteFile(params: { id: string }): Promise<void> {\r\n const [deleted] = await db\r\n .delete(schemas.files)\r\n .where(eq(schemas.files.id, params.id))\r\n .returning();\r\n if (!deleted) return;\r\n await s3Client.delete(deleted.key);\r\n },\r\n\r\n async acquireFile(params: ById & { \r\n purpose?: string \r\n }): Promise<FileRecord> {\r\n const [updated] = await db\r\n .update(schemas.files)\r\n .set({ refCount: sql`${schemas.files.refCount} + 1` })\r\n .where(\r\n and(\r\n eq(schemas.files.id, params.id),\r\n params.purpose ? eq(schemas.files.purpose, params.purpose) : undefined,\r\n )\r\n )\r\n .returning();\r\n if (!updated) {\r\n throw new Error(\"File not found\");\r\n }\r\n return updated;\r\n },\r\n\r\n async releaseFile(params: ById): Promise<void> {\r\n const [updated] = await db\r\n .update(schemas.files)\r\n .set({ refCount: sql`${schemas.files.refCount} - 1` })\r\n .where(eq(schemas.files.id, params.id))\r\n .returning();\r\n if (!updated) {\r\n throw new Error(\"File not found\");\r\n }\r\n if (updated.refCount < 1) {\r\n await this.deleteFile({ id: params.id });\r\n }\r\n },\r\n };\r\n}","import type { PgDatabase } from \"drizzle-orm/pg-core\";\r\nimport { createRoutes, type PurposePolicy } from \"./routes\";\r\nimport { createSchemas } from \"./schemas\";\r\nimport { createServices } from \"./services\";\r\nimport type { S3Options } from \"bun\";\r\n\r\nexport interface FilesBackendOptions<TPurposes extends string> {\r\n db: PgDatabase<any, any, any>;\r\n s3Options: S3Options;\r\n policies: Record<TPurposes, PurposePolicy>;\r\n presignExpiresIn?: number;\r\n}\r\n\r\nexport function createFilesBackend<const TPurpose extends string>(\r\n options: FilesBackendOptions<TPurpose>\r\n) {\r\n const schemas = createSchemas();\r\n const services = createServices({\r\n db: options.db,\r\n schemas,\r\n s3Options: options.s3Options,\r\n presignExpiresIn: options.presignExpiresIn,\r\n });\r\n const routes = createRoutes({\r\n services,\r\n policies: options.policies,\r\n });\r\n return {\r\n schemas,\r\n routes,\r\n services,\r\n } as const;\r\n}"],"mappings":"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"}
@@ -0,0 +1,482 @@
1
+ import * as elysia from "elysia";
2
+ import { Elysia } from "elysia";
3
+ import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
4
+ import { PgDatabase } from "drizzle-orm/pg-core";
5
+ import { S3Options } from "bun";
6
+
7
+ //#region src/backend/schemas.d.ts
8
+ type Schemas = ReturnType<typeof createSchemas>;
9
+ type FileSchema = Schemas["files"];
10
+ type FileRecord = FileSchema["$inferSelect"];
11
+ declare function createSchemas(): {
12
+ files: drizzle_orm_pg_core0.PgTableWithColumns<{
13
+ name: "files";
14
+ schema: undefined;
15
+ columns: {
16
+ id: drizzle_orm_pg_core0.PgColumn<{
17
+ name: "id";
18
+ tableName: "files";
19
+ dataType: "string";
20
+ columnType: "PgText";
21
+ data: string;
22
+ driverParam: string;
23
+ notNull: true;
24
+ hasDefault: true;
25
+ isPrimaryKey: true;
26
+ isAutoincrement: false;
27
+ hasRuntimeDefault: true;
28
+ enumValues: [string, ...string[]];
29
+ baseColumn: never;
30
+ identity: undefined;
31
+ generated: undefined;
32
+ }, {}, {}>;
33
+ purpose: drizzle_orm_pg_core0.PgColumn<{
34
+ name: "purpose";
35
+ tableName: "files";
36
+ dataType: "string";
37
+ columnType: "PgText";
38
+ data: string;
39
+ driverParam: string;
40
+ notNull: true;
41
+ hasDefault: false;
42
+ isPrimaryKey: false;
43
+ isAutoincrement: false;
44
+ hasRuntimeDefault: false;
45
+ enumValues: [string, ...string[]];
46
+ baseColumn: never;
47
+ identity: undefined;
48
+ generated: undefined;
49
+ }, {}, {}>;
50
+ name: drizzle_orm_pg_core0.PgColumn<{
51
+ name: "name";
52
+ tableName: "files";
53
+ dataType: "string";
54
+ columnType: "PgText";
55
+ data: string;
56
+ driverParam: string;
57
+ notNull: false;
58
+ hasDefault: false;
59
+ isPrimaryKey: false;
60
+ isAutoincrement: false;
61
+ hasRuntimeDefault: false;
62
+ enumValues: [string, ...string[]];
63
+ baseColumn: never;
64
+ identity: undefined;
65
+ generated: undefined;
66
+ }, {}, {}>;
67
+ key: drizzle_orm_pg_core0.PgColumn<{
68
+ name: "key";
69
+ tableName: "files";
70
+ dataType: "string";
71
+ columnType: "PgText";
72
+ data: string;
73
+ driverParam: string;
74
+ notNull: true;
75
+ hasDefault: false;
76
+ isPrimaryKey: false;
77
+ isAutoincrement: false;
78
+ hasRuntimeDefault: false;
79
+ enumValues: [string, ...string[]];
80
+ baseColumn: never;
81
+ identity: undefined;
82
+ generated: undefined;
83
+ }, {}, {}>;
84
+ size: drizzle_orm_pg_core0.PgColumn<{
85
+ name: "size";
86
+ tableName: "files";
87
+ dataType: "number";
88
+ columnType: "PgInteger";
89
+ data: number;
90
+ driverParam: string | number;
91
+ notNull: true;
92
+ hasDefault: false;
93
+ isPrimaryKey: false;
94
+ isAutoincrement: false;
95
+ hasRuntimeDefault: false;
96
+ enumValues: undefined;
97
+ baseColumn: never;
98
+ identity: undefined;
99
+ generated: undefined;
100
+ }, {}, {}>;
101
+ mimeType: drizzle_orm_pg_core0.PgColumn<{
102
+ name: "mime_type";
103
+ tableName: "files";
104
+ dataType: "string";
105
+ columnType: "PgText";
106
+ data: string;
107
+ driverParam: string;
108
+ notNull: true;
109
+ hasDefault: true;
110
+ isPrimaryKey: false;
111
+ isAutoincrement: false;
112
+ hasRuntimeDefault: false;
113
+ enumValues: [string, ...string[]];
114
+ baseColumn: never;
115
+ identity: undefined;
116
+ generated: undefined;
117
+ }, {}, {}>;
118
+ refCount: drizzle_orm_pg_core0.PgColumn<{
119
+ name: "ref_count";
120
+ tableName: "files";
121
+ dataType: "number";
122
+ columnType: "PgInteger";
123
+ data: number;
124
+ driverParam: string | number;
125
+ notNull: true;
126
+ hasDefault: true;
127
+ isPrimaryKey: false;
128
+ isAutoincrement: false;
129
+ hasRuntimeDefault: false;
130
+ enumValues: undefined;
131
+ baseColumn: never;
132
+ identity: undefined;
133
+ generated: undefined;
134
+ }, {}, {}>;
135
+ visibility: drizzle_orm_pg_core0.PgColumn<{
136
+ name: "visibility";
137
+ tableName: "files";
138
+ dataType: "string";
139
+ columnType: "PgEnumColumn";
140
+ data: "private" | "public";
141
+ driverParam: string;
142
+ notNull: true;
143
+ hasDefault: true;
144
+ isPrimaryKey: false;
145
+ isAutoincrement: false;
146
+ hasRuntimeDefault: false;
147
+ enumValues: ["private", "public"];
148
+ baseColumn: never;
149
+ identity: undefined;
150
+ generated: undefined;
151
+ }, {}, {}>;
152
+ createdAt: drizzle_orm_pg_core0.PgColumn<{
153
+ name: "created_at";
154
+ tableName: "files";
155
+ dataType: "date";
156
+ columnType: "PgTimestamp";
157
+ data: Date;
158
+ driverParam: string;
159
+ notNull: true;
160
+ hasDefault: true;
161
+ isPrimaryKey: false;
162
+ isAutoincrement: false;
163
+ hasRuntimeDefault: false;
164
+ enumValues: undefined;
165
+ baseColumn: never;
166
+ identity: undefined;
167
+ generated: undefined;
168
+ }, {}, {}>;
169
+ updatedAt: drizzle_orm_pg_core0.PgColumn<{
170
+ name: "updated_at";
171
+ tableName: "files";
172
+ dataType: "date";
173
+ columnType: "PgTimestamp";
174
+ data: Date;
175
+ driverParam: string;
176
+ notNull: true;
177
+ hasDefault: true;
178
+ isPrimaryKey: false;
179
+ isAutoincrement: false;
180
+ hasRuntimeDefault: false;
181
+ enumValues: undefined;
182
+ baseColumn: never;
183
+ identity: undefined;
184
+ generated: undefined;
185
+ }, {}, {}>;
186
+ };
187
+ dialect: "pg";
188
+ }>;
189
+ };
190
+ //#endregion
191
+ //#region src/backend/routes.d.ts
192
+ type Visibility = "private" | "public";
193
+ interface PurposePolicy {
194
+ maxSize?: number;
195
+ allowedMimeTypes?: string[];
196
+ visibility?: Visibility;
197
+ }
198
+ //#endregion
199
+ //#region src/backend/backend.d.ts
200
+ interface FilesBackendOptions<TPurposes extends string> {
201
+ db: PgDatabase<any, any, any>;
202
+ s3Options: S3Options;
203
+ policies: Record<TPurposes, PurposePolicy>;
204
+ presignExpiresIn?: number;
205
+ }
206
+ declare function createFilesBackend<const TPurpose extends string>(options: FilesBackendOptions<TPurpose>): {
207
+ readonly schemas: {
208
+ files: drizzle_orm_pg_core0.PgTableWithColumns<{
209
+ name: "files";
210
+ schema: undefined;
211
+ columns: {
212
+ id: drizzle_orm_pg_core0.PgColumn<{
213
+ name: "id";
214
+ tableName: "files";
215
+ dataType: "string";
216
+ columnType: "PgText";
217
+ data: string;
218
+ driverParam: string;
219
+ notNull: true;
220
+ hasDefault: true;
221
+ isPrimaryKey: true;
222
+ isAutoincrement: false;
223
+ hasRuntimeDefault: true;
224
+ enumValues: [string, ...string[]];
225
+ baseColumn: never;
226
+ identity: undefined;
227
+ generated: undefined;
228
+ }, {}, {}>;
229
+ purpose: drizzle_orm_pg_core0.PgColumn<{
230
+ name: "purpose";
231
+ tableName: "files";
232
+ dataType: "string";
233
+ columnType: "PgText";
234
+ data: string;
235
+ driverParam: string;
236
+ notNull: true;
237
+ hasDefault: false;
238
+ isPrimaryKey: false;
239
+ isAutoincrement: false;
240
+ hasRuntimeDefault: false;
241
+ enumValues: [string, ...string[]];
242
+ baseColumn: never;
243
+ identity: undefined;
244
+ generated: undefined;
245
+ }, {}, {}>;
246
+ name: drizzle_orm_pg_core0.PgColumn<{
247
+ name: "name";
248
+ tableName: "files";
249
+ dataType: "string";
250
+ columnType: "PgText";
251
+ data: string;
252
+ driverParam: string;
253
+ notNull: false;
254
+ hasDefault: false;
255
+ isPrimaryKey: false;
256
+ isAutoincrement: false;
257
+ hasRuntimeDefault: false;
258
+ enumValues: [string, ...string[]];
259
+ baseColumn: never;
260
+ identity: undefined;
261
+ generated: undefined;
262
+ }, {}, {}>;
263
+ key: drizzle_orm_pg_core0.PgColumn<{
264
+ name: "key";
265
+ tableName: "files";
266
+ dataType: "string";
267
+ columnType: "PgText";
268
+ data: string;
269
+ driverParam: string;
270
+ notNull: true;
271
+ hasDefault: false;
272
+ isPrimaryKey: false;
273
+ isAutoincrement: false;
274
+ hasRuntimeDefault: false;
275
+ enumValues: [string, ...string[]];
276
+ baseColumn: never;
277
+ identity: undefined;
278
+ generated: undefined;
279
+ }, {}, {}>;
280
+ size: drizzle_orm_pg_core0.PgColumn<{
281
+ name: "size";
282
+ tableName: "files";
283
+ dataType: "number";
284
+ columnType: "PgInteger";
285
+ data: number;
286
+ driverParam: string | number;
287
+ notNull: true;
288
+ hasDefault: false;
289
+ isPrimaryKey: false;
290
+ isAutoincrement: false;
291
+ hasRuntimeDefault: false;
292
+ enumValues: undefined;
293
+ baseColumn: never;
294
+ identity: undefined;
295
+ generated: undefined;
296
+ }, {}, {}>;
297
+ mimeType: drizzle_orm_pg_core0.PgColumn<{
298
+ name: "mime_type";
299
+ tableName: "files";
300
+ dataType: "string";
301
+ columnType: "PgText";
302
+ data: string;
303
+ driverParam: string;
304
+ notNull: true;
305
+ hasDefault: true;
306
+ isPrimaryKey: false;
307
+ isAutoincrement: false;
308
+ hasRuntimeDefault: false;
309
+ enumValues: [string, ...string[]];
310
+ baseColumn: never;
311
+ identity: undefined;
312
+ generated: undefined;
313
+ }, {}, {}>;
314
+ refCount: drizzle_orm_pg_core0.PgColumn<{
315
+ name: "ref_count";
316
+ tableName: "files";
317
+ dataType: "number";
318
+ columnType: "PgInteger";
319
+ data: number;
320
+ driverParam: string | number;
321
+ notNull: true;
322
+ hasDefault: true;
323
+ isPrimaryKey: false;
324
+ isAutoincrement: false;
325
+ hasRuntimeDefault: false;
326
+ enumValues: undefined;
327
+ baseColumn: never;
328
+ identity: undefined;
329
+ generated: undefined;
330
+ }, {}, {}>;
331
+ visibility: drizzle_orm_pg_core0.PgColumn<{
332
+ name: "visibility";
333
+ tableName: "files";
334
+ dataType: "string";
335
+ columnType: "PgEnumColumn";
336
+ data: "private" | "public";
337
+ driverParam: string;
338
+ notNull: true;
339
+ hasDefault: true;
340
+ isPrimaryKey: false;
341
+ isAutoincrement: false;
342
+ hasRuntimeDefault: false;
343
+ enumValues: ["private", "public"];
344
+ baseColumn: never;
345
+ identity: undefined;
346
+ generated: undefined;
347
+ }, {}, {}>;
348
+ createdAt: drizzle_orm_pg_core0.PgColumn<{
349
+ name: "created_at";
350
+ tableName: "files";
351
+ dataType: "date";
352
+ columnType: "PgTimestamp";
353
+ data: Date;
354
+ driverParam: string;
355
+ notNull: true;
356
+ hasDefault: true;
357
+ isPrimaryKey: false;
358
+ isAutoincrement: false;
359
+ hasRuntimeDefault: false;
360
+ enumValues: undefined;
361
+ baseColumn: never;
362
+ identity: undefined;
363
+ generated: undefined;
364
+ }, {}, {}>;
365
+ updatedAt: drizzle_orm_pg_core0.PgColumn<{
366
+ name: "updated_at";
367
+ tableName: "files";
368
+ dataType: "date";
369
+ columnType: "PgTimestamp";
370
+ data: Date;
371
+ driverParam: string;
372
+ notNull: true;
373
+ hasDefault: true;
374
+ isPrimaryKey: false;
375
+ isAutoincrement: false;
376
+ hasRuntimeDefault: false;
377
+ enumValues: undefined;
378
+ baseColumn: never;
379
+ identity: undefined;
380
+ generated: undefined;
381
+ }, {}, {}>;
382
+ };
383
+ dialect: "pg";
384
+ }>;
385
+ };
386
+ readonly routes: elysia.default<"/api", {
387
+ decorator: {};
388
+ store: {};
389
+ derive: {};
390
+ resolve: {};
391
+ }, {
392
+ typebox: {};
393
+ error: {};
394
+ }, {
395
+ schema: {};
396
+ standaloneSchema: {};
397
+ macro: {};
398
+ macroFn: {};
399
+ parser: {};
400
+ response: {};
401
+ }, {
402
+ api: {
403
+ files: {
404
+ upload: {
405
+ post: {
406
+ body: {
407
+ purpose: TPurpose;
408
+ file: File;
409
+ };
410
+ params: {};
411
+ query: unknown;
412
+ headers: unknown;
413
+ response: {
414
+ 200: {
415
+ id: string;
416
+ name: string | null;
417
+ key: string;
418
+ size: number;
419
+ mimeType: string;
420
+ createdAt: Date;
421
+ };
422
+ 400: {
423
+ message: string;
424
+ };
425
+ 500: {
426
+ message: string;
427
+ };
428
+ 422: {
429
+ type: "validation";
430
+ on: string;
431
+ summary?: string;
432
+ message?: string;
433
+ found?: unknown;
434
+ property?: string;
435
+ expected?: string;
436
+ };
437
+ };
438
+ };
439
+ };
440
+ };
441
+ };
442
+ }, {
443
+ derive: {};
444
+ resolve: {};
445
+ schema: {};
446
+ standaloneSchema: {};
447
+ response: {};
448
+ }, {
449
+ derive: {};
450
+ resolve: {};
451
+ schema: {};
452
+ standaloneSchema: {};
453
+ response: {};
454
+ }>;
455
+ readonly services: {
456
+ getFile(params: {
457
+ id: string;
458
+ }): Promise<FileRecord>;
459
+ getUrl(params: {
460
+ id: string;
461
+ }): Promise<string>;
462
+ uploadFile(params: {
463
+ file: File;
464
+ purpose: string;
465
+ visibility: "private" | "public";
466
+ }): Promise<FileRecord>;
467
+ deleteFile(params: {
468
+ id: string;
469
+ }): Promise<void>;
470
+ acquireFile(params: {
471
+ id: string;
472
+ } & {
473
+ purpose?: string;
474
+ }): Promise<FileRecord>;
475
+ releaseFile(params: {
476
+ id: string;
477
+ }): Promise<void>;
478
+ };
479
+ };
480
+ //#endregion
481
+ export { FileRecord as a, Visibility as i, createFilesBackend as n, PurposePolicy as r, FilesBackendOptions as t };
482
+ //# sourceMappingURL=backend-DpyA_n4-.d.mts.map
@@ -0,0 +1,101 @@
1
+ import { n as createFilesBackend } from "../backend-DpyA_n4-.mjs";
2
+ import "../backend/index.mjs";
3
+ import { ReactNode } from "react";
4
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
5
+ import * as _elysiajs_eden0 from "@elysiajs/eden";
6
+ import { Treaty } from "@elysiajs/eden";
7
+ import * as _tanstack_react_query0 from "@tanstack/react-query";
8
+ import { InferMutationOptions } from "eden2query";
9
+
10
+ //#region src/client/client.d.ts
11
+ declare function createFilesClient<TBackend extends ReturnType<typeof createFilesBackend<any>>>(domain: string, config?: Treaty.Config): Treaty.Create<TBackend["routes"]>;
12
+ type FilesClient = ReturnType<typeof createFilesClient>;
13
+ //#endregion
14
+ //#region src/client/provider.d.ts
15
+ declare function FilesClientProvider(props: {
16
+ filesClient: FilesClient;
17
+ children: ReactNode;
18
+ }): react_jsx_runtime0.JSX.Element;
19
+ declare function useFilesClient(): {
20
+ api: {
21
+ files: {
22
+ upload: {
23
+ post: (body: {
24
+ purpose: any;
25
+ file: File | Bun.BunFile;
26
+ }, options?: {
27
+ headers?: Record<string, unknown> | undefined;
28
+ query?: Record<string, unknown> | undefined;
29
+ fetch?: RequestInit | undefined;
30
+ } | undefined) => Promise<_elysiajs_eden0.Treaty.TreatyResponse<{
31
+ 200: {
32
+ id: string;
33
+ name: string | null;
34
+ key: string;
35
+ size: number;
36
+ mimeType: string;
37
+ createdAt: Date;
38
+ };
39
+ 400: {
40
+ message: string;
41
+ };
42
+ 500: {
43
+ message: string;
44
+ };
45
+ 422: {
46
+ type: "validation";
47
+ on: string;
48
+ summary?: string;
49
+ message?: string;
50
+ found?: unknown;
51
+ property?: string;
52
+ expected?: string;
53
+ };
54
+ }>>;
55
+ };
56
+ };
57
+ };
58
+ };
59
+ //#endregion
60
+ //#region src/client/hooks.d.ts
61
+ declare function useUpload(options: InferMutationOptions<FilesClient["api"]["files"]["upload"]["post"]>): _tanstack_react_query0.UseMutationResult<{
62
+ id: string;
63
+ name: string | null;
64
+ key: string;
65
+ size: number;
66
+ mimeType: string;
67
+ createdAt: Date;
68
+ } | null, {
69
+ status: 400;
70
+ value: {
71
+ message: string;
72
+ };
73
+ } | {
74
+ status: 500;
75
+ value: {
76
+ message: string;
77
+ };
78
+ } | {
79
+ status: 422;
80
+ value: {
81
+ type: "validation";
82
+ on: string;
83
+ summary?: string;
84
+ message?: string;
85
+ found?: unknown;
86
+ property?: string;
87
+ expected?: string;
88
+ };
89
+ }, {
90
+ headers?: Record<string, unknown> | undefined;
91
+ query?: Record<string, unknown> | undefined;
92
+ fetch?: RequestInit | undefined;
93
+ } & {
94
+ body: {
95
+ purpose: any;
96
+ file: File | Bun.BunFile;
97
+ };
98
+ }, unknown>;
99
+ //#endregion
100
+ export { type FilesClient, FilesClientProvider, createFilesClient, useFilesClient, useUpload };
101
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,39 @@
1
+ import { createContext, useContext } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { treaty } from "@elysiajs/eden";
4
+ import { useMutation } from "@tanstack/react-query";
5
+ import { edenMutationOptions } from "eden2query";
6
+
7
+ //#region src/client/provider.tsx
8
+ const FilesClientContext = createContext(null);
9
+ function FilesClientProvider(props) {
10
+ return /* @__PURE__ */ jsx(FilesClientContext.Provider, {
11
+ value: props.filesClient,
12
+ children: props.children
13
+ });
14
+ }
15
+ function useFilesClient() {
16
+ const context = useContext(FilesClientContext);
17
+ if (!context) throw new Error("useFilesClient must be used within a FilesClientProvider");
18
+ return context;
19
+ }
20
+
21
+ //#endregion
22
+ //#region src/client/client.ts
23
+ function createFilesClient(domain, config) {
24
+ return treaty(domain, config);
25
+ }
26
+
27
+ //#endregion
28
+ //#region src/client/hooks.ts
29
+ function useUpload(options) {
30
+ const filesClient = useFilesClient();
31
+ return useMutation({
32
+ ...options,
33
+ ...edenMutationOptions(filesClient.api.files.upload.post)
34
+ });
35
+ }
36
+
37
+ //#endregion
38
+ export { FilesClientProvider, createFilesClient, useFilesClient, useUpload };
39
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/client/provider.tsx","../../src/client/client.ts","../../src/client/hooks.ts"],"sourcesContent":["import { createContext, useContext, type ReactNode } from \"react\";\r\nimport type { FilesClient } from \"./client\";\r\n\r\n\r\nconst FilesClientContext = createContext<FilesClient | null>(null);\r\n\r\nexport function FilesClientProvider(props: {\r\n filesClient: FilesClient;\r\n children: ReactNode;\r\n}) {\r\n return (\r\n <FilesClientContext.Provider value={props.filesClient}>\r\n {props.children}\r\n </FilesClientContext.Provider>\r\n );\r\n}\r\n\r\nexport function useFilesClient() {\r\n const context = useContext(FilesClientContext);\r\n if (!context) {\r\n throw new Error(\"useFilesClient must be used within a FilesClientProvider\");\r\n }\r\n return context;\r\n}\r\n","import { treaty, type Treaty } from \"@elysiajs/eden\";\r\nimport type { createFilesBackend } from \"../backend\";\r\n\r\nexport function createFilesClient<\r\n TBackend extends ReturnType<typeof createFilesBackend<any>>\r\n>(\r\n domain: string,\r\n config?: Treaty.Config\r\n) {\r\n return treaty<TBackend[\"routes\"]>(domain, config);\r\n}\r\n\r\nexport type FilesClient = ReturnType<typeof createFilesClient>;","import { useMutation, type UseMutationOptions } from \"@tanstack/react-query\";\r\nimport type { FilesClient } from \"./client\";\r\nimport { edenMutationOptions, type InferMutationOptions } from \"eden2query\";\r\nimport { useFilesClient } from \"./provider\";\r\n\r\nexport function useUpload(\r\n options: InferMutationOptions<FilesClient[\"api\"][\"files\"][\"upload\"][\"post\"]>\r\n) {\r\n const filesClient = useFilesClient() ;\r\n return useMutation({\r\n ...options,\r\n ...edenMutationOptions(\r\n filesClient.api.files.upload.post,\r\n ),\r\n });\r\n}"],"mappings":";;;;;;;AAIA,MAAM,qBAAqB,cAAkC,KAAK;AAElE,SAAgB,oBAAoB,OAGjC;AACD,QACE,oBAAC,mBAAmB;EAAS,OAAO,MAAM;YACvC,MAAM;GACqB;;AAIlC,SAAgB,iBAAiB;CAC/B,MAAM,UAAU,WAAW,mBAAmB;AAC9C,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,2DAA2D;AAE7E,QAAO;;;;;ACnBT,SAAgB,kBAGd,QACA,QACA;AACA,QAAO,OAA2B,QAAQ,OAAO;;;;;ACJnD,SAAgB,UACd,SACA;CACA,MAAM,cAAc,gBAAgB;AACpC,QAAO,YAAY;EACjB,GAAG;EACH,GAAG,oBACD,YAAY,IAAI,MAAM,OAAO,KAC9B;EACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { a as FileRecord, i as Visibility, n as createFilesBackend, r as PurposePolicy, t as FilesBackendOptions } from "./backend-DpyA_n4-.mjs";
2
+ import "./backend/index.mjs";
3
+ export { FileRecord, FilesBackendOptions, PurposePolicy, Visibility, createFilesBackend };
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { t as createFilesBackend } from "./backend-BOAJlrA-.mjs";
2
+ import "./backend/index.mjs";
3
+
4
+ export { createFilesBackend };
package/package.json CHANGED
@@ -1,37 +1,21 @@
1
1
  {
2
2
  "name": "@edkstack/files",
3
3
  "description": "File management utilities for EDK Stack",
4
- "version": "0.1.7",
4
+ "version": "0.1.9",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
8
8
  "README.md"
9
9
  ],
10
- "module": "./dist/index.js",
11
- "types": "./dist/index.d.ts",
10
+ "types": "./dist/index.d.mts",
12
11
  "exports": {
13
- ".": {
14
- "import": {
15
- "types": "./dist/index.d.ts",
16
- "default": "./dist/index.js"
17
- }
18
- },
19
- "./backend": {
20
- "import": {
21
- "types": "./dist/backend/index.d.ts",
22
- "default": "./dist/backend/index.js"
23
- }
24
- },
25
- "./client": {
26
- "import": {
27
- "types": "./dist/client/index.d.ts",
28
- "default": "./dist/client/index.js"
29
- }
30
- },
12
+ ".": "./dist/index.mjs",
13
+ "./backend": "./dist/backend/index.mjs",
14
+ "./client": "./dist/client/index.mjs",
31
15
  "./package.json": "./package.json"
32
16
  },
33
17
  "scripts": {
34
- "build": "bunup",
18
+ "build": "tsdown",
35
19
  "prepublishOnly": "bun run build"
36
20
  },
37
21
  "keywords": [
@@ -43,21 +27,13 @@
43
27
  ],
44
28
  "author": "",
45
29
  "license": "MIT",
46
- "repository": {
47
- "type": "git",
48
- "url": ""
49
- },
50
- "bugs": {
51
- "url": ""
52
- },
53
- "homepage": "",
54
30
  "publishConfig": {
55
31
  "access": "public"
56
32
  },
57
33
  "devDependencies": {
58
34
  "@types/bun": "latest",
59
35
  "@types/react": "^19.2.13",
60
- "bunup": "^0.16.25"
36
+ "tsdown": "^0.20.3"
61
37
  },
62
38
  "peerDependencies": {
63
39
  "@elysiajs/eden": "^1.4.6",
@@ -1,20 +0,0 @@
1
- import { PgDatabase as PgDatabase2 } from "drizzle-orm/pg-core";
2
- type Schemas = ReturnType<typeof createSchemas>;
3
- type FileSchema = Schemas["files"];
4
- type FileRecord = FileSchema["$inferSelect"];
5
- declare function createSchemas(): {};
6
- type Visibility = "private" | "public";
7
- interface PurposePolicy {
8
- maxSize?: number;
9
- allowedMimeTypes?: string[];
10
- visibility?: Visibility;
11
- }
12
- import { S3Options as S3Options2 } from "bun";
13
- interface FilesBackendOptions<TPurposes extends string> {
14
- db: PgDatabase2<any, any, any>;
15
- s3Options: S3Options2;
16
- policies: Record<TPurposes, PurposePolicy>;
17
- presignExpiresIn?: number;
18
- }
19
- declare function createFilesBackend<const TPurpose extends string>(options: FilesBackendOptions<TPurpose>): {};
20
- export { createFilesBackend, Visibility, PurposePolicy, FilesBackendOptions, FileRecord };
@@ -1,10 +0,0 @@
1
- // @bun
2
- import {
3
- createFilesBackend
4
- } from "../shared/chunk-wq7ndb52.js";
5
- export {
6
- createFilesBackend
7
- };
8
-
9
- //# debugId=9C07B894307CE45C64756E2164756E21
10
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICBdLAogICJtYXBwaW5ncyI6ICIiLAogICJkZWJ1Z0lkIjogIjlDMDdCODk0MzA3Q0U0NUM2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
@@ -1,27 +0,0 @@
1
- import { ReactNode } from "react";
2
- import { Treaty } from "@elysiajs/eden";
3
- import { PgDatabase as PgDatabase2 } from "drizzle-orm/pg-core";
4
- type Visibility = "private" | "public";
5
- interface PurposePolicy {
6
- maxSize?: number;
7
- allowedMimeTypes?: string[];
8
- visibility?: Visibility;
9
- }
10
- import { S3Options as S3Options2 } from "bun";
11
- interface FilesBackendOptions<TPurposes extends string> {
12
- db: PgDatabase2<any, any, any>;
13
- s3Options: S3Options2;
14
- policies: Record<TPurposes, PurposePolicy>;
15
- presignExpiresIn?: number;
16
- }
17
- declare function createFilesBackend<const TPurpose extends string>(options: FilesBackendOptions<TPurpose>): {};
18
- declare function createFilesClient<TBackend extends ReturnType<typeof createFilesBackend<any>>>(domain: string, config?: Treaty.Config);
19
- type FilesClient = ReturnType<typeof createFilesClient>;
20
- declare function FilesClientProvider(props: {
21
- filesClient: FilesClient;
22
- children: ReactNode;
23
- });
24
- declare function useFilesClient();
25
- import { InferMutationOptions } from "eden2query";
26
- declare function useUpload(options: InferMutationOptions<FilesClient["api"]["files"]["upload"]["post"]>);
27
- export { useUpload, useFilesClient, createFilesClient, FilesClientProvider, FilesClient };
@@ -1,42 +0,0 @@
1
- // @bun
2
- // src/client/provider.tsx
3
- import { createContext, useContext } from "react";
4
- import { jsxDEV } from "react/jsx-dev-runtime";
5
- var FilesClientContext = createContext(null);
6
- function FilesClientProvider(props) {
7
- return /* @__PURE__ */ jsxDEV(FilesClientContext.Provider, {
8
- value: props.filesClient,
9
- children: props.children
10
- }, undefined, false, undefined, this);
11
- }
12
- function useFilesClient() {
13
- const context = useContext(FilesClientContext);
14
- if (!context) {
15
- throw new Error("useFilesClient must be used within a FilesClientProvider");
16
- }
17
- return context;
18
- }
19
- // src/client/client.ts
20
- import { treaty } from "@elysiajs/eden";
21
- function createFilesClient(domain, config) {
22
- return treaty(domain, config);
23
- }
24
- // src/client/hooks.ts
25
- import { useMutation } from "@tanstack/react-query";
26
- import { edenMutationOptions } from "eden2query";
27
- function useUpload(options) {
28
- const filesClient = useFilesClient();
29
- return useMutation({
30
- ...options,
31
- ...edenMutationOptions(filesClient.api.files.upload.post)
32
- });
33
- }
34
- export {
35
- useUpload,
36
- useFilesClient,
37
- createFilesClient,
38
- FilesClientProvider
39
- };
40
-
41
- //# debugId=B5D4D3E17929B8B464756E2164756E21
42
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjXFxjbGllbnRcXHByb3ZpZGVyLnRzeCIsICJzcmNcXGNsaWVudFxcY2xpZW50LnRzIiwgInNyY1xcY2xpZW50XFxob29rcy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICAgICJpbXBvcnQgeyBjcmVhdGVDb250ZXh0LCB1c2VDb250ZXh0LCB0eXBlIFJlYWN0Tm9kZSB9IGZyb20gXCJyZWFjdFwiO1xyXG5pbXBvcnQgdHlwZSB7IEZpbGVzQ2xpZW50IH0gZnJvbSBcIi4vY2xpZW50XCI7XHJcblxyXG5cclxuY29uc3QgRmlsZXNDbGllbnRDb250ZXh0ID0gY3JlYXRlQ29udGV4dDxGaWxlc0NsaWVudCB8IG51bGw+KG51bGwpO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIEZpbGVzQ2xpZW50UHJvdmlkZXIocHJvcHM6IHtcclxuICBmaWxlc0NsaWVudDogRmlsZXNDbGllbnQ7XHJcbiAgY2hpbGRyZW46IFJlYWN0Tm9kZTtcclxufSkge1xyXG4gIHJldHVybiAoXHJcbiAgICA8RmlsZXNDbGllbnRDb250ZXh0LlByb3ZpZGVyIHZhbHVlPXtwcm9wcy5maWxlc0NsaWVudH0+XHJcbiAgICAgIHtwcm9wcy5jaGlsZHJlbn1cclxuICAgIDwvRmlsZXNDbGllbnRDb250ZXh0LlByb3ZpZGVyPlxyXG4gICk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2VGaWxlc0NsaWVudCgpIHtcclxuICBjb25zdCBjb250ZXh0ID0gdXNlQ29udGV4dChGaWxlc0NsaWVudENvbnRleHQpO1xyXG4gIGlmICghY29udGV4dCkge1xyXG4gICAgdGhyb3cgbmV3IEVycm9yKFwidXNlRmlsZXNDbGllbnQgbXVzdCBiZSB1c2VkIHdpdGhpbiBhIEZpbGVzQ2xpZW50UHJvdmlkZXJcIik7XHJcbiAgfVxyXG4gIHJldHVybiBjb250ZXh0O1xyXG59XHJcbiIsCiAgICAiaW1wb3J0IHsgdHJlYXR5LCB0eXBlIFRyZWF0eSB9IGZyb20gXCJAZWx5c2lhanMvZWRlblwiO1xyXG5pbXBvcnQgdHlwZSB7IGNyZWF0ZUZpbGVzQmFja2VuZCB9IGZyb20gXCIuLi9iYWNrZW5kXCI7XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRmlsZXNDbGllbnQ8XHJcbiAgVEJhY2tlbmQgZXh0ZW5kcyBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVGaWxlc0JhY2tlbmQ8YW55Pj5cclxuPihcclxuICBkb21haW46IHN0cmluZyxcclxuICBjb25maWc/OiBUcmVhdHkuQ29uZmlnXHJcbikge1xyXG4gIHJldHVybiB0cmVhdHk8VEJhY2tlbmRbXCJyb3V0ZXNcIl0+KGRvbWFpbiwgY29uZmlnKTtcclxufVxyXG5cclxuZXhwb3J0IHR5cGUgRmlsZXNDbGllbnQgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVGaWxlc0NsaWVudD47IiwKICAgICJpbXBvcnQgeyB1c2VNdXRhdGlvbiwgdHlwZSBVc2VNdXRhdGlvbk9wdGlvbnMgfSBmcm9tIFwiQHRhbnN0YWNrL3JlYWN0LXF1ZXJ5XCI7XHJcbmltcG9ydCB0eXBlIHsgRmlsZXNDbGllbnQgfSBmcm9tIFwiLi9jbGllbnRcIjtcclxuaW1wb3J0IHsgZWRlbk11dGF0aW9uT3B0aW9ucywgdHlwZSBJbmZlck11dGF0aW9uT3B0aW9ucyB9IGZyb20gXCJlZGVuMnF1ZXJ5XCI7XHJcbmltcG9ydCB7IHVzZUZpbGVzQ2xpZW50IH0gZnJvbSBcIi4vcHJvdmlkZXJcIjtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2VVcGxvYWQoXHJcbiAgb3B0aW9uczogSW5mZXJNdXRhdGlvbk9wdGlvbnM8RmlsZXNDbGllbnRbXCJhcGlcIl1bXCJmaWxlc1wiXVtcInVwbG9hZFwiXVtcInBvc3RcIl0+XHJcbikge1xyXG4gIGNvbnN0IGZpbGVzQ2xpZW50ID0gdXNlRmlsZXNDbGllbnQoKSA7XHJcbiAgcmV0dXJuIHVzZU11dGF0aW9uKHtcclxuICAgIC4uLm9wdGlvbnMsXHJcbiAgICAuLi5lZGVuTXV0YXRpb25PcHRpb25zKFxyXG4gICAgICBmaWxlc0NsaWVudC5hcGkuZmlsZXMudXBsb2FkLnBvc3QsXHJcbiAgICApLFxyXG4gIH0pO1xyXG59IgogIF0sCiAgIm1hcHBpbmdzIjogIjs7QUFBQTtBQUFBO0FBSUEsSUFBTSxxQkFBcUIsY0FBa0MsSUFBSTtBQUUxRCxTQUFTLG1CQUFtQixDQUFDLE9BR2pDO0FBQUEsRUFDRCx1QkFDRSxPQUVFLG1CQUFtQixVQUZyQjtBQUFBLElBQTZCLE9BQU8sTUFBTTtBQUFBLElBQTFDLFVBQ0csTUFBTTtBQUFBLEtBRFQsaUNBRUU7QUFBQTtBQUlDLFNBQVMsY0FBYyxHQUFHO0FBQUEsRUFDL0IsTUFBTSxVQUFVLFdBQVcsa0JBQWtCO0FBQUEsRUFDN0MsSUFBSSxDQUFDLFNBQVM7QUFBQSxJQUNaLE1BQU0sSUFBSSxNQUFNLDBEQUEwRDtBQUFBLEVBQzVFO0FBQUEsRUFDQSxPQUFPO0FBQUE7O0FDdEJUO0FBR08sU0FBUyxpQkFFZixDQUNDLFFBQ0EsUUFDQTtBQUFBLEVBQ0EsT0FBTyxPQUEyQixRQUFRLE1BQU07QUFBQTs7QUNUbEQ7QUFFQTtBQUdPLFNBQVMsU0FBUyxDQUN2QixTQUNBO0FBQUEsRUFDQSxNQUFNLGNBQWMsZUFBZTtBQUFBLEVBQ25DLE9BQU8sWUFBWTtBQUFBLE9BQ2Q7QUFBQSxPQUNBLG9CQUNELFlBQVksSUFBSSxNQUFNLE9BQU8sSUFDL0I7QUFBQSxFQUNGLENBQUM7QUFBQTsiLAogICJkZWJ1Z0lkIjogIkI1RDREM0UxNzkyOUI4QjQ2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
package/dist/index.d.ts DELETED
@@ -1,20 +0,0 @@
1
- import { PgDatabase as PgDatabase2 } from "drizzle-orm/pg-core";
2
- type Schemas = ReturnType<typeof createSchemas>;
3
- type FileSchema = Schemas["files"];
4
- type FileRecord = FileSchema["$inferSelect"];
5
- declare function createSchemas(): {};
6
- type Visibility = "private" | "public";
7
- interface PurposePolicy {
8
- maxSize?: number;
9
- allowedMimeTypes?: string[];
10
- visibility?: Visibility;
11
- }
12
- import { S3Options as S3Options2 } from "bun";
13
- interface FilesBackendOptions<TPurposes extends string> {
14
- db: PgDatabase2<any, any, any>;
15
- s3Options: S3Options2;
16
- policies: Record<TPurposes, PurposePolicy>;
17
- presignExpiresIn?: number;
18
- }
19
- declare function createFilesBackend<const TPurpose extends string>(options: FilesBackendOptions<TPurpose>): {};
20
- export { createFilesBackend, Visibility, PurposePolicy, FilesBackendOptions, FileRecord };
package/dist/index.js DELETED
@@ -1,10 +0,0 @@
1
- // @bun
2
- import {
3
- createFilesBackend
4
- } from "./shared/chunk-wq7ndb52.js";
5
- export {
6
- createFilesBackend
7
- };
8
-
9
- //# debugId=A60E55F38BA0FEA664756E2164756E21
10
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICBdLAogICJtYXBwaW5ncyI6ICIiLAogICJkZWJ1Z0lkIjogIkE2MEU1NUYzOEJBMEZFQTY2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
@@ -1,203 +0,0 @@
1
- // @bun
2
- // src/backend/routes.ts
3
- import { Elysia, t } from "elysia";
4
- function createRoutes(options) {
5
- const {
6
- services,
7
- policies
8
- } = options;
9
- return new Elysia({
10
- prefix: "/api"
11
- }).post("/files/upload", async ({ status, body }) => {
12
- const { file, purpose } = body;
13
- const policy = policies[purpose];
14
- if (!policy) {
15
- return status(400, {
16
- message: "Purpose not supported"
17
- });
18
- }
19
- if (policy.maxSize !== undefined && file.size > policy.maxSize) {
20
- return status(400, {
21
- message: "File size exceeds the maximum allowed size"
22
- });
23
- }
24
- if (policy.allowedMimeTypes !== undefined && !policy.allowedMimeTypes.includes(file.type)) {
25
- return status(400, {
26
- message: "File type not allowed"
27
- });
28
- }
29
- const uploaded = await services.uploadFile({
30
- file,
31
- purpose,
32
- visibility: policy.visibility ?? "private"
33
- });
34
- return status(200, {
35
- id: uploaded.id,
36
- name: uploaded.name,
37
- key: uploaded.key,
38
- size: uploaded.size,
39
- mimeType: uploaded.mimeType,
40
- createdAt: uploaded.createdAt
41
- });
42
- }, {
43
- body: UploadRequest(Object.keys(policies)),
44
- response: {
45
- 200: FileResponse,
46
- 400: ErrorResponse,
47
- 500: ErrorResponse
48
- }
49
- });
50
- }
51
- function UploadRequest(purposes) {
52
- return t.Object({
53
- file: t.File(),
54
- purpose: t.Union(purposes.map((p) => t.Literal(p)))
55
- });
56
- }
57
- var ErrorResponse = t.Object({
58
- message: t.String()
59
- });
60
- var FileResponse = t.Object({
61
- id: t.String(),
62
- name: t.Nullable(t.String()),
63
- key: t.String(),
64
- size: t.Number(),
65
- mimeType: t.String(),
66
- createdAt: t.Date()
67
- });
68
-
69
- // src/backend/schemas.ts
70
- import { nanoid } from "nanoid";
71
- import { index, integer, pgTable, text, timestamp, pgEnum } from "drizzle-orm/pg-core";
72
- function createSchemas() {
73
- const files = pgTable("files", {
74
- id: text("id").primaryKey().$defaultFn(() => `file_${nanoid()}`),
75
- purpose: text("purpose").notNull(),
76
- name: text("name"),
77
- key: text("key").notNull().unique(),
78
- size: integer("size").notNull(),
79
- mimeType: text("mime_type").notNull().default("application/octet-stream"),
80
- refCount: integer("ref_count").notNull().default(0),
81
- visibility: pgEnum("visibility", ["private", "public"])().notNull().default("private"),
82
- createdAt: timestamp("created_at").notNull().defaultNow(),
83
- updatedAt: timestamp("updated_at").notNull().defaultNow()
84
- }, (table) => [
85
- index("files_key_idx").on(table.key),
86
- index("files_created_at_idx").on(table.createdAt)
87
- ]);
88
- return {
89
- files
90
- };
91
- }
92
-
93
- // src/backend/services.ts
94
- var {S3Client } = globalThis.Bun;
95
- import { nanoid as nanoid2 } from "nanoid";
96
- import { extname } from "path";
97
- import { eq, sql, and } from "drizzle-orm";
98
- function createServices(options) {
99
- const {
100
- db,
101
- schemas,
102
- s3Options,
103
- keyPrefix = "files",
104
- presignExpiresIn = 3600
105
- } = options;
106
- const { endpoint } = s3Options;
107
- const s3Client = new S3Client(s3Options);
108
- return {
109
- async getFile(params) {
110
- const [found] = await db.select().from(schemas.files).where(eq(schemas.files.id, params.id)).limit(1);
111
- if (!found) {
112
- throw new Error("File not found");
113
- }
114
- return found;
115
- },
116
- async getUrl(params) {
117
- const [found] = await db.select({
118
- key: schemas.files.key,
119
- visibility: schemas.files.visibility
120
- }).from(schemas.files).where(eq(schemas.files.id, params.id)).limit(1);
121
- if (!found) {
122
- throw new Error("File not found");
123
- }
124
- if (found.visibility === "public") {
125
- return `${endpoint}/${found.key}`;
126
- }
127
- return s3Client.presign(found.key, { expiresIn: presignExpiresIn });
128
- },
129
- async uploadFile(params) {
130
- const id = nanoid2();
131
- const ext = extname(params.file.name);
132
- const key = [keyPrefix, params.visibility, params.purpose, `${id}${ext}`].join("/");
133
- const s3file = s3Client.file(key);
134
- await s3file.write(params.file, {
135
- type: params.file.type,
136
- acl: params.visibility === "public" ? "public-read" : "private"
137
- });
138
- try {
139
- const [created] = await db.insert(schemas.files).values({
140
- purpose: params.purpose,
141
- key,
142
- size: s3file.size,
143
- name: s3file.name ?? null,
144
- mimeType: s3file.type,
145
- visibility: params.visibility
146
- }).returning();
147
- if (!created) {
148
- throw new Error("Failed to create file record");
149
- }
150
- return created;
151
- } catch (error) {
152
- await s3file.delete().catch();
153
- throw error;
154
- }
155
- },
156
- async deleteFile(params) {
157
- const [deleted] = await db.delete(schemas.files).where(eq(schemas.files.id, params.id)).returning();
158
- if (!deleted)
159
- return;
160
- await s3Client.delete(deleted.key);
161
- },
162
- async acquireFile(params) {
163
- 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();
164
- if (!updated) {
165
- throw new Error("File not found");
166
- }
167
- return updated;
168
- },
169
- async releaseFile(params) {
170
- const [updated] = await db.update(schemas.files).set({ refCount: sql`${schemas.files.refCount} - 1` }).where(eq(schemas.files.id, params.id)).returning();
171
- if (!updated) {
172
- throw new Error("File not found");
173
- }
174
- if (updated.refCount < 1) {
175
- await this.deleteFile({ id: params.id });
176
- }
177
- }
178
- };
179
- }
180
-
181
- // src/backend/backend.ts
182
- function createFilesBackend(options) {
183
- const schemas = createSchemas();
184
- const services = createServices({
185
- db: options.db,
186
- schemas,
187
- s3Options: options.s3Options,
188
- presignExpiresIn: options.presignExpiresIn
189
- });
190
- const routes = createRoutes({
191
- services,
192
- policies: options.policies
193
- });
194
- return {
195
- schemas,
196
- routes,
197
- services
198
- };
199
- }
200
- export { createFilesBackend };
201
-
202
- //# debugId=DDC8BE7935E77F6D64756E2164756E21
203
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjXFxiYWNrZW5kXFxyb3V0ZXMudHMiLCAic3JjXFxiYWNrZW5kXFxzY2hlbWFzLnRzIiwgInNyY1xcYmFja2VuZFxcc2VydmljZXMudHMiLCAic3JjXFxiYWNrZW5kXFxiYWNrZW5kLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWwogICAgImltcG9ydCB7IEVseXNpYSwgdCB9IGZyb20gXCJlbHlzaWFcIjtcclxuaW1wb3J0IHR5cGUgeyBTZXJ2aWNlcyB9IGZyb20gXCIuL3NlcnZpY2VzXCI7XHJcblxyXG5leHBvcnQgdHlwZSBWaXNpYmlsaXR5ID0gXCJwcml2YXRlXCIgfCBcInB1YmxpY1wiO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBQdXJwb3NlUG9saWN5IHtcclxuICBtYXhTaXplPzogbnVtYmVyO1xyXG4gIGFsbG93ZWRNaW1lVHlwZXM/OiBzdHJpbmdbXTtcclxuICB2aXNpYmlsaXR5PzogVmlzaWJpbGl0eTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVJvdXRlczxjb25zdCBUUHVycG9zZSBleHRlbmRzIHN0cmluZz4oXHJcbiAgb3B0aW9uczoge1xyXG4gICAgc2VydmljZXM6IFNlcnZpY2VzO1xyXG4gICAgcG9saWNpZXM6IFJlY29yZDxUUHVycG9zZSwgUHVycG9zZVBvbGljeT5cclxuICB9XHJcbikge1xyXG4gIFxyXG4gIGNvbnN0IHsgXHJcbiAgICBzZXJ2aWNlcywgXHJcbiAgICBwb2xpY2llcyxcclxuICB9ID0gb3B0aW9ucztcclxuXHJcbiAgcmV0dXJuIG5ldyBFbHlzaWEoe1xyXG4gICAgcHJlZml4OiBcIi9hcGlcIixcclxuICB9KS5wb3N0KFwiL2ZpbGVzL3VwbG9hZFwiLCBhc3luYyAoeyBzdGF0dXMsIGJvZHkgfSkgPT4ge1xyXG4gICAgY29uc3QgeyBmaWxlLCBwdXJwb3NlIH0gPSBib2R5O1xyXG4gICAgY29uc3QgcG9saWN5ID0gcG9saWNpZXNbcHVycG9zZSBhcyBUUHVycG9zZV07XHJcbiAgICBpZiAoIXBvbGljeSkge1xyXG4gICAgICByZXR1cm4gc3RhdHVzKDQwMCwge1xyXG4gICAgICAgIG1lc3NhZ2U6IFwiUHVycG9zZSBub3Qgc3VwcG9ydGVkXCIsXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgaWYgKHBvbGljeS5tYXhTaXplICE9PSB1bmRlZmluZWQgJiYgZmlsZS5zaXplID4gcG9saWN5Lm1heFNpemUpIHtcclxuICAgICAgcmV0dXJuIHN0YXR1cyg0MDAsIHtcclxuICAgICAgICBtZXNzYWdlOiBcIkZpbGUgc2l6ZSBleGNlZWRzIHRoZSBtYXhpbXVtIGFsbG93ZWQgc2l6ZVwiLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuICAgIGlmIChwb2xpY3kuYWxsb3dlZE1pbWVUeXBlcyAhPT0gdW5kZWZpbmVkICYmICFwb2xpY3kuYWxsb3dlZE1pbWVUeXBlcy5pbmNsdWRlcyhmaWxlLnR5cGUpKSB7XHJcbiAgICAgIHJldHVybiBzdGF0dXMoNDAwLCB7XHJcbiAgICAgICAgbWVzc2FnZTogXCJGaWxlIHR5cGUgbm90IGFsbG93ZWRcIixcclxuICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBjb25zdCB1cGxvYWRlZCA9IGF3YWl0IHNlcnZpY2VzLnVwbG9hZEZpbGUoeyBcclxuICAgICAgZmlsZSwgXHJcbiAgICAgIHB1cnBvc2UsIFxyXG4gICAgICB2aXNpYmlsaXR5OiBwb2xpY3kudmlzaWJpbGl0eSA/PyBcInByaXZhdGVcIiBcclxuICAgIH0pO1xyXG4gICAgcmV0dXJuIHN0YXR1cygyMDAsIHtcclxuICAgICAgaWQ6IHVwbG9hZGVkLmlkLFxyXG4gICAgICBuYW1lOiB1cGxvYWRlZC5uYW1lLFxyXG4gICAgICBrZXk6IHVwbG9hZGVkLmtleSxcclxuICAgICAgc2l6ZTogdXBsb2FkZWQuc2l6ZSxcclxuICAgICAgbWltZVR5cGU6IHVwbG9hZGVkLm1pbWVUeXBlLFxyXG4gICAgICBjcmVhdGVkQXQ6IHVwbG9hZGVkLmNyZWF0ZWRBdCxcclxuICAgIH0pO1xyXG4gIH0sIHtcclxuICAgIGJvZHk6IFVwbG9hZFJlcXVlc3QoT2JqZWN0LmtleXMocG9saWNpZXMpIGFzIFRQdXJwb3NlW10pLFxyXG4gICAgcmVzcG9uc2U6IHtcclxuICAgICAgMjAwOiBGaWxlUmVzcG9uc2UsXHJcbiAgICAgIDQwMDogRXJyb3JSZXNwb25zZSxcclxuICAgICAgNTAwOiBFcnJvclJlc3BvbnNlLFxyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcblxyXG5mdW5jdGlvbiBVcGxvYWRSZXF1ZXN0PGNvbnN0IFRQdXJwb3NlIGV4dGVuZHMgc3RyaW5nPihcclxuICBwdXJwb3NlczogVFB1cnBvc2VbXVxyXG4pIHtcclxuICByZXR1cm4gdC5PYmplY3Qoe1xyXG4gICAgZmlsZTogdC5GaWxlKCksXHJcbiAgICBwdXJwb3NlOiB0LlVuaW9uKFxyXG4gICAgICBwdXJwb3Nlcy5tYXAoKHApID0+IHQuTGl0ZXJhbChwKSkgYXMgW1xyXG4gICAgICAgIFJldHVyblR5cGU8dHlwZW9mIHQuTGl0ZXJhbDxUUHVycG9zZT4+LFxyXG4gICAgICAgIC4uLlJldHVyblR5cGU8dHlwZW9mIHQuTGl0ZXJhbDxUUHVycG9zZT4+W10sXHJcbiAgICAgIF1cclxuICAgIClcclxuICB9KTtcclxufVxyXG5cclxuY29uc3QgRXJyb3JSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBtZXNzYWdlOiB0LlN0cmluZygpLFxyXG59KTtcclxuXHJcbmNvbnN0IEZpbGVSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBpZDogdC5TdHJpbmcoKSxcclxuICBuYW1lOiB0Lk51bGxhYmxlKHQuU3RyaW5nKCkpLFxyXG4gIGtleTogdC5TdHJpbmcoKSxcclxuICBzaXplOiB0Lk51bWJlcigpLFxyXG4gIG1pbWVUeXBlOiB0LlN0cmluZygpLFxyXG4gIGNyZWF0ZWRBdDogdC5EYXRlKCksXHJcbn0pOyIsCiAgICAiaW1wb3J0IHsgbmFub2lkIH0gZnJvbSBcIm5hbm9pZFwiO1xyXG5pbXBvcnQgeyBpbmRleCwgaW50ZWdlciwgcGdUYWJsZSwgdGV4dCwgdGltZXN0YW1wLCBwZ0VudW0gfSBmcm9tIFwiZHJpenpsZS1vcm0vcGctY29yZVwiO1xyXG5cclxuXHJcbmV4cG9ydCB0eXBlIFNjaGVtYXMgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVTY2hlbWFzPjtcclxuZXhwb3J0IHR5cGUgRmlsZVNjaGVtYSA9IFNjaGVtYXNbXCJmaWxlc1wiXTtcclxuZXhwb3J0IHR5cGUgRmlsZVJlY29yZCA9IEZpbGVTY2hlbWFbXCIkaW5mZXJTZWxlY3RcIl07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU2NoZW1hcygpIHtcclxuXHJcbiAgY29uc3QgZmlsZXMgPSBwZ1RhYmxlKFwiZmlsZXNcIiwge1xyXG4gICAgaWQ6IHRleHQoXCJpZFwiKS5wcmltYXJ5S2V5KCkuJGRlZmF1bHRGbigoKSA9PiBgZmlsZV8ke25hbm9pZCgpfWApLFxyXG4gICAgcHVycG9zZTogdGV4dChcInB1cnBvc2VcIikubm90TnVsbCgpLFxyXG4gICAgbmFtZTogdGV4dChcIm5hbWVcIiksXHJcbiAgICBrZXk6IHRleHQoXCJrZXlcIikubm90TnVsbCgpLnVuaXF1ZSgpLFxyXG4gICAgc2l6ZTogaW50ZWdlcihcInNpemVcIikubm90TnVsbCgpLFxyXG4gICAgbWltZVR5cGU6IHRleHQoXCJtaW1lX3R5cGVcIikubm90TnVsbCgpLmRlZmF1bHQoXCJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW1cIiksXHJcbiAgICByZWZDb3VudDogaW50ZWdlcihcInJlZl9jb3VudFwiKS5ub3ROdWxsKCkuZGVmYXVsdCgwKSxcclxuICAgIHZpc2liaWxpdHk6IHBnRW51bShcInZpc2liaWxpdHlcIiwgW1wicHJpdmF0ZVwiLCBcInB1YmxpY1wiXSkoKS5ub3ROdWxsKCkuZGVmYXVsdChcInByaXZhdGVcIiksXHJcbiAgICBjcmVhdGVkQXQ6IHRpbWVzdGFtcChcImNyZWF0ZWRfYXRcIikubm90TnVsbCgpLmRlZmF1bHROb3coKSxcclxuICAgIHVwZGF0ZWRBdDogdGltZXN0YW1wKFwidXBkYXRlZF9hdFwiKS5ub3ROdWxsKCkuZGVmYXVsdE5vdygpLFxyXG4gIH0sICh0YWJsZSkgPT4gW1xyXG4gICAgaW5kZXgoXCJmaWxlc19rZXlfaWR4XCIpLm9uKHRhYmxlLmtleSksXHJcbiAgICBpbmRleChcImZpbGVzX2NyZWF0ZWRfYXRfaWR4XCIpLm9uKHRhYmxlLmNyZWF0ZWRBdCksXHJcbiAgXSk7XHJcblxyXG4gIHJldHVybiB7XHJcbiAgICBmaWxlcyxcclxuICB9XHJcbn0iLAogICAgImltcG9ydCB7IFMzQ2xpZW50LCB0eXBlIFMzT3B0aW9ucyB9IGZyb20gXCJidW5cIjtcclxuaW1wb3J0IHsgbmFub2lkIH0gZnJvbSBcIm5hbm9pZFwiO1xyXG5pbXBvcnQgeyBleHRuYW1lIH0gZnJvbSBcInBhdGhcIjtcclxuaW1wb3J0IHsgZXEsIHNxbCwgbHQsIGFuZCB9IGZyb20gXCJkcml6emxlLW9ybVwiO1xyXG5pbXBvcnQgdHlwZSB7IFNjaGVtYXMsIEZpbGVSZWNvcmQgfSBmcm9tIFwiLi9zY2hlbWFzXCI7XHJcbmltcG9ydCB0eXBlIHsgUGdEYXRhYmFzZSB9IGZyb20gXCJkcml6emxlLW9ybS9wZy1jb3JlXCI7XHJcblxyXG5leHBvcnQgdHlwZSBTZXJ2aWNlcyA9IFJldHVyblR5cGU8dHlwZW9mIGNyZWF0ZVNlcnZpY2VzPjtcclxuXHJcbnR5cGUgQnlJZCA9IHsgaWQ6IHN0cmluZyB9O1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVNlcnZpY2VzKFxyXG4gIG9wdGlvbnM6IHtcclxuICAgIGRiOiBQZ0RhdGFiYXNlPGFueSwgYW55LCBhbnk+O1xyXG4gICAgc2NoZW1hczogU2NoZW1hcztcclxuICAgIHMzT3B0aW9uczogUzNPcHRpb25zO1xyXG4gICAga2V5UHJlZml4Pzogc3RyaW5nO1xyXG4gICAgcHJlc2lnbkV4cGlyZXNJbj86IG51bWJlcjtcclxuICB9XHJcbikge1xyXG4gIGNvbnN0IHsgXHJcbiAgICBkYiwgXHJcbiAgICBzY2hlbWFzLCBcclxuICAgIHMzT3B0aW9ucyxcclxuICAgIGtleVByZWZpeCA9IFwiZmlsZXNcIixcclxuICAgIHByZXNpZ25FeHBpcmVzSW4gPSAzNjAwXHJcbiAgfSA9IG9wdGlvbnM7XHJcbiAgXHJcbiAgY29uc3QgeyBlbmRwb2ludCB9ID0gczNPcHRpb25zO1xyXG4gIGNvbnN0IHMzQ2xpZW50ID0gbmV3IFMzQ2xpZW50KHMzT3B0aW9ucyk7XHJcblxyXG4gIHJldHVybiB7XHJcblxyXG4gICAgYXN5bmMgZ2V0RmlsZShwYXJhbXM6IEJ5SWQpOiBQcm9taXNlPEZpbGVSZWNvcmQ+IHtcclxuICAgICAgY29uc3QgW2ZvdW5kXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLnNlbGVjdCgpXHJcbiAgICAgICAgLmZyb20oc2NoZW1hcy5maWxlcylcclxuICAgICAgICAud2hlcmUoZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSlcclxuICAgICAgICAubGltaXQoMSk7XHJcbiAgICAgIGlmICghZm91bmQpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJGaWxlIG5vdCBmb3VuZFwiKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gZm91bmQ7XHJcbiAgICB9LFxyXG5cclxuICAgIGFzeW5jIGdldFVybChwYXJhbXM6IEJ5SWQpOiBQcm9taXNlPHN0cmluZz4ge1xyXG4gICAgICBjb25zdCBbZm91bmRdID0gYXdhaXQgZGJcclxuICAgICAgICAuc2VsZWN0KHsgXHJcbiAgICAgICAgICBrZXk6IHNjaGVtYXMuZmlsZXMua2V5LFxyXG4gICAgICAgICAgdmlzaWJpbGl0eTogc2NoZW1hcy5maWxlcy52aXNpYmlsaXR5LFxyXG4gICAgICAgIH0pXHJcbiAgICAgICAgLmZyb20oc2NoZW1hcy5maWxlcylcclxuICAgICAgICAud2hlcmUoZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSlcclxuICAgICAgICAubGltaXQoMSk7XHJcbiAgICAgIGlmICghZm91bmQpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJGaWxlIG5vdCBmb3VuZFwiKTtcclxuICAgICAgfVxyXG4gICAgICBpZiAoZm91bmQudmlzaWJpbGl0eSA9PT0gXCJwdWJsaWNcIikge1xyXG4gICAgICAgIHJldHVybiBgJHtlbmRwb2ludH0vJHtmb3VuZC5rZXl9YDtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gczNDbGllbnQucHJlc2lnbihmb3VuZC5rZXksIHsgZXhwaXJlc0luOiBwcmVzaWduRXhwaXJlc0luIH0pO1xyXG4gICAgfSxcclxuXHJcbiAgICBhc3luYyB1cGxvYWRGaWxlKHBhcmFtczoge1xyXG4gICAgICBmaWxlOiBGaWxlO1xyXG4gICAgICBwdXJwb3NlOiBzdHJpbmc7XHJcbiAgICAgIHZpc2liaWxpdHk6IFwicHJpdmF0ZVwiIHwgXCJwdWJsaWNcIjtcclxuICAgIH0pOiBQcm9taXNlPEZpbGVSZWNvcmQ+IHtcclxuICAgICAgY29uc3QgaWQgPSBuYW5vaWQoKTtcclxuICAgICAgY29uc3QgZXh0ID0gZXh0bmFtZShwYXJhbXMuZmlsZS5uYW1lKTtcclxuICAgICAgY29uc3Qga2V5ID0gW2tleVByZWZpeCwgcGFyYW1zLnZpc2liaWxpdHksIHBhcmFtcy5wdXJwb3NlLCBgJHtpZH0ke2V4dH1gXS5qb2luKFwiL1wiKTtcclxuICAgICAgY29uc3QgczNmaWxlID0gczNDbGllbnQuZmlsZShrZXkpO1xyXG4gICAgICBhd2FpdCBzM2ZpbGUud3JpdGUocGFyYW1zLmZpbGUsIHtcclxuICAgICAgICB0eXBlOiBwYXJhbXMuZmlsZS50eXBlLFxyXG4gICAgICAgIGFjbDogcGFyYW1zLnZpc2liaWxpdHkgPT09IFwicHVibGljXCIgPyBcInB1YmxpYy1yZWFkXCIgOiBcInByaXZhdGVcIixcclxuICAgICAgfSk7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgW2NyZWF0ZWRdID0gYXdhaXQgZGIuaW5zZXJ0KHNjaGVtYXMuZmlsZXMpXHJcbiAgICAgICAgICAudmFsdWVzKHtcclxuICAgICAgICAgICAgcHVycG9zZTogcGFyYW1zLnB1cnBvc2UsXHJcbiAgICAgICAgICAgIGtleSxcclxuICAgICAgICAgICAgc2l6ZTogczNmaWxlLnNpemUsXHJcbiAgICAgICAgICAgIG5hbWU6IHMzZmlsZS5uYW1lID8/IG51bGwsXHJcbiAgICAgICAgICAgIG1pbWVUeXBlOiBzM2ZpbGUudHlwZSxcclxuICAgICAgICAgICAgdmlzaWJpbGl0eTogcGFyYW1zLnZpc2liaWxpdHksXHJcbiAgICAgICAgICB9KVxyXG4gICAgICAgICAgLnJldHVybmluZygpO1xyXG4gICAgICAgIGlmICghY3JlYXRlZCkge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmFpbGVkIHRvIGNyZWF0ZSBmaWxlIHJlY29yZFwiKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIGNyZWF0ZWQ7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgYXdhaXQgczNmaWxlLmRlbGV0ZSgpLmNhdGNoKCk7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgZGVsZXRlRmlsZShwYXJhbXM6IHsgaWQ6IHN0cmluZyB9KTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgIGNvbnN0IFtkZWxldGVkXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLmRlbGV0ZShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgaWYgKCFkZWxldGVkKSByZXR1cm47XHJcbiAgICAgIGF3YWl0IHMzQ2xpZW50LmRlbGV0ZShkZWxldGVkLmtleSk7XHJcbiAgICB9LFxyXG5cclxuICAgIGFzeW5jIGFjcXVpcmVGaWxlKHBhcmFtczogQnlJZCAmIHsgXHJcbiAgICAgIHB1cnBvc2U/OiBzdHJpbmcgXHJcbiAgICB9KTogUHJvbWlzZTxGaWxlUmVjb3JkPiB7XHJcbiAgICAgIGNvbnN0IFt1cGRhdGVkXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLnVwZGF0ZShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC5zZXQoeyByZWZDb3VudDogc3FsYCR7c2NoZW1hcy5maWxlcy5yZWZDb3VudH0gKyAxYCB9KVxyXG4gICAgICAgIC53aGVyZShcclxuICAgICAgICAgIGFuZChcclxuICAgICAgICAgICAgZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSxcclxuICAgICAgICAgICAgcGFyYW1zLnB1cnBvc2UgPyBlcShzY2hlbWFzLmZpbGVzLnB1cnBvc2UsIHBhcmFtcy5wdXJwb3NlKSA6IHVuZGVmaW5lZCxcclxuICAgICAgICAgIClcclxuICAgICAgICApXHJcbiAgICAgICAgLnJldHVybmluZygpO1xyXG4gICAgICBpZiAoIXVwZGF0ZWQpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJGaWxlIG5vdCBmb3VuZFwiKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdXBkYXRlZDtcclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgcmVsZWFzZUZpbGUocGFyYW1zOiBCeUlkKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgIGNvbnN0IFt1cGRhdGVkXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLnVwZGF0ZShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC5zZXQoeyByZWZDb3VudDogc3FsYCR7c2NoZW1hcy5maWxlcy5yZWZDb3VudH0gLSAxYCB9KVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgaWYgKCF1cGRhdGVkKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmlsZSBub3QgZm91bmRcIik7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKHVwZGF0ZWQucmVmQ291bnQgPCAxKSB7XHJcbiAgICAgICAgYXdhaXQgdGhpcy5kZWxldGVGaWxlKHsgaWQ6IHBhcmFtcy5pZCB9KTtcclxuICAgICAgfVxyXG4gICAgfSxcclxuICB9O1xyXG59IiwKICAgICJpbXBvcnQgdHlwZSB7IFBnRGF0YWJhc2UgfSBmcm9tIFwiZHJpenpsZS1vcm0vcGctY29yZVwiO1xyXG5pbXBvcnQgeyBjcmVhdGVSb3V0ZXMsIHR5cGUgUHVycG9zZVBvbGljeSB9IGZyb20gXCIuL3JvdXRlc1wiO1xyXG5pbXBvcnQgeyBjcmVhdGVTY2hlbWFzIH0gZnJvbSBcIi4vc2NoZW1hc1wiO1xyXG5pbXBvcnQgeyBjcmVhdGVTZXJ2aWNlcyB9IGZyb20gXCIuL3NlcnZpY2VzXCI7XHJcbmltcG9ydCB0eXBlIHsgUzNPcHRpb25zIH0gZnJvbSBcImJ1blwiO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBGaWxlc0JhY2tlbmRPcHRpb25zPFRQdXJwb3NlcyBleHRlbmRzIHN0cmluZz4ge1xyXG4gIGRiOiBQZ0RhdGFiYXNlPGFueSwgYW55LCBhbnk+O1xyXG4gIHMzT3B0aW9uczogUzNPcHRpb25zO1xyXG4gIHBvbGljaWVzOiBSZWNvcmQ8VFB1cnBvc2VzLCBQdXJwb3NlUG9saWN5PjtcclxuICBwcmVzaWduRXhwaXJlc0luPzogbnVtYmVyO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRmlsZXNCYWNrZW5kPGNvbnN0IFRQdXJwb3NlIGV4dGVuZHMgc3RyaW5nPihcclxuICBvcHRpb25zOiBGaWxlc0JhY2tlbmRPcHRpb25zPFRQdXJwb3NlPlxyXG4pIHtcclxuICBjb25zdCBzY2hlbWFzID0gY3JlYXRlU2NoZW1hcygpO1xyXG4gIGNvbnN0IHNlcnZpY2VzID0gY3JlYXRlU2VydmljZXMoe1xyXG4gICAgZGI6IG9wdGlvbnMuZGIsXHJcbiAgICBzY2hlbWFzLFxyXG4gICAgczNPcHRpb25zOiBvcHRpb25zLnMzT3B0aW9ucyxcclxuICAgIHByZXNpZ25FeHBpcmVzSW46IG9wdGlvbnMucHJlc2lnbkV4cGlyZXNJbixcclxuICB9KTtcclxuICBjb25zdCByb3V0ZXMgPSBjcmVhdGVSb3V0ZXMoe1xyXG4gICAgc2VydmljZXMsXHJcbiAgICBwb2xpY2llczogb3B0aW9ucy5wb2xpY2llcyxcclxuICB9KTtcclxuICByZXR1cm4ge1xyXG4gICAgc2NoZW1hcyxcclxuICAgIHJvdXRlcyxcclxuICAgIHNlcnZpY2VzLFxyXG4gIH0gYXMgY29uc3Q7XHJcbn0iCiAgXSwKICAibWFwcGluZ3MiOiAiOztBQUFBO0FBV08sU0FBUyxZQUEyQyxDQUN6RCxTQUlBO0FBQUEsRUFFQTtBQUFBLElBQ0U7QUFBQSxJQUNBO0FBQUEsTUFDRTtBQUFBLEVBRUosT0FBTyxJQUFJLE9BQU87QUFBQSxJQUNoQixRQUFRO0FBQUEsRUFDVixDQUFDLEVBQUUsS0FBSyxpQkFBaUIsU0FBUyxRQUFRLFdBQVc7QUFBQSxJQUNuRCxRQUFRLE1BQU0sWUFBWTtBQUFBLElBQzFCLE1BQU0sU0FBUyxTQUFTO0FBQUEsSUFDeEIsSUFBSSxDQUFDLFFBQVE7QUFBQSxNQUNYLE9BQU8sT0FBTyxLQUFLO0FBQUEsUUFDakIsU0FBUztBQUFBLE1BQ1gsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLElBQUksT0FBTyxZQUFZLGFBQWEsS0FBSyxPQUFPLE9BQU8sU0FBUztBQUFBLE1BQzlELE9BQU8sT0FBTyxLQUFLO0FBQUEsUUFDakIsU0FBUztBQUFBLE1BQ1gsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLElBQUksT0FBTyxxQkFBcUIsYUFBYSxDQUFDLE9BQU8saUJBQWlCLFNBQVMsS0FBSyxJQUFJLEdBQUc7QUFBQSxNQUN6RixPQUFPLE9BQU8sS0FBSztBQUFBLFFBQ2pCLFNBQVM7QUFBQSxNQUNYLENBQUM7QUFBQSxJQUNIO0FBQUEsSUFDQSxNQUFNLFdBQVcsTUFBTSxTQUFTLFdBQVc7QUFBQSxNQUN6QztBQUFBLE1BQ0E7QUFBQSxNQUNBLFlBQVksT0FBTyxjQUFjO0FBQUEsSUFDbkMsQ0FBQztBQUFBLElBQ0QsT0FBTyxPQUFPLEtBQUs7QUFBQSxNQUNqQixJQUFJLFNBQVM7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LGVBQWUsQ0FBQyxFQUNwRCxNQUFNLEdBQUcsUUFBUSxNQUFNLElBQUksT0FBTyxFQUFFLENBQUMsRUFDckMsVUFBVTtBQUFBLE1BQ2IsSUFBSSxDQUFDLFNBQVM7QUFBQSxRQUNaLE1BQU0sSUFBSSxNQUFNLGdCQUFnQjtBQUFBLE1BQ2xDO0FBQUEsTUFDQSxJQUFJLFFBQVEsV0FBVyxHQUFHO0FBQUEsUUFDeEIsTUFBTSxLQUFLLFdBQVcsRUFBRSxJQUFJLE9BQU8sR0FBRyxDQUFDO0FBQUEsTUFDekM7QUFBQTtBQUFBLEVBRUo7QUFBQTs7O0FDN0hLLFNBQVMsa0JBQWlELENBQy9ELFNBQ0E7QUFBQSxFQUNBLE1BQU0sVUFBVSxjQUFjO0FBQUEsRUFDOUIsTUFBTSxXQUFXLGVBQWU7QUFBQSxJQUM5QixJQUFJLFFBQVE7QUFBQSxJQUNaO0FBQUEsSUFDQSxXQUFXLFFBQVE7QUFBQSxJQUNuQixrQkFBa0IsUUFBUTtBQUFBLEVBQzVCLENBQUM7QUFBQSxFQUNELE1BQU0sU0FBUyxhQUFhO0FBQUEsSUFDMUI7QUFBQSxJQUNBLFVBQVUsUUFBUTtBQUFBLEVBQ3BCLENBQUM7QUFBQSxFQUNELE9BQU87QUFBQSxJQUNMO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxFQUNGO0FBQUE7IiwKICAiZGVidWdJZCI6ICJEREM4QkU3OTM1RTc3RjZENjQ3NTZFMjE2NDc1NkUyMSIsCiAgIm5hbWVzIjogW10KfQ==