@fluid-app/fluid-cli-widget 0.1.4 → 0.1.5

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/index.mjs CHANGED
@@ -687,11 +687,12 @@ async function copyWidgetTemplate(templatePath, targetPath, variables) {
687
687
  const files = await getFiles(templatePath);
688
688
  for (const file of files) {
689
689
  const sourcePath = path.join(templatePath, file);
690
- const outputFile = getOutputFilename(file);
691
- const destinationPath = path.join(targetPath, outputFile);
692
- await mkdir(path.dirname(destinationPath), { recursive: true });
693
- await writeFile(destinationPath, renderTemplate(await readFile(sourcePath, "utf-8"), variables), "utf-8");
690
+ const isTemplate = file.endsWith(".template");
691
+ const outputFiles = getOutputFilenames(file);
692
+ const content = await readFile(sourcePath, "utf-8");
693
+ await writeOutputFiles(targetPath, outputFiles, isTemplate ? renderTemplate(content, variables) : content);
694
694
  }
695
+ for (const skillFile of await getSharedTemplateSkillFiles()) await writeOutputFiles(targetPath, getOutputFilenames(skillFile.relativePath), skillFile.content);
695
696
  }
696
697
  async function getPortalSdkVersion(options = {}) {
697
698
  return getWorkspacePackageVersion(["portal", "sdk"], "^0.1.0", options);
@@ -727,6 +728,26 @@ async function getFiles(dir, baseDir = dir) {
727
728
  }
728
729
  return files;
729
730
  }
