@voidbase-cloud/voidbase 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/README.md +16 -7
- package/bin/voidbase.ts +67 -6
- package/docs/adapter.md +280 -0
- package/docs/ci.md +195 -0
- package/docs/deploy.md +83 -5
- package/docs/releasing.md +39 -31
- package/hooks-plugin.ts +15 -4
- package/package.json +10 -4
- package/routes/api/[...path].ts +0 -5
- package/scripts/cf-builds.ts +228 -0
- package/scripts/ci-browser.sh +60 -0
- package/scripts/ci-cache.sh +40 -0
- package/scripts/ci-lib.sh +40 -0
- package/scripts/ci-oracles.sh +19 -0
- package/scripts/ci-plan.ts +270 -0
- package/scripts/ci-status.ts +126 -0
- package/scripts/ci-suites.sh +11 -1
- package/scripts/ci.sh +188 -0
- package/scripts/gh-release.ts +48 -0
- package/scripts/release.sh +104 -0
- package/scripts/seed-reference.sh +7 -2
- package/scripts/sync-app.ts +1 -0
- package/src/adapter/bundle.ts +130 -0
- package/src/adapter/codegen.ts +269 -0
- package/src/adapter/index.ts +6 -0
- package/src/adapter/plugin.ts +132 -0
- package/src/adapter/runtime.ts +325 -0
- package/src/adapter/scan.ts +277 -0
- package/src/cloud/rest.ts +10 -2
- package/src/env/define.ts +195 -0
- package/src/node/assets.ts +7 -1
- package/src/node/cloud-init.ts +14 -0
- package/src/node/deploy-cf.ts +124 -16
- package/src/node/secrets.ts +237 -0
- package/src/node/serve.ts +16 -2
- package/src/server/api.ts +7 -2
- package/src/server/app.ts +6 -1
- package/src/server/hooks/index.ts +27 -1
- package/src/server/hooks/migrations.ts +4 -1
- package/src/server/hooks/runtime.ts +10 -2
- package/src/server/jobs.ts +3 -1
- package/src/server/webauthn.ts +23 -6
- package/tsconfig.json +5 -0
- package/tsconfig.node.json +3 -1
package/docs/ci.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# CI and release: one flow, Cloudflare runs it
|
|
2
|
+
|
|
3
|
+
`scripts/ci.sh` is the whole CI, and its last step runs `scripts/release.sh`, the whole release flow, when the
|
|
4
|
+
commits call for it. Both run the same way on a dev machine and on Cloudflare Workers Builds; GitHub Actions only
|
|
5
|
+
starts the builds (`.github/workflows/cloudflare.yml`).
|
|
6
|
+
Every step is recorded (`scripts/ci-lib.sh`) and `scripts/ci-status.ts` renders the record into `ci/public`:
|
|
7
|
+
`index.html`, `status.json`, `badge.svg`, the suite logs and the screenshots. That directory is the status Worker a
|
|
8
|
+
build deploys.
|
|
9
|
+
|
|
10
|
+
## The CI steps (`scripts/ci.sh`)
|
|
11
|
+
|
|
12
|
+
| step | what |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| install | `bun install --frozen-lockfile` |
|
|
15
|
+
| commitlint | the commits the push or pull request introduces (`--last` when there is nothing to compare with) |
|
|
16
|
+
| oracles | the starter (`scripts/ci-oracles.sh`: `STARTER_DIR`, else the sibling checkout, else a shallow clone in the cache), the panel (`panel:sync`), the starter's frontend build next to it (`app:sync`), `void prepare` |
|
|
17
|
+
| plan | `scripts/ci-plan.ts`: which of the following steps and suites this run needs (below); hot mode trims the list to a time budget |
|
|
18
|
+
| typecheck, unit | `tsc --noEmit`, `bun test` |
|
|
19
|
+
| browser | a Chrome for the panel and starter suites (`scripts/ci-browser.sh`, below); `CI_BROWSER=0` skips them |
|
|
20
|
+
| boot | the run's `.env`, `void db migrate`, the dev server on 5180 (`CI_PORT`), the app user |
|
|
21
|
+
| reference | PocketBase 0.39.11 on 8090 (`CI_PB_PORT`) freshly seeded from the starter (again before the Bun pass: the reference keeps state the suites cannot undo, such as a stored S3 secret), the SMTP sink, the OIDC, S3 and Cloudflare API mocks, awaited before a first mail warms the SMTP transport; whatever already listens on a port is reused |
|
|
22
|
+
| suites | `scripts/ci-suites.sh`: every differential suite, the SDK suite, the panel suites |
|
|
23
|
+
| suites-bun | the same suites against `voidbase serve` on 8093 (Bun, SQLite, local files), without the browser suites |
|
|
24
|
+
| deploy-cf, adapter, fresh-db, mail-http, exe-smoke | the deploy dry run against the API mock, a Void app converted and run through the adapter, the fresh-database boot and the HTTP mail transport of the production build, the prebuilt executable and its update flow |
|
|
25
|
+
| starter | the unmodified starter frontend against voidbase |
|
|
26
|
+
| release | on master, with `GH_TOKEN`: the release flow (docs/releasing.md) when a releasable commit was pushed, the release PR was merged, a `Release: dry-run` trailer asks for a rehearsal, or a release still needs npm or its executables |
|
|
27
|
+
|
|
28
|
+
The script stops at the first failed step, prints the relevant logs, renders the status page and stops the servers
|
|
29
|
+
it started; a dev machine's `.env` is put back. Steps the plan does not select are recorded as skipped with the reason. Ports, oracles and Chrome come from the environment, so a dev machine
|
|
30
|
+
runs the same flow with `bun run ci`.
|
|
31
|
+
|
|
32
|
+
## Incremental runs (`scripts/ci-plan.ts`)
|
|
33
|
+
|
|
34
|
+
A full run takes about nine minutes on the build image, most of it the two suite passes, so a run only repeats the
|
|
35
|
+
checks its changes reach. Every check has a set of files: the import closure of its test entry point plus the runtime
|
|
36
|
+
it exercises, resolved through the same import graph (`#platform/*` follows the `workerd` condition for the Workers
|
|
37
|
+
server and the default one for the Bun runtime), so `src/node/deploy-cf.ts` reaches the deploy dry run, the
|
|
38
|
+
executable smoke, typecheck and the unit tests and nothing else, while a file of the server reaches every suite on
|
|
39
|
+
both runtimes. File hashes come from git blob ids, so they are exact and cost nothing; the combined hash of a check's
|
|
40
|
+
files is compared with the one the last green run recorded, and the check runs only when they differ. The record is
|
|
41
|
+
the deployed status page of master (`CI_STATUS_URL`, https://release.voidbase.cloud/status.json, whose `verified`
|
|
42
|
+
map holds the hash each check last passed on); on a dev machine it is `ci/public/status.json` from the previous run. What passed
|
|
43
|
+
gets this run's hashes, what was skipped keeps the previous record's, so a chain of partial runs stays sound. The
|
|
44
|
+
oracle sync, the servers, the reference and Chrome happen only when a selected check needs them.
|
|
45
|
+
|
|
46
|
+
| change | what runs |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| docs, README, surface, `ci/`, `.github/` | commit messages and the plan: about half a minute |
|
|
49
|
+
| one suite's file | that suite on both runtimes, with the servers it needs |
|
|
50
|
+
| `src/node/deploy-cf.ts`, `src/cloud` | typecheck, unit, the deploy dry run, the executable smoke, cloud-rest: under two minutes |
|
|
51
|
+
| `src/node/serve.ts`, `bin/voidbase.ts` | the Bun pass and the executable smoke |
|
|
52
|
+
| `src/server`, `routes`, the app config, the harness | everything |
|
|
53
|
+
|
|
54
|
+
`bun scripts/ci-plan.ts affected <file>` prints the checks a file reaches; `explain` prints every check with its
|
|
55
|
+
file count and hash. Uncommitted changes never match a record, so a dirty working tree reruns what it touches.
|
|
56
|
+
`CI_PLAN=full` (or `--full`) runs everything regardless, and so does a commit whose message carries `Tests: all`; the
|
|
57
|
+
same happens when the record cannot be fetched. Only the repository's own files are hashed: a new commit of the
|
|
58
|
+
starter oracle is picked up by the next full run.
|
|
59
|
+
|
|
60
|
+
## Hot mode
|
|
61
|
+
|
|
62
|
+
The suites test the server as a black box, so a server change reaches all of them and a full run is the honest
|
|
63
|
+
answer. During a development phase that is too slow, so hot mode keeps every run within a time budget:
|
|
64
|
+
`bun scripts/cf-builds.ts hot on` (`--budget 60` to change the default of sixty seconds; `hot off` to return to
|
|
65
|
+
full runs). With hot mode on, a run does typecheck and the unit tests always, then whatever the commits name, then
|
|
66
|
+
the suites of the commits' scopes, then the cheapest of the remaining selected checks until the budget is spent,
|
|
67
|
+
using the durations the last run recorded (`status.json`, `suites[].seconds`). The Bun pass, the browser suites and
|
|
68
|
+
the starter smoke wait for a normal run. Deferred checks are listed on the status page and are never marked verified,
|
|
69
|
+
so the first run after `hot off` does them all. Releasing in hot mode publishes to npm only, so voidbase-site can
|
|
70
|
+
pick the version up at once; the executables of that release are built by the first normal run on master.
|
|
71
|
+
|
|
72
|
+
The commits steer it: a Conventional Commit scope (`fix(records): ...`) puts that area's suites first
|
|
73
|
+
(`SCOPE_KEYS` in the planner maps every scope of `commitlint.config.js` to suites), a `Tests:` trailer names checks
|
|
74
|
+
that are never deferred (`Tests: thumbs s3`, `Tests: bun` for the Bun pass, `Tests: browser`), and `Tests: all`
|
|
75
|
+
forces a full run. A commit that edits a suite's file always runs that suite. The messages of every commit since the
|
|
76
|
+
last green run count, not only the last one: a Cloudflare build checks out a single commit, so the planner deepens
|
|
77
|
+
the history until the last green run's commit is reachable before it reads them.
|
|
78
|
+
|
|
79
|
+
## What is kept between runs
|
|
80
|
+
|
|
81
|
+
`CI_CACHE_DIR` holds the downloads: Playwright's headless shell and the unpacked libraries, the Ubuntu packages,
|
|
82
|
+
the starter clone with its `node_modules` and its frontend build (reused while the starter's commit is the same), the
|
|
83
|
+
panel tarball (through `XDG_CACHE_HOME`) and the PocketBase archive of the reference. On a dev machine it is
|
|
84
|
+
`~/.cache/voidbase-ci` and simply stays there. Workers Builds keeps nothing between builds but the package manager's
|
|
85
|
+
cache, and that one only until the lockfile changes, so on Cloudflare `scripts/ci-cache.sh` restores the directory from
|
|
86
|
+
an R2 bucket at the start of a build and saves it at the end, one archive per component, uploaded only when its content
|
|
87
|
+
changed. `setup` creates the bucket (`voidbase-ci-cache`) when `CI_CACHE_TOKEN` is in the environment (an API token with
|
|
88
|
+
Workers R2 Storage edit; the deploy token of the site is accepted) and stores it on every trigger as a build secret,
|
|
89
|
+
with `CI_CACHE_ACCOUNT` and `CI_CACHE_BUCKET`. Without those the builds fetch everything each time, about forty
|
|
90
|
+
seconds of a full run.
|
|
91
|
+
|
|
92
|
+
## Chrome (`scripts/ci-browser.sh`)
|
|
93
|
+
|
|
94
|
+
The browser suites launch whatever `CHROME_PATH` points at, else the Chrome on the PATH (a dev machine). Where there
|
|
95
|
+
is no Chrome, the script downloads Playwright's chromium-headless-shell under `.void/browsers` and, when the machine also
|
|
96
|
+
lacks the shared libraries Chrome needs, unpacks them from Ubuntu's packages into `.void/chrome-libs` without root (a
|
|
97
|
+
private apt root, `dpkg-deb -x`, `LD_LIBRARY_PATH`): the Workers Builds image has neither Chrome nor sudo nor those
|
|
98
|
+
libraries. The unpacking path is exercised on a dev machine with `CI_BROWSER_DOWNLOAD=1 CI_BROWSER_LIBS=always`.
|
|
99
|
+
|
|
100
|
+
## GitHub Actions only starts builds
|
|
101
|
+
|
|
102
|
+
`.github/workflows/cloudflare.yml` is the only workflow. It never runs the flows: it asks Cloudflare Workers Builds
|
|
103
|
+
for a build of the commit at hand through the Builds API (a few seconds of Actions time) and exits.
|
|
104
|
+
|
|
105
|
+
| event | build started |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| push to master | the master trigger; the release step of the build then refreshes the release PR for releasable commits or publishes a merged release PR |
|
|
108
|
+
| pull request from a branch of this repository | the branches trigger: a preview URL of the results, no release work, no secrets |
|
|
109
|
+
| release published | the master trigger for the tagged commit, whose release step publishes it |
|
|
110
|
+
| Actions > cloudflare > Run workflow | the ref's trigger |
|
|
111
|
+
|
|
112
|
+
The job is skipped until the repository variables exist (`CF_ACCOUNT_ID`, `CF_CI_TRIGGER_MASTER`,
|
|
113
|
+
`CF_CI_TRIGGER_BRANCHES`) together with the secret `CLOUDFLARE_BUILDS_TOKEN`; `bun scripts/cf-builds.ts setup
|
|
114
|
+
--github` stores all four. With the repository variable
|
|
115
|
+
`CF_BUILDS_WAIT=1` the job also waits for the builds it started and fails when one fails, so the pull request check
|
|
116
|
+
reflects the result; without it the check only means "started", and the result lives in the dashboard, in
|
|
117
|
+
`cf-builds.ts logs`, and on the status page. Pushes never build on their own: every trigger's watch paths exclude every
|
|
118
|
+
path, and the API is not subject to them. Standard GitHub-hosted runners are free for public repositories anyway; the
|
|
119
|
+
trigger job spends seconds.
|
|
120
|
+
|
|
121
|
+
Two things stop existing with this layout because they need the OIDC token only a GitHub Actions run can mint:
|
|
122
|
+
`npm publish --provenance` and the build attestations of the release archives. Releases carry checksums only.
|
|
123
|
+
|
|
124
|
+
## Cloudflare Workers Builds
|
|
125
|
+
|
|
126
|
+
[Workers Builds](https://developers.cloudflare.com/workers/ci-cd/builds/) is Cloudflare's build system for Workers: the
|
|
127
|
+
"Cloudflare Workers and Pages" GitHub App starts a build on every push to a connected repository, runs a build command
|
|
128
|
+
and a deploy command in Cloudflare's build image, and posts a check run (and a preview URL on pull requests) back to
|
|
129
|
+
GitHub. One project, `voidbase-ci`, runs voidbase's flows there: a Worker whose deploy publishes the status page of
|
|
130
|
+
the build (`ci/wrangler.jsonc`, assets only). A project has at most two triggers with one command each, which is
|
|
131
|
+
why the release flow runs inside the CI build rather than as a project of its own:
|
|
132
|
+
|
|
133
|
+
| trigger | build command | deploy command |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| master | `bash scripts/ci.sh` | `wrangler deploy -c ci/wrangler.jsonc` |
|
|
136
|
+
| branches | `bash scripts/ci.sh` | `wrangler versions upload -c ci/wrangler.jsonc`: a preview URL of the results on the pull request |
|
|
137
|
+
|
|
138
|
+
A failed build command means no deploy, so the status Worker shows the last build that ran to the end; the log of a
|
|
139
|
+
failed build is in the dashboard and in `cf-builds.ts logs`. The release secrets (`GH_TOKEN`, `NPM_TOKEN`, optionally
|
|
140
|
+
`GH_PACKAGES_TOKEN`) are build secrets of the master trigger only, so builds of other branches never carry them.
|
|
141
|
+
|
|
142
|
+
### Limits and cost
|
|
143
|
+
|
|
144
|
+
| | Free plan | Paid plan |
|
|
145
|
+
| --- | --- | --- |
|
|
146
|
+
| build minutes | 3,000 a month | 6,000 a month, then $0.005 a minute |
|
|
147
|
+
| concurrent builds | 1 | 6 |
|
|
148
|
+
| build timeout | 20 minutes | 20 minutes |
|
|
149
|
+
| CPU, memory | 2 vCPU, 8 GB | 4 vCPU, 8 GB |
|
|
150
|
+
|
|
151
|
+
The image is Ubuntu 24.04 x86_64 with Node 22, Bun 1.2.15 (the projects set `BUN_VERSION=1.3.14`, the version the
|
|
152
|
+
workflows pin), git, curl, unzip and build-essential; no Chrome, no lsof, no jq, no gh. The scripts need none of them:
|
|
153
|
+
`scripts/gh-release.ts` talks to GitHub's API directly. The 20-minute timeout is the constraint to watch: the CI run
|
|
154
|
+
takes 7.5 minutes on GitHub's 4 vCPU and gets 2 on the Free plan, and the two projects build one after the other there.
|
|
155
|
+
|
|
156
|
+
### Setup, once
|
|
157
|
+
|
|
158
|
+
1. Install the [Cloudflare Workers and Pages GitHub App](https://github.com/apps/cloudflare-workers-and-pages) for
|
|
159
|
+
`voidbase-cloud/voidbase` (an organization owner does this on GitHub; limit it to that repository).
|
|
160
|
+
2. Create a user API token at dash.cloudflare.com/profile/api-tokens with **Workers Builds Configuration: Edit** and
|
|
161
|
+
**Workers Scripts: Edit**, and export it as `CLOUDFLARE_BUILDS_TOKEN`. The Builds API takes user tokens only; the
|
|
162
|
+
account-owned token `voidbase deploy` uses is rejected.
|
|
163
|
+
3. `GH_TOKEN=... NPM_TOKEN=... bun scripts/cf-builds.ts setup --github` connects the repository, creates the Worker
|
|
164
|
+
and its two triggers with push builds off, sets `BUN_VERSION`, stores the release secrets it finds in the
|
|
165
|
+
environment on the master trigger, and writes the workflow's variables and secret into the GitHub repository
|
|
166
|
+
(`gh variable set`, `gh secret set`). The release secrets: `GH_TOKEN` (a fine-grained PAT with contents and pull requests write on the repository, for release-please
|
|
167
|
+
and the release assets), `NPM_TOKEN` (the npm granular token), optionally `GH_PACKAGES_TOKEN` (a classic PAT with
|
|
168
|
+
`write:packages`; fine-grained tokens cannot publish packages, and without it the GitHub Packages copy is skipped).
|
|
169
|
+
The first run stops when the account has no build token yet: open the `voidbase-ci` Worker in the dashboard,
|
|
170
|
+
Settings > Builds > API token > Create new token, and run setup again.
|
|
171
|
+
4. `bun scripts/cf-builds.ts build --branch master --follow` runs the first build and streams its log; `status`,
|
|
172
|
+
`builds`, `logs <uuid>`, `cancel <uuid>` and `env` cover the rest (the header of the script lists them).
|
|
173
|
+
5. From then on every push and pull request goes through the workflow, one build each. `gh variable set
|
|
174
|
+
CF_BUILDS_WAIT --body 1` makes the workflow wait for the build it started.
|
|
175
|
+
|
|
176
|
+
`test/cf-builds.ts` runs the CLI against `test/cf-mock.ts`, whose Builds endpoints follow the request and response
|
|
177
|
+
shapes of Cloudflare's API reference; the live API is exercised the first time the App and the user token exist.
|
|
178
|
+
|
|
179
|
+
### What to expect
|
|
180
|
+
|
|
181
|
+
- No provenance and no attestations (OIDC): `npm publish` runs without `--provenance`, the release archives carry
|
|
182
|
+
checksums only.
|
|
183
|
+
- GitHub Packages only with `GH_PACKAGES_TOKEN`.
|
|
184
|
+
- Logs live in the dashboard and in `cf-builds.ts logs`; the deployed page is the last build that ran to the end.
|
|
185
|
+
- release-please and the release assets use `GH_TOKEN`, a PAT, so a release it creates fires the `release` event and
|
|
186
|
+
the workflow starts one more release build for the tagged commit, which finds everything published and stops.
|
|
187
|
+
|
|
188
|
+
## The status page
|
|
189
|
+
|
|
190
|
+
`ci/public/index.html` lists the steps with their durations and logs, every suite with its result and last line, the
|
|
191
|
+
screenshots of the panel and starter suites, and links `status.json` (the same, as data) and `badge.svg`
|
|
192
|
+
(`ci: passing`). The page's canonical address is https://release.voidbase.cloud (the custom domain
|
|
193
|
+
`ci/wrangler.jsonc` declares; the workers.dev address stays on for the preview URLs): `badge.svg` there is the badge
|
|
194
|
+
in the README and `status.json` the record of the last green run and the feed for anything else; a pull request's
|
|
195
|
+
build uploads a version, so its preview URL shows the same page for that commit.
|
package/docs/deploy.md
CHANGED
|
@@ -43,7 +43,26 @@ deploy just skips the jobs queue and says so.
|
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
export VOIDBASE_DEPLOY_CF_API_KEY=... # or put it in .env next to pb_hooks, or a CI secret
|
|
46
|
-
voidbase deploy
|
|
46
|
+
voidbase deploy # --name <worker>, --account <id> when the token reaches several accounts
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A static site is served at `/` by the same Worker when there is one: `./pb_public` (PocketBase's convention, picked up
|
|
50
|
+
automatically when the directory exists, exactly like `voidbase serve`), `--public-dir <dir>` or
|
|
51
|
+
`VOIDBASE_DEPLOY_PUBLIC_DIR`. Build it first: the directory needs an `index.html`. Unknown paths get its `404.html`
|
|
52
|
+
(a copy of `index.html` unless the build made one) with status 404, `/api` and `/_/` are untouched. A `_redirects`
|
|
53
|
+
file in that directory (Netlify/Pages syntax, `source destination [status]`) is split in two: path-only lines are
|
|
54
|
+
uploaded with the assets as Cloudflare's own `_redirects` (evaluated before the Worker), and lines whose source names a
|
|
55
|
+
host (`https://api.example.com/ /_/ 302`) become zone Redirect Rules after the upload, tagged with the Worker's name so
|
|
56
|
+
a redeploy replaces exactly its own rules. That is how one Worker behind several custom domains answers differently per
|
|
57
|
+
hostname: the site on the apex, the API hostname's root sent to the admin panel, `www` sent to the apex. Writing them needs
|
|
58
|
+
the zone permission Single Redirect > Edit on the deploy token (the API's permission listing calls it Dynamic URL
|
|
59
|
+
Redirects Write; it sits with the zone permissions, like DNS Edit, scoped to the zone). Without it the deploy prints the
|
|
60
|
+
rules to create by hand and carries on.
|
|
61
|
+
(Void's own `routing.redirects` are not used: they are applied by the Void platform's dispatch worker, which a
|
|
62
|
+
self-hosted deploy does not have.)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
voidbase deploy --public-dir ../sk/build # a build that lives elsewhere
|
|
47
66
|
```
|
|
48
67
|
|
|
49
68
|
What it does, in order: resolves the account through the token, creates `<name>-db` (D1), `<name>-storage`
|
|
@@ -51,7 +70,8 @@ What it does, in order: resolves the account through the token, creates `<name>-
|
|
|
51
70
|
(`node_modules/voidbase/.cloud/<name>`, nothing appears in your tree) with a `wrangler.jsonc` carrying the real
|
|
52
71
|
ids and, when the directory has a `main.ts` exporting `register(app)`, composes it into the Worker; stores the
|
|
53
72
|
superuser as worker secrets (from `VOIDBASE_SUPERUSER_*` / `PB_SUPERUSER_*`, or a generated
|
|
54
|
-
password saved in `pb_data/.superuser-credentials`; the local dev default `changeme123` never goes live)
|
|
73
|
+
password saved in `pb_data/.superuser-credentials`; the local dev default `changeme123` never goes live) together
|
|
74
|
+
with the secrets and vars `pb_secrets/` declares (below), syncs
|
|
55
75
|
the admin panel and your frontend build into that project, and runs `void deploy --backend cloudflare`,
|
|
56
76
|
which builds, applies the D1 migrations and uploads the Worker with its cron trigger. It ends with the
|
|
57
77
|
`https://<name>.<your-subdomain>.workers.dev` URL and a health check. Re-running is idempotent: existing resources
|
|
@@ -66,8 +86,9 @@ https://dash.cloudflare.com/?to=/:account/api-tokens).
|
|
|
66
86
|
|
|
67
87
|
### A custom domain
|
|
68
88
|
|
|
69
|
-
`voidbase deploy --domain api.example.com` (or `VOIDBASE_DEPLOY_DOMAIN
|
|
70
|
-
the
|
|
89
|
+
`voidbase deploy --domain api.example.com` (or `VOIDBASE_DEPLOY_DOMAIN`; several hostnames comma separated, the first is
|
|
90
|
+
the URL the deploy reports) turns workers.dev off for the Worker and attaches each hostname through the Workers Custom
|
|
91
|
+
Domains API after the upload: Cloudflare creates the DNS record and the
|
|
71
92
|
certificate (a minute or two), and the token needs nothing beyond Workers Scripts edit, provided the zone is on the same
|
|
72
93
|
account. Cloudflare still requires the account to have a workers.dev subdomain before it accepts any upload (error
|
|
73
94
|
10063): open Workers & Pages once, or `PUT /accounts/<id>/workers/subdomain {"subdomain": "<name>"}`.
|
|
@@ -83,6 +104,63 @@ account. Cloudflare still requires the account to have a workers.dev subdomain b
|
|
|
83
104
|
| `HUB` (Durable Object `VoidbaseHub`, SQLite-backed, in this Worker) | the realtime hub: every SSE connection holds one hibernatable socket to it, writes publish to it, so events arrive in tens of milliseconds instead of the D1 poll's second, and idle apps cost nothing (the object sleeps). Free plan included | `--no-hub` / `VOIDBASE_DEPLOY_HUB=0` keeps the D1 poll |
|
|
84
105
|
| Smart Placement | the Worker runs next to its D1 database | always on |
|
|
85
106
|
|
|
107
|
+
### Configuration and secrets: `pb_secrets/`
|
|
108
|
+
|
|
109
|
+
The app's configuration is declared once, in code, with Void's validators, and valued in each deploy's environment
|
|
110
|
+
(twelve-factor III): locally a git-ignored file, on Cloudflare the Worker's own secrets and vars.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
// pb_secrets/main.ts committed
|
|
114
|
+
import { defineSecrets, describe, string, number, url } from "@voidbase-cloud/voidbase/secrets";
|
|
115
|
+
|
|
116
|
+
export default defineSecrets({
|
|
117
|
+
SMTP_PASSWORD: describe(string().secret(), "the mail provider's password"),
|
|
118
|
+
ADMIN_EMAILS: string().default(""),
|
|
119
|
+
MAX_UPLOAD_MB: number().default(10),
|
|
120
|
+
PUBLIC_SITE_URL: url().optional().public(),
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
pb_secrets/secrets.json { "SMTP_PASSWORD": "...", "ADMIN_EMAILS": "me@example.com" } git-ignored
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Every key has an access tier, which decides where its value lives and who can read it:
|
|
129
|
+
|
|
130
|
+
| tier | declared as | lives in | readable by |
|
|
131
|
+
| --- | --- | --- | --- |
|
|
132
|
+
| secret | `string().secret()` (or `secret(schema)`) | the Worker's encrypted secrets | hooks and routes; never listed, never in a build |
|
|
133
|
+
| server | a bare validator | the Worker's plain vars | hooks and routes; never in a client build |
|
|
134
|
+
| public | `.public()` (or `pub(schema)`) | the Worker's vars and the client build (`import.meta.env.KEY`) | everyone, the browser included |
|
|
135
|
+
|
|
136
|
+
The validators are the ones a Void project's `env.ts` uses (`string()`, `number()`, `boolean()`, `url()`,
|
|
137
|
+
`email()`, `oneOf()`, `json()`, each with `.optional()` and `.default()`), and any Standard Schema validator works
|
|
138
|
+
inside `secret()` / `server()` / `pub()`. A value is parsed through its validator wherever it is read, so a default is
|
|
139
|
+
filled in, a number is a number, and a bad or missing value stops the process with the key's name, never its value.
|
|
140
|
+
In hooks, `$os.getenv("NAME")` (the stored string); in TypeScript, `await definition.read((n) => $os.getenv(n))`
|
|
141
|
+
gives the typed values.
|
|
142
|
+
|
|
143
|
+
`voidbase init` writes an empty declaration and the `.gitignore` lines. `voidbase serve` parses `secrets.json` and
|
|
144
|
+
the shell and puts the result, defaults included, into the environment (the shell outranks the file, the file
|
|
145
|
+
outranks `.env`). `voidbase deploy` stores every server and public value as the Worker's vars on every deploy (a var
|
|
146
|
+
is the code's to set), stores the secrets the Worker does not have yet as its secrets, and refuses to deploy while a
|
|
147
|
+
value is invalid or a required one is missing everywhere. A secret the Worker already holds is left alone by a
|
|
148
|
+
deploy: a deploy ships code, and a checkout whose `secrets.json` carries dev values (another OAuth client, the
|
|
149
|
+
placeholder password) must not overwrite production by deploying. Replacing is explicit:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
voidbase secrets # each key: tier, local value or default, and for secrets whether the Worker has it
|
|
153
|
+
voidbase secrets push # store the local secrets on the Worker (replacing), without redeploying
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
That is what makes CI simple: a checkout without `secrets.json` deploys with nothing but the deploy token, because
|
|
157
|
+
the secrets were pushed once from a machine that has them and the plain values come from the declared defaults or the
|
|
158
|
+
build's environment. The superuser follows the same rule: a checkout without credentials of its own keeps the
|
|
159
|
+
superuser the Worker has. A value in `secrets.json` that the declaration does not name is never deployed (the list
|
|
160
|
+
says so). `VOIDBASE_DEPLOY_VARS=A,B` and `VOIDBASE_DEPLOY_SECRETS=X,Y` still bake or store plain environment variables
|
|
161
|
+
for a deploy driven purely by the shell. Cloudflare's account-level Secrets Store is deliberately not used: one store
|
|
162
|
+
is shared by every Worker of the account, and its bindings are read asynchronously, which `$os.getenv` is not.
|
|
163
|
+
|
|
86
164
|
### Every instance is isolated
|
|
87
165
|
|
|
88
166
|
Two voidbase instances on one account never share a resource. Everything the deploy creates is named or derived from
|
|
@@ -128,7 +206,7 @@ release: `provisionInstance(cf, { account, name, release, superuser })` creates
|
|
|
128
206
|
assets through an upload session and the script with its bindings, DO migration, cron trigger and workers.dev
|
|
129
207
|
subdomain, tagged `voidbase` + `voidbase-release:<version>`; `destroyInstance` removes all of it (worker first,
|
|
130
208
|
bucket last, emptied before); `listVoidbaseWorkers` finds instances by tag. The token is the user's OAuth access token
|
|
131
|
-
(`cloudflare` OAuth2 provider, see `voidbase-site/
|
|
209
|
+
(`cloudflare` OAuth2 provider, see `voidbase-site/cloud`) or an API token with the same permissions.
|
|
132
210
|
`test/cloud-rest.ts` exercises it against `test/cf-mock.ts`. The hub and the queue are decided when the release
|
|
133
211
|
is bundled (`voidbase bundle --no-hub` / `--no-queue`), not per instance: an instance can leave them out at
|
|
134
212
|
provisioning, but cannot add what the release does not carry. Tokens a control plane keeps go to rest sealed
|
package/docs/releasing.md
CHANGED
|
@@ -36,52 +36,60 @@ ci(release): compile release notes with release-please
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
`.husky/commit-msg` runs commitlint on every commit; `.husky/pre-commit` runs `bun run check` and `bun test`.
|
|
39
|
-
`bun install` installs the hooks (`prepare`); `git commit --no-verify` skips them, and CI
|
|
40
|
-
`commitlint`) checks the pushed or proposed commits regardless.
|
|
39
|
+
`bun install` installs the hooks (`prepare`); `git commit --no-verify` skips them, and the CI build
|
|
40
|
+
(`scripts/ci.sh`, step `commitlint`) checks the pushed or proposed commits regardless.
|
|
41
41
|
|
|
42
42
|
## The release
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
with `
|
|
57
|
-
|
|
44
|
+
`scripts/release.sh` is the whole flow. It runs as the last step of the CI build on master (docs/ci.md) when the
|
|
45
|
+
pushed commits are releasable (`feat`, `fix`, `perf`, `revert`, a breaking change), when the release PR was merged,
|
|
46
|
+
when a commit carries a `Release: dry-run` trailer, or when a release still needs npm or its executables (one cut
|
|
47
|
+
by hand, or one published in hot mode). It is idempotent: a re-run after a partial failure does only what is still
|
|
48
|
+
missing. In hot mode it publishes to npm and leaves the executables to the first normal run.
|
|
49
|
+
|
|
50
|
+
1. Push or merge conventional commits to `master`. `release-pr` (release-please) opens or updates the pull request
|
|
51
|
+
"chore(master): release X.Y.Z": the next version from the commit types, the `CHANGELOG.md` section compiled from
|
|
52
|
+
the commits, the `package.json` bump. Keep merging work; the PR follows.
|
|
53
|
+
2. Merge the PR. `github-release` tags `vX.Y.Z` and creates the GitHub release with that section as notes.
|
|
54
|
+
3. `publish`, when release `v<package.json version>` exists and npm lacks the version: install, typecheck, the unit
|
|
55
|
+
and cloud-rest tests, `npm pack`, a smoke install of the tarball that runs the CLI from it, `npm publish`, the
|
|
56
|
+
GitHub Packages copy (with `GH_PACKAGES_TOKEN`), and the tarball attached to the release (`scripts/gh-release.ts`).
|
|
57
|
+
4. `executables`, when that release lacks `checksums.txt`: the prebuilt executables for every platform
|
|
58
|
+
(`scripts/build-exe.ts`: Bun cross-compiles from one machine; the panel, the system migrations and the hooks
|
|
59
|
+
typings are embedded), the smoke of the machine's own build (`test/exe-smoke.ts`: serve with pb_hooks, the panel
|
|
60
|
+
from the embedded zip, a thumbnail through the wasm, then `voidbase update` against a mock GitHub API),
|
|
61
|
+
`voidbase_<version>_<os>_<arch>.zip` for linux/darwin/windows × amd64/arm64 (plus musl builds) and `checksums.txt`
|
|
62
|
+
attached to the release, and the release notes in PocketBase's shape: the `./voidbase update` hint first, then the
|
|
63
|
+
compiled notes.
|
|
58
64
|
|
|
59
65
|
The layout mirrors PocketBase's releases: the zip holds the executable, `CHANGELOG.md` and `LICENSE`;
|
|
60
66
|
`checksums.txt` is goreleaser's format (`<sha256> <file>`), which `voidbase update` checks before replacing the
|
|
61
|
-
executable.
|
|
62
|
-
|
|
63
|
-
once in the repository settings (Settings > General >
|
|
64
|
-
turn on.
|
|
67
|
+
executable. Builds on Cloudflare cannot attest the archives or publish with npm provenance (both need the OIDC token
|
|
68
|
+
of a GitHub Actions run), so the checksums are the integrity check. For the "Immutable" badge and the release
|
|
69
|
+
attestation GitHub adds itself, enable immutable releases once in the repository settings (Settings > General >
|
|
70
|
+
Releases); it is a setting, not something a workflow can turn on.
|
|
65
71
|
|
|
66
72
|
`.release-please-manifest.json` holds the released version (0.1.0 was cut by hand and its notes written by hand;
|
|
67
73
|
everything after it is compiled). `release-please-config.json` maps commit types to changelog sections.
|
|
68
74
|
|
|
69
75
|
## Rehearsals and manual paths
|
|
70
76
|
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
- A dry run: a commit on master whose message carries a `Release: dry-run` trailer makes the build rehearse the
|
|
78
|
+
flow (release-please in dry-run mode, `npm publish --dry-run`, the executables, nothing published, no release
|
|
79
|
+
touched), or locally `bun run release -- --dry-run` (needs `GH_TOKEN` with read access and `NPM_TOKEN`).
|
|
80
|
+
- A release cut by hand also publishes: `gh release create vX.Y.Z --notes-file notes.md` after bumping `package.json`
|
|
81
|
+
to X.Y.Z on `master`; the `release` event starts a build of the tagged commit, whose release step publishes it.
|
|
75
82
|
- Publishing from a machine: `bun run check && bun test`, then
|
|
76
83
|
`NPM_CONFIG_//registry.npmjs.org/:_authToken=$VOIDBASE_NPM_TOKEN npm publish --access public`.
|
|
77
84
|
|
|
78
|
-
Secrets and permissions
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
Secrets and permissions. The master trigger on Cloudflare holds `GH_TOKEN` (a fine-grained PAT with contents, pull
|
|
86
|
+
requests and issues write on this repository: release-please opens and labels the release PR and creates the release
|
|
87
|
+
with it, the assets are uploaded with it), `NPM_TOKEN` (an npm granular token with publish rights on the
|
|
88
|
+
`@voidbase-cloud` scope) and optionally `GH_PACKAGES_TOKEN` (a classic PAT with `write:packages`; without it the
|
|
89
|
+
GitHub Packages copy is skipped). `bun scripts/cf-builds.ts setup` stores them from the environment; builds of other
|
|
90
|
+
branches never carry them. GitHub itself holds only what the workflow needs to start builds: the secret
|
|
91
|
+
`CLOUDFLARE_BUILDS_TOKEN` and the trigger variables. release-please's PR needs no organization setting for Actions,
|
|
92
|
+
since a PAT opens it.
|
|
85
93
|
|
|
86
94
|
Consumers: `bun add @voidbase-cloud/voidbase`; from GitHub Packages instead, `.npmrc` with
|
|
87
95
|
`@voidbase-cloud:registry=https://npm.pkg.github.com` and a token with `read:packages`.
|
package/hooks-plugin.ts
CHANGED
|
@@ -21,24 +21,25 @@ const ASYNC_PROPS = new Set([
|
|
|
21
21
|
"findAuthRecordByEmail", "findAuthRecordByToken", "expandRecord", "expandRecords",
|
|
22
22
|
"fileFromURL", "fileFromBytes", "fileFromPath", "bindBody", "requestInfo",
|
|
23
23
|
"importCollections",
|
|
24
|
+
"queueJob", // $jobs.queueJob hands work to the jobs queue
|
|
24
25
|
]);
|
|
25
26
|
|
|
26
27
|
export const HOOK_GLOBALS = [
|
|
27
|
-
"$app", "$apis", "$http", "$os", "$filesystem", "$security", "$mails", "$template", "$dbx",
|
|
28
|
+
"$app", "$apis", "$http", "$os", "$filesystem", "$security", "$mails", "$template", "$dbx", "$env", "$jobs",
|
|
28
29
|
"routerAdd", "routerUse", "cronAdd", "cronRemove", "migrate",
|
|
29
30
|
"Record", "Collection", "RecordUpsertForm", "MailerMessage", "DateTime", "RequestInfo",
|
|
30
31
|
"Field", "TextField", "EditorField", "NumberField", "BoolField", "EmailField", "URLField", "DateField", "AutodateField", "SelectField", "FileField", "RelationField", "JSONField", "GeoPointField", "PasswordField",
|
|
31
32
|
"ApiError", "NotFoundError", "BadRequestError", "ForbiddenError", "UnauthorizedError", "InternalServerError", "ValidationError",
|
|
32
33
|
"__hooks", "require", "module", "exports", "console", "toString", "sleep", "arrayOf", "unmarshal",
|
|
33
34
|
];
|
|
34
|
-
const EVENT_HOOKS = ["Bootstrap", "Serve", "Terminate", "BackupCreate", "BackupRestore",
|
|
35
|
+
export const EVENT_HOOKS = ["Bootstrap", "Serve", "Terminate", "BackupCreate", "BackupRestore",
|
|
35
36
|
"ModelValidate", "ModelCreate", "ModelCreateExecute", "ModelAfterCreateSuccess", "ModelAfterCreateError", "ModelUpdate", "ModelUpdateExecute", "ModelAfterUpdateSuccess", "ModelAfterUpdateError", "ModelDelete", "ModelDeleteExecute", "ModelAfterDeleteSuccess", "ModelAfterDeleteError",
|
|
36
37
|
"RecordEnrich", "RecordValidate", "RecordCreate", "RecordCreateExecute", "RecordAfterCreateSuccess", "RecordAfterCreateError", "RecordUpdate", "RecordUpdateExecute", "RecordAfterUpdateSuccess", "RecordAfterUpdateError", "RecordDelete", "RecordDeleteExecute", "RecordAfterDeleteSuccess", "RecordAfterDeleteError",
|
|
37
38
|
"CollectionValidate", "CollectionCreate", "CollectionCreateExecute", "CollectionAfterCreateSuccess", "CollectionAfterCreateError", "CollectionUpdate", "CollectionUpdateExecute", "CollectionAfterUpdateSuccess", "CollectionAfterUpdateError", "CollectionDelete", "CollectionDeleteExecute", "CollectionAfterDeleteSuccess", "CollectionAfterDeleteError",
|
|
38
39
|
"MailerSend", "MailerRecordAuthAlertSend", "MailerRecordPasswordResetSend", "MailerRecordVerificationSend", "MailerRecordEmailChangeSend", "MailerRecordOTPSend",
|
|
39
40
|
"RealtimeConnectRequest", "RealtimeMessageSend", "RealtimeSubscribeRequest",
|
|
40
41
|
"SettingsListRequest", "SettingsUpdateRequest", "SettingsReload", "FileDownloadRequest", "FileTokenRequest",
|
|
41
|
-
"RecordAuthRequest", "RecordAuthWithPasswordRequest", "RecordAuthRefreshRequest", "RecordRequestPasswordResetRequest", "RecordConfirmPasswordResetRequest", "RecordRequestVerificationRequest", "RecordConfirmVerificationRequest", "RecordRequestEmailChangeRequest", "RecordConfirmEmailChangeRequest", "RecordRequestOTPRequest", "RecordAuthWithOTPRequest",
|
|
42
|
+
"RecordAuthRequest", "RecordAuthWithPasswordRequest", "RecordAuthWithOAuth2Request", "RecordAuthRefreshRequest", "RecordRequestPasswordResetRequest", "RecordConfirmPasswordResetRequest", "RecordRequestVerificationRequest", "RecordConfirmVerificationRequest", "RecordRequestEmailChangeRequest", "RecordConfirmEmailChangeRequest", "RecordRequestOTPRequest", "RecordAuthWithOTPRequest",
|
|
42
43
|
"RecordsListRequest", "RecordViewRequest", "RecordCreateRequest", "RecordUpdateRequest", "RecordDeleteRequest",
|
|
43
44
|
"CollectionsListRequest", "CollectionViewRequest", "CollectionCreateRequest", "CollectionUpdateRequest", "CollectionDeleteRequest", "CollectionsImportRequest", "BatchRequest",
|
|
44
45
|
].map((n) => "on" + n);
|
|
@@ -126,10 +127,15 @@ function transform(sf: ts.SourceFile, asyncNames: Set<string>, asyncFns: Set<ts.
|
|
|
126
127
|
return out;
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
// Generated bundles in pb_hooks must skip the await insertion below: it rewrites calls by method name (`delete`,
|
|
131
|
+
// `next`, `send` ...), which in ordinary bundled code turns the wrong functions async and breaks their callers. A
|
|
132
|
+
// file whose first line is `// voidbase:raw` is emitted as it stands, with only module/exports/require in scope.
|
|
133
|
+
const isRaw = (code: string) => /^\s*\/\/\s*voidbase:raw\b/.test(code);
|
|
134
|
+
|
|
129
135
|
export function compileHooksDir(dir: string): string {
|
|
130
136
|
const files = readDir(dir);
|
|
131
137
|
const sources = new Map<string, ts.SourceFile>();
|
|
132
|
-
for (const f of files) if (f.kind !== "file") sources.set(f.name, ts.createSourceFile(f.name, f.code, ts.ScriptTarget.ES2022, true, ts.ScriptKind.JS));
|
|
138
|
+
for (const f of files) if (f.kind !== "file" && !isRaw(f.code)) sources.set(f.name, ts.createSourceFile(f.name, f.code, ts.ScriptTarget.ES2022, true, ts.ScriptKind.JS));
|
|
133
139
|
const asyncNames = new Set<string>();
|
|
134
140
|
const asyncFns = new Set<ts.Node>();
|
|
135
141
|
for (let i = 0; i < 20; i++) {
|
|
@@ -143,6 +149,11 @@ export function compileHooksDir(dir: string): string {
|
|
|
143
149
|
const raw: string[] = [];
|
|
144
150
|
for (const f of files) {
|
|
145
151
|
if (f.kind === "file") { raw.push(`${JSON.stringify(f.name)}: ${JSON.stringify(f.code)}`); continue; }
|
|
152
|
+
if (isRaw(f.code)) {
|
|
153
|
+
// no destructure of every global: a bundle declares its own names and would collide with them
|
|
154
|
+
modules.push(`${JSON.stringify(basename(f.name, ".js"))}: async function (__g) { const { module, exports, require } = __g;\n${f.code}\nreturn module.exports; }`);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
146
157
|
const body = transform(sources.get(f.name)!, asyncNames, asyncFns);
|
|
147
158
|
if (f.kind === "hook") hooks.push(`{ name: ${JSON.stringify(f.name)}, run: async function (__g) { ${destructure}\n${body}\n} }`);
|
|
148
159
|
else modules.push(`${JSON.stringify(basename(f.name, ".js"))}: async function (__g) { ${destructure}\n${body}\nreturn module.exports; }`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voidbase-cloud/voidbase",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "PocketBase-compatible backend on Cloudflare Workers (D1, R2, Queues, Durable Objects) via Void, or a single Bun process",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,7 +35,10 @@
|
|
|
35
35
|
"./api": "./src/server/api.ts",
|
|
36
36
|
"./passkeys": "./src/server/webauthn.ts",
|
|
37
37
|
"./cloud": "./src/cloud/rest.ts",
|
|
38
|
-
"./bundle": "./src/node/bundle.ts"
|
|
38
|
+
"./bundle": "./src/node/bundle.ts",
|
|
39
|
+
"./adapter": "./src/adapter/runtime.ts",
|
|
40
|
+
"./adapter/plugin": "./src/adapter/index.ts",
|
|
41
|
+
"./secrets": "./src/env/define.ts"
|
|
39
42
|
},
|
|
40
43
|
"imports": {
|
|
41
44
|
"#platform/env": {
|
|
@@ -110,11 +113,14 @@
|
|
|
110
113
|
"surface": "bun surface/render.ts",
|
|
111
114
|
"test": "bun test",
|
|
112
115
|
"app:sync": "bun scripts/sync-app.ts",
|
|
113
|
-
"check": "void prepare && tsc -p tsconfig.json --noEmit && tsc -p tsconfig.node.json --noEmit",
|
|
116
|
+
"check": "void prepare && tsc -p tsconfig.json --noEmit && tsc -p tsconfig.node.json --noEmit && tsc -p tsconfig.scripts.json --noEmit",
|
|
114
117
|
"pack:check": "npm pack --dry-run",
|
|
115
118
|
"prepublishOnly": "bun run check && bun test",
|
|
116
119
|
"prepare": "husky || true",
|
|
117
|
-
"build:exe": "bun scripts/build-exe.ts"
|
|
120
|
+
"build:exe": "bun scripts/build-exe.ts",
|
|
121
|
+
"ci": "bash scripts/ci.sh",
|
|
122
|
+
"release": "bash scripts/release.sh",
|
|
123
|
+
"cf:builds": "bun scripts/cf-builds.ts"
|
|
118
124
|
},
|
|
119
125
|
"dependencies": {
|
|
120
126
|
"@cf-wasm/photon": "^0.4.0",
|
package/routes/api/[...path].ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
// Every /api/* request is handled by the voidbase Hono app (PocketBase wire protocol).
|
|
2
2
|
import { defineHandler } from "void";
|
|
3
3
|
import { app } from "../../src/server/app";
|
|
4
|
-
import { appApi } from "../../src/server/api";
|
|
5
|
-
import { mountWebAuthn } from "../../src/server/webauthn";
|
|
6
|
-
|
|
7
|
-
// this checkout serves the pocketbase-sveltekit-starter, whose backend registers passkey routes (pb/webauthn)
|
|
8
|
-
mountWebAuthn(appApi().router);
|
|
9
4
|
|
|
10
5
|
const handle = defineHandler((c) =>
|
|
11
6
|
app.fetch(c.req.raw, c.env, (c as unknown as { executionCtx?: ExecutionContext }).executionCtx),
|