@gqloom/core 0.2.0 → 0.2.2
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 +18 -12
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/package.json +10 -2
- package/README.zh-CN.md +0 -267
package/README.md
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# GQLoom
|
|
4
4
|
|
|
5
|
-
GQLoom is a GraphQL weaver for TypeScript/JavaScript
|
|
5
|
+
GQLoom is a GraphQL weaver for TypeScript/JavaScript that weaves GraphQL Schema using Valibot, Zod, or Yup, and supports sophisticated type inference to provide the best development experience.
|
|
6
6
|
|
|
7
|
-
GQLoom is inspired by [tRPC](https://trpc.io/), [TypeGraphQL](https://typegraphql.com/),
|
|
7
|
+
The design of GQLoom is inspired by [tRPC](https://trpc.io/), [TypeGraphQL](https://typegraphql.com/), [Pothos](https://pothos-graphql.dev/).
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
- 🔒
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
11
|
+
- 🚀 GraphQL: flexible and efficient, reducing redundant data transfers;
|
|
12
|
+
- 🔒 Robust type safety: enjoy smart hints during development and spot potential problems during editing;
|
|
13
|
+
- 🔋 Ready to go: middleware, contexts, subscriptions, federated graphs are ready to go;
|
|
14
|
+
- 🔮 No extra magic: no decorators, no metadata and reflection, no code generation, you just need JavaScript/TypeScript;
|
|
15
|
+
- 🧩 Familiar schema libraries: use the schema libraries you already know (Zod, Yup, Valibot) to build GraphQL Schema and validate inputs;
|
|
16
|
+
- 🧑💻 Develop happily: highly readable and semantic APIs designed to keep your code tidy;
|
|
16
17
|
|
|
17
|
-
## Hello
|
|
18
|
+
## Hello World
|
|
18
19
|
|
|
19
20
|
```ts
|
|
20
|
-
import { weave } from "@gqloom/
|
|
21
|
-
import { resolver, query } from "@gqloom/valibot"
|
|
21
|
+
import { resolver, query, weave } from "@gqloom/valibot"
|
|
22
22
|
import * as v from "valibot"
|
|
23
23
|
|
|
24
24
|
const HelloResolver = resolver({
|
|
@@ -27,3 +27,9 @@ const HelloResolver = resolver({
|
|
|
27
27
|
|
|
28
28
|
export const schema = weave(HelloResolver)
|
|
29
29
|
```
|
|
30
|
+
|
|
31
|
+
Read [Introduction](https://gqloom.dev/guide/introduction.html) to learn more about GQLoom.
|
|
32
|
+
|
|
33
|
+
## Getting Started
|
|
34
|
+
|
|
35
|
+
See [Getting Started](https://gqloom.dev/guide/getting-started.html) to learn how to use GQLoom.
|
package/dist/index.d.cts
CHANGED
|
@@ -205,9 +205,13 @@ declare function getSubscriptionOptions(subscribeOrOptions: (() => any) | Subscr
|
|
|
205
205
|
declare function getFieldOptions({ description, deprecationReason, extensions, }: GraphQLFieldOptions): GraphQLFieldOptions;
|
|
206
206
|
|
|
207
207
|
interface MiddlewarePayload<TField extends GenericFieldOrOperation = FieldOrOperation<any, any, any, any>> {
|
|
208
|
+
/** The Output Silk of the field */
|
|
208
209
|
outputSilk: InferSilkO<InferFieldOutput<TField>>;
|
|
210
|
+
/** The previous object, which for a field on the root Query type is often not used. */
|
|
209
211
|
parent: TField extends FieldOrOperation<infer TParent, any, any, any> ? TParent extends undefined ? undefined : InferSilkO<NonNullable<TParent>> : never;
|
|
212
|
+
/** A function to parse the input of the field */
|
|
210
213
|
parseInput: TField extends FieldOrOperation<any, any, infer TInput, any> ? CallableInputParser<TInput> : undefined;
|
|
214
|
+
/** The type of the field: `query`, `mutation`, `subscription` or `field` */
|
|
211
215
|
type: FieldOrOperationType;
|
|
212
216
|
}
|
|
213
217
|
type Middleware<TField extends GenericFieldOrOperation = FieldOrOperation<any, any, any, any>> = (next: () => MayPromise<InferSilkO<InferFieldOutput<TField>>>, payload: MiddlewarePayload<TField>) => MayPromise<InferSilkO<InferFieldOutput<TField>>>;
|
|
@@ -223,7 +227,7 @@ interface ResolverPayload<TContext extends object = object, TField extends Field
|
|
|
223
227
|
*/
|
|
224
228
|
readonly root: any;
|
|
225
229
|
/**
|
|
226
|
-
* The
|
|
230
|
+
* The arguments provided to the field in the GraphQL query.
|
|
227
231
|
*/
|
|
228
232
|
readonly args: Record<string, any>;
|
|
229
233
|
/**
|
|
@@ -231,7 +235,7 @@ interface ResolverPayload<TContext extends object = object, TField extends Field
|
|
|
231
235
|
*/
|
|
232
236
|
readonly context: TContext;
|
|
233
237
|
/**
|
|
234
|
-
*
|
|
238
|
+
* A custom object each resolver can read from/write to.
|
|
235
239
|
*/
|
|
236
240
|
readonly info: GraphQLResolveInfo;
|
|
237
241
|
/**
|
|
@@ -557,7 +561,7 @@ declare function inputToArgs(input: InputSchema<GraphQLSilk>): GraphQLFieldConfi
|
|
|
557
561
|
declare function ensureInputType(silkOrType: GraphQLType | GraphQLSilk): GraphQLInputType;
|
|
558
562
|
declare function ensureInputObjectType(object: GraphQLObjectType | GraphQLInterfaceType | GraphQLInputObjectType): GraphQLInputObjectType;
|
|
559
563
|
|
|
560
|
-
declare function ensureInterfaceType(gqlType: GraphQLOutputType, interfaceConfig?: GraphQLInterfaceTypeConfig<any, any
|
|
564
|
+
declare function ensureInterfaceType(gqlType: GraphQLOutputType, interfaceConfig?: Partial<GraphQLInterfaceTypeConfig<any, any>>): GraphQLInterfaceType;
|
|
561
565
|
|
|
562
566
|
interface GQLoomExtensions {
|
|
563
567
|
defaultValue?: any;
|
package/dist/index.d.ts
CHANGED
|
@@ -205,9 +205,13 @@ declare function getSubscriptionOptions(subscribeOrOptions: (() => any) | Subscr
|
|
|
205
205
|
declare function getFieldOptions({ description, deprecationReason, extensions, }: GraphQLFieldOptions): GraphQLFieldOptions;
|
|
206
206
|
|
|
207
207
|
interface MiddlewarePayload<TField extends GenericFieldOrOperation = FieldOrOperation<any, any, any, any>> {
|
|
208
|
+
/** The Output Silk of the field */
|
|
208
209
|
outputSilk: InferSilkO<InferFieldOutput<TField>>;
|
|
210
|
+
/** The previous object, which for a field on the root Query type is often not used. */
|
|
209
211
|
parent: TField extends FieldOrOperation<infer TParent, any, any, any> ? TParent extends undefined ? undefined : InferSilkO<NonNullable<TParent>> : never;
|
|
212
|
+
/** A function to parse the input of the field */
|
|
210
213
|
parseInput: TField extends FieldOrOperation<any, any, infer TInput, any> ? CallableInputParser<TInput> : undefined;
|
|
214
|
+
/** The type of the field: `query`, `mutation`, `subscription` or `field` */
|
|
211
215
|
type: FieldOrOperationType;
|
|
212
216
|
}
|
|
213
217
|
type Middleware<TField extends GenericFieldOrOperation = FieldOrOperation<any, any, any, any>> = (next: () => MayPromise<InferSilkO<InferFieldOutput<TField>>>, payload: MiddlewarePayload<TField>) => MayPromise<InferSilkO<InferFieldOutput<TField>>>;
|
|
@@ -223,7 +227,7 @@ interface ResolverPayload<TContext extends object = object, TField extends Field
|
|
|
223
227
|
*/
|
|
224
228
|
readonly root: any;
|
|
225
229
|
/**
|
|
226
|
-
* The
|
|
230
|
+
* The arguments provided to the field in the GraphQL query.
|
|
227
231
|
*/
|
|
228
232
|
readonly args: Record<string, any>;
|
|
229
233
|
/**
|
|
@@ -231,7 +235,7 @@ interface ResolverPayload<TContext extends object = object, TField extends Field
|
|
|
231
235
|
*/
|
|
232
236
|
readonly context: TContext;
|
|
233
237
|
/**
|
|
234
|
-
*
|
|
238
|
+
* A custom object each resolver can read from/write to.
|
|
235
239
|
*/
|
|
236
240
|
readonly info: GraphQLResolveInfo;
|
|
237
241
|
/**
|
|
@@ -557,7 +561,7 @@ declare function inputToArgs(input: InputSchema<GraphQLSilk>): GraphQLFieldConfi
|
|
|
557
561
|
declare function ensureInputType(silkOrType: GraphQLType | GraphQLSilk): GraphQLInputType;
|
|
558
562
|
declare function ensureInputObjectType(object: GraphQLObjectType | GraphQLInterfaceType | GraphQLInputObjectType): GraphQLInputObjectType;
|
|
559
563
|
|
|
560
|
-
declare function ensureInterfaceType(gqlType: GraphQLOutputType, interfaceConfig?: GraphQLInterfaceTypeConfig<any, any
|
|
564
|
+
declare function ensureInterfaceType(gqlType: GraphQLOutputType, interfaceConfig?: Partial<GraphQLInterfaceTypeConfig<any, any>>): GraphQLInterfaceType;
|
|
561
565
|
|
|
562
566
|
interface GQLoomExtensions {
|
|
563
567
|
defaultValue?: any;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gqloom/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Create GraphQL schema and resolvers with TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,7 +26,15 @@
|
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"graphql": ">= 16.8.0"
|
|
28
28
|
},
|
|
29
|
-
"keywords": [
|
|
29
|
+
"keywords": [
|
|
30
|
+
"gqloom",
|
|
31
|
+
"graphql",
|
|
32
|
+
"schema",
|
|
33
|
+
"typescript",
|
|
34
|
+
"valibot",
|
|
35
|
+
"zod",
|
|
36
|
+
"yup"
|
|
37
|
+
],
|
|
30
38
|
"author": "xcfox",
|
|
31
39
|
"license": "MIT",
|
|
32
40
|
"publishConfig": {
|
package/README.zh-CN.md
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
# [GQLoom](../../README.zh-CN.md)
|
|
2
|
-
|
|
3
|
-
[English](./README.md) | 简体中文
|
|
4
|
-
|
|
5
|
-
[GQLoom](../../README.zh-CN.md) 是一个用于 TypeScript/JavaScript 的 GraphQL 编织器,使用 Zod、Yup 或者 Valibot 来愉快地编织 GraphQL Schema, 支持完善的类型推断以提供最好的开发体验。
|
|
6
|
-
|
|
7
|
-
## 你好,世界!
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
import { weave, loom, silk } from "@gqloom/core"
|
|
11
|
-
import { GraphQLString } from "graphql"
|
|
12
|
-
|
|
13
|
-
const HelloResolver = loom.resolver({
|
|
14
|
-
hello: loom.query(silk(GraphQLString), () => "world"),
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
export const schema = weave(HelloResolver)
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
# GQLoom Core
|
|
21
|
-
|
|
22
|
-
GQLoom Core 是 GQLoom 的核心库,提供了 GQLoom 的基础功能。
|
|
23
|
-
|
|
24
|
-
## 概念
|
|
25
|
-
|
|
26
|
-
GQLoom 意为 GraphQL 的织布机(Loom),其核心理念是从各式Schema 编织出 GraphQL Schema。
|
|
27
|
-
|
|
28
|
-
### 丝线|Silk
|
|
29
|
-
|
|
30
|
-
丝线是 GQLoom 的基础单位,它同时反应 GraphQL 类型和 TypeScript 类型。
|
|
31
|
-
我们可以通过 `silk` 函数创建丝线:
|
|
32
|
-
|
|
33
|
-
##### 简单的标量丝线
|
|
34
|
-
|
|
35
|
-
```ts
|
|
36
|
-
import { silk } from "@gqloom/core"
|
|
37
|
-
import { GraphQLString, GraphQLInt } from "graphql"
|
|
38
|
-
|
|
39
|
-
const StringSilk = silk(GraphQLString)
|
|
40
|
-
const IntSilk = silk(GraphQLInt)
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
##### 携带类型的对象丝线
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
import { silk } from "@gqloom/core"
|
|
47
|
-
import {
|
|
48
|
-
GraphQLObjectType,
|
|
49
|
-
GraphQLNonNull,
|
|
50
|
-
GraphQLString,
|
|
51
|
-
GraphQLInt,
|
|
52
|
-
} from "graphql"
|
|
53
|
-
|
|
54
|
-
interface ICat {
|
|
55
|
-
name: string
|
|
56
|
-
age: number
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// 这里我们使用 `silk`的泛型参数将 Cat 的类型标记为 ICat
|
|
60
|
-
export const Cat = silk<ICat>(
|
|
61
|
-
new GraphQLObjectType({
|
|
62
|
-
name: "Cat",
|
|
63
|
-
fields: {
|
|
64
|
-
name: { type: new GraphQLNonNull(GraphQLString) },
|
|
65
|
-
age: { type: new GraphQLNonNull(GraphQLInt) },
|
|
66
|
-
},
|
|
67
|
-
})
|
|
68
|
-
)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
直接使用 [graphql.js](https://graphql.org/graphql-js/constructing-types/) 构造 GraphQL 类型可能导致冗长的代码,并且需要手动为丝线添加类型声明。
|
|
72
|
-
使用 Zod、Valibot 可以更轻松地创建丝线。
|
|
73
|
-
|
|
74
|
-
##### 使用 Zod 创建丝线
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
import { zodSilk } from "@gqloom/zod"
|
|
78
|
-
import { z } from "zod"
|
|
79
|
-
|
|
80
|
-
const Cat = zodSilk(
|
|
81
|
-
z.object({
|
|
82
|
-
__typename: z.literal("Cat").nullish(), // 定义 GraphQL Object 的名称
|
|
83
|
-
name: z.string(),
|
|
84
|
-
age: z.number(),
|
|
85
|
-
})
|
|
86
|
-
)
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
##### 使用 Valibot 创建丝线
|
|
90
|
-
|
|
91
|
-
```ts
|
|
92
|
-
import { valibotSilk } from "@gqloom/valibot"
|
|
93
|
-
import * as v from "valibot"
|
|
94
|
-
|
|
95
|
-
const Cat = valibotSilk(
|
|
96
|
-
v.object({
|
|
97
|
-
__typename: v.nullish(v.literal("Cat")), // 定义 GraphQL Object 的名称
|
|
98
|
-
name: v.string(),
|
|
99
|
-
age: v.number(),
|
|
100
|
-
})
|
|
101
|
-
)
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### 解析器|Resolver
|
|
105
|
-
|
|
106
|
-
解析器是放置 GraphQL 操作(Query、Mutation、Subscription)的地方,GQLoom 会将各个解析器汇总编织成 GraphQL Schema。
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
import { loom, silk } from "@gqloom/core"
|
|
110
|
-
import { GraphQLString } from "graphql"
|
|
111
|
-
|
|
112
|
-
const { resolver, query, mutation } = loom
|
|
113
|
-
|
|
114
|
-
const HelloResolver = resolver({
|
|
115
|
-
hello: query(silk(GraphQLString), () => "world"),
|
|
116
|
-
bye: mutation(silk(GraphQLString), {
|
|
117
|
-
description: "say bye",
|
|
118
|
-
resolve: () => "see you later",
|
|
119
|
-
}),
|
|
120
|
-
})
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
解析器内可以存放多个操作。操作通过 `query`、`mutation`或`subscription` 函数创建,它们接受两个参数:
|
|
124
|
-
|
|
125
|
-
- 1. 丝线:定义了操作返回值的类型;
|
|
126
|
-
- 2. 解析函数以及更多配置:定义了操作的具体逻辑、输入类型及其他配置。
|
|
127
|
-
|
|
128
|
-
#### 带输入的解析操作
|
|
129
|
-
|
|
130
|
-
为 GraphQL 操作添加输入类型,只需要在解析函数中添加一个 `input` 参数即可。
|
|
131
|
-
|
|
132
|
-
```ts
|
|
133
|
-
import { loom, silk } from "@gqloom/core"
|
|
134
|
-
import { GraphQLString } from "graphql"
|
|
135
|
-
|
|
136
|
-
const { resolver, query } = loom
|
|
137
|
-
|
|
138
|
-
const GreetingResolver = resolver({
|
|
139
|
-
greet: query(silk(GraphQLString), {
|
|
140
|
-
input: { name: silk(GraphQLString) },
|
|
141
|
-
resolve: ({ name }) => `Hello, ${name}!`,
|
|
142
|
-
}),
|
|
143
|
-
})
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
在以上代码中,我们定义了一个 `greet` 操作,它接受一个 `name` 作为输入,然后我们可以在 `resolve` 函数中轻松获取到 `name` 的值。
|
|
147
|
-
|
|
148
|
-
#### 为对象添加更多字段
|
|
149
|
-
|
|
150
|
-
我们可以使用 `field` 函数在解析器内为对象添加额外的字段。
|
|
151
|
-
|
|
152
|
-
```ts
|
|
153
|
-
import { loom, silk } from "@gqloom/core"
|
|
154
|
-
import { GraphQLInt } from "graphql"
|
|
155
|
-
import { Cat } from "./schemas"
|
|
156
|
-
|
|
157
|
-
const CatResolver = resolver.of(Cat, {
|
|
158
|
-
cat: query(Cat, () => ({
|
|
159
|
-
name: "Tom",
|
|
160
|
-
birthday: "2020-01-01",
|
|
161
|
-
})),
|
|
162
|
-
|
|
163
|
-
// 我们可以在解析函数的第一个参数获取到 `Cat` 实例的值
|
|
164
|
-
age: field(silk(GraphQLInt), (cat) => {
|
|
165
|
-
return new Date().getFullYear() - new Date(cat.birthday).getFullYear()
|
|
166
|
-
}),
|
|
167
|
-
|
|
168
|
-
ageAt: field(silk(GraphQLInt), {
|
|
169
|
-
input: { year: silk(GraphQLInt) },
|
|
170
|
-
// 当字段包含输入时,我们可以在第二个参数中获取到输入的值
|
|
171
|
-
resolve: (cat, { year }) => {
|
|
172
|
-
return year - new Date(cat.birthday).getFullYear()
|
|
173
|
-
},
|
|
174
|
-
}),
|
|
175
|
-
|
|
176
|
-
// 在对象之间建立关联
|
|
177
|
-
friend: field(Cat, () => ({
|
|
178
|
-
name: "Jerry",
|
|
179
|
-
birthday: "2020-01-01",
|
|
180
|
-
})),
|
|
181
|
-
})
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### 编织器|Weaver
|
|
185
|
-
|
|
186
|
-
使用 `weave` 函数将解析器编织成 GraphQL Schema。
|
|
187
|
-
`weave` 能够接受解析器、丝线、中间件、各式配置作为输入,将在之后介绍。
|
|
188
|
-
|
|
189
|
-
```ts
|
|
190
|
-
import { weave } from "@gqloom/core"
|
|
191
|
-
import { HelloResolver } from "./resolvers"
|
|
192
|
-
|
|
193
|
-
export const schema = weave(HelloResolver, GreetingResolver)
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### 中间件|Middleware
|
|
197
|
-
|
|
198
|
-
GQLoom 提供了洋葱式中间件机制,可以在解析器执行之前或之后执行逻辑。
|
|
199
|
-
|
|
200
|
-
##### 声明一个简单中间件
|
|
201
|
-
|
|
202
|
-
```ts
|
|
203
|
-
import { Middleware } from "@gqloom/core"
|
|
204
|
-
|
|
205
|
-
const simpleMiddleware: Middleware = async (next) => {
|
|
206
|
-
console.log("Before resolve")
|
|
207
|
-
const result = await next()
|
|
208
|
-
console.log("After resolve")
|
|
209
|
-
return result
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### 上下文|Context
|
|
214
|
-
|
|
215
|
-
使用 `useContext` 函数随处获取上下文。
|
|
216
|
-
|
|
217
|
-
##### 在解析器中
|
|
218
|
-
|
|
219
|
-
```ts
|
|
220
|
-
import { loom, silk, useContext } from "@gqloom/core"
|
|
221
|
-
import { GraphQLString } from "graphql"
|
|
222
|
-
|
|
223
|
-
const { query, mutation } = loom
|
|
224
|
-
|
|
225
|
-
const HelloResolver = resolver({
|
|
226
|
-
hello: query(silk(GraphQLString), () => {
|
|
227
|
-
const name = useContext().info.name
|
|
228
|
-
return `Hello, ${name}!`
|
|
229
|
-
}),
|
|
230
|
-
})
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
##### 在中间件中
|
|
234
|
-
|
|
235
|
-
```ts
|
|
236
|
-
import { Middleware, useContext } from "@gqloom/core"
|
|
237
|
-
|
|
238
|
-
const simpleMiddleware: Middleware = async (next) => {
|
|
239
|
-
const context = useContext()
|
|
240
|
-
console.log(`Hello, ${context.info.name}!`)
|
|
241
|
-
return next()
|
|
242
|
-
}
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
Context 的内容取决于你选择的适配器。
|
|
246
|
-
|
|
247
|
-
#### 上下文记忆|Context Memoization
|
|
248
|
-
|
|
249
|
-
我们有时需要在上下文中设置一些内容,并且在同一个上下文的中间件或解析器中获取同样的值,例如当前访问用户、DataLoader。
|
|
250
|
-
GQLoom 提供了上下文记忆来便捷地实现这一点。
|
|
251
|
-
|
|
252
|
-
```ts
|
|
253
|
-
import { createMemoization, useContext } from "@gqloom/core"
|
|
254
|
-
|
|
255
|
-
// 创建一个上下文记忆
|
|
256
|
-
const useVisitor = createMemoization(async () => {
|
|
257
|
-
const context = useContext()
|
|
258
|
-
return await fetchVisitor(context.info.visitorId)
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
// 在中间件中使用上下文记忆
|
|
262
|
-
const AllowAdmin: Middleware = async (next) => {
|
|
263
|
-
const visitor = await useVisitor()
|
|
264
|
-
if (visitor.role !== "admin") throw new Error("Forbidden")
|
|
265
|
-
return next()
|
|
266
|
-
}
|
|
267
|
-
```
|