@aotter/mantle-runtime 0.0.11-alpha.13 → 0.0.11-alpha.15

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 (103) hide show
  1. package/dist/domain/model/TemplateRegistry.d.ts +3 -0
  2. package/dist/domain/model/TemplateRegistry.d.ts.map +1 -1
  3. package/dist/domain/model/TemplateRegistry.js.map +1 -1
  4. package/dist/domain/port/MediaAssetRepository.d.ts +34 -0
  5. package/dist/domain/port/MediaAssetRepository.d.ts.map +1 -0
  6. package/dist/domain/port/MediaAssetRepository.js +2 -0
  7. package/dist/domain/port/MediaAssetRepository.js.map +1 -0
  8. package/dist/domain/port/MediaStorage.d.ts +159 -44
  9. package/dist/domain/port/MediaStorage.d.ts.map +1 -1
  10. package/dist/domain/port/MediaStorage.js +12 -1
  11. package/dist/domain/port/MediaStorage.js.map +1 -1
  12. package/dist/domain/port/SiteConfigRepository.d.ts +2 -2
  13. package/dist/domain/port/SiteConfigRepository.d.ts.map +1 -1
  14. package/dist/domain/port/index.d.ts +3 -1
  15. package/dist/domain/port/index.d.ts.map +1 -1
  16. package/dist/domain/port/index.js +1 -0
  17. package/dist/domain/port/index.js.map +1 -1
  18. package/dist/domain/service/HtmlRenderer.d.ts +3 -0
  19. package/dist/domain/service/HtmlRenderer.d.ts.map +1 -1
  20. package/dist/domain/service/HtmlRenderer.js +7 -1
  21. package/dist/domain/service/HtmlRenderer.js.map +1 -1
  22. package/dist/domain/service/io/MediaAssetReferences.d.ts +13 -0
  23. package/dist/domain/service/io/MediaAssetReferences.d.ts.map +1 -0
  24. package/dist/domain/service/io/MediaAssetReferences.js +48 -0
  25. package/dist/domain/service/io/MediaAssetReferences.js.map +1 -0
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/infrastructure/boot/canonicalMigrations.d.ts.map +1 -1
  31. package/dist/infrastructure/boot/canonicalMigrations.js +17 -0
  32. package/dist/infrastructure/boot/canonicalMigrations.js.map +1 -1
  33. package/dist/infrastructure/mcp/McpJsonRpcDispatcher.d.ts +9 -5
  34. package/dist/infrastructure/mcp/McpJsonRpcDispatcher.d.ts.map +1 -1
  35. package/dist/infrastructure/mcp/McpJsonRpcDispatcher.js +84 -11
  36. package/dist/infrastructure/mcp/McpJsonRpcDispatcher.js.map +1 -1
  37. package/dist/infrastructure/mcp/McpToolCatalog.d.ts +8 -4
  38. package/dist/infrastructure/mcp/McpToolCatalog.d.ts.map +1 -1
  39. package/dist/infrastructure/mcp/McpToolCatalog.js +108 -24
  40. package/dist/infrastructure/mcp/McpToolCatalog.js.map +1 -1
  41. package/dist/infrastructure/persistence/DatabaseMediaAssetRepository.d.ts +27 -0
  42. package/dist/infrastructure/persistence/DatabaseMediaAssetRepository.d.ts.map +1 -0
  43. package/dist/infrastructure/persistence/DatabaseMediaAssetRepository.js +80 -0
  44. package/dist/infrastructure/persistence/DatabaseMediaAssetRepository.js.map +1 -0
  45. package/dist/infrastructure/persistence/DatabaseSiteConfigRepository.d.ts +2 -2
  46. package/dist/infrastructure/persistence/DatabaseSiteConfigRepository.d.ts.map +1 -1
  47. package/dist/infrastructure/persistence/DatabaseSiteConfigRepository.js +24 -3
  48. package/dist/infrastructure/persistence/DatabaseSiteConfigRepository.js.map +1 -1
  49. package/dist/infrastructure/persistence/index.d.ts +1 -0
  50. package/dist/infrastructure/persistence/index.d.ts.map +1 -1
  51. package/dist/infrastructure/persistence/index.js +1 -0
  52. package/dist/infrastructure/persistence/index.js.map +1 -1
  53. package/dist/infrastructure/render/HtmlPublishOrchestrator.d.ts +3 -1
  54. package/dist/infrastructure/render/HtmlPublishOrchestrator.d.ts.map +1 -1
  55. package/dist/infrastructure/render/HtmlPublishOrchestrator.js +8 -2
  56. package/dist/infrastructure/render/HtmlPublishOrchestrator.js.map +1 -1
  57. package/dist/runtime.d.ts +12 -4
  58. package/dist/runtime.d.ts.map +1 -1
  59. package/dist/runtime.js +12 -7
  60. package/dist/runtime.js.map +1 -1
  61. package/dist/usecase/dto/media/index.d.ts +42 -15
  62. package/dist/usecase/dto/media/index.d.ts.map +1 -1
  63. package/dist/usecase/dto/media/index.js +10 -3
  64. package/dist/usecase/dto/media/index.js.map +1 -1
  65. package/dist/usecase/media/CommitMediaUploadUseCase.d.ts +14 -4
  66. package/dist/usecase/media/CommitMediaUploadUseCase.d.ts.map +1 -1
  67. package/dist/usecase/media/CommitMediaUploadUseCase.js +30 -19
  68. package/dist/usecase/media/CommitMediaUploadUseCase.js.map +1 -1
  69. package/dist/usecase/media/CreateMediaUploadUseCase.d.ts +34 -2
  70. package/dist/usecase/media/CreateMediaUploadUseCase.d.ts.map +1 -1
  71. package/dist/usecase/media/CreateMediaUploadUseCase.js +184 -41
  72. package/dist/usecase/media/CreateMediaUploadUseCase.js.map +1 -1
  73. package/dist/usecase/media/PendingUploadRecord.d.ts +29 -10
  74. package/dist/usecase/media/PendingUploadRecord.d.ts.map +1 -1
  75. package/dist/usecase/media/UploadMediaVariantUseCase.d.ts +35 -0
  76. package/dist/usecase/media/UploadMediaVariantUseCase.d.ts.map +1 -0
  77. package/dist/usecase/media/UploadMediaVariantUseCase.js +75 -0
  78. package/dist/usecase/media/UploadMediaVariantUseCase.js.map +1 -0
  79. package/dist/usecase/media/diagnostics.d.ts +19 -2
  80. package/dist/usecase/media/diagnostics.d.ts.map +1 -1
  81. package/dist/usecase/media/diagnostics.js +66 -7
  82. package/dist/usecase/media/diagnostics.js.map +1 -1
  83. package/dist/usecase/media/index.d.ts +1 -0
  84. package/dist/usecase/media/index.d.ts.map +1 -1
  85. package/dist/usecase/media/index.js +1 -0
  86. package/dist/usecase/media/index.js.map +1 -1
  87. package/dist/usecase/media/mediaAllowlist.d.ts +1 -1
  88. package/dist/usecase/media/mediaAllowlist.d.ts.map +1 -1
  89. package/dist/usecase/media/mediaAllowlist.js +3 -0
  90. package/dist/usecase/media/mediaAllowlist.js.map +1 -1
  91. package/dist/usecase/render/PreviewEntryUseCase.d.ts +3 -1
  92. package/dist/usecase/render/PreviewEntryUseCase.d.ts.map +1 -1
  93. package/dist/usecase/render/PreviewEntryUseCase.js +6 -1
  94. package/dist/usecase/render/PreviewEntryUseCase.js.map +1 -1
  95. package/dist/usecase/render/RenderEntryLiveUseCase.d.ts +3 -1
  96. package/dist/usecase/render/RenderEntryLiveUseCase.d.ts.map +1 -1
  97. package/dist/usecase/render/RenderEntryLiveUseCase.js +6 -1
  98. package/dist/usecase/render/RenderEntryLiveUseCase.js.map +1 -1
  99. package/dist/usecase/render/RenderListLiveUseCase.d.ts +3 -1
  100. package/dist/usecase/render/RenderListLiveUseCase.d.ts.map +1 -1
  101. package/dist/usecase/render/RenderListLiveUseCase.js +6 -1
  102. package/dist/usecase/render/RenderListLiveUseCase.js.map +1 -1
  103. package/package.json +4 -4
