@claudian-collab/protocol 1.0.0 → 2.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.
@@ -1,8 +1,10 @@
1
1
  import { COLLAB_PROTOCOL_VERSION } from './CollabConstants';
2
2
  import { CollabError, type CollabDiagnosticContext, type CollabErrorCode, type CollabRecoveryAction } from './CollabError';
3
- export declare const COLLAB_CLOUD_BINDING_VERSION: 1;
4
- export declare const COLLAB_CLOUD_CAPABILITY_DOCUMENT_SCHEMA_VERSION: 1;
5
- export declare const COLLAB_CLOUD_CAPABILITIES: readonly ["accept", "development-bootstrap", "git-receive-pack-personal-ref", "git-upload-pack", "project-events", "project-snapshot", "requests", "tickets"];
3
+ import { COLLAB_PROJECT_CHECKPOINT_ARTIFACTS } from './CollabProjectCheckpoint';
4
+ import type { CollabIsoTimestamp, CollabProjectId } from './types';
5
+ export declare const COLLAB_CLOUD_BINDING_VERSION: 2;
6
+ export declare const COLLAB_CLOUD_CAPABILITY_DOCUMENT_SCHEMA_VERSION: 2;
7
+ export declare const COLLAB_CLOUD_CAPABILITIES: readonly ["accept", "authority-transfer", "development-bootstrap", "git-receive-pack-personal-ref", "git-upload-pack", "project-checkpoint-export", "project-events", "project-retirement", "project-snapshot", "requests", "tickets"];
6
8
  export type CollabCloudCapability = typeof COLLAB_CLOUD_CAPABILITIES[number];
7
9
  export declare const COLLAB_CLOUD_JSON_OPERATIONS: readonly ["getProjectSnapshot", ...(keyof import("./CollabProtocol").CollabControlOperationMap)[]];
8
10
  export type CollabCloudJsonOperation = typeof COLLAB_CLOUD_JSON_OPERATIONS[number];
@@ -15,6 +17,10 @@ export declare const COLLAB_CLOUD_BINDING_LIMITS: Readonly<{
15
17
  readonly maxCloudProjectMembers: 100;
16
18
  readonly maxCloudSnapshotUtf8Bytes: number;
17
19
  readonly maxCloudTicketHighlights: 5;
20
+ readonly maxCheckpointCoordinationBytes: number;
21
+ readonly maxCheckpointManifestUtf8Bytes: number;
22
+ readonly maxCheckpointRepositoryBundleBytes: number;
23
+ readonly maxCheckpointStagingBytes: number;
18
24
  readonly maxDevelopmentBootstrapGitBundleBytes: number;
19
25
  readonly maxDevelopmentBootstrapManifestUtf8Bytes: number;
20
26
  readonly maxDevelopmentBootstrapReportUtf8Bytes: number;
@@ -30,6 +36,10 @@ export declare const COLLAB_CLOUD_BINDING_LIMITS: Readonly<{
30
36
  readonly uploadIdleTimeoutMs: 30000;
31
37
  }>;
32
38
  export interface CollabCloudCapabilityLimits {
39
+ readonly maxCheckpointCoordinationBytes: number;
40
+ readonly maxCheckpointManifestUtf8Bytes: number;
41
+ readonly maxCheckpointRepositoryBundleBytes: number;
42
+ readonly maxCheckpointStagingBytes: number;
33
43
  readonly maxDevelopmentBootstrapGitBundleBytes: number;
34
44
  readonly maxDevelopmentBootstrapManifestUtf8Bytes: number;
35
45
  readonly maxDevelopmentBootstrapReportUtf8Bytes: number;
@@ -47,6 +57,16 @@ export interface CollabCloudCapabilityDocument {
47
57
  }
48
58
  export type DevelopmentBootstrapOperation = 'beginDevelopmentBootstrap' | 'submitDevelopmentBootstrapReport' | 'getDevelopmentBootstrap' | 'activateDevelopmentBootstrap' | 'cancelDevelopmentBootstrap' | 'putDevelopmentBootstrapGitBundle';
49
59
  export type CollabCloudGitService = 'git-upload-pack' | 'git-receive-pack';
60
+ export type CollabCloudAuthorityTransferArtifact = typeof COLLAB_PROJECT_CHECKPOINT_ARTIFACTS[number];
61
+ export type CollabCloudAuthorityTransferArtifactDirection = 'download' | 'upload';
62
+ export interface CollabCloudProjectCheckpointExportStatus {
63
+ readonly checkpointSha256: string | null;
64
+ readonly createdAt: CollabIsoTimestamp;
65
+ readonly expiresAt: CollabIsoTimestamp;
66
+ readonly exportId: string;
67
+ readonly projectId: CollabProjectId;
68
+ readonly state: 'preparing' | 'ready';
69
+ }
50
70
  export type CollabCloudRouteMatch = {
51
71
  readonly kind: 'capabilities';
52
72
  } | {
@@ -64,6 +84,22 @@ export type CollabCloudRouteMatch = {
64
84
  } | {
65
85
  readonly kind: 'git-upload-pack' | 'git-receive-pack';
66
86
  readonly projectId: string;
87
+ } | {
88
+ readonly artifact: CollabCloudAuthorityTransferArtifact;
89
+ readonly direction: CollabCloudAuthorityTransferArtifactDirection;
90
+ readonly kind: 'authority-transfer-artifact';
91
+ readonly projectId: string;
92
+ readonly transferId: string;
93
+ } | {
94
+ readonly exportId: string;
95
+ readonly kind: 'project-checkpoint-export';
96
+ readonly operation: 'begin' | 'status';
97
+ readonly projectId: string;
98
+ } | {
99
+ readonly artifact: CollabCloudAuthorityTransferArtifact;
100
+ readonly exportId: string;
101
+ readonly kind: 'project-checkpoint-export-artifact';
102
+ readonly projectId: string;
67
103
  } | {
68
104
  readonly attemptId?: string;
69
105
  readonly kind: 'development-bootstrap';
@@ -94,6 +130,10 @@ export declare function collabCloudProjectOperationRoute(projectId: string, oper
94
130
  export declare function collabCloudProjectEventsRoute(projectId: string, afterSequence: number): CollabCloudRoute;
95
131
  export declare function collabCloudGitRoute(projectId: string, routeKind: 'info-refs', service: CollabCloudGitService): CollabCloudRoute;
96
132
  export declare function collabCloudGitRoute(projectId: string, routeKind: CollabCloudGitService): CollabCloudRoute;
133
+ export declare function collabCloudAuthorityTransferArtifactRoute(projectId: string, transferId: string, direction: CollabCloudAuthorityTransferArtifactDirection, artifact: CollabCloudAuthorityTransferArtifact): CollabCloudRoute;
134
+ export declare function collabCloudProjectCheckpointExportArtifactRoute(projectId: string, exportId: string, artifact: CollabCloudAuthorityTransferArtifact): CollabCloudRoute;
135
+ export declare function collabCloudProjectCheckpointExportRoute(projectId: string, exportId: string, operation: 'begin' | 'status'): CollabCloudRoute;
136
+ export declare function decodeCollabCloudProjectCheckpointExportStatus(value: unknown): CollabCloudProjectCheckpointExportStatus;
97
137
  export declare function collabDevelopmentBootstrapRoute(operation: 'beginDevelopmentBootstrap'): CollabCloudRoute;
98
138
  export declare function collabDevelopmentBootstrapRoute(operation: Exclude<DevelopmentBootstrapOperation, 'beginDevelopmentBootstrap'>, attemptId: string): CollabCloudRoute;
99
139
  export declare function matchCollabCloudRoute(method: string, target: string): CollabCloudRouteMatch | null;
@@ -5,6 +5,10 @@ exports.collabCloudCapabilitiesRoute = collabCloudCapabilitiesRoute;
5
5
  exports.collabCloudProjectOperationRoute = collabCloudProjectOperationRoute;
6
6
  exports.collabCloudProjectEventsRoute = collabCloudProjectEventsRoute;
7
7
  exports.collabCloudGitRoute = collabCloudGitRoute;
8
+ exports.collabCloudAuthorityTransferArtifactRoute = collabCloudAuthorityTransferArtifactRoute;
9
+ exports.collabCloudProjectCheckpointExportArtifactRoute = collabCloudProjectCheckpointExportArtifactRoute;
10
+ exports.collabCloudProjectCheckpointExportRoute = collabCloudProjectCheckpointExportRoute;
11
+ exports.decodeCollabCloudProjectCheckpointExportStatus = decodeCollabCloudProjectCheckpointExportStatus;
8
12
  exports.collabDevelopmentBootstrapRoute = collabDevelopmentBootstrapRoute;
9
13
  exports.matchCollabCloudRoute = matchCollabCloudRoute;
10
14
  exports.decodeCollabCloudCapabilityDocument = decodeCollabCloudCapabilityDocument;
@@ -18,14 +22,18 @@ const CollabConstants_1 = require("./CollabConstants");
18
22
  const CollabControlOperationCodecs_1 = require("./CollabControlOperationCodecs");
19
23
  const CollabError_1 = require("./CollabError");
20
24
  const CollabValidation_1 = require("./CollabValidation");
21
- exports.COLLAB_CLOUD_BINDING_VERSION = 1;
22
- exports.COLLAB_CLOUD_CAPABILITY_DOCUMENT_SCHEMA_VERSION = 1;
25
+ const CollabProjectCheckpoint_1 = require("./CollabProjectCheckpoint");
26
+ exports.COLLAB_CLOUD_BINDING_VERSION = 2;
27
+ exports.COLLAB_CLOUD_CAPABILITY_DOCUMENT_SCHEMA_VERSION = 2;
23
28
  exports.COLLAB_CLOUD_CAPABILITIES = Object.freeze([
24
29
  'accept',
30
+ 'authority-transfer',
25
31
  'development-bootstrap',
26
32
  'git-receive-pack-personal-ref',
27
33
  'git-upload-pack',
34
+ 'project-checkpoint-export',
28
35
  'project-events',
36
+ 'project-retirement',
29
37
  'project-snapshot',
30
38
  'requests',
31
39
  'tickets',
@@ -43,6 +51,10 @@ exports.COLLAB_CLOUD_BINDING_LIMITS = Object.freeze({
43
51
  maxCloudProjectMembers: 100,
44
52
  maxCloudSnapshotUtf8Bytes: 448 * 1024,
45
53
  maxCloudTicketHighlights: 5,
54
+ maxCheckpointCoordinationBytes: CollabProjectCheckpoint_1.COLLAB_CHECKPOINT_ARTIFACT_LIMITS.maxCoordinationBytes,
55
+ maxCheckpointManifestUtf8Bytes: CollabProjectCheckpoint_1.COLLAB_CHECKPOINT_ARTIFACT_LIMITS.maxManifestBytes,
56
+ maxCheckpointRepositoryBundleBytes: CollabProjectCheckpoint_1.COLLAB_CHECKPOINT_ARTIFACT_LIMITS.maxRepositoryBundleBytes,
57
+ maxCheckpointStagingBytes: CollabProjectCheckpoint_1.COLLAB_CHECKPOINT_ARTIFACT_LIMITS.maxStagingBytes,
46
58
  maxDevelopmentBootstrapGitBundleBytes: 1024 * 1024 * 1024,
47
59
  maxDevelopmentBootstrapManifestUtf8Bytes: 64 * 1024,
48
60
  maxDevelopmentBootstrapReportUtf8Bytes: 64 * 1024,
@@ -59,6 +71,7 @@ exports.COLLAB_CLOUD_BINDING_LIMITS = Object.freeze({
59
71
  });
60
72
  const CLOUD_CAPABILITY_SET = new Set(exports.COLLAB_CLOUD_CAPABILITIES);
61
73
  const CLOUD_JSON_OPERATION_SET = new Set(exports.COLLAB_CLOUD_JSON_OPERATIONS);
74
+ const COLLAB_PROJECT_CHECKPOINT_ARTIFACTS_SET = new Set(CollabProjectCheckpoint_1.COLLAB_PROJECT_CHECKPOINT_ARTIFACTS);
62
75
  const COLLAB_ERROR_CODE_SET = new Set(CollabError_1.COLLAB_ERROR_CODES);
63
76
  const RECOVERY_ACTION_SET = new Set([
64
77
  'retry',
@@ -66,6 +79,7 @@ const RECOVERY_ACTION_SET = new Set([
66
79
  'request-access',
67
80
  ]);
68
81
  const CAPABILITY_TOKEN_PATTERN = /^[a-z][a-z0-9-]{0,63}$/u;
82
+ const SHA256_PATTERN = /^[0-9a-f]{64}$/u;
69
83
  function invalidPayload(field) {
70
84
  return new CollabError_1.CollabError({
71
85
  code: 'protocol-payload-invalid',
@@ -152,6 +166,14 @@ function assertAttemptId(attemptId) {
152
166
  function isCloudJsonOperation(value) {
153
167
  return CLOUD_JSON_OPERATION_SET.has(value);
154
168
  }
169
+ function isoTimestamp(value, field) {
170
+ if (typeof value !== 'string'
171
+ || value.length > 64
172
+ || Number.isNaN(Date.parse(value))
173
+ || new Date(value).toISOString() !== value)
174
+ throw invalidPayload(field);
175
+ return value;
176
+ }
155
177
  function collabCloudCapabilitiesRoute() {
156
178
  return route('GET', '/collab/capabilities', { kind: 'capabilities' });
157
179
  }
@@ -159,7 +181,7 @@ function collabCloudProjectOperationRoute(projectId, operation) {
159
181
  assertProjectId(projectId);
160
182
  if (!isCloudJsonOperation(operation))
161
183
  invalidRoute();
162
- return route('POST', `/v1/projects/${projectId}/operations/${operation}`, {
184
+ return route('POST', `/v2/projects/${projectId}/operations/${operation}`, {
163
185
  kind: 'project-operation',
164
186
  operation,
165
187
  projectId,
@@ -169,7 +191,7 @@ function collabCloudProjectEventsRoute(projectId, afterSequence) {
169
191
  assertProjectId(projectId);
170
192
  if (!Number.isSafeInteger(afterSequence) || afterSequence < 0)
171
193
  invalidRoute();
172
- return route('GET', `/v1/projects/${projectId}/events?afterSequence=${afterSequence}`, {
194
+ return route('GET', `/v2/projects/${projectId}/events?afterSequence=${afterSequence}`, {
173
195
  afterSequence,
174
196
  kind: 'project-events',
175
197
  projectId,
@@ -180,17 +202,85 @@ function collabCloudGitRoute(projectId, routeKind, service) {
180
202
  if (routeKind === 'info-refs') {
181
203
  if (service !== 'git-upload-pack' && service !== 'git-receive-pack')
182
204
  invalidRoute();
183
- return route('GET', `/v1/projects/${projectId}/repository.git/info/refs?service=${service}`, { kind: 'git-info-refs', projectId, service });
205
+ return route('GET', `/v2/projects/${projectId}/repository.git/info/refs?service=${service}`, { kind: 'git-info-refs', projectId, service });
184
206
  }
185
207
  if (service !== undefined)
186
208
  invalidRoute();
187
- return route('POST', `/v1/projects/${projectId}/repository.git/${routeKind}`, {
209
+ return route('POST', `/v2/projects/${projectId}/repository.git/${routeKind}`, {
188
210
  kind: routeKind,
189
211
  projectId,
190
212
  });
191
213
  }
214
+ function collabCloudAuthorityTransferArtifactRoute(projectId, transferId, direction, artifact) {
215
+ assertProjectId(projectId);
216
+ assertAttemptId(transferId);
217
+ if (direction !== 'download' && direction !== 'upload'
218
+ || !COLLAB_PROJECT_CHECKPOINT_ARTIFACTS_SET.has(artifact))
219
+ invalidRoute();
220
+ return route(direction === 'upload' ? 'PUT' : 'GET', `/v2/projects/${projectId}/authority-transfers/${transferId}/checkpoint/${artifact}`, {
221
+ artifact,
222
+ direction,
223
+ kind: 'authority-transfer-artifact',
224
+ projectId,
225
+ transferId,
226
+ });
227
+ }
228
+ function collabCloudProjectCheckpointExportArtifactRoute(projectId, exportId, artifact) {
229
+ assertProjectId(projectId);
230
+ assertAttemptId(exportId);
231
+ if (!COLLAB_PROJECT_CHECKPOINT_ARTIFACTS_SET.has(artifact))
232
+ invalidRoute();
233
+ return route('GET', `/v2/projects/${projectId}/checkpoint-exports/${exportId}/checkpoint/${artifact}`, {
234
+ artifact,
235
+ exportId,
236
+ kind: 'project-checkpoint-export-artifact',
237
+ projectId,
238
+ });
239
+ }
240
+ function collabCloudProjectCheckpointExportRoute(projectId, exportId, operation) {
241
+ assertProjectId(projectId);
242
+ assertAttemptId(exportId);
243
+ if (operation !== 'begin' && operation !== 'status')
244
+ invalidRoute();
245
+ return route(operation === 'begin' ? 'POST' : 'GET', `/v2/projects/${projectId}/checkpoint-exports/${exportId}`, { exportId, kind: 'project-checkpoint-export', operation, projectId });
246
+ }
247
+ function decodeCollabCloudProjectCheckpointExportStatus(value) {
248
+ const source = exactRecord(value, 'checkpointExport', [
249
+ 'checkpointSha256',
250
+ 'createdAt',
251
+ 'expiresAt',
252
+ 'exportId',
253
+ 'projectId',
254
+ 'state',
255
+ ]);
256
+ if (!(0, CollabValidation_1.isCollabOpaqueId)(source.exportId) || !(0, CollabValidation_1.isCollabProjectId)(source.projectId)) {
257
+ throw invalidPayload('checkpointExport');
258
+ }
259
+ const state = source.state;
260
+ if (state !== 'preparing' && state !== 'ready')
261
+ throw invalidPayload('state');
262
+ if (source.checkpointSha256 !== null && typeof source.checkpointSha256 !== 'string') {
263
+ throw invalidPayload('checkpointSha256');
264
+ }
265
+ const checkpointSha256 = source.checkpointSha256;
266
+ if ((state === 'preparing' && checkpointSha256 !== null)
267
+ || (state === 'ready' && (typeof checkpointSha256 !== 'string' || !SHA256_PATTERN.test(checkpointSha256))))
268
+ throw invalidPayload('checkpointSha256');
269
+ const createdAt = isoTimestamp(source.createdAt, 'createdAt');
270
+ const expiresAt = isoTimestamp(source.expiresAt, 'expiresAt');
271
+ if (Date.parse(expiresAt) <= Date.parse(createdAt))
272
+ throw invalidPayload('expiresAt');
273
+ return Object.freeze({
274
+ checkpointSha256,
275
+ createdAt,
276
+ expiresAt,
277
+ exportId: source.exportId,
278
+ projectId: source.projectId,
279
+ state,
280
+ });
281
+ }
192
282
  function collabDevelopmentBootstrapRoute(operation, attemptId) {
193
- const base = '/v1/development/bootstrap/attempts';
283
+ const base = '/v2/development/bootstrap/attempts';
194
284
  if (operation === 'beginDevelopmentBootstrap') {
195
285
  if (attemptId !== undefined)
196
286
  invalidRoute();
@@ -231,7 +321,7 @@ function parseCanonicalNonNegativeInteger(value) {
231
321
  return Number.isSafeInteger(parsed) ? parsed : null;
232
322
  }
233
323
  function matchCollabCloudRoute(method, target) {
234
- if (!target.startsWith('/') || target.includes('#'))
324
+ if (!target.startsWith('/') || target.startsWith('//') || target.includes('#'))
235
325
  return null;
236
326
  let url;
237
327
  try {
@@ -240,6 +330,8 @@ function matchCollabCloudRoute(method, target) {
240
330
  catch {
241
331
  return null;
242
332
  }
333
+ if (`${url.pathname}${url.search}` !== target)
334
+ return null;
243
335
  const segments = url.pathname.split('/').filter(Boolean);
244
336
  if (url.pathname !== `/${segments.join('/')}` || target.endsWith('?'))
245
337
  return null;
@@ -247,10 +339,51 @@ function matchCollabCloudRoute(method, target) {
247
339
  && url.pathname === '/collab/capabilities'
248
340
  && exactQuery(url, []))
249
341
  return { kind: 'capabilities' };
250
- if (segments[0] === 'v1'
342
+ if (segments[0] === 'v2'
251
343
  && segments[1] === 'projects'
252
344
  && (0, CollabValidation_1.isCollabProjectId)(segments[2])) {
253
345
  const projectId = segments[2];
346
+ if (method === 'GET'
347
+ && segments.length === 7
348
+ && segments[3] === 'checkpoint-exports'
349
+ && (0, CollabValidation_1.isCollabOpaqueId)(segments[4])
350
+ && segments[5] === 'checkpoint'
351
+ && COLLAB_PROJECT_CHECKPOINT_ARTIFACTS_SET.has(segments[6])
352
+ && exactQuery(url, [])) {
353
+ return {
354
+ artifact: segments[6],
355
+ exportId: segments[4],
356
+ kind: 'project-checkpoint-export-artifact',
357
+ projectId,
358
+ };
359
+ }
360
+ if ((method === 'POST' || method === 'GET')
361
+ && segments.length === 5
362
+ && segments[3] === 'checkpoint-exports'
363
+ && (0, CollabValidation_1.isCollabOpaqueId)(segments[4])
364
+ && exactQuery(url, [])) {
365
+ return {
366
+ exportId: segments[4],
367
+ kind: 'project-checkpoint-export',
368
+ operation: method === 'POST' ? 'begin' : 'status',
369
+ projectId,
370
+ };
371
+ }
372
+ if ((method === 'GET' || method === 'PUT')
373
+ && segments.length === 7
374
+ && segments[3] === 'authority-transfers'
375
+ && (0, CollabValidation_1.isCollabOpaqueId)(segments[4])
376
+ && segments[5] === 'checkpoint'
377
+ && COLLAB_PROJECT_CHECKPOINT_ARTIFACTS_SET.has(segments[6])
378
+ && exactQuery(url, [])) {
379
+ return {
380
+ artifact: segments[6],
381
+ direction: method === 'PUT' ? 'upload' : 'download',
382
+ kind: 'authority-transfer-artifact',
383
+ projectId,
384
+ transferId: segments[4],
385
+ };
386
+ }
254
387
  if (method === 'POST'
255
388
  && segments.length === 5
256
389
  && segments[3] === 'operations'
@@ -285,7 +418,7 @@ function matchCollabCloudRoute(method, target) {
285
418
  && exactQuery(url, []))
286
419
  return { kind: segments[4], projectId };
287
420
  }
288
- if (segments[0] !== 'v1'
421
+ if (segments[0] !== 'v2'
289
422
  || segments[1] !== 'development'
290
423
  || segments[2] !== 'bootstrap'
291
424
  || segments[3] !== 'attempts'
@@ -329,6 +462,10 @@ function decodeCollabCloudCapabilityDocument(value) {
329
462
  requireSupportedVersion(protocolVersions, CollabConstants_1.COLLAB_PROTOCOL_VERSION, 'canonical-wire');
330
463
  const capabilities = sortedUniqueTokens(source.capabilities, 'capabilities');
331
464
  const limitsSource = exactRecord(source.limits, 'limits', [
465
+ 'maxCheckpointCoordinationBytes',
466
+ 'maxCheckpointManifestUtf8Bytes',
467
+ 'maxCheckpointRepositoryBundleBytes',
468
+ 'maxCheckpointStagingBytes',
332
469
  'maxDevelopmentBootstrapGitBundleBytes',
333
470
  'maxDevelopmentBootstrapManifestUtf8Bytes',
334
471
  'maxDevelopmentBootstrapReportUtf8Bytes',
@@ -338,6 +475,10 @@ function decodeCollabCloudCapabilityDocument(value) {
338
475
  'maxRepositoryBytes',
339
476
  ]);
340
477
  const limits = Object.freeze({
478
+ maxCheckpointCoordinationBytes: positiveSafeInteger(limitsSource.maxCheckpointCoordinationBytes, 'maxCheckpointCoordinationBytes', exports.COLLAB_CLOUD_BINDING_LIMITS.maxCheckpointCoordinationBytes),
479
+ maxCheckpointManifestUtf8Bytes: positiveSafeInteger(limitsSource.maxCheckpointManifestUtf8Bytes, 'maxCheckpointManifestUtf8Bytes', exports.COLLAB_CLOUD_BINDING_LIMITS.maxCheckpointManifestUtf8Bytes),
480
+ maxCheckpointRepositoryBundleBytes: positiveSafeInteger(limitsSource.maxCheckpointRepositoryBundleBytes, 'maxCheckpointRepositoryBundleBytes', exports.COLLAB_CLOUD_BINDING_LIMITS.maxCheckpointRepositoryBundleBytes),
481
+ maxCheckpointStagingBytes: positiveSafeInteger(limitsSource.maxCheckpointStagingBytes, 'maxCheckpointStagingBytes', exports.COLLAB_CLOUD_BINDING_LIMITS.maxCheckpointStagingBytes),
341
482
  maxDevelopmentBootstrapGitBundleBytes: positiveSafeInteger(limitsSource.maxDevelopmentBootstrapGitBundleBytes, 'maxDevelopmentBootstrapGitBundleBytes', exports.COLLAB_CLOUD_BINDING_LIMITS.maxDevelopmentBootstrapGitBundleBytes),
342
483
  maxDevelopmentBootstrapManifestUtf8Bytes: positiveSafeInteger(limitsSource.maxDevelopmentBootstrapManifestUtf8Bytes, 'maxDevelopmentBootstrapManifestUtf8Bytes', exports.COLLAB_CLOUD_BINDING_LIMITS.maxDevelopmentBootstrapManifestUtf8Bytes),
343
484
  maxDevelopmentBootstrapReportUtf8Bytes: positiveSafeInteger(limitsSource.maxDevelopmentBootstrapReportUtf8Bytes, 'maxDevelopmentBootstrapReportUtf8Bytes', exports.COLLAB_CLOUD_BINDING_LIMITS.maxDevelopmentBootstrapReportUtf8Bytes),
@@ -1,8 +1,11 @@
1
1
  import { COLLAB_PROTOCOL_VERSION } from './CollabConstants';
2
2
  import type { CollabGitOid, CollabIsoTimestamp, CollabMemberId, CollabProjectId, CollabRequestId, CollabTicketId } from './types';
3
- export declare const COLLAB_CLOUD_EVENT_KINDS: readonly ["membership.updated", "request.updated", "request.comment-added", "ticket.updated", "ticket.comment-added", "main.updated"];
3
+ export declare const COLLAB_CLOUD_EVENT_KINDS: readonly ["membership.updated", "request.updated", "request.comment-added", "ticket.updated", "ticket.comment-added", "main.updated", "authority-transfer.updated", "membership.claimed", "project.retired"];
4
4
  export type CollabCloudEventKind = typeof COLLAB_CLOUD_EVENT_KINDS[number];
5
5
  export interface CollabCloudEventPayloadMap {
6
+ readonly 'authority-transfer.updated': {
7
+ readonly transferId: string;
8
+ };
6
9
  readonly 'main.updated': {
7
10
  readonly mainOid: CollabGitOid;
8
11
  readonly requestId: CollabRequestId;
@@ -10,6 +13,14 @@ export interface CollabCloudEventPayloadMap {
10
13
  readonly 'membership.updated': {
11
14
  readonly memberId: CollabMemberId;
12
15
  };
16
+ readonly 'membership.claimed': {
17
+ readonly memberId: CollabMemberId;
18
+ readonly transferId: string;
19
+ };
20
+ readonly 'project.retired': {
21
+ readonly retiredAt: CollabIsoTimestamp;
22
+ readonly retirementId: string;
23
+ };
13
24
  readonly 'request.comment-added': {
14
25
  readonly requestId: CollabRequestId;
15
26
  };
@@ -12,6 +12,9 @@ exports.COLLAB_CLOUD_EVENT_KINDS = Object.freeze([
12
12
  'ticket.updated',
13
13
  'ticket.comment-added',
14
14
  'main.updated',
15
+ 'authority-transfer.updated',
16
+ 'membership.claimed',
17
+ 'project.retired',
15
18
  ]);
16
19
  const EVENT_KIND_SET = new Set(exports.COLLAB_CLOUD_EVENT_KINDS);
17
20
  function invalidPayload(field) {
@@ -67,10 +70,28 @@ function timestamp(source, field) {
67
70
  }
68
71
  function decodePayload(kind, value) {
69
72
  switch (kind) {
73
+ case 'authority-transfer.updated': {
74
+ const source = exactRecord(value, 'payload', ['transferId']);
75
+ return { transferId: stringField(source, 'transferId', 128, CollabValidation_1.isCollabOpaqueId) };
76
+ }
70
77
  case 'membership.updated': {
71
78
  const source = exactRecord(value, 'payload', ['memberId']);
72
79
  return { memberId: stringField(source, 'memberId', 64, CollabValidation_1.isCollabMemberId) };
73
80
  }
81
+ case 'membership.claimed': {
82
+ const source = exactRecord(value, 'payload', ['memberId', 'transferId']);
83
+ return {
84
+ memberId: stringField(source, 'memberId', 64, CollabValidation_1.isCollabMemberId),
85
+ transferId: stringField(source, 'transferId', 128, CollabValidation_1.isCollabOpaqueId),
86
+ };
87
+ }
88
+ case 'project.retired': {
89
+ const source = exactRecord(value, 'payload', ['retiredAt', 'retirementId']);
90
+ return {
91
+ retiredAt: timestamp(source, 'retiredAt'),
92
+ retirementId: stringField(source, 'retirementId', 128, CollabValidation_1.isCollabOpaqueId),
93
+ };
94
+ }
74
95
  case 'request.updated':
75
96
  case 'request.comment-added': {
76
97
  const source = exactRecord(value, 'payload', ['requestId']);
@@ -1,4 +1,4 @@
1
- export declare const COLLAB_PROTOCOL_VERSION: 4;
1
+ export declare const COLLAB_PROTOCOL_VERSION: 5;
2
2
  export declare const COLLAB_MAIN_REF: "refs/heads/main";
3
3
  export declare const COLLAB_MEMBER_REF_PREFIX: "refs/heads/members/";
4
4
  export declare const COLLAB_LIMITS: Readonly<{
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.COLLAB_LIMITS = exports.COLLAB_MEMBER_REF_PREFIX = exports.COLLAB_MAIN_REF = exports.COLLAB_PROTOCOL_VERSION = void 0;
4
- exports.COLLAB_PROTOCOL_VERSION = 4;
4
+ exports.COLLAB_PROTOCOL_VERSION = 5;
5
5
  exports.COLLAB_MAIN_REF = 'refs/heads/main';
6
6
  exports.COLLAB_MEMBER_REF_PREFIX = 'refs/heads/members/';
7
7
  exports.COLLAB_LIMITS = Object.freeze({
@@ -23,6 +23,23 @@ export declare const COLLAB_CONTROL_OPERATION_CODECS: Readonly<{
23
23
  readonly reopenTicket: CollabControlOperationCodec<import("./CollabProtocol").ChangeTicketStatusRequest, import("./CollabProtocol").TicketMutationResponse>;
24
24
  readonly updateMyRequestMetadata: CollabControlOperationCodec<import("./CollabProtocol").UpdateMyRequestMetadataRequest, import("./CollabProtocol").UpdateMyRequestMetadataResponse>;
25
25
  readonly acceptRequest: CollabControlOperationCodec<import("./CollabProtocol").AcceptRequest, import("./CollabProtocol").AcceptResponse>;
26
+ readonly requestLanToCloudTransfer: CollabControlOperationCodec<import("./CollabAuthorityTransfer").RequestLanToCloudTransferRequest, import("./CollabAuthorityTransfer").CollabAuthorityTransferStatus>;
27
+ readonly acceptLanToCloudTransferTarget: CollabControlOperationCodec<import("./CollabAuthorityTransfer").AcceptLanToCloudTransferTargetRequest, import("./CollabAuthorityTransfer").CollabAuthorityTransferStatus>;
28
+ readonly beginLanToCloudTransfer: CollabControlOperationCodec<import("./CollabAuthorityTransfer").BeginLanToCloudTransferRequest, import("./CollabAuthorityTransfer").CollabAuthorityTransferStatus>;
29
+ readonly getProjectAuthorityTransfer: CollabControlOperationCodec<import("./CollabAuthorityTransfer").GetProjectAuthorityTransferRequest, import("./CollabAuthorityTransfer").CollabAuthorityTransferStatus>;
30
+ readonly rotateTransferredMembershipClaims: CollabControlOperationCodec<import("./CollabAuthorityTransfer").RotateTransferredMembershipClaimsRequest, import("./CollabAuthorityTransfer").CollabTransferredMembershipClaimBatch>;
31
+ readonly acknowledgeTransferredMembershipClaimBatch: CollabControlOperationCodec<import("./CollabAuthorityTransfer").AcknowledgeTransferredMembershipClaimBatchRequest, import("./CollabAuthorityTransfer").CollabTransferredMembershipClaimCustodyReceipt>;
32
+ readonly getTransferredMembershipClaim: CollabControlOperationCodec<import("./CollabAuthorityTransfer").GetTransferredMembershipClaimRequest, import("./CollabAuthorityTransfer").CollabTransferredMembershipClaim>;
33
+ readonly claimTransferredMembership: CollabControlOperationCodec<import("./CollabAuthorityTransfer").ClaimTransferredMembershipRequest, import("./CollabAuthorityTransfer").CollabTransferredMembershipRedemptionReceipt>;
34
+ readonly acknowledgeTransferredMembershipClaimRedemption: CollabControlOperationCodec<import("./CollabAuthorityTransfer").AcknowledgeTransferredMembershipClaimRedemptionRequest, import("./CollabAuthorityTransfer").CollabTransferredMembershipRedemptionAcknowledgement>;
35
+ readonly commitLanToCloudRelinquishment: CollabControlOperationCodec<import("./CollabAuthorityTransfer").CommitLanToCloudRelinquishmentRequest, import("./CollabAuthorityTransfer").CollabAuthorityTransferStatus>;
36
+ readonly beginCloudToLanTransfer: CollabControlOperationCodec<import("./CollabAuthorityTransfer").BeginCloudToLanTransferRequest, import("./CollabAuthorityTransfer").CollabAuthorityTransferStatus>;
37
+ readonly acceptCloudToLanTransferTarget: CollabControlOperationCodec<import("./CollabAuthorityTransfer").AcceptCloudToLanTransferTargetRequest, import("./CollabAuthorityTransfer").CollabAuthorityTransferStatus>;
38
+ readonly reportCloudToLanTargetStaged: CollabControlOperationCodec<import("./CollabAuthorityTransfer").ReportCloudToLanTargetStagedRequest, import("./CollabAuthorityTransfer").CollabTransferredMembershipClaimCustodyReceipt>;
39
+ readonly confirmCloudToLanTargetActive: CollabControlOperationCodec<import("./CollabAuthorityTransfer").ConfirmCloudToLanTargetActiveRequest, import("./CollabAuthorityTransfer").CollabAuthorityTransferStatus>;
40
+ readonly cancelProjectAuthorityTransfer: CollabControlOperationCodec<import("./CollabAuthorityTransfer").CancelProjectAuthorityTransferRequest, import("./CollabAuthorityTransfer").CollabAuthorityTransferStatus>;
41
+ readonly retireProject: CollabControlOperationCodec<import("./CollabProjectRetirement").CollabProjectRetirementRequest, import("./CollabProjectRetirement").CollabProjectRetirementResult>;
42
+ readonly acknowledgeProjectRetirement: CollabControlOperationCodec<import("./CollabProjectRetirement").CollabProjectRetirementAcknowledgementRequest, import("./CollabProjectRetirement").CollabProjectRetirementAcknowledgement>;
26
43
  }>;
27
44
  export declare function collabControlOperationCodec<Operation extends CollabControlOperation>(operation: Operation): CodecMap[Operation];
28
45
  export {};
@@ -2,13 +2,40 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.COLLAB_CONTROL_OPERATION_CODECS = void 0;
4
4
  exports.collabControlOperationCodec = collabControlOperationCodec;
5
+ const CollabAuthorityTransfer_1 = require("./CollabAuthorityTransfer");
5
6
  const CollabError_1 = require("./CollabError");
7
+ const CollabProjectRetirement_1 = require("./CollabProjectRetirement");
6
8
  const CollabRequestTicketRequestCodecs_1 = require("./CollabRequestTicketRequestCodecs");
7
9
  const CollabRequestTicketResponseCodecs_1 = require("./CollabRequestTicketResponseCodecs");
10
+ const AUTHORITY_TRANSFER_OPERATION_SET = new Set(CollabAuthorityTransfer_1.COLLAB_AUTHORITY_TRANSFER_OPERATIONS);
11
+ const PROJECT_RETIREMENT_OPERATION_SET = new Set(CollabProjectRetirement_1.COLLAB_PROJECT_RETIREMENT_OPERATIONS);
12
+ function lifecycleDecodeResult(decode) {
13
+ try {
14
+ return { status: 'ok', value: decode() };
15
+ }
16
+ catch (error) {
17
+ if (error instanceof CollabError_1.CollabError && error.code === 'protocol-payload-invalid') {
18
+ return { error, status: 'invalid' };
19
+ }
20
+ throw error;
21
+ }
22
+ }
8
23
  function decodeRequest(operation, input) {
24
+ if (AUTHORITY_TRANSFER_OPERATION_SET.has(operation)) {
25
+ return lifecycleDecodeResult(() => (0, CollabAuthorityTransfer_1.decodeCollabAuthorityTransferOperationRequest)(operation, input));
26
+ }
27
+ if (PROJECT_RETIREMENT_OPERATION_SET.has(operation)) {
28
+ return lifecycleDecodeResult(() => (0, CollabProjectRetirement_1.decodeCollabProjectRetirementOperationRequest)(operation, input));
29
+ }
9
30
  return (0, CollabRequestTicketRequestCodecs_1.decodeCollabRequestTicketOperationRequest)(operation, input);
10
31
  }
11
32
  function decodeResponse(operation, input) {
33
+ if (AUTHORITY_TRANSFER_OPERATION_SET.has(operation)) {
34
+ return (0, CollabAuthorityTransfer_1.decodeCollabAuthorityTransferOperationResponse)(operation, input);
35
+ }
36
+ if (PROJECT_RETIREMENT_OPERATION_SET.has(operation)) {
37
+ return (0, CollabProjectRetirement_1.decodeCollabProjectRetirementOperationResponse)(operation, input);
38
+ }
12
39
  switch (operation) {
13
40
  case 'getRequest': return (0, CollabRequestTicketResponseCodecs_1.decodeRequestDetailResponse)(input);
14
41
  case 'listRequestComments': return (0, CollabRequestTicketResponseCodecs_1.decodeCommentPageResponse)(input);
@@ -50,6 +77,23 @@ exports.COLLAB_CONTROL_OPERATION_CODECS = Object.freeze({
50
77
  reopenTicket: codec('reopenTicket'),
51
78
  updateMyRequestMetadata: codec('updateMyRequestMetadata'),
52
79
  acceptRequest: codec('acceptRequest'),
80
+ requestLanToCloudTransfer: codec('requestLanToCloudTransfer'),
81
+ acceptLanToCloudTransferTarget: codec('acceptLanToCloudTransferTarget'),
82
+ beginLanToCloudTransfer: codec('beginLanToCloudTransfer'),
83
+ getProjectAuthorityTransfer: codec('getProjectAuthorityTransfer'),
84
+ rotateTransferredMembershipClaims: codec('rotateTransferredMembershipClaims'),
85
+ acknowledgeTransferredMembershipClaimBatch: codec('acknowledgeTransferredMembershipClaimBatch'),
86
+ getTransferredMembershipClaim: codec('getTransferredMembershipClaim'),
87
+ claimTransferredMembership: codec('claimTransferredMembership'),
88
+ acknowledgeTransferredMembershipClaimRedemption: codec('acknowledgeTransferredMembershipClaimRedemption'),
89
+ commitLanToCloudRelinquishment: codec('commitLanToCloudRelinquishment'),
90
+ beginCloudToLanTransfer: codec('beginCloudToLanTransfer'),
91
+ acceptCloudToLanTransferTarget: codec('acceptCloudToLanTransferTarget'),
92
+ reportCloudToLanTargetStaged: codec('reportCloudToLanTargetStaged'),
93
+ confirmCloudToLanTargetActive: codec('confirmCloudToLanTargetActive'),
94
+ cancelProjectAuthorityTransfer: codec('cancelProjectAuthorityTransfer'),
95
+ retireProject: codec('retireProject'),
96
+ acknowledgeProjectRetirement: codec('acknowledgeProjectRetirement'),
53
97
  });
54
98
  function collabControlOperationCodec(operation) {
55
99
  if (!Object.hasOwn(exports.COLLAB_CONTROL_OPERATION_CODECS, operation)) {
@@ -1,4 +1,4 @@
1
- export declare const COLLAB_ERROR_CODES: readonly ["protocol-version-unsupported", "protocol-payload-invalid", "project-not-found", "quota-exceeded", "authentication-failed", "authorization-denied", "membership-revoked", "stale-main", "stale-request-head", "personal-ref-diverged", "content-conflict", "description-required", "request-not-open", "request-head-not-pushed", "ticket-not-found", "resolving-ticket-reference-not-found", "ticket-not-open", "stale-ticket", "stale-request-metadata", "authority-not-synchronized", "idempotency-conflict", "acceptance-recovery-required", "authority-integrity-error", "operation-timeout", "operation-failed"];
1
+ export declare const COLLAB_ERROR_CODES: readonly ["protocol-version-unsupported", "protocol-payload-invalid", "project-not-found", "quota-exceeded", "authentication-failed", "authorization-denied", "membership-revoked", "stale-main", "stale-request-head", "personal-ref-diverged", "content-conflict", "description-required", "request-not-open", "request-head-not-pushed", "ticket-not-found", "resolving-ticket-reference-not-found", "ticket-not-open", "stale-ticket", "stale-request-metadata", "authority-not-synchronized", "authority-transfer-not-found", "authority-transfer-stale", "authority-transfer-cancellation-forbidden", "membership-claim-invalid", "membership-claim-expired", "membership-claim-revoked", "membership-claim-already-redeemed", "project-retired", "idempotency-conflict", "acceptance-recovery-required", "authority-integrity-error", "operation-timeout", "operation-failed"];
2
2
  export type CollabErrorCode = typeof COLLAB_ERROR_CODES[number];
3
3
  export type CollabErrorGroup = 'setup' | 'path' | 'authorization' | 'state' | 'integrity' | 'operation';
4
4
  declare const COLLAB_RECOVERY_ACTIONS: readonly ["retry", "review-conflicts", "request-access"];
@@ -24,6 +24,14 @@ exports.COLLAB_ERROR_CODES = Object.freeze([
24
24
  'stale-ticket',
25
25
  'stale-request-metadata',
26
26
  'authority-not-synchronized',
27
+ 'authority-transfer-not-found',
28
+ 'authority-transfer-stale',
29
+ 'authority-transfer-cancellation-forbidden',
30
+ 'membership-claim-invalid',
31
+ 'membership-claim-expired',
32
+ 'membership-claim-revoked',
33
+ 'membership-claim-already-redeemed',
34
+ 'project-retired',
27
35
  'idempotency-conflict',
28
36
  'acceptance-recovery-required',
29
37
  'authority-integrity-error',
@@ -95,11 +103,15 @@ function collabErrorGroup(code) {
95
103
  case 'protocol-payload-invalid':
96
104
  return 'setup';
97
105
  case 'project-not-found':
106
+ case 'authority-transfer-not-found':
98
107
  case 'quota-exceeded':
99
108
  return 'path';
100
109
  case 'authentication-failed':
101
110
  case 'authorization-denied':
102
111
  case 'membership-revoked':
112
+ case 'membership-claim-invalid':
113
+ case 'membership-claim-expired':
114
+ case 'membership-claim-revoked':
103
115
  return 'authorization';
104
116
  case 'stale-main':
105
117
  case 'stale-request-head':
@@ -114,6 +126,10 @@ function collabErrorGroup(code) {
114
126
  case 'stale-ticket':
115
127
  case 'stale-request-metadata':
116
128
  case 'authority-not-synchronized':
129
+ case 'authority-transfer-stale':
130
+ case 'authority-transfer-cancellation-forbidden':
131
+ case 'membership-claim-already-redeemed':
132
+ case 'project-retired':
117
133
  case 'idempotency-conflict':
118
134
  return 'state';
119
135
  case 'acceptance-recovery-required':