@hey-api/json-schema-ref-parser 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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +138 -0
  3. package/dist/lib/bundle.d.ts +26 -0
  4. package/dist/lib/bundle.js +293 -0
  5. package/dist/lib/dereference.d.ts +11 -0
  6. package/dist/lib/dereference.js +224 -0
  7. package/dist/lib/index.d.ts +74 -0
  8. package/dist/lib/index.js +208 -0
  9. package/dist/lib/options.d.ts +61 -0
  10. package/dist/lib/options.js +45 -0
  11. package/dist/lib/parse.d.ts +13 -0
  12. package/dist/lib/parse.js +87 -0
  13. package/dist/lib/parsers/binary.d.ts +2 -0
  14. package/dist/lib/parsers/binary.js +12 -0
  15. package/dist/lib/parsers/json.d.ts +2 -0
  16. package/dist/lib/parsers/json.js +38 -0
  17. package/dist/lib/parsers/text.d.ts +2 -0
  18. package/dist/lib/parsers/text.js +18 -0
  19. package/dist/lib/parsers/yaml.d.ts +2 -0
  20. package/dist/lib/parsers/yaml.js +28 -0
  21. package/dist/lib/pointer.d.ts +88 -0
  22. package/dist/lib/pointer.js +293 -0
  23. package/dist/lib/ref.d.ts +180 -0
  24. package/dist/lib/ref.js +226 -0
  25. package/dist/lib/refs.d.ts +127 -0
  26. package/dist/lib/refs.js +232 -0
  27. package/dist/lib/resolve-external.d.ts +13 -0
  28. package/dist/lib/resolve-external.js +147 -0
  29. package/dist/lib/resolvers/file.d.ts +4 -0
  30. package/dist/lib/resolvers/file.js +61 -0
  31. package/dist/lib/resolvers/url.d.ts +11 -0
  32. package/dist/lib/resolvers/url.js +57 -0
  33. package/dist/lib/types/index.d.ts +43 -0
  34. package/dist/lib/types/index.js +2 -0
  35. package/dist/lib/util/convert-path-to-posix.d.ts +1 -0
  36. package/dist/lib/util/convert-path-to-posix.js +14 -0
  37. package/dist/lib/util/errors.d.ts +56 -0
  38. package/dist/lib/util/errors.js +112 -0
  39. package/dist/lib/util/is-windows.d.ts +1 -0
  40. package/dist/lib/util/is-windows.js +6 -0
  41. package/dist/lib/util/plugins.d.ts +16 -0
  42. package/dist/lib/util/plugins.js +45 -0
  43. package/dist/lib/util/url.d.ts +79 -0
  44. package/dist/lib/util/url.js +285 -0
  45. package/dist/vite.config.d.ts +2 -0
  46. package/dist/vite.config.js +18 -0
  47. package/lib/bundle.ts +299 -0
  48. package/lib/dereference.ts +286 -0
  49. package/lib/index.ts +209 -0
  50. package/lib/options.ts +108 -0
  51. package/lib/parse.ts +56 -0
  52. package/lib/parsers/binary.ts +13 -0
  53. package/lib/parsers/json.ts +39 -0
  54. package/lib/parsers/text.ts +21 -0
  55. package/lib/parsers/yaml.ts +26 -0
  56. package/lib/pointer.ts +327 -0
  57. package/lib/ref.ts +279 -0
  58. package/lib/refs.ts +239 -0
  59. package/lib/resolve-external.ts +141 -0
  60. package/lib/resolvers/file.ts +24 -0
  61. package/lib/resolvers/url.ts +78 -0
  62. package/lib/types/index.ts +51 -0
  63. package/lib/util/convert-path-to-posix.ts +11 -0
  64. package/lib/util/errors.ts +153 -0
  65. package/lib/util/is-windows.ts +2 -0
  66. package/lib/util/plugins.ts +56 -0
  67. package/lib/util/url.ts +266 -0
  68. package/package.json +96 -0
