stable-diffusion-cpp-node-api 0.9.1

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 node-stable-diffusion-cpp contributors
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,166 @@
1
+ # node-stable-diffusion-cpp
2
+
3
+ > **WARNING: This library was vibe coded.** The entire codebase — C++ bindings, JS wrappers, build
4
+ > system, and documentation — was generated by an AI assistant in a single session. It compiles,
5
+ > passes basic smoke tests, and the API surface matches the upstream C library. However, it has
6
+ > **not** been battle-tested in production. Edge cases, memory leaks under unusual error paths, and
7
+ > platform-specific build quirks may exist. Use at your own risk, report issues, and read the source
8
+ > before shipping anything important.
9
+
10
+ Native Node.js bindings for [stable-diffusion.cpp](https://github.com/leejet/stable-diffusion.cpp) — a pure C/C++ implementation of Stable Diffusion, SDXL, SD3, FLUX, Wan video, and more. Built on N-API for ABI stability across Node.js versions. All heavy operations (model loading, image/video generation, upscaling) run on background threads and return Promises.
11
+
12
+ ## Features
13
+
14
+ - **Image generation** — txt2img, img2img, inpainting, ControlNet, PhotoMaker, LoRA
15
+ - **Video generation** — Wan2.1/2.2 text-to-video and image-to-video
16
+ - **Upscaling** — ESRGAN-based super resolution
17
+ - **Model conversion** — safetensors/ckpt to GGUF quantized formats
18
+ - **GPU auto-detection** — Metal (macOS), CUDA (Linux/Windows), Vulkan
19
+ - **Async everything** — context creation, generation, upscaling all return Promises
20
+ - **Callbacks** — log, progress, and preview callbacks bridged to the JS event loop
21
+ - **Electron-ready** — N-API v8 targets Node.js 18+ / Electron 22+
22
+ - **TypeScript** — full type declarations included
23
+
24
+ ## Requirements
25
+
26
+ - Node.js >= 18
27
+ - CMake >= 3.15
28
+ - C++17 compiler (Xcode, GCC 9+, MSVC 2019+)
29
+ - Platform SDK for GPU backend (Xcode for Metal, CUDA Toolkit for NVIDIA, Vulkan SDK)
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ git clone --recursive <your-repo-url>
35
+ cd node-stable-diffusion-cpp
36
+ npm install
37
+ ```
38
+
39
+ The `npm install` step compiles the native module. On macOS it automatically enables Metal; on systems with CUDA or Vulkan SDKs installed, those backends are enabled too.
40
+
41
+ ### Manual build
42
+
43
+ ```bash
44
+ npm run build # Release build
45
+ npm run build:debug # Debug build
46
+ npm run rebuild # Clean + rebuild
47
+ npm run clean # Remove build artifacts
48
+ ```
49
+
50
+ ## Quick start
51
+
52
+ ```javascript
53
+ const sd = require('node-stable-diffusion-cpp');
54
+ const fs = require('fs');
55
+
56
+ // Optional: log what the library is doing
57
+ sd.setLogCallback(({ level, text }) => {
58
+ if (level >= 1) process.stderr.write(text);
59
+ });
60
+
61
+ // Optional: track generation progress
62
+ sd.setProgressCallback(({ step, steps, time }) => {
63
+ process.stderr.write(`\rStep ${step}/${steps} (${time.toFixed(1)}s)`);
64
+ });
65
+
66
+ (async () => {
67
+ // Load a model (runs on background thread)
68
+ // Use modelPath for single-file models (ckpt, safetensors, full gguf).
69
+ // Use diffusionModelPath only for standalone diffusion-only component files.
70
+ const ctx = await sd.StableDiffusionContext.create({
71
+ modelPath: 'path/to/model.gguf',
72
+ });
73
+
74
+ // Generate an image
75
+ const images = await ctx.generateImage({
76
+ prompt: 'a photo of an astronaut riding a horse on mars',
77
+ width: 512,
78
+ height: 512,
79
+ sampleParams: {
80
+ sampleSteps: 20,
81
+ guidance: { txtCfg: 7.0 },
82
+ },
83
+ });
84
+
85
+ // Save raw RGB pixels (use stb_image or sharp to encode to PNG)
86
+ const img = images[0];
87
+ console.log(`\nGenerated ${img.width}x${img.height} image (${img.data.length} bytes)`);
88
+ fs.writeFileSync('output.raw', img.data);
89
+
90
+ ctx.close();
91
+ })();
92
+ ```
93
+
94
+ ## Smoke test
95
+
96
+ A ready-made script is included:
97
+
98
+ ```bash
99
+ node smoke.js path/to/model.gguf
100
+ ```
101
+
102
+ Edit the constants at the top of `smoke.js` to change prompt, dimensions, steps, etc.
103
+
104
+ ## API overview
105
+
106
+ | Function / Class | Description |
107
+ |---|---|
108
+ | `StableDiffusionContext.create(opts)` | Load a model, returns `Promise<StableDiffusionContext>` |
109
+ | `ctx.generateImage(opts)` | Text/image to image, returns `Promise<SdImage[]>` |
110
+ | `ctx.generateVideo(opts)` | Text/image to video frames, returns `Promise<SdImage[]>` |
111
+ | `ctx.getDefaultSampleMethod()` | Default sampler for loaded model |
112
+ | `ctx.getDefaultScheduler(method?)` | Default scheduler for sampler |
113
+ | `ctx.close()` | Free native resources |
114
+ | `UpscalerContext.create(opts)` | Load ESRGAN model, returns `Promise<UpscalerContext>` |
115
+ | `upscaler.upscale(image, factor?)` | Upscale an image, returns `Promise<SdImage>` |
116
+ | `convert(opts)` | Convert model format, returns `Promise<boolean>` |
117
+ | `preprocessCanny(image, opts?)` | Canny edge detection (sync, in-place) |
118
+ | `setLogCallback(fn)` | Set/clear log callback |
119
+ | `setProgressCallback(fn)` | Set/clear progress callback |
120
+ | `setPreviewCallback(fn, opts?)` | Set/clear preview callback |
121
+ | `getSystemInfo()` | Backend/hardware info string |
122
+ | `getNumPhysicalCores()` | CPU core count |
123
+ | `version()` / `commit()` | Library version and git commit |
124
+
125
+ See [docs/API.md](docs/API.md) for the full API reference with all options, types, and examples.
126
+
127
+ ## Supported models
128
+
129
+ Anything stable-diffusion.cpp supports:
130
+
131
+ - Stable Diffusion 1.x, 2.x
132
+ - SDXL, SDXL Turbo
133
+ - SD3, SD3.5
134
+ - FLUX.1 (dev, schnell)
135
+ - Wan2.1, Wan2.2 (video)
136
+ - Chroma, Qwen-Image
137
+ - LoRA, ControlNet, PhotoMaker
138
+ - GGUF quantized models (Q4_0, Q5_0, Q8_0, F16, etc.)
139
+
140
+ ## Image data format
141
+
142
+ All images are plain objects with raw pixel data:
143
+
144
+ ```typescript
145
+ interface SdImage {
146
+ width: number; // pixels
147
+ height: number; // pixels
148
+ channel: number; // 3 for RGB
149
+ data: Buffer; // raw RGB bytes, length = width * height * channel
150
+ }
151
+ ```
152
+
153
+ To save as PNG, use a library like [sharp](https://sharp.pixelplumbing.com/):
154
+
155
+ ```javascript
156
+ const sharp = require('sharp');
157
+ await sharp(img.data, { raw: { width: img.width, height: img.height, channels: img.channel } })
158
+ .png()
159
+ .toFile('output.png');
160
+ ```
161
+
162
+ ## License
163
+
164
+ MIT. See [LICENSE](LICENSE).
165
+
166
+ stable-diffusion.cpp is also MIT licensed. GGML is MIT licensed.
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "stable-diffusion-cpp-node-api",
3
+ "version": "0.9.1",
4
+ "description": "Node.js N-API bindings for stable-diffusion.cpp — run Stable Diffusion, SDXL, SD3, FLUX, and Wan video models natively",
5
+ "main": "src/js/index.js",
6
+ "types": "src/js/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/sir-ingvarr/node-stable-diffusion-cpp.git"
10
+ },
11
+ "license": "MIT",
12
+ "scripts": {
13
+ "preinstall": "test -d .git && git submodule update --init --recursive || true",
14
+ "build": "cmake-js compile --release",
15
+ "build:debug": "cmake-js compile --debug",
16
+ "rebuild": "cmake-js rebuild --release",
17
+ "clean": "cmake-js clean",
18
+ "test": "node test/basic.js",
19
+ "smoke": "node smoke.js"
20
+ },
21
+ "keywords": [
22
+ "stable-diffusion",
23
+ "sdxl",
24
+ "sd3",
25
+ "flux",
26
+ "wan",
27
+ "image-generation",
28
+ "video-generation",
29
+ "diffusion",
30
+ "ai",
31
+ "machine-learning",
32
+ "ggml",
33
+ "gguf",
34
+ "native",
35
+ "napi",
36
+ "electron"
37
+ ],
38
+ "files": [
39
+ "src/js/",
40
+ "LICENSE",
41
+ "README.md"
42
+ ],
43
+ "engines": {
44
+ "node": ">=18.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "cmake-js": "^8.0.0",
48
+ "node-addon-api": "^8.3.1"
49
+ },
50
+ "optionalDependencies": {
51
+ "@stable-diffusion-cpp-node-api/darwin-arm64": "0.9.1",
52
+ "@stable-diffusion-cpp-node-api/darwin-x64": "0.9.1",
53
+ "@stable-diffusion-cpp-node-api/linux-x64": "0.9.1",
54
+ "@stable-diffusion-cpp-node-api/win32-x64": "0.9.1"
55
+ }
56
+ }
@@ -0,0 +1,345 @@
1
+ /// <reference types="node" />
2
+
3
+ // --- Enum string literal types ---
4
+
5
+ export type SampleMethod =
6
+ | 'euler'
7
+ | 'euler_a'
8
+ | 'heun'
9
+ | 'dpm2'
10
+ | 'dpm++2s_a'
11
+ | 'dpm++2m'
12
+ | 'dpm++2mv2'
13
+ | 'ipndm'
14
+ | 'ipndm_v'
15
+ | 'lcm'
16
+ | 'ddim_trailing'
17
+ | 'tcd'
18
+ | 'res_multistep'
19
+ | 'res_2s';
20
+
21
+ export type Scheduler =
22
+ | 'discrete'
23
+ | 'karras'
24
+ | 'exponential'
25
+ | 'ays'
26
+ | 'gits'
27
+ | 'sgm_uniform'
28
+ | 'simple'
29
+ | 'smoothstep'
30
+ | 'kl_optimal'
31
+ | 'lcm'
32
+ | 'bong_tangent';
33
+
34
+ export type SdType =
35
+ | 'f32'
36
+ | 'f16'
37
+ | 'q4_0'
38
+ | 'q4_1'
39
+ | 'q5_0'
40
+ | 'q5_1'
41
+ | 'q8_0'
42
+ | 'q8_1'
43
+ | 'q2_k'
44
+ | 'q3_k'
45
+ | 'q4_k'
46
+ | 'q5_k'
47
+ | 'q6_k'
48
+ | 'q8_k'
49
+ | 'iq2_xxs'
50
+ | 'iq2_xs'
51
+ | 'iq3_xxs'
52
+ | 'iq1_s'
53
+ | 'iq4_nl'
54
+ | 'iq3_s'
55
+ | 'iq2_s'
56
+ | 'iq4_xs'
57
+ | 'i8'
58
+ | 'i16'
59
+ | 'i32'
60
+ | 'i64'
61
+ | 'f64'
62
+ | 'iq1_m'
63
+ | 'bf16'
64
+ | 'tq1_0'
65
+ | 'tq2_0'
66
+ | 'mxfp4';
67
+
68
+ export type RngType = 'std_default' | 'cuda' | 'cpu';
69
+
70
+ export type Prediction = 'eps' | 'v' | 'edm_v' | 'flow' | 'flux_flow' | 'flux2_flow';
71
+
72
+ export type PreviewMode = 'none' | 'proj' | 'tae' | 'vae';
73
+
74
+ export type LogLevel = 0 | 1 | 2 | 3; // debug, info, warn, error
75
+
76
+ export type LoraApplyMode = 'auto' | 'immediately' | 'at_runtime';
77
+
78
+ export type CacheMode = 'disabled' | 'easycache' | 'ucache' | 'dbcache' | 'taylorseer' | 'cachedit';
79
+
80
+ // --- Data interfaces ---
81
+
82
+ export interface SdImage {
83
+ width: number;
84
+ height: number;
85
+ channel: number;
86
+ data: Buffer;
87
+ }
88
+
89
+ export interface LoraDefinition {
90
+ path: string;
91
+ multiplier?: number;
92
+ isHighNoise?: boolean;
93
+ }
94
+
95
+ export interface EmbeddingDefinition {
96
+ name: string;
97
+ path: string;
98
+ }
99
+
100
+ export interface SlgParams {
101
+ layers?: number[];
102
+ layerStart?: number;
103
+ layerEnd?: number;
104
+ scale?: number;
105
+ }
106
+
107
+ export interface GuidanceParams {
108
+ txtCfg?: number;
109
+ imgCfg?: number;
110
+ distilledGuidance?: number;
111
+ slg?: SlgParams;
112
+ }
113
+
114
+ export interface SampleParams {
115
+ guidance?: GuidanceParams;
116
+ scheduler?: Scheduler;
117
+ sampleMethod?: SampleMethod;
118
+ sampleSteps?: number;
119
+ eta?: number;
120
+ shiftedTimestep?: number;
121
+ flowShift?: number;
122
+ customSigmas?: number[];
123
+ }
124
+
125
+ export interface CacheParams {
126
+ mode?: CacheMode;
127
+ reuseThreshold?: number;
128
+ startPercent?: number;
129
+ endPercent?: number;
130
+ errorDecayRate?: number;
131
+ useRelativeThreshold?: boolean;
132
+ resetErrorOnCompute?: boolean;
133
+ fnComputeBlocks?: number;
134
+ bnComputeBlocks?: number;
135
+ residualDiffThreshold?: number;
136
+ maxWarmupSteps?: number;
137
+ maxCachedSteps?: number;
138
+ maxContinuousCachedSteps?: number;
139
+ taylorseerNDerivatives?: number;
140
+ taylorseerSkipInterval?: number;
141
+ scmPolicyDynamic?: boolean;
142
+ }
143
+
144
+ export interface TilingParams {
145
+ enabled?: boolean;
146
+ tileSizeX?: number;
147
+ tileSizeY?: number;
148
+ targetOverlap?: number;
149
+ relSizeX?: number;
150
+ relSizeY?: number;
151
+ }
152
+
153
+ export interface PhotoMakerParams {
154
+ idImages?: SdImage[];
155
+ idEmbedPath?: string;
156
+ styleStrength?: number;
157
+ }
158
+
159
+ // --- Abort support ---
160
+
161
+ export interface AbortableOptions {
162
+ signal?: AbortSignal;
163
+ }
164
+
165
+ // --- Context creation options ---
166
+
167
+ export interface ContextOptions extends AbortableOptions {
168
+ modelPath?: string;
169
+ clipLPath?: string;
170
+ clipGPath?: string;
171
+ clipVisionPath?: string;
172
+ t5xxlPath?: string;
173
+ llmPath?: string;
174
+ llmVisionPath?: string;
175
+ diffusionModelPath?: string;
176
+ highNoiseDiffusionModelPath?: string;
177
+ vaePath?: string;
178
+ taesdPath?: string;
179
+ controlNetPath?: string;
180
+ photoMakerPath?: string;
181
+ tensorTypeRules?: string;
182
+ embeddings?: EmbeddingDefinition[];
183
+ vaeDecodeOnly?: boolean;
184
+ freeParamsImmediately?: boolean;
185
+ nThreads?: number;
186
+ wtype?: SdType;
187
+ rngType?: RngType;
188
+ samplerRngType?: RngType;
189
+ prediction?: Prediction;
190
+ loraApplyMode?: LoraApplyMode;
191
+ offloadParamsToCpu?: boolean;
192
+ enableMmap?: boolean;
193
+ keepClipOnCpu?: boolean;
194
+ keepControlNetOnCpu?: boolean;
195
+ keepVaeOnCpu?: boolean;
196
+ flashAttn?: boolean;
197
+ diffusionFlashAttn?: boolean;
198
+ taePreviewOnly?: boolean;
199
+ diffusionConvDirect?: boolean;
200
+ vaeConvDirect?: boolean;
201
+ circularX?: boolean;
202
+ circularY?: boolean;
203
+ forceSdxlVaeConvScale?: boolean;
204
+ chromaUseDitMask?: boolean;
205
+ chromaUseT5Mask?: boolean;
206
+ chromaT5MaskPad?: number;
207
+ qwenImageZeroCondT?: boolean;
208
+ }
209
+
210
+ // --- Generation options ---
211
+
212
+ export interface ImageGenerationOptions extends AbortableOptions {
213
+ prompt?: string;
214
+ negativePrompt?: string;
215
+ clipSkip?: number;
216
+ initImage?: SdImage;
217
+ refImages?: SdImage[];
218
+ autoResizeRefImage?: boolean;
219
+ increaseRefIndex?: boolean;
220
+ maskImage?: SdImage;
221
+ width?: number;
222
+ height?: number;
223
+ sampleParams?: SampleParams;
224
+ strength?: number;
225
+ seed?: number;
226
+ batchCount?: number;
227
+ controlImage?: SdImage;
228
+ controlStrength?: number;
229
+ photoMaker?: PhotoMakerParams;
230
+ vaeTiling?: TilingParams;
231
+ cache?: CacheParams;
232
+ loras?: LoraDefinition[];
233
+ }
234
+
235
+ export interface VideoGenerationOptions extends AbortableOptions {
236
+ prompt?: string;
237
+ negativePrompt?: string;
238
+ clipSkip?: number;
239
+ initImage?: SdImage;
240
+ endImage?: SdImage;
241
+ controlFrames?: SdImage[];
242
+ width?: number;
243
+ height?: number;
244
+ sampleParams?: SampleParams;
245
+ highNoiseSampleParams?: SampleParams;
246
+ moeBoundary?: number;
247
+ strength?: number;
248
+ seed?: number;
249
+ videoFrames?: number;
250
+ vaceStrength?: number;
251
+ vaeTiling?: TilingParams;
252
+ cache?: CacheParams;
253
+ loras?: LoraDefinition[];
254
+ }
255
+
256
+ export interface UpscalerOptions extends AbortableOptions {
257
+ esrganPath: string;
258
+ offloadParamsToCpu?: boolean;
259
+ direct?: boolean;
260
+ nThreads?: number;
261
+ tileSize?: number;
262
+ }
263
+
264
+ export interface UpscaleOptions extends AbortableOptions {}
265
+
266
+ export interface ConvertOptions extends AbortableOptions {
267
+ inputPath: string;
268
+ outputPath: string;
269
+ vaePath?: string;
270
+ outputType?: SdType;
271
+ tensorTypeRules?: string;
272
+ convertName?: boolean;
273
+ }
274
+
275
+ export interface CannyOptions {
276
+ highThreshold?: number;
277
+ lowThreshold?: number;
278
+ weak?: number;
279
+ strong?: number;
280
+ inverse?: boolean;
281
+ }
282
+
283
+ export interface PreviewOptions {
284
+ mode?: PreviewMode;
285
+ interval?: number;
286
+ denoised?: boolean;
287
+ noisy?: boolean;
288
+ }
289
+
290
+ // --- Callback data types ---
291
+
292
+ export interface LogCallbackData {
293
+ level: LogLevel;
294
+ text: string;
295
+ }
296
+
297
+ export interface ProgressCallbackData {
298
+ step: number;
299
+ steps: number;
300
+ time: number;
301
+ }
302
+
303
+ export interface PreviewCallbackData {
304
+ step: number;
305
+ isNoisy: boolean;
306
+ frames: SdImage[];
307
+ }
308
+
309
+ // --- Classes ---
310
+
311
+ export class StableDiffusionContext {
312
+ private constructor();
313
+ static create(options: ContextOptions): Promise<StableDiffusionContext>;
314
+ generateImage(options: ImageGenerationOptions): Promise<SdImage[]>;
315
+ generateVideo(options: VideoGenerationOptions): Promise<SdImage[]>;
316
+ getDefaultSampleMethod(): SampleMethod;
317
+ getDefaultScheduler(sampleMethod?: SampleMethod): Scheduler;
318
+ close(): void;
319
+ readonly isClosed: boolean;
320
+ }
321
+
322
+ export class UpscalerContext {
323
+ private constructor();
324
+ static create(options: UpscalerOptions): Promise<UpscalerContext>;
325
+ upscale(image: SdImage, upscaleFactor?: number, options?: UpscaleOptions): Promise<SdImage>;
326
+ getUpscaleFactor(): number;
327
+ close(): void;
328
+ readonly isClosed: boolean;
329
+ }
330
+
331
+ // --- Free functions ---
332
+
333
+ export function abort(): void;
334
+ export function convert(options: ConvertOptions): Promise<boolean>;
335
+ export function preprocessCanny(image: SdImage, options?: CannyOptions): boolean;
336
+ export function setLogCallback(callback: ((data: LogCallbackData) => void) | null): void;
337
+ export function setProgressCallback(callback: ((data: ProgressCallbackData) => void) | null): void;
338
+ export function setPreviewCallback(
339
+ callback: ((data: PreviewCallbackData) => void) | null,
340
+ options?: PreviewOptions,
341
+ ): void;
342
+ export function getSystemInfo(): string;
343
+ export function getNumPhysicalCores(): number;
344
+ export function version(): string;
345
+ export function commit(): string;
@@ -0,0 +1,170 @@
1
+ const loadBinding = require('./load-bindings');
2
+
3
+ const native = loadBinding();
4
+
5
+ function withAbortSignal(promise, signal) {
6
+ if (!signal) return promise;
7
+ if (signal.aborted) {
8
+ native.abort();
9
+ return Promise.reject(signal.reason ?? new DOMException('The operation was aborted.', 'AbortError'));
10
+ }
11
+ return new Promise((resolve, reject) => {
12
+ const onAbort = () => {
13
+ native.abort();
14
+ reject(signal.reason ?? new DOMException('The operation was aborted.', 'AbortError'));
15
+ };
16
+ signal.addEventListener('abort', onAbort, { once: true });
17
+ promise.then(
18
+ (v) => { signal.removeEventListener('abort', onAbort); resolve(v); },
19
+ (e) => { signal.removeEventListener('abort', onAbort); reject(e); },
20
+ );
21
+ });
22
+ }
23
+
24
+ class StableDiffusionContext {
25
+ #native;
26
+
27
+ /** @internal — use StableDiffusionContext.create() */
28
+ constructor(nativeCtx) {
29
+ this.#native = nativeCtx;
30
+ }
31
+
32
+ /**
33
+ * Create a new StableDiffusion context asynchronously.
34
+ * Model loading happens on a background thread.
35
+ * @param {object} options
36
+ * @returns {Promise<StableDiffusionContext>}
37
+ */
38
+ static async create(options) {
39
+ const { signal, ...opts } = options || {};
40
+ const ctx = await withAbortSignal(native.StableDiffusionContext.create(opts), signal);
41
+ return new StableDiffusionContext(ctx);
42
+ }
43
+
44
+ /**
45
+ * Generate images from a text prompt.
46
+ * @param {object} options
47
+ * @returns {Promise<Array<{width: number, height: number, channel: number, data: Buffer}>>}
48
+ */
49
+ async generateImage(options) {
50
+ const { signal, ...opts } = options || {};
51
+ return withAbortSignal(this.#native.generateImage(opts), signal);
52
+ }
53
+
54
+ /**
55
+ * Generate video frames from a text prompt.
56
+ * @param {object} options
57
+ * @returns {Promise<Array<{width: number, height: number, channel: number, data: Buffer}>>}
58
+ */
59
+ async generateVideo(options) {
60
+ const { signal, ...opts } = options || {};
61
+ return withAbortSignal(this.#native.generateVideo(opts), signal);
62
+ }
63
+
64
+ /**
65
+ * Get the default sample method for this model.
66
+ * @returns {string}
67
+ */
68
+ getDefaultSampleMethod() {
69
+ return this.#native.getDefaultSampleMethod();
70
+ }
71
+
72
+ /**
73
+ * Get the default scheduler for the given sample method.
74
+ * @param {string} [sampleMethod]
75
+ * @returns {string}
76
+ */
77
+ getDefaultScheduler(sampleMethod) {
78
+ return this.#native.getDefaultScheduler(sampleMethod);
79
+ }
80
+
81
+ /**
82
+ * Free the native context resources.
83
+ */
84
+ close() {
85
+ this.#native.close();
86
+ }
87
+
88
+ /**
89
+ * Whether the context has been closed.
90
+ * @returns {boolean}
91
+ */
92
+ get isClosed() {
93
+ return this.#native.isClosed;
94
+ }
95
+ }
96
+
97
+ class UpscalerContext {
98
+ #native;
99
+
100
+ /** @internal — use UpscalerContext.create() */
101
+ constructor(nativeCtx) {
102
+ this.#native = nativeCtx;
103
+ }
104
+
105
+ /**
106
+ * Create a new upscaler context asynchronously.
107
+ * @param {object} options
108
+ * @returns {Promise<UpscalerContext>}
109
+ */
110
+ static async create(options) {
111
+ const { signal, ...opts } = options || {};
112
+ const ctx = await withAbortSignal(native.UpscalerContext.create(opts), signal);
113
+ return new UpscalerContext(ctx);
114
+ }
115
+
116
+ /**
117
+ * Upscale an image.
118
+ * @param {{width: number, height: number, channel: number, data: Buffer}} image
119
+ * @param {number} [upscaleFactor=4]
120
+ * @param {{ signal?: AbortSignal }} [options]
121
+ * @returns {Promise<{width: number, height: number, channel: number, data: Buffer}>}
122
+ */
123
+ async upscale(image, upscaleFactor = 4, options) {
124
+ const { signal } = options || {};
125
+ return withAbortSignal(this.#native.upscale(image, upscaleFactor), signal);
126
+ }
127
+
128
+ /**
129
+ * Get the upscale factor for this model.
130
+ * @returns {number}
131
+ */
132
+ getUpscaleFactor() {
133
+ return this.#native.getUpscaleFactor();
134
+ }
135
+
136
+ /**
137
+ * Free the native context resources.
138
+ */
139
+ close() {
140
+ this.#native.close();
141
+ }
142
+
143
+ /**
144
+ * Whether the context has been closed.
145
+ * @returns {boolean}
146
+ */
147
+ get isClosed() {
148
+ return this.#native.isClosed;
149
+ }
150
+ }
151
+
152
+ module.exports = {
153
+ StableDiffusionContext,
154
+ UpscalerContext,
155
+
156
+ // Free functions
157
+ convert(options) {
158
+ const { signal, ...opts } = options || {};
159
+ return withAbortSignal(native.convert(opts), signal);
160
+ },
161
+ preprocessCanny: native.preprocessCanny,
162
+ setLogCallback: native.setLogCallback,
163
+ setProgressCallback: native.setProgressCallback,
164
+ setPreviewCallback: native.setPreviewCallback,
165
+ abort: native.abort,
166
+ getSystemInfo: native.getSystemInfo,
167
+ getNumPhysicalCores: native.getNumPhysicalCores,
168
+ version: native.version,
169
+ commit: native.commit,
170
+ };
@@ -0,0 +1,40 @@
1
+ const path = require('path');
2
+
3
+ const PLATFORMS = {
4
+ 'darwin-arm64': '@stable-diffusion-cpp-node-api/darwin-arm64',
5
+ 'darwin-x64': '@stable-diffusion-cpp-node-api/darwin-x64',
6
+ 'linux-x64': '@stable-diffusion-cpp-node-api/linux-x64',
7
+ 'win32-x64': '@stable-diffusion-cpp-node-api/win32-x64',
8
+ };
9
+
10
+ let binding;
11
+
12
+ function loadBinding() {
13
+ if (binding) return binding;
14
+
15
+ const key = `${process.platform}-${process.arch}`;
16
+ const pkg = PLATFORMS[key];
17
+
18
+ // 1. Try platform-specific npm package
19
+ if (pkg) {
20
+ try {
21
+ binding = require(pkg);
22
+ return binding;
23
+ } catch (_) {}
24
+ }
25
+
26
+ // 2. Fallback to local build (development / unsupported platform)
27
+ try {
28
+ binding = require(path.resolve(__dirname, '..', '..', 'build', 'Release', 'node_stable_diffusion.node'));
29
+ return binding;
30
+ } catch (_) {}
31
+
32
+ throw new Error(
33
+ `stable-diffusion-cpp-node-api: no prebuilt binary for ${key}.\n` +
34
+ 'Supported platforms: ' + Object.keys(PLATFORMS).join(', ') + '.\n' +
35
+ 'To build from source, install from git instead:\n' +
36
+ ' npm install git+https://github.com/sir-ingvarr/node-stable-diffusion-cpp.git'
37
+ );
38
+ }
39
+
40
+ module.exports = loadBinding;