Compare commits
6 Commits
next
...
696976b52f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
696976b52f | ||
|
|
67bf12d7a9 | ||
|
|
a6ef4defe6 | ||
|
|
9ee50bfd19 | ||
|
|
7b3ed49369 | ||
|
|
9348178b0f |
84
package.json
84
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thilawyn/thilalib",
|
||||
"version": "0.1.21",
|
||||
"version": "0.1.14",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"./dist"
|
||||
@@ -8,75 +8,79 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./Express": {
|
||||
"types": "./dist/Express/index.d.ts",
|
||||
"default": "./dist/Express/index.js"
|
||||
"require": {
|
||||
"types": "./dist/index.d.cts",
|
||||
"default": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"./TRPC": {
|
||||
"types": "./dist/TRPC/index.d.ts",
|
||||
"default": "./dist/TRPC/index.js"
|
||||
"./Layers": {
|
||||
"import": {
|
||||
"types": "./dist/Layers/index.d.ts",
|
||||
"default": "./dist/Layers/index.js"
|
||||
},
|
||||
"./Types": {
|
||||
"types": "./dist/Types/index.d.ts",
|
||||
"default": "./dist/Types/index.js"
|
||||
"require": {
|
||||
"types": "./dist/Layers/index.d.cts",
|
||||
"default": "./dist/Layers/index.cjs"
|
||||
}
|
||||
},
|
||||
"./Schema": {
|
||||
"import": {
|
||||
"types": "./dist/Schema/index.d.ts",
|
||||
"default": "./dist/Schema/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/Schema/index.d.cts",
|
||||
"default": "./dist/Schema/index.cjs"
|
||||
}
|
||||
},
|
||||
"./Schema/MobX": {
|
||||
"import": {
|
||||
"types": "./dist/Schema/MobX/index.d.ts",
|
||||
"default": "./dist/Schema/MobX/index.js"
|
||||
},
|
||||
"./Schema/TanStackForm": {
|
||||
"types": "./dist/Schema/TanStackForm/index.d.ts",
|
||||
"default": "./dist/Schema/TanStackForm/index.js"
|
||||
"require": {
|
||||
"types": "./dist/Schema/MobX/index.d.cts",
|
||||
"default": "./dist/Schema/MobX/index.cjs"
|
||||
}
|
||||
},
|
||||
"./JSONWebToken": {
|
||||
"types": "./dist/JSONWebToken.d.ts",
|
||||
"default": "./dist/JSONWebToken.js"
|
||||
"./Types": {
|
||||
"import": {
|
||||
"types": "./dist/Types/index.d.ts",
|
||||
"default": "./dist/Types/index.js"
|
||||
},
|
||||
"./OpenAIClient": {
|
||||
"types": "./dist/OpenAIClient.d.ts",
|
||||
"default": "./dist/OpenAIClient.js"
|
||||
},
|
||||
"./*": {
|
||||
"types": "./dist/*.d.ts",
|
||||
"default": "./dist/*.js"
|
||||
"require": {
|
||||
"types": "./dist/Types/index.d.cts",
|
||||
"default": "./dist/Types/index.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build": "tsup",
|
||||
"lint:tsc": "tsc --noEmit",
|
||||
"clean:cache": "rm -f tsconfig.tsbuildinfo",
|
||||
"clean:dist": "rm -rf dist",
|
||||
"clean:node": "rm -rf node_modules"
|
||||
},
|
||||
"dependencies": {
|
||||
"remeda": "^2.12.0",
|
||||
"type-fest": "^4.26.1"
|
||||
"remeda": "^2.11.0",
|
||||
"type-fest": "^4.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@effect/schema": "^0.72.3",
|
||||
"@tanstack/form-core": "^0.32.0",
|
||||
"@trpc/server": "^10.45.2",
|
||||
"@types/express": "^4.17.21",
|
||||
"@effect/schema": "^0.71.1",
|
||||
"@prisma/studio-server": "^0.502.0",
|
||||
"@types/jsonwebtoken": "^9.0.6",
|
||||
"@types/ws": "^8.5.12",
|
||||
"bun-types": "^1.1.27",
|
||||
"effect": "^3.7.2",
|
||||
"express": "^4.21.0",
|
||||
"bun-types": "^1.1.26",
|
||||
"effect": "^3.6.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mobx": "^6.13.2",
|
||||
"npm-check-updates": "^17.1.1",
|
||||
"mobx": "^6.13.1",
|
||||
"npm-check-updates": "^17.1.0",
|
||||
"npm-sort": "^0.0.4",
|
||||
"openai": "^4.60.0",
|
||||
"tsup": "^8.2.4",
|
||||
"tsx": "^4.19.1",
|
||||
"typescript": "^5.6.2",
|
||||
"ws": "^8.18.0"
|
||||
"tsx": "^4.18.0",
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Config, Context, Effect, Layer } from "effect"
|
||||
import type { Express } from "express"
|
||||
import { ImportError } from "../ImportError"
|
||||
|
||||
|
||||
export class ExpressApp extends Context.Tag("ExpressApp")<ExpressApp, Express>() {}
|
||||
|
||||
|
||||
const importExpress = Effect.tryPromise({
|
||||
try: () => import("express"),
|
||||
catch: cause => new ImportError({ path: "express", cause }),
|
||||
})
|
||||
|
||||
export const ExpressAppLive = (
|
||||
config: {
|
||||
readonly trustProxy?: Config.Config<boolean | undefined>
|
||||
} = {}
|
||||
) => Layer.effect(ExpressApp, Effect.gen(function*() {
|
||||
const app = (yield* importExpress).default()
|
||||
app.set("trust proxy", (yield* config.trustProxy ?? Config.succeed(undefined)) ?? false)
|
||||
return app
|
||||
}))
|
||||
@@ -1,70 +0,0 @@
|
||||
import { Config, Context, Effect, Layer, Match } from "effect"
|
||||
import type { Server } from "node:http"
|
||||
import type { AddressInfo } from "node:net"
|
||||
import { ImportError } from "../ImportError"
|
||||
import { ExpressApp } from "./ExpressApp"
|
||||
|
||||
|
||||
export class ExpressNodeHTTPServer extends Context.Tag("ExpressNodeHTTPServer")<ExpressNodeHTTPServer, Server>() {}
|
||||
|
||||
|
||||
const importNodeHTTP = Effect.tryPromise({
|
||||
try: () => import("node:http"),
|
||||
catch: cause => new ImportError({ path: "node:http", cause }),
|
||||
})
|
||||
|
||||
const serverListeningMessage = Match.type<AddressInfo | string | null>().pipe(
|
||||
Match.when(Match.null, () => "HTTP server listening"),
|
||||
Match.when(Match.string, v => `HTTP server listening on ${ v }`),
|
||||
Match.orElse(v => `HTTP server listening on ${ v.address === "::" ? "*" : v.address }:${ v.port }`),
|
||||
)
|
||||
|
||||
export const ExpressNodeHTTPServerLive = (
|
||||
config: {
|
||||
readonly backlog?: Config.Config<number | undefined>
|
||||
readonly exclusive?: Config.Config<boolean | undefined>
|
||||
readonly host?: Config.Config<string | undefined>
|
||||
readonly ipv6Only?: Config.Config<boolean | undefined>
|
||||
readonly path?: Config.Config<string | undefined>
|
||||
readonly port?: Config.Config<number | undefined>
|
||||
readonly readableAll?: Config.Config<boolean | undefined>
|
||||
readonly signal?: AbortSignal
|
||||
readonly writableAll?: Config.Config<boolean | undefined>
|
||||
} = {}
|
||||
) => Layer.effect(ExpressNodeHTTPServer, Effect.acquireRelease(
|
||||
Effect.gen(function*() {
|
||||
const app = yield* ExpressApp
|
||||
const http = yield* importNodeHTTP
|
||||
|
||||
const options = {
|
||||
backlog: yield* config.backlog ?? Config.succeed(undefined),
|
||||
exclusive: yield* config.exclusive ?? Config.succeed(undefined),
|
||||
host: yield* config.host ?? Config.succeed(undefined),
|
||||
ipv6Only: yield* config.ipv6Only ?? Config.succeed(undefined),
|
||||
path: yield* config.path ?? Config.succeed(undefined),
|
||||
port: yield* config.port ?? Config.succeed(undefined),
|
||||
readableAll: yield* config.readableAll ?? Config.succeed(undefined),
|
||||
signal: config.signal,
|
||||
writableAll: yield* config.writableAll ?? Config.succeed(undefined),
|
||||
} as const
|
||||
|
||||
return yield* Effect.async<Server>(resume => {
|
||||
const server = http.createServer(app).listen(options,
|
||||
() => resume(
|
||||
Effect.succeed(server).pipe(
|
||||
Effect.tap(Effect.logInfo(
|
||||
serverListeningMessage(server.address())
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
}),
|
||||
|
||||
server => Effect.gen(function*() {
|
||||
yield* Effect.logInfo("HTTP server is stopping. Waiting for existing connections to end...")
|
||||
yield* Effect.async(resume => {
|
||||
server.close(() => resume(Effect.logInfo("HTTP server closed")))
|
||||
})
|
||||
}),
|
||||
))
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Effect, Layer } from "effect"
|
||||
import { ExpressAppLive } from "./ExpressApp"
|
||||
import { ExpressNodeHTTPServerLive } from "./ExpressNodeHTTPServer"
|
||||
|
||||
|
||||
const AppLive = ExpressAppLive()
|
||||
const HTTPServerLive = ExpressNodeHTTPServerLive()
|
||||
|
||||
const ServerLive = Layer.empty.pipe(
|
||||
Layer.provideMerge(HTTPServerLive),
|
||||
Layer.provideMerge(AppLive),
|
||||
)
|
||||
|
||||
Layer.launch(ServerLive).pipe(
|
||||
Effect.scoped,
|
||||
Effect.runPromise,
|
||||
)
|
||||
@@ -1,2 +0,0 @@
|
||||
export * as ExpressApp from "./ExpressApp"
|
||||
export * as ExpressNodeHTTPServer from "./ExpressNodeHTTPServer"
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Data } from "effect"
|
||||
|
||||
|
||||
export class ImportError extends Data.TaggedError("ImportError")<{
|
||||
path: string
|
||||
cause: unknown
|
||||
}> {
|
||||
toString(): string {
|
||||
return `Could not import '${ this.path }'`
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import type * as JWT from "jsonwebtoken"
|
||||
import { ImportError } from "./ImportError"
|
||||
|
||||
|
||||
export class JSONWebToken extends Context.Tag("JSONWebToken")<JSONWebToken, JSONWebTokenService>() {}
|
||||
|
||||
export interface JSONWebTokenService {
|
||||
sign: (
|
||||
payload: string | object | Buffer,
|
||||
secretOrPrivateKey: JWT.Secret,
|
||||
options: JWT.SignOptions,
|
||||
) => Effect.Effect<
|
||||
string,
|
||||
Error,
|
||||
never
|
||||
>
|
||||
|
||||
verify: (
|
||||
token: string,
|
||||
secretOrPublicKey: JWT.Secret,
|
||||
options: JWT.VerifyOptions,
|
||||
) => Effect.Effect<
|
||||
string | JWT.Jwt | JWT.JwtPayload,
|
||||
JWT.VerifyErrors | Error,
|
||||
never
|
||||
>
|
||||
}
|
||||
|
||||
|
||||
const importJWT = Effect.tryPromise({
|
||||
try: () => import("jsonwebtoken"),
|
||||
catch: cause => new ImportError({ path: "jsonwebtoken", cause }),
|
||||
})
|
||||
|
||||
export const JSONWebTokenLive = Layer.effect(JSONWebToken, importJWT.pipe(
|
||||
Effect.map(jwt => JSONWebToken.of({
|
||||
sign: (payload, secretOrPrivateKey, options) => Effect.async(resume =>
|
||||
jwt.sign(payload, secretOrPrivateKey, options, (err, token) => {
|
||||
resume(token
|
||||
? Effect.succeed(token)
|
||||
: Effect.fail(err || new Error("Unknown error"))
|
||||
)
|
||||
})
|
||||
),
|
||||
|
||||
verify: (token, secretOrPublicKey, options) => Effect.async(resume =>
|
||||
jwt.verify(token, secretOrPublicKey, options, (err, decoded) => {
|
||||
resume(decoded
|
||||
? Effect.succeed(decoded)
|
||||
: Effect.fail(err || new Error("Unknown error"))
|
||||
)
|
||||
})
|
||||
),
|
||||
}))
|
||||
))
|
||||
46
src/Layers/JSONWebToken.ts
Normal file
46
src/Layers/JSONWebToken.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import jwt from "jsonwebtoken"
|
||||
|
||||
|
||||
export class JSONWebToken extends Context.Tag("JSONWebToken")<JSONWebToken, {
|
||||
sign: (
|
||||
payload: string | object | Buffer,
|
||||
secretOrPrivateKey: jwt.Secret,
|
||||
options: jwt.SignOptions,
|
||||
) => Effect.Effect<
|
||||
string,
|
||||
Error,
|
||||
never
|
||||
>,
|
||||
|
||||
verify: (
|
||||
token: string,
|
||||
secretOrPublicKey: jwt.Secret,
|
||||
options: jwt.VerifyOptions,
|
||||
) => Effect.Effect<
|
||||
string | jwt.Jwt | jwt.JwtPayload,
|
||||
jwt.VerifyErrors | Error,
|
||||
never
|
||||
>,
|
||||
}>() {}
|
||||
|
||||
|
||||
export const JSONWebTokenLive = Layer.succeed(JSONWebToken, {
|
||||
sign: (payload, secretOrPrivateKey, options) => Effect.async(resume =>
|
||||
jwt.sign(payload, secretOrPrivateKey, options, (err, token) => {
|
||||
resume(token
|
||||
? Effect.succeed(token)
|
||||
: Effect.fail(err || new Error("Unknown error"))
|
||||
)
|
||||
})
|
||||
),
|
||||
|
||||
verify: (token, secretOrPublicKey, options) => Effect.async(resume =>
|
||||
jwt.verify(token, secretOrPublicKey, options, (err, decoded) => {
|
||||
resume(decoded
|
||||
? Effect.succeed(decoded)
|
||||
: Effect.fail(err || new Error("Unknown error"))
|
||||
)
|
||||
})
|
||||
),
|
||||
})
|
||||
29
src/Layers/PrismaStudioRoute.ts
Normal file
29
src/Layers/PrismaStudioRoute.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
// import { StudioServer } from "@prisma/studio-server"
|
||||
// import { Config, Effect, Layer } from "effect"
|
||||
|
||||
|
||||
// export const PrismaStudioRouteLive = ({
|
||||
// httpPort = Config.succeed(5555),
|
||||
// schemaPath = Config.succeed(""),
|
||||
// schemaText = Config.succeed(""),
|
||||
// }: {
|
||||
// httpPort: Config.Config<number>
|
||||
// schemaPath: Config.Config<string>
|
||||
// schemaText: Config.Config<string>
|
||||
// }) =>
|
||||
// Layer.effectDiscard(Effect.gen(function*() {
|
||||
|
||||
// return Effect.acquireRelease(
|
||||
// Effect.gen(function*() {
|
||||
// const server = new StudioServer({
|
||||
// port: yield* httpPort,
|
||||
// schemaPath: yield* schemaPath,
|
||||
// schemaText: yield* schemaText,
|
||||
// })
|
||||
// }),
|
||||
|
||||
// () => Effect.gen(function*() {
|
||||
|
||||
// }),
|
||||
// )
|
||||
// }))
|
||||
5
src/Layers/index.ts
Normal file
5
src/Layers/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* A wrapper around the jsonwebtoken library for Effect
|
||||
* Requires `effect`, `jsonwebtoken` and `@types/jsonwebtoken` to be installed
|
||||
*/
|
||||
export * as JSONWebToken from "./JSONWebToken"
|
||||
@@ -1,69 +0,0 @@
|
||||
import { Config, Context, Effect, Layer } from "effect"
|
||||
import type { OpenAI } from "openai"
|
||||
import { ImportError } from "./ImportError"
|
||||
|
||||
|
||||
export class OpenAIClient extends Context.Tag("OpenAIClient")<OpenAIClient, OpenAIClientService>() {}
|
||||
|
||||
export class OpenAIClientService {
|
||||
constructor(
|
||||
readonly openai: Effect.Effect.Success<typeof importOpenAI>,
|
||||
readonly client: OpenAI,
|
||||
) {}
|
||||
|
||||
try<A>(
|
||||
try_: (
|
||||
client: OpenAI,
|
||||
signal: AbortSignal,
|
||||
) => Promise<A>
|
||||
) {
|
||||
return Effect.tryPromise({
|
||||
try: signal => try_(this.client, signal),
|
||||
catch: e => e instanceof this.openai.OpenAIError
|
||||
? e
|
||||
: new Error(`Unknown OpenAIClient error: ${ e }`),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const importOpenAI = Effect.tryPromise({
|
||||
try: () => import("openai"),
|
||||
catch: cause => new ImportError({ path: "openai", cause }),
|
||||
})
|
||||
|
||||
export const OpenAIClientLive = (
|
||||
config: {
|
||||
readonly apiKey: Config.Config<string>
|
||||
readonly organization?: Config.Config<string | undefined>
|
||||
readonly project?: Config.Config<string | undefined>
|
||||
readonly baseURL?: Config.Config<string | undefined>
|
||||
readonly timeout?: Config.Config<number | undefined>
|
||||
readonly maxRetries?: Config.Config<number | undefined>
|
||||
|
||||
readonly httpAgent?: any
|
||||
readonly fetch?: any
|
||||
readonly defaultHeaders?: { [x: string]: string }
|
||||
readonly defaultQuery?: { [x: string]: string }
|
||||
}
|
||||
) => Layer.effect(OpenAIClient, Effect.gen(function*() {
|
||||
const openai = yield* importOpenAI
|
||||
|
||||
return new OpenAIClientService(
|
||||
openai,
|
||||
|
||||
new openai.OpenAI({
|
||||
apiKey: yield* config.apiKey,
|
||||
organization: (yield* config.organization ?? Config.succeed(undefined)) ?? null,
|
||||
project: (yield* config.project ?? Config.succeed(undefined)) ?? null,
|
||||
baseURL: (yield* config.baseURL ?? Config.succeed(undefined)) ?? "https://api.openai.com/v1",
|
||||
timeout: yield* config.timeout ?? Config.succeed(undefined),
|
||||
maxRetries: yield* config.maxRetries ?? Config.succeed(undefined),
|
||||
|
||||
httpAgent: config.httpAgent,
|
||||
fetch: config.fetch,
|
||||
defaultHeaders: config.defaultHeaders,
|
||||
defaultQuery: config.defaultQuery,
|
||||
}),
|
||||
)
|
||||
}))
|
||||
@@ -1,44 +0,0 @@
|
||||
// import { StudioServer } from "@prisma/studio-server"
|
||||
// import { Config, Effect, Layer } from "effect"
|
||||
// import proxy from "express-http-proxy"
|
||||
// import { ExpressApp } from "../http/ExpressApp.service"
|
||||
// import { PrismaClient } from "./PrismaClient.service"
|
||||
|
||||
|
||||
// export const PrismaStudioRoute = ({
|
||||
// httpRoot = Config.succeed("/studio"),
|
||||
// httpPort = Config.succeed(5555),
|
||||
// schemaPath = Config.succeed(""),
|
||||
// schemaText = Config.succeed(""),
|
||||
// }: {
|
||||
// readonly httpRoot?: Config.Config<string>
|
||||
// readonly httpPort?: Config.Config<number>
|
||||
// readonly schemaPath?: Config.Config<string>
|
||||
// readonly schemaText?: Config.Config<string>
|
||||
// } = {}) =>
|
||||
// Layer.effectDiscard(Effect.acquireRelease(
|
||||
// Effect.gen(function*() {
|
||||
// const prisma = yield* PrismaClient
|
||||
// const app = yield* ExpressApp
|
||||
|
||||
// const port = yield* httpPort
|
||||
|
||||
// const server = new StudioServer({
|
||||
// port,
|
||||
// schemaPath: yield* schemaPath,
|
||||
// schemaText: yield* schemaText,
|
||||
// versions: { prisma: prisma.Prisma.prismaVersion.client },
|
||||
// })
|
||||
|
||||
// app.use(yield* httpRoot, proxy(`http://localhost:${ port }`))
|
||||
|
||||
// yield* Effect.promise(() => server.start())
|
||||
// return server
|
||||
// }),
|
||||
|
||||
// server => Effect.sync(() => server.stop()),
|
||||
// ))
|
||||
|
||||
|
||||
// export const PrismaStudioRouteLive = Layer.empty
|
||||
// export const PrismaStudioRouteDebug = PrismaStudioRoute()
|
||||
@@ -3,12 +3,12 @@ import { makeObservable, observable, type CreateObservableOptions } from "mobx"
|
||||
import { mapValues } from "remeda"
|
||||
|
||||
|
||||
export interface ObservableClassSelf {
|
||||
interface ObservableClassSelf {
|
||||
new(...args: any[]): Schema.Struct.Type<Schema.Struct.Fields>
|
||||
readonly fields: { readonly [K in keyof Schema.Struct.Fields]: Schema.Struct.Fields[K] }
|
||||
}
|
||||
|
||||
export interface ObservableClassOptions extends Omit<CreateObservableOptions, "proxy"> {}
|
||||
interface ObservableClassOptions extends Omit<CreateObservableOptions, "proxy"> {}
|
||||
|
||||
export const ObservableClass = (options?: ObservableClassOptions) =>
|
||||
<Self extends ObservableClassSelf>(self: Self) =>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./makeSchemaFormValidator"
|
||||
@@ -1,138 +0,0 @@
|
||||
import { ArrayFormatter, Schema } from "@effect/schema"
|
||||
import type { ParseOptions } from "@effect/schema/AST"
|
||||
import type { DeepKeys, DeepValue, FieldApi, FormApi, FormValidationError, ValidationError, Validator } from "@tanstack/form-core"
|
||||
import { Array, Effect, Either, flow, Layer, ManagedRuntime, pipe } from "effect"
|
||||
import { mapValues } from "remeda"
|
||||
|
||||
|
||||
export const makeSchemaFormValidator = <A, I>(
|
||||
schema: Schema.Schema<A, I, never>,
|
||||
options?: ParseOptions,
|
||||
) => {
|
||||
const decodeEither = Schema.decodeEither(schema, options)
|
||||
|
||||
return <
|
||||
TFormData extends I,
|
||||
TFormValidator extends Validator<TFormData, unknown> | undefined,
|
||||
>(props: {
|
||||
value: TFormData
|
||||
formApi: FormApi<TFormData, TFormValidator>
|
||||
}): FormValidationError<TFormData> =>
|
||||
decodeEither(props.value).pipe(result =>
|
||||
Either.isLeft(result)
|
||||
? {
|
||||
form: "Please check the fields",
|
||||
fields: issuesToFieldsRecord(ArrayFormatter.formatErrorSync(result.left), props.formApi) as any,
|
||||
}
|
||||
: null
|
||||
)
|
||||
}
|
||||
|
||||
export const makeSchemaAsyncFormValidator = <A, I>(
|
||||
schema: Schema.Schema<A, I, never>,
|
||||
options?: ParseOptions,
|
||||
) => {
|
||||
const runtime = ManagedRuntime.make(Layer.empty)
|
||||
const decode = Schema.decode(schema, options)
|
||||
|
||||
return <
|
||||
TFormData extends I,
|
||||
TFormValidator extends Validator<TFormData, unknown> | undefined,
|
||||
>(props: {
|
||||
value: TFormData
|
||||
formApi: FormApi<TFormData, TFormValidator>
|
||||
signal: AbortSignal
|
||||
}): Promise<FormValidationError<TFormData>> =>
|
||||
decode(props.value).pipe(
|
||||
Effect.matchEffect({
|
||||
onSuccess: () => Effect.succeed(null),
|
||||
|
||||
onFailure: e => ArrayFormatter.formatError(e).pipe(
|
||||
Effect.map(issues => ({
|
||||
form: "Please check the fields",
|
||||
fields: issuesToFieldsRecord(issues, props.formApi) as any,
|
||||
}))
|
||||
),
|
||||
}),
|
||||
|
||||
prgm => runtime.runPromise(prgm, { signal: props.signal }),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: handle nested array fields
|
||||
*/
|
||||
const issuesToFieldsRecord = <
|
||||
TFormData,
|
||||
TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
|
||||
>(
|
||||
issues: readonly ArrayFormatter.Issue[],
|
||||
_formApi: FormApi<TFormData, TFormValidator>,
|
||||
) => pipe(issues,
|
||||
Array.groupBy(issue => issue.path.join(".")),
|
||||
|
||||
mapValues(flow(
|
||||
Array.map(issue => issue.message),
|
||||
Array.join("\n"),
|
||||
)),
|
||||
)
|
||||
|
||||
|
||||
export const makeSchemaFieldValidator = <A, I>(
|
||||
schema: Schema.Schema<A, I, never>,
|
||||
options?: ParseOptions,
|
||||
) => {
|
||||
const decodeEither = Schema.decodeEither(schema, options)
|
||||
|
||||
return <
|
||||
TParentData,
|
||||
TName extends DeepKeys<TParentData>,
|
||||
TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined,
|
||||
TFormValidator extends Validator<TParentData, unknown> | undefined,
|
||||
TData extends I & DeepValue<TParentData, TName>,
|
||||
>(props: {
|
||||
value: TData
|
||||
fieldApi: FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>
|
||||
}): ValidationError =>
|
||||
decodeEither(props.value).pipe(result =>
|
||||
Either.isLeft(result)
|
||||
? ArrayFormatter.formatErrorSync(result.left)
|
||||
.map(issue => issue.message)
|
||||
.join("\n")
|
||||
: null
|
||||
)
|
||||
}
|
||||
|
||||
export const makeSchemaAsyncFieldValidator = <A, I>(
|
||||
schema: Schema.Schema<A, I, never>,
|
||||
options?: ParseOptions,
|
||||
) => {
|
||||
const runtime = ManagedRuntime.make(Layer.empty)
|
||||
const decode = Schema.decode(schema, options)
|
||||
|
||||
return <
|
||||
TParentData,
|
||||
TName extends DeepKeys<TParentData>,
|
||||
TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined,
|
||||
TFormValidator extends Validator<TParentData, unknown> | undefined,
|
||||
TData extends I & DeepValue<TParentData, TName>,
|
||||
>(props: {
|
||||
value: TData
|
||||
fieldApi: FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>
|
||||
signal: AbortSignal
|
||||
}): Promise<ValidationError> =>
|
||||
decode(props.value).pipe(
|
||||
Effect.matchEffect({
|
||||
onSuccess: () => Effect.succeed(null),
|
||||
|
||||
onFailure: e => ArrayFormatter.formatError(e).pipe(
|
||||
Effect.map(flow(
|
||||
Array.map(issue => issue.message),
|
||||
Array.join("\n"),
|
||||
))
|
||||
),
|
||||
}),
|
||||
|
||||
prgm => runtime.runPromise(prgm, { signal: props.signal }),
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Schema } from "@effect/schema"
|
||||
|
||||
|
||||
export const email = Schema.pattern(
|
||||
/^(?!\.)(?!.*\.\.)([A-Z0-9_+-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i,
|
||||
|
||||
{
|
||||
identifier: "email",
|
||||
title: "email",
|
||||
message: () => "Not an email address",
|
||||
},
|
||||
)
|
||||
@@ -1,6 +1,5 @@
|
||||
export * from "./Class"
|
||||
export * from "./DateTime"
|
||||
export * from "./email"
|
||||
export * from "./encodedAsPrismaJsonValue"
|
||||
export * from "./Kind"
|
||||
export * as MobX from "./MobX"
|
||||
@@ -8,5 +7,4 @@ export * from "./MutableClass"
|
||||
export * from "./MutableTaggedClass"
|
||||
export * from "./Tag"
|
||||
export * from "./TaggedClass"
|
||||
export * as TanStackForm from "./TanStackForm"
|
||||
export * from "./toJsonifiable"
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { type TRPCContext } from "./TRPCContext"
|
||||
import { importTRPCServer } from "./importTRPCServer"
|
||||
|
||||
|
||||
const createTRPC = <R>() => importTRPCServer.pipe(Effect.map(({ initTRPC }) =>
|
||||
initTRPC.context<TRPCContext<R>>().create()
|
||||
))
|
||||
|
||||
export const Identifier = "@thilalib/TRPC/TRPCBuilder"
|
||||
export interface TRPCBuilder<R> extends Context.Tag<typeof Identifier, TRPCBuilderService<R>> {}
|
||||
export interface TRPCBuilderService<R> extends Effect.Effect.Success<ReturnType<typeof createTRPC<R>>> {}
|
||||
|
||||
|
||||
export const make = <R>() => {
|
||||
const TRPCBuilder = Context.GenericTag<typeof Identifier, TRPCBuilderService<R>>(Identifier)
|
||||
const TRPCBuilderLive = Layer.effect(TRPCBuilder, createTRPC())
|
||||
|
||||
return { TRPCBuilder, TRPCBuilderLive } as const
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import type { TRPCError } from "@trpc/server"
|
||||
import { Data, type Effect, type Runtime } from "effect"
|
||||
import type { RuntimeFiber } from "effect/Fiber"
|
||||
import type express from "express"
|
||||
import type { IncomingMessage } from "node:http"
|
||||
import type { WebSocket } from "ws"
|
||||
|
||||
|
||||
export interface TRPCContext<R> extends TRPCContextRuntime<R>, TRPCContextTransaction {}
|
||||
|
||||
export interface TRPCContextRuntime<R> {
|
||||
readonly runtime: Runtime.Runtime<R>
|
||||
|
||||
readonly run: <A, E>(
|
||||
effect: Effect.Effect<A, E, R>,
|
||||
options?: { readonly signal?: AbortSignal },
|
||||
) => Promise<A>
|
||||
|
||||
readonly fork: <A, E>(
|
||||
effect: Effect.Effect<A, E, R>,
|
||||
options?: Runtime.RunForkOptions,
|
||||
) => RuntimeFiber<A, TRPCError>
|
||||
}
|
||||
|
||||
export interface TRPCContextTransaction {
|
||||
readonly transaction: TRPCTransaction
|
||||
}
|
||||
|
||||
|
||||
export type TRPCTransaction = Data.TaggedEnum<{
|
||||
readonly Express: {
|
||||
readonly req: express.Request
|
||||
readonly res: express.Response
|
||||
}
|
||||
|
||||
readonly WebSocket: {
|
||||
readonly req: IncomingMessage
|
||||
readonly res: WebSocket
|
||||
}
|
||||
}>
|
||||
|
||||
export const TRPCTransactionEnum = Data.taggedEnum<TRPCTransaction>()
|
||||
@@ -1,57 +0,0 @@
|
||||
import type { CreateExpressContextOptions } from "@trpc/server/adapters/express"
|
||||
import type { CreateWSSContextFnOptions } from "@trpc/server/adapters/ws"
|
||||
import { Context, Effect, Layer, Runtime } from "effect"
|
||||
import { createTRCPErrorMapper } from "./createTRCPErrorMapper"
|
||||
import { TRPCTransactionEnum, type TRPCContext, type TRPCTransaction } from "./TRPCContext"
|
||||
|
||||
|
||||
export const Identifier = "@thilalib/TRPC/TRPCContextCreator"
|
||||
|
||||
export interface TRPCContextCreator<R> extends Context.Tag<typeof Identifier, TRPCContextCreatorService<R>> {}
|
||||
|
||||
export interface TRPCContextCreatorService<R> {
|
||||
readonly createContext: (transaction: TRPCTransaction) => TRPCContext<R>
|
||||
readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext<R>
|
||||
readonly createWebSocketContext: (context: CreateWSSContextFnOptions) => TRPCContext<R>
|
||||
}
|
||||
|
||||
export const TRPCUnknownContextCreator = Context.GenericTag<typeof Identifier, TRPCContextCreatorService<unknown>>(Identifier)
|
||||
|
||||
|
||||
export const make = <R>() => {
|
||||
const TRPCContextCreator = Context.GenericTag<typeof Identifier, TRPCContextCreatorService<R>>(Identifier)
|
||||
|
||||
const TRPCContextCreatorLive = Layer.effect(TRPCContextCreator, Effect.gen(function*() {
|
||||
const runtime = yield* Effect.runtime<R>()
|
||||
const mapErrors = yield* createTRCPErrorMapper
|
||||
|
||||
const run = <A, E>(
|
||||
effect: Effect.Effect<A, E, R>,
|
||||
options?: { readonly signal?: AbortSignal },
|
||||
) => Runtime.runPromise(runtime)(
|
||||
effect.pipe(mapErrors),
|
||||
options,
|
||||
)
|
||||
|
||||
const fork = <A, E>(
|
||||
effect: Effect.Effect<A, E, R>,
|
||||
options?: Runtime.RunForkOptions,
|
||||
) => Runtime.runFork(runtime)(
|
||||
effect.pipe(mapErrors),
|
||||
options,
|
||||
)
|
||||
|
||||
const createContext = (transaction: TRPCTransaction) => ({
|
||||
runtime,
|
||||
run,
|
||||
fork,
|
||||
transaction,
|
||||
})
|
||||
const createExpressContext = (context: CreateExpressContextOptions) => createContext(TRPCTransactionEnum.Express(context))
|
||||
const createWebSocketContext = (context: CreateWSSContextFnOptions) => createContext(TRPCTransactionEnum.WebSocket(context))
|
||||
|
||||
return { createContext, createExpressContext, createWebSocketContext } as const
|
||||
}))
|
||||
|
||||
return { TRPCContextCreator, TRPCContextCreatorLive } as const
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Config, Effect, Layer } from "effect"
|
||||
import { ExpressApp } from "../Express"
|
||||
import { ImportError } from "../ImportError"
|
||||
import { TRPCUnknownContextCreator } from "./TRPCContextCreator"
|
||||
import { TRPCAnyRouter } from "./TRPCRouter"
|
||||
|
||||
|
||||
const importTRPCServerExpressAdapter = Effect.tryPromise({
|
||||
try: () => import("@trpc/server/adapters/express"),
|
||||
catch: cause => new ImportError({ path: "@trpc/server/adapters/express", cause }),
|
||||
})
|
||||
|
||||
export const TRPCExpressRouteLive = (
|
||||
config: {
|
||||
readonly root: Config.Config<string>
|
||||
}
|
||||
) => Layer.effectDiscard(Effect.gen(function*() {
|
||||
const { createExpressMiddleware } = yield* importTRPCServerExpressAdapter
|
||||
const app = yield* ExpressApp.ExpressApp
|
||||
|
||||
app.use(yield* config.root,
|
||||
createExpressMiddleware({
|
||||
router: yield* TRPCAnyRouter,
|
||||
createContext: (yield* TRPCUnknownContextCreator).createExpressContext,
|
||||
})
|
||||
)
|
||||
}))
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { AnyRouter } from "@trpc/server"
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
|
||||
|
||||
export const Identifier = "@thilalib/TRPC/TRPCRouter"
|
||||
export interface TRPCRouter<T extends AnyRouter> extends Context.Tag<typeof Identifier, T> {}
|
||||
|
||||
export const TRPCAnyRouter = Context.GenericTag<typeof Identifier, AnyRouter>(Identifier)
|
||||
|
||||
|
||||
export const make = <A extends AnyRouter, E, R>(
|
||||
router: Effect.Effect<A, E, R>
|
||||
) => {
|
||||
const TRPCRouter = Context.GenericTag<typeof Identifier, A>(Identifier)
|
||||
const TRPCRouterLive = Layer.effect(TRPCRouter, router)
|
||||
|
||||
return { TRPCRouter, TRPCRouterLive } as const
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import type { applyWSSHandler } from "@trpc/server/adapters/ws"
|
||||
import { Config, Context, Effect, Layer } from "effect"
|
||||
import type ws from "ws"
|
||||
import { ExpressNodeHTTPServer } from "../Express"
|
||||
import { ImportError } from "../ImportError"
|
||||
import { TRPCUnknownContextCreator } from "./TRPCContextCreator"
|
||||
import { TRPCAnyRouter } from "./TRPCRouter"
|
||||
|
||||
|
||||
export class TRPCWebSocketServer extends Context.Tag("@thilalib/TRPC/TRPCWebSocketServer")<TRPCWebSocketServer, TRPCWebSocketServerService>() {}
|
||||
|
||||
export interface TRPCWebSocketServerService {
|
||||
wss: ws.Server
|
||||
handler: ReturnType<typeof applyWSSHandler>
|
||||
}
|
||||
|
||||
|
||||
const importWS = Effect.tryPromise({
|
||||
try: () => import("ws"),
|
||||
catch: cause => new ImportError({ path: "ws", cause }),
|
||||
})
|
||||
|
||||
const importTRPCServerWSAdapter = Effect.tryPromise({
|
||||
try: () => import("@trpc/server/adapters/ws"),
|
||||
catch: cause => new ImportError({ path: "@trpc/server/adapters/ws", cause }),
|
||||
})
|
||||
|
||||
export const TRPCWebSocketServerLive = (
|
||||
config: {
|
||||
readonly host: Config.Config<string>
|
||||
}
|
||||
) => Layer.effect(TRPCWebSocketServer, Effect.gen(function*() {
|
||||
const { WebSocketServer } = yield* importWS
|
||||
const { applyWSSHandler } = yield* importTRPCServerWSAdapter
|
||||
|
||||
const host = yield* config.host
|
||||
|
||||
return yield* Effect.acquireRelease(
|
||||
Effect.gen(function*() {
|
||||
yield* Effect.logInfo(`WebSocket server started on ${ host }`)
|
||||
|
||||
const wss = new WebSocketServer({
|
||||
server: yield* ExpressNodeHTTPServer.ExpressNodeHTTPServer,
|
||||
host,
|
||||
})
|
||||
|
||||
return {
|
||||
wss,
|
||||
handler: applyWSSHandler({
|
||||
wss,
|
||||
router: yield* TRPCAnyRouter,
|
||||
createContext: (yield* TRPCUnknownContextCreator).createWebSocketContext,
|
||||
}),
|
||||
}
|
||||
}),
|
||||
|
||||
({ wss, handler }) => Effect.gen(function*() {
|
||||
yield* Effect.logInfo(`WebSocket server on ${ host } is stopping. Waiting for existing connections to end...`)
|
||||
|
||||
handler.broadcastReconnectNotification()
|
||||
yield* Effect.async(resume => {
|
||||
wss.close(() => resume(Effect.logInfo("WebSocket server closed")))
|
||||
})
|
||||
}),
|
||||
)
|
||||
}))
|
||||
@@ -1,63 +0,0 @@
|
||||
import { Effect, type Cause } from "effect"
|
||||
import { importTRPCServer } from "./importTRPCServer"
|
||||
|
||||
|
||||
export const createTRCPErrorMapper = importTRPCServer.pipe(Effect.map(({ TRPCError }) =>
|
||||
<A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.sandbox(effect).pipe(
|
||||
Effect.catchTags({
|
||||
Empty: cause => Effect.fail(
|
||||
new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
cause: new TRPCErrorCause(cause),
|
||||
})
|
||||
),
|
||||
|
||||
Fail: cause => Effect.fail(
|
||||
cause.error instanceof TRPCError
|
||||
? cause.error
|
||||
: new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
cause: new TRPCErrorCause(cause),
|
||||
})
|
||||
),
|
||||
|
||||
Die: cause => Effect.fail(
|
||||
cause.defect instanceof TRPCError
|
||||
? cause.defect
|
||||
: new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
cause: new TRPCErrorCause(cause),
|
||||
})
|
||||
),
|
||||
|
||||
Interrupt: cause => Effect.fail(
|
||||
new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
cause: new TRPCErrorCause(cause),
|
||||
})
|
||||
),
|
||||
|
||||
Sequential: cause => Effect.fail(
|
||||
new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
cause: new TRPCErrorCause(cause),
|
||||
})
|
||||
),
|
||||
|
||||
Parallel: cause => Effect.fail(
|
||||
new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
cause: new TRPCErrorCause(cause),
|
||||
})
|
||||
),
|
||||
}),
|
||||
|
||||
Effect.tapError(Effect.logError),
|
||||
)
|
||||
))
|
||||
|
||||
class TRPCErrorCause<E> extends Error {
|
||||
constructor(readonly cause: Cause.Cause<E>) {
|
||||
super()
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { Config, Effect, Layer } from "effect"
|
||||
import * as TRPC from "."
|
||||
import { Express, JSONWebToken } from ".."
|
||||
|
||||
|
||||
// Context available to the router procedures
|
||||
type Services =
|
||||
| JSONWebToken.JSONWebToken
|
||||
|
||||
const ServicesLive = Layer.empty.pipe(
|
||||
Layer.provideMerge(JSONWebToken.JSONWebTokenLive)
|
||||
)
|
||||
|
||||
|
||||
const { TRPCContextCreator, TRPCContextCreatorLive } = TRPC.TRPCContextCreator.make<Services>()
|
||||
const { TRPCBuilder, TRPCBuilderLive } = TRPC.TRPCBuilder.make<Services>()
|
||||
|
||||
|
||||
const router = Effect.gen(function*() {
|
||||
const t = yield* TRPCBuilder
|
||||
|
||||
return t.router({
|
||||
ping: t.procedure.query(({ ctx }) => ctx.run(
|
||||
Effect.succeed("pong")
|
||||
)),
|
||||
|
||||
expressOnlyProcedure: t.procedure
|
||||
.use(yield* TRPC.ExpressOnly)
|
||||
.query(({ ctx }) => ctx.run(Effect.gen(function*() {
|
||||
ctx.transaction
|
||||
}))),
|
||||
})
|
||||
})
|
||||
|
||||
const { TRPCRouter, TRPCRouterLive } = TRPC.TRPCRouter.make(router)
|
||||
|
||||
|
||||
const ServerLive = Layer.empty.pipe(
|
||||
Layer.provideMerge(TRPC.TRPCExpressRoute.TRPCExpressRouteLive({
|
||||
root: Config.succeed("/rpc")
|
||||
})),
|
||||
Layer.provideMerge(TRPC.TRPCWebSocketServer.TRPCWebSocketServerLive({
|
||||
host: Config.succeed("/rpc")
|
||||
})),
|
||||
|
||||
Layer.provideMerge(TRPCRouterLive),
|
||||
Layer.provideMerge(TRPCBuilderLive),
|
||||
Layer.provideMerge(TRPCContextCreatorLive),
|
||||
|
||||
Layer.provideMerge(Express.ExpressNodeHTTPServer.ExpressNodeHTTPServerLive({
|
||||
port: Config.succeed(3000)
|
||||
})),
|
||||
Layer.provideMerge(Express.ExpressApp.ExpressAppLive())
|
||||
)
|
||||
|
||||
await Effect.gen(function*() {
|
||||
return yield* Layer.launch(ServerLive)
|
||||
}).pipe(
|
||||
Effect.provide(ServicesLive),
|
||||
Effect.scoped,
|
||||
Effect.runPromise,
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Effect } from "effect"
|
||||
import { ImportError } from "../ImportError"
|
||||
|
||||
|
||||
export const importTRPCServer: Effect.Effect<
|
||||
typeof import("@trpc/server"),
|
||||
ImportError
|
||||
> = Effect.tryPromise({
|
||||
try: () => import("@trpc/server"),
|
||||
catch: cause => new ImportError({ path: "@trpc/server", cause }),
|
||||
})
|
||||
@@ -1,7 +0,0 @@
|
||||
export * from "./middlewares"
|
||||
export * as TRPCBuilder from "./TRPCBuilder"
|
||||
export * as TRPCContext from "./TRPCContext"
|
||||
export * as TRPCContextCreator from "./TRPCContextCreator"
|
||||
export * as TRPCExpressRoute from "./TRPCExpressRoute"
|
||||
export * as TRPCRouter from "./TRPCRouter"
|
||||
export * as TRPCWebSocketServer from "./TRPCWebSocketServer"
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Schema } from "@effect/schema"
|
||||
import type { ParseOptions } from "@effect/schema/AST"
|
||||
import { Effect } from "effect"
|
||||
import { importTRPCServer } from "../importTRPCServer"
|
||||
import type { TRPCContextRuntime } from "../TRPCContext"
|
||||
|
||||
|
||||
export const DecodeInput = <A, I>(
|
||||
schema: Schema.Schema<A, I>,
|
||||
options?: ParseOptions,
|
||||
) => Effect.gen(function*() {
|
||||
const { experimental_standaloneMiddleware, TRPCError } = yield* importTRPCServer
|
||||
|
||||
const decode = (value: I) => Schema.decode(schema, options)(value).pipe(
|
||||
Effect.matchEffect({
|
||||
onSuccess: Effect.succeed,
|
||||
onFailure: e => Effect.fail(new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Could not decode input",
|
||||
cause: e,
|
||||
})),
|
||||
})
|
||||
)
|
||||
|
||||
return experimental_standaloneMiddleware<{
|
||||
ctx: TRPCContextRuntime<never>
|
||||
input: I
|
||||
}>().create(
|
||||
async ({ ctx, input, next }) => next({
|
||||
ctx: { decodedInput: await ctx.run(decode(input)) } as const
|
||||
})
|
||||
)
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Effect, identity, Match } from "effect"
|
||||
import { importTRPCServer } from "../importTRPCServer"
|
||||
import type { TRPCContextTransaction } from "../TRPCContext"
|
||||
|
||||
|
||||
export const ExpressOnly = importTRPCServer.pipe(Effect.map(({
|
||||
experimental_standaloneMiddleware,
|
||||
TRPCError,
|
||||
}) => experimental_standaloneMiddleware<{
|
||||
ctx: TRPCContextTransaction
|
||||
}>().create(({ ctx, next }) => next({
|
||||
ctx: {
|
||||
transaction: Match.value(ctx.transaction).pipe(
|
||||
Match.tag("Express", identity),
|
||||
|
||||
Match.orElse(() => {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Only Express transport is supported by this procedure",
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}))))
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Effect, identity, Match } from "effect"
|
||||
import { importTRPCServer } from "../importTRPCServer"
|
||||
import type { TRPCContextTransaction } from "../TRPCContext"
|
||||
|
||||
|
||||
export const WebSocketOnly = importTRPCServer.pipe(Effect.map(({
|
||||
experimental_standaloneMiddleware,
|
||||
TRPCError,
|
||||
}) => experimental_standaloneMiddleware<{
|
||||
ctx: TRPCContextTransaction
|
||||
}>().create(({ ctx, next }) => next({
|
||||
ctx: {
|
||||
transaction: Match.value(ctx.transaction).pipe(
|
||||
Match.tag("WebSocket", identity),
|
||||
|
||||
Match.orElse(() => {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Only WebSocket transport is supported by this procedure",
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}))))
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from "./DecodeInput"
|
||||
export * from "./ExpressOnly"
|
||||
export * from "./WebSocketOnly"
|
||||
@@ -1,7 +1,3 @@
|
||||
export * as Express from "./Express"
|
||||
export * from "./ImportError"
|
||||
export * as JSONWebToken from "./JSONWebToken"
|
||||
export * as OpenAIClient from "./OpenAIClient"
|
||||
export * as Layers from "./Layers"
|
||||
export * as Schema from "./Schema"
|
||||
export * as TRPC from "./TRPC"
|
||||
export * as Types from "./Types"
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
// "allowJs": true,
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
// "allowImportingTsExtensions": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
// "noEmit": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
@@ -22,12 +22,6 @@
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
|
||||
// Build
|
||||
"outDir": "./dist",
|
||||
"declaration": true
|
||||
},
|
||||
|
||||
"include": ["./src"]
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,12 @@ import { defineConfig } from "tsup"
|
||||
export default defineConfig({
|
||||
entry: [
|
||||
"./src/index.ts",
|
||||
"./src/Express/index.ts",
|
||||
"./src/TRPC/index.ts",
|
||||
"./src/Types/index.ts",
|
||||
"./src/Layers/index.ts",
|
||||
"./src/Schema/index.ts",
|
||||
"./src/Schema/MobX/index.ts",
|
||||
"./src/Schema/TanStackForm/index.ts",
|
||||
"./src/JSONWebToken.ts",
|
||||
"./src/OpenAIClient.ts",
|
||||
"./src/Types/index.ts",
|
||||
],
|
||||
format: ["esm"],
|
||||
format: ["esm", "cjs"],
|
||||
skipNodeModulesBundle: true,
|
||||
dts: true,
|
||||
splitting: true,
|
||||
|
||||
Reference in New Issue
Block a user