@whatcha/cli 0.1.0 → 0.4.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 +223 -25
- package/dist/auth/session.js +50 -0
- package/dist/auth/session.js.map +1 -1
- package/dist/auth/token-store.js.map +1 -1
- package/dist/cli.js +44 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/diaries.js +14 -3
- package/dist/commands/diaries.js.map +1 -1
- package/dist/commands/diary-cover.js +54 -0
- package/dist/commands/diary-cover.js.map +1 -0
- package/dist/commands/login.js +82 -7
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/profiles.js +187 -11
- package/dist/commands/profiles.js.map +1 -1
- package/dist/commands/publish.js +81 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/read.js +7 -1
- package/dist/commands/read.js.map +1 -1
- package/dist/commands/shared-diary.js +255 -0
- package/dist/commands/shared-diary.js.map +1 -0
- package/dist/commands/whoami.js +50 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/commands/wiki.js +386 -0
- package/dist/commands/wiki.js.map +1 -0
- package/dist/commands/write.js +119 -7
- package/dist/commands/write.js.map +1 -1
- package/dist/http-client.js +16 -0
- package/dist/http-client.js.map +1 -1
- package/dist/lib/append-entry.js +90 -11
- package/dist/lib/append-entry.js.map +1 -1
- package/dist/lib/entry-date.js +22 -2
- package/dist/lib/entry-date.js.map +1 -1
- package/dist/lib/entry-get.js +31 -0
- package/dist/lib/entry-get.js.map +1 -0
- package/dist/lib/image-upload.js +126 -0
- package/dist/lib/image-upload.js.map +1 -0
- package/dist/lib/personal-access-token.js +46 -0
- package/dist/lib/personal-access-token.js.map +1 -0
- package/dist/lib/profile-family.js +29 -0
- package/dist/lib/profile-family.js.map +1 -0
- package/dist/lib/publication.js +55 -0
- package/dist/lib/publication.js.map +1 -0
- package/package.json +4 -4
package/dist/http-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-client.js","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":";;;AAAA,qCAAoC;
|
|
1
|
+
{"version":3,"file":"http-client.js","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/append-entry.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_TAGS = exports.MAX_TAG_LENGTH = exports.MAX_TITLE_LENGTH = void 0;
|
|
4
|
+
exports.normalizeTags = normalizeTags;
|
|
5
|
+
exports.mergeHeader = mergeHeader;
|
|
3
6
|
exports.writeEntry = writeEntry;
|
|
4
7
|
const errors_1 = require("../errors");
|
|
8
|
+
const entry_get_1 = require("./entry-get");
|
|
9
|
+
/** The API's own limits (entries-content.ts `contentSchema`). */
|
|
10
|
+
exports.MAX_TITLE_LENGTH = 200;
|
|
11
|
+
exports.MAX_TAG_LENGTH = 64;
|
|
12
|
+
exports.MAX_TAGS = 32;
|
|
5
13
|
function existingEntryIdFrom(err) {
|
|
6
14
|
if (err.status !== 409 || err.code !== "VERSION_CONFLICT")
|
|
7
15
|
return null;
|
|
@@ -13,21 +21,60 @@ function headerOf(entry) {
|
|
|
13
21
|
title: entry.title,
|
|
14
22
|
mood: entry.mood,
|
|
15
23
|
location: entry.location,
|
|
24
|
+
// Only sent back when the API sent them: an older server that does not
|
|
25
|
+
// know the fields must not be handed keys it would drop or refuse.
|
|
26
|
+
...(entry.locationLat !== undefined ? { locationLat: entry.locationLat } : {}),
|
|
27
|
+
...(entry.locationLng !== undefined ? { locationLng: entry.locationLng } : {}),
|
|
16
28
|
tags: entry.tags,
|
|
17
29
|
coverMediaAssetId: entry.coverMediaAssetId,
|
|
18
30
|
};
|
|
19
31
|
}
|
|
32
|
+
/** Tags trimmed, empties dropped, exact duplicates removed -- the app's own rule (entry editor's _addTag). */
|
|
33
|
+
function normalizeTags(tags) {
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const raw of tags) {
|
|
36
|
+
const tag = raw.trim();
|
|
37
|
+
if (tag && !out.includes(tag))
|
|
38
|
+
out.push(tag);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The header an append saves: the entry's own, with `meta` merged in under
|
|
44
|
+
* the append rule. Exported for the tests.
|
|
45
|
+
*/
|
|
46
|
+
function mergeHeader(header, meta) {
|
|
47
|
+
const wanted = meta.title?.trim();
|
|
48
|
+
const hasTitle = Boolean(header.title && header.title.trim());
|
|
49
|
+
const title = hasTitle ? header.title : wanted ? wanted : header.title;
|
|
50
|
+
const tags = normalizeTags([...(header.tags ?? []), ...(meta.tags ?? [])]);
|
|
51
|
+
// Kept as the entry had them when nothing new was asked for -- a merge
|
|
52
|
+
// that only re-normalised somebody else's tags would still be a change.
|
|
53
|
+
const finalTags = meta.tags && meta.tags.length > 0 ? tags : header.tags;
|
|
54
|
+
if (finalTags.length > exports.MAX_TAGS) {
|
|
55
|
+
throw new errors_1.CliUsageError(`This entry would end up with ${finalTags.length} tags; the limit is ${exports.MAX_TAGS}. Nothing was written.`);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
header: { ...header, title, tags: finalTags },
|
|
59
|
+
titleKept: Boolean(wanted && hasTitle && header.title.trim() !== wanted),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
20
62
|
/**
|
|
21
63
|
* What the stored body should read as after appending `text`, mirroring the
|
|
22
64
|
* API's own `appendedBody` (apps/api/src/modules/diaries/entries/
|
|
23
65
|
* entries-content.ts, merged to master in #1424): existing text, trimmed,
|
|
24
66
|
* then a blank line, then the new text -- or just the new text if there was
|
|
25
|
-
* none yet.
|
|
67
|
+
* none yet. No text (only images or a cover, #51) leaves it as it was.
|
|
26
68
|
*/
|
|
27
69
|
function expectedAppendedBody(existingBody, text) {
|
|
28
70
|
const trimmed = existingBody.trimEnd();
|
|
71
|
+
if (!text.trim())
|
|
72
|
+
return trimmed;
|
|
29
73
|
return trimmed ? `${trimmed}\n\n${text}` : text;
|
|
30
74
|
}
|
|
75
|
+
function mediaOf(ids) {
|
|
76
|
+
return (ids ?? []).map((mediaAssetId) => ({ mediaAssetId }));
|
|
77
|
+
}
|
|
31
78
|
/**
|
|
32
79
|
* The one thing this whole command exists to get right: add `text` to
|
|
33
80
|
* `diaryId`'s entry for `occurredAt`'s day without ever touching what
|
|
@@ -60,14 +107,33 @@ function expectedAppendedBody(existingBody, text) {
|
|
|
60
107
|
* Either way, nothing is ever PATCHed as a plain replacement of a body this
|
|
61
108
|
* function did not just read.
|
|
62
109
|
*/
|
|
63
|
-
async function writeEntry(client, diaryId, occurredAt, text) {
|
|
110
|
+
async function writeEntry(client, diaryId, occurredAt, text, meta = {}) {
|
|
111
|
+
const title = meta.title?.trim() || undefined;
|
|
112
|
+
const tags = normalizeTags(meta.tags ?? []);
|
|
113
|
+
const cover = meta.coverMediaAssetId;
|
|
114
|
+
const images = meta.images ?? [];
|
|
64
115
|
try {
|
|
65
116
|
const created = await client.post("/entries", {
|
|
66
117
|
diaryId,
|
|
67
118
|
occurredAt: occurredAt.toISOString(),
|
|
68
|
-
content: {
|
|
119
|
+
content: {
|
|
120
|
+
body: text,
|
|
121
|
+
...(title ? { title } : {}),
|
|
122
|
+
...(tags.length > 0 ? { tags } : {}),
|
|
123
|
+
...(cover ? { coverMediaAssetId: cover } : {}),
|
|
124
|
+
},
|
|
125
|
+
...(images.length > 0 ? { media: mediaOf(images) } : {}),
|
|
69
126
|
});
|
|
70
|
-
return {
|
|
127
|
+
return {
|
|
128
|
+
entryId: created.id,
|
|
129
|
+
headCommitId: created.headCommitId,
|
|
130
|
+
mode: "created",
|
|
131
|
+
verified: null,
|
|
132
|
+
title: title ?? null,
|
|
133
|
+
tags,
|
|
134
|
+
titleKept: false,
|
|
135
|
+
...(cover ? { coverKept: false } : {}),
|
|
136
|
+
};
|
|
71
137
|
}
|
|
72
138
|
catch (err) {
|
|
73
139
|
if (!(err instanceof errors_1.ApiError))
|
|
@@ -75,20 +141,32 @@ async function writeEntry(client, diaryId, occurredAt, text) {
|
|
|
75
141
|
const existingEntryId = existingEntryIdFrom(err);
|
|
76
142
|
if (!existingEntryId)
|
|
77
143
|
throw err;
|
|
78
|
-
return appendToExisting(client, existingEntryId, text);
|
|
144
|
+
return appendToExisting(client, existingEntryId, text, { title, tags, images, coverMediaAssetId: cover });
|
|
79
145
|
}
|
|
80
146
|
}
|
|
81
|
-
async function appendToExisting(client, entryId, text) {
|
|
82
|
-
const existing = await
|
|
83
|
-
const
|
|
147
|
+
async function appendToExisting(client, entryId, text, meta) {
|
|
148
|
+
const existing = await (0, entry_get_1.getEntry)(client, entryId);
|
|
149
|
+
const merged = mergeHeader(headerOf(existing), meta);
|
|
150
|
+
const { titleKept } = merged;
|
|
151
|
+
let header = merged.header;
|
|
152
|
+
let coverKept;
|
|
153
|
+
if (meta.coverMediaAssetId) {
|
|
154
|
+
coverKept = Boolean(header.coverMediaAssetId);
|
|
155
|
+
if (!coverKept)
|
|
156
|
+
header = { ...header, coverMediaAssetId: meta.coverMediaAssetId };
|
|
157
|
+
}
|
|
158
|
+
const after = { title: header.title, tags: header.tags, titleKept, ...(coverKept !== undefined ? { coverKept } : {}) };
|
|
159
|
+
// Sent only when there are images to add: leaving `media` out keeps the entry's own.
|
|
160
|
+
const media = meta.images && meta.images.length > 0 ? { media: [...(0, entry_get_1.entryMedia)(existing), ...mediaOf(meta.images)] } : {};
|
|
84
161
|
try {
|
|
85
162
|
const saved = await client.patch(`/entries/${entryId}`, {
|
|
86
163
|
content: { body: text, ...header },
|
|
164
|
+
...media,
|
|
87
165
|
appendBody: true,
|
|
88
166
|
expectedHeadCommitId: existing.headCommitId,
|
|
89
167
|
});
|
|
90
168
|
const verified = await verifyAppend(client, entryId, existing.body, text);
|
|
91
|
-
return { entryId, headCommitId: saved.headCommitId, mode: "appended", verified };
|
|
169
|
+
return { entryId, headCommitId: saved.headCommitId, mode: "appended", verified, ...after };
|
|
92
170
|
}
|
|
93
171
|
catch (err) {
|
|
94
172
|
if (!(err instanceof errors_1.ApiError))
|
|
@@ -105,6 +183,7 @@ async function appendToExisting(client, entryId, text) {
|
|
|
105
183
|
const saved = await client
|
|
106
184
|
.patch(`/entries/${entryId}`, {
|
|
107
185
|
content: { body: mergedBody, ...header },
|
|
186
|
+
...media,
|
|
108
187
|
expectedHeadCommitId: existing.headCommitId,
|
|
109
188
|
})
|
|
110
189
|
.catch((fallbackErr) => {
|
|
@@ -113,7 +192,7 @@ async function appendToExisting(client, entryId, text) {
|
|
|
113
192
|
throw fallbackErr;
|
|
114
193
|
});
|
|
115
194
|
const verified = await verifyAppend(client, entryId, existing.body, text);
|
|
116
|
-
return { entryId, headCommitId: saved.headCommitId, mode: "appended-fallback", verified };
|
|
195
|
+
return { entryId, headCommitId: saved.headCommitId, mode: "appended-fallback", verified, ...after };
|
|
117
196
|
}
|
|
118
197
|
}
|
|
119
198
|
function concurrentWriteError(err) {
|
|
@@ -135,7 +214,7 @@ function concurrentWriteError(err) {
|
|
|
135
214
|
* this does not mistake a corrupted write for a clean one.
|
|
136
215
|
*/
|
|
137
216
|
async function verifyAppend(client, entryId, bodyBeforeWrite, text) {
|
|
138
|
-
const after = await
|
|
217
|
+
const after = await (0, entry_get_1.getEntry)(client, entryId);
|
|
139
218
|
return after.body === expectedAppendedBody(bodyBeforeWrite, text);
|
|
140
219
|
}
|
|
141
220
|
//# sourceMappingURL=append-entry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"append-entry.js","sourceRoot":"","sources":["../../src/lib/append-entry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"append-entry.js","sourceRoot":"","sources":["../../src/lib/append-entry.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/entry-date.js
CHANGED
|
@@ -53,10 +53,30 @@ function timeKeyInTimeZone(date, timeZone) {
|
|
|
53
53
|
*/
|
|
54
54
|
function zonedTimeToUtc(dateStr, timeStr, timeZone) {
|
|
55
55
|
const asIfUtc = new Date(`${dateStr}T${timeStr}:00.000Z`);
|
|
56
|
-
const
|
|
57
|
-
const diff = asIfUtc.getTime() - readInZone.getTime();
|
|
56
|
+
const diff = asIfUtc.getTime() - wallClockAsUtcMs(asIfUtc, timeZone);
|
|
58
57
|
return new Date(asIfUtc.getTime() + diff);
|
|
59
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* The wall-clock reading of `instant` in `timeZone`, re-read as if it were
|
|
61
|
+
* UTC. Assembled from formatToParts and Date.UTC rather than by parsing a
|
|
62
|
+
* toLocaleString() back through `new Date()` -- that parse happens in the
|
|
63
|
+
* MACHINE's timezone, which shifted every --date/--time by the machine's
|
|
64
|
+
* own UTC offset on anything not set to UTC.
|
|
65
|
+
*/
|
|
66
|
+
function wallClockAsUtcMs(instant, timeZone) {
|
|
67
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
68
|
+
timeZone,
|
|
69
|
+
year: "numeric",
|
|
70
|
+
month: "2-digit",
|
|
71
|
+
day: "2-digit",
|
|
72
|
+
hour: "2-digit",
|
|
73
|
+
minute: "2-digit",
|
|
74
|
+
second: "2-digit",
|
|
75
|
+
hourCycle: "h23",
|
|
76
|
+
}).formatToParts(instant);
|
|
77
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value);
|
|
78
|
+
return Date.UTC(get("year"), get("month") - 1, get("day"), get("hour"), get("minute"), get("second"));
|
|
79
|
+
}
|
|
60
80
|
/**
|
|
61
81
|
* Resolves `--date`/`--time` into the `occurredAt` instant to send the API,
|
|
62
82
|
* defaulting both to "now" in the diary's own timezone -- not the machine's,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry-date.js","sourceRoot":"","sources":["../../src/lib/entry-date.ts"],"names":[],"mappings":";;AAKA,0CASC;AAED,0CAEC;AAGD,8CAMC;AAGD,8CAOC;AAcD,
|
|
1
|
+
{"version":3,"file":"entry-date.js","sourceRoot":"","sources":["../../src/lib/entry-date.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getEntry = getEntry;
|
|
4
|
+
exports.entryBody = entryBody;
|
|
5
|
+
exports.entryMedia = entryMedia;
|
|
6
|
+
/**
|
|
7
|
+
* `GET /entries/:id`, with `body` always a string (#53).
|
|
8
|
+
*
|
|
9
|
+
* The API answers with the commit's `items`; a top-level `body` came back
|
|
10
|
+
* only with the fix for #53. Every server without it made `whatcha read`
|
|
11
|
+
* print "undefined" and made `whatcha write` unable to confirm that an
|
|
12
|
+
* append kept the text before it. So the text item is the fallback, and
|
|
13
|
+
* nothing here trusts `body` to be present.
|
|
14
|
+
*/
|
|
15
|
+
async function getEntry(client, entryId) {
|
|
16
|
+
const entry = await client.get(`/entries/${entryId}`);
|
|
17
|
+
return { ...entry, body: entryBody(entry) };
|
|
18
|
+
}
|
|
19
|
+
function entryBody(entry) {
|
|
20
|
+
if (typeof entry.body === "string")
|
|
21
|
+
return entry.body;
|
|
22
|
+
const text = (entry.items ?? []).find((i) => i.type === "text" && i.text);
|
|
23
|
+
return text?.text?.content ?? "";
|
|
24
|
+
}
|
|
25
|
+
/** The entry's images in order, as a save must resend them to keep them. */
|
|
26
|
+
function entryMedia(entry) {
|
|
27
|
+
return (entry.items ?? [])
|
|
28
|
+
.filter((i) => i.type === "media" && i.media?.mediaAssetId)
|
|
29
|
+
.map((i) => ({ mediaAssetId: i.media.mediaAssetId, caption: i.media.caption ?? null }));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=entry-get.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-get.js","sourceRoot":"","sources":["../../src/lib/entry-get.ts"],"names":[],"mappings":";;AAYA,4BAGC;AAED,8BAIC;AAGD,gCAIC;AAzBD;;;;;;;;GAQG;AACI,KAAK,UAAU,QAAQ,CAAC,MAAqB,EAAE,OAAe;IACnE,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,GAAG,CAAc,YAAY,OAAO,EAAE,CAAC,CAAC;IACnE,OAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;AAC9C,CAAC;AAED,SAAgB,SAAS,CAAC,KAA0C;IAClE,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC;IACtD,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1E,OAAO,IAAI,EAAE,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;AACnC,CAAC;AAED,4EAA4E;AAC5E,SAAgB,UAAU,CAAC,KAAiC;IAC1D,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;SACvB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC;SAC1D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,KAAM,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC,KAAM,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;AAC9F,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PUBLIC_SQUARE_GROUP_ID = exports.MAX_UPLOAD_BYTES = void 0;
|
|
4
|
+
exports.sniffImageType = sniffImageType;
|
|
5
|
+
exports.readImageFile = readImageFile;
|
|
6
|
+
exports.uploadImage = uploadImage;
|
|
7
|
+
exports.wikiImageRef = wikiImageRef;
|
|
8
|
+
const promises_1 = require("node:fs/promises");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const errors_1 = require("../errors");
|
|
11
|
+
/**
|
|
12
|
+
* Local image files to media assets, for `whatcha wiki write --image/--cover`
|
|
13
|
+
* and `whatcha wiki cover` (#1667). `readImageFile` also checks the file
|
|
14
|
+
* `whatcha profile-add --avatar` sends (#1699).
|
|
15
|
+
*
|
|
16
|
+
* The upload is the app's own three steps -- `POST /uploads` (into the
|
|
17
|
+
* page's group), PUT the bytes to the signed URL, `POST /uploads/:id/complete`
|
|
18
|
+
* -- so the server applies the same limits it applies to an entry's photos.
|
|
19
|
+
* The checks here are those same limits, made before anything is sent, so a
|
|
20
|
+
* wrong file is a clear message instead of a half-done upload:
|
|
21
|
+
*
|
|
22
|
+
* - at most 40 MB (the API's UPLOAD_LIMITS.maxFileBytes);
|
|
23
|
+
* - a JPEG, PNG, WebP, GIF or AVIF image -- the image types the API accepts,
|
|
24
|
+
* and a wiki page shows nothing else. The type is read from the file's
|
|
25
|
+
* first bytes, the way the server resolves it; the extension only fills in
|
|
26
|
+
* for an AVIF variant the magic-number check does not recognise.
|
|
27
|
+
*/
|
|
28
|
+
exports.MAX_UPLOAD_BYTES = 40 * 1024 * 1024;
|
|
29
|
+
/** Public Square's group: where the public wiki's images are uploaded. */
|
|
30
|
+
exports.PUBLIC_SQUARE_GROUP_ID = "00000000-0000-0000-0000-0000000000aa";
|
|
31
|
+
const BY_EXTENSION = {
|
|
32
|
+
".jpg": "image/jpeg",
|
|
33
|
+
".jpeg": "image/jpeg",
|
|
34
|
+
".png": "image/png",
|
|
35
|
+
".webp": "image/webp",
|
|
36
|
+
".gif": "image/gif",
|
|
37
|
+
".avif": "image/avif",
|
|
38
|
+
};
|
|
39
|
+
function ascii(bytes, start, end) {
|
|
40
|
+
return String.fromCharCode(...bytes.subarray(start, end));
|
|
41
|
+
}
|
|
42
|
+
/** The image type the bytes are, or null. Mirrors the API's core/media/image-type.ts. */
|
|
43
|
+
function sniffImageType(bytes) {
|
|
44
|
+
if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
|
|
45
|
+
return "image/jpeg";
|
|
46
|
+
if (bytes.length >= 8 && bytes[0] === 0x89 && ascii(bytes, 1, 4) === "PNG" && bytes[4] === 0x0d && bytes[5] === 0x0a) {
|
|
47
|
+
return "image/png";
|
|
48
|
+
}
|
|
49
|
+
if (bytes.length >= 6 && (ascii(bytes, 0, 6) === "GIF87a" || ascii(bytes, 0, 6) === "GIF89a"))
|
|
50
|
+
return "image/gif";
|
|
51
|
+
if (bytes.length >= 12 && ascii(bytes, 0, 4) === "RIFF" && ascii(bytes, 8, 12) === "WEBP")
|
|
52
|
+
return "image/webp";
|
|
53
|
+
if (bytes.length >= 16 && ascii(bytes, 4, 8) === "ftyp") {
|
|
54
|
+
const box = Math.min(((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0, bytes.length, 64);
|
|
55
|
+
const brands = [ascii(bytes, 8, 12)];
|
|
56
|
+
for (let i = 16; i + 4 <= box; i += 4)
|
|
57
|
+
brands.push(ascii(bytes, i, i + 4));
|
|
58
|
+
if (brands.includes("avif") || brands.includes("avis"))
|
|
59
|
+
return "image/avif";
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function megabytes(n) {
|
|
64
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
65
|
+
}
|
|
66
|
+
/** Reads and checks one image file. Throws CliUsageError with the reason when it cannot be uploaded. */
|
|
67
|
+
async function readImageFile(path) {
|
|
68
|
+
let size;
|
|
69
|
+
try {
|
|
70
|
+
const info = await (0, promises_1.stat)(path);
|
|
71
|
+
if (!info.isFile())
|
|
72
|
+
throw new errors_1.CliUsageError(`${path} is not a file.`);
|
|
73
|
+
size = info.size;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (err instanceof errors_1.CliUsageError)
|
|
77
|
+
throw err;
|
|
78
|
+
throw new errors_1.CliUsageError(`Cannot read ${path}: ${err.code ?? err.message}`);
|
|
79
|
+
}
|
|
80
|
+
// Before reading it: a 2 GB video passed by mistake should not be loaded to find out.
|
|
81
|
+
if (size > exports.MAX_UPLOAD_BYTES) {
|
|
82
|
+
throw new errors_1.CliUsageError(`${path} is ${megabytes(size)}; an upload is at most ${exports.MAX_UPLOAD_BYTES / (1024 * 1024)} MB.`);
|
|
83
|
+
}
|
|
84
|
+
if (size === 0)
|
|
85
|
+
throw new errors_1.CliUsageError(`${path} is empty.`);
|
|
86
|
+
const handle = await (0, promises_1.open)(path, "r");
|
|
87
|
+
let bytes;
|
|
88
|
+
try {
|
|
89
|
+
bytes = new Uint8Array(await handle.readFile());
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await handle.close();
|
|
93
|
+
}
|
|
94
|
+
if (bytes.length > exports.MAX_UPLOAD_BYTES) {
|
|
95
|
+
throw new errors_1.CliUsageError(`${path} is ${megabytes(bytes.length)}; an upload is at most ${exports.MAX_UPLOAD_BYTES / (1024 * 1024)} MB.`);
|
|
96
|
+
}
|
|
97
|
+
const byExtension = BY_EXTENSION[(0, node_path_1.extname)(path).toLowerCase()];
|
|
98
|
+
const sniffed = sniffImageType(bytes);
|
|
99
|
+
// An ISO-BMFF file (ftyp box) named .avif whose brands this check does not list.
|
|
100
|
+
const contentType = sniffed ?? (byExtension === "image/avif" && ascii(bytes, 4, 8) === "ftyp" ? byExtension : null);
|
|
101
|
+
if (!contentType) {
|
|
102
|
+
throw new errors_1.CliUsageError(`${path} is not a JPEG, PNG, WebP, GIF or AVIF image; nothing else can be shown.`);
|
|
103
|
+
}
|
|
104
|
+
return { path, fileName: (0, node_path_1.basename)(path), contentType, bytes };
|
|
105
|
+
}
|
|
106
|
+
/** `POST /uploads` -> PUT to the signed URL -> `POST /uploads/:id/complete`. The new media asset's id. */
|
|
107
|
+
async function uploadImage(client, groupId, image) {
|
|
108
|
+
const ticket = await client.post("/uploads", {
|
|
109
|
+
fileName: image.fileName,
|
|
110
|
+
contentType: image.contentType,
|
|
111
|
+
size: image.bytes.length,
|
|
112
|
+
groupId,
|
|
113
|
+
});
|
|
114
|
+
await client.putBytes(ticket.uploadUrl, ticket.headers ?? { "Content-Type": image.contentType }, image.bytes);
|
|
115
|
+
await client.post(`/uploads/${ticket.mediaAssetId}/complete`);
|
|
116
|
+
return ticket.mediaAssetId;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* ``, the wiki's image reference. The alt text is the file
|
|
120
|
+
* name; a `]` or line break in it would end the reference early.
|
|
121
|
+
*/
|
|
122
|
+
function wikiImageRef(alt, mediaAssetId) {
|
|
123
|
+
const clean = alt.replace(/[[\]\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
124
|
+
return ``;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=image-upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-upload.js","sourceRoot":"","sources":["../../src/lib/image-upload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WHERE_KEYS_ARE_ISSUED = exports.PERSONAL_ACCESS_TOKEN_PREFIX = void 0;
|
|
4
|
+
exports.looksLikePersonalAccessToken = looksLikePersonalAccessToken;
|
|
5
|
+
exports.isWellFormedPersonalAccessToken = isWellFormedPersonalAccessToken;
|
|
6
|
+
exports.translateKeyError = translateKeyError;
|
|
7
|
+
const errors_1 = require("../errors");
|
|
8
|
+
/**
|
|
9
|
+
* Personal access keys (#1469, server side in PR #1550; CLI side #1663).
|
|
10
|
+
*
|
|
11
|
+
* The server tells a key from a Supabase session JWT by this literal prefix
|
|
12
|
+
* alone (apps/api/src/modules/account/auth/access-tokens.ts,
|
|
13
|
+
* `looksLikeAccessToken`), and so does the CLI: a key is sent as-is as the
|
|
14
|
+
* Bearer credential, never refreshed, and never exchanged for anything.
|
|
15
|
+
*/
|
|
16
|
+
exports.PERSONAL_ACCESS_TOKEN_PREFIX = "whatcha_pat_";
|
|
17
|
+
/** `whatcha_pat_<24 hex>_<64 hex>` -- the server's own WIRE_FORM. */
|
|
18
|
+
const WIRE_FORM = /^whatcha_pat_[0-9a-f]{24}_[0-9a-f]{64}$/;
|
|
19
|
+
function looksLikePersonalAccessToken(raw) {
|
|
20
|
+
return raw.trim().startsWith(exports.PERSONAL_ACCESS_TOKEN_PREFIX);
|
|
21
|
+
}
|
|
22
|
+
function isWellFormedPersonalAccessToken(raw) {
|
|
23
|
+
return WIRE_FORM.test(raw.trim());
|
|
24
|
+
}
|
|
25
|
+
exports.WHERE_KEYS_ARE_ISSUED = "Profile → Connected apps and keys";
|
|
26
|
+
/**
|
|
27
|
+
* A key's refusals, said in terms of the key.
|
|
28
|
+
*
|
|
29
|
+
* The API answers a revoked key exactly like one that never existed -- 401
|
|
30
|
+
* UNAUTHENTICATED, "Invalid access token." -- on purpose (revocation deletes
|
|
31
|
+
* the row; acceptAccessToken). For a session that means "sign in again"; for
|
|
32
|
+
* a key the only fix is a new key, so a script gets a code it can branch on
|
|
33
|
+
* instead of the generic one. Expiry (TOKEN_EXPIRED) likewise.
|
|
34
|
+
*/
|
|
35
|
+
function translateKeyError(err) {
|
|
36
|
+
if (!(err instanceof errors_1.ApiError))
|
|
37
|
+
return err;
|
|
38
|
+
if (err.status === 401 && err.code === "TOKEN_EXPIRED") {
|
|
39
|
+
return new errors_1.ApiError(401, "KEY_EXPIRED", `This personal access key has expired. Issue a new one in ${exports.WHERE_KEYS_ARE_ISSUED}, then run \`whatcha login --key <key>\`.`);
|
|
40
|
+
}
|
|
41
|
+
if (err.status === 401) {
|
|
42
|
+
return new errors_1.ApiError(401, "KEY_REVOKED", `This personal access key was revoked or is not valid. Issue a new one in ${exports.WHERE_KEYS_ARE_ISSUED}, then run \`whatcha login --key <key>\`.`);
|
|
43
|
+
}
|
|
44
|
+
return err;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=personal-access-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personal-access-token.js","sourceRoot":"","sources":["../../src/lib/personal-access-token.ts"],"names":[],"mappings":";;;AAeA,oEAEC;AAED,0EAEC;AAaD,8CAiBC;AAnDD,sCAAqC;AAErC;;;;;;;GAOG;AACU,QAAA,4BAA4B,GAAG,cAAc,CAAC;AAE3D,qEAAqE;AACrE,MAAM,SAAS,GAAG,yCAAyC,CAAC;AAE5D,SAAgB,4BAA4B,CAAC,GAAW;IACtD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,oCAA4B,CAAC,CAAC;AAC7D,CAAC;AAED,SAAgB,+BAA+B,CAAC,GAAW;IACzD,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;AACpC,CAAC;AAEY,QAAA,qBAAqB,GAAG,mCAAmC,CAAC;AAEzE;;;;;;;;GAQG;AACH,SAAgB,iBAAiB,CAAC,GAAY;IAC5C,IAAI,CAAC,CAAC,GAAG,YAAY,iBAAQ,CAAC;QAAE,OAAO,GAAG,CAAC;IAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACvD,OAAO,IAAI,iBAAQ,CACjB,GAAG,EACH,aAAa,EACb,4DAA4D,6BAAqB,2CAA2C,CAC7H,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,OAAO,IAAI,iBAAQ,CACjB,GAAG,EACH,aAAa,EACb,4EAA4E,6BAAqB,2CAA2C,CAC7I,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findProfile = findProfile;
|
|
4
|
+
exports.bearerClients = bearerClients;
|
|
5
|
+
exports.clientAsProfile = clientAsProfile;
|
|
6
|
+
const http_client_1 = require("../http-client");
|
|
7
|
+
/** A profile of this login by @handle (the `@` optional, case-insensitive) or id. */
|
|
8
|
+
function findProfile(profiles, target) {
|
|
9
|
+
const wanted = target.replace(/^@/, "").toLowerCase();
|
|
10
|
+
return profiles.find((p) => p.id === target || (p.username ?? "").toLowerCase() === wanted);
|
|
11
|
+
}
|
|
12
|
+
function bearerClients(baseUrl) {
|
|
13
|
+
return (accessToken) => new http_client_1.FetchWhatchaClient(baseUrl, async () => `Bearer ${accessToken}`);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A client that IS one of this login's sub-profiles, for the rest of one
|
|
17
|
+
* command -- the session `whatcha switch` would save, used and dropped
|
|
18
|
+
* instead. Some things only a profile can do for itself (set its own
|
|
19
|
+
* avatar, accept an invite into a group), and the saved session must not
|
|
20
|
+
* change under a command that was not asked to switch.
|
|
21
|
+
*
|
|
22
|
+
* The API reaches only sub-profiles of the caller's own family, never the
|
|
23
|
+
* account itself; its refusal is passed on as it came.
|
|
24
|
+
*/
|
|
25
|
+
async function clientAsProfile(client, profileId, forToken) {
|
|
26
|
+
const issued = await client.post(`/me/profiles/${profileId}/session`, {});
|
|
27
|
+
return forToken(issued.accessToken);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=profile-family.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-family.js","sourceRoot":"","sources":["../../src/lib/profile-family.ts"],"names":[],"mappings":";;AA+BA,kCAGC;AAKD,sCAEC;AAYD,0CAOC;AA5DD,gDAAwE;AA8BxE,qFAAqF;AACrF,SAAgB,WAAW,CAAC,QAA6B,EAAE,MAAc;IACvE,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACtD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;AAC9F,CAAC;AAKD,SAAgB,aAAa,CAAC,OAAe;IAC3C,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,gCAAkB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,UAAU,WAAW,EAAE,CAAC,CAAC;AAC/F,CAAC;AAED;;;;;;;;;GASG;AACI,KAAK,UAAU,eAAe,CACnC,MAAqB,EACrB,SAAiB,EACjB,QAAwB;IAExB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAiB,gBAAgB,SAAS,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1F,OAAO,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.publicDiaryUrl = publicDiaryUrl;
|
|
4
|
+
exports.publicDiaryIds = publicDiaryIds;
|
|
5
|
+
/**
|
|
6
|
+
* Whether anybody, signed in or not, can read a diary -- and where (#1701).
|
|
7
|
+
*
|
|
8
|
+
* Publishing belongs to the group, not the diary: `PUT /groups/:id/publication`
|
|
9
|
+
* (owner only; the app's "Publish" card on a group's page) makes a shared
|
|
10
|
+
* group public, and with it every diary in it that the group's members can
|
|
11
|
+
* read. A diary kept to its creator (`visibility: "private"`) stays private
|
|
12
|
+
* inside a published group. That is the API's own test (PUBLIC_SCOPE in
|
|
13
|
+
* public-square-repository.ts), read here from what the app reads: the
|
|
14
|
+
* group's `isPublic` from `GET /groups`, the diary's `visibility` from
|
|
15
|
+
* `GET /diaries/:id`.
|
|
16
|
+
*/
|
|
17
|
+
/** The web page a published diary is read at: the site's origin, which is the API's (`/api/v1` hangs off it). */
|
|
18
|
+
function publicDiaryUrl(apiBaseUrl, diaryId) {
|
|
19
|
+
return `${new URL(apiBaseUrl).origin}/diaries/${diaryId}`;
|
|
20
|
+
}
|
|
21
|
+
async function publishedGroupIds(client) {
|
|
22
|
+
const ids = new Set();
|
|
23
|
+
let cursor;
|
|
24
|
+
for (;;) {
|
|
25
|
+
const page = await client.getPage("/groups", { limit: "50", cursor });
|
|
26
|
+
for (const g of page.items)
|
|
27
|
+
if (g.isPublic)
|
|
28
|
+
ids.add(g.id);
|
|
29
|
+
if (!page.hasNext || !page.nextCursor)
|
|
30
|
+
break;
|
|
31
|
+
cursor = page.nextCursor;
|
|
32
|
+
}
|
|
33
|
+
return ids;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The ids of the diaries among `diaries` that are public. One `GET /groups`
|
|
37
|
+
* walk, plus one `GET /diaries/:id` per diary in a published group only --
|
|
38
|
+
* a shelf with nothing published costs a single extra call.
|
|
39
|
+
*/
|
|
40
|
+
async function publicDiaryIds(client, diaries) {
|
|
41
|
+
const published = new Set();
|
|
42
|
+
const shared = diaries.filter((d) => d.groupType !== "personal");
|
|
43
|
+
if (shared.length === 0)
|
|
44
|
+
return published;
|
|
45
|
+
const groups = await publishedGroupIds(client);
|
|
46
|
+
for (const d of shared) {
|
|
47
|
+
if (!groups.has(d.groupId))
|
|
48
|
+
continue;
|
|
49
|
+
const detail = await client.get(`/diaries/${d.id}`);
|
|
50
|
+
if (detail.visibility === "group")
|
|
51
|
+
published.add(d.id);
|
|
52
|
+
}
|
|
53
|
+
return published;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=publication.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publication.js","sourceRoot":"","sources":["../../src/lib/publication.ts"],"names":[],"mappings":";;AAiBA,wCAEC;AAwBD,wCAWC;AAnDD;;;;;;;;;;;GAWG;AAEH,iHAAiH;AACjH,SAAgB,cAAc,CAAC,UAAkB,EAAE,OAAe;IAChE,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,MAAM,YAAY,OAAO,EAAE,CAAC;AAC5D,CAAC;AAOD,KAAK,UAAU,iBAAiB,CAAC,MAAqB;IACpD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,IAAI,MAA0B,CAAC;IAC/B,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAgB,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACrF,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,CAAC,QAAQ;gBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,MAAM;QAC7C,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,cAAc,CAAC,MAAqB,EAAE,OAAgB;IAC1E,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC;IACjE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;YAAE,SAAS;QACrC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAsC,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzF,IAAI,MAAM,CAAC,UAAU,KAAK,OAAO;YAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whatcha/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Official command-line client for what-are-you-do-ing (whatareyoudo.ing) -- login, list diaries, write and read entries.",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Official command-line client for what-are-you-do-ing (whatareyoudo.ing) -- login (password or personal access key), list diaries, write and read entries, switch profiles, read and write the Experience Wiki, with images.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://whatareyoudo.ing/cli",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://
|
|
9
|
+
"url": "git+https://github.com/what-are-you-do-ing/whatcha.git",
|
|
10
10
|
"directory": "apps/cli"
|
|
11
11
|
},
|
|
12
12
|
"bugs": {
|
|
13
|
-
"url": "https://
|
|
13
|
+
"url": "https://github.com/what-are-you-do-ing/whatcha/issues"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"whatcha",
|