@xxxaz/json-rpc-schema-typing 0.10.30 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -4
- package/cjs/JsonRpcException.js +4 -5
- package/cjs/JsonRpcException.js.map +1 -1
- package/cjs/JsonRpcMethod.js +22 -8
- package/cjs/JsonRpcMethod.js.map +1 -1
- package/cjs/JsonRpcValidator.js +12 -4
- package/cjs/JsonRpcValidator.js.map +1 -1
- package/cjs/JsonSchemaValidator.js +12 -5
- package/cjs/JsonSchemaValidator.js.map +1 -1
- package/cjs/WebSocketWrapper.js +8 -3
- package/cjs/WebSocketWrapper.js.map +1 -1
- package/cjs/client/JsonRpcClient.js +77 -14
- package/cjs/client/JsonRpcClient.js.map +1 -1
- package/cjs/client/JsonRpcHttp2Client.js +8 -6
- package/cjs/client/JsonRpcHttp2Client.js.map +1 -1
- package/cjs/client/JsonRpcHttpClient.js +5 -5
- package/cjs/client/JsonRpcHttpClient.js.map +1 -1
- package/cjs/client/JsonRpcMessagePortClient.js +4 -5
- package/cjs/client/JsonRpcMessagePortClient.js.map +1 -1
- package/cjs/client/JsonRpcWebSocketClient.js +5 -6
- package/cjs/client/JsonRpcWebSocketClient.js.map +1 -1
- package/cjs/client/index.js +1 -1
- package/cjs/client/index.js.map +1 -1
- package/cjs/index.js +3 -3
- package/cjs/index.js.map +1 -1
- package/cjs/router/FileSystemRouter.js +3 -3
- package/cjs/router/FileSystemRouter.js.map +1 -1
- package/cjs/router/JsonRpcRouter.js.map +1 -1
- package/cjs/router/OpenRpc.js +141 -0
- package/cjs/router/OpenRpc.js.map +1 -0
- package/cjs/router/StaticRouter.js +41 -8
- package/cjs/router/StaticRouter.js.map +1 -1
- package/cjs/router/hashObject.js +7 -4
- package/cjs/router/hashObject.js.map +1 -1
- package/cjs/router/index.js +1 -0
- package/cjs/router/index.js.map +1 -1
- package/cjs/schemas/Complex.js +10 -13
- package/cjs/schemas/Complex.js.map +1 -1
- package/cjs/schemas/Primitive.js.map +1 -1
- package/cjs/schemas/Structure.js +5 -4
- package/cjs/schemas/Structure.js.map +1 -1
- package/cjs/schemas/index.js +1 -1
- package/cjs/schemas/index.js.map +1 -1
- package/cjs/server/JsonRpcHttpReceiver.js +11 -6
- package/cjs/server/JsonRpcHttpReceiver.js.map +1 -1
- package/cjs/server/JsonRpcMessagePortReceiver.js +3 -4
- package/cjs/server/JsonRpcMessagePortReceiver.js.map +1 -1
- package/cjs/server/JsonRpcServer.js +12 -6
- package/cjs/server/JsonRpcServer.js.map +1 -1
- package/cjs/server/JsonRpcWebSocketReceiver.js +3 -4
- package/cjs/server/JsonRpcWebSocketReceiver.js.map +1 -1
- package/cjs/server/index.js +1 -1
- package/cjs/server/index.js.map +1 -1
- package/cjs/utility.js +8 -14
- package/cjs/utility.js.map +1 -1
- package/esm/JsonRpcException.js +4 -5
- package/esm/JsonRpcException.js.map +1 -1
- package/esm/JsonRpcMethod.js +22 -8
- package/esm/JsonRpcMethod.js.map +1 -1
- package/esm/JsonRpcValidator.js +12 -4
- package/esm/JsonRpcValidator.js.map +1 -1
- package/esm/JsonSchemaValidator.js +13 -6
- package/esm/JsonSchemaValidator.js.map +1 -1
- package/esm/WebSocketWrapper.js +8 -3
- package/esm/WebSocketWrapper.js.map +1 -1
- package/esm/client/JsonRpcClient.js +78 -15
- package/esm/client/JsonRpcClient.js.map +1 -1
- package/esm/client/JsonRpcHttp2Client.js +10 -8
- package/esm/client/JsonRpcHttp2Client.js.map +1 -1
- package/esm/client/JsonRpcHttpClient.js +8 -8
- package/esm/client/JsonRpcHttpClient.js.map +1 -1
- package/esm/client/JsonRpcMessagePortClient.js +6 -7
- package/esm/client/JsonRpcMessagePortClient.js.map +1 -1
- package/esm/client/JsonRpcWebSocketClient.js +8 -9
- package/esm/client/JsonRpcWebSocketClient.js.map +1 -1
- package/esm/client/index.js +1 -1
- package/esm/client/index.js.map +1 -1
- package/esm/index.js +3 -3
- package/esm/index.js.map +1 -1
- package/esm/router/FileSystemRouter.js +5 -5
- package/esm/router/FileSystemRouter.js.map +1 -1
- package/esm/router/JsonRpcRouter.js +1 -1
- package/esm/router/JsonRpcRouter.js.map +1 -1
- package/esm/router/OpenRpc.js +138 -0
- package/esm/router/OpenRpc.js.map +1 -0
- package/esm/router/StaticRouter.js +42 -9
- package/esm/router/StaticRouter.js.map +1 -1
- package/esm/router/hashObject.js +7 -4
- package/esm/router/hashObject.js.map +1 -1
- package/esm/router/index.js +1 -0
- package/esm/router/index.js.map +1 -1
- package/esm/schemas/Complex.js +10 -13
- package/esm/schemas/Complex.js.map +1 -1
- package/esm/schemas/Primitive.js.map +1 -1
- package/esm/schemas/Structure.js +6 -5
- package/esm/schemas/Structure.js.map +1 -1
- package/esm/schemas/index.js +1 -1
- package/esm/schemas/index.js.map +1 -1
- package/esm/server/JsonRpcHttpReceiver.js +13 -8
- package/esm/server/JsonRpcHttpReceiver.js.map +1 -1
- package/esm/server/JsonRpcMessagePortReceiver.js +3 -4
- package/esm/server/JsonRpcMessagePortReceiver.js.map +1 -1
- package/esm/server/JsonRpcServer.js +15 -9
- package/esm/server/JsonRpcServer.js.map +1 -1
- package/esm/server/JsonRpcWebSocketReceiver.js +4 -5
- package/esm/server/JsonRpcWebSocketReceiver.js.map +1 -1
- package/esm/server/index.js +1 -1
- package/esm/server/index.js.map +1 -1
- package/esm/utility.js +8 -14
- package/esm/utility.js.map +1 -1
- package/package.json +4 -1
- package/src/JsonRpcException.ts +29 -26
- package/src/JsonRpcMethod.ts +173 -33
- package/src/JsonRpcValidator.ts +28 -15
- package/src/JsonSchemaValidator.ts +54 -25
- package/src/WebSocketWrapper.ts +36 -19
- package/src/client/JsonRpcClient.ts +188 -58
- package/src/client/JsonRpcHttp2Client.ts +55 -34
- package/src/client/JsonRpcHttpClient.ts +56 -30
- package/src/client/JsonRpcMessagePortClient.ts +36 -27
- package/src/client/JsonRpcWebSocketClient.ts +38 -21
- package/src/client/index.ts +1 -1
- package/src/index.ts +4 -7
- package/src/router/FileSystemRouter.ts +37 -21
- package/src/router/JsonRpcRouter.ts +50 -24
- package/src/router/OpenRpc.ts +262 -0
- package/src/router/StaticRouter.ts +115 -16
- package/src/router/hashObject.ts +11 -6
- package/src/router/index.ts +1 -0
- package/src/schemas/Complex.ts +33 -28
- package/src/schemas/Primitive.ts +0 -1
- package/src/schemas/Structure.ts +18 -9
- package/src/schemas/index.ts +1 -1
- package/src/server/JsonRpcHttpReceiver.ts +60 -28
- package/src/server/JsonRpcMessagePortReceiver.ts +27 -16
- package/src/server/JsonRpcServer.ts +59 -29
- package/src/server/JsonRpcWebSocketReceiver.ts +32 -25
- package/src/server/index.ts +1 -1
- package/src/types.ts +72 -43
- package/src/utility.ts +13 -19
- package/types/JsonRpcException.d.ts +2 -2
- package/types/JsonRpcMethod.d.ts +70 -3
- package/types/JsonRpcValidator.d.ts +3 -3
- package/types/JsonSchemaValidator.d.ts +2 -2
- package/types/WebSocketWrapper.d.ts +2 -2
- package/types/client/JsonRpcClient.d.ts +20 -9
- package/types/client/JsonRpcHttp2Client.d.ts +8 -6
- package/types/client/JsonRpcHttpClient.d.ts +7 -5
- package/types/client/JsonRpcMessagePortClient.d.ts +3 -3
- package/types/client/JsonRpcWebSocketClient.d.ts +4 -4
- package/types/client/index.d.ts +1 -1
- package/types/index.d.ts +3 -3
- package/types/router/FileSystemRouter.d.ts +3 -3
- package/types/router/JsonRpcRouter.d.ts +2 -2
- package/types/router/OpenRpc.d.ts +101 -0
- package/types/router/StaticRouter.d.ts +40 -4
- package/types/router/hashObject.d.ts +1 -1
- package/types/router/index.d.ts +1 -0
- package/types/schemas/Complex.d.ts +4 -4
- package/types/schemas/Structure.d.ts +5 -2
- package/types/schemas/index.d.ts +1 -1
- package/types/server/JsonRpcHttpReceiver.d.ts +2 -2
- package/types/server/JsonRpcMessagePortReceiver.d.ts +2 -2
- package/types/server/JsonRpcServer.d.ts +2 -2
- package/types/server/JsonRpcWebSocketReceiver.d.ts +1 -1
- package/types/server/index.d.ts +1 -1
- package/types/types.d.ts +12 -6
- package/types/utility.d.ts +2 -2
package/src/JsonRpcMethod.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type JSONSchema, type FromSchema } from "./types.js";
|
|
2
1
|
import { InvalidContext } from './JsonRpcException.js';
|
|
3
2
|
import { $Object, $Tuple } from './schemas/Structure.js';
|
|
3
|
+
import type { FromSchema, JSONSchema } from './types.js';
|
|
4
4
|
|
|
5
5
|
type ByPositionSchema = {
|
|
6
6
|
readonly type: 'array';
|
|
@@ -13,36 +13,118 @@ type ByNameSchema = {
|
|
|
13
13
|
readonly type: 'object';
|
|
14
14
|
readonly properties: ObjectProperties;
|
|
15
15
|
};
|
|
16
|
-
export type ParameterSchema = ByPositionSchema|ByNameSchema|undefined;
|
|
16
|
+
export type ParameterSchema = ByPositionSchema | ByNameSchema | undefined;
|
|
17
17
|
|
|
18
|
-
export interface JsonRpcMethodSchema<
|
|
18
|
+
export interface JsonRpcMethodSchema<
|
|
19
|
+
PrmSch extends ParameterSchema,
|
|
20
|
+
RtnSch extends JSONSchema | undefined,
|
|
21
|
+
> {
|
|
19
22
|
readonly $params?: PrmSch;
|
|
20
23
|
readonly $return?: RtnSch;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
/**
|
|
27
|
+
* クライアント generic (`Schema`) の受け口となる leaf 型。
|
|
28
|
+
* - 素の method schema (`$params`/`$return`) … 従来の JsonRpcSchema 経路 (WS/MessagePort 等)
|
|
29
|
+
* - `JsonRpcMethodDefinition` … StaticRouter に直接埋めた定義
|
|
30
|
+
* - `() => Promise<...>` / `Promise<...>` … route thunk map (lazy import) 経路
|
|
31
|
+
*/
|
|
32
|
+
type SchemaLeaf =
|
|
33
|
+
| JsonRpcMethodSchema<any, any>
|
|
34
|
+
| JsonRpcMethodDefinition<any, any, any>
|
|
35
|
+
| (() => Promise<unknown>)
|
|
36
|
+
| Promise<unknown>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* クライアント generic の**適合検証用の固定上限型**。
|
|
40
|
+
* 自己参照 (`Schema extends Conforms<Schema>`) や厳密な `RouteMap<Ctx>` は
|
|
41
|
+
* 巨大な route リテラルで TS2589 (instantiation too deep) を誘発するため、
|
|
42
|
+
* 「各 path が leaf か、さらに入れ子の schema 形」という**非自己参照の再帰上限**にして
|
|
43
|
+
* 適合検証を必ず通しつつ再帰爆発を避ける。primitive / 非 method 形の leaf は弾かれる。
|
|
44
|
+
*/
|
|
45
|
+
export type AnySchemaShape = {
|
|
46
|
+
readonly [path: string]: SchemaLeaf | AnySchemaShape;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type Params<Schema> = Schema extends ByPositionSchema
|
|
50
|
+
? FromSchema<Schema>
|
|
26
51
|
: Schema extends ByNameSchema
|
|
27
|
-
|
|
28
|
-
|
|
52
|
+
? [FromSchema<Schema>]
|
|
53
|
+
: Schema extends undefined
|
|
29
54
|
? []
|
|
30
|
-
|
|
31
|
-
export type Return<Schema>
|
|
32
|
-
|
|
33
|
-
? void
|
|
55
|
+
: never;
|
|
56
|
+
export type Return<Schema> = Schema extends undefined
|
|
57
|
+
? void
|
|
34
58
|
: Schema extends JSONSchema
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
export type JsonRpcMethod<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
59
|
+
? FromSchema<Schema>
|
|
60
|
+
: never;
|
|
61
|
+
|
|
62
|
+
export type JsonRpcMethod<
|
|
63
|
+
Context,
|
|
64
|
+
PrmSch extends ParameterSchema,
|
|
65
|
+
RtnSch extends JSONSchema | undefined,
|
|
66
|
+
> = (
|
|
67
|
+
this: Context,
|
|
68
|
+
...params: Params<PrmSch>
|
|
69
|
+
) => Promise<Return<RtnSch>> | Return<RtnSch>;
|
|
70
|
+
|
|
71
|
+
type ContextClass<Ctx> = { new (...args: any[]): Ctx };
|
|
42
72
|
type Context<Cls, FB> = Cls extends ContextClass<infer Ctx> ? Ctx : FB;
|
|
43
73
|
|
|
74
|
+
/**
|
|
75
|
+
* OpenRPC 準拠のメソッドメタ情報。
|
|
76
|
+
* 型 (JsonRpcCaller 等) は $params/$return のみ参照するため、メタは呼び出し型に影響しない。
|
|
77
|
+
*/
|
|
78
|
+
export type JsonRpcParameterMeta = {
|
|
79
|
+
readonly summary?: string;
|
|
80
|
+
readonly description?: string;
|
|
81
|
+
readonly required?: boolean;
|
|
82
|
+
readonly deprecated?: boolean;
|
|
83
|
+
};
|
|
84
|
+
export type JsonRpcResultMeta = {
|
|
85
|
+
readonly name: string;
|
|
86
|
+
readonly summary?: string;
|
|
87
|
+
readonly description?: string;
|
|
88
|
+
};
|
|
89
|
+
export type JsonRpcErrorMeta = {
|
|
90
|
+
readonly code: number;
|
|
91
|
+
readonly message: string;
|
|
92
|
+
readonly data?: unknown;
|
|
93
|
+
};
|
|
94
|
+
export type JsonRpcExampleMeta = {
|
|
95
|
+
readonly name: string;
|
|
96
|
+
readonly summary?: string;
|
|
97
|
+
readonly description?: string;
|
|
98
|
+
readonly params: readonly unknown[] | Readonly<Record<string, unknown>>;
|
|
99
|
+
readonly result?: unknown;
|
|
100
|
+
};
|
|
101
|
+
export type JsonRpcExternalDocsMeta = {
|
|
102
|
+
readonly url: string;
|
|
103
|
+
readonly description?: string;
|
|
104
|
+
};
|
|
105
|
+
export type JsonRpcMethodMeta = {
|
|
106
|
+
readonly summary?: string;
|
|
107
|
+
readonly description?: string;
|
|
108
|
+
readonly tags?: readonly string[];
|
|
109
|
+
readonly result?: JsonRpcResultMeta;
|
|
110
|
+
/**
|
|
111
|
+
* パラメータ別メタ。paramsByName の場合キーは $Object のプロパティ名。
|
|
112
|
+
* paramsByPosition の場合はキーの記述順が位置に対応する (キー名が param name になる)。
|
|
113
|
+
*/
|
|
114
|
+
readonly params?: Readonly<Record<string, JsonRpcParameterMeta>>;
|
|
115
|
+
readonly errors?: readonly JsonRpcErrorMeta[];
|
|
116
|
+
readonly examples?: readonly JsonRpcExampleMeta[];
|
|
117
|
+
readonly deprecated?: boolean;
|
|
118
|
+
readonly externalDocs?: JsonRpcExternalDocsMeta;
|
|
119
|
+
};
|
|
120
|
+
|
|
44
121
|
const methodKey = Symbol('JsonRpcMethodDefinition.method');
|
|
45
|
-
export class JsonRpcMethodDefinition<
|
|
122
|
+
export class JsonRpcMethodDefinition<
|
|
123
|
+
Context,
|
|
124
|
+
PrmSch extends ParameterSchema,
|
|
125
|
+
RtnSch extends JSONSchema | undefined,
|
|
126
|
+
> implements JsonRpcMethodSchema<PrmSch, RtnSch>
|
|
127
|
+
{
|
|
46
128
|
static readonly method: typeof methodKey = methodKey;
|
|
47
129
|
readonly [methodKey]: JsonRpcMethod<Context, PrmSch, RtnSch>;
|
|
48
130
|
|
|
@@ -51,53 +133,111 @@ export class JsonRpcMethodDefinition<Context, PrmSch extends ParameterSchema, Rt
|
|
|
51
133
|
readonly $params: PrmSch,
|
|
52
134
|
readonly $return: RtnSch,
|
|
53
135
|
readonly $contextClass?: ContextClass<Context>,
|
|
136
|
+
readonly $meta?: JsonRpcMethodMeta,
|
|
54
137
|
) {
|
|
55
138
|
this[methodKey] = method;
|
|
56
139
|
}
|
|
57
140
|
|
|
141
|
+
// instanceof はモジュール実体が二重ロードされた際に一致しないため、duck-typing でも判定する
|
|
142
|
+
static isDefinition(
|
|
143
|
+
obj: unknown,
|
|
144
|
+
): obj is JsonRpcMethodDefinition<any, any, any> {
|
|
145
|
+
if (obj instanceof JsonRpcMethodDefinition) return true;
|
|
146
|
+
const key = (obj as any)?.constructor?.method;
|
|
147
|
+
return typeof key === 'symbol' && (obj as any)[key] instanceof Function;
|
|
148
|
+
}
|
|
149
|
+
|
|
58
150
|
$apply(ctx: Context, params: any) {
|
|
59
|
-
if(this.$contextClass && !(ctx instanceof this.$contextClass)) {
|
|
151
|
+
if (this.$contextClass && !(ctx instanceof this.$contextClass)) {
|
|
60
152
|
const passedCtxType = (ctx as any)?.constructor?.name ?? typeof ctx;
|
|
61
|
-
throw new InvalidContext(
|
|
153
|
+
throw new InvalidContext(
|
|
154
|
+
`Invalid context: expected ${this.$contextClass.name} but got ${passedCtxType}`,
|
|
155
|
+
);
|
|
62
156
|
}
|
|
63
157
|
if (this.$params?.type === 'object') params = [params];
|
|
64
158
|
return this[methodKey].apply(ctx, params);
|
|
65
159
|
}
|
|
66
160
|
|
|
67
|
-
static get builder(){
|
|
161
|
+
static get builder() {
|
|
68
162
|
return new DefinitionBuilder(undefined, undefined, undefined);
|
|
69
163
|
}
|
|
70
164
|
}
|
|
71
165
|
|
|
72
|
-
class DefinitionBuilder<
|
|
166
|
+
class DefinitionBuilder<
|
|
167
|
+
CtxCls extends ContextClass<any> | undefined,
|
|
168
|
+
PrmSch extends ParameterSchema,
|
|
169
|
+
RtnSch extends JSONSchema | undefined,
|
|
170
|
+
> implements JsonRpcMethodSchema<PrmSch, RtnSch>
|
|
171
|
+
{
|
|
73
172
|
constructor(
|
|
74
173
|
readonly $contextClass: CtxCls,
|
|
75
174
|
readonly $params: PrmSch,
|
|
76
175
|
readonly $return: RtnSch,
|
|
176
|
+
readonly $meta?: JsonRpcMethodMeta,
|
|
77
177
|
) {}
|
|
78
178
|
|
|
79
|
-
define<Ctx = {}>(
|
|
80
|
-
|
|
179
|
+
define<Ctx = {}>(
|
|
180
|
+
method: JsonRpcMethod<Context<CtxCls, Ctx>, PrmSch, RtnSch>,
|
|
181
|
+
) {
|
|
182
|
+
return new JsonRpcMethodDefinition<
|
|
183
|
+
Context<CtxCls, Ctx>,
|
|
184
|
+
PrmSch,
|
|
185
|
+
RtnSch
|
|
186
|
+
>(method, this.$params, this.$return, this.$contextClass, this.$meta);
|
|
81
187
|
}
|
|
82
188
|
|
|
83
189
|
contextClass<CtxCls extends ContextClass<any>>(contextClass: CtxCls) {
|
|
84
|
-
return new DefinitionBuilder(
|
|
190
|
+
return new DefinitionBuilder(
|
|
191
|
+
contextClass,
|
|
192
|
+
this.$params,
|
|
193
|
+
this.$return,
|
|
194
|
+
this.$meta,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
meta(meta: JsonRpcMethodMeta) {
|
|
199
|
+
return new DefinitionBuilder(
|
|
200
|
+
this.$contextClass,
|
|
201
|
+
this.$params,
|
|
202
|
+
this.$return,
|
|
203
|
+
meta,
|
|
204
|
+
);
|
|
85
205
|
}
|
|
86
206
|
|
|
87
207
|
params<NewPrm extends ParameterSchema>(params: NewPrm) {
|
|
88
|
-
return new DefinitionBuilder(
|
|
208
|
+
return new DefinitionBuilder(
|
|
209
|
+
this.$contextClass,
|
|
210
|
+
params,
|
|
211
|
+
this.$return,
|
|
212
|
+
this.$meta,
|
|
213
|
+
);
|
|
89
214
|
}
|
|
90
215
|
|
|
91
216
|
paramsByName<NewPrm extends ObjectProperties>(params: NewPrm) {
|
|
92
|
-
return new DefinitionBuilder(
|
|
217
|
+
return new DefinitionBuilder(
|
|
218
|
+
this.$contextClass,
|
|
219
|
+
$Object(params),
|
|
220
|
+
this.$return,
|
|
221
|
+
this.$meta,
|
|
222
|
+
);
|
|
93
223
|
}
|
|
94
224
|
|
|
95
225
|
paramsByPosition<NewPrm extends readonly JSONSchema[]>(...params: NewPrm) {
|
|
96
|
-
return new DefinitionBuilder(
|
|
226
|
+
return new DefinitionBuilder(
|
|
227
|
+
this.$contextClass,
|
|
228
|
+
$Tuple(...params),
|
|
229
|
+
this.$return,
|
|
230
|
+
this.$meta,
|
|
231
|
+
);
|
|
97
232
|
}
|
|
98
233
|
|
|
99
|
-
return<NewRtn extends JSONSchema|undefined>(rtn: NewRtn) {
|
|
100
|
-
return new DefinitionBuilder(
|
|
234
|
+
return<NewRtn extends JSONSchema | undefined>(rtn: NewRtn) {
|
|
235
|
+
return new DefinitionBuilder(
|
|
236
|
+
this.$contextClass,
|
|
237
|
+
this.$params,
|
|
238
|
+
rtn,
|
|
239
|
+
this.$meta,
|
|
240
|
+
);
|
|
101
241
|
}
|
|
102
242
|
|
|
103
243
|
/** @deprecated paramsByPosition を使用してください */
|
|
@@ -105,7 +245,7 @@ class DefinitionBuilder<CtxCls extends ContextClass<any>|undefined, PrmSch exten
|
|
|
105
245
|
return this.paramsByPosition(...params);
|
|
106
246
|
}
|
|
107
247
|
/** @deprecated return を使用してください */
|
|
108
|
-
returnSchema<NewRtn extends JSONSchema|undefined>(rtn: NewRtn) {
|
|
248
|
+
returnSchema<NewRtn extends JSONSchema | undefined>(rtn: NewRtn) {
|
|
109
249
|
return this.return(rtn);
|
|
110
250
|
}
|
|
111
251
|
}
|
package/src/JsonRpcValidator.ts
CHANGED
|
@@ -1,41 +1,54 @@
|
|
|
1
|
-
import { FromSchema } from 'json-schema-to-ts';
|
|
2
|
-
import { JsonRpcMethodSchema, ParameterSchema } from "./JsonRpcMethod.js";
|
|
3
|
-
import { JSONSchema } from "./types.js";
|
|
1
|
+
import type { FromSchema } from 'json-schema-to-ts';
|
|
4
2
|
import { InvalidParams, InvalidReturn } from './JsonRpcException.js';
|
|
3
|
+
import type { JsonRpcMethodSchema, ParameterSchema } from './JsonRpcMethod.js';
|
|
5
4
|
import { JsonSchemaValidator } from './JsonSchemaValidator.js';
|
|
5
|
+
import type { JSONSchema } from './types.js';
|
|
6
6
|
|
|
7
7
|
type Valid<S> = S extends JSONSchema ? FromSchema<S> : never;
|
|
8
8
|
|
|
9
9
|
export class JsonRpcValidator<
|
|
10
10
|
P extends ParameterSchema,
|
|
11
11
|
R extends JSONSchema | undefined,
|
|
12
|
-
Sch extends JsonRpcMethodSchema<P, R
|
|
12
|
+
Sch extends JsonRpcMethodSchema<P, R>,
|
|
13
13
|
> {
|
|
14
14
|
constructor(readonly schema: Sch) {
|
|
15
|
-
this.#paramValidator = schema.$params
|
|
16
|
-
|
|
15
|
+
this.#paramValidator = schema.$params
|
|
16
|
+
? new JsonSchemaValidator(schema.$params)
|
|
17
|
+
: null;
|
|
18
|
+
this.#returnValidator = schema.$return
|
|
19
|
+
? new JsonSchemaValidator(schema.$return)
|
|
20
|
+
: null;
|
|
17
21
|
}
|
|
18
|
-
#paramValidator:JsonSchemaValidator | null;
|
|
19
|
-
#returnValidator:JsonSchemaValidator | null;
|
|
22
|
+
#paramValidator: JsonSchemaValidator | null;
|
|
23
|
+
#returnValidator: JsonSchemaValidator | null;
|
|
20
24
|
|
|
21
25
|
validateParams(params: unknown): Valid<Sch['$params']> {
|
|
22
|
-
if(!this.#paramValidator) return params as Valid<Sch['$params']>;
|
|
26
|
+
if (!this.#paramValidator) return params as Valid<Sch['$params']>;
|
|
23
27
|
const validated = this.#paramValidator.validate(params);
|
|
24
|
-
if(!validated.valid && validated.errors.length > 0) {
|
|
25
|
-
const message = validated.errors
|
|
28
|
+
if (!validated.valid && validated.errors.length > 0) {
|
|
29
|
+
const message = validated.errors
|
|
30
|
+
.map(
|
|
31
|
+
({ instancePath, message }) =>
|
|
32
|
+
`params${instancePath ?? ''}: ${message}`,
|
|
33
|
+
)
|
|
34
|
+
.join('\n');
|
|
26
35
|
throw new InvalidParams(message, validated.errors);
|
|
27
36
|
}
|
|
28
37
|
return params as Valid<Sch['$params']>;
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
validateReturn(result: unknown): Valid<Sch['$return']> {
|
|
32
|
-
if(!this.#returnValidator) return result as Valid<Sch['$return']>;
|
|
41
|
+
if (!this.#returnValidator) return result as Valid<Sch['$return']>;
|
|
33
42
|
const validated = this.#returnValidator.validate(result);
|
|
34
43
|
if (!validated.valid && validated.errors.length > 0) {
|
|
35
|
-
const message = validated.errors
|
|
44
|
+
const message = validated.errors
|
|
45
|
+
.map(
|
|
46
|
+
({ instancePath, message }) =>
|
|
47
|
+
`return.${instancePath ?? ''}: ${message}`,
|
|
48
|
+
)
|
|
49
|
+
.join('\n');
|
|
36
50
|
throw new InvalidReturn(message, validated.errors);
|
|
37
51
|
}
|
|
38
52
|
return result as Valid<Sch['$return']>;
|
|
39
53
|
}
|
|
40
|
-
|
|
41
|
-
}
|
|
54
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
Ajv,
|
|
3
|
+
type Options as AjvOptions,
|
|
4
|
+
type ErrorObject,
|
|
5
|
+
type ValidateFunction,
|
|
6
|
+
} from 'ajv';
|
|
7
|
+
import type { JSONSchema } from 'json-schema-to-ts';
|
|
3
8
|
import type { Worker as NodeWorker } from 'worker_threads';
|
|
4
9
|
|
|
5
10
|
const isBrowser =
|
|
@@ -16,12 +21,14 @@ function currentFile() {
|
|
|
16
21
|
return globalThis.__filename;
|
|
17
22
|
}
|
|
18
23
|
try {
|
|
19
|
-
return Function(
|
|
24
|
+
return Function('return import.meta.filename')();
|
|
20
25
|
} catch (err) {
|
|
21
26
|
if (!(err instanceof SyntaxError)) throw err;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
const script = globalThis.document?.currentScript as
|
|
29
|
+
const script = globalThis.document?.currentScript as
|
|
30
|
+
| HTMLScriptElement
|
|
31
|
+
| undefined;
|
|
25
32
|
if (script?.src) {
|
|
26
33
|
return script?.src;
|
|
27
34
|
}
|
|
@@ -29,7 +36,6 @@ function currentFile() {
|
|
|
29
36
|
return null;
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
|
|
33
39
|
type SerializedError = {
|
|
34
40
|
propertyName?: string;
|
|
35
41
|
message?: string;
|
|
@@ -39,26 +45,35 @@ type SerializedError = {
|
|
|
39
45
|
schemaPath?: string;
|
|
40
46
|
};
|
|
41
47
|
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
function* serializeErrors(
|
|
49
|
+
error?: ErrorObject[] | null,
|
|
50
|
+
): Generator<SerializedError> {
|
|
51
|
+
if (!error) return;
|
|
52
|
+
for (const {
|
|
53
|
+
propertyName,
|
|
54
|
+
message,
|
|
55
|
+
schemaPath,
|
|
56
|
+
data,
|
|
57
|
+
keyword,
|
|
58
|
+
instancePath,
|
|
59
|
+
params,
|
|
60
|
+
} of error) {
|
|
45
61
|
const err = {} as SerializedError;
|
|
46
|
-
if(propertyName) err.propertyName = propertyName;
|
|
47
|
-
if(message) err.message = message;
|
|
48
|
-
if(data) err.data = data;
|
|
49
|
-
if(keyword) err.keyword = keyword;
|
|
50
|
-
if(instancePath) err.instancePath = instancePath;
|
|
51
|
-
if(schemaPath) err.schemaPath = schemaPath;
|
|
62
|
+
if (propertyName) err.propertyName = propertyName;
|
|
63
|
+
if (message) err.message = message;
|
|
64
|
+
if (data) err.data = data;
|
|
65
|
+
if (keyword) err.keyword = keyword;
|
|
66
|
+
if (instancePath) err.instancePath = instancePath;
|
|
67
|
+
if (schemaPath) err.schemaPath = schemaPath;
|
|
52
68
|
if (Object.keys(err).length > 0) {
|
|
53
69
|
yield err;
|
|
54
70
|
}
|
|
55
71
|
if (params.errors) {
|
|
56
|
-
yield
|
|
72
|
+
yield* serializeErrors(params.errors);
|
|
57
73
|
}
|
|
58
74
|
}
|
|
59
75
|
}
|
|
60
76
|
|
|
61
|
-
|
|
62
77
|
type ValidationResult =
|
|
63
78
|
| { valid: true }
|
|
64
79
|
| {
|
|
@@ -72,7 +87,7 @@ const methodName = '@xxxaz/json-schema-typing.JsonSchemaValidator.validate';
|
|
|
72
87
|
export class JsonSchemaValidator {
|
|
73
88
|
constructor(
|
|
74
89
|
readonly schema: JSONSchema & object,
|
|
75
|
-
ajvOptions?: AjvOptions
|
|
90
|
+
ajvOptions?: AjvOptions,
|
|
76
91
|
) {
|
|
77
92
|
this.#ajv = new Ajv(ajvOptions);
|
|
78
93
|
this.#validate = this.#ajv.compile(this.schema);
|
|
@@ -106,13 +121,18 @@ export class JsonSchemaValidator {
|
|
|
106
121
|
const id = crypto.randomUUID();
|
|
107
122
|
const listener = Promise.withResolvers<ValidationResult>();
|
|
108
123
|
listeners[id] = listener;
|
|
109
|
-
worker.postMessage({
|
|
124
|
+
worker.postMessage({
|
|
125
|
+
method: methodName,
|
|
126
|
+
schema: this.schema,
|
|
127
|
+
data,
|
|
128
|
+
id,
|
|
129
|
+
});
|
|
110
130
|
return listener.promise;
|
|
111
131
|
}
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
type WorkerType = NodeWorker | Worker;
|
|
115
|
-
|
|
135
|
+
const workers: WeakMap<object, WorkerType> = new WeakMap();
|
|
116
136
|
let listeners: Record<string, PromiseWithResolvers<ValidationResult>> = {};
|
|
117
137
|
async function getWorker(key: object): Promise<WorkerType | null> {
|
|
118
138
|
if (workers.has(key)) return workers.get(key) ?? null;
|
|
@@ -138,7 +158,11 @@ async function getWorker(key: object): Promise<WorkerType | null> {
|
|
|
138
158
|
workers.set(key, worker);
|
|
139
159
|
return worker;
|
|
140
160
|
}
|
|
141
|
-
function onMessage(data: {
|
|
161
|
+
function onMessage(data: {
|
|
162
|
+
id: string;
|
|
163
|
+
result?: ValidationResult;
|
|
164
|
+
error?: any;
|
|
165
|
+
}) {
|
|
142
166
|
const listener = listeners[data.id];
|
|
143
167
|
if (listener) {
|
|
144
168
|
if (data.error) {
|
|
@@ -150,7 +174,9 @@ function onMessage(data: {id: string, result?: ValidationResult, error?: any}) {
|
|
|
150
174
|
}
|
|
151
175
|
}
|
|
152
176
|
function onError(err: Error) {
|
|
153
|
-
Object.values(listeners).forEach(({ reject }) =>
|
|
177
|
+
Object.values(listeners).forEach(({ reject }) => {
|
|
178
|
+
reject(err);
|
|
179
|
+
});
|
|
154
180
|
listeners = {};
|
|
155
181
|
}
|
|
156
182
|
function onFaild(data: { id: string }) {
|
|
@@ -162,7 +188,12 @@ function onFaild(data: { id: string }) {
|
|
|
162
188
|
}
|
|
163
189
|
|
|
164
190
|
(async function kickValidateInWorker(): Promise<void> {
|
|
165
|
-
type ReceiveMesg = {
|
|
191
|
+
type ReceiveMesg = {
|
|
192
|
+
method: string;
|
|
193
|
+
id: string;
|
|
194
|
+
schema: JSONSchema & object;
|
|
195
|
+
data: unknown;
|
|
196
|
+
};
|
|
166
197
|
const onMessage = (msg: ReceiveMesg, callback: (result: {}) => void) => {
|
|
167
198
|
const { id, schema, data } = msg;
|
|
168
199
|
try {
|
|
@@ -180,7 +211,7 @@ function onFaild(data: { id: string }) {
|
|
|
180
211
|
if (msg.method !== methodName) return;
|
|
181
212
|
onMessage(msg, (result) => parentPort.postMessage(result));
|
|
182
213
|
});
|
|
183
|
-
}
|
|
214
|
+
}
|
|
184
215
|
if (isWebWorker) {
|
|
185
216
|
self.addEventListener('message', (ev) => {
|
|
186
217
|
const msg = ev.data as ReceiveMesg;
|
|
@@ -191,5 +222,3 @@ function onFaild(data: { id: string }) {
|
|
|
191
222
|
});
|
|
192
223
|
}
|
|
193
224
|
})();
|
|
194
|
-
|
|
195
|
-
|
package/src/WebSocketWrapper.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export type WrapableWebSocket = WebSocket|NodeWebSocket;
|
|
5
|
-
export function wrapWebSocket(
|
|
1
|
+
import type { JsonSerializable } from '@xxxaz/stream-api-json';
|
|
2
|
+
import type { WebSocket as NodeWebSocket, RawData } from 'ws';
|
|
3
|
+
|
|
4
|
+
export type WrapableWebSocket = WebSocket | NodeWebSocket;
|
|
5
|
+
export function wrapWebSocket(
|
|
6
|
+
socket: WrapableWebSocket,
|
|
7
|
+
listener: SocketListener,
|
|
8
|
+
closedListener: ScokcetClosedListener,
|
|
9
|
+
) {
|
|
6
10
|
if ('WebSocket' in globalThis && socket instanceof WebSocket) {
|
|
7
11
|
return new BrowserWebSocketWrapper(socket, listener, closedListener);
|
|
8
12
|
}
|
|
9
|
-
return new NodeWebSocketWrapper(
|
|
13
|
+
return new NodeWebSocketWrapper(
|
|
14
|
+
socket as NodeWebSocket,
|
|
15
|
+
listener,
|
|
16
|
+
closedListener,
|
|
17
|
+
);
|
|
10
18
|
}
|
|
11
19
|
|
|
12
20
|
type ClosedData = {
|
|
@@ -19,10 +27,12 @@ type ClosedData = {
|
|
|
19
27
|
type SocketListener = (data: JsonSerializable) => void;
|
|
20
28
|
type ScokcetClosedListener = (data: ClosedData) => void;
|
|
21
29
|
|
|
22
|
-
type ConnectionState = 'CONNECTING'|'OPEN'|'CLOSING'|'CLOSED';
|
|
30
|
+
type ConnectionState = 'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED';
|
|
23
31
|
export type WebSocketState = (typeof WebSocket)[ConnectionState];
|
|
24
32
|
|
|
25
|
-
export interface WebSocketWrapper<
|
|
33
|
+
export interface WebSocketWrapper<
|
|
34
|
+
Socket extends WrapableWebSocket = WrapableWebSocket,
|
|
35
|
+
> {
|
|
26
36
|
readonly socket: Socket;
|
|
27
37
|
readonly listener: SocketListener;
|
|
28
38
|
readonly readyState: WebSocketState;
|
|
@@ -35,7 +45,7 @@ export class NodeWebSocketWrapper implements WebSocketWrapper<NodeWebSocket> {
|
|
|
35
45
|
constructor(
|
|
36
46
|
readonly socket: NodeWebSocket,
|
|
37
47
|
readonly listener: SocketListener,
|
|
38
|
-
readonly closedListener: ScokcetClosedListener
|
|
48
|
+
readonly closedListener: ScokcetClosedListener,
|
|
39
49
|
) {
|
|
40
50
|
socket.on('message', this.#onMessage);
|
|
41
51
|
socket.on('close', (code, reasonBuffer) => {
|
|
@@ -67,14 +77,14 @@ export class NodeWebSocketWrapper implements WebSocketWrapper<NodeWebSocket> {
|
|
|
67
77
|
this.listener(parsed);
|
|
68
78
|
};
|
|
69
79
|
|
|
70
|
-
async #parseMessage(data: RawData)
|
|
80
|
+
async #parseMessage(data: RawData): Promise<JsonSerializable> {
|
|
71
81
|
if (data instanceof ArrayBuffer) {
|
|
72
82
|
return JSON.parse(await new Blob([data]).text());
|
|
73
83
|
}
|
|
74
|
-
const json
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
const json =
|
|
85
|
+
data instanceof Array
|
|
86
|
+
? data.map((d) => JSON.parse(d.toString())).join('')
|
|
87
|
+
: data.toString();
|
|
78
88
|
return JSON.parse(json);
|
|
79
89
|
}
|
|
80
90
|
}
|
|
@@ -83,11 +93,16 @@ export class BrowserWebSocketWrapper implements WebSocketWrapper<WebSocket> {
|
|
|
83
93
|
constructor(
|
|
84
94
|
readonly socket: WebSocket,
|
|
85
95
|
readonly listener: SocketListener,
|
|
86
|
-
readonly closedListener: ScokcetClosedListener
|
|
96
|
+
readonly closedListener: ScokcetClosedListener,
|
|
87
97
|
) {
|
|
88
98
|
socket.addEventListener('message', this.#onMessage);
|
|
89
|
-
socket.addEventListener('close', ev => {
|
|
90
|
-
closedListener({
|
|
99
|
+
socket.addEventListener('close', (ev) => {
|
|
100
|
+
closedListener({
|
|
101
|
+
code: ev.code,
|
|
102
|
+
reason: ev.reason,
|
|
103
|
+
wasClean: ev.wasClean,
|
|
104
|
+
wrapper: this,
|
|
105
|
+
});
|
|
91
106
|
this.socket.removeEventListener('message', this.#onMessage);
|
|
92
107
|
});
|
|
93
108
|
}
|
|
@@ -101,7 +116,9 @@ export class BrowserWebSocketWrapper implements WebSocketWrapper<WebSocket> {
|
|
|
101
116
|
this.listener(parsed);
|
|
102
117
|
};
|
|
103
118
|
|
|
104
|
-
async #parseMessage(
|
|
119
|
+
async #parseMessage(
|
|
120
|
+
data: ArrayBuffer | string | object,
|
|
121
|
+
): Promise<JsonSerializable> {
|
|
105
122
|
if (data instanceof ArrayBuffer) {
|
|
106
123
|
return JSON.parse(await new Blob([data]).text());
|
|
107
124
|
}
|
|
@@ -127,4 +144,4 @@ export class BrowserWebSocketWrapper implements WebSocketWrapper<WebSocket> {
|
|
|
127
144
|
this.detach();
|
|
128
145
|
this.socket.close();
|
|
129
146
|
}
|
|
130
|
-
}
|
|
147
|
+
}
|