openapi-typesafe 1.0.0-rc.5 → 1.0.0-rc.6
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 +73 -0
- package/dist/adapters/redocly-loader/query-document-names.cjs +50 -0
- package/dist/adapters/redocly-loader/query-document-names.cjs.map +1 -0
- package/dist/adapters/redocly-loader/query-document-names.mjs +48 -0
- package/dist/adapters/redocly-loader/query-document-names.mjs.map +1 -0
- package/dist/adapters/redocly-loader/query-named-components.cjs +29 -37
- package/dist/adapters/redocly-loader/query-named-components.cjs.map +1 -1
- package/dist/adapters/redocly-loader/query-named-components.mjs +29 -37
- package/dist/adapters/redocly-loader/query-named-components.mjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-config-source.cjs +3 -0
- package/dist/adapters/redocly-loader/redocly-config-source.cjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-config-source.mjs +4 -1
- package/dist/adapters/redocly-loader/redocly-config-source.mjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-schema-loader.cjs +16 -7
- package/dist/adapters/redocly-loader/redocly-schema-loader.cjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-schema-loader.mjs +17 -8
- package/dist/adapters/redocly-loader/redocly-schema-loader.mjs.map +1 -1
- package/dist/adapters/redocly-loader/ref-access.cjs +9 -1
- package/dist/adapters/redocly-loader/ref-access.cjs.map +1 -1
- package/dist/adapters/redocly-loader/ref-access.mjs +10 -2
- package/dist/adapters/redocly-loader/ref-access.mjs.map +1 -1
- package/dist/adapters/redocly-loader/remote-fetch.cjs +16 -1
- package/dist/adapters/redocly-loader/remote-fetch.cjs.map +1 -1
- package/dist/adapters/redocly-loader/remote-fetch.mjs +16 -2
- package/dist/adapters/redocly-loader/remote-fetch.mjs.map +1 -1
- package/dist/api/cwd-option.cjs +58 -0
- package/dist/api/cwd-option.cjs.map +1 -0
- package/dist/api/cwd-option.mjs +52 -0
- package/dist/api/cwd-option.mjs.map +1 -0
- package/dist/api/default-ports.cjs +15 -7
- package/dist/api/default-ports.cjs.map +1 -1
- package/dist/api/default-ports.mjs +14 -8
- package/dist/api/default-ports.mjs.map +1 -1
- package/dist/api/document-run.cjs +2 -16
- package/dist/api/document-run.cjs.map +1 -1
- package/dist/api/document-run.mjs +2 -12
- package/dist/api/document-run.mjs.map +1 -1
- package/dist/api/legacy-utils.cjs +1 -1
- package/dist/api/legacy-utils.cjs.map +1 -1
- package/dist/api/legacy-utils.mjs +1 -1
- package/dist/api/legacy-utils.mjs.map +1 -1
- package/dist/api/openapi-ts.cjs.map +1 -1
- package/dist/api/openapi-ts.mjs.map +1 -1
- package/dist/api/option-errors.cjs +24 -0
- package/dist/api/option-errors.cjs.map +1 -0
- package/dist/api/option-errors.mjs +21 -0
- package/dist/api/option-errors.mjs.map +1 -0
- package/dist/api/ref-limit-options.cjs +19 -24
- package/dist/api/ref-limit-options.cjs.map +1 -1
- package/dist/api/ref-limit-options.mjs +15 -20
- package/dist/api/ref-limit-options.mjs.map +1 -1
- package/dist/application/transform/document/path-template-refs.cjs +12 -2
- package/dist/application/transform/document/path-template-refs.cjs.map +1 -1
- package/dist/application/transform/document/path-template-refs.mjs +12 -2
- package/dist/application/transform/document/path-template-refs.mjs.map +1 -1
- package/dist/application/transform/ref-pointer/generated-shape.cjs +28 -10
- package/dist/application/transform/ref-pointer/generated-shape.cjs.map +1 -1
- package/dist/application/transform/ref-pointer/generated-shape.mjs +29 -11
- package/dist/application/transform/ref-pointer/generated-shape.mjs.map +1 -1
- package/dist/application/transform/ref-pointer/original-index.cjs +1 -1
- package/dist/application/transform/ref-pointer/original-index.cjs.map +1 -1
- package/dist/application/transform/ref-pointer/original-index.mjs +1 -1
- package/dist/application/transform/ref-pointer/original-index.mjs.map +1 -1
- package/dist/application/transform/ref-pointer/property-shape.cjs +5 -0
- package/dist/application/transform/ref-pointer/property-shape.cjs.map +1 -1
- package/dist/application/transform/ref-pointer/property-shape.mjs +5 -1
- package/dist/application/transform/ref-pointer/property-shape.mjs.map +1 -1
- package/dist/application/transform/ref-pointer/typed-walk.cjs +11 -1
- package/dist/application/transform/ref-pointer/typed-walk.cjs.map +1 -1
- package/dist/application/transform/ref-pointer/typed-walk.mjs +12 -2
- package/dist/application/transform/ref-pointer/typed-walk.mjs.map +1 -1
- package/dist/cli/api-settings.cjs +3 -2
- package/dist/cli/api-settings.cjs.map +1 -1
- package/dist/cli/api-settings.mjs +2 -1
- package/dist/cli/api-settings.mjs.map +1 -1
- package/dist/cli/api-writes.cjs +7 -1
- package/dist/cli/api-writes.cjs.map +1 -1
- package/dist/cli/api-writes.mjs +7 -1
- package/dist/cli/api-writes.mjs.map +1 -1
- package/dist/cli/cli-arguments.cjs +25 -107
- package/dist/cli/cli-arguments.cjs.map +1 -1
- package/dist/cli/cli-arguments.mjs +18 -100
- package/dist/cli/cli-arguments.mjs.map +1 -1
- package/dist/cli/cli-errors.cjs +1 -0
- package/dist/cli/cli-errors.cjs.map +1 -1
- package/dist/cli/cli-errors.mjs +1 -0
- package/dist/cli/cli-errors.mjs.map +1 -1
- package/dist/cli/cli-flag-names.cjs +77 -0
- package/dist/cli/cli-flag-names.cjs.map +1 -0
- package/dist/cli/cli-flag-names.mjs +64 -0
- package/dist/cli/cli-flag-names.mjs.map +1 -0
- package/dist/cli/cli-output.cjs +7 -2
- package/dist/cli/cli-output.cjs.map +1 -1
- package/dist/cli/cli-output.mjs +7 -2
- package/dist/cli/cli-output.mjs.map +1 -1
- package/dist/cli/main.cjs +1 -1
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.mjs +1 -1
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/redocly-apis.cjs +14 -4
- package/dist/cli/redocly-apis.cjs.map +1 -1
- package/dist/cli/redocly-apis.mjs +14 -4
- package/dist/cli/redocly-apis.mjs.map +1 -1
- package/dist/cli/written-flags.cjs +83 -0
- package/dist/cli/written-flags.cjs.map +1 -0
- package/dist/cli/written-flags.mjs +77 -0
- package/dist/cli/written-flags.mjs.map +1 -0
- package/dist/domain/errors.cjs +1 -0
- package/dist/domain/errors.cjs.map +1 -1
- package/dist/domain/errors.mjs +1 -0
- package/dist/domain/errors.mjs.map +1 -1
- package/dist/domain/location-text.cjs +7 -1
- package/dist/domain/location-text.cjs.map +1 -1
- package/dist/domain/location-text.mjs +7 -1
- package/dist/domain/location-text.mjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/package.json +1 -1
- package/src/adapters/redocly-loader/query-document-names.ts +79 -0
- package/src/adapters/redocly-loader/query-named-components.ts +48 -49
- package/src/adapters/redocly-loader/redocly-config-source.ts +11 -1
- package/src/adapters/redocly-loader/redocly-schema-loader.ts +20 -6
- package/src/adapters/redocly-loader/ref-access.ts +16 -2
- package/src/adapters/redocly-loader/remote-fetch.ts +26 -0
- package/src/api/cwd-option.ts +76 -0
- package/src/api/default-ports.ts +33 -10
- package/src/api/document-run.ts +2 -17
- package/src/api/legacy-utils.ts +1 -1
- package/src/api/openapi-ts.ts +14 -1
- package/src/api/option-errors.ts +22 -0
- package/src/api/ref-limit-options.ts +17 -24
- package/src/application/ports/redocly-config-source.ts +5 -0
- package/src/application/transform/document/path-template-refs.ts +28 -7
- package/src/application/transform/ref-pointer/generated-shape.ts +57 -14
- package/src/application/transform/ref-pointer/original-index.ts +6 -3
- package/src/application/transform/ref-pointer/property-shape.ts +9 -0
- package/src/application/transform/ref-pointer/typed-walk.ts +26 -4
- package/src/cli/api-writes.ts +11 -3
- package/src/cli/cli-arguments.ts +42 -153
- package/src/cli/cli-errors.ts +1 -0
- package/src/cli/cli-flag-names.ts +99 -0
- package/src/cli/cli-output.ts +20 -6
- package/src/cli/main.ts +2 -1
- package/src/cli/redocly-apis.ts +21 -5
- package/src/cli/written-flags.ts +131 -0
- package/src/domain/errors.ts +1 -0
- package/src/domain/location-text.ts +17 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,79 @@
|
|
|
2
2
|
|
|
3
3
|
This package is a fork of `openapi-typescript`. The entries above `7.13.0` are releases of openapi-typesafe; `7.13.0` and the entries below it are the upstream history up to the fork point (upstream commit `d46a319a`), with `openapi-typescript` versions.
|
|
4
4
|
|
|
5
|
+
## 1.0.0-rc.6
|
|
6
|
+
|
|
7
|
+
### Patch Changes
|
|
8
|
+
|
|
9
|
+
- [#91](https://github.com/openapi-typesafe/openapi-typesafe/pull/91) [`b3fcff5`](https://github.com/openapi-typesafe/openapi-typesafe/commit/b3fcff541be5f8ddd0bbea19b95a60df26fda92e) Thanks [@fedorello](https://github.com/fedorello)! - A `$ref` to the `additionalProperties` of an object that also has `properties`
|
|
10
|
+
(`#/components/schemas/Stats/additionalProperties`) is typed as that schema again. Since the index
|
|
11
|
+
signature also takes the property types, `Stats["additionalProperties"]` read
|
|
12
|
+
`number | { count?: number }`, which accepted `5`, and a `$ref` one level deeper
|
|
13
|
+
(`.../additionalProperties/properties/count`) did not compile, also with `--fix ref-pointers`.
|
|
14
|
+
|
|
15
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - With several APIs in `redocly.yaml`, an error about an output file (`OUTPUT_STALE`,
|
|
16
|
+
`OUTPUT_READ_FAILED`, `OUTPUT_WRITE_FAILED`, `CLI_OUTPUT_INVALID`, `CLI_OUTPUT_IS_INPUT`,
|
|
17
|
+
`CLI_OUTPUT_DUPLICATE`) starts with `API <name>:`, as the other errors of an API do.
|
|
18
|
+
|
|
19
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - `--check` compares lines, not line ends: an output that Git checked out with CRLF line ends
|
|
20
|
+
(`core.autocrlf` on Windows) is no longer reported as stale (`OUTPUT_STALE`).
|
|
21
|
+
|
|
22
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - A path with a colon in its name, such as `-o out:v2.ts` or the schema `spec:v2.yaml`, is read as a
|
|
23
|
+
file path. Any text before a colon was taken as a URL scheme, so the output failed with
|
|
24
|
+
`CLI_OUTPUT_INVALID`. A location is a URL when it starts with `http:`, `https:` or `file:`, or with
|
|
25
|
+
a scheme and `//`.
|
|
26
|
+
|
|
27
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - Warnings and errors on stderr get colors only when stderr is a terminal. A log file that stderr
|
|
28
|
+
was redirected to got color escape sequences whenever stdout was a terminal.
|
|
29
|
+
|
|
30
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - The Node API's `cwd` option is read like `localRefRoot`: a string such as `"file:///app/specs/"` is
|
|
31
|
+
a URL, where it was read as a relative path. A `cwd` of another type (a number), a URL of another
|
|
32
|
+
scheme, or a relative `localRefRoot` with an `http:` `cwd` rejects with `OPTION_INVALID` instead of
|
|
33
|
+
resolving a wrong directory or throwing a plain `TypeError`.
|
|
34
|
+
|
|
35
|
+
- [#91](https://github.com/openapi-typesafe/openapi-typesafe/pull/91) [`b3fcff5`](https://github.com/openapi-typesafe/openapi-typesafe/commit/b3fcff541be5f8ddd0bbea19b95a60df26fda92e) Thanks [@fedorello](https://github.com/fedorello)! - `--enum-values` with `--path-params-as-types`: the arrays of a path with a `number` or `boolean`
|
|
36
|
+
path parameter compile. Their type indexed `FlattenedDeepRequired<paths>["/items/{id}"]`, a key
|
|
37
|
+
the type does not have (TS2339); it is now indexed by the path's template
|
|
38
|
+
(``FlattenedDeepRequired<paths>[`/items/${number}`]``), as a `$ref` into that path already was.
|
|
39
|
+
|
|
40
|
+
- [#91](https://github.com/openapi-typesafe/openapi-typesafe/pull/91) [`b3fcff5`](https://github.com/openapi-typesafe/openapi-typesafe/commit/b3fcff541be5f8ddd0bbea19b95a60df26fda92e) Thanks [@fedorello](https://github.com/fedorello)! - With `--exclude-deprecated`, a `$ref` to a property it leaves out is typed as that property's
|
|
41
|
+
schema also when the object has `additionalProperties`. It was read through the index signature,
|
|
42
|
+
so a `type: string` property was typed as the `additionalProperties` type (`number`), without an
|
|
43
|
+
error.
|
|
44
|
+
|
|
45
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - A second schema argument fails with the new error `CLI_INPUT_EXTRA` (exit code 2). Only the first
|
|
46
|
+
one was generated, so an unquoted glob that the shell expanded (`g/*.yaml`) generated one schema and
|
|
47
|
+
passed.
|
|
48
|
+
|
|
49
|
+
- [#93](https://github.com/openapi-typesafe/openapi-typesafe/pull/93) [`25f631d`](https://github.com/openapi-typesafe/openapi-typesafe/commit/25f631d8dabed24e18a859b08e8395496e145118) Thanks [@fedorello](https://github.com/fedorello)! - Inline `postTransform` and `transformProperty` callbacks in `openapiTS(source, { … })` without
|
|
50
|
+
`irCallbacks` get their parameter types again (`ts.TypeNode`, `ts.PropertySignature`) instead of
|
|
51
|
+
an implicit `any` that `strict` rejects (TS7006), as with openapi-typescript 7.
|
|
52
|
+
|
|
53
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - The `--no-` form of a flag that takes a value (`--no-output`, `--no-redocly`, `--no-fix`,
|
|
54
|
+
`--no-local-ref-root`, `--no-remote-ref-hosts`) fails with `CLI_FLAG_INVALID`. It used the text
|
|
55
|
+
`false` as the value: `--no-output` wrote the types to a file named `false`.
|
|
56
|
+
|
|
57
|
+
- [#91](https://github.com/openapi-typesafe/openapi-typesafe/pull/91) [`b3fcff5`](https://github.com/openapi-typesafe/openapi-typesafe/commit/b3fcff541be5f8ddd0bbea19b95a60df26fda92e) Thanks [@fedorello](https://github.com/fedorello)! - A `$ref` to one pattern of `patternProperties`
|
|
58
|
+
(`#/components/schemas/Labels/patternProperties/^label_`) compiles without `--fix`: it is typed as
|
|
59
|
+
that pattern's schema instead of `components["schemas"]["Labels"]["patternProperties"]["^label_"]`
|
|
60
|
+
(TS2339).
|
|
61
|
+
|
|
62
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - With several APIs in `redocly.yaml`, each API is linted with its own `rules` (and other lint
|
|
63
|
+
settings) from `apis.<name>`, as `redocly lint <name>` lints it. Every API was linted with the
|
|
64
|
+
root rules only.
|
|
65
|
+
|
|
66
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - Remote `$ref` documents whose queries end in the same text after a `/` (a branch such as
|
|
67
|
+
`?ref=feature/new-api`) are each named after their own file. The pet document was named after the
|
|
68
|
+
owner document (`components["schemas"]["owner"]`), and the owner one `owner-2`.
|
|
69
|
+
|
|
70
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - Relative `$ref`s of a remote document that redirects resolve from the URL it was redirected to.
|
|
71
|
+
A spec URL `https://host/latest` that redirects to `/static/v1/root.yaml` loaded `./pet.yaml` from
|
|
72
|
+
`https://host/pet.yaml`, which gave the types of another file or failed with a 404.
|
|
73
|
+
|
|
74
|
+
- [#92](https://github.com/openapi-typesafe/openapi-typesafe/pull/92) [`cf9c4db`](https://github.com/openapi-typesafe/openapi-typesafe/commit/cf9c4db102193818022a30b46d6476bef2f98a93) Thanks [@fedorello](https://github.com/fedorello)! - An unknown flag before the schema no longer takes the schema path as its value:
|
|
75
|
+
`--alphabetise api.yaml -o out.ts` prints the notice for the flag and generates `api.yaml`, where it
|
|
76
|
+
read stdin instead. A schema path that looks like a number (`1.10`, `0x10`) is read as written.
|
|
77
|
+
|
|
5
78
|
## 1.0.0-rc.5
|
|
6
79
|
|
|
7
80
|
### Patch Changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const REMOTE_URL_RE = /^https?:\/\//;
|
|
4
|
+
const PATH_SEPARATOR_RE = /[/\\]/;
|
|
5
|
+
const EXTENSION_RE = /\.[^.]+$/;
|
|
6
|
+
function redoclyBaseName(ref) {
|
|
7
|
+
return (ref.split(PATH_SEPARATOR_RE).at(-1) ?? "").replace(EXTENSION_RE, "");
|
|
8
|
+
}
|
|
9
|
+
function queryFreeBaseName(url) {
|
|
10
|
+
const withoutQuery = new URL(url.pathname, url.origin).href;
|
|
11
|
+
return redoclyBaseName(withoutQuery) || url.hostname;
|
|
12
|
+
}
|
|
13
|
+
function documentNamesOf(ref) {
|
|
14
|
+
const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : void 0;
|
|
15
|
+
if (url === void 0 || url.search === "") {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
const names = { named: redoclyBaseName(ref), clean: queryFreeBaseName(url) };
|
|
19
|
+
return names.named === names.clean ? void 0 : names;
|
|
20
|
+
}
|
|
21
|
+
function isNode(value) {
|
|
22
|
+
return typeof value === "object" && value !== null;
|
|
23
|
+
}
|
|
24
|
+
function nodesOf(parsed) {
|
|
25
|
+
const visited = /* @__PURE__ */ new Set();
|
|
26
|
+
const pending = isNode(parsed) ? [parsed] : [];
|
|
27
|
+
for (let node = pending.pop(); node !== void 0; node = pending.pop()) {
|
|
28
|
+
if (!visited.has(node)) {
|
|
29
|
+
visited.add(node);
|
|
30
|
+
pending.push(...Object.values(node).filter(isNode));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return [...visited];
|
|
34
|
+
}
|
|
35
|
+
function queryDocumentNames(documents) {
|
|
36
|
+
const byNode = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const { source, parsed } of documents) {
|
|
38
|
+
const names = documentNamesOf(source);
|
|
39
|
+
if (names === void 0) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
for (const node of nodesOf(parsed)) {
|
|
43
|
+
byNode.set(node, names);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return byNode;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
exports.queryDocumentNames = queryDocumentNames;
|
|
50
|
+
//# sourceMappingURL=query-document-names.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-document-names.cjs","sources":["../../../src/adapters/redocly-loader/query-document-names.ts"],"sourcesContent":["import type { LoadedDocument } from \"./unsupported-keys.js\";\n\n/**\n * Which loaded document with a query each node comes from, and the names Redocly and the loader\n * give that document. Redocly names a component after the text behind the last `/` of the URL,\n * query included, so `pet.json?ref=feature/new-api` and `owner.yaml?ref=feature/new-api` both give\n * `new-api`: the name alone cannot tell the documents apart, the node can.\n */\n\nconst REMOTE_URL_RE = /^https?:\\/\\//;\nconst PATH_SEPARATOR_RE = /[/\\\\]/;\nconst EXTENSION_RE = /\\.[^.]+$/;\n\n/** The base name Redocly gives a document and the one without the query. */\nexport interface DocumentNames {\n readonly named: string;\n readonly clean: string;\n}\n\n/** The base name Redocly 1.34 gives a document (`refBaseName`): the last segment, no extension. */\nfunction redoclyBaseName(ref: string): string {\n return (ref.split(PATH_SEPARATOR_RE).at(-1) ?? \"\").replace(EXTENSION_RE, \"\");\n}\n\n/**\n * The base name of the URL without its query and fragment. A URL with no last segment\n * (`https://host/?key=…`) is named after its host, so the name is never empty.\n */\nfunction queryFreeBaseName(url: URL): string {\n const withoutQuery = new URL(url.pathname, url.origin).href;\n return redoclyBaseName(withoutQuery) || url.hostname;\n}\n\n/** The names of a remote document with a query whose Redocly name keeps it; else undefined. */\nfunction documentNamesOf(ref: string): DocumentNames | undefined {\n const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : undefined;\n if (url === undefined || url.search === \"\") {\n return undefined;\n }\n const names = { named: redoclyBaseName(ref), clean: queryFreeBaseName(url) };\n return names.named === names.clean ? undefined : names;\n}\n\nfunction isNode(value: unknown): value is object {\n return typeof value === \"object\" && value !== null;\n}\n\n/** Every object and array of a parsed document, each once (a YAML alias can repeat one). */\nfunction nodesOf(parsed: unknown): object[] {\n const visited = new Set<object>();\n const pending = isNode(parsed) ? [parsed] : [];\n for (let node = pending.pop(); node !== undefined; node = pending.pop()) {\n if (!visited.has(node)) {\n visited.add(node);\n pending.push(...Object.values(node).filter(isNode));\n }\n }\n return [...visited];\n}\n\n/**\n * The names of the document each node of a loaded document with a query belongs to. The bundle\n * keeps the nodes of the loaded documents as its components, so a component finds its document.\n */\nexport function queryDocumentNames(\n documents: readonly LoadedDocument[],\n): ReadonlyMap<object, DocumentNames> {\n const byNode = new Map<object, DocumentNames>();\n for (const { source, parsed } of documents) {\n const names = documentNamesOf(source);\n if (names === undefined) {\n continue;\n }\n for (const node of nodesOf(parsed)) {\n byNode.set(node, names);\n }\n }\n return byNode;\n}\n"],"names":[],"mappings":";;AASA,MAAM,aAAA,GAAgB,cAAA;AACtB,MAAM,iBAAA,GAAoB,OAAA;AAC1B,MAAM,YAAA,GAAe,UAAA;AASrB,SAAS,gBAAgB,GAAA,EAAqB;AAC5C,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,iBAAiB,CAAA,CAAE,EAAA,CAAG,EAAE,CAAA,IAAK,EAAA,EAAI,OAAA,CAAQ,YAAA,EAAc,EAAE,CAAA;AAC7E;AAMA,SAAS,kBAAkB,GAAA,EAAkB;AAC3C,EAAA,MAAM,eAAe,IAAI,GAAA,CAAI,IAAI,QAAA,EAAU,GAAA,CAAI,MAAM,CAAA,CAAE,IAAA;AACvD,EAAA,OAAO,eAAA,CAAgB,YAAY,CAAA,IAAK,GAAA,CAAI,QAAA;AAC9C;AAGA,SAAS,gBAAgB,GAAA,EAAwC;AAC/D,EAAA,MAAM,GAAA,GAAM,aAAA,CAAc,IAAA,CAAK,GAAG,CAAA,IAAK,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,GAAI,IAAI,GAAA,CAAI,GAAG,CAAA,GAAI,MAAA;AAC1E,EAAA,IAAI,GAAA,KAAQ,MAAA,IAAa,GAAA,CAAI,MAAA,KAAW,EAAA,EAAI;AAC1C,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,KAAA,GAAQ,EAAE,KAAA,EAAO,eAAA,CAAgB,GAAG,CAAA,EAAG,KAAA,EAAO,iBAAA,CAAkB,GAAG,CAAA,EAAE;AAC3E,EAAA,OAAO,KAAA,CAAM,KAAA,KAAU,KAAA,CAAM,KAAA,GAAQ,MAAA,GAAY,KAAA;AACnD;AAEA,SAAS,OAAO,KAAA,EAAiC;AAC/C,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA;AAChD;AAGA,SAAS,QAAQ,MAAA,EAA2B;AAC1C,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,EAAA,MAAM,UAAU,MAAA,CAAO,MAAM,IAAI,CAAC,MAAM,IAAI,EAAC;AAC7C,EAAA,KAAA,IAAS,IAAA,GAAO,QAAQ,GAAA,EAAI,EAAG,SAAS,MAAA,EAAW,IAAA,GAAO,OAAA,CAAQ,GAAA,EAAI,EAAG;AACvE,IAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA,EAAG;AACtB,MAAA,OAAA,CAAQ,IAAI,IAAI,CAAA;AAChB,MAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA,CAAE,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,IACpD;AAAA,EACF;AACA,EAAA,OAAO,CAAC,GAAG,OAAO,CAAA;AACpB;AAMO,SAAS,mBACd,SAAA,EACoC;AACpC,EAAA,MAAM,MAAA,uBAAa,GAAA,EAA2B;AAC9C,EAAA,KAAA,MAAW,EAAE,MAAA,EAAQ,MAAA,EAAO,IAAK,SAAA,EAAW;AAC1C,IAAA,MAAM,KAAA,GAAQ,gBAAgB,MAAM,CAAA;AACpC,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA;AAAA,IACF;AACA,IAAA,KAAA,MAAW,IAAA,IAAQ,OAAA,CAAQ,MAAM,CAAA,EAAG;AAClC,MAAA,MAAA,CAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AAAA,IACxB;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;;"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const REMOTE_URL_RE = /^https?:\/\//;
|
|
2
|
+
const PATH_SEPARATOR_RE = /[/\\]/;
|
|
3
|
+
const EXTENSION_RE = /\.[^.]+$/;
|
|
4
|
+
function redoclyBaseName(ref) {
|
|
5
|
+
return (ref.split(PATH_SEPARATOR_RE).at(-1) ?? "").replace(EXTENSION_RE, "");
|
|
6
|
+
}
|
|
7
|
+
function queryFreeBaseName(url) {
|
|
8
|
+
const withoutQuery = new URL(url.pathname, url.origin).href;
|
|
9
|
+
return redoclyBaseName(withoutQuery) || url.hostname;
|
|
10
|
+
}
|
|
11
|
+
function documentNamesOf(ref) {
|
|
12
|
+
const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : void 0;
|
|
13
|
+
if (url === void 0 || url.search === "") {
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
const names = { named: redoclyBaseName(ref), clean: queryFreeBaseName(url) };
|
|
17
|
+
return names.named === names.clean ? void 0 : names;
|
|
18
|
+
}
|
|
19
|
+
function isNode(value) {
|
|
20
|
+
return typeof value === "object" && value !== null;
|
|
21
|
+
}
|
|
22
|
+
function nodesOf(parsed) {
|
|
23
|
+
const visited = /* @__PURE__ */ new Set();
|
|
24
|
+
const pending = isNode(parsed) ? [parsed] : [];
|
|
25
|
+
for (let node = pending.pop(); node !== void 0; node = pending.pop()) {
|
|
26
|
+
if (!visited.has(node)) {
|
|
27
|
+
visited.add(node);
|
|
28
|
+
pending.push(...Object.values(node).filter(isNode));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return [...visited];
|
|
32
|
+
}
|
|
33
|
+
function queryDocumentNames(documents) {
|
|
34
|
+
const byNode = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const { source, parsed } of documents) {
|
|
36
|
+
const names = documentNamesOf(source);
|
|
37
|
+
if (names === void 0) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
for (const node of nodesOf(parsed)) {
|
|
41
|
+
byNode.set(node, names);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return byNode;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { queryDocumentNames };
|
|
48
|
+
//# sourceMappingURL=query-document-names.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-document-names.mjs","sources":["../../../src/adapters/redocly-loader/query-document-names.ts"],"sourcesContent":["import type { LoadedDocument } from \"./unsupported-keys.js\";\n\n/**\n * Which loaded document with a query each node comes from, and the names Redocly and the loader\n * give that document. Redocly names a component after the text behind the last `/` of the URL,\n * query included, so `pet.json?ref=feature/new-api` and `owner.yaml?ref=feature/new-api` both give\n * `new-api`: the name alone cannot tell the documents apart, the node can.\n */\n\nconst REMOTE_URL_RE = /^https?:\\/\\//;\nconst PATH_SEPARATOR_RE = /[/\\\\]/;\nconst EXTENSION_RE = /\\.[^.]+$/;\n\n/** The base name Redocly gives a document and the one without the query. */\nexport interface DocumentNames {\n readonly named: string;\n readonly clean: string;\n}\n\n/** The base name Redocly 1.34 gives a document (`refBaseName`): the last segment, no extension. */\nfunction redoclyBaseName(ref: string): string {\n return (ref.split(PATH_SEPARATOR_RE).at(-1) ?? \"\").replace(EXTENSION_RE, \"\");\n}\n\n/**\n * The base name of the URL without its query and fragment. A URL with no last segment\n * (`https://host/?key=…`) is named after its host, so the name is never empty.\n */\nfunction queryFreeBaseName(url: URL): string {\n const withoutQuery = new URL(url.pathname, url.origin).href;\n return redoclyBaseName(withoutQuery) || url.hostname;\n}\n\n/** The names of a remote document with a query whose Redocly name keeps it; else undefined. */\nfunction documentNamesOf(ref: string): DocumentNames | undefined {\n const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : undefined;\n if (url === undefined || url.search === \"\") {\n return undefined;\n }\n const names = { named: redoclyBaseName(ref), clean: queryFreeBaseName(url) };\n return names.named === names.clean ? undefined : names;\n}\n\nfunction isNode(value: unknown): value is object {\n return typeof value === \"object\" && value !== null;\n}\n\n/** Every object and array of a parsed document, each once (a YAML alias can repeat one). */\nfunction nodesOf(parsed: unknown): object[] {\n const visited = new Set<object>();\n const pending = isNode(parsed) ? [parsed] : [];\n for (let node = pending.pop(); node !== undefined; node = pending.pop()) {\n if (!visited.has(node)) {\n visited.add(node);\n pending.push(...Object.values(node).filter(isNode));\n }\n }\n return [...visited];\n}\n\n/**\n * The names of the document each node of a loaded document with a query belongs to. The bundle\n * keeps the nodes of the loaded documents as its components, so a component finds its document.\n */\nexport function queryDocumentNames(\n documents: readonly LoadedDocument[],\n): ReadonlyMap<object, DocumentNames> {\n const byNode = new Map<object, DocumentNames>();\n for (const { source, parsed } of documents) {\n const names = documentNamesOf(source);\n if (names === undefined) {\n continue;\n }\n for (const node of nodesOf(parsed)) {\n byNode.set(node, names);\n }\n }\n return byNode;\n}\n"],"names":[],"mappings":"AASA,MAAM,aAAA,GAAgB,cAAA;AACtB,MAAM,iBAAA,GAAoB,OAAA;AAC1B,MAAM,YAAA,GAAe,UAAA;AASrB,SAAS,gBAAgB,GAAA,EAAqB;AAC5C,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,iBAAiB,CAAA,CAAE,EAAA,CAAG,EAAE,CAAA,IAAK,EAAA,EAAI,OAAA,CAAQ,YAAA,EAAc,EAAE,CAAA;AAC7E;AAMA,SAAS,kBAAkB,GAAA,EAAkB;AAC3C,EAAA,MAAM,eAAe,IAAI,GAAA,CAAI,IAAI,QAAA,EAAU,GAAA,CAAI,MAAM,CAAA,CAAE,IAAA;AACvD,EAAA,OAAO,eAAA,CAAgB,YAAY,CAAA,IAAK,GAAA,CAAI,QAAA;AAC9C;AAGA,SAAS,gBAAgB,GAAA,EAAwC;AAC/D,EAAA,MAAM,GAAA,GAAM,aAAA,CAAc,IAAA,CAAK,GAAG,CAAA,IAAK,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,GAAI,IAAI,GAAA,CAAI,GAAG,CAAA,GAAI,MAAA;AAC1E,EAAA,IAAI,GAAA,KAAQ,MAAA,IAAa,GAAA,CAAI,MAAA,KAAW,EAAA,EAAI;AAC1C,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,KAAA,GAAQ,EAAE,KAAA,EAAO,eAAA,CAAgB,GAAG,CAAA,EAAG,KAAA,EAAO,iBAAA,CAAkB,GAAG,CAAA,EAAE;AAC3E,EAAA,OAAO,KAAA,CAAM,KAAA,KAAU,KAAA,CAAM,KAAA,GAAQ,MAAA,GAAY,KAAA;AACnD;AAEA,SAAS,OAAO,KAAA,EAAiC;AAC/C,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA;AAChD;AAGA,SAAS,QAAQ,MAAA,EAA2B;AAC1C,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,EAAA,MAAM,UAAU,MAAA,CAAO,MAAM,IAAI,CAAC,MAAM,IAAI,EAAC;AAC7C,EAAA,KAAA,IAAS,IAAA,GAAO,QAAQ,GAAA,EAAI,EAAG,SAAS,MAAA,EAAW,IAAA,GAAO,OAAA,CAAQ,GAAA,EAAI,EAAG;AACvE,IAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA,EAAG;AACtB,MAAA,OAAA,CAAQ,IAAI,IAAI,CAAA;AAChB,MAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA,CAAE,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,IACpD;AAAA,EACF;AACA,EAAA,OAAO,CAAC,GAAG,OAAO,CAAA;AACpB;AAMO,SAAS,mBACd,SAAA,EACoC;AACpC,EAAA,MAAM,MAAA,uBAAa,GAAA,EAA2B;AAC9C,EAAA,KAAA,MAAW,EAAE,MAAA,EAAQ,MAAA,EAAO,IAAK,SAAA,EAAW;AAC1C,IAAA,MAAM,KAAA,GAAQ,gBAAgB,MAAM,CAAA;AACpC,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA;AAAA,IACF;AACA,IAAA,KAAA,MAAW,IAAA,IAAQ,OAAA,CAAQ,MAAM,CAAA,EAAG;AAClC,MAAA,MAAA,CAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AAAA,IACxB;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;;"}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const jsonPointer = require('../../domain/openapi/json-pointer.cjs');
|
|
4
|
+
const queryDocumentNames = require('./query-document-names.cjs');
|
|
4
5
|
const refHolders = require('./ref-holders.cjs');
|
|
5
6
|
|
|
6
7
|
const COMPONENTS_KEY = "components";
|
|
7
|
-
const REMOTE_URL_RE = /^https?:\/\//;
|
|
8
|
-
const PATH_SEPARATOR_RE = /[/\\]/;
|
|
9
|
-
const EXTENSION_RE = /\.[^.]+$/;
|
|
10
8
|
const NAME_SUFFIX_STARTS = ["_", "-"];
|
|
11
9
|
const FIRST_SERIAL = 2;
|
|
10
|
+
const REDOCLY_SERIAL_RE = /-\d+$/;
|
|
12
11
|
function isContainer(value) {
|
|
13
12
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
13
|
}
|
|
@@ -32,34 +31,13 @@ function componentKeysOf(document) {
|
|
|
32
31
|
}
|
|
33
32
|
return keys;
|
|
34
33
|
}
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
function queryFreeBaseName(url) {
|
|
39
|
-
const withoutQuery = new URL(url.pathname, url.origin).href;
|
|
40
|
-
return redoclyBaseName(withoutQuery) || url.hostname;
|
|
41
|
-
}
|
|
42
|
-
function queryBaseNames(documentRefs) {
|
|
43
|
-
const names = /* @__PURE__ */ new Map();
|
|
44
|
-
for (const ref of documentRefs) {
|
|
45
|
-
const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : void 0;
|
|
46
|
-
if (url === void 0 || url.search === "") {
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
const named = redoclyBaseName(ref);
|
|
50
|
-
const clean = queryFreeBaseName(url);
|
|
51
|
-
if (named !== clean) {
|
|
52
|
-
names.set(named, clean);
|
|
53
|
-
}
|
|
34
|
+
function queryFreeName(key, names) {
|
|
35
|
+
if (names === void 0) {
|
|
36
|
+
return void 0;
|
|
54
37
|
}
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const matches = [...baseNames.keys()].filter(
|
|
59
|
-
(named2) => key === named2 || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named2}${start}`))
|
|
60
|
-
);
|
|
61
|
-
const named = matches.sort((a, b) => b.length - a.length)[0];
|
|
62
|
-
return named === void 0 ? void 0 : `${baseNames.get(named)}${key.slice(named.length)}`;
|
|
38
|
+
const { named, clean } = names;
|
|
39
|
+
const hasBaseName = key === named || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named}${start}`));
|
|
40
|
+
return hasBaseName ? `${clean}${key.slice(named.length).replace(REDOCLY_SERIAL_RE, "")}` : void 0;
|
|
63
41
|
}
|
|
64
42
|
function freeName(name, taken) {
|
|
65
43
|
let free = name;
|
|
@@ -68,11 +46,13 @@ function freeName(name, taken) {
|
|
|
68
46
|
}
|
|
69
47
|
return free;
|
|
70
48
|
}
|
|
71
|
-
function renameGroup(entries, keysBefore,
|
|
49
|
+
function renameGroup(entries, keysBefore, documentNames) {
|
|
72
50
|
const renames = /* @__PURE__ */ new Map();
|
|
73
51
|
const taken = new Set(Object.keys(entries));
|
|
74
52
|
for (const key of Object.keys(entries)) {
|
|
75
|
-
const
|
|
53
|
+
const node = entries[key];
|
|
54
|
+
const names = typeof node === "object" && node !== null ? documentNames.get(node) : void 0;
|
|
55
|
+
const name = keysBefore.has(key) ? void 0 : queryFreeName(key, names);
|
|
76
56
|
if (name !== void 0) {
|
|
77
57
|
taken.delete(key);
|
|
78
58
|
const free = freeName(name, taken);
|
|
@@ -86,6 +66,17 @@ function renameGroup(entries, keysBefore, baseNames) {
|
|
|
86
66
|
}
|
|
87
67
|
return renames;
|
|
88
68
|
}
|
|
69
|
+
const COLLISION_RE = /^Two schemas are referenced with the same name but different content\. Renamed (.+) to (.+)\.$/;
|
|
70
|
+
const SERIAL_RE = /^-\d+$/;
|
|
71
|
+
function resolvesCollision(message, renames) {
|
|
72
|
+
const [, first, second] = COLLISION_RE.exec(message) ?? [];
|
|
73
|
+
if (first === void 0 || second === void 0) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const renamed = new Map([...renames.values()].flatMap((group) => [...group]));
|
|
77
|
+
const [newFirst, newSecond] = [renamed.get(first) ?? first, renamed.get(second) ?? second];
|
|
78
|
+
return !(newSecond.startsWith(newFirst) && SERIAL_RE.test(newSecond.slice(newFirst.length)));
|
|
79
|
+
}
|
|
89
80
|
function renamedPointer(pointer, renames) {
|
|
90
81
|
if (!pointer.startsWith(`${jsonPointer.ROOT_POINTER}/`)) {
|
|
91
82
|
return pointer;
|
|
@@ -111,12 +102,12 @@ function rewriteRefs(document, renaming) {
|
|
|
111
102
|
}
|
|
112
103
|
}
|
|
113
104
|
}
|
|
114
|
-
function renameQueryNamedComponents(bundled, keysBefore,
|
|
115
|
-
const
|
|
105
|
+
function renameQueryNamedComponents(bundled, keysBefore, documents) {
|
|
106
|
+
const documentNames = queryDocumentNames.queryDocumentNames(documents);
|
|
116
107
|
const renames = /* @__PURE__ */ new Map();
|
|
117
|
-
if (
|
|
108
|
+
if (documentNames.size > 0) {
|
|
118
109
|
for (const [group, entries] of Object.entries(componentsOf(bundled))) {
|
|
119
|
-
const groupRenames = renameGroup(entries, keysBefore.get(group) ?? /* @__PURE__ */ new Set(),
|
|
110
|
+
const groupRenames = renameGroup(entries, keysBefore.get(group) ?? /* @__PURE__ */ new Set(), documentNames);
|
|
120
111
|
if (groupRenames.size > 0) {
|
|
121
112
|
renames.set(group, groupRenames);
|
|
122
113
|
}
|
|
@@ -124,7 +115,8 @@ function renameQueryNamedComponents(bundled, keysBefore, documentRefs) {
|
|
|
124
115
|
}
|
|
125
116
|
const renaming = {
|
|
126
117
|
pointer: (pointer) => renamedPointer(pointer, renames),
|
|
127
|
-
text: (text) => renamedText(text, renames)
|
|
118
|
+
text: (text) => renamedText(text, renames),
|
|
119
|
+
isResolvedCollision: (message) => resolvesCollision(message, renames)
|
|
128
120
|
};
|
|
129
121
|
if (renames.size > 0) {
|
|
130
122
|
rewriteRefs(bundled, renaming);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-named-components.cjs","sources":["../../../src/adapters/redocly-loader/query-named-components.ts"],"sourcesContent":["import {\n appendPointer,\n canonicalRef,\n parseRefPointer,\n ROOT_POINTER,\n} from \"../../domain/openapi/json-pointer.js\";\nimport { findMappings, findRefHolders } from \"./ref-holders.js\";\n\n/**\n * Redocly 1.x bundles an external `$ref` into a component named after the last segment of the\n * document's URL, without its extension, when the pointer gives no free name (a `$ref` to a whole\n * document, or two targets with the same pointer). The query stays in that name, and a query holds\n * API keys and signatures, so `https://host/schemas/pet?token=…` became the component\n * `pet?token=…` in the generated file and in messages (threat model T4b). This renames such a\n * component after the URL without its query: `pet`, with a `-2` suffix when that name is taken.\n */\n\ntype Components = Record<string, Record<string, unknown>>;\n\n/** The keys of each `components` group before bundling: the bundle edits the document in place. */\nexport type ComponentKeys = ReadonlyMap<string, ReadonlySet<string>>;\n\n/** Old and new name of each renamed component, per `components` group. */\ntype Renames = Map<string, Map<string, string>>;\n\nconst COMPONENTS_KEY = \"components\";\nconst REMOTE_URL_RE = /^https?:\\/\\//;\nconst PATH_SEPARATOR_RE = /[/\\\\]/;\nconst EXTENSION_RE = /\\.[^.]+$/;\n/** What Redocly puts between the base name and the rest of a component name. */\nconst NAME_SUFFIX_STARTS = [\"_\", \"-\"];\nconst FIRST_SERIAL = 2;\n\nfunction isContainer(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction componentsOf(document: unknown): Components {\n const components = isContainer(document) ? document[COMPONENTS_KEY] : undefined;\n if (!isContainer(components)) {\n return {};\n }\n const groups: Components = {};\n for (const [group, entries] of Object.entries(components)) {\n if (isContainer(entries)) {\n groups[group] = entries;\n }\n }\n return groups;\n}\n\n/** The keys of every `components` group of a document. */\nexport function componentKeysOf(document: unknown): ComponentKeys {\n const keys = new Map<string, ReadonlySet<string>>();\n for (const [group, entries] of Object.entries(componentsOf(document))) {\n keys.set(group, new Set(Object.keys(entries)));\n }\n return keys;\n}\n\n/** The base name Redocly 1.34 gives a document (`refBaseName`): the last segment, no extension. */\nfunction redoclyBaseName(ref: string): string {\n return (ref.split(PATH_SEPARATOR_RE).at(-1) ?? \"\").replace(EXTENSION_RE, \"\");\n}\n\n/**\n * The base name of the URL without its query and fragment. A URL with no last segment\n * (`https://host/?key=…`) is named after its host, so the name is never empty.\n */\nfunction queryFreeBaseName(url: URL): string {\n const withoutQuery = new URL(url.pathname, url.origin).href;\n return redoclyBaseName(withoutQuery) || url.hostname;\n}\n\n/** Each base name Redocly takes from a remote URL with a query, with the name to use instead. */\nfunction queryBaseNames(documentRefs: Iterable<string>): Map<string, string> {\n const names = new Map<string, string>();\n for (const ref of documentRefs) {\n const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : undefined;\n if (url === undefined || url.search === \"\") {\n continue;\n }\n const named = redoclyBaseName(ref);\n const clean = queryFreeBaseName(url);\n if (named !== clean) {\n names.set(named, clean);\n }\n }\n return names;\n}\n\n/** The name `key` gets, or undefined when no query base name starts it. */\nfunction queryFreeName(key: string, baseNames: ReadonlyMap<string, string>): string | undefined {\n // The longest base name first: `pet?a=1` must not match as `pet?a` when both were loaded.\n const matches = [...baseNames.keys()].filter(\n (named) =>\n key === named || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named}${start}`)),\n );\n const named = matches.sort((a, b) => b.length - a.length)[0];\n return named === undefined ? undefined : `${baseNames.get(named)}${key.slice(named.length)}`;\n}\n\nfunction freeName(name: string, taken: ReadonlySet<string>): string {\n let free = name;\n for (let serial = FIRST_SERIAL; taken.has(free); serial += 1) {\n free = `${name}-${serial}`;\n }\n return free;\n}\n\n/** Rename the bundled components of one group in place, keeping their order. */\nfunction renameGroup(\n entries: Record<string, unknown>,\n keysBefore: ReadonlySet<string>,\n baseNames: ReadonlyMap<string, string>,\n): Map<string, string> {\n const renames = new Map<string, string>();\n const taken = new Set(Object.keys(entries));\n for (const key of Object.keys(entries)) {\n const name = keysBefore.has(key) ? undefined : queryFreeName(key, baseNames);\n if (name !== undefined) {\n taken.delete(key);\n const free = freeName(name, taken);\n taken.add(free);\n renames.set(key, free);\n }\n }\n for (const [key, value] of Object.entries(entries)) {\n delete entries[key];\n entries[renames.get(key) ?? key] = value;\n }\n return renames;\n}\n\n/** Rewrites pointers and messages that name a renamed component. */\nexport interface ComponentRenaming {\n /** The pointer with a renamed component's new name; any other text as it is. */\n readonly pointer: (pointer: string) => string;\n /** The text with each renamed component's old name replaced by its new name. */\n readonly text: (text: string) => string;\n}\n\nfunction renamedPointer(pointer: string, renames: Renames): string {\n if (!pointer.startsWith(`${ROOT_POINTER}/`)) {\n return pointer;\n }\n const [components, group = \"\", name = \"\", ...rest] = parseRefPointer(canonicalRef(pointer));\n const renamed = components === COMPONENTS_KEY ? renames.get(group)?.get(name) : undefined;\n return renamed === undefined\n ? pointer\n : appendPointer(ROOT_POINTER, [COMPONENTS_KEY, group, renamed, ...rest]);\n}\n\nfunction renamedText(text: string, renames: Renames): string {\n const pairs = [...renames.values()].flatMap((group) => [...group]);\n // The longest old name first, so a name that starts another one does not split it.\n pairs.sort(([a], [b]) => b.length - a.length);\n return pairs.reduce((shown, [old, renamed]) => shown.replaceAll(old, renamed), text);\n}\n\nfunction rewriteRefs(document: unknown, renaming: ComponentRenaming): void {\n for (const { node } of findRefHolders(document)) {\n node.$ref = renaming.pointer(node.$ref);\n }\n for (const mapping of findMappings(document)) {\n for (const [name, value] of Object.entries(mapping)) {\n if (typeof value === \"string\") {\n mapping[name] = renaming.pointer(value);\n }\n }\n }\n}\n\n/**\n * Rename, in place, the components the bundle named after a URL with a query, and every local\n * `$ref` and `mapping` value that names them. `documentRefs` are the refs of the loaded documents.\n */\nexport function renameQueryNamedComponents(\n bundled: unknown,\n keysBefore: ComponentKeys,\n documentRefs: Iterable<string>,\n): ComponentRenaming {\n const baseNames = queryBaseNames(documentRefs);\n const renames: Renames = new Map();\n if (baseNames.size > 0) {\n for (const [group, entries] of Object.entries(componentsOf(bundled))) {\n const groupRenames = renameGroup(entries, keysBefore.get(group) ?? new Set(), baseNames);\n if (groupRenames.size > 0) {\n renames.set(group, groupRenames);\n }\n }\n }\n const renaming: ComponentRenaming = {\n pointer: (pointer) => renamedPointer(pointer, renames),\n text: (text) => renamedText(text, renames),\n };\n if (renames.size > 0) {\n rewriteRefs(bundled, renaming);\n }\n return renaming;\n}\n"],"names":["named","ROOT_POINTER","parseRefPointer","canonicalRef","appendPointer","findRefHolders","findMappings"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query-named-components.cjs","sources":["../../../src/adapters/redocly-loader/query-named-components.ts"],"sourcesContent":["import {\n appendPointer,\n canonicalRef,\n parseRefPointer,\n ROOT_POINTER,\n} from \"../../domain/openapi/json-pointer.js\";\nimport { type DocumentNames, queryDocumentNames } from \"./query-document-names.js\";\nimport { findMappings, findRefHolders } from \"./ref-holders.js\";\nimport type { LoadedDocument } from \"./unsupported-keys.js\";\n\n/**\n * Redocly 1.x bundles an external `$ref` into a component named after the last segment of the\n * document's URL, without its extension, when the pointer gives no free name (a `$ref` to a whole\n * document, or two targets with the same pointer). The query stays in that name, and a query holds\n * API keys and signatures, so `https://host/schemas/pet?token=…` became the component\n * `pet?token=…` in the generated file and in messages (threat model T4b). This renames such a\n * component after the URL without its query: `pet`, with a `-2` suffix when that name is taken.\n * Each component is matched to its document by its node, since two URLs can give one name.\n */\n\ntype Components = Record<string, Record<string, unknown>>;\n\n/** The keys of each `components` group before bundling: the bundle edits the document in place. */\nexport type ComponentKeys = ReadonlyMap<string, ReadonlySet<string>>;\n\n/** Old and new name of each renamed component, per `components` group. */\ntype Renames = Map<string, Map<string, string>>;\n\nconst COMPONENTS_KEY = \"components\";\n/** What Redocly puts between the base name and the rest of a component name. */\nconst NAME_SUFFIX_STARTS = [\"_\", \"-\"];\nconst FIRST_SERIAL = 2;\n/**\n * The serial Redocly adds when a name is taken (`pet-2`). Two URLs that differ only in the query\n * took one name; without the query they may not, so the serial is chosen again (`freeName`).\n */\nconst REDOCLY_SERIAL_RE = /-\\d+$/;\n\nfunction isContainer(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction componentsOf(document: unknown): Components {\n const components = isContainer(document) ? document[COMPONENTS_KEY] : undefined;\n if (!isContainer(components)) {\n return {};\n }\n const groups: Components = {};\n for (const [group, entries] of Object.entries(components)) {\n if (isContainer(entries)) {\n groups[group] = entries;\n }\n }\n return groups;\n}\n\n/** The keys of every `components` group of a document. */\nexport function componentKeysOf(document: unknown): ComponentKeys {\n const keys = new Map<string, ReadonlySet<string>>();\n for (const [group, entries] of Object.entries(componentsOf(document))) {\n keys.set(group, new Set(Object.keys(entries)));\n }\n return keys;\n}\n\n/** The name a component of a document with a query gets; undefined for any other component. */\nfunction queryFreeName(key: string, names: DocumentNames | undefined): string | undefined {\n if (names === undefined) {\n return undefined;\n }\n const { named, clean } = names;\n const hasBaseName =\n key === named || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named}${start}`));\n return hasBaseName\n ? `${clean}${key.slice(named.length).replace(REDOCLY_SERIAL_RE, \"\")}`\n : undefined;\n}\n\nfunction freeName(name: string, taken: ReadonlySet<string>): string {\n let free = name;\n for (let serial = FIRST_SERIAL; taken.has(free); serial += 1) {\n free = `${name}-${serial}`;\n }\n return free;\n}\n\n/** Rename the bundled components of one group in place, keeping their order. */\nfunction renameGroup(\n entries: Record<string, unknown>,\n keysBefore: ReadonlySet<string>,\n documentNames: ReadonlyMap<object, DocumentNames>,\n): Map<string, string> {\n const renames = new Map<string, string>();\n const taken = new Set(Object.keys(entries));\n for (const key of Object.keys(entries)) {\n const node = entries[key];\n const names = typeof node === \"object\" && node !== null ? documentNames.get(node) : undefined;\n const name = keysBefore.has(key) ? undefined : queryFreeName(key, names);\n if (name !== undefined) {\n taken.delete(key);\n const free = freeName(name, taken);\n taken.add(free);\n renames.set(key, free);\n }\n }\n for (const [key, value] of Object.entries(entries)) {\n delete entries[key];\n entries[renames.get(key) ?? key] = value;\n }\n return renames;\n}\n\n/** Rewrites pointers and messages that name a renamed component. */\nexport interface ComponentRenaming {\n /** The pointer with a renamed component's new name; any other text as it is. */\n readonly pointer: (pointer: string) => string;\n /** The text with each renamed component's old name replaced by its new name. */\n readonly text: (text: string) => string;\n /**\n * Whether the problem is Redocly's report of two components with one name that the new names\n * tell apart: it renamed `new-api` to `new-api-2`, and the documents are now `pet` and `owner`.\n */\n readonly isResolvedCollision: (message: string) => boolean;\n}\n\n/** The warning Redocly 1.34 gives when it adds a serial to a component name. */\nconst COLLISION_RE =\n /^Two schemas are referenced with the same name but different content\\. Renamed (.+) to (.+)\\.$/;\nconst SERIAL_RE = /^-\\d+$/;\n\nfunction resolvesCollision(message: string, renames: Renames): boolean {\n const [, first, second] = COLLISION_RE.exec(message) ?? [];\n if (first === undefined || second === undefined) {\n return false;\n }\n const renamed = new Map([...renames.values()].flatMap((group) => [...group]));\n const [newFirst, newSecond] = [renamed.get(first) ?? first, renamed.get(second) ?? second];\n return !(newSecond.startsWith(newFirst) && SERIAL_RE.test(newSecond.slice(newFirst.length)));\n}\n\nfunction renamedPointer(pointer: string, renames: Renames): string {\n if (!pointer.startsWith(`${ROOT_POINTER}/`)) {\n return pointer;\n }\n const [components, group = \"\", name = \"\", ...rest] = parseRefPointer(canonicalRef(pointer));\n const renamed = components === COMPONENTS_KEY ? renames.get(group)?.get(name) : undefined;\n return renamed === undefined\n ? pointer\n : appendPointer(ROOT_POINTER, [COMPONENTS_KEY, group, renamed, ...rest]);\n}\n\nfunction renamedText(text: string, renames: Renames): string {\n const pairs = [...renames.values()].flatMap((group) => [...group]);\n // The longest old name first, so a name that starts another one does not split it.\n pairs.sort(([a], [b]) => b.length - a.length);\n return pairs.reduce((shown, [old, renamed]) => shown.replaceAll(old, renamed), text);\n}\n\nfunction rewriteRefs(document: unknown, renaming: ComponentRenaming): void {\n for (const { node } of findRefHolders(document)) {\n node.$ref = renaming.pointer(node.$ref);\n }\n for (const mapping of findMappings(document)) {\n for (const [name, value] of Object.entries(mapping)) {\n if (typeof value === \"string\") {\n mapping[name] = renaming.pointer(value);\n }\n }\n }\n}\n\n/**\n * Rename, in place, the components the bundle named after a URL with a query, and every local\n * `$ref` and `mapping` value that names them. `documents` are the documents the load read.\n */\nexport function renameQueryNamedComponents(\n bundled: unknown,\n keysBefore: ComponentKeys,\n documents: readonly LoadedDocument[],\n): ComponentRenaming {\n const documentNames = queryDocumentNames(documents);\n const renames: Renames = new Map();\n if (documentNames.size > 0) {\n for (const [group, entries] of Object.entries(componentsOf(bundled))) {\n const groupRenames = renameGroup(entries, keysBefore.get(group) ?? new Set(), documentNames);\n if (groupRenames.size > 0) {\n renames.set(group, groupRenames);\n }\n }\n }\n const renaming: ComponentRenaming = {\n pointer: (pointer) => renamedPointer(pointer, renames),\n text: (text) => renamedText(text, renames),\n isResolvedCollision: (message) => resolvesCollision(message, renames),\n };\n if (renames.size > 0) {\n rewriteRefs(bundled, renaming);\n }\n return renaming;\n}\n"],"names":["ROOT_POINTER","parseRefPointer","canonicalRef","appendPointer","findRefHolders","findMappings","queryDocumentNames"],"mappings":"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"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ROOT_POINTER, parseRefPointer, canonicalRef, appendPointer } from '../../domain/openapi/json-pointer.mjs';
|
|
2
|
+
import { queryDocumentNames } from './query-document-names.mjs';
|
|
2
3
|
import { findRefHolders, findMappings } from './ref-holders.mjs';
|
|
3
4
|
|
|
4
5
|
const COMPONENTS_KEY = "components";
|
|
5
|
-
const REMOTE_URL_RE = /^https?:\/\//;
|
|
6
|
-
const PATH_SEPARATOR_RE = /[/\\]/;
|
|
7
|
-
const EXTENSION_RE = /\.[^.]+$/;
|
|
8
6
|
const NAME_SUFFIX_STARTS = ["_", "-"];
|
|
9
7
|
const FIRST_SERIAL = 2;
|
|
8
|
+
const REDOCLY_SERIAL_RE = /-\d+$/;
|
|
10
9
|
function isContainer(value) {
|
|
11
10
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
11
|
}
|
|
@@ -30,34 +29,13 @@ function componentKeysOf(document) {
|
|
|
30
29
|
}
|
|
31
30
|
return keys;
|
|
32
31
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
function queryFreeBaseName(url) {
|
|
37
|
-
const withoutQuery = new URL(url.pathname, url.origin).href;
|
|
38
|
-
return redoclyBaseName(withoutQuery) || url.hostname;
|
|
39
|
-
}
|
|
40
|
-
function queryBaseNames(documentRefs) {
|
|
41
|
-
const names = /* @__PURE__ */ new Map();
|
|
42
|
-
for (const ref of documentRefs) {
|
|
43
|
-
const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : void 0;
|
|
44
|
-
if (url === void 0 || url.search === "") {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
const named = redoclyBaseName(ref);
|
|
48
|
-
const clean = queryFreeBaseName(url);
|
|
49
|
-
if (named !== clean) {
|
|
50
|
-
names.set(named, clean);
|
|
51
|
-
}
|
|
32
|
+
function queryFreeName(key, names) {
|
|
33
|
+
if (names === void 0) {
|
|
34
|
+
return void 0;
|
|
52
35
|
}
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const matches = [...baseNames.keys()].filter(
|
|
57
|
-
(named2) => key === named2 || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named2}${start}`))
|
|
58
|
-
);
|
|
59
|
-
const named = matches.sort((a, b) => b.length - a.length)[0];
|
|
60
|
-
return named === void 0 ? void 0 : `${baseNames.get(named)}${key.slice(named.length)}`;
|
|
36
|
+
const { named, clean } = names;
|
|
37
|
+
const hasBaseName = key === named || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named}${start}`));
|
|
38
|
+
return hasBaseName ? `${clean}${key.slice(named.length).replace(REDOCLY_SERIAL_RE, "")}` : void 0;
|
|
61
39
|
}
|
|
62
40
|
function freeName(name, taken) {
|
|
63
41
|
let free = name;
|
|
@@ -66,11 +44,13 @@ function freeName(name, taken) {
|
|
|
66
44
|
}
|
|
67
45
|
return free;
|
|
68
46
|
}
|
|
69
|
-
function renameGroup(entries, keysBefore,
|
|
47
|
+
function renameGroup(entries, keysBefore, documentNames) {
|
|
70
48
|
const renames = /* @__PURE__ */ new Map();
|
|
71
49
|
const taken = new Set(Object.keys(entries));
|
|
72
50
|
for (const key of Object.keys(entries)) {
|
|
73
|
-
const
|
|
51
|
+
const node = entries[key];
|
|
52
|
+
const names = typeof node === "object" && node !== null ? documentNames.get(node) : void 0;
|
|
53
|
+
const name = keysBefore.has(key) ? void 0 : queryFreeName(key, names);
|
|
74
54
|
if (name !== void 0) {
|
|
75
55
|
taken.delete(key);
|
|
76
56
|
const free = freeName(name, taken);
|
|
@@ -84,6 +64,17 @@ function renameGroup(entries, keysBefore, baseNames) {
|
|
|
84
64
|
}
|
|
85
65
|
return renames;
|
|
86
66
|
}
|
|
67
|
+
const COLLISION_RE = /^Two schemas are referenced with the same name but different content\. Renamed (.+) to (.+)\.$/;
|
|
68
|
+
const SERIAL_RE = /^-\d+$/;
|
|
69
|
+
function resolvesCollision(message, renames) {
|
|
70
|
+
const [, first, second] = COLLISION_RE.exec(message) ?? [];
|
|
71
|
+
if (first === void 0 || second === void 0) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const renamed = new Map([...renames.values()].flatMap((group) => [...group]));
|
|
75
|
+
const [newFirst, newSecond] = [renamed.get(first) ?? first, renamed.get(second) ?? second];
|
|
76
|
+
return !(newSecond.startsWith(newFirst) && SERIAL_RE.test(newSecond.slice(newFirst.length)));
|
|
77
|
+
}
|
|
87
78
|
function renamedPointer(pointer, renames) {
|
|
88
79
|
if (!pointer.startsWith(`${ROOT_POINTER}/`)) {
|
|
89
80
|
return pointer;
|
|
@@ -109,12 +100,12 @@ function rewriteRefs(document, renaming) {
|
|
|
109
100
|
}
|
|
110
101
|
}
|
|
111
102
|
}
|
|
112
|
-
function renameQueryNamedComponents(bundled, keysBefore,
|
|
113
|
-
const
|
|
103
|
+
function renameQueryNamedComponents(bundled, keysBefore, documents) {
|
|
104
|
+
const documentNames = queryDocumentNames(documents);
|
|
114
105
|
const renames = /* @__PURE__ */ new Map();
|
|
115
|
-
if (
|
|
106
|
+
if (documentNames.size > 0) {
|
|
116
107
|
for (const [group, entries] of Object.entries(componentsOf(bundled))) {
|
|
117
|
-
const groupRenames = renameGroup(entries, keysBefore.get(group) ?? /* @__PURE__ */ new Set(),
|
|
108
|
+
const groupRenames = renameGroup(entries, keysBefore.get(group) ?? /* @__PURE__ */ new Set(), documentNames);
|
|
118
109
|
if (groupRenames.size > 0) {
|
|
119
110
|
renames.set(group, groupRenames);
|
|
120
111
|
}
|
|
@@ -122,7 +113,8 @@ function renameQueryNamedComponents(bundled, keysBefore, documentRefs) {
|
|
|
122
113
|
}
|
|
123
114
|
const renaming = {
|
|
124
115
|
pointer: (pointer) => renamedPointer(pointer, renames),
|
|
125
|
-
text: (text) => renamedText(text, renames)
|
|
116
|
+
text: (text) => renamedText(text, renames),
|
|
117
|
+
isResolvedCollision: (message) => resolvesCollision(message, renames)
|
|
126
118
|
};
|
|
127
119
|
if (renames.size > 0) {
|
|
128
120
|
rewriteRefs(bundled, renaming);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-named-components.mjs","sources":["../../../src/adapters/redocly-loader/query-named-components.ts"],"sourcesContent":["import {\n appendPointer,\n canonicalRef,\n parseRefPointer,\n ROOT_POINTER,\n} from \"../../domain/openapi/json-pointer.js\";\nimport { findMappings, findRefHolders } from \"./ref-holders.js\";\n\n/**\n * Redocly 1.x bundles an external `$ref` into a component named after the last segment of the\n * document's URL, without its extension, when the pointer gives no free name (a `$ref` to a whole\n * document, or two targets with the same pointer). The query stays in that name, and a query holds\n * API keys and signatures, so `https://host/schemas/pet?token=…` became the component\n * `pet?token=…` in the generated file and in messages (threat model T4b). This renames such a\n * component after the URL without its query: `pet`, with a `-2` suffix when that name is taken.\n */\n\ntype Components = Record<string, Record<string, unknown>>;\n\n/** The keys of each `components` group before bundling: the bundle edits the document in place. */\nexport type ComponentKeys = ReadonlyMap<string, ReadonlySet<string>>;\n\n/** Old and new name of each renamed component, per `components` group. */\ntype Renames = Map<string, Map<string, string>>;\n\nconst COMPONENTS_KEY = \"components\";\nconst REMOTE_URL_RE = /^https?:\\/\\//;\nconst PATH_SEPARATOR_RE = /[/\\\\]/;\nconst EXTENSION_RE = /\\.[^.]+$/;\n/** What Redocly puts between the base name and the rest of a component name. */\nconst NAME_SUFFIX_STARTS = [\"_\", \"-\"];\nconst FIRST_SERIAL = 2;\n\nfunction isContainer(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction componentsOf(document: unknown): Components {\n const components = isContainer(document) ? document[COMPONENTS_KEY] : undefined;\n if (!isContainer(components)) {\n return {};\n }\n const groups: Components = {};\n for (const [group, entries] of Object.entries(components)) {\n if (isContainer(entries)) {\n groups[group] = entries;\n }\n }\n return groups;\n}\n\n/** The keys of every `components` group of a document. */\nexport function componentKeysOf(document: unknown): ComponentKeys {\n const keys = new Map<string, ReadonlySet<string>>();\n for (const [group, entries] of Object.entries(componentsOf(document))) {\n keys.set(group, new Set(Object.keys(entries)));\n }\n return keys;\n}\n\n/** The base name Redocly 1.34 gives a document (`refBaseName`): the last segment, no extension. */\nfunction redoclyBaseName(ref: string): string {\n return (ref.split(PATH_SEPARATOR_RE).at(-1) ?? \"\").replace(EXTENSION_RE, \"\");\n}\n\n/**\n * The base name of the URL without its query and fragment. A URL with no last segment\n * (`https://host/?key=…`) is named after its host, so the name is never empty.\n */\nfunction queryFreeBaseName(url: URL): string {\n const withoutQuery = new URL(url.pathname, url.origin).href;\n return redoclyBaseName(withoutQuery) || url.hostname;\n}\n\n/** Each base name Redocly takes from a remote URL with a query, with the name to use instead. */\nfunction queryBaseNames(documentRefs: Iterable<string>): Map<string, string> {\n const names = new Map<string, string>();\n for (const ref of documentRefs) {\n const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : undefined;\n if (url === undefined || url.search === \"\") {\n continue;\n }\n const named = redoclyBaseName(ref);\n const clean = queryFreeBaseName(url);\n if (named !== clean) {\n names.set(named, clean);\n }\n }\n return names;\n}\n\n/** The name `key` gets, or undefined when no query base name starts it. */\nfunction queryFreeName(key: string, baseNames: ReadonlyMap<string, string>): string | undefined {\n // The longest base name first: `pet?a=1` must not match as `pet?a` when both were loaded.\n const matches = [...baseNames.keys()].filter(\n (named) =>\n key === named || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named}${start}`)),\n );\n const named = matches.sort((a, b) => b.length - a.length)[0];\n return named === undefined ? undefined : `${baseNames.get(named)}${key.slice(named.length)}`;\n}\n\nfunction freeName(name: string, taken: ReadonlySet<string>): string {\n let free = name;\n for (let serial = FIRST_SERIAL; taken.has(free); serial += 1) {\n free = `${name}-${serial}`;\n }\n return free;\n}\n\n/** Rename the bundled components of one group in place, keeping their order. */\nfunction renameGroup(\n entries: Record<string, unknown>,\n keysBefore: ReadonlySet<string>,\n baseNames: ReadonlyMap<string, string>,\n): Map<string, string> {\n const renames = new Map<string, string>();\n const taken = new Set(Object.keys(entries));\n for (const key of Object.keys(entries)) {\n const name = keysBefore.has(key) ? undefined : queryFreeName(key, baseNames);\n if (name !== undefined) {\n taken.delete(key);\n const free = freeName(name, taken);\n taken.add(free);\n renames.set(key, free);\n }\n }\n for (const [key, value] of Object.entries(entries)) {\n delete entries[key];\n entries[renames.get(key) ?? key] = value;\n }\n return renames;\n}\n\n/** Rewrites pointers and messages that name a renamed component. */\nexport interface ComponentRenaming {\n /** The pointer with a renamed component's new name; any other text as it is. */\n readonly pointer: (pointer: string) => string;\n /** The text with each renamed component's old name replaced by its new name. */\n readonly text: (text: string) => string;\n}\n\nfunction renamedPointer(pointer: string, renames: Renames): string {\n if (!pointer.startsWith(`${ROOT_POINTER}/`)) {\n return pointer;\n }\n const [components, group = \"\", name = \"\", ...rest] = parseRefPointer(canonicalRef(pointer));\n const renamed = components === COMPONENTS_KEY ? renames.get(group)?.get(name) : undefined;\n return renamed === undefined\n ? pointer\n : appendPointer(ROOT_POINTER, [COMPONENTS_KEY, group, renamed, ...rest]);\n}\n\nfunction renamedText(text: string, renames: Renames): string {\n const pairs = [...renames.values()].flatMap((group) => [...group]);\n // The longest old name first, so a name that starts another one does not split it.\n pairs.sort(([a], [b]) => b.length - a.length);\n return pairs.reduce((shown, [old, renamed]) => shown.replaceAll(old, renamed), text);\n}\n\nfunction rewriteRefs(document: unknown, renaming: ComponentRenaming): void {\n for (const { node } of findRefHolders(document)) {\n node.$ref = renaming.pointer(node.$ref);\n }\n for (const mapping of findMappings(document)) {\n for (const [name, value] of Object.entries(mapping)) {\n if (typeof value === \"string\") {\n mapping[name] = renaming.pointer(value);\n }\n }\n }\n}\n\n/**\n * Rename, in place, the components the bundle named after a URL with a query, and every local\n * `$ref` and `mapping` value that names them. `documentRefs` are the refs of the loaded documents.\n */\nexport function renameQueryNamedComponents(\n bundled: unknown,\n keysBefore: ComponentKeys,\n documentRefs: Iterable<string>,\n): ComponentRenaming {\n const baseNames = queryBaseNames(documentRefs);\n const renames: Renames = new Map();\n if (baseNames.size > 0) {\n for (const [group, entries] of Object.entries(componentsOf(bundled))) {\n const groupRenames = renameGroup(entries, keysBefore.get(group) ?? new Set(), baseNames);\n if (groupRenames.size > 0) {\n renames.set(group, groupRenames);\n }\n }\n }\n const renaming: ComponentRenaming = {\n pointer: (pointer) => renamedPointer(pointer, renames),\n text: (text) => renamedText(text, renames),\n };\n if (renames.size > 0) {\n rewriteRefs(bundled, renaming);\n }\n return renaming;\n}\n"],"names":["named"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query-named-components.mjs","sources":["../../../src/adapters/redocly-loader/query-named-components.ts"],"sourcesContent":["import {\n appendPointer,\n canonicalRef,\n parseRefPointer,\n ROOT_POINTER,\n} from \"../../domain/openapi/json-pointer.js\";\nimport { type DocumentNames, queryDocumentNames } from \"./query-document-names.js\";\nimport { findMappings, findRefHolders } from \"./ref-holders.js\";\nimport type { LoadedDocument } from \"./unsupported-keys.js\";\n\n/**\n * Redocly 1.x bundles an external `$ref` into a component named after the last segment of the\n * document's URL, without its extension, when the pointer gives no free name (a `$ref` to a whole\n * document, or two targets with the same pointer). The query stays in that name, and a query holds\n * API keys and signatures, so `https://host/schemas/pet?token=…` became the component\n * `pet?token=…` in the generated file and in messages (threat model T4b). This renames such a\n * component after the URL without its query: `pet`, with a `-2` suffix when that name is taken.\n * Each component is matched to its document by its node, since two URLs can give one name.\n */\n\ntype Components = Record<string, Record<string, unknown>>;\n\n/** The keys of each `components` group before bundling: the bundle edits the document in place. */\nexport type ComponentKeys = ReadonlyMap<string, ReadonlySet<string>>;\n\n/** Old and new name of each renamed component, per `components` group. */\ntype Renames = Map<string, Map<string, string>>;\n\nconst COMPONENTS_KEY = \"components\";\n/** What Redocly puts between the base name and the rest of a component name. */\nconst NAME_SUFFIX_STARTS = [\"_\", \"-\"];\nconst FIRST_SERIAL = 2;\n/**\n * The serial Redocly adds when a name is taken (`pet-2`). Two URLs that differ only in the query\n * took one name; without the query they may not, so the serial is chosen again (`freeName`).\n */\nconst REDOCLY_SERIAL_RE = /-\\d+$/;\n\nfunction isContainer(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction componentsOf(document: unknown): Components {\n const components = isContainer(document) ? document[COMPONENTS_KEY] : undefined;\n if (!isContainer(components)) {\n return {};\n }\n const groups: Components = {};\n for (const [group, entries] of Object.entries(components)) {\n if (isContainer(entries)) {\n groups[group] = entries;\n }\n }\n return groups;\n}\n\n/** The keys of every `components` group of a document. */\nexport function componentKeysOf(document: unknown): ComponentKeys {\n const keys = new Map<string, ReadonlySet<string>>();\n for (const [group, entries] of Object.entries(componentsOf(document))) {\n keys.set(group, new Set(Object.keys(entries)));\n }\n return keys;\n}\n\n/** The name a component of a document with a query gets; undefined for any other component. */\nfunction queryFreeName(key: string, names: DocumentNames | undefined): string | undefined {\n if (names === undefined) {\n return undefined;\n }\n const { named, clean } = names;\n const hasBaseName =\n key === named || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named}${start}`));\n return hasBaseName\n ? `${clean}${key.slice(named.length).replace(REDOCLY_SERIAL_RE, \"\")}`\n : undefined;\n}\n\nfunction freeName(name: string, taken: ReadonlySet<string>): string {\n let free = name;\n for (let serial = FIRST_SERIAL; taken.has(free); serial += 1) {\n free = `${name}-${serial}`;\n }\n return free;\n}\n\n/** Rename the bundled components of one group in place, keeping their order. */\nfunction renameGroup(\n entries: Record<string, unknown>,\n keysBefore: ReadonlySet<string>,\n documentNames: ReadonlyMap<object, DocumentNames>,\n): Map<string, string> {\n const renames = new Map<string, string>();\n const taken = new Set(Object.keys(entries));\n for (const key of Object.keys(entries)) {\n const node = entries[key];\n const names = typeof node === \"object\" && node !== null ? documentNames.get(node) : undefined;\n const name = keysBefore.has(key) ? undefined : queryFreeName(key, names);\n if (name !== undefined) {\n taken.delete(key);\n const free = freeName(name, taken);\n taken.add(free);\n renames.set(key, free);\n }\n }\n for (const [key, value] of Object.entries(entries)) {\n delete entries[key];\n entries[renames.get(key) ?? key] = value;\n }\n return renames;\n}\n\n/** Rewrites pointers and messages that name a renamed component. */\nexport interface ComponentRenaming {\n /** The pointer with a renamed component's new name; any other text as it is. */\n readonly pointer: (pointer: string) => string;\n /** The text with each renamed component's old name replaced by its new name. */\n readonly text: (text: string) => string;\n /**\n * Whether the problem is Redocly's report of two components with one name that the new names\n * tell apart: it renamed `new-api` to `new-api-2`, and the documents are now `pet` and `owner`.\n */\n readonly isResolvedCollision: (message: string) => boolean;\n}\n\n/** The warning Redocly 1.34 gives when it adds a serial to a component name. */\nconst COLLISION_RE =\n /^Two schemas are referenced with the same name but different content\\. Renamed (.+) to (.+)\\.$/;\nconst SERIAL_RE = /^-\\d+$/;\n\nfunction resolvesCollision(message: string, renames: Renames): boolean {\n const [, first, second] = COLLISION_RE.exec(message) ?? [];\n if (first === undefined || second === undefined) {\n return false;\n }\n const renamed = new Map([...renames.values()].flatMap((group) => [...group]));\n const [newFirst, newSecond] = [renamed.get(first) ?? first, renamed.get(second) ?? second];\n return !(newSecond.startsWith(newFirst) && SERIAL_RE.test(newSecond.slice(newFirst.length)));\n}\n\nfunction renamedPointer(pointer: string, renames: Renames): string {\n if (!pointer.startsWith(`${ROOT_POINTER}/`)) {\n return pointer;\n }\n const [components, group = \"\", name = \"\", ...rest] = parseRefPointer(canonicalRef(pointer));\n const renamed = components === COMPONENTS_KEY ? renames.get(group)?.get(name) : undefined;\n return renamed === undefined\n ? pointer\n : appendPointer(ROOT_POINTER, [COMPONENTS_KEY, group, renamed, ...rest]);\n}\n\nfunction renamedText(text: string, renames: Renames): string {\n const pairs = [...renames.values()].flatMap((group) => [...group]);\n // The longest old name first, so a name that starts another one does not split it.\n pairs.sort(([a], [b]) => b.length - a.length);\n return pairs.reduce((shown, [old, renamed]) => shown.replaceAll(old, renamed), text);\n}\n\nfunction rewriteRefs(document: unknown, renaming: ComponentRenaming): void {\n for (const { node } of findRefHolders(document)) {\n node.$ref = renaming.pointer(node.$ref);\n }\n for (const mapping of findMappings(document)) {\n for (const [name, value] of Object.entries(mapping)) {\n if (typeof value === \"string\") {\n mapping[name] = renaming.pointer(value);\n }\n }\n }\n}\n\n/**\n * Rename, in place, the components the bundle named after a URL with a query, and every local\n * `$ref` and `mapping` value that names them. `documents` are the documents the load read.\n */\nexport function renameQueryNamedComponents(\n bundled: unknown,\n keysBefore: ComponentKeys,\n documents: readonly LoadedDocument[],\n): ComponentRenaming {\n const documentNames = queryDocumentNames(documents);\n const renames: Renames = new Map();\n if (documentNames.size > 0) {\n for (const [group, entries] of Object.entries(componentsOf(bundled))) {\n const groupRenames = renameGroup(entries, keysBefore.get(group) ?? new Set(), documentNames);\n if (groupRenames.size > 0) {\n renames.set(group, groupRenames);\n }\n }\n }\n const renaming: ComponentRenaming = {\n pointer: (pointer) => renamedPointer(pointer, renames),\n text: (text) => renamedText(text, renames),\n isResolvedCollision: (message) => resolvesCollision(message, renames),\n };\n if (renames.size > 0) {\n rewriteRefs(bundled, renaming);\n }\n return renaming;\n}\n"],"names":[],"mappings":";;;;AA4BA,MAAM,cAAA,GAAiB,YAAA;AAEvB,MAAM,kBAAA,GAAqB,CAAC,GAAA,EAAK,GAAG,CAAA;AACpC,MAAM,YAAA,GAAe,CAAA;AAKrB,MAAM,iBAAA,GAAoB,OAAA;AAE1B,SAAS,YAAY,KAAA,EAAkD;AACrE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AAEA,SAAS,aAAa,QAAA,EAA+B;AACnD,EAAA,MAAM,aAAa,WAAA,CAAY,QAAQ,CAAA,GAAI,QAAA,CAAS,cAAc,CAAA,GAAI,MAAA;AACtE,EAAA,IAAI,CAAC,WAAA,CAAY,UAAU,CAAA,EAAG;AAC5B,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,MAAM,SAAqB,EAAC;AAC5B,EAAA,KAAA,MAAW,CAAC,KAAA,EAAO,OAAO,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AACzD,IAAA,IAAI,WAAA,CAAY,OAAO,CAAA,EAAG;AACxB,MAAA,MAAA,CAAO,KAAK,CAAA,GAAI,OAAA;AAAA,IAClB;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,gBAAgB,QAAA,EAAkC;AAChE,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAiC;AAClD,EAAA,KAAA,MAAW,CAAC,OAAO,OAAO,CAAA,IAAK,OAAO,OAAA,CAAQ,YAAA,CAAa,QAAQ,CAAC,CAAA,EAAG;AACrE,IAAA,IAAA,CAAK,GAAA,CAAI,OAAO,IAAI,GAAA,CAAI,OAAO,IAAA,CAAK,OAAO,CAAC,CAAC,CAAA;AAAA,EAC/C;AACA,EAAA,OAAO,IAAA;AACT;AAGA,SAAS,aAAA,CAAc,KAAa,KAAA,EAAsD;AACxF,EAAA,IAAI,UAAU,MAAA,EAAW;AACvB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAI,KAAA;AACzB,EAAA,MAAM,WAAA,GACJ,GAAA,KAAQ,KAAA,IAAS,kBAAA,CAAmB,KAAK,CAAC,KAAA,KAAU,GAAA,CAAI,UAAA,CAAW,CAAA,EAAG,KAAK,CAAA,EAAG,KAAK,EAAE,CAAC,CAAA;AACxF,EAAA,OAAO,WAAA,GACH,CAAA,EAAG,KAAK,CAAA,EAAG,GAAA,CAAI,KAAA,CAAM,KAAA,CAAM,MAAM,CAAA,CAAE,OAAA,CAAQ,iBAAA,EAAmB,EAAE,CAAC,CAAA,CAAA,GACjE,MAAA;AACN;AAEA,SAAS,QAAA,CAAS,MAAc,KAAA,EAAoC;AAClE,EAAA,IAAI,IAAA,GAAO,IAAA;AACX,EAAA,KAAA,IAAS,SAAS,YAAA,EAAc,KAAA,CAAM,IAAI,IAAI,CAAA,EAAG,UAAU,CAAA,EAAG;AAC5D,IAAA,IAAA,GAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,EAC1B;AACA,EAAA,OAAO,IAAA;AACT;AAGA,SAAS,WAAA,CACP,OAAA,EACA,UAAA,EACA,aAAA,EACqB;AACrB,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAoB;AACxC,EAAA,MAAM,QAAQ,IAAI,GAAA,CAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAC,CAAA;AAC1C,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,EAAG;AACtC,IAAA,MAAM,IAAA,GAAO,QAAQ,GAAG,CAAA;AACxB,IAAA,MAAM,KAAA,GAAQ,OAAO,IAAA,KAAS,QAAA,IAAY,SAAS,IAAA,GAAO,aAAA,CAAc,GAAA,CAAI,IAAI,CAAA,GAAI,MAAA;AACpF,IAAA,MAAM,IAAA,GAAO,WAAW,GAAA,CAAI,GAAG,IAAI,MAAA,GAAY,aAAA,CAAc,KAAK,KAAK,CAAA;AACvE,IAAA,IAAI,SAAS,MAAA,EAAW;AACtB,MAAA,KAAA,CAAM,OAAO,GAAG,CAAA;AAChB,MAAA,MAAM,IAAA,GAAO,QAAA,CAAS,IAAA,EAAM,KAAK,CAAA;AACjC,MAAA,KAAA,CAAM,IAAI,IAAI,CAAA;AACd,MAAA,OAAA,CAAQ,GAAA,CAAI,KAAK,IAAI,CAAA;AAAA,IACvB;AAAA,EACF;AACA,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AAClD,IAAA,OAAO,QAAQ,GAAG,CAAA;AAClB,IAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,IAAK,GAAG,CAAA,GAAI,KAAA;AAAA,EACrC;AACA,EAAA,OAAO,OAAA;AACT;AAgBA,MAAM,YAAA,GACJ,gGAAA;AACF,MAAM,SAAA,GAAY,QAAA;AAElB,SAAS,iBAAA,CAAkB,SAAiB,OAAA,EAA2B;AACrE,EAAA,MAAM,GAAG,KAAA,EAAO,MAAM,IAAI,YAAA,CAAa,IAAA,CAAK,OAAO,CAAA,IAAK,EAAC;AACzD,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,MAAA,KAAW,MAAA,EAAW;AAC/C,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,MAAM,UAAU,IAAI,GAAA,CAAI,CAAC,GAAG,QAAQ,MAAA,EAAQ,CAAA,CAAE,OAAA,CAAQ,CAAC,KAAA,KAAU,CAAC,GAAG,KAAK,CAAC,CAAC,CAAA;AAC5E,EAAA,MAAM,CAAC,QAAA,EAAU,SAAS,CAAA,GAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,KAAK,CAAA,IAAK,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,MAAM,KAAK,MAAM,CAAA;AACzF,EAAA,OAAO,EAAE,SAAA,CAAU,UAAA,CAAW,QAAQ,CAAA,IAAK,SAAA,CAAU,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,QAAA,CAAS,MAAM,CAAC,CAAA,CAAA;AAC5F;AAEA,SAAS,cAAA,CAAe,SAAiB,OAAA,EAA0B;AACjE,EAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,CAAW,CAAA,EAAG,YAAY,GAAG,CAAA,EAAG;AAC3C,IAAA,OAAO,OAAA;AAAA,EACT;AACA,EAAA,MAAM,CAAC,UAAA,EAAY,KAAA,GAAQ,EAAA,EAAI,IAAA,GAAO,EAAA,EAAI,GAAG,IAAI,CAAA,GAAI,eAAA,CAAgB,YAAA,CAAa,OAAO,CAAC,CAAA;AAC1F,EAAA,MAAM,OAAA,GAAU,eAAe,cAAA,GAAiB,OAAA,CAAQ,IAAI,KAAK,CAAA,EAAG,GAAA,CAAI,IAAI,CAAA,GAAI,MAAA;AAChF,EAAA,OAAO,OAAA,KAAY,MAAA,GACf,OAAA,GACA,aAAA,CAAc,YAAA,EAAc,CAAC,cAAA,EAAgB,KAAA,EAAO,OAAA,EAAS,GAAG,IAAI,CAAC,CAAA;AAC3E;AAEA,SAAS,WAAA,CAAY,MAAc,OAAA,EAA0B;AAC3D,EAAA,MAAM,KAAA,GAAQ,CAAC,GAAG,OAAA,CAAQ,MAAA,EAAQ,CAAA,CAAE,OAAA,CAAQ,CAAC,KAAA,KAAU,CAAC,GAAG,KAAK,CAAC,CAAA;AAEjE,EAAA,KAAA,CAAM,IAAA,CAAK,CAAC,CAAC,CAAC,CAAA,EAAG,CAAC,CAAC,CAAA,KAAM,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,MAAM,CAAA;AAC5C,EAAA,OAAO,KAAA,CAAM,MAAA,CAAO,CAAC,KAAA,EAAO,CAAC,GAAA,EAAK,OAAO,CAAA,KAAM,KAAA,CAAM,UAAA,CAAW,GAAA,EAAK,OAAO,GAAG,IAAI,CAAA;AACrF;AAEA,SAAS,WAAA,CAAY,UAAmB,QAAA,EAAmC;AACzE,EAAA,KAAA,MAAW,EAAE,IAAA,EAAK,IAAK,cAAA,CAAe,QAAQ,CAAA,EAAG;AAC/C,IAAA,IAAA,CAAK,IAAA,GAAO,QAAA,CAAS,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AAAA,EACxC;AACA,EAAA,KAAA,MAAW,OAAA,IAAW,YAAA,CAAa,QAAQ,CAAA,EAAG;AAC5C,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AACnD,MAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,QAAA,OAAA,CAAQ,IAAI,CAAA,GAAI,QAAA,CAAS,OAAA,CAAQ,KAAK,CAAA;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;AAMO,SAAS,0BAAA,CACd,OAAA,EACA,UAAA,EACA,SAAA,EACmB;AACnB,EAAA,MAAM,aAAA,GAAgB,mBAAmB,SAAS,CAAA;AAClD,EAAA,MAAM,OAAA,uBAAuB,GAAA,EAAI;AACjC,EAAA,IAAI,aAAA,CAAc,OAAO,CAAA,EAAG;AAC1B,IAAA,KAAA,MAAW,CAAC,OAAO,OAAO,CAAA,IAAK,OAAO,OAAA,CAAQ,YAAA,CAAa,OAAO,CAAC,CAAA,EAAG;AACpE,MAAA,MAAM,YAAA,GAAe,WAAA,CAAY,OAAA,EAAS,UAAA,CAAW,GAAA,CAAI,KAAK,CAAA,oBAAK,IAAI,GAAA,EAAI,EAAG,aAAa,CAAA;AAC3F,MAAA,IAAI,YAAA,CAAa,OAAO,CAAA,EAAG;AACzB,QAAA,OAAA,CAAQ,GAAA,CAAI,OAAO,YAAY,CAAA;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AACA,EAAA,MAAM,QAAA,GAA8B;AAAA,IAClC,OAAA,EAAS,CAAC,OAAA,KAAY,cAAA,CAAe,SAAS,OAAO,CAAA;AAAA,IACrD,IAAA,EAAM,CAAC,IAAA,KAAS,WAAA,CAAY,MAAM,OAAO,CAAA;AAAA,IACzC,mBAAA,EAAqB,CAAC,OAAA,KAAY,iBAAA,CAAkB,SAAS,OAAO;AAAA,GACtE;AACA,EAAA,IAAI,OAAA,CAAQ,OAAO,CAAA,EAAG;AACpB,IAAA,WAAA,CAAY,SAAS,QAAQ,CAAA;AAAA,EAC/B;AACA,EAAA,OAAO,QAAA;AACT;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redocly-config-source.cjs","sources":["../../../src/adapters/redocly-loader/redocly-config-source.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport {
|
|
1
|
+
{"version":3,"file":"redocly-config-source.cjs","sources":["../../../src/adapters/redocly-loader/redocly-config-source.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n createConfig,\n findConfig,\n getMergedConfig,\n IGNORE_FILE,\n loadConfig,\n} from \"@redocly/openapi-core\";\nimport type {\n RedoclyConfig,\n RedoclyConfigSource,\n} from \"../../application/ports/redocly-config-source.js\";\nimport { ERROR_CODES, OpenAPITypesafeError } from \"../../domain/errors.js\";\n\n/**\n * The first line of a failure, with the error's name: a YAML error goes on with a snippet of the\n * file, and the name tells a YAML error from a file system one.\n */\nfunction reasonOf(error: unknown): string {\n return String(error).replace(/\\n[\\s\\S]*$/, \"\");\n}\n\n/**\n * The config, or `CLI_CONFIG_INVALID`. Redocly reads the config file and the lint-ignore file\n * next to it (or in the working directory); either can be malformed, and its error does not\n * always name the file, so the message names both.\n */\nasync function loadedConfig(\n load: () => Promise<RedoclyConfig>,\n configPath: string | undefined,\n): Promise<RedoclyConfig> {\n try {\n return await load();\n } catch (error) {\n const config =\n configPath === undefined ? \"the default Redocly config\" : `the Redocly config ${configPath}`;\n const where = configPath === undefined ? \"in the working directory\" : \"next to it\";\n throw new OpenAPITypesafeError({\n code: ERROR_CODES.CLI_CONFIG_INVALID,\n message: `Cannot load ${config} or the ${IGNORE_FILE} ${where}: ${reasonOf(error)}`,\n details: { configPath },\n cause: error,\n });\n }\n}\n\n/** The Redocly config port on `@redocly/openapi-core`, resolving paths from `cwd`. */\nclass RedoclyConfigFiles implements RedoclyConfigSource {\n readonly #cwd: string;\n\n constructor(cwd: string) {\n this.#cwd = cwd;\n }\n\n find(explicitPath: string | undefined): string | undefined {\n if (!explicitPath) {\n return findConfig(this.#cwd);\n }\n const resolved = path.resolve(this.#cwd, explicitPath);\n if (!fs.existsSync(resolved)) {\n return undefined;\n }\n return fs.statSync(resolved).isDirectory() ? findConfig(resolved) : resolved;\n }\n\n load(configPath: string): Promise<RedoclyConfig> {\n return loadedConfig(() => loadConfig({ configPath }), configPath);\n }\n\n createDefault(): Promise<RedoclyConfig> {\n const create = () =>\n createConfig(\n { rules: { \"operation-operationId-unique\": { severity: \"warn\" } } },\n { extends: [\"minimal\"] },\n );\n return loadedConfig(create, undefined);\n }\n\n forApi(config: RedoclyConfig, apiName: string): RedoclyConfig {\n return getMergedConfig(config, apiName);\n }\n}\n\n/** Find and load `redocly.yaml` relative to the directory `cwd`. */\nexport function createRedoclyConfigSource(cwd: string): RedoclyConfigSource {\n return new RedoclyConfigFiles(cwd);\n}\n"],"names":["OpenAPITypesafeError","ERROR_CODES","IGNORE_FILE","findConfig","path","fs","loadConfig","createConfig","getMergedConfig"],"mappings":";;;;;;;;;;;;AAmBA,SAAS,SAAS,KAAA,EAAwB;AACxC,EAAA,OAAO,MAAA,CAAO,KAAK,CAAA,CAAE,OAAA,CAAQ,cAAc,EAAE,CAAA;AAC/C;AAOA,eAAe,YAAA,CACb,MACA,UAAA,EACwB;AACxB,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,IAAA,EAAK;AAAA,EACpB,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,MAAA,GACJ,UAAA,KAAe,MAAA,GAAY,4BAAA,GAA+B,sBAAsB,UAAU,CAAA,CAAA;AAC5F,IAAA,MAAM,KAAA,GAAQ,UAAA,KAAe,MAAA,GAAY,0BAAA,GAA6B,YAAA;AACtE,IAAA,MAAM,IAAIA,2BAAA,CAAqB;AAAA,MAC7B,MAAMC,kBAAA,CAAY,kBAAA;AAAA,MAClB,OAAA,EAAS,CAAA,YAAA,EAAe,MAAM,CAAA,QAAA,EAAWC,uBAAW,IAAI,KAAK,CAAA,EAAA,EAAK,QAAA,CAAS,KAAK,CAAC,CAAA,CAAA;AAAA,MACjF,OAAA,EAAS,EAAE,UAAA,EAAW;AAAA,MACtB,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACF;AAGA,MAAM,kBAAA,CAAkD;AAAA,EAC7C,IAAA;AAAA,EAET,YAAY,GAAA,EAAa;AACvB,IAAA,IAAA,CAAK,IAAA,GAAO,GAAA;AAAA,EACd;AAAA,EAEA,KAAK,YAAA,EAAsD;AACzD,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,OAAOC,sBAAA,CAAW,KAAK,IAAI,CAAA;AAAA,IAC7B;AACA,IAAA,MAAM,QAAA,GAAWC,aAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,MAAM,YAAY,CAAA;AACrD,IAAA,IAAI,CAACC,WAAA,CAAG,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC5B,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAOA,WAAA,CAAG,SAAS,QAAQ,CAAA,CAAE,aAAY,GAAIF,sBAAA,CAAW,QAAQ,CAAA,GAAI,QAAA;AAAA,EACtE;AAAA,EAEA,KAAK,UAAA,EAA4C;AAC/C,IAAA,OAAO,aAAa,MAAMG,sBAAA,CAAW,EAAE,UAAA,EAAY,GAAG,UAAU,CAAA;AAAA,EAClE;AAAA,EAEA,aAAA,GAAwC;AACtC,IAAA,MAAM,SAAS,MACbC,wBAAA;AAAA,MACE,EAAE,OAAO,EAAE,8BAAA,EAAgC,EAAE,QAAA,EAAU,MAAA,IAAS,EAAE;AAAA,MAClE,EAAE,OAAA,EAAS,CAAC,SAAS,CAAA;AAAE,KACzB;AACF,IAAA,OAAO,YAAA,CAAa,QAAQ,MAAS,CAAA;AAAA,EACvC;AAAA,EAEA,MAAA,CAAO,QAAuB,OAAA,EAAgC;AAC5D,IAAA,OAAOC,2BAAA,CAAgB,QAAQ,OAAO,CAAA;AAAA,EACxC;AACF;AAGO,SAAS,0BAA0B,GAAA,EAAkC;AAC1E,EAAA,OAAO,IAAI,mBAAmB,GAAG,CAAA;AACnC;;"}
|