@tanstack/ai-fal 0.9.12 → 0.9.14
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/esm/adapters/audio.js +92 -76
- package/dist/esm/adapters/audio.js.map +1 -1
- package/dist/esm/adapters/image.js +99 -108
- package/dist/esm/adapters/image.js.map +1 -1
- package/dist/esm/adapters/speech.js +86 -83
- package/dist/esm/adapters/speech.js.map +1 -1
- package/dist/esm/adapters/transcription.js +90 -89
- package/dist/esm/adapters/transcription.js.map +1 -1
- package/dist/esm/adapters/video.js +146 -150
- package/dist/esm/adapters/video.js.map +1 -1
- package/dist/esm/image/generated/image-field-overrides.js +809 -367
- package/dist/esm/image/generated/image-field-overrides.js.map +1 -1
- package/dist/esm/image/image-inputs.js +143 -104
- package/dist/esm/image/image-inputs.js.map +1 -1
- package/dist/esm/image/image-provider-options.js +16 -21
- package/dist/esm/image/image-provider-options.js.map +1 -1
- package/dist/esm/index.js +2 -17
- package/dist/esm/utils/billing.js +89 -45
- package/dist/esm/utils/billing.js.map +1 -1
- package/dist/esm/utils/client.js +90 -87
- package/dist/esm/utils/client.js.map +1 -1
- package/dist/esm/video/video-provider-options.js +15 -16
- package/dist/esm/video/video-provider-options.js.map +1 -1
- package/package.json +7 -7
- package/dist/esm/index.js.map +0 -1
|
@@ -1,23 +1,18 @@
|
|
|
1
|
+
//#region src/image/image-provider-options.ts
|
|
1
2
|
function mapSizeToFalFormat(size) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return { aspect_ratio: size };
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return {
|
|
17
|
-
image_size: size
|
|
18
|
-
};
|
|
3
|
+
if (!size) return void 0;
|
|
4
|
+
if (typeof size === "string") {
|
|
5
|
+
if (size.includes("_")) {
|
|
6
|
+
const [first, second] = size.split("_");
|
|
7
|
+
if (first && first.includes(":")) return {
|
|
8
|
+
aspect_ratio: first,
|
|
9
|
+
resolution: second
|
|
10
|
+
};
|
|
11
|
+
} else if (size.includes(":")) return { aspect_ratio: size };
|
|
12
|
+
}
|
|
13
|
+
return { image_size: size };
|
|
19
14
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
//# sourceMappingURL=image-provider-options.js.map
|
|
15
|
+
//#endregion
|
|
16
|
+
export { mapSizeToFalFormat };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=image-provider-options.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-provider-options.js","sources":["../../../src/image/image-provider-options.ts"],"sourcesContent":["import type { FalModelImageSize, FalModelImageSizeInput } from '../model-meta'\n\nexport function mapSizeToFalFormat<TModel extends string>(\n size: FalModelImageSize<TModel> | undefined,\n): FalModelImageSizeInput<TModel> | undefined {\n if (!size) return undefined\n\n // \"16:9_4K\" → { aspect_ratio, resolution }\n // \"16:9\" → { aspect_ratio }\n // \"4K\" → { resolution } (no colon, no underscore, model has `resolution`)\n // \"square_hd\" → { image_size } (no colon, no resolution field on model)\n if (typeof size === 'string') {\n if (size.includes('_')) {\n const [first, second] = size.split('_')\n if (first && first.includes(':')) {\n return {\n aspect_ratio: first,\n resolution: second,\n } as FalModelImageSizeInput<TModel>\n }\n } else if (size.includes(':')) {\n return { aspect_ratio: size } as FalModelImageSizeInput<TModel>\n }\n }\n\n return {\n image_size: size,\n } as FalModelImageSizeInput<TModel>\n}\n"],"
|
|
1
|
+
{"version":3,"file":"image-provider-options.js","names":[],"sources":["../../../src/image/image-provider-options.ts"],"sourcesContent":["import type { FalModelImageSize, FalModelImageSizeInput } from '../model-meta'\n\nexport function mapSizeToFalFormat<TModel extends string>(\n size: FalModelImageSize<TModel> | undefined,\n): FalModelImageSizeInput<TModel> | undefined {\n if (!size) return undefined\n\n // \"16:9_4K\" → { aspect_ratio, resolution }\n // \"16:9\" → { aspect_ratio }\n // \"4K\" → { resolution } (no colon, no underscore, model has `resolution`)\n // \"square_hd\" → { image_size } (no colon, no resolution field on model)\n if (typeof size === 'string') {\n if (size.includes('_')) {\n const [first, second] = size.split('_')\n if (first && first.includes(':')) {\n return {\n aspect_ratio: first,\n resolution: second,\n } as FalModelImageSizeInput<TModel>\n }\n } else if (size.includes(':')) {\n return { aspect_ratio: size } as FalModelImageSizeInput<TModel>\n }\n }\n\n return {\n image_size: size,\n } as FalModelImageSizeInput<TModel>\n}\n"],"mappings":";AAEA,SAAgB,mBACd,MAC4C;CAC5C,IAAI,CAAC,MAAM,OAAO,KAAA;CAMlB,IAAI,OAAO,SAAS;MACd,KAAK,SAAS,GAAG,GAAG;GACtB,MAAM,CAAC,OAAO,UAAU,KAAK,MAAM,GAAG;GACtC,IAAI,SAAS,MAAM,SAAS,GAAG,GAC7B,OAAO;IACL,cAAc;IACd,YAAY;GACd;EAEJ,OAAO,IAAI,KAAK,SAAS,GAAG,GAC1B,OAAO,EAAE,cAAc,KAAK;CAAA;CAIhC,OAAO,EACL,YAAY,KACd;AACF"}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,22 +1,7 @@
|
|
|
1
|
+
import { configureFalClient, generateId, getFalApiKeyFromEnv } from "./utils/client.js";
|
|
1
2
|
import { FalImageAdapter, falImage } from "./adapters/image.js";
|
|
2
3
|
import { FalVideoAdapter, falVideo } from "./adapters/video.js";
|
|
3
4
|
import { FalSpeechAdapter, falSpeech } from "./adapters/speech.js";
|
|
4
5
|
import { FalTranscriptionAdapter, falTranscription } from "./adapters/transcription.js";
|
|
5
6
|
import { FalAudioAdapter, falAudio } from "./adapters/audio.js";
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
FalAudioAdapter,
|
|
9
|
-
FalImageAdapter,
|
|
10
|
-
FalSpeechAdapter,
|
|
11
|
-
FalTranscriptionAdapter,
|
|
12
|
-
FalVideoAdapter,
|
|
13
|
-
configureFalClient,
|
|
14
|
-
falAudio,
|
|
15
|
-
falImage,
|
|
16
|
-
falSpeech,
|
|
17
|
-
falTranscription,
|
|
18
|
-
falVideo,
|
|
19
|
-
generateId,
|
|
20
|
-
getFalApiKeyFromEnv
|
|
21
|
-
};
|
|
22
|
-
//# sourceMappingURL=index.js.map
|
|
7
|
+
export { FalAudioAdapter, FalImageAdapter, FalSpeechAdapter, FalTranscriptionAdapter, FalVideoAdapter, configureFalClient, falAudio, falImage, falSpeech, falTranscription, falVideo, generateId, getFalApiKeyFromEnv };
|
|
@@ -1,55 +1,99 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
//#region src/utils/billing.ts
|
|
2
|
+
/**
|
|
3
|
+
* Response header fal sets on a queue *result* fetch carrying the real billed
|
|
4
|
+
* quantity for the generation, denominated in the endpoint's priced unit.
|
|
5
|
+
*/
|
|
6
|
+
var FAL_BILLABLE_UNITS_HEADER = "x-fal-billable-units";
|
|
7
|
+
/**
|
|
8
|
+
* Response header fal sets carrying the request id. The fal client surfaces this
|
|
9
|
+
* same value as `Result.requestId`, so keying captured billable units by it
|
|
10
|
+
* guarantees the adapter's lookup matches the fetch the units came from — no URL
|
|
11
|
+
* parsing or global correlation registry of our own design needed.
|
|
12
|
+
*/
|
|
13
|
+
var FAL_REQUEST_ID_HEADER = "x-fal-request-id";
|
|
14
|
+
/**
|
|
15
|
+
* Upper bound on retained, not-yet-consumed billable-unit entries. Each
|
|
16
|
+
* successful generation reads-and-deletes its entry (see {@link takeBillableUnits}),
|
|
17
|
+
* so this only guards against an unbounded leak when a result fetch records units
|
|
18
|
+
* but the adapter never resolves (e.g. it throws before reading). When the cap is
|
|
19
|
+
* exceeded the oldest entry is evicted (Map preserves insertion order).
|
|
20
|
+
*/
|
|
21
|
+
var MAX_PENDING_ENTRIES = 256;
|
|
22
|
+
var billableUnitsByRequestId = /* @__PURE__ */ new Map();
|
|
23
|
+
/**
|
|
24
|
+
* Parse the `x-fal-billable-units` header value into a finite number. Returns
|
|
25
|
+
* `undefined` for a missing or non-numeric value so callers can skip attaching
|
|
26
|
+
* usage rather than surfacing `NaN`.
|
|
27
|
+
*/
|
|
5
28
|
function parseBillableUnits(value) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
29
|
+
if (value == null || value === "") return void 0;
|
|
30
|
+
const parsed = Number(value);
|
|
31
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
9
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Record the billable units carried by a fal result response, keyed by the
|
|
35
|
+
* request id from the same response. Reading headers does not consume the body,
|
|
36
|
+
* so the response can be returned to the fal client untouched.
|
|
37
|
+
*/
|
|
10
38
|
function recordBillableUnitsFromResponse(response) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
billableUnitsByRequestId.set(requestId, units);
|
|
39
|
+
const units = parseBillableUnits(response.headers.get(FAL_BILLABLE_UNITS_HEADER));
|
|
40
|
+
if (units == null) return;
|
|
41
|
+
const requestId = response.headers.get(FAL_REQUEST_ID_HEADER);
|
|
42
|
+
if (!requestId) return;
|
|
43
|
+
if (billableUnitsByRequestId.size >= MAX_PENDING_ENTRIES && !billableUnitsByRequestId.has(requestId)) {
|
|
44
|
+
const oldest = billableUnitsByRequestId.keys().next().value;
|
|
45
|
+
if (oldest !== void 0) billableUnitsByRequestId.delete(oldest);
|
|
46
|
+
}
|
|
47
|
+
billableUnitsByRequestId.set(requestId, units);
|
|
22
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Read and remove the billable units recorded for a request id. Removing on read
|
|
51
|
+
* keeps the registry from growing across the lifetime of the process.
|
|
52
|
+
*/
|
|
23
53
|
function takeBillableUnits(requestId) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
54
|
+
if (!requestId) return void 0;
|
|
55
|
+
const units = billableUnitsByRequestId.get(requestId);
|
|
56
|
+
if (units !== void 0) billableUnitsByRequestId.delete(requestId);
|
|
57
|
+
return units;
|
|
28
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Build a {@link TokenUsage} carrying fal's billed quantity. Media generation has
|
|
61
|
+
* no tokens, so the token fields are zero and the real billing signal rides on
|
|
62
|
+
* `unitsBilled` — mirroring how the duration-billed transcription adapters
|
|
63
|
+
* surface `durationSeconds`. Returns `undefined` when no units were captured so
|
|
64
|
+
* callers can omit `usage` entirely.
|
|
65
|
+
*/
|
|
29
66
|
function buildFalUsage(unitsBilled) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
67
|
+
if (unitsBilled == null) return void 0;
|
|
68
|
+
return {
|
|
69
|
+
promptTokens: 0,
|
|
70
|
+
completionTokens: 0,
|
|
71
|
+
totalTokens: 0,
|
|
72
|
+
unitsBilled
|
|
73
|
+
};
|
|
37
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Wrap a fetch so every fal request's response is inspected for the
|
|
77
|
+
* `x-fal-billable-units` header before being returned untouched. Installed as
|
|
78
|
+
* fal's `config.fetch`, which (unlike a global `responseHandler`) is honoured for
|
|
79
|
+
* every request — the fal client forces `resultResponseHandler` per queue
|
|
80
|
+
* operation, clobbering any configured response handler.
|
|
81
|
+
*
|
|
82
|
+
* `baseFetch` is the underlying implementation to delegate to (defaults to the
|
|
83
|
+
* global `fetch`). Injecting it keeps usage capture working when a caller
|
|
84
|
+
* supplies a custom fetch — a proxy, instrumentation, or a test mock — without
|
|
85
|
+
* mutating any global.
|
|
86
|
+
*/
|
|
38
87
|
function createBillingFetch(baseFetch = globalThis.fetch) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
};
|
|
88
|
+
return async (input, init) => {
|
|
89
|
+
const response = await baseFetch(input, init);
|
|
90
|
+
try {
|
|
91
|
+
recordBillableUnitsFromResponse(response);
|
|
92
|
+
} catch {}
|
|
93
|
+
return response;
|
|
94
|
+
};
|
|
47
95
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
recordBillableUnitsFromResponse,
|
|
53
|
-
takeBillableUnits
|
|
54
|
-
};
|
|
55
|
-
//# sourceMappingURL=billing.js.map
|
|
96
|
+
//#endregion
|
|
97
|
+
export { buildFalUsage, createBillingFetch, parseBillableUnits, recordBillableUnitsFromResponse, takeBillableUnits };
|
|
98
|
+
|
|
99
|
+
//# sourceMappingURL=billing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"billing.js","sources":["../../../src/utils/billing.ts"],"sourcesContent":["import type { TokenUsage } from '@tanstack/ai'\n\n/**\n * Response header fal sets on a queue *result* fetch carrying the real billed\n * quantity for the generation, denominated in the endpoint's priced unit.\n */\nconst FAL_BILLABLE_UNITS_HEADER = 'x-fal-billable-units'\n\n/**\n * Response header fal sets carrying the request id. The fal client surfaces this\n * same value as `Result.requestId`, so keying captured billable units by it\n * guarantees the adapter's lookup matches the fetch the units came from — no URL\n * parsing or global correlation registry of our own design needed.\n */\nconst FAL_REQUEST_ID_HEADER = 'x-fal-request-id'\n\n/**\n * Upper bound on retained, not-yet-consumed billable-unit entries. Each\n * successful generation reads-and-deletes its entry (see {@link takeBillableUnits}),\n * so this only guards against an unbounded leak when a result fetch records units\n * but the adapter never resolves (e.g. it throws before reading). When the cap is\n * exceeded the oldest entry is evicted (Map preserves insertion order).\n */\nconst MAX_PENDING_ENTRIES = 256\n\nconst billableUnitsByRequestId = new Map<string, number>()\n\n/**\n * Parse the `x-fal-billable-units` header value into a finite number. Returns\n * `undefined` for a missing or non-numeric value so callers can skip attaching\n * usage rather than surfacing `NaN`.\n */\nexport function parseBillableUnits(value: string | null): number | undefined {\n if (value == null || value === '') return undefined\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n}\n\n/**\n * Record the billable units carried by a fal result response, keyed by the\n * request id from the same response. Reading headers does not consume the body,\n * so the response can be returned to the fal client untouched.\n */\nexport function recordBillableUnitsFromResponse(response: Response): void {\n const units = parseBillableUnits(\n response.headers.get(FAL_BILLABLE_UNITS_HEADER),\n )\n if (units == null) return\n const requestId = response.headers.get(FAL_REQUEST_ID_HEADER)\n if (!requestId) return\n if (\n billableUnitsByRequestId.size >= MAX_PENDING_ENTRIES &&\n !billableUnitsByRequestId.has(requestId)\n ) {\n const oldest = billableUnitsByRequestId.keys().next().value\n if (oldest !== undefined) billableUnitsByRequestId.delete(oldest)\n }\n billableUnitsByRequestId.set(requestId, units)\n}\n\n/**\n * Read and remove the billable units recorded for a request id. Removing on read\n * keeps the registry from growing across the lifetime of the process.\n */\nexport function takeBillableUnits(\n requestId: string | undefined,\n): number | undefined {\n if (!requestId) return undefined\n const units = billableUnitsByRequestId.get(requestId)\n if (units !== undefined) billableUnitsByRequestId.delete(requestId)\n return units\n}\n\n/**\n * Build a {@link TokenUsage} carrying fal's billed quantity. Media generation has\n * no tokens, so the token fields are zero and the real billing signal rides on\n * `unitsBilled` — mirroring how the duration-billed transcription adapters\n * surface `durationSeconds`. Returns `undefined` when no units were captured so\n * callers can omit `usage` entirely.\n */\nexport function buildFalUsage(\n unitsBilled: number | undefined,\n): TokenUsage | undefined {\n if (unitsBilled == null) return undefined\n return {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n unitsBilled,\n }\n}\n\n/**\n * Wrap a fetch so every fal request's response is inspected for the\n * `x-fal-billable-units` header before being returned untouched. Installed as\n * fal's `config.fetch`, which (unlike a global `responseHandler`) is honoured for\n * every request — the fal client forces `resultResponseHandler` per queue\n * operation, clobbering any configured response handler.\n *\n * `baseFetch` is the underlying implementation to delegate to (defaults to the\n * global `fetch`). Injecting it keeps usage capture working when a caller\n * supplies a custom fetch — a proxy, instrumentation, or a test mock — without\n * mutating any global.\n */\nexport function createBillingFetch(\n baseFetch: typeof fetch = globalThis.fetch,\n): typeof fetch {\n return async (input, init) => {\n const response = await baseFetch(input, init)\n try {\n recordBillableUnitsFromResponse(response)\n } catch {\n // Capturing usage must never break the underlying request.\n }\n return response\n }\n}\n"],"
|
|
1
|
+
{"version":3,"file":"billing.js","names":[],"sources":["../../../src/utils/billing.ts"],"sourcesContent":["import type { TokenUsage } from '@tanstack/ai'\n\n/**\n * Response header fal sets on a queue *result* fetch carrying the real billed\n * quantity for the generation, denominated in the endpoint's priced unit.\n */\nconst FAL_BILLABLE_UNITS_HEADER = 'x-fal-billable-units'\n\n/**\n * Response header fal sets carrying the request id. The fal client surfaces this\n * same value as `Result.requestId`, so keying captured billable units by it\n * guarantees the adapter's lookup matches the fetch the units came from — no URL\n * parsing or global correlation registry of our own design needed.\n */\nconst FAL_REQUEST_ID_HEADER = 'x-fal-request-id'\n\n/**\n * Upper bound on retained, not-yet-consumed billable-unit entries. Each\n * successful generation reads-and-deletes its entry (see {@link takeBillableUnits}),\n * so this only guards against an unbounded leak when a result fetch records units\n * but the adapter never resolves (e.g. it throws before reading). When the cap is\n * exceeded the oldest entry is evicted (Map preserves insertion order).\n */\nconst MAX_PENDING_ENTRIES = 256\n\nconst billableUnitsByRequestId = new Map<string, number>()\n\n/**\n * Parse the `x-fal-billable-units` header value into a finite number. Returns\n * `undefined` for a missing or non-numeric value so callers can skip attaching\n * usage rather than surfacing `NaN`.\n */\nexport function parseBillableUnits(value: string | null): number | undefined {\n if (value == null || value === '') return undefined\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n}\n\n/**\n * Record the billable units carried by a fal result response, keyed by the\n * request id from the same response. Reading headers does not consume the body,\n * so the response can be returned to the fal client untouched.\n */\nexport function recordBillableUnitsFromResponse(response: Response): void {\n const units = parseBillableUnits(\n response.headers.get(FAL_BILLABLE_UNITS_HEADER),\n )\n if (units == null) return\n const requestId = response.headers.get(FAL_REQUEST_ID_HEADER)\n if (!requestId) return\n if (\n billableUnitsByRequestId.size >= MAX_PENDING_ENTRIES &&\n !billableUnitsByRequestId.has(requestId)\n ) {\n const oldest = billableUnitsByRequestId.keys().next().value\n if (oldest !== undefined) billableUnitsByRequestId.delete(oldest)\n }\n billableUnitsByRequestId.set(requestId, units)\n}\n\n/**\n * Read and remove the billable units recorded for a request id. Removing on read\n * keeps the registry from growing across the lifetime of the process.\n */\nexport function takeBillableUnits(\n requestId: string | undefined,\n): number | undefined {\n if (!requestId) return undefined\n const units = billableUnitsByRequestId.get(requestId)\n if (units !== undefined) billableUnitsByRequestId.delete(requestId)\n return units\n}\n\n/**\n * Build a {@link TokenUsage} carrying fal's billed quantity. Media generation has\n * no tokens, so the token fields are zero and the real billing signal rides on\n * `unitsBilled` — mirroring how the duration-billed transcription adapters\n * surface `durationSeconds`. Returns `undefined` when no units were captured so\n * callers can omit `usage` entirely.\n */\nexport function buildFalUsage(\n unitsBilled: number | undefined,\n): TokenUsage | undefined {\n if (unitsBilled == null) return undefined\n return {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n unitsBilled,\n }\n}\n\n/**\n * Wrap a fetch so every fal request's response is inspected for the\n * `x-fal-billable-units` header before being returned untouched. Installed as\n * fal's `config.fetch`, which (unlike a global `responseHandler`) is honoured for\n * every request — the fal client forces `resultResponseHandler` per queue\n * operation, clobbering any configured response handler.\n *\n * `baseFetch` is the underlying implementation to delegate to (defaults to the\n * global `fetch`). Injecting it keeps usage capture working when a caller\n * supplies a custom fetch — a proxy, instrumentation, or a test mock — without\n * mutating any global.\n */\nexport function createBillingFetch(\n baseFetch: typeof fetch = globalThis.fetch,\n): typeof fetch {\n return async (input, init) => {\n const response = await baseFetch(input, init)\n try {\n recordBillableUnitsFromResponse(response)\n } catch {\n // Capturing usage must never break the underlying request.\n }\n return response\n }\n}\n"],"mappings":";;;;;AAMA,IAAM,4BAA4B;;;;;;;AAQlC,IAAM,wBAAwB;;;;;;;;AAS9B,IAAM,sBAAsB;AAE5B,IAAM,2CAA2B,IAAI,IAAoB;;;;;;AAOzD,SAAgB,mBAAmB,OAA0C;CAC3E,IAAI,SAAS,QAAQ,UAAU,IAAI,OAAO,KAAA;CAC1C,MAAM,SAAS,OAAO,KAAK;CAC3B,OAAO,OAAO,SAAS,MAAM,IAAI,SAAS,KAAA;AAC5C;;;;;;AAOA,SAAgB,gCAAgC,UAA0B;CACxE,MAAM,QAAQ,mBACZ,SAAS,QAAQ,IAAI,yBAAyB,CAChD;CACA,IAAI,SAAS,MAAM;CACnB,MAAM,YAAY,SAAS,QAAQ,IAAI,qBAAqB;CAC5D,IAAI,CAAC,WAAW;CAChB,IACE,yBAAyB,QAAQ,uBACjC,CAAC,yBAAyB,IAAI,SAAS,GACvC;EACA,MAAM,SAAS,yBAAyB,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;EACtD,IAAI,WAAW,KAAA,GAAW,yBAAyB,OAAO,MAAM;CAClE;CACA,yBAAyB,IAAI,WAAW,KAAK;AAC/C;;;;;AAMA,SAAgB,kBACd,WACoB;CACpB,IAAI,CAAC,WAAW,OAAO,KAAA;CACvB,MAAM,QAAQ,yBAAyB,IAAI,SAAS;CACpD,IAAI,UAAU,KAAA,GAAW,yBAAyB,OAAO,SAAS;CAClE,OAAO;AACT;;;;;;;;AASA,SAAgB,cACd,aACwB;CACxB,IAAI,eAAe,MAAM,OAAO,KAAA;CAChC,OAAO;EACL,cAAc;EACd,kBAAkB;EAClB,aAAa;EACb;CACF;AACF;;;;;;;;;;;;;AAcA,SAAgB,mBACd,YAA0B,WAAW,OACvB;CACd,OAAO,OAAO,OAAO,SAAS;EAC5B,MAAM,WAAW,MAAM,UAAU,OAAO,IAAI;EAC5C,IAAI;GACF,gCAAgC,QAAQ;EAC1C,QAAQ,CAER;EACA,OAAO;CACT;AACF"}
|
package/dist/esm/utils/client.js
CHANGED
|
@@ -1,101 +1,104 @@
|
|
|
1
|
-
import { fal } from "@fal-ai/client";
|
|
2
|
-
import { generateId as generateId$1, getApiKeyFromEnv } from "@tanstack/ai-utils";
|
|
3
1
|
import { createBillingFetch } from "./billing.js";
|
|
2
|
+
import { fal } from "@fal-ai/client";
|
|
3
|
+
import { generateId, getApiKeyFromEnv } from "@tanstack/ai-utils";
|
|
4
|
+
//#region src/utils/client.ts
|
|
4
5
|
function getFalApiKeyFromEnv() {
|
|
5
|
-
|
|
6
|
+
return getApiKeyFromEnv("FAL_KEY");
|
|
6
7
|
}
|
|
7
8
|
function configureFalClient(config) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
fetch: createBillingFetch(config?.fetch),
|
|
15
|
-
...config?.proxyUrl ? { proxyUrl: config.proxyUrl } : {}
|
|
16
|
-
});
|
|
9
|
+
const apiKey = config?.apiKey ?? getFalApiKeyFromEnv();
|
|
10
|
+
fal.config({
|
|
11
|
+
credentials: apiKey,
|
|
12
|
+
fetch: createBillingFetch(config?.fetch),
|
|
13
|
+
...config?.proxyUrl ? { proxyUrl: config.proxyUrl } : {}
|
|
14
|
+
});
|
|
17
15
|
}
|
|
18
|
-
function generateId(prefix) {
|
|
19
|
-
|
|
16
|
+
function generateId$1(prefix) {
|
|
17
|
+
return generateId(prefix);
|
|
20
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Extract a safe file extension from a URL. Strips query strings, URL
|
|
21
|
+
* fragments, and any trailing slashes, and only returns the extension when
|
|
22
|
+
* it looks like a real one (2-5 alphanumeric chars). Returns undefined
|
|
23
|
+
* otherwise so callers can fall back to a default.
|
|
24
|
+
*/
|
|
21
25
|
function extractUrlExtension(url) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return /^[a-z0-9]{2,5}$/i.test(extension) ? extension : void 0;
|
|
26
|
+
let pathname;
|
|
27
|
+
try {
|
|
28
|
+
pathname = new URL(url).pathname;
|
|
29
|
+
} catch {
|
|
30
|
+
pathname = url.split("?")[0]?.split("#")[0] ?? url;
|
|
31
|
+
}
|
|
32
|
+
const normalized = pathname.replace(/\/+$/, "");
|
|
33
|
+
if (!normalized.includes("/")) return void 0;
|
|
34
|
+
const lastSegment = normalized.split("/").pop();
|
|
35
|
+
if (!lastSegment) return void 0;
|
|
36
|
+
const extension = lastSegment.split(".").pop();
|
|
37
|
+
if (!extension || extension === lastSegment) return void 0;
|
|
38
|
+
return /^[a-z0-9]{2,5}$/i.test(extension) ? extension : void 0;
|
|
36
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Derive a reasonable audio content-type. Prefers the explicit MIME (stripped
|
|
42
|
+
* of parameters), then an extension-based lookup for common audio formats,
|
|
43
|
+
* otherwise falls back to audio/mpeg — fal URLs virtually always serve mp3.
|
|
44
|
+
*/
|
|
37
45
|
function deriveAudioContentType(explicitContentType, url) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
case "m4a":
|
|
54
|
-
case "mp4":
|
|
55
|
-
return "audio/mp4";
|
|
56
|
-
case "webm":
|
|
57
|
-
return "audio/webm";
|
|
58
|
-
case void 0:
|
|
59
|
-
default:
|
|
60
|
-
return "audio/mpeg";
|
|
61
|
-
}
|
|
46
|
+
const stripped = explicitContentType?.split(";")[0]?.trim();
|
|
47
|
+
if (stripped) return stripped;
|
|
48
|
+
switch (extractUrlExtension(url)?.toLowerCase()) {
|
|
49
|
+
case "mp3": return "audio/mpeg";
|
|
50
|
+
case "wav": return "audio/wav";
|
|
51
|
+
case "ogg":
|
|
52
|
+
case "oga": return "audio/ogg";
|
|
53
|
+
case "flac": return "audio/flac";
|
|
54
|
+
case "aac": return "audio/aac";
|
|
55
|
+
case "m4a":
|
|
56
|
+
case "mp4": return "audio/mp4";
|
|
57
|
+
case "webm": return "audio/webm";
|
|
58
|
+
case void 0:
|
|
59
|
+
default: return "audio/mpeg";
|
|
60
|
+
}
|
|
62
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Decode a `data:` URL into a Blob so fal-client can auto-upload it via
|
|
64
|
+
* `fal.storage.upload`. fal's inference API rejects data URLs with a 422
|
|
65
|
+
* "Unsupported data URL", so we convert them before handing them off.
|
|
66
|
+
*
|
|
67
|
+
* Supports both base64 and URL-encoded data URLs. Returns `undefined` for
|
|
68
|
+
* anything that isn't a data URL, so callers can fall through to other
|
|
69
|
+
* handling (http URLs are passed to fal as-is).
|
|
70
|
+
*/
|
|
63
71
|
function dataUrlToBlob(value) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
return new Blob([decodeURIComponent(payload)], { type: mimeType });
|
|
72
|
+
if (!value.startsWith("data:")) return void 0;
|
|
73
|
+
const commaIndex = value.indexOf(",");
|
|
74
|
+
if (commaIndex === -1) return void 0;
|
|
75
|
+
const header = value.slice(5, commaIndex);
|
|
76
|
+
const payload = value.slice(commaIndex + 1);
|
|
77
|
+
const isBase64 = /;base64$/i.test(header);
|
|
78
|
+
const mimeType = header.split(";")[0] || "application/octet-stream";
|
|
79
|
+
if (isBase64) {
|
|
80
|
+
const binary = atob(payload);
|
|
81
|
+
const bytes = new Uint8Array(binary.length);
|
|
82
|
+
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
|
|
83
|
+
return new Blob([bytes], { type: mimeType });
|
|
84
|
+
}
|
|
85
|
+
return new Blob([decodeURIComponent(payload)], { type: mimeType });
|
|
80
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Convert an ArrayBuffer to base64 in a cross-runtime way.
|
|
89
|
+
*
|
|
90
|
+
* The naive `btoa(String.fromCharCode(...bytes))` form blows up V8's argument
|
|
91
|
+
* limit (~65k) on realistic audio payloads, so we either use `Buffer` (Node /
|
|
92
|
+
* Bun) or walk the byte array in a single loop (browser).
|
|
93
|
+
*/
|
|
81
94
|
function arrayBufferToBase64(bytes) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
for (const byte of view) {
|
|
88
|
-
binary += String.fromCharCode(byte);
|
|
89
|
-
}
|
|
90
|
-
return btoa(binary);
|
|
95
|
+
if (typeof Buffer !== "undefined" && typeof Buffer.from === "function") return Buffer.from(bytes).toString("base64");
|
|
96
|
+
const view = new Uint8Array(bytes);
|
|
97
|
+
let binary = "";
|
|
98
|
+
for (const byte of view) binary += String.fromCharCode(byte);
|
|
99
|
+
return btoa(binary);
|
|
91
100
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
deriveAudioContentType,
|
|
97
|
-
extractUrlExtension,
|
|
98
|
-
generateId,
|
|
99
|
-
getFalApiKeyFromEnv
|
|
100
|
-
};
|
|
101
|
-
//# sourceMappingURL=client.js.map
|
|
101
|
+
//#endregion
|
|
102
|
+
export { arrayBufferToBase64, configureFalClient, dataUrlToBlob, deriveAudioContentType, extractUrlExtension, generateId$1 as generateId, getFalApiKeyFromEnv };
|
|
103
|
+
|
|
104
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../../src/utils/client.ts"],"sourcesContent":["import { fal } from '@fal-ai/client'\nimport { generateId as _generateId, getApiKeyFromEnv } from '@tanstack/ai-utils'\nimport { createBillingFetch } from './billing'\n\nexport interface FalClientConfig {\n apiKey: string\n proxyUrl?: string\n /**\n * Override the underlying fetch used for fal requests. The adapter wraps it to\n * read the `x-fal-billable-units` header (see ./billing.ts), so usage capture\n * still works. Defaults to the global `fetch`. Useful for proxying,\n * instrumentation, or pointing requests at a mock in tests.\n */\n fetch?: typeof fetch\n}\n\nexport function getFalApiKeyFromEnv(): string {\n return getApiKeyFromEnv('FAL_KEY')\n}\n\nexport function configureFalClient(config?: FalClientConfig): void {\n const apiKey = config?.apiKey ?? getFalApiKeyFromEnv()\n fal.config({\n credentials: apiKey,\n // Wrap the (optionally overridden) fetch to read fal's\n // `x-fal-billable-units` header off every response so adapters can surface\n // the billed quantity as `result.usage`. See ./billing.ts.\n fetch: createBillingFetch(config?.fetch),\n ...(config?.proxyUrl ? { proxyUrl: config.proxyUrl } : {}),\n })\n}\n\nexport function generateId(prefix: string): string {\n return _generateId(prefix)\n}\n\n/**\n * Extract a safe file extension from a URL. Strips query strings, URL\n * fragments, and any trailing slashes, and only returns the extension when\n * it looks like a real one (2-5 alphanumeric chars). Returns undefined\n * otherwise so callers can fall back to a default.\n */\nexport function extractUrlExtension(url: string): string | undefined {\n // Parse via URL when possible so we only look at the pathname and never\n // mistake a TLD (e.g. the `.com` in `https://x.com/`) for a file extension.\n let pathname: string\n try {\n const parsed = new URL(url)\n pathname = parsed.pathname\n } catch {\n // Fall back to treating the input as a raw path when URL parsing fails\n // (e.g. the caller passed a bare path). Still strip ?query and #fragment.\n pathname = url.split('?')[0]?.split('#')[0] ?? url\n }\n // Drop trailing slashes so `/path/audio.mp3/` still yields `mp3`.\n const normalized = pathname.replace(/\\/+$/, '')\n // Require at least one `/` — otherwise we're looking at an empty pathname\n // (bare-host URLs like `https://x.com/` land here after stripping the\n // trailing slash).\n if (!normalized.includes('/')) return undefined\n const lastSegment = normalized.split('/').pop()\n if (!lastSegment) return undefined\n const extension = lastSegment.split('.').pop()\n if (!extension || extension === lastSegment) return undefined\n return /^[a-z0-9]{2,5}$/i.test(extension) ? extension : undefined\n}\n\n/**\n * Derive a reasonable audio content-type. Prefers the explicit MIME (stripped\n * of parameters), then an extension-based lookup for common audio formats,\n * otherwise falls back to audio/mpeg — fal URLs virtually always serve mp3.\n */\nexport function deriveAudioContentType(\n explicitContentType: string | undefined,\n url: string,\n): string {\n const stripped = explicitContentType?.split(';')[0]?.trim()\n if (stripped) return stripped\n\n const ext = extractUrlExtension(url)?.toLowerCase()\n switch (ext) {\n case 'mp3':\n return 'audio/mpeg'\n case 'wav':\n return 'audio/wav'\n case 'ogg':\n case 'oga':\n return 'audio/ogg'\n case 'flac':\n return 'audio/flac'\n case 'aac':\n return 'audio/aac'\n case 'm4a':\n case 'mp4':\n return 'audio/mp4'\n case 'webm':\n return 'audio/webm'\n case undefined:\n default:\n return 'audio/mpeg'\n }\n}\n\n/**\n * Decode a `data:` URL into a Blob so fal-client can auto-upload it via\n * `fal.storage.upload`. fal's inference API rejects data URLs with a 422\n * \"Unsupported data URL\", so we convert them before handing them off.\n *\n * Supports both base64 and URL-encoded data URLs. Returns `undefined` for\n * anything that isn't a data URL, so callers can fall through to other\n * handling (http URLs are passed to fal as-is).\n */\nexport function dataUrlToBlob(value: string): Blob | undefined {\n if (!value.startsWith('data:')) return undefined\n const commaIndex = value.indexOf(',')\n if (commaIndex === -1) return undefined\n\n const header = value.slice(5, commaIndex)\n const payload = value.slice(commaIndex + 1)\n const isBase64 = /;base64$/i.test(header)\n const mimeType = header.split(';')[0] || 'application/octet-stream'\n\n if (isBase64) {\n const binary = atob(payload)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i += 1) {\n bytes[i] = binary.charCodeAt(i)\n }\n return new Blob([bytes], { type: mimeType })\n }\n\n return new Blob([decodeURIComponent(payload)], { type: mimeType })\n}\n\n/**\n * Convert an ArrayBuffer to base64 in a cross-runtime way.\n *\n * The naive `btoa(String.fromCharCode(...bytes))` form blows up V8's argument\n * limit (~65k) on realistic audio payloads, so we either use `Buffer` (Node /\n * Bun) or walk the byte array in a single loop (browser).\n */\nexport function arrayBufferToBase64(bytes: ArrayBuffer): string {\n if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {\n return Buffer.from(bytes).toString('base64')\n }\n const view = new Uint8Array(bytes)\n let binary = ''\n for (const byte of view) {\n binary += String.fromCharCode(byte)\n }\n return btoa(binary)\n}\n"],"
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../../../src/utils/client.ts"],"sourcesContent":["import { fal } from '@fal-ai/client'\nimport { generateId as _generateId, getApiKeyFromEnv } from '@tanstack/ai-utils'\nimport { createBillingFetch } from './billing'\n\nexport interface FalClientConfig {\n apiKey: string\n proxyUrl?: string\n /**\n * Override the underlying fetch used for fal requests. The adapter wraps it to\n * read the `x-fal-billable-units` header (see ./billing.ts), so usage capture\n * still works. Defaults to the global `fetch`. Useful for proxying,\n * instrumentation, or pointing requests at a mock in tests.\n */\n fetch?: typeof fetch\n}\n\nexport function getFalApiKeyFromEnv(): string {\n return getApiKeyFromEnv('FAL_KEY')\n}\n\nexport function configureFalClient(config?: FalClientConfig): void {\n const apiKey = config?.apiKey ?? getFalApiKeyFromEnv()\n fal.config({\n credentials: apiKey,\n // Wrap the (optionally overridden) fetch to read fal's\n // `x-fal-billable-units` header off every response so adapters can surface\n // the billed quantity as `result.usage`. See ./billing.ts.\n fetch: createBillingFetch(config?.fetch),\n ...(config?.proxyUrl ? { proxyUrl: config.proxyUrl } : {}),\n })\n}\n\nexport function generateId(prefix: string): string {\n return _generateId(prefix)\n}\n\n/**\n * Extract a safe file extension from a URL. Strips query strings, URL\n * fragments, and any trailing slashes, and only returns the extension when\n * it looks like a real one (2-5 alphanumeric chars). Returns undefined\n * otherwise so callers can fall back to a default.\n */\nexport function extractUrlExtension(url: string): string | undefined {\n // Parse via URL when possible so we only look at the pathname and never\n // mistake a TLD (e.g. the `.com` in `https://x.com/`) for a file extension.\n let pathname: string\n try {\n const parsed = new URL(url)\n pathname = parsed.pathname\n } catch {\n // Fall back to treating the input as a raw path when URL parsing fails\n // (e.g. the caller passed a bare path). Still strip ?query and #fragment.\n pathname = url.split('?')[0]?.split('#')[0] ?? url\n }\n // Drop trailing slashes so `/path/audio.mp3/` still yields `mp3`.\n const normalized = pathname.replace(/\\/+$/, '')\n // Require at least one `/` — otherwise we're looking at an empty pathname\n // (bare-host URLs like `https://x.com/` land here after stripping the\n // trailing slash).\n if (!normalized.includes('/')) return undefined\n const lastSegment = normalized.split('/').pop()\n if (!lastSegment) return undefined\n const extension = lastSegment.split('.').pop()\n if (!extension || extension === lastSegment) return undefined\n return /^[a-z0-9]{2,5}$/i.test(extension) ? extension : undefined\n}\n\n/**\n * Derive a reasonable audio content-type. Prefers the explicit MIME (stripped\n * of parameters), then an extension-based lookup for common audio formats,\n * otherwise falls back to audio/mpeg — fal URLs virtually always serve mp3.\n */\nexport function deriveAudioContentType(\n explicitContentType: string | undefined,\n url: string,\n): string {\n const stripped = explicitContentType?.split(';')[0]?.trim()\n if (stripped) return stripped\n\n const ext = extractUrlExtension(url)?.toLowerCase()\n switch (ext) {\n case 'mp3':\n return 'audio/mpeg'\n case 'wav':\n return 'audio/wav'\n case 'ogg':\n case 'oga':\n return 'audio/ogg'\n case 'flac':\n return 'audio/flac'\n case 'aac':\n return 'audio/aac'\n case 'm4a':\n case 'mp4':\n return 'audio/mp4'\n case 'webm':\n return 'audio/webm'\n case undefined:\n default:\n return 'audio/mpeg'\n }\n}\n\n/**\n * Decode a `data:` URL into a Blob so fal-client can auto-upload it via\n * `fal.storage.upload`. fal's inference API rejects data URLs with a 422\n * \"Unsupported data URL\", so we convert them before handing them off.\n *\n * Supports both base64 and URL-encoded data URLs. Returns `undefined` for\n * anything that isn't a data URL, so callers can fall through to other\n * handling (http URLs are passed to fal as-is).\n */\nexport function dataUrlToBlob(value: string): Blob | undefined {\n if (!value.startsWith('data:')) return undefined\n const commaIndex = value.indexOf(',')\n if (commaIndex === -1) return undefined\n\n const header = value.slice(5, commaIndex)\n const payload = value.slice(commaIndex + 1)\n const isBase64 = /;base64$/i.test(header)\n const mimeType = header.split(';')[0] || 'application/octet-stream'\n\n if (isBase64) {\n const binary = atob(payload)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i += 1) {\n bytes[i] = binary.charCodeAt(i)\n }\n return new Blob([bytes], { type: mimeType })\n }\n\n return new Blob([decodeURIComponent(payload)], { type: mimeType })\n}\n\n/**\n * Convert an ArrayBuffer to base64 in a cross-runtime way.\n *\n * The naive `btoa(String.fromCharCode(...bytes))` form blows up V8's argument\n * limit (~65k) on realistic audio payloads, so we either use `Buffer` (Node /\n * Bun) or walk the byte array in a single loop (browser).\n */\nexport function arrayBufferToBase64(bytes: ArrayBuffer): string {\n if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {\n return Buffer.from(bytes).toString('base64')\n }\n const view = new Uint8Array(bytes)\n let binary = ''\n for (const byte of view) {\n binary += String.fromCharCode(byte)\n }\n return btoa(binary)\n}\n"],"mappings":";;;;AAgBA,SAAgB,sBAA8B;CAC5C,OAAO,iBAAiB,SAAS;AACnC;AAEA,SAAgB,mBAAmB,QAAgC;CACjE,MAAM,SAAS,QAAQ,UAAU,oBAAoB;CACrD,IAAI,OAAO;EACT,aAAa;EAIb,OAAO,mBAAmB,QAAQ,KAAK;EACvC,GAAI,QAAQ,WAAW,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;CAC1D,CAAC;AACH;AAEA,SAAgB,aAAW,QAAwB;CACjD,OAAO,WAAY,MAAM;AAC3B;;;;;;;AAQA,SAAgB,oBAAoB,KAAiC;CAGnE,IAAI;CACJ,IAAI;EAEF,WAAW,IADQ,IAAI,GACZ,CAAA,CAAO;CACpB,QAAQ;EAGN,WAAW,IAAI,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM;CACjD;CAEA,MAAM,aAAa,SAAS,QAAQ,QAAQ,EAAE;CAI9C,IAAI,CAAC,WAAW,SAAS,GAAG,GAAG,OAAO,KAAA;CACtC,MAAM,cAAc,WAAW,MAAM,GAAG,CAAC,CAAC,IAAI;CAC9C,IAAI,CAAC,aAAa,OAAO,KAAA;CACzB,MAAM,YAAY,YAAY,MAAM,GAAG,CAAC,CAAC,IAAI;CAC7C,IAAI,CAAC,aAAa,cAAc,aAAa,OAAO,KAAA;CACpD,OAAO,mBAAmB,KAAK,SAAS,IAAI,YAAY,KAAA;AAC1D;;;;;;AAOA,SAAgB,uBACd,qBACA,KACQ;CACR,MAAM,WAAW,qBAAqB,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK;CAC1D,IAAI,UAAU,OAAO;CAGrB,QADY,oBAAoB,GAAG,CAAC,EAAE,YAAY,GAClD;EACE,KAAK,OACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK;EACL,KAAK,OACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK;EACL,KAAK,OACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,KAAA;EACL,SACE,OAAO;CACX;AACF;;;;;;;;;;AAWA,SAAgB,cAAc,OAAiC;CAC7D,IAAI,CAAC,MAAM,WAAW,OAAO,GAAG,OAAO,KAAA;CACvC,MAAM,aAAa,MAAM,QAAQ,GAAG;CACpC,IAAI,eAAe,IAAI,OAAO,KAAA;CAE9B,MAAM,SAAS,MAAM,MAAM,GAAG,UAAU;CACxC,MAAM,UAAU,MAAM,MAAM,aAAa,CAAC;CAC1C,MAAM,WAAW,YAAY,KAAK,MAAM;CACxC,MAAM,WAAW,OAAO,MAAM,GAAG,CAAC,CAAC,MAAM;CAEzC,IAAI,UAAU;EACZ,MAAM,SAAS,KAAK,OAAO;EAC3B,MAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;EAC1C,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GACtC,MAAM,KAAK,OAAO,WAAW,CAAC;EAEhC,OAAO,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,MAAM,SAAS,CAAC;CAC7C;CAEA,OAAO,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,GAAG,EAAE,MAAM,SAAS,CAAC;AACnE;;;;;;;;AASA,SAAgB,oBAAoB,OAA4B;CAC9D,IAAI,OAAO,WAAW,eAAe,OAAO,OAAO,SAAS,YAC1D,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,QAAQ;CAE7C,MAAM,OAAO,IAAI,WAAW,KAAK;CACjC,IAAI,SAAS;CACb,KAAK,MAAM,QAAQ,MACjB,UAAU,OAAO,aAAa,IAAI;CAEpC,OAAO,KAAK,MAAM;AACpB"}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
+
//#region src/video/video-provider-options.ts
|
|
1
2
|
function mapVideoSizeToFalFormat(size) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
return { resolution: size };
|
|
3
|
+
if (!size) return void 0;
|
|
4
|
+
if (size.includes("_")) {
|
|
5
|
+
const [aspect_ratio, resolution] = size.split("_");
|
|
6
|
+
return {
|
|
7
|
+
aspect_ratio,
|
|
8
|
+
resolution
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
if (size.includes(":")) return { aspect_ratio: size };
|
|
12
|
+
return { resolution: size };
|
|
14
13
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//# sourceMappingURL=video-provider-options.js.map
|
|
14
|
+
//#endregion
|
|
15
|
+
export { mapVideoSizeToFalFormat };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=video-provider-options.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-provider-options.js","sources":["../../../src/video/video-provider-options.ts"],"sourcesContent":["import type { FalModelVideoSize, FalModelVideoSizeInput } from '../model-meta'\n\nexport function mapVideoSizeToFalFormat<TModel extends string>(\n size: FalModelVideoSize<TModel> | undefined,\n): FalModelVideoSizeInput<TModel> | undefined {\n if (!size) return undefined\n\n // \"16:9_720p\" → { aspect_ratio, resolution }\n // \"16:9\" → { aspect_ratio }\n // \"720p\" → { resolution }\n if (size.includes('_')) {\n const [aspect_ratio, resolution] = size.split('_')\n return {\n aspect_ratio,\n resolution,\n } as FalModelVideoSizeInput<TModel>\n }\n\n if (size.includes(':')) {\n return { aspect_ratio: size } as FalModelVideoSizeInput<TModel>\n }\n\n return { resolution: size } as FalModelVideoSizeInput<TModel>\n}\n"],"
|
|
1
|
+
{"version":3,"file":"video-provider-options.js","names":[],"sources":["../../../src/video/video-provider-options.ts"],"sourcesContent":["import type { FalModelVideoSize, FalModelVideoSizeInput } from '../model-meta'\n\nexport function mapVideoSizeToFalFormat<TModel extends string>(\n size: FalModelVideoSize<TModel> | undefined,\n): FalModelVideoSizeInput<TModel> | undefined {\n if (!size) return undefined\n\n // \"16:9_720p\" → { aspect_ratio, resolution }\n // \"16:9\" → { aspect_ratio }\n // \"720p\" → { resolution }\n if (size.includes('_')) {\n const [aspect_ratio, resolution] = size.split('_')\n return {\n aspect_ratio,\n resolution,\n } as FalModelVideoSizeInput<TModel>\n }\n\n if (size.includes(':')) {\n return { aspect_ratio: size } as FalModelVideoSizeInput<TModel>\n }\n\n return { resolution: size } as FalModelVideoSizeInput<TModel>\n}\n"],"mappings":";AAEA,SAAgB,wBACd,MAC4C;CAC5C,IAAI,CAAC,MAAM,OAAO,KAAA;CAKlB,IAAI,KAAK,SAAS,GAAG,GAAG;EACtB,MAAM,CAAC,cAAc,cAAc,KAAK,MAAM,GAAG;EACjD,OAAO;GACL;GACA;EACF;CACF;CAEA,IAAI,KAAK,SAAS,GAAG,GACnB,OAAO,EAAE,cAAc,KAAK;CAG9B,OAAO,EAAE,YAAY,KAAK;AAC5B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-fal",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.14",
|
|
4
4
|
"description": "fal.ai adapter for TanStack AI image, video, audio, speech, and transcription generation.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,22 +49,22 @@
|
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@fal-ai/client": "^1.10.1",
|
|
52
|
-
"@tanstack/ai-utils": "0.
|
|
52
|
+
"@tanstack/ai-utils": "0.4.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@vitest/coverage-v8": "4.0.14",
|
|
56
|
-
"vite": "^
|
|
57
|
-
"@tanstack/ai": "0.
|
|
56
|
+
"vite": "^8.1.4",
|
|
57
|
+
"@tanstack/ai": "0.43.1"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"@tanstack/ai": "0.
|
|
60
|
+
"@tanstack/ai": "0.43.1"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "vite build",
|
|
64
64
|
"clean": "premove ./build ./dist",
|
|
65
|
-
"lint:fix": "
|
|
65
|
+
"lint:fix": "oxlint src --type-aware --fix",
|
|
66
66
|
"test:build": "publint --strict",
|
|
67
|
-
"test:
|
|
67
|
+
"test:oxlint": "oxlint src --type-aware",
|
|
68
68
|
"test:lib": "vitest run",
|
|
69
69
|
"test:lib:dev": "pnpm test:lib --watch",
|
|
70
70
|
"test:types": "tsc"
|
package/dist/esm/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
|