toolcraft-openapi 0.0.27 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/bearer-token-auth.js +1 -1
- package/dist/bin/generate.d.ts +9 -1
- package/dist/bin/generate.js +65 -9
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.js +6 -0
- package/dist/generate.d.ts +20 -3
- package/dist/generate.js +604 -144
- package/dist/http.d.ts +17 -1
- package/dist/http.js +113 -7
- package/dist/index.d.ts +6 -1
- package/dist/index.js +3 -0
- package/dist/inspect-source.d.ts +10 -0
- package/dist/inspect-source.js +14 -0
- package/dist/inspect.d.ts +22 -0
- package/dist/inspect.js +171 -0
- package/dist/interpreter.js +6 -2
- package/dist/mock/fetch.js +28 -12
- package/dist/mock/forgeyard.d.ts +4 -0
- package/dist/mock/forgeyard.js +452 -0
- package/dist/mock.d.ts +1 -0
- package/dist/mock.js +1 -0
- package/dist/naming.d.ts +4 -1
- package/dist/naming.js +93 -7
- package/dist/network-error.js +29 -12
- package/dist/normalize-swagger.d.ts +2 -0
- package/dist/normalize-swagger.js +201 -0
- package/dist/render-inspection.d.ts +2 -0
- package/dist/render-inspection.js +130 -0
- package/dist/request-shape.d.ts +4 -0
- package/dist/request-shape.js +1 -0
- package/dist/runtime.js +7 -1
- package/dist/spec-source.js +7 -4
- package/node_modules/auth-store/dist/create-secret-store.js +6 -1
- package/node_modules/auth-store/dist/encrypted-file-store.d.ts +1 -1
- package/node_modules/auth-store/dist/encrypted-file-store.js +32 -28
- package/node_modules/auth-store/dist/error-codes.d.ts +1 -0
- package/node_modules/auth-store/dist/error-codes.js +5 -0
- package/node_modules/auth-store/dist/keychain-store.js +23 -8
- package/node_modules/toolcraft-design/README.md +148 -0
- package/node_modules/toolcraft-design/dist/components/catalog.d.ts +26 -0
- package/node_modules/toolcraft-design/dist/components/catalog.js +94 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/index.d.ts +2 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/index.js +1 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/components/footer.js +1 -2
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/components/output-pane.js +16 -19
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/components/stats-pane.js +1 -15
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/demo.js +2 -2
- package/node_modules/toolcraft-design/dist/explorer/theme.js +29 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/index.d.ts +7 -1
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/index.js +4 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/internal/theme-detect.d.ts +1 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/internal/theme-detect.js +28 -6
- package/node_modules/toolcraft-design/dist/internal/theme-state.d.ts +12 -0
- package/node_modules/toolcraft-design/dist/internal/theme-state.js +35 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/theme.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/prompts/theme.js +15 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/parser/frontmatter.js +50 -0
- package/node_modules/toolcraft-design/dist/tokens/brand.d.ts +5 -0
- package/node_modules/toolcraft-design/dist/tokens/brand.js +5 -0
- package/node_modules/toolcraft-design/dist/tokens/colors.d.ts +37 -0
- package/node_modules/toolcraft-design/dist/tokens/colors.js +85 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/tokens/index.d.ts +2 -0
- package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/tokens/index.js +1 -0
- package/node_modules/toolcraft-design/package.json +42 -0
- package/package.json +8 -6
- package/node_modules/@poe-code/design-system/dist/explorer/theme.js +0 -97
- package/node_modules/@poe-code/design-system/dist/prompts/theme.js +0 -12
- package/node_modules/@poe-code/design-system/dist/tokens/colors.d.ts +0 -35
- package/node_modules/@poe-code/design-system/dist/tokens/colors.js +0 -34
- package/node_modules/@poe-code/design-system/package.json +0 -29
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/acp/components.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/acp/components.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/acp/index.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/acp/index.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/acp/writer.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/acp/writer.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/browser.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/browser.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/color.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/color.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/command-errors.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/command-errors.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/detail-card.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/detail-card.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/help-formatter-plain.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/help-formatter-plain.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/help-formatter.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/help-formatter.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/logger.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/logger.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/symbols.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/symbols.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/table.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/table.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/template.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/template.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/text.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/components/text.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/ansi.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/ansi.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/buffer.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/buffer.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/components/border.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/components/border.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/components/footer.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/components/output-pane.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/components/stats-pane.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/dashboard.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/dashboard.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/demo.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/demo.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/index.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/index.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/keymap.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/keymap.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/layout.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/layout.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/should-use-dashboard.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/should-use-dashboard.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/snapshot.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/snapshot.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/store.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/store.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/terminal-width.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/terminal-width.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/terminal.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/terminal.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/types.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/dashboard/types.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/actions.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/actions.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/demo.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/events.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/events.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/filter.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/filter.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/index.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/index.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/jobs.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/jobs.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/keymap.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/keymap.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/layout.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/layout.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/reducer.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/reducer.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/detail.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/detail.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/footer.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/footer.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/header.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/header.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/index.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/index.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/list.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/list.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/modal.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/modal.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/test-fixtures.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/test-fixtures.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/text.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/render/text.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/runtime.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/runtime.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/runtime.test-helpers.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/runtime.test-helpers.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/state.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/state.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/explorer/theme.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/internal/color-support.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/internal/color-support.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/internal/output-format.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/internal/output-format.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/internal/strip-ansi.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/internal/strip-ansi.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/index.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/index.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/cancel.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/cancel.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/intro.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/intro.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/log.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/log.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/note.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/note.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/outro.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/outro.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/spinner.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/prompts/primitives/spinner.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/static/index.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/static/index.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/static/menu.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/static/menu.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/static/spinner.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/static/spinner.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/ast.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/ast.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/demo-content.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/demo-content.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/index.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/index.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/parser/block.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/parser/block.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/parser/frontmatter.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/parser/inline.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/parser/inline.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/parser.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/parser.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/renderer.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/renderer.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/testing/theme-render-fixture.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/terminal-markdown/testing/theme-render-fixture.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/tokens/spacing.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/tokens/spacing.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/tokens/typography.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/tokens/typography.js +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/tokens/widths.d.ts +0 -0
- /package/node_modules/{@poe-code/design-system → toolcraft-design}/dist/tokens/widths.js +0 -0
package/dist/network-error.js
CHANGED
|
@@ -5,7 +5,7 @@ export function classifyNetworkError(error, url) {
|
|
|
5
5
|
const urlParts = new URL(url);
|
|
6
6
|
const host = getHost(networkError, urlParts);
|
|
7
7
|
const redactedUrl = redactSensitiveQueryValues(url);
|
|
8
|
-
switch (networkError
|
|
8
|
+
switch (readStringProperty(networkError, "code")) {
|
|
9
9
|
case "ECONNREFUSED":
|
|
10
10
|
return new UserError(`Connection refused: ${host}:${getPort(networkError, urlParts)}. Is the server running?`, { cause: error });
|
|
11
11
|
case "ETIMEDOUT":
|
|
@@ -36,10 +36,10 @@ export function classifyNetworkError(error, url) {
|
|
|
36
36
|
function findNetworkError(error) {
|
|
37
37
|
let current = error;
|
|
38
38
|
while (isErrorLikeObject(current)) {
|
|
39
|
-
if (
|
|
39
|
+
if (readStringProperty(current, "code") !== undefined) {
|
|
40
40
|
return current;
|
|
41
41
|
}
|
|
42
|
-
current = current
|
|
42
|
+
current = readOwnProperty(current, "cause");
|
|
43
43
|
}
|
|
44
44
|
return null;
|
|
45
45
|
}
|
|
@@ -52,19 +52,20 @@ function findAbortError(error) {
|
|
|
52
52
|
if (isAbortError(current)) {
|
|
53
53
|
return current;
|
|
54
54
|
}
|
|
55
|
-
current = current
|
|
55
|
+
current = readOwnProperty(current, "cause");
|
|
56
56
|
}
|
|
57
57
|
return null;
|
|
58
58
|
}
|
|
59
59
|
function hasCause(error) {
|
|
60
|
-
return "cause"
|
|
60
|
+
return readOwnProperty(error, "cause") !== undefined;
|
|
61
61
|
}
|
|
62
62
|
function getHost(error, url) {
|
|
63
|
-
return
|
|
63
|
+
return readStringProperty(error, "address") ?? url.hostname;
|
|
64
64
|
}
|
|
65
65
|
function getPort(error, url) {
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
const port = readStringOrNumberProperty(error, "port");
|
|
67
|
+
if (port !== undefined) {
|
|
68
|
+
return String(port);
|
|
68
69
|
}
|
|
69
70
|
if (url.port) {
|
|
70
71
|
return url.port;
|
|
@@ -72,14 +73,30 @@ function getPort(error, url) {
|
|
|
72
73
|
return url.protocol === "https:" ? "443" : "80";
|
|
73
74
|
}
|
|
74
75
|
function getTimeoutMs(error) {
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
const ms = readStringOrNumberProperty(error, "ms");
|
|
77
|
+
if (ms !== undefined) {
|
|
78
|
+
return String(ms);
|
|
77
79
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
const timeout = readStringOrNumberProperty(error, "timeout");
|
|
81
|
+
if (timeout !== undefined) {
|
|
82
|
+
return String(timeout);
|
|
80
83
|
}
|
|
81
84
|
return "unknown";
|
|
82
85
|
}
|
|
83
86
|
function isErrorLikeObject(value) {
|
|
84
87
|
return typeof value === "object" && value !== null;
|
|
85
88
|
}
|
|
89
|
+
function readOwnProperty(value, name) {
|
|
90
|
+
if (value === null || value === undefined || !Object.prototype.hasOwnProperty.call(value, name)) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
return value[name];
|
|
94
|
+
}
|
|
95
|
+
function readStringProperty(value, name) {
|
|
96
|
+
const property = readOwnProperty(value, name);
|
|
97
|
+
return typeof property === "string" ? property : undefined;
|
|
98
|
+
}
|
|
99
|
+
function readStringOrNumberProperty(value, name) {
|
|
100
|
+
const property = readOwnProperty(value, name);
|
|
101
|
+
return typeof property === "string" || typeof property === "number" ? property : undefined;
|
|
102
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
const HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
|
|
2
|
+
const SCHEMA_PARAMETER_KEYS = [
|
|
3
|
+
"type",
|
|
4
|
+
"format",
|
|
5
|
+
"items",
|
|
6
|
+
"default",
|
|
7
|
+
"enum",
|
|
8
|
+
"minimum",
|
|
9
|
+
"maximum",
|
|
10
|
+
"minLength",
|
|
11
|
+
"maxLength",
|
|
12
|
+
"pattern",
|
|
13
|
+
"minItems",
|
|
14
|
+
"maxItems"
|
|
15
|
+
];
|
|
16
|
+
export function normalizeOpenApiDocument(document) {
|
|
17
|
+
const source = document;
|
|
18
|
+
if (source.swagger !== "2.0") {
|
|
19
|
+
return document;
|
|
20
|
+
}
|
|
21
|
+
const consumes = readStringArray(source.consumes);
|
|
22
|
+
const produces = readStringArray(source.produces);
|
|
23
|
+
const paths = Object.fromEntries(Object.entries(source.paths ?? {}).map(([path, pathItem]) => [
|
|
24
|
+
path,
|
|
25
|
+
pathItem === undefined
|
|
26
|
+
? undefined
|
|
27
|
+
: normalizePathItem(pathItem, consumes, produces, isRecord(source.parameters) ? source.parameters : {})
|
|
28
|
+
]));
|
|
29
|
+
const components = {
|
|
30
|
+
...(source.components ?? {}),
|
|
31
|
+
...(!isRecord(source.definitions) ? {} : { schemas: rewriteReferences(source.definitions) }),
|
|
32
|
+
...(!isRecord(source.parameters) ? {} : { parameters: rewriteReferences(source.parameters) }),
|
|
33
|
+
...(!isRecord(source.responses) ? {} : { responses: rewriteReferences(source.responses) }),
|
|
34
|
+
...(!isRecord(source.securityDefinitions)
|
|
35
|
+
? {}
|
|
36
|
+
: { securitySchemes: rewriteReferences(source.securityDefinitions) })
|
|
37
|
+
};
|
|
38
|
+
return rewriteReferences({
|
|
39
|
+
...source,
|
|
40
|
+
openapi: "3.0.3",
|
|
41
|
+
paths,
|
|
42
|
+
components
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function normalizePathItem(pathItem, documentConsumes, documentProduces, reusableParameters) {
|
|
46
|
+
const normalized = {
|
|
47
|
+
...pathItem,
|
|
48
|
+
...(pathItem.parameters === undefined
|
|
49
|
+
? {}
|
|
50
|
+
: { parameters: pathItem.parameters.map((parameter) => normalizeParameter(resolveReusableParameter(parameter, reusableParameters))).filter(isOpenApiParameter) })
|
|
51
|
+
};
|
|
52
|
+
for (const method of HTTP_METHODS) {
|
|
53
|
+
const operation = pathItem[method];
|
|
54
|
+
if (!isRecord(operation) || isReferenceObject(operation)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
normalized[method] = normalizeOperation(operation, documentConsumes, documentProduces, reusableParameters);
|
|
58
|
+
}
|
|
59
|
+
return normalized;
|
|
60
|
+
}
|
|
61
|
+
function normalizeOperation(operation, documentConsumes, documentProduces, reusableParameters) {
|
|
62
|
+
const parameters = (operation.parameters ?? []).map((parameter) => normalizeParameter(resolveReusableParameter(parameter, reusableParameters)));
|
|
63
|
+
const body = parameters.find(isSwaggerBodyParameter);
|
|
64
|
+
const formParameters = parameters.filter(isSwaggerFormParameter);
|
|
65
|
+
const nonBodyParameters = parameters.filter((parameter) => isOpenApiParameter(parameter) && !isSwaggerFormParameter(parameter));
|
|
66
|
+
const consumes = readStringArray(operation.consumes).length > 0
|
|
67
|
+
? readStringArray(operation.consumes)
|
|
68
|
+
: documentConsumes;
|
|
69
|
+
const produces = readStringArray(operation.produces).length > 0
|
|
70
|
+
? readStringArray(operation.produces)
|
|
71
|
+
: documentProduces;
|
|
72
|
+
return {
|
|
73
|
+
...operation,
|
|
74
|
+
parameters: nonBodyParameters,
|
|
75
|
+
...(body !== undefined
|
|
76
|
+
? { requestBody: normalizeBodyParameter(body, consumes) }
|
|
77
|
+
: formParameters.length === 0
|
|
78
|
+
? {}
|
|
79
|
+
: { requestBody: normalizeFormParameters(formParameters, consumes) }),
|
|
80
|
+
responses: Object.fromEntries(Object.entries(operation.responses ?? {}).map(([status, response]) => [
|
|
81
|
+
status,
|
|
82
|
+
normalizeResponse(response, produces)
|
|
83
|
+
]))
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function resolveReusableParameter(parameter, reusableParameters) {
|
|
87
|
+
if (!isReferenceObject(parameter) || !parameter.$ref.startsWith("#/parameters/")) {
|
|
88
|
+
return parameter;
|
|
89
|
+
}
|
|
90
|
+
return reusableParameters[parameter.$ref.slice("#/parameters/".length)];
|
|
91
|
+
}
|
|
92
|
+
function normalizeParameter(parameter) {
|
|
93
|
+
if (isReferenceObject(parameter)) {
|
|
94
|
+
return { $ref: rewriteReference(parameter.$ref) };
|
|
95
|
+
}
|
|
96
|
+
if (parameter.in === "body") {
|
|
97
|
+
return {
|
|
98
|
+
in: "body",
|
|
99
|
+
required: parameter.required === true,
|
|
100
|
+
description: typeof parameter.description === "string" ? parameter.description : undefined,
|
|
101
|
+
schema: rewriteReferences(parameter.schema ?? {})
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const parameterRecord = parameter;
|
|
105
|
+
const schema = isRecord(parameterRecord.schema)
|
|
106
|
+
? parameterRecord.schema
|
|
107
|
+
: Object.fromEntries(SCHEMA_PARAMETER_KEYS.flatMap((key) => parameterRecord[key] === undefined ? [] : [[key, parameterRecord[key]]]));
|
|
108
|
+
const normalized = {
|
|
109
|
+
...parameter,
|
|
110
|
+
schema: rewriteReferences(parameter.in === "formData" && parameterRecord.type === "file"
|
|
111
|
+
? { type: "string", format: "binary" }
|
|
112
|
+
: schema)
|
|
113
|
+
};
|
|
114
|
+
return parameter.in === "formData"
|
|
115
|
+
? normalized
|
|
116
|
+
: normalized;
|
|
117
|
+
}
|
|
118
|
+
function normalizeBodyParameter(parameter, consumes) {
|
|
119
|
+
const mediaTypes = consumes.length === 0 ? ["application/json"] : consumes;
|
|
120
|
+
return {
|
|
121
|
+
...(parameter.description === undefined ? {} : { description: parameter.description }),
|
|
122
|
+
...(parameter.required ? { required: true } : {}),
|
|
123
|
+
content: Object.fromEntries(mediaTypes.map((mediaType) => [mediaType, { schema: parameter.schema }]))
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function normalizeResponse(response, produces) {
|
|
127
|
+
if (isReferenceObject(response)) {
|
|
128
|
+
return { $ref: rewriteReference(response.$ref) };
|
|
129
|
+
}
|
|
130
|
+
const responseRecord = response;
|
|
131
|
+
if (responseRecord.schema === undefined) {
|
|
132
|
+
return response;
|
|
133
|
+
}
|
|
134
|
+
const mediaTypes = produces.length === 0 ? ["application/json"] : produces;
|
|
135
|
+
return {
|
|
136
|
+
...response,
|
|
137
|
+
content: Object.fromEntries(mediaTypes.map((mediaType) => [
|
|
138
|
+
mediaType,
|
|
139
|
+
{ schema: rewriteReferences(responseRecord.schema) }
|
|
140
|
+
]))
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function isSwaggerBodyParameter(parameter) {
|
|
144
|
+
return !isReferenceObject(parameter) && parameter.in === "body";
|
|
145
|
+
}
|
|
146
|
+
function isSwaggerFormParameter(parameter) {
|
|
147
|
+
return !isReferenceObject(parameter) && parameter.in === "formData";
|
|
148
|
+
}
|
|
149
|
+
function normalizeFormParameters(parameters, consumes) {
|
|
150
|
+
const required = parameters.filter((parameter) => parameter.required === true).map((parameter) => parameter.name);
|
|
151
|
+
const mediaType = consumes.find((value) => value.toLowerCase() === "multipart/form-data") ??
|
|
152
|
+
"application/x-www-form-urlencoded";
|
|
153
|
+
return {
|
|
154
|
+
...(required.length === 0 ? {} : { required: true }),
|
|
155
|
+
content: {
|
|
156
|
+
[mediaType]: {
|
|
157
|
+
schema: {
|
|
158
|
+
type: "object",
|
|
159
|
+
...(required.length === 0 ? {} : { required }),
|
|
160
|
+
properties: Object.fromEntries(parameters.map((parameter) => [
|
|
161
|
+
parameter.name,
|
|
162
|
+
{
|
|
163
|
+
...parameter.schema,
|
|
164
|
+
...(parameter.description === undefined ? {} : { description: parameter.description })
|
|
165
|
+
}
|
|
166
|
+
]))
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function isOpenApiParameter(parameter) {
|
|
173
|
+
return isReferenceObject(parameter) || (parameter.in !== "body" && parameter.in !== "formData");
|
|
174
|
+
}
|
|
175
|
+
function isReferenceObject(value) {
|
|
176
|
+
return isRecord(value) && typeof value.$ref === "string";
|
|
177
|
+
}
|
|
178
|
+
function rewriteReferences(value) {
|
|
179
|
+
if (Array.isArray(value)) {
|
|
180
|
+
return value.map((entry) => rewriteReferences(entry));
|
|
181
|
+
}
|
|
182
|
+
if (!isRecord(value)) {
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
186
|
+
key,
|
|
187
|
+
key === "$ref" && typeof entry === "string" ? rewriteReference(entry) : rewriteReferences(entry)
|
|
188
|
+
]));
|
|
189
|
+
}
|
|
190
|
+
function rewriteReference(reference) {
|
|
191
|
+
return reference
|
|
192
|
+
.replace("#/definitions/", "#/components/schemas/")
|
|
193
|
+
.replace("#/parameters/", "#/components/parameters/")
|
|
194
|
+
.replace("#/responses/", "#/components/responses/");
|
|
195
|
+
}
|
|
196
|
+
function readStringArray(value) {
|
|
197
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
198
|
+
}
|
|
199
|
+
function isRecord(value) {
|
|
200
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
201
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { getTheme, renderCatalog, resolveOutputFormat } from "toolcraft-design";
|
|
2
|
+
const LARGE_API_OPERATION_THRESHOLD = 50;
|
|
3
|
+
export function renderOpenApiInspection(report) {
|
|
4
|
+
if (resolveOutputFormat() === "json") {
|
|
5
|
+
return JSON.stringify(report, null, 2);
|
|
6
|
+
}
|
|
7
|
+
return renderCatalog({
|
|
8
|
+
theme: getTheme(),
|
|
9
|
+
title: report.title ?? "OpenAPI",
|
|
10
|
+
...(report.version === undefined ? {} : { subtitle: `v${report.version}` }),
|
|
11
|
+
metrics: [
|
|
12
|
+
{
|
|
13
|
+
label: "compatible",
|
|
14
|
+
value: `${Math.round((report.supportedCount / Math.max(report.operationCount, 1)) * 100)}%`,
|
|
15
|
+
tone: report.unsupportedCount === 0 ? "success" : "accent"
|
|
16
|
+
},
|
|
17
|
+
{ label: pluralize("operation", report.operationCount), value: report.operationCount },
|
|
18
|
+
{ label: "supported", value: report.supportedCount, tone: "success" },
|
|
19
|
+
{
|
|
20
|
+
label: "unsupported",
|
|
21
|
+
value: report.unsupportedCount,
|
|
22
|
+
tone: report.unsupportedCount === 0 ? "muted" : "warning"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
groups: report.operationCount > LARGE_API_OPERATION_THRESHOLD
|
|
26
|
+
? summarizeOperationGroups(report.operations)
|
|
27
|
+
: groupOperations(report.operations)
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function groupOperations(operations) {
|
|
31
|
+
return collectOperationGroups(operations).map(([title, groupOperations]) => ({
|
|
32
|
+
title,
|
|
33
|
+
items: groupOperations.map((operation) => ({
|
|
34
|
+
label: operation.status === "supported" ? "ready" : "blocked",
|
|
35
|
+
value: `${operation.method} ${operation.path}`,
|
|
36
|
+
detail: operation.status === "supported"
|
|
37
|
+
? (operation.commandPath ?? operation.operationId)
|
|
38
|
+
: (operation.reason ?? operation.operationId),
|
|
39
|
+
tone: operation.status === "supported" ? "success" : "warning"
|
|
40
|
+
}))
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
function summarizeOperationGroups(operations) {
|
|
44
|
+
const groups = collectOperationGroups(operations);
|
|
45
|
+
const incompatibilities = summarizeIncompatibilities(operations);
|
|
46
|
+
const blockedGroups = groups
|
|
47
|
+
.filter(([, groupOperations]) => groupOperations.some((operation) => operation.status === "unsupported"))
|
|
48
|
+
.sort(([leftTitle, leftOperations], [rightTitle, rightOperations]) => {
|
|
49
|
+
const blockedCountDifference = countUnsupported(rightOperations) - countUnsupported(leftOperations);
|
|
50
|
+
return blockedCountDifference || leftTitle.localeCompare(rightTitle);
|
|
51
|
+
});
|
|
52
|
+
const resourceItems = blockedGroups.length === 0
|
|
53
|
+
? [
|
|
54
|
+
{
|
|
55
|
+
label: `${operations.length}/${operations.length}`,
|
|
56
|
+
value: "all resources",
|
|
57
|
+
detail: `${groups.length} groups compatible`,
|
|
58
|
+
tone: "success"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
: blockedGroups.map(([title, groupOperations]) => {
|
|
62
|
+
const supportedCount = groupOperations.filter((operation) => operation.status === "supported").length;
|
|
63
|
+
const unsupportedCount = countUnsupported(groupOperations);
|
|
64
|
+
const firstUnsupported = groupOperations.find((operation) => operation.status === "unsupported");
|
|
65
|
+
return {
|
|
66
|
+
label: `${supportedCount}/${groupOperations.length}`,
|
|
67
|
+
value: title,
|
|
68
|
+
detail: `${unsupportedCount} unsupported · ${firstUnsupported?.reason ?? "inspect JSON output"}`,
|
|
69
|
+
tone: "warning"
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
return [
|
|
73
|
+
{
|
|
74
|
+
title: "resources",
|
|
75
|
+
description: blockedGroups.length === 0
|
|
76
|
+
? "Every generated command group is compatible."
|
|
77
|
+
: `Showing ${blockedGroups.length} of ${groups.length} command groups that need attention. Use --output-format json for every route.`,
|
|
78
|
+
items: resourceItems
|
|
79
|
+
},
|
|
80
|
+
...(incompatibilities.length === 0
|
|
81
|
+
? []
|
|
82
|
+
: [
|
|
83
|
+
{
|
|
84
|
+
title: "top incompatibilities",
|
|
85
|
+
description: "First failing compatibility check per blocked route.",
|
|
86
|
+
items: incompatibilities.map(([reason, count]) => ({
|
|
87
|
+
label: `${count} ${pluralize("route", count)}`,
|
|
88
|
+
value: reason,
|
|
89
|
+
tone: "warning"
|
|
90
|
+
}))
|
|
91
|
+
}
|
|
92
|
+
])
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
function countUnsupported(operations) {
|
|
96
|
+
return operations.filter((operation) => operation.status === "unsupported").length;
|
|
97
|
+
}
|
|
98
|
+
function summarizeIncompatibilities(operations) {
|
|
99
|
+
const counts = new Map();
|
|
100
|
+
for (const operation of operations) {
|
|
101
|
+
if (operation.status !== "unsupported" || operation.reason === undefined) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const normalizedReason = operation.reason.replace(/^Operation "[^"]+" /, "Operation ");
|
|
105
|
+
counts.set(normalizedReason, (counts.get(normalizedReason) ?? 0) + 1);
|
|
106
|
+
}
|
|
107
|
+
return [...counts.entries()]
|
|
108
|
+
.sort(([leftReason, leftCount], [rightReason, rightCount]) => rightCount - leftCount || leftReason.localeCompare(rightReason))
|
|
109
|
+
.slice(0, 5);
|
|
110
|
+
}
|
|
111
|
+
function collectOperationGroups(operations) {
|
|
112
|
+
const groups = new Map();
|
|
113
|
+
for (const operation of operations) {
|
|
114
|
+
const groupName = getGroupName(operation);
|
|
115
|
+
const group = groups.get(groupName) ?? [];
|
|
116
|
+
group.push(operation);
|
|
117
|
+
groups.set(groupName, group);
|
|
118
|
+
}
|
|
119
|
+
return [...groups.entries()].sort(([left], [right]) => left.localeCompare(right));
|
|
120
|
+
}
|
|
121
|
+
function getGroupName(operation) {
|
|
122
|
+
if (operation.commandPath !== undefined) {
|
|
123
|
+
return operation.commandPath.split(" ")[0] ?? "root";
|
|
124
|
+
}
|
|
125
|
+
return (operation.path.split("/").find((segment) => segment.length > 0 && !segment.startsWith("{")) ??
|
|
126
|
+
"root");
|
|
127
|
+
}
|
|
128
|
+
function pluralize(noun, count) {
|
|
129
|
+
return count === 1 ? noun : `${noun}s`;
|
|
130
|
+
}
|
package/dist/request-shape.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export declare const REQUEST_PARAM_SECTIONS: readonly [{
|
|
|
6
6
|
readonly location: "query";
|
|
7
7
|
readonly key: "query";
|
|
8
8
|
readonly omittable: false;
|
|
9
|
+
}, {
|
|
10
|
+
readonly location: "header";
|
|
11
|
+
readonly key: "headers";
|
|
12
|
+
readonly omittable: false;
|
|
9
13
|
}, {
|
|
10
14
|
readonly location: "body";
|
|
11
15
|
readonly key: "body";
|
package/dist/request-shape.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const REQUEST_PARAM_SECTIONS = [
|
|
2
2
|
{ location: "path", key: "pathParams", omittable: false },
|
|
3
3
|
{ location: "query", key: "query", omittable: false },
|
|
4
|
+
{ location: "header", key: "headers", omittable: false },
|
|
4
5
|
{ location: "body", key: "body", omittable: true }
|
|
5
6
|
];
|
package/dist/runtime.js
CHANGED
|
@@ -53,10 +53,15 @@ function createRuntimeHandler(command) {
|
|
|
53
53
|
const resolvedValues = executePreflightBlocks(command.preflightBlocks, params);
|
|
54
54
|
const requestShape = buildRequestShape(command.requestFields, command.sectionRenders, command.optionalSections, params, resolvedValues);
|
|
55
55
|
return requestJson({
|
|
56
|
-
baseUrl,
|
|
56
|
+
baseUrl: command.baseUrl ?? baseUrl,
|
|
57
57
|
path: command.path,
|
|
58
58
|
method: command.method,
|
|
59
59
|
auth: command.auth,
|
|
60
|
+
responseMode: command.responseMode,
|
|
61
|
+
accept: command.accept,
|
|
62
|
+
bodyMode: command.bodyMode,
|
|
63
|
+
contentType: command.contentType,
|
|
64
|
+
multipartBinaryFields: command.multipartBinaryFields,
|
|
60
65
|
tokenSource,
|
|
61
66
|
fetch,
|
|
62
67
|
dryRun: params.dryRun,
|
|
@@ -80,6 +85,7 @@ const RUNTIME_DEFINITION_BUILDERS = {
|
|
|
80
85
|
},
|
|
81
86
|
boolean: (_definition, options) => options === undefined ? S.Boolean() : S.Boolean(options),
|
|
82
87
|
enum: (definition, options) => options === undefined ? S.Enum(definition.enumValues) : S.Enum(definition.enumValues, options),
|
|
88
|
+
json: (_definition) => S.Json(),
|
|
83
89
|
number: (_definition, options) => options === undefined ? S.Number() : S.Number(options),
|
|
84
90
|
string: (_definition, options) => options === undefined ? S.String() : S.String(options)
|
|
85
91
|
};
|
package/dist/spec-source.js
CHANGED
|
@@ -93,8 +93,11 @@ function formatParseErrorMessage(error, sourceText, filePath) {
|
|
|
93
93
|
filePath
|
|
94
94
|
})}`;
|
|
95
95
|
}
|
|
96
|
+
function hasOwnProperty(value, name) {
|
|
97
|
+
return Object.prototype.hasOwnProperty.call(value, name);
|
|
98
|
+
}
|
|
96
99
|
function getYamlLinePosition(error) {
|
|
97
|
-
if (typeof error !== "object" || error === null || !("linePos"
|
|
100
|
+
if (typeof error !== "object" || error === null || !hasOwnProperty(error, "linePos")) {
|
|
98
101
|
return null;
|
|
99
102
|
}
|
|
100
103
|
const linePos = error.linePos;
|
|
@@ -105,15 +108,15 @@ function getYamlLinePosition(error) {
|
|
|
105
108
|
if (typeof firstPosition !== "object" || firstPosition === null) {
|
|
106
109
|
return null;
|
|
107
110
|
}
|
|
108
|
-
const line = "line"
|
|
109
|
-
const column = "col"
|
|
111
|
+
const line = hasOwnProperty(firstPosition, "line") ? firstPosition.line : undefined;
|
|
112
|
+
const column = hasOwnProperty(firstPosition, "col") ? firstPosition.col : undefined;
|
|
110
113
|
if (typeof line !== "number" || typeof column !== "number") {
|
|
111
114
|
return null;
|
|
112
115
|
}
|
|
113
116
|
return { line, column };
|
|
114
117
|
}
|
|
115
118
|
function getYamlOffsetPosition(error, sourceText) {
|
|
116
|
-
if (typeof error !== "object" || error === null || !("pos"
|
|
119
|
+
if (typeof error !== "object" || error === null || !hasOwnProperty(error, "pos")) {
|
|
117
120
|
return null;
|
|
118
121
|
}
|
|
119
122
|
const pos = error.pos;
|
|
@@ -27,7 +27,7 @@ export function createSecretStore(input) {
|
|
|
27
27
|
}
|
|
28
28
|
function resolveBackend(input) {
|
|
29
29
|
const envVar = input.backendEnvVar ?? DEFAULT_BACKEND_ENV_VAR;
|
|
30
|
-
const configuredBackend = input.backend ?? input.env
|
|
30
|
+
const configuredBackend = input.backend ?? getOwnEnvValue(input.env, envVar) ?? getOwnEnvValue(process.env, envVar);
|
|
31
31
|
if (configuredBackend === "keychain") {
|
|
32
32
|
return "keychain";
|
|
33
33
|
}
|
|
@@ -36,3 +36,8 @@ function resolveBackend(input) {
|
|
|
36
36
|
}
|
|
37
37
|
throw new Error(`Unsupported auth store backend: ${configuredBackend}`);
|
|
38
38
|
}
|
|
39
|
+
function getOwnEnvValue(env, key) {
|
|
40
|
+
return env !== undefined && Object.prototype.hasOwnProperty.call(env, key)
|
|
41
|
+
? env[key]
|
|
42
|
+
: undefined;
|
|
43
|
+
}
|
|
@@ -42,6 +42,6 @@ export declare class EncryptedFileStore implements SecretStore {
|
|
|
42
42
|
get(): Promise<string | null>;
|
|
43
43
|
set(value: string): Promise<void>;
|
|
44
44
|
delete(): Promise<void>;
|
|
45
|
-
private
|
|
45
|
+
private assertCredentialPathHasNoSymbolicLinks;
|
|
46
46
|
private getEncryptionKey;
|
|
47
47
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { createCipheriv, createDecipheriv, randomBytes, scrypt } from "node:crypto";
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes, randomUUID, scrypt } from "node:crypto";
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
3
|
import { homedir, hostname, userInfo } from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { hasOwnErrorCode } from "./error-codes.js";
|
|
5
6
|
const derivedKeyCache = new Map();
|
|
6
7
|
const ENCRYPTION_ALGORITHM = "aes-256-gcm";
|
|
7
8
|
const ENCRYPTION_VERSION = 1;
|
|
@@ -9,7 +10,6 @@ const ENCRYPTION_KEY_BYTES = 32;
|
|
|
9
10
|
const ENCRYPTION_IV_BYTES = 12;
|
|
10
11
|
const ENCRYPTION_AUTH_TAG_BYTES = 16;
|
|
11
12
|
const ENCRYPTION_FILE_MODE = 0o600;
|
|
12
|
-
let temporaryFileSequence = 0;
|
|
13
13
|
export class EncryptedFileStore {
|
|
14
14
|
fs;
|
|
15
15
|
filePath;
|
|
@@ -35,7 +35,7 @@ export class EncryptedFileStore {
|
|
|
35
35
|
this.getRandomBytes = input.getRandomBytes ?? randomBytes;
|
|
36
36
|
}
|
|
37
37
|
async get() {
|
|
38
|
-
await this.
|
|
38
|
+
await this.assertCredentialPathHasNoSymbolicLinks(this.filePath);
|
|
39
39
|
let rawDocument;
|
|
40
40
|
try {
|
|
41
41
|
rawDocument = await this.fs.readFile(this.filePath, "utf8");
|
|
@@ -69,7 +69,7 @@ export class EncryptedFileStore {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
async set(value) {
|
|
72
|
-
await this.
|
|
72
|
+
await this.assertCredentialPathHasNoSymbolicLinks(this.filePath);
|
|
73
73
|
const key = await this.getEncryptionKey();
|
|
74
74
|
const iv = this.getRandomBytes(ENCRYPTION_IV_BYTES);
|
|
75
75
|
const cipher = createCipheriv(ENCRYPTION_ALGORITHM, key, iv);
|
|
@@ -85,26 +85,29 @@ export class EncryptedFileStore {
|
|
|
85
85
|
ciphertext: ciphertext.toString("base64")
|
|
86
86
|
};
|
|
87
87
|
await this.fs.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
88
|
-
await this.
|
|
89
|
-
const temporaryPath = `${this.filePath}.${process.pid}.${
|
|
88
|
+
await this.assertCredentialPathHasNoSymbolicLinks(this.filePath);
|
|
89
|
+
const temporaryPath = `${this.filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
90
|
+
let temporaryCreated = false;
|
|
90
91
|
try {
|
|
92
|
+
await this.assertCredentialPathHasNoSymbolicLinks(temporaryPath);
|
|
91
93
|
await this.fs.writeFile(temporaryPath, JSON.stringify(document), {
|
|
92
94
|
encoding: "utf8",
|
|
93
95
|
flag: "wx",
|
|
94
96
|
mode: ENCRYPTION_FILE_MODE
|
|
95
97
|
});
|
|
98
|
+
temporaryCreated = true;
|
|
96
99
|
await this.fs.chmod(temporaryPath, ENCRYPTION_FILE_MODE);
|
|
97
100
|
await this.fs.rename(temporaryPath, this.filePath);
|
|
98
101
|
}
|
|
99
102
|
catch (error) {
|
|
100
|
-
if (!isAlreadyExistsError(error)) {
|
|
103
|
+
if (temporaryCreated || !isAlreadyExistsError(error)) {
|
|
101
104
|
await removeIfPresent(this.fs, temporaryPath).catch(() => undefined);
|
|
102
105
|
}
|
|
103
106
|
throw error;
|
|
104
107
|
}
|
|
105
108
|
}
|
|
106
109
|
async delete() {
|
|
107
|
-
await this.
|
|
110
|
+
await this.assertCredentialPathHasNoSymbolicLinks(this.filePath);
|
|
108
111
|
try {
|
|
109
112
|
await this.fs.unlink(this.filePath);
|
|
110
113
|
}
|
|
@@ -114,8 +117,8 @@ export class EncryptedFileStore {
|
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
}
|
|
117
|
-
async
|
|
118
|
-
const resolvedPath = path.resolve(
|
|
120
|
+
async assertCredentialPathHasNoSymbolicLinks(targetPath) {
|
|
121
|
+
const resolvedPath = path.resolve(targetPath);
|
|
119
122
|
const protectedPaths = getProtectedCredentialPaths(resolvedPath, this.symbolicLinkCheckStartPath);
|
|
120
123
|
for (const currentPath of protectedPaths) {
|
|
121
124
|
try {
|
|
@@ -179,12 +182,6 @@ async function removeIfPresent(fileSystem, filePath) {
|
|
|
179
182
|
}
|
|
180
183
|
}
|
|
181
184
|
}
|
|
182
|
-
function isAlreadyExistsError(error) {
|
|
183
|
-
return (typeof error === "object" &&
|
|
184
|
-
error !== null &&
|
|
185
|
-
"code" in error &&
|
|
186
|
-
error.code === "EEXIST");
|
|
187
|
-
}
|
|
188
185
|
function defaultMachineIdentity() {
|
|
189
186
|
return {
|
|
190
187
|
hostname: hostname(),
|
|
@@ -222,19 +219,23 @@ function parseEncryptedDocument(raw) {
|
|
|
222
219
|
if (!isRecord(parsed)) {
|
|
223
220
|
return null;
|
|
224
221
|
}
|
|
225
|
-
|
|
222
|
+
const version = getOwnEntry(parsed, "version");
|
|
223
|
+
const iv = getOwnEntry(parsed, "iv");
|
|
224
|
+
const authTag = getOwnEntry(parsed, "authTag");
|
|
225
|
+
const ciphertext = getOwnEntry(parsed, "ciphertext");
|
|
226
|
+
if (version !== ENCRYPTION_VERSION) {
|
|
226
227
|
return null;
|
|
227
228
|
}
|
|
228
|
-
if (typeof
|
|
229
|
-
typeof
|
|
230
|
-
typeof
|
|
229
|
+
if (typeof iv !== "string" ||
|
|
230
|
+
typeof authTag !== "string" ||
|
|
231
|
+
typeof ciphertext !== "string") {
|
|
231
232
|
return null;
|
|
232
233
|
}
|
|
233
234
|
return {
|
|
234
|
-
version
|
|
235
|
-
iv
|
|
236
|
-
authTag
|
|
237
|
-
ciphertext
|
|
235
|
+
version,
|
|
236
|
+
iv,
|
|
237
|
+
authTag,
|
|
238
|
+
ciphertext
|
|
238
239
|
};
|
|
239
240
|
}
|
|
240
241
|
catch {
|
|
@@ -244,9 +245,12 @@ function parseEncryptedDocument(raw) {
|
|
|
244
245
|
function isRecord(value) {
|
|
245
246
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
246
247
|
}
|
|
248
|
+
function getOwnEntry(record, key) {
|
|
249
|
+
return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : undefined;
|
|
250
|
+
}
|
|
247
251
|
function isNotFoundError(error) {
|
|
248
|
-
return
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
+
return hasOwnErrorCode(error, "ENOENT");
|
|
253
|
+
}
|
|
254
|
+
function isAlreadyExistsError(error) {
|
|
255
|
+
return hasOwnErrorCode(error, "EEXIST");
|
|
252
256
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function hasOwnErrorCode(error: unknown, code: string): boolean;
|