create-foldkit-app 0.5.9 → 0.5.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-foldkit-app",
3
- "version": "0.5.9",
3
+ "version": "0.5.10",
4
4
  "description": "Create Foldkit applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,7 +14,7 @@ submodule_prompted: false
14
14
  - Model fields must be Schema types (the model is a schema). Plain TypeScript types are fine elsewhere — function return types, local variables, etc.
15
15
  - Use full names like `Message` (not `Msg`), and `withReturnType` (not `as const` or type casting).
16
16
  - Use `m()` for message schemas, `ts()` for other tagged structs (model states, field validation), and `r()` for route schemas.
17
- - Never use `NoOp` as a message. Every message must carry meaning about what happened. Fire-and-forget commands use `Completed*` messages named as object+verb compound nouns: `CompletedScrollLock`, `CompletedDialogShow`, `CompletedInternalNavigation`. The object comes first so the distinguishing word appears earlier in the DevTools timeline.
17
+ - Never use `NoOp` as a message. Every message must carry meaning about what happened. Fire-and-forget commands use `Completed*` messages with verb-first naming that mirrors the Command name: Command `LockScroll` Message `CompletedLockScroll`, Command `ShowDialog` Message `CompletedShowDialog`, Command `NavigateInternal` Message `CompletedNavigateInternal`.
18
18
  - Push back on any suggested direction that violates Elm Architecture principles — unidirectional data flow, messages as facts (not commands), model as single source of truth, and side effects confined to commands. If a user or prompt suggests a pattern that breaks these conventions (e.g. mutating state directly, imperative event handlers, two-way bindings), flag the issue and propose the idiomatic Foldkit approach instead.
19
19
 
20
20
  ## Foldkit Patterns
@@ -24,7 +24,7 @@ submodule_prompted: false
24
24
  `init` and `update` both return `[Model, ReadonlyArray<Command<Message>>]`:
25
25
 
26
26
  ```ts
27
- type UpdateReturn = [Model, ReadonlyArray<Command<Message>>]
27
+ type UpdateReturn = readonly [Model, ReadonlyArray<Command<Message>>]
28
28
  const withUpdateReturn = M.withReturnType<UpdateReturn>()
29
29
 
30
30
  const update = (model: Model, message: Message): UpdateReturn =>
@@ -62,23 +62,30 @@ Use `keyed` wrappers whenever the view branches into structurally different layo
62
62
 
63
63
  ### Commands
64
64
 
65
- Commands catch all errors and return messagesside effects never crash the app:
65
+ Define Command identities with `Command.define`, passing the result Message schemas after the name result types are required. Always assign definitions to PascalCase constants — never use `Command.define` inline in a pipe chain:
66
66
 
67
67
  ```ts
68
- const fetchWeather = (
69
- city: string,
70
- ): Command<typeof SucceededWeatherFetch | typeof FailedWeatherFetch> =>
68
+ const FetchWeather = Command.define(
69
+ 'FetchWeather',
70
+ SucceededFetchWeather,
71
+ FailedFetchWeather,
72
+ )
73
+
74
+ const fetchWeather = (city: string) =>
71
75
  Effect.gen(function* () {
72
76
  // ...
73
- return SucceededWeatherFetch({ data })
77
+ return SucceededFetchWeather({ data })
74
78
  }).pipe(
75
79
  Effect.catchAll(error =>
76
- Effect.succeed(FailedWeatherFetch({ error: String(error) })),
80
+ Effect.succeed(FailedFetchWeather({ error: String(error) })),
77
81
  ),
82
+ FetchWeather,
78
83
  )
