npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@usefractal/fracctl-core

v0.3.1

Published

Core implementation package for building Fractal CLIs and developer tools.

Readme

@usefractal/fracctl-core

Core implementation package for building Fractal CLIs and developer tools.

fracctl-core contains the implementation for Fractal auth, deployment, database, and cloud storage operations. A CLI wrapper should own command routing, help text, flag parsing, frac.json lookup, and output formatting.

Install

npm install @usefractal/fracctl-core
pnpm add @usefractal/fracctl-core

TypeScript

The package ships TypeScript declarations. Consumers can import it directly:

import { createFracctlCore, get_runtime_config, type FracctlEnvironment } from "@usefractal/fracctl-core";

The published package exposes dist/index.d.ts through both types and the package exports map.

Runtime Config

The Fractal API URL and WorkOS CLI client ID have package defaults. Both are still configurable: pass explicit values to createFracctlCore(...) or set env vars and read them with get_runtime_config(...).

import { createFracctlCore } from "@usefractal/fracctl-core";

const core = createFracctlCore();

To override either value explicitly:

const core = createFracctlCore({
  baseUrl: "https://your-fractal-api.example.com",
  workosClientId: "client_your_workos_client_id",
});

If your CLI wants to read Fractal environment variables, use the exported helper explicitly:

import { get_runtime_config } from "@usefractal/fracctl-core";

const runtime = get_runtime_config(process.env);

const baseUrl = runtime.baseUrl;
const workosClientId = runtime.workosClientId;
const projectId = runtime.projectId;
const environment = runtime.environment ?? "dev";

get_runtime_config(env) reads:

  • FRACTAL_API_URL
  • FRACTAL_WORKOS_CLIENT_ID
  • FRACTAL_PROJECT_ID
  • FRACTAL_CLOUD_ENVIRONMENT

This is optional. Callers can pass their own values from flags, config files, UI inputs, tests, or any other source.

The same helper is also available on the core object:

const runtime = core.get_runtime_config(process.env);

API

type FracctlCore = {
  get_runtime_config(env?: NodeJS.ProcessEnv): FracctlRuntimeConfig;

  auth_login(): Promise<void>;
  auth_status(): Promise<void>;
  auth_logout(): Promise<void>;

  login(): Promise<void>;
  status(): Promise<void>;
  logout(): Promise<void>;

  list_projects(): Promise<Project[]>;
  create_deployment_project(options: { name: string }): Promise<Project>;
  assert_project_exists(options: { projectId: string }): Promise<Project>;
  deploy_project(options: { projectId: string; sourceDir: string }): Promise<{ deploymentId?: string }>;

  create_cloud_storage_token(options: {
    projectId: string;
    environment: "dev" | "prod";
    expiresInSeconds?: number;
  }): Promise<{ token: string; expiresAt: string; origin: string }>;

  get_database_connection_url(options: {
    projectId: string;
    environment: "dev" | "prod";
  }): Promise<string>;
  open_database_shell(options: {
    projectId: string;
    environment: "dev" | "prod";
  }): Promise<void>;

  enable_cloud_storage(options: { projectId: string }): Promise<Project>;
  cloud_storage_put(options: {
    projectId: string;
    environment: "dev" | "prod";
    localFile: string;
    remotePath: string;
    contentType?: string;
    expiresInSeconds?: number;
  }): Promise<{ key: string; size: number }>;
  cloud_storage_get(options: {
    projectId: string;
    environment: "dev" | "prod";
    remotePath: string;
    expiresInSeconds?: number;
  }): Promise<{ body: Uint8Array }>;
  cloud_storage_list(options: {
    projectId: string;
    environment: "dev" | "prod";
    prefix?: string;
    limit?: number;
    expiresInSeconds?: number;
  }): Promise<CloudStorageListResponse>;
};

Implementing CLI Commands

The wrapper CLI should parse flags and then call the corresponding core method with explicit values.

CLI Wrapper Example

This example shows the expected split: the wrapper reads env vars, parses flags, reads frac.json, resolves projectId, then passes explicit values into core.

#!/usr/bin/env node

import { readFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { CliError, createFracctlCore, get_runtime_config, type FracctlEnvironment } from "@usefractal/fracctl-core";

type FracJson = {
  projectId: string;
};

function readOption(args: string[], names: string[]): string | undefined {
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index];
    for (const name of names) {
      if (arg === name) return args[index + 1];
      if (arg.startsWith(`${name}=`)) return arg.slice(name.length + 1);
    }
  }
  return undefined;
}

function readProjectOption(args: string[]): string | undefined {
  return readOption(args, ["--project", "--project-id", "-p"]);
}

function readEnvironment(args: string[], fallback?: FracctlEnvironment): FracctlEnvironment {
  const environment = readOption(args, ["--env", "-e"]) || fallback || "dev";
  if (environment !== "dev" && environment !== "prod") {
    throw new CliError("--env must be either dev or prod.");
  }
  return environment;
}

