@techspokes/typescript-wsdl-client 1.1.8 → 1.2.0
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 +17 -3
- package/dist/checkedPlanning.js +27 -4
- package/dist/cli.js +17 -1
- package/dist/client/generateClient.js +72 -16
- package/dist/client/generateOperations.js +30 -11
- package/dist/client/generateUtils.js +5 -1
- package/dist/compiler/canonicalGraph.d.ts +4 -15
- package/dist/compiler/canonicalGraph.js +1 -8
- package/dist/compiler/componentRecordProjection.d.ts +3 -0
- package/dist/compiler/componentRecordProjection.js +10 -0
- package/dist/compiler/declarationIdentity.d.ts +22 -0
- package/dist/compiler/declarationIdentity.js +9 -0
- package/dist/compiler/directCodec.d.ts +22 -2
- package/dist/compiler/directCodec.js +40 -7
- package/dist/compiler/directOperation.d.ts +26 -4
- package/dist/compiler/directOperation.js +59 -5
- package/dist/compiler/directSoap.js +24 -7
- package/dist/compiler/directSourceCapsule.js +59 -3
- package/dist/compiler/directStructure.js +6 -2
- package/dist/compiler/directStructurePlan.d.ts +10 -1
- package/dist/compiler/directStructurePlan.js +27 -7
- package/dist/compiler/directValues.d.ts +15 -3
- package/dist/compiler/directValues.js +20 -4
- package/dist/compiler/legacyDeclarations.d.ts +26 -0
- package/dist/compiler/legacyDeclarations.js +144 -0
- package/dist/compiler/projectionScalar.d.ts +18 -1
- package/dist/compiler/projectionScalar.js +47 -13
- package/dist/compiler/rpcStringValues.d.ts +30 -0
- package/dist/compiler/rpcStringValues.js +196 -0
- package/dist/compiler/rpcTypedValues.d.ts +48 -0
- package/dist/compiler/rpcTypedValues.js +190 -0
- package/dist/compiler/schemaCompiler.js +44 -109
- package/dist/compiler/semanticCatalog.d.ts +2 -2
- package/dist/gateway/generateComposedGateway.d.ts +16 -0
- package/dist/gateway/generateComposedGateway.js +65 -0
- package/dist/gateway/generateGateway.js +1 -0
- package/dist/gateway/generators.js +19 -4
- package/dist/gateway/helpers.d.ts +1 -0
- package/dist/gateway/helpers.js +15 -0
- package/dist/generation/componentShape.js +25 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/loader/orderedSyntax.d.ts +4 -0
- package/dist/loader/orderedSyntax.js +18 -4
- package/dist/multiServicePipeline.d.ts +41 -0
- package/dist/multiServicePipeline.js +287 -0
- package/dist/openapi/composeOpenAPI.d.ts +29 -0
- package/dist/openapi/composeOpenAPI.js +322 -0
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +2 -0
- package/docs/agent-skill.md +10 -1
- package/docs/api-reference.md +127 -9
- package/docs/checked-gateway.md +2 -0
- package/docs/checked-generation.md +11 -5
- package/docs/cli-reference.md +25 -5
- package/docs/concepts.md +4 -5
- package/docs/configuration.md +10 -3
- package/docs/corpus.md +1 -1
- package/docs/gateway-guide.md +104 -27
- package/docs/generated-code.md +12 -6
- package/docs/migration-playbook.md +4 -0
- package/docs/migration.md +124 -6
- package/docs/output-anatomy.md +20 -17
- package/docs/production.md +7 -5
- package/docs/start-here.md +12 -2
- package/docs/supported-patterns.md +17 -0
- package/docs/testing.md +2 -0
- package/docs/troubleshooting.md +3 -1
- package/llms.txt +9 -1
- package/package.json +2 -2
- package/src/runtime/clientStreamMethods.tpl.txt +156 -148
- package/src/runtime/operationsStreamHelper.tpl.txt +6 -6
- package/src/runtime/streamXml.ts +48 -20
|
@@ -2,6 +2,7 @@ import type { DirectOperationContract } from './directOperation.js';
|
|
|
2
2
|
import type { DirectValueMapping, DirectValueReceipt, DirectValueFailure, DirectExpandedValue, ComponentValueMapping, ProjectionExpandedValue, ProjectionValueReceipt } from './directValues.js';
|
|
3
3
|
import type { SyntaxElement, SyntaxSource } from '../loader/orderedSyntax.js';
|
|
4
4
|
import type { ResolverUsage } from './semanticBudget.js';
|
|
5
|
+
import type { RpcTypedValue } from './rpcTypedValues.js';
|
|
5
6
|
export interface DirectXmlLimits {
|
|
6
7
|
readonly maxBytes?: number;
|
|
7
8
|
readonly maxDepth?: number;
|
|
@@ -53,7 +54,7 @@ export interface ComponentEncodedXml {
|
|
|
53
54
|
readonly admitted: false;
|
|
54
55
|
readonly encoding: 'utf-8';
|
|
55
56
|
readonly bytes: Uint8Array;
|
|
56
|
-
readonly expanded: ProjectionExpandedValue;
|
|
57
|
+
readonly expanded: ProjectionExpandedValue | RpcExpandedValue;
|
|
57
58
|
readonly mapping: ComponentValueMapping;
|
|
58
59
|
readonly usage: ResolverUsage;
|
|
59
60
|
}
|
|
@@ -61,10 +62,23 @@ export interface ComponentDecodedXml {
|
|
|
61
62
|
readonly kind: 'component-decoded';
|
|
62
63
|
readonly admitted: false;
|
|
63
64
|
readonly payload: SyntaxElement;
|
|
64
|
-
readonly validated: ProjectionValueReceipt;
|
|
65
|
+
readonly validated: ProjectionValueReceipt | RpcValueReceipt;
|
|
65
66
|
readonly mapping: ComponentValueMapping;
|
|
66
67
|
readonly usage: ResolverUsage;
|
|
67
68
|
}
|
|
69
|
+
export interface RpcValueReceipt {
|
|
70
|
+
readonly kind: 'rpc-string-value' | 'rpc-typed-value';
|
|
71
|
+
readonly admitted: false;
|
|
72
|
+
readonly value: RpcTypedValue;
|
|
73
|
+
readonly usage: ResolverUsage;
|
|
74
|
+
}
|
|
75
|
+
export interface RpcExpandedValue {
|
|
76
|
+
readonly kind: 'rpc-string-expanded' | 'rpc-typed-expanded';
|
|
77
|
+
readonly admitted: false;
|
|
78
|
+
readonly payload: SyntaxElement;
|
|
79
|
+
readonly validated: RpcValueReceipt;
|
|
80
|
+
readonly usage: ResolverUsage;
|
|
81
|
+
}
|
|
68
82
|
export interface ComponentPayloadSelection {
|
|
69
83
|
readonly payload: SyntaxElement;
|
|
70
84
|
readonly mapping: ComponentValueMapping;
|
|
@@ -88,6 +102,9 @@ export declare function encodeComponentXml(contract: DirectOperationContract, ma
|
|
|
88
102
|
*/
|
|
89
103
|
export declare function observeComponentXml(contract: DirectOperationContract, mapping: unknown, bytes: Uint8Array, options?: Options & Readonly<{
|
|
90
104
|
uri?: string;
|
|
105
|
+
componentResponseEncoding?: {
|
|
106
|
+
charset?: string;
|
|
107
|
+
};
|
|
91
108
|
}>): ComponentXmlObservation | DirectCodecFailure;
|
|
92
109
|
/** Validate a genuine observed subtree through an independently qualified component mapping.
|
|
93
110
|
* @remarks XML capacities belong to the observation episode. This entry assesses the selected mapping's
|
|
@@ -111,6 +128,9 @@ export declare function decodeDirectXml(contract: DirectOperationContract, mappi
|
|
|
111
128
|
*/
|
|
112
129
|
export declare function decodeComponentXml(contract: DirectOperationContract, mapping: unknown, bytes: Uint8Array, options?: Options & Readonly<{
|
|
113
130
|
uri?: string;
|
|
131
|
+
componentResponseEncoding?: {
|
|
132
|
+
charset?: string;
|
|
133
|
+
};
|
|
114
134
|
selectPayload?: (root: SyntaxElement) => ComponentPayloadSelection;
|
|
115
135
|
}>): ComponentDecodedXml | DirectCodecFailure;
|
|
116
136
|
export {};
|
|
@@ -3,11 +3,13 @@ import { directDataOwnership, directLimit } from './directExecutionPolicy.js';
|
|
|
3
3
|
import { checkSemanticBudget } from './semanticBudget.js';
|
|
4
4
|
import { DataOwnershipLimitError } from './dataOwnership.js';
|
|
5
5
|
import { types } from 'node:util';
|
|
6
|
-
import { inspectDirectOperation } from './directOperation.js';
|
|
6
|
+
import { inspectDirectOperation, isRpcMessageRoot } from './directOperation.js';
|
|
7
7
|
import { inspectDirectValueMapping, expandDirectValue, assessDirectScalars, inspectComponentValueMapping, expandProjectionValue, assessProjectionScalars } from './directValues.js';
|
|
8
8
|
import { matchDirectOperation, matchDirectProjection } from './directStructure.js';
|
|
9
9
|
import { parseOrderedSyntax, SchemaLoadingError, XML_NAMESPACE } from '../loader/orderedSyntax.js';
|
|
10
10
|
import { isXmlNamespaceBinding } from './directStructurePlan.js';
|
|
11
|
+
import { expandRpcStrings, readRpcStrings } from './rpcStringValues.js';
|
|
12
|
+
import { expandRpcTyped, readRpcTyped } from './rpcTypedValues.js';
|
|
11
13
|
export const DIRECT_XML_LIMITS = Object.freeze({ maxBytes: 1024 * 1024, maxDepth: 256, maxNodes: 100_000, maxText: 1024 * 1024 });
|
|
12
14
|
const componentObservations = new WeakSet();
|
|
13
15
|
class Stop extends Error {
|
|
@@ -34,9 +36,11 @@ function runMapping(contract, supplied, options, profile, inspect, work) {
|
|
|
34
36
|
try {
|
|
35
37
|
const result = budget.guard(() => {
|
|
36
38
|
check();
|
|
37
|
-
|
|
39
|
+
const rpc = profile === componentProfile && (mapping.version === 'component-rpc-string-values-v1' || mapping.version === 'component-rpc-typed-values-v1') && isRpcMessageRoot(mapping.root)
|
|
40
|
+
&& contract.binding.style === 'rpc' && contract.binding.use === 'encoded' && contract.binding.encoding === 'http://schemas.xmlsoap.org/soap/encoding/';
|
|
41
|
+
if ((!rpc && mapping.version !== profile.version) || mapping.contextId !== contract.contextId
|
|
38
42
|
|| contract.basis !== profile.basis || contract.correspondence !== profile.correspondence
|
|
39
|
-
|| contract.binding.version !== '1.1' || contract.binding.style !== 'document' || contract.binding.use !== 'literal'
|
|
43
|
+
|| contract.binding.version !== '1.1' || !rpc && (contract.binding.style !== 'document' || contract.binding.use !== 'literal')
|
|
40
44
|
|| contract.binding.transport !== 'http://schemas.xmlsoap.org/soap/http')
|
|
41
45
|
throw new Stop('unsupported', profile.rule);
|
|
42
46
|
const direction = mapping.direction;
|
|
@@ -235,6 +239,16 @@ export function encodeDirectXml(contract, mapping, application, options = {}) {
|
|
|
235
239
|
export function encodeComponentXml(contract, mapping, application, options = {}) {
|
|
236
240
|
return runComponent(contract, mapping, options, (owned, session) => {
|
|
237
241
|
const { context } = inspectDirectOperation(contract), { budget, limits } = session;
|
|
242
|
+
if (isRpcMessageRoot(owned.root)) {
|
|
243
|
+
const control = { ...session, fail: (kind, rule, node) => { throw new Stop(kind, rule, node?.source); } };
|
|
244
|
+
const { rpcParts } = inspectComponentValueMapping(owned, contract);
|
|
245
|
+
const result = rpcParts ? expandRpcTyped(context, owned.root, rpcParts, application, control) : expandRpcStrings(owned.root, application, control);
|
|
246
|
+
const validated = { kind: rpcParts ? 'rpc-typed-value' : 'rpc-string-value', admitted: false, value: result.value, usage: budget.usage() };
|
|
247
|
+
const expanded = { kind: rpcParts ? 'rpc-typed-expanded' : 'rpc-string-expanded', admitted: false, payload: result.payload, validated, usage: budget.usage() };
|
|
248
|
+
directDataOwnership(budget).freeze(expanded);
|
|
249
|
+
const bytes = emitObservedXml(result.payload, session);
|
|
250
|
+
return Object.freeze({ kind: 'component-encoded', admitted: false, encoding: 'utf-8', bytes, expanded, mapping: owned, usage: budget.usage() });
|
|
251
|
+
}
|
|
238
252
|
// @why Application items include attributes; XML syntax-node/text caps count elements and character data.
|
|
239
253
|
// Encoded bytes bound inverse input capacity without treating those different counters as equal.
|
|
240
254
|
const expanded = expandProjectionValue(context, owned.root.element, application, { ...options,
|
|
@@ -247,7 +261,7 @@ export function encodeComponentXml(contract, mapping, application, options = {})
|
|
|
247
261
|
});
|
|
248
262
|
}
|
|
249
263
|
/** Retain the shared byte-copy, XML-version, entity and explicit capacity boundaries for both profiles. */
|
|
250
|
-
function parseObservedXml(bytes, { budget, limits, check }, sourceUri) {
|
|
264
|
+
function parseObservedXml(bytes, { budget, limits, check }, sourceUri, componentResponseEncoding) {
|
|
251
265
|
if (!types.isUint8Array(bytes) || types.isProxy(bytes))
|
|
252
266
|
throw new TypeError('XML requires Uint8Array bytes');
|
|
253
267
|
// Use native internal-slot access, avoiding subclass/own length, buffer and
|
|
@@ -266,7 +280,7 @@ function parseObservedXml(bytes, { budget, limits, check }, sourceUri) {
|
|
|
266
280
|
if (typeof uri !== 'string')
|
|
267
281
|
throw new TypeError('XML source URI must be a string');
|
|
268
282
|
check();
|
|
269
|
-
return parseOrderedSyntax(input, uri, { ...limits, xml10Only: true,
|
|
283
|
+
return parseOrderedSyntax(input, uri, { ...limits, xml10Only: true, componentResponseEncoding,
|
|
270
284
|
chargeWork: () => check(), reserveNode: () => { check(); budget.reserveNodes(1); } }).root;
|
|
271
285
|
}
|
|
272
286
|
function assertObservedPayload(root, payload, check) {
|
|
@@ -291,7 +305,7 @@ function assertObservedPayload(root, payload, check) {
|
|
|
291
305
|
*/
|
|
292
306
|
export function observeComponentXml(contract, mapping, bytes, options = {}) {
|
|
293
307
|
const result = runComponent(contract, mapping, options, (_owned, session) => {
|
|
294
|
-
const root = parseObservedXml(bytes, session, options.uri ?? 'urn:typescript-wsdl-client:observed-component-xml');
|
|
308
|
+
const root = parseObservedXml(bytes, session, options.uri ?? 'urn:typescript-wsdl-client:observed-component-xml', options.componentResponseEncoding);
|
|
295
309
|
directDataOwnership(session.budget).freeze(root);
|
|
296
310
|
return Object.freeze({ kind: 'observed-component-xml', root, usage: Object.freeze({ ...session.budget.usage() }) });
|
|
297
311
|
});
|
|
@@ -310,6 +324,15 @@ export function assessObservedComponentPayload(contract, mapping, observation, p
|
|
|
310
324
|
return runComponent(contract, mapping, options, (owned, { budget, check }) => {
|
|
311
325
|
assertObservedPayload(observation.root, payload, check);
|
|
312
326
|
const { context } = inspectDirectOperation(contract);
|
|
327
|
+
if (isRpcMessageRoot(owned.root)) {
|
|
328
|
+
const control = { budget, check, fail: (kind, rule, node) => { throw new Stop(kind, rule, node?.source); } };
|
|
329
|
+
const { rpcParts } = inspectComponentValueMapping(owned, contract);
|
|
330
|
+
const value = rpcParts && owned.direction.kind === 'input' ? readRpcTyped(context, owned.root, rpcParts, payload, control)
|
|
331
|
+
: readRpcStrings(owned.root, payload, owned.direction.kind === 'output', control);
|
|
332
|
+
const validated = { kind: rpcParts ? 'rpc-typed-value' : 'rpc-string-value', admitted: false, value, usage: budget.usage() };
|
|
333
|
+
directDataOwnership(budget).freeze(validated);
|
|
334
|
+
return { kind: 'component-decoded', admitted: false, payload, validated, mapping: owned, usage: budget.usage() };
|
|
335
|
+
}
|
|
313
336
|
const structural = matchDirectProjection(context, owned.root.element, payload, options);
|
|
314
337
|
if (structural.kind !== 'projection-match')
|
|
315
338
|
return structural;
|
|
@@ -358,10 +381,20 @@ export function decodeDirectXml(contract, mapping, bytes, options = {}) {
|
|
|
358
381
|
export function decodeComponentXml(contract, mapping, bytes, options = {}) {
|
|
359
382
|
return runComponent(contract, mapping, options, (owned, session) => {
|
|
360
383
|
const { budget, check } = session, { context } = inspectDirectOperation(contract);
|
|
361
|
-
const root = parseObservedXml(bytes, session, options.uri ?? 'urn:typescript-wsdl-client:observed-component-xml');
|
|
384
|
+
const root = parseObservedXml(bytes, session, options.uri ?? 'urn:typescript-wsdl-client:observed-component-xml', options.componentResponseEncoding);
|
|
362
385
|
const selection = options.selectPayload?.(root) ?? { payload: root, mapping: owned };
|
|
363
386
|
const selected = inspectComponentValueMapping(selection.mapping, contract).mapping;
|
|
364
387
|
assertObservedPayload(root, selection.payload, check);
|
|
388
|
+
if (isRpcMessageRoot(selected.root)) {
|
|
389
|
+
const control = { ...session, fail: (kind, rule, node) => { throw new Stop(kind, rule, node?.source); } };
|
|
390
|
+
const { rpcParts } = inspectComponentValueMapping(selected, contract);
|
|
391
|
+
const value = rpcParts && selected.direction.kind === 'input' ? readRpcTyped(context, selected.root, rpcParts, selection.payload, control)
|
|
392
|
+
: readRpcStrings(selected.root, selection.payload, selected.direction.kind === 'output', control);
|
|
393
|
+
const validated = { kind: rpcParts ? 'rpc-typed-value' : 'rpc-string-value', admitted: false, value, usage: budget.usage() };
|
|
394
|
+
directDataOwnership(budget).freeze(validated);
|
|
395
|
+
directDataOwnership(budget).freeze(selection.payload);
|
|
396
|
+
return { kind: 'component-decoded', admitted: false, payload: selection.payload, validated, mapping: selected, usage: budget.usage() };
|
|
397
|
+
}
|
|
365
398
|
const structural = matchDirectProjection(context, selected.root.element, selection.payload, options);
|
|
366
399
|
if (structural.kind !== 'projection-match')
|
|
367
400
|
return structural;
|
|
@@ -19,6 +19,27 @@ export interface DirectMessageRoot {
|
|
|
19
19
|
readonly type: Reference;
|
|
20
20
|
readonly source: SyntaxSource;
|
|
21
21
|
}
|
|
22
|
+
/** Source-owned RPC parameters; these are WSDL parts rather than schema element declarations.
|
|
23
|
+
* @since 1.1.9
|
|
24
|
+
*/
|
|
25
|
+
export interface RpcMessageRoot {
|
|
26
|
+
readonly kind: 'rpc-typed-parts-v1';
|
|
27
|
+
readonly message: ExpandedName;
|
|
28
|
+
readonly parts: readonly Readonly<{
|
|
29
|
+
name: string;
|
|
30
|
+
type: Reference;
|
|
31
|
+
source: SyntaxSource;
|
|
32
|
+
}>[];
|
|
33
|
+
readonly wrapper: ExpandedName;
|
|
34
|
+
readonly bodyNamespace: string;
|
|
35
|
+
readonly encoding: 'http://schemas.xmlsoap.org/soap/encoding/';
|
|
36
|
+
readonly source: SyntaxSource;
|
|
37
|
+
}
|
|
38
|
+
/** Distinguish typed RPC parameters without inventing a document element.
|
|
39
|
+
* @param root Selected native message.
|
|
40
|
+
* @returns Whether the message uses the bounded RPC profile.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isRpcMessageRoot(root: DirectMessageRoot | RpcMessageRoot): root is RpcMessageRoot;
|
|
22
43
|
export interface DirectOperationContract {
|
|
23
44
|
readonly selector: DirectOperationSelector;
|
|
24
45
|
readonly contextId: string;
|
|
@@ -26,14 +47,15 @@ export interface DirectOperationContract {
|
|
|
26
47
|
readonly correspondence: 'dated-XSD-2004-direct-v1' | 'dated-XSD-2004-components-v1';
|
|
27
48
|
readonly binding: Readonly<{
|
|
28
49
|
version: '1.1';
|
|
29
|
-
style: 'document';
|
|
30
|
-
use: 'literal';
|
|
50
|
+
style: 'document' | 'rpc';
|
|
51
|
+
use: 'literal' | 'encoded';
|
|
52
|
+
encoding?: string;
|
|
31
53
|
transport: string;
|
|
32
54
|
action: string;
|
|
33
55
|
source: SyntaxSource;
|
|
34
56
|
}>;
|
|
35
|
-
readonly input: DirectMessageRoot;
|
|
36
|
-
readonly output: DirectMessageRoot;
|
|
57
|
+
readonly input: DirectMessageRoot | RpcMessageRoot;
|
|
58
|
+
readonly output: DirectMessageRoot | RpcMessageRoot;
|
|
37
59
|
readonly faults: readonly Readonly<{
|
|
38
60
|
name: string;
|
|
39
61
|
root: DirectMessageRoot;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { directLimit, directLoadingLimits } from './directExecutionPolicy.js';
|
|
2
2
|
/** Transient source-owned schema selection. This module grants no runtime admission. */
|
|
3
3
|
import { loadSemanticSchemaInput } from '../loader/schemaInput.js';
|
|
4
|
-
import { SchemaLoadingError, WSDL_NAMESPACE, syntaxAttribute, syntaxElements, isXmlNCName } from '../loader/orderedSyntax.js';
|
|
4
|
+
import { SchemaLoadingError, WSDL_NAMESPACE, XSD_NAMESPACE, syntaxAttribute, syntaxElements, isXmlNCName } from '../loader/orderedSyntax.js';
|
|
5
5
|
import { globalId } from './canonicalGraph.js';
|
|
6
6
|
import { createApplicationBudget, DEFAULT_MAX_NODES } from './semanticBudget.js';
|
|
7
7
|
import { qualifyDirectSource } from './checkedSourcePreparation.js';
|
|
@@ -10,6 +10,13 @@ import { prepareWsdlPolicyRequirements, assertWsdlPolicyRequirements } from './w
|
|
|
10
10
|
const SOAP = 'http://schemas.xmlsoap.org/wsdl/soap/';
|
|
11
11
|
const HTTP = 'http://schemas.xmlsoap.org/soap/http';
|
|
12
12
|
const ADDRESSING_WSDL = 'http://www.w3.org/2006/05/addressing/wsdl';
|
|
13
|
+
/** Distinguish typed RPC parameters without inventing a document element.
|
|
14
|
+
* @param root Selected native message.
|
|
15
|
+
* @returns Whether the message uses the bounded RPC profile.
|
|
16
|
+
*/
|
|
17
|
+
export function isRpcMessageRoot(root) {
|
|
18
|
+
return 'kind' in root && root.kind === 'rpc-typed-parts-v1';
|
|
19
|
+
}
|
|
13
20
|
const issued = new WeakMap();
|
|
14
21
|
/** Object identity authenticates the exact issued roots; context IDs and digests do not. */
|
|
15
22
|
export function inspectDirectOperation(contract, expectedContext) {
|
|
@@ -181,7 +188,7 @@ async function selectOperationInRequest(source, selector, options, budget, profi
|
|
|
181
188
|
portType: ['name'], operation: ['name', 'parameterOrder'], input: ['name', 'message'], output: ['name', 'message'], fault: ['name', 'message'],
|
|
182
189
|
binding: ['name', 'type'], service: ['name'], port: ['name', 'binding'],
|
|
183
190
|
};
|
|
184
|
-
const soapAttributes = { binding: ['style', 'transport'], operation: ['soapAction', 'style'], body: ['use', 'parts'], fault: ['name', 'use'], address: ['location'] };
|
|
191
|
+
const soapAttributes = { binding: ['style', 'transport'], operation: ['soapAction', 'style'], body: profile === 'components' ? ['use', 'parts', 'namespace', 'encodingStyle'] : ['use', 'parts'], fault: ['name', 'use'], address: ['location'] };
|
|
185
192
|
for (const document of input.documents) {
|
|
186
193
|
const stack = [document.root];
|
|
187
194
|
while (stack.length) {
|
|
@@ -256,7 +263,8 @@ async function selectOperationInRequest(source, selector, options, budget, profi
|
|
|
256
263
|
return refuse('unsupported', 'soap-binding-style');
|
|
257
264
|
if (attr(soapBinding, 'transport') !== HTTP)
|
|
258
265
|
return refuse('unsupported', 'soap-transport');
|
|
259
|
-
|
|
266
|
+
const style = attr(soapOperation, 'style') ?? attr(soapBinding, 'style') ?? 'document';
|
|
267
|
+
if (style !== 'document' && !(profile === 'components' && style === 'rpc'))
|
|
260
268
|
return refuse('unsupported', 'soap-style');
|
|
261
269
|
const action = attr(soapOperation, 'soapAction');
|
|
262
270
|
if (action === undefined)
|
|
@@ -283,6 +291,7 @@ async function selectOperationInRequest(source, selector, options, budget, profi
|
|
|
283
291
|
shape(boundSyntax, [soap(faultName === undefined ? 'body' : 'fault'), ws('documentation')]);
|
|
284
292
|
const body = exactly(boundSyntax, SOAP, faultName === undefined ? 'body' : 'fault');
|
|
285
293
|
shape(body, []);
|
|
294
|
+
attrs(body, faultName === undefined ? ['use', 'parts'] : ['use', 'name']);
|
|
286
295
|
if (attr(body, 'use') !== 'literal')
|
|
287
296
|
return refuse('unsupported', 'literal-required');
|
|
288
297
|
if (faultName !== undefined && attr(body, 'name') !== faultName)
|
|
@@ -309,17 +318,61 @@ async function selectOperationInRequest(source, selector, options, budget, profi
|
|
|
309
318
|
return refuse('selection-refused', 'element-type');
|
|
310
319
|
return Object.freeze({ message: message.name, part: partName, element, type: element.type, source: part.source });
|
|
311
320
|
};
|
|
321
|
+
const rpcRoot = (messageSyntax, boundSyntax, direction) => {
|
|
322
|
+
attrs(messageSyntax, ['name', 'message']);
|
|
323
|
+
attrs(boundSyntax, ['name']);
|
|
324
|
+
shape(messageSyntax, [ws('documentation')]);
|
|
325
|
+
shape(boundSyntax, [soap('body'), ws('documentation')]);
|
|
326
|
+
const body = exactly(boundSyntax, SOAP, 'body');
|
|
327
|
+
shape(body, []);
|
|
328
|
+
const encoding = 'http://schemas.xmlsoap.org/soap/encoding/';
|
|
329
|
+
if (attr(body, 'use') !== 'encoded' || attr(body, 'encodingStyle')?.trim() !== encoding)
|
|
330
|
+
return refuse('unsupported', 'rpc-string-encoding');
|
|
331
|
+
const bodyNamespace = attr(body, 'namespace');
|
|
332
|
+
if (!bodyNamespace || !/^[A-Za-z][A-Za-z0-9+.-]*:/.test(bodyNamespace))
|
|
333
|
+
return refuse('unsupported', 'rpc-body-namespace');
|
|
334
|
+
const message = reference(portType, messageSyntax, 'message');
|
|
335
|
+
if (message.kind !== 'wsdl' || message.role !== 'message')
|
|
336
|
+
return refuse('selection-refused', 'message-reference');
|
|
337
|
+
shape(message.syntax, [ws('part'), ws('documentation')]);
|
|
338
|
+
const declared = children(message.syntax).filter(c => c.name.namespace === WSDL_NAMESPACE && c.name.local === 'part');
|
|
339
|
+
if (!declared.length || direction === 'output' && declared.length !== 1)
|
|
340
|
+
return refuse('unsupported', 'rpc-single-string-result');
|
|
341
|
+
const names = new Set();
|
|
342
|
+
const parts = declared.map(part => {
|
|
343
|
+
attrs(part, ['name', 'type']);
|
|
344
|
+
shape(part, [ws('documentation')]);
|
|
345
|
+
const name = attr(part, 'name');
|
|
346
|
+
if (!name || !isXmlNCName(name) || names.has(name))
|
|
347
|
+
return refuse('selection-refused', 'rpc-part-name');
|
|
348
|
+
names.add(name);
|
|
349
|
+
const path = part.source.path.slice(message.syntax.source.path.length) || '/';
|
|
350
|
+
const refs = message.references.filter(r => r.path === path && r.attribute === 'type');
|
|
351
|
+
if (refs.length !== 1)
|
|
352
|
+
return refuse('unsupported', 'rpc-part-type-required');
|
|
353
|
+
const resolved = context.resolve(refs[0].reference);
|
|
354
|
+
if (direction === 'output' && (resolved.kind !== 'builtin' || resolved.name.namespace !== XSD_NAMESPACE || resolved.name.local !== 'string'))
|
|
355
|
+
return refuse('unsupported', 'rpc-string-part-required');
|
|
356
|
+
return Object.freeze({ name, type: refs[0].reference, source: part.source });
|
|
357
|
+
});
|
|
358
|
+
if (attr(body, 'parts') !== undefined && attr(body, 'parts').trim().split(/[\t\r\n ]+/).join(' ') !== parts.map(p => p.name).join(' '))
|
|
359
|
+
return refuse('unsupported', 'rpc-body-parts');
|
|
360
|
+
return Object.freeze({ kind: 'rpc-typed-parts-v1', message: message.name, parts, bodyNamespace, encoding,
|
|
361
|
+
wrapper: { namespace: bodyNamespace, local: ownedSelector.operation + (direction === 'output' ? 'Response' : '') }, source: body.source });
|
|
362
|
+
};
|
|
312
363
|
const io = (direction, declaration) => {
|
|
313
364
|
const boundIo = exactly(bound, WSDL_NAMESPACE, direction);
|
|
314
365
|
const boundName = attr(boundIo, 'name');
|
|
315
366
|
const expectedName = attr(declaration, 'name') ?? `${ownedSelector.operation}${direction === 'input' ? 'Request' : 'Response'}`;
|
|
316
367
|
if (boundName !== undefined && boundName !== expectedName)
|
|
317
368
|
return refuse('selection-refused', 'binding-message-name');
|
|
318
|
-
return root(declaration, boundIo);
|
|
369
|
+
return style === 'rpc' ? rpcRoot(declaration, boundIo, direction) : root(declaration, boundIo);
|
|
319
370
|
};
|
|
320
371
|
const requestRoot = io('input', abstractInput), responseRoot = io('output', abstractOutput);
|
|
321
372
|
const declaredFaults = children(abstract).filter(c => c.name.namespace === WSDL_NAMESPACE && c.name.local === 'fault');
|
|
322
373
|
const boundFaults = children(bound).filter(c => c.name.namespace === WSDL_NAMESPACE && c.name.local === 'fault');
|
|
374
|
+
if (style === 'rpc' && (declaredFaults.length || boundFaults.length))
|
|
375
|
+
return refuse('unsupported', 'rpc-declared-fault');
|
|
323
376
|
if (declaredFaults.length !== boundFaults.length)
|
|
324
377
|
return refuse('selection-refused', 'fault-mapping');
|
|
325
378
|
const faultNames = new Set();
|
|
@@ -340,7 +393,8 @@ async function selectOperationInRequest(source, selector, options, budget, profi
|
|
|
340
393
|
const contract = { selector: ownedSelector, contextId: context.contextId,
|
|
341
394
|
basis: profile === 'direct' ? 'native-direct-premises-v1' : 'native-component-premises-v1',
|
|
342
395
|
correspondence: profile === 'direct' ? 'dated-XSD-2004-direct-v1' : 'dated-XSD-2004-components-v1',
|
|
343
|
-
binding: { version: '1.1', style:
|
|
396
|
+
binding: { version: '1.1', style: style, use: style === 'rpc' ? 'encoded' : 'literal',
|
|
397
|
+
...(style === 'rpc' ? { encoding: 'http://schemas.xmlsoap.org/soap/encoding/' } : {}), transport: HTTP, action, source: binding.context.source },
|
|
344
398
|
input: requestRoot, output: responseRoot, faults,
|
|
345
399
|
...(policyOwner ? { policy: policyOwner.forBinding(binding.syntax), addressingActions: { required: false,
|
|
346
400
|
input: attr(abstractInput, 'Action', ADDRESSING_WSDL), output: attr(abstractOutput, 'Action', ADDRESSING_WSDL),
|
|
@@ -171,6 +171,7 @@ async function exchangeSoap(contract, input, application, options, profile) {
|
|
|
171
171
|
&& (typeof allowEndpoint !== 'function' || allowEndpoint(new URL(endpoint.href)) !== true))
|
|
172
172
|
return fail('unsupported', 'endpoint authorization');
|
|
173
173
|
const action = contract.binding.action;
|
|
174
|
+
const rpc = contract.binding.style === 'rpc';
|
|
174
175
|
if (/[^\x20-\x21\x23-\x7e]|\\/.test(action))
|
|
175
176
|
return fail('unsupported', 'unsafe SOAPAction');
|
|
176
177
|
const elements = (node) => {
|
|
@@ -191,9 +192,10 @@ async function exchangeSoap(contract, input, application, options, profile) {
|
|
|
191
192
|
return fail(mapping.kind, mapping.rule);
|
|
192
193
|
faults.set(fault.name, mapping);
|
|
193
194
|
}
|
|
194
|
-
const shape = (node) => {
|
|
195
|
+
const shape = (node, encodedScope = false) => {
|
|
195
196
|
const children = elements(node);
|
|
196
|
-
if (node.attributes.
|
|
197
|
+
if (node.attributes.some(a => !(encodedScope && a.name.namespace === SOAP && a.name.local === 'encodingStyle'
|
|
198
|
+
&& a.value === 'http://schemas.xmlsoap.org/soap/encoding/')) || node.children.some(n => n.kind === 'text' && /[^\t\r\n ]/.test(n.value)))
|
|
197
199
|
stop('protocol', 'SOAP protocol attributes or text');
|
|
198
200
|
return children;
|
|
199
201
|
};
|
|
@@ -201,7 +203,7 @@ async function exchangeSoap(contract, input, application, options, profile) {
|
|
|
201
203
|
const bodyRoot = (root, incoming = false) => {
|
|
202
204
|
if (!same(root.name, { namespace: SOAP, local: 'Envelope' }))
|
|
203
205
|
stop('protocol', 'SOAP 1.1 Envelope required');
|
|
204
|
-
const children = shape(root);
|
|
206
|
+
const children = shape(root, rpc);
|
|
205
207
|
if (children[0]?.name.namespace === SOAP && children[0].name.local === 'Header') {
|
|
206
208
|
if (profile.component && incoming) {
|
|
207
209
|
const assessed = assessComponentSoapHeader(children[0], check);
|
|
@@ -219,13 +221,25 @@ async function exchangeSoap(contract, input, application, options, profile) {
|
|
|
219
221
|
}
|
|
220
222
|
if (children.length !== 1 || !same(children[0].name, { namespace: SOAP, local: 'Body' }))
|
|
221
223
|
stop('protocol', 'one SOAP Body required');
|
|
222
|
-
const body = shape(children[0]);
|
|
224
|
+
const body = shape(children[0], rpc);
|
|
225
|
+
if (rpc && !body.some(n => same(n.name, { namespace: SOAP, local: 'Fault' })))
|
|
226
|
+
return children[0];
|
|
223
227
|
if (body.length !== 1)
|
|
224
228
|
stop('protocol', 'one SOAP Body payload required');
|
|
225
229
|
return body[0];
|
|
226
230
|
};
|
|
227
231
|
const wsdl = await profile.prepare(config, check);
|
|
228
232
|
check();
|
|
233
|
+
// @why node-soap's RPC writer would wrap `_xml` as a parameter. This private
|
|
234
|
+
// instance retains its selected method/transport while native bytes own RPC construction.
|
|
235
|
+
const rpcArguments = Object.freeze({});
|
|
236
|
+
if (rpc)
|
|
237
|
+
wsdl.objectToRpcXML = (name, args) => {
|
|
238
|
+
check();
|
|
239
|
+
if (name !== contract.selector.operation || args !== rpcArguments)
|
|
240
|
+
stop('protocol', 'dependency RPC selection mismatch');
|
|
241
|
+
return Buffer.from(encoded.bytes).toString('utf8');
|
|
242
|
+
};
|
|
229
243
|
// HTTP and codec byte limits bound buffering and output copies directly.
|
|
230
244
|
agent = endpoint.protocol === 'https:' ? new HttpsAgent({ keepAlive: false, maxSockets: 1, rejectUnauthorized: true,
|
|
231
245
|
...(config.ca === undefined ? {} : { ca: config.ca }) }) : new Agent({ keepAlive: false, maxSockets: 1 });
|
|
@@ -290,7 +304,7 @@ async function exchangeSoap(contract, input, application, options, profile) {
|
|
|
290
304
|
if (typeof method !== 'function')
|
|
291
305
|
return fail('unsupported', 'dependency selected method unavailable');
|
|
292
306
|
check();
|
|
293
|
-
await new Promise((resolve, reject) => method({ _xml: Buffer.from(encoded.bytes).toString('utf8') }, (error) => error === complete ? resolve() : reject(error), {}, { SOAPAction: `"${action}"` }));
|
|
307
|
+
await new Promise((resolve, reject) => method(rpc ? rpcArguments : { _xml: Buffer.from(encoded.bytes).toString('utf8') }, (error) => error === complete ? resolve() : reject(error), {}, { SOAPAction: `"${action}"` }));
|
|
294
308
|
check();
|
|
295
309
|
if (timedOut)
|
|
296
310
|
return fail('transport', 'SOAP deadline exceeded');
|
|
@@ -301,19 +315,22 @@ async function exchangeSoap(contract, input, application, options, profile) {
|
|
|
301
315
|
const observed = response;
|
|
302
316
|
if (observed.encoding && observed.encoding.toLowerCase() !== 'identity')
|
|
303
317
|
return fail('unsupported', 'HTTP compression');
|
|
304
|
-
const media = /^text\/xml(?:\s*;\s*charset\s*=\s*(?:"(utf-8|utf-16|utf-16le|utf-16be)"|(utf-8|utf-16|utf-16le|utf-16be)))?\s*$/i.exec(observed.contentType);
|
|
318
|
+
const media = /^text\/xml(?:\s*;\s*charset\s*=\s*(?:"(utf-8|utf-16|utf-16le|utf-16be|iso-8859-1)"|(utf-8|utf-16|utf-16le|utf-16be|iso-8859-1)))?\s*$/i.exec(observed.contentType);
|
|
305
319
|
if (!media)
|
|
306
320
|
return fail('protocol', 'SOAP response Content-Type');
|
|
307
321
|
if (observed.status !== 200 && observed.status !== 500)
|
|
308
322
|
return fail('transport', 'SOAP HTTP status');
|
|
309
323
|
// Header charset is a protocol assertion, checked against actual BOM bytes.
|
|
310
324
|
const charset = (media[1] ?? media[2])?.toLowerCase();
|
|
325
|
+
if (!profile.component && charset === 'iso-8859-1')
|
|
326
|
+
return fail('protocol', 'SOAP response Content-Type');
|
|
311
327
|
const bom = observed.bytes[0] === 0xff && observed.bytes[1] === 0xfe ? 'utf-16le'
|
|
312
328
|
: observed.bytes[0] === 0xfe && observed.bytes[1] === 0xff ? 'utf-16be' : 'utf-8';
|
|
313
|
-
if (charset && charset !== bom && !(charset === 'utf-16' && bom.startsWith('utf-16')))
|
|
329
|
+
if (!profile.component && charset && charset !== bom && !(charset === 'utf-16' && bom.startsWith('utf-16')))
|
|
314
330
|
return fail('protocol', 'SOAP charset disagrees with bytes');
|
|
315
331
|
let faultInfo;
|
|
316
332
|
const decoded = profile.decode(contract, output, observed.bytes, { signal, limits, uri: endpoint.href,
|
|
333
|
+
...(profile.component ? { componentResponseEncoding: { charset } } : {}),
|
|
317
334
|
selectPayload: root => {
|
|
318
335
|
const payload = bodyRoot(root, true);
|
|
319
336
|
if (!same(payload.name, { namespace: SOAP, local: 'Fault' })) {
|
|
@@ -316,6 +316,8 @@ export function checkDirectSourceCompatibility(expected, contract, selection, si
|
|
|
316
316
|
const componentVersions = Object.freeze({ ...versions, scope: 'component-soap11-utf8-graph-v1', mapping: 'component-values-v1',
|
|
317
317
|
runtimePolicy: 'explicit-http-verified-https-transport-v1' });
|
|
318
318
|
const scopedComponentVersions = Object.freeze({ ...componentVersions, scope: 'component-soap11-utf8-graph-v2', mapping: 'component-values-and-record-scope-v1' });
|
|
319
|
+
const rpcComponentVersions = Object.freeze({ ...componentVersions, scope: 'component-soap11-rpc-strings-v1', mapping: 'component-rpc-string-values-v1' });
|
|
320
|
+
const typedRpcComponentVersions = Object.freeze({ ...componentVersions, scope: 'component-soap11-rpc-typed-latin1-v1', mapping: 'component-rpc-typed-values-v1' });
|
|
319
321
|
const componentReconstructions = new WeakMap();
|
|
320
322
|
function componentFailure(error, budget) {
|
|
321
323
|
return Object.freeze({ ...failure(error, budget), repair: 'Regenerate matched component artifacts from explicitly authorized original WSDL and dependencies.' });
|
|
@@ -362,6 +364,17 @@ async function componentDescriptor(contract, selection, signal, responseProjecti
|
|
|
362
364
|
if (mapping.kind !== 'component-mapping' && mapping.kind !== 'component-record-projection')
|
|
363
365
|
throw new Stop(mapping.kind === 'resource-limit' || mapping.kind === 'cancelled' ? mapping.kind : 'unsupported', mapping.rule);
|
|
364
366
|
mappings.push(mapping);
|
|
367
|
+
if ('parts' in mapping.root)
|
|
368
|
+
return { kind: 'rpc-typed-parts-v1', message: mapping.root.message,
|
|
369
|
+
wrapper: mapping.root.wrapper, bodyNamespace: mapping.root.bodyNamespace, encoding: contract.binding.encoding,
|
|
370
|
+
parts: mapping.root.parts.map(part => {
|
|
371
|
+
const resolved = inspectDirectOperation(contract).context.resolve(part.type);
|
|
372
|
+
const type = resolved.kind === 'builtin' ? resolved.name : resolved.kind === 'found' && resolved.node.identity.kind === 'global'
|
|
373
|
+
? resolved.node.identity.name : undefined;
|
|
374
|
+
if (!type)
|
|
375
|
+
throw new Stop('unsupported', 'rpc-named-type-required');
|
|
376
|
+
return { name: part.name, type };
|
|
377
|
+
}) };
|
|
365
378
|
return { element: mapping.root.element.name, message: mapping.root.message, part: mapping.root.part };
|
|
366
379
|
};
|
|
367
380
|
const input = make({ kind: 'input' }), output = make({ kind: 'output' });
|
|
@@ -371,6 +384,8 @@ async function componentDescriptor(contract, selection, signal, responseProjecti
|
|
|
371
384
|
let projection = null;
|
|
372
385
|
if (responseProjection) {
|
|
373
386
|
const { companion } = inspectComponentRecordProjection(responseProjection, contract);
|
|
387
|
+
if ('parts' in companion.output)
|
|
388
|
+
throw new Stop('unsupported', 'record-projection-requires-document-output');
|
|
374
389
|
const { context } = inspectDirectOperation(companion);
|
|
375
390
|
projection = { spec: responseProjection.spec, source: { selector: companion.selector, action: companion.binding.action,
|
|
376
391
|
output: { element: companion.output.element.name, message: companion.output.message, part: companion.output.part },
|
|
@@ -412,7 +427,11 @@ export async function createComponentSourceCapsule(contract, selection, signal,
|
|
|
412
427
|
function componentCapsule(derived, budget) {
|
|
413
428
|
const { compatibility, resources, libraries, projection } = derived;
|
|
414
429
|
const extended = libraries.length > 0 || projection !== null;
|
|
415
|
-
const
|
|
430
|
+
const rpc = 'kind' in compatibility.input && compatibility.input.kind === 'rpc-typed-parts-v1';
|
|
431
|
+
const typedRpc = rpc && compatibility.input.parts.some(part => part.type.namespace !== 'http://www.w3.org/2001/XMLSchema' || part.type.local !== 'string');
|
|
432
|
+
if (rpc && (extended || derived.applicationGroups))
|
|
433
|
+
throw new Stop('unsupported', 'rpc-artifact-extension');
|
|
434
|
+
const value = { versions: typedRpc ? typedRpcComponentVersions : rpc ? rpcComponentVersions : extended ? scopedComponentVersions : componentVersions, resources, compatibility,
|
|
416
435
|
...(extended ? { libraries, projection } : {}), ...(derived.applicationGroups ? { applicationGroups: derived.applicationGroups, applicationGroupIdentity: derived.applicationGroupIdentity } : {}) };
|
|
417
436
|
return { ...value, fingerprint: hash(json(value, budget)) };
|
|
418
437
|
}
|
|
@@ -470,13 +489,45 @@ function readComponent(bytes, budget, signal) {
|
|
|
470
489
|
if (!/^[a-f0-9]{64}$/.test(parsed.applicationGroupIdentity))
|
|
471
490
|
reject('application-group-identity');
|
|
472
491
|
}
|
|
473
|
-
const
|
|
492
|
+
const typedRpc = !!parsed.versions && typeof parsed.versions === 'object' && parsed.versions.scope === typedRpcComponentVersions.scope;
|
|
493
|
+
const rpc = typedRpc || !!parsed.versions && typeof parsed.versions === 'object' && parsed.versions.scope === rpcComponentVersions.scope;
|
|
494
|
+
if (rpc && (extended || grouped))
|
|
495
|
+
reject('rpc-artifact-extension');
|
|
496
|
+
const expectedVersions = typedRpc ? typedRpcComponentVersions : rpc ? rpcComponentVersions : extended ? scopedComponentVersions : componentVersions;
|
|
474
497
|
const actual = record(parsed.versions, Object.keys(expectedVersions));
|
|
475
498
|
for (const key of Object.keys(expectedVersions))
|
|
476
499
|
if (actual[key] !== expectedVersions[key])
|
|
477
500
|
reject('unsupported-version-mode-or-policy');
|
|
478
501
|
validateComponentResources(parsed.resources, budget, signal);
|
|
479
|
-
const root = (value) => {
|
|
502
|
+
const root = (value) => {
|
|
503
|
+
if (rpc) {
|
|
504
|
+
const r = record(value, ['kind', 'message', 'wrapper', 'bodyNamespace', 'encoding', 'parts']);
|
|
505
|
+
if (r.kind !== 'rpc-typed-parts-v1' || r.encoding !== 'http://schemas.xmlsoap.org/soap/encoding/')
|
|
506
|
+
reject('rpc-root-profile');
|
|
507
|
+
name(r.message);
|
|
508
|
+
name(r.wrapper);
|
|
509
|
+
text(r.bodyNamespace);
|
|
510
|
+
if (!Array.isArray(r.parts) || !r.parts.length)
|
|
511
|
+
return reject('rpc-parts-required');
|
|
512
|
+
const names = new Set();
|
|
513
|
+
for (const part of r.parts) {
|
|
514
|
+
check(budget, signal);
|
|
515
|
+
const p = record(part, ['name', 'type']);
|
|
516
|
+
ncname(p.name);
|
|
517
|
+
name(p.type);
|
|
518
|
+
if (names.has(p.name))
|
|
519
|
+
reject('duplicate-rpc-part');
|
|
520
|
+
names.add(p.name);
|
|
521
|
+
if (!typedRpc && (p.type.namespace !== 'http://www.w3.org/2001/XMLSchema' || p.type.local !== 'string'))
|
|
522
|
+
reject('rpc-part-type');
|
|
523
|
+
}
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const r = record(value, ['element', 'message', 'part']);
|
|
527
|
+
name(r.element);
|
|
528
|
+
name(r.message);
|
|
529
|
+
ncname(r.part);
|
|
530
|
+
};
|
|
480
531
|
const selector = (value) => { const s = record(value, ['portType', 'binding', 'operation']); name(s.portType); name(s.binding); ncname(s.operation); };
|
|
481
532
|
if (extended) {
|
|
482
533
|
validateComponentLibraries(parsed.libraries);
|
|
@@ -520,6 +571,11 @@ function readComponent(bytes, budget, signal) {
|
|
|
520
571
|
text(c.action);
|
|
521
572
|
root(c.input);
|
|
522
573
|
root(c.output);
|
|
574
|
+
if (rpc) {
|
|
575
|
+
const parts = c.input.parts;
|
|
576
|
+
if (typedRpc !== parts.some(part => part.type.namespace !== 'http://www.w3.org/2001/XMLSchema' || part.type.local !== 'string'))
|
|
577
|
+
reject('rpc-type-version');
|
|
578
|
+
}
|
|
523
579
|
if (!Array.isArray(c.faults))
|
|
524
580
|
return reject('faults-required');
|
|
525
581
|
const names = new Set();
|
|
@@ -514,8 +514,12 @@ export function matchDirectOperation(contract, direction, payload, options = {})
|
|
|
514
514
|
throw new Stop('unsupported', 'unknown-fault');
|
|
515
515
|
root = selected.root;
|
|
516
516
|
}
|
|
517
|
-
else
|
|
518
|
-
|
|
517
|
+
else {
|
|
518
|
+
const selected = contract[ownedDirection.kind];
|
|
519
|
+
if ('parts' in selected)
|
|
520
|
+
throw new Stop('unsupported', 'direct-document-root-required');
|
|
521
|
+
root = selected;
|
|
522
|
+
}
|
|
519
523
|
const matched = matchDirectProjection(context, root.element, payload, { ...options, flatOnly: true });
|
|
520
524
|
if (matched.kind !== 'projection-match')
|
|
521
525
|
return matched;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Single native direct slot interpretation shared by matching and value mapping. */
|
|
2
2
|
import type { DirectMessageRoot } from './directOperation.js';
|
|
3
3
|
import type { SemanticContext } from './semanticContext.js';
|
|
4
|
-
import type { AttributeNode, ComplexTypeNode, ElementNode, NodeId, ParticleNode } from './canonicalGraph.js';
|
|
4
|
+
import type { AttributeNode, ComplexTypeNode, ElementNode, GraphNode, NodeId, ParticleNode, Reference } from './canonicalGraph.js';
|
|
5
5
|
import type { ExpandedName } from '../loader/orderedSyntax.js';
|
|
6
6
|
import type { ProjectionScalarList } from './projectionScalar.js';
|
|
7
7
|
import type { SemanticParticle, SemanticParticleView } from './particleSemanticView.js';
|
|
@@ -143,6 +143,15 @@ export declare function selectProjectionAttribute(context: SemanticContext, reco
|
|
|
143
143
|
* The checked-direct preview adapter below retains its flat-only grammar.
|
|
144
144
|
*/
|
|
145
145
|
export declare function planDirectProjection(context: SemanticContext, root: ElementNode, check: () => void, unsupported: (detail?: DirectProjectionUnavailable) => never): DirectProjectionPlan;
|
|
146
|
+
/** Plan a source-owned type using native scalar, content and particle owners.
|
|
147
|
+
* @param context Native source context.
|
|
148
|
+
* @param owner Exact source graph node owning the type reference.
|
|
149
|
+
* @param reference Selected source type.
|
|
150
|
+
* @param check Cooperative checkpoint.
|
|
151
|
+
* @param unsupported Explicit refusal callback.
|
|
152
|
+
* @returns The same native projection used by document elements, without element facts.
|
|
153
|
+
*/
|
|
154
|
+
export declare function planProjectionType(context: SemanticContext, owner: GraphNode, reference: Reference, check: () => void, unsupported: (detail?: DirectProjectionUnavailable) => never): DirectProjectionPlan;
|
|
146
155
|
/** Adapt the native projection to the supported flat preview without widening admission. */
|
|
147
156
|
export declare function planDirectRoot(context: SemanticContext, root: DirectMessageRoot, check: () => void, unsupported: () => never): DirectRootPlan;
|
|
148
157
|
/** Flat-only projection adapter; its input is derived data, never native admission. */
|