I am observing weird behavior during dereference, which I am wary to call a 'bug', because most probably it's a result of JS limitations and not an algorithmic flaw.
an additional property is saved both to objects BEING dereferenced and THAT CONTAINS information attached in place of a $ref.
dereferenced YAML
```yaml
asyncapi: 3.0.0
info:
title: Example Service
version: 1.0.0
description: Example Service.
channels:
commentLikedChannel:
address: comment/liked
x-origin: ./channels.yaml#/channels/commentLikedChannel
userSignedup:
address: user/signedup
messages:
userSignedUpMessage:
payload:
type: object
properties:
displayName:
type: string
description: Name of the user
email:
type: string
format: email
description: Email of the user
x-origin: ./messages.yaml#/messages/UserSignedUp
test:
address: /test
messages:
testMessage:
payload: # | <-- object from below,
type: string # | <-- dereferencing is done correctly
x-origin: '#/components/messages/TestMessage' # | this property is expected to be added
operations:
UserSignedUp:
action: send
channel:
$ref: '#/channels/userSignedup'
messages:
- $ref: '#/channels/userSignedup/messages/userSignedUpMessage'
TestOpp:
action: send
channel:
$ref: '#/channels/test'
messages:
- $ref: '#/channels/test/messages/testMessage'
components:
messages:
TestMessage:
payload:
type: string
x-origin: '#/components/messages/TestMessage' # <-- but this property shouldn't be here after dereference of the above object
```
I suspect the reason for such behavior is that the replacement-value is passed to the object being dereferenced by reference and not by value. I think so, because I tried changing the names of both objects so they are not the same (to exclude the possibility that the second object is hitting a cache entry in a Set() and thus is being served back the same value) and changes to one object are still mirrored to the other one.
I have traced this behavior up to the crawl() function. Before it, the YAML was still intact, and after it, it already contained this unexpected change.
If my guess of passing an object by reference gets confirmed, I would suggest replacing objects' assignments with cloning using the ECMAScript 2022's structuredClone() method that creates a deep clone of a given value using the structured clone algorithm.
I have to differentiate internal JSON Pointers into those that SHOULD be dereferenced and those that SHOULD NOT, so I use dereference with conditions instead of unconditional bundle.
(x-origin is a property containing the historical value of a dereferenced $ref, meant mainly for humans' reference where the $ref came from, thus it's not important for the story)
I am observing weird behavior during dereference, which I am wary to call a 'bug', because most probably it's a result of JS limitations and not an algorithmic flaw.
In a
YAML
```yaml asyncapi: 3.0.0 info: title: Example Service version: 1.0.0 description: Example Service. channels: commentLikedChannel: $ref: './test/integration/bundle/channels.yaml#/channels/commentLikedChannel' userSignedup: address: 'user/signedup' messages: userSignedUpMessage: $ref: './test/integration/bundle/messages.yaml#/messages/UserSignedUp' test: address: '/test' messages: testMessage: $ref: '#/components/messages/TestMessage' operations: UserSignedUp: action: send channel: $ref: '#/channels/userSignedup' messages: - $ref: '#/channels/userSignedup/messages/userSignedUpMessage' TestOpp: action: send channel: $ref: '#/channels/test' messages: - $ref: '#/channels/test/messages/testMessage' components: messages: TestMessage: payload: type: string ```an additional property is saved both to objects BEING dereferenced and THAT CONTAINS information attached in place of a
$ref
.dereferenced YAML
```yaml asyncapi: 3.0.0 info: title: Example Service version: 1.0.0 description: Example Service. channels: commentLikedChannel: address: comment/liked x-origin: ./channels.yaml#/channels/commentLikedChannel userSignedup: address: user/signedup messages: userSignedUpMessage: payload: type: object properties: displayName: type: string description: Name of the user email: type: string format: email description: Email of the user x-origin: ./messages.yaml#/messages/UserSignedUp test: address: /test messages: testMessage: payload: # | <-- object from below, type: string # | <-- dereferencing is done correctly x-origin: '#/components/messages/TestMessage' # | this property is expected to be added operations: UserSignedUp: action: send channel: $ref: '#/channels/userSignedup' messages: - $ref: '#/channels/userSignedup/messages/userSignedUpMessage' TestOpp: action: send channel: $ref: '#/channels/test' messages: - $ref: '#/channels/test/messages/testMessage' components: messages: TestMessage: payload: type: string x-origin: '#/components/messages/TestMessage' # <-- but this property shouldn't be here after dereference of the above object ```I suspect the reason for such behavior is that the replacement-value is passed to the object being dereferenced by reference and not by value. I think so, because I tried changing the names of both objects so they are not the same (to exclude the possibility that the second object is hitting a cache entry in a
Set()
and thus is being served back the same value) and changes to one object are still mirrored to the other one.I have traced this behavior up to the
crawl()
function. Before it, the YAML was still intact, and after it, it already contained this unexpected change.If my guess of passing an object by reference gets confirmed, I would suggest replacing objects' assignments with cloning using the ECMAScript 2022's
structuredClone()
method that creates a deep clone of a given value using the structured clone algorithm.The code used for dereferencing: https://github.com/asyncapi/bundler/blob/master/src/parser.ts#L45
I have to differentiate internal JSON Pointers into those that SHOULD be dereferenced and those that SHOULD NOT, so I use
dereference
with conditions instead of unconditionalbundle
.(
x-origin
is a property containing the historical value of a dereferenced$ref
, meant mainly for humans' reference where the$ref
came from, thus it's not important for the story)