@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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +361 -2
  3. package/dist/bundle.d.ts +973 -0
  4. package/dist/bundle.js +11 -0
  5. package/dist/codec.d.ts +2188 -0
  6. package/dist/codec.js +42 -0
  7. package/dist/document.d.ts +9 -0
  8. package/dist/document.js +28 -0
  9. package/dist/errors.d.ts +105 -0
  10. package/dist/errors.js +162 -0
  11. package/dist/hoist.d.ts +10 -0
  12. package/dist/hoist.js +444 -0
  13. package/dist/http.d.ts +2 -0
  14. package/dist/http.js +163 -0
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.js +2 -0
  17. package/dist/load.d.ts +17 -0
  18. package/dist/load.js +128 -0
  19. package/dist/loader.d.ts +11 -0
  20. package/dist/loader.js +89 -0
  21. package/dist/parsers.d.ts +5 -0
  22. package/dist/parsers.js +90 -0
  23. package/dist/pipeline.d.ts +3 -0
  24. package/dist/pipeline.js +446 -0
  25. package/dist/plugins.d.ts +95 -0
  26. package/dist/plugins.js +61 -0
  27. package/dist/pointer.d.ts +7 -0
  28. package/dist/pointer.js +80 -0
  29. package/dist/run.d.ts +17 -0
  30. package/dist/run.js +313 -0
  31. package/dist/schemas/3.1/callback.d.ts +205 -0
  32. package/dist/schemas/3.1/callback.js +4 -0
  33. package/dist/schemas/3.1/components.d.ts +801 -0
  34. package/dist/schemas/3.1/components.js +25 -0
  35. package/dist/schemas/3.1/contact.d.ts +7 -0
  36. package/dist/schemas/3.1/contact.js +7 -0
  37. package/dist/schemas/3.1/document.d.ts +1093 -0
  38. package/dist/schemas/3.1/document.js +25 -0
  39. package/dist/schemas/3.1/encoding.d.ts +14 -0
  40. package/dist/schemas/3.1/encoding.js +11 -0
  41. package/dist/schemas/3.1/example.d.ts +8 -0
  42. package/dist/schemas/3.1/example.js +10 -0
  43. package/dist/schemas/3.1/external-docs.d.ts +6 -0
  44. package/dist/schemas/3.1/external-docs.js +6 -0
  45. package/dist/schemas/3.1/header.d.ts +194 -0
  46. package/dist/schemas/3.1/header.js +29 -0
  47. package/dist/schemas/3.1/http-method.d.ts +4 -0
  48. package/dist/schemas/3.1/http-method.js +12 -0
  49. package/dist/schemas/3.1/index.d.ts +29 -0
  50. package/dist/schemas/3.1/index.js +29 -0
  51. package/dist/schemas/3.1/info.d.ts +19 -0
  52. package/dist/schemas/3.1/info.js +13 -0
  53. package/dist/schemas/3.1/license.d.ts +7 -0
  54. package/dist/schemas/3.1/license.js +9 -0
  55. package/dist/schemas/3.1/link.d.ts +18 -0
  56. package/dist/schemas/3.1/link.js +13 -0
  57. package/dist/schemas/3.1/media-type.d.ts +27 -0
  58. package/dist/schemas/3.1/media-type.js +14 -0
  59. package/dist/schemas/3.1/oauth-flow.d.ts +8 -0
  60. package/dist/schemas/3.1/oauth-flow.js +8 -0
  61. package/dist/schemas/3.1/oauth-flows.d.ts +28 -0
  62. package/dist/schemas/3.1/oauth-flows.js +13 -0
  63. package/dist/schemas/3.1/object.d.ts +5 -0
  64. package/dist/schemas/3.1/object.js +17 -0
  65. package/dist/schemas/3.1/operation.d.ts +1040 -0
  66. package/dist/schemas/3.1/operation.js +25 -0
  67. package/dist/schemas/3.1/parameter.d.ts +101 -0
  68. package/dist/schemas/3.1/parameter.js +33 -0
  69. package/dist/schemas/3.1/path-item.d.ts +127 -0
  70. package/dist/schemas/3.1/path-item.js +22 -0
  71. package/dist/schemas/3.1/paths.d.ts +126 -0
  72. package/dist/schemas/3.1/paths.js +4 -0
  73. package/dist/schemas/3.1/reference.d.ts +13 -0
  74. package/dist/schemas/3.1/reference.js +11 -0
  75. package/dist/schemas/3.1/request-body.d.ts +31 -0
  76. package/dist/schemas/3.1/request-body.js +8 -0
  77. package/dist/schemas/3.1/response.d.ts +149 -0
  78. package/dist/schemas/3.1/response.js +12 -0
  79. package/dist/schemas/3.1/responses.d.ts +152 -0
  80. package/dist/schemas/3.1/responses.js +7 -0
  81. package/dist/schemas/3.1/schema.d.ts +4 -0
  82. package/dist/schemas/3.1/schema.js +6 -0
  83. package/dist/schemas/3.1/security-requirement.d.ts +3 -0
  84. package/dist/schemas/3.1/security-requirement.js +2 -0
  85. package/dist/schemas/3.1/security-scheme.d.ts +49 -0
  86. package/dist/schemas/3.1/security-scheme.js +25 -0
  87. package/dist/schemas/3.1/server-variable.d.ts +7 -0
  88. package/dist/schemas/3.1/server-variable.js +7 -0
  89. package/dist/schemas/3.1/server.d.ts +11 -0
  90. package/dist/schemas/3.1/server.js +8 -0
  91. package/dist/schemas/3.1/tag.d.ts +10 -0
  92. package/dist/schemas/3.1/tag.js +8 -0
  93. package/dist/upgraders/3.0-to-3.1.d.ts +9 -0
  94. package/dist/upgraders/3.0-to-3.1.js +310 -0
  95. package/dist/url.d.ts +29 -0
  96. package/dist/url.js +255 -0
  97. package/dist/yaml.d.ts +6 -0
  98. package/dist/yaml.js +235 -0
  99. 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.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
+ }