@c9up/comet 0.1.1 → 0.1.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/dist/client.d.ts.map +1 -1
- package/dist/client.js +45 -9
- package/dist/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +51 -10
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAGN,QAAQ,EAGR,MAAM,eAAe,CAAC;AAEvB;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CAC1B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,OAAO,EACb,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,MAAM,WAAW,gBAAgB;IAChC,wDAAwD;IACxD,SAAS,EAAE,YAAY,CAAC;IACxB,qCAAqC;IACrC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,mDAAmD;AACnD,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IAC1C;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,CAAC,CAAC;IAC7B,uFAAuF;IACvF,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,wFAAwF;AACxF,MAAM,WAAW,OAAO,CAAC,CAAC,GAAG,OAAO;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,CAAC,CAAC;CAC7B;AAED,+EAA+E;AAC/E,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,OAAO,IAC9B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AAElC,MAAM,WAAW,SAAS;IACzB;;;;;OAKG;IACH,IAAI,CAAC,CAAC,GAAG,OAAO,EACf,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAAC;IACd;;;OAGG;IACH,KAAK,CACJ,KAAK,EAAE,OAAO,EAAE,EAChB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAChC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;CACxB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAGN,QAAQ,EAGR,MAAM,eAAe,CAAC;AAEvB;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CAC1B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,OAAO,EACb,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,MAAM,WAAW,gBAAgB;IAChC,wDAAwD;IACxD,SAAS,EAAE,YAAY,CAAC;IACxB,qCAAqC;IACrC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,mDAAmD;AACnD,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IAC1C;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,CAAC,CAAC;IAC7B,uFAAuF;IACvF,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,wFAAwF;AACxF,MAAM,WAAW,OAAO,CAAC,CAAC,GAAG,OAAO;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,CAAC,CAAC;CAC7B;AAED,+EAA+E;AAC/E,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,OAAO,IAC9B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AAElC,MAAM,WAAW,SAAS;IACzB;;;;;OAKG;IACH,IAAI,CAAC,CAAC,GAAG,OAAO,EACf,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAAC;IACd;;;OAGG;IACH,KAAK,CACJ,KAAK,EAAE,OAAO,EAAE,EAChB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAChC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;CACxB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAyHpE"}
|
package/dist/client.js
CHANGED
|
@@ -28,11 +28,20 @@ export function createRpcClient(options) {
|
|
|
28
28
|
if (!isObject(res) || res.jsonrpc !== "2.0" || res.id !== id) {
|
|
29
29
|
throw new RpcError(RpcErrorCode.InternalError, `Malformed or mismatched JSON-RPC response for "${method}" (bad jsonrpc/id envelope)`);
|
|
30
30
|
}
|
|
31
|
+
// §5: `result` and `error` are mutually exclusive — "Either the
|
|
32
|
+
// result member or error member MUST be included, but both members
|
|
33
|
+
// MUST NOT be included." A response carrying both is malformed, and
|
|
34
|
+
// reading the error out of it is guessing at which half the sender
|
|
35
|
+
// meant. Refused, the way a bad jsonrpc/id envelope already is.
|
|
36
|
+
const hasResult = "result" in res;
|
|
37
|
+
if (hasResult && res.error !== undefined) {
|
|
38
|
+
throw new RpcError(RpcErrorCode.InternalError, `Malformed JSON-RPC response for "${method}" (carries both result and error)`);
|
|
39
|
+
}
|
|
31
40
|
if (res.error !== undefined)
|
|
32
41
|
throw toRpcError(res.error);
|
|
33
42
|
// A conformant success response carries `result` (any JSON value,
|
|
34
43
|
// including null) and no error — neither key present is malformed.
|
|
35
|
-
if (!
|
|
44
|
+
if (!hasResult) {
|
|
36
45
|
throw new RpcError(RpcErrorCode.InternalError, `JSON-RPC response for "${method}" has neither result nor error`);
|
|
37
46
|
}
|
|
38
47
|
// Result boundary — the same unchecked `T` assertion HTTP clients use,
|
|
@@ -53,21 +62,48 @@ export function createRpcClient(options) {
|
|
|
53
62
|
if (!Array.isArray(res)) {
|
|
54
63
|
throw new RpcError(RpcErrorCode.InternalError, "Malformed JSON-RPC batch response");
|
|
55
64
|
}
|
|
65
|
+
// Envelope conformance, per item, exactly as `call` applies it — this
|
|
66
|
+
// checked none of it, so `{ jsonrpc: "1.0", id: 0 }` came back as
|
|
67
|
+
// `{ ok: true, value: undefined }` where the single-call path would
|
|
68
|
+
// have refused it.
|
|
56
69
|
const byId = new Map();
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
70
|
+
const duplicated = new Set();
|
|
71
|
+
for (const item of res) {
|
|
72
|
+
if (!isObject(item))
|
|
73
|
+
continue;
|
|
74
|
+
// A repeated id is a malformed batch, and silently keeping the last
|
|
75
|
+
// one lets a response answer a call it was not for.
|
|
76
|
+
if (byId.has(item.id))
|
|
77
|
+
duplicated.add(item.id);
|
|
78
|
+
byId.set(item.id, item);
|
|
79
|
+
}
|
|
60
80
|
return calls.map((c, index) => {
|
|
81
|
+
const fail = (message) => ({
|
|
82
|
+
ok: false,
|
|
83
|
+
error: new RpcError(RpcErrorCode.InternalError, message),
|
|
84
|
+
});
|
|
61
85
|
const envelope = byId.get(index);
|
|
62
|
-
if (!envelope)
|
|
63
|
-
return {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
if (!envelope)
|
|
87
|
+
return fail(`No response for "${c.method}"`);
|
|
88
|
+
if (duplicated.has(index)) {
|
|
89
|
+
return fail(`More than one response carried id ${index}`);
|
|
90
|
+
}
|
|
91
|
+
if (envelope.jsonrpc !== "2.0") {
|
|
92
|
+
return fail(`Malformed JSON-RPC response for "${c.method}" (bad jsonrpc version)`);
|
|
93
|
+
}
|
|
94
|
+
const hasResult = "result" in envelope;
|
|
95
|
+
// §5: mutually exclusive. See the single-call path.
|
|
96
|
+
if (hasResult && envelope.error !== undefined) {
|
|
97
|
+
return fail(`Malformed JSON-RPC response for "${c.method}" (carries both result and error)`);
|
|
67
98
|
}
|
|
68
99
|
if (envelope.error !== undefined) {
|
|
69
100
|
return { ok: false, error: toRpcError(envelope.error) };
|
|
70
101
|
}
|
|
102
|
+
// A conformant success carries `result` — any JSON value, null
|
|
103
|
+
// included — so its absence is malformed, not an undefined value.
|
|
104
|
+
if (!hasResult) {
|
|
105
|
+
return fail(`JSON-RPC response for "${c.method}" has neither result nor error`);
|
|
106
|
+
}
|
|
71
107
|
const value = c.parse ? c.parse(envelope.result) : envelope.result;
|
|
72
108
|
return { ok: true, value };
|
|
73
109
|
});
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACN,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,UAAU,GACV,MAAM,eAAe,CAAC;AAgEvB,MAAM,UAAU,eAAe,CAAC,OAAyB;IACxD,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC;IAClC,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,OAAO;QACN,KAAK,CAAC,IAAI,CACT,MAAc,EACd,MAAgB,EAChB,WAA+B;YAE/B,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC;YACpB,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE;gBAClE,MAAM,EAAE,WAAW,EAAE,MAAM;aAC3B,CAAC,CAAC;YACH,yEAAyE;YACzE,mEAAmE;YACnE,uEAAuE;YACvE,YAAY;YACZ,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,OAAO,KAAK,KAAK,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;gBAC9D,MAAM,IAAI,QAAQ,CACjB,YAAY,CAAC,aAAa,EAC1B,kDAAkD,MAAM,6BAA6B,CACrF,CAAC;YACH,CAAC;YACD,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;gBAAE,MAAM,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACzD,kEAAkE;YAClE,mEAAmE;YACnE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -105,10 +105,22 @@ export function createRpcClient(options: RpcClientOptions): RpcClient {
|
|
|
105
105
|
`Malformed or mismatched JSON-RPC response for "${method}" (bad jsonrpc/id envelope)`,
|
|
106
106
|
);
|
|
107
107
|
}
|
|
108
|
+
// §5: `result` and `error` are mutually exclusive — "Either the
|
|
109
|
+
// result member or error member MUST be included, but both members
|
|
110
|
+
// MUST NOT be included." A response carrying both is malformed, and
|
|
111
|
+
// reading the error out of it is guessing at which half the sender
|
|
112
|
+
// meant. Refused, the way a bad jsonrpc/id envelope already is.
|
|
113
|
+
const hasResult = "result" in res;
|
|
114
|
+
if (hasResult && res.error !== undefined) {
|
|
115
|
+
throw new RpcError(
|
|
116
|
+
RpcErrorCode.InternalError,
|
|
117
|
+
`Malformed JSON-RPC response for "${method}" (carries both result and error)`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
108
120
|
if (res.error !== undefined) throw toRpcError(res.error);
|
|
109
121
|
// A conformant success response carries `result` (any JSON value,
|
|
110
122
|
// including null) and no error — neither key present is malformed.
|
|
111
|
-
if (!
|
|
123
|
+
if (!hasResult) {
|
|
112
124
|
throw new RpcError(
|
|
113
125
|
RpcErrorCode.InternalError,
|
|
114
126
|
`JSON-RPC response for "${method}" has neither result nor error`,
|
|
@@ -139,22 +151,51 @@ export function createRpcClient(options: RpcClientOptions): RpcClient {
|
|
|
139
151
|
"Malformed JSON-RPC batch response",
|
|
140
152
|
);
|
|
141
153
|
}
|
|
154
|
+
// Envelope conformance, per item, exactly as `call` applies it — this
|
|
155
|
+
// checked none of it, so `{ jsonrpc: "1.0", id: 0 }` came back as
|
|
156
|
+
// `{ ok: true, value: undefined }` where the single-call path would
|
|
157
|
+
// have refused it.
|
|
142
158
|
const byId = new Map<unknown, Record<string, unknown>>();
|
|
143
|
-
|
|
159
|
+
const duplicated = new Set<unknown>();
|
|
160
|
+
for (const item of res) {
|
|
161
|
+
if (!isObject(item)) continue;
|
|
162
|
+
// A repeated id is a malformed batch, and silently keeping the last
|
|
163
|
+
// one lets a response answer a call it was not for.
|
|
164
|
+
if (byId.has(item.id)) duplicated.add(item.id);
|
|
165
|
+
byId.set(item.id, item);
|
|
166
|
+
}
|
|
144
167
|
return calls.map((c, index) => {
|
|
168
|
+
const fail = (message: string): RpcResult => ({
|
|
169
|
+
ok: false,
|
|
170
|
+
error: new RpcError(RpcErrorCode.InternalError, message),
|
|
171
|
+
});
|
|
145
172
|
const envelope = byId.get(index);
|
|
146
|
-
if (!envelope) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
153
|
-
|
|
173
|
+
if (!envelope) return fail(`No response for "${c.method}"`);
|
|
174
|
+
if (duplicated.has(index)) {
|
|
175
|
+
return fail(`More than one response carried id ${index}`);
|
|
176
|
+
}
|
|
177
|
+
if (envelope.jsonrpc !== "2.0") {
|
|
178
|
+
return fail(
|
|
179
|
+
`Malformed JSON-RPC response for "${c.method}" (bad jsonrpc version)`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const hasResult = "result" in envelope;
|
|
183
|
+
// §5: mutually exclusive. See the single-call path.
|
|
184
|
+
if (hasResult && envelope.error !== undefined) {
|
|
185
|
+
return fail(
|
|
186
|
+
`Malformed JSON-RPC response for "${c.method}" (carries both result and error)`,
|
|
187
|
+
);
|
|
154
188
|
}
|
|
155
189
|
if (envelope.error !== undefined) {
|
|
156
190
|
return { ok: false, error: toRpcError(envelope.error) };
|
|
157
191
|
}
|
|
192
|
+
// A conformant success carries `result` — any JSON value, null
|
|
193
|
+
// included — so its absence is malformed, not an undefined value.
|
|
194
|
+
if (!hasResult) {
|
|
195
|
+
return fail(
|
|
196
|
+
`JSON-RPC response for "${c.method}" has neither result nor error`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
158
199
|
const value = c.parse ? c.parse(envelope.result) : envelope.result;
|
|
159
200
|
return { ok: true, value };
|
|
160
201
|
});
|