@claudian-collab/protocol 3.2.0 → 3.3.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.
Files changed (35) hide show
  1. package/README.md +8 -6
  2. package/dist/CollabAuthorityTransfer.d.ts +1 -1
  3. package/dist/CollabCloudBinding.d.ts +1 -1
  4. package/dist/CollabCloudBinding.js +7 -0
  5. package/dist/CollabControlOperationCodecs.d.ts +18 -0
  6. package/dist/CollabControlOperationCodecs.js +2 -0
  7. package/dist/CollabProjectBackupCheckpoint.d.ts +93 -4
  8. package/dist/CollabProjectBackupCheckpoint.js +640 -12
  9. package/dist/CollabProjectMembership.d.ts +318 -0
  10. package/dist/CollabProjectMembership.js +714 -0
  11. package/dist/CollabProtocol.d.ts +2 -1
  12. package/dist/esm/CollabAuthorityTransfer.mjs +884 -0
  13. package/dist/esm/CollabCloudBinding.mjs +559 -0
  14. package/dist/esm/CollabCloudProjectEvent.mjs +150 -0
  15. package/dist/esm/CollabCloudProjectSnapshot.mjs +277 -0
  16. package/dist/esm/CollabConstants.mjs +31 -0
  17. package/dist/esm/CollabControlOperationCodecs.mjs +106 -0
  18. package/dist/esm/CollabError.mjs +175 -0
  19. package/dist/esm/CollabMarkdownProse.mjs +54 -0
  20. package/dist/esm/CollabMemberMentionParser.mjs +48 -0
  21. package/dist/esm/CollabProjectBackupCheckpoint.mjs +2101 -0
  22. package/dist/esm/CollabProjectCheckpoint.mjs +1326 -0
  23. package/dist/esm/CollabProjectMembership.mjs +709 -0
  24. package/dist/esm/CollabProjectRetirement.mjs +119 -0
  25. package/dist/esm/CollabProtocol.mjs +64 -0
  26. package/dist/esm/CollabRequestTicketRequestCodecs.mjs +263 -0
  27. package/dist/esm/CollabRequestTicketResponseCodecs.mjs +353 -0
  28. package/dist/esm/CollabTicketReferenceParser.mjs +52 -0
  29. package/dist/esm/CollabValidation.mjs +19 -0
  30. package/dist/esm/DevelopmentBootstrap.mjs +537 -0
  31. package/dist/esm/index.mjs +17 -0
  32. package/dist/esm/types.mjs +8 -0
  33. package/dist/index.d.ts +4 -2
  34. package/dist/index.js +10 -3
  35. package/package.json +5 -2
