create-foldkit-app 0.30.1 → 0.31.0

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 (59) hide show
  1. package/dist/templates/base/FOLDKIT.md +3 -1
  2. package/dist/templates/examples/auth/src/main.ts +3 -3
  3. package/dist/templates/examples/auth/src/page/loggedIn/model.ts +2 -2
  4. package/dist/templates/examples/auth/src/page/loggedOut/model.ts +2 -2
  5. package/dist/templates/examples/auth/src/route.ts +19 -29
  6. package/dist/templates/examples/auth/src/scene.test.ts +2 -2
  7. package/dist/templates/examples/auth/src/update.ts +3 -4
  8. package/dist/templates/examples/form/src/main.ts +12 -21
  9. package/dist/templates/examples/interrupting-commands/src/scene.test.ts +2 -2
  10. package/dist/templates/examples/interrupting-commands/src/story.test.ts +7 -7
  11. package/dist/templates/examples/job-application/src/main.ts +2 -2
  12. package/dist/templates/examples/job-application/src/model.ts +7 -12
  13. package/dist/templates/examples/job-application/src/scene.test.ts +5 -11
  14. package/dist/templates/examples/job-application/src/story.test.ts +4 -4
  15. package/dist/templates/examples/job-application/src/update.ts +8 -4
  16. package/dist/templates/examples/job-application/src/view/review.ts +92 -94
  17. package/dist/templates/examples/managed-resource-layer/src/main.ts +23 -27
  18. package/dist/templates/examples/managed-resource-layer/src/scene.test.ts +3 -4
  19. package/dist/templates/examples/managed-resource-layer/src/story.test.ts +11 -20
  20. package/dist/templates/examples/map/src/main.fixtures.ts +2 -2
  21. package/dist/templates/examples/map/src/main.ts +23 -21
  22. package/dist/templates/examples/map/src/scene.test.ts +4 -2
  23. package/dist/templates/examples/map/src/story.test.ts +9 -10
  24. package/dist/templates/examples/query-sync/src/main.ts +30 -29
  25. package/dist/templates/examples/query-sync/src/scene.test.ts +8 -15
  26. package/dist/templates/examples/query-sync/src/story.test.ts +21 -8
  27. package/dist/templates/examples/route-transitions/src/main.ts +15 -25
  28. package/dist/templates/examples/route-transitions/src/route.ts +21 -26
  29. package/dist/templates/examples/route-transitions/src/story.test.ts +20 -26
  30. package/dist/templates/examples/routing/src/fileTree.ts +3 -3
  31. package/dist/templates/examples/routing/src/main.ts +2 -12
  32. package/dist/templates/examples/routing/src/page/people.ts +18 -16
  33. package/dist/templates/examples/routing/src/route.ts +18 -32
  34. package/dist/templates/examples/routing/src/scene.test.ts +12 -20
  35. package/dist/templates/examples/routing/src/story.test.ts +9 -6
  36. package/dist/templates/examples/shopping-cart/src/route.ts +11 -19
  37. package/dist/templates/examples/shopping-cart/src/scene.test.ts +10 -8
  38. package/dist/templates/examples/shopping-cart/src/story.test.ts +2 -2
  39. package/dist/templates/examples/ssg/src/route.ts +9 -8
  40. package/dist/templates/examples/state-machine/src/main.ts +68 -63
  41. package/dist/templates/examples/state-machine/src/story.test.ts +8 -7
  42. package/dist/templates/examples/state-machine/src/view.ts +7 -13
  43. package/dist/templates/examples/todo/src/main.ts +11 -17
  44. package/dist/templates/examples/todo/src/scene.test.ts +2 -2
  45. package/dist/templates/examples/todo/src/story.test.ts +11 -12
  46. package/dist/templates/examples/ui-showcase/src/main.ts +64 -85
  47. package/dist/templates/examples/ui-showcase/src/scene.test.ts +14 -30
  48. package/dist/templates/examples/ui-showcase/src/story.test.ts +2 -2
  49. package/dist/templates/examples/ui-showcase/src/ui/message.ts +0 -70
  50. package/dist/templates/examples/view-transitions/src/main.ts +1 -1
  51. package/dist/templates/examples/view-transitions/src/route.ts +9 -12
  52. package/dist/templates/examples/view-transitions/src/scene.test.ts +5 -14
  53. package/dist/templates/examples/websocket-chat/src/main.ts +29 -37
  54. package/dist/templates/examples/websocket-chat/src/scene.test.ts +18 -10
  55. package/dist/templates/examples/websocket-chat/src/story.test.ts +19 -15
  56. package/dist/templates/release.json +13 -13
  57. package/dist/templates/rendering/ssg/src/route.ts +9 -8
  58. package/dist/templates/rendering/ssg/src/scene.test.ts +2 -2
  59. package/package.json +1 -1
