@danieljvdm/dev-kit 0.11.1 → 0.11.3
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 +2 -2
- package/package.json +1 -1
- package/skills/dev-kit/SKILL.md +3 -3
- package/skills/effect-ts/SKILL.md +1 -1
- package/skills/effect-ts/references/atom-cache-lifecycle.md +6 -0
- package/skills/effect-ts/references/atom-http-and-invalidation.md +4 -0
- package/skills/effect-ts/references/atom-testing.md +4 -0
- package/skills/effect-ts/references/guide-atom-data-fetching.md +17 -1
- package/src/oxlint.js +5 -2
- package/src/oxlint.ts +5 -2
- package/templates/vite-plus/github-actions-check.yml +2 -4
package/README.md
CHANGED
|
@@ -376,8 +376,8 @@ install, runs `dev-kit apply --locked`, and only then runs custom preparation,
|
|
|
376
376
|
formatting, linting, tests, and typechecking. Its default typecheck command is
|
|
377
377
|
`vp run typecheck`; `workflow.typecheck` replaces it. Vite+ maps install flags
|
|
378
378
|
to the detected package manager. The template installs the consumer's declared
|
|
379
|
-
Bun version
|
|
380
|
-
`
|
|
379
|
+
Bun version, follows the maintained `setup-bun@v2` tag, and names the current
|
|
380
|
+
`setup-vp` release because that action's `v1` tag is frozen. Existing
|
|
381
381
|
workflows remain user-owned until their rendered content matches exactly—Dev
|
|
382
382
|
Kit never merges YAML. See the primary
|
|
383
383
|
[`setup-vp` versioning guidance](https://github.com/voidzero-dev/setup-vp#versioning),
|
package/package.json
CHANGED
package/skills/dev-kit/SKILL.md
CHANGED
|
@@ -146,9 +146,9 @@ and `workflow.typecheck`; treat these commands as trusted manifest input.
|
|
|
146
146
|
|
|
147
147
|
The workflow must use one frozen, script-suppressed install, then locked Dev Kit
|
|
148
148
|
convergence before preparation or checks. Set up Bun from the consumer's
|
|
149
|
-
`packageManager` or `engines.bun` declaration before Vite+ setup.
|
|
150
|
-
`setup-bun`
|
|
151
|
-
`v1` tag is frozen
|
|
149
|
+
`packageManager` or `engines.bun` declaration before Vite+ setup. Follow the
|
|
150
|
+
maintained `setup-bun` major tag, name an explicit `setup-vp` release because
|
|
151
|
+
its `v1` tag is frozen, and let Vite+ resolve the consumer's compatible locked
|
|
152
152
|
version.
|
|
153
153
|
|
|
154
154
|
## Ownership and conflicts
|
|
@@ -110,7 +110,7 @@ When the task touches one of these areas, consult the matching guide before impl
|
|
|
110
110
|
values, time zones, DST-safe arithmetic, formatting, Date interoperability,
|
|
111
111
|
and deterministic `TestClock` tests
|
|
112
112
|
- `./references/guide-atom-data-fetching.md` for the core Effect Atom HTTP
|
|
113
|
-
data-fetching workflow and ownership rules
|
|
113
|
+
data-fetching workflow, React hook choice, and action-lifetime ownership rules
|
|
114
114
|
- `./references/atom-cache-lifecycle.md` for Effect Atom registry scope,
|
|
115
115
|
runtime memoization, families, TTL, SWR, polling, and aggregation resets
|
|
116
116
|
- `./references/atom-http-and-invalidation.md` for `AtomHttpApi.Service`,
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
Place one provider around the client application subtree that should share data. Nested or route-local providers create separate caches.
|
|
8
8
|
|
|
9
|
+
### React mounts and registry disposal
|
|
10
|
+
|
|
11
|
+
`useAtomValue` keeps an atom active through its value subscription. `useAtomSet` and `useAtomRefresh` mount an atom through a React effect, while `useAtom` combines one value subscription with a setter. Use the combined hook when one component reads and writes the same atom; composing `useAtomValue` and `useAtomSet` adds a second mount and obscures which lifetime owns the work.
|
|
12
|
+
|
|
13
|
+
Unmount releases only that hook's subscription or mount. The registry removes a node only after it has no remaining consumers and its idle TTL permits removal. Node disposal runs registered finalizers, including cancellation of interruptible Effect work. Therefore component unmount, registry eviction, and `Atom.Interrupt` are distinct events; do not use an unconditional interrupt write as a substitute for releasing a React mount.
|
|
14
|
+
|
|
9
15
|
An atom runtime and a registry solve different problems:
|
|
10
16
|
|
|
11
17
|
- the registry stores atom nodes, values, subscriptions, idle timers, and finalizers;
|
|
@@ -51,6 +51,10 @@ await mutate({
|
|
|
51
51
|
|
|
52
52
|
The mutation's `reactivityKeys` are invalidated only after the request succeeds. Failed mutations do not invalidate. Keep navigation, toasts, dialog closure, and optimistic UI at the action owner; keep shared server-state refresh in reactivity keys.
|
|
53
53
|
|
|
54
|
+
If the component also renders the mutation result, use `useAtom(updateProject)` instead of pairing `useAtomValue` with `useAtomSet`. A module-scoped mutation atom is shared registry state, so an unconditional cleanup write of `Atom.Interrupt` can cancel work owned by another consumer and publishes an interrupted failure.
|
|
55
|
+
|
|
56
|
+
Keep a multi-step mutation sequence in an owner that lives for the whole sequence. When navigation can unmount the initiating route after one request succeeds, a component-owned sequential fan-out can leave a partially completed operation. Use one stable workflow atom or service when client ownership is sufficient; use one server-side command or durable workflow when completion must survive browser navigation or disconnects.
|
|
57
|
+
|
|
54
58
|
## Use one key vocabulary
|
|
55
59
|
|
|
56
60
|
Array keys represent independent keys. Record keys support hierarchical broad-plus-entity invalidation:
|
|
@@ -56,6 +56,10 @@ Prefer the installed test APIs over invented helpers or matchers. The upstream E
|
|
|
56
56
|
3. Run a failed mutation; assert no invalidation.
|
|
57
57
|
4. Assert cleanup removes invalidation handlers after query disposal.
|
|
58
58
|
|
|
59
|
+
### React Strict Mode and action ownership
|
|
60
|
+
|
|
61
|
+
Use a React integration test when hook cleanup or action ownership is part of the behavior. Render the owner inside `StrictMode` and assert the development setup-cleanup replay does not write `Atom.Interrupt` or publish an interrupted failure before an explicit cancellation. When an action spans multiple requests, unmount during the sequence and prove the chosen policy: either the stable owner completes every step, or an explicit cancellation interrupts it intentionally. If the atom is shared, also prove unmounting one consumer does not cancel work still owned by another.
|
|
62
|
+
|
|
59
63
|
### Aggregate stability
|
|
60
64
|
|
|
61
65
|
Mount a route atom that uses `AsyncResult.all`, resolve every input, unmount, and remount inside the retention window. Assert the aggregate never returns to `Initial`. Then let one input expire and prove the aggregate reset is caused by that input, not the retained queries.
|
|
@@ -23,6 +23,22 @@ Model server data as stable atoms owned outside React renders. Give the applicat
|
|
|
23
23
|
- Ensure every input to a route-level `AsyncResult.all` has stable atom identity and compatible retention. One newly allocated or immediately evicted input returns to `Initial` and makes the whole aggregate appear reset even when the other inputs remain cached.
|
|
24
24
|
- Do not describe manual refresh or polling as freshness caching. Refresh is forceful; `staleTime` only gates SWR's automatic mount/focus decisions.
|
|
25
25
|
|
|
26
|
+
## React hooks and action lifetime
|
|
27
|
+
|
|
28
|
+
Choose the hook that expresses the component's ownership:
|
|
29
|
+
|
|
30
|
+
- `useAtomValue(atom)` subscribes for rendering without returning a setter;
|
|
31
|
+
- `useAtomSet(atom)` mounts a writable atom and returns a setter without rendering from its value;
|
|
32
|
+
- `useAtom(atom)` subscribes and returns a setter when the same component both renders and writes the atom.
|
|
33
|
+
|
|
34
|
+
Do not pair `useAtomValue(atom)` with `useAtomSet(atom)` for the same atom in one component. The split form creates both a value subscription and a separate mount. Use `useAtom(atom)` instead. Calling an `Atom.family` directly with a stable primitive during render already returns the stable family member; a surrounding `useMemo` is not needed merely to preserve atom identity.
|
|
35
|
+
|
|
36
|
+
The React hooks release their subscriptions and mounts when their component lifetime ends. Registry node removal then follows the atom's idle TTL and remaining consumers, and node disposal runs the atom lifetime finalizers. Do not confuse hook cleanup, idle retention, and operation cancellation: another consumer or a nonzero TTL can intentionally keep the node and its work alive after one component unmounts.
|
|
37
|
+
|
|
38
|
+
Do not add a cleanup-only effect such as `useEffect(() => () => set(Atom.Interrupt), [set])` merely to mirror component unmount. `Atom.Interrupt` is a write that publishes an interrupted `AsyncResult.Failure`, not a passive release function. The cleanup also runs on dependency changes and during React Strict Mode's development effect replay, and it can interrupt work still owned by another consumer. Use `Atom.Interrupt` for an explicit user cancellation or a deliberately scoped cancellation policy, and test that ownership under Strict Mode.
|
|
39
|
+
|
|
40
|
+
Decide whether an action must survive its initiating component before choosing its owner. A sequential client fan-out can partially complete if route unmount disposes its owner after earlier mutations succeed but before later ones start. If the whole sequence must be durable, move it to a stable workflow owner or one server-side command instead of relying on a route component's hook lifetime.
|
|
41
|
+
|
|
26
42
|
## Completion check
|
|
27
43
|
|
|
28
|
-
Confirm one registry boundary, stable atom identity, deliberate TTL/staleness/polling values, matching query and mutation keys, SSR-safe browser access, and tests for every lifecycle behavior changed.
|
|
44
|
+
Confirm one registry boundary, stable atom identity, deliberate TTL/staleness/polling values, matching query and mutation keys, an intentional React hook and action lifetime, SSR-safe browser access, and tests for every lifecycle behavior changed.
|
package/src/oxlint.js
CHANGED
|
@@ -24,7 +24,7 @@ export const recommendedOxlintConfig = {
|
|
|
24
24
|
"import/default": "off",
|
|
25
25
|
"import/namespace": "off",
|
|
26
26
|
"import/no-cycle": "error",
|
|
27
|
-
"import/no-duplicates": "error",
|
|
27
|
+
"import/no-duplicates": ["error", { preferInline: true }],
|
|
28
28
|
"import/no-self-import": "error",
|
|
29
29
|
"react/exhaustive-deps": "error",
|
|
30
30
|
"react/rules-of-hooks": "error",
|
|
@@ -38,7 +38,10 @@ export const recommendedOxlintConfig = {
|
|
|
38
38
|
},
|
|
39
39
|
{ blankLine: "always", prev: "*", next: "return" },
|
|
40
40
|
],
|
|
41
|
-
"typescript/consistent-type-imports":
|
|
41
|
+
"typescript/consistent-type-imports": [
|
|
42
|
+
"error",
|
|
43
|
+
{ fixStyle: "inline-type-imports", prefer: "type-imports" },
|
|
44
|
+
],
|
|
42
45
|
"typescript/no-floating-promises": "off",
|
|
43
46
|
"typescript/no-explicit-any": "error",
|
|
44
47
|
"typescript/no-misused-spread": "off",
|
package/src/oxlint.ts
CHANGED
|
@@ -27,7 +27,7 @@ export const recommendedOxlintConfig = {
|
|
|
27
27
|
"import/default": "off",
|
|
28
28
|
"import/namespace": "off",
|
|
29
29
|
"import/no-cycle": "error",
|
|
30
|
-
"import/no-duplicates": "error",
|
|
30
|
+
"import/no-duplicates": ["error", { preferInline: true }],
|
|
31
31
|
"import/no-self-import": "error",
|
|
32
32
|
"react/exhaustive-deps": "error",
|
|
33
33
|
"react/rules-of-hooks": "error",
|
|
@@ -41,7 +41,10 @@ export const recommendedOxlintConfig = {
|
|
|
41
41
|
},
|
|
42
42
|
{ blankLine: "always", prev: "*", next: "return" },
|
|
43
43
|
],
|
|
44
|
-
"typescript/consistent-type-imports":
|
|
44
|
+
"typescript/consistent-type-imports": [
|
|
45
|
+
"error",
|
|
46
|
+
{ fixStyle: "inline-type-imports", prefer: "type-imports" },
|
|
47
|
+
],
|
|
45
48
|
"typescript/no-floating-promises": "off",
|
|
46
49
|
"typescript/no-explicit-any": "error",
|
|
47
50
|
"typescript/no-misused-spread": "off",
|
|
@@ -24,12 +24,10 @@ jobs:
|
|
|
24
24
|
|
|
25
25
|
# setup-bun resolves the consumer's packageManager or engines.bun version.
|
|
26
26
|
- name: Set up Bun
|
|
27
|
-
uses: oven-sh/setup-bun@
|
|
27
|
+
uses: oven-sh/setup-bun@v2
|
|
28
28
|
|
|
29
|
-
# setup-vp's v1 tag is frozen. Keep this v1.16.1 commit current with
|
|
30
|
-
# Renovate or Dependabot so fixes arrive through reviewed pull requests.
|
|
31
29
|
- name: Set up Vite+ and install dependencies
|
|
32
|
-
uses: voidzero-dev/setup-vp@
|
|
30
|
+
uses: voidzero-dev/setup-vp@v1.16.1
|
|
33
31
|
with:
|
|
34
32
|
# Vite+ version intentionally resolves from the consumer manifest/lock.
|
|
35
33
|
node-version: "24"
|