create-foldkit-app 0.29.0 → 0.29.1

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.
Files changed (68) hide show
  1. package/dist/templates/base/AGENTS.md +5 -112
  2. package/dist/templates/base/FOLDKIT.md +144 -0
  3. package/dist/templates/examples/api-cache/src/main.ts +36 -40
  4. package/dist/templates/examples/auth/src/main.ts +16 -15
  5. package/dist/templates/examples/auth/src/page/loggedIn/update.ts +10 -11
  6. package/dist/templates/examples/auth/src/page/loggedOut/page/login.ts +44 -55
  7. package/dist/templates/examples/auth/src/page/loggedOut/update.ts +8 -12
  8. package/dist/templates/examples/auth/src/update.ts +67 -73
  9. package/dist/templates/examples/canvas-art/src/main.ts +32 -31
  10. package/dist/templates/examples/charting/src/init.test.ts +6 -3
  11. package/dist/templates/examples/charting/src/init.ts +4 -4
  12. package/dist/templates/examples/charting/src/update.ts +34 -38
  13. package/dist/templates/examples/counter/src/main.ts +13 -13
  14. package/dist/templates/examples/counters/src/counter.ts +9 -8
  15. package/dist/templates/examples/counters/src/main.ts +20 -26
  16. package/dist/templates/examples/crash-view/src/main.ts +5 -3
  17. package/dist/templates/examples/embedding/src/main.ts +11 -9
  18. package/dist/templates/examples/form/src/main.ts +79 -89
  19. package/dist/templates/examples/generative-art/src/main.ts +6 -4
  20. package/dist/templates/examples/generative-art/src/update.ts +26 -23
  21. package/dist/templates/examples/interrupting-commands/src/main.ts +68 -66
  22. package/dist/templates/examples/job-application/src/main.ts +3 -4
  23. package/dist/templates/examples/job-application/src/step/attachments.ts +16 -19
  24. package/dist/templates/examples/job-application/src/step/coverLetter.ts +5 -5
  25. package/dist/templates/examples/job-application/src/step/education/education.ts +12 -17
  26. package/dist/templates/examples/job-application/src/step/education/entry.ts +29 -42
  27. package/dist/templates/examples/job-application/src/step/personalInfo.ts +35 -44
  28. package/dist/templates/examples/job-application/src/step/skills/entry.ts +15 -21
  29. package/dist/templates/examples/job-application/src/step/skills/skills.ts +12 -17
  30. package/dist/templates/examples/job-application/src/step/workHistory/entry.ts +41 -58
  31. package/dist/templates/examples/job-application/src/step/workHistory/workHistory.ts +12 -17
  32. package/dist/templates/examples/job-application/src/update.ts +26 -29
  33. package/dist/templates/examples/kanban/src/main.ts +3 -4
  34. package/dist/templates/examples/kanban/src/update.ts +31 -31
  35. package/dist/templates/examples/managed-resource-layer/src/main.ts +30 -34
  36. package/dist/templates/examples/map/src/main.ts +48 -56
  37. package/dist/templates/examples/pixel-art/src/comparison.bench.test.ts +34 -13
  38. package/dist/templates/examples/pixel-art/src/main.bench.ts +3 -3
  39. package/dist/templates/examples/pixel-art/src/main.ts +3 -4
  40. package/dist/templates/examples/pixel-art/src/update.ts +109 -100
  41. package/dist/templates/examples/query-sync/src/main.ts +27 -25
  42. package/dist/templates/examples/route-transitions/src/main.ts +46 -43
  43. package/dist/templates/examples/route-transitions/src/story.test.ts +11 -11
  44. package/dist/templates/examples/routing/src/main.ts +16 -14
  45. package/dist/templates/examples/routing/src/page/people.ts +23 -28
  46. package/dist/templates/examples/shopping-cart/src/main.ts +41 -42
  47. package/dist/templates/examples/shopping-cart/src/page/products.ts +28 -35
  48. package/dist/templates/examples/slow-warnings/src/main.ts +23 -32
  49. package/dist/templates/examples/snake/src/main.ts +135 -143
  50. package/dist/templates/examples/ssg/src/main.ts +20 -17
  51. package/dist/templates/examples/ssr/src/main.ts +20 -24
  52. package/dist/templates/examples/state-machine/src/main.ts +24 -11
  53. package/dist/templates/examples/state-machine/src/story.test.ts +2 -2
  54. package/dist/templates/examples/stopwatch/src/main.ts +42 -50
  55. package/dist/templates/examples/todo/src/main.ts +152 -160
  56. package/dist/templates/examples/ui-showcase/src/main.ts +53 -54
  57. package/dist/templates/examples/ui-showcase/src/scene.test.ts +2 -2
  58. package/dist/templates/examples/ui-showcase/src/story.test.ts +2 -2
  59. package/dist/templates/examples/ui-showcase/src/ui/init.ts +6 -7
  60. package/dist/templates/examples/ui-showcase/src/ui/update.ts +265 -336
  61. package/dist/templates/examples/view-transitions/src/main.ts +18 -17
  62. package/dist/templates/examples/weather/src/main.ts +33 -40
  63. package/dist/templates/examples/web-components/src/main.ts +14 -17
  64. package/dist/templates/examples/websocket-chat/src/main.ts +44 -50
  65. package/dist/templates/release.json +8 -8
  66. package/dist/templates/rendering/ssg/src/main.ts +20 -17
  67. package/dist/templates/rendering/ssr/src/main.ts +20 -24
  68. package/package.json +1 -1
