@edkstack/files 0.1.1 → 0.1.3

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.
@@ -1,4 +1,4 @@
1
- import { BunSQLDatabase as BunSQLDatabase2 } from "drizzle-orm/bun-sql";
1
+ import { PgDatabase as PgDatabase2 } from "drizzle-orm/pg-core";
2
2
  type Visibility = "private" | "public";
3
3
  interface PurposePolicy {
4
4
  maxSize?: number;
@@ -7,8 +7,8 @@ interface PurposePolicy {
7
7
  }
8
8
  import { S3Client as S3Client2 } from "bun";
9
9
  interface FilesBackendOptions<TPurposes extends string> {
10
- db: BunSQLDatabase2;
11
- client: S3Client2;
10
+ db: PgDatabase2<any, any, any>;
11
+ s3Client: S3Client2;
12
12
  policies: Record<TPurposes, PurposePolicy>;
13
13
  publicBaseUrl: string;
14
14
  presignExpiresIn?: number;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createFilesBackend
3
- } from "../shared/chunk-5g4ke10y.js";
3
+ } from "../shared/chunk-99btckfr.js";
4
4
  export {
5
5
  createFilesBackend
6
6
  };
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from "react";
2
2
  import { Treaty } from "@elysiajs/eden";
3
- import { BunSQLDatabase as BunSQLDatabase2 } from "drizzle-orm/bun-sql";
3
+ import { PgDatabase as PgDatabase2 } from "drizzle-orm/pg-core";
4
4
  type Visibility = "private" | "public";
