foldkit 0.160.0 → 0.161.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.
Files changed (56) hide show
  1. package/dist/calendar/index.d.ts +1 -1
  2. package/dist/calendar/index.d.ts.map +1 -1
  3. package/dist/calendar/index.js +1 -1
  4. package/dist/calendar/locale.d.ts +219 -10
  5. package/dist/calendar/locale.d.ts.map +1 -1
  6. package/dist/calendar/locale.js +166 -82
  7. package/dist/calendar/public.d.ts +1 -1
  8. package/dist/calendar/public.d.ts.map +1 -1
  9. package/dist/calendar/public.js +1 -1
  10. package/dist/command/index.d.ts +9 -12
  11. package/dist/command/index.d.ts.map +1 -1
  12. package/dist/command/index.js +9 -12
  13. package/dist/devTools/protocol.d.ts +9 -1
  14. package/dist/devTools/protocol.d.ts.map +1 -1
  15. package/dist/devTools/protocol.js +2 -1
  16. package/dist/devTools/serialize.d.ts.map +1 -1
  17. package/dist/devTools/serialize.js +1 -0
  18. package/dist/devTools/store.d.ts +3 -0
  19. package/dist/devTools/store.d.ts.map +1 -1
  20. package/dist/devTools/store.js +24 -0
  21. package/dist/dom/dialogRuntime.d.ts +9 -0
  22. package/dist/dom/dialogRuntime.d.ts.map +1 -0
  23. package/dist/dom/dialogRuntime.js +3 -0
  24. package/dist/dom/dom.d.ts +22 -11
  25. package/dist/dom/dom.d.ts.map +1 -1
  26. package/dist/dom/dom.js +196 -18
  27. package/dist/dom/inert.d.ts +9 -0
  28. package/dist/dom/inert.d.ts.map +1 -1
  29. package/dist/dom/inert.js +74 -2
  30. package/dist/managedResource/managedResource.d.ts +44 -2
  31. package/dist/managedResource/managedResource.d.ts.map +1 -1
  32. package/dist/managedResource/managedResource.js +40 -10
  33. package/dist/runtime/devToolsIntegration.d.ts +8 -3
  34. package/dist/runtime/devToolsIntegration.d.ts.map +1 -1
  35. package/dist/runtime/devToolsIntegration.js +20 -3
  36. package/dist/runtime/runtime.d.ts.map +1 -1
  37. package/dist/runtime/runtime.js +54 -12
  38. package/dist/subscription/fromEvent.d.ts +103 -28
  39. package/dist/subscription/fromEvent.d.ts.map +1 -1
  40. package/dist/subscription/fromEvent.js +48 -27
  41. package/dist/subscription/keyboardShortcuts.d.ts +110 -0
  42. package/dist/subscription/keyboardShortcuts.d.ts.map +1 -0
  43. package/dist/subscription/keyboardShortcuts.js +444 -0
  44. package/dist/subscription/public.d.ts +3 -1
  45. package/dist/subscription/public.d.ts.map +1 -1
  46. package/dist/subscription/public.js +1 -0
  47. package/dist/subscription/subscription.d.ts +61 -1
  48. package/dist/subscription/subscription.d.ts.map +1 -1
  49. package/dist/subscription/subscription.js +51 -8
  50. package/dist/update/public.d.ts +1 -1
  51. package/dist/update/public.d.ts.map +1 -1
  52. package/dist/update/public.js +1 -1
  53. package/dist/update/update.d.ts +189 -1
  54. package/dist/update/update.d.ts.map +1 -1
  55. package/dist/update/update.js +178 -16
  56. package/package.json +1 -1
@@ -1,11 +1,17 @@
1
1
  import { Array, Effect, Option, PubSub, Stream, SubscriptionRef, } from 'effect';
2
2
  import { createDevToolsStore, } from '../devTools/store.js';
3
+ import { extractSubmodelInfo, isTagged } from '../devTools/submodelPath.js';
3
4
  import { startWebSocketBridge } from '../devTools/webSocketBridge.js';
4
5
  import { MountRuntime, liveViewStateChanges, } from '../mount/index.js';
5
6
  import { resolveDevToolsConfig, resolveDevToolsKeyframeInterval, resolveDevToolsMaxEntries, resolveExcludeFromHistoryTags, } from './devToolsConfig.js';
