create-foldkit-app 0.31.0 → 0.31.1-canary.4e7d8d4010be

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.
@@ -60,7 +60,7 @@ When composing one of those results, bind the whole result to a value named afte
60
60
 
61
61
  Pass optional Commands directly to APIs that accept them: `Command.mapMessages(homeInit.commands, toParentMessage)`. Use `result.commands ?? []` only when the next operation requires a concrete array for spreading, concatenating, execution, or an assertion.
62
62
 
63
- `Update.Return<Model, Message>` rejects an OutMessage-producing result where an API would consume only its Model and Commands. `Update.ReturnWithOutMessage<Model, Message, OutMessage>` still accepts a result with no `outMessage`, because omission means the update emitted nothing.
63
+ Use `Update.Return<Model, Message>` when an update cannot emit an OutMessage. It prevents a result containing an OutMessage from entering code that would keep only its Model and Commands. A result with no `outMessage` can still be used where `Update.ReturnWithOutMessage<Model, Message, OutMessage>` is expected. The missing field means that update emitted no OutMessage.
64
64
 
65
65
  Manual unpacking of a child result usually means the site should use `Update.foldChild` or `Update.foldChildStep`.
66
66
 
@@ -68,7 +68,7 @@ Use `Update.combine` when a later Step should receive the Model produced by an e
68
68
 
69
69
  When the OutMessage is already known while constructing a new result, include it directly: `{ model, commands, outMessage }`. Use `Update.withOutMessage` when attaching an OutMessage to an existing plain return or when the value has the type `OutMessage | undefined`. Pipe an existing return into the helper: `pipe(dialogClose, Update.withOutMessage(outMessage))`. When constructing the plain return in the same expression, pass it first: `Update.withOutMessage({ model, commands }, outMessage)`.
70
70
 
71
- Add `toParentOutMessage` only when at least one child OutMessage is forwarded from the current Submodel to its parent. Omit it when no variant is forwarded. Local handling through `foldOutMessage` is independent, so a forwarded variant may also update the current parent. Never write `toParentOutMessage: () => undefined`.
71
+ Add `toParentOutMessage` only when at least one child OutMessage should continue to the current Submodel's parent. For partial forwarding, match every child variant and return `undefined` for the variants that stop here. Omit `toParentOutMessage` when every variant stops here. `foldOutMessage` still handles each variant locally, including variants that continue upward. Never write `toParentOutMessage: () => undefined`.
72
72
 
73
73
  Use `evo()` from `foldkit/struct` for immutable model updates. Never spread or `Object.assign`.
74
74
 
@@ -109,7 +109,7 @@ Scene runs at any level, since a page's own `update`/`view` pair drops into `sce
109
109
  ## Code Style
110
110
 
111
111
  - Encode state in discriminated unions, not booleans or nullable fields. `Idle | Loading | Error | Ok`, not `isLoading: boolean`. Make impossible states unrepresentable.
112
- - Use `Option` for absence in the Model and domain values instead of `null` or `undefined`. Foldkit return records are the exception: omit `commands` and `outMessage` when absent. A partial `toParentOutMessage` mapper returns `undefined` for each named child variant that stops at the current parent. Prefix Option-typed values with `maybe*`. Match with `Option.match`; don't unwrap with `Option.map(...)` + `Option.getOrElse(...)` when you can just match.
112
+ - Use `Option` for absence in the Model and domain values instead of `null` or `undefined`. Foldkit return records are the exception: omit `commands` and `outMessage` when absent. A partial `toParentOutMessage` mapper returns `undefined` for each child variant that stops at this Submodel. Prefix Option-typed values with `maybe*`. Match with `Option.match`; don't unwrap with `Option.map(...)` + `Option.getOrElse(...)` when you can just match.
113
113
  - 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.
114
114
  - Never cast Schema values with `as Type`. Use the callable constructor: `Message.SucceededLogin({ sessionId })`, not `{ _tag: 'SucceededLogin', sessionId } as Message`.
115
115
  - Always `Array.isArrayEmpty` / `Array.isArrayNonEmpty` (not `.length === 0` / `.length > 0`). Use `Array.match` when handling both empty and non-empty cases.
@@ -13,53 +13,51 @@ import { formatInteger } from './format'
13
13
 
14
14
  export const CHART_HOST_ID = 'charting-chart'
15
15
 
