@plutoframework/frontend 0.1.1 → 0.1.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 CHANGED
@@ -196,3 +196,20 @@ The one-time npm trusted publisher is scoped to:
196
196
  The workflow uses GitHub-hosted runners with `id-token: write`; npm exchanges the
197
197
  workflow identity for a short-lived publish credential. No `NPM_TOKEN` secret is
198
198
  needed. See [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
199
+
200
+ ## Source readability
201
+
202
+ Development tooling uses ESLint Stylistic with an 80-column expanded convention.
203
+ Run `npm run fmt` to apply the canonical Prettier + ESLint result. `npm run check`
204
+ checks formatting without writing, then lint, types, behavior tests and builds.
205
+ Braces, multiline blocks, separated object/type fields and logical blank lines
206
+ are enforced; nested ternaries require explicit branches or named mappings.
207
+ The policy is visible in `scripts/readability.mjs`. These are dev-only tools and
208
+ add no runtime dependencies to state or React entrypoints.
209
+
210
+ Standalone comment blocks have one blank line above them, except at line 1.
211
+ Consecutive comment lines stay together, and the comment remains attached to the
212
+ code below. JavaScript and TypeScript lint/format checks enforce this convention,
213
+ including the start of a function or branch. Inline trailing comments are kept
214
+ in place, and next-line compiler/linter directives retain their target. The
215
+ cross-language convention is also recorded in AGENTS.md for future changes.
@@ -3,5 +3,8 @@ import { useSyncExternalStore } from "react";
3
3
  /** Subscribe only. The host owns the controller's reset/disposal lifecycle. */
4
4
  export function useMachine(machine) {
5
5
  const state = useSyncExternalStore(machine.subscribe, machine.getSnapshot, machine.getInitialSnapshot);
6
- return { state, send: machine.send };
6
+ return {
7
+ state,
8
+ send: machine.send,
9
+ };
7
10
  }
@@ -8,21 +8,25 @@ export function createMachine(options) {
8
8
  const listeners = new Set();
9
9
  const executions = new Set();
10
10
  function notify() {
11
- for (const listener of [...listeners])
11
+ for (const listener of [...listeners]) {
12
12
  listener();
13
+ }
13
14
  }
14
15
  function cancel() {
15
16
  // Fence callbacks BEFORE synchronous abort handlers can send events.
16
17
  epoch++;
17
- for (const controller of executions)
18
+ for (const controller of executions) {
18
19
  controller.abort();
20
+ }
19
21
  executions.clear();
20
22
  }
21
23
  async function send(event) {
22
- if (disposed)
24
+ if (disposed) {
23
25
  return false;
24
- if (evaluating)
26
+ }
27
+ if (evaluating) {
25
28
  throw new Error("Transitions must not send events");
29
+ }
26
30
  const generation = epoch;
27
31
  evaluating = true;
28
32
  let decision;
@@ -32,8 +36,9 @@ export function createMachine(options) {
32
36
  finally {
33
37
  evaluating = false;
34
38
  }
35
- if (!decision.accepted)
39
+ if (!decision.accepted) {
36
40
  return false;
41
+ }
37
42
  if (decision.effect !== undefined && !execute) {
38
43
  throw new Error("An effect requires an executor");
39
44
  }
@@ -44,8 +49,9 @@ export function createMachine(options) {
44
49
  try {
45
50
  notify();
46
51
  // Subscribers may reset/dispose the flow during notification.
47
- if (generation !== epoch || disposed)
52
+ if (generation !== epoch || disposed) {
48
53
  return true;
54
+ }
49
55
  if (decision.effect !== undefined && execute) {
50
56
  await execute(decision.effect, {
51
57
  signal: controller.signal,
@@ -68,8 +74,9 @@ export function createMachine(options) {
68
74
  getSnapshot: () => current,
69
75
  getInitialSnapshot: () => initial,
70
76
  subscribe(listener) {
71
- if (disposed)
77
+ if (disposed) {
72
78
  return () => { };
79
+ }
73
80
  listeners.add(listener);
74
81
  return () => {
75
82
  listeners.delete(listener);
@@ -77,15 +84,17 @@ export function createMachine(options) {
77
84
  },
78
85
  send,
79
86
  reset() {
80
- if (disposed)
87
+ if (disposed) {
81
88
  return;
89
+ }
82
90
  cancel();
83
91
  current = initial;
84
92
  notify();
85
93
  },
86
94
  dispose() {
87
- if (disposed)
95
+ if (disposed) {
88
96
  return;
97
+ }
89
98
  disposed = true;
90
99
  cancel();
91
100
  listeners.clear();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plutoframework/frontend",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Explicit, typed frontend state transitions and React subscriptions for Pluto applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,22 +38,27 @@
38
38
  },
39
39
  "scripts": {
40
40
  "build": "tsc -p tsconfig.build.json",
41
- "fmt": "prettier --write .",
42
- "check": "prettier --check . && tsc --noEmit && npm test && npm run build",
43
- "test": "vitest run",
41
+ "fmt": "node scripts/format.mjs && eslint .",
42
+ "check": "node scripts/format.mjs --check && eslint . && tsc --noEmit && npm test && npm run build",
43
+ "test": "vitest run && node --test scripts/format.test.mjs",
44
44
  "test:mutation": "python3 scripts/mutation.py",
45
45
  "prepack": "npm run build"
46
46
  },
47
47
  "devDependencies": {
48
- "typescript": "5.9.3",
49
- "vitest": "5.0.5",
50
- "fast-check": "4.10.2",
51
- "react": "19.3.1",
52
- "react-dom": "19.3.1",
48
+ "@eslint/js": "10.0.1",
49
+ "@stylistic/eslint-plugin": "5.10.0",
50
+ "@testing-library/react": "16.3.3",
53
51
  "@types/react": "19.3.0",
54
52
  "@types/react-dom": "19.3.0",
55
- "@testing-library/react": "16.3.3",
53
+ "eslint": "10.13.0",
54
+ "eslint-plugin-react-hooks": "7.1.1",
55
+ "fast-check": "4.10.2",
56
56
  "jsdom": "30.1.3",
57
- "prettier": "3.9.10"
57
+ "prettier": "3.9.10",
58
+ "react": "19.3.1",
59
+ "react-dom": "19.3.1",
60
+ "typescript": "5.9.3",
61
+ "typescript-eslint": "8.72.1",
62
+ "vitest": "5.0.5"
58
63
  }
59
64
  }