diff --git a/packages/aeria-build/src/watch.ts b/packages/aeria-build/src/watch.ts index 5006f97e..dfa6ece9 100644 --- a/packages/aeria-build/src/watch.ts +++ b/packages/aeria-build/src/watch.ts @@ -1,11 +1,31 @@ import chokidar from 'chokidar' -import { spawn } from 'child_process' +import path from 'path' +import { spawn, fork } from 'child_process' +import { systemFunctions } from '@aeriajs/builtins' import { compile } from './compile.js' import { log } from './log.js' -const compileAndSpawn = async () => { +export const compileAndSpawn = async () => { const result = await compile() + try { + const { getConfig } = await import('aeria-sdk/utils') + const { writeMirrorFiles } = await import('aeria-sdk/mirror') + + const mirror = await systemFunctions.describe({ + router: true, + noMemoize: true, + }) + + const config = await getConfig() + writeMirrorFiles(mirror, config) + + } catch( err: any ) { + if( err.code !== 'MODULE_NOT_FOUND' ) { + throw err + } + } + if( result.success ) { const api = spawn('node', [ '-r', @@ -14,6 +34,7 @@ const compileAndSpawn = async () => { '.env', 'dist/index.js', ]) + api.stdout.on('data', (data) => { process.stdout.write(data) }) @@ -23,10 +44,12 @@ const compileAndSpawn = async () => { return api } + + return null } export const watch = async () => { - let runningApi = await compileAndSpawn() + let runningApi = fork(path.join(__dirname, 'watchWorker.js')) const srcWatcher = chokidar.watch([ './src', './package.json', @@ -34,14 +57,21 @@ export const watch = async () => { './.env', ]) - srcWatcher.on('change', async (path) => { - if( runningApi ) { - runningApi.kill() + srcWatcher.on('change', async (filePath) => { + runningApi.kill() + + if( runningApi.exitCode === null ) { + await new Promise((resolve) => { + runningApi.on('exit', () => { + resolve() + }) + }) } - log('info', `change detected in file: ${path}`) + log('info', `change detected in file: ${filePath}`) log('info', 'compiling...') - runningApi = await compileAndSpawn() + + runningApi = fork(path.join(__dirname, 'watchWorker.js')) }) } diff --git a/packages/aeria-build/src/watchWorker.ts b/packages/aeria-build/src/watchWorker.ts new file mode 100644 index 00000000..40dd528e --- /dev/null +++ b/packages/aeria-build/src/watchWorker.ts @@ -0,0 +1,19 @@ +import { compileAndSpawn } from './watch.js' + +const result = compileAndSpawn() + +process.on('SIGTERM', async () => { + const proc = await result + if( !proc ) { + process.exit(1) + } + + if( proc.exitCode !== null ) { + process.exit(1) + } + + proc.kill() + proc.on('exit', () => { + process.exit(0) + }) +}) diff --git a/packages/aeria-build/tsconfig.json b/packages/aeria-build/tsconfig.json index 856843db..9d047f25 100644 --- a/packages/aeria-build/tsconfig.json +++ b/packages/aeria-build/tsconfig.json @@ -14,6 +14,12 @@ ], "@aeriajs/builtins": [ "../builtins/dist" + ], + "aeria-sdk": [ + "../aeria-sdk/dist" + ], + "aeria-sdk/*": [ + "../aeria-sdk/dist/*" ] } }, diff --git a/packages/aeria-sdk/package.json b/packages/aeria-sdk/package.json index 21593edb..ac063ef3 100644 --- a/packages/aeria-sdk/package.json +++ b/packages/aeria-sdk/package.json @@ -34,6 +34,9 @@ "require": "./dist/index.js", "types": "./dist/index.d.ts" }, + "./types": { + "types": "./types.d.ts" + }, "./storage": { "import": "./dist/storage.mjs", "require": "./dist/storage.js", @@ -43,6 +46,16 @@ "import": "./dist/topLevel.mjs", "require": "./dist/topLevel.js", "types": "./dist/topLevel.d.ts" + }, + "./utils": { + "import": "./dist/utils.mjs", + "require": "./dist/utils.js", + "types": "./dist/utils.d.ts" + }, + "./mirror": { + "import": "./dist/mirror.mjs", + "require": "./dist/mirror.js", + "types": "./dist/mirror.d.ts" } }, "peerDependencies": { diff --git a/packages/aeria-sdk/src/cli.ts b/packages/aeria-sdk/src/cli.ts index 0736df26..2e0770fa 100644 --- a/packages/aeria-sdk/src/cli.ts +++ b/packages/aeria-sdk/src/cli.ts @@ -1,14 +1,9 @@ -import path from 'path' -import { mirror } from './mirror.js' +import { mirrorRemotely } from './mirror.js' +import { getConfig } from './utils.js' const main = async () => { - const { aeriaSdk } = require(path.join(process.cwd(), 'package.json')) - if( typeof aeriaSdk !== 'object' || !aeriaSdk ) { - console.log('aeriaSdk is absent in package.json') - return - } - - mirror(aeriaSdk) + const config = await getConfig() + mirrorRemotely(config) } main() diff --git a/packages/aeria-sdk/src/index.ts b/packages/aeria-sdk/src/index.ts index 22cf0a8f..cf7b6721 100644 --- a/packages/aeria-sdk/src/index.ts +++ b/packages/aeria-sdk/src/index.ts @@ -5,3 +5,5 @@ export * from '@aeriajs/common' export * from './topLevel.js' export * from './runtime.js' export * from './storage.js' +export * from './mirror.js' +export * from './utils.js' diff --git a/packages/aeria-sdk/src/mirror.ts b/packages/aeria-sdk/src/mirror.ts index b96f57bf..763aea6e 100644 --- a/packages/aeria-sdk/src/mirror.ts +++ b/packages/aeria-sdk/src/mirror.ts @@ -90,19 +90,25 @@ export const aeria = Aeria(config) export const storage = getStorage(config) \n` -export const mirror = async (config: InstanceConfig) => { - const api = topLevel(config) +export const writeMirrorFiles = async (mirror: any, config: InstanceConfig) => { const runtimeBase = path.dirname(require.resolve('aeria-sdk')) - const mirror = deserialize(await api.describe.POST({ - router: true, - })) - await mkdir(runtimeBase, { recursive: true, }) + await writeFile(path.join(process.cwd(), 'aeria-sdk.d.ts'), mirrorDts(mirror)) await writeFile(path.join(runtimeBase, 'runtime.js'), runtimeCjs(config)) await writeFile(path.join(runtimeBase, 'runtime.mjs'), runtimeEsm(config)) } +export const mirrorRemotely = async (config: InstanceConfig) => { + const api = topLevel(config) + + const mirror = deserialize(await api.describe.POST({ + router: true, + })) + + return writeMirrorFiles(mirror, config) +} + diff --git a/packages/aeria-sdk/src/utils.ts b/packages/aeria-sdk/src/utils.ts index e3fdf213..78539e70 100644 --- a/packages/aeria-sdk/src/utils.ts +++ b/packages/aeria-sdk/src/utils.ts @@ -1,4 +1,5 @@ import type { InstanceConfig } from './types.js' +import path from 'path' export const apiUrl = (config: InstanceConfig) => { if( typeof config.apiUrl === 'string' ) { @@ -10,3 +11,12 @@ export const apiUrl = (config: InstanceConfig) => { : config.apiUrl.development } +export const getConfig = async () => { + const { aeriaSdk } = await import(path.join(process.cwd(), 'package.json')) + if( typeof aeriaSdk !== 'object' || !aeriaSdk ) { + throw new Error('aeriaSdk is absent in package.json') + } + + return aeriaSdk + +} diff --git a/packages/builtins/src/functions/describe.ts b/packages/builtins/src/functions/describe.ts index c900ce11..5173bd84 100644 --- a/packages/builtins/src/functions/describe.ts +++ b/packages/builtins/src/functions/describe.ts @@ -12,9 +12,10 @@ type Payload = { roles?: boolean revalidate?: boolean router?: boolean + noMemoize?: boolean } -export const describe = async (context: Context): Promise => { +export const describe = async (contextOrPayload: Context | Payload) => { const result = {} as { descriptions: typeof descriptions roles?: string[] @@ -24,7 +25,9 @@ export const describe = async (context: Context): Promise => { router?: any } - const props: Payload = context.request.payload + const props = 'request' in contextOrPayload + ? contextOrPayload.request.payload + : contextOrPayload if( props.revalidate ) { const authEither = await authenticate({ @@ -43,7 +46,9 @@ export const describe = async (context: Context): Promise => { result.auth = JSON.parse(JSON.stringify(auth)) } - const collections = await getCollections() + const collections = await getCollections({ + memoize: !props.noMemoize + }) const retrievedCollections = props.collections?.length ? Object.fromEntries(Object.entries(collections).filter(([key]) => props.collections!.includes(key))) @@ -72,11 +77,11 @@ export const describe = async (context: Context): Promise => { result.router = router.routesMeta } - if( props.noSerialize ) { + if( props.noSerialize || !('response' in contextOrPayload) ) { return result } - context.response.setHeader('content-type', 'application/bson') - return context.response.end(serialize(result)) + contextOrPayload.response.setHeader('content-type', 'application/bson') + return contextOrPayload.response.end(serialize(result)) } diff --git a/packages/entrypoint/src/index.ts b/packages/entrypoint/src/index.ts index a2c3b386..a52b0b45 100644 --- a/packages/entrypoint/src/index.ts +++ b/packages/entrypoint/src/index.ts @@ -6,12 +6,16 @@ import fs from 'fs/promises' let collectionsMemo: Awaited> | undefined const collectionMemo: Record = {} -export const getEntrypoint = async () => { +export const getEntrypointPath = async () => { const { main, aeriaMain } = JSON.parse(await fs.readFile(path.join(process.cwd(), 'package.json'), { encoding: 'utf8', })) - return dynamicImport(path.join(process.cwd(), aeriaMain || main)) + return path.join(process.cwd(), aeriaMain || main) +} + +export const getEntrypoint = async () => { + return dynamicImport(await getEntrypointPath()) } const internalGetCollections = async (): Promise Collection)>> => { @@ -24,8 +28,8 @@ const internalGetCollections = async (): Promise { - if( collectionsMemo ) { +export const getCollections = async ({ memoize } = { memoize: true }) => { + if( memoize && collectionsMemo ) { return Object.assign({}, collectionsMemo) }