16
- export const MountChart = Mount.define(
17
- 'MountChart',
18
- { hostId: S.String },
19
- Message.SucceededMountChart,
20
- Message.FailedMountChart,
21
- )(
22
- ({ hostId }) =>
23
- element =>
24
- Effect.gen(function* () {
25
- if (!(element instanceof HTMLElement)) {
26
- return Message.FailedMountChart({
27
- reason: 'Chart host is not an HTMLElement.',
28
- })
29
- }
16
+ const mountChart = (element: Element, hostId: string) =>
17
+ Effect.gen(function* () {
18
+ if (!(element instanceof HTMLElement)) {
19
+ return Message.FailedMountChart({
20
+ reason: 'Chart host is not an HTMLElement.',
21
+ })
22
+ }
30
23
 
31
- return yield* Effect.acquireRelease(
32
- Effect.try({
33
- try: () => {
34
- const chart = echarts.init(element, undefined, {
35
- renderer: 'canvas',
36
- })
37
- const resizeObserver = new ResizeObserver(() => chart.resize())
38
- resizeObserver.observe(element)
39
- const onWindowResize = () => chart.resize()
40
- window.addEventListener('resize', onWindowResize)
41
- setChart(hostId, chart)
42
- return { resizeObserver, onWindowResize }
43
- },
44
- catch: error =>
45
- error instanceof Error
46
- ? error
47
- : new Error(`Failed to mount chart: ${error}`),
48
- }),
49
- ({ resizeObserver, onWindowResize }) =>
50
- Effect.sync(() => {
51
- resizeObserver.disconnect()
52
- window.removeEventListener('resize', onWindowResize)
53
- removeChart(hostId)
54
- }),
55
- ).pipe(
56
- Effect.map(() => Message.SucceededMountChart({ hostId })),
57
- Effect.catch(error =>
58
- Effect.succeed(Message.FailedMountChart({ reason: error.message })),
59
- ),
60
- )
24
+ return yield* Effect.acquireRelease(
25
+ Effect.try({
26
+ try: () => {
27
+ const chart = echarts.init(element, undefined, {
28
+ renderer: 'canvas',
29
+ })
30
+ const resizeObserver = new ResizeObserver(() => chart.resize())
31
+ resizeObserver.observe(element)
32
+ const onWindowResize = () => chart.resize()
33
+ window.addEventListener('resize', onWindowResize)
34
+ setChart(hostId, chart)
35
+ return { resizeObserver, onWindowResize }
36
+ },
37
+ catch: error =>
38
+ error instanceof Error
39
+ ? error
40
+ : new Error(`Failed to mount chart: ${error}`),
61
41
  }),
62
- )
42
+ ({ resizeObserver, onWindowResize }) =>
43
+ Effect.sync(() => {
44
+ resizeObserver.disconnect()
45
+ window.removeEventListener('resize', onWindowResize)
46
+ removeChart(hostId)
47
+ }),
48
+ ).pipe(
49
+ Effect.map(() => Message.SucceededMountChart({ hostId })),
50
+ Effect.catch(error =>
51
+ Effect.succeed(Message.FailedMountChart({ reason: error.message })),
52
+ ),
53
+ )
54
+ })
55
+
56
+ export const MountChart = Mount.define('MountChart', {
57
+ args: { hostId: S.String },
58
+ messages: [Message.SucceededMountChart, Message.FailedMountChart],
59
+ execute: ({ element, hostId }) => mountChart(element, hostId),
60
+ })
63
61
 
64
62
  export const chartPanelView = (
65
63
  model: Model,
@@ -310,61 +310,58 @@ export const init: Runtime.ApplicationInit<Model, Message> = () => ({
310
310
 
311
311
  // MAP MOUNT
312
312
 
313
- export const MountMap = Mount.define(
314
- 'MountMap',
315
- { hostId: S.String },
316
- Message.SucceededMountMap,
317
- Message.FailedMountMap,
318
- )(
319
- ({ hostId }) =>
320
- element =>
321
- Effect.gen(function* () {
322
- if (!(element instanceof HTMLElement)) {
323
- return Message.FailedMountMap({
324
- reason: 'Map host is not an HTMLElement.',
313
+ const mountMap = (element: Element, hostId: string) =>
314
+ Effect.gen(function* () {
315
+ if (!(element instanceof HTMLElement)) {
316
+ return Message.FailedMountMap({
317
+ reason: 'Map host is not an HTMLElement.',
318
+ })
319
+ }
320
+
321
+ return yield* Effect.gen(function* () {
322
+ yield* Effect.acquireRelease(
323
+ Effect.gen(function* () {
324
+ const maplibre = yield* Effect.tryPromise(() => import('maplibre-gl'))
325
+ const map = new maplibre.Map({
326
+ container: element,
327
+ style: 'https://demotiles.maplibre.org/style.json',
328
+ center: [0, 20],
329
+ zoom: INITIAL_MAP_ZOOM,
325
330
  })
326
- }
327
- return yield* Effect.gen(function* () {
328
- yield* Effect.acquireRelease(
329
- Effect.gen(function* () {
330
- const maplibre = yield* Effect.tryPromise(
331
- () => import('maplibre-gl'),
332
- )
333
- const map = new maplibre.Map({
334
- container: element,
335
- style: 'https://demotiles.maplibre.org/style.json',
336
- center: [0, 20],
337
- zoom: INITIAL_MAP_ZOOM,
338
- })
339
-
340
- Array.forEach(featuredLocations, ({ id, lng, lat }) => {
341
- const markerElement = document.createElement('button')
342
- markerElement.setAttribute('data-location-id', id)
343
- markerElement.setAttribute('aria-label', `Marker: ${id}`)
344
- markerElement.className = markerStyle
345
- new maplibre.Marker({ element: markerElement })
346
- .setLngLat([lng, lat])
347
- .addTo(map)
348
- })
349
-
350
- setMap(hostId, map)
351
- return map
352
- }),
353
- () => Effect.sync(() => removeMap(hostId)),
354
- )
355
331
 
356
- return Message.SucceededMountMap({ hostId })
357
- }).pipe(
358
- Effect.catch(error =>
359
- Effect.succeed(
360
- Message.FailedMountMap({
361
- reason: error instanceof Error ? error.message : `${error}`,
362
- }),
363
- ),
364
- ),
365
- )
366
- }),
367
- )
332
+ Array.forEach(featuredLocations, ({ id, lng, lat }) => {
333
+ const markerElement = document.createElement('button')
334
+ markerElement.setAttribute('data-location-id', id)
335
+ markerElement.setAttribute('aria-label', `Marker: ${id}`)
336
+ markerElement.className = markerStyle
337
+ new maplibre.Marker({ element: markerElement })
338
+ .setLngLat([lng, lat])
339
+ .addTo(map)
340
+ })
341
+
342
+ setMap(hostId, map)
343
+ return map
344
+ }),
345
+ () => Effect.sync(() => removeMap(hostId)),
346
+ )
347
+
348
+ return Message.SucceededMountMap({ hostId })
349
+ }).pipe(
350
+ Effect.catch(error =>
351
+ Effect.succeed(
352
+ Message.FailedMountMap({
353
+ reason: error instanceof Error ? error.message : `${error}`,
354
+ }),
355
+ ),
356
+ ),
357
+ )
358
+ })
359
+
360
+ export const MountMap = Mount.define('MountMap', {
361
+ args: { hostId: S.String },
362
+ messages: [Message.SucceededMountMap, Message.FailedMountMap],
363
+ execute: ({ element, hostId }) => mountMap(element, hostId),
364
+ })
368
365
 
369
366
  // SUBSCRIPTIONS
370
367
 
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "channel": "stable",
4
- "sourceCommit": "51ee84596c4ff4c6141d2642c7c76446d6bf7890",
3
+ "channel": "canary",
4
+ "sourceCommit": "4e7d8d4010be5a84dd37d1dc48bb97b4f4e599f3",
5
5
  "packages": {
6
- "create-foldkit-app": "0.31.0",
7
- "@foldkit/devtools": "0.153.0",
8
- "@foldkit/devtools-mcp": "0.19.0",
9
- "foldkit": "0.153.0",
10
- "@foldkit/markdown": "0.8.0",
11
- "@foldkit/oxlint-plugin": "0.9.0",
12
- "@foldkit/ui": "0.153.0",
13
- "@foldkit/vite-plugin": "0.19.0"
6
+ "create-foldkit-app": "0.31.1-canary.4e7d8d4010be",
7
+ "@foldkit/devtools": "0.154.0-canary.4e7d8d4010be",
8
+ "@foldkit/devtools-mcp": "0.19.1-canary.4e7d8d4010be",
9
+ "foldkit": "0.154.0-canary.4e7d8d4010be",
10
+ "@foldkit/markdown": "0.8.0-canary.4e7d8d4010be",
11
+ "@foldkit/oxlint-plugin": "0.10.0-canary.4e7d8d4010be",
12
+ "@foldkit/ui": "0.154.0-canary.4e7d8d4010be",
13
+ "@foldkit/vite-plugin": "0.19.0-canary.4e7d8d4010be"
14
14
  },
15
15
  "dependencies": {
16
- "@foldkit/devtools": "0.153.0",
17
- "@foldkit/vite-plugin": "0.19.0",
18
- "@foldkit/devtools-mcp": "0.19.0",
19
- "@foldkit/oxlint-plugin": "0.9.0",
16
+ "@foldkit/devtools": "0.154.0-canary.4e7d8d4010be",
17
+ "@foldkit/vite-plugin": "0.19.0-canary.4e7d8d4010be",
18
+ "@foldkit/devtools-mcp": "0.19.1-canary.4e7d8d4010be",
19
+ "@foldkit/oxlint-plugin": "0.10.0-canary.4e7d8d4010be",
20
20
  "@trivago/prettier-plugin-sort-imports": "^6.0.2",
21
21
  "@types/node": "^25.9.3",
22
22
  "happy-dom": "^20.10.4",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-foldkit-app",
3
- "version": "0.31.0",
3
+ "version": "0.31.1-canary.4e7d8d4010be",
4
4
  "description": "Create Foldkit applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -38,6 +38,7 @@
38
38
  "url": "https://github.com/foldkit/foldkit.git",
39
39
  "directory": "packages/create-foldkit-app"
40
40
  },
41
+ "homepage": "https://foldkit.dev/get-started/getting-started",
41
42
  "publishConfig": {
42
43
  "access": "public"
43
44
  },