@techspokes/typescript-wsdl-client 1.1.8 → 1.3.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.
Files changed (85) hide show
  1. package/README.md +22 -3
  2. package/dist/checkedPlanning.js +27 -4
  3. package/dist/cli.js +17 -1
  4. package/dist/client/checkedDirectRuntime.d.ts +11 -1
  5. package/dist/client/checkedDirectRuntime.js +19 -2
  6. package/dist/client/directClientRuntime.d.ts +18 -1
  7. package/dist/client/directClientRuntime.js +15 -3
  8. package/dist/client/directClientVersion.d.ts +2 -0
  9. package/dist/client/directClientVersion.js +2 -0
  10. package/dist/client/generateClient.js +72 -16
  11. package/dist/client/generateDirectClient.js +14 -9
  12. package/dist/client/generateOperations.js +30 -11
  13. package/dist/client/generateUtils.js +5 -1
  14. package/dist/compiler/canonicalGraph.d.ts +4 -15
  15. package/dist/compiler/canonicalGraph.js +1 -8
  16. package/dist/compiler/componentRecordProjection.d.ts +3 -0
  17. package/dist/compiler/componentRecordProjection.js +10 -0
  18. package/dist/compiler/declarationIdentity.d.ts +22 -0
  19. package/dist/compiler/declarationIdentity.js +9 -0
  20. package/dist/compiler/directCodec.d.ts +22 -2
  21. package/dist/compiler/directCodec.js +40 -7
  22. package/dist/compiler/directOperation.d.ts +28 -5
  23. package/dist/compiler/directOperation.js +84 -14
  24. package/dist/compiler/directSoap.d.ts +40 -10
  25. package/dist/compiler/directSoap.js +151 -18
  26. package/dist/compiler/directSoapSource.js +3 -2
  27. package/dist/compiler/directSourceCapsule.js +69 -4
  28. package/dist/compiler/directStructure.js +6 -2
  29. package/dist/compiler/directStructurePlan.d.ts +10 -1
  30. package/dist/compiler/directStructurePlan.js +27 -7
  31. package/dist/compiler/directValues.d.ts +15 -3
  32. package/dist/compiler/directValues.js +20 -4
  33. package/dist/compiler/legacyDeclarations.d.ts +26 -0
  34. package/dist/compiler/legacyDeclarations.js +144 -0
  35. package/dist/compiler/projectionScalar.d.ts +18 -1
  36. package/dist/compiler/projectionScalar.js +47 -13
  37. package/dist/compiler/rpcStringValues.d.ts +30 -0
  38. package/dist/compiler/rpcStringValues.js +196 -0
  39. package/dist/compiler/rpcTypedValues.d.ts +48 -0
  40. package/dist/compiler/rpcTypedValues.js +190 -0
  41. package/dist/compiler/schemaCompiler.js +44 -109
  42. package/dist/compiler/semanticCatalog.d.ts +2 -2
  43. package/dist/compiler/soapHeaders.d.ts +10 -4
  44. package/dist/compiler/soapHeaders.js +28 -9
  45. package/dist/gateway/generateCheckedGateway.js +26 -6
  46. package/dist/gateway/generateComposedGateway.d.ts +16 -0
  47. package/dist/gateway/generateComposedGateway.js +65 -0
  48. package/dist/gateway/generateGateway.js +1 -0
  49. package/dist/gateway/generators.js +19 -4
  50. package/dist/gateway/helpers.d.ts +1 -0
  51. package/dist/gateway/helpers.js +15 -0
  52. package/dist/generation/componentShape.js +25 -1
  53. package/dist/index.d.ts +5 -1
  54. package/dist/index.js +3 -1
  55. package/dist/loader/orderedSyntax.d.ts +4 -0
  56. package/dist/loader/orderedSyntax.js +18 -4
  57. package/dist/multiServicePipeline.d.ts +41 -0
  58. package/dist/multiServicePipeline.js +287 -0
  59. package/dist/openapi/composeOpenAPI.d.ts +29 -0
  60. package/dist/openapi/composeOpenAPI.js +322 -0
  61. package/dist/pipeline.d.ts +1 -1
  62. package/dist/pipeline.js +2 -0
  63. package/docs/agent-skill.md +10 -1
  64. package/docs/api-reference.md +127 -9
  65. package/docs/checked-gateway.md +10 -0
  66. package/docs/checked-generation.md +11 -5
  67. package/docs/cli-reference.md +25 -5
  68. package/docs/concepts.md +4 -5
  69. package/docs/configuration.md +10 -3
  70. package/docs/corpus.md +1 -1
  71. package/docs/gateway-guide.md +104 -27
  72. package/docs/generated-code.md +12 -6
  73. package/docs/migration-playbook.md +4 -0
  74. package/docs/migration.md +134 -6
  75. package/docs/output-anatomy.md +20 -17
  76. package/docs/production.md +7 -5
  77. package/docs/start-here.md +12 -2
  78. package/docs/supported-patterns.md +23 -0
  79. package/docs/testing.md +2 -0
  80. package/docs/troubleshooting.md +3 -1
  81. package/llms.txt +9 -1
  82. package/package.json +2 -2
  83. package/src/runtime/clientStreamMethods.tpl.txt +156 -148
  84. package/src/runtime/operationsStreamHelper.tpl.txt +6 -6
  85. package/src/runtime/streamXml.ts +48 -20
