@company-semantics/contracts 63.1.0 → 63.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.
- package/package.json +2 -2
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +133 -1
- package/src/comments/__tests__/schemas.test.ts +2 -0
- package/src/comments/__tests__/suggestion.test.ts +28 -2
- package/src/comments/schemas.ts +26 -0
- package/src/comments/suggestion.ts +36 -7
- package/src/generated/openapi-routes.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@company-semantics/contracts",
|
|
3
|
-
"version": "63.
|
|
3
|
+
"version": "63.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"anchor-corpus:sync": "tsx scripts/anchor-corpus.ts --sync",
|
|
119
119
|
"generate:spec-hash": "tsx scripts/generate-spec-hash.ts",
|
|
120
120
|
"generate:spec-hash:check": "tsx scripts/generate-spec-hash.ts --check",
|
|
121
|
-
"generate:api": "pnpm generate:api-types && pnpm generate:spec-hash && pnpm generate:openapi-routes",
|
|
121
|
+
"generate:api": "pnpm generate:api-types && pnpm generate:spec-hash && pnpm generate:openapi-routes && pnpm file-sizes:pin",
|
|
122
122
|
"generate:api:check": "pnpm generate:api-types:check && pnpm generate:spec-hash:check && pnpm generate:openapi-routes:check",
|
|
123
123
|
"generate:api-types": "openapi-typescript openapi/backend.yaml -o src/api/generated.ts",
|
|
124
124
|
"generate:openapi-routes": "tsx scripts/generate-openapi-routes.ts",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '191634e28786' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '191634e28786b02448431968a7b8f6f3fbbcd5f5cadc9f48e3067511a790c38b' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -3778,6 +3778,23 @@ export interface paths {
|
|
|
3778
3778
|
patch?: never;
|
|
3779
3779
|
trace?: never;
|
|
3780
3780
|
};
|
|
3781
|
+
"/api/comments/threads/{threadId}": {
|
|
3782
|
+
parameters: {
|
|
3783
|
+
query?: never;
|
|
3784
|
+
header?: never;
|
|
3785
|
+
path?: never;
|
|
3786
|
+
cookie?: never;
|
|
3787
|
+
};
|
|
3788
|
+
get?: never;
|
|
3789
|
+
put?: never;
|
|
3790
|
+
post?: never;
|
|
3791
|
+
/** Soft-delete a thread of either kind (creator or subject owner) */
|
|
3792
|
+
delete: operations["deleteCommentThread"];
|
|
3793
|
+
options?: never;
|
|
3794
|
+
head?: never;
|
|
3795
|
+
patch?: never;
|
|
3796
|
+
trace?: never;
|
|
3797
|
+
};
|
|
3781
3798
|
"/api/comments/threads/{threadId}/accept-intent": {
|
|
3782
3799
|
parameters: {
|
|
3783
3800
|
query?: never;
|
|
@@ -3863,6 +3880,23 @@ export interface paths {
|
|
|
3863
3880
|
patch?: never;
|
|
3864
3881
|
trace?: never;
|
|
3865
3882
|
};
|
|
3883
|
+
"/api/comments/threads/{threadId}/revert": {
|
|
3884
|
+
parameters: {
|
|
3885
|
+
query?: never;
|
|
3886
|
+
header?: never;
|
|
3887
|
+
path?: never;
|
|
3888
|
+
cookie?: never;
|
|
3889
|
+
};
|
|
3890
|
+
get?: never;
|
|
3891
|
+
put?: never;
|
|
3892
|
+
/** Revert a decided suggestion, restoring the passage an acceptance replaced */
|
|
3893
|
+
post: operations["revertSuggestion"];
|
|
3894
|
+
delete?: never;
|
|
3895
|
+
options?: never;
|
|
3896
|
+
head?: never;
|
|
3897
|
+
patch?: never;
|
|
3898
|
+
trace?: never;
|
|
3899
|
+
};
|
|
3866
3900
|
}
|
|
3867
3901
|
export type webhooks = Record<string, never>;
|
|
3868
3902
|
export interface components {
|
|
@@ -7028,6 +7062,11 @@ export interface components {
|
|
|
7028
7062
|
AnchorTraceRequest: {
|
|
7029
7063
|
lastAnchoredOffset: number;
|
|
7030
7064
|
};
|
|
7065
|
+
/** @description Acknowledges a soft-deleted thread: its id and the deletion stamp. */
|
|
7066
|
+
CommentThreadDeletedResponse: {
|
|
7067
|
+
threadId: string;
|
|
7068
|
+
deletedAt: string;
|
|
7069
|
+
};
|
|
7031
7070
|
/** @description The acceptance lease: the opaque claim token that alone can complete the accept, and how long the lease lives. */
|
|
7032
7071
|
SuggestionClaimResponse: {
|
|
7033
7072
|
claimToken: string;
|
|
@@ -7045,6 +7084,14 @@ export interface components {
|
|
|
7045
7084
|
claimToken: string;
|
|
7046
7085
|
expectedRevision: string;
|
|
7047
7086
|
};
|
|
7087
|
+
/** @description The reverted thread and the inverse document mutation that landed with it, or null when the suggestion had never been applied. */
|
|
7088
|
+
SuggestionRevertedResponse: {
|
|
7089
|
+
thread: components["schemas"]["CommentThreadSummary"];
|
|
7090
|
+
reverted: components["schemas"]["SuggestionApplied"] | null;
|
|
7091
|
+
};
|
|
7092
|
+
SuggestionRevertRequest: {
|
|
7093
|
+
expectedRevision?: string;
|
|
7094
|
+
};
|
|
7048
7095
|
/** @description Server-enforced collaboration size limits, published so clients can pre-check. */
|
|
7049
7096
|
CollabSyncLimits: {
|
|
7050
7097
|
maxUpdateBytes: number;
|
|
@@ -7205,7 +7252,7 @@ export interface components {
|
|
|
7205
7252
|
op: "insert" | "delete" | "replace";
|
|
7206
7253
|
insertedText?: string;
|
|
7207
7254
|
/** @enum {string} */
|
|
7208
|
-
status: "open" | "accepted" | "rejected";
|
|
7255
|
+
status: "open" | "accepted" | "rejected" | "reverted";
|
|
7209
7256
|
};
|
|
7210
7257
|
/** @description One comment; body is null iff soft-deleted (redacted projection with empty mentions). */
|
|
7211
7258
|
Comment: {
|
|
@@ -7246,6 +7293,8 @@ export interface components {
|
|
|
7246
7293
|
canReopen: boolean;
|
|
7247
7294
|
canDecideSuggestion: boolean;
|
|
7248
7295
|
canWithdraw: boolean;
|
|
7296
|
+
canRevertSuggestion: boolean;
|
|
7297
|
+
canDeleteThread: boolean;
|
|
7249
7298
|
};
|
|
7250
7299
|
/** @description A comment thread without its comments. */
|
|
7251
7300
|
CommentThreadSummary: {
|
|
@@ -13750,6 +13799,42 @@ export interface operations {
|
|
|
13750
13799
|
};
|
|
13751
13800
|
};
|
|
13752
13801
|
};
|
|
13802
|
+
deleteCommentThread: {
|
|
13803
|
+
parameters: {
|
|
13804
|
+
query?: never;
|
|
13805
|
+
header?: never;
|
|
13806
|
+
path: {
|
|
13807
|
+
threadId: string;
|
|
13808
|
+
};
|
|
13809
|
+
cookie?: never;
|
|
13810
|
+
};
|
|
13811
|
+
requestBody?: never;
|
|
13812
|
+
responses: {
|
|
13813
|
+
/** @description Thread soft-deleted; the response acknowledges the id and the deletion stamp. Accepted suggestion text stays in the document */
|
|
13814
|
+
200: {
|
|
13815
|
+
headers: {
|
|
13816
|
+
[name: string]: unknown;
|
|
13817
|
+
};
|
|
13818
|
+
content: {
|
|
13819
|
+
"application/json": components["schemas"]["CommentThreadDeletedResponse"];
|
|
13820
|
+
};
|
|
13821
|
+
};
|
|
13822
|
+
/** @description Thread not found, already deleted, or the caller is neither its creator nor a subject owner */
|
|
13823
|
+
404: {
|
|
13824
|
+
headers: {
|
|
13825
|
+
[name: string]: unknown;
|
|
13826
|
+
};
|
|
13827
|
+
content?: never;
|
|
13828
|
+
};
|
|
13829
|
+
/** @description A live acceptance lease is held on the suggestion */
|
|
13830
|
+
409: {
|
|
13831
|
+
headers: {
|
|
13832
|
+
[name: string]: unknown;
|
|
13833
|
+
};
|
|
13834
|
+
content?: never;
|
|
13835
|
+
};
|
|
13836
|
+
};
|
|
13837
|
+
};
|
|
13753
13838
|
acceptSuggestionIntent: {
|
|
13754
13839
|
parameters: {
|
|
13755
13840
|
query?: never;
|
|
@@ -13963,4 +14048,51 @@ export interface operations {
|
|
|
13963
14048
|
};
|
|
13964
14049
|
};
|
|
13965
14050
|
};
|
|
14051
|
+
revertSuggestion: {
|
|
14052
|
+
parameters: {
|
|
14053
|
+
query?: never;
|
|
14054
|
+
header?: never;
|
|
14055
|
+
path: {
|
|
14056
|
+
threadId: string;
|
|
14057
|
+
};
|
|
14058
|
+
cookie?: never;
|
|
14059
|
+
};
|
|
14060
|
+
requestBody: {
|
|
14061
|
+
content: {
|
|
14062
|
+
"application/json": components["schemas"]["SuggestionRevertRequest"];
|
|
14063
|
+
};
|
|
14064
|
+
};
|
|
14065
|
+
responses: {
|
|
14066
|
+
/** @description Suggestion reverted (terminal; thread stays resolved). On an accepted suggestion the inverse edit landed in the document on the SAME transaction and is reported; on a rejected one `reverted` is null */
|
|
14067
|
+
200: {
|
|
14068
|
+
headers: {
|
|
14069
|
+
[name: string]: unknown;
|
|
14070
|
+
};
|
|
14071
|
+
content: {
|
|
14072
|
+
"application/json": components["schemas"]["SuggestionRevertedResponse"];
|
|
14073
|
+
};
|
|
14074
|
+
};
|
|
14075
|
+
/** @description Malformed expectedRevision, or an accepted suggestion reverted without one */
|
|
14076
|
+
400: {
|
|
14077
|
+
headers: {
|
|
14078
|
+
[name: string]: unknown;
|
|
14079
|
+
};
|
|
14080
|
+
content?: never;
|
|
14081
|
+
};
|
|
14082
|
+
/** @description Thread not found, or the caller may not decide suggestions on it */
|
|
14083
|
+
404: {
|
|
14084
|
+
headers: {
|
|
14085
|
+
[name: string]: unknown;
|
|
14086
|
+
};
|
|
14087
|
+
content?: never;
|
|
14088
|
+
};
|
|
14089
|
+
/** @description Terminal, never retry: not a suggestion thread, still open, already reverted, a claim is on the row, the document moved past expectedRevision, or the inserted text cannot be found or no longer reads as inserted — re-read the document and decide again */
|
|
14090
|
+
409: {
|
|
14091
|
+
headers: {
|
|
14092
|
+
[name: string]: unknown;
|
|
14093
|
+
};
|
|
14094
|
+
content?: never;
|
|
14095
|
+
};
|
|
14096
|
+
};
|
|
14097
|
+
};
|
|
13966
14098
|
}
|
|
@@ -34,10 +34,36 @@ describe("the vocabulary tuples", () => {
|
|
|
34
34
|
expect([...SUGGESTION_OPS]).toEqual(["insert", "delete", "replace"]);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
it("SUGGESTION_STATUSES is exactly open, accepted and
|
|
37
|
+
it("SUGGESTION_STATUSES is exactly open, accepted, rejected and reverted", () => {
|
|
38
38
|
// The SECOND status level. The thread-level enum stays open|resolved —
|
|
39
39
|
// `./schemas.test.ts` pins that separately — and the decision lives here.
|
|
40
|
-
|
|
40
|
+
//
|
|
41
|
+
// PINNED AS A LIST, NOT A LENGTH, because widening it is the one change on
|
|
42
|
+
// this surface that cannot be walked back: a published `z.enum` refuses a
|
|
43
|
+
// member it has not shipped, and this one is nested inside the thread
|
|
44
|
+
// summary, so an unfamiliar status costs a client the whole document's
|
|
45
|
+
// comments. Failing here is the reminder that adding a member is a
|
|
46
|
+
// coordinated release — readers first, emitter second.
|
|
47
|
+
expect([...SUGGESTION_STATUSES]).toEqual([
|
|
48
|
+
"open",
|
|
49
|
+
"accepted",
|
|
50
|
+
"rejected",
|
|
51
|
+
"reverted",
|
|
52
|
+
]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("accepts a reverted payload", () => {
|
|
56
|
+
// The state a taken-back decision lands in. Parsed here so the widening is
|
|
57
|
+
// proven at the schema, not just in the tuple above.
|
|
58
|
+
expect(
|
|
59
|
+
SuggestionSchema.safeParse(
|
|
60
|
+
makeSuggestion({
|
|
61
|
+
op: "replace",
|
|
62
|
+
insertedText: "x",
|
|
63
|
+
status: "reverted",
|
|
64
|
+
}),
|
|
65
|
+
).success,
|
|
66
|
+
).toBe(true);
|
|
41
67
|
});
|
|
42
68
|
});
|
|
43
69
|
|
package/src/comments/schemas.ts
CHANGED
|
@@ -355,6 +355,32 @@ export const ThreadCapabilitiesSchema = z.object({
|
|
|
355
355
|
* their own suggestion.
|
|
356
356
|
*/
|
|
357
357
|
canWithdraw: z.boolean(),
|
|
358
|
+
/**
|
|
359
|
+
* Take back a decision on a DECIDED suggestion — the complement of
|
|
360
|
+
* `canDecideSuggestion`, exactly as `canReopen` is the complement of
|
|
361
|
+
* `canResolve` one kind over.
|
|
362
|
+
*
|
|
363
|
+
* The edit band, like deciding: undoing an acceptance puts the replaced
|
|
364
|
+
* passage back into the document, which is the same authority as putting the
|
|
365
|
+
* proposal in. A reverted suggestion offers it no longer — `reverted` is
|
|
366
|
+
* terminal, so this is false there as well as on an open one.
|
|
367
|
+
*/
|
|
368
|
+
canRevertSuggestion: z.boolean(),
|
|
369
|
+
/**
|
|
370
|
+
* Delete the whole thread — the conversation, not one comment in it.
|
|
371
|
+
*
|
|
372
|
+
* TRUE FOR BOTH KINDS, and it is the only capability here that does not
|
|
373
|
+
* mention one. A comment thread could previously only be dismantled by
|
|
374
|
+
* deleting its first comment, which cascaded; a suggestion thread could not be
|
|
375
|
+
* deleted at all, because its proposal is not a comment and there was nothing
|
|
376
|
+
* to aim the cascade at. One flag answers both.
|
|
377
|
+
*
|
|
378
|
+
* IT SAYS NOTHING ABOUT THE DOCUMENT. Deleting a thread whose suggestion was
|
|
379
|
+
* accepted leaves the accepted words where they are: they are the document's
|
|
380
|
+
* now, and taking them back is `canRevertSuggestion`'s act, deliberately kept
|
|
381
|
+
* separate so neither one silently performs the other.
|
|
382
|
+
*/
|
|
383
|
+
canDeleteThread: z.boolean(),
|
|
358
384
|
});
|
|
359
385
|
export type ThreadCapabilities = z.infer<typeof ThreadCapabilitiesSchema>;
|
|
360
386
|
|
|
@@ -37,14 +37,43 @@ export const SUGGESTION_OPS = ["insert", "delete", "replace"] as const;
|
|
|
37
37
|
export type SuggestionOp = (typeof SUGGESTION_OPS)[number];
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* open -> accepted | rejected
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
40
|
+
* `open -> accepted | rejected -> reverted`. This is the SECOND status level —
|
|
41
|
+
* the thread-level `status` enum stays exactly `open | resolved`, and a decided
|
|
42
|
+
* suggestion arrives as thread status `resolved` with this field carrying which
|
|
43
|
+
* way it went. Widening the closed thread enum instead would be a parse outage
|
|
44
|
+
* for every deployed client.
|
|
45
|
+
*
|
|
46
|
+
* A DECISION IS NO LONGER THE LAST WORD, BUT IT IS STILL ONE-WAY. This tuple
|
|
47
|
+
* read `open | accepted | rejected` with "terminal states are terminal: there is
|
|
48
|
+
* no reopen for a decided suggestion" written beside it, and that is now false:
|
|
49
|
+
* people are allowed to change their minds, and undoing an acceptance restores
|
|
50
|
+
* the passage the edit replaced. What has NOT changed is that the arrow never
|
|
51
|
+
* points back to `open`. `reverted` is its own terminal state rather than a
|
|
52
|
+
* return to the start, for two reasons — a reverted proposal is a decision
|
|
53
|
+
* somebody took and then took back, which is a different fact from one nobody
|
|
54
|
+
* has answered yet; and an `open` suggestion whose document still carries an
|
|
55
|
+
* application receipt is exactly the shape the server's reconciler treats as
|
|
56
|
+
* proof of an unacknowledged accept, so returning one to `open` would have it
|
|
57
|
+
* silently re-accepted by background maintenance.
|
|
58
|
+
*
|
|
59
|
+
* WIDENING THIS TUPLE IS ORDERED, NOT FREE. `SuggestionSchema` below is a
|
|
60
|
+
* `z.strictObject` nested inside `CommentThreadSummarySchema`, so for anyone who
|
|
61
|
+
* PARSES a thread list, one row carrying a status they do not know fails the
|
|
62
|
+
* whole list — every comment on that document, not just the odd suggestion — and
|
|
63
|
+
* it fails the first time somebody reverts rather than at deploy. No consumer in
|
|
64
|
+
* this workspace parses these at runtime today (the app is typed off the
|
|
65
|
+
* generated spec; the backend validates against its own local copies), which was
|
|
66
|
+
* checked rather than assumed, so this package leads the release and the emitter
|
|
67
|
+
* follows. The next person to add a member owes that check again for their own
|
|
68
|
+
* consumers — which is why `__tests__/suggestion.test.ts` pins this list
|
|
69
|
+
* exactly. See the ADR with slug `suggestion-revert-vocabulary`.
|
|
46
70
|
*/
|
|
47
|
-
export const SUGGESTION_STATUSES = [
|
|
71
|
+
export const SUGGESTION_STATUSES = [
|
|
72
|
+
"open",
|
|
73
|
+
"accepted",
|
|
74
|
+
"rejected",
|
|
75
|
+
"reverted",
|
|
76
|
+
] as const;
|
|
48
77
|
export type SuggestionStatus = (typeof SUGGESTION_STATUSES)[number];
|
|
49
78
|
|
|
50
79
|
/**
|
|
@@ -24,6 +24,7 @@ export const openApiRoutes = {
|
|
|
24
24
|
'/api/chats/{id}/pin': ['DELETE', 'POST'],
|
|
25
25
|
'/api/comments': ['GET'],
|
|
26
26
|
'/api/comments/threads': ['POST'],
|
|
27
|
+
'/api/comments/threads/{threadId}': ['DELETE'],
|
|
27
28
|
'/api/comments/threads/{threadId}/accept-apply': ['POST'],
|
|
28
29
|
'/api/comments/threads/{threadId}/accept-complete': ['POST'],
|
|
29
30
|
'/api/comments/threads/{threadId}/accept-intent': ['POST'],
|
|
@@ -33,6 +34,7 @@ export const openApiRoutes = {
|
|
|
33
34
|
'/api/comments/threads/{threadId}/reject': ['POST'],
|
|
34
35
|
'/api/comments/threads/{threadId}/reopen': ['POST'],
|
|
35
36
|
'/api/comments/threads/{threadId}/resolve': ['POST'],
|
|
37
|
+
'/api/comments/threads/{threadId}/revert': ['POST'],
|
|
36
38
|
'/api/comments/{commentId}': ['DELETE', 'PATCH'],
|
|
37
39
|
'/api/company-md/access-requests/{id}/approve': ['POST'],
|
|
38
40
|
'/api/company-md/access-requests/{id}/deny': ['POST'],
|