@navios/adapter-xml 0.1.0 → 0.7.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/CHANGELOG.md +35 -0
- package/README.md +2 -1
- package/bun-plugin.mts +5 -3
- package/bunPlugin.cache +1 -1
- package/dist/bun-plugin.d.mts.map +1 -1
- package/dist/src/adapters/xml-stream-adapter.service.d.mts +63 -8
- package/dist/src/adapters/xml-stream-adapter.service.d.mts.map +1 -1
- package/dist/src/decorators/component.decorator.d.mts +68 -2
- package/dist/src/decorators/component.decorator.d.mts.map +1 -1
- package/dist/src/decorators/xml-stream.decorator.d.mts +58 -3
- package/dist/src/decorators/xml-stream.decorator.d.mts.map +1 -1
- package/dist/src/define-environment.d.mts +2 -2
- package/dist/src/define-environment.d.mts.map +1 -1
- package/dist/src/handlers/xml-stream.d.mts +25 -3
- package/dist/src/handlers/xml-stream.d.mts.map +1 -1
- package/dist/src/runtime/render-to-xml.d.mts +70 -2
- package/dist/src/runtime/render-to-xml.d.mts.map +1 -1
- package/dist/src/tags/define-tag.d.mts +8 -0
- package/dist/src/tags/define-tag.d.mts.map +1 -1
- package/dist/src/types/component.d.mts +46 -2
- package/dist/src/types/component.d.mts.map +1 -1
- package/dist/src/types/config.d.mts +22 -0
- package/dist/src/types/config.d.mts.map +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/tsconfig.spec.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsdown.config.d.mts +3 -0
- package/dist/tsdown.config.d.mts.map +1 -0
- package/dist/vitest.e2e.fastify.config.d.mts.map +1 -1
- package/lib/create-element--Gd2L8UR.d.cts +26 -0
- package/lib/create-element--Gd2L8UR.d.cts.map +1 -0
- package/lib/create-element-61MP7_99.d.mts +26 -0
- package/lib/create-element-61MP7_99.d.mts.map +1 -0
- package/lib/create-element-DiOt_-Vs.cjs +183 -0
- package/lib/create-element-DiOt_-Vs.cjs.map +1 -0
- package/lib/create-element-NZEYaRw5.mjs +124 -0
- package/lib/create-element-NZEYaRw5.mjs.map +1 -0
- package/lib/index.cjs +746 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +520 -0
- package/lib/index.d.cts.map +1 -0
- package/lib/index.d.mts +520 -29
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +692 -223
- package/lib/index.mjs.map +1 -1
- package/lib/jsx-dev-runtime.cjs +11 -0
- package/lib/jsx-dev-runtime.cjs.map +1 -0
- package/lib/jsx-dev-runtime.d.cts +8 -0
- package/lib/jsx-dev-runtime.d.cts.map +1 -0
- package/lib/jsx-dev-runtime.d.mts +8 -4
- package/lib/jsx-dev-runtime.d.mts.map +1 -0
- package/lib/jsx-dev-runtime.mjs +5 -6
- package/lib/jsx-dev-runtime.mjs.map +1 -1
- package/lib/jsx-runtime.cjs +5 -0
- package/lib/jsx-runtime.d.cts +3 -0
- package/lib/jsx-runtime.d.mts +3 -3
- package/lib/jsx-runtime.mjs +3 -3
- package/lib/jsx.cjs +0 -0
- package/lib/jsx.d.cts +19 -0
- package/lib/jsx.d.cts.map +1 -0
- package/lib/jsx.d.mts +19 -1
- package/lib/jsx.d.mts.map +1 -0
- package/lib/jsx.mjs +1 -3
- package/lib/xml-node-BQA44ydQ.d.mts +96 -0
- package/lib/xml-node-BQA44ydQ.d.mts.map +1 -0
- package/lib/xml-node-CddziLym.d.cts +96 -0
- package/lib/xml-node-CddziLym.d.cts.map +1 -0
- package/package.json +20 -20
- package/project.json +2 -2
- package/src/adapters/xml-stream-adapter.service.mts +77 -16
- package/src/decorators/component.decorator.mts +72 -10
- package/src/decorators/component.decorator.spec.mts +40 -22
- package/src/decorators/xml-stream.decorator.mts +58 -3
- package/src/define-environment.mts +5 -6
- package/src/handlers/xml-stream.mts +25 -3
- package/src/runtime/render-to-xml.mts +72 -4
- package/src/tags/define-tag.mts +8 -0
- package/src/types/component.mts +46 -2
- package/src/types/config.mts +22 -0
- package/tsdown.config.mts +39 -0
- package/dist/tsup.config.d.mts +0 -3
- package/dist/tsup.config.d.mts.map +0 -1
- package/lib/_tsup-dts-rollup.d.mts +0 -414
- package/lib/_tsup-dts-rollup.d.ts +0 -414
- package/lib/chunk-6OR6LGJA.mjs +0 -153
- package/lib/chunk-6OR6LGJA.mjs.map +0 -1
- package/lib/index.d.ts +0 -29
- package/lib/index.js +0 -376
- package/lib/index.js.map +0 -1
- package/lib/jsx-dev-runtime.d.ts +0 -4
- package/lib/jsx-dev-runtime.js +0 -61
- package/lib/jsx-dev-runtime.js.map +0 -1
- package/lib/jsx-runtime.d.ts +0 -3
- package/lib/jsx-runtime.js +0 -57
- package/lib/jsx-runtime.js.map +0 -1
- package/lib/jsx-runtime.mjs.map +0 -1
- package/lib/jsx.d.ts +0 -1
- package/lib/jsx.js +0 -4
- package/lib/jsx.js.map +0 -1
- package/lib/jsx.mjs.map +0 -1
- package/tsup.config.mts +0 -18
|
@@ -1,33 +1,74 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AbstractHttpHandlerAdapterInterface,
|
|
3
|
+
ClassType,
|
|
3
4
|
HandlerMetadata,
|
|
5
|
+
ScopedContainer,
|
|
4
6
|
} from '@navios/core'
|
|
5
|
-
import type { ClassType, RequestContextHolder } from '@navios/di'
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
8
|
+
import {
|
|
9
|
+
inject,
|
|
10
|
+
Injectable,
|
|
11
|
+
StreamAdapterToken,
|
|
12
|
+
XmlStreamAdapterToken,
|
|
13
|
+
} from '@navios/core'
|
|
9
14
|
|
|
10
15
|
import type { BaseXmlStreamConfig } from '../types/config.mjs'
|
|
11
16
|
import type { AnyXmlNode } from '../types/xml-node.mjs'
|
|
12
17
|
|
|
13
18
|
import { renderToXml } from '../runtime/render-to-xml.mjs'
|
|
14
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Adapter service for handling XML Stream endpoints in Navios.
|
|
22
|
+
*
|
|
23
|
+
* This service integrates with the base stream adapter (Fastify or Bun) to handle
|
|
24
|
+
* XML endpoints that return JSX-based XML responses. It automatically renders JSX
|
|
25
|
+
* nodes to XML strings and sends them with the appropriate Content-Type headers.
|
|
26
|
+
*
|
|
27
|
+
* The service supports:
|
|
28
|
+
* - Async components (resolved in parallel)
|
|
29
|
+
* - Class components (resolved via DI container)
|
|
30
|
+
* - Regular JSX elements
|
|
31
|
+
* - CDATA sections
|
|
32
|
+
* - Raw XML content
|
|
33
|
+
*
|
|
34
|
+
* @implements {AbstractHttpHandlerAdapterInterface}
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* // This service is automatically registered when using defineXmlEnvironment()
|
|
39
|
+
* // and is used by endpoints decorated with @XmlStream()
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
15
42
|
@Injectable({
|
|
16
43
|
token: XmlStreamAdapterToken,
|
|
17
44
|
})
|
|
18
45
|
export class XmlStreamAdapterService implements AbstractHttpHandlerAdapterInterface {
|
|
19
|
-
protected container = inject(Container)
|
|
20
46
|
/** Base stream adapter - we proxy hasSchema, prepareArguments, provideSchema to it */
|
|
21
47
|
protected streamAdapter = inject(StreamAdapterToken)
|
|
22
48
|
|
|
23
49
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
50
|
+
* Prepares argument getters for parsing request data.
|
|
51
|
+
*
|
|
52
|
+
* Proxies to the base stream adapter to reuse existing argument preparation logic
|
|
53
|
+
* that handles query parameters, request body, and URL parameters for both
|
|
54
|
+
* Fastify and Bun adapters.
|
|
55
|
+
*
|
|
56
|
+
* @param handlerMetadata - The handler metadata with schemas and configuration.
|
|
57
|
+
* @returns An array of getter functions that populate request arguments.
|
|
26
58
|
*/
|
|
27
59
|
prepareArguments(handlerMetadata: HandlerMetadata<BaseXmlStreamConfig>) {
|
|
28
60
|
return this.streamAdapter.prepareArguments?.(handlerMetadata) ?? []
|
|
29
61
|
}
|
|
30
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Provides schema information for the handler.
|
|
65
|
+
*
|
|
66
|
+
* Proxies to the base stream adapter to reuse existing schema generation logic.
|
|
67
|
+
* For Fastify, this enables built-in validation. For Bun, this returns an empty object.
|
|
68
|
+
*
|
|
69
|
+
* @param handlerMetadata - The handler metadata containing configuration and schemas.
|
|
70
|
+
* @returns Schema information (Fastify route schema or empty object for Bun).
|
|
71
|
+
*/
|
|
31
72
|
provideSchema(
|
|
32
73
|
handlerMetadata: HandlerMetadata<BaseXmlStreamConfig>,
|
|
33
74
|
): Record<string, any> {
|
|
@@ -40,6 +81,14 @@ export class XmlStreamAdapterService implements AbstractHttpHandlerAdapterInterf
|
|
|
40
81
|
return {}
|
|
41
82
|
}
|
|
42
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Checks if the handler has any validation schemas defined.
|
|
86
|
+
*
|
|
87
|
+
* Proxies to the base stream adapter to check for query or request schemas.
|
|
88
|
+
*
|
|
89
|
+
* @param handlerMetadata - The handler metadata containing configuration.
|
|
90
|
+
* @returns `true` if the handler has any schemas (query or request).
|
|
91
|
+
*/
|
|
43
92
|
hasSchema(handlerMetadata: HandlerMetadata<any>): boolean {
|
|
44
93
|
if (
|
|
45
94
|
'hasSchema' in this.streamAdapter &&
|
|
@@ -51,12 +100,25 @@ export class XmlStreamAdapterService implements AbstractHttpHandlerAdapterInterf
|
|
|
51
100
|
}
|
|
52
101
|
|
|
53
102
|
/**
|
|
54
|
-
*
|
|
103
|
+
* Creates a request handler function for XML Stream endpoints.
|
|
104
|
+
*
|
|
105
|
+
* This method generates a handler that:
|
|
106
|
+
* 1. Parses and validates request data (body, query, URL params) using the base adapter
|
|
107
|
+
* 2. Invokes the controller method with validated arguments (returns JSX)
|
|
108
|
+
* 3. Renders the JSX tree to XML string (resolves async and class components)
|
|
109
|
+
* 4. Sends the XML response with appropriate Content-Type header
|
|
110
|
+
*
|
|
111
|
+
* The handler automatically detects the environment (Fastify vs Bun) and uses the
|
|
112
|
+
* appropriate response mechanism (reply object vs Response object).
|
|
113
|
+
*
|
|
114
|
+
* @param controller - The controller class containing the handler method.
|
|
115
|
+
* @param handlerMetadata - The handler metadata with configuration and schemas.
|
|
116
|
+
* @returns A function that handles incoming requests and sends XML responses.
|
|
55
117
|
*/
|
|
56
118
|
provideHandler(
|
|
57
119
|
controller: ClassType,
|
|
58
120
|
handlerMetadata: HandlerMetadata<BaseXmlStreamConfig>,
|
|
59
|
-
): (context:
|
|
121
|
+
): (context: ScopedContainer, request: any, reply: any) => Promise<any> {
|
|
60
122
|
const getters = this.prepareArguments(handlerMetadata)
|
|
61
123
|
const config = handlerMetadata.config
|
|
62
124
|
|
|
@@ -74,14 +136,9 @@ export class XmlStreamAdapterService implements AbstractHttpHandlerAdapterInterf
|
|
|
74
136
|
}
|
|
75
137
|
|
|
76
138
|
const contentType = config.contentType ?? 'application/xml'
|
|
77
|
-
const renderOptions = {
|
|
78
|
-
declaration: config.xmlDeclaration ?? true,
|
|
79
|
-
encoding: config.encoding ?? 'UTF-8',
|
|
80
|
-
container: this.container,
|
|
81
|
-
}
|
|
82
139
|
|
|
83
|
-
return async (context:
|
|
84
|
-
const controllerInstance = await
|
|
140
|
+
return async (context: ScopedContainer, request: any, reply: any) => {
|
|
141
|
+
const controllerInstance = await context.get(controller)
|
|
85
142
|
const argument = await formatArguments(request)
|
|
86
143
|
|
|
87
144
|
// Call controller method - returns XmlNode (JSX), may contain async/class components
|
|
@@ -89,7 +146,11 @@ export class XmlStreamAdapterService implements AbstractHttpHandlerAdapterInterf
|
|
|
89
146
|
await controllerInstance[handlerMetadata.classMethod](argument)
|
|
90
147
|
|
|
91
148
|
// Render JSX to XML string (async - resolves all async and class components)
|
|
92
|
-
const xml = await renderToXml(xmlNode,
|
|
149
|
+
const xml = await renderToXml(xmlNode, {
|
|
150
|
+
declaration: config.xmlDeclaration ?? true,
|
|
151
|
+
encoding: config.encoding ?? 'UTF-8',
|
|
152
|
+
container: context,
|
|
153
|
+
})
|
|
93
154
|
|
|
94
155
|
// Environment detection: Bun doesn't have reply
|
|
95
156
|
const isHttpStandardEnvironment = reply === undefined
|
|
@@ -1,19 +1,70 @@
|
|
|
1
|
+
import type { Registry } from '@navios/core'
|
|
1
2
|
import type { z, ZodObject, ZodRawShape } from 'zod/v4'
|
|
2
3
|
|
|
3
|
-
import type { Registry } from '@navios/di'
|
|
4
|
-
|
|
5
4
|
import {
|
|
5
|
+
globalRegistry,
|
|
6
6
|
InjectableScope,
|
|
7
|
-
InjectableType,
|
|
8
7
|
InjectableTokenMeta,
|
|
8
|
+
InjectableType,
|
|
9
9
|
InjectionToken,
|
|
10
|
-
|
|
11
|
-
} from '@navios/di'
|
|
10
|
+
} from '@navios/core'
|
|
12
11
|
|
|
13
12
|
import type { ComponentClass, XmlComponent } from '../types/component.mjs'
|
|
14
13
|
|
|
15
14
|
export const ComponentMeta = Symbol.for('xml.component.meta')
|
|
16
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Decorator for class-based XML components with dependency injection support.
|
|
18
|
+
*
|
|
19
|
+
* Class components must implement the `XmlComponent` interface with a `render()` method.
|
|
20
|
+
* They can optionally accept props via constructor, validated with a Zod schema.
|
|
21
|
+
*
|
|
22
|
+
* @overload
|
|
23
|
+
* Component without props (no schema).
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* @Component()
|
|
28
|
+
* class LatestPostsComponent implements XmlComponent {
|
|
29
|
+
* private readonly postService = inject(PostService)
|
|
30
|
+
*
|
|
31
|
+
* async render() {
|
|
32
|
+
* const posts = await this.postService.getLatestPosts()
|
|
33
|
+
* return <>{posts.map(post => <item>...</item>)}</>
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @overload
|
|
39
|
+
* Component with props schema for type-safe props.
|
|
40
|
+
*
|
|
41
|
+
* @param options - Configuration object with schema and optional registry.
|
|
42
|
+
* @param options.schema - Zod schema for validating and typing component props.
|
|
43
|
+
* @param options.registry - Optional custom DI registry (defaults to global registry).
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* const DescriptionSchema = z.object({
|
|
48
|
+
* content: z.string(),
|
|
49
|
+
* wrapInCData: z.boolean().optional(),
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* @Component({ schema: DescriptionSchema })
|
|
53
|
+
* class DescriptionComponent implements XmlComponent {
|
|
54
|
+
* constructor(private props: z.output<typeof DescriptionSchema>) {}
|
|
55
|
+
*
|
|
56
|
+
* async render() {
|
|
57
|
+
* return <description>{this.props.wrapInCData ? <CData>{this.props.content}</CData> : this.props.content}</description>
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @overload
|
|
63
|
+
* Component with custom registry only (no props).
|
|
64
|
+
*
|
|
65
|
+
* @param options - Configuration object with registry.
|
|
66
|
+
* @param options.registry - Custom DI registry to use for this component.
|
|
67
|
+
*/
|
|
17
68
|
// #1 Component without props (no schema)
|
|
18
69
|
export function Component(): <T extends ComponentClass>(
|
|
19
70
|
target: T,
|
|
@@ -32,10 +83,7 @@ export function Component<Schema extends ZodObject<ZodRawShape>>(options: {
|
|
|
32
83
|
// #3 Component with custom registry only
|
|
33
84
|
export function Component(options: {
|
|
34
85
|
registry: Registry
|
|
35
|
-
}): <T extends ComponentClass>(
|
|
36
|
-
target: T,
|
|
37
|
-
context?: ClassDecoratorContext,
|
|
38
|
-
) => T
|
|
86
|
+
}): <T extends ComponentClass>(target: T, context?: ClassDecoratorContext) => T
|
|
39
87
|
|
|
40
88
|
export function Component(
|
|
41
89
|
options: {
|
|
@@ -91,7 +139,21 @@ export function Component(
|
|
|
91
139
|
}
|
|
92
140
|
|
|
93
141
|
/**
|
|
94
|
-
* Type guard to check if a
|
|
142
|
+
* Type guard to check if a value is a component class.
|
|
143
|
+
*
|
|
144
|
+
* Component classes are classes decorated with `@Component` that implement
|
|
145
|
+
* the `XmlComponent` interface.
|
|
146
|
+
*
|
|
147
|
+
* @param value - The value to check.
|
|
148
|
+
* @returns `true` if the value is a component class, `false` otherwise.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* if (isComponentClass(MyClass)) {
|
|
153
|
+
* // MyClass is a component class
|
|
154
|
+
* const instance = await container.get(MyClass)
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
95
157
|
*/
|
|
96
158
|
export function isComponentClass(value: unknown): value is ComponentClass {
|
|
97
159
|
return (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Container, Registry } from '@navios/
|
|
1
|
+
import { Container, Registry } from '@navios/core'
|
|
2
2
|
|
|
3
3
|
import { describe, expect, it } from 'vitest'
|
|
4
4
|
import { z } from 'zod/v4'
|
|
@@ -152,13 +152,16 @@ describe('class component rendering', () => {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
const container = new Container()
|
|
155
|
-
container.beginRequest('test-request')
|
|
155
|
+
const requestContainer = container.beginRequest('test-request')
|
|
156
156
|
try {
|
|
157
157
|
const node = createElement(SimpleComponent, null)
|
|
158
|
-
const xml = await renderToXml(node, {
|
|
158
|
+
const xml = await renderToXml(node, {
|
|
159
|
+
declaration: false,
|
|
160
|
+
container: requestContainer,
|
|
161
|
+
})
|
|
159
162
|
expect(xml).toBe('<simple>content</simple>')
|
|
160
163
|
} finally {
|
|
161
|
-
await
|
|
164
|
+
await requestContainer.endRequest()
|
|
162
165
|
}
|
|
163
166
|
})
|
|
164
167
|
|
|
@@ -177,13 +180,16 @@ describe('class component rendering', () => {
|
|
|
177
180
|
}
|
|
178
181
|
|
|
179
182
|
const container = new Container()
|
|
180
|
-
container.beginRequest('test-request')
|
|
183
|
+
const requestContainer = container.beginRequest('test-request')
|
|
181
184
|
try {
|
|
182
185
|
const node = createElement(Greeting, { name: 'World' })
|
|
183
|
-
const xml = await renderToXml(node, {
|
|
186
|
+
const xml = await renderToXml(node, {
|
|
187
|
+
declaration: false,
|
|
188
|
+
container: requestContainer,
|
|
189
|
+
})
|
|
184
190
|
expect(xml).toBe('<greeting>Hello, World!</greeting>')
|
|
185
191
|
} finally {
|
|
186
|
-
await
|
|
192
|
+
await requestContainer.endRequest()
|
|
187
193
|
}
|
|
188
194
|
})
|
|
189
195
|
|
|
@@ -203,13 +209,16 @@ describe('class component rendering', () => {
|
|
|
203
209
|
}
|
|
204
210
|
|
|
205
211
|
const container = new Container()
|
|
206
|
-
container.beginRequest('test-request')
|
|
212
|
+
const requestContainer = container.beginRequest('test-request')
|
|
207
213
|
try {
|
|
208
214
|
const node = createElement(Outer, null)
|
|
209
|
-
const xml = await renderToXml(node, {
|
|
215
|
+
const xml = await renderToXml(node, {
|
|
216
|
+
declaration: false,
|
|
217
|
+
container: requestContainer,
|
|
218
|
+
})
|
|
210
219
|
expect(xml).toBe('<outer><inner>nested</inner></outer>')
|
|
211
220
|
} finally {
|
|
212
|
-
await
|
|
221
|
+
await requestContainer.endRequest()
|
|
213
222
|
}
|
|
214
223
|
})
|
|
215
224
|
|
|
@@ -223,13 +232,16 @@ describe('class component rendering', () => {
|
|
|
223
232
|
}
|
|
224
233
|
|
|
225
234
|
const container = new Container()
|
|
226
|
-
container.beginRequest('test-request')
|
|
235
|
+
const requestContainer = container.beginRequest('test-request')
|
|
227
236
|
try {
|
|
228
237
|
const node = createElement(AsyncComponent, null)
|
|
229
|
-
const xml = await renderToXml(node, {
|
|
238
|
+
const xml = await renderToXml(node, {
|
|
239
|
+
declaration: false,
|
|
240
|
+
container: requestContainer,
|
|
241
|
+
})
|
|
230
242
|
expect(xml).toBe('<async>loaded</async>')
|
|
231
243
|
} finally {
|
|
232
|
-
await
|
|
244
|
+
await requestContainer.endRequest()
|
|
233
245
|
}
|
|
234
246
|
})
|
|
235
247
|
|
|
@@ -263,13 +275,13 @@ describe('class component rendering', () => {
|
|
|
263
275
|
}
|
|
264
276
|
|
|
265
277
|
const container = new Container()
|
|
266
|
-
container.beginRequest('test-request')
|
|
278
|
+
const requestContainer = container.beginRequest('test-request')
|
|
267
279
|
try {
|
|
268
280
|
// Valid props
|
|
269
281
|
const validNode = createElement(StrictComponent, { count: 5 })
|
|
270
282
|
const xml = await renderToXml(validNode, {
|
|
271
283
|
declaration: false,
|
|
272
|
-
container,
|
|
284
|
+
container: requestContainer,
|
|
273
285
|
})
|
|
274
286
|
expect(xml).toBe('<count>5</count>')
|
|
275
287
|
|
|
@@ -279,7 +291,7 @@ describe('class component rendering', () => {
|
|
|
279
291
|
renderToXml(invalidNode, { declaration: false, container }),
|
|
280
292
|
).rejects.toThrow()
|
|
281
293
|
} finally {
|
|
282
|
-
await
|
|
294
|
+
await requestContainer.endRequest()
|
|
283
295
|
}
|
|
284
296
|
})
|
|
285
297
|
|
|
@@ -296,7 +308,7 @@ describe('class component rendering', () => {
|
|
|
296
308
|
}
|
|
297
309
|
|
|
298
310
|
const container = new Container()
|
|
299
|
-
container.beginRequest('test-request')
|
|
311
|
+
const requestContainer = container.beginRequest('test-request')
|
|
300
312
|
try {
|
|
301
313
|
const node = createElement(
|
|
302
314
|
'page',
|
|
@@ -304,12 +316,15 @@ describe('class component rendering', () => {
|
|
|
304
316
|
createElement(Header, { title: 'Welcome' }),
|
|
305
317
|
createElement(Footer, null),
|
|
306
318
|
)
|
|
307
|
-
const xml = await renderToXml(node, {
|
|
319
|
+
const xml = await renderToXml(node, {
|
|
320
|
+
declaration: false,
|
|
321
|
+
container: requestContainer,
|
|
322
|
+
})
|
|
308
323
|
expect(xml).toBe(
|
|
309
324
|
'<page><header>Welcome</header><footer>Copyright 2025</footer></page>',
|
|
310
325
|
)
|
|
311
326
|
} finally {
|
|
312
|
-
await
|
|
327
|
+
await requestContainer.endRequest()
|
|
313
328
|
}
|
|
314
329
|
})
|
|
315
330
|
|
|
@@ -328,7 +343,7 @@ describe('class component rendering', () => {
|
|
|
328
343
|
}
|
|
329
344
|
|
|
330
345
|
const container = new Container()
|
|
331
|
-
container.beginRequest('test-request')
|
|
346
|
+
const requestContainer = container.beginRequest('test-request')
|
|
332
347
|
try {
|
|
333
348
|
const node = createElement(
|
|
334
349
|
Wrapper,
|
|
@@ -336,10 +351,13 @@ describe('class component rendering', () => {
|
|
|
336
351
|
createElement('child1', null),
|
|
337
352
|
createElement('child2', null),
|
|
338
353
|
)
|
|
339
|
-
const xml = await renderToXml(node, {
|
|
354
|
+
const xml = await renderToXml(node, {
|
|
355
|
+
declaration: false,
|
|
356
|
+
container: requestContainer,
|
|
357
|
+
})
|
|
340
358
|
expect(xml).toBe('<wrapper><child1/><child2/></wrapper>')
|
|
341
359
|
} finally {
|
|
342
|
-
await
|
|
360
|
+
await requestContainer.endRequest()
|
|
343
361
|
}
|
|
344
362
|
})
|
|
345
363
|
})
|
|
@@ -5,6 +5,28 @@ import { getEndpointMetadata, XmlStreamAdapterToken } from '@navios/core'
|
|
|
5
5
|
|
|
6
6
|
import type { BaseXmlStreamConfig } from '../types/config.mjs'
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Type helper that extracts the parameter types for an XML Stream endpoint handler.
|
|
10
|
+
*
|
|
11
|
+
* This type automatically infers the correct parameter types based on the endpoint
|
|
12
|
+
* configuration, including URL parameters, query parameters, and request body.
|
|
13
|
+
*
|
|
14
|
+
* @template EndpointDeclaration - The endpoint declaration type from `declareXmlStream`.
|
|
15
|
+
* @template Url - The URL path pattern.
|
|
16
|
+
* @template QuerySchema - The query parameter schema type.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const getFeed = declareXmlStream({
|
|
21
|
+
* method: 'GET',
|
|
22
|
+
* url: '/feed/:category',
|
|
23
|
+
* querySchema: z.object({ page: z.string() }),
|
|
24
|
+
* })
|
|
25
|
+
*
|
|
26
|
+
* // XmlStreamParams<typeof getFeed> resolves to:
|
|
27
|
+
* // { urlParams: { category: string }, query: { page: string } }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
8
30
|
export type XmlStreamParams<
|
|
9
31
|
EndpointDeclaration extends {
|
|
10
32
|
config: BaseXmlStreamConfig<any, any, any, any>
|
|
@@ -22,16 +44,30 @@ export type XmlStreamParams<
|
|
|
22
44
|
/**
|
|
23
45
|
* Decorator for XML Stream endpoints that return JSX-based XML responses.
|
|
24
46
|
*
|
|
47
|
+
* This decorator marks controller methods that return JSX elements, which will be
|
|
48
|
+
* automatically rendered to XML and sent with the appropriate Content-Type header.
|
|
49
|
+
* The method can be async and can contain async components, class components, and
|
|
50
|
+
* regular JSX elements.
|
|
51
|
+
*
|
|
52
|
+
* @template Method - The HTTP method (GET, POST, etc.).
|
|
53
|
+
* @template Url - The URL path pattern (supports parameters like `/posts/:id`).
|
|
54
|
+
* @template QuerySchema - Optional Zod schema for query parameter validation.
|
|
55
|
+
* @template RequestSchema - Optional Zod schema for request body validation.
|
|
56
|
+
*
|
|
57
|
+
* @param endpoint - The endpoint declaration created with `declareXmlStream`.
|
|
58
|
+
* @returns A method decorator function.
|
|
59
|
+
*
|
|
60
|
+
* @throws {Error} If used on a non-function or non-method.
|
|
61
|
+
* @throws {Error} If the endpoint URL already exists.
|
|
62
|
+
*
|
|
25
63
|
* @example
|
|
26
64
|
* ```typescript
|
|
27
|
-
* import { XmlStream } from '@navios/adapter-xml'
|
|
65
|
+
* import { XmlStream, declareXmlStream } from '@navios/adapter-xml'
|
|
28
66
|
* import { Controller } from '@navios/core'
|
|
29
67
|
*
|
|
30
68
|
* const getRssFeed = declareXmlStream({
|
|
31
69
|
* method: 'GET',
|
|
32
70
|
* url: '/feed.xml',
|
|
33
|
-
* querySchema: undefined,
|
|
34
|
-
* requestSchema: undefined,
|
|
35
71
|
* contentType: 'application/rss+xml',
|
|
36
72
|
* })
|
|
37
73
|
*
|
|
@@ -49,6 +85,25 @@ export type XmlStreamParams<
|
|
|
49
85
|
* }
|
|
50
86
|
* }
|
|
51
87
|
* ```
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* // With query parameters
|
|
92
|
+
* const getSitemap = declareXmlStream({
|
|
93
|
+
* method: 'GET',
|
|
94
|
+
* url: '/sitemap.xml',
|
|
95
|
+
* querySchema: z.object({ page: z.string().optional() }),
|
|
96
|
+
* })
|
|
97
|
+
*
|
|
98
|
+
* @Controller()
|
|
99
|
+
* class SitemapController {
|
|
100
|
+
* @XmlStream(getSitemap)
|
|
101
|
+
* async getSitemap(params: { query?: { page?: string } }) {
|
|
102
|
+
* const page = params.query?.page
|
|
103
|
+
* return <urlset>...</urlset>
|
|
104
|
+
* }
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
52
107
|
*/
|
|
53
108
|
export function XmlStream<
|
|
54
109
|
Method extends HttpMethod = HttpMethod,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { AnyInjectableType } from '@navios/
|
|
1
|
+
import type { AnyInjectableType } from '@navios/core'
|
|
2
2
|
|
|
3
|
-
import { XmlStreamAdapterToken } from '@navios/core'
|
|
4
|
-
import { InjectionToken } from '@navios/di'
|
|
3
|
+
import { InjectionToken, XmlStreamAdapterToken } from '@navios/core'
|
|
5
4
|
|
|
6
5
|
import { XmlStreamAdapterService } from './adapters/index.mjs'
|
|
7
6
|
|
|
@@ -31,9 +30,9 @@ import { XmlStreamAdapterService } from './adapters/index.mjs'
|
|
|
31
30
|
* ```
|
|
32
31
|
*/
|
|
33
32
|
export function defineXmlEnvironment() {
|
|
34
|
-
const httpTokens = new Map<InjectionToken<any, undefined>, AnyInjectableType>(
|
|
35
|
-
[XmlStreamAdapterToken, XmlStreamAdapterService],
|
|
36
|
-
|
|
33
|
+
const httpTokens = new Map<InjectionToken<any, undefined>, AnyInjectableType>(
|
|
34
|
+
[[XmlStreamAdapterToken, XmlStreamAdapterService]],
|
|
35
|
+
)
|
|
37
36
|
return {
|
|
38
37
|
httpTokens,
|
|
39
38
|
}
|
|
@@ -3,19 +3,41 @@ import type { HttpMethod } from '@navios/builder'
|
|
|
3
3
|
import type { BaseXmlStreamConfig } from '../types/config.mjs'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Declares an XML Stream endpoint configuration for use with
|
|
6
|
+
* Declares an XML Stream endpoint configuration for use with `@XmlStream` decorator.
|
|
7
|
+
*
|
|
8
|
+
* This function creates an endpoint declaration that can be used with the `@XmlStream`
|
|
9
|
+
* decorator to mark controller methods that return JSX-based XML responses.
|
|
10
|
+
*
|
|
11
|
+
* @template Method - The HTTP method (GET, POST, etc.).
|
|
12
|
+
* @template Url - The URL path pattern (supports parameters like `/posts/:id`).
|
|
13
|
+
* @template QuerySchema - Optional Zod schema for query parameter validation.
|
|
14
|
+
* @template RequestSchema - Optional Zod schema for request body validation.
|
|
15
|
+
*
|
|
16
|
+
* @param config - The endpoint configuration including method, URL, schemas, and XML options.
|
|
17
|
+
* @returns An endpoint declaration object to be used with `@XmlStream` decorator.
|
|
7
18
|
*
|
|
8
19
|
* @example
|
|
9
20
|
* ```typescript
|
|
10
21
|
* import { declareXmlStream } from '@navios/adapter-xml'
|
|
22
|
+
* import { z } from 'zod/v4'
|
|
11
23
|
*
|
|
24
|
+
* // Simple endpoint
|
|
12
25
|
* export const getRssFeed = declareXmlStream({
|
|
13
26
|
* method: 'GET',
|
|
14
27
|
* url: '/feed.xml',
|
|
15
|
-
* querySchema: undefined,
|
|
16
|
-
* requestSchema: undefined,
|
|
17
28
|
* contentType: 'application/rss+xml',
|
|
29
|
+
* })
|
|
30
|
+
*
|
|
31
|
+
* // With query parameters
|
|
32
|
+
* export const getSitemap = declareXmlStream({
|
|
33
|
+
* method: 'GET',
|
|
34
|
+
* url: '/sitemap.xml',
|
|
35
|
+
* querySchema: z.object({
|
|
36
|
+
* page: z.string().optional(),
|
|
37
|
+
* }),
|
|
38
|
+
* contentType: 'application/xml',
|
|
18
39
|
* xmlDeclaration: true,
|
|
40
|
+
* encoding: 'UTF-8',
|
|
19
41
|
* })
|
|
20
42
|
* ```
|
|
21
43
|
*/
|