A pack is a .feb: a zip whose root is manifest.json plus the modules it
names. The manifest maps page ids to modules — the pack’s implementation
of the host contract.
Authors write a file tree of pages (ids, not URL routes). The tree below is Svelte; other adapters swap the component and head extensions and keep the same folder ids — see Framework adapters:
src/pages/
+layout.svelte
harbor/
+layout.svelte
home/+page.svelte → harbor.home
home/+head.svelte → harbor.home document head
user/+page.svelte → harbor.user
notFound/+page.svelte → harbor.notFoundThe folder ids are identical. React, Solid, and Preact use +layout.tsx and
+page.tsx; Vue uses +layout.vue and +page.vue; Lit and Angular use
+layout.ts and +page.ts. All six carry the head in a sibling +head.html.
Lit and Angular page modules have no component extension, so a page that needs
a different id adds a sibling +page.id.ts.
The id is the directory path with / turned into .. A sibling +page.ts
may export const id = "harbor.home" when the folder name should not be the
id. +layout.svelte files wrap descendant pages, root-first. Route params
like [slug] are not ids — the host owns URLs. A sibling +head.svelte
(Svelte) or +head.html (other adapters) compiles into the page’s manifest
head — see
Document head and SEO.
import { bootstrap } from "@kolektiv/keel-svelte"
void bootstrap()import { bootstrap } from "@kolektiv/keel-react"
void bootstrap()import { bootstrap } from "@kolektiv/keel-vue"
void bootstrap()import { bootstrap } from "@kolektiv/keel-solid"
void bootstrap()import { bootstrap } from "@kolektiv/keel-preact"
void bootstrap()import { bootstrap } from "@kolektiv/keel-lit"
void bootstrap()import { bootstrap } from "@kolektiv/keel-angular"
void bootstrap()keelPack discovers those files, emits a module per id, writes the
manifest, and can zip the .feb.
import { svelte } from "@sveltejs/vite-plugin-svelte"
import { keelPack } from "@kolektiv/keel-pack/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
svelte(),
keelPack({
id: "harbor",
version: "0.1.0",
framework: "svelte",
pagesDir: "src/pages",
bootstrap: "src/bootstrap.ts",
contract: "src/lib/page-types.json",
notFound: "harbor.notFound",
pack: "dist/harbor.feb",
}),
],
})import react from "@vitejs/plugin-react"
import { keelPack } from "@kolektiv/keel-pack/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
react(),
keelPack({
id: "harbor",
version: "0.1.0",
framework: "react",
pagesDir: "src/pages",
bootstrap: "src/bootstrap.ts",
contract: "src/lib/page-types.json",
notFound: "harbor.notFound",
pack: "dist/harbor.feb",
}),
],
})import vue from "@vitejs/plugin-vue"
import { keelPack } from "@kolektiv/keel-pack/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
vue(),
keelPack({
id: "harbor",
version: "0.1.0",
framework: "vue",
pagesDir: "src/pages",
bootstrap: "src/bootstrap.ts",
contract: "src/lib/page-types.json",
notFound: "harbor.notFound",
pack: "dist/harbor.feb",
}),
],
})import solid from "vite-plugin-solid"
import { keelPack } from "@kolektiv/keel-pack/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
solid(),
keelPack({
id: "harbor",
version: "0.1.0",
framework: "solid",
pagesDir: "src/pages",
bootstrap: "src/bootstrap.ts",
contract: "src/lib/page-types.json",
notFound: "harbor.notFound",
pack: "dist/harbor.feb",
}),
],
})import preact from "@preact/preset-vite"
import { keelPack } from "@kolektiv/keel-pack/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
preact(),
keelPack({
id: "harbor",
version: "0.1.0",
framework: "preact",
pagesDir: "src/pages",
bootstrap: "src/bootstrap.ts",
contract: "src/lib/page-types.json",
notFound: "harbor.notFound",
pack: "dist/harbor.feb",
}),
],
})import { keelPack } from "@kolektiv/keel-pack/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
keelPack({
id: "harbor",
version: "0.1.0",
framework: "lit",
pagesDir: "src/pages",
bootstrap: "src/bootstrap.ts",
contract: "src/lib/page-types.json",
notFound: "harbor.notFound",
pack: "dist/harbor.feb",
}),
],
})import analog from "@analogjs/vite-plugin-angular"
import { keelPack } from "@kolektiv/keel-pack/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
analog({ tsconfig: "tsconfig.app.json" }),
keelPack({
id: "harbor",
version: "0.1.0",
framework: "angular",
pagesDir: "src/pages",
bootstrap: "src/bootstrap.ts",
contract: "src/lib/page-types.json",
notFound: "harbor.notFound",
pack: "dist/harbor.feb",
}),
],
})The host loads that artifact as a FrontendBundle from a jar resource, a
file, or an exploded directory (dev / watch).
val harbor = FrontendBundle.fromResource("keel/harbor.feb")
val unpacked = FrontendBundle.fromDirectory(Path.of("pack/dist"))
val file = FrontendBundle.fromFile(Path.of("harbor.feb"))Asset URLs are per bundle: /__keel/pack/{bundleId}/…. Seed entry / css
are rewritten to that prefix so multiple bundles on one app do not clash.
Pass contract (typegen’s keel/1 JSON) so a pack that implements an
unknown id fails the build. A pack may implement a subset; the call site
passes the pack per route.
The .feb layout, PageModule, and RouterAdapter are specified under
Pack adapter.