async function findFracJson(startDir: string): Promise<FracJson | null> {
  let dir = resolve(startDir);

  while (true) {
    const candidate = join(dir, "frac.json");
    try {
      const parsed = JSON.parse(await readFile(candidate, "utf8")) as Partial<FracJson>;
      if (!parsed.projectId || typeof parsed.projectId !== "string") {
        throw new CliError(`${candidate} is missing projectId.`);
      }
      return { projectId: parsed.projectId };
    } catch (error) {
      if (error instanceof CliError) throw error;
      const code = typeof error === "object" && error && "code" in error ? (error as { code?: string }).code : undefined;
      if (code !== "ENOENT") {
        throw new CliError(`Could not read ${candidate}: ${error instanceof Error ? error.message : String(error)}`);
      }
    }

    const parent = dirname(dir);
    if (parent === dir) return null;
    dir = parent;
  }
}

async function resolveProjectId(args: string[], envProjectId?: string): Promise<string> {
  const flagProjectId = readProjectOption(args);
  if (flagProjectId) return flagProjectId;

  const fracJson = await findFracJson(process.cwd());
  if (fracJson) return fracJson.projectId;

  if (envProjectId) return envProjectId;

  throw new CliError("Project ID is required. Pass --project <id>, add frac.json, or set FRACTAL_PROJECT_ID.");
}

async function main(argv: string[]): Promise<void> {
  const runtime = get_runtime_config(process.env);
  const core = createFracctlCore({
    baseUrl: runtime.baseUrl,
    workosClientId: runtime.workosClientId,
  });

  const [command, ...args] = argv;

  if (command === "auth:login") {
    await core.auth_login();
    return;
  }

  if (command === "token") {
    const projectId = await resolveProjectId(args, runtime.projectId);
    const environment = readEnvironment(args, runtime.environment);
    const token = await core.create_cloud_storage_token({ projectId, environment });
    console.log(token.token);
    return;
  }

  if (command === "sql") {
    const projectId = await resolveProjectId(args, runtime.projectId);
    const environment = readEnvironment(args, runtime.environment);
    console.log(await core.get_database_connection_url({ projectId, environment }));
    return;
  }

  throw new CliError(`Unknown command: ${command || ""}`);
}

main(process.argv.slice(2)).catch((error: unknown) => {
  console.error(error instanceof Error ? error.message : String(error));
  process.exitCode = 1;
});

Auth

await core.auth_login();
await core.auth_status();
await core.auth_logout();

auth_login() starts the WorkOS device login flow and stores the session in macOS Keychain. auth_status() prints the current session status. auth_logout() removes the stored session.

Token

const token = await core.create_cloud_storage_token({
  projectId: "project_123",
  environment: "dev",
  expiresInSeconds: 3600,
});

console.log(`export FRACTAL_CLOUD_TOKEN='${token.token}'`);
console.log(`export FRACTAL_CLOUD_STORAGE_ORIGIN='${token.origin}'`);

A CLI can source projectId from --project, frac.json, FRACTAL_PROJECT_ID, or a prompt. Core just receives the final value.

SQL

const connectionUrl = await core.get_database_connection_url({
  projectId: "project_123",
  environment: "dev",
});

console.log(connectionUrl);

To open psql directly:

await core.open_database_shell({
  projectId: "project_123",
  environment: "dev",
});

Deploy

await core.assert_project_exists({ projectId: "project_123" });

const deployment = await core.deploy_project({
  projectId: "project_123",
  sourceDir: process.cwd(),
});

console.log(deployment.deploymentId);

If a CLI wants to create a deployment-only project when no project is configured:

const project = await core.create_deployment_project({
  name: "My Deployment",
});

await core.deploy_project({
  projectId: project.id,
  sourceDir: process.cwd(),
});

deploy_project() packages sourceDir, uploads it to Fractal, and starts a deployment. It uses .gitignore when packaging from a git worktree and excludes common secret/build paths such as .env, .ssh, .aws, .wrangler, node_modules, dist, and private key files.

Cloud Storage

Enable cloud storage:

await core.enable_cloud_storage({
  projectId: "project_123",
});

Upload an object:

const uploaded = await core.cloud_storage_put({
  projectId: "project_123",
  environment: "dev",
  localFile: "./hello.txt",
  remotePath: "hello.txt",
});

console.log(uploaded.key, uploaded.size);

Download an object:

import { writeFile } from "node:fs/promises";

const downloaded = await core.cloud_storage_get({
  projectId: "project_123",
  environment: "dev",
  remotePath: "hello.txt",
});

await writeFile("./downloaded-hello.txt", downloaded.body);

List objects:

const listing = await core.cloud_storage_list({
  projectId: "project_123",
  environment: "dev",
  prefix: "uploads/",
  limit: 100,
});

for (const object of listing.objects) {
  console.log(object.key, object.size, object.uploadedAt);
}

Error Handling

Core throws CliError for expected user-facing failures. A wrapper CLI should catch errors at the process boundary and print the message.

try {
  await core.cloud_storage_put({
    projectId: "project_123",
    environment: "dev",
    localFile: "./hello.txt",
    remotePath: "hello.txt",
  });
} catch (error) {
  if (error instanceof CliError) {
    console.error(error.message);
    process.exitCode = 1;
  } else {
    throw error;
  }
}

Runtime Requirements

  • Node.js 20 or newer.
  • macOS for Keychain-backed auth storage.
  • psql on PATH for open_database_shell().
  • A valid Fractal API URL and WorkOS client ID provided by the caller.