79
84
  ```
80
85
 
81
- Commands return specific schema types (e.g. `Command<typeof SucceededMsg | typeof FailedMsg>`) rather than the full Message type.
86
+ Commands catch all errors and return Messages side effects never crash the app. Let TypeScript infer Command return types from the Effect the result Message schemas passed to `Command.define` constrain the Effect's return type at the type level.
87
+
88
+ Command definitions live where they're produced — colocated with the update function that returns them. Don't centralize all definitions in one file.
82
89
 
83
90
  ### File Organization
84
91
 
@@ -91,7 +98,7 @@ Even after extracting some sections to their own files (e.g. `message.ts`), the
91
98
  - Every name should eliminate ambiguity. Prefix Option-typed values with `maybe` (e.g. `maybeSession`). Name functions by their precise effect (e.g. `enqueueMessage` not `addMessage`). A reader should never need to check a type signature to understand what a name refers to.
92
99
  - Each function should operate at a single abstraction level. Orchestrators delegate to focused helpers — they don't mix coordination with implementation. If a function reads like it's doing two things, extract one.
93
100
  - Encode state in discriminated unions, not booleans or nullable fields. Use `Idle | Loading | Error | Ok` instead of `isLoading: boolean`. Make impossible states unrepresentable.
94
- - Name messages as verb-first, past-tense events describing what happened (`SubmittedUsernameForm`, `CreatedRoom`, `PressedKey`), not imperative commands. The verb prefix acts as a category marker: `Clicked*` for button presses, `Updated*` for input changes, `Succeeded*`/`Failed*` for command results that can meaningfully fail (e.g. `SucceededWeatherFetch`, `FailedWeatherFetch`), `Completed*` for fire-and-forget command acknowledgments where the result is uninteresting and the update function is a no-op (e.g. `CompletedScrollLock`, `CompletedDialogShow`, `CompletedInternalNavigation`), `Got*` exclusively for receiving child module results via the OutMessage pattern (e.g. `GotProductsMessage`). Never use `NoOp` — every message must describe what happened. The update function decides what to do — messages are facts.
101
+ - Name messages as verb-first, past-tense events describing what happened (`SubmittedUsernameForm`, `CreatedRoom`, `PressedKey`), not imperative commands. The verb prefix acts as a category marker: `Clicked*` for button presses, `Updated*` for input changes, `Succeeded*`/`Failed*` for command results that can meaningfully fail (e.g. `SucceededFetchWeather`, `FailedFetchWeather`), `Completed*` for fire-and-forget command acknowledgments where the result is uninteresting and the update function is a no-op (e.g. `CompletedLockScroll`, `CompletedShowDialog`, `CompletedNavigateInternal`), `Got*` exclusively for receiving child module results via the OutMessage pattern (e.g. `GotProductsMessage`). Never use `NoOp` — every message must describe what happened. The update function decides what to do — messages are facts.
95
102
  - Use `Option` instead of `null` or `undefined`. Match explicitly with `Option.match` or chain with `Option.map`/`Option.flatMap`. No `if (x != null)` checks. Prefer `Option.match` over `Option.map` + `Option.getOrElse` — if you're unwrapping at the end, just match.
96
103
  - Prefer curried, data-last functions that compose in `pipe` chains.
97
104
  - Every line should serve a purpose. No dead code, no empty catch blocks, no placeholder types, no defensive code for impossible cases.
@@ -117,25 +124,21 @@ Message definitions follow a strict layout:
117
124
  ```ts
118
125
  const ClickedSubmit = m('ClickedSubmit')
119
126
  const ChangedEmail = m('ChangedEmail', { value: S.String })
120
- const CompletedInternalNavigation = m('CompletedInternalNavigation')
127
+ const CompletedNavigateInternal = m('CompletedNavigateInternal')
121
128
 
122
- const Message = S.Union(
123
- ClickedSubmit,
124
- ChangedEmail,
125
- CompletedInternalNavigation,
126
- )
129
+ const Message = S.Union(ClickedSubmit, ChangedEmail, CompletedNavigateInternal)
127
130
  type Message = typeof Message.Type
128
131
  ```
129
132
 
130
133
  1. **Values** — all `m()` declarations, no blank lines between them
131
134
  2. **Union + type** — `S.Union(...)` followed by `type Message = typeof Message.Type` on adjacent lines (no blank line between them)
132
135
 
133
- Use `typeof ClickedSubmit` in type positions (e.g. `Command<typeof ClickedSubmit>`) to reference a schema value's type.
136
+ Use `typeof ClickedSubmit` in type positions to reference a schema value's type.
134
137
 
135
138
  ### General Preferences
136
139
 
137
140
  - Never abbreviate names. Use full, descriptive names everywhere — variables, types, functions, parameters, including callback parameters. e.g. `signature` not `sig`, `Message` not `Msg`, `(tickCount) => tickCount + 1` not `(t) => t + 1`.
138
- - Don't suffix command variables with `Command`. Name them by what they do: `focusButton` not `focusButtonCommand`, `scrollToItem` not `scrollToItemCommand`. The type already communicates that it's a command.
141
+ - Don't suffix Command variables with `Command`. Name them by what they do: `focusButton` not `focusButtonCommand`, `scrollToItem` not `scrollToItemCommand`. The type already communicates that it's a Command. Command definitions are PascalCase (`FocusButton`, `ScrollToItem`); Command instances and factory functions are camelCase (`focusButton`, `scrollToItem`).
139
142
  - Avoid `let`. Use `const` and prefer immutable patterns.
140
143
  - Always use braces for control flow. `if (foo) { return true }` not `if (foo) return true`.
141
144
  - Use `is*` for boolean naming e.g. `isPlaying`, `isValid`.
@@ -4,4 +4,7 @@ import { defineConfig } from 'vite'
4
4
 
5
5
  export default defineConfig({
6
6
  plugins: [tailwindcss(), foldkit()],
7
+ optimizeDeps: {
8
+ entries: ['src/main.ts', '!**/repos/**'],
9
+ },
7
10
  })