@@ -15,8 +15,8 @@ import { Command, Route, Runtime, Update } from 'foldkit'
15
15
  import { Document, Html, HtmlBuilder, childAttributes } from 'foldkit/html'
16
16
  import { defineMessageUnion } from 'foldkit/message'
17
17
  import { UrlRequest, load, pushUrl, replaceUrl } from 'foldkit/navigation'
18
- import { r } from 'foldkit/route'
19
- import { ts } from 'foldkit/schema'
18
+ import { defineRouteUnion } from 'foldkit/route'
19
+ import { defineTaggedUnion } from 'foldkit/schema'
20
20
  import { evo } from 'foldkit/struct'
21
21
  import { Url, toString as urlToString } from 'foldkit/url'
22
22
 
@@ -30,11 +30,12 @@ const Period = S.Literals(['Triassic', 'Jurassic', 'Cretaceous'])
30
30
  const SortColumn = S.Literals(['Name', 'Period', 'Diet', 'Length', 'Weight'])
31
31
  type SortColumn = typeof SortColumn.Type
32
32
 
33
- export const Unsorted = ts('Unsorted')
34
- export const Ascending = ts('Ascending', { column: SortColumn })
35
- export const Descending = ts('Descending', { column: SortColumn })
36
- const Sorting = S.Union([Unsorted, Ascending, Descending])
37
- type Sorting = typeof Sorting.Type
33
+ export const Sorting = defineTaggedUnion({
34
+ Unsorted: {},
35
+ Ascending: { column: SortColumn },
36
+ Descending: { column: SortColumn },
37
+ })
38
+ export type Sorting = typeof Sorting.Type
38
39
 
39
40
  const dietFilterItems: ReadonlyArray<string> = ['', ...Diet.literals]
40
41
  const periodFilterItems: ReadonlyArray<string> = ['', ...Period.literals]
