@zlink-systems/framework 0.11.0 → 0.12.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/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Configuration/EndpointNotation.d.ts +5 -4
- package/dist/contracts/Configuration/EndpointNotation.js +5 -4
- package/dist/contracts/Configuration/InternalDefaults.d.ts +0 -1
- package/dist/contracts/Configuration/InternalDefaults.js +1 -2
- package/dist/contracts/Configuration/RegistrationNormalizers.js +1 -2
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +2 -5
- package/dist/contracts/Configuration/RegistrationValidators.js +0 -1
- package/dist/contracts/Errors/ZLinkFrameworkException.d.ts +6 -7
- package/dist/contracts/Errors/ZLinkFrameworkException.js +6 -7
- package/dist/runtime/actors/actor-client.js +1 -1
- package/dist/runtime/actors/actor-mailbox.js +1 -5
- package/dist/runtime/backend/contracts/index.d.ts +2 -0
- package/dist/runtime/backend/mesh-completion-table.d.ts +1 -3
- package/dist/runtime/backend/mesh-completion-table.js +11 -15
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +11 -4
- package/dist/runtime/backend/mesh-dispatch-pump.js +201 -134
- package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +3 -4
- package/dist/runtime/backend/node/node-backend-adapter-factory.js +51 -37
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
- package/dist/runtime/backend/node/node-backend-adapter-support.js +2 -2
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +2 -2
- package/dist/runtime/backend/node/node-event-loop-poller.d.ts +16 -0
- package/dist/runtime/backend/node/node-event-loop-poller.js +49 -0
- package/dist/runtime/backend/node/node-raw-binding-port.js +24 -41
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +8 -8
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +100 -107
- package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +3 -2
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +18 -67
- package/dist/runtime/backend/raw-binding-port.d.ts +1 -0
- package/dist/runtime/backend/runtime-values.d.ts +5 -1
- package/dist/runtime/channels/channel-dispatchers.js +2 -2
- package/dist/runtime/channels/channel-envelope.js +324 -121
- package/dist/runtime/channels/channel-multipart.d.ts +8 -1
- package/dist/runtime/channels/channel-outbound-operations.js +0 -2
- package/dist/runtime/channels/channel-receive-loops.d.ts +1 -3
- package/dist/runtime/channels/channel-receive-loops.js +25 -65
- package/dist/runtime/channels/channel-socket-registry.js +3 -1
- package/dist/runtime/channels/channel-transports.d.ts +2 -1
- package/dist/runtime/channels/channel-transports.js +12 -10
- package/dist/runtime/execution/serial-execution-queue.d.ts +5 -18
- package/dist/runtime/execution/serial-execution-queue.js +16 -80
- package/dist/runtime/foundation/event-loop-resources.d.ts +0 -3
- package/dist/runtime/foundation/event-loop-resources.js +2 -11
- package/dist/runtime/foundation/operation-registry.d.ts +2 -8
- package/dist/runtime/foundation/operation-registry.js +3 -21
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +19 -7
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +56 -10
- package/dist/runtime/foundation/service-mailbox.d.ts +1 -7
- package/dist/runtime/foundation/service-mailbox.js +3 -2
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +2 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +1 -1
- package/dist/runtime/foundation/service-stateful-registry.js +2 -2
- package/dist/runtime/foundation/service-stateful-runtime.js +14 -14
- package/dist/runtime/framework-errors-internal.d.ts +4 -4
- package/dist/runtime/framework-errors-internal.js +8 -10
- package/dist/runtime/host/actor-placement-coordinator.js +1 -1
- package/dist/runtime/host/index.js +1 -1
- package/dist/runtime/host/service-relocation-host-runtime.js +0 -4
- package/dist/runtime/host/spot-address-transport.js +1 -1
- package/dist/runtime/host/user-spot-creation-coordinator.js +1 -1
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +0 -12
- package/dist/runtime/spots/spot-node-runtime-manager.js +6 -166
- package/dist/runtime/spots/spot-serial-turn-executor.js +1 -4
- package/dist/runtime/spots/spot-timer.d.ts +3 -0
- package/dist/runtime/spots/spot-timer.js +87 -18
- package/dist/runtime/streams/session-serial-executor.js +1 -3
- package/dist/runtime/streams/stream-session-runtime.d.ts +2 -2
- package/dist/runtime/streams/stream-session-runtime.js +13 -19
- package/dist/runtime/workers/index.d.ts +0 -1
- package/dist/runtime/workers/index.js +1 -9
- package/package.json +3 -3
- package/src/contracts/Configuration/EndpointNotation.ts +5 -4
- package/src/contracts/Configuration/InternalDefaults.ts +0 -1
- package/src/contracts/Configuration/RegistrationNormalizers.ts +2 -4
- package/src/contracts/Configuration/RegistrationTypes.ts +2 -5
- package/src/contracts/Configuration/RegistrationValidators.ts +0 -1
- package/src/contracts/Errors/ZLinkFrameworkException.ts +6 -7
- package/src/runtime/actors/actor-client.ts +1 -1
- package/src/runtime/actors/actor-mailbox.ts +1 -12
- package/src/runtime/backend/contracts/index.ts +2 -0
- package/src/runtime/backend/mesh-completion-table.ts +12 -23
- package/src/runtime/backend/mesh-dispatch-pump.ts +206 -138
- package/src/runtime/backend/node/node-backend-adapter-factory.ts +66 -35
- package/src/runtime/backend/node/node-backend-adapter-support.ts +4 -4
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +2 -2
- package/src/runtime/backend/node/node-event-loop-poller.ts +55 -0
- package/src/runtime/backend/node/node-raw-binding-port.ts +30 -45
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +97 -110
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +23 -69
- package/src/runtime/backend/raw-binding-port.ts +1 -0
- package/src/runtime/backend/runtime-values.ts +7 -1
- package/src/runtime/channels/channel-dispatchers.ts +2 -2
- package/src/runtime/channels/channel-envelope.ts +379 -109
- package/src/runtime/channels/channel-multipart.ts +9 -1
- package/src/runtime/channels/channel-outbound-operations.ts +0 -1
- package/src/runtime/channels/channel-receive-loops.ts +24 -75
- package/src/runtime/channels/channel-socket-registry.ts +4 -1
- package/src/runtime/channels/channel-transports.ts +20 -10
- package/src/runtime/execution/serial-execution-queue.ts +16 -108
- package/src/runtime/foundation/event-loop-resources.ts +2 -11
- package/src/runtime/foundation/operation-registry.ts +2 -22
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +74 -19
- package/src/runtime/foundation/service-mailbox.ts +3 -13
- package/src/runtime/foundation/service-runtime-contracts.ts +2 -1
- package/src/runtime/foundation/service-stateful-registry.ts +2 -2
- package/src/runtime/foundation/service-stateful-runtime.ts +14 -14
- package/src/runtime/framework-errors-internal.ts +8 -10
- package/src/runtime/host/actor-placement-coordinator.ts +1 -1
- package/src/runtime/host/index.ts +2 -1
- package/src/runtime/host/service-relocation-host-runtime.ts +0 -4
- package/src/runtime/host/spot-address-transport.ts +1 -1
- package/src/runtime/host/user-spot-creation-coordinator.ts +1 -1
- package/src/runtime/spots/spot-node-runtime-manager.ts +13 -233
- package/src/runtime/spots/spot-serial-turn-executor.ts +1 -8
- package/src/runtime/spots/spot-timer.ts +109 -18
- package/src/runtime/streams/session-serial-executor.ts +1 -7
- package/src/runtime/streams/stream-session-runtime.ts +13 -17
- package/src/runtime/workers/index.ts +2 -17
|
@@ -96,22 +96,21 @@ export function encodeChannelEnvelopeParts(
|
|
|
96
96
|
const encoded = encodePayload(payload, codecsForFrameworkPacket(messageName, codecs), messageName, 'payload');
|
|
97
97
|
const flow = createFlow ? currentOrCreateFlow('Application') : undefined;
|
|
98
98
|
const envelopeCorrelationId = correlationIdForOutboundKind(kind, correlationId);
|
|
99
|
-
|
|
100
|
-
formatMarker: ZLINK_CHANNEL_FORMAT_MARKER,
|
|
99
|
+
return [encodeChannelHeader(
|
|
101
100
|
kind,
|
|
102
101
|
channelName,
|
|
103
102
|
messageName,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
topic
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
103
|
+
encoded.contentType,
|
|
104
|
+
envelopeCorrelationId,
|
|
105
|
+
timeoutMs === undefined ? null : new Date(Date.now() + timeoutMs).toISOString(),
|
|
106
|
+
topic ?? null,
|
|
107
|
+
null,
|
|
108
|
+
null,
|
|
109
|
+
undefined,
|
|
110
|
+
applicationMetadataRecord(metadata),
|
|
111
|
+
flow?.flowId,
|
|
112
|
+
flow?.flowOrigin
|
|
113
|
+
), encoded.message];
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
/**
|
|
@@ -139,22 +138,21 @@ export function encodeChannelEnvelopePartsAtDeadline(
|
|
|
139
138
|
const encoded = encodePayload(payload, codecsForFrameworkPacket(messageName, codecs), messageName, 'payload');
|
|
140
139
|
const flow = createFlow ? currentOrCreateFlow('Application') : undefined;
|
|
141
140
|
const envelopeCorrelationId = correlationIdForOutboundKind(kind, correlationId);
|
|
142
|
-
|
|
143
|
-
formatMarker: ZLINK_CHANNEL_FORMAT_MARKER,
|
|
141
|
+
return [encodeChannelHeader(
|
|
144
142
|
kind,
|
|
145
143
|
channelName,
|
|
146
144
|
messageName,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
topic
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
145
|
+
encoded.contentType,
|
|
146
|
+
envelopeCorrelationId,
|
|
147
|
+
new Date(deadlineUnixMs).toISOString(),
|
|
148
|
+
topic ?? null,
|
|
149
|
+
null,
|
|
150
|
+
null,
|
|
151
|
+
undefined,
|
|
152
|
+
applicationMetadataRecord(metadata),
|
|
153
|
+
flow?.flowId,
|
|
154
|
+
flow?.flowOrigin
|
|
155
|
+
), encoded.message];
|
|
158
156
|
}
|
|
159
157
|
|
|
160
158
|
export function encodeChannelPublishEnvelopeParts(
|
|
@@ -169,22 +167,21 @@ export function encodeChannelPublishEnvelopeParts(
|
|
|
169
167
|
const messageName = resolveFrameworkPacketName(payload, packetName, 'Channel');
|
|
170
168
|
const encoded = encodePayload(payload, codecsForFrameworkPacket(messageName, codecs), messageName, 'payload');
|
|
171
169
|
const flow = createFlow ? currentOrCreateFlow('Application') : undefined;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
kind: ZLinkChannelMessageKind.Publish,
|
|
170
|
+
return [encodeChannelHeader(
|
|
171
|
+
ZLinkChannelMessageKind.Publish,
|
|
175
172
|
channelName,
|
|
176
173
|
messageName,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
174
|
+
encoded.contentType,
|
|
175
|
+
null,
|
|
176
|
+
null,
|
|
180
177
|
topic,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
178
|
+
null,
|
|
179
|
+
null,
|
|
180
|
+
undefined,
|
|
181
|
+
applicationMetadataRecord(metadata),
|
|
182
|
+
flow?.flowId,
|
|
183
|
+
flow?.flowOrigin
|
|
184
|
+
), encoded.message];
|
|
188
185
|
}
|
|
189
186
|
|
|
190
187
|
export function encodeChannelReplyParts(
|
|
@@ -198,20 +195,21 @@ export function encodeChannelReplyParts(
|
|
|
198
195
|
request.messageName,
|
|
199
196
|
'reply'
|
|
200
197
|
);
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
198
|
+
return [encodeChannelHeader(
|
|
199
|
+
ZLinkChannelMessageKind.Response,
|
|
200
|
+
request.channelName,
|
|
201
|
+
request.messageName,
|
|
202
|
+
encoded.contentType,
|
|
203
|
+
request.correlationId,
|
|
204
|
+
null,
|
|
205
|
+
null,
|
|
206
|
+
undefined,
|
|
207
|
+
undefined,
|
|
208
|
+
undefined,
|
|
209
|
+
EMPTY_APPLICATION_METADATA,
|
|
210
|
+
request.flowId,
|
|
211
|
+
request.flowOrigin
|
|
212
|
+
), encoded.message];
|
|
215
213
|
}
|
|
216
214
|
|
|
217
215
|
/**
|
|
@@ -236,13 +234,12 @@ const ERROR_KIND_WIRE_NAMES: readonly string[] = [
|
|
|
236
234
|
'not_configured', // 3
|
|
237
235
|
'rejected', // 4
|
|
238
236
|
'unavailable', // 5
|
|
239
|
-
'
|
|
240
|
-
'
|
|
241
|
-
'
|
|
242
|
-
'
|
|
243
|
-
'
|
|
244
|
-
'
|
|
245
|
-
'internal_failure' // 12
|
|
237
|
+
'deadline_exceeded', // 6
|
|
238
|
+
'shutting_down', // 7
|
|
239
|
+
'protocol_error', // 8
|
|
240
|
+
'invalid_operation', // 9
|
|
241
|
+
'data_lost', // 10
|
|
242
|
+
'internal_failure' // 11
|
|
246
243
|
];
|
|
247
244
|
|
|
248
245
|
const ERROR_KIND_BY_WIRE_NAME: ReadonlyMap<string, ZLinkFrameworkErrorKind> = new Map(
|
|
@@ -275,22 +272,21 @@ export function encodeChannelErrorReplyParts(
|
|
|
275
272
|
: ZLinkFrameworkErrorKind.InternalFailure
|
|
276
273
|
);
|
|
277
274
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
topic: null,
|
|
275
|
+
return [encodeChannelHeader(
|
|
276
|
+
ZLinkChannelMessageKind.Error,
|
|
277
|
+
request.channelName,
|
|
278
|
+
request.messageName,
|
|
279
|
+
JSON_CONTENT_TYPE,
|
|
280
|
+
request.correlationId,
|
|
281
|
+
null,
|
|
282
|
+
null,
|
|
287
283
|
errorCode,
|
|
288
284
|
errorMessage,
|
|
285
|
+
undefined,
|
|
289
286
|
metadata,
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return [encodeChannelHeader(header), encodeJsonBytes(null)];
|
|
287
|
+
request.flowId,
|
|
288
|
+
request.flowOrigin
|
|
289
|
+
), encodeJsonBytes(null)];
|
|
294
290
|
}
|
|
295
291
|
|
|
296
292
|
export function decodeChannelReply<TReply>(
|
|
@@ -491,25 +487,162 @@ function encodeJsonBytes(value: unknown, schema?: ZLinkJsonSchema): Buffer {
|
|
|
491
487
|
return Buffer.from(stringifyFrameworkJsonV1(value, schema));
|
|
492
488
|
}
|
|
493
489
|
|
|
494
|
-
function encodeChannelHeader(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
490
|
+
function encodeChannelHeader(
|
|
491
|
+
kind: ZLinkChannelMessageKind,
|
|
492
|
+
channelName: string,
|
|
493
|
+
messageName: string,
|
|
494
|
+
contentType: string,
|
|
495
|
+
correlationId: string | null,
|
|
496
|
+
deadline: string | null,
|
|
497
|
+
topic: string | null,
|
|
498
|
+
errorCode: string | null | undefined,
|
|
499
|
+
errorMessage: string | null | undefined,
|
|
500
|
+
source: string | null | undefined,
|
|
501
|
+
metadata: Readonly<Record<string, string>>,
|
|
502
|
+
flowId: string | undefined,
|
|
503
|
+
flowOrigin: ZLinkFlowOrigin | undefined
|
|
504
|
+
): Buffer {
|
|
505
|
+
// The wire has a fixed field order. Measure escaped UTF-8 first, then write
|
|
506
|
+
// each field once into the only header Buffer: no header object, whole JSON
|
|
507
|
+
// string, or post-encode byte copy exists on the outbound path.
|
|
508
|
+
const measure = new ChannelHeaderWriter();
|
|
509
|
+
writeChannelHeader(
|
|
510
|
+
measure, kind, channelName, messageName, contentType, correlationId,
|
|
511
|
+
deadline, topic, errorCode, errorMessage, source, metadata, flowId, flowOrigin
|
|
512
|
+
);
|
|
513
|
+
const result = Buffer.alloc(measure.byteLength);
|
|
514
|
+
const writer = new ChannelHeaderWriter(result);
|
|
515
|
+
writeChannelHeader(
|
|
516
|
+
writer, kind, channelName, messageName, contentType, correlationId,
|
|
517
|
+
deadline, topic, errorCode, errorMessage, source, metadata, flowId, flowOrigin
|
|
518
|
+
);
|
|
519
|
+
return result;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function writeChannelHeader(
|
|
523
|
+
writer: ChannelHeaderWriter,
|
|
524
|
+
kind: ZLinkChannelMessageKind,
|
|
525
|
+
channelName: string,
|
|
526
|
+
messageName: string,
|
|
527
|
+
contentType: string,
|
|
528
|
+
correlationId: string | null,
|
|
529
|
+
deadline: string | null,
|
|
530
|
+
topic: string | null,
|
|
531
|
+
errorCode: string | null | undefined,
|
|
532
|
+
errorMessage: string | null | undefined,
|
|
533
|
+
source: string | null | undefined,
|
|
534
|
+
metadata: Readonly<Record<string, string>>,
|
|
535
|
+
flowId: string | undefined,
|
|
536
|
+
flowOrigin: ZLinkFlowOrigin | undefined
|
|
537
|
+
): void {
|
|
538
|
+
writer.append('{"formatMarker":');
|
|
539
|
+
writer.append(String(ZLINK_CHANNEL_FORMAT_MARKER));
|
|
540
|
+
writer.append(',"kind":');
|
|
541
|
+
writer.append(String(kind));
|
|
542
|
+
writer.append(',"channelName":');
|
|
543
|
+
writer.string(channelName);
|
|
544
|
+
writer.append(',"messageName":');
|
|
545
|
+
writer.string(messageName);
|
|
546
|
+
writer.append(',"contentType":');
|
|
547
|
+
writer.string(contentType);
|
|
548
|
+
writer.append(',"correlationId":');
|
|
549
|
+
writer.nullableString(correlationId);
|
|
550
|
+
writer.append(',"deadline":');
|
|
551
|
+
writer.nullableString(deadline);
|
|
552
|
+
writer.append(',"topic":');
|
|
553
|
+
writer.nullableString(topic);
|
|
554
|
+
if (errorCode !== undefined) {
|
|
555
|
+
writer.append(',"errorCode":');
|
|
556
|
+
writer.nullableString(errorCode);
|
|
557
|
+
}
|
|
558
|
+
if (errorMessage !== undefined) {
|
|
559
|
+
writer.append(',"errorMessage":');
|
|
560
|
+
writer.nullableString(errorMessage);
|
|
561
|
+
}
|
|
562
|
+
if (source !== undefined) {
|
|
563
|
+
writer.append(',"source":');
|
|
564
|
+
writer.nullableString(source);
|
|
565
|
+
}
|
|
566
|
+
writer.append(',"metadata":{');
|
|
567
|
+
let first = true;
|
|
568
|
+
for (const name in metadata) {
|
|
569
|
+
if (!Object.prototype.hasOwnProperty.call(metadata, name)) continue;
|
|
570
|
+
if (!first) writer.append(',');
|
|
571
|
+
first = false;
|
|
572
|
+
writer.string(name);
|
|
573
|
+
writer.append(':');
|
|
574
|
+
writer.string(metadata[name]!);
|
|
575
|
+
}
|
|
576
|
+
writer.append('}');
|
|
577
|
+
if (flowId !== undefined) {
|
|
578
|
+
writer.append(',"flowId":');
|
|
579
|
+
writer.string(flowId);
|
|
580
|
+
}
|
|
581
|
+
if (flowOrigin !== undefined) {
|
|
582
|
+
writer.append(',"flowOrigin":');
|
|
583
|
+
writer.append(String(encodeFlowOrigin(flowOrigin)));
|
|
584
|
+
}
|
|
585
|
+
writer.append('}');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
class ChannelHeaderWriter {
|
|
589
|
+
private offset = 0;
|
|
590
|
+
|
|
591
|
+
constructor(private readonly output?: Buffer) {}
|
|
592
|
+
|
|
593
|
+
get byteLength(): number {
|
|
594
|
+
return this.offset;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
append(value: string): void {
|
|
598
|
+
if (this.output === undefined) {
|
|
599
|
+
this.offset += Buffer.byteLength(value);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
this.offset += this.output.write(value, this.offset, 'utf8');
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
nullableString(value: string | null): void {
|
|
606
|
+
if (value === null) this.append('null');
|
|
607
|
+
else this.string(value);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
string(value: string): void {
|
|
611
|
+
this.append('"');
|
|
612
|
+
let start = 0;
|
|
613
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
614
|
+
const code = value.charCodeAt(index);
|
|
615
|
+
let escaped: string | undefined;
|
|
616
|
+
switch (code) {
|
|
617
|
+
case 0x08: escaped = '\\b'; break;
|
|
618
|
+
case 0x09: escaped = '\\t'; break;
|
|
619
|
+
case 0x0a: escaped = '\\n'; break;
|
|
620
|
+
case 0x0c: escaped = '\\f'; break;
|
|
621
|
+
case 0x0d: escaped = '\\r'; break;
|
|
622
|
+
case 0x22: escaped = '\\"'; break;
|
|
623
|
+
case 0x5c: escaped = '\\\\'; break;
|
|
624
|
+
default:
|
|
625
|
+
if (code < 0x20) {
|
|
626
|
+
escaped = `\\u00${code.toString(16).padStart(2, '0')}`;
|
|
627
|
+
} else if (code >= 0xd800 && code <= 0xdbff) {
|
|
628
|
+
const next = value.charCodeAt(index + 1);
|
|
629
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
630
|
+
index += 1;
|
|
631
|
+
} else {
|
|
632
|
+
escaped = `\\u${code.toString(16)}`;
|
|
633
|
+
}
|
|
634
|
+
} else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
635
|
+
escaped = `\\u${code.toString(16)}`;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (escaped === undefined) continue;
|
|
639
|
+
this.append(value.slice(start, index));
|
|
640
|
+
this.append(escaped);
|
|
641
|
+
start = index + 1;
|
|
642
|
+
}
|
|
643
|
+
this.append(value.slice(start));
|
|
644
|
+
this.append('"');
|
|
645
|
+
}
|
|
513
646
|
}
|
|
514
647
|
|
|
515
648
|
function encodeFlowOrigin(origin: ZLinkFlowOrigin): number {
|
|
@@ -528,7 +661,7 @@ export function decodeChannelHeader(
|
|
|
528
661
|
if (parts.length === 0) {
|
|
529
662
|
throw new ZLinkConfigurationException('Channel envelope header part is missing.');
|
|
530
663
|
}
|
|
531
|
-
return validateChannelHeader(
|
|
664
|
+
return validateChannelHeader(parseChannelHeaderBytes(parts[0].data()), flowEnabled);
|
|
532
665
|
}
|
|
533
666
|
|
|
534
667
|
function parseWireJson(payload: string, schema?: ZLinkJsonSchema): unknown {
|
|
@@ -537,6 +670,146 @@ function parseWireJson(payload: string, schema?: ZLinkJsonSchema): unknown {
|
|
|
537
670
|
}, schema);
|
|
538
671
|
}
|
|
539
672
|
|
|
673
|
+
function parseChannelHeaderBytes(payload: Buffer): unknown {
|
|
674
|
+
return tryParseCanonicalChannelHeader(payload) ?? parseWireJson(payload.toString());
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Accept the byte layout emitted by writeChannelHeader without materializing
|
|
679
|
+
* the enclosing JSON text. Any variation falls through to the established
|
|
680
|
+
* framework-json-v1 parser, which remains the sole authority for JSON
|
|
681
|
+
* failures, unknown properties, and prototype-key rejection.
|
|
682
|
+
*/
|
|
683
|
+
function tryParseCanonicalChannelHeader(payload: Buffer): Record<string, unknown> | undefined {
|
|
684
|
+
const reader = new CanonicalChannelHeaderReader(payload);
|
|
685
|
+
if (!reader.consume('{"formatMarker":242,"kind":')) return undefined;
|
|
686
|
+
const kind = reader.readSingleDigit();
|
|
687
|
+
if (kind === undefined || !reader.consume(',"channelName":')) return undefined;
|
|
688
|
+
const channelName = reader.readString();
|
|
689
|
+
if (channelName === undefined || !reader.consume(',"messageName":')) return undefined;
|
|
690
|
+
const messageName = reader.readString();
|
|
691
|
+
if (messageName === undefined || !reader.consume(',"contentType":')) return undefined;
|
|
692
|
+
const contentType = reader.readString();
|
|
693
|
+
if (contentType === undefined || !reader.consume(',"correlationId":')) return undefined;
|
|
694
|
+
const correlationId = reader.readNullableString();
|
|
695
|
+
if (correlationId === undefined || !reader.consume(',"deadline":')) return undefined;
|
|
696
|
+
const deadline = reader.readNullableString();
|
|
697
|
+
if (deadline === undefined || !reader.consume(',"topic":')) return undefined;
|
|
698
|
+
const topic = reader.readNullableString();
|
|
699
|
+
if (topic === undefined) return undefined;
|
|
700
|
+
|
|
701
|
+
const header: Record<string, unknown> = {
|
|
702
|
+
formatMarker: ZLINK_CHANNEL_FORMAT_MARKER,
|
|
703
|
+
kind,
|
|
704
|
+
channelName,
|
|
705
|
+
messageName,
|
|
706
|
+
contentType,
|
|
707
|
+
correlationId: correlationId === NULL_JSON_STRING ? null : correlationId,
|
|
708
|
+
deadline: deadline === NULL_JSON_STRING ? null : deadline,
|
|
709
|
+
topic: topic === NULL_JSON_STRING ? null : topic
|
|
710
|
+
};
|
|
711
|
+
if (!reader.readOptionalNullableString('errorCode', header)) return undefined;
|
|
712
|
+
if (!reader.readOptionalNullableString('errorMessage', header)) return undefined;
|
|
713
|
+
if (!reader.readOptionalNullableString('source', header)) return undefined;
|
|
714
|
+
if (!reader.consume(',"metadata":{}')) return undefined;
|
|
715
|
+
header.metadata = {};
|
|
716
|
+
if (!reader.readOptionalString('flowId', header)) return undefined;
|
|
717
|
+
if (!reader.readOptionalSingleDigit('flowOrigin', header)) return undefined;
|
|
718
|
+
return reader.consume('}') && reader.atEnd() ? header : undefined;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const NULL_JSON_STRING = Symbol('null JSON string');
|
|
722
|
+
|
|
723
|
+
class CanonicalChannelHeaderReader {
|
|
724
|
+
private index = 0;
|
|
725
|
+
|
|
726
|
+
constructor(private readonly bytes: Buffer) {}
|
|
727
|
+
|
|
728
|
+
atEnd(): boolean {
|
|
729
|
+
return this.index === this.bytes.length;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
consume(value: string): boolean {
|
|
733
|
+
if (this.index + value.length > this.bytes.length) return false;
|
|
734
|
+
for (let offset = 0; offset < value.length; offset += 1) {
|
|
735
|
+
if (this.bytes[this.index + offset] !== value.charCodeAt(offset)) return false;
|
|
736
|
+
}
|
|
737
|
+
this.index += value.length;
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
readSingleDigit(): number | undefined {
|
|
742
|
+
if (this.index === this.bytes.length) return undefined;
|
|
743
|
+
const value = this.bytes[this.index];
|
|
744
|
+
if (value < 0x30 || value > 0x39) return undefined;
|
|
745
|
+
this.index += 1;
|
|
746
|
+
return value - 0x30;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
readString(): string | undefined {
|
|
750
|
+
if (this.bytes[this.index] !== 0x22) return undefined;
|
|
751
|
+
const start = this.index;
|
|
752
|
+
this.index += 1;
|
|
753
|
+
while (this.index < this.bytes.length) {
|
|
754
|
+
const value = this.bytes[this.index++]!;
|
|
755
|
+
if (value === 0x22) {
|
|
756
|
+
return JSON.parse(this.bytes.toString('utf8', start, this.index)) as string;
|
|
757
|
+
}
|
|
758
|
+
if (value < 0x20) return undefined;
|
|
759
|
+
if (value !== 0x5c) continue;
|
|
760
|
+
if (this.index === this.bytes.length) return undefined;
|
|
761
|
+
const escaped = this.bytes[this.index++]!;
|
|
762
|
+
if (escaped === 0x75) {
|
|
763
|
+
for (let count = 0; count < 4; count += 1) {
|
|
764
|
+
if (this.index === this.bytes.length) return undefined;
|
|
765
|
+
const digit = this.bytes[this.index++];
|
|
766
|
+
if (!isHexDigit(digit)) return undefined;
|
|
767
|
+
}
|
|
768
|
+
} else if (escaped !== 0x22 && escaped !== 0x5c && escaped !== 0x2f
|
|
769
|
+
&& escaped !== 0x62 && escaped !== 0x66 && escaped !== 0x6e
|
|
770
|
+
&& escaped !== 0x72 && escaped !== 0x74) {
|
|
771
|
+
return undefined;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
return undefined;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
readNullableString(): string | symbol | undefined {
|
|
778
|
+
if (this.consume('null')) return NULL_JSON_STRING;
|
|
779
|
+
return this.readString();
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
readOptionalNullableString(name: string, target: Record<string, unknown>): boolean {
|
|
783
|
+
if (!this.consume(`,\"${name}\":`)) return true;
|
|
784
|
+
const value = this.readNullableString();
|
|
785
|
+
if (value === undefined) return false;
|
|
786
|
+
target[name] = value === NULL_JSON_STRING ? null : value;
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
readOptionalString(name: string, target: Record<string, unknown>): boolean {
|
|
791
|
+
if (!this.consume(`,\"${name}\":`)) return true;
|
|
792
|
+
const value = this.readString();
|
|
793
|
+
if (value === undefined) return false;
|
|
794
|
+
target[name] = value;
|
|
795
|
+
return true;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
readOptionalSingleDigit(name: string, target: Record<string, unknown>): boolean {
|
|
799
|
+
if (!this.consume(`,\"${name}\":`)) return true;
|
|
800
|
+
const value = this.readSingleDigit();
|
|
801
|
+
if (value === undefined) return false;
|
|
802
|
+
target[name] = value;
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function isHexDigit(value: number): boolean {
|
|
808
|
+
return (value >= 0x30 && value <= 0x39)
|
|
809
|
+
|| (value >= 0x41 && value <= 0x46)
|
|
810
|
+
|| (value >= 0x61 && value <= 0x66);
|
|
811
|
+
}
|
|
812
|
+
|
|
540
813
|
function schemaForInboundChannelEnvelope(header: ZLinkChannelEnvelopeHeader): ZLinkJsonSchema | undefined {
|
|
541
814
|
const contract = readZLinkPacketJsonContract(header.messageName);
|
|
542
815
|
return header.kind === ZLinkChannelMessageKind.Response ? contract?.reply : contract?.payload;
|
|
@@ -565,22 +838,19 @@ function validateChannelHeader(value: unknown, flowEnabled = true): ZLinkChannel
|
|
|
565
838
|
}
|
|
566
839
|
const correlationId = requireNullableString(header.correlationId, 'correlationId');
|
|
567
840
|
validateCorrelationForKind(kind, correlationId);
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
flowId,
|
|
582
|
-
flowOrigin
|
|
583
|
-
};
|
|
841
|
+
requireString(header.channelName, 'channelName');
|
|
842
|
+
requireString(header.messageName, 'messageName');
|
|
843
|
+
requireNullableString(header.deadline, 'deadline');
|
|
844
|
+
requireNullableString(header.topic, 'topic');
|
|
845
|
+
// Parsing already gives this receive operation an owned object. Validate
|
|
846
|
+
// and normalize it here instead of allocating a second header per record.
|
|
847
|
+
header.errorCode = header.errorCode === undefined ? null : requireNullableString(header.errorCode, 'errorCode');
|
|
848
|
+
header.errorMessage = header.errorMessage === undefined ? null : requireNullableString(header.errorMessage, 'errorMessage');
|
|
849
|
+
header.source = header.source === undefined ? undefined : requireNullableString(header.source, 'source');
|
|
850
|
+
header.metadata = requireApplicationMetadata(header.metadata);
|
|
851
|
+
header.flowId = flowId;
|
|
852
|
+
header.flowOrigin = flowOrigin;
|
|
853
|
+
return header as unknown as ZLinkChannelEnvelopeHeader;
|
|
584
854
|
}
|
|
585
855
|
|
|
586
856
|
function correlationIdForOutboundKind(
|
|
@@ -16,8 +16,16 @@ export interface ZLinkMultipartSubmitOperation extends ZLinkMultipartOperation<Z
|
|
|
16
16
|
submit(): unknown;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Binding submit()은 제출 스냅샷을 돌려준다. admission을 기다리려면 `.admitted`를
|
|
21
|
+
* await 한다. binding 타입과 구조적으로 호환되므로 import 하지 않는다.
|
|
22
|
+
*/
|
|
23
|
+
export interface ZLinkMultipartSubmission {
|
|
24
|
+
readonly admitted: Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
export interface ZLinkMultipartAsyncSubmitOperation extends ZLinkMultipartOperation<ZLinkMultipartAsyncSubmitOperation> {
|
|
20
|
-
submit():
|
|
28
|
+
submit(): ZLinkMultipartSubmission;
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
export type ZLinkMultipartReplyOperation = ZLinkMultipartSubmitOperation;
|
|
@@ -572,7 +572,6 @@ function requestTerminalResult(
|
|
|
572
572
|
if (signal?.aborted === true) return 'cancelled';
|
|
573
573
|
if (error instanceof ZLinkFrameworkException) {
|
|
574
574
|
if (error.kind === ZLinkFrameworkErrorKind.ShuttingDown) return 'shutdown';
|
|
575
|
-
if (error.kind === ZLinkFrameworkErrorKind.CapacityExceeded) return 'backpressured';
|
|
576
575
|
}
|
|
577
576
|
return 'failed';
|
|
578
577
|
}
|