@naylence/runtime 0.3.5-test.950 → 0.3.5-test.952

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.
@@ -51,14 +51,12 @@ function serializeTransportFrame(frame) {
51
51
  return serializable;
52
52
  }
53
53
  /**
54
- * Unwrap a transport frame, validating source and destination
54
+ * Unwrap a transport frame (pure deserializer - no filtering)
55
55
  *
56
56
  * @param raw - Raw data from the bus
57
- * @param localNodeId - This connector's node ID
58
- * @param remoteNodeId - Expected remote node ID
59
- * @returns Unwrapped payload if frame is valid and addressed to us, null otherwise
57
+ * @returns Unwrapped frame with payload as Uint8Array, or null if invalid structure
60
58
  */
61
- function unwrapTransportFrame(raw, localNodeId, remoteNodeId) {
59
+ function unwrapTransportFrame(raw) {
62
60
  // Validate basic structure
63
61
  if (!raw || typeof raw !== 'object') {
64
62
  return null;
@@ -72,16 +70,17 @@ function unwrapTransportFrame(raw, localNodeId, remoteNodeId) {
72
70
  if (typeof frame.src !== 'string' || typeof frame.dst !== 'string') {
73
71
  return null;
74
72
  }
75
- // Only accept frames addressed to us from the expected remote
76
- if (frame.dst !== localNodeId || frame.src !== remoteNodeId) {
77
- return null;
78
- }
79
73
  // Extract payload
80
74
  if (!frame.payload || !Array.isArray(frame.payload)) {
81
75
  return null;
82
76
  }
83
- // Convert array back to Uint8Array
84
- return Uint8Array.from(frame.payload);
77
+ // Convert array back to Uint8Array and return full frame
78
+ return {
79
+ v: frame.v,
80
+ src: frame.src,
81
+ dst: frame.dst,
82
+ payload: Uint8Array.from(frame.payload),
83
+ };
85
84
  }
86
85
  /**
87
86
  * Check if raw data looks like a transport frame
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  // This file is auto-generated during build - do not edit manually
3
- // Generated from package.json version: 0.3.5-test.950
3
+ // Generated from package.json version: 0.3.5-test.952
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.VERSION = void 0;
6
6
  /**
7
7
  * The package version, injected at build time.
8
8
  * @internal
9
9
  */
10
- exports.VERSION = '0.3.5-test.950';
10
+ exports.VERSION = '0.3.5-test.952';
@@ -116,43 +116,64 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
116
116
  return;
117
117
  }
118
118
  // Try to unwrap as transport frame
119
- const unwrapped = unwrapTransportFrame(busMessage.payload, this.localNodeId, this.remoteNodeId === '*' ? busMessage.senderId : this.remoteNodeId);
120
- if (unwrapped) {
121
- // Successfully unwrapped transport frame
122
- logger.debug('broadcast_channel_transport_frame_received', {
123
- channel: this.channelName,
124
- sender_id: busMessage.senderId,
125
- connector_id: this.connectorId,
126
- local_node_id: this.localNodeId,
127
- remote_node_id: this.remoteNodeId,
128
- payload_length: unwrapped.byteLength,
129
- });
130
- if (this._shouldSkipDuplicateAck(busMessage.senderId, unwrapped)) {
131
- return;
132
- }
133
- try {
134
- if (typeof this.inbox.tryEnqueue === 'function') {
135
- const accepted = this.inbox.tryEnqueue(unwrapped);
136
- if (accepted) {
137
- return;
138
- }
119
+ const frame = unwrapTransportFrame(busMessage.payload);
120
+ if (frame) {
121
+ // Apply connector's filtering policy: strict dst check, src accepts wildcard
122
+ const srcMatches = this.remoteNodeId === '*' || frame.src === this.remoteNodeId;
123
+ const dstMatches = frame.dst === this.localNodeId;
124
+ if (dstMatches && srcMatches) {
125
+ // Successfully received and filtered transport frame
126
+ logger.debug('broadcast_channel_transport_frame_received', {
127
+ channel: this.channelName,
128
+ sender_id: busMessage.senderId,
129
+ connector_id: this.connectorId,
130
+ local_node_id: this.localNodeId,
131
+ remote_node_id: this.remoteNodeId,
132
+ frame_src: frame.src,
133
+ frame_dst: frame.dst,
134
+ payload_length: frame.payload.byteLength,
135
+ });
136
+ const unwrapped = frame.payload;
137
+ if (this._shouldSkipDuplicateAck(busMessage.senderId, unwrapped)) {
138
+ return;
139
139
  }
140
- this.inbox.enqueue(unwrapped);
141
- }
142
- catch (error) {
143
- if (error instanceof QueueFullError) {
144
- logger.warning('broadcast_channel_receive_queue_full', {
145
- channel: this.channelName,
146
- });
140
+ try {
141
+ if (typeof this.inbox.tryEnqueue === 'function') {
142
+ const accepted = this.inbox.tryEnqueue(unwrapped);
143
+ if (accepted) {
144
+ return;
145
+ }
146
+ }
147
+ this.inbox.enqueue(unwrapped);
147
148
  }
148
- else {
149
- logger.error('broadcast_channel_receive_error', {
150
- channel: this.channelName,
151
- error: error instanceof Error ? error.message : String(error),
152
- });
149
+ catch (error) {
150
+ if (error instanceof QueueFullError) {
151
+ logger.warning('broadcast_channel_receive_queue_full', {
152
+ channel: this.channelName,
153
+ });
154
+ }
155
+ else {
156
+ logger.error('broadcast_channel_receive_error', {
157
+ channel: this.channelName,
158
+ error: error instanceof Error ? error.message : String(error),
159
+ });
160
+ }
153
161
  }
162
+ return;
163
+ }
164
+ else {
165
+ // Frame filtered out by addressing rules
166
+ logger.debug('broadcast_channel_transport_frame_filtered', {
167
+ channel: this.channelName,
168
+ connector_id: this.connectorId,
169
+ local_node_id: this.localNodeId,
170
+ remote_node_id: this.remoteNodeId,
171
+ frame_src: frame.src,
172
+ frame_dst: frame.dst,
173
+ reason: !dstMatches ? 'wrong_destination' : 'wrong_source',
174
+ });
175
+ return;
154
176
  }
155
- return;
156
177
  }
157
178
  // Fall back to legacy format (no transport frame)
158
179
  const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
@@ -210,41 +210,44 @@ export class BroadcastChannelListener extends TransportListener {
210
210
  if (typeof senderId !== 'string' || senderId.length === 0) {
211
211
  return null;
212
212
  }
213
- const payload = coercePayload(record.payload);
214
- if (!payload) {
215
- logger.debug('broadcast_channel_listener_ignored_event_without_payload', {
216
- sender_id: senderId,
217
- });
218
- return null;
219
- }
220
- // Try to unwrap as transport frame first
221
- // Use wildcard for remoteNodeId since we don't know the sender's node ID yet
222
- let unwrapped = null;
223
- if (this._routingNode) {
224
- try {
225
- // First try to deserialize as transport frame
226
- const parsed = JSON.parse(new TextDecoder().decode(payload));
227
- if (parsed && typeof parsed === 'object' && 'v' in parsed && 'src' in parsed && 'dst' in parsed) {
228
- const frame = parsed;
229
- // Check if this frame is addressed to us (localNodeId)
230
- if (frame.dst === this._routingNode.id) {
231
- unwrapped = unwrapTransportFrame(payload, this._routingNode.id, '*');
232
- if (unwrapped) {
233
- logger.debug('broadcast_channel_listener_unwrapped_transport_frame', {
234
- sender_id: senderId,
235
- src: frame.src,
236
- dst: frame.dst,
237
- });
238
- }
239
- }
213
+ // Check if payload is a transport frame object first
214
+ let envelopePayload = null;
215
+ if (this._routingNode && record.payload && typeof record.payload === 'object') {
216
+ // Try to unwrap as transport frame
217
+ const frame = unwrapTransportFrame(record.payload);
218
+ if (frame) {
219
+ // Apply listener's filtering policy: accept frames addressed to us OR with wildcard destination
220
+ // Wildcard is needed because downstream nodes don't know the sentinel's ID during initial attach
221
+ const isAddressedToUs = frame.dst === this._routingNode.id || frame.dst === '*';
222
+ if (isAddressedToUs) {
223
+ envelopePayload = frame.payload;
224
+ logger.debug('broadcast_channel_listener_unwrapped_transport_frame', {
225
+ sender_id: senderId,
226
+ src: frame.src,
227
+ dst: frame.dst,
228
+ });
229
+ }
230
+ else {
231
+ // Frame addressed to a different node, ignore it
232
+ logger.debug('broadcast_channel_listener_ignored_frame_wrong_destination', {
233
+ sender_id: senderId,
234
+ dst: frame.dst,
235
+ expected: this._routingNode.id,
236
+ });
237
+ return null;
240
238
  }
241
239
  }
242
- catch {
243
- // Not a transport frame, continue with legacy format
240
+ }
241
+ // If not a transport frame, try to coerce as legacy format
242
+ if (!envelopePayload) {
243
+ envelopePayload = coercePayload(record.payload);
244
+ if (!envelopePayload) {
245
+ logger.debug('broadcast_channel_listener_ignored_event_without_payload', {
246
+ sender_id: senderId,
247
+ });
248
+ return null;
244
249
  }
245
250
  }
246
- // Use unwrapped payload if available, otherwise use raw payload
247
- const envelopePayload = unwrapped ?? payload;
248
251
  let envelope;
249
252
  try {
250
253
  const decoded = new TextDecoder().decode(envelopePayload);
@@ -254,7 +257,7 @@ export class BroadcastChannelListener extends TransportListener {
254
257
  catch (error) {
255
258
  const decoded = (() => {
256
259
  try {
257
- return new TextDecoder().decode(payload);
260
+ return new TextDecoder().decode(envelopePayload);
258
261
  }
259
262
  catch {
260
263
  return null;
@@ -126,40 +126,61 @@ export class InPageConnector extends BaseAsyncConnector {
126
126
  return;
127
127
  }
128
128
  // Try to unwrap as transport frame
129
- const unwrapped = unwrapTransportFrame(busMessage.payload, this.localNodeId, this.remoteNodeId === '*' ? busMessage.senderId : this.remoteNodeId);
130
- if (unwrapped) {
131
- // Successfully unwrapped transport frame
132
- logger.debug('inpage_transport_frame_received', {
133
- channel: this.channelName,
134
- sender_id: busMessage.senderId,
135
- connector_id: this.connectorId,
136
- local_node_id: this.localNodeId,
137
- remote_node_id: this.remoteNodeId,
138
- payload_length: unwrapped.byteLength,
139
- });
140
- try {
141
- if (typeof this.inbox.tryEnqueue === 'function') {
142
- const accepted = this.inbox.tryEnqueue(unwrapped);
143
- if (accepted) {
144
- return;
129
+ const frame = unwrapTransportFrame(busMessage.payload);
130
+ if (frame) {
131
+ // Apply connector's filtering policy: strict dst check, src accepts wildcard
132
+ const srcMatches = this.remoteNodeId === '*' || frame.src === this.remoteNodeId;
133
+ const dstMatches = frame.dst === this.localNodeId;
134
+ if (dstMatches && srcMatches) {
135
+ // Successfully received and filtered transport frame
136
+ logger.debug('inpage_transport_frame_received', {
137
+ channel: this.channelName,
138
+ sender_id: busMessage.senderId,
139
+ connector_id: this.connectorId,
140
+ local_node_id: this.localNodeId,
141
+ remote_node_id: this.remoteNodeId,
142
+ frame_src: frame.src,
143
+ frame_dst: frame.dst,
144
+ payload_length: frame.payload.byteLength,
145
+ });
146
+ const unwrapped = frame.payload;
147
+ try {
148
+ if (typeof this.inbox.tryEnqueue === 'function') {
149
+ const accepted = this.inbox.tryEnqueue(unwrapped);
150
+ if (accepted) {
151
+ return;
152
+ }
145
153
  }
154
+ this.inbox.enqueue(unwrapped);
146
155
  }
147
- this.inbox.enqueue(unwrapped);
148
- }
149
- catch (error) {
150
- if (error instanceof QueueFullError) {
151
- logger.warning('inpage_receive_queue_full', {
152
- channel: this.channelName,
153
- });
154
- }
155
- else {
156
- logger.error('inpage_receive_error', {
157
- channel: this.channelName,
158
- error: error instanceof Error ? error.message : String(error),
159
- });
156
+ catch (error) {
157
+ if (error instanceof QueueFullError) {
158
+ logger.warning('inpage_receive_queue_full', {
159
+ channel: this.channelName,
160
+ });
161
+ }
162
+ else {
163
+ logger.error('inpage_receive_error', {
164
+ channel: this.channelName,
165
+ error: error instanceof Error ? error.message : String(error),
166
+ });
167
+ }
160
168
  }
169
+ return;
170
+ }
171
+ else {
172
+ // Frame filtered out by addressing rules
173
+ logger.debug('inpage_transport_frame_filtered', {
174
+ channel: this.channelName,
175
+ connector_id: this.connectorId,
176
+ local_node_id: this.localNodeId,
177
+ remote_node_id: this.remoteNodeId,
178
+ frame_src: frame.src,
179
+ frame_dst: frame.dst,
180
+ reason: !dstMatches ? 'wrong_destination' : 'wrong_source',
181
+ });
182
+ return;
161
183
  }
162
- return;
163
184
  }
164
185
  // Fall back to legacy format (no transport frame)
165
186
  const payload = InPageConnector.coercePayload(busMessage.payload);
@@ -44,14 +44,12 @@ export function serializeTransportFrame(frame) {
44
44
  return serializable;
45
45
  }
46
46
  /**
47
- * Unwrap a transport frame, validating source and destination
47
+ * Unwrap a transport frame (pure deserializer - no filtering)
48
48
  *
49
49
  * @param raw - Raw data from the bus
50
- * @param localNodeId - This connector's node ID
51
- * @param remoteNodeId - Expected remote node ID
52
- * @returns Unwrapped payload if frame is valid and addressed to us, null otherwise
50
+ * @returns Unwrapped frame with payload as Uint8Array, or null if invalid structure
53
51
  */
54
- export function unwrapTransportFrame(raw, localNodeId, remoteNodeId) {
52
+ export function unwrapTransportFrame(raw) {
55
53
  // Validate basic structure
56
54
  if (!raw || typeof raw !== 'object') {
57
55
  return null;
@@ -65,16 +63,17 @@ export function unwrapTransportFrame(raw, localNodeId, remoteNodeId) {
65
63
  if (typeof frame.src !== 'string' || typeof frame.dst !== 'string') {
66
64
  return null;
67
65
  }
68
- // Only accept frames addressed to us from the expected remote
69
- if (frame.dst !== localNodeId || frame.src !== remoteNodeId) {
70
- return null;
71
- }
72
66
  // Extract payload
73
67
  if (!frame.payload || !Array.isArray(frame.payload)) {
74
68
  return null;
75
69
  }
76
- // Convert array back to Uint8Array
77
- return Uint8Array.from(frame.payload);
70
+ // Convert array back to Uint8Array and return full frame
71
+ return {
72
+ v: frame.v,
73
+ src: frame.src,
74
+ dst: frame.dst,
75
+ payload: Uint8Array.from(frame.payload),
76
+ };
78
77
  }
79
78
  /**
80
79
  * Check if raw data looks like a transport frame
@@ -1,7 +1,7 @@
1
1
  // This file is auto-generated during build - do not edit manually
2
- // Generated from package.json version: 0.3.5-test.950
2
+ // Generated from package.json version: 0.3.5-test.952
3
3
  /**
4
4
  * The package version, injected at build time.
5
5
  * @internal
6
6
  */
7
- export const VERSION = '0.3.5-test.950';
7
+ export const VERSION = '0.3.5-test.952';