@zlooks.cn/images-server 1.0.1

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.
Files changed (141) hide show
  1. package/dist/assets/provider.d.ts +3 -0
  2. package/dist/assets/provider.d.ts.map +1 -0
  3. package/dist/assets/provider.js +6 -0
  4. package/dist/assets/provider.js.map +1 -0
  5. package/dist/assets/read-image-asset.d.ts +5 -0
  6. package/dist/assets/read-image-asset.d.ts.map +1 -0
  7. package/dist/assets/read-image-asset.js +88 -0
  8. package/dist/assets/read-image-asset.js.map +1 -0
  9. package/dist/assets/response-policy.d.ts +6 -0
  10. package/dist/assets/response-policy.d.ts.map +1 -0
  11. package/dist/assets/response-policy.js +49 -0
  12. package/dist/assets/response-policy.js.map +1 -0
  13. package/dist/caches/public-image-cache.d.ts +18 -0
  14. package/dist/caches/public-image-cache.d.ts.map +1 -0
  15. package/dist/caches/public-image-cache.js +58 -0
  16. package/dist/caches/public-image-cache.js.map +1 -0
  17. package/dist/command/index.d.ts +3 -0
  18. package/dist/command/index.d.ts.map +1 -0
  19. package/dist/command/index.js +23 -0
  20. package/dist/command/index.js.map +1 -0
  21. package/dist/domain/image-error.d.ts +5 -0
  22. package/dist/domain/image-error.d.ts.map +1 -0
  23. package/dist/domain/image-error.js +9 -0
  24. package/dist/domain/image-error.js.map +1 -0
  25. package/dist/domain/list-cursor.d.ts +10 -0
  26. package/dist/domain/list-cursor.d.ts.map +1 -0
  27. package/dist/domain/list-cursor.js +31 -0
  28. package/dist/domain/list-cursor.js.map +1 -0
  29. package/dist/domain/output-schema.d.ts +63 -0
  30. package/dist/domain/output-schema.d.ts.map +1 -0
  31. package/dist/domain/output-schema.js +25 -0
  32. package/dist/domain/output-schema.js.map +1 -0
  33. package/dist/domain/schema.d.ts +69 -0
  34. package/dist/domain/schema.d.ts.map +1 -0
  35. package/dist/domain/schema.js +44 -0
  36. package/dist/domain/schema.js.map +1 -0
  37. package/dist/identity.d.ts +3 -0
  38. package/dist/identity.d.ts.map +1 -0
  39. package/dist/identity.js +3 -0
  40. package/dist/identity.js.map +1 -0
  41. package/dist/mcp/provider.d.ts +2 -0
  42. package/dist/mcp/provider.d.ts.map +1 -0
  43. package/dist/mcp/provider.js +96 -0
  44. package/dist/mcp/provider.js.map +1 -0
  45. package/dist/messages/public-asset/read.msg.d.ts +3 -0
  46. package/dist/messages/public-asset/read.msg.d.ts.map +1 -0
  47. package/dist/messages/public-asset/read.msg.js +10 -0
  48. package/dist/messages/public-asset/read.msg.js.map +1 -0
  49. package/dist/migrations/index.d.ts +3 -0
  50. package/dist/migrations/index.d.ts.map +1 -0
  51. package/dist/migrations/index.js +3 -0
  52. package/dist/migrations/index.js.map +1 -0
  53. package/dist/models/delete-image.model.d.ts +21 -0
  54. package/dist/models/delete-image.model.d.ts.map +1 -0
  55. package/dist/models/delete-image.model.js +36 -0
  56. package/dist/models/delete-image.model.js.map +1 -0
  57. package/dist/models/list-image.model.d.ts +21 -0
  58. package/dist/models/list-image.model.d.ts.map +1 -0
  59. package/dist/models/list-image.model.js +15 -0
  60. package/dist/models/list-image.model.js.map +1 -0
  61. package/dist/models/read-image.model.d.ts +20 -0
  62. package/dist/models/read-image.model.d.ts.map +1 -0
  63. package/dist/models/read-image.model.js +15 -0
  64. package/dist/models/read-image.model.js.map +1 -0
  65. package/dist/models/upload-image.model.d.ts +22 -0
  66. package/dist/models/upload-image.model.d.ts.map +1 -0
  67. package/dist/models/upload-image.model.js +52 -0
  68. package/dist/models/upload-image.model.js.map +1 -0
  69. package/dist/persistence/entities.d.ts +22 -0
  70. package/dist/persistence/entities.d.ts.map +1 -0
  71. package/dist/persistence/entities.js +20 -0
  72. package/dist/persistence/entities.js.map +1 -0
  73. package/dist/persistence/image-query.d.ts +5 -0
  74. package/dist/persistence/image-query.d.ts.map +1 -0
  75. package/dist/persistence/image-query.js +44 -0
  76. package/dist/persistence/image-query.js.map +1 -0
  77. package/dist/persistence/image-store.d.ts +30 -0
  78. package/dist/persistence/image-store.d.ts.map +1 -0
  79. package/dist/persistence/image-store.js +63 -0
  80. package/dist/persistence/image-store.js.map +1 -0
  81. package/dist/presentation/image-projection.d.ts +24 -0
  82. package/dist/presentation/image-projection.d.ts.map +1 -0
  83. package/dist/presentation/image-projection.js +19 -0
  84. package/dist/presentation/image-projection.js.map +1 -0
  85. package/dist/runtime/recover-images.d.ts +5 -0
  86. package/dist/runtime/recover-images.d.ts.map +1 -0
  87. package/dist/runtime/recover-images.js +21 -0
  88. package/dist/runtime/recover-images.js.map +1 -0
  89. package/dist/services/application.service.d.ts +4 -0
  90. package/dist/services/application.service.d.ts.map +1 -0
  91. package/dist/services/application.service.js +22 -0
  92. package/dist/services/application.service.js.map +1 -0
  93. package/dist/services/config.service.d.ts +25 -0
  94. package/dist/services/config.service.d.ts.map +1 -0
  95. package/dist/services/config.service.js +19 -0
  96. package/dist/services/config.service.js.map +1 -0
  97. package/dist/services/database.service.d.ts +3 -0
  98. package/dist/services/database.service.d.ts.map +1 -0
  99. package/dist/services/database.service.js +23 -0
  100. package/dist/services/database.service.js.map +1 -0
  101. package/dist/services/images-runtime.service.d.ts +9 -0
  102. package/dist/services/images-runtime.service.d.ts.map +1 -0
  103. package/dist/services/images-runtime.service.js +16 -0
  104. package/dist/services/images-runtime.service.js.map +1 -0
  105. package/dist/services/images.boot.d.ts +3 -0
  106. package/dist/services/images.boot.d.ts.map +1 -0
  107. package/dist/services/images.boot.js +8 -0
  108. package/dist/services/images.boot.js.map +1 -0
  109. package/dist/services/logger.service.d.ts +4 -0
  110. package/dist/services/logger.service.d.ts.map +1 -0
  111. package/dist/services/logger.service.js +8 -0
  112. package/dist/services/logger.service.js.map +1 -0
  113. package/dist/services/mcp-provider.service.d.ts +3 -0
  114. package/dist/services/mcp-provider.service.d.ts.map +1 -0
  115. package/dist/services/mcp-provider.service.js +15 -0
  116. package/dist/services/mcp-provider.service.js.map +1 -0
  117. package/dist/services/public-assets.service.d.ts +4 -0
  118. package/dist/services/public-assets.service.d.ts.map +1 -0
  119. package/dist/services/public-assets.service.js +15 -0
  120. package/dist/services/public-assets.service.js.map +1 -0
  121. package/dist/services/redis.service.d.ts +3 -0
  122. package/dist/services/redis.service.d.ts.map +1 -0
  123. package/dist/services/redis.service.js +35 -0
  124. package/dist/services/redis.service.js.map +1 -0
  125. package/dist/storage/image-path.d.ts +3 -0
  126. package/dist/storage/image-path.d.ts.map +1 -0
  127. package/dist/storage/image-path.js +17 -0
  128. package/dist/storage/image-path.js.map +1 -0
  129. package/dist/storage/inspect-image.d.ts +9 -0
  130. package/dist/storage/inspect-image.d.ts.map +1 -0
  131. package/dist/storage/inspect-image.js +36 -0
  132. package/dist/storage/inspect-image.js.map +1 -0
  133. package/dist/storage/local-storage.d.ts +10 -0
  134. package/dist/storage/local-storage.d.ts.map +1 -0
  135. package/dist/storage/local-storage.js +65 -0
  136. package/dist/storage/local-storage.js.map +1 -0
  137. package/dist/storage/read-upload-bytes.d.ts +3 -0
  138. package/dist/storage/read-upload-bytes.d.ts.map +1 -0
  139. package/dist/storage/read-upload-bytes.js +11 -0
  140. package/dist/storage/read-upload-bytes.js.map +1 -0
  141. package/package.json +69 -0
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ export const IMAGE_MAX_BYTES = 20 * 1024 * 1024;
3
+ export const BASE64_MAX_BYTES = 6 * 1024 * 1024;
4
+ export const mediaTypeSchema = z.enum(['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/avif']);
5
+ export const statusSchema = z.enum(['uploading', 'ready', 'failed', 'deleting', 'deleted']);
6
+ const idempotencyKey = z.string().min(1).max(128).regex(/^[a-zA-Z0-9._:-]+$/)
7
+ .describe('调用方为一次上传指定的稳定键,1..128 字符,仅字母、数字、点、下划线、冒号和连字符。同一调用方重试相同请求使用原键;不同输入不能复用该键。确认 failed 后新上传使用新键。');
8
+ const filename = z.string().trim().min(1).max(200).refine((value) => !/[\x00-\x1f\x7f/\\]/.test(value), '文件名不得包含路径或控制字符');
9
+ const uploadFields = { idempotencyKey, filename: filename.optional().describe('可选原始文件名,用于元数据和搜索;去除首尾空白后 1..200 字符,不得含路径分隔符或控制字符。不决定存储路径、扩展名或响应头。') };
10
+ export const uploadImageSchema = z.strictObject({ ...uploadFields,
11
+ source: z.literal('base64').describe('提交调用方持有的图片字节,使用 Base64 编码;不接受 URL、文件路径或远程导入。'),
12
+ base64: z.string().min(4).max(Math.ceil(BASE64_MAX_BYTES / 3) * 4).regex(/^[A-Za-z0-9+/]*={0,2}$/).refine((value) => value.length % 4 === 0, 'Invalid Base64 length').describe(`将原图的完整文件字节编码为规范标准 Base64,不能编码文件路径或 URL。字符仅 A-Z、a-z、0-9、+、/;仅末尾可按需使用 1 或 2 个 = 填充,必要的填充不能省略,总长度必须是 4 的倍数。不含空格、换行或 data:image/png;base64, 等 data: 前缀;不接受以 -、_ 替代 +、/ 的 URL-safe 编码。解码后原图最多 6 MiB(${BASE64_MAX_BYTES} 字节),编码文本最多 ${Math.ceil(BASE64_MAX_BYTES / 3) * 4} 个字符(不含 JSON 包装)。必须提交完整编码,不能截断、使用省略号或手工猜测补齐;真实图片格式由服务检测。`),
13
+ });
14
+ export const readImageSchema = z.strictObject({ id: z.uuid().describe('上传、查询或元数据 Resource 返回的不可变图片 UUID,不是文件路径或公开 URL。') });
15
+ export const deleteImageSchema = readImageSchema.extend({ expectedSha256: z.string().regex(/^[a-f0-9]{64}$/).describe('最后读取目标元数据得到的 64 位小写十六进制 SHA-256;先向用户确认目标,摘要不匹配时拒绝删除。与 id 一起标识幂等删除目标,无需删除幂等键。') });
16
+ const timestamp = z.iso.datetime({ offset: true }).transform((value) => new Date(value).toISOString());
17
+ const nonnegativeInt = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
18
+ const positiveInt = nonnegativeInt.min(1);
19
+ export const listImageSchema = z.strictObject({
20
+ createdFrom: timestamp.optional().describe('记录创建时间起点(包含),带时区 ISO-8601,例如 2026-09-01T00:00:00+08:00;不是拍摄时间或上传完成时间。'),
21
+ createdTo: timestamp.optional().describe('记录创建时间终点(不包含),带时区 ISO-8601,必须晚于 createdFrom。查询自然日/月请传下一日/月起点,不使用 23:59:59 近似。'),
22
+ mediaTypes: z.array(mediaTypeSchema).min(1).max(5).transform((v) => [...new Set(v)].sort()).optional().describe('真实图片 MIME type,1..5 项;数组内部 OR、自动去重。与其他筛选条件 AND,尚无格式元数据的记录不匹配。'),
23
+ filenameContains: z.string().trim().min(1).max(200).optional().describe('原始文件名包含文本,忽略大小写;去除首尾空白后 1..200 字符。百分号、下划线按字面量匹配,不是通配符或正则;无文件名记录不匹配。'),
24
+ minBytes: nonnegativeInt.optional().describe('原始文件字节数下界(包含),非负安全整数;无大小元数据的记录不匹配。'),
25
+ maxBytes: nonnegativeInt.optional().describe('原始文件字节数上界(包含),非负安全整数,不小于 minBytes。'),
26
+ minWidth: positiveInt.optional().describe('宽度像素下界(包含),正安全整数;无尺寸元数据的记录不匹配。'),
27
+ maxWidth: positiveInt.optional().describe('宽度像素上界(包含),正安全整数,不小于 minWidth。'),
28
+ minHeight: positiveInt.optional().describe('高度像素下界(包含),正安全整数;无尺寸元数据的记录不匹配。'),
29
+ maxHeight: positiveInt.optional().describe('高度像素上界(包含),正安全整数,不小于 minHeight。'),
30
+ statuses: z.array(statusSchema).min(1).max(5).default(['uploading', 'ready', 'failed', 'deleting']).transform((v) => [...new Set(v)].sort()).describe('状态集合,1..5 项,内部 OR、自动去重;省略时排除 deleted。uploading 上传中、ready 可读、failed 失败、deleting 清理中、deleted 已删除。'),
31
+ createdBy: z.string().min(1).max(128).optional().describe('创建者 MCP clientId(缺失时为 subject),精确匹配,1..128 字符。仅筛选,不是身份声明,不扩大 images:read 授权范围。'),
32
+ sha256: z.string().regex(/^[a-f0-9]{64}$/).optional().describe('原始内容 SHA-256,64 位小写十六进制,精确匹配;可查同内容的多个独立记录,无摘要记录不匹配。'),
33
+ order: z.enum(['asc', 'desc']).default('desc').describe('按创建时间及同方向 UUID 排序:默认 desc(新到旧),asc(旧到新)。翻页时不能改变排序方向。'),
34
+ limit: z.number().int().min(1).max(100).default(20).describe('单页最多条数,1..100,默认 20;可在翻页时调整,不提供全量 total。'),
35
+ cursor: z.string().min(1).max(2048).optional().describe('首请求省略;下一页将上次结果的 nextCursor 原样作为 cursor。保留筛选条件和排序方向,不自行拼接;hasMore=false 时停止。游标不是快照或授权凭证。'),
36
+ }).superRefine((value, ctx) => {
37
+ for (const [lower, upper] of [['minBytes', 'maxBytes'], ['minWidth', 'maxWidth'], ['minHeight', 'maxHeight']]) {
38
+ if (value[lower] !== undefined && value[upper] !== undefined && value[lower] > value[upper])
39
+ ctx.addIssue({ code: 'custom', path: [upper], message: '最大值不得小于最小值' });
40
+ }
41
+ if (value.createdFrom && value.createdTo && value.createdFrom >= value.createdTo)
42
+ ctx.addIssue({ code: 'custom', path: ['createdTo'], message: '结束时间必须晚于开始时间' });
43
+ });
44
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/domain/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const IMAGES_ID = "images";
2
+ export declare const IMAGES_NAMESPACE = "cn.zlooks.images.server";
3
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,WAAW,CAAC;AAClC,eAAO,MAAM,gBAAgB,4BAA4B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export const IMAGES_ID = 'images';
2
+ export const IMAGES_NAMESPACE = 'cn.zlooks.images.server';
3
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAC;AAClC,MAAM,CAAC,MAAM,gBAAgB,GAAG,yBAAyB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function createImagesProvider(): import("@hile/mcp").McpProviderDefinition;
2
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/mcp/provider.ts"],"names":[],"mappings":"AAmBA,wBAAgB,oBAAoB,8CAqDnC"}
@@ -0,0 +1,96 @@
1
+ import { createInvocationContext } from '@hile/context';
2
+ import { defineMcpProvider, defineMcpResource, defineMcpTool } from '@hile/mcp';
3
+ import { loadModel } from '@hile/model';
4
+ import { z } from 'zod';
5
+ import { IMAGES_ID } from '../identity.js';
6
+ import { ImageError } from '../domain/image-error.js';
7
+ import { deleteImageSchema, listImageSchema, readImageSchema, uploadImageSchema } from '../domain/schema.js';
8
+ import { imageMetadataSchema, imagePageSchema } from '../domain/output-schema.js';
9
+ import uploadImageModel from '../models/upload-image.model.js';
10
+ import readImageModel from '../models/read-image.model.js';
11
+ import listImageModel from '../models/list-image.model.js';
12
+ import deleteImageModel from '../models/delete-image.model.js';
13
+ const readAccess = { scopes: ['images:read'] };
14
+ const writeAccess = { scopes: ['images:write'] };
15
+ const readonlyHints = { readOnlyHint: true, idempotentHint: true, destructiveHint: false };
16
+ export function createImagesProvider() {
17
+ return defineMcpProvider({
18
+ id: IMAGES_ID,
19
+ displayName: '图片存储',
20
+ tools: {
21
+ 'upload-image': defineMcpTool({
22
+ name: 'upload-image', title: '上传图片', inputSchema: uploadImageSchema, outputSchema: imageMetadataSchema,
23
+ description: '通过 Base64(解码后至多 6 MiB)上传调用方持有的 JPEG、PNG、WebP、GIF 或 AVIF 原图;不接受 URL 导入或本地路径,不下载远程图片。对完整文件字节进行标准 Base64 编码,提交前按 base64 参数说明检查字符、填充、长度和大小,不要提交截断内容。总像素最多 40 MP(动画按全部帧计),不接受 SVG、不转换原始内容。只有持久化并提交 ready 后成功返回元数据与公开 URL。响应丢失时,同一调用方用原幂等键和完全相同输入重试,不会重复创建;IDEMPOTENCY_CONFLICT 表示同键输入不同。UPLOAD_NOT_READY 时先通过 images.read-image 或 images.list-image 确认状态:uploading 不另建重复上传,确认 failed 后新上传使用新幂等键,deleted 不会复活。文件名仅用于搜索,不能指定存储路径。',
24
+ access: writeAccess,
25
+ annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false },
26
+ execution: { timeoutMs: 60_000, retry: 'never' },
27
+ }, (input, context) => toolResult(context, 'images:write', async (invocation) => {
28
+ const actor = z.string().min(1).max(128).parse(context.principal?.clientId ?? context.principal?.subject);
29
+ return loadModel(uploadImageModel, { actor, request: uploadImageSchema.parse(input) }, invocation);
30
+ })),
31
+ 'list-image': defineMcpTool({
32
+ name: 'list-image', title: '查询图片', inputSchema: listImageSchema, outputSchema: imagePageSchema,
33
+ description: '按创建时间、格式、文件名、字节数、宽高、状态、上传者及 SHA-256 组合查询;不同条件 AND,集合内部 OR。时间范围左闭右开,默认创建时间倒序;每页默认 20、最多 100 条。将返回的 nextCursor 作为 cursor 查询下一页,保持筛选条件及排序;hasMore=false 时停止。默认不返回 deleted,不提供 total;空结果不是错误。分页不是跨请求快照,INVALID_CURSOR 时检查条件,变更条件需从第一页重新查询。',
34
+ access: readAccess, annotations: readonlyHints,
35
+ execution: { timeoutMs: 10_000, retry: 'idempotent-failover' },
36
+ }, (input, context) => toolResult(context, 'images:read', (invocation) => loadModel(listImageModel, listImageSchema.parse(input), invocation))),
37
+ 'read-image': defineMcpTool({
38
+ name: 'read-image', title: '读取图片元数据', inputSchema: readImageSchema, outputSchema: imageMetadataSchema,
39
+ description: '按图片 ID 读取元数据、当前状态和公开原图地址。不会返回图片二进制或本地路径;只有 ready 状态有公开 URL。可用于检查上传结果,或在删除前确认 SHA-256 与目标图片。',
40
+ access: readAccess, annotations: readonlyHints,
41
+ execution: { timeoutMs: 10_000, retry: 'idempotent-failover' },
42
+ }, (input, context) => toolResult(context, 'images:read', (invocation) => loadModel(readImageModel, readImageSchema.parse(input), invocation))),
43
+ 'delete-image': defineMcpTool({
44
+ name: 'delete-image', title: '删除图片', inputSchema: deleteImageSchema, outputSchema: imageMetadataSchema,
45
+ description: '永久删除本地原图并禁用新的源站读取。先用 images.read-image 读取目标并向用户确认,再提交 id 和 expectedSha256,无需删除幂等键。只允许 ready、deleting 或已 deleted 的记录;HASH_CONFLICT 或 STATE_CONFLICT 时重新读取,不能猜测摘要重试。清理失败或响应丢失后用原 ID 与摘要重试;并发和重复删除安全,成功返回 deleted 元数据且保留首次 deletedAt。此操作不能撤销,也无法清除浏览器或第三方已缓存的副本。',
46
+ access: writeAccess,
47
+ annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: true },
48
+ execution: { timeoutMs: 15_000, retry: 'never' },
49
+ }, (input, context) => toolResult(context, 'images:write', (invocation) => loadModel(deleteImageModel, deleteImageSchema.parse(input), invocation))),
50
+ },
51
+ resources: {
52
+ image: defineMcpResource({
53
+ kind: 'template', name: 'image', uriTemplate: 'images://image/{id}',
54
+ title: '图片元数据', mimeType: 'application/json', access: readAccess,
55
+ description: '由应用按图片 UUID 读取的 JSON 元数据,与 images.read-image 返回字段一致。包含状态、原始文件名、媒体属性、SHA-256、创建者和时间;只有 ready 返回公开 URL。不返回图片二进制、Base64、本地路径或上传幂等键,也不触发上传、删除或恢复。图片展示使用返回的公开 URL;需要 images:read 权限。',
56
+ }, async (variables, context) => {
57
+ assertAccess(context, 'images:read');
58
+ try {
59
+ const request = readImageSchema.parse({ id: variables.id });
60
+ const result = await loadModel(readImageModel, request, createInvocationContext(context.executionContext, context.signal));
61
+ return { contents: [{ uri: `images://image/${request.id}`, mimeType: 'application/json', text: JSON.stringify(result) }] };
62
+ }
63
+ catch (error) {
64
+ context.signal.throwIfAborted();
65
+ throw safeError(error);
66
+ }
67
+ }),
68
+ },
69
+ });
70
+ }
71
+ function assertAccess(context, scope) {
72
+ context.signal.throwIfAborted();
73
+ const scopes = context.principal?.scopes ?? [];
74
+ if (!scopes.includes('*') && !scopes.includes(scope))
75
+ throw new ImageError('FORBIDDEN', '没有图片操作权限');
76
+ }
77
+ async function toolResult(context, scope, run) {
78
+ assertAccess(context, scope);
79
+ try {
80
+ const result = await run(createInvocationContext(context.executionContext, context.signal));
81
+ return { content: [{ type: 'text', text: JSON.stringify(result) }], structuredContent: result };
82
+ }
83
+ catch (error) {
84
+ context.signal.throwIfAborted();
85
+ const safe = safeError(error);
86
+ return { isError: true, content: [{ type: 'text', text: `${safe.code}: ${safe.message}` }] };
87
+ }
88
+ }
89
+ function safeError(error) {
90
+ if (error instanceof ImageError)
91
+ return error;
92
+ if (error instanceof z.ZodError)
93
+ return new ImageError('INVALID_INPUT', '图片请求参数无效');
94
+ return new ImageError('UNAVAILABLE', '图片服务暂时不可用,请读取状态后重试');
95
+ }
96
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/mcp/provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@hile/message-loader").MessageRegisterProps<any, import("@hile/micro").MicroMessageHandlerExtras>;
2
+ export default _default;
3
+ //# sourceMappingURL=read.msg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.msg.d.ts","sourceRoot":"","sources":["../../../src/messages/public-asset/read.msg.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import { loadService } from '@hile/core';
2
+ import { defineMicroMessage } from '@hile/micro';
3
+ import { readImageAsset } from '../../assets/read-image-asset.js';
4
+ import imagesRuntimeService from '../../services/images-runtime.service.js';
5
+ export default defineMicroMessage(async function* ({ data, invocation }) {
6
+ invocation.signal.throwIfAborted();
7
+ const { store, storage } = await loadService(imagesRuntimeService);
8
+ yield* readImageAsset(store, storage, data, invocation.signal);
9
+ });
10
+ //# sourceMappingURL=read.msg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.msg.js","sourceRoot":"","sources":["../../../src/messages/public-asset/read.msg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,oBAAoB,MAAM,0CAA0C,CAAC;AAE5E,eAAe,kBAAkB,CAAC,KAAK,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;IACrE,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,WAAW,CAAC,oBAAoB,CAAC,CAAC;IACnE,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DatabaseMigration } from '@zlooks.cn/database-command';
2
+ export declare const imagesMigrations: readonly DatabaseMigration[];
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAGrE,eAAO,MAAM,gBAAgB,EAAE,SAAS,iBAAiB,EAAO,CAAC"}
@@ -0,0 +1,3 @@
1
+ // Schema synchronization belongs to startup; no historical data migration exists yet.
2
+ export const imagesMigrations = [];
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,MAAM,CAAC,MAAM,gBAAgB,GAAiC,EAAE,CAAC"}
@@ -0,0 +1,21 @@
1
+ declare const _default: import("@hile/model").ModelDefinition<{
2
+ id: string;
3
+ expectedSha256: string;
4
+ }, {
5
+ id: string;
6
+ originalFilename: string | null;
7
+ status: "deleted" | "deleting" | "failed" | "ready" | "uploading";
8
+ url: string | null;
9
+ mediaType: string | null;
10
+ byteSize: number | null;
11
+ width: number | null;
12
+ height: number | null;
13
+ sha256: string | null;
14
+ createdBy: string;
15
+ createdAt: string;
16
+ readyAt: string | null;
17
+ deletedAt: string | null;
18
+ failureCode: string | null;
19
+ }>;
20
+ export default _default;
21
+ //# sourceMappingURL=delete-image.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-image.model.d.ts","sourceRoot":"","sources":["../../src/models/delete-image.model.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ import { loadService } from '@hile/core';
2
+ import { defineModel } from '@hile/model';
3
+ import { ImageError } from '../domain/image-error.js';
4
+ import { deleteImageSchema } from '../domain/schema.js';
5
+ import { imageProjection } from '../presentation/image-projection.js';
6
+ import imagesRuntimeService from '../services/images-runtime.service.js';
7
+ export default defineModel(async (input, invocation) => {
8
+ invocation.signal.throwIfAborted();
9
+ const request = deleteImageSchema.parse(input);
10
+ const { store, storage, origin } = await loadService(imagesRuntimeService);
11
+ invocation.signal.throwIfAborted();
12
+ const row = await store.read(request.id);
13
+ if (row.sha256 !== request.expectedSha256)
14
+ throw new ImageError('HASH_CONFLICT', '图片摘要不匹配,删除未执行');
15
+ if (row.status === 'deleted')
16
+ return imageProjection(row, origin());
17
+ if (row.status !== 'ready' && row.status !== 'deleting')
18
+ throw new ImageError('STATE_CONFLICT', '当前图片不可删除');
19
+ invocation.signal.throwIfAborted();
20
+ if (row.status === 'ready' && !await store.compareAndSet(row.id, 'ready', { status: 'deleting' }, invocation.signal)) {
21
+ const current = await store.read(row.id);
22
+ if (current.status === 'deleted')
23
+ return imageProjection(current, origin());
24
+ if (current.status !== 'deleting')
25
+ throw new ImageError('STATE_CONFLICT', '图片状态已改变,请重新读取');
26
+ }
27
+ // Once origin reads are disabled, finish cleanup even if the caller disconnects.
28
+ if (row.relativePath)
29
+ await storage.remove(row.relativePath);
30
+ await store.compareAndSet(row.id, 'deleting', { status: 'deleted', deletedAt: new Date() });
31
+ const deleted = await store.read(row.id);
32
+ if (deleted.status !== 'deleted')
33
+ throw new ImageError('STATE_CONFLICT', '图片状态已改变,请重新读取');
34
+ return imageProjection(deleted, origin());
35
+ });
36
+ //# sourceMappingURL=delete-image.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-image.model.js","sourceRoot":"","sources":["../../src/models/delete-image.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAyB,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAsB,MAAM,qCAAqC,CAAC;AAC1F,OAAO,oBAAoB,MAAM,uCAAuC,CAAC;AAEzE,eAAe,WAAW,CAAkC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;IACtF,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,oBAAoB,CAAC,CAAC;IAC3E,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO,CAAC,cAAc;QAAE,MAAM,IAAI,UAAU,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;IAClG,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;IACpE,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU;QAAE,MAAM,IAAI,UAAU,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;IAC5G,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACrH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5E,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU;YAAE,MAAM,IAAI,UAAU,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;IAC7F,CAAC;IACD,iFAAiF;IACjF,IAAI,GAAG,CAAC,YAAY;QAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC7D,MAAM,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IAC5F,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACzC,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;QAAE,MAAM,IAAI,UAAU,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;IAC1F,OAAO,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type ImagePage } from '../presentation/image-projection.js';
2
+ declare const _default: import("@hile/model").ModelDefinition<{
3
+ createdFrom?: string | undefined;
4
+ createdTo?: string | undefined;
5
+ mediaTypes?: ("image/avif" | "image/gif" | "image/jpeg" | "image/png" | "image/webp")[] | undefined;
6
+ filenameContains?: string | undefined;
7
+ minBytes?: number | undefined;
8
+ maxBytes?: number | undefined;
9
+ minWidth?: number | undefined;
10
+ maxWidth?: number | undefined;
11
+ minHeight?: number | undefined;
12
+ maxHeight?: number | undefined;
13
+ statuses?: ("deleted" | "deleting" | "failed" | "ready" | "uploading")[] | undefined;
14
+ createdBy?: string | undefined;
15
+ sha256?: string | undefined;
16
+ order?: "asc" | "desc" | undefined;
17
+ limit?: number | undefined;
18
+ cursor?: string | undefined;
19
+ }, ImagePage>;
20
+ export default _default;
21
+ //# sourceMappingURL=list-image.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-image.model.d.ts","sourceRoot":"","sources":["../../src/models/list-image.model.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,qCAAqC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { loadService } from '@hile/core';
2
+ import { defineModel } from '@hile/model';
3
+ import { listImageSchema } from '../domain/schema.js';
4
+ import { imageProjection } from '../presentation/image-projection.js';
5
+ import imagesRuntimeService from '../services/images-runtime.service.js';
6
+ export default defineModel(async (input, invocation) => {
7
+ invocation.signal.throwIfAborted();
8
+ const request = listImageSchema.parse(input);
9
+ const { store, origin } = await loadService(imagesRuntimeService);
10
+ invocation.signal.throwIfAborted();
11
+ const result = await store.list(request);
12
+ invocation.signal.throwIfAborted();
13
+ return { ...result, items: result.items.map((row) => imageProjection(row, origin())) };
14
+ });
15
+ //# sourceMappingURL=list-image.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-image.model.js","sourceRoot":"","sources":["../../src/models/list-image.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAyB,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAkB,MAAM,qCAAqC,CAAC;AACtF,OAAO,oBAAoB,MAAM,uCAAuC,CAAC;AAEzE,eAAe,WAAW,CAA8B,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;IAClF,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,oBAAoB,CAAC,CAAC;IAClE,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;AACzF,CAAC,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ declare const _default: import("@hile/model").ModelDefinition<{
2
+ id: string;
3
+ }, {
4
+ id: string;
5
+ originalFilename: string | null;
6
+ status: "deleted" | "deleting" | "failed" | "ready" | "uploading";
7
+ url: string | null;
8
+ mediaType: string | null;
9
+ byteSize: number | null;
10
+ width: number | null;
11
+ height: number | null;
12
+ sha256: string | null;
13
+ createdBy: string;
14
+ createdAt: string;
15
+ readyAt: string | null;
16
+ deletedAt: string | null;
17
+ failureCode: string | null;
18
+ }>;
19
+ export default _default;
20
+ //# sourceMappingURL=read-image.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-image.model.d.ts","sourceRoot":"","sources":["../../src/models/read-image.model.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import { loadService } from '@hile/core';
2
+ import { defineModel } from '@hile/model';
3
+ import { readImageSchema } from '../domain/schema.js';
4
+ import { imageProjection } from '../presentation/image-projection.js';
5
+ import imagesRuntimeService from '../services/images-runtime.service.js';
6
+ export default defineModel(async (input, invocation) => {
7
+ invocation.signal.throwIfAborted();
8
+ const request = readImageSchema.parse(input);
9
+ const { store, origin } = await loadService(imagesRuntimeService);
10
+ invocation.signal.throwIfAborted();
11
+ const row = await store.read(request.id);
12
+ invocation.signal.throwIfAborted();
13
+ return imageProjection(row, origin());
14
+ });
15
+ //# sourceMappingURL=read-image.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-image.model.js","sourceRoot":"","sources":["../../src/models/read-image.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAuB,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAsB,MAAM,qCAAqC,CAAC;AAC1F,OAAO,oBAAoB,MAAM,uCAAuC,CAAC;AAEzE,eAAe,WAAW,CAAgC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;IACpF,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,oBAAoB,CAAC,CAAC;IAClE,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,OAAO,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type UploadImageInput } from '../domain/schema.js';
2
+ declare const _default: import("@hile/model").ModelDefinition<{
3
+ actor: string;
4
+ request: UploadImageInput;
5
+ }, {
6
+ id: string;
7
+ originalFilename: string | null;
8
+ status: "deleted" | "deleting" | "failed" | "ready" | "uploading";
9
+ url: string | null;
10
+ mediaType: string | null;
11
+ byteSize: number | null;
12
+ width: number | null;
13
+ height: number | null;
14
+ sha256: string | null;
15
+ createdBy: string;
16
+ createdAt: string;
17
+ readyAt: string | null;
18
+ deletedAt: string | null;
19
+ failureCode: string | null;
20
+ }>;
21
+ export default _default;
22
+ //# sourceMappingURL=upload-image.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-image.model.d.ts","sourceRoot":"","sources":["../../src/models/upload-image.model.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;;WAO3C,MAAM;aAAW,gBAAgB"}
@@ -0,0 +1,52 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { loadService } from '@hile/core';
3
+ import { defineModel } from '@hile/model';
4
+ import { ImageError } from '../domain/image-error.js';
5
+ import { uploadImageSchema } from '../domain/schema.js';
6
+ import { inspectImage } from '../storage/inspect-image.js';
7
+ import { imageRelativePath } from '../storage/image-path.js';
8
+ import { readUploadBytes } from '../storage/read-upload-bytes.js';
9
+ import { imageProjection } from '../presentation/image-projection.js';
10
+ import imagesRuntimeService from '../services/images-runtime.service.js';
11
+ export default defineModel(async (input, invocation) => {
12
+ invocation.signal.throwIfAborted();
13
+ const request = uploadImageSchema.parse(input.request);
14
+ const { store, storage, origin } = await loadService(imagesRuntimeService);
15
+ const signal = AbortSignal.any([invocation.signal, AbortSignal.timeout(45_000)]);
16
+ signal.throwIfAborted();
17
+ const hash = createHash('sha256').update(JSON.stringify(request)).digest('hex');
18
+ const reserved = await store.reserve(input.actor, request.idempotencyKey, hash, request.filename ?? null);
19
+ if (!reserved.created) {
20
+ if (reserved.row.status === 'ready')
21
+ return imageProjection(reserved.row, origin());
22
+ throw new ImageError('UPLOAD_NOT_READY', '该幂等请求未完成或已失效,请读取图片状态;失败后使用新幂等键');
23
+ }
24
+ const id = reserved.row.id;
25
+ try {
26
+ const bytes = await readUploadBytes(request, signal);
27
+ signal.throwIfAborted();
28
+ const metadata = await inspectImage(bytes);
29
+ signal.throwIfAborted();
30
+ const relativePath = imageRelativePath(id, metadata.extension, reserved.row.createdAt);
31
+ await store.transition(id, 'uploading', { ...metadata, relativePath }, signal);
32
+ await storage.publish(relativePath, bytes);
33
+ signal.throwIfAborted();
34
+ await store.transition(id, 'uploading', { status: 'ready', readyAt: new Date() }, signal);
35
+ return imageProjection(await store.read(id), origin());
36
+ }
37
+ catch (error) {
38
+ // Re-read before cleanup: a lost DB reply may conceal a successful ready commit.
39
+ const current = await store.read(id);
40
+ if (current.status === 'ready')
41
+ return imageProjection(current, origin());
42
+ if (current.status === 'uploading') {
43
+ await store.transition(id, 'uploading', {
44
+ status: 'failed', failureCode: error instanceof ImageError ? error.code : 'UPLOAD_FAILED',
45
+ });
46
+ }
47
+ if (current.relativePath)
48
+ await storage.remove(current.relativePath);
49
+ throw error;
50
+ }
51
+ });
52
+ //# sourceMappingURL=upload-image.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-image.model.js","sourceRoot":"","sources":["../../src/models/upload-image.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAyB,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAsB,MAAM,qCAAqC,CAAC;AAC1F,OAAO,oBAAoB,MAAM,uCAAuC,CAAC;AAEzE,eAAe,WAAW,CAA8D,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;IAClH,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,oBAAoB,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACjF,MAAM,CAAC,cAAc,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAChF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;IAC1G,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACtB,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO,eAAe,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;QACpF,MAAM,IAAI,UAAU,CAAC,kBAAkB,EAAE,gCAAgC,CAAC,CAAC;IAC7E,CAAC;IACD,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACrD,MAAM,CAAC,cAAc,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,cAAc,EAAE,CAAC;QACxB,MAAM,YAAY,GAAG,iBAAiB,CAAC,EAAE,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvF,MAAM,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,WAAW,EAAE,EAAE,GAAG,QAAQ,EAAE,YAAY,EAAE,EAAE,MAAM,CAAC,CAAC;QAC/E,MAAM,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,cAAc,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QAC1F,OAAO,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iFAAiF;QACjF,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrC,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1E,IAAI,OAAO,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACnC,MAAM,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,WAAW,EAAE;gBACtC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe;aAC1F,CAAC,CAAC;QACL,CAAC;QACD,IAAI,OAAO,CAAC,YAAY;YAAE,MAAM,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACrE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { EntitySchema } from 'typeorm';
2
+ export interface ImageRow {
3
+ id: string;
4
+ createdBy: string;
5
+ uploadIdempotencyKey: string;
6
+ uploadRequestFingerprint: string;
7
+ status: 'uploading' | 'ready' | 'failed' | 'deleting' | 'deleted';
8
+ originalFilename: string | null;
9
+ mediaType: string | null;
10
+ extension: string | null;
11
+ byteSize: number | null;
12
+ width: number | null;
13
+ height: number | null;
14
+ sha256: string | null;
15
+ relativePath: string | null;
16
+ failureCode: string | null;
17
+ createdAt: Date;
18
+ readyAt: Date | null;
19
+ deletedAt: Date | null;
20
+ }
21
+ export declare const ImageEntity: EntitySchema<ImageRow>;
22
+ //# sourceMappingURL=entities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../../src/persistence/entities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAC;IAAC,wBAAwB,EAAE,MAAM,CAAC;IAC9F,MAAM,EAAE,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAClE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACpF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5F,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACxD,SAAS,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;CAC/D;AACD,eAAO,MAAM,WAAW,wBAiBtB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { EntitySchema } from 'typeorm';
2
+ export const ImageEntity = new EntitySchema({
3
+ name: 'Image', tableName: 'images',
4
+ columns: {
5
+ id: { type: 'uuid', primary: true }, createdBy: { name: 'created_by', type: 'varchar', length: 128 },
6
+ uploadIdempotencyKey: { name: 'upload_idempotency_key', type: 'varchar', length: 128 },
7
+ uploadRequestFingerprint: { name: 'upload_request_fingerprint', type: 'varchar', length: 64 },
8
+ status: { type: 'varchar', length: 16 }, originalFilename: { name: 'original_filename', type: 'varchar', length: 200, nullable: true },
9
+ mediaType: { name: 'media_type', type: 'varchar', length: 64, nullable: true }, extension: { type: 'varchar', length: 8, nullable: true },
10
+ byteSize: { name: 'byte_size', type: 'integer', nullable: true }, width: { type: 'integer', nullable: true }, height: { type: 'integer', nullable: true },
11
+ sha256: { type: 'varchar', length: 64, nullable: true }, relativePath: { name: 'relative_path', type: 'varchar', length: 128, nullable: true },
12
+ failureCode: { name: 'failure_code', type: 'varchar', length: 64, nullable: true },
13
+ createdAt: { name: 'created_at', type: 'timestamptz', precision: 3, createDate: true },
14
+ readyAt: { name: 'ready_at', type: 'timestamptz', precision: 3, nullable: true },
15
+ deletedAt: { name: 'deleted_at', type: 'timestamptz', precision: 3, nullable: true },
16
+ },
17
+ uniques: [{ name: 'images_upload_actor_key', columns: ['createdBy', 'uploadIdempotencyKey'] }],
18
+ indices: [{ columns: ['createdAt', 'id'] }, { columns: ['status', 'createdAt', 'id'] }, { columns: ['createdBy', 'createdAt', 'id'] }, { columns: ['sha256'] }],
19
+ });
20
+ //# sourceMappingURL=entities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/persistence/entities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAUvC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,YAAY,CAAW;IACpD,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ;IAClC,OAAO,EAAE;QACP,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE;QACpG,oBAAoB,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE;QACtF,wBAAwB,EAAE,EAAE,IAAI,EAAE,4BAA4B,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE;QAC7F,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE;QACtI,SAAS,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;QACzI,QAAQ,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;QACzJ,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC9I,WAAW,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;QAClF,SAAS,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE;QACtF,OAAO,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;QAChF,SAAS,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;KACrF;IACD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,sBAAsB,CAAC,EAAE,CAAC;IAC9F,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;CAChK,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { type FindManyOptions } from 'typeorm';
2
+ import type { ListImageInput } from '../domain/schema.js';
3
+ import type { ImageRow } from './entities.js';
4
+ export declare function imageQueryOptions(query: ListImageInput): FindManyOptions<ImageRow>;
5
+ //# sourceMappingURL=image-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-query.d.ts","sourceRoot":"","sources":["../../src/persistence/image-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAAC,QAAQ,CAAC,CAyBlF"}
@@ -0,0 +1,44 @@
1
+ import { And, Equal, ILike, In, LessThan, LessThanOrEqual, MoreThan, MoreThanOrEqual, } from 'typeorm';
2
+ import { decodeCursor } from '../domain/list-cursor.js';
3
+ export function imageQueryOptions(query) {
4
+ const filter = { status: In(query.statuses) };
5
+ if (query.createdBy)
6
+ filter.createdBy = Equal(query.createdBy);
7
+ if (query.sha256)
8
+ filter.sha256 = Equal(query.sha256);
9
+ if (query.mediaTypes)
10
+ filter.mediaType = In(query.mediaTypes);
11
+ if (query.filenameContains) {
12
+ filter.originalFilename = ILike(`%${query.filenameContains.replace(/[\\%_]/g, '\\$&')}%`);
13
+ }
14
+ const times = [];
15
+ if (query.createdFrom)
16
+ times.push(MoreThanOrEqual(new Date(query.createdFrom)));
17
+ if (query.createdTo)
18
+ times.push(LessThan(new Date(query.createdTo)));
19
+ if (times.length)
20
+ filter.createdAt = And(...times);
21
+ addBounds(filter, 'byteSize', query.minBytes, query.maxBytes);
22
+ addBounds(filter, 'width', query.minWidth, query.maxWidth);
23
+ addBounds(filter, 'height', query.minHeight, query.maxHeight);
24
+ const cursor = decodeCursor(query);
25
+ const compare = query.order === 'asc' ? MoreThan : LessThan;
26
+ return {
27
+ where: cursor ? [
28
+ { ...filter, createdAt: And(...times, compare(cursor.createdAt)) },
29
+ { ...filter, createdAt: And(...times, Equal(cursor.createdAt)), id: compare(cursor.id) },
30
+ ] : filter,
31
+ order: { createdAt: query.order, id: query.order },
32
+ take: query.limit + 1,
33
+ };
34
+ }
35
+ function addBounds(filter, column, min, max) {
36
+ const bounds = [];
37
+ if (min !== undefined)
38
+ bounds.push(MoreThanOrEqual(min));
39
+ if (max !== undefined)
40
+ bounds.push(LessThanOrEqual(max));
41
+ if (bounds.length)
42
+ filter[column] = And(...bounds);
43
+ }
44
+ //# sourceMappingURL=image-query.js.map