@@ -7,7 +7,7 @@ import {
7
7
  Stream,
8
8
  pipe,
9
9
  } from 'effect'
10
- import { Command, Runtime, Subscription } from 'foldkit'
10
+ import { Runtime, Subscription, type Update } from 'foldkit'
11
11
  import { type Document, type Html, HtmlBuilder, createLazy } from 'foldkit/html'
12
12
  import { defineMessageUnion } from 'foldkit/message'
13
13
  import { evo } from 'foldkit/struct'
@@ -160,76 +160,67 @@ const prependWarning =
160
160
  (warnings: ReadonlyArray<SlowWarning>): ReadonlyArray<SlowWarning> =>
161
161
  pipe(warnings, Array.prepend(warning), Array.take(MAX_WARNING_COUNT))
162
162
 
163
- type UpdateReturn = readonly [Model, ReadonlyArray<Command.Command<Message>>]
164
-
165
163
  export const update = (model: Model, message: Message) =>
166
- Message.match<UpdateReturn>(message, {
164
+ Message.match<Update.Return<Model, Message>>(message, {
167
165
  ClickedRunUpdateWork: () => {
168
166
  burnCpu(UPDATE_WORK_MS)
169
167
 
170
- return [
171
- evo(model, {
168
+ return {
169
+ model: evo(model, {
172
170
  activeWorkload: () => 'Update',
173
171
  }),
174
- [],
175
- ]
172
+ }
176
173
  },
177
- ClickedRunViewWork: () => [
178
- evo(model, {
174
+ ClickedRunViewWork: () => ({
175
+ model: evo(model, {
179
176
  activeWorkload: () => 'View',
180
177
  }),
181
- [],
182
- ],
183
- ClickedRunPatchWork: () => [
184
- evo(model, {
178
+ }),
179
+ ClickedRunPatchWork: () => ({
180
+ model: evo(model, {
185
181
  activeWorkload: () => 'Patch',
186
182
  patchRows: () => PATCH_ROW_COUNT,
187
183
  patchRun: Number.increment,
188
184
  }),
189
- [],
190
- ],
191
- ClickedRunSubscriptionDependenciesWork: () => [
192
- evo(model, {
185
+ }),
186
+ ClickedRunSubscriptionDependenciesWork: () => ({
187
+ model: evo(model, {
193
188
  activeWorkload: () => 'SubscriptionDependencies',
194
189
  }),
195
- [],
196
- ],
197
- ClickedClearWarnings: () => [
198
- evo(model, {
190
+ }),
191
+ ClickedClearWarnings: () => ({
192
+ model: evo(model, {
199
193
  activeWorkload: () => 'Idle',
200
194
  warnings: () => [],
201
195
  }),
202
- [],
203
- ],
196
+ }),
204
197
  RecordedSlowWarning: ({ report }) => {
205
198
  const warning: SlowWarning = {
206
199
  id: model.nextWarningId,
207
200
  ...report,
208
201
  }
209
202
 
210
- return [
211
- evo(model, {
203
+ return {
204
+ model: evo(model, {
212
205
  activeWorkload: () => 'Idle',
213
206
  nextWarningId: Number.increment,
214
207
  warnings: prependWarning(warning),
215
208
  }),
216
- [],
217
- ]
209
+ }
218
210
  },
219
211
  })
220
212
 
221
213
  // INIT
222
214
 
223
- export const init: Runtime.ApplicationInit<Model, Message> = () => [
224
- {
215
+ export const init: Runtime.ApplicationInit<Model, Message> = () => ({
216
+ model: {
225
217
  activeWorkload: 'Idle',
226
218
  nextWarningId: 1,
227
219
  warnings: [],
228
220
  patchRows: 0,
229
221
  patchRun: 0,
230
222
  },
231
- [],
232
- ]
223
+ })
233
224
 
234
225
  // SUBSCRIPTION
235
226
 
@@ -7,7 +7,7 @@ import {
7
7
  Stream,
8
8
  pipe,
9
9
  } from 'effect'
10
- import { Command, Runtime, Subscription } from 'foldkit'
10
+ import { Command, Runtime, Subscription, type Update } from 'foldkit'
11
11
  import { Document, Html, HtmlBuilder } from 'foldkit/html'
12
12
  import { defineMessageUnion } from 'foldkit/message'
13
13
  import { evo } from 'foldkit/struct'
@@ -53,8 +53,8 @@ export type Message = typeof Message.Type
53
53
  export const init: Runtime.ApplicationInit<Model, Message> = () => {
54
54
  const snake = Snake.create(GAME.INITIAL_POSITION)
55
55
 
56
- return [
57
- {
56
+ return {
57
+ model: {
58
58
  snake,
59
59
  apple: { x: 15, y: 15 },
60
60
  direction: GAME.INITIAL_DIRECTION,
@@ -63,161 +63,153 @@ export const init: Runtime.ApplicationInit<Model, Message> = () => {
63
63
  points: 0,
64
64
  highScore: 0,
65
65
  },
66
- [GenerateApplePosition({ snake: snake })],
67
- ]
66
+ commands: [GenerateApplePosition({ snake: snake })],
67
+ }
68
68
  }
69
69
 
70
70
  // UPDATE
71
71
 
72
+ type UpdateReturn = Update.Return<Model, Message>
73
+
72
74
  export const update = (model: Model, message: Message) =>
73
- Message.match<readonly [Model, ReadonlyArray<Command.Command<Message>>]>(
74
- message,
75
- {
76
- PressedKey: ({ key }) =>
77
- M.value(key).pipe(
78
- M.withReturnType<
79
- readonly [Model, ReadonlyArray<Command.Command<Message>>]
80
- >(),
81
- M.whenOr(
82
- 'ArrowUp',
83
- 'ArrowDown',
84
- 'ArrowLeft',
85
- 'ArrowRight',
86
- 'w',
87
- 'a',
88
- 's',
89
- 'd',
90
- moveKey => {
91
- const nextDirection = M.value(moveKey).pipe(
92
- M.withReturnType<Direction.Direction>(),
93
- M.whenOr('ArrowUp', 'w', () => 'Up'),
94
- M.whenOr('ArrowDown', 's', () => 'Down'),
95
- M.whenOr('ArrowLeft', 'a', () => 'Left'),
96
- M.whenOr('ArrowRight', 'd', () => 'Right'),
97
- M.exhaustive,
98
- )
99
-
100
- if (model.gameState === 'Playing') {
101
- return [
102
- evo(model, {
103
- nextDirection: () => nextDirection,
104
- }),
105
- [],
106
- ]
107
- } else {
108
- return [model, []]
109
- }
110
- },
111
- ),
112
- M.when(' ', () => {
113
- const nextGameState = M.value(model.gameState).pipe(
114
- M.withReturnType<GameState>(),
115
- M.when('NotStarted', () => 'Playing'),
116
- M.when('Playing', () => 'Paused'),
117
- M.when('Paused', () => 'Playing'),
118
- M.when('GameOver', () => 'GameOver'),
75
+ Message.match<UpdateReturn>(message, {
76
+ PressedKey: ({ key }) =>
77
+ M.value(key).pipe(
78
+ M.withReturnType<UpdateReturn>(),
79
+ M.whenOr(
80
+ 'ArrowUp',
81
+ 'ArrowDown',
82
+ 'ArrowLeft',
83
+ 'ArrowRight',
84
+ 'w',
85
+ 'a',
86
+ 's',
87
+ 'd',
88
+ moveKey => {
89
+ const nextDirection = M.value(moveKey).pipe(
90
+ M.withReturnType<Direction.Direction>(),
91
+ M.whenOr('ArrowUp', 'w', () => 'Up'),
92
+ M.whenOr('ArrowDown', 's', () => 'Down'),
93
+ M.whenOr('ArrowLeft', 'a', () => 'Left'),
94
+ M.whenOr('ArrowRight', 'd', () => 'Right'),
119
95
  M.exhaustive,
120
96
  )
121
- return [
122
- evo(model, {
123
- gameState: () => nextGameState,
124
- }),
125
- [],
126
- ]
127
- }),
128
- M.when('r', () => {
129
- const nextSnake = Snake.create(GAME.INITIAL_POSITION)
130
-
131
- return [
132
- evo(model, {
133
- snake: () => nextSnake,
134
- direction: () => GAME.INITIAL_DIRECTION,
135
- nextDirection: () => GAME.INITIAL_DIRECTION,
136
- gameState: () => 'NotStarted',
137
- points: () => 0,
138
- }),
139
- [GenerateApplePosition({ snake: nextSnake })],
140
- ]
141
- }),
142
- M.orElse(() => [model, []]),
143
- ),
144
-
145
- TickedClock: () => {
146
- if (model.gameState !== 'Playing') {
147
- return [model, []]
148
- }
149
97
 
150
- const currentDirection = Direction.isOpposite(
151
- model.direction,
152
- model.nextDirection,
153
- )
154
- ? model.direction
155
- : model.nextDirection
156
-
157
- const newHead = Position.move(model.snake[0], currentDirection)
158
- const willEatApple = Position.equivalence(newHead, model.apple)
159
-
160
- const nextSnake = willEatApple
161
- ? Snake.grow(model.snake, currentDirection)
162
- : Snake.move(model.snake, currentDirection)
163
-
164
- if (Snake.hasCollision(nextSnake)) {
165
- return [
166
- evo(model, {
167
- gameState: () => 'GameOver',
168
- highScore: highScore => Math.max(model.points, highScore),
98
+ if (model.gameState === 'Playing') {
99
+ return {
100
+ model: evo(model, {
101
+ nextDirection: () => nextDirection,
102
+ }),
103
+ }
104
+ } else {
105
+ return { model }
106
+ }
107
+ },
108
+ ),
109
+ M.when(' ', () => {
110
+ const nextGameState = M.value(model.gameState).pipe(
111
+ M.withReturnType<GameState>(),
112
+ M.when('NotStarted', () => 'Playing'),
113
+ M.when('Playing', () => 'Paused'),
114
+ M.when('Paused', () => 'Playing'),
115
+ M.when('GameOver', () => 'GameOver'),
116
+ M.exhaustive,
117
+ )
118
+ return {
119
+ model: evo(model, {
120
+ gameState: () => nextGameState,
169
121
  }),
170
- [],
171
- ]
172
- }
173
-
174
- const commands = willEatApple
175
- ? [GenerateApplePosition({ snake: nextSnake })]
176
- : []
122
+ }
123
+ }),
124
+ M.when('r', () => {
125
+ const nextSnake = Snake.create(GAME.INITIAL_POSITION)
126
+
127
+ return {
128
+ model: evo(model, {
129
+ snake: () => nextSnake,
130
+ direction: () => GAME.INITIAL_DIRECTION,
131
+ nextDirection: () => GAME.INITIAL_DIRECTION,
132
+ gameState: () => 'NotStarted',
133
+ points: () => 0,
134
+ }),
135
+ commands: [GenerateApplePosition({ snake: nextSnake })],
136
+ }
137
+ }),
138
+ M.orElse(() => ({ model })),
139
+ ),
177
140
 
178
- return [
179
- evo(model, {
180
- snake: () => nextSnake,
181
- direction: () => currentDirection,
182
- points: points =>
183
- willEatApple ? points + GAME.POINTS_PER_APPLE : points,
141
+ TickedClock: () => {
142
+ if (model.gameState !== 'Playing') {
143
+ return { model }
144
+ }
145
+
146
+ const currentDirection = Direction.isOpposite(
147
+ model.direction,
148
+ model.nextDirection,
149
+ )
150
+ ? model.direction
151
+ : model.nextDirection
152
+
153
+ const newHead = Position.move(model.snake[0], currentDirection)
154
+ const willEatApple = Position.equivalence(newHead, model.apple)
155
+
156
+ const nextSnake = willEatApple
157
+ ? Snake.grow(model.snake, currentDirection)
158
+ : Snake.move(model.snake, currentDirection)
159
+
160
+ if (Snake.hasCollision(nextSnake)) {
161
+ return {
162
+ model: evo(model, {
163
+ gameState: () => 'GameOver',
164
+ highScore: highScore => Math.max(model.points, highScore),
184
165
  }),
185
- commands,
186
- ]
187
- },
188
-
189
- PausedGame: () => [
190
- evo(model, {
191
- gameState: gameState =>
192
- gameState === 'Playing' ? 'Paused' : 'Playing',
166
+ }
167
+ }
168
+
169
+ const commands = willEatApple
170
+ ? [GenerateApplePosition({ snake: nextSnake })]
171
+ : []
172
+
173
+ return {
174
+ model: evo(model, {
175
+ snake: () => nextSnake,
176
+ direction: () => currentDirection,
177
+ points: points =>
178
+ willEatApple ? points + GAME.POINTS_PER_APPLE : points,
193
179
  }),
194
- [],
195
- ],
196
-
197
- RestartedGame: () => {
198
- const startPos: Position.Position = { x: 10, y: 10 }
199
- const nextSnake = Snake.create(startPos)
200
-
201
- return [
202
- evo(model, {
203
- snake: () => nextSnake,
204
- direction: () => 'Right',
205
- nextDirection: () => 'Right',
206
- gameState: () => 'NotStarted',
207
- points: () => 0,
208
- }),
209
- [GenerateApplePosition({ snake: nextSnake })],
210
- ]
211
- },
180
+ commands,
181
+ }
182
+ },
212
183
 
213
- CompletedGenerateApplePosition: ({ position }) => [
214
- evo(model, {
215
- apple: () => position,
184
+ PausedGame: () => ({
185
+ model: evo(model, {
186
+ gameState: gameState =>
187
+ gameState === 'Playing' ? 'Paused' : 'Playing',
188
+ }),
189
+ }),
190
+
191
+ RestartedGame: () => {
192
+ const startPosition: Position.Position = { x: 10, y: 10 }
193
+ const nextSnake = Snake.create(startPosition)
194
+
195
+ return {
196
+ model: evo(model, {
197
+ snake: () => nextSnake,
198
+ direction: () => 'Right',
199
+ nextDirection: () => 'Right',
200
+ gameState: () => 'NotStarted',
201
+ points: () => 0,
216
202
  }),
217
- [],
218
- ],
203
+ commands: [GenerateApplePosition({ snake: nextSnake })],
204
+ }
219
205
  },
220
- )
206
+
207
+ CompletedGenerateApplePosition: ({ position }) => ({
208
+ model: evo(model, {
209
+ apple: () => position,
210
+ }),
211
+ }),
212
+ })
221
213
 
222
214
  // COMMAND
223
215
 
@@ -1,5 +1,5 @@
1
1
  import { Effect, Match as M, Schema as S } from 'effect'
2
- import { Command, Runtime } from 'foldkit'
2
+ import { Command, Runtime, type Update } from 'foldkit'
3
3
  import { type Document, type Html, type HtmlBuilder } from 'foldkit/html'
4
4
  import { defineMessageUnion } from 'foldkit/message'
5
5
  import { UrlRequest, load, pushUrl } from 'foldkit/navigation'
@@ -30,10 +30,9 @@ export type Message = typeof Message.Type
30
30
 
31
31
  // INIT
32
32
 
33
- export const init: Runtime.RoutingApplicationInit<Model, Message> = url => [
34
- { route: urlToAppRoute(url), count: 0 },
35
- [],
36
- ]
33
+ export const init: Runtime.RoutingApplicationInit<Model, Message> = url => ({
34
+ model: { route: urlToAppRoute(url), count: 0 },
35
+ })
37
36
 
38
37
  // COMMAND
39
38
 
@@ -53,29 +52,33 @@ const LoadExternal = Command.define('LoadExternal', {
53
52
 
54
53
  // UPDATE
55
54
 
56
- type UpdateReturn = readonly [Model, ReadonlyArray<Command.Command<Message>>]
55
+ type UpdateReturn = Update.Return<Model, Message>
57
56
  const withUpdateReturn = M.withReturnType<UpdateReturn>()
58
57
 
59
58
  export const update = (model: Model, message: Message) =>
60
59
  Message.match<UpdateReturn>(message, {
61
- ClickedIncrement: () => [evo(model, { count: count => count + 1 }), []],
60
+ ClickedIncrement: () => ({
61
+ model: evo(model, { count: count => count + 1 }),
62
+ }),
62
63
  ClickedLink: ({ request }) =>
63
64
  M.value(request).pipe(
64
65
  withUpdateReturn,
65
66
  M.tagsExhaustive({
66
- Internal: ({ url }) => [
67
+ Internal: ({ url }) => ({
67
68
  model,
68
- [NavigateInternal({ url: urlToString(url) })],
69
- ],
70
- External: ({ href }) => [model, [LoadExternal({ href })]],
69
+ commands: [NavigateInternal({ url: urlToString(url) })],
70
+ }),
71
+ External: ({ href }) => ({
72
+ model,
73
+ commands: [LoadExternal({ href })],
74
+ }),
71
75
  }),
72
76
  ),
73
- ChangedUrl: ({ url }) => [
74
- evo(model, { route: () => urlToAppRoute(url) }),
75
- [],
76
- ],
77
- CompletedNavigateInternal: () => [model, []],
78
- CompletedLoadExternal: () => [model, []],
77
+ ChangedUrl: ({ url }) => ({
78
+ model: evo(model, { route: () => urlToAppRoute(url) }),
79
+ }),
80
+ CompletedNavigateInternal: () => ({ model }),
81
+ CompletedLoadExternal: () => ({ model }),
79
82
  })
80
83
 
81
84
  // VIEW
@@ -1,5 +1,5 @@
1
1
  import { Effect, Schema as S } from 'effect'
2
- import { Command, Runtime } from 'foldkit'
2
+ import { Command, Runtime, type Update } from 'foldkit'
3
3
  import { type Document, type Html, type HtmlBuilder } from 'foldkit/html'
4
4
  import { defineMessageUnion } from 'foldkit/message'
5
5
  import { evo } from 'foldkit/struct'
@@ -39,26 +39,23 @@ export type Message = typeof Message.Type
39
39
  // UPDATE
40
40
 
41
41
  export const update = (model: Model, message: Message) =>
42
- Message.match<readonly [Model, ReadonlyArray<Command.Command<Message>>]>(
43
- message,
44
- {
45
- ClickedDecrement: () => {
46
- const nextCount = model.count - 1
47
- return [
48
- evo(model, { count: () => nextCount }),
49
- [PersistCount({ count: nextCount })],
50
- ]
51
- },
52
- ClickedIncrement: () => {
53
- const nextCount = model.count + 1
54
- return [
55
- evo(model, { count: () => nextCount }),
56
- [PersistCount({ count: nextCount })],
57
- ]
58
- },
59
- CompletedPersistCount: () => [model, []],
42
+ Message.match<Update.Return<Model, Message>>(message, {
43
+ ClickedDecrement: () => {
44
+ const nextCount = model.count - 1
45
+ return {
46
+ model: evo(model, { count: () => nextCount }),
47
+ commands: [PersistCount({ count: nextCount })],
48
+ }
60
49
  },
61
- )
50
+ ClickedIncrement: () => {
51
+ const nextCount = model.count + 1
52
+ return {
53
+ model: evo(model, { count: () => nextCount }),
54
+ commands: [PersistCount({ count: nextCount })],
55
+ }
56
+ },
57
+ CompletedPersistCount: () => ({ model }),
58
+ })
62
59
 
63
60
  // COMMAND
64
61
 
@@ -78,14 +75,13 @@ export const PersistCount = Command.define('PersistCount', {
78
75
 
79
76
  // INIT
80
77
 
81
- export const init: Runtime.ApplicationInit<Model, Message, Flags> = flags => [
82
- {
78
+ export const init: Runtime.ApplicationInit<Model, Message, Flags> = flags => ({
79
+ model: {
83
80
  count: flags.initialCount,
84
81
  renderedAt: flags.renderedAt,
85
82
  renderedOn: flags.renderedOn,
86
83
  },
87
- [],
88
- ]
84
+ })
89
85
 
90
86
  // VIEW
91
87
 
@@ -334,14 +334,13 @@ export const initialModel = Model.make({
334
334
  nextTransitionLogId: 0,
335
335
  })
336
336
 
337
- export const init: Runtime.ApplicationInit<Model, Message> = () => [
338
- initialModel,
339
- [],
340
- ]
337
+ export const init: Runtime.ApplicationInit<Model, Message> = () => ({
338
+ model: initialModel,
339
+ })
341
340
 
342
341
  // UPDATE
343
342
 
344
- type UpdateReturn = readonly [Model, ReadonlyArray<Command.Command<Message>>]
343
+ type UpdateReturn = Update.Return<Model, Message>
345
344
 
346
345
  export const TRANSITION_LOG_LIMIT = 20
347
346
 
@@ -376,8 +375,8 @@ const stepMachine =
376
375
  summary: resultToTransitionSummary(result),
377
376
  }
378
377
 
379
- return [
380
- evo(model, {
378
+ return {
379
+ model: evo(model, {
381
380
  checkout: () => nextCheckout,
382
381
  transitionLog: flow(
383
382
  Array.prepend(transitionLogEntry),
@@ -385,8 +384,8 @@ const stepMachine =
385
384
  ),
386
385
  nextTransitionLogId: Number.increment,
387
386
  }),
388
- transitionCommands,
389
- ]
387
+ commands: transitionCommands,
388
+ }
390
389
  }
391
390
 
392
391
  const foldEditionRadioGroupOutMessage = M.type<RadioGroup.OutMessage>().pipe(
@@ -410,11 +409,25 @@ const foldEditionRadioGroup = Update.foldChild({
410
409
  foldOutMessage: foldEditionRadioGroupOutMessage,
411
410
  })
412
411
 
413
- export const update = (model: Model, message: Message): UpdateReturn =>
412
+ export const update = (model: Model, message: Message) =>
414
413
  M.value(message).pipe(
415
414
  M.withReturnType<UpdateReturn>(),
416
415
  M.tag('GotEditionRadioGroupMessage', ({ message }) =>
417
416
  foldEditionRadioGroup(model, message),
418
417
  ),
419
- M.orElse(() => stepMachine(message)(model)),
418
+ M.tag(
419
+ 'ClickedContinue',
420
+ 'ClickedBack',
421
+ 'ClickedCancel',
422
+ 'ClickedPlaceOrder',
423
+ 'ClickedStartOver',
424
+ 'ToggledPaymentMethod',
425
+ 'SelectedEdition',
426
+ 'ToggledTermsAccepted',
427
+ 'UpdatedPromoCode',
428
+ 'SubmittedPromoCode',
429
+ 'SucceededPlaceOrder',
430
+ () => stepMachine(message)(model),
431
+ ),
432
+ M.exhaustive,
420
433
  )
@@ -183,8 +183,8 @@ describe('update', () => {
183
183
  Message.SelectedEdition({ isShippingRequired: index % 2 === 0 }),
184
184
  ),
185
185
  Array.reduce(initialModel, (model, message) => {
186
- const [nextModel] = update(model, message)
187
- return nextModel
186
+ const messageUpdate = update(model, message)
187
+ return messageUpdate.model
188
188
  }),
189
189
  )
190
190