bikeshed-now 0.17.0 → 0.18.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 +11 -1
- package/package.json +1 -1
- package/src/index.mjs +63 -10
package/README.md
CHANGED
|
@@ -82,7 +82,10 @@ bikeshed publish platform.md --id 01-platform
|
|
|
82
82
|
`publish` says where it filed the document and who can read it there.
|
|
83
83
|
`bikeshed folders` counts who can read each folder, and `bikeshed folder <id>`
|
|
84
84
|
names them: filing a document in a folder shares it with all of them, and
|
|
85
|
-
nobody is emailed.
|
|
85
|
+
nobody is emailed. Under a folder shared by link or made public it is readable
|
|
86
|
+
by anyone holding the link, or anyone, and the output says through which
|
|
87
|
+
folder. Given a folder's link, `bikeshed folder <link>` lists what that link
|
|
88
|
+
opens, to read only.
|
|
86
89
|
|
|
87
90
|
Ids are shared by the whole deployment and never change, so choose one every
|
|
88
91
|
time: put the date first on something time-bound
|
|
@@ -146,6 +149,13 @@ to change visibility, `--public-edit true|false` to change open editing, or
|
|
|
146
149
|
Omitted settings stay unchanged. These commands are owner-only, including on
|
|
147
150
|
expired documents; the returned URL includes an unlisted key when needed.
|
|
148
151
|
|
|
152
|
+
`bikeshed visibility folder <id|link>` does the same for a folder, without
|
|
153
|
+
`--public-edit`. A folder's link or public level opens everything beneath it to
|
|
154
|
+
read, without an account: its subfolders, documents filed later, and documents
|
|
155
|
+
that are private on their own. Its expiry ends only that; the people it is
|
|
156
|
+
shared with keep their access. `bikeshed visibility document <id>` names a
|
|
157
|
+
document explicitly, for one whose id is `folder`.
|
|
158
|
+
|
|
149
159
|
`bikeshed rename-folder <id> <name>` and
|
|
150
160
|
`bikeshed move-folder <id> <folder|root>` require edit rights.
|
|
151
161
|
`bikeshed delete-folder <id>` is owner-only: contents move to its parent or the
|
package/package.json
CHANGED
package/src/index.mjs
CHANGED
|
@@ -84,7 +84,8 @@ const USAGE = `bikeshed ${VERSION} — publish and revise documents
|
|
|
84
84
|
|
|
85
85
|
Organising
|
|
86
86
|
bikeshed folders the folder tree, with ids and readers
|
|
87
|
-
bikeshed folder <id>
|
|
87
|
+
bikeshed folder <id|link> who can read a folder, and so what is in it;
|
|
88
|
+
with a folder's link, what the link opens
|
|
88
89
|
bikeshed mkdir <name> [--parent <id>] create a folder
|
|
89
90
|
bikeshed mv <id> <folder|root> move a document
|
|
90
91
|
bikeshed rename <id> <title> change a document's title; its address stays
|
|
@@ -96,6 +97,9 @@ Management
|
|
|
96
97
|
bikeshed visibility <id> [private|unlisted|public]
|
|
97
98
|
--public-edit true|false change open editing
|
|
98
99
|
--expires <ISO timestamp|none> set or clear expiry; omitted settings stay unchanged
|
|
100
|
+
bikeshed visibility folder <id> [private|unlisted|public] [--expires <ISO timestamp|none>]
|
|
101
|
+
a folder's link or public level opens EVERYTHING
|
|
102
|
+
beneath it to read; its expiry ends only that
|
|
99
103
|
bikeshed rename-folder <id> <name> rename a folder you can edit
|
|
100
104
|
bikeshed move-folder <id> <folder|root> move a folder and recompute inherited access
|
|
101
105
|
bikeshed delete-folder <id> owner-only; reparent contents, retain documents
|
|
@@ -816,20 +820,51 @@ function describeAccess(result) {
|
|
|
816
820
|
const where = result.filedIn ? `filed in ${result.filedIn.path}` : "at the root";
|
|
817
821
|
const who = access.readers === 1 ? "only you can read it" : `${access.readers} people can read it`;
|
|
818
822
|
const also = access.alsoReadableBy ? `, and ${access.alsoReadableBy}` : "";
|
|
819
|
-
|
|
823
|
+
// A folder's link or public level reaches what is filed beneath it; say which.
|
|
824
|
+
const through = access.throughFolders?.length
|
|
825
|
+
? ` (through ${access.throughFolders.map((f) => f.path).join(", ")})`
|
|
826
|
+
: "";
|
|
827
|
+
return `${where}: ${who}${also}${through}`;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* A folder's id, and its key when what was pasted is the folder's link,
|
|
832
|
+
* `…/folder/<id>?k=<key>` (SPEC §17 "A folder's reach").
|
|
833
|
+
*/
|
|
834
|
+
function folderArgs(ref) {
|
|
835
|
+
if (!/^https?:\/\//i.test(ref)) return { id: ref };
|
|
836
|
+
let url;
|
|
837
|
+
try { url = new URL(ref); } catch { fail(`not a folder link: ${ref}`); }
|
|
838
|
+
const match = url.pathname.match(/^\/folder\/([^/]+)\/?$/);
|
|
839
|
+
if (!match) fail(`not a folder link: ${ref}`);
|
|
840
|
+
const k = url.searchParams.get("k");
|
|
841
|
+
return { id: decodeURIComponent(match[1]), ...(k ? { k } : {}) };
|
|
820
842
|
}
|
|
821
843
|
|
|
822
844
|
/** Who can read one folder: and so who can read anything filed in it. */
|
|
823
|
-
async function folder(flags, [
|
|
824
|
-
if (!
|
|
845
|
+
async function folder(flags, [ref]) {
|
|
846
|
+
if (!ref) fail("usage: bikeshed folder <id|link>");
|
|
825
847
|
const { origin, token } = resolveAuth(flags);
|
|
826
|
-
const result = await callTool(origin, token, "get_folder",
|
|
848
|
+
const result = await callTool(origin, token, "get_folder", folderArgs(ref));
|
|
849
|
+
// Opened by its link: what it holds, to read, and nothing about who reads it.
|
|
850
|
+
if (result.viaLink) {
|
|
851
|
+
out(flags.json, result, () => {
|
|
852
|
+
console.log(`${result.path.map((p) => p.name).join("/")} ${result.folderId} (read-only, through its link)`);
|
|
853
|
+
for (const f of result.folders) console.log(` ${f.name}/ ${f.folderId}`);
|
|
854
|
+
for (const d of result.documents) console.log(` ${d.title} ${d.url ?? d.docId}`);
|
|
855
|
+
if (result.truncated) console.log(" (more than can be listed at once)");
|
|
856
|
+
});
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
827
859
|
out(flags.json, result, () => {
|
|
828
860
|
console.log(`${result.path} ${result.id}${result.canWrite ? "" : " (read-only)"}`);
|
|
829
861
|
console.log(`${result.readers} can read it${result.readers === 1 ? " (only you)" : ""}:`);
|
|
830
862
|
for (const person of result.people) console.log(` ${person}`);
|
|
831
863
|
if (result.morePeople > 0) console.log(` and ${result.morePeople} more`);
|
|
832
864
|
if (result.teams.length) console.log(`teams: ${result.teams.join(", ")}`);
|
|
865
|
+
for (const f of result.throughFolders ?? []) {
|
|
866
|
+
console.log(`also ${f.visibility === "public" ? "anyone" : "anyone with the link"}, through ${f.path}${f.expiresAt === null ? "" : ` until ${new Date(f.expiresAt).toISOString()}`}`);
|
|
867
|
+
}
|
|
833
868
|
console.log(`can edit: ${[...result.editors, ...(result.moreEditors ? [`and ${result.moreEditors} more`] : [])].join(", ")}`);
|
|
834
869
|
});
|
|
835
870
|
}
|
|
@@ -840,11 +875,18 @@ function printManagedAccess(result) {
|
|
|
840
875
|
for (const [role, people] of Object.entries(result.direct)) console.log(`${role}: ${people.join(", ") || "none"}`);
|
|
841
876
|
for (const grant of result.inherited) console.log(`inherited ${grant.role}: ${grant.principal} via ${grant.path}`);
|
|
842
877
|
for (const [role, people] of Object.entries(result.effective)) console.log(`${role}: ${people.join(", ") || "none"}`);
|
|
878
|
+
for (const r of result.inheritedReach ?? []) {
|
|
879
|
+
console.log(`readable by ${r.visibility === "public" ? "anyone" : "anyone with the folder's link"} through ${r.path}${r.expiresAt === null ? "" : `, until ${new Date(r.expiresAt).toISOString()}`}`);
|
|
880
|
+
}
|
|
843
881
|
if (result.visibility !== undefined) printVisibility(result);
|
|
844
882
|
}
|
|
845
883
|
|
|
846
884
|
function printVisibility(result) {
|
|
847
|
-
|
|
885
|
+
const expires = result.expiresAt === null ? "never" : new Date(result.expiresAt).toISOString();
|
|
886
|
+
// A folder has no open editing, and its date ends only its link.
|
|
887
|
+
console.log(result.publicEdit === undefined
|
|
888
|
+
? `visibility: ${result.visibility}; link ends: ${expires}`
|
|
889
|
+
: `visibility: ${result.visibility}; open editing: ${result.publicEdit ? "on" : "off"}; expires: ${expires}`);
|
|
848
890
|
console.log(result.url);
|
|
849
891
|
}
|
|
850
892
|
|
|
@@ -867,9 +909,16 @@ async function access(flags, [kind, ref, ...rest]) {
|
|
|
867
909
|
out(flags.json, result, () => printManagedAccess(result));
|
|
868
910
|
}
|
|
869
911
|
|
|
870
|
-
async function visibility(flags,
|
|
871
|
-
|
|
872
|
-
|
|
912
|
+
async function visibility(flags, positionals) {
|
|
913
|
+
// `visibility folder <id> …` and `visibility document <id> …` name the kind;
|
|
914
|
+
// a bare `visibility <id>` is a document, as it always was.
|
|
915
|
+
const named = ["folder", "document"].includes(positionals[0]) && positionals.length > 1;
|
|
916
|
+
const kind = named ? positionals[0] : "document";
|
|
917
|
+
const [ref, level, ...rest] = named ? positionals.slice(1) : positionals;
|
|
918
|
+
const usage = "usage: bikeshed visibility [folder|document] <id> [private|unlisted|public] [--public-edit true|false] [--expires <ISO timestamp|none>]";
|
|
919
|
+
if (!ref || rest.length || (level !== undefined && !["private", "unlisted", "public"].includes(level))) fail(usage);
|
|
920
|
+
if (kind === "folder" && flags["public-edit"] !== undefined) fail("a folder has no open editing: what its link opens is read-only");
|
|
921
|
+
const args = { id: kind === "folder" ? folderArgs(ref).id : docArgs(ref).id };
|
|
873
922
|
if (level !== undefined) args.visibility = level;
|
|
874
923
|
if (flags["public-edit"] !== undefined) {
|
|
875
924
|
if (!["true", "false"].includes(flags["public-edit"])) fail("--public-edit must be true or false");
|
|
@@ -883,7 +932,11 @@ async function visibility(flags, [ref, level, ...rest]) {
|
|
|
883
932
|
}
|
|
884
933
|
}
|
|
885
934
|
const { origin, token } = resolveAuth(flags);
|
|
886
|
-
const
|
|
935
|
+
const changing = Object.keys(args).length > 1;
|
|
936
|
+
const tool = kind === "folder"
|
|
937
|
+
? (changing ? "set_folder_visibility" : "get_folder_access")
|
|
938
|
+
: (changing ? "set_document_visibility" : "get_document_access");
|
|
939
|
+
const result = await callTool(origin, token, tool, args);
|
|
887
940
|
out(flags.json, result, () => printVisibility(result));
|
|
888
941
|
}
|
|
889
942
|
|