@torqlab/generate-activity-image 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TORQ Lab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,230 @@
1
+ [![Publish](https://github.com/torqlab/generate-activity-image/actions/workflows/publish.yml/badge.svg)](https://github.com/torqlab/generate-activity-image/actions/workflows/publish.yml)
2
+
3
+ # @torqlab/generate-activity-image
4
+
5
+ AI-powered image generation for athletic activities using Pollinations.ai with automatic retry logic, fallback support, and comprehensive error handling.
6
+
7
+ This package provides a robust interface for generating creative images from text prompts describing activities. It handles retries automatically, falls back to default prompts on failure, and returns images as base64-encoded data URLs for easy integration.
8
+
9
+ ## Install
10
+
11
+ Published to NPM.
12
+
13
+ ```bash
14
+ npm i @torqlab/generate-activity-image
15
+ ```
16
+
17
+ Or with Bun:
18
+
19
+ ```bash
20
+ bun add @torqlab/generate-activity-image
21
+ ```
22
+
23
+ ## Quick start
24
+
25
+ ```ts
26
+ import generateActivityImage from '@torqlab/generate-activity-image';
27
+ import type {
28
+ GenerateActivityImageInput,
29
+ GenerateActivityImageOutput,
30
+ } from '@torqlab/generate-activity-image';
31
+
32
+ const input: GenerateActivityImageInput = {
33
+ prompt: 'A runner on a mountain trail at sunset, dynamic motion, vibrant colors',
34
+ defaultPrompt: 'A person running outdoors',
35
+ provider: 'pollinations',
36
+ };
37
+
38
+ const result: GenerateActivityImageOutput = await generateActivityImage(input);
39
+
40
+ console.log('Image generated successfully');
41
+ console.log('Data URL:', result.imageData.substring(0, 50) + '...');
42
+ console.log('Used fallback:', result.fallback);
43
+ console.log('Attempts:', result.attempts);
44
+ ```
45
+
46
+ ## Features
47
+
48
+ - **AIpowered Image Generation**: Uses Pollinations.ai for high-quality image generation from text prompts
49
+ - **Automatic Retries**: Built-in retry logic with automatic fallback to default prompts
50
+ - **Base64 Data URLs**: Returns images as base64-encoded `data:image/png;base64,...` URLs
51
+ - **Provider Pattern**: Extensible provider architecture for adding new image generation services
52
+ - **Type Safety**: Full TypeScript support with comprehensive type definitions
53
+ - **Error Handling**: Graceful error handling with fallback mechanisms
54
+ - **Zero Dependencies**: No external runtime dependencies
55
+ - **Configurable**: Support for custom prompts, retry attempts, and provider selection
56
+
57
+ ## API Reference
58
+
59
+ ### `generateActivityImage(input)`
60
+
61
+ Generate an image from a text prompt with automatic retry and fallback.
62
+
63
+ #### Parameters
64
+
65
+ ```ts
66
+ interface GenerateActivityImageInput {
67
+ // Text prompt for image generation (max 1000 characters)
68
+ prompt: string;
69
+
70
+ // Default/fallback prompt (used if main prompt fails)
71
+ defaultPrompt: string;
72
+
73
+ // Number of retry attempts (optional)
74
+ attempts?: number;
75
+
76
+ // Image generation provider (optional, defaults to 'pollinations')
77
+ provider?: GenerateActivityImageProviderName;
78
+
79
+ // API keys for providers (optional)
80
+ providerApiKeys?: GenerateActivityImageProviderApiKeys;
81
+ }
82
+ ```
83
+
84
+ #### Returns
85
+
86
+ ```ts
87
+ interface GenerateActivityImageOutput {
88
+ // Base64-encoded image data URL
89
+ imageData: string;
90
+
91
+ // Whether fallback prompt was used
92
+ fallback: boolean;
93
+
94
+ // Number of retry attempts performed
95
+ attempts: number;
96
+ }
97
+ ```
98
+
99
+ #### Example
100
+
101
+ ```ts
102
+ const result = await generateActivityImage({
103
+ prompt: 'A cyclist riding through a forest at golden hour',
104
+ defaultPrompt: 'A person cycling outdoors',
105
+ provider: 'pollinations',
106
+ attempts: 3,
107
+ });
108
+
109
+ if (result.fallback) {
110
+ console.log('Used fallback prompt after retries');
111
+ }
112
+
113
+ // Use result.imageData in an image element
114
+ document.getElementById('activity-image').src = result.imageData;
115
+ ```
116
+
117
+ ## Providers
118
+
119
+ ### Pollinations (Default)
120
+
121
+ Free, unlimited image generation via [Pollinations.ai](https://pollinations.ai).
122
+
123
+ - **Model**: Flux (best for illustrations and balanced quality)
124
+ - **Cost**: Free
125
+ - **Rate Limiting**: None
126
+ - **Authentication**: Optional API key
127
+
128
+ #### Supported Models
129
+
130
+ - `flux` (default, best for illustrations)
131
+ - `seedream` (excellent prompt understanding)
132
+ - `gpt-image-large` (photorealism)
133
+ - `kontext` (context-aware)
134
+
135
+ The model is configurable via code customization.
136
+
137
+ ## Constants
138
+
139
+ ```ts
140
+ import {
141
+ GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH,
142
+ GENERATE_ACTIVITY_IMAGE_MAX_RETRIES,
143
+ } from '@torqlab/generate-activity-image';
144
+
145
+ // Maximum prompt length in characters
146
+ console.log(GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH); // 1000
147
+
148
+ // Maximum number of retries
149
+ console.log(GENERATE_ACTIVITY_IMAGE_MAX_RETRIES); // 2
150
+ ```
151
+
152
+ ## Type Definitions
153
+
154
+ ```ts
155
+ // Supported provider names
156
+ export type GenerateActivityImageProviderName = 'pollinations';
157
+
158
+ // API keys configuration
159
+ export interface GenerateActivityImageProviderApiKeys {
160
+ pollinations?: string;
161
+ }
162
+
163
+ // Input configuration
164
+ export interface GenerateActivityImageInput {
165
+ prompt: string;
166
+ defaultPrompt: string;
167
+ attempts?: number;
168
+ provider?: GenerateActivityImageProviderName;
169
+ providerApiKeys?: GenerateActivityImageProviderApiKeys;
170
+ }
171
+
172
+ // Output result
173
+ export interface GenerateActivityImageOutput {
174
+ imageData: string;
175
+ fallback: boolean;
176
+ attempts: number;
177
+ }
178
+
179
+ // Provider function type
180
+ export type GenerateActivityImageProvider = (prompt: string, apiKey?: string) => Promise<string>;
181
+ ```
182
+
183
+ ## Error Handling
184
+
185
+ The package provides comprehensive error handling with automatic fallback:
186
+
187
+ ```ts
188
+ import generateActivityImage from '@torqlab/generate-activity-image';
189
+
190
+ try {
191
+ const result = await generateActivityImage({
192
+ prompt: 'A runner on a scenic trail',
193
+ defaultPrompt: 'A person running',
194
+ });
195
+
196
+ if (result.fallback) {
197
+ console.log('Image generated using fallback prompt');
198
+ console.log('Prompt exceeded length or generation failed');
199
+ }
200
+ } catch (error) {
201
+ console.error('Image generation failed:', error);
202
+ // Handle error appropriately
203
+ }
204
+ ```
205
+
206
+ ## Behavior
207
+
208
+ ### Generation Flow
209
+
210
+ 1. Validate prompt length (max 1000 characters)
211
+ 2. Attempt generation with original prompt
212
+ 3. On failure, retry with default prompt (up to 2 retries)
213
+ 4. Return generated image as base64 data URL
214
+ 5. If all retries fail, use fallback prompt for final attempt
215
+ 6. Always returns a valid response (never throws if fallback succeeds)
216
+
217
+ ### Retry Logic
218
+
219
+ - **Attempt 1**: Original prompt
220
+ - **Attempt 2**: Default prompt (if original fails)
221
+ - **Attempt 3**: Default prompt again (if Attempt 2 fails)
222
+ - **Fallback**: Uses default prompt if all retries exhausted
223
+
224
+ ## License
225
+
226
+ MIT License - see [LICENSE](LICENSE) file for details.
227
+
228
+ ## Contributing
229
+
230
+ Issues and pull requests are welcome on [GitHub](https://github.com/torqlab/generate-activity-image).
@@ -0,0 +1,3 @@
1
+ export declare const GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH = 1000;
2
+ export declare const GENERATE_ACTIVITY_IMAGE_MAX_RETRIES = 2;
3
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yCAAyC,OAAO,CAAC;AAE9D,eAAO,MAAM,mCAAmC,IAAI,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { GenerateActivityImageInput, GenerateActivityImageOutput } from './types';
2
+ /**
3
+ * Generates activity image using configured AI provider.
4
+ *
5
+ * Provider is controlled by IMAGE_PROVIDER environment variable:
6
+ * - 'pollinations' (default): Free, unlimited Pollinations.ai
7
+ *
8
+ * Implements retry logic with prompt simplification and fallback mechanism.
9
+ * Attempts image generation up to GENERATE_ACTIVITY_IMAGE_MAX_RETRIES times, simplifying the prompt
10
+ * on each retry. If all retries fail, uses a safe fallback prompt. Images are
11
+ * downloaded from the provider and returned as base64-encoded data URLs.
12
+ *
13
+ * Generation process:
14
+ * 1. Get configured provider based on IMAGE_PROVIDER env var (defaults to Pollinations)
15
+ * 2. Validate prompt text length (max 600 characters)
16
+ * 3. Attempt generation with original prompt
17
+ * 4. On failure, retry with simplified prompt (max 2 retries)
18
+ * 5. If all retries fail, use fallback prompt
19
+ * 6. Images are downloaded from provider and returned as base64 data URLs
20
+ * 7. Always returns a valid base64-encoded image data URL
21
+ *
22
+ * @param {GenerateActivityImageInput} input - Image generation input with prompt
23
+ * @returns {Promise<GenerateActivityImageOutput>} Promise resolving to generated image data and metadata
24
+ * @throws {Error} Throws error if generation fails
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const result = await generateImage({ prompt });
29
+ * console.log('Image data:', result.imageData);
30
+ * console.log('Used fallback:', result.fallback);
31
+ * ```
32
+ */
33
+ declare const generateActivityImage: ({ prompt, defaultPrompt, provider, providerApiKeys, attempts, }: GenerateActivityImageInput) => Promise<GenerateActivityImageOutput>;
34
+ export default generateActivityImage;
35
+ //# sourceMappingURL=generate-activity-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-activity-image.d.ts","sourceRoot":"","sources":["../generate-activity-image.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,0BAA0B,EAC1B,2BAA2B,EAE5B,MAAM,SAAS,CAAC;AA4CjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,QAAA,MAAM,qBAAqB,GAAU,iEAMlC,0BAA0B,KAAG,OAAO,CAAC,2BAA2B,CAuClE,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
package/dist/index.cjs ADDED
@@ -0,0 +1,162 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
8
+ var __toCommonJS = (from) => {
9
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
10
+ if (entry)
11
+ return entry;
12
+ entry = __defProp({}, "__esModule", { value: true });
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
21
+ __moduleCache.set(from, entry);
22
+ return entry;
23
+ };
24
+ var __moduleCache;
25
+ var __returnValue = (v) => v;
26
+ function __exportSetter(name, newValue) {
27
+ this[name] = __returnValue.bind(null, newValue);
28
+ }
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, {
32
+ get: all[name],
33
+ enumerable: true,
34
+ configurable: true,
35
+ set: __exportSetter.bind(all, name)
36
+ });
37
+ };
38
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
39
+
40
+ // providers/pollinations/constants.ts
41
+ var BASE_URL = "https://gen.pollinations.ai/image/", MODEL = "flux", PROMPT_ENHANCER = "side view, natural human anatomy, natural and clear eyes, attractive face, proportional arms and legs, symmetrical joints, clean lines, soft natural lighting, professional illustration", NEGATIVE_PROMPT = "distorted or warped eyes, twisted joints, distorted feet, unrealistic pose, distorted or warped faces, warped or distorted eyes, extra limbs, malformed or distorted hands, blurry, low quality, pixelated, ugly, deformed body, disfigured, bad or broken anatomy, bad proportions, extra fingers, missing fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, noise, extra limbs, cloned face, disfigured, gross proportions, malformed limbs, missing arms, missing legs, extra arms, extra legs, mutated hands, out of focus, long neck, long body, logo, watermark", BASE_64_PREFIX = "data:image/png;base64,", IMAGE_WIDTH = 1024, IMAGE_HEIGHT = 1024;
42
+
43
+ // providers/pollinations/pollinations.ts
44
+ var getSeed = () => String(Math.floor(Math.random() * 1e6)), getUrl = (prompt) => {
45
+ const promptEnhanced = `${PROMPT_ENHANCER}, ${prompt}`;
46
+ const url = new URL(`${BASE_URL}${encodeURIComponent(promptEnhanced)}`);
47
+ url.searchParams.set("width", String(IMAGE_WIDTH));
48
+ url.searchParams.set("height", String(IMAGE_HEIGHT));
49
+ url.searchParams.set("model", MODEL);
50
+ url.searchParams.set("nologo", "true");
51
+ url.searchParams.set("enhance", "true");
52
+ url.searchParams.set("negative", encodeURIComponent(NEGATIVE_PROMPT));
53
+ url.searchParams.set("seed", getSeed());
54
+ return url.toString();
55
+ }, pollinations = async (prompt, apiKey) => {
56
+ const url = getUrl(prompt);
57
+ const response = await fetch(url, {
58
+ ...apiKey && {
59
+ headers: {
60
+ Authorization: `Bearer ${apiKey}`
61
+ }
62
+ }
63
+ });
64
+ if (response.ok) {
65
+ const imageBuffer = await response.arrayBuffer();
66
+ const base64 = Buffer.from(imageBuffer).toString("base64");
67
+ return `${BASE_64_PREFIX}${base64}`;
68
+ } else {
69
+ throw new Error(`Pollinations API error: ${response.statusText}`);
70
+ }
71
+ }, pollinations_default;
72
+ var init_pollinations = __esm(() => {
73
+ pollinations_default = pollinations;
74
+ });
75
+
76
+ // providers/pollinations/index.ts
77
+ var exports_pollinations = {};
78
+ __export(exports_pollinations, {
79
+ default: () => pollinations_default
80
+ });
81
+ var init_pollinations2 = __esm(() => {
82
+ init_pollinations();
83
+ });
84
+
85
+ // index.ts
86
+ var exports_generate_activity_image = {};
87
+ __export(exports_generate_activity_image, {
88
+ default: () => generate_activity_image_default,
89
+ GENERATE_ACTIVITY_IMAGE_MAX_RETRIES: () => GENERATE_ACTIVITY_IMAGE_MAX_RETRIES,
90
+ GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH: () => GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH
91
+ });
92
+ module.exports = __toCommonJS(exports_generate_activity_image);
93
+
94
+ // providers/get-provider.ts
95
+ var getProvider = (providerName = "pollinations", providerApiKeys) => {
96
+ switch (providerName) {
97
+ case "pollinations": {
98
+ return async (prompt) => {
99
+ const { default: pollinations2 } = await Promise.resolve().then(() => (init_pollinations2(), exports_pollinations));
100
+ return pollinations2(prompt, providerApiKeys?.pollinations);
101
+ };
102
+ }
103
+ default: {
104
+ throw new Error(`Unknown image generation provider: ${String(providerName)}.`);
105
+ }
106
+ }
107
+ };
108
+ var get_provider_default = getProvider;
109
+ // constants.ts
110
+ var GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH = 1000;
111
+ var GENERATE_ACTIVITY_IMAGE_MAX_RETRIES = 2;
112
+
113
+ // generate-activity-image.ts
114
+ var attemptGeneration = async (prompt, defaultPrompt, attempt, maxAttempts, providerName, providerApiKeys) => {
115
+ const provider = get_provider_default(providerName, providerApiKeys);
116
+ try {
117
+ return await provider(prompt);
118
+ } catch (error) {
119
+ if (attempt < maxAttempts) {
120
+ const nextAttempt = attempt + 1;
121
+ return attemptGeneration(defaultPrompt, defaultPrompt, nextAttempt, maxAttempts, providerName, providerApiKeys);
122
+ } else {
123
+ throw error;
124
+ }
125
+ }
126
+ };
127
+ var generateActivityImage = async ({
128
+ prompt,
129
+ defaultPrompt,
130
+ provider,
131
+ providerApiKeys,
132
+ attempts
133
+ }) => {
134
+ const isAllowedPromptLength = prompt.length <= GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH;
135
+ if (isAllowedPromptLength) {
136
+ try {
137
+ const imageData = await attemptGeneration(prompt, defaultPrompt, 0, GENERATE_ACTIVITY_IMAGE_MAX_RETRIES, provider, providerApiKeys);
138
+ return {
139
+ fallback: false,
140
+ attempts: attempts ?? 0,
141
+ imageData
142
+ };
143
+ } catch {
144
+ const run = get_provider_default(provider, providerApiKeys);
145
+ const fallbackImageData = await run(defaultPrompt);
146
+ return {
147
+ imageData: fallbackImageData,
148
+ fallback: true,
149
+ attempts: GENERATE_ACTIVITY_IMAGE_MAX_RETRIES
150
+ };
151
+ }
152
+ } else {
153
+ const run = get_provider_default(provider, providerApiKeys);
154
+ const fallbackImageData = await run(defaultPrompt);
155
+ return {
156
+ imageData: fallbackImageData,
157
+ fallback: true,
158
+ attempts: GENERATE_ACTIVITY_IMAGE_MAX_RETRIES
159
+ };
160
+ }
161
+ };
162
+ var generate_activity_image_default = generateActivityImage;
@@ -0,0 +1,4 @@
1
+ export { default } from './generate-activity-image';
2
+ export { GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH, GENERATE_ACTIVITY_IMAGE_MAX_RETRIES, } from './constants';
3
+ export type { GenerateActivityImageProvider, GenerateActivityImageProviderName, GenerateActivityImageProviderApiKeys, GenerateActivityImageInput, GenerateActivityImageOutput, } from './types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,EACL,yCAAyC,EACzC,mCAAmC,GACpC,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,6BAA6B,EAC7B,iCAAiC,EACjC,oCAAoC,EACpC,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,SAAS,CAAC"}
package/dist/index.mjs ADDED
@@ -0,0 +1,135 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __returnValue = (v) => v;
3
+ function __exportSetter(name, newValue) {
4
+ this[name] = __returnValue.bind(null, newValue);
5
+ }
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true,
11
+ configurable: true,
12
+ set: __exportSetter.bind(all, name)
13
+ });
14
+ };
15
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
16
+
17
+ // providers/pollinations/constants.ts
18
+ var BASE_URL = "https://gen.pollinations.ai/image/", MODEL = "flux", PROMPT_ENHANCER = "side view, natural human anatomy, natural and clear eyes, attractive face, proportional arms and legs, symmetrical joints, clean lines, soft natural lighting, professional illustration", NEGATIVE_PROMPT = "distorted or warped eyes, twisted joints, distorted feet, unrealistic pose, distorted or warped faces, warped or distorted eyes, extra limbs, malformed or distorted hands, blurry, low quality, pixelated, ugly, deformed body, disfigured, bad or broken anatomy, bad proportions, extra fingers, missing fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, noise, extra limbs, cloned face, disfigured, gross proportions, malformed limbs, missing arms, missing legs, extra arms, extra legs, mutated hands, out of focus, long neck, long body, logo, watermark", BASE_64_PREFIX = "data:image/png;base64,", IMAGE_WIDTH = 1024, IMAGE_HEIGHT = 1024;
19
+
20
+ // providers/pollinations/pollinations.ts
21
+ var getSeed = () => String(Math.floor(Math.random() * 1e6)), getUrl = (prompt) => {
22
+ const promptEnhanced = `${PROMPT_ENHANCER}, ${prompt}`;
23
+ const url = new URL(`${BASE_URL}${encodeURIComponent(promptEnhanced)}`);
24
+ url.searchParams.set("width", String(IMAGE_WIDTH));
25
+ url.searchParams.set("height", String(IMAGE_HEIGHT));
26
+ url.searchParams.set("model", MODEL);
27
+ url.searchParams.set("nologo", "true");
28
+ url.searchParams.set("enhance", "true");
29
+ url.searchParams.set("negative", encodeURIComponent(NEGATIVE_PROMPT));
30
+ url.searchParams.set("seed", getSeed());
31
+ return url.toString();
32
+ }, pollinations = async (prompt, apiKey) => {
33
+ const url = getUrl(prompt);
34
+ const response = await fetch(url, {
35
+ ...apiKey && {
36
+ headers: {
37
+ Authorization: `Bearer ${apiKey}`
38
+ }
39
+ }
40
+ });
41
+ if (response.ok) {
42
+ const imageBuffer = await response.arrayBuffer();
43
+ const base64 = Buffer.from(imageBuffer).toString("base64");
44
+ return `${BASE_64_PREFIX}${base64}`;
45
+ } else {
46
+ throw new Error(`Pollinations API error: ${response.statusText}`);
47
+ }
48
+ }, pollinations_default;
49
+ var init_pollinations = __esm(() => {
50
+ pollinations_default = pollinations;
51
+ });
52
+
53
+ // providers/pollinations/index.ts
54
+ var exports_pollinations = {};
55
+ __export(exports_pollinations, {
56
+ default: () => pollinations_default
57
+ });
58
+ var init_pollinations2 = __esm(() => {
59
+ init_pollinations();
60
+ });
61
+
62
+ // providers/get-provider.ts
63
+ var getProvider = (providerName = "pollinations", providerApiKeys) => {
64
+ switch (providerName) {
65
+ case "pollinations": {
66
+ return async (prompt) => {
67
+ const { default: pollinations2 } = await Promise.resolve().then(() => (init_pollinations2(), exports_pollinations));
68
+ return pollinations2(prompt, providerApiKeys?.pollinations);
69
+ };
70
+ }
71
+ default: {
72
+ throw new Error(`Unknown image generation provider: ${String(providerName)}.`);
73
+ }
74
+ }
75
+ };
76
+ var get_provider_default = getProvider;
77
+ // constants.ts
78
+ var GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH = 1000;
79
+ var GENERATE_ACTIVITY_IMAGE_MAX_RETRIES = 2;
80
+
81
+ // generate-activity-image.ts
82
+ var attemptGeneration = async (prompt, defaultPrompt, attempt, maxAttempts, providerName, providerApiKeys) => {
83
+ const provider = get_provider_default(providerName, providerApiKeys);
84
+ try {
85
+ return await provider(prompt);
86
+ } catch (error) {
87
+ if (attempt < maxAttempts) {
88
+ const nextAttempt = attempt + 1;
89
+ return attemptGeneration(defaultPrompt, defaultPrompt, nextAttempt, maxAttempts, providerName, providerApiKeys);
90
+ } else {
91
+ throw error;
92
+ }
93
+ }
94
+ };
95
+ var generateActivityImage = async ({
96
+ prompt,
97
+ defaultPrompt,
98
+ provider,
99
+ providerApiKeys,
100
+ attempts
101
+ }) => {
102
+ const isAllowedPromptLength = prompt.length <= GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH;
103
+ if (isAllowedPromptLength) {
104
+ try {
105
+ const imageData = await attemptGeneration(prompt, defaultPrompt, 0, GENERATE_ACTIVITY_IMAGE_MAX_RETRIES, provider, providerApiKeys);
106
+ return {
107
+ fallback: false,
108
+ attempts: attempts ?? 0,
109
+ imageData
110
+ };
111
+ } catch {
112
+ const run = get_provider_default(provider, providerApiKeys);
113
+ const fallbackImageData = await run(defaultPrompt);
114
+ return {
115
+ imageData: fallbackImageData,
116
+ fallback: true,
117
+ attempts: GENERATE_ACTIVITY_IMAGE_MAX_RETRIES
118
+ };
119
+ }
120
+ } else {
121
+ const run = get_provider_default(provider, providerApiKeys);
122
+ const fallbackImageData = await run(defaultPrompt);
123
+ return {
124
+ imageData: fallbackImageData,
125
+ fallback: true,
126
+ attempts: GENERATE_ACTIVITY_IMAGE_MAX_RETRIES
127
+ };
128
+ }
129
+ };
130
+ var generate_activity_image_default = generateActivityImage;
131
+ export {
132
+ generate_activity_image_default as default,
133
+ GENERATE_ACTIVITY_IMAGE_MAX_RETRIES,
134
+ GENERATE_ACTIVITY_IMAGE_MAX_PROMPT_LENGTH
135
+ };
@@ -0,0 +1,20 @@
1
+ import { GenerateActivityImageProvider, GenerateActivityImageProviderApiKeys, GenerateActivityImageProviderName } from '../types';
2
+ /**
3
+ * Gets the configured image generation provider.
4
+ *
5
+ * Supported providers:
6
+ * - 'pollinations' (default): Free, unlimited, no authentication.
7
+ *
8
+ * @param {GenerateActivityImageProviderName} [providerName] - Image generation provider name (`pollinations` by default).
9
+ * @param {GenerateActivityImageProviderApiKeys} [providerApiKeys] - Optional provider API keys.
10
+ * @returns {GenerateActivityImageProvider} Image generation provider instance.
11
+ * @throws {Error} Throws if provider name is invalid.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const provider2 = getProvider('pollinations');
16
+ * ```
17
+ */
18
+ declare const getProvider: (providerName?: GenerateActivityImageProviderName, providerApiKeys?: GenerateActivityImageProviderApiKeys) => GenerateActivityImageProvider;
19
+ export default getProvider;
20
+ //# sourceMappingURL=get-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-provider.d.ts","sourceRoot":"","sources":["../../providers/get-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,oCAAoC,EACpC,iCAAiC,EAClC,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,WAAW,GACf,eAAc,iCAAkD,EAChE,kBAAkB,oCAAoC,KACrD,6BAaF,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { default as getProvider } from './get-provider';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,36 @@
1
+ export declare const BASE_URL = "https://gen.pollinations.ai/image/";
2
+ /**
3
+ * Pollinations model for image generation.
4
+ * Available models:
5
+ * - `flux` (best for illustrations, balanced quality).
6
+ * - `seedream` (excellent prompt understanding).
7
+ * - `gpt-image-large` (photorealism).
8
+ * - `kontext` (context-aware).
9
+ */
10
+ export declare const MODEL = "flux";
11
+ /**
12
+ * Prompt enhancer to improve image quality.
13
+ *
14
+ * Pollinations generates poor images by default.
15
+ * This enhancer helps achieve better results.
16
+ */
17
+ export declare const PROMPT_ENHANCER = "side view, natural human anatomy, natural and clear eyes, attractive face, proportional arms and legs, symmetrical joints, clean lines, soft natural lighting, professional illustration";
18
+ /**
19
+ * Negative prompt allows to avoid common image generation issues.
20
+ * Addresses: distorted faces, extra limbs, malformed hands, blurry images, low quality, and others.
21
+ * Pollinations generates poor images due to limited resources; negative prompts help mitigate this.
22
+ */
23
+ export declare const NEGATIVE_PROMPT = "distorted or warped eyes, twisted joints, distorted feet, unrealistic pose, distorted or warped faces, warped or distorted eyes, extra limbs, malformed or distorted hands, blurry, low quality, pixelated, ugly, deformed body, disfigured, bad or broken anatomy, bad proportions, extra fingers, missing fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, noise, extra limbs, cloned face, disfigured, gross proportions, malformed limbs, missing arms, missing legs, extra arms, extra legs, mutated hands, out of focus, long neck, long body, logo, watermark";
24
+ /**
25
+ * Base64 prefix for PNG images.
26
+ */
27
+ export declare const BASE_64_PREFIX = "data:image/png;base64,";
28
+ /**
29
+ * Image width in pixels.
30
+ */
31
+ export declare const IMAGE_WIDTH = 1024;
32
+ /**
33
+ * Image height in pixels.
34
+ */
35
+ export declare const IMAGE_HEIGHT = 1024;
36
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../providers/pollinations/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,uCAAuC,CAAC;AAE7D;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,SAAS,CAAC;AAE5B;;;;;GAKG;AACH,eAAO,MAAM,eAAe,6LACgK,CAAC;AAE7L;;;;GAIG;AACH,eAAO,MAAM,eAAe,ikBACoiB,CAAC;AAEjkB;;GAEG;AACH,eAAO,MAAM,cAAc,2BAA2B,CAAC;AAEvD;;GAEG;AACH,eAAO,MAAM,WAAW,OAAO,CAAC;AAEhC;;GAEG;AACH,eAAO,MAAM,YAAY,OAAO,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { default } from './pollinations';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../providers/pollinations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { GenerateActivityImageProvider } from '../../types';
2
+ /**
3
+ * Pollinations.ai image generation provider.
4
+ *
5
+ * Completely free, no authentication required.
6
+ * Uses negative prompts to avoid common image generation issues.
7
+ *
8
+ * Supports multiple models:
9
+ * - `flux` (default, best for illustrations)
10
+ * - `seedream`
11
+ * - `gpt-image-large`
12
+ * - `kontext`
13
+ *
14
+ * @param {string} prompt - Text prompt for image generation.
15
+ * @param {string} [apiKey] - Optional API key (not required for Pollinations).
16
+ * @returns {Promise<string>} Promise resolving to base64-encoded image data URL (`data:image/png;base64,...`).
17
+ * @throws {Error} Throws error if generation fails.
18
+ * @see {@link https://pollinations.ai | Pollinations.ai}
19
+ */
20
+ declare const pollinations: GenerateActivityImageProvider;
21
+ export default pollinations;
22
+ //# sourceMappingURL=pollinations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pollinations.d.ts","sourceRoot":"","sources":["../../../providers/pollinations/pollinations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AAwC5D;;;;;;;;;;;;;;;;;GAiBG;AACH,QAAA,MAAM,YAAY,EAAE,6BAqBnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Common interface for all image generation providers.
3
+ * Generates an image from a text prompt.
4
+ * @param {string} prompt - Text prompt for image generation.
5
+ * @param {string} [apiKey] - Optional API key for the provider (if required).
6
+ * @returns {Promise<string>} Promise resolving to base64-encoded image data URL (`data:image/png;base64,...`).
7
+ * @throws {Error} Throws error if generation fails.
8
+ */
9
+ export type GenerateActivityImageProvider = (prompt: string, apiKey?: string) => Promise<string>;
10
+ /**
11
+ * Supported image generation providers:
12
+ * - `pollinations`: Free, unlimited, no authentication.
13
+ */
14
+ export type GenerateActivityImageProviderName = 'pollinations';
15
+ export interface GenerateActivityImageProviderApiKeys {
16
+ pollinations?: string;
17
+ }
18
+ export interface GenerateActivityImageInput {
19
+ prompt: string;
20
+ defaultPrompt: string;
21
+ attempts?: number;
22
+ provider?: GenerateActivityImageProviderName;
23
+ providerApiKeys?: GenerateActivityImageProviderApiKeys;
24
+ }
25
+ export interface GenerateActivityImageOutput {
26
+ /** Base64-encoded image data URL. */
27
+ imageData: string;
28
+ /** Whether fallback was used. */
29
+ fallback: boolean;
30
+ /** Number of retry attempts performed. */
31
+ attempts: number;
32
+ }
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAGjG;;;GAGG;AACH,MAAM,MAAM,iCAAiC,GAAG,cAAc,CAAC;AAE/D,MAAM,WAAW,oCAAoC;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,iCAAiC,CAAC;IAC7C,eAAe,CAAC,EAAE,oCAAoC,CAAC;CACxD;AAED,MAAM,WAAW,2BAA2B;IAC1C,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@torqlab/generate-activity-image",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered image generator for athletic activities.",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "Bohdan Balov",
8
+ "email": "balovbohdan@gmail.com",
9
+ "url": "https://balov.dev"
10
+ },
11
+ "keywords": [
12
+ "ai",
13
+ "pollinations",
14
+ "torq"
15
+ ],
16
+ "type": "module",
17
+ "main": "dist/index.cjs",
18
+ "module": "dist/index.mjs",
19
+ "types": "dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.mjs",
24
+ "require": "./dist/index.cjs"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "engines": {
31
+ "node": "24.x"
32
+ },
33
+ "publishConfig": {
34
+ "registry": "https://registry.npmjs.org/",
35
+ "access": "public"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/torqlab/generate-activity-image.git"
40
+ },
41
+ "homepage": "https://github.com/torqlab/generate-activity-image",
42
+ "bugs": {
43
+ "url": "https://github.com/torqlab/generate-activity-image/issues"
44
+ },
45
+ "scripts": {
46
+ "build:types": "bunx tsc -p tsconfig.build.json",
47
+ "build:esm": "bun build ./index.ts --outfile dist/index.mjs --target node",
48
+ "build:cjs": "bun build ./index.ts --outfile dist/index.cjs --target node --format cjs",
49
+ "build": "bun run clean && bun run build:types && bun run build:esm && bun run build:cjs",
50
+ "test": "bun test",
51
+ "lint": "eslint . --ext .ts",
52
+ "format": "prettier --write .",
53
+ "format:check": "prettier --check .",
54
+ "prepare": "husky",
55
+ "prepublishOnly": "bun run build",
56
+ "clean": "rm -rf dist"
57
+ },
58
+ "devDependencies": {
59
+ "@eslint/js": "^9.15.0",
60
+ "@types/bun": "^1.1.13",
61
+ "@types/node": "^25.3.3",
62
+ "bun": "^1.1.38",
63
+ "eslint": "^9.15.0",
64
+ "eslint-config-prettier": "^9.1.0",
65
+ "eslint-plugin-jsdoc": "^50.5.0",
66
+ "husky": "^9.1.7",
67
+ "prettier": "^3.3.3",
68
+ "typescript": "^5.6.3",
69
+ "typescript-eslint": "^8.15.0"
70
+ }
71
+ }