@neta-art/cohub-cli 8.4.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -2
- package/dist/avatar.js +3 -1
- package/dist/board-export.d.ts +22 -41
- package/dist/board-export.js +178 -115
- package/dist/board-fonts.d.ts +7 -0
- package/dist/board-fonts.js +10 -0
- package/dist/board-kernel/browserAll.js +2760 -0
- package/dist/board-kernel/getPo2TextureFromSource.js +13138 -0
- package/dist/board-kernel/init.js +867 -0
- package/dist/board-kernel/webworkerAll.js +3 -0
- package/dist/board-kernel.d.ts +2 -0
- package/dist/board-kernel.js +28812 -0
- package/dist/board-time.d.ts +3 -0
- package/dist/board-time.js +28 -0
- package/dist/commands/apps.js +2 -2
- package/dist/commands/auth.js +2 -2
- package/dist/commands/boards/examples.d.ts +8 -2
- package/dist/commands/boards/examples.js +79 -411
- package/dist/commands/boards.d.ts +4 -9
- package/dist/commands/boards.js +356 -286
- package/dist/commands/desktop.d.ts +1 -5
- package/dist/commands/desktop.js +10 -19
- package/dist/commands/generations.js +1 -1
- package/dist/commands/me.js +1 -1
- package/dist/commands/public.js +3 -3
- package/dist/commands/references.js +3 -0
- package/dist/commands/referrals.js +2 -2
- package/dist/commands/run.js +3 -3
- package/dist/commands/runtime.d.ts +1 -1
- package/dist/commands/runtime.js +12 -8
- package/dist/commands/search.js +15 -7
- package/dist/commands/space-activity.js +3 -5
- package/dist/commands/space-displays.d.ts +2 -0
- package/dist/commands/space-displays.js +347 -0
- package/dist/commands/space-files.d.ts +5 -0
- package/dist/commands/space-files.js +215 -0
- package/dist/commands/space-invitations.js +3 -3
- package/dist/commands/spaces.d.ts +3 -11
- package/dist/commands/spaces.js +146 -75
- package/dist/fonts/geist-latin-400-normal.woff2 +0 -0
- package/dist/fonts/geist-latin-500-normal.woff2 +0 -0
- package/dist/fonts/geist-latin-600-normal.woff2 +0 -0
- package/dist/fonts/geist-latin-700-normal.woff2 +0 -0
- package/dist/fonts/geist-mono-latin-500-normal.woff2 +0 -0
- package/dist/fonts/geist-mono-latin-600-normal.woff2 +0 -0
- package/dist/fonts/geist-mono-latin-700-normal.woff2 +0 -0
- package/dist/index.js +7 -4
- package/dist/runtime/launch.d.ts +2 -0
- package/dist/runtime/launch.js +35 -8
- package/dist/runtime/presentation.d.ts +1 -0
- package/dist/runtime/presentation.js +3 -2
- package/dist/runtime/supervisor.d.ts +1 -0
- package/dist/runtime/supervisor.js +2 -1
- package/dist/self-update.d.ts +1 -0
- package/dist/self-update.js +52 -16
- package/dist/space-files/limit.d.ts +2 -0
- package/dist/space-files/limit.js +16 -0
- package/dist/space-files/operands.d.ts +16 -0
- package/dist/space-files/operands.js +33 -0
- package/dist/space-files/transfer.d.ts +53 -0
- package/dist/space-files/transfer.js +296 -0
- package/dist/space-files/upload.d.ts +14 -0
- package/dist/space-files/upload.js +53 -0
- package/dist/space.d.ts +18 -16
- package/dist/space.js +86 -39
- package/dist/web.d.ts +1 -0
- package/dist/web.js +6 -0
- package/package.json +8 -4
- package/dist/commands/board-domain.d.ts +0 -2
- package/dist/commands/board-domain.js +0 -16
- package/dist/commands/boards/animation.d.ts +0 -2
- package/dist/commands/boards/animation.js +0 -171
- package/dist/commands/boards/appearance.d.ts +0 -2
- package/dist/commands/boards/appearance.js +0 -127
- package/dist/commands/boards/batch.d.ts +0 -4
- package/dist/commands/boards/batch.js +0 -62
- package/dist/commands/boards/connections.d.ts +0 -2
- package/dist/commands/boards/connections.js +0 -95
- package/dist/commands/boards/context.d.ts +0 -31
- package/dist/commands/boards/context.js +0 -71
- package/dist/commands/boards/items.d.ts +0 -2
- package/dist/commands/boards/items.js +0 -184
- package/dist/commands/boards/transactions.d.ts +0 -12
- package/dist/commands/boards/transactions.js +0 -69
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { createBoardConnection } from "@neta-art/cohub/board";
|
|
3
|
-
import { handleHttp, json, jsonRequested, table } from "../../output.js";
|
|
4
|
-
import { mutateSemantic, readInput, readOptions, resolvedBoard, showUpdated, withJson, } from "./context.js";
|
|
5
|
-
const columns = [
|
|
6
|
-
{ key: "id", label: "ID" },
|
|
7
|
-
{ key: "source", label: "SOURCE" },
|
|
8
|
-
{ key: "target", label: "TARGET" },
|
|
9
|
-
{ key: "relation", label: "RELATION" },
|
|
10
|
-
{ key: "direction", label: "DIRECTION" },
|
|
11
|
-
{ key: "label", label: "LABEL" },
|
|
12
|
-
];
|
|
13
|
-
function rows(connections) {
|
|
14
|
-
return connections.map((connection) => ({
|
|
15
|
-
id: connection.id,
|
|
16
|
-
source: connection.source.itemId,
|
|
17
|
-
target: connection.target.itemId,
|
|
18
|
-
relation: connection.relation ?? "",
|
|
19
|
-
direction: connection.direction ?? "",
|
|
20
|
-
label: connection.label ?? "",
|
|
21
|
-
}));
|
|
22
|
-
}
|
|
23
|
-
export function registerBoardConnectionCommands(boards) {
|
|
24
|
-
const connections = boards.command("connections").description("Manage Board connections");
|
|
25
|
-
readOptions(withJson(connections.command("list <board>").alias("ls").description("List Board connections")))
|
|
26
|
-
.action(async (target, options) => {
|
|
27
|
-
try {
|
|
28
|
-
const board = await resolvedBoard(boards, target);
|
|
29
|
-
const result = await board.authoring(readInput(options, "connections"));
|
|
30
|
-
const value = result.connections ?? [];
|
|
31
|
-
if (jsonRequested(options))
|
|
32
|
-
return json(value);
|
|
33
|
-
table(rows(value), columns);
|
|
34
|
-
}
|
|
35
|
-
catch (cause) {
|
|
36
|
-
handleHttp(cause);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
withJson(connections.command("get <board> <connection-id>").description("Get one complete Board connection"))
|
|
40
|
-
.action(async (target, connectionId, options) => {
|
|
41
|
-
try {
|
|
42
|
-
const board = await resolvedBoard(boards, target);
|
|
43
|
-
const result = await board.authoring({ include: ["connections"], connectionIds: [connectionId] });
|
|
44
|
-
const connection = result.connections?.[0];
|
|
45
|
-
if (!connection)
|
|
46
|
-
throw new Error(`Connection not found: ${connectionId}`);
|
|
47
|
-
if (jsonRequested(options))
|
|
48
|
-
return json(connection);
|
|
49
|
-
table(rows([connection]), columns);
|
|
50
|
-
}
|
|
51
|
-
catch (cause) {
|
|
52
|
-
handleHttp(cause);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
withJson(connections.command("create <board> <source> <target>").description("Create a Board connection")
|
|
56
|
-
.option("--id <id>", "Connection id")
|
|
57
|
-
.option("--relation <relation>", "Relation type")
|
|
58
|
-
.option("--direction <direction>", "none, forward, backward, or both", "forward")
|
|
59
|
-
.option("--label <label>", "Connection label")
|
|
60
|
-
.option("--source-port <id>", "Source port id")
|
|
61
|
-
.option("--target-port <id>", "Target port id"))
|
|
62
|
-
.action(async (target, source, destination, options) => {
|
|
63
|
-
try {
|
|
64
|
-
const direction = options.direction ?? "forward";
|
|
65
|
-
if (!["none", "forward", "backward", "both"].includes(direction)) {
|
|
66
|
-
throw new Error("--direction must be none, forward, backward, or both");
|
|
67
|
-
}
|
|
68
|
-
const board = await resolvedBoard(boards, target);
|
|
69
|
-
const connection = createBoardConnection({
|
|
70
|
-
id: options.id ?? randomUUID(),
|
|
71
|
-
sourceItemId: source,
|
|
72
|
-
targetItemId: destination,
|
|
73
|
-
relation: options.relation,
|
|
74
|
-
direction: direction,
|
|
75
|
-
label: options.label,
|
|
76
|
-
sourcePortId: options.sourcePort,
|
|
77
|
-
targetPortId: options.targetPort,
|
|
78
|
-
});
|
|
79
|
-
showUpdated(await mutateSemantic(board, [{ type: "connection.create", connection }]), options);
|
|
80
|
-
}
|
|
81
|
-
catch (cause) {
|
|
82
|
-
handleHttp(cause);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
withJson(connections.command("delete <board> <connection-id>").alias("rm").description("Delete a Board connection"))
|
|
86
|
-
.action(async (target, connectionId, options) => {
|
|
87
|
-
try {
|
|
88
|
-
const board = await resolvedBoard(boards, target);
|
|
89
|
-
showUpdated(await mutateSemantic(board, [{ type: "connection.delete", connectionId }]), options);
|
|
90
|
-
}
|
|
91
|
-
catch (cause) {
|
|
92
|
-
handleHttp(cause);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { BoardAuthoringReadInput, BoardClient, BoardSemanticCommand } from "@neta-art/cohub";
|
|
2
|
-
import type { Command } from "commander";
|
|
3
|
-
export type JsonOptions = {
|
|
4
|
-
json?: boolean;
|
|
5
|
-
};
|
|
6
|
-
export type ReadOptions = JsonOptions & {
|
|
7
|
-
ids?: string;
|
|
8
|
-
};
|
|
9
|
-
export declare function withJson(command: Command): Command;
|
|
10
|
-
export declare function finite(value: string | undefined, name: string, fallback?: number): number;
|
|
11
|
-
export declare function readInput(options: ReadOptions, section: NonNullable<BoardAuthoringReadInput["include"]>[number]): BoardAuthoringReadInput;
|
|
12
|
-
export declare function readOptions(command: Command, label?: string): Command;
|
|
13
|
-
export declare function resolvedBoard(boards: Command, target: string): Promise<BoardClient>;
|
|
14
|
-
/**
|
|
15
|
-
* Send semantic commands with one automatic version-conflict retry when the
|
|
16
|
-
* caller did not pin a base version. An explicit `baseVersion` is a strict
|
|
17
|
-
* optimistic-concurrency check and must fail instead of being rebased.
|
|
18
|
-
*/
|
|
19
|
-
export declare function mutateSemantic(board: BoardClient, commands: BoardSemanticCommand[], options?: {
|
|
20
|
-
baseVersion?: number;
|
|
21
|
-
mutationId?: string;
|
|
22
|
-
dryRun?: boolean;
|
|
23
|
-
}): Promise<import("@neta-art/cohub").BoardMutationReceipt>;
|
|
24
|
-
export declare function showUpdated(result: {
|
|
25
|
-
board: {
|
|
26
|
-
version: number;
|
|
27
|
-
};
|
|
28
|
-
status?: string;
|
|
29
|
-
outcome?: string;
|
|
30
|
-
replayed?: boolean;
|
|
31
|
-
}, options: JsonOptions): void;
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { createClient } from "../../client.js";
|
|
2
|
-
import { resolveBoardId } from "../../board-command-support.js";
|
|
3
|
-
import { json, jsonRequested, ok } from "../../output.js";
|
|
4
|
-
import { resolveSpace } from "../../space.js";
|
|
5
|
-
export function withJson(command) {
|
|
6
|
-
return command.option("--json", "Output as JSON");
|
|
7
|
-
}
|
|
8
|
-
export function finite(value, name, fallback) {
|
|
9
|
-
if (value === undefined && fallback !== undefined)
|
|
10
|
-
return fallback;
|
|
11
|
-
const result = Number(value);
|
|
12
|
-
if (!Number.isFinite(result))
|
|
13
|
-
throw new Error(`${name} must be a finite number`);
|
|
14
|
-
return result;
|
|
15
|
-
}
|
|
16
|
-
export function readInput(options, section) {
|
|
17
|
-
const ids = options.ids?.split(",").map((id) => id.trim()).filter(Boolean);
|
|
18
|
-
return {
|
|
19
|
-
include: [section],
|
|
20
|
-
...(ids?.length ? { [`${section.slice(0, -1)}Ids`]: ids } : {}),
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export function readOptions(command, label = "Comma-separated resource IDs") {
|
|
24
|
-
return command.option("--ids <ids>", label);
|
|
25
|
-
}
|
|
26
|
-
export async function resolvedBoard(boards, target) {
|
|
27
|
-
const spaceId = await resolveSpace(boards);
|
|
28
|
-
const boardId = await resolveBoardId(spaceId, target);
|
|
29
|
-
return createClient().space(spaceId).board(boardId);
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Send semantic commands with one automatic version-conflict retry when the
|
|
33
|
-
* caller did not pin a base version. An explicit `baseVersion` is a strict
|
|
34
|
-
* optimistic-concurrency check and must fail instead of being rebased.
|
|
35
|
-
*/
|
|
36
|
-
export async function mutateSemantic(board, commands, options = {}) {
|
|
37
|
-
const pinnedVersion = options.baseVersion;
|
|
38
|
-
const baseVersion = pinnedVersion ?? (await board.summary()).board.version;
|
|
39
|
-
const send = (version) => board.mutateSemantic({
|
|
40
|
-
mutationId: options.mutationId,
|
|
41
|
-
baseVersion: version,
|
|
42
|
-
dryRun: options.dryRun ?? false,
|
|
43
|
-
commands,
|
|
44
|
-
});
|
|
45
|
-
try {
|
|
46
|
-
return await send(baseVersion);
|
|
47
|
-
}
|
|
48
|
-
catch (cause) {
|
|
49
|
-
if (options.dryRun || pinnedVersion !== undefined || !isVersionConflict(cause))
|
|
50
|
-
throw cause;
|
|
51
|
-
const retry = (await board.summary()).board.version;
|
|
52
|
-
return send(retry);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function isVersionConflict(error) {
|
|
56
|
-
return Boolean(error && typeof error === "object" &&
|
|
57
|
-
error.code === "VERSION_CONFLICT");
|
|
58
|
-
}
|
|
59
|
-
export function showUpdated(result, options) {
|
|
60
|
-
if (jsonRequested(options))
|
|
61
|
-
return json(result);
|
|
62
|
-
if (result.outcome === "dry-run") {
|
|
63
|
-
ok(`Validated against Board version ${result.board.version}; no changes written`);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (result.outcome === "noop" || result.status === "validated") {
|
|
67
|
-
ok(`Board already matches the requested state at version ${result.board.version}`);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
ok(`Board updated to version ${result.board.version}${result.replayed ? " (replayed)" : ""}`);
|
|
71
|
-
}
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { BoardAuthoringItemSchema, BoardItemPatchSchema, } from "@neta-art/cohub";
|
|
3
|
-
import { computeArrowFrame, computeDrawBounds } from "@neta-art/cohub/board";
|
|
4
|
-
import { BOARD_DOMAIN_INPUT_MAX_BYTES, readBoardJsonObject, } from "../../board-command-support.js";
|
|
5
|
-
import { handleHttp, json, jsonRequested, ok, table } from "../../output.js";
|
|
6
|
-
import { readInput, readOptions, resolvedBoard, withJson, } from "./context.js";
|
|
7
|
-
const baseVersion = (value, fallback) => {
|
|
8
|
-
if (value === undefined)
|
|
9
|
-
return fallback;
|
|
10
|
-
const parsed = Number(value);
|
|
11
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
12
|
-
throw new Error("base version must be a non-negative integer");
|
|
13
|
-
}
|
|
14
|
-
return parsed;
|
|
15
|
-
};
|
|
16
|
-
async function execute(boards, target, command, options) {
|
|
17
|
-
const board = await resolvedBoard(boards, target);
|
|
18
|
-
const snapshot = await board.summary();
|
|
19
|
-
const mutation = {
|
|
20
|
-
mutationId: options.mutationId?.trim() || randomUUID(),
|
|
21
|
-
baseVersion: baseVersion(options.baseVersion, snapshot.board.version),
|
|
22
|
-
dryRun: Boolean(options.dryRun),
|
|
23
|
-
commands: [command],
|
|
24
|
-
};
|
|
25
|
-
// Server-side validation: schema, version, references, cascade rules — the
|
|
26
|
-
// same checks the real write would run, just without persisting anything.
|
|
27
|
-
const receipt = await board.mutateSemantic(mutation);
|
|
28
|
-
if (jsonRequested(options))
|
|
29
|
-
return json(receipt);
|
|
30
|
-
if (options.dryRun) {
|
|
31
|
-
ok(`Validated against Board version ${receipt.board.version}; no changes written`);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
ok(`${receipt.replayed ? "Replayed" : "Applied"} mutation at Board version ${receipt.board.version}`);
|
|
35
|
-
}
|
|
36
|
-
function mutationOptions(command) {
|
|
37
|
-
return withJson(command)
|
|
38
|
-
.option("--base-version <version>", "Expected Board version; defaults to latest")
|
|
39
|
-
.option("--mutation-id <id>", "Stable id for safe retries")
|
|
40
|
-
.option("--dry-run", "Validate on the server (references, version, cascade) without writing");
|
|
41
|
-
}
|
|
42
|
-
function itemLayout(item) {
|
|
43
|
-
if ("position" in item) {
|
|
44
|
-
const size = "size" in item ? item.size : undefined;
|
|
45
|
-
return { x: item.position.x, y: item.position.y, width: size?.width ?? "", height: size?.height ?? "" };
|
|
46
|
-
}
|
|
47
|
-
if (item.type === "draw") {
|
|
48
|
-
const points = item.props.points;
|
|
49
|
-
const strokeWidth = item.style?.strokeWidth ?? 4;
|
|
50
|
-
const bounds = computeDrawBounds(points, strokeWidth);
|
|
51
|
-
return { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height };
|
|
52
|
-
}
|
|
53
|
-
const bounds = computeArrowFrame({
|
|
54
|
-
start: item.props.start,
|
|
55
|
-
end: item.props.end,
|
|
56
|
-
bend: item.props.bend,
|
|
57
|
-
size: item.style?.strokeWidth ?? 2.5,
|
|
58
|
-
});
|
|
59
|
-
return { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height };
|
|
60
|
-
}
|
|
61
|
-
export function registerBoardItemCommands(boards) {
|
|
62
|
-
const items = boards.command("items").description("Author Board items with semantic JSON");
|
|
63
|
-
readOptions(withJson(items.command("list <board>").alias("ls").description("List semantic Board items")))
|
|
64
|
-
.action(async (target, options) => {
|
|
65
|
-
try {
|
|
66
|
-
const board = await resolvedBoard(boards, target);
|
|
67
|
-
const snapshot = await board.authoring(readInput(options, "items"));
|
|
68
|
-
const items = snapshot.items ?? [];
|
|
69
|
-
if (jsonRequested(options))
|
|
70
|
-
return json(items);
|
|
71
|
-
table(items.map((item) => ({
|
|
72
|
-
id: item.id,
|
|
73
|
-
type: item.type,
|
|
74
|
-
...itemLayout(item),
|
|
75
|
-
})), [
|
|
76
|
-
{ key: "id", label: "ID" },
|
|
77
|
-
{ key: "type", label: "TYPE" },
|
|
78
|
-
{ key: "x", label: "X" },
|
|
79
|
-
{ key: "y", label: "Y" },
|
|
80
|
-
{ key: "width", label: "WIDTH" },
|
|
81
|
-
{ key: "height", label: "HEIGHT" },
|
|
82
|
-
]);
|
|
83
|
-
}
|
|
84
|
-
catch (cause) {
|
|
85
|
-
handleHttp(cause);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
withJson(items.command("get <board> <item-id>").description("Get one complete Board item"))
|
|
89
|
-
.action(async (target, itemId, options) => {
|
|
90
|
-
try {
|
|
91
|
-
const board = await resolvedBoard(boards, target);
|
|
92
|
-
const snapshot = await board.authoring({ include: ["items"], itemIds: [itemId] });
|
|
93
|
-
const item = snapshot.items?.[0];
|
|
94
|
-
if (!item)
|
|
95
|
-
throw new Error(`Item not found: ${itemId}`);
|
|
96
|
-
if (jsonRequested(options))
|
|
97
|
-
return json(item);
|
|
98
|
-
table([{
|
|
99
|
-
id: item.id,
|
|
100
|
-
type: item.type,
|
|
101
|
-
...itemLayout(item),
|
|
102
|
-
}], [
|
|
103
|
-
{ key: "id", label: "ID" },
|
|
104
|
-
{ key: "type", label: "TYPE" },
|
|
105
|
-
{ key: "x", label: "X" },
|
|
106
|
-
{ key: "y", label: "Y" },
|
|
107
|
-
{ key: "width", label: "WIDTH" },
|
|
108
|
-
{ key: "height", label: "HEIGHT" },
|
|
109
|
-
]);
|
|
110
|
-
}
|
|
111
|
-
catch (cause) {
|
|
112
|
-
handleHttp(cause);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
mutationOptions(items.command("create <board>")
|
|
116
|
-
.description("Create one item")
|
|
117
|
-
.requiredOption("-i, --input <file>", "Board Item JSON; use - for stdin")
|
|
118
|
-
.addHelpText("after", `
|
|
119
|
-
Minimal text item:
|
|
120
|
-
{"id":"title","type":"text","position":{"x":120,"y":80},"size":{"width":320,"height":48},"props":{"text":"Launch plan","fontSize":32},"style":{"color":"brand"}}`))
|
|
121
|
-
.action(async (target, options) => {
|
|
122
|
-
try {
|
|
123
|
-
const value = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
124
|
-
await execute(boards, target, {
|
|
125
|
-
type: "item.create",
|
|
126
|
-
item: BoardAuthoringItemSchema.parse(value),
|
|
127
|
-
}, options);
|
|
128
|
-
}
|
|
129
|
-
catch (cause) {
|
|
130
|
-
handleHttp(cause);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
mutationOptions(items.command("patch <board> <item-id>")
|
|
134
|
-
.description("Recursively merge item fields")
|
|
135
|
-
.requiredOption("-i, --input <file>", "Board Item patch JSON; use - for stdin")
|
|
136
|
-
.addHelpText("after", `
|
|
137
|
-
Objects merge recursively, arrays replace, and null clears optional fields.
|
|
138
|
-
Move and rename a text item without replacing its size or font:
|
|
139
|
-
{"position":{"x":160,"y":120},"props":{"text":"Updated"}}`))
|
|
140
|
-
.action(async (target, itemId, options) => {
|
|
141
|
-
try {
|
|
142
|
-
const value = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
143
|
-
await execute(boards, target, {
|
|
144
|
-
type: "item.patch",
|
|
145
|
-
itemId,
|
|
146
|
-
patch: BoardItemPatchSchema.parse(value),
|
|
147
|
-
}, options);
|
|
148
|
-
}
|
|
149
|
-
catch (cause) {
|
|
150
|
-
handleHttp(cause);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
mutationOptions(items.command("replace <board> <item-id>")
|
|
154
|
-
.description("Replace one complete item")
|
|
155
|
-
.requiredOption("-i, --input <file>", "Complete Board Item JSON; use - for stdin"))
|
|
156
|
-
.action(async (target, itemId, options) => {
|
|
157
|
-
try {
|
|
158
|
-
const value = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
159
|
-
await execute(boards, target, {
|
|
160
|
-
type: "item.replace",
|
|
161
|
-
itemId,
|
|
162
|
-
item: BoardAuthoringItemSchema.parse(value),
|
|
163
|
-
}, options);
|
|
164
|
-
}
|
|
165
|
-
catch (cause) {
|
|
166
|
-
handleHttp(cause);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
mutationOptions(items.command("delete <board> <item-id>").alias("rm")
|
|
170
|
-
.description("Delete one item")
|
|
171
|
-
.option("--cascade", "Atomically remove relations, effects, and animation references"))
|
|
172
|
-
.action(async (target, itemId, options) => {
|
|
173
|
-
try {
|
|
174
|
-
await execute(boards, target, {
|
|
175
|
-
type: "item.delete",
|
|
176
|
-
itemId,
|
|
177
|
-
cascade: Boolean(options.cascade),
|
|
178
|
-
}, options);
|
|
179
|
-
}
|
|
180
|
-
catch (cause) {
|
|
181
|
-
handleHttp(cause);
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { BoardTransactionRecord } from "@neta-art/cohub";
|
|
2
|
-
import type { Command } from "commander";
|
|
3
|
-
/** Compact `type×count` summary, e.g. `node.patch×3 connection.create`. */
|
|
4
|
-
export declare function summarizeOperations(operations: BoardTransactionRecord["operations"]): string;
|
|
5
|
-
export declare function transactionRows(transactions: BoardTransactionRecord[]): {
|
|
6
|
-
version: number;
|
|
7
|
-
createdAt: string;
|
|
8
|
-
actor: string;
|
|
9
|
-
via: import("@neta-art/cohub/board").BoardReplayActorKind;
|
|
10
|
-
changes: string;
|
|
11
|
-
}[];
|
|
12
|
-
export declare function registerBoardTransactionCommands(boards: Command): void;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { boardReplayActorKind } from "@neta-art/cohub/board";
|
|
2
|
-
import { handleHttp, json, jsonRequested, table } from "../../output.js";
|
|
3
|
-
import { finite, resolvedBoard, withJson } from "./context.js";
|
|
4
|
-
const columns = [
|
|
5
|
-
{ key: "version", label: "VERSION" },
|
|
6
|
-
{ key: "createdAt", label: "AT" },
|
|
7
|
-
{ key: "actor", label: "ACTOR" },
|
|
8
|
-
{ key: "via", label: "VIA" },
|
|
9
|
-
{ key: "changes", label: "CHANGES" },
|
|
10
|
-
];
|
|
11
|
-
/** Compact `type×count` summary, e.g. `node.patch×3 connection.create`. */
|
|
12
|
-
export function summarizeOperations(operations) {
|
|
13
|
-
const counts = new Map();
|
|
14
|
-
for (const operation of operations)
|
|
15
|
-
counts.set(operation.type, (counts.get(operation.type) ?? 0) + 1);
|
|
16
|
-
return [...counts.entries()].map(([type, count]) => (count > 1 ? `${type}×${count}` : type)).join(" ");
|
|
17
|
-
}
|
|
18
|
-
export function transactionRows(transactions) {
|
|
19
|
-
return transactions.map((transaction) => ({
|
|
20
|
-
version: transaction.version,
|
|
21
|
-
createdAt: transaction.createdAt,
|
|
22
|
-
actor: transaction.actorId,
|
|
23
|
-
via: boardReplayActorKind(transaction.source),
|
|
24
|
-
changes: summarizeOperations(transaction.operations),
|
|
25
|
-
}));
|
|
26
|
-
}
|
|
27
|
-
export function registerBoardTransactionCommands(boards) {
|
|
28
|
-
withJson(boards
|
|
29
|
-
.command("transactions <board>")
|
|
30
|
-
.alias("history")
|
|
31
|
-
.description("List the Board transaction log, newest first")
|
|
32
|
-
.option("--before <version>", "Only transactions below this version")
|
|
33
|
-
.option("--limit <count>", "Page size (max 500)", "50")
|
|
34
|
-
.option("--operations", "Include full operations and inverses in --json output")
|
|
35
|
-
.addHelpText("after", `
|
|
36
|
-
Every applied mutation is one transaction. --json prints the page summary; add
|
|
37
|
-
--operations for the full payloads, inverses and (on the newest page) the current
|
|
38
|
-
node and connection rows, which is what the web replay view consumes.`)).action(async (target, options) => {
|
|
39
|
-
try {
|
|
40
|
-
const board = await resolvedBoard(boards, target);
|
|
41
|
-
// The snapshot is only ever printed in full --json --operations output;
|
|
42
|
-
// every other mode would fetch the whole Board just to drop it.
|
|
43
|
-
const full = jsonRequested(options) && Boolean(options.operations);
|
|
44
|
-
const page = await board.transactions({
|
|
45
|
-
...(options.before !== undefined ? { before: finite(options.before, "--before") } : {}),
|
|
46
|
-
limit: finite(options.limit, "--limit", 50),
|
|
47
|
-
snapshot: full,
|
|
48
|
-
});
|
|
49
|
-
if (jsonRequested(options)) {
|
|
50
|
-
return json(full
|
|
51
|
-
? page
|
|
52
|
-
: {
|
|
53
|
-
...page,
|
|
54
|
-
transactions: page.transactions.map(({ operations, ...transaction }) => ({
|
|
55
|
-
...transaction,
|
|
56
|
-
operationCount: operations.length,
|
|
57
|
-
})),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
table(transactionRows(page.transactions), columns);
|
|
61
|
-
if (page.nextBefore !== null) {
|
|
62
|
-
console.log(`\nOlder transactions: --before ${page.nextBefore}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
catch (cause) {
|
|
66
|
-
handleHttp(cause);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
}
|