0.1.19 (#20)
All checks were successful
Publish / publish (push) Successful in 12s
Lint / lint (push) Successful in 11s

Co-authored-by: Julien Valverdé <julien.valverde@mailo.com>
Reviewed-on: #20
This commit was merged in pull request #20.
This commit is contained in:
Julien Valverdé
2024-09-08 01:42:56 +02:00
parent ebc5b45380
commit 704aa945f7
11 changed files with 100 additions and 60 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@thilawyn/thilalib", "name": "@thilawyn/thilalib",
"version": "0.1.18", "version": "0.1.19",
"type": "module", "type": "module",
"files": [ "files": [
"./dist" "./dist"

View File

@@ -3,12 +3,12 @@ import { makeObservable, observable, type CreateObservableOptions } from "mobx"
import { mapValues } from "remeda" import { mapValues } from "remeda"
interface ObservableClassSelf { export interface ObservableClassSelf {
new(...args: any[]): Schema.Struct.Type<Schema.Struct.Fields> new(...args: any[]): Schema.Struct.Type<Schema.Struct.Fields>
readonly fields: { readonly [K in keyof Schema.Struct.Fields]: Schema.Struct.Fields[K] } readonly fields: { readonly [K in keyof Schema.Struct.Fields]: Schema.Struct.Fields[K] }
} }
interface ObservableClassOptions extends Omit<CreateObservableOptions, "proxy"> {} export interface ObservableClassOptions extends Omit<CreateObservableOptions, "proxy"> {}
export const ObservableClass = (options?: ObservableClassOptions) => export const ObservableClass = (options?: ObservableClassOptions) =>
<Self extends ObservableClassSelf>(self: Self) => <Self extends ObservableClassSelf>(self: Self) =>

View File

@@ -1,11 +1,11 @@
import { Schema } from "@effect/schema" import { Schema } from "@effect/schema"
export const Email = Schema.pattern( export const email = Schema.pattern(
/^(?!\.)(?!.*\.\.)([A-Z0-9_+-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i, /^(?!\.)(?!.*\.\.)([A-Z0-9_+-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i,
{ {
identifier: "Email", identifier: "email",
title: "email", title: "email",
message: () => "Not an email address", message: () => "Not an email address",
}, },

View File

@@ -1,6 +1,6 @@
export * from "./Class" export * from "./Class"
export * from "./DateTime" export * from "./DateTime"
export * from "./Email" export * from "./email"
export * from "./encodedAsPrismaJsonValue" export * from "./encodedAsPrismaJsonValue"
export * from "./Kind" export * from "./Kind"
export * as MobX from "./MobX" export * as MobX from "./MobX"

View File

@@ -6,7 +6,9 @@ import type { IncomingMessage } from "node:http"
import type { WebSocket } from "ws" import type { WebSocket } from "ws"
export interface TRPCContext<R> { export interface TRPCContext<R> extends TRPCContextRuntime<R>, TRPCContextTransaction {}
export interface TRPCContextRuntime<R> {
readonly runtime: Runtime.Runtime<R> readonly runtime: Runtime.Runtime<R>
readonly run: <A, E>( readonly run: <A, E>(
@@ -18,12 +20,14 @@ export interface TRPCContext<R> {
effect: Effect.Effect<A, E, R>, effect: Effect.Effect<A, E, R>,
options?: Runtime.RunForkOptions, options?: Runtime.RunForkOptions,
) => RuntimeFiber<A, TRPCError> ) => RuntimeFiber<A, TRPCError>
}
readonly transaction: TRPCContextTransaction export interface TRPCContextTransaction {
readonly transaction: TRPCTransaction
} }
export type TRPCContextTransaction = Data.TaggedEnum<{ export type TRPCTransaction = Data.TaggedEnum<{
readonly Express: { readonly Express: {
readonly req: express.Request readonly req: express.Request
readonly res: express.Response readonly res: express.Response
@@ -35,4 +39,4 @@ export type TRPCContextTransaction = Data.TaggedEnum<{
} }
}> }>
export const TRPCContextTransactionEnum = Data.taggedEnum<TRPCContextTransaction>() export const TRPCTransactionEnum = Data.taggedEnum<TRPCTransaction>()

View File

@@ -2,7 +2,7 @@ import type { CreateExpressContextOptions } from "@trpc/server/adapters/express"
import type { CreateWSSContextFnOptions } from "@trpc/server/adapters/ws" import type { CreateWSSContextFnOptions } from "@trpc/server/adapters/ws"
import { Context, Effect, Layer, Runtime } from "effect" import { Context, Effect, Layer, Runtime } from "effect"
import { createTRCPErrorMapper } from "./createTRCPErrorMapper" import { createTRCPErrorMapper } from "./createTRCPErrorMapper"
import { TRPCContextTransactionEnum, type TRPCContext, type TRPCContextTransaction } from "./TRPCContext" import { TRPCTransactionEnum, type TRPCContext, type TRPCTransaction } from "./TRPCContext"
export const Identifier = "@thilalib/TRPC/TRPCContextCreator" export const Identifier = "@thilalib/TRPC/TRPCContextCreator"
@@ -10,7 +10,7 @@ export const Identifier = "@thilalib/TRPC/TRPCContextCreator"
export interface TRPCContextCreator<R> extends Context.Tag<typeof Identifier, TRPCContextCreatorService<R>> {} export interface TRPCContextCreator<R> extends Context.Tag<typeof Identifier, TRPCContextCreatorService<R>> {}
export interface TRPCContextCreatorService<R> { export interface TRPCContextCreatorService<R> {
readonly createContext: (transaction: TRPCContextTransaction) => TRPCContext<R> readonly createContext: (transaction: TRPCTransaction) => TRPCContext<R>
readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext<R> readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext<R>
readonly createWebSocketContext: (context: CreateWSSContextFnOptions) => TRPCContext<R> readonly createWebSocketContext: (context: CreateWSSContextFnOptions) => TRPCContext<R>
} }
@@ -41,16 +41,16 @@ export const make = <R>() => {
options, options,
) )
const createContext = (transaction: TRPCContextTransaction) => ({ const createContext = (transaction: TRPCTransaction) => ({
runtime, runtime,
run, run,
fork, fork,
transaction, transaction,
}) })
const createExpressContext = (context: CreateExpressContextOptions) => createContext(TRPCContextTransactionEnum.Express(context)) const createExpressContext = (context: CreateExpressContextOptions) => createContext(TRPCTransactionEnum.Express(context))
const createWebSocketContext = (context: CreateWSSContextFnOptions) => createContext(TRPCContextTransactionEnum.WebSocket(context)) const createWebSocketContext = (context: CreateWSSContextFnOptions) => createContext(TRPCTransactionEnum.WebSocket(context))
return { createContext, createExpressContext, createWebSocketContext } return { createContext, createExpressContext, createWebSocketContext } as const
})) }))
return { TRPCContextCreator, TRPCContextCreatorLive } as const return { TRPCContextCreator, TRPCContextCreatorLive } as const

View File

@@ -1,44 +0,0 @@
import { Effect, Match } from "effect"
import type { TRPCContextTransaction } from "./TRPCContext"
import { importTRPCServer } from "./importTRPCServer"
export const ExpressOnly = importTRPCServer.pipe(Effect.map(({
experimental_standaloneMiddleware,
TRPCError,
}) => experimental_standaloneMiddleware<{
ctx: { readonly transaction: TRPCContextTransaction }
}>().create(opts =>
Match.value(opts.ctx.transaction).pipe(
Match.tag("Express", transaction =>
opts.next({ ctx: { transaction } })
),
Match.orElse(() => {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Only Express transport is supported by this procedure",
})
}),
)
)))
export const WebSocketOnly = importTRPCServer.pipe(Effect.map(({
experimental_standaloneMiddleware,
TRPCError,
}) => experimental_standaloneMiddleware<{
ctx: { readonly transaction: TRPCContextTransaction }
}>().create(opts =>
Match.value(opts.ctx.transaction).pipe(
Match.tag("WebSocket", transaction =>
opts.next({ ctx: { transaction } })
),
Match.orElse(() => {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Only WebSocket transport is supported by this procedure",
})
}),
)
)))

View File

@@ -0,0 +1,33 @@
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<any>
input: I
}>().create(
async ({ ctx, input, next }) => next({
ctx: { decodedInput: await ctx.run(decode(input)) } as const
})
)
})

View File

@@ -0,0 +1,22 @@
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: 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",
})
}),
)
}))))

View File

@@ -0,0 +1,22 @@
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: 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",
})
}),
)
}))))

View File

@@ -0,0 +1,3 @@
export * from "./DecodeInput"
export * from "./ExpressOnly"
export * from "./WebSocketOnly"