New:Socket for Asana Is Now Available.Learn more
Get Started

@glbforge/meshy

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@glbforge/meshy - npm Package Compare versions

Comparing version
0.3.0
to
0.4.0
+51
dist/fal.d.ts
/**
* fal.ai client for open-weight image-to-3D models (Hunyuan3D, TRELLIS,
* TripoSR). Same queue-poll-download shape as the Meshy client, so the
* worker and studio treat every generation provider identically.
*
* Schema tolerance: fal model outputs differ per model and drift over
* time, so results are deep-scanned for the first .glb URL instead of
* hardcoding response paths.
*/
export declare const FAL_MODELS: {
/** Tencent Hunyuan3D 2 — highest quality of the open trio. */
readonly hunyuan: "fal-ai/hunyuan3d/v2";
/** Microsoft TRELLIS — strong quality/speed balance. */
readonly trellis: "fal-ai/trellis";
/** TripoSR — fastest and cheapest, lighter on detail. */
readonly triposr: "fal-ai/triposr";
};
export type FalModelKey = keyof typeof FAL_MODELS;
/** Queue routes use the root app id: 'fal-ai/hunyuan3d/v2' submits on the
* full path but polls on 'fal-ai/hunyuan3d'. */
export declare function rootAppId(model: string): string;
export declare class FalError extends Error {
readonly status?: number | undefined;
constructor(message: string, status?: number | undefined);
}
export interface FalClientOptions {
/** Defaults to process.env.FAL_KEY. */
apiKey?: string;
baseUrl?: string;
fetch?: typeof globalThis.fetch;
}
/** Depth-first scan for the first URL ending in .glb anywhere in a payload. */
export declare function findGlbUrl(value: unknown): string | null;
export declare class FalClient {
private readonly apiKey;
private readonly baseUrl;
private readonly fetch;
constructor(opts?: FalClientOptions);
private request;
/** Submit an image (data URI or URL). Returns the queue request id. */
submit(model: string, imageUrl: string, opts?: {
textured?: boolean;
}): Promise<string>;
status(model: string, requestId: string): Promise<{
status: 'IN_QUEUE' | 'IN_PROGRESS' | 'COMPLETED' | string;
queuePosition: number | null;
}>;
/** Fetch the finished result and extract its GLB URL. */
resultGlbUrl(model: string, requestId: string): Promise<string>;
downloadGlb(url: string): Promise<Uint8Array>;
}
/**
* fal.ai client for open-weight image-to-3D models (Hunyuan3D, TRELLIS,
* TripoSR). Same queue-poll-download shape as the Meshy client, so the
* worker and studio treat every generation provider identically.
*
* Schema tolerance: fal model outputs differ per model and drift over
* time, so results are deep-scanned for the first .glb URL instead of
* hardcoding response paths.
*/
export const FAL_MODELS = {
/** Tencent Hunyuan3D 2 — highest quality of the open trio. */
hunyuan: 'fal-ai/hunyuan3d/v2',
/** Microsoft TRELLIS — strong quality/speed balance. */
trellis: 'fal-ai/trellis',
/** TripoSR — fastest and cheapest, lighter on detail. */
triposr: 'fal-ai/triposr',
};
/** Queue routes use the root app id: 'fal-ai/hunyuan3d/v2' submits on the
* full path but polls on 'fal-ai/hunyuan3d'. */
export function rootAppId(model) {
return model.split('/').slice(0, 2).join('/');
}
export class FalError extends Error {
status;
constructor(message, status) {
super(message);
this.status = status;
this.name = 'FalError';
}
}
/** Depth-first scan for the first URL ending in .glb anywhere in a payload. */
export function findGlbUrl(value) {
if (typeof value === 'string') {
return /^https?:\/\/\S+\.glb(\?\S*)?$/i.test(value) ? value : null;
}
if (Array.isArray(value)) {
for (const item of value) {
const hit = findGlbUrl(item);
if (hit)
return hit;
}
return null;
}
if (value && typeof value === 'object') {
for (const key of Object.keys(value)) {
const hit = findGlbUrl(value[key]);
if (hit)
return hit;
}
}
return null;
}
export class FalClient {
apiKey;
baseUrl;
fetch;
constructor(opts = {}) {
const apiKey = opts.apiKey ?? process.env.FAL_KEY;
if (!apiKey) {
throw new FalError('Missing fal.ai API key. Set FAL_KEY (keys: https://fal.ai/dashboard/keys) ' +
'or pass { apiKey } explicitly.');
}
this.apiKey = apiKey;
this.baseUrl = (opts.baseUrl ?? 'https://queue.fal.run').replace(/\/$/, '');
this.fetch = opts.fetch ?? globalThis.fetch;
}
async request(method, path, body) {
const res = await this.fetch(this.baseUrl + path, {
method,
headers: {
authorization: `Key ${this.apiKey}`,
...(body !== undefined && { 'content-type': 'application/json' }),
},
body: body !== undefined ? JSON.stringify(body) : undefined,
});
const text = await res.text();
let json;
try {
json = text ? JSON.parse(text) : undefined;
}
catch {
json = text;
}
if (!res.ok) {
const detail = json?.detail;
throw new FalError(typeof detail === 'string' ? detail : `fal ${method} ${path} failed with HTTP ${res.status}`, res.status);
}
return json;
}
/** Submit an image (data URI or URL). Returns the queue request id. */
async submit(model, imageUrl, opts = {}) {
// Models disagree on input keys; send the common aliases together —
// unknown keys are ignored. Hunyuan's paint stage only runs when
// textured_mesh is set; TRELLIS/TripoSR texture natively.
const textured = opts.textured !== false;
const res = await this.request('POST', `/${model}`, {
image_url: imageUrl,
input_image_url: imageUrl,
input_image_urls: [imageUrl],
textured_mesh: textured,
texture: textured,
});
if (!res.request_id)
throw new FalError('fal did not return a request id');
return res.request_id;
}
async status(model, requestId) {
const res = await this.request('GET', `/${rootAppId(model)}/requests/${requestId}/status`);
return { status: res.status, queuePosition: res.queue_position ?? null };
}
/** Fetch the finished result and extract its GLB URL. */
async resultGlbUrl(model, requestId) {
const res = await this.request('GET', `/${rootAppId(model)}/requests/${requestId}`);
const url = findGlbUrl(res);
if (!url) {
throw new FalError(`No .glb in the ${model} result — the model may output a different format. ` +
`Top-level keys: ${Object.keys(res ?? {}).join(', ')}`);
}
return url;
}
async downloadGlb(url) {
const res = await this.fetch(url);
if (!res.ok)
throw new FalError(`model download failed: HTTP ${res.status}`, res.status);
return new Uint8Array(await res.arrayBuffer());
}
}
+1
-0
export * from './types.js';
export { MeshyClient, MeshyError, type MeshyClientOptions } from './client.js';
export { FalClient, FalError, FAL_MODELS, findGlbUrl, rootAppId, type FalModelKey, type FalClientOptions } from './fal.js';
export * from './types.js';
export { MeshyClient, MeshyError } from './client.js';
export { FalClient, FalError, FAL_MODELS, findGlbUrl, rootAppId } from './fal.js';
+1
-1
{
"name": "@glbforge/meshy",
"version": "0.3.0",
"version": "0.4.0",
"type": "module",

@@ -5,0 +5,0 @@ "main": "dist/index.js",