@serve.zone/interfaces 31.9.0 → 32.0.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/.smartconfig.json +1 -0
- package/changelog.md +467 -0
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/appstore/types.d.ts +4 -6
- package/dist_ts/appstore/types.js +5 -5
- package/dist_ts/data/backup.d.ts +3 -26
- package/dist_ts/data/cluster.d.ts +36 -11
- package/dist_ts/data/cluster.js +33 -22
- package/dist_ts/data/clusteringress.d.ts +8 -0
- package/dist_ts/data/clusteringress.js +13 -8
- package/dist_ts/data/clusternode.d.ts +121 -74
- package/dist_ts/data/clusternode.js +109 -132
- package/dist_ts/data/coremail.d.ts +3 -7
- package/dist_ts/data/coremail.js +2 -2
- package/dist_ts/data/coremail.runtime.d.ts +0 -1
- package/dist_ts/data/coremail.runtime.js +5 -22
- package/dist_ts/data/deploymentadoption.d.ts +0 -2
- package/dist_ts/data/deploymentadoption.js +40 -6
- package/dist_ts/data/deploymentoperation.js +16 -3
- package/dist_ts/data/deploymentpreflight.d.ts +0 -2
- package/dist_ts/data/deploymentpreflight.js +1 -1
- package/dist_ts/data/immutableimage.d.ts +38 -21
- package/dist_ts/data/immutableimage.js +45 -5
- package/dist_ts/data/index.d.ts +1 -1
- package/dist_ts/data/isolatedrestore.d.ts +139 -21
- package/dist_ts/data/isolatedrestore.golden.d.ts +0 -1
- package/dist_ts/data/isolatedrestore.golden.js +6 -9
- package/dist_ts/data/isolatedrestore.js +176 -40
- package/dist_ts/data/mail.d.ts +1 -1
- package/dist_ts/data/networkaddressplan.d.ts +8 -8
- package/dist_ts/data/networkaddressplan.js +4 -4
- package/dist_ts/data/networkauthority.d.ts +20 -22
- package/dist_ts/data/networkauthority.js +26 -28
- package/dist_ts/data/networkcredential.d.ts +10 -10
- package/dist_ts/data/networkcredential.js +16 -16
- package/dist_ts/data/networkdelivery.d.ts +18 -18
- package/dist_ts/data/networkdelivery.js +16 -16
- package/dist_ts/data/networklease.d.ts +18 -7
- package/dist_ts/data/networklease.js +29 -7
- package/dist_ts/data/networknode.d.ts +2 -2
- package/dist_ts/data/networkprojection.d.ts +59 -60
- package/dist_ts/data/networkprojection.js +53 -57
- package/dist_ts/data/networkprotection.d.ts +27 -28
- package/dist_ts/data/networkprotection.js +36 -36
- package/dist_ts/data/networkrouter.d.ts +11 -11
- package/dist_ts/data/networkrouter.js +1 -1
- package/dist_ts/data/networksigning.d.ts +16 -18
- package/dist_ts/data/networksigning.js +20 -21
- package/dist_ts/data/nodecredential.d.ts +4 -6
- package/dist_ts/data/nodeenrollment.d.ts +35 -40
- package/dist_ts/data/nodeenrollment.js +57 -72
- package/dist_ts/data/privatenetwork.d.ts +21 -24
- package/dist_ts/data/privatenetwork.js +39 -54
- package/dist_ts/data/runtimeassignment.d.ts +50 -34
- package/dist_ts/data/runtimeassignment.js +79 -45
- package/dist_ts/data/runtimeconfig.d.ts +21 -22
- package/dist_ts/data/runtimeconfig.js +24 -24
- package/dist_ts/data/runtimesession.d.ts +100 -62
- package/dist_ts/data/runtimesession.js +98 -79
- package/dist_ts/data/runtimeterminalreceipt.d.ts +28 -29
- package/dist_ts/data/runtimeterminalreceipt.js +42 -42
- package/dist_ts/data/secret.d.ts +18 -25
- package/dist_ts/data/secret.js +12 -52
- package/dist_ts/data/servicecredential.d.ts +14 -16
- package/dist_ts/data/servicecredential.js +17 -28
- package/dist_ts/data/serviceruntime.d.ts +7 -7
- package/dist_ts/data/serviceruntime.js +4 -4
- package/dist_ts/data/sparknodereport.d.ts +27 -14
- package/dist_ts/data/sparknodereport.js +35 -28
- package/dist_ts/data/webpush.d.ts +0 -1
- package/dist_ts/index.d.ts +2 -1
- package/dist_ts/index.js +3 -2
- package/dist_ts/platform/objectstorageretention.d.ts +21 -24
- package/dist_ts/platform/objectstorageretention.js +6 -18
- package/dist_ts/platform/storage.d.ts +1 -3
- package/dist_ts/platform/storagemigration.d.ts +0 -7
- package/dist_ts/platform/storagemigration.golden.d.ts +0 -1
- package/dist_ts/platform/storagemigration.golden.js +8 -12
- package/dist_ts/platform/storagemigration.js +6 -51
- package/dist_ts/platform/types.d.ts +2 -2
- package/dist_ts/private/canonicaljson.d.ts +9 -1
- package/dist_ts/private/canonicaljson.js +14 -2
- package/dist_ts/private/dataproperties.d.ts +62 -0
- package/dist_ts/private/dataproperties.js +117 -0
- package/dist_ts/private/networkcontract.d.ts +10 -10
- package/dist_ts/private/networkcontract.js +1 -1
- package/dist_ts/private/releaseversion.d.ts +37 -0
- package/dist_ts/private/releaseversion.js +48 -0
- package/dist_ts/private/runtimepolicy.d.ts +2 -2
- package/dist_ts/private/runtimepolicy.js +1 -1
- package/dist_ts/private/tokenlist.d.ts +9 -0
- package/dist_ts/private/tokenlist.js +23 -0
- package/dist_ts/protocol.d.ts +137 -0
- package/dist_ts/protocol.js +255 -0
- package/dist_ts/requests/admin.d.ts +8 -8
- package/dist_ts/requests/backup.d.ts +102 -112
- package/dist_ts/requests/cluster.d.ts +10 -10
- package/dist_ts/requests/clusteringress.d.ts +9 -0
- package/dist_ts/requests/clusteringress.js +1 -1
- package/dist_ts/requests/config.d.ts +3 -3
- package/dist_ts/requests/corestore.d.ts +0 -3
- package/dist_ts/requests/corestore.js +5 -9
- package/dist_ts/requests/network.d.ts +30 -30
- package/dist_ts/requests/node.d.ts +15 -28
- package/dist_ts/requests/pallet.d.ts +13 -13
- package/dist_ts/requests/runtimesession.d.ts +53 -41
- package/dist_ts/requests/secret.d.ts +4 -5
- package/dist_ts/requests/secret.js +1 -5
- package/dist_ts/runtime.corestore.d.ts +56 -74
- package/dist_ts/runtime.corestore.databaseexportpackage.d.ts +30 -36
- package/dist_ts/runtime.corestore.databaseexportpackage.golden.d.ts +11 -12
- package/dist_ts/runtime.corestore.databaseexportpackage.golden.js +54 -71
- package/dist_ts/runtime.corestore.databaseexportpackage.js +9 -25
- package/dist_ts/runtime.corestore.js +35 -135
- package/dist_ts/runtime.corestore.relay.js +2 -7
- package/dist_ts/runtime.d.ts +38 -48
- package/dist_ts/runtime.js +20 -77
- package/dist_ts/runtime.networkprojection.d.ts +4 -4
- package/dist_ts/runtime.networkprojection.js +18 -18
- package/dist_ts/runtime.workloadinit.d.ts +23 -24
- package/dist_ts/runtime.workloadinit.js +6 -13
- package/package.json +2 -2
- package/readme.md +421 -270
- package/ts/00_commitinfo_data.ts +3 -3
- package/ts/appstore/types.ts +4 -6
- package/ts/data/backup.ts +3 -38
- package/ts/data/cluster.ts +51 -34
- package/ts/data/clusteringress.ts +12 -12
- package/ts/data/clusternode.ts +204 -232
- package/ts/data/coremail.runtime.ts +4 -21
- package/ts/data/coremail.ts +3 -7
- package/ts/data/deploymentadoption.ts +46 -4
- package/ts/data/deploymentoperation.ts +16 -1
- package/ts/data/deploymentpreflight.ts +0 -2
- package/ts/data/immutableimage.ts +71 -24
- package/ts/data/index.ts +1 -1
- package/ts/data/isolatedrestore.golden.ts +5 -9
- package/ts/data/isolatedrestore.ts +339 -54
- package/ts/data/mail.ts +1 -1
- package/ts/data/networkaddressplan.ts +13 -13
- package/ts/data/networkauthority.ts +42 -46
- package/ts/data/networkcredential.ts +24 -24
- package/ts/data/networkdelivery.ts +27 -27
- package/ts/data/networklease.ts +40 -13
- package/ts/data/networknode.ts +2 -2
- package/ts/data/networkprojection.ts +119 -124
- package/ts/data/networkprotection.ts +54 -55
- package/ts/data/networkrouter.ts +14 -14
- package/ts/data/networksigning.ts +29 -32
- package/ts/data/nodecredential.ts +4 -6
- package/ts/data/nodeenrollment.ts +96 -114
- package/ts/data/privatenetwork.ts +58 -74
- package/ts/data/runtimeassignment.ts +143 -94
- package/ts/data/runtimeconfig.ts +45 -46
- package/ts/data/runtimesession.ts +185 -138
- package/ts/data/runtimeterminalreceipt.ts +72 -73
- package/ts/data/secret.ts +28 -76
- package/ts/data/servicecredential.ts +33 -43
- package/ts/data/serviceruntime.ts +11 -11
- package/ts/data/sparknodereport.ts +48 -36
- package/ts/data/webpush.ts +0 -1
- package/ts/index.ts +2 -0
- package/ts/platform/objectstorageretention.ts +36 -55
- package/ts/platform/storage.ts +1 -3
- package/ts/platform/storagemigration.golden.ts +7 -11
- package/ts/platform/storagemigration.ts +5 -55
- package/ts/platform/types.ts +2 -2
- package/ts/private/canonicaljson.ts +13 -1
- package/ts/private/dataproperties.ts +139 -0
- package/ts/private/networkcontract.ts +9 -9
- package/ts/private/releaseversion.ts +70 -0
- package/ts/private/runtimepolicy.ts +2 -2
- package/ts/private/tokenlist.ts +30 -0
- package/ts/protocol.ts +364 -0
- package/ts/requests/admin.ts +11 -11
- package/ts/requests/backup.ts +112 -117
- package/ts/requests/cluster.ts +13 -13
- package/ts/requests/clusteringress.ts +9 -0
- package/ts/requests/config.ts +3 -3
- package/ts/requests/corestore.ts +4 -16
- package/ts/requests/network.ts +40 -40
- package/ts/requests/node.ts +21 -38
- package/ts/requests/pallet.ts +17 -17
- package/ts/requests/runtimesession.ts +63 -51
- package/ts/requests/secret.ts +5 -10
- package/ts/runtime.corestore.databaseexportpackage.golden.ts +77 -95
- package/ts/runtime.corestore.databaseexportpackage.ts +51 -73
- package/ts/runtime.corestore.relay.ts +1 -10
- package/ts/runtime.corestore.ts +118 -268
- package/ts/runtime.networkprojection.ts +30 -30
- package/ts/runtime.ts +83 -158
- package/ts/runtime.workloadinit.ts +36 -48
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.d.ts +0 -902
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.d.ts +0 -35
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.js +0 -315
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.js +0 -3315
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.d.ts +0 -291
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.d.ts +0 -30
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.js +0 -408
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.js +0 -1126
- package/ts/runtime.cloudlylegacydeploymentsettlement.golden.ts +0 -421
- package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.ts +0 -510
- package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.ts +0 -2470
- package/ts/runtime.cloudlylegacydeploymentsettlement.ts +0 -6630
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import type { IIdentityCredential } from './user.js';
|
|
2
2
|
import { isSha256Digest } from './immutableimage.js';
|
|
3
3
|
import type {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
IRuntimeAssignmentObservation, IRuntimeAssignmentReference, IRuntimeAssignment,
|
|
5
|
+
IRuntimeController, TRuntimeWorkloadAuthority,
|
|
6
6
|
} from './runtimeassignment.js';
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
getRuntimeAssignmentReference, isResolvedWorkloadAuthorities, snapshotRuntimeAssignmentObservation,
|
|
9
|
+
snapshotRuntimeAssignment, validateRuntimeAssignmentObservation, validateRuntimeAssignment,
|
|
10
10
|
} from './runtimeassignment.js';
|
|
11
|
-
import type
|
|
12
|
-
import {
|
|
11
|
+
import { negotiateProtocol, readProtocolOffer, type IProtocolOffer } from '../protocol.js';
|
|
12
|
+
import type { IRuntimeConfig } from './runtimeconfig.js';
|
|
13
|
+
import { bindRuntimeConfigToAssignment, snapshotRuntimeConfig } from './runtimeconfig.js';
|
|
13
14
|
import { canonicalizeStrictJson } from '../private/canonicaljson.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -18,20 +19,18 @@ import { canonicalizeStrictJson } from '../private/canonicaljson.js';
|
|
|
18
19
|
* is not authentication: every receiver must compare it with its live peer context.
|
|
19
20
|
* Onebox local execution uses the same shape with its own authenticated IPC owner.
|
|
20
21
|
*/
|
|
21
|
-
export interface
|
|
22
|
-
schemaVersion: 1;
|
|
22
|
+
export interface IRuntimeSessionBinding {
|
|
23
23
|
reporterSessionId: string;
|
|
24
24
|
nodeId: string;
|
|
25
25
|
credentialGeneration: number;
|
|
26
26
|
sessionEpoch: number;
|
|
27
27
|
controllerInstanceId: string;
|
|
28
|
-
controller:
|
|
28
|
+
controller: IRuntimeController;
|
|
29
29
|
runtimeNamespace: string;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/** Pallet-initiated WSS on its configured Cloudly HTTPS origin; never HTTP RPC. */
|
|
33
|
-
export interface
|
|
34
|
-
schemaVersion: 1;
|
|
33
|
+
export interface IRegisterPalletRuntimeSessionRequest {
|
|
35
34
|
nodeId: string;
|
|
36
35
|
credentialGeneration: number;
|
|
37
36
|
/** Sensitive ephemeral input; exclude the complete request from hooks and logs. */
|
|
@@ -41,80 +40,115 @@ export interface IRegisterPalletRuntimeSessionRequestV1 {
|
|
|
41
40
|
* registration. It proves which node process is registering when the transport cannot:
|
|
42
41
|
* one relay socket carries many nodes, so the physical peer no longer distinguishes a
|
|
43
42
|
* node restart from a replay. It is not a secret and it grants no authority; the bearer
|
|
44
|
-
* still authenticates.
|
|
45
|
-
* exclusively from a directly connected node.
|
|
43
|
+
* still authenticates.
|
|
46
44
|
*/
|
|
47
|
-
nodeInstanceNonce
|
|
45
|
+
nodeInstanceNonce: string;
|
|
46
|
+
/**
|
|
47
|
+
* What this node can resolve for a workload, sorted and duplicate-free.
|
|
48
|
+
*
|
|
49
|
+
* It states a capability of the build that is registering, not a permission: the controller
|
|
50
|
+
* refuses an assignment whose `requiredWorkloadAuthorities` this list does not cover, instead
|
|
51
|
+
* of a node accepting work it cannot carry out. It is not part of the registration identity —
|
|
52
|
+
* a changed build is a new process and therefore a new `nodeInstanceNonce` — so a replay of one
|
|
53
|
+
* registration with different authorities is a conflict rather than an update.
|
|
54
|
+
*/
|
|
55
|
+
resolvedAuthorities: TRuntimeWorkloadAuthority[];
|
|
56
|
+
/** Read and negotiated before anything else on this body; see `runtimeSessionContract`. */
|
|
57
|
+
protocol: IProtocolOffer;
|
|
48
58
|
}
|
|
49
59
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
/**
|
|
61
|
+
* What the controller answers an accepted registration with.
|
|
62
|
+
*
|
|
63
|
+
* The offer travels beside the binding rather than inside it: the binding is embedded in every
|
|
64
|
+
* session-scoped request and compared as a whole, so an offer in it would make two builds of one
|
|
65
|
+
* controller produce two bindings for one session.
|
|
66
|
+
*/
|
|
67
|
+
export interface IRegisterPalletRuntimeSessionResponse {
|
|
68
|
+
session: IRuntimeSessionBinding;
|
|
69
|
+
protocol: IProtocolOffer;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface IApplyRuntimeAssignmentRequest {
|
|
73
|
+
session: IRuntimeSessionBinding;
|
|
74
|
+
assignment: IRuntimeAssignment;
|
|
75
|
+
config: IRuntimeConfig;
|
|
54
76
|
/** Exact removed incumbent for a new attempt; not assignment.previous. */
|
|
55
|
-
predecessor:
|
|
77
|
+
predecessor: IRuntimeAssignmentReference | null;
|
|
56
78
|
}
|
|
57
79
|
|
|
58
80
|
/** Durable admission acknowledgement only; it asserts neither effects nor readiness. */
|
|
59
|
-
export interface
|
|
81
|
+
export interface IApplyRuntimeAssignmentResponse {
|
|
60
82
|
status: 'accepted' | 'replay' | 'historical';
|
|
61
|
-
assignment:
|
|
83
|
+
assignment: IRuntimeAssignmentReference;
|
|
62
84
|
}
|
|
63
85
|
|
|
64
|
-
export interface
|
|
86
|
+
export interface IReportRuntimeAssignmentObservationRequest {
|
|
65
87
|
/** Always the CURRENT authenticated connection, including after credential rotation. */
|
|
66
|
-
session:
|
|
88
|
+
session: IRuntimeSessionBinding;
|
|
67
89
|
/** Exact durable outbox body. Never rewrite reporterSessionId, digest or sequence. */
|
|
68
|
-
observation:
|
|
90
|
+
observation: IRuntimeAssignmentObservation;
|
|
69
91
|
}
|
|
70
92
|
|
|
71
|
-
export type
|
|
93
|
+
export type TRuntimeObservationAuthorityUse = 'current-session' | 'historical-recovery';
|
|
72
94
|
|
|
73
95
|
/** Content comparison result only; all supplied authority must come from its trusted owner. */
|
|
74
|
-
export interface
|
|
75
|
-
session:
|
|
76
|
-
assignment:
|
|
77
|
-
reporterHistory:
|
|
78
|
-
authorityUse:
|
|
96
|
+
export interface IRuntimeReporterSessionBinding {
|
|
97
|
+
session: IRuntimeSessionBinding;
|
|
98
|
+
assignment: IRuntimeAssignment;
|
|
99
|
+
reporterHistory: IRuntimeSessionBinding;
|
|
100
|
+
authorityUse: TRuntimeObservationAuthorityUse;
|
|
79
101
|
}
|
|
80
102
|
|
|
81
|
-
export interface
|
|
103
|
+
export interface IReportRuntimeAssignmentObservationResponse {
|
|
82
104
|
/** Exact request body after a durable sequence/receipt transaction. */
|
|
83
|
-
observation:
|
|
105
|
+
observation: IRuntimeAssignmentObservation;
|
|
84
106
|
/**
|
|
85
107
|
* Historical recovery can advance the durable counter and ACK only. It must not
|
|
86
108
|
* update active readiness, routing or liveness; those require a fresh report
|
|
87
109
|
* from the current session. Neither classification is proof of writer fencing.
|
|
88
110
|
*/
|
|
89
|
-
authorityUse:
|
|
111
|
+
authorityUse: TRuntimeObservationAuthorityUse;
|
|
90
112
|
}
|
|
91
113
|
|
|
92
|
-
export interface
|
|
93
|
-
session:
|
|
114
|
+
export interface IGetRuntimeRegistryCredentialRequest {
|
|
115
|
+
session: IRuntimeSessionBinding;
|
|
94
116
|
/** The server must authorize this exact CURRENT assignment, including its node. */
|
|
95
|
-
assignment:
|
|
117
|
+
assignment: IRuntimeAssignmentReference;
|
|
96
118
|
}
|
|
97
119
|
|
|
98
120
|
/** Sensitive ephemeral material; never persist, hash, log or include in diagnostics. */
|
|
99
|
-
export interface
|
|
121
|
+
export interface IGetRuntimeRegistryCredentialResponse {
|
|
100
122
|
username: string;
|
|
101
123
|
password: string;
|
|
102
124
|
expiresAt: number;
|
|
103
125
|
}
|
|
104
126
|
|
|
105
127
|
/** Cloudly human/machine JWT registration on a physical TypedSocket; reject HTTP. */
|
|
106
|
-
export interface
|
|
107
|
-
schemaVersion: 1;
|
|
128
|
+
export interface IRegisterCloudlyClientSessionRequest {
|
|
108
129
|
/** Authenticate the current JWT server-side; do not accept client identity claims. */
|
|
109
130
|
identity: IIdentityCredential;
|
|
131
|
+
/** Read and negotiated before the identity is authenticated; see `runtimeSessionContract`. */
|
|
132
|
+
protocol: IProtocolOffer;
|
|
110
133
|
}
|
|
111
134
|
|
|
112
|
-
export interface
|
|
135
|
+
export interface IRegisterCloudlyClientSessionResponse {
|
|
113
136
|
/** Server-owned identity tagging completed; never trust a client-authored tag. */
|
|
114
137
|
registered: true;
|
|
138
|
+
/** The controller's offer, so an accepted client can judge the session it just opened. */
|
|
139
|
+
protocol: IProtocolOffer;
|
|
115
140
|
}
|
|
116
141
|
|
|
117
|
-
|
|
142
|
+
/**
|
|
143
|
+
* The rules every runtime session registration is judged by.
|
|
144
|
+
*
|
|
145
|
+
* Every carrier here is judged in the one server order `protocol.readProtocolOffer` states,
|
|
146
|
+
* against this session kind's own offer, so the snapshots below see a body whose offer this
|
|
147
|
+
* controller already accepted. For a node that order is also what keeps a refused registration
|
|
148
|
+
* from touching its packet access: the refusal is named before `networkVpn` is asked to mutate
|
|
149
|
+
* anything.
|
|
150
|
+
*/
|
|
151
|
+
export const runtimeSessionContract = Object.freeze({
|
|
118
152
|
maximumWireCanonicalBytes: 327_680,
|
|
119
153
|
maximumCredentialBytes: 16_384,
|
|
120
154
|
maximumRegistryPasswordBytes: 8192,
|
|
@@ -123,12 +157,10 @@ export const runtimeSessionV1Contract = Object.freeze({
|
|
|
123
157
|
nodeInstanceNonceFormat: 'canonical-lowercase-uuid-per-node-process',
|
|
124
158
|
/** The registration a controller must treat as one: a change in any part is a new session. */
|
|
125
159
|
registrationIdentity: 'node-id-and-credential-generation-and-node-instance-nonce',
|
|
126
|
-
/** A relay socket carries many nodes, so its transport cannot prove a node process. */
|
|
127
|
-
relayRegistrationRequiresNodeInstanceNonce: true,
|
|
128
160
|
error: 'Runtime session contract rejected.',
|
|
129
161
|
} as const);
|
|
130
162
|
|
|
131
|
-
const fail = (): never => { throw new Error(
|
|
163
|
+
const fail = (): never => { throw new Error(runtimeSessionContract.error); };
|
|
132
164
|
function requireValue(conditionArg: unknown): asserts conditionArg {
|
|
133
165
|
if (!conditionArg) fail();
|
|
134
166
|
}
|
|
@@ -163,9 +195,9 @@ const capture = (valueArg: unknown): unknown => safe(() => {
|
|
|
163
195
|
let stringBytes = 0;
|
|
164
196
|
const ancestors = new Set<object>();
|
|
165
197
|
const account = (value: string): void => {
|
|
166
|
-
requireValue(value.length <=
|
|
198
|
+
requireValue(value.length <= runtimeSessionContract.maximumWireCanonicalBytes);
|
|
167
199
|
stringBytes += encoder.encode(value).byteLength;
|
|
168
|
-
requireValue(stringBytes <=
|
|
200
|
+
requireValue(stringBytes <= runtimeSessionContract.maximumWireCanonicalBytes);
|
|
169
201
|
};
|
|
170
202
|
const copy = (value: unknown, depth: number): unknown => {
|
|
171
203
|
requireValue(++nodes <= 10_240 && depth <= 16);
|
|
@@ -202,7 +234,7 @@ const capture = (valueArg: unknown): unknown => safe(() => {
|
|
|
202
234
|
} finally { ancestors.delete(value); }
|
|
203
235
|
};
|
|
204
236
|
const serialized = canonical(copy(valueArg, 0));
|
|
205
|
-
requireValue(encoder.encode(serialized).byteLength <=
|
|
237
|
+
requireValue(encoder.encode(serialized).byteLength <= runtimeSessionContract.maximumWireCanonicalBytes);
|
|
206
238
|
return JSON.parse(serialized);
|
|
207
239
|
});
|
|
208
240
|
|
|
@@ -215,139 +247,154 @@ const record = (
|
|
|
215
247
|
&& keys.every((key) => keysArg.includes(key) || optionalKeysArg.includes(key)));
|
|
216
248
|
return valueArg as Record<string, unknown>;
|
|
217
249
|
};
|
|
218
|
-
const reference = (valueArg: unknown):
|
|
250
|
+
const reference = (valueArg: unknown): IRuntimeAssignmentReference => {
|
|
219
251
|
const value = record(valueArg, ['assignmentId', 'generation', 'digest']);
|
|
220
252
|
requireValue(typeof value.digest === 'string' && isSha256Digest(value.digest));
|
|
221
253
|
return { assignmentId: identifier(value.assignmentId), generation: positive(value.generation), digest: value.digest };
|
|
222
254
|
};
|
|
223
|
-
const session = (valueArg: unknown):
|
|
224
|
-
const value = record(valueArg, ['
|
|
255
|
+
const session = (valueArg: unknown): IRuntimeSessionBinding => {
|
|
256
|
+
const value = record(valueArg, ['reporterSessionId', 'nodeId', 'credentialGeneration',
|
|
225
257
|
'sessionEpoch', 'controllerInstanceId', 'controller', 'runtimeNamespace']);
|
|
226
258
|
const controller = record(value.controller, ['kind', 'id', 'epoch']);
|
|
227
|
-
requireValue(
|
|
259
|
+
requireValue(controller.kind === 'cloudly' || controller.kind === 'onebox');
|
|
228
260
|
return {
|
|
229
|
-
|
|
261
|
+
reporterSessionId: identifier(value.reporterSessionId), nodeId: nodeId(value.nodeId),
|
|
230
262
|
credentialGeneration: positive(value.credentialGeneration), sessionEpoch: positive(value.sessionEpoch),
|
|
231
263
|
controllerInstanceId: identifier(value.controllerInstanceId),
|
|
232
264
|
controller: { kind: controller.kind, id: identifier(controller.id), epoch: identifier(controller.epoch) },
|
|
233
265
|
runtimeNamespace: identifier(value.runtimeNamespace),
|
|
234
266
|
};
|
|
235
267
|
};
|
|
236
|
-
const sameSession = (leftArg:
|
|
268
|
+
const sameSession = (leftArg: IRuntimeSessionBinding, rightArg: IRuntimeSessionBinding): boolean =>
|
|
237
269
|
canonical(leftArg) === canonical(rightArg);
|
|
238
|
-
const sameOwner = (leftArg:
|
|
270
|
+
const sameOwner = (leftArg: IRuntimeSessionBinding, rightArg: IRuntimeSessionBinding): boolean =>
|
|
239
271
|
leftArg.nodeId === rightArg.nodeId && leftArg.runtimeNamespace === rightArg.runtimeNamespace
|
|
240
272
|
&& canonical(leftArg.controller) === canonical(rightArg.controller);
|
|
241
|
-
const matchesAssignment = (sessionArg:
|
|
273
|
+
const matchesAssignment = (sessionArg: IRuntimeSessionBinding, assignmentArg: IRuntimeAssignment): boolean =>
|
|
242
274
|
sessionArg.nodeId === assignmentArg.nodeId && sessionArg.runtimeNamespace === assignmentArg.runtimeNamespace
|
|
243
275
|
&& canonical(sessionArg.controller) === canonical(assignmentArg.controller);
|
|
244
276
|
|
|
245
277
|
/** Synchronous detached structural assertion only; never an authentication check. */
|
|
246
|
-
export const
|
|
278
|
+
export const snapshotRuntimeSessionBinding = (valueArg: unknown): IRuntimeSessionBinding =>
|
|
247
279
|
safe(() => session(capture(valueArg)));
|
|
248
280
|
|
|
249
|
-
export const
|
|
281
|
+
export const snapshotRegisterPalletRuntimeSessionRequest = (
|
|
250
282
|
valueArg: unknown,
|
|
251
|
-
):
|
|
252
|
-
const value = record(capture(valueArg),
|
|
253
|
-
['nodeInstanceNonce']);
|
|
254
|
-
requireValue(value.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
283
|
+
): IRegisterPalletRuntimeSessionRequest => safe(() => {
|
|
284
|
+
const value = record(capture(valueArg),
|
|
285
|
+
['nodeId', 'credentialGeneration', 'bearer', 'nodeInstanceNonce', 'resolvedAuthorities', 'protocol']);
|
|
286
|
+
requireValue(typeof value.bearer === 'string' && /^palletToken[0-9a-f]{64}$/.test(value.bearer));
|
|
287
|
+
requireValue(isResolvedWorkloadAuthorities(value.resolvedAuthorities));
|
|
288
|
+
return {
|
|
289
|
+
nodeId: nodeId(value.nodeId), credentialGeneration: positive(value.credentialGeneration),
|
|
290
|
+
bearer: value.bearer, nodeInstanceNonce: nodeInstanceNonce(value.nodeInstanceNonce),
|
|
291
|
+
resolvedAuthorities: [...value.resolvedAuthorities], protocol: readProtocolOffer(value),
|
|
292
|
+
};
|
|
261
293
|
});
|
|
262
294
|
|
|
263
|
-
export const
|
|
295
|
+
export const snapshotRegisterPalletRuntimeSessionResponse = (
|
|
264
296
|
valueArg: unknown,
|
|
265
|
-
):
|
|
297
|
+
): IRegisterPalletRuntimeSessionResponse => safe(() => {
|
|
298
|
+
const value = record(capture(valueArg), ['session', 'protocol']);
|
|
299
|
+
return { session: session(value.session), protocol: readProtocolOffer(value) };
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
export const snapshotApplyRuntimeAssignmentRequest = (
|
|
303
|
+
valueArg: unknown,
|
|
304
|
+
): IApplyRuntimeAssignmentRequest => safe(() => {
|
|
266
305
|
const value = record(capture(valueArg), ['session', 'assignment', 'config', 'predecessor']);
|
|
267
|
-
const assignment =
|
|
306
|
+
const assignment = snapshotRuntimeAssignment(value.assignment);
|
|
268
307
|
const predecessor = value.predecessor === null ? null : reference(value.predecessor);
|
|
269
308
|
// Assignment revisions use assignment.previous; a replacement names a different attempt.
|
|
270
309
|
requireValue(predecessor === null || predecessor.assignmentId !== assignment.assignmentId);
|
|
271
|
-
return { session: session(value.session), assignment, config:
|
|
310
|
+
return { session: session(value.session), assignment, config: snapshotRuntimeConfig(value.config), predecessor };
|
|
272
311
|
});
|
|
273
312
|
|
|
274
|
-
export const
|
|
313
|
+
export const snapshotApplyRuntimeAssignmentResponse = (
|
|
275
314
|
valueArg: unknown,
|
|
276
|
-
):
|
|
315
|
+
): IApplyRuntimeAssignmentResponse => safe(() => {
|
|
277
316
|
const value = record(capture(valueArg), ['status', 'assignment']);
|
|
278
317
|
requireValue(value.status === 'accepted' || value.status === 'replay' || value.status === 'historical');
|
|
279
318
|
return { status: value.status, assignment: reference(value.assignment) };
|
|
280
319
|
});
|
|
281
320
|
|
|
282
|
-
export const
|
|
321
|
+
export const snapshotReportRuntimeAssignmentObservationRequest = (
|
|
283
322
|
valueArg: unknown,
|
|
284
|
-
):
|
|
323
|
+
): IReportRuntimeAssignmentObservationRequest => safe(() => {
|
|
285
324
|
const value = record(capture(valueArg), ['session', 'observation']);
|
|
286
|
-
return { session: session(value.session), observation:
|
|
325
|
+
return { session: session(value.session), observation: snapshotRuntimeAssignmentObservation(value.observation) };
|
|
287
326
|
});
|
|
288
327
|
|
|
289
|
-
export const
|
|
328
|
+
export const snapshotReportRuntimeAssignmentObservationResponse = (
|
|
290
329
|
valueArg: unknown,
|
|
291
|
-
):
|
|
330
|
+
): IReportRuntimeAssignmentObservationResponse => safe(() => {
|
|
292
331
|
const value = record(capture(valueArg), ['observation', 'authorityUse']);
|
|
293
332
|
requireValue(value.authorityUse === 'current-session' || value.authorityUse === 'historical-recovery');
|
|
294
|
-
return { observation:
|
|
333
|
+
return { observation: snapshotRuntimeAssignmentObservation(value.observation), authorityUse: value.authorityUse };
|
|
295
334
|
});
|
|
296
335
|
|
|
297
|
-
export const
|
|
336
|
+
export const snapshotGetRuntimeRegistryCredentialRequest = (
|
|
298
337
|
valueArg: unknown,
|
|
299
|
-
):
|
|
338
|
+
): IGetRuntimeRegistryCredentialRequest => safe(() => {
|
|
300
339
|
const value = record(capture(valueArg), ['session', 'assignment']);
|
|
301
340
|
return { session: session(value.session), assignment: reference(value.assignment) };
|
|
302
341
|
});
|
|
303
342
|
|
|
304
|
-
export const
|
|
343
|
+
export const snapshotGetRuntimeRegistryCredentialResponse = (
|
|
305
344
|
valueArg: unknown,
|
|
306
|
-
):
|
|
345
|
+
): IGetRuntimeRegistryCredentialResponse => safe(() => {
|
|
307
346
|
const value = record(capture(valueArg), ['username', 'password', 'expiresAt']);
|
|
308
347
|
requireValue(typeof value.username === 'string' && value.username.length > 0
|
|
309
348
|
&& encoder.encode(value.username).byteLength <= 1024 && !/[\s:\u0000-\u001f\u007f]/.test(value.username));
|
|
310
349
|
requireValue(typeof value.password === 'string' && value.password.length > 0
|
|
311
|
-
&& encoder.encode(value.password).byteLength <=
|
|
350
|
+
&& encoder.encode(value.password).byteLength <= runtimeSessionContract.maximumRegistryPasswordBytes
|
|
312
351
|
&& !/[\u0000-\u001f\u007f]/.test(value.password));
|
|
313
352
|
return { username: value.username, password: value.password, expiresAt: positive(value.expiresAt) };
|
|
314
353
|
});
|
|
315
354
|
|
|
316
|
-
export const
|
|
355
|
+
export const snapshotRegisterCloudlyClientSessionRequest = (
|
|
317
356
|
valueArg: unknown,
|
|
318
|
-
):
|
|
319
|
-
const value = record(capture(valueArg), ['
|
|
357
|
+
): IRegisterCloudlyClientSessionRequest => safe(() => {
|
|
358
|
+
const value = record(capture(valueArg), ['identity', 'protocol']);
|
|
320
359
|
const identity = record(value.identity, ['jwt']);
|
|
321
|
-
requireValue(
|
|
322
|
-
&& identity.jwt.length <=
|
|
360
|
+
requireValue(typeof identity.jwt === 'string'
|
|
361
|
+
&& identity.jwt.length <= runtimeSessionContract.maximumCredentialBytes
|
|
323
362
|
&& /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(identity.jwt));
|
|
324
|
-
return {
|
|
363
|
+
return { identity: { jwt: identity.jwt }, protocol: readProtocolOffer(value) };
|
|
325
364
|
});
|
|
326
365
|
|
|
327
|
-
export const
|
|
366
|
+
export const snapshotRegisterCloudlyClientSessionResponse = (
|
|
328
367
|
valueArg: unknown,
|
|
329
|
-
):
|
|
330
|
-
const value = record(capture(valueArg), ['registered']);
|
|
368
|
+
): IRegisterCloudlyClientSessionResponse => safe(() => {
|
|
369
|
+
const value = record(capture(valueArg), ['registered', 'protocol']);
|
|
331
370
|
requireValue(value.registered === true);
|
|
332
|
-
return { registered: true };
|
|
371
|
+
return { registered: true, protocol: readProtocolOffer(value) };
|
|
333
372
|
});
|
|
334
373
|
|
|
335
|
-
/**
|
|
336
|
-
|
|
374
|
+
/**
|
|
375
|
+
* Bind the authenticated Cloudly registration reply to the node, credential and offer sent.
|
|
376
|
+
*
|
|
377
|
+
* The client negotiates the answered offer against the one it sent: acceptance by a controller is
|
|
378
|
+
* that controller's judgement, and a node that cannot serve the session it was given must not open
|
|
379
|
+
* it. Both offers were read from the two bodies, so this compares what was actually exchanged.
|
|
380
|
+
*/
|
|
381
|
+
export const bindRegisterPalletRuntimeSessionResponse = (
|
|
337
382
|
valueArg: unknown, requestArg: unknown,
|
|
338
|
-
):
|
|
339
|
-
const response =
|
|
340
|
-
const request =
|
|
341
|
-
requireValue(response.controller.kind === 'cloudly'
|
|
342
|
-
&& response.
|
|
383
|
+
): IRegisterPalletRuntimeSessionResponse => safe(() => {
|
|
384
|
+
const response = snapshotRegisterPalletRuntimeSessionResponse(valueArg);
|
|
385
|
+
const request = snapshotRegisterPalletRuntimeSessionRequest(requestArg);
|
|
386
|
+
requireValue(response.session.controller.kind === 'cloudly'
|
|
387
|
+
&& response.session.nodeId === request.nodeId
|
|
388
|
+
&& response.session.credentialGeneration === request.credentialGeneration
|
|
389
|
+
&& negotiateProtocol(request.protocol, response.protocol).compatible);
|
|
343
390
|
return response;
|
|
344
391
|
});
|
|
345
392
|
|
|
346
393
|
/** Reject already expired material; issuance policy and current assignment authority remain server-owned. */
|
|
347
|
-
export const
|
|
394
|
+
export const bindGetRuntimeRegistryCredentialResponse = (
|
|
348
395
|
valueArg: unknown, nowArg: number,
|
|
349
|
-
):
|
|
350
|
-
const response =
|
|
396
|
+
): IGetRuntimeRegistryCredentialResponse => safe(() => {
|
|
397
|
+
const response = snapshotGetRuntimeRegistryCredentialResponse(valueArg);
|
|
351
398
|
requireValue(response.expiresAt > positive(nowArg));
|
|
352
399
|
return response;
|
|
353
400
|
});
|
|
@@ -357,25 +404,25 @@ export const bindGetRuntimeRegistryCredentialResponseV1 = (
|
|
|
357
404
|
* come from the receiving live connection, never from another request field.
|
|
358
405
|
* This binds content only; durable CAS, slot admission and authority remain owners.
|
|
359
406
|
*/
|
|
360
|
-
export const
|
|
407
|
+
export const bindApplyRuntimeAssignmentRequest = async (
|
|
361
408
|
valueArg: unknown, trustedSessionArg: unknown,
|
|
362
|
-
): Promise<
|
|
409
|
+
): Promise<IApplyRuntimeAssignmentRequest> => {
|
|
363
410
|
try {
|
|
364
|
-
const request =
|
|
365
|
-
const trustedSession =
|
|
411
|
+
const request = snapshotApplyRuntimeAssignmentRequest(valueArg);
|
|
412
|
+
const trustedSession = snapshotRuntimeSessionBinding(trustedSessionArg);
|
|
366
413
|
requireValue(sameSession(request.session, trustedSession) && matchesAssignment(trustedSession, request.assignment));
|
|
367
|
-
await
|
|
414
|
+
await bindRuntimeConfigToAssignment(request.config, request.assignment);
|
|
368
415
|
return request;
|
|
369
416
|
} catch { return fail(); }
|
|
370
417
|
};
|
|
371
418
|
|
|
372
419
|
/** Match an admission acknowledgement to the exact sent revision, including historical replies. */
|
|
373
|
-
export const
|
|
420
|
+
export const bindApplyRuntimeAssignmentResponse = (
|
|
374
421
|
valueArg: unknown, requestArg: unknown,
|
|
375
|
-
):
|
|
376
|
-
const response =
|
|
377
|
-
const request =
|
|
378
|
-
requireValue(canonical(response.assignment) === canonical(
|
|
422
|
+
): IApplyRuntimeAssignmentResponse => safe(() => {
|
|
423
|
+
const response = snapshotApplyRuntimeAssignmentResponse(valueArg);
|
|
424
|
+
const request = snapshotApplyRuntimeAssignmentRequest(requestArg);
|
|
425
|
+
requireValue(canonical(response.assignment) === canonical(getRuntimeAssignmentReference(request.assignment)));
|
|
379
426
|
return response;
|
|
380
427
|
});
|
|
381
428
|
|
|
@@ -386,15 +433,15 @@ export const bindApplyRuntimeAssignmentResponseV1 = (
|
|
|
386
433
|
* The owning transaction must recheck current credential/peer authority. Assignment
|
|
387
434
|
* digest validation and report-specific authority remain the caller's responsibility.
|
|
388
435
|
*/
|
|
389
|
-
export const
|
|
436
|
+
export const bindRuntimeReporterSession = (
|
|
390
437
|
sessionArg: unknown, reporterSessionIdArg: string, assignmentArg: unknown,
|
|
391
438
|
trustedSessionArg: unknown, reporterHistoryArg: unknown,
|
|
392
|
-
):
|
|
393
|
-
const incomingSession =
|
|
439
|
+
): IRuntimeReporterSessionBinding => safe(() => {
|
|
440
|
+
const incomingSession = snapshotRuntimeSessionBinding(sessionArg);
|
|
394
441
|
const reporterSessionId = identifier(reporterSessionIdArg);
|
|
395
|
-
const assignment =
|
|
396
|
-
const trustedSession =
|
|
397
|
-
const reporterHistory =
|
|
442
|
+
const assignment = snapshotRuntimeAssignment(capture(assignmentArg));
|
|
443
|
+
const trustedSession = snapshotRuntimeSessionBinding(trustedSessionArg);
|
|
444
|
+
const reporterHistory = snapshotRuntimeSessionBinding(reporterHistoryArg);
|
|
398
445
|
requireValue(sameSession(incomingSession, trustedSession) && matchesAssignment(trustedSession, assignment)
|
|
399
446
|
&& reporterSessionId === reporterHistory.reporterSessionId && sameOwner(trustedSession, reporterHistory));
|
|
400
447
|
const current = sameSession(reporterHistory, trustedSession);
|
|
@@ -408,32 +455,32 @@ export const bindRuntimeReporterSessionV1 = (
|
|
|
408
455
|
/**
|
|
409
456
|
* trustedSessionArg is the CURRENT authenticated physical peer. reporterHistoryArg
|
|
410
457
|
* must be server-indexed immutable session history, obtained by the original
|
|
411
|
-
* observation's reporterSessionId; never accept caller-supplied history.
|
|
412
|
-
* runtime
|
|
458
|
+
* observation's reporterSessionId; never accept caller-supplied history. Every
|
|
459
|
+
* runtime session is a Pallet-purpose session, so Spark history cannot qualify.
|
|
413
460
|
*
|
|
414
461
|
* This verifies content and classifies recovery, not authentication, history
|
|
415
462
|
* provenance, sequence admission or current authority. The server must transact
|
|
416
463
|
* receipt/counter acceptance under current credential and peer authority. A
|
|
417
464
|
* historical result MUST NOT restore active readiness, routes or liveness.
|
|
418
465
|
*/
|
|
419
|
-
export const
|
|
466
|
+
export const bindReportRuntimeAssignmentObservationRequest = async (
|
|
420
467
|
valueArg: unknown, assignmentArg: unknown, trustedSessionArg: unknown, reporterHistoryArg: unknown,
|
|
421
|
-
): Promise<{ request:
|
|
468
|
+
): Promise<{ request: IReportRuntimeAssignmentObservationRequest; authorityUse: TRuntimeObservationAuthorityUse }> => {
|
|
422
469
|
try {
|
|
423
|
-
const request =
|
|
424
|
-
const binding =
|
|
470
|
+
const request = snapshotReportRuntimeAssignmentObservationRequest(valueArg);
|
|
471
|
+
const binding = bindRuntimeReporterSession(request.session, request.observation.reporterSessionId,
|
|
425
472
|
assignmentArg, trustedSessionArg, reporterHistoryArg);
|
|
426
|
-
requireValue((await
|
|
473
|
+
requireValue((await validateRuntimeAssignmentObservation(request.observation, binding.assignment)).length === 0);
|
|
427
474
|
return { request, authorityUse: binding.authorityUse };
|
|
428
475
|
} catch { return fail(); }
|
|
429
476
|
};
|
|
430
477
|
|
|
431
478
|
/** Reject body rewriting and a historical receipt mislabeled as current authority. */
|
|
432
|
-
export const
|
|
479
|
+
export const bindReportRuntimeAssignmentObservationResponse = (
|
|
433
480
|
valueArg: unknown, requestArg: unknown,
|
|
434
|
-
):
|
|
435
|
-
const response =
|
|
436
|
-
const request =
|
|
481
|
+
): IReportRuntimeAssignmentObservationResponse => safe(() => {
|
|
482
|
+
const response = snapshotReportRuntimeAssignmentObservationResponse(valueArg);
|
|
483
|
+
const request = snapshotReportRuntimeAssignmentObservationRequest(requestArg);
|
|
437
484
|
requireValue(canonical(response.observation) === canonical(request.observation)
|
|
438
485
|
&& response.authorityUse === (request.observation.reporterSessionId === request.session.reporterSessionId
|
|
439
486
|
? 'current-session' : 'historical-recovery'));
|
|
@@ -441,17 +488,17 @@ export const bindReportRuntimeAssignmentObservationResponseV1 = (
|
|
|
441
488
|
});
|
|
442
489
|
|
|
443
490
|
/** Current assignment lookup and live peer authorization remain mandatory server-owned inputs. */
|
|
444
|
-
export const
|
|
491
|
+
export const bindGetRuntimeRegistryCredentialRequest = async (
|
|
445
492
|
valueArg: unknown, assignmentArg: unknown, trustedSessionArg: unknown,
|
|
446
|
-
): Promise<
|
|
493
|
+
): Promise<IGetRuntimeRegistryCredentialRequest> => {
|
|
447
494
|
try {
|
|
448
|
-
const request =
|
|
449
|
-
const assignment =
|
|
450
|
-
const trustedSession =
|
|
495
|
+
const request = snapshotGetRuntimeRegistryCredentialRequest(valueArg);
|
|
496
|
+
const assignment = snapshotRuntimeAssignment(capture(assignmentArg));
|
|
497
|
+
const trustedSession = snapshotRuntimeSessionBinding(trustedSessionArg);
|
|
451
498
|
requireValue(sameSession(request.session, trustedSession) && matchesAssignment(trustedSession, assignment)
|
|
452
499
|
&& assignment.disposition === 'run'
|
|
453
|
-
&& canonical(request.assignment) === canonical(
|
|
454
|
-
requireValue((await
|
|
500
|
+
&& canonical(request.assignment) === canonical(getRuntimeAssignmentReference(assignment)));
|
|
501
|
+
requireValue((await validateRuntimeAssignment(assignment)).length === 0);
|
|
455
502
|
return request;
|
|
456
503
|
} catch { return fail(); }
|
|
457
504
|
};
|