@@ -71,7 +72,7 @@ const sortingFromParam = (() => {
71
72
  SchemaTransformation.transform({
72
73
  decode: (maybeRaw: Option.Option<string>): Sorting =>
73
74
  Option.match(maybeRaw, {
74
- onNone: () => Unsorted(),
75
+ onNone: () => Sorting.Unsorted(),
75
76
  onSome: value => {
76
77
  const parts = String.split(value, SORT_PARAM_SEPARATOR)
77
78
 
@@ -90,12 +91,12 @@ const sortingFromParam = (() => {
90
91
  }),
91
92
  Option.map(({ column, direction }) =>
92
93
  M.value(direction).pipe(
93
- M.when('Ascending', () => Ascending({ column })),
94
- M.when('Descending', () => Descending({ column })),
94
+ M.when('Ascending', () => Sorting.Ascending({ column })),
95
+ M.when('Descending', () => Sorting.Descending({ column })),
95
96
  M.exhaustive,
96
97
  ),
97
98
  ),
98
- Option.getOrElse(() => Unsorted()),
99
+ Option.getOrElse(() => Sorting.Unsorted()),
99
100
  )
100
101
  },
101
102
  }),
@@ -115,19 +116,19 @@ const sortingFromParam = (() => {
115
116
  )
116
117
  })()
117
118
 
118
- export const BrowseRoute = r('Browse', {
119
- search: S.Option(S.String),
120
- sorting: Sorting,
121
- diet: S.Option(Diet),
122
- period: S.Option(Period),
119
+ export const AppRoute = defineRouteUnion({
120
+ Browse: {
121
+ search: S.Option(S.String),
122
+ sorting: Sorting,
123
+ diet: S.Option(Diet),
124
+ period: S.Option(Period),
125
+ },
126
+ NotFound: { path: S.String },
123
127
  })
124
128
 
125
- export const NotFoundRoute = r('NotFound', { path: S.String })
126
-
127
- const AppRoute = S.Union([BrowseRoute, NotFoundRoute])
128
- type AppRoute = typeof AppRoute.Type
129
+ export type AppRoute = typeof AppRoute.Type
129
130
 
130
- const browseRouter = pipe(
131
+ export const browseRouter = pipe(
131
132
  Route.root,
132
133
  Route.query(
133
134
  S.Struct({
@@ -137,11 +138,11 @@ const browseRouter = pipe(
137
138
  period: optionFromValidParam(Period),
138
139
  }),
139
140
  ),
140
- Route.mapTo(BrowseRoute),
141
+ Route.mapTo(AppRoute.Browse),
141
142
  )
142
143
 
143
144
  const routeParser = Route.oneOf(browseRouter)
144
- const urlToAppRoute = Route.parseUrlWithFallback(routeParser, NotFoundRoute)
145
+ const urlToAppRoute = Route.parseUrlWithFallback(routeParser, AppRoute.NotFound)
145
146
 
146
147
  // MODEL
147
148
 
@@ -170,11 +171,11 @@ export type Message = typeof Message.Type
170
171
 
171
172
  // INIT
172
173
 
173
- type BrowseFields = Omit<typeof BrowseRoute.Type, '_tag'>
174
+ type BrowseFields = Omit<typeof AppRoute.Browse.Type, '_tag'>
174
175
 
175
176
  const emptyBrowseFields: BrowseFields = {
176
177
  search: Option.none(),
177
- sorting: Unsorted(),
178
+ sorting: Sorting.Unsorted(),
178
179
  diet: Option.none(),
179
180
  period: Option.none(),
180
181
  }
@@ -219,9 +220,9 @@ const nextSorting = (sorting: Sorting, column: SortColumn): Sorting =>
219
220
  pipe(
220
221
  columnSortDirection(sorting, column),
221
222
  M.value,
222
- M.when('Unsorted', () => Ascending({ column })),
223
- M.when('Ascending', () => Descending({ column })),
224
- M.when('Descending', () => Unsorted()),
223
+ M.when('Unsorted', () => Sorting.Ascending({ column })),
224
+ M.when('Ascending', () => Sorting.Descending({ column })),
225
+ M.when('Descending', () => Sorting.Unsorted()),
225
226
  M.exhaustive,
226
227
  )
227
228
 
@@ -649,7 +650,7 @@ const periodLabel = (item: string): string =>
649
650
 
650
651
  const browseView = (
651
652
  model: Model,
652
- route: typeof BrowseRoute.Type,
653
+ route: typeof AppRoute.Browse.Type,
653
654
  h: HtmlBuilder<Message>,
654
655
  ): Html => {
655
656
  const fields = routeToBrowseFields(route)
@@ -4,19 +4,12 @@ import { describe, test } from 'vitest'
4
4
 
5
5
  import { Listbox } from '@foldkit/ui'
6
6
 
7
- import {
8
- BrowseRoute,
9
- type Model,
10
- NotFoundRoute,
11
- Unsorted,
12
- update,
13
- view,
14
- } from './main'
7
+ import { AppRoute, type Model, Sorting, update, view } from './main'
15
8
 
16
9
  const browseModel: Model = {
17
- route: BrowseRoute({
10
+ route: AppRoute.Browse({
18
11
  search: Option.none(),
19
- sorting: Unsorted(),
12
+ sorting: Sorting.Unsorted(),
20
13
  diet: Option.none(),
21
14
  period: Option.none(),
22
15
  }),
@@ -47,9 +40,9 @@ describe('view', () => {
47
40
  { update, view },
48
41
  given({
49
42
  ...browseModel,
50
- route: BrowseRoute({
43
+ route: AppRoute.Browse({
51
44
  search: Option.some('Tyranno'),
52
- sorting: Unsorted(),
45
+ sorting: Sorting.Unsorted(),
53
46
  diet: Option.none(),
54
47
  period: Option.none(),
55
48
  }),
@@ -63,9 +56,9 @@ describe('view', () => {
63
56
  { update, view },
64
57
  given({
65
58
  ...browseModel,
66
- route: BrowseRoute({
59
+ route: AppRoute.Browse({
67
60
  search: Option.some('zzzNoMatch'),
68
- sorting: Unsorted(),
61
+ sorting: Sorting.Unsorted(),
69
62
  diet: Option.none(),
70
63
  period: Option.none(),
71
64
  }),
@@ -79,7 +72,7 @@ describe('view', () => {
79
72
  { update, view },
80
73
  given({
81
74
  ...browseModel,
82
- route: NotFoundRoute({ path: '/oops' }),
75
+ route: AppRoute.NotFound({ path: '/oops' }),
83
76
  }),
84
77
  expect(role('heading', { name: '404 — Page Not Found' })).toExist(),
85
78
  expect(text('The path "/oops" was not found.')).toExist(),
@@ -7,19 +7,19 @@ import { Listbox } from '@foldkit/ui'
7
7
  import { Message as ListboxMessage } from '@foldkit/ui/listbox'
8
8
 
9
9
  import {
10
- Ascending,
11
- BrowseRoute,
10
+ AppRoute,
12
11
  Message,
13
12
  type Model,
14
13
  ReplaceFilters,
15
- Unsorted,
14
+ Sorting,
15
+ browseRouter,
16
16
  update,
17
17
  } from './main'
18
18
 
19
19
  const browseModel: Model = {
20
- route: BrowseRoute({
20
+ route: AppRoute.Browse({
21
21
  search: Option.none(),
22
- sorting: Unsorted(),
22
+ sorting: Sorting.Unsorted(),
23
23
  diet: Option.none(),
24
24
  period: Option.none(),
25
25
  }),
@@ -33,6 +33,19 @@ const urlOrThrow = (raw: string) =>
33
33
  () => new Error(`Failed to parse url: ${raw}`),
34
34
  )
35
35
 
36
+ describe('routing', () => {
37
+ test('prints sorting in the same format the parser accepts', () => {
38
+ const path = browseRouter({
39
+ search: Option.none(),
40
+ sorting: Sorting.Ascending({ column: 'Length' }),
41
+ diet: Option.none(),
42
+ period: Option.none(),
43
+ })
44
+
45
+ expect(decodeURIComponent(path)).toBe('/?sorting=Length:Ascending')
46
+ })
47
+ })
48
+
36
49
  describe('update', () => {
37
50
  describe('ChangedUrl', () => {
38
51
  test('parses search, sorting, diet, and period from the URL', () => {
@@ -52,7 +65,7 @@ describe('update', () => {
52
65
  }
53
66
  expect(model.route.search).toStrictEqual(Option.some('raptor'))
54
67
  expect(model.route.sorting).toStrictEqual(
55
- Ascending({ column: 'Length' }),
68
+ Sorting.Ascending({ column: 'Length' }),
56
69
  )
57
70
  expect(model.route.diet).toStrictEqual(Option.some('Carnivore'))
58
71
  expect(model.route.period).toStrictEqual(Option.some('Cretaceous'))
@@ -92,9 +105,9 @@ describe('update', () => {
92
105
  update,
93
106
  given({
94
107
  ...browseModel,
95
- route: BrowseRoute({
108
+ route: AppRoute.Browse({
96
109
  search: Option.some('foo'),
97
- sorting: Unsorted(),
110
+ sorting: Sorting.Unsorted(),
98
111
  diet: Option.none(),
99
112
  period: Option.none(),
100
113
  }),
@@ -12,7 +12,7 @@ import { Document, Html, HtmlBuilder } from 'foldkit/html'
12
12
  import { defineMessageUnion } from 'foldkit/message'
13
13
  import { UrlRequest, load, pushUrl } from 'foldkit/navigation'
14
14
  import { Transition } from 'foldkit/route'
15
- import { ts } from 'foldkit/schema'
15
+ import { defineTaggedUnion } from 'foldkit/schema'
16
16
  import { evo } from 'foldkit/struct'
17
17
  import { Url, toString as urlToString } from 'foldkit/url'
18
18
 
@@ -26,14 +26,7 @@ import {
26
26
  urlToAppRoute,
27
27
  } from './route'
28
28
 
29
- export {
30
- AppRoute,
31
- GalleryRoute,
32
- HomeRoute,
33
- NotFoundRoute,
34
- PaintingRoute,
35
- StudioRoute,
36
- } from './route'
29
+ export { AppRoute } from './route'
37
30
 
38
31
  const CATALOG_LATENCY = Duration.millis(600)
39
32
  const PAINTING_LATENCY = Duration.millis(400)
@@ -45,15 +38,11 @@ const MAX_LOGGED_TRANSITIONS = 20
45
38
  export const CatalogStatus = S.Literals(['Idle', 'Loading', 'Ready'])
46
39
  export type CatalogStatus = typeof CatalogStatus.Type
47
40
 
48
- export const PaintingIdle = ts('PaintingIdle')
49
- export const PaintingLoading = ts('PaintingLoading', { paintingId: S.Number })
50
- export const PaintingReady = ts('PaintingReady', { paintingId: S.Number })
51
-
52
- export const PaintingStatus = S.Union([
53
- PaintingIdle,
54
- PaintingLoading,
55
- PaintingReady,
56
- ])
41
+ export const PaintingStatus = defineTaggedUnion({
42
+ Idle: {},
43
+ Loading: { paintingId: S.Number },
44
+ Ready: { paintingId: S.Number },
45
+ })
57
46
  export type PaintingStatus = typeof PaintingStatus.Type
58
47
 
59
48
  export const LoggedTransition = S.Struct({
@@ -180,7 +169,7 @@ const loadPaintingOnEntry =
180
169
  onNone: () => ({ model }),
181
170
  onSome: ({ paintingId }) => ({
182
171
  model: evo(model, {
183
- paintingStatus: () => PaintingLoading({ paintingId }),
172
+ paintingStatus: () => PaintingStatus.Loading({ paintingId }),
184
173
  }),
185
174
  commands: [LoadPainting({ paintingId })],
186
175
  }),
@@ -197,7 +186,9 @@ const reloadPaintingOnIdChange =
197
186
  : {
198
187
  model: evo(model, {
199
188
  paintingStatus: () =>
200
- PaintingLoading({ paintingId: nextRoute.paintingId }),
189
+ PaintingStatus.Loading({
190
+ paintingId: nextRoute.paintingId,
191
+ }),
201
192
  }),
202
193
  commands: [LoadPainting({ paintingId: nextRoute.paintingId })],
203
194
  },
@@ -260,11 +251,11 @@ export const update = (model: Model, message: Message) =>
260
251
  }),
261
252
 
262
253
  SucceededLoadPainting: ({ paintingId }) =>
263
- model.paintingStatus._tag === 'PaintingLoading' &&
254
+ model.paintingStatus._tag === 'Loading' &&
264
255
  model.paintingStatus.paintingId === paintingId
265
256
  ? {
266
257
  model: evo(model, {
267
- paintingStatus: () => PaintingReady({ paintingId }),
258
+ paintingStatus: () => PaintingStatus.Ready({ paintingId }),
268
259
  }),
269
260
  }
270
261
  : { model },
@@ -288,7 +279,7 @@ export const init: Runtime.RoutingApplicationInit<Model, Message> = (
288
279
  route,
289
280
  transitionLog: [],
290
281
  catalogStatus: 'Idle',
291
- paintingStatus: PaintingIdle(),
282
+ paintingStatus: PaintingStatus.Idle(),
292
283
  studioDraft: '',
293
284
  maybeSavedDraft: Option.none(),
294
285
  })
@@ -544,8 +535,7 @@ const foundPaintingView = (
544
535
  h: HtmlBuilder<Message>,
545
536
  ): Html => {
546
537
  const isPaintingReady =
547
- paintingStatus._tag === 'PaintingReady' &&
548
- paintingStatus.paintingId === painting.id
538
+ paintingStatus._tag === 'Ready' && paintingStatus.paintingId === painting.id
549
539
 
550
540
  return h.div(
551
541
  [],
@@ -1,39 +1,34 @@
1
1
  import { Schema as S, pipe } from 'effect'
2
2
  import { Route } from 'foldkit'
3
- import { int, literal, r, slash } from 'foldkit/route'
4
-
5
- export const HomeRoute = r('Home')
6
- export const GalleryRoute = r('Gallery')
7
- export const PaintingRoute = r('Painting', { paintingId: S.Number })
8
- export const StudioRoute = r('Studio')
9
- export const NotFoundRoute = r('NotFound', { path: S.String })
10
-
11
- export const AppRoute = S.Union([
12
- HomeRoute,
13
- GalleryRoute,
14
- PaintingRoute,
15
- StudioRoute,
16
- NotFoundRoute,
17
- ])
18
-
19
- export type HomeRoute = typeof HomeRoute.Type
20
- export type GalleryRoute = typeof GalleryRoute.Type
21
- export type PaintingRoute = typeof PaintingRoute.Type
22
- export type StudioRoute = typeof StudioRoute.Type
23
- export type NotFoundRoute = typeof NotFoundRoute.Type
3
+ import { defineRouteUnion, int, literal, slash } from 'foldkit/route'
4
+
5
+ export const AppRoute = defineRouteUnion({
6
+ Home: {},
7
+ Gallery: {},
8
+ Painting: { paintingId: S.Number },
9
+ Studio: {},
10
+ NotFound: { path: S.String },
11
+ })
12
+
24
13
  export type AppRoute = typeof AppRoute.Type
25
14
 
26
- export const homeRouter = pipe(Route.root, Route.mapTo(HomeRoute))
15
+ export const homeRouter = pipe(Route.root, Route.mapTo(AppRoute.Home))
27
16
 
28
- export const galleryRouter = pipe(literal('gallery'), Route.mapTo(GalleryRoute))
17
+ export const galleryRouter = pipe(
18
+ literal('gallery'),
19
+ Route.mapTo(AppRoute.Gallery),
20
+ )
29
21
 
30
22
  export const paintingRouter = pipe(
31
23
  literal('gallery'),
32
24
  slash(int('paintingId')),
33
- Route.mapTo(PaintingRoute),
25
+ Route.mapTo(AppRoute.Painting),
34
26
  )
35
27
 
36
- export const studioRouter = pipe(literal('studio'), Route.mapTo(StudioRoute))
28
+ export const studioRouter = pipe(
29
+ literal('studio'),
30
+ Route.mapTo(AppRoute.Studio),
31
+ )
37
32
 
38
33
  const routeParser = Route.oneOf(
39
34
  paintingRouter,
@@ -44,5 +39,5 @@ const routeParser = Route.oneOf(
44
39
 
45
40
  export const urlToAppRoute = Route.parseUrlWithFallback(
46
41
  routeParser,
47
- NotFoundRoute,
42
+ AppRoute.NotFound,
48
43
  )
@@ -6,18 +6,12 @@ import { describe, expect, test } from 'vitest'
6
6
 
7
7
  import {
8
8
  AppRoute,
9
- GalleryRoute,
10
- HomeRoute,
11
9
  LoadCatalog,
12
10
  LoadPainting,
13
11
  Message,
14
12
  Model,
15
- PaintingIdle,
16
- PaintingLoading,
17
- PaintingReady,
18
- PaintingRoute,
13
+ PaintingStatus,
19
14
  SaveDraft,
20
- StudioRoute,
21
15
  init,
22
16
  update,
23
17
  } from './main'
@@ -33,7 +27,7 @@ const modelOn = (route: AppRoute): Model =>
33
27
  route,
34
28
  transitionLog: [],
35
29
  catalogStatus: 'Idle',
36
- paintingStatus: PaintingIdle(),
30
+ paintingStatus: PaintingStatus.Idle(),
37
31
  studioDraft: '',
38
32
  maybeSavedDraft: Option.none(),
39
33
  })
@@ -67,7 +61,7 @@ describe('update', () => {
67
61
  test('entering the gallery loads the catalog and logs the transition', () => {
68
62
  story(
69
63
  update,
70
- given(modelOn(HomeRoute())),
64
+ given(modelOn(AppRoute.Home())),
71
65
  message(
72
66
  Message.ChangedUrl({ url: urlOrThrow('http://localhost/gallery') }),
73
67
  ),
@@ -87,7 +81,7 @@ describe('update', () => {
87
81
  test('re-entering the gallery while a catalog load is in flight does not fire another', () => {
88
82
  story(
89
83
  update,
90
- given(evo(modelOn(HomeRoute()), { catalogStatus: () => 'Loading' })),
84
+ given(evo(modelOn(AppRoute.Home()), { catalogStatus: () => 'Loading' })),
91
85
  message(
92
86
  Message.ChangedUrl({ url: urlOrThrow('http://localhost/gallery') }),
93
87
  ),
@@ -101,13 +95,13 @@ describe('update', () => {
101
95
  test('entering a painting loads it with the payload from the route', () => {
102
96
  story(
103
97
  update,
104
- given(modelOn(GalleryRoute())),
98
+ given(modelOn(AppRoute.Gallery())),
105
99
  message(
106
100
  Message.ChangedUrl({ url: urlOrThrow('http://localhost/gallery/3') }),
107
101
  ),
108
102
  model(model => {
109
103
  expect(model.paintingStatus).toStrictEqual(
110
- PaintingLoading({ paintingId: 3 }),
104
+ PaintingStatus.Loading({ paintingId: 3 }),
111
105
  )
112
106
  }),
113
107
  Command.expectHas(LoadPainting),
@@ -117,7 +111,7 @@ describe('update', () => {
117
111
  ),
118
112
  model(model => {
119
113
  expect(model.paintingStatus).toStrictEqual(
120
- PaintingReady({ paintingId: 3 }),
114
+ PaintingStatus.Ready({ paintingId: 3 }),
121
115
  )
122
116
  }),
123
117
  )
@@ -127,8 +121,8 @@ describe('update', () => {
127
121
  story(
128
122
  update,
129
123
  given(
130
- evo(modelOn(PaintingRoute({ paintingId: 1 })), {
131
- paintingStatus: () => PaintingReady({ paintingId: 1 }),
124
+ evo(modelOn(AppRoute.Painting({ paintingId: 1 })), {
125
+ paintingStatus: () => PaintingStatus.Ready({ paintingId: 1 }),
132
126
  }),
133
127
  ),
134
128
  message(
@@ -136,11 +130,11 @@ describe('update', () => {
136
130
  ),
137
131
  model(model => {
138
132
  expect(model.paintingStatus).toStrictEqual(
139
- PaintingLoading({ paintingId: 2 }),
133
+ PaintingStatus.Loading({ paintingId: 2 }),
140
134
  )
141
135
  expect(
142
136
  Array.map(model.transitionLog, entry => entry.maybePreviousRoute),
143
- ).toStrictEqual([Option.some(PaintingRoute({ paintingId: 1 }))])
137
+ ).toStrictEqual([Option.some(AppRoute.Painting({ paintingId: 1 }))])
144
138
  }),
145
139
  Command.expectHas(LoadPainting),
146
140
  Command.resolve(
@@ -149,7 +143,7 @@ describe('update', () => {
149
143
  ),
150
144
  model(model => {
151
145
  expect(model.paintingStatus).toStrictEqual(
152
- PaintingReady({ paintingId: 2 }),
146
+ PaintingStatus.Ready({ paintingId: 2 }),
153
147
  )
154
148
  }),
155
149
  )
@@ -159,8 +153,8 @@ describe('update', () => {
159
153
  story(
160
154
  update,
161
155
  given(
162
- evo(modelOn(PaintingRoute({ paintingId: 1 })), {
163
- paintingStatus: () => PaintingReady({ paintingId: 1 }),
156
+ evo(modelOn(AppRoute.Painting({ paintingId: 1 })), {
157
+ paintingStatus: () => PaintingStatus.Ready({ paintingId: 1 }),
164
158
  }),
165
159
  ),
166
160
  message(
@@ -169,7 +163,7 @@ describe('update', () => {
169
163
  Command.expectNone(),
170
164
  model(model => {
171
165
  expect(model.paintingStatus).toStrictEqual(
172
- PaintingReady({ paintingId: 1 }),
166
+ PaintingStatus.Ready({ paintingId: 1 }),
173
167
  )
174
168
  }),
175
169
  )
@@ -179,14 +173,14 @@ describe('update', () => {
179
173
  story(
180
174
  update,
181
175
  given(
182
- evo(modelOn(PaintingRoute({ paintingId: 2 })), {
183
- paintingStatus: () => PaintingLoading({ paintingId: 2 }),
176
+ evo(modelOn(AppRoute.Painting({ paintingId: 2 })), {
177
+ paintingStatus: () => PaintingStatus.Loading({ paintingId: 2 }),
184
178
  }),
185
179
  ),
186
180
  message(Message.SucceededLoadPainting({ paintingId: 1 })),
187
181
  model(model => {
188
182
  expect(model.paintingStatus).toStrictEqual(
189
- PaintingLoading({ paintingId: 2 }),
183
+ PaintingStatus.Loading({ paintingId: 2 }),
190
184
  )
191
185
  }),
192
186
  )
@@ -196,7 +190,7 @@ describe('update', () => {
196
190
  story(
197
191
  update,
198
192
  given(
199
- evo(modelOn(StudioRoute()), {
193
+ evo(modelOn(AppRoute.Studio()), {
200
194
  studioDraft: () => 'half-finished thought',
201
195
  }),
202
196
  ),
@@ -217,7 +211,7 @@ describe('update', () => {
217
211
  test('leaving the studio with an empty draft saves nothing', () => {
218
212
  story(
219
213
  update,
220
- given(modelOn(StudioRoute())),
214
+ given(modelOn(AppRoute.Studio())),
221
215
  message(Message.ChangedUrl({ url: urlOrThrow('http://localhost/') })),
222
216
  Command.expectNone(),
223
217
  )
@@ -1,5 +1,5 @@
1
1
  import { Array, Match as M, Option, Schema as S, pipe } from 'effect'
2
- import { ts } from 'foldkit/schema'
2
+ import { taggedStruct } from 'foldkit/schema'
3
3
 
4
4
  export type File = typeof File.Type
5
5
  export type Directory = Readonly<{
@@ -9,12 +9,12 @@ export type Directory = Readonly<{
9
9
  }>
10
10
  export type FileTreeEntry = File | Directory
11
11
 
12
- export const File = ts('File', {
12
+ export const File = taggedStruct('File', {
13
13
  name: S.String,
14
14
  sizeInBytes: S.Number,
15
15
  })
16
16
 
17
- export const Directory = ts('Directory', {
17
+ export const Directory = taggedStruct('Directory', {
18
18
  name: S.String,
19
19
  entries: S.Array(S.suspend((): S.Codec<FileTreeEntry> => FileTreeEntry)),
20
20
  })
@@ -16,7 +16,6 @@ import {
16
16
  import { People } from './page'
17
17
  import {
18
18
  AppRoute,
19
- PeopleRoute,
20
19
  filesIndexRouter,
21
20
  filesRouter,
22
21
  homeRouter,
@@ -25,16 +24,7 @@ import {
25
24
  urlToAppRoute,
26
25
  } from './route'
27
26
 
28
- export {
29
- AppRoute,
30
- FilesIndexRoute,
31
- FilesRoute,
32
- HomeRoute,
33
- NestedRoute,
34
- NotFoundRoute,
35
- PeopleRoute,
36
- PersonRoute,
37
- } from './route'
27
+ export { AppRoute } from './route'
38
28
 
39
29
  // MODEL
40
30
 
@@ -66,7 +56,7 @@ export const init: Runtime.RoutingApplicationInit<Model, Message> = (
66
56
 
67
57
  const initialPeopleRoute = M.value(route).pipe(
68
58
  M.tag('People', peopleRoute => peopleRoute),
69
- M.orElse(() => PeopleRoute({ searchText: Option.none() })),
59
+ M.orElse(() => AppRoute.People({ searchText: Option.none() })),
70
60
  )
71
61
 
72
62
  const peopleInit = People.init(initialPeopleRoute)