@base44-preview/cli 0.0.37-pr.364.15494ed → 0.0.37-pr.364.45ad860
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/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js.map
CHANGED
|
@@ -979,7 +979,7 @@
|
|
|
979
979
|
"import { join } from \"node:path\";\nimport { PROJECT_SUBDIR, TYPES_OUTPUT_SUBDIR } from \"@/core/consts.js\";\nimport { pathExists, readJsonFile, writeJsonFile } from \"@/core/utils/fs.js\";\n\nconst TYPES_INCLUDE_PATH = `${PROJECT_SUBDIR}/${TYPES_OUTPUT_SUBDIR}/*.d.ts`;\n\n/**\n * Update project configuration files after generating types.\n * Currently handles:\n * - tsconfig.json: adds base44/.types to the include array\n *\n * @returns true if tsconfig.json was updated, false otherwise\n */\nexport async function updateProjectConfig(\n projectRoot: string,\n): Promise<boolean> {\n const tsconfigPath = join(projectRoot, \"tsconfig.json\");\n\n if (!(await pathExists(tsconfigPath))) {\n return false;\n }\n\n try {\n const tsconfig = (await readJsonFile(tsconfigPath)) as {\n include?: string[];\n };\n\n // Ensure include array exists\n if (!tsconfig.include) {\n tsconfig.include = [];\n }\n\n // Check if already included\n if (tsconfig.include.includes(TYPES_INCLUDE_PATH)) {\n return false;\n }\n\n // Add to include array\n tsconfig.include.push(TYPES_INCLUDE_PATH);\n await writeJsonFile(tsconfigPath, tsconfig);\n return true;\n } catch {\n // If we can't parse or update, silently fail and let user configure manually\n return false;\n }\n}\n",
|
|
980
980
|
"import { Command } from \"commander\";\nimport type { CLIContext } from \"@/cli/types.js\";\nimport { runCommand, runTask } from \"@/cli/utils/index.js\";\nimport type { RunCommandResult } from \"@/cli/utils/runCommand.js\";\nimport { readProjectConfig } from \"@/core/index.js\";\nimport { generateTypesFile, updateProjectConfig } from \"@/core/types/index.js\";\n\nconst TYPES_FILE_PATH = \"base44/.types/types.d.ts\";\n\nasync function generateTypesAction(): Promise<RunCommandResult> {\n const { entities, functions, agents, connectors, project } =\n await readProjectConfig();\n\n await runTask(\"Generating types\", async () => {\n await generateTypesFile({ entities, functions, agents, connectors });\n });\n\n const tsconfigUpdated = await updateProjectConfig(project.root);\n\n return {\n outroMessage: tsconfigUpdated\n ? `Generated ${TYPES_FILE_PATH} and updated tsconfig.json`\n : `Generated ${TYPES_FILE_PATH}`,\n };\n}\n\nexport function getTypesGenerateCommand(context: CLIContext): Command {\n return new Command(\"generate\")\n .description(\n \"Generate TypeScript declaration file (types.d.ts) from project resources\",\n )\n .action(async () => {\n await runCommand(\n () => generateTypesAction(),\n { requireAuth: false },\n context,\n );\n });\n}\n",
|
|
981
981
|
"import { Command } from \"commander\";\nimport type { CLIContext } from \"@/cli/types.js\";\nimport { getTypesGenerateCommand } from \"./generate.js\";\n\nexport function getTypesCommand(context: CLIContext): Command {\n return new Command(\"types\")\n .description(\"Manage TypeScript type generation\")\n .addCommand(getTypesGenerateCommand(context));\n}\n",
|
|
982
|
-
"import type { Server } from \"node:http\";\nimport { dirname, join } from \"node:path\";\nimport { log as clackLog } from \"@clack/prompts\";\nimport cors from \"cors\";\nimport express from \"express\";\nimport getPort from \"get-port\";\nimport { createProxyMiddleware } from \"http-proxy-middleware\";\nimport { dir } from \"tmp-promise\";\nimport { createDevLogger } from \"@/cli/dev/createDevLogger.js\";\nimport { FunctionManager } from \"@/cli/dev/dev-server/function-manager.js\";\nimport { createFunctionRouter } from \"@/cli/dev/dev-server/routes/functions.js\";\nimport type { ProjectData } from \"@/core/project/types.js\";\nimport { Database } from \"./database.js\";\nimport {\n type BroadcastEntityEvent,\n broadcastEntityEvent,\n createRealtimeServer,\n} from \"./realtime.js\";\nimport { createEntityRoutes } from \"./routes/entities.js\";\nimport {\n createCustomIntegrationRoutes,\n createIntegrationRoutes,\n} from \"./routes/integrations.js\";\nimport { WatchBase44 } from \"./watcher.js\";\n\nconst DEFAULT_PORT = 4400;\nconst BASE44_APP_URL = \"https://base44.app\";\n\ninterface DevServerOptions {\n port?: number;\n loadResources: () => Promise<{\n functions: ProjectData[\"functions\"];\n entities: ProjectData[\"entities\"];\n project: ProjectData[\"project\"];\n }>;\n}\n\ninterface DevServerResult {\n port: number;\n server: Server;\n}\n\nexport async function createDevServer(\n options: DevServerOptions,\n): Promise<DevServerResult> {\n const { port: userPort } = options;\n const port = userPort ?? (await getPort({ port: DEFAULT_PORT }));\n const baseUrl = `http://localhost:${port}`;\n\n const { functions, entities, project } = await options.loadResources();\n\n const app = express();\n\n const remoteProxy = createProxyMiddleware({\n target: BASE44_APP_URL,\n changeOrigin: true,\n });\n\n app.use(\n cors({\n origin: /^http:\\/\\/localhost(:\\d+)?$/,\n credentials: true,\n }),\n );\n\n // Redirect OAuth routes to base44.app directly — proxying breaks the\n // redirect flow and session cookies set by the provider.\n const AUTH_ROUTE_PATTERN = /^\\/api\\/apps\\/auth(\\/|$)/;\n app.use((req, res, next) => {\n if (AUTH_ROUTE_PATTERN.test(req.path)) {\n const targetUrl = new URL(req.originalUrl, BASE44_APP_URL);\n return res.redirect(targetUrl.toString());\n }\n next();\n });\n\n const devLogger = createDevLogger();\n\n const functionManager = new FunctionManager(functions, devLogger);\n const functionRoutes = createFunctionRouter(functionManager, devLogger);\n app.use(\"/api/apps/:appId/functions\", functionRoutes);\n\n if (functionManager.getFunctionNames().length > 0) {\n clackLog.info(\n `Loaded functions: ${functionManager.getFunctionNames().join(\", \")}`,\n );\n }\n\n const db = new Database();\n db.load(entities);\n if (db.getCollectionNames().length > 0) {\n clackLog.info(`Loaded entities: ${db.getCollectionNames().join(\", \")}`);\n }\n\n // Socket.IO is attached after the HTTP server starts; entity routes receive\n // a broadcast callback that becomes a no-op until the server is ready.\n let emitEntityEvent: BroadcastEntityEvent = () => {};\n const entityRoutes = createEntityRoutes(\n db,\n devLogger,\n remoteProxy,\n (...args) => emitEntityEvent(...args),\n );\n app.use(\"/api/apps/:appId/entities\", entityRoutes);\n\n const { path: mediaFilesDir } = await dir();\n\n // Serve uploaded files statically\n app.use(\"/media\", express.static(mediaFilesDir));\n\n const integrationRoutes = createIntegrationRoutes(\n mediaFilesDir,\n baseUrl,\n remoteProxy,\n devLogger,\n );\n app.use(\"/api/apps/:appId/integration-endpoints\", integrationRoutes);\n\n const customIntegrationRoutes = createCustomIntegrationRoutes(\n remoteProxy,\n devLogger,\n );\n app.use(\"/api/apps/:appId/integrations/custom\", customIntegrationRoutes);\n\n app.use((req, res, next) => {\n devLogger.warn(\n `\"${req.originalUrl}\" is not supported in local development, passing call to production`,\n );\n remoteProxy(req, res, next);\n });\n\n const server = await new Promise<Server>((resolve, reject) => {\n const s = app.listen(port, \"127.0.0.1\", (err) => {\n if (err) {\n if (\"code\" in err && err.code === \"EADDRINUSE\") {\n reject(\n new Error(\n `Port ${port} is already in use. Stop the other process and try again.`,\n ),\n );\n } else {\n reject(err);\n }\n } else {\n resolve(s);\n }\n });\n });\n\n const io = createRealtimeServer(server);\n emitEntityEvent = (appId, entityName, event) => {\n broadcastEntityEvent(io, appId, entityName, event);\n };\n\n const base44ConfigWatcher = new WatchBase44(\n {\n functions: join(dirname(project.configPath), project.functionsDir),\n entities: join(dirname(project.configPath), project.entitiesDir),\n },\n devLogger,\n );\n base44ConfigWatcher.on(\"change\", async (name) => {\n try {\n const { functions, entities } = await options.loadResources();\n\n if (name === \"functions\") {\n const previousFunctionCount = functionManager.getFunctionNames().length;\n functionManager.reload(functions);\n\n const names = functionManager.getFunctionNames();\n if (names.length > 0) {\n devLogger.log(`Reloaded functions: ${names.sort().join(\", \")}`);\n } else if (previousFunctionCount > 0) {\n devLogger.log(\"All functions removed\");\n }\n }\n
|
|
982
|
+
"import type { Server } from \"node:http\";\nimport { dirname, join } from \"node:path\";\nimport { log as clackLog } from \"@clack/prompts\";\nimport cors from \"cors\";\nimport express from \"express\";\nimport getPort from \"get-port\";\nimport { createProxyMiddleware } from \"http-proxy-middleware\";\nimport { dir } from \"tmp-promise\";\nimport { createDevLogger } from \"@/cli/dev/createDevLogger.js\";\nimport { FunctionManager } from \"@/cli/dev/dev-server/function-manager.js\";\nimport { createFunctionRouter } from \"@/cli/dev/dev-server/routes/functions.js\";\nimport type { ProjectData } from \"@/core/project/types.js\";\nimport { Database } from \"./database.js\";\nimport {\n type BroadcastEntityEvent,\n broadcastEntityEvent,\n createRealtimeServer,\n} from \"./realtime.js\";\nimport { createEntityRoutes } from \"./routes/entities.js\";\nimport {\n createCustomIntegrationRoutes,\n createIntegrationRoutes,\n} from \"./routes/integrations.js\";\nimport { WatchBase44 } from \"./watcher.js\";\n\nconst DEFAULT_PORT = 4400;\nconst BASE44_APP_URL = \"https://base44.app\";\n\ninterface DevServerOptions {\n port?: number;\n loadResources: () => Promise<{\n functions: ProjectData[\"functions\"];\n entities: ProjectData[\"entities\"];\n project: ProjectData[\"project\"];\n }>;\n}\n\ninterface DevServerResult {\n port: number;\n server: Server;\n}\n\nexport async function createDevServer(\n options: DevServerOptions,\n): Promise<DevServerResult> {\n const { port: userPort } = options;\n const port = userPort ?? (await getPort({ port: DEFAULT_PORT }));\n const baseUrl = `http://localhost:${port}`;\n\n const { functions, entities, project } = await options.loadResources();\n\n const app = express();\n\n const remoteProxy = createProxyMiddleware({\n target: BASE44_APP_URL,\n changeOrigin: true,\n });\n\n app.use(\n cors({\n origin: /^http:\\/\\/localhost(:\\d+)?$/,\n credentials: true,\n }),\n );\n\n // Redirect OAuth routes to base44.app directly — proxying breaks the\n // redirect flow and session cookies set by the provider.\n const AUTH_ROUTE_PATTERN = /^\\/api\\/apps\\/auth(\\/|$)/;\n app.use((req, res, next) => {\n if (AUTH_ROUTE_PATTERN.test(req.path)) {\n const targetUrl = new URL(req.originalUrl, BASE44_APP_URL);\n return res.redirect(targetUrl.toString());\n }\n next();\n });\n\n const devLogger = createDevLogger();\n\n const functionManager = new FunctionManager(functions, devLogger);\n const functionRoutes = createFunctionRouter(functionManager, devLogger);\n app.use(\"/api/apps/:appId/functions\", functionRoutes);\n\n if (functionManager.getFunctionNames().length > 0) {\n clackLog.info(\n `Loaded functions: ${functionManager.getFunctionNames().join(\", \")}`,\n );\n }\n\n const db = new Database();\n db.load(entities);\n if (db.getCollectionNames().length > 0) {\n clackLog.info(`Loaded entities: ${db.getCollectionNames().join(\", \")}`);\n }\n\n // Socket.IO is attached after the HTTP server starts; entity routes receive\n // a broadcast callback that becomes a no-op until the server is ready.\n let emitEntityEvent: BroadcastEntityEvent = () => {};\n const entityRoutes = createEntityRoutes(\n db,\n devLogger,\n remoteProxy,\n (...args) => emitEntityEvent(...args),\n );\n app.use(\"/api/apps/:appId/entities\", entityRoutes);\n\n const { path: mediaFilesDir } = await dir();\n\n // Serve uploaded files statically\n app.use(\"/media\", express.static(mediaFilesDir));\n\n const integrationRoutes = createIntegrationRoutes(\n mediaFilesDir,\n baseUrl,\n remoteProxy,\n devLogger,\n );\n app.use(\"/api/apps/:appId/integration-endpoints\", integrationRoutes);\n\n const customIntegrationRoutes = createCustomIntegrationRoutes(\n remoteProxy,\n devLogger,\n );\n app.use(\"/api/apps/:appId/integrations/custom\", customIntegrationRoutes);\n\n app.use((req, res, next) => {\n devLogger.warn(\n `\"${req.originalUrl}\" is not supported in local development, passing call to production`,\n );\n remoteProxy(req, res, next);\n });\n\n const server = await new Promise<Server>((resolve, reject) => {\n const s = app.listen(port, \"127.0.0.1\", (err) => {\n if (err) {\n if (\"code\" in err && err.code === \"EADDRINUSE\") {\n reject(\n new Error(\n `Port ${port} is already in use. Stop the other process and try again.`,\n ),\n );\n } else {\n reject(err);\n }\n } else {\n resolve(s);\n }\n });\n });\n\n const io = createRealtimeServer(server);\n emitEntityEvent = (appId, entityName, event) => {\n broadcastEntityEvent(io, appId, entityName, event);\n };\n\n const base44ConfigWatcher = new WatchBase44(\n {\n functions: join(dirname(project.configPath), project.functionsDir),\n entities: join(dirname(project.configPath), project.entitiesDir),\n },\n devLogger,\n );\n base44ConfigWatcher.on(\"change\", async (name) => {\n try {\n const { functions, entities } = await options.loadResources();\n\n if (name === \"functions\") {\n const previousFunctionCount = functionManager.getFunctionNames().length;\n functionManager.reload(functions);\n\n const names = functionManager.getFunctionNames();\n if (names.length > 0) {\n devLogger.log(`Reloaded functions: ${names.sort().join(\", \")}`);\n } else if (previousFunctionCount > 0) {\n devLogger.log(\"All functions removed\");\n }\n }\n\n if (name === \"entities\") {\n const previousEntityCount = db.getCollectionNames().length;\n db.dropAll();\n if (previousEntityCount > 0) {\n devLogger.log(\"Entities directory changed, clearing data...\");\n }\n db.load(entities);\n if (db.getCollectionNames().length > 0) {\n devLogger.log(\n `Loaded entities: ${db.getCollectionNames().join(\", \")}`,\n );\n }\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n devLogger.error(errorMessage);\n }\n });\n await base44ConfigWatcher.start();\n\n const shutdown = () => {\n base44ConfigWatcher.close();\n io.close();\n functionManager.stopAll();\n server.close();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n return { port, server };\n}\n",
|
|
983
983
|
"import net from 'node:net';\nimport os from 'node:os';\n\nclass Locked extends Error {\n\tconstructor(port) {\n\t\tsuper(`${port} is locked`);\n\t}\n}\n\nconst lockedPorts = {\n\told: new Set(),\n\tyoung: new Set(),\n};\n\n// On this interval, the old locked ports are discarded,\n// the young locked ports are moved to old locked ports,\n// and a new young set for locked ports are created.\nconst releaseOldLockedPortsIntervalMs = 1000 * 15;\n\nconst minPort = 1024;\nconst maxPort = 65_535;\n\n// Lazily create timeout on first use\nlet timeout;\n\nconst getLocalHosts = () => {\n\tconst interfaces = os.networkInterfaces();\n\n\t// Add undefined value for createServer function to use default host,\n\t// and default IPv4 host in case createServer defaults to IPv6.\n\tconst results = new Set([undefined, '0.0.0.0']);\n\n\tfor (const _interface of Object.values(interfaces)) {\n\t\tfor (const config of _interface) {\n\t\t\tresults.add(config.address);\n\t\t}\n\t}\n\n\treturn results;\n};\n\nconst checkAvailablePort = options =>\n\tnew Promise((resolve, reject) => {\n\t\tconst server = net.createServer();\n\t\tserver.unref();\n\t\tserver.on('error', reject);\n\n\t\tserver.listen(options, () => {\n\t\t\tconst {port} = server.address();\n\t\t\tserver.close(() => {\n\t\t\t\tresolve(port);\n\t\t\t});\n\t\t});\n\t});\n\nconst getAvailablePort = async (options, hosts) => {\n\tif (options.host || options.port === 0) {\n\t\treturn checkAvailablePort(options);\n\t}\n\n\tfor (const host of hosts) {\n\t\ttry {\n\t\t\tawait checkAvailablePort({port: options.port, host}); // eslint-disable-line no-await-in-loop\n\t\t} catch (error) {\n\t\t\tif (!['EADDRNOTAVAIL', 'EINVAL'].includes(error.code)) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn options.port;\n};\n\nconst portCheckSequence = function * (ports) {\n\tif (ports) {\n\t\tyield * ports;\n\t}\n\n\tyield 0; // Fall back to 0 if anything else failed\n};\n\nexport default async function getPorts(options) {\n\tlet ports;\n\tlet exclude = new Set();\n\n\tif (options) {\n\t\tif (options.port) {\n\t\t\tports = typeof options.port === 'number' ? [options.port] : options.port;\n\t\t}\n\n\t\tif (options.exclude) {\n\t\t\tconst excludeIterable = options.exclude;\n\n\t\t\tif (typeof excludeIterable[Symbol.iterator] !== 'function') {\n\t\t\t\tthrow new TypeError('The `exclude` option must be an iterable.');\n\t\t\t}\n\n\t\t\tfor (const element of excludeIterable) {\n\t\t\t\tif (typeof element !== 'number') {\n\t\t\t\t\tthrow new TypeError('Each item in the `exclude` option must be a number corresponding to the port you want excluded.');\n\t\t\t\t}\n\n\t\t\t\tif (!Number.isSafeInteger(element)) {\n\t\t\t\t\tthrow new TypeError(`Number ${element} in the exclude option is not a safe integer and can't be used`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\texclude = new Set(excludeIterable);\n\t\t}\n\t}\n\n\tif (timeout === undefined) {\n\t\ttimeout = setTimeout(() => {\n\t\t\ttimeout = undefined;\n\n\t\t\tlockedPorts.old = lockedPorts.young;\n\t\t\tlockedPorts.young = new Set();\n\t\t}, releaseOldLockedPortsIntervalMs);\n\n\t\t// Does not exist in some environments (Electron, Jest jsdom env, browser, etc).\n\t\tif (timeout.unref) {\n\t\t\ttimeout.unref();\n\t\t}\n\t}\n\n\tconst hosts = getLocalHosts();\n\n\tfor (const port of portCheckSequence(ports)) {\n\t\ttry {\n\t\t\tif (exclude.has(port)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet availablePort = await getAvailablePort({...options, port}, hosts); // eslint-disable-line no-await-in-loop\n\t\t\twhile (lockedPorts.old.has(availablePort) || lockedPorts.young.has(availablePort)) {\n\t\t\t\tif (port !== 0) {\n\t\t\t\t\tthrow new Locked(port);\n\t\t\t\t}\n\n\t\t\t\tavailablePort = await getAvailablePort({...options, port}, hosts); // eslint-disable-line no-await-in-loop\n\t\t\t}\n\n\t\t\tlockedPorts.young.add(availablePort);\n\n\t\t\treturn availablePort;\n\t\t} catch (error) {\n\t\t\tif (!['EADDRINUSE', 'EACCES'].includes(error.code) && !(error instanceof Locked)) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow new Error('No available ports found');\n}\n\nexport function portNumbers(from, to) {\n\tif (!Number.isInteger(from) || !Number.isInteger(to)) {\n\t\tthrow new TypeError('`from` and `to` must be integer numbers');\n\t}\n\n\tif (from < minPort || from > maxPort) {\n\t\tthrow new RangeError(`'from' must be between ${minPort} and ${maxPort}`);\n\t}\n\n\tif (to < minPort || to > maxPort) {\n\t\tthrow new RangeError(`'to' must be between ${minPort} and ${maxPort}`);\n\t}\n\n\tif (from > to) {\n\t\tthrow new RangeError('`to` must be greater than or equal to `from`');\n\t}\n\n\tconst generator = function * (from, to) {\n\t\tfor (let port = from; port <= to; port++) {\n\t\t\tyield port;\n\t\t}\n\t};\n\n\treturn generator(from, to);\n}\n\nexport function clearLockedPorts() {\n\tlockedPorts.old.clear();\n\tlockedPorts.young.clear();\n}\n",
|
|
984
984
|
"'use strict';\n\nconst { promisify } = require(\"util\");\nconst tmp = require(\"tmp\");\n\n// file\nmodule.exports.fileSync = tmp.fileSync;\nconst fileWithOptions = promisify((options, cb) =>\n tmp.file(options, (err, path, fd, cleanup) =>\n err ? cb(err) : cb(undefined, { path, fd, cleanup: promisify(cleanup) })\n )\n);\nmodule.exports.file = async (options) => fileWithOptions(options);\n\nmodule.exports.withFile = async function withFile(fn, options) {\n const { path, fd, cleanup } = await module.exports.file(options);\n try {\n return await fn({ path, fd });\n } finally {\n await cleanup();\n }\n};\n\n\n// directory\nmodule.exports.dirSync = tmp.dirSync;\nconst dirWithOptions = promisify((options, cb) =>\n tmp.dir(options, (err, path, cleanup) =>\n err ? cb(err) : cb(undefined, { path, cleanup: promisify(cleanup) })\n )\n);\nmodule.exports.dir = async (options) => dirWithOptions(options);\n\nmodule.exports.withDir = async function withDir(fn, options) {\n const { path, cleanup } = await module.exports.dir(options);\n try {\n return await fn({ path });\n } finally {\n await cleanup();\n }\n};\n\n\n// name generation\nmodule.exports.tmpNameSync = tmp.tmpNameSync;\nmodule.exports.tmpName = promisify(tmp.tmpName);\n\nmodule.exports.tmpdir = tmp.tmpdir;\n\nmodule.exports.setGracefulCleanup = tmp.setGracefulCleanup;\n",
|
|
985
985
|
"import { theme } from \"@/cli/utils/theme\";\n\ntype LogType = \"log\" | \"error\" | \"warn\";\n\nexport interface Logger {\n log: (msg: string) => void;\n error: (msg: string, err?: unknown) => void;\n warn: (msg: string) => void;\n}\n\nconst colorByType: Record<LogType, (text: string) => string> = {\n error: theme.styles.error,\n warn: theme.styles.warn,\n log: (text: string) => text,\n};\n\nexport function createDevLogger(): Logger {\n const print = (type: LogType, msg: string) => {\n const colorize = colorByType[type];\n console[type](colorize(msg));\n };\n\n return {\n log: (msg: string) => print(\"log\", msg),\n error: (msg: string, err?: unknown) => {\n print(\"error\", msg);\n if (err) {\n print(\"error\", String(err));\n }\n },\n warn: (msg: string) => print(\"warn\", msg),\n };\n}\n",
|