diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0228ed8..326db28 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [unreleased]
+> [!NOTE]
+> Breaking release. Every project will need a re-scaffold.
+
+### Added
+
+- Named Collections
+ - Support for named collections of assets that are managed together and can be individually previewed.
+ - Expiration trio: --expires-in, --expires-at, --expires-never.
+ - Collection selector utilities that can be run at the edge code to activate a collection.
+ - Default collection never expires.
+
+- git-style subcommands
+ - Actions separated into subcommands, such as `clean`, `publish-content`, and `collections`.
+ - Dry-run mode (--dry-run) for commands that mutates KV or disk.
+
+- KV Store
+ - Bytes of assets are stored in the Fastly KV Store.
+ - Items in the KV Store are keyed by the hash of the file, keeping storage efficient and deduplicated, even across collections.
+ - Metadata (file sizes, encodings, compression) for static assets is stored in KV Store Item metadata.
+ - Large-object chunking: files > 20 MB are split into segments behind the scenes and reassembled at read time.
+ - Upload process has been optimized - files are uploaded in parallel, and are only compressed and uploaded when necessary.
+ - Automatic retry with exponential back-off when the Fastly KV API rate-limits a burst of uploads.
+ - Fully supported in the local development environment during development.
+ - `--local` flag for all management commands. Passing this flag makes the command operate on the local KV Store instead of the Fastly KV Store.
+
+### Changed
+
+- Separate config files:
+ - `static-publisher.rc.js` now owns behavior that is common to the scaffolded Compute app
+ - publish-time settings are in `publish-content.config.js`.
+
+- Script names in the scaffold are now grouped by environment: `dev:publish`, `dev:start`, `fastly:publish`, `fastly:deploy`.
+
+- Asset inclusion test renamed to `kvStoreAssetInclusionTest` and now expects a boolean return value.
+
+### Removed / Deprecated
+
+- This tool drops `wasm-inline`, and no longer inlines bytes of assets into the Wasm binary.
+
+- Static files metadata is no longer stored in the Wasm binary,
+
+- This tool drops `moduleAssets`.
+
## [6.3.0] - 2025-03-19
### Added
diff --git a/LICENSE b/LICENSE
index cb8d11d..56ccc75 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2022 Fastly, Inc.
+Copyright (c) 2025 Fastly, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/MIGRATING.md b/MIGRATING.md
index 795c4d1..3b919b3 100644
--- a/MIGRATING.md
+++ b/MIGRATING.md
@@ -5,256 +5,6 @@ are generated during scaffolding. For this reason, it is recommended that you re
This is straightforward if you're using `compute-js-static-publisher` out-of-the-box. Otherwise, read on.
-## KV Store
-
-Starting with `v5.0.0`, this tool refers to the KV Store using its finalized product name, "KV Store". References in
-code and in configuration that used the previous "Object Store" name have been changed to the new name. If you have
-been using the feature, you can take the following steps:
-
-* In your `static-publish.rc.js` file:
- * Rename the `objectStore` key to `kvStoreName`. For example, if your KV Store is named `'my-store'`, then change:
- ```
- objectStore: 'my-store'
- ```
- to
- ```
- kvStoreName: 'my-store'
- ```
-
-* In your `fastly.toml` file, find all lines that pertain to object store entries.
- * Such lines may look like this and there may be many:
- ```toml
- [[local_server.object_store.my-store]]
- key = "4QEM5nIpyLiNF3wwsbnda5:/404.html_aeed29478691e636b4ded20c17e9eae437614617067a8751882368b965a21bcb"
- path = "output/404.html"
- ```
- * Change the `object_store` of these lines to `kv_stores`. For example:
- ```toml
- [[local_server.kv_stores.my-store]]
- key = "4QEM5nIpyLiNF3wwsbnda5:/404.html_aeed29478691e636b4ded20c17e9eae437614617067a8751882368b965a21bcb"
- path = "output/404.html"
- ```
-
-## Webpack
-
-Starting with `v4.0.0` of this tool, webpack is no longer required and is disabled by default for new applications. This can simplify development and result in shorter build times.
-
-You may still wish to use webpack if you need some of the features it provides, e.g., the ability to use loaders, asset modules, module replacement, dynamic imports, etc.
-
-To migrate away from using webpack, make the following changes in your `./compute-js` directory:
-
-* First, check your `webpack.config.js` file to make sure you aren't actually depending on any custom webpack features. When you're ready, continue to the next step.
-* Delete `webpack.config.js`.
-* Modify `static-publish.rc.js`:
- * Change the line `module.exports = {` to `const config = {`
- * At the end of the file, add `export default config;`
-* In your `package.json` file:
- * At the top level, add a `"type"` key if one doesn't already exist, with the value `"module"`.
- * Under `devDependencies`, remove the `webpack` and `webpack-cli` entries.
- * Under `scripts`, modify the `prebuild` script by removing the `&& webpack` at the end
- of it.
- * Under `scripts`, modify the `build` script by replacing the parameter `./bin/index.js`
- with `./src/index.js`.
- * In the end, the two scripts should look like this (along with any other scripts you may have):
- ```json
- {
- "prebuild": "npx @fastly/compute-js-static-publish --build-static",
- "build": "js-compute-runtime ./src/index.js ./bin/main.wasm"
- }
- ```
-
-If you aren't moving away from webpack just yet, check that your `webpack.config.js` is up-to-date. Refer
-to the [default `webpack.config.js` in this package](./resources/webpack.config.js) and add your changes,
-or modify your configuration file using the following steps:
-
-* To make the resulting bundle easier to debug, it is recommended to set the `devtool` value to `false`.
-
-* The JavaScript SDK automatically adds the named condition `fastly` when resolving dependency packages.
- To match the behavior when bundling with webpack, set `resolve.conditionsNames` to the following:
- ```
- resolve: {
- conditionNames: [
- 'fastly',
- '...',
- ],
- ],
- ```
-
-* Starting `v3.0.0`, we depend on `v1.0.0` of the `js-compute` library, which provides namespaced exports for Fastly
- features. To use them, you'll need to make the following changes to `webpack.config.js`:
-
- * Set the `target` value to `false`.
-
- * The `output` section should look like this:
- ```
- output: {
- filename: "index.js",
- path: path.resolve(__dirname, "bin"),
- chunkFormat: 'commonjs',
- library: {
- type: 'commonjs',
- },
- },
- ```
-
- * Add a new `externals` array to the bottom if it doesn't exist already. Add the following entry:
-
- ```javascript
- module.exports = {
- /* ... other config ... */
- externals: [
- /^fastly:.*$/,
- ],
- }
- ```
-
-* Starting `v3.0.0`, we no longer use webpack static assets to include the contents of static files, and instead [use the
- `includeBytes` function](https://js-compute-reference-docs.edgecompute.app/docs/fastly:experimental/includeBytes)
- to enable more performant loading, as well as a more size-efficient Wasm binary. As a result, the following code can
- safely be removed from the `module.rules` array.
-
- ```javascript
- {
- // asset/source exports the source code of the asset.
- resourceQuery: /staticText/,
- type: "asset/source",
- },
- {
- // asset/inline exports the raw bytes of the asset.
- // We base64 encode them here
- resourceQuery: /staticBinary/,
- type: "asset/inline",
- generator: {
- /**
- * @param {Buffer} content
- * @returns {string}
- */
- dataUrl: content => {
- return content.toString('base64');
- },
- }
- },
- ```
-
-If you need webpack for a new project you are scaffolding with this site, specify the `--webpack` command-line option
-when you scaffold your application, e.g.:
-
-```
-npx @fastly/compute-js-static-publish@latest --webpack --root-dir=./public
-```
-
-## Removal of Expressly
-
-Previous versions of `@fastly/compute-js-static-publish` used [Expressly](https://expressly.edgecompute.app) to serve
-assets. `v4` does away with this dependency and implements its own server in the `PublisherServer`
-class.
-
-When using `v4`, you can remove the dependency on Expressly by deleting the `@fastly/expressly` entry from
-`dependencies` or `devDependencies`, in your `package.json` file.
-
-If your application depended on Expressly for things like middleware, you will need to make further
-changes.
-
-### The entry point `src/index.js`
-
-As of `v4`, the `src/index.js` entry point no longer uses Expressly, and looks like this:
-
-```js
-///
-import { getServer } from './statics.js';
-const staticContentServer = getServer();
-
-// eslint-disable-next-line no-restricted-globals
-addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
-async function handleRequest(event) {
-
- const response = await staticContentServer.serveRequest(event.request);
- if (response != null) {
- return response;
- }
-
- // Do custom things here!
- // Handle API requests, serve non-static responses, etc.
-
- return new Response('Not found', { status: 404 });
-}
-```
-
-If you've previously made changes to `src/index.js`, you will need to make the equivalent changes in this new format.
-
-## `static-publish.rc.js`
-
-This configuration file has changed in v4, and you may find that some features have stopped working after
-upgrading from v3.
-
-* In v3, the configuration object was typed `Config`. In v4, it is now typed with a more descriptive name, `StaticPublisherConfig`.
-
-```js
-/** @type {import('@fastly/compute-js-static-publish').StaticPublisherConfig} */
-const config = {
- rootDir: './public',
- // ... and so on
-};
-```
-
-* A new key, `server`, was added to group configurations that pertain to Publisher Server.
-
-To migrate this file, you'll need to make the following changes:
-
-* `publicDir` - rename this to `rootDir`. All files under this root directory will be included by default in the publishing,
- except for those that are excluded using some of the following features.
-* `excludeDirs`, `includeDirs`, `excludeTest`, `moduleTest` - In v3, these were used in combination to determine whether
- each file would be included in the publishing, and whether files would be included as modules. The interaction between
- these four tests was not clearly defined, often having one option exclude files, only to have other options add them
- back. In addition, in v3 it was not possible to have a module asset that was not also already a content asset.
- In v4, these are more clearly defined. These four options should be rewritten in terms of
- `excludeDirs`, `excludeDotFiles`, `includeWellKnown`, `contentAssetInclusionTest`, and `moduleAssetInclusionTest`.
-* `staticDirs` - in v4, this was renamed to `staticItems` and moved under the new `server` key.
-* `spa` - in v4, this was renamed to `spaFile` and moved under the new `server` key.
-* `notFoundPage` - in v4, this was renamed to `notFoundPageFile` and moved under the new `server` key.
-* `autoExt` - in v4, this was moved under the new `server` key.
-* `autoIndex` - in v4, this was moved under the new `server` key.
-* `contentTypes` - This is unchanged.
-
-See [static-publish.rc.js config file](./README.md#static-publish-rc) for a detailed explanation of each of these new values.
-
-* `.gitignore`
-
- Depending on the version of `compute-js-static-publisher` used to scaffold your application, your `.gitignore` file
- may have been generated with different entries. Add any of the following entries that may be missing from your
- `.gitignore` file:
-
- ```gitignore
- /src/statics.js
- /src/statics.d.ts
- /src/statics-metadata.js
- /src/statics-metadata.d.ts
- /src/static-content
- ```
-
-* Build scripts
- * Various versions of `@fastly/compute-js-static-publish` have specified different build scripts. We recommend the following setup, regardless of the version of `@fastly/compute-js-static-publish` or Fastly CLI.
-
- * The build script listed in `fastly.toml` of your `compute-js` directory should look like this:
- ```toml
- [scripts]
- build = "npm run build"
- ```
-
- * If you're using webpack, then the `scripts` section of `package.json` of your `compute-js` directory should contain
- the following items (along with any other scripts):
- ```json
- {
- "prebuild": "npx @fastly/compute-js-static-publish --build-static && webpack",
- "build": "js-compute-runtime ./bin/index.js ./bin/main.wasm"
- }
- ```
-
- * If you're not using webpack, then the `scripts` section of `package.json` of your `compute-js` directory should
- contain the following items (along with any other scripts):
- ```json
- {
- "prebuild": "npx @fastly/compute-js-static-publish --build-static",
- "build": "js-compute-runtime ./src/index.js ./bin/main.wasm"
- }
- ```
+> [!NOTE]
+> This document is under construction.
+>
diff --git a/README.md b/README.md
index 08bf715..f0eaaaf 100644
--- a/README.md
+++ b/README.md
@@ -1,631 +1,843 @@
# Static Publisher for JavaScript on Fastly Compute
-Using a static site generator to build your website? Do you simply need to serve some static files? With `compute-js-static-publish`, now you can deploy and serve everything from Fastly's blazing-fast [Compute](https://developer.fastly.com/learning/compute/).
+> [!NOTE]
+> These docs are for v7, a major rewrite that adds powerful new features such as named collections.
+> If you're looking for v6, please check out the [v6 branch](https://github.com/fastly/compute-js-static-publish/tree/v6).
-## Prerequisites
+> [!WARNING]
+> Version 7 no longer supports module assets. If you require this feature, consider using [v6](https://github.com/fastly/compute-js-static-publish/tree/v6).
-Although your published application runs on a Fastly Compute service, the publishing process offered by this package requires Node.js 20 or newer.
+## π Table of Contents
-## How it works
+- [β¨ Key Features](#-key-features)
+- [π Quick Start](#-quick-start)
+- [βοΈ Configuring `static-publish.rc.js`](#οΈ-configuring-static-publishrcjs)
+- [π§Ύ Config for Publishing and Server: `publish-content.config.js`](#-config-for-publishing-and-server-publish-contentconfigjs)
+- [π¦ Collections (Publish, Preview, Promote)](#-collections-publish-preview-promote)
+- [π§Ή Cleaning Up](#-cleaning-up)
+- [π Content Compression](#-content-compression)
+- [π§© Using PublisherServer in Custom Apps](#-using-publisherserver-in-custom-apps)
+- [π₯ Using Published Assets in Your Code](#-using-published-assets-in-your-code)
+- [π CLI Reference](#-cli-reference)
+- [π Appendix](#-appendix)
+- [π Next Steps](#-next-steps)
-You have some HTML files, along with some accompanying CSS, JavaScript, image, and font files in a directory. Perhaps you've used a framework or static site generator to build these files.
+Serve static websites and web apps at the edge — no backends and no CDN invalidation delays.
-Assuming the root directory that contains your static files is `./public`,
+`@fastly/compute-js-static-publish` helps you deploy static files to [Fastly Compute](https://developer.fastly.com/learning/compute/) using Fastly's KV Store for fast, cacheable, and content-addressed delivery.
-### 1. Run `compute-js-static-publish`
+## β¨ Key Features
-```shell
-npx @fastly/compute-js-static-publish@latest --root-dir=./public
-```
+- π¦ Easy to scaffold and deploy
+- π Content stored in Fastly KV Store using hashed keys
+- π Publish new content without deploying new Wasm binaries
+- π Organize releases into named collections which can be previewed (e.g. `live`, `staging`, `preview-123`)
+- π§Ό Cleanup tools to remove expired or orphaned files
+- βοΈ Configurable per-collection server configurations (e.g. fallback files)
+- π Supports Brotli/gzip compression, conditional GET, and long cache TTLs
-This will generate a Compute application at `./compute-js`. It will add a default `./compute-js/src/index.js` file that instantiates the [`PublisherServer`](#publisherserver) class and runs it to serve the static files from your project.
+---
-> [!TIP]
-> This process creates a `./compute-js/static-publish.rc.js` to hold your configuration. This, as well as the other files created in your new Compute program at `./compute-js`, can be committed to source control (except for the ones we specify in `.gitignore`!)
+## π Quick Start
+
+### 1. Scaffold a Compute App
+
+Create a directory for your project, place your static files in `./public`, then type:
+
+```sh
+npx @fastly/compute-js-static-publish \
+ --root-dir=./public \
+ --kv-store-name=site-content
+```
+
+You get a Compute app in `./compute-js` with:
-> [!IMPORTANT]
-> This step generates an application that includes your files and a program that serves them, and needs to be run only once. To make modifications to your application, simply make changes to your static files and rebuild it. Read the rest of this section for more details.
+- `fastly.toml` (service config)
+- `src/index.js` (entry point)
+- `static-publish.rc.js` (app config)
+- `publish-content.config.js` (publish-time / runtime behavior)
-### 2. Test your application locally
+### 2. Preview Locally
-The `start` script builds and runs your application using [Fastly's local development server](https://developer.fastly.com/learning/compute/testing/#running-a-local-testing-server).
+Type the following — no Fastly account or service required yet!
-```shell
-cd ./compute-js
+```sh
+cd compute-js
npm install
-npm run start
+npm run dev:publish
+npm run dev:start
```
-The build process scans your `./public` directory to generate files in the `./compute-js/static-publisher` directory. These files are packaged into your application's Wasm binary.
+Fastly's [local development environment](https://www.fastly.com/documentation/guides/compute/testing/#running-a-local-testing-server) serves your static website at `http://127.0.0.1:7676`, powered by a simulated KV Store.
-Once your application is running, your files will be served under `http://127.0.0.1:7676/` at the corresponding paths relative to the `./public` directory. For example, making a request to `http://127.0.0.1:7676/foo/bar.html` will attempt to serve the file at `./public/foo/bar.html`.
+### 3. Deploy Your App
-### 3. Make changes to your application
+Ready to go live? All you need is a [free Fastly account](https://www.fastly.com/signup/?tier=free)!
-Now, you're free to make changes to your static files. Add, modify, or remove files in the `./public` directory, and then re-build and re-run your application by typing `npm run start` again.
+```sh
+npm run fastly:deploy
+```
-Each time you re-build the project, `compute-js-static-publish` will re-scan your `./public` directory and regenerate the files in the `./compute-js/static-publisher` directory.
+The command publishes your Compute app and creates the KV Store. (No content uploaded yet!)
-> [!TIP]
-> You can make further customizations to the behavior of your application, such as specifying directories of your static files, specifying whether to use GZIP compression on your files, specifying custom MIME types of your files, and more. You can also run custom code alongside the default server behavior, or even access the contents of the files directly from custom code. See [Advanced Usages](#advanced-usages) below for details. Rebuilding will not modify the files in your `./compute-js/src` directory, so feel safe making customizations to your code.
+### 4. Publish Content
+
+```sh
+npm run fastly:publish
+```
-### 4. When you're ready to go live, deploy your Compute service
+Upload static files to the KV Store and applies the server config. Your website is now up and live!
-The `deploy` script builds and [publishes your application to a Compute service in your Fastly account](https://developer.fastly.com/reference/cli/compute/publish/).
+---
+
+## π Project Layout
+
+Here's what your project might look like after scaffolding:
-```shell
-npm run deploy
+```
+my-project/
+βββ public/ # Your static site files (HTML, CSS, JS, etc.)
+β βββ index.html
+β βββ scripts.js
+β βββ styles.css
+β βββ (... other static files ...)
+βββ compute-js/ # The scaffolded Compute application
+ βββ fastly.toml # Fastly service configuration
+ βββ package.json # Scaffolded package metadata
+ βββ package-lock.json # Dependency lockfile
+ βββ .gitignore # Ignores build artifacts by default
+ βββ static-publish.rc.js # App config
+ βββ publish-content.config.js # Publishing / runtime config
+ βββ static-publisher/ # β οΈ Do not commit - generated content for local dev and publishing
+ β βββ kvstore.json # Simulates KV Store content for local preview
+ β βββ kv-store-content/ # Preprocessed and compressed files for KV upload
+ βββ src/
+ βββ index.js # Your Compute app entry point
```
-## Features
+## βοΈ Configuring `static-publish.rc.js`
-- Simple to set up, with a built-in server module.
-- Or, make file contents directly available to your application, so you can write your own server.
-- Content and metadata are available to your application, accessible by files' pre-package file paths.
-- Brotli and Gzip compression.
-- Support for `If-None-Match` and `If-Modified-Since` request headers.
-- Optionally use webpack as a module bundler.
-- Selectively serve files from Fastly's [KV Store](#kv-store), or embedded into your Wasm module.
-- Supports loading JavaScript files as code into your Compute application.
-- Presets for several static site generators.
+This file defines your compute-js-static-publish application's settings. A copy of this is also baked into the Wasm binary and loaded when running your Compute app locally or on the edge.
-Some of these features are new! If you wish to update to this version, you may need to re-scaffold your application, or follow the steps outlined in [MIGRATING.md](./MIGRATING.md).
+### Example: `static-publish.rc.js`
-## How does it work? Where are the files?
+```js
+const rc = {
+ kvStoreName: 'site-content',
+ defaultCollectionName: 'live',
+ publishId: 'default',
+ staticPublisherWorkingDir: './static-publisher',
+};
+
+export default rc;
+```
-Once your application is scaffolded, `@fastly/compute-js-static-publish` integrates into your development process by
-running as part of your build process.
+### Fields:
-The files you have configured to be included (`--root-dir`) are enumerated and prepared. Their contents are included into
-your Wasm binary (or made available via [KV Store](#kv-store), if so configured). This process is called "publishing".
+All fields are required.
-Once the files are published, they are available to the other source files in the Compute application. For example,
-the stock application runs the [PublisherServer](#publisherserver) class to serve these files.
+- `kvStoreName` - The name of the KV Store used for publishing.
+- `defaultCollectionName` - Collection to serve when none is specified.
+- `publishId` - Unique prefix for all keys in the KV Store. Override only for advanced setups (e.g., multiple apps sharing the same KV Store).
+- `staticPublisherWorkingDir` - Directory to hold working files during publish.
-For more advanced uses, such as accessing the contents of these file in your application, see the
-[Using the packaged objects in your own application](#using-published-assets-in-your-own-application) section below.
+> [!NOTE]
+> Changes to this file require rebuilding the Compute app, since a copy of it is baked into the Wasm binary.
-Publishing is meant to run each time before building your Compute application into a Wasm file.
-If the files in `--root-dir` have changed, then a new set of files will be published.
+## π§Ύ Config for Publishing and Server: `publish-content.config.js`
-### Content Compression
+This file is included as part of the scaffolding. Every time you publish content, the publish settings in this file are used for publishing the content, and the server settings are taken from this file and saved as the settings used by the server for that collection.
-During publishing, this tool supports pre-compression of content. By default, your assets for select content types are
-compressed using the Brotli and gzip algorithms, and then stored alongside the original files in your Wasm binary (or
-[KV Store](#kv-store)).
+```js
+const config = {
+ // these paths are relative to compute-js dir
+ rootDir: '../public',
+
+ // Include/exclude filters (optional):
+ excludeDirs: ['node_modules'],
+ excludeDotfiles: true,
+ includeWellKnown: true,
+
+ // Advanced filtering (optional):
+ kvStoreAssetInclusionTest: (key, contentType) => {
+ return true; // include everything by default
+ },
+
+ // Override which compressed variants to create for each asset during publish (optional):
+ contentCompression: ['br', 'gzip'],
+
+ // Content type definitions/overrides (optional):
+ contentTypes: [
+ { test: /\.custom$/, contentType: 'application/x-custom', text: false },
+ ],
+
+ // Server settings
+ server: {
+ publicDir: './public',
+ spaFile: '/spa.html',
+ notFoundPageFile: '/404.html',
+ autoIndex: ['index.html'],
+ autoExt: ['.html'],
+ staticItems: ['/static/', '/assets/'],
+ allowedEncodings: ['br', 'gzip'],
+ }
+};
-The content types that are compressed include any that are considered `text` types, as well as certain binary types that
-expect to see a benefit of being compressed.
+export default config;
+```
-* Compressed text types: `.txt` `.html` `.xml` `.json` `.map` `.js` `.css` `.svg`
-* Compressed binary types: `.bmp` `.tar`
+You can override this file for a single `publish-content` command by specifying an alternative using `--config` on the command line.
+
+### Fields:
+
+- `rootDir` - Directory to scan for content, relative to this file (required).
+- `excludeDirs` - Array of directory names or regex patterns to exclude (default: `['./node_modules']`).
+- `excludeDotFiles` - Exclude dotfiles and dot-named directories (default: true).
+- `includeWellKnown` - Always include `.well-known` even if dotfiles are excluded (default: true).
+- `kvStoreAssetInclusionTest` - Function to determine inclusion and variant behavior per file.
+- `contentCompression` - Array of compression formats to pre-generate (`['br', 'gzip']` by default).
+- `contentTypes` - Additional or override content type definitions.
+
+- `server` - Server runtime config that contains the following fields:
+ - `publicDir` - The 'public' directory. The Publisher Server will
+ resolve requests relative to this directory (default: same value as 'root-dir').
+ - `spaFile` - Path to a file to be used to serve in a SPA application.
+ - `notFoundPageFile` - Path to a file to be used to serve as a 404 not found page.
+ - `autoIndex` - List of files to automatically use as index.
+ - `autoExt` - List of extensions to automatically apply to a path and retry when
+ the requested path is not found.
+ - `staticItems` - Directories to specify as containing 'static' files. The
+ Publisher Server will serve files from these directories with a long TTL.
+ - `allowedEncodings` - Specifies which compression formats the server is allowed
+ to serve based on the client's `Accept-Encoding` header.
+
+## π¦ Collections (Publish, Preview, Promote)
+
+Collections are a powerful feature that allow you to publish and manage multiple versions of your site simultaneously. Each collection is a named set of:
+
+- Static files
+- Server configuration (e.g., fallback file, static directories, etc.)
+- An index file that maps paths to those hashes
+
+Collections are published using a `--collection-name`, and can be reused, updated, or deleted independently. For example, you can create a staging version of your site using:
+
+```sh
+npx @fastly/compute-js-static-publish publish-content \
+ --collection-name=staging \
+ --expires-in=7d \
+ --config=./publish-content.config.js
+```
-To configure these content types, use the `contentTypes` field of the [`static-publish.rc.js` config file](#static-publish-rc).
+You can overwrite or republish any collection at any time. Old file hashes will be reused automatically where contents match.
-> [!IMPORTANT]
-> By default, pre-compressed content assets are not generated when the KV Store is not used.
-This is done to prevent the inclusion multiple of copies of each asset from making the Wasm binary too large.
-If you want to pre-compress assets when not using KV Store, add a value for 'contentCompression' to your
-`static-publish.rc.js` file.
+### Expiration (Auto-cleanup)
-## CLI options
+Collections can expire automatically:
-Except for `--root-dir`, most arguments are optional.
+- Expired collections are ignored by the server and return 404s
+- The default collection never expires
+- Expiration limits can be modified (shortened, extended, reenstated) using `collections update-expiration`
+- They are cleaned up by `clean --delete-expired-collections`
-```shell
-npx @fastly/compute-js-static-publish \
- --root-dir=./build \
- --public-dir=./build/public \
- --static-dir=./build/public/static \
- --output=./compute-js \
- --spa=./build/spa.html
+```sh
+--expires-in=3d # relative (e.g. 1h, 2d, 1w)
+--expires-at=2025-05-01T12:00Z # absolute (ISO 8601)
+--expires-never # the collection never expires
```
-If you provide options, they override the defaults described below.
+*(Only one of **`--expires-in`**, **`--expires-at`**, or **`--expires-never`** may be specified)*
-Any configuration options will be written to a `static-publish.rc.js` file, and used each time you build your Compute
-application.
+### Switching the active collection
-On subsequent builds of your Compute application, `compute-js-static-publish` will run with a special flag, `build-static`,
-reading from stored configuration, then scanning the `--public-dir` directory to recreate `./compute-js/static-publisher/statics.js`.
+By default, the server app serves assets from the "default collection", named in `static-publish.rc.js` under `defaultCollectionName`. To switch the active collection, you add custom code to your Compute app that calls `publisherServer.setActiveCollectionName(name)`:
-Any relative file and directory paths passed at the command line are handled as relative to the current directory.
+```js
+publisherServer.setActiveCollectionName("preview-42");
+```
-### Publishing options:
+This only affects the current request (in Compute, requests do not share state).
-| Option | Default | Description |
-|-----------------------------|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `--preset` | (None) | Apply default options from a specified preset. See ["Frameworks and Static Site Generators"](#usage-with-frameworks-and-static-site-generators). |
-| `--output` | `./compute-js` | The directory in which to create the Compute application. |
-| `--static-content-root-dir` | (output directory) + `/static-publisher` | The directory under the Compute application where static asset and metadata are written. |
-| `--root-dir` | (None) | **Required**. The root of the directory that contains the files to include in the publishing. All files you wish to include must reside under this root. |
+#### Example: Subdomain-based Routing
-### Server options:
+In the following example, assume that the Compute application is hosted using a wildcard domain `*.example.com`. A request for `preview-pr-123.example.com` would activate the collection `'pr-123'`.
-Used to populate the `server` key under `static-publish.rc.js`.
+```js
+import { PublisherServer, collectionSelector } from '@fastly/compute-js-static-publish';
+import rc from '../static-publish.rc.js';
-| Option | Default | Description |
-|--------------------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `--public-dir` | | The directory that contains your website's public files. |
-| `--static-dir` | (None) | Any directories under `--public-dir` that contain the website's static assets that will be served with a very long TTL. You can specify as many such directories as you wish, by listing multiple items. |
-| `--auto-ext` | `.html,.htm` | Specify automatic file extensions. |
-| `--auto-index` | `index.html,index.htm` | Specify filenames for automatically serving an index file. |
-| `--spa` | (None) | Path to a fallback file for SPA applications. |
-| `--not-found-page` | `/404.html` | Path to a fallback file for 404 Not Found. |
+const publisherServer = PublisherServer.fromStaticPublishRc(rc);
-See [PublisherServer](#publisherserver) for more information about these features.
+addEventListener("fetch", event => {
+ const request = event.request;
+ const collectionName = collectionSelector.fromHostDomain(request, /^preview-([^\.]*)\./);
+ if (collectionName != null) {
+ publisherServer.setActiveCollectionName(collectionName);
+ }
-For backwards compatibility, if you do not specify a `--root-dir` but you have provided a `--public-dir`, then that value is used for `--root-dir`.
+ event.respondWith(publisherServer.serveRequest(request));
+});
+```
-Note that the files referenced by `--spa` and `--not-found-page` do not necessarily have to reside inside `--public-dir`.
+### π Selecting a Collection at Runtime
-### Fastly service options
+The `collectionSelector` module provides helpers to extract a collection name from different parts of a request:
-These arguments are used to populate the `fastly.toml` and `package.json` files of your Compute application.
+```js
+collectionSelector.fromHostDomain(request, /^preview-([^\.]*)\./);
+```
+
+#### From the Request URL
-| Option | Default | Description |
-|-------------------|--------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `--name` | `name` from `package.json`, or `compute-js-static-site` | The name of your Compute application. |
-| `--description` | `description` from `package.json`, or `Compute static site` | The description of your Compute application. |
-| `--author` | `author` from `package.json`, or `you@example.com` | The author of your Compute application. |
-| `--service-id` | (None) | The ID of an existing Fastly WASM service for your Compute application. |
-| `--kv-store-name` | (None) | The name of an existing [Fastly KV Store](https://developer.fastly.com/learning/concepts/data-stores/#kv-stores) to hold the content assets. In addition, a Resource Link to this KV Store must already exist on the service specified by `--service-id` and have the same name as the KV Store. |
+```js
+collectionSelector.fromRequestUrl(request, url => url.pathname.split('/')[2]);
+```
-## Usage with frameworks and static site generators
+#### With a Custom Request Matcher
-`compute-js-static-publish` supports preset defaults for a number of frameworks and static site generators:
+```js
+collectionSelector.fromRequest(request, req => req.headers.get('x-collection') ?? 'live');
+```
-| `--preset` | `--root-dir` | `--static-dir` | Notes |
-|-------------------------------|--------------|------------------|-------------------------------------------------------------------------------------------------------------------------------------|
-| `cra` (or `create-react-app`) | `./build` | `./build/static` | For apps written using [Create React App](https://create-react-app.dev). Checks for a dependency on `react-scripts`. |
-| `cra-eject` | `./build` | `./build/static` | For apps written using Create React App, but which have since been ejected via `npm run eject`. Does not check for `react-scripts`. |
-| `vite` | `./dist` | (None) | For apps written using [Vite](https://vitejs.dev). |
-| `sveltekit` | `./dist` | (None) | For apps written using [SvelteKit](https://kit.svelte.dev). |
-| `vue` | `./dist` | (None) | For apps written using [Vue](https://vuejs.org), and that were created using [create-vue](https://github.com/vuejs/create-vue). |
-| `next` | `./out` | (None) | For apps written using [Next.js](https://nextjs.org), using `npm run export`. *1 |
-| `astro` | `./dist` | (None) | For apps written using [Astro](https://astro.build) (static apps only). *2 |
-| `gatsby` | `./public` | (None) | For apps written using [Gatsby](https://www.gatsbyjs.com). |
-| `docusaurus` | `./build` | (None) | For apps written using [Docusaurus](https://docusaurus.io) |
+#### From a Cookie
-You may still override any of these options individually.
+See [fromCookie](#fromcookie) for details on this feature.
-*1 - For Next.js, consider using `@fastly/next-compute-js`, a Next.js server implementation that allows you to run
- your Next.js application on Compute.
+```js
+const { collectionName, redirectResponse } = collectionSelector.fromCookie(request);
+```
-*2 - Astro support does not support SSR.
+#### From a Fastly Config Store
-## PublisherServer
+```js
+collectionSelector.fromConfigStore('my-config-store', 'collection-key');
+```
-`PublisherServer` is a simple yet powerful server that can be used out of the box to serve the files prepared by this tool.
+### π Promoting a Collection
-This server handles the following automatically:
+If you're happy with a preview or staging collection and want to make it live, use the `collections promote` command:
-* Maps the path of your request to a path under `--public-dir` and serves the content of the asset
-* Sources the content from the content packaged in the Wasm binary, or from the [KV Store](#kv-store), if configured.
-* Applies long-lived cache headers to files served from `--static-dir` directories. Files under these directories will be cached by the browser for 1 year. (Use versioned or hashed filenames to avoid serving stale assets.)
-* Performs Brotli and gzip compression as requested by the `Accept-Encoding` headers.
-* Provides `Last-Modified` and `ETag` response headers, and uses them with `If-Modified-Since` and `If-None-Match` request headers to produce `304 Not Modified` responses.
-* If an exact match is not found for the request path, applies automatic extensions (e.g., `.html`) and automatic index files (e.g., `index.html`).
-* Can be configured to serve a fallback file for SPA apps. Useful for apps that use [client-side routing](https://create-react-app.dev/docs/deployment#serving-apps-with-client-side-routing).
-* Can be configured to serve a 404 not found file.
-* Returns `null` if nothing matches, so that you can add your own handling if necessary.
+```sh
+npx @fastly/compute-js-static-publish collections promote \
+ --collection-name=staging
+ --to=live
+```
-During initial scaffolding, the configuration based on the command-line parameters and preset are written to your `./static-publisher.rc.js` file under the `server` key.
+This copies all content and server settings from the `staging` collection to `live`.
-### Configuring PublisherServer
+You can also specify a new expiration:
-You can further configure the server by making modifications to the `server` key under `./static-publisher.rc.js`.
+```sh
+npx @fastly/compute-js-static-publish collections promote \
+ --collection-name=preview-42 \
+ --to=staging \
+ --expires-in=7d
+```
-| Key | Default | Description |
-|--------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `publicDirPrefix` | `''` | Prefix to apply to web requests. Effectively, a directory within `rootDir` that is used by the web server to determine the asset to respond with. |
-| `staticItems` | `[]` | A test to apply to item names to decide whether to serve them as "static" files, in other words, with a long TTL. These are used for files that are not expected to change. They can be provided as a string or array of strings. |
-| `compression` | `[ 'br', 'gzip' ]` | If the request contains an `Accept-Encoding` header, they are checked for the values listed here. The compression algorithm that produces the smallest transfer size is applied. |
-| `autoExt` | `[]` | When a file is not found, and it doesn't end in a slash, then try auto-ext: we try to serve a file with the same name post-fixed with the specified strings, tested in the order listed. These are tested before auto-index, if any. |
-| `autoIndex` | `[]` | When a file is not found, then try auto-index: we treat it as a directory, then try to serve a file that has the specified strings, tested in the order listed. |
-| `spaFile` | `null` | Asset key of a content item to serve with a status code of `200` when a GET request comes arrives for an unknown asset, and the Accept header includes text/html. |
-| `notFoundPageFile` | `null` | Asset key of a content item to serve with a status code of `404` when a GET request comes arrives for an unknown asset, and the Accept header includes text/html. |
+> [!NOTE]
+> The collection denoted by `defaultCollectionName` is exempt from expiration.
-For `staticItems`:
-* Items that contain asterisks are interpreted as glob patterns (for example, `/public/static/**/*.js`)
-* Items that end with a trailing slash are interpreted as a directory name.
-* Items that don't contain asterisks and that do not end in slash are checked for exact match.
+## π Development β Production Workflow
-For `compression`, the following values are allowed:
-* `'br'` - Brotli
-* `'gzip'` - Gzip
+### Local development
-## Associating your project with a Fastly Service
+During development, the local preview server (`npm run dev:start`) will run against assets loaded into the simulated KV Store provided by the local development environment.
-The project created by this tool is a Fastly Compute JavaScript application, complete with a `fastly.toml` file that
-describes your project to the Fastly CLI.
+Prior to starting the server, publish the content to the simulated KV Store:
-To deploy your project to production, you deploy it to a [Fastly service](https://developer.fastly.com/reference/glossary#term-service)
-in your account. Usually, you create your service automatically as part of your first deployment of the project.
+```sh
+npm run dev:publish # 'publish' your files to the simulated local KV Store
+npm run dev:start # preview locally
+```
-In this case, `fastly.toml` has no value for `service_id` at the time you deploy, so the Fastly CLI will prompt
-you to create a Fastly service in your account, after which it will save the new service's ID to your `fastly.toml` file.
+This simulates publishing by writing to `kvstore.json` instead of uploading to the actual KV Store. You can preview your site at `http://127.0.0.1:7676` - no Fastly account or service required.
-Alternatively, you may deploy to a service that already exists. You can create this service using the
-[Fastly CLI](https://developer.fastly.com/reference/cli/service/create/) or the [Fastly web app](https://manage.fastly.com/).
-Note that since this is a Compute application, the service must be created as a Wasm service.
+Note that for local development, you will have to stop and restart the local development server each time you publish updates to your content.
-Before deploying your application, specify the service by setting the `service_id` value in the `fastly.toml` file to the
-ID of the service. The Fastly CLI will deploy to the service identified by this value.
+To publish to an alternative collection name, use:
-To specify the service at the time you are scaffolding the project (for example, if you are running this tool and deploying
-as part of a CI process), specify the `--service-id` command line argument to populate `fastly.toml` with this value.
+```sh
+npm run dev:publish -- --collection-name=preview-123
+```
-## Using the KV Store (BETA)
+### Production
-
+When you're ready for production:
-Starting with v4, it's now possible to upload assets to and serve them from a [Fastly KV Store](https://developer.fastly.com/learning/concepts/data-stores/#kv-stores).
+1. [Create a free Fastly account](https://www.fastly.com/signup/?tier=free) if you haven't already.
+2. Run `npm run fastly:deploy`
+ - This builds your Compute app into a Wasm binary
+ - Deploys it to a new or existing Fastly Compute service
+ - If creating a new service:
+ - you'll be prompted for backend info - **you can skip this**, as no backend is needed (all content is served from KV)
+ - KV Store will be created if necessary and automatically linked to your new service.
-When this mode is enabled, you build your application as normal, and as a step during the build, your files
-are uploaded to the Fastly KV Store, and metadata in the application is marked to source them from there instead
-of from bytes in the Wasm binary.
+Once deployed, publish content like so:
-You can enable the use of KV Store with `@fastly/compute-js-static-publish` as you scaffold your application, or
-at any later time.
+```sh
+npm run fastly:publish
+```
-At the time you enable the use of KV Store:
+This:
-* Your Fastly service must already exist. See [Associating your project with a Fastly Service](#associating-your-project-with-a-fastly-service) above.
+- Uses the default collection name
+- Uploads static files to the KV Store
+- Stores server configuration for the collection
-* Your [KV Store](https://www.fastly.com/documentation/guides/concepts/edge-state/data-stores/#kv-stores) must already
- exist under the same Fastly account, and be linked to the service. The
- [Resource Link](https://www.fastly.com/documentation/guides/concepts/resources/) must have the same name as the
- KV Store.
+> [!TIP]
+> Upload to a specific collection by specifying the collection name when publishing content:
+> ```sh
+> npm run fastly:publish -- --collection-name=preview-42
+> ```
-To create your KV Store and link it to the service, follow these steps:
+**No Wasm redeploy needed** unless you:
-```shell
-# Create a KV Store
-$ npx fastly kv-store create --name=example-store
-SUCCESS: Created KV Store 'example-store' (onvbnv9ntdvlnldtn1qwnb)
-```
+- Modify `src/index.js` - such as when you update your custom routing logic (e.g. collection selection) or
+- Change `static-publish.rc.js`
-Make a note of the KV Store ID in this response (`onvbnv9ntdvlnldtn1qwnb` in the above example). Next, use this ID
-value to create a Resource Link between your service and the KV Store. You **MUST** use the same name for your Resource
-Link as you use for your KV Store. After linking the KV Store, activate the new version of the service.
+If you do need to rebuild and redeploy the Compute app, simply run:
-```shell
-# Link the KV Store to a service provide the KV Store ID as resource-id
-$ npx fastly resource-link create --version=latest --autoclone --resource-id=onvbnv9ntdvlnldtn1qwnb --name=example-store
-$ npx fastly service-version activate --version=latest
+```sh
+npm run dev:deploy
```
-Once the KV Store is created and linked to your service, add the name of the KV Store (`example-store` in
-γthe above example) name to your `static-publish.rc.js` file under the `kvStoreName` key.
+## π§Ή Cleaning Up
-To specify the KV Store at the time you are scaffolding the project (for example, if you are running this tool and
-deploying as part of a CI process), specify the `--service-id` and `--kv-store-name` command line arguments to populate
-the respective files with these values.
+Every time you publish, old files are left behind for safety. **However, files with the same content will be re-used across collections and publishing events.** They are only stored once in the KV Store using their content hash as a key. This ensures that unchanged files aren't duplicated, keeping storage efficient and deduplicated.
-After you have performed the above steps, go ahead and build your application as normal.
-As a new step during the build process, the tool will send these files to the KV Store.
+Over time, however, collections may expire, old versions of files will be left behind, and some assets in the KV Store will no longer be referenced by any live collection. To avoid bloat, use:
-> [!IMPORTANT]
-> This step writes to your KV Store. When building your application, you must set the environment variable `FASTLY_API_TOKEN` to a Fastly API token that has access to write to this KV Store.
->
-> Alternatively, if this environment variable is not found, the tool will attempt to detect an API token by calling `fastly profile token`.
+```sh
+npm run dev:clean
+```
+and
+```sh
+npm run fastly:clean
+```
-> [!TIP]
-> By running `npx @fastly/cli compute build --verbose` (or `npm run build` directly), you should see output in your logs saying that files are being sent to the KV Store.
+These scripts run against the local and Fastly KV Stores respectively, and run the following command:
+```
+npx @fastly/compute-js-static-publish clean --delete-expired-collections
+```
-The `statics-metadata.js` file should now show `"type": "kv-store"` for content assets.
-Your Wasm binary should also be smaller, as the content of the files are no longer inlined in the build artifact.
-You can deploy this and run it from Fastly, and the referenced files will be served from KV Store.
+This removes:
-You will also see entries in `fastly.toml` that represent the local KV Store.
-These enable the site to also run correctly when served using the local development environment.
+- Expired collection index files (only if `--delete-expired-collections` is passed)
+- Unused content blobs (no longer referenced)
+- Orphaned server config files
-### Cleaning unused items from KV Store
+### π Dry Run Mode
-The files that are uploaded to the KV Store are submitted using keys of the following format:
+Preview what will be deleted without making changes:
-`:__`
+```sh
+npx @fastly/compute-js-static-publish clean --dry-run
+```
-For example:
-`12345abcde67890ABCDE00:/public/index.html_br_aeed29478691e67f6d5s36b4ded20c17e9eae437614617067a8751882368b965`
+> β οΈ Cleanup never deletes the default collection and never deletes content thatβs still in use.
-Using such a key ensures that whenever the file contents are identical, the same key will be generated.
-This enables to detect whether an unchanged file already exists in the KV Store, avoiding having to re-submit
-files that have not changed. If the file contents have changed, then a new hash is generated. This ensures that
-even during the brief amount of time between deploys, any request served by a prior version will still serve the same
-corresponding previous version of the content.
-
-However, this system never deletes files automatically. After many deployments, extraneous files may be left over.
-
-`@fastly/compute-js-static-publish` includes a feature to delete these old versions of the files that are no longer being
-used. To run it, type the following command:
-
-`npx @fastly/compute-js-static-publish --clean-kv-store`
+## π Content Compression
-It works by scanning `statics-metadata.js` for all currently-used keys. Then it enumerates all the existing
-keys in the configured KV Store and that belong to this application (can do so by narrowing down all keys to the ones
-that begin with the "publish id"). If any of the keys is not in the list of currently-used keys, then a request is made
-to delete that KV Store value.
+This project supports pre-compressing and serving assets in Brotli and Gzip formats. Compression is controlled at two different stages:
-And that's it! It should be possible to run this task to clean up once in a while.
+- **During publishing**, the `contentCompression` field in the `publish` section of `publish-content.config.js` defines which compressed variants (e.g., `br`, `gzip`) should be generated and uploaded to the KV Store.
-## Advanced Usages
+Assets are stored in multiple formats (uncompressed + compressed) if configured. The following file types are compressed by default:
-### The `static-publish.rc.js` config file
+- Text-based: `.html`, `.js`, `.css`, `.svg`, `.json`, `.txt`, `.xml`, `.map`
+- Certain binary formats: `.bmp`, `.tar`
-* `rootDir` - All files under this root directory will be included by default in the publishing,
- except for those that are excluded using some of the following features. Files outside this root cannot be
- included in the publishing.
+- **At runtime**, the `allowedEncodings` field in the `server` section of `publish-content.config.js` specifies which compression formats the server is allowed to serve based on the client's `Accept-Encoding` header.
-* `staticContentRootDir` - Static asset loader and metadata files are created under this directory.
- For legacy compatibility, if not provided, defaults to `'./src'`.
+`PublisherServer` will serve the smallest appropriate version based on the `Accept-Encoding` header.
-* `kvStoreName` - Set this value to the _name_ of an existing KV Store to enable uploading of content assets
- to Fastly KV Store. See [Using the KV Store](#kv-store) for more information.
+## π§© Using PublisherServer in Custom Apps
-* `excludeDirs` - Specifies names of files and directories within `rootDir` to exclude from the publishing. Each entry can
- be a string or a JavaScript `RegExp` object. Every file and directory under `rootDir` is checked against each entry of
- the array by testing its path relative to `rootDir`. The file or directory (included all children) and excluded if the
- condition matches:
- * If a string is specified, then an exact match is checked.
- * If a `RegExp` is specified, then it is tested with the regular expression.
- * If this setting is not set, then the default value is `['./node_modules']`.
- * If you specifically set this to the empty array, then no files are excluded by this mechanism.
+You can combine PublisherServer with custom logic to support APIs, authentication, redirects, or A/B testing. `PublisherServer` returns `null` when it cannot handle a request, allowing you to chain in your own logic.
-* `excludeDotfiles` - Unless disabled, will exclude all files and directories (and their children)
- whose names begin with a `'.''`. This is `true` by default.
+```js
+import { PublisherServer } from '@fastly/compute-js-static-publish';
+import rc from '../static-publish.rc.js';
-* `includeWellKnown` - Unless disabled, will include a file or directory called `.well-known`
- even if `excludeDotfiles` would normally exclude it. This is `true` by default.
+const publisherServer = PublisherServer.fromStaticPublishRc(rc);
-* `contentAssetInclusionTest` - Optionally specify a test function that can be run against each enumerated asset during
- the publishing, to determine whether to include the asset as a content asset. For every file, this function is passed
- the [asset key](#asset-keys), as well as its content type (MIME type string). You may return one of three values from
- this function:
- * Boolean `true` - Include the file as a content asset in this publishing. Upload the file to and serve it from the
- KV Store if KV Store mode is enabled, or include the contents of the file in the Wasm binary if KV Store
- mode is not enabled.
- * String `"inline"` - Include the file as a content asset in this publishing. Include the contents of the file in the
- Wasm binary, regardless of whether KV Store mode is enabled.
- * Boolean `false` - Do not include this file as a content asset in this publishing.
+addEventListener("fetch", event => {
+ event.respondWith(handleRequest(event.request));
+});
- If you do not provide a function, then every file will be included in this publishing as a content asset, and their
- contents will be uploaded to and served from the KV Store if KV Store mode is enabled, or included in the Wasm
- binary if KV Store mode is not enabled.
+async function handleRequest(request) {
+ const response = await publisherServer.serveRequest(request);
+ if (response) {
+ return response;
+ }
-* `contentCompression` - During the publishing, the tool will pre-generate compressed versions of content assets in these
- formats and make them available to the Publisher Server or your application. Default value is [ 'br' | 'gzip' ] if
- KV Store is enabled, or [] if KV Store is not enabled.
+ // Add your custom logic here
+ if (request.url.endsWith('/api/hello')) {
+ return new Response(JSON.stringify({ greeting: "hi" }), {
+ headers: { 'content-type': 'application/json' }
+ });
+ }
-* `moduleAssetInclusionTest` - Optionally specify a test function that can be run against each enumerated asset during
- the publishing, to determine whether to include the asset as a module asset. For every file, this function is passed
- the [asset key](#asset-keys), as well as its content type (MIME type string). You may return one of three values from this function:
- * `true` (boolean) - Include the file as a module asset in this publishing.
- * `"static-import"` (string) - Include the file as a module asset in this publishing, and statically import it. This causes
- any top-level code in these modules to run at application initialization time.
- * `false` (boolean) - Do not include this file as a module asset in this publishing.
+ return new Response("Not Found", { status: 404 });
+}
+```
- If you do not provide a function, then no module assets will be included in this publishing.
+## π₯ Using Published Assets in Your Code
-* `contentTypes` - Provide custom content types and/or override them.
+To access files you've published, use the `getMatchingAsset()` and `loadKvAssetVariant()` methods on `publisherServer`.
- This tool comes with a [default set of content types](./src/util/content-types.ts) defined for many common
- file extensions. This list can be used to add to and/or override items in the default list.
- Content type definitions are checked in the provided order, and if none of them match, the default content types are
- tested afterward.
+### Access Metadata for a File:
- Provide these as an array of content type definition objects, each with the following keys and values:
- * `test` - a RegExp or function to perform on the asset key. If the test succeeds, then the content asset is considered
- to be of this content type definition.
- * `contentType` - The content type header to apply when serving an asset of this content type definition.
- * `text` - If `true`, this content type definition is considered to contain textual data. This makes `.text()` and `.json()`
- available for calling on store entries. If not specified, this is treated as `false`.
- * `precompressAsset` - When `true`, this tool generates pre-compressed versions of content assets and serves them to user
- agents that assert an appropriate `Accept` header. See [Content Compression*](#content-compression) for details. If
- not specified, this is `true` if `text` is `true`, and `false` if `text` is not `true`.
-
- For example, to add a custom content type `application/x-custom` for files that have a `.custom` extension, not treat
- it as a text file, but precompress it during the generation of the application, add the following to your
- `static-publish.rc.js` file:
-
- ```javascript
- const config = {
- /* ... other config ... */
- contentTypes: [
- { test: /\.custom$/, contentType: 'application/x-custom', text: false, precompressAsset: true },
- ],
- };
- ```
+```js
+const asset = await publisherServer.getMatchingAsset('/index.html');
+if (asset != null) {
+ // Asset exists with that path
+ asset.contentType; // e.g., 'text/html'
+ asset.lastModifiedTime; // Unix timestamp
+ asset.size; // Size in bytes of the base version
+ asset.hash; // SHA256 hash of base version
+ asset.variants; // Available variants (e.g., ['gzip'])
+}
+```
- > Note that content types are tested at publishing time, not at runtime.
-
-* `server` - [Configuration of `PublisherServer()`](#configuring-publisherserver).
- above.
-
-### Running custom code alongside Publisher Server
-
-The generated `./src/index.js` program instantiates the server and simply asks it to respond to a request.
-
-You are free to add code to this file.
-
-For example, if the `PublisherServer` is unable to formulate a response to the request, then it returns `null`. You may
-add your own code to handle these cases, such as to provide custom responses.
+### Load the File from KV Store:
```js
-import { getServer } from './statics.js';
-const staticContentServer = getServer();
+const kvAssetVariant = await publisherServer.loadKvAssetVariant(asset, null); // pass 'gzip' or 'br' for compressed
-addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
-async function handleRequest(event) {
+kvAssetVariant.kvStoreEntry; // KV Store entry (type KVStoreEntry defined in 'fastly:kv-store')
+kvAssetVariant.size; // Size of the variant
+kvAssetVariant.hash; // SHA256 of the variant
+kvAssetVariant.contentEncoding; // 'gzip', 'br', or null
+kvAssetVariant.numChunks; // Number of chunks (for large files)
+```
- const response = await staticContentServer.serveRequest(event.request);
- if (response != null) {
- return response;
- }
-
- // Do custom things here!
- // Handle API requests, serve non-static responses, etc.
+You can stream `kvAssetVariant.kvStoreEntry.body` directly to a `Response`, or read it using `.text()`, `.json()`, or `.arrayBuffer()` depending on its content type.
- return new Response('Not found', { status: 404 });
-}
-```
+---
-### Using published assets in your own application
+## π CLI Reference
-Publishing, as described earlier, is the process of preparing files for inclusion into your application.
-This process also makes metadata available about each of the files that are included, such as its content type, the last
-modified date, the file hash, and so on.
+### Available Commands
-The [`PublisherServer` class](#publisherserver) used by the default scaffolded application is a simple application of this content
-and metadata. By importing `./statics.js` into your Compute application, you can just as easily access this
-information about the assets that were included during publishing.
+#### Outside a Compute App Directory
+- `npx @fastly/compute-js-static-publish [options]` - Scaffold a new Compute app
-> IMPORTANT: Use a static `import` statement, rather than using `await import()` to load `./statics.js`, in order to
-ensure that its top-level code runs during the initialization phase of your Compute application.
+#### Inside a Compute App Directory
+- `publish-content` - Publish static files to the KV Store under a named collection
+- `clean` - Delete expired and unreferenced KV entries
+- `collections list` - List all published collections
+- `collections delete` - Delete a specific collection index
+- `collections promote` - Copy a collection to another name
+- `collections update-expiration` - Modify expiration time for an existing collection
-#### Assets
+### π App Scaffolding
-There are two categories of assets: Content Assets and Module Assets.
+Run outside an existing Compute app directory:
+
+```sh
+npx @fastly/compute-js-static-publish \
+ --root-dir=./public \
+ --kv-store-name=site-content \
+ [--output=./compute-js] \
+ [--static-publisher-working-dir=