5
5
  interface PurposePolicy {
6
6
  maxSize?: number;
@@ -9,8 +9,8 @@ interface PurposePolicy {
9
9
  }
10
10
  import { S3Client as S3Client2 } from "bun";
11
11
  interface FilesBackendOptions<TPurposes extends string> {
12
- db: BunSQLDatabase2;
13
- client: S3Client2;
12
+ db: PgDatabase2<any, any, any>;
13
+ s3Client: S3Client2;
14
14
  policies: Record<TPurposes, PurposePolicy>;
15
15
  publicBaseUrl: string;
16
16
  presignExpiresIn?: number;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BunSQLDatabase as BunSQLDatabase2 } from "drizzle-orm/bun-sql";
1
+ import { PgDatabase as PgDatabase2 } from "drizzle-orm/pg-core";
2
2
  type Visibility = "private" | "public";
3
3
  interface PurposePolicy {
4
4
  maxSize?: number;
@@ -7,8 +7,8 @@ interface PurposePolicy {
7
7
  }
8
8
  import { S3Client as S3Client2 } from "bun";
9
9
  interface FilesBackendOptions<TPurposes extends string> {
10
- db: BunSQLDatabase2;
11
- client: S3Client2;
10
+ db: PgDatabase2<any, any, any>;
11
+ s3Client: S3Client2;
12
12
  policies: Record<TPurposes, PurposePolicy>;
13
13
  publicBaseUrl: string;
14
14
  presignExpiresIn?: number;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createFilesBackend
3
- } from "./shared/chunk-5g4ke10y.js";
3
+ } from "./shared/chunk-99btckfr.js";
4
4
  export {
5
5
  createFilesBackend
6
6
  };
@@ -0,0 +1,202 @@
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.s3Client,
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 { createFilesBackend };
200
+
201
+ //# debugId=0CF1199D7469E2DE64756E2164756E21
202
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjXFxiYWNrZW5kXFxyb3V0ZXMudHMiLCAic3JjXFxiYWNrZW5kXFxzY2hlbWFzLnRzIiwgInNyY1xcYmFja2VuZFxcc2VydmljZXMudHMiLCAic3JjXFxiYWNrZW5kXFxiYWNrZW5kLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWwogICAgImltcG9ydCB7IEVseXNpYSwgdCB9IGZyb20gXCJlbHlzaWFcIjtcclxuaW1wb3J0IHR5cGUgeyBTZXJ2aWNlcyB9IGZyb20gXCIuL3NlcnZpY2VzXCI7XHJcblxyXG5leHBvcnQgdHlwZSBWaXNpYmlsaXR5ID0gXCJwcml2YXRlXCIgfCBcInB1YmxpY1wiO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBQdXJwb3NlUG9saWN5IHtcclxuICBtYXhTaXplPzogbnVtYmVyO1xyXG4gIGFsbG93ZWRNaW1lVHlwZXM/OiBzdHJpbmdbXTtcclxuICB2aXNpYmlsaXR5PzogVmlzaWJpbGl0eTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVJvdXRlczxjb25zdCBUUHVycG9zZSBleHRlbmRzIHN0cmluZz4oXHJcbiAgb3B0aW9uczoge1xyXG4gICAgc2VydmljZXM6IFNlcnZpY2VzO1xyXG4gICAgcG9saWNpZXM6IFJlY29yZDxUUHVycG9zZSwgUHVycG9zZVBvbGljeT5cclxuICB9XHJcbikge1xyXG4gIFxyXG4gIGNvbnN0IHsgXHJcbiAgICBzZXJ2aWNlcywgXHJcbiAgICBwb2xpY2llcyxcclxuICB9ID0gb3B0aW9ucztcclxuXHJcbiAgcmV0dXJuIG5ldyBFbHlzaWEoe1xyXG4gICAgcHJlZml4OiBcIi9hcGlcIixcclxuICB9KS5wb3N0KFwiL2ZpbGVzL3VwbG9hZFwiLCBhc3luYyAoeyBzdGF0dXMsIGJvZHkgfSkgPT4ge1xyXG4gICAgY29uc3QgeyBmaWxlLCBwdXJwb3NlIH0gPSBib2R5O1xyXG4gICAgY29uc3QgcG9saWN5ID0gcG9saWNpZXNbcHVycG9zZSBhcyBUUHVycG9zZV07XHJcbiAgICBpZiAoIXBvbGljeSkge1xyXG4gICAgICByZXR1cm4gc3RhdHVzKDQwMCwge1xyXG4gICAgICAgIG1lc3NhZ2U6IFwiUHVycG9zZSBub3Qgc3VwcG9ydGVkXCIsXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgaWYgKHBvbGljeS5tYXhTaXplICE9PSB1bmRlZmluZWQgJiYgZmlsZS5zaXplID4gcG9saWN5Lm1heFNpemUpIHtcclxuICAgICAgcmV0dXJuIHN0YXR1cyg0MDAsIHtcclxuICAgICAgICBtZXNzYWdlOiBcIkZpbGUgc2l6ZSBleGNlZWRzIHRoZSBtYXhpbXVtIGFsbG93ZWQgc2l6ZVwiLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuICAgIGlmIChwb2xpY3kuYWxsb3dlZE1pbWVUeXBlcyAhPT0gdW5kZWZpbmVkICYmICFwb2xpY3kuYWxsb3dlZE1pbWVUeXBlcy5pbmNsdWRlcyhmaWxlLnR5cGUpKSB7XHJcbiAgICAgIHJldHVybiBzdGF0dXMoNDAwLCB7XHJcbiAgICAgICAgbWVzc2FnZTogXCJGaWxlIHR5cGUgbm90IGFsbG93ZWRcIixcclxuICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBjb25zdCB1cGxvYWRlZCA9IGF3YWl0IHNlcnZpY2VzLnVwbG9hZEZpbGUoeyBcclxuICAgICAgZmlsZSwgXHJcbiAgICAgIHB1cnBvc2UsIFxyXG4gICAgICB2aXNpYmlsaXR5OiBwb2xpY3kudmlzaWJpbGl0eSA/PyBcInByaXZhdGVcIiBcclxuICAgIH0pO1xyXG4gICAgcmV0dXJuIHN0YXR1cygyMDAsIHtcclxuICAgICAgaWQ6IHVwbG9hZGVkLmlkLFxyXG4gICAgICBuYW1lOiB1cGxvYWRlZC5uYW1lLFxyXG4gICAgICBrZXk6IHVwbG9hZGVkLmtleSxcclxuICAgICAgc2l6ZTogdXBsb2FkZWQuc2l6ZSxcclxuICAgICAgbWltZVR5cGU6IHVwbG9hZGVkLm1pbWVUeXBlLFxyXG4gICAgICBjcmVhdGVkQXQ6IHVwbG9hZGVkLmNyZWF0ZWRBdCxcclxuICAgIH0pO1xyXG4gIH0sIHtcclxuICAgIGJvZHk6IFVwbG9hZFJlcXVlc3QoT2JqZWN0LmtleXMocG9saWNpZXMpIGFzIFRQdXJwb3NlW10pLFxyXG4gICAgcmVzcG9uc2U6IHtcclxuICAgICAgMjAwOiBGaWxlUmVzcG9uc2UsXHJcbiAgICAgIDQwMDogRXJyb3JSZXNwb25zZSxcclxuICAgICAgNTAwOiBFcnJvclJlc3BvbnNlLFxyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcblxyXG5mdW5jdGlvbiBVcGxvYWRSZXF1ZXN0PGNvbnN0IFRQdXJwb3NlIGV4dGVuZHMgc3RyaW5nPihcclxuICBwdXJwb3NlczogVFB1cnBvc2VbXVxyXG4pIHtcclxuICByZXR1cm4gdC5PYmplY3Qoe1xyXG4gICAgZmlsZTogdC5GaWxlKCksXHJcbiAgICBwdXJwb3NlOiB0LlVuaW9uKFxyXG4gICAgICBwdXJwb3Nlcy5tYXAoKHApID0+IHQuTGl0ZXJhbChwKSkgYXMgW1xyXG4gICAgICAgIFJldHVyblR5cGU8dHlwZW9mIHQuTGl0ZXJhbDxUUHVycG9zZT4+LFxyXG4gICAgICAgIC4uLlJldHVyblR5cGU8dHlwZW9mIHQuTGl0ZXJhbDxUUHVycG9zZT4+W10sXHJcbiAgICAgIF1cclxuICAgIClcclxuICB9KTtcclxufVxyXG5cclxuY29uc3QgRXJyb3JSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBtZXNzYWdlOiB0LlN0cmluZygpLFxyXG59KTtcclxuXHJcbmNvbnN0IEZpbGVSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBpZDogdC5TdHJpbmcoKSxcclxuICBuYW1lOiB0Lk51bGxhYmxlKHQuU3RyaW5nKCkpLFxyXG4gIGtleTogdC5TdHJpbmcoKSxcclxuICBzaXplOiB0Lk51bWJlcigpLFxyXG4gIG1pbWVUeXBlOiB0LlN0cmluZygpLFxyXG4gIGNyZWF0ZWRBdDogdC5EYXRlKCksXHJcbn0pOyIsCiAgICAiaW1wb3J0IHsgbmFub2lkIH0gZnJvbSBcIm5hbm9pZFwiO1xyXG5pbXBvcnQgeyBpbmRleCwgaW50ZWdlciwgcGdUYWJsZSwgdGV4dCwgdGltZXN0YW1wLCBwZ0VudW0gfSBmcm9tIFwiZHJpenpsZS1vcm0vcGctY29yZVwiO1xyXG5cclxuXHJcbmV4cG9ydCB0eXBlIEFsbFNjaGVtYXMgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVTY2hlbWFzPjtcclxuZXhwb3J0IHR5cGUgRmlsZVNjaGVtYSA9IEFsbFNjaGVtYXNbXCJmaWxlc1wiXTtcclxuZXhwb3J0IHR5cGUgRmlsZVNlbGVjdCA9IEZpbGVTY2hlbWFbXCIkaW5mZXJTZWxlY3RcIl07XHJcbmV4cG9ydCB0eXBlIEZpbGVJbnNlcnQgPSBGaWxlU2NoZW1hW1wiJGluZmVySW5zZXJ0XCJdO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVNjaGVtYXMoKSB7XHJcblxyXG4gIGNvbnN0IGZpbGVzID0gcGdUYWJsZShcImZpbGVzXCIsIHtcclxuICAgIGlkOiB0ZXh0KFwiaWRcIikucHJpbWFyeUtleSgpLiRkZWZhdWx0Rm4oKCkgPT4gYGZpbGVfJHtuYW5vaWQoKX1gKSxcclxuICAgIHB1cnBvc2U6IHRleHQoXCJwdXJwb3NlXCIpLm5vdE51bGwoKSxcclxuICAgIG5hbWU6IHRleHQoXCJuYW1lXCIpLFxyXG4gICAga2V5OiB0ZXh0KFwia2V5XCIpLm5vdE51bGwoKS51bmlxdWUoKSxcclxuICAgIHNpemU6IGludGVnZXIoXCJzaXplXCIpLm5vdE51bGwoKSxcclxuICAgIG1pbWVUeXBlOiB0ZXh0KFwibWltZV90eXBlXCIpLm5vdE51bGwoKS5kZWZhdWx0KFwiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtXCIpLFxyXG4gICAgcmVmQ291bnQ6IGludGVnZXIoXCJyZWZfY291bnRcIikubm90TnVsbCgpLmRlZmF1bHQoMCksXHJcbiAgICB2aXNpYmlsaXR5OiBwZ0VudW0oXCJ2aXNpYmlsaXR5XCIsIFtcInByaXZhdGVcIiwgXCJwdWJsaWNcIl0pKCkubm90TnVsbCgpLmRlZmF1bHQoXCJwcml2YXRlXCIpLFxyXG4gICAgY3JlYXRlZEF0OiB0aW1lc3RhbXAoXCJjcmVhdGVkX2F0XCIpLm5vdE51bGwoKS5kZWZhdWx0Tm93KCksXHJcbiAgICB1cGRhdGVkQXQ6IHRpbWVzdGFtcChcInVwZGF0ZWRfYXRcIikubm90TnVsbCgpLmRlZmF1bHROb3coKSxcclxuICB9LCAodGFibGUpID0+IFtcclxuICAgIGluZGV4KFwiZmlsZXNfa2V5X2lkeFwiKS5vbih0YWJsZS5rZXkpLFxyXG4gICAgaW5kZXgoXCJmaWxlc19jcmVhdGVkX2F0X2lkeFwiKS5vbih0YWJsZS5jcmVhdGVkQXQpLFxyXG4gIF0pO1xyXG5cclxuICByZXR1cm4ge1xyXG4gICAgZmlsZXMsXHJcbiAgfVxyXG59IiwKICAgICJpbXBvcnQgdHlwZSB7IFMzQ2xpZW50IH0gZnJvbSBcImJ1blwiO1xyXG5pbXBvcnQgeyBuYW5vaWQgfSBmcm9tIFwibmFub2lkXCI7XHJcbmltcG9ydCB7IGV4dG5hbWUgfSBmcm9tIFwicGF0aFwiO1xyXG5pbXBvcnQgeyBlcSwgc3FsLCBsdCwgYW5kIH0gZnJvbSBcImRyaXp6bGUtb3JtXCI7XHJcbmltcG9ydCB0eXBlIHsgQWxsU2NoZW1hcywgRmlsZVNlbGVjdCB9IGZyb20gXCIuL3NjaGVtYXNcIjtcclxuaW1wb3J0IHR5cGUgeyBQZ0RhdGFiYXNlIH0gZnJvbSBcImRyaXp6bGUtb3JtL3BnLWNvcmVcIjtcclxuXHJcbmV4cG9ydCB0eXBlIFNlcnZpY2VzID0gUmV0dXJuVHlwZTx0eXBlb2YgY3JlYXRlU2VydmljZXM+O1xyXG5cclxudHlwZSBCeUlkID0geyBpZDogc3RyaW5nIH07XHJcbnR5cGUgQnlLZXkgPSB7IGtleTogc3RyaW5nIH07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU2VydmljZXMoXHJcbiAgb3B0aW9uczoge1xyXG4gICAgZGI6IFBnRGF0YWJhc2U8YW55LCBhbnksIGFueT47XHJcbiAgICBzY2hlbWFzOiBBbGxTY2hlbWFzO1xyXG4gICAgczNDbGllbnQ6IFMzQ2xpZW50O1xyXG4gICAgcHVibGljQmFzZVVybDogc3RyaW5nO1xyXG4gICAga2V5UHJlZml4Pzogc3RyaW5nO1xyXG4gICAgcHJlc2lnbkV4cGlyZXNJbj86IG51bWJlcjtcclxuICB9XHJcbikge1xyXG4gIGNvbnN0IHsgXHJcbiAgICBkYiwgXHJcbiAgICBzY2hlbWFzLCBcclxuICAgIHMzQ2xpZW50LCBcclxuICAgIHB1YmxpY0Jhc2VVcmwsIFxyXG4gICAga2V5UHJlZml4ID0gXCJmaWxlc1wiLFxyXG4gICAgcHJlc2lnbkV4cGlyZXNJbiA9IDM2MDBcclxuICB9ID0gb3B0aW9ucztcclxuICBcclxuICByZXR1cm4ge1xyXG5cclxuICAgIHMzQ2xpZW50LFxyXG5cclxuICAgIGFzeW5jIGdldEZpbGUocGFyYW1zOiBCeUlkKTogUHJvbWlzZTxGaWxlU2VsZWN0PiB7XHJcbiAgICAgIGNvbnN0IFtmb3VuZF0gPSBhd2FpdCBkYlxyXG4gICAgICAgIC5zZWxlY3QoKVxyXG4gICAgICAgIC5mcm9tKHNjaGVtYXMuZmlsZXMpXHJcbiAgICAgICAgLndoZXJlKGVxKHNjaGVtYXMuZmlsZXMuaWQsIHBhcmFtcy5pZCkpXHJcbiAgICAgICAgLmxpbWl0KDEpO1xyXG4gICAgICBpZiAoIWZvdW5kKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmlsZSBub3QgZm91bmRcIik7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGZvdW5kO1xyXG4gICAgfSxcclxuXHJcbiAgICBhc3luYyBnZXRVcmwocGFyYW1zOiBCeUlkKTogUHJvbWlzZTxzdHJpbmc+IHtcclxuICAgICAgY29uc3QgW2ZvdW5kXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLnNlbGVjdCh7IFxyXG4gICAgICAgICAga2V5OiBzY2hlbWFzLmZpbGVzLmtleSxcclxuICAgICAgICAgIHZpc2liaWxpdHk6IHNjaGVtYXMuZmlsZXMudmlzaWJpbGl0eSxcclxuICAgICAgICB9KVxyXG4gICAgICAgIC5mcm9tKHNjaGVtYXMuZmlsZXMpXHJcbiAgICAgICAgLndoZXJlKGVxKHNjaGVtYXMuZmlsZXMuaWQsIHBhcmFtcy5pZCkpXHJcbiAgICAgICAgLmxpbWl0KDEpO1xyXG4gICAgICBpZiAoIWZvdW5kKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmlsZSBub3QgZm91bmRcIik7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKGZvdW5kLnZpc2liaWxpdHkgPT09IFwicHVibGljXCIpIHtcclxuICAgICAgICByZXR1cm4gYCR7cHVibGljQmFzZVVybH0vJHtmb3VuZC5rZXl9YDtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gczNDbGllbnQucHJlc2lnbihmb3VuZC5rZXksIHsgZXhwaXJlc0luOiBwcmVzaWduRXhwaXJlc0luIH0pO1xyXG4gICAgfSxcclxuXHJcbiAgICBhc3luYyB1cGxvYWRGaWxlKHBhcmFtczoge1xyXG4gICAgICBmaWxlOiBGaWxlO1xyXG4gICAgICBwdXJwb3NlOiBzdHJpbmc7XHJcbiAgICAgIHZpc2liaWxpdHk6IFwicHJpdmF0ZVwiIHwgXCJwdWJsaWNcIjtcclxuICAgIH0pOiBQcm9taXNlPEZpbGVTZWxlY3Q+IHtcclxuICAgICAgY29uc3QgaWQgPSBuYW5vaWQoKTtcclxuICAgICAgY29uc3QgZXh0ID0gZXh0bmFtZShwYXJhbXMuZmlsZS5uYW1lKTtcclxuICAgICAgY29uc3Qga2V5ID0gW2tleVByZWZpeCwgcGFyYW1zLnZpc2liaWxpdHksIHBhcmFtcy5wdXJwb3NlLCBgJHtpZH0ke2V4dH1gXS5qb2luKFwiL1wiKTtcclxuICAgICAgY29uc3QgczNmaWxlID0gczNDbGllbnQuZmlsZShrZXkpO1xyXG4gICAgICBhd2FpdCBzM2ZpbGUud3JpdGUocGFyYW1zLmZpbGUsIHtcclxuICAgICAgICB0eXBlOiBwYXJhbXMuZmlsZS50eXBlLFxyXG4gICAgICAgIGFjbDogcGFyYW1zLnZpc2liaWxpdHkgPT09IFwicHVibGljXCIgPyBcInB1YmxpYy1yZWFkXCIgOiBcInByaXZhdGVcIixcclxuICAgICAgfSk7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgW2NyZWF0ZWRdID0gYXdhaXQgZGIuaW5zZXJ0KHNjaGVtYXMuZmlsZXMpXHJcbiAgICAgICAgICAudmFsdWVzKHtcclxuICAgICAgICAgICAgcHVycG9zZTogcGFyYW1zLnB1cnBvc2UsXHJcbiAgICAgICAgICAgIGtleSxcclxuICAgICAgICAgICAgc2l6ZTogczNmaWxlLnNpemUsXHJcbiAgICAgICAgICAgIG5hbWU6IHMzZmlsZS5uYW1lID8/IG51bGwsXHJcbiAgICAgICAgICAgIG1pbWVUeXBlOiBzM2ZpbGUudHlwZSxcclxuICAgICAgICAgICAgdmlzaWJpbGl0eTogcGFyYW1zLnZpc2liaWxpdHksXHJcbiAgICAgICAgICB9KVxyXG4gICAgICAgICAgLnJldHVybmluZygpO1xyXG4gICAgICAgIGlmICghY3JlYXRlZCkge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmFpbGVkIHRvIGNyZWF0ZSBmaWxlIHJlY29yZFwiKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIGNyZWF0ZWQ7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgYXdhaXQgczNmaWxlLmRlbGV0ZSgpLmNhdGNoKCk7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgZGVsZXRlRmlsZShwYXJhbXM6IHsgaWQ6IHN0cmluZyB9KTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgIGNvbnN0IFtkZWxldGVkXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLmRlbGV0ZShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgaWYgKCFkZWxldGVkKSByZXR1cm47XHJcbiAgICAgIGF3YWl0IHMzQ2xpZW50LmRlbGV0ZShkZWxldGVkLmtleSk7XHJcbiAgICB9LFxyXG5cclxuICAgIGFzeW5jIGFjcXVpcmVGaWxlKHBhcmFtczogQnlJZCAmIHsgXHJcbiAgICAgIHB1cnBvc2U/OiBzdHJpbmcgXHJcbiAgICB9KTogUHJvbWlzZTxGaWxlU2VsZWN0PiB7XHJcbiAgICAgIGNvbnN0IFt1cGRhdGVkXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLnVwZGF0ZShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC5zZXQoeyByZWZDb3VudDogc3FsYCR7c2NoZW1hcy5maWxlcy5yZWZDb3VudH0gKyAxYCB9KVxyXG4gICAgICAgIC53aGVyZShcclxuICAgICAgICAgIGFuZChcclxuICAgICAgICAgICAgZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSxcclxuICAgICAgICAgICAgcGFyYW1zLnB1cnBvc2UgPyBlcShzY2hlbWFzLmZpbGVzLnB1cnBvc2UsIHBhcmFtcy5wdXJwb3NlKSA6IHVuZGVmaW5lZCxcclxuICAgICAgICAgIClcclxuICAgICAgICApXHJcbiAgICAgICAgLnJldHVybmluZygpO1xyXG4gICAgICBpZiAoIXVwZGF0ZWQpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJGaWxlIG5vdCBmb3VuZFwiKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdXBkYXRlZDtcclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgcmVsZWFzZUZpbGUocGFyYW1zOiBCeUlkKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgIGNvbnN0IFt1cGRhdGVkXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLnVwZGF0ZShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC5zZXQoeyByZWZDb3VudDogc3FsYCR7c2NoZW1hcy5maWxlcy5yZWZDb3VudH0gLSAxYCB9KVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgaWYgKCF1cGRhdGVkKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmlsZSBub3QgZm91bmRcIik7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKHVwZGF0ZWQucmVmQ291bnQgPCAxKSB7XHJcbiAgICAgICAgYXdhaXQgdGhpcy5kZWxldGVGaWxlKHsgaWQ6IHBhcmFtcy5pZCB9KTtcclxuICAgICAgfVxyXG4gICAgfSxcclxuICB9O1xyXG59IiwKICAgICJpbXBvcnQgdHlwZSB7IFBnRGF0YWJhc2UgfSBmcm9tIFwiZHJpenpsZS1vcm0vcGctY29yZVwiO1xyXG5pbXBvcnQgeyBjcmVhdGVSb3V0ZXMsIHR5cGUgUHVycG9zZVBvbGljeSB9IGZyb20gXCIuL3JvdXRlc1wiO1xyXG5pbXBvcnQgeyBjcmVhdGVTY2hlbWFzIH0gZnJvbSBcIi4vc2NoZW1hc1wiO1xyXG5pbXBvcnQgeyBjcmVhdGVTZXJ2aWNlcyB9IGZyb20gXCIuL3NlcnZpY2VzXCI7XHJcbmltcG9ydCB0eXBlIHsgUzNDbGllbnQgfSBmcm9tIFwiYnVuXCI7XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEZpbGVzQmFja2VuZE9wdGlvbnM8VFB1cnBvc2VzIGV4dGVuZHMgc3RyaW5nPiB7XHJcbiAgZGI6IFBnRGF0YWJhc2U8YW55LCBhbnksIGFueT47XHJcbiAgczNDbGllbnQ6IFMzQ2xpZW50O1xyXG4gIHBvbGljaWVzOiBSZWNvcmQ8VFB1cnBvc2VzLCBQdXJwb3NlUG9saWN5PjtcclxuICBwdWJsaWNCYXNlVXJsOiBzdHJpbmc7XHJcbiAgcHJlc2lnbkV4cGlyZXNJbj86IG51bWJlcjtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUZpbGVzQmFja2VuZDxjb25zdCBUUHVycG9zZSBleHRlbmRzIHN0cmluZz4oXHJcbiAgb3B0aW9uczogRmlsZXNCYWNrZW5kT3B0aW9uczxUUHVycG9zZT5cclxuKSB7XHJcbiAgY29uc3Qgc2NoZW1hcyA9IGNyZWF0ZVNjaGVtYXMoKTtcclxuICBjb25zdCBzZXJ2aWNlcyA9IGNyZWF0ZVNlcnZpY2VzKHtcclxuICAgIGRiOiBvcHRpb25zLmRiLFxyXG4gICAgc2NoZW1hcyxcclxuICAgIHMzQ2xpZW50OiBvcHRpb25zLnMzQ2xpZW50LFxyXG4gICAgcHVibGljQmFzZVVybDogb3B0aW9ucy5wdWJsaWNCYXNlVXJsLFxyXG4gICAgcHJlc2lnbkV4cGlyZXNJbjogb3B0aW9ucy5wcmVzaWduRXhwaXJlc0luLFxyXG4gIH0pO1xyXG4gIGNvbnN0IHJvdXRlcyA9IGNyZWF0ZVJvdXRlcyh7XHJcbiAgICBzZXJ2aWNlcyxcclxuICAgIHBvbGljaWVzOiBvcHRpb25zLnBvbGljaWVzLFxyXG4gIH0pO1xyXG4gIHJldHVybiB7XHJcbiAgICBzY2hlbWFzLFxyXG4gICAgcm91dGVzLFxyXG4gICAgc2VydmljZXMsXHJcbiAgfSBhcyBjb25zdDtcclxufSIKICBdLAogICJtYXBwaW5ncyI6ICI7QUFBQTtBQVdPLFNBQVMsWUFBMkMsQ0FDekQsU0FJQTtBQUFBLEVBRUE7QUFBQSxJQUNFO0FBQUEsSUFDQTtBQUFBLE1BQ0U7QUFBQSxFQUVKLE9BQU8sSUFBSSxPQUFPO0FBQUEsSUFDaEIsUUFBUTtBQUFBLEVBQ1YsQ0FBQyxFQUFFLEtBQUssaUJBQWlCLFNBQVMsUUFBUSxXQUFXO0FBQUEsSUFDbkQsUUFBUSxNQUFNLFlBQVk7QUFBQSxJQUMxQixNQUFNLFNBQVMsU0FBUztBQUFBLElBQ3hCLElBQUksQ0FBQyxRQUFRO0FBQUEsTUFDWCxPQUFPLE9BQU8sS0FBSztBQUFBLFFBQ2pCLFNBQVM7QUFBQSxNQUNYLENBQUM7QUFBQSxJQUNIO0FBQUEsSUFDQSxJQUFJLE9BQU8sWUFBWSxhQUFhLEtBQUssT0FBTyxPQUFPLFNBQVM7QUFBQSxNQUM5RCxPQUFPLE9BQU8sS0FBSztBQUFBLFFBQ2pCLFNBQVM7QUFBQSxNQUNYLENBQUM7QUFBQSxJQUNIO0FBQUEsSUFDQSxJQUFJLE9BQU8scUJBQXFCLGFBQWEsQ0FBQyxPQUFPLGlCQUFpQixTQUFTLEtBQUssSUFBSSxHQUFHO0FBQUEsTUFDekYsT0FBTyxPQUFPLEtBQUs7QUFBQSxRQUNqQixTQUFTO0FBQUEsTUFDWCxDQUFDO0FBQUEs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QUFBQSxTQUdILFlBQVcsQ0FBQyxRQUE2QjtBQUFBLE1BQzdDLE9BQU8sV0FBVyxNQUFNLEdBQ3JCLE9BQU8sUUFBUSxLQUFLLEVBQ3BCLElBQUksRUFBRSxVQUFVLE1BQU0sUUFBUSxNQUFNLGVBQWUsQ0FBQyxFQUNwRCxNQUFNLEdBQUcsUUFBUSxNQUFNLElBQUksT0FBTyxFQUFFLENBQUMsRUFDckMsVUFBVTtBQUFBLE1BQ2IsSUFBSSxDQUFDLFNBQVM7QUFBQSxRQUNaLE1BQU0sSUFBSSxNQUFNLGdCQUFnQjtBQUFBLE1BQ2xDO0FBQUEsTUFDQSxJQUFJLFFBQVEsV0FBVyxHQUFHO0FBQUEsUUFDeEIsTUFBTSxLQUFLLFdBQVcsRUFBRSxJQUFJLE9BQU8sR0FBRyxDQUFDO0FBQUEsTUFDekM7QUFBQTtBQUFBLEVBRUo7QUFBQTs7O0FDOUhLLFNBQVMsa0JBQWlELENBQy9ELFNBQ0E7QUFBQSxFQUNBLE1BQU0sVUFBVSxjQUFjO0FBQUEsRUFDOUIsTUFBTSxXQUFXLGVBQWU7QUFBQSxJQUM5QixJQUFJLFFBQVE7QUFBQSxJQUNaO0FBQUEsSUFDQSxVQUFVLFFBQVE7QUFBQSxJQUNsQixlQUFlLFFBQVE7QUFBQSxJQUN2QixrQkFBa0IsUUFBUTtBQUFBLEVBQzVCLENBQUM7QUFBQSxFQUNELE1BQU0sU0FBUyxhQUFhO0FBQUEsSUFDMUI7QUFBQSxJQUNBLFVBQVUsUUFBUTtBQUFBLEVBQ3BCLENBQUM7QUFBQSxFQUNELE9BQU87QUFBQSxJQUNMO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxFQUNGO0FBQUE7IiwKICAiZGVidWdJZCI6ICIwQ0YxMTk5RDc0NjlFMkRFNjQ3NTZFMjE2NDc1NkUyMSIsCiAgIm5hbWVzIjogW10KfQ==
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@edkstack/files",
3
3
  "description": "File management utilities for EDK Stack",
4
- "version": "0.1.1",
4
+ "version": "0.1.3",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -1,202 +0,0 @@
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 { createFilesBackend };
200
-
201
- //# debugId=A718F9B93FD6F67B64756E2164756E21
202
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjXFxiYWNrZW5kXFxyb3V0ZXMudHMiLCAic3JjXFxiYWNrZW5kXFxzY2hlbWFzLnRzIiwgInNyY1xcYmFja2VuZFxcc2VydmljZXMudHMiLCAic3JjXFxiYWNrZW5kXFxiYWNrZW5kLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWwogICAgImltcG9ydCB7IEVseXNpYSwgdCB9IGZyb20gXCJlbHlzaWFcIjtcclxuaW1wb3J0IHR5cGUgeyBTZXJ2aWNlcyB9IGZyb20gXCIuL3NlcnZpY2VzXCI7XHJcblxyXG5leHBvcnQgdHlwZSBWaXNpYmlsaXR5ID0gXCJwcml2YXRlXCIgfCBcInB1YmxpY1wiO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBQdXJwb3NlUG9saWN5IHtcclxuICBtYXhTaXplPzogbnVtYmVyO1xyXG4gIGFsbG93ZWRNaW1lVHlwZXM/OiBzdHJpbmdbXTtcclxuICB2aXNpYmlsaXR5PzogVmlzaWJpbGl0eTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVJvdXRlczxjb25zdCBUUHVycG9zZSBleHRlbmRzIHN0cmluZz4oXHJcbiAgb3B0aW9uczoge1xyXG4gICAgc2VydmljZXM6IFNlcnZpY2VzO1xyXG4gICAgcG9saWNpZXM6IFJlY29yZDxUUHVycG9zZSwgUHVycG9zZVBvbGljeT5cclxuICB9XHJcbikge1xyXG4gIFxyXG4gIGNvbnN0IHsgXHJcbiAgICBzZXJ2aWNlcywgXHJcbiAgICBwb2xpY2llcyxcclxuICB9ID0gb3B0aW9ucztcclxuXHJcbiAgcmV0dXJuIG5ldyBFbHlzaWEoe1xyXG4gICAgcHJlZml4OiBcIi9hcGlcIixcclxuICB9KS5wb3N0KFwiL2ZpbGVzL3VwbG9hZFwiLCBhc3luYyAoeyBzdGF0dXMsIGJvZHkgfSkgPT4ge1xyXG4gICAgY29uc3QgeyBmaWxlLCBwdXJwb3NlIH0gPSBib2R5O1xyXG4gICAgY29uc3QgcG9saWN5ID0gcG9saWNpZXNbcHVycG9zZSBhcyBUUHVycG9zZV07XHJcbiAgICBpZiAoIXBvbGljeSkge1xyXG4gICAgICByZXR1cm4gc3RhdHVzKDQwMCwge1xyXG4gICAgICAgIG1lc3NhZ2U6IFwiUHVycG9zZSBub3Qgc3VwcG9ydGVkXCIsXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgaWYgKHBvbGljeS5tYXhTaXplICE9PSB1bmRlZmluZWQgJiYgZmlsZS5zaXplID4gcG9saWN5Lm1heFNpemUpIHtcclxuICAgICAgcmV0dXJuIHN0YXR1cyg0MDAsIHtcclxuICAgICAgICBtZXNzYWdlOiBcIkZpbGUgc2l6ZSBleGNlZWRzIHRoZSBtYXhpbXVtIGFsbG93ZWQgc2l6ZVwiLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuICAgIGlmIChwb2xpY3kuYWxsb3dlZE1pbWVUeXBlcyAhPT0gdW5kZWZpbmVkICYmICFwb2xpY3kuYWxsb3dlZE1pbWVUeXBlcy5pbmNsdWRlcyhmaWxlLnR5cGUpKSB7XHJcbiAgICAgIHJldHVybiBzdGF0dXMoNDAwLCB7XHJcbiAgICAgICAgbWVzc2FnZTogXCJGaWxlIHR5cGUgbm90IGFsbG93ZWRcIixcclxuICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBjb25zdCB1cGxvYWRlZCA9IGF3YWl0IHNlcnZpY2VzLnVwbG9hZEZpbGUoeyBcclxuICAgICAgZmlsZSwgXHJcbiAgICAgIHB1cnBvc2UsIFxyXG4gICAgICB2aXNpYmlsaXR5OiBwb2xpY3kudmlzaWJpbGl0eSA/PyBcInByaXZhdGVcIiBcclxuICAgIH0pO1xyXG4gICAgcmV0dXJuIHN0YXR1cygyMDAsIHtcclxuICAgICAgaWQ6IHVwbG9hZGVkLmlkLFxyXG4gICAgICBuYW1lOiB1cGxvYWRlZC5uYW1lLFxyXG4gICAgICBrZXk6IHVwbG9hZGVkLmtleSxcclxuICAgICAgc2l6ZTogdXBsb2FkZWQuc2l6ZSxcclxuICAgICAgbWltZVR5cGU6IHVwbG9hZGVkLm1pbWVUeXBlLFxyXG4gICAgICBjcmVhdGVkQXQ6IHVwbG9hZGVkLmNyZWF0ZWRBdCxcclxuICAgIH0pO1xyXG4gIH0sIHtcclxuICAgIGJvZHk6IFVwbG9hZFJlcXVlc3QoT2JqZWN0LmtleXMocG9saWNpZXMpIGFzIFRQdXJwb3NlW10pLFxyXG4gICAgcmVzcG9uc2U6IHtcclxuICAgICAgMjAwOiBGaWxlUmVzcG9uc2UsXHJcbiAgICAgIDQwMDogRXJyb3JSZXNwb25zZSxcclxuICAgICAgNTAwOiBFcnJvclJlc3BvbnNlLFxyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcblxyXG5mdW5jdGlvbiBVcGxvYWRSZXF1ZXN0PGNvbnN0IFRQdXJwb3NlIGV4dGVuZHMgc3RyaW5nPihcclxuICBwdXJwb3NlczogVFB1cnBvc2VbXVxyXG4pIHtcclxuICByZXR1cm4gdC5PYmplY3Qoe1xyXG4gICAgZmlsZTogdC5GaWxlKCksXHJcbiAgICBwdXJwb3NlOiB0LlVuaW9uKFxyXG4gICAgICBwdXJwb3Nlcy5tYXAoKHApID0+IHQuTGl0ZXJhbChwKSkgYXMgW1xyXG4gICAgICAgIFJldHVyblR5cGU8dHlwZW9mIHQuTGl0ZXJhbDxUUHVycG9zZT4+LFxyXG4gICAgICAgIC4uLlJldHVyblR5cGU8dHlwZW9mIHQuTGl0ZXJhbDxUUHVycG9zZT4+W10sXHJcbiAgICAgIF1cclxuICAgIClcclxuICB9KTtcclxufVxyXG5cclxuY29uc3QgRXJyb3JSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBtZXNzYWdlOiB0LlN0cmluZygpLFxyXG59KTtcclxuXHJcbmNvbnN0IEZpbGVSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBpZDogdC5TdHJpbmcoKSxcclxuICBuYW1lOiB0Lk51bGxhYmxlKHQuU3RyaW5nKCkpLFxyXG4gIGtleTogdC5TdHJpbmcoKSxcclxuICBzaXplOiB0Lk51bWJlcigpLFxyXG4gIG1pbWVUeXBlOiB0LlN0cmluZygpLFxyXG4gIGNyZWF0ZWRBdDogdC5EYXRlKCksXHJcbn0pOyIsCiAgICAiaW1wb3J0IHsgbmFub2lkIH0gZnJvbSBcIm5hbm9pZFwiO1xyXG5pbXBvcnQgeyBpbmRleCwgaW50ZWdlciwgcGdUYWJsZSwgdGV4dCwgdGltZXN0YW1wLCBwZ0VudW0gfSBmcm9tIFwiZHJpenpsZS1vcm0vcGctY29yZVwiO1xyXG5cclxuXHJcbmV4cG9ydCB0eXBlIEFsbFNjaGVtYXMgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVTY2hlbWFzPjtcclxuZXhwb3J0IHR5cGUgRmlsZVNjaGVtYSA9IEFsbFNjaGVtYXNbXCJmaWxlc1wiXTtcclxuZXhwb3J0IHR5cGUgRmlsZVNlbGVjdCA9IEZpbGVTY2hlbWFbXCIkaW5mZXJTZWxlY3RcIl07XHJcbmV4cG9ydCB0eXBlIEZpbGVJbnNlcnQgPSBGaWxlU2NoZW1hW1wiJGluZmVySW5zZXJ0XCJdO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVNjaGVtYXMoKSB7XHJcblxyXG4gIGNvbnN0IGZpbGVzID0gcGdUYWJsZShcImZpbGVzXCIsIHtcclxuICAgIGlkOiB0ZXh0KFwiaWRcIikucHJpbWFyeUtleSgpLiRkZWZhdWx0Rm4oKCkgPT4gYGZpbGVfJHtuYW5vaWQoKX1gKSxcclxuICAgIHB1cnBvc2U6IHRleHQoXCJwdXJwb3NlXCIpLm5vdE51bGwoKSxcclxuICAgIG5hbWU6IHRleHQoXCJuYW1lXCIpLFxyXG4gICAga2V5OiB0ZXh0KFwia2V5XCIpLm5vdE51bGwoKS51bmlxdWUoKSxcclxuICAgIHNpemU6IGludGVnZXIoXCJzaXplXCIpLm5vdE51bGwoKSxcclxuICAgIG1pbWVUeXBlOiB0ZXh0KFwibWltZV90eXBlXCIpLm5vdE51bGwoKS5kZWZhdWx0KFwiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtXCIpLFxyXG4gICAgcmVmQ291bnQ6IGludGVnZXIoXCJyZWZfY291bnRcIikubm90TnVsbCgpLmRlZmF1bHQoMCksXHJcbiAgICB2aXNpYmlsaXR5OiBwZ0VudW0oXCJ2aXNpYmlsaXR5XCIsIFtcInByaXZhdGVcIiwgXCJwdWJsaWNcIl0pKCkubm90TnVsbCgpLmRlZmF1bHQoXCJwcml2YXRlXCIpLFxyXG4gICAgY3JlYXRlZEF0OiB0aW1lc3RhbXAoXCJjcmVhdGVkX2F0XCIpLm5vdE51bGwoKS5kZWZhdWx0Tm93KCksXHJcbiAgICB1cGRhdGVkQXQ6IHRpbWVzdGFtcChcInVwZGF0ZWRfYXRcIikubm90TnVsbCgpLmRlZmF1bHROb3coKSxcclxuICB9LCAodGFibGUpID0+IFtcclxuICAgIGluZGV4KFwiZmlsZXNfa2V5X2lkeFwiKS5vbih0YWJsZS5rZXkpLFxyXG4gICAgaW5kZXgoXCJmaWxlc19jcmVhdGVkX2F0X2lkeFwiKS5vbih0YWJsZS5jcmVhdGVkQXQpLFxyXG4gIF0pO1xyXG5cclxuICByZXR1cm4ge1xyXG4gICAgZmlsZXMsXHJcbiAgfVxyXG59IiwKICAgICJpbXBvcnQgdHlwZSB7IFMzQ2xpZW50LCBTM0ZpbGUgfSBmcm9tIFwiYnVuXCI7XHJcbmltcG9ydCB7IG5hbm9pZCB9IGZyb20gXCJuYW5vaWRcIjtcclxuaW1wb3J0IHsgZXh0bmFtZSB9IGZyb20gXCJwYXRoXCI7XHJcbmltcG9ydCB7IGVxLCBzcWwsIGx0LCBhbmQgfSBmcm9tIFwiZHJpenpsZS1vcm1cIjtcclxuaW1wb3J0IHR5cGUgeyBCdW5TUUxEYXRhYmFzZSB9IGZyb20gXCJkcml6emxlLW9ybS9idW4tc3FsXCI7XHJcbmltcG9ydCB0eXBlIHsgQWxsU2NoZW1hcywgRmlsZVNlbGVjdCB9IGZyb20gXCIuL3NjaGVtYXNcIjtcclxuXHJcbmV4cG9ydCB0eXBlIFNlcnZpY2VzID0gUmV0dXJuVHlwZTx0eXBlb2YgY3JlYXRlU2VydmljZXM+O1xyXG5cclxudHlwZSBCeUlkID0geyBpZDogc3RyaW5nIH07XHJcbnR5cGUgQnlLZXkgPSB7IGtleTogc3RyaW5nIH07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU2VydmljZXMoXHJcbiAgb3B0aW9uczoge1xyXG4gICAgZGI6IEJ1blNRTERhdGFiYXNlO1xyXG4gICAgc2NoZW1hczogQWxsU2NoZW1hcztcclxuICAgIHMzQ2xpZW50OiBTM0NsaWVudDtcclxuICAgIHB1YmxpY0Jhc2VVcmw6IHN0cmluZztcclxuICAgIGtleVByZWZpeD86IHN0cmluZztcclxuICAgIHByZXNpZ25FeHBpcmVzSW4/OiBudW1iZXI7XHJcbiAgfVxyXG4pIHtcclxuICBjb25zdCB7IFxyXG4gICAgZGIsIFxyXG4gICAgc2NoZW1hcywgXHJcbiAgICBzM0NsaWVudCwgXHJcbiAgICBwdWJsaWNCYXNlVXJsLCBcclxuICAgIGtleVByZWZpeCA9IFwiZmlsZXNcIixcclxuICAgIHByZXNpZ25FeHBpcmVzSW4gPSAzNjAwXHJcbiAgfSA9IG9wdGlvbnM7XHJcbiAgXHJcbiAgcmV0dXJuIHtcclxuXHJcbiAgICBzM0NsaWVudCxcclxuXHJcbiAgICBhc3luYyBnZXRGaWxlKHBhcmFtczogQnlJZCk6IFByb21pc2U8RmlsZVNlbGVjdD4ge1xyXG4gICAgICBjb25zdCBbZm91bmRdID0gYXdhaXQgZGJcclxuICAgICAgICAuc2VsZWN0KClcclxuICAgICAgICAuZnJvbShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5saW1pdCgxKTtcclxuICAgICAgaWYgKCFmb3VuZCkge1xyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZpbGUgbm90IGZvdW5kXCIpO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBmb3VuZDtcclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgZ2V0VXJsKHBhcmFtczogQnlJZCk6IFByb21pc2U8c3RyaW5nPiB7XHJcbiAgICAgIGNvbnN0IFtmb3VuZF0gPSBhd2FpdCBkYlxyXG4gICAgICAgIC5zZWxlY3QoeyBcclxuICAgICAgICAgIGtleTogc2NoZW1hcy5maWxlcy5rZXksXHJcbiAgICAgICAgICB2aXNpYmlsaXR5OiBzY2hlbWFzLmZpbGVzLnZpc2liaWxpdHksXHJcbiAgICAgICAgfSlcclxuICAgICAgICAuZnJvbShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5saW1pdCgxKTtcclxuICAgICAgaWYgKCFmb3VuZCkge1xyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZpbGUgbm90IGZvdW5kXCIpO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChmb3VuZC52aXNpYmlsaXR5ID09PSBcInB1YmxpY1wiKSB7XHJcbiAgICAgICAgcmV0dXJuIGAke3B1YmxpY0Jhc2VVcmx9LyR7Zm91bmQua2V5fWA7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHMzQ2xpZW50LnByZXNpZ24oZm91bmQua2V5LCB7IGV4cGlyZXNJbjogcHJlc2lnbkV4cGlyZXNJbiB9KTtcclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgdXBsb2FkRmlsZShwYXJhbXM6IHtcclxuICAgICAgZmlsZTogRmlsZTtcclxuICAgICAgcHVycG9zZTogc3RyaW5nO1xyXG4gICAgICB2aXNpYmlsaXR5OiBcInByaXZhdGVcIiB8IFwicHVibGljXCI7XHJcbiAgICB9KTogUHJvbWlzZTxGaWxlU2VsZWN0PiB7XHJcbiAgICAgIGNvbnN0IGlkID0gbmFub2lkKCk7XHJcbiAgICAgIGNvbnN0IGV4dCA9IGV4dG5hbWUocGFyYW1zLmZpbGUubmFtZSk7XHJcbiAgICAgIGNvbnN0IGtleSA9IFtrZXlQcmVmaXgsIHBhcmFtcy52aXNpYmlsaXR5LCBwYXJhbXMucHVycG9zZSwgYCR7aWR9JHtleHR9YF0uam9pbihcIi9cIik7XHJcbiAgICAgIGNvbnN0IHMzZmlsZSA9IHMzQ2xpZW50LmZpbGUoa2V5KTtcclxuICAgICAgYXdhaXQgczNmaWxlLndyaXRlKHBhcmFtcy5maWxlLCB7XHJcbiAgICAgICAgdHlwZTogcGFyYW1zLmZpbGUudHlwZSxcclxuICAgICAgICBhY2w6IHBhcmFtcy52aXNpYmlsaXR5ID09PSBcInB1YmxpY1wiID8gXCJwdWJsaWMtcmVhZFwiIDogXCJwcml2YXRlXCIsXHJcbiAgICAgIH0pO1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IFtjcmVhdGVkXSA9IGF3YWl0IGRiLmluc2VydChzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgICAgLnZhbHVlcyh7XHJcbiAgICAgICAgICAgIHB1cnBvc2U6IHBhcmFtcy5wdXJwb3NlLFxyXG4gICAgICAgICAgICBrZXksXHJcbiAgICAgICAgICAgIHNpemU6IHMzZmlsZS5zaXplLFxyXG4gICAgICAgICAgICBuYW1lOiBzM2ZpbGUubmFtZSA/PyBudWxsLFxyXG4gICAgICAgICAgICBtaW1lVHlwZTogczNmaWxlLnR5cGUsXHJcbiAgICAgICAgICAgIHZpc2liaWxpdHk6IHBhcmFtcy52aXNpYmlsaXR5LFxyXG4gICAgICAgICAgfSlcclxuICAgICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgICBpZiAoIWNyZWF0ZWQpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZhaWxlZCB0byBjcmVhdGUgZmlsZSByZWNvcmRcIik7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBjcmVhdGVkO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGF3YWl0IHMzZmlsZS5kZWxldGUoKS5jYXRjaCgpO1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIGFzeW5jIGRlbGV0ZUZpbGUocGFyYW1zOiB7IGlkOiBzdHJpbmcgfSk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICBjb25zdCBbZGVsZXRlZF0gPSBhd2FpdCBkYlxyXG4gICAgICAgIC5kZWxldGUoc2NoZW1hcy5maWxlcylcclxuICAgICAgICAud2hlcmUoZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSlcclxuICAgICAgICAucmV0dXJuaW5nKCk7XHJcbiAgICAgIGlmICghZGVsZXRlZCkgcmV0dXJuO1xyXG4gICAgICBhd2FpdCBzM0NsaWVudC5kZWxldGUoZGVsZXRlZC5rZXkpO1xyXG4gICAgfSxcclxuXHJcbiAgICBhc3luYyBhY3F1aXJlRmlsZShwYXJhbXM6IEJ5SWQgJiB7IFxyXG4gICAgICBwdXJwb3NlPzogc3RyaW5nIFxyXG4gICAgfSk6IFByb21pc2U8RmlsZVNlbGVjdD4ge1xyXG4gICAgICBjb25zdCBbdXBkYXRlZF0gPSBhd2FpdCBkYlxyXG4gICAgICAgIC51cGRhdGUoc2NoZW1hcy5maWxlcylcclxuICAgICAgICAuc2V0KHsgcmVmQ291bnQ6IHNxbGAke3NjaGVtYXMuZmlsZXMucmVmQ291bnR9ICsgMWAgfSlcclxuICAgICAgICAud2hlcmUoXHJcbiAgICAgICAgICBhbmQoXHJcbiAgICAgICAgICAgIGVxKHNjaGVtYXMuZmlsZXMuaWQsIHBhcmFtcy5pZCksXHJcbiAgICAgICAgICAgIHBhcmFtcy5wdXJwb3NlID8gZXEoc2NoZW1hcy5maWxlcy5wdXJwb3NlLCBwYXJhbXMucHVycG9zZSkgOiB1bmRlZmluZWQsXHJcbiAgICAgICAgICApXHJcbiAgICAgICAgKVxyXG4gICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgaWYgKCF1cGRhdGVkKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmlsZSBub3QgZm91bmRcIik7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHVwZGF0ZWQ7XHJcbiAgICB9LFxyXG5cclxuICAgIGFzeW5jIHJlbGVhc2VGaWxlKHBhcmFtczogQnlJZCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICBjb25zdCBbdXBkYXRlZF0gPSBhd2FpdCBkYlxyXG4gICAgICAgIC51cGRhdGUoc2NoZW1hcy5maWxlcylcclxuICAgICAgICAuc2V0KHsgcmVmQ291bnQ6IHNxbGAke3NjaGVtYXMuZmlsZXMucmVmQ291bnR9IC0gMWAgfSlcclxuICAgICAgICAud2hlcmUoZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSlcclxuICAgICAgICAucmV0dXJuaW5nKCk7XHJcbiAgICAgIGlmICghdXBkYXRlZCkge1xyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZpbGUgbm90IGZvdW5kXCIpO1xyXG4gICAgICB9XHJcbiAgICAgIGlmICh1cGRhdGVkLnJlZkNvdW50IDwgMSkge1xyXG4gICAgICAgIGF3YWl0IHRoaXMuZGVsZXRlRmlsZSh7IGlkOiBwYXJhbXMuaWQgfSk7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgfTtcclxufSIsCiAgICAiaW1wb3J0IHR5cGUgeyBCdW5TUUxEYXRhYmFzZSB9IGZyb20gXCJkcml6emxlLW9ybS9idW4tc3FsXCI7XHJcbmltcG9ydCB7IGNyZWF0ZVJvdXRlcywgdHlwZSBQdXJwb3NlUG9saWN5IH0gZnJvbSBcIi4vcm91dGVzXCI7XHJcbmltcG9ydCB7IGNyZWF0ZVNjaGVtYXMgfSBmcm9tIFwiLi9zY2hlbWFzXCI7XHJcbmltcG9ydCB7IGNyZWF0ZVNlcnZpY2VzIH0gZnJvbSBcIi4vc2VydmljZXNcIjtcclxuaW1wb3J0IHR5cGUgeyBTM0NsaWVudCB9IGZyb20gXCJidW5cIjtcclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgRmlsZXNCYWNrZW5kT3B0aW9uczxUUHVycG9zZXMgZXh0ZW5kcyBzdHJpbmc+IHtcclxuICBkYjogQnVuU1FMRGF0YWJhc2U7XHJcbiAgY2xpZW50OiBTM0NsaWVudDtcclxuICBwb2xpY2llczogUmVjb3JkPFRQdXJwb3NlcywgUHVycG9zZVBvbGljeT47XHJcbiAgcHVibGljQmFzZVVybDogc3RyaW5nO1xyXG4gIHByZXNpZ25FeHBpcmVzSW4/OiBudW1iZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVGaWxlc0JhY2tlbmQ8Y29uc3QgVFB1cnBvc2UgZXh0ZW5kcyBzdHJpbmc+KFxyXG4gIG9wdGlvbnM6IEZpbGVzQmFja2VuZE9wdGlvbnM8VFB1cnBvc2U+XHJcbikge1xyXG4gIGNvbnN0IHNjaGVtYXMgPSBjcmVhdGVTY2hlbWFzKCk7XHJcbiAgY29uc3Qgc2VydmljZXMgPSBjcmVhdGVTZXJ2aWNlcyh7XHJcbiAgICBkYjogb3B0aW9ucy5kYixcclxuICAgIHNjaGVtYXMsXHJcbiAgICBzM0NsaWVudDogb3B0aW9ucy5jbGllbnQsXHJcbiAgICBwdWJsaWNCYXNlVXJsOiBvcHRpb25zLnB1YmxpY0Jhc2VVcmwsXHJcbiAgICBwcmVzaWduRXhwaXJlc0luOiBvcHRpb25zLnByZXNpZ25FeHBpcmVzSW4sXHJcbiAgfSk7XHJcbiAgY29uc3Qgcm91dGVzID0gY3JlYXRlUm91dGVzKHtcclxuICAgIHNlcnZpY2VzLFxyXG4gICAgcG9saWNpZXM6IG9wdGlvbnMucG9saWNpZXMsXHJcbiAgfSk7XHJcbiAgcmV0dXJuIHtcclxuICAgIHNjaGVtYXMsXHJcbiAgICByb3V0ZXMsXHJcbiAgICBzZXJ2aWNlcyxcclxuICB9IGFzIGNvbnN0O1xyXG59IgogIF0sCiAgIm1hcHBpbmdzIjogIjtBQUFBO0FBV08sU0FBUyxZQUEyQyxDQUN6RCxTQUlBO0FBQUEsRUFFQTtBQUFBLElBQ0U7QUFBQSxJQUNBO0FBQUEsTUFDRTtBQUFBLEVBRUosT0FBTyxJQUFJLE9BQU87QUFBQSxJQUNoQixRQUFRO0FBQUEsRUFDVixDQUFDLEVBQUUsS0FBSyxpQkFBaUIsU0FBUyxRQUFRLFdBQVc7QUFBQSxJQUNuRCxRQUFRLE1BQU0sWUFBWTtBQUFBLElBQzFCLE1BQU0sU0FBUyxTQUFTO0FBQUEsSUFDeEIsSUFBSSxDQUFDLFFBQVE7QUFBQSxNQUNYLE9BQU8sT0FBTyxLQUFLO0FBQUEsUUFDakIsU0FBUztBQUFBLE1BQ1gsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLElBQUksT0FBTyxZQUFZLGFBQWEsS0FBSyxPQUFPLE9BQU8sU0FBUztBQUFBLE1BQzlELE9BQU8sT0FBTyxLQUFLO0FBQUEsUUFDakIsU0FBUztBQUFBLE1BQ1gsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLElBQUksT0FBTyxxQkFBcUIsYUFBYSxDQUFDLE9BQU8saUJBQWlCLFNBQVMsS0FBSyxJQUFJLEdBQUc7QUFBQSxNQUN6RixPQUFPLE9BQU8sS0FBSztBQUFBLFFBQ2pCLFNBQVM7QUFBQSxNQUNYLENBQUM7QUFBQSxJQUNIO0FBQUEs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WUFBVyxDQUFDLFFBQTZCO0FBQUEsTUFDN0MsT0FBTyxXQUFXLE1BQU0sR0FDckIsT0FBTyxRQUFRLEtBQUssRUFDcEIsSUFBSSxFQUFFLFVBQVUsTUFBTSxRQUFRLE1BQU0sZUFBZSxDQUFDLEVBQ3BELE1BQU0sR0FBRyxRQUFRLE1BQU0sSUFBSSxPQUFPLEVBQUUsQ0FBQyxFQUNyQyxVQUFVO0FBQUEsTUFDYixJQUFJLENBQUMsU0FBUztBQUFBLFFBQ1osTUFBTSxJQUFJLE1BQU0sZ0JBQWdCO0FBQUEsTUFDbEM7QUFBQSxNQUNBLElBQUksUUFBUSxXQUFXLEdBQUc7QUFBQSxRQUN4QixNQUFNLEtBQUssV0FBVyxFQUFFLElBQUksT0FBTyxHQUFHLENBQUM7QUFBQSxNQUN6QztBQUFBO0FBQUEsRUFFSjtBQUFBOzs7QUM5SEssU0FBUyxrQkFBaUQsQ0FDL0QsU0FDQTtBQUFBLEVBQ0EsTUFBTSxVQUFVLGNBQWM7QUFBQSxFQUM5QixNQUFNLFdBQVcsZUFBZTtBQUFBLElBQzlCLElBQUksUUFBUTtBQUFBLElBQ1o7QUFBQSxJQUNBLFVBQVUsUUFBUTtBQUFBLElBQ2xCLGVBQWUsUUFBUTtBQUFBLElBQ3ZCLGtCQUFrQixRQUFRO0FBQUEsRUFDNUIsQ0FBQztBQUFBLEVBQ0QsTUFBTSxTQUFTLGFBQWE7QUFBQSxJQUMxQjtBQUFBLElBQ0EsVUFBVSxRQUFRO0FBQUEsRUFDcEIsQ0FBQztBQUFBLEVBQ0QsT0FBTztBQUFBLElBQ0w7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLEVBQ0Y7QUFBQTsiLAogICJkZWJ1Z0lkIjogIkE3MThGOUI5M0ZENkY2N0I2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9