artyfax 0.3.1 → 0.3.2
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/dist/cli.js +166 -15
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -55,7 +55,8 @@ async function apiFetch(config, path, init2) {
|
|
|
55
55
|
if (raw) {
|
|
56
56
|
try {
|
|
57
57
|
const parsed = JSON.parse(raw);
|
|
58
|
-
if (parsed.
|
|
58
|
+
if (parsed.message) message = parsed.message;
|
|
59
|
+
else if (parsed.error) message = parsed.error;
|
|
59
60
|
} catch {
|
|
60
61
|
const snippet = raw.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
61
62
|
if (snippet) message = `HTTP ${res.status}: ${snippet}`;
|
|
@@ -375,6 +376,11 @@ async function read(config, slugOrId, forceSecure) {
|
|
|
375
376
|
} else {
|
|
376
377
|
process.stdout.write(data.md_source ?? data.content);
|
|
377
378
|
}
|
|
379
|
+
if (data.content_hash) {
|
|
380
|
+
process.stderr.write(`
|
|
381
|
+
content_hash: ${data.content_hash}
|
|
382
|
+
`);
|
|
383
|
+
}
|
|
378
384
|
}
|
|
379
385
|
|
|
380
386
|
// src/commands/secure.ts
|
|
@@ -399,7 +405,7 @@ async function secure(config, slugOrId) {
|
|
|
399
405
|
spin.text = "Uploading\u2026";
|
|
400
406
|
await apiFetch(config, `/documents/${doc.id}/content`, {
|
|
401
407
|
method: "PATCH",
|
|
402
|
-
body: JSON.stringify({ content: encrypted })
|
|
408
|
+
body: JSON.stringify({ content: encrypted, force: true })
|
|
403
409
|
});
|
|
404
410
|
await apiFetch(config, `/documents/${doc.id}`, {
|
|
405
411
|
method: "PATCH",
|
|
@@ -427,7 +433,7 @@ async function unsecure(config, slugOrId) {
|
|
|
427
433
|
spin.text = "Uploading\u2026";
|
|
428
434
|
await apiFetch(config, `/documents/${doc.id}/content`, {
|
|
429
435
|
method: "PATCH",
|
|
430
|
-
body: JSON.stringify({ content: plaintext })
|
|
436
|
+
body: JSON.stringify({ content: plaintext, force: true })
|
|
431
437
|
});
|
|
432
438
|
await apiFetch(config, `/documents/${doc.id}`, {
|
|
433
439
|
method: "PATCH",
|
|
@@ -595,7 +601,7 @@ async function open(config, slugOrId) {
|
|
|
595
601
|
// src/commands/update.ts
|
|
596
602
|
import { readFileSync } from "fs";
|
|
597
603
|
import { resolve } from "path";
|
|
598
|
-
async function update(config, slugOrId, file, json) {
|
|
604
|
+
async function update(config, slugOrId, file, json, force = false) {
|
|
599
605
|
const spin = spinner("Resolving document\u2026");
|
|
600
606
|
spin.start();
|
|
601
607
|
const doc = await resolveSlug(config, slugOrId);
|
|
@@ -624,11 +630,35 @@ async function update(config, slugOrId, file, json) {
|
|
|
624
630
|
const sdk = await getSDK(config);
|
|
625
631
|
content = await encryptSecure(content, sdk);
|
|
626
632
|
}
|
|
633
|
+
let ifMatch = null;
|
|
634
|
+
if (!force) {
|
|
635
|
+
spin.text = "Checking current version\u2026";
|
|
636
|
+
const cur = await apiFetch(config, `/documents/${doc.id}/content`);
|
|
637
|
+
ifMatch = cur.content_hash ?? null;
|
|
638
|
+
}
|
|
627
639
|
spin.text = "Uploading\u2026";
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
640
|
+
try {
|
|
641
|
+
await apiFetch(config, `/documents/${doc.id}/content`, {
|
|
642
|
+
method: "PATCH",
|
|
643
|
+
body: JSON.stringify(
|
|
644
|
+
force ? { content, source: "cli", force: true } : { content, source: "cli", if_match: ifMatch ?? void 0 }
|
|
645
|
+
)
|
|
646
|
+
});
|
|
647
|
+
} catch (e) {
|
|
648
|
+
spin.stop();
|
|
649
|
+
const status = e.status;
|
|
650
|
+
if (status === 409) {
|
|
651
|
+
throw new Error(
|
|
652
|
+
"This document changed since you read it. Re-pull it with `artyfax read` and re-apply your edit, or pass --force to overwrite."
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
if (status === 428) {
|
|
656
|
+
throw new Error(
|
|
657
|
+
"Could not read the current version to guard this write. Re-run, or pass --force to overwrite."
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
throw e;
|
|
661
|
+
}
|
|
632
662
|
spin.stop();
|
|
633
663
|
if (json) {
|
|
634
664
|
console.log(JSON.stringify({ updated: true, slug: doc.slug, id: doc.id, secure: doc.secure === 1 }));
|
|
@@ -756,6 +786,106 @@ async function addImage(config, slugOrId, file, opts) {
|
|
|
756
786
|
}
|
|
757
787
|
}
|
|
758
788
|
|
|
789
|
+
// ../shared/src/limits.ts
|
|
790
|
+
var SERVER_SIDE_BYTE_CAP = 10 * 1024 * 1024;
|
|
791
|
+
var CLIENT_UPLOAD_BYTE_CAP_BY_TIER = {
|
|
792
|
+
free: 25 * 1024 * 1024,
|
|
793
|
+
personal: 50 * 1024 * 1024,
|
|
794
|
+
pro: 100 * 1024 * 1024
|
|
795
|
+
};
|
|
796
|
+
var TOTAL_STORAGE_BYTES_BY_TIER = {
|
|
797
|
+
free: 100 * 1024 * 1024,
|
|
798
|
+
personal: 10 * 1024 * 1024 * 1024,
|
|
799
|
+
pro: 50 * 1024 * 1024 * 1024
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
// ../shared/src/unsplash.ts
|
|
803
|
+
function sanitizeMarkdownLabel(text) {
|
|
804
|
+
if (!text) return "";
|
|
805
|
+
return text.replace(/[[\]]/g, "").replace(/\s+/g, " ").trim();
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// src/commands/unsplash.ts
|
|
809
|
+
async function search2(config, query, page) {
|
|
810
|
+
const qs = new URLSearchParams({ q: query, page: String(page) });
|
|
811
|
+
return apiFetch(config, `/unsplash/search?${qs.toString()}`);
|
|
812
|
+
}
|
|
813
|
+
async function unsplashSearch(config, query, opts) {
|
|
814
|
+
const spin = spinner("Searching Unsplash\u2026");
|
|
815
|
+
spin.start();
|
|
816
|
+
const res = await search2(config, query, opts.page ?? 1);
|
|
817
|
+
spin.stop();
|
|
818
|
+
if (opts.json) {
|
|
819
|
+
console.log(JSON.stringify(res));
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
if (res.results.length === 0) {
|
|
823
|
+
console.log(c.muted(`No Unsplash photos found for "${query}".`));
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
res.results.forEach((r, i) => {
|
|
827
|
+
const n = c.bright(String(i + 1).padStart(2));
|
|
828
|
+
const desc = r.description ? c.bright(r.description) : c.muted("(no description)");
|
|
829
|
+
console.log(`${n}. ${desc}`);
|
|
830
|
+
console.log(` ${c.muted(`${r.photographer.name} \xB7 ${r.width}\xD7${r.height} \xB7 ${r.photo_id}`)}`);
|
|
831
|
+
});
|
|
832
|
+
console.log(
|
|
833
|
+
c.muted(
|
|
834
|
+
`
|
|
835
|
+
${res.total} results. Use: artyfax cover <doc> --photo <id> | artyfax image <doc> --photo <id>`
|
|
836
|
+
)
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
async function resolvePhotoId(config, opts) {
|
|
840
|
+
if (opts.photo) return opts.photo;
|
|
841
|
+
if (!opts.unsplash) throw new Error('Provide --photo <id> or --unsplash "<query>"');
|
|
842
|
+
const res = await search2(config, opts.unsplash, 1);
|
|
843
|
+
if (res.results.length === 0) throw new Error(`No Unsplash photos found for "${opts.unsplash}"`);
|
|
844
|
+
return res.results[0].photo_id;
|
|
845
|
+
}
|
|
846
|
+
async function coverFromUnsplash(config, slugOrId, opts) {
|
|
847
|
+
const spin = spinner("Resolving document\u2026");
|
|
848
|
+
spin.start();
|
|
849
|
+
const doc = await resolveSlug(config, slugOrId);
|
|
850
|
+
spin.text = "Finding photo\u2026";
|
|
851
|
+
const photoId = await resolvePhotoId(config, opts);
|
|
852
|
+
spin.text = "Importing cover from Unsplash\u2026";
|
|
853
|
+
const result = await apiFetch(
|
|
854
|
+
config,
|
|
855
|
+
`/documents/${doc.id}/cover/from-unsplash`,
|
|
856
|
+
{ method: "POST", body: JSON.stringify({ photo_id: photoId }) }
|
|
857
|
+
);
|
|
858
|
+
spin.stop();
|
|
859
|
+
if (opts.json) {
|
|
860
|
+
console.log(JSON.stringify({ ok: true, id: doc.id, slug: doc.slug, cover_image: result.path }));
|
|
861
|
+
} else {
|
|
862
|
+
console.log(`${c.teal("Cover set")} on ${c.bright(doc.slug)} ${c.muted(`(Unsplash ${photoId})`)}`);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
async function imageFromUnsplash(config, slugOrId, opts) {
|
|
866
|
+
const spin = spinner("Resolving document\u2026");
|
|
867
|
+
spin.start();
|
|
868
|
+
const doc = await resolveSlug(config, slugOrId);
|
|
869
|
+
spin.text = "Finding photo\u2026";
|
|
870
|
+
const photoId = await resolvePhotoId(config, opts);
|
|
871
|
+
spin.text = "Importing image from Unsplash\u2026";
|
|
872
|
+
const result = await apiFetch(
|
|
873
|
+
config,
|
|
874
|
+
`/documents/${doc.id}/images/from-unsplash`,
|
|
875
|
+
{ method: "POST", body: JSON.stringify({ photo_id: photoId }) }
|
|
876
|
+
);
|
|
877
|
+
spin.stop();
|
|
878
|
+
const markdown = `
|
|
879
|
+
: ${result.credit_markdown}`;
|
|
880
|
+
if (opts.json) {
|
|
881
|
+
console.log(JSON.stringify({ ok: true, id: doc.id, slug: doc.slug, path: result.path, markdown }));
|
|
882
|
+
} else {
|
|
883
|
+
console.log(`${c.teal("Imported")} to ${c.bright(doc.slug)} ${c.muted(`(Unsplash ${photoId})`)}`);
|
|
884
|
+
console.log(c.muted(" Paste this into the document body:"));
|
|
885
|
+
console.log(` ${markdown.replace("\n", "\n ")}`);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
759
889
|
// src/commands/share.ts
|
|
760
890
|
async function shareCreate(config, slugOrId, json) {
|
|
761
891
|
const spin = spinner("Resolving document\u2026");
|
|
@@ -2145,7 +2275,7 @@ async function pagesMove(config, childRef, position, targetRef, json) {
|
|
|
2145
2275
|
}
|
|
2146
2276
|
|
|
2147
2277
|
// src/cli.ts
|
|
2148
|
-
var VERSION = true ? "0.3.
|
|
2278
|
+
var VERSION = true ? "0.3.2" : "0.0.0-dev";
|
|
2149
2279
|
function brandedHelp() {
|
|
2150
2280
|
return `
|
|
2151
2281
|
${c.amber("artyfax")} ${c.muted(`v${VERSION}`)} \u2014 your personal document library
|
|
@@ -2502,6 +2632,7 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
2502
2632
|
"metadata",
|
|
2503
2633
|
"cover",
|
|
2504
2634
|
"image",
|
|
2635
|
+
"unsplash",
|
|
2505
2636
|
"publish",
|
|
2506
2637
|
"unpublish",
|
|
2507
2638
|
"doctor",
|
|
@@ -2696,8 +2827,8 @@ async function main() {
|
|
|
2696
2827
|
}
|
|
2697
2828
|
case "update": {
|
|
2698
2829
|
const slug = positional[0];
|
|
2699
|
-
if (!slug) error("Missing slug argument", "Usage: artyfax update <slug> [file] (reads from stdin if no file)");
|
|
2700
|
-
await update(config, slug, positional[1], json);
|
|
2830
|
+
if (!slug) error("Missing slug argument", "Usage: artyfax update <slug> [file] [--force] (reads from stdin if no file)");
|
|
2831
|
+
await update(config, slug, positional[1], json, !!flags.force);
|
|
2701
2832
|
break;
|
|
2702
2833
|
}
|
|
2703
2834
|
case "metadata": {
|
|
@@ -2718,15 +2849,35 @@ async function main() {
|
|
|
2718
2849
|
case "cover": {
|
|
2719
2850
|
const slug = positional[0];
|
|
2720
2851
|
const file = positional[1];
|
|
2721
|
-
|
|
2722
|
-
|
|
2852
|
+
const unsplash = flags.unsplash;
|
|
2853
|
+
const photo = flags.photo;
|
|
2854
|
+
if (!slug) error("Missing arguments", 'Usage: artyfax cover <doc> <image-file> | cover <doc> --unsplash "<query>" | --photo <id>');
|
|
2855
|
+
if (unsplash || photo) {
|
|
2856
|
+
await coverFromUnsplash(config, slug, { unsplash, photo, json });
|
|
2857
|
+
} else {
|
|
2858
|
+
if (!file) error("Missing image", 'Provide an image file, or --unsplash "<query>", or --photo <id>');
|
|
2859
|
+
await setCover(config, slug, file, { alt: flags.alt, json });
|
|
2860
|
+
}
|
|
2723
2861
|
break;
|
|
2724
2862
|
}
|
|
2725
2863
|
case "image": {
|
|
2726
2864
|
const slug = positional[0];
|
|
2727
2865
|
const file = positional[1];
|
|
2728
|
-
|
|
2729
|
-
|
|
2866
|
+
const unsplash = flags.unsplash;
|
|
2867
|
+
const photo = flags.photo;
|
|
2868
|
+
if (!slug) error("Missing arguments", 'Usage: artyfax image <doc> <image-file> | image <doc> --unsplash "<query>" | --photo <id>');
|
|
2869
|
+
if (unsplash || photo) {
|
|
2870
|
+
await imageFromUnsplash(config, slug, { unsplash, photo, json });
|
|
2871
|
+
} else {
|
|
2872
|
+
if (!file) error("Missing image", 'Provide an image file, or --unsplash "<query>", or --photo <id>');
|
|
2873
|
+
await addImage(config, slug, file, { alt: flags.alt, json });
|
|
2874
|
+
}
|
|
2875
|
+
break;
|
|
2876
|
+
}
|
|
2877
|
+
case "unsplash": {
|
|
2878
|
+
const query = positional.join(" ").trim();
|
|
2879
|
+
if (!query) error("Missing query", "Usage: artyfax unsplash <query>");
|
|
2880
|
+
await unsplashSearch(config, query, { json, page: flags.page ? Number(flags.page) : void 0 });
|
|
2730
2881
|
break;
|
|
2731
2882
|
}
|
|
2732
2883
|
case "share": {
|