document-rest 1.4.89 → 1.4.91
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/bin.js +21 -11
- package/dist/index.cjs +18 -10
- package/dist/index.js +18 -10
- package/package.json +4 -4
package/dist/bin.js
CHANGED
|
@@ -5,11 +5,18 @@ import { OdbReportNotSpecifiedError, OdmUnresolvedSectionError } from "documents
|
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/server.ts
|
|
7
7
|
const OPERATIONS_BY_NAME = new Map(DOCUMENT_OPERATIONS.map((operation) => [operation.name, operation]));
|
|
8
|
+
/** The HTTP status codes this server ever sends, named for readability at each call site rather than repeated as bare literals. Exported so tests can assert against the same names rather than re-declaring the numbers. */
|
|
9
|
+
const HTTP_STATUS = {
|
|
10
|
+
ok: 200,
|
|
11
|
+
badRequest: 400,
|
|
12
|
+
notFound: 404,
|
|
13
|
+
methodNotAllowed: 405
|
|
14
|
+
};
|
|
8
15
|
function buildErrorMappers() {
|
|
9
16
|
return /* @__PURE__ */ new Map([["odb_render_report", (error) => {
|
|
10
17
|
if (!(error instanceof OdbReportNotSpecifiedError)) return void 0;
|
|
11
18
|
return {
|
|
12
|
-
status:
|
|
19
|
+
status: HTTP_STATUS.badRequest,
|
|
13
20
|
body: {
|
|
14
21
|
error: error.message,
|
|
15
22
|
availableReports: error.availableReports
|
|
@@ -18,7 +25,7 @@ function buildErrorMappers() {
|
|
|
18
25
|
}], ["odm_to_pdf", (error) => {
|
|
19
26
|
if (!(error instanceof OdmUnresolvedSectionError)) return void 0;
|
|
20
27
|
return {
|
|
21
|
-
status:
|
|
28
|
+
status: HTTP_STATUS.badRequest,
|
|
22
29
|
body: {
|
|
23
30
|
error: `${error.message} Pass chaptersDir containing these files, or an explicit chapters override, for each href.`,
|
|
24
31
|
hrefs: error.hrefs
|
|
@@ -50,26 +57,27 @@ async function handleOperationRequest(req, res, operation) {
|
|
|
50
57
|
try {
|
|
51
58
|
rawBody = await readJsonBody(req);
|
|
52
59
|
} catch (error) {
|
|
53
|
-
sendJson(res,
|
|
60
|
+
sendJson(res, HTTP_STATUS.badRequest, { error: `Request body is not valid JSON: ${error instanceof Error ? error.message : String(error)}` });
|
|
54
61
|
return;
|
|
55
62
|
}
|
|
56
63
|
const parsed = operation.inputSchema.safeParse(rawBody);
|
|
57
64
|
if (!parsed.success) {
|
|
58
|
-
sendJson(res,
|
|
65
|
+
sendJson(res, HTTP_STATUS.badRequest, {
|
|
59
66
|
error: "Request body failed validation.",
|
|
60
67
|
issues: z.treeifyError(parsed.error)
|
|
61
68
|
});
|
|
62
69
|
return;
|
|
63
70
|
}
|
|
64
71
|
try {
|
|
65
|
-
|
|
72
|
+
const result = await operation.run(parsed.data, { signal: abortSignalFor(res) });
|
|
73
|
+
sendJson(res, HTTP_STATUS.ok, { result });
|
|
66
74
|
} catch (error) {
|
|
67
75
|
const mapped = buildErrorMappers().get(operation.name)?.(error);
|
|
68
76
|
if (mapped !== void 0) {
|
|
69
77
|
sendJson(res, mapped.status, mapped.body);
|
|
70
78
|
return;
|
|
71
79
|
}
|
|
72
|
-
sendJson(res,
|
|
80
|
+
sendJson(res, HTTP_STATUS.badRequest, { error: error instanceof Error ? error.message : String(error) });
|
|
73
81
|
}
|
|
74
82
|
}
|
|
75
83
|
function listOperations() {
|
|
@@ -86,22 +94,22 @@ function createRestServer() {
|
|
|
86
94
|
return createServer((req, res) => {
|
|
87
95
|
(async () => {
|
|
88
96
|
if (req.url === void 0 || req.method === void 0) {
|
|
89
|
-
sendJson(res,
|
|
97
|
+
sendJson(res, HTTP_STATUS.badRequest, { error: "Malformed request: no url or method." });
|
|
90
98
|
return;
|
|
91
99
|
}
|
|
92
100
|
const url = new URL(req.url, "http://localhost");
|
|
93
101
|
if (url.pathname === "/" && req.method === "GET") {
|
|
94
|
-
sendJson(res,
|
|
102
|
+
sendJson(res, HTTP_STATUS.ok, listOperations());
|
|
95
103
|
return;
|
|
96
104
|
}
|
|
97
105
|
const name = url.pathname.slice(1);
|
|
98
106
|
const operation = OPERATIONS_BY_NAME.get(name);
|
|
99
107
|
if (operation === void 0) {
|
|
100
|
-
sendJson(res,
|
|
108
|
+
sendJson(res, HTTP_STATUS.notFound, { error: `No operation named "${name}". GET / lists every available operation.` });
|
|
101
109
|
return;
|
|
102
110
|
}
|
|
103
111
|
if (req.method !== "POST") {
|
|
104
|
-
sendJson(res,
|
|
112
|
+
sendJson(res, HTTP_STATUS.methodNotAllowed, { error: `${name} only accepts POST, received ${req.method}.` });
|
|
105
113
|
return;
|
|
106
114
|
}
|
|
107
115
|
await handleOperationRequest(req, res, operation);
|
|
@@ -111,6 +119,8 @@ function createRestServer() {
|
|
|
111
119
|
//#endregion
|
|
112
120
|
//#region src/cli.ts
|
|
113
121
|
const DEFAULT_PORT = 3100;
|
|
122
|
+
/** The highest valid TCP port number (16 bits, so 2^16 - 1). */
|
|
123
|
+
const MAX_TCP_PORT = 65535;
|
|
114
124
|
function readFlag(args, name) {
|
|
115
125
|
const prefix = `--${name}=`;
|
|
116
126
|
for (const [index, arg] of args.entries()) {
|
|
@@ -124,7 +134,7 @@ function readFlag(args, name) {
|
|
|
124
134
|
}
|
|
125
135
|
function parsePort(raw) {
|
|
126
136
|
const port = Number.parseInt(raw, 10);
|
|
127
|
-
if (!Number.isInteger(port) || String(port) !== raw.trim() || port < 0 || port > 65535) throw new Error(`--port must be an integer between 0 and
|
|
137
|
+
if (!Number.isInteger(port) || String(port) !== raw.trim() || port < 0 || port > 65535) throw new Error(`--port must be an integer between 0 and ${String(MAX_TCP_PORT)}, got "${raw}"`);
|
|
128
138
|
return port;
|
|
129
139
|
}
|
|
130
140
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -5,11 +5,18 @@ let documents_js = require("documents.js");
|
|
|
5
5
|
let zod = require("zod");
|
|
6
6
|
//#region src/server.ts
|
|
7
7
|
const OPERATIONS_BY_NAME = new Map(document_operations.DOCUMENT_OPERATIONS.map((operation) => [operation.name, operation]));
|
|
8
|
+
/** The HTTP status codes this server ever sends, named for readability at each call site rather than repeated as bare literals. Exported so tests can assert against the same names rather than re-declaring the numbers. */
|
|
9
|
+
const HTTP_STATUS = {
|
|
10
|
+
ok: 200,
|
|
11
|
+
badRequest: 400,
|
|
12
|
+
notFound: 404,
|
|
13
|
+
methodNotAllowed: 405
|
|
14
|
+
};
|
|
8
15
|
function buildErrorMappers() {
|
|
9
16
|
return /* @__PURE__ */ new Map([["odb_render_report", (error) => {
|
|
10
17
|
if (!(error instanceof documents_js.OdbReportNotSpecifiedError)) return void 0;
|
|
11
18
|
return {
|
|
12
|
-
status:
|
|
19
|
+
status: HTTP_STATUS.badRequest,
|
|
13
20
|
body: {
|
|
14
21
|
error: error.message,
|
|
15
22
|
availableReports: error.availableReports
|
|
@@ -18,7 +25,7 @@ function buildErrorMappers() {
|
|
|
18
25
|
}], ["odm_to_pdf", (error) => {
|
|
19
26
|
if (!(error instanceof documents_js.OdmUnresolvedSectionError)) return void 0;
|
|
20
27
|
return {
|
|
21
|
-
status:
|
|
28
|
+
status: HTTP_STATUS.badRequest,
|
|
22
29
|
body: {
|
|
23
30
|
error: `${error.message} Pass chaptersDir containing these files, or an explicit chapters override, for each href.`,
|
|
24
31
|
hrefs: error.hrefs
|
|
@@ -50,26 +57,27 @@ async function handleOperationRequest(req, res, operation) {
|
|
|
50
57
|
try {
|
|
51
58
|
rawBody = await readJsonBody(req);
|
|
52
59
|
} catch (error) {
|
|
53
|
-
sendJson(res,
|
|
60
|
+
sendJson(res, HTTP_STATUS.badRequest, { error: `Request body is not valid JSON: ${error instanceof Error ? error.message : String(error)}` });
|
|
54
61
|
return;
|
|
55
62
|
}
|
|
56
63
|
const parsed = operation.inputSchema.safeParse(rawBody);
|
|
57
64
|
if (!parsed.success) {
|
|
58
|
-
sendJson(res,
|
|
65
|
+
sendJson(res, HTTP_STATUS.badRequest, {
|
|
59
66
|
error: "Request body failed validation.",
|
|
60
67
|
issues: zod.z.treeifyError(parsed.error)
|
|
61
68
|
});
|
|
62
69
|
return;
|
|
63
70
|
}
|
|
64
71
|
try {
|
|
65
|
-
|
|
72
|
+
const result = await operation.run(parsed.data, { signal: abortSignalFor(res) });
|
|
73
|
+
sendJson(res, HTTP_STATUS.ok, { result });
|
|
66
74
|
} catch (error) {
|
|
67
75
|
const mapped = buildErrorMappers().get(operation.name)?.(error);
|
|
68
76
|
if (mapped !== void 0) {
|
|
69
77
|
sendJson(res, mapped.status, mapped.body);
|
|
70
78
|
return;
|
|
71
79
|
}
|
|
72
|
-
sendJson(res,
|
|
80
|
+
sendJson(res, HTTP_STATUS.badRequest, { error: error instanceof Error ? error.message : String(error) });
|
|
73
81
|
}
|
|
74
82
|
}
|
|
75
83
|
function listOperations() {
|
|
@@ -86,22 +94,22 @@ function createRestServer() {
|
|
|
86
94
|
return (0, node_http.createServer)((req, res) => {
|
|
87
95
|
(async () => {
|
|
88
96
|
if (req.url === void 0 || req.method === void 0) {
|
|
89
|
-
sendJson(res,
|
|
97
|
+
sendJson(res, HTTP_STATUS.badRequest, { error: "Malformed request: no url or method." });
|
|
90
98
|
return;
|
|
91
99
|
}
|
|
92
100
|
const url = new URL(req.url, "http://localhost");
|
|
93
101
|
if (url.pathname === "/" && req.method === "GET") {
|
|
94
|
-
sendJson(res,
|
|
102
|
+
sendJson(res, HTTP_STATUS.ok, listOperations());
|
|
95
103
|
return;
|
|
96
104
|
}
|
|
97
105
|
const name = url.pathname.slice(1);
|
|
98
106
|
const operation = OPERATIONS_BY_NAME.get(name);
|
|
99
107
|
if (operation === void 0) {
|
|
100
|
-
sendJson(res,
|
|
108
|
+
sendJson(res, HTTP_STATUS.notFound, { error: `No operation named "${name}". GET / lists every available operation.` });
|
|
101
109
|
return;
|
|
102
110
|
}
|
|
103
111
|
if (req.method !== "POST") {
|
|
104
|
-
sendJson(res,
|
|
112
|
+
sendJson(res, HTTP_STATUS.methodNotAllowed, { error: `${name} only accepts POST, received ${req.method}.` });
|
|
105
113
|
return;
|
|
106
114
|
}
|
|
107
115
|
await handleOperationRequest(req, res, operation);
|
package/dist/index.js
CHANGED
|
@@ -4,11 +4,18 @@ import { OdbReportNotSpecifiedError, OdmUnresolvedSectionError } from "documents
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
//#region src/server.ts
|
|
6
6
|
const OPERATIONS_BY_NAME = new Map(DOCUMENT_OPERATIONS.map((operation) => [operation.name, operation]));
|
|
7
|
+
/** The HTTP status codes this server ever sends, named for readability at each call site rather than repeated as bare literals. Exported so tests can assert against the same names rather than re-declaring the numbers. */
|
|
8
|
+
const HTTP_STATUS = {
|
|
9
|
+
ok: 200,
|
|
10
|
+
badRequest: 400,
|
|
11
|
+
notFound: 404,
|
|
12
|
+
methodNotAllowed: 405
|
|
13
|
+
};
|
|
7
14
|
function buildErrorMappers() {
|
|
8
15
|
return /* @__PURE__ */ new Map([["odb_render_report", (error) => {
|
|
9
16
|
if (!(error instanceof OdbReportNotSpecifiedError)) return void 0;
|
|
10
17
|
return {
|
|
11
|
-
status:
|
|
18
|
+
status: HTTP_STATUS.badRequest,
|
|
12
19
|
body: {
|
|
13
20
|
error: error.message,
|
|
14
21
|
availableReports: error.availableReports
|
|
@@ -17,7 +24,7 @@ function buildErrorMappers() {
|
|
|
17
24
|
}], ["odm_to_pdf", (error) => {
|
|
18
25
|
if (!(error instanceof OdmUnresolvedSectionError)) return void 0;
|
|
19
26
|
return {
|
|
20
|
-
status:
|
|
27
|
+
status: HTTP_STATUS.badRequest,
|
|
21
28
|
body: {
|
|
22
29
|
error: `${error.message} Pass chaptersDir containing these files, or an explicit chapters override, for each href.`,
|
|
23
30
|
hrefs: error.hrefs
|
|
@@ -49,26 +56,27 @@ async function handleOperationRequest(req, res, operation) {
|
|
|
49
56
|
try {
|
|
50
57
|
rawBody = await readJsonBody(req);
|
|
51
58
|
} catch (error) {
|
|
52
|
-
sendJson(res,
|
|
59
|
+
sendJson(res, HTTP_STATUS.badRequest, { error: `Request body is not valid JSON: ${error instanceof Error ? error.message : String(error)}` });
|
|
53
60
|
return;
|
|
54
61
|
}
|
|
55
62
|
const parsed = operation.inputSchema.safeParse(rawBody);
|
|
56
63
|
if (!parsed.success) {
|
|
57
|
-
sendJson(res,
|
|
64
|
+
sendJson(res, HTTP_STATUS.badRequest, {
|
|
58
65
|
error: "Request body failed validation.",
|
|
59
66
|
issues: z.treeifyError(parsed.error)
|
|
60
67
|
});
|
|
61
68
|
return;
|
|
62
69
|
}
|
|
63
70
|
try {
|
|
64
|
-
|
|
71
|
+
const result = await operation.run(parsed.data, { signal: abortSignalFor(res) });
|
|
72
|
+
sendJson(res, HTTP_STATUS.ok, { result });
|
|
65
73
|
} catch (error) {
|
|
66
74
|
const mapped = buildErrorMappers().get(operation.name)?.(error);
|
|
67
75
|
if (mapped !== void 0) {
|
|
68
76
|
sendJson(res, mapped.status, mapped.body);
|
|
69
77
|
return;
|
|
70
78
|
}
|
|
71
|
-
sendJson(res,
|
|
79
|
+
sendJson(res, HTTP_STATUS.badRequest, { error: error instanceof Error ? error.message : String(error) });
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
function listOperations() {
|
|
@@ -85,22 +93,22 @@ function createRestServer() {
|
|
|
85
93
|
return createServer((req, res) => {
|
|
86
94
|
(async () => {
|
|
87
95
|
if (req.url === void 0 || req.method === void 0) {
|
|
88
|
-
sendJson(res,
|
|
96
|
+
sendJson(res, HTTP_STATUS.badRequest, { error: "Malformed request: no url or method." });
|
|
89
97
|
return;
|
|
90
98
|
}
|
|
91
99
|
const url = new URL(req.url, "http://localhost");
|
|
92
100
|
if (url.pathname === "/" && req.method === "GET") {
|
|
93
|
-
sendJson(res,
|
|
101
|
+
sendJson(res, HTTP_STATUS.ok, listOperations());
|
|
94
102
|
return;
|
|
95
103
|
}
|
|
96
104
|
const name = url.pathname.slice(1);
|
|
97
105
|
const operation = OPERATIONS_BY_NAME.get(name);
|
|
98
106
|
if (operation === void 0) {
|
|
99
|
-
sendJson(res,
|
|
107
|
+
sendJson(res, HTTP_STATUS.notFound, { error: `No operation named "${name}". GET / lists every available operation.` });
|
|
100
108
|
return;
|
|
101
109
|
}
|
|
102
110
|
if (req.method !== "POST") {
|
|
103
|
-
sendJson(res,
|
|
111
|
+
sendJson(res, HTTP_STATUS.methodNotAllowed, { error: `${name} only accepts POST, received ${req.method}.` });
|
|
104
112
|
return;
|
|
105
113
|
}
|
|
106
114
|
await handleOperationRequest(req, res, operation);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document-rest",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.91",
|
|
4
4
|
"description": "A plain REST API server exposing document-operations' conversion, editing, inspection, and .odb operations as JSON-over-HTTP endpoints — one POST route per operation, for a caller with no MCP client and no Node runtime of their own.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"license": "MIT",
|
|
88
88
|
"packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",
|
|
89
89
|
"dependencies": {
|
|
90
|
-
"document-operations": "1.1.
|
|
91
|
-
"documents.js": "14.4.
|
|
90
|
+
"document-operations": "1.1.88",
|
|
91
|
+
"documents.js": "14.4.26",
|
|
92
92
|
"zod": "4.4.3"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"@vitest/coverage-v8": "4.1.11",
|
|
98
98
|
"eslint": "10.8.1",
|
|
99
99
|
"husky": "9.1.7",
|
|
100
|
-
"odf.js": "11.1.
|
|
100
|
+
"odf.js": "11.1.6",
|
|
101
101
|
"publint": "0.3.22",
|
|
102
102
|
"semantic-release": "25.0.9",
|
|
103
103
|
"tsdown": "0.22.14",
|