@@ -1,18 +1,50 @@
1
1
  import type { Clock } from "../../domain/port/Clock.js";
2
+ import type { IdGenerator } from "../../domain/port/IdGenerator.js";
2
3
  import type { KvCache } from "../../domain/port/KvCache.js";
3
4
  import type { MediaStorage } from "../../domain/port/MediaStorage.js";
4
5
  import type { SiteConfigRepository } from "../../domain/port/SiteConfigRepository.js";
5
6
  import type { CreateMediaUploadRequest, CreateMediaUploadResponse } from "../dto/media/index.js";
7
+ /**
8
+ * Issue presigned upload capabilities for every variant the agent
9
+ * declares for one logical media asset (#272). The asset's `id` is
10
+ * minted here and surfaces as `uploadGroupId` in the response — the
11
+ * caller passes it back to `commit_media_upload`.
12
+ *
13
+ * Optimization runs agent-side via `@aotter/mantle-media-tools`. This
14
+ * use case enforces policy only — never transforms bytes:
15
+ * - purpose must be declared in `siteDefaults.media.purposes`
16
+ * - variants must cover every required mime for that purpose
17
+ * - each variant's byteSize ≤ `policy.maxBytes[mime]`
18
+ * - mime allowlist (still gates per variant; SVG opt-in via ctor)
19
+ * - "suspicious shape" heuristic: modern formats (avif / webp) MUST
20
+ * NOT be larger than the fallback (jpeg) — uploader skipped
21
+ * optimization → hard fail rather than emit a `<picture>` slower
22
+ * than the bare `<img>`.
23
+ */
6
24
  export declare class CreateMediaUploadUseCase {
7
25
  private readonly storage;
8
26
  private readonly kv;
9
27
  private readonly clock;
28
+ private readonly idgen;
10
29
  private readonly siteConfig;
11
30
  private readonly opts;
12
- constructor(storage: MediaStorage, kv: KvCache, clock: Clock, siteConfig: SiteConfigRepository, opts?: {
31
+ constructor(storage: MediaStorage, kv: KvCache, clock: Clock, idgen: IdGenerator, siteConfig: SiteConfigRepository, opts?: {
13
32
  readonly allowSvg: boolean;
14
- readonly maxBytes?: number;
15
33
  });
16
34
  execute(request: CreateMediaUploadRequest): Promise<CreateMediaUploadResponse>;
35
+ private assertVariantsCoverPolicy;
36
+ /** Enforce per-mime byte caps from the purpose policy BEFORE the
37
+ * adapter signs presigned PUT URLs. Mantle docs / ADR-0017
38
+ * promise this gate at create time — without it, oversized
39
+ * variants only fail after R2 has accepted the bytes (wasted
40
+ * upload + potential bill).
41
+ *
42
+ * Also gates on positive-integer byteSize. JSON-RPC and HTTP
43
+ * validation only narrow to `typeof === "number"`, so 0, -1, or
44
+ * 0.5 can reach here and produce signed URLs against a maxBytes
45
+ * comparison that accepts them. */
46
+ private assertVariantSizesUnderCap;
47
+ private assertEachVariantAccepted;
48
+ private assertNoSuspiciousSizing;
17
49
  }
18
50
  //# sourceMappingURL=CreateMediaUploadUseCase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CreateMediaUploadUseCase.d.ts","sourceRoot":"","sources":["../../../src/usecase/media/CreateMediaUploadUseCase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EACV,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,uBAAuB,CAAC;AAiB/B,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAJJ,OAAO,EAAE,YAAY,EACrB,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,oBAAoB,EAChC,IAAI,GAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAE9E;IAGG,OAAO,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC;CAkErF"}
1
+ {"version":3,"file":"CreateMediaUploadUseCase.d.ts","sourceRoot":"","sources":["../../../src/usecase/media/CreateMediaUploadUseCase.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,KAAK,EAEV,YAAY,EACb,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EACV,wBAAwB,EACxB,yBAAyB,EAE1B,MAAM,uBAAuB,CAAC;AAqB/B;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;gBALJ,OAAO,EAAE,YAAY,EACrB,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,oBAAoB,EAChC,IAAI,GAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;KAAwB;IAGvE,OAAO,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA0EpF,OAAO,CAAC,yBAAyB;IA2EjC;;;;;;;;;wCASoC;IACpC,OAAO,CAAC,0BAA0B;IA4BlC,OAAO,CAAC,yBAAyB;IAcjC,OAAO,CAAC,wBAAwB;CAmBjC"}
@@ -1,71 +1,214 @@
1
- import { DiagnosticError } from "@aotter/mantle-spec";
2
- import { mediaMimeRejectedDiagnostic, mediaPurposeRejectedDiagnostic, mediaSizeExceededDiagnostic, mediaSvgRejectedDiagnostic, } from "./diagnostics.js";
3
- import { DEFAULT_MAX_BYTES, MEDIA_SVG_MIME, PENDING_UPLOAD_KV_PREFIX, PENDING_UPLOAD_KV_TTL_SECONDS, UPLOAD_URL_TTL_SECONDS, isAllowedMime, } from "./mediaAllowlist.js";
1
+ import { DiagnosticError, expandPolicyRequired, } from "@aotter/mantle-spec";
2
+ import { mediaMimeRejectedDiagnostic, mediaPurposeRejectedDiagnostic, mediaSvgRejectedDiagnostic, mediaVariantSizeExceededDiagnostic, mediaVariantsIncompleteDiagnostic, mediaVariantsSuspiciousSizeDiagnostic, } from "./diagnostics.js";
3
+ import { MEDIA_SVG_MIME, PENDING_UPLOAD_KV_PREFIX, PENDING_UPLOAD_KV_TTL_SECONDS, UPLOAD_URL_TTL_SECONDS, isAllowedMime, } from "./mediaAllowlist.js";
4
+ /**
5
+ * Issue presigned upload capabilities for every variant the agent
6
+ * declares for one logical media asset (#272). The asset's `id` is
7
+ * minted here and surfaces as `uploadGroupId` in the response — the
8
+ * caller passes it back to `commit_media_upload`.
9
+ *
10
+ * Optimization runs agent-side via `@aotter/mantle-media-tools`. This
11
+ * use case enforces policy only — never transforms bytes:
12
+ * - purpose must be declared in `siteDefaults.media.purposes`
13
+ * - variants must cover every required mime for that purpose
14
+ * - each variant's byteSize ≤ `policy.maxBytes[mime]`
15
+ * - mime allowlist (still gates per variant; SVG opt-in via ctor)
16
+ * - "suspicious shape" heuristic: modern formats (avif / webp) MUST
17
+ * NOT be larger than the fallback (jpeg) — uploader skipped
18
+ * optimization → hard fail rather than emit a `<picture>` slower
19
+ * than the bare `<img>`.
20
+ */
4
21
  export class CreateMediaUploadUseCase {
5
22
  storage;
6
23
  kv;
7
24
  clock;
25
+ idgen;
8
26
  siteConfig;
9
27
  opts;
10
- constructor(storage, kv, clock, siteConfig, opts = {
11
- allowSvg: false,
12
- }) {
28
+ constructor(storage, kv, clock, idgen, siteConfig, opts = { allowSvg: false }) {
13
29
  this.storage = storage;
14
30
  this.kv = kv;
15
31
  this.clock = clock;
32
+ this.idgen = idgen;
16
33
  this.siteConfig = siteConfig;
17
34
  this.opts = opts;
18
35
  }
19
36
  async execute(request) {
20
37
  const opPath = "usecase/CreateMediaUpload";
21
- const maxBytes = this.opts.maxBytes ?? DEFAULT_MAX_BYTES;
22
- // Fail-closed purpose enforcement (#262). Empty `media.purposes` —
23
- // either undeclared or operator-cleared — disables uploads entirely
24
- // at this layer; the MCP tool catalog + admin endpoint also gate on
25
- // the same condition for cleaner UX. Reading per request rather
26
- // than snapshotting at boot lets operator edits via the admin
27
- // Settings page take effect without a redeploy.
28
38
  const declared = await this.siteConfig.readMediaPurposes();
29
- if (declared.length === 0 || !request.purpose || !declared.includes(request.purpose)) {
30
- throw new DiagnosticError(mediaPurposeRejectedDiagnostic(opPath, request.purpose, declared));
31
- }
32
- if (request.mimeType === MEDIA_SVG_MIME && !this.opts.allowSvg) {
33
- throw new DiagnosticError(mediaSvgRejectedDiagnostic(opPath));
34
- }
35
- if (!isAllowedMime(request.mimeType, this.opts.allowSvg)) {
36
- throw new DiagnosticError(mediaMimeRejectedDiagnostic(opPath, request.mimeType));
37
- }
38
- // Enforce the ceiling before minting a presigned URL — see
39
- // CreateMediaUploadRequest.byteSize for the WHY.
40
- if (request.byteSize > maxBytes) {
41
- throw new DiagnosticError(mediaSizeExceededDiagnostic(opPath, request.byteSize, maxBytes));
39
+ const declaredNames = declared.map((p) => p.name);
40
+ const policy = declared.find((p) => p.name === request.purpose);
41
+ if (!policy) {
42
+ throw new DiagnosticError(mediaPurposeRejectedDiagnostic(opPath, request.purpose, declaredNames));
42
43
  }
44
+ this.assertVariantsCoverPolicy(opPath, request, policy);
45
+ this.assertEachVariantAccepted(opPath, request);
46
+ this.assertVariantSizesUnderCap(opPath, request, policy);
47
+ this.assertNoSuspiciousSizing(opPath, request);
43
48
  const now = this.clock.now();
44
49
  const expiresAt = now + UPLOAD_URL_TTL_SECONDS * 1000;
45
- const capability = await this.storage.createUpload({
46
- filename: request.filename,
47
- mimeType: request.mimeType,
48
- byteSize: request.byteSize,
49
- maxBytes,
50
+ const uploadGroupId = this.idgen.next();
51
+ const variantSpecs = request.variants.map((v) => ({
52
+ mimeType: v.mimeType,
53
+ byteSize: v.byteSize,
54
+ maxBytes: policy.maxBytes[v.mimeType] ?? Number.MAX_SAFE_INTEGER,
55
+ role: v.role,
56
+ }));
57
+ const result = await this.storage.createUpload({
58
+ uploadGroupId,
50
59
  purpose: request.purpose,
60
+ filename: request.filename,
61
+ variants: variantSpecs,
51
62
  now,
52
63
  expiresAt,
53
64
  });
65
+ const pendingVariants = result.capabilities.map((cap, i) => ({
66
+ mimeType: cap.mimeType,
67
+ role: cap.role,
68
+ storageKey: cap.storageKey,
69
+ expectedSize: variantSpecs[i].byteSize,
70
+ maxBytes: variantSpecs[i].maxBytes,
71
+ }));
54
72
  const record = {
55
- storageKey: capability.storageKey,
56
- expectedMimeType: request.mimeType,
57
- expectedSize: request.byteSize,
58
- expiresAt: capability.expiresAt,
73
+ purpose: request.purpose,
74
+ filename: request.filename,
75
+ variants: pendingVariants,
76
+ alt: request.alt,
77
+ caption: request.caption,
78
+ expiresAt: result.expiresAt,
59
79
  createdAt: now,
60
80
  };
61
- await this.kv.put(`${PENDING_UPLOAD_KV_PREFIX}${capability.uploadId}`, JSON.stringify(record), { expirationTtl: PENDING_UPLOAD_KV_TTL_SECONDS });
81
+ await this.kv.put(`${PENDING_UPLOAD_KV_PREFIX}${result.uploadGroupId}`, JSON.stringify(record), { expirationTtl: PENDING_UPLOAD_KV_TTL_SECONDS });
62
82
  return {
63
- uploadId: capability.uploadId,
64
- uploadUrl: capability.uploadUrl,
65
- method: capability.method,
66
- requiredHeaders: capability.requiredHeaders,
67
- expiresAt: capability.expiresAt,
83
+ uploadGroupId: result.uploadGroupId,
84
+ capabilities: result.capabilities.map((cap) => ({
85
+ mimeType: cap.mimeType,
86
+ role: cap.role,
87
+ method: cap.method,
88
+ uploadUrl: cap.uploadUrl,
89
+ requiredHeaders: cap.requiredHeaders,
90
+ })),
91
+ expiresAt: result.expiresAt,
68
92
  };
69
93
  }
94
+ assertVariantsCoverPolicy(opPath, request, policy) {
95
+ // Expand the `<input accept>`-style grammar (#282) into per-slot
96
+ // acceptable mime sets. The policy validator already rejected
97
+ // overlapping mime sets across slots + empty slots, so variant→slot
98
+ // mapping here is unambiguous: each supplied mime appears in
99
+ // exactly one slot. Role (primary vs alternate) stays free per
100
+ // variant — the agent picks which mime is primary, constrained
101
+ // only by the exactly-one-primary rule below.
102
+ const slots = expandPolicyRequired(policy.required);
103
+ const expandedSet = new Set();
104
+ const mimeToSlot = new Map();
105
+ slots.forEach((mimes, slotIdx) => {
106
+ for (const m of mimes) {
107
+ expandedSet.add(m);
108
+ mimeToSlot.set(m, slotIdx);
109
+ }
110
+ });
111
+ const supplied = request.variants.map((v) => v.mimeType);
112
+ // Closed set: any supplied mime outside the expanded acceptable
113
+ // set has no maxBytes coverage and no slot assignment. Reject.
114
+ const extras = request.variants.filter((v) => !expandedSet.has(v.mimeType));
115
+ if (extras.length > 0) {
116
+ throw new DiagnosticError(mediaVariantsIncompleteDiagnostic(opPath, policy.name, policy.required, supplied));
117
+ }
118
+ // Exactly one variant per slot — covers "missing slot" and
119
+ // "two variants for the same slot" (e.g. an extra `image/jpeg`
120
+ // when slot 0 = `image/jpg,image/png`: both jpg and png supplied
121
+ // would fill slot 0 twice and leave the png slot unfilled if you
122
+ // count by mime-set).
123
+ const filledSlots = new Set();
124
+ for (const v of request.variants) {
125
+ const slotIdx = mimeToSlot.get(v.mimeType); // safe: extras rejected above
126
+ if (filledSlots.has(slotIdx)) {
127
+ throw new DiagnosticError(mediaVariantsIncompleteDiagnostic(opPath, policy.name, policy.required, supplied));
128
+ }
129
+ filledSlots.add(slotIdx);
130
+ }
131
+ if (filledSlots.size !== slots.length) {
132
+ throw new DiagnosticError(mediaVariantsIncompleteDiagnostic(opPath, policy.name, policy.required, supplied));
133
+ }
134
+ // Exactly one primary. Storage key layout is `<group>/<role>.<ext>`,
135
+ // so two primaries collide on the same R2 key + ambiguate which
136
+ // variant `<img>` falls back to. Two alternates with different
137
+ // mimes don't collide (different ext); two alternates with the
138
+ // same mime are impossible here because the mime-to-slot mapping
139
+ // would have caught the duplicate above.
140
+ const primaries = request.variants.filter((v) => v.role === "primary");
141
+ if (primaries.length !== 1) {
142
+ throw new DiagnosticError(mediaVariantsIncompleteDiagnostic(opPath, policy.name, policy.required, supplied));
143
+ }
144
+ }
145
+ /** Enforce per-mime byte caps from the purpose policy BEFORE the
146
+ * adapter signs presigned PUT URLs. Mantle docs / ADR-0017
147
+ * promise this gate at create time — without it, oversized
148
+ * variants only fail after R2 has accepted the bytes (wasted
149
+ * upload + potential bill).
150
+ *
151
+ * Also gates on positive-integer byteSize. JSON-RPC and HTTP
152
+ * validation only narrow to `typeof === "number"`, so 0, -1, or
153
+ * 0.5 can reach here and produce signed URLs against a maxBytes
154
+ * comparison that accepts them. */
155
+ assertVariantSizesUnderCap(opPath, request, policy) {
156
+ for (const v of request.variants) {
157
+ if (!Number.isSafeInteger(v.byteSize) || v.byteSize <= 0) {
158
+ throw new DiagnosticError(mediaVariantSizeExceededDiagnostic(opPath, v.mimeType, v.byteSize, 0));
159
+ }
160
+ const cap = policy.maxBytes[v.mimeType];
161
+ // mime outside policy.required was already rejected in
162
+ // assertVariantsCoverPolicy; defensive check so a future
163
+ // refactor doesn't silently uncap.
164
+ if (cap === undefined) {
165
+ throw new DiagnosticError(mediaVariantSizeExceededDiagnostic(opPath, v.mimeType, v.byteSize, 0));
166
+ }
167
+ if (v.byteSize > cap) {
168
+ throw new DiagnosticError(mediaVariantSizeExceededDiagnostic(opPath, v.mimeType, v.byteSize, cap));
169
+ }
170
+ }
171
+ }
172
+ assertEachVariantAccepted(opPath, request) {
173
+ for (const v of request.variants) {
174
+ if (v.mimeType === MEDIA_SVG_MIME && !this.opts.allowSvg) {
175
+ throw new DiagnosticError(mediaSvgRejectedDiagnostic(opPath));
176
+ }
177
+ if (!isAllowedMime(v.mimeType, this.opts.allowSvg)) {
178
+ throw new DiagnosticError(mediaMimeRejectedDiagnostic(opPath, v.mimeType));
179
+ }
180
+ }
181
+ }
182
+ assertNoSuspiciousSizing(opPath, request) {
183
+ const fallback = pickFallbackForSizing(request.variants);
184
+ if (!fallback)
185
+ return;
186
+ for (const v of request.variants) {
187
+ if (v === fallback)
188
+ continue;
189
+ if (!MODERN_FORMATS.has(v.mimeType))
190
+ continue;
191
+ if (v.byteSize > fallback.byteSize) {
192
+ throw new DiagnosticError(mediaVariantsSuspiciousSizeDiagnostic(opPath, `${v.mimeType} (${v.byteSize}B) > ${fallback.mimeType} (${fallback.byteSize}B)`));
193
+ }
194
+ }
195
+ }
196
+ }
197
+ /** Modern formats that, by convention, should be smaller than the
198
+ * universal fallback (jpeg / png). Used by the "suspicious sizing"
199
+ * heuristic — if any of these weigh more than the fallback, the
200
+ * uploader almost certainly skipped optimization. */
201
+ const MODERN_FORMATS = new Set(["image/avif", "image/webp"]);
202
+ /** Pick the variant to compare modern formats against — preferred
203
+ * order: jpeg → png → gif. Returns undefined when none of the
204
+ * classic-format variants are present (e.g. an avif-only upload),
205
+ * in which case the heuristic skips. */
206
+ function pickFallbackForSizing(variants) {
207
+ for (const mime of ["image/jpeg", "image/png", "image/gif"]) {
208
+ const found = variants.find((v) => v.mimeType === mime);
209
+ if (found)
210
+ return found;
211
+ }
212
+ return undefined;
70
213
  }
71
214
  //# sourceMappingURL=CreateMediaUploadUseCase.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CreateMediaUploadUseCase.js","sourceRoot":"","sources":["../../../src/usecase/media/CreateMediaUploadUseCase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CreateMediaUploadUseCase.js","sourceRoot":"","sources":["../../../src/usecase/media/CreateMediaUploadUseCase.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,36 @@
1
+ import type { MediaVariantRole } from "../../domain/port/MediaStorage.js";
1
2
  /** KV record persisted by `CreateMediaUploadUseCase` and read by
2
3
  * `CommitMediaUploadUseCase`. Stored under
3
- * `${PENDING_UPLOAD_KV_PREFIX}${uploadId}` with a TTL set to
4
- * `PENDING_UPLOAD_KV_TTL_SECONDS`. */
4
+ * `${PENDING_UPLOAD_KV_PREFIX}${uploadGroupId}` with a TTL set to
5
+ * `PENDING_UPLOAD_KV_TTL_SECONDS`.
6
+ *
7
+ * Multi-variant from the start (#272): one record tracks every
8
+ * variant the agent declared, so commit can HEAD all N R2 objects
9
+ * in one atomic shot.
10
+ */
5
11
  export interface PendingUploadRecord {
6
- readonly storageKey: string;
7
- readonly expectedMimeType: string;
8
- /** Caller-declared byte size, persisted at create time so commit can
9
- * enforce a tight per-upload ceiling instead of just the adapter-
10
- * wide `maxBytes`. Adapter ultimately verifies actual stored bytes
11
- * ≤ commit-supplied maxBytes — `CommitMediaUploadUseCase` passes
12
- * `min(adapterCap, record.expectedSize)`. */
13
- readonly expectedSize: number;
12
+ readonly purpose: string;
13
+ /** Original filename — forwarded by commit into R2 customMetadata
14
+ * on every variant. Preserved here so the commit path sees it
15
+ * without a second hop. */
16
+ readonly filename: string;
17
+ readonly variants: ReadonlyArray<PendingUploadVariant>;
18
+ readonly alt?: string;
19
+ readonly caption?: string;
14
20
  readonly expiresAt: number;
15
21
  readonly createdAt: number;
16
22
  }
23
+ export interface PendingUploadVariant {
24
+ readonly mimeType: string;
25
+ readonly role: MediaVariantRole;
26
+ readonly storageKey: string;
27
+ /** Caller-declared byte size, persisted at create time so commit
28
+ * can enforce a tight per-variant ceiling rather than just the
29
+ * per-purpose `maxBytes`. The adapter ultimately verifies actual
30
+ * stored bytes ≤ this. */
31
+ readonly expectedSize: number;
32
+ /** Per-purpose `maxBytes[mimeType]`. Forwarded to the adapter so
33
+ * HEAD-verify rejects oversized PUTs with the right cap message. */
34
+ readonly maxBytes: number;
35
+ }
17
36
  //# sourceMappingURL=PendingUploadRecord.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PendingUploadRecord.d.ts","sourceRoot":"","sources":["../../../src/usecase/media/PendingUploadRecord.ts"],"names":[],"mappings":"AAAA;;;uCAGuC;AACvC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;kDAI8C;IAC9C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
1
+ {"version":3,"file":"PendingUploadRecord.d.ts","sourceRoot":"","sources":["../../../src/usecase/media/PendingUploadRecord.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;gCAE4B;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IACvD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;+BAG2B;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;yEACqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B"}
@@ -0,0 +1,35 @@
1
+ import type { Clock } from "../../domain/port/Clock.js";
2
+ import type { KvCache } from "../../domain/port/KvCache.js";
3
+ import type { MediaStorage } from "../../domain/port/MediaStorage.js";
4
+ import type { UploadMediaVariantRequest } from "../dto/media/index.js";
5
+ /**
6
+ * Server-side byte write for one variant of a pending upload — the
7
+ * sandboxed-agent alternative to the presigned-PUT path produced by
8
+ * `createUpload`. Used by the `upload_media_variant` MCP tool when
9
+ * the caller can't reach the storage backend directly (#283 — Claude
10
+ * Cowork's outbound proxy doesn't allowlist R2 hosts; the MCP
11
+ * transport gets traffic to the Worker via api.anthropic.com).
12
+ *
13
+ * Validates the request against the existing `PendingUploadRecord`
14
+ * (must match purpose + role + mimeType + size cap) then asks the
15
+ * storage adapter to write the bytes. The pending record is NOT
16
+ * deleted here — commit still HEAD-verifies the object via the same
17
+ * code path as the presigned-PUT case. All-or-nothing across the
18
+ * variant bundle still applies at commit time; this use case just
19
+ * lands one variant's bytes.
20
+ *
21
+ * Same purpose policies as the presigned path: per-mime `maxBytes`,
22
+ * per-purpose declared variant set, all already validated by
23
+ * `CreateMediaUploadUseCase` when the agent first opened the group.
24
+ */
25
+ export declare class UploadMediaVariantUseCase {
26
+ private readonly storage;
27
+ private readonly kv;
28
+ private readonly clock;
29
+ constructor(storage: MediaStorage, kv: KvCache, clock: Clock);
30
+ execute(request: UploadMediaVariantRequest): Promise<{
31
+ storageKey: string;
32
+ byteSize: number;
33
+ }>;
34
+ }
35
+ //# sourceMappingURL=UploadMediaVariantUseCase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UploadMediaVariantUseCase.d.ts","sourceRoot":"","sources":["../../../src/usecase/media/UploadMediaVariantUseCase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,KAAK,EACV,YAAY,EAEb,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAYvE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,yBAAyB;IAElC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAFL,OAAO,EAAE,YAAY,EACrB,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,KAAK;IAGzB,OAAO,CACX,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAyDrD"}
@@ -0,0 +1,75 @@
1
+ import { DiagnosticError } from "@aotter/mantle-spec";
2
+ import { mediaUploadExpiredDiagnostic, mediaVariantSizeExceededDiagnostic, mediaMimeRejectedDiagnostic, } from "./diagnostics.js";
3
+ import { PENDING_UPLOAD_KV_PREFIX } from "./mediaAllowlist.js";
4
+ /**
5
+ * Server-side byte write for one variant of a pending upload — the
6
+ * sandboxed-agent alternative to the presigned-PUT path produced by
7
+ * `createUpload`. Used by the `upload_media_variant` MCP tool when
8
+ * the caller can't reach the storage backend directly (#283 — Claude
9
+ * Cowork's outbound proxy doesn't allowlist R2 hosts; the MCP
10
+ * transport gets traffic to the Worker via api.anthropic.com).
11
+ *
12
+ * Validates the request against the existing `PendingUploadRecord`
13
+ * (must match purpose + role + mimeType + size cap) then asks the
14
+ * storage adapter to write the bytes. The pending record is NOT
15
+ * deleted here — commit still HEAD-verifies the object via the same
16
+ * code path as the presigned-PUT case. All-or-nothing across the
17
+ * variant bundle still applies at commit time; this use case just
18
+ * lands one variant's bytes.
19
+ *
20
+ * Same purpose policies as the presigned path: per-mime `maxBytes`,
21
+ * per-purpose declared variant set, all already validated by
22
+ * `CreateMediaUploadUseCase` when the agent first opened the group.
23
+ */
24
+ export class UploadMediaVariantUseCase {
25
+ storage;
26
+ kv;
27
+ clock;
28
+ constructor(storage, kv, clock) {
29
+ this.storage = storage;
30
+ this.kv = kv;
31
+ this.clock = clock;
32
+ }
33
+ async execute(request) {
34
+ const opPath = "usecase/UploadMediaVariant";
35
+ const kvKey = `${PENDING_UPLOAD_KV_PREFIX}${request.uploadGroupId}`;
36
+ const raw = await this.kv.get(kvKey);
37
+ if (!raw) {
38
+ throw new DiagnosticError(mediaUploadExpiredDiagnostic(opPath, request.uploadGroupId));
39
+ }
40
+ const record = JSON.parse(raw);
41
+ // Wall-clock expiry check. KV TTL covers the eventual-deletion
42
+ // case, but during the window between `expiresAt` and the TTL
43
+ // firing the record is still readable — reject explicitly so the
44
+ // agent can't ship variant bytes against a stale capability.
45
+ if (this.clock.now() >= record.expiresAt) {
46
+ throw new DiagnosticError(mediaUploadExpiredDiagnostic(opPath, request.uploadGroupId));
47
+ }
48
+ // Find the matching declared variant. The pending record's
49
+ // variants[] is the source of truth for "what role + mime are
50
+ // valid in this group"; an agent uploading something not in the
51
+ // declared set is rejected the same way create-then-PUT would
52
+ // have rejected an unknown storageKey.
53
+ const declared = findMatchingVariant(record.variants, request.role, request.mimeType);
54
+ if (!declared) {
55
+ throw new DiagnosticError(mediaMimeRejectedDiagnostic(opPath, request.mimeType));
56
+ }
57
+ const byteSize = request.bytes.byteLength;
58
+ if (byteSize > declared.maxBytes) {
59
+ throw new DiagnosticError(mediaVariantSizeExceededDiagnostic(opPath, request.mimeType, byteSize, declared.maxBytes));
60
+ }
61
+ const result = await this.storage.putVariantBytes({
62
+ uploadGroupId: request.uploadGroupId,
63
+ purpose: record.purpose,
64
+ role: request.role,
65
+ mimeType: request.mimeType,
66
+ filename: record.filename,
67
+ bytes: request.bytes,
68
+ });
69
+ return { storageKey: result.storageKey, byteSize };
70
+ }
71
+ }
72
+ function findMatchingVariant(variants, role, mimeType) {
73
+ return variants.find((v) => v.role === role && v.mimeType === mimeType);
74
+ }
75
+ //# sourceMappingURL=UploadMediaVariantUseCase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UploadMediaVariantUseCase.js","sourceRoot":"","sources":["../../../src/usecase/media/UploadMediaVariantUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAQtD,OAAO,EACL,4BAA4B,EAC5B,kCAAkC,EAClC,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAM/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,yBAAyB;IAEjB;IACA;IACA;IAHnB,YACmB,OAAqB,EACrB,EAAW,EACX,KAAY;QAFZ,YAAO,GAAP,OAAO,CAAc;QACrB,OAAE,GAAF,EAAE,CAAS;QACX,UAAK,GAAL,KAAK,CAAO;IAC5B,CAAC;IAEJ,KAAK,CAAC,OAAO,CACX,OAAkC;QAElC,MAAM,MAAM,GAAG,4BAA4B,CAAC;QAE5C,MAAM,KAAK,GAAG,GAAG,wBAAwB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;QACpE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,eAAe,CACvB,4BAA4B,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAC5D,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwB,CAAC;QAEtD,+DAA+D;QAC/D,8DAA8D;QAC9D,iEAAiE;QACjE,6DAA6D;QAC7D,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACzC,MAAM,IAAI,eAAe,CACvB,4BAA4B,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAC5D,CAAC;QACJ,CAAC;QAED,2DAA2D;QAC3D,8DAA8D;QAC9D,gEAAgE;QAChE,8DAA8D;QAC9D,uCAAuC;QACvC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACtF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,eAAe,CACvB,2BAA2B,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CACtD,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;QAC1C,IAAI,QAAQ,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACjC,MAAM,IAAI,eAAe,CACvB,kCAAkC,CAChC,MAAM,EACN,OAAO,CAAC,QAAQ,EAChB,QAAQ,EACR,QAAQ,CAAC,QAAQ,CAClB,CACF,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;YAChD,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,KAAK,EAAE,OAAO,CAAC,KAAK;SACrB,CAAC,CAAC;QAEH,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC;IACrD,CAAC;CACF;AAED,SAAS,mBAAmB,CAC1B,QAA6C,EAC7C,IAAsB,EACtB,QAAgB;IAEhB,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;AAC1E,CAAC"}
@@ -3,8 +3,8 @@ export declare function mediaNotConfiguredDiagnostic(opPath: string): Diagnostic
3
3
  export declare function mediaMimeRejectedDiagnostic(opPath: string, mime: string): Diagnostic;
4
4
  export declare function mediaSvgRejectedDiagnostic(opPath: string): Diagnostic;
5
5
  export declare function mediaSizeExceededDiagnostic(opPath: string, byteSize: number, maxBytes: number): Diagnostic;
6
- export declare function mediaUploadExpiredDiagnostic(opPath: string, uploadId: string): Diagnostic;
7
- export declare function mediaObjectNotFoundDiagnostic(opPath: string, uploadId: string): Diagnostic;
6
+ export declare function mediaUploadExpiredDiagnostic(opPath: string, uploadGroupId: string): Diagnostic;
7
+ export declare function mediaObjectNotFoundDiagnostic(opPath: string, uploadGroupId: string): Diagnostic;
8
8
  /** Caller supplied a `purpose` that this deployment did not declare in
9
9
  * `siteDefaults.media.purposes`. Fail-closed per #262: alpha has no
10
10
  * production consumers, so there is no warn-and-allow compatibility
@@ -12,4 +12,21 @@ export declare function mediaObjectNotFoundDiagnostic(opPath: string, uploadId:
12
12
  * field carries the declared set so agents can self-correct without
13
13
  * another round trip. */
14
14
  export declare function mediaPurposeRejectedDiagnostic(opPath: string, purpose: string | undefined, declared: readonly string[]): Diagnostic;
15
+ /** Caller's variants manifest is missing one or more mimes the
16
+ * purpose policy declares as required. The expected field carries
17
+ * the declared required set + the supplied set so the agent script
18
+ * can self-correct without another round trip. */
19
+ export declare function mediaVariantsIncompleteDiagnostic(opPath: string, purpose: string, required: readonly string[], supplied: readonly string[]): Diagnostic;
20
+ export declare function mediaVariantSizeExceededDiagnostic(opPath: string, mime: string, byteSize: number, maxBytes: number): Diagnostic;
21
+ /**
22
+ * Variant byte sizes don't match the format hierarchy the uploader
23
+ * promised — typically avif > webp > jpeg (each modern format
24
+ * smaller). When the upload manifest claims a modern format that's
25
+ * larger than the universal-fallback (e.g. `avif` weighing more than
26
+ * `jpeg`), the uploader almost certainly skipped optimization for
27
+ * that variant. Fail hard rather than emit a `<picture>` that's
28
+ * slower than the bare `<img>` fallback.
29
+ */
30
+ export declare function mediaVariantsSuspiciousSizeDiagnostic(opPath: string, detail: string): Diagnostic;
31
+ export declare function mediaAssetNotFoundDiagnostic(opPath: string, id: string): Diagnostic;
15
32
  //# sourceMappingURL=diagnostics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/usecase/media/diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItE,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAUvE;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CASpF;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAUrE;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,UAAU,CASZ;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAUzF;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAU1F;AAED;;;;;0BAK0B;AAC1B,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,UAAU,CAiBZ"}
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/usecase/media/diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItE,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAUvE;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CASpF;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAUrE;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,UAAU,CASZ;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAU9F;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAU/F;AAED;;;;;0BAK0B;AAC1B,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,UAAU,CAiBZ;AAED;;;mDAGmD;AACnD,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,UAAU,CAaZ;AAED,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,UAAU,CAWZ;AAED;;;;;;;;GAQG;AACH,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,UAAU,CAYZ;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAWnF"}