diff --git a/docs/PACKAGE_AUTHORING.md b/docs/PACKAGE_AUTHORING.md index 6252cd5..48a9a75 100644 --- a/docs/PACKAGE_AUTHORING.md +++ b/docs/PACKAGE_AUTHORING.md @@ -79,7 +79,7 @@ It tells Moorline how to describe and distribute the package: - display `name` - `description` - `version` -- catalog tags and category +- npm discovery tags and category - recommendation metadata - release and channel metadata @@ -131,7 +131,7 @@ The normal distribution format is a finished bundle, not source code. An API adapter package exposes Moorline's control API over a protocol. The official shipped adapter is `official/http`, and the official CLI currently talks to HTTP endpoints. -API adapter packages occupy the built-in `api-adapter` activation key. Moorline selects at most one API adapter at a time. +API adapter packages occupy the core `api-adapter` activation key. Moorline selects at most one API adapter at a time. ### Provider @@ -143,7 +143,7 @@ Use it when you want to integrate: - an app-server style agent runtime - a local or remote agent wrapper -Provider packages occupy the built-in `provider` activation key. Moorline activates at most one package for that key; packages that need multiple upstream providers should expose that multiplexing inside a single provider package. +Provider packages occupy the core `provider` activation key. Moorline activates at most one package for that key; packages that need multiple upstream providers should expose that multiplexing inside a single provider package. ### Transport @@ -156,7 +156,7 @@ Use it when you want to integrate: - email - a custom chat surface -Transport packages occupy the built-in `transport` activation key. Moorline activates at most one package for that key; packages that need to bridge multiple external surfaces should expose that multiplexing inside a single transport package. +Transport packages occupy the core `transport` activation key. Moorline activates at most one package for that key; packages that need to bridge multiple external surfaces should expose that multiplexing inside a single transport package. ### Plugin @@ -206,10 +206,11 @@ Bundle members declare: - package id - semantic version range - activation behavior: install, select, or enable +- optional source metadata for members that should not be resolved by package id Bundles are metadata-only. They should not ship runtime JavaScript behavior; put behavior in provider, transport, plugin, or skill packages. -In this implementation, bundle member resolution is catalog-backed. A local or third-party bundle can be installed as a bundle package, but each member it declares must resolve to package metadata already present in the Moorline catalog. Local-only member source descriptors are not embedded in bundle manifests yet. +Bundle member resolution is source-backed. Members can be embedded by npm bundle packages, can point at an explicit source descriptor, or can resolve by package id through npm metadata. Do not rely on a host-shipped official catalog. ## Authoring Structure @@ -441,7 +442,7 @@ Use hard dependencies when your package truly requires another package. Every package needs `moorline.dist.json`. -Keep discovery, catalog, and distribution metadata here. +Keep discovery and distribution metadata here. Required fields: @@ -686,8 +687,8 @@ Runtime code is first imported later when the selected package is actually loade ## Publishing Through npm -Moorline can discover public npm packages that carry Moorline package metadata. -Users still install through Moorline CLI commands or the control API; npm is only the temporary artifact and search backend. +Moorline discovers and installs public npm packages that carry Moorline package metadata. +Users still install through Moorline CLI commands or the control API; npm is the package distribution, discovery, and search source. Pack a package for npm: diff --git a/packages/package-kit/src/packageKit.ts b/packages/package-kit/src/packageKit.ts index 4dd2e9b..85d44ec 100644 --- a/packages/package-kit/src/packageKit.ts +++ b/packages/package-kit/src/packageKit.ts @@ -115,6 +115,9 @@ interface SourcePackageMetadata { repository?: string | Record; homepage?: string; keywords?: string[]; + main?: string; + types?: string; + exports?: unknown; } function readJson(path: string): unknown { @@ -183,13 +186,18 @@ function readSourcePackageMetadata(sourceDir: string): SourcePackageMetadata { const license = optionalPackageJsonString(packageJson.license, 'package.json.license'); const repository = optionalPackageJsonRepository(packageJson.repository); const homepage = optionalPackageJsonString(packageJson.homepage, 'package.json.homepage'); + const main = optionalPackageJsonString(packageJson.main, 'package.json.main'); + const types = optionalPackageJsonString(packageJson.types, 'package.json.types'); const keywords = validateStringArray(packageJson.keywords, 'package.json.keywords'); return { ...(description ? { description } : {}), ...(license ? { license } : {}), ...(repository ? { repository } : {}), ...(homepage ? { homepage } : {}), - ...(keywords ? { keywords } : {}) + ...(keywords ? { keywords } : {}), + ...(main ? { main } : {}), + ...(types ? { types } : {}), + ...(packageJson.exports !== undefined ? { exports: packageJson.exports } : {}) }; } @@ -509,7 +517,7 @@ function copyOfficialHttpRuntimeAssets(sourceDir: string, outDir: string, manife if (manifest.id !== 'official/http') { return; } - const resourcesRoot = resolve(sourceDir, '..', 'core', 'resources'); + const resourcesRoot = resolve(sourceDir, 'resources'); if (existsSync(resourcesRoot)) { cpSync(resourcesRoot, join(outDir, 'resources'), { recursive: true }); } @@ -732,32 +740,13 @@ function generatedPackageJson(input: { ...sourceKeywords.map((keyword) => npmKeywordSafe(keyword)).filter((keyword): keyword is string => Boolean(keyword)), ...distroTags.map((tag) => npmKeywordSafe(tag)).filter((tag): tag is string => Boolean(tag)) ]; - const runtimeEntrypoint = - input.manifest.id === 'official/http' - ? { - main: './index.mjs', - types: './index.d.ts', - exports: { - '.': { - types: './index.d.ts', - default: './index.mjs' - }, - './server': { - types: './server.d.ts', - default: './server.mjs' - }, - './server.js': { - types: './server.d.ts', - default: './server.mjs' - } - } - } - : { - main: './index.mjs', - exports: { - '.': './index.mjs' - } - }; + const runtimeEntrypoint = { + main: input.sourcePackage?.main ?? './index.mjs', + ...(input.sourcePackage?.types ? { types: input.sourcePackage.types } : {}), + exports: input.sourcePackage?.exports ?? { + '.': './index.mjs' + } + }; return { name: input.npmName, version: input.manifest.version, diff --git a/tests/helpers/temp.ts b/tests/helpers/temp.ts index a6d58cb..fe84fb0 100644 --- a/tests/helpers/temp.ts +++ b/tests/helpers/temp.ts @@ -37,7 +37,6 @@ export async function cleanupTempRoots(options: { testFailed?: boolean } = {}): } } -afterEach(async (context) => { - const task = context.task as { result?: { state?: string } } | undefined; - await cleanupTempRoots({ testFailed: task?.result?.state === 'fail' }); +afterEach(async () => { + await cleanupTempRoots(); }); diff --git a/tests/unit/package-kit-npm-pack.test.ts b/tests/unit/package-kit-npm-pack.test.ts index 3624dd2..fb2e77d 100644 --- a/tests/unit/package-kit-npm-pack.test.ts +++ b/tests/unit/package-kit-npm-pack.test.ts @@ -45,15 +45,31 @@ function writePluginSource(root: string): void { function writeOfficialHttpAdapterSource(root: string): void { mkdirSync(root, { recursive: true }); - mkdirSync(join(root, '..', 'core', 'resources', 'migrations'), { recursive: true }); - mkdirSync(join(root, '..', 'core', 'resources', 'policies'), { recursive: true }); - writeFileSync(join(root, '..', 'core', 'resources', 'migrations', '001_sessions.sql'), '-- migration\n'); - writeFileSync(join(root, '..', 'core', 'resources', 'policies', 'default-secure.json'), '{}\n'); + mkdirSync(join(root, 'resources', 'migrations'), { recursive: true }); + mkdirSync(join(root, 'resources', 'policies'), { recursive: true }); + writeFileSync(join(root, 'resources', 'migrations', '001_sessions.sql'), '-- migration\n'); + writeFileSync(join(root, 'resources', 'policies', 'default-secure.json'), '{}\n'); writeFileSync(join(root, 'package.json'), JSON.stringify({ name: '@moorline/http', version: '1.0.0', description: 'Official Moorline HTTP API adapter.', type: 'module', + main: './index.mjs', + types: './index.d.ts', + exports: { + '.': { + types: './index.d.ts', + default: './index.mjs' + }, + './server': { + types: './server.d.ts', + default: './server.mjs' + }, + './server.js': { + types: './server.d.ts', + default: './server.mjs' + } + }, private: false, license: 'MIT', repository: {