@techspokes/typescript-wsdl-client 1.3.0 → 1.4.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/README.md +9 -0
- package/dist/checkedPipeline.d.ts +73 -2
- package/dist/checkedPipeline.js +118 -8
- package/dist/checkedPlanning.d.ts +5 -2
- package/dist/checkedPlanning.js +30 -5
- package/dist/checkedResponseInspection.d.ts +100 -0
- package/dist/checkedResponseInspection.js +499 -0
- package/dist/cli.js +55 -1
- package/dist/client/checkedDirectRuntime.d.ts +8 -0
- package/dist/client/checkedDirectRuntime.js +22 -14
- package/dist/client/directClientRuntime.d.ts +10 -3
- package/dist/client/directClientRuntime.js +14 -8
- package/dist/client/directClientVersion.d.ts +2 -0
- package/dist/client/directClientVersion.js +2 -0
- package/dist/client/generateDirectClient.js +7 -7
- package/dist/compiler/componentRecordProjection.d.ts +1 -0
- package/dist/compiler/componentRecordProjection.js +15 -4
- package/dist/compiler/directCodec.js +3 -11
- package/dist/compiler/directSoap.d.ts +50 -5
- package/dist/compiler/directSoap.js +204 -98
- package/dist/compiler/sourceCorrections.d.ts +118 -0
- package/dist/compiler/sourceCorrections.js +794 -0
- package/dist/gateway/checkedDocumentation.d.ts +14 -0
- package/dist/gateway/checkedDocumentation.js +106 -0
- package/dist/gateway/generateCheckedGateway.js +30 -53
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/util/readBoundedFile.d.ts +9 -0
- package/dist/util/readBoundedFile.js +36 -0
- package/dist/util/xmlBytes.d.ts +9 -0
- package/dist/util/xmlBytes.js +24 -0
- package/docs/api-reference.md +30 -1
- package/docs/checked-gateway.md +48 -0
- package/docs/cli-reference.md +16 -1
- package/docs/migration.md +16 -0
- package/package.json +1 -1
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
/** Offline, value-redacted inspection of actual SOAP response bytes against selected checked sources.
|
|
2
|
+
* @since Unreleased
|
|
3
|
+
* @constraints Observation never grants transport, generation, request-value or source-correction authority.
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs/promises';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { types } from 'node:util';
|
|
10
|
+
import { xmlByteLength, snapshotXmlBytes } from './util/xmlBytes.js';
|
|
11
|
+
import { prepareCheckedComponentPlan } from './checkedPlanning.js';
|
|
12
|
+
import { parseCheckedComponentChoices, acquireCheckedSourceInputs } from './checkedPipeline.js';
|
|
13
|
+
import { inspectDirectOperation, selectComponentOperations } from './compiler/directOperation.js';
|
|
14
|
+
import { inspectComponentSoapResponse } from './compiler/directSoap.js';
|
|
15
|
+
import { createComponentValueMapping } from './compiler/directValues.js';
|
|
16
|
+
import { observeComponentXml, assessObservedComponentPayload, DIRECT_XML_LIMITS } from './compiler/directCodec.js';
|
|
17
|
+
import { inspectComponentRecordProjection } from './compiler/componentRecordProjection.js';
|
|
18
|
+
import { acquireStandardSchemaLibrary } from './compiler/standardSchemaComponents.js';
|
|
19
|
+
import { inspectSourceCorrectionInventory, SourceCorrectionError } from './compiler/sourceCorrections.js';
|
|
20
|
+
import { loadSemanticSchemaInput, prepareSchemaSnapshotFiles, copyOriginalSchemaResource } from './loader/schemaInput.js';
|
|
21
|
+
import { resolveResourceUri } from './loader/schemaResources.js';
|
|
22
|
+
import { syntaxElements, syntaxAttribute, resolveLexicalQName, XSD_NAMESPACE, WSDL_NAMESPACE, SchemaLoadingError } from './loader/orderedSyntax.js';
|
|
23
|
+
const sameSelector = (a, b) => a.operation === b.operation &&
|
|
24
|
+
a.portType.namespace === b.portType.namespace && a.portType.local === b.portType.local &&
|
|
25
|
+
a.binding.namespace === b.binding.namespace && a.binding.local === b.binding.local;
|
|
26
|
+
const sameName = (a, b) => a.namespace === b.namespace && a.local === b.local;
|
|
27
|
+
const digest = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
28
|
+
const guidance = 'Inspect the scoped source rule and observed XML structure; author any correction explicitly, then rerun fresh native checks.';
|
|
29
|
+
const httpRules = new Set(['HTTP compression', 'SOAP response Content-Type', 'SOAP HTTP status', 'SOAP charset disagrees with bytes',
|
|
30
|
+
'HTTP 500 requires SOAP Fault', 'HTTP 400 requires SOAP Sender Fault']);
|
|
31
|
+
function record(value, allowed) {
|
|
32
|
+
if (!value || typeof value !== 'object' || types.isProxy(value) || ![null, Object.prototype].includes(Object.getPrototypeOf(value)))
|
|
33
|
+
throw new TypeError('Data-only response inspection options required');
|
|
34
|
+
const copy = Object.create(null);
|
|
35
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
36
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
37
|
+
if (typeof key !== 'string' || !allowed.includes(key) || !Object.hasOwn(property, 'value'))
|
|
38
|
+
throw new TypeError('Unsupported response inspection option');
|
|
39
|
+
copy[key] = property.value;
|
|
40
|
+
}
|
|
41
|
+
return copy;
|
|
42
|
+
}
|
|
43
|
+
function localFile(reference) {
|
|
44
|
+
const uri = resolveResourceUri(reference);
|
|
45
|
+
if (new URL(uri).protocol !== 'file:')
|
|
46
|
+
throw new TypeError('Offline response inspection requires local source files');
|
|
47
|
+
return fileURLToPath(uri);
|
|
48
|
+
}
|
|
49
|
+
function failureAssessment(kind) {
|
|
50
|
+
return ['mismatch', 'scalar-invalid', 'value-invalid', 'xml-invalid', 'protocol', 'transport'].includes(kind) ? 'mismatch' : 'refused';
|
|
51
|
+
}
|
|
52
|
+
/** Inspect actual caller-owned SOAP bytes without constructing a request or contacting any endpoint.
|
|
53
|
+
* @param source Local WSDL path, retaining optional configured logical snapshot identities.
|
|
54
|
+
* @param options Full operation selector, closed checked choices, actual bytes and optional actual HTTP metadata.
|
|
55
|
+
* @returns A bounded value-redacted report; independently conforming companion diagnostics never erase outer failures.
|
|
56
|
+
* @throws TypeError For malformed invocation data; RangeError for invalid capacities; filesystem errors for unavailable input files.
|
|
57
|
+
* @sideEffects Reads authorized local metadata only; creates no files and performs no SOAP transport.
|
|
58
|
+
*/
|
|
59
|
+
export async function inspectCheckedResponse(source, options) {
|
|
60
|
+
if (typeof source !== 'string' || !source)
|
|
61
|
+
throw new TypeError('Local WSDL source string required');
|
|
62
|
+
const supplied = record(options, ['checkedComponents', 'selector', 'responseBytes', 'responseMetadata', 'signal', 'limits', 'includeCorrectionDraft']);
|
|
63
|
+
if (supplied.includeCorrectionDraft !== undefined && typeof supplied.includeCorrectionDraft !== 'boolean')
|
|
64
|
+
throw new TypeError('includeCorrectionDraft must be boolean');
|
|
65
|
+
const selector = parseCheckedComponentChoices({ selectors: [supplied.selector] }).selectors[0];
|
|
66
|
+
const choices = parseCheckedComponentChoices(supplied.checkedComponents ?? {});
|
|
67
|
+
const responseByteLength = xmlByteLength(supplied.responseBytes);
|
|
68
|
+
const responseBytes = supplied.responseBytes;
|
|
69
|
+
if (supplied.signal !== undefined && (types.isProxy(supplied.signal) || !(supplied.signal instanceof AbortSignal)))
|
|
70
|
+
throw new TypeError('Native AbortSignal required');
|
|
71
|
+
const signals = [supplied.signal, choices.signal].filter((value) => value instanceof AbortSignal);
|
|
72
|
+
const signal = signals.length ? AbortSignal.any(signals) : undefined;
|
|
73
|
+
const limits = { ...DIRECT_XML_LIMITS, ...(supplied.limits === undefined ? {} : record(supplied.limits, ['maxBytes', 'maxDepth', 'maxNodes', 'maxText'])) };
|
|
74
|
+
for (const value of Object.values(limits))
|
|
75
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
76
|
+
throw new RangeError('Positive safe XML capacities required');
|
|
77
|
+
let metadata;
|
|
78
|
+
let metadataOverLimit = false;
|
|
79
|
+
if (supplied.responseMetadata !== undefined) {
|
|
80
|
+
const value = record(supplied.responseMetadata, ['status', 'contentType', 'contentEncoding']);
|
|
81
|
+
if (!Number.isSafeInteger(value.status) || value.status < 100 || value.status > 599 || typeof value.contentType !== 'string'
|
|
82
|
+
|| value.contentEncoding !== undefined && typeof value.contentEncoding !== 'string')
|
|
83
|
+
throw new TypeError('Actual HTTP status and content-type metadata required');
|
|
84
|
+
metadata = { status: value.status, contentType: value.contentType, ...(value.contentEncoding === undefined ? {} : { contentEncoding: value.contentEncoding }) };
|
|
85
|
+
metadataOverLimit = metadata.contentType.length > 8192 || (metadata.contentEncoding?.length ?? 0) > 256;
|
|
86
|
+
}
|
|
87
|
+
let truncated = false;
|
|
88
|
+
const bounded = (value, max = 512) => { if (value.length > max)
|
|
89
|
+
truncated = true; return value.slice(0, max); };
|
|
90
|
+
const displaySelector = { portType: { namespace: bounded(selector.portType.namespace), local: bounded(selector.portType.local) },
|
|
91
|
+
binding: { namespace: bounded(selector.binding.namespace), local: bounded(selector.binding.local) }, operation: bounded(selector.operation) };
|
|
92
|
+
const diagnostics = [];
|
|
93
|
+
const inputs = [];
|
|
94
|
+
const locations = new Map();
|
|
95
|
+
const resources = [];
|
|
96
|
+
const retain = (input) => {
|
|
97
|
+
if (inputs.includes(input))
|
|
98
|
+
return;
|
|
99
|
+
inputs.push(input);
|
|
100
|
+
for (const document of input.documents)
|
|
101
|
+
if (!locations.has(document.uri)) {
|
|
102
|
+
const identity = { resource: `source-${locations.size + 1}`, digest: document.digest };
|
|
103
|
+
locations.set(document.uri, identity);
|
|
104
|
+
if (resources.length < 64)
|
|
105
|
+
resources.push(identity);
|
|
106
|
+
else
|
|
107
|
+
truncated = true;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const withinByteLimit = responseByteLength <= limits.maxBytes;
|
|
111
|
+
const computedDigest = withinByteLimit && !signal?.aborted;
|
|
112
|
+
const bytes = computedDigest ? snapshotXmlBytes(responseBytes) : responseBytes;
|
|
113
|
+
const responseIdentity = { byteLength: responseByteLength,
|
|
114
|
+
digestAssessment: computedDigest ? 'computed' : signal?.aborted ? 'unassessed-cancelled' : 'unassessed-limit', ...(computedDigest ? { digest: digest(bytes) } : {}) };
|
|
115
|
+
const location = (at) => ({ resource: at.uri === 'urn:typescript-wsdl-client:inspected-response' ? 'response' : locations.get(at.uri)?.resource ?? 'unlocated-source',
|
|
116
|
+
digest: at.uri === 'urn:typescript-wsdl-client:inspected-response' ? responseIdentity.digest : locations.get(at.uri)?.digest ?? at.digest,
|
|
117
|
+
path: bounded(at.path), line: at.start.line, column: at.start.column });
|
|
118
|
+
const add = (scope, result) => {
|
|
119
|
+
if (diagnostics.length === 32) {
|
|
120
|
+
truncated = true;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
diagnostics.push({ scope, kind: bounded(result.kind, 80), rule: result.kind === 'xml-invalid' ? 'observed-XML-invalid' : bounded(result.rule), guidance,
|
|
124
|
+
...(result.source ? { source: location(result.source) } : {}), ...(result.schemaPath ? { schemaPath: bounded(result.schemaPath) } : {}),
|
|
125
|
+
...(result.payloadPath ? { payloadPath: bounded(result.payloadPath) } : {}) });
|
|
126
|
+
};
|
|
127
|
+
let responseOutcome = { assessment: 'unassessed' };
|
|
128
|
+
let wrapper, companion;
|
|
129
|
+
let fault = { assessment: 'unassessed' };
|
|
130
|
+
let kind = 'refused';
|
|
131
|
+
let http = { assessment: metadata ? 'not-established' : 'unassessed', ...(metadata ? { status: metadata.status } : {}) };
|
|
132
|
+
let correctionDraft;
|
|
133
|
+
let correctionEvidence;
|
|
134
|
+
let lastFailure;
|
|
135
|
+
let failedObservedPayload;
|
|
136
|
+
const finish = () => {
|
|
137
|
+
const scoped = (value) => ({ assessment: value.assessment, ...(value.rule ? { rule: bounded(value.rule) } : {}) });
|
|
138
|
+
const report = { version: 'checked-response-inspection-v1', kind, scope: metadata ? 'full-response' : 'xml-only',
|
|
139
|
+
admitted: false, executed: false, selector: displaySelector, response: responseIdentity, http, fault, resources,
|
|
140
|
+
outcomes: { response: scoped(responseOutcome), ...(wrapper ? { wrapper: scoped(wrapper) } : {}), ...(companion ? { companion: scoped(companion) } : {}) }, diagnostics,
|
|
141
|
+
...(correctionDraft ? { correctionDraft } : {}), ...(correctionEvidence ? { sourceCorrections: correctionEvidence } : {}), truncated };
|
|
142
|
+
if (Buffer.byteLength(JSON.stringify(report)) > 128 * 1024 && correctionDraft) {
|
|
143
|
+
correctionDraft = undefined;
|
|
144
|
+
truncated = true;
|
|
145
|
+
return finish();
|
|
146
|
+
}
|
|
147
|
+
return report;
|
|
148
|
+
};
|
|
149
|
+
if (signal?.aborted || !withinByteLimit || metadataOverLimit) {
|
|
150
|
+
add('response', { kind: signal?.aborted ? 'cancelled' : 'resource-limit', rule: signal?.aborted ? 'cooperative-cancellation' : metadataOverLimit ? 'HTTP metadata limit' : 'XML response byte limit' });
|
|
151
|
+
responseOutcome = { assessment: 'refused', rule: diagnostics[0].rule };
|
|
152
|
+
return finish();
|
|
153
|
+
}
|
|
154
|
+
const rootFile = localFile(source);
|
|
155
|
+
await fs.access(rootFile);
|
|
156
|
+
for (const entry of choices.sourceResources ?? [])
|
|
157
|
+
await fs.access(localFile(entry.file));
|
|
158
|
+
for (const entry of choices.recordProjections ?? [])
|
|
159
|
+
await fs.access(localFile(entry.source));
|
|
160
|
+
if (choices.sourceCorrectionsFile)
|
|
161
|
+
await fs.access(localFile(choices.sourceCorrectionsFile));
|
|
162
|
+
const selectedChoices = { ...choices, sourceResources: choices.sourceResources ?? [], signal };
|
|
163
|
+
const prepared = await prepareCheckedComponentPlan(rootFile, selectedChoices, undefined, selector);
|
|
164
|
+
if (prepared.sourceInput)
|
|
165
|
+
retain(prepared.sourceInput);
|
|
166
|
+
if (prepared.sources) {
|
|
167
|
+
retain(prepared.sources.main);
|
|
168
|
+
for (const input of prepared.sources.companions.values())
|
|
169
|
+
retain(input);
|
|
170
|
+
}
|
|
171
|
+
if (prepared.sources) {
|
|
172
|
+
const provenance = prepared.sources.provenance;
|
|
173
|
+
if (provenance.resources.length > 64)
|
|
174
|
+
truncated = true;
|
|
175
|
+
correctionEvidence = { recipeDigest: provenance.recipeFile.digest, resources: provenance.resources.slice(0, 64).map(resource => ({
|
|
176
|
+
resource: locations.get(resource.uri)?.resource ?? 'unlocated-source', originalDigest: resource.originalDigest,
|
|
177
|
+
effectiveDigest: resource.effectiveDigest, changed: resource.changed
|
|
178
|
+
})) };
|
|
179
|
+
}
|
|
180
|
+
const native = prepared.native?.expected.find(operation => sameSelector(operation.contract.selector, selector));
|
|
181
|
+
const projectionEntry = choices.recordProjections?.find(entry => sameSelector(entry.selector, selector));
|
|
182
|
+
if (!prepared.report.ready || !native) {
|
|
183
|
+
for (const blocker of prepared.report.blockers)
|
|
184
|
+
add('preparation', { kind: blocker.category, rule: blocker.reason });
|
|
185
|
+
if (!diagnostics.length)
|
|
186
|
+
add('preparation', { kind: 'unsupported', rule: 'selected-operation-not-prepared' });
|
|
187
|
+
responseOutcome = { assessment: 'refused', rule: diagnostics[0].rule };
|
|
188
|
+
if (projectionEntry && !signal?.aborted) {
|
|
189
|
+
wrapper = { assessment: 'refused', rule: 'wrapper-source-or-projection-not-qualified' };
|
|
190
|
+
if (choices.sourceCorrectionsFile && !prepared.sources) {
|
|
191
|
+
// @constraints An unprepared correction contract cannot be replaced by the original source for diagnosis.
|
|
192
|
+
companion = { assessment: 'unassessed', rule: 'effective-companion-source-unavailable' };
|
|
193
|
+
}
|
|
194
|
+
else
|
|
195
|
+
try {
|
|
196
|
+
const independently = await independentCompanion(projectionEntry, selectedChoices, bytes, limits, metadata, retain, prepared.sources?.companions.get(resolveResourceUri(projectionEntry.source)));
|
|
197
|
+
companion = independently.outcome;
|
|
198
|
+
if (independently.failure)
|
|
199
|
+
add('companion', independently.failure);
|
|
200
|
+
lastFailure = independently.failure;
|
|
201
|
+
failedObservedPayload = independently.payload;
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
if (!(error instanceof SchemaLoadingError))
|
|
205
|
+
throw error;
|
|
206
|
+
companion = { assessment: 'unassessed', rule: 'closed-companion-source-unavailable' };
|
|
207
|
+
add('companion', { kind: error.category, rule: 'closed-companion-source-unavailable' });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
retain(inspectDirectOperation(native.contract).source);
|
|
213
|
+
const input = native.mappings.find(mapping => mapping.kind === 'component-mapping' && mapping.direction.kind === 'input');
|
|
214
|
+
if (!input)
|
|
215
|
+
throw new TypeError('Prepared operation input mapping unavailable');
|
|
216
|
+
const projection = native.responseProjection;
|
|
217
|
+
if (projection)
|
|
218
|
+
retain(inspectDirectOperation(inspectComponentRecordProjection(projection).companion).source);
|
|
219
|
+
const assessed = inspectComponentSoapResponse(native.contract, input, new Uint8Array(bytes), { signal, limits: limits,
|
|
220
|
+
uri: 'urn:typescript-wsdl-client:inspected-response', responseMetadata: metadata, responseProjection: projection });
|
|
221
|
+
if ('admitted' in assessed) {
|
|
222
|
+
kind = 'conforming';
|
|
223
|
+
responseOutcome = { assessment: 'conforming' };
|
|
224
|
+
if (metadata)
|
|
225
|
+
http = { assessment: 'conforming', status: metadata.status };
|
|
226
|
+
fault = { assessment: 'conforming', observed: assessed.kind === 'soap-fault', ...(assessed.kind === 'soap-fault' ? { declared: assessed.decoded !== undefined } : {}) };
|
|
227
|
+
if (projection && assessed.kind === 'soap-response') {
|
|
228
|
+
wrapper = { assessment: 'conforming' };
|
|
229
|
+
companion = { assessment: 'conforming' };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
kind = failureAssessment(assessed.kind);
|
|
234
|
+
responseOutcome = { assessment: kind, rule: assessed.kind === 'xml-invalid' ? 'observed-XML-invalid' : bounded(assessed.rule) };
|
|
235
|
+
const source = assessed.source ?? (projection && /^record-wrapper-|^record-standard-/.test(assessed.rule) && !('parts' in native.contract.output)
|
|
236
|
+
? native.contract.output.element.context.source : undefined);
|
|
237
|
+
add('response', { ...assessed, ...(source ? { source } : {}) });
|
|
238
|
+
lastFailure = assessed;
|
|
239
|
+
if (metadata && httpRules.has(assessed.rule))
|
|
240
|
+
http = { assessment: 'mismatch', status: metadata.status };
|
|
241
|
+
if (projection) {
|
|
242
|
+
wrapper = { assessment: /^record-wrapper-|^record-standard-/.test(assessed.rule) ? kind : 'unassessed', rule: bounded(assessed.rule) };
|
|
243
|
+
const diagnostic = companionFromObservation(native.contract, projection, bytes, limits, metadata, signal);
|
|
244
|
+
companion = diagnostic.outcome;
|
|
245
|
+
if (diagnostic.failure)
|
|
246
|
+
add('companion', diagnostic.failure);
|
|
247
|
+
if (diagnostic.failure)
|
|
248
|
+
lastFailure = diagnostic.failure;
|
|
249
|
+
failedObservedPayload = diagnostic.payload;
|
|
250
|
+
}
|
|
251
|
+
else if (assessed.rule === 'unexpected-child') {
|
|
252
|
+
const observation = observeComponentXml(native.contract, input, bytes, { signal, limits: limits,
|
|
253
|
+
uri: 'urn:typescript-wsdl-client:inspected-response', componentResponseEncoding: { charset: charset(metadata) } });
|
|
254
|
+
if (observation.kind === 'observed-component-xml')
|
|
255
|
+
failedObservedPayload = envelopePayload(observation.root);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const wrapperCorrectionSite = wrapper?.assessment === 'mismatch' || diagnostics.some(diagnostic => diagnostic.scope === 'preparation'
|
|
260
|
+
&& /^record-wrapper-|^record-standard-/.test(diagnostic.rule));
|
|
261
|
+
if (supplied.includeCorrectionDraft && kind !== 'conforming' && choices.sourceCorrectionsFile) {
|
|
262
|
+
add('preparation', { kind: 'unsupported', rule: 'extend-original-source-correction-recipe-explicitly' });
|
|
263
|
+
}
|
|
264
|
+
else if (supplied.includeCorrectionDraft && kind !== 'conforming' && inputs.length && !signal?.aborted
|
|
265
|
+
&& (wrapperCorrectionSite || lastFailure?.rule === 'unexpected-child')) {
|
|
266
|
+
let inventory;
|
|
267
|
+
let draftInputs;
|
|
268
|
+
const activeInputs = [...inputs];
|
|
269
|
+
try {
|
|
270
|
+
// @why A draft must execute against the unchanged project, including companions of inactive operations.
|
|
271
|
+
const closure = await acquireCheckedSourceInputs(rootFile, { ...selectedChoices,
|
|
272
|
+
fileRoots: choices.fileRoots ?? [path.dirname(rootFile)], sourceResources: choices.sourceResources ?? [],
|
|
273
|
+
sourceOrigins: choices.sourceOrigins ?? [] }, prepared.sourceInput);
|
|
274
|
+
draftInputs = [closure.main, ...closure.companions.values()];
|
|
275
|
+
const assessed = new Map(activeInputs.flatMap(input => input.resources.map(resource => [resource.uri, resource.digest])));
|
|
276
|
+
const current = new Map(draftInputs.flatMap(input => input.resources.map(resource => [resource.uri, resource.digest])));
|
|
277
|
+
if ([...assessed].some(([uri, digest]) => current.get(uri) !== digest)) {
|
|
278
|
+
add('preparation', { kind: 'stale-source', rule: 'correction-draft-source-changed-after-assessment' });
|
|
279
|
+
return finish();
|
|
280
|
+
}
|
|
281
|
+
for (const input of draftInputs)
|
|
282
|
+
retain(input);
|
|
283
|
+
inventory = inspectSourceCorrectionInventory(draftInputs, signal);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
if (!(error instanceof SourceCorrectionError) && !(error instanceof SchemaLoadingError))
|
|
287
|
+
throw error;
|
|
288
|
+
add('preparation', { kind: error.category, rule: `correction-draft-inventory-${error.category}` });
|
|
289
|
+
return finish();
|
|
290
|
+
}
|
|
291
|
+
const pending = [];
|
|
292
|
+
const wrapperDeclaration = findOutputDeclaration(activeInputs[0], selector);
|
|
293
|
+
const scopes = [];
|
|
294
|
+
let wrapperScope, insertionScope;
|
|
295
|
+
if (projectionEntry && wrapperDeclaration && wrapperCorrectionSite) {
|
|
296
|
+
wrapperScope = declarationType(activeInputs, wrapperDeclaration);
|
|
297
|
+
if (wrapperScope)
|
|
298
|
+
scopes.push(wrapperScope);
|
|
299
|
+
}
|
|
300
|
+
if (lastFailure?.rule === 'unexpected-child' && lastFailure.schemaPath) {
|
|
301
|
+
const matches = activeInputs.flatMap(input => input.documents.flatMap(document => {
|
|
302
|
+
const result = findSyntax([input], document.uri, lastFailure.schemaPath);
|
|
303
|
+
return result ? [result] : [];
|
|
304
|
+
})).filter((node, index, all) => all.findIndex(other => other.source.uri === node.source.uri && other.source.path === node.source.path) === index);
|
|
305
|
+
if (matches.length === 1) {
|
|
306
|
+
insertionScope = declarationType(activeInputs, matches[0]);
|
|
307
|
+
if (insertionScope)
|
|
308
|
+
scopes.push(insertionScope);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const scoped = inventory.nodes.filter(node => node.kind === 'particle' && !node.ambiguousContext && scopes.some(scope => scope.source.uri === node.uri && (node.path === scope.source.path || node.path.startsWith(scope.source.path + '/'))));
|
|
312
|
+
for (const node of scoped) {
|
|
313
|
+
const target = { id: node.id, uri: node.uri, digest: node.digest };
|
|
314
|
+
const wrapperParticle = wrapperScope && wrapperScope.source.uri === node.uri && node.path.startsWith(wrapperScope.source.path + '/');
|
|
315
|
+
if (wrapperParticle && node.syntaxName.namespace === XSD_NAMESPACE && node.syntaxName.local === 'any')
|
|
316
|
+
pending.push({ kind: 'set-wildcard-namespaces', target, namespaces: null });
|
|
317
|
+
if (wrapperParticle && node.syntaxName.namespace === XSD_NAMESPACE && node.syntaxName.local === 'element') {
|
|
318
|
+
const syntax = findSyntax(activeInputs, node.uri, node.path), ref = syntax && syntaxAttribute(syntax, 'ref');
|
|
319
|
+
if (ref && syntax) {
|
|
320
|
+
try {
|
|
321
|
+
if (sameName(resolveLexicalQName(ref, syntax), { namespace: XSD_NAMESPACE, local: 'schema' }))
|
|
322
|
+
pending.push({ kind: 'set-occurs', target, min: null, max: node.occurs?.max ?? '1' });
|
|
323
|
+
}
|
|
324
|
+
catch { /* Invalid authored QNames remain native source diagnostics. */ }
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (lastFailure?.rule === 'unexpected-child') {
|
|
329
|
+
const sequences = scoped.filter(node => insertionScope?.source.uri === node.uri && node.path.startsWith(insertionScope.source.path + '/')
|
|
330
|
+
&& node.syntaxName.namespace === XSD_NAMESPACE && node.syntaxName.local === 'sequence');
|
|
331
|
+
if (sequences.length === 1)
|
|
332
|
+
pending.push({ kind: 'insert-element', target: { id: sequences[0].id, uri: sequences[0].uri, digest: sequences[0].digest },
|
|
333
|
+
position: insertionAnchor(activeInputs, inventory.nodes, sequences[0], failedObservedPayload, lastFailure.payloadPath),
|
|
334
|
+
element: { kind: 'declaration', name: null, type: null, min: null, max: null } });
|
|
335
|
+
}
|
|
336
|
+
if (pending.length && (pending.length > 16 || inventory.resources.length > 64)) {
|
|
337
|
+
truncated = true;
|
|
338
|
+
add('preparation', { kind: 'resource-limit', rule: 'complete-correction-draft-exceeds-report-capacity' });
|
|
339
|
+
}
|
|
340
|
+
else if (pending.length)
|
|
341
|
+
correctionDraft = { version: 1, resources: inventory.resources.map(resource => ({ uri: resource.uri, digest: resource.digest })), corrections: pending };
|
|
342
|
+
}
|
|
343
|
+
return finish();
|
|
344
|
+
}
|
|
345
|
+
function findSyntax(inputs, uri, at) {
|
|
346
|
+
for (const input of inputs) {
|
|
347
|
+
const document = input.documents.find(document => document.uri === uri);
|
|
348
|
+
if (!document)
|
|
349
|
+
continue;
|
|
350
|
+
const pending = [document.root];
|
|
351
|
+
while (pending.length) {
|
|
352
|
+
const node = pending.pop();
|
|
353
|
+
if (node.source.path === at)
|
|
354
|
+
return node;
|
|
355
|
+
pending.push(...syntaxElements(node));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
function findOutputDeclaration(input, selector) {
|
|
361
|
+
try {
|
|
362
|
+
const types = input.documents.flatMap(document => syntaxAttribute(document.root, 'targetNamespace') === selector.portType.namespace
|
|
363
|
+
? syntaxElements(document.root, WSDL_NAMESPACE, 'portType').filter(node => syntaxAttribute(node, 'name') === selector.portType.local) : []);
|
|
364
|
+
if (types.length !== 1)
|
|
365
|
+
return undefined;
|
|
366
|
+
const operations = syntaxElements(types[0], WSDL_NAMESPACE, 'operation').filter(node => syntaxAttribute(node, 'name') === selector.operation);
|
|
367
|
+
const output = operations.length === 1 ? syntaxElements(operations[0], WSDL_NAMESPACE, 'output') : [];
|
|
368
|
+
const authoredMessage = output.length === 1 ? syntaxAttribute(output[0], 'message') : undefined;
|
|
369
|
+
if (!authoredMessage)
|
|
370
|
+
return undefined;
|
|
371
|
+
const messageName = resolveLexicalQName(authoredMessage, output[0]);
|
|
372
|
+
const messages = input.documents.flatMap(document => syntaxAttribute(document.root, 'targetNamespace') === messageName.namespace
|
|
373
|
+
? syntaxElements(document.root, WSDL_NAMESPACE, 'message').filter(node => syntaxAttribute(node, 'name') === messageName.local) : []);
|
|
374
|
+
const parts = messages.length === 1 ? syntaxElements(messages[0], WSDL_NAMESPACE, 'part') : [];
|
|
375
|
+
const authoredElement = parts.length === 1 ? syntaxAttribute(parts[0], 'element') : undefined;
|
|
376
|
+
if (!authoredElement)
|
|
377
|
+
return undefined;
|
|
378
|
+
const elementName = resolveLexicalQName(authoredElement, parts[0]);
|
|
379
|
+
const roots = input.schemas.flatMap(schema => schema.targetNamespace === elementName.namespace
|
|
380
|
+
? syntaxElements(schema.syntax, XSD_NAMESPACE, 'element').filter(node => syntaxAttribute(node, 'name') === elementName.local) : []);
|
|
381
|
+
return roots.length === 1 ? roots[0] : undefined;
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function declarationType(inputs, declaration) {
|
|
388
|
+
const inline = syntaxElements(declaration, XSD_NAMESPACE, 'complexType');
|
|
389
|
+
if (inline.length === 1)
|
|
390
|
+
return inline[0];
|
|
391
|
+
const authored = syntaxAttribute(declaration, 'type');
|
|
392
|
+
if (!authored)
|
|
393
|
+
return undefined;
|
|
394
|
+
try {
|
|
395
|
+
const name = resolveLexicalQName(authored, declaration);
|
|
396
|
+
const matches = inputs.flatMap(input => input.schemas.flatMap(schema => schema.targetNamespace === name.namespace
|
|
397
|
+
? syntaxElements(schema.syntax, XSD_NAMESPACE, 'complexType').filter(node => syntaxAttribute(node, 'name') === name.local) : []))
|
|
398
|
+
.filter((node, index, all) => all.findIndex(other => other.source.uri === node.source.uri && other.source.path === node.source.path) === index);
|
|
399
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
return undefined;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function charset(metadata) {
|
|
406
|
+
return metadata?.contentType.match(/charset\s*=\s*"?([^;"\s]+)/i)?.[1]?.toLowerCase();
|
|
407
|
+
}
|
|
408
|
+
function envelopePayload(root) {
|
|
409
|
+
if (!['http://schemas.xmlsoap.org/soap/envelope/', 'http://www.w3.org/2003/05/soap-envelope'].includes(root.name.namespace) || root.name.local !== 'Envelope')
|
|
410
|
+
return undefined;
|
|
411
|
+
const body = syntaxElements(root).filter(node => node.name.namespace === root.name.namespace && node.name.local === 'Body');
|
|
412
|
+
const payload = body.length === 1 ? syntaxElements(body[0]) : [];
|
|
413
|
+
return payload.length === 1 ? payload[0] : undefined;
|
|
414
|
+
}
|
|
415
|
+
function insertionAnchor(inputs, nodes, sequence, payload, at) {
|
|
416
|
+
if (!payload || !at || !/^\/0(?:\/children\/\d+)+$/.test(at))
|
|
417
|
+
return null;
|
|
418
|
+
const indexes = [...at.matchAll(/\/children\/(\d+)/g)].map(match => Number(match[1]));
|
|
419
|
+
let parent = payload;
|
|
420
|
+
for (const index of indexes.slice(0, -1)) {
|
|
421
|
+
const child = parent.children[index];
|
|
422
|
+
if (child?.kind !== 'element')
|
|
423
|
+
return null;
|
|
424
|
+
parent = child;
|
|
425
|
+
}
|
|
426
|
+
const index = indexes.at(-1);
|
|
427
|
+
const before = parent.children.slice(0, index).filter((node) => node.kind === 'element').at(-1);
|
|
428
|
+
const after = parent.children.slice(index + 1).find((node) => node.kind === 'element');
|
|
429
|
+
const authored = nodes.filter(node => node.uri === sequence.uri && node.digest === sequence.digest && sequence.children?.includes(node.id)
|
|
430
|
+
&& node.syntaxName.namespace === XSD_NAMESPACE && node.syntaxName.local === 'element');
|
|
431
|
+
for (const [observed, kind] of [[before, 'after'], [after, 'before']]) {
|
|
432
|
+
if (!observed)
|
|
433
|
+
continue;
|
|
434
|
+
const matches = authored.filter(node => {
|
|
435
|
+
const syntax = findSyntax(inputs, node.uri, node.path);
|
|
436
|
+
if (!syntax)
|
|
437
|
+
return false;
|
|
438
|
+
const ref = syntaxAttribute(syntax, 'ref');
|
|
439
|
+
if (ref) {
|
|
440
|
+
try {
|
|
441
|
+
return sameName(resolveLexicalQName(ref, syntax), observed.name);
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const schemas = inputs.flatMap(input => input.schemas).filter(schema => schema.documentUri === node.uri && node.path.startsWith(schema.syntax.source.path + '/'));
|
|
448
|
+
const qualified = syntaxAttribute(syntax, 'form') === 'qualified' || syntaxAttribute(syntax, 'form') !== 'unqualified'
|
|
449
|
+
&& schemas.some(schema => syntaxAttribute(schema.syntax, 'elementFormDefault') === 'qualified');
|
|
450
|
+
return syntaxAttribute(syntax, 'name') === observed.name.local && observed.name.namespace === (qualified ? node.namespace : '');
|
|
451
|
+
});
|
|
452
|
+
if (matches.length === 1)
|
|
453
|
+
return { kind, target: { id: matches[0].id, uri: matches[0].uri, digest: matches[0].digest } };
|
|
454
|
+
}
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
function observedCompanion(contract, parser, companion, mapping, bytes, limits, metadata, signal) {
|
|
458
|
+
const observation = observeComponentXml(contract, parser, bytes, { signal, limits, uri: 'urn:typescript-wsdl-client:inspected-response', componentResponseEncoding: { charset: charset(metadata) } });
|
|
459
|
+
if (observation.kind !== 'observed-component-xml')
|
|
460
|
+
return { outcome: { assessment: failureAssessment(observation.kind), rule: observation.kind === 'xml-invalid' ? 'observed-XML-invalid' : observation.rule }, failure: observation };
|
|
461
|
+
const root = observation.root;
|
|
462
|
+
const soap = ['http://schemas.xmlsoap.org/soap/envelope/', 'http://www.w3.org/2003/05/soap-envelope'].includes(root.name.namespace) && root.name.local === 'Envelope';
|
|
463
|
+
const body = soap ? syntaxElements(root).filter(node => node.name.namespace === root.name.namespace && node.name.local === 'Body') : [];
|
|
464
|
+
const outer = body.length === 1 ? syntaxElements(body[0]) : [];
|
|
465
|
+
const output = companion.output;
|
|
466
|
+
if ('parts' in output)
|
|
467
|
+
return { outcome: { assessment: 'unassessed', rule: 'companion-document-output-required' } };
|
|
468
|
+
const candidates = outer.length === 1 ? syntaxElements(outer[0]).filter(node => sameName(node.name, output.element.name)) : [];
|
|
469
|
+
if (candidates.length !== 1)
|
|
470
|
+
return { outcome: { assessment: 'unassessed', rule: 'unique-genuine-companion-subtree-required' } };
|
|
471
|
+
const result = assessObservedComponentPayload(companion, mapping, observation, candidates[0], { signal });
|
|
472
|
+
return result.kind === 'component-decoded' ? { outcome: { assessment: 'conforming' }, payload: candidates[0] }
|
|
473
|
+
: { outcome: { assessment: failureAssessment(result.kind), rule: result.rule }, failure: result, payload: candidates[0] };
|
|
474
|
+
}
|
|
475
|
+
function companionFromObservation(outer, projection, bytes, limits, metadata, signal) {
|
|
476
|
+
const owned = inspectComponentRecordProjection(projection, outer);
|
|
477
|
+
return observedCompanion(outer, owned.parser, owned.companion, owned.mapping, bytes, limits, metadata, signal);
|
|
478
|
+
}
|
|
479
|
+
async function independentCompanion(entry, choices, bytes, limits, metadata, retain, effective) {
|
|
480
|
+
const loading = { policy: { fileRoots: choices.fileRoots ?? [path.dirname(localFile(entry.source))], allowedOrigins: choices.sourceOrigins ?? [] }, signal: choices.signal,
|
|
481
|
+
namespaceLocations: new Map(choices.namespaceLocations?.map(item => [item.namespace, item.location])) };
|
|
482
|
+
const offlineResources = effective ? new Map([...effective.resourceAliases].map(([alias, uri]) => {
|
|
483
|
+
const resource = effective.resources.find(resource => resource.uri === uri);
|
|
484
|
+
return [alias, { bytes: copyOriginalSchemaResource(effective, uri, () => { }), digest: resource.digest, canonicalUri: uri }];
|
|
485
|
+
})) : undefined;
|
|
486
|
+
const closed = effective ? { source: effective.root.uri, loading: { ...loading, offlineResources } }
|
|
487
|
+
: await prepareSchemaSnapshotFiles(localFile(entry.source), choices.sourceResources ?? [], loading);
|
|
488
|
+
const input = effective ?? await loadSemanticSchemaInput(closed.source, closed.loading);
|
|
489
|
+
retain(input);
|
|
490
|
+
const selected = (await selectComponentOperations(input.root.uri, [entry.recordSelector], { loading: closed.loading,
|
|
491
|
+
signal: choices.signal, standardLibraries: entry.standardLibraries?.map(acquireStandardSchemaLibrary) }))[0];
|
|
492
|
+
if (!selected || selected.kind !== 'selected')
|
|
493
|
+
return { outcome: { assessment: 'unassessed', rule: 'companion-native-source-not-qualified' } };
|
|
494
|
+
const parser = createComponentValueMapping(selected.contract, { kind: 'input' }, { signal: choices.signal });
|
|
495
|
+
const output = createComponentValueMapping(selected.contract, { kind: 'output' }, { signal: choices.signal });
|
|
496
|
+
if (parser.kind !== 'component-mapping' || output.kind !== 'component-mapping')
|
|
497
|
+
return { outcome: { assessment: 'unassessed', rule: 'companion-native-mapping-not-qualified' } };
|
|
498
|
+
return observedCompanion(selected.contract, parser, selected.contract, output, bytes, limits, metadata, choices.signal);
|
|
499
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
* command with legacy flags.
|
|
11
11
|
*/
|
|
12
12
|
import { assessSchemaSource, serializeAssessmentReport, assessmentExitCode } from "./compiler/checkedAssessment.js";
|
|
13
|
+
import { inspectCheckedResponse } from './checkedResponseInspection.js';
|
|
14
|
+
import { readBoundedFile } from './util/readBoundedFile.js';
|
|
15
|
+
import { parseCheckedComponentChoices } from './checkedPipeline.js';
|
|
16
|
+
import { writeCheckedPlanningFile } from './checkedPlanning.js';
|
|
13
17
|
import yargs from "yargs/yargs";
|
|
14
18
|
import { hideBin } from "yargs/helpers";
|
|
15
19
|
import fs from "node:fs";
|
|
@@ -52,7 +56,7 @@ function loadStreamConfigOrExit(filePath) {
|
|
|
52
56
|
const rawArgs = hideBin(process.argv);
|
|
53
57
|
const hasServicesConfig = rawArgs.some(argument => argument === "--services-config" || argument.startsWith("--services-config="));
|
|
54
58
|
// Available commands
|
|
55
|
-
const COMMANDS = ["compile", "client", "openapi", "gateway", "app", "pipeline", "assess"];
|
|
59
|
+
const COMMANDS = ["compile", "client", "openapi", "gateway", "app", "pipeline", "assess", "inspect-response"];
|
|
56
60
|
// ---------------------------------------------------------------------------
|
|
57
61
|
// Show help if no subcommand provided
|
|
58
62
|
// ---------------------------------------------------------------------------
|
|
@@ -62,6 +66,7 @@ if (!rawArgs[0] || !COMMANDS.includes(rawArgs[0])) {
|
|
|
62
66
|
.scriptName("wsdl-tsc")
|
|
63
67
|
.usage("$0 <command> [options]")
|
|
64
68
|
.command("assess", "Assess narrow direct-schema obligations; component readiness uses pipeline --checked-report")
|
|
69
|
+
.command('inspect-response', 'Inspect a captured SOAP response offline against a checked contract')
|
|
65
70
|
.command("compile", "Compile WSDL to catalog.json")
|
|
66
71
|
.command("client", "Generate TypeScript SOAP client from WSDL or catalog")
|
|
67
72
|
.command("openapi", "Generate OpenAPI specification from WSDL or catalog")
|
|
@@ -75,6 +80,55 @@ if (!rawArgs[0] || !COMMANDS.includes(rawArgs[0])) {
|
|
|
75
80
|
process.exit(1);
|
|
76
81
|
}
|
|
77
82
|
// Assessment is deliberately independent of every generation command.
|
|
83
|
+
if (rawArgs[0] === 'inspect-response') {
|
|
84
|
+
const argv = await yargs(rawArgs.slice(1)).version(false).scriptName('wsdl-tsc inspect-response')
|
|
85
|
+
.fail(() => { console.error('Invalid inspection arguments; use inspect-response --help'); process.exit(2); })
|
|
86
|
+
.option('wsdl-source', { type: 'string', demandOption: true })
|
|
87
|
+
.option('checked-config', { type: 'string', demandOption: true })
|
|
88
|
+
.option('operation', { type: 'string', demandOption: true })
|
|
89
|
+
.option('response-file', { type: 'string', demandOption: true })
|
|
90
|
+
.option('response-metadata', { type: 'string' })
|
|
91
|
+
.option('report', { type: 'string', demandOption: true })
|
|
92
|
+
.option('corrections-draft', { type: 'string' })
|
|
93
|
+
.strict().help().parse();
|
|
94
|
+
const controller = new AbortController();
|
|
95
|
+
const cancel = () => controller.abort();
|
|
96
|
+
process.on('SIGINT', cancel);
|
|
97
|
+
try {
|
|
98
|
+
const read = (file) => readBoundedFile(file, 1024 * 1024);
|
|
99
|
+
const choices = parseCheckedComponentChoices(JSON.parse(read(argv['checked-config']).toString('utf8')));
|
|
100
|
+
const selectors = choices.selectors?.filter(selector => selector.operation === argv.operation) ?? [];
|
|
101
|
+
if (selectors.length !== 1) {
|
|
102
|
+
console.error('Set checked-config selectors to exactly one matching operation; narrow the binding when operation names collide');
|
|
103
|
+
throw new TypeError('Ambiguous or missing inspection selector');
|
|
104
|
+
}
|
|
105
|
+
const metadata = argv['response-metadata'] ? JSON.parse(read(argv['response-metadata']).toString('utf8')) : undefined;
|
|
106
|
+
if (metadata !== undefined && (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)
|
|
107
|
+
|| Object.keys(metadata).some(key => !['status', 'contentType', 'contentEncoding'].includes(key))
|
|
108
|
+
|| !('status' in metadata) || !Number.isInteger(metadata.status) || Number(metadata.status) < 100 || Number(metadata.status) > 599
|
|
109
|
+
|| !('contentType' in metadata) || typeof metadata.contentType !== 'string'
|
|
110
|
+
|| ('contentEncoding' in metadata && typeof metadata.contentEncoding !== 'string')))
|
|
111
|
+
throw new TypeError('Invalid response metadata');
|
|
112
|
+
const report = await inspectCheckedResponse(argv['wsdl-source'], { checkedComponents: choices, selector: selectors[0],
|
|
113
|
+
responseBytes: read(argv['response-file']), signal: controller.signal,
|
|
114
|
+
includeCorrectionDraft: argv['corrections-draft'] !== undefined,
|
|
115
|
+
...(metadata === undefined ? {} : { responseMetadata: metadata }) });
|
|
116
|
+
// @why Exact local source pins belong only in the explicitly requested draft artifact.
|
|
117
|
+
const { correctionDraft, ...diagnostics } = report;
|
|
118
|
+
writeCheckedPlanningFile(argv.report, diagnostics, false);
|
|
119
|
+
if (argv['corrections-draft'] && correctionDraft)
|
|
120
|
+
writeCheckedPlanningFile(argv['corrections-draft'], correctionDraft, false);
|
|
121
|
+
console.error(`Response inspection: ${report.kind}; see the report for assessment scope`);
|
|
122
|
+
process.exitCode = report.kind === 'conforming' ? 0 : 1;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
console.error('Inspection invocation or file operation failed; verify configuration, input capacities and exclusive output paths');
|
|
126
|
+
process.exitCode = 2;
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
process.off('SIGINT', cancel);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
78
132
|
if (rawArgs[0] === "assess") {
|
|
79
133
|
const argv = await yargs(rawArgs.slice(1)).version(false).scriptName("wsdl-tsc assess")
|
|
80
134
|
.option("wsdl-source", { type: "string", demandOption: true, desc: "Authorized local WSDL/XSD source" })
|
|
@@ -103,4 +103,12 @@ export declare const checkedComponentRuntimeV2: Readonly<{
|
|
|
103
103
|
version: typeof COMPONENT_SOAP12_CLIENT_RUNTIME_VERSION;
|
|
104
104
|
execute(expectedBase64: string, input: unknown, options: ComponentOptions, version: typeof COMPONENT_SOAP12_CLIENT_RUNTIME_VERSION): Promise<ComponentClientResult>;
|
|
105
105
|
}>;
|
|
106
|
+
/** SOAP 1.1 binding for generated clients that distinguish protocol faults from declared detail values.
|
|
107
|
+
* @since 1.4.0
|
|
108
|
+
* @returns Native-validated values, SOAP 1.1 protocol faults with their original bytes, or explicit refusal.
|
|
109
|
+
*/
|
|
110
|
+
export declare const checkedComponentRuntimeV3: Readonly<{
|
|
111
|
+
version: "checked-component-soap11-runtime-v1";
|
|
112
|
+
execute(expectedBase64: string, input: unknown, options: ComponentOptions, version: "checked-component-soap11-runtime-v1"): Promise<ComponentClientResult<import("../compiler/directSoap.js").Soap11FaultMetadata> | Failure>;
|
|
113
|
+
}>;
|
|
106
114
|
export {};
|