@claudian-collab/protocol 3.2.0 → 3.2.1
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/README.md +1 -1
- package/dist/esm/CollabAuthorityTransfer.mjs +884 -0
- package/dist/esm/CollabCloudBinding.mjs +552 -0
- package/dist/esm/CollabCloudProjectEvent.mjs +150 -0
- package/dist/esm/CollabCloudProjectSnapshot.mjs +277 -0
- package/dist/esm/CollabConstants.mjs +31 -0
- package/dist/esm/CollabControlOperationCodecs.mjs +104 -0
- package/dist/esm/CollabError.mjs +175 -0
- package/dist/esm/CollabMarkdownProse.mjs +54 -0
- package/dist/esm/CollabMemberMentionParser.mjs +48 -0
- package/dist/esm/CollabProjectBackupCheckpoint.mjs +1473 -0
- package/dist/esm/CollabProjectCheckpoint.mjs +1326 -0
- package/dist/esm/CollabProjectRetirement.mjs +119 -0
- package/dist/esm/CollabProtocol.mjs +64 -0
- package/dist/esm/CollabRequestTicketRequestCodecs.mjs +263 -0
- package/dist/esm/CollabRequestTicketResponseCodecs.mjs +353 -0
- package/dist/esm/CollabTicketReferenceParser.mjs +52 -0
- package/dist/esm/CollabValidation.mjs +19 -0
- package/dist/esm/DevelopmentBootstrap.mjs +537 -0
- package/dist/esm/index.mjs +16 -0
- package/dist/esm/types.mjs +8 -0
- package/package.json +5 -2
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
import { CollabError } from './CollabError.mjs';
|
|
2
|
+
import { hasUtf8ByteLengthAtMost, isCollabMemberId, isCollabOpaqueId, isCollabProjectId, } from './CollabValidation.mjs';
|
|
3
|
+
export const COLLAB_LAN_TO_CLOUD_TRANSFER_PHASES = Object.freeze([
|
|
4
|
+
'collecting-readiness',
|
|
5
|
+
'source-quiesced',
|
|
6
|
+
'checkpoint-received',
|
|
7
|
+
'checkpoint-validated',
|
|
8
|
+
'claims-retained',
|
|
9
|
+
'repository-published',
|
|
10
|
+
'source-relinquished',
|
|
11
|
+
'cloud-activated',
|
|
12
|
+
'completed',
|
|
13
|
+
]);
|
|
14
|
+
export const COLLAB_CLOUD_TO_LAN_TRANSFER_PHASES = Object.freeze([
|
|
15
|
+
'collecting-readiness',
|
|
16
|
+
'cloud-quiesced',
|
|
17
|
+
'checkpoint-captured',
|
|
18
|
+
'target-staged',
|
|
19
|
+
'claims-retained',
|
|
20
|
+
'cloud-relinquished',
|
|
21
|
+
'lan-activated',
|
|
22
|
+
'completed',
|
|
23
|
+
]);
|
|
24
|
+
export const COLLAB_AUTHORITY_TRANSFER_CANCELLATION_PHASES = Object.freeze([
|
|
25
|
+
'cancel-intent',
|
|
26
|
+
'target-invalidated',
|
|
27
|
+
'target-cleaned',
|
|
28
|
+
'source-reopened',
|
|
29
|
+
'cancelled',
|
|
30
|
+
]);
|
|
31
|
+
export const COLLAB_AUTHORITY_TRANSFER_CANCELLABLE_PHASES = Object.freeze([
|
|
32
|
+
'collecting-readiness',
|
|
33
|
+
'source-quiesced',
|
|
34
|
+
'checkpoint-received',
|
|
35
|
+
'checkpoint-validated',
|
|
36
|
+
'claims-retained',
|
|
37
|
+
'repository-published',
|
|
38
|
+
'cloud-quiesced',
|
|
39
|
+
'checkpoint-captured',
|
|
40
|
+
'target-staged',
|
|
41
|
+
'cancel-intent',
|
|
42
|
+
'target-invalidated',
|
|
43
|
+
'target-cleaned',
|
|
44
|
+
'source-reopened',
|
|
45
|
+
]);
|
|
46
|
+
export const COLLAB_AUTHORITY_TRANSFER_OPERATIONS = Object.freeze([
|
|
47
|
+
'requestLanToCloudTransfer',
|
|
48
|
+
'acceptLanToCloudTransferTarget',
|
|
49
|
+
'beginLanToCloudTransfer',
|
|
50
|
+
'getProjectAuthorityTransfer',
|
|
51
|
+
'getAuthorityTransferReceiptVerifier',
|
|
52
|
+
'rotateTransferredMembershipClaims',
|
|
53
|
+
'acknowledgeTransferredMembershipClaimBatch',
|
|
54
|
+
'getTransferredMembershipClaim',
|
|
55
|
+
'claimTransferredMembership',
|
|
56
|
+
'acknowledgeTransferredMembershipClaimRedemption',
|
|
57
|
+
'commitLanToCloudRelinquishment',
|
|
58
|
+
'beginCloudToLanTransfer',
|
|
59
|
+
'acceptCloudToLanTransferTarget',
|
|
60
|
+
'reportCloudToLanTargetStaged',
|
|
61
|
+
'confirmCloudToLanTargetActive',
|
|
62
|
+
'cancelProjectAuthorityTransfer',
|
|
63
|
+
]);
|
|
64
|
+
const SHA256_PATTERN = /^[0-9a-f]{64}$/u;
|
|
65
|
+
const BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/u;
|
|
66
|
+
const BASE64URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
67
|
+
function invalidPayload(field) {
|
|
68
|
+
return new CollabError({ code: 'protocol-payload-invalid', safeContext: { field } });
|
|
69
|
+
}
|
|
70
|
+
function record(value, field) {
|
|
71
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
72
|
+
throw invalidPayload(field);
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
function exactRecord(value, field, keys) {
|
|
77
|
+
const source = record(value, field);
|
|
78
|
+
const expected = new Set(keys);
|
|
79
|
+
if (!keys.every(key => Object.hasOwn(source, key))
|
|
80
|
+
|| Object.keys(source).some(key => !expected.has(key)))
|
|
81
|
+
throw invalidPayload(field);
|
|
82
|
+
return source;
|
|
83
|
+
}
|
|
84
|
+
function token(source, field, validate = isCollabOpaqueId) {
|
|
85
|
+
const value = source[field];
|
|
86
|
+
if (typeof value !== 'string' || !validate(value))
|
|
87
|
+
throw invalidPayload(field);
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
function boundedString(source, field, maximumBytes) {
|
|
91
|
+
const value = source[field];
|
|
92
|
+
if (typeof value !== 'string'
|
|
93
|
+
|| value.length === 0
|
|
94
|
+
|| !hasUtf8ByteLengthAtMost(value, maximumBytes))
|
|
95
|
+
throw invalidPayload(field);
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
function positiveInteger(source, field) {
|
|
99
|
+
const value = source[field];
|
|
100
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 1) {
|
|
101
|
+
throw invalidPayload(field);
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
function timestamp(source, field) {
|
|
106
|
+
const value = source[field];
|
|
107
|
+
if (typeof value !== 'string'
|
|
108
|
+
|| value.length > 64
|
|
109
|
+
|| Number.isNaN(Date.parse(value))
|
|
110
|
+
|| new Date(value).toISOString() !== value)
|
|
111
|
+
throw invalidPayload(field);
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
function sha256(source, field) {
|
|
115
|
+
const value = source[field];
|
|
116
|
+
if (typeof value !== 'string' || !SHA256_PATTERN.test(value))
|
|
117
|
+
throw invalidPayload(field);
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
function base64url(source, field, maximumBytes = 4096) {
|
|
121
|
+
const value = boundedString(source, field, maximumBytes);
|
|
122
|
+
if (!BASE64URL_PATTERN.test(value))
|
|
123
|
+
throw invalidPayload(field);
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
function fixedBase64url(source, field, decodedBytes) {
|
|
127
|
+
const encodedLength = Math.ceil(decodedBytes * 4 / 3);
|
|
128
|
+
const value = base64url(source, field, encodedLength);
|
|
129
|
+
const finalIndex = BASE64URL_ALPHABET.indexOf(value[value.length - 1]);
|
|
130
|
+
const remainder = value.length % 4;
|
|
131
|
+
if (value.length !== encodedLength
|
|
132
|
+
|| remainder === 1
|
|
133
|
+
|| (remainder === 2 && (finalIndex & 15) !== 0)
|
|
134
|
+
|| (remainder === 3 && (finalIndex & 3) !== 0))
|
|
135
|
+
throw invalidPayload(field);
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
function literal(source, field, values) {
|
|
139
|
+
const value = source[field];
|
|
140
|
+
if (typeof value !== 'string' || !values.includes(value))
|
|
141
|
+
throw invalidPayload(field);
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
function authority(value, field) {
|
|
145
|
+
const source = exactRecord(value, field, ['generation', 'kind']);
|
|
146
|
+
return {
|
|
147
|
+
generation: positiveInteger(source, 'generation'),
|
|
148
|
+
kind: literal(source, 'kind', ['cloud', 'lan']),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function absoluteTargetUrl(source) {
|
|
152
|
+
const targetUrl = boundedString(source, 'targetUrl', 2048);
|
|
153
|
+
let parsed;
|
|
154
|
+
try {
|
|
155
|
+
parsed = new URL(targetUrl);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
throw invalidPayload('targetUrl');
|
|
159
|
+
}
|
|
160
|
+
if ((parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
|
|
161
|
+
|| parsed.username.length > 0
|
|
162
|
+
|| parsed.password.length > 0
|
|
163
|
+
|| targetUrl.includes('?')
|
|
164
|
+
|| targetUrl.includes('#'))
|
|
165
|
+
throw invalidPayload('targetUrl');
|
|
166
|
+
return targetUrl;
|
|
167
|
+
}
|
|
168
|
+
export function decodeCollabAuthorityTransferProposal(value) {
|
|
169
|
+
const source = exactRecord(value, 'proposal', [
|
|
170
|
+
'expectedSourceAuthority',
|
|
171
|
+
'idempotencyKey',
|
|
172
|
+
'projectId',
|
|
173
|
+
'proposedByMemberId',
|
|
174
|
+
'proposedAt',
|
|
175
|
+
'targetAuthorityKind',
|
|
176
|
+
'targetUrl',
|
|
177
|
+
]);
|
|
178
|
+
const expectedSourceAuthority = authority(source.expectedSourceAuthority, 'expectedSourceAuthority');
|
|
179
|
+
const targetAuthorityKind = literal(source, 'targetAuthorityKind', ['cloud', 'lan']);
|
|
180
|
+
if (expectedSourceAuthority.kind === targetAuthorityKind) {
|
|
181
|
+
throw invalidPayload('targetAuthorityKind');
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
expectedSourceAuthority,
|
|
185
|
+
idempotencyKey: token(source, 'idempotencyKey'),
|
|
186
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
187
|
+
proposedByMemberId: token(source, 'proposedByMemberId', isCollabMemberId),
|
|
188
|
+
proposedAt: timestamp(source, 'proposedAt'),
|
|
189
|
+
targetAuthorityKind,
|
|
190
|
+
targetUrl: absoluteTargetUrl(source),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function claimItem(value) {
|
|
194
|
+
const source = exactRecord(value, 'claim', ['claim', 'memberId']);
|
|
195
|
+
return {
|
|
196
|
+
claim: base64url(source, 'claim'),
|
|
197
|
+
memberId: token(source, 'memberId', isCollabMemberId),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
export function decodeCollabTransferredMembershipClaimBatch(value) {
|
|
201
|
+
const source = exactRecord(value, 'claimBatch', [
|
|
202
|
+
'batchRevision',
|
|
203
|
+
'batchSha256',
|
|
204
|
+
'checkpointSha256',
|
|
205
|
+
'claims',
|
|
206
|
+
'expiresAt',
|
|
207
|
+
'projectId',
|
|
208
|
+
'targetAuthorityGeneration',
|
|
209
|
+
'transferId',
|
|
210
|
+
]);
|
|
211
|
+
if (!Array.isArray(source.claims)) {
|
|
212
|
+
throw invalidPayload('claims');
|
|
213
|
+
}
|
|
214
|
+
const claims = source.claims.map(claimItem);
|
|
215
|
+
if (claims.some((item, index) => index > 0
|
|
216
|
+
&& claims[index - 1].memberId.localeCompare(item.memberId, 'en-US') >= 0)
|
|
217
|
+
|| new Set(claims.map(item => item.claim)).size !== claims.length)
|
|
218
|
+
throw invalidPayload('claims');
|
|
219
|
+
return {
|
|
220
|
+
batchRevision: positiveInteger(source, 'batchRevision'),
|
|
221
|
+
batchSha256: sha256(source, 'batchSha256'),
|
|
222
|
+
checkpointSha256: sha256(source, 'checkpointSha256'),
|
|
223
|
+
claims: Object.freeze(claims),
|
|
224
|
+
expiresAt: timestamp(source, 'expiresAt'),
|
|
225
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
226
|
+
targetAuthorityGeneration: positiveInteger(source, 'targetAuthorityGeneration'),
|
|
227
|
+
transferId: token(source, 'transferId'),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
export function encodeCollabTransferredMembershipClaimBatchDigestInput(batch) {
|
|
231
|
+
const decoded = decodeCollabTransferredMembershipClaimBatch(batch);
|
|
232
|
+
return JSON.stringify({
|
|
233
|
+
batchRevision: decoded.batchRevision,
|
|
234
|
+
checkpointSha256: decoded.checkpointSha256,
|
|
235
|
+
claims: decoded.claims,
|
|
236
|
+
expiresAt: decoded.expiresAt,
|
|
237
|
+
projectId: decoded.projectId,
|
|
238
|
+
targetAuthorityGeneration: decoded.targetAuthorityGeneration,
|
|
239
|
+
transferId: decoded.transferId,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
export function decodeCollabTransferredMembershipClaimCustodyReceipt(value) {
|
|
243
|
+
const source = exactRecord(value, 'custodyReceipt', [
|
|
244
|
+
'batchRevision',
|
|
245
|
+
'batchSha256',
|
|
246
|
+
'checkpointSha256',
|
|
247
|
+
'committedAt',
|
|
248
|
+
'custodyAuthority',
|
|
249
|
+
'operationIntentId',
|
|
250
|
+
'projectId',
|
|
251
|
+
'receiptId',
|
|
252
|
+
'submittedByMemberId',
|
|
253
|
+
'targetAuthorityGeneration',
|
|
254
|
+
'transferId',
|
|
255
|
+
]);
|
|
256
|
+
const custodyAuthority = authority(source.custodyAuthority, 'custodyAuthority');
|
|
257
|
+
const targetAuthorityGeneration = positiveInteger(source, 'targetAuthorityGeneration');
|
|
258
|
+
if (targetAuthorityGeneration !== custodyAuthority.generation + 1) {
|
|
259
|
+
throw invalidPayload('targetAuthorityGeneration');
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
batchRevision: positiveInteger(source, 'batchRevision'),
|
|
263
|
+
batchSha256: sha256(source, 'batchSha256'),
|
|
264
|
+
checkpointSha256: sha256(source, 'checkpointSha256'),
|
|
265
|
+
committedAt: timestamp(source, 'committedAt'),
|
|
266
|
+
custodyAuthority,
|
|
267
|
+
operationIntentId: token(source, 'operationIntentId'),
|
|
268
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
269
|
+
receiptId: token(source, 'receiptId'),
|
|
270
|
+
submittedByMemberId: token(source, 'submittedByMemberId', isCollabMemberId),
|
|
271
|
+
targetAuthorityGeneration,
|
|
272
|
+
transferId: token(source, 'transferId'),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
export function decodeCollabTransferredMembershipClaim(value) {
|
|
276
|
+
const source = exactRecord(value, 'claim', [
|
|
277
|
+
'claim',
|
|
278
|
+
'expiresAt',
|
|
279
|
+
'memberId',
|
|
280
|
+
'projectId',
|
|
281
|
+
'targetAuthorityGeneration',
|
|
282
|
+
'transferId',
|
|
283
|
+
]);
|
|
284
|
+
return {
|
|
285
|
+
claim: base64url(source, 'claim'),
|
|
286
|
+
expiresAt: timestamp(source, 'expiresAt'),
|
|
287
|
+
memberId: token(source, 'memberId', isCollabMemberId),
|
|
288
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
289
|
+
targetAuthorityGeneration: positiveInteger(source, 'targetAuthorityGeneration'),
|
|
290
|
+
transferId: token(source, 'transferId'),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const REDEMPTION_RECEIPT_SIGNING_PAYLOAD_KEYS = [
|
|
294
|
+
'checkpointSha256',
|
|
295
|
+
'claimSha256',
|
|
296
|
+
'memberId',
|
|
297
|
+
'operationIntentId',
|
|
298
|
+
'projectId',
|
|
299
|
+
'receiptId',
|
|
300
|
+
'receiptKeyId',
|
|
301
|
+
'redeemedAt',
|
|
302
|
+
'signatureAlgorithm',
|
|
303
|
+
'targetAuthorityGeneration',
|
|
304
|
+
'transferId',
|
|
305
|
+
];
|
|
306
|
+
function redemptionReceiptSigningPayload(source) {
|
|
307
|
+
return {
|
|
308
|
+
checkpointSha256: sha256(source, 'checkpointSha256'),
|
|
309
|
+
claimSha256: sha256(source, 'claimSha256'),
|
|
310
|
+
memberId: token(source, 'memberId', isCollabMemberId),
|
|
311
|
+
operationIntentId: token(source, 'operationIntentId'),
|
|
312
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
313
|
+
receiptId: token(source, 'receiptId'),
|
|
314
|
+
receiptKeyId: boundedString(source, 'receiptKeyId', 256),
|
|
315
|
+
redeemedAt: timestamp(source, 'redeemedAt'),
|
|
316
|
+
signatureAlgorithm: literal(source, 'signatureAlgorithm', ['ed25519']),
|
|
317
|
+
targetAuthorityGeneration: positiveInteger(source, 'targetAuthorityGeneration'),
|
|
318
|
+
transferId: token(source, 'transferId'),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
export function encodeCollabTransferredMembershipRedemptionReceiptSigningInput(payload) {
|
|
322
|
+
const source = exactRecord(payload, 'redemptionReceiptSigningPayload', REDEMPTION_RECEIPT_SIGNING_PAYLOAD_KEYS);
|
|
323
|
+
return JSON.stringify({
|
|
324
|
+
domain: 'claudian-collab.transferred-membership-redemption-receipt.v1',
|
|
325
|
+
payload: redemptionReceiptSigningPayload(source),
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
export function decodeCollabTransferredMembershipRedemptionReceipt(value) {
|
|
329
|
+
const source = exactRecord(value, 'redemptionReceipt', [
|
|
330
|
+
...REDEMPTION_RECEIPT_SIGNING_PAYLOAD_KEYS,
|
|
331
|
+
'signature',
|
|
332
|
+
]);
|
|
333
|
+
const payload = redemptionReceiptSigningPayload(source);
|
|
334
|
+
return {
|
|
335
|
+
checkpointSha256: payload.checkpointSha256,
|
|
336
|
+
claimSha256: payload.claimSha256,
|
|
337
|
+
memberId: payload.memberId,
|
|
338
|
+
operationIntentId: payload.operationIntentId,
|
|
339
|
+
projectId: payload.projectId,
|
|
340
|
+
receiptId: payload.receiptId,
|
|
341
|
+
receiptKeyId: payload.receiptKeyId,
|
|
342
|
+
redeemedAt: payload.redeemedAt,
|
|
343
|
+
signature: fixedBase64url(source, 'signature', 64),
|
|
344
|
+
signatureAlgorithm: payload.signatureAlgorithm,
|
|
345
|
+
targetAuthorityGeneration: payload.targetAuthorityGeneration,
|
|
346
|
+
transferId: payload.transferId,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
const RELINQUISHMENT_PROOF_SIGNING_PAYLOAD_KEYS = [
|
|
350
|
+
'batchRevision',
|
|
351
|
+
'batchSha256',
|
|
352
|
+
'certificateAlgorithm',
|
|
353
|
+
'checkpointSha256',
|
|
354
|
+
'committedAt',
|
|
355
|
+
'operationIntentId',
|
|
356
|
+
'projectId',
|
|
357
|
+
'sourceAuthority',
|
|
358
|
+
'sourceHostMemberId',
|
|
359
|
+
'targetAuthority',
|
|
360
|
+
'transferId',
|
|
361
|
+
];
|
|
362
|
+
function authorityRelinquishmentProofSigningPayload(source) {
|
|
363
|
+
const sourceAuthority = authority(source.sourceAuthority, 'sourceAuthority');
|
|
364
|
+
const targetAuthority = authority(source.targetAuthority, 'targetAuthority');
|
|
365
|
+
const sourceHostMemberId = source.sourceHostMemberId === null
|
|
366
|
+
? null
|
|
367
|
+
: token(source, 'sourceHostMemberId', isCollabMemberId);
|
|
368
|
+
if (sourceAuthority.kind === targetAuthority.kind
|
|
369
|
+
|| targetAuthority.generation !== sourceAuthority.generation + 1
|
|
370
|
+
|| (sourceAuthority.kind === 'lan') !== (sourceHostMemberId !== null))
|
|
371
|
+
throw invalidPayload('targetAuthority');
|
|
372
|
+
return {
|
|
373
|
+
batchRevision: positiveInteger(source, 'batchRevision'),
|
|
374
|
+
batchSha256: sha256(source, 'batchSha256'),
|
|
375
|
+
certificateAlgorithm: literal(source, 'certificateAlgorithm', ['ed25519']),
|
|
376
|
+
checkpointSha256: sha256(source, 'checkpointSha256'),
|
|
377
|
+
committedAt: timestamp(source, 'committedAt'),
|
|
378
|
+
operationIntentId: token(source, 'operationIntentId'),
|
|
379
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
380
|
+
sourceAuthority,
|
|
381
|
+
sourceHostMemberId,
|
|
382
|
+
targetAuthority,
|
|
383
|
+
transferId: token(source, 'transferId'),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
export function encodeCollabAuthorityRelinquishmentProofSigningInput(payload) {
|
|
387
|
+
const source = exactRecord(payload, 'relinquishmentProofSigningPayload', RELINQUISHMENT_PROOF_SIGNING_PAYLOAD_KEYS);
|
|
388
|
+
return JSON.stringify({
|
|
389
|
+
domain: 'claudian-collab.authority-relinquishment-proof.v1',
|
|
390
|
+
payload: authorityRelinquishmentProofSigningPayload(source),
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
export function decodeCollabAuthorityRelinquishmentProof(value) {
|
|
394
|
+
const source = exactRecord(value, 'relinquishmentProof', [
|
|
395
|
+
...RELINQUISHMENT_PROOF_SIGNING_PAYLOAD_KEYS,
|
|
396
|
+
'certificate',
|
|
397
|
+
]);
|
|
398
|
+
const payload = authorityRelinquishmentProofSigningPayload(source);
|
|
399
|
+
return {
|
|
400
|
+
batchRevision: payload.batchRevision,
|
|
401
|
+
batchSha256: payload.batchSha256,
|
|
402
|
+
certificate: fixedBase64url(source, 'certificate', 64),
|
|
403
|
+
certificateAlgorithm: payload.certificateAlgorithm,
|
|
404
|
+
checkpointSha256: payload.checkpointSha256,
|
|
405
|
+
committedAt: payload.committedAt,
|
|
406
|
+
operationIntentId: payload.operationIntentId,
|
|
407
|
+
projectId: payload.projectId,
|
|
408
|
+
sourceAuthority: payload.sourceAuthority,
|
|
409
|
+
sourceHostMemberId: payload.sourceHostMemberId,
|
|
410
|
+
targetAuthority: payload.targetAuthority,
|
|
411
|
+
transferId: payload.transferId,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const LAN_TO_CLOUD_PHASE_SET = new Set(COLLAB_LAN_TO_CLOUD_TRANSFER_PHASES);
|
|
415
|
+
const CLOUD_TO_LAN_PHASE_SET = new Set(COLLAB_CLOUD_TO_LAN_TRANSFER_PHASES);
|
|
416
|
+
const CANCELLATION_PHASE_SET = new Set(COLLAB_AUTHORITY_TRANSFER_CANCELLATION_PHASES);
|
|
417
|
+
const CANCELLABLE_PHASE_SET = new Set(COLLAB_AUTHORITY_TRANSFER_CANCELLABLE_PHASES);
|
|
418
|
+
const LAN_TO_CLOUD_CHECKPOINT_REQUIRED_PHASE_SET = new Set(COLLAB_LAN_TO_CLOUD_TRANSFER_PHASES.slice(2));
|
|
419
|
+
const CLOUD_TO_LAN_CHECKPOINT_REQUIRED_PHASE_SET = new Set(COLLAB_CLOUD_TO_LAN_TRANSFER_PHASES.slice(2));
|
|
420
|
+
const LAN_TO_CLOUD_BATCH_REQUIRED_PHASE_SET = new Set(COLLAB_LAN_TO_CLOUD_TRANSFER_PHASES.slice(4));
|
|
421
|
+
const CLOUD_TO_LAN_BATCH_REQUIRED_PHASE_SET = new Set(COLLAB_CLOUD_TO_LAN_TRANSFER_PHASES.slice(4));
|
|
422
|
+
const LAN_TO_CLOUD_RELINQUISHMENT_REQUIRED_PHASE_SET = new Set(COLLAB_LAN_TO_CLOUD_TRANSFER_PHASES.slice(6));
|
|
423
|
+
const CLOUD_TO_LAN_RELINQUISHMENT_REQUIRED_PHASE_SET = new Set(COLLAB_CLOUD_TO_LAN_TRANSFER_PHASES.slice(5));
|
|
424
|
+
function transferPhase(source, direction) {
|
|
425
|
+
const value = source.phase;
|
|
426
|
+
const directionSet = direction === 'lan-to-cloud'
|
|
427
|
+
? LAN_TO_CLOUD_PHASE_SET
|
|
428
|
+
: CLOUD_TO_LAN_PHASE_SET;
|
|
429
|
+
if (typeof value !== 'string' || (!directionSet.has(value) && !CANCELLATION_PHASE_SET.has(value))) {
|
|
430
|
+
throw invalidPayload('phase');
|
|
431
|
+
}
|
|
432
|
+
return value;
|
|
433
|
+
}
|
|
434
|
+
export function decodeCollabAuthorityTransferLifecycleFence(value) {
|
|
435
|
+
const source = exactRecord(value, 'transferLifecycleFence', [
|
|
436
|
+
'batchRevision',
|
|
437
|
+
'batchSha256',
|
|
438
|
+
'checkpointSha256',
|
|
439
|
+
'direction',
|
|
440
|
+
'phase',
|
|
441
|
+
]);
|
|
442
|
+
const direction = literal(source, 'direction', ['cloud-to-lan', 'lan-to-cloud']);
|
|
443
|
+
const phase = transferPhase(source, direction);
|
|
444
|
+
const checkpointSha256 = source.checkpointSha256 === null
|
|
445
|
+
? null
|
|
446
|
+
: sha256(source, 'checkpointSha256');
|
|
447
|
+
const batchRevision = source.batchRevision === null
|
|
448
|
+
? null
|
|
449
|
+
: positiveInteger(source, 'batchRevision');
|
|
450
|
+
const batchSha256 = source.batchSha256 === null
|
|
451
|
+
? null
|
|
452
|
+
: sha256(source, 'batchSha256');
|
|
453
|
+
if ((batchRevision === null) !== (batchSha256 === null)
|
|
454
|
+
|| (batchRevision !== null && checkpointSha256 === null)
|
|
455
|
+
|| ((direction === 'lan-to-cloud'
|
|
456
|
+
? LAN_TO_CLOUD_CHECKPOINT_REQUIRED_PHASE_SET
|
|
457
|
+
: CLOUD_TO_LAN_CHECKPOINT_REQUIRED_PHASE_SET).has(phase)
|
|
458
|
+
&& checkpointSha256 === null)
|
|
459
|
+
|| ((direction === 'lan-to-cloud'
|
|
460
|
+
? LAN_TO_CLOUD_BATCH_REQUIRED_PHASE_SET
|
|
461
|
+
: CLOUD_TO_LAN_BATCH_REQUIRED_PHASE_SET).has(phase)
|
|
462
|
+
&& batchRevision === null))
|
|
463
|
+
throw invalidPayload('claimBatch');
|
|
464
|
+
const relinquishmentRequired = (direction === 'lan-to-cloud'
|
|
465
|
+
? LAN_TO_CLOUD_RELINQUISHMENT_REQUIRED_PHASE_SET
|
|
466
|
+
: CLOUD_TO_LAN_RELINQUISHMENT_REQUIRED_PHASE_SET).has(phase);
|
|
467
|
+
return {
|
|
468
|
+
batchRevision,
|
|
469
|
+
batchSha256,
|
|
470
|
+
checkpointSha256,
|
|
471
|
+
direction,
|
|
472
|
+
phase,
|
|
473
|
+
relinquishmentRequired,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
export function decodeCollabAuthorityTransferStatus(value) {
|
|
477
|
+
const source = exactRecord(value, 'transferStatus', [
|
|
478
|
+
'batchRevision',
|
|
479
|
+
'batchSha256',
|
|
480
|
+
'checkpointSha256',
|
|
481
|
+
'createdAt',
|
|
482
|
+
'direction',
|
|
483
|
+
'expiresAt',
|
|
484
|
+
'phase',
|
|
485
|
+
'projectId',
|
|
486
|
+
'relinquishmentProof',
|
|
487
|
+
'sourceAuthority',
|
|
488
|
+
'state',
|
|
489
|
+
'targetAuthority',
|
|
490
|
+
'targetUrl',
|
|
491
|
+
'transferId',
|
|
492
|
+
'updatedAt',
|
|
493
|
+
]);
|
|
494
|
+
const lifecycleFence = decodeCollabAuthorityTransferLifecycleFence({
|
|
495
|
+
batchRevision: source.batchRevision,
|
|
496
|
+
batchSha256: source.batchSha256,
|
|
497
|
+
checkpointSha256: source.checkpointSha256,
|
|
498
|
+
direction: source.direction,
|
|
499
|
+
phase: source.phase,
|
|
500
|
+
});
|
|
501
|
+
const { batchRevision, batchSha256, checkpointSha256, direction, phase, relinquishmentRequired, } = lifecycleFence;
|
|
502
|
+
const sourceAuthority = authority(source.sourceAuthority, 'sourceAuthority');
|
|
503
|
+
const targetAuthority = authority(source.targetAuthority, 'targetAuthority');
|
|
504
|
+
const state = literal(source, 'state', ['active', 'cancelled', 'completed']);
|
|
505
|
+
if (sourceAuthority.kind === targetAuthority.kind
|
|
506
|
+
|| targetAuthority.generation !== sourceAuthority.generation + 1
|
|
507
|
+
|| (direction === 'lan-to-cloud'
|
|
508
|
+
&& (sourceAuthority.kind !== 'lan' || targetAuthority.kind !== 'cloud'))
|
|
509
|
+
|| (direction === 'cloud-to-lan'
|
|
510
|
+
&& (sourceAuthority.kind !== 'cloud' || targetAuthority.kind !== 'lan'))
|
|
511
|
+
|| (state === 'cancelled' && phase !== 'cancelled')
|
|
512
|
+
|| (state === 'completed' && phase !== 'completed')
|
|
513
|
+
|| (state === 'active' && (phase === 'cancelled' || phase === 'completed')))
|
|
514
|
+
throw invalidPayload('transferStatus');
|
|
515
|
+
const relinquishmentProof = source.relinquishmentProof === null
|
|
516
|
+
? null
|
|
517
|
+
: decodeCollabAuthorityRelinquishmentProof(source.relinquishmentProof);
|
|
518
|
+
if (relinquishmentRequired !== (relinquishmentProof !== null)
|
|
519
|
+
|| (relinquishmentProof !== null && (relinquishmentProof.batchRevision !== batchRevision
|
|
520
|
+
|| relinquishmentProof.batchSha256 !== batchSha256
|
|
521
|
+
|| relinquishmentProof.checkpointSha256 !== checkpointSha256
|
|
522
|
+
|| relinquishmentProof.projectId !== source.projectId
|
|
523
|
+
|| relinquishmentProof.sourceAuthority.generation !== sourceAuthority.generation
|
|
524
|
+
|| relinquishmentProof.sourceAuthority.kind !== sourceAuthority.kind
|
|
525
|
+
|| relinquishmentProof.targetAuthority.generation !== targetAuthority.generation
|
|
526
|
+
|| relinquishmentProof.targetAuthority.kind !== targetAuthority.kind
|
|
527
|
+
|| relinquishmentProof.transferId !== source.transferId)))
|
|
528
|
+
throw invalidPayload('relinquishmentProof');
|
|
529
|
+
const createdAt = timestamp(source, 'createdAt');
|
|
530
|
+
const updatedAt = timestamp(source, 'updatedAt');
|
|
531
|
+
const expiresAt = timestamp(source, 'expiresAt');
|
|
532
|
+
if (Date.parse(updatedAt) < Date.parse(createdAt)
|
|
533
|
+
|| Date.parse(expiresAt) <= Date.parse(updatedAt))
|
|
534
|
+
throw invalidPayload('transferStatus');
|
|
535
|
+
return {
|
|
536
|
+
batchRevision,
|
|
537
|
+
batchSha256,
|
|
538
|
+
checkpointSha256,
|
|
539
|
+
createdAt,
|
|
540
|
+
direction,
|
|
541
|
+
expiresAt,
|
|
542
|
+
phase,
|
|
543
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
544
|
+
relinquishmentProof,
|
|
545
|
+
sourceAuthority,
|
|
546
|
+
state,
|
|
547
|
+
targetAuthority,
|
|
548
|
+
targetUrl: absoluteTargetUrl(source),
|
|
549
|
+
transferId: token(source, 'transferId'),
|
|
550
|
+
updatedAt,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function mutationFields(source) {
|
|
554
|
+
return {
|
|
555
|
+
idempotencyKey: token(source, 'idempotencyKey'),
|
|
556
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
function decodeRequestLanToCloudTransfer(value) {
|
|
560
|
+
const source = exactRecord(value, 'request', [
|
|
561
|
+
'expectedAuthorityGeneration',
|
|
562
|
+
'idempotencyKey',
|
|
563
|
+
'projectId',
|
|
564
|
+
'targetUrl',
|
|
565
|
+
]);
|
|
566
|
+
return {
|
|
567
|
+
expectedAuthorityGeneration: positiveInteger(source, 'expectedAuthorityGeneration'),
|
|
568
|
+
...mutationFields(source),
|
|
569
|
+
targetUrl: absoluteTargetUrl(source),
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function decodeAcceptLanToCloudTransferTarget(value) {
|
|
573
|
+
const source = exactRecord(value, 'request', [
|
|
574
|
+
'expectedAuthorityGeneration',
|
|
575
|
+
'idempotencyKey',
|
|
576
|
+
'projectId',
|
|
577
|
+
'targetUrl',
|
|
578
|
+
'transferId',
|
|
579
|
+
]);
|
|
580
|
+
return {
|
|
581
|
+
expectedAuthorityGeneration: positiveInteger(source, 'expectedAuthorityGeneration'),
|
|
582
|
+
...mutationFields(source),
|
|
583
|
+
targetUrl: absoluteTargetUrl(source),
|
|
584
|
+
transferId: token(source, 'transferId'),
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
function decodeBeginLanToCloudTransfer(value) {
|
|
588
|
+
const source = exactRecord(value, 'request', [
|
|
589
|
+
'checkpointManifestSha256',
|
|
590
|
+
'expectedSourceAuthorityGeneration',
|
|
591
|
+
'idempotencyKey',
|
|
592
|
+
'projectId',
|
|
593
|
+
'sourceHostMemberId',
|
|
594
|
+
'sourceProof',
|
|
595
|
+
'targetUrl',
|
|
596
|
+
'transferId',
|
|
597
|
+
]);
|
|
598
|
+
return {
|
|
599
|
+
checkpointManifestSha256: sha256(source, 'checkpointManifestSha256'),
|
|
600
|
+
expectedSourceAuthorityGeneration: positiveInteger(source, 'expectedSourceAuthorityGeneration'),
|
|
601
|
+
...mutationFields(source),
|
|
602
|
+
sourceHostMemberId: token(source, 'sourceHostMemberId', isCollabMemberId),
|
|
603
|
+
sourceProof: base64url(source, 'sourceProof'),
|
|
604
|
+
targetUrl: absoluteTargetUrl(source),
|
|
605
|
+
transferId: token(source, 'transferId'),
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function decodeGetProjectAuthorityTransfer(value) {
|
|
609
|
+
const source = exactRecord(value, 'request', ['projectId', 'transferId']);
|
|
610
|
+
return {
|
|
611
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
612
|
+
transferId: token(source, 'transferId'),
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
function decodeAuthorityTransferReceiptVerifier(value) {
|
|
616
|
+
const source = exactRecord(value, 'receiptVerifier', [
|
|
617
|
+
'projectId',
|
|
618
|
+
'receiptKeyId',
|
|
619
|
+
'receiptPublicKey',
|
|
620
|
+
'receiptPublicKeyEncoding',
|
|
621
|
+
'signatureAlgorithm',
|
|
622
|
+
'transferId',
|
|
623
|
+
]);
|
|
624
|
+
return {
|
|
625
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
626
|
+
receiptKeyId: boundedString(source, 'receiptKeyId', 256),
|
|
627
|
+
receiptPublicKey: fixedBase64url(source, 'receiptPublicKey', 32),
|
|
628
|
+
receiptPublicKeyEncoding: literal(source, 'receiptPublicKeyEncoding', ['base64url-raw']),
|
|
629
|
+
signatureAlgorithm: literal(source, 'signatureAlgorithm', ['ed25519']),
|
|
630
|
+
transferId: token(source, 'transferId'),
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
function decodeRotateTransferredMembershipClaims(value) {
|
|
634
|
+
const source = exactRecord(value, 'request', [
|
|
635
|
+
'expectedBatchRevision',
|
|
636
|
+
'expectedBatchSha256',
|
|
637
|
+
'idempotencyKey',
|
|
638
|
+
'projectId',
|
|
639
|
+
'transferId',
|
|
640
|
+
]);
|
|
641
|
+
return {
|
|
642
|
+
expectedBatchRevision: positiveInteger(source, 'expectedBatchRevision'),
|
|
643
|
+
expectedBatchSha256: sha256(source, 'expectedBatchSha256'),
|
|
644
|
+
...mutationFields(source),
|
|
645
|
+
transferId: token(source, 'transferId'),
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
function decodeAcknowledgeTransferredMembershipClaimBatch(value) {
|
|
649
|
+
const source = exactRecord(value, 'request', [
|
|
650
|
+
'batchRevision',
|
|
651
|
+
'batchSha256',
|
|
652
|
+
'idempotencyKey',
|
|
653
|
+
'operationIntentId',
|
|
654
|
+
'projectId',
|
|
655
|
+
'transferId',
|
|
656
|
+
]);
|
|
657
|
+
return {
|
|
658
|
+
batchRevision: positiveInteger(source, 'batchRevision'),
|
|
659
|
+
batchSha256: sha256(source, 'batchSha256'),
|
|
660
|
+
...mutationFields(source),
|
|
661
|
+
operationIntentId: token(source, 'operationIntentId'),
|
|
662
|
+
transferId: token(source, 'transferId'),
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function decodeGetTransferredMembershipClaim(value) {
|
|
666
|
+
return decodeGetProjectAuthorityTransfer(value);
|
|
667
|
+
}
|
|
668
|
+
function decodeClaimTransferredMembership(value) {
|
|
669
|
+
const raw = record(value, 'request');
|
|
670
|
+
const hasCredentialHash = Object.hasOwn(raw, 'credentialHash');
|
|
671
|
+
const source = exactRecord(value, 'request', hasCredentialHash
|
|
672
|
+
? ['claim', 'credentialHash', 'idempotencyKey', 'projectId', 'transferId']
|
|
673
|
+
: ['claim', 'idempotencyKey', 'projectId', 'transferId']);
|
|
674
|
+
const common = {
|
|
675
|
+
claim: base64url(source, 'claim'),
|
|
676
|
+
...mutationFields(source),
|
|
677
|
+
transferId: token(source, 'transferId'),
|
|
678
|
+
};
|
|
679
|
+
return hasCredentialHash
|
|
680
|
+
? {
|
|
681
|
+
...common,
|
|
682
|
+
credentialHash: sha256(source, 'credentialHash'),
|
|
683
|
+
}
|
|
684
|
+
: common;
|
|
685
|
+
}
|
|
686
|
+
function decodeAcknowledgeTransferredMembershipClaimRedemption(value) {
|
|
687
|
+
const source = exactRecord(value, 'request', [
|
|
688
|
+
'idempotencyKey',
|
|
689
|
+
'projectId',
|
|
690
|
+
'receipt',
|
|
691
|
+
'transferId',
|
|
692
|
+
]);
|
|
693
|
+
const common = mutationFields(source);
|
|
694
|
+
const transferId = token(source, 'transferId');
|
|
695
|
+
const receipt = decodeCollabTransferredMembershipRedemptionReceipt(source.receipt);
|
|
696
|
+
if (receipt.projectId !== common.projectId || receipt.transferId !== transferId) {
|
|
697
|
+
throw invalidPayload('receipt');
|
|
698
|
+
}
|
|
699
|
+
return { ...common, receipt, transferId };
|
|
700
|
+
}
|
|
701
|
+
function decodeCommitLanToCloudRelinquishment(value) {
|
|
702
|
+
const source = exactRecord(value, 'request', [
|
|
703
|
+
'idempotencyKey',
|
|
704
|
+
'projectId',
|
|
705
|
+
'proof',
|
|
706
|
+
'transferId',
|
|
707
|
+
]);
|
|
708
|
+
const common = mutationFields(source);
|
|
709
|
+
const transferId = token(source, 'transferId');
|
|
710
|
+
const proof = decodeCollabAuthorityRelinquishmentProof(source.proof);
|
|
711
|
+
if (proof.projectId !== common.projectId || proof.transferId !== transferId) {
|
|
712
|
+
throw invalidPayload('proof');
|
|
713
|
+
}
|
|
714
|
+
return { ...common, proof, transferId };
|
|
715
|
+
}
|
|
716
|
+
function decodeBeginCloudToLanTransfer(value) {
|
|
717
|
+
const source = exactRecord(value, 'request', [
|
|
718
|
+
'expectedAuthorityGeneration',
|
|
719
|
+
'idempotencyKey',
|
|
720
|
+
'projectId',
|
|
721
|
+
'targetHostMemberId',
|
|
722
|
+
'targetUrl',
|
|
723
|
+
]);
|
|
724
|
+
return {
|
|
725
|
+
expectedAuthorityGeneration: positiveInteger(source, 'expectedAuthorityGeneration'),
|
|
726
|
+
...mutationFields(source),
|
|
727
|
+
targetHostMemberId: token(source, 'targetHostMemberId', isCollabMemberId),
|
|
728
|
+
targetUrl: absoluteTargetUrl(source),
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function decodeAcceptCloudToLanTransferTarget(value) {
|
|
732
|
+
const source = exactRecord(value, 'request', [
|
|
733
|
+
'idempotencyKey',
|
|
734
|
+
'projectId',
|
|
735
|
+
'targetHostMemberId',
|
|
736
|
+
'targetProof',
|
|
737
|
+
'transferId',
|
|
738
|
+
]);
|
|
739
|
+
return {
|
|
740
|
+
...mutationFields(source),
|
|
741
|
+
targetHostMemberId: token(source, 'targetHostMemberId', isCollabMemberId),
|
|
742
|
+
targetProof: base64url(source, 'targetProof'),
|
|
743
|
+
transferId: token(source, 'transferId'),
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function decodeReportCloudToLanTargetStaged(value) {
|
|
747
|
+
const source = exactRecord(value, 'request', [
|
|
748
|
+
'checkpointSha256',
|
|
749
|
+
'claimBatch',
|
|
750
|
+
'idempotencyKey',
|
|
751
|
+
'projectId',
|
|
752
|
+
'stageSha256',
|
|
753
|
+
'targetAuthority',
|
|
754
|
+
'targetProof',
|
|
755
|
+
'transferId',
|
|
756
|
+
]);
|
|
757
|
+
const targetAuthority = authority(source.targetAuthority, 'targetAuthority');
|
|
758
|
+
if (targetAuthority.kind !== 'lan')
|
|
759
|
+
throw invalidPayload('targetAuthority');
|
|
760
|
+
const common = mutationFields(source);
|
|
761
|
+
const transferId = token(source, 'transferId');
|
|
762
|
+
const checkpointSha256 = sha256(source, 'checkpointSha256');
|
|
763
|
+
const claimBatch = decodeCollabTransferredMembershipClaimBatch(source.claimBatch);
|
|
764
|
+
if (claimBatch.projectId !== common.projectId
|
|
765
|
+
|| claimBatch.transferId !== transferId
|
|
766
|
+
|| claimBatch.checkpointSha256 !== checkpointSha256
|
|
767
|
+
|| claimBatch.targetAuthorityGeneration !== targetAuthority.generation)
|
|
768
|
+
throw invalidPayload('claimBatch');
|
|
769
|
+
return {
|
|
770
|
+
checkpointSha256,
|
|
771
|
+
claimBatch,
|
|
772
|
+
...common,
|
|
773
|
+
stageSha256: sha256(source, 'stageSha256'),
|
|
774
|
+
targetAuthority,
|
|
775
|
+
targetProof: base64url(source, 'targetProof'),
|
|
776
|
+
transferId,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function decodeConfirmCloudToLanTargetActive(value) {
|
|
780
|
+
const source = exactRecord(value, 'request', [
|
|
781
|
+
'idempotencyKey',
|
|
782
|
+
'projectId',
|
|
783
|
+
'relinquishmentProof',
|
|
784
|
+
'targetActivationProof',
|
|
785
|
+
'transferId',
|
|
786
|
+
]);
|
|
787
|
+
const common = mutationFields(source);
|
|
788
|
+
const transferId = token(source, 'transferId');
|
|
789
|
+
const relinquishmentProof = decodeCollabAuthorityRelinquishmentProof(source.relinquishmentProof);
|
|
790
|
+
if (relinquishmentProof.projectId !== common.projectId
|
|
791
|
+
|| relinquishmentProof.transferId !== transferId
|
|
792
|
+
|| relinquishmentProof.sourceAuthority.kind !== 'cloud'
|
|
793
|
+
|| relinquishmentProof.targetAuthority.kind !== 'lan')
|
|
794
|
+
throw invalidPayload('relinquishmentProof');
|
|
795
|
+
return {
|
|
796
|
+
...common,
|
|
797
|
+
relinquishmentProof,
|
|
798
|
+
targetActivationProof: base64url(source, 'targetActivationProof'),
|
|
799
|
+
transferId,
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function decodeCancelProjectAuthorityTransfer(value) {
|
|
803
|
+
const source = exactRecord(value, 'request', [
|
|
804
|
+
'expectedPhase',
|
|
805
|
+
'idempotencyKey',
|
|
806
|
+
'projectId',
|
|
807
|
+
'transferId',
|
|
808
|
+
]);
|
|
809
|
+
const expectedPhase = source.expectedPhase;
|
|
810
|
+
if (typeof expectedPhase !== 'string'
|
|
811
|
+
|| !CANCELLABLE_PHASE_SET.has(expectedPhase))
|
|
812
|
+
throw invalidPayload('expectedPhase');
|
|
813
|
+
return {
|
|
814
|
+
expectedPhase: expectedPhase,
|
|
815
|
+
...mutationFields(source),
|
|
816
|
+
transferId: token(source, 'transferId'),
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
export function decodeCollabAuthorityTransferOperationRequest(operation, value) {
|
|
820
|
+
const decoded = (() => {
|
|
821
|
+
switch (operation) {
|
|
822
|
+
case 'requestLanToCloudTransfer': return decodeRequestLanToCloudTransfer(value);
|
|
823
|
+
case 'acceptLanToCloudTransferTarget': return decodeAcceptLanToCloudTransferTarget(value);
|
|
824
|
+
case 'beginLanToCloudTransfer': return decodeBeginLanToCloudTransfer(value);
|
|
825
|
+
case 'getProjectAuthorityTransfer': return decodeGetProjectAuthorityTransfer(value);
|
|
826
|
+
case 'getAuthorityTransferReceiptVerifier':
|
|
827
|
+
return decodeGetProjectAuthorityTransfer(value);
|
|
828
|
+
case 'rotateTransferredMembershipClaims':
|
|
829
|
+
return decodeRotateTransferredMembershipClaims(value);
|
|
830
|
+
case 'acknowledgeTransferredMembershipClaimBatch':
|
|
831
|
+
return decodeAcknowledgeTransferredMembershipClaimBatch(value);
|
|
832
|
+
case 'getTransferredMembershipClaim': return decodeGetTransferredMembershipClaim(value);
|
|
833
|
+
case 'claimTransferredMembership': return decodeClaimTransferredMembership(value);
|
|
834
|
+
case 'acknowledgeTransferredMembershipClaimRedemption':
|
|
835
|
+
return decodeAcknowledgeTransferredMembershipClaimRedemption(value);
|
|
836
|
+
case 'commitLanToCloudRelinquishment':
|
|
837
|
+
return decodeCommitLanToCloudRelinquishment(value);
|
|
838
|
+
case 'beginCloudToLanTransfer': return decodeBeginCloudToLanTransfer(value);
|
|
839
|
+
case 'acceptCloudToLanTransferTarget': return decodeAcceptCloudToLanTransferTarget(value);
|
|
840
|
+
case 'reportCloudToLanTargetStaged': return decodeReportCloudToLanTargetStaged(value);
|
|
841
|
+
case 'confirmCloudToLanTargetActive': return decodeConfirmCloudToLanTargetActive(value);
|
|
842
|
+
case 'cancelProjectAuthorityTransfer': return decodeCancelProjectAuthorityTransfer(value);
|
|
843
|
+
}
|
|
844
|
+
})();
|
|
845
|
+
return decoded;
|
|
846
|
+
}
|
|
847
|
+
function decodeRedemptionAcknowledgement(value) {
|
|
848
|
+
const source = exactRecord(value, 'redemptionAcknowledgement', [
|
|
849
|
+
'acknowledgedAt',
|
|
850
|
+
'memberId',
|
|
851
|
+
'projectId',
|
|
852
|
+
'receiptId',
|
|
853
|
+
'transferId',
|
|
854
|
+
]);
|
|
855
|
+
return {
|
|
856
|
+
acknowledgedAt: timestamp(source, 'acknowledgedAt'),
|
|
857
|
+
memberId: token(source, 'memberId', isCollabMemberId),
|
|
858
|
+
projectId: token(source, 'projectId', isCollabProjectId),
|
|
859
|
+
receiptId: token(source, 'receiptId'),
|
|
860
|
+
transferId: token(source, 'transferId'),
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
export function decodeCollabAuthorityTransferOperationResponse(operation, value) {
|
|
864
|
+
const decoded = (() => {
|
|
865
|
+
switch (operation) {
|
|
866
|
+
case 'rotateTransferredMembershipClaims':
|
|
867
|
+
return decodeCollabTransferredMembershipClaimBatch(value);
|
|
868
|
+
case 'acknowledgeTransferredMembershipClaimBatch':
|
|
869
|
+
case 'reportCloudToLanTargetStaged':
|
|
870
|
+
return decodeCollabTransferredMembershipClaimCustodyReceipt(value);
|
|
871
|
+
case 'getTransferredMembershipClaim':
|
|
872
|
+
return decodeCollabTransferredMembershipClaim(value);
|
|
873
|
+
case 'claimTransferredMembership':
|
|
874
|
+
return decodeCollabTransferredMembershipRedemptionReceipt(value);
|
|
875
|
+
case 'acknowledgeTransferredMembershipClaimRedemption':
|
|
876
|
+
return decodeRedemptionAcknowledgement(value);
|
|
877
|
+
case 'getAuthorityTransferReceiptVerifier':
|
|
878
|
+
return decodeAuthorityTransferReceiptVerifier(value);
|
|
879
|
+
default:
|
|
880
|
+
return decodeCollabAuthorityTransferStatus(value);
|
|
881
|
+
}
|
|
882
|
+
})();
|
|
883
|
+
return decoded;
|
|
884
|
+
}
|