@anchrd/intel-contract 0.24.0 → 0.26.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/dist/contract/board.d.ts +25 -0
- package/dist/contract/board.js +36 -0
- package/dist/contract/feed.d.ts +70 -0
- package/dist/contract/feed.js +84 -0
- package/package.json +5 -1
package/dist/contract/board.d.ts
CHANGED
|
@@ -107,3 +107,28 @@ export declare const BoardTaskUpdateInput: z.ZodObject<{
|
|
|
107
107
|
idempotencyKey: z.ZodString;
|
|
108
108
|
}, z.core.$strict>;
|
|
109
109
|
export type BoardTaskUpdateInput = z.infer<typeof BoardTaskUpdateInput>;
|
|
110
|
+
export declare const BoardAssignee: z.ZodObject<{
|
|
111
|
+
id: z.ZodString;
|
|
112
|
+
name: z.ZodString;
|
|
113
|
+
email: z.ZodEmail;
|
|
114
|
+
isMachine: z.ZodBoolean;
|
|
115
|
+
}, z.core.$strict>;
|
|
116
|
+
export type BoardAssignee = z.infer<typeof BoardAssignee>;
|
|
117
|
+
export declare const BoardAssigneeSearchInput: z.ZodObject<{
|
|
118
|
+
boardId: z.ZodString;
|
|
119
|
+
query: z.ZodString;
|
|
120
|
+
}, z.core.$strict>;
|
|
121
|
+
export type BoardAssigneeSearchInput = z.infer<typeof BoardAssigneeSearchInput>;
|
|
122
|
+
export declare const BoardAssigneeResolveInput: z.ZodObject<{
|
|
123
|
+
ids: z.ZodArray<z.ZodString>;
|
|
124
|
+
}, z.core.$strict>;
|
|
125
|
+
export type BoardAssigneeResolveInput = z.infer<typeof BoardAssigneeResolveInput>;
|
|
126
|
+
export declare const BoardAssigneeList: z.ZodObject<{
|
|
127
|
+
items: z.ZodArray<z.ZodObject<{
|
|
128
|
+
id: z.ZodString;
|
|
129
|
+
name: z.ZodString;
|
|
130
|
+
email: z.ZodEmail;
|
|
131
|
+
isMachine: z.ZodBoolean;
|
|
132
|
+
}, z.core.$strict>>;
|
|
133
|
+
}, z.core.$strict>;
|
|
134
|
+
export type BoardAssigneeList = z.infer<typeof BoardAssigneeList>;
|
package/dist/contract/board.js
CHANGED
|
@@ -188,3 +188,39 @@ export const BoardTaskUpdateInput = z.strictObject({
|
|
|
188
188
|
.describe("A node this card waits for. `null` clears the dependency."),
|
|
189
189
|
idempotencyKey: IdempotencyKey,
|
|
190
190
|
});
|
|
191
|
+
// ── Who a card can be given to ────────────────────────────────────────────────────────────────
|
|
192
|
+
// D70 (`~/Dev/anchrd/core/DECISIONS.md`): the people offered here are the ones who can actually open
|
|
193
|
+
// THIS board, never everyone who holds an account. The narrower answer is the point, and it is what
|
|
194
|
+
// makes the door safe to offer to anyone who can see the board.
|
|
195
|
+
//
|
|
196
|
+
// ⚠️ `email` travels because two colleagues share a first name more often than a picker can afford,
|
|
197
|
+
// and a list of indistinguishable "Anton" rows is not a picker. It is no wider than what gate hands
|
|
198
|
+
// any signed-in person at `POST /directory/search`; the narrowing this door adds is WHO appears,
|
|
199
|
+
// not WHAT is said about them.
|
|
200
|
+
//
|
|
201
|
+
// `isMachine` travels for the same reason gate carries it: whoever picks an assignee wants to know
|
|
202
|
+
// whether they are handing the card to a colleague or to an agent.
|
|
203
|
+
export const BoardAssignee = z.strictObject({
|
|
204
|
+
id: IntelId,
|
|
205
|
+
name: z.string(),
|
|
206
|
+
email: z.email(),
|
|
207
|
+
isMachine: z.boolean(),
|
|
208
|
+
});
|
|
209
|
+
export const BoardAssigneeSearchInput = z.strictObject({
|
|
210
|
+
boardId: IntelId.describe("The board whose people to offer. Only those who can open this board appear, so the same query against two boards can give two different answers."),
|
|
211
|
+
query: z
|
|
212
|
+
.string()
|
|
213
|
+
.describe("What was typed. Under two characters the answer is empty rather than everybody: a single letter is a listing under a different name."),
|
|
214
|
+
});
|
|
215
|
+
// ⚠️ A DIFFERENT question from the one above, and deliberately not board-scoped. This one names
|
|
216
|
+
// people who are ALREADY recorded on a card, so that a circle can carry initials instead of a raw
|
|
217
|
+
// id (`#258`). Someone whose access was withdrawn, or whose account was switched off, still has to
|
|
218
|
+
// be nameable: a card that reads as unassigned is a worse answer than the truth.
|
|
219
|
+
export const BoardAssigneeResolveInput = z.strictObject({
|
|
220
|
+
ids: z
|
|
221
|
+
.array(IntelId)
|
|
222
|
+
.min(1)
|
|
223
|
+
.max(100)
|
|
224
|
+
.describe("The ids to name. Ids that cannot be named are absent from the answer rather than reported, and more than a hundred is refused rather than silently shortened."),
|
|
225
|
+
});
|
|
226
|
+
export const BoardAssigneeList = z.strictObject({ items: z.array(BoardAssignee) });
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const FeedAction: z.ZodEnum<{
|
|
3
|
+
"node.create": "node.create";
|
|
4
|
+
"node.save": "node.save";
|
|
5
|
+
"node.update": "node.update";
|
|
6
|
+
"node.append": "node.append";
|
|
7
|
+
"node.archive": "node.archive";
|
|
8
|
+
"node.share": "node.share";
|
|
9
|
+
"node.revoke": "node.revoke";
|
|
10
|
+
}>;
|
|
11
|
+
export type FeedAction = z.infer<typeof FeedAction>;
|
|
12
|
+
export declare const FeedPathSegment: z.ZodObject<{
|
|
13
|
+
id: z.ZodString;
|
|
14
|
+
title: z.ZodString;
|
|
15
|
+
}, z.core.$strict>;
|
|
16
|
+
export type FeedPathSegment = z.infer<typeof FeedPathSegment>;
|
|
17
|
+
export declare const FeedEvent: z.ZodObject<{
|
|
18
|
+
id: z.ZodString;
|
|
19
|
+
actorId: z.ZodString;
|
|
20
|
+
action: z.ZodEnum<{
|
|
21
|
+
"node.create": "node.create";
|
|
22
|
+
"node.save": "node.save";
|
|
23
|
+
"node.update": "node.update";
|
|
24
|
+
"node.append": "node.append";
|
|
25
|
+
"node.archive": "node.archive";
|
|
26
|
+
"node.share": "node.share";
|
|
27
|
+
"node.revoke": "node.revoke";
|
|
28
|
+
}>;
|
|
29
|
+
nodeId: z.ZodString;
|
|
30
|
+
nodeTitle: z.ZodString;
|
|
31
|
+
path: z.ZodArray<z.ZodObject<{
|
|
32
|
+
id: z.ZodString;
|
|
33
|
+
title: z.ZodString;
|
|
34
|
+
}, z.core.$strict>>;
|
|
35
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
36
|
+
occurredAt: z.ZodISODateTime;
|
|
37
|
+
}, z.core.$strict>;
|
|
38
|
+
export type FeedEvent = z.infer<typeof FeedEvent>;
|
|
39
|
+
export declare const FeedCursor: z.ZodString;
|
|
40
|
+
export declare const FeedListRequest: z.ZodObject<{
|
|
41
|
+
actor: z.ZodOptional<z.ZodString>;
|
|
42
|
+
before: z.ZodOptional<z.ZodString>;
|
|
43
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
44
|
+
}, z.core.$strict>;
|
|
45
|
+
export type FeedListRequest = z.infer<typeof FeedListRequest>;
|
|
46
|
+
export declare const FeedListResponse: z.ZodObject<{
|
|
47
|
+
events: z.ZodArray<z.ZodObject<{
|
|
48
|
+
id: z.ZodString;
|
|
49
|
+
actorId: z.ZodString;
|
|
50
|
+
action: z.ZodEnum<{
|
|
51
|
+
"node.create": "node.create";
|
|
52
|
+
"node.save": "node.save";
|
|
53
|
+
"node.update": "node.update";
|
|
54
|
+
"node.append": "node.append";
|
|
55
|
+
"node.archive": "node.archive";
|
|
56
|
+
"node.share": "node.share";
|
|
57
|
+
"node.revoke": "node.revoke";
|
|
58
|
+
}>;
|
|
59
|
+
nodeId: z.ZodString;
|
|
60
|
+
nodeTitle: z.ZodString;
|
|
61
|
+
path: z.ZodArray<z.ZodObject<{
|
|
62
|
+
id: z.ZodString;
|
|
63
|
+
title: z.ZodString;
|
|
64
|
+
}, z.core.$strict>>;
|
|
65
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
66
|
+
occurredAt: z.ZodISODateTime;
|
|
67
|
+
}, z.core.$strict>>;
|
|
68
|
+
nextCursor: z.ZodNullable<z.ZodString>;
|
|
69
|
+
}, z.core.$strict>;
|
|
70
|
+
export type FeedListResponse = z.infer<typeof FeedListResponse>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { IntelId, IsoDateTime } from "./contract.js";
|
|
3
|
+
// What a person did, in the words a person would use. This is a POSITIVE LIST and not the set of
|
|
4
|
+
// actions the journal carries (#740).
|
|
5
|
+
//
|
|
6
|
+
// ⚠️ Two omissions are the point of the list, not an oversight. `node.version` is written by the
|
|
7
|
+
// same batch as `node.save`, so admitting both would show every edit twice — and because this feed
|
|
8
|
+
// does not group anything yet, twice means two cards. `node.link` and `node.import` are machine
|
|
9
|
+
// traces of a save rather than something anybody did.
|
|
10
|
+
//
|
|
11
|
+
// ⚠️ And it stays a positive list. "Everything except" would silently admit whatever the next
|
|
12
|
+
// migration starts writing, on a surface whose whole job is to be readable.
|
|
13
|
+
export const FeedAction = z.enum([
|
|
14
|
+
"node.create",
|
|
15
|
+
"node.save",
|
|
16
|
+
"node.update",
|
|
17
|
+
"node.append",
|
|
18
|
+
"node.archive",
|
|
19
|
+
"node.share",
|
|
20
|
+
"node.revoke",
|
|
21
|
+
]);
|
|
22
|
+
// One step on the way down to the node, from the root. It carries the id as well as the title
|
|
23
|
+
// because the surface links each step, and two folders may share a name.
|
|
24
|
+
export const FeedPathSegment = z.strictObject({
|
|
25
|
+
id: IntelId,
|
|
26
|
+
title: z.string().min(1),
|
|
27
|
+
});
|
|
28
|
+
// One card's worth of journal.
|
|
29
|
+
//
|
|
30
|
+
// ⚠️ `nodeTitle` and `path` are read from `nodes` at query time, so they are TODAY's title and
|
|
31
|
+
// today's place — not the ones the node had when the event happened. That is the right answer for a
|
|
32
|
+
// feed (the reader wants to find the thing now) and the wrong one for an audit trail, which is one
|
|
33
|
+
// more reason these are two doors and not one.
|
|
34
|
+
//
|
|
35
|
+
// `metadata` is whatever the write site recorded, untyped on purpose: a schema per action would
|
|
36
|
+
// have to be kept in step with eleven write sites and would go stale in silence. What the surface
|
|
37
|
+
// needs from it today is `sequence` on a `node.save`, which is the version number behind the card's
|
|
38
|
+
// button.
|
|
39
|
+
export const FeedEvent = z.strictObject({
|
|
40
|
+
id: IntelId,
|
|
41
|
+
actorId: z.string().min(1),
|
|
42
|
+
action: FeedAction,
|
|
43
|
+
nodeId: IntelId,
|
|
44
|
+
nodeTitle: z.string().min(1),
|
|
45
|
+
path: z.array(FeedPathSegment),
|
|
46
|
+
metadata: z.record(z.string(), z.unknown()),
|
|
47
|
+
occurredAt: IsoDateTime,
|
|
48
|
+
});
|
|
49
|
+
// ⚠️ Opaque BY CONTRACT, like the audit cursor and for the same reason: it is a PAIR
|
|
50
|
+
// (`occurredAt`, `id`), and a timestamp alone cannot separate two events written in the same
|
|
51
|
+
// millisecond — the normal case inside one batch. A reader continuing on the timestamp alone skips
|
|
52
|
+
// the second one with no error and no log.
|
|
53
|
+
//
|
|
54
|
+
// ⚠️ It is deliberately NOT interchangeable with an audit cursor, although both encode the same
|
|
55
|
+
// pair. The two doors walk the journal in opposite directions, so handing one's position to the
|
|
56
|
+
// other reads on from the wrong end — and would look like a feed that suddenly starts at the
|
|
57
|
+
// beginning of time rather than like a mistake. The encoding therefore carries its own marker and
|
|
58
|
+
// the other door's cursor is refused.
|
|
59
|
+
export const FeedCursor = z.string().min(1).max(400);
|
|
60
|
+
export const FeedListRequest = z.strictObject({
|
|
61
|
+
actor: z
|
|
62
|
+
.string()
|
|
63
|
+
.min(1)
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Show only what this person or agent did. Omit it for everybody whose work you may see anyway."),
|
|
66
|
+
before: FeedCursor.optional().describe("Where to continue: the `nextCursor` of a previous answer, passed back unchanged. Omit it to start at the most recent event. Do not build one — it is opaque on purpose, and a cursor from `audit_list` is refused rather than followed backwards."),
|
|
67
|
+
limit: z
|
|
68
|
+
.number()
|
|
69
|
+
.int()
|
|
70
|
+
.min(1)
|
|
71
|
+
.max(100)
|
|
72
|
+
.default(30)
|
|
73
|
+
.describe("How many events to return at most. The answer may be shorter."),
|
|
74
|
+
});
|
|
75
|
+
// `nextCursor` is present exactly when another page may exist, and it is the cursor of the LAST
|
|
76
|
+
// returned row.
|
|
77
|
+
//
|
|
78
|
+
// ⚠️ Present does not promise the next page is non-empty: the rows a reader may see can shrink
|
|
79
|
+
// between calls. Treating "cursor present" as "there is more" is fair; treating an empty answer as
|
|
80
|
+
// "you have reached the beginning of the journal" is not.
|
|
81
|
+
export const FeedListResponse = z.strictObject({
|
|
82
|
+
events: z.array(FeedEvent),
|
|
83
|
+
nextCursor: FeedCursor.nullable(),
|
|
84
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anchrd/intel-contract",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
"types": "./dist/contract/bundle.d.ts",
|
|
29
29
|
"default": "./dist/contract/bundle.js"
|
|
30
30
|
},
|
|
31
|
+
"./feed": {
|
|
32
|
+
"types": "./dist/contract/feed.d.ts",
|
|
33
|
+
"default": "./dist/contract/feed.js"
|
|
34
|
+
},
|
|
31
35
|
"./flow": {
|
|
32
36
|
"types": "./dist/contract/flow.d.ts",
|
|
33
37
|
"default": "./dist/contract/flow.js"
|