@skillcdn/console 0.4.2 → 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 +120 -33
- 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 +38 -10
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +22 -15
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/plugin-check.d.ts +10 -1
- package/dist/cli/plugin-check.d.ts.map +1 -1
- package/dist/cli/plugin-check.js +45 -4
- package/dist/cli/plugin-check.js.map +1 -1
- package/dist/cli/plugin-cli.d.ts +1 -1
- package/dist/cli/plugin-cli.d.ts.map +1 -1
- package/dist/cli/plugin-cli.js +80 -5
- package/dist/cli/plugin-cli.js.map +1 -1
- package/dist/cli/plugin-init.d.ts +19 -0
- package/dist/cli/plugin-init.d.ts.map +1 -1
- package/dist/cli/plugin-init.js +141 -3
- package/dist/cli/plugin-init.js.map +1 -1
- package/dist/client.d.ts +7 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +18 -5
- 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/comments.d.ts.map +1 -1
- package/dist/components/comments.js +1 -1
- package/dist/components/comments.js.map +1 -1
- package/dist/components/decision-form.d.ts +8 -1
- package/dist/components/decision-form.d.ts.map +1 -1
- package/dist/components/decision-form.js +57 -7
- package/dist/components/decision-form.js.map +1 -1
- package/dist/components/decision-list.d.ts +9 -1
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +53 -7
- 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/my-decisions.d.ts +2 -0
- package/dist/components/my-decisions.d.ts.map +1 -1
- package/dist/components/my-decisions.js +2 -2
- package/dist/components/my-decisions.js.map +1 -1
- 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 +2 -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 +764 -192
- package/dist/console.d.ts +10 -3
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +49 -28
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +6 -3
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +31 -7
- 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 +180 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +181 -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 +176 -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 +8 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +8 -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 +24 -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 +574 -11
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +154 -25
- 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 +49 -35
- package/docs/specs/rest.md +20 -18
- package/package.json +7 -7
- package/skills/building-a-plugin/SKILL.md +10 -9
- package/skills/planning-the-board/references/setting-up.md +2 -1
- package/skills/working-the-board/SKILL.md +3 -3
- package/src/api.ts +10 -0
- package/src/cli/cli.ts +162 -39
- package/src/cli/format.ts +54 -10
- package/src/cli/help.ts +23 -14
- package/src/cli/plugin-check.ts +68 -2
- package/src/cli/plugin-cli.ts +104 -6
- package/src/cli/plugin-init.ts +178 -3
- package/src/client.ts +36 -9
- package/src/compat.ts +1 -1
- package/src/components/board.tsx +3 -3
- package/src/components/comments.tsx +1 -0
- package/src/components/decision-form.tsx +166 -29
- package/src/components/decision-list.tsx +116 -15
- 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/my-decisions.tsx +4 -1
- 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 +9 -1
- package/src/components/ui.tsx +6 -2
- package/src/connect-page.tsx +5 -6
- package/src/console.tsx +98 -42
- package/src/data.ts +41 -11
- package/src/docs-page.tsx +6 -5
- package/src/home-tiles.ts +438 -0
- package/src/i18n/en.ts +185 -0
- package/src/i18n/index.ts +17 -14
- package/src/i18n/ko.ts +181 -3
- package/src/index.ts +7 -5
- package/src/limits.ts +8 -0
- package/src/plugin/bridge.ts +77 -1
- package/src/plugin/capabilities.ts +17 -1
- package/src/plugin/context.ts +24 -2
- 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 +190 -28
- package/src/styles/console.css +764 -192
- package/src/use-action.ts +16 -7
- package/src/vocabulary.ts +4 -0
- package/template/plugin/console-plugin.json +3 -1
- package/template/plugin/package.json +3 -3
package/src/cli/plugin-check.ts
CHANGED
|
@@ -8,11 +8,14 @@ import { descriptionProblems, type PluginDescription } from "../plugin/define.js
|
|
|
8
8
|
import type { ServerLoad } from "../plugin/load.js";
|
|
9
9
|
import {
|
|
10
10
|
CURRENT_PLUGIN_API,
|
|
11
|
+
capabilitiesAsked,
|
|
11
12
|
PLUGIN_API_MAJORS,
|
|
12
13
|
type PluginManifest,
|
|
13
14
|
parsePluginManifest,
|
|
14
15
|
} from "../plugin/manifest.js";
|
|
15
16
|
import { forbiddenImportsIn, removedGlobalsIn } from "../plugin/sandbox.js";
|
|
17
|
+
import { formatTriple, satisfiesRange, versionOfDependency } from "../plugin/semver.js";
|
|
18
|
+
import { isLanguageMap } from "./plugin-init.js";
|
|
16
19
|
|
|
17
20
|
export interface CheckReport {
|
|
18
21
|
readonly ok: boolean;
|
|
@@ -308,10 +311,37 @@ function describeCounts(manifest: PluginManifest, description: PluginDescription
|
|
|
308
311
|
return counts.join(", ");
|
|
309
312
|
}
|
|
310
313
|
|
|
314
|
+
/**
|
|
315
|
+
* The words of the manifest, as JSON, whose translation repeats the English words (`name.ko`,
|
|
316
|
+
* for one): what `init --languages` copies for the author to translate, and what was left.
|
|
317
|
+
*/
|
|
318
|
+
export function sameAsEnglish(value: unknown, at = ""): string[] {
|
|
319
|
+
if (Array.isArray(value)) {
|
|
320
|
+
return value.flatMap((item, index) => sameAsEnglish(item, `${at}[${index}]`));
|
|
321
|
+
}
|
|
322
|
+
if (typeof value !== "object" || value === null) {
|
|
323
|
+
return [];
|
|
324
|
+
}
|
|
325
|
+
if (isLanguageMap(value)) {
|
|
326
|
+
return Object.entries(value)
|
|
327
|
+
.filter(([language, words]) => language !== "en" && words === value.en)
|
|
328
|
+
.map(([language]) => `${at}.${language}`.replace(/^\./, ""));
|
|
329
|
+
}
|
|
330
|
+
return Object.entries(value).flatMap(([key, inner]) =>
|
|
331
|
+
sameAsEnglish(inner, at.length === 0 ? key : `${at}.${key}`),
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export interface CheckOptions {
|
|
336
|
+
/** The version of the console this command belongs to, held against the manifest's `console`; left out, not compared. */
|
|
337
|
+
readonly consoleVersion?: string | undefined;
|
|
338
|
+
}
|
|
339
|
+
|
|
311
340
|
/** Reads the plugin at `root` as a console would, and says what it would refuse. */
|
|
312
341
|
export async function checkPlugin(
|
|
313
342
|
root: string,
|
|
314
343
|
loadServer: (file: string) => Promise<ServerLoad>,
|
|
344
|
+
options: CheckOptions = {},
|
|
315
345
|
): Promise<CheckReport> {
|
|
316
346
|
const errors: string[] = [];
|
|
317
347
|
const warnings: string[] = [];
|
|
@@ -338,6 +368,20 @@ export async function checkPlugin(
|
|
|
338
368
|
`console-plugin.json: pluginApi ${manifest.pluginApi} is not one this command knows (${PLUGIN_API_MAJORS.join(", ")}); write against ${CURRENT_PLUGIN_API}`,
|
|
339
369
|
);
|
|
340
370
|
}
|
|
371
|
+
for (const path of sameAsEnglish(manifestRead.value)) {
|
|
372
|
+
warnings.push(
|
|
373
|
+
`console-plugin.json: ${path} is the same as en: translate it, or leave the language out`,
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
if (
|
|
377
|
+
manifest.console !== undefined &&
|
|
378
|
+
options.consoleVersion !== undefined &&
|
|
379
|
+
!satisfiesRange(options.consoleVersion, manifest.console)
|
|
380
|
+
) {
|
|
381
|
+
warnings.push(
|
|
382
|
+
`console-plugin.json: console ${manifest.console} leaves out ${options.consoleVersion}, the console this command belongs to`,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
341
385
|
|
|
342
386
|
const packageRead = await readJson(join(root, "package.json"));
|
|
343
387
|
let version = "0.0.0";
|
|
@@ -366,6 +410,25 @@ export async function checkPlugin(
|
|
|
366
410
|
if (pkg.type !== "module") {
|
|
367
411
|
errors.push('package.json: type is not "module"; the server module is ESM');
|
|
368
412
|
}
|
|
413
|
+
// The console the plugin is built against is the one its types come from: a devDependency
|
|
414
|
+
// outside the consoles it declares is a plugin built for a console it says it does not run on.
|
|
415
|
+
const devDependencies = (
|
|
416
|
+
typeof pkg.devDependencies === "object" && pkg.devDependencies !== null
|
|
417
|
+
? pkg.devDependencies
|
|
418
|
+
: {}
|
|
419
|
+
) as Record<string, unknown>;
|
|
420
|
+
const dependency = devDependencies["@skillcdn/console"];
|
|
421
|
+
const builtAgainst =
|
|
422
|
+
typeof dependency === "string" ? versionOfDependency(dependency) : undefined;
|
|
423
|
+
if (
|
|
424
|
+
manifest.console !== undefined &&
|
|
425
|
+
builtAgainst !== undefined &&
|
|
426
|
+
!satisfiesRange(formatTriple(builtAgainst), manifest.console)
|
|
427
|
+
) {
|
|
428
|
+
errors.push(
|
|
429
|
+
`package.json: devDependencies names @skillcdn/console ${String(dependency)}, outside the consoles the manifest declares (console ${manifest.console})`,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
369
432
|
const scripts = (
|
|
370
433
|
typeof pkg.scripts === "object" && pkg.scripts !== null ? pkg.scripts : {}
|
|
371
434
|
) as Record<string, unknown>;
|
|
@@ -429,14 +492,17 @@ export async function checkPlugin(
|
|
|
429
492
|
errors.push(`${manifest.server}: ${problem}`);
|
|
430
493
|
}
|
|
431
494
|
if (
|
|
432
|
-
manifest.
|
|
495
|
+
capabilitiesAsked(manifest).includes("endpoints") &&
|
|
433
496
|
Object.keys(description.endpoints).length === 0
|
|
434
497
|
) {
|
|
435
498
|
warnings.push(
|
|
436
499
|
"console-plugin.json: asks the capability endpoints and the server module defines no endpoint",
|
|
437
500
|
);
|
|
438
501
|
}
|
|
439
|
-
if (
|
|
502
|
+
if (
|
|
503
|
+
capabilitiesAsked(manifest).includes("jobs") &&
|
|
504
|
+
Object.keys(description.jobs).length === 0
|
|
505
|
+
) {
|
|
440
506
|
warnings.push(
|
|
441
507
|
"console-plugin.json: asks the capability jobs and the server module defines no job",
|
|
442
508
|
);
|
package/src/cli/plugin-cli.ts
CHANGED
|
@@ -5,8 +5,9 @@ import { resolve } from "node:path";
|
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
6
|
import type { ServerLoad } from "../plugin/load.js";
|
|
7
7
|
import { isPluginKey, RESERVED_PLUGIN_KEYS } from "../plugin/manifest.js";
|
|
8
|
+
import { lineRangeFrom, versionOfDependency } from "../plugin/semver.js";
|
|
8
9
|
import { checkPlugin } from "./plugin-check.js";
|
|
9
|
-
import { nameFromKey, writePluginFromTemplate } from "./plugin-init.js";
|
|
10
|
+
import { LANGUAGE_TAG, nameFromKey, PACKAGE_NAME, writePluginFromTemplate } from "./plugin-init.js";
|
|
10
11
|
|
|
11
12
|
export interface PluginCliIo {
|
|
12
13
|
stdout(text: string): void;
|
|
@@ -25,12 +26,20 @@ export const PLUGIN_EXIT = { ok: 0, failed: 1, usage: 2 } as const;
|
|
|
25
26
|
export const PLUGIN_USAGE = `console-plugin: writes and checks a plugin of the SkillCDN Console.
|
|
26
27
|
|
|
27
28
|
console-plugin init <dir> --key <key> [--name <name>] [--console <dependency>]
|
|
29
|
+
[--package <name>] [--repository <url> [--directory <path>]] [--languages <tags>]
|
|
28
30
|
Writes a plugin into <dir>, which must not exist or be empty, from the template this
|
|
29
31
|
package ships: the manifest, a server module with one collection, one command and one
|
|
30
32
|
query, the source of a web bundle with one page and one widget, one skill with its
|
|
31
33
|
manifest, tests, and the build. <key> is the plugin's key, lowercase letters, digits and
|
|
32
34
|
hyphens; --name its name for people, from the key unless said; --console what the
|
|
33
|
-
plugin's devDependency on @skillcdn/console says, ^<this version> unless said
|
|
35
|
+
plugin's devDependency on @skillcdn/console says, ^<this version> unless said;
|
|
36
|
+
--package the package's name, console-plugin-<key> unless said, a scoped one
|
|
37
|
+
(@scope/console-plugin-<key>) published as public; --repository the https address of
|
|
38
|
+
the repository it lives in, with --directory its path there in a repository of several,
|
|
39
|
+
which fill repository, homepage and bugs and the manifest's store; --languages the
|
|
40
|
+
languages of its words with commas between, en first (en,ko), each language map and
|
|
41
|
+
both manifests' translations given the others as English words to translate, of which
|
|
42
|
+
check warns while they stay English.
|
|
34
43
|
|
|
35
44
|
console-plugin check [<dir>]
|
|
36
45
|
Reads the plugin in <dir>, the working directory unless said, as a console would, built:
|
|
@@ -68,13 +77,32 @@ export async function runPluginCli(argv: readonly string[], io: PluginCliIo): Pr
|
|
|
68
77
|
return PLUGIN_EXIT.usage;
|
|
69
78
|
}
|
|
70
79
|
|
|
80
|
+
const INIT_USAGE =
|
|
81
|
+
"usage: console-plugin init <dir> --key <key> [--name <name>] [--console <dependency>] [--package <name>] [--repository <url> [--directory <path>]] [--languages <tags>]\n";
|
|
82
|
+
|
|
71
83
|
async function init(args: readonly string[], io: PluginCliIo): Promise<number> {
|
|
72
|
-
let values: {
|
|
84
|
+
let values: {
|
|
85
|
+
key?: string;
|
|
86
|
+
name?: string;
|
|
87
|
+
console?: string;
|
|
88
|
+
package?: string;
|
|
89
|
+
repository?: string;
|
|
90
|
+
directory?: string;
|
|
91
|
+
languages?: string;
|
|
92
|
+
};
|
|
73
93
|
let positionals: string[];
|
|
74
94
|
try {
|
|
75
95
|
({ values, positionals } = parseArgs({
|
|
76
96
|
args: [...args],
|
|
77
|
-
options: {
|
|
97
|
+
options: {
|
|
98
|
+
key: { type: "string" },
|
|
99
|
+
name: { type: "string" },
|
|
100
|
+
console: { type: "string" },
|
|
101
|
+
package: { type: "string" },
|
|
102
|
+
repository: { type: "string" },
|
|
103
|
+
directory: { type: "string" },
|
|
104
|
+
languages: { type: "string" },
|
|
105
|
+
},
|
|
78
106
|
allowPositionals: true,
|
|
79
107
|
strict: true,
|
|
80
108
|
}));
|
|
@@ -85,8 +113,51 @@ async function init(args: readonly string[], io: PluginCliIo): Promise<number> {
|
|
|
85
113
|
const [dir, ...extra] = positionals;
|
|
86
114
|
const key = values.key;
|
|
87
115
|
if (dir === undefined || extra.length > 0 || key === undefined) {
|
|
116
|
+
io.stderr(INIT_USAGE);
|
|
117
|
+
return PLUGIN_EXIT.usage;
|
|
118
|
+
}
|
|
119
|
+
const packageName = values.package;
|
|
120
|
+
if (packageName !== undefined && (packageName.length > 214 || !PACKAGE_NAME.test(packageName))) {
|
|
121
|
+
io.stderr(
|
|
122
|
+
`console-plugin init: ${packageName} is not a package name: lowercase letters, digits, hyphens, dots and underscores, with a scope as @scope/name\n`,
|
|
123
|
+
);
|
|
124
|
+
return PLUGIN_EXIT.usage;
|
|
125
|
+
}
|
|
126
|
+
if (values.directory !== undefined && values.repository === undefined) {
|
|
127
|
+
io.stderr("console-plugin init: --directory goes with --repository\n");
|
|
128
|
+
return PLUGIN_EXIT.usage;
|
|
129
|
+
}
|
|
130
|
+
if (values.repository !== undefined && !isRepositoryAddress(values.repository)) {
|
|
131
|
+
io.stderr(
|
|
132
|
+
`console-plugin init: ${values.repository} is not the https address of a repository\n`,
|
|
133
|
+
);
|
|
134
|
+
return PLUGIN_EXIT.usage;
|
|
135
|
+
}
|
|
136
|
+
if (
|
|
137
|
+
values.directory !== undefined &&
|
|
138
|
+
!/^[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/.test(values.directory)
|
|
139
|
+
) {
|
|
88
140
|
io.stderr(
|
|
89
|
-
"
|
|
141
|
+
"console-plugin init: --directory is a path within the repository, with forward slashes\n",
|
|
142
|
+
);
|
|
143
|
+
return PLUGIN_EXIT.usage;
|
|
144
|
+
}
|
|
145
|
+
const languages = values.languages
|
|
146
|
+
?.split(",")
|
|
147
|
+
.map((language) => language.trim())
|
|
148
|
+
.filter((language) => language.length > 0);
|
|
149
|
+
if (
|
|
150
|
+
languages !== undefined &&
|
|
151
|
+
(languages.length === 0 || languages.some((language) => !LANGUAGE_TAG.test(language)))
|
|
152
|
+
) {
|
|
153
|
+
io.stderr(
|
|
154
|
+
"console-plugin init: --languages is a list of language tags with commas between, en first: en,ko\n",
|
|
155
|
+
);
|
|
156
|
+
return PLUGIN_EXIT.usage;
|
|
157
|
+
}
|
|
158
|
+
if (languages !== undefined && languages[0] !== "en") {
|
|
159
|
+
io.stderr(
|
|
160
|
+
"console-plugin init: --languages begins with en, the language the template is written in\n",
|
|
90
161
|
);
|
|
91
162
|
return PLUGIN_EXIT.usage;
|
|
92
163
|
}
|
|
@@ -106,12 +177,24 @@ async function init(args: readonly string[], io: PluginCliIo): Promise<number> {
|
|
|
106
177
|
return PLUGIN_EXIT.usage;
|
|
107
178
|
}
|
|
108
179
|
const consoleDependency = values.console ?? (io.version === undefined ? "*" : `^${io.version}`);
|
|
180
|
+
// The consoles the plugin runs on: the line of the version it is written against, read from
|
|
181
|
+
// the dependency where it names one (`^0.4.3`, `0.4.3`); a dependency that names none
|
|
182
|
+
// (`workspace:*`) leaves the manifest naming no console.
|
|
183
|
+
const writtenAgainst = versionOfDependency(consoleDependency);
|
|
184
|
+
const consoleRange = writtenAgainst === undefined ? undefined : lineRangeFrom(writtenAgainst);
|
|
109
185
|
const target = resolve(io.cwd, dir);
|
|
110
186
|
try {
|
|
111
187
|
const written = await writePluginFromTemplate(io.templateDir, target, {
|
|
112
188
|
key,
|
|
113
189
|
name,
|
|
114
190
|
consoleDependency,
|
|
191
|
+
consoleRange,
|
|
192
|
+
packageName,
|
|
193
|
+
repository:
|
|
194
|
+
values.repository === undefined
|
|
195
|
+
? undefined
|
|
196
|
+
: { url: values.repository, directory: values.directory },
|
|
197
|
+
languages,
|
|
115
198
|
});
|
|
116
199
|
io.stdout(`Wrote the plugin ${key} into ${target}: ${written.length} files.\n`);
|
|
117
200
|
io.stdout(
|
|
@@ -124,6 +207,19 @@ async function init(args: readonly string[], io: PluginCliIo): Promise<number> {
|
|
|
124
207
|
}
|
|
125
208
|
}
|
|
126
209
|
|
|
210
|
+
/** An https address of a repository, as git names it or as a browser opens it. */
|
|
211
|
+
function isRepositoryAddress(value: string): boolean {
|
|
212
|
+
const address = value.replace(/^git\+/, "");
|
|
213
|
+
if (!/^https:\/\/\S+$/.test(address)) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
return new URL(address).hostname.length > 0;
|
|
218
|
+
} catch {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
127
223
|
async function check(args: readonly string[], io: PluginCliIo): Promise<number> {
|
|
128
224
|
const [dir, ...extra] = args;
|
|
129
225
|
if (extra.length > 0 || dir?.startsWith("-")) {
|
|
@@ -131,7 +227,9 @@ async function check(args: readonly string[], io: PluginCliIo): Promise<number>
|
|
|
131
227
|
return PLUGIN_EXIT.usage;
|
|
132
228
|
}
|
|
133
229
|
const root = resolve(io.cwd, dir ?? ".");
|
|
134
|
-
const report = await checkPlugin(root, (file) => io.loadServer(file)
|
|
230
|
+
const report = await checkPlugin(root, (file) => io.loadServer(file), {
|
|
231
|
+
consoleVersion: io.version,
|
|
232
|
+
});
|
|
135
233
|
io.stdout(`console-plugin check ${root}\n`);
|
|
136
234
|
io.stdout(wordsOf(report.errors, "error"));
|
|
137
235
|
io.stdout(wordsOf(report.warnings, "warning"));
|
package/src/cli/plugin-init.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// `console-plugin init` (docs/specs/plugins.md, "Authoring"): writes a plugin from the template
|
|
2
|
-
// the package ships, the words `__key__`, `__name__` and `__console__` in the
|
|
3
|
-
// their names replaced
|
|
4
|
-
// the
|
|
2
|
+
// the package ships, the words `__package__`, `__key__`, `__name__` and `__console__` in the
|
|
3
|
+
// template's files and their names replaced; then what the template cannot say in a word, where
|
|
4
|
+
// it was asked for: the fields a published or a repository-bound package carries, and the
|
|
5
|
+
// languages beyond English. `examples/plugin-hello` in the console's repository is what it writes
|
|
6
|
+
// for the key `hello` with nothing else said, and a test holds the two equal.
|
|
5
7
|
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
6
8
|
import { join, relative, sep } from "node:path";
|
|
7
9
|
|
|
@@ -11,19 +13,47 @@ export interface InitOptions {
|
|
|
11
13
|
readonly name: string;
|
|
12
14
|
/** What the plugin's devDependency on `@skillcdn/console` says: `^<version>` of the command, unless said. */
|
|
13
15
|
readonly consoleDependency: string;
|
|
16
|
+
/** The consoles the plugin runs on, the manifest's `console`: the line of the version it is written against; left out, the manifest names none. */
|
|
17
|
+
readonly consoleRange?: string | undefined;
|
|
18
|
+
/** The package's name: `console-plugin-<key>` unless said. A scoped name is published with `publishConfig.access` `public`. */
|
|
19
|
+
readonly packageName?: string | undefined;
|
|
20
|
+
/** The repository the plugin lives in and, in a repository of several, its directory: `repository`, `homepage`, `bugs` and the manifest's `store`. */
|
|
21
|
+
readonly repository?:
|
|
22
|
+
| { readonly url: string; readonly directory?: string | undefined }
|
|
23
|
+
| undefined;
|
|
24
|
+
/** The languages the plugin's words are in, `en` first; `en` alone unless said. Every language map and both manifests' translations get the others, copied from English for the author to translate. */
|
|
25
|
+
readonly languages?: readonly string[] | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A language tag as the manifest's language maps are keyed: `en`, `ko`, `pt-BR`. */
|
|
29
|
+
export const LANGUAGE_TAG = /^[a-z]{2,3}(?:-[A-Za-z0-9]+)*$/;
|
|
30
|
+
|
|
31
|
+
/** A package's name as the registry takes one: lowercase, with a scope as `@scope/name`. */
|
|
32
|
+
export const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
33
|
+
|
|
34
|
+
/** The package's name from the options: what was said, else `console-plugin-<key>`. */
|
|
35
|
+
export function packageNameOf(options: InitOptions): string {
|
|
36
|
+
return options.packageName ?? `console-plugin-${options.key}`;
|
|
14
37
|
}
|
|
15
38
|
|
|
16
39
|
/** The words the template uses, and what each becomes. */
|
|
17
40
|
export function substitutions(options: InitOptions): readonly [string, string][] {
|
|
18
41
|
return [
|
|
42
|
+
["__package__", packageNameOf(options)],
|
|
19
43
|
["__key__", options.key],
|
|
20
44
|
["__name__", options.name],
|
|
45
|
+
["__consoleRange__", options.consoleRange ?? "*"],
|
|
21
46
|
["__console__", options.consoleDependency],
|
|
22
47
|
];
|
|
23
48
|
}
|
|
24
49
|
|
|
25
50
|
export function substitute(text: string, options: InitOptions): string {
|
|
26
51
|
let result = text;
|
|
52
|
+
if (options.consoleRange === undefined) {
|
|
53
|
+
// No console to name: the manifest's line goes, and any console that serves the plugin API
|
|
54
|
+
// runs the plugin, as before the field. Taken out as text, so that the file keeps its shape.
|
|
55
|
+
result = result.replace(/^[ \t]*"console": "__consoleRange__",\r?\n/m, "");
|
|
56
|
+
}
|
|
27
57
|
for (const [word, value] of substitutions(options)) {
|
|
28
58
|
result = result.replaceAll(word, value);
|
|
29
59
|
}
|
|
@@ -86,5 +116,150 @@ export async function writePluginFromTemplate(
|
|
|
86
116
|
await writeFile(path, substitute(text, options), "utf8");
|
|
87
117
|
written.push(target);
|
|
88
118
|
}
|
|
119
|
+
await adaptPlugin(targetDir, options);
|
|
89
120
|
return written;
|
|
90
121
|
}
|
|
122
|
+
|
|
123
|
+
/** Whether a value of the manifest is a language map: words per language, `en` among them. */
|
|
124
|
+
export function isLanguageMap(value: unknown): value is Record<string, string> {
|
|
125
|
+
return (
|
|
126
|
+
typeof value === "object" &&
|
|
127
|
+
value !== null &&
|
|
128
|
+
!Array.isArray(value) &&
|
|
129
|
+
"en" in value &&
|
|
130
|
+
Object.entries(value).every(
|
|
131
|
+
([language, words]) => LANGUAGE_TAG.test(language) && typeof words === "string",
|
|
132
|
+
)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Every language map of the manifest, wherever it is, gets `languages`, each copied from English where it is missing. */
|
|
137
|
+
function addLanguages(value: unknown, languages: readonly string[]): void {
|
|
138
|
+
if (Array.isArray(value)) {
|
|
139
|
+
for (const item of value) {
|
|
140
|
+
addLanguages(item, languages);
|
|
141
|
+
}
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (typeof value !== "object" || value === null) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (isLanguageMap(value)) {
|
|
148
|
+
const english = value.en ?? "";
|
|
149
|
+
for (const language of languages) {
|
|
150
|
+
if (!(language in value)) {
|
|
151
|
+
value[language] = english;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
for (const inner of Object.values(value)) {
|
|
157
|
+
addLanguages(inner, languages);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** The page of a repository at its host, from the address git names it by. */
|
|
162
|
+
function pageOf(url: string): string {
|
|
163
|
+
return url
|
|
164
|
+
.replace(/^git\+/, "")
|
|
165
|
+
.replace(/\.git$/, "")
|
|
166
|
+
.replace(/\/$/, "");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The manifest's front-matter with `translations` for `languages`, each the English words
|
|
171
|
+
* again, inserted before the front-matter's end; under `parent` where the format nests them
|
|
172
|
+
* (`skillcdn.translations` in a skill). The description is the one the front-matter has, as
|
|
173
|
+
* written; the title is the plugin's name.
|
|
174
|
+
*/
|
|
175
|
+
function withTranslations(
|
|
176
|
+
text: string,
|
|
177
|
+
languages: readonly string[],
|
|
178
|
+
titleKey: "name" | "title",
|
|
179
|
+
title: string,
|
|
180
|
+
parent?: "skillcdn",
|
|
181
|
+
): string {
|
|
182
|
+
const newline = text.includes("\r\n") ? "\r\n" : "\n";
|
|
183
|
+
const lines = text.split(/\r?\n/);
|
|
184
|
+
const end = lines.indexOf("---", 1);
|
|
185
|
+
if (lines[0] !== "---" || end === -1) {
|
|
186
|
+
return text;
|
|
187
|
+
}
|
|
188
|
+
const described = lines
|
|
189
|
+
.slice(1, end)
|
|
190
|
+
.find((line) => line.startsWith("description:"))
|
|
191
|
+
?.slice("description:".length)
|
|
192
|
+
.trim();
|
|
193
|
+
const indent = parent === undefined ? "" : " ";
|
|
194
|
+
const block = [
|
|
195
|
+
...(parent === undefined ? [] : [`${parent}:`]),
|
|
196
|
+
`${indent}translations:`,
|
|
197
|
+
...languages.flatMap((language) => [
|
|
198
|
+
`${indent} ${language}:`,
|
|
199
|
+
`${indent} ${titleKey}: ${JSON.stringify(title)}`,
|
|
200
|
+
...(described === undefined ? [] : [`${indent} description: ${described}`]),
|
|
201
|
+
]),
|
|
202
|
+
];
|
|
203
|
+
return [...lines.slice(0, end), ...block, ...lines.slice(end)].join(newline);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* What the template cannot say in a word, where it was asked for: `publishConfig` for a scoped
|
|
208
|
+
* name, `repository`, `homepage` and `bugs` with the manifest's `store` for a repository, and
|
|
209
|
+
* the languages beyond English in every language map and both manifests' translations, each
|
|
210
|
+
* copied from the English words for the author to translate (`console-plugin check` warns of
|
|
211
|
+
* one left as it was). Nothing is rewritten when nothing was asked, so that the example stays
|
|
212
|
+
* what the template says.
|
|
213
|
+
*/
|
|
214
|
+
async function adaptPlugin(targetDir: string, options: InitOptions): Promise<void> {
|
|
215
|
+
const name = packageNameOf(options);
|
|
216
|
+
const repository = options.repository;
|
|
217
|
+
const languages = (options.languages ?? []).filter((language) => language !== "en");
|
|
218
|
+
if (!name.startsWith("@") && repository === undefined && languages.length === 0) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const packagePath = join(targetDir, "package.json");
|
|
222
|
+
const pkg = JSON.parse(await readFile(packagePath, "utf8")) as Record<string, unknown>;
|
|
223
|
+
const manifestPath = join(targetDir, "console-plugin.json");
|
|
224
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf8")) as Record<string, unknown>;
|
|
225
|
+
if (name.startsWith("@")) {
|
|
226
|
+
pkg.publishConfig = { access: "public" };
|
|
227
|
+
}
|
|
228
|
+
if (repository !== undefined) {
|
|
229
|
+
const page = pageOf(repository.url);
|
|
230
|
+
// The paths a git host has for a directory and for issues are the host's; only the one
|
|
231
|
+
// whose paths are known gets them.
|
|
232
|
+
const atGitHub = new URL(page).hostname === "github.com";
|
|
233
|
+
const homepage =
|
|
234
|
+
repository.directory !== undefined && atGitHub
|
|
235
|
+
? `${page}/tree/HEAD/${repository.directory}`
|
|
236
|
+
: page;
|
|
237
|
+
pkg.repository = {
|
|
238
|
+
type: "git",
|
|
239
|
+
url: repository.url,
|
|
240
|
+
...(repository.directory === undefined ? {} : { directory: repository.directory }),
|
|
241
|
+
};
|
|
242
|
+
pkg.homepage = homepage;
|
|
243
|
+
if (atGitHub) {
|
|
244
|
+
pkg.bugs = { url: `${page}/issues` };
|
|
245
|
+
}
|
|
246
|
+
manifest.store = { homepage, repository: page };
|
|
247
|
+
}
|
|
248
|
+
if (languages.length > 0) {
|
|
249
|
+
addLanguages(manifest, languages);
|
|
250
|
+
const skillcdn = join(targetDir, "SKILLCDN.md");
|
|
251
|
+
await writeFile(
|
|
252
|
+
skillcdn,
|
|
253
|
+
withTranslations(await readFile(skillcdn, "utf8"), languages, "name", options.name),
|
|
254
|
+
"utf8",
|
|
255
|
+
);
|
|
256
|
+
const skill = join(targetDir, "skills", options.key, "SKILL.md");
|
|
257
|
+
await writeFile(
|
|
258
|
+
skill,
|
|
259
|
+
withTranslations(await readFile(skill, "utf8"), languages, "title", options.name, "skillcdn"),
|
|
260
|
+
"utf8",
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
await writeFile(packagePath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
264
|
+
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
265
|
+
}
|
package/src/client.ts
CHANGED
|
@@ -325,7 +325,8 @@ export interface ProjectClient {
|
|
|
325
325
|
* while the decision waits, for as long as it allows, and answers the decision as it stands.
|
|
326
326
|
*/
|
|
327
327
|
awaitDecision(id: string, waitSeconds: number, signal?: AbortSignal): Promise<RestDecision>;
|
|
328
|
-
|
|
328
|
+
/** Raises a decision; with `files`, the files put on it (ADR-0055) go in the one request. */
|
|
329
|
+
raiseDecision(input: RestDecisionInput, files?: readonly FileUpload[]): Promise<RestDecision>;
|
|
329
330
|
answerDecision(id: string, input: RestAnswerInput): Promise<RestDecision>;
|
|
330
331
|
/** Grows a decision's record: its context, or what followed. */
|
|
331
332
|
updateDecision(id: string, patch: RestDecisionPatch): Promise<RestDecision>;
|
|
@@ -343,7 +344,7 @@ export interface ProjectClient {
|
|
|
343
344
|
handIn(runId: string, input: RestArtifactInput): Promise<RestRun>;
|
|
344
345
|
/** Hands in a file, on a run of the asker's: the console keeps the bytes. */
|
|
345
346
|
handInFile(runId: string, input: FileUpload): Promise<RestRun>;
|
|
346
|
-
/** Where the bytes of a file of the project are read, by its id: handed in by a run, put in a comment, or attached to a page. */
|
|
347
|
+
/** Where the bytes of a file of the project are read, by its id: handed in by a run, put in a comment or on a decision, or attached to a page. */
|
|
347
348
|
fileUrl(fileId: string): string;
|
|
348
349
|
/** The bytes of a file of the project, by its id, with the name and the type the console says. */
|
|
349
350
|
readFile(fileId: string, signal?: AbortSignal): Promise<FileRead>;
|
|
@@ -497,6 +498,8 @@ export interface PluginClient {
|
|
|
497
498
|
fileUrl(fileId: string): string;
|
|
498
499
|
/** The bytes of a record's file, with the name and the type the console says. */
|
|
499
500
|
readFile(fileId: string, signal?: AbortSignal): Promise<FileRead>;
|
|
501
|
+
/** A person's picture, for a frame, which names the person and never an address (ADR-0063). */
|
|
502
|
+
personPicture(personId: string, signal?: AbortSignal): Promise<FileRead>;
|
|
500
503
|
/** Where the frame of a page or a widget is (ADR-0054), on a project when its key is given. */
|
|
501
504
|
frameUrl(options: {
|
|
502
505
|
readonly page?: string | undefined;
|
|
@@ -542,6 +545,8 @@ export interface PluginClient {
|
|
|
542
545
|
disable(): Promise<RestPluginDetail>;
|
|
543
546
|
/** Changes the settings' values, without secrets; `null` clears one. */
|
|
544
547
|
updateSettings(patch: RestPluginSettingsPatch): Promise<RestPluginDetail>;
|
|
548
|
+
/** Says what an enabled plugin holds: those it requires, named again, and the optional ones to turn on. */
|
|
549
|
+
updateConsent(consent: readonly string[]): Promise<RestPluginDetail>;
|
|
545
550
|
/** Seals a secret for the plugin; it is answered by nothing from then on. */
|
|
546
551
|
setSecret(name: string, value: string): Promise<void>;
|
|
547
552
|
removeSecret(name: string): Promise<void>;
|
|
@@ -774,11 +779,9 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
|
|
|
774
779
|
return form;
|
|
775
780
|
};
|
|
776
781
|
|
|
777
|
-
/**
|
|
778
|
-
const
|
|
779
|
-
const
|
|
780
|
-
form.set("body", input.body);
|
|
781
|
-
for (const file of input.files ?? []) {
|
|
782
|
+
/** The files appended to a form, a part `file` each, with its name and its type. */
|
|
783
|
+
const appendFiles = (form: FormData, files: readonly FileUpload[]): void => {
|
|
784
|
+
for (const file of files) {
|
|
782
785
|
const bytes =
|
|
783
786
|
file.bytes instanceof Blob ? file.bytes : new Blob([Uint8Array.from(file.bytes)]);
|
|
784
787
|
form.append(
|
|
@@ -787,6 +790,21 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
|
|
|
787
790
|
file.name,
|
|
788
791
|
);
|
|
789
792
|
}
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
/** A comment with files as the parts of a form: the part `body`, and a part `file` for each file. */
|
|
796
|
+
const commentFormOf = (input: CommentInput): FormData => {
|
|
797
|
+
const form = new FormData();
|
|
798
|
+
form.set("body", input.body);
|
|
799
|
+
appendFiles(form, input.files ?? []);
|
|
800
|
+
return form;
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
/** A decision with files as the parts of a form (ADR-0055): the part `decision`, what the JSON would say, and a part `file` for each file. */
|
|
804
|
+
const decisionFormOf = (input: RestDecisionInput, files: readonly FileUpload[]): FormData => {
|
|
805
|
+
const form = new FormData();
|
|
806
|
+
form.set("decision", JSON.stringify(input));
|
|
807
|
+
appendFiles(form, files);
|
|
790
808
|
return form;
|
|
791
809
|
};
|
|
792
810
|
|
|
@@ -860,8 +878,13 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
|
|
|
860
878
|
restDecisionSchema,
|
|
861
879
|
signal,
|
|
862
880
|
),
|
|
863
|
-
raiseDecision: (input) =>
|
|
864
|
-
request(
|
|
881
|
+
raiseDecision: (input, files = []) =>
|
|
882
|
+
request(
|
|
883
|
+
"POST",
|
|
884
|
+
`${base}${at("decisions")}`,
|
|
885
|
+
files.length > 0 ? decisionFormOf(input, files) : input,
|
|
886
|
+
restDecisionSchema,
|
|
887
|
+
),
|
|
865
888
|
answerDecision: (id, input) =>
|
|
866
889
|
request("POST", `${base}${at("decisions", id)}/answer`, input, restDecisionSchema),
|
|
867
890
|
updateDecision: (id, patch) =>
|
|
@@ -1124,6 +1147,8 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
|
|
|
1124
1147
|
),
|
|
1125
1148
|
fileUrl: (fileId) => `${base}${at("files", fileId)}`,
|
|
1126
1149
|
readFile: (fileId, signal) => read(`${base}${at("files", fileId)}`, fileId, signal),
|
|
1150
|
+
personPicture: (personId, signal) =>
|
|
1151
|
+
read(`${base}${at("people", personId, "picture")}`, personId, signal),
|
|
1127
1152
|
frameUrl: (options) =>
|
|
1128
1153
|
withQuery(at("frame"), {
|
|
1129
1154
|
page: options.page,
|
|
@@ -1194,6 +1219,8 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
|
|
|
1194
1219
|
disable: () => request("POST", `${base}${at("disable")}`, undefined, restPluginDetailSchema),
|
|
1195
1220
|
updateSettings: (patch) =>
|
|
1196
1221
|
request("PATCH", `${base}${at("settings")}`, patch, restPluginDetailSchema),
|
|
1222
|
+
updateConsent: (consent) =>
|
|
1223
|
+
request("PATCH", `${base}${at("consent")}`, { consent }, restPluginDetailSchema),
|
|
1197
1224
|
setSecret: (name, value) =>
|
|
1198
1225
|
request("PUT", `${base}${at("secrets", name)}`, { value }, undefined),
|
|
1199
1226
|
removeSecret: (name) =>
|
package/src/compat.ts
CHANGED
|
@@ -113,7 +113,7 @@ export interface ClientPolicy {
|
|
|
113
113
|
* last changed what the command reads or sends (ADR-0038), whose changelog entry begins with
|
|
114
114
|
* `Breaking:`; `pnpm check` holds it to a version the package has, or is about to have.
|
|
115
115
|
*/
|
|
116
|
-
export const CLIENT_POLICY: ClientPolicy = { atLeast: "0.4.
|
|
116
|
+
export const CLIENT_POLICY: ClientPolicy = { atLeast: "0.4.4" };
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
119
|
* The oldest console this command works with (ADR-0038): raised by the release that last
|
package/src/components/board.tsx
CHANGED
|
@@ -71,13 +71,13 @@ export interface BoardProps {
|
|
|
71
71
|
export const FOLDED_STATES: readonly TaskState[] = ["done", "dropped"];
|
|
72
72
|
|
|
73
73
|
/** How far a mouse travels on a card before the press is a drag and not a click. */
|
|
74
|
-
const DRAG_SLACK = 4;
|
|
74
|
+
export const DRAG_SLACK = 4;
|
|
75
75
|
/**
|
|
76
76
|
* How long a finger rests on a card before the card comes loose, and how far it may wander
|
|
77
77
|
* meanwhile; a finger that moves sooner is scrolling the page, not moving the card.
|
|
78
78
|
*/
|
|
79
|
-
const HOLD_MS = 300;
|
|
80
|
-
const HOLD_SLACK = 8;
|
|
79
|
+
export const HOLD_MS = 300;
|
|
80
|
+
export const HOLD_SLACK = 8;
|
|
81
81
|
/** How near the pointer comes to the board's edge before the board scrolls to show more columns. */
|
|
82
82
|
const EDGE = 48;
|
|
83
83
|
/** How fast the board scrolls, in pixels a second, with the pointer at the edge itself; slower further in. */
|