@howells/motif-sdk 7.1.0 → 7.2.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/image.js CHANGED
@@ -119,7 +119,7 @@ const MODELS = {
119
119
  "xhigh",
120
120
  "max"
121
121
  ],
122
- supportsResolution: false,
122
+ supportsResolution: true,
123
123
  supportsSyncMode: true,
124
124
  type: "generation",
125
125
  useQueue: true
@@ -186,7 +186,7 @@ const MODELS = {
186
186
  "xhigh",
187
187
  "max"
188
188
  ],
189
- supportsResolution: false,
189
+ supportsResolution: true,
190
190
  supportsSyncMode: true,
191
191
  type: "generation",
192
192
  useQueue: true
package/dist/index.cjs CHANGED
@@ -142,6 +142,9 @@ function scaleWithin(longEdge, longTerm, unit) {
142
142
  function roundUpTo(value, unit) {
143
143
  return unit * Math.ceil(value / unit);
144
144
  }
145
+ function roundDownTo(value, unit) {
146
+ return unit * Math.floor(value / unit);
147
+ }
145
148
  /**
146
149
  * Exact pixel dimensions for an aspect ratio at a resolution tier, or null for
147
150
  * `"auto"`.
@@ -171,8 +174,9 @@ function withinImageSizeBounds(size, bounds) {
171
174
  }
172
175
  /**
173
176
  * The exact size for an aspect on an endpoint with limits: the long edge as
174
- * close to `longEdge` as the ratio allows without passing it, then scaled up
175
- * only as far as a minimum edge or pixel count demands. Null for `"auto"`.
177
+ * close to `longEdge` as the ratio allows without passing it or the endpoint's
178
+ * maximum edge or pixel count, then scaled up only as far as a minimum edge or
179
+ * pixel count demands. Null for `"auto"`.
176
180
  * Undefined when no size in that ratio fits the limits: the caller refuses,
177
181
  * because rounding to another ratio is the defect this exists to remove.
178
182
  */
@@ -182,6 +186,8 @@ function boundedImageSize(aspect, longEdge, bounds) {
182
186
  const [width, height] = terms;
183
187
  const unit = bounds.multipleOf ?? EDGE_MULTIPLE;
184
188
  let scale = scaleWithin(longEdge, Math.max(width, height), unit);
189
+ if (bounds.maxEdge !== void 0) scale = Math.min(scale, roundDownTo(bounds.maxEdge / Math.max(width, height), unit));
190
+ if (bounds.maxPixels !== void 0) scale = Math.min(scale, roundDownTo(Math.sqrt(bounds.maxPixels / (width * height)), unit));
185
191
  if (bounds.minEdge !== void 0) scale = Math.max(scale, roundUpTo(bounds.minEdge / Math.min(width, height), unit));
186
192
  if (bounds.minPixels !== void 0) scale = Math.max(scale, roundUpTo(Math.sqrt(bounds.minPixels / (width * height)), unit));
187
193
  const size = {
@@ -760,7 +766,7 @@ const MODELS = {
760
766
  "xhigh",
761
767
  "max"
762
768
  ],
763
- supportsResolution: false,
769
+ supportsResolution: true,
764
770
  supportsSyncMode: true,
765
771
  type: "generation",
766
772
  useQueue: true
@@ -827,7 +833,7 @@ const MODELS = {
827
833
  "xhigh",
828
834
  "max"
829
835
  ],
830
- supportsResolution: false,
836
+ supportsResolution: true,
831
837
  supportsSyncMode: true,
832
838
  type: "generation",
833
839
  useQueue: true
@@ -2124,15 +2130,16 @@ function normalizeImageSize(imageSize, sizeMode) {
2124
2130
  throw new Error(`fal image_size must be one of ${FAL_IMAGE_SIZE_PRESETS.join(", ")} or WIDTHxHEIGHT`);
2125
2131
  }
2126
2132
  /**
2127
- * `image_size` for an aspect: fal's named preset where one holds the ratio
2128
- * exactly, else exact dimensions where the endpoint takes them, else the
2129
- * nearest preset. A Model without a resolution setting is sized to the preset
2133
+ * `image_size` for an aspect: exact dimensions at the requested resolution
2134
+ * where the endpoint takes them and has a resolution setting; otherwise fal's
2135
+ * named preset where one holds the ratio exactly, else exact dimensions where
2136
+ * the endpoint takes them, else the nearest preset. A Model without a resolution setting is sized to the preset
2130
2137
  * long edge, so an exact size costs what a preset would. Out of the Model's
2131
2138
  * limits it throws rather than rounding to another ratio.
2132
2139
  */
2133
2140
  function falImageSizeFor(config, aspect, resolution) {
2134
2141
  const bounds = config.customImageSize;
2135
- if (bounds === void 0 || FAL_PRESET_ASPECTS.includes(aspect)) return aspectToFalImageSize(aspect);
2142
+ if (bounds === void 0 || !config.supportsResolution && FAL_PRESET_ASPECTS.includes(aspect)) return aspectToFalImageSize(aspect);
2136
2143
  const size = boundedImageSize(aspect, config.supportsResolution ? RESOLUTION_LONG_EDGE[resolution] : FAL_PRESET_LONG_EDGE, bounds);
2137
2144
  if (size === void 0) throw new ImageSizeBoundsError(config.name, aspect, bounds);
2138
2145
  return size ?? aspectToFalImageSize(aspect);
@@ -2145,6 +2152,7 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
2145
2152
  if (options.editImageUrls !== void 0 && options.editImageUrls.length > 0 && !config.supportsEdit) unsupported(config, "image editing");
2146
2153
  if (options.aspect !== void 0 && sizeMode === "none") unsupported(config, "aspect");
2147
2154
  if (options.resolution !== void 0 && options.resolution !== "2K" && !config.supportsResolution) unsupported(config, "resolution");
2155
+ if (config.autoEditSize === true && hasEditImages && options.aspect === void 0 && options.imageSize === void 0 && options.resolution !== void 0) throw new Error(`${config.name} sizes an edit from its source image; pass an aspect with resolution`);
2148
2156
  if (options.aspect !== void 0 && config.supportedAspects !== void 0 && !config.supportedAspects.includes(options.aspect)) throw new Error(`${config.name} aspect must be one of ${config.supportedAspects.join(", ")}`);
2149
2157
  if (options.resolution !== void 0 && config.supportedResolutions !== void 0 && !config.supportedResolutions.includes(options.resolution)) throw new Error(`${config.name} resolution must be one of ${config.supportedResolutions.join(", ")}`);
2150
2158
  if (options.numImages !== void 0 && options.numImages !== 1 && !config.supportsNumImages) unsupported(config, "numImages");
package/dist/index.js CHANGED
@@ -141,6 +141,9 @@ function scaleWithin(longEdge, longTerm, unit) {
141
141
  function roundUpTo(value, unit) {
142
142
  return unit * Math.ceil(value / unit);
143
143
  }
144
+ function roundDownTo(value, unit) {
145
+ return unit * Math.floor(value / unit);
146
+ }
144
147
  /**
145
148
  * Exact pixel dimensions for an aspect ratio at a resolution tier, or null for
146
149
  * `"auto"`.
@@ -170,8 +173,9 @@ function withinImageSizeBounds(size, bounds) {
170
173
  }
171
174
  /**
172
175
  * The exact size for an aspect on an endpoint with limits: the long edge as
173
- * close to `longEdge` as the ratio allows without passing it, then scaled up
174
- * only as far as a minimum edge or pixel count demands. Null for `"auto"`.
176
+ * close to `longEdge` as the ratio allows without passing it or the endpoint's
177
+ * maximum edge or pixel count, then scaled up only as far as a minimum edge or
178
+ * pixel count demands. Null for `"auto"`.
175
179
  * Undefined when no size in that ratio fits the limits: the caller refuses,
176
180
  * because rounding to another ratio is the defect this exists to remove.
177
181
  */
@@ -181,6 +185,8 @@ function boundedImageSize(aspect, longEdge, bounds) {
181
185
  const [width, height] = terms;
182
186
  const unit = bounds.multipleOf ?? EDGE_MULTIPLE;
183
187
  let scale = scaleWithin(longEdge, Math.max(width, height), unit);
188
+ if (bounds.maxEdge !== void 0) scale = Math.min(scale, roundDownTo(bounds.maxEdge / Math.max(width, height), unit));
189
+ if (bounds.maxPixels !== void 0) scale = Math.min(scale, roundDownTo(Math.sqrt(bounds.maxPixels / (width * height)), unit));
184
190
  if (bounds.minEdge !== void 0) scale = Math.max(scale, roundUpTo(bounds.minEdge / Math.min(width, height), unit));
185
191
  if (bounds.minPixels !== void 0) scale = Math.max(scale, roundUpTo(Math.sqrt(bounds.minPixels / (width * height)), unit));
186
192
  const size = {
@@ -759,7 +765,7 @@ const MODELS = {
759
765
  "xhigh",
760
766
  "max"
761
767
  ],
762
- supportsResolution: false,
768
+ supportsResolution: true,
763
769
  supportsSyncMode: true,
764
770
  type: "generation",
765
771
  useQueue: true
@@ -826,7 +832,7 @@ const MODELS = {
826
832
  "xhigh",
827
833
  "max"
828
834
  ],
829
- supportsResolution: false,
835
+ supportsResolution: true,
830
836
  supportsSyncMode: true,
831
837
  type: "generation",
832
838
  useQueue: true
@@ -2123,15 +2129,16 @@ function normalizeImageSize(imageSize, sizeMode) {
2123
2129
  throw new Error(`fal image_size must be one of ${FAL_IMAGE_SIZE_PRESETS.join(", ")} or WIDTHxHEIGHT`);
2124
2130
  }
2125
2131
  /**
2126
- * `image_size` for an aspect: fal's named preset where one holds the ratio
2127
- * exactly, else exact dimensions where the endpoint takes them, else the
2128
- * nearest preset. A Model without a resolution setting is sized to the preset
2132
+ * `image_size` for an aspect: exact dimensions at the requested resolution
2133
+ * where the endpoint takes them and has a resolution setting; otherwise fal's
2134
+ * named preset where one holds the ratio exactly, else exact dimensions where
2135
+ * the endpoint takes them, else the nearest preset. A Model without a resolution setting is sized to the preset
2129
2136
  * long edge, so an exact size costs what a preset would. Out of the Model's
2130
2137
  * limits it throws rather than rounding to another ratio.
2131
2138
  */
2132
2139
  function falImageSizeFor(config, aspect, resolution) {
2133
2140
  const bounds = config.customImageSize;
2134
- if (bounds === void 0 || FAL_PRESET_ASPECTS.includes(aspect)) return aspectToFalImageSize(aspect);
2141
+ if (bounds === void 0 || !config.supportsResolution && FAL_PRESET_ASPECTS.includes(aspect)) return aspectToFalImageSize(aspect);
2135
2142
  const size = boundedImageSize(aspect, config.supportsResolution ? RESOLUTION_LONG_EDGE[resolution] : FAL_PRESET_LONG_EDGE, bounds);
2136
2143
  if (size === void 0) throw new ImageSizeBoundsError(config.name, aspect, bounds);
2137
2144
  return size ?? aspectToFalImageSize(aspect);
@@ -2144,6 +2151,7 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
2144
2151
  if (options.editImageUrls !== void 0 && options.editImageUrls.length > 0 && !config.supportsEdit) unsupported(config, "image editing");
2145
2152
  if (options.aspect !== void 0 && sizeMode === "none") unsupported(config, "aspect");
2146
2153
  if (options.resolution !== void 0 && options.resolution !== "2K" && !config.supportsResolution) unsupported(config, "resolution");
2154
+ if (config.autoEditSize === true && hasEditImages && options.aspect === void 0 && options.imageSize === void 0 && options.resolution !== void 0) throw new Error(`${config.name} sizes an edit from its source image; pass an aspect with resolution`);
2147
2155
  if (options.aspect !== void 0 && config.supportedAspects !== void 0 && !config.supportedAspects.includes(options.aspect)) throw new Error(`${config.name} aspect must be one of ${config.supportedAspects.join(", ")}`);
2148
2156
  if (options.resolution !== void 0 && config.supportedResolutions !== void 0 && !config.supportedResolutions.includes(options.resolution)) throw new Error(`${config.name} resolution must be one of ${config.supportedResolutions.join(", ")}`);
2149
2157
  if (options.numImages !== void 0 && options.numImages !== 1 && !config.supportsNumImages) unsupported(config, "numImages");
package/dist/registry.js CHANGED
@@ -96,7 +96,7 @@ const MODELS = {
96
96
  "xhigh",
97
97
  "max"
98
98
  ],
99
- supportsResolution: false,
99
+ supportsResolution: true,
100
100
  supportsSyncMode: true,
101
101
  type: "generation",
102
102
  useQueue: true
@@ -163,7 +163,7 @@ const MODELS = {
163
163
  "xhigh",
164
164
  "max"
165
165
  ],
166
- supportsResolution: false,
166
+ supportsResolution: true,
167
167
  supportsSyncMode: true,
168
168
  type: "generation",
169
169
  useQueue: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howells/motif-sdk",
3
- "version": "7.1.0",
3
+ "version": "7.2.0",
4
4
  "description": "Node SDK for Motif: image, video and 3D Tasks on fal.ai, with Motif choosing the Model.",
5
5
  "keywords": [
6
6
  "fal",