@@ -0,0 +1,150 @@
1
+ import { COLLAB_LIMITS, COLLAB_PROTOCOL_VERSION } from './CollabConstants.mjs';
2
+ import { CollabError } from './CollabError.mjs';
3
+ import { hasUtf8ByteLengthAtMost, isCollabGitOid, isCollabMemberId, isCollabOpaqueId, isCollabProjectId, } from './CollabValidation.mjs';
4
+ export const COLLAB_CLOUD_EVENT_KINDS = Object.freeze([
5
+ 'membership.updated',
6
+ 'request.updated',
7
+ 'request.comment-added',
8
+ 'ticket.updated',
9
+ 'ticket.comment-added',
10
+ 'main.updated',
11
+ 'authority-transfer.updated',
12
+ 'membership.claimed',
13
+ 'project.retired',
14
+ ]);
15
+ const EVENT_KIND_SET = new Set(COLLAB_CLOUD_EVENT_KINDS);
16
+ function invalidPayload(field) {
17
+ return new CollabError({
18
+ code: 'protocol-payload-invalid',
19
+ safeContext: { field },
20
+ });
21
+ }
22
+ function record(value, field) {
23
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
24
+ throw invalidPayload(field);
25
+ }
26
+ return value;
27
+ }
28
+ function exactRecord(value, field, keys) {
29
+ const source = record(value, field);
30
+ const expected = new Set(keys);
31
+ if (!keys.every(key => Object.hasOwn(source, key))
32
+ || Object.keys(source).some(key => !expected.has(key)))
33
+ throw invalidPayload(field);
34
+ return source;
35
+ }
36
+ function stringField(source, field, maximum, validate) {
37
+ const value = source[field];
38
+ if (typeof value !== 'string'
39
+ || value.length === 0
40
+ || value.length > maximum
41
+ || !validate(value))
42
+ throw invalidPayload(field);
43
+ return value;
44
+ }
45
+ function nonNegativeInteger(source, field) {
46
+ const value = source[field];
47
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
48
+ throw invalidPayload(field);
49
+ }
50
+ return value;
51
+ }
52
+ function positiveInteger(source, field) {
53
+ const value = nonNegativeInteger(source, field);
54
+ if (value < 1)
55
+ throw invalidPayload(field);
56
+ return value;
57
+ }
58
+ function timestamp(source, field) {
59
+ const value = source[field];
60
+ if (typeof value !== 'string'
61
+ || value.length > 64
62
+ || Number.isNaN(Date.parse(value))
63
+ || new Date(value).toISOString() !== value)
64
+ throw invalidPayload(field);
65
+ return value;
66
+ }
67
+ function decodePayload(kind, value) {
68
+ switch (kind) {
69
+ case 'authority-transfer.updated': {
70
+ const source = exactRecord(value, 'payload', ['transferId']);
71
+ return { transferId: stringField(source, 'transferId', 128, isCollabOpaqueId) };
72
+ }
73
+ case 'membership.updated': {
74
+ const source = exactRecord(value, 'payload', ['memberId']);
75
+ return { memberId: stringField(source, 'memberId', 64, isCollabMemberId) };
76
+ }
77
+ case 'membership.claimed': {
78
+ const source = exactRecord(value, 'payload', ['memberId', 'transferId']);
79
+ return {
80
+ memberId: stringField(source, 'memberId', 64, isCollabMemberId),
81
+ transferId: stringField(source, 'transferId', 128, isCollabOpaqueId),
82
+ };
83
+ }
84
+ case 'project.retired': {
85
+ const source = exactRecord(value, 'payload', ['retiredAt', 'retirementId']);
86
+ return {
87
+ retiredAt: timestamp(source, 'retiredAt'),
88
+ retirementId: stringField(source, 'retirementId', 128, isCollabOpaqueId),
89
+ };
90
+ }
91
+ case 'request.updated':
92
+ case 'request.comment-added': {
93
+ const source = exactRecord(value, 'payload', ['requestId']);
94
+ return { requestId: stringField(source, 'requestId', 128, isCollabOpaqueId) };
95
+ }
96
+ case 'ticket.updated':
97
+ case 'ticket.comment-added': {
98
+ const source = exactRecord(value, 'payload', ['ticketId']);
99
+ return { ticketId: stringField(source, 'ticketId', 128, isCollabOpaqueId) };
100
+ }
101
+ case 'main.updated': {
102
+ const source = exactRecord(value, 'payload', ['mainOid', 'requestId']);
103
+ return {
104
+ mainOid: stringField(source, 'mainOid', 64, isCollabGitOid),
105
+ requestId: stringField(source, 'requestId', 128, isCollabOpaqueId),
106
+ };
107
+ }
108
+ }
109
+ }
110
+ export function decodeCollabCloudProjectEventMessage(value) {
111
+ let serialized;
112
+ try {
113
+ serialized = JSON.stringify(value);
114
+ }
115
+ catch {
116
+ throw invalidPayload('event');
117
+ }
118
+ if (!hasUtf8ByteLengthAtMost(serialized, COLLAB_LIMITS.maxJsonPayloadUtf8Bytes)) {
119
+ throw invalidPayload('event');
120
+ }
121
+ const candidate = record(value, 'event');
122
+ if (candidate.kind === 'snapshot.required') {
123
+ const source = exactRecord(candidate, 'snapshot.required', ['kind', 'latestSequence']);
124
+ return {
125
+ kind: 'snapshot.required',
126
+ latestSequence: nonNegativeInteger(source, 'latestSequence'),
127
+ };
128
+ }
129
+ const source = exactRecord(candidate, 'event', [
130
+ 'kind',
131
+ 'occurredAt',
132
+ 'payload',
133
+ 'projectId',
134
+ 'protocolVersion',
135
+ 'sequence',
136
+ ]);
137
+ if (source.protocolVersion !== COLLAB_PROTOCOL_VERSION
138
+ || typeof source.kind !== 'string'
139
+ || !EVENT_KIND_SET.has(source.kind))
140
+ throw invalidPayload('event');
141
+ const kind = source.kind;
142
+ return {
143
+ kind,
144
+ occurredAt: timestamp(source, 'occurredAt'),
145
+ payload: decodePayload(kind, source.payload),
146
+ projectId: stringField(source, 'projectId', 64, isCollabProjectId),
147
+ protocolVersion: COLLAB_PROTOCOL_VERSION,
148
+ sequence: positiveInteger(source, 'sequence'),
149
+ };
150
+ }
@@ -0,0 +1,277 @@
1
+ import { COLLAB_LIMITS, COLLAB_MAIN_REF, } from './CollabConstants.mjs';
2
+ import { COLLAB_CLOUD_BINDING_LIMITS } from './CollabCloudBinding.mjs';
3
+ import { CollabError } from './CollabError.mjs';
4
+ import { collabMemberRef } from './types.mjs';
5
+ import { hasUtf8ByteLengthAtMost, isCollabGitOid, isCollabMemberId, isCollabOpaqueId, isCollabProjectId, } from './CollabValidation.mjs';
6
+ function invalidPayload(field) {
7
+ return new CollabError({
8
+ code: 'protocol-payload-invalid',
9
+ safeContext: { field },
10
+ });
11
+ }
12
+ function record(value, field) {
13
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
14
+ throw invalidPayload(field);
15
+ }
16
+ return value;
17
+ }
18
+ function exactRecord(value, field, required, optional = []) {
19
+ const source = record(value, field);
20
+ const allowed = new Set([...required, ...optional]);
21
+ if (!required.every(key => Object.hasOwn(source, key))
22
+ || Object.keys(source).some(key => !allowed.has(key)))
23
+ throw invalidPayload(field);
24
+ return source;
25
+ }
26
+ function stringField(source, field, maximum, validate, allowEmpty = false) {
27
+ const value = source[field];
28
+ if (typeof value !== 'string'
29
+ || (!allowEmpty && value.length === 0)
30
+ || value.length > maximum
31
+ || (validate && !validate(value)))
32
+ throw invalidPayload(field);
33
+ return value;
34
+ }
35
+ function textUtf8(source, field, maximum) {
36
+ const value = source[field];
37
+ if (typeof value !== 'string'
38
+ || !hasUtf8ByteLengthAtMost(value, maximum))
39
+ throw invalidPayload(field);
40
+ return value;
41
+ }
42
+ function timestamp(source, field) {
43
+ const value = stringField(source, field, 64);
44
+ if (Number.isNaN(Date.parse(value)) || new Date(value).toISOString() !== value) {
45
+ throw invalidPayload(field);
46
+ }
47
+ return value;
48
+ }
49
+ function nonNegativeInteger(source, field, maximum) {
50
+ const value = source[field];
51
+ if (typeof value !== 'number'
52
+ || !Number.isSafeInteger(value)
53
+ || value < 0
54
+ || (maximum !== undefined && value > maximum))
55
+ throw invalidPayload(field);
56
+ return value;
57
+ }
58
+ function positiveInteger(source, field) {
59
+ const value = nonNegativeInteger(source, field);
60
+ if (value < 1)
61
+ throw invalidPayload(field);
62
+ return value;
63
+ }
64
+ function decodeProject(value) {
65
+ const source = exactRecord(value, 'project', [
66
+ 'createdAt',
67
+ 'expectedMainOid',
68
+ 'id',
69
+ 'mainRef',
70
+ 'name',
71
+ ]);
72
+ if (source.mainRef !== COLLAB_MAIN_REF)
73
+ throw invalidPayload('mainRef');
74
+ return {
75
+ createdAt: timestamp(source, 'createdAt'),
76
+ expectedMainOid: stringField(source, 'expectedMainOid', 64, isCollabGitOid),
77
+ id: stringField(source, 'id', 64, isCollabProjectId),
78
+ mainRef: COLLAB_MAIN_REF,
79
+ name: stringField(source, 'name', COLLAB_LIMITS.maxProjectNameUtf16),
80
+ };
81
+ }
82
+ function decodeMember(value) {
83
+ const source = exactRecord(value, 'member', [
84
+ 'activatedAt',
85
+ 'createdAt',
86
+ 'displayName',
87
+ 'id',
88
+ 'personalRef',
89
+ 'role',
90
+ 'status',
91
+ ]);
92
+ const id = stringField(source, 'id', 64, isCollabMemberId);
93
+ if (source.status !== 'active'
94
+ || (source.role !== 'manager' && source.role !== 'member'))
95
+ throw invalidPayload('member');
96
+ const personalRef = stringField(source, 'personalRef', COLLAB_LIMITS.maxRepositoryPathUtf16);
97
+ if (personalRef !== collabMemberRef(id))
98
+ throw invalidPayload('personalRef');
99
+ return {
100
+ activatedAt: timestamp(source, 'activatedAt'),
101
+ createdAt: timestamp(source, 'createdAt'),
102
+ displayName: stringField(source, 'displayName', COLLAB_LIMITS.maxMemberDisplayNameUtf16),
103
+ id,
104
+ personalRef,
105
+ role: source.role,
106
+ status: 'active',
107
+ };
108
+ }
109
+ function decodeRequestTicketRelation(value) {
110
+ const source = exactRecord(value, 'request.ticketRelations', [
111
+ 'commitOid',
112
+ 'id',
113
+ 'kind',
114
+ 'state',
115
+ 'ticketId',
116
+ 'ticketNumber',
117
+ 'ticketRevision',
118
+ 'ticketTitle',
119
+ ]);
120
+ if ((source.kind !== 'references' && source.kind !== 'resolves')
121
+ || (source.state !== 'pending' && source.state !== 'accepted'))
122
+ throw invalidPayload('request.ticketRelations');
123
+ return {
124
+ commitOid: stringField(source, 'commitOid', 64, isCollabGitOid),
125
+ id: stringField(source, 'id', 128, isCollabOpaqueId),
126
+ kind: source.kind,
127
+ state: source.state,
128
+ ticketId: stringField(source, 'ticketId', 128, isCollabOpaqueId),
129
+ ticketNumber: positiveInteger(source, 'ticketNumber'),
130
+ ticketRevision: positiveInteger(source, 'ticketRevision'),
131
+ ticketTitle: stringField(source, 'ticketTitle', COLLAB_LIMITS.maxTicketTitleUtf16),
132
+ };
133
+ }
134
+ function decodeOpenRequest(value) {
135
+ const source = exactRecord(value, 'openRequest', [
136
+ 'commentCount',
137
+ 'createdAt',
138
+ 'description',
139
+ 'firstBaseOid',
140
+ 'id',
141
+ 'latestHeadOid',
142
+ 'memberId',
143
+ 'revision',
144
+ 'status',
145
+ 'ticketRelations',
146
+ 'updatedAt',
147
+ ]);
148
+ if (source.status !== 'open'
149
+ || !Array.isArray(source.ticketRelations)
150
+ || source.ticketRelations.length > COLLAB_LIMITS.maxRequestTicketRelations)
151
+ throw invalidPayload('openRequest');
152
+ return {
153
+ commentCount: nonNegativeInteger(source, 'commentCount', COLLAB_LIMITS.maxRequestComments),
154
+ createdAt: timestamp(source, 'createdAt'),
155
+ description: textUtf8(source, 'description', COLLAB_LIMITS.maxRequestDescriptionBytes),
156
+ firstBaseOid: stringField(source, 'firstBaseOid', 64, isCollabGitOid),
157
+ id: stringField(source, 'id', 128, isCollabOpaqueId),
158
+ latestHeadOid: stringField(source, 'latestHeadOid', 64, isCollabGitOid),
159
+ memberId: stringField(source, 'memberId', 64, isCollabMemberId),
160
+ revision: nonNegativeInteger(source, 'revision'),
161
+ status: 'open',
162
+ ticketRelations: source.ticketRelations.map(decodeRequestTicketRelation),
163
+ updatedAt: timestamp(source, 'updatedAt'),
164
+ };
165
+ }
166
+ function decodeOpenTicket(value) {
167
+ const source = exactRecord(value, 'ticketHighlight', [
168
+ 'acceptedRelationCount',
169
+ 'authorMemberId',
170
+ 'commentCount',
171
+ 'createdAt',
172
+ 'id',
173
+ 'number',
174
+ 'revision',
175
+ 'status',
176
+ 'title',
177
+ 'updatedAt',
178
+ ]);
179
+ if (source.status !== 'open')
180
+ throw invalidPayload('ticketHighlight');
181
+ return {
182
+ acceptedRelationCount: nonNegativeInteger(source, 'acceptedRelationCount', COLLAB_LIMITS.maxTicketAcceptedRelations),
183
+ authorMemberId: stringField(source, 'authorMemberId', 64, isCollabMemberId),
184
+ commentCount: nonNegativeInteger(source, 'commentCount', COLLAB_LIMITS.maxTicketComments),
185
+ createdAt: timestamp(source, 'createdAt'),
186
+ id: stringField(source, 'id', 128, isCollabOpaqueId),
187
+ number: positiveInteger(source, 'number'),
188
+ revision: nonNegativeInteger(source, 'revision'),
189
+ status: 'open',
190
+ title: stringField(source, 'title', COLLAB_LIMITS.maxTicketTitleUtf16),
191
+ updatedAt: timestamp(source, 'updatedAt'),
192
+ };
193
+ }
194
+ function assertSnapshotSize(value) {
195
+ let serialized;
196
+ try {
197
+ serialized = JSON.stringify(value);
198
+ }
199
+ catch {
200
+ throw invalidPayload('snapshot');
201
+ }
202
+ if (!hasUtf8ByteLengthAtMost(serialized, COLLAB_CLOUD_BINDING_LIMITS.maxCloudSnapshotUtf8Bytes))
203
+ throw invalidPayload('snapshot');
204
+ }
205
+ export function decodeCollabCloudProjectSnapshot(value) {
206
+ assertSnapshotSize(value);
207
+ const source = exactRecord(value, 'snapshot', [
208
+ 'currentMember',
209
+ 'eventSequence',
210
+ 'members',
211
+ 'openRequests',
212
+ 'openTicketCount',
213
+ 'project',
214
+ 'ticketHighlights',
215
+ ]);
216
+ if (!Array.isArray(source.members)
217
+ || source.members.length < 1
218
+ || source.members.length > COLLAB_CLOUD_BINDING_LIMITS.maxCloudProjectMembers
219
+ || !Array.isArray(source.openRequests)
220
+ || source.openRequests.length > COLLAB_CLOUD_BINDING_LIMITS.maxCloudOpenRequests
221
+ || !Array.isArray(source.ticketHighlights)
222
+ || source.ticketHighlights.length > COLLAB_CLOUD_BINDING_LIMITS.maxCloudTicketHighlights)
223
+ throw invalidPayload('snapshotCollections');
224
+ const members = source.members.map(decodeMember);
225
+ if (members.some((item, index) => (index > 0 && members[index - 1].id.localeCompare(item.id, 'en-US') >= 0)))
226
+ throw invalidPayload('members');
227
+ const currentMember = decodeMember(source.currentMember);
228
+ const matchingMember = members.find(member => member.id === currentMember.id);
229
+ if (!matchingMember || JSON.stringify(matchingMember) !== JSON.stringify(currentMember)) {
230
+ throw invalidPayload('currentMember');
231
+ }
232
+ const openRequests = source.openRequests.map(decodeOpenRequest);
233
+ if (openRequests.some((item, index) => ((index > 0 && openRequests[index - 1].id.localeCompare(item.id, 'en-US') >= 0)
234
+ || !members.some(member => member.id === item.memberId))))
235
+ throw invalidPayload('openRequests');
236
+ const ticketHighlights = source.ticketHighlights.map(decodeOpenTicket);
237
+ if (ticketHighlights.some((item, index) => {
238
+ if (index === 0)
239
+ return false;
240
+ const previous = ticketHighlights[index - 1];
241
+ return previous.updatedAt < item.updatedAt
242
+ || (previous.updatedAt === item.updatedAt
243
+ && previous.id.localeCompare(item.id, 'en-US') >= 0);
244
+ }))
245
+ throw invalidPayload('ticketHighlights');
246
+ const openTicketCount = nonNegativeInteger(source, 'openTicketCount');
247
+ if (openTicketCount < ticketHighlights.length)
248
+ throw invalidPayload('openTicketCount');
249
+ return {
250
+ currentMember,
251
+ eventSequence: nonNegativeInteger(source, 'eventSequence'),
252
+ members,
253
+ openRequests,
254
+ openTicketCount,
255
+ project: decodeProject(source.project),
256
+ ticketHighlights,
257
+ };
258
+ }
259
+ function decodeSnapshotRequest(value) {
260
+ try {
261
+ const source = exactRecord(value, 'request', ['projectId']);
262
+ return {
263
+ status: 'ok',
264
+ value: { projectId: stringField(source, 'projectId', 64, isCollabProjectId) },
265
+ };
266
+ }
267
+ catch (error) {
268
+ return {
269
+ status: 'invalid',
270
+ error: error instanceof CollabError ? error : invalidPayload('request'),
271
+ };
272
+ }
273
+ }
274
+ export const COLLAB_CLOUD_PROJECT_SNAPSHOT_CODEC = Object.freeze({
275
+ decodeRequest: decodeSnapshotRequest,
276
+ decodeResponse: decodeCollabCloudProjectSnapshot,
277
+ });
@@ -0,0 +1,31 @@
1
+ export const COLLAB_PROTOCOL_VERSION = 6;
2
+ export const COLLAB_MAIN_REF = 'refs/heads/main';
3
+ export const COLLAB_MEMBER_REF_PREFIX = 'refs/heads/members/';
4
+ export const COLLAB_LIMITS = Object.freeze({
5
+ maxBlobBytes: 50 * 1024 * 1024,
6
+ maxChangedPaths: 2_000,
7
+ maxCommentBytes: 16 * 1024,
8
+ maxMemberDisplayNameUtf16: 200,
9
+ maxRequestDescriptionBytes: 16 * 1024,
10
+ maxProjectNameUtf16: 200,
11
+ maxTicketTitleUtf16: 200,
12
+ maxTicketBodyBytes: 32 * 1024,
13
+ maxTicketCommentBytes: 16 * 1024,
14
+ maxRequestTicketRelations: 32,
15
+ maxRequestComments: 500,
16
+ defaultTicketPageSize: 50,
17
+ maxTicketPageSize: 100,
18
+ maxTicketComments: 500,
19
+ maxTicketAcceptedRelations: 2_000,
20
+ defaultCommentPageSize: 50,
21
+ maxCommentPageSize: 100,
22
+ commentPageMaxUtf8Bytes: 128 * 1024,
23
+ maxRelationsPerPage: 100,
24
+ relationPageMaxUtf8Bytes: 96 * 1024,
25
+ ticketPageMaxUtf8Bytes: 96 * 1024,
26
+ detailMaxUtf8Bytes: 448 * 1024,
27
+ maxJsonPayloadUtf8Bytes: 512 * 1024,
28
+ maxPageCursorUtf16: 512,
29
+ maxPathSegmentUtf16: 120,
30
+ maxRepositoryPathUtf16: 240,
31
+ });
@@ -0,0 +1,106 @@
1
+ import { COLLAB_AUTHORITY_TRANSFER_OPERATIONS, decodeCollabAuthorityTransferOperationRequest, decodeCollabAuthorityTransferOperationResponse, } from './CollabAuthorityTransfer.mjs';
2
+ import { CollabError } from './CollabError.mjs';
3
+ import { COLLAB_PROJECT_MEMBERSHIP_OPERATION_CODECS } from './CollabProjectMembership.mjs';
4
+ import { COLLAB_PROJECT_RETIREMENT_OPERATIONS, decodeCollabProjectRetirementOperationRequest, decodeCollabProjectRetirementOperationResponse, } from './CollabProjectRetirement.mjs';
5
+ import { decodeCollabRequestTicketOperationRequest, } from './CollabRequestTicketRequestCodecs.mjs';
6
+ import { decodeAcceptResponse, decodeCommentPageResponse, decodeCreateCommentResponse, decodeCreateTicketResponse, decodeEnsureMyRequestResponse, decodeRequestDetailResponse, decodeTicketAcceptedRelationPageResponse, decodeTicketCommentPageResponse, decodeTicketCommentResponse, decodeTicketDetailResponse, decodeTicketMutationResponse, decodeTicketPageResponse, decodeUpdateRequestMetadataResponse, } from './CollabRequestTicketResponseCodecs.mjs';
7
+ const AUTHORITY_TRANSFER_OPERATION_SET = new Set(COLLAB_AUTHORITY_TRANSFER_OPERATIONS);
8
+ const PROJECT_RETIREMENT_OPERATION_SET = new Set(COLLAB_PROJECT_RETIREMENT_OPERATIONS);
9
+ function lifecycleDecodeResult(decode) {
10
+ try {
11
+ return { status: 'ok', value: decode() };
12
+ }
13
+ catch (error) {
14
+ if (error instanceof CollabError && error.code === 'protocol-payload-invalid') {
15
+ return { error, status: 'invalid' };
16
+ }
17
+ throw error;
18
+ }
19
+ }
20
+ function decodeRequest(operation, input) {
21
+ if (AUTHORITY_TRANSFER_OPERATION_SET.has(operation)) {
22
+ return lifecycleDecodeResult(() => decodeCollabAuthorityTransferOperationRequest(operation, input));
23
+ }
24
+ if (PROJECT_RETIREMENT_OPERATION_SET.has(operation)) {
25
+ return lifecycleDecodeResult(() => decodeCollabProjectRetirementOperationRequest(operation, input));
26
+ }
27
+ return decodeCollabRequestTicketOperationRequest(operation, input);
28
+ }
29
+ function decodeResponse(operation, input) {
30
+ if (AUTHORITY_TRANSFER_OPERATION_SET.has(operation)) {
31
+ return decodeCollabAuthorityTransferOperationResponse(operation, input);
32
+ }
33
+ if (PROJECT_RETIREMENT_OPERATION_SET.has(operation)) {
34
+ return decodeCollabProjectRetirementOperationResponse(operation, input);
35
+ }
36
+ switch (operation) {
37
+ case 'getRequest': return decodeRequestDetailResponse(input);
38
+ case 'listRequestComments': return decodeCommentPageResponse(input);
39
+ case 'ensureMyRequest': return decodeEnsureMyRequestResponse(input);
40
+ case 'createComment': return decodeCreateCommentResponse(input);
41
+ case 'listTickets': return decodeTicketPageResponse(input);
42
+ case 'getTicket': return decodeTicketDetailResponse(input);
43
+ case 'listTicketComments': return decodeTicketCommentPageResponse(input);
44
+ case 'listTicketAcceptedRelations':
45
+ return decodeTicketAcceptedRelationPageResponse(input);
46
+ case 'createTicket': return decodeCreateTicketResponse(input);
47
+ case 'updateTicketContent': return decodeTicketMutationResponse(input);
48
+ case 'createTicketComment': return decodeTicketCommentResponse(input);
49
+ case 'closeTicket': return decodeTicketMutationResponse(input);
50
+ case 'reopenTicket': return decodeTicketMutationResponse(input);
51
+ case 'updateMyRequestMetadata': return decodeUpdateRequestMetadataResponse(input);
52
+ case 'acceptRequest': return decodeAcceptResponse(input);
53
+ }
54
+ }
55
+ function codec(operation) {
56
+ return Object.freeze({
57
+ decodeRequest: (input) => decodeRequest(operation, input),
58
+ decodeResponse: (input) => decodeResponse(operation, input),
59
+ });
60
+ }
61
+ export const COLLAB_CONTROL_OPERATION_CODECS = Object.freeze({
62
+ getRequest: codec('getRequest'),
63
+ listRequestComments: codec('listRequestComments'),
64
+ ensureMyRequest: codec('ensureMyRequest'),
65
+ createComment: codec('createComment'),
66
+ listTickets: codec('listTickets'),
67
+ getTicket: codec('getTicket'),
68
+ listTicketComments: codec('listTicketComments'),
69
+ listTicketAcceptedRelations: codec('listTicketAcceptedRelations'),
70
+ createTicket: codec('createTicket'),
71
+ updateTicketContent: codec('updateTicketContent'),
72
+ createTicketComment: codec('createTicketComment'),
73
+ closeTicket: codec('closeTicket'),
74
+ reopenTicket: codec('reopenTicket'),
75
+ updateMyRequestMetadata: codec('updateMyRequestMetadata'),
76
+ acceptRequest: codec('acceptRequest'),
77
+ requestLanToCloudTransfer: codec('requestLanToCloudTransfer'),
78
+ acceptLanToCloudTransferTarget: codec('acceptLanToCloudTransferTarget'),
79
+ beginLanToCloudTransfer: codec('beginLanToCloudTransfer'),
80
+ getProjectAuthorityTransfer: codec('getProjectAuthorityTransfer'),
81
+ getAuthorityTransferReceiptVerifier: codec('getAuthorityTransferReceiptVerifier'),
82
+ rotateTransferredMembershipClaims: codec('rotateTransferredMembershipClaims'),
83
+ acknowledgeTransferredMembershipClaimBatch: codec('acknowledgeTransferredMembershipClaimBatch'),
84
+ getTransferredMembershipClaim: codec('getTransferredMembershipClaim'),
85
+ claimTransferredMembership: codec('claimTransferredMembership'),
86
+ acknowledgeTransferredMembershipClaimRedemption: codec('acknowledgeTransferredMembershipClaimRedemption'),
87
+ commitLanToCloudRelinquishment: codec('commitLanToCloudRelinquishment'),
88
+ beginCloudToLanTransfer: codec('beginCloudToLanTransfer'),
89
+ acceptCloudToLanTransferTarget: codec('acceptCloudToLanTransferTarget'),
90
+ reportCloudToLanTargetStaged: codec('reportCloudToLanTargetStaged'),
91
+ confirmCloudToLanTargetActive: codec('confirmCloudToLanTargetActive'),
92
+ cancelProjectAuthorityTransfer: codec('cancelProjectAuthorityTransfer'),
93
+ retireProject: codec('retireProject'),
94
+ acknowledgeProjectRetirement: codec('acknowledgeProjectRetirement'),
95
+ ...COLLAB_PROJECT_MEMBERSHIP_OPERATION_CODECS,
96
+ });
97
+ export function collabControlOperationCodec(operation) {
98
+ if (!Object.hasOwn(COLLAB_CONTROL_OPERATION_CODECS, operation)) {
99
+ throw new CollabError({
100
+ code: 'operation-failed',
101
+ safeContext: { reason: 'control-operation-codec-missing' },
102
+ });
103
+ }
104
+ const selected = COLLAB_CONTROL_OPERATION_CODECS[operation];
105
+ return selected;
106
+ }