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.
Files changed (149) hide show
  1. package/dist/commands/create.js +9 -7
  2. package/dist/templates/base/FOLDKIT.md +22 -6
  3. package/dist/templates/examples/api-cache/src/data.ts +14 -14
  4. package/dist/templates/examples/api-cache/src/main.ts +42 -39
  5. package/dist/templates/examples/auth/src/command.ts +3 -3
  6. package/dist/templates/examples/auth/src/domain/session.ts +8 -6
  7. package/dist/templates/examples/auth/src/main.ts +11 -11
  8. package/dist/templates/examples/auth/src/message.ts +3 -3
  9. package/dist/templates/examples/auth/src/model.ts +2 -2
  10. package/dist/templates/examples/auth/src/page/loggedIn/index.ts +1 -1
  11. package/dist/templates/examples/auth/src/page/loggedIn/view.ts +3 -3
  12. package/dist/templates/examples/auth/src/page/loggedOut/index.ts +1 -1
  13. package/dist/templates/examples/auth/src/page/loggedOut/page/login.ts +9 -17
  14. package/dist/templates/examples/auth/src/page/loggedOut/update.ts +4 -6
  15. package/dist/templates/examples/auth/src/page/loggedOut/view.ts +3 -3
  16. package/dist/templates/examples/auth/src/route.ts +2 -2
  17. package/dist/templates/examples/auth/src/update.ts +37 -40
  18. package/dist/templates/examples/auth/src/view.ts +6 -6
  19. package/dist/templates/examples/canvas-art/src/main.ts +22 -30
  20. package/dist/templates/examples/charting/src/command.ts +3 -3
  21. package/dist/templates/examples/charting/src/domain.ts +53 -48
  22. package/dist/templates/examples/charting/src/echarts.ts +6 -6
  23. package/dist/templates/examples/charting/src/githubApi.ts +33 -34
  24. package/dist/templates/examples/charting/src/http.ts +7 -7
  25. package/dist/templates/examples/charting/src/message.ts +6 -6
  26. package/dist/templates/examples/charting/src/model.ts +6 -6
  27. package/dist/templates/examples/charting/src/npmApi.ts +19 -15
  28. package/dist/templates/examples/charting/src/subscription.ts +5 -5
  29. package/dist/templates/examples/charting/src/telemetry.test.ts +18 -12
  30. package/dist/templates/examples/charting/src/telemetry.ts +11 -18
  31. package/dist/templates/examples/charting/src/update.ts +17 -23
  32. package/dist/templates/examples/charting/src/view/chart.ts +2 -2
  33. package/dist/templates/examples/counter/src/main.ts +2 -2
  34. package/dist/templates/examples/counters/src/counter.ts +2 -2
  35. package/dist/templates/examples/counters/src/main.ts +8 -8
  36. package/dist/templates/examples/embedding/src/main.ts +10 -7
  37. package/dist/templates/examples/form/src/main.ts +18 -14
  38. package/dist/templates/examples/generative-art/src/command.ts +7 -2
  39. package/dist/templates/examples/generative-art/src/message.ts +14 -14
  40. package/dist/templates/examples/generative-art/src/model.ts +21 -21
  41. package/dist/templates/examples/generative-art/src/update.ts +15 -17
  42. package/dist/templates/examples/interrupting-commands/src/main.ts +30 -27
  43. package/dist/templates/examples/job-application/src/domain/proficiencyLevel.ts +2 -2
  44. package/dist/templates/examples/job-application/src/domain/pronounOption.ts +2 -2
  45. package/dist/templates/examples/job-application/src/domain/step.ts +11 -11
  46. package/dist/templates/examples/job-application/src/main.ts +5 -5
  47. package/dist/templates/examples/job-application/src/message.ts +2 -2
  48. package/dist/templates/examples/job-application/src/model.ts +5 -5
  49. package/dist/templates/examples/job-application/src/step/attachments.ts +32 -34
  50. package/dist/templates/examples/job-application/src/step/coverLetter.ts +4 -4
  51. package/dist/templates/examples/job-application/src/step/education/education.ts +11 -14
  52. package/dist/templates/examples/job-application/src/step/education/entry.ts +21 -22
  53. package/dist/templates/examples/job-application/src/step/personalInfo.ts +44 -54
  54. package/dist/templates/examples/job-application/src/step/skills/entry.ts +12 -14
  55. package/dist/templates/examples/job-application/src/step/skills/skills.ts +11 -14
  56. package/dist/templates/examples/job-application/src/step/workHistory/entry.ts +43 -45
  57. package/dist/templates/examples/job-application/src/step/workHistory/workHistory.ts +11 -14
  58. package/dist/templates/examples/job-application/src/update.ts +17 -17
  59. package/dist/templates/examples/job-application/src/view/attachments.ts +5 -5
  60. package/dist/templates/examples/job-application/src/view/datePicker.ts +3 -3
  61. package/dist/templates/examples/job-application/src/view/field.ts +5 -5
  62. package/dist/templates/examples/job-application/src/view/index.ts +28 -22
  63. package/dist/templates/examples/kanban/src/command.ts +4 -4
  64. package/dist/templates/examples/kanban/src/domain/card.ts +6 -6
  65. package/dist/templates/examples/kanban/src/domain/column.ts +9 -9
  66. package/dist/templates/examples/kanban/src/main.ts +4 -4
  67. package/dist/templates/examples/kanban/src/message.ts +6 -6
  68. package/dist/templates/examples/kanban/src/model.ts +11 -9
  69. package/dist/templates/examples/kanban/src/update.ts +5 -5
  70. package/dist/templates/examples/managed-resource-layer/src/main.ts +20 -20
  71. package/dist/templates/examples/map/src/locations.ts +7 -7
  72. package/dist/templates/examples/map/src/main.ts +83 -44
  73. package/dist/templates/examples/map/src/mount.test.ts +127 -0
  74. package/dist/templates/examples/pixel-art/src/command.ts +12 -5
  75. package/dist/templates/examples/pixel-art/src/main.ts +4 -4
  76. package/dist/templates/examples/pixel-art/src/message.ts +7 -7
  77. package/dist/templates/examples/pixel-art/src/model.ts +34 -29
  78. package/dist/templates/examples/pixel-art/src/subscription.ts +2 -2
  79. package/dist/templates/examples/pixel-art/src/update.ts +79 -86
  80. package/dist/templates/examples/query-sync/src/main.ts +106 -102
  81. package/dist/templates/examples/route-transitions/src/data.ts +6 -6
  82. package/dist/templates/examples/route-transitions/src/main.ts +23 -29
  83. package/dist/templates/examples/route-transitions/src/route.ts +3 -3
  84. package/dist/templates/examples/routing/src/fileTree.ts +10 -8
  85. package/dist/templates/examples/routing/src/main.ts +23 -21
  86. package/dist/templates/examples/routing/src/page/people.ts +14 -22
  87. package/dist/templates/examples/routing/src/route.ts +7 -7
  88. package/dist/templates/examples/shopping-cart/src/main.ts +30 -31
  89. package/dist/templates/examples/shopping-cart/src/page/products.ts +10 -10
  90. package/dist/templates/examples/shopping-cart/src/route.ts +6 -4
  91. package/dist/templates/examples/slow-warnings/src/main.ts +35 -43
  92. package/dist/templates/examples/snake/src/domain/snake.ts +2 -2
  93. package/dist/templates/examples/snake/src/main.ts +39 -47
  94. package/dist/templates/examples/ssg/src/main.ts +5 -5
  95. package/dist/templates/examples/ssg/src/route.ts +2 -2
  96. package/dist/templates/examples/ssr/src/cookie.ts +3 -3
  97. package/dist/templates/examples/ssr/src/main.ts +10 -10
  98. package/dist/templates/examples/state-machine/src/main.ts +55 -53
  99. package/dist/templates/examples/state-machine/src/view.ts +7 -7
  100. package/dist/templates/examples/stopwatch/src/main.ts +10 -10
  101. package/dist/templates/examples/todo/src/main.ts +41 -34
  102. package/dist/templates/examples/ui-showcase/src/main.ts +54 -42
  103. package/dist/templates/examples/ui-showcase/src/scene.test.ts +1 -1
  104. package/dist/templates/examples/ui-showcase/src/ui/init.ts +1 -2
  105. package/dist/templates/examples/ui-showcase/src/ui/message.ts +17 -18
  106. package/dist/templates/examples/ui-showcase/src/ui/model.ts +47 -44
  107. package/dist/templates/examples/ui-showcase/src/ui/subscriptions.ts +1 -1
  108. package/dist/templates/examples/ui-showcase/src/ui/toast.ts +4 -4
  109. package/dist/templates/examples/ui-showcase/src/ui/update.ts +287 -298
  110. package/dist/templates/examples/ui-showcase/src/ui/view/animation.ts +2 -2
  111. package/dist/templates/examples/ui-showcase/src/ui/view/button.ts +1 -1
  112. package/dist/templates/examples/ui-showcase/src/ui/view/calendar.ts +4 -4
  113. package/dist/templates/examples/ui-showcase/src/ui/view/checkbox.ts +1 -1
  114. package/dist/templates/examples/ui-showcase/src/ui/view/combobox.ts +1 -1
  115. package/dist/templates/examples/ui-showcase/src/ui/view/datePicker.ts +4 -4
  116. package/dist/templates/examples/ui-showcase/src/ui/view/dialog.ts +1 -1
  117. package/dist/templates/examples/ui-showcase/src/ui/view/disclosure.ts +1 -1
  118. package/dist/templates/examples/ui-showcase/src/ui/view/dragAndDrop.ts +1 -1
  119. package/dist/templates/examples/ui-showcase/src/ui/view/fieldset.ts +1 -1
  120. package/dist/templates/examples/ui-showcase/src/ui/view/fileDrop.ts +6 -6
  121. package/dist/templates/examples/ui-showcase/src/ui/view/hoverIntent.ts +9 -20
  122. package/dist/templates/examples/ui-showcase/src/ui/view/input.ts +1 -1
  123. package/dist/templates/examples/ui-showcase/src/ui/view/listbox.ts +1 -1
  124. package/dist/templates/examples/ui-showcase/src/ui/view/menu.ts +15 -15
  125. package/dist/templates/examples/ui-showcase/src/ui/view/popover.ts +1 -1
  126. package/dist/templates/examples/ui-showcase/src/ui/view/radioGroup.ts +1 -1
  127. package/dist/templates/examples/ui-showcase/src/ui/view/select.ts +1 -1
  128. package/dist/templates/examples/ui-showcase/src/ui/view/slider.ts +1 -1
  129. package/dist/templates/examples/ui-showcase/src/ui/view/switch.ts +1 -1
  130. package/dist/templates/examples/ui-showcase/src/ui/view/tabs.ts +7 -7
  131. package/dist/templates/examples/ui-showcase/src/ui/view/textarea.ts +1 -1
  132. package/dist/templates/examples/ui-showcase/src/ui/view/toast.ts +8 -8
  133. package/dist/templates/examples/ui-showcase/src/ui/view/tooltip.ts +1 -1
  134. package/dist/templates/examples/ui-showcase/src/ui/view/virtualList.ts +7 -7
  135. package/dist/templates/examples/view-transitions/src/artwork.ts +7 -7
  136. package/dist/templates/examples/view-transitions/src/main.ts +6 -6
  137. package/dist/templates/examples/view-transitions/src/route.ts +3 -3
  138. package/dist/templates/examples/weather/src/main.ts +42 -42
  139. package/dist/templates/examples/weather/src/story.test.ts +5 -5
  140. package/dist/templates/examples/web-components/src/main.ts +16 -16
  141. package/dist/templates/examples/websocket-chat/src/main.ts +30 -30
  142. package/dist/templates/release.json +13 -13
  143. package/dist/templates/rendering/ssg/src/main.ts +5 -5
  144. package/dist/templates/rendering/ssg/src/route.ts +2 -2
  145. package/dist/templates/rendering/ssr/server/main.ts +15 -15
  146. package/dist/templates/rendering/ssr/src/cookie.ts +3 -3
  147. package/dist/templates/rendering/ssr/src/main.ts +10 -10
  148. package/dist/utils/packages.js +17 -0
  149. package/package.json +1 -1
