@fluid-app/fluid-cli-portal 0.1.37 → 0.1.38

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
@@ -5,12 +5,13 @@ import chalk from "chalk";
5
5
  import ora from "ora";
6
6
  import path, { basename, dirname, join, relative, resolve } from "node:path";
7
7
  import { fileURLToPath, pathToFileURL } from "node:url";
8
- import { copyFile, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
8
+ import { copyFile, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
9
9
  import { failure, findProjectConfig, getActiveProfile, getAuthToken, listProfileNames, readConfig, success } from "@fluid-app/fluid-cli";
10
10
  import prompts from "prompts";
11
- import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
11
+ import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
12
12
  import Handlebars from "handlebars";
13
13
  import { execa } from "execa";
14
+ import os from "node:os";
14
15
  import fs from "fs-extra";
15
16
  import path$1 from "path";
16
17
  import { createHash } from "node:crypto";
@@ -404,8 +405,297 @@ async function installDependencies(cwd) {
404
405
  await runPackageManager(["install"], cwd);
405
406
  }
406
407
  //#endregion
408
+ //#region src/lib/git.ts
409
+ /**
410
+ * Git wrapper for the portal CLI — shells out to the system `git` via
411
+ * execa. We deliberately do NOT bundle git or use a pure-JS impl; users
412
+ * are expected to have it installed (the same stance as `fluid mist`).
413
+ *
414
+ * Security: a token-bearing `remote_url` (minted by the portal
415
+ * `git_credential` endpoint) must never be logged, persisted, *or*
416
+ * exposed in the process table. The token is stripped from the URL and
417
+ * handed to git through a GIT_ASKPASS script via env vars
418
+ * (FLUID_PORTAL_GIT_USER / FLUID_PORTAL_GIT_PASS) — so it never appears
419
+ * in argv (visible to `ps aux` / `/proc/<pid>/cmdline`) and is never
420
+ * written to `.git/config`.
421
+ *
422
+ * This mirrors `packages/cli/mist/src/lib/git.ts`. Kept portal-local for
423
+ * now; if the widget/mist tracks extract a shared git lib we should
424
+ * consume it instead (see overnight/track2-portal.md).
425
+ */
426
+ var GitError = class extends Error {
427
+ exitCode;
428
+ constructor(message, exitCode = 1) {
429
+ super(message);
430
+ this.name = "GitError";
431
+ this.exitCode = exitCode;
432
+ }
433
+ };
434
+ /**
435
+ * execa throws ENOENT when the git binary itself is missing — the single
436
+ * most common failure on non-developer machines. Translate it into an
437
+ * actionable message. Returns null for every other failure so call sites
438
+ * keep their specific messages.
439
+ */
440
+ function gitMissingError(err) {
441
+ if (err?.code !== "ENOENT") return null;
442
+ return new GitError("Git isn't installed (or isn't on your PATH), so this command can't run.\n Install it, then try again:\n macOS: xcode-select --install (or `brew install git`)\n Windows: https://git-scm.com/download/win\n Linux: use your package manager, e.g. `apt install git`", 127);
443
+ }
444
+ /**
445
+ * Is `git` available on this machine? Cheap, never throws — used to
446
+ * decide whether to take the GitHub-template path or fall back to the
447
+ * bundled scaffold.
448
+ */
449
+ async function isGitAvailable() {
450
+ try {
451
+ await execa("git", ["--version"]);
452
+ return true;
453
+ } catch {
454
+ return false;
455
+ }
456
+ }
457
+ /** Confirm git is installed, throwing the friendly message otherwise. */
458
+ async function ensureGitAvailable() {
459
+ if (!await isGitAvailable()) throw new GitError("Git isn't installed (or isn't on your PATH), so this command can't run.\n Install it from https://git-scm.com/downloads (or `brew install git`), then try again.");
460
+ }
461
+ /**
462
+ * Split a token-bearing remote URL into the userless URL + creds. Keeps
463
+ * the token off git's argv: we pass `cleanUrl` to git and feed
464
+ * `username` / `password` back through GIT_ASKPASS env vars.
465
+ *
466
+ * If the URL has no embedded credentials, returns empty strings — fine
467
+ * for public repos where git won't invoke askpass at all.
468
+ */
469
+ function splitRemoteCredentials(remoteUrl) {
470
+ const u = new URL(remoteUrl);
471
+ const username = decodeURIComponent(u.username);
472
+ const password = decodeURIComponent(u.password);
473
+ u.username = "";
474
+ u.password = "";
475
+ return {
476
+ cleanUrl: u.toString(),
477
+ username,
478
+ password
479
+ };
480
+ }
481
+ /**
482
+ * Write a stub GIT_ASKPASS shell script to a per-process tmpdir and
483
+ * return its path. The script contains no secret — it echoes back
484
+ * whichever env var (FLUID_PORTAL_GIT_USER / FLUID_PORTAL_GIT_PASS)
485
+ * matches git's prompt. The credentials only live in the spawned git
486
+ * process's env for the duration of the call.
487
+ *
488
+ * One POSIX script for all platforms: Git for Windows executes
489
+ * GIT_ASKPASS through its bundled sh, so a plain sh script works
490
+ * everywhere. Idempotent: subsequent calls reuse the same path.
491
+ */
492
+ let askpassPath = null;
493
+ function ensureAskpassScript() {
494
+ if (askpassPath && existsSync(askpassPath)) return askpassPath;
495
+ const dir = path.join(os.tmpdir(), `fluid-portal-askpass-${process.pid}`);
496
+ if (!existsSync(dir)) mkdirSync(dir, {
497
+ recursive: true,
498
+ mode: 448
499
+ });
500
+ const p = path.join(dir, "askpass.sh");
501
+ writeFileSync(p, `#!/bin/sh\ncase "$1" in\n Username*) printf '%s' "$FLUID_PORTAL_GIT_USER" ;;\n *) printf '%s' "$FLUID_PORTAL_GIT_PASS" ;;\nesac\n`, { mode: 448 });
502
+ chmodSync(p, 448);
503
+ askpassPath = p;
504
+ return askpassPath;
505
+ }
506
+ /**
507
+ * Build the spawn env + cleaned URL for a credentialed git invocation.
508
+ * - `cleanUrl`: URL with userinfo stripped — safe to pass as argv.
509
+ * - `env`: parent env plus GIT_ASKPASS + FLUID_PORTAL_GIT_USER/PASS.
510
+ * GIT_TERMINAL_PROMPT=0 prevents the TTY-prompt fallback if the
511
+ * credentials are empty (auth should fail fast, not hang).
512
+ */
513
+ function gitAuthSpawn(remoteUrl) {
514
+ const { cleanUrl, username, password } = splitRemoteCredentials(remoteUrl);
515
+ return {
516
+ cleanUrl,
517
+ env: {
518
+ ...process.env,
519
+ GIT_ASKPASS: ensureAskpassScript(),
520
+ GIT_TERMINAL_PROMPT: "0",
521
+ FLUID_PORTAL_GIT_USER: username,
522
+ FLUID_PORTAL_GIT_PASS: password
523
+ }
524
+ };
525
+ }
526
+ /** Map an execa failure to a GitError, preferring the friendly
527
+ * git-missing message when the binary itself is absent. */
528
+ function toGitError(err, verb) {
529
+ const missing = gitMissingError(err);
530
+ if (missing) return missing;
531
+ const e = err;
532
+ return new GitError(`git ${verb} failed${e.exitCode != null ? ` (exit ${e.exitCode})` : ""}`, e.exitCode ?? 1);
533
+ }
534
+ /**
535
+ * `git clone <cleanUrl> <targetDir>`. When `remoteUrl` carries a token,
536
+ * gitAuthSpawn strips it from the URL and re-injects it via GIT_ASKPASS
537
+ * so it never appears in argv. `credential.helper=` stops git from
538
+ * caching what askpass returned to any system credential store.
539
+ *
540
+ * `depth` shallow-clones (used by the create flow, which discards `.git`
541
+ * afterwards anyway).
542
+ */
543
+ async function gitClone(remoteUrl, targetDir, opts = {}) {
544
+ const { cleanUrl, env } = gitAuthSpawn(remoteUrl);
545
+ const depthArgs = opts.depth ? ["--depth", String(opts.depth)] : [];
546
+ try {
547
+ await execa("git", [
548
+ "-c",
549
+ "credential.helper=",
550
+ "clone",
551
+ "--quiet",
552
+ ...depthArgs,
553
+ cleanUrl,
554
+ targetDir
555
+ ], {
556
+ env,
557
+ stdio: [
558
+ "ignore",
559
+ "inherit",
560
+ "inherit"
561
+ ]
562
+ });
563
+ } catch (err) {
564
+ throw toGitError(err, "clone");
565
+ }
566
+ }
567
+ /** Initialize a fresh repo (used after scaffolding so the project is
568
+ * git-ready). Best-effort: never throws — a scaffold without a repo is
569
+ * still usable, and a missing git binary is handled by the caller. */
570
+ async function gitInit(cwd) {
571
+ try {
572
+ await execa("git", ["init", "--quiet"], { cwd });
573
+ return true;
574
+ } catch {
575
+ return false;
576
+ }
577
+ }
578
+ //#endregion
579
+ //#region src/utils/scaffold-github.ts
580
+ /**
581
+ * GitHub-template scaffolding for `fluid portal create`.
582
+ *
583
+ * The canonical starter lives at github.com/Fluid-WeCommerce/portal-template
584
+ * as a **fully rendered, runnable** project (GitHub's generate-from-template
585
+ * copies files verbatim, so it can't contain `{{handlebars}}` placeholders).
586
+ * The project name is baked in as a distinctive token (CANONICAL_PROJECT_NAME);
587
+ * after cloning we re-substitute it with the user's chosen app name.
588
+ *
589
+ * This is the primary create path. When git or the network is unavailable
590
+ * the command falls back to the bundled template (see create.ts), so
591
+ * scaffolding never hard-fails offline.
592
+ */
593
+ /** Public HTTPS URL of the portal starter template repo. */
594
+ const TEMPLATE_REPO_URL = "https://github.com/Fluid-WeCommerce/portal-template.git";
595
+ /**
596
+ * The project name baked into the rendered template repo. `fluid portal
597
+ * create` replaces every occurrence with the user's app name after
598
+ * cloning. Must match the name the template was rendered with — keep in
599
+ * sync with the render used to publish the repo.
600
+ */
601
+ const CANONICAL_PROJECT_NAME = "fluid-portal-starter";
602
+ /**
603
+ * Files/dirs we never rewrite when re-substituting the project name.
604
+ * The lockfile keys packages by import path (`.`), not the root name, so
605
+ * touching it would only risk corruption for zero benefit.
606
+ */
607
+ const SUBSTITUTION_SKIP = new Set([
608
+ ".git",
609
+ "node_modules",
610
+ "pnpm-lock.yaml"
611
+ ]);
612
+ /** Extensions we treat as text and safe to rewrite. Everything else is
613
+ * left byte-for-byte (avoids mangling any binary asset a future template
614
+ * might carry). */
615
+ const TEXT_EXTENSIONS = new Set([
616
+ ".ts",
617
+ ".tsx",
618
+ ".js",
619
+ ".jsx",
620
+ ".mjs",
621
+ ".cjs",
622
+ ".json",
623
+ ".md",
624
+ ".yml",
625
+ ".yaml",
626
+ ".css",
627
+ ".html",
628
+ ".txt"
629
+ ]);
630
+ function hasTextExtension(file) {
631
+ const dot = file.lastIndexOf(".");
632
+ if (dot < 0) return false;
633
+ return TEXT_EXTENSIONS.has(file.slice(dot));
634
+ }
635
+ /** Recursively list absolute file paths, skipping SUBSTITUTION_SKIP dirs. */
636
+ async function listFiles(dir) {
637
+ const entries = await readdir(dir, { withFileTypes: true });
638
+ const files = [];
639
+ for (const entry of entries) {
640
+ if (SUBSTITUTION_SKIP.has(entry.name)) continue;
641
+ const full = join(dir, entry.name);
642
+ if (entry.isDirectory()) files.push(...await listFiles(full));
643
+ else files.push(full);
644
+ }
645
+ return files;
646
+ }
647
+ /** Escape a string for use in a RegExp. */
648
+ function escapeRegExp(value) {
649
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
650
+ }
651
+ /**
652
+ * Replace every occurrence of the canonical project name with `appName`
653
+ * across the cloned tree's text files. Only files that actually contain
654
+ * the token are rewritten.
655
+ */
656
+ async function rewriteProjectName(targetPath, appName) {
657
+ if (appName === "fluid-portal-starter") return;
658
+ const token = new RegExp(escapeRegExp(CANONICAL_PROJECT_NAME), "g");
659
+ const files = await listFiles(targetPath);
660
+ for (const file of files) {
661
+ if (!hasTextExtension(file)) continue;
662
+ const content = await readFile(file, "utf-8");
663
+ if (!content.includes("fluid-portal-starter")) continue;
664
+ await writeFile(file, content.replace(token, () => appName), "utf-8");
665
+ }
666
+ }
667
+ /** Rewrite `.fluidrc` with the chosen CLI profile (the template ships an
668
+ * empty profile). No-op when the file is absent. */
669
+ async function writeFluidrcProfile(targetPath, profileName) {
670
+ const fluidrcPath = join(targetPath, ".fluidrc");
671
+ if (!existsSync(fluidrcPath)) return;
672
+ await writeFile(fluidrcPath, JSON.stringify({ profile: profileName }, null, 2) + "\n", "utf-8");
673
+ }
674
+ /**
675
+ * Clone the GitHub starter template into `targetPath`, discard its git
676
+ * history, re-substitute the project name, wire `.fluidrc`, and copy
677
+ * `.env.example` → `.env`.
678
+ *
679
+ * Throws (GitError or otherwise) on any failure — the caller decides
680
+ * whether to fall back to the bundled template.
681
+ */
682
+ async function scaffoldFromGitHub(options) {
683
+ const { targetPath, appName, profileName } = options;
684
+ await gitClone(options.repoUrl ?? "https://github.com/Fluid-WeCommerce/portal-template.git", targetPath, { depth: 1 });
685
+ await rm(join(targetPath, ".git"), {
686
+ recursive: true,
687
+ force: true
688
+ });
689
+ await rewriteProjectName(targetPath, appName);
690
+ await writeFluidrcProfile(targetPath, profileName);
691
+ const envExample = join(targetPath, ".env.example");
692
+ try {
693
+ if ((await stat(envExample)).isFile()) await copyFile(envExample, join(targetPath, ".env"));
694
+ } catch {}
695
+ }
696
+ //#endregion
407
697
  //#region src/commands/create.ts
408
- const createCommand = new Command("create").description("Create a new Fluid portal application").argument("<app-name>", "Name of the application to create").option("--skip-install", "Skip dependency installation").option("-o, --output-dir <dir>", "Directory to create the project in (defaults to cwd)").option("--local", "Use local monorepo packages via file: links (for development testing)").action(async (appName, options) => {
698
+ const createCommand = new Command("create").description("Create a new Fluid portal application").argument("<app-name>", "Name of the application to create").option("--skip-install", "Skip dependency installation").option("-o, --output-dir <dir>", "Directory to create the project in (defaults to cwd)").option("--local", "Use local monorepo packages via file: links (for development testing)").option("--no-template", "Skip the GitHub starter template; scaffold from the bundled template").action(async (appName, options) => {
409
699
  try {
410
700
  console.log();
411
701
  console.log(chalk.bold("Creating a new Fluid portal application"));
@@ -426,65 +716,39 @@ const createCommand = new Command("create").description("Create a new Fluid port
426
716
  process.exit(0);
427
717
  }
428
718
  console.log();
429
- const templatePaths = getTemplatePaths("starter");
430
- if (!await directoryExists(templatePaths.base)) {
431
- console.error(chalk.red("Error: Base template not found"));
432
- process.exit(1);
433
- }
434
- if (!await directoryExists(templatePaths.overlay)) {
435
- console.error(chalk.red("Error: Starter template not found"));
436
- process.exit(1);
437
- }
438
- let sdkVersion;
439
- let localCoreVersion;
440
- const cliVersion = await getCliVersion();
441
- if (!!options.local) {
442
- const monorepoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "..");
443
- const sdkPath = join(monorepoRoot, "packages", "portal", "sdk");
444
- const corePath = join(monorepoRoot, "packages", "portal", "core");
445
- if (!await directoryExists(sdkPath) || !await directoryExists(corePath)) {
446
- console.error(chalk.red("Error: --local requires running from within the fluid-mono monorepo\n Could not find packages/portal/sdk or packages/portal/core"));
447
- process.exit(1);
719
+ const isLocal = !!options.local;
720
+ const gitReady = !(isLocal || options.template === false || config.selectedPages.length > 0) && await isGitAvailable();
721
+ let scaffolded = false;
722
+ if (gitReady) {
723
+ const spinner = ora("Fetching starter template from GitHub...").start();
724
+ try {
725
+ await scaffoldFromGitHub({
726
+ targetPath,
727
+ appName: config.name,
728
+ profileName: config.profileName
729
+ });
730
+ spinner.succeed("Fetched starter template");
731
+ scaffolded = true;
732
+ } catch (error) {
733
+ spinner.warn("Couldn't fetch the GitHub template — using the bundled starter");
734
+ const reason = error instanceof GitError ? error.message.split("\n")[0] : error instanceof Error ? error.message : String(error);
735
+ console.log(chalk.dim(` (${reason}; source: ${TEMPLATE_REPO_URL})`));
736
+ try {
737
+ await rm(targetPath, {
738
+ recursive: true,
739
+ force: true
740
+ });
741
+ } catch {}
448
742
  }
449
- sdkVersion = getLocalPackageLinkVersion(targetPath, sdkPath);
450
- localCoreVersion = getLocalPackageLinkVersion(targetPath, corePath);
451
- console.log(chalk.cyan(" Using local packages (--local mode)"));
452
- } else sdkVersion = await getSdkVersion();
453
- const spinner = ora("Creating project directory...").start();
454
- try {
455
- await createDirectory(targetPath);
456
- spinner.succeed("Created project directory");
457
- } catch (error) {
458
- spinner.fail("Failed to create project directory");
459
- throw error;
460
- }
461
- const templateVariables = {
462
- projectName: config.name,
463
- sdkVersion,
464
- localCoreVersion,
465
- cliVersion,
466
- selectedPages: config.selectedPages,
467
- hasSelectedPages: config.selectedPages.length > 0,
468
- profileName: config.profileName
469
- };
470
- spinner.start("Copying template files...");
471
- try {
472
- await copyTemplate(templatePaths.base, targetPath, templateVariables);
473
- await copyTemplate(templatePaths.overlay, targetPath, templateVariables);
474
- const envExamplePath = join(targetPath, ".env.example");
475
- if (await fileExists(envExamplePath)) await copyFile(envExamplePath, join(targetPath, ".env"));
476
- spinner.succeed("Copied template files");
477
- } catch (error) {
478
- spinner.fail("Failed to copy template files");
479
- throw error;
480
743
  }
744
+ if (!scaffolded) await scaffoldFromBundled(targetPath, config, isLocal);
481
745
  if (config.installDeps) {
482
- spinner.start("Installing dependencies with pnpm...");
746
+ const installSpinner = ora("Installing dependencies with pnpm...").start();
483
747
  try {
484
748
  await installDependencies(targetPath);
485
- spinner.succeed("Installed dependencies");
749
+ installSpinner.succeed("Installed dependencies");
486
750
  } catch {
487
- spinner.fail("Failed to install dependencies");
751
+ installSpinner.fail("Failed to install dependencies");
488
752
  console.log();
489
753
  console.log(chalk.yellow("You can try installing dependencies manually:"));
490
754
  console.log(chalk.cyan(` cd ${appName}`));
@@ -517,6 +781,59 @@ const createCommand = new Command("create").description("Create a new Fluid port
517
781
  process.exit(1);
518
782
  }
519
783
  });
784
+ /**
785
+ * Scaffold from the bundled base + overlay templates (Handlebars). Used
786
+ * when the GitHub template is skipped/unavailable, or with `--local`
787
+ * (which rewrites SDK/core deps to workspace `file:`/`link:` paths so the
788
+ * generated project resolves unpublished monorepo source).
789
+ *
790
+ * Throws on failure; the caller reports and exits.
791
+ */
792
+ async function scaffoldFromBundled(targetPath, config, isLocal) {
793
+ const templatePaths = getTemplatePaths("starter");
794
+ if (!await directoryExists(templatePaths.base)) throw new Error("Base template not found");
795
+ if (!await directoryExists(templatePaths.overlay)) throw new Error("Starter template not found");
796
+ let sdkVersion;
797
+ let localCoreVersion;
798
+ const cliVersion = await getCliVersion();
799
+ if (isLocal) {
800
+ const monorepoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "..");
801
+ const sdkPath = join(monorepoRoot, "packages", "portal", "sdk");
802
+ const corePath = join(monorepoRoot, "packages", "portal", "core");
803
+ if (!await directoryExists(sdkPath) || !await directoryExists(corePath)) throw new Error("--local requires running from within the fluid-mono monorepo\n Could not find packages/portal/sdk or packages/portal/core");
804
+ sdkVersion = getLocalPackageLinkVersion(targetPath, sdkPath);
805
+ localCoreVersion = getLocalPackageLinkVersion(targetPath, corePath);
806
+ console.log(chalk.cyan(" Using local packages (--local mode)"));
807
+ } else sdkVersion = await getSdkVersion();
808
+ const spinner = ora("Creating project directory...").start();
809
+ try {
810
+ await createDirectory(targetPath);
811
+ spinner.succeed("Created project directory");
812
+ } catch (error) {
813
+ spinner.fail("Failed to create project directory");
814
+ throw error;
815
+ }
816
+ const templateVariables = {
817
+ projectName: config.name,
818
+ sdkVersion,
819
+ localCoreVersion,
820
+ cliVersion,
821
+ selectedPages: config.selectedPages,
822
+ hasSelectedPages: config.selectedPages.length > 0,
823
+ profileName: config.profileName
824
+ };
825
+ spinner.start("Copying template files...");
826
+ try {
827
+ await copyTemplate(templatePaths.base, targetPath, templateVariables);
828
+ await copyTemplate(templatePaths.overlay, targetPath, templateVariables);
829
+ const envExamplePath = join(targetPath, ".env.example");
830
+ if (await fileExists(envExamplePath)) await copyFile(envExamplePath, join(targetPath, ".env"));
831
+ spinner.succeed("Copied template files");
832
+ } catch (error) {
833
+ spinner.fail("Failed to copy template files");
834
+ throw error;
835
+ }
836
+ }
520
837
  //#endregion
521
838
  //#region src/commands/dev.ts
522
839
  /**
@@ -5311,6 +5628,6 @@ const plugin = {
5311
5628
  }
5312
5629
  };
5313
5630
  //#endregion
5314
- export { FILE_SYSTEM_ERRORS, TEMPLATES, WIDGET_PACKAGE_BUILDER_VERSION, WidgetPackageUploadError, buildIdToSlugMap, buildNavigationIdToSlugMap, buildSharedWidgetPackage, buildSnapshot, buildThemeIdToSlugMap, buildWidgetPackageCompleteUploadPayload, buildWidgetPackageDescriptor, buildWidgetPackageUploadSessionPayload, categorizeChanges, collectWidgetPackageArtifacts, completeWidgetPackageUpload, computeArtifactMetadata, computeFileHash, copyTemplate, copyTemplateSafe, createAuthenticatedWidgetPackageClient, createCommand, createDirectory, createDirectorySafe, createPublishManifestMetadata, createWidgetPackageEntrySource, createWidgetPackagePublishRequest, plugin as default, deployCommand, deriveScreenSlug, deriveSlug, diffAgainstSnapshot, directoryExists, doctorCommand, fileExists, getArtifactContentType, getInstallCommand, getRunCommand, getSdkVersion, getSdkVersionSafe, getTemplatePaths, getWidgetPackageCompleteUploadRoute, getWidgetPackageUploadSessionRoute, installDependencies, loadSourceWidgetPackages, normalizeWidgetPackageCompleteResponse, normalizeWidgetPackageUploadSession, pathExists, printDryRunSessionPayload, printRuntimeOnlyNotice, printWidgetPublishSummary, promptProjectConfig, publishWidgetPackageVersion, publishWidgetRuntimeArtifacts, pullCommand, pushCommand, readFileSafe, readMappings, readSnapshot, removeMapping, requestWidgetPackageUploadSession, resolveIdToSlug, resolvePortalWidgetSourceConfig, resolveSlugToId, runPackageManager, sha256, slugFromPath, topLevelWidgetCommand, transformNavigation, transformNavigationItems, transformProfile, transformScreen, transformTheme, updateMapping, uploadWidgetPackageArtifacts, validateCrossReferences, validateSingleSourceWidgetPackage, validateWidgetPublishOutDir, versionCommand, widgetCommand, writeFileSafe, writeMappings, writeSnapshot };
5631
+ export { CANONICAL_PROJECT_NAME, FILE_SYSTEM_ERRORS, GitError, TEMPLATES, TEMPLATE_REPO_URL, WIDGET_PACKAGE_BUILDER_VERSION, WidgetPackageUploadError, buildIdToSlugMap, buildNavigationIdToSlugMap, buildSharedWidgetPackage, buildSnapshot, buildThemeIdToSlugMap, buildWidgetPackageCompleteUploadPayload, buildWidgetPackageDescriptor, buildWidgetPackageUploadSessionPayload, categorizeChanges, collectWidgetPackageArtifacts, completeWidgetPackageUpload, computeArtifactMetadata, computeFileHash, copyTemplate, copyTemplateSafe, createAuthenticatedWidgetPackageClient, createCommand, createDirectory, createDirectorySafe, createPublishManifestMetadata, createWidgetPackageEntrySource, createWidgetPackagePublishRequest, plugin as default, deployCommand, deriveScreenSlug, deriveSlug, diffAgainstSnapshot, directoryExists, doctorCommand, ensureGitAvailable, fileExists, getArtifactContentType, getInstallCommand, getRunCommand, getSdkVersion, getSdkVersionSafe, getTemplatePaths, getWidgetPackageCompleteUploadRoute, getWidgetPackageUploadSessionRoute, gitClone, gitInit, gitMissingError, installDependencies, isGitAvailable, loadSourceWidgetPackages, normalizeWidgetPackageCompleteResponse, normalizeWidgetPackageUploadSession, pathExists, printDryRunSessionPayload, printRuntimeOnlyNotice, printWidgetPublishSummary, promptProjectConfig, publishWidgetPackageVersion, publishWidgetRuntimeArtifacts, pullCommand, pushCommand, readFileSafe, readMappings, readSnapshot, removeMapping, requestWidgetPackageUploadSession, resolveIdToSlug, resolvePortalWidgetSourceConfig, resolveSlugToId, rewriteProjectName, runPackageManager, scaffoldFromGitHub, sha256, slugFromPath, topLevelWidgetCommand, transformNavigation, transformNavigationItems, transformProfile, transformScreen, transformTheme, updateMapping, uploadWidgetPackageArtifacts, validateCrossReferences, validateSingleSourceWidgetPackage, validateWidgetPublishOutDir, versionCommand, widgetCommand, writeFileSafe, writeFluidrcProfile, writeMappings, writeSnapshot };
5315
5632
 
5316
5633
  //# sourceMappingURL=index.mjs.map