731
+ async function getSharedTemplateSkillFiles() {
732
+ const packageRoot = findFluidCliPackageRoot();
733
+ const skillsRoot = path.join(packageRoot, "template-skills");
734
+ const files = await getFiles(skillsRoot);
735
+ return Promise.all(files.map(async (file) => ({
736
+ relativePath: path.join("skills", file),
737
+ content: await readFile(path.join(skillsRoot, file), "utf-8")
738
+ })));
739
+ }
740
+ function findFluidCliPackageRoot() {
741
+ const workspacePackageRoot = path.join(findPackageRoot(), "..", "core");
742
+ if (existsSync(path.join(workspacePackageRoot, "template-skills"))) return workspacePackageRoot;
743
+ let dir = path.dirname(fileURLToPath(import.meta.resolve("@fluid-app/fluid-cli")));
744
+ while (!existsSync(path.join(dir, "package.json"))) {
745
+ const parent = path.dirname(dir);
746
+ if (parent === dir) throw new Error("Could not find Fluid CLI package root");
747
+ dir = parent;
748
+ }
749
+ return dir;
750
+ }
730
751
  function renderTemplate(content, variables) {
731
752
  return content.replace(/{{\s*#if\s+([a-zA-Z0-9_]+)\s*}}([\s\S]*?){{\s*\/if\s*}}/g, (match, key, block) => {
732
753
  if (!isTemplateVariableKey(key)) return match;
@@ -748,6 +769,20 @@ function isTemplateVariableKey(key) {
748
769
  "droplet"
749
770
  ].includes(key);
750
771
  }
772
+ async function writeOutputFiles(targetPath, outputFiles, content) {
773
+ for (const outputFile of outputFiles) {
774
+ const destinationPath = path.join(targetPath, outputFile);
775
+ await mkdir(path.dirname(destinationPath), { recursive: true });
776
+ await writeFile(destinationPath, content, "utf-8");
777
+ }
778
+ }
779
+ function getOutputFilenames(filename) {
780
+ const outputFilename = getOutputFilename(filename);
781
+ const normalized = outputFilename.replace(/\\/g, "/");
782
+ if (normalized === "AGENTS.md") return ["AGENTS.md", "CLAUDE.md"];
783
+ if (normalized.startsWith("skills/")) return [path.join(".agents", outputFilename), path.join(".claude", outputFilename)];
784
+ return [outputFilename];
785
+ }
751
786
  function getOutputFilename(filename) {
752
787
  return filename.endsWith(".template") ? filename.slice(0, -9) : filename;
753
788
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["isNodeError","isRecord","isRecord","readPackageJson","isRecord","isRecord","isRecord","resolveDroplet"],"sources":["../src/utils/config.ts","../src/utils/validation.ts","../src/utils/source-package.ts","../src/utils/command.ts","../src/utils/package-manager.ts","../src/utils/package-scripts.ts","../src/commands/build.ts","../src/utils/template.ts","../src/commands/create.ts","../src/commands/dev.ts","../src/utils/auth-client.ts","../src/utils/droplets.ts","../src/commands/link.ts","../src/utils/droplet-api.ts","../src/commands/logs.ts","../src/commands/publish.ts","../src/commands/status.ts","../src/commands/validate.ts","../src/index.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const WIDGET_CONFIG_FILE = \"fluid.widget.config.ts\";\n\nexport interface FluidWidgetConfig {\n readonly droplet?: string;\n}\n\nexport interface LoadedWidgetConfig {\n readonly path: string;\n readonly config: Partial<FluidWidgetConfig>;\n}\n\nexport class WidgetConfigParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"WidgetConfigParseError\";\n }\n}\n\nexport function resolveWidgetConfigPath(projectDir: string): string {\n return path.join(projectDir, WIDGET_CONFIG_FILE);\n}\n\nexport async function loadWidgetConfig(\n projectDir: string,\n): Promise<LoadedWidgetConfig> {\n const configPath = resolveWidgetConfigPath(projectDir);\n\n let source: string;\n try {\n source = await readFile(configPath, \"utf-8\");\n } catch (err) {\n if (isNodeError(err) && err.code === \"ENOENT\") {\n return { path: configPath, config: {} };\n }\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Unable to read ${WIDGET_CONFIG_FILE}: ${message}`);\n }\n\n return { path: configPath, config: parseWidgetConfigSource(source) };\n}\n\nexport async function writeWidgetConfig(\n projectDir: string,\n config: FluidWidgetConfig,\n): Promise<void> {\n await writeFile(\n resolveWidgetConfigPath(projectDir),\n formatWidgetConfig(config),\n \"utf-8\",\n );\n}\n\nexport function formatWidgetConfig(config: FluidWidgetConfig): string {\n const lines = [\n 'import type { FluidWidgetConfig } from \"@fluid-app/fluid-cli-widget\";',\n \"\",\n \"const config = {\",\n ];\n\n if (config.droplet) {\n lines.push(` droplet: ${JSON.stringify(config.droplet)},`);\n }\n\n lines.push(\n \"} satisfies FluidWidgetConfig;\",\n \"\",\n \"const widgetConfig: FluidWidgetConfig = config;\",\n \"\",\n \"export default config;\",\n \"export const droplet = widgetConfig.droplet;\",\n 'export { widgetPackage, widgetPackages } from \"./manifest\";',\n \"\",\n );\n return lines.join(\"\\n\");\n}\n\nexport function parseWidgetConfigSource(\n source: string,\n): Partial<FluidWidgetConfig> {\n const objectSource = extractConfigObjectLiteral(source);\n const config = parseConfigObjectProperties(objectSource);\n\n return config;\n}\n\nexport function mergeWidgetConfig(\n current: Partial<FluidWidgetConfig>,\n updates: Partial<FluidWidgetConfig>,\n): FluidWidgetConfig {\n const droplet =\n normalizeOptionalString(updates.droplet) ??\n normalizeOptionalString(current.droplet);\n\n return droplet ? { droplet } : {};\n}\n\nexport function validateDropletUuid(dropletUuid: string): string | undefined {\n if (!dropletUuid) return \"Droplet UUID is required.\";\n if (dropletUuid.length > MAX_DROPLET_UUID_LENGTH) {\n return `Droplet UUID must be ${MAX_DROPLET_UUID_LENGTH} characters or fewer.`;\n }\n if (!URL_SAFE_IDENTIFIER_PATTERN.test(dropletUuid)) {\n return (\n \"Droplet UUID must be URL-safe text: letters, numbers, \" +\n \"'_', '~', and '-' only.\"\n );\n }\n return undefined;\n}\n\nfunction extractConfigObjectLiteral(source: string): string {\n const configStart = /\\bconst\\s+config\\s*=\\s*\\{/.exec(source);\n if (!configStart) {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} must declare \\`const config = { ... } satisfies FluidWidgetConfig\\`.`,\n );\n }\n\n const openBraceIndex = source.indexOf(\"{\", configStart.index);\n const closeBraceIndex = findMatchingBrace(source, openBraceIndex);\n const suffix = stripComments(source.slice(closeBraceIndex + 1));\n if (!/^\\s*(?:as\\s+const\\s+)?satisfies\\s+FluidWidgetConfig\\s*;/.test(suffix)) {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} config object must use \\`satisfies FluidWidgetConfig\\` or \\`as const satisfies FluidWidgetConfig\\`.`,\n );\n }\n\n return stripComments(source.slice(openBraceIndex + 1, closeBraceIndex));\n}\n\nfunction parseConfigObjectProperties(\n objectSource: string,\n): Partial<FluidWidgetConfig> {\n const config: { droplet?: string } = {};\n\n for (const member of splitTopLevelMembers(objectSource)) {\n const trimmedMember = member.trim();\n if (!trimmedMember) continue;\n\n const propertyMatch = /^(droplet)\\s*:\\s*([\\s\\S]+)$/.exec(trimmedMember);\n if (!propertyMatch) {\n const propertyName = /^([A-Za-z_$][\\w$]*)\\s*:/.exec(trimmedMember)?.[1];\n throw new WidgetConfigParseError(\n propertyName\n ? `${WIDGET_CONFIG_FILE} does not support config field ${propertyName}. Supported field is droplet.`\n : `${WIDGET_CONFIG_FILE} config fields must use optional droplet: \"...\" string literal.`,\n );\n }\n\n const propertyName = propertyMatch[1];\n if (propertyName !== \"droplet\") {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} config field could not be parsed.`,\n );\n }\n const valueSource = propertyMatch[2]?.trim() ?? \"\";\n const value = parseStringLiteralValue(valueSource, propertyName);\n if (!value.trim()) {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} field ${propertyName} must be a non-empty string literal.`,\n );\n }\n\n if (config.droplet !== undefined) {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} field droplet must only be declared once.`,\n );\n }\n config.droplet = value;\n }\n\n return config;\n}\n\nfunction parseStringLiteralValue(\n valueSource: string,\n propertyName: string,\n): string {\n if (valueSource.startsWith('\"') && valueSource.endsWith('\"')) {\n try {\n const parsed = JSON.parse(valueSource) as unknown;\n if (typeof parsed === \"string\") return parsed;\n } catch {\n // Fall through to the actionable error below.\n }\n }\n\n if (valueSource.startsWith(\"'\") && valueSource.endsWith(\"'\")) {\n return parseSingleQuotedStringLiteral(valueSource);\n }\n\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} field ${propertyName} must be a string literal (for example ${propertyName}: \"value\").`,\n );\n}\n\nfunction parseSingleQuotedStringLiteral(valueSource: string): string {\n let value = \"\";\n\n for (let index = 1; index < valueSource.length - 1; index += 1) {\n const char = valueSource[index];\n if (char !== \"\\\\\") {\n value += char;\n continue;\n }\n\n index += 1;\n const escaped = valueSource[index];\n switch (escaped) {\n case \"\\\\\":\n case \"'\":\n case '\"':\n value += escaped;\n break;\n case \"n\":\n value += \"\\n\";\n break;\n case \"r\":\n value += \"\\r\";\n break;\n case \"t\":\n value += \"\\t\";\n break;\n case \"b\":\n value += \"\\b\";\n break;\n case \"f\":\n value += \"\\f\";\n break;\n case \"v\":\n value += \"\\v\";\n break;\n default:\n value += escaped ?? \"\";\n break;\n }\n }\n\n return value;\n}\n\nfunction stripComments(source: string): string {\n let result = \"\";\n let stringQuote: '\"' | \"'\" | \"`\" | undefined;\n let escaped = false;\n let lineComment = false;\n let blockComment = false;\n\n for (let index = 0; index < source.length; index += 1) {\n const char = source[index];\n const nextChar = source[index + 1];\n\n if (lineComment) {\n if (char === \"\\n\" || char === \"\\r\") {\n lineComment = false;\n result += char;\n } else {\n result += \" \";\n }\n continue;\n }\n\n if (blockComment) {\n if (char === \"*\" && nextChar === \"/\") {\n blockComment = false;\n result += \" \";\n index += 1;\n } else if (char === \"\\n\" || char === \"\\r\") {\n result += char;\n } else {\n result += \" \";\n }\n continue;\n }\n\n if (stringQuote) {\n result += char;\n if (escaped) {\n escaped = false;\n } else if (char === \"\\\\\") {\n escaped = true;\n } else if (char === stringQuote) {\n stringQuote = undefined;\n }\n continue;\n }\n\n if (char === \"/\" && nextChar === \"/\") {\n lineComment = true;\n result += \" \";\n index += 1;\n continue;\n }\n\n if (char === \"/\" && nextChar === \"*\") {\n blockComment = true;\n result += \" \";\n index += 1;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n stringQuote = char;\n }\n result += char;\n }\n\n return result;\n}\n\nfunction splitTopLevelMembers(objectSource: string): string[] {\n const members: string[] = [];\n let memberStart = 0;\n let stringQuote: '\"' | \"'\" | \"`\" | undefined;\n let escaped = false;\n let nestedDepth = 0;\n\n for (let index = 0; index < objectSource.length; index += 1) {\n const char = objectSource[index];\n\n if (stringQuote) {\n if (escaped) {\n escaped = false;\n } else if (char === \"\\\\\") {\n escaped = true;\n } else if (char === stringQuote) {\n stringQuote = undefined;\n }\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n stringQuote = char;\n continue;\n }\n\n if (char === \"{\" || char === \"[\" || char === \"(\") {\n nestedDepth += 1;\n continue;\n }\n if (char === \"}\" || char === \"]\" || char === \")\") {\n nestedDepth -= 1;\n continue;\n }\n\n if (char === \",\" && nestedDepth === 0) {\n members.push(objectSource.slice(memberStart, index));\n memberStart = index + 1;\n }\n }\n\n members.push(objectSource.slice(memberStart));\n return members;\n}\n\nfunction findMatchingBrace(source: string, openBraceIndex: number): number {\n let depth = 0;\n let stringQuote: '\"' | \"'\" | \"`\" | undefined;\n let escaped = false;\n let lineComment = false;\n let blockComment = false;\n\n for (let index = openBraceIndex; index < source.length; index += 1) {\n const char = source[index];\n const nextChar = source[index + 1];\n\n if (lineComment) {\n if (char === \"\\n\" || char === \"\\r\") lineComment = false;\n continue;\n }\n\n if (blockComment) {\n if (char === \"*\" && nextChar === \"/\") {\n blockComment = false;\n index += 1;\n }\n continue;\n }\n\n if (stringQuote) {\n if (escaped) {\n escaped = false;\n } else if (char === \"\\\\\") {\n escaped = true;\n } else if (char === stringQuote) {\n stringQuote = undefined;\n }\n continue;\n }\n\n if (char === \"/\" && nextChar === \"/\") {\n lineComment = true;\n index += 1;\n continue;\n }\n\n if (char === \"/\" && nextChar === \"*\") {\n blockComment = true;\n index += 1;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n stringQuote = char;\n continue;\n }\n\n if (char === \"{\") depth += 1;\n if (char === \"}\") {\n depth -= 1;\n if (depth === 0) return index;\n }\n }\n\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} config object is missing a closing brace.`,\n );\n}\n\nfunction normalizeOptionalString(\n value: string | undefined,\n): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\ninterface NodeError extends Error {\n readonly code?: string;\n}\n\nfunction isNodeError(error: unknown): error is NodeError {\n return error instanceof Error && \"code\" in error;\n}\n\nconst MAX_DROPLET_UUID_LENGTH = 256;\nconst URL_SAFE_SEGMENT_SOURCE = \"[A-Za-z0-9][A-Za-z0-9_~-]*\";\nconst URL_SAFE_IDENTIFIER_PATTERN = new RegExp(`^${URL_SAFE_SEGMENT_SOURCE}$`);\n","export interface WidgetCliValidationError {\n readonly path?: string;\n readonly message: string;\n}\n\nexport function formatValidationErrors(\n errors: readonly WidgetCliValidationError[],\n): string {\n if (errors.length === 0) return \"Validation failed.\";\n\n return [\n \"Validation failed:\",\n ...errors.map((error) => {\n const location = error.path ? `${error.path}: ` : \"\";\n return ` - ${location}${error.message}`;\n }),\n ].join(\"\\n\");\n}\n","import { existsSync } from \"node:fs\";\nimport { mkdir, rm, rmdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n loadSourceWidgetPackages,\n validateSingleSourceWidgetPackage,\n} from \"@fluid-app/fluid-cli-portal\";\nimport {\n WIDGET_CONFIG_FILE,\n loadWidgetConfig,\n validateDropletUuid,\n} from \"./config.js\";\nimport type {\n ValidatedWidgetPackageBuild,\n WidgetPackageValidationError,\n} from \"@fluid-app/fluid-cli-portal\";\nimport { formatValidationErrors } from \"./validation.js\";\n\nexport const DEFAULT_WIDGET_OUT_DIR = \".fluid/widget-dist\";\nexport const ROOT_WIDGET_CONFIG_FILE: string = WIDGET_CONFIG_FILE;\n\nconst SOURCE_CONFIG_CANDIDATES = [\n \"src/widgets.config.ts\",\n \"src/portal.config.ts\",\n \"portal.config.ts\",\n] as const;\n\nexport interface WidgetValidationResult {\n readonly projectDir: string;\n readonly sourceConfigPath?: string;\n readonly validated: ValidatedWidgetPackageBuild;\n}\n\nexport class WidgetValidationError extends Error {\n public readonly errors: readonly WidgetPackageValidationError[];\n\n constructor(errors: readonly WidgetPackageValidationError[]) {\n super(formatValidationErrors(errors));\n this.name = \"WidgetValidationError\";\n this.errors = errors;\n }\n}\n\nexport async function validateDropletWidgetProject(\n projectDir: string,\n packageKeyOverride?: string,\n): Promise<WidgetValidationResult> {\n const resolvedProjectDir = path.resolve(projectDir);\n const sourceConfig = resolveWidgetSourceConfig(resolvedProjectDir);\n if (!sourceConfig) {\n throw new WidgetValidationError([\n {\n code: \"NO_SOURCE_PACKAGE\",\n path: ROOT_WIDGET_CONFIG_FILE,\n message:\n `No widget package config found. Create ${ROOT_WIDGET_CONFIG_FILE} ` +\n \"or src/widgets.config.ts that exports defineWidgetPackage(...).\",\n },\n ]);\n }\n\n const configErrors = await validateRootWidgetConfig(\n resolvedProjectDir,\n packageKeyOverride,\n );\n if (configErrors.length > 0) {\n throw new WidgetValidationError(configErrors);\n }\n\n const sourcePackages = await withRootWidgetConfigBridge(\n resolvedProjectDir,\n async () => {\n const loadResult = await loadSourceWidgetPackages(resolvedProjectDir);\n if (!loadResult.success) {\n throw new WidgetValidationError([\n {\n code: \"INVALID_SOURCE_PACKAGE\",\n path: sourceConfig.relativePath,\n message: appendDetails(\n loadResult.error.message,\n loadResult.error.details,\n ),\n },\n ]);\n }\n return loadResult.value;\n },\n packageKeyOverride,\n );\n\n const validation = validateSingleSourceWidgetPackage(sourcePackages, {\n owner: \"droplet\",\n });\n const errors = [\n ...validation.errors,\n ...validateDropletMetadata(sourcePackages),\n ];\n\n if (!validation.success || !validation.value || errors.length > 0) {\n throw new WidgetValidationError(errors);\n }\n\n return {\n projectDir: resolvedProjectDir,\n sourceConfigPath: sourceConfig.relativePath,\n validated: validation.value,\n };\n}\n\nexport async function withRootWidgetConfigBridge<T>(\n projectDir: string,\n callback: () => Promise<T>,\n packageKeyOverride?: string,\n): Promise<T> {\n const rootConfigPath = path.join(projectDir, ROOT_WIDGET_CONFIG_FILE);\n if (!existsSync(rootConfigPath) || resolvePortalSourceConfig(projectDir)) {\n return callback();\n }\n\n const packageKey =\n packageKeyOverride?.trim() ||\n (await loadWidgetConfig(projectDir)).config.droplet;\n if (!packageKey) {\n throw new Error(\n \"fluid.widget.config.ts must set droplet before validating, building, or publishing.\",\n );\n }\n\n const srcDir = path.join(projectDir, \"src\");\n const bridgePath = path.join(srcDir, \"widgets.config.ts\");\n const srcDirExisted = existsSync(srcDir);\n await mkdir(srcDir, { recursive: true });\n try {\n await writeFile(\n bridgePath,\n createRootWidgetConfigBridgeSource(packageKey),\n {\n encoding: \"utf-8\",\n flag: \"wx\",\n },\n );\n } catch (err) {\n if (isNodeError(err) && err.code === \"EEXIST\") {\n throw new Error(\n `${path.relative(projectDir, bridgePath)} already exists. ` +\n \"Remove this stale Fluid widget CLI bridge file and try again.\",\n );\n }\n throw err;\n }\n\n try {\n return await callback();\n } finally {\n await rm(bridgePath, { force: true }).catch(() => {});\n if (!srcDirExisted) await rmdir(srcDir).catch(() => {});\n }\n}\n\nfunction createRootWidgetConfigBridgeSource(packageKey: string): string {\n return `import { widgetPackage as sourceWidgetPackage } from \"../manifest\";\n\nconst packageKey = ${JSON.stringify(packageKey)};\nconst packageId = sourceWidgetPackage.scope + \".\" + packageKey;\n\nexport const widgetPackage = {\n ...sourceWidgetPackage,\n packageStableId: packageKey,\n packageId,\n};\n\nexport const widgetPackages = [widgetPackage] as const;\nexport default widgetPackage;\n`;\n}\n\nexport function resolveWidgetSourceConfig(\n projectDir: string,\n): { readonly path: string; readonly relativePath: string } | undefined {\n const rootConfig = path.join(projectDir, ROOT_WIDGET_CONFIG_FILE);\n if (existsSync(rootConfig)) {\n return { path: rootConfig, relativePath: ROOT_WIDGET_CONFIG_FILE };\n }\n\n return resolvePortalSourceConfig(projectDir);\n}\n\nexport function formatValidationSuccess(\n result: WidgetValidationResult,\n): string {\n return [\n \"Widget package is valid.\",\n `Config: ${result.sourceConfigPath ?? \"unknown\"}`,\n `Package: ${result.validated.packageId}`,\n `Version: ${result.validated.version}`,\n `Widgets: ${result.validated.widgets.length}`,\n ].join(\"\\n\");\n}\n\nasync function validateRootWidgetConfig(\n projectDir: string,\n packageKeyOverride?: string,\n): Promise<WidgetPackageValidationError[]> {\n const configPath = path.join(projectDir, ROOT_WIDGET_CONFIG_FILE);\n if (!existsSync(configPath)) return [];\n\n try {\n const loaded = await loadWidgetConfig(projectDir);\n const droplet = packageKeyOverride ?? loaded.config.droplet ?? \"\";\n const packageKeyError = validateDropletUuid(droplet);\n if (!packageKeyError) return [];\n\n return [\n {\n code: \"INVALID_PACKAGE_KEY\",\n path: ROOT_WIDGET_CONFIG_FILE,\n message: packageKeyError,\n },\n ];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return [\n {\n code: \"INVALID_SOURCE_PACKAGE\",\n path: ROOT_WIDGET_CONFIG_FILE,\n message,\n },\n ];\n }\n}\n\nfunction validateDropletMetadata(\n sourcePackages: readonly unknown[],\n): WidgetPackageValidationError[] {\n if (sourcePackages.length !== 1) return [];\n\n const sourcePackage = sourcePackages[0];\n if (!isRecord(sourcePackage)) return [];\n\n const errors: WidgetPackageValidationError[] = [];\n if (sourcePackage[\"packageType\"] !== \"droplet\") {\n errors.push({\n code: \"UNSUPPORTED_OWNER\",\n path: \"packageType\",\n message:\n 'Droplet widget packages must set packageType: \"droplet\" in package metadata.',\n });\n }\n\n if (!isNonEmptyString(sourcePackage[\"packageStableId\"])) {\n errors.push({\n code: \"INVALID_PACKAGE_KEY\",\n path: \"packageStableId\",\n message:\n \"Droplet widget packages must set packageStableId so published widget types remain stable across builds.\",\n });\n }\n\n const widgets = sourcePackage[\"widgets\"];\n if (Array.isArray(widgets)) {\n widgets.forEach((widget, index) => {\n if (!isRecord(widget)) return;\n validateJsonSerializable(\n widget[\"propertySchema\"],\n `widgets[${index}].propertySchema`,\n errors,\n );\n validateJsonSerializable(\n widget[\"defaultProps\"],\n `widgets[${index}].defaultProps`,\n errors,\n );\n });\n }\n\n return errors;\n}\n\nfunction resolvePortalSourceConfig(\n projectDir: string,\n): { readonly path: string; readonly relativePath: string } | undefined {\n for (const relativePath of SOURCE_CONFIG_CANDIDATES) {\n const candidate = path.join(projectDir, relativePath);\n if (existsSync(candidate)) return { path: candidate, relativePath };\n }\n\n return undefined;\n}\n\nfunction validateJsonSerializable(\n value: unknown,\n pathLabel: string,\n errors: WidgetPackageValidationError[],\n): void {\n if (value === undefined) return;\n\n try {\n assertJsonSerializable(value, pathLabel);\n } catch (err) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: pathLabel,\n message: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\nfunction assertJsonSerializable(value: unknown, pathLabel: string): void {\n if (value === null) return;\n\n const valueType = typeof value;\n if (valueType === \"string\" || valueType === \"boolean\") return;\n if (valueType === \"number\") {\n if (Number.isFinite(value)) return;\n throw new Error(`${pathLabel} must not contain NaN or Infinity.`);\n }\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n assertJsonSerializable(item, `${pathLabel}[${index}]`);\n });\n return;\n }\n if (isRecord(value)) {\n for (const [key, item] of Object.entries(value)) {\n if (item === undefined) {\n throw new Error(`${pathLabel}.${key} must not be undefined.`);\n }\n assertJsonSerializable(item, `${pathLabel}.${key}`);\n }\n return;\n }\n\n throw new Error(`${pathLabel} must contain only JSON-serializable values.`);\n}\n\nfunction appendDetails(message: string, details: string | undefined): string {\n return details ? `${message}\\n${details}` : message;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\ninterface NodeError extends Error {\n readonly code?: string;\n}\n\nfunction isNodeError(error: unknown): error is NodeError {\n return error instanceof Error && \"code\" in error;\n}\n","import chalk from \"chalk\";\n\nexport async function runCliAction(action: () => Promise<void>): Promise<void> {\n try {\n await action();\n } catch (err) {\n console.error(chalk.red(\"Error:\") + \" \" + formatUnknownError(err));\n process.exit(1);\n }\n}\n\nexport function formatUnknownError(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n","import { spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\nexport function getRunCommand(\n script: string,\n cwd: string = process.cwd(),\n): string {\n return `${detectPackageManager(cwd)} run ${script}`;\n}\n\nexport function getInstallCommand(cwd: string = process.cwd()): string {\n return `${detectPackageManager(cwd)} install`;\n}\n\nexport async function installDependencies(cwd: string): Promise<void> {\n const packageManager = detectPackageManager(cwd);\n await runPackageManager(packageManager, [\"install\"], cwd);\n}\n\nexport function detectPackageManager(cwd: string): PackageManager {\n if (existsSync(path.join(cwd, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(path.join(cwd, \"yarn.lock\"))) return \"yarn\";\n if (existsSync(path.join(cwd, \"bun.lockb\"))) return \"bun\";\n if (existsSync(path.join(cwd, \"package-lock.json\"))) return \"npm\";\n\n const invokingPackageManager = readInvokingPackageManager();\n if (invokingPackageManager) return invokingPackageManager;\n\n const declaredPackageManager = readDeclaredPackageManager(cwd);\n if (declaredPackageManager && isCommandAvailable(declaredPackageManager)) {\n return declaredPackageManager;\n }\n\n return findAvailablePackageManager() ?? \"pnpm\";\n}\n\nfunction runPackageManager(\n command: PackageManager,\n args: readonly string[],\n cwd: string,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, [...args], {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n\n child.once(\"error\", (error: Error) => {\n reject(error);\n });\n\n child.once(\"exit\", (code: number | null) => {\n if (code === 0) {\n resolve();\n return;\n }\n\n reject(\n new Error(\n `${command} ${args.join(\" \")} exited with code ${code ?? \"unknown\"}`,\n ),\n );\n });\n });\n}\n\nfunction readInvokingPackageManager(): PackageManager | undefined {\n const userAgent = process.env[\"npm_config_user_agent\"];\n if (!userAgent) return undefined;\n\n const name = userAgent.split(\"/\")[0];\n if (isPackageManager(name)) return name;\n return undefined;\n}\n\nfunction readDeclaredPackageManager(cwd: string): PackageManager | undefined {\n const packageJsonPath = path.join(cwd, \"package.json\");\n if (!existsSync(packageJsonPath)) return undefined;\n\n try {\n const packageJson = JSON.parse(\n readFileSync(packageJsonPath, \"utf-8\"),\n ) as unknown;\n if (!isRecord(packageJson)) return undefined;\n\n const packageManager = packageJson[\"packageManager\"];\n if (typeof packageManager !== \"string\") return undefined;\n\n const name = packageManager.split(\"@\")[0];\n if (isPackageManager(name)) return name;\n } catch {\n // Malformed package.json is reported by command paths that need to parse it.\n }\n\n return undefined;\n}\n\nfunction findAvailablePackageManager(): PackageManager | undefined {\n for (const packageManager of [\"pnpm\", \"yarn\", \"bun\", \"npm\"] as const) {\n if (isCommandAvailable(packageManager)) return packageManager;\n }\n return undefined;\n}\n\nfunction isCommandAvailable(command: PackageManager): boolean {\n const result = spawnSync(command, [\"--version\"], {\n shell: process.platform === \"win32\",\n stdio: \"ignore\",\n });\n return result.status === 0;\n}\n\nfunction isPackageManager(value: string | undefined): value is PackageManager {\n return (\n value === \"pnpm\" || value === \"yarn\" || value === \"bun\" || value === \"npm\"\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { detectPackageManager } from \"./package-manager.js\";\n\nexport interface PackageScriptRunResult {\n readonly ran: boolean;\n readonly command?: string;\n}\n\nexport async function runPackageScriptIfAvailable(options: {\n readonly projectDir: string;\n readonly scriptName: string;\n}): Promise<PackageScriptRunResult> {\n const packageJsonPath = path.join(options.projectDir, \"package.json\");\n if (!existsSync(packageJsonPath)) return { ran: false };\n\n const packageJson = await readPackageJson(packageJsonPath);\n if (!hasPackageScript(packageJson, options.scriptName)) return { ran: false };\n\n const packageManager = detectPackageManager(options.projectDir);\n const args = [\"run\", options.scriptName];\n await runCommand(packageManager, args, options.projectDir);\n\n return {\n ran: true,\n command: `${packageManager} ${args.join(\" \")}`,\n };\n}\n\nfunction runCommand(\n command: string,\n args: readonly string[],\n cwd: string,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: \"inherit\",\n env: process.env,\n shell: process.platform === \"win32\",\n });\n\n child.on(\"error\", reject);\n child.on(\"exit\", (code, signal) => {\n if (code === 0) {\n resolve();\n return;\n }\n\n reject(\n new Error(\n signal\n ? `${command} ${args.join(\" \")} exited with signal ${signal}.`\n : `${command} ${args.join(\" \")} exited with code ${code ?? \"unknown\"}.`,\n ),\n );\n });\n });\n}\n\nasync function readPackageJson(packageJsonPath: string): Promise<unknown> {\n const source = await readFile(packageJsonPath, \"utf-8\");\n try {\n return JSON.parse(source) as unknown;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Unable to parse package.json: ${message}`);\n }\n}\n\nfunction hasPackageScript(value: unknown, scriptName: string): boolean {\n if (!isRecord(value)) return false;\n const scripts = value[\"scripts\"];\n if (!isRecord(scripts)) return false;\n return typeof scripts[scriptName] === \"string\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { Command } from \"commander\";\nimport {\n buildSharedWidgetPackage,\n validateWidgetPublishOutDir,\n} from \"@fluid-app/fluid-cli-portal\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport {\n DEFAULT_WIDGET_OUT_DIR,\n validateDropletWidgetProject,\n withRootWidgetConfigBridge,\n} from \"../utils/source-package.js\";\nimport { runCliAction } from \"../utils/command.js\";\nimport { runPackageScriptIfAvailable } from \"../utils/package-scripts.js\";\n\nexport interface BuildWidgetOptions {\n readonly outDir?: string;\n readonly skipTypecheck?: boolean;\n}\n\nexport interface BuildWidgetPackageOptions {\n readonly projectDir: string;\n readonly outDir: string;\n readonly skipTypecheck?: boolean;\n}\n\nexport interface BuildWidgetPackageResult {\n readonly outDir: string;\n readonly packageId: string;\n readonly version: string;\n}\n\nexport async function buildWidgetPackage(\n options: BuildWidgetPackageOptions,\n): Promise<BuildWidgetPackageResult> {\n await validateDropletWidgetProject(options.projectDir);\n\n if (options.skipTypecheck !== true) {\n await runPackageScriptIfAvailable({\n projectDir: options.projectDir,\n scriptName: \"typecheck\",\n });\n }\n\n const outDir = validateWidgetPublishOutDir(\n options.projectDir,\n options.outDir,\n );\n const buildResult = await withRootWidgetConfigBridge(options.projectDir, () =>\n buildSharedWidgetPackage({\n projectDir: options.projectDir,\n publishDir: outDir,\n owner: \"droplet\",\n }),\n );\n\n if (!buildResult.success) {\n const details = buildResult.error.details\n ? `\\n${buildResult.error.details}`\n : \"\";\n throw new Error(`${buildResult.error.message}${details}`);\n }\n\n return {\n outDir: buildResult.value.publishDir,\n packageId: buildResult.value.packageId,\n version: buildResult.value.version,\n };\n}\n\nexport const buildCommand: Command = new Command(\"build\")\n .description(\"Validate and build the current droplet widget package\")\n .option(\n \"-o, --out-dir <dir>\",\n \"Widget artifact output directory\",\n DEFAULT_WIDGET_OUT_DIR,\n )\n .option(\"--skip-typecheck\", \"Skip package typecheck script when present\")\n .action((options: BuildWidgetOptions) => {\n return runCliAction(async () => {\n const outDir = options.outDir ?? DEFAULT_WIDGET_OUT_DIR;\n console.log(chalk.blue.bold(\"Building Fluid widget package\"));\n console.log();\n\n const spinner = ora(\n \"Validating and building widget artifacts...\",\n ).start();\n const result = await buildWidgetPackage({\n projectDir: process.cwd(),\n outDir,\n skipTypecheck: options.skipTypecheck,\n });\n spinner.succeed(\"Built widget runtime artifacts\");\n\n console.log();\n console.log(chalk.gray(\"Package: \") + chalk.white(result.packageId));\n console.log(chalk.gray(\"Version: \") + chalk.white(result.version));\n console.log(chalk.gray(\"Output: \") + chalk.cyan(outDir));\n });\n });\n","import { mkdir, readdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { WidgetTemplateVariables } from \"../types.js\";\n\nexport function getDefaultTemplatePath(): string {\n return path.join(findPackageRoot(), \"templates\", \"default\");\n}\n\nexport async function directoryExists(filePath: string): Promise<boolean> {\n try {\n const stats = await stat(filePath);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function copyWidgetTemplate(\n templatePath: string,\n targetPath: string,\n variables: WidgetTemplateVariables,\n): Promise<void> {\n const files = await getFiles(templatePath);\n\n for (const file of files) {\n const sourcePath = path.join(templatePath, file);\n const outputFile = getOutputFilename(file);\n const destinationPath = path.join(targetPath, outputFile);\n\n await mkdir(path.dirname(destinationPath), { recursive: true });\n\n const content = await readFile(sourcePath, \"utf-8\");\n const processed = renderTemplate(content, variables);\n await writeFile(destinationPath, processed, \"utf-8\");\n }\n}\n\nexport async function getPortalSdkVersion(\n options: {\n readonly local?: boolean;\n readonly targetPath?: string;\n } = {},\n): Promise<string> {\n return getWorkspacePackageVersion([\"portal\", \"sdk\"], \"^0.1.0\", options);\n}\n\nexport async function getPortalCoreVersion(\n options: {\n readonly local?: boolean;\n readonly targetPath?: string;\n } = {},\n): Promise<string> {\n return getWorkspacePackageVersion([\"portal\", \"core\"], \"^0.1.0\", options);\n}\n\nexport async function getCliVersion(\n options: {\n readonly local?: boolean;\n readonly targetPath?: string;\n } = {},\n): Promise<string> {\n return getWorkspacePackageVersion([\"cli\", \"core\"], \"^0.1.0\", options);\n}\n\nexport async function getWidgetCliVersion(\n options: {\n readonly local?: boolean;\n readonly targetPath?: string;\n } = {},\n): Promise<string> {\n return getWorkspacePackageVersion([\"cli\", \"widget\"], \"^0.1.0\", options);\n}\n\nasync function getWorkspacePackageVersion(\n packagePathSegments: readonly string[],\n fallback: string,\n options: { readonly local?: boolean; readonly targetPath?: string },\n): Promise<string> {\n try {\n const packageRoot = findPackageRoot();\n const packagesRoot = path.join(packageRoot, \"..\", \"..\");\n const targetPackageRoot = path.join(packagesRoot, ...packagePathSegments);\n\n if (options.local) {\n const linkPath = (\n options.targetPath\n ? path.relative(options.targetPath, targetPackageRoot)\n : targetPackageRoot\n ).replace(/\\\\/g, \"/\");\n return `link:${linkPath || \".\"}`;\n }\n\n const packageJsonPath = path.join(targetPackageRoot, \"package.json\");\n const content = await readFile(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as { version?: string };\n return `^${pkg.version ?? fallback.replace(/^\\^/, \"\")}`;\n } catch {\n return fallback;\n }\n}\n\nasync function getFiles(dir: string, baseDir = dir): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: string[] = [];\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await getFiles(fullPath, baseDir)));\n } else {\n files.push(path.relative(baseDir, fullPath));\n }\n }\n\n return files;\n}\n\nfunction renderTemplate(\n content: string,\n variables: WidgetTemplateVariables,\n): string {\n const withConditionals = content.replace(\n /{{\\s*#if\\s+([a-zA-Z0-9_]+)\\s*}}([\\s\\S]*?){{\\s*\\/if\\s*}}/g,\n (match, key, block) => {\n if (!isTemplateVariableKey(key)) return match;\n return variables[key] ? String(block) : \"\";\n },\n );\n\n return withConditionals.replace(\n /{{\\s*([a-zA-Z0-9_]+)\\s*}}/g,\n (match, key) => {\n if (isTemplateVariableKey(key)) return variables[key] ?? \"\";\n return match;\n },\n );\n}\n\nfunction isTemplateVariableKey(\n key: string,\n): key is keyof WidgetTemplateVariables {\n return [\n \"projectName\",\n \"widgetScope\",\n \"widgetPackageVersion\",\n \"sdkVersion\",\n \"localCoreVersion\",\n \"fluidCliVersion\",\n \"widgetCliVersion\",\n \"droplet\",\n ].includes(key);\n}\n\nfunction getOutputFilename(filename: string): string {\n return filename.endsWith(\".template\")\n ? filename.slice(0, -\".template\".length)\n : filename;\n}\n\nfunction findPackageRoot(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (!existsSync(path.join(dir, \"package.json\"))) {\n const parent = path.dirname(dir);\n if (parent === dir)\n throw new Error(\"Could not find widget CLI package root\");\n dir = parent;\n }\n return dir;\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport path from \"node:path\";\nimport type { WidgetCreateOptions } from \"../types.js\";\nimport {\n copyWidgetTemplate,\n directoryExists,\n getCliVersion,\n getDefaultTemplatePath,\n getPortalCoreVersion,\n getPortalSdkVersion,\n getWidgetCliVersion,\n} from \"../utils/template.js\";\nimport {\n getInstallCommand,\n getRunCommand,\n installDependencies,\n} from \"../utils/package-manager.js\";\nimport {\n mergeWidgetConfig,\n validateDropletUuid,\n writeWidgetConfig,\n} from \"../utils/config.js\";\n\nconst DEFAULT_WIDGET_PACKAGE_VERSION = \"0.1.0\";\nconst DEFAULT_WIDGET_SCOPE = \"droplet\";\n\nexport const createCommand: Command = new Command(\"create\")\n .description(\"Create a standalone Fluid widget project\")\n .argument(\"<project-name>\", \"Name of the widget project to create\")\n .option(\"--skip-install\", \"Skip dependency installation\")\n .option(\n \"-o, --output-dir <dir>\",\n \"Directory to create the project in (defaults to cwd)\",\n )\n .option(\n \"--scope <scope>\",\n \"Widget package scope/namespace\",\n DEFAULT_WIDGET_SCOPE,\n )\n .option(\n \"--package-version <version>\",\n \"Initial widget package version\",\n DEFAULT_WIDGET_PACKAGE_VERSION,\n )\n .option(\n \"--local\",\n \"Use local monorepo packages via file: links (for development testing)\",\n )\n .option(\"--droplet <uuid>\", \"Existing droplet UUID that owns the package\")\n .action(async (projectName: string, options: WidgetCreateOptions) => {\n try {\n await createWidgetProject(projectName, options);\n } catch (err) {\n console.log();\n console.error(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n console.log();\n process.exit(1);\n }\n });\n\nexport async function createWidgetProject(\n projectName: string,\n options: WidgetCreateOptions,\n): Promise<void> {\n console.log();\n console.log(chalk.bold(\"Creating a new Fluid widget project\"));\n console.log();\n\n const projectNameError = validateProjectName(projectName);\n if (projectNameError) throw new Error(projectNameError);\n\n const droplet = options.droplet?.trim();\n if (droplet) {\n const dropletError = validateDropletUuid(droplet);\n if (dropletError) throw new Error(dropletError);\n }\n\n const outputDir = path.resolve(options.outputDir ?? process.cwd());\n const targetPath = path.join(outputDir, projectName);\n if (await directoryExists(targetPath)) {\n throw new Error(`Directory already exists: ${targetPath}`);\n }\n\n const templatePath = getDefaultTemplatePath();\n if (!(await directoryExists(templatePath))) {\n throw new Error(\"Default widget template not found.\");\n }\n\n const config = mergeWidgetConfig({}, { droplet });\n const widgetScope = options.scope ?? DEFAULT_WIDGET_SCOPE;\n const localOptions = { local: options.local === true, targetPath };\n const variables = {\n projectName,\n droplet: config.droplet,\n widgetScope,\n widgetPackageVersion:\n options.packageVersion ?? DEFAULT_WIDGET_PACKAGE_VERSION,\n sdkVersion: await getPortalSdkVersion(localOptions),\n localCoreVersion: options.local\n ? await getPortalCoreVersion(localOptions)\n : undefined,\n fluidCliVersion: await getCliVersion(localOptions),\n widgetCliVersion: await getWidgetCliVersion(localOptions),\n };\n\n const spinner = ora(\"Copying widget template...\").start();\n try {\n await copyWidgetTemplate(templatePath, targetPath, variables);\n await writeWidgetConfig(targetPath, config);\n spinner.succeed(\"Copied widget template\");\n } catch (err) {\n spinner.fail(\"Failed to create widget project\");\n throw err;\n }\n\n const installCommand = getInstallCommand(targetPath);\n if (options.skipInstall !== true) {\n spinner.start(`Installing dependencies with ${installCommand}...`);\n try {\n await installDependencies(targetPath);\n spinner.succeed(\"Installed dependencies\");\n } catch {\n spinner.fail(\"Failed to install dependencies\");\n console.log();\n console.log(chalk.yellow(\"You can install dependencies manually:\"));\n console.log(chalk.cyan(` cd ${formatCdPath(targetPath)}`));\n console.log(chalk.cyan(` ${installCommand}`));\n }\n }\n\n console.log();\n console.log(\n chalk.green.bold(\"Success!\") + ` Created ${chalk.cyan(projectName)}`,\n );\n console.log();\n console.log(\"Next steps:\");\n console.log(chalk.cyan(` cd ${formatCdPath(targetPath)}`));\n if (options.skipInstall === true) {\n console.log(chalk.cyan(` ${installCommand}`));\n }\n if (!config.droplet) {\n console.log(chalk.cyan(\" fluid widget link\"));\n }\n console.log(chalk.cyan(` ${getRunCommand(\"dev\", targetPath)}`));\n console.log();\n console.log(\n \"Edit \" + chalk.cyan(\"manifest.ts\") + \" to customize your widget package.\",\n );\n console.log(\n chalk.dim(\n \"This command only creates local project files; it does not create a droplet.\",\n ),\n );\n console.log();\n}\n\nfunction validateProjectName(projectName: string): string | undefined {\n if (!projectName) return \"Project name is required.\";\n if (!/^[a-z0-9][a-z0-9-]*$/.test(projectName)) {\n return \"Project name must contain only lowercase letters, numbers, and hyphens.\";\n }\n return undefined;\n}\n\nfunction formatCdPath(targetPath: string): string {\n return path.relative(process.cwd(), targetPath) || \".\";\n}\n","import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { runCliAction } from \"../utils/command.js\";\n\nexport const DEFAULT_WIDGET_DEV_PORT = 5174;\n\nexport const WIDGET_DEV_ROUTES = [\n \"/\",\n \"/builder-preview\",\n \"/__manifests__\",\n \"/__preview__\",\n \"/__runtime-entry__\",\n] as const;\n\nexport interface WidgetDevOptions {\n readonly port?: string;\n readonly host?: boolean | string;\n readonly mode?: string;\n}\n\nexport interface WidgetDevProjectSummary {\n readonly projectDir: string;\n readonly packageName: string;\n readonly viteConfigPath: string;\n readonly localUrl: string;\n readonly target: string;\n readonly mode: string;\n readonly routes: readonly string[];\n}\n\nexport const devCommand: Command = new Command(\"dev\")\n .description(\"Start the local Fluid widget preview development server\")\n .option(\n \"-p, --port <port>\",\n \"Port to run the widget dev server on\",\n String(DEFAULT_WIDGET_DEV_PORT),\n )\n .option(\"--host [host]\", \"Expose the dev server to the network\")\n .option(\"--mode <mode>\", \"Vite mode to run\", \"development\")\n .action((options: WidgetDevOptions) => {\n return runCliAction(async () => {\n const projectDir = process.cwd();\n const summary = await resolveWidgetDevProject(projectDir, options);\n\n printWidgetDevSummary(summary);\n await runViteDevServer(projectDir, options);\n });\n });\n\nexport async function resolveWidgetDevProject(\n projectDir: string,\n options: WidgetDevOptions = {},\n): Promise<WidgetDevProjectSummary> {\n const resolvedProjectDir = path.resolve(projectDir);\n const packageJsonPath = path.join(resolvedProjectDir, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n throw new Error(\n \"No package.json found. Run this command from a Fluid widget project directory.\",\n );\n }\n\n const packageJson = await readPackageJson(packageJsonPath);\n const packageName = readPackageName(packageJson) ?? path.basename(projectDir);\n\n const viteConfigPath = path.join(resolvedProjectDir, \"vite.config.ts\");\n if (!existsSync(viteConfigPath)) {\n throw new Error(\n \"No vite.config.ts found. Run this command from a Fluid widget project directory.\",\n );\n }\n\n const port = parsePort(options.port ?? String(DEFAULT_WIDGET_DEV_PORT));\n const host = normalizeHostOption(options.host);\n const mode = normalizeMode(options.mode);\n\n return {\n projectDir: resolvedProjectDir,\n packageName,\n viteConfigPath,\n localUrl: formatLocalUrl(port, host),\n target: packageName,\n mode,\n routes: WIDGET_DEV_ROUTES,\n };\n}\n\nexport function buildViteDevArgs(options: WidgetDevOptions = {}): string[] {\n const port = parsePort(options.port ?? String(DEFAULT_WIDGET_DEV_PORT));\n const host = normalizeHostOption(options.host);\n const mode = normalizeMode(options.mode);\n const args = [\"--port\", String(port), \"--mode\", mode];\n\n if (host) {\n args.push(\"--host\", host);\n }\n\n return args;\n}\n\nexport function printWidgetDevSummary(summary: WidgetDevProjectSummary): void {\n console.log();\n console.log(chalk.blue.bold(\"Fluid Widget Dev\"));\n console.log(chalk.gray(\"Local: \") + chalk.cyan(summary.localUrl));\n console.log(chalk.gray(\"Target: \") + chalk.white(summary.target));\n console.log(chalk.gray(\"Mode: \") + chalk.white(summary.mode));\n console.log(chalk.gray(\"Routes:\"));\n for (const route of summary.routes) {\n console.log(chalk.gray(\" - \") + chalk.cyan(route));\n }\n console.log();\n}\n\nexport async function runViteDevServer(\n projectDir: string,\n options: WidgetDevOptions = {},\n): Promise<void> {\n const viteCliPath = resolveViteCliPath(projectDir);\n if (!viteCliPath) {\n throw new Error(\n \"Unable to find Vite in node_modules. Run your package manager install command before starting widget dev.\",\n );\n }\n\n await runNodeScript(viteCliPath, buildViteDevArgs(options), projectDir);\n}\n\nfunction resolveViteCliPath(projectDir: string): string | undefined {\n const viteCliPath = path.join(\n projectDir,\n \"node_modules\",\n \"vite\",\n \"bin\",\n \"vite.js\",\n );\n return existsSync(viteCliPath) ? viteCliPath : undefined;\n}\n\nasync function readPackageJson(packageJsonPath: string): Promise<unknown> {\n try {\n return JSON.parse(await readFile(packageJsonPath, \"utf-8\")) as unknown;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Unable to read package.json: ${message}`);\n }\n}\n\nfunction readPackageName(packageJson: unknown): string | undefined {\n if (!isRecord(packageJson)) return undefined;\n const name = packageJson[\"name\"];\n if (typeof name !== \"string\") return undefined;\n const trimmed = name.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction parsePort(value: string): number {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65_535) {\n throw new Error(\"Port must be an integer between 1 and 65535.\");\n }\n return port;\n}\n\nfunction normalizeHostOption(\n host: WidgetDevOptions[\"host\"],\n): string | undefined {\n if (host === undefined || host === false) return undefined;\n if (host === true) return \"0.0.0.0\";\n\n const trimmed = host.trim();\n return trimmed.length > 0 ? trimmed : \"0.0.0.0\";\n}\n\nfunction normalizeMode(mode: string | undefined): string {\n const trimmed = mode?.trim();\n return trimmed && trimmed.length > 0 ? trimmed : \"development\";\n}\n\nfunction formatLocalUrl(port: number, host: string | undefined): string {\n const displayHost = host && host !== \"0.0.0.0\" ? host : \"localhost\";\n return `http://${displayHost}:${port}`;\n}\n\nfunction runNodeScript(\n scriptPath: string,\n args: readonly string[],\n cwd: string,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(process.execPath, [scriptPath, ...args], {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n\n child.on(\"error\", reject);\n child.on(\"exit\", (code, signal) => {\n if (code === 0 || signal === \"SIGINT\" || signal === \"SIGTERM\") {\n resolve();\n return;\n }\n\n reject(\n new Error(\n signal\n ? `Vite dev server exited with signal ${signal}.`\n : `Vite dev server exited with code ${code ?? \"unknown\"}.`,\n ),\n );\n });\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { createAuthenticatedWidgetPackageClient } from \"@fluid-app/fluid-cli-portal\";\nimport type { FetchClient } from \"@fluid-app/fluid-cli\";\n\nexport interface AuthenticatedClientResult {\n readonly client: FetchClient;\n}\n\nexport function createAuthenticatedClient(): AuthenticatedClientResult {\n return { client: createAuthenticatedWidgetPackageClient() };\n}\n","import prompts from \"prompts\";\nimport type { FetchClient } from \"@fluid-app/fluid-cli\";\nimport type { Choice } from \"prompts\";\n\nexport interface DropletChoice {\n readonly uuid: string;\n readonly name: string;\n}\n\nexport async function promptForDroplet(\n client: FetchClient,\n): Promise<string | undefined> {\n const droplets = await listDroplets(client);\n\n if (droplets.length === 0) {\n throw new Error(\n \"No droplets were found for this account. Create a droplet in Fluid first, then run this command again.\",\n );\n }\n\n const response = await prompts({\n type: \"autocomplete\",\n name: \"droplet\",\n message: \"Select the droplet that owns this widget package\",\n choices: droplets.map((droplet) => ({\n title: `${droplet.name} (${droplet.uuid})`,\n value: droplet.uuid,\n })),\n suggest: (input: string, choices: Choice[]) =>\n Promise.resolve(\n input\n ? choices.filter((choice) =>\n choice.title.toLowerCase().includes(input.toLowerCase()),\n )\n : choices,\n ),\n });\n\n return response[\"droplet\"] as string | undefined;\n}\n\nexport async function listDroplets(\n client: FetchClient,\n): Promise<readonly DropletChoice[]> {\n try {\n const response = await client.get<unknown>(\"/api/droplets\");\n return normalizeDropletsResponse(response);\n } catch (err) {\n if (isApiLikeError(err)) {\n if (err.status === 401 || err.status === 403) {\n throw new Error(\n \"Your Fluid session has expired. Run `fluid login` and try again.\",\n );\n }\n throw new Error(\n `Could not fetch droplets from Fluid (HTTP ${err.status}). ${err.message}`,\n );\n }\n\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Could not fetch droplets from Fluid. ${message}`);\n }\n}\n\nfunction normalizeDropletsResponse(response: unknown): DropletChoice[] {\n const candidates = extractDropletArray(response);\n return candidates\n .map(normalizeDroplet)\n .filter((droplet): droplet is DropletChoice => droplet !== undefined);\n}\n\nfunction extractDropletArray(response: unknown): readonly unknown[] {\n if (Array.isArray(response)) return response;\n if (!isRecord(response)) return [];\n\n const directDroplets = response[\"droplets\"];\n if (Array.isArray(directDroplets)) return directDroplets;\n\n const data = response[\"data\"];\n if (Array.isArray(data)) return data;\n if (isRecord(data) && Array.isArray(data[\"droplets\"])) {\n return data[\"droplets\"];\n }\n\n return [];\n}\n\nfunction normalizeDroplet(value: unknown): DropletChoice | undefined {\n if (!isRecord(value)) return undefined;\n\n const uuid = readString(value, \"uuid\") ?? readString(value, \"id\");\n if (!uuid) return undefined;\n\n const name =\n readString(value, \"name\") ??\n readString(value, \"title\") ??\n readString(value, \"slug\") ??\n uuid;\n\n return { uuid, name };\n}\n\nfunction readString(\n record: Readonly<Record<string, unknown>>,\n key: string,\n): string | undefined {\n const value = record[key];\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction isApiLikeError(\n error: unknown,\n): error is { readonly status: number; readonly message: string } {\n return (\n error instanceof Error &&\n \"status\" in error &&\n typeof (error as { readonly status?: unknown }).status === \"number\"\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import path from \"node:path\";\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport type { WidgetLinkOptions } from \"../types.js\";\nimport { createAuthenticatedClient } from \"../utils/auth-client.js\";\nimport { promptForDroplet } from \"../utils/droplets.js\";\nimport {\n WIDGET_CONFIG_FILE,\n loadWidgetConfig,\n mergeWidgetConfig,\n validateDropletUuid,\n writeWidgetConfig,\n} from \"../utils/config.js\";\n\nexport const linkCommand: Command = new Command(\"link\")\n .description(\"Link this widget project to an existing Fluid droplet\")\n .option(\"--droplet <uuid>\", \"Droplet UUID that owns the widget package\")\n .action(async (options: WidgetLinkOptions) => {\n try {\n const projectDir = process.cwd();\n const loaded = await loadWidgetConfig(projectDir);\n\n let droplet = options.droplet?.trim() || undefined;\n if (!droplet) {\n const spinner = ora(\"Fetching droplets...\").start();\n try {\n const { client } = createAuthenticatedClient();\n spinner.stop();\n droplet = await promptForDroplet(client);\n } catch (err) {\n spinner.fail(\"Unable to load droplets\");\n throw err;\n }\n\n if (!droplet) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n }\n\n const nextConfig = mergeWidgetConfig(loaded.config, { droplet });\n const nextDropletError = validateDropletUuid(nextConfig.droplet ?? \"\");\n if (nextDropletError) throw new Error(nextDropletError);\n\n await writeWidgetConfig(projectDir, nextConfig);\n\n console.log();\n console.log(chalk.green(\"Linked widget project.\"));\n console.log(\n chalk.gray(\"Config: \") +\n chalk.cyan(\n path.relative(projectDir, loaded.path) || WIDGET_CONFIG_FILE,\n ),\n );\n console.log(\n chalk.gray(\"Droplet: \") + chalk.white(nextConfig.droplet),\n );\n console.log(\n chalk.gray(\"Package key: \") +\n chalk.white(`${nextConfig.droplet} (derived from droplet)`),\n );\n console.log();\n } catch (err) {\n console.log();\n console.error(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n console.log();\n process.exit(1);\n }\n });\n","import type { FetchClient } from \"@fluid-app/fluid-cli\";\n\nexport interface DropletWidgetVersionSummary {\n readonly packageKey?: string;\n readonly packageId?: string;\n readonly version?: string;\n readonly status?: string;\n readonly createdAt?: string;\n readonly updatedAt?: string;\n readonly artifactCount?: number;\n readonly raw: unknown;\n}\n\nexport interface DropletWidgetLogEntry {\n readonly timestamp?: string;\n readonly level?: string;\n readonly message: string;\n readonly raw: unknown;\n}\n\nexport async function fetchDropletWidgetStatus(options: {\n readonly client: FetchClient;\n readonly droplet: string;\n}): Promise<DropletWidgetVersionSummary[]> {\n const response = await options.client.get<unknown>(\n `/api/droplets/${encodeURIComponent(options.droplet)}/widget_package_versions`,\n );\n return extractArray(response).map(normalizeVersionSummary);\n}\n\nexport async function fetchDropletWidgetLogs(options: {\n readonly client: FetchClient;\n readonly droplet: string;\n readonly limit?: number;\n}): Promise<DropletWidgetLogEntry[]> {\n const response = await options.client.get<unknown>(\n `/api/droplets/${encodeURIComponent(options.droplet)}/widget_package_versions/logs`,\n options.limit ? { limit: options.limit } : undefined,\n );\n return extractArray(response).map(normalizeLogEntry);\n}\n\nexport function formatStatusRows(\n versions: readonly DropletWidgetVersionSummary[],\n): string {\n if (versions.length === 0) return \"No widget package versions found.\";\n\n return versions\n .map((version) => {\n const identity =\n version.packageId ?? version.packageKey ?? \"unknown-package\";\n const release = version.version ?? \"unknown-version\";\n const status = version.status ?? \"unknown\";\n const updated = version.updatedAt ?? version.createdAt ?? \"unknown time\";\n const artifacts =\n version.artifactCount === undefined\n ? \"unknown artifacts\"\n : `${version.artifactCount} artifact${version.artifactCount === 1 ? \"\" : \"s\"}`;\n return `${identity}@${release} — ${status} — ${artifacts} — ${updated}`;\n })\n .join(\"\\n\");\n}\n\nexport function formatLogRows(logs: readonly DropletWidgetLogEntry[]): string {\n if (logs.length === 0) return \"No widget package logs found.\";\n\n return logs\n .map((entry) => {\n const timestamp = entry.timestamp ?? \"unknown time\";\n const level = entry.level ? entry.level.toUpperCase() : \"INFO\";\n return `[${timestamp}] ${level} ${entry.message}`;\n })\n .join(\"\\n\");\n}\n\nfunction normalizeVersionSummary(value: unknown): DropletWidgetVersionSummary {\n const record = unwrapKnownNestedRecord(value);\n const artifacts = readArray(record, \"artifacts\");\n return {\n packageKey: readFirstString(record, [\"package_key\", \"packageKey\"]),\n packageId: readFirstString(record, [\"package_id\", \"packageId\"]),\n version: readFirstString(record, [\"version\", \"name\"]),\n status: readFirstString(record, [\"status\", \"state\"]),\n createdAt: readFirstString(record, [\"created_at\", \"createdAt\"]),\n updatedAt: readFirstString(record, [\"updated_at\", \"updatedAt\"]),\n ...(artifacts ? { artifactCount: artifacts.length } : {}),\n raw: value,\n };\n}\n\nfunction normalizeLogEntry(value: unknown): DropletWidgetLogEntry {\n const record = unwrapKnownNestedRecord(value);\n return {\n timestamp: readFirstString(record, [\n \"timestamp\",\n \"created_at\",\n \"createdAt\",\n ]),\n level: readFirstString(record, [\"level\", \"severity\"]),\n message:\n readFirstString(record, [\"message\", \"event\", \"status\", \"state\"]) ??\n JSON.stringify(value),\n raw: value,\n };\n}\n\nfunction extractArray(value: unknown): unknown[] {\n if (Array.isArray(value)) return value;\n if (!isRecord(value)) return [];\n\n for (const key of [\n \"widget_package_versions\",\n \"widgetPackageVersions\",\n \"versions\",\n \"logs\",\n \"events\",\n \"data\",\n ] as const) {\n const nested = value[key];\n if (Array.isArray(nested)) return nested;\n }\n\n return [value];\n}\n\nfunction unwrapKnownNestedRecord(value: unknown): Record<string, unknown> {\n if (!isRecord(value)) return {};\n for (const key of [\n \"widget_package_version\",\n \"widgetPackageVersion\",\n \"version\",\n \"log\",\n \"event\",\n ] as const) {\n const nested = value[key];\n if (isRecord(nested)) return nested;\n }\n return value;\n}\n\nfunction readArray(\n record: Record<string, unknown>,\n key: string,\n): readonly unknown[] | undefined {\n const value = record[key];\n return Array.isArray(value) ? value : undefined;\n}\n\nfunction readFirstString(\n record: Record<string, unknown>,\n keys: readonly string[],\n): string | undefined {\n for (const key of keys) {\n const value = record[key];\n if (typeof value === \"string\" && value.trim().length > 0) return value;\n if (typeof value === \"number\" && Number.isFinite(value))\n return String(value);\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { Command } from \"commander\";\nimport { createAuthenticatedWidgetPackageClient } from \"@fluid-app/fluid-cli-portal\";\nimport chalk from \"chalk\";\nimport { loadWidgetConfig, validateDropletUuid } from \"../utils/config.js\";\nimport { fetchDropletWidgetLogs, formatLogRows } from \"../utils/droplet-api.js\";\nimport { runCliAction } from \"../utils/command.js\";\n\nexport interface LogsCommandOptions {\n readonly droplet?: string;\n readonly limit?: string;\n readonly json?: boolean;\n}\n\nexport const logsCommand: Command = new Command(\"logs\")\n .description(\"Show recent widget package publish logs for a Droplet\")\n .option(\"--droplet <uuid>\", \"Droplet UUID that owns the widget package\")\n .option(\"--limit <number>\", \"Maximum number of log entries to request\", \"50\")\n .option(\"--json\", \"Print raw log response as JSON\")\n .action((options: LogsCommandOptions) => {\n return runCliAction(async () => {\n const droplet = await resolveDroplet(process.cwd(), options.droplet);\n const limit = parseLimit(options.limit);\n const logs = await fetchDropletWidgetLogs({\n client: createAuthenticatedWidgetPackageClient(),\n droplet,\n limit,\n });\n\n if (options.json === true) {\n console.log(\n JSON.stringify(\n logs.map((entry) => entry.raw),\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(chalk.blue.bold(`Widget logs for droplet ${droplet}`));\n console.log();\n console.log(formatLogRows(logs));\n });\n });\n\nasync function resolveDroplet(\n projectDir: string,\n override: string | undefined,\n): Promise<string> {\n const droplet =\n override?.trim() || (await loadWidgetConfig(projectDir)).config.droplet;\n if (droplet?.trim()) {\n const trimmedDroplet = droplet.trim();\n const dropletError = validateDropletUuid(trimmedDroplet);\n if (dropletError) throw new Error(dropletError);\n return trimmedDroplet;\n }\n\n throw new Error(\n \"Missing droplet UUID. Pass --droplet <uuid> or set droplet in fluid.widget.config.ts.\",\n );\n}\n\nfunction parseLimit(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const parsed = Number(value);\n if (Number.isInteger(parsed) && parsed > 0 && parsed <= 500) return parsed;\n throw new Error(\"--limit must be an integer between 1 and 500.\");\n}\n","import { Command } from \"commander\";\nimport {\n createAuthenticatedWidgetPackageClient,\n printDryRunSessionPayload,\n printRuntimeOnlyNotice,\n printWidgetPublishSummary,\n publishWidgetRuntimeArtifacts,\n} from \"@fluid-app/fluid-cli-portal\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport {\n DEFAULT_WIDGET_OUT_DIR,\n validateDropletWidgetProject,\n withRootWidgetConfigBridge,\n} from \"../utils/source-package.js\";\nimport { loadWidgetConfig, validateDropletUuid } from \"../utils/config.js\";\nimport { runCliAction } from \"../utils/command.js\";\nimport { runPackageScriptIfAvailable } from \"../utils/package-scripts.js\";\n\nexport interface PublishWidgetPackageOptions {\n readonly projectDir: string;\n readonly outDir: string;\n readonly droplet?: string;\n readonly dryRun?: boolean;\n readonly skipTypecheck?: boolean;\n readonly client?: unknown;\n}\n\nexport interface PublishWidgetPackageResult {\n readonly dryRun: boolean;\n readonly uploaded: boolean;\n readonly outDir: string;\n readonly droplet?: string;\n}\n\nexport interface PublishWidgetPackageDependencies {\n readonly buildWidgetPackage: (options: {\n readonly projectDir: string;\n readonly outDir: string;\n }) => Promise<void>;\n readonly uploadWidgetPackage: (options: {\n readonly outDir: string;\n readonly droplet?: string;\n readonly client?: unknown;\n }) => Promise<void>;\n}\n\nexport async function publishWidgetPackage(\n options: PublishWidgetPackageOptions,\n dependencies?: PublishWidgetPackageDependencies,\n): Promise<PublishWidgetPackageResult> {\n if (dependencies) {\n await dependencies.buildWidgetPackage({\n projectDir: options.projectDir,\n outDir: options.outDir,\n });\n\n const dryRun = options.dryRun === true;\n if (dryRun) {\n return { dryRun, uploaded: false, outDir: options.outDir };\n }\n\n await dependencies.uploadWidgetPackage({\n outDir: options.outDir,\n droplet: options.droplet,\n client: options.client,\n });\n return { dryRun, uploaded: true, outDir: options.outDir };\n }\n\n const droplet = await resolvePublishDroplet(\n options.projectDir,\n options.droplet,\n );\n\n await validateDropletWidgetProject(options.projectDir, droplet);\n if (options.skipTypecheck !== true) {\n await runPackageScriptIfAvailable({\n projectDir: options.projectDir,\n scriptName: \"typecheck\",\n });\n }\n\n const dryRun = options.dryRun === true;\n await withRootWidgetConfigBridge(\n options.projectDir,\n () =>\n publishWidgetRuntimeArtifacts({\n projectDir: options.projectDir,\n outDir: options.outDir,\n buildOwner: \"droplet\",\n uploadOwner: { kind: \"droplet\", uuid: droplet },\n dryRun,\n ...(dryRun ? {} : { client: createAuthenticatedWidgetPackageClient() }),\n }),\n droplet,\n );\n\n return {\n dryRun,\n uploaded: !dryRun,\n outDir: options.outDir,\n droplet,\n };\n}\n\nexport interface PublishCommandOptions {\n readonly outDir?: string;\n readonly droplet?: string;\n readonly dryRun?: boolean;\n readonly skipTypecheck?: boolean;\n}\n\nexport const publishCommand: Command = new Command(\"publish\")\n .description(\"Publish a Fluid widget package\")\n .option(\n \"-o, --out-dir <dir>\",\n \"Widget artifact output directory\",\n DEFAULT_WIDGET_OUT_DIR,\n )\n .option(\"--droplet <uuid>\", \"Droplet UUID that owns the widget package\")\n .option(\"--dry-run\", \"Build and validate without uploading\")\n .option(\"--skip-typecheck\", \"Skip package typecheck script when present\")\n .action((options: PublishCommandOptions) => {\n return runCliAction(async () => {\n const projectDir = process.cwd();\n const outDir = options.outDir ?? DEFAULT_WIDGET_OUT_DIR;\n const droplet = await resolvePublishDroplet(projectDir, options.droplet);\n const dryRun = options.dryRun === true;\n\n console.log();\n console.log(chalk.blue.bold(\"Fluid Widget Publish\"));\n console.log();\n printRuntimeOnlyNotice();\n console.log(chalk.gray(\"Droplet: \") + chalk.white(droplet));\n console.log(chalk.gray(\"Output: \") + chalk.cyan(outDir));\n if (dryRun) {\n console.log(chalk.yellow(\"Dry run: no upload will be created.\"));\n }\n console.log();\n\n const validateSpinner = ora(\"Validating widget package...\").start();\n await validateDropletWidgetProject(projectDir, droplet);\n validateSpinner.succeed(\"Validated widget package\");\n\n if (options.skipTypecheck !== true) {\n const typecheckSpinner = ora(\"Running package typecheck...\").start();\n const typecheck = await runPackageScriptIfAvailable({\n projectDir,\n scriptName: \"typecheck\",\n });\n if (typecheck.ran) {\n typecheckSpinner.succeed(\"Package typecheck passed\");\n } else {\n typecheckSpinner.info(\"No typecheck script found; skipping\");\n }\n }\n\n const publishSpinner = ora(\n dryRun\n ? \"Building and preparing dry-run upload payload...\"\n : \"Building and publishing widget artifacts...\",\n ).start();\n\n const result = await withRootWidgetConfigBridge(\n projectDir,\n () =>\n publishWidgetRuntimeArtifacts({\n projectDir,\n outDir,\n buildOwner: \"droplet\",\n uploadOwner: { kind: \"droplet\", uuid: droplet },\n dryRun,\n ...(dryRun\n ? {}\n : { client: createAuthenticatedWidgetPackageClient() }),\n }),\n droplet,\n );\n\n publishSpinner.succeed(\n dryRun ? \"Prepared dry-run upload payload\" : \"Published widget package\",\n );\n\n if (dryRun) {\n console.log(\n chalk.yellow(\"Dry run complete — upload session was not requested.\"),\n );\n console.log();\n printDryRunSessionPayload(result);\n }\n\n printWidgetPublishSummary({\n title: dryRun ? \"Widget publish dry run\" : \"Widget publish\",\n ownerLabel: `droplet ${droplet}`,\n outDir,\n result,\n });\n });\n });\n\nasync function resolvePublishDroplet(\n projectDir: string,\n override: string | undefined,\n): Promise<string> {\n const droplet =\n override?.trim() || (await loadWidgetConfig(projectDir)).config.droplet;\n if (droplet?.trim()) {\n const trimmedDroplet = droplet.trim();\n const dropletError = validateDropletUuid(trimmedDroplet);\n if (dropletError) throw new Error(dropletError);\n return trimmedDroplet;\n }\n\n throw new Error(\n \"Missing droplet UUID. Pass --droplet <uuid> or set droplet in fluid.widget.config.ts.\",\n );\n}\n","import { Command } from \"commander\";\nimport { createAuthenticatedWidgetPackageClient } from \"@fluid-app/fluid-cli-portal\";\nimport chalk from \"chalk\";\nimport { loadWidgetConfig, validateDropletUuid } from \"../utils/config.js\";\nimport {\n fetchDropletWidgetStatus,\n formatStatusRows,\n} from \"../utils/droplet-api.js\";\nimport { runCliAction } from \"../utils/command.js\";\n\nexport interface StatusCommandOptions {\n readonly droplet?: string;\n readonly json?: boolean;\n}\n\nexport const statusCommand: Command = new Command(\"status\")\n .description(\"Show published widget package status for a Droplet\")\n .option(\"--droplet <uuid>\", \"Droplet UUID that owns the widget package\")\n .option(\"--json\", \"Print raw status response as JSON\")\n .action((options: StatusCommandOptions) => {\n return runCliAction(async () => {\n const droplet = await resolveDroplet(process.cwd(), options.droplet);\n const versions = await fetchDropletWidgetStatus({\n client: createAuthenticatedWidgetPackageClient(),\n droplet,\n });\n\n if (options.json === true) {\n console.log(\n JSON.stringify(\n versions.map((version) => version.raw),\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(chalk.blue.bold(`Widget status for droplet ${droplet}`));\n console.log();\n console.log(formatStatusRows(versions));\n });\n });\n\nasync function resolveDroplet(\n projectDir: string,\n override: string | undefined,\n): Promise<string> {\n const droplet =\n override?.trim() || (await loadWidgetConfig(projectDir)).config.droplet;\n if (droplet?.trim()) {\n const trimmedDroplet = droplet.trim();\n const dropletError = validateDropletUuid(trimmedDroplet);\n if (dropletError) throw new Error(dropletError);\n return trimmedDroplet;\n }\n\n throw new Error(\n \"Missing droplet UUID. Pass --droplet <uuid> or set droplet in fluid.widget.config.ts.\",\n );\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport {\n formatValidationSuccess,\n validateDropletWidgetProject,\n} from \"../utils/source-package.js\";\nimport { runCliAction } from \"../utils/command.js\";\n\nexport interface ValidateWidgetOptions {\n readonly json?: boolean;\n}\n\nexport const validateCommand: Command = new Command(\"validate\")\n .description(\"Validate the current droplet widget package\")\n .option(\"--json\", \"Print machine-readable validation output\")\n .action((options: ValidateWidgetOptions) => {\n return runCliAction(async () => {\n const result = await validateDropletWidgetProject(process.cwd());\n\n if (options.json === true) {\n console.log(\n JSON.stringify(\n {\n valid: true,\n config: result.sourceConfigPath,\n packageId: result.validated.packageId,\n version: result.validated.version,\n widgets: result.validated.widgets.map((widget) => ({\n name: widget.name,\n type: widget.type,\n })),\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(chalk.green(formatValidationSuccess(result)));\n });\n });\n","import { Command } from \"commander\";\nimport type { FluidPlugin, PluginContext } from \"@fluid-app/fluid-cli\";\nimport { buildCommand } from \"./commands/build.js\";\nimport { createCommand } from \"./commands/create.js\";\nimport { devCommand } from \"./commands/dev.js\";\nimport { linkCommand } from \"./commands/link.js\";\nimport { logsCommand } from \"./commands/logs.js\";\nimport { publishCommand } from \"./commands/publish.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { validateCommand } from \"./commands/validate.js\";\n\nexport const widgetCommand: Command = new Command(\"widget\")\n .description(\n \"Create, preview, link, validate, build, and publish standalone Fluid widgets\",\n )\n .addCommand(createCommand)\n .addCommand(devCommand)\n .addCommand(linkCommand)\n .addCommand(validateCommand)\n .addCommand(buildCommand)\n .addCommand(publishCommand)\n .addCommand(statusCommand)\n .addCommand(logsCommand);\n\nconst plugin: FluidPlugin = {\n name: \"@fluid-app/fluid-cli-widget\",\n version: \"0.1.0\",\n register(ctx: PluginContext) {\n const existingWidgetCommand = findCommand(ctx.program, \"widget\");\n if (existingWidgetCommand) {\n addCommandIfMissing(existingWidgetCommand, createCommand);\n addCommandIfMissing(existingWidgetCommand, devCommand);\n addCommandIfMissing(existingWidgetCommand, linkCommand);\n addCommandIfMissing(existingWidgetCommand, validateCommand);\n addCommandIfMissing(existingWidgetCommand, buildCommand);\n addCommandIfMissing(existingWidgetCommand, publishCommand);\n addCommandIfMissing(existingWidgetCommand, statusCommand);\n addCommandIfMissing(existingWidgetCommand, logsCommand);\n return;\n }\n\n ctx.program.addCommand(widgetCommand);\n },\n};\n\nexport default plugin;\n\nexport type {\n WidgetCommandOptions,\n WidgetCreateOptions,\n WidgetLinkOptions,\n WidgetTemplateVariables,\n} from \"./types.js\";\nexport { buildCommand, buildWidgetPackage } from \"./commands/build.js\";\nexport type {\n BuildWidgetOptions,\n BuildWidgetPackageOptions,\n BuildWidgetPackageResult,\n} from \"./commands/build.js\";\nexport { createCommand, createWidgetProject } from \"./commands/create.js\";\nexport {\n DEFAULT_WIDGET_DEV_PORT,\n WIDGET_DEV_ROUTES,\n buildViteDevArgs,\n devCommand,\n printWidgetDevSummary,\n resolveWidgetDevProject,\n runViteDevServer,\n} from \"./commands/dev.js\";\nexport type {\n WidgetDevOptions,\n WidgetDevProjectSummary,\n} from \"./commands/dev.js\";\nexport { linkCommand } from \"./commands/link.js\";\nexport { logsCommand } from \"./commands/logs.js\";\nexport type { LogsCommandOptions } from \"./commands/logs.js\";\nexport { publishCommand, publishWidgetPackage } from \"./commands/publish.js\";\nexport type {\n PublishCommandOptions,\n PublishWidgetPackageDependencies,\n PublishWidgetPackageOptions,\n PublishWidgetPackageResult,\n} from \"./commands/publish.js\";\nexport { statusCommand } from \"./commands/status.js\";\nexport type { StatusCommandOptions } from \"./commands/status.js\";\nexport { validateCommand } from \"./commands/validate.js\";\nexport type { ValidateWidgetOptions } from \"./commands/validate.js\";\nexport {\n WIDGET_CONFIG_FILE,\n formatWidgetConfig,\n loadWidgetConfig,\n mergeWidgetConfig,\n parseWidgetConfigSource,\n resolveWidgetConfigPath,\n validateDropletUuid,\n writeWidgetConfig,\n type FluidWidgetConfig,\n type LoadedWidgetConfig,\n} from \"./utils/config.js\";\nexport {\n DEFAULT_WIDGET_OUT_DIR,\n ROOT_WIDGET_CONFIG_FILE,\n WidgetValidationError,\n formatValidationSuccess,\n resolveWidgetSourceConfig,\n validateDropletWidgetProject,\n withRootWidgetConfigBridge,\n type WidgetValidationResult,\n} from \"./utils/source-package.js\";\nexport { formatValidationErrors } from \"./utils/validation.js\";\nexport type { WidgetCliValidationError } from \"./utils/validation.js\";\n\nfunction addCommandIfMissing(parent: Command, child: Command): void {\n if (findCommand(parent, child.name())) return;\n parent.addCommand(child);\n}\n\nfunction findCommand(parent: Command, name: string): Command | undefined {\n return parent.commands?.find((command) => command.name() === name);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["isNodeError","isRecord","isRecord","readPackageJson","isRecord","isRecord","isRecord","resolveDroplet"],"sources":["../src/utils/config.ts","../src/utils/validation.ts","../src/utils/source-package.ts","../src/utils/command.ts","../src/utils/package-manager.ts","../src/utils/package-scripts.ts","../src/commands/build.ts","../src/utils/template.ts","../src/commands/create.ts","../src/commands/dev.ts","../src/utils/auth-client.ts","../src/utils/droplets.ts","../src/commands/link.ts","../src/utils/droplet-api.ts","../src/commands/logs.ts","../src/commands/publish.ts","../src/commands/status.ts","../src/commands/validate.ts","../src/index.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const WIDGET_CONFIG_FILE = \"fluid.widget.config.ts\";\n\nexport interface FluidWidgetConfig {\n readonly droplet?: string;\n}\n\nexport interface LoadedWidgetConfig {\n readonly path: string;\n readonly config: Partial<FluidWidgetConfig>;\n}\n\nexport class WidgetConfigParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"WidgetConfigParseError\";\n }\n}\n\nexport function resolveWidgetConfigPath(projectDir: string): string {\n return path.join(projectDir, WIDGET_CONFIG_FILE);\n}\n\nexport async function loadWidgetConfig(\n projectDir: string,\n): Promise<LoadedWidgetConfig> {\n const configPath = resolveWidgetConfigPath(projectDir);\n\n let source: string;\n try {\n source = await readFile(configPath, \"utf-8\");\n } catch (err) {\n if (isNodeError(err) && err.code === \"ENOENT\") {\n return { path: configPath, config: {} };\n }\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Unable to read ${WIDGET_CONFIG_FILE}: ${message}`);\n }\n\n return { path: configPath, config: parseWidgetConfigSource(source) };\n}\n\nexport async function writeWidgetConfig(\n projectDir: string,\n config: FluidWidgetConfig,\n): Promise<void> {\n await writeFile(\n resolveWidgetConfigPath(projectDir),\n formatWidgetConfig(config),\n \"utf-8\",\n );\n}\n\nexport function formatWidgetConfig(config: FluidWidgetConfig): string {\n const lines = [\n 'import type { FluidWidgetConfig } from \"@fluid-app/fluid-cli-widget\";',\n \"\",\n \"const config = {\",\n ];\n\n if (config.droplet) {\n lines.push(` droplet: ${JSON.stringify(config.droplet)},`);\n }\n\n lines.push(\n \"} satisfies FluidWidgetConfig;\",\n \"\",\n \"const widgetConfig: FluidWidgetConfig = config;\",\n \"\",\n \"export default config;\",\n \"export const droplet = widgetConfig.droplet;\",\n 'export { widgetPackage, widgetPackages } from \"./manifest\";',\n \"\",\n );\n return lines.join(\"\\n\");\n}\n\nexport function parseWidgetConfigSource(\n source: string,\n): Partial<FluidWidgetConfig> {\n const objectSource = extractConfigObjectLiteral(source);\n const config = parseConfigObjectProperties(objectSource);\n\n return config;\n}\n\nexport function mergeWidgetConfig(\n current: Partial<FluidWidgetConfig>,\n updates: Partial<FluidWidgetConfig>,\n): FluidWidgetConfig {\n const droplet =\n normalizeOptionalString(updates.droplet) ??\n normalizeOptionalString(current.droplet);\n\n return droplet ? { droplet } : {};\n}\n\nexport function validateDropletUuid(dropletUuid: string): string | undefined {\n if (!dropletUuid) return \"Droplet UUID is required.\";\n if (dropletUuid.length > MAX_DROPLET_UUID_LENGTH) {\n return `Droplet UUID must be ${MAX_DROPLET_UUID_LENGTH} characters or fewer.`;\n }\n if (!URL_SAFE_IDENTIFIER_PATTERN.test(dropletUuid)) {\n return (\n \"Droplet UUID must be URL-safe text: letters, numbers, \" +\n \"'_', '~', and '-' only.\"\n );\n }\n return undefined;\n}\n\nfunction extractConfigObjectLiteral(source: string): string {\n const configStart = /\\bconst\\s+config\\s*=\\s*\\{/.exec(source);\n if (!configStart) {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} must declare \\`const config = { ... } satisfies FluidWidgetConfig\\`.`,\n );\n }\n\n const openBraceIndex = source.indexOf(\"{\", configStart.index);\n const closeBraceIndex = findMatchingBrace(source, openBraceIndex);\n const suffix = stripComments(source.slice(closeBraceIndex + 1));\n if (!/^\\s*(?:as\\s+const\\s+)?satisfies\\s+FluidWidgetConfig\\s*;/.test(suffix)) {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} config object must use \\`satisfies FluidWidgetConfig\\` or \\`as const satisfies FluidWidgetConfig\\`.`,\n );\n }\n\n return stripComments(source.slice(openBraceIndex + 1, closeBraceIndex));\n}\n\nfunction parseConfigObjectProperties(\n objectSource: string,\n): Partial<FluidWidgetConfig> {\n const config: { droplet?: string } = {};\n\n for (const member of splitTopLevelMembers(objectSource)) {\n const trimmedMember = member.trim();\n if (!trimmedMember) continue;\n\n const propertyMatch = /^(droplet)\\s*:\\s*([\\s\\S]+)$/.exec(trimmedMember);\n if (!propertyMatch) {\n const propertyName = /^([A-Za-z_$][\\w$]*)\\s*:/.exec(trimmedMember)?.[1];\n throw new WidgetConfigParseError(\n propertyName\n ? `${WIDGET_CONFIG_FILE} does not support config field ${propertyName}. Supported field is droplet.`\n : `${WIDGET_CONFIG_FILE} config fields must use optional droplet: \"...\" string literal.`,\n );\n }\n\n const propertyName = propertyMatch[1];\n if (propertyName !== \"droplet\") {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} config field could not be parsed.`,\n );\n }\n const valueSource = propertyMatch[2]?.trim() ?? \"\";\n const value = parseStringLiteralValue(valueSource, propertyName);\n if (!value.trim()) {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} field ${propertyName} must be a non-empty string literal.`,\n );\n }\n\n if (config.droplet !== undefined) {\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} field droplet must only be declared once.`,\n );\n }\n config.droplet = value;\n }\n\n return config;\n}\n\nfunction parseStringLiteralValue(\n valueSource: string,\n propertyName: string,\n): string {\n if (valueSource.startsWith('\"') && valueSource.endsWith('\"')) {\n try {\n const parsed = JSON.parse(valueSource) as unknown;\n if (typeof parsed === \"string\") return parsed;\n } catch {\n // Fall through to the actionable error below.\n }\n }\n\n if (valueSource.startsWith(\"'\") && valueSource.endsWith(\"'\")) {\n return parseSingleQuotedStringLiteral(valueSource);\n }\n\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} field ${propertyName} must be a string literal (for example ${propertyName}: \"value\").`,\n );\n}\n\nfunction parseSingleQuotedStringLiteral(valueSource: string): string {\n let value = \"\";\n\n for (let index = 1; index < valueSource.length - 1; index += 1) {\n const char = valueSource[index];\n if (char !== \"\\\\\") {\n value += char;\n continue;\n }\n\n index += 1;\n const escaped = valueSource[index];\n switch (escaped) {\n case \"\\\\\":\n case \"'\":\n case '\"':\n value += escaped;\n break;\n case \"n\":\n value += \"\\n\";\n break;\n case \"r\":\n value += \"\\r\";\n break;\n case \"t\":\n value += \"\\t\";\n break;\n case \"b\":\n value += \"\\b\";\n break;\n case \"f\":\n value += \"\\f\";\n break;\n case \"v\":\n value += \"\\v\";\n break;\n default:\n value += escaped ?? \"\";\n break;\n }\n }\n\n return value;\n}\n\nfunction stripComments(source: string): string {\n let result = \"\";\n let stringQuote: '\"' | \"'\" | \"`\" | undefined;\n let escaped = false;\n let lineComment = false;\n let blockComment = false;\n\n for (let index = 0; index < source.length; index += 1) {\n const char = source[index];\n const nextChar = source[index + 1];\n\n if (lineComment) {\n if (char === \"\\n\" || char === \"\\r\") {\n lineComment = false;\n result += char;\n } else {\n result += \" \";\n }\n continue;\n }\n\n if (blockComment) {\n if (char === \"*\" && nextChar === \"/\") {\n blockComment = false;\n result += \" \";\n index += 1;\n } else if (char === \"\\n\" || char === \"\\r\") {\n result += char;\n } else {\n result += \" \";\n }\n continue;\n }\n\n if (stringQuote) {\n result += char;\n if (escaped) {\n escaped = false;\n } else if (char === \"\\\\\") {\n escaped = true;\n } else if (char === stringQuote) {\n stringQuote = undefined;\n }\n continue;\n }\n\n if (char === \"/\" && nextChar === \"/\") {\n lineComment = true;\n result += \" \";\n index += 1;\n continue;\n }\n\n if (char === \"/\" && nextChar === \"*\") {\n blockComment = true;\n result += \" \";\n index += 1;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n stringQuote = char;\n }\n result += char;\n }\n\n return result;\n}\n\nfunction splitTopLevelMembers(objectSource: string): string[] {\n const members: string[] = [];\n let memberStart = 0;\n let stringQuote: '\"' | \"'\" | \"`\" | undefined;\n let escaped = false;\n let nestedDepth = 0;\n\n for (let index = 0; index < objectSource.length; index += 1) {\n const char = objectSource[index];\n\n if (stringQuote) {\n if (escaped) {\n escaped = false;\n } else if (char === \"\\\\\") {\n escaped = true;\n } else if (char === stringQuote) {\n stringQuote = undefined;\n }\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n stringQuote = char;\n continue;\n }\n\n if (char === \"{\" || char === \"[\" || char === \"(\") {\n nestedDepth += 1;\n continue;\n }\n if (char === \"}\" || char === \"]\" || char === \")\") {\n nestedDepth -= 1;\n continue;\n }\n\n if (char === \",\" && nestedDepth === 0) {\n members.push(objectSource.slice(memberStart, index));\n memberStart = index + 1;\n }\n }\n\n members.push(objectSource.slice(memberStart));\n return members;\n}\n\nfunction findMatchingBrace(source: string, openBraceIndex: number): number {\n let depth = 0;\n let stringQuote: '\"' | \"'\" | \"`\" | undefined;\n let escaped = false;\n let lineComment = false;\n let blockComment = false;\n\n for (let index = openBraceIndex; index < source.length; index += 1) {\n const char = source[index];\n const nextChar = source[index + 1];\n\n if (lineComment) {\n if (char === \"\\n\" || char === \"\\r\") lineComment = false;\n continue;\n }\n\n if (blockComment) {\n if (char === \"*\" && nextChar === \"/\") {\n blockComment = false;\n index += 1;\n }\n continue;\n }\n\n if (stringQuote) {\n if (escaped) {\n escaped = false;\n } else if (char === \"\\\\\") {\n escaped = true;\n } else if (char === stringQuote) {\n stringQuote = undefined;\n }\n continue;\n }\n\n if (char === \"/\" && nextChar === \"/\") {\n lineComment = true;\n index += 1;\n continue;\n }\n\n if (char === \"/\" && nextChar === \"*\") {\n blockComment = true;\n index += 1;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n stringQuote = char;\n continue;\n }\n\n if (char === \"{\") depth += 1;\n if (char === \"}\") {\n depth -= 1;\n if (depth === 0) return index;\n }\n }\n\n throw new WidgetConfigParseError(\n `${WIDGET_CONFIG_FILE} config object is missing a closing brace.`,\n );\n}\n\nfunction normalizeOptionalString(\n value: string | undefined,\n): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\ninterface NodeError extends Error {\n readonly code?: string;\n}\n\nfunction isNodeError(error: unknown): error is NodeError {\n return error instanceof Error && \"code\" in error;\n}\n\nconst MAX_DROPLET_UUID_LENGTH = 256;\nconst URL_SAFE_SEGMENT_SOURCE = \"[A-Za-z0-9][A-Za-z0-9_~-]*\";\nconst URL_SAFE_IDENTIFIER_PATTERN = new RegExp(`^${URL_SAFE_SEGMENT_SOURCE}$`);\n","export interface WidgetCliValidationError {\n readonly path?: string;\n readonly message: string;\n}\n\nexport function formatValidationErrors(\n errors: readonly WidgetCliValidationError[],\n): string {\n if (errors.length === 0) return \"Validation failed.\";\n\n return [\n \"Validation failed:\",\n ...errors.map((error) => {\n const location = error.path ? `${error.path}: ` : \"\";\n return ` - ${location}${error.message}`;\n }),\n ].join(\"\\n\");\n}\n","import { existsSync } from \"node:fs\";\nimport { mkdir, rm, rmdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n loadSourceWidgetPackages,\n validateSingleSourceWidgetPackage,\n} from \"@fluid-app/fluid-cli-portal\";\nimport {\n WIDGET_CONFIG_FILE,\n loadWidgetConfig,\n validateDropletUuid,\n} from \"./config.js\";\nimport type {\n ValidatedWidgetPackageBuild,\n WidgetPackageValidationError,\n} from \"@fluid-app/fluid-cli-portal\";\nimport { formatValidationErrors } from \"./validation.js\";\n\nexport const DEFAULT_WIDGET_OUT_DIR = \".fluid/widget-dist\";\nexport const ROOT_WIDGET_CONFIG_FILE: string = WIDGET_CONFIG_FILE;\n\nconst SOURCE_CONFIG_CANDIDATES = [\n \"src/widgets.config.ts\",\n \"src/portal.config.ts\",\n \"portal.config.ts\",\n] as const;\n\nexport interface WidgetValidationResult {\n readonly projectDir: string;\n readonly sourceConfigPath?: string;\n readonly validated: ValidatedWidgetPackageBuild;\n}\n\nexport class WidgetValidationError extends Error {\n public readonly errors: readonly WidgetPackageValidationError[];\n\n constructor(errors: readonly WidgetPackageValidationError[]) {\n super(formatValidationErrors(errors));\n this.name = \"WidgetValidationError\";\n this.errors = errors;\n }\n}\n\nexport async function validateDropletWidgetProject(\n projectDir: string,\n packageKeyOverride?: string,\n): Promise<WidgetValidationResult> {\n const resolvedProjectDir = path.resolve(projectDir);\n const sourceConfig = resolveWidgetSourceConfig(resolvedProjectDir);\n if (!sourceConfig) {\n throw new WidgetValidationError([\n {\n code: \"NO_SOURCE_PACKAGE\",\n path: ROOT_WIDGET_CONFIG_FILE,\n message:\n `No widget package config found. Create ${ROOT_WIDGET_CONFIG_FILE} ` +\n \"or src/widgets.config.ts that exports defineWidgetPackage(...).\",\n },\n ]);\n }\n\n const configErrors = await validateRootWidgetConfig(\n resolvedProjectDir,\n packageKeyOverride,\n );\n if (configErrors.length > 0) {\n throw new WidgetValidationError(configErrors);\n }\n\n const sourcePackages = await withRootWidgetConfigBridge(\n resolvedProjectDir,\n async () => {\n const loadResult = await loadSourceWidgetPackages(resolvedProjectDir);\n if (!loadResult.success) {\n throw new WidgetValidationError([\n {\n code: \"INVALID_SOURCE_PACKAGE\",\n path: sourceConfig.relativePath,\n message: appendDetails(\n loadResult.error.message,\n loadResult.error.details,\n ),\n },\n ]);\n }\n return loadResult.value;\n },\n packageKeyOverride,\n );\n\n const validation = validateSingleSourceWidgetPackage(sourcePackages, {\n owner: \"droplet\",\n });\n const errors = [\n ...validation.errors,\n ...validateDropletMetadata(sourcePackages),\n ];\n\n if (!validation.success || !validation.value || errors.length > 0) {\n throw new WidgetValidationError(errors);\n }\n\n return {\n projectDir: resolvedProjectDir,\n sourceConfigPath: sourceConfig.relativePath,\n validated: validation.value,\n };\n}\n\nexport async function withRootWidgetConfigBridge<T>(\n projectDir: string,\n callback: () => Promise<T>,\n packageKeyOverride?: string,\n): Promise<T> {\n const rootConfigPath = path.join(projectDir, ROOT_WIDGET_CONFIG_FILE);\n if (!existsSync(rootConfigPath) || resolvePortalSourceConfig(projectDir)) {\n return callback();\n }\n\n const packageKey =\n packageKeyOverride?.trim() ||\n (await loadWidgetConfig(projectDir)).config.droplet;\n if (!packageKey) {\n throw new Error(\n \"fluid.widget.config.ts must set droplet before validating, building, or publishing.\",\n );\n }\n\n const srcDir = path.join(projectDir, \"src\");\n const bridgePath = path.join(srcDir, \"widgets.config.ts\");\n const srcDirExisted = existsSync(srcDir);\n await mkdir(srcDir, { recursive: true });\n try {\n await writeFile(\n bridgePath,\n createRootWidgetConfigBridgeSource(packageKey),\n {\n encoding: \"utf-8\",\n flag: \"wx\",\n },\n );\n } catch (err) {\n if (isNodeError(err) && err.code === \"EEXIST\") {\n throw new Error(\n `${path.relative(projectDir, bridgePath)} already exists. ` +\n \"Remove this stale Fluid widget CLI bridge file and try again.\",\n );\n }\n throw err;\n }\n\n try {\n return await callback();\n } finally {\n await rm(bridgePath, { force: true }).catch(() => {});\n if (!srcDirExisted) await rmdir(srcDir).catch(() => {});\n }\n}\n\nfunction createRootWidgetConfigBridgeSource(packageKey: string): string {\n return `import { widgetPackage as sourceWidgetPackage } from \"../manifest\";\n\nconst packageKey = ${JSON.stringify(packageKey)};\nconst packageId = sourceWidgetPackage.scope + \".\" + packageKey;\n\nexport const widgetPackage = {\n ...sourceWidgetPackage,\n packageStableId: packageKey,\n packageId,\n};\n\nexport const widgetPackages = [widgetPackage] as const;\nexport default widgetPackage;\n`;\n}\n\nexport function resolveWidgetSourceConfig(\n projectDir: string,\n): { readonly path: string; readonly relativePath: string } | undefined {\n const rootConfig = path.join(projectDir, ROOT_WIDGET_CONFIG_FILE);\n if (existsSync(rootConfig)) {\n return { path: rootConfig, relativePath: ROOT_WIDGET_CONFIG_FILE };\n }\n\n return resolvePortalSourceConfig(projectDir);\n}\n\nexport function formatValidationSuccess(\n result: WidgetValidationResult,\n): string {\n return [\n \"Widget package is valid.\",\n `Config: ${result.sourceConfigPath ?? \"unknown\"}`,\n `Package: ${result.validated.packageId}`,\n `Version: ${result.validated.version}`,\n `Widgets: ${result.validated.widgets.length}`,\n ].join(\"\\n\");\n}\n\nasync function validateRootWidgetConfig(\n projectDir: string,\n packageKeyOverride?: string,\n): Promise<WidgetPackageValidationError[]> {\n const configPath = path.join(projectDir, ROOT_WIDGET_CONFIG_FILE);\n if (!existsSync(configPath)) return [];\n\n try {\n const loaded = await loadWidgetConfig(projectDir);\n const droplet = packageKeyOverride ?? loaded.config.droplet ?? \"\";\n const packageKeyError = validateDropletUuid(droplet);\n if (!packageKeyError) return [];\n\n return [\n {\n code: \"INVALID_PACKAGE_KEY\",\n path: ROOT_WIDGET_CONFIG_FILE,\n message: packageKeyError,\n },\n ];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return [\n {\n code: \"INVALID_SOURCE_PACKAGE\",\n path: ROOT_WIDGET_CONFIG_FILE,\n message,\n },\n ];\n }\n}\n\nfunction validateDropletMetadata(\n sourcePackages: readonly unknown[],\n): WidgetPackageValidationError[] {\n if (sourcePackages.length !== 1) return [];\n\n const sourcePackage = sourcePackages[0];\n if (!isRecord(sourcePackage)) return [];\n\n const errors: WidgetPackageValidationError[] = [];\n if (sourcePackage[\"packageType\"] !== \"droplet\") {\n errors.push({\n code: \"UNSUPPORTED_OWNER\",\n path: \"packageType\",\n message:\n 'Droplet widget packages must set packageType: \"droplet\" in package metadata.',\n });\n }\n\n if (!isNonEmptyString(sourcePackage[\"packageStableId\"])) {\n errors.push({\n code: \"INVALID_PACKAGE_KEY\",\n path: \"packageStableId\",\n message:\n \"Droplet widget packages must set packageStableId so published widget types remain stable across builds.\",\n });\n }\n\n const widgets = sourcePackage[\"widgets\"];\n if (Array.isArray(widgets)) {\n widgets.forEach((widget, index) => {\n if (!isRecord(widget)) return;\n validateJsonSerializable(\n widget[\"propertySchema\"],\n `widgets[${index}].propertySchema`,\n errors,\n );\n validateJsonSerializable(\n widget[\"defaultProps\"],\n `widgets[${index}].defaultProps`,\n errors,\n );\n });\n }\n\n return errors;\n}\n\nfunction resolvePortalSourceConfig(\n projectDir: string,\n): { readonly path: string; readonly relativePath: string } | undefined {\n for (const relativePath of SOURCE_CONFIG_CANDIDATES) {\n const candidate = path.join(projectDir, relativePath);\n if (existsSync(candidate)) return { path: candidate, relativePath };\n }\n\n return undefined;\n}\n\nfunction validateJsonSerializable(\n value: unknown,\n pathLabel: string,\n errors: WidgetPackageValidationError[],\n): void {\n if (value === undefined) return;\n\n try {\n assertJsonSerializable(value, pathLabel);\n } catch (err) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: pathLabel,\n message: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\nfunction assertJsonSerializable(value: unknown, pathLabel: string): void {\n if (value === null) return;\n\n const valueType = typeof value;\n if (valueType === \"string\" || valueType === \"boolean\") return;\n if (valueType === \"number\") {\n if (Number.isFinite(value)) return;\n throw new Error(`${pathLabel} must not contain NaN or Infinity.`);\n }\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n assertJsonSerializable(item, `${pathLabel}[${index}]`);\n });\n return;\n }\n if (isRecord(value)) {\n for (const [key, item] of Object.entries(value)) {\n if (item === undefined) {\n throw new Error(`${pathLabel}.${key} must not be undefined.`);\n }\n assertJsonSerializable(item, `${pathLabel}.${key}`);\n }\n return;\n }\n\n throw new Error(`${pathLabel} must contain only JSON-serializable values.`);\n}\n\nfunction appendDetails(message: string, details: string | undefined): string {\n return details ? `${message}\\n${details}` : message;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\ninterface NodeError extends Error {\n readonly code?: string;\n}\n\nfunction isNodeError(error: unknown): error is NodeError {\n return error instanceof Error && \"code\" in error;\n}\n","import chalk from \"chalk\";\n\nexport async function runCliAction(action: () => Promise<void>): Promise<void> {\n try {\n await action();\n } catch (err) {\n console.error(chalk.red(\"Error:\") + \" \" + formatUnknownError(err));\n process.exit(1);\n }\n}\n\nexport function formatUnknownError(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n","import { spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\nexport function getRunCommand(\n script: string,\n cwd: string = process.cwd(),\n): string {\n return `${detectPackageManager(cwd)} run ${script}`;\n}\n\nexport function getInstallCommand(cwd: string = process.cwd()): string {\n return `${detectPackageManager(cwd)} install`;\n}\n\nexport async function installDependencies(cwd: string): Promise<void> {\n const packageManager = detectPackageManager(cwd);\n await runPackageManager(packageManager, [\"install\"], cwd);\n}\n\nexport function detectPackageManager(cwd: string): PackageManager {\n if (existsSync(path.join(cwd, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(path.join(cwd, \"yarn.lock\"))) return \"yarn\";\n if (existsSync(path.join(cwd, \"bun.lockb\"))) return \"bun\";\n if (existsSync(path.join(cwd, \"package-lock.json\"))) return \"npm\";\n\n const invokingPackageManager = readInvokingPackageManager();\n if (invokingPackageManager) return invokingPackageManager;\n\n const declaredPackageManager = readDeclaredPackageManager(cwd);\n if (declaredPackageManager && isCommandAvailable(declaredPackageManager)) {\n return declaredPackageManager;\n }\n\n return findAvailablePackageManager() ?? \"pnpm\";\n}\n\nfunction runPackageManager(\n command: PackageManager,\n args: readonly string[],\n cwd: string,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, [...args], {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n\n child.once(\"error\", (error: Error) => {\n reject(error);\n });\n\n child.once(\"exit\", (code: number | null) => {\n if (code === 0) {\n resolve();\n return;\n }\n\n reject(\n new Error(\n `${command} ${args.join(\" \")} exited with code ${code ?? \"unknown\"}`,\n ),\n );\n });\n });\n}\n\nfunction readInvokingPackageManager(): PackageManager | undefined {\n const userAgent = process.env[\"npm_config_user_agent\"];\n if (!userAgent) return undefined;\n\n const name = userAgent.split(\"/\")[0];\n if (isPackageManager(name)) return name;\n return undefined;\n}\n\nfunction readDeclaredPackageManager(cwd: string): PackageManager | undefined {\n const packageJsonPath = path.join(cwd, \"package.json\");\n if (!existsSync(packageJsonPath)) return undefined;\n\n try {\n const packageJson = JSON.parse(\n readFileSync(packageJsonPath, \"utf-8\"),\n ) as unknown;\n if (!isRecord(packageJson)) return undefined;\n\n const packageManager = packageJson[\"packageManager\"];\n if (typeof packageManager !== \"string\") return undefined;\n\n const name = packageManager.split(\"@\")[0];\n if (isPackageManager(name)) return name;\n } catch {\n // Malformed package.json is reported by command paths that need to parse it.\n }\n\n return undefined;\n}\n\nfunction findAvailablePackageManager(): PackageManager | undefined {\n for (const packageManager of [\"pnpm\", \"yarn\", \"bun\", \"npm\"] as const) {\n if (isCommandAvailable(packageManager)) return packageManager;\n }\n return undefined;\n}\n\nfunction isCommandAvailable(command: PackageManager): boolean {\n const result = spawnSync(command, [\"--version\"], {\n shell: process.platform === \"win32\",\n stdio: \"ignore\",\n });\n return result.status === 0;\n}\n\nfunction isPackageManager(value: string | undefined): value is PackageManager {\n return (\n value === \"pnpm\" || value === \"yarn\" || value === \"bun\" || value === \"npm\"\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { detectPackageManager } from \"./package-manager.js\";\n\nexport interface PackageScriptRunResult {\n readonly ran: boolean;\n readonly command?: string;\n}\n\nexport async function runPackageScriptIfAvailable(options: {\n readonly projectDir: string;\n readonly scriptName: string;\n}): Promise<PackageScriptRunResult> {\n const packageJsonPath = path.join(options.projectDir, \"package.json\");\n if (!existsSync(packageJsonPath)) return { ran: false };\n\n const packageJson = await readPackageJson(packageJsonPath);\n if (!hasPackageScript(packageJson, options.scriptName)) return { ran: false };\n\n const packageManager = detectPackageManager(options.projectDir);\n const args = [\"run\", options.scriptName];\n await runCommand(packageManager, args, options.projectDir);\n\n return {\n ran: true,\n command: `${packageManager} ${args.join(\" \")}`,\n };\n}\n\nfunction runCommand(\n command: string,\n args: readonly string[],\n cwd: string,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: \"inherit\",\n env: process.env,\n shell: process.platform === \"win32\",\n });\n\n child.on(\"error\", reject);\n child.on(\"exit\", (code, signal) => {\n if (code === 0) {\n resolve();\n return;\n }\n\n reject(\n new Error(\n signal\n ? `${command} ${args.join(\" \")} exited with signal ${signal}.`\n : `${command} ${args.join(\" \")} exited with code ${code ?? \"unknown\"}.`,\n ),\n );\n });\n });\n}\n\nasync function readPackageJson(packageJsonPath: string): Promise<unknown> {\n const source = await readFile(packageJsonPath, \"utf-8\");\n try {\n return JSON.parse(source) as unknown;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Unable to parse package.json: ${message}`);\n }\n}\n\nfunction hasPackageScript(value: unknown, scriptName: string): boolean {\n if (!isRecord(value)) return false;\n const scripts = value[\"scripts\"];\n if (!isRecord(scripts)) return false;\n return typeof scripts[scriptName] === \"string\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { Command } from \"commander\";\nimport {\n buildSharedWidgetPackage,\n validateWidgetPublishOutDir,\n} from \"@fluid-app/fluid-cli-portal\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport {\n DEFAULT_WIDGET_OUT_DIR,\n validateDropletWidgetProject,\n withRootWidgetConfigBridge,\n} from \"../utils/source-package.js\";\nimport { runCliAction } from \"../utils/command.js\";\nimport { runPackageScriptIfAvailable } from \"../utils/package-scripts.js\";\n\nexport interface BuildWidgetOptions {\n readonly outDir?: string;\n readonly skipTypecheck?: boolean;\n}\n\nexport interface BuildWidgetPackageOptions {\n readonly projectDir: string;\n readonly outDir: string;\n readonly skipTypecheck?: boolean;\n}\n\nexport interface BuildWidgetPackageResult {\n readonly outDir: string;\n readonly packageId: string;\n readonly version: string;\n}\n\nexport async function buildWidgetPackage(\n options: BuildWidgetPackageOptions,\n): Promise<BuildWidgetPackageResult> {\n await validateDropletWidgetProject(options.projectDir);\n\n if (options.skipTypecheck !== true) {\n await runPackageScriptIfAvailable({\n projectDir: options.projectDir,\n scriptName: \"typecheck\",\n });\n }\n\n const outDir = validateWidgetPublishOutDir(\n options.projectDir,\n options.outDir,\n );\n const buildResult = await withRootWidgetConfigBridge(options.projectDir, () =>\n buildSharedWidgetPackage({\n projectDir: options.projectDir,\n publishDir: outDir,\n owner: \"droplet\",\n }),\n );\n\n if (!buildResult.success) {\n const details = buildResult.error.details\n ? `\\n${buildResult.error.details}`\n : \"\";\n throw new Error(`${buildResult.error.message}${details}`);\n }\n\n return {\n outDir: buildResult.value.publishDir,\n packageId: buildResult.value.packageId,\n version: buildResult.value.version,\n };\n}\n\nexport const buildCommand: Command = new Command(\"build\")\n .description(\"Validate and build the current droplet widget package\")\n .option(\n \"-o, --out-dir <dir>\",\n \"Widget artifact output directory\",\n DEFAULT_WIDGET_OUT_DIR,\n )\n .option(\"--skip-typecheck\", \"Skip package typecheck script when present\")\n .action((options: BuildWidgetOptions) => {\n return runCliAction(async () => {\n const outDir = options.outDir ?? DEFAULT_WIDGET_OUT_DIR;\n console.log(chalk.blue.bold(\"Building Fluid widget package\"));\n console.log();\n\n const spinner = ora(\n \"Validating and building widget artifacts...\",\n ).start();\n const result = await buildWidgetPackage({\n projectDir: process.cwd(),\n outDir,\n skipTypecheck: options.skipTypecheck,\n });\n spinner.succeed(\"Built widget runtime artifacts\");\n\n console.log();\n console.log(chalk.gray(\"Package: \") + chalk.white(result.packageId));\n console.log(chalk.gray(\"Version: \") + chalk.white(result.version));\n console.log(chalk.gray(\"Output: \") + chalk.cyan(outDir));\n });\n });\n","import { mkdir, readdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { WidgetTemplateVariables } from \"../types.js\";\n\nexport function getDefaultTemplatePath(): string {\n return path.join(findPackageRoot(), \"templates\", \"default\");\n}\n\nexport async function directoryExists(filePath: string): Promise<boolean> {\n try {\n const stats = await stat(filePath);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function copyWidgetTemplate(\n templatePath: string,\n targetPath: string,\n variables: WidgetTemplateVariables,\n): Promise<void> {\n const files = await getFiles(templatePath);\n\n for (const file of files) {\n const sourcePath = path.join(templatePath, file);\n const isTemplate = file.endsWith(\".template\");\n const outputFiles = getOutputFilenames(file);\n\n const content = await readFile(sourcePath, \"utf-8\");\n const processed = isTemplate ? renderTemplate(content, variables) : content;\n\n await writeOutputFiles(targetPath, outputFiles, processed);\n }\n\n for (const skillFile of await getSharedTemplateSkillFiles()) {\n await writeOutputFiles(\n targetPath,\n getOutputFilenames(skillFile.relativePath),\n skillFile.content,\n );\n }\n}\n\nexport async function getPortalSdkVersion(\n options: {\n readonly local?: boolean;\n readonly targetPath?: string;\n } = {},\n): Promise<string> {\n return getWorkspacePackageVersion([\"portal\", \"sdk\"], \"^0.1.0\", options);\n}\n\nexport async function getPortalCoreVersion(\n options: {\n readonly local?: boolean;\n readonly targetPath?: string;\n } = {},\n): Promise<string> {\n return getWorkspacePackageVersion([\"portal\", \"core\"], \"^0.1.0\", options);\n}\n\nexport async function getCliVersion(\n options: {\n readonly local?: boolean;\n readonly targetPath?: string;\n } = {},\n): Promise<string> {\n return getWorkspacePackageVersion([\"cli\", \"core\"], \"^0.1.0\", options);\n}\n\nexport async function getWidgetCliVersion(\n options: {\n readonly local?: boolean;\n readonly targetPath?: string;\n } = {},\n): Promise<string> {\n return getWorkspacePackageVersion([\"cli\", \"widget\"], \"^0.1.0\", options);\n}\n\nasync function getWorkspacePackageVersion(\n packagePathSegments: readonly string[],\n fallback: string,\n options: { readonly local?: boolean; readonly targetPath?: string },\n): Promise<string> {\n try {\n const packageRoot = findPackageRoot();\n const packagesRoot = path.join(packageRoot, \"..\", \"..\");\n const targetPackageRoot = path.join(packagesRoot, ...packagePathSegments);\n\n if (options.local) {\n const linkPath = (\n options.targetPath\n ? path.relative(options.targetPath, targetPackageRoot)\n : targetPackageRoot\n ).replace(/\\\\/g, \"/\");\n return `link:${linkPath || \".\"}`;\n }\n\n const packageJsonPath = path.join(targetPackageRoot, \"package.json\");\n const content = await readFile(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as { version?: string };\n return `^${pkg.version ?? fallback.replace(/^\\^/, \"\")}`;\n } catch {\n return fallback;\n }\n}\n\nasync function getFiles(dir: string, baseDir = dir): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: string[] = [];\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await getFiles(fullPath, baseDir)));\n } else {\n files.push(path.relative(baseDir, fullPath));\n }\n }\n\n return files;\n}\n\ninterface TemplateSkillFile {\n readonly relativePath: string;\n readonly content: string;\n}\n\nasync function getSharedTemplateSkillFiles(): Promise<TemplateSkillFile[]> {\n const packageRoot = findFluidCliPackageRoot();\n const skillsRoot = path.join(packageRoot, \"template-skills\");\n const files = await getFiles(skillsRoot);\n\n return Promise.all(\n files.map(async (file) => ({\n relativePath: path.join(\"skills\", file),\n content: await readFile(path.join(skillsRoot, file), \"utf-8\"),\n })),\n );\n}\n\nfunction findFluidCliPackageRoot(): string {\n const workspacePackageRoot = path.join(findPackageRoot(), \"..\", \"core\");\n if (existsSync(path.join(workspacePackageRoot, \"template-skills\"))) {\n return workspacePackageRoot;\n }\n\n let dir = path.dirname(\n fileURLToPath(import.meta.resolve(\"@fluid-app/fluid-cli\")),\n );\n while (!existsSync(path.join(dir, \"package.json\"))) {\n const parent = path.dirname(dir);\n if (parent === dir)\n throw new Error(\"Could not find Fluid CLI package root\");\n dir = parent;\n }\n return dir;\n}\n\nfunction renderTemplate(\n content: string,\n variables: WidgetTemplateVariables,\n): string {\n const withConditionals = content.replace(\n /{{\\s*#if\\s+([a-zA-Z0-9_]+)\\s*}}([\\s\\S]*?){{\\s*\\/if\\s*}}/g,\n (match, key, block) => {\n if (!isTemplateVariableKey(key)) return match;\n return variables[key] ? String(block) : \"\";\n },\n );\n\n return withConditionals.replace(\n /{{\\s*([a-zA-Z0-9_]+)\\s*}}/g,\n (match, key) => {\n if (isTemplateVariableKey(key)) return variables[key] ?? \"\";\n return match;\n },\n );\n}\n\nfunction isTemplateVariableKey(\n key: string,\n): key is keyof WidgetTemplateVariables {\n return [\n \"projectName\",\n \"widgetScope\",\n \"widgetPackageVersion\",\n \"sdkVersion\",\n \"localCoreVersion\",\n \"fluidCliVersion\",\n \"widgetCliVersion\",\n \"droplet\",\n ].includes(key);\n}\n\nasync function writeOutputFiles(\n targetPath: string,\n outputFiles: readonly string[],\n content: string,\n): Promise<void> {\n for (const outputFile of outputFiles) {\n const destinationPath = path.join(targetPath, outputFile);\n await mkdir(path.dirname(destinationPath), { recursive: true });\n await writeFile(destinationPath, content, \"utf-8\");\n }\n}\n\nfunction getOutputFilenames(filename: string): string[] {\n const outputFilename = getOutputFilename(filename);\n const normalized = outputFilename.replace(/\\\\/g, \"/\");\n\n if (normalized === \"AGENTS.md\") return [\"AGENTS.md\", \"CLAUDE.md\"];\n\n if (normalized.startsWith(\"skills/\")) {\n return [\n path.join(\".agents\", outputFilename),\n path.join(\".claude\", outputFilename),\n ];\n }\n\n return [outputFilename];\n}\n\nfunction getOutputFilename(filename: string): string {\n return filename.endsWith(\".template\")\n ? filename.slice(0, -\".template\".length)\n : filename;\n}\n\nfunction findPackageRoot(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (!existsSync(path.join(dir, \"package.json\"))) {\n const parent = path.dirname(dir);\n if (parent === dir)\n throw new Error(\"Could not find widget CLI package root\");\n dir = parent;\n }\n return dir;\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport path from \"node:path\";\nimport type { WidgetCreateOptions } from \"../types.js\";\nimport {\n copyWidgetTemplate,\n directoryExists,\n getCliVersion,\n getDefaultTemplatePath,\n getPortalCoreVersion,\n getPortalSdkVersion,\n getWidgetCliVersion,\n} from \"../utils/template.js\";\nimport {\n getInstallCommand,\n getRunCommand,\n installDependencies,\n} from \"../utils/package-manager.js\";\nimport {\n mergeWidgetConfig,\n validateDropletUuid,\n writeWidgetConfig,\n} from \"../utils/config.js\";\n\nconst DEFAULT_WIDGET_PACKAGE_VERSION = \"0.1.0\";\nconst DEFAULT_WIDGET_SCOPE = \"droplet\";\n\nexport const createCommand: Command = new Command(\"create\")\n .description(\"Create a standalone Fluid widget project\")\n .argument(\"<project-name>\", \"Name of the widget project to create\")\n .option(\"--skip-install\", \"Skip dependency installation\")\n .option(\n \"-o, --output-dir <dir>\",\n \"Directory to create the project in (defaults to cwd)\",\n )\n .option(\n \"--scope <scope>\",\n \"Widget package scope/namespace\",\n DEFAULT_WIDGET_SCOPE,\n )\n .option(\n \"--package-version <version>\",\n \"Initial widget package version\",\n DEFAULT_WIDGET_PACKAGE_VERSION,\n )\n .option(\n \"--local\",\n \"Use local monorepo packages via file: links (for development testing)\",\n )\n .option(\"--droplet <uuid>\", \"Existing droplet UUID that owns the package\")\n .action(async (projectName: string, options: WidgetCreateOptions) => {\n try {\n await createWidgetProject(projectName, options);\n } catch (err) {\n console.log();\n console.error(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n console.log();\n process.exit(1);\n }\n });\n\nexport async function createWidgetProject(\n projectName: string,\n options: WidgetCreateOptions,\n): Promise<void> {\n console.log();\n console.log(chalk.bold(\"Creating a new Fluid widget project\"));\n console.log();\n\n const projectNameError = validateProjectName(projectName);\n if (projectNameError) throw new Error(projectNameError);\n\n const droplet = options.droplet?.trim();\n if (droplet) {\n const dropletError = validateDropletUuid(droplet);\n if (dropletError) throw new Error(dropletError);\n }\n\n const outputDir = path.resolve(options.outputDir ?? process.cwd());\n const targetPath = path.join(outputDir, projectName);\n if (await directoryExists(targetPath)) {\n throw new Error(`Directory already exists: ${targetPath}`);\n }\n\n const templatePath = getDefaultTemplatePath();\n if (!(await directoryExists(templatePath))) {\n throw new Error(\"Default widget template not found.\");\n }\n\n const config = mergeWidgetConfig({}, { droplet });\n const widgetScope = options.scope ?? DEFAULT_WIDGET_SCOPE;\n const localOptions = { local: options.local === true, targetPath };\n const variables = {\n projectName,\n droplet: config.droplet,\n widgetScope,\n widgetPackageVersion:\n options.packageVersion ?? DEFAULT_WIDGET_PACKAGE_VERSION,\n sdkVersion: await getPortalSdkVersion(localOptions),\n localCoreVersion: options.local\n ? await getPortalCoreVersion(localOptions)\n : undefined,\n fluidCliVersion: await getCliVersion(localOptions),\n widgetCliVersion: await getWidgetCliVersion(localOptions),\n };\n\n const spinner = ora(\"Copying widget template...\").start();\n try {\n await copyWidgetTemplate(templatePath, targetPath, variables);\n await writeWidgetConfig(targetPath, config);\n spinner.succeed(\"Copied widget template\");\n } catch (err) {\n spinner.fail(\"Failed to create widget project\");\n throw err;\n }\n\n const installCommand = getInstallCommand(targetPath);\n if (options.skipInstall !== true) {\n spinner.start(`Installing dependencies with ${installCommand}...`);\n try {\n await installDependencies(targetPath);\n spinner.succeed(\"Installed dependencies\");\n } catch {\n spinner.fail(\"Failed to install dependencies\");\n console.log();\n console.log(chalk.yellow(\"You can install dependencies manually:\"));\n console.log(chalk.cyan(` cd ${formatCdPath(targetPath)}`));\n console.log(chalk.cyan(` ${installCommand}`));\n }\n }\n\n console.log();\n console.log(\n chalk.green.bold(\"Success!\") + ` Created ${chalk.cyan(projectName)}`,\n );\n console.log();\n console.log(\"Next steps:\");\n console.log(chalk.cyan(` cd ${formatCdPath(targetPath)}`));\n if (options.skipInstall === true) {\n console.log(chalk.cyan(` ${installCommand}`));\n }\n if (!config.droplet) {\n console.log(chalk.cyan(\" fluid widget link\"));\n }\n console.log(chalk.cyan(` ${getRunCommand(\"dev\", targetPath)}`));\n console.log();\n console.log(\n \"Edit \" + chalk.cyan(\"manifest.ts\") + \" to customize your widget package.\",\n );\n console.log(\n chalk.dim(\n \"This command only creates local project files; it does not create a droplet.\",\n ),\n );\n console.log();\n}\n\nfunction validateProjectName(projectName: string): string | undefined {\n if (!projectName) return \"Project name is required.\";\n if (!/^[a-z0-9][a-z0-9-]*$/.test(projectName)) {\n return \"Project name must contain only lowercase letters, numbers, and hyphens.\";\n }\n return undefined;\n}\n\nfunction formatCdPath(targetPath: string): string {\n return path.relative(process.cwd(), targetPath) || \".\";\n}\n","import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { runCliAction } from \"../utils/command.js\";\n\nexport const DEFAULT_WIDGET_DEV_PORT = 5174;\n\nexport const WIDGET_DEV_ROUTES = [\n \"/\",\n \"/builder-preview\",\n \"/__manifests__\",\n \"/__preview__\",\n \"/__runtime-entry__\",\n] as const;\n\nexport interface WidgetDevOptions {\n readonly port?: string;\n readonly host?: boolean | string;\n readonly mode?: string;\n}\n\nexport interface WidgetDevProjectSummary {\n readonly projectDir: string;\n readonly packageName: string;\n readonly viteConfigPath: string;\n readonly localUrl: string;\n readonly target: string;\n readonly mode: string;\n readonly routes: readonly string[];\n}\n\nexport const devCommand: Command = new Command(\"dev\")\n .description(\"Start the local Fluid widget preview development server\")\n .option(\n \"-p, --port <port>\",\n \"Port to run the widget dev server on\",\n String(DEFAULT_WIDGET_DEV_PORT),\n )\n .option(\"--host [host]\", \"Expose the dev server to the network\")\n .option(\"--mode <mode>\", \"Vite mode to run\", \"development\")\n .action((options: WidgetDevOptions) => {\n return runCliAction(async () => {\n const projectDir = process.cwd();\n const summary = await resolveWidgetDevProject(projectDir, options);\n\n printWidgetDevSummary(summary);\n await runViteDevServer(projectDir, options);\n });\n });\n\nexport async function resolveWidgetDevProject(\n projectDir: string,\n options: WidgetDevOptions = {},\n): Promise<WidgetDevProjectSummary> {\n const resolvedProjectDir = path.resolve(projectDir);\n const packageJsonPath = path.join(resolvedProjectDir, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n throw new Error(\n \"No package.json found. Run this command from a Fluid widget project directory.\",\n );\n }\n\n const packageJson = await readPackageJson(packageJsonPath);\n const packageName = readPackageName(packageJson) ?? path.basename(projectDir);\n\n const viteConfigPath = path.join(resolvedProjectDir, \"vite.config.ts\");\n if (!existsSync(viteConfigPath)) {\n throw new Error(\n \"No vite.config.ts found. Run this command from a Fluid widget project directory.\",\n );\n }\n\n const port = parsePort(options.port ?? String(DEFAULT_WIDGET_DEV_PORT));\n const host = normalizeHostOption(options.host);\n const mode = normalizeMode(options.mode);\n\n return {\n projectDir: resolvedProjectDir,\n packageName,\n viteConfigPath,\n localUrl: formatLocalUrl(port, host),\n target: packageName,\n mode,\n routes: WIDGET_DEV_ROUTES,\n };\n}\n\nexport function buildViteDevArgs(options: WidgetDevOptions = {}): string[] {\n const port = parsePort(options.port ?? String(DEFAULT_WIDGET_DEV_PORT));\n const host = normalizeHostOption(options.host);\n const mode = normalizeMode(options.mode);\n const args = [\"--port\", String(port), \"--mode\", mode];\n\n if (host) {\n args.push(\"--host\", host);\n }\n\n return args;\n}\n\nexport function printWidgetDevSummary(summary: WidgetDevProjectSummary): void {\n console.log();\n console.log(chalk.blue.bold(\"Fluid Widget Dev\"));\n console.log(chalk.gray(\"Local: \") + chalk.cyan(summary.localUrl));\n console.log(chalk.gray(\"Target: \") + chalk.white(summary.target));\n console.log(chalk.gray(\"Mode: \") + chalk.white(summary.mode));\n console.log(chalk.gray(\"Routes:\"));\n for (const route of summary.routes) {\n console.log(chalk.gray(\" - \") + chalk.cyan(route));\n }\n console.log();\n}\n\nexport async function runViteDevServer(\n projectDir: string,\n options: WidgetDevOptions = {},\n): Promise<void> {\n const viteCliPath = resolveViteCliPath(projectDir);\n if (!viteCliPath) {\n throw new Error(\n \"Unable to find Vite in node_modules. Run your package manager install command before starting widget dev.\",\n );\n }\n\n await runNodeScript(viteCliPath, buildViteDevArgs(options), projectDir);\n}\n\nfunction resolveViteCliPath(projectDir: string): string | undefined {\n const viteCliPath = path.join(\n projectDir,\n \"node_modules\",\n \"vite\",\n \"bin\",\n \"vite.js\",\n );\n return existsSync(viteCliPath) ? viteCliPath : undefined;\n}\n\nasync function readPackageJson(packageJsonPath: string): Promise<unknown> {\n try {\n return JSON.parse(await readFile(packageJsonPath, \"utf-8\")) as unknown;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Unable to read package.json: ${message}`);\n }\n}\n\nfunction readPackageName(packageJson: unknown): string | undefined {\n if (!isRecord(packageJson)) return undefined;\n const name = packageJson[\"name\"];\n if (typeof name !== \"string\") return undefined;\n const trimmed = name.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction parsePort(value: string): number {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65_535) {\n throw new Error(\"Port must be an integer between 1 and 65535.\");\n }\n return port;\n}\n\nfunction normalizeHostOption(\n host: WidgetDevOptions[\"host\"],\n): string | undefined {\n if (host === undefined || host === false) return undefined;\n if (host === true) return \"0.0.0.0\";\n\n const trimmed = host.trim();\n return trimmed.length > 0 ? trimmed : \"0.0.0.0\";\n}\n\nfunction normalizeMode(mode: string | undefined): string {\n const trimmed = mode?.trim();\n return trimmed && trimmed.length > 0 ? trimmed : \"development\";\n}\n\nfunction formatLocalUrl(port: number, host: string | undefined): string {\n const displayHost = host && host !== \"0.0.0.0\" ? host : \"localhost\";\n return `http://${displayHost}:${port}`;\n}\n\nfunction runNodeScript(\n scriptPath: string,\n args: readonly string[],\n cwd: string,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(process.execPath, [scriptPath, ...args], {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n\n child.on(\"error\", reject);\n child.on(\"exit\", (code, signal) => {\n if (code === 0 || signal === \"SIGINT\" || signal === \"SIGTERM\") {\n resolve();\n return;\n }\n\n reject(\n new Error(\n signal\n ? `Vite dev server exited with signal ${signal}.`\n : `Vite dev server exited with code ${code ?? \"unknown\"}.`,\n ),\n );\n });\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { createAuthenticatedWidgetPackageClient } from \"@fluid-app/fluid-cli-portal\";\nimport type { FetchClient } from \"@fluid-app/fluid-cli\";\n\nexport interface AuthenticatedClientResult {\n readonly client: FetchClient;\n}\n\nexport function createAuthenticatedClient(): AuthenticatedClientResult {\n return { client: createAuthenticatedWidgetPackageClient() };\n}\n","import prompts from \"prompts\";\nimport type { FetchClient } from \"@fluid-app/fluid-cli\";\nimport type { Choice } from \"prompts\";\n\nexport interface DropletChoice {\n readonly uuid: string;\n readonly name: string;\n}\n\nexport async function promptForDroplet(\n client: FetchClient,\n): Promise<string | undefined> {\n const droplets = await listDroplets(client);\n\n if (droplets.length === 0) {\n throw new Error(\n \"No droplets were found for this account. Create a droplet in Fluid first, then run this command again.\",\n );\n }\n\n const response = await prompts({\n type: \"autocomplete\",\n name: \"droplet\",\n message: \"Select the droplet that owns this widget package\",\n choices: droplets.map((droplet) => ({\n title: `${droplet.name} (${droplet.uuid})`,\n value: droplet.uuid,\n })),\n suggest: (input: string, choices: Choice[]) =>\n Promise.resolve(\n input\n ? choices.filter((choice) =>\n choice.title.toLowerCase().includes(input.toLowerCase()),\n )\n : choices,\n ),\n });\n\n return response[\"droplet\"] as string | undefined;\n}\n\nexport async function listDroplets(\n client: FetchClient,\n): Promise<readonly DropletChoice[]> {\n try {\n const response = await client.get<unknown>(\"/api/droplets\");\n return normalizeDropletsResponse(response);\n } catch (err) {\n if (isApiLikeError(err)) {\n if (err.status === 401 || err.status === 403) {\n throw new Error(\n \"Your Fluid session has expired. Run `fluid login` and try again.\",\n );\n }\n throw new Error(\n `Could not fetch droplets from Fluid (HTTP ${err.status}). ${err.message}`,\n );\n }\n\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Could not fetch droplets from Fluid. ${message}`);\n }\n}\n\nfunction normalizeDropletsResponse(response: unknown): DropletChoice[] {\n const candidates = extractDropletArray(response);\n return candidates\n .map(normalizeDroplet)\n .filter((droplet): droplet is DropletChoice => droplet !== undefined);\n}\n\nfunction extractDropletArray(response: unknown): readonly unknown[] {\n if (Array.isArray(response)) return response;\n if (!isRecord(response)) return [];\n\n const directDroplets = response[\"droplets\"];\n if (Array.isArray(directDroplets)) return directDroplets;\n\n const data = response[\"data\"];\n if (Array.isArray(data)) return data;\n if (isRecord(data) && Array.isArray(data[\"droplets\"])) {\n return data[\"droplets\"];\n }\n\n return [];\n}\n\nfunction normalizeDroplet(value: unknown): DropletChoice | undefined {\n if (!isRecord(value)) return undefined;\n\n const uuid = readString(value, \"uuid\") ?? readString(value, \"id\");\n if (!uuid) return undefined;\n\n const name =\n readString(value, \"name\") ??\n readString(value, \"title\") ??\n readString(value, \"slug\") ??\n uuid;\n\n return { uuid, name };\n}\n\nfunction readString(\n record: Readonly<Record<string, unknown>>,\n key: string,\n): string | undefined {\n const value = record[key];\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction isApiLikeError(\n error: unknown,\n): error is { readonly status: number; readonly message: string } {\n return (\n error instanceof Error &&\n \"status\" in error &&\n typeof (error as { readonly status?: unknown }).status === \"number\"\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import path from \"node:path\";\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport type { WidgetLinkOptions } from \"../types.js\";\nimport { createAuthenticatedClient } from \"../utils/auth-client.js\";\nimport { promptForDroplet } from \"../utils/droplets.js\";\nimport {\n WIDGET_CONFIG_FILE,\n loadWidgetConfig,\n mergeWidgetConfig,\n validateDropletUuid,\n writeWidgetConfig,\n} from \"../utils/config.js\";\n\nexport const linkCommand: Command = new Command(\"link\")\n .description(\"Link this widget project to an existing Fluid droplet\")\n .option(\"--droplet <uuid>\", \"Droplet UUID that owns the widget package\")\n .action(async (options: WidgetLinkOptions) => {\n try {\n const projectDir = process.cwd();\n const loaded = await loadWidgetConfig(projectDir);\n\n let droplet = options.droplet?.trim() || undefined;\n if (!droplet) {\n const spinner = ora(\"Fetching droplets...\").start();\n try {\n const { client } = createAuthenticatedClient();\n spinner.stop();\n droplet = await promptForDroplet(client);\n } catch (err) {\n spinner.fail(\"Unable to load droplets\");\n throw err;\n }\n\n if (!droplet) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n }\n\n const nextConfig = mergeWidgetConfig(loaded.config, { droplet });\n const nextDropletError = validateDropletUuid(nextConfig.droplet ?? \"\");\n if (nextDropletError) throw new Error(nextDropletError);\n\n await writeWidgetConfig(projectDir, nextConfig);\n\n console.log();\n console.log(chalk.green(\"Linked widget project.\"));\n console.log(\n chalk.gray(\"Config: \") +\n chalk.cyan(\n path.relative(projectDir, loaded.path) || WIDGET_CONFIG_FILE,\n ),\n );\n console.log(\n chalk.gray(\"Droplet: \") + chalk.white(nextConfig.droplet),\n );\n console.log(\n chalk.gray(\"Package key: \") +\n chalk.white(`${nextConfig.droplet} (derived from droplet)`),\n );\n console.log();\n } catch (err) {\n console.log();\n console.error(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n console.log();\n process.exit(1);\n }\n });\n","import type { FetchClient } from \"@fluid-app/fluid-cli\";\n\nexport interface DropletWidgetVersionSummary {\n readonly packageKey?: string;\n readonly packageId?: string;\n readonly version?: string;\n readonly status?: string;\n readonly createdAt?: string;\n readonly updatedAt?: string;\n readonly artifactCount?: number;\n readonly raw: unknown;\n}\n\nexport interface DropletWidgetLogEntry {\n readonly timestamp?: string;\n readonly level?: string;\n readonly message: string;\n readonly raw: unknown;\n}\n\nexport async function fetchDropletWidgetStatus(options: {\n readonly client: FetchClient;\n readonly droplet: string;\n}): Promise<DropletWidgetVersionSummary[]> {\n const response = await options.client.get<unknown>(\n `/api/droplets/${encodeURIComponent(options.droplet)}/widget_package_versions`,\n );\n return extractArray(response).map(normalizeVersionSummary);\n}\n\nexport async function fetchDropletWidgetLogs(options: {\n readonly client: FetchClient;\n readonly droplet: string;\n readonly limit?: number;\n}): Promise<DropletWidgetLogEntry[]> {\n const response = await options.client.get<unknown>(\n `/api/droplets/${encodeURIComponent(options.droplet)}/widget_package_versions/logs`,\n options.limit ? { limit: options.limit } : undefined,\n );\n return extractArray(response).map(normalizeLogEntry);\n}\n\nexport function formatStatusRows(\n versions: readonly DropletWidgetVersionSummary[],\n): string {\n if (versions.length === 0) return \"No widget package versions found.\";\n\n return versions\n .map((version) => {\n const identity =\n version.packageId ?? version.packageKey ?? \"unknown-package\";\n const release = version.version ?? \"unknown-version\";\n const status = version.status ?? \"unknown\";\n const updated = version.updatedAt ?? version.createdAt ?? \"unknown time\";\n const artifacts =\n version.artifactCount === undefined\n ? \"unknown artifacts\"\n : `${version.artifactCount} artifact${version.artifactCount === 1 ? \"\" : \"s\"}`;\n return `${identity}@${release} — ${status} — ${artifacts} — ${updated}`;\n })\n .join(\"\\n\");\n}\n\nexport function formatLogRows(logs: readonly DropletWidgetLogEntry[]): string {\n if (logs.length === 0) return \"No widget package logs found.\";\n\n return logs\n .map((entry) => {\n const timestamp = entry.timestamp ?? \"unknown time\";\n const level = entry.level ? entry.level.toUpperCase() : \"INFO\";\n return `[${timestamp}] ${level} ${entry.message}`;\n })\n .join(\"\\n\");\n}\n\nfunction normalizeVersionSummary(value: unknown): DropletWidgetVersionSummary {\n const record = unwrapKnownNestedRecord(value);\n const artifacts = readArray(record, \"artifacts\");\n return {\n packageKey: readFirstString(record, [\"package_key\", \"packageKey\"]),\n packageId: readFirstString(record, [\"package_id\", \"packageId\"]),\n version: readFirstString(record, [\"version\", \"name\"]),\n status: readFirstString(record, [\"status\", \"state\"]),\n createdAt: readFirstString(record, [\"created_at\", \"createdAt\"]),\n updatedAt: readFirstString(record, [\"updated_at\", \"updatedAt\"]),\n ...(artifacts ? { artifactCount: artifacts.length } : {}),\n raw: value,\n };\n}\n\nfunction normalizeLogEntry(value: unknown): DropletWidgetLogEntry {\n const record = unwrapKnownNestedRecord(value);\n return {\n timestamp: readFirstString(record, [\n \"timestamp\",\n \"created_at\",\n \"createdAt\",\n ]),\n level: readFirstString(record, [\"level\", \"severity\"]),\n message:\n readFirstString(record, [\"message\", \"event\", \"status\", \"state\"]) ??\n JSON.stringify(value),\n raw: value,\n };\n}\n\nfunction extractArray(value: unknown): unknown[] {\n if (Array.isArray(value)) return value;\n if (!isRecord(value)) return [];\n\n for (const key of [\n \"widget_package_versions\",\n \"widgetPackageVersions\",\n \"versions\",\n \"logs\",\n \"events\",\n \"data\",\n ] as const) {\n const nested = value[key];\n if (Array.isArray(nested)) return nested;\n }\n\n return [value];\n}\n\nfunction unwrapKnownNestedRecord(value: unknown): Record<string, unknown> {\n if (!isRecord(value)) return {};\n for (const key of [\n \"widget_package_version\",\n \"widgetPackageVersion\",\n \"version\",\n \"log\",\n \"event\",\n ] as const) {\n const nested = value[key];\n if (isRecord(nested)) return nested;\n }\n return value;\n}\n\nfunction readArray(\n record: Record<string, unknown>,\n key: string,\n): readonly unknown[] | undefined {\n const value = record[key];\n return Array.isArray(value) ? value : undefined;\n}\n\nfunction readFirstString(\n record: Record<string, unknown>,\n keys: readonly string[],\n): string | undefined {\n for (const key of keys) {\n const value = record[key];\n if (typeof value === \"string\" && value.trim().length > 0) return value;\n if (typeof value === \"number\" && Number.isFinite(value))\n return String(value);\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { Command } from \"commander\";\nimport { createAuthenticatedWidgetPackageClient } from \"@fluid-app/fluid-cli-portal\";\nimport chalk from \"chalk\";\nimport { loadWidgetConfig, validateDropletUuid } from \"../utils/config.js\";\nimport { fetchDropletWidgetLogs, formatLogRows } from \"../utils/droplet-api.js\";\nimport { runCliAction } from \"../utils/command.js\";\n\nexport interface LogsCommandOptions {\n readonly droplet?: string;\n readonly limit?: string;\n readonly json?: boolean;\n}\n\nexport const logsCommand: Command = new Command(\"logs\")\n .description(\"Show recent widget package publish logs for a Droplet\")\n .option(\"--droplet <uuid>\", \"Droplet UUID that owns the widget package\")\n .option(\"--limit <number>\", \"Maximum number of log entries to request\", \"50\")\n .option(\"--json\", \"Print raw log response as JSON\")\n .action((options: LogsCommandOptions) => {\n return runCliAction(async () => {\n const droplet = await resolveDroplet(process.cwd(), options.droplet);\n const limit = parseLimit(options.limit);\n const logs = await fetchDropletWidgetLogs({\n client: createAuthenticatedWidgetPackageClient(),\n droplet,\n limit,\n });\n\n if (options.json === true) {\n console.log(\n JSON.stringify(\n logs.map((entry) => entry.raw),\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(chalk.blue.bold(`Widget logs for droplet ${droplet}`));\n console.log();\n console.log(formatLogRows(logs));\n });\n });\n\nasync function resolveDroplet(\n projectDir: string,\n override: string | undefined,\n): Promise<string> {\n const droplet =\n override?.trim() || (await loadWidgetConfig(projectDir)).config.droplet;\n if (droplet?.trim()) {\n const trimmedDroplet = droplet.trim();\n const dropletError = validateDropletUuid(trimmedDroplet);\n if (dropletError) throw new Error(dropletError);\n return trimmedDroplet;\n }\n\n throw new Error(\n \"Missing droplet UUID. Pass --droplet <uuid> or set droplet in fluid.widget.config.ts.\",\n );\n}\n\nfunction parseLimit(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const parsed = Number(value);\n if (Number.isInteger(parsed) && parsed > 0 && parsed <= 500) return parsed;\n throw new Error(\"--limit must be an integer between 1 and 500.\");\n}\n","import { Command } from \"commander\";\nimport {\n createAuthenticatedWidgetPackageClient,\n printDryRunSessionPayload,\n printRuntimeOnlyNotice,\n printWidgetPublishSummary,\n publishWidgetRuntimeArtifacts,\n} from \"@fluid-app/fluid-cli-portal\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport {\n DEFAULT_WIDGET_OUT_DIR,\n validateDropletWidgetProject,\n withRootWidgetConfigBridge,\n} from \"../utils/source-package.js\";\nimport { loadWidgetConfig, validateDropletUuid } from \"../utils/config.js\";\nimport { runCliAction } from \"../utils/command.js\";\nimport { runPackageScriptIfAvailable } from \"../utils/package-scripts.js\";\n\nexport interface PublishWidgetPackageOptions {\n readonly projectDir: string;\n readonly outDir: string;\n readonly droplet?: string;\n readonly dryRun?: boolean;\n readonly skipTypecheck?: boolean;\n readonly client?: unknown;\n}\n\nexport interface PublishWidgetPackageResult {\n readonly dryRun: boolean;\n readonly uploaded: boolean;\n readonly outDir: string;\n readonly droplet?: string;\n}\n\nexport interface PublishWidgetPackageDependencies {\n readonly buildWidgetPackage: (options: {\n readonly projectDir: string;\n readonly outDir: string;\n }) => Promise<void>;\n readonly uploadWidgetPackage: (options: {\n readonly outDir: string;\n readonly droplet?: string;\n readonly client?: unknown;\n }) => Promise<void>;\n}\n\nexport async function publishWidgetPackage(\n options: PublishWidgetPackageOptions,\n dependencies?: PublishWidgetPackageDependencies,\n): Promise<PublishWidgetPackageResult> {\n if (dependencies) {\n await dependencies.buildWidgetPackage({\n projectDir: options.projectDir,\n outDir: options.outDir,\n });\n\n const dryRun = options.dryRun === true;\n if (dryRun) {\n return { dryRun, uploaded: false, outDir: options.outDir };\n }\n\n await dependencies.uploadWidgetPackage({\n outDir: options.outDir,\n droplet: options.droplet,\n client: options.client,\n });\n return { dryRun, uploaded: true, outDir: options.outDir };\n }\n\n const droplet = await resolvePublishDroplet(\n options.projectDir,\n options.droplet,\n );\n\n await validateDropletWidgetProject(options.projectDir, droplet);\n if (options.skipTypecheck !== true) {\n await runPackageScriptIfAvailable({\n projectDir: options.projectDir,\n scriptName: \"typecheck\",\n });\n }\n\n const dryRun = options.dryRun === true;\n await withRootWidgetConfigBridge(\n options.projectDir,\n () =>\n publishWidgetRuntimeArtifacts({\n projectDir: options.projectDir,\n outDir: options.outDir,\n buildOwner: \"droplet\",\n uploadOwner: { kind: \"droplet\", uuid: droplet },\n dryRun,\n ...(dryRun ? {} : { client: createAuthenticatedWidgetPackageClient() }),\n }),\n droplet,\n );\n\n return {\n dryRun,\n uploaded: !dryRun,\n outDir: options.outDir,\n droplet,\n };\n}\n\nexport interface PublishCommandOptions {\n readonly outDir?: string;\n readonly droplet?: string;\n readonly dryRun?: boolean;\n readonly skipTypecheck?: boolean;\n}\n\nexport const publishCommand: Command = new Command(\"publish\")\n .description(\"Publish a Fluid widget package\")\n .option(\n \"-o, --out-dir <dir>\",\n \"Widget artifact output directory\",\n DEFAULT_WIDGET_OUT_DIR,\n )\n .option(\"--droplet <uuid>\", \"Droplet UUID that owns the widget package\")\n .option(\"--dry-run\", \"Build and validate without uploading\")\n .option(\"--skip-typecheck\", \"Skip package typecheck script when present\")\n .action((options: PublishCommandOptions) => {\n return runCliAction(async () => {\n const projectDir = process.cwd();\n const outDir = options.outDir ?? DEFAULT_WIDGET_OUT_DIR;\n const droplet = await resolvePublishDroplet(projectDir, options.droplet);\n const dryRun = options.dryRun === true;\n\n console.log();\n console.log(chalk.blue.bold(\"Fluid Widget Publish\"));\n console.log();\n printRuntimeOnlyNotice();\n console.log(chalk.gray(\"Droplet: \") + chalk.white(droplet));\n console.log(chalk.gray(\"Output: \") + chalk.cyan(outDir));\n if (dryRun) {\n console.log(chalk.yellow(\"Dry run: no upload will be created.\"));\n }\n console.log();\n\n const validateSpinner = ora(\"Validating widget package...\").start();\n await validateDropletWidgetProject(projectDir, droplet);\n validateSpinner.succeed(\"Validated widget package\");\n\n if (options.skipTypecheck !== true) {\n const typecheckSpinner = ora(\"Running package typecheck...\").start();\n const typecheck = await runPackageScriptIfAvailable({\n projectDir,\n scriptName: \"typecheck\",\n });\n if (typecheck.ran) {\n typecheckSpinner.succeed(\"Package typecheck passed\");\n } else {\n typecheckSpinner.info(\"No typecheck script found; skipping\");\n }\n }\n\n const publishSpinner = ora(\n dryRun\n ? \"Building and preparing dry-run upload payload...\"\n : \"Building and publishing widget artifacts...\",\n ).start();\n\n const result = await withRootWidgetConfigBridge(\n projectDir,\n () =>\n publishWidgetRuntimeArtifacts({\n projectDir,\n outDir,\n buildOwner: \"droplet\",\n uploadOwner: { kind: \"droplet\", uuid: droplet },\n dryRun,\n ...(dryRun\n ? {}\n : { client: createAuthenticatedWidgetPackageClient() }),\n }),\n droplet,\n );\n\n publishSpinner.succeed(\n dryRun ? \"Prepared dry-run upload payload\" : \"Published widget package\",\n );\n\n if (dryRun) {\n console.log(\n chalk.yellow(\"Dry run complete — upload session was not requested.\"),\n );\n console.log();\n printDryRunSessionPayload(result);\n }\n\n printWidgetPublishSummary({\n title: dryRun ? \"Widget publish dry run\" : \"Widget publish\",\n ownerLabel: `droplet ${droplet}`,\n outDir,\n result,\n });\n });\n });\n\nasync function resolvePublishDroplet(\n projectDir: string,\n override: string | undefined,\n): Promise<string> {\n const droplet =\n override?.trim() || (await loadWidgetConfig(projectDir)).config.droplet;\n if (droplet?.trim()) {\n const trimmedDroplet = droplet.trim();\n const dropletError = validateDropletUuid(trimmedDroplet);\n if (dropletError) throw new Error(dropletError);\n return trimmedDroplet;\n }\n\n throw new Error(\n \"Missing droplet UUID. Pass --droplet <uuid> or set droplet in fluid.widget.config.ts.\",\n );\n}\n","import { Command } from \"commander\";\nimport { createAuthenticatedWidgetPackageClient } from \"@fluid-app/fluid-cli-portal\";\nimport chalk from \"chalk\";\nimport { loadWidgetConfig, validateDropletUuid } from \"../utils/config.js\";\nimport {\n fetchDropletWidgetStatus,\n formatStatusRows,\n} from \"../utils/droplet-api.js\";\nimport { runCliAction } from \"../utils/command.js\";\n\nexport interface StatusCommandOptions {\n readonly droplet?: string;\n readonly json?: boolean;\n}\n\nexport const statusCommand: Command = new Command(\"status\")\n .description(\"Show published widget package status for a Droplet\")\n .option(\"--droplet <uuid>\", \"Droplet UUID that owns the widget package\")\n .option(\"--json\", \"Print raw status response as JSON\")\n .action((options: StatusCommandOptions) => {\n return runCliAction(async () => {\n const droplet = await resolveDroplet(process.cwd(), options.droplet);\n const versions = await fetchDropletWidgetStatus({\n client: createAuthenticatedWidgetPackageClient(),\n droplet,\n });\n\n if (options.json === true) {\n console.log(\n JSON.stringify(\n versions.map((version) => version.raw),\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(chalk.blue.bold(`Widget status for droplet ${droplet}`));\n console.log();\n console.log(formatStatusRows(versions));\n });\n });\n\nasync function resolveDroplet(\n projectDir: string,\n override: string | undefined,\n): Promise<string> {\n const droplet =\n override?.trim() || (await loadWidgetConfig(projectDir)).config.droplet;\n if (droplet?.trim()) {\n const trimmedDroplet = droplet.trim();\n const dropletError = validateDropletUuid(trimmedDroplet);\n if (dropletError) throw new Error(dropletError);\n return trimmedDroplet;\n }\n\n throw new Error(\n \"Missing droplet UUID. Pass --droplet <uuid> or set droplet in fluid.widget.config.ts.\",\n );\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport {\n formatValidationSuccess,\n validateDropletWidgetProject,\n} from \"../utils/source-package.js\";\nimport { runCliAction } from \"../utils/command.js\";\n\nexport interface ValidateWidgetOptions {\n readonly json?: boolean;\n}\n\nexport const validateCommand: Command = new Command(\"validate\")\n .description(\"Validate the current droplet widget package\")\n .option(\"--json\", \"Print machine-readable validation output\")\n .action((options: ValidateWidgetOptions) => {\n return runCliAction(async () => {\n const result = await validateDropletWidgetProject(process.cwd());\n\n if (options.json === true) {\n console.log(\n JSON.stringify(\n {\n valid: true,\n config: result.sourceConfigPath,\n packageId: result.validated.packageId,\n version: result.validated.version,\n widgets: result.validated.widgets.map((widget) => ({\n name: widget.name,\n type: widget.type,\n })),\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(chalk.green(formatValidationSuccess(result)));\n });\n });\n","import { Command } from \"commander\";\nimport type { FluidPlugin, PluginContext } from \"@fluid-app/fluid-cli\";\nimport { buildCommand } from \"./commands/build.js\";\nimport { createCommand } from \"./commands/create.js\";\nimport { devCommand } from \"./commands/dev.js\";\nimport { linkCommand } from \"./commands/link.js\";\nimport { logsCommand } from \"./commands/logs.js\";\nimport { publishCommand } from \"./commands/publish.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { validateCommand } from \"./commands/validate.js\";\n\nexport const widgetCommand: Command = new Command(\"widget\")\n .description(\n \"Create, preview, link, validate, build, and publish standalone Fluid widgets\",\n )\n .addCommand(createCommand)\n .addCommand(devCommand)\n .addCommand(linkCommand)\n .addCommand(validateCommand)\n .addCommand(buildCommand)\n .addCommand(publishCommand)\n .addCommand(statusCommand)\n .addCommand(logsCommand);\n\nconst plugin: FluidPlugin = {\n name: \"@fluid-app/fluid-cli-widget\",\n version: \"0.1.0\",\n register(ctx: PluginContext) {\n const existingWidgetCommand = findCommand(ctx.program, \"widget\");\n if (existingWidgetCommand) {\n addCommandIfMissing(existingWidgetCommand, createCommand);\n addCommandIfMissing(existingWidgetCommand, devCommand);\n addCommandIfMissing(existingWidgetCommand, linkCommand);\n addCommandIfMissing(existingWidgetCommand, validateCommand);\n addCommandIfMissing(existingWidgetCommand, buildCommand);\n addCommandIfMissing(existingWidgetCommand, publishCommand);\n addCommandIfMissing(existingWidgetCommand, statusCommand);\n addCommandIfMissing(existingWidgetCommand, logsCommand);\n return;\n }\n\n ctx.program.addCommand(widgetCommand);\n },\n};\n\nexport default plugin;\n\nexport type {\n WidgetCommandOptions,\n WidgetCreateOptions,\n WidgetLinkOptions,\n WidgetTemplateVariables,\n} from \"./types.js\";\nexport { buildCommand, buildWidgetPackage } from \"./commands/build.js\";\nexport type {\n BuildWidgetOptions,\n BuildWidgetPackageOptions,\n BuildWidgetPackageResult,\n} from \"./commands/build.js\";\nexport { createCommand, createWidgetProject } from \"./commands/create.js\";\nexport {\n DEFAULT_WIDGET_DEV_PORT,\n WIDGET_DEV_ROUTES,\n buildViteDevArgs,\n devCommand,\n printWidgetDevSummary,\n resolveWidgetDevProject,\n runViteDevServer,\n} from \"./commands/dev.js\";\nexport type {\n WidgetDevOptions,\n WidgetDevProjectSummary,\n} from \"./commands/dev.js\";\nexport { linkCommand } from \"./commands/link.js\";\nexport { logsCommand } from \"./commands/logs.js\";\nexport type { LogsCommandOptions } from \"./commands/logs.js\";\nexport { publishCommand, publishWidgetPackage } from \"./commands/publish.js\";\nexport type {\n PublishCommandOptions,\n PublishWidgetPackageDependencies,\n PublishWidgetPackageOptions,\n PublishWidgetPackageResult,\n} from \"./commands/publish.js\";\nexport { statusCommand } from \"./commands/status.js\";\nexport type { StatusCommandOptions } from \"./commands/status.js\";\nexport { validateCommand } from \"./commands/validate.js\";\nexport type { ValidateWidgetOptions } from \"./commands/validate.js\";\nexport {\n WIDGET_CONFIG_FILE,\n formatWidgetConfig,\n loadWidgetConfig,\n mergeWidgetConfig,\n parseWidgetConfigSource,\n resolveWidgetConfigPath,\n validateDropletUuid,\n writeWidgetConfig,\n type FluidWidgetConfig,\n type LoadedWidgetConfig,\n} from \"./utils/config.js\";\nexport {\n DEFAULT_WIDGET_OUT_DIR,\n ROOT_WIDGET_CONFIG_FILE,\n WidgetValidationError,\n formatValidationSuccess,\n resolveWidgetSourceConfig,\n validateDropletWidgetProject,\n withRootWidgetConfigBridge,\n type WidgetValidationResult,\n} from \"./utils/source-package.js\";\nexport { formatValidationErrors } from \"./utils/validation.js\";\nexport type { WidgetCliValidationError } from \"./utils/validation.js\";\n\nfunction addCommandIfMissing(parent: Command, child: Command): void {\n if (findCommand(parent, child.name())) return;\n parent.addCommand(child);\n}\n\nfunction findCommand(parent: Command, name: string): Command | undefined {\n return parent.commands?.find((command) => command.name() === name);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-app/fluid-cli-widget",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Fluid CLI plugin for standalone widget package workflows",
5
5
  "files": [
6
6
  "dist",
@@ -23,8 +23,8 @@
23
23
  "commander": "^12.0.0",
24
24
  "ora": "^8.0.0",
25
25
  "prompts": "^2.4.2",
26
- "@fluid-app/fluid-cli-portal": "0.1.32",
27
- "@fluid-app/fluid-cli": "0.1.11"
26
+ "@fluid-app/fluid-cli-portal": "0.1.33",
27
+ "@fluid-app/fluid-cli": "0.1.12"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^24",
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ .DS_Store
3
+ dist
4
+ .env
5
+ .env.local
6
+ .fluid
7
+ *.tsbuildinfo
@@ -2,9 +2,184 @@
2
2
 
3
3
  Guidance for AI coding tools working in this standalone Fluid widget project.
4
4
 
5
- - This project contains a widget package only; do not scaffold or create droplets here.
6
- - Author widgets with `defineWidget()` and `defineWidgetPackage()` from `@fluid-app/portal-sdk`.
5
+ This repository is a generated Fluid widget package. Treat these instructions as the source of truth for code generated here. Do not assume the parent Fluid monorepo is present.
6
+
7
+ ## Project boundary
8
+
9
+ - This project contains a widget package only. Do not scaffold droplets, portal apps, Next.js apps, Rails code, API servers, or monorepo packages here.
10
+ - Use React, TypeScript, Vite, and the Fluid widget CLI scripts already in `package.json`.
7
11
  - Keep widget source under `src/widgets/` and package metadata in `manifest.ts`.
8
- - Run `pnpm validate` before considering changes complete.
9
- - Use React + TypeScript + Vite patterns; do not add Next.js app structure.
10
- - Keep runtime styles in `styles.css` and import them from a module loaded by the widget builder (for example `manifest.ts` or the widget component) so generated CSS artifacts are discovered automatically.
12
+ - Keep runtime registration in `src/index.ts`. Do not rewrite the global registration protocol unless the Fluid widget CLI changes.
13
+ - Keep package ownership in `fluid.widget.config.ts`. For droplet-owned widgets, link or set the droplet with `pnpm run widget:link` or `fluid widget link`; do not hard-code a droplet into component source.
14
+ - Runtime CSS belongs in `styles.css` or files imported by modules loaded by the widget builder. Import CSS from `manifest.ts` or the widget component so build artifacts are discovered.
15
+
16
+ ## Manifest authoring
17
+
18
+ Use `defineWidget()` and `defineWidgetPackage()` from `@fluid-app/portal-sdk`.
19
+
20
+ ### `defineWidget()` checklist
21
+
22
+ Each widget should define:
23
+
24
+ - `name`: stable, URL-safe widget name. Use letters, numbers, underscore, hyphen, or tilde. Changing it changes the generated widget type.
25
+ - `component`: React component that renders the widget.
26
+ - `displayName`, `description`, `icon`, and `category`: palette metadata for the builder.
27
+ - `defaultProps`: JSON-serializable default props. Do not use functions, Dates, undefined values, class instances, NaN, or Infinity.
28
+ - `propertySchema`: JSON-serializable editable fields for the builder property panel.
29
+ - `container`: usually `block` or `card`; use `inline` only for inline content and `fullscreen` only for true full-screen experiences.
30
+ - `resizable`: omit or set false for fixed widgets; otherwise use `true`, `horizontal`, `vertical`, `both`, or an object with horizontal/vertical booleans and optional min sizes.
31
+
32
+ ### `defineWidgetPackage()` checklist
33
+
34
+ The generated package is droplet-owned:
35
+
36
+ - `scope` is the namespace passed at project creation.
37
+ - `packageType` must stay `droplet`.
38
+ - `version` must be SemVer without build metadata, such as `1.2.3` or `1.2.3-beta.1`.
39
+ - `remoteEntryUrl` should stay `widget.js` for the standalone publish flow.
40
+ - `widgets` must contain at least one `defineWidget()` result.
41
+ - Do not manually set `packageStableId` for the default droplet template. The CLI injects the linked droplet as the stable package key during validation, build, and publish.
42
+
43
+ ## Property schema reference
44
+
45
+ A property schema describes the builder editing UI. Use this shape:
46
+
47
+ ```ts
48
+ propertySchema: {
49
+ tabsConfig: [{ id: "content", label: "Content" }],
50
+ dataSourceTargetProps: ["title", "items"],
51
+ fields: [
52
+ {
53
+ key: "title",
54
+ label: "Title",
55
+ type: "text",
56
+ defaultValue: "Featured items",
57
+ tab: "content",
58
+ group: "Copy",
59
+ },
60
+ ],
61
+ }
62
+ ```
63
+
64
+ Base field keys:
65
+
66
+ - `key`: prop key written into widget props. For visual-only fields such as section headers, use a unique non-prop key.
67
+ - `label`: human-readable property label.
68
+ - `type`: one of the supported field types below.
69
+ - `description`: optional helper text.
70
+ - `defaultValue`: optional JSON-serializable value matching the prop.
71
+ - `tab`: optional tab id from `tabsConfig`.
72
+ - `group`: optional group label inside a tab.
73
+ - `advanced`: true for theme override controls; advanced fields render in the Custom styling group.
74
+ - `requiresKeyValue`: conditionally show the field when another prop has a value. Arrays are AND logic.
75
+
76
+ Supported field types:
77
+
78
+ - `text`: single-line string. Optional `placeholder`, `maxLength`, `tokenSuggestions`.
79
+ - `textarea`: multi-line string. Optional `placeholder`, `rows`, `maxLength`.
80
+ - `number`: numeric input. Optional `min`, `max`, `step`.
81
+ - `boolean`: toggle.
82
+ - `select`: dropdown. Requires `options` with `label` and `value` entries.
83
+ - `color`: basic color value.
84
+ - `range`: slider. Requires `min` and `max`; optional `step`.
85
+ - `dataSource`: data-source selector/configuration entry point.
86
+ - `resource`: single shareable/resource selector. Optional `allowedTypes`.
87
+ - `image`: media picker. Optional `accept` as `image`, `video`, or `any`.
88
+ - `alignment`: alignment picker. Requires `options.verticalEnabled` and `options.horizontalEnabled`.
89
+ - `slider`: numeric slider with optional `unit` suffix plus `min`, `max`, and `step`.
90
+ - `colorPicker`: color picker with optional `swatches`.
91
+ - `sectionHeader`: visual grouping header with optional `subtitle`.
92
+ - `separator`: visual separator.
93
+ - `buttonGroup`: segmented control. Requires `options`; each option has `value` plus optional `label`, `ariaLabel`, and icon.
94
+ - `colorSelect`: semantic theme color selector. Optional `excludeColors`.
95
+ - `sectionLayoutSelect`: visual layout selector.
96
+ - `background`: combined resource/color background control.
97
+ - `contentPosition`: 3-by-3 content position picker.
98
+ - `textSizeSelect`: theme text size selector.
99
+ - `cssUnit`: number plus unit. Optional `allowedUnits`, `defaultUnit`, and min/max/step maps per unit.
100
+ - `fontPicker`: Google font picker with optional `placeholder`.
101
+ - `stringArray`: editable list of strings with optional `placeholder`.
102
+ - `borderRadius`: composite radius editor. Requires `keys.topLeft`, `keys.topRight`, `keys.bottomLeft`, and `keys.bottomRight` mapping to real prop keys.
103
+ - `screenPicker`: portal screen picker. Optional `includeSystemItems`.
104
+
105
+ Data-source-ready props:
106
+
107
+ - Add bindable prop keys to `dataSourceTargetProps`.
108
+ - Keep those prop types serializable and tolerant of missing, empty, or partially populated data.
109
+ - For arrays, render empty states and validate each item before reading nested properties.
110
+ - Keep manual defaults in `defaultProps`; data sources should override props without requiring component rewrites.
111
+ - If selected data needs per-item settings, add `itemConfigSchema` with its own `fields` array.
112
+
113
+ ## Component quality bar
114
+
115
+ - Components must be deterministic, portable, and host-safe. Avoid direct assumptions about the embedding portal, route, global CSS reset, or parent DOM.
116
+ - Props are untrusted. Provide defaults, guard array access, and handle nullish values.
117
+ - Use stable keys for lists. Do not use array indexes when items have stable ids.
118
+ - Avoid side effects during render. Use effects only for host-safe subscriptions and clean them up.
119
+ - Do not fetch data directly unless the widget explicitly owns that integration. Prefer props and data-source-ready schemas.
120
+ - Keep bundle weight small. Avoid large UI kits or date/chart libraries unless the widget truly needs them.
121
+ - Do not store secrets, API tokens, or tenant-specific credentials in source, props, or defaultProps.
122
+
123
+ ## Accessibility requirements
124
+
125
+ - Use semantic HTML first: sections, headings, buttons, lists, forms, and labels.
126
+ - Every interactive control must be keyboard reachable and have a visible focus state.
127
+ - Icon-only buttons need `aria-label`.
128
+ - Images need meaningful alt text, or empty alt text when decorative.
129
+ - Preserve heading order inside the widget; do not choose headings based only on size.
130
+ - Announce dynamic changes when necessary with appropriate ARIA live regions.
131
+ - Meet contrast expectations by using theme foreground/background token pairs.
132
+ - Respect reduced motion for animation-heavy widgets.
133
+
134
+ ## Theme and styling guidance
135
+
136
+ Fluid hosts provide semantic CSS variables. Prefer those over hard-coded colors:
137
+
138
+ - Surfaces: `--background`, `--card`, `--popover`.
139
+ - Text on surfaces: `--foreground`, `--card-foreground`, `--popover-foreground`.
140
+ - Brand/action: `--primary`, `--primary-foreground`.
141
+ - Supporting UI: `--secondary`, `--secondary-foreground`, `--muted`, `--muted-foreground`, `--accent`, `--accent-foreground`.
142
+ - Status and chrome: `--destructive`, `--destructive-foreground`, `--border`, `--input`, `--ring`.
143
+ - Charts: `--chart-1` through `--chart-5`.
144
+ - Radius: `--radius`, `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl`.
145
+ - Theme engine aliases may also provide `--font-header`, `--font-body`, `--font-size-extra-small`, `--font-size-small`, `--font-size-regular`, `--font-size-large`, `--font-size-extra-large`, and `--font-size-giant`.
146
+
147
+ Tailwind equivalents, when Tailwind is available in a host or preview, are the semantic utilities: `bg-background`, `text-foreground`, `bg-card`, `text-card-foreground`, `bg-primary`, `text-primary-foreground`, `border-border`, `ring-ring`, `rounded-lg`, and text sizes such as `text-sm` or `text-xl`. In this standalone template, plain CSS is the safest runtime default.
148
+
149
+ Light/dark behavior:
150
+
151
+ - The portal switches theme values with `data-theme-mode="dark"` and may also honor system dark mode when configured.
152
+ - Do not write separate hard-coded dark palettes unless absolutely necessary. Use semantic variables so the host theme controls both modes.
153
+ - If you need mode-specific refinements, scope them to `[data-theme-mode="dark"]` and keep them token-based.
154
+ - Avoid styling that only works on white backgrounds. Test in the local builder preview dark toggle and with high-contrast colors.
155
+
156
+ Runtime CSS rules:
157
+
158
+ - Put runtime selectors in `styles.css` or imported CSS modules.
159
+ - Keep selectors prefixed with the widget name to avoid leaking styles into the host.
160
+ - Do not rely on global body styles for runtime appearance; body styles are only for local preview.
161
+ - Ensure CSS is imported by `manifest.ts` or another module included in the widget build, otherwise CSS artifacts may not be published.
162
+
163
+ ## Validation workflow
164
+
165
+ Before considering changes complete, run:
166
+
167
+ ```bash
168
+ pnpm typecheck
169
+ pnpm validate
170
+ pnpm build
171
+ ```
172
+
173
+ Use `pnpm dev` for local preview and `pnpm run widget:publish -- --dry-run` when checking publish readiness without uploading.
174
+
175
+ Common failures and fixes:
176
+
177
+ - Missing droplet UUID: run `pnpm run widget:link` or pass `--droplet` to validate/build/publish commands.
178
+ - No source package found: ensure `fluid.widget.config.ts` exports `widgetPackage` or `widgetPackages` from `manifest.ts`.
179
+ - Invalid package type: keep `packageType: "droplet"` in `defineWidgetPackage()`.
180
+ - Invalid widget name or package key: use URL-safe names only.
181
+ - Invalid version: use SemVer without build metadata.
182
+ - Non-serializable metadata: remove functions, undefined values, Dates, NaN, Infinity, Maps, Sets, and class instances from `propertySchema` and `defaultProps`.
183
+ - CSS URL mismatch: import runtime CSS into the bundle or remove manual `cssUrls` entries.
184
+ - Build succeeds but styles are missing: confirm the CSS import is reachable from `manifest.ts` or the widget component.
185
+ - Preview works but published widget fails: check that browser-only APIs are guarded and no local-only URLs or environment variables are required at runtime.
@@ -0,0 +1,271 @@
1
+ # {{projectName}}
2
+
3
+ Standalone Fluid widget project generated by `@fluid-app/fluid-cli-widget`.
4
+
5
+ This project builds and publishes a widget package only. It does **not** scaffold droplets, a full portal application, or backend services.
6
+
7
+ ## Scripts
8
+
9
+ ```bash
10
+ pnpm dev # Run the local Vite/builder preview
11
+ pnpm typecheck # Run TypeScript checking
12
+ pnpm validate # Validate package metadata with the Fluid widget CLI
13
+ pnpm build # Validate and build runtime artifacts
14
+ pnpm run widget:link # Link this project to an existing Fluid droplet
15
+ pnpm run widget:publish # Publish to the linked droplet
16
+ pnpm run widget:publish -- --dry-run # Build upload payload without uploading
17
+ ```
18
+
19
+ ## Project structure
20
+
21
+ ```text
22
+ .
23
+ ├── AGENTS.md # Portable AI authoring guidance
24
+ ├── CLAUDE.md # Claude-compatible AI authoring guidance
25
+ ├── .agents/skills/ # Agent skill docs for this project
26
+ ├── .claude/skills/ # Claude skill docs for this project
27
+ ├── fluid.widget.config.ts # Widget CLI config and manifest exports
28
+ ├── manifest.ts # defineWidget/defineWidgetPackage metadata + runtime CSS import
29
+ ├── styles.css # Runtime + preview styles
30
+ ├── src/index.ts # Runtime registration entry
31
+ ├── src/widget-preview.tsx # Local preview entry
32
+ └── src/widgets/review-carousel/ReviewCarousel.tsx
33
+ ```
34
+
35
+ ## Package boundary
36
+
37
+ Stay inside the standalone widget package:
38
+
39
+ - Put reusable widget components under `src/widgets/`.
40
+ - Put package and palette metadata in `manifest.ts`.
41
+ - Keep runtime registration in `src/index.ts`.
42
+ - Keep runtime styles in `styles.css` or CSS files imported by widget modules.
43
+ - Do not add Next.js app folders, Rails code, API servers, portal app shells, or droplet scaffolding.
44
+
45
+ Droplet ownership is managed by the Fluid widget CLI through `fluid.widget.config.ts`. Link the project with `pnpm run widget:link` or pass a droplet option to CLI commands when needed.
46
+
47
+ ## Authoring widgets
48
+
49
+ Widgets are authored with `defineWidget()` and grouped with `defineWidgetPackage()` from `@fluid-app/portal-sdk`.
50
+
51
+ `defineWidget()` describes one widget:
52
+
53
+ - `name`: stable URL-safe widget name. Changing it changes the generated widget type.
54
+ - `component`: React component rendered by the host.
55
+ - `displayName`, `description`, `icon`, `category`: builder palette metadata.
56
+ - `defaultProps`: JSON-serializable defaults for new widget instances.
57
+ - `propertySchema`: builder property panel fields.
58
+ - `container`: `block`, `card`, `inline`, or `fullscreen`.
59
+ - `resizable`: false/omitted, true, `horizontal`, `vertical`, `both`, or an object with horizontal/vertical flags and optional minimum sizes.
60
+
61
+ `defineWidgetPackage()` describes the package:
62
+
63
+ - Keep `packageType` set to `droplet` for this template.
64
+ - Keep `remoteEntryUrl` as `widget.js` for the default publish flow.
65
+ - Keep `widgets` as the array of widgets exported by this package.
66
+ - Use a SemVer `version` without build metadata.
67
+ - Do not manually set `packageStableId` in this generated droplet template; the CLI injects the linked droplet key during validation, build, and publish.
68
+
69
+ ## Manifest structure
70
+
71
+ `manifest.ts` should remain the single source of package metadata:
72
+
73
+ ```ts
74
+ import { defineWidget, defineWidgetPackage } from "@fluid-app/portal-sdk";
75
+ import "./styles.css";
76
+ import { ExampleWidget } from "./src/widgets/example/ExampleWidget";
77
+
78
+ export const exampleWidget = defineWidget({
79
+ name: "ExampleWidget",
80
+ component: ExampleWidget,
81
+ displayName: "Example Widget",
82
+ description: "A focused reusable Fluid widget.",
83
+ icon: "box",
84
+ category: "components",
85
+ container: "card",
86
+ defaultProps: {
87
+ title: "Featured content",
88
+ },
89
+ propertySchema: {
90
+ tabsConfig: [{ id: "content", label: "Content" }],
91
+ dataSourceTargetProps: ["title"],
92
+ fields: [
93
+ {
94
+ key: "title",
95
+ label: "Title",
96
+ type: "text",
97
+ defaultValue: "Featured content",
98
+ tab: "content",
99
+ group: "Copy",
100
+ },
101
+ ],
102
+ },
103
+ });
104
+
105
+ export const widgetPackage = defineWidgetPackage({
106
+ scope: "droplet",
107
+ version: "0.1.0",
108
+ packageType: "droplet",
109
+ remoteEntryUrl: "widget.js",
110
+ widgets: [exampleWidget],
111
+ });
112
+
113
+ export const widgetPackages = [widgetPackage] as const;
114
+ export default widgetPackage;
115
+ ```
116
+
117
+ ## Property schema reference
118
+
119
+ Property schemas define editable builder controls. They must be JSON-serializable.
120
+
121
+ Top-level schema keys:
122
+
123
+ - `tabsConfig`: optional array of tab objects with `id` and `label`.
124
+ - `fields`: array of controls shown in the builder property panel.
125
+ - `dataSourceTargetProps`: prop keys that can be populated from configured data sources.
126
+ - `itemConfigSchema`: optional per-item configuration schema for custom data-source selections.
127
+ - `validate`: optional custom validator in in-app manifests; avoid it for published standalone metadata because package metadata must be serializable.
128
+
129
+ Base field keys:
130
+
131
+ | Key | Purpose |
132
+ | --- | --- |
133
+ | `key` | Widget prop key written by the field. Use a unique non-prop key for visual-only controls. |
134
+ | `label` | Builder label. |
135
+ | `type` | Supported field type listed below. |
136
+ | `description` | Optional helper text. |
137
+ | `defaultValue` | Optional JSON-serializable default. |
138
+ | `tab` | Optional tab id matching `tabsConfig`. |
139
+ | `group` | Optional group label inside a tab. |
140
+ | `advanced` | Marks theme overrides or low-frequency settings; advanced fields render in Custom styling. |
141
+ | `requiresKeyValue` | Optional conditional display rule. An array means all conditions must match. |
142
+
143
+ Supported field types:
144
+
145
+ | Type | Use | Extra keys |
146
+ | --- | --- | --- |
147
+ | `text` | Single-line string | `placeholder`, `maxLength`, `tokenSuggestions` |
148
+ | `textarea` | Multi-line string | `placeholder`, `rows`, `maxLength` |
149
+ | `number` | Number input | `min`, `max`, `step` |
150
+ | `boolean` | Toggle | None |
151
+ | `select` | Dropdown | `options` array with `label` and `value` |
152
+ | `color` | Basic color value | None |
153
+ | `range` | Slider | Required `min`, `max`; optional `step` |
154
+ | `dataSource` | Data-source selector/configuration | None |
155
+ | `resource` | Single shareable/resource picker | `allowedTypes` |
156
+ | `image` | Media picker | `accept` as `image`, `video`, or `any` |
157
+ | `alignment` | Alignment picker | `options.verticalEnabled`, `options.horizontalEnabled` |
158
+ | `slider` | Numeric slider | `min`, `max`, `step`, `unit` |
159
+ | `colorPicker` | Color picker | `swatches` |
160
+ | `sectionHeader` | Visual section heading | `subtitle` |
161
+ | `separator` | Visual divider | None |
162
+ | `buttonGroup` | Segmented choice | `options` with `value`, optional `label`, `ariaLabel`, `icon` |
163
+ | `colorSelect` | Semantic theme color picker | `excludeColors` |
164
+ | `sectionLayoutSelect` | Visual section layout picker | None |
165
+ | `background` | Combined background resource/color control | Resource/color field keys as needed |
166
+ | `contentPosition` | 3-by-3 content position picker | None |
167
+ | `textSizeSelect` | Theme text-size picker | None |
168
+ | `cssUnit` | Number with unit selector | `allowedUnits`, `defaultUnit`, `minByUnit`, `maxByUnit`, `stepByUnit` |
169
+ | `fontPicker` | Google font picker | `placeholder` |
170
+ | `stringArray` | Editable list of strings | `placeholder` |
171
+ | `borderRadius` | Composite four-corner radius editor | `keys.topLeft`, `keys.topRight`, `keys.bottomLeft`, `keys.bottomRight` |
172
+ | `screenPicker` | Portal screen picker | `includeSystemItems` |
173
+
174
+ ### Data-source-ready props
175
+
176
+ For props that may later be connected to data sources:
177
+
178
+ - Add the prop key to `dataSourceTargetProps`.
179
+ - Keep the component tolerant of missing, empty, or partially populated values.
180
+ - Render useful empty states for empty arrays and failed mapping.
181
+ - Keep prop values JSON-serializable.
182
+ - Avoid fetching inside the component when a prop or data source can provide the data.
183
+ - Use `itemConfigSchema` when each selected item needs widget-specific settings.
184
+
185
+ ## Component quality bar
186
+
187
+ - Use TypeScript interfaces for public props.
188
+ - Provide defaults for optional props.
189
+ - Treat incoming props as untrusted and validate before reading nested values.
190
+ - Keep render logic deterministic and side-effect free.
191
+ - Clean up effects and subscriptions.
192
+ - Keep bundles small; avoid heavy libraries unless the widget requires them.
193
+ - Avoid secrets, tenant credentials, local URLs, and environment-only assumptions.
194
+ - Prefer semantic HTML over div-only structures.
195
+
196
+ ## Accessibility
197
+
198
+ - Use headings, sections, lists, buttons, labels, and form elements semantically.
199
+ - Make every interactive element keyboard reachable.
200
+ - Provide visible focus states.
201
+ - Add `aria-label` to icon-only controls.
202
+ - Add alt text to meaningful images and empty alt text to decorative images.
203
+ - Preserve logical heading order inside the widget.
204
+ - Use ARIA live regions for important dynamic updates.
205
+ - Respect reduced-motion preferences for animation-heavy widgets.
206
+ - Use semantic foreground/background token pairs for contrast.
207
+
208
+ ## Runtime CSS and theme tokens
209
+
210
+ Fluid hosts provide semantic CSS variables. Prefer token-based CSS instead of hard-coded palettes.
211
+
212
+ Common CSS variables:
213
+
214
+ - Surfaces: `--background`, `--card`, `--popover`.
215
+ - Text: `--foreground`, `--card-foreground`, `--popover-foreground`.
216
+ - Brand/actions: `--primary`, `--primary-foreground`.
217
+ - Supporting UI: `--secondary`, `--secondary-foreground`, `--muted`, `--muted-foreground`, `--accent`, `--accent-foreground`.
218
+ - Status/chrome: `--destructive`, `--destructive-foreground`, `--border`, `--input`, `--ring`.
219
+ - Charts: `--chart-1`, `--chart-2`, `--chart-3`, `--chart-4`, `--chart-5`.
220
+ - Radius: `--radius`, `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl`.
221
+ - Theme-engine aliases may include `--font-header`, `--font-body`, `--font-size-extra-small`, `--font-size-small`, `--font-size-regular`, `--font-size-large`, `--font-size-extra-large`, and `--font-size-giant`.
222
+
223
+ Tailwind equivalents, when Tailwind is available, are semantic utilities such as `bg-background`, `text-foreground`, `bg-card`, `text-card-foreground`, `bg-primary`, `text-primary-foreground`, `border-border`, `ring-ring`, `rounded-lg`, `text-sm`, and `text-xl`. This standalone template uses plain CSS for runtime portability.
224
+
225
+ Light/dark behavior:
226
+
227
+ - Fluid themes update variables for light and dark mode.
228
+ - Explicit dark mode is represented by `data-theme-mode="dark"` on a host element.
229
+ - Some hosts also use system dark mode when no explicit mode is selected.
230
+ - Prefer semantic variables so both modes work automatically.
231
+ - If mode-specific styling is unavoidable, scope it with `[data-theme-mode="dark"]` and keep values token-based.
232
+
233
+ Runtime CSS rules:
234
+
235
+ - Import runtime CSS from `manifest.ts` or a widget module included in the build.
236
+ - Prefix selectors with the widget name to prevent host leakage.
237
+ - Do not rely on `body` styles for published runtime rendering; body styles are preview-only.
238
+ - If published styles are missing, check that the CSS import is reachable from the built entry.
239
+
240
+ ## Validation, build, and publish preflight
241
+
242
+ Before shipping changes, run:
243
+
244
+ ```bash
245
+ pnpm typecheck
246
+ pnpm validate
247
+ pnpm build
248
+ pnpm run widget:publish -- --dry-run
249
+ ```
250
+
251
+ Publish only after the dry run succeeds:
252
+
253
+ ```bash
254
+ pnpm run widget:publish
255
+ ```
256
+
257
+ Build output is written under `.fluid/widget-dist/`. The runtime entry registers the package with `window.FluidWidgets` when loaded by a Fluid portal host.
258
+
259
+ ## Common failures
260
+
261
+ | Failure | Fix |
262
+ | --- | --- |
263
+ | Missing droplet UUID | Run `pnpm run widget:link` or pass a droplet option to validate/build/publish. |
264
+ | No source package found | Ensure `fluid.widget.config.ts` exports `widgetPackage` or `widgetPackages` from `manifest.ts`. |
265
+ | Invalid package type | Keep `packageType: "droplet"`. |
266
+ | Invalid package key or widget name | Use URL-safe letters, numbers, dots for package keys, and underscore/hyphen/tilde where allowed. |
267
+ | Invalid version | Use SemVer without build metadata. |
268
+ | Non-serializable metadata | Remove functions, undefined values, Dates, NaN, Infinity, Maps, Sets, and class instances from `propertySchema` and `defaultProps`. |
269
+ | Relative CSS URL rejected | Import CSS into the bundle or remove manual `cssUrls` entries. |
270
+ | Styles present in preview but absent after publish | Confirm `styles.css` is imported by `manifest.ts` or a widget module loaded by the builder. |
271
+ | Published widget fails but preview works | Guard browser-only APIs, remove local-only URLs, and avoid environment variables required at runtime. |
@@ -1,38 +0,0 @@
1
- # {{projectName}}
2
-
3
- Standalone Fluid widget project generated by `@fluid-app/fluid-cli-widget`.
4
-
5
- This project builds a widget package only. It does **not** scaffold droplets or a full portal application.
6
-
7
- ## Scripts
8
-
9
- ```bash
10
- pnpm dev # Run the local Vite preview
11
- pnpm validate # Validate the widget package with the Fluid widget CLI
12
- pnpm build # Build the widget runtime bundle with the Fluid widget CLI
13
- pnpm run widget:publish # Publish with the Fluid widget CLI
14
- ```
15
-
16
- ## Project structure
17
-
18
- ```text
19
- .
20
- ├── fluid.widget.config.ts # Widget package config export
21
- ├── manifest.ts # defineWidget/defineWidgetPackage metadata + runtime CSS import
22
- ├── styles.css # Runtime + preview styles
23
- ├── src/index.ts # Runtime registration entry
24
- ├── src/widget-preview.tsx # Local preview entry
25
- └── src/widgets/review-carousel/ReviewCarousel.tsx
26
- ```
27
-
28
- ## Authoring widgets
29
-
30
- Widgets are authored with `defineWidget()` and grouped with `defineWidgetPackage()` from `@fluid-app/portal-sdk`.
31
-
32
- Edit `manifest.ts` to update package metadata such as `version` and to add widgets. Droplet package identity is managed by the Fluid widget CLI from `fluid.widget.config.ts` and the linked droplet.
33
-
34
- Add new widgets under `src/widgets/`, import them in `manifest.ts`, and add them to the `widgets` array. Keep runtime CSS imported by `manifest.ts` or another module loaded by the widget builder; the build discovers emitted CSS artifacts automatically.
35
-
36
- ## Build output
37
-
38
- `pnpm build` validates the package and writes runtime artifacts under `.fluid/widget-dist/`. The runtime entry registers the package with `window.FluidWidgets` when loaded by a Fluid portal host.