@edkstack/files 0.1.9 → 0.1.10

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