@@ -4,6 +4,7 @@
4
4
  * Generates a fully-typed operations interface for the SOAP client.
5
5
  * This standalone interface enables mocking and testing without importing
6
6
  * the concrete SOAP client class or its runtime dependencies.
7
+ * @since Unreleased
7
8
  */
8
9
  import { writeGeneratedSource } from "../generation/writeGeneratedSource.js";
9
10
  import { deriveClientName, pascal } from "../util/tools.js";
@@ -31,18 +32,25 @@ export function generateOperations(outFile, compiled) {
31
32
  .map(line => line.replace(/\*\//g, "*\\/"));
32
33
  // Collect type names used in method signatures for the import statement
33
34
  const importedTypes = new Set();
35
+ const operationTypes = compiled.operations.map(operation => ({ operation,
36
+ input: operation.inputTypeName ?? (operation.inputElement ? pascal(operation.inputElement.local) : undefined),
37
+ output: operation.outputTypeName ?? (operation.outputElement ? pascal(operation.outputElement.local) : undefined),
38
+ }));
39
+ const reservedTypes = new Set(["Promise", "AsyncIterable", "AbortSignal", "StreamCallOptions", "StreamOperationResponse", `${clientName}Operations`]);
40
+ // @constraints Qualify model references when their public names overlap generated helpers or native transport types.
41
+ const namespaceModels = operationTypes.some(({ operation, input, output }) => [input, output, operation.stream?.recordTypeName].some(name => name !== undefined && reservedTypes.has(name)));
42
+ const modelType = (name) => namespaceModels ? `T.${name}` : name;
34
43
  const methods = [];
35
44
  // Does any operation opt into streaming? If so we also emit the
36
45
  // StreamOperationResponse helper type below.
37
46
  let anyStream = false;
38
- for (const op of compiled.operations) {
39
- const inTypeName = op.inputTypeName ?? (op.inputElement ? pascal(op.inputElement.local) : undefined);
40
- const outTypeName = op.outputTypeName ?? (op.outputElement ? pascal(op.outputElement.local) : undefined);
47
+ for (const { operation: op, input: inTypeName, output: outTypeName } of operationTypes) {
41
48
  if (!inTypeName && !outTypeName) {
42
49
  continue;
43
50
  }
44
- const inTs = inTypeName ?? "Record<string, unknown>";
45
- const outTs = outTypeName ?? "unknown";
51
+ // @constraints WSDL DTOs may be named Record and shadow the built-in utility type.
52
+ const inTs = inTypeName ? modelType(inTypeName) : "{[key: string]: unknown}";
53
+ const outTs = outTypeName ? modelType(outTypeName) : "unknown";
46
54
  if (inTypeName)
47
55
  importedTypes.add(inTypeName);
48
56
  if (outTypeName)
@@ -56,8 +64,9 @@ export function generateOperations(outFile, compiled) {
56
64
  const recordTs = op.stream.recordTypeName;
57
65
  importedTypes.add(recordTs);
58
66
  methods.push(`${docBlock} ${op.name}(\n` +
59
- ` args: ${inTs}\n` +
60
- ` ): Promise<StreamOperationResponse<${recordTs}>>;\n`);
67
+ ` args: ${inTs},\n` +
68
+ ` options?: StreamCallOptions\n` +
69
+ ` ): Promise<StreamOperationResponse<${modelType(recordTs)}>>;\n`);
61
70
  }
62
71
  else {
63
72
  methods.push(`${docBlock} ${op.name}(\n` +
@@ -68,16 +77,26 @@ export function generateOperations(outFile, compiled) {
68
77
  // Build sorted import list for deterministic output
69
78
  const sortedImports = Array.from(importedTypes).sort();
70
79
  const typeImport = sortedImports.length > 0
71
- ? `import type {\n${sortedImports.map((t) => ` ${t},`).join("\n")}\n} from "./types${suffix}";\n\n`
80
+ ? namespaceModels
81
+ ? `import type * as T from "./types${suffix}";\n\n`
82
+ : `import type {\n${sortedImports.map((t) => ` ${t},`).join("\n")}\n} from "./types${suffix}";\n\n`
72
83
  : "";
73
84
  // Emit the StreamOperationResponse helper type iff at least one operation
74
85
  // is stream-configured. Kept inside operations.ts so that mocks and gateway
75
86
  // code can import it without pulling in the SOAP runtime. The raw template
76
87
  // lives in a sibling .tpl file so the IDE does not try to parse embedded
77
- // TypeScript type-parameter defaults (HeadersType = Record<...>) as
78
- // JavaScript assignment expressions.
88
+ // TypeScript generic defaults as JavaScript assignment expressions.
79
89
  const streamHelper = anyStream
80
- ? loadRuntimeSource("operationsStreamHelper.tpl.txt").replace(/__CLIENT_NAME__/g, clientName)
90
+ ? `/**
91
+ * Per-call stream cancellation and total request/response deadline.
92
+ * @since Unreleased
93
+ */
94
+ export interface StreamCallOptions {
95
+ signal?: AbortSignal;
96
+ timeoutMs?: number;
97
+ }
98
+
99
+ ` + loadRuntimeSource("operationsStreamHelper.tpl.txt").replace(/__CLIENT_NAME__/g, clientName)
81
100
  : "";
82
101
  const content = `/**
83
102
  * Typed operations interface for the ${clientName} service.
@@ -48,7 +48,9 @@ export function generateUtils(outFile, compiled) {
48
48
  return max === "unbounded" || (typeof max === "number" && max > 1);
49
49
  }).sort(),
50
50
  ]));
51
- const metas = JSON.stringify({ Attributes: attrSpec, AttributeTypes: compiled.meta.attrType ?? {}, ChildrenTypes: childType, RepeatedElements: repeatedElements }, null, 2);
51
+ const metas = JSON.stringify({ Attributes: attrSpec, AttributeTypes: compiled.meta.attrType ?? {}, ChildrenTypes: childType, RepeatedElements: repeatedElements,
52
+ NonNillableElements: Object.fromEntries(Object.entries(compiled.meta.propMeta ?? {}).sort(([a], [b]) => a.localeCompare(b))
53
+ .map(([name, properties]) => [name, Object.keys(properties).filter(key => properties[key].nillable === false).sort()])) }, null, 2);
52
54
  const dataTypes = `/**
53
55
  * ${clientName} WSDL data types for proper serialization/deserialization.
54
56
  * Used to distinguish between XML attributes and elements during conversion.
@@ -62,6 +64,8 @@ export interface ${clientName}DataTypes {
62
64
  ChildrenTypes: Record<string, Readonly<Record<string, string>>>;
63
65
  /** Maps type names to repeated child properties; optional for custom metadata compatibility. */
64
66
  RepeatedElements?: Record<string, readonly string[]>;
67
+ /** Distinguishes empty complex nodes collapsed to null by node-soap from declared nullable children. */
68
+ NonNillableElements?: Record<string, readonly string[]>;
65
69
  }
66
70
 
67
71
  export const ${clientConstant}_DATA_TYPES: ${clientName}DataTypes = ${metas} as const;\n`;
@@ -1,19 +1,11 @@
1
+ /** Internal, language-neutral S04 contract. S05 resolves and composes these references. */
2
+ import type { Identity, NodeId, SymbolRole } from "./declarationIdentity.js";
3
+ export { globalId, scopedId } from "./declarationIdentity.js";
4
+ export type { Identity, NodeId, SymbolRole } from "./declarationIdentity.js";
1
5
  import type { ExpandedName, SyntaxAttribute, SyntaxElement, SyntaxSource } from "../loader/orderedSyntax.js";
2
6
  export declare const GRAPH_MODEL: "xsd10-faithful-v1";
3
7
  export declare const DEFAULT_GRAPH_NODES = 100000;
4
8
  export declare const isBuiltinType: (local: string) => boolean;
5
- export type NodeId = string;
6
- export type SymbolRole = "element" | "attribute" | "type" | "group" | "attributeGroup" | "message" | "portType" | "binding" | "service";
7
- export type Identity = Readonly<{
8
- kind: "global";
9
- role: SymbolRole;
10
- name: ExpandedName;
11
- } | {
12
- kind: "scoped";
13
- owner: NodeId;
14
- path: string;
15
- role: string;
16
- }>;
17
9
  export type GraphContext = Readonly<{
18
10
  namespaces: Readonly<Record<string, string>>;
19
11
  baseUri: string;
@@ -190,8 +182,5 @@ export declare class GraphError extends Error {
190
182
  readonly source?: SyntaxSource | undefined;
191
183
  constructor(category: "invalid-schema" | "resource-limit", message: string, source?: SyntaxSource | undefined);
192
184
  }
193
- export declare function globalId(role: SymbolRole, name: ExpandedName): NodeId;
194
- export declare function scopedId(owner: NodeId, path: string, role: string): NodeId;
195
185
  export declare function wsdlReferenceRoles(node: SyntaxElement): Record<string, SymbolRole>;
196
186
  export declare function deepFreeze<T>(value: T): T;
197
- export {};
@@ -1,5 +1,4 @@
1
- /** Internal, language-neutral S04 contract. S05 resolves and composes these references. */
2
- import { createHash } from "node:crypto";
1
+ export { globalId, scopedId } from "./declarationIdentity.js";
3
2
  import { WSDL_NAMESPACE } from "../loader/orderedSyntax.js";
4
3
  export const GRAPH_MODEL = "xsd10-faithful-v1";
5
4
  export const DEFAULT_GRAPH_NODES = 100_000;
@@ -15,12 +14,6 @@ export class GraphError extends Error {
15
14
  this.name = "GraphError";
16
15
  }
17
16
  }
18
- export function globalId(role, name) {
19
- return JSON.stringify(["global", role, name.namespace, name.local]);
20
- }
21
- export function scopedId(owner, path, role) {
22
- return "scoped:" + createHash("sha256").update(JSON.stringify([owner, path, role])).digest("hex");
23
- }
24
17
  export function wsdlReferenceRoles(node) {
25
18
  if (node.name.namespace !== WSDL_NAMESPACE)
26
19
  return {};
@@ -101,6 +101,9 @@ export declare function decodeComponentRecordXml(outer: DirectOperationContract,
101
101
  signal?: AbortSignal;
102
102
  limits?: DirectXmlLimits;
103
103
  uri?: string;
104
+ componentResponseEncoding?: {
105
+ charset?: string;
106
+ };
104
107
  selectPayload?: (root: SyntaxElement) => {
105
108
  payload: SyntaxElement;
106
109
  mapping: ComponentRecordProjection | ComponentValueMapping;
@@ -46,6 +46,8 @@ export function createComponentRecordProjection(outer, companion, supplied, opti
46
46
  try {
47
47
  const result = budget.guard(() => {
48
48
  check();
49
+ if ('parts' in outer.output || 'parts' in companion.output)
50
+ return failure('unsupported', 'record-projection-requires-document-output');
49
51
  if (outer.basis !== 'native-component-premises-v1' || companion.basis !== 'native-component-premises-v1'
50
52
  || outer.correspondence !== 'dated-XSD-2004-components-v1' || companion.correspondence !== 'dated-XSD-2004-components-v1')
51
53
  stop('component-record-source-authority');
@@ -75,6 +77,8 @@ export function createComponentRecordProjection(outer, companion, supplied, opti
75
77
  if (mapping.kind !== 'component-mapping')
76
78
  return mapping;
77
79
  const projection = inspectComponentValueMapping(mapping, companion).projection;
80
+ if (!projection)
81
+ return failure('unsupported', 'record-projection-requires-document-mapping');
78
82
  const records = new Map(projection.records.map(record => [record.type.id, record]));
79
83
  if (association && !Object.hasOwn(requested, 'recordPath')) {
80
84
  const candidates = [];
@@ -226,6 +230,8 @@ export function inspectComponentRecordValue(value, projection) {
226
230
  */
227
231
  export function validateComponentRecordApplication(projection, application, options = {}) {
228
232
  const owned = inspectComponentRecordProjection(projection), { context } = inspectDirectOperation(owned.companion);
233
+ if ('parts' in owned.mapping.root)
234
+ throw new TypeError('Record projection requires a document mapping');
229
235
  return expandProjectionValue(context, owned.mapping.root.element, application, options);
230
236
  }
231
237
  /** Decode once and validate the declared wrapper plus its explicitly selected companion payload.
@@ -253,6 +259,8 @@ export function decodeComponentRecordXml(outer, projection, bytes, options = {})
253
259
  return assessObservedComponentPayload(outer, mapping, observation, selected.payload, options);
254
260
  }
255
261
  const wrapper = selected.payload;
262
+ if ('parts' in outer.output)
263
+ throw new ScopeStop('unsupported', 'record-projection-requires-document-output');
256
264
  if (!same(wrapper.name, outer.output.element.name))
257
265
  throw new ScopeStop('mismatch', 'record-wrapper-root');
258
266
  if (wrapper.attributes.length || wrapper.children.some(node => node.kind === 'text' && /[^\t\r\n ]/.test(node.value)))
@@ -284,6 +292,8 @@ export function decodeComponentRecordXml(outer, projection, bytes, options = {})
284
292
  const decoded = assessObservedComponentPayload(owned.companion, owned.mapping, observation, payload, options);
285
293
  if (decoded.kind !== 'component-decoded')
286
294
  return decoded;
295
+ if (decoded.validated.kind !== 'projection-value')
296
+ throw new TypeError('Record projection requires a document value receipt');
287
297
  const recordCount = countRecords(observation, payload, projection.spec.recordPath, check);
288
298
  const validated = Object.freeze({ kind: 'component-record-value', scope: 'records-with-declared-wrapper-scope',
289
299
  admitted: false, value: decoded.validated.value, recordCount, companion: decoded.validated });
@@ -0,0 +1,22 @@
1
+ export type NodeId = string;
2
+ export type SymbolRole = "element" | "attribute" | "type" | "group" | "attributeGroup" | "message" | "portType" | "binding" | "service";
3
+ export type Identity = Readonly<{
4
+ kind: "global";
5
+ role: SymbolRole;
6
+ name: Readonly<{
7
+ namespace: string;
8
+ local: string;
9
+ }>;
10
+ } | {
11
+ kind: "scoped";
12
+ owner: NodeId;
13
+ path: string;
14
+ role: string;
15
+ }>;
16
+ /** Encode XML symbol identity independently of source location and emitted language names. */
17
+ export declare function globalId(role: SymbolRole, name: Readonly<{
18
+ namespace: string;
19
+ local: string;
20
+ }>): NodeId;
21
+ /** Encode a declaration within its owner's scope using the caller's structural path convention. */
22
+ export declare function scopedId(owner: NodeId, path: string, role: string): NodeId;
@@ -0,0 +1,9 @@
1
+ import { createHash } from "node:crypto";
2
+ /** Encode XML symbol identity independently of source location and emitted language names. */
3
+ export function globalId(role, name) {
4
+ return JSON.stringify(["global", role, name.namespace, name.local]);
5
+ }
6
+ /** Encode a declaration within its owner's scope using the caller's structural path convention. */
7
+ export function scopedId(owner, path, role) {
8
+ return "scoped:" + createHash("sha256").update(JSON.stringify([owner, path, role])).digest("hex");
9
+ }
@@ -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
- if (mapping.version !== profile.version || mapping.contextId !== contract.contextId
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' || profile === componentProfile && contract.binding.version === '1.2' && !rpc) || !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,21 +19,44 @@ 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;
25
46
  readonly basis: 'native-direct-premises-v1' | 'native-component-premises-v1';
26
47
  readonly correspondence: 'dated-XSD-2004-direct-v1' | 'dated-XSD-2004-components-v1';
27
48
  readonly binding: Readonly<{
28
- version: '1.1';
29
- style: 'document';
30
- use: 'literal';
49
+ version: '1.1' | '1.2';
50
+ style: 'document' | 'rpc';
51
+ use: 'literal' | 'encoded';
52
+ encoding?: string;
31
53
  transport: string;
32
54
  action: string;
55
+ actionRequired?: boolean;
33
56
  source: SyntaxSource;
34
57
  }>;
35
- readonly input: DirectMessageRoot;
36
- readonly output: DirectMessageRoot;
58
+ readonly input: DirectMessageRoot | RpcMessageRoot;
59
+ readonly output: DirectMessageRoot | RpcMessageRoot;
37
60
  readonly faults: readonly Readonly<{
38
61
  name: string;
39
62
  root: DirectMessageRoot;