@@ -0,0 +1,2 @@
1
+ const isWindowsConst = /^win/.test(globalThis.process ? globalThis.process.platform : "");
2
+ export const isWindows = () => isWindowsConst;
@@ -0,0 +1,56 @@
1
+ import type { FileInfo, JSONSchema } from "../types/index.js";
2
+ import type { Plugin } from "../types/index.js";
3
+
4
+ export interface PluginResult {
5
+ error?: any;
6
+ plugin: Pick<Plugin, 'handler'>;
7
+ result?: string | Buffer | JSONSchema;
8
+ }
9
+
10
+ /**
11
+ * Runs the specified method of the given plugins, in order, until one of them returns a successful result.
12
+ * Each method can return a synchronous value, a Promise, or call an error-first callback.
13
+ * If the promise resolves successfully, or the callback is called without an error, then the result
14
+ * is immediately returned and no further plugins are called.
15
+ * If the promise rejects, or the callback is called with an error, then the next plugin is called.
16
+ * If ALL plugins fail, then the last error is thrown.
17
+ */
18
+ export async function run(plugins: Pick<Plugin, 'handler'>[], file: FileInfo) {
19
+ let index = 0;
20
+ let lastError: PluginResult;
21
+ let plugin: Pick<Plugin, 'handler'>;
22
+
23
+ return new Promise<PluginResult>((resolve, reject) => {
24
+ const runNextPlugin = async () => {
25
+ plugin = plugins[index++];
26
+
27
+ if (!plugin) {
28
+ // there are no more functions, re-throw the last error
29
+ return reject(lastError);
30
+ }
31
+
32
+ try {
33
+ const result = await plugin.handler(file)
34
+
35
+ if (result !== undefined) {
36
+ return resolve({
37
+ plugin,
38
+ result,
39
+ });
40
+ }
41
+
42
+ if (index === plugins.length) {
43
+ throw new Error("No promise has been returned.");
44
+ }
45
+ } catch (e) {
46
+ lastError = {
47
+ plugin,
48
+ error: e,
49
+ };
50
+ runNextPlugin();
51
+ }
52
+ }
53
+
54
+ runNextPlugin();
55
+ });
56
+ }
@@ -0,0 +1,266 @@
1
+ import convertPathToPosix from "./convert-path-to-posix";
2
+ import path, { win32 } from "path";
3
+
4
+ const forwardSlashPattern = /\//g;
5
+ const protocolPattern = /^(\w{2,}):\/\//i;
6
+
7
+ import { join } from "path";
8
+ import { isWindows } from "./is-windows";
9
+
10
+ // RegExp patterns to URL-encode special characters in local filesystem paths
11
+ const urlEncodePatterns = [
12
+ [/\?/g, "%3F"],
13
+ [/#/g, "%23"],
14
+ ] as [RegExp, string][];
15
+
16
+ // RegExp patterns to URL-decode special characters for local filesystem paths
17
+ const urlDecodePatterns = [/%23/g, "#", /%24/g, "$", /%26/g, "&", /%2C/g, ",", /%40/g, "@"];
18
+
19
+ /**
20
+ * Returns resolved target URL relative to a base URL in a manner similar to that of a Web browser resolving an anchor tag HREF.
21
+ *
22
+ * @returns
23
+ */
24
+ export function resolve(from: string, to: string) {
25
+ const fromUrl = new URL(convertPathToPosix(from), "resolve://");
26
+ const resolvedUrl = new URL(convertPathToPosix(to), fromUrl);
27
+ const endSpaces = to.match(/(\s*)$/)?.[1] || "";
28
+ if (resolvedUrl.protocol === "resolve:") {
29
+ // `from` is a relative URL.
30
+ const { pathname, search, hash } = resolvedUrl;
31
+ return pathname + search + hash + endSpaces;
32
+ }
33
+ return resolvedUrl.toString() + endSpaces;
34
+ }
35
+
36
+ /**
37
+ * Returns the current working directory (in Node) or the current page URL (in browsers).
38
+ *
39
+ * @returns
40
+ */
41
+ export function cwd() {
42
+ if (typeof window !== "undefined") {
43
+ return location.href;
44
+ }
45
+
46
+ const path = process.cwd();
47
+
48
+ const lastChar = path.slice(-1);
49
+ if (lastChar === "/" || lastChar === "\\") {
50
+ return path;
51
+ } else {
52
+ return path + "/";
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Returns the protocol of the given URL, or `undefined` if it has no protocol.
58
+ *
59
+ * @param path
60
+ * @returns
61
+ */
62
+ export function getProtocol(path: string | undefined) {
63
+ const match = protocolPattern.exec(path || "");
64
+ if (match) {
65
+ return match[1].toLowerCase();
66
+ }
67
+ return undefined;
68
+ }
69
+
70
+ /**
71
+ * Returns the lowercased file extension of the given URL,
72
+ * or an empty string if it has no extension.
73
+ *
74
+ * @param path
75
+ * @returns
76
+ */
77
+ export function getExtension(path: any) {
78
+ const lastDot = path.lastIndexOf(".");
79
+ if (lastDot > -1) {
80
+ return stripQuery(path.substr(lastDot).toLowerCase());
81
+ }
82
+ return "";
83
+ }
84
+
85
+ /**
86
+ * Removes the query, if any, from the given path.
87
+ *
88
+ * @param path
89
+ * @returns
90
+ */
91
+ export function stripQuery(path: any) {
92
+ const queryIndex = path.indexOf("?");
93
+ if (queryIndex > -1) {
94
+ path = path.substr(0, queryIndex);
95
+ }
96
+ return path;
97
+ }
98
+
99
+ /**
100
+ * Returns the hash (URL fragment), of the given path.
101
+ * If there is no hash, then the root hash ("#") is returned.
102
+ *
103
+ * @param path
104
+ * @returns
105
+ */
106
+ export function getHash(path: undefined | string) {
107
+ if (!path) {
108
+ return "#";
109
+ }
110
+ const hashIndex = path.indexOf("#");
111
+ if (hashIndex > -1) {
112
+ return path.substring(hashIndex);
113
+ }
114
+ return "#";
115
+ }
116
+
117
+ /**
118
+ * Removes the hash (URL fragment), if any, from the given path.
119
+ *
120
+ * @param path
121
+ * @returns
122
+ */
123
+ export function stripHash(path?: string | undefined) {
124
+ if (!path) {
125
+ return "";
126
+ }
127
+ const hashIndex = path.indexOf("#");
128
+ if (hashIndex > -1) {
129
+ path = path.substring(0, hashIndex);
130
+ }
131
+ return path;
132
+ }
133
+
134
+ /**
135
+ * Determines whether the given path is a filesystem path.
136
+ * This includes "file://" URLs.
137
+ *
138
+ * @param path
139
+ * @returns
140
+ */
141
+ export function isFileSystemPath(path: string | undefined) {
142
+ // @ts-ignore
143
+ if (typeof window !== "undefined" || (typeof process !== "undefined" && process.browser)) {
144
+ // We're running in a browser, so assume that all paths are URLs.
145
+ // This way, even relative paths will be treated as URLs rather than as filesystem paths
146
+ return false;
147
+ }
148
+
149
+ const protocol = getProtocol(path);
150
+ return protocol === undefined || protocol === "file";
151
+ }
152
+
153
+ /**
154
+ * Converts a filesystem path to a properly-encoded URL.
155
+ *
156
+ * This is intended to handle situations where JSON Schema $Ref Parser is called
157
+ * with a filesystem path that contains characters which are not allowed in URLs.
158
+ *
159
+ * @example
160
+ * The following filesystem paths would be converted to the following URLs:
161
+ *
162
+ * <"!@#$%^&*+=?'>.json ==> %3C%22!@%23$%25%5E&*+=%3F\'%3E.json
163
+ * C:\\My Documents\\File (1).json ==> C:/My%20Documents/File%20(1).json
164
+ * file://Project #42/file.json ==> file://Project%20%2342/file.json
165
+ *
166
+ * @param path
167
+ * @returns
168
+ */
169
+ export function fromFileSystemPath(path: string) {
170
+ // Step 1: On Windows, replace backslashes with forward slashes,
171
+ // rather than encoding them as "%5C"
172
+ if (isWindows()) {
173
+ const projectDir = cwd();
174
+ const upperPath = path.toUpperCase();
175
+ const projectDirPosixPath = convertPathToPosix(projectDir);
176
+ const posixUpper = projectDirPosixPath.toUpperCase();
177
+ const hasProjectDir = upperPath.includes(posixUpper);
178
+ const hasProjectUri = upperPath.includes(posixUpper);
179
+ const isAbsolutePath =
180
+ win32?.isAbsolute(path) ||
181
+ path.startsWith("http://") ||
182
+ path.startsWith("https://") ||
183
+ path.startsWith("file://");
184
+
185
+ if (!(hasProjectDir || hasProjectUri || isAbsolutePath) && !projectDir.startsWith("http")) {
186
+ path = join(projectDir, path);
187
+ }
188
+ path = convertPathToPosix(path);
189
+ }
190
+
191
+ // Step 2: `encodeURI` will take care of MOST characters
192
+ path = encodeURI(path);
193
+
194
+ // Step 3: Manually encode characters that are not encoded by `encodeURI`.
195
+ // This includes characters such as "#" and "?", which have special meaning in URLs,
196
+ // but are just normal characters in a filesystem path.
197
+ for (const pattern of urlEncodePatterns) {
198
+ path = path.replace(pattern[0], pattern[1]);
199
+ }
200
+
201
+ return path;
202
+ }
203
+
204
+ /**
205
+ * Converts a URL to a local filesystem path.
206
+ */
207
+ export function toFileSystemPath(path: string | undefined, keepFileProtocol?: boolean): string {
208
+ // Step 1: `decodeURI` will decode characters such as Cyrillic characters, spaces, etc.
209
+ path = decodeURI(path!);
210
+
211
+ // Step 2: Manually decode characters that are not decoded by `decodeURI`.
212
+ // This includes characters such as "#" and "?", which have special meaning in URLs,
213
+ // but are just normal characters in a filesystem path.
214
+ for (let i = 0; i < urlDecodePatterns.length; i += 2) {
215
+ path = path.replace(urlDecodePatterns[i], urlDecodePatterns[i + 1] as string);
216
+ }
217
+
218
+ // Step 3: If it's a "file://" URL, then format it consistently
219
+ // or convert it to a local filesystem path
220
+ let isFileUrl = path.substr(0, 7).toLowerCase() === "file://";
221
+ if (isFileUrl) {
222
+ // Strip-off the protocol, and the initial "/", if there is one
223
+ path = path[7] === "/" ? path.substr(8) : path.substr(7);
224
+
225
+ // insert a colon (":") after the drive letter on Windows
226
+ if (isWindows() && path[1] === "/") {
227
+ path = path[0] + ":" + path.substr(1);
228
+ }
229
+
230
+ if (keepFileProtocol) {
231
+ // Return the consistently-formatted "file://" URL
232
+ path = "file:///" + path;
233
+ } else {
234
+ // Convert the "file://" URL to a local filesystem path.
235
+ // On Windows, it will start with something like "C:/".
236
+ // On Posix, it will start with "/"
237
+ isFileUrl = false;
238
+ path = isWindows() ? path : "/" + path;
239
+ }
240
+ }
241
+
242
+ // Step 4: Normalize Windows paths (unless it's a "file://" URL)
243
+ if (isWindows() && !isFileUrl) {
244
+ // Replace forward slashes with backslashes
245
+ path = path.replace(forwardSlashPattern, "\\");
246
+
247
+ // Capitalize the drive letter
248
+ if (path.substr(1, 2) === ":\\") {
249
+ path = path[0].toUpperCase() + path.substr(1);
250
+ }
251
+ }
252
+
253
+ return path;
254
+ }
255
+
256
+ export function relative(from: string, to: string) {
257
+ if (!isFileSystemPath(from) || !isFileSystemPath(to)) {
258
+ return resolve(from, to);
259
+ }
260
+
261
+ const fromDir = path.dirname(stripHash(from));
262
+ const toPath = stripHash(to);
263
+
264
+ const result = path.relative(fromDir, toPath);
265
+ return result + getHash(to);
266
+ }
package/package.json ADDED
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "@hey-api/json-schema-ref-parser",
3
+ "version": "0.0.1",
4
+ "description": "Parse, Resolve, and Dereference JSON Schema $ref pointers",
5
+ "homepage": "https://heyapi.dev/",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/hey-api/json-schema-ref-parser.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/hey-api/json-schema-ref-parser/issues"
12
+ },
13
+ "license": "MIT",
14
+ "author": {
15
+ "email": "lubos@heyapi.dev",
16
+ "name": "Hey API",
17
+ "url": "https://heyapi.dev"
18
+ },
19
+ "funding": "https://github.com/sponsors/hey-api",
20
+ "keywords": [
21
+ "json",
22
+ "schema",
23
+ "jsonschema",
24
+ "json-schema",
25
+ "json-pointer",
26
+ "$ref",
27
+ "dereference",
28
+ "resolve"
29
+ ],
30
+ "types": "dist/lib/index.d.ts",
31
+ "main": "dist/lib/index.js",
32
+ "browser": {
33
+ "fs": false
34
+ },
35
+ "engines": {
36
+ "node": ">= 16"
37
+ },
38
+ "files": [
39
+ "lib",
40
+ "dist",
41
+ "cjs"
42
+ ],
43
+ "scripts": {
44
+ "build": "rimraf dist && tsc",
45
+ "lint": "eslint lib",
46
+ "prepublishOnly": "yarn build",
47
+ "prettier": "prettier --write \"**/*.+(js|jsx|ts|tsx|har||json|css|md)\"",
48
+ "test:browser": "cross-env BROWSER=\"true\" yarn test",
49
+ "test:node": "yarn test",
50
+ "test:update": "vitest -u",
51
+ "test:watch": "vitest -w",
52
+ "test": "vitest --coverage",
53
+ "typecheck": "tsc --noEmit"
54
+ },
55
+ "devDependencies": {
56
+ "@eslint/compat": "^1.2.4",
57
+ "@eslint/js": "^9.16.0",
58
+ "@types/eslint": "9.6.1",
59
+ "@types/js-yaml": "^4.0.9",
60
+ "@types/node": "^22",
61
+ "@typescript-eslint/eslint-plugin": "^8.17.0",
62
+ "@typescript-eslint/parser": "^8.17.0",
63
+ "@vitest/coverage-v8": "^2.1.8",
64
+ "cross-env": "^7.0.3",
65
+ "eslint": "^9.16.0",
66
+ "eslint-config-prettier": "^9.1.0",
67
+ "eslint-config-standard": "^17.1.0",
68
+ "eslint-plugin-import": "^2.31.0",
69
+ "eslint-plugin-prettier": "^5.2.1",
70
+ "eslint-plugin-promise": "^7.2.1",
71
+ "eslint-plugin-unused-imports": "^4.1.4",
72
+ "globals": "^15.13.0",
73
+ "jsdom": "^25.0.1",
74
+ "prettier": "^3.4.2",
75
+ "rimraf": "^6.0.1",
76
+ "typescript": "^5.7.2",
77
+ "typescript-eslint": "^8.17.0",
78
+ "vitest": "^2.1.8"
79
+ },
80
+ "dependencies": {
81
+ "@jsdevtools/ono": "^7.1.3",
82
+ "@types/json-schema": "^7.0.15",
83
+ "js-yaml": "^4.1.0"
84
+ },
85
+ "release": {
86
+ "branches": [
87
+ "main"
88
+ ],
89
+ "plugins": [
90
+ "@semantic-release/commit-analyzer",
91
+ "@semantic-release/release-notes-generator",
92
+ "@semantic-release/npm",
93
+ "@semantic-release/github"
94
+ ]
95
+ }
96
+ }