@effect-uai/fal 0.13.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/codec.mjs ADDED
@@ -0,0 +1,73 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { Array, Effect, Match, Option, Schema, pipe } from "effect";
3
+ import * as AiError from "@effect-uai/core/AiError";
4
+ //#region src/codec.ts
5
+ var codec_exports = /* @__PURE__ */ __exportAll({
6
+ PROVIDER: () => "fal",
7
+ httpError: () => httpError,
8
+ missingImageField: () => missingImageField,
9
+ transportFailure: () => transportFailure
10
+ });
11
+ const PROVIDER = "fal";
12
+ const transportFailure = (cause) => new AiError.Unavailable({
13
+ provider: "fal",
14
+ raw: cause
15
+ });
16
+ const Detail = Schema.Struct({
17
+ loc: Schema.optional(Schema.Array(Schema.Unknown)),
18
+ msg: Schema.optional(Schema.String),
19
+ type: Schema.optional(Schema.String)
20
+ });
21
+ /** Wrapped in `detail` off the gateway, bare off the runner. */
22
+ const WireError = Schema.Union([Schema.Struct({ detail: Schema.Array(Detail) }), Schema.Array(Detail)]);
23
+ const decodeWireError = Schema.decodeUnknownEffect(Schema.fromJsonString(WireError));
24
+ const detailsOf = (wire) => "detail" in wire ? wire.detail : wire;
25
+ /** The one refusal `type` fal documents; every other type is a bad field. */
26
+ const CONTENT_POLICY = "content_policy_violation";
27
+ const refusalOf = (wire, raw) => pipe(detailsOf(wire), Array.findFirst((detail) => detail.type === CONTENT_POLICY), Option.map((detail) => new AiError.ContentFiltered({
28
+ provider: "fal",
29
+ ...detail.msg !== void 0 && { reason: detail.msg },
30
+ raw
31
+ })));
32
+ const statusError = (status, body) => Match.value(status).pipe(Match.when(429, () => new AiError.RateLimited({
33
+ provider: "fal",
34
+ raw: body
35
+ })), Match.whenOr(408, 504, () => new AiError.Timeout({
36
+ provider: "fal",
37
+ raw: body
38
+ })), Match.when(401, () => new AiError.AuthFailed({
39
+ provider: "fal",
40
+ subtype: "auth",
41
+ raw: body
42
+ })), Match.when(403, () => new AiError.AuthFailed({
43
+ provider: "fal",
44
+ subtype: "permission",
45
+ raw: body
46
+ })), Match.when(402, () => new AiError.AuthFailed({
47
+ provider: "fal",
48
+ subtype: "billing",
49
+ raw: body
50
+ })), Match.when(413, () => new AiError.ContextLengthExceeded({
51
+ provider: "fal",
52
+ raw: body
53
+ })), Match.when((n) => n >= 500, (n) => new AiError.Unavailable({
54
+ provider: "fal",
55
+ status: n,
56
+ raw: body
57
+ })), Match.orElse(() => new AiError.InvalidRequest({
58
+ provider: "fal",
59
+ raw: body
60
+ })));
61
+ /** Never fails: the returned error is the value. */
62
+ const httpError = (status, body) => decodeWireError(body).pipe(Effect.map((wire) => Option.getOrElse(refusalOf(wire, body), () => statusError(status, body))), Effect.orElseSucceed(() => statusError(status, body)));
63
+ /** `image_url`, `image_urls`, `input_image_urls`, `reference_image_url`, … */
64
+ const IMAGE_FIELD = /^[a-z_]*image[a-z_]*_urls?$/;
65
+ /**
66
+ * The reference field this endpoint asked for, if it rejected the request
67
+ * for want of one. `Effect` only because the decode is; it never fails.
68
+ */
69
+ const missingImageField = (body) => decodeWireError(body).pipe(Effect.map((wire) => pipe(detailsOf(wire), Array.filter((detail) => detail.type === "missing"), Array.map((detail) => detail.loc?.[1]), Array.findFirst((name) => typeof name === "string" && IMAGE_FIELD.test(name)))), Effect.orElseSucceed(() => Option.none()));
70
+ //#endregion
71
+ export { PROVIDER, httpError, missingImageField, codec_exports as t, transportFailure };
72
+
73
+ //# sourceMappingURL=codec.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Effect, Match, Option, Schema, pipe } from \"effect\"\nimport * as AiError from \"@effect-uai/core/AiError\"\n\nexport const PROVIDER = \"fal\"\n\nexport const transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: PROVIDER, raw: cause })\n\n// ---------------------------------------------------------------------------\n// Errors\n//\n// fal validates request bodies with Pydantic, so a rejected field and a\n// refused prompt arrive the same way: 422 with a `detail` array. The entry's\n// `type` is what separates them. Errors that carry `detail` as a plain\n// string (a missing app, a bad key) fail this decode and fall through to the\n// status mapping, which is where they belong.\n//\n// Reference: https://fal.ai/docs/model-apis/errors\n// ---------------------------------------------------------------------------\n\nconst Detail = Schema.Struct({\n loc: Schema.optional(Schema.Array(Schema.Unknown)),\n msg: Schema.optional(Schema.String),\n type: Schema.optional(Schema.String),\n})\ntype Detail = typeof Detail.Type\n\n/** Wrapped in `detail` off the gateway, bare off the runner. */\nconst WireError = Schema.Union([\n Schema.Struct({ detail: Schema.Array(Detail) }),\n Schema.Array(Detail),\n])\ntype WireError = typeof WireError.Type\n\nconst decodeWireError = Schema.decodeUnknownEffect(Schema.fromJsonString(WireError))\n\nconst detailsOf = (wire: WireError): ReadonlyArray<Detail> =>\n \"detail\" in wire ? wire.detail : wire\n\n/** The one refusal `type` fal documents; every other type is a bad field. */\nconst CONTENT_POLICY = \"content_policy_violation\"\n\nconst refusalOf = (wire: WireError, raw: string): Option.Option<AiError.AiError> =>\n pipe(\n detailsOf(wire),\n Arr.findFirst((detail) => detail.type === CONTENT_POLICY),\n Option.map(\n (detail) =>\n new AiError.ContentFiltered({\n provider: PROVIDER,\n ...(detail.msg !== undefined && { reason: detail.msg }),\n raw,\n }),\n ),\n )\n\nconst statusError = (status: number, body: string): AiError.AiError =>\n Match.value(status).pipe(\n Match.when(\n 429,\n (): AiError.AiError => new AiError.RateLimited({ provider: PROVIDER, raw: body }),\n ),\n Match.whenOr(\n 408,\n 504,\n (): AiError.AiError => new AiError.Timeout({ provider: PROVIDER, raw: body }),\n ),\n Match.when(\n 401,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: PROVIDER, subtype: \"auth\", raw: body }),\n ),\n Match.when(\n 403,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: PROVIDER, subtype: \"permission\", raw: body }),\n ),\n Match.when(\n 402,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: PROVIDER, subtype: \"billing\", raw: body }),\n ),\n Match.when(\n 413,\n (): AiError.AiError => new AiError.ContextLengthExceeded({ provider: PROVIDER, raw: body }),\n ),\n Match.when(\n (n) => n >= 500,\n (n): AiError.AiError => new AiError.Unavailable({ provider: PROVIDER, status: n, raw: body }),\n ),\n Match.orElse(\n (): AiError.AiError => new AiError.InvalidRequest({ provider: PROVIDER, raw: body }),\n ),\n )\n\n/** Never fails: the returned error is the value. */\nexport const httpError = (status: number, body: string): Effect.Effect<AiError.AiError> =>\n decodeWireError(body).pipe(\n Effect.map((wire) => Option.getOrElse(refusalOf(wire, body), () => statusError(status, body))),\n Effect.orElseSucceed(() => statusError(status, body)),\n )\n\n// ---------------------------------------------------------------------------\n// Reference-field discovery\n//\n// Endpoints spell the reference field four different ways and the id does\n// not say which. Rather than table fal's catalogue, read the answer off\n// the endpoint's own validation error: a `missing` entry names the field\n// it wanted in `loc`.\n// ---------------------------------------------------------------------------\n\n/** `image_url`, `image_urls`, `input_image_urls`, `reference_image_url`, … */\nconst IMAGE_FIELD = /^[a-z_]*image[a-z_]*_urls?$/\n\n/**\n * The reference field this endpoint asked for, if it rejected the request\n * for want of one. `Effect` only because the decode is; it never fails.\n */\nexport const missingImageField = (body: string): Effect.Effect<Option.Option<string>> =>\n decodeWireError(body).pipe(\n Effect.map((wire) =>\n pipe(\n detailsOf(wire),\n Arr.filter((detail) => detail.type === \"missing\"),\n Arr.map((detail) => detail.loc?.[1]),\n Arr.findFirst((name): name is string => typeof name === \"string\" && IMAGE_FIELD.test(name)),\n ),\n ),\n Effect.orElseSucceed(() => Option.none<string>()),\n )\n"],"mappings":";;;;;;;;;;AAGA,MAAa,WAAW;AAExB,MAAa,oBAAoB,UAC/B,IAAI,QAAQ,YAAY;CAAE,UAAA;CAAoB,KAAK;AAAM,CAAC;AAc5D,MAAM,SAAS,OAAO,OAAO;CAC3B,KAAK,OAAO,SAAS,OAAO,MAAM,OAAO,OAAO,CAAC;CACjD,KAAK,OAAO,SAAS,OAAO,MAAM;CAClC,MAAM,OAAO,SAAS,OAAO,MAAM;AACrC,CAAC;;AAID,MAAM,YAAY,OAAO,MAAM,CAC7B,OAAO,OAAO,EAAE,QAAQ,OAAO,MAAM,MAAM,EAAE,CAAC,GAC9C,OAAO,MAAM,MAAM,CACrB,CAAC;AAGD,MAAM,kBAAkB,OAAO,oBAAoB,OAAO,eAAe,SAAS,CAAC;AAEnF,MAAM,aAAa,SACjB,YAAY,OAAO,KAAK,SAAS;;AAGnC,MAAM,iBAAiB;AAEvB,MAAM,aAAa,MAAiB,QAClC,KACE,UAAU,IAAI,GACdA,MAAI,WAAW,WAAW,OAAO,SAAS,cAAc,GACxD,OAAO,KACJ,WACC,IAAI,QAAQ,gBAAgB;CAC1B,UAAA;CACA,GAAI,OAAO,QAAQ,KAAA,KAAa,EAAE,QAAQ,OAAO,IAAI;CACrD;AACF,CAAC,CACL,CACF;AAEF,MAAM,eAAe,QAAgB,SACnC,MAAM,MAAM,MAAM,CAAC,CAAC,KAClB,MAAM,KACJ,WACuB,IAAI,QAAQ,YAAY;CAAE,UAAA;CAAoB,KAAK;AAAK,CAAC,CAClF,GACA,MAAM,OACJ,KACA,WACuB,IAAI,QAAQ,QAAQ;CAAE,UAAA;CAAoB,KAAK;AAAK,CAAC,CAC9E,GACA,MAAM,KACJ,WAEE,IAAI,QAAQ,WAAW;CAAE,UAAA;CAAoB,SAAS;CAAQ,KAAK;AAAK,CAAC,CAC7E,GACA,MAAM,KACJ,WAEE,IAAI,QAAQ,WAAW;CAAE,UAAA;CAAoB,SAAS;CAAc,KAAK;AAAK,CAAC,CACnF,GACA,MAAM,KACJ,WAEE,IAAI,QAAQ,WAAW;CAAE,UAAA;CAAoB,SAAS;CAAW,KAAK;AAAK,CAAC,CAChF,GACA,MAAM,KACJ,WACuB,IAAI,QAAQ,sBAAsB;CAAE,UAAA;CAAoB,KAAK;AAAK,CAAC,CAC5F,GACA,MAAM,MACH,MAAM,KAAK,MACX,MAAuB,IAAI,QAAQ,YAAY;CAAE,UAAA;CAAoB,QAAQ;CAAG,KAAK;AAAK,CAAC,CAC9F,GACA,MAAM,aACmB,IAAI,QAAQ,eAAe;CAAE,UAAA;CAAoB,KAAK;AAAK,CAAC,CACrF,CACF;;AAGF,MAAa,aAAa,QAAgB,SACxC,gBAAgB,IAAI,CAAC,CAAC,KACpB,OAAO,KAAK,SAAS,OAAO,UAAU,UAAU,MAAM,IAAI,SAAS,YAAY,QAAQ,IAAI,CAAC,CAAC,GAC7F,OAAO,oBAAoB,YAAY,QAAQ,IAAI,CAAC,CACtD;;AAYF,MAAM,cAAc;;;;;AAMpB,MAAa,qBAAqB,SAChC,gBAAgB,IAAI,CAAC,CAAC,KACpB,OAAO,KAAK,SACV,KACE,UAAU,IAAI,GACdA,MAAI,QAAQ,WAAW,OAAO,SAAS,SAAS,GAChDA,MAAI,KAAK,WAAW,OAAO,MAAM,EAAE,GACnCA,MAAI,WAAW,SAAyB,OAAO,SAAS,YAAY,YAAY,KAAK,IAAI,CAAC,CAC5F,CACF,GACA,OAAO,oBAAoB,OAAO,KAAa,CAAC,CAClD"}
@@ -0,0 +1,4 @@
1
+ import { FalImageEditModel, FalImageModel } from "./models.mjs";
2
+ import { t as FalImageGenerator_d_exports } from "./FalImageGenerator.mjs";
3
+ import { t as codec_d_exports } from "./codec.mjs";
4
+ export { FalImageEditModel, FalImageGenerator_d_exports as FalImageGenerator, FalImageModel, codec_d_exports as codec };
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { t as codec_exports } from "./codec.mjs";
2
+ import { t as FalImageGenerator_exports } from "./FalImageGenerator.mjs";
3
+ import "./models.mjs";
4
+ export { FalImageGenerator_exports as FalImageGenerator, codec_exports as codec };
@@ -0,0 +1,32 @@
1
+ //#region src/models.d.ts
2
+ /**
3
+ * fal endpoint identifiers (as of September 2026). Unlike every other
4
+ * provider here, the "model" is a *path*: it selects the endpoint, and
5
+ * text-to-image and edit are separate endpoints of the same model
6
+ * family. Pass a `FalImageModel` to `generate` and a `FalImageEditModel`
7
+ * to `edit`.
8
+ *
9
+ * The `(string & {})` tail keeps autocomplete on the literals while
10
+ * accepting any string, so fal's whole catalogue works without an SDK
11
+ * update: the id is the model page's URL after `fal.ai/models/`.
12
+ *
13
+ * Read that literally. Whether an id carries the `fal-ai/` prefix varies
14
+ * even between generations of one model (`fal-ai/bytedance/seedream/v4.5/edit`
15
+ * has it, `bytedance/seedream/v5/pro/text-to-image` does not), and a
16
+ * wrong guess is fal's "Application not found", not a 404 you can read.
17
+ *
18
+ * Reference: https://fal.ai/models
19
+ */
20
+ type FalImageModel = "fal-ai/flux-2-pro" | "fal-ai/flux/schnell" | "fal-ai/flux/dev" | "bytedance/seedream/v5/pro/text-to-image" | "alibaba/qwen-image-3/text-to-image" | "meta/muse-image/text-to-image" | "openai/gpt-image-2" | "fal-ai/nano-banana-2" | (string & {});
21
+ /**
22
+ * Edit endpoints. A generate id sent to `edit` gets fal's 422 for an
23
+ * unknown field.
24
+ *
25
+ * Which wire field the references ride in differs per endpoint and the
26
+ * adapter works that out on its own, so any edit endpoint fal hosts
27
+ * works here, single-image ones included.
28
+ */
29
+ type FalImageEditModel = "fal-ai/flux-2-pro/edit" | "bytedance/seedream/v5/pro/edit" | "fal-ai/bytedance/seedream/v4.5/edit" | "alibaba/qwen-image-3/edit" | "fal-ai/nano-banana-2/edit" | "openai/gpt-image-2/edit" | "fal-ai/qwen-image-edit" | "fal-ai/flux/dev/image-to-image" | (string & {});
30
+ //#endregion
31
+ export { FalImageEditModel, FalImageModel };
32
+ //# sourceMappingURL=models.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;;;;;;;;;;;;;;;;;;KAkBY;;;;;;;;;KAoBA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@effect-uai/fal",
3
+ "version": "0.13.0",
4
+ "description": "fal provider for @effect-uai/core.",
5
+ "keywords": [
6
+ "ai",
7
+ "effect",
8
+ "fal",
9
+ "flux",
10
+ "image-generation",
11
+ "seedream"
12
+ ],
13
+ "homepage": "https://github.com/betalyra/effect-uai/tree/main/packages/providers/fal",
14
+ "bugs": {
15
+ "url": "https://github.com/betalyra/effect-uai/issues"
16
+ },
17
+ "license": "MIT",
18
+ "author": "Betalyra",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/betalyra/effect-uai",
22
+ "directory": "packages/providers/fal"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "src",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "type": "module",
31
+ "main": "./dist/index.mjs",
32
+ "types": "./dist/index.d.mts",
33
+ "exports": {
34
+ ".": {
35
+ "types": "./dist/index.d.mts",
36
+ "import": "./dist/index.mjs"
37
+ },
38
+ "./FalImageGenerator": {
39
+ "types": "./dist/FalImageGenerator.d.mts",
40
+ "import": "./dist/FalImageGenerator.mjs"
41
+ }
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "devDependencies": {
47
+ "effect": "4.0.0-rc.111",
48
+ "typescript": "^7.0.2",
49
+ "@effect-uai/core": "0.13.0"
50
+ },
51
+ "peerDependencies": {
52
+ "@effect-uai/core": ">=0.14.0 <1",
53
+ "effect": ">=4.0.0-rc.111 <5.0.0"
54
+ },
55
+ "scripts": {
56
+ "build": "tsdown",
57
+ "typecheck": "tsc --noEmit"
58
+ }
59
+ }