From 3cf1bd22f942da305dc47bcd2b1b6325f7a41f17 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 4 Jun 2026 23:05:22 -0400 Subject: [PATCH] docs: document script lib runtime package helpers --- scripts/lib/plugin-npm-runtime-build.mjs | 8 ++++++++ scripts/lib/plugin-package-dependencies.mjs | 4 ++++ scripts/lib/plugin-prerelease-test-plan.mjs | 4 ++++ scripts/lib/plugin-sdk-entries.mjs | 12 ++++++++++++ scripts/lib/record-shared.mjs | 3 +++ 5 files changed, 31 insertions(+) diff --git a/scripts/lib/plugin-npm-runtime-build.mjs b/scripts/lib/plugin-npm-runtime-build.mjs index e8ea52678f45..4af907dbd036 100644 --- a/scripts/lib/plugin-npm-runtime-build.mjs +++ b/scripts/lib/plugin-npm-runtime-build.mjs @@ -1,3 +1,4 @@ +// Builds package-local runtime dist files for publishable bundled plugins. import fs from "node:fs"; import path from "node:path"; import { pathToFileURL } from "node:url"; @@ -20,6 +21,7 @@ function readJsonFile(filePath) { return JSON.parse(fs.readFileSync(filePath, "utf8")); } +/** Return whether a plugin package is marked for npm publishing. */ export function isPublishablePluginPackage(packageJson) { return packageJson.openclaw?.release?.publishToNpm === true; } @@ -91,6 +93,7 @@ function packageRelativePathExists(packageDir, relativePath) { return fs.existsSync(path.join(packageDir, relativePath)); } +/** List extension package dirs whose package metadata enables npm publishing. */ export function listPublishablePluginPackageDirs(params = {}) { const repoRoot = path.resolve(params.repoRoot ?? "."); const extensionsRoot = path.join(repoRoot, "extensions"); @@ -107,12 +110,14 @@ export function listPublishablePluginPackageDirs(params = {}) { .toSorted((left, right) => left.localeCompare(right)); } +/** List package-local runtime output files expected from a runtime build plan. */ export function listPluginNpmRuntimeBuildOutputs(plan) { return Object.keys(plan.entry) .map((entryKey) => `./dist/${entryKey}.js`) .toSorted((left, right) => left.localeCompare(right)); } +/** Resolve package `files` entries needed for runtime build outputs and plugin metadata. */ export function resolvePluginNpmRuntimePackageFiles(plan) { const merged = new Set( Array.isArray(plan.packageJson.files) @@ -158,6 +163,7 @@ function resolveOpenClawPeerRange(packageJson, rootPackageJson) { ); } +/** Resolve package peer dependency metadata for the OpenClaw plugin API. */ export function resolvePluginNpmRuntimePackagePeerMetadata(plan) { const openclawPeerRange = resolveOpenClawPeerRange(plan.packageJson, plan.rootPackageJson); if (!openclawPeerRange) { @@ -183,6 +189,7 @@ export function resolvePluginNpmRuntimePackagePeerMetadata(plan) { }; } +/** Resolve the package-local runtime build plan for one publishable plugin package. */ export function resolvePluginNpmRuntimeBuildPlan(params) { const repoRoot = path.resolve(params.repoRoot ?? "."); const packageDir = resolvePackageDir(repoRoot, params.packageDir); @@ -247,6 +254,7 @@ export function resolvePluginNpmRuntimeBuildPlan(params) { }; } +/** Build package-local runtime files and static assets for one plugin package. */ export async function buildPluginNpmRuntime(params) { const plan = resolvePluginNpmRuntimeBuildPlan(params); if (!plan) { diff --git a/scripts/lib/plugin-package-dependencies.mjs b/scripts/lib/plugin-package-dependencies.mjs index f61031fc20fd..fed4320df177 100644 --- a/scripts/lib/plugin-package-dependencies.mjs +++ b/scripts/lib/plugin-package-dependencies.mjs @@ -1,6 +1,8 @@ +// Collects runtime dependency specs from bundled plugin packages. import fs from "node:fs"; import path from "node:path"; +/** Collect dependencies and optionalDependencies needed at plugin runtime. */ export function collectRuntimeDependencySpecs(packageJson = {}) { return new Map( [ @@ -10,6 +12,7 @@ export function collectRuntimeDependencySpecs(packageJson = {}) { ); } +/** Extract an npm package name from a bare module specifier. */ export function packageNameFromSpecifier(specifier) { if ( typeof specifier !== "string" || @@ -27,6 +30,7 @@ export function packageNameFromSpecifier(specifier) { return first.startsWith("@") && second ? `${first}/${second}` : first; } +/** Collect runtime dependency specs across bundled plugin packages and note conflicts. */ export function collectBundledPluginPackageDependencySpecs(bundledPluginsDir) { const specs = new Map(); diff --git a/scripts/lib/plugin-prerelease-test-plan.mjs b/scripts/lib/plugin-prerelease-test-plan.mjs index adedb08c84e9..451a3b1e294c 100644 --- a/scripts/lib/plugin-prerelease-test-plan.mjs +++ b/scripts/lib/plugin-prerelease-test-plan.mjs @@ -1,5 +1,7 @@ +// Defines the plugin prerelease validation surface and matching test lanes. import { BUNDLED_PLUGIN_INSTALL_UNINSTALL_SHARDS } from "./docker-e2e-scenarios.mjs"; +/** Required behavioral surfaces that plugin prerelease validation must cover. */ export const PLUGIN_PRERELEASE_REQUIRED_SURFACES = Object.freeze([ "package-artifact", "bundled-lifecycle", @@ -140,6 +142,7 @@ function coveredSurfaces(entries) { ].toSorted((a, b) => a.localeCompare(b)); } +/** Build the plugin prerelease plan from Docker lanes and static checks. */ export function createPluginPrereleaseTestPlan() { const dockerLanes = pluginPrereleaseDockerLanes.map((entry) => entry.lane); const allEntries = [...pluginPrereleaseDockerLanes, ...staticChecks]; @@ -155,6 +158,7 @@ export function createPluginPrereleaseTestPlan() { }; } +/** Assert that a plugin prerelease plan covers every required surface. */ export function assertPluginPrereleaseTestPlanComplete(plan = createPluginPrereleaseTestPlan()) { const missing = PLUGIN_PRERELEASE_REQUIRED_SURFACES.filter( (surface) => !plan.surfaces.includes(surface), diff --git a/scripts/lib/plugin-sdk-entries.mjs b/scripts/lib/plugin-sdk-entries.mjs index 433507b0f270..467ecfe39ea6 100644 --- a/scripts/lib/plugin-sdk-entries.mjs +++ b/scripts/lib/plugin-sdk-entries.mjs @@ -1,10 +1,13 @@ +// Derives plugin SDK entrypoint sets, package exports, and dist artifact paths. import deprecatedBarrelPluginSdkSubpathList from "./plugin-sdk-deprecated-barrel-subpaths.json" with { type: "json" }; import deprecatedPublicPluginSdkSubpathList from "./plugin-sdk-deprecated-public-subpaths.json" with { type: "json" }; import pluginSdkEntryList from "./plugin-sdk-entrypoints.json" with { type: "json" }; import privateLocalOnlyPluginSdkSubpathList from "./plugin-sdk-private-local-only-subpaths.json" with { type: "json" }; +/** All plugin SDK entrypoints, including the package root index. */ export const pluginSdkEntrypoints = [...pluginSdkEntryList]; +/** Plugin SDK subpath entrypoints, excluding the package root index. */ export const pluginSdkSubpaths = pluginSdkEntrypoints.filter((entry) => entry !== "index"); const privateLocalOnlyPluginSdkSubpathSet = new Set( @@ -13,30 +16,37 @@ const privateLocalOnlyPluginSdkSubpathSet = new Set( ), ); +/** Private plugin SDK entrypoints that are built locally but not exported publicly. */ export const privateLocalOnlyPluginSdkEntrypoints = pluginSdkSubpaths.filter((entry) => privateLocalOnlyPluginSdkSubpathSet.has(entry), ); +/** Public plugin SDK entrypoints that appear in package exports. */ export const publicPluginSdkEntrypoints = pluginSdkEntrypoints.filter( (entry) => entry === "index" || !privateLocalOnlyPluginSdkSubpathSet.has(entry), ); +/** Public plugin SDK subpaths, excluding the package root index. */ export const publicPluginSdkSubpaths = publicPluginSdkEntrypoints.filter( (entry) => entry !== "index", ); +/** Deprecated public plugin SDK subpaths kept for compatibility. */ export const deprecatedPublicPluginSdkEntrypoints = publicPluginSdkSubpaths.filter((entry) => deprecatedPublicPluginSdkSubpathList.includes(entry), ); +/** Deprecated barrel entrypoints that should not be expanded further. */ export const deprecatedBarrelPluginSdkEntrypoints = pluginSdkSubpaths.filter((entry) => deprecatedBarrelPluginSdkSubpathList.includes(entry), ); +/** Build tsdown entry source paths for plugin SDK entrypoints. */ export function buildPluginSdkEntrySources(entries = pluginSdkEntrypoints) { return Object.fromEntries(entries.map((entry) => [entry, `src/plugin-sdk/${entry}.ts`])); } +/** Build package export metadata for public plugin SDK entrypoints. */ export function buildPluginSdkPackageExports() { return Object.fromEntries( publicPluginSdkEntrypoints.map((entry) => [ @@ -49,6 +59,7 @@ export function buildPluginSdkPackageExports() { ); } +/** List public plugin SDK dist artifacts expected in package output. */ export function listPluginSdkDistArtifacts() { return publicPluginSdkEntrypoints.flatMap((entry) => [ `dist/plugin-sdk/${entry}.js`, @@ -56,6 +67,7 @@ export function listPluginSdkDistArtifacts() { ]); } +/** List private local-only plugin SDK dist artifacts expected after local builds. */ export function listPrivateLocalOnlyPluginSdkDistArtifacts() { return privateLocalOnlyPluginSdkEntrypoints.flatMap((entry) => [ `dist/plugin-sdk/${entry}.js`, diff --git a/scripts/lib/record-shared.mjs b/scripts/lib/record-shared.mjs index a7aafcbe1321..9a2fea696bb9 100644 --- a/scripts/lib/record-shared.mjs +++ b/scripts/lib/record-shared.mjs @@ -1,7 +1,10 @@ +// Tiny shared value-normalization helpers for script JSON records. +/** Return whether a value is a plain non-array object record. */ export function isRecord(value) { return value !== null && typeof value === "object" && !Array.isArray(value); } +/** Trim string values while converting non-strings to an empty string. */ export function trimString(value) { return typeof value === "string" ? value.trim() : ""; }