foldkit-primitives 0.1.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/LICENSE +21 -0
- package/README.md +454 -0
- package/dist/device/index.d.mts +312 -0
- package/dist/device/index.d.mts.map +1 -0
- package/dist/device/index.mjs +490 -0
- package/dist/device/index.mjs.map +1 -0
- package/dist/dom/index.d.mts +73 -0
- package/dist/dom/index.d.mts.map +1 -0
- package/dist/dom/index.mjs +218 -0
- package/dist/dom/index.mjs.map +1 -0
- package/dist/events/index.d.mts +154 -0
- package/dist/events/index.d.mts.map +1 -0
- package/dist/events/index.mjs +287 -0
- package/dist/events/index.mjs.map +1 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.mjs +10 -0
- package/dist/media/index.d.mts +79 -0
- package/dist/media/index.d.mts.map +1 -0
- package/dist/media/index.mjs +127 -0
- package/dist/media/index.mjs.map +1 -0
- package/dist/motion/index.d.mts +112 -0
- package/dist/motion/index.d.mts.map +1 -0
- package/dist/motion/index.mjs +208 -0
- package/dist/motion/index.mjs.map +1 -0
- package/dist/net/index.d.mts +199 -0
- package/dist/net/index.d.mts.map +1 -0
- package/dist/net/index.mjs +329 -0
- package/dist/net/index.mjs.map +1 -0
- package/dist/observers/index.d.mts +59 -0
- package/dist/observers/index.d.mts.map +1 -0
- package/dist/observers/index.mjs +156 -0
- package/dist/observers/index.mjs.map +1 -0
- package/dist/state/index.d.mts +342 -0
- package/dist/state/index.d.mts.map +1 -0
- package/dist/state/index.mjs +504 -0
- package/dist/state/index.mjs.map +1 -0
- package/dist/time/index.d.mts +129 -0
- package/dist/time/index.d.mts.map +1 -0
- package/dist/time/index.mjs +213 -0
- package/dist/time/index.mjs.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { Bundle } from "foldkit-bundle";
|
|
3
|
+
//#region src/motion/presence.d.ts
|
|
4
|
+
export declare const PresenceModel: Schema.Struct<{
|
|
5
|
+
readonly phase: Schema.Literals<readonly ["shown", "hiding", "hidden"]>;
|
|
6
|
+
readonly generation: Schema.Number;
|
|
7
|
+
}>;
|
|
8
|
+
export type PresenceModel = typeof PresenceModel.Type;
|
|
9
|
+
export declare const PresenceMessage: import("foldkit/message").MessageUnion<{
|
|
10
|
+
readonly Show: {};
|
|
11
|
+
readonly Hide: {};
|
|
12
|
+
readonly Hidden: {
|
|
13
|
+
readonly generation: Schema.Number;
|
|
14
|
+
};
|
|
15
|
+
}>;
|
|
16
|
+
export type PresenceMessage = typeof PresenceMessage.Type;
|
|
17
|
+
/** Whether the content renders: shown or mid-exit. */
|
|
18
|
+
export declare const isVisible: (model: PresenceModel) => boolean;
|
|
19
|
+
export declare const Presence: Bundle.Bundle<"Presence", {
|
|
20
|
+
readonly durationMs: number;
|
|
21
|
+
}, {
|
|
22
|
+
readonly phase: "shown" | "hiding" | "hidden";
|
|
23
|
+
readonly generation: number;
|
|
24
|
+
}, {
|
|
25
|
+
readonly _tag: "Show";
|
|
26
|
+
} | {
|
|
27
|
+
readonly _tag: "Hide";
|
|
28
|
+
} | {
|
|
29
|
+
readonly _tag: "Hidden";
|
|
30
|
+
readonly generation: number;
|
|
31
|
+
}, never, never, never, void, {}, {}>;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/motion/spring.d.ts
|
|
34
|
+
export declare const SpringModel: Schema.Struct<{
|
|
35
|
+
readonly value: Schema.Number;
|
|
36
|
+
readonly velocity: Schema.Number;
|
|
37
|
+
readonly running: Schema.Boolean;
|
|
38
|
+
}>;
|
|
39
|
+
export type SpringModel = typeof SpringModel.Type;
|
|
40
|
+
export declare const SpringMessage: import("foldkit/message").MessageUnion<{
|
|
41
|
+
readonly Started: {};
|
|
42
|
+
readonly Stopped: {};
|
|
43
|
+
readonly Ticked: {
|
|
44
|
+
readonly value: Schema.Number;
|
|
45
|
+
readonly velocity: Schema.Number;
|
|
46
|
+
};
|
|
47
|
+
/** Carries the exact end value: the integrator stops near it, not on it. */
|
|
48
|
+
readonly Finished: {
|
|
49
|
+
readonly value: Schema.Number;
|
|
50
|
+
};
|
|
51
|
+
}>;
|
|
52
|
+
export type SpringMessage = typeof SpringMessage.Type;
|
|
53
|
+
export declare const Spring: Bundle.Bundle<"Spring", {
|
|
54
|
+
readonly from: number;
|
|
55
|
+
readonly to: number;
|
|
56
|
+
readonly stiffness: number;
|
|
57
|
+
readonly damping: number;
|
|
58
|
+
}, {
|
|
59
|
+
value: number;
|
|
60
|
+
velocity: number;
|
|
61
|
+
running: boolean;
|
|
62
|
+
}, {
|
|
63
|
+
readonly _tag: "Started";
|
|
64
|
+
} | {
|
|
65
|
+
readonly _tag: "Stopped";
|
|
66
|
+
} | {
|
|
67
|
+
readonly _tag: "Ticked";
|
|
68
|
+
readonly value: number;
|
|
69
|
+
readonly velocity: number;
|
|
70
|
+
} | {
|
|
71
|
+
readonly _tag: "Finished";
|
|
72
|
+
readonly value: number;
|
|
73
|
+
}, never, never, never, void, {}, {}>;
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/motion/tween.d.ts
|
|
76
|
+
export declare const TweenModel: Schema.Struct<{
|
|
77
|
+
readonly value: Schema.Number;
|
|
78
|
+
readonly running: Schema.Boolean;
|
|
79
|
+
}>;
|
|
80
|
+
export type TweenModel = typeof TweenModel.Type;
|
|
81
|
+
export declare const TweenMessage: import("foldkit/message").MessageUnion<{
|
|
82
|
+
readonly Started: {};
|
|
83
|
+
readonly Stopped: {};
|
|
84
|
+
readonly Ticked: {
|
|
85
|
+
readonly value: Schema.Number;
|
|
86
|
+
};
|
|
87
|
+
/** Carries the exact end value: the stream may stop on either side of it. */
|
|
88
|
+
readonly Finished: {
|
|
89
|
+
readonly value: Schema.Number;
|
|
90
|
+
};
|
|
91
|
+
}>;
|
|
92
|
+
export type TweenMessage = typeof TweenMessage.Type;
|
|
93
|
+
export declare const Tween: Bundle.Bundle<"Tween", {
|
|
94
|
+
readonly from: number;
|
|
95
|
+
readonly to: number;
|
|
96
|
+
readonly ms: number;
|
|
97
|
+
}, {
|
|
98
|
+
value: number;
|
|
99
|
+
running: boolean;
|
|
100
|
+
}, {
|
|
101
|
+
readonly _tag: "Started";
|
|
102
|
+
} | {
|
|
103
|
+
readonly _tag: "Stopped";
|
|
104
|
+
} | {
|
|
105
|
+
readonly _tag: "Ticked";
|
|
106
|
+
readonly value: number;
|
|
107
|
+
} | {
|
|
108
|
+
readonly _tag: "Finished";
|
|
109
|
+
readonly value: number;
|
|
110
|
+
}, never, never, never, void, {}, {}>;
|
|
111
|
+
//#endregion
|
|
112
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/motion/presence.ts","../../src/motion/spring.ts","../../src/motion/tween.ts"],"mappings":";;;qBAWa,eAAa,OAAA;;;;YAId,uBAAuB,cAAc;qBAEpC,2CAAe;;;;yBAAf,OAAA;;;YAKD,yBAAyB,gBAAgB;;qBAGxC,YAAS,OAAW;qBAEpB,UAAQ,OAAA;WAII;;;;;;;;;;;;;;qBCnBZ,aAAW,OAAA;;;;;YAKZ,qBAAqB,YAAY;qBAEhC,yCAAa;;;;;;;;;;;;YAOd,uBAAuB,cAAc;qBAapC,QAAM,OAAA;;;;;;;;;;;;;;;;;;;;;;;qBC5BN,YAAU,OAAA;;;;YACX,oBAAoB,WAAW;qBAE9B,wCAAY;;;;;;;;;;;YAOb,sBAAsB,aAAa;qBAElC,OAAK,OAAA"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { Clock, Effect, Schema, Stream } from "effect";
|
|
2
|
+
import { defineMessageUnion } from "foldkit/message";
|
|
3
|
+
import * as Subscription from "foldkit/subscription";
|
|
4
|
+
import { Bundle } from "foldkit-bundle";
|
|
5
|
+
//#region src/motion/presence.ts
|
|
6
|
+
/**
|
|
7
|
+
* Presence as a bundle: mount-transition state for exit animations. `Hide`
|
|
8
|
+
* starts a timed `hiding` phase; the `Hidden` fact it yields carries the
|
|
9
|
+
* generation it was scheduled under, so a `Show` in between wins and the
|
|
10
|
+
* late fact is ignored. The clock is Effect's, so tests drive it.
|
|
11
|
+
*/
|
|
12
|
+
const PresenceModel = Schema.Struct({
|
|
13
|
+
phase: Schema.Literals([
|
|
14
|
+
"shown",
|
|
15
|
+
"hiding",
|
|
16
|
+
"hidden"
|
|
17
|
+
]),
|
|
18
|
+
generation: Schema.Number
|
|
19
|
+
});
|
|
20
|
+
const PresenceMessage = defineMessageUnion({
|
|
21
|
+
Show: {},
|
|
22
|
+
Hide: {},
|
|
23
|
+
Hidden: { generation: Schema.Number }
|
|
24
|
+
});
|
|
25
|
+
/** Whether the content renders: shown or mid-exit. */
|
|
26
|
+
const isVisible = (model) => model.phase !== "hidden";
|
|
27
|
+
const Presence = Bundle.make("Presence", {
|
|
28
|
+
Model: PresenceModel,
|
|
29
|
+
Message: PresenceMessage,
|
|
30
|
+
args: Schema.Struct({ durationMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())) }),
|
|
31
|
+
init: () => ({ model: {
|
|
32
|
+
phase: "hidden",
|
|
33
|
+
generation: 0
|
|
34
|
+
} }),
|
|
35
|
+
update: (model, message, args) => PresenceMessage.match(message, {
|
|
36
|
+
Show: () => ({ model: {
|
|
37
|
+
...model,
|
|
38
|
+
phase: "shown",
|
|
39
|
+
generation: model.generation + 1
|
|
40
|
+
} }),
|
|
41
|
+
Hide: () => {
|
|
42
|
+
const generation = model.generation + 1;
|
|
43
|
+
return {
|
|
44
|
+
model: {
|
|
45
|
+
...model,
|
|
46
|
+
phase: "hiding",
|
|
47
|
+
generation
|
|
48
|
+
},
|
|
49
|
+
commands: [{
|
|
50
|
+
name: "Presence.hide",
|
|
51
|
+
args: { generation },
|
|
52
|
+
effect: Effect.as(Effect.sleep(args.durationMs), PresenceMessage.Hidden({ generation }))
|
|
53
|
+
}]
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
Hidden: ({ generation }) => generation === model.generation ? { model: {
|
|
57
|
+
...model,
|
|
58
|
+
phase: "hidden"
|
|
59
|
+
} } : { model }
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/motion/spring.ts
|
|
64
|
+
/**
|
|
65
|
+
* A spring as a bundle: a number pulled toward a target with stiffness and
|
|
66
|
+
* damping, in the Model. Semi-implicit Euler at a fixed 16ms step, so the
|
|
67
|
+
* trajectory is deterministic under TestClock; the stream ends with
|
|
68
|
+
* Finished, and the value rests exactly at `to`. Underdamped springs
|
|
69
|
+
* overshoot — that is the point — but the rest value is still exact.
|
|
70
|
+
*/
|
|
71
|
+
const SpringModel = Schema.Struct({
|
|
72
|
+
value: Schema.Number,
|
|
73
|
+
velocity: Schema.Number,
|
|
74
|
+
running: Schema.Boolean
|
|
75
|
+
});
|
|
76
|
+
const SpringMessage = defineMessageUnion({
|
|
77
|
+
Started: {},
|
|
78
|
+
Stopped: {},
|
|
79
|
+
Ticked: {
|
|
80
|
+
value: Schema.Number,
|
|
81
|
+
velocity: Schema.Number
|
|
82
|
+
},
|
|
83
|
+
/** Carries the exact end value: the integrator stops near it, not on it. */
|
|
84
|
+
Finished: { value: Schema.Number }
|
|
85
|
+
});
|
|
86
|
+
const step = 16;
|
|
87
|
+
const epsilon = .001;
|
|
88
|
+
const settled = (point, to) => Math.abs(to - point.x) < epsilon && Math.abs(point.v) < epsilon;
|
|
89
|
+
const Spring = Bundle.make("Spring", {
|
|
90
|
+
Model: SpringModel,
|
|
91
|
+
Message: SpringMessage,
|
|
92
|
+
args: Schema.Struct({
|
|
93
|
+
from: Schema.Number.pipe(Schema.check(Schema.isFinite())),
|
|
94
|
+
to: Schema.Number.pipe(Schema.check(Schema.isFinite())),
|
|
95
|
+
stiffness: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())),
|
|
96
|
+
damping: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite()))
|
|
97
|
+
}),
|
|
98
|
+
init: (args) => ({ model: {
|
|
99
|
+
value: args.from,
|
|
100
|
+
velocity: 0,
|
|
101
|
+
running: false
|
|
102
|
+
} }),
|
|
103
|
+
update: (model, message) => SpringMessage.match(message, {
|
|
104
|
+
Started: () => ({ model: {
|
|
105
|
+
...model,
|
|
106
|
+
running: true
|
|
107
|
+
} }),
|
|
108
|
+
Stopped: () => ({ model: {
|
|
109
|
+
...model,
|
|
110
|
+
running: false
|
|
111
|
+
} }),
|
|
112
|
+
Ticked: ({ value, velocity }) => ({ model: {
|
|
113
|
+
...model,
|
|
114
|
+
value,
|
|
115
|
+
velocity
|
|
116
|
+
} }),
|
|
117
|
+
Finished: ({ value }) => ({ model: {
|
|
118
|
+
...model,
|
|
119
|
+
value,
|
|
120
|
+
velocity: 0,
|
|
121
|
+
running: false
|
|
122
|
+
} })
|
|
123
|
+
}),
|
|
124
|
+
subscriptions: ({ from, to, stiffness, damping }) => Subscription.make()((entry) => ({ ticks: entry({ running: Schema.Boolean }, {
|
|
125
|
+
modelToDependencies: (model) => ({ running: model.running }),
|
|
126
|
+
dependenciesToStream: ({ running }) => {
|
|
127
|
+
if (!running) return Stream.empty;
|
|
128
|
+
const dt = step / 1e3;
|
|
129
|
+
const points = Stream.tick(step).pipe(Stream.scan({
|
|
130
|
+
x: from,
|
|
131
|
+
v: 0
|
|
132
|
+
}, (point) => {
|
|
133
|
+
const force = -stiffness * (point.x - to) - damping * point.v;
|
|
134
|
+
const v = point.v + force * dt;
|
|
135
|
+
return {
|
|
136
|
+
x: point.x + v * dt,
|
|
137
|
+
v
|
|
138
|
+
};
|
|
139
|
+
}), Stream.drop(1));
|
|
140
|
+
return Stream.concat(points.pipe(Stream.takeUntil((point) => settled(point, to)), Stream.map((point) => SpringMessage.Ticked({
|
|
141
|
+
value: point.x,
|
|
142
|
+
velocity: point.v
|
|
143
|
+
}))), Stream.make(SpringMessage.Finished({ value: to })));
|
|
144
|
+
}
|
|
145
|
+
}) }))
|
|
146
|
+
});
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/motion/tween.ts
|
|
149
|
+
/**
|
|
150
|
+
* A tween as a bundle: a number animated from one value to another over a
|
|
151
|
+
* duration, in the Model. Progress comes from Effect's clock, so tests drive
|
|
152
|
+
* it with TestClock; the stream ends with Finished, and the value rests at
|
|
153
|
+
* `to`. Linear interpolation only: easing curves stay the application's job.
|
|
154
|
+
*/
|
|
155
|
+
const TweenModel = Schema.Struct({
|
|
156
|
+
value: Schema.Number,
|
|
157
|
+
running: Schema.Boolean
|
|
158
|
+
});
|
|
159
|
+
const TweenMessage = defineMessageUnion({
|
|
160
|
+
Started: {},
|
|
161
|
+
Stopped: {},
|
|
162
|
+
Ticked: { value: Schema.Number },
|
|
163
|
+
/** Carries the exact end value: the stream may stop on either side of it. */
|
|
164
|
+
Finished: { value: Schema.Number }
|
|
165
|
+
});
|
|
166
|
+
const Tween = Bundle.make("Tween", {
|
|
167
|
+
Model: TweenModel,
|
|
168
|
+
Message: TweenMessage,
|
|
169
|
+
args: Schema.Struct({
|
|
170
|
+
from: Schema.Number.pipe(Schema.check(Schema.isFinite())),
|
|
171
|
+
to: Schema.Number.pipe(Schema.check(Schema.isFinite())),
|
|
172
|
+
ms: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite()))
|
|
173
|
+
}),
|
|
174
|
+
init: (args) => ({ model: {
|
|
175
|
+
value: args.from,
|
|
176
|
+
running: false
|
|
177
|
+
} }),
|
|
178
|
+
update: (model, message) => TweenMessage.match(message, {
|
|
179
|
+
Started: () => ({ model: {
|
|
180
|
+
...model,
|
|
181
|
+
running: true
|
|
182
|
+
} }),
|
|
183
|
+
Stopped: () => ({ model: {
|
|
184
|
+
...model,
|
|
185
|
+
running: false
|
|
186
|
+
} }),
|
|
187
|
+
Ticked: ({ value }) => ({ model: {
|
|
188
|
+
...model,
|
|
189
|
+
value
|
|
190
|
+
} }),
|
|
191
|
+
Finished: ({ value }) => ({ model: {
|
|
192
|
+
...model,
|
|
193
|
+
value,
|
|
194
|
+
running: false
|
|
195
|
+
} })
|
|
196
|
+
}),
|
|
197
|
+
subscriptions: ({ from, to, ms }) => Subscription.make()((entry) => ({ ticks: entry({ running: Schema.Boolean }, {
|
|
198
|
+
modelToDependencies: (model) => ({ running: model.running }),
|
|
199
|
+
dependenciesToStream: ({ running }) => running ? Stream.unwrap(Clock.currentTimeMillis.pipe(Effect.map((startedAt) => {
|
|
200
|
+
const progress = Stream.tick(16).pipe(Stream.mapEffect(() => Clock.currentTimeMillis), Stream.map((now) => Math.min(1, (now - startedAt) / ms)));
|
|
201
|
+
return Stream.concat(progress.pipe(Stream.takeUntil((done) => done >= 1), Stream.map((done) => TweenMessage.Ticked({ value: from + (to - from) * done }))), Stream.make(TweenMessage.Finished({ value: to })));
|
|
202
|
+
}))) : Stream.empty
|
|
203
|
+
}) }))
|
|
204
|
+
});
|
|
205
|
+
//#endregion
|
|
206
|
+
export { Presence, PresenceMessage, PresenceModel, Spring, SpringMessage, SpringModel, Tween, TweenMessage, TweenModel, isVisible };
|
|
207
|
+
|
|
208
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/motion/presence.ts","../../src/motion/spring.ts","../../src/motion/tween.ts"],"sourcesContent":["/**\n * Presence as a bundle: mount-transition state for exit animations. `Hide`\n * starts a timed `hiding` phase; the `Hidden` fact it yields carries the\n * generation it was scheduled under, so a `Show` in between wins and the\n * late fact is ignored. The clock is Effect's, so tests drive it.\n */\nimport { Effect, Schema } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport type * as Update from 'foldkit/update'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const PresenceModel = Schema.Struct({\n phase: Schema.Literals(['shown', 'hiding', 'hidden']),\n generation: Schema.Number,\n})\nexport type PresenceModel = typeof PresenceModel.Type\n\nexport const PresenceMessage = defineMessageUnion({\n Show: {},\n Hide: {},\n Hidden: { generation: Schema.Number },\n})\nexport type PresenceMessage = typeof PresenceMessage.Type\n\n/** Whether the content renders: shown or mid-exit. */\nexport const isVisible = (model: PresenceModel): boolean => model.phase !== 'hidden'\n\nexport const Presence = Bundle.make<\n 'Presence',\n PresenceModel,\n PresenceMessage,\n { readonly durationMs: number },\n never,\n never,\n never,\n void,\n {},\n {}\n>('Presence', {\n Model: PresenceModel,\n Message: PresenceMessage,\n args: Schema.Struct({\n durationMs: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n }),\n init: () => ({ model: { phase: 'hidden', generation: 0 } }),\n update: (model, message, args) =>\n PresenceMessage.match<Update.ReturnWithOutMessage<PresenceModel, PresenceMessage, never>>(\n message,\n {\n // Every new transition invalidates pending timeouts.\n Show: () => ({\n model: { ...model, phase: 'shown' as const, generation: model.generation + 1 },\n }),\n Hide: () => {\n const generation = model.generation + 1\n return {\n model: { ...model, phase: 'hiding' as const, generation },\n commands: [\n {\n name: 'Presence.hide',\n args: { generation },\n effect: Effect.as(\n Effect.sleep(args.durationMs),\n PresenceMessage.Hidden({ generation }),\n ),\n },\n ],\n }\n },\n Hidden: ({ generation }) =>\n generation === model.generation\n ? { model: { ...model, phase: 'hidden' as const } }\n : { model },\n },\n ),\n})\n","/**\n * A spring as a bundle: a number pulled toward a target with stiffness and\n * damping, in the Model. Semi-implicit Euler at a fixed 16ms step, so the\n * trajectory is deterministic under TestClock; the stream ends with\n * Finished, and the value rests exactly at `to`. Underdamped springs\n * overshoot — that is the point — but the rest value is still exact.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const SpringModel = Schema.Struct({\n value: Schema.Number,\n velocity: Schema.Number,\n running: Schema.Boolean,\n})\nexport type SpringModel = typeof SpringModel.Type\n\nexport const SpringMessage = defineMessageUnion({\n Started: {},\n Stopped: {},\n Ticked: { value: Schema.Number, velocity: Schema.Number },\n /** Carries the exact end value: the integrator stops near it, not on it. */\n Finished: { value: Schema.Number },\n})\nexport type SpringMessage = typeof SpringMessage.Type\n\nconst step = 16\nconst epsilon = 0.001\n\ninterface Point {\n readonly x: number\n readonly v: number\n}\n\nconst settled = (point: Point, to: number): boolean =>\n Math.abs(to - point.x) < epsilon && Math.abs(point.v) < epsilon\n\nexport const Spring = Bundle.make('Spring', {\n Model: SpringModel,\n Message: SpringMessage,\n args: Schema.Struct({\n from: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n to: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n stiffness: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n damping: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n }),\n init: args => ({ model: { value: args.from, velocity: 0, running: false } }),\n update: (model, message) =>\n SpringMessage.match(message, {\n Started: () => ({ model: { ...model, running: true } }),\n Stopped: () => ({ model: { ...model, running: false } }),\n Ticked: ({ value, velocity }) => ({ model: { ...model, value, velocity } }),\n Finished: ({ value }) => ({ model: { ...model, value, velocity: 0, running: false } }),\n }),\n subscriptions: ({\n from,\n to,\n stiffness,\n damping,\n }): Subscription.Subscriptions<SpringModel, SpringMessage> =>\n Subscription.make<SpringModel, SpringMessage>()(entry => ({\n ticks: entry(\n { running: Schema.Boolean },\n {\n modelToDependencies: model => ({ running: model.running }),\n dependenciesToStream: ({ running }) => {\n if (!running) return Stream.empty\n // One integration step per tick, not per elapsed wall time: the\n // trajectory is identical on the live clock and TestClock, and a\n // stalled event loop slows the animation instead of catching up\n // all at once.\n const dt = step / 1000\n const points = Stream.tick(step).pipe(\n Stream.scan({ x: from, v: 0 }, point => {\n const force = -stiffness * (point.x - to) - damping * point.v\n const v = point.v + force * dt\n return { x: point.x + v * dt, v }\n }),\n // scan emits the seed (no movement yet) before the first step.\n Stream.drop(1),\n )\n return Stream.concat(\n points.pipe(\n Stream.takeUntil(point => settled(point, to)),\n Stream.map(point => SpringMessage.Ticked({ value: point.x, velocity: point.v })),\n ),\n Stream.make(SpringMessage.Finished({ value: to })),\n )\n },\n },\n ),\n })),\n})\n","/**\n * A tween as a bundle: a number animated from one value to another over a\n * duration, in the Model. Progress comes from Effect's clock, so tests drive\n * it with TestClock; the stream ends with Finished, and the value rests at\n * `to`. Linear interpolation only: easing curves stay the application's job.\n */\nimport { Clock, Effect, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const TweenModel = Schema.Struct({ value: Schema.Number, running: Schema.Boolean })\nexport type TweenModel = typeof TweenModel.Type\n\nexport const TweenMessage = defineMessageUnion({\n Started: {},\n Stopped: {},\n Ticked: { value: Schema.Number },\n /** Carries the exact end value: the stream may stop on either side of it. */\n Finished: { value: Schema.Number },\n})\nexport type TweenMessage = typeof TweenMessage.Type\n\nexport const Tween = Bundle.make('Tween', {\n Model: TweenModel,\n Message: TweenMessage,\n args: Schema.Struct({\n from: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n to: Schema.Number.pipe(Schema.check(Schema.isFinite())),\n ms: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())),\n }),\n init: args => ({ model: { value: args.from, running: false } }),\n update: (model, message) =>\n TweenMessage.match(message, {\n Started: () => ({ model: { ...model, running: true } }),\n Stopped: () => ({ model: { ...model, running: false } }),\n Ticked: ({ value }) => ({ model: { ...model, value } }),\n Finished: ({ value }) => ({ model: { ...model, value, running: false } }),\n }),\n subscriptions: ({ from, to, ms }): Subscription.Subscriptions<TweenModel, TweenMessage> =>\n Subscription.make<TweenModel, TweenMessage>()(entry => ({\n ticks: entry(\n { running: Schema.Boolean },\n {\n modelToDependencies: model => ({ running: model.running }),\n dependenciesToStream: ({ running }) =>\n running\n ? Stream.unwrap(\n Clock.currentTimeMillis.pipe(\n Effect.map(startedAt => {\n const progress = Stream.tick(16).pipe(\n Stream.mapEffect(() => Clock.currentTimeMillis),\n Stream.map(now => Math.min(1, (now - startedAt) / ms)),\n )\n return Stream.concat(\n progress.pipe(\n Stream.takeUntil(done => done >= 1),\n Stream.map(done =>\n TweenMessage.Ticked({ value: from + (to - from) * done }),\n ),\n ),\n Stream.make(TweenMessage.Finished({ value: to })),\n )\n }),\n ),\n )\n : Stream.empty,\n },\n ),\n })),\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { Option, Queue, Schema, Stream } from "effect";
|
|
2
|
+
import { Bundle } from "foldkit-bundle";
|
|
3
|
+
import * as ManagedResource from "foldkit/managedResource";
|
|
4
|
+
import { Command } from "foldkit/command";
|
|
5
|
+
import * as Update from "foldkit/update";
|
|
6
|
+
//#region src/net/broadcast.d.ts
|
|
7
|
+
export declare const BroadcastMessage: import("foldkit/message").MessageUnion<{
|
|
8
|
+
/** Notifies without storing: project the payload into your own field to keep it. */
|
|
9
|
+
readonly Received: {
|
|
10
|
+
readonly data: Schema.Unknown;
|
|
11
|
+
};
|
|
12
|
+
readonly Posted: {};
|
|
13
|
+
readonly BroadcastFailed: {
|
|
14
|
+
readonly message: Schema.String;
|
|
15
|
+
};
|
|
16
|
+
}>;
|
|
17
|
+
export type BroadcastMessage = typeof BroadcastMessage.Type;
|
|
18
|
+
/** The slice of a BroadcastChannel this module needs; satisfied by the platform class. */
|
|
19
|
+
export interface BroadcastChannelHandle {
|
|
20
|
+
postMessage(data: unknown): void;
|
|
21
|
+
close(): void;
|
|
22
|
+
onmessage: ((event: {
|
|
23
|
+
readonly data: unknown;
|
|
24
|
+
}) => void) | null;
|
|
25
|
+
}
|
|
26
|
+
type ChannelFactory = (name: string) => BroadcastChannelHandle;
|
|
27
|
+
/**
|
|
28
|
+
* Every post to `name` from another channel, as Messages. Without a
|
|
29
|
+
* BroadcastChannel API the stream is empty instead of throwing; closing the
|
|
30
|
+
* stream closes the channel.
|
|
31
|
+
*/
|
|
32
|
+
export declare const broadcastMessages: (name: string, create?: ChannelFactory | undefined) => Stream.Stream<BroadcastMessage>;
|
|
33
|
+
/**
|
|
34
|
+
* Posts one message to `name`. Opens a channel, posts, and closes it: the
|
|
35
|
+
* send is one-shot, so it is a Command, not a resource. Without a
|
|
36
|
+
* BroadcastChannel API — or when the post throws — it yields
|
|
37
|
+
* `BroadcastFailed` instead of throwing.
|
|
38
|
+
*/
|
|
39
|
+
export declare const postBroadcast: (name: string, data: unknown, create?: ChannelFactory | undefined) => Command<BroadcastMessage, never, never>;
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/net/online.d.ts
|
|
42
|
+
export declare const OnlineModel: Schema.Struct<{
|
|
43
|
+
readonly online: Schema.Boolean;
|
|
44
|
+
}>;
|
|
45
|
+
export type OnlineModel = typeof OnlineModel.Type;
|
|
46
|
+
export declare const OnlineMessage: import("foldkit/message").MessageUnion<{
|
|
47
|
+
readonly Changed: {
|
|
48
|
+
readonly online: Schema.Boolean;
|
|
49
|
+
};
|
|
50
|
+
}>;
|
|
51
|
+
export type OnlineMessage = typeof OnlineMessage.Type;
|
|
52
|
+
export declare const Online: Bundle.Bundle<"Online", void, {
|
|
53
|
+
online: boolean;
|
|
54
|
+
}, {
|
|
55
|
+
readonly _tag: "Changed";
|
|
56
|
+
readonly online: boolean;
|
|
57
|
+
}, never, never, never, void, {}, {}>;
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/net/sse.d.ts
|
|
60
|
+
/** The slice of an EventSource the bundle needs; satisfied by the platform class. */
|
|
61
|
+
export interface SourceHandle {
|
|
62
|
+
readonly readyState: number;
|
|
63
|
+
close(): void;
|
|
64
|
+
onopen: ((event: any) => void) | null;
|
|
65
|
+
onmessage: ((event: any) => void) | null;
|
|
66
|
+
onerror: ((event: any) => void) | null;
|
|
67
|
+
}
|
|
68
|
+
export declare const SseModel: Schema.Struct<{
|
|
69
|
+
readonly url: Schema.String;
|
|
70
|
+
readonly status: Schema.Literals<readonly ["closed", "connecting", "open"]>;
|
|
71
|
+
readonly lastError: Schema.NullOr<Schema.String>;
|
|
72
|
+
}>;
|
|
73
|
+
export type SseModel = typeof SseModel.Type;
|
|
74
|
+
export declare const SseMessage: import("foldkit/message").MessageUnion<{
|
|
75
|
+
readonly Connecting: {};
|
|
76
|
+
readonly Opened: {};
|
|
77
|
+
/** Notifies without storing: project the payload into your own field to keep it. */
|
|
78
|
+
readonly Received: {
|
|
79
|
+
readonly data: Schema.String;
|
|
80
|
+
};
|
|
81
|
+
readonly Closed: {};
|
|
82
|
+
readonly Failed: {
|
|
83
|
+
readonly message: Schema.String;
|
|
84
|
+
};
|
|
85
|
+
}>;
|
|
86
|
+
export type SseMessage = typeof SseMessage.Type;
|
|
87
|
+
/** The registry value behind the source tag: the live source and its incoming queue. */
|
|
88
|
+
export interface AcquiredSource {
|
|
89
|
+
readonly source: SourceHandle;
|
|
90
|
+
readonly events: Queue.Queue<SseMessage>;
|
|
91
|
+
}
|
|
92
|
+
/** The live stream, for the incoming subscription. One assembly holds one. */
|
|
93
|
+
export declare const Source: ManagedResource.ManagedResource<AcquiredSource, ManagedResource.ManagedResourceService<"sse">>;
|
|
94
|
+
/** The source service a placed SSE stream requires. */
|
|
95
|
+
export type SourceService = ManagedResource.ServiceOf<typeof Source>;
|
|
96
|
+
export declare const sse: <const Name extends string>(config: {
|
|
97
|
+
readonly name: Name;
|
|
98
|
+
/** Defaults to the platform EventSource, read lazily so tests can substitute a double. */
|
|
99
|
+
readonly createSource?: ((url: string) => SourceHandle) | undefined;
|
|
100
|
+
}) => Bundle.Bundle<Name, {
|
|
101
|
+
readonly url: string;
|
|
102
|
+
}, {
|
|
103
|
+
readonly url: string;
|
|
104
|
+
readonly status: "closed" | "connecting" | "open";
|
|
105
|
+
readonly lastError: string | null;
|
|
106
|
+
}, {
|
|
107
|
+
readonly _tag: "Connecting";
|
|
108
|
+
} | {
|
|
109
|
+
readonly _tag: "Opened";
|
|
110
|
+
} | {
|
|
111
|
+
readonly _tag: "Closed";
|
|
112
|
+
} | {
|
|
113
|
+
readonly _tag: "Received";
|
|
114
|
+
readonly data: string;
|
|
115
|
+
} | {
|
|
116
|
+
readonly _tag: "Failed";
|
|
117
|
+
readonly message: string;
|
|
118
|
+
}, never, ManagedResource.ManagedResourceService<"sse">, ManagedResource.ManagedResourceService<"sse">, void, Readonly<{
|
|
119
|
+
source: ManagedResource.Entry<SseModel, SseMessage, Option.Option<string>, AcquiredSource, SourceService, () => SseMessage>;
|
|
120
|
+
}>, {}>;
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/net/websocket.d.ts
|
|
123
|
+
/** The slice of a WebSocket the bundle needs; satisfied by the platform socket and test doubles. Handler events are `any`: the DOM and `ws` type them differently, and only `data` is read. */
|
|
124
|
+
export interface SocketHandle {
|
|
125
|
+
readonly readyState: number;
|
|
126
|
+
send(data: string): void;
|
|
127
|
+
close(): void;
|
|
128
|
+
onopen: ((event: any) => void) | null;
|
|
129
|
+
onmessage: ((event: any) => void) | null;
|
|
130
|
+
onclose: ((event: any) => void) | null;
|
|
131
|
+
onerror: ((event: any) => void) | null;
|
|
132
|
+
}
|
|
133
|
+
export declare const WebSocketModel: Schema.Struct<{
|
|
134
|
+
readonly url: Schema.String;
|
|
135
|
+
readonly status: Schema.Literals<readonly ["closed", "connecting", "open"]>;
|
|
136
|
+
readonly lastError: Schema.NullOr<Schema.String>;
|
|
137
|
+
}>;
|
|
138
|
+
export type WebSocketModel = typeof WebSocketModel.Type;
|
|
139
|
+
export declare const WebSocketMessage: import("foldkit/message").MessageUnion<{
|
|
140
|
+
readonly Connecting: {};
|
|
141
|
+
readonly Opened: {};
|
|
142
|
+
/** Notifies without storing: project the payload into your own field to keep it. */
|
|
143
|
+
readonly Received: {
|
|
144
|
+
readonly data: Schema.String;
|
|
145
|
+
};
|
|
146
|
+
readonly Closed: {};
|
|
147
|
+
readonly Failed: {
|
|
148
|
+
readonly message: Schema.String;
|
|
149
|
+
};
|
|
150
|
+
readonly SendFailed: {
|
|
151
|
+
readonly message: Schema.String;
|
|
152
|
+
};
|
|
153
|
+
/** Acknowledges dispatch, not delivery; update ignores it. */
|
|
154
|
+
readonly Sent: {};
|
|
155
|
+
}>;
|
|
156
|
+
export type WebSocketMessage = typeof WebSocketMessage.Type;
|
|
157
|
+
/** The registry value behind the socket tag: the live socket and its incoming queue. */
|
|
158
|
+
export interface Acquired {
|
|
159
|
+
readonly socket: SocketHandle;
|
|
160
|
+
readonly events: Queue.Queue<WebSocketMessage>;
|
|
161
|
+
}
|
|
162
|
+
/** The live connection, for `send` and the incoming stream. One assembly holds one. */
|
|
163
|
+
export declare const Socket: ManagedResource.ManagedResource<Acquired, ManagedResource.ManagedResourceService<"websocket">>;
|
|
164
|
+
/** The socket service a placed WebSocket's streams and send commands require. */
|
|
165
|
+
export type SocketService = ManagedResource.ServiceOf<typeof Socket>;
|
|
166
|
+
export declare const websocket: <const Name extends string>(config: {
|
|
167
|
+
readonly name: Name;
|
|
168
|
+
/** Defaults to the platform WebSocket, read lazily so tests can substitute a double. */
|
|
169
|
+
readonly createSocket?: ((url: string) => SocketHandle) | undefined;
|
|
170
|
+
}) => Bundle.Bundle<Name, {
|
|
171
|
+
readonly url: string;
|
|
172
|
+
}, {
|
|
173
|
+
readonly url: string;
|
|
174
|
+
readonly status: "closed" | "connecting" | "open";
|
|
175
|
+
readonly lastError: string | null;
|
|
176
|
+
}, {
|
|
177
|
+
readonly _tag: "Received";
|
|
178
|
+
readonly data: string;
|
|
179
|
+
} | {
|
|
180
|
+
readonly _tag: "Connecting";
|
|
181
|
+
} | {
|
|
182
|
+
readonly _tag: "Opened";
|
|
183
|
+
} | {
|
|
184
|
+
readonly _tag: "Closed";
|
|
185
|
+
} | {
|
|
186
|
+
readonly _tag: "Failed";
|
|
187
|
+
readonly message: string;
|
|
188
|
+
} | {
|
|
189
|
+
readonly _tag: "SendFailed";
|
|
190
|
+
readonly message: string;
|
|
191
|
+
} | {
|
|
192
|
+
readonly _tag: "Sent";
|
|
193
|
+
}, never, ManagedResource.ManagedResourceService<"websocket">, ManagedResource.ManagedResourceService<"websocket">, void, Readonly<{
|
|
194
|
+
socket: ManagedResource.Entry<WebSocketModel, WebSocketMessage, Option.Option<string>, Acquired, SocketService, () => WebSocketMessage>;
|
|
195
|
+
}>, Readonly<{
|
|
196
|
+
send: (model: WebSocketModel, data: string) => Update.ReturnWithOutMessage<WebSocketModel, WebSocketMessage, never, SocketService>;
|
|
197
|
+
}>>;
|
|
198
|
+
//#endregion
|
|
199
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/net/broadcast.ts","../../src/net/online.ts","../../src/net/sse.ts","../../src/net/websocket.ts"],"mappings":";;;;;;qBAWa,4CAAgB;;;;;;;;;;YAMjB,0BAA0B,iBAAiB;;iBAGtC;EACf,YAAY;EACZ;EACA,aAAa;aAAkB;;;KAG5B,kBAAkB,iBAAiB;;;;;;qBAmB3B,oBAAiB,cAChB,SAEH,+BACR,OAAO,OAAO;;;;;;;qBAyBJ,gBAAa,cACZ,eACC,SACJ,+BACR,QAAQ;;;qBCpEE,aAAW,OAAA;;;YACZ,qBAAqB,YAAY;qBAEhC,yCAAa;;qBAAb,OAAA;;;YACD,uBAAuB,cAAc;qBAkBpC,QAAM,OAAA;;;;;;;;;iBClBF;WACN;EACT;EACA,UAAU;EACV,aAAa;EACb,WAAW;;qBAGA,UAAQ,OAAA;;;;;YAKT,kBAAkB,SAAS;qBAE1B,sCAAU;;;;;;;;;;;;YAQX,oBAAoB,WAAW;;iBAG1B;WACN,QAAQ;WACR,QAAQ,MAAM,MAAM;;;qBAIlB,QAAM,gBAAA,gBAAA,gBAAA,gBAAA;;YAGP,gBAAgB,gBAAgB,iBAAiB;qBAgBhD,YAAa,qBAAmB;WAClC,MAAM;;WAEN,iBAAiB,gBAAgB;MAC3C,OAAA,OAAA;WAOmB;;;;;;;;;;;;;;;;;;EAxBV,QAAA,gBAAgB,MACtB,UACA,YACA,OAAO,gBACP,gBACA,qBACM;;;;;iBC1CO;WACN;EACT,KAAK;EACL;EACA,UAAU;EACV,aAAa;EACb,WAAW;EACX,WAAW;;qBAGA,gBAAc,OAAA;;;;;YAKf,wBAAwB,eAAe;qBAEtC,4CAAgB;;;;;;;;;;;;;;;;;YAWjB,0BAA0B,iBAAiB;;iBAGtC;WACN,QAAQ;WACR,QAAQ,MAAM,MAAM;;;qBAIlB,QAAM,gBAAA,gBAAA,UAAA,gBAAA;;YAGP,gBAAgB,gBAAgB,iBAAiB;qBAgChD,kBAAmB,qBAAmB;WACxC,MAAM;;WAEN,iBAAiB,gBAAgB;MAC3C,OAAA,OAAA;WAOmB;;;;;;;;;;;;;;;;;;;;;;;EAxCV,QAAA,gBAAgB,MACtB,gBACA,kBACA,OAAO,gBACP,UACA,qBACM;;EAKF,OACJ,OAAO,gBACP,iBACG,OAAO,qBAAqB,gBAAgB,yBAAyB"}
|