@edkstack/files 0.1.3 → 0.1.5

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.
@@ -5,10 +5,10 @@ interface PurposePolicy {
5
5
  allowedMimeTypes?: string[];
6
6
  visibility?: Visibility;
7
7
  }
8
- import { S3Client as S3Client2 } from "bun";
8
+ import { S3Options as S3Options2 } from "bun";
9
9
  interface FilesBackendOptions<TPurposes extends string> {
10
- db: PgDatabase2<any, any, any>;
11
- s3Client: S3Client2;
10
+ db: PgDatabase2<any>;
11
+ s3Options: S3Options2;
12
12
  policies: Record<TPurposes, PurposePolicy>;
13
13
  publicBaseUrl: string;
14
14
  presignExpiresIn?: number;
@@ -1,9 +1,10 @@
1
+ // @bun
1
2
  import {
2
3
  createFilesBackend
3
- } from "../shared/chunk-99btckfr.js";
4
+ } from "../shared/chunk-wq7ndb52.js";
4
5
  export {
5
6
  createFilesBackend
6
7
  };
7
8
 
8
- //# debugId=3A9B94D5D89A800764756E2164756E21
9
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICBdLAogICJtYXBwaW5ncyI6ICIiLAogICJkZWJ1Z0lkIjogIjNBOUI5NEQ1RDg5QTgwMDc2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
9
+ //# debugId=9C07B894307CE45C64756E2164756E21
10
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICBdLAogICJtYXBwaW5ncyI6ICIiLAogICJkZWJ1Z0lkIjogIjlDMDdCODk0MzA3Q0U0NUM2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
@@ -7,10 +7,10 @@ interface PurposePolicy {
7
7
  allowedMimeTypes?: string[];
8
8
  visibility?: Visibility;
9
9
  }
