effect-genserver 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/README.md +3 -0
- package/dist/AtomGenServer.d.ts +26 -0
- package/dist/AtomGenServer.d.ts.map +1 -0
- package/dist/AtomGenServer.js +28 -0
- package/dist/AtomGenServer.js.map +1 -0
- package/dist/ClusterGenServer.d.ts +39 -0
- package/dist/ClusterGenServer.d.ts.map +1 -0
- package/dist/ClusterGenServer.js +31 -0
- package/dist/ClusterGenServer.js.map +1 -0
- package/dist/GenServer.d.ts +139 -0
- package/dist/GenServer.d.ts.map +1 -0
- package/dist/GenServer.js +181 -0
- package/dist/GenServer.js.map +1 -0
- package/dist/RpcGenServer.d.ts +29 -0
- package/dist/RpcGenServer.d.ts.map +1 -0
- package/dist/RpcGenServer.js +32 -0
- package/dist/RpcGenServer.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
- package/src/AtomGenServer.ts +112 -0
- package/src/ClusterGenServer.ts +96 -0
- package/src/GenServer.ts +493 -0
- package/src/RpcGenServer.ts +65 -0
- package/src/index.ts +19 -0
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "effect-genserver",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js",
|
|
10
|
+
"./*": "./dist/*.js",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "Tim Smart <hello@timsmart.co>",
|
|
19
|
+
"homepage": "https://github.com/tim-smart/effect-genserver",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/tim-smart/effect-genserver.git"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"effect": "4.0.0-beta.48"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@changesets/changelog-github": "^0.6.0",
|
|
30
|
+
"@changesets/cli": "^2.29.8",
|
|
31
|
+
"@effect/language-service": "^0.85.1",
|
|
32
|
+
"@effect/vitest": "4.0.0-beta.48",
|
|
33
|
+
"@types/node": "^25.6.0",
|
|
34
|
+
"@typescript/native-preview": "7.0.0-dev.20260412.1",
|
|
35
|
+
"effect": "4.0.0-beta.48",
|
|
36
|
+
"husky": "^9.1.7",
|
|
37
|
+
"lint-staged": "^16.4.0",
|
|
38
|
+
"oxlint": "^1.57.0",
|
|
39
|
+
"prettier": "^3.8.2",
|
|
40
|
+
"typescript": "^6.0.2",
|
|
41
|
+
"vitest": "^4.1.1"
|
|
42
|
+
},
|
|
43
|
+
"lint-staged": {
|
|
44
|
+
"*.{ts,tsx}": [
|
|
45
|
+
"oxlint --fix",
|
|
46
|
+
"prettier --write"
|
|
47
|
+
],
|
|
48
|
+
"*.{json,md,yml,yaml}": [
|
|
49
|
+
"prettier --write"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"check": "oxlint -c .oxlintrc.json --fix && pnpm tsc -b tsconfig.json",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"build": "tsc -b tsconfig.json"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type * as Rpc from "effect/unstable/rpc/Rpc"
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
|
+
import type * as Schema from "effect/Schema"
|
|
7
|
+
import * as Layer from "effect/Layer"
|
|
8
|
+
import * as GenServer from "./GenServer.ts"
|
|
9
|
+
import * as Atom from "effect/unstable/reactivity/Atom"
|
|
10
|
+
import { identity, pipe } from "effect/Function"
|
|
11
|
+
import * as Stream from "effect/Stream"
|
|
12
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
|
|
13
|
+
import * as Duration from "effect/Duration"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
* @category Atom
|
|
18
|
+
*/
|
|
19
|
+
export const make = <
|
|
20
|
+
State extends Schema.Top,
|
|
21
|
+
Rpcs extends Rpc.Any,
|
|
22
|
+
E,
|
|
23
|
+
InitialState extends State["Type"] | undefined = undefined,
|
|
24
|
+
>(
|
|
25
|
+
server: GenServer.GenServer<State, Rpcs>,
|
|
26
|
+
layer:
|
|
27
|
+
| Layer.Layer<
|
|
28
|
+
GenServer.ToHandler<Rpcs> | GenServer.InitialState,
|
|
29
|
+
E,
|
|
30
|
+
GenServer.SendDiscard
|
|
31
|
+
>
|
|
32
|
+
| ((
|
|
33
|
+
get: Atom.AtomContext,
|
|
34
|
+
) => Layer.Layer<
|
|
35
|
+
GenServer.ToHandler<Rpcs> | GenServer.InitialState,
|
|
36
|
+
E,
|
|
37
|
+
GenServer.SendDiscard
|
|
38
|
+
>),
|
|
39
|
+
options?: {
|
|
40
|
+
readonly memoMap?: Layer.MemoMap | undefined
|
|
41
|
+
readonly idleTTL?: Duration.Input | undefined
|
|
42
|
+
readonly initialState?: InitialState | undefined
|
|
43
|
+
},
|
|
44
|
+
): {
|
|
45
|
+
readonly actor: Atom.Atom<
|
|
46
|
+
AsyncResult.AsyncResult<GenServer.Actor<State, Rpcs>, E>
|
|
47
|
+
>
|
|
48
|
+
readonly send: <Tag extends Rpcs["_tag"]>(
|
|
49
|
+
tag: Tag,
|
|
50
|
+
options?: {
|
|
51
|
+
readonly concurrent?: boolean | undefined
|
|
52
|
+
},
|
|
53
|
+
) => Atom.AtomResultFn<
|
|
54
|
+
Rpc.PayloadConstructor<Rpc.ExtractTag<Rpcs, Tag>>,
|
|
55
|
+
Rpc.Success<Rpc.ExtractTag<Rpcs, Tag>>,
|
|
56
|
+
E | Schema.Schema.Type<Rpc.ErrorSchema<Rpc.ExtractTag<Rpcs, Tag>>>
|
|
57
|
+
>
|
|
58
|
+
readonly state: Atom.Atom<
|
|
59
|
+
[InitialState] extends [undefined]
|
|
60
|
+
? AsyncResult.AsyncResult<State["Type"], E>
|
|
61
|
+
: State["Type"]
|
|
62
|
+
>
|
|
63
|
+
} => {
|
|
64
|
+
const memoMap = options?.memoMap ?? Atom.runtime.memoMap
|
|
65
|
+
|
|
66
|
+
const actor = Atom.make((get) => {
|
|
67
|
+
const layer_ = typeof layer === "function" ? layer(get) : layer
|
|
68
|
+
return GenServer.makeActor(server, layer_).pipe(
|
|
69
|
+
Effect.provideService(Layer.CurrentMemoMap, memoMap),
|
|
70
|
+
)
|
|
71
|
+
}).pipe(Atom.setIdleTTL(options?.idleTTL ?? Duration.zero))
|
|
72
|
+
|
|
73
|
+
const state = pipe(
|
|
74
|
+
Atom.make((get) =>
|
|
75
|
+
pipe(
|
|
76
|
+
get.result(actor),
|
|
77
|
+
Effect.map((actor) => actor.changes),
|
|
78
|
+
Stream.unwrap,
|
|
79
|
+
),
|
|
80
|
+
),
|
|
81
|
+
options?.initialState
|
|
82
|
+
? Atom.map(AsyncResult.getOrElse(() => options.initialState))
|
|
83
|
+
: identity,
|
|
84
|
+
) as Atom.Atom<AsyncResult.AsyncResult<State["Type"], E>>
|
|
85
|
+
|
|
86
|
+
const sendFamily = <
|
|
87
|
+
Tag extends Rpcs["_tag"],
|
|
88
|
+
Rpc = Rpc.ExtractTag<Rpcs, Tag>,
|
|
89
|
+
>([tag, concurrent]: [Tag, boolean]): Atom.AtomResultFn<
|
|
90
|
+
Rpc.PayloadConstructor<Rpc>,
|
|
91
|
+
Rpc.Success<Rpc>,
|
|
92
|
+
Rpc.Error<Rpc> | E
|
|
93
|
+
> => {
|
|
94
|
+
return Atom.fn(
|
|
95
|
+
(payload, get) =>
|
|
96
|
+
pipe(
|
|
97
|
+
get.result(actor),
|
|
98
|
+
Effect.flatMap((actor) => actor.send(tag, payload)),
|
|
99
|
+
),
|
|
100
|
+
{ concurrent },
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const send = <Tag extends Rpcs["_tag"]>(
|
|
105
|
+
tag: Tag,
|
|
106
|
+
options?: {
|
|
107
|
+
readonly concurrent?: boolean | undefined
|
|
108
|
+
},
|
|
109
|
+
) => sendFamily([tag, options?.concurrent ?? false])
|
|
110
|
+
|
|
111
|
+
return { actor, send, state }
|
|
112
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type * as Rpc from "effect/unstable/rpc/Rpc"
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
|
+
import type * as Schema from "effect/Schema"
|
|
7
|
+
import type * as Layer from "effect/Layer"
|
|
8
|
+
import type * as Scope from "effect/Scope"
|
|
9
|
+
import * as Context from "effect/Context"
|
|
10
|
+
import type * as RpcSchema from "effect/unstable/rpc/RpcSchema"
|
|
11
|
+
import * as Entity from "effect/unstable/cluster/Entity"
|
|
12
|
+
import type * as Envelope from "effect/unstable/cluster/Envelope"
|
|
13
|
+
import * as GenServer from "./GenServer.ts"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
* @category Entity
|
|
18
|
+
*/
|
|
19
|
+
export const entity = <
|
|
20
|
+
const Type extends string,
|
|
21
|
+
State extends Schema.Top,
|
|
22
|
+
Rpcs extends Rpc.Any,
|
|
23
|
+
>(
|
|
24
|
+
type: Type,
|
|
25
|
+
schema: GenServer.GenServer<State, Rpcs>,
|
|
26
|
+
): Entity.Entity<
|
|
27
|
+
Type,
|
|
28
|
+
| Rpcs
|
|
29
|
+
| Rpc.Rpc<
|
|
30
|
+
"GenServerChanges",
|
|
31
|
+
Schema.Void,
|
|
32
|
+
RpcSchema.Stream<State, Schema.Never>
|
|
33
|
+
>
|
|
34
|
+
> =>
|
|
35
|
+
Entity.fromRpcGroup(
|
|
36
|
+
type,
|
|
37
|
+
schema.protocol.add(GenServer.RpcStateChanges(schema)),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @since 1.0.0
|
|
42
|
+
* @category Entity
|
|
43
|
+
*/
|
|
44
|
+
export type EntityHandlersFrom<Rpcs extends Rpc.Any> = {
|
|
45
|
+
readonly [Current in Rpcs as Current["_tag"]]: (
|
|
46
|
+
envelope: Envelope.Request<Current>,
|
|
47
|
+
) => Rpc.WrapperOr<Rpc.ResultFrom<Current, never>>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @since 1.0.0
|
|
52
|
+
* @category Entity
|
|
53
|
+
*/
|
|
54
|
+
export const entityHandlers = Effect.fnUntraced(function* <
|
|
55
|
+
State extends Schema.Top,
|
|
56
|
+
Rpcs extends Rpc.Any,
|
|
57
|
+
E,
|
|
58
|
+
R,
|
|
59
|
+
>(
|
|
60
|
+
schema: GenServer.GenServer<State, Rpcs>,
|
|
61
|
+
layer: Layer.Layer<GenServer.ToHandler<Rpcs> | GenServer.InitialState, E, R>,
|
|
62
|
+
): Effect.fn.Return<
|
|
63
|
+
EntityHandlersFrom<
|
|
64
|
+
| Rpcs
|
|
65
|
+
| Rpc.Rpc<
|
|
66
|
+
"GenServerChanges",
|
|
67
|
+
Schema.Void,
|
|
68
|
+
RpcSchema.Stream<State, Schema.Never>
|
|
69
|
+
>
|
|
70
|
+
>,
|
|
71
|
+
E,
|
|
72
|
+
Exclude<R, GenServer.SendDiscard> | Scope.Scope
|
|
73
|
+
> {
|
|
74
|
+
const { handlers } = yield* GenServer.makeHandlers(schema, layer)
|
|
75
|
+
const entityHandlers: Record<
|
|
76
|
+
string,
|
|
77
|
+
(request: Envelope.Request<any>) => any
|
|
78
|
+
> = {}
|
|
79
|
+
for (const { rpc, handler } of handlers.values()) {
|
|
80
|
+
entityHandlers[rpc._tag] = (request: Envelope.Request<any>) =>
|
|
81
|
+
handler({
|
|
82
|
+
payload: request.payload,
|
|
83
|
+
context: ClusterRequest.context(request),
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
return entityHandlers as any
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @since 1.0.0
|
|
91
|
+
* @category Entity
|
|
92
|
+
*/
|
|
93
|
+
export class ClusterRequest extends Context.Service<
|
|
94
|
+
ClusterRequest,
|
|
95
|
+
Envelope.Request<Rpc.AnyWithProps>
|
|
96
|
+
>()("effect-genserver/ClusterGenServer/ClusterRequest") {}
|