foldkit-primitives 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `foldkit-primitives`
2
2
 
3
- Ready-made [`foldkit-bundle`](./bundle) primitives: media queries, breakpoints,
3
+ Ready-made [`foldkit-bundle`](../bundle) primitives: media queries, breakpoints,
4
4
  presence, timers, intervals, debounce, tweens, springs, pagination, history,
5
5
  locales, selections, ranges, geolocation, cameras, permissions, sockets,
6
6
  broadcasts, observers, and clipboard. Most are ordinary bundles — Model,
@@ -33,6 +33,15 @@ package depends on `foldkit-bundle` and nothing else (besides peers).
33
33
  | One-shot actions | Command, not a bundle | clipboard copy, share, script load |
34
34
  | Derived data | pure function, not a bundle | range, relative time, platform |
35
35
 
36
+ Choose by what you need to own. State that outlives the moment — a match,
37
+ a count, a page, a position — wants a bundle: the Model keeps it, replay
38
+ sees it. A stream you only react to wants an entry: map it into the
39
+ parent's Message and keep nothing. Work bound to one element wants a
40
+ Mount. Work that runs once and reports back wants a Command. A value
41
+ computed from data you already have wants a pure function. When in doubt,
42
+ start with the lighter form; promote to a bundle the day the state needs
43
+ a name in the Model.
44
+
36
45
  A bundle holds no state and performs no I/O by itself. The parent Model owns
37
46
  the placed slice; the browser (or server, or clock) only reports facts as
38
47
  Messages. The same rule as everywhere else: observation is not ownership.
@@ -69,6 +78,20 @@ pnpm add foldkit-primitives foldkit-bundle effect foldkit
69
78
  `foldkit-primitives/events`, `foldkit-primitives/observers`, `foldkit-primitives/dom` —
70
79
  so bundlers drop the primitives you never import.
71
80
 
81
+ ## Map of the package
82
+
83
+ Each subpath is one concern, one import:
84
+
85
+ - `media` — environment facts: MediaQuery (+presets), Breakpoints, platform
86
+ - `net` — remote facts: Online, WebSocket, SSE, BroadcastChannel
87
+ - `time` — clock facts: Timer, Interval, Debounce, Throttle, relative time
88
+ - `state` — owned UI state: Pagination, History, Locale, SelectionSet, Virtual, range
89
+ - `motion` — animation state: Tween, Spring, Presence
90
+ - `device` — hardware: Geolocation, MediaDevices, MediaStream, Permissions, Fullscreen
91
+ - `events` — raw browser events: Visibility, WindowSize, Idle, keyboard, pointer, scroll, focus
92
+ - `observers` — element Mounts: Resize, Intersection, Mutation, Bounds
93
+ - `dom` — element Mounts and one-shot Commands: Autofocus, InputMask, clipboard, share, script loading
94
+
72
95
  ## Sixty seconds: follow the color scheme
73
96
 
74
97
  **Declare where it lives.** A declaration names the Model field and the
@@ -105,13 +128,29 @@ const config = placements.complete({
105
128
 
106
129
  `update` routes `GotDarkMessage` to the bundle; `subscriptions` runs the
107
130
  `matchMedia` stream; `initial` starts `matches` at `false` and the stream
108
- corrects it on subscribe. The Solid equivalent this replaces:
131
+ corrects it on subscribe. None of the placement calls perform I/O:
132
+ `declare` names a slot, `at` binds config to it, `assemble` derives the
133
+ folding and the streams — the browser is touched only when the runtime
134
+ subscribes. The Solid equivalent this replaces:
109
135
 
110
136
  ```ts
111
137
  // solid-primitives: const dark = createMediaQuery('(prefers-color-scheme: dark)')
112
138
  // Here the fact lives in the Model: replay, DevTools, and time travel see it.
113
139
  ```
114
140
 
141
+ ## Composing placements
142
+
143
+ One assembly holds every placement: spread its update, init,
144
+ subscriptions, and resources once per application, as above. Two
145
+ placements of one bundle observe twice — share the field instead, so one
146
+ stream feeds every reader. One assembly holds one socket, stream, or
147
+ watch: resource tags are per module, and `assemble` refuses the second.
148
+ Bundles whose settled value is the whole point (`Debounce`, `Throttle`)
149
+ surface it as an OutMessage the placement handles with `onOut`, so the
150
+ signal can never be dropped by omission. Entries lift with
151
+ `Subscription.persistent`, mapping into the parent's Message; Mounts
152
+ attach in views with `h.OnMount`.
153
+
115
154
  ## Media: `foldkit-primitives/media`
116
155
 
117
156
  > Reference: [`./media/README.md`](./media/README.md)
@@ -403,15 +442,6 @@ browser), restored through a `MirrorRestored` Message the application
403
442
  reduces. Nothing here duplicates it — reach for the mirror when a slice
404
443
  should survive reload, and keep this package's bundles for live facts.
405
444
 
406
- ## With Surface and Mirror
407
-
408
- Placed state is ordinary Model, so the surrounding tools apply unchanged —
409
- no bundle-specific Surface or Mirror API exists, by design. Declare a Surface
410
- over the placed fields to render them or expose them to an agent; point
411
- `Mirror.url` at them to link them; spread the bundle's cases into the same
412
- unions. The field refs and wrapper Messages are the same ones the rest of the
413
- application uses.
414
-
415
445
  ## Motion: `foldkit-primitives/motion`
416
446
 
417
447
  > Reference: [`./motion/README.md`](./motion/README.md)
@@ -455,6 +485,15 @@ forking before the first `TestClock.adjust`, or dispatched events hit
455
485
  unregistered listeners. Each ingredient is demonstrated in this package's
456
486
  `test/` directory, named after its primitive.
457
487
 
488
+ ## With Surface and Mirror
489
+
490
+ Placed state is ordinary Model, so the surrounding tools apply unchanged —
491
+ no bundle-specific Surface or Mirror API exists, by design. Declare a Surface
492
+ over the placed fields to render them or expose them to an agent; point
493
+ `Mirror.url` at them to link them; spread the bundle's cases into the same
494
+ unions. The field refs and wrapper Messages are the same ones the rest of the
495
+ application uses.
496
+
458
497
  ## Failure and recovery
459
498
 
460
499
  | Failure | Behaviour |
@@ -59,7 +59,7 @@ export declare const GeolocationMessage: import("foldkit/message").MessageUnion<
59
59
  }>;
