@shipworthy/openapi-parser 0.0.0-stage → 0.0.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/LICENSE +21 -0
- package/README.md +361 -2
- package/dist/bundle.d.ts +973 -0
- package/dist/bundle.js +11 -0
- package/dist/codec.d.ts +2188 -0
- package/dist/codec.js +42 -0
- package/dist/document.d.ts +9 -0
- package/dist/document.js +28 -0
- package/dist/errors.d.ts +105 -0
- package/dist/errors.js +162 -0
- package/dist/hoist.d.ts +10 -0
- package/dist/hoist.js +444 -0
- package/dist/http.d.ts +2 -0
- package/dist/http.js +163 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/load.d.ts +17 -0
- package/dist/load.js +128 -0
- package/dist/loader.d.ts +11 -0
- package/dist/loader.js +89 -0
- package/dist/parsers.d.ts +5 -0
- package/dist/parsers.js +90 -0
- package/dist/pipeline.d.ts +3 -0
- package/dist/pipeline.js +446 -0
- package/dist/plugins.d.ts +95 -0
- package/dist/plugins.js +61 -0
- package/dist/pointer.d.ts +7 -0
- package/dist/pointer.js +80 -0
- package/dist/run.d.ts +17 -0
- package/dist/run.js +313 -0
- package/dist/schemas/3.1/callback.d.ts +205 -0
- package/dist/schemas/3.1/callback.js +4 -0
- package/dist/schemas/3.1/components.d.ts +801 -0
- package/dist/schemas/3.1/components.js +25 -0
- package/dist/schemas/3.1/contact.d.ts +7 -0
- package/dist/schemas/3.1/contact.js +7 -0
- package/dist/schemas/3.1/document.d.ts +1093 -0
- package/dist/schemas/3.1/document.js +25 -0
- package/dist/schemas/3.1/encoding.d.ts +14 -0
- package/dist/schemas/3.1/encoding.js +11 -0
- package/dist/schemas/3.1/example.d.ts +8 -0
- package/dist/schemas/3.1/example.js +10 -0
- package/dist/schemas/3.1/external-docs.d.ts +6 -0
- package/dist/schemas/3.1/external-docs.js +6 -0
- package/dist/schemas/3.1/header.d.ts +194 -0
- package/dist/schemas/3.1/header.js +29 -0
- package/dist/schemas/3.1/http-method.d.ts +4 -0
- package/dist/schemas/3.1/http-method.js +12 -0
- package/dist/schemas/3.1/index.d.ts +29 -0
- package/dist/schemas/3.1/index.js +29 -0
- package/dist/schemas/3.1/info.d.ts +19 -0
- package/dist/schemas/3.1/info.js +13 -0
- package/dist/schemas/3.1/license.d.ts +7 -0
- package/dist/schemas/3.1/license.js +9 -0
- package/dist/schemas/3.1/link.d.ts +18 -0
- package/dist/schemas/3.1/link.js +13 -0
- package/dist/schemas/3.1/media-type.d.ts +27 -0
- package/dist/schemas/3.1/media-type.js +14 -0
- package/dist/schemas/3.1/oauth-flow.d.ts +8 -0
- package/dist/schemas/3.1/oauth-flow.js +8 -0
- package/dist/schemas/3.1/oauth-flows.d.ts +28 -0
- package/dist/schemas/3.1/oauth-flows.js +13 -0
- package/dist/schemas/3.1/object.d.ts +5 -0
- package/dist/schemas/3.1/object.js +17 -0
- package/dist/schemas/3.1/operation.d.ts +1040 -0
- package/dist/schemas/3.1/operation.js +25 -0
- package/dist/schemas/3.1/parameter.d.ts +101 -0
- package/dist/schemas/3.1/parameter.js +33 -0
- package/dist/schemas/3.1/path-item.d.ts +127 -0
- package/dist/schemas/3.1/path-item.js +22 -0
- package/dist/schemas/3.1/paths.d.ts +126 -0
- package/dist/schemas/3.1/paths.js +4 -0
- package/dist/schemas/3.1/reference.d.ts +13 -0
- package/dist/schemas/3.1/reference.js +11 -0
- package/dist/schemas/3.1/request-body.d.ts +31 -0
- package/dist/schemas/3.1/request-body.js +8 -0
- package/dist/schemas/3.1/response.d.ts +149 -0
- package/dist/schemas/3.1/response.js +12 -0
- package/dist/schemas/3.1/responses.d.ts +152 -0
- package/dist/schemas/3.1/responses.js +7 -0
- package/dist/schemas/3.1/schema.d.ts +4 -0
- package/dist/schemas/3.1/schema.js +6 -0
- package/dist/schemas/3.1/security-requirement.d.ts +3 -0
- package/dist/schemas/3.1/security-requirement.js +2 -0
- package/dist/schemas/3.1/security-scheme.d.ts +49 -0
- package/dist/schemas/3.1/security-scheme.js +25 -0
- package/dist/schemas/3.1/server-variable.d.ts +7 -0
- package/dist/schemas/3.1/server-variable.js +7 -0
- package/dist/schemas/3.1/server.d.ts +11 -0
- package/dist/schemas/3.1/server.js +8 -0
- package/dist/schemas/3.1/tag.d.ts +10 -0
- package/dist/schemas/3.1/tag.js +8 -0
- package/dist/upgraders/3.0-to-3.1.d.ts +9 -0
- package/dist/upgraders/3.0-to-3.1.js +310 -0
- package/dist/url.d.ts +29 -0
- package/dist/url.js +255 -0
- package/dist/yaml.d.ts +6 -0
- package/dist/yaml.js +235 -0
- package/package.json +50 -4
package/dist/yaml.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import * as Yaml from "effect/encoding/Yaml";
|
|
2
|
+
const blockScalar = /^[|>](?:[1-9]?[+-]?|[+-]?[1-9]?)$/;
|
|
3
|
+
/**
|
|
4
|
+
* Effect's YAML decoder rejects `- - value` and treats `<<` as a normal key.
|
|
5
|
+
* OpenAPI fixtures in this repo use both, so expand those forms before parsing.
|
|
6
|
+
*/
|
|
7
|
+
export function parseYamlDocument(source) {
|
|
8
|
+
const text = source.charCodeAt(0) === 0xfeff ? source.slice(1) : source;
|
|
9
|
+
if (text.trim().length === 0)
|
|
10
|
+
return null;
|
|
11
|
+
return applyMergeKeys(Yaml.parse(expandCompactNestedSequences(deepenShallowBlockScalars(relaxIndentTabs(text)))));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Effect closes a block scalar when the next line is not indented past the header.
|
|
15
|
+
* `- |2` followed by text at the same column is that case. Indent the content and
|
|
16
|
+
* drop the explicit indicator so auto-detection keeps the scalar text.
|
|
17
|
+
*/
|
|
18
|
+
function deepenShallowBlockScalars(source) {
|
|
19
|
+
const lines = source.replace(/\r\n?/g, "\n").split("\n");
|
|
20
|
+
const output = [];
|
|
21
|
+
for (let index = 0; index < lines.length; index++) {
|
|
22
|
+
const line = lines[index] ?? "";
|
|
23
|
+
const indent = leadingSpaces(line);
|
|
24
|
+
const text = stripComment(line.slice(indent)).trimEnd();
|
|
25
|
+
if (!isExplicitBlockScalar(text) || !hasShallowContent(lines, index, indent)) {
|
|
26
|
+
output.push(line);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
output.push(dropExplicitIndent(line));
|
|
30
|
+
index++;
|
|
31
|
+
while (index < lines.length) {
|
|
32
|
+
const next = lines[index] ?? "";
|
|
33
|
+
if (next.trim().length > 0 && leadingSpaces(next) < indent)
|
|
34
|
+
break;
|
|
35
|
+
output.push(next.trim().length === 0 ? next : ` ${next}`);
|
|
36
|
+
index++;
|
|
37
|
+
}
|
|
38
|
+
index--;
|
|
39
|
+
}
|
|
40
|
+
return output.join("\n");
|
|
41
|
+
}
|
|
42
|
+
function isExplicitBlockScalar(text) {
|
|
43
|
+
return /^[|>](?:[+-]?[1-9]|[1-9][+-])$/.test(scalarToken(text));
|
|
44
|
+
}
|
|
45
|
+
function scalarToken(text) {
|
|
46
|
+
const separator = mappingSeparator(text);
|
|
47
|
+
const token = separator === -1 ? text : text.slice(separator + 1).trim();
|
|
48
|
+
if (token === "-" || token.startsWith("- ")) {
|
|
49
|
+
const item = token.slice(1).trimStart();
|
|
50
|
+
const nested = mappingSeparator(item);
|
|
51
|
+
return nested === -1 ? item : item.slice(nested + 1).trim();
|
|
52
|
+
}
|
|
53
|
+
return token;
|
|
54
|
+
}
|
|
55
|
+
function hasShallowContent(lines, header, indent) {
|
|
56
|
+
for (let index = header + 1; index < lines.length; index++) {
|
|
57
|
+
const line = lines[index] ?? "";
|
|
58
|
+
if (line.trim().length === 0)
|
|
59
|
+
continue;
|
|
60
|
+
return leadingSpaces(line) <= indent;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
function dropExplicitIndent(line) {
|
|
65
|
+
return line.replace(/([|>])(\+|-)?[1-9](\+|-)?(\s*(?:#.*)?)?$/, (_, bar, before, after, tail) => `${bar}${before ?? ""}${after ?? ""}${tail ?? ""}`);
|
|
66
|
+
}
|
|
67
|
+
/** Effect rejects a tab after leading spaces, including inside block scalars. */
|
|
68
|
+
function relaxIndentTabs(source) {
|
|
69
|
+
return source.replace(/^( *)\t+/gm, (match, spaces) => spaces + " ".repeat(match.length - spaces.length));
|
|
70
|
+
}
|
|
71
|
+
export function expandCompactNestedSequences(source) {
|
|
72
|
+
const lines = source
|
|
73
|
+
.replace(/^\uFEFF/, "")
|
|
74
|
+
.replace(/\r\n?/g, "\n")
|
|
75
|
+
.split("\n");
|
|
76
|
+
const output = [];
|
|
77
|
+
let blockIndent = -1;
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
let raw = line;
|
|
80
|
+
while (true) {
|
|
81
|
+
const indent = leadingSpaces(raw);
|
|
82
|
+
if (blockIndent >= 0) {
|
|
83
|
+
if (raw.trim().length > 0 && indent <= blockIndent)
|
|
84
|
+
blockIndent = -1;
|
|
85
|
+
else
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
const text = stripComment(raw.slice(indent)).trimEnd();
|
|
89
|
+
const compact = compactRest(raw, indent);
|
|
90
|
+
if (compact === undefined) {
|
|
91
|
+
if (isBlockScalar(text))
|
|
92
|
+
blockIndent = indent;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
output.push(`${" ".repeat(indent)}-`);
|
|
96
|
+
raw = `${" ".repeat(indent + 2)}${compact}`;
|
|
97
|
+
}
|
|
98
|
+
output.push(raw);
|
|
99
|
+
}
|
|
100
|
+
return output.join("\n");
|
|
101
|
+
}
|
|
102
|
+
function compactRest(raw, indent) {
|
|
103
|
+
const body = raw.slice(indent);
|
|
104
|
+
if (!body.startsWith("- "))
|
|
105
|
+
return undefined;
|
|
106
|
+
const rest = body.slice(2);
|
|
107
|
+
const text = stripComment(rest).trimEnd();
|
|
108
|
+
if (text === "-" || text.startsWith("- "))
|
|
109
|
+
return rest;
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
function isBlockScalar(text) {
|
|
113
|
+
if (blockScalar.test(text))
|
|
114
|
+
return true;
|
|
115
|
+
const separator = mappingSeparator(text);
|
|
116
|
+
if (separator !== -1)
|
|
117
|
+
return blockScalar.test(text.slice(separator + 1).trim());
|
|
118
|
+
if (text === "-" || text.startsWith("- ")) {
|
|
119
|
+
const item = text.slice(1).trimStart();
|
|
120
|
+
const nested = mappingSeparator(item);
|
|
121
|
+
if (nested === -1)
|
|
122
|
+
return blockScalar.test(item);
|
|
123
|
+
return blockScalar.test(item.slice(nested + 1).trim());
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
function applyMergeKeys(value, seen = new Set()) {
|
|
128
|
+
if (typeof value !== "object" || value === null || seen.has(value))
|
|
129
|
+
return value;
|
|
130
|
+
seen.add(value);
|
|
131
|
+
if (Array.isArray(value)) {
|
|
132
|
+
for (let index = 0; index < value.length; index++)
|
|
133
|
+
value[index] = applyMergeKeys(value[index], seen);
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
136
|
+
const record = value;
|
|
137
|
+
for (const key of Object.keys(record))
|
|
138
|
+
record[key] = applyMergeKeys(record[key], seen);
|
|
139
|
+
if (!Object.keys(record).some(isMergeKey))
|
|
140
|
+
return record;
|
|
141
|
+
const next = {};
|
|
142
|
+
for (const key of Object.keys(record)) {
|
|
143
|
+
if (!isMergeKey(key)) {
|
|
144
|
+
next[key] = record[key];
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const source = record[key];
|
|
148
|
+
const entries = Array.isArray(source) ? source : [source];
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry))
|
|
151
|
+
continue;
|
|
152
|
+
for (const [mergedKey, mergedValue] of Object.entries(entry)) {
|
|
153
|
+
if (isMergeKey(mergedKey) || Object.hasOwn(next, mergedKey))
|
|
154
|
+
continue;
|
|
155
|
+
next[mergedKey] = mergedValue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (const key of Object.keys(record))
|
|
160
|
+
delete record[key];
|
|
161
|
+
Object.assign(record, next);
|
|
162
|
+
return record;
|
|
163
|
+
}
|
|
164
|
+
function isMergeKey(key) {
|
|
165
|
+
const text = key.trim();
|
|
166
|
+
return text === "<<" || /^!!\S+\s+<<$/.test(text) || /^!<[^>]+>\s+<<$/.test(text);
|
|
167
|
+
}
|
|
168
|
+
function leadingSpaces(raw) {
|
|
169
|
+
let index = 0;
|
|
170
|
+
while (raw.charCodeAt(index) === 32)
|
|
171
|
+
index++;
|
|
172
|
+
return index;
|
|
173
|
+
}
|
|
174
|
+
const closingQuote = (input, quote, start) => {
|
|
175
|
+
for (let index = start; index < input.length; index++) {
|
|
176
|
+
const character = input[index];
|
|
177
|
+
if (quote === '"' && character === "\\")
|
|
178
|
+
index++;
|
|
179
|
+
else if (character === quote) {
|
|
180
|
+
if (quote === "'" && input[index + 1] === quote)
|
|
181
|
+
index++;
|
|
182
|
+
else
|
|
183
|
+
return index;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return -1;
|
|
187
|
+
};
|
|
188
|
+
const findIndicator = (input, indicator) => {
|
|
189
|
+
let depth = 0;
|
|
190
|
+
let tokenStart = true;
|
|
191
|
+
for (let index = 0; index < input.length; index++) {
|
|
192
|
+
const character = input[index];
|
|
193
|
+
if (tokenStart && (character === "'" || character === '"')) {
|
|
194
|
+
index = closingQuote(input, character, index + 1);
|
|
195
|
+
if (index === -1)
|
|
196
|
+
return -1;
|
|
197
|
+
tokenStart = false;
|
|
198
|
+
}
|
|
199
|
+
else if (tokenStart && character === "&") {
|
|
200
|
+
while (index + 1 < input.length && !/[\s,[\]{}]/.test(input[index + 1] ?? ""))
|
|
201
|
+
index++;
|
|
202
|
+
}
|
|
203
|
+
else if (character === "#" && (index === 0 || /\s/.test(input[index - 1] ?? ""))) {
|
|
204
|
+
return indicator === "#" ? index : -1;
|
|
205
|
+
}
|
|
206
|
+
else if (tokenStart && (character === "[" || character === "{")) {
|
|
207
|
+
depth++;
|
|
208
|
+
}
|
|
209
|
+
else if (depth > 0 && (character === "]" || character === "}")) {
|
|
210
|
+
depth--;
|
|
211
|
+
tokenStart = false;
|
|
212
|
+
}
|
|
213
|
+
else if (depth > 0 && character === ",") {
|
|
214
|
+
tokenStart = true;
|
|
215
|
+
}
|
|
216
|
+
else if (character === ":" &&
|
|
217
|
+
(depth > 0 || input[index + 1] === undefined || /\s/.test(input[index + 1] ?? ""))) {
|
|
218
|
+
if (depth === 0 && indicator === ":")
|
|
219
|
+
return index;
|
|
220
|
+
tokenStart = true;
|
|
221
|
+
}
|
|
222
|
+
else if (tokenStart && character === "-" && /\s/.test(input[index + 1] ?? "")) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
else if (!/\s/.test(character ?? "")) {
|
|
226
|
+
tokenStart = false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return -1;
|
|
230
|
+
};
|
|
231
|
+
const stripComment = (input) => {
|
|
232
|
+
const index = findIndicator(input, "#");
|
|
233
|
+
return index === -1 ? input : input.slice(0, index).trimEnd();
|
|
234
|
+
};
|
|
235
|
+
const mappingSeparator = (input) => findIndicator(input, ":");
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipworthy/openapi-parser",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@effect/platform-node": "^4.0.0"
|
|
9
|
+
},
|
|
10
|
+
"peerDependencies": {
|
|
11
|
+
"effect": "^4.0.0"
|
|
12
|
+
},
|
|
13
|
+
"description": "Parse, bundle, and upgrade OpenAPI documents with Effect schemas.",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/ShipWorthyAI/openapi.git",
|
|
18
|
+
"directory": "packages/openapi-parser"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./schemas/3.1": {
|
|
27
|
+
"types": "./dist/schemas/3.1/index.d.ts",
|
|
28
|
+
"default": "./dist/schemas/3.1/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@effect/vitest": "4.0.0",
|
|
38
|
+
"@shipworthy/typescript-config": "0.0.1",
|
|
39
|
+
"@types/node": "^24.10.3",
|
|
40
|
+
"effect": "^4.0.0",
|
|
41
|
+
"typescript": "^7.0.2",
|
|
42
|
+
"vitest": "5.0.3"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=22.19.0"
|
|
46
|
+
},
|
|
47
|
+
"main": "./dist/index.js",
|
|
48
|
+
"types": "./dist/index.d.ts",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
}
|
|
52
|
+
}
|