creo 0.2.5 → 0.2.7
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/AGENTS.md +156 -0
- package/CHANGELOG.md +138 -0
- package/README.md +4 -2
- package/dist/functional/key.d.ts +0 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +293 -146
- package/dist/index.js.map +15 -15
- package/dist/internal/internal_view.d.ts +10 -1
- package/dist/public/primitive.d.ts +9 -10
- package/dist/public/primitives/primitives.d.ts +341 -111
- package/dist/public/state.d.ts +6 -0
- package/dist/public/view.d.ts +27 -12
- package/dist/render/html_render.d.ts +8 -0
- package/dist/render/render_interface.d.ts +14 -0
- package/dist/render/string_render.d.ts +0 -2
- package/docs/create-app.md +110 -0
- package/docs/events.md +236 -0
- package/docs/getting-started.md +201 -0
- package/docs/how-to/data-fetching.md +155 -0
- package/docs/how-to/deploy-vercel.md +130 -0
- package/docs/how-to/router.md +111 -0
- package/docs/how-to/styles.md +124 -0
- package/docs/how-to/suspense.md +116 -0
- package/docs/index.md +66 -0
- package/docs/lifecycle.md +173 -0
- package/docs/primitives.md +195 -0
- package/docs/renderers.md +183 -0
- package/docs/state.md +131 -0
- package/docs/store.md +135 -0
- package/docs/view.md +205 -0
- package/package.json +5 -2
- package/dist/public/event_handle.d.ts +0 -32
- package/dist/render/canvas_render.d.ts +0 -1
- package/dist/render/stream_render.d.ts +0 -1
- package/dist/structures/indexed_list.d.ts +0 -46
- package/dist/structures/list.d.ts +0 -68
package/AGENTS.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Agent Guide for Creo
|
|
2
|
+
|
|
3
|
+
## What is Creo?
|
|
4
|
+
|
|
5
|
+
Creo is an imperative UI framework with virtual DOM. No JSX — views are function calls (`div()`, `text()`, `span()`). State is reactive (`use()`, `store`). Reconciliation uses Vue 3-style keyed diffing with LIS.
|
|
6
|
+
|
|
7
|
+
## Project Layout
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
packages/creo/ — Core creo framework
|
|
11
|
+
src/internal/engine.ts — Core: reconciler, dirty queue, render loop
|
|
12
|
+
src/internal/internal_view.ts — ViewRecord type, flags, structural comparison
|
|
13
|
+
src/internal/orchestrator.ts — Tracks current active engine
|
|
14
|
+
src/public/view.ts — view() factory, ViewBody, Slot types
|
|
15
|
+
src/public/state.ts — State, Reactive, Use types
|
|
16
|
+
src/public/store.ts — Store (global reactive state)
|
|
17
|
+
src/public/app.ts — createApp() entry point
|
|
18
|
+
src/public/primitives/ — HTML element factories (div, span, text, etc.)
|
|
19
|
+
src/public/primitive.ts — $primitive symbol, EventHandlerProps
|
|
20
|
+
src/render/html_render.ts — DOM renderer (event delegation, attribute diffing)
|
|
21
|
+
src/render/json_render.ts — JSON AST renderer (testing)
|
|
22
|
+
src/render/string_render.ts — HTML string renderer (SSR)
|
|
23
|
+
src/render/render_interface.ts — IRender<Output> interface
|
|
24
|
+
src/functional/lis.ts — Longest Increasing Subsequence (keyed reconciliation)
|
|
25
|
+
src/functional/shallow_equal.ts — Object comparison (no Object.keys allocation)
|
|
26
|
+
packages/creo-router/ — Hash-based router (separate package)
|
|
27
|
+
packages/creo-create-app/ — CLI scaffolding tool
|
|
28
|
+
packages/creo-create-tauri-app/ — CLI scaffolding tool (Tauri)
|
|
29
|
+
scripts/version.ts — Version bump orchestrator
|
|
30
|
+
scripts/publish.ts — Build + publish orchestrator
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Key Architecture Decisions
|
|
34
|
+
|
|
35
|
+
### ViewRecord is a plain object (not pooled)
|
|
36
|
+
Views are GC'd objects with direct parent pointers (`view.parent: Maybe<ViewRecord>`). Children are arrays (`view.children: Maybe<ViewRecord[]>`). No arena allocator, no numeric IDs, no linked lists.
|
|
37
|
+
|
|
38
|
+
### Composites have no DOM footprint
|
|
39
|
+
Unlike React (which doesn't either) or the old creo (which used comment nodes), composite views set `renderRef = true` as a mounted marker. No DOM nodes are created for composites.
|
|
40
|
+
|
|
41
|
+
### Primitives store `{ element, prevProps }` as renderRef
|
|
42
|
+
The `kind` discriminant was removed. Use `view.flags & F_PRIMITIVE` to distinguish primitives from composites.
|
|
43
|
+
|
|
44
|
+
### Slot children (sc) are pre-collected
|
|
45
|
+
When a view has a slot, `newView()` calls `#collect(slot, [], res)` to eagerly evaluate the slot and store results in `view.sc`. This enables structural comparison (`hasScStructuralChange`) without re-running the slot.
|
|
46
|
+
|
|
47
|
+
### scHost optimization
|
|
48
|
+
Composites that receive a slot track `view.scHost` — the primitive whose `.children` contains the live slot children. When slot children have prop changes but identical structure, `#propagateScProps` updates live children directly, skipping the composite's reconcile.
|
|
49
|
+
|
|
50
|
+
### preCollectedSc
|
|
51
|
+
`#patchOrReplace` passes `pendView.sc` to `nextProps` as `preCollectedSc`, avoiding double slot evaluation. The sc items get re-parented to the old view.
|
|
52
|
+
|
|
53
|
+
### Event delegation
|
|
54
|
+
HTML renderer uses Inferno-style delegation: one listener per event type on the container element. Handlers are stored on elements via `Symbol.for("creo.ev")`. Zero closures per element — handler mapping happens at dispatch time.
|
|
55
|
+
|
|
56
|
+
### textContent shortcut
|
|
57
|
+
When a primitive has a single text child, the renderer sets `element.textContent` directly instead of creating a Text node. The text child gets `F_TEXT_CONTENT` flag.
|
|
58
|
+
|
|
59
|
+
### Dispose optimization
|
|
60
|
+
When a primitive with DOM is disposed, its children go through `#disposeVirtual` (skips `renderer.unmount`) since removing the parent element removes all descendants from DOM automatically.
|
|
61
|
+
|
|
62
|
+
## Flags (internal_view.ts)
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
F_PENDING = 1 — View not yet initialized (body is null)
|
|
66
|
+
F_DIRTY = 1 << 1 — Needs reconcile + render
|
|
67
|
+
F_MOVED = 1 << 2 — Position changed, needs DOM repositioning
|
|
68
|
+
F_PRIMITIVE = 1 << 3 — Is an HTML element/text, not a composite
|
|
69
|
+
F_TEXT_CONTENT = 1 << 4 — Text rendered via parent's textContent
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Render Loop Flow
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
markDirty(view) → add to #dirtyQueue Set → schedule()
|
|
76
|
+
↓
|
|
77
|
+
render() processes #dirtyQueue:
|
|
78
|
+
for each view in Set (visits items added during iteration):
|
|
79
|
+
1. initViewBody(view) — if F_PENDING, call viewFn once
|
|
80
|
+
2. isNew = !view.renderRef
|
|
81
|
+
3. if F_DIRTY:
|
|
82
|
+
a. reconcile(view) — collect pending children, diff with old
|
|
83
|
+
b. renderer.render(view) — mount or update DOM
|
|
84
|
+
c. clear F_DIRTY, F_MOVED
|
|
85
|
+
4. else (F_MOVED only):
|
|
86
|
+
a. renderer.render(view) — reposition DOM
|
|
87
|
+
b. clear F_MOVED
|
|
88
|
+
#dirtyQueue.clear()
|
|
89
|
+
fire deferred callbacks (onMount, onUpdateAfter)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Reconciliation Algorithm
|
|
93
|
+
|
|
94
|
+
### Non-keyed
|
|
95
|
+
Position-based: `old[i]` paired with `pending[i]`. Same viewFn → `nextProps`. Different → dispose + init. Extras disposed or mounted.
|
|
96
|
+
|
|
97
|
+
### Keyed (Vue 3-style)
|
|
98
|
+
1. **Head sync** — match from start while keys equal
|
|
99
|
+
2. **Tail sync** — match from end while keys equal
|
|
100
|
+
3. **Simple cases** — pure insertion or pure removal
|
|
101
|
+
4. **Middle diff** — build `newKeyToIndex` map, compute `newIdxToOldIdx`, find LIS. Views in LIS stay; others get `markMoved`.
|
|
102
|
+
|
|
103
|
+
## Testing
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
bun test packages/creo/src/ # All tests
|
|
107
|
+
bun test packages/creo/src/render/render.spec.ts # Renderer + state tests
|
|
108
|
+
bun test packages/creo/src/internal/ # Virtual DOM correctness
|
|
109
|
+
bun test packages/creo/src/render/benchmark.spec.ts # Performance benchmarks
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Tests use happy-dom for DOM simulation. The benchmark tests validate correctness of create/update/swap/select/remove/clear/replace operations on 1000-row tables.
|
|
113
|
+
|
|
114
|
+
## Common Patterns When Modifying
|
|
115
|
+
|
|
116
|
+
### Adding a new primitive
|
|
117
|
+
Add to `packages/creo/src/public/primitives/primitives.ts`:
|
|
118
|
+
```ts
|
|
119
|
+
export const myTag = html<HtmlAttrs & { myProp?: string }, ContainerEvents>("my-tag");
|
|
120
|
+
```
|
|
121
|
+
Export from `packages/creo/src/index.ts`.
|
|
122
|
+
|
|
123
|
+
### Changing the engine
|
|
124
|
+
- `packages/creo/src/internal/engine.ts` is the core — reconciler, dirty queue, render loop
|
|
125
|
+
- After changes, run `bun test packages/creo/src/` (all tests must pass)
|
|
126
|
+
- Test with `cd docs && bun run dev` and open the playground recipes (simple-todo, advanced-todo, table, chess) for visual verification
|
|
127
|
+
|
|
128
|
+
### Changing the HTML renderer
|
|
129
|
+
- `html_render.ts` — DOM operations, event delegation, attribute diffing
|
|
130
|
+
- `findParentDom(view)` walks parent chain for nearest primitive element
|
|
131
|
+
- `findInsertionPoint(view)` scans siblings + recurses up for composites
|
|
132
|
+
- After changes, verify with both todo and router examples
|
|
133
|
+
|
|
134
|
+
## Conventions
|
|
135
|
+
|
|
136
|
+
### `_` for Empty Props
|
|
137
|
+
Always use `_` (from `@/functional/maybe`, re-exported by `creo`) instead of `{}` when a primitive or view needs no props:
|
|
138
|
+
```ts
|
|
139
|
+
div(_, () => { ... }); // not div({}, () => { ... })
|
|
140
|
+
h1(_, "Title"); // not h1({}, "Title")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Inline Strings
|
|
144
|
+
Prefer passing strings directly as slots instead of wrapping in `() => text(...)`. The engine auto-wraps strings into text nodes:
|
|
145
|
+
```ts
|
|
146
|
+
button({ on: { click: handler } }, "Click me"); // not () => text("Click me")
|
|
147
|
+
li(_, "Item text"); // not () => text("Item text")
|
|
148
|
+
span({ class: "label" }, title); // string variable works too
|
|
149
|
+
```
|
|
150
|
+
Use `text()` only for dynamic values or when mixing text with other elements inside a function slot.
|
|
151
|
+
|
|
152
|
+
### Changing ViewRecord shape
|
|
153
|
+
- Update type in `internal_view.ts`
|
|
154
|
+
- Update `newView()` in `engine.ts` (initializes all fields)
|
|
155
|
+
- Update `#disposeVirtual()` if new field needs cleanup
|
|
156
|
+
- JSON/String renderers may need updates if they access the field
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# 0.2.7
|
|
2
|
+
1. Add support for new api `dispose`
|
|
3
|
+
2. Add `ref` to primitives (DOM element) and composite views (exposed API)
|
|
4
|
+
3. Provider-side rename: `ctx.expose(...)` is now `ctx.ref(...)`, matching the consumer-side `ref` prop. `Expose<Api>` is now `RefSetter<Api>`.
|
|
5
|
+
4. Event handlers move from flat `on*` props to a nested `on: { ... }` object — e.g. `button({ on: { click: handler } })`. Removes the per-prop `isEventProp` charCode scan from the renderer's hot path; the `on` object's reference identity short-circuits the event diff. `EventHandlerProps<>` is removed.
|
|
6
|
+
5. Drop `mouseEnter` / `mouseLeave` — use `pointerEnter` / `pointerLeave`. PointerEvents already cover mouse, touch, and pen via the `pointerType` field, so the duplicate mouse-only events were dead weight.
|
|
7
|
+
|
|
8
|
+
## Migration from 0.2.6
|
|
9
|
+
|
|
10
|
+
### Event handlers: flat `on*` props → nested `on: { ... }` object
|
|
11
|
+
|
|
12
|
+
Event names lose the `on` prefix and the leading-capital — they become the camelCase form already used in `ContainerEvents` / `FormEvents` / `MediaEvents` (`click`, `pointerDown`, `keyDown`, `volumeChange`, …).
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// Before
|
|
16
|
+
button({ class: "btn", onClick: handleClick }, "Save");
|
|
17
|
+
input({
|
|
18
|
+
value: v.get(),
|
|
19
|
+
onInput: handleInput,
|
|
20
|
+
onKeyDown: handleKey,
|
|
21
|
+
onBlur: handleBlur,
|
|
22
|
+
});
|
|
23
|
+
details({ onToggle: (e) => log(e.open) }, () => summary(_, "more"));
|
|
24
|
+
|
|
25
|
+
// After
|
|
26
|
+
button({ class: "btn", on: { click: handleClick } }, "Save");
|
|
27
|
+
input({
|
|
28
|
+
value: v.get(),
|
|
29
|
+
on: { input: handleInput, keyDown: handleKey, blur: handleBlur },
|
|
30
|
+
});
|
|
31
|
+
details({ on: { toggle: (e) => log(e.open) } }, () => summary(_, "more"));
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Mapping table for every prop the framework defines:
|
|
35
|
+
|
|
36
|
+
| Before | After (key inside `on: { ... }`) |
|
|
37
|
+
| ------------- | -------------------------------- |
|
|
38
|
+
| `onClick` | `click` |
|
|
39
|
+
| `onDblclick` | `dblclick` |
|
|
40
|
+
| `onPointerDown` | `pointerDown` |
|
|
41
|
+
| `onPointerUp` | `pointerUp` |
|
|
42
|
+
| `onPointerMove` | `pointerMove` |
|
|
43
|
+
| `onPointerCancel` | `pointerCancel` |
|
|
44
|
+
| `onPointerEnter` | `pointerEnter` |
|
|
45
|
+
| `onPointerLeave` | `pointerLeave` |
|
|
46
|
+
| `onMouseEnter` | use `pointerEnter` |
|
|
47
|
+
| `onMouseLeave` | use `pointerLeave` |
|
|
48
|
+
| `onKeyDown` | `keyDown` |
|
|
49
|
+
| `onKeyUp` | `keyUp` |
|
|
50
|
+
| `onFocus` | `focus` |
|
|
51
|
+
| `onBlur` | `blur` |
|
|
52
|
+
| `onInput` | `input` |
|
|
53
|
+
| `onChange` | `change` |
|
|
54
|
+
| `onScroll` | `scroll` |
|
|
55
|
+
| `onLoad` | `load` |
|
|
56
|
+
| `onError` | `error` |
|
|
57
|
+
| `onToggle` | `toggle` |
|
|
58
|
+
| `onVolumeChange` | `volumeChange` |
|
|
59
|
+
| `onPlay` / `onPause` / `onEnded` | `play` / `pause` / `ended` |
|
|
60
|
+
| `onTimeUpdate` | `timeUpdate` |
|
|
61
|
+
| `onLoadedMetadata` | `loadedMetadata` |
|
|
62
|
+
| `onLoadedData` | `loadedData` |
|
|
63
|
+
| `onCanPlay` | `canPlay` |
|
|
64
|
+
| `onCanPlayThrough` | `canPlayThrough` |
|
|
65
|
+
| `onDurationChange` | `durationChange` |
|
|
66
|
+
| `onRateChange` | `rateChange` |
|
|
67
|
+
| `onSeeking` / `onSeeked` | `seeking` / `seeked` |
|
|
68
|
+
| `onStalled` / `onWaiting` | `stalled` / `waiting` |
|
|
69
|
+
|
|
70
|
+
Only primitive event props move. Custom `on*` props you define on your own views (`TodoItem({ onEdit, onSave })`, etc.) are plain props — leave them as-is.
|
|
71
|
+
|
|
72
|
+
**Optional perf tip.** If you hoist the events object once, the renderer skips the per-event diff via `prev.on === next.on`:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const events = { click: handleClick, pointerDown: handleDown };
|
|
76
|
+
// inside render():
|
|
77
|
+
button({ class: "btn", on: events }, "Save");
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Inline `on: { ... }` still works; the renderer falls back to a sub-key diff, which is still cheap.
|
|
81
|
+
|
|
82
|
+
### Exposing an API: `ctx.expose(api)` → `ctx.ref(api)`
|
|
83
|
+
|
|
84
|
+
Pure rename — same semantics, same `applyRef` wiring. The provider verb now uses the same word as the consumer noun.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// Before
|
|
88
|
+
const TextInput = view<{ placeholder: string }, { focus: () => void }>(
|
|
89
|
+
({ props, expose }) => {
|
|
90
|
+
let el: HTMLInputElement | null = null;
|
|
91
|
+
expose({ focus: () => el?.focus() });
|
|
92
|
+
return {
|
|
93
|
+
render() {
|
|
94
|
+
input({
|
|
95
|
+
placeholder: props().placeholder,
|
|
96
|
+
ref: (e) => { el = e as HTMLInputElement; },
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// After
|
|
104
|
+
const TextInput = view<{ placeholder: string }, { focus: () => void }>(
|
|
105
|
+
({ props, ref }) => {
|
|
106
|
+
let el: HTMLInputElement | null = null;
|
|
107
|
+
ref({ focus: () => el?.focus() });
|
|
108
|
+
return {
|
|
109
|
+
render() {
|
|
110
|
+
input({
|
|
111
|
+
placeholder: props().placeholder,
|
|
112
|
+
ref: (e) => { el = e as HTMLInputElement; },
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The `Expose<Api>` type export is now `RefSetter<Api>`.
|
|
121
|
+
|
|
122
|
+
The consumer side is unchanged: `TextInput({ placeholder: "Email", ref: myRef })`.
|
|
123
|
+
|
|
124
|
+
### Removed type exports
|
|
125
|
+
|
|
126
|
+
- `Expose<Api>` → renamed to `RefSetter<Api>`
|
|
127
|
+
- `EventHandlerProps<Events>` → removed. The replacement shape is just `{ on?: Partial<Events> }`, applied automatically by `PrimitiveProps<Attrs, Events>`.
|
|
128
|
+
|
|
129
|
+
# 0.2.6
|
|
130
|
+
|
|
131
|
+
1. Cache first mount render props for primitives
|
|
132
|
+
2. Fix re-bounding event to primitives
|
|
133
|
+
3. Fix old children slice handling to avoid incorrect data duplicity
|
|
134
|
+
4. Support chainable state & store
|
|
135
|
+
5. Improve child placement lookup performance from O(N) to O(1)
|
|
136
|
+
6. Improve "live-DOM" value handling: value, mute, checked
|
|
137
|
+
7. Make stores to use publicly visible symbol Symbol.for("creo.store")
|
|
138
|
+
8.
|
package/README.md
CHANGED
|
@@ -336,8 +336,10 @@ bun test packages/creo/src/ # Run tests
|
|
|
336
336
|
bun run build # Build all packages
|
|
337
337
|
bun run typecheck # Type-check
|
|
338
338
|
|
|
339
|
-
# Run
|
|
340
|
-
cd
|
|
339
|
+
# Run the docs site (with the live recipe playground — simple-todo, advanced-todo, table, chess, etc.):
|
|
340
|
+
cd docs && bun install && bun run dev
|
|
341
|
+
|
|
342
|
+
# Run a standalone example:
|
|
341
343
|
cd examples/router && bun install && bun run dev
|
|
342
344
|
|
|
343
345
|
# Version management:
|
package/dist/functional/key.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
export { createApp } from "./public/app";
|
|
2
|
-
export { view } from "./public/view";
|
|
3
|
-
export type { ViewBody, ViewFn, Slot, SlotContent, PublicView } from "./public/view";
|
|
2
|
+
export { view, applyRef } from "./public/view";
|
|
3
|
+
export type { ViewBody, ViewFn, Slot, SlotContent, PublicView, Ref, RefCallback, RefObject, RefSetter, } from "./public/view";
|
|
4
4
|
export type { Reactive, Use } from "./public/state";
|
|
5
5
|
export { State } from "./public/state";
|
|
6
6
|
export { Store, store, isStore } from "./public/store";
|
|
7
7
|
export { $primitive } from "./public/primitive";
|
|
8
|
-
export type { PrimitiveProps
|
|
8
|
+
export type { PrimitiveProps } from "./public/primitive";
|
|
9
9
|
export { html, text, div, span, section, article, aside, nav, header, footer, main, address, hgroup, search, p, h1, h2, h3, h4, h5, h6, pre, code, em, strong, small, br, hr, a, blockquote, label, abbr, b, bdi, bdo, cite, data, dfn, i, kbd, mark, q, rp, rt, ruby, s, samp, sub, sup, time, u, varEl, wbr, del, ins, ul, ol, li, dl, dt, dd, table, thead, tbody, tfoot, tr, th, td, caption, colgroup, col, form, button, input, textarea, select, option, fieldset, legend, datalist, optgroup, output, progress, meter, img, video, audio, canvas, source, track, map, area, picture, iframe, embed, object, portal, svg, details, summary, dialog, menu, figure, figcaption, script, noscript, template, slot, } from "./public/primitives/primitives";
|
|
10
10
|
export type { BaseEventData, PointerEventData, KeyEventData, InputEventData, FocusEventData, MediaEventData, ScrollEventData, LoadEventData, ErrorEventData, ToggleEventData, ContainerEvents, FormEvents, MediaEvents, DisclosureEvents, HtmlAttrs, } from "./public/primitives/primitives";
|
|
11
11
|
export type { IRender } from "./render/render_interface";
|
|
12
12
|
export { HtmlRender } from "./render/html_render";
|
|
13
13
|
export { JsonRender } from "./render/json_render";
|
|
14
14
|
export type { JsonNode } from "./render/json_render";
|
|
15
|
-
export { HtmlStringRender
|
|
15
|
+
export { HtmlStringRender } from "./render/string_render";
|
|
16
16
|
export { Engine, type Scheduler } from "./internal/engine";
|
|
17
|
-
export { type Maybe, type None, type Just, just, withDefault, _ } from "./functional/maybe";
|
|
17
|
+
export { type Maybe, type None, type Just, just, withDefault, _, } from "./functional/maybe";
|
|
18
18
|
export type { Key } from "./functional/key";
|
|
19
19
|
export { shallowEqual } from "./functional/shallow_equal";
|
|
20
20
|
export { assertNever } from "./functional/assert";
|