create-foldkit-app 0.32.0 → 0.32.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.
- package/dist/commands/create.js +9 -7
- package/dist/templates/base/FOLDKIT.md +22 -6
- package/dist/templates/examples/api-cache/src/data.ts +14 -14
- package/dist/templates/examples/api-cache/src/main.ts +42 -39
- package/dist/templates/examples/auth/src/command.ts +3 -3
- package/dist/templates/examples/auth/src/domain/session.ts +8 -6
- package/dist/templates/examples/auth/src/main.ts +11 -11
- package/dist/templates/examples/auth/src/message.ts +3 -3
- package/dist/templates/examples/auth/src/model.ts +2 -2
- package/dist/templates/examples/auth/src/page/loggedIn/index.ts +1 -1
- package/dist/templates/examples/auth/src/page/loggedIn/view.ts +3 -3
- package/dist/templates/examples/auth/src/page/loggedOut/index.ts +1 -1
- package/dist/templates/examples/auth/src/page/loggedOut/page/login.ts +9 -17
- package/dist/templates/examples/auth/src/page/loggedOut/update.ts +4 -6
- package/dist/templates/examples/auth/src/page/loggedOut/view.ts +3 -3
- package/dist/templates/examples/auth/src/route.ts +2 -2
- package/dist/templates/examples/auth/src/update.ts +37 -40
- package/dist/templates/examples/auth/src/view.ts +6 -6
- package/dist/templates/examples/canvas-art/src/main.ts +22 -30
- package/dist/templates/examples/charting/src/command.ts +3 -3
- package/dist/templates/examples/charting/src/domain.ts +53 -48
- package/dist/templates/examples/charting/src/echarts.ts +6 -6
- package/dist/templates/examples/charting/src/githubApi.ts +33 -34
- package/dist/templates/examples/charting/src/http.ts +7 -7
- package/dist/templates/examples/charting/src/message.ts +6 -6
- package/dist/templates/examples/charting/src/model.ts +6 -6
- package/dist/templates/examples/charting/src/npmApi.ts +19 -15
- package/dist/templates/examples/charting/src/subscription.ts +5 -5
- package/dist/templates/examples/charting/src/telemetry.test.ts +18 -12
- package/dist/templates/examples/charting/src/telemetry.ts +11 -18
- package/dist/templates/examples/charting/src/update.ts +17 -23
- package/dist/templates/examples/charting/src/view/chart.ts +2 -2
- package/dist/templates/examples/counter/src/main.ts +2 -2
- package/dist/templates/examples/counters/src/counter.ts +2 -2
- package/dist/templates/examples/counters/src/main.ts +8 -8
- package/dist/templates/examples/embedding/src/main.ts +10 -7
- package/dist/templates/examples/form/src/main.ts +18 -14
- package/dist/templates/examples/generative-art/src/command.ts +7 -2
- package/dist/templates/examples/generative-art/src/message.ts +14 -14
- package/dist/templates/examples/generative-art/src/model.ts +21 -21
- package/dist/templates/examples/generative-art/src/update.ts +15 -17
- package/dist/templates/examples/interrupting-commands/src/main.ts +30 -27
- package/dist/templates/examples/job-application/src/domain/proficiencyLevel.ts +2 -2
- package/dist/templates/examples/job-application/src/domain/pronounOption.ts +2 -2
- package/dist/templates/examples/job-application/src/domain/step.ts +11 -11
- package/dist/templates/examples/job-application/src/main.ts +5 -5
- package/dist/templates/examples/job-application/src/message.ts +2 -2
- package/dist/templates/examples/job-application/src/model.ts +5 -5
- package/dist/templates/examples/job-application/src/step/attachments.ts +32 -34
- package/dist/templates/examples/job-application/src/step/coverLetter.ts +4 -4
- package/dist/templates/examples/job-application/src/step/education/education.ts +11 -14
- package/dist/templates/examples/job-application/src/step/education/entry.ts +21 -22
- package/dist/templates/examples/job-application/src/step/personalInfo.ts +44 -54
- package/dist/templates/examples/job-application/src/step/skills/entry.ts +12 -14
- package/dist/templates/examples/job-application/src/step/skills/skills.ts +11 -14
- package/dist/templates/examples/job-application/src/step/workHistory/entry.ts +43 -45
- package/dist/templates/examples/job-application/src/step/workHistory/workHistory.ts +11 -14
- package/dist/templates/examples/job-application/src/update.ts +17 -17
- package/dist/templates/examples/job-application/src/view/attachments.ts +5 -5
- package/dist/templates/examples/job-application/src/view/datePicker.ts +3 -3
- package/dist/templates/examples/job-application/src/view/field.ts +5 -5
- package/dist/templates/examples/job-application/src/view/index.ts +28 -22
- package/dist/templates/examples/kanban/src/command.ts +4 -4
- package/dist/templates/examples/kanban/src/domain/card.ts +6 -6
- package/dist/templates/examples/kanban/src/domain/column.ts +9 -9
- package/dist/templates/examples/kanban/src/main.ts +4 -4
- package/dist/templates/examples/kanban/src/message.ts +6 -6
- package/dist/templates/examples/kanban/src/model.ts +11 -9
- package/dist/templates/examples/kanban/src/update.ts +5 -5
- package/dist/templates/examples/managed-resource-layer/src/main.ts +20 -20
- package/dist/templates/examples/map/src/locations.ts +7 -7
- package/dist/templates/examples/map/src/main.ts +83 -44
- package/dist/templates/examples/map/src/mount.test.ts +127 -0
- package/dist/templates/examples/pixel-art/src/command.ts +12 -5
- package/dist/templates/examples/pixel-art/src/main.ts +4 -4
- package/dist/templates/examples/pixel-art/src/message.ts +7 -7
- package/dist/templates/examples/pixel-art/src/model.ts +34 -29
- package/dist/templates/examples/pixel-art/src/subscription.ts +2 -2
- package/dist/templates/examples/pixel-art/src/update.ts +79 -86
- package/dist/templates/examples/query-sync/src/main.ts +106 -102
- package/dist/templates/examples/route-transitions/src/data.ts +6 -6
- package/dist/templates/examples/route-transitions/src/main.ts +23 -29
- package/dist/templates/examples/route-transitions/src/route.ts +3 -3
- package/dist/templates/examples/routing/src/fileTree.ts +10 -8
- package/dist/templates/examples/routing/src/main.ts +23 -21
- package/dist/templates/examples/routing/src/page/people.ts +14 -22
- package/dist/templates/examples/routing/src/route.ts +7 -7
- package/dist/templates/examples/shopping-cart/src/main.ts +30 -31
- package/dist/templates/examples/shopping-cart/src/page/products.ts +10 -10
- package/dist/templates/examples/shopping-cart/src/route.ts +6 -4
- package/dist/templates/examples/slow-warnings/src/main.ts +35 -43
- package/dist/templates/examples/snake/src/domain/snake.ts +2 -2
- package/dist/templates/examples/snake/src/main.ts +39 -47
- package/dist/templates/examples/ssg/src/main.ts +5 -5
- package/dist/templates/examples/ssg/src/route.ts +2 -2
- package/dist/templates/examples/ssr/src/cookie.ts +3 -3
- package/dist/templates/examples/ssr/src/main.ts +10 -10
- package/dist/templates/examples/state-machine/src/main.ts +55 -53
- package/dist/templates/examples/state-machine/src/view.ts +7 -7
- package/dist/templates/examples/stopwatch/src/main.ts +10 -10
- package/dist/templates/examples/todo/src/main.ts +41 -34
- package/dist/templates/examples/ui-showcase/src/main.ts +54 -42
- package/dist/templates/examples/ui-showcase/src/scene.test.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/init.ts +1 -2
- package/dist/templates/examples/ui-showcase/src/ui/message.ts +17 -18
- package/dist/templates/examples/ui-showcase/src/ui/model.ts +47 -44
- package/dist/templates/examples/ui-showcase/src/ui/subscriptions.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/toast.ts +4 -4
- package/dist/templates/examples/ui-showcase/src/ui/update.ts +287 -298
- package/dist/templates/examples/ui-showcase/src/ui/view/animation.ts +2 -2
- package/dist/templates/examples/ui-showcase/src/ui/view/button.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/calendar.ts +4 -4
- package/dist/templates/examples/ui-showcase/src/ui/view/checkbox.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/combobox.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/datePicker.ts +4 -4
- package/dist/templates/examples/ui-showcase/src/ui/view/dialog.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/disclosure.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/dragAndDrop.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/fieldset.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/fileDrop.ts +6 -6
- package/dist/templates/examples/ui-showcase/src/ui/view/hoverIntent.ts +9 -20
- package/dist/templates/examples/ui-showcase/src/ui/view/input.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/listbox.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/menu.ts +15 -15
- package/dist/templates/examples/ui-showcase/src/ui/view/popover.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/radioGroup.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/select.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/slider.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/switch.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/tabs.ts +7 -7
- package/dist/templates/examples/ui-showcase/src/ui/view/textarea.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/toast.ts +8 -8
- package/dist/templates/examples/ui-showcase/src/ui/view/tooltip.ts +1 -1
- package/dist/templates/examples/ui-showcase/src/ui/view/virtualList.ts +7 -7
- package/dist/templates/examples/view-transitions/src/artwork.ts +7 -7
- package/dist/templates/examples/view-transitions/src/main.ts +6 -6
- package/dist/templates/examples/view-transitions/src/route.ts +3 -3
- package/dist/templates/examples/weather/src/main.ts +42 -42
- package/dist/templates/examples/weather/src/story.test.ts +5 -5
- package/dist/templates/examples/web-components/src/main.ts +16 -16
- package/dist/templates/examples/websocket-chat/src/main.ts +30 -30
- package/dist/templates/release.json +13 -13
- package/dist/templates/rendering/ssg/src/main.ts +5 -5
- package/dist/templates/rendering/ssg/src/route.ts +2 -2
- package/dist/templates/rendering/ssr/server/main.ts +15 -15
- package/dist/templates/rendering/ssr/src/cookie.ts +3 -3
- package/dist/templates/rendering/ssr/src/main.ts +10 -10
- package/dist/utils/packages.js +17 -0
- package/package.json +1 -1
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Array,
|
|
3
|
-
Duration,
|
|
4
|
-
Effect,
|
|
5
|
-
Option,
|
|
6
|
-
Schema as S,
|
|
7
|
-
String,
|
|
8
|
-
pipe,
|
|
9
|
-
} from 'effect'
|
|
1
|
+
import { Array, Duration, Effect, Option, Schema, String, pipe } from 'effect'
|
|
10
2
|
import { Command, Submodel, type Update } from 'foldkit'
|
|
11
3
|
import { Html, HtmlBuilder } from 'foldkit/html'
|
|
12
4
|
import { defineMessageUnion } from 'foldkit/message'
|
|
@@ -25,10 +17,10 @@ import {
|
|
|
25
17
|
|
|
26
18
|
// DOMAIN
|
|
27
19
|
|
|
28
|
-
const Person =
|
|
29
|
-
id:
|
|
30
|
-
name:
|
|
31
|
-
role:
|
|
20
|
+
const Person = Schema.Struct({
|
|
21
|
+
id: Schema.Number,
|
|
22
|
+
name: Schema.String,
|
|
23
|
+
role: Schema.String,
|
|
32
24
|
})
|
|
33
25
|
type Person = typeof Person.Type
|
|
34
26
|
|
|
@@ -83,13 +75,13 @@ export const findPerson = (id: number) =>
|
|
|
83
75
|
|
|
84
76
|
export const SearchResults = defineTaggedUnion({
|
|
85
77
|
Loading: {},
|
|
86
|
-
Loaded: { query:
|
|
78
|
+
Loaded: { query: Schema.String, people: Schema.Array(Person) },
|
|
87
79
|
})
|
|
88
80
|
export type SearchResults = typeof SearchResults.Type
|
|
89
81
|
|
|
90
|
-
export const Model =
|
|
91
|
-
searchInput:
|
|
92
|
-
searchHistory:
|
|
82
|
+
export const Model = Schema.Struct({
|
|
83
|
+
searchInput: Schema.String,
|
|
84
|
+
searchHistory: Schema.Array(Schema.String),
|
|
93
85
|
results: SearchResults,
|
|
94
86
|
})
|
|
95
87
|
export type Model = typeof Model.Type
|
|
@@ -97,12 +89,12 @@ export type Model = typeof Model.Type
|
|
|
97
89
|
// MESSAGE
|
|
98
90
|
|
|
99
91
|
export const Message = defineMessageUnion({
|
|
100
|
-
ChangedSearchInput: { value:
|
|
92
|
+
ChangedSearchInput: { value: Schema.String },
|
|
101
93
|
SubmittedSearch: {},
|
|
102
94
|
ChangedRoute: { route: AppRoute.People },
|
|
103
95
|
SucceededFetchPeople: {
|
|
104
|
-
query:
|
|
105
|
-
people:
|
|
96
|
+
query: Schema.String,
|
|
97
|
+
people: Schema.Array(Person),
|
|
106
98
|
},
|
|
107
99
|
CompletedPushSearchUrl: {},
|
|
108
100
|
})
|
|
@@ -128,7 +120,7 @@ export const init = (route: PeopleRoute): InitReturn => {
|
|
|
128
120
|
// COMMAND
|
|
129
121
|
|
|
130
122
|
export const PushSearchUrl = Command.define('PushSearchUrl', {
|
|
131
|
-
args: { searchText:
|
|
123
|
+
args: { searchText: Schema.Option(Schema.String) },
|
|
132
124
|
messages: [Message.CompletedPushSearchUrl],
|
|
133
125
|
execute: ({ searchText }) =>
|
|
134
126
|
pushUrl(peopleRouter({ searchText })).pipe(
|
|
@@ -137,7 +129,7 @@ export const PushSearchUrl = Command.define('PushSearchUrl', {
|
|
|
137
129
|
})
|
|
138
130
|
|
|
139
131
|
export const FetchPeople = Command.define('FetchPeople', {
|
|
140
|
-
args: { searchText:
|
|
132
|
+
args: { searchText: Schema.String },
|
|
141
133
|
messages: [Message.SucceededFetchPeople],
|
|
142
134
|
execute: ({ searchText }) =>
|
|
143
135
|
Effect.sleep(SEARCH_LATENCY).pipe(
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { Schema
|
|
1
|
+
import { Schema, pipe } from 'effect'
|
|
2
2
|
import { Route } from 'foldkit'
|
|
3
3
|
import { defineRouteUnion, int, literal, rest, slash } from 'foldkit/route'
|
|
4
4
|
|
|
5
5
|
export const AppRoute = defineRouteUnion({
|
|
6
6
|
Home: {},
|
|
7
7
|
Nested: {},
|
|
8
|
-
People: { searchText:
|
|
9
|
-
Person: { personId:
|
|
8
|
+
People: { searchText: Schema.Option(Schema.String) },
|
|
9
|
+
Person: { personId: Schema.Number },
|
|
10
10
|
FilesIndex: {},
|
|
11
|
-
Files: { path:
|
|
12
|
-
NotFound: { path:
|
|
11
|
+
Files: { path: Schema.NonEmptyArray(Schema.String) },
|
|
12
|
+
NotFound: { path: Schema.String },
|
|
13
13
|
})
|
|
14
14
|
|
|
15
15
|
export type AppRoute = typeof AppRoute.Type
|
|
@@ -29,8 +29,8 @@ export const nestedRouter = pipe(
|
|
|
29
29
|
export const peopleRouter = pipe(
|
|
30
30
|
literal('people'),
|
|
31
31
|
Route.query(
|
|
32
|
-
|
|
33
|
-
searchText:
|
|
32
|
+
Schema.Struct({
|
|
33
|
+
searchText: Schema.OptionFromOptional(Schema.String),
|
|
34
34
|
}),
|
|
35
35
|
),
|
|
36
36
|
Route.mapTo(AppRoute.People),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, Match
|
|
1
|
+
import { Effect, Match, Option, Schema } from 'effect'
|
|
2
2
|
import { Command, Runtime, Update } from 'foldkit'
|
|
3
3
|
import { Document, Html, HtmlBuilder } from 'foldkit/html'
|
|
4
4
|
import { defineMessageUnion } from 'foldkit/message'
|
|
@@ -19,11 +19,11 @@ import {
|
|
|
19
19
|
|
|
20
20
|
// MODEL
|
|
21
21
|
|
|
22
|
-
export const Model =
|
|
22
|
+
export const Model = Schema.Struct({
|
|
23
23
|
route: AppRoute,
|
|
24
24
|
cart: Cart.Cart,
|
|
25
|
-
deliveryInstructions:
|
|
26
|
-
orderPlaced:
|
|
25
|
+
deliveryInstructions: Schema.String,
|
|
26
|
+
orderPlaced: Schema.Boolean,
|
|
27
27
|
productsPage: Products.Model,
|
|
28
28
|
})
|
|
29
29
|
export type Model = typeof Model.Type
|
|
@@ -36,11 +36,11 @@ export const Message = defineMessageUnion({
|
|
|
36
36
|
ClickedLink: { request: UrlRequest },
|
|
37
37
|
ChangedUrl: { url: Url },
|
|
38
38
|
GotProductsMessage: { message: Products.Message },
|
|
39
|
-
ClickedIncrementQuantity: { itemId:
|
|
40
|
-
ClickedDecrementQuantity: { itemId:
|
|
41
|
-
ClickedRemoveCartItem: { itemId:
|
|
39
|
+
ClickedIncrementQuantity: { itemId: Schema.String },
|
|
40
|
+
ClickedDecrementQuantity: { itemId: Schema.String },
|
|
41
|
+
ClickedRemoveCartItem: { itemId: Schema.String },
|
|
42
42
|
ClickedClearCart: {},
|
|
43
|
-
UpdatedDeliveryInstructions: { value:
|
|
43
|
+
UpdatedDeliveryInstructions: { value: Schema.String },
|
|
44
44
|
ClickedPlaceOrder: {},
|
|
45
45
|
})
|
|
46
46
|
|
|
@@ -65,14 +65,14 @@ export const init: Runtime.RoutingApplicationInit<Model, Message> = (
|
|
|
65
65
|
// COMMAND
|
|
66
66
|
|
|
67
67
|
const NavigateInternal = Command.define('NavigateInternal', {
|
|
68
|
-
args: { url:
|
|
68
|
+
args: { url: Schema.String },
|
|
69
69
|
messages: [Message.CompletedNavigateInternal],
|
|
70
70
|
execute: ({ url }) =>
|
|
71
71
|
pushUrl(url).pipe(Effect.as(Message.CompletedNavigateInternal())),
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
const LoadExternal = Command.define('LoadExternal', {
|
|
75
|
-
args: { href:
|
|
75
|
+
args: { href: Schema.String },
|
|
76
76
|
messages: [Message.CompletedLoadExternal],
|
|
77
77
|
execute: ({ href }) =>
|
|
78
78
|
load(href).pipe(Effect.as(Message.CompletedLoadExternal())),
|
|
@@ -82,24 +82,23 @@ const LoadExternal = Command.define('LoadExternal', {
|
|
|
82
82
|
|
|
83
83
|
type UpdateReturn = Update.Return<Model, Message>
|
|
84
84
|
|
|
85
|
-
const foldProductsOutMessage =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
85
|
+
const foldProductsOutMessage = Products.OutMessage.match<
|
|
86
|
+
Update.Step<Model, Message>
|
|
87
|
+
>({
|
|
88
|
+
AddedToCart:
|
|
89
|
+
({ item }) =>
|
|
90
|
+
model => ({ model: evo(model, { cart: Cart.addItem(item) }) }),
|
|
91
|
+
IncrementedQuantity:
|
|
92
|
+
({ itemId }) =>
|
|
93
|
+
model => ({
|
|
94
|
+
model: evo(model, { cart: Cart.incrementQuantity(itemId) }),
|
|
95
|
+
}),
|
|
96
|
+
DecrementedQuantity:
|
|
97
|
+
({ itemId }) =>
|
|
98
|
+
model => ({
|
|
99
|
+
model: evo(model, { cart: Cart.decrementQuantity(itemId) }),
|
|
100
|
+
}),
|
|
101
|
+
})
|
|
103
102
|
|
|
104
103
|
const foldProducts = Update.foldChild({
|
|
105
104
|
update: Products.update,
|
|
@@ -271,9 +270,9 @@ const notFoundView = (path: string, h: HtmlBuilder<Message>): Html =>
|
|
|
271
270
|
)
|
|
272
271
|
|
|
273
272
|
const routeTitle = (route: Model['route']): string =>
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
273
|
+
Match.value(route).pipe(
|
|
274
|
+
Match.tag('Products', () => 'Shopping Cart'),
|
|
275
|
+
Match.orElse(({ _tag }) => `${_tag} | Shopping Cart`),
|
|
277
276
|
)
|
|
278
277
|
|
|
279
278
|
export const view = (model: Model, h: HtmlBuilder<Message>): Document => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Array, Effect, Option, Schema
|
|
1
|
+
import { Array, Effect, Option, Schema } from 'effect'
|
|
2
2
|
import { Command, Submodel, type Update } from 'foldkit'
|
|
3
3
|
import { defineMessageUnion } from 'foldkit/message'
|
|
4
4
|
import { replaceUrl } from 'foldkit/navigation'
|
|
@@ -11,9 +11,9 @@ import { cartRouter, productsRouter } from '../route'
|
|
|
11
11
|
|
|
12
12
|
// MODEL
|
|
13
13
|
|
|
14
|
-
export const Model =
|
|
15
|
-
products:
|
|
16
|
-
searchText:
|
|
14
|
+
export const Model = Schema.Struct({
|
|
15
|
+
products: Schema.Array(Item.Item),
|
|
16
|
+
searchText: Schema.String,
|
|
17
17
|
})
|
|
18
18
|
export type Model = typeof Model.Type
|
|
19
19
|
|
|
@@ -21,10 +21,10 @@ export type Model = typeof Model.Type
|
|
|
21
21
|
|
|
22
22
|
export const Message = defineMessageUnion({
|
|
23
23
|
CompletedReplaceSearchUrl: {},
|
|
24
|
-
ChangedSearchInput: { value:
|
|
24
|
+
ChangedSearchInput: { value: Schema.String },
|
|
25
25
|
ClickedAddToCart: { item: Item.Item },
|
|
26
|
-
ClickedIncrementQuantity: { itemId:
|
|
27
|
-
ClickedDecrementQuantity: { itemId:
|
|
26
|
+
ClickedIncrementQuantity: { itemId: Schema.String },
|
|
27
|
+
ClickedDecrementQuantity: { itemId: Schema.String },
|
|
28
28
|
})
|
|
29
29
|
|
|
30
30
|
export type Message = typeof Message.Type
|
|
@@ -33,8 +33,8 @@ export type Message = typeof Message.Type
|
|
|
33
33
|
|
|
34
34
|
export const OutMessage = defineMessageUnion({
|
|
35
35
|
AddedToCart: { item: Item.Item },
|
|
36
|
-
IncrementedQuantity: { itemId:
|
|
37
|
-
DecrementedQuantity: { itemId:
|
|
36
|
+
IncrementedQuantity: { itemId: Schema.String },
|
|
37
|
+
DecrementedQuantity: { itemId: Schema.String },
|
|
38
38
|
})
|
|
39
39
|
|
|
40
40
|
export type OutMessage = typeof OutMessage.Type
|
|
@@ -53,7 +53,7 @@ export const init = (products: ReadonlyArray<Item.Item>): Model => ({
|
|
|
53
53
|
// COMMAND
|
|
54
54
|
|
|
55
55
|
const ReplaceSearchUrl = Command.define('ReplaceSearchUrl', {
|
|
56
|
-
args: { url:
|
|
56
|
+
args: { url: Schema.String },
|
|
57
57
|
messages: [Message.CompletedReplaceSearchUrl],
|
|
58
58
|
execute: ({ url }) =>
|
|
59
59
|
replaceUrl(url).pipe(Effect.as(Message.CompletedReplaceSearchUrl())),
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { Schema
|
|
1
|
+
import { Schema, pipe } from 'effect'
|
|
2
2
|
import { Route } from 'foldkit'
|
|
3
3
|
import { defineRouteUnion, literal } from 'foldkit/route'
|
|
4
4
|
|
|
5
5
|
// ROUTE
|
|
6
6
|
|
|
7
7
|
export const AppRoute = defineRouteUnion({
|
|
8
|
-
Products: { searchText:
|
|
8
|
+
Products: { searchText: Schema.Option(Schema.String) },
|
|
9
9
|
Cart: {},
|
|
10
10
|
Checkout: {},
|
|
11
|
-
NotFound: { path:
|
|
11
|
+
NotFound: { path: Schema.String },
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
export type AppRoute = typeof AppRoute.Type
|
|
@@ -17,7 +17,9 @@ export type AppRoute = typeof AppRoute.Type
|
|
|
17
17
|
|
|
18
18
|
export const productsRouter = pipe(
|
|
19
19
|
Route.root,
|
|
20
|
-
Route.query(
|
|
20
|
+
Route.query(
|
|
21
|
+
Schema.Struct({ searchText: Schema.OptionFromOptional(Schema.String) }),
|
|
22
|
+
),
|
|
21
23
|
Route.mapTo(AppRoute.Products),
|
|
22
24
|
)
|
|
23
25
|
export const cartRouter = pipe(literal('cart'), Route.mapTo(AppRoute.Cart))
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Array,
|
|
3
|
-
Match as M,
|
|
4
|
-
Number,
|
|
5
|
-
Option,
|
|
6
|
-
Schema as S,
|
|
7
|
-
Stream,
|
|
8
|
-
pipe,
|
|
9
|
-
} from 'effect'
|
|
1
|
+
import { Array, Match, Number, Option, Schema, Stream, pipe } from 'effect'
|
|
10
2
|
import { Runtime, Subscription, type Update } from 'foldkit'
|
|
11
3
|
import { type Document, type Html, HtmlBuilder, createLazy } from 'foldkit/html'
|
|
12
4
|
import { defineMessageUnion } from 'foldkit/message'
|
|
@@ -19,7 +11,7 @@ const PATCH_ROW_COUNT = 4000
|
|
|
19
11
|
const MAX_WARNING_COUNT = 8
|
|
20
12
|
const SLOW_WARNING_EVENT = 'foldkit:slow-warning'
|
|
21
13
|
|
|
22
|
-
const Workload =
|
|
14
|
+
const Workload = Schema.Literals([
|
|
23
15
|
'Idle',
|
|
24
16
|
'Update',
|
|
25
17
|
'View',
|
|
@@ -30,29 +22,29 @@ type Workload = typeof Workload.Type
|
|
|
30
22
|
|
|
31
23
|
type SlowPhase = Runtime.SlowPhase
|
|
32
24
|
|
|
33
|
-
export const SlowWarningReport =
|
|
25
|
+
export const SlowWarningReport = Schema.Struct({
|
|
34
26
|
phase: Runtime.SlowPhase,
|
|
35
|
-
durationMs:
|
|
36
|
-
thresholdMs:
|
|
37
|
-
trigger:
|
|
38
|
-
details:
|
|
27
|
+
durationMs: Schema.Number,
|
|
28
|
+
thresholdMs: Schema.Number,
|
|
29
|
+
trigger: Schema.String,
|
|
30
|
+
details: Schema.String,
|
|
39
31
|
})
|
|
40
32
|
export type SlowWarningReport = typeof SlowWarningReport.Type
|
|
41
33
|
|
|
42
|
-
export const SlowWarning =
|
|
43
|
-
id:
|
|
34
|
+
export const SlowWarning = Schema.Struct({
|
|
35
|
+
id: Schema.Number,
|
|
44
36
|
...SlowWarningReport.fields,
|
|
45
37
|
})
|
|
46
38
|
export type SlowWarning = typeof SlowWarning.Type
|
|
47
39
|
|
|
48
40
|
// MODEL
|
|
49
41
|
|
|
50
|
-
export const Model =
|
|
42
|
+
export const Model = Schema.Struct({
|
|
51
43
|
activeWorkload: Workload,
|
|
52
|
-
nextWarningId:
|
|
53
|
-
warnings:
|
|
54
|
-
patchRows:
|
|
55
|
-
patchRun:
|
|
44
|
+
nextWarningId: Schema.Number,
|
|
45
|
+
warnings: Schema.Array(SlowWarning),
|
|
46
|
+
patchRows: Schema.Number,
|
|
47
|
+
patchRun: Schema.Number,
|
|
56
48
|
})
|
|
57
49
|
export type Model = typeof Model.Type
|
|
58
50
|
|
|
@@ -93,9 +85,9 @@ const maybeMessageTrigger = (message: Option.Option<Message>): string =>
|
|
|
93
85
|
const triggerForSlowContext = (
|
|
94
86
|
context: Runtime.SlowContext<Model, Message>,
|
|
95
87
|
): string =>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
88
|
+
Match.value(context).pipe(
|
|
89
|
+
Match.withReturnType<string>(),
|
|
90
|
+
Match.tagsExhaustive({
|
|
99
91
|
Update: ({ message }) => messageToTag(message),
|
|
100
92
|
View: ({ message }) => maybeMessageTrigger(message),
|
|
101
93
|
Patch: ({ message }) => maybeMessageTrigger(message),
|
|
@@ -106,9 +98,9 @@ const triggerForSlowContext = (
|
|
|
106
98
|
const detailsForSlowContext = (
|
|
107
99
|
context: Runtime.SlowContext<Model, Message>,
|
|
108
100
|
): string =>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
Match.value(context).pipe(
|
|
102
|
+
Match.withReturnType<string>(),
|
|
103
|
+
Match.tagsExhaustive({
|
|
112
104
|
Update: () =>
|
|
113
105
|
'CPU work ran inside update before Foldkit could return the next Model.',
|
|
114
106
|
View: () =>
|
|
@@ -121,13 +113,13 @@ const detailsForSlowContext = (
|
|
|
121
113
|
)
|
|
122
114
|
|
|
123
115
|
const phaseLabel = (phase: SlowPhase): string =>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
116
|
+
Match.value(phase).pipe(
|
|
117
|
+
Match.withReturnType<string>(),
|
|
118
|
+
Match.when('Update', () => 'Update'),
|
|
119
|
+
Match.when('View', () => 'View'),
|
|
120
|
+
Match.when('Patch', () => 'Patch'),
|
|
121
|
+
Match.when('SubscriptionDependencies', () => 'Subscription dependencies'),
|
|
122
|
+
Match.exhaustive,
|
|
131
123
|
)
|
|
132
124
|
|
|
133
125
|
const slowContextToReport = (
|
|
@@ -235,7 +227,7 @@ export const subscriptions = Subscription.make<Model, Message>()(entry => ({
|
|
|
235
227
|
toMessage: event =>
|
|
236
228
|
pipe(
|
|
237
229
|
event.detail,
|
|
238
|
-
|
|
230
|
+
Schema.decodeUnknownOption(SlowWarningReport),
|
|
239
231
|
Option.map(report => Message.RecordedSlowWarning({ report })),
|
|
240
232
|
),
|
|
241
233
|
}),
|
|
@@ -270,16 +262,16 @@ const secondaryButtonClass =
|
|
|
270
262
|
'inline-flex items-center justify-center rounded-md border border-zinc-300 bg-white px-3 py-2 text-sm font-semibold text-zinc-900 transition hover:bg-zinc-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-950'
|
|
271
263
|
|
|
272
264
|
const phaseAccentClass = (phase: SlowPhase): string =>
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
265
|
+
Match.value(phase).pipe(
|
|
266
|
+
Match.withReturnType<string>(),
|
|
267
|
+
Match.when('Update', () => 'border-amber-400 bg-amber-50 text-amber-950'),
|
|
268
|
+
Match.when('View', () => 'border-sky-400 bg-sky-50 text-sky-950'),
|
|
269
|
+
Match.when('Patch', () => 'border-rose-400 bg-rose-50 text-rose-950'),
|
|
270
|
+
Match.when(
|
|
279
271
|
'SubscriptionDependencies',
|
|
280
272
|
() => 'border-emerald-400 bg-emerald-50 text-emerald-950',
|
|
281
273
|
),
|
|
282
|
-
|
|
274
|
+
Match.exhaustive,
|
|
283
275
|
)
|
|
284
276
|
|
|
285
277
|
const burnCpuDuringView = (workload: Workload): void => {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Array, Schema
|
|
1
|
+
import { Array, Schema } from 'effect'
|
|
2
2
|
|
|
3
3
|
import type { Direction } from './direction'
|
|
4
4
|
import * as Position from './position'
|
|
5
5
|
|
|
6
6
|
export const INITIAL_LENGTH = 3
|
|
7
7
|
|
|
8
|
-
export const Snake =
|
|
8
|
+
export const Snake = Schema.NonEmptyArray(Position.Position)
|
|
9
9
|
export type Snake = typeof Snake.Type
|
|
10
10
|
|
|
11
11
|
export const create = (startPos: Position.Position): Snake =>
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Array,
|
|
3
|
-
Duration,
|
|
4
|
-
Effect,
|
|
5
|
-
Match as M,
|
|
6
|
-
Schema as S,
|
|
7
|
-
Stream,
|
|
8
|
-
pipe,
|
|
9
|
-
} from 'effect'
|
|
1
|
+
import { Array, Duration, Effect, Match, Schema, Stream, pipe } from 'effect'
|
|
10
2
|
import { Command, Runtime, Subscription, type Update } from 'foldkit'
|
|
11
3
|
import { Document, Html, HtmlBuilder } from 'foldkit/html'
|
|
12
4
|
import { defineMessageUnion } from 'foldkit/message'
|
|
@@ -17,7 +9,7 @@ import { Apple, Direction, Position, Snake } from './domain'
|
|
|
17
9
|
|
|
18
10
|
// MODEL
|
|
19
11
|
|
|
20
|
-
export const GameState =
|
|
12
|
+
export const GameState = Schema.Literals([
|
|
21
13
|
'NotStarted',
|
|
22
14
|
'Playing',
|
|
23
15
|
'Paused',
|
|
@@ -25,14 +17,14 @@ export const GameState = S.Literals([
|
|
|
25
17
|
])
|
|
26
18
|
export type GameState = typeof GameState.Type
|
|
27
19
|
|
|
28
|
-
export const Model =
|
|
20
|
+
export const Model = Schema.Struct({
|
|
29
21
|
snake: Snake.Snake,
|
|
30
22
|
apple: Position.Position,
|
|
31
23
|
direction: Direction.Direction,
|
|
32
24
|
nextDirection: Direction.Direction,
|
|
33
25
|
gameState: GameState,
|
|
34
|
-
points:
|
|
35
|
-
highScore:
|
|
26
|
+
points: Schema.Number,
|
|
27
|
+
highScore: Schema.Number,
|
|
36
28
|
})
|
|
37
29
|
export type Model = typeof Model.Type
|
|
38
30
|
|
|
@@ -40,7 +32,7 @@ export type Model = typeof Model.Type
|
|
|
40
32
|
|
|
41
33
|
export const Message = defineMessageUnion({
|
|
42
34
|
TickedClock: {},
|
|
43
|
-
PressedKey: { key:
|
|
35
|
+
PressedKey: { key: Schema.String },
|
|
44
36
|
PausedGame: {},
|
|
45
37
|
RestartedGame: {},
|
|
46
38
|
CompletedGenerateApplePosition: { position: Position.Position },
|
|
@@ -74,9 +66,9 @@ type UpdateReturn = Update.Return<Model, Message>
|
|
|
74
66
|
export const update = (model: Model, message: Message) =>
|
|
75
67
|
Message.match<UpdateReturn>(message, {
|
|
76
68
|
PressedKey: ({ key }) =>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
69
|
+
Match.value(key).pipe(
|
|
70
|
+
Match.withReturnType<UpdateReturn>(),
|
|
71
|
+
Match.whenOr(
|
|
80
72
|
'ArrowUp',
|
|
81
73
|
'ArrowDown',
|
|
82
74
|
'ArrowLeft',
|
|
@@ -86,13 +78,13 @@ export const update = (model: Model, message: Message) =>
|
|
|
86
78
|
's',
|
|
87
79
|
'd',
|
|
88
80
|
moveKey => {
|
|
89
|
-
const nextDirection =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
const nextDirection = Match.value(moveKey).pipe(
|
|
82
|
+
Match.withReturnType<Direction.Direction>(),
|
|
83
|
+
Match.whenOr('ArrowUp', 'w', () => 'Up'),
|
|
84
|
+
Match.whenOr('ArrowDown', 's', () => 'Down'),
|
|
85
|
+
Match.whenOr('ArrowLeft', 'a', () => 'Left'),
|
|
86
|
+
Match.whenOr('ArrowRight', 'd', () => 'Right'),
|
|
87
|
+
Match.exhaustive,
|
|
96
88
|
)
|
|
97
89
|
|
|
98
90
|
if (model.gameState === 'Playing') {
|
|
@@ -106,14 +98,14 @@ export const update = (model: Model, message: Message) =>
|
|
|
106
98
|
}
|
|
107
99
|
},
|
|
108
100
|
),
|
|
109
|
-
|
|
110
|
-
const nextGameState =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
101
|
+
Match.when(' ', () => {
|
|
102
|
+
const nextGameState = Match.value(model.gameState).pipe(
|
|
103
|
+
Match.withReturnType<GameState>(),
|
|
104
|
+
Match.when('NotStarted', () => 'Playing'),
|
|
105
|
+
Match.when('Playing', () => 'Paused'),
|
|
106
|
+
Match.when('Paused', () => 'Playing'),
|
|
107
|
+
Match.when('GameOver', () => 'GameOver'),
|
|
108
|
+
Match.exhaustive,
|
|
117
109
|
)
|
|
118
110
|
return {
|
|
119
111
|
model: evo(model, {
|
|
@@ -121,7 +113,7 @@ export const update = (model: Model, message: Message) =>
|
|
|
121
113
|
}),
|
|
122
114
|
}
|
|
123
115
|
}),
|
|
124
|
-
|
|
116
|
+
Match.when('r', () => {
|
|
125
117
|
const nextSnake = Snake.create(GAME.INITIAL_POSITION)
|
|
126
118
|
|
|
127
119
|
return {
|
|
@@ -135,7 +127,7 @@ export const update = (model: Model, message: Message) =>
|
|
|
135
127
|
commands: [GenerateApplePosition({ snake: nextSnake })],
|
|
136
128
|
}
|
|
137
129
|
}),
|
|
138
|
-
|
|
130
|
+
Match.orElse(() => ({ model })),
|
|
139
131
|
),
|
|
140
132
|
|
|
141
133
|
TickedClock: () => {
|
|
@@ -229,8 +221,8 @@ export const GenerateApplePosition = Command.define('GenerateApplePosition', {
|
|
|
229
221
|
export const subscriptions = Subscription.make<Model, Message>()(entry => ({
|
|
230
222
|
gameClock: entry(
|
|
231
223
|
{
|
|
232
|
-
isPlaying:
|
|
233
|
-
interval:
|
|
224
|
+
isPlaying: Schema.Boolean,
|
|
225
|
+
interval: Schema.Number,
|
|
234
226
|
},
|
|
235
227
|
{
|
|
236
228
|
modelToDependencies: model => ({
|
|
@@ -272,11 +264,11 @@ const cellClass = (x: number, y: number, model: Model): string => {
|
|
|
272
264
|
)
|
|
273
265
|
const isApple = Position.equivalence({ x, y }, model.apple)
|
|
274
266
|
|
|
275
|
-
return
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
267
|
+
return Match.value({ isSnakeHead, isSnakeTail, isApple }).pipe(
|
|
268
|
+
Match.when({ isSnakeHead: true }, () => 'bg-green-700'),
|
|
269
|
+
Match.when({ isSnakeTail: true }, () => 'bg-green-500'),
|
|
270
|
+
Match.when({ isApple: true }, () => 'bg-red-500'),
|
|
271
|
+
Match.orElse(() => 'bg-gray-800'),
|
|
280
272
|
)
|
|
281
273
|
}
|
|
282
274
|
|
|
@@ -294,12 +286,12 @@ const gridView = (model: Model, h: HtmlBuilder<Message>): Html =>
|
|
|
294
286
|
)
|
|
295
287
|
|
|
296
288
|
const gameStateView = (gameState: GameState): string =>
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
289
|
+
Match.value(gameState).pipe(
|
|
290
|
+
Match.when('NotStarted', () => 'Press SPACE to start'),
|
|
291
|
+
Match.when('Playing', () => 'Playing - SPACE to pause'),
|
|
292
|
+
Match.when('Paused', () => 'Paused - SPACE to continue'),
|
|
293
|
+
Match.when('GameOver', () => 'Game Over - Press R to restart'),
|
|
294
|
+
Match.exhaustive,
|
|
303
295
|
)
|
|
304
296
|
|
|
305
297
|
const instructionsView = (h: HtmlBuilder<Message>): Html =>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, Schema
|
|
1
|
+
import { Effect, Schema } from 'effect'
|
|
2
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'
|
|
@@ -10,9 +10,9 @@ import { AppRoute, aboutRouter, homeRouter, urlToAppRoute } from './route'
|
|
|
10
10
|
|
|
11
11
|
// MODEL
|
|
12
12
|
|
|
13
|
-
export const Model =
|
|
13
|
+
export const Model = Schema.Struct({
|
|
14
14
|
route: AppRoute,
|
|
15
|
-
count:
|
|
15
|
+
count: Schema.Number,
|
|
16
16
|
})
|
|
17
17
|
export type Model = typeof Model.Type
|
|
18
18
|
|
|
@@ -37,14 +37,14 @@ export const init: Runtime.RoutingApplicationInit<Model, Message> = url => ({
|
|
|
37
37
|
// COMMAND
|
|
38
38
|
|
|
39
39
|
const NavigateInternal = Command.define('NavigateInternal', {
|
|
40
|
-
args: { url:
|
|
40
|
+
args: { url: Schema.String },
|
|
41
41
|
messages: [Message.CompletedNavigateInternal],
|
|
42
42
|
execute: ({ url }) =>
|
|
43
43
|
pushUrl(url).pipe(Effect.as(Message.CompletedNavigateInternal())),
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
const LoadExternal = Command.define('LoadExternal', {
|
|
47
|
-
args: { href:
|
|
47
|
+
args: { href: Schema.String },
|
|
48
48
|
messages: [Message.CompletedLoadExternal],
|
|
49
49
|
execute: ({ href }) =>
|
|
50
50
|
load(href).pipe(Effect.as(Message.CompletedLoadExternal())),
|