create-foldkit-app 0.27.3 → 0.28.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.
- package/dist/index.js +1 -5
- package/{templates → dist/templates}/base/AGENTS.md +17 -19
- package/dist/templates/examples/api-cache/package.json +29 -0
- package/dist/templates/examples/api-cache/src/data.test.ts +28 -0
- package/dist/templates/examples/api-cache/src/data.ts +126 -0
- package/dist/templates/examples/api-cache/src/entry.ts +17 -0
- package/dist/templates/examples/api-cache/src/main.fixtures.ts +70 -0
- package/dist/templates/examples/api-cache/src/main.ts +744 -0
- package/dist/templates/examples/api-cache/src/scene.test.ts +120 -0
- package/dist/templates/examples/api-cache/src/story.test.ts +288 -0
- package/dist/templates/examples/api-cache/src/styles.css +1 -0
- package/dist/templates/examples/api-cache/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/auth/package.json +30 -0
- package/dist/templates/examples/auth/src/command.ts +49 -0
- package/dist/templates/examples/auth/src/constant.ts +1 -0
- package/dist/templates/examples/auth/src/domain/session.ts +9 -0
- package/dist/templates/examples/auth/src/entry.ts +25 -0
- package/dist/templates/examples/auth/src/main.ts +75 -0
- package/dist/templates/examples/auth/src/message.ts +22 -0
- package/dist/templates/examples/auth/src/model.ts +9 -0
- package/dist/templates/examples/auth/src/notFoundView.ts +25 -0
- package/dist/templates/examples/auth/src/page/index.ts +2 -0
- package/dist/templates/examples/auth/src/page/loggedIn/index.ts +4 -0
- package/dist/templates/examples/auth/src/page/loggedIn/message.ts +17 -0
- package/dist/templates/examples/auth/src/page/loggedIn/model.ts +21 -0
- package/dist/templates/examples/auth/src/page/loggedIn/page/dashboard.ts +40 -0
- package/dist/templates/examples/auth/src/page/loggedIn/page/settings.ts +71 -0
- package/dist/templates/examples/auth/src/page/loggedIn/update.ts +16 -0
- package/dist/templates/examples/auth/src/page/loggedIn/view.ts +87 -0
- package/dist/templates/examples/auth/src/page/loggedOut/index.ts +4 -0
- package/dist/templates/examples/auth/src/page/loggedOut/message.ts +20 -0
- package/dist/templates/examples/auth/src/page/loggedOut/model.ts +21 -0
- package/dist/templates/examples/auth/src/page/loggedOut/page/home.ts +68 -0
- package/dist/templates/examples/auth/src/page/loggedOut/page/login.scene.test.ts +116 -0
- package/dist/templates/examples/auth/src/page/loggedOut/page/login.story.test.ts +112 -0
- package/dist/templates/examples/auth/src/page/loggedOut/page/login.ts +346 -0
- package/dist/templates/examples/auth/src/page/loggedOut/update.ts +32 -0
- package/dist/templates/examples/auth/src/page/loggedOut/view.ts +31 -0
- package/dist/templates/examples/auth/src/route.ts +56 -0
- package/dist/templates/examples/auth/src/scene.test.ts +61 -0
- package/dist/templates/examples/auth/src/styles.css +1 -0
- package/dist/templates/examples/auth/src/update.ts +179 -0
- package/dist/templates/examples/auth/src/view.ts +40 -0
- package/dist/templates/examples/auth/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/canvas-art/package.json +29 -0
- package/dist/templates/examples/canvas-art/src/entry.ts +17 -0
- package/dist/templates/examples/canvas-art/src/main.ts +274 -0
- package/dist/templates/examples/canvas-art/src/scene.test.ts +70 -0
- package/dist/templates/examples/canvas-art/src/story.test.ts +155 -0
- package/dist/templates/examples/canvas-art/src/styles.css +1 -0
- package/dist/templates/examples/canvas-art/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/charting/package.json +31 -0
- package/dist/templates/examples/charting/src/chartHost.ts +20 -0
- package/dist/templates/examples/charting/src/command.ts +62 -0
- package/dist/templates/examples/charting/src/domain.ts +175 -0
- package/dist/templates/examples/charting/src/echarts.ts +408 -0
- package/dist/templates/examples/charting/src/entry.ts +25 -0
- package/dist/templates/examples/charting/src/githubApi.ts +271 -0
- package/dist/templates/examples/charting/src/http.ts +50 -0
- package/dist/templates/examples/charting/src/init.test.ts +14 -0
- package/dist/templates/examples/charting/src/init.ts +29 -0
- package/dist/templates/examples/charting/src/main.fixtures.ts +162 -0
- package/dist/templates/examples/charting/src/message.ts +23 -0
- package/dist/templates/examples/charting/src/model.ts +22 -0
- package/dist/templates/examples/charting/src/npmApi.ts +75 -0
- package/dist/templates/examples/charting/src/radioGroups.ts +11 -0
- package/dist/templates/examples/charting/src/scene.test.ts +103 -0
- package/dist/templates/examples/charting/src/story.test.ts +145 -0
- package/dist/templates/examples/charting/src/styles.css +1 -0
- package/dist/templates/examples/charting/src/subscription.ts +70 -0
- package/dist/templates/examples/charting/src/telemetry.test.ts +64 -0
- package/dist/templates/examples/charting/src/telemetry.ts +432 -0
- package/dist/templates/examples/charting/src/update.ts +217 -0
- package/dist/templates/examples/charting/src/view/chart.ts +129 -0
- package/dist/templates/examples/charting/src/view/format.ts +23 -0
- package/dist/templates/examples/charting/src/view/index.ts +1 -0
- package/dist/templates/examples/charting/src/view/layout.ts +175 -0
- package/dist/templates/examples/charting/src/view/sidebar.ts +309 -0
- package/dist/templates/examples/charting/src/view/view.ts +23 -0
- package/dist/templates/examples/charting/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/charting/src/weeks.ts +105 -0
- package/dist/templates/examples/counter/package.json +29 -0
- package/dist/templates/examples/counter/src/entry.ts +16 -0
- package/dist/templates/examples/counter/src/main.ts +95 -0
- package/dist/templates/examples/counter/src/scene.test.ts +57 -0
- package/dist/templates/examples/counter/src/story.test.ts +71 -0
- package/dist/templates/examples/counter/src/styles.css +1 -0
- package/dist/templates/examples/counter/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/counters/package.json +29 -0
- package/dist/templates/examples/counters/src/counter.ts +70 -0
- package/dist/templates/examples/counters/src/entry.ts +16 -0
- package/dist/templates/examples/counters/src/main.ts +171 -0
- package/dist/templates/examples/counters/src/scene.test.ts +90 -0
- package/dist/templates/examples/counters/src/story.test.ts +127 -0
- package/dist/templates/examples/counters/src/styles.css +1 -0
- package/dist/templates/examples/counters/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/crash-view/package.json +29 -0
- package/dist/templates/examples/crash-view/src/entry.ts +28 -0
- package/dist/templates/examples/crash-view/src/main.ts +104 -0
- package/dist/templates/examples/crash-view/src/scene.test.ts +14 -0
- package/dist/templates/examples/crash-view/src/story.test.ts +11 -0
- package/dist/templates/examples/crash-view/src/styles.css +1 -0
- package/dist/templates/examples/crash-view/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/embedding/package.json +29 -0
- package/dist/templates/examples/embedding/src/entry.ts +3 -0
- package/dist/templates/examples/embedding/src/host.ts +108 -0
- package/dist/templates/examples/embedding/src/main.ts +144 -0
- package/dist/templates/examples/embedding/src/scene.test.ts +28 -0
- package/dist/templates/examples/embedding/src/story.test.ts +64 -0
- package/dist/templates/examples/embedding/src/styles.css +1 -0
- package/dist/templates/examples/embedding/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/form/package.json +30 -0
- package/dist/templates/examples/form/src/entry.ts +16 -0
- package/dist/templates/examples/form/src/main.ts +493 -0
- package/dist/templates/examples/form/src/scene.test.ts +165 -0
- package/dist/templates/examples/form/src/story.test.ts +190 -0
- package/dist/templates/examples/form/src/styles.css +1 -0
- package/dist/templates/examples/form/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/generative-art/package.json +29 -0
- package/dist/templates/examples/generative-art/src/command.ts +109 -0
- package/dist/templates/examples/generative-art/src/constant.ts +68 -0
- package/dist/templates/examples/generative-art/src/entry.ts +22 -0
- package/dist/templates/examples/generative-art/src/main.ts +59 -0
- package/dist/templates/examples/generative-art/src/message.ts +35 -0
- package/dist/templates/examples/generative-art/src/model.ts +33 -0
- package/dist/templates/examples/generative-art/src/noise.ts +93 -0
- package/dist/templates/examples/generative-art/src/scene.test.ts +103 -0
- package/dist/templates/examples/generative-art/src/story.test.ts +203 -0
- package/dist/templates/examples/generative-art/src/styles.css +1 -0
- package/dist/templates/examples/generative-art/src/subscription.ts +35 -0
- package/dist/templates/examples/generative-art/src/update.ts +319 -0
- package/dist/templates/examples/generative-art/src/view.ts +451 -0
- package/dist/templates/examples/generative-art/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/interrupting-commands/package.json +29 -0
- package/dist/templates/examples/interrupting-commands/src/entry.ts +16 -0
- package/dist/templates/examples/interrupting-commands/src/main.ts +354 -0
- package/dist/templates/examples/interrupting-commands/src/scene.test.ts +89 -0
- package/dist/templates/examples/interrupting-commands/src/story.test.ts +224 -0
- package/dist/templates/examples/interrupting-commands/src/styles.css +1 -0
- package/dist/templates/examples/interrupting-commands/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/job-application/package.json +30 -0
- package/dist/templates/examples/job-application/src/command.ts +23 -0
- package/dist/templates/examples/job-application/src/domain/index.ts +3 -0
- package/dist/templates/examples/job-application/src/domain/proficiencyLevel.ts +11 -0
- package/dist/templates/examples/job-application/src/domain/pronounOption.ts +13 -0
- package/dist/templates/examples/job-application/src/domain/step.ts +28 -0
- package/dist/templates/examples/job-application/src/entry.ts +17 -0
- package/dist/templates/examples/job-application/src/main.ts +69 -0
- package/dist/templates/examples/job-application/src/message.ts +44 -0
- package/dist/templates/examples/job-application/src/model.ts +47 -0
- package/dist/templates/examples/job-application/src/scene.test.ts +269 -0
- package/dist/templates/examples/job-application/src/step/attachments.ts +111 -0
- package/dist/templates/examples/job-application/src/step/coverLetter.ts +34 -0
- package/dist/templates/examples/job-application/src/step/education/education.ts +121 -0
- package/dist/templates/examples/job-application/src/step/education/entry.ts +168 -0
- package/dist/templates/examples/job-application/src/step/education/index.ts +2 -0
- package/dist/templates/examples/job-application/src/step/index.ts +6 -0
- package/dist/templates/examples/job-application/src/step/personalInfo.ts +305 -0
- package/dist/templates/examples/job-application/src/step/skills/entry.ts +126 -0
- package/dist/templates/examples/job-application/src/step/skills/index.ts +2 -0
- package/dist/templates/examples/job-application/src/step/skills/skills.ts +118 -0
- package/dist/templates/examples/job-application/src/step/validation.ts +6 -0
- package/dist/templates/examples/job-application/src/step/workHistory/entry.ts +206 -0
- package/dist/templates/examples/job-application/src/step/workHistory/index.ts +2 -0
- package/dist/templates/examples/job-application/src/step/workHistory/workHistory.ts +121 -0
- package/dist/templates/examples/job-application/src/story.test.ts +775 -0
- package/dist/templates/examples/job-application/src/styles.css +1 -0
- package/dist/templates/examples/job-application/src/update.ts +183 -0
- package/dist/templates/examples/job-application/src/view/attachments.ts +215 -0
- package/dist/templates/examples/job-application/src/view/coverLetter.ts +73 -0
- package/dist/templates/examples/job-application/src/view/datePicker.ts +207 -0
- package/dist/templates/examples/job-application/src/view/education.ts +63 -0
- package/dist/templates/examples/job-application/src/view/educationEntry.ts +165 -0
- package/dist/templates/examples/job-application/src/view/field.ts +183 -0
- package/dist/templates/examples/job-application/src/view/format.ts +50 -0
- package/dist/templates/examples/job-application/src/view/icon.ts +21 -0
- package/dist/templates/examples/job-application/src/view/index.ts +352 -0
- package/dist/templates/examples/job-application/src/view/personalInfo.ts +224 -0
- package/dist/templates/examples/job-application/src/view/preview.ts +265 -0
- package/dist/templates/examples/job-application/src/view/review.ts +388 -0
- package/dist/templates/examples/job-application/src/view/skillEntry.ts +102 -0
- package/dist/templates/examples/job-application/src/view/skills.ts +49 -0
- package/dist/templates/examples/job-application/src/view/stepNav.ts +181 -0
- package/dist/templates/examples/job-application/src/view/workEntry.ts +159 -0
- package/dist/templates/examples/job-application/src/view/workHistory.ts +55 -0
- package/dist/templates/examples/job-application/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/kanban/package.json +31 -0
- package/dist/templates/examples/kanban/src/command.ts +46 -0
- package/dist/templates/examples/kanban/src/constant.ts +149 -0
- package/dist/templates/examples/kanban/src/domain/card.ts +10 -0
- package/dist/templates/examples/kanban/src/domain/column.ts +115 -0
- package/dist/templates/examples/kanban/src/domain/index.ts +2 -0
- package/dist/templates/examples/kanban/src/entry.ts +27 -0
- package/dist/templates/examples/kanban/src/main.ts +56 -0
- package/dist/templates/examples/kanban/src/message.ts +21 -0
- package/dist/templates/examples/kanban/src/model.ts +21 -0
- package/dist/templates/examples/kanban/src/scene.test.ts +161 -0
- package/dist/templates/examples/kanban/src/story.test.ts +461 -0
- package/dist/templates/examples/kanban/src/styles.css +1 -0
- package/dist/templates/examples/kanban/src/subscription.ts +16 -0
- package/dist/templates/examples/kanban/src/update.ts +205 -0
- package/dist/templates/examples/kanban/src/view/card.ts +79 -0
- package/dist/templates/examples/kanban/src/view/column.ts +283 -0
- package/dist/templates/examples/kanban/src/view/index.ts +71 -0
- package/dist/templates/examples/kanban/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/managed-resource-layer/package.json +29 -0
- package/dist/templates/examples/managed-resource-layer/src/entry.ts +17 -0
- package/dist/templates/examples/managed-resource-layer/src/main.ts +309 -0
- package/dist/templates/examples/managed-resource-layer/src/scene.test.ts +117 -0
- package/dist/templates/examples/managed-resource-layer/src/story.test.ts +118 -0
- package/dist/templates/examples/managed-resource-layer/src/styles.css +1 -0
- package/dist/templates/examples/managed-resource-layer/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/map/package.json +31 -0
- package/dist/templates/examples/map/src/entry.ts +17 -0
- package/dist/templates/examples/map/src/locations.ts +69 -0
- package/dist/templates/examples/map/src/main.fixtures.ts +28 -0
- package/dist/templates/examples/map/src/main.ts +781 -0
- package/dist/templates/examples/map/src/mapHost.ts +23 -0
- package/dist/templates/examples/map/src/scene.test.ts +136 -0
- package/dist/templates/examples/map/src/story.test.ts +190 -0
- package/dist/templates/examples/map/src/styles.css +2 -0
- package/dist/templates/examples/map/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/pixel-art/package.json +31 -0
- package/dist/templates/examples/pixel-art/src/command.ts +87 -0
- package/dist/templates/examples/pixel-art/src/comparison.bench.test.ts +204 -0
- package/dist/templates/examples/pixel-art/src/constant.ts +21 -0
- package/dist/templates/examples/pixel-art/src/entry.ts +27 -0
- package/dist/templates/examples/pixel-art/src/grid.ts +134 -0
- package/dist/templates/examples/pixel-art/src/main.bench.ts +133 -0
- package/dist/templates/examples/pixel-art/src/main.ts +85 -0
- package/dist/templates/examples/pixel-art/src/message.ts +36 -0
- package/dist/templates/examples/pixel-art/src/model.ts +79 -0
- package/dist/templates/examples/pixel-art/src/palette.ts +134 -0
- package/dist/templates/examples/pixel-art/src/scene.test.ts +250 -0
- package/dist/templates/examples/pixel-art/src/story.test.ts +482 -0
- package/dist/templates/examples/pixel-art/src/styles.css +1 -0
- package/dist/templates/examples/pixel-art/src/subscription.ts +61 -0
- package/dist/templates/examples/pixel-art/src/update.ts +469 -0
- package/dist/templates/examples/pixel-art/src/view/canvas.ts +134 -0
- package/dist/templates/examples/pixel-art/src/view/dialog.ts +152 -0
- package/dist/templates/examples/pixel-art/src/view/history.ts +214 -0
- package/dist/templates/examples/pixel-art/src/view/index.ts +1 -0
- package/dist/templates/examples/pixel-art/src/view/toolbar.ts +452 -0
- package/dist/templates/examples/pixel-art/src/view/view.ts +183 -0
- package/dist/templates/examples/pixel-art/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/query-sync/package.json +30 -0
- package/dist/templates/examples/query-sync/src/data.ts +166 -0
- package/dist/templates/examples/query-sync/src/entry.ts +20 -0
- package/dist/templates/examples/query-sync/src/main.ts +898 -0
- package/dist/templates/examples/query-sync/src/scene.test.ts +89 -0
- package/dist/templates/examples/query-sync/src/story.test.ts +151 -0
- package/dist/templates/examples/query-sync/src/styles.css +1 -0
- package/dist/templates/examples/query-sync/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/route-transitions/package.json +28 -0
- package/dist/templates/examples/route-transitions/src/data.ts +61 -0
- package/dist/templates/examples/route-transitions/src/entry.ts +20 -0
- package/dist/templates/examples/route-transitions/src/main.ts +798 -0
- package/dist/templates/examples/route-transitions/src/route.ts +48 -0
- package/dist/templates/examples/route-transitions/src/story.test.ts +225 -0
- package/dist/templates/examples/route-transitions/src/styles.css +1 -0
- package/dist/templates/examples/route-transitions/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/routing/package.json +29 -0
- package/dist/templates/examples/routing/src/entry.ts +20 -0
- package/dist/templates/examples/routing/src/fileTree.ts +98 -0
- package/dist/templates/examples/routing/src/main.ts +573 -0
- package/dist/templates/examples/routing/src/page/index.ts +1 -0
- package/dist/templates/examples/routing/src/page/people.ts +349 -0
- package/dist/templates/examples/routing/src/route.ts +82 -0
- package/dist/templates/examples/routing/src/scene.test.ts +180 -0
- package/dist/templates/examples/routing/src/story.test.ts +242 -0
- package/dist/templates/examples/routing/src/styles.css +1 -0
- package/dist/templates/examples/routing/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/shopping-cart/package.json +29 -0
- package/dist/templates/examples/shopping-cart/src/data/products.ts +10 -0
- package/dist/templates/examples/shopping-cart/src/domain/cart.ts +77 -0
- package/dist/templates/examples/shopping-cart/src/domain/index.ts +2 -0
- package/dist/templates/examples/shopping-cart/src/domain/item.ts +16 -0
- package/dist/templates/examples/shopping-cart/src/entry.ts +20 -0
- package/dist/templates/examples/shopping-cart/src/main.ts +307 -0
- package/dist/templates/examples/shopping-cart/src/page/cart.ts +203 -0
- package/dist/templates/examples/shopping-cart/src/page/checkout.ts +205 -0
- package/dist/templates/examples/shopping-cart/src/page/index.ts +3 -0
- package/dist/templates/examples/shopping-cart/src/page/products.ts +267 -0
- package/dist/templates/examples/shopping-cart/src/route.ts +43 -0
- package/dist/templates/examples/shopping-cart/src/scene.test.ts +138 -0
- package/dist/templates/examples/shopping-cart/src/story.test.ts +233 -0
- package/dist/templates/examples/shopping-cart/src/styles.css +1 -0
- package/dist/templates/examples/shopping-cart/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/slow-warnings/package.json +28 -0
- package/dist/templates/examples/slow-warnings/src/entry.ts +29 -0
- package/dist/templates/examples/slow-warnings/src/handleSlow.test.ts +43 -0
- package/dist/templates/examples/slow-warnings/src/main.ts +574 -0
- package/dist/templates/examples/slow-warnings/src/scene.test.ts +57 -0
- package/dist/templates/examples/slow-warnings/src/story.test.ts +118 -0
- package/dist/templates/examples/slow-warnings/src/styles.css +1 -0
- package/dist/templates/examples/slow-warnings/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/snake/package.json +28 -0
- package/dist/templates/examples/snake/src/constants.ts +11 -0
- package/dist/templates/examples/snake/src/domain/apple.ts +22 -0
- package/dist/templates/examples/snake/src/domain/direction.ts +17 -0
- package/dist/templates/examples/snake/src/domain/index.ts +4 -0
- package/dist/templates/examples/snake/src/domain/position.ts +32 -0
- package/dist/templates/examples/snake/src/domain/snake.ts +43 -0
- package/dist/templates/examples/snake/src/entry.ts +17 -0
- package/dist/templates/examples/snake/src/main.ts +345 -0
- package/dist/templates/examples/snake/src/scene.test.ts +77 -0
- package/dist/templates/examples/snake/src/story.test.ts +165 -0
- package/dist/templates/examples/snake/src/styles.css +1 -0
- package/dist/templates/examples/snake/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/ssg/package.json +29 -0
- package/dist/templates/examples/ssg/src/entry.server.ts +14 -0
- package/{templates/rendering → dist/templates/examples}/ssg/src/entry.ts +3 -11
- package/dist/templates/examples/ssg/src/main.ts +163 -0
- package/dist/templates/examples/ssg/src/styles.css +1 -0
- package/dist/templates/examples/ssr/package.json +33 -0
- package/dist/templates/examples/ssr/src/cookie.ts +13 -0
- package/dist/templates/examples/ssr/src/entry.server.ts +51 -0
- package/dist/templates/examples/ssr/src/main.ts +170 -0
- package/{templates/rendering → dist/templates/examples}/ssr/src/scene.test.ts +5 -8
- package/dist/templates/examples/ssr/src/styles.css +1 -0
- package/dist/templates/examples/ssr/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/state-machine/package.json +30 -0
- package/dist/templates/examples/state-machine/src/entry.ts +17 -0
- package/dist/templates/examples/state-machine/src/icon.ts +51 -0
- package/dist/templates/examples/state-machine/src/main.ts +420 -0
- package/dist/templates/examples/state-machine/src/scene.test.ts +85 -0
- package/dist/templates/examples/state-machine/src/story.test.ts +199 -0
- package/dist/templates/examples/state-machine/src/styles.css +53 -0
- package/dist/templates/examples/state-machine/src/view.ts +1675 -0
- package/dist/templates/examples/state-machine/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/stopwatch/package.json +29 -0
- package/dist/templates/examples/stopwatch/src/entry.ts +17 -0
- package/dist/templates/examples/stopwatch/src/main.ts +237 -0
- package/dist/templates/examples/stopwatch/src/scene.test.ts +124 -0
- package/dist/templates/examples/stopwatch/src/story.test.ts +126 -0
- package/dist/templates/examples/stopwatch/src/styles.css +1 -0
- package/dist/templates/examples/stopwatch/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/todo/package.json +30 -0
- package/dist/templates/examples/todo/src/entry.ts +17 -0
- package/dist/templates/examples/todo/src/main.ts +682 -0
- package/dist/templates/examples/todo/src/scene.test.ts +177 -0
- package/dist/templates/examples/todo/src/story.test.ts +374 -0
- package/dist/templates/examples/todo/src/styles.css +1 -0
- package/dist/templates/examples/todo/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/ui-showcase/package.json +30 -0
- package/dist/templates/examples/ui-showcase/src/entry.ts +31 -0
- package/dist/templates/examples/ui-showcase/src/icon.ts +64 -0
- package/dist/templates/examples/ui-showcase/src/main.ts +673 -0
- package/dist/templates/examples/ui-showcase/src/scene.test.ts +102 -0
- package/dist/templates/examples/ui-showcase/src/story.test.ts +128 -0
- package/dist/templates/examples/ui-showcase/src/styles.css +21 -0
- package/dist/templates/examples/ui-showcase/src/ui/init.ts +185 -0
- package/dist/templates/examples/ui-showcase/src/ui/message.ts +164 -0
- package/dist/templates/examples/ui-showcase/src/ui/model.ts +138 -0
- package/dist/templates/examples/ui-showcase/src/ui/subscriptions.ts +55 -0
- package/dist/templates/examples/ui-showcase/src/ui/toast.ts +13 -0
- package/dist/templates/examples/ui-showcase/src/ui/update.ts +1234 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/animation.ts +58 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/button.ts +64 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/calendar.ts +202 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/checkbox.ts +185 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/combobox.ts +326 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/datePicker.ts +263 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/dialog.ts +427 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/disclosure.ts +142 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/dragAndDrop.ts +230 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/fieldset.ts +284 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/fileDrop.ts +119 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/index.ts +24 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/input.ts +90 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/listbox.ts +263 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/menu.ts +172 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/popover.ts +262 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/radioGroup.ts +192 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/select.ts +129 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/slider.ts +135 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/switch.ts +82 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/tabs.ts +187 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/textarea.ts +99 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/toast.ts +165 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/tooltip.ts +123 -0
- package/dist/templates/examples/ui-showcase/src/ui/view/virtualList.ts +361 -0
- package/dist/templates/examples/ui-showcase/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/view-transitions/package.json +28 -0
- package/dist/templates/examples/view-transitions/src/artwork.ts +80 -0
- package/dist/templates/examples/view-transitions/src/entry.ts +21 -0
- package/dist/templates/examples/view-transitions/src/main.ts +348 -0
- package/dist/templates/examples/view-transitions/src/route.ts +29 -0
- package/dist/templates/examples/view-transitions/src/scene.test.ts +130 -0
- package/dist/templates/examples/view-transitions/src/styles.css +66 -0
- package/dist/templates/examples/view-transitions/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/weather/package.json +29 -0
- package/dist/templates/examples/weather/src/entry.ts +16 -0
- package/dist/templates/examples/weather/src/main.fixtures.ts +59 -0
- package/dist/templates/examples/weather/src/main.ts +356 -0
- package/dist/templates/examples/weather/src/scene.test.ts +109 -0
- package/dist/templates/examples/weather/src/story.test.ts +125 -0
- package/dist/templates/examples/weather/src/styles.css +1 -0
- package/dist/templates/examples/weather/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/web-components/package.json +32 -0
- package/dist/templates/examples/web-components/src/entry.ts +16 -0
- package/dist/templates/examples/web-components/src/main.ts +327 -0
- package/dist/templates/examples/web-components/src/scene.test.ts +90 -0
- package/dist/templates/examples/web-components/src/story.test.ts +90 -0
- package/dist/templates/examples/web-components/src/styles.css +1 -0
- package/dist/templates/examples/web-components/src/vitest-setup.ts +3 -0
- package/dist/templates/examples/websocket-chat/package.json +29 -0
- package/dist/templates/examples/websocket-chat/src/entry.ts +26 -0
- package/dist/templates/examples/websocket-chat/src/main.ts +626 -0
- package/dist/templates/examples/websocket-chat/src/scene.test.ts +166 -0
- package/dist/templates/examples/websocket-chat/src/story.test.ts +183 -0
- package/dist/templates/examples/websocket-chat/src/styles.css +1 -0
- package/dist/templates/examples/websocket-chat/src/vitest-setup.ts +3 -0
- package/dist/templates/release.json +27 -0
- package/dist/templates/rendering/ssg/src/entry.ts +20 -0
- package/{templates → dist/templates}/rendering/ssg/src/main.ts +36 -43
- package/dist/templates/rendering/ssg/src/route.ts +20 -0
- package/dist/templates/rendering/ssg/src/vite-env.d.ts +19 -0
- package/dist/templates/rendering/ssr/src/entry.ts +17 -0
- package/{templates → dist/templates}/rendering/ssr/src/main.ts +17 -25
- package/dist/templates/rendering/ssr/src/scene.test.ts +40 -0
- package/dist/templates/rendering/ssr/src/vite-env.d.ts +19 -0
- package/dist/utils/files.js +23 -71
- package/dist/utils/packages.js +63 -44
- package/package.json +3 -4
- /package/{templates → dist/templates}/base/.mcp.json +0 -0
- /package/{templates → dist/templates}/base/.oxlintrc.json +0 -0
- /package/{templates → dist/templates}/base/.prettierignore +0 -0
- /package/{templates → dist/templates}/base/.prettierrc +0 -0
- /package/{templates → dist/templates}/base/README.md +0 -0
- /package/{templates → dist/templates}/base/gitignore +0 -0
- /package/{templates → dist/templates}/base/ignore +0 -0
- /package/{templates → dist/templates}/base/index.html +0 -0
- /package/{templates → dist/templates}/base/package.json +0 -0
- /package/{templates → dist/templates}/base/src/styles.css +0 -0
- /package/{templates → dist/templates}/base/src/vitest-setup.ts +0 -0
- /package/{templates → dist/templates}/base/tsconfig.json +0 -0
- /package/{templates → dist/templates}/base/vite.config.ts +0 -0
- /package/{templates → dist/templates}/base/vitest.config.ts +0 -0
- /package/{templates/rendering → dist/templates/examples}/ssg/src/route.ts +0 -0
- /package/{templates/rendering → dist/templates/examples}/ssg/src/vite-env.d.ts +0 -0
- /package/{templates/rendering → dist/templates/examples}/ssr/src/entry.ts +0 -0
- /package/{templates/rendering → dist/templates/examples}/ssr/src/vite-env.d.ts +0 -0
- /package/{templates → dist/templates}/package-managers/pnpm/pnpm-workspace.yaml +0 -0
- /package/{templates → dist/templates}/rendering/ssg/README.md +0 -0
- /package/{templates → dist/templates}/rendering/ssg/package.json +0 -0
- /package/{templates → dist/templates}/rendering/ssg/scripts/build.mjs +0 -0
- /package/{templates → dist/templates}/rendering/ssg/scripts/prerender.ts +0 -0
- /package/{templates → dist/templates}/rendering/ssg/src/entry.server.ts +0 -0
- /package/{templates → dist/templates}/rendering/ssg/src/scene.test.ts +0 -0
- /package/{templates → dist/templates}/rendering/ssg/tsconfig.json +0 -0
- /package/{templates → dist/templates}/rendering/ssg/vite.config.ts +0 -0
- /package/{templates → dist/templates}/rendering/ssr/README.md +0 -0
- /package/{templates → dist/templates}/rendering/ssr/package.json +0 -0
- /package/{templates → dist/templates}/rendering/ssr/scripts/build.mjs +0 -0
- /package/{templates → dist/templates}/rendering/ssr/server/main.ts +0 -0
- /package/{templates → dist/templates}/rendering/ssr/src/cookie.ts +0 -0
- /package/{templates → dist/templates}/rendering/ssr/src/entry.server.ts +0 -0
- /package/{templates → dist/templates}/rendering/ssr/tsconfig.json +0 -0
- /package/{templates → dist/templates}/rendering/ssr/vite.config.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Effect, Layer, Option, Schema } from 'effect';
|
|
3
3
|
import { Command, Flag } from 'effect/unstable/cli';
|
|
4
|
-
import { FetchHttpClient } from 'effect/unstable/http';
|
|
5
4
|
import { createRequire } from 'node:module';
|
|
6
5
|
import { NodeRuntime, NodeServices, NodeStdio } from '@effect/platform-node';
|
|
7
6
|
import { create as create_ } from './commands/create.js';
|
|
@@ -33,7 +32,4 @@ const create = Command.make('create', {
|
|
|
33
32
|
const cli = Command.run(create, {
|
|
34
33
|
version: packageJson.version,
|
|
35
34
|
});
|
|
36
|
-
cli.pipe(Effect.provide([
|
|
37
|
-
FetchHttpClient.layer,
|
|
38
|
-
Layer.mergeAll(NodeServices.layer, NodeStdio.layer),
|
|
39
|
-
]), NodeRuntime.runMain);
|
|
35
|
+
cli.pipe(Effect.provide([Layer.mergeAll(NodeServices.layer, NodeStdio.layer)]), NodeRuntime.runMain);
|
|
@@ -25,7 +25,7 @@ If `foldkit-skills` is installed as a Claude Code plugin, the `generate-program`
|
|
|
25
25
|
- Foldkit is tightly coupled to the Effect ecosystem. Do not suggest solutions outside of Effect-TS.
|
|
26
26
|
- Model fields must be Schema types (the model is a schema). Plain TypeScript types are fine elsewhere (function return types, local variables, etc.).
|
|
27
27
|
- Use full names like `Message` (not `Msg`), and `withReturnType` (not `as const` or type casting).
|
|
28
|
-
- Use `
|
|
28
|
+
- Use `defineMessageUnion()` for Message unions, `ts()` for tagged structs (Model states, field validation), and `r()` for route schemas.
|
|
29
29
|
- Push back on any direction that violates Elm Architecture principles: unidirectional data flow, messages as facts (not commands), model as single source of truth, side effects confined to commands. If a prompt suggests mutating state, imperative event handlers, or two-way bindings, flag the issue and propose the idiomatic Foldkit approach.
|
|
30
30
|
- Never use `NoOp`. Every message must describe what happened. A command's result message is named from the command, not from the fact it reports, whether or not it carries a payload: `LockScroll` → `CompletedLockScroll`, `DetermineStartTime` → `CompletedDetermineStartTime` (never `DeterminedStartTime`).
|
|
31
31
|
|
|
@@ -37,15 +37,11 @@ If `foldkit-skills` is installed as a Claude Code plugin, the `generate-program`
|
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
39
|
type UpdateReturn = readonly [Model, ReadonlyArray<Command<Message>>]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
M.tagsExhaustive({
|
|
46
|
-
ClickedIncrement: () => [evo(model, { count: count => count + 1 }), []],
|
|
47
|
-
}),
|
|
48
|
-
)
|
|
40
|
+
|
|
41
|
+
const update = (model: Model, message: Message) =>
|
|
42
|
+
Message.match<UpdateReturn>(message, {
|
|
43
|
+
ClickedIncrement: () => [evo(model, { count: count => count + 1 }), []],
|
|
44
|
+
})
|
|
49
45
|
```
|
|
50
46
|
|
|
51
47
|
Use `evo()` from `foldkit/struct` for immutable model updates. Never spread or `Object.assign`.
|
|
@@ -60,7 +56,7 @@ Omit the children argument when an element has none: `h.div([h.Class('divider')]
|
|
|
60
56
|
|
|
61
57
|
### Commands
|
|
62
58
|
|
|
63
|
-
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(FailedX(...)))` so side effects never crash the app. Definitions live colocated with the update function that returns them.
|
|
59
|
+
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.
|
|
64
60
|
|
|
65
61
|
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.
|
|
66
62
|
|
|
@@ -89,27 +85,29 @@ Scene runs at any level, since a page's own `update`/`view` pair drops into `sce
|
|
|
89
85
|
- Encode state in discriminated unions, not booleans or nullable fields. `Idle | Loading | Error | Ok`, not `isLoading: boolean`. Make impossible states unrepresentable.
|
|
90
86
|
- Use `Option` instead of `null` or `undefined`. Prefix Option-typed values with `maybe*`. Match with `Option.match`; don't unwrap with `Option.map(...)` + `Option.getOrElse(...)` when you can just match.
|
|
91
87
|
- Use Effect modules over native methods in `pipe` chains (`Array.map`, `String.startsWith`, `Array.findFirst`). Native methods are fine when calling directly on a named variable.
|
|
92
|
-
- Never cast Schema values with `as Type`. Use the callable constructor: `SucceededLogin({ sessionId })`, not `{ _tag: 'SucceededLogin', sessionId } as Message`.
|
|
88
|
+
- Never cast Schema values with `as Type`. Use the callable constructor: `Message.SucceededLogin({ sessionId })`, not `{ _tag: 'SucceededLogin', sessionId } as Message`.
|
|
93
89
|
- Always `Array.isArrayEmpty` / `Array.isArrayNonEmpty` (not `.length === 0` / `.length > 0`). Use `Array.match` when handling both empty and non-empty cases.
|
|
94
90
|
- Never use `for` loops or `let` for iteration. Reach for `Array.map`, `Array.filterMap`, `Array.makeBy`, `Array.reduce`.
|
|
95
91
|
- Never use `T[]`. Always `Array<T>` or `ReadonlyArray<T>`.
|
|
96
|
-
-
|
|
92
|
+
- Use `Message.match` for exhaustive Message matching. Use Effect `Match` for other tagged unions, partial matching, fallbacks, and one handler shared across multiple tags. Never use `switch`.
|
|
97
93
|
- Always use braces for control flow: `if (foo) { return true }`.
|
|
98
94
|
- Don't add inline comments to explain code. Use better names instead. Reserve `// NOTE:` for behavior that would mislead a careful reader.
|
|
99
95
|
|
|
100
96
|
## Message Layout
|
|
101
97
|
|
|
102
|
-
|
|
98
|
+
Declare the whole Message union with `defineMessageUnion()`, then put `type Message = typeof Message.Type` on the next line:
|
|
103
99
|
|
|
104
100
|
```ts
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
const Message = defineMessageUnion({
|
|
102
|
+
ClickedSubmit: {},
|
|
103
|
+
UpdatedEmail: { value: S.String },
|
|
104
|
+
})
|
|
109
105
|
type Message = typeof Message.Type
|
|
110
106
|
```
|
|
111
107
|
|
|
112
|
-
Keep the
|
|
108
|
+
Keep the `defineMessageUnion()` declaration and `type Message` alias adjacent. Construct values through the namespace (`Message.ClickedSubmit()`) and handle the union with `Message.match`. Never destructure constructors from `Message` or `OutMessage`; the owning namespace stays visible at every call site.
|
|
109
|
+
|
|
110
|
+
Keep each case's payload object on one line when it fits. Let Prettier wrap payloads that need more space, so the declaration remains easy to scan as one variant per line.
|
|
113
111
|
|
|
114
112
|
Messages are verb-first past-tense. Common prefixes: `Clicked*`, `Updated*` (input changes and external state updates), `Submitted*`, `Pressed*`, `Selected*`, `Succeeded*` / `Failed*` (paired async results), `Completed*` (every other Command result), `Got*` (child OutMessage in the Submodel pattern).
|
|
115
113
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "api-cache-example",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"typecheck": "tsc --noEmit",
|
|
9
|
+
"test": "vitest run"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@effect/platform-browser": "4.0.0-rc.109",
|
|
13
|
+
"@foldkit/ui": "workspace:*",
|
|
14
|
+
"@tailwindcss/vite": "^4.3.1",
|
|
15
|
+
"effect": "4.0.0-rc.109",
|
|
16
|
+
"foldkit": "workspace:*",
|
|
17
|
+
"tailwindcss": "^4.3.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@foldkit/devtools": "workspace:*",
|
|
21
|
+
"@foldkit/vite-plugin": "workspace:*",
|
|
22
|
+
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
23
|
+
"happy-dom": "^20.10.4",
|
|
24
|
+
"prettier": "^3.8.4",
|
|
25
|
+
"typescript": "^6.0.3",
|
|
26
|
+
"vite": "^8.0.16",
|
|
27
|
+
"vitest": "^4.1.9"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Effect, Exit } from 'effect'
|
|
2
|
+
import { expect, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { FLAKY_POST_ID, fetchPostDetail } from './data'
|
|
5
|
+
|
|
6
|
+
test('a regular post resolves with its detail', async () => {
|
|
7
|
+
const detail = await Effect.runPromise(fetchPostDetail('model-is-the-cache'))
|
|
8
|
+
|
|
9
|
+
expect(detail.title).toBe('The Model Is the Cache')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test('the flaky post fails on the first fetch and succeeds on retry', async () => {
|
|
13
|
+
const firstAttempt = await Effect.runPromiseExit(
|
|
14
|
+
fetchPostDetail(FLAKY_POST_ID),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
expect(Exit.isFailure(firstAttempt)).toBe(true)
|
|
18
|
+
|
|
19
|
+
const secondAttempt = await Effect.runPromise(fetchPostDetail(FLAKY_POST_ID))
|
|
20
|
+
|
|
21
|
+
expect(secondAttempt.id).toBe(FLAKY_POST_ID)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('an unknown post id fails', async () => {
|
|
25
|
+
const attempt = await Effect.runPromiseExit(fetchPostDetail('does-not-exist'))
|
|
26
|
+
|
|
27
|
+
expect(Exit.isFailure(attempt)).toBe(true)
|
|
28
|
+
})
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Array, Duration, Effect, Option, Random, Schema as S } from 'effect'
|
|
2
|
+
|
|
3
|
+
export const Post = S.Struct({
|
|
4
|
+
id: S.String,
|
|
5
|
+
title: S.String,
|
|
6
|
+
excerpt: S.String,
|
|
7
|
+
})
|
|
8
|
+
export type Post = typeof Post.Type
|
|
9
|
+
|
|
10
|
+
export const PostDetail = S.Struct({
|
|
11
|
+
id: S.String,
|
|
12
|
+
title: S.String,
|
|
13
|
+
author: S.String,
|
|
14
|
+
body: S.String,
|
|
15
|
+
})
|
|
16
|
+
export type PostDetail = typeof PostDetail.Type
|
|
17
|
+
|
|
18
|
+
export const Stats = S.Struct({
|
|
19
|
+
activeUsers: S.Number,
|
|
20
|
+
requestsPerSecond: S.Number,
|
|
21
|
+
cacheHitRatePercent: S.Number,
|
|
22
|
+
})
|
|
23
|
+
export type Stats = typeof Stats.Type
|
|
24
|
+
|
|
25
|
+
export const FLAKY_POST_ID = 'flaky-connection'
|
|
26
|
+
|
|
27
|
+
const SERVER_LATENCY = Duration.millis(700)
|
|
28
|
+
|
|
29
|
+
type Article = Readonly<{
|
|
30
|
+
id: string
|
|
31
|
+
title: string
|
|
32
|
+
excerpt: string
|
|
33
|
+
author: string
|
|
34
|
+
body: string
|
|
35
|
+
}>
|
|
36
|
+
|
|
37
|
+
const articles: ReadonlyArray<Article> = [
|
|
38
|
+
{
|
|
39
|
+
id: 'model-is-the-cache',
|
|
40
|
+
title: 'The Model Is the Cache',
|
|
41
|
+
excerpt: 'Why a single source of truth needs no query client.',
|
|
42
|
+
author: 'Maya Okafor',
|
|
43
|
+
body: 'A cache is a place where fetched data lives between requests. In The Elm Architecture that place already exists: the Model. Store each query as a small state machine and every view reads the same truth.',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'stale-while-revalidate',
|
|
47
|
+
title: 'Stale-While-Revalidate, Explained',
|
|
48
|
+
excerpt: 'Show the old data while the new data loads.',
|
|
49
|
+
author: 'Theo Lindqvist',
|
|
50
|
+
body: 'Dropping back to a spinner throws away perfectly good data. A Refreshing state carries the previous value while the fetch runs, so the screen never goes blank.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'query-keys-are-names',
|
|
54
|
+
title: 'Query Keys Are Just Names',
|
|
55
|
+
excerpt: 'A Model field per query replaces stringly-typed keys.',
|
|
56
|
+
author: 'Priya Raman',
|
|
57
|
+
body: 'When queries are known statically, the field name is the key. Reach for a HashMap keyed by a domain identifier only when the entries are genuinely dynamic, like these post details.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'invalidation-is-a-message',
|
|
61
|
+
title: 'Invalidation Is a Message',
|
|
62
|
+
excerpt: 'Marking data stale is a fact, not a framework feature.',
|
|
63
|
+
author: 'Jonas Weber',
|
|
64
|
+
body: 'Invalidation means the cached value can no longer be trusted. Dispatch a Message, move the entry to Refreshing, and return the fetch Command. The whole policy is visible in update.',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: FLAKY_POST_ID,
|
|
68
|
+
title: 'This Post Fails Every Other Fetch',
|
|
69
|
+
excerpt: 'Open it to see the Failure state, then retry.',
|
|
70
|
+
author: 'Flaky McNetwork',
|
|
71
|
+
body: 'You made it. The fake server failed your first attempt on purpose and succeeded on the retry, which is exactly the round trip a Failure state plus a retry Message is for.',
|
|
72
|
+
},
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
const posts = Array.map(articles, ({ id, title, excerpt }) =>
|
|
76
|
+
Post.make({ id, title, excerpt }),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
const postDetails = Array.map(articles, ({ id, title, author, body }) =>
|
|
80
|
+
PostDetail.make({ id, title, author, body }),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
export const fetchPosts = Effect.gen(function* () {
|
|
84
|
+
yield* Effect.sleep(SERVER_LATENCY)
|
|
85
|
+
|
|
86
|
+
return posts
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// NOTE: Module-level mutation simulates a flaky server so the Failure and
|
|
90
|
+
// retry path is reachable from the UI. The Foldkit app itself never mutates.
|
|
91
|
+
const flakyAttempts = { count: 0 }
|
|
92
|
+
|
|
93
|
+
export const fetchPostDetail = (
|
|
94
|
+
postId: string,
|
|
95
|
+
): Effect.Effect<PostDetail, string> =>
|
|
96
|
+
Effect.gen(function* () {
|
|
97
|
+
yield* Effect.sleep(SERVER_LATENCY)
|
|
98
|
+
|
|
99
|
+
if (postId === FLAKY_POST_ID) {
|
|
100
|
+
flakyAttempts.count += 1
|
|
101
|
+
|
|
102
|
+
if (flakyAttempts.count % 2 === 1) {
|
|
103
|
+
return yield* Effect.fail(
|
|
104
|
+
'The connection dropped. Retry to fetch this post again.',
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return yield* Option.match(
|
|
110
|
+
Array.findFirst(postDetails, ({ id }) => id === postId),
|
|
111
|
+
{
|
|
112
|
+
onNone: () => Effect.fail(`No post found with id ${postId}`),
|
|
113
|
+
onSome: Effect.succeed,
|
|
114
|
+
},
|
|
115
|
+
)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
export const fetchStats = Effect.gen(function* () {
|
|
119
|
+
yield* Effect.sleep(SERVER_LATENCY)
|
|
120
|
+
|
|
121
|
+
const activeUsers = yield* Random.nextIntBetween(80, 140)
|
|
122
|
+
const requestsPerSecond = yield* Random.nextIntBetween(900, 1600)
|
|
123
|
+
const cacheHitRatePercent = yield* Random.nextIntBetween(86, 99)
|
|
124
|
+
|
|
125
|
+
return Stats.make({ activeUsers, requestsPerSecond, cacheHitRatePercent })
|
|
126
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Runtime } from 'foldkit'
|
|
2
|
+
|
|
3
|
+
import { Message, Model, init, subscriptions, update, view } from './main'
|
|
4
|
+
|
|
5
|
+
const application = Runtime.makeApplication({
|
|
6
|
+
Model,
|
|
7
|
+
init,
|
|
8
|
+
update,
|
|
9
|
+
view,
|
|
10
|
+
subscriptions,
|
|
11
|
+
container: document.getElementById('root'),
|
|
12
|
+
devTools: {
|
|
13
|
+
Message,
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
Runtime.run(application)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { HashMap, Option } from 'effect'
|
|
2
|
+
|
|
3
|
+
import { Tabs } from '@foldkit/ui'
|
|
4
|
+
|
|
5
|
+
import type { Post, PostDetail, Stats } from './data'
|
|
6
|
+
import type { Model } from './main'
|
|
7
|
+
import { PostDetailData, PostsData, StatsData, TABS_ID } from './main'
|
|
8
|
+
|
|
9
|
+
export const FETCHED_AT = 1_750_000_000_000
|
|
10
|
+
|
|
11
|
+
export const fixturePosts: ReadonlyArray<Post> = [
|
|
12
|
+
{
|
|
13
|
+
id: 'first-post',
|
|
14
|
+
title: 'First Post',
|
|
15
|
+
excerpt: 'The first fixture post.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'second-post',
|
|
19
|
+
title: 'Second Post',
|
|
20
|
+
excerpt: 'The second fixture post.',
|
|
21
|
+
},
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
export const firstPostDetail: PostDetail = {
|
|
25
|
+
id: 'first-post',
|
|
26
|
+
title: 'First Post',
|
|
27
|
+
author: 'Grace Hopper',
|
|
28
|
+
body: 'The whole body of the first fixture post.',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const fixtureStats: Stats = {
|
|
32
|
+
activeUsers: 120,
|
|
33
|
+
requestsPerSecond: 1234,
|
|
34
|
+
cacheHitRatePercent: 97,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const loadingPostsModel: Model = {
|
|
38
|
+
tabs: Tabs.init({ id: TABS_ID }),
|
|
39
|
+
activeTab: 'Posts',
|
|
40
|
+
posts: PostsData.Loading(),
|
|
41
|
+
postDetailById: HashMap.empty(),
|
|
42
|
+
maybeSelectedPostId: Option.none(),
|
|
43
|
+
stats: StatsData.Idle(),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const loadedPostsModel: Model = {
|
|
47
|
+
...loadingPostsModel,
|
|
48
|
+
posts: PostsData.Success({
|
|
49
|
+
data: { posts: fixturePosts, fetchedAt: FETCHED_AT },
|
|
50
|
+
}),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const cachedFirstPostModel: Model = {
|
|
54
|
+
...loadedPostsModel,
|
|
55
|
+
postDetailById: HashMap.set(
|
|
56
|
+
HashMap.empty(),
|
|
57
|
+
'first-post',
|
|
58
|
+
PostDetailData.Success({
|
|
59
|
+
data: { detail: firstPostDetail, fetchedAt: FETCHED_AT },
|
|
60
|
+
}),
|
|
61
|
+
),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const loadedStatsModel: Model = {
|
|
65
|
+
...loadedPostsModel,
|
|
66
|
+
activeTab: 'Stats',
|
|
67
|
+
stats: StatsData.Success({
|
|
68
|
+
data: { stats: fixtureStats, fetchedAt: FETCHED_AT },
|
|
69
|
+
}),
|
|
70
|
+
}
|