@claudian-collab/protocol 1.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/LICENSE +21 -0
- package/README.md +105 -0
- package/dist/CollabCloudBinding.d.ts +106 -0
- package/dist/CollabCloudBinding.js +431 -0
- package/dist/CollabCloudProjectEvent.d.ts +41 -0
- package/dist/CollabCloudProjectEvent.js +133 -0
- package/dist/CollabCloudProjectSnapshot.d.ts +42 -0
- package/dist/CollabCloudProjectSnapshot.js +281 -0
- package/dist/CollabConstants.d.ts +32 -0
- package/dist/CollabConstants.js +34 -0
- package/dist/CollabControlOperationCodecs.d.ts +28 -0
- package/dist/CollabControlOperationCodecs.js +63 -0
- package/dist/CollabError.d.ts +27 -0
- package/dist/CollabError.js +165 -0
- package/dist/CollabMarkdownProse.d.ts +1 -0
- package/dist/CollabMarkdownProse.js +57 -0
- package/dist/CollabMemberMentionParser.d.ts +6 -0
- package/dist/CollabMemberMentionParser.js +51 -0
- package/dist/CollabProtocol.d.ts +144 -0
- package/dist/CollabProtocol.js +67 -0
- package/dist/CollabRequestTicketRequestCodecs.d.ts +5 -0
- package/dist/CollabRequestTicketRequestCodecs.js +266 -0
- package/dist/CollabRequestTicketResponseCodecs.d.ts +15 -0
- package/dist/CollabRequestTicketResponseCodecs.js +368 -0
- package/dist/CollabTicketReferenceParser.d.ts +24 -0
- package/dist/CollabTicketReferenceParser.js +56 -0
- package/dist/CollabValidation.d.ts +5 -0
- package/dist/CollabValidation.js +26 -0
- package/dist/DevelopmentBootstrap.d.ts +199 -0
- package/dist/DevelopmentBootstrap.js +544 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +67 -0
- package/dist/types.d.ts +138 -0
- package/dist/types.js +11 -0
- package/package.json +66 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeCollabRequestTicketOperationRequest = decodeCollabRequestTicketOperationRequest;
|
|
4
|
+
const CollabConstants_1 = require("./CollabConstants");
|
|
5
|
+
const CollabError_1 = require("./CollabError");
|
|
6
|
+
const CollabValidation_1 = require("./CollabValidation");
|
|
7
|
+
function invalid(reason) {
|
|
8
|
+
return {
|
|
9
|
+
error: new CollabError_1.CollabError({
|
|
10
|
+
code: 'protocol-payload-invalid',
|
|
11
|
+
safeContext: { reason },
|
|
12
|
+
}),
|
|
13
|
+
status: 'invalid',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
function hasExactKeys(value, required, optional = []) {
|
|
20
|
+
const allowed = new Set([...required, ...optional]);
|
|
21
|
+
return required.every(key => Object.hasOwn(value, key))
|
|
22
|
+
&& Object.keys(value).every(key => allowed.has(key));
|
|
23
|
+
}
|
|
24
|
+
function isRevision(value, minimum = 0) {
|
|
25
|
+
return typeof value === 'number'
|
|
26
|
+
&& Number.isSafeInteger(value)
|
|
27
|
+
&& value >= minimum;
|
|
28
|
+
}
|
|
29
|
+
function isPageQuery(value, maxLimit = CollabConstants_1.COLLAB_LIMITS.maxCommentPageSize) {
|
|
30
|
+
return (value.cursor === undefined
|
|
31
|
+
|| (typeof value.cursor === 'string'
|
|
32
|
+
&& value.cursor.length > 0
|
|
33
|
+
&& value.cursor.length <= CollabConstants_1.COLLAB_LIMITS.maxPageCursorUtf16))
|
|
34
|
+
&& (value.limit === undefined
|
|
35
|
+
|| (isRevision(value.limit, 1) && value.limit <= maxLimit));
|
|
36
|
+
}
|
|
37
|
+
function mutationContext(value) {
|
|
38
|
+
return (0, CollabValidation_1.isCollabProjectId)(value.projectId) && (0, CollabValidation_1.isCollabOpaqueId)(value.idempotencyKey);
|
|
39
|
+
}
|
|
40
|
+
function resolvingTickets(value) {
|
|
41
|
+
if (!Array.isArray(value) || value.length > CollabConstants_1.COLLAB_LIMITS.maxRequestTicketRelations)
|
|
42
|
+
return null;
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
const result = [];
|
|
45
|
+
for (const entry of value) {
|
|
46
|
+
if (!isRecord(entry)
|
|
47
|
+
|| !(0, CollabValidation_1.isCollabOpaqueId)(entry.ticketId)
|
|
48
|
+
|| seen.has(entry.ticketId)
|
|
49
|
+
|| !isRevision(entry.revision, 1))
|
|
50
|
+
return null;
|
|
51
|
+
seen.add(entry.ticketId);
|
|
52
|
+
result.push({ ticketId: entry.ticketId, revision: entry.revision });
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
function decodeRequestTicketRequest(operation, input) {
|
|
57
|
+
if (!isRecord(input))
|
|
58
|
+
return null;
|
|
59
|
+
switch (operation) {
|
|
60
|
+
case 'getRequest':
|
|
61
|
+
return (0, CollabValidation_1.isCollabProjectId)(input.projectId) && (0, CollabValidation_1.isCollabOpaqueId)(input.requestId)
|
|
62
|
+
? { projectId: input.projectId, requestId: input.requestId }
|
|
63
|
+
: null;
|
|
64
|
+
case 'ensureMyRequest':
|
|
65
|
+
return mutationContext(input)
|
|
66
|
+
&& (0, CollabValidation_1.isCollabGitOid)(input.expectedMainOid)
|
|
67
|
+
&& (0, CollabValidation_1.isCollabGitOid)(input.headOid)
|
|
68
|
+
&& typeof input.description === 'string'
|
|
69
|
+
&& (0, CollabValidation_1.hasUtf8ByteLengthAtMost)(input.description, CollabConstants_1.COLLAB_LIMITS.maxRequestDescriptionBytes)
|
|
70
|
+
? {
|
|
71
|
+
description: input.description,
|
|
72
|
+
expectedMainOid: input.expectedMainOid,
|
|
73
|
+
headOid: input.headOid,
|
|
74
|
+
idempotencyKey: input.idempotencyKey,
|
|
75
|
+
projectId: input.projectId,
|
|
76
|
+
}
|
|
77
|
+
: null;
|
|
78
|
+
case 'createComment':
|
|
79
|
+
return mutationContext(input)
|
|
80
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.requestId)
|
|
81
|
+
&& typeof input.body === 'string'
|
|
82
|
+
&& (0, CollabValidation_1.hasUtf8ByteLengthAtMost)(input.body, CollabConstants_1.COLLAB_LIMITS.maxCommentBytes)
|
|
83
|
+
&& input.anchor === undefined
|
|
84
|
+
? {
|
|
85
|
+
body: input.body,
|
|
86
|
+
idempotencyKey: input.idempotencyKey,
|
|
87
|
+
projectId: input.projectId,
|
|
88
|
+
requestId: input.requestId,
|
|
89
|
+
}
|
|
90
|
+
: null;
|
|
91
|
+
case 'updateMyRequestMetadata':
|
|
92
|
+
return mutationContext(input)
|
|
93
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.requestId)
|
|
94
|
+
&& (0, CollabValidation_1.isCollabGitOid)(input.expectedHeadOid)
|
|
95
|
+
&& isRevision(input.expectedRequestRevision)
|
|
96
|
+
&& typeof input.description === 'string'
|
|
97
|
+
&& (0, CollabValidation_1.hasUtf8ByteLengthAtMost)(input.description, CollabConstants_1.COLLAB_LIMITS.maxRequestDescriptionBytes)
|
|
98
|
+
? {
|
|
99
|
+
description: input.description,
|
|
100
|
+
expectedHeadOid: input.expectedHeadOid,
|
|
101
|
+
expectedRequestRevision: input.expectedRequestRevision,
|
|
102
|
+
idempotencyKey: input.idempotencyKey,
|
|
103
|
+
projectId: input.projectId,
|
|
104
|
+
requestId: input.requestId,
|
|
105
|
+
}
|
|
106
|
+
: null;
|
|
107
|
+
case 'acceptRequest': {
|
|
108
|
+
const expectedResolvingTickets = resolvingTickets(input.expectedResolvingTickets);
|
|
109
|
+
return mutationContext(input)
|
|
110
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.requestId)
|
|
111
|
+
&& (0, CollabValidation_1.isCollabGitOid)(input.expectedMainOid)
|
|
112
|
+
&& (0, CollabValidation_1.isCollabGitOid)(input.expectedHeadOid)
|
|
113
|
+
&& isRevision(input.expectedRequestRevision)
|
|
114
|
+
&& expectedResolvingTickets !== null
|
|
115
|
+
? {
|
|
116
|
+
expectedHeadOid: input.expectedHeadOid,
|
|
117
|
+
expectedMainOid: input.expectedMainOid,
|
|
118
|
+
expectedRequestRevision: input.expectedRequestRevision,
|
|
119
|
+
expectedResolvingTickets,
|
|
120
|
+
idempotencyKey: input.idempotencyKey,
|
|
121
|
+
projectId: input.projectId,
|
|
122
|
+
requestId: input.requestId,
|
|
123
|
+
}
|
|
124
|
+
: null;
|
|
125
|
+
}
|
|
126
|
+
case 'listTickets':
|
|
127
|
+
return hasExactKeys(input, ['projectId', 'status'], ['cursor', 'limit'])
|
|
128
|
+
&& (0, CollabValidation_1.isCollabProjectId)(input.projectId)
|
|
129
|
+
&& (input.status === 'open' || input.status === 'closed' || input.status === 'all')
|
|
130
|
+
&& isPageQuery(input, CollabConstants_1.COLLAB_LIMITS.maxTicketPageSize)
|
|
131
|
+
? {
|
|
132
|
+
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
133
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
134
|
+
projectId: input.projectId,
|
|
135
|
+
status: input.status,
|
|
136
|
+
}
|
|
137
|
+
: null;
|
|
138
|
+
case 'listRequestComments':
|
|
139
|
+
return hasExactKeys(input, ['projectId', 'requestId'], ['cursor', 'limit'])
|
|
140
|
+
&& (0, CollabValidation_1.isCollabProjectId)(input.projectId)
|
|
141
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.requestId)
|
|
142
|
+
&& isPageQuery(input)
|
|
143
|
+
? {
|
|
144
|
+
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
145
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
146
|
+
projectId: input.projectId,
|
|
147
|
+
requestId: input.requestId,
|
|
148
|
+
}
|
|
149
|
+
: null;
|
|
150
|
+
case 'listTicketComments':
|
|
151
|
+
return hasExactKeys(input, ['projectId', 'ticketId'], ['cursor', 'limit'])
|
|
152
|
+
&& (0, CollabValidation_1.isCollabProjectId)(input.projectId)
|
|
153
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.ticketId)
|
|
154
|
+
&& isPageQuery(input)
|
|
155
|
+
? {
|
|
156
|
+
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
157
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
158
|
+
projectId: input.projectId,
|
|
159
|
+
ticketId: input.ticketId,
|
|
160
|
+
}
|
|
161
|
+
: null;
|
|
162
|
+
case 'listTicketAcceptedRelations':
|
|
163
|
+
return hasExactKeys(input, ['projectId', 'ticketId'], ['cursor', 'limit'])
|
|
164
|
+
&& (0, CollabValidation_1.isCollabProjectId)(input.projectId)
|
|
165
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.ticketId)
|
|
166
|
+
&& isPageQuery(input, CollabConstants_1.COLLAB_LIMITS.maxRelationsPerPage)
|
|
167
|
+
? {
|
|
168
|
+
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
169
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
170
|
+
projectId: input.projectId,
|
|
171
|
+
ticketId: input.ticketId,
|
|
172
|
+
}
|
|
173
|
+
: null;
|
|
174
|
+
case 'getTicket':
|
|
175
|
+
return hasExactKeys(input, ['projectId', 'ticketId'])
|
|
176
|
+
&& (0, CollabValidation_1.isCollabProjectId)(input.projectId)
|
|
177
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.ticketId)
|
|
178
|
+
? { projectId: input.projectId, ticketId: input.ticketId }
|
|
179
|
+
: null;
|
|
180
|
+
case 'createTicket':
|
|
181
|
+
return hasExactKeys(input, ['body', 'idempotencyKey', 'projectId', 'title'])
|
|
182
|
+
&& mutationContext(input)
|
|
183
|
+
&& typeof input.body === 'string'
|
|
184
|
+
&& typeof input.title === 'string'
|
|
185
|
+
&& (0, CollabValidation_1.hasUtf8ByteLengthAtMost)(input.body, CollabConstants_1.COLLAB_LIMITS.maxTicketBodyBytes)
|
|
186
|
+
&& input.title.length <= CollabConstants_1.COLLAB_LIMITS.maxTicketTitleUtf16
|
|
187
|
+
? {
|
|
188
|
+
body: input.body,
|
|
189
|
+
idempotencyKey: input.idempotencyKey,
|
|
190
|
+
projectId: input.projectId,
|
|
191
|
+
title: input.title,
|
|
192
|
+
}
|
|
193
|
+
: null;
|
|
194
|
+
case 'updateTicketContent':
|
|
195
|
+
return hasExactKeys(input, [
|
|
196
|
+
'body', 'expectedRevision', 'idempotencyKey', 'projectId', 'ticketId', 'title',
|
|
197
|
+
])
|
|
198
|
+
&& mutationContext(input)
|
|
199
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.ticketId)
|
|
200
|
+
&& isRevision(input.expectedRevision, 1)
|
|
201
|
+
&& typeof input.body === 'string'
|
|
202
|
+
&& typeof input.title === 'string'
|
|
203
|
+
&& (0, CollabValidation_1.hasUtf8ByteLengthAtMost)(input.body, CollabConstants_1.COLLAB_LIMITS.maxTicketBodyBytes)
|
|
204
|
+
&& input.title.length <= CollabConstants_1.COLLAB_LIMITS.maxTicketTitleUtf16
|
|
205
|
+
? {
|
|
206
|
+
body: input.body,
|
|
207
|
+
expectedRevision: input.expectedRevision,
|
|
208
|
+
idempotencyKey: input.idempotencyKey,
|
|
209
|
+
projectId: input.projectId,
|
|
210
|
+
ticketId: input.ticketId,
|
|
211
|
+
title: input.title,
|
|
212
|
+
}
|
|
213
|
+
: null;
|
|
214
|
+
case 'createTicketComment':
|
|
215
|
+
return hasExactKeys(input, ['body', 'idempotencyKey', 'projectId', 'ticketId'])
|
|
216
|
+
&& mutationContext(input)
|
|
217
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.ticketId)
|
|
218
|
+
&& typeof input.body === 'string'
|
|
219
|
+
&& (0, CollabValidation_1.hasUtf8ByteLengthAtMost)(input.body, CollabConstants_1.COLLAB_LIMITS.maxTicketCommentBytes)
|
|
220
|
+
? {
|
|
221
|
+
body: input.body,
|
|
222
|
+
idempotencyKey: input.idempotencyKey,
|
|
223
|
+
projectId: input.projectId,
|
|
224
|
+
ticketId: input.ticketId,
|
|
225
|
+
}
|
|
226
|
+
: null;
|
|
227
|
+
case 'closeTicket':
|
|
228
|
+
case 'reopenTicket':
|
|
229
|
+
return hasExactKeys(input, [
|
|
230
|
+
'expectedRevision', 'idempotencyKey', 'projectId', 'ticketId',
|
|
231
|
+
])
|
|
232
|
+
&& mutationContext(input)
|
|
233
|
+
&& (0, CollabValidation_1.isCollabOpaqueId)(input.ticketId)
|
|
234
|
+
&& isRevision(input.expectedRevision, 1)
|
|
235
|
+
? {
|
|
236
|
+
expectedRevision: input.expectedRevision,
|
|
237
|
+
idempotencyKey: input.idempotencyKey,
|
|
238
|
+
projectId: input.projectId,
|
|
239
|
+
ticketId: input.ticketId,
|
|
240
|
+
}
|
|
241
|
+
: null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const INVALID_REASONS = {
|
|
245
|
+
acceptRequest: 'request-accept-payload-invalid',
|
|
246
|
+
closeTicket: 'ticket-mutation-payload-invalid',
|
|
247
|
+
createComment: 'request-comment-payload-invalid',
|
|
248
|
+
createTicket: 'ticket-create-payload-invalid',
|
|
249
|
+
createTicketComment: 'ticket-comment-payload-invalid',
|
|
250
|
+
ensureMyRequest: 'request-ensure-payload-invalid',
|
|
251
|
+
getRequest: 'request-read-payload-invalid',
|
|
252
|
+
getTicket: 'ticket-read-payload-invalid',
|
|
253
|
+
listRequestComments: 'request-comment-page-query-invalid',
|
|
254
|
+
listTicketAcceptedRelations: 'ticket-relation-page-query-invalid',
|
|
255
|
+
listTicketComments: 'ticket-comment-page-query-invalid',
|
|
256
|
+
listTickets: 'ticket-list-query-invalid',
|
|
257
|
+
reopenTicket: 'ticket-mutation-payload-invalid',
|
|
258
|
+
updateMyRequestMetadata: 'request-metadata-payload-invalid',
|
|
259
|
+
updateTicketContent: 'ticket-content-payload-invalid',
|
|
260
|
+
};
|
|
261
|
+
function decodeCollabRequestTicketOperationRequest(operation, input) {
|
|
262
|
+
const value = decodeRequestTicketRequest(operation, input);
|
|
263
|
+
return value
|
|
264
|
+
? { status: 'ok', value: value }
|
|
265
|
+
: invalid(INVALID_REASONS[operation]);
|
|
266
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type AcceptResponse, type CreateCommentResponse, type CreateTicketCommentResponse, type CreateTicketResponse, type EnsureMyRequestResponse, type TicketMutationResponse, type UpdateMyRequestMetadataResponse } from './CollabProtocol';
|
|
2
|
+
import { type CollabCommentPage, type CollabRequestDetail, type CollabTicketAcceptedRelationPage, type CollabTicketCommentPage, type CollabTicketDetail, type CollabTicketPage } from './types';
|
|
3
|
+
export declare function decodeEnsureMyRequestResponse(value: unknown): EnsureMyRequestResponse;
|
|
4
|
+
export declare function decodeRequestDetailResponse(value: unknown): CollabRequestDetail;
|
|
5
|
+
export declare function decodeCommentPageResponse(value: unknown): CollabCommentPage;
|
|
6
|
+
export declare function decodeTicketCommentPageResponse(value: unknown): CollabTicketCommentPage;
|
|
7
|
+
export declare function decodeTicketAcceptedRelationPageResponse(value: unknown): CollabTicketAcceptedRelationPage;
|
|
8
|
+
export declare function decodeCreateCommentResponse(value: unknown): CreateCommentResponse;
|
|
9
|
+
export declare function decodeAcceptResponse(value: unknown): AcceptResponse;
|
|
10
|
+
export declare function decodeTicketPageResponse(value: unknown): CollabTicketPage;
|
|
11
|
+
export declare function decodeTicketDetailResponse(value: unknown): CollabTicketDetail;
|
|
12
|
+
export declare function decodeCreateTicketResponse(value: unknown): CreateTicketResponse;
|
|
13
|
+
export declare function decodeTicketMutationResponse(value: unknown): TicketMutationResponse;
|
|
14
|
+
export declare function decodeTicketCommentResponse(value: unknown): CreateTicketCommentResponse;
|
|
15
|
+
export declare function decodeUpdateRequestMetadataResponse(value: unknown): UpdateMyRequestMetadataResponse;
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeEnsureMyRequestResponse = decodeEnsureMyRequestResponse;
|
|
4
|
+
exports.decodeRequestDetailResponse = decodeRequestDetailResponse;
|
|
5
|
+
exports.decodeCommentPageResponse = decodeCommentPageResponse;
|
|
6
|
+
exports.decodeTicketCommentPageResponse = decodeTicketCommentPageResponse;
|
|
7
|
+
exports.decodeTicketAcceptedRelationPageResponse = decodeTicketAcceptedRelationPageResponse;
|
|
8
|
+
exports.decodeCreateCommentResponse = decodeCreateCommentResponse;
|
|
9
|
+
exports.decodeAcceptResponse = decodeAcceptResponse;
|
|
10
|
+
exports.decodeTicketPageResponse = decodeTicketPageResponse;
|
|
11
|
+
exports.decodeTicketDetailResponse = decodeTicketDetailResponse;
|
|
12
|
+
exports.decodeCreateTicketResponse = decodeCreateTicketResponse;
|
|
13
|
+
exports.decodeTicketMutationResponse = decodeTicketMutationResponse;
|
|
14
|
+
exports.decodeTicketCommentResponse = decodeTicketCommentResponse;
|
|
15
|
+
exports.decodeUpdateRequestMetadataResponse = decodeUpdateRequestMetadataResponse;
|
|
16
|
+
const CollabConstants_1 = require("./CollabConstants");
|
|
17
|
+
const CollabError_1 = require("./CollabError");
|
|
18
|
+
const CollabValidation_1 = require("./CollabValidation");
|
|
19
|
+
function decodeError(field) {
|
|
20
|
+
return new CollabError_1.CollabError({
|
|
21
|
+
code: 'protocol-payload-invalid',
|
|
22
|
+
recoveryActions: ['retry'],
|
|
23
|
+
safeContext: { field },
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function isRecord(value) {
|
|
27
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
28
|
+
}
|
|
29
|
+
function record(value, field) {
|
|
30
|
+
if (!isRecord(value))
|
|
31
|
+
throw decodeError(field);
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
function assertJsonUtf8ByteLengthAtMost(value, maximum, field) {
|
|
35
|
+
let serialized;
|
|
36
|
+
try {
|
|
37
|
+
serialized = JSON.stringify(value);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw decodeError(field);
|
|
41
|
+
}
|
|
42
|
+
if (serialized === undefined
|
|
43
|
+
|| !(0, CollabValidation_1.hasUtf8ByteLengthAtMost)(serialized, maximum)) {
|
|
44
|
+
throw decodeError(field);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function string(value, field, maxLength, validate, unit = 'utf16') {
|
|
48
|
+
const candidate = value[field];
|
|
49
|
+
if (typeof candidate !== 'string'
|
|
50
|
+
|| candidate.length === 0
|
|
51
|
+
|| (unit === 'utf8'
|
|
52
|
+
? !(0, CollabValidation_1.hasUtf8ByteLengthAtMost)(candidate, maxLength)
|
|
53
|
+
: candidate.length > maxLength)
|
|
54
|
+
|| (validate && !validate(candidate))) {
|
|
55
|
+
throw decodeError(field);
|
|
56
|
+
}
|
|
57
|
+
return candidate;
|
|
58
|
+
}
|
|
59
|
+
function text(value, field, maxLength, unit = 'utf16') {
|
|
60
|
+
const candidate = value[field];
|
|
61
|
+
if (typeof candidate !== 'string'
|
|
62
|
+
|| (unit === 'utf8'
|
|
63
|
+
? !(0, CollabValidation_1.hasUtf8ByteLengthAtMost)(candidate, maxLength)
|
|
64
|
+
: candidate.length > maxLength))
|
|
65
|
+
throw decodeError(field);
|
|
66
|
+
return candidate;
|
|
67
|
+
}
|
|
68
|
+
function timestamp(value, field) {
|
|
69
|
+
const candidate = string(value, field, 64);
|
|
70
|
+
if (Number.isNaN(Date.parse(candidate)) || new Date(candidate).toISOString() !== candidate) {
|
|
71
|
+
throw decodeError(field);
|
|
72
|
+
}
|
|
73
|
+
return candidate;
|
|
74
|
+
}
|
|
75
|
+
function optionalTimestamp(value, field) {
|
|
76
|
+
return value[field] === undefined ? undefined : timestamp(value, field);
|
|
77
|
+
}
|
|
78
|
+
function nonNegativeInteger(value, field) {
|
|
79
|
+
const candidate = value[field];
|
|
80
|
+
if (typeof candidate !== 'number'
|
|
81
|
+
|| !Number.isSafeInteger(candidate)
|
|
82
|
+
|| candidate < 0) {
|
|
83
|
+
throw decodeError(field);
|
|
84
|
+
}
|
|
85
|
+
return candidate;
|
|
86
|
+
}
|
|
87
|
+
function boundedNonNegativeInteger(value, field, maximum) {
|
|
88
|
+
const candidate = nonNegativeInteger(value, field);
|
|
89
|
+
if (candidate > maximum)
|
|
90
|
+
throw decodeError(field);
|
|
91
|
+
return candidate;
|
|
92
|
+
}
|
|
93
|
+
function positiveInteger(value, field) {
|
|
94
|
+
const candidate = nonNegativeInteger(value, field);
|
|
95
|
+
if (candidate < 1)
|
|
96
|
+
throw decodeError(field);
|
|
97
|
+
return candidate;
|
|
98
|
+
}
|
|
99
|
+
function requestTicketRelation(value) {
|
|
100
|
+
const source = record(value, 'request.ticketRelations');
|
|
101
|
+
const kind = source.kind;
|
|
102
|
+
const state = source.state;
|
|
103
|
+
if ((kind !== 'references' && kind !== 'resolves')
|
|
104
|
+
|| (state !== 'pending' && state !== 'accepted')) {
|
|
105
|
+
throw decodeError('request.ticketRelations');
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
commitOid: string(source, 'commitOid', 64, CollabValidation_1.isCollabGitOid),
|
|
109
|
+
id: string(source, 'id', 128, CollabValidation_1.isCollabOpaqueId),
|
|
110
|
+
kind,
|
|
111
|
+
state,
|
|
112
|
+
ticketId: string(source, 'ticketId', 128, CollabValidation_1.isCollabOpaqueId),
|
|
113
|
+
ticketNumber: positiveInteger(source, 'ticketNumber'),
|
|
114
|
+
ticketRevision: positiveInteger(source, 'ticketRevision'),
|
|
115
|
+
ticketTitle: string(source, 'ticketTitle', CollabConstants_1.COLLAB_LIMITS.maxTicketTitleUtf16),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function changeRequest(value) {
|
|
119
|
+
const source = record(value, 'request');
|
|
120
|
+
const status = source.status;
|
|
121
|
+
if ((status !== 'open' && status !== 'merged' && status !== 'discarded')
|
|
122
|
+
|| !Array.isArray(source.ticketRelations)
|
|
123
|
+
|| source.ticketRelations.length > CollabConstants_1.COLLAB_LIMITS.maxRequestTicketRelations) {
|
|
124
|
+
throw decodeError('request.status');
|
|
125
|
+
}
|
|
126
|
+
const mergedOid = source.mergedOid === undefined
|
|
127
|
+
? undefined
|
|
128
|
+
: string(source, 'mergedOid', 64, CollabValidation_1.isCollabGitOid);
|
|
129
|
+
return {
|
|
130
|
+
commentCount: boundedNonNegativeInteger(source, 'commentCount', CollabConstants_1.COLLAB_LIMITS.maxRequestComments),
|
|
131
|
+
createdAt: timestamp(source, 'createdAt'),
|
|
132
|
+
description: text(source, 'description', CollabConstants_1.COLLAB_LIMITS.maxRequestDescriptionBytes, 'utf8'),
|
|
133
|
+
firstBaseOid: string(source, 'firstBaseOid', 64, CollabValidation_1.isCollabGitOid),
|
|
134
|
+
id: string(source, 'id', 128, CollabValidation_1.isCollabOpaqueId),
|
|
135
|
+
latestHeadOid: string(source, 'latestHeadOid', 64, CollabValidation_1.isCollabGitOid),
|
|
136
|
+
memberId: string(source, 'memberId', 64, CollabValidation_1.isCollabMemberId),
|
|
137
|
+
...(mergedOid ? { mergedOid } : {}),
|
|
138
|
+
revision: nonNegativeInteger(source, 'revision'),
|
|
139
|
+
status,
|
|
140
|
+
ticketRelations: source.ticketRelations.map(requestTicketRelation),
|
|
141
|
+
updatedAt: timestamp(source, 'updatedAt'),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function ticketSummary(value) {
|
|
145
|
+
const source = record(value, 'ticket');
|
|
146
|
+
const status = source.status;
|
|
147
|
+
const closedAt = optionalTimestamp(source, 'closedAt');
|
|
148
|
+
const closedByMemberId = source.closedByMemberId === undefined
|
|
149
|
+
? undefined
|
|
150
|
+
: string(source, 'closedByMemberId', 64, CollabValidation_1.isCollabMemberId);
|
|
151
|
+
if ((status !== 'open' && status !== 'closed')
|
|
152
|
+
|| (status === 'open' && (closedAt !== undefined || closedByMemberId !== undefined))
|
|
153
|
+
|| (status === 'closed' && (closedAt === undefined || closedByMemberId === undefined))) {
|
|
154
|
+
throw decodeError('ticket.status');
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
acceptedRelationCount: boundedNonNegativeInteger(source, 'acceptedRelationCount', CollabConstants_1.COLLAB_LIMITS.maxTicketAcceptedRelations),
|
|
158
|
+
authorMemberId: string(source, 'authorMemberId', 64, CollabValidation_1.isCollabMemberId),
|
|
159
|
+
...(closedAt && closedByMemberId ? { closedAt, closedByMemberId } : {}),
|
|
160
|
+
commentCount: boundedNonNegativeInteger(source, 'commentCount', CollabConstants_1.COLLAB_LIMITS.maxTicketComments),
|
|
161
|
+
createdAt: timestamp(source, 'createdAt'),
|
|
162
|
+
id: string(source, 'id', 128, CollabValidation_1.isCollabOpaqueId),
|
|
163
|
+
number: positiveInteger(source, 'number'),
|
|
164
|
+
revision: positiveInteger(source, 'revision'),
|
|
165
|
+
status,
|
|
166
|
+
title: string(source, 'title', CollabConstants_1.COLLAB_LIMITS.maxTicketTitleUtf16),
|
|
167
|
+
updatedAt: timestamp(source, 'updatedAt'),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function ticketComment(value) {
|
|
171
|
+
const source = record(value, 'ticket.comment');
|
|
172
|
+
return {
|
|
173
|
+
authorMemberId: string(source, 'authorMemberId', 64, CollabValidation_1.isCollabMemberId),
|
|
174
|
+
body: string(source, 'body', CollabConstants_1.COLLAB_LIMITS.maxTicketCommentBytes, undefined, 'utf8'),
|
|
175
|
+
createdAt: timestamp(source, 'createdAt'),
|
|
176
|
+
id: string(source, 'id', 128, CollabValidation_1.isCollabOpaqueId),
|
|
177
|
+
ticketId: string(source, 'ticketId', 128, CollabValidation_1.isCollabOpaqueId),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function acceptedTicketRelation(value) {
|
|
181
|
+
const source = record(value, 'ticket.acceptedRelation');
|
|
182
|
+
const kind = source.kind;
|
|
183
|
+
if (kind !== 'references' && kind !== 'resolves') {
|
|
184
|
+
throw decodeError('ticket.acceptedRelation.kind');
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
acceptedAt: timestamp(source, 'acceptedAt'),
|
|
188
|
+
acceptedMergeOid: string(source, 'acceptedMergeOid', 64, CollabValidation_1.isCollabGitOid),
|
|
189
|
+
commitOid: string(source, 'commitOid', 64, CollabValidation_1.isCollabGitOid),
|
|
190
|
+
id: string(source, 'id', 128, CollabValidation_1.isCollabOpaqueId),
|
|
191
|
+
kind,
|
|
192
|
+
requestId: string(source, 'requestId', 128, CollabValidation_1.isCollabOpaqueId),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function pageCursor(source) {
|
|
196
|
+
return source.nextCursor === undefined
|
|
197
|
+
? undefined
|
|
198
|
+
: string(source, 'nextCursor', CollabConstants_1.COLLAB_LIMITS.maxPageCursorUtf16);
|
|
199
|
+
}
|
|
200
|
+
function commentPage(value) {
|
|
201
|
+
const source = record(value, 'commentPage');
|
|
202
|
+
assertJsonUtf8ByteLengthAtMost(source, CollabConstants_1.COLLAB_LIMITS.commentPageMaxUtf8Bytes, 'commentPage.bytes');
|
|
203
|
+
if (!Array.isArray(source.comments)
|
|
204
|
+
|| source.comments.length > CollabConstants_1.COLLAB_LIMITS.maxCommentPageSize) {
|
|
205
|
+
throw decodeError('commentPage');
|
|
206
|
+
}
|
|
207
|
+
const nextCursor = pageCursor(source);
|
|
208
|
+
return {
|
|
209
|
+
comments: source.comments.map(comment),
|
|
210
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function ticketCommentPage(value) {
|
|
214
|
+
const source = record(value, 'ticketCommentPage');
|
|
215
|
+
assertJsonUtf8ByteLengthAtMost(source, CollabConstants_1.COLLAB_LIMITS.commentPageMaxUtf8Bytes, 'ticketCommentPage.bytes');
|
|
216
|
+
if (!Array.isArray(source.comments)
|
|
217
|
+
|| source.comments.length > CollabConstants_1.COLLAB_LIMITS.maxCommentPageSize) {
|
|
218
|
+
throw decodeError('ticketCommentPage');
|
|
219
|
+
}
|
|
220
|
+
const nextCursor = pageCursor(source);
|
|
221
|
+
return {
|
|
222
|
+
comments: source.comments.map(ticketComment),
|
|
223
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function acceptedRelationPage(value) {
|
|
227
|
+
const source = record(value, 'ticketAcceptedRelationPage');
|
|
228
|
+
assertJsonUtf8ByteLengthAtMost(source, CollabConstants_1.COLLAB_LIMITS.relationPageMaxUtf8Bytes, 'ticketAcceptedRelationPage.bytes');
|
|
229
|
+
if (!Array.isArray(source.acceptedRelations)
|
|
230
|
+
|| source.acceptedRelations.length > CollabConstants_1.COLLAB_LIMITS.maxRelationsPerPage) {
|
|
231
|
+
throw decodeError('ticketAcceptedRelationPage');
|
|
232
|
+
}
|
|
233
|
+
const nextCursor = pageCursor(source);
|
|
234
|
+
return {
|
|
235
|
+
acceptedRelations: source.acceptedRelations.map(acceptedTicketRelation),
|
|
236
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function ticketDetail(value) {
|
|
240
|
+
const source = record(value, 'ticketDetail');
|
|
241
|
+
assertJsonUtf8ByteLengthAtMost(source, CollabConstants_1.COLLAB_LIMITS.detailMaxUtf8Bytes, 'ticketDetail.bytes');
|
|
242
|
+
if (!isRecord(source.comments)
|
|
243
|
+
|| !isRecord(source.acceptedRelations)) {
|
|
244
|
+
throw decodeError('ticketDetail');
|
|
245
|
+
}
|
|
246
|
+
const decodedTicket = ticketSummary(source.ticket);
|
|
247
|
+
const comments = ticketCommentPage(source.comments);
|
|
248
|
+
if (comments.comments.some(commentValue => commentValue.ticketId !== decodedTicket.id)) {
|
|
249
|
+
throw decodeError('ticketDetail.comments');
|
|
250
|
+
}
|
|
251
|
+
const acceptedRelations = acceptedRelationPage(source.acceptedRelations);
|
|
252
|
+
return {
|
|
253
|
+
acceptedRelations,
|
|
254
|
+
body: string(source, 'body', CollabConstants_1.COLLAB_LIMITS.maxTicketBodyBytes, undefined, 'utf8'),
|
|
255
|
+
comments,
|
|
256
|
+
ticket: decodedTicket,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function comment(value) {
|
|
260
|
+
const source = record(value, 'comment');
|
|
261
|
+
return {
|
|
262
|
+
authorMemberId: string(source, 'authorMemberId', 64, CollabValidation_1.isCollabMemberId),
|
|
263
|
+
body: string(source, 'body', CollabConstants_1.COLLAB_LIMITS.maxCommentBytes, undefined, 'utf8'),
|
|
264
|
+
createdAt: timestamp(source, 'createdAt'),
|
|
265
|
+
id: string(source, 'id', 128, CollabValidation_1.isCollabOpaqueId),
|
|
266
|
+
requestId: string(source, 'requestId', 128, CollabValidation_1.isCollabOpaqueId),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function envelopeData(value) {
|
|
270
|
+
return value;
|
|
271
|
+
}
|
|
272
|
+
function decodeEnsureMyRequestResponse(value) {
|
|
273
|
+
const data = record(envelopeData(value), 'data');
|
|
274
|
+
return {
|
|
275
|
+
mainOid: string(data, 'mainOid', 64, CollabValidation_1.isCollabGitOid),
|
|
276
|
+
request: changeRequest(data.request),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function decodeRequestDetailResponse(value) {
|
|
280
|
+
const data = record(envelopeData(value), 'data');
|
|
281
|
+
assertJsonUtf8ByteLengthAtMost(data, CollabConstants_1.COLLAB_LIMITS.detailMaxUtf8Bytes, 'requestDetail.bytes');
|
|
282
|
+
if (!isRecord(data.comments)
|
|
283
|
+
|| data.changedFiles !== undefined) {
|
|
284
|
+
throw decodeError('requestDetail');
|
|
285
|
+
}
|
|
286
|
+
const decodedRequest = changeRequest(data.request);
|
|
287
|
+
const reviewedHeadOid = string(data, 'reviewedHeadOid', 64, CollabValidation_1.isCollabGitOid);
|
|
288
|
+
const reviewCondition = data.reviewCondition;
|
|
289
|
+
const comments = commentPage(data.comments);
|
|
290
|
+
if (reviewedHeadOid !== decodedRequest.latestHeadOid
|
|
291
|
+
|| !['clean', 'conflicting', 'stale'].includes(String(reviewCondition))
|
|
292
|
+
|| comments.comments.some(item => item.requestId !== decodedRequest.id)) {
|
|
293
|
+
throw decodeError('requestDetail');
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
comments,
|
|
297
|
+
currentMainOid: string(data, 'currentMainOid', 64, CollabValidation_1.isCollabGitOid),
|
|
298
|
+
request: decodedRequest,
|
|
299
|
+
reviewCondition: reviewCondition,
|
|
300
|
+
reviewedHeadOid,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function decodeCommentPageResponse(value) {
|
|
304
|
+
return commentPage(record(envelopeData(value), 'data'));
|
|
305
|
+
}
|
|
306
|
+
function decodeTicketCommentPageResponse(value) {
|
|
307
|
+
return ticketCommentPage(record(envelopeData(value), 'data'));
|
|
308
|
+
}
|
|
309
|
+
function decodeTicketAcceptedRelationPageResponse(value) {
|
|
310
|
+
return acceptedRelationPage(record(envelopeData(value), 'data'));
|
|
311
|
+
}
|
|
312
|
+
function decodeCreateCommentResponse(value) {
|
|
313
|
+
const data = record(envelopeData(value), 'data');
|
|
314
|
+
const decodedRequest = changeRequest(data.request);
|
|
315
|
+
const decodedComment = comment(data.comment);
|
|
316
|
+
if (decodedComment.requestId !== decodedRequest.id
|
|
317
|
+
|| decodedRequest.commentCount < 1) {
|
|
318
|
+
throw decodeError('commentResponse');
|
|
319
|
+
}
|
|
320
|
+
return { comment: decodedComment, request: decodedRequest };
|
|
321
|
+
}
|
|
322
|
+
function decodeAcceptResponse(value) {
|
|
323
|
+
const data = record(envelopeData(value), 'data');
|
|
324
|
+
const mainOid = string(data, 'mainOid', 64, CollabValidation_1.isCollabGitOid);
|
|
325
|
+
const mergeCommitOid = string(data, 'mergeCommitOid', 64, CollabValidation_1.isCollabGitOid);
|
|
326
|
+
const decodedRequest = changeRequest(data.request);
|
|
327
|
+
if (mergeCommitOid !== mainOid
|
|
328
|
+
|| decodedRequest.status !== 'merged'
|
|
329
|
+
|| decodedRequest.mergedOid !== mainOid) {
|
|
330
|
+
throw decodeError('acceptResponse');
|
|
331
|
+
}
|
|
332
|
+
return { mainOid, mergeCommitOid, request: decodedRequest };
|
|
333
|
+
}
|
|
334
|
+
function decodeTicketPageResponse(value) {
|
|
335
|
+
const data = record(envelopeData(value), 'data');
|
|
336
|
+
assertJsonUtf8ByteLengthAtMost(data, CollabConstants_1.COLLAB_LIMITS.ticketPageMaxUtf8Bytes, 'ticketPage.bytes');
|
|
337
|
+
if (!Array.isArray(data.tickets)
|
|
338
|
+
|| data.tickets.length > CollabConstants_1.COLLAB_LIMITS.maxTicketPageSize)
|
|
339
|
+
throw decodeError('ticketPage');
|
|
340
|
+
const nextCursor = data.nextCursor === undefined
|
|
341
|
+
? undefined
|
|
342
|
+
: string(data, 'nextCursor', 512);
|
|
343
|
+
return {
|
|
344
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
345
|
+
tickets: data.tickets.map(ticketSummary),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function decodeTicketDetailResponse(value) {
|
|
349
|
+
return ticketDetail(envelopeData(value));
|
|
350
|
+
}
|
|
351
|
+
function decodeCreateTicketResponse(value) {
|
|
352
|
+
return { ticket: ticketDetail(record(envelopeData(value), 'data').ticket) };
|
|
353
|
+
}
|
|
354
|
+
function decodeTicketMutationResponse(value) {
|
|
355
|
+
return { ticket: ticketSummary(record(envelopeData(value), 'data').ticket) };
|
|
356
|
+
}
|
|
357
|
+
function decodeTicketCommentResponse(value) {
|
|
358
|
+
const data = record(envelopeData(value), 'data');
|
|
359
|
+
const decodedTicket = ticketSummary(data.ticket);
|
|
360
|
+
const decodedComment = ticketComment(data.comment);
|
|
361
|
+
if (decodedComment.ticketId !== decodedTicket.id) {
|
|
362
|
+
throw decodeError('ticketCommentResponse');
|
|
363
|
+
}
|
|
364
|
+
return { comment: decodedComment, ticket: decodedTicket };
|
|
365
|
+
}
|
|
366
|
+
function decodeUpdateRequestMetadataResponse(value) {
|
|
367
|
+
return { request: changeRequest(record(envelopeData(value), 'data').request) };
|
|
368
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CollabParsedTicketReference, CollabTicketCommitRelationKind } from './types';
|
|
2
|
+
export type CollabTicketReferenceParseFailureReason = 'description-too-large' | 'ticket-number-out-of-range';
|
|
3
|
+
export type CollabTicketReferenceParseResult = {
|
|
4
|
+
status: 'ok';
|
|
5
|
+
references: readonly CollabParsedTicketReference[];
|
|
6
|
+
} | {
|
|
7
|
+
status: 'invalid';
|
|
8
|
+
reason: CollabTicketReferenceParseFailureReason;
|
|
9
|
+
};
|
|
10
|
+
export interface CollabTicketReferenceToken {
|
|
11
|
+
readonly from: number;
|
|
12
|
+
readonly kind: CollabTicketCommitRelationKind;
|
|
13
|
+
readonly ticketNumber: number;
|
|
14
|
+
readonly to: number;
|
|
15
|
+
}
|
|
16
|
+
export type CollabTicketReferenceScanResult = {
|
|
17
|
+
readonly status: 'ok';
|
|
18
|
+
readonly tokens: readonly CollabTicketReferenceToken[];
|
|
19
|
+
} | {
|
|
20
|
+
readonly reason: 'ticket-number-out-of-range';
|
|
21
|
+
readonly status: 'invalid';
|
|
22
|
+
};
|
|
23
|
+
export declare function parseCollabTicketReferences(description: string): CollabTicketReferenceParseResult;
|
|
24
|
+
export declare function scanCollabTicketReferences(description: string): CollabTicketReferenceScanResult;
|