@@ -5,7 +5,7 @@ import { spawnSync } from 'node:child_process';
5
5
  import { examples } from '../examples.js';
6
6
  import { Scaffold, renderings } from '../rendering.js';
7
7
  import { createProject } from '../utils/files.js';
8
- import { devCommand, installDependencies, } from '../utils/packages.js';
8
+ import { devCommand, installDependencies, readFoldkitSubtreeRef, } from '../utils/packages.js';
9
9
  import { validateProjectName } from '../validateName.js';
10
10
  const isWindows = process.platform === 'win32';
11
11
  const promptForName = Prompt.text({
@@ -90,7 +90,7 @@ const installProjectDependencies = (projectPath, packageManager, scaffold, maybe
90
90
  yield* Console.log(chalk.green('✅ Dependencies installed'));
91
91
  yield* Console.log('');
92
92
  });
93
- const displaySuccessMessage = (name, packageManager) => Effect.gen(function* () {
93
+ const displaySuccessMessage = (name, packageManager, subtreeRef) => Effect.gen(function* () {
94
94
  yield* Console.log(chalk.bold('All systems nominal.'));
95
95
  yield* Console.log('');
96
96
  yield* Console.log(` > ${chalk.cyan('cd')} ${name}`);
@@ -98,15 +98,16 @@ const displaySuccessMessage = (name, packageManager) => Effect.gen(function* ()
98
98
  yield* Console.log('');
99
99
  yield* Console.log(chalk.bold('AI-Assisted Development'));
100
100
  yield* Console.log('');
101
- yield* Console.log(' Vendor Foldkit in as a git subtree so your AI assistant can\n' +
102
- ' reference the source, examples, and documentation. Commit\n' +
103
- ' the scaffold first so subtree has a base commit to merge into:');
101
+ yield* Console.log(' Vendor Foldkit in as a git subtree, pinned to the release this\n' +
102
+ ' project installs, so your AI assistant can reference the matching\n' +
103
+ ' source, examples, and documentation. Commit the scaffold first so\n' +
104
+ ' subtree has a base commit to merge into:');
104
105
  yield* Console.log('');
105
106
  yield* Console.log(` > ${chalk.cyan('cd')} ${name}`);
106
107
  yield* Console.log(` > ${chalk.cyan('git init')}`);
107
108
  yield* Console.log(` > ${chalk.cyan('git add .')}`);
108
109
  yield* Console.log(` > ${chalk.cyan('git commit -m "chore: initial commit"')}`);
109
- yield* Console.log(` > ${chalk.cyan('git subtree add --prefix=repos/foldkit https://github.com/foldkit/foldkit.git main --squash')}`);
110
+ yield* Console.log(` > ${chalk.cyan(`git subtree add --prefix=repos/foldkit https://github.com/foldkit/foldkit.git ${subtreeRef} --squash`)}`);
110
111
  yield* Console.log('');
111
112
  yield* Console.log(` Details: ${chalk.cyan('foldkit.dev/ai/overview')}`);
112
113
  yield* Console.log('');
@@ -138,8 +139,9 @@ export const create = (input) => Effect.gen(function* () {
138
139
  }
139
140
  const projectPath = path.resolve(name);
140
141
  yield* validateProject(name, projectPath, packageManager);
142
+ const subtreeRef = yield* readFoldkitSubtreeRef;
141
143
  yield* setupProject(name, projectPath, scaffold, packageManager);
142
144
  yield* installProjectDependencies(projectPath, packageManager, scaffold, input.maybeDependencyManifestsDirectory);
143
- yield* displaySuccessMessage(name, packageManager);
145
+ yield* displaySuccessMessage(name, packageManager, subtreeRef);
144
146
  return name;
145
147
  });
@@ -16,15 +16,29 @@ If `./repos/foldkit` exists, it contains the full Foldkit repository, vendored i
16
16
 
17
17
  Treat the subtree as read-only reference: never import from `repos/foldkit/` in your project source. Imports must come from the `foldkit` npm package.
18
18
 
19
- If `./repos/foldkit` does not exist and `AGENTS.md` says `subtree_prompted: false`, offer to run `git subtree add --prefix=repos/foldkit https://github.com/foldkit/foldkit.git main --squash`. This gives you access to the full source, examples, and docs. Whether the user accepts or declines, set that line in `AGENTS.md` to `true` so it isn't asked again.
19
+ If `./repos/foldkit` does not exist and `AGENTS.md` says `subtree_prompted: false`, offer to vendor the repository. It gives you access to the full source, examples, and docs. Pin it to the release git tag matching the installed `foldkit` package; vendoring `main` instead can hand you examples and APIs from a release the project has not installed:
20
+
21
+ ```sh
22
+ git subtree add --prefix=repos/foldkit https://github.com/foldkit/foldkit.git "foldkit@$(node -p "require('./node_modules/foldkit/package.json').version")" --squash
23
+ ```
24
+
25
+ A canary install has no release tag to pin to. Its version names its source commit (`0.156.0-canary.<commit>`); use the full hash of that commit as the ref instead. GitHub expands the short hash at `https://github.com/foldkit/foldkit/commit/<commit>`.
26
+
27
+ Whether the user accepts or declines, set that line in `AGENTS.md` to `true` so it isn't asked again.
20
28
 
21
29
  If `foldkit-skills` is installed as a Claude Code plugin, the `generate-program` and `audit-program` skills carry snapshot architecture and conventions guides synced from the live code.
22
30
 
23
- ## Keeping this file current
31
+ ## After a Foldkit upgrade
24
32
 
25
- Foldkit's APIs and conventions change, and this file changes with them. A stale copy sends agents after APIs the installed packages no longer export.
33
+ Two things go stale when the project upgrades its Foldkit packages: the vendored subtree and this file. Bring both forward, subtree first, since the fresh copy of this file comes from it.
26
34
 
27
- Replace it whenever the project upgrades its Foldkit packages. If `./repos/foldkit` exists, copy `repos/foldkit/packages/create-foldkit-app/templates/base/FOLDKIT.md` over this file. Otherwise take the [current template on GitHub](https://github.com/foldkit/foldkit/blob/main/packages/create-foldkit-app/templates/base/FOLDKIT.md). There is nothing here to merge or preserve.
35
+ Re-pin the subtree to the newly installed release (for a canary install, pin to the commit its version names, as above):
36
+
37
+ ```sh
38
+ git subtree pull --prefix=repos/foldkit https://github.com/foldkit/foldkit.git "foldkit@$(node -p "require('./node_modules/foldkit/package.json').version")" --squash
39
+ ```
40
+
41
+ Then replace this file whole. Foldkit's conventions change with its APIs, and a stale copy sends agents after APIs the installed packages no longer export. Copy `repos/foldkit/packages/create-foldkit-app/templates/base/FOLDKIT.md` over this file. Without the subtree, take the template from GitHub at the tag matching the installed version: `https://github.com/foldkit/foldkit/blob/foldkit@<version>/packages/create-foldkit-app/templates/base/FOLDKIT.md`. There is nothing here to merge or preserve.
28
42
 
29
43
  ## Project Conventions
30
44
 
@@ -84,9 +98,11 @@ Omit the children argument when an element has none: `h.div([h.Class('divider')]
84
98
 
85
99
  Define a Command with `Command.define(name, { args, messages, execute })`; omit `args` when the Command takes none. Assign definitions to PascalCase constants. Never inline in pipe chains. Name the effect `execute` performs, not the later Model transition caused when update handles its result: a timer that only waits before update starts a dismissal is `WaitBeforeDismissal`, not `DismissAfter`. Commands catch all errors via `Effect.catch(() => Effect.succeed(Message.FailedX(...)))` so side effects never crash the app. Definitions live colocated with the update function that returns them.
86
100
 
101
+ Command args contain values already present in the Model or Message. Calling `Date.now()`, `crypto.randomUUID()`, or another source of time or randomness while preparing a Command happens before the Command executes, whether the call appears directly in the args object or its result is assigned to a local variable first. Obtain those values in `execute` and return them in the result Message.
102
+
87
103
  For the with-args shape, see `repos/foldkit/examples/weather/src/main.ts` or `repos/foldkit/examples/kanban/src/command.ts`. For an argless DOM-side-effect Command, the argless form in `kanban/src/command.ts` (`FocusAddCardInput`) is the canonical reference.
88
104
 
89
- For DOM operations (focus, scroll, modals, scroll lock), Foldkit ships a `Dom` module. For time, randomness, UUIDs, and delays, use Effect's built-ins directly (`Clock`, `Random`, `Effect.uuid`, `Effect.sleep`). Don't reach for raw `document.querySelector`, `setTimeout`, `Date.now()`, or `Math.random()`.
105
+ For DOM operations (focus, scroll, modals, scroll lock), Foldkit ships a `Dom` module. For time, randomness, UUIDs, and delays, use Effect's APIs directly (`Clock`, `Random`, `Crypto.Crypto`, `Effect.sleep`). Provide the platform Crypto layer when using `Crypto.Crypto`. Don't reach for raw `document.querySelector`, `setTimeout`, `Date.now()`, or `Math.random()`.
90
106
 
91
107
  ### File Organization
92
108
 
@@ -126,7 +142,7 @@ Declare the whole Message union with `defineMessageUnion()`, then put `type Mess
126
142
  ```ts
127
143
  const Message = defineMessageUnion({
128
144
  ClickedSubmit: {},
129
- UpdatedEmail: { value: S.String },
145
+ UpdatedEmail: { value: Schema.String },
130
146
  })
131
147
  type Message = typeof Message.Type
132
148
  ```
@@ -1,24 +1,24 @@
1
- import { Array, Duration, Effect, Option, Random, Schema as S } from 'effect'
1
+ import { Array, Duration, Effect, Option, Random, Schema } from 'effect'
2
2
 
3
- export const Post = S.Struct({
4
- id: S.String,
5
- title: S.String,
6
- excerpt: S.String,
3
+ export const Post = Schema.Struct({
4
+ id: Schema.String,
5
+ title: Schema.String,
6
+ excerpt: Schema.String,
7
7
  })
8
8
  export type Post = typeof Post.Type
9
9
 
10
- export const PostDetail = S.Struct({
11
- id: S.String,
12
- title: S.String,
13
- author: S.String,
14
- body: S.String,
10
+ export const PostDetail = Schema.Struct({
11
+ id: Schema.String,
12
+ title: Schema.String,
13
+ author: Schema.String,
14
+ body: Schema.String,
15
15
  })
16
16
  export type PostDetail = typeof PostDetail.Type
17
17
 
18
- export const Stats = S.Struct({
19
- activeUsers: S.Number,
20
- requestsPerSecond: S.Number,
21
- cacheHitRatePercent: S.Number,
18
+ export const Stats = Schema.Struct({
19
+ activeUsers: Schema.Number,
20
+ requestsPerSecond: Schema.Number,
21
+ cacheHitRatePercent: Schema.Number,
22
22
  })
23
23
  export type Stats = typeof Stats.Type
24
24
 
@@ -4,9 +4,9 @@ import {
4
4
  Duration,
5
5
  Effect,
6
6
  HashMap,
7
- Match as M,
7
+ Match,
8
8
  Option,
9
- Schema as S,
9
+ Schema,
10
10
  Stream,
11
11
  pipe,
12
12
  } from 'effect'
@@ -32,36 +32,39 @@ export const TABS_ID = 'api-cache-tabs'
32
32
 
33
33
  // MODEL
34
34
 
35
- const FetchedPosts = S.Struct({ posts: S.Array(Post), fetchedAt: S.Number })
35
+ const FetchedPosts = Schema.Struct({
36
+ posts: Schema.Array(Post),
37
+ fetchedAt: Schema.Number,
38
+ })
36
39
 
37
- const FetchedPostDetail = S.Struct({
40
+ const FetchedPostDetail = Schema.Struct({
38
41
  detail: PostDetail,
39
- fetchedAt: S.Number,
42
+ fetchedAt: Schema.Number,
40
43
  })
41
44
 
42
- const FetchedStats = S.Struct({ stats: Stats, fetchedAt: S.Number })
45
+ const FetchedStats = Schema.Struct({ stats: Stats, fetchedAt: Schema.Number })
43
46
 
44
- export const PostsData = AsyncData.Schema(FetchedPosts, S.String)
45
- export const PostDetailData = AsyncData.Schema(FetchedPostDetail, S.String)
46
- export const StatsData = AsyncData.Schema(FetchedStats, S.String)
47
+ export const PostsData = AsyncData.Schema(FetchedPosts, Schema.String)
48
+ export const PostDetailData = AsyncData.Schema(FetchedPostDetail, Schema.String)
49
+ export const StatsData = AsyncData.Schema(FetchedStats, Schema.String)
47
50
 
48
51
  type PostsData = typeof PostsData.schema.Type
49
52
  type PostDetailData = typeof PostDetailData.schema.Type
50
53
  type StatsData = typeof StatsData.schema.Type
51
54
 
52
- const Tab = S.Literals(['Posts', 'Stats'])
55
+ const Tab = Schema.Literals(['Posts', 'Stats'])
53
56
  type Tab = typeof Tab.Type
54
57
 
55
58
  const tabValues: ReadonlyArray<Tab> = Tab.literals
56
59
 
57
60
  export const AppTabs = Tabs.create<Tab>()
58
61
 
59
- export const Model = S.Struct({
62
+ export const Model = Schema.Struct({
60
63
  tabs: Tabs.Model,
61
64
  activeTab: Tab,
62
65
  posts: PostsData.schema,
63
- postDetailById: S.HashMap(S.String, PostDetailData.schema),
64
- maybeSelectedPostId: S.Option(S.String),
66
+ postDetailById: Schema.HashMap(Schema.String, PostDetailData.schema),
67
+ maybeSelectedPostId: Schema.Option(Schema.String),
65
68
  stats: StatsData.schema,
66
69
  })
67
70
  export type Model = typeof Model.Type
@@ -70,20 +73,20 @@ export type Model = typeof Model.Type
70
73
 
71
74
  export const Message = defineMessageUnion({
72
75
  GotTabsMessage: { message: Tabs.Message },
73
- ClickedPost: { postId: S.String },
76
+ ClickedPost: { postId: Schema.String },
74
77
  ClickedBackToPosts: {},
75
78
  ClickedInvalidatePosts: {},
76
79
  ClickedRetryPosts: {},
77
- ClickedRetryPostDetail: { postId: S.String },
80
+ ClickedRetryPostDetail: { postId: Schema.String },
78
81
  ClickedRefreshStats: {},
79
82
  ClickedRetryStats: {},
80
83
  TickedRevalidateStats: {},
81
- SettledFetchPosts: { result: S.Result(FetchedPosts, S.String) },
84
+ SettledFetchPosts: { result: Schema.Result(FetchedPosts, Schema.String) },
82
85
  SettledFetchPostDetail: {
83
- postId: S.String,
84
- result: S.Result(FetchedPostDetail, S.String),
86
+ postId: Schema.String,
87
+ result: Schema.Result(FetchedPostDetail, Schema.String),
85
88
  },
86
- SettledFetchStats: { result: S.Result(FetchedStats, S.String) },
89
+ SettledFetchStats: { result: Schema.Result(FetchedStats, Schema.String) },
87
90
  })
88
91
 
89
92
  export type Message = typeof Message.Type
@@ -122,33 +125,33 @@ const setPostDetail = (postId: string, postDetail: PostDetailData) =>
122
125
  const activateTab = (model: Model, tab: Tab): UpdateReturn => {
123
126
  const modelWithActiveTab = evo(model, { activeTab: () => tab })
124
127
 
125
- return M.value(tab).pipe(
126
- M.withReturnType<UpdateReturn>(),
127
- M.when('Posts', () =>
128
+ return Match.value(tab).pipe(
129
+ Match.withReturnType<UpdateReturn>(),
130
+ Match.when('Posts', () =>
128
131
  applyPostsTransition(
129
132
  modelWithActiveTab,
130
133
  AsyncData.loadIfMissing(modelWithActiveTab.posts),
131
134
  ),
132
135
  ),
133
- M.when('Stats', () =>
136
+ Match.when('Stats', () =>
134
137
  applyStatsTransition(
135
138
  modelWithActiveTab,
136
139
  AsyncData.loadIfMissing(modelWithActiveTab.stats),
137
140
  ),
138
141
  ),
139
- M.exhaustive,
142
+ Match.exhaustive,
140
143
  )
141
144
  }
142
145
 
143
- const foldTabsOutMessage = M.type<Tabs.OutMessage<Tab>>().pipe(
144
- M.withReturnType<Update.Step<Model, Message>>(),
145
- M.tagsExhaustive({
146
- Selected:
147
- ({ value }) =>
148
- model =>
149
- activateTab(model, value),
150
- }),
151
- )
146
+ const foldTabsOutMessage = Tabs.OutMessage.match<
147
+ Update.Step<Model, Message>,
148
+ Tabs.OutMessage<Tab>
149
+ >({
150
+ Selected:
151
+ ({ value }) =>
152
+ model =>
153
+ activateTab(model, value),
154
+ })
152
155
 
153
156
  const foldTabs = Update.foldChild({
154
157
  update: AppTabs.update,
@@ -249,7 +252,7 @@ export const FetchPosts = Command.define('FetchPosts', {
249
252
  })
250
253
 
251
254
  export const FetchPostDetail = Command.define('FetchPostDetail', {
252
- args: { postId: S.String },
255
+ args: { postId: Schema.String },
253
256
  messages: [Message.SettledFetchPostDetail],
254
257
  execute: ({ postId }) =>
255
258
  pipe(
@@ -280,7 +283,7 @@ export const FetchStats = Command.define('FetchStats', {
280
283
 
281
284
  export const subscriptions = Subscription.make<Model, Message>()(entry => ({
282
285
  revalidateStats: entry(
283
- { isObservingStats: S.Boolean },
286
+ { isObservingStats: Schema.Boolean },
284
287
  {
285
288
  modelToDependencies: model => ({
286
289
  isObservingStats:
@@ -350,10 +353,10 @@ export const view = (model: Model, h: HtmlBuilder<Message>): Document => ({
350
353
  tabInfo.value,
351
354
  [...tabInfo.panel, h.Class('flex flex-col gap-4')],
352
355
  [
353
- M.value(tabInfo.value).pipe(
354
- M.when('Posts', () => postsTabView(model, h)),
355
- M.when('Stats', () => statsTabView(model, h)),
356
- M.exhaustive,
356
+ Match.value(tabInfo.value).pipe(
357
+ Match.when('Posts', () => postsTabView(model, h)),
358
+ Match.when('Stats', () => statsTabView(model, h)),
359
+ Match.exhaustive,
357
360
  ),
358
361
  ],
359
362
  ),
@@ -1,4 +1,4 @@
1
- import { Console, Effect, Schema as S } from 'effect'
1
+ import { Console, Effect, Schema } from 'effect'
2
2
  import { KeyValueStore } from 'effect/unstable/persistence'
3
3
  import { Command } from 'foldkit'
4
4
 
@@ -16,7 +16,7 @@ export const SaveSession = Command.define('SaveSession', {
16
16
  const store = yield* KeyValueStore.KeyValueStore
17
17
  yield* store.set(
18
18
  SESSION_STORAGE_KEY,
19
- S.encodeSync(SessionJsonString)(session),
19
+ Schema.encodeSync(SessionJsonString)(session),
20
20
  )
21
21
  return Message.SucceededSaveSession()
22
22
  }).pipe(
@@ -42,7 +42,7 @@ export const ClearSession = Command.define('ClearSession', {
42
42
  })
43
43
 
44
44
  export const LogError = Command.define('LogError', {
45
- args: { entries: S.Array(S.Unknown) },
45
+ args: { entries: Schema.Array(Schema.Unknown) },
46
46
  messages: [Message.CompletedLogError],
47
47
  execute: ({ entries }) =>
48
48
  Console.error(...entries).pipe(Effect.as(Message.CompletedLogError())),
@@ -1,11 +1,13 @@
1
- import { Schema as S } from 'effect'
1
+ import { Schema } from 'effect'
2
2
 
3
- export const Session = S.Struct({
4
- userId: S.String,
5
- email: S.String,
6
- name: S.String,
3
+ export const Session = Schema.Struct({
4
+ userId: Schema.String,
5
+ email: Schema.String,
6
+ name: Schema.String,
7
7
  })
8
8
 
9
9
  export type Session = typeof Session.Type
10
10
 
11
- export const SessionJsonString = S.fromJsonString(S.toCodecJson(Session))
11
+ export const SessionJsonString = Schema.fromJsonString(
12
+ Schema.toCodecJson(Session),
13
+ )
@@ -1,4 +1,4 @@
1
- import { Effect, Match as M, Option, Schema as S } from 'effect'
1
+ import { Effect, Match, Option, Schema } from 'effect'
2
2
  import { KeyValueStore } from 'effect/unstable/persistence'
3
3
  import { Runtime, type Update } from 'foldkit'
4
4
  import { Url } from 'foldkit/url'
@@ -14,8 +14,8 @@ import { RedirectToDashboard, RedirectToLogin } from './update'
14
14
 
15
15
  // FLAGS
16
16
 
17
- export const Flags = S.Struct({
18
- maybeSession: S.Option(Session),
17
+ export const Flags = Schema.Struct({
18
+ maybeSession: Schema.Option(Session),
19
19
  })
20
20
 
21
21
  export const flags: Effect.Effect<Flags> = Effect.gen(function* () {
@@ -24,7 +24,7 @@ export const flags: Effect.Effect<Flags> = Effect.gen(function* () {
24
24
  Option.fromNullishOr(yield* store.get(SESSION_STORAGE_KEY)),
25
25
  )
26
26
 
27
- const decodeSession = S.decodeEffect(SessionJsonString)
27
+ const decodeSession = Schema.decodeEffect(SessionJsonString)
28
28
  const session = yield* decodeSession(sessionJson)
29
29
 
30
30
  return Flags.make({ maybeSession: Option.some(session) })
@@ -40,7 +40,7 @@ export type Flags = typeof Flags.Type
40
40
  // INIT
41
41
 
42
42
  type InitReturn = Update.Return<Model, Message>
43
- const withInitReturn = M.withReturnType<InitReturn>()
43
+ const withInitReturn = Match.withReturnType<InitReturn>()
44
44
 
45
45
  export const init: Runtime.RoutingApplicationInit<Model, Message, Flags> = (
46
46
  flags: Flags,
@@ -50,24 +50,24 @@ export const init: Runtime.RoutingApplicationInit<Model, Message, Flags> = (
50
50
 
51
51
  return Option.match(flags.maybeSession, {
52
52
  onNone: () =>
53
- M.value(route).pipe(
53
+ Match.value(route).pipe(
54
54
  withInitReturn,
55
- M.tag('Home', 'Login', 'NotFound', route => ({
55
+ Match.tag('Home', 'Login', 'NotFound', route => ({
56
56
  model: LoggedOut.init(route),
57
57
  })),
58
- M.orElse(() => ({
58
+ Match.orElse(() => ({
59
59
  model: LoggedOut.init(AppRoute.Login()),
60
60
  commands: [RedirectToLogin()],
61
61
  })),
62
62
  ),
63
63
 
64
64
  onSome: session =>
65
- M.value(route).pipe(
65
+ Match.value(route).pipe(
66
66
  withInitReturn,
67
- M.tag('Dashboard', 'Settings', 'NotFound', route => ({
67
+ Match.tag('Dashboard', 'Settings', 'NotFound', route => ({
68
68
  model: LoggedIn.init(route, session),
69
69
  })),
70
- M.orElse(() => ({
70
+ Match.orElse(() => ({
71
71
  model: LoggedIn.init(AppRoute.Dashboard(), session),
72
72
  commands: [RedirectToDashboard()],
73
73
  })),
@@ -1,4 +1,4 @@
1
- import { Schema as S } from 'effect'
1
+ import { Schema } from 'effect'
2
2
  import { defineMessageUnion } from 'foldkit/message'
3
3
  import { UrlRequest } from 'foldkit/navigation'
4
4
  import { Url } from 'foldkit/url'
@@ -12,9 +12,9 @@ export const Message = defineMessageUnion({
12
12
  ClickedLink: { request: UrlRequest },
13
13
  ChangedUrl: { url: Url },
14
14
  SucceededSaveSession: {},
15
- FailedSaveSession: { error: S.String },
15
+ FailedSaveSession: { error: Schema.String },
16
16
  SucceededClearSession: {},
17
- FailedClearSession: { error: S.String },
17
+ FailedClearSession: { error: Schema.String },
18
18
  GotLoggedOutMessage: { message: LoggedOut.Message },
19
19
  GotLoggedInMessage: { message: LoggedIn.Message },
20
20
  })
@@ -1,8 +1,8 @@
1
- import { Schema as S } from 'effect'
1
+ import { Schema } from 'effect'
2
2
 
3
3
  import { LoggedIn, LoggedOut } from './page'
4
4
 
5
- export const Model = S.Union([LoggedOut.Model, LoggedIn.Model])
5
+ export const Model = Schema.Union([LoggedOut.Model, LoggedIn.Model])
6
6
 
7
7
  export type Model = typeof Model.Type
8
8
 
@@ -1,4 +1,4 @@
1
1
  export { Model, init } from './model'
2
- export { Message, type OutMessage } from './message'
2
+ export { Message, OutMessage } from './message'
3
3
  export { update } from './update'
4
4
  export { view } from './view'
@@ -1,5 +1,5 @@
1
1
  import { clsx } from 'clsx'
2
- import { Match as M } from 'effect'
2
+ import { Match } from 'effect'
3
3
  import { Submodel } from 'foldkit'
4
4
  import { Html, HtmlBuilder } from 'foldkit/html'
5
5
 
@@ -72,8 +72,8 @@ export const view = Submodel.defineView<Model, Message>((model, h) =>
72
72
  h.main(
73
73
  [h.Class('py-8')],
74
74
  [
75
- M.value(model.route).pipe(
76
- M.tagsExhaustive({
75
+ Match.value(model.route).pipe(
76
+ Match.tagsExhaustive({
77
77
  Dashboard: () => Dashboard.view(model.session, h),
78
78
  Settings: () => Settings.view(model.session, h),
79
79
  NotFound: ({ path }) =>
@@ -1,4 +1,4 @@
1
1
  export { Model, init } from './model'
2
- export { Message, type OutMessage } from './message'
2
+ export { Message, OutMessage } from './message'
3
3
  export { update } from './update'
4
4
  export { view } from './view'
@@ -1,13 +1,5 @@
1
1
  import { clsx } from 'clsx'
2
- import {
3
- Array,
4
- Duration,
5
- Effect,
6
- Option,
7
- Schema as S,
8
- String,
9
- pipe,
10
- } from 'effect'
2
+ import { Array, Duration, Effect, Option, Schema, String, pipe } from 'effect'
11
3
  import { Command, FieldValidation, Submodel, type Update } from 'foldkit'
12
4
  import {
13
5
  Field,
@@ -29,10 +21,10 @@ import { homeRouter } from '../../../route'
29
21
 
30
22
  // MODEL
31
23
 
32
- export const Model = S.Struct({
33
- email: Field(S.String),
34
- password: Field(S.String),
35
- isSubmitting: S.Boolean,
24
+ export const Model = Schema.Struct({
25
+ email: Field(Schema.String),
26
+ password: Field(Schema.String),
27
+ isSubmitting: Schema.Boolean,
36
28
  })
37
29
 
38
30
  export type Model = typeof Model.Type
@@ -46,11 +38,11 @@ export const initModel = (): Model => ({
46
38
  // MESSAGE
47
39
 
48
40
  export const Message = defineMessageUnion({
49
- ChangedEmail: { value: S.String },
50
- ChangedPassword: { value: S.String },
41
+ ChangedEmail: { value: Schema.String },
42
+ ChangedPassword: { value: Schema.String },
51
43
  SubmittedForm: {},
52
44
  SucceededSimulateAuthRequest: { session: Session },
53
- FailedSimulateAuthRequest: { error: S.String },
45
+ FailedSimulateAuthRequest: { error: Schema.String },
54
46
  })
55
47
 
56
48
  export type Message = typeof Message.Type
@@ -86,7 +78,7 @@ const isFormValid = (model: Model): boolean =>
86
78
  // UPDATE
87
79
 
88
80
  export const SimulateAuthRequest = Command.define('SimulateAuthRequest', {
89
- args: { email: S.String, password: S.String },
81
+ args: { email: Schema.String, password: Schema.String },
90
82
  messages: [
91
83
  Message.SucceededSimulateAuthRequest,
92
84
  Message.FailedSimulateAuthRequest,
@@ -1,4 +1,4 @@
1
- import { Match as M, Option } from 'effect'
1
+ import { Option } from 'effect'
2
2
  import { Update } from 'foldkit'
3
3
  import { evo } from 'foldkit/struct'
4
4
 
@@ -12,11 +12,9 @@ const foldLogin = Update.foldChild({
12
12
  write: (model, nextLoginModel) =>
13
13
  evo(model, { loginModel: () => nextLoginModel }),
14
14
  toParentMessage: message => Message.GotLoginMessage({ message }),
15
- toParentOutMessage: M.type<Login.OutMessage>().pipe(
16
- M.tagsExhaustive({
17
- SucceededLogin: ({ session }) => OutMessage.SucceededLogin({ session }),
18
- }),
19
- ),
15
+ toParentOutMessage: Login.OutMessage.match<OutMessage>({
16
+ SucceededLogin: ({ session }) => OutMessage.SucceededLogin({ session }),
17
+ }),
20
18
  })
21
19
 
22
20
  export const update = (model: Model, message: Message) =>
@@ -1,4 +1,4 @@
1
- import { Match as M } from 'effect'
1
+ import { Match } from 'effect'
2
2
  import { Submodel } from 'foldkit'
3
3
 
4
4
  import { notFoundView } from '../../notFoundView'
@@ -12,8 +12,8 @@ export const view = Submodel.defineView<Model, Message>((model, h) =>
12
12
  h.div(
13
13
  [h.Class('py-8')],
14
14
  [
15
- M.value(model.route).pipe(
16
- M.tagsExhaustive({
15
+ Match.value(model.route).pipe(
16
+ Match.tagsExhaustive({
17
17
  Home: () => Home.view(h),
18
18
  Login: () =>
19
19
  h.submodel({
@@ -1,4 +1,4 @@
1
- import { Schema as S, pipe } from 'effect'
1
+ import { Schema, pipe } from 'effect'
2
2
  import { Route } from 'foldkit'
3
3
  import { defineRouteUnion, literal } from 'foldkit/route'
4
4
 
@@ -7,7 +7,7 @@ export const AppRoute = defineRouteUnion({
7
7
  Login: {},
8
8
  Dashboard: {},
9
9
  Settings: {},
10
- NotFound: { path: S.String },
10
+ NotFound: { path: Schema.String },
11
11
  })
12
12
 
13
13
  export type AppRoute = typeof AppRoute.Type