6
- const toCommandRecord = (command) => command.args !== undefined
7
- ? { name: command.name, args: command.args }
8
- : { name: command.name };
7
+ const toCommandRecord = ({ id, command }) => command.args !== undefined
8
+ ? {
9
+ id,
10
+ name: command.name,
11
+ args: command.args,
12
+ maybeSubmodelPath: Option.none(),
13
+ }
14
+ : { id, name: command.name, maybeSubmodelPath: Option.none() };
9
15
  /**
10
16
  * Builds the DevTools integration for one runtime from the `devTools`
11
17
  * config. `update` and `maybeFreezeModel` drive time-travel replay, and
@@ -169,8 +175,18 @@ export const makeDevToolsIntegration = ({ devTools, update, maybeFreezeModel, en
169
175
  }
170
176
  }
171
177
  };
178
+ const recordCommandResult = (id, message) => {
179
+ if (devToolsStore === null) {
180
+ return;
181
+ }
182
+ const submodelPath = isTagged(message)
183
+ ? extractSubmodelInfo(message._tag, message).submodelPath
184
+ : [];
185
+ Effect.runFork(devToolsStore.recordResolvedCommand(id, submodelPath));
186
+ };
172
187
  const resumeDevTools = Effect.suspend(() => devToolsStore !== null ? devToolsStore.resume : Effect.void);
173
188
  return {
189
+ isRecordingCommands: Option.isSome(resolvedDevTools),
174
190
  mountTracker,
175
191
  mountRuntime,
176
192
  drainMountEvents,
@@ -181,6 +197,7 @@ export const makeDevToolsIntegration = ({ devTools, update, maybeFreezeModel, en
181
197
  resumeDevTools,
182
198
  recordInit,
183
199
  recordMessage,
200
+ recordCommandResult,
184
201
  attachRenderedMounts,
185
202
  };
186
203
  });
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  import { Array, Duration, Effect, Exit, Option, Predicate, PubSub, Schema, pipe, } from 'effect';
2
+ import { DialogRuntime } from '../dom/dialogRuntime.js';
3
+ import { releaseDialogResources } from '../dom/dom.js';
2
4
  import { __htmlBuilder as htmlBuilderFor, } from '../html/index.js';
3
5
  import { RenderCommit, createCommitNotifier } from '../render/commit.js';
4
6
  import { fromString as urlFromString } from '../url/index.js';
@@ -56,6 +58,7 @@ export const makeRuntime = ({ ports, kind, Model, Flags: FlagsCodec, configuredF
56
58
  // the same signal. A commit in one embedded application must never wake a
57
59
  // `Render.afterCommit` awaiting inside another.
58
60
  const commitNotifier = createCommitNotifier();
61
+ const dialogIds = new Set();
59
62
  return Effect.scoped(Effect.gen(function* () {
60
63
  if (runtimeId === '') {
61
64
  return yield* Effect.die(new Error('[foldkit] Runtime container must have an `id` for Model preservation across reloads. ' +
@@ -174,7 +177,12 @@ export const makeRuntime = ({ ports, kind, Model, Flags: FlagsCodec, configuredF
174
177
  maybeFreezeModel,
175
178
  enqueueMessageEffect,
176
179
  });
177
- const { installDevToolsStore, recordInit, recordMessage } = devToolsIntegration;
180
+ const { isRecordingCommands, installDevToolsStore, recordInit, recordMessage, recordCommandResult, } = devToolsIntegration;
181
+ let nextCommandId = 0;
182
+ const assignCommandIds = (commands) => Array.map(commands, command => ({
183
+ id: nextCommandId++,
184
+ command,
185
+ }));
178
186
  if (import.meta.hot) {
179
187
  yield* Effect.addFinalizer(() => Effect.sync(() => duplicateIdScanner?.cancel()));
180
188
  }
@@ -210,6 +218,7 @@ export const makeRuntime = ({ ports, kind, Model, Flags: FlagsCodec, configuredF
210
218
  messageQueue,
211
219
  devToolsIntegration,
212
220
  });
221
+ yield* Effect.addFinalizer(() => Effect.forEach(pipe(dialogIds, Array.fromIterable, Array.reverse), releaseDialogResources, { discard: true }));
213
222
  // NOTE: the fork is deferred one microtask so a Command's Effect
214
223
  // never begins on the dispatching stack. Commands are facts from
215
224
  // outside the update loop; their results always arrive
@@ -223,7 +232,7 @@ export const makeRuntime = ({ ports, kind, Model, Flags: FlagsCodec, configuredF
223
232
  // Stream step) would reschedule through clamped `setTimeout`
224
233
  // instead of the browser microtask scheduler carried by
225
234
  // `runtimeContext`.
226
- const forkCommand = (command, message) => {
235
+ const forkCommand = (command, message, id) => {
227
236
  queueMicrotask(() => {
228
237
  // NOTE: `isCrashed` as well as `isRuntimeDisposed`. A crash is
229
238
  // terminal but does not dispose the runtime, and a Command forked
@@ -235,9 +244,13 @@ export const makeRuntime = ({ ports, kind, Model, Flags: FlagsCodec, configuredF
235
244
  if (status.isRuntimeDisposed || status.isCrashed) {
236
245
  return;
237
246
  }
238
- Effect.runForkWith(runtimeContext)(Effect.forkIn(runtimeScope)(command.effect.pipe(Effect.withSpan(command.name, {
247
+ const providedCommand = command.effect.pipe(Effect.withSpan(command.name, {
239
248
  attributes: command.args ?? {},
240
- }), provideAllResources, Effect.flatMap(enqueueMessageEffect), Effect.catchCause(cause => crashWith(cause, message)))));
249
+ }), provideAllResources);
250
+ const recordedCommand = id !== undefined
251
+ ? providedCommand.pipe(Effect.tap(result => Effect.sync(() => recordCommandResult(id, result))))
252
+ : providedCommand;
253
+ Effect.runForkWith(runtimeContext)(Effect.forkIn(runtimeScope)(recordedCommand.pipe(Effect.flatMap(enqueueMessageEffect), Effect.catchCause(cause => crashWith(cause, message)))));
241
254
  });
242
255
  };
243
256
  const processMessagePlain = (message) => {
@@ -263,14 +276,22 @@ export const makeRuntime = ({ ports, kind, Model, Flags: FlagsCodec, configuredF
263
276
  }
264
277
  scheduleRenderFrame();
265
278
  }
266
- if (!Array.isReadonlyArrayEmpty(commands)) {
279
+ if (isRecordingCommands) {
280
+ const commandInvocations = assignCommandIds(commands);
281
+ for (const { command, id } of commandInvocations) {
282
+ forkCommand(
283
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
284
+ command, Option.some(message), id);
285
+ }
286
+ recordMessage(message, currentModel, nextModel, commandInvocations);
287
+ }
288
+ else {
267
289
  for (const command of commands) {
268
290
  forkCommand(
269
291
  /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
270
292
  command, Option.some(message));
271
293
  }
272
294
  }
273
- recordMessage(message, currentModel, nextModel, commands);
274
295
  };
275
296
  yield* installDevToolsStore(devToolsRenderBridge);
276
297
  const initRenderExit = yield* Effect.exit(render(initModel, Option.none()));
@@ -285,7 +306,12 @@ export const makeRuntime = ({ ports, kind, Model, Flags: FlagsCodec, configuredF
285
306
  if (isPreserveScrollActive) {
286
307
  yield* restorePreservedScrollPosition(runtimeId);
287
308
  }
288
- yield* recordInit(initModel, initCommands);
309
+ const initCommandInvocations = isRecordingCommands
310
+ ? assignCommandIds(initCommands)
311
+ : [];
312
+ if (isRecordingCommands) {
313
+ yield* recordInit(initModel, initCommandInvocations);
314
+ }
289
315
  if (subscriptions) {
290
316
  yield* forkSubscriptionFibers({
291
317
  subscriptions,
@@ -328,10 +354,19 @@ export const makeRuntime = ({ ports, kind, Model, Flags: FlagsCodec, configuredF
328
354
  // ManagedResources, ports) is attached. forkCommand also defers each
329
355
  // start by a microtask, so a fully synchronous init Command still
330
356
  // delivers its result asynchronously.
331
- for (const command of initCommands) {
332
- forkCommand(
333
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
334
- command, Option.none());
357
+ if (isRecordingCommands) {
358
+ for (const { command, id } of initCommandInvocations) {
359
+ forkCommand(
360
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
361
+ command, Option.none(), id);
362
+ }
363
+ }
364
+ else {
365
+ for (const command of initCommands) {
366
+ forkCommand(
367
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
368
+ command, Option.none());
369
+ }
335
370
  }
336
371
  completeBoot();
337
372
  // NOTE: suspend forever. Messages are processed synchronously on
@@ -340,7 +375,14 @@ export const makeRuntime = ({ ports, kind, Model, Flags: FlagsCodec, configuredF
340
375
  // runtime scope open until interruption (an embedded app's dispose)
341
376
  // or the document goes away.
342
377
  yield* Effect.never;
343
- })).pipe(Effect.provideService(RenderCommit, commitNotifier.service));
378
+ })).pipe(Effect.provideService(RenderCommit, commitNotifier.service), Effect.provideService(DialogRuntime, {
379
+ register: id => {
380
+ dialogIds.add(id);
381
+ },
382
+ unregister: id => {
383
+ dialogIds.delete(id);
384
+ },
385
+ }));
344
386
  };
345
387
  const start = (preservedModel) => startWith(Option.none(), preservedModel, 'Fresh');
346
388
  const program = {
@@ -1,4 +1,46 @@
1
1
  import { Option, Stream } from 'effect';
2
+ declare const EventMapMarker: unique symbol;
3
+ /**
4
+ * An `EventTarget` that declares the events it dispatches, so the `fromEvent`
5
+ * helpers can resolve an event name to its event type the way they do for
6
+ * `window`, `document`, and the DOM interfaces lib.dom declares event maps
7
+ * for.
8
+ *
9
+ * Annotate a target with this and the mapper's parameter follows from the
10
+ * event name, including a `CustomEvent`'s `detail`. A declared event overrides
11
+ * the corresponding native event and otherwise augments the target's native
12
+ * events, so an element that dispatches custom events can be annotated without
13
+ * losing events such as `click`. Any `EventTarget` is assignable to it, so the
14
+ * annotation is the only change needed.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const slowWarningTarget: Subscription.TypedEventTarget<{
19
+ * 'foldkit:slow-warning': CustomEvent<SlowWarningReport>
20
+ * }> = new EventTarget()
21
+ * ```
22
+ */
23
+ export interface TypedEventTarget<EventMap extends {
24
+ readonly [Type in keyof EventMap]: Event;
25
+ }> extends EventTarget {
26
+ readonly [EventMapMarker]?: EventMap;
27
+ }
28
+ type EventMapMarkerKey = typeof EventMapMarker;
29
+ type DeclaredEventMap<Target> = EventMapMarkerKey extends keyof Target ? NonNullable<Target[EventMapMarkerKey & keyof Target]> : never;
30
+ type EventFromHandler<Handler> = NonNullable<Handler> extends (event: infer EventType, ...args: ReadonlyArray<unknown>) => unknown ? EventType extends Event ? EventType : never : never;
31
+ type EventMapFromHandlers<Target> = {
32
+ readonly [Key in keyof Target as Key extends `on${infer Type}` ? [EventFromHandler<Target[Key]>] extends [never] ? never : Type : never]: EventFromHandler<Target[Key]>;
33
+ };
34
+ type OverrideEventMap<Base, Declared> = Omit<Base, keyof Declared> & Declared;
35
+ type ElementEventMapOf<Target> = Target extends HTMLVideoElement ? HTMLVideoElementEventMap : Target extends HTMLMediaElement ? HTMLMediaElementEventMap : Target extends HTMLBodyElement ? HTMLBodyElementEventMap : Target extends HTMLFrameSetElement ? HTMLFrameSetElementEventMap : Target extends HTMLElement ? HTMLElementEventMap : Target extends MathMLElement ? MathMLElementEventMap : Target extends SVGSVGElement ? SVGSVGElementEventMap : Target extends SVGElement ? SVGElementEventMap : Target extends Element ? ElementEventMap & GlobalEventHandlersEventMap : never;
36
+ type NativeEventMapOf<Target> = Target extends Window ? WindowEventMap : Target extends Document ? DocumentEventMap : Target extends XMLHttpRequest ? XMLHttpRequestEventMap : Target extends XMLHttpRequestEventTarget ? XMLHttpRequestEventTargetEventMap : [ElementEventMapOf<Target>] extends [never] ? EventMapFromHandlers<Target> : ElementEventMapOf<Target>;
37
+ type EventMapOfMember<Target> = [DeclaredEventMap<Target>] extends [never] ? [keyof NativeEventMapOf<Target>] extends [never] ? Readonly<Record<string, Event>> : NativeEventMapOf<Target> : [keyof NativeEventMapOf<Target>] extends [never] ? DeclaredEventMap<Target> : OverrideEventMap<NativeEventMapOf<Target>, DeclaredEventMap<Target>>;
38
+ type EventMapOf<Target> = Target extends unknown ? EventMapOfMember<Target> : never;
39
+ type EventTypeOf<Target> = keyof EventMapOf<Target> & string;
40
+ type EventOf<Target, Type extends EventTypeOf<Target>> = Type extends keyof EventMapOf<Target> ? EventMapOf<Target>[Type] extends infer Resolved extends Event ? Resolved : Event : Event;
41
+ type PreventDefaultEventListenerOptions = Omit<AddEventListenerOptions, 'passive'> & Readonly<{
42
+ passive?: false;
43
+ }>;
2
44
  /**
3
45
  * Configuration for the `fromEvent` Stream helper.
4
46
  *
@@ -8,6 +50,10 @@ import { Option, Stream } from 'effect';
8
50
  * pass the `EventTarget` directly for always-present globals like `window` or
9
51
  * `document`.
10
52
  *
53
+ * `type` is constrained to the event names the target declares, and
54
+ * `toMessage`'s parameter is the event those two resolve to. Annotating that
55
+ * parameter is checked against the resolved event rather than replacing it.
56
+ *
11
57
  * `toMessage(event)` transforms each dispatched event into a Message. The
12
58
  * mapper runs synchronously in the same call stack as the browser's event
13
59
  * dispatch, so calling `event.preventDefault()` inside it takes effect,
@@ -17,10 +63,10 @@ import { Option, Stream } from 'effect';
17
63
  * those events, or reach for `fromEventFilterMapPreventDefault`, which does
18
64
  * so for you.
19
65
  */
20
- export type FromEventConfig<EventType extends Event, Message> = Readonly<{
21
- target: EventTarget | (() => EventTarget);
22
- type: string;
23
- toMessage: (event: EventType) => Message;
66
+ export type FromEventConfig<Target extends EventTarget, Type extends EventTypeOf<Target>, Message> = Readonly<{
67
+ target: Target | (() => Target);
68
+ type: Type;
69
+ toMessage: (event: EventOf<Target, Type>) => Message;
24
70
  options?: AddEventListenerOptions;
25
71
  }>;
26
72
  /**
@@ -32,6 +78,10 @@ export type FromEventConfig<EventType extends Event, Message> = Readonly<{
32
78
  * pass the `EventTarget` directly for always-present globals like `window` or
33
79
  * `document`.
34
80
  *
81
+ * `type` is constrained to the event names the target declares, and
82
+ * `toMessage`'s parameter is the event those two resolve to. Annotating that
83
+ * parameter is checked against the resolved event rather than replacing it.
84
+ *
35
85
  * `toMessage(event)` returns `Option.some(message)` to emit a Message for the
36
86
  * event, or `Option.none()` to ignore it. The mapper runs synchronously in the
37
87
  * same call stack as the browser's event dispatch, so calling
@@ -41,10 +91,10 @@ export type FromEventConfig<EventType extends Event, Message> = Readonly<{
41
91
  * `options: { passive: false }` explicitly when cancelling those events, or
42
92
  * reach for `fromEventFilterMapPreventDefault`, which does so for you.
43
93
  */
44
- export type FromEventFilterMapConfig<EventType extends Event, Message> = Readonly<{
45
- target: EventTarget | (() => EventTarget);
46
- type: string;
47
- toMessage: (event: EventType) => Option.Option<Message>;
94
+ export type FromEventFilterMapConfig<Target extends EventTarget, Type extends EventTypeOf<Target>, Message> = Readonly<{
95
+ target: Target | (() => Target);
96
+ type: Type;
97
+ toMessage: (event: EventOf<Target, Type>) => Option.Option<Message>;
48
98
  options?: AddEventListenerOptions;
49
99
  }>;
50
100
  /**
@@ -56,6 +106,10 @@ export type FromEventFilterMapConfig<EventType extends Event, Message> = Readonl
56
106
  * pass the `EventTarget` directly for always-present globals like `window` or
57
107
  * `document`.
58
108
  *
109
+ * `type` is constrained to the event names the target declares, and
110
+ * `toMessage`'s parameter is the event those two resolve to. Annotating that
111
+ * parameter is checked against the resolved event rather than replacing it.
112
+ *
59
113
  * `toMessage(event)` returns `Option.some(message)` to mark the dispatch
60
114
  * handled, or `Option.none()` to leave the default behavior intact. For a
61
115
  * handled dispatch the helper calls `event.preventDefault()` and queues the
@@ -63,14 +117,15 @@ export type FromEventFilterMapConfig<EventType extends Event, Message> = Readonl
63
117
  * `preventDefault()`.
64
118
  *
65
119
  * `options.passive` defaults to `false` so `preventDefault()` keeps working
66
- * for the events browsers would otherwise register as passive. Passing
67
- * `passive: true` explicitly contradicts the helper's purpose and throws.
120
+ * for the events browsers would otherwise register as passive. The config
121
+ * rejects `passive: true`; the runtime guard also throws for unchecked
122
+ * JavaScript inputs.
68
123
  */
69
- export type FromEventFilterMapPreventDefaultConfig<EventType extends Event, Message> = Readonly<{
70
- target: EventTarget | (() => EventTarget);
71
- type: string;
72
- toMessage: (event: EventType) => Option.Option<Message>;
73
- options?: AddEventListenerOptions;
124
+ export type FromEventFilterMapPreventDefaultConfig<Target extends EventTarget, Type extends EventTypeOf<Target>, Message> = Readonly<{
125
+ target: Target | (() => Target);
126
+ type: Type;
127
+ toMessage: (event: EventOf<Target, Type>) => Option.Option<Message>;
128
+ options?: PreventDefaultEventListenerOptions;
74
129
  }>;
75
130
  /**
76
131
  * Build a Stream that emits a Message for the dispatches of a DOM event the
@@ -79,7 +134,9 @@ export type FromEventFilterMapPreventDefaultConfig<EventType extends Event, Mess
79
134
  *
80
135
  * This is the filtered variant of `fromEvent`. Its `toMessage` returns
81
136
  * `Option.some(message)` to emit and `Option.none()` to ignore the event, so a
82
- * single listener can react to some dispatches while passing on the rest.
137
+ * single listener can react to some dispatches while passing on the rest. A
138
+ * mapper that never emits produces a `Stream<never>`, which composes wherever
139
+ * a Message-producing Stream is expected.
83
140
  *
84
141
  * Reach for this over a downstream `Stream.filterMap` whenever the decision to
85
142
  * keep an event is paired with `event.preventDefault()`. The mapper runs
@@ -91,6 +148,13 @@ export type FromEventFilterMapPreventDefaultConfig<EventType extends Event, Mess
91
148
  * `options: { passive: false }` explicitly when cancelling those events, or
92
149
  * reach for `fromEventFilterMapPreventDefault`, which does so for you.
93
150
  *
151
+ * The target, the event name, and the event the mapper receives are one fact:
152
+ * `type` is constrained to the names the target declares, and the mapper's
153
+ * parameter is what those two resolve to, so annotating it narrows nothing and
154
+ * cannot contradict the name. A target that is neither annotated nor one
155
+ * lib.dom declares a map for accepts any name and reports `Event`; annotate it
156
+ * with {@link TypedEventTarget} to resolve its own events.
157
+ *
94
158
  * The listener lifecycle uses `Effect.acquireRelease`. The `addEventListener`
95
159
  * call happens inside the acquire Effect, and the matching
96
160
  * `removeEventListener` is registered only after acquire completes, so the
@@ -111,8 +175,8 @@ export type FromEventFilterMapPreventDefaultConfig<EventType extends Event, Mess
111
175
  * modelToDependencies: model => ({ isListening: model.isListening }),
112
176
  * dependenciesToStream: ({ isListening }) =>
113
177
  * Stream.when(
114
- * Subscription.fromEventFilterMap<KeyboardEvent, Message>({
115
- * target: document,
178
+ * Subscription.fromEventFilterMap({
179
+ * target: window,
116
180
  * type: 'keydown',
117
181
  * toMessage: event =>
118
182
  * event.key === 'Escape'
@@ -126,12 +190,19 @@ export type FromEventFilterMapPreventDefaultConfig<EventType extends Event, Mess
126
190
  * }))
127
191
  * ```
128
192
  */
129
- export declare const fromEventFilterMap: <EventType extends Event, Message>(config: FromEventFilterMapConfig<EventType, Message>) => Stream.Stream<Message>;
193
+ export declare const fromEventFilterMap: <Target extends EventTarget, Type extends EventTypeOf<Target>, Message>(config: FromEventFilterMapConfig<Target, Type, Message>) => Stream.Stream<Message>;
130
194
  /**
131
195
  * Build a Stream that emits a Message for every dispatch of a DOM event,
132
196
  * registering the listener when the Stream's scope opens and removing it when
133
197
  * the scope closes.
134
198
  *
199
+ * The target, the event name, and the event the mapper receives are one fact:
200
+ * `type` is constrained to the names the target declares, and the mapper's
201
+ * parameter is what those two resolve to, so annotating it narrows nothing and
202
+ * cannot contradict the name. A target that is neither annotated nor one
203
+ * lib.dom declares a map for accepts any name and reports `Event`; annotate it
204
+ * with {@link TypedEventTarget} to resolve its own events.
205
+ *
135
206
  * The listener lifecycle uses `Effect.acquireRelease`. The `addEventListener`
136
207
  * call happens inside the acquire Effect, and the matching
137
208
  * `removeEventListener` is registered only after acquire completes, so the
@@ -157,7 +228,7 @@ export declare const fromEventFilterMap: <EventType extends Event, Message>(conf
157
228
  * modelToDependencies: model => ({ isListening: model.isListening }),
158
229
  * dependenciesToStream: ({ isListening }) =>
159
230
  * Stream.when(
160
- * Subscription.fromEvent<KeyboardEvent, Message>({
231
+ * Subscription.fromEvent({
161
232
  * target: window,
162
233
  * type: 'keydown',
163
234
  * toMessage: event => Message.PressedKey({ key: event.key }),
@@ -169,7 +240,7 @@ export declare const fromEventFilterMap: <EventType extends Event, Message>(conf
169
240
  * }))
170
241
  * ```
171
242
  */
172
- export declare const fromEvent: <EventType extends Event, Message>(config: FromEventConfig<EventType, Message>) => Stream.Stream<Message>;
243
+ export declare const fromEvent: <Target extends EventTarget, Type extends EventTypeOf<Target>, Message>(config: FromEventConfig<Target, Type, Message>) => Stream.Stream<Message>;
173
244
  /**
174
245
  * Build a Stream that emits a Message for the dispatches of a DOM event the
175
246
  * mapper marks handled, calling `event.preventDefault()` on each of them,
@@ -186,8 +257,14 @@ export declare const fromEvent: <EventType extends Event, Message>(config: FromE
186
257
  * Because cancelling is the point, the listener registers with
187
258
  * `passive: false` when the config does not say otherwise. This keeps wheel
188
259
  * and touch events cancelable when a browser would otherwise make listeners
189
- * on a global target passive. Passing `passive: true` explicitly contradicts
190
- * the helper's purpose and throws.
260
+ * on a global target passive. The config rejects `passive: true`; the runtime
261
+ * guard also throws for unchecked JavaScript inputs.
262
+ *
263
+ * The target, event name, and mapper parameter are one fact: `type` is
264
+ * constrained to the names the target declares, and the mapper receives the
265
+ * event those two resolve to. A target with no declared event map accepts any
266
+ * name and reports `Event`; annotate it with {@link TypedEventTarget} to
267
+ * resolve its own events.
191
268
  *
192
269
  * The listener lifecycle uses `Effect.acquireRelease`. The `addEventListener`
193
270
  * call happens inside the acquire Effect, and the matching
@@ -209,10 +286,7 @@ export declare const fromEvent: <EventType extends Event, Message>(config: FromE
209
286
  * modelToDependencies: model => ({ isModalOpen: model.isModalOpen }),
210
287
  * dependenciesToStream: ({ isModalOpen }) =>
211
288
  * Stream.when(
212
- * Subscription.fromEventFilterMapPreventDefault<
213
- * WheelEvent,
214
- * Message
215
- * >({
289
+ * Subscription.fromEventFilterMapPreventDefault({
216
290
  * target: window,
217
291
  * type: 'wheel',
218
292
  * toMessage: () => Option.some(Message.SuppressedWheelScroll()),
@@ -224,5 +298,6 @@ export declare const fromEvent: <EventType extends Event, Message>(config: FromE
224
298
  * }))
225
299
  * ```
226
300
  */
227
- export declare const fromEventFilterMapPreventDefault: <EventType extends Event, Message>(config: FromEventFilterMapPreventDefaultConfig<EventType, Message>) => Stream.Stream<Message>;
301
+ export declare const fromEventFilterMapPreventDefault: <Target extends EventTarget, Type extends EventTypeOf<Target>, Message>(config: FromEventFilterMapPreventDefaultConfig<Target, Type, Message>) => Stream.Stream<Message>;
302
+ export {};
228
303
  //# sourceMappingURL=fromEvent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fromEvent.d.ts","sourceRoot":"","sources":["../../src/subscription/fromEvent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,MAAM,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEtD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,eAAe,CAAC,SAAS,SAAS,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IACvE,MAAM,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAA;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAA;IACxC,OAAO,CAAC,EAAE,uBAAuB,CAAA;CAClC,CAAC,CAAA;AAMF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,wBAAwB,CAClC,SAAS,SAAS,KAAK,EACvB,OAAO,IACL,QAAQ,CAAC;IACX,MAAM,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAA;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACvD,OAAO,CAAC,EAAE,uBAAuB,CAAA;CAClC,CAAC,CAAA;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,sCAAsC,CAChD,SAAS,SAAS,KAAK,EACvB,OAAO,IACL,QAAQ,CAAC;IACX,MAAM,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAA;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACvD,OAAO,CAAC,EAAE,uBAAuB,CAAA;CAClC,CAAC,CAAA;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,SAAS,KAAK,EAAE,OAAO,UACzD,wBAAwB,CAAC,SAAS,EAAE,OAAO,CAAC,KACnD,MAAM,CAAC,MAAM,CAAC,OAAO,CAsBrB,CAAA;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,SAAS,GAAI,SAAS,SAAS,KAAK,EAAE,OAAO,UAChD,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,KAC1C,MAAM,CAAC,MAAM,CAAC,OAAO,CAIpB,CAAA;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,gCAAgC,GAC3C,SAAS,SAAS,KAAK,EACvB,OAAO,UAEC,sCAAsC,CAAC,SAAS,EAAE,OAAO,CAAC,KACjE,MAAM,CAAC,MAAM,CAAC,OAAO,CA0BvB,CAAA"}
1
+ {"version":3,"file":"fromEvent.d.ts","sourceRoot":"","sources":["../../src/subscription/fromEvent.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,19 @@
1
1
  import { Effect, Option, Queue, Stream } from 'effect';
2
2
  const resolveTarget = (target) => (typeof target === 'function' ? target() : target);
3
+ const listen = (config) => Stream.callback(queue => Effect.acquireRelease(Effect.sync(() => {
4
+ const target = resolveTarget(config.target);
5
+ const handleEvent = (event) => {
6
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
7
+ const maybeMessage = config.toMessage(event);
8
+ if (Option.isSome(maybeMessage)) {
9
+ Queue.offerUnsafe(queue, maybeMessage.value);
10
+ }
11
+ };
12
+ target.addEventListener(config.type, handleEvent, config.options);
13
+ return { target, handleEvent };
14
+ }), ({ target, handleEvent }) => Effect.sync(() => {
15
+ target.removeEventListener(config.type, handleEvent, config.options);
16
+ })).pipe(Effect.flatMap(() => Effect.never)));
3
17
  /**
4
18
  * Build a Stream that emits a Message for the dispatches of a DOM event the
5
19
  * mapper chooses to keep, registering the listener when the Stream's scope
@@ -7,7 +21,9 @@ const resolveTarget = (target) => (typeof target === 'function' ? target() : tar
7
21
  *
8
22
  * This is the filtered variant of `fromEvent`. Its `toMessage` returns
9
23
  * `Option.some(message)` to emit and `Option.none()` to ignore the event, so a
10
- * single listener can react to some dispatches while passing on the rest.
24
+ * single listener can react to some dispatches while passing on the rest. A
25
+ * mapper that never emits produces a `Stream<never>`, which composes wherever
26
+ * a Message-producing Stream is expected.
11
27
  *
12
28
  * Reach for this over a downstream `Stream.filterMap` whenever the decision to
13
29
  * keep an event is paired with `event.preventDefault()`. The mapper runs
@@ -19,6 +35,13 @@ const resolveTarget = (target) => (typeof target === 'function' ? target() : tar
19
35
  * `options: { passive: false }` explicitly when cancelling those events, or
20
36
  * reach for `fromEventFilterMapPreventDefault`, which does so for you.
21
37
  *
38
+ * The target, the event name, and the event the mapper receives are one fact:
39
+ * `type` is constrained to the names the target declares, and the mapper's
40
+ * parameter is what those two resolve to, so annotating it narrows nothing and
41
+ * cannot contradict the name. A target that is neither annotated nor one
42
+ * lib.dom declares a map for accepts any name and reports `Event`; annotate it
43
+ * with {@link TypedEventTarget} to resolve its own events.
44
+ *
22
45
  * The listener lifecycle uses `Effect.acquireRelease`. The `addEventListener`
23
46
  * call happens inside the acquire Effect, and the matching
24
47
  * `removeEventListener` is registered only after acquire completes, so the
@@ -39,8 +62,8 @@ const resolveTarget = (target) => (typeof target === 'function' ? target() : tar
39
62
  * modelToDependencies: model => ({ isListening: model.isListening }),
40
63
  * dependenciesToStream: ({ isListening }) =>
41
64
  * Stream.when(
42
- * Subscription.fromEventFilterMap<KeyboardEvent, Message>({
43
- * target: document,
65
+ * Subscription.fromEventFilterMap({
66
+ * target: window,
44
67
  * type: 'keydown',
45
68
  * toMessage: event =>
46
69
  * event.key === 'Escape'
@@ -54,25 +77,19 @@ const resolveTarget = (target) => (typeof target === 'function' ? target() : tar
54
77
  * }))
55
78
  * ```
56
79
  */
57
- export const fromEventFilterMap = (config) => Stream.callback(queue => Effect.acquireRelease(Effect.sync(() => {
58
- const target = resolveTarget(config.target);
59
- const handleEvent = (event) => {
60
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
61
- const maybeMessage = config.toMessage(event);
62
- if (Option.isSome(maybeMessage)) {
63
- Queue.offerUnsafe(queue, maybeMessage.value);
64
- }
65
- };
66
- target.addEventListener(config.type, handleEvent, config.options);
67
- return { target, handleEvent };
68
- }), ({ target, handleEvent }) => Effect.sync(() => {
69
- target.removeEventListener(config.type, handleEvent, config.options);
70
- })).pipe(Effect.flatMap(() => Effect.never)));
80
+ export const fromEventFilterMap = (config) => listen(config);
71
81
  /**
72
82
  * Build a Stream that emits a Message for every dispatch of a DOM event,
73
83
  * registering the listener when the Stream's scope opens and removing it when
74
84
  * the scope closes.
75
85
  *
86
+ * The target, the event name, and the event the mapper receives are one fact:
87
+ * `type` is constrained to the names the target declares, and the mapper's
88
+ * parameter is what those two resolve to, so annotating it narrows nothing and
89
+ * cannot contradict the name. A target that is neither annotated nor one
90
+ * lib.dom declares a map for accepts any name and reports `Event`; annotate it
91
+ * with {@link TypedEventTarget} to resolve its own events.
92
+ *
76
93
  * The listener lifecycle uses `Effect.acquireRelease`. The `addEventListener`
77
94
  * call happens inside the acquire Effect, and the matching
78
95
  * `removeEventListener` is registered only after acquire completes, so the
@@ -98,7 +115,7 @@ export const fromEventFilterMap = (config) => Stream.callback(queue => Effect.ac
98
115
  * modelToDependencies: model => ({ isListening: model.isListening }),
99
116
  * dependenciesToStream: ({ isListening }) =>
100
117
  * Stream.when(
101
- * Subscription.fromEvent<KeyboardEvent, Message>({
118
+ * Subscription.fromEvent({
102
119
  * target: window,
103
120
  * type: 'keydown',
104
121
  * toMessage: event => Message.PressedKey({ key: event.key }),
@@ -110,7 +127,7 @@ export const fromEventFilterMap = (config) => Stream.callback(queue => Effect.ac
110
127
  * }))
111
128
  * ```
112
129
  */
113
- export const fromEvent = (config) => fromEventFilterMap({
130
+ export const fromEvent = (config) => listen({
114
131
  ...config,
115
132
  toMessage: event => Option.some(config.toMessage(event)),
116
133
  });
@@ -130,8 +147,14 @@ export const fromEvent = (config) => fromEventFilterMap({
130
147
  * Because cancelling is the point, the listener registers with
131
148
  * `passive: false` when the config does not say otherwise. This keeps wheel
132
149
  * and touch events cancelable when a browser would otherwise make listeners
133
- * on a global target passive. Passing `passive: true` explicitly contradicts
134
- * the helper's purpose and throws.
150
+ * on a global target passive. The config rejects `passive: true`; the runtime
151
+ * guard also throws for unchecked JavaScript inputs.
152
+ *
153
+ * The target, event name, and mapper parameter are one fact: `type` is
154
+ * constrained to the names the target declares, and the mapper receives the
155
+ * event those two resolve to. A target with no declared event map accepts any
156
+ * name and reports `Event`; annotate it with {@link TypedEventTarget} to
157
+ * resolve its own events.
135
158
  *
136
159
  * The listener lifecycle uses `Effect.acquireRelease`. The `addEventListener`
137
160
  * call happens inside the acquire Effect, and the matching
@@ -153,10 +176,7 @@ export const fromEvent = (config) => fromEventFilterMap({
153
176
  * modelToDependencies: model => ({ isModalOpen: model.isModalOpen }),
154
177
  * dependenciesToStream: ({ isModalOpen }) =>
155
178
  * Stream.when(
156
- * Subscription.fromEventFilterMapPreventDefault<
157
- * WheelEvent,
158
- * Message
159
- * >({
179
+ * Subscription.fromEventFilterMapPreventDefault({
160
180
  * target: window,
161
181
  * type: 'wheel',
162
182
  * toMessage: () => Option.some(Message.SuppressedWheelScroll()),
@@ -169,7 +189,8 @@ export const fromEvent = (config) => fromEventFilterMap({
169
189
  * ```
170
190
  */
171
191
  export const fromEventFilterMapPreventDefault = (config) => {
172
- if (config.options?.passive === true) {
192
+ const options = config.options;
193
+ if (options?.passive === true) {
173
194
  throw new Error(`Foldkit: \`Subscription.fromEventFilterMapPreventDefault\` was passed ` +
174
195
  `\`options: { passive: true }\` for a "${config.type}" listener. ` +
175
196
  `The helper exists to call \`event.preventDefault()\` on every ` +
@@ -182,7 +203,7 @@ export const fromEventFilterMapPreventDefault = (config) => {
182
203
  }
183
204
  return fromEventFilterMap({
184
205
  ...config,
185
- options: { ...config.options, passive: false },
206
+ options: { ...options, passive: false },
186
207
  toMessage: event => {
187
208
  const maybeMessage = config.toMessage(event);
188
209
  if (Option.isSome(maybeMessage)) {