10
- import { S3Client as S3Client2 } from "bun";
10
+ import { S3Options as S3Options2 } from "bun";
11
11
  interface FilesBackendOptions<TPurposes extends string> {
12
- db: PgDatabase2<any, any, any>;
13
- s3Client: S3Client2;
12
+ db: PgDatabase2<any>;
13
+ s3Options: S3Options2;
14
14
  policies: Record<TPurposes, PurposePolicy>;
15
15
  publicBaseUrl: string;
16
16
  presignExpiresIn?: number;
@@ -1,3 +1,4 @@
1
+ // @bun
1
2
  // src/client/provider.tsx
2
3
  import { createContext, useContext } from "react";
3
4
  import { jsxDEV } from "react/jsx-dev-runtime";
@@ -37,5 +38,5 @@ export {
37
38
  FilesClientProvider
38
39
  };
39
40
 
40
- //# debugId=A2BBBD5C97CF307B64756E2164756E21
41
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjXFxjbGllbnRcXHByb3ZpZGVyLnRzeCIsICJzcmNcXGNsaWVudFxcY2xpZW50LnRzIiwgInNyY1xcY2xpZW50XFxob29rcy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICAgICJpbXBvcnQgeyBjcmVhdGVDb250ZXh0LCB1c2VDb250ZXh0LCB0eXBlIFJlYWN0Tm9kZSB9IGZyb20gXCJyZWFjdFwiO1xyXG5pbXBvcnQgdHlwZSB7IEZpbGVzQ2xpZW50IH0gZnJvbSBcIi4vY2xpZW50XCI7XHJcblxyXG5cclxuY29uc3QgRmlsZXNDbGllbnRDb250ZXh0ID0gY3JlYXRlQ29udGV4dDxGaWxlc0NsaWVudCB8IG51bGw+KG51bGwpO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIEZpbGVzQ2xpZW50UHJvdmlkZXIocHJvcHM6IHtcclxuICBmaWxlc0NsaWVudDogRmlsZXNDbGllbnQ7XHJcbiAgY2hpbGRyZW46IFJlYWN0Tm9kZTtcclxufSkge1xyXG4gIHJldHVybiAoXHJcbiAgICA8RmlsZXNDbGllbnRDb250ZXh0LlByb3ZpZGVyIHZhbHVlPXtwcm9wcy5maWxlc0NsaWVudH0+XHJcbiAgICAgIHtwcm9wcy5jaGlsZHJlbn1cclxuICAgIDwvRmlsZXNDbGllbnRDb250ZXh0LlByb3ZpZGVyPlxyXG4gICk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2VGaWxlc0NsaWVudCgpIHtcclxuICBjb25zdCBjb250ZXh0ID0gdXNlQ29udGV4dChGaWxlc0NsaWVudENvbnRleHQpO1xyXG4gIGlmICghY29udGV4dCkge1xyXG4gICAgdGhyb3cgbmV3IEVycm9yKFwidXNlRmlsZXNDbGllbnQgbXVzdCBiZSB1c2VkIHdpdGhpbiBhIEZpbGVzQ2xpZW50UHJvdmlkZXJcIik7XHJcbiAgfVxyXG4gIHJldHVybiBjb250ZXh0O1xyXG59XHJcbiIsCiAgICAiaW1wb3J0IHsgdHJlYXR5LCB0eXBlIFRyZWF0eSB9IGZyb20gXCJAZWx5c2lhanMvZWRlblwiO1xyXG5pbXBvcnQgdHlwZSB7IGNyZWF0ZUZpbGVzQmFja2VuZCB9IGZyb20gXCIuLi9iYWNrZW5kXCI7XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRmlsZXNDbGllbnQ8XHJcbiAgVEJhY2tlbmQgZXh0ZW5kcyBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVGaWxlc0JhY2tlbmQ8YW55Pj5cclxuPihcclxuICBkb21haW46IHN0cmluZyxcclxuICBjb25maWc/OiBUcmVhdHkuQ29uZmlnXHJcbikge1xyXG4gIHJldHVybiB0cmVhdHk8VEJhY2tlbmRbXCJyb3V0ZXNcIl0+KGRvbWFpbiwgY29uZmlnKTtcclxufVxyXG5cclxuZXhwb3J0IHR5cGUgRmlsZXNDbGllbnQgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVGaWxlc0NsaWVudD47IiwKICAgICJpbXBvcnQgeyB1c2VNdXRhdGlvbiwgdHlwZSBVc2VNdXRhdGlvbk9wdGlvbnMgfSBmcm9tIFwiQHRhbnN0YWNrL3JlYWN0LXF1ZXJ5XCI7XHJcbmltcG9ydCB0eXBlIHsgRmlsZXNDbGllbnQgfSBmcm9tIFwiLi9jbGllbnRcIjtcclxuaW1wb3J0IHsgZWRlbk11dGF0aW9uT3B0aW9ucywgdHlwZSBJbmZlck11dGF0aW9uT3B0aW9ucyB9IGZyb20gXCJlZGVuMnF1ZXJ5XCI7XHJcbmltcG9ydCB7IHVzZUZpbGVzQ2xpZW50IH0gZnJvbSBcIi4vcHJvdmlkZXJcIjtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2VVcGxvYWQoXHJcbiAgb3B0aW9uczogSW5mZXJNdXRhdGlvbk9wdGlvbnM8RmlsZXNDbGllbnRbXCJhcGlcIl1bXCJmaWxlc1wiXVtcInVwbG9hZFwiXVtcInBvc3RcIl0+XHJcbikge1xyXG4gIGNvbnN0IGZpbGVzQ2xpZW50ID0gdXNlRmlsZXNDbGllbnQoKSA7XHJcbiAgcmV0dXJuIHVzZU11dGF0aW9uKHtcclxuICAgIC4uLm9wdGlvbnMsXHJcbiAgICAuLi5lZGVuTXV0YXRpb25PcHRpb25zKFxyXG4gICAgICBmaWxlc0NsaWVudC5hcGkuZmlsZXMudXBsb2FkLnBvc3QsXHJcbiAgICApLFxyXG4gIH0pO1xyXG59IgogIF0sCiAgIm1hcHBpbmdzIjogIjtBQUFBO0FBQUE7QUFJQSxJQUFNLHFCQUFxQixjQUFrQyxJQUFJO0FBRTFELFNBQVMsbUJBQW1CLENBQUMsT0FHakM7QUFBQSxFQUNELHVCQUNFLE9BRUUsbUJBQW1CLFVBRnJCO0FBQUEsSUFBNkIsT0FBTyxNQUFNO0FBQUEsSUFBMUMsVUFDRyxNQUFNO0FBQUEsS0FEVCxpQ0FFRTtBQUFBO0FBSUMsU0FBUyxjQUFjLEdBQUc7QUFBQSxFQUMvQixNQUFNLFVBQVUsV0FBVyxrQkFBa0I7QUFBQSxFQUM3QyxJQUFJLENBQUMsU0FBUztBQUFBLElBQ1osTUFBTSxJQUFJLE1BQU0sMERBQTBEO0FBQUEsRUFDNUU7QUFBQSxFQUNBLE9BQU87QUFBQTs7QUN0QlQ7QUFHTyxTQUFTLGlCQUVmLENBQ0MsUUFDQSxRQUNBO0FBQUEsRUFDQSxPQUFPLE9BQTJCLFFBQVEsTUFBTTtBQUFBOztBQ1RsRDtBQUVBO0FBR08sU0FBUyxTQUFTLENBQ3ZCLFNBQ0E7QUFBQSxFQUNBLE1BQU0sY0FBYyxlQUFlO0FBQUEsRUFDbkMsT0FBTyxZQUFZO0FBQUEsT0FDZDtBQUFBLE9BQ0Esb0JBQ0QsWUFBWSxJQUFJLE1BQU0sT0FBTyxJQUMvQjtBQUFBLEVBQ0YsQ0FBQztBQUFBOyIsCiAgImRlYnVnSWQiOiAiQTJCQkJENUM5N0NGMzA3QjY0NzU2RTIxNjQ3NTZFMjEiLAogICJuYW1lcyI6IFtdCn0=
41
+ //# debugId=B5D4D3E17929B8B464756E2164756E21
42
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjXFxjbGllbnRcXHByb3ZpZGVyLnRzeCIsICJzcmNcXGNsaWVudFxcY2xpZW50LnRzIiwgInNyY1xcY2xpZW50XFxob29rcy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICAgICJpbXBvcnQgeyBjcmVhdGVDb250ZXh0LCB1c2VDb250ZXh0LCB0eXBlIFJlYWN0Tm9kZSB9IGZyb20gXCJyZWFjdFwiO1xyXG5pbXBvcnQgdHlwZSB7IEZpbGVzQ2xpZW50IH0gZnJvbSBcIi4vY2xpZW50XCI7XHJcblxyXG5cclxuY29uc3QgRmlsZXNDbGllbnRDb250ZXh0ID0gY3JlYXRlQ29udGV4dDxGaWxlc0NsaWVudCB8IG51bGw+KG51bGwpO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIEZpbGVzQ2xpZW50UHJvdmlkZXIocHJvcHM6IHtcclxuICBmaWxlc0NsaWVudDogRmlsZXNDbGllbnQ7XHJcbiAgY2hpbGRyZW46IFJlYWN0Tm9kZTtcclxufSkge1xyXG4gIHJldHVybiAoXHJcbiAgICA8RmlsZXNDbGllbnRDb250ZXh0LlByb3ZpZGVyIHZhbHVlPXtwcm9wcy5maWxlc0NsaWVudH0+XHJcbiAgICAgIHtwcm9wcy5jaGlsZHJlbn1cclxuICAgIDwvRmlsZXNDbGllbnRDb250ZXh0LlByb3ZpZGVyPlxyXG4gICk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2VGaWxlc0NsaWVudCgpIHtcclxuICBjb25zdCBjb250ZXh0ID0gdXNlQ29udGV4dChGaWxlc0NsaWVudENvbnRleHQpO1xyXG4gIGlmICghY29udGV4dCkge1xyXG4gICAgdGhyb3cgbmV3IEVycm9yKFwidXNlRmlsZXNDbGllbnQgbXVzdCBiZSB1c2VkIHdpdGhpbiBhIEZpbGVzQ2xpZW50UHJvdmlkZXJcIik7XHJcbiAgfVxyXG4gIHJldHVybiBjb250ZXh0O1xyXG59XHJcbiIsCiAgICAiaW1wb3J0IHsgdHJlYXR5LCB0eXBlIFRyZWF0eSB9IGZyb20gXCJAZWx5c2lhanMvZWRlblwiO1xyXG5pbXBvcnQgdHlwZSB7IGNyZWF0ZUZpbGVzQmFja2VuZCB9IGZyb20gXCIuLi9iYWNrZW5kXCI7XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRmlsZXNDbGllbnQ8XHJcbiAgVEJhY2tlbmQgZXh0ZW5kcyBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVGaWxlc0JhY2tlbmQ8YW55Pj5cclxuPihcclxuICBkb21haW46IHN0cmluZyxcclxuICBjb25maWc/OiBUcmVhdHkuQ29uZmlnXHJcbikge1xyXG4gIHJldHVybiB0cmVhdHk8VEJhY2tlbmRbXCJyb3V0ZXNcIl0+KGRvbWFpbiwgY29uZmlnKTtcclxufVxyXG5cclxuZXhwb3J0IHR5cGUgRmlsZXNDbGllbnQgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVGaWxlc0NsaWVudD47IiwKICAgICJpbXBvcnQgeyB1c2VNdXRhdGlvbiwgdHlwZSBVc2VNdXRhdGlvbk9wdGlvbnMgfSBmcm9tIFwiQHRhbnN0YWNrL3JlYWN0LXF1ZXJ5XCI7XHJcbmltcG9ydCB0eXBlIHsgRmlsZXNDbGllbnQgfSBmcm9tIFwiLi9jbGllbnRcIjtcclxuaW1wb3J0IHsgZWRlbk11dGF0aW9uT3B0aW9ucywgdHlwZSBJbmZlck11dGF0aW9uT3B0aW9ucyB9IGZyb20gXCJlZGVuMnF1ZXJ5XCI7XHJcbmltcG9ydCB7IHVzZUZpbGVzQ2xpZW50IH0gZnJvbSBcIi4vcHJvdmlkZXJcIjtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2VVcGxvYWQoXHJcbiAgb3B0aW9uczogSW5mZXJNdXRhdGlvbk9wdGlvbnM8RmlsZXNDbGllbnRbXCJhcGlcIl1bXCJmaWxlc1wiXVtcInVwbG9hZFwiXVtcInBvc3RcIl0+XHJcbikge1xyXG4gIGNvbnN0IGZpbGVzQ2xpZW50ID0gdXNlRmlsZXNDbGllbnQoKSA7XHJcbiAgcmV0dXJuIHVzZU11dGF0aW9uKHtcclxuICAgIC4uLm9wdGlvbnMsXHJcbiAgICAuLi5lZGVuTXV0YXRpb25PcHRpb25zKFxyXG4gICAgICBmaWxlc0NsaWVudC5hcGkuZmlsZXMudXBsb2FkLnBvc3QsXHJcbiAgICApLFxyXG4gIH0pO1xyXG59IgogIF0sCiAgIm1hcHBpbmdzIjogIjs7QUFBQTtBQUFBO0FBSUEsSUFBTSxxQkFBcUIsY0FBa0MsSUFBSTtBQUUxRCxTQUFTLG1CQUFtQixDQUFDLE9BR2pDO0FBQUEsRUFDRCx1QkFDRSxPQUVFLG1CQUFtQixVQUZyQjtBQUFBLElBQTZCLE9BQU8sTUFBTTtBQUFBLElBQTFDLFVBQ0csTUFBTTtBQUFBLEtBRFQsaUNBRUU7QUFBQTtBQUlDLFNBQVMsY0FBYyxHQUFHO0FBQUEsRUFDL0IsTUFBTSxVQUFVLFdBQVcsa0JBQWtCO0FBQUEsRUFDN0MsSUFBSSxDQUFDLFNBQVM7QUFBQSxJQUNaLE1BQU0sSUFBSSxNQUFNLDBEQUEwRDtBQUFBLEVBQzVFO0FBQUEsRUFDQSxPQUFPO0FBQUE7O0FDdEJUO0FBR08sU0FBUyxpQkFFZixDQUNDLFFBQ0EsUUFDQTtBQUFBLEVBQ0EsT0FBTyxPQUEyQixRQUFRLE1BQU07QUFBQTs7QUNUbEQ7QUFFQTtBQUdPLFNBQVMsU0FBUyxDQUN2QixTQUNBO0FBQUEsRUFDQSxNQUFNLGNBQWMsZUFBZTtBQUFBLEVBQ25DLE9BQU8sWUFBWTtBQUFBLE9BQ2Q7QUFBQSxPQUNBLG9CQUNELFlBQVksSUFBSSxNQUFNLE9BQU8sSUFDL0I7QUFBQSxFQUNGLENBQUM7QUFBQTsiLAogICJkZWJ1Z0lkIjogIkI1RDREM0UxNzkyOUI4QjQ2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
package/dist/index.d.ts CHANGED
@@ -5,10 +5,10 @@ interface PurposePolicy {
5
5
  allowedMimeTypes?: string[];
6
6
  visibility?: Visibility;
7
7
  }
8
- import { S3Client as S3Client2 } from "bun";
8
+ import { S3Options as S3Options2 } from "bun";
9
9
  interface FilesBackendOptions<TPurposes extends string> {
10
- db: PgDatabase2<any, any, any>;
11
- s3Client: S3Client2;
10
+ db: PgDatabase2<any>;
11
+ s3Options: S3Options2;
12
12
  policies: Record<TPurposes, PurposePolicy>;
13
13
  publicBaseUrl: string;
14
14
  presignExpiresIn?: number;
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
+ // @bun
1
2
  import {
2
3
  createFilesBackend
3
- } from "./shared/chunk-99btckfr.js";
4
+ } from "./shared/chunk-wq7ndb52.js";
4
5
  export {
5
6
  createFilesBackend
6
7
  };
7
8
 
8
- //# debugId=3233116F2811A0A464756E2164756E21
9
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICBdLAogICJtYXBwaW5ncyI6ICIiLAogICJkZWJ1Z0lkIjogIjMyMzMxMTZGMjgxMUEwQTQ2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
9
+ //# debugId=A60E55F38BA0FEA664756E2164756E21
10
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICBdLAogICJtYXBwaW5ncyI6ICIiLAogICJkZWJ1Z0lkIjogIkE2MEU1NUYzOEJBMEZFQTY2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
@@ -0,0 +1,203 @@
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+W10sXHJcbiAgICAgIF1cclxuICAgIClcclxuICB9KTtcclxufVxyXG5cclxuY29uc3QgRXJyb3JSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBtZXNzYWdlOiB0LlN0cmluZygpLFxyXG59KTtcclxuXHJcbmNvbnN0IEZpbGVSZXNwb25zZSA9IHQuT2JqZWN0KHtcclxuICBpZDogdC5TdHJpbmcoKSxcclxuICBuYW1lOiB0Lk51bGxhYmxlKHQuU3RyaW5nKCkpLFxyXG4gIGtleTogdC5TdHJpbmcoKSxcclxuICBzaXplOiB0Lk51bWJlcigpLFxyXG4gIG1pbWVUeXBlOiB0LlN0cmluZygpLFxyXG4gIGNyZWF0ZWRBdDogdC5EYXRlKCksXHJcbn0pOyIsCiAgICAiaW1wb3J0IHsgbmFub2lkIH0gZnJvbSBcIm5hbm9pZFwiO1xyXG5pbXBvcnQgeyBpbmRleCwgaW50ZWdlciwgcGdUYWJsZSwgdGV4dCwgdGltZXN0YW1wLCBwZ0VudW0gfSBmcm9tIFwiZHJpenpsZS1vcm0vcGctY29yZVwiO1xyXG5cclxuXHJcbmV4cG9ydCB0eXBlIEFsbFNjaGVtYXMgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVTY2hlbWFzPjtcclxuZXhwb3J0IHR5cGUgRmlsZVNjaGVtYSA9IEFsbFNjaGVtYXNbXCJmaWxlc1wiXTtcclxuZXhwb3J0IHR5cGUgRmlsZVNlbGVjdCA9IEZpbGVTY2hlbWFbXCIkaW5mZXJTZWxlY3RcIl07XHJcbmV4cG9ydCB0eXBlIEZpbGVJbnNlcnQgPSBGaWxlU2NoZW1hW1wiJGluZmVySW5zZXJ0XCJdO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVNjaGVtYXMoKSB7XHJcblxyXG4gIGNvbnN0IGZpbGVzID0gcGdUYWJsZShcImZpbGVzXCIsIHtcclxuICAgIGlkOiB0ZXh0KFwiaWRcIikucHJpbWFyeUtleSgpLiRkZWZhdWx0Rm4oKCkgPT4gYGZpbGVfJHtuYW5vaWQoKX1gKSxcclxuICAgIHB1cnBvc2U6IHRleHQoXCJwdXJwb3NlXCIpLm5vdE51bGwoKSxcclxuICAgIG5hbWU6IHRleHQoXCJuYW1lXCIpLFxyXG4gICAga2V5OiB0ZXh0KFwia2V5XCIpLm5vdE51bGwoKS51bmlxdWUoKSxcclxuICAgIHNpemU6IGludGVnZXIoXCJzaXplXCIpLm5vdE51bGwoKSxcclxuICAgIG1pbWVUeXBlOiB0ZXh0KFwibWltZV90eXBlXCIpLm5vdE51bGwoKS5kZWZhdWx0KFwiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtXCIpLFxyXG4gICAgcmVmQ291bnQ6IGludGVnZXIoXCJyZWZfY291bnRcIikubm90TnVsbCgpLmRlZmF1bHQoMCksXHJcbiAgICB2aXNpYmlsaXR5OiBwZ0VudW0oXCJ2aXNpYmlsaXR5XCIsIFtcInByaXZhdGVcIiwgXCJwdWJsaWNcIl0pKCkubm90TnVsbCgpLmRlZmF1bHQoXCJwcml2YXRlXCIpLFxyXG4gICAgY3JlYXRlZEF0OiB0aW1lc3RhbXAoXCJjcmVhdGVkX2F0XCIpLm5vdE51bGwoKS5kZWZhdWx0Tm93KCksXHJcbiAgICB1cGRhdGVkQXQ6IHRpbWVzdGFtcChcInVwZGF0ZWRfYXRcIikubm90TnVsbCgpLmRlZmF1bHROb3coKSxcclxuICB9LCAodGFibGUpID0+IFtcclxuICAgIGluZGV4KFwiZmlsZXNfa2V5X2lkeFwiKS5vbih0YWJsZS5rZXkpLFxyXG4gICAgaW5kZXgoXCJmaWxlc19jcmVhdGVkX2F0X2lkeFwiKS5vbih0YWJsZS5jcmVhdGVkQXQpLFxyXG4gIF0pO1xyXG5cclxuICByZXR1cm4ge1xyXG4gICAgZmlsZXMsXHJcbiAgfVxyXG59IiwKICAgICJpbXBvcnQgeyBTM0NsaWVudCwgdHlwZSBTM09wdGlvbnMgfSBmcm9tIFwiYnVuXCI7XHJcbmltcG9ydCB7IG5hbm9pZCB9IGZyb20gXCJuYW5vaWRcIjtcclxuaW1wb3J0IHsgZXh0bmFtZSB9IGZyb20gXCJwYXRoXCI7XHJcbmltcG9ydCB7IGVxLCBzcWwsIGx0LCBhbmQgfSBmcm9tIFwiZHJpenpsZS1vcm1cIjtcclxuaW1wb3J0IHR5cGUgeyBBbGxTY2hlbWFzLCBGaWxlU2VsZWN0IH0gZnJvbSBcIi4vc2NoZW1hc1wiO1xyXG5pbXBvcnQgdHlwZSB7IFBnRGF0YWJhc2UgfSBmcm9tIFwiZHJpenpsZS1vcm0vcGctY29yZVwiO1xyXG5cclxuZXhwb3J0IHR5cGUgU2VydmljZXMgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVTZXJ2aWNlcz47XHJcblxyXG50eXBlIEJ5SWQgPSB7IGlkOiBzdHJpbmcgfTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTZXJ2aWNlcyhcclxuICBvcHRpb25zOiB7XHJcbiAgICBkYjogUGdEYXRhYmFzZTxhbnk+O1xyXG4gICAgc2NoZW1hczogQWxsU2NoZW1hcztcclxuICAgIHMzT3B0aW9uczogUzNPcHRpb25zO1xyXG4gICAga2V5UHJlZml4Pzogc3RyaW5nO1xyXG4gICAgcHJlc2lnbkV4cGlyZXNJbj86IG51bWJlcjtcclxuICB9XHJcbikge1xyXG4gIGNvbnN0IHsgXHJcbiAgICBkYiwgXHJcbiAgICBzY2hlbWFzLCBcclxuICAgIHMzT3B0aW9ucyxcclxuICAgIGtleVByZWZpeCA9IFwiZmlsZXNcIixcclxuICAgIHByZXNpZ25FeHBpcmVzSW4gPSAzNjAwXHJcbiAgfSA9IG9wdGlvbnM7XHJcbiAgXHJcbiAgY29uc3QgeyBlbmRwb2ludCB9ID0gczNPcHRpb25zO1xyXG4gIGNvbnN0IHMzQ2xpZW50ID0gbmV3IFMzQ2xpZW50KHMzT3B0aW9ucyk7XHJcblxyXG4gIHJldHVybiB7XHJcblxyXG4gICAgYXN5bmMgZ2V0RmlsZShwYXJhbXM6IEJ5SWQpOiBQcm9taXNlPEZpbGVTZWxlY3Q+IHtcclxuICAgICAgY29uc3QgW2ZvdW5kXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLnNlbGVjdCgpXHJcbiAgICAgICAgLmZyb20oc2NoZW1hcy5maWxlcylcclxuICAgICAgICAud2hlcmUoZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSlcclxuICAgICAgICAubGltaXQoMSk7XHJcbiAgICAgIGlmICghZm91bmQpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJGaWxlIG5vdCBmb3VuZFwiKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gZm91bmQ7XHJcbiAgICB9LFxyXG5cclxuICAgIGFzeW5jIGdldFVybChwYXJhbXM6IEJ5SWQpOiBQcm9taXNlPHN0cmluZz4ge1xyXG4gICAgICBjb25zdCBbZm91bmRdID0gYXdhaXQgZGJcclxuICAgICAgICAuc2VsZWN0KHsgXHJcbiAgICAgICAgICBrZXk6IHNjaGVtYXMuZmlsZXMua2V5LFxyXG4gICAgICAgICAgdmlzaWJpbGl0eTogc2NoZW1hcy5maWxlcy52aXNpYmlsaXR5LFxyXG4gICAgICAgIH0pXHJcbiAgICAgICAgLmZyb20oc2NoZW1hcy5maWxlcylcclxuICAgICAgICAud2hlcmUoZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSlcclxuICAgICAgICAubGltaXQoMSk7XHJcbiAgICAgIGlmICghZm91bmQpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJGaWxlIG5vdCBmb3VuZFwiKTtcclxuICAgICAgfVxyXG4gICAgICBpZiAoZm91bmQudmlzaWJpbGl0eSA9PT0gXCJwdWJsaWNcIikge1xyXG4gICAgICAgIHJldHVybiBgJHtlbmRwb2ludH0vJHtmb3VuZC5rZXl9YDtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gczNDbGllbnQucHJlc2lnbihmb3VuZC5rZXksIHsgZXhwaXJlc0luOiBwcmVzaWduRXhwaXJlc0luIH0pO1xyXG4gICAgfSxcclxuXHJcbiAgICBhc3luYyB1cGxvYWRGaWxlKHBhcmFtczoge1xyXG4gICAgICBmaWxlOiBGaWxlO1xyXG4gICAgICBwdXJwb3NlOiBzdHJpbmc7XHJcbiAgICAgIHZpc2liaWxpdHk6IFwicHJpdmF0ZVwiIHwgXCJwdWJsaWNcIjtcclxuICAgIH0pOiBQcm9taXNlPEZpbGVTZWxlY3Q+IHtcclxuICAgICAgY29uc3QgaWQgPSBuYW5vaWQoKTtcclxuICAgICAgY29uc3QgZXh0ID0gZXh0bmFtZShwYXJhbXMuZmlsZS5uYW1lKTtcclxuICAgICAgY29uc3Qga2V5ID0gW2tleVByZWZpeCwgcGFyYW1zLnZpc2liaWxpdHksIHBhcmFtcy5wdXJwb3NlLCBgJHtpZH0ke2V4dH1gXS5qb2luKFwiL1wiKTtcclxuICAgICAgY29uc3QgczNmaWxlID0gczNDbGllbnQuZmlsZShrZXkpO1xyXG4gICAgICBhd2FpdCBzM2ZpbGUud3JpdGUocGFyYW1zLmZpbGUsIHtcclxuICAgICAgICB0eXBlOiBwYXJhbXMuZmlsZS50eXBlLFxyXG4gICAgICAgIGFjbDogcGFyYW1zLnZpc2liaWxpdHkgPT09IFwicHVibGljXCIgPyBcInB1YmxpYy1yZWFkXCIgOiBcInByaXZhdGVcIixcclxuICAgICAgfSk7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgW2NyZWF0ZWRdID0gYXdhaXQgZGIuaW5zZXJ0KHNjaGVtYXMuZmlsZXMpXHJcbiAgICAgICAgICAudmFsdWVzKHtcclxuICAgICAgICAgICAgcHVycG9zZTogcGFyYW1zLnB1cnBvc2UsXHJcbiAgICAgICAgICAgIGtleSxcclxuICAgICAgICAgICAgc2l6ZTogczNmaWxlLnNpemUsXHJcbiAgICAgICAgICAgIG5hbWU6IHMzZmlsZS5uYW1lID8/IG51bGwsXHJcbiAgICAgICAgICAgIG1pbWVUeXBlOiBzM2ZpbGUudHlwZSxcclxuICAgICAgICAgICAgdmlzaWJpbGl0eTogcGFyYW1zLnZpc2liaWxpdHksXHJcbiAgICAgICAgICB9KVxyXG4gICAgICAgICAgLnJldHVybmluZygpO1xyXG4gICAgICAgIGlmICghY3JlYXRlZCkge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmFpbGVkIHRvIGNyZWF0ZSBmaWxlIHJlY29yZFwiKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIGNyZWF0ZWQ7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgYXdhaXQgczNmaWxlLmRlbGV0ZSgpLmNhdGNoKCk7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgZGVsZXRlRmlsZShwYXJhbXM6IHsgaWQ6IHN0cmluZyB9KTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgIGNvbnN0IFtkZWxldGVkXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLmRlbGV0ZShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgaWYgKCFkZWxldGVkKSByZXR1cm47XHJcbiAgICAgIGF3YWl0IHMzQ2xpZW50LmRlbGV0ZShkZWxldGVkLmtleSk7XHJcbiAgICB9LFxyXG5cclxuICAgIGFzeW5jIGFjcXVpcmVGaWxlKHBhcmFtczogQnlJZCAmIHsgXHJcbiAgICAgIHB1cnBvc2U/OiBzdHJpbmcgXHJcbiAgICB9KTogUHJvbWlzZTxGaWxlU2VsZWN0PiB7XHJcbiAgICAgIGNvbnN0IFt1cGRhdGVkXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLnVwZGF0ZShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC5zZXQoeyByZWZDb3VudDogc3FsYCR7c2NoZW1hcy5maWxlcy5yZWZDb3VudH0gKyAxYCB9KVxyXG4gICAgICAgIC53aGVyZShcclxuICAgICAgICAgIGFuZChcclxuICAgICAgICAgICAgZXEoc2NoZW1hcy5maWxlcy5pZCwgcGFyYW1zLmlkKSxcclxuICAgICAgICAgICAgcGFyYW1zLnB1cnBvc2UgPyBlcShzY2hlbWFzLmZpbGVzLnB1cnBvc2UsIHBhcmFtcy5wdXJwb3NlKSA6IHVuZGVmaW5lZCxcclxuICAgICAgICAgIClcclxuICAgICAgICApXHJcbiAgICAgICAgLnJldHVybmluZygpO1xyXG4gICAgICBpZiAoIXVwZGF0ZWQpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJGaWxlIG5vdCBmb3VuZFwiKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdXBkYXRlZDtcclxuICAgIH0sXHJcblxyXG4gICAgYXN5bmMgcmVsZWFzZUZpbGUocGFyYW1zOiBCeUlkKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgIGNvbnN0IFt1cGRhdGVkXSA9IGF3YWl0IGRiXHJcbiAgICAgICAgLnVwZGF0ZShzY2hlbWFzLmZpbGVzKVxyXG4gICAgICAgIC5zZXQoeyByZWZDb3VudDogc3FsYCR7c2NoZW1hcy5maWxlcy5yZWZDb3VudH0gLSAxYCB9KVxyXG4gICAgICAgIC53aGVyZShlcShzY2hlbWFzLmZpbGVzLmlkLCBwYXJhbXMuaWQpKVxyXG4gICAgICAgIC5yZXR1cm5pbmcoKTtcclxuICAgICAgaWYgKCF1cGRhdGVkKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmlsZSBub3QgZm91bmRcIik7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKHVwZGF0ZWQucmVmQ291bnQgPCAxKSB7XHJcbiAgICAgICAgYXdhaXQgdGhpcy5kZWxldGVGaWxlKHsgaWQ6IHBhcmFtcy5pZCB9KTtcclxuICAgICAgfVxyXG4gICAgfSxcclxuICB9O1xyXG59IiwKICAgICJpbXBvcnQgdHlwZSB7IFBnRGF0YWJhc2UgfSBmcm9tIFwiZHJpenpsZS1vcm0vcGctY29yZVwiO1xyXG5pbXBvcnQgeyBjcmVhdGVSb3V0ZXMsIHR5cGUgUHVycG9zZVBvbGljeSB9IGZyb20gXCIuL3JvdXRlc1wiO1xyXG5pbXBvcnQgeyBjcmVhdGVTY2hlbWFzIH0gZnJvbSBcIi4vc2NoZW1hc1wiO1xyXG5pbXBvcnQgeyBjcmVhdGVTZXJ2aWNlcyB9IGZyb20gXCIuL3NlcnZpY2VzXCI7XHJcbmltcG9ydCB0eXBlIHsgUzNPcHRpb25zIH0gZnJvbSBcImJ1blwiO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBGaWxlc0JhY2tlbmRPcHRpb25zPFRQdXJwb3NlcyBleHRlbmRzIHN0cmluZz4ge1xyXG4gIGRiOiBQZ0RhdGFiYXNlPGFueT47XHJcbiAgczNPcHRpb25zOiBTM09wdGlvbnM7XHJcbiAgcG9saWNpZXM6IFJlY29yZDxUUHVycG9zZXMsIFB1cnBvc2VQb2xpY3k+O1xyXG4gIHB1YmxpY0Jhc2VVcmw6IHN0cmluZztcclxuICBwcmVzaWduRXhwaXJlc0luPzogbnVtYmVyO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRmlsZXNCYWNrZW5kPGNvbnN0IFRQdXJwb3NlIGV4dGVuZHMgc3RyaW5nPihcclxuICBvcHRpb25zOiBGaWxlc0JhY2tlbmRPcHRpb25zPFRQdXJwb3NlPlxyXG4pIHtcclxuICBjb25zdCBzY2hlbWFzID0gY3JlYXRlU2NoZW1hcygpO1xyXG4gIGNvbnN0IHNlcnZpY2VzID0gY3JlYXRlU2VydmljZXMoe1xyXG4gICAgZGI6IG9wdGlvbnMuZGIsXHJcbiAgICBzY2hlbWFzLFxyXG4gICAgczNPcHRpb25zOiBvcHRpb25zLnMzT3B0aW9ucyxcclxuICAgIHByZXNpZ25FeHBpcmVzSW46IG9wdGlvbnMucHJlc2lnbkV4cGlyZXNJbixcclxuICB9KTtcclxuICBjb25zdCByb3V0ZXMgPSBjcmVhdGVSb3V0ZXMoe1xyXG4gICAgc2VydmljZXMsXHJcbiAgICBwb2xpY2llczogb3B0aW9ucy5wb2xpY2llcyxcclxuICB9KTtcclxuICByZXR1cm4ge1xyXG4gICAgc2NoZW1hcyxcclxuICAgIHJvdXRlcyxcclxuICAgIHNlcnZpY2VzLFxyXG4gIH0gYXMgY29uc3Q7XHJcbn0iCiAgXSwKICAibWFwcGluZ3MiOiAiOztBQUFBO0FBV08sU0FBUyxZQUEyQyxDQUN6RCxTQUlBO0FBQUEsRUFFQTtBQUFBLElBQ0U7QUFBQSxJQUNBO0FBQUEsTUFDRTtBQUFBLEVBRUosT0FBTyxJQUFJLE9BQU87QUFBQSxJQUNoQixRQUFRO0FBQUEsRUFDVixDQUFDLEVBQUUsS0FBSyxpQkFBaUIsU0FBUyxRQUFRLFdBQVc7QUFBQSxJQUNuRCxRQUFRLE1BQU0sWUFBWTtBQUFBLElBQzFCLE1BQU0sU0FBUyxTQUFTO0FBQUEsSUFDeEIsSUFBSSxDQUFDLFFBQVE7QUFBQSxNQUNYLE9BQU8sT0FBTyxLQUFLO0FBQUEsUUFDakIsU0FBUztBQUFBLE1BQ1gsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLElBQUksT0FBTyxZQUFZLGFBQWEsS0FBSyxPQUFPLE9BQU8sU0FBUztBQUFBLE1BQzlELE9BQU8sT0FBTyxLQUFLO0FBQUEsUUFDakIsU0FBUztBQUFBLE1BQ1gsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLElBQUksT0FBTyxxQkFBcUIsYUFBYSxDQUFDLE9BQU8saUJBQWlCLFNBQVMsS0FBSyxJQUFJLEdBQUc7QUFBQSxNQUN6RixPQUFPLE9BQU8sS0FBSztBQUFBLFFBQ2pCLFNBQVM7QUFBQSxNQUNYLENBQUM7QUFBQSxJQUNIO0FBQUEsSUFDQSxNQUFNLFdBQVcsTUFBTSxTQUFTLFdBQVc7QUFBQSxNQUN6QztBQUFBLE1BQ0E7QUFBQSxNQUNB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LE1BQzdDLE9BQU8sV0FBVyxNQUFNLEdBQ3JCLE9BQU8sUUFBUSxLQUFLLEVBQ3BCLElBQUksRUFBRSxVQUFVLE1BQU0sUUFBUSxNQUFNLGVBQWUsQ0FBQyxFQUNwRCxNQUFNLEdBQUcsUUFBUSxNQUFNLElBQUksT0FBTyxFQUFFLENBQUMsRUFDckMsVUFBVTtBQUFBLE1BQ2IsSUFBSSxDQUFDLFNBQVM7QUFBQSxRQUNaLE1BQU0sSUFBSSxNQUFNLGdCQUFnQjtBQUFBLE1BQ2xDO0FBQUEsTUFDQSxJQUFJLFFBQVEsV0FBVyxHQUFHO0FBQUEsUUFDeEIsTUFBTSxLQUFLLFdBQVcsRUFBRSxJQUFJLE9BQU8sR0FBRyxDQUFDO0FBQUEsTUFDekM7QUFBQTtBQUFBLEVBRUo7QUFBQTs7O0FDNUhLLFNBQVMsa0JBQWlELENBQy9ELFNBQ0E7QUFBQSxFQUNBLE1BQU0sVUFBVSxjQUFjO0FBQUEsRUFDOUIsTUFBTSxXQUFXLGVBQWU7QUFBQSxJQUM5QixJQUFJLFFBQVE7QUFBQSxJQUNaO0FBQUEsSUFDQSxXQUFXLFFBQVE7QUFBQSxJQUNuQixrQkFBa0IsUUFBUTtBQUFBLEVBQzVCLENBQUM7QUFBQSxFQUNELE1BQU0sU0FBUyxhQUFhO0FBQUEsSUFDMUI7QUFBQSxJQUNBLFVBQVUsUUFBUTtBQUFBLEVBQ3BCLENBQUM7QUFBQSxFQUNELE9BQU87QUFBQSxJQUNMO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxFQUNGO0FBQUE7IiwKICAiZGVidWdJZCI6ICJEREM4QkU3OTM1RTc3RjZENjQ3NTZFMjE2NDc1NkUyMSIsCiAgIm5hbWVzIjogW10KfQ==
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.3",
4
+ "version": "0.1.5",
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.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==