60
60
  export type GeolocationMessage = typeof GeolocationMessage.Type;
61
61
  export declare const Geolocation: Bundle.Bundle<"Geolocation", void, {
62
- readonly status: "unknown" | "denied" | "ready";
62
+ readonly status: "unknown" | "ready" | "denied";
63
63
  readonly coords: {
64
64
  readonly latitude: number;
65
65
  readonly longitude: number;
@@ -67,6 +67,8 @@ export declare const Geolocation: Bundle.Bundle<"Geolocation", void, {
67
67
  } | null;
68
68
  readonly lastError: string | null;
69
69
  }, {
70
+ readonly _tag: "Denied";
71
+ } | {
70
72
  readonly _tag: "Failed";
71
73
  readonly message: string;
72
74
  } | {
@@ -74,8 +76,6 @@ export declare const Geolocation: Bundle.Bundle<"Geolocation", void, {
74
76
  readonly latitude: number;
75
77
  readonly longitude: number;
76
78
  readonly accuracy: number;
77
- } | {
78
- readonly _tag: "Denied";
79
79
  }, never, never, never, void, {}, {}>;
80
80
  //#endregion
81
81
  //#region src/device/media-devices.d.ts
@@ -130,7 +130,7 @@ export declare const mediaDevices: <const Name extends string>(config: {
130
130
  /** Read lazily so tests can substitute a double, like the SSE bundle. */
131
131
  readonly create?: ((navigator: Navigator) => MediaDevicesHandle | null) | undefined;
132
132
  }) => Bundle.Bundle<Name, void, {
133
- readonly status: "unknown" | "denied" | "ready";
133
+ readonly status: "unknown" | "ready" | "denied";
134
134
  readonly devices: readonly {
135
135
  readonly deviceId: string;
136
136
  readonly groupId: string;
@@ -139,8 +139,6 @@ export declare const mediaDevices: <const Name extends string>(config: {
139
139
  }[];
140
140
  readonly lastError: string | null;
141
141
  }, {
142
- readonly _tag: "Denied";
143
- } | {
144
142
  readonly _tag: "Failed";
145
143
  readonly message: string;
146
144
  } | {
@@ -155,6 +153,8 @@ export declare const mediaDevices: <const Name extends string>(config: {
155
153
  readonly kind: "audioinput" | "audiooutput" | "videoinput";
156
154
  readonly label: string;
157
155
  }[];
156
+ } | {
157
+ readonly _tag: "Denied";
158
158
  }, never, never, never, void, {}, {}>;
159
159
  //#endregion
160
160
  //#region src/device/media-stream.d.ts
@@ -288,20 +288,20 @@ export declare const permissions: <const Name extends string>(config: {
288
288
  readonly names: ReadonlyArray<string>;
289
289
  }, {
290
290
  readonly states: {
291
- readonly [x: string]: "granted" | "denied" | "prompt";
291
+ readonly [x: string]: "denied" | "granted" | "prompt";
292
292
  };
293
293
  readonly lastError: string | null;
294
294
  }, {
295
295
  readonly _tag: "Changed";
296
296
  readonly name: string;
297
- readonly state: "granted" | "denied" | "prompt";
297
+ readonly state: "denied" | "granted" | "prompt";
298
298
  } | {
299
299
  readonly _tag: "Failed";
300
300
  readonly message: string;
301
301
  } | {
302
302
  readonly _tag: "Snapshot";
303
303
  readonly states: {
304
- readonly [x: string]: "granted" | "denied" | "prompt";
304
+ readonly [x: string]: "denied" | "granted" | "prompt";
305
305
  };
306
306
  } | {
307
307
  readonly _tag: "Cleared";
@@ -106,14 +106,14 @@ export declare const sse: <const Name extends string>(config: {
106
106
  readonly status: "closed" | "connecting" | "open";
107
107
  readonly lastError: string | null;
108
108
  }, {
109
- readonly _tag: "Received";
110
- readonly data: string;
111
- } | {
112
109
  readonly _tag: "Connecting";
113
110
  } | {
114
111
  readonly _tag: "Opened";
115
112
  } | {
116
113
  readonly _tag: "Closed";
114
+ } | {
115
+ readonly _tag: "Received";
116
+ readonly data: string;
117
117
  } | {
118
118
  readonly _tag: "Failed";
119
119
  readonly message: string;
@@ -179,11 +179,11 @@ export declare const websocket: <const Name extends string>(config: {
179
179
  readonly _tag: "Connecting";
180
180
  } | {
181
181
  readonly _tag: "Opened";
182
- } | {
183
- readonly _tag: "Closed";
184
182
  } | {
185
183
  readonly _tag: "Received";
186
184
  readonly data: string;
185
+ } | {
186
+ readonly _tag: "Closed";
187
187
  } | {
188
188
  readonly _tag: "Failed";
189
189
  readonly message: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foldkit-primitives",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Ready-made primitives for the Elm architecture, packaged with foldkit-bundle: media queries, presence, timers, pagination, history, sockets, observers, and clipboard.",
5
5
  "keywords": [
6
6
  "foldkit",