foldkit 0.23.0 → 0.25.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/dist/fieldValidation/index.d.ts +3 -4
- package/dist/fieldValidation/index.d.ts.map +1 -1
- package/dist/fieldValidation/index.js +11 -15
- package/dist/html/index.d.ts +42 -0
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +15 -3
- package/dist/html/lazy.d.ts +12 -0
- package/dist/html/lazy.d.ts.map +1 -0
- package/dist/html/lazy.js +35 -0
- package/dist/html/public.d.ts +1 -0
- package/dist/html/public.d.ts.map +1 -1
- package/dist/html/public.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/managedResource/index.d.ts +38 -0
- package/dist/managedResource/index.d.ts.map +1 -0
- package/dist/managedResource/index.js +20 -0
- package/dist/managedResource/public.d.ts +5 -0
- package/dist/managedResource/public.d.ts.map +1 -0
- package/dist/managedResource/public.js +2 -0
- package/dist/runtime/managedResource.d.ts +114 -0
- package/dist/runtime/managedResource.d.ts.map +1 -0
- package/dist/runtime/managedResource.js +92 -0
- package/dist/runtime/public.d.ts +2 -2
- package/dist/runtime/public.d.ts.map +1 -1
- package/dist/runtime/public.js +1 -1
- package/dist/runtime/runtime.d.ts +79 -90
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +95 -19
- package/dist/runtime/subscription.d.ts +25 -0
- package/dist/runtime/subscription.d.ts.map +1 -0
- package/dist/runtime/subscription.js +7 -0
- package/dist/struct/index.d.ts +2 -0
- package/dist/struct/index.d.ts.map +1 -1
- package/dist/struct/index.js +4 -0
- package/dist/struct/public.d.ts +1 -1
- package/dist/struct/public.d.ts.map +1 -1
- package/dist/struct/public.js +1 -1
- package/dist/subscription/public.d.ts +3 -0
- package/dist/subscription/public.d.ts.map +1 -0
- package/dist/subscription/public.js +1 -0
- package/dist/ui/disclosure/index.d.ts.map +1 -1
- package/dist/ui/disclosure/index.js +3 -2
- package/dist/ui/group.d.ts +8 -0
- package/dist/ui/group.d.ts.map +1 -0
- package/dist/ui/group.js +13 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/keyboard.d.ts +2 -0
- package/dist/ui/keyboard.d.ts.map +1 -1
- package/dist/ui/keyboard.js +2 -0
- package/dist/ui/listbox/multi.d.ts +172 -0
- package/dist/ui/listbox/multi.d.ts.map +1 -0
- package/dist/ui/listbox/multi.js +25 -0
- package/dist/ui/listbox/multiPublic.d.ts +3 -0
- package/dist/ui/listbox/multiPublic.d.ts.map +1 -0
- package/dist/ui/listbox/multiPublic.js +1 -0
- package/dist/ui/listbox/public.d.ts +7 -0
- package/dist/ui/listbox/public.d.ts.map +1 -0
- package/dist/ui/listbox/public.js +3 -0
- package/dist/ui/listbox/shared.d.ts +236 -0
- package/dist/ui/listbox/shared.d.ts.map +1 -0
- package/dist/ui/listbox/shared.js +519 -0
- package/dist/ui/listbox/single.d.ts +172 -0
- package/dist/ui/listbox/single.d.ts.map +1 -0
- package/dist/ui/listbox/single.js +29 -0
- package/dist/ui/menu/index.d.ts +4 -9
- package/dist/ui/menu/index.d.ts.map +1 -1
- package/dist/ui/menu/index.js +9 -29
- package/dist/ui/typeahead.d.ts +4 -0
- package/dist/ui/typeahead.d.ts.map +1 -0
- package/dist/ui/typeahead.js +14 -0
- package/package.json +13 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Record } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* Creates type-safe managed resource configurations from a dependency schema.
|
|
4
|
+
*
|
|
5
|
+
* Use this when a resource is expensive or stateful and should only exist while
|
|
6
|
+
* the model is in a particular state — a camera stream during a video call, a
|
|
7
|
+
* WebSocket connection while on a chat page, or a Web Worker pool during a
|
|
8
|
+
* computation. For resources that live for the entire application lifetime, use
|
|
9
|
+
* the static `resources` config instead.
|
|
10
|
+
*
|
|
11
|
+
* **Lifecycle** — The runtime watches each config's `modelToMaybeRequirements`
|
|
12
|
+
* after every model update, structurally comparing the result against the
|
|
13
|
+
* previous value:
|
|
14
|
+
*
|
|
15
|
+
* - `Option.none()` → `Option.some(params)`: calls `acquire(params)`, then
|
|
16
|
+
* dispatches `onAcquired(value)`.
|
|
17
|
+
* - `Option.some(paramsA)` → `Option.some(paramsB)` (structurally different):
|
|
18
|
+
* releases the old resource, then acquires a new one with `paramsB`.
|
|
19
|
+
* - `Option.some(params)` → `Option.none()`: calls `release(value)`, then
|
|
20
|
+
* dispatches `onReleased()`. No re-acquisition occurs.
|
|
21
|
+
*
|
|
22
|
+
* If `acquire` fails, `onAcquireError` is dispatched and the resource daemon
|
|
23
|
+
* continues watching for the next deps change — a failed acquisition does not
|
|
24
|
+
* crash the application.
|
|
25
|
+
*
|
|
26
|
+
* **Config fields:**
|
|
27
|
+
*
|
|
28
|
+
* - `resource` — The identity tag created with `ManagedResource.tag`. Appears
|
|
29
|
+
* in the Effect R channel so commands that call `.get` are type-checked.
|
|
30
|
+
* - `modelToMaybeRequirements` — Extracts requirements from the model.
|
|
31
|
+
* `Option.none()` means "release", `Option.some(params)` means
|
|
32
|
+
* "acquire/re-acquire if params changed". For resources with no
|
|
33
|
+
* parameters, use `S.Option(S.Null)` and return `Option.some(null)` —
|
|
34
|
+
* not `S.Struct({})`, which has no fields for equivalence comparison.
|
|
35
|
+
* - `acquire` — Creates the resource from the unwrapped params. The returned
|
|
36
|
+
* Effect should fail when acquisition fails — errors in the error channel
|
|
37
|
+
* flow to `onAcquireError` as a message instead of crashing the runtime.
|
|
38
|
+
* - `release` — Tears down the resource. Errors thrown here are silently
|
|
39
|
+
* swallowed — release must not block cleanup.
|
|
40
|
+
* - `onAcquired` — Message dispatched when `acquire` succeeds.
|
|
41
|
+
* - `onAcquireError` — Message dispatched when `acquire` fails.
|
|
42
|
+
* - `onReleased` — Message dispatched after `release` completes.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const CameraStream = ManagedResource.tag<MediaStream>()('CameraStream')
|
|
47
|
+
*
|
|
48
|
+
* const ManagedResourceDeps = S.Struct({
|
|
49
|
+
* camera: S.Option(S.Struct({ facingMode: S.String })),
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* const managedResources = ManagedResource.makeManagedResources(
|
|
53
|
+
* ManagedResourceDeps,
|
|
54
|
+
* )<Model, Message>({
|
|
55
|
+
* camera: {
|
|
56
|
+
* resource: CameraStream,
|
|
57
|
+
* modelToMaybeRequirements: model =>
|
|
58
|
+
* pipe(
|
|
59
|
+
* model.callState,
|
|
60
|
+
* Option.liftPredicate(
|
|
61
|
+
* (callState): callState is typeof InCall.Type =>
|
|
62
|
+
* callState._tag === 'InCall',
|
|
63
|
+
* ),
|
|
64
|
+
* Option.map(callState => ({ facingMode: callState.facingMode })),
|
|
65
|
+
* ),
|
|
66
|
+
* acquire: ({ facingMode }) =>
|
|
67
|
+
* Effect.tryPromise(() =>
|
|
68
|
+
* navigator.mediaDevices.getUserMedia({ video: { facingMode } }),
|
|
69
|
+
* ),
|
|
70
|
+
* release: stream =>
|
|
71
|
+
* Effect.sync(() => stream.getTracks().forEach(track => track.stop())),
|
|
72
|
+
* onAcquired: () => AcquiredCamera(),
|
|
73
|
+
* onAcquireError: error => FailedToAcquireCamera({ error: String(error) }),
|
|
74
|
+
* onReleased: () => ReleasedCamera(),
|
|
75
|
+
* },
|
|
76
|
+
* })
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @param ManagedResourceDeps - An Effect Schema struct where each field's type
|
|
80
|
+
* drives the requirements for one managed resource. Wrap in `S.Option(...)` for
|
|
81
|
+
* resources that can be released (most cases).
|
|
82
|
+
*
|
|
83
|
+
* @see {@link ManagedResource.tag} for creating the resource identity.
|
|
84
|
+
*/
|
|
85
|
+
export const makeManagedResources = (ManagedResourceDeps) => (configs) =>
|
|
86
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
87
|
+
Record.map(configs, (config, key) =>
|
|
88
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
89
|
+
({
|
|
90
|
+
schema: ManagedResourceDeps.fields[key],
|
|
91
|
+
...config,
|
|
92
|
+
}));
|
package/dist/runtime/public.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { makeElement, makeApplication, run
|
|
2
|
-
export type { BrowserConfig,
|
|
1
|
+
export { makeElement, makeApplication, run } from './runtime';
|
|
2
|
+
export type { BrowserConfig, ElementConfigWithFlags, ElementConfigWithoutFlags, ApplicationConfigWithFlags, ApplicationConfigWithoutFlags, ElementInit, ApplicationInit, MakeRuntimeReturn, } from './runtime';
|
|
3
3
|
export { UrlRequest } from './urlRequest';
|
|
4
4
|
export type { Internal, External } from './urlRequest';
|
|
5
5
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/runtime/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/runtime/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAE7D,YAAY,EACV,aAAa,EACb,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,6BAA6B,EAC7B,WAAW,EACX,eAAe,EACf,iBAAiB,GAClB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/runtime/public.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { makeElement, makeApplication, run
|
|
1
|
+
export { makeElement, makeApplication, run } from './runtime';
|
|
2
2
|
export { UrlRequest } from './urlRequest';
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { Context, Effect, Layer, Schema
|
|
1
|
+
import { Context, Effect, Layer, Schema } from 'effect';
|
|
2
2
|
import type { Command } from '../command';
|
|
3
3
|
import { Html } from '../html';
|
|
4
4
|
import { Url } from '../url';
|
|
5
|
+
import type { ManagedResources } from './managedResource';
|
|
6
|
+
import type { Subscriptions } from './subscription';
|
|
5
7
|
import { UrlRequest } from './urlRequest';
|
|
6
8
|
declare const Dispatch_base: Context.TagClass<Dispatch, "@foldkit/Dispatch", {
|
|
7
9
|
readonly dispatchAsync: (message: unknown) => Effect.Effect<void>;
|
|
@@ -12,116 +14,103 @@ export declare class Dispatch extends Dispatch_base {
|
|
|
12
14
|
}
|
|
13
15
|
export type { Command } from '../command';
|
|
14
16
|
/** Configuration for browser URL integration with handlers for URL requests and URL changes. */
|
|
15
|
-
export type BrowserConfig<Message> = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface RuntimeConfig<Model, Message, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Flags, Resources = never> {
|
|
17
|
+
export type BrowserConfig<Message> = Readonly<{
|
|
18
|
+
onUrlRequest: (request: UrlRequest) => Message;
|
|
19
|
+
onUrlChange: (url: Url) => Message;
|
|
20
|
+
}>;
|
|
21
|
+
type BaseElementConfig<Model, Message, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Resources = never, ManagedResourceServices = never> = Readonly<{
|
|
21
22
|
Model: Schema.Schema<Model, any, never>;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
* RTCPeerConnection, CanvasRenderingContext2D) — not for stateless utilities
|
|
35
|
-
* (HttpClient, JSON encoding) which should be provided per-command.
|
|
36
|
-
*
|
|
37
|
-
* The runtime memoizes the layer, ensuring a single shared instance for all
|
|
38
|
-
* commands and subscriptions throughout the application's lifetime.
|
|
39
|
-
*/
|
|
40
|
-
readonly resources?: Layer.Layer<Resources>;
|
|
41
|
-
}
|
|
42
|
-
interface BaseElementConfig<Model, Message, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Resources = never> {
|
|
43
|
-
readonly Model: Schema.Schema<Model, any, never>;
|
|
44
|
-
readonly update: (model: Model, message: Message) => [Model, ReadonlyArray<Command<Message, never, Resources>>];
|
|
45
|
-
readonly view: (model: Model) => Html;
|
|
46
|
-
readonly subscriptions?: Subscriptions<Model, Message, StreamDepsMap, Resources>;
|
|
47
|
-
readonly container: HTMLElement;
|
|
48
|
-
readonly errorView?: (error: Error) => Html;
|
|
49
|
-
readonly resources?: Layer.Layer<Resources>;
|
|
50
|
-
}
|
|
23
|
+
update: (model: Model, message: Message) => [
|
|
24
|
+
Model,
|
|
25
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
26
|
+
];
|
|
27
|
+
view: (model: Model) => Html;
|
|
28
|
+
subscriptions?: Subscriptions<Model, Message, StreamDepsMap, Resources | ManagedResourceServices>;
|
|
29
|
+
container: HTMLElement;
|
|
30
|
+
errorView?: (error: Error) => Html;
|
|
31
|
+
slowViewThresholdMs?: number | false;
|
|
32
|
+
resources?: Layer.Layer<Resources>;
|
|
33
|
+
managedResources?: ManagedResources<Model, Message, ManagedResourceServices>;
|
|
34
|
+
}>;
|
|
51
35
|
/** Configuration for `makeElement` when the element receives initial data via flags. */
|
|
52
|
-
export
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
36
|
+
export type ElementConfigWithFlags<Model, Message, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Flags, Resources = never, ManagedResourceServices = never> = BaseElementConfig<Model, Message, StreamDepsMap, Resources, ManagedResourceServices> & Readonly<{
|
|
37
|
+
Flags: Schema.Schema<Flags, any, never>;
|
|
38
|
+
flags: Effect.Effect<Flags>;
|
|
39
|
+
init: (flags: Flags) => [
|
|
40
|
+
Model,
|
|
41
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
42
|
+
];
|
|
43
|
+
}>;
|
|
57
44
|
/** Configuration for `makeElement` without flags. */
|
|
58
|
-
export
|
|
59
|
-
|
|
45
|
+
export type ElementConfigWithoutFlags<Model, Message, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Resources = never, ManagedResourceServices = never> = BaseElementConfig<Model, Message, StreamDepsMap, Resources, ManagedResourceServices> & Readonly<{
|
|
46
|
+
init: () => [
|
|
60
47
|
Model,
|
|
61
|
-
ReadonlyArray<Command<Message, never, Resources>>
|
|
48
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
62
49
|
];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
50
|
+
}>;
|
|
51
|
+
type BaseApplicationConfig<Model, Message, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Resources = never, ManagedResourceServices = never> = Readonly<{
|
|
52
|
+
Model: Schema.Schema<Model, any, never>;
|
|
53
|
+
update: (model: Model, message: Message) => [
|
|
54
|
+
Model,
|
|
55
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
56
|
+
];
|
|
57
|
+
view: (model: Model) => Html;
|
|
58
|
+
subscriptions?: Subscriptions<Model, Message, StreamDepsMap, Resources | ManagedResourceServices>;
|
|
59
|
+
container: HTMLElement;
|
|
60
|
+
browser: BrowserConfig<Message>;
|
|
61
|
+
errorView?: (error: Error) => Html;
|
|
62
|
+
slowViewThresholdMs?: number | false;
|
|
63
|
+
resources?: Layer.Layer<Resources>;
|
|
64
|
+
managedResources?: ManagedResources<Model, Message, ManagedResourceServices>;
|
|
65
|
+
}>;
|
|
74
66
|
/** Configuration for `makeApplication` when the application receives initial data via flags. */
|
|
75
|
-
export
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
export type ApplicationConfigWithFlags<Model, Message, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Flags, Resources = never, ManagedResourceServices = never> = BaseApplicationConfig<Model, Message, StreamDepsMap, Resources, ManagedResourceServices> & Readonly<{
|
|
68
|
+
Flags: Schema.Schema<Flags, any, never>;
|
|
69
|
+
flags: Effect.Effect<Flags>;
|
|
70
|
+
init: (flags: Flags, url: Url) => [
|
|
71
|
+
Model,
|
|
72
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
73
|
+
];
|
|
74
|
+
}>;
|
|
80
75
|
/** Configuration for `makeApplication` without flags. */
|
|
81
|
-
export
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
export type ApplicationConfigWithoutFlags<Model, Message, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Resources = never, ManagedResourceServices = never> = BaseApplicationConfig<Model, Message, StreamDepsMap, Resources, ManagedResourceServices> & Readonly<{
|
|
77
|
+
init: (url: Url) => [
|
|
78
|
+
Model,
|
|
79
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
80
|
+
];
|
|
81
|
+
}>;
|
|
84
82
|
/** The `init` function type for elements, with an optional `flags` parameter when `Flags` is not `void`. */
|
|
85
|
-
export type ElementInit<Model, Message, Flags = void, Resources = never> = Flags extends void ? () => [
|
|
83
|
+
export type ElementInit<Model, Message, Flags = void, Resources = never, ManagedResourceServices = never> = Flags extends void ? () => [
|
|
84
|
+
Model,
|
|
85
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
86
|
+
] : (flags: Flags) => [
|
|
87
|
+
Model,
|
|
88
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
89
|
+
];
|
|
86
90
|
/** The `init` function type for applications, receives the current URL and optional flags. */
|
|
87
|
-
export type ApplicationInit<Model, Message, Flags = void, Resources = never> = Flags extends void ? (url: Url) => [
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
readonly schema: Schema.Schema<StreamDeps>;
|
|
95
|
-
} & Subscription<Model, Message, StreamDeps, Resources>;
|
|
96
|
-
/** A record of named subscription configurations, keyed by dependency field name. */
|
|
97
|
-
export type Subscriptions<Model, Message, SubscriptionDeps extends Schema.Struct<any>, Resources = never> = {
|
|
98
|
-
readonly [K in keyof Schema.Schema.Type<SubscriptionDeps>]: SubscriptionConfig<Model, Message, Schema.Schema.Type<SubscriptionDeps>[K], Resources>;
|
|
99
|
-
};
|
|
100
|
-
/** Creates type-safe subscription configurations from a dependency schema. */
|
|
101
|
-
export declare const makeSubscriptions: <SubscriptionDeps extends Schema.Struct<any>>(SubscriptionDeps: SubscriptionDeps) => <Model, Message, Resources = never>(configs: { [K in keyof Schema.Schema.Type<SubscriptionDeps>]: {
|
|
102
|
-
modelToDeps: (model: Model) => Schema.Schema.Type<SubscriptionDeps>[K];
|
|
103
|
-
depsToStream: (deps: Schema.Schema.Type<SubscriptionDeps>[K]) => Stream.Stream<Command<Message, never, Resources>>;
|
|
104
|
-
}; }) => Record<string, {
|
|
105
|
-
schema: any;
|
|
106
|
-
modelToDeps: (model: Model) => Schema.Schema.Type<SubscriptionDeps>[K];
|
|
107
|
-
depsToStream: (deps: Schema.Schema.Type<SubscriptionDeps>[K]) => Stream.Stream<Command<Message, never, Resources>>;
|
|
108
|
-
}>;
|
|
91
|
+
export type ApplicationInit<Model, Message, Flags = void, Resources = never, ManagedResourceServices = never> = Flags extends void ? (url: Url) => [
|
|
92
|
+
Model,
|
|
93
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
94
|
+
] : (flags: Flags, url: Url) => [
|
|
95
|
+
Model,
|
|
96
|
+
ReadonlyArray<Command<Message, never, Resources | ManagedResourceServices>>
|
|
97
|
+
];
|
|
109
98
|
/** A configured Foldkit runtime returned by `makeElement` or `makeApplication`, passed to `run` to start the application. */
|
|
110
99
|
export type MakeRuntimeReturn = (hmrModel?: unknown) => Effect.Effect<void>;
|
|
111
100
|
/** Creates a Foldkit element (no URL routing) and returns a runtime that can be passed to `run`. */
|
|
112
101
|
export declare function makeElement<Model, Message extends {
|
|
113
102
|
_tag: string;
|
|
114
|
-
}, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Flags, Resources = never>(config: ElementConfigWithFlags<Model, Message, StreamDepsMap, Flags, Resources>): MakeRuntimeReturn;
|
|
103
|
+
}, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Flags, Resources = never, ManagedResourceServices = never>(config: ElementConfigWithFlags<Model, Message, StreamDepsMap, Flags, Resources, ManagedResourceServices>): MakeRuntimeReturn;
|
|
115
104
|
export declare function makeElement<Model, Message extends {
|
|
116
105
|
_tag: string;
|
|
117
|
-
}, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Resources = never>(config: ElementConfigWithoutFlags<Model, Message, StreamDepsMap, Resources>): MakeRuntimeReturn;
|
|
106
|
+
}, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Resources = never, ManagedResourceServices = never>(config: ElementConfigWithoutFlags<Model, Message, StreamDepsMap, Resources, ManagedResourceServices>): MakeRuntimeReturn;
|
|
118
107
|
/** Creates a Foldkit application with URL routing and returns a runtime that can be passed to `run`. */
|
|
119
108
|
export declare function makeApplication<Model, Message extends {
|
|
120
109
|
_tag: string;
|
|
121
|
-
}, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Flags, Resources = never>(config: ApplicationConfigWithFlags<Model, Message, StreamDepsMap, Flags, Resources>): MakeRuntimeReturn;
|
|
110
|
+
}, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Flags, Resources = never, ManagedResourceServices = never>(config: ApplicationConfigWithFlags<Model, Message, StreamDepsMap, Flags, Resources, ManagedResourceServices>): MakeRuntimeReturn;
|
|
122
111
|
export declare function makeApplication<Model, Message extends {
|
|
123
112
|
_tag: string;
|
|
124
|
-
}, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Resources = never>(config: ApplicationConfigWithoutFlags<Model, Message, StreamDepsMap, Resources>): MakeRuntimeReturn;
|
|
113
|
+
}, StreamDepsMap extends Schema.Struct<Schema.Struct.Fields>, Resources = never, ManagedResourceServices = never>(config: ApplicationConfigWithoutFlags<Model, Message, StreamDepsMap, Resources, ManagedResourceServices>): MakeRuntimeReturn;
|
|
125
114
|
/** Starts a Foldkit runtime, with HMR support for development. */
|
|
126
115
|
export declare const run: (foldkitRuntime: MakeRuntimeReturn) => void;
|
|
127
116
|
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/runtime.js
CHANGED
|
@@ -1,28 +1,45 @@
|
|
|
1
1
|
import { BrowserRuntime } from '@effect/platform-browser/index';
|
|
2
|
-
import { Cause, Context, Effect, Either, Function, Layer, Option, Predicate, Queue, Record, Ref, Runtime, Schema, Stream, SubscriptionRef, pipe, } from 'effect';
|
|
2
|
+
import { Array, Cause, Context, Effect, Either, Function, Layer, Option, Predicate, Queue, Record, Ref, Runtime, Schema, Stream, SubscriptionRef, pipe, } from 'effect';
|
|
3
3
|
import { h } from 'snabbdom';
|
|
4
4
|
import { fromString as urlFromString } from '../url';
|
|
5
5
|
import { patch, toVNode } from '../vdom';
|
|
6
6
|
import { addBfcacheRestoreListener, addNavigationEventListeners, } from './browserListeners';
|
|
7
7
|
import { defaultErrorView, noOpDispatch } from './errorUI';
|
|
8
|
+
const SLOW_VIEW_THRESHOLD_MS = 16;
|
|
8
9
|
/** Effect service tag that provides message dispatching to the view layer. */
|
|
9
10
|
export class Dispatch extends Context.Tag('@foldkit/Dispatch')() {
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
export const makeSubscriptions = (SubscriptionDeps) => (configs) => Record.map(configs, ({ modelToDeps, depsToStream }, key) => ({
|
|
13
|
-
schema: SubscriptionDeps.fields[key],
|
|
14
|
-
modelToDeps,
|
|
15
|
-
depsToStream,
|
|
16
|
-
}));
|
|
17
|
-
const makeRuntime = ({ Model, Flags: _Flags, flags: flags_, init, update, view, subscriptions, container, browser: browserConfig, errorView, resources, }) => (hmrModel) => Effect.scoped(Effect.gen(function* () {
|
|
12
|
+
const makeRuntime = ({ Model, Flags: _Flags, flags: flags_, init, update, view, subscriptions, container, browser: browserConfig, errorView, slowViewThresholdMs = SLOW_VIEW_THRESHOLD_MS, resources, managedResources, }) => (hmrModel) => Effect.scoped(Effect.gen(function* () {
|
|
18
13
|
const maybeResourceLayer = resources
|
|
19
14
|
? Option.some(yield* Layer.memoize(resources))
|
|
20
15
|
: Option.none();
|
|
21
|
-
const
|
|
16
|
+
const managedResourceEntries = managedResources
|
|
17
|
+
? /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
18
|
+
Record.toEntries(managedResources)
|
|
19
|
+
: [];
|
|
20
|
+
const managedResourceRefs = yield* Effect.forEach(managedResourceEntries, ([_key, config]) => Ref.make(Option.none()).pipe(Effect.map(ref => ({ config, ref }))));
|
|
21
|
+
const mergeResourceIntoLayer = (layer, { config, ref }) => Layer.merge(layer, Layer.succeed(
|
|
22
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
23
|
+
config.resource._tag, ref));
|
|
24
|
+
const maybeManagedResourceLayer = Array.match(managedResourceRefs, {
|
|
25
|
+
onEmpty: () => Option.none(),
|
|
26
|
+
onNonEmpty: refs => Option.some(Array.reduce(refs,
|
|
22
27
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
23
|
-
|
|
24
|
-
onSome: resourceLayer => Effect.provide(command, resourceLayer),
|
|
28
|
+
Layer.empty, mergeResourceIntoLayer)),
|
|
25
29
|
});
|
|
30
|
+
const provideAllResources = (effect) => {
|
|
31
|
+
const withResources = Option.match(maybeResourceLayer, {
|
|
32
|
+
onNone: () => effect,
|
|
33
|
+
onSome: resourceLayer => Effect.provide(effect, resourceLayer),
|
|
34
|
+
});
|
|
35
|
+
return Option.match(maybeManagedResourceLayer, {
|
|
36
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
37
|
+
onNone: () => withResources,
|
|
38
|
+
onSome: managedLayer =>
|
|
39
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
40
|
+
Effect.provide(withResources, managedLayer),
|
|
41
|
+
});
|
|
42
|
+
};
|
|
26
43
|
const flags = yield* flags_;
|
|
27
44
|
const modelEquivalence = Schema.equivalence(Model);
|
|
28
45
|
const messageQueue = yield* Queue.unbounded();
|
|
@@ -35,7 +52,7 @@ const makeRuntime = ({ Model, Flags: _Flags, flags: flags_, init, update, view,
|
|
|
35
52
|
}))
|
|
36
53
|
: init(flags, Option.getOrUndefined(currentUrl));
|
|
37
54
|
const modelSubscriptionRef = yield* SubscriptionRef.make(initModel);
|
|
38
|
-
yield* Effect.forEach(initCommands, command => Effect.forkDaemon(command.pipe(
|
|
55
|
+
yield* Effect.forEach(initCommands, command => Effect.forkDaemon(command.pipe(provideAllResources, Effect.flatMap(enqueueMessage))));
|
|
39
56
|
if (browserConfig) {
|
|
40
57
|
addNavigationEventListeners(messageQueue, browserConfig);
|
|
41
58
|
}
|
|
@@ -51,7 +68,7 @@ const makeRuntime = ({ Model, Flags: _Flags, flags: flags_, init, update, view,
|
|
|
51
68
|
yield* SubscriptionRef.set(modelSubscriptionRef, nextModel);
|
|
52
69
|
preserveModel(nextModel);
|
|
53
70
|
}
|
|
54
|
-
yield* Effect.forEach(commands, command => Effect.forkDaemon(command.pipe(
|
|
71
|
+
yield* Effect.forEach(commands, command => Effect.forkDaemon(command.pipe(provideAllResources, Effect.flatMap(enqueueMessage))));
|
|
55
72
|
});
|
|
56
73
|
const runProcessMessage = (messageEffect) => (runtime) => {
|
|
57
74
|
try {
|
|
@@ -79,11 +96,19 @@ const makeRuntime = ({ Model, Flags: _Flags, flags: flags_, init, update, view,
|
|
|
79
96
|
const dispatchAsync = (message) =>
|
|
80
97
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
81
98
|
enqueueMessage(message);
|
|
82
|
-
const render = (model) =>
|
|
99
|
+
const render = (model) => Effect.gen(function* () {
|
|
100
|
+
const viewStart = performance.now();
|
|
101
|
+
const nextVNodeNullish = yield* view(model);
|
|
102
|
+
const viewDuration = performance.now() - viewStart;
|
|
103
|
+
if (import.meta.hot &&
|
|
104
|
+
slowViewThresholdMs !== false &&
|
|
105
|
+
viewDuration > slowViewThresholdMs) {
|
|
106
|
+
console.warn(`[foldkit] Slow view: ${viewDuration.toFixed(1)}ms (budget: ${slowViewThresholdMs}ms). Consider moving computation to update or memoizing with createLazy.`);
|
|
107
|
+
}
|
|
83
108
|
const maybeCurrentVNode = yield* Ref.get(maybeCurrentVNodeRef);
|
|
84
109
|
const patchedVNode = yield* Effect.sync(() => patchVNode(maybeCurrentVNode, nextVNodeNullish, container));
|
|
85
110
|
yield* Ref.set(maybeCurrentVNodeRef, Option.some(patchedVNode));
|
|
86
|
-
})
|
|
111
|
+
}).pipe(Effect.provideService(Dispatch, {
|
|
87
112
|
dispatchAsync,
|
|
88
113
|
dispatchSync,
|
|
89
114
|
}));
|
|
@@ -92,14 +117,45 @@ const makeRuntime = ({ Model, Flags: _Flags, flags: flags_, init, update, view,
|
|
|
92
117
|
yield* render(initModel);
|
|
93
118
|
addBfcacheRestoreListener();
|
|
94
119
|
if (subscriptions) {
|
|
95
|
-
yield* pipe(subscriptions, Record.toEntries, Effect.forEach(([_key, { schema,
|
|
120
|
+
yield* pipe(subscriptions, Record.toEntries, Effect.forEach(([_key, { schema, modelToDependencies, depsToStream }]) => {
|
|
96
121
|
const modelStream = Stream.concat(Stream.make(initModel), modelSubscriptionRef.changes);
|
|
97
|
-
return Effect.forkDaemon(modelStream.pipe(Stream.map(
|
|
122
|
+
return Effect.forkDaemon(modelStream.pipe(Stream.map(modelToDependencies), Stream.changesWith(Schema.equivalence(schema)), Stream.flatMap(depsToStream, { switch: true }), Stream.runForEach(command => command.pipe(Effect.flatMap(enqueueMessage))), provideAllResources));
|
|
98
123
|
}, {
|
|
99
124
|
concurrency: 'unbounded',
|
|
100
125
|
discard: true,
|
|
101
126
|
}));
|
|
102
127
|
}
|
|
128
|
+
const maybeRequirementsToLifecycle = (config, resourceRef) => (maybeRequirements) => {
|
|
129
|
+
if (Option.isOption(maybeRequirements) &&
|
|
130
|
+
Option.isNone(maybeRequirements)) {
|
|
131
|
+
return Stream.empty;
|
|
132
|
+
}
|
|
133
|
+
const requirements = Option.isOption(maybeRequirements)
|
|
134
|
+
? Option.getOrThrow(maybeRequirements)
|
|
135
|
+
: maybeRequirements;
|
|
136
|
+
const acquire = Effect.gen(function* () {
|
|
137
|
+
const value = yield* config.acquire(requirements);
|
|
138
|
+
yield* Ref.set(resourceRef, Option.some(value));
|
|
139
|
+
return value;
|
|
140
|
+
});
|
|
141
|
+
const release = (value) => Effect.gen(function* () {
|
|
142
|
+
yield* config.release(value);
|
|
143
|
+
yield* Ref.set(resourceRef, Option.none());
|
|
144
|
+
yield* enqueueMessage(config.onReleased());
|
|
145
|
+
}).pipe(Effect.catchAllCause(() => Effect.void));
|
|
146
|
+
return pipe(Stream.scoped(Effect.acquireRelease(acquire, release)), Stream.flatMap(value => Stream.concat(Stream.make(config.onAcquired(value)), Stream.never)), Stream.map(Effect.succeed), Stream.catchAll(error => Stream.make(Effect.succeed(config.onAcquireError(error)))));
|
|
147
|
+
};
|
|
148
|
+
const forkManagedResourceLifecycle = ({ config, ref: resourceRef, }) => Effect.gen(function* () {
|
|
149
|
+
const modelStream = Stream.concat(Stream.make(initModel), modelSubscriptionRef.changes);
|
|
150
|
+
const equivalence = Schema.equivalence(config.schema);
|
|
151
|
+
yield* Effect.forkDaemon(modelStream.pipe(Stream.map(config.modelToMaybeRequirements), Stream.changesWith(equivalence), Stream.flatMap(maybeRequirementsToLifecycle(config, resourceRef), {
|
|
152
|
+
switch: true,
|
|
153
|
+
}), Stream.runForEach(Effect.flatMap(enqueueMessage))));
|
|
154
|
+
});
|
|
155
|
+
yield* Effect.forEach(managedResourceRefs, forkManagedResourceLifecycle, {
|
|
156
|
+
concurrency: 'unbounded',
|
|
157
|
+
discard: true,
|
|
158
|
+
});
|
|
103
159
|
yield* pipe(Effect.forever(Effect.gen(function* () {
|
|
104
160
|
const message = yield* Queue.take(messageQueue);
|
|
105
161
|
yield* processMessage(message);
|
|
@@ -146,17 +202,27 @@ export function makeElement(config) {
|
|
|
146
202
|
...(config.subscriptions && { subscriptions: config.subscriptions }),
|
|
147
203
|
container: config.container,
|
|
148
204
|
...(config.errorView && { errorView: config.errorView }),
|
|
205
|
+
...(Predicate.isNotUndefined(config.slowViewThresholdMs) && {
|
|
206
|
+
slowViewThresholdMs: config.slowViewThresholdMs,
|
|
207
|
+
}),
|
|
149
208
|
...(config.resources && { resources: config.resources }),
|
|
209
|
+
...(config.managedResources && {
|
|
210
|
+
managedResources: config.managedResources,
|
|
211
|
+
}),
|
|
150
212
|
};
|
|
151
213
|
if ('Flags' in config) {
|
|
214
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
152
215
|
return makeRuntime({
|
|
153
216
|
...baseConfig,
|
|
154
217
|
Flags: config.Flags,
|
|
155
218
|
flags: config.flags,
|
|
156
|
-
init: flags =>
|
|
219
|
+
init: (flags) =>
|
|
220
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
221
|
+
config.init(flags),
|
|
157
222
|
});
|
|
158
223
|
}
|
|
159
224
|
else {
|
|
225
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
160
226
|
return makeRuntime({
|
|
161
227
|
...baseConfig,
|
|
162
228
|
Flags: Schema.Void,
|
|
@@ -175,17 +241,27 @@ export function makeApplication(config) {
|
|
|
175
241
|
container: config.container,
|
|
176
242
|
browser: config.browser,
|
|
177
243
|
...(config.errorView && { errorView: config.errorView }),
|
|
244
|
+
...(Predicate.isNotUndefined(config.slowViewThresholdMs) && {
|
|
245
|
+
slowViewThresholdMs: config.slowViewThresholdMs,
|
|
246
|
+
}),
|
|
178
247
|
...(config.resources && { resources: config.resources }),
|
|
248
|
+
...(config.managedResources && {
|
|
249
|
+
managedResources: config.managedResources,
|
|
250
|
+
}),
|
|
179
251
|
};
|
|
180
252
|
if ('Flags' in config) {
|
|
253
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
181
254
|
return makeRuntime({
|
|
182
255
|
...baseConfig,
|
|
183
256
|
Flags: config.Flags,
|
|
184
257
|
flags: config.flags,
|
|
185
|
-
init: (flags, url) =>
|
|
258
|
+
init: (flags, url) =>
|
|
259
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
260
|
+
config.init(flags, url ?? currentUrl),
|
|
186
261
|
});
|
|
187
262
|
}
|
|
188
263
|
else {
|
|
264
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
189
265
|
return makeRuntime({
|
|
190
266
|
...baseConfig,
|
|
191
267
|
Flags: Schema.Void,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Schema, type Stream } from 'effect';
|
|
2
|
+
import type { Command } from '../command';
|
|
3
|
+
/** A reactive binding between model state and a long-running stream of commands. */
|
|
4
|
+
export type Subscription<Model, Message, StreamDeps, Resources = never> = {
|
|
5
|
+
readonly modelToDependencies: (model: Model) => StreamDeps;
|
|
6
|
+
readonly depsToStream: (deps: StreamDeps) => Stream.Stream<Command<Message, never, Resources>, never, Resources>;
|
|
7
|
+
};
|
|
8
|
+
type SubscriptionConfig<Model, Message, StreamDeps, Resources = never> = {
|
|
9
|
+
readonly schema: Schema.Schema<StreamDeps>;
|
|
10
|
+
} & Subscription<Model, Message, StreamDeps, Resources>;
|
|
11
|
+
/** A record of named subscription configurations, keyed by dependency field name. */
|
|
12
|
+
export type Subscriptions<Model, Message, SubscriptionDeps extends Schema.Struct<any>, Resources = never> = {
|
|
13
|
+
readonly [K in keyof Schema.Schema.Type<SubscriptionDeps>]: SubscriptionConfig<Model, Message, Schema.Schema.Type<SubscriptionDeps>[K], Resources>;
|
|
14
|
+
};
|
|
15
|
+
/** Creates type-safe subscription configurations from a dependency schema. */
|
|
16
|
+
export declare const makeSubscriptions: <SubscriptionDeps extends Schema.Struct<any>>(SubscriptionDeps: SubscriptionDeps) => <Model, Message, Resources = never>(configs: { [K in keyof Schema.Schema.Type<SubscriptionDeps>]: {
|
|
17
|
+
modelToDependencies: (model: Model) => Schema.Schema.Type<SubscriptionDeps>[K];
|
|
18
|
+
depsToStream: (deps: Schema.Schema.Type<SubscriptionDeps>[K]) => Stream.Stream<Command<Message, never, Resources>, never, Resources>;
|
|
19
|
+
}; }) => Record<string, {
|
|
20
|
+
schema: any;
|
|
21
|
+
modelToDependencies: (model: Model) => Schema.Schema.Type<SubscriptionDeps>[K];
|
|
22
|
+
depsToStream: (deps: Schema.Schema.Type<SubscriptionDeps>[K]) => Stream.Stream<Command<Message, never, Resources>, never, Resources>;
|
|
23
|
+
}>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=subscription.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/runtime/subscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEzC,oFAAoF;AACpF,MAAM,MAAM,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,GAAG,KAAK,IAAI;IACxE,QAAQ,CAAC,mBAAmB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,UAAU,CAAA;IAC1D,QAAQ,CAAC,YAAY,EAAE,CACrB,IAAI,EAAE,UAAU,KACb,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;CACzE,CAAA;AAED,KAAK,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,GAAG,KAAK,IAAI;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;CAC3C,GAAG,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;AAEvD,qFAAqF;AACrF,MAAM,MAAM,aAAa,CACvB,KAAK,EACL,OAAO,EACP,gBAAgB,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAC3C,SAAS,GAAG,KAAK,IACf;IACF,QAAQ,EAAE,CAAC,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,kBAAkB,CAC5E,KAAK,EACL,OAAO,EACP,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EACvC,SAAS,CACV;CACF,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,iBAAiB,GAC3B,gBAAgB,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAC1C,kBAAkB,gBAAgB,MAEnC,KAAK,EAAE,OAAO,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,GAC1C,CAAC,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG;IACjD,mBAAmB,EAAE,CACnB,KAAK,EAAE,KAAK,KACT,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5C,YAAY,EAAE,CACZ,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAC1C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;CACzE,GACF;;iCANY,KAAK,KACT,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;yBAEpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAC1C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC;EAOvE,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Record } from 'effect';
|
|
2
|
+
/** Creates type-safe subscription configurations from a dependency schema. */
|
|
3
|
+
export const makeSubscriptions = (SubscriptionDeps) => (configs) => Record.map(configs, ({ modelToDependencies, depsToStream }, key) => ({
|
|
4
|
+
schema: SubscriptionDeps.fields[key],
|
|
5
|
+
modelToDependencies,
|
|
6
|
+
depsToStream,
|
|
7
|
+
}));
|
package/dist/struct/index.d.ts
CHANGED
|
@@ -12,5 +12,7 @@ export declare const evo: {
|
|
|
12
12
|
<O, const T extends EvolveTransform<O>>(t: StrictKeys<O, T>): (obj: O) => Evolved<O, T>;
|
|
13
13
|
<O, const T extends EvolveTransform<O>>(obj: O, t: StrictKeys<O, T>): Evolved<O, T>;
|
|
14
14
|
};
|
|
15
|
+
/** Creates a variant of `evo` whose transforms are checked against a supertype. Useful in generic contexts where `evo`'s `StrictKeys` can't resolve `keyof` on an open type parameter. The returned function evolves a subtype model, preserving all fields not in the transform, and returns the subtype. */
|
|
16
|
+
export declare const makeConstrainedEvo: <Constraint extends Record<string, unknown>>() => <Model extends Constraint>(model: Model, transforms: EvolveTransform<Constraint>) => Model;
|
|
15
17
|
export {};
|
|
16
18
|
//# sourceMappingURL=index.d.ts.map
|