@fungi.computer/darkroom 0.3.1 → 0.4.0

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.
package/dist/server.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { mediaPendingSchema } from "./media-binding.js";
2
+ export { estimateMediaModel } from "./cloudflare/provider.js";
2
3
  export { mediaPendingSchema, mediaBindingCallSchema, mediaBindingGenerationSchema, mediaImageBindingInputSchema, mediaVideoBindingInputSchema, parseMediaBindingCall, mediaBindingRequest, readMediaBindingReply, } from "./media-binding.js";
3
4
  import { Hono } from "hono";
4
5
  import { z } from "zod";
6
+ import { AppError, describeAppError } from "@fungi.computer/agar/errors";
5
7
  import * as Effect from "effect/Effect";
6
8
  import * as Result from "effect/Result";
7
9
  import { runMediaGeneration } from "./media-generation.js";
@@ -47,7 +49,10 @@ const headers = {
47
49
  function failure(error, refusalMessages) {
48
50
  if (error instanceof MediaSafetyFilterRefusal)
49
51
  return Response.json({
50
- error: "The model's safety filter refused this prompt — try rewording.",
52
+ error: describeAppError(new AppError(error.message, {
53
+ code: "SAFETY_FILTER_REFUSED",
54
+ retryable: false,
55
+ })).message,
51
56
  stage: "provider",
52
57
  code: "SAFETY_FILTER_REFUSED",
53
58
  retryable: false,
@@ -56,7 +61,7 @@ function failure(error, refusalMessages) {
56
61
  if (error instanceof MediaExecutionError &&
57
62
  error.cause instanceof MediaOutputFormatMismatch)
58
63
  return Response.json({
59
- error: "Media provider did not return the requested image format; nothing was saved or charged. Start a new request.",
64
+ error: "Media provider did not return the requested image format; nothing was saved. Start a new request.",
60
65
  stage: error.stage,
61
66
  code: "HOST_FAILURE",
62
67
  retryable: false,
@@ -90,13 +95,27 @@ function failure(error, refusalMessages) {
90
95
  status: 400,
91
96
  headers,
92
97
  });
98
+ if (error.stage === "provider") {
99
+ const code = error.customerCharge === "none"
100
+ ? "MEDIA_PROVIDER_FAILURE_UNCHARGED"
101
+ : "MEDIA_PROVIDER_FAILURE";
102
+ return Response.json({
103
+ error: describeAppError(new AppError(error.message, {
104
+ code,
105
+ retryable: false,
106
+ })).message,
107
+ stage: error.stage,
108
+ code,
109
+ retryable: false,
110
+ provider: error.provider,
111
+ }, { status: 503, headers });
112
+ }
93
113
  const messages = {
94
114
  "generation-authorization": "Media generation permission could not be checked; try again.",
95
115
  "destination-authorization": "Media destination permission could not be checked; try again.",
96
116
  "destination-write": "Media generated an image but could not save it; retry this request to save the retained result.",
97
117
  estimate: "Media could not prepare the image inputs or estimate their cost; check the reference files and retry.",
98
118
  billing: "Media billing is unavailable; retry this request.",
99
- provider: "Media provider failed; nothing was charged. Start a new request.",
100
119
  normalization: "Media provider returned an unusable image; start a new request only after checking provider status.",
101
120
  artifact: "Media generated output could not be retained or read; try again.",
102
121
  operation: "Media operation is unresolved; try again.",
@@ -105,7 +124,7 @@ function failure(error, refusalMessages) {
105
124
  error: messages[error.stage],
106
125
  stage: error.stage,
107
126
  code: "HOST_FAILURE",
108
- retryable: error.stage !== "normalization" && error.stage !== "provider",
127
+ retryable: error.stage !== "normalization",
109
128
  provider: error instanceof MediaExecutionError ? error.provider : undefined,
110
129
  }, {
111
130
  status: 503,
@@ -10,10 +10,202 @@
10
10
 
11
11
  ## Variables
12
12
 
13
+ ### mediaModelPriceEstimateSchema
14
+
15
+ ```ts
16
+ const mediaModelPriceEstimateSchema: ZodObject<{
17
+ kind: ZodLiteral<"estimate">;
18
+ basis: ZodEnum<{
19
+ provider-cost: "provider-cost";
20
+ team-price: "team-price";
21
+ }>;
22
+ costMilliunits: ZodNumber;
23
+ settings: ZodObject<{
24
+ model: ZodString;
25
+ prompt: ZodString;
26
+ width: ZodNumber;
27
+ height: ZodNumber;
28
+ steps: ZodOptional<ZodNumber>;
29
+ durationSeconds: ZodOptional<ZodNumber>;
30
+ resolution: ZodOptional<ZodEnum<{
31
+ p480: "480p";
32
+ p720: "720p";
33
+ p768: "768p";
34
+ p1080: "1080p";
35
+ p4k: "4k";
36
+ }>>;
37
+ aspectRatio: ZodOptional<ZodEnum<{
38
+ wide: "16:9";
39
+ landscape: "4:3";
40
+ square: "1:1";
41
+ portrait: "3:4";
42
+ tall: "9:16";
43
+ ultrawide: "21:9";
44
+ ultratall: "9:21";
45
+ }>>;
46
+ generateAudio: ZodOptional<ZodBoolean>;
47
+ references: ZodOptional<ZodArray<ZodObject<{
48
+ computerId: ZodString;
49
+ path: ZodString;
50
+ role: ZodOptional<ZodEnum<{
51
+ first: ...;
52
+ last: ...;
53
+ reference: ...;
54
+ }>>;
55
+ }, $strict>>>;
56
+ }, $strict>;
57
+ }, $strict>;
58
+ ```
59
+
60
+ A labelled estimate for one explicitly described generation, never a receipt.
61
+
62
+ ---
63
+
64
+ ### mediaCatalogModelSchema
65
+
66
+ ```ts
67
+ const mediaCatalogModelSchema: ZodReadonly<ZodObject<{
68
+ priceEstimate: ZodOptional<ZodObject<{
69
+ kind: ZodLiteral<"estimate">;
70
+ basis: ZodEnum<{
71
+ provider-cost: "provider-cost";
72
+ team-price: "team-price";
73
+ }>;
74
+ costMilliunits: ZodNumber;
75
+ settings: ZodObject<{
76
+ model: ZodString;
77
+ prompt: ZodString;
78
+ width: ZodNumber;
79
+ height: ZodNumber;
80
+ steps: ZodOptional<ZodNumber>;
81
+ durationSeconds: ZodOptional<ZodNumber>;
82
+ resolution: ZodOptional<ZodEnum<{
83
+ p480: ...;
84
+ p720: ...;
85
+ p768: ...;
86
+ p1080: ...;
87
+ p4k: ...;
88
+ }>>;
89
+ aspectRatio: ZodOptional<ZodEnum<{
90
+ wide: ...;
91
+ landscape: ...;
92
+ square: ...;
93
+ portrait: ...;
94
+ tall: ...;
95
+ ultrawide: ...;
96
+ ultratall: ...;
97
+ }>>;
98
+ generateAudio: ZodOptional<ZodBoolean>;
99
+ references: ZodOptional<ZodArray<ZodObject<..., ...>>>;
100
+ }, $strict>;
101
+ }, $strict>>;
102
+ id: ZodString;
103
+ label: ZodString;
104
+ kind: ZodEnum<{
105
+ image: "image";
106
+ video: "video";
107
+ }>;
108
+ tier: ZodEnum<{
109
+ draft: "draft";
110
+ balanced: "balanced";
111
+ premium: "premium";
112
+ max: "max";
113
+ }>;
114
+ outputFormats: ZodReadonly<ZodArray<ZodEnum<{
115
+ png: "png";
116
+ jpeg: "jpeg";
117
+ webp: "webp";
118
+ }>>>;
119
+ maxPromptLength: ZodNumber;
120
+ input: ZodLazy<ZodReadonly<ZodObject<{
121
+ transport: ZodEnum<{
122
+ json: "json";
123
+ multipart: "multipart";
124
+ }>;
125
+ dimensions: ZodEnum<{
126
+ none: "none";
127
+ own: "own";
128
+ }>;
129
+ maxDimension: ZodNullable<ZodNumber>;
130
+ maxDimensionDocumented: ZodBoolean;
131
+ defaultWidth: ZodNumber;
132
+ defaultHeight: ZodNumber;
133
+ steps: ZodEnum<{
134
+ steps: "steps";
135
+ num_steps: "num_steps";
136
+ fixed: "fixed";
137
+ }>;
138
+ maxSteps: ZodNullable<ZodNumber>;
139
+ defaultSteps: ZodNullable<ZodNumber>;
140
+ referenceField: ZodNullable<ZodString>;
141
+ maxReferences: ZodNumber;
142
+ }, $strict>>>;
143
+ sizes: ZodNullable<ZodReadonly<ZodArray<ZodString>>>;
144
+ video: ZodNullable<ZodLazy<ZodReadonly<ZodObject<{
145
+ minSeconds: ZodNumber;
146
+ maxSeconds: ZodNumber;
147
+ defaultSeconds: ZodNumber;
148
+ durations: ZodReadonly<ZodArray<ZodNumber>>;
149
+ provider: ZodEnum<{
150
+ seedance: "seedance";
151
+ veo: "veo";
152
+ minimax: "minimax";
153
+ }>;
154
+ formats: ZodReadonly<ZodArray<ZodLazy<...>>>;
155
+ defaultResolution: ZodEnum<{
156
+ p480: "480p";
157
+ p720: "720p";
158
+ p768: "768p";
159
+ p1080: "1080p";
160
+ p4k: "4k";
161
+ }>;
162
+ defaultAspectRatio: ZodEnum<{
163
+ wide: "16:9";
164
+ landscape: "4:3";
165
+ square: "1:1";
166
+ portrait: "3:4";
167
+ tall: "9:16";
168
+ ultrawide: "21:9";
169
+ ultratall: "9:21";
170
+ }>;
171
+ supportsAudio: ZodBoolean;
172
+ defaultGenerateAudio: ZodNullable<ZodBoolean>;
173
+ referenceRoles: ZodReadonly<ZodArray<ZodEnum<...>>>;
174
+ maxReferenceImages: ZodNumber;
175
+ referenceResolutions: ZodReadonly<ZodArray<ZodEnum<...>>>;
176
+ prices: ZodReadonly<ZodArray<ZodReadonly<...>>>;
177
+ }, $strict>>>>;
178
+ }, $strict>>;
179
+ ```
180
+
181
+ Host catalog projection; provider metadata remains owned by the sole catalog.
182
+
183
+ ---
184
+
13
185
  ### mediaModelsSchema
14
186
 
15
187
  ```ts
16
188
  const mediaModelsSchema: ZodLazy<ZodArray<ZodReadonly<ZodObject<{
189
+ priceEstimate: ZodOptional<ZodObject<{
190
+ kind: ZodLiteral<"estimate">;
191
+ basis: ZodEnum<{
192
+ provider-cost: "provider-cost";
193
+ team-price: "team-price";
194
+ }>;
195
+ costMilliunits: ZodNumber;
196
+ settings: ZodObject<{
197
+ model: ZodString;
198
+ prompt: ZodString;
199
+ width: ZodNumber;
200
+ height: ZodNumber;
201
+ steps: ZodOptional<...>;
202
+ durationSeconds: ZodOptional<...>;
203
+ resolution: ZodOptional<...>;
204
+ aspectRatio: ZodOptional<...>;
205
+ generateAudio: ZodOptional<...>;
206
+ references: ZodOptional<...>;
207
+ }, $strict>;
208
+ }, $strict>>;
17
209
  id: ZodString;
18
210
  label: ZodString;
19
211
  kind: ZodEnum<{
@@ -42,6 +234,7 @@ const mediaModelsSchema: ZodLazy<ZodArray<ZodReadonly<ZodObject<{
42
234
  own: ...;
43
235
  }>;
44
236
  maxDimension: ZodNullable<ZodNumber>;
237
+ maxDimensionDocumented: ZodBoolean;
45
238
  defaultWidth: ZodNumber;
46
239
  defaultHeight: ZodNumber;
47
240
  steps: ZodEnum<{
@@ -64,6 +257,7 @@ const mediaModelsSchema: ZodLazy<ZodArray<ZodReadonly<ZodObject<{
64
257
  formats: ZodReadonly<...>;
65
258
  defaultResolution: ZodEnum<...>;
66
259
  defaultAspectRatio: ZodEnum<...>;
260
+ supportsAudio: ZodBoolean;
67
261
  defaultGenerateAudio: ZodNullable<...>;
68
262
  referenceRoles: ZodReadonly<...>;
69
263
  maxReferenceImages: ZodNumber;
@@ -248,6 +442,8 @@ const appMediaBrokerFailureSchema: ZodObject<{
248
442
  INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
249
443
  HOST_FAILURE: "HOST_FAILURE";
250
444
  INVALID_ARGUMENT: "INVALID_ARGUMENT";
445
+ MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
446
+ MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
251
447
  SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
252
448
  }>;
253
449
  message: ZodString;
@@ -265,20 +461,7 @@ const appMediaBrokerFailureSchema: ZodObject<{
265
461
  destination-write: "destination-write";
266
462
  }>;
267
463
  retryable: ZodBoolean;
268
- provider: ZodOptional<ZodPipe<ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, ZodTransform<
269
- | Readonly<{
270
- category: | "InferenceUpstreamError"
271
- | "AiInternalError"
272
- | "TypeError"
273
- | "AbortError"
274
- | "TimeoutError"
275
- | "Error";
276
- code: number | null;
277
- }>
278
- | Readonly<{
279
- category: "result_download";
280
- status: number;
281
- }>, unknown>>>;
464
+ provider: ZodOptional<ZodType<MediaProviderDiagnostic, unknown, $ZodTypeInternals<MediaProviderDiagnostic, unknown>>>;
282
465
  }, $strict>;
283
466
  ```
284
467
 
@@ -312,6 +495,8 @@ const appMediaRefusalSchema: ZodUnion<readonly [ZodObject<{
312
495
  INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
313
496
  HOST_FAILURE: "HOST_FAILURE";
314
497
  INVALID_ARGUMENT: "INVALID_ARGUMENT";
498
+ MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
499
+ MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
315
500
  SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
316
501
  }>;
317
502
  message: ZodString;
@@ -329,15 +514,7 @@ const appMediaRefusalSchema: ZodUnion<readonly [ZodObject<{
329
514
  destination-write: "destination-write";
330
515
  }>;
331
516
  retryable: ZodBoolean;
332
- provider: ZodOptional<ZodPipe<ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, ZodTransform<
333
- | Readonly<{
334
- category: ... | ... | ... | ... | ... | ...;
335
- code: ... | ...;
336
- }>
337
- | Readonly<{
338
- category: "result_download";
339
- status: number;
340
- }>, unknown>>>;
517
+ provider: ZodOptional<ZodType<MediaProviderDiagnostic, unknown, $ZodTypeInternals<MediaProviderDiagnostic, unknown>>>;
341
518
  }, $strict>]>;
342
519
  ```
343
520
 
@@ -350,6 +527,12 @@ errors.
350
527
 
351
528
  ```ts
352
529
  const appMediaModelsOutcomeSchema: ZodUnion<readonly [ZodLazy<ZodArray<ZodReadonly<ZodObject<{
530
+ priceEstimate: ZodOptional<ZodObject<{
531
+ kind: ZodLiteral<...>;
532
+ basis: ZodEnum<...>;
533
+ costMilliunits: ZodNumber;
534
+ settings: ZodObject<..., ...>;
535
+ }, $strict>>;
353
536
  id: ZodString;
354
537
  label: ZodString;
355
538
  kind: ZodEnum<{
@@ -372,6 +555,7 @@ const appMediaModelsOutcomeSchema: ZodUnion<readonly [ZodLazy<ZodArray<ZodReadon
372
555
  transport: ...;
373
556
  dimensions: ...;
374
557
  maxDimension: ...;
558
+ maxDimensionDocumented: ...;
375
559
  defaultWidth: ...;
376
560
  defaultHeight: ...;
377
561
  steps: ...;
@@ -404,6 +588,8 @@ const appMediaModelsOutcomeSchema: ZodUnion<readonly [ZodLazy<ZodArray<ZodReadon
404
588
  INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
405
589
  HOST_FAILURE: "HOST_FAILURE";
406
590
  INVALID_ARGUMENT: "INVALID_ARGUMENT";
591
+ MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
592
+ MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
407
593
  SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
408
594
  }>;
409
595
  message: ZodString;
@@ -421,7 +607,7 @@ const appMediaModelsOutcomeSchema: ZodUnion<readonly [ZodLazy<ZodArray<ZodReadon
421
607
  destination-write: "destination-write";
422
608
  }>;
423
609
  retryable: ZodBoolean;
424
- provider: ZodOptional<ZodPipe<ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, ZodTransform<Readonly<...> | Readonly<...>, unknown>>>;
610
+ provider: ZodOptional<ZodType<MediaProviderDiagnostic, unknown, $ZodTypeInternals<MediaProviderDiagnostic, unknown>>>;
425
611
  }, $strict>]>]>;
426
612
  ```
427
613
 
@@ -474,6 +660,8 @@ const appMediaGenerationOutcomeSchema: ZodUnion<readonly [ZodObject<{
474
660
  INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
475
661
  HOST_FAILURE: "HOST_FAILURE";
476
662
  INVALID_ARGUMENT: "INVALID_ARGUMENT";
663
+ MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
664
+ MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
477
665
  SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
478
666
  }>;
479
667
  message: ZodString;
@@ -491,7 +679,7 @@ const appMediaGenerationOutcomeSchema: ZodUnion<readonly [ZodObject<{
491
679
  destination-write: "destination-write";
492
680
  }>;
493
681
  retryable: ZodBoolean;
494
- provider: ZodOptional<ZodPipe<ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, ZodTransform<Readonly<...> | Readonly<...>, unknown>>>;
682
+ provider: ZodOptional<ZodType<MediaProviderDiagnostic, unknown, $ZodTypeInternals<MediaProviderDiagnostic, unknown>>>;
495
683
  }, $strict>]>]>;
496
684
  ```
497
685
 
@@ -65,23 +65,12 @@ const mediaFailureSchema: ZodObject<{
65
65
  INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
66
66
  HOST_FAILURE: "HOST_FAILURE";
67
67
  INVALID_ARGUMENT: "INVALID_ARGUMENT";
68
+ MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
69
+ MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
68
70
  SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
69
71
  }>;
70
72
  retryable: ZodBoolean;
71
- provider: ZodOptional<ZodPipe<ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, ZodTransform<
72
- | Readonly<{
73
- category: | "InferenceUpstreamError"
74
- | "AiInternalError"
75
- | "TypeError"
76
- | "AbortError"
77
- | "TimeoutError"
78
- | "Error";
79
- code: number | null;
80
- }>
81
- | Readonly<{
82
- category: "result_download";
83
- status: number;
84
- }>, unknown>>>;
73
+ provider: ZodOptional<ZodType<MediaProviderDiagnostic, unknown, $ZodTypeInternals<MediaProviderDiagnostic, unknown>>>;
85
74
  }, $strict>;
86
75
  ```
87
76
 
@@ -115,23 +104,11 @@ function readMediaFailure(
115
104
  | "INSUFFICIENT_CREDIT"
116
105
  | "HOST_FAILURE"
117
106
  | "INVALID_ARGUMENT"
107
+ | "MEDIA_PROVIDER_FAILURE"
108
+ | "MEDIA_PROVIDER_FAILURE_UNCHARGED"
118
109
  | "SAFETY_FILTER_REFUSED";
119
110
  retryable: boolean;
120
- provider?:
121
- | Readonly<{
122
- category:
123
- | "InferenceUpstreamError"
124
- | "AiInternalError"
125
- | "TypeError"
126
- | "AbortError"
127
- | "TimeoutError"
128
- | "Error";
129
- code: number | null;
130
- }>
131
- | Readonly<{
132
- category: "result_download";
133
- status: number;
134
- }>;
111
+ provider?: MediaProviderDiagnostic;
135
112
  } | null>;
136
113
  ```
137
114
 
@@ -151,11 +128,9 @@ Read and decode one Media host failure at the browser HTTP boundary.
151
128
  \| `"operation"` \| `"normalization"` \| `"artifact"` \|
152
129
  `"destination-authorization"` \| `"destination-write"`; `code`: \|
153
130
  `"CAPABILITY_DENIED"` \| `"INSUFFICIENT_CREDIT"` \| `"HOST_FAILURE"` \|
154
- `"INVALID_ARGUMENT"` \| `"SAFETY_FILTER_REFUSED"`; `retryable`: `boolean`;
155
- `provider?`: \| `Readonly`\<\{ `category`: \| `"InferenceUpstreamError"` \|
156
- `"AiInternalError"` \| `"TypeError"` \| `"AbortError"` \| `"TimeoutError"` \|
157
- `"Error"`; `code`: `number` \| `null`; \}\> \| `Readonly`\<\{ `category`:
158
- `"result_download"`; `status`: `number`; \}\>; \} \| `null`\>
131
+ `"INVALID_ARGUMENT"` \| `"MEDIA_PROVIDER_FAILURE"` \|
132
+ `"MEDIA_PROVIDER_FAILURE_UNCHARGED"` \| `"SAFETY_FILTER_REFUSED"`; `retryable`:
133
+ `boolean`; `provider?`: `MediaProviderDiagnostic`; \} \| `null`\>
159
134
 
160
135
  ## References
161
136
 
@@ -554,9 +554,9 @@ Start the native resize and encode chain.
554
554
 
555
555
  ###### Returns
556
556
 
557
- | Name | Type | Description |
558
- | ------------- | ----------------------- | -------------------------------------- |
559
- | `transform()` | (`options`) => `object` | Apply the native scale-down transform. |
557
+ | Name | Type | Description |
558
+ | ------------- | ----------------------- | ------------------------------------------ |
559
+ | `transform()` | (`options`) => `object` | Apply the native aspect-preserving resize. |
560
560
 
561
561
  ---
562
562
 
@@ -602,9 +602,9 @@ AI Gateway route.
602
602
  function createImagesReferenceFitter(images): ReferenceImageFitter;
603
603
  ```
604
604
 
605
- Media's reference preparation on the Images binding: decode the real bytes,
606
- scale an oversized image down to MAX_REFERENCE_DIMENSION as PNG, then decode the
607
- output so its metadata describes the bytes actually sent.
605
+ Media's reference preparation on the Images binding: decode the real bytes, fit
606
+ the selected model's reference size as PNG, then decode the output so its
607
+ metadata describes the bytes actually sent.
608
608
 
609
609
  #### Parameters
610
610