@atproto/xrpc-server 0.4.4 → 0.5.1
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/CHANGELOG.md +19 -0
- package/dist/auth.d.ts +1 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +124 -0
- package/dist/auth.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -53682
- package/dist/index.js.map +1 -7
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +7 -0
- package/dist/logger.js.map +1 -0
- package/dist/rate-limiter.d.ts +1 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +166 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/server.d.ts +5 -4
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +472 -0
- package/dist/server.js.map +1 -0
- package/dist/stream/frames.d.ts +2 -1
- package/dist/stream/frames.d.ts.map +1 -0
- package/dist/stream/frames.js +141 -0
- package/dist/stream/frames.js.map +1 -0
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.d.ts.map +1 -0
- package/dist/stream/index.js +22 -0
- package/dist/stream/index.js.map +1 -0
- package/dist/stream/logger.d.ts +1 -0
- package/dist/stream/logger.d.ts.map +1 -0
- package/dist/stream/logger.js +7 -0
- package/dist/stream/logger.js.map +1 -0
- package/dist/stream/server.d.ts +2 -0
- package/dist/stream/server.d.ts.map +1 -0
- package/dist/stream/server.js +70 -0
- package/dist/stream/server.js.map +1 -0
- package/dist/stream/stream.d.ts +1 -0
- package/dist/stream/stream.d.ts.map +1 -0
- package/dist/stream/stream.js +44 -0
- package/dist/stream/stream.js.map +1 -0
- package/dist/stream/subscription.d.ts +2 -0
- package/dist/stream/subscription.d.ts.map +1 -0
- package/dist/stream/subscription.js +80 -0
- package/dist/stream/subscription.js.map +1 -0
- package/dist/stream/types.d.ts +1 -0
- package/dist/stream/types.d.ts.map +1 -0
- package/dist/stream/types.js +47 -0
- package/dist/stream/types.js.map +1 -0
- package/dist/stream/websocket-keepalive.d.ts +2 -0
- package/dist/stream/websocket-keepalive.d.ts.map +1 -0
- package/dist/stream/websocket-keepalive.js +160 -0
- package/dist/stream/websocket-keepalive.js.map +1 -0
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +163 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +1 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +263 -0
- package/dist/util.js.map +1 -0
- package/jest.config.js +4 -3
- package/package.json +10 -11
- package/src/server.ts +15 -12
- package/src/stream/frames.ts +1 -1
- package/src/stream/websocket-keepalive.ts +2 -1
- package/src/types.ts +8 -1
- package/src/util.ts +5 -2
- package/tests/bodies.test.ts +4 -4
- package/tests/errors.test.ts +1 -1
- package/tsconfig.build.json +6 -2
- package/tsconfig.json +3 -11
- package/tsconfig.tests.json +7 -0
- package/babel.config.js +0 -1
- package/build.js +0 -14
package/dist/types.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MethodNotImplementedError = exports.UpstreamTimeoutError = exports.NotEnoughResourcesError = exports.UpstreamFailureError = exports.InternalServerError = exports.RateLimitExceededError = exports.ForbiddenError = exports.AuthRequiredError = exports.InvalidRequestError = exports.isHandlerError = exports.XRPCError = exports.isShared = exports.handlerError = exports.handlerPipeThrough = exports.handlerSuccess = exports.handlerAuth = exports.handlerInput = void 0;
|
|
7
|
+
const http_errors_1 = require("http-errors");
|
|
8
|
+
const zod_1 = __importDefault(require("zod"));
|
|
9
|
+
const xrpc_1 = require("@atproto/xrpc");
|
|
10
|
+
exports.handlerInput = zod_1.default.object({
|
|
11
|
+
encoding: zod_1.default.string(),
|
|
12
|
+
body: zod_1.default.any(),
|
|
13
|
+
});
|
|
14
|
+
exports.handlerAuth = zod_1.default.object({
|
|
15
|
+
credentials: zod_1.default.any(),
|
|
16
|
+
artifacts: zod_1.default.any(),
|
|
17
|
+
});
|
|
18
|
+
exports.handlerSuccess = zod_1.default.object({
|
|
19
|
+
encoding: zod_1.default.string(),
|
|
20
|
+
body: zod_1.default.any(),
|
|
21
|
+
headers: zod_1.default.record(zod_1.default.string()).optional(),
|
|
22
|
+
});
|
|
23
|
+
exports.handlerPipeThrough = zod_1.default.object({
|
|
24
|
+
encoding: zod_1.default.string(),
|
|
25
|
+
buffer: zod_1.default.instanceof(ArrayBuffer),
|
|
26
|
+
headers: zod_1.default.record(zod_1.default.string()).optional(),
|
|
27
|
+
});
|
|
28
|
+
exports.handlerError = zod_1.default.object({
|
|
29
|
+
status: zod_1.default.number(),
|
|
30
|
+
error: zod_1.default.string().optional(),
|
|
31
|
+
message: zod_1.default.string().optional(),
|
|
32
|
+
});
|
|
33
|
+
const isShared = (opts) => {
|
|
34
|
+
return typeof opts['name'] === 'string';
|
|
35
|
+
};
|
|
36
|
+
exports.isShared = isShared;
|
|
37
|
+
class XRPCError extends Error {
|
|
38
|
+
constructor(type, errorMessage, customErrorName) {
|
|
39
|
+
super(errorMessage);
|
|
40
|
+
Object.defineProperty(this, "type", {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
writable: true,
|
|
44
|
+
value: type
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(this, "errorMessage", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
configurable: true,
|
|
49
|
+
writable: true,
|
|
50
|
+
value: errorMessage
|
|
51
|
+
});
|
|
52
|
+
Object.defineProperty(this, "customErrorName", {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
configurable: true,
|
|
55
|
+
writable: true,
|
|
56
|
+
value: customErrorName
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
get payload() {
|
|
60
|
+
return {
|
|
61
|
+
error: this.customErrorName ?? this.typeName,
|
|
62
|
+
message: this.type === xrpc_1.ResponseType.InternalServerError
|
|
63
|
+
? this.typeStr // Do not respond with error details for 500s
|
|
64
|
+
: this.errorMessage || this.typeStr,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
get typeName() {
|
|
68
|
+
return xrpc_1.ResponseTypeNames[this.type];
|
|
69
|
+
}
|
|
70
|
+
get typeStr() {
|
|
71
|
+
return xrpc_1.ResponseTypeStrings[this.type];
|
|
72
|
+
}
|
|
73
|
+
static fromError(error) {
|
|
74
|
+
if (error instanceof XRPCError) {
|
|
75
|
+
return error;
|
|
76
|
+
}
|
|
77
|
+
let resultErr;
|
|
78
|
+
if ((0, http_errors_1.isHttpError)(error)) {
|
|
79
|
+
resultErr = new XRPCError(error.status, error.message, error.name);
|
|
80
|
+
}
|
|
81
|
+
else if (isHandlerError(error)) {
|
|
82
|
+
resultErr = new XRPCError(error.status, error.message, error.error);
|
|
83
|
+
}
|
|
84
|
+
else if (error instanceof Error) {
|
|
85
|
+
resultErr = new InternalServerError(error.message);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
resultErr = new InternalServerError('Unexpected internal server error');
|
|
89
|
+
}
|
|
90
|
+
resultErr.cause = error;
|
|
91
|
+
return resultErr;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.XRPCError = XRPCError;
|
|
95
|
+
function isHandlerError(v) {
|
|
96
|
+
return (!!v &&
|
|
97
|
+
typeof v === 'object' &&
|
|
98
|
+
typeof v['status'] === 'number' &&
|
|
99
|
+
(v['error'] === undefined || typeof v['error'] === 'string') &&
|
|
100
|
+
(v['message'] === undefined || typeof v['message'] === 'string'));
|
|
101
|
+
}
|
|
102
|
+
exports.isHandlerError = isHandlerError;
|
|
103
|
+
class InvalidRequestError extends XRPCError {
|
|
104
|
+
constructor(errorMessage, customErrorName) {
|
|
105
|
+
super(xrpc_1.ResponseType.InvalidRequest, errorMessage, customErrorName);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.InvalidRequestError = InvalidRequestError;
|
|
109
|
+
class AuthRequiredError extends XRPCError {
|
|
110
|
+
constructor(errorMessage, customErrorName) {
|
|
111
|
+
super(xrpc_1.ResponseType.AuthRequired, errorMessage, customErrorName);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.AuthRequiredError = AuthRequiredError;
|
|
115
|
+
class ForbiddenError extends XRPCError {
|
|
116
|
+
constructor(errorMessage, customErrorName) {
|
|
117
|
+
super(xrpc_1.ResponseType.Forbidden, errorMessage, customErrorName);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.ForbiddenError = ForbiddenError;
|
|
121
|
+
class RateLimitExceededError extends XRPCError {
|
|
122
|
+
constructor(status, errorMessage, customErrorName) {
|
|
123
|
+
super(xrpc_1.ResponseType.RateLimitExceeded, errorMessage, customErrorName);
|
|
124
|
+
Object.defineProperty(this, "status", {
|
|
125
|
+
enumerable: true,
|
|
126
|
+
configurable: true,
|
|
127
|
+
writable: true,
|
|
128
|
+
value: status
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
exports.RateLimitExceededError = RateLimitExceededError;
|
|
133
|
+
class InternalServerError extends XRPCError {
|
|
134
|
+
constructor(errorMessage, customErrorName) {
|
|
135
|
+
super(xrpc_1.ResponseType.InternalServerError, errorMessage, customErrorName);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exports.InternalServerError = InternalServerError;
|
|
139
|
+
class UpstreamFailureError extends XRPCError {
|
|
140
|
+
constructor(errorMessage, customErrorName) {
|
|
141
|
+
super(xrpc_1.ResponseType.UpstreamFailure, errorMessage, customErrorName);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.UpstreamFailureError = UpstreamFailureError;
|
|
145
|
+
class NotEnoughResourcesError extends XRPCError {
|
|
146
|
+
constructor(errorMessage, customErrorName) {
|
|
147
|
+
super(xrpc_1.ResponseType.NotEnoughResources, errorMessage, customErrorName);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.NotEnoughResourcesError = NotEnoughResourcesError;
|
|
151
|
+
class UpstreamTimeoutError extends XRPCError {
|
|
152
|
+
constructor(errorMessage, customErrorName) {
|
|
153
|
+
super(xrpc_1.ResponseType.UpstreamTimeout, errorMessage, customErrorName);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
exports.UpstreamTimeoutError = UpstreamTimeoutError;
|
|
157
|
+
class MethodNotImplementedError extends XRPCError {
|
|
158
|
+
constructor(errorMessage, customErrorName) {
|
|
159
|
+
super(xrpc_1.ResponseType.MethodNotImplemented, errorMessage, customErrorName);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.MethodNotImplementedError = MethodNotImplementedError;
|
|
163
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/util.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAGA,OAAO,OAAO,MAAM,SAAS,CAAA;AAE7B,OAAO,EAEL,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACpB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,eAAe,EACf,MAAM,EACN,YAAY,EACZ,cAAc,EAKd,SAAS,EACV,MAAM,SAAS,CAAA;AAEhB,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,gBAAgB,GAAG,YAAY,GAAG,mBAAmB,EAC1D,MAAM,EAAE,eAAe,GACtB,MAAM,CAmBR;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAcvC;AAED,wBAAgB,cAAc,CAAC,GAAG,SAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAU1E;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,GAAG,YAAY,EACpC,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,QAAQ,GACjB,YAAY,GAAG,SAAS,CA8D1B;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,GAAG,YAAY,EACpC,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,QAAQ,EAAE,QAAQ,GACjB,cAAc,GAAG,SAAS,CA0C5B;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,OAOtC;AAUD,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,6BAI3C;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAM5E;AA0CD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,UASzD;AAED,qBAAa,WAAY,YAAW,YAAY;IAIrC,IAAI,EAAE,MAAM;IACZ,WAAW,CAAC;IAJd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACxB,OAAO,CAAC,OAAO,CAAC,CAAQ;gBAEf,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,oBAAQ;IAE7B,KAAK;IAIL,IAAI;CAKL;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB"}
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ServerTimer = exports.serverTimingHeader = exports.processBodyAsBytes = exports.hasBody = exports.normalizeMime = exports.validateOutput = exports.validateInput = exports.getQueryParams = exports.decodeQueryParam = exports.decodeQueryParams = void 0;
|
|
7
|
+
const assert_1 = __importDefault(require("assert"));
|
|
8
|
+
const zlib_1 = require("zlib");
|
|
9
|
+
const mime_types_1 = __importDefault(require("mime-types"));
|
|
10
|
+
const lexicon_1 = require("@atproto/lexicon");
|
|
11
|
+
const common_1 = require("@atproto/common");
|
|
12
|
+
const types_1 = require("./types");
|
|
13
|
+
function decodeQueryParams(def, params) {
|
|
14
|
+
const decoded = {};
|
|
15
|
+
for (const k in params) {
|
|
16
|
+
const val = params[k];
|
|
17
|
+
const property = def.parameters?.properties?.[k];
|
|
18
|
+
if (property) {
|
|
19
|
+
if (property.type === 'array') {
|
|
20
|
+
const vals = [];
|
|
21
|
+
decoded[k] = val
|
|
22
|
+
? vals
|
|
23
|
+
.concat(val) // Cast to array
|
|
24
|
+
.flatMap((v) => decodeQueryParam(property.items.type, v) ?? [])
|
|
25
|
+
: undefined;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
decoded[k] = decodeQueryParam(property.type, val);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return decoded;
|
|
33
|
+
}
|
|
34
|
+
exports.decodeQueryParams = decodeQueryParams;
|
|
35
|
+
function decodeQueryParam(type, value) {
|
|
36
|
+
if (!value) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
if (type === 'string' || type === 'datetime') {
|
|
40
|
+
return String(value);
|
|
41
|
+
}
|
|
42
|
+
if (type === 'float') {
|
|
43
|
+
return Number(String(value));
|
|
44
|
+
}
|
|
45
|
+
else if (type === 'integer') {
|
|
46
|
+
return parseInt(String(value), 10) || 0;
|
|
47
|
+
}
|
|
48
|
+
else if (type === 'boolean') {
|
|
49
|
+
return value === 'true';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.decodeQueryParam = decodeQueryParam;
|
|
53
|
+
function getQueryParams(url = '') {
|
|
54
|
+
const { searchParams } = new URL(url ?? '', 'http://x');
|
|
55
|
+
const result = {};
|
|
56
|
+
for (const key of searchParams.keys()) {
|
|
57
|
+
result[key] = searchParams.getAll(key);
|
|
58
|
+
if (result[key].length === 1) {
|
|
59
|
+
result[key] = result[key][0];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
exports.getQueryParams = getQueryParams;
|
|
65
|
+
function validateInput(nsid, def, req, opts, lexicons) {
|
|
66
|
+
// request expectation
|
|
67
|
+
const reqHasBody = hasBody(req);
|
|
68
|
+
if (reqHasBody && (def.type !== 'procedure' || !def.input)) {
|
|
69
|
+
throw new types_1.InvalidRequestError(`A request body was provided when none was expected`);
|
|
70
|
+
}
|
|
71
|
+
if (def.type === 'query') {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (!reqHasBody && def.input) {
|
|
75
|
+
throw new types_1.InvalidRequestError(`A request body is expected but none was provided`);
|
|
76
|
+
}
|
|
77
|
+
// mimetype
|
|
78
|
+
const inputEncoding = normalizeMime(req.headers['content-type'] || '');
|
|
79
|
+
if (def.input?.encoding &&
|
|
80
|
+
(!inputEncoding || !isValidEncoding(def.input?.encoding, inputEncoding))) {
|
|
81
|
+
if (!inputEncoding) {
|
|
82
|
+
throw new types_1.InvalidRequestError(`Request encoding (Content-Type) required but not provided`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
throw new types_1.InvalidRequestError(`Wrong request encoding (Content-Type): ${inputEncoding}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!inputEncoding) {
|
|
89
|
+
// no input body
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
// if input schema, validate
|
|
93
|
+
if (def.input?.schema) {
|
|
94
|
+
try {
|
|
95
|
+
const lexBody = req.body ? (0, lexicon_1.jsonToLex)(req.body) : req.body;
|
|
96
|
+
req.body = lexicons.assertValidXrpcInput(nsid, lexBody);
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
throw new types_1.InvalidRequestError(e instanceof Error ? e.message : String(e));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// if middleware already got the body, we pass that along as input
|
|
103
|
+
// otherwise, we pass along a decoded readable stream
|
|
104
|
+
let body;
|
|
105
|
+
if (req.readableEnded) {
|
|
106
|
+
body = req.body;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
body = decodeBodyStream(req, opts.blobLimit);
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
encoding: inputEncoding,
|
|
113
|
+
body,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
exports.validateInput = validateInput;
|
|
117
|
+
function validateOutput(nsid, def, output, lexicons) {
|
|
118
|
+
// initial validation
|
|
119
|
+
if (output) {
|
|
120
|
+
types_1.handlerSuccess.parse(output);
|
|
121
|
+
}
|
|
122
|
+
// response expectation
|
|
123
|
+
if (output?.body && !def.output) {
|
|
124
|
+
throw new types_1.InternalServerError(`A response body was provided when none was expected`);
|
|
125
|
+
}
|
|
126
|
+
if (!output?.body && def.output) {
|
|
127
|
+
throw new types_1.InternalServerError(`A response body is expected but none was provided`);
|
|
128
|
+
}
|
|
129
|
+
// mimetype
|
|
130
|
+
if (def.output?.encoding &&
|
|
131
|
+
(!output?.encoding ||
|
|
132
|
+
!isValidEncoding(def.output?.encoding, output?.encoding))) {
|
|
133
|
+
throw new types_1.InternalServerError(`Invalid response encoding: ${output?.encoding}`);
|
|
134
|
+
}
|
|
135
|
+
// output schema
|
|
136
|
+
if (def.output?.schema) {
|
|
137
|
+
try {
|
|
138
|
+
const result = lexicons.assertValidXrpcOutput(nsid, output?.body);
|
|
139
|
+
if (output) {
|
|
140
|
+
output.body = result;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
throw new types_1.InternalServerError(e instanceof Error ? e.message : String(e));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return output;
|
|
148
|
+
}
|
|
149
|
+
exports.validateOutput = validateOutput;
|
|
150
|
+
function normalizeMime(v) {
|
|
151
|
+
if (!v)
|
|
152
|
+
return false;
|
|
153
|
+
const fullType = mime_types_1.default.contentType(v);
|
|
154
|
+
if (!fullType)
|
|
155
|
+
return false;
|
|
156
|
+
const shortType = fullType.split(';')[0];
|
|
157
|
+
if (!shortType)
|
|
158
|
+
return false;
|
|
159
|
+
return shortType;
|
|
160
|
+
}
|
|
161
|
+
exports.normalizeMime = normalizeMime;
|
|
162
|
+
function isValidEncoding(possibleStr, value) {
|
|
163
|
+
const possible = possibleStr.split(',').map((v) => v.trim());
|
|
164
|
+
const normalized = normalizeMime(value);
|
|
165
|
+
if (!normalized)
|
|
166
|
+
return false;
|
|
167
|
+
if (possible.includes('*/*'))
|
|
168
|
+
return true;
|
|
169
|
+
return possible.includes(normalized);
|
|
170
|
+
}
|
|
171
|
+
function hasBody(req) {
|
|
172
|
+
const contentLength = req.headers['content-length'];
|
|
173
|
+
const transferEncoding = req.headers['transfer-encoding'];
|
|
174
|
+
return (contentLength && parseInt(contentLength, 10) > 0) || transferEncoding;
|
|
175
|
+
}
|
|
176
|
+
exports.hasBody = hasBody;
|
|
177
|
+
function processBodyAsBytes(req) {
|
|
178
|
+
return new Promise((resolve) => {
|
|
179
|
+
const chunks = [];
|
|
180
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
181
|
+
req.on('end', () => resolve(new Uint8Array(Buffer.concat(chunks))));
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
exports.processBodyAsBytes = processBodyAsBytes;
|
|
185
|
+
function decodeBodyStream(req, maxSize) {
|
|
186
|
+
let stream = req;
|
|
187
|
+
const contentEncoding = req.headers['content-encoding'];
|
|
188
|
+
const contentLength = req.headers['content-length'];
|
|
189
|
+
if (maxSize !== undefined &&
|
|
190
|
+
contentLength &&
|
|
191
|
+
parseInt(contentLength, 10) > maxSize) {
|
|
192
|
+
throw new types_1.XRPCError(413, 'request entity too large');
|
|
193
|
+
}
|
|
194
|
+
let decoder;
|
|
195
|
+
if (contentEncoding === 'gzip') {
|
|
196
|
+
decoder = (0, zlib_1.createGunzip)();
|
|
197
|
+
}
|
|
198
|
+
else if (contentEncoding === 'deflate') {
|
|
199
|
+
decoder = (0, zlib_1.createDeflate)();
|
|
200
|
+
}
|
|
201
|
+
if (decoder) {
|
|
202
|
+
(0, common_1.forwardStreamErrors)(stream, decoder);
|
|
203
|
+
stream = stream.pipe(decoder);
|
|
204
|
+
}
|
|
205
|
+
if (maxSize !== undefined) {
|
|
206
|
+
const maxSizeChecker = new common_1.MaxSizeChecker(maxSize, () => new types_1.XRPCError(413, 'request entity too large'));
|
|
207
|
+
(0, common_1.forwardStreamErrors)(stream, maxSizeChecker);
|
|
208
|
+
stream = stream.pipe(maxSizeChecker);
|
|
209
|
+
}
|
|
210
|
+
return stream;
|
|
211
|
+
}
|
|
212
|
+
function serverTimingHeader(timings) {
|
|
213
|
+
return timings
|
|
214
|
+
.map((timing) => {
|
|
215
|
+
let header = timing.name;
|
|
216
|
+
if (timing.duration)
|
|
217
|
+
header += `;dur=${timing.duration}`;
|
|
218
|
+
if (timing.description)
|
|
219
|
+
header += `;desc="${timing.description}"`;
|
|
220
|
+
return header;
|
|
221
|
+
})
|
|
222
|
+
.join(', ');
|
|
223
|
+
}
|
|
224
|
+
exports.serverTimingHeader = serverTimingHeader;
|
|
225
|
+
class ServerTimer {
|
|
226
|
+
constructor(name, description) {
|
|
227
|
+
Object.defineProperty(this, "name", {
|
|
228
|
+
enumerable: true,
|
|
229
|
+
configurable: true,
|
|
230
|
+
writable: true,
|
|
231
|
+
value: name
|
|
232
|
+
});
|
|
233
|
+
Object.defineProperty(this, "description", {
|
|
234
|
+
enumerable: true,
|
|
235
|
+
configurable: true,
|
|
236
|
+
writable: true,
|
|
237
|
+
value: description
|
|
238
|
+
});
|
|
239
|
+
Object.defineProperty(this, "duration", {
|
|
240
|
+
enumerable: true,
|
|
241
|
+
configurable: true,
|
|
242
|
+
writable: true,
|
|
243
|
+
value: void 0
|
|
244
|
+
});
|
|
245
|
+
Object.defineProperty(this, "startMs", {
|
|
246
|
+
enumerable: true,
|
|
247
|
+
configurable: true,
|
|
248
|
+
writable: true,
|
|
249
|
+
value: void 0
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
start() {
|
|
253
|
+
this.startMs = Date.now();
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
stop() {
|
|
257
|
+
(0, assert_1.default)(this.startMs, "timer hasn't been started");
|
|
258
|
+
this.duration = Date.now() - this.startMs;
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
exports.ServerTimer = ServerTimer;
|
|
263
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/jest.config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/** @type {import('jest').Config} */
|
|
3
2
|
module.exports = {
|
|
4
|
-
...base,
|
|
5
3
|
displayName: 'XRPC Server',
|
|
4
|
+
transform: { '^.+\\.(t|j)s$': '@swc/jest' },
|
|
5
|
+
transformIgnorePatterns: [`<rootDir>/node_modules/(?!get-port)`],
|
|
6
|
+
setupFiles: ['<rootDir>/../../jest.setup.ts'],
|
|
6
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/xrpc-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "atproto HTTP API (XRPC) server library",
|
|
6
6
|
"keywords": [
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"directory": "packages/xrpc-server"
|
|
15
15
|
},
|
|
16
16
|
"main": "dist/index.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
17
18
|
"dependencies": {
|
|
18
19
|
"cbor-x": "^1.5.1",
|
|
19
20
|
"express": "^4.17.2",
|
|
@@ -23,9 +24,9 @@
|
|
|
23
24
|
"uint8arrays": "3.0.0",
|
|
24
25
|
"ws": "^8.12.0",
|
|
25
26
|
"zod": "^3.21.4",
|
|
26
|
-
"@atproto/common": "^0.
|
|
27
|
-
"@atproto/crypto": "^0.
|
|
28
|
-
"@atproto/lexicon": "^0.
|
|
27
|
+
"@atproto/common": "^0.4.0",
|
|
28
|
+
"@atproto/crypto": "^0.4.0",
|
|
29
|
+
"@atproto/lexicon": "^0.4.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@types/express": "^4.17.13",
|
|
@@ -34,16 +35,14 @@
|
|
|
34
35
|
"@types/ws": "^8.5.4",
|
|
35
36
|
"get-port": "^6.1.2",
|
|
36
37
|
"jose": "^4.15.4",
|
|
38
|
+
"jest": "^28.1.2",
|
|
37
39
|
"key-encoder": "^2.0.3",
|
|
38
40
|
"multiformats": "^9.9.0",
|
|
39
|
-
"@atproto/crypto": "^0.
|
|
40
|
-
"@atproto/xrpc": "^0.
|
|
41
|
+
"@atproto/crypto": "^0.4.0",
|
|
42
|
+
"@atproto/xrpc": "^0.5.0"
|
|
41
43
|
},
|
|
42
44
|
"scripts": {
|
|
43
45
|
"test": "jest",
|
|
44
|
-
"build": "
|
|
45
|
-
|
|
46
|
-
"update-main-to-dist": "node ../../update-main-to-dist.js packages/xrpc-server"
|
|
47
|
-
},
|
|
48
|
-
"types": "dist/index.d.ts"
|
|
46
|
+
"build": "tsc --build tsconfig.build.json"
|
|
47
|
+
}
|
|
49
48
|
}
|
package/src/server.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Readable } from 'stream'
|
|
2
2
|
import express, {
|
|
3
|
+
Application,
|
|
4
|
+
Express,
|
|
5
|
+
Router,
|
|
6
|
+
Request,
|
|
7
|
+
Response,
|
|
3
8
|
ErrorRequestHandler,
|
|
4
9
|
NextFunction,
|
|
5
10
|
RequestHandler,
|
|
@@ -37,7 +42,6 @@ import {
|
|
|
37
42
|
isShared,
|
|
38
43
|
RateLimitExceededError,
|
|
39
44
|
HandlerPipeThrough,
|
|
40
|
-
handlerPipeThrough,
|
|
41
45
|
} from './types'
|
|
42
46
|
import {
|
|
43
47
|
decodeQueryParams,
|
|
@@ -53,8 +57,8 @@ export function createServer(lexicons?: LexiconDoc[], options?: Options) {
|
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
export class Server {
|
|
56
|
-
router = express()
|
|
57
|
-
routes = express.Router()
|
|
60
|
+
router: Express = express()
|
|
61
|
+
routes: Router = express.Router()
|
|
58
62
|
subscriptions = new Map<string, XrpcStreamServer>()
|
|
59
63
|
lex = new Lexicons()
|
|
60
64
|
options: Options
|
|
@@ -68,9 +72,12 @@ export class Server {
|
|
|
68
72
|
this.addLexicons(lexicons)
|
|
69
73
|
}
|
|
70
74
|
this.router.use(this.routes)
|
|
71
|
-
this.router.use(
|
|
75
|
+
this.router.use(
|
|
76
|
+
'/xrpc/:methodId',
|
|
77
|
+
opts?.catchall ?? this.catchall.bind(this),
|
|
78
|
+
)
|
|
72
79
|
this.router.use(errorMiddleware)
|
|
73
|
-
this.router.once('mount', (app:
|
|
80
|
+
this.router.once('mount', (app: Application) => {
|
|
74
81
|
this.enableStreamingOnListen(app)
|
|
75
82
|
})
|
|
76
83
|
this.options = opts ?? {}
|
|
@@ -179,11 +186,7 @@ export class Server {
|
|
|
179
186
|
)
|
|
180
187
|
}
|
|
181
188
|
|
|
182
|
-
async catchall(
|
|
183
|
-
req: express.Request,
|
|
184
|
-
_res: express.Response,
|
|
185
|
-
next: NextFunction,
|
|
186
|
-
) {
|
|
189
|
+
async catchall(req: Request, _res: Response, next: NextFunction) {
|
|
187
190
|
const def = this.lex.getDef(req.params.methodId)
|
|
188
191
|
if (!def) {
|
|
189
192
|
return next(new MethodNotImplementedError())
|
|
@@ -213,7 +216,7 @@ export class Server {
|
|
|
213
216
|
const routeOpts = {
|
|
214
217
|
blobLimit: routeCfg.opts?.blobLimit ?? this.options.payload?.blobLimit,
|
|
215
218
|
}
|
|
216
|
-
const validateReqInput = (req:
|
|
219
|
+
const validateReqInput = (req: Request) =>
|
|
217
220
|
validateInput(nsid, def, req, routeOpts, this.lex)
|
|
218
221
|
const validateResOutput =
|
|
219
222
|
this.options.validateResponse === false
|
|
@@ -390,7 +393,7 @@ export class Server {
|
|
|
390
393
|
)
|
|
391
394
|
}
|
|
392
395
|
|
|
393
|
-
private enableStreamingOnListen(app:
|
|
396
|
+
private enableStreamingOnListen(app: Application) {
|
|
394
397
|
const _listen = app.listen
|
|
395
398
|
app.listen = (...args) => {
|
|
396
399
|
// @ts-ignore the args spread
|
package/src/stream/frames.ts
CHANGED
|
@@ -80,7 +80,7 @@ export class WebSocketKeepAlive {
|
|
|
80
80
|
|
|
81
81
|
startHeartbeat(ws: WebSocket) {
|
|
82
82
|
let isAlive = true
|
|
83
|
-
let heartbeatInterval: NodeJS.
|
|
83
|
+
let heartbeatInterval: NodeJS.Timeout | null = null
|
|
84
84
|
|
|
85
85
|
const checkAlive = () => {
|
|
86
86
|
if (!isAlive) {
|
|
@@ -145,6 +145,7 @@ function forwardSignal(signal: AbortSignal, ac: AbortController) {
|
|
|
145
145
|
return ac.abort(signal.reason)
|
|
146
146
|
} else {
|
|
147
147
|
signal.addEventListener('abort', () => ac.abort(signal.reason), {
|
|
148
|
+
// @ts-ignore https://github.com/DefinitelyTyped/DefinitelyTyped/pull/68625
|
|
148
149
|
signal: ac.signal,
|
|
149
150
|
})
|
|
150
151
|
}
|
package/src/types.ts
CHANGED
|
@@ -8,8 +8,15 @@ import {
|
|
|
8
8
|
ResponseTypeNames,
|
|
9
9
|
} from '@atproto/xrpc'
|
|
10
10
|
|
|
11
|
+
export type CatchallHandler = (
|
|
12
|
+
req: express.Request,
|
|
13
|
+
_res: express.Response,
|
|
14
|
+
next: express.NextFunction,
|
|
15
|
+
) => unknown
|
|
16
|
+
|
|
11
17
|
export type Options = {
|
|
12
18
|
validateResponse?: boolean
|
|
19
|
+
catchall?: CatchallHandler
|
|
13
20
|
payload?: {
|
|
14
21
|
jsonLimit?: number
|
|
15
22
|
blobLimit?: number
|
|
@@ -22,7 +29,7 @@ export type Options = {
|
|
|
22
29
|
}
|
|
23
30
|
}
|
|
24
31
|
|
|
25
|
-
export type UndecodedParams = typeof express.request['query']
|
|
32
|
+
export type UndecodedParams = (typeof express.request)['query']
|
|
26
33
|
|
|
27
34
|
export type Primitive = string | number | boolean
|
|
28
35
|
export type Params = Record<string, Primitive | Primitive[] | undefined>
|
package/src/util.ts
CHANGED
|
@@ -33,7 +33,7 @@ export function decodeQueryParams(
|
|
|
33
33
|
const property = def.parameters?.properties?.[k]
|
|
34
34
|
if (property) {
|
|
35
35
|
if (property.type === 'array') {
|
|
36
|
-
const vals: typeof val[] = []
|
|
36
|
+
const vals: (typeof val)[] = []
|
|
37
37
|
decoded[k] = val
|
|
38
38
|
? vals
|
|
39
39
|
.concat(val) // Cast to array
|
|
@@ -282,7 +282,10 @@ export function serverTimingHeader(timings: ServerTiming[]) {
|
|
|
282
282
|
export class ServerTimer implements ServerTiming {
|
|
283
283
|
public duration?: number
|
|
284
284
|
private startMs?: number
|
|
285
|
-
constructor(
|
|
285
|
+
constructor(
|
|
286
|
+
public name: string,
|
|
287
|
+
public description?: string,
|
|
288
|
+
) {}
|
|
286
289
|
start() {
|
|
287
290
|
this.startMs = Date.now()
|
|
288
291
|
return this
|