apollo-conn-gen 0.14.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/oas.js +2 -0
- package/dist/cli/oas.js.map +1 -1
- package/dist/oas/io/operationWriter.js +18 -6
- package/dist/oas/io/operationWriter.js.map +1 -1
- package/dist/oas/io/security.d.ts +10 -2
- package/dist/oas/io/security.js +42 -9
- package/dist/oas/io/security.js.map +1 -1
- package/dist/oas/io/writer.js +12 -9
- package/dist/oas/io/writer.js.map +1 -1
- package/dist/oas/nodes/comp.js +23 -18
- package/dist/oas/nodes/comp.js.map +1 -1
- package/dist/oas/nodes/factory.js +4 -6
- package/dist/oas/nodes/factory.js.map +1 -1
- package/dist/oas/nodes/obj.js +3 -2
- package/dist/oas/nodes/obj.js.map +1 -1
- package/dist/oas/nodes/propArray.js.map +1 -1
- package/dist/oas/nodes/type.js +8 -6
- package/dist/oas/nodes/type.js.map +1 -1
- package/dist/oas/nodes/typeUtils.d.ts +3 -1
- package/dist/oas/nodes/typeUtils.js +47 -3
- package/dist/oas/nodes/typeUtils.js.map +1 -1
- package/dist/oas/nodes/union.d.ts +3 -1
- package/dist/oas/nodes/union.js +19 -4
- package/dist/oas/nodes/union.js.map +1 -1
- package/dist/oas/oasContext.d.ts +1 -0
- package/dist/oas/oasContext.js.map +1 -1
- package/dist/oas/oasGen.d.ts +1 -0
- package/dist/oas/oasGen.js.map +1 -1
- package/dist/oas/utils/naming.js +4 -1
- package/dist/oas/utils/naming.js.map +1 -1
- package/dist/tests/runners.d.ts +1 -0
- package/dist/tests/runners.js +1 -0
- package/dist/tests/runners.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/oas.js
CHANGED
|
@@ -61,6 +61,7 @@ async function main(sourceFile, opts) {
|
|
|
61
61
|
mapper: mapper,
|
|
62
62
|
skipOptionalArgs: opts.skipOptionalArgs,
|
|
63
63
|
inferEntityResolvers: opts.inferEntityResolvers,
|
|
64
|
+
skipAuth: opts.skipAuth,
|
|
64
65
|
});
|
|
65
66
|
await gen.visit();
|
|
66
67
|
let pathSet = Array.from(gen.paths.values());
|
|
@@ -113,6 +114,7 @@ program
|
|
|
113
114
|
.option('--batch <file>', 'Load batch endpoints (op id -> { maxSize? }) from a JSON file')
|
|
114
115
|
.option('--skip-optional-args', 'Skip optional arguments in queries', false)
|
|
115
116
|
.option('--infer-entity-resolvers', 'Infer entity resolvers and emit @key / entity: true', false)
|
|
117
|
+
.option('--skip-auth', 'Omit all auth (no headers on @source, no auth on @connect)', false)
|
|
116
118
|
.parse(process.argv);
|
|
117
119
|
const source = program.args[0];
|
|
118
120
|
main(source, program.opts()).then(() => console.log('done'));
|
package/dist/cli/oas.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oas.js","sourceRoot":"","sources":["../../src/cli/oas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,OAAO,EAAgB,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAoB,MAAM,wBAAwB,CAAC;AAErF,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CACnC;IACE,GAAG,EAAE,OAAO,CAAC,GAAG;CACjB,EACD,OAAO,CACR,CAAC;AAEF,SAAS,SAAS,CAAC,IAAkB;IACnC,IAAI,MAAM,CAAC;IACX,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACxD,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kCAAkC,KAAK,EAAE,CAAC,CAAC;YACzD,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,IAAkB;IACvC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACpB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;QACnD,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,IAAkB;IACnC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,6BAA6B,KAAK,EAAE,CAAC,CAAC;QACpD,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,UAAkB,EAAE,IAAkB;IACxD,OAAO,CAAC,GAAG,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;IAEvB,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAE9B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE;QAC5C,GAAG,IAAI;QACP,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS;QACT,KAAK;QACL,sBAAsB,EAAE,KAAK;QAC7B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;QACzC,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;QAC/C,MAAM,EAAE,MAAM;QACd,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;
|
|
1
|
+
{"version":3,"file":"oas.js","sourceRoot":"","sources":["../../src/cli/oas.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport { Command, OptionValues } from 'commander';\nimport { DEFAULT_VERSIONS } from '../versions.js';\nimport { generateFromSelection, promptForSelection } from './oas-helpers/index.js';\nimport { OasGen } from '../oas/oasGen.js';\nimport { BatchConfig, RequestOverride } from '../oas/oasContext.js';\nimport { RulesLoader, OpNameMapper, MapRules, Mapper } from '../oas/mapper/index.js';\n\nconst originalConsole = Object.assign(\n {\n log: console.log,\n },\n console,\n);\n\nfunction loadRules(opts: OptionValues): Mapper | undefined {\n let mapper;\n if (opts.transformRules) {\n try {\n const rules = RulesLoader.fromFile(opts.transformRules);\n mapper = OpNameMapper.fromRules(rules);\n } catch (error) {\n console.error(`Error loading transform rules: ${error}`);\n return undefined;\n }\n }\n\n return mapper;\n}\n\nfunction loadOverrides(opts: OptionValues): Record<string, RequestOverride> | undefined {\n if (!opts.overrides) {\n return undefined;\n }\n try {\n return JSON.parse(fs.readFileSync(opts.overrides, 'utf-8'));\n } catch (error) {\n console.error(`Error loading overrides: ${error}`);\n return undefined;\n }\n}\n\nfunction loadBatch(opts: OptionValues): BatchConfig | undefined {\n if (!opts.batch) {\n return undefined;\n }\n try {\n return JSON.parse(fs.readFileSync(opts.batch, 'utf-8'));\n } catch (error) {\n console.error(`Error loading batch file: ${error}`);\n return undefined;\n }\n}\n\nasync function main(sourceFile: string, opts: OptionValues): Promise<void> {\n console.log = () => {};\n\n const mapper = loadRules(opts);\n const overrides = loadOverrides(opts);\n const batch = loadBatch(opts);\n\n const gen = await OasGen.fromFile(sourceFile, {\n ...opts,\n baseURL: opts.baseUrl,\n overrides,\n batch,\n showParentInSelections: false,\n federationVersion: opts.federationVersion,\n connectorSpecVersion: opts.connectorSpecVersion,\n mapper: mapper,\n skipOptionalArgs: opts.skipOptionalArgs,\n inferEntityResolvers: opts.inferEntityResolvers,\n skipAuth: opts.skipAuth,\n });\n\n await gen.visit();\n\n let pathSet = Array.from(gen.paths.values());\n if (opts.loadSelections) {\n generateFromSelection(opts, gen);\n return;\n }\n\n if (opts.grep !== '*') {\n const regex = new RegExp(opts.grep, 'ig');\n pathSet = pathSet.filter((p) => regex.test(p.path()));\n }\n\n if (opts.listPaths) {\n pathSet.forEach((path) => console.info(path.path()));\n return;\n }\n\n let paths: string[];\n if (opts.skipSelection) {\n paths = pathSet.map((p) => p.path() + '>**');\n } else {\n paths = await promptForSelection(gen, opts, pathSet);\n }\n\n if (opts.verbose) console = originalConsole;\n\n console.info(gen.generateSchema(paths));\n\n if (opts.printSelections) {\n console.info('# --------------- Selections -----------------');\n console.info(gen.selections);\n console.info('# --------------- Paths -----------------');\n console.info('paths =', JSON.stringify(paths, null, 2));\n }\n}\n\nconst program = new Command();\nprogram\n .version('0.0.1')\n .argument('<source>', 'source spec (yaml or json)')\n .option('-i --skip-validation', 'Skip validation step', false)\n .option('-n --skip-selection', 'Generate all [filtered] paths without prompting for a selection', false)\n .option('-l --list-paths', 'Only list the paths that can be generated', false)\n .option('-g --grep <regex>', 'Filter the list of paths with the passed expression', '*')\n .option('-p --page-size <num>', 'Number of rows to display in selection mode', '10')\n .option('-s --load-selections <file>', 'Load a JSON file with field selections (other options are ignored)')\n .option('-v --verbose', 'Log all messages from generator')\n .option('-m --print-selections', 'Print selections from generator')\n .option('-t --transform-rules <file>', 'Load transform rules from a JSON file to apply multiple name transformations')\n .option('--federation-version <version>', 'Federation version to use', DEFAULT_VERSIONS.federationVersion)\n .option('--connector-spec-version <version>', 'Connector spec version to use', DEFAULT_VERSIONS.connectorSpecVersion)\n .option('--base-url <url>', 'Override the @source base URL (default: servers[0] from the spec)')\n .option('--overrides <file>', 'Load per-operation path/queryParams overrides from a JSON file')\n .option('--batch <file>', 'Load batch endpoints (op id -> { maxSize? }) from a JSON file')\n .option('--skip-optional-args', 'Skip optional arguments in queries', false)\n .option('--infer-entity-resolvers', 'Infer entity resolvers and emit @key / entity: true', false)\n .option('--skip-auth', 'Omit all auth (no headers on @source, no auth on @connect)', false)\n .parse(process.argv);\n\nconst source = program.args[0];\nmain(source, program.opts()).then(() => console.log('done'));\n"]}
|
|
@@ -3,6 +3,8 @@ import { T } from '../nodes/internal.js';
|
|
|
3
3
|
import { Naming } from '../utils/naming.js';
|
|
4
4
|
import { Params } from '../utils/params.js';
|
|
5
5
|
import { ErrorsWriter } from './errorsWriter.js';
|
|
6
|
+
import { DEFAULT_VERSIONS, meetsMinimum } from '../../versions.js';
|
|
7
|
+
import { warn } from '../log/trace.js';
|
|
6
8
|
export class OperationWriter {
|
|
7
9
|
gen;
|
|
8
10
|
security;
|
|
@@ -69,7 +71,15 @@ export class OperationWriter {
|
|
|
69
71
|
// body is emitted last; its presence is cheap to compute up front. the body block streams
|
|
70
72
|
// through the node tree (Body.select), so unlike query/headers it writes to `writer` directly
|
|
71
73
|
// instead of returning a string. `null` drops the body; a string overrides it; see ROADMAP R9.
|
|
72
|
-
|
|
74
|
+
// C2: `body:` is a connect v0.2+ feature — gate it like errors (R4) and batch (R6). Below the
|
|
75
|
+
// floor, drop the body and warn once per op (do not emit invalid output for an ancient target).
|
|
76
|
+
const wouldEmitBody = typeof override?.body === 'string' || (override?.body === undefined && !!op.body);
|
|
77
|
+
const version = this.gen.options.connectorSpecVersion || DEFAULT_VERSIONS.connectorSpecVersion;
|
|
78
|
+
const bodyAllowed = meetsMinimum(version, 'v0.2');
|
|
79
|
+
if (!bodyAllowed && wouldEmitBody) {
|
|
80
|
+
warn(context, '[body]', `@connect(http.body) requires connect v0.2, but target is ${version} — body not emitted for ${op.verb} ${op.operation.path}`);
|
|
81
|
+
}
|
|
82
|
+
const hasBody = bodyAllowed && wouldEmitBody;
|
|
73
83
|
// query & header blocks are pure string-building — they return the block, or null when empty
|
|
74
84
|
const queryBlock = this.queryParamsBlock(context, op, override, queryAuth);
|
|
75
85
|
const headerBlock = this.headersBlock(op, override, headerAuth);
|
|
@@ -86,11 +96,13 @@ export class OperationWriter {
|
|
|
86
96
|
writer.write(queryBlock);
|
|
87
97
|
if (headerBlock)
|
|
88
98
|
writer.write(headerBlock);
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
if (hasBody) {
|
|
100
|
+
if (typeof override?.body === 'string') {
|
|
101
|
+
this.writeBodyOverride(writer, override.body);
|
|
102
|
+
}
|
|
103
|
+
else if (op.body) {
|
|
104
|
+
this.writeBodySelection(context, writer, op.body, selection);
|
|
105
|
+
}
|
|
94
106
|
}
|
|
95
107
|
writer.write(' '.repeat(6) + '}');
|
|
96
108
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operationWriter.js","sourceRoot":"","sources":["../../../src/oas/io/operationWriter.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,QAAQ,CAAC;AAIvB,OAAO,EAA0B,CAAC,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIjD,MAAM,OAAO,eAAe;IAIhB;IACA;IAJF,YAAY,CAAe;IAEnC,YACU,GAAW,EACX,QAAsB;QADtB,QAAG,GAAH,GAAG,CAAQ;QACX,aAAQ,GAAR,QAAQ,CAAc;QAE9B,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC;IAC5C,CAAC;IAEM,UAAU,CAAC,OAAmB,EAAE,MAAc,EAAE,SAA6B,EAAE,SAAmB;QACvG,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QAE7B,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAEM,cAAc,CAAC,OAAmB,EAAE,MAAc,EAAE,SAA6B,EAAE,SAAmB;QAC3G,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QAE7B,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAEM,cAAc,CAAC,OAAmB,EAAE,MAAc,EAAE,IAAW,EAAE,SAAmB;QACzF,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC;QAChB,MAAM,MAAM,GAAG,CAAC,CAAC;QACjB,IAAI,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAE3C,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAE9E,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAE3D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAE5D,qFAAqF;QACrF,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAErC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QAErD,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAEO,aAAa,CAAC,OAAmB,EAAE,MAAc,EAAE,EAAM,EAAE,SAAmB,EAAE,OAAe;QACrG,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAE5D,6FAA6F;QAC7F,uFAAuF;QACvF,kEAAkE;QAClE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAEzE,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAE9C,0FAA0F;QAC1F,8FAA8F;QAC9F,+FAA+F;QAC/F,MAAM,OAAO,GAAG,OAAO,QAAQ,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC,QAAQ,EAAE,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAElG,6FAA6F;QAC7F,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC3E,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QAEhE,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5C,yEAAyE;YACzE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,MAAM,IAAI,IAAI,CAAC,CAAC;YACzC,OAAO;QACT,CAAC;QAED,6FAA6F;QAC7F,6FAA6F;QAC7F,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,IAAI,MAAM,IAAI,KAAK,CAAC,CAAC;QACxD,IAAI,UAAU;YAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,WAAW;YAAE,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,OAAO,QAAQ,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;aAAM,IAAI,QAAQ,EAAE,IAAI,KAAK,SAAS,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YACnD,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC/D,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;IACpC,CAAC;IAED,yGAAyG;IACzG,iGAAiG;IACzF,aAAa,CAAC,EAAM,EAAE,QAA0B;QACtD,OAAO,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAC/E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAClE,CAAC;IACJ,CAAC;IAED,gGAAgG;IAChG,+FAA+F;IAC/F,wDAAwD;IAChD,gBAAgB,CACtB,OAAmB,EACnB,EAAM,EACN,QAAqC,EACrC,IAAsB;QAEtB,gFAAgF;QAChF,4DAA4D;QAC5D,IAAI,WAAW,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAQ,EAAE,EAAE;YAC9C,OAAO,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,mDAAmD;QACnD,IAAI,OAAO,CAAC,eAAe,EAAE,gBAAgB,EAAE,CAAC;YAC9C,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7D,CAAC;QAED,uFAAuF;QACvF,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CACjC,WAAW,EACX,QAAQ,EAAE,WAAW,IAAI,EAAE,EAC3B,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAC5D,CAAC;QAEF,8FAA8F;QAC9F,+FAA+F;QAC/F,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAa,CAAC,0BAA0B,CAAC,CAAC;QACrD,6FAA6F;QAC7F,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAChC,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;gBACtC,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,MAAM,KAAK,EAAE,CAAC,CAAC;YAChD,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5B,CAAC;QACD,gGAAgG;QAChG,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC1B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjC,CAAC;IAED,+FAA+F;IAC/F,6FAA6F;IACrF,YAAY,CAAC,EAAM,EAAE,QAAqC,EAAE,IAAsB;QACxF,mFAAmF;QACnF,MAAM,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,CAAC;QACpG,IAAI,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAElG,2FAA2F;QAC3F,8FAA8F;QAC9F,wEAAwE;QACxE,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAClF,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvE,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,GAAG,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAa,CAAC,oBAAoB,CAAC,CAAC;QAC/C,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,sBAAsB,IAAI,cAAc,KAAK,KAAK,CAAC,CAAC;QACjE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjC,CAAC;IAED,uFAAuF;IACvF,mEAAmE;IAC3D,cAAc,CACpB,QAAa,EACb,SAAwC,EACxC,aAAkC;QAElC,MAAM,IAAI,GAAG,QAAQ;aAClB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;aAC/C,GAAG,CAAC,CAAC,IAAI,EAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAEvG,yFAAyF;QACzF,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAgB,EAAE,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;gBACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,8FAA8F;IACtF,aAAa,CAAC,CAAkB;QACtC,IAAI,CAAC,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;YACtB,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,uDAAuD;IAC/C,SAAS,CAAC,CAAQ;QACxB,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAEO,cAAc,CAAC,OAAmB,EAAE,MAAc,EAAE,IAAW,EAAE,SAAmB;QAC1F,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,+FAA+F;IAC/F,gGAAgG;IAChG,mCAAmC;IAC3B,iBAAiB,CAAC,MAAc,EAAE,IAAY;QACpD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,aAAa,CAAC,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC;IAClC,CAAC;IAEO,kBAAkB,CAAC,OAAmB,EAAE,MAAc,EAAE,IAAU,EAAE,SAAmB;QAC7F,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;CACF","sourcesContent":["import _ from 'lodash';\nimport { ParameterObject } from 'oas/types';\nimport { OasContext, RequestOverride } from '../oasContext.js';\nimport { OasGen } from '../oasGen.js';\nimport { Body, IType, Op, Param, T } from '../nodes/internal.js';\nimport { Naming } from '../utils/naming.js';\nimport { Params } from '../utils/params.js';\nimport { ErrorsWriter } from './errorsWriter.js';\nimport { Writer } from './writer.js';\nimport { NameValue, SecurityPlan } from './security.js';\n\nexport class OperationWriter {\n private errorsWriter: ErrorsWriter;\n\n constructor(\n private gen: OasGen,\n private security: SecurityPlan,\n ) {\n this.errorsWriter = new ErrorsWriter(gen);\n }\n\n public writeQuery(context: OasContext, writer: Writer, collected: Map<string, IType>, selection: string[]): void {\n const selectionSet = new Set<string>(selection.map((s) => s.split('>')[0]));\n\n const paths = Array.from(collected.values()).filter((path) => selectionSet.has(path.id));\n if (_.isEmpty(paths)) return;\n\n writer.write('type Query {\\n');\n\n for (const path of paths) {\n path.generate(context, writer, []);\n this.writeConnector(context, writer, path, selection);\n context.generatedSet.add(path.id);\n }\n\n writer.write('}\\n\\n');\n }\n\n public writeMutations(context: OasContext, writer: Writer, collected: Map<string, IType>, selection: string[]): void {\n const selectionSet = new Set<string>(selection.map((s) => s.split('>')[0]));\n\n const paths = Array.from(collected.values()).filter((path) => selectionSet.has(path.id));\n if (_.isEmpty(paths)) return;\n\n writer.write('type Mutation {\\n');\n\n for (const path of paths) {\n path.generate(context, writer, []);\n this.writeConnector(context, writer, path, selection);\n context.generatedSet.add(path.id);\n }\n\n writer.write('}\\n\\n');\n }\n\n public writeConnector(context: OasContext, writer: Writer, type: IType, selection: string[]): void {\n if (!T.isOp(type)) {\n throw new Error(`expected an operation node, got ${type.id}`);\n }\n\n const op = type;\n const indent = 0;\n let spacing = ' '.repeat(indent + 4);\n writer.write(spacing).write('@connect(\\n');\n\n spacing = ' '.repeat(indent + 6);\n writer.write(spacing).write('source: \"api\"\\n').write(spacing).write('http: ');\n\n this.requestMethod(context, writer, op, selection, indent);\n\n writer.write('\\n').write(spacing).write('selection: \"\"\"\\n');\n\n // truthiness, not _.has — the declared-but-unset field is still an own property. #33\n if (op.resultType) {\n this.writeSelection(context, writer, op.resultType, selection);\n }\n\n writer.write(spacing).write('\"\"\"\\n');\n\n this.errorsWriter.write(context, writer, op, indent);\n\n spacing = ' '.repeat(indent + 4);\n writer.write(spacing).write(')\\n');\n }\n\n private requestMethod(context: OasContext, writer: Writer, op: Op, selection: string[], _indent: number): void {\n const override = context.generateOptions.overrides?.[op.id];\n\n // R5: this op's resolved auth, split by placement. A header lives on @connect only in per-op\n // mode (uniform mode puts it on @source); query auth always lives here (@source has no\n // queryParams). Warnings are owned by the plan — see security.ts.\n const { header: headerAuth, query: queryAuth } = this.security.forOp(op);\n\n const path = this.templatedPath(op, override);\n\n // body is emitted last; its presence is cheap to compute up front. the body block streams\n // through the node tree (Body.select), so unlike query/headers it writes to `writer` directly\n // instead of returning a string. `null` drops the body; a string overrides it; see ROADMAP R9.\n const hasBody = typeof override?.body === 'string' || (override?.body === undefined && !!op.body);\n\n // query & header blocks are pure string-building — they return the block, or null when empty\n const queryBlock = this.queryParamsBlock(context, op, override, queryAuth);\n const headerBlock = this.headersBlock(op, override, headerAuth);\n\n if (!queryBlock && !headerBlock && !hasBody) {\n // nothing but the path: keep the compact single-line form `{ GET: \"/x\"}`\n writer.write(`{ ${op.verb}: \"${path}\"}`);\n return;\n }\n\n // expanded: `http: {` on its own line, the verb and each member at indent 8, the closing `}`\n // aligned under `http:` (indent 6). members are comma-less (GraphQL multiline object style).\n writer.write('{\\n');\n writer.write(' '.repeat(8) + `${op.verb}: \"${path}\"\\n`);\n if (queryBlock) writer.write(queryBlock);\n if (headerBlock) writer.write(headerBlock);\n if (typeof override?.body === 'string') {\n this.writeBodyOverride(writer, override.body);\n } else if (override?.body === undefined && op.body) {\n this.writeBodySelection(context, writer, op.body, selection);\n }\n writer.write(' '.repeat(6) + '}');\n }\n\n // template each {elem} as {$args.<sanitised>} (the arg name), not the raw OAS key. see docs/issues.md #2\n // an override path may already template (`{$args.id}`, `{$config.v}`) — leave `$` segments alone\n private templatedPath(op: Op, override?: RequestOverride): string {\n return (override?.path ?? op.operation.path).replace(/\\{([^}]+)\\}/g, (m, name) =>\n name.startsWith('$') ? m : `{$args.${Naming.genParamName(name)}}`,\n );\n }\n\n // the op's queryParams block (a self-contained string ending in a newline), or null when the op\n // has no query params and no apiKey-in-query auth. `auth` is the op's resolved apiKey-in-query\n // entry (or null) — see requestMethod / security.forOp.\n private queryParamsBlock(\n context: OasContext,\n op: Op,\n override: RequestOverride | undefined,\n auth: NameValue | null,\n ): string | null {\n // we now include all query params, not just required ones. if they are not set,\n // then the connectors will not include them in the request.\n let queryParams = op.params.filter((p: Param) => {\n return p.parameter.in && p.parameter.in.toLowerCase() === 'query';\n });\n\n // Skip optional params if skipOptionalArgs is true\n if (context.generateOptions?.skipOptionalArgs) {\n queryParams = queryParams.filter((p: Param) => p.required);\n }\n\n // e.g. `\"api-version\": $('2024-01')` appended, `\"ids\": ids->joinNotNull(\";\")` replaced\n const entries = this.mergeOverrides(\n queryParams,\n override?.queryParams ?? {},\n (p) => `${Naming.genParamName(p.name)}${this.arrayJoin(p)}`,\n );\n\n // R5 slice 3: apiKey-in-query auth merges into the same query-param object as a JSONSelection\n // sibling outside the `$args { … }` block, so an auth-only op still emits a queryParams block.\n if (entries.length === 0 && !auth) {\n return null;\n }\n\n const lines: string[] = [' queryParams: \"\"\"'];\n // the `$args { … }` block only when there are arg-derived params (skipped for auth-only ops)\n if (entries.length > 0) {\n lines.push(' $args {');\n for (const { name, value } of entries) {\n lines.push(` \"${name}\": ${value}`);\n }\n lines.push(' }');\n }\n // e.g. `\"api_key\": $config.apiKey` — key quoted so non-identifier names like `api-key` are safe\n if (auth) {\n lines.push(` \"${auth.name}\": ${auth.value}`);\n }\n lines.push(' \"\"\"');\n return lines.join('\\n') + '\\n';\n }\n\n // the op's headers block (a self-contained string ending in a newline), or null when there are\n // no headers to send. `auth` is the op's resolved auth header (or null) — see requestMethod.\n private headersBlock(op: Op, override: RequestOverride | undefined, auth: NameValue | null): string | null {\n // OAS `header` params, with user overrides merged in (string replaces, null drops)\n const headers = op.operation.getParameters().filter((p) => p.in && p.in.toLowerCase() === 'header');\n let entries = this.mergeOverrides(headers, override?.headers ?? {}, (p) => this.headerExample(p));\n\n // HTTP header names are case-insensitive: an explicit user override of the same name wins;\n // otherwise the resolved auth replaces any inferred OAS header of that name (so we never emit\n // a real credential alongside a placeholder that differs only in case).\n if (auth) {\n const sameName = (name: string) => name.toLowerCase() === auth.name.toLowerCase();\n const overridden = Object.keys(override?.headers ?? {}).some(sameName);\n if (!overridden) {\n entries = [auth, ...entries.filter((entry) => !sameName(entry.name))];\n }\n }\n\n if (entries.length === 0) {\n return null;\n }\n\n const lines: string[] = [' headers: ['];\n for (const { name, value } of entries) {\n lines.push(` { name: \"${name}\", value: \"${value}\" }`);\n }\n lines.push(' ]');\n return lines.join('\\n') + '\\n';\n }\n\n // merge user overrides over the inferred params: a string replaces the inferred value,\n // null drops the entry, an unknown key is appended. see ROADMAP R8\n private mergeOverrides<T extends { name: string }>(\n inferred: T[],\n overrides: Record<string, string | null>,\n inferredValue: (item: T) => string,\n ): NameValue[] {\n const kept = inferred\n .filter((item) => overrides[item.name] !== null)\n .map((item): NameValue => ({ name: item.name, value: overrides[item.name] ?? inferredValue(item) }));\n\n // overrides whose key isn't an inferred param are appended (null already excluded above)\n const known = new Set(inferred.map((item) => item.name));\n const appended: NameValue[] = [];\n for (const [name, value] of Object.entries(overrides)) {\n if (!known.has(name) && value != null) {\n appended.push({ name, value });\n }\n }\n\n return [...kept, ...appended];\n }\n\n // inferred header value: the example, the example keys, or a placeholder for the user to fill\n private headerExample(p: ParameterObject): string {\n if (p.example != null) {\n return p.example.toString();\n }\n if (p.examples && Object.keys(p.examples).length > 0) {\n return Object.keys(p.examples).join(',');\n }\n return '<placeholder>';\n }\n\n // see Params.arrayJoin (shared with the R6 batch path)\n private arrayJoin(p: Param): string {\n return Params.arrayJoin(p.parameter);\n }\n\n private writeSelection(context: OasContext, writer: Writer, type: IType, selection: string[]): void {\n context.indent = 6;\n type.select(context, writer, selection);\n }\n\n // mirrors Body.select formatting, but the user's raw JSONSelection replaces the whole mapping.\n // emits the block at indent 8 (an http-object member), ending with a newline; the caller places\n // the http object's closing brace.\n private writeBodyOverride(writer: Writer, body: string): void {\n const spacing = ' '.repeat(8);\n writer.write(spacing + 'body: \"\"\"\\n');\n for (const line of body.split('\\n')) {\n writer.write(spacing + ' ' + line + '\\n');\n }\n writer.write(spacing + '\"\"\"\\n');\n }\n\n private writeBodySelection(context: OasContext, writer: Writer, body: Body, selection: string[]): void {\n context.indent = 8;\n body.select(context, writer, selection);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"operationWriter.js","sourceRoot":"","sources":["../../../src/oas/io/operationWriter.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,QAAQ,CAAC;AAIvB,OAAO,EAA0B,CAAC,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,MAAM,OAAO,eAAe;IAIhB;IACA;IAJF,YAAY,CAAe;IAEnC,YACU,GAAW,EACX,QAAsB;QADtB,QAAG,GAAH,GAAG,CAAQ;QACX,aAAQ,GAAR,QAAQ,CAAc;QAE9B,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC;IAC5C,CAAC;IAEM,UAAU,CAAC,OAAmB,EAAE,MAAc,EAAE,SAA6B,EAAE,SAAmB;QACvG,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QAE7B,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAEM,cAAc,CAAC,OAAmB,EAAE,MAAc,EAAE,SAA6B,EAAE,SAAmB;QAC3G,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QAE7B,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAEM,cAAc,CAAC,OAAmB,EAAE,MAAc,EAAE,IAAW,EAAE,SAAmB;QACzF,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC;QAChB,MAAM,MAAM,GAAG,CAAC,CAAC;QACjB,IAAI,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAE3C,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAE9E,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAE3D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAE5D,qFAAqF;QACrF,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAErC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QAErD,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAEO,aAAa,CAAC,OAAmB,EAAE,MAAc,EAAE,EAAM,EAAE,SAAmB,EAAE,OAAe;QACrG,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAE5D,6FAA6F;QAC7F,uFAAuF;QACvF,kEAAkE;QAClE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAEzE,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAE9C,0FAA0F;QAC1F,8FAA8F;QAC9F,+FAA+F;QAC/F,8FAA8F;QAC9F,gGAAgG;QAChG,MAAM,aAAa,GAAG,OAAO,QAAQ,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC,QAAQ,EAAE,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QACxG,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,IAAI,gBAAgB,CAAC,oBAAoB,CAAC;QAC/F,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAClD,IAAI,CAAC,WAAW,IAAI,aAAa,EAAE,CAAC;YAClC,IAAI,CACF,OAAO,EACP,QAAQ,EACR,4DAA4D,OAAO,2BAA2B,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAC7H,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,WAAW,IAAI,aAAa,CAAC;QAE7C,6FAA6F;QAC7F,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC3E,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QAEhE,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5C,yEAAyE;YACzE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,MAAM,IAAI,IAAI,CAAC,CAAC;YACzC,OAAO;QACT,CAAC;QAED,6FAA6F;QAC7F,6FAA6F;QAC7F,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,IAAI,MAAM,IAAI,KAAK,CAAC,CAAC;QACxD,IAAI,UAAU;YAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,WAAW;YAAE,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,OAAO,QAAQ,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACvC,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;YAChD,CAAC;iBAAM,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;gBACnB,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;IACpC,CAAC;IAED,yGAAyG;IACzG,iGAAiG;IACzF,aAAa,CAAC,EAAM,EAAE,QAA0B;QACtD,OAAO,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAC/E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAClE,CAAC;IACJ,CAAC;IAED,gGAAgG;IAChG,+FAA+F;IAC/F,wDAAwD;IAChD,gBAAgB,CACtB,OAAmB,EACnB,EAAM,EACN,QAAqC,EACrC,IAAsB;QAEtB,gFAAgF;QAChF,4DAA4D;QAC5D,IAAI,WAAW,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAQ,EAAE,EAAE;YAC9C,OAAO,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,mDAAmD;QACnD,IAAI,OAAO,CAAC,eAAe,EAAE,gBAAgB,EAAE,CAAC;YAC9C,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7D,CAAC;QAED,uFAAuF;QACvF,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CACjC,WAAW,EACX,QAAQ,EAAE,WAAW,IAAI,EAAE,EAC3B,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAC5D,CAAC;QAEF,8FAA8F;QAC9F,+FAA+F;QAC/F,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAa,CAAC,0BAA0B,CAAC,CAAC;QACrD,6FAA6F;QAC7F,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAChC,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;gBACtC,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,MAAM,KAAK,EAAE,CAAC,CAAC;YAChD,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5B,CAAC;QACD,gGAAgG;QAChG,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC1B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjC,CAAC;IAED,+FAA+F;IAC/F,6FAA6F;IACrF,YAAY,CAAC,EAAM,EAAE,QAAqC,EAAE,IAAsB;QACxF,mFAAmF;QACnF,MAAM,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,CAAC;QACpG,IAAI,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAElG,2FAA2F;QAC3F,8FAA8F;QAC9F,wEAAwE;QACxE,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAClF,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvE,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,GAAG,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAa,CAAC,oBAAoB,CAAC,CAAC;QAC/C,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,sBAAsB,IAAI,cAAc,KAAK,KAAK,CAAC,CAAC;QACjE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjC,CAAC;IAED,uFAAuF;IACvF,mEAAmE;IAC3D,cAAc,CACpB,QAAa,EACb,SAAwC,EACxC,aAAkC;QAElC,MAAM,IAAI,GAAG,QAAQ;aAClB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;aAC/C,GAAG,CAAC,CAAC,IAAI,EAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAEvG,yFAAyF;QACzF,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAgB,EAAE,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;gBACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,8FAA8F;IACtF,aAAa,CAAC,CAAkB;QACtC,IAAI,CAAC,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;YACtB,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,uDAAuD;IAC/C,SAAS,CAAC,CAAQ;QACxB,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAEO,cAAc,CAAC,OAAmB,EAAE,MAAc,EAAE,IAAW,EAAE,SAAmB;QAC1F,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,+FAA+F;IAC/F,gGAAgG;IAChG,mCAAmC;IAC3B,iBAAiB,CAAC,MAAc,EAAE,IAAY;QACpD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,aAAa,CAAC,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC;IAClC,CAAC;IAEO,kBAAkB,CAAC,OAAmB,EAAE,MAAc,EAAE,IAAU,EAAE,SAAmB;QAC7F,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;CACF","sourcesContent":["import _ from 'lodash';\nimport { ParameterObject } from 'oas/types';\nimport { OasContext, RequestOverride } from '../oasContext.js';\nimport { OasGen } from '../oasGen.js';\nimport { Body, IType, Op, Param, T } from '../nodes/internal.js';\nimport { Naming } from '../utils/naming.js';\nimport { Params } from '../utils/params.js';\nimport { ErrorsWriter } from './errorsWriter.js';\nimport { Writer } from './writer.js';\nimport { NameValue, SecurityPlan } from './security.js';\nimport { DEFAULT_VERSIONS, meetsMinimum } from '../../versions.js';\nimport { warn } from '../log/trace.js';\n\nexport class OperationWriter {\n private errorsWriter: ErrorsWriter;\n\n constructor(\n private gen: OasGen,\n private security: SecurityPlan,\n ) {\n this.errorsWriter = new ErrorsWriter(gen);\n }\n\n public writeQuery(context: OasContext, writer: Writer, collected: Map<string, IType>, selection: string[]): void {\n const selectionSet = new Set<string>(selection.map((s) => s.split('>')[0]));\n\n const paths = Array.from(collected.values()).filter((path) => selectionSet.has(path.id));\n if (_.isEmpty(paths)) return;\n\n writer.write('type Query {\\n');\n\n for (const path of paths) {\n path.generate(context, writer, []);\n this.writeConnector(context, writer, path, selection);\n context.generatedSet.add(path.id);\n }\n\n writer.write('}\\n\\n');\n }\n\n public writeMutations(context: OasContext, writer: Writer, collected: Map<string, IType>, selection: string[]): void {\n const selectionSet = new Set<string>(selection.map((s) => s.split('>')[0]));\n\n const paths = Array.from(collected.values()).filter((path) => selectionSet.has(path.id));\n if (_.isEmpty(paths)) return;\n\n writer.write('type Mutation {\\n');\n\n for (const path of paths) {\n path.generate(context, writer, []);\n this.writeConnector(context, writer, path, selection);\n context.generatedSet.add(path.id);\n }\n\n writer.write('}\\n\\n');\n }\n\n public writeConnector(context: OasContext, writer: Writer, type: IType, selection: string[]): void {\n if (!T.isOp(type)) {\n throw new Error(`expected an operation node, got ${type.id}`);\n }\n\n const op = type;\n const indent = 0;\n let spacing = ' '.repeat(indent + 4);\n writer.write(spacing).write('@connect(\\n');\n\n spacing = ' '.repeat(indent + 6);\n writer.write(spacing).write('source: \"api\"\\n').write(spacing).write('http: ');\n\n this.requestMethod(context, writer, op, selection, indent);\n\n writer.write('\\n').write(spacing).write('selection: \"\"\"\\n');\n\n // truthiness, not _.has — the declared-but-unset field is still an own property. #33\n if (op.resultType) {\n this.writeSelection(context, writer, op.resultType, selection);\n }\n\n writer.write(spacing).write('\"\"\"\\n');\n\n this.errorsWriter.write(context, writer, op, indent);\n\n spacing = ' '.repeat(indent + 4);\n writer.write(spacing).write(')\\n');\n }\n\n private requestMethod(context: OasContext, writer: Writer, op: Op, selection: string[], _indent: number): void {\n const override = context.generateOptions.overrides?.[op.id];\n\n // R5: this op's resolved auth, split by placement. A header lives on @connect only in per-op\n // mode (uniform mode puts it on @source); query auth always lives here (@source has no\n // queryParams). Warnings are owned by the plan — see security.ts.\n const { header: headerAuth, query: queryAuth } = this.security.forOp(op);\n\n const path = this.templatedPath(op, override);\n\n // body is emitted last; its presence is cheap to compute up front. the body block streams\n // through the node tree (Body.select), so unlike query/headers it writes to `writer` directly\n // instead of returning a string. `null` drops the body; a string overrides it; see ROADMAP R9.\n // C2: `body:` is a connect v0.2+ feature — gate it like errors (R4) and batch (R6). Below the\n // floor, drop the body and warn once per op (do not emit invalid output for an ancient target).\n const wouldEmitBody = typeof override?.body === 'string' || (override?.body === undefined && !!op.body);\n const version = this.gen.options.connectorSpecVersion || DEFAULT_VERSIONS.connectorSpecVersion;\n const bodyAllowed = meetsMinimum(version, 'v0.2');\n if (!bodyAllowed && wouldEmitBody) {\n warn(\n context,\n '[body]',\n `@connect(http.body) requires connect v0.2, but target is ${version} — body not emitted for ${op.verb} ${op.operation.path}`,\n );\n }\n const hasBody = bodyAllowed && wouldEmitBody;\n\n // query & header blocks are pure string-building — they return the block, or null when empty\n const queryBlock = this.queryParamsBlock(context, op, override, queryAuth);\n const headerBlock = this.headersBlock(op, override, headerAuth);\n\n if (!queryBlock && !headerBlock && !hasBody) {\n // nothing but the path: keep the compact single-line form `{ GET: \"/x\"}`\n writer.write(`{ ${op.verb}: \"${path}\"}`);\n return;\n }\n\n // expanded: `http: {` on its own line, the verb and each member at indent 8, the closing `}`\n // aligned under `http:` (indent 6). members are comma-less (GraphQL multiline object style).\n writer.write('{\\n');\n writer.write(' '.repeat(8) + `${op.verb}: \"${path}\"\\n`);\n if (queryBlock) writer.write(queryBlock);\n if (headerBlock) writer.write(headerBlock);\n if (hasBody) {\n if (typeof override?.body === 'string') {\n this.writeBodyOverride(writer, override.body);\n } else if (op.body) {\n this.writeBodySelection(context, writer, op.body, selection);\n }\n }\n writer.write(' '.repeat(6) + '}');\n }\n\n // template each {elem} as {$args.<sanitised>} (the arg name), not the raw OAS key. see docs/issues.md #2\n // an override path may already template (`{$args.id}`, `{$config.v}`) — leave `$` segments alone\n private templatedPath(op: Op, override?: RequestOverride): string {\n return (override?.path ?? op.operation.path).replace(/\\{([^}]+)\\}/g, (m, name) =>\n name.startsWith('$') ? m : `{$args.${Naming.genParamName(name)}}`,\n );\n }\n\n // the op's queryParams block (a self-contained string ending in a newline), or null when the op\n // has no query params and no apiKey-in-query auth. `auth` is the op's resolved apiKey-in-query\n // entry (or null) — see requestMethod / security.forOp.\n private queryParamsBlock(\n context: OasContext,\n op: Op,\n override: RequestOverride | undefined,\n auth: NameValue | null,\n ): string | null {\n // we now include all query params, not just required ones. if they are not set,\n // then the connectors will not include them in the request.\n let queryParams = op.params.filter((p: Param) => {\n return p.parameter.in && p.parameter.in.toLowerCase() === 'query';\n });\n\n // Skip optional params if skipOptionalArgs is true\n if (context.generateOptions?.skipOptionalArgs) {\n queryParams = queryParams.filter((p: Param) => p.required);\n }\n\n // e.g. `\"api-version\": $('2024-01')` appended, `\"ids\": ids->joinNotNull(\";\")` replaced\n const entries = this.mergeOverrides(\n queryParams,\n override?.queryParams ?? {},\n (p) => `${Naming.genParamName(p.name)}${this.arrayJoin(p)}`,\n );\n\n // R5 slice 3: apiKey-in-query auth merges into the same query-param object as a JSONSelection\n // sibling outside the `$args { … }` block, so an auth-only op still emits a queryParams block.\n if (entries.length === 0 && !auth) {\n return null;\n }\n\n const lines: string[] = [' queryParams: \"\"\"'];\n // the `$args { … }` block only when there are arg-derived params (skipped for auth-only ops)\n if (entries.length > 0) {\n lines.push(' $args {');\n for (const { name, value } of entries) {\n lines.push(` \"${name}\": ${value}`);\n }\n lines.push(' }');\n }\n // e.g. `\"api_key\": $config.apiKey` — key quoted so non-identifier names like `api-key` are safe\n if (auth) {\n lines.push(` \"${auth.name}\": ${auth.value}`);\n }\n lines.push(' \"\"\"');\n return lines.join('\\n') + '\\n';\n }\n\n // the op's headers block (a self-contained string ending in a newline), or null when there are\n // no headers to send. `auth` is the op's resolved auth header (or null) — see requestMethod.\n private headersBlock(op: Op, override: RequestOverride | undefined, auth: NameValue | null): string | null {\n // OAS `header` params, with user overrides merged in (string replaces, null drops)\n const headers = op.operation.getParameters().filter((p) => p.in && p.in.toLowerCase() === 'header');\n let entries = this.mergeOverrides(headers, override?.headers ?? {}, (p) => this.headerExample(p));\n\n // HTTP header names are case-insensitive: an explicit user override of the same name wins;\n // otherwise the resolved auth replaces any inferred OAS header of that name (so we never emit\n // a real credential alongside a placeholder that differs only in case).\n if (auth) {\n const sameName = (name: string) => name.toLowerCase() === auth.name.toLowerCase();\n const overridden = Object.keys(override?.headers ?? {}).some(sameName);\n if (!overridden) {\n entries = [auth, ...entries.filter((entry) => !sameName(entry.name))];\n }\n }\n\n if (entries.length === 0) {\n return null;\n }\n\n const lines: string[] = [' headers: ['];\n for (const { name, value } of entries) {\n lines.push(` { name: \"${name}\", value: \"${value}\" }`);\n }\n lines.push(' ]');\n return lines.join('\\n') + '\\n';\n }\n\n // merge user overrides over the inferred params: a string replaces the inferred value,\n // null drops the entry, an unknown key is appended. see ROADMAP R8\n private mergeOverrides<T extends { name: string }>(\n inferred: T[],\n overrides: Record<string, string | null>,\n inferredValue: (item: T) => string,\n ): NameValue[] {\n const kept = inferred\n .filter((item) => overrides[item.name] !== null)\n .map((item): NameValue => ({ name: item.name, value: overrides[item.name] ?? inferredValue(item) }));\n\n // overrides whose key isn't an inferred param are appended (null already excluded above)\n const known = new Set(inferred.map((item) => item.name));\n const appended: NameValue[] = [];\n for (const [name, value] of Object.entries(overrides)) {\n if (!known.has(name) && value != null) {\n appended.push({ name, value });\n }\n }\n\n return [...kept, ...appended];\n }\n\n // inferred header value: the example, the example keys, or a placeholder for the user to fill\n private headerExample(p: ParameterObject): string {\n if (p.example != null) {\n return p.example.toString();\n }\n if (p.examples && Object.keys(p.examples).length > 0) {\n return Object.keys(p.examples).join(',');\n }\n return '<placeholder>';\n }\n\n // see Params.arrayJoin (shared with the R6 batch path)\n private arrayJoin(p: Param): string {\n return Params.arrayJoin(p.parameter);\n }\n\n private writeSelection(context: OasContext, writer: Writer, type: IType, selection: string[]): void {\n context.indent = 6;\n type.select(context, writer, selection);\n }\n\n // mirrors Body.select formatting, but the user's raw JSONSelection replaces the whole mapping.\n // emits the block at indent 8 (an http-object member), ending with a newline; the caller places\n // the http object's closing brace.\n private writeBodyOverride(writer: Writer, body: string): void {\n const spacing = ' '.repeat(8);\n writer.write(spacing + 'body: \"\"\"\\n');\n for (const line of body.split('\\n')) {\n writer.write(spacing + ' ' + line + '\\n');\n }\n writer.write(spacing + '\"\"\"\\n');\n }\n\n private writeBodySelection(context: OasContext, writer: Writer, body: Body, selection: string[]): void {\n context.indent = 8;\n body.select(context, writer, selection);\n }\n}\n"]}
|
|
@@ -14,11 +14,17 @@ export interface NameValue {
|
|
|
14
14
|
* each `@connect`. Query auth always lives per-`@connect` — `SourceHTTP` has no `queryParams`.
|
|
15
15
|
*/
|
|
16
16
|
export declare class SecurityPlan {
|
|
17
|
+
private readonly skipAuth;
|
|
17
18
|
private readonly schemes;
|
|
18
19
|
private readonly globalReq;
|
|
19
20
|
private readonly perOpMode;
|
|
21
|
+
private readonly globalWarningBodies;
|
|
22
|
+
private globalWarningsEmitted;
|
|
20
23
|
private constructor();
|
|
21
|
-
static from(api: Oas
|
|
24
|
+
static from(api: Oas, opts?: {
|
|
25
|
+
skipAuth?: boolean;
|
|
26
|
+
}): SecurityPlan;
|
|
27
|
+
private emitGlobalWarningsOnce;
|
|
22
28
|
/** The `@source`-level auth header literal (uniform mode + header kind), or null. */
|
|
23
29
|
sourceHeader(): string | null;
|
|
24
30
|
/**
|
|
@@ -27,7 +33,9 @@ export declare class SecurityPlan {
|
|
|
27
33
|
* - `query`: the query entry, always (SourceHTTP has no queryParams), else null
|
|
28
34
|
*
|
|
29
35
|
* Per-op warnings are emitted here with op context; in uniform mode the global warnings were
|
|
30
|
-
* already emitted once by sourceHeader.
|
|
36
|
+
* already emitted once by sourceHeader. C3: in per-op mode, the global's dropped-scheme
|
|
37
|
+
* warnings are emitted exactly once (via emitGlobalWarningsOnce) — only genuinely op-specific
|
|
38
|
+
* warnings carry the `(operation …)` suffix.
|
|
31
39
|
*/
|
|
32
40
|
forOp(op: Op): {
|
|
33
41
|
header: NameValue | null;
|
package/dist/oas/io/security.js
CHANGED
|
@@ -99,24 +99,45 @@ function hasPerOperationSecurity(def) {
|
|
|
99
99
|
* each `@connect`. Query auth always lives per-`@connect` — `SourceHTTP` has no `queryParams`.
|
|
100
100
|
*/
|
|
101
101
|
export class SecurityPlan {
|
|
102
|
+
skipAuth;
|
|
102
103
|
schemes;
|
|
103
104
|
globalReq;
|
|
104
105
|
perOpMode;
|
|
105
|
-
|
|
106
|
+
// C3: the global requirement's dropped-scheme warnings, pre-resolved once. In per-op mode every
|
|
107
|
+
// inheriting op would re-fire them otherwise; this set identifies them so forOp can skip what's
|
|
108
|
+
// already been emitted (once) and only surface genuinely op-specific warnings with op context.
|
|
109
|
+
globalWarningBodies;
|
|
110
|
+
globalWarningsEmitted = false;
|
|
111
|
+
constructor(schemes, globalReq, perOpMode, skipAuth = false) {
|
|
112
|
+
this.skipAuth = skipAuth;
|
|
106
113
|
this.schemes = schemes;
|
|
107
114
|
this.globalReq = globalReq;
|
|
108
115
|
this.perOpMode = perOpMode;
|
|
116
|
+
this.globalWarningBodies = new Set(globalReq ? resolveAuth(globalReq, schemes).warnings : []);
|
|
109
117
|
}
|
|
110
|
-
static from(api) {
|
|
118
|
+
static from(api, opts) {
|
|
111
119
|
const def = api.getDefinition();
|
|
112
120
|
// oas-normalize usually up-converts Swagger 2.0 → components.securitySchemes; the
|
|
113
121
|
// securityDefinitions fallback (same shape) is a safety net. refs are dereferenced by oas,
|
|
114
122
|
// so the shape is a plain SecuritySchemesObject.
|
|
115
123
|
const schemes = (def.components?.securitySchemes ?? def.securityDefinitions ?? {});
|
|
116
|
-
return new SecurityPlan(schemes, def.security, hasPerOperationSecurity(def));
|
|
124
|
+
return new SecurityPlan(schemes, def.security, hasPerOperationSecurity(def), opts?.skipAuth ?? false);
|
|
125
|
+
}
|
|
126
|
+
// C3: emit the global's dropped-scheme warnings exactly once per generation. Called from
|
|
127
|
+
// sourceHeader (uniform mode) and the first forOp (per-op mode) — both run before the warnings
|
|
128
|
+
// would otherwise leak per-op. Idempotent via `globalWarningsEmitted`.
|
|
129
|
+
emitGlobalWarningsOnce() {
|
|
130
|
+
if (this.globalWarningsEmitted)
|
|
131
|
+
return;
|
|
132
|
+
this.globalWarningsEmitted = true;
|
|
133
|
+
for (const body of this.globalWarningBodies)
|
|
134
|
+
console.warn(body);
|
|
117
135
|
}
|
|
118
136
|
/** The `@source`-level auth header literal (uniform mode + header kind), or null. */
|
|
119
137
|
sourceHeader() {
|
|
138
|
+
// --skip-auth: emit no auth anywhere — no header on @source (and forOp returns nothing too)
|
|
139
|
+
if (this.skipAuth)
|
|
140
|
+
return null;
|
|
120
141
|
// per-op mode: each @connect carries its own auth — nothing on @source
|
|
121
142
|
if (this.perOpMode)
|
|
122
143
|
return null;
|
|
@@ -124,9 +145,10 @@ export class SecurityPlan {
|
|
|
124
145
|
if (!this.globalReq || this.globalReq.length === 0)
|
|
125
146
|
return null;
|
|
126
147
|
const { auth, warnings } = resolveAuth(this.globalReq, this.schemes);
|
|
127
|
-
// surface
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
// C3: surface the global's dropped-scheme warnings exactly once (uniform mode; per-op mode
|
|
149
|
+
// emits them from the first forOp).
|
|
150
|
+
this.emitGlobalWarningsOnce();
|
|
151
|
+
void warnings; // already accounted for via globalWarningBodies
|
|
130
152
|
// @source carries only header auth; query auth lives on each @connect (SourceHTTP has no
|
|
131
153
|
// queryParams) and is returned by forOp — emit nothing here.
|
|
132
154
|
if (auth?.kind !== 'header') {
|
|
@@ -144,16 +166,27 @@ export class SecurityPlan {
|
|
|
144
166
|
* - `query`: the query entry, always (SourceHTTP has no queryParams), else null
|
|
145
167
|
*
|
|
146
168
|
* Per-op warnings are emitted here with op context; in uniform mode the global warnings were
|
|
147
|
-
* already emitted once by sourceHeader.
|
|
169
|
+
* already emitted once by sourceHeader. C3: in per-op mode, the global's dropped-scheme
|
|
170
|
+
* warnings are emitted exactly once (via emitGlobalWarningsOnce) — only genuinely op-specific
|
|
171
|
+
* warnings carry the `(operation …)` suffix.
|
|
148
172
|
*/
|
|
149
173
|
forOp(op) {
|
|
174
|
+
// --skip-auth: no per-@connect auth either (companion to sourceHeader's skip)
|
|
175
|
+
if (this.skipAuth)
|
|
176
|
+
return { header: null, query: null };
|
|
150
177
|
// `??` not `||`: an explicit `security: []` (public) must NOT fall back to the global.
|
|
151
178
|
const effective = op.operation.schema.security ?? this.globalReq;
|
|
152
179
|
const { auth, warnings } = resolveAuth(effective, this.schemes);
|
|
153
180
|
if (this.perOpMode) {
|
|
181
|
+
// surface the global's dropped-scheme warnings exactly once (idempotent), then warn only
|
|
182
|
+
// about op-specific drops with the op's location attached.
|
|
183
|
+
this.emitGlobalWarningsOnce();
|
|
154
184
|
const where = `${op.verb} ${op.operation.path}`;
|
|
155
|
-
for (const w of warnings)
|
|
156
|
-
|
|
185
|
+
for (const w of warnings) {
|
|
186
|
+
if (!this.globalWarningBodies.has(w)) {
|
|
187
|
+
console.warn(`${w} (operation ${where})`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
157
190
|
}
|
|
158
191
|
return {
|
|
159
192
|
header: this.perOpMode && auth?.kind === 'header' ? auth : null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/oas/io/security.ts"],"names":[],"mappings":"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","sourcesContent":["import _ from 'lodash';\nimport type Oas from 'oas';\nimport { SecurityRequirementObject, SecuritySchemeObject, SecuritySchemesObject } from 'oas/types';\nimport type { Op } from '../nodes/internal.js';\n\n// A connector name/value entry: a header (`{ name, value }`) or a query param (`\"name\": value`).\n// Also the shape mergeOverrides produces for inferred/overridden header & query params.\nexport interface NameValue {\n name: string;\n value: string;\n}\n\n// One resolved auth credential: a NameValue plus where it goes. Header values carry `{}`\n// interpolation (`Bearer {$config.token}`); a query value is a raw JSONSelection (`$config.apiKey`).\ninterface AuthEntry extends NameValue {\n kind: 'header' | 'query';\n}\n\n// The HTTP methods the generator actually emits (see OasGen.isSupported). Scanning only these for\n// the per-op-mode switch keeps the decision consistent with what gets written — a `security` on an\n// un-emitted HEAD/OPTIONS op no longer flips the whole spec into per-op mode.\nconst SUPPORTED_METHODS: readonly string[] = ['get', 'post', 'put', 'patch', 'delete'];\n\n/**\n * Map one OAS security scheme to its connector auth entry, or null when it makes none.\n * Header values carry `{}` interpolation; a query value is a raw JSONSelection (no braces) because\n * it lands in a `queryParams: \"\"\"…\"\"\"` block, which is already a JSONSelection context.\n */\nfunction mapSchemeToAuth(scheme: SecuritySchemeObject): AuthEntry | null {\n switch (scheme.type) {\n case 'apiKey':\n // apiKey in a header, e.g. `{ type: apiKey, in: header, name: X-API-Key }`\n if (scheme.in === 'header' && scheme.name) {\n return { kind: 'header', name: scheme.name, value: '{$config.apiKey}' };\n }\n // apiKey in the query string, e.g. `{ type: apiKey, in: query, name: api_key }`\n if (scheme.in === 'query' && scheme.name) {\n return { kind: 'query', name: scheme.name, value: '$config.apiKey' };\n }\n return null; // cookie is deferred and warned about, not emitted\n case 'http':\n if (scheme.scheme === 'bearer') {\n return { kind: 'header', name: 'Authorization', value: 'Bearer {$config.token}' };\n }\n if (scheme.scheme === 'basic') {\n return { kind: 'header', name: 'Authorization', value: 'Basic {$config.token}' };\n }\n return null;\n case 'oauth2':\n case 'openIdConnect':\n // both flows ultimately send a bearer token\n return { kind: 'header', name: 'Authorization', value: 'Bearer {$config.token}' };\n default:\n return null;\n }\n}\n\n/** The warning text for a referenced scheme we did not emit (undefined, deferred, or not the winner). */\nfunction droppedSchemeWarning(name: string, scheme: SecuritySchemeObject | undefined): string {\n if (!scheme) {\n return `[security] scheme \"${name}\" is referenced but not defined; skipped.`;\n }\n if (scheme.type === 'apiKey' && scheme.in === 'cookie') {\n return `[security] scheme \"${name}\" is apiKey in cookie — not emitted (cookie auth deferred).`;\n }\n return `[security] scheme \"${name}\" (${scheme.type}) has no supported connector mapping; skipped.`;\n}\n\n/**\n * Resolve a security requirement to the single auth entry the connector should send.\n *\n * A requirement is an OR of scheme-name sets (`[{ a: [] }, { b: [] }]` = \"a OR b\"). We flatten to\n * names in order and pick the FIRST that maps to an entry. A later viable alternative is a\n * legitimate OR choice, not a failure, so it stays silent; only genuinely unresolvable names —\n * undefined, deferred (cookie), or an unmappable scheme — are reported in `warnings`. An absent or\n * empty (`[]`, public) requirement yields no entry and no warnings.\n */\nfunction resolveAuth(\n securityRequirements: SecurityRequirementObject[] | undefined,\n schemes: SecuritySchemesObject,\n): { auth: AuthEntry | null; warnings: string[] } {\n let auth: AuthEntry | null = null;\n const warnings: string[] = [];\n\n for (const name of securityRequirements?.flatMap((requirement) => Object.keys(requirement)) ?? []) {\n const scheme = schemes[name];\n const entry = scheme ? mapSchemeToAuth(scheme) : null;\n\n if (entry) {\n // first scheme that maps wins; a later viable alternative is a silent OR choice\n if (!auth) auth = entry;\n } else {\n // genuinely unresolvable: undefined scheme, cookie, or no supported mapping\n warnings.push(droppedSchemeWarning(name, scheme));\n }\n }\n\n return { auth, warnings };\n}\n\n/** True when ANY emitted operation declares its own `security` (empty `[]` or non-empty). */\nfunction hasPerOperationSecurity(def: ReturnType<Oas['getDefinition']>): boolean {\n const paths = def.paths ?? {};\n for (const pathName of Object.keys(paths)) {\n const path = paths[pathName];\n if (!path) continue;\n for (const method of SUPPORTED_METHODS) {\n // own property (not inherited) — an explicit `security: []` is still a declaration\n if (_.has(path, [method, 'security'])) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Resolved security for one generation pass: computed once from the parsed spec, then queried for\n * `@source` (sourceHeader) and per-`@connect` (forOp). Owning all auth warnings here removes the old\n * uniform-vs-per-op \"who warns when\" coordination between SchemaWriter and OperationWriter.\n *\n * Mode switch: when any emitted op declares its own `security`, a shared `@source` header is unsafe\n * (a public op would leak it; a differently-named override would send both), so header auth moves to\n * each `@connect`. Query auth always lives per-`@connect` — `SourceHTTP` has no `queryParams`.\n */\nexport class SecurityPlan {\n private readonly schemes: SecuritySchemesObject;\n private readonly globalReq: SecurityRequirementObject[] | undefined;\n private readonly perOpMode: boolean;\n\n private constructor(\n schemes: SecuritySchemesObject,\n globalReq: SecurityRequirementObject[] | undefined,\n perOpMode: boolean,\n ) {\n this.schemes = schemes;\n this.globalReq = globalReq;\n this.perOpMode = perOpMode;\n }\n\n static from(api: Oas): SecurityPlan {\n const def = api.getDefinition();\n // oas-normalize usually up-converts Swagger 2.0 → components.securitySchemes; the\n // securityDefinitions fallback (same shape) is a safety net. refs are dereferenced by oas,\n // so the shape is a plain SecuritySchemesObject.\n const schemes = (def.components?.securitySchemes ?? def.securityDefinitions ?? {}) as SecuritySchemesObject;\n return new SecurityPlan(schemes, def.security, hasPerOperationSecurity(def));\n }\n\n /** The `@source`-level auth header literal (uniform mode + header kind), or null. */\n sourceHeader(): string | null {\n // per-op mode: each @connect carries its own auth — nothing on @source\n if (this.perOpMode) return null;\n // no global requirement -> keep the headerless @source byte-for-byte\n if (!this.globalReq || this.globalReq.length === 0) return null;\n\n const { auth, warnings } = resolveAuth(this.globalReq, this.schemes);\n // surface every dropped scheme loudly, exactly once\n for (const w of warnings) console.warn(w);\n\n // @source carries only header auth; query auth lives on each @connect (SourceHTTP has no\n // queryParams) and is returned by forOp — emit nothing here.\n if (auth?.kind !== 'header') {\n if (!auth) {\n const names = this.globalReq.flatMap((req) => Object.keys(req));\n console.warn(\n `[security] global security requirement (${names.join(', ')}) maps to no header-producing scheme; no auth header emitted on @source.`,\n );\n }\n return null;\n }\n\n return `{ name: \"${auth.name}\", value: \"${auth.value}\" }`;\n }\n\n /**\n * This op's resolved auth, split by placement.\n * - `header`: the header entry in per-op mode (uniform mode puts it on `@source`), else null\n * - `query`: the query entry, always (SourceHTTP has no queryParams), else null\n *\n * Per-op warnings are emitted here with op context; in uniform mode the global warnings were\n * already emitted once by sourceHeader.\n */\n forOp(op: Op): { header: NameValue | null; query: NameValue | null } {\n // `??` not `||`: an explicit `security: []` (public) must NOT fall back to the global.\n const effective = op.operation.schema.security ?? this.globalReq;\n const { auth, warnings } = resolveAuth(effective, this.schemes);\n\n if (this.perOpMode) {\n const where = `${op.verb} ${op.operation.path}`;\n for (const w of warnings) console.warn(`${w} (operation ${where})`);\n }\n\n return {\n header: this.perOpMode && auth?.kind === 'header' ? auth : null,\n query: auth?.kind === 'query' ? auth : null,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/oas/io/security.ts"],"names":[],"mappings":"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","sourcesContent":["import _ from 'lodash';\nimport type Oas from 'oas';\nimport { SecurityRequirementObject, SecuritySchemeObject, SecuritySchemesObject } from 'oas/types';\nimport type { Op } from '../nodes/internal.js';\n\n// A connector name/value entry: a header (`{ name, value }`) or a query param (`\"name\": value`).\n// Also the shape mergeOverrides produces for inferred/overridden header & query params.\nexport interface NameValue {\n name: string;\n value: string;\n}\n\n// One resolved auth credential: a NameValue plus where it goes. Header values carry `{}`\n// interpolation (`Bearer {$config.token}`); a query value is a raw JSONSelection (`$config.apiKey`).\ninterface AuthEntry extends NameValue {\n kind: 'header' | 'query';\n}\n\n// The HTTP methods the generator actually emits (see OasGen.isSupported). Scanning only these for\n// the per-op-mode switch keeps the decision consistent with what gets written — a `security` on an\n// un-emitted HEAD/OPTIONS op no longer flips the whole spec into per-op mode.\nconst SUPPORTED_METHODS: readonly string[] = ['get', 'post', 'put', 'patch', 'delete'];\n\n/**\n * Map one OAS security scheme to its connector auth entry, or null when it makes none.\n * Header values carry `{}` interpolation; a query value is a raw JSONSelection (no braces) because\n * it lands in a `queryParams: \"\"\"…\"\"\"` block, which is already a JSONSelection context.\n */\nfunction mapSchemeToAuth(scheme: SecuritySchemeObject): AuthEntry | null {\n switch (scheme.type) {\n case 'apiKey':\n // apiKey in a header, e.g. `{ type: apiKey, in: header, name: X-API-Key }`\n if (scheme.in === 'header' && scheme.name) {\n return { kind: 'header', name: scheme.name, value: '{$config.apiKey}' };\n }\n // apiKey in the query string, e.g. `{ type: apiKey, in: query, name: api_key }`\n if (scheme.in === 'query' && scheme.name) {\n return { kind: 'query', name: scheme.name, value: '$config.apiKey' };\n }\n return null; // cookie is deferred and warned about, not emitted\n case 'http':\n if (scheme.scheme === 'bearer') {\n return { kind: 'header', name: 'Authorization', value: 'Bearer {$config.token}' };\n }\n if (scheme.scheme === 'basic') {\n return { kind: 'header', name: 'Authorization', value: 'Basic {$config.token}' };\n }\n return null;\n case 'oauth2':\n case 'openIdConnect':\n // both flows ultimately send a bearer token\n return { kind: 'header', name: 'Authorization', value: 'Bearer {$config.token}' };\n default:\n return null;\n }\n}\n\n/** The warning text for a referenced scheme we did not emit (undefined, deferred, or not the winner). */\nfunction droppedSchemeWarning(name: string, scheme: SecuritySchemeObject | undefined): string {\n if (!scheme) {\n return `[security] scheme \"${name}\" is referenced but not defined; skipped.`;\n }\n if (scheme.type === 'apiKey' && scheme.in === 'cookie') {\n return `[security] scheme \"${name}\" is apiKey in cookie — not emitted (cookie auth deferred).`;\n }\n return `[security] scheme \"${name}\" (${scheme.type}) has no supported connector mapping; skipped.`;\n}\n\n/**\n * Resolve a security requirement to the single auth entry the connector should send.\n *\n * A requirement is an OR of scheme-name sets (`[{ a: [] }, { b: [] }]` = \"a OR b\"). We flatten to\n * names in order and pick the FIRST that maps to an entry. A later viable alternative is a\n * legitimate OR choice, not a failure, so it stays silent; only genuinely unresolvable names —\n * undefined, deferred (cookie), or an unmappable scheme — are reported in `warnings`. An absent or\n * empty (`[]`, public) requirement yields no entry and no warnings.\n */\nfunction resolveAuth(\n securityRequirements: SecurityRequirementObject[] | undefined,\n schemes: SecuritySchemesObject,\n): { auth: AuthEntry | null; warnings: string[] } {\n let auth: AuthEntry | null = null;\n const warnings: string[] = [];\n\n for (const name of securityRequirements?.flatMap((requirement) => Object.keys(requirement)) ?? []) {\n const scheme = schemes[name];\n const entry = scheme ? mapSchemeToAuth(scheme) : null;\n\n if (entry) {\n // first scheme that maps wins; a later viable alternative is a silent OR choice\n if (!auth) auth = entry;\n } else {\n // genuinely unresolvable: undefined scheme, cookie, or no supported mapping\n warnings.push(droppedSchemeWarning(name, scheme));\n }\n }\n\n return { auth, warnings };\n}\n\n/** True when ANY emitted operation declares its own `security` (empty `[]` or non-empty). */\nfunction hasPerOperationSecurity(def: ReturnType<Oas['getDefinition']>): boolean {\n const paths = def.paths ?? {};\n for (const pathName of Object.keys(paths)) {\n const path = paths[pathName];\n if (!path) continue;\n for (const method of SUPPORTED_METHODS) {\n // own property (not inherited) — an explicit `security: []` is still a declaration\n if (_.has(path, [method, 'security'])) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Resolved security for one generation pass: computed once from the parsed spec, then queried for\n * `@source` (sourceHeader) and per-`@connect` (forOp). Owning all auth warnings here removes the old\n * uniform-vs-per-op \"who warns when\" coordination between SchemaWriter and OperationWriter.\n *\n * Mode switch: when any emitted op declares its own `security`, a shared `@source` header is unsafe\n * (a public op would leak it; a differently-named override would send both), so header auth moves to\n * each `@connect`. Query auth always lives per-`@connect` — `SourceHTTP` has no `queryParams`.\n */\nexport class SecurityPlan {\n private readonly schemes: SecuritySchemesObject;\n private readonly globalReq: SecurityRequirementObject[] | undefined;\n private readonly perOpMode: boolean;\n // C3: the global requirement's dropped-scheme warnings, pre-resolved once. In per-op mode every\n // inheriting op would re-fire them otherwise; this set identifies them so forOp can skip what's\n // already been emitted (once) and only surface genuinely op-specific warnings with op context.\n private readonly globalWarningBodies: Set<string>;\n private globalWarningsEmitted = false;\n\n private constructor(\n schemes: SecuritySchemesObject,\n globalReq: SecurityRequirementObject[] | undefined,\n perOpMode: boolean,\n private readonly skipAuth: boolean = false,\n ) {\n this.schemes = schemes;\n this.globalReq = globalReq;\n this.perOpMode = perOpMode;\n this.globalWarningBodies = new Set(globalReq ? resolveAuth(globalReq, schemes).warnings : []);\n }\n\n static from(api: Oas, opts?: { skipAuth?: boolean }): SecurityPlan {\n const def = api.getDefinition();\n // oas-normalize usually up-converts Swagger 2.0 → components.securitySchemes; the\n // securityDefinitions fallback (same shape) is a safety net. refs are dereferenced by oas,\n // so the shape is a plain SecuritySchemesObject.\n const schemes = (def.components?.securitySchemes ?? def.securityDefinitions ?? {}) as SecuritySchemesObject;\n return new SecurityPlan(schemes, def.security, hasPerOperationSecurity(def), opts?.skipAuth ?? false);\n }\n\n // C3: emit the global's dropped-scheme warnings exactly once per generation. Called from\n // sourceHeader (uniform mode) and the first forOp (per-op mode) — both run before the warnings\n // would otherwise leak per-op. Idempotent via `globalWarningsEmitted`.\n private emitGlobalWarningsOnce(): void {\n if (this.globalWarningsEmitted) return;\n this.globalWarningsEmitted = true;\n for (const body of this.globalWarningBodies) console.warn(body);\n }\n\n /** The `@source`-level auth header literal (uniform mode + header kind), or null. */\n sourceHeader(): string | null {\n // --skip-auth: emit no auth anywhere — no header on @source (and forOp returns nothing too)\n if (this.skipAuth) return null;\n // per-op mode: each @connect carries its own auth — nothing on @source\n if (this.perOpMode) return null;\n // no global requirement -> keep the headerless @source byte-for-byte\n if (!this.globalReq || this.globalReq.length === 0) return null;\n\n const { auth, warnings } = resolveAuth(this.globalReq, this.schemes);\n // C3: surface the global's dropped-scheme warnings exactly once (uniform mode; per-op mode\n // emits them from the first forOp).\n this.emitGlobalWarningsOnce();\n void warnings; // already accounted for via globalWarningBodies\n\n // @source carries only header auth; query auth lives on each @connect (SourceHTTP has no\n // queryParams) and is returned by forOp — emit nothing here.\n if (auth?.kind !== 'header') {\n if (!auth) {\n const names = this.globalReq.flatMap((req) => Object.keys(req));\n console.warn(\n `[security] global security requirement (${names.join(', ')}) maps to no header-producing scheme; no auth header emitted on @source.`,\n );\n }\n return null;\n }\n\n return `{ name: \"${auth.name}\", value: \"${auth.value}\" }`;\n }\n\n /**\n * This op's resolved auth, split by placement.\n * - `header`: the header entry in per-op mode (uniform mode puts it on `@source`), else null\n * - `query`: the query entry, always (SourceHTTP has no queryParams), else null\n *\n * Per-op warnings are emitted here with op context; in uniform mode the global warnings were\n * already emitted once by sourceHeader. C3: in per-op mode, the global's dropped-scheme\n * warnings are emitted exactly once (via emitGlobalWarningsOnce) — only genuinely op-specific\n * warnings carry the `(operation …)` suffix.\n */\n forOp(op: Op): { header: NameValue | null; query: NameValue | null } {\n // --skip-auth: no per-@connect auth either (companion to sourceHeader's skip)\n if (this.skipAuth) return { header: null, query: null };\n // `??` not `||`: an explicit `security: []` (public) must NOT fall back to the global.\n const effective = op.operation.schema.security ?? this.globalReq;\n const { auth, warnings } = resolveAuth(effective, this.schemes);\n\n if (this.perOpMode) {\n // surface the global's dropped-scheme warnings exactly once (idempotent), then warn only\n // about op-specific drops with the op's location attached.\n this.emitGlobalWarningsOnce();\n const where = `${op.verb} ${op.operation.path}`;\n for (const w of warnings) {\n if (!this.globalWarningBodies.has(w)) {\n console.warn(`${w} (operation ${where})`);\n }\n }\n }\n\n return {\n header: this.perOpMode && auth?.kind === 'header' ? auth : null,\n query: auth?.kind === 'query' ? auth : null,\n };\n }\n}\n"]}
|
package/dist/oas/io/writer.js
CHANGED
|
@@ -19,7 +19,7 @@ export class Writer {
|
|
|
19
19
|
// resolve the spec's security once (schemes, global requirement, per-op mode) and share it —
|
|
20
20
|
// schemaWriter (@source) and operationWriter (per-@connect) both query it instead of re-walking
|
|
21
21
|
// the whole spec per operation.
|
|
22
|
-
const security = SecurityPlan.from(gen.parser);
|
|
22
|
+
const security = SecurityPlan.from(gen.parser, { skipAuth: gen.options.skipAuth });
|
|
23
23
|
this.schemaWriter = new SchemaWriter(gen, security);
|
|
24
24
|
this.operationWriter = new OperationWriter(gen, security);
|
|
25
25
|
}
|
|
@@ -56,15 +56,18 @@ export class Writer {
|
|
|
56
56
|
this.schemaWriter.writeDirectives(writer);
|
|
57
57
|
this.schemaWriter.writeJSONScalar(writer);
|
|
58
58
|
types.forEach((type) => {
|
|
59
|
+
if (!type.name) {
|
|
60
|
+
// Box `metadata_query_indices` has `MetadataQueryIndex.fields.items` as an inline allOf.
|
|
61
|
+
// That node must be named `Fields` before the writer can emit `type Fields { ... }`.
|
|
62
|
+
throw new Error(`[writer] cannot emit a GraphQL definition without a name: id=${type.id}. ` +
|
|
63
|
+
`Name the type where it is created.`);
|
|
64
|
+
}
|
|
59
65
|
const count = refCount.get(type.name) !== undefined ? refCount.get(type.name) : Infinity;
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
// will print. use the printed name (`Pet`), not the `$ref`: an output `Pet` and its request
|
|
66
|
-
// `PetInput` come from the same `$ref` but are different types, so keying on the `$ref` would
|
|
67
|
-
// drop one. see #26
|
|
66
|
+
// `generatedSet` is keyed by internal id, but two ids can print the same GraphQL type.
|
|
67
|
+
// Launch Library reaches `AgencyMini` as both `obj:type:...AgencyMini` (`program.agencies`)
|
|
68
|
+
// and `comp:type:...AgencyMini` (`mission_patches.agency`). Track the printed name too, so
|
|
69
|
+
// `type AgencyMini` is emitted once. Keep the kind suffix because output `Pet` and input
|
|
70
|
+
// `PetInput` can both come from the same $ref.
|
|
68
71
|
const nameKey = T.isRef(type.name)
|
|
69
72
|
? 'name:' + Naming.genTypeName(type.name) + (type.kind === 'input' ? 'Input' : '')
|
|
70
73
|
: null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../../src/oas/io/writer.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,CAAC,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,CAAC,MAAM,QAAQ,CAAC;AAEvB,MAAM,OAAO,MAAM;IAKE;IAJX,YAAY,CAAe;IAC3B,eAAe,CAAkB;IAClC,MAAM,CAAW;IAExB,YAAmB,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;QAC5B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,6FAA6F;QAC7F,gGAAgG;QAChG,gCAAgC;QAChC,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../../src/oas/io/writer.ts"],"names":[],"mappings":"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","sourcesContent":["import { OasGen } from '../oasGen.js';\nimport { IType, T } from '../nodes/internal.js';\nimport { inferEntityResolvers } from '../nodes/entity.js';\nimport { applyBatchResolvers } from '../nodes/batch.js';\nimport { promoteInterfaces } from '../nodes/interfacePromotion.js';\nimport { OperationWriter } from './operationWriter.js';\nimport { SchemaWriter } from './schemaWriter.js';\nimport { SecurityPlan } from './security.js';\nimport { TypesCollector } from '../generator/typesCollector.js';\nimport { Naming } from '../utils/naming.js';\nimport _ from 'lodash';\n\nexport class Writer {\n private schemaWriter: SchemaWriter;\n private operationWriter: OperationWriter;\n public buffer: string[];\n\n constructor(public gen: OasGen) {\n this.buffer = [];\n // resolve the spec's security once (schemes, global requirement, per-op mode) and share it —\n // schemaWriter (@source) and operationWriter (per-@connect) both query it instead of re-walking\n // the whole spec per operation.\n const security = SecurityPlan.from(gen.parser, { skipAuth: gen.options.skipAuth });\n this.schemaWriter = new SchemaWriter(gen, security);\n this.operationWriter = new OperationWriter(gen, security);\n }\n\n public write(input: string): Writer {\n this.buffer.push(input);\n return this;\n }\n\n public flush(): string {\n return this.buffer.join('');\n }\n\n public generate(paths: string[]): string[] {\n const collector = new TypesCollector(this.gen);\n collector.collect(paths);\n\n return this.generateWith(collector.types, collector.expanded);\n }\n\n public generateWith(types: Map<string, IType>, selection: string[]) {\n this.writeSchema(this, types, selection);\n return selection;\n }\n\n public writeSchema(writer: Writer, types: Map<string, IType>, selection: string[]): void {\n const context = this.gen.context!;\n const generatedSet = context.generatedSet;\n\n // make our own copy of the refCount, so it doesn't get modified by the writing process\n const refCount = _.cloneDeep(context.refCount);\n\n // Attach entity resolvers onto the (single, canonical) collected type instances the\n // loop below generates, so each entity type can emit @key + its type-level\n // @connect/$this resolver. Resets first, so it's a no-op when the flag is off.\n inferEntityResolvers(context, this.gen, types, selection);\n\n // R6: add batch resolvers (needs the R1 @key above) when a --batch file was given.\n applyBatchResolvers(context, this.gen, types);\n\n // R2: promote discriminated oneOf-with-shared-allOf-base to a GraphQL interface (id-neutral;\n // no-op unless consolidateUnions is off and a qualifying union exists). Same `types` map.\n promoteInterfaces(context, this.gen, types, selection);\n\n this.schemaWriter.writeDirectives(writer);\n this.schemaWriter.writeJSONScalar(writer);\n\n types.forEach((type: IType) => {\n if (!type.name) {\n // Box `metadata_query_indices` has `MetadataQueryIndex.fields.items` as an inline allOf.\n // That node must be named `Fields` before the writer can emit `type Fields { ... }`.\n throw new Error(\n `[writer] cannot emit a GraphQL definition without a name: id=${type.id}. ` +\n `Name the type where it is created.`,\n );\n }\n\n const count = refCount.get(type.name) !== undefined ? refCount.get(type.name)! : Infinity;\n\n // `generatedSet` is keyed by internal id, but two ids can print the same GraphQL type.\n // Launch Library reaches `AgencyMini` as both `obj:type:...AgencyMini` (`program.agencies`)\n // and `comp:type:...AgencyMini` (`mission_patches.agency`). Track the printed name too, so\n // `type AgencyMini` is emitted once. Keep the kind suffix because output `Pet` and input\n // `PetInput` can both come from the same $ref.\n const nameKey = T.isRef(type.name)\n ? 'name:' + Naming.genTypeName(type.name) + (type.kind === 'input' ? 'Input' : '')\n : null;\n\n if (!generatedSet.has(type.id) && !(nameKey && generatedSet.has(nameKey)) && count > 0) {\n type.generate(context, this, selection);\n generatedSet.add(type.id);\n if (nameKey) generatedSet.add(nameKey);\n refCount.set(type.name, count - 1);\n }\n });\n\n const expanded = [...this.gen.paths];\n\n const queries = new Map(expanded.filter(([_k, type]) => type.id.startsWith('get:')));\n const mutations = new Map(expanded.filter(([_k, type]) => T.isMutationType(type)));\n\n this.operationWriter.writeQuery(context, writer, queries, selection);\n this.operationWriter.writeMutations(context, writer, mutations, selection);\n\n writer.flush();\n }\n}\n"]}
|
package/dist/oas/nodes/comp.js
CHANGED
|
@@ -171,26 +171,31 @@ export class Composed extends Type {
|
|
|
171
171
|
return super.add(this.withUniqueName(child));
|
|
172
172
|
}
|
|
173
173
|
updateName() {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
174
|
+
if (this.name) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (this.parent instanceof Res) {
|
|
178
|
+
const op = this.parent.parent;
|
|
179
|
+
this.name = op.getGqlOpName() + 'Response';
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (this.schema?.allOf?.length === 1) {
|
|
183
|
+
// A single-member allOf with a $ref is just a wrapper around that component:
|
|
184
|
+
// `allOf: [{ $ref: "#/components/schemas/Field" }]` emits `Field`.
|
|
185
|
+
// A single inline member has no component name, so `fields.items.allOf: [{ type: object }]`
|
|
186
|
+
// falls through and is named from the parent field as `Fields`.
|
|
187
|
+
const ref = _.get(this.schema.allOf[0], '$ref');
|
|
188
|
+
if (ref) {
|
|
189
|
+
this.name = ref;
|
|
190
|
+
return;
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
|
-
this.
|
|
193
|
+
if (this.parent instanceof Prop) {
|
|
194
|
+
this.name = Naming.genTypeName(Naming.getRefName(this.parent.name));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
// Consolidated allOf members keep the internal id that selection paths reference.
|
|
198
|
+
this.name = `[inline:${this.parent.name}]`;
|
|
194
199
|
}
|
|
195
200
|
}
|
|
196
201
|
//# sourceMappingURL=comp.js.map
|