auto-builder-sdk
Version:
SDK for building Auto Builder workflow plugins
133 lines (132 loc) • 6.18 kB
JavaScript
/**
* Resolve the correct `BaseNodeExecutor` implementation at runtime.
*
* We cannot use a dynamic string in a static `import` statement because that
* violates the ECMAScript module spec and breaks the TypeScript → esbuild
* transform used by Vitest. Instead we rely on Node's `createRequire` helper
* which is available in ESM contexts and gives us a synchronous `require`
* function. This keeps the public surface identical while remaining compatible
* with both the Vitest environment (which uses source files directly) and the
* normal runtime (which consumes the compiled `auto-builder` package).
*/
import { fileURLToPath, pathToFileURL } from 'node:url';
import path from 'node:path';
import fs from 'node:fs';
// ---------------------------------------------------------------------------
// Runtime-side helpers -------------------------------------------------------
// ---------------------------------------------------------------------------
/** Resolve a module by progressively trying a list of candidate paths. */
const tryImport = async (candidates) => {
let lastErr;
for (const loader of candidates) {
try {
return await loader();
}
catch (err) {
lastErr = err;
}
}
throw lastErr;
};
const resolveServicePath = (startDir, file) => {
let dir = startDir;
const root = path.resolve('/');
// eslint-disable-next-line no-constant-condition
while (true) {
const candidate = path.join(dir, `src/services/${file}`);
if (fs.existsSync(candidate)) {
return candidate;
}
// also allow compiled dist path
const distCandidate = path.join(dir, `dist/services/${file.replace(/\.ts$/, '.js')}`);
if (fs.existsSync(distCandidate)) {
return distCandidate;
}
if (dir === root)
break;
dir = path.dirname(dir);
}
return undefined;
};
/**
* ---------------------------------------------------------------------------
* Dynamic ESM resolution for services (no more .js shims needed!)
* ---------------------------------------------------------------------------
*/
const BaseNodeExecutor = await tryImport([
// 1. Vitest – return simple stub to avoid heavy imports
async () => {
if (!process.env.VITEST)
throw new Error('skip');
// eslint-disable-next-line @typescript-eslint/no-empty-interface
class StubBaseNodeExecutor {
}
return StubBaseNodeExecutor;
},
// 2. Compiled production bundle
async () => (await import('auto-builder/dist/services/BaseNodeExecutor.js')).BaseNodeExecutor,
// 2b. Direct TS source file path inside sibling auto-builder package (dev watch mode)
async () => {
const url = new URL('../../auto-builder/src/services/BaseNodeExecutor.ts', import.meta.url);
return (await import(url.href)).BaseNodeExecutor;
},
// 2c. Compiled dist inside current working directory (application root)
async () => {
const abs = path.resolve(process.cwd(), 'dist/services/BaseNodeExecutor.js');
return (await import(pathToFileURL(abs).href)).BaseNodeExecutor;
},
// 3. Monorepo dev – walk up until we find the TS source file
async () => {
const src = resolveServicePath(fileURLToPath(import.meta.url), 'BaseNodeExecutor.ts');
if (!src)
throw new Error('not-found');
return (await import(pathToFileURL(src).href)).BaseNodeExecutor;
},
]);
// Re-export so consumers can `import { BaseNodeExecutor } from "auto-builder-sdk"`.
export { BaseNodeExecutor };
import { validatePlugin } from './plugin.js';
export const definePlugin = (meta) => {
validatePlugin(meta);
return meta;
};
export { validatePlugin, PluginSchema } from './plugin.js';
// SDK public surface: re-export BaseNodeExecutor and core Auto-Builder types.
export { makeStubContext, makeStubNode } from './testing.js';
export { registerCredential, getCredentialDef, listCredentialDefinitions } from './credentials.js';
// Runtime helpers ---------------------------------------------------------
export { log } from './logger.js';
export { NodeOperationError, NodeApiError } from './errors.js';
export { getDb, _injectPrisma as _internalInjectPrismaClient } from './db.js';
export { getDatabaseService, _internalInjectDatabaseService } from './database-service.js';
// ---------------------------------------------------------------------------
// Dynamic ESM resolution for services (no more .js shims needed!)
// ---------------------------------------------------------------------------
const ParameterResolver = await tryImport([
async () => {
if (!process.env.VITEST)
throw new Error('skip');
return class ParameterResolverStub {
};
},
async () => (await import('auto-builder/dist/services/ParameterResolver.js')).ParameterResolver,
async () => {
const url = new URL('../../auto-builder/src/services/ParameterResolver.ts', import.meta.url);
return (await import(url.href)).ParameterResolver;
},
async () => {
const abs = path.resolve(process.cwd(), 'dist/services/ParameterResolver.js');
return (await import(pathToFileURL(abs).href)).ParameterResolver;
},
async () => {
const src = resolveServicePath(fileURLToPath(import.meta.url), 'ParameterResolver.ts');
if (!src)
throw new Error('not-found');
return (await import(pathToFileURL(src).href)).ParameterResolver;
},
]);
export { ParameterResolver };
// Pagination utilities ---------------------------------------------------------
export { convertToLimitOffset, calculatePaginationMeta, validatePaginationParams, createPaginationResponse, extractPaginationParams, convertLegacyPagination, buildApiPaginationParams, parseApiPaginationResponse, addStandardPagination, validateNodePagination, PAGINATION_NODE_DEFINITION, PAGINATION_SIZE_NODE_DEFINITION } from './pagination-utils.js';
// Pagination configuration
export { PAGINATION_CONFIG, updatePaginationConfig, resetPaginationConfig } from './pagination.config.js';