Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion content/(concepts)/spaces.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ What changes:
- The old slug stays reserved for this Space.
- Version URLs do not move. They were computed once and stored.

One thing blocks a rename. While `name` is set in your `sf.jsonc`, the API refuses to rename the Space — the file would win back the old name on the next publish. Remove the key or rename it in the file. See [Config file](/config-file).
One thing blocks a rename. While `name` is set in your project configuration, the API refuses to rename the Space — the file would win back the old name on the next publish. Remove the key or rename it in the file. See [Config file](/config-file).

## Deleting and restoring

Expand Down
20 changes: 9 additions & 11 deletions content/(dynamic)/crons.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
title: Crons
description: Schedule a GET against one of your own paths. Declared in sf.jsonc, applied by publishing, with failures reported in the dashboard and sf crons.
description: Schedule a GET against one of your own paths. Declare crons in project configuration and publish to apply them.
sidebar:
order: 4
---

There's no dashboard editor for crons. Declare schedules in `sf.jsonc`, then publish and make that version live to apply them. A preview does not replace the live version's schedules.
There's no dashboard editor for crons. Declare schedules in `spacefast.config.ts` or `sf.jsonc`, then publish and make that version live to apply them. A preview does not replace the live version's schedules.

## How a run happens

Expand All @@ -23,15 +23,17 @@ A response under 400 is a success and is silent. A response of 400 or worse, or

## Declare it

There is no write API and no dashboard editor. `sf.jsonc` is the only writer, and a publish is the only edit. That is what makes a rollback restore the previous schedules for free.
There is no write API and no dashboard editor. [Project configuration](/config-file) is the only writer, and a publish is the only edit. A rollback restores the previous schedules.

```jsonc sf.jsonc
{
```ts spacefast.config.ts
import { defineConfig } from "spacefast/config";

export default defineConfig({
"crons": [
{ "path": "/api/digest", "schedule": "daily" },
{ "path": "/api/poll", "schedule": "*/15 * * * *" }
]
}
});
```

```bash
Expand Down Expand Up @@ -89,11 +91,7 @@ The run prints a receipt with the URL, status, duration, content type, byte coun
`sf crons run` cannot send the platform's own `x-spacefast-cron` header. A handler that gates on it will refuse a manual run, which is why the example above passes a shared secret explicitly.
:::

Before the first publish, `sf crons ls` prints:

```text
No scheduled jobs declared. Add a `crons` block to sf.jsonc.
```
Before the first publish, `sf crons ls` reports no scheduled jobs. Add a `crons` block to your existing project configuration, either `spacefast.config.ts` or `sf.jsonc`, then publish. Do not create a second configuration file just to add crons.

## Failures

Expand Down
4 changes: 2 additions & 2 deletions content/(dynamic)/database.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ sidebar:
There's no connection string for this database — your code reaches it through `ctx.db` or `env.DB`, or through a single-use SQL console when you need raw SQL.

:::note[Don't have a database yet?]
For [Zero](/zero-runtime), start with `sf init --runtime zero`, or add the full [runtime block](/zero-runtime#declare-it) to `sf.jsonc`, including `kind: "zero"`, `server`, and `client` entry paths. The server file is required; Zero can generate a client shell. Publish the app before querying its live database. A [Functions](/functions#what-is-on-env) worker also gets a database through `env.DB`, but the Zero table commands do not apply to it.
For [Zero](/zero-runtime), start with `sf init --runtime zero`, or add the full [runtime block](/zero-runtime#declare-it) to your existing `spacefast.config.ts` or `sf.jsonc`, including `kind: "zero"`, `server`, and `client` entry paths. The server file is required; Zero can generate a client shell. Publish the app before querying its live database. A [Functions](/functions#what-is-on-env) worker also gets a database through `env.DB`, but the Zero table commands do not apply to it.
:::

## What it is
Expand Down Expand Up @@ -150,7 +150,7 @@ sf db migrate --rename
| `drop_table`, `drop_column`, `drop_index` | `--drop` |
| `rename_table`, `rename_column` | `--rename` |

`sf db migrate` decides Zero-ness from your local `sf.jsonc` before it contacts the API. A static or Functions project fails with `runtime_kind_mismatch` and exit code `30`, having sent no request. `--previous-artifact` diffs against a named artifact instead of the live schema.
`sf db migrate` decides Zero-ness from your local project configuration before it contacts the API. A static or Functions project fails with `runtime_kind_mismatch` and exit code `30`, having sent no request. `--previous-artifact` diffs against a named artifact instead of the live schema.

`sf db`, `sf db dump`, and `sf db migrate` are Zero-only. A worker declares no table set, so they answer `runtime_kind_mismatch` for one.

Expand Down
2 changes: 1 addition & 1 deletion content/(dynamic)/environment-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ So a brand new variable with no flag is write-only. Pass `--no-secret` when you

### Build time, `{{ vars.NAME }}`

List a file under `templates` in `sf.jsonc`. Every `{{ vars.NAME }}` token in it is substituted when the version finalizes.
List a file under `templates` in your existing `spacefast.config.ts` or `sf.jsonc`. Every `{{ vars.NAME }}` token in it is substituted when the version finalizes.

```jsonc sf.jsonc
{
Expand Down
2 changes: 1 addition & 1 deletion content/(dynamic)/functions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default {
};
```

`sf init my-app --runtime functions` writes exactly that file plus the matching `sf.jsonc`.
`sf init my-app --runtime functions` writes exactly that file plus the matching `spacefast.config.ts`.

Requests and responses are the standard `Request` and `Response`. There is no Spacefast wrapper around either.

Expand Down
6 changes: 3 additions & 3 deletions content/(dynamic)/zero-runtime.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ sidebar:
order: 0
---

Zero is available on every account, with [usage allowances and metered rates](/usage). To use it, declare the full [runtime block](#declare-it) in `sf.jsonc` and provide the server entry.
Zero is available on every account, with [usage allowances and metered rates](/usage). To use it, declare the full [runtime block](#declare-it) in your existing project configuration (`spacefast.config.ts` or `sf.jsonc`) and provide the server entry.

## What Zero is

Expand Down Expand Up @@ -50,7 +50,7 @@ sf publish
| Endpoints | Plain HTTP routes in your capsule, for webhooks and callers outside the app. | This page |
| Queries, mutations, and actions | The read, transactional write, and outbound-work paths your client calls. Queries are live. | This page |
| Environment variables | `ctx.env`, from `.env.server` or `sf env`. | [Environment variables](/environment-variables) |
| Crons | Scheduled `GET` requests to your own paths, declared in `sf.jsonc`. | [Crons](/crons) |
| Crons | Scheduled `GET` requests to your own paths, declared in project configuration. | [Crons](/crons) |
| Storage | Runtime object storage for visitor uploads. | [Storage](/object-storage) |
| Logs | What your handlers wrote, plus every request the edge served. | [Logs](/logs) |

Expand Down Expand Up @@ -278,7 +278,7 @@ sf dev
<Card title="Functions" href="/functions" icon="zap">
Workers, the `functions/` file router, and OpenNext.
</Card>
<Card title="sf.jsonc reference" href="/config-file" icon="file-json">
<Card title="Project configuration" href="/config-file" icon="file-json">
Every key the config schema accepts.
</Card>
<Card title="Logs" href="/logs" icon="scroll-text">
Expand Down
10 changes: 6 additions & 4 deletions content/(publish)/git.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,20 @@ The platform builds your repository and publishes the output. It detects the fra

Detected presets include Astro, Next, Nuxt, SvelteKit, Remix, TanStack Start, Vite, Solid Start, Qwik, Angular, Ember, Gatsby, Docusaurus, Eleventy, VitePress, Nextra, Hugo, Jekyll, Zola, MkDocs, and Sphinx. See [Frameworks](/frameworks).

Override any of it in `sf.jsonc` or on the connection.
Repository builds read `spacefast.config.ts` in the isolated build environment. Override build settings there or on the connection. `sf.jsonc` remains supported; TypeScript takes precedence when both files exist. See [Project configuration](/config-file) for imports, migration, and file precedence.

```jsonc sf.jsonc
{
```ts spacefast.config.ts
import { defineConfig } from "spacefast/config";

export default defineConfig({
"build": {
"rootDirectory": "apps/web",
"installCommand": "pnpm install",
"buildCommand": "pnpm build",
"outputDirectory": "dist",
"timeoutSeconds": 900
}
}
});
```

The dashboard's **Build settings** section holds the same four fields as **Root directory**, **Install command**, **Build command**, and **Output directory**, each with an **Auto** placeholder. Leave a field empty and the builder decides from the detected framework. **Detect build settings** re-runs detection and shows candidates with a confidence, which you can **Apply**.
Expand Down
103 changes: 89 additions & 14 deletions content/(reference)/config-file.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,85 @@
---
title: sf.jsonc reference
description: Every key sf.jsonc accepts, what it does, and which defaults the finalizer actually computes
title: Project configuration
description: Configure a project with spacefast.config.ts or sf.jsonc, migrate between formats, and set serving and build defaults.
sidebar:
order: 1
---

`sf.jsonc` is the project config Spacefast reads when a version finalizes. This page lists every key the schema accepts. After it you can write the file by hand and know which defaults are real.
`spacefast.config.ts` is the preferred project configuration for new projects. `sf init` creates it. `sf.jsonc` and its JSON/JSONC aliases remain permanently supported; they are not deprecated.

Both formats declare the settings below. The CLI evaluates TypeScript during a build and freezes the result into JSON configuration for serving. The server does not execute TypeScript configuration on visitor requests.

## TypeScript configuration

```ts spacefast.config.ts
import { defineConfig } from "spacefast/config";

export default defineConfig({
name: "My App",
build: { buildCommand: "npm run build", outputDirectory: "dist" },
crons: [{ path: "/api/digest", schedule: "daily" }],
});
```

The module must have a default export. It can be an object, a promise of an object, or a function returning either. Async functions and local imports work. Package imports resolve from your project, so install those dependencies there.

`spacefast/config` exports `defineConfig`, `SpacefastConfig`, and `SpacefastConfigInput`. `defineConfig` preserves type inference and checks the input type; the CLI validates the resolved configuration when it loads it. `SpacefastConfig` describes the object, while `SpacefastConfigInput` also accepts promises and factories. You can use the object type without the helper:

```ts spacefast.config.ts
import type { SpacefastConfig } from "spacefast/config";

export default {
name: "My App",
cleanUrls: true,
} satisfies SpacefastConfig;
```

The installed CLI supplies `defineConfig` when it loads the file, including global and standalone installations. For editor type checking, add `spacefast` as a project development dependency:

```bash
npm install --save-dev spacefast
```

The resolved object must contain JSON-compatible values. Optional properties with `undefined` values are omitted when the CLI writes JSON. Configuration is executable code: only build projects you trust, and do not put secrets in settings that become part of a published artifact.

## Migrate from sf.jsonc

Migration is optional. There is no `sf migrate config` command; `sf migrate` publishes a site from a public URL.

1. Create `spacefast.config.ts` beside your existing `sf.jsonc`.
2. Add the `defineConfig` import and wrap the existing object in `export default defineConfig(...)`. JSON property names and comments can stay. Remove the optional `$schema` editor pointer; TypeScript uses the exported types instead.
3. Keep all your settings, including `runtime`, routing rules, crons, and build options. Do not replace your file with an empty starter.
4. Run `sf build` from the project root to validate and build without publishing. Fix any configuration or build errors.
5. Remove the old `sf.jsonc` after the build succeeds. Commit the TypeScript file and any dependency changes.

For example, this JSONC:

```jsonc sf.jsonc
{
"$schema": "https://spacefast.com/schemas/sf.json",
"name": "My App",
"cleanUrls": true
}
```

becomes:

```ts spacefast.config.ts
import { defineConfig } from "spacefast/config";

export default defineConfig({
name: "My App",
cleanUrls: true,
});
```

Keep `.spacefast/space.json` if you have linked the project. Changing the config format does not replace that link.

## Repository builds and uploads

Connected Git repository builds read `spacefast.config.ts` too. Commit it with your source; evaluation happens in the isolated build environment. The CLI also accepts it through `--config` (or `SPACEFAST_CONFIG`) on commands that expose that option, including Git build configuration. Do not depend on files or environment variables that exist only on your laptop.

Source builds preserve the authored TypeScript file and put the resolved JSON configuration in the build archive. Raw file uploads and hosted MCP inline publishing cannot execute it. To publish prebuilt content without losing configuration, run `sf build`, then `sf publish .spacefast/build-output.tgz --prebuilt`. If you set `sf build --output`, publish that archive instead. Plain `sf publish` from the project root also loads the project configuration.

## A full example

Expand Down Expand Up @@ -68,19 +142,20 @@ sidebar:

## Where the file lives

The canonical name is `sf.jsonc` at the root of the directory you publish. Six aliases are accepted too. The first file found wins, in this order.
Put the config at the project root. During project builds, the first file found wins, in this order:

1. `sf.jsonc`
2. `spacefast.jsonc`
3. `spacefast.json`
4. `sf.json`
5. `.sf/sf.json`
6. `.sf/config.jsonc`
7. `.sf/config.json`
1. `spacefast.config.ts`
2. `sf.jsonc`
3. `spacefast.jsonc`
4. `spacefast.json`
5. `sf.json`
6. `.sf/sf.json`
7. `.sf/config.jsonc`
8. `.sf/config.json`

All seven are private files. They are never served. Keep exactly one, since two config files in the same tree is an error.
All eight are private files. They are never served. Keep one authored config: if several exist, the CLI warns that it ignored the lower-priority files. An invalid or unreadable winning file fails the command; it does not fall back to another file. An explicit `--config` selects that file instead of discovery.

The file is JSON with comments, capped at 256 KiB. Point `$schema` at `https://spacefast.com/schemas/sf.json` for editor completion and validation.
The JSONC format accepts comments and is capped at 256 KiB. Point `$schema` at [the JSON Schema](https://spacefast.com/schemas/sf.json) for editor completion and validation. That schema describes JSON configuration, not executable TypeScript modules.

## Identity

Expand Down Expand Up @@ -206,7 +281,7 @@ See [Functions](/functions).
| `crons[].path` | string, up to 512 chars | none | Path to GET. Starts with `/`, no query, no wildcards, no `:placeholders` |
| `crons[].schedule` | string | none | A named schedule, a shorthand, or a five-field crontab |

`sf.jsonc` is the only writer. See [Crons](/crons) for the schedule grammar.
Project configuration is the only writer. Use `spacefast.config.ts` or `sf.jsonc`. See [Crons](/crons) for the schedule grammar.

## Access

Expand Down
8 changes: 4 additions & 4 deletions content/(serve)/customization.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ title: Customization
description: Theme the pages Spacefast draws, set the title and image links unfurl with, add an analytics tag, and run one site-wide script
---

Everything you set here lives on the Space, not a version — it survives every publish, and it wins over whatever `theme` or `meta` your `sf.jsonc` declares.
Everything you set here lives on the Space, not a version — it survives every publish, and it wins over whatever `theme` or `meta` your project configuration declares.

Open the Space and go to **Customization**. Four tabs, in order: **Theme**, **Search & sharing**, **Google Analytics**, and **Custom JS**. A large live preview sits beside the controls, labelled **Visitor sees**, painted from your unsaved draft.

## What lives on the Space, not the version

Everything on this page is stored on the Space rather than on a version, so it survives a publish and a rollback alike. It also wins over the published `sf.jsonc`: the Space's `theme` replaces the file's rather than merging with it. So a project that sets `theme` in its config, then edits Theme here, keeps seeing the dashboard's answer until you clear it. The same holds for `meta`.
Everything on this page is stored on the Space rather than on a version, so it survives a publish and a rollback alike. It also wins over the published configuration: the Space's `theme` replaces the file's rather than merging with it. So a project that sets `theme` in its config, then edits Theme here, keeps seeing the dashboard's answer until you clear it. The same holds for `meta`.

Theme and share metadata apply without republishing. Save, and the next request gets them. **Custom JS** is the exception, and its own field note says so: a script change takes effect on the next publish.

Everything the four tabs write is `meta`, `theme`, and `superpowers` in the Space's config. The same keys are documented in the [`sf.jsonc` reference](/config-file), and the [`updateSpace`](/api/reference/spaces/updatespace) operation is what the dashboard calls.
Everything the four tabs write is `meta`, `theme`, and `superpowers` in the Space's config. The same keys are documented in the [Project configuration reference](/config-file), and the [`updateSpace`](/api/reference/spaces/updatespace) operation is what the dashboard calls.

## Theme

Expand Down Expand Up @@ -112,6 +112,6 @@ The header's **Copy as spacefast.jsonc** button, titled "Theme and share metadat
}
```

Paste it into your project's `sf.jsonc` and those two keys travel with the repository. `spacefast.jsonc` is the button's wording and a legacy filename the loader still accepts; the canonical name is `sf.jsonc`. The rest of the file is unaffected, and both keys work under either name.
Merge these two keys into your existing project configuration: inside `defineConfig` for `spacefast.config.ts`, or into the object in `sf.jsonc`. Keep the other settings. Do not create a second configuration file. `spacefast.jsonc` is the button's wording and a supported JSONC alias; it does not take precedence over TypeScript.

One thing to know before you paste it: as long as the Space also carries a `theme`, the Space keeps winning. Clear the Theme tab's fields if you want the file to be the source of truth.
Loading
Loading