@zlink-systems/framework 0.31.0 → 0.32.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/Actors/ZLinkActor.d.ts +2 -0
- package/dist/contracts/Actors/ZLinkSessionBindingIdentity.d.ts +6 -0
- package/dist/contracts/Actors/ZLinkSessionBindingIdentity.js +2 -0
- package/dist/contracts/Actors/index.d.ts +1 -0
- package/dist/contracts/Actors/index.js +1 -0
- package/dist/contracts/Common/payload-slab.d.ts +4 -0
- package/dist/contracts/Common/payload-slab.js +30 -0
- package/dist/contracts/Handlers/Contexts.d.ts +1 -0
- package/dist/internal.d.ts +9 -0
- package/dist/internal.js +23 -1
- package/dist/runtime/actors/actor-client.d.ts +1 -1
- package/dist/runtime/actors/actor-client.js +32 -23
- package/dist/runtime/actors/actor-mailbox.d.ts +1 -1
- package/dist/runtime/actors/actor-runtime-state.d.ts +1 -1
- package/dist/runtime/actors/spot-actor-dispatch.d.ts +4 -2
- package/dist/runtime/actors/spot-actor-dispatch.js +2 -1
- package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +1 -0
- package/dist/runtime/application-jobs/application-ingress-record-owner.js +3 -0
- package/dist/runtime/backend/contracts/index.d.ts +2 -0
- package/dist/runtime/backend/mesh-completion-table.d.ts +1 -0
- package/dist/runtime/backend/mesh-completion-table.js +1 -0
- package/dist/runtime/backend/node/node-mesh-backend-adapter.js +5 -1
- package/dist/runtime/backend/node/node-raw-binding-port.d.ts +7 -2
- package/dist/runtime/backend/node/node-raw-binding-port.js +17 -11
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +2 -2
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +15 -8
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +7 -6
- package/dist/runtime/channels/channel-dispatch-pipeline.js +1 -0
- package/dist/runtime/channels/channel-dispatchers.d.ts +7 -3
- package/dist/runtime/channels/channel-dispatchers.js +27 -19
- package/dist/runtime/channels/channel-envelope.js +543 -23
- package/dist/runtime/channels/channel-receive-loops.d.ts +9 -6
- package/dist/runtime/channels/channel-receive-loops.js +59 -18
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +22 -3
- package/dist/runtime/channels/channel-socket-registry.d.ts +22 -4
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +1 -1
- package/dist/runtime/diagnostics/runtime-metrics.js +2 -1
- package/dist/runtime/execution/serial-execution-queue.d.ts +26 -13
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +27 -11
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +29 -25
- package/dist/runtime/foundation/service-liveness-registry.d.ts +1 -1
- package/dist/runtime/foundation/service-mailbox.d.ts +3 -1
- package/dist/runtime/foundation/service-mailbox.js +4 -1
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +3 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +2 -0
- package/dist/runtime/foundation/service-stateful-registry.js +28 -12
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +51 -19
- package/dist/runtime/foundation/service-stateful-runtime.js +54 -5
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +3 -0
- package/dist/runtime/foundation/service-stateful-wire-codec.js +15 -0
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +12 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +2 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.js +17 -7
- package/dist/runtime/host/actor-placement-coordinator.d.ts +1 -1
- package/dist/runtime/host/actor-transfer-runtime.d.ts +3 -3
- package/dist/runtime/host/application-job-queue.d.ts +1 -1
- package/dist/runtime/host/index.d.ts +89 -36
- package/dist/runtime/host/index.js +101 -7
- package/dist/runtime/host/location-runtime-owner.d.ts +2 -2
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +181 -44
- package/dist/runtime/host/service-relocation-host-runtime.js +47 -13
- package/dist/runtime/host/spot-address-transport.js +50 -42
- package/dist/runtime/locations/auto-connect-loop.d.ts +0 -1
- package/dist/runtime/locations/auto-connect-loop.js +25 -19
- package/dist/runtime/locations/auto-connect-planner.js +2 -5
- package/dist/runtime/locations/auto-connect-reconciler.d.ts +1 -0
- package/dist/runtime/locations/auto-connect-reconciler.js +35 -23
- package/dist/runtime/locations/in-memory-location-store.d.ts +0 -2
- package/dist/runtime/locations/in-memory-location-store.js +1 -38
- package/dist/runtime/locations/location-store-repository.d.ts +0 -1
- package/dist/runtime/locations/location-store-repository.js +13 -115
- package/dist/runtime/locations/runtime.d.ts +3 -3
- package/dist/runtime/locations/runtime.js +12 -16
- package/dist/runtime/messaging/creation-payload-codec.js +2 -2
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +4 -0
- package/dist/runtime/protocol/service_wire_codec.generated.js +17 -7
- package/dist/runtime/spots/index.d.ts +20 -5
- package/dist/runtime/spots/index.js +14 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +3 -3
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +13 -7
- package/dist/runtime/spots/spot-actor-packet-drain.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-packet-drain.js +12 -5
- package/dist/runtime/spots/spot-entry-activation.d.ts +2 -1
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +8 -6
- package/dist/runtime/spots/spot-node-runtime-manager.js +2 -0
- package/dist/runtime/spots/spot-route-packet-dispatch.js +7 -4
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +3 -1
- package/dist/runtime/spots/spot-serial-executor.d.ts +2 -1
- package/dist/runtime/spots/spot-subscription-dispatch.js +3 -1
- package/dist/runtime/spots/spot-timer.d.ts +1 -1
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +9 -3
- package/dist/runtime/streams/actor-session-binding-registry.js +17 -0
- package/dist/runtime/streams/index.d.ts +3 -3
- package/dist/runtime/streams/index.js +22 -2
- package/dist/runtime/streams/managed-stream.js +2 -1
- package/dist/runtime/streams/session-calls.d.ts +1 -1
- package/dist/runtime/streams/session-calls.js +3 -9
- package/dist/runtime/streams/session-context.d.ts +2 -2
- package/dist/runtime/streams/session-context.js +2 -2
- package/dist/runtime/streams/stream-session-runtime.d.ts +1 -0
- package/dist/runtime/streams/stream-session-runtime.js +37 -12
- package/package.json +3 -3
- package/src/contracts/Actors/ZLinkActor.ts +6 -0
- package/src/contracts/Actors/ZLinkSessionBindingIdentity.ts +7 -0
- package/src/contracts/Actors/index.ts +1 -0
- package/src/contracts/Common/payload-slab.ts +29 -0
- package/src/contracts/Handlers/Contexts.ts +1 -0
- package/src/internal.ts +17 -0
- package/src/runtime/actors/actor-client.ts +48 -23
- package/src/runtime/actors/actor-mailbox.ts +1 -1
- package/src/runtime/actors/actor-runtime-state.ts +1 -1
- package/src/runtime/actors/spot-actor-dispatch.ts +4 -3
- package/src/runtime/application-jobs/application-ingress-record-owner.ts +4 -0
- package/src/runtime/backend/contracts/index.ts +2 -0
- package/src/runtime/backend/mesh-completion-table.ts +2 -0
- package/src/runtime/backend/node/node-mesh-backend-adapter.ts +5 -1
- package/src/runtime/backend/node/node-raw-binding-port.ts +20 -7
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +18 -10
- package/src/runtime/channels/channel-dispatch-pipeline.ts +6 -5
- package/src/runtime/channels/channel-dispatchers.ts +39 -19
- package/src/runtime/channels/channel-envelope.ts +547 -20
- package/src/runtime/channels/channel-receive-loops.ts +93 -36
- package/src/runtime/channels/channel-runtime-lifecycle.ts +2 -2
- package/src/runtime/channels/channel-socket-registry.ts +3 -3
- package/src/runtime/diagnostics/runtime-metrics.ts +3 -1
- package/src/runtime/execution/serial-execution-queue.ts +13 -13
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +40 -39
- package/src/runtime/foundation/service-liveness-registry.ts +1 -1
- package/src/runtime/foundation/service-mailbox.ts +6 -1
- package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
- package/src/runtime/foundation/service-stateful-registry.ts +39 -14
- package/src/runtime/foundation/service-stateful-runtime.ts +87 -21
- package/src/runtime/foundation/service-stateful-wire-codec.ts +18 -0
- package/src/runtime/foundation/service-wire-constants.generated.ts +10 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +29 -7
- package/src/runtime/host/actor-placement-coordinator.ts +1 -1
- package/src/runtime/host/actor-transfer-runtime.ts +3 -3
- package/src/runtime/host/application-job-queue.ts +1 -1
- package/src/runtime/host/index.ts +183 -44
- package/src/runtime/host/location-runtime-owner.ts +2 -2
- package/src/runtime/host/service-relocation-host-runtime.ts +101 -65
- package/src/runtime/host/spot-address-transport.ts +67 -61
- package/src/runtime/locations/auto-connect-loop.ts +24 -16
- package/src/runtime/locations/auto-connect-planner.ts +2 -5
- package/src/runtime/locations/auto-connect-reconciler.ts +37 -24
- package/src/runtime/locations/in-memory-location-store.ts +1 -74
- package/src/runtime/locations/location-store-repository.ts +32 -153
- package/src/runtime/locations/runtime.ts +16 -28
- package/src/runtime/messaging/creation-payload-codec.ts +3 -3
- package/src/runtime/protocol/service_wire_codec.generated.ts +18 -8
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/index.ts +28 -4
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +1 -1
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +16 -6
- package/src/runtime/spots/spot-actor-packet-drain.ts +21 -6
- package/src/runtime/spots/spot-entry-activation.ts +1 -1
- package/src/runtime/spots/spot-node-runtime-manager.ts +8 -6
- package/src/runtime/spots/spot-route-packet-dispatch.ts +8 -4
- package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +3 -1
- package/src/runtime/spots/spot-serial-executor.ts +1 -1
- package/src/runtime/spots/spot-subscription-dispatch.ts +3 -1
- package/src/runtime/spots/spot-timer.ts +1 -1
- package/src/runtime/streams/actor-session-binding-registry.ts +30 -3
- package/src/runtime/streams/index.ts +30 -4
- package/src/runtime/streams/managed-stream.ts +2 -1
- package/src/runtime/streams/session-calls.ts +9 -14
- package/src/runtime/streams/session-context.ts +6 -11
- package/src/runtime/streams/stream-session-runtime.ts +49 -20
|
@@ -521,33 +521,62 @@ function decodeChannelHeader(parts, flowEnabled = true) {
|
|
|
521
521
|
if (parts.length === 0) {
|
|
522
522
|
throw new configuration_1.ZLinkConfigurationException('Channel envelope header part is missing.');
|
|
523
523
|
}
|
|
524
|
-
return validateChannelHeader(parseChannelHeaderBytes(parts[0].data()), flowEnabled);
|
|
524
|
+
return validateChannelHeader(parseChannelHeaderBytes(parts[0].data(), flowEnabled), flowEnabled);
|
|
525
525
|
}
|
|
526
526
|
const CHANNEL_JSON_PARSE_OPTIONS = Object.freeze({ rejectPropertyName: isPrototypeKey });
|
|
527
527
|
function parseWireJson(payload, schema) {
|
|
528
528
|
return (0, framework_json_v1_1.parseFrameworkJsonV1)(payload, CHANNEL_JSON_PARSE_OPTIONS, schema);
|
|
529
529
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
530
|
+
const CHANNEL_HEADER_FIELD_NAMES = [
|
|
531
|
+
'',
|
|
532
|
+
'formatMarker',
|
|
533
|
+
'kind',
|
|
534
|
+
'channelName',
|
|
535
|
+
'messageName',
|
|
536
|
+
'contentType',
|
|
537
|
+
'correlationId',
|
|
538
|
+
'deadline',
|
|
539
|
+
'topic',
|
|
540
|
+
'errorCode',
|
|
541
|
+
'errorMessage',
|
|
542
|
+
'source',
|
|
543
|
+
'flowId',
|
|
544
|
+
'flowOrigin',
|
|
545
|
+
'metadata'
|
|
546
|
+
];
|
|
547
|
+
const CHANNEL_HEADER_FIELD_COUNT = CHANNEL_HEADER_FIELD_NAMES.length - 1;
|
|
548
|
+
const CHANNEL_HEADER_INVALID_VALUE = Symbol('invalid channel header value');
|
|
549
|
+
const CANONICAL_CHANNEL_HEADER_PREFIX = '{"formatMarker":242,"kind":';
|
|
550
|
+
function parseChannelHeaderBytes(payload, flowEnabled) {
|
|
551
|
+
if (startsWithJson(payload, CANONICAL_CHANNEL_HEADER_PREFIX)) {
|
|
552
|
+
const reader = new CanonicalChannelHeaderReader(payload, CANONICAL_CHANNEL_HEADER_PREFIX.length);
|
|
553
|
+
const canonical = tryParseCanonicalChannelHeader(reader, flowEnabled);
|
|
554
|
+
// Canonical ASCII headers need no UTF-8 pass; all other paths are checked
|
|
555
|
+
// before byte slices are decoded.
|
|
556
|
+
if (canonical !== undefined && !reader.sawNonAscii)
|
|
557
|
+
return canonical;
|
|
558
|
+
if (canonical !== undefined) {
|
|
559
|
+
if (!(0, node_buffer_1.isUtf8)(payload)) {
|
|
560
|
+
throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.ProtocolError, 'Channel envelope header must contain valid UTF-8.');
|
|
561
|
+
}
|
|
562
|
+
return canonical;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
537
565
|
if (!(0, node_buffer_1.isUtf8)(payload)) {
|
|
538
566
|
throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.ProtocolError, 'Channel envelope header must contain valid UTF-8.');
|
|
539
567
|
}
|
|
540
|
-
return
|
|
568
|
+
return new ChannelHeaderJsonReader(payload, flowEnabled).parse();
|
|
541
569
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
570
|
+
function startsWithJson(bytes, value) {
|
|
571
|
+
if (bytes.length < value.length)
|
|
572
|
+
return false;
|
|
573
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
574
|
+
if (bytes[index] !== value.charCodeAt(index))
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
function tryParseCanonicalChannelHeader(reader, flowEnabled) {
|
|
551
580
|
const kind = reader.readSingleDigit();
|
|
552
581
|
if (kind === undefined || !reader.consume(',"channelName":'))
|
|
553
582
|
return undefined;
|
|
@@ -591,10 +620,13 @@ function tryParseCanonicalChannelHeader(reader) {
|
|
|
591
620
|
return undefined;
|
|
592
621
|
if (!reader.readOptionalNullableString(CANONICAL_SOURCE, 'source', header))
|
|
593
622
|
return undefined;
|
|
594
|
-
|
|
595
|
-
if (
|
|
623
|
+
const metadata = reader.readMetadata();
|
|
624
|
+
if (metadata === undefined)
|
|
596
625
|
return undefined;
|
|
597
|
-
|
|
626
|
+
// Empty metadata validates to the shared empty record.
|
|
627
|
+
if (metadata !== NULL_JSON_STRING)
|
|
628
|
+
header.metadata = metadata;
|
|
629
|
+
if (!reader.readOptionalString(CANONICAL_FLOW_ID, 'flowId', header, flowEnabled))
|
|
598
630
|
return undefined;
|
|
599
631
|
if (!reader.readOptionalSingleDigit(CANONICAL_FLOW_ORIGIN, 'flowOrigin', header))
|
|
600
632
|
return undefined;
|
|
@@ -611,8 +643,9 @@ class CanonicalChannelHeaderReader {
|
|
|
611
643
|
index = 0;
|
|
612
644
|
/** A string held a byte outside ASCII, so the caller must still check UTF-8. */
|
|
613
645
|
sawNonAscii = false;
|
|
614
|
-
constructor(bytes) {
|
|
646
|
+
constructor(bytes, initialIndex = 0) {
|
|
615
647
|
this.bytes = bytes;
|
|
648
|
+
this.index = initialIndex;
|
|
616
649
|
}
|
|
617
650
|
atEnd() {
|
|
618
651
|
return this.index === this.bytes.length;
|
|
@@ -687,6 +720,41 @@ class CanonicalChannelHeaderReader {
|
|
|
687
720
|
return NULL_JSON_STRING;
|
|
688
721
|
return this.readString();
|
|
689
722
|
}
|
|
723
|
+
readMetadata() {
|
|
724
|
+
if (!this.consume(',"metadata":{'))
|
|
725
|
+
return undefined;
|
|
726
|
+
if (this.consume('}'))
|
|
727
|
+
return NULL_JSON_STRING;
|
|
728
|
+
const metadata = {};
|
|
729
|
+
for (;;) {
|
|
730
|
+
const key = this.readString();
|
|
731
|
+
if (key === undefined)
|
|
732
|
+
return undefined;
|
|
733
|
+
if (Object.prototype.hasOwnProperty.call(metadata, key)) {
|
|
734
|
+
throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.ProtocolError, 'duplicate envelope metadata key');
|
|
735
|
+
}
|
|
736
|
+
if (!this.consume(':'))
|
|
737
|
+
return undefined;
|
|
738
|
+
const value = this.readString();
|
|
739
|
+
if (value === undefined)
|
|
740
|
+
return undefined;
|
|
741
|
+
if (key === '__proto__') {
|
|
742
|
+
Object.defineProperty(metadata, key, {
|
|
743
|
+
configurable: true,
|
|
744
|
+
enumerable: true,
|
|
745
|
+
value,
|
|
746
|
+
writable: true
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
metadata[key] = value;
|
|
751
|
+
}
|
|
752
|
+
if (this.consume('}'))
|
|
753
|
+
return metadata;
|
|
754
|
+
if (!this.consume(','))
|
|
755
|
+
return undefined;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
690
758
|
readOptionalNullableString(token, name, target) {
|
|
691
759
|
if (!this.consume(token))
|
|
692
760
|
return true;
|
|
@@ -696,15 +764,54 @@ class CanonicalChannelHeaderReader {
|
|
|
696
764
|
target[name] = value === NULL_JSON_STRING ? null : value;
|
|
697
765
|
return true;
|
|
698
766
|
}
|
|
699
|
-
readOptionalString(token, name, target) {
|
|
767
|
+
readOptionalString(token, name, target, decode = true) {
|
|
700
768
|
if (!this.consume(token))
|
|
701
769
|
return true;
|
|
770
|
+
if (!decode)
|
|
771
|
+
return this.skipString();
|
|
702
772
|
const value = this.readString();
|
|
703
773
|
if (value === undefined)
|
|
704
774
|
return false;
|
|
705
775
|
target[name] = value;
|
|
706
776
|
return true;
|
|
707
777
|
}
|
|
778
|
+
skipString() {
|
|
779
|
+
if (this.bytes[this.index] !== 0x22)
|
|
780
|
+
return false;
|
|
781
|
+
this.index += 1;
|
|
782
|
+
while (this.index < this.bytes.length) {
|
|
783
|
+
const value = this.bytes[this.index++];
|
|
784
|
+
if (value === 0x22)
|
|
785
|
+
return true;
|
|
786
|
+
if (value < 0x20)
|
|
787
|
+
return false;
|
|
788
|
+
if (value >= 0x80)
|
|
789
|
+
this.sawNonAscii = true;
|
|
790
|
+
if (value !== 0x5c)
|
|
791
|
+
continue;
|
|
792
|
+
if (this.index === this.bytes.length)
|
|
793
|
+
return false;
|
|
794
|
+
const escaped = this.bytes[this.index++];
|
|
795
|
+
if (escaped === 0x75) {
|
|
796
|
+
for (let count = 0; count < 4; count += 1) {
|
|
797
|
+
if (this.index === this.bytes.length || !isHexDigit(this.bytes[this.index]))
|
|
798
|
+
return false;
|
|
799
|
+
this.index += 1;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
else if (escaped !== 0x22 &&
|
|
803
|
+
escaped !== 0x5c &&
|
|
804
|
+
escaped !== 0x2f &&
|
|
805
|
+
escaped !== 0x62 &&
|
|
806
|
+
escaped !== 0x66 &&
|
|
807
|
+
escaped !== 0x6e &&
|
|
808
|
+
escaped !== 0x72 &&
|
|
809
|
+
escaped !== 0x74) {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
708
815
|
readOptionalSingleDigit(token, name, target) {
|
|
709
816
|
if (!this.consume(token))
|
|
710
817
|
return true;
|
|
@@ -720,6 +827,419 @@ function isHexDigit(value) {
|
|
|
720
827
|
(value >= 0x41 && value <= 0x46) ||
|
|
721
828
|
(value >= 0x61 && value <= 0x66));
|
|
722
829
|
}
|
|
830
|
+
/** Reads a noncanonical channel header directly from its UTF-8 bytes. */
|
|
831
|
+
class ChannelHeaderJsonReader {
|
|
832
|
+
bytes;
|
|
833
|
+
flowEnabled;
|
|
834
|
+
index = 0;
|
|
835
|
+
propertyName = '';
|
|
836
|
+
stringStart = 0;
|
|
837
|
+
stringEnd = 0;
|
|
838
|
+
stringHasEscapes = false;
|
|
839
|
+
constructor(bytes, flowEnabled) {
|
|
840
|
+
this.bytes = bytes;
|
|
841
|
+
this.flowEnabled = flowEnabled;
|
|
842
|
+
}
|
|
843
|
+
parse() {
|
|
844
|
+
if (this.bytes.length >= 3 &&
|
|
845
|
+
this.bytes[0] === 0xef &&
|
|
846
|
+
this.bytes[1] === 0xbb &&
|
|
847
|
+
this.bytes[2] === 0xbf) {
|
|
848
|
+
throw new SyntaxError('framework-json-v1 does not allow a UTF-8 BOM.');
|
|
849
|
+
}
|
|
850
|
+
this.skipWhitespace();
|
|
851
|
+
if (this.bytes[this.index] !== 0x7b) {
|
|
852
|
+
this.skipValue();
|
|
853
|
+
this.skipWhitespace();
|
|
854
|
+
if (this.index !== this.bytes.length)
|
|
855
|
+
this.fail('trailing data');
|
|
856
|
+
return CHANNEL_HEADER_INVALID_VALUE;
|
|
857
|
+
}
|
|
858
|
+
const header = {};
|
|
859
|
+
let seenFields = 0;
|
|
860
|
+
this.index += 1;
|
|
861
|
+
this.skipWhitespace();
|
|
862
|
+
if (this.consumeByte(0x7d))
|
|
863
|
+
return this.finish(header);
|
|
864
|
+
for (;;) {
|
|
865
|
+
const field = this.readPropertyName(true);
|
|
866
|
+
const propertyName = this.propertyName;
|
|
867
|
+
this.skipWhitespace();
|
|
868
|
+
this.expectByte(0x3a, "':' after object property");
|
|
869
|
+
this.skipWhitespace();
|
|
870
|
+
if (field !== 0) {
|
|
871
|
+
const bit = 1 << field;
|
|
872
|
+
if ((seenFields & bit) !== 0) {
|
|
873
|
+
throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.ProtocolError, 'duplicate envelope header field');
|
|
874
|
+
}
|
|
875
|
+
seenFields |= bit;
|
|
876
|
+
const name = CHANNEL_HEADER_FIELD_NAMES[field];
|
|
877
|
+
if (!this.flowEnabled && (field === 12 || field === 13)) {
|
|
878
|
+
this.skipValue();
|
|
879
|
+
header[name] = undefined;
|
|
880
|
+
}
|
|
881
|
+
else if (field === 14) {
|
|
882
|
+
header[name] = this.readMetadata();
|
|
883
|
+
}
|
|
884
|
+
else {
|
|
885
|
+
header[name] = this.readValue(field === 1 || field === 2 || field === 13);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
const value = this.readValue();
|
|
890
|
+
if (propertyName === '__proto__') {
|
|
891
|
+
Object.defineProperty(header, propertyName, {
|
|
892
|
+
configurable: true,
|
|
893
|
+
enumerable: true,
|
|
894
|
+
value,
|
|
895
|
+
writable: true
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
header[propertyName] = value;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
this.skipWhitespace();
|
|
903
|
+
if (this.consumeByte(0x7d))
|
|
904
|
+
break;
|
|
905
|
+
this.expectByte(0x2c, "',' or '}' after object property");
|
|
906
|
+
this.skipWhitespace();
|
|
907
|
+
}
|
|
908
|
+
return this.finish(header);
|
|
909
|
+
}
|
|
910
|
+
finish(header) {
|
|
911
|
+
this.skipWhitespace();
|
|
912
|
+
if (this.index !== this.bytes.length)
|
|
913
|
+
this.fail('trailing data');
|
|
914
|
+
return header;
|
|
915
|
+
}
|
|
916
|
+
readMetadata() {
|
|
917
|
+
if (this.bytes[this.index] !== 0x7b)
|
|
918
|
+
return this.readValue();
|
|
919
|
+
this.index += 1;
|
|
920
|
+
this.skipWhitespace();
|
|
921
|
+
if (this.consumeByte(0x7d))
|
|
922
|
+
return undefined;
|
|
923
|
+
const metadata = {};
|
|
924
|
+
for (;;) {
|
|
925
|
+
const key = this.readString();
|
|
926
|
+
if (Object.prototype.hasOwnProperty.call(metadata, key)) {
|
|
927
|
+
throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.ProtocolError, 'duplicate envelope metadata key');
|
|
928
|
+
}
|
|
929
|
+
this.skipWhitespace();
|
|
930
|
+
this.expectByte(0x3a, "':' after object property");
|
|
931
|
+
this.skipWhitespace();
|
|
932
|
+
const value = this.readValue();
|
|
933
|
+
if (key === '__proto__') {
|
|
934
|
+
Object.defineProperty(metadata, key, {
|
|
935
|
+
configurable: true,
|
|
936
|
+
enumerable: true,
|
|
937
|
+
value,
|
|
938
|
+
writable: true
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
metadata[key] = value;
|
|
943
|
+
}
|
|
944
|
+
this.skipWhitespace();
|
|
945
|
+
if (this.consumeByte(0x7d))
|
|
946
|
+
return metadata;
|
|
947
|
+
this.expectByte(0x2c, "',' or '}' after object property");
|
|
948
|
+
this.skipWhitespace();
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
readPropertyName(materializeUnknown = false) {
|
|
952
|
+
this.scanString();
|
|
953
|
+
let field = 0;
|
|
954
|
+
let decoded;
|
|
955
|
+
if (this.stringHasEscapes) {
|
|
956
|
+
decoded = this.decodeString();
|
|
957
|
+
for (let candidate = 1; candidate <= CHANNEL_HEADER_FIELD_COUNT; candidate += 1) {
|
|
958
|
+
if (CHANNEL_HEADER_FIELD_NAMES[candidate] === decoded) {
|
|
959
|
+
field = candidate;
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
else {
|
|
965
|
+
for (let candidate = 1; candidate <= CHANNEL_HEADER_FIELD_COUNT; candidate += 1) {
|
|
966
|
+
if (this.matchesAscii(this.stringStart + 1, this.stringEnd - 1, CHANNEL_HEADER_FIELD_NAMES[candidate])) {
|
|
967
|
+
field = candidate;
|
|
968
|
+
break;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
this.propertyName =
|
|
973
|
+
field !== 0
|
|
974
|
+
? CHANNEL_HEADER_FIELD_NAMES[field]
|
|
975
|
+
: (decoded ??
|
|
976
|
+
(materializeUnknown
|
|
977
|
+
? this.bytes.toString('utf8', this.stringStart + 1, this.stringEnd - 1)
|
|
978
|
+
: ''));
|
|
979
|
+
return field;
|
|
980
|
+
}
|
|
981
|
+
readValue(integerOnly = false) {
|
|
982
|
+
const value = this.bytes[this.index];
|
|
983
|
+
if (value === 0x22)
|
|
984
|
+
return this.readString();
|
|
985
|
+
if (value === 0x7b)
|
|
986
|
+
return this.readObject();
|
|
987
|
+
if (value === 0x5b)
|
|
988
|
+
return this.readArray();
|
|
989
|
+
if (value === 0x74) {
|
|
990
|
+
this.readLiteral('true');
|
|
991
|
+
return true;
|
|
992
|
+
}
|
|
993
|
+
if (value === 0x66) {
|
|
994
|
+
this.readLiteral('false');
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
if (value === 0x6e) {
|
|
998
|
+
this.readLiteral('null');
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
return this.readNumber(integerOnly);
|
|
1002
|
+
}
|
|
1003
|
+
readObject() {
|
|
1004
|
+
this.index += 1;
|
|
1005
|
+
this.skipWhitespace();
|
|
1006
|
+
const object = {};
|
|
1007
|
+
if (this.consumeByte(0x7d))
|
|
1008
|
+
return object;
|
|
1009
|
+
for (;;) {
|
|
1010
|
+
const key = this.readString();
|
|
1011
|
+
this.skipWhitespace();
|
|
1012
|
+
this.expectByte(0x3a, "':' after object property");
|
|
1013
|
+
this.skipWhitespace();
|
|
1014
|
+
const value = this.readValue();
|
|
1015
|
+
if (key === '__proto__') {
|
|
1016
|
+
Object.defineProperty(object, key, {
|
|
1017
|
+
configurable: true,
|
|
1018
|
+
enumerable: true,
|
|
1019
|
+
value,
|
|
1020
|
+
writable: true
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
object[key] = value;
|
|
1025
|
+
}
|
|
1026
|
+
this.skipWhitespace();
|
|
1027
|
+
if (this.consumeByte(0x7d))
|
|
1028
|
+
return object;
|
|
1029
|
+
this.expectByte(0x2c, "',' or '}' after object property");
|
|
1030
|
+
this.skipWhitespace();
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
readArray() {
|
|
1034
|
+
this.index += 1;
|
|
1035
|
+
this.skipWhitespace();
|
|
1036
|
+
const array = [];
|
|
1037
|
+
if (this.consumeByte(0x5d))
|
|
1038
|
+
return array;
|
|
1039
|
+
for (;;) {
|
|
1040
|
+
array.push(this.readValue());
|
|
1041
|
+
this.skipWhitespace();
|
|
1042
|
+
if (this.consumeByte(0x5d))
|
|
1043
|
+
return array;
|
|
1044
|
+
this.expectByte(0x2c, "',' or ']' after array item");
|
|
1045
|
+
this.skipWhitespace();
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
skipValue() {
|
|
1049
|
+
const value = this.bytes[this.index];
|
|
1050
|
+
if (value === 0x22) {
|
|
1051
|
+
this.scanString();
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
if (value === 0x7b)
|
|
1055
|
+
return this.skipObject();
|
|
1056
|
+
if (value === 0x5b)
|
|
1057
|
+
return this.skipArray();
|
|
1058
|
+
if (value === 0x74)
|
|
1059
|
+
return this.readLiteral('true');
|
|
1060
|
+
if (value === 0x66)
|
|
1061
|
+
return this.readLiteral('false');
|
|
1062
|
+
if (value === 0x6e)
|
|
1063
|
+
return this.readLiteral('null');
|
|
1064
|
+
this.readNumber();
|
|
1065
|
+
}
|
|
1066
|
+
skipObject() {
|
|
1067
|
+
this.index += 1;
|
|
1068
|
+
this.skipWhitespace();
|
|
1069
|
+
if (this.consumeByte(0x7d))
|
|
1070
|
+
return;
|
|
1071
|
+
for (;;) {
|
|
1072
|
+
this.readPropertyName();
|
|
1073
|
+
this.skipWhitespace();
|
|
1074
|
+
this.expectByte(0x3a, "':' after object property");
|
|
1075
|
+
this.skipWhitespace();
|
|
1076
|
+
this.skipValue();
|
|
1077
|
+
this.skipWhitespace();
|
|
1078
|
+
if (this.consumeByte(0x7d))
|
|
1079
|
+
return;
|
|
1080
|
+
this.expectByte(0x2c, "',' or '}' after object property");
|
|
1081
|
+
this.skipWhitespace();
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
skipArray() {
|
|
1085
|
+
this.index += 1;
|
|
1086
|
+
this.skipWhitespace();
|
|
1087
|
+
if (this.consumeByte(0x5d))
|
|
1088
|
+
return;
|
|
1089
|
+
for (;;) {
|
|
1090
|
+
this.skipValue();
|
|
1091
|
+
this.skipWhitespace();
|
|
1092
|
+
if (this.consumeByte(0x5d))
|
|
1093
|
+
return;
|
|
1094
|
+
this.expectByte(0x2c, "',' or ']' after array item");
|
|
1095
|
+
this.skipWhitespace();
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
readString() {
|
|
1099
|
+
this.scanString();
|
|
1100
|
+
return this.decodeString();
|
|
1101
|
+
}
|
|
1102
|
+
scanString() {
|
|
1103
|
+
if (this.bytes[this.index] !== 0x22)
|
|
1104
|
+
this.fail('string');
|
|
1105
|
+
this.stringStart = this.index++;
|
|
1106
|
+
this.stringHasEscapes = false;
|
|
1107
|
+
while (this.index < this.bytes.length) {
|
|
1108
|
+
const value = this.bytes[this.index++];
|
|
1109
|
+
if (value === 0x22) {
|
|
1110
|
+
this.stringEnd = this.index;
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
if (value < 0x20)
|
|
1114
|
+
this.fail('valid string character');
|
|
1115
|
+
if (value !== 0x5c)
|
|
1116
|
+
continue;
|
|
1117
|
+
this.stringHasEscapes = true;
|
|
1118
|
+
if (this.index === this.bytes.length)
|
|
1119
|
+
this.fail('valid string escape');
|
|
1120
|
+
const escaped = this.bytes[this.index++];
|
|
1121
|
+
if (escaped === 0x75) {
|
|
1122
|
+
for (let count = 0; count < 4; count += 1) {
|
|
1123
|
+
if (this.index === this.bytes.length || !isHexDigit(this.bytes[this.index]))
|
|
1124
|
+
this.fail('four hexadecimal digits after \\u');
|
|
1125
|
+
this.index += 1;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
else if (escaped !== 0x22 &&
|
|
1129
|
+
escaped !== 0x5c &&
|
|
1130
|
+
escaped !== 0x2f &&
|
|
1131
|
+
escaped !== 0x62 &&
|
|
1132
|
+
escaped !== 0x66 &&
|
|
1133
|
+
escaped !== 0x6e &&
|
|
1134
|
+
escaped !== 0x72 &&
|
|
1135
|
+
escaped !== 0x74) {
|
|
1136
|
+
this.fail('valid string escape');
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
this.fail('terminated string');
|
|
1140
|
+
}
|
|
1141
|
+
decodeString() {
|
|
1142
|
+
return this.stringHasEscapes
|
|
1143
|
+
? JSON.parse(this.bytes.toString('utf8', this.stringStart, this.stringEnd))
|
|
1144
|
+
: this.bytes.toString('utf8', this.stringStart + 1, this.stringEnd - 1);
|
|
1145
|
+
}
|
|
1146
|
+
readNumber(integerOnly = false) {
|
|
1147
|
+
const start = this.index;
|
|
1148
|
+
let hasFractionOrExponent = false;
|
|
1149
|
+
if (this.bytes[this.index] === 0x2d)
|
|
1150
|
+
this.index += 1;
|
|
1151
|
+
if (this.bytes[this.index] === 0x30) {
|
|
1152
|
+
this.index += 1;
|
|
1153
|
+
}
|
|
1154
|
+
else if (this.isDigitOneToNine(this.bytes[this.index])) {
|
|
1155
|
+
while (this.isDigit(this.bytes[this.index]))
|
|
1156
|
+
this.index += 1;
|
|
1157
|
+
}
|
|
1158
|
+
else {
|
|
1159
|
+
this.fail('JSON value');
|
|
1160
|
+
}
|
|
1161
|
+
if (this.bytes[this.index] === 0x2e) {
|
|
1162
|
+
hasFractionOrExponent = true;
|
|
1163
|
+
this.index += 1;
|
|
1164
|
+
if (!this.isDigit(this.bytes[this.index]))
|
|
1165
|
+
this.fail('digit after decimal point');
|
|
1166
|
+
while (this.isDigit(this.bytes[this.index]))
|
|
1167
|
+
this.index += 1;
|
|
1168
|
+
}
|
|
1169
|
+
if (this.bytes[this.index] === 0x65 || this.bytes[this.index] === 0x45) {
|
|
1170
|
+
hasFractionOrExponent = true;
|
|
1171
|
+
this.index += 1;
|
|
1172
|
+
if (this.bytes[this.index] === 0x2b || this.bytes[this.index] === 0x2d)
|
|
1173
|
+
this.index += 1;
|
|
1174
|
+
if (!this.isDigit(this.bytes[this.index]))
|
|
1175
|
+
this.fail('digit in exponent');
|
|
1176
|
+
while (this.isDigit(this.bytes[this.index]))
|
|
1177
|
+
this.index += 1;
|
|
1178
|
+
}
|
|
1179
|
+
if (integerOnly && hasFractionOrExponent) {
|
|
1180
|
+
throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.ProtocolError, 'Channel envelope integer fields must use an integer literal.');
|
|
1181
|
+
}
|
|
1182
|
+
if (integerOnly) {
|
|
1183
|
+
const negative = this.bytes[start] === 0x2d;
|
|
1184
|
+
const digitsStart = start + (negative ? 1 : 0);
|
|
1185
|
+
if (this.index - digitsStart <= 3) {
|
|
1186
|
+
let integer = 0;
|
|
1187
|
+
for (let offset = digitsStart; offset < this.index; offset += 1) {
|
|
1188
|
+
integer = integer * 10 + this.bytes[offset] - 0x30;
|
|
1189
|
+
}
|
|
1190
|
+
return negative ? -integer : integer;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
const number = Number(this.bytes.toString('ascii', start, this.index));
|
|
1194
|
+
if (!Number.isFinite(number)) {
|
|
1195
|
+
throw new TypeError('framework-json-v1 only accepts finite JSON numbers.');
|
|
1196
|
+
}
|
|
1197
|
+
return number;
|
|
1198
|
+
}
|
|
1199
|
+
readLiteral(value) {
|
|
1200
|
+
for (let offset = 0; offset < value.length; offset += 1) {
|
|
1201
|
+
if (this.bytes[this.index + offset] !== value.charCodeAt(offset))
|
|
1202
|
+
this.fail(value);
|
|
1203
|
+
}
|
|
1204
|
+
this.index += value.length;
|
|
1205
|
+
}
|
|
1206
|
+
matchesAscii(start, end, value) {
|
|
1207
|
+
if (end - start !== value.length)
|
|
1208
|
+
return false;
|
|
1209
|
+
for (let offset = 0; offset < value.length; offset += 1) {
|
|
1210
|
+
if (this.bytes[start + offset] !== value.charCodeAt(offset))
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1213
|
+
return true;
|
|
1214
|
+
}
|
|
1215
|
+
isDigit(value) {
|
|
1216
|
+
return value !== undefined && value >= 0x30 && value <= 0x39;
|
|
1217
|
+
}
|
|
1218
|
+
isDigitOneToNine(value) {
|
|
1219
|
+
return value !== undefined && value >= 0x31 && value <= 0x39;
|
|
1220
|
+
}
|
|
1221
|
+
skipWhitespace() {
|
|
1222
|
+
while (this.bytes[this.index] === 0x20 ||
|
|
1223
|
+
this.bytes[this.index] === 0x09 ||
|
|
1224
|
+
this.bytes[this.index] === 0x0a ||
|
|
1225
|
+
this.bytes[this.index] === 0x0d) {
|
|
1226
|
+
this.index += 1;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
consumeByte(value) {
|
|
1230
|
+
if (this.bytes[this.index] !== value)
|
|
1231
|
+
return false;
|
|
1232
|
+
this.index += 1;
|
|
1233
|
+
return true;
|
|
1234
|
+
}
|
|
1235
|
+
expectByte(value, expected) {
|
|
1236
|
+
if (!this.consumeByte(value))
|
|
1237
|
+
this.fail(expected);
|
|
1238
|
+
}
|
|
1239
|
+
fail(expected) {
|
|
1240
|
+
throw new SyntaxError(`framework-json-v1 expected ${expected} at offset ${this.index}.`);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
723
1243
|
function schemaForInboundChannelEnvelope(header) {
|
|
724
1244
|
const contract = (0, Attributes_1.readZLinkPacketJsonContract)(header.messageName);
|
|
725
1245
|
return header.kind === 2 /* ZLinkChannelMessageKind.Response */ ? contract?.reply : contract?.payload;
|