Documentation site for BizSouq products, served at docs.bizsouq.app. Built
with Astro Starlight and deployed as a
static-assets Cloudflare Worker, built from the version-16 branch of this
repository by a Cloudflare Workers Build.
Currently covers SME Books under
/books. Further products get their own directory under src/content/docs/
and their own sidebar group — no restructuring needed.
| Command | Does |
|---|---|
npm run dev |
Dev server on localhost:4321 |
npm run check:links |
Verifies the URL contract (see below) |
npm run build |
Runs the link check, then builds to dist/ |
npm run preview |
Builds, then serves dist/ through Cloudflare's local runtime |
npm run deploy |
Builds and deploys the Worker by hand, bypassing the Git build |
npm run cf-typegen |
Regenerates worker-configuration.d.ts from wrangler.jsonc |
npm run icons |
Regenerates the PWA icons from the BizSouq mark (see below) |
Search is powered by Pagefind, which only indexes on a real build — it does
nothing in npm run dev. Use npm run build && npm run preview to test it.
This is the thing to be careful about. The SME Books app already ships help links to this site. It builds them by string concatenation:
// sme-books src/components/Sidebar.vue
ipc.openLink('https://docs.bizsouq.app/' + docsPathRef.value);So a page that is renamed or removed here becomes a 404 for a user who clicked Help in the app, and nothing in either repo would otherwise notice.
books-routes.json records every path the app links to — 28 pages and 4
anchors — and npm run check:links asserts each one resolves. It runs as part
of npm run build, so a broken contract fails the build rather than the site.
Two rules follow from this:
- Do not rename or delete a page under
src/content/docs/books/without changingdocsPathMapin the app first, shipping that, and then updatingbooks-routes.jsonhere. - When
docsPathMapchanges in the app, regeneratebooks-routes.jsonand add the new pages here.
Anchors are only enforced for English, because the app's links carry no locale segment. Bengali pages use Bengali headings, which produce different ids.
astro.config.mjs sets trailingSlash: 'never' and build.format: 'file', so
/books/dashboard is served directly rather than redirecting to
/books/dashboard/. This matches the unsuffixed URLs the app links to and
avoids a redirect on every Help click. Changing either setting reintroduces the
redirect.
wrangler.jsonc sets assets.html_handling to drop-trailing-slash to serve
that shape, so the two files have to be changed together.
The site is a PWA: installable as its own window, and pages already read stay
readable with no connection. Four static files under public/ do it —
manifest.webmanifest, sw.js, offline.html and the icons — wired in through
the head array in astro.config.mjs.
Hand-written rather than generated. @vite-pwa/astro, the usual route, peers on
astro: ^1 || … || ^5, and this site runs Astro 7 — installing it means forcing
peer deps in a repo where npm run build is the deploy pipeline.
The worker caches by request URL, never by file path. This follows from the
URL contract above: /books/dashboard is the file dist/books/dashboard.html,
so a build-time precache manifest listing dist paths would never match a
single navigation. Caching what was actually requested avoids the mismatch, and
means adding a page needs no change to sw.js.
| Request | Strategy | Why |
|---|---|---|
| Navigations | Network-first, then cache, then offline.html |
A deploy is live on the next online load; no stale docs |
/_astro/* |
Cache-first | Content-hashed, so the URL identifies the bytes |
/pagefind/* |
Cache-first | Search keeps working offline once used |
| Icons, manifest | Cache-first | Change about as often as the logo |
| Everything else | Untouched | — |
Both caches are capped by entry count. Hashed _astro names are never
invalidated — each deploy just introduces new ones — so without a cap they
accumulate indefinitely. VERSION in sw.js is for changes to the strategies
themselves, not for deploys.
offline.html is deliberately a raw public/ file rather than a Starlight
route: routes land in sitemap-0.xml, and Pagefind only indexes pages carrying
data-pagefind-body, which Starlight adds and this page does not.
One trap if you touch how it is cached: drop-trailing-slash redirects
/offline.html to /offline, so fetching it gives a response flagged
redirected. Handing that to respondWith for a navigation fails the load
outright — navigations use redirect: 'manual'. sw.js re-wraps the body to
clear the flag, and the symptom if that is removed is ERR_FAILED with no
console message rather than anything that looks like a caching bug.
The worker registers in npm run dev too, where it is effectively inert — pages
are network-first, and the _astro and pagefind URLs only exist in a real
build. npm run preview is what actually exercises it. To drop it while
debugging:
navigator.serviceWorker.getRegistrations().then((r) => r.forEach((x) => x.unregister()));npm run icons regenerates icon-192, icon-512, icon-maskable-512 and
apple-touch-icon from scripts/brand/bizsouq-mark.png — the same orange B the
favicons were cut from. It is only 236×285, so the 512 sizes are about a 1.4×
upscale; that holds because the mark is a flat two-colour letterform. Replace
that file with larger artwork and re-run to improve them. The favicons predate
the script and are left alone.
The maskable icon sits at 55% of the canvas on white. Launchers crop maskable icons to their own shape and only guarantee the middle 80%, and iOS drops the alpha channel — hence the padding and the opaque background.
English is the root locale and carries no path prefix — required, because the
app's URLs have no locale segment. Bengali lives under /bn/.
| Language | Content | UI strings |
|---|---|---|
| English | src/content/docs/ |
Starlight built-in |
| Bengali | src/content/docs/bn/ |
src/content/i18n/bn.json |
Starlight ships no Bengali UI strings, hence bn.json. Without it the chrome
("On this page", "Search", theme labels) falls back to English on Bengali pages.
Images live in src/assets/books/ for English and src/assets/books/bn/ for
Bengali, and pages reference them with a relative markdown image so Astro
optimises them. They are stored as WebP; public/ is deliberately not used,
because assets there are copied verbatim and never processed.
Every screenshot is of Our Daily Fashion, the demo company the app's own repository builds:
cd ../books
yarn seed:odf # rebuilds odf.localhost from scratch
yarn build && yarn start # then sign in at http://odf.localhost:8000That company is a Dhaka children's-wear shop with two years of trading in it — so the figures in the images hang together, and re-shooting a page after a UI change means opening the same route again rather than inventing data. The Bengali set is the same routes with the app's language set to Bengali.
Content describes this application. It is not upstream Frappe Books and their documentation does not apply — SME Books runs in the browser rather than as a desktop app, selects its company by hostname, signs in with email and password, stores data in MariaDB, and ships a Bangladesh chart of accounts with BDT and a July–June fiscal year. Check behaviour against the running app before documenting it.