@moku-labs/ci 1.2.6 → 1.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/README.md
CHANGED
|
@@ -24,6 +24,7 @@ everything else lives here, once. Not a build tool and not a framework — it ca
|
|
|
24
24
|
[Workflows](#workflows) ·
|
|
25
25
|
[Project checks](#project-checks) ·
|
|
26
26
|
[PR previews](#pr-previews) ·
|
|
27
|
+
[Demos](#demos) ·
|
|
27
28
|
[CLI](#cli) ·
|
|
28
29
|
[The contract](#the-contract) ·
|
|
29
30
|
[Versioning](#versioning) ·
|
|
@@ -40,14 +41,16 @@ bun add -d @moku-labs/ci
|
|
|
40
41
|
bun run release:setup
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
`setup` writes the two workflow files
|
|
44
|
-
registers the trusted publisher
|
|
45
|
-
only does what is still missing.
|
|
44
|
+
`setup` writes the two workflow files and the Dependabot files, adds the missing scripts,
|
|
45
|
+
does the first publish, registers the trusted publisher, applies the branch ruleset and
|
|
46
|
+
allows auto-merge. Run it again any time — it only does what is still missing.
|
|
46
47
|
|
|
47
48
|
> [!NOTE]
|
|
48
|
-
> **Status: `1.x`,
|
|
49
|
-
>
|
|
50
|
-
> `moku-release setup`
|
|
49
|
+
> **Status: `1.x`, in use.** All eight moku packages — `core`, `common`, `web`, `worker`, `room`,
|
|
50
|
+
> `system`, `ai`, `native` — run `package-ci.yml` and `package-release.yml` from `@v1`, and each
|
|
51
|
+
> has shipped a tokenless release with provenance through them. `moku-release setup` did every
|
|
52
|
+
> one of those migrations live, including two first publishes behind 2FA.
|
|
53
|
+
> `app-deploy.yml` has no live user yet.
|
|
51
54
|
|
|
52
55
|
> [!IMPORTANT]
|
|
53
56
|
> Two steps are yours alone. The CLI never handles a credential, and there is no `NPM_TOKEN`
|
|
@@ -60,7 +63,7 @@ only does what is still missing.
|
|
|
60
63
|
|
|
61
64
|
## Why @moku-labs/ci
|
|
62
65
|
|
|
63
|
-
- **One copy of the pipeline.**
|
|
66
|
+
- **One copy of the pipeline.** Eight packages had eight different `ci.yml` and eight different
|
|
64
67
|
`publish.yml`. A fix now lands here and reaches every project through the `@v1` tag.
|
|
65
68
|
- **Scripts are the interface, not tools.** The workflows call `bun run lint`, never `biome`.
|
|
66
69
|
A project with special needs changes its own script, not the central YAML.
|
|
@@ -106,10 +109,13 @@ flowchart LR
|
|
|
106
109
|
|---|---|
|
|
107
110
|
| [`examples/package/publish.local-publish.yml`](examples/package/publish.local-publish.yml) | Fallback: publish from the project's own job. Use it only if the central publish fails npm auth. |
|
|
108
111
|
| [`rulesets/main.json`](rulesets/main.json) | Branch ruleset for `main`: PR only, no force-push, the four `ci / …` checks required. |
|
|
112
|
+
| [`examples/dependabot.yml`](examples/dependabot.yml) | Dependabot config: one grouped PR a day for `@moku-labs/*` only. `setup` writes it to `.github/dependabot.yml`. |
|
|
113
|
+
| [`examples/package/dependabot-automerge.yml`](examples/package/dependabot-automerge.yml) | Turns on auto-merge for a Dependabot PR; the ruleset checks stay the gate. `setup` writes it and allows auto-merge on the repo. |
|
|
109
114
|
|
|
110
115
|
> [!TIP]
|
|
111
|
-
> A Layer-3 app copies `examples/app/ci.yml` by hand and needs
|
|
112
|
-
>
|
|
116
|
+
> A Layer-3 app copies `examples/app/ci.yml` and `examples/dependabot.yml` by hand and needs
|
|
117
|
+
> a `deploy` script. An app merges Dependabot PRs by hand, because the merge runs the deploy.
|
|
118
|
+
> The CLI sets up packages only.
|
|
113
119
|
|
|
114
120
|
## Project checks
|
|
115
121
|
|
|
@@ -152,11 +158,45 @@ bun add https://pkg.pr.new/@moku-labs/core@42 # 42 = PR number, a commit sha w
|
|
|
152
158
|
| A preview URL never reaches `main`: the `lint` job and `doctor` both refuse it. | `package-ci.yml`, `preview-deps` |
|
|
153
159
|
| `preview` is not a required check. Turn it off with `with: { preview: false }`. | caller `ci.yml` |
|
|
154
160
|
|
|
161
|
+
## Demos
|
|
162
|
+
|
|
163
|
+
`demos.yml` runs the moku demos (`moku-labs/demos`) against what a pull request builds, so an
|
|
164
|
+
engine or editor change that breaks a real game fails on its own PR.
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
demos:
|
|
168
|
+
needs: ci
|
|
169
|
+
if: github.event_name == 'pull_request'
|
|
170
|
+
uses: moku-labs/ci/.github/workflows/demos.yml@v1
|
|
171
|
+
with:
|
|
172
|
+
game: https://pkg.pr.new/@moku-labs/game@${{ github.event.pull_request.head.sha }}
|
|
173
|
+
tier: ${{ contains(github.event.pull_request.labels.*.name, 'demos:full') && 'full' || 'fast' }}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
| Input | Default | Meaning |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `game`, `editor` | `""` | A version, a pkg.pr.new URL, or empty for each demo's own pin |
|
|
179
|
+
| `tier` | `fast` | `fast`: `bun run test` on ubuntu. `full`: plus `test:visual` on macos-latest and `test:editor` |
|
|
180
|
+
| `demos_ref` | `main` | demos branch when no paired branch exists |
|
|
181
|
+
| `paired_ref` | PR branch | demos branch with this name wins, for an intended break |
|
|
182
|
+
| `only` | `[]` | JSON array of demo folders; empty = all |
|
|
183
|
+
|
|
184
|
+
| Rule | Where |
|
|
185
|
+
|---|---|
|
|
186
|
+
| A demo is a folder whose `package.json` has `"moku": { "demo": true }`. | demos repo |
|
|
187
|
+
| The workflow only calls the demo's scripts with `--engine` / `--editor`; the demo's runner picks the package, as it does locally. | demo runner |
|
|
188
|
+
| A tracked file with `/Users/`, `/home/…` or `?? "../` fails the run. Paths are parameters. | `discover` |
|
|
189
|
+
| Checks to require: `demos / fast`; `demos / full` reports only on the full tier. | rulesets |
|
|
190
|
+
| Pixels run on macOS: on Linux runners WebGPU loses its device. | `visual-run` |
|
|
191
|
+
|
|
192
|
+
Examples: `examples/package/ci-with-demos.yml` (label `demos:full`) and
|
|
193
|
+
`examples/package/demos-comment.yml` (PR comment `/demos full`).
|
|
194
|
+
|
|
155
195
|
## CLI
|
|
156
196
|
|
|
157
197
|
| Command | When | What it does |
|
|
158
198
|
|---|---|---|
|
|
159
|
-
| `bun run release:setup` | once per project | Idempotent wizard: workflows, script contract, first publish, first tag, trusted publisher, branch ruleset, then `doctor`. `--dry-run` prints every action and
|
|
199
|
+
| `bun run release:setup` | once per project | Idempotent wizard: workflows, Dependabot, script contract, first publish, first tag, trusted publisher, branch ruleset, auto-merge, then `doctor`. `--dry-run` prints every action, changes nothing and asks nothing. `--yes` answers every confirmation for an agent or CI; a step that needs your npm OTP is then printed, not run. |
|
|
160
200
|
| `bun run release:doctor` | any time | Changes nothing in the project; it only runs `git fetch --tags` first. Twelve checks, one line each, and the exact `fix:` command for every red line. `--json` for machines. |
|
|
161
201
|
| `bun run release <patch\|minor\|major\|prerelease>` | each release | Refuses unless the tree is clean and `HEAD == origin/main`. Dispatches `publish.yml`, watches the run, verifies the version and dist-tag on npm. |
|
|
162
202
|
|
package/dist/release.mjs
CHANGED
|
@@ -1109,6 +1109,25 @@ const CI_WORKFLOW_REF = "moku-labs/ci/.github/workflows/package-ci.yml@v1";
|
|
|
1109
1109
|
/** The `ci.yml` body, read from `examples/package/ci.yml`. */
|
|
1110
1110
|
const CI_WORKFLOW = readCentral("examples/package/ci.yml");
|
|
1111
1111
|
//#endregion
|
|
1112
|
+
//#region src/templates/dependabot.ts
|
|
1113
|
+
/**
|
|
1114
|
+
* @file `moku-release` — the Dependabot templates.
|
|
1115
|
+
*
|
|
1116
|
+
* Read from this package's own `examples/dependabot.yml` and
|
|
1117
|
+
* `examples/package/dependabot-automerge.yml`. Dependabot opens one grouped PR a day when a
|
|
1118
|
+
* new `@moku-labs/*` version is out; the automerge workflow merges it once the required
|
|
1119
|
+
* `ci / …` checks pass. The config sits outside `examples/package/` on purpose: it is not a
|
|
1120
|
+
* workflow, and the self-test runs actionlint on every `.yml` one folder below `examples/`.
|
|
1121
|
+
*/
|
|
1122
|
+
/** Repo-relative path the Dependabot config is written to. */
|
|
1123
|
+
const DEPENDABOT_CONFIG_PATH = ".github/dependabot.yml";
|
|
1124
|
+
/** The Dependabot config body, read from `examples/dependabot.yml`. */
|
|
1125
|
+
const DEPENDABOT_CONFIG = readCentral("examples/dependabot.yml");
|
|
1126
|
+
/** Repo-relative path the automerge workflow is written to. */
|
|
1127
|
+
const DEPENDABOT_AUTOMERGE_PATH = ".github/workflows/dependabot-automerge.yml";
|
|
1128
|
+
/** The automerge workflow body, read from `examples/package/dependabot-automerge.yml`. */
|
|
1129
|
+
const DEPENDABOT_AUTOMERGE = readCentral("examples/package/dependabot-automerge.yml");
|
|
1130
|
+
//#endregion
|
|
1112
1131
|
//#region src/templates/publish.ts
|
|
1113
1132
|
/**
|
|
1114
1133
|
* @file `moku-release` — the `.github/workflows/publish.yml` template.
|
|
@@ -1163,6 +1182,14 @@ const workflowTemplates = [{
|
|
|
1163
1182
|
content: PUBLISH_WORKFLOW,
|
|
1164
1183
|
ref: PUBLISH_WORKFLOW_REF
|
|
1165
1184
|
}];
|
|
1185
|
+
/** The Dependabot config and its automerge workflow, in the order `setup` writes them. */
|
|
1186
|
+
const dependabotTemplates = [{
|
|
1187
|
+
path: DEPENDABOT_CONFIG_PATH,
|
|
1188
|
+
content: DEPENDABOT_CONFIG
|
|
1189
|
+
}, {
|
|
1190
|
+
path: DEPENDABOT_AUTOMERGE_PATH,
|
|
1191
|
+
content: DEPENDABOT_AUTOMERGE
|
|
1192
|
+
}];
|
|
1166
1193
|
/**
|
|
1167
1194
|
* Whether a workflow file on disk is the thin caller — i.e. it delegates to the pinned
|
|
1168
1195
|
* central reusable workflow. A file that does not is a legacy hand-written pipeline.
|
|
@@ -2290,6 +2317,32 @@ async function writeWorkflows(setup) {
|
|
|
2290
2317
|
}
|
|
2291
2318
|
}
|
|
2292
2319
|
/**
|
|
2320
|
+
* Write the Dependabot config and its automerge workflow. A missing file is written; a
|
|
2321
|
+
* file that differs is the project's own config and is left alone, without asking.
|
|
2322
|
+
*
|
|
2323
|
+
* @param setup - The wizard state.
|
|
2324
|
+
* @returns Nothing.
|
|
2325
|
+
* @example
|
|
2326
|
+
* await writeDependabot(setup);
|
|
2327
|
+
*/
|
|
2328
|
+
async function writeDependabot(setup) {
|
|
2329
|
+
setup.ui.heading("Dependabot");
|
|
2330
|
+
for (const template of dependabotTemplates) {
|
|
2331
|
+
const existing = await setup.ctx.files.read(template.path);
|
|
2332
|
+
if (existing === template.content) {
|
|
2333
|
+
setup.ui.check(true, `${template.path} up to date`);
|
|
2334
|
+
continue;
|
|
2335
|
+
}
|
|
2336
|
+
if (existing !== void 0) {
|
|
2337
|
+
setup.ui.check(true, `${template.path} is the project's own, left as is`);
|
|
2338
|
+
continue;
|
|
2339
|
+
}
|
|
2340
|
+
if (deferred(setup, `write ${template.path}`)) continue;
|
|
2341
|
+
await setup.ctx.files.write(template.path, template.content);
|
|
2342
|
+
setup.ui.check(true, `${template.path} written`);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
/**
|
|
2293
2346
|
* Bring `package.json` onto the release contract. The change summary is printed first and
|
|
2294
2347
|
* applied only after a confirm; an already-compliant manifest is a checkmark.
|
|
2295
2348
|
*
|
|
@@ -2510,6 +2563,39 @@ async function applyBranchRuleset(setup, ownerRepo) {
|
|
|
2510
2563
|
setup.ui.check(created.code === 0, "branch ruleset applied", created.stderr.trim() || void 0);
|
|
2511
2564
|
}
|
|
2512
2565
|
/**
|
|
2566
|
+
* Turn on the repo setting the automerge workflow needs. Without it `gh pr merge --auto`
|
|
2567
|
+
* fails and every Dependabot PR waits for a manual merge.
|
|
2568
|
+
*
|
|
2569
|
+
* @param setup - The wizard state.
|
|
2570
|
+
* @param ownerRepo - The `owner/repo` slug.
|
|
2571
|
+
* @returns Nothing.
|
|
2572
|
+
* @example
|
|
2573
|
+
* await enableAutoMerge(setup, "moku-labs/common");
|
|
2574
|
+
*/
|
|
2575
|
+
async function enableAutoMerge(setup, ownerRepo) {
|
|
2576
|
+
setup.ui.heading("Auto-merge");
|
|
2577
|
+
const current = await setup.ctx.exec.capture("gh", [
|
|
2578
|
+
"api",
|
|
2579
|
+
`repos/${ownerRepo}`,
|
|
2580
|
+
"--jq",
|
|
2581
|
+
".allow_auto_merge"
|
|
2582
|
+
]);
|
|
2583
|
+
if (current.code === 0 && current.stdout.trim() === "true") {
|
|
2584
|
+
setup.ui.check(true, "auto-merge already allowed");
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
if (deferred(setup, `allow auto-merge on ${ownerRepo}`)) return;
|
|
2588
|
+
const updated = await setup.ctx.exec.capture("gh", [
|
|
2589
|
+
"api",
|
|
2590
|
+
`repos/${ownerRepo}`,
|
|
2591
|
+
"--method",
|
|
2592
|
+
"PATCH",
|
|
2593
|
+
"-F",
|
|
2594
|
+
"allow_auto_merge=true"
|
|
2595
|
+
]);
|
|
2596
|
+
setup.ui.check(updated.code === 0, "auto-merge allowed", updated.stderr.trim() || void 0);
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2513
2599
|
* Run the whole wizard. Every step is skippable, idempotent, and re-runnable; the last one
|
|
2514
2600
|
* is always `doctor`, so the wizard's own verdict is the same report the operator gets
|
|
2515
2601
|
* from `release:doctor`.
|
|
@@ -2537,6 +2623,7 @@ async function runSetup(options) {
|
|
|
2537
2623
|
return 1;
|
|
2538
2624
|
}
|
|
2539
2625
|
await writeWorkflows(setup);
|
|
2626
|
+
await writeDependabot(setup);
|
|
2540
2627
|
await normalizeContract(setup, manifest);
|
|
2541
2628
|
const published = await firstPublish(setup, manifest.name, manifest.version);
|
|
2542
2629
|
if (published === "absent") {
|
|
@@ -2546,8 +2633,11 @@ async function runSetup(options) {
|
|
|
2546
2633
|
await registerTrustedPublisher(setup);
|
|
2547
2634
|
const declared = repositoryUrlOf(manifest);
|
|
2548
2635
|
const ownerRepo = declared === void 0 ? void 0 : ownerRepoFrom(declared);
|
|
2549
|
-
if (ownerRepo === void 0) ui.warn("no GitHub owner/repo — skipping the branch ruleset");
|
|
2550
|
-
else
|
|
2636
|
+
if (ownerRepo === void 0) ui.warn("no GitHub owner/repo — skipping the branch ruleset and auto-merge");
|
|
2637
|
+
else {
|
|
2638
|
+
await applyBranchRuleset(setup, ownerRepo);
|
|
2639
|
+
await enableAutoMerge(setup, ownerRepo);
|
|
2640
|
+
}
|
|
2551
2641
|
ui.heading("Doctor");
|
|
2552
2642
|
const report = await runDoctor({
|
|
2553
2643
|
ctx: setup.ctx,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Dependabot for a moku-family repo. Only @moku-labs/* is watched, grouped into one PR a day.
|
|
2
|
+
# A package also gets examples/package/dependabot-automerge.yml, so the PR merges itself
|
|
3
|
+
# once the required ci / … checks pass. An app merges it by hand: that merge runs the deploy.
|
|
4
|
+
version: 2
|
|
5
|
+
updates:
|
|
6
|
+
- package-ecosystem: "bun"
|
|
7
|
+
directory: "/"
|
|
8
|
+
schedule:
|
|
9
|
+
interval: "daily"
|
|
10
|
+
allow:
|
|
11
|
+
- dependency-name: "@moku-labs/*"
|
|
12
|
+
groups:
|
|
13
|
+
moku:
|
|
14
|
+
patterns:
|
|
15
|
+
- "@moku-labs/*"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# ci.yml of a package whose PRs must run the moku demos (the engine, the editor).
|
|
4
|
+
# Same thin caller as ci.yml, plus a `demos` job after the preview is published.
|
|
5
|
+
# Required checks: "ci / lint", "ci / types", "ci / test", "ci / build" and "demos / fast".
|
|
6
|
+
# The label `demos:full` on the PR adds pixels (macOS) and editor scenarios: "demos / full".
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
pull_request:
|
|
11
|
+
types: [opened, synchronize, reopened, labeled]
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
ci:
|
|
18
|
+
uses: moku-labs/ci/.github/workflows/package-ci.yml@v1
|
|
19
|
+
|
|
20
|
+
demos:
|
|
21
|
+
needs: ci
|
|
22
|
+
if: github.event_name == 'pull_request'
|
|
23
|
+
uses: moku-labs/ci/.github/workflows/demos.yml@v1
|
|
24
|
+
with:
|
|
25
|
+
# The preview job of `ci` published this commit; a full sha resolves on pkg.pr.new.
|
|
26
|
+
# In the editor's repo, pass `editor:` instead of `game:`.
|
|
27
|
+
game: https://pkg.pr.new/@moku-labs/game@${{ github.event.pull_request.head.sha }}
|
|
28
|
+
tier: ${{ contains(github.event.pull_request.labels.*.name, 'demos:full') && 'full' || 'fast' }}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Demos on comment
|
|
2
|
+
|
|
3
|
+
# A PR comment `/demos full` runs the full demos tier against the PR's preview, without a label.
|
|
4
|
+
# Only members and collaborators can trigger it.
|
|
5
|
+
on:
|
|
6
|
+
issue_comment:
|
|
7
|
+
types: [created]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
head:
|
|
14
|
+
if: >-
|
|
15
|
+
github.event.issue.pull_request &&
|
|
16
|
+
startsWith(github.event.comment.body, '/demos full') &&
|
|
17
|
+
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
permissions:
|
|
20
|
+
pull-requests: read
|
|
21
|
+
outputs:
|
|
22
|
+
sha: ${{ steps.pr.outputs.sha }}
|
|
23
|
+
ref: ${{ steps.pr.outputs.ref }}
|
|
24
|
+
steps:
|
|
25
|
+
- id: pr
|
|
26
|
+
env:
|
|
27
|
+
GH_TOKEN: ${{ github.token }}
|
|
28
|
+
PR: ${{ github.event.issue.number }}
|
|
29
|
+
REPO: ${{ github.repository }}
|
|
30
|
+
run: |
|
|
31
|
+
set -euo pipefail
|
|
32
|
+
gh api "repos/$REPO/pulls/$PR" --jq '"sha=\(.head.sha)\nref=\(.head.ref)"' >> "$GITHUB_OUTPUT"
|
|
33
|
+
|
|
34
|
+
demos:
|
|
35
|
+
needs: head
|
|
36
|
+
uses: moku-labs/ci/.github/workflows/demos.yml@v1
|
|
37
|
+
with:
|
|
38
|
+
game: https://pkg.pr.new/@moku-labs/game@${{ needs.head.outputs.sha }}
|
|
39
|
+
# issue_comment has no head_ref: name the branch to pair with explicitly.
|
|
40
|
+
paired_ref: ${{ needs.head.outputs.ref }}
|
|
41
|
+
tier: full
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Dependabot automerge
|
|
2
|
+
|
|
3
|
+
# Turns on auto-merge for a Dependabot PR. The branch ruleset stays the gate: the merge
|
|
4
|
+
# waits for ci / lint, ci / types, ci / test and ci / build. Needs the repo setting
|
|
5
|
+
# allow_auto_merge, which `moku-release setup` turns on.
|
|
6
|
+
on: pull_request
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
pull-requests: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
automerge:
|
|
14
|
+
if: github.event.pull_request.user.login == 'dependabot[bot]'
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Merge once required checks pass
|
|
18
|
+
run: gh pr merge --auto --squash "$PR_URL"
|
|
19
|
+
env:
|
|
20
|
+
PR_URL: ${{ github.event.pull_request.html_url }}
|
|
21
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moku-labs/ci",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Central CI and release for the moku family: reusable workflows, caller examples and the moku-release CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@biomejs/biome": "2.4.16",
|
|
35
|
-
"@moku-labs/common": "0.3.
|
|
35
|
+
"@moku-labs/common": "0.3.3",
|
|
36
36
|
"@types/bun": "1.3.14",
|
|
37
37
|
"@vitest/coverage-istanbul": "4.0.18",
|
|
38
38
|
"eslint": "9.39.3",
|