@skillcdn/console 0.4.3 → 0.4.4
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/README.md +4 -4
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +3 -3
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +95 -20
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +4 -2
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +32 -9
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +17 -14
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/plugin-check.d.ts +5 -1
- package/dist/cli/plugin-check.d.ts.map +1 -1
- package/dist/cli/plugin-check.js +23 -4
- package/dist/cli/plugin-check.js.map +1 -1
- package/dist/cli/plugin-cli.d.ts.map +1 -1
- package/dist/cli/plugin-cli.js +10 -1
- package/dist/cli/plugin-cli.js.map +1 -1
- package/dist/cli/plugin-init.d.ts +2 -0
- package/dist/cli/plugin-init.d.ts.map +1 -1
- package/dist/cli/plugin-init.js +6 -0
- package/dist/cli/plugin-init.js.map +1 -1
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -1
- package/dist/compat.js +1 -1
- package/dist/components/board.d.ts +8 -0
- package/dist/components/board.d.ts.map +1 -1
- package/dist/components/board.js +3 -3
- package/dist/components/board.js.map +1 -1
- package/dist/components/decision-form.d.ts +6 -0
- package/dist/components/decision-form.d.ts.map +1 -1
- package/dist/components/decision-form.js +23 -5
- package/dist/components/decision-form.js.map +1 -1
- package/dist/components/decision-list.d.ts +5 -1
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +25 -5
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +7 -1
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/goals.d.ts.map +1 -1
- package/dist/components/goals.js +2 -2
- package/dist/components/goals.js.map +1 -1
- package/dist/components/home.d.ts +35 -0
- package/dist/components/home.d.ts.map +1 -0
- package/dist/components/home.js +326 -0
- package/dist/components/home.js.map +1 -0
- package/dist/components/pack-connect.d.ts.map +1 -1
- package/dist/components/pack-connect.js +5 -4
- package/dist/components/pack-connect.js.map +1 -1
- package/dist/components/people.d.ts +2 -0
- package/dist/components/people.d.ts.map +1 -1
- package/dist/components/people.js +15 -2
- package/dist/components/people.js.map +1 -1
- package/dist/components/plugin-frame.d.ts +3 -12
- package/dist/components/plugin-frame.d.ts.map +1 -1
- package/dist/components/plugin-frame.js +53 -20
- package/dist/components/plugin-frame.js.map +1 -1
- package/dist/components/plugin-settings.d.ts.map +1 -1
- package/dist/components/plugin-settings.js +151 -26
- package/dist/components/plugin-settings.js.map +1 -1
- package/dist/components/projects.d.ts +0 -8
- package/dist/components/projects.d.ts.map +1 -1
- package/dist/components/projects.js +1 -13
- package/dist/components/projects.js.map +1 -1
- package/dist/components/task-view.d.ts.map +1 -1
- package/dist/components/task-view.js +2 -1
- package/dist/components/task-view.js.map +1 -1
- package/dist/components/ui.d.ts.map +1 -1
- package/dist/components/ui.js +5 -2
- package/dist/components/ui.js.map +1 -1
- package/dist/connect-page.d.ts.map +1 -1
- package/dist/connect-page.js +5 -6
- package/dist/connect-page.js.map +1 -1
- package/dist/console.css +761 -191
- package/dist/console.d.ts +10 -3
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +33 -13
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +4 -2
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +5 -0
- package/dist/data.js.map +1 -1
- package/dist/docs-page.d.ts.map +1 -1
- package/dist/docs-page.js +6 -5
- package/dist/docs-page.js.map +1 -1
- package/dist/home-tiles.d.ts +92 -0
- package/dist/home-tiles.d.ts.map +1 -0
- package/dist/home-tiles.js +294 -0
- package/dist/home-tiles.js.map +1 -0
- package/dist/i18n/en.d.ts +174 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +175 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/index.d.ts +4 -2
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +10 -11
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +170 -3
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +6 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +6 -0
- package/dist/limits.js.map +1 -1
- package/dist/plugin/bridge.d.ts +44 -3
- package/dist/plugin/bridge.d.ts.map +1 -1
- package/dist/plugin/bridge.js +26 -0
- package/dist/plugin/bridge.js.map +1 -1
- package/dist/plugin/capabilities.d.ts +7 -1
- package/dist/plugin/capabilities.d.ts.map +1 -1
- package/dist/plugin/capabilities.js +14 -1
- package/dist/plugin/capabilities.js.map +1 -1
- package/dist/plugin/context.d.ts +18 -1
- package/dist/plugin/context.d.ts.map +1 -1
- package/dist/plugin/define.d.ts +1 -0
- package/dist/plugin/define.d.ts.map +1 -1
- package/dist/plugin/define.js +1 -1
- package/dist/plugin/define.js.map +1 -1
- package/dist/plugin/limits.d.ts +5 -2
- package/dist/plugin/limits.d.ts.map +1 -1
- package/dist/plugin/limits.js +5 -2
- package/dist/plugin/limits.js.map +1 -1
- package/dist/plugin/manifest.d.ts +83 -6
- package/dist/plugin/manifest.d.ts.map +1 -1
- package/dist/plugin/manifest.js +111 -7
- package/dist/plugin/manifest.js.map +1 -1
- package/dist/plugin/rest-schemas.d.ts +76 -0
- package/dist/plugin/rest-schemas.d.ts.map +1 -1
- package/dist/plugin/rest-schemas.js +23 -2
- package/dist/plugin/rest-schemas.js.map +1 -1
- package/dist/plugin/sandbox.d.ts +4 -3
- package/dist/plugin/sandbox.d.ts.map +1 -1
- package/dist/plugin/sandbox.js +33 -6
- package/dist/plugin/sandbox.js.map +1 -1
- package/dist/plugin/semver.d.ts +26 -0
- package/dist/plugin/semver.d.ts.map +1 -0
- package/dist/plugin/semver.js +76 -0
- package/dist/plugin/semver.js.map +1 -0
- package/dist/plugin/testing.d.ts.map +1 -1
- package/dist/plugin/testing.js +2 -1
- package/dist/plugin/testing.js.map +1 -1
- package/dist/plugin/web.d.ts +10 -1
- package/dist/plugin/web.d.ts.map +1 -1
- package/dist/plugin/web.js +34 -0
- package/dist/plugin/web.js.map +1 -1
- package/dist/plugin.d.ts +3 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +3 -2
- package/dist/plugin.js.map +1 -1
- package/dist/rules.d.ts +2 -2
- package/dist/rules.js +3 -3
- package/dist/schemas.d.ts +497 -2
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +96 -9
- package/dist/schemas.js.map +1 -1
- package/dist/use-action.d.ts +8 -2
- package/dist/use-action.d.ts.map +1 -1
- package/dist/use-action.js +13 -6
- package/dist/use-action.js.map +1 -1
- package/dist/vocabulary.d.ts +3 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +3 -0
- package/dist/vocabulary.js.map +1 -1
- package/docs/specs/cli.md +4 -3
- package/docs/specs/plugins.md +46 -33
- package/docs/specs/rest.md +16 -15
- package/package.json +1 -1
- package/skills/building-a-plugin/SKILL.md +8 -7
- package/skills/planning-the-board/references/setting-up.md +2 -1
- package/skills/working-the-board/SKILL.md +3 -3
- package/src/api.ts +6 -0
- package/src/cli/cli.ts +124 -19
- package/src/cli/format.ts +42 -9
- package/src/cli/help.ts +17 -13
- package/src/cli/plugin-check.ts +41 -2
- package/src/cli/plugin-cli.ts +10 -1
- package/src/cli/plugin-init.ts +8 -0
- package/src/client.ts +8 -0
- package/src/compat.ts +1 -1
- package/src/components/board.tsx +3 -3
- package/src/components/decision-form.tsx +75 -19
- package/src/components/decision-list.tsx +45 -13
- package/src/components/event-feed.tsx +7 -1
- package/src/components/goals.tsx +4 -1
- package/src/components/home.tsx +615 -0
- package/src/components/pack-connect.tsx +5 -4
- package/src/components/people.tsx +136 -54
- package/src/components/plugin-frame.tsx +62 -63
- package/src/components/plugin-settings.tsx +428 -87
- package/src/components/projects.tsx +0 -55
- package/src/components/task-view.tsx +7 -0
- package/src/components/ui.tsx +6 -2
- package/src/connect-page.tsx +5 -6
- package/src/console.tsx +80 -30
- package/src/data.ts +9 -2
- package/src/docs-page.tsx +6 -5
- package/src/home-tiles.ts +438 -0
- package/src/i18n/en.ts +178 -0
- package/src/i18n/index.ts +17 -14
- package/src/i18n/ko.ts +174 -3
- package/src/index.ts +7 -5
- package/src/limits.ts +6 -0
- package/src/plugin/bridge.ts +77 -1
- package/src/plugin/capabilities.ts +17 -1
- package/src/plugin/context.ts +18 -1
- package/src/plugin/define.ts +4 -1
- package/src/plugin/limits.ts +5 -2
- package/src/plugin/manifest.ts +154 -10
- package/src/plugin/rest-schemas.ts +27 -1
- package/src/plugin/sandbox.ts +36 -8
- package/src/plugin/semver.ts +89 -0
- package/src/plugin/testing.ts +2 -1
- package/src/plugin/web.ts +49 -0
- package/src/plugin.ts +18 -0
- package/src/rules.ts +3 -3
- package/src/schemas.ts +118 -10
- package/src/styles/console.css +761 -191
- package/src/use-action.ts +16 -7
- package/src/vocabulary.ts +4 -0
- package/template/plugin/console-plugin.json +3 -1
package/src/plugin/sandbox.ts
CHANGED
|
@@ -13,6 +13,15 @@ export const FORBIDDEN_BUILTINS: readonly string[] = [
|
|
|
13
13
|
"dgram",
|
|
14
14
|
"dns",
|
|
15
15
|
"dns/promises",
|
|
16
|
+
// The same network, under the names Node keeps for compatibility.
|
|
17
|
+
"_http_agent",
|
|
18
|
+
"_http_client",
|
|
19
|
+
"_http_common",
|
|
20
|
+
"_http_incoming",
|
|
21
|
+
"_http_outgoing",
|
|
22
|
+
"_http_server",
|
|
23
|
+
"_tls_common",
|
|
24
|
+
"_tls_wrap",
|
|
16
25
|
"child_process",
|
|
17
26
|
"cluster",
|
|
18
27
|
"worker_threads",
|
|
@@ -67,6 +76,15 @@ export interface HardenedThread {
|
|
|
67
76
|
readonly removed: readonly string[];
|
|
68
77
|
}
|
|
69
78
|
|
|
79
|
+
/** What a plugin is told of a built-in it may not have, by whichever way it asked. */
|
|
80
|
+
function refusedBuiltin(specifier: string): Error {
|
|
81
|
+
const error = new Error(
|
|
82
|
+
`the host refuses ${specifier}: a plugin reaches the console through ctx alone`,
|
|
83
|
+
);
|
|
84
|
+
(error as { code?: string }).code = "ERR_PLUGIN_FORBIDDEN_BUILTIN";
|
|
85
|
+
return error;
|
|
86
|
+
}
|
|
87
|
+
|
|
70
88
|
/** The module hook: refuses the forbidden built-ins and lets everything else resolve as it would. */
|
|
71
89
|
export function forbiddingResolve<C, R>(
|
|
72
90
|
specifier: string,
|
|
@@ -74,19 +92,16 @@ export function forbiddingResolve<C, R>(
|
|
|
74
92
|
nextResolve: (specifier: string, context: C) => R,
|
|
75
93
|
): R {
|
|
76
94
|
if (isForbiddenBuiltin(specifier)) {
|
|
77
|
-
|
|
78
|
-
`the host refuses ${specifier}: a plugin reaches the console through ctx alone`,
|
|
79
|
-
);
|
|
80
|
-
(error as { code?: string }).code = "ERR_PLUGIN_FORBIDDEN_BUILTIN";
|
|
81
|
-
throw error;
|
|
95
|
+
throw refusedBuiltin(specifier);
|
|
82
96
|
}
|
|
83
97
|
return nextResolve(specifier, context);
|
|
84
98
|
}
|
|
85
99
|
|
|
86
100
|
/**
|
|
87
|
-
* Hardens the current thread: refuses the forbidden built-ins through the module hooks
|
|
88
|
-
* the globals, empties the environment,
|
|
89
|
-
* before importing plugin code; it reaches
|
|
101
|
+
* Hardens the current thread: refuses the forbidden built-ins through the module hooks and through
|
|
102
|
+
* `process.getBuiltinModule`, which goes round them, removes the globals, empties the environment,
|
|
103
|
+
* and blocks `process.binding`. Call it in a worker thread before importing plugin code; it reaches
|
|
104
|
+
* nothing outside that thread.
|
|
90
105
|
*/
|
|
91
106
|
export function hardenThread(
|
|
92
107
|
moduleApi: Pick<typeof import("node:module"), "registerHooks">,
|
|
@@ -112,6 +127,19 @@ export function hardenThread(
|
|
|
112
127
|
for (const name of ["binding", "_linkedBinding", "dlopen"]) {
|
|
113
128
|
Object.defineProperty(proc, name, { value: refuse, writable: false, configurable: false });
|
|
114
129
|
}
|
|
130
|
+
const getBuiltinModule = proc.getBuiltinModule;
|
|
131
|
+
if (typeof getBuiltinModule === "function") {
|
|
132
|
+
Object.defineProperty(proc, "getBuiltinModule", {
|
|
133
|
+
value: (specifier: string): unknown => {
|
|
134
|
+
if (isForbiddenBuiltin(specifier)) {
|
|
135
|
+
throw refusedBuiltin(specifier);
|
|
136
|
+
}
|
|
137
|
+
return getBuiltinModule.call(proc, specifier);
|
|
138
|
+
},
|
|
139
|
+
writable: false,
|
|
140
|
+
configurable: false,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
115
143
|
const env = proc.env as Record<string, unknown> | undefined;
|
|
116
144
|
if (env !== undefined) {
|
|
117
145
|
for (const key of Object.keys(env)) {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Versions and ranges as a manifest spells them (docs/specs/plugins.md, "The manifest"): the
|
|
2
|
+
// range of another plugin's version in `requires`, and the range of the console's version in
|
|
3
|
+
// `console`. Read loosely, as npm spells the common ones, with no library: a plugin declares
|
|
4
|
+
// `>=0.4.3 <0.6`, and the console, the authoring command and the tests compare the same way.
|
|
5
|
+
|
|
6
|
+
export type Triple = readonly [number, number, number];
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A version read loosely: `0.4.3`, `v0.4.3`, `0.6` (as `0.6.0`), `1` (as `1.0.0`), a prerelease or
|
|
10
|
+
* a build after the numbers ignored; nothing for anything else.
|
|
11
|
+
*/
|
|
12
|
+
export function parseLooseVersion(text: string): Triple | undefined {
|
|
13
|
+
const match = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:[-+].*)?$/.exec(text.trim());
|
|
14
|
+
return match === null
|
|
15
|
+
? undefined
|
|
16
|
+
: [Number(match[1]), Number(match[2] ?? "0"), Number(match[3] ?? "0")];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function compareTriples(a: Triple, b: Triple): number {
|
|
20
|
+
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Whether a version is within a range as npm spells the common ones: `*`, exact, `^`, `~`, `>=`,
|
|
25
|
+
* `>`, `<=`, `<`, several with spaces between (all must hold) and alternatives with `||` between
|
|
26
|
+
* (one must hold). A partial version in the range is read as `.0` filled in: `<0.6` is `<0.6.0`.
|
|
27
|
+
*/
|
|
28
|
+
export function satisfiesRange(version: string, range: string): boolean {
|
|
29
|
+
const have = parseLooseVersion(version);
|
|
30
|
+
const trimmed = range.trim();
|
|
31
|
+
if (trimmed === "*" || trimmed === "" || trimmed === "x" || trimmed === "latest") {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (have === undefined) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return trimmed.split(/\s*\|\|\s*/).some((alternative) =>
|
|
38
|
+
alternative.split(/\s+/).every((part) => {
|
|
39
|
+
const operator = /^(\^|~|>=|<=|>|<|=)?(.*)$/.exec(part);
|
|
40
|
+
const wanted = parseLooseVersion(operator?.[2] ?? "");
|
|
41
|
+
if (wanted === undefined) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
switch (operator?.[1]) {
|
|
45
|
+
case "^":
|
|
46
|
+
return (
|
|
47
|
+
compareTriples(have, wanted) >= 0 &&
|
|
48
|
+
(wanted[0] === 0 ? have[0] === 0 && have[1] === wanted[1] : have[0] === wanted[0])
|
|
49
|
+
);
|
|
50
|
+
case "~":
|
|
51
|
+
return (
|
|
52
|
+
compareTriples(have, wanted) >= 0 && have[0] === wanted[0] && have[1] === wanted[1]
|
|
53
|
+
);
|
|
54
|
+
case ">=":
|
|
55
|
+
return compareTriples(have, wanted) >= 0;
|
|
56
|
+
case ">":
|
|
57
|
+
return compareTriples(have, wanted) > 0;
|
|
58
|
+
case "<=":
|
|
59
|
+
return compareTriples(have, wanted) <= 0;
|
|
60
|
+
case "<":
|
|
61
|
+
return compareTriples(have, wanted) < 0;
|
|
62
|
+
default:
|
|
63
|
+
return compareTriples(have, wanted) === 0;
|
|
64
|
+
}
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The version a dependency spells, read loosely: `^0.4.3`, `~0.4.3`, `>=0.4.3` and `0.4.3` are
|
|
71
|
+
* all `0.4.3`; `workspace:*`, `*`, `latest` and a tag name none.
|
|
72
|
+
*/
|
|
73
|
+
export function versionOfDependency(spec: string): Triple | undefined {
|
|
74
|
+
return parseLooseVersion(spec.trim().replace(/^(?:\^|~|>=|=)\s*/, ""));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function formatTriple(version: Triple): string {
|
|
78
|
+
return `${version[0]}.${version[1]}.${version[2]}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The range of consoles a plugin written against `version` runs on, as `console-plugin init`
|
|
83
|
+
* writes it: from that version on, within its line, since the plugin API moves with the console's
|
|
84
|
+
* line before 1.0 (`>=0.4.3 <0.5.0`), and within its major from 1.0 (`>=1.2.0 <2.0.0`).
|
|
85
|
+
*/
|
|
86
|
+
export function lineRangeFrom(version: Triple): string {
|
|
87
|
+
const below: Triple = version[0] === 0 ? [0, version[1] + 1, 0] : [version[0] + 1, 0, 0];
|
|
88
|
+
return `>=${formatTriple(version)} <${formatTriple(below)}`;
|
|
89
|
+
}
|
package/src/plugin/testing.ts
CHANGED
|
@@ -519,7 +519,7 @@ function refusingConsole(given: TestConsoleOptions | undefined): PluginConsole {
|
|
|
519
519
|
return {
|
|
520
520
|
workspace:
|
|
521
521
|
given?.workspace ?? (async () => forbidden("console.workspace", "console.workspace")),
|
|
522
|
-
people: fill("people", ["list", "get"], given?.people),
|
|
522
|
+
people: fill("people", ["list", "get", "granted"], given?.people),
|
|
523
523
|
projects: fill("projects", ["list", "get", "members"], given?.projects),
|
|
524
524
|
tasks: fill("tasks", ["list", "get", "create", "update", "comment"], given?.tasks),
|
|
525
525
|
decisions: fill("decisions", ["list", "get", "ask"], given?.decisions),
|
|
@@ -538,6 +538,7 @@ function build(store: Store, options: TestContextOptions, caller: PluginCaller):
|
|
|
538
538
|
version: "0.0.0",
|
|
539
539
|
dataVersion: 1,
|
|
540
540
|
settings: {},
|
|
541
|
+
capabilities: [],
|
|
541
542
|
board: null,
|
|
542
543
|
projects: "all",
|
|
543
544
|
...options.plugin,
|
package/src/plugin/web.ts
CHANGED
|
@@ -11,11 +11,13 @@ import {
|
|
|
11
11
|
type BridgeFile,
|
|
12
12
|
type BridgeInit,
|
|
13
13
|
type BridgeMethod,
|
|
14
|
+
type BridgePicture,
|
|
14
15
|
type BridgeToPage,
|
|
15
16
|
isBridgePath,
|
|
16
17
|
isBridgeToFrame,
|
|
17
18
|
MAX_BRIDGE_TITLE_LENGTH,
|
|
18
19
|
PLUGIN_BRIDGE_VERSION,
|
|
20
|
+
type PluginWidgetSizeName,
|
|
19
21
|
} from "./bridge.js";
|
|
20
22
|
import type { PluginRecord, RecordPage, RecordVersion } from "./context.js";
|
|
21
23
|
|
|
@@ -111,6 +113,13 @@ export interface PluginFrame {
|
|
|
111
113
|
type: string,
|
|
112
114
|
): Promise<{ readonly handle: string }>;
|
|
113
115
|
};
|
|
116
|
+
readonly people: {
|
|
117
|
+
/**
|
|
118
|
+
* An address to put in an image, of the person of that id (ADR-0063), with `people:read`; or
|
|
119
|
+
* `null` for a person with no picture to show. Kept for the life of the frame.
|
|
120
|
+
*/
|
|
121
|
+
picture(id: string): Promise<string | null>;
|
|
122
|
+
};
|
|
114
123
|
/** The console's page follows a path of the console's in place; a URL is refused. */
|
|
115
124
|
navigate(path: string): void;
|
|
116
125
|
/** The console's page downloads a file of this plugin. */
|
|
@@ -125,12 +134,21 @@ export interface PluginFrame {
|
|
|
125
134
|
onTheme(handler: (theme: Readonly<Record<string, string>>) => void): () => void;
|
|
126
135
|
/** The console navigated within this page's address. */
|
|
127
136
|
onPath(handler: (path: string) => void): () => void;
|
|
137
|
+
/** The person gave the widget another size (ADR-0059); `context.size` was the first. */
|
|
138
|
+
onSize(handler: (size: PluginWidgetSizeName) => void): () => void;
|
|
128
139
|
/** Stops listening to the console's page. */
|
|
129
140
|
close(): void;
|
|
130
141
|
}
|
|
131
142
|
|
|
132
143
|
const DEFAULT_CONNECT_MS = 10_000;
|
|
133
144
|
|
|
145
|
+
/** What a page is told for a person with no picture to show, which is no failure of the page's. */
|
|
146
|
+
const NO_PICTURE: ReadonlySet<string> = new Set([
|
|
147
|
+
"plugin.not_found",
|
|
148
|
+
"picture.refused",
|
|
149
|
+
"picture.unavailable",
|
|
150
|
+
]);
|
|
151
|
+
|
|
134
152
|
function globalWindow(): FrameWindow {
|
|
135
153
|
const candidate = (globalThis as { window?: FrameWindow }).window;
|
|
136
154
|
if (candidate === undefined) {
|
|
@@ -219,7 +237,9 @@ export function connect(options: ConnectOptions = {}): Promise<PluginFrame> {
|
|
|
219
237
|
const eventHandlers = new Set<(kind: BridgeEventKind, data: unknown) => void>();
|
|
220
238
|
const themeHandlers = new Set<(theme: Readonly<Record<string, string>>) => void>();
|
|
221
239
|
const pathHandlers = new Set<(path: string) => void>();
|
|
240
|
+
const sizeHandlers = new Set<(size: PluginWidgetSizeName) => void>();
|
|
222
241
|
const subscribed = new Set<BridgeEventKind>();
|
|
242
|
+
const pictures = new Map<string, Promise<string | null>>();
|
|
223
243
|
let counter = 0;
|
|
224
244
|
let stopHeight: (() => void) | undefined;
|
|
225
245
|
|
|
@@ -301,6 +321,26 @@ export function connect(options: ConnectOptions = {}): Promise<PluginFrame> {
|
|
|
301
321
|
}>;
|
|
302
322
|
},
|
|
303
323
|
},
|
|
324
|
+
people: {
|
|
325
|
+
picture: (id) => {
|
|
326
|
+
const kept = pictures.get(id);
|
|
327
|
+
if (kept !== undefined) {
|
|
328
|
+
return kept;
|
|
329
|
+
}
|
|
330
|
+
const asked = (call("people.picture", { id }) as Promise<BridgePicture>).then(
|
|
331
|
+
({ type, bytes }) => URL.createObjectURL(new Blob([bytes], { type })),
|
|
332
|
+
(error: unknown) => {
|
|
333
|
+
if (error instanceof PluginBridgeError && NO_PICTURE.has(error.code)) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
pictures.delete(id);
|
|
337
|
+
throw error;
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
pictures.set(id, asked);
|
|
341
|
+
return asked;
|
|
342
|
+
},
|
|
343
|
+
},
|
|
304
344
|
navigate: (path) => {
|
|
305
345
|
if (!isBridgePath(path)) {
|
|
306
346
|
throw new PluginBridgeError({
|
|
@@ -338,6 +378,10 @@ export function connect(options: ConnectOptions = {}): Promise<PluginFrame> {
|
|
|
338
378
|
pathHandlers.add(handler);
|
|
339
379
|
return () => pathHandlers.delete(handler);
|
|
340
380
|
},
|
|
381
|
+
onSize: (handler) => {
|
|
382
|
+
sizeHandlers.add(handler);
|
|
383
|
+
return () => sizeHandlers.delete(handler);
|
|
384
|
+
},
|
|
341
385
|
close: () => {
|
|
342
386
|
win.removeEventListener("message", listener);
|
|
343
387
|
stopHeight?.();
|
|
@@ -393,6 +437,11 @@ export function connect(options: ConnectOptions = {}): Promise<PluginFrame> {
|
|
|
393
437
|
handler(message.path);
|
|
394
438
|
}
|
|
395
439
|
return;
|
|
440
|
+
case "size":
|
|
441
|
+
for (const handler of sizeHandlers) {
|
|
442
|
+
handler(message.size);
|
|
443
|
+
}
|
|
444
|
+
return;
|
|
396
445
|
default:
|
|
397
446
|
return;
|
|
398
447
|
}
|
package/src/plugin.ts
CHANGED
|
@@ -29,6 +29,7 @@ export {
|
|
|
29
29
|
type CapabilityWords,
|
|
30
30
|
capabilityWords,
|
|
31
31
|
isPluginCapability,
|
|
32
|
+
OUTBOUND_WEBRTC,
|
|
32
33
|
outboundAllows,
|
|
33
34
|
PLUGIN_CAPABILITIES,
|
|
34
35
|
PLUGIN_CAPABILITY_PREFIXES,
|
|
@@ -72,8 +73,10 @@ export { PluginDefinitionError } from "./plugin/definition-error.js";
|
|
|
72
73
|
export * from "./plugin/limits.js";
|
|
73
74
|
export {
|
|
74
75
|
CURRENT_PLUGIN_API,
|
|
76
|
+
capabilitiesAsked,
|
|
75
77
|
isPluginKey,
|
|
76
78
|
type Localized,
|
|
79
|
+
legacyWidgetSize,
|
|
77
80
|
localizedText,
|
|
78
81
|
type ManifestParse,
|
|
79
82
|
PLUGIN_API_MAJORS,
|
|
@@ -83,6 +86,8 @@ export {
|
|
|
83
86
|
PLUGIN_PAGE_LEVELS,
|
|
84
87
|
PLUGIN_SETTING_TYPES,
|
|
85
88
|
PLUGIN_WIDGET_PLACES,
|
|
89
|
+
PLUGIN_WIDGET_SIZE_CELLS,
|
|
90
|
+
PLUGIN_WIDGET_SIZE_NAMES,
|
|
86
91
|
PLUGIN_WIDGET_SIZES,
|
|
87
92
|
type PluginBoard,
|
|
88
93
|
type PluginBoardNeeds,
|
|
@@ -93,16 +98,20 @@ export {
|
|
|
93
98
|
type PluginPageLevel,
|
|
94
99
|
type PluginRole,
|
|
95
100
|
type PluginSetting,
|
|
101
|
+
type PluginSettingGroup,
|
|
96
102
|
type PluginSettingType,
|
|
97
103
|
type PluginWidget,
|
|
98
104
|
type PluginWidgetPlace,
|
|
99
105
|
type PluginWidgetSize,
|
|
106
|
+
type PluginWidgetSizeName,
|
|
100
107
|
parsePluginManifest,
|
|
101
108
|
pluginManifestSchema,
|
|
102
109
|
RESERVED_COMMAND_NAMES,
|
|
103
110
|
RESERVED_PAGE_KEYS,
|
|
104
111
|
RESERVED_PLUGIN_KEYS,
|
|
105
112
|
SETTING_KEY_PATTERN,
|
|
113
|
+
TIME_OF_DAY,
|
|
114
|
+
widgetSizesOf,
|
|
106
115
|
} from "./plugin/manifest.js";
|
|
107
116
|
export {
|
|
108
117
|
isPluginError,
|
|
@@ -124,3 +133,12 @@ export {
|
|
|
124
133
|
REMOVED_GLOBALS,
|
|
125
134
|
removedGlobalsIn,
|
|
126
135
|
} from "./plugin/sandbox.js";
|
|
136
|
+
export {
|
|
137
|
+
compareTriples,
|
|
138
|
+
formatTriple,
|
|
139
|
+
lineRangeFrom,
|
|
140
|
+
parseLooseVersion,
|
|
141
|
+
satisfiesRange,
|
|
142
|
+
type Triple,
|
|
143
|
+
versionOfDependency,
|
|
144
|
+
} from "./plugin/semver.js";
|
package/src/rules.ts
CHANGED
|
@@ -83,7 +83,7 @@ export function isReferenceRules(body: string): boolean {
|
|
|
83
83
|
/** What stands in the rules for the language of the person the agent works for. */
|
|
84
84
|
export const RULES_LANGUAGE_PLACEHOLDER = "{language}";
|
|
85
85
|
|
|
86
|
-
/** What the rules say
|
|
86
|
+
/** What the rules say for a person with no language yet: the task's, as the people of the board wrote it. */
|
|
87
87
|
const TASKS_LANGUAGE = "the language the task is written in";
|
|
88
88
|
|
|
89
89
|
/** The name of a language in English, from its tag (`ko` is Korean); the tag itself when it is not one. */
|
|
@@ -101,8 +101,8 @@ export function languageNameOf(tag: string): string {
|
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
103
|
* The rules as they read for a person: `{language}` filled with the name of the language they
|
|
104
|
-
*
|
|
105
|
-
*
|
|
104
|
+
* read in (ADR-0065), or with the task's language while they have none. The command fills them
|
|
105
|
+
* for the person whose token it holds; the pages for whoever reads them.
|
|
106
106
|
*/
|
|
107
107
|
export function fillRules(body: string, language: string | null | undefined): string {
|
|
108
108
|
const name = language == null ? TASKS_LANGUAGE : languageNameOf(language);
|
package/src/schemas.ts
CHANGED
|
@@ -16,9 +16,12 @@ import {
|
|
|
16
16
|
MAX_BODY_LENGTH,
|
|
17
17
|
MAX_CATEGORY_KEY_LENGTH,
|
|
18
18
|
MAX_CATEGORY_NAME_LENGTH,
|
|
19
|
+
MAX_DECIDERS,
|
|
19
20
|
MAX_DOCUMENT_LENGTH,
|
|
20
21
|
MAX_DOCUMENT_PATH_LENGTH,
|
|
21
22
|
MAX_FILE_NAME_LENGTH,
|
|
23
|
+
MAX_HOME_ROW,
|
|
24
|
+
MAX_HOME_TILES,
|
|
22
25
|
MAX_LANGUAGE_TAG_LENGTH,
|
|
23
26
|
MAX_LINK_LABEL_LENGTH,
|
|
24
27
|
MAX_LINKS,
|
|
@@ -42,6 +45,7 @@ import {
|
|
|
42
45
|
MAX_WORKERS,
|
|
43
46
|
MIN_OPTIONS,
|
|
44
47
|
} from "./limits.js";
|
|
48
|
+
import { PLUGIN_WIDGET_SIZE_NAMES } from "./plugin/manifest.js";
|
|
45
49
|
import { hasForbiddenCodePoint } from "./text.js";
|
|
46
50
|
import {
|
|
47
51
|
ARTIFACT_KINDS,
|
|
@@ -100,6 +104,14 @@ const day = z.iso.date();
|
|
|
100
104
|
const count = z.int().check(z.nonnegative());
|
|
101
105
|
|
|
102
106
|
/** A person of the workspace, as the pages show them. Nothing here is a secret. */
|
|
107
|
+
/** A language tag as a browser sends one (`en`, `ko-KR`). */
|
|
108
|
+
const languageTag = z
|
|
109
|
+
.string()
|
|
110
|
+
.check(
|
|
111
|
+
z.maxLength(MAX_LANGUAGE_TAG_LENGTH),
|
|
112
|
+
z.regex(/^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/, "must be a language tag"),
|
|
113
|
+
);
|
|
114
|
+
|
|
103
115
|
export const restPersonSchema = z.object({
|
|
104
116
|
id: uuid,
|
|
105
117
|
/** What the account is called at its provider: a login at the git host, an address at Google. */
|
|
@@ -116,6 +128,19 @@ export const restPersonSchema = z.object({
|
|
|
116
128
|
source: z.enum(MEMBERSHIP_SOURCES),
|
|
117
129
|
/** When they were deactivated, or `null`. */
|
|
118
130
|
deactivatedAt: z.nullable(instant),
|
|
131
|
+
/**
|
|
132
|
+
* The id of the person they report to, the reporting line of the directory an administrator
|
|
133
|
+
* keeps, or `null` for nobody; what a plugin reads as the default approver, the default
|
|
134
|
+
* assignee, in place of a line of its own. Answered from 0.4.4 and read as optional, so that
|
|
135
|
+
* a console built on this package still reads an older console's people.
|
|
136
|
+
*/
|
|
137
|
+
managerId: z.optional(z.nullable(uuid)),
|
|
138
|
+
/**
|
|
139
|
+
* The language the person reads in (ADR-0065), a tag: the one they chose, else the one the pages
|
|
140
|
+
* first saw them in; `null` until they have opened the pages. What a plugin or an agent writes
|
|
141
|
+
* for them in. Answered from 0.4.4 and read as optional, as `managerId` is.
|
|
142
|
+
*/
|
|
143
|
+
language: z.optional(z.nullable(languageTag)),
|
|
119
144
|
});
|
|
120
145
|
export type RestPerson = z.infer<typeof restPersonSchema>;
|
|
121
146
|
|
|
@@ -195,16 +220,59 @@ export const timeZoneName = z
|
|
|
195
220
|
z.regex(/^[A-Za-z0-9_+-]+(?:\/[A-Za-z0-9_+-]+)*$/, "must be a time zone name"),
|
|
196
221
|
);
|
|
197
222
|
|
|
198
|
-
/**
|
|
199
|
-
|
|
223
|
+
/** What `PATCH /api/v1/me` is sent: the language the person chose, or `null` to choose none. */
|
|
224
|
+
/** The sizes of a tile on the front page (ADR-0061): a project's icon, and the widgets' family. */
|
|
225
|
+
export const HOME_TILE_SIZES = ["icon", ...PLUGIN_WIDGET_SIZE_NAMES] as const;
|
|
226
|
+
|
|
227
|
+
/** A tile by what it is: a project by its id, a widget by its plugin's key and its own. */
|
|
228
|
+
const homeTileId = z
|
|
200
229
|
.string()
|
|
201
230
|
.check(
|
|
202
|
-
z.
|
|
203
|
-
|
|
231
|
+
z.regex(
|
|
232
|
+
/^(?:project:[0-9a-f-]{36}|widget:[a-z][a-z0-9-]{0,31}:[a-z][a-z0-9-]{0,31})$/,
|
|
233
|
+
"must name a project or a widget",
|
|
234
|
+
),
|
|
204
235
|
);
|
|
205
236
|
|
|
206
|
-
/**
|
|
207
|
-
export const
|
|
237
|
+
/** One tile of a person's own front page, at a place of the grid and at a size. */
|
|
238
|
+
export const restHomeTileSchema = z.object({
|
|
239
|
+
tile: homeTileId,
|
|
240
|
+
x: z.int().check(z.gte(0), z.lte(11)),
|
|
241
|
+
y: z.int().check(z.gte(0), z.lte(MAX_HOME_ROW)),
|
|
242
|
+
size: z.enum(HOME_TILE_SIZES),
|
|
243
|
+
});
|
|
244
|
+
export type RestHomeTile = z.infer<typeof restHomeTileSchema>;
|
|
245
|
+
|
|
246
|
+
const homeLayoutSchema = z.array(restHomeTileSchema).check(z.maxLength(MAX_HOME_TILES));
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* A person's own front page (ADR-0061): the tiles they took off it, which stay off, and what
|
|
250
|
+
* they arranged at each width of the grid, by its columns; what they have not seen yet takes
|
|
251
|
+
* the first room.
|
|
252
|
+
*/
|
|
253
|
+
export const restHomeSchema = z.object({
|
|
254
|
+
hidden: z.array(homeTileId).check(z.maxLength(MAX_HOME_TILES)),
|
|
255
|
+
layouts: z.object({
|
|
256
|
+
12: z.optional(homeLayoutSchema),
|
|
257
|
+
8: z.optional(homeLayoutSchema),
|
|
258
|
+
6: z.optional(homeLayoutSchema),
|
|
259
|
+
4: z.optional(homeLayoutSchema),
|
|
260
|
+
}),
|
|
261
|
+
});
|
|
262
|
+
export type RestHome = z.infer<typeof restHomeSchema>;
|
|
263
|
+
|
|
264
|
+
/** What `PATCH /api/v1/me` is sent: the language, the front page, either or both; `null` lets the console choose. */
|
|
265
|
+
export const restMePatchSchema = z
|
|
266
|
+
.object({
|
|
267
|
+
language: z.optional(z.nullable(languageTag)),
|
|
268
|
+
home: z.optional(z.nullable(restHomeSchema)),
|
|
269
|
+
})
|
|
270
|
+
.check(
|
|
271
|
+
z.refine(
|
|
272
|
+
(patch) => patch.language !== undefined || patch.home !== undefined,
|
|
273
|
+
"must name a language or the front page",
|
|
274
|
+
),
|
|
275
|
+
);
|
|
208
276
|
export type RestMePatch = z.infer<typeof restMePatchSchema>;
|
|
209
277
|
|
|
210
278
|
/**
|
|
@@ -274,8 +342,13 @@ export const restMeSchema = z.object({
|
|
|
274
342
|
* person's own knows it holds one. `null` on a session, and for nobody.
|
|
275
343
|
*/
|
|
276
344
|
agent: z.nullable(z.string()),
|
|
277
|
-
/** The language the person
|
|
345
|
+
/** The language the person reads in (ADR-0065): the one they chose, else the one the pages first saw them in; `null` until then. */
|
|
278
346
|
language: z.nullable(languageTag),
|
|
347
|
+
/**
|
|
348
|
+
* The person's own front page (ADR-0061), or `null` while they have arranged nothing; answered
|
|
349
|
+
* from 0.4.4 and read as optional.
|
|
350
|
+
*/
|
|
351
|
+
home: z.optional(z.nullable(restHomeSchema)),
|
|
279
352
|
signIn: z.array(restProviderSchema),
|
|
280
353
|
/**
|
|
281
354
|
* The console's version and the range of command versions it takes, as npm spells one
|
|
@@ -313,11 +386,14 @@ export const restPersonPatchSchema = z
|
|
|
313
386
|
.object({
|
|
314
387
|
role: z.optional(z.enum(PERSON_ROLES)),
|
|
315
388
|
status: z.optional(z.enum(PERSON_STATUSES)),
|
|
389
|
+
/** Whom the person reports to: another active person of the workspace, or `null` for nobody. */
|
|
390
|
+
managerId: z.optional(z.nullable(uuid)),
|
|
316
391
|
})
|
|
317
392
|
.check(
|
|
318
393
|
z.refine(
|
|
319
|
-
(patch) =>
|
|
320
|
-
|
|
394
|
+
(patch) =>
|
|
395
|
+
patch.role !== undefined || patch.status !== undefined || patch.managerId !== undefined,
|
|
396
|
+
"must name a role, a status or a manager",
|
|
321
397
|
),
|
|
322
398
|
);
|
|
323
399
|
export type RestPersonPatch = z.infer<typeof restPersonPatchSchema>;
|
|
@@ -505,6 +581,8 @@ export const restTaskSchema = z.object({
|
|
|
505
581
|
owner: restPersonSchema,
|
|
506
582
|
/** The plugin that made it, by key (ADR-0052), or `null`; left out by a console older than the plugins. */
|
|
507
583
|
plugin: z.optional(z.nullable(z.string())),
|
|
584
|
+
/** The agent the task was written through, or `null` when a person wrote it themselves (ADR-0056); the owner may have changed since. Left out by an older console. */
|
|
585
|
+
agent: z.optional(z.nullable(z.string())),
|
|
508
586
|
/** The people at work on it (ADR-0037), in the order they came to it; `[]` while nobody is. */
|
|
509
587
|
workers: z.array(restPersonSchema),
|
|
510
588
|
/** The task this one is part of, or `null`. */
|
|
@@ -615,6 +693,8 @@ export const restGoalSchema = z.object({
|
|
|
615
693
|
/** The day the goal is meant to be reached by, or `null`. */
|
|
616
694
|
dueOn: z.nullable(day),
|
|
617
695
|
createdBy: restPersonSchema,
|
|
696
|
+
/** The agent the goal was set through, or `null` when a person set it themselves (ADR-0056); left out by an older console. */
|
|
697
|
+
agent: z.optional(z.nullable(z.string())),
|
|
618
698
|
/** The tasks that work towards it, counted by state. */
|
|
619
699
|
taskCounts: restTaskCountsSchema,
|
|
620
700
|
createdAt: instant,
|
|
@@ -714,8 +794,15 @@ export const restDecisionSchema = z.object({
|
|
|
714
794
|
/** The task's number, when there is a task, or `null`. */
|
|
715
795
|
taskNumber: z.nullable(z.int()),
|
|
716
796
|
raisedBy: restPersonSchema,
|
|
717
|
-
/**
|
|
797
|
+
/** The first of those to answer (ADR-0042): the raiser, unless others of the project were named. */
|
|
718
798
|
decider: restPersonSchema,
|
|
799
|
+
/**
|
|
800
|
+
* Everyone the decision is addressed to (ADR-0057), `decider` first: each finds it among their
|
|
801
|
+
* decisions, any of them may answer, and the first answer ends it. Answered from 0.4.4 and read
|
|
802
|
+
* as optional, so that a console built on this package still reads an older console's
|
|
803
|
+
* decisions, where it is `[decider]`.
|
|
804
|
+
*/
|
|
805
|
+
deciders: z.optional(z.array(restPersonSchema)),
|
|
719
806
|
/** The run that raised it, as the agent asked, or `null` when a person did. */
|
|
720
807
|
run: z.nullable(z.object({ id: uuid, agent: z.string() })),
|
|
721
808
|
/** The plugin that raised it, by key (ADR-0052), or `null`; left out by a console older than the plugins. */
|
|
@@ -729,10 +816,24 @@ export const restDecisionSchema = z.object({
|
|
|
729
816
|
note: z.nullable(z.string()),
|
|
730
817
|
by: restPersonSchema,
|
|
731
818
|
at: instant,
|
|
819
|
+
/**
|
|
820
|
+
* The agent the answer came through, when the person's agent wrote it in their name
|
|
821
|
+
* (`console answer`, ADR-0021), or `null` when they answered themselves (ADR-0056).
|
|
822
|
+
* Answered from 0.4.4 and read as optional, so that a console built on this package still
|
|
823
|
+
* reads an older console's decisions.
|
|
824
|
+
*/
|
|
825
|
+
agent: z.optional(z.nullable(z.string())),
|
|
732
826
|
}),
|
|
733
827
|
),
|
|
734
828
|
/** What followed the decision, in Markdown, written afterwards; `null` until someone does. */
|
|
735
829
|
outcome: z.nullable(z.string()),
|
|
830
|
+
/**
|
|
831
|
+
* Who wrote what followed, the agent they wrote it through (`null` when themselves) and when
|
|
832
|
+
* (ADR-0056); each `null` until someone does. Answered from 0.4.4 and read as optional.
|
|
833
|
+
*/
|
|
834
|
+
outcomeBy: z.optional(z.nullable(restPersonSchema)),
|
|
835
|
+
outcomeAgent: z.optional(z.nullable(z.string())),
|
|
836
|
+
outcomeAt: z.optional(z.nullable(instant)),
|
|
736
837
|
/**
|
|
737
838
|
* The files put on it when it was raised (ADR-0055), in the order they came; a picture among
|
|
738
839
|
* them is shown where the context names it as ``, else after it. Answered
|
|
@@ -820,6 +921,11 @@ export const restDecisionInputSchema = z.object({
|
|
|
820
921
|
runId: z.optional(z.nullable(uuid)),
|
|
821
922
|
/** Who is to answer (ADR-0042): a person who may work in the project; the asker when left out. */
|
|
822
923
|
deciderId: z.optional(uuid),
|
|
924
|
+
/**
|
|
925
|
+
* Several to answer (ADR-0057), in place of `deciderId`: each a person who may work in the
|
|
926
|
+
* project, up to `MAX_DECIDERS`; any of them may answer, and the first answer ends the decision.
|
|
927
|
+
*/
|
|
928
|
+
deciderIds: z.optional(z.array(uuid).check(z.minLength(1), z.maxLength(MAX_DECIDERS))),
|
|
823
929
|
});
|
|
824
930
|
export type RestDecisionInput = z.infer<typeof restDecisionInputSchema>;
|
|
825
931
|
|
|
@@ -873,6 +979,8 @@ export const restEventDataSchema = z.object({
|
|
|
873
979
|
/** For `person.role_changed` and the events about a project's members: whose role, and what it became. */
|
|
874
980
|
login: z.optional(z.string()),
|
|
875
981
|
role: z.optional(z.string()),
|
|
982
|
+
/** For `person.manager_changed`: the login of whom the person reports to now, or `null` for nobody. */
|
|
983
|
+
manager: z.optional(z.nullable(z.string())),
|
|
876
984
|
/** For the events about a project: its key and its name. */
|
|
877
985
|
key: z.optional(z.string()),
|
|
878
986
|
name: z.optional(z.string()),
|