rbxts-trpc 1.0.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 +15 -0
- package/README.md +56 -0
- package/index.d.ts +1 -0
- package/out/client/createClient.d.ts +11 -0
- package/out/client/createClient.luau +135 -0
- package/out/core/errors.d.ts +15 -0
- package/out/core/errors.luau +69 -0
- package/out/core/event.d.ts +23 -0
- package/out/core/event.luau +154 -0
- package/out/core/initTRPC.d.ts +28 -0
- package/out/core/initTRPC.luau +48 -0
- package/out/core/procedure.d.ts +18 -0
- package/out/core/procedure.luau +115 -0
- package/out/core/router.d.ts +2 -0
- package/out/core/router.luau +26 -0
- package/out/core/types.d.ts +149 -0
- package/out/core/types.luau +2 -0
- package/out/index.d.ts +6 -0
- package/out/infer.d.ts +23 -0
- package/out/infer.luau +2 -0
- package/out/init.luau +10 -0
- package/out/react/hooks.d.ts +25 -0
- package/out/react/hooks.luau +134 -0
- package/out/react/index.d.ts +1 -0
- package/out/react/init.luau +9 -0
- package/out/runtime/execute.d.ts +36 -0
- package/out/runtime/execute.luau +199 -0
- package/out/runtime/schema.d.ts +3 -0
- package/out/runtime/schema.luau +38 -0
- package/out/server/createServer.d.ts +10 -0
- package/out/server/createServer.luau +181 -0
- package/package.json +78 -0
- package/react/index.d.ts +1 -0
- package/react/init.luau +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mergemat
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# rbxts-trpc
|
|
2
|
+
|
|
3
|
+
Type-safe tRPC-style procedures and events for Roblox TS, built on top of `@rbxts/remo`.
|
|
4
|
+
|
|
5
|
+
## Docs
|
|
6
|
+
|
|
7
|
+
- Live docs: https://rbxts-trpc.vercel.app
|
|
8
|
+
- Source docs app: `docs/`
|
|
9
|
+
|
|
10
|
+
Everything else (guides, API reference, React usage, events/procedures, and LLM endpoints) lives in the docs site.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
bun add rbxts-trpc @rbxts/remo @rbxts/t
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
If you use React helpers, also install `@rbxts/react`.
|
|
19
|
+
|
|
20
|
+
## Quick Example
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { t as v } from "@rbxts/t";
|
|
24
|
+
import { createClient, createServer, initTRPC } from "rbxts-trpc";
|
|
25
|
+
|
|
26
|
+
const trpc = initTRPC()
|
|
27
|
+
.context<{ player?: Player }>()
|
|
28
|
+
.create({
|
|
29
|
+
createContext: ({ player }) => ({ player }),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const appRouter = trpc.router({
|
|
33
|
+
todos: trpc.router({
|
|
34
|
+
list: trpc.procedure
|
|
35
|
+
.intent("query")
|
|
36
|
+
.output<string[]>(v.array(v.string))
|
|
37
|
+
.resolve(() => ["Milk", "Eggs"]),
|
|
38
|
+
add: trpc.procedure.input<string>(v.string).resolve(({ input }) => input.size() > 0),
|
|
39
|
+
changed: trpc.event.client.input<string[]>(v.array(v.string)).create(),
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const server = createServer({ t: trpc, router: appRouter });
|
|
44
|
+
server.events.todos.changed.emitAll(["Milk", "Eggs", "Bread"]);
|
|
45
|
+
|
|
46
|
+
const client = createClient({ t: trpc, router: appRouter });
|
|
47
|
+
client.todos.list.query(undefined).then((todos) => print(todos.size()));
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Develop
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
bun run build
|
|
54
|
+
bun run lint
|
|
55
|
+
bun run docs:dev
|
|
56
|
+
```
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./out/index";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TRPCFactory } from "../core/initTRPC";
|
|
2
|
+
import type { ClientEvent, ClientEventProxy, Procedure, ProcedureCallProxy, Router, RouterShape, ServerEvent, ServerEventProxy } from "../core/types";
|
|
3
|
+
type ClientShapeFromRouter<TRouter extends Router<any, RouterShape<any>>> = ClientShapeFromNodes<TRouter["_def"]["shape"]>;
|
|
4
|
+
type ClientShapeFromNodes<TShape> = {
|
|
5
|
+
[K in keyof TShape]: TShape[K] extends Router<any, any> ? ClientShapeFromNodes<TShape[K]["_def"]["shape"]> : TShape[K] extends Procedure<any, infer TInput, infer TOutput, infer TIntent> ? ProcedureCallProxy<TInput, TOutput, TIntent> : TShape[K] extends ServerEvent<any, infer TInput> ? ServerEventProxy<TInput> : TShape[K] extends ClientEvent<any, infer TInput> ? ClientEventProxy<TInput> : never;
|
|
6
|
+
};
|
|
7
|
+
export declare function createClient<TContext, TRouter extends Router<TContext, RouterShape<TContext>>>(options: {
|
|
8
|
+
t: TRPCFactory<TContext>;
|
|
9
|
+
router: TRouter;
|
|
10
|
+
}): ClientShapeFromRouter<TRouter>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local createRemotes = TS.import(script, TS.getModule(script, "@rbxts", "remo").src).createRemotes
|
|
4
|
+
local _errors = TS.import(script, script.Parent.Parent, "core", "errors")
|
|
5
|
+
local TRPCClientError = _errors.TRPCClientError
|
|
6
|
+
local toErrorShape = _errors.toErrorShape
|
|
7
|
+
local _execute = TS.import(script, script.Parent.Parent, "runtime", "execute")
|
|
8
|
+
local formatError = _execute.formatError
|
|
9
|
+
local runClientEvent = _execute.runClientEvent
|
|
10
|
+
local _schema = TS.import(script, script.Parent.Parent, "runtime", "schema")
|
|
11
|
+
local buildRemoSchema = _schema.buildRemoSchema
|
|
12
|
+
local joinPath = _schema.joinPath
|
|
13
|
+
local function isRouterNode(node)
|
|
14
|
+
return node._def.kind == "router"
|
|
15
|
+
end
|
|
16
|
+
local function isProcedureNode(node)
|
|
17
|
+
return node._def.kind == "procedure"
|
|
18
|
+
end
|
|
19
|
+
local function isServerEventNode(node)
|
|
20
|
+
return node._def.kind == "event" and node._def.direction == "server"
|
|
21
|
+
end
|
|
22
|
+
local function isClientEventNode(node)
|
|
23
|
+
return node._def.kind == "event" and node._def.direction == "client"
|
|
24
|
+
end
|
|
25
|
+
local function createClient(options)
|
|
26
|
+
local schema = buildRemoSchema(options.router)
|
|
27
|
+
local remotes = createRemotes(schema)
|
|
28
|
+
local listenersByPath = {}
|
|
29
|
+
local addListener = function(path, listener)
|
|
30
|
+
local listeners = listenersByPath[path] or {}
|
|
31
|
+
local _listener = listener
|
|
32
|
+
table.insert(listeners, _listener)
|
|
33
|
+
listenersByPath[path] = listeners
|
|
34
|
+
return function()
|
|
35
|
+
local current = listenersByPath[path]
|
|
36
|
+
if not current then
|
|
37
|
+
return nil
|
|
38
|
+
end
|
|
39
|
+
local _listener_1 = listener
|
|
40
|
+
local index = (table.find(current, _listener_1) or 0) - 1
|
|
41
|
+
if index >= 0 then
|
|
42
|
+
table.remove(current, index + 1)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
local walk
|
|
47
|
+
walk = function(shape, remoteShape, target, parent)
|
|
48
|
+
for key, rawNode in pairs(shape) do
|
|
49
|
+
local keyName = key
|
|
50
|
+
local node = rawNode
|
|
51
|
+
local path = joinPath(parent, keyName)
|
|
52
|
+
local remoteNode = remoteShape[keyName]
|
|
53
|
+
if isRouterNode(node) then
|
|
54
|
+
local nested = {}
|
|
55
|
+
target[keyName] = nested
|
|
56
|
+
walk(node._def.shape, remoteNode, nested, path)
|
|
57
|
+
continue
|
|
58
|
+
end
|
|
59
|
+
if isProcedureNode(node) then
|
|
60
|
+
local execute = TS.async(function(input)
|
|
61
|
+
local requestRemote = remoteNode
|
|
62
|
+
local result
|
|
63
|
+
TS.try(function()
|
|
64
|
+
result = TS.await(requestRemote.request(requestRemote, input))
|
|
65
|
+
end, function(caught)
|
|
66
|
+
local shape = formatError(options.t._config, caught)
|
|
67
|
+
error(TRPCClientError.new(shape))
|
|
68
|
+
end)
|
|
69
|
+
if not result.ok then
|
|
70
|
+
error(TRPCClientError.new(result.error))
|
|
71
|
+
end
|
|
72
|
+
return result.data
|
|
73
|
+
end)
|
|
74
|
+
local _object = {
|
|
75
|
+
__kind = "procedure",
|
|
76
|
+
__path = path,
|
|
77
|
+
__intent = node._def.intent,
|
|
78
|
+
}
|
|
79
|
+
for _k, _v in (if node._def.intent == "query" then {
|
|
80
|
+
query = execute,
|
|
81
|
+
} else {
|
|
82
|
+
mutate = execute,
|
|
83
|
+
}) do
|
|
84
|
+
_object[_k] = _v
|
|
85
|
+
end
|
|
86
|
+
target[keyName] = _object
|
|
87
|
+
continue
|
|
88
|
+
end
|
|
89
|
+
if isServerEventNode(node) then
|
|
90
|
+
target[keyName] = {
|
|
91
|
+
__kind = "serverEvent",
|
|
92
|
+
__path = path,
|
|
93
|
+
emit = function(input)
|
|
94
|
+
local fireRemote = remoteNode
|
|
95
|
+
fireRemote.fire(fireRemote, input)
|
|
96
|
+
end,
|
|
97
|
+
}
|
|
98
|
+
continue
|
|
99
|
+
end
|
|
100
|
+
if isClientEventNode(node) then
|
|
101
|
+
local connectRemote = remoteNode
|
|
102
|
+
connectRemote.connect(connectRemote, function(input)
|
|
103
|
+
task.spawn(TS.async(function()
|
|
104
|
+
local listeners = listenersByPath[path] or {}
|
|
105
|
+
TS.try(function()
|
|
106
|
+
TS.await(runClientEvent({
|
|
107
|
+
config = options.t._config,
|
|
108
|
+
path = path,
|
|
109
|
+
input = input,
|
|
110
|
+
inputValidator = node._def.inputValidator,
|
|
111
|
+
middlewares = node._def.middlewares,
|
|
112
|
+
listeners = listeners,
|
|
113
|
+
}))
|
|
114
|
+
end, function(caught)
|
|
115
|
+
warn(`[rbxts-trpc] client event error on '{path}':`, toErrorShape(caught))
|
|
116
|
+
end)
|
|
117
|
+
end))
|
|
118
|
+
end)
|
|
119
|
+
target[keyName] = {
|
|
120
|
+
__kind = "clientEvent",
|
|
121
|
+
__path = path,
|
|
122
|
+
on = function(listener)
|
|
123
|
+
return addListener(path, listener)
|
|
124
|
+
end,
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
local client = {}
|
|
130
|
+
walk(options.router._def.shape, remotes, client)
|
|
131
|
+
return client
|
|
132
|
+
end
|
|
133
|
+
return {
|
|
134
|
+
createClient = createClient,
|
|
135
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ErrorShape, TRPCErrorCode } from "./types";
|
|
2
|
+
export declare class TRPCError {
|
|
3
|
+
readonly name = "TRPCError";
|
|
4
|
+
readonly code: TRPCErrorCode;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
readonly data?: unknown;
|
|
7
|
+
constructor(code: TRPCErrorCode, message: string, data?: unknown);
|
|
8
|
+
}
|
|
9
|
+
export declare class TRPCClientError {
|
|
10
|
+
readonly name = "TRPCClientError";
|
|
11
|
+
readonly message: string;
|
|
12
|
+
readonly shape: ErrorShape;
|
|
13
|
+
constructor(shape: ErrorShape);
|
|
14
|
+
}
|
|
15
|
+
export declare function toErrorShape(caught: unknown): ErrorShape;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TRPCError
|
|
3
|
+
do
|
|
4
|
+
TRPCError = setmetatable({}, {
|
|
5
|
+
__tostring = function()
|
|
6
|
+
return "TRPCError"
|
|
7
|
+
end,
|
|
8
|
+
})
|
|
9
|
+
TRPCError.__index = TRPCError
|
|
10
|
+
function TRPCError.new(...)
|
|
11
|
+
local self = setmetatable({}, TRPCError)
|
|
12
|
+
return self:constructor(...) or self
|
|
13
|
+
end
|
|
14
|
+
function TRPCError:constructor(code, message, data)
|
|
15
|
+
self.name = "TRPCError"
|
|
16
|
+
self.code = code
|
|
17
|
+
self.message = message
|
|
18
|
+
self.data = data
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
local TRPCClientError
|
|
22
|
+
do
|
|
23
|
+
TRPCClientError = setmetatable({}, {
|
|
24
|
+
__tostring = function()
|
|
25
|
+
return "TRPCClientError"
|
|
26
|
+
end,
|
|
27
|
+
})
|
|
28
|
+
TRPCClientError.__index = TRPCClientError
|
|
29
|
+
function TRPCClientError.new(...)
|
|
30
|
+
local self = setmetatable({}, TRPCClientError)
|
|
31
|
+
return self:constructor(...) or self
|
|
32
|
+
end
|
|
33
|
+
function TRPCClientError:constructor(shape)
|
|
34
|
+
self.name = "TRPCClientError"
|
|
35
|
+
self.shape = shape
|
|
36
|
+
self.message = shape.message
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
local function toErrorShape(caught)
|
|
40
|
+
local _caught = caught
|
|
41
|
+
if type(_caught) == "table" then
|
|
42
|
+
local maybeError = caught
|
|
43
|
+
local _code = maybeError.code
|
|
44
|
+
local _condition = type(_code) == "string"
|
|
45
|
+
if _condition then
|
|
46
|
+
local _message = maybeError.message
|
|
47
|
+
_condition = type(_message) == "string"
|
|
48
|
+
end
|
|
49
|
+
if _condition then
|
|
50
|
+
return {
|
|
51
|
+
code = maybeError.code,
|
|
52
|
+
message = maybeError.message,
|
|
53
|
+
data = maybeError.data,
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
local _object = {
|
|
58
|
+
code = "INTERNAL_SERVER_ERROR",
|
|
59
|
+
}
|
|
60
|
+
local _left = "message"
|
|
61
|
+
local _caught_1 = caught
|
|
62
|
+
_object[_left] = if type(_caught_1) == "string" then caught else "Internal server error"
|
|
63
|
+
return _object
|
|
64
|
+
end
|
|
65
|
+
return {
|
|
66
|
+
toErrorShape = toErrorShape,
|
|
67
|
+
TRPCError = TRPCError,
|
|
68
|
+
TRPCClientError = TRPCClientError,
|
|
69
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ClientEvent, EventHandler, EventMiddleware, ServerEvent, Validator } from "./types";
|
|
2
|
+
export declare class ServerEventBuilder<TContext, TInput> {
|
|
3
|
+
private readonly currentInputValidator?;
|
|
4
|
+
private readonly currentMiddlewares;
|
|
5
|
+
constructor(options?: {
|
|
6
|
+
inputValidator?: Validator<TInput>;
|
|
7
|
+
middlewares?: ReadonlyArray<EventMiddleware<TContext, TInput>>;
|
|
8
|
+
});
|
|
9
|
+
input<TNextInput>(validator: Validator<TNextInput>): ServerEventBuilder<TContext, TNextInput>;
|
|
10
|
+
use(middleware: EventMiddleware<TContext, TInput>): ServerEventBuilder<TContext, TInput>;
|
|
11
|
+
handle(handler: EventHandler<TContext, TInput>): ServerEvent<TContext, TInput>;
|
|
12
|
+
}
|
|
13
|
+
export declare class ClientEventBuilder<TContext, TInput> {
|
|
14
|
+
private readonly currentInputValidator?;
|
|
15
|
+
private readonly currentMiddlewares;
|
|
16
|
+
constructor(options?: {
|
|
17
|
+
inputValidator?: Validator<TInput>;
|
|
18
|
+
middlewares?: ReadonlyArray<EventMiddleware<TContext, TInput>>;
|
|
19
|
+
});
|
|
20
|
+
input<TNextInput>(validator: Validator<TNextInput>): ClientEventBuilder<TContext, TNextInput>;
|
|
21
|
+
use(middleware: EventMiddleware<TContext, TInput>): ClientEventBuilder<TContext, TInput>;
|
|
22
|
+
create(): ClientEvent<TContext, TInput>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local ServerEventImpl
|
|
3
|
+
do
|
|
4
|
+
ServerEventImpl = setmetatable({}, {
|
|
5
|
+
__tostring = function()
|
|
6
|
+
return "ServerEventImpl"
|
|
7
|
+
end,
|
|
8
|
+
})
|
|
9
|
+
ServerEventImpl.__index = ServerEventImpl
|
|
10
|
+
function ServerEventImpl.new(...)
|
|
11
|
+
local self = setmetatable({}, ServerEventImpl)
|
|
12
|
+
return self:constructor(...) or self
|
|
13
|
+
end
|
|
14
|
+
function ServerEventImpl:constructor(def)
|
|
15
|
+
self._def = def
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
local ClientEventImpl
|
|
19
|
+
do
|
|
20
|
+
ClientEventImpl = setmetatable({}, {
|
|
21
|
+
__tostring = function()
|
|
22
|
+
return "ClientEventImpl"
|
|
23
|
+
end,
|
|
24
|
+
})
|
|
25
|
+
ClientEventImpl.__index = ClientEventImpl
|
|
26
|
+
function ClientEventImpl.new(...)
|
|
27
|
+
local self = setmetatable({}, ClientEventImpl)
|
|
28
|
+
return self:constructor(...) or self
|
|
29
|
+
end
|
|
30
|
+
function ClientEventImpl:constructor(def)
|
|
31
|
+
self._def = def
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
local ServerEventBuilder
|
|
35
|
+
do
|
|
36
|
+
ServerEventBuilder = setmetatable({}, {
|
|
37
|
+
__tostring = function()
|
|
38
|
+
return "ServerEventBuilder"
|
|
39
|
+
end,
|
|
40
|
+
})
|
|
41
|
+
ServerEventBuilder.__index = ServerEventBuilder
|
|
42
|
+
function ServerEventBuilder.new(...)
|
|
43
|
+
local self = setmetatable({}, ServerEventBuilder)
|
|
44
|
+
return self:constructor(...) or self
|
|
45
|
+
end
|
|
46
|
+
function ServerEventBuilder:constructor(options)
|
|
47
|
+
local _result = options
|
|
48
|
+
if _result ~= nil then
|
|
49
|
+
_result = _result.inputValidator
|
|
50
|
+
end
|
|
51
|
+
self.currentInputValidator = _result
|
|
52
|
+
local _result_1 = options
|
|
53
|
+
if _result_1 ~= nil then
|
|
54
|
+
_result_1 = _result_1.middlewares
|
|
55
|
+
end
|
|
56
|
+
local _condition = _result_1
|
|
57
|
+
if _condition == nil then
|
|
58
|
+
_condition = {}
|
|
59
|
+
end
|
|
60
|
+
self.currentMiddlewares = _condition
|
|
61
|
+
end
|
|
62
|
+
function ServerEventBuilder:input(validator)
|
|
63
|
+
return ServerEventBuilder.new({
|
|
64
|
+
inputValidator = validator,
|
|
65
|
+
middlewares = self.currentMiddlewares,
|
|
66
|
+
})
|
|
67
|
+
end
|
|
68
|
+
function ServerEventBuilder:use(middleware)
|
|
69
|
+
local _object = {
|
|
70
|
+
inputValidator = self.currentInputValidator,
|
|
71
|
+
}
|
|
72
|
+
local _left = "middlewares"
|
|
73
|
+
local _array = {}
|
|
74
|
+
local _length = #_array
|
|
75
|
+
local _array_1 = self.currentMiddlewares
|
|
76
|
+
local _Length = #_array_1
|
|
77
|
+
table.move(_array_1, 1, _Length, _length + 1, _array)
|
|
78
|
+
_length += _Length
|
|
79
|
+
_array[_length + 1] = middleware
|
|
80
|
+
_object[_left] = _array
|
|
81
|
+
return ServerEventBuilder.new(_object)
|
|
82
|
+
end
|
|
83
|
+
function ServerEventBuilder:handle(handler)
|
|
84
|
+
return ServerEventImpl.new({
|
|
85
|
+
kind = "event",
|
|
86
|
+
direction = "server",
|
|
87
|
+
inputValidator = self.currentInputValidator,
|
|
88
|
+
middlewares = self.currentMiddlewares,
|
|
89
|
+
handle = handler,
|
|
90
|
+
})
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
local ClientEventBuilder
|
|
94
|
+
do
|
|
95
|
+
ClientEventBuilder = setmetatable({}, {
|
|
96
|
+
__tostring = function()
|
|
97
|
+
return "ClientEventBuilder"
|
|
98
|
+
end,
|
|
99
|
+
})
|
|
100
|
+
ClientEventBuilder.__index = ClientEventBuilder
|
|
101
|
+
function ClientEventBuilder.new(...)
|
|
102
|
+
local self = setmetatable({}, ClientEventBuilder)
|
|
103
|
+
return self:constructor(...) or self
|
|
104
|
+
end
|
|
105
|
+
function ClientEventBuilder:constructor(options)
|
|
106
|
+
local _result = options
|
|
107
|
+
if _result ~= nil then
|
|
108
|
+
_result = _result.inputValidator
|
|
109
|
+
end
|
|
110
|
+
self.currentInputValidator = _result
|
|
111
|
+
local _result_1 = options
|
|
112
|
+
if _result_1 ~= nil then
|
|
113
|
+
_result_1 = _result_1.middlewares
|
|
114
|
+
end
|
|
115
|
+
local _condition = _result_1
|
|
116
|
+
if _condition == nil then
|
|
117
|
+
_condition = {}
|
|
118
|
+
end
|
|
119
|
+
self.currentMiddlewares = _condition
|
|
120
|
+
end
|
|
121
|
+
function ClientEventBuilder:input(validator)
|
|
122
|
+
return ClientEventBuilder.new({
|
|
123
|
+
inputValidator = validator,
|
|
124
|
+
middlewares = self.currentMiddlewares,
|
|
125
|
+
})
|
|
126
|
+
end
|
|
127
|
+
function ClientEventBuilder:use(middleware)
|
|
128
|
+
local _object = {
|
|
129
|
+
inputValidator = self.currentInputValidator,
|
|
130
|
+
}
|
|
131
|
+
local _left = "middlewares"
|
|
132
|
+
local _array = {}
|
|
133
|
+
local _length = #_array
|
|
134
|
+
local _array_1 = self.currentMiddlewares
|
|
135
|
+
local _Length = #_array_1
|
|
136
|
+
table.move(_array_1, 1, _Length, _length + 1, _array)
|
|
137
|
+
_length += _Length
|
|
138
|
+
_array[_length + 1] = middleware
|
|
139
|
+
_object[_left] = _array
|
|
140
|
+
return ClientEventBuilder.new(_object)
|
|
141
|
+
end
|
|
142
|
+
function ClientEventBuilder:create()
|
|
143
|
+
return ClientEventImpl.new({
|
|
144
|
+
kind = "event",
|
|
145
|
+
direction = "client",
|
|
146
|
+
inputValidator = self.currentInputValidator,
|
|
147
|
+
middlewares = self.currentMiddlewares,
|
|
148
|
+
})
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
return {
|
|
152
|
+
ServerEventBuilder = ServerEventBuilder,
|
|
153
|
+
ClientEventBuilder = ClientEventBuilder,
|
|
154
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ClientEventBuilder, ServerEventBuilder } from "./event";
|
|
2
|
+
import { ProcedureBuilder } from "./procedure";
|
|
3
|
+
import type { InitTRPCConfig, Router, RouterShape } from "./types";
|
|
4
|
+
export interface TRPCFactory<TContext> {
|
|
5
|
+
readonly _config: InitTRPCConfig<TContext>;
|
|
6
|
+
readonly procedure: ProcedureBuilder<TContext, undefined, unknown, "mutation">;
|
|
7
|
+
readonly event: {
|
|
8
|
+
readonly server: ServerEventBuilder<TContext, undefined>;
|
|
9
|
+
readonly client: ClientEventBuilder<TContext, undefined>;
|
|
10
|
+
};
|
|
11
|
+
router<TShape extends RouterShape<TContext>>(shape: TShape): Router<TContext, TShape>;
|
|
12
|
+
}
|
|
13
|
+
declare class FactoryImpl<TContext> implements TRPCFactory<TContext> {
|
|
14
|
+
readonly _config: InitTRPCConfig<TContext>;
|
|
15
|
+
readonly procedure: ProcedureBuilder<TContext, undefined, unknown, "mutation">;
|
|
16
|
+
readonly event: {
|
|
17
|
+
server: ServerEventBuilder<TContext, undefined>;
|
|
18
|
+
client: ClientEventBuilder<TContext, undefined>;
|
|
19
|
+
};
|
|
20
|
+
constructor(config: InitTRPCConfig<TContext>);
|
|
21
|
+
router<TShape extends RouterShape<TContext>>(shape: TShape): Router<TContext, TShape>;
|
|
22
|
+
}
|
|
23
|
+
export declare function initTRPC(): {
|
|
24
|
+
context<TContext>(): {
|
|
25
|
+
create(config?: InitTRPCConfig<TContext>): FactoryImpl<TContext>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _event = TS.import(script, script.Parent, "event")
|
|
4
|
+
local ClientEventBuilder = _event.ClientEventBuilder
|
|
5
|
+
local ServerEventBuilder = _event.ServerEventBuilder
|
|
6
|
+
local ProcedureBuilder = TS.import(script, script.Parent, "procedure").ProcedureBuilder
|
|
7
|
+
local createRouter = TS.import(script, script.Parent, "router").createRouter
|
|
8
|
+
local FactoryImpl
|
|
9
|
+
do
|
|
10
|
+
FactoryImpl = setmetatable({}, {
|
|
11
|
+
__tostring = function()
|
|
12
|
+
return "FactoryImpl"
|
|
13
|
+
end,
|
|
14
|
+
})
|
|
15
|
+
FactoryImpl.__index = FactoryImpl
|
|
16
|
+
function FactoryImpl.new(...)
|
|
17
|
+
local self = setmetatable({}, FactoryImpl)
|
|
18
|
+
return self:constructor(...) or self
|
|
19
|
+
end
|
|
20
|
+
function FactoryImpl:constructor(config)
|
|
21
|
+
self.procedure = ProcedureBuilder.new()
|
|
22
|
+
self.event = {
|
|
23
|
+
server = ServerEventBuilder.new(),
|
|
24
|
+
client = ClientEventBuilder.new(),
|
|
25
|
+
}
|
|
26
|
+
self._config = config
|
|
27
|
+
end
|
|
28
|
+
function FactoryImpl:router(shape)
|
|
29
|
+
return createRouter(shape)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
local function initTRPC()
|
|
33
|
+
return {
|
|
34
|
+
context = function(self)
|
|
35
|
+
return {
|
|
36
|
+
create = function(self, config)
|
|
37
|
+
if config == nil then
|
|
38
|
+
config = {}
|
|
39
|
+
end
|
|
40
|
+
return FactoryImpl.new(config)
|
|
41
|
+
end,
|
|
42
|
+
}
|
|
43
|
+
end,
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
return {
|
|
47
|
+
initTRPC = initTRPC,
|
|
48
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Procedure, ProcedureIntent, ProcedureMiddleware, ProcedureResolver, Validator } from "./types";
|
|
2
|
+
export declare class ProcedureBuilder<TContext, TInput, TOutput, TIntent extends ProcedureIntent = "mutation"> {
|
|
3
|
+
private readonly inputValidator?;
|
|
4
|
+
private readonly outputValidator?;
|
|
5
|
+
private readonly middlewares;
|
|
6
|
+
private readonly currentIntent;
|
|
7
|
+
constructor(options?: {
|
|
8
|
+
inputValidator?: Validator<TInput>;
|
|
9
|
+
outputValidator?: Validator<TOutput>;
|
|
10
|
+
middlewares?: ReadonlyArray<ProcedureMiddleware<TContext, TInput, TOutput>>;
|
|
11
|
+
intent?: TIntent;
|
|
12
|
+
});
|
|
13
|
+
input<TNextInput>(validator: Validator<TNextInput>): ProcedureBuilder<TContext, TNextInput, TOutput, TIntent>;
|
|
14
|
+
output<TNextOutput>(validator: Validator<TNextOutput>): ProcedureBuilder<TContext, TInput, TNextOutput, TIntent>;
|
|
15
|
+
use(middleware: ProcedureMiddleware<TContext, TInput, TOutput>): ProcedureBuilder<TContext, TInput, TOutput, TIntent>;
|
|
16
|
+
intent<TNextIntent extends ProcedureIntent>(intent: TNextIntent): ProcedureBuilder<TContext, TInput, TOutput, TNextIntent>;
|
|
17
|
+
resolve<TResolvedOutput extends TOutput>(resolve: ProcedureResolver<TContext, TInput, TResolvedOutput>): Procedure<TContext, TInput, TResolvedOutput, TIntent>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local ProcedureImpl
|
|
3
|
+
do
|
|
4
|
+
ProcedureImpl = setmetatable({}, {
|
|
5
|
+
__tostring = function()
|
|
6
|
+
return "ProcedureImpl"
|
|
7
|
+
end,
|
|
8
|
+
})
|
|
9
|
+
ProcedureImpl.__index = ProcedureImpl
|
|
10
|
+
function ProcedureImpl.new(...)
|
|
11
|
+
local self = setmetatable({}, ProcedureImpl)
|
|
12
|
+
return self:constructor(...) or self
|
|
13
|
+
end
|
|
14
|
+
function ProcedureImpl:constructor(def)
|
|
15
|
+
self._def = def
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
local ProcedureBuilder
|
|
19
|
+
do
|
|
20
|
+
ProcedureBuilder = setmetatable({}, {
|
|
21
|
+
__tostring = function()
|
|
22
|
+
return "ProcedureBuilder"
|
|
23
|
+
end,
|
|
24
|
+
})
|
|
25
|
+
ProcedureBuilder.__index = ProcedureBuilder
|
|
26
|
+
function ProcedureBuilder.new(...)
|
|
27
|
+
local self = setmetatable({}, ProcedureBuilder)
|
|
28
|
+
return self:constructor(...) or self
|
|
29
|
+
end
|
|
30
|
+
function ProcedureBuilder:constructor(options)
|
|
31
|
+
local _result = options
|
|
32
|
+
if _result ~= nil then
|
|
33
|
+
_result = _result.inputValidator
|
|
34
|
+
end
|
|
35
|
+
self.inputValidator = _result
|
|
36
|
+
local _result_1 = options
|
|
37
|
+
if _result_1 ~= nil then
|
|
38
|
+
_result_1 = _result_1.outputValidator
|
|
39
|
+
end
|
|
40
|
+
self.outputValidator = _result_1
|
|
41
|
+
local _result_2 = options
|
|
42
|
+
if _result_2 ~= nil then
|
|
43
|
+
_result_2 = _result_2.middlewares
|
|
44
|
+
end
|
|
45
|
+
local _condition = _result_2
|
|
46
|
+
if _condition == nil then
|
|
47
|
+
_condition = {}
|
|
48
|
+
end
|
|
49
|
+
self.middlewares = _condition
|
|
50
|
+
local _result_3 = options
|
|
51
|
+
if _result_3 ~= nil then
|
|
52
|
+
_result_3 = _result_3.intent
|
|
53
|
+
end
|
|
54
|
+
local _condition_1 = _result_3
|
|
55
|
+
if _condition_1 == nil then
|
|
56
|
+
_condition_1 = "mutation"
|
|
57
|
+
end
|
|
58
|
+
self.currentIntent = _condition_1
|
|
59
|
+
end
|
|
60
|
+
function ProcedureBuilder:input(validator)
|
|
61
|
+
return ProcedureBuilder.new({
|
|
62
|
+
inputValidator = validator,
|
|
63
|
+
outputValidator = self.outputValidator,
|
|
64
|
+
middlewares = self.middlewares,
|
|
65
|
+
intent = self.currentIntent,
|
|
66
|
+
})
|
|
67
|
+
end
|
|
68
|
+
function ProcedureBuilder:output(validator)
|
|
69
|
+
return ProcedureBuilder.new({
|
|
70
|
+
inputValidator = self.inputValidator,
|
|
71
|
+
outputValidator = validator,
|
|
72
|
+
middlewares = self.middlewares,
|
|
73
|
+
intent = self.currentIntent,
|
|
74
|
+
})
|
|
75
|
+
end
|
|
76
|
+
function ProcedureBuilder:use(middleware)
|
|
77
|
+
local _object = {
|
|
78
|
+
inputValidator = self.inputValidator,
|
|
79
|
+
outputValidator = self.outputValidator,
|
|
80
|
+
}
|
|
81
|
+
local _left = "middlewares"
|
|
82
|
+
local _array = {}
|
|
83
|
+
local _length = #_array
|
|
84
|
+
local _array_1 = self.middlewares
|
|
85
|
+
local _Length = #_array_1
|
|
86
|
+
table.move(_array_1, 1, _Length, _length + 1, _array)
|
|
87
|
+
_length += _Length
|
|
88
|
+
_array[_length + 1] = middleware
|
|
89
|
+
_object[_left] = _array
|
|
90
|
+
_object.intent = self.currentIntent
|
|
91
|
+
return ProcedureBuilder.new(_object)
|
|
92
|
+
end
|
|
93
|
+
function ProcedureBuilder:intent(intent)
|
|
94
|
+
return ProcedureBuilder.new({
|
|
95
|
+
inputValidator = self.inputValidator,
|
|
96
|
+
outputValidator = self.outputValidator,
|
|
97
|
+
middlewares = self.middlewares,
|
|
98
|
+
intent = intent,
|
|
99
|
+
})
|
|
100
|
+
end
|
|
101
|
+
function ProcedureBuilder:resolve(resolve)
|
|
102
|
+
local def = {
|
|
103
|
+
kind = "procedure",
|
|
104
|
+
intent = self.currentIntent,
|
|
105
|
+
inputValidator = self.inputValidator,
|
|
106
|
+
outputValidator = self.outputValidator,
|
|
107
|
+
middlewares = self.middlewares,
|
|
108
|
+
resolve = resolve,
|
|
109
|
+
}
|
|
110
|
+
return ProcedureImpl.new(def)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
return {
|
|
114
|
+
ProcedureBuilder = ProcedureBuilder,
|
|
115
|
+
}
|