notefeed 0.7.0 → 0.9.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 +3 -3
- package/dist/cli.js +50 -26
- package/dist/client.d.ts +48 -11
- package/dist/client.js +89 -15
- package/dist/generated/index.d.ts +2 -2
- package/dist/generated/index.js +1 -1
- package/dist/generated/sdk.gen.d.ts +9 -9
- package/dist/generated/sdk.gen.js +12 -11
- package/dist/generated/types.gen.d.ts +205 -70
- package/dist/index.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,10 +27,10 @@ for await (const note of client.notes()) console.log(note.created_at, note.title
|
|
|
27
27
|
|
|
28
28
|
A feed can have its own password, which gates reading, listing and posting to it. Pass `feedPassword: "..."` to `new Client(...)` (or per call, e.g. `client.post(text, { feedPassword })`), or set `NOTEFEED_FEED_PASSWORD`, which `fromEnv()` and the `notefeed` command both read (there is deliberately no flag, so it stays out of shell history). It is sent as `X-Feed-Password`; `Authorization` stays the instance password. A feed password is 1 to 256 printable ASCII characters with no space at the start or end; an empty one is the same as none.
|
|
29
29
|
|
|
30
|
-
A posted note can be changed or removed: `await client.edit(id,
|
|
30
|
+
A posted note can be changed or removed: `await client.edit(id, content)` replaces its content and returns the note (its id and URLs stay), and `await client.delete(id)` removes it. Both take the same `{ feed, feedPassword }` options as `post`, and a missing note is a `NotFoundError`. `notefeed edit <id> <text | - | --file PATH>` prints the note URL; `notefeed delete <id>` prints nothing. Anyone who can post to a feed can edit and delete its notes, and deleting is permanent.
|
|
31
31
|
|
|
32
32
|
A feed can have a title and a description, and can be deleted: `await client.feedInfo()` returns `{ name, title, description, protected, read_url }`, `await client.updateFeed({ title, description })` replaces both (an empty string clears one) and returns the feed, and `await client.deleteFeed()` removes the feed with all its notes for good. All three take the same `{ feed, feedPassword }` options as `post`, and a feed that doesn't exist is a `NotFoundError`. Anyone who can post to a feed can do this, and deleting is permanent; there are no `notefeed` commands for it.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
A picture is a note of its own: `await client.post(bytes, { type: "image/png", alt: "…" })` takes a `Uint8Array` or a `Blob` (PNG, JPEG, GIF or WebP; a Blob's own type is used if you leave `type` out) and returns what a markdown post returns, with the picture's `file` and `file_url` added. Write `` in a markdown note to show it; the feed is created by its first note, and a picture that is too large is a `NoteTooLargeError`. The server checks the bytes against the declared type. `await client.update(id, { title, alt })` changes a note's title or a picture's alt text, and `await client.edit(id, content)` replaces its content. `await client.updateFeed({ title, description, image: file })` makes a picture the feed's title image (`image: ""` removes it; leaving `image` out keeps it). `notefeed post --file photo.png` posts a file (the type comes from the extension, or `--type`). To post a note with its pictures in one call, `await client.post(text, { attachments: [{ name: "chart.png", content: bytes, type: "image/png" }] })` posts them with the text in one request, and the server swaps `` for each file name (`null` content posts only the pictures) (`notefeed post "…" --attach chart.png` does the same); nothing is posted when one is refused. Pictures are public to anyone with the feed's read link, and metadata such as EXIF is not removed.
|
|
35
35
|
|
|
36
|
-
Full documentation: https://docs.notefeed.me/clients/
|
|
36
|
+
Full documentation: https://docs.notefeed.me/integrations/clients/
|
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
/** `notefeed post`, `edit`, `
|
|
1
|
+
/** `notefeed post`, `edit`, `update`, `delete` and `notes`: post, change, remove and read notes, markdown or any accepted file, from the command line. */
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { basename, extname } from "node:path";
|
|
4
5
|
import { parseArgs } from "node:util";
|
|
5
6
|
import { Client, ConfigError, NotefeedError } from "./client.js";
|
|
6
7
|
class UsageError extends Error {
|
|
7
8
|
}
|
|
8
9
|
const USAGE = [
|
|
9
|
-
"usage: notefeed post <text | - | --file PATH> [--tag TAG]... [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
10
|
-
" notefeed edit <id> <text | - | --file PATH> [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
10
|
+
"usage: notefeed post <text | - | --file PATH> [--type MEDIA_TYPE] [--title TITLE] [--tag TAG]... [--attach PATH]... [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
11
|
+
" notefeed edit <id> <text | - | --file PATH> [--type MEDIA_TYPE] [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
12
|
+
" notefeed update <id> [--title TITLE] [--alt ALT] [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
11
13
|
" notefeed delete <id> [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
12
|
-
" notefeed image <PATH> [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
13
14
|
" notefeed notes [--limit N] [--tag TAG] [--json] [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
14
15
|
].join("\n");
|
|
16
|
+
// The media type of a file by its extension: the types the server accepts.
|
|
17
|
+
const TYPES = { ".md": "text/markdown", ".markdown": "text/markdown", ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp" };
|
|
15
18
|
/** Returns the exit code: 0 ok, 1 server/network error, 2 usage/config error. */
|
|
16
19
|
export async function main(argv, io = process) {
|
|
17
20
|
try {
|
|
@@ -23,7 +26,11 @@ export async function main(argv, io = process) {
|
|
|
23
26
|
feed: { type: "string" },
|
|
24
27
|
password: { type: "string" },
|
|
25
28
|
file: { type: "string" },
|
|
29
|
+
type: { type: "string" },
|
|
30
|
+
title: { type: "string" },
|
|
31
|
+
alt: { type: "string" },
|
|
26
32
|
tag: { type: "string", multiple: true },
|
|
33
|
+
attach: { type: "string", multiple: true },
|
|
27
34
|
limit: { type: "string" },
|
|
28
35
|
json: { type: "boolean" },
|
|
29
36
|
version: { type: "boolean" },
|
|
@@ -41,29 +48,30 @@ export async function main(argv, io = process) {
|
|
|
41
48
|
}
|
|
42
49
|
const [command, text, ...rest] = positionals;
|
|
43
50
|
if (command === "post" && !rest.length) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
const attachments = await Promise.all((values.attach ?? []).map(attachment));
|
|
52
|
+
// Pictures alone need no text; the result is then the first picture, whose URL is also in `attachments`.
|
|
53
|
+
const noText = attachments.length > 0 && text === undefined && !values.file;
|
|
54
|
+
const input = noText ? { content: null, type: undefined, name: undefined } : await read(text, values.file, values.type, io);
|
|
55
|
+
// The server refuses a file name next to attachments: the text has none then.
|
|
56
|
+
const note = await client(values).post(input.content, { type: input.type, title: values.title, tags: values.tag, name: attachments.length ? undefined : input.name, attachments });
|
|
57
|
+
io.stdout.write(`${[...(noText ? [] : [note.url]), ...note.attachments.map((a) => a.url)].join("\n")}\n`);
|
|
47
58
|
return 0;
|
|
48
59
|
}
|
|
49
60
|
if (command === "edit" && text !== undefined && rest.length <= 1) {
|
|
50
|
-
const
|
|
61
|
+
const input = await read(rest[0], values.file, values.type, io);
|
|
62
|
+
const note = await client(values).edit(text, input.content, { type: input.type });
|
|
51
63
|
io.stdout.write(`${note.url}\n`);
|
|
52
64
|
return 0;
|
|
53
65
|
}
|
|
54
|
-
if (command === "
|
|
55
|
-
|
|
66
|
+
if (command === "update" && text !== undefined && !rest.length) {
|
|
67
|
+
if (values.title === undefined && values.alt === undefined)
|
|
68
|
+
throw new UsageError("give --title and/or --alt");
|
|
69
|
+
const note = await client(values).update(text, { title: values.title, alt: values.alt });
|
|
70
|
+
io.stdout.write(`${note.url}\n`);
|
|
56
71
|
return 0;
|
|
57
72
|
}
|
|
58
|
-
if (command === "
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
bytes = await readFile(text);
|
|
62
|
-
}
|
|
63
|
-
catch (e) {
|
|
64
|
-
throw new UsageError(`cannot read ${text}: ${e.code ?? e.message}`);
|
|
65
|
-
}
|
|
66
|
-
io.stdout.write(`${(await client(values).uploadImage(bytes)).markdown}\n`);
|
|
73
|
+
if (command === "delete" && text !== undefined && !rest.length) {
|
|
74
|
+
await client(values).delete(text);
|
|
67
75
|
return 0;
|
|
68
76
|
}
|
|
69
77
|
if (command === "notes" && text === undefined) {
|
|
@@ -75,7 +83,7 @@ export async function main(argv, io = process) {
|
|
|
75
83
|
// To the second, UTC: the same form as the Python CLI, so scripts read either the same way.
|
|
76
84
|
const when = new Date(n.created_at).toISOString().replace(/\.\d+Z$/, "Z");
|
|
77
85
|
// --json: exactly the documented fields, like the Python CLI, even if the server adds more.
|
|
78
|
-
const fields = { id: n.id, title: n.title,
|
|
86
|
+
const fields = { id: n.id, type: n.type, title: n.title, ...(n.content !== undefined && { content: n.content }), file: n.file, file_url: n.file_url, size: n.size, tags: n.tags, created_at: when, url: n.url };
|
|
79
87
|
io.stdout.write(values.json ? `${JSON.stringify(fields)}\n` : `${when} ${n.title || n.id} ${n.url}\n`);
|
|
80
88
|
if (--left === 0)
|
|
81
89
|
break;
|
|
@@ -108,26 +116,42 @@ function client(values) {
|
|
|
108
116
|
feedPassword: process.env.NOTEFEED_FEED_PASSWORD,
|
|
109
117
|
});
|
|
110
118
|
}
|
|
111
|
-
|
|
119
|
+
// What a post or an edit sends: text on the command line or stdin is markdown; a file is sent as it is, as `--type` or its extension says.
|
|
120
|
+
async function read(text, file, type, io) {
|
|
112
121
|
if (file) {
|
|
122
|
+
const media = type ?? TYPES[extname(file).toLowerCase()];
|
|
123
|
+
if (!media)
|
|
124
|
+
throw new UsageError(`cannot tell the type of ${file}: pass --type (${Object.values(TYPES).filter((t, i, a) => a.indexOf(t) === i).join(", ")})`);
|
|
125
|
+
let bytes;
|
|
113
126
|
try {
|
|
114
|
-
|
|
127
|
+
bytes = await readFile(file);
|
|
115
128
|
}
|
|
116
129
|
catch (e) {
|
|
117
|
-
if (e instanceof UsageError)
|
|
118
|
-
throw e;
|
|
119
130
|
throw new UsageError(`cannot read ${file}: ${e.code ?? e.message}`);
|
|
120
131
|
}
|
|
132
|
+
return { content: media === "text/markdown" ? utf8(bytes, file) : bytes, type: media, name: basename(file) };
|
|
121
133
|
}
|
|
122
134
|
if (text === "-") {
|
|
123
135
|
const chunks = [];
|
|
124
136
|
for await (const c of io.stdin)
|
|
125
137
|
chunks.push(typeof c === "string" ? Buffer.from(c) : c);
|
|
126
|
-
return utf8(Buffer.concat(chunks), "stdin");
|
|
138
|
+
return { content: utf8(Buffer.concat(chunks), "stdin"), type: type ?? "text/markdown" };
|
|
127
139
|
}
|
|
128
140
|
if (text === undefined)
|
|
129
141
|
throw new UsageError('give the note text, "-" for stdin, or --file PATH');
|
|
130
|
-
return text;
|
|
142
|
+
return { content: text, type: type ?? "text/markdown" };
|
|
143
|
+
}
|
|
144
|
+
// A picture to post with the note: its name is the file's (the client checks it), its type comes from the extension.
|
|
145
|
+
async function attachment(path) {
|
|
146
|
+
const type = TYPES[extname(path).toLowerCase()];
|
|
147
|
+
if (!type || type === "text/markdown")
|
|
148
|
+
throw new UsageError(`cannot attach ${path}: a picture (${Object.keys(TYPES).filter((e) => TYPES[e] !== "text/markdown").join(", ")}) is expected`);
|
|
149
|
+
try {
|
|
150
|
+
return { name: basename(path), content: await readFile(path), type };
|
|
151
|
+
}
|
|
152
|
+
catch (e) {
|
|
153
|
+
throw new UsageError(`cannot read ${path}: ${e.code ?? e.message}`);
|
|
154
|
+
}
|
|
131
155
|
}
|
|
132
156
|
function utf8(bytes, source) {
|
|
133
157
|
try {
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import type { Created, Error as ApiError, Feed, FeedSettings,
|
|
1
|
+
import type { Created, Error as ApiError, Feed, FeedSettings, Note } from "./generated/types.gen.js";
|
|
2
2
|
/** The stable error codes the API answers with. */
|
|
3
3
|
export type ErrorCode = NonNullable<ApiError["code"]>;
|
|
4
|
-
export type { Created, Feed, FeedSettings,
|
|
4
|
+
export type { Created, Feed, FeedSettings, Note };
|
|
5
|
+
/** A picture posted with a note: `` in the markdown refers to it. `type` as for post(). */
|
|
6
|
+
export type Attachment = {
|
|
7
|
+
name: string;
|
|
8
|
+
content: Uint8Array | Blob;
|
|
9
|
+
type?: string;
|
|
10
|
+
alt?: string;
|
|
11
|
+
};
|
|
5
12
|
/** `timeoutMs` (default 10000) limits each whole request, including reading the answer. */
|
|
6
13
|
export type ClientOptions = {
|
|
7
14
|
url: string;
|
|
@@ -49,19 +56,49 @@ export declare class Client {
|
|
|
49
56
|
constructor(options: ClientOptions);
|
|
50
57
|
/** NOTEFEED_URL, NOTEFEED_FEED, NOTEFEED_PASSWORD and NOTEFEED_FEED_PASSWORD. */
|
|
51
58
|
static fromEnv(env?: Record<string, string | undefined>): Client;
|
|
52
|
-
/**
|
|
53
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Post a note: a string is markdown, bytes or a Blob are a file whose media type is `type` (a Blob's own `type` if left out): `text/markdown`,
|
|
61
|
+
* `image/png`, `image/jpeg`, `image/gif` or `image/webp`. The server accepts nothing else and checks that the body is what the type says.
|
|
62
|
+
* The feed is created by its first note. `Created.file` is the note's file name: write `` in a markdown note to show a picture, or
|
|
63
|
+
* pass it as `image` to updateFeed; `file_url` is for a link outside notefeed.
|
|
64
|
+
*
|
|
65
|
+
* `feedPassword` overrides the client's, for a feed that has its own password. `tags` label the note (at most 10, each 1 to 32 characters of
|
|
66
|
+
* letters, digits, `-`, `_`, `.`, `:`; not verified). `title` is its title (at most 100 characters, one line); left out, it is taken from the
|
|
67
|
+
* text. `alt` is a picture's alternative text, `name` the file's original name. `readId` is the read id the feed gets when this post creates it
|
|
68
|
+
* (3 to 64 characters of `a-z`, `0-9`, `-`, `_`; random when left out; ignored for a feed that exists; a `taken` error when another feed has it).
|
|
69
|
+
*
|
|
70
|
+
* `attachments` are pictures posted with the note in one request: `` in the markdown shows one (a picture the text never refers to is
|
|
71
|
+
* added at the end), and `Created.attachments` lists them in order. `tags` label the pictures too. `content` may be `null` when there are
|
|
72
|
+
* attachments: only the pictures are posted, `title` and `tags` go on each, and the result is the first picture plus `attachments`. Checked
|
|
73
|
+
* here, before anything is sent (ConfigError): a name given twice, an attachment that is not a picture, content that is not markdown, `alt` or
|
|
74
|
+
* `name` with attachments. What a name may be is the server's rule (docs/using/pictures.md, "Posting a note with its pictures"): a name it refuses is an
|
|
75
|
+
* InvalidRequestError, `attachment "<name>": ...`, and whenever the server refuses any part (its message names it) nothing is posted.
|
|
76
|
+
*/
|
|
77
|
+
post(content: string | Uint8Array | Blob | null, options?: {
|
|
54
78
|
feed?: string;
|
|
55
79
|
feedPassword?: string;
|
|
80
|
+
type?: string;
|
|
81
|
+
title?: string;
|
|
56
82
|
tags?: string[];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
83
|
+
alt?: string;
|
|
84
|
+
name?: string;
|
|
85
|
+
readId?: string;
|
|
86
|
+
attachments?: Attachment[];
|
|
87
|
+
}): Promise<Created & {
|
|
88
|
+
attachments: Created[];
|
|
89
|
+
}>;
|
|
90
|
+
private postOne;
|
|
91
|
+
/** Replace a note's content (a string is markdown; bytes and Blobs need the note's own `type`, as for post()). Its id, URLs and metadata stay. Same options as post(). */
|
|
92
|
+
edit(id: string, content: string | Uint8Array | Blob, options?: {
|
|
60
93
|
feed?: string;
|
|
61
94
|
feedPassword?: string;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
95
|
+
type?: string;
|
|
96
|
+
}): Promise<Note>;
|
|
97
|
+
/** Set a note's title and/or alt text (alt for pictures); `""` removes one, so a markdown note's title follows its text again. Same options as post(). */
|
|
98
|
+
update(id: string, meta: {
|
|
99
|
+
title?: string;
|
|
100
|
+
alt?: string;
|
|
101
|
+
}, options?: {
|
|
65
102
|
feed?: string;
|
|
66
103
|
feedPassword?: string;
|
|
67
104
|
}): Promise<Note>;
|
|
@@ -75,7 +112,7 @@ export declare class Client {
|
|
|
75
112
|
feed?: string;
|
|
76
113
|
feedPassword?: string;
|
|
77
114
|
}): Promise<Feed>;
|
|
78
|
-
/** Replace the feed's title and description (both; an empty string clears one), and set (`image`: a file name from uploadImage) or clear (`""`) the title image; leave `image` out to keep it. The feed must already exist. Same options as post(). */
|
|
115
|
+
/** Replace the feed's title and description (both; an empty string clears one), and set (`image`: a file name from uploadImage) or clear (`""`) the title image; leave `image` out to keep it. `read_id` gives the feed another read id (3 to 64 characters of `a-z`, `0-9`, `-`, `_`; `""` for a random one; left out keeps it); the old one is freed, and relative image links in notes follow while full URLs do not. The feed must already exist. Same options as post(). */
|
|
79
116
|
updateFeed(settings: FeedSettings, options?: {
|
|
80
117
|
feed?: string;
|
|
81
118
|
feedPassword?: string;
|
package/dist/client.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* OpenAPI description (./generated, `npm run generate` in the repo). No runtime dependencies.
|
|
4
4
|
*/
|
|
5
5
|
import { createClient, createConfig } from "./generated/client/index.js";
|
|
6
|
-
import { deleteFeed, deleteNote, editNote, getFeed, getNote, getReadNote, listNotes, listReadNotes, postNote, updateFeed
|
|
6
|
+
import { deleteFeed, deleteNote, editNote, getFeed, getNote, getReadNote, listNotes, listReadNotes, patchNote, postNote, updateFeed } from "./generated/sdk.gen.js";
|
|
7
7
|
/** Any failure talking to notefeed. `status` and `code` are null when there was no API answer. */
|
|
8
8
|
export class NotefeedError extends Error {
|
|
9
9
|
status;
|
|
@@ -54,9 +54,23 @@ const BY_CODE = {
|
|
|
54
54
|
invalid_body: InvalidRequestError,
|
|
55
55
|
invalid_request: InvalidRequestError,
|
|
56
56
|
feed_exists: InvalidRequestError,
|
|
57
|
+
taken: InvalidRequestError,
|
|
57
58
|
unsupported_type: InvalidRequestError,
|
|
58
59
|
};
|
|
60
|
+
const IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
59
61
|
const FEED_RE = /^[a-z0-9_-]{1,64}$/; // same rule as the server; reserved names still come back as a 400
|
|
62
|
+
// A header holds bytes, and fetch refuses a character above U+00FF: text that may not be ASCII goes as its UTF-8 bytes written as
|
|
63
|
+
// latin1 characters, which is what the server reads back.
|
|
64
|
+
const headerValue = (text) => Array.from(new TextEncoder().encode(text), (b) => String.fromCharCode(b)).join("");
|
|
65
|
+
// What a post sends: a string is markdown; bytes and Blobs need a media type (a Blob's own, if it has one).
|
|
66
|
+
function asFile(content, type) {
|
|
67
|
+
if (typeof content === "string")
|
|
68
|
+
return { body: new Blob([content]), type: type ?? "text/markdown" };
|
|
69
|
+
const media = type ?? (content instanceof Blob ? content.type : "");
|
|
70
|
+
if (!media)
|
|
71
|
+
throw new ConfigError("give the file's media type as `type`, e.g. image/png");
|
|
72
|
+
return { body: content instanceof Blob ? content : new Blob([content]), type: media };
|
|
73
|
+
}
|
|
60
74
|
export class Client {
|
|
61
75
|
url;
|
|
62
76
|
feed;
|
|
@@ -87,23 +101,83 @@ export class Client {
|
|
|
87
101
|
feedPassword: env.NOTEFEED_FEED_PASSWORD,
|
|
88
102
|
});
|
|
89
103
|
}
|
|
90
|
-
/**
|
|
91
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Post a note: a string is markdown, bytes or a Blob are a file whose media type is `type` (a Blob's own `type` if left out): `text/markdown`,
|
|
106
|
+
* `image/png`, `image/jpeg`, `image/gif` or `image/webp`. The server accepts nothing else and checks that the body is what the type says.
|
|
107
|
+
* The feed is created by its first note. `Created.file` is the note's file name: write `` in a markdown note to show a picture, or
|
|
108
|
+
* pass it as `image` to updateFeed; `file_url` is for a link outside notefeed.
|
|
109
|
+
*
|
|
110
|
+
* `feedPassword` overrides the client's, for a feed that has its own password. `tags` label the note (at most 10, each 1 to 32 characters of
|
|
111
|
+
* letters, digits, `-`, `_`, `.`, `:`; not verified). `title` is its title (at most 100 characters, one line); left out, it is taken from the
|
|
112
|
+
* text. `alt` is a picture's alternative text, `name` the file's original name. `readId` is the read id the feed gets when this post creates it
|
|
113
|
+
* (3 to 64 characters of `a-z`, `0-9`, `-`, `_`; random when left out; ignored for a feed that exists; a `taken` error when another feed has it).
|
|
114
|
+
*
|
|
115
|
+
* `attachments` are pictures posted with the note in one request: `` in the markdown shows one (a picture the text never refers to is
|
|
116
|
+
* added at the end), and `Created.attachments` lists them in order. `tags` label the pictures too. `content` may be `null` when there are
|
|
117
|
+
* attachments: only the pictures are posted, `title` and `tags` go on each, and the result is the first picture plus `attachments`. Checked
|
|
118
|
+
* here, before anything is sent (ConfigError): a name given twice, an attachment that is not a picture, content that is not markdown, `alt` or
|
|
119
|
+
* `name` with attachments. What a name may be is the server's rule (docs/using/pictures.md, "Posting a note with its pictures"): a name it refuses is an
|
|
120
|
+
* InvalidRequestError, `attachment "<name>": ...`, and whenever the server refuses any part (its message names it) nothing is posted.
|
|
121
|
+
*/
|
|
122
|
+
async post(content, options = {}) {
|
|
123
|
+
const { attachments = [], ...own } = options;
|
|
124
|
+
if (content === null && attachments.length === 0)
|
|
125
|
+
throw new ConfigError("no content and no attachments");
|
|
126
|
+
if (attachments.length === 0)
|
|
127
|
+
return { ...(await this.postOne(content, own)), attachments: [] };
|
|
128
|
+
if (content !== null && (typeof content !== "string" || (own.type ?? "text/markdown") !== "text/markdown"))
|
|
129
|
+
throw new ConfigError("attachments go with a markdown string");
|
|
130
|
+
if (own.alt || own.name)
|
|
131
|
+
throw new ConfigError("alt and name do not go with attachments: give each attachment its alt");
|
|
132
|
+
const form = new FormData();
|
|
133
|
+
if (content !== null)
|
|
134
|
+
form.append("text", new Blob([content], { type: "text/markdown" }));
|
|
135
|
+
for (const a of attachments) {
|
|
136
|
+
if (attachments.findIndex((b) => b.name === a.name) !== attachments.indexOf(a))
|
|
137
|
+
throw new ConfigError(`attachment "${a.name}" is given twice`);
|
|
138
|
+
const { body, type } = asFile(a.content, a.type);
|
|
139
|
+
if (!IMAGE_TYPES.includes(type))
|
|
140
|
+
throw new ConfigError(`attachment "${a.name}" must be a picture (${IMAGE_TYPES.join(", ")})`);
|
|
141
|
+
form.append("file", new Blob([body], { type }), a.name);
|
|
142
|
+
if (a.alt)
|
|
143
|
+
form.append(`alt.${a.name}`, a.alt);
|
|
144
|
+
}
|
|
145
|
+
const feed = this.feedFor(own.feed);
|
|
146
|
+
const headers = {
|
|
147
|
+
...this.opts(own.feedPassword).headers,
|
|
148
|
+
"Content-Type": null, // fetch sets multipart/form-data with its boundary
|
|
149
|
+
...(own.title && { "X-Note-Title": headerValue(own.title) }),
|
|
150
|
+
...(own.tags?.length && { "X-Note-Tags": own.tags.join(",") }),
|
|
151
|
+
...(own.readId && { "X-Read-Id": own.readId }),
|
|
152
|
+
};
|
|
153
|
+
const created = await this.call(postNote({ client: this.api, path: { feed }, body: form, headers, signal: AbortSignal.timeout(this.timeoutMs) }));
|
|
154
|
+
return { ...created, attachments: created.attachments ?? [] };
|
|
155
|
+
}
|
|
156
|
+
async postOne(content, options) {
|
|
92
157
|
const feed = this.feedFor(options.feed);
|
|
93
|
-
const
|
|
94
|
-
|
|
158
|
+
const { body, type } = asFile(content, options.type);
|
|
159
|
+
const headers = {
|
|
160
|
+
...this.opts(options.feedPassword).headers,
|
|
161
|
+
"Content-Type": type,
|
|
162
|
+
...(options.title && { "X-Note-Title": headerValue(options.title) }),
|
|
163
|
+
...(options.tags?.length && { "X-Note-Tags": options.tags.join(",") }),
|
|
164
|
+
...(options.alt && { "X-Note-Alt": headerValue(options.alt) }),
|
|
165
|
+
...(options.name && { "X-Note-Name": headerValue(options.name) }),
|
|
166
|
+
...(options.readId && { "X-Read-Id": options.readId }),
|
|
167
|
+
};
|
|
168
|
+
return this.call(postNote({ client: this.api, path: { feed }, body, headers, signal: AbortSignal.timeout(this.timeoutMs) }));
|
|
95
169
|
}
|
|
96
|
-
/**
|
|
97
|
-
async
|
|
170
|
+
/** Replace a note's content (a string is markdown; bytes and Blobs need the note's own `type`, as for post()). Its id, URLs and metadata stay. Same options as post(). */
|
|
171
|
+
async edit(id, content, options = {}) {
|
|
98
172
|
const feed = this.feedFor(options.feed);
|
|
99
|
-
const body
|
|
100
|
-
const headers = { ...this.opts(options.feedPassword).headers, "Content-Type":
|
|
101
|
-
return this.call(
|
|
173
|
+
const { body, type } = asFile(content, options.type);
|
|
174
|
+
const headers = { ...this.opts(options.feedPassword).headers, "Content-Type": type };
|
|
175
|
+
return this.call(editNote({ client: this.api, path: { feed, id }, body, headers, signal: AbortSignal.timeout(this.timeoutMs) }));
|
|
102
176
|
}
|
|
103
|
-
/**
|
|
104
|
-
async
|
|
177
|
+
/** Set a note's title and/or alt text (alt for pictures); `""` removes one, so a markdown note's title follows its text again. Same options as post(). */
|
|
178
|
+
async update(id, meta, options = {}) {
|
|
105
179
|
const feed = this.feedFor(options.feed);
|
|
106
|
-
return this.call(
|
|
180
|
+
return this.call(patchNote({ client: this.api, path: { feed, id }, body: meta, ...this.opts(options.feedPassword) }));
|
|
107
181
|
}
|
|
108
182
|
/** Remove a note for good. The feed stays, even with no notes left. Same options as post(). */
|
|
109
183
|
async delete(id, options = {}) {
|
|
@@ -115,10 +189,10 @@ export class Client {
|
|
|
115
189
|
const feed = this.feedFor(options.feed);
|
|
116
190
|
return this.call(getFeed({ client: this.api, path: { feed }, ...this.opts(options.feedPassword) }));
|
|
117
191
|
}
|
|
118
|
-
/** Replace the feed's title and description (both; an empty string clears one), and set (`image`: a file name from uploadImage) or clear (`""`) the title image; leave `image` out to keep it. The feed must already exist. Same options as post(). */
|
|
192
|
+
/** Replace the feed's title and description (both; an empty string clears one), and set (`image`: a file name from uploadImage) or clear (`""`) the title image; leave `image` out to keep it. `read_id` gives the feed another read id (3 to 64 characters of `a-z`, `0-9`, `-`, `_`; `""` for a random one; left out keeps it); the old one is freed, and relative image links in notes follow while full URLs do not. The feed must already exist. Same options as post(). */
|
|
119
193
|
async updateFeed(settings, options = {}) {
|
|
120
194
|
const feed = this.feedFor(options.feed);
|
|
121
|
-
const body = { title: settings.title, description: settings.description, ...(settings.image === undefined ? {} : { image: settings.image }) };
|
|
195
|
+
const body = { title: settings.title, description: settings.description, ...(settings.image === undefined ? {} : { image: settings.image }), ...(settings.read_id === undefined ? {} : { read_id: settings.read_id }) };
|
|
122
196
|
return this.call(updateFeed({ client: this.api, path: { feed }, body, ...this.opts(options.feedPassword) }));
|
|
123
197
|
}
|
|
124
198
|
/** Delete the feed with all its notes, settings and password, for good; its name is free again. Same options as post(). */
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { changeFeedPassword, deleteFeed, deleteNote, editNote, getFeed, getNote, getOpenApi, getReadFeed, getReadNote, listNotes, listReadNotes, type Options, postNote, removeFeedPassword, updateFeed
|
|
2
|
-
export type { ChangeFeedPasswordData, ChangeFeedPasswordError, ChangeFeedPasswordErrors, ChangeFeedPasswordResponse, ChangeFeedPasswordResponses, ClientOptions, Created, DeleteFeedData, DeleteFeedError, DeleteFeedErrors, DeleteFeedResponse, DeleteFeedResponses, DeleteNoteData, DeleteNoteError, DeleteNoteErrors, DeleteNoteResponse, DeleteNoteResponses, EditNoteData, EditNoteError, EditNoteErrors, EditNoteResponse, EditNoteResponses, Error, Feed, FeedSettings, GetFeedData, GetFeedError, GetFeedErrors, GetFeedResponse, GetFeedResponses, GetNoteData, GetNoteError, GetNoteErrors, GetNoteResponse, GetNoteResponses, GetOpenApiData, GetOpenApiResponse, GetOpenApiResponses, GetReadFeedData, GetReadFeedError, GetReadFeedErrors, GetReadFeedResponse, GetReadFeedResponses, GetReadNoteData, GetReadNoteError, GetReadNoteErrors, GetReadNoteResponse, GetReadNoteResponses,
|
|
1
|
+
export { changeFeedPassword, deleteFeed, deleteNote, editNote, getFeed, getNote, getOpenApi, getReadFeed, getReadNote, listNotes, listReadNotes, type Options, patchNote, postNote, removeFeedPassword, updateFeed } from './sdk.gen.js';
|
|
2
|
+
export type { ChangeFeedPasswordData, ChangeFeedPasswordError, ChangeFeedPasswordErrors, ChangeFeedPasswordResponse, ChangeFeedPasswordResponses, ClientOptions, Created, DeleteFeedData, DeleteFeedError, DeleteFeedErrors, DeleteFeedResponse, DeleteFeedResponses, DeleteNoteData, DeleteNoteError, DeleteNoteErrors, DeleteNoteResponse, DeleteNoteResponses, EditNoteData, EditNoteError, EditNoteErrors, EditNoteResponse, EditNoteResponses, Error, Feed, FeedSettings, GetFeedData, GetFeedError, GetFeedErrors, GetFeedResponse, GetFeedResponses, GetNoteData, GetNoteError, GetNoteErrors, GetNoteResponse, GetNoteResponses, GetOpenApiData, GetOpenApiResponse, GetOpenApiResponses, GetReadFeedData, GetReadFeedError, GetReadFeedErrors, GetReadFeedResponse, GetReadFeedResponses, GetReadNoteData, GetReadNoteError, GetReadNoteErrors, GetReadNoteResponse, GetReadNoteResponses, ListNotesData, ListNotesError, ListNotesErrors, ListNotesResponse, ListNotesResponses, ListReadNotesData, ListReadNotesError, ListReadNotesErrors, ListReadNotesResponse, ListReadNotesResponses, MultipartNote, Note, NoteEdited, NoteList, NoteMeta, PasswordJson, PatchNoteData, PatchNoteError, PatchNoteErrors, PatchNoteResponse, PatchNoteResponses, Posted, PostNoteData, PostNoteError, PostNoteErrors, PostNoteResponse, PostNoteResponses, ReadFeed, RemoveFeedPasswordData, RemoveFeedPasswordError, RemoveFeedPasswordErrors, RemoveFeedPasswordResponse, RemoveFeedPasswordResponses, UpdateFeedData, UpdateFeedError, UpdateFeedErrors, UpdateFeedResponse, UpdateFeedResponses } from './types.gen.js';
|
package/dist/generated/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// This file is auto-generated by @hey-api/openapi-ts
|
|
2
|
-
export { changeFeedPassword, deleteFeed, deleteNote, editNote, getFeed, getNote, getOpenApi, getReadFeed, getReadNote, listNotes, listReadNotes, postNote, removeFeedPassword, updateFeed
|
|
2
|
+
export { changeFeedPassword, deleteFeed, deleteNote, editNote, getFeed, getNote, getOpenApi, getReadFeed, getReadNote, listNotes, listReadNotes, patchNote, postNote, removeFeedPassword, updateFeed } from './sdk.gen.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Client, ClientMeta, Options as Options2, RequestResult, TDataShape } from './client/index.js';
|
|
2
|
-
import type { ChangeFeedPasswordData, ChangeFeedPasswordErrors, ChangeFeedPasswordResponses, DeleteFeedData, DeleteFeedErrors, DeleteFeedResponses, DeleteNoteData, DeleteNoteErrors, DeleteNoteResponses, EditNoteData, EditNoteErrors, EditNoteResponses, GetFeedData, GetFeedErrors, GetFeedResponses, GetNoteData, GetNoteErrors, GetNoteResponses, GetOpenApiData, GetOpenApiResponses, GetReadFeedData, GetReadFeedErrors, GetReadFeedResponses, GetReadNoteData, GetReadNoteErrors, GetReadNoteResponses, ListNotesData, ListNotesErrors, ListNotesResponses, ListReadNotesData, ListReadNotesErrors, ListReadNotesResponses, PostNoteData, PostNoteErrors, PostNoteResponses, RemoveFeedPasswordData, RemoveFeedPasswordErrors, RemoveFeedPasswordResponses, UpdateFeedData, UpdateFeedErrors, UpdateFeedResponses
|
|
2
|
+
import type { ChangeFeedPasswordData, ChangeFeedPasswordErrors, ChangeFeedPasswordResponses, DeleteFeedData, DeleteFeedErrors, DeleteFeedResponses, DeleteNoteData, DeleteNoteErrors, DeleteNoteResponses, EditNoteData, EditNoteErrors, EditNoteResponses, GetFeedData, GetFeedErrors, GetFeedResponses, GetNoteData, GetNoteErrors, GetNoteResponses, GetOpenApiData, GetOpenApiResponses, GetReadFeedData, GetReadFeedErrors, GetReadFeedResponses, GetReadNoteData, GetReadNoteErrors, GetReadNoteResponses, ListNotesData, ListNotesErrors, ListNotesResponses, ListReadNotesData, ListReadNotesErrors, ListReadNotesResponses, PatchNoteData, PatchNoteErrors, PatchNoteResponses, PostNoteData, PostNoteErrors, PostNoteResponses, RemoveFeedPasswordData, RemoveFeedPasswordErrors, RemoveFeedPasswordResponses, UpdateFeedData, UpdateFeedErrors, UpdateFeedResponses } from './types.gen.js';
|
|
3
3
|
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean, TResponse = unknown> = Options2<TData, ThrowOnError, TResponse> & {
|
|
4
4
|
/**
|
|
5
5
|
* You can provide a client instance returned by `createClient()` instead of
|
|
@@ -22,7 +22,7 @@ export declare const listNotes: <ThrowOnError extends boolean = false>(options:
|
|
|
22
22
|
/**
|
|
23
23
|
* Post a note
|
|
24
24
|
*
|
|
25
|
-
* Creates the feed with its first note, optionally protected by its own password (`X-Feed-Password
|
|
25
|
+
* The body is the note, a file, and `Content-Type` says which kind: `text/markdown` (UTF-8, at most 102400 bytes), or an image, `image/png`, `image/jpeg`, `image/gif` or `image/webp` (at most NOTEFEED_MAX_IMAGE_BYTES, default 5 MiB). Nothing is guessed: any other type, or none, is `415`, and so is a body that is not what the type says (an image is recognized by its first bytes; SVG is refused). It is stored byte for byte, with no resizing and no metadata stripped (EXIF such as GPS position stays in an image). Creates the feed with its first note, optionally protected by its own password (`X-Feed-Password`; 1 to 256 printable ASCII characters, with no space at the start or end) and with the read id in `X-Read-Id`; both are only used by the post that creates the feed. Posting to a protected feed needs its password. Also served at `POST /{feed}`, the short form curl one-liners use. The response names the note's `file` and where it is served, `file_url`, under the feed's read id (public like the read link). Metadata goes in headers: `X-Note-Title`, `X-Note-Tags` (at most 10 tags, each 1 to 32 characters of letters, digits, `-`, `_`, `.` and `:`; case is folded to lowercase, duplicates are removed), `X-Note-Alt` (images), `X-Note-Name` (the original file name). A markdown note with its pictures is one `multipart/form-data` request: a `text` part (the markdown, a field or a file part; a file part keeps its line breaks, a field's are sent as CRLF), `file` parts (up to 10 pictures, each with its file name and image `Content-Type`) and `alt.<file name>` fields. The pictures are stored first, each as its own note named by its file name, then the text with its references to them (``, `[x]: chart.png`, as written or percent-decoded) swapped for the stored files; a picture it never refers to is appended as ``. Everything is checked before the first write, and a failed write removes what the request stored: all or nothing. `X-Note-Alt` and `X-Note-Name` are 400. The answer has `attachments`, the stored pictures in the order of the `file` parts. The `text` part is optional: `X-Note-Title` goes on the text note and `X-Note-Tags` on every note; with no `text` part only the pictures are stored, each with the title, and the answer's top level is the first picture. A refused multipart post that would have created a protected feed creates nothing.
|
|
26
26
|
*/
|
|
27
27
|
export declare const postNote: <ThrowOnError extends boolean = false>(options: Options<PostNoteData, ThrowOnError>) => RequestResult<PostNoteResponses, PostNoteErrors, ThrowOnError>;
|
|
28
28
|
/**
|
|
@@ -36,17 +36,17 @@ export declare const deleteNote: <ThrowOnError extends boolean = false>(options:
|
|
|
36
36
|
*/
|
|
37
37
|
export declare const getNote: <ThrowOnError extends boolean = false>(options: Options<GetNoteData, ThrowOnError>) => RequestResult<GetNoteResponses, GetNoteErrors, ThrowOnError>;
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* Change a note's title or alt text
|
|
40
40
|
*
|
|
41
|
-
*
|
|
41
|
+
* Sets the note's title and/or alt text (alt only for images). An empty string removes one: a markdown note's title follows its text again. At least one is needed. Needs the feed's password if it has one, and counts against the post rate limit. Read links can't change notes.
|
|
42
42
|
*/
|
|
43
|
-
export declare const
|
|
43
|
+
export declare const patchNote: <ThrowOnError extends boolean = false>(options: Options<PatchNoteData, ThrowOnError>) => RequestResult<PatchNoteResponses, PatchNoteErrors, ThrowOnError>;
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* Replace a note's content
|
|
46
46
|
*
|
|
47
|
-
* The body is the
|
|
47
|
+
* The body is the new file, with the same rules as posting: a `Content-Type` that is one of the accepted types, a body that is what it declares. A note keeps its type, so the type must be the note's own (`415` otherwise). Id, creation time and metadata stay: the title of a markdown note without one set follows the new text. Change the title or alt text with `PATCH`. Needs the feed's password if it has one, and counts against the post rate limit. Read links can't edit. A markdown note with its pictures is one `multipart/form-data` request: a `text` part (the markdown, a field or a file part; a file part keeps its line breaks, a field's are sent as CRLF), `file` parts (up to 10 pictures, each with its file name and image `Content-Type`) and `alt.<file name>` fields. The pictures are stored first, each as its own note named by its file name, then the text with its references to them (``, `[x]: chart.png`, as written or percent-decoded) swapped for the stored files; a picture it never refers to is appended as ``. Everything is checked before the first write, and a failed write removes what the request stored: all or nothing. `X-Note-Alt` and `X-Note-Name` are 400. The answer has `attachments`, the stored pictures in the order of the `file` parts. On a `PUT` the `text` part is required and the note must be a markdown note; the note keeps its own title and tags, `X-Note-Tags` goes on the new pictures. A raw body ignores `X-Note-Tags`. The note is looked up first: a missing note is `404` and an image note `415` whatever the body holds.
|
|
48
48
|
*/
|
|
49
|
-
export declare const
|
|
49
|
+
export declare const editNote: <ThrowOnError extends boolean = false>(options: Options<EditNoteData, ThrowOnError>) => RequestResult<EditNoteResponses, EditNoteErrors, ThrowOnError>;
|
|
50
50
|
/**
|
|
51
51
|
* Delete a feed
|
|
52
52
|
*
|
|
@@ -62,7 +62,7 @@ export declare const getFeed: <ThrowOnError extends boolean = false>(options: Op
|
|
|
62
62
|
/**
|
|
63
63
|
* Change a feed's settings
|
|
64
64
|
*
|
|
65
|
-
* Replaces both the title (at most 100 characters) and the description (at most 500); surrounding whitespace is trimmed and control characters are refused. `show_sender` (default true) shows who posted each note to readers; omitted leaves it as it is. `image` is the file name `uploadImage` returned for this feed (title image), empty to remove it, or omitted to leave it as it is; any other value is a 400. Needs the feed's password if it has one, and counts against the post rate limit. Only on a feed that exists: it is created by its first note. Read links can't change settings.
|
|
65
|
+
* Replaces both the title (at most 100 characters) and the description (at most 500); surrounding whitespace is trimmed and control and text-direction override characters are refused. `show_sender` (default true) shows who posted each note to readers; omitted leaves it as it is. `read_id` gives the feed another read link (3 to 64 characters: a-z, 0-9, - and _; empty for a random one): the old id is freed and answers like an unknown read id until another feed takes it, and the notes are not edited (a relative image link follows the new id, a full URL does not). A short readable read id is guessable, so protect the feed with a password if that matters. An instance can turn chosen read ids off (NOTEFEED_ALLOW_CUSTOM_IDS=0): then only an empty `read_id` is accepted. `image` is the file name `uploadImage` returned for this feed (title image), empty to remove it, or omitted to leave it as it is; any other value is a 400. Needs the feed's password if it has one, and counts against the post rate limit. Only on a feed that exists: it is created by its first note. Read links can't change settings.
|
|
66
66
|
*/
|
|
67
67
|
export declare const updateFeed: <ThrowOnError extends boolean = false>(options: Options<UpdateFeedData, ThrowOnError>) => RequestResult<UpdateFeedResponses, UpdateFeedErrors, ThrowOnError>;
|
|
68
68
|
/**
|
|
@@ -13,14 +13,15 @@ export const listNotes = (options) => (options.client ?? client).get({
|
|
|
13
13
|
/**
|
|
14
14
|
* Post a note
|
|
15
15
|
*
|
|
16
|
-
* Creates the feed with its first note, optionally protected by its own password (`X-Feed-Password
|
|
16
|
+
* The body is the note, a file, and `Content-Type` says which kind: `text/markdown` (UTF-8, at most 102400 bytes), or an image, `image/png`, `image/jpeg`, `image/gif` or `image/webp` (at most NOTEFEED_MAX_IMAGE_BYTES, default 5 MiB). Nothing is guessed: any other type, or none, is `415`, and so is a body that is not what the type says (an image is recognized by its first bytes; SVG is refused). It is stored byte for byte, with no resizing and no metadata stripped (EXIF such as GPS position stays in an image). Creates the feed with its first note, optionally protected by its own password (`X-Feed-Password`; 1 to 256 printable ASCII characters, with no space at the start or end) and with the read id in `X-Read-Id`; both are only used by the post that creates the feed. Posting to a protected feed needs its password. Also served at `POST /{feed}`, the short form curl one-liners use. The response names the note's `file` and where it is served, `file_url`, under the feed's read id (public like the read link). Metadata goes in headers: `X-Note-Title`, `X-Note-Tags` (at most 10 tags, each 1 to 32 characters of letters, digits, `-`, `_`, `.` and `:`; case is folded to lowercase, duplicates are removed), `X-Note-Alt` (images), `X-Note-Name` (the original file name). A markdown note with its pictures is one `multipart/form-data` request: a `text` part (the markdown, a field or a file part; a file part keeps its line breaks, a field's are sent as CRLF), `file` parts (up to 10 pictures, each with its file name and image `Content-Type`) and `alt.<file name>` fields. The pictures are stored first, each as its own note named by its file name, then the text with its references to them (``, `[x]: chart.png`, as written or percent-decoded) swapped for the stored files; a picture it never refers to is appended as ``. Everything is checked before the first write, and a failed write removes what the request stored: all or nothing. `X-Note-Alt` and `X-Note-Name` are 400. The answer has `attachments`, the stored pictures in the order of the `file` parts. The `text` part is optional: `X-Note-Title` goes on the text note and `X-Note-Tags` on every note; with no `text` part only the pictures are stored, each with the title, and the answer's top level is the first picture. A refused multipart post that would have created a protected feed creates nothing.
|
|
17
17
|
*/
|
|
18
18
|
export const postNote = (options) => (options.client ?? client).post({
|
|
19
|
+
bodySerializer: null,
|
|
19
20
|
security: [{ scheme: 'bearer', type: 'http' }],
|
|
20
21
|
url: '/api/v1/feeds/{feed}/notes',
|
|
21
22
|
...options,
|
|
22
23
|
headers: {
|
|
23
|
-
'Content-Type': '
|
|
24
|
+
'Content-Type': 'text/markdown',
|
|
24
25
|
...options.headers
|
|
25
26
|
}
|
|
26
27
|
});
|
|
@@ -43,11 +44,11 @@ export const getNote = (options) => (options.client ?? client).get({
|
|
|
43
44
|
...options
|
|
44
45
|
});
|
|
45
46
|
/**
|
|
46
|
-
*
|
|
47
|
+
* Change a note's title or alt text
|
|
47
48
|
*
|
|
48
|
-
*
|
|
49
|
+
* Sets the note's title and/or alt text (alt only for images). An empty string removes one: a markdown note's title follows its text again. At least one is needed. Needs the feed's password if it has one, and counts against the post rate limit. Read links can't change notes.
|
|
49
50
|
*/
|
|
50
|
-
export const
|
|
51
|
+
export const patchNote = (options) => (options.client ?? client).patch({
|
|
51
52
|
security: [{ scheme: 'bearer', type: 'http' }],
|
|
52
53
|
url: '/api/v1/feeds/{feed}/notes/{id}',
|
|
53
54
|
...options,
|
|
@@ -57,17 +58,17 @@ export const editNote = (options) => (options.client ?? client).put({
|
|
|
57
58
|
}
|
|
58
59
|
});
|
|
59
60
|
/**
|
|
60
|
-
*
|
|
61
|
+
* Replace a note's content
|
|
61
62
|
*
|
|
62
|
-
* The body is the
|
|
63
|
+
* The body is the new file, with the same rules as posting: a `Content-Type` that is one of the accepted types, a body that is what it declares. A note keeps its type, so the type must be the note's own (`415` otherwise). Id, creation time and metadata stay: the title of a markdown note without one set follows the new text. Change the title or alt text with `PATCH`. Needs the feed's password if it has one, and counts against the post rate limit. Read links can't edit. A markdown note with its pictures is one `multipart/form-data` request: a `text` part (the markdown, a field or a file part; a file part keeps its line breaks, a field's are sent as CRLF), `file` parts (up to 10 pictures, each with its file name and image `Content-Type`) and `alt.<file name>` fields. The pictures are stored first, each as its own note named by its file name, then the text with its references to them (``, `[x]: chart.png`, as written or percent-decoded) swapped for the stored files; a picture it never refers to is appended as ``. Everything is checked before the first write, and a failed write removes what the request stored: all or nothing. `X-Note-Alt` and `X-Note-Name` are 400. The answer has `attachments`, the stored pictures in the order of the `file` parts. On a `PUT` the `text` part is required and the note must be a markdown note; the note keeps its own title and tags, `X-Note-Tags` goes on the new pictures. A raw body ignores `X-Note-Tags`. The note is looked up first: a missing note is `404` and an image note `415` whatever the body holds.
|
|
63
64
|
*/
|
|
64
|
-
export const
|
|
65
|
+
export const editNote = (options) => (options.client ?? client).put({
|
|
65
66
|
bodySerializer: null,
|
|
66
67
|
security: [{ scheme: 'bearer', type: 'http' }],
|
|
67
|
-
url: '/api/v1/feeds/{feed}/
|
|
68
|
+
url: '/api/v1/feeds/{feed}/notes/{id}',
|
|
68
69
|
...options,
|
|
69
70
|
headers: {
|
|
70
|
-
'Content-Type': '
|
|
71
|
+
'Content-Type': 'text/markdown',
|
|
71
72
|
...options.headers
|
|
72
73
|
}
|
|
73
74
|
});
|
|
@@ -94,7 +95,7 @@ export const getFeed = (options) => (options.client ?? client).get({
|
|
|
94
95
|
/**
|
|
95
96
|
* Change a feed's settings
|
|
96
97
|
*
|
|
97
|
-
* Replaces both the title (at most 100 characters) and the description (at most 500); surrounding whitespace is trimmed and control characters are refused. `show_sender` (default true) shows who posted each note to readers; omitted leaves it as it is. `image` is the file name `uploadImage` returned for this feed (title image), empty to remove it, or omitted to leave it as it is; any other value is a 400. Needs the feed's password if it has one, and counts against the post rate limit. Only on a feed that exists: it is created by its first note. Read links can't change settings.
|
|
98
|
+
* Replaces both the title (at most 100 characters) and the description (at most 500); surrounding whitespace is trimmed and control and text-direction override characters are refused. `show_sender` (default true) shows who posted each note to readers; omitted leaves it as it is. `read_id` gives the feed another read link (3 to 64 characters: a-z, 0-9, - and _; empty for a random one): the old id is freed and answers like an unknown read id until another feed takes it, and the notes are not edited (a relative image link follows the new id, a full URL does not). A short readable read id is guessable, so protect the feed with a password if that matters. An instance can turn chosen read ids off (NOTEFEED_ALLOW_CUSTOM_IDS=0): then only an empty `read_id` is accepted. `image` is the file name `uploadImage` returned for this feed (title image), empty to remove it, or omitted to leave it as it is; any other value is a 400. Needs the feed's password if it has one, and counts against the post rate limit. Only on a feed that exists: it is created by its first note. Read links can't change settings.
|
|
98
99
|
*/
|
|
99
100
|
export const updateFeed = (options) => (options.client ?? client).put({
|
|
100
101
|
security: [{ scheme: 'bearer', type: 'http' }],
|
|
@@ -3,17 +3,41 @@ export type ClientOptions = {
|
|
|
3
3
|
};
|
|
4
4
|
export type Note = {
|
|
5
5
|
/**
|
|
6
|
-
* UTC time to the second plus a
|
|
6
|
+
* The note's id: a UTC time to the second plus a random UUID for notes made here; any name without a dot for a file placed by hand
|
|
7
7
|
*/
|
|
8
8
|
id: string;
|
|
9
9
|
/**
|
|
10
|
-
* The
|
|
10
|
+
* The media type of the note's file: `text/markdown`, `image/png`, `image/jpeg`, `image/gif` or `image/webp`
|
|
11
|
+
*/
|
|
12
|
+
type: string;
|
|
13
|
+
/**
|
|
14
|
+
* The note's file name, `<id>.<extension>`
|
|
15
|
+
*/
|
|
16
|
+
file: string;
|
|
17
|
+
/**
|
|
18
|
+
* Where the note's file is served, absolute, under the feed's read id (public like the read link); null while the feed has no read link
|
|
19
|
+
*/
|
|
20
|
+
file_url: string | null;
|
|
21
|
+
/**
|
|
22
|
+
* The size of the note's file in bytes
|
|
23
|
+
*/
|
|
24
|
+
size: number;
|
|
25
|
+
/**
|
|
26
|
+
* The title set for the note, else the first heading or the first non-empty line of a markdown note, else the picture's alt text; may be empty
|
|
11
27
|
*/
|
|
12
28
|
title: string;
|
|
13
29
|
/**
|
|
14
|
-
* The note, byte-for-byte as posted
|
|
30
|
+
* The note's text, byte-for-byte as posted: for a text type (markdown) only; absent for a picture
|
|
31
|
+
*/
|
|
32
|
+
content?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Alternative text of an image note, when it has one
|
|
15
35
|
*/
|
|
16
|
-
|
|
36
|
+
alt?: string;
|
|
37
|
+
/**
|
|
38
|
+
* The file name an image was posted with, when it came with one
|
|
39
|
+
*/
|
|
40
|
+
name?: string;
|
|
17
41
|
/**
|
|
18
42
|
* When the note was posted (UTC)
|
|
19
43
|
*/
|
|
@@ -55,52 +79,132 @@ export type Created = {
|
|
|
55
79
|
* The feed's read-only RSS link, safe to share; null only if the server can't read the feed's stored read id, or if that id and the derived one both belong to other feeds
|
|
56
80
|
*/
|
|
57
81
|
read_url: string | null;
|
|
82
|
+
/**
|
|
83
|
+
* The note's file name, `<id>.<extension>`. For an image note, pass it as a feed's `image` setting
|
|
84
|
+
*/
|
|
85
|
+
file: string;
|
|
86
|
+
/**
|
|
87
|
+
* Where the note's file is served, absolute, under the feed's read id (public like the read link); null while the feed has no read link
|
|
88
|
+
*/
|
|
89
|
+
file_url: string | null;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* The note the post is about: its text note, or with no `text` part its first picture
|
|
93
|
+
*/
|
|
94
|
+
export type Posted = {
|
|
95
|
+
id: string;
|
|
96
|
+
/**
|
|
97
|
+
* The note's page in the web UI
|
|
98
|
+
*/
|
|
99
|
+
url: string;
|
|
100
|
+
/**
|
|
101
|
+
* The feed's page in the web UI
|
|
102
|
+
*/
|
|
103
|
+
feed_url: string;
|
|
104
|
+
/**
|
|
105
|
+
* The feed's read-only RSS link, safe to share; null only if the server can't read the feed's stored read id, or if that id and the derived one both belong to other feeds
|
|
106
|
+
*/
|
|
107
|
+
read_url: string | null;
|
|
108
|
+
/**
|
|
109
|
+
* The note's file name, `<id>.<extension>`. For an image note, pass it as a feed's `image` setting
|
|
110
|
+
*/
|
|
111
|
+
file: string;
|
|
112
|
+
/**
|
|
113
|
+
* Where the note's file is served, absolute, under the feed's read id (public like the read link); null while the feed has no read link
|
|
114
|
+
*/
|
|
115
|
+
file_url: string | null;
|
|
116
|
+
/**
|
|
117
|
+
* A multipart request's pictures, one per `file` part, in their order; absent for a raw body
|
|
118
|
+
*/
|
|
119
|
+
attachments?: Array<Created>;
|
|
58
120
|
};
|
|
59
|
-
export type
|
|
121
|
+
export type NoteEdited = {
|
|
122
|
+
/**
|
|
123
|
+
* The note's id: a UTC time to the second plus a random UUID for notes made here; any name without a dot for a file placed by hand
|
|
124
|
+
*/
|
|
125
|
+
id: string;
|
|
60
126
|
/**
|
|
61
|
-
* The
|
|
127
|
+
* The media type of the note's file: `text/markdown`, `image/png`, `image/jpeg`, `image/gif` or `image/webp`
|
|
128
|
+
*/
|
|
129
|
+
type: string;
|
|
130
|
+
/**
|
|
131
|
+
* The note's file name, `<id>.<extension>`
|
|
62
132
|
*/
|
|
63
133
|
file: string;
|
|
64
134
|
/**
|
|
65
|
-
* Where the
|
|
135
|
+
* Where the note's file is served, absolute, under the feed's read id (public like the read link); null while the feed has no read link
|
|
136
|
+
*/
|
|
137
|
+
file_url: string | null;
|
|
138
|
+
/**
|
|
139
|
+
* The size of the note's file in bytes
|
|
140
|
+
*/
|
|
141
|
+
size: number;
|
|
142
|
+
/**
|
|
143
|
+
* The title set for the note, else the first heading or the first non-empty line of a markdown note, else the picture's alt text; may be empty
|
|
144
|
+
*/
|
|
145
|
+
title: string;
|
|
146
|
+
/**
|
|
147
|
+
* The note's text, byte-for-byte as posted: for a text type (markdown) only; absent for a picture
|
|
148
|
+
*/
|
|
149
|
+
content?: string;
|
|
150
|
+
/**
|
|
151
|
+
* Alternative text of an image note, when it has one
|
|
152
|
+
*/
|
|
153
|
+
alt?: string;
|
|
154
|
+
/**
|
|
155
|
+
* The file name an image was posted with, when it came with one
|
|
156
|
+
*/
|
|
157
|
+
name?: string;
|
|
158
|
+
/**
|
|
159
|
+
* When the note was posted (UTC)
|
|
160
|
+
*/
|
|
161
|
+
created_at: string;
|
|
162
|
+
/**
|
|
163
|
+
* The note's page in the web UI
|
|
66
164
|
*/
|
|
67
165
|
url: string;
|
|
68
166
|
/**
|
|
69
|
-
*
|
|
167
|
+
* Verified sign-in name of the poster; absent when the note was posted without a sign-in
|
|
168
|
+
*/
|
|
169
|
+
sender?: string | null;
|
|
170
|
+
/**
|
|
171
|
+
* Labels the poster gave the note (not verified, and shown to readers like the note itself); empty when none
|
|
70
172
|
*/
|
|
71
|
-
|
|
173
|
+
tags: Array<string>;
|
|
174
|
+
/**
|
|
175
|
+
* A multipart request's pictures, one per `file` part, in their order; absent for a raw body
|
|
176
|
+
*/
|
|
177
|
+
attachments?: Array<Created>;
|
|
72
178
|
};
|
|
73
|
-
export type
|
|
179
|
+
export type MultipartNote = {
|
|
74
180
|
/**
|
|
75
|
-
*
|
|
181
|
+
* The markdown note (UTF-8): a field, or a file part (its name is ignored; its type, if any, `text/markdown` or `application/octet-stream`) to keep its line breaks as they are, since a form field's are sent as CRLF. At most one; required on a PUT. Its references to the files (``, `[x]: chart.png`) are swapped for the stored files; a file it never refers to is appended as ``
|
|
76
182
|
*/
|
|
77
|
-
|
|
183
|
+
text?: string;
|
|
78
184
|
/**
|
|
79
|
-
*
|
|
185
|
+
* A picture: up to 10 parts, each with a file name (one path segment, 1 to 200 characters, unique in the request) and its image `Content-Type`. An `alt.<file name>` field gives one its alternative text
|
|
80
186
|
*/
|
|
81
|
-
|
|
187
|
+
file?: Array<Blob | File>;
|
|
82
188
|
};
|
|
83
|
-
export type
|
|
84
|
-
markdown: string;
|
|
189
|
+
export type Error = {
|
|
85
190
|
/**
|
|
86
|
-
*
|
|
191
|
+
* A short reason, for people
|
|
87
192
|
*/
|
|
88
|
-
|
|
193
|
+
error: string;
|
|
89
194
|
/**
|
|
90
|
-
*
|
|
195
|
+
* Stable machine-readable code; absent only on a 500
|
|
91
196
|
*/
|
|
92
|
-
|
|
197
|
+
code?: 'invalid_feed' | 'reserved_feed' | 'auth' | 'rate_limited' | 'too_many_attempts' | 'feed_limit' | 'note_limit' | 'image_limit' | 'empty_note' | 'too_large' | 'unsupported_type' | 'invalid_body' | 'invalid_request' | 'not_found' | 'feed_exists' | 'taken';
|
|
93
198
|
};
|
|
94
|
-
export type
|
|
95
|
-
markdown: string;
|
|
199
|
+
export type NoteMeta = {
|
|
96
200
|
/**
|
|
97
|
-
*
|
|
201
|
+
* The note's title, at most 100 characters, one line (control and text-direction override characters are refused); empty or left out means the title is taken from the text (a markdown note) or there is none (an image)
|
|
98
202
|
*/
|
|
99
|
-
|
|
203
|
+
title?: string;
|
|
100
204
|
/**
|
|
101
|
-
*
|
|
205
|
+
* Alternative text of an image note, at most 500 characters, one line (control and text-direction override characters are refused)
|
|
102
206
|
*/
|
|
103
|
-
|
|
207
|
+
alt?: string;
|
|
104
208
|
};
|
|
105
209
|
export type PasswordJson = {
|
|
106
210
|
/**
|
|
@@ -110,11 +214,11 @@ export type PasswordJson = {
|
|
|
110
214
|
};
|
|
111
215
|
export type FeedSettings = {
|
|
112
216
|
/**
|
|
113
|
-
* Display title, at most 100 characters, one line; empty means none (the feed's name is shown)
|
|
217
|
+
* Display title, at most 100 characters, one line (control and text-direction override characters are refused); empty means none (the feed's name is shown)
|
|
114
218
|
*/
|
|
115
219
|
title: string;
|
|
116
220
|
/**
|
|
117
|
-
* Description, at most 500 characters, one line; may be empty
|
|
221
|
+
* Description, at most 500 characters, one line (control and text-direction override characters are refused); may be empty
|
|
118
222
|
*/
|
|
119
223
|
description: string;
|
|
120
224
|
/**
|
|
@@ -125,6 +229,10 @@ export type FeedSettings = {
|
|
|
125
229
|
* Whether readers (RSS, the read API and pages) see who posted each note; omitted leaves it as it is, a new feed starts with true
|
|
126
230
|
*/
|
|
127
231
|
show_sender?: boolean;
|
|
232
|
+
/**
|
|
233
|
+
* A new read id (3 to 64 characters: a-z, 0-9, - and _), empty for a random one; omitted leaves it as it is. The old read link stops showing this feed, and may later show another one. Notes are not edited: a relative image link (``) follows the new id, a full URL keeps the old one and is yours to change. Reserved feeds keep theirs. 409 when it is taken
|
|
234
|
+
*/
|
|
235
|
+
read_id?: string;
|
|
128
236
|
};
|
|
129
237
|
export type Feed = {
|
|
130
238
|
/**
|
|
@@ -132,11 +240,11 @@ export type Feed = {
|
|
|
132
240
|
*/
|
|
133
241
|
name: string;
|
|
134
242
|
/**
|
|
135
|
-
* Display title, at most 100 characters, one line; empty means none (the feed's name is shown)
|
|
243
|
+
* Display title, at most 100 characters, one line (control and text-direction override characters are refused); empty means none (the feed's name is shown)
|
|
136
244
|
*/
|
|
137
245
|
title: string;
|
|
138
246
|
/**
|
|
139
|
-
* Description, at most 500 characters, one line; may be empty
|
|
247
|
+
* Description, at most 500 characters, one line (control and text-direction override characters are refused); may be empty
|
|
140
248
|
*/
|
|
141
249
|
description: string;
|
|
142
250
|
/**
|
|
@@ -158,11 +266,11 @@ export type Feed = {
|
|
|
158
266
|
};
|
|
159
267
|
export type ReadFeed = {
|
|
160
268
|
/**
|
|
161
|
-
* Display title, at most 100 characters, one line; empty means none (the feed's name is shown)
|
|
269
|
+
* Display title, at most 100 characters, one line (control and text-direction override characters are refused); empty means none (the feed's name is shown)
|
|
162
270
|
*/
|
|
163
271
|
title: string;
|
|
164
272
|
/**
|
|
165
|
-
* Description, at most 500 characters, one line; may be empty
|
|
273
|
+
* Description, at most 500 characters, one line (control and text-direction override characters are refused); may be empty
|
|
166
274
|
*/
|
|
167
275
|
description: string;
|
|
168
276
|
/**
|
|
@@ -223,16 +331,35 @@ export type ListNotesResponses = {
|
|
|
223
331
|
};
|
|
224
332
|
export type ListNotesResponse = ListNotesResponses[keyof ListNotesResponses];
|
|
225
333
|
export type PostNoteData = {
|
|
226
|
-
|
|
334
|
+
/**
|
|
335
|
+
* The note, a file: its bytes, of the type `Content-Type` declares
|
|
336
|
+
*/
|
|
337
|
+
body: Blob | File;
|
|
227
338
|
headers?: {
|
|
228
339
|
/**
|
|
229
340
|
* The feed's own password, when it has one: to post, list or get, and (as the current password) to change or remove it. On the `POST` that creates a feed it sets the feed's password; on a `POST` to an existing feed that has none it answers 409. An empty value is the same as no header, so a `POST` with an empty one creates an open feed.
|
|
230
341
|
*/
|
|
231
342
|
'X-Feed-Password'?: string;
|
|
343
|
+
/**
|
|
344
|
+
* The read id the feed gets when this post creates it: 3 to 64 characters (a-z, 0-9, - and _), random when left out or empty, ignored for a feed that exists. A short readable one is guessable: protect the feed with a password if that matters. 409 when it is taken
|
|
345
|
+
*/
|
|
346
|
+
'X-Read-Id'?: string;
|
|
347
|
+
/**
|
|
348
|
+
* The note's title, for a raw body; a JSON or form body's own `title` wins. At most 100 characters, one line, with no control or text-direction override characters.
|
|
349
|
+
*/
|
|
350
|
+
'X-Note-Title'?: string;
|
|
232
351
|
/**
|
|
233
352
|
* Tags for the note, comma-separated (`ci,deploy`): at most 10 tags, each 1 to 32 characters of letters, digits, `-`, `_`, `.` and `:`; case is folded to lowercase, duplicates are removed. For a raw markdown body; a JSON or form body's own `tags` wins. Empty is none.
|
|
234
353
|
*/
|
|
235
354
|
'X-Note-Tags'?: string;
|
|
355
|
+
/**
|
|
356
|
+
* For an image body: its alternative text; a form's own `alt` wins.
|
|
357
|
+
*/
|
|
358
|
+
'X-Note-Alt'?: string;
|
|
359
|
+
/**
|
|
360
|
+
* For an image body: the picture's original file name, kept with the note (at most 200 characters)
|
|
361
|
+
*/
|
|
362
|
+
'X-Note-Name'?: string;
|
|
236
363
|
};
|
|
237
364
|
path: {
|
|
238
365
|
/**
|
|
@@ -245,7 +372,7 @@ export type PostNoteData = {
|
|
|
245
372
|
};
|
|
246
373
|
export type PostNoteErrors = {
|
|
247
374
|
/**
|
|
248
|
-
* Invalid or reserved feed name;
|
|
375
|
+
* Invalid or reserved feed name; a blank note; a bad title, alt text or tags; a new password that is not printable ASCII; a multipart body with an unexpected part, no `text` and no `file`, more than 10 files, a bad or repeated file name, a text sent with pictures that takes too long to read (`NOTEFEED_PARSE_TIMEOUT_MS`), or `X-Note-Alt` / `X-Note-Name`
|
|
249
376
|
*/
|
|
250
377
|
400: Error;
|
|
251
378
|
/**
|
|
@@ -253,15 +380,15 @@ export type PostNoteErrors = {
|
|
|
253
380
|
*/
|
|
254
381
|
401: Error;
|
|
255
382
|
/**
|
|
256
|
-
* A password was sent for a feed that already exists without one: it can't be claimed
|
|
383
|
+
* A password was sent for a feed that already exists without one: it can't be claimed; or the chosen read id is taken
|
|
257
384
|
*/
|
|
258
385
|
409: Error;
|
|
259
386
|
/**
|
|
260
|
-
*
|
|
387
|
+
* Markdown over 102400 bytes, or an image over NOTEFEED_MAX_IMAGE_BYTES (each picture of a multipart body too), or a multipart body over 102400 bytes plus 10 images plus 64 KiB
|
|
261
388
|
*/
|
|
262
389
|
413: Error;
|
|
263
390
|
/**
|
|
264
|
-
*
|
|
391
|
+
* Content-Type missing or not one of the accepted types, or the body (or a multipart body's picture) is not what it declares
|
|
265
392
|
*/
|
|
266
393
|
415: Error;
|
|
267
394
|
/**
|
|
@@ -269,16 +396,16 @@ export type PostNoteErrors = {
|
|
|
269
396
|
*/
|
|
270
397
|
429: Error;
|
|
271
398
|
/**
|
|
272
|
-
* NOTEFEED_MAX_FEEDS or
|
|
399
|
+
* NOTEFEED_MAX_FEEDS, NOTEFEED_MAX_NOTES_PER_FEED or NOTEFEED_MAX_IMAGES_PER_FEED reached
|
|
273
400
|
*/
|
|
274
401
|
507: Error;
|
|
275
402
|
};
|
|
276
403
|
export type PostNoteError = PostNoteErrors[keyof PostNoteErrors];
|
|
277
404
|
export type PostNoteResponses = {
|
|
278
405
|
/**
|
|
279
|
-
* Stored
|
|
406
|
+
* Stored: the note, and with a multipart body its pictures
|
|
280
407
|
*/
|
|
281
|
-
201:
|
|
408
|
+
201: Posted;
|
|
282
409
|
};
|
|
283
410
|
export type PostNoteResponse = PostNoteResponses[keyof PostNoteResponses];
|
|
284
411
|
export type DeleteNoteData = {
|
|
@@ -375,8 +502,8 @@ export type GetNoteResponses = {
|
|
|
375
502
|
200: Note;
|
|
376
503
|
};
|
|
377
504
|
export type GetNoteResponse = GetNoteResponses[keyof GetNoteResponses];
|
|
378
|
-
export type
|
|
379
|
-
body:
|
|
505
|
+
export type PatchNoteData = {
|
|
506
|
+
body: NoteMeta;
|
|
380
507
|
headers?: {
|
|
381
508
|
/**
|
|
382
509
|
* The feed's own password, when it has one: to post, list or get, and (as the current password) to change or remove it. On the `POST` that creates a feed it sets the feed's password; on a `POST` to an existing feed that has none it answers 409. An empty value is the same as no header, so a `POST` with an empty one creates an open feed.
|
|
@@ -396,9 +523,9 @@ export type EditNoteData = {
|
|
|
396
523
|
query?: never;
|
|
397
524
|
url: '/api/v1/feeds/{feed}/notes/{id}';
|
|
398
525
|
};
|
|
399
|
-
export type
|
|
526
|
+
export type PatchNoteErrors = {
|
|
400
527
|
/**
|
|
401
|
-
* Invalid or reserved feed name;
|
|
528
|
+
* Invalid or reserved feed name; nothing to change; a bad title or alt text; alt for a note that has none; bad JSON
|
|
402
529
|
*/
|
|
403
530
|
400: Error;
|
|
404
531
|
/**
|
|
@@ -410,11 +537,7 @@ export type EditNoteErrors = {
|
|
|
410
537
|
*/
|
|
411
538
|
404: Error;
|
|
412
539
|
/**
|
|
413
|
-
*
|
|
414
|
-
*/
|
|
415
|
-
413: Error;
|
|
416
|
-
/**
|
|
417
|
-
* Unsupported content type
|
|
540
|
+
* Content-Type is not application/json
|
|
418
541
|
*/
|
|
419
542
|
415: Error;
|
|
420
543
|
/**
|
|
@@ -422,17 +545,17 @@ export type EditNoteErrors = {
|
|
|
422
545
|
*/
|
|
423
546
|
429: Error;
|
|
424
547
|
};
|
|
425
|
-
export type
|
|
426
|
-
export type
|
|
548
|
+
export type PatchNoteError = PatchNoteErrors[keyof PatchNoteErrors];
|
|
549
|
+
export type PatchNoteResponses = {
|
|
427
550
|
/**
|
|
428
551
|
* The note as it is now
|
|
429
552
|
*/
|
|
430
553
|
200: Note;
|
|
431
554
|
};
|
|
432
|
-
export type
|
|
433
|
-
export type
|
|
555
|
+
export type PatchNoteResponse = PatchNoteResponses[keyof PatchNoteResponses];
|
|
556
|
+
export type EditNoteData = {
|
|
434
557
|
/**
|
|
435
|
-
* The
|
|
558
|
+
* The note, a file: its bytes, of the type `Content-Type` declares
|
|
436
559
|
*/
|
|
437
560
|
body: Blob | File;
|
|
438
561
|
headers?: {
|
|
@@ -440,19 +563,27 @@ export type UploadImageData = {
|
|
|
440
563
|
* The feed's own password, when it has one: to post, list or get, and (as the current password) to change or remove it. On the `POST` that creates a feed it sets the feed's password; on a `POST` to an existing feed that has none it answers 409. An empty value is the same as no header, so a `POST` with an empty one creates an open feed.
|
|
441
564
|
*/
|
|
442
565
|
'X-Feed-Password'?: string;
|
|
566
|
+
/**
|
|
567
|
+
* Only for a multipart body: the tags go on the new pictures (the note keeps its own). A raw body ignores it. Comma-separated, empty is none.
|
|
568
|
+
*/
|
|
569
|
+
'X-Note-Tags'?: string;
|
|
443
570
|
};
|
|
444
571
|
path: {
|
|
445
572
|
/**
|
|
446
573
|
* The feed's name. It is the write key: anyone who knows it can post.
|
|
447
574
|
*/
|
|
448
575
|
feed: string;
|
|
576
|
+
/**
|
|
577
|
+
* The note's id
|
|
578
|
+
*/
|
|
579
|
+
id: string;
|
|
449
580
|
};
|
|
450
581
|
query?: never;
|
|
451
|
-
url: '/api/v1/feeds/{feed}/
|
|
582
|
+
url: '/api/v1/feeds/{feed}/notes/{id}';
|
|
452
583
|
};
|
|
453
|
-
export type
|
|
584
|
+
export type EditNoteErrors = {
|
|
454
585
|
/**
|
|
455
|
-
* Invalid or reserved feed name
|
|
586
|
+
* Invalid or reserved feed name; a blank note; a multipart body with an unexpected part, no `text`, more than 10 files, a bad or repeated file name, a text sent with pictures that takes too long to read (`NOTEFEED_PARSE_TIMEOUT_MS`), or `X-Note-Alt` / `X-Note-Name`
|
|
456
587
|
*/
|
|
457
588
|
400: Error;
|
|
458
589
|
/**
|
|
@@ -460,19 +591,19 @@ export type UploadImageErrors = {
|
|
|
460
591
|
*/
|
|
461
592
|
401: Error;
|
|
462
593
|
/**
|
|
463
|
-
* No such
|
|
594
|
+
* No such note
|
|
464
595
|
*/
|
|
465
596
|
404: Error;
|
|
466
597
|
/**
|
|
467
|
-
*
|
|
598
|
+
* Markdown over 102400 bytes, or an image over NOTEFEED_MAX_IMAGE_BYTES (each picture of a multipart body too), or a multipart body over 102400 bytes plus 10 images plus 64 KiB
|
|
468
599
|
*/
|
|
469
600
|
413: Error;
|
|
470
601
|
/**
|
|
471
|
-
*
|
|
602
|
+
* Content-Type missing, not accepted or not the note's own type (a multipart body is for a markdown note), or the body (or a picture) is not what it declares
|
|
472
603
|
*/
|
|
473
604
|
415: Error;
|
|
474
605
|
/**
|
|
475
|
-
* Too many posts,
|
|
606
|
+
* Too many posts, edits and deletes, or wrong passwords, from this client
|
|
476
607
|
*/
|
|
477
608
|
429: Error;
|
|
478
609
|
/**
|
|
@@ -480,14 +611,14 @@ export type UploadImageErrors = {
|
|
|
480
611
|
*/
|
|
481
612
|
507: Error;
|
|
482
613
|
};
|
|
483
|
-
export type
|
|
484
|
-
export type
|
|
614
|
+
export type EditNoteError = EditNoteErrors[keyof EditNoteErrors];
|
|
615
|
+
export type EditNoteResponses = {
|
|
485
616
|
/**
|
|
486
|
-
*
|
|
617
|
+
* The note as it is now, and with a multipart body the new pictures
|
|
487
618
|
*/
|
|
488
|
-
|
|
619
|
+
200: NoteEdited;
|
|
489
620
|
};
|
|
490
|
-
export type
|
|
621
|
+
export type EditNoteResponse = EditNoteResponses[keyof EditNoteResponses];
|
|
491
622
|
export type DeleteFeedData = {
|
|
492
623
|
body?: never;
|
|
493
624
|
headers?: {
|
|
@@ -593,7 +724,7 @@ export type UpdateFeedData = {
|
|
|
593
724
|
};
|
|
594
725
|
export type UpdateFeedErrors = {
|
|
595
726
|
/**
|
|
596
|
-
* Invalid or reserved feed name, bad JSON,
|
|
727
|
+
* Invalid or reserved feed name, bad JSON, a title or description that is too long or has control or text-direction override characters, a malformed read id, a reserved feed's read id, or chosen read ids turned off
|
|
597
728
|
*/
|
|
598
729
|
400: Error;
|
|
599
730
|
/**
|
|
@@ -604,6 +735,10 @@ export type UpdateFeedErrors = {
|
|
|
604
735
|
* No such feed
|
|
605
736
|
*/
|
|
606
737
|
404: Error;
|
|
738
|
+
/**
|
|
739
|
+
* The read id belongs to another feed or is held back for a reserved one
|
|
740
|
+
*/
|
|
741
|
+
409: Error;
|
|
607
742
|
/**
|
|
608
743
|
* Too many posts, edits and deletes, or wrong passwords, from this client
|
|
609
744
|
*/
|
|
@@ -707,7 +842,7 @@ export type GetReadFeedData = {
|
|
|
707
842
|
body?: never;
|
|
708
843
|
path: {
|
|
709
844
|
/**
|
|
710
|
-
* The feed's read id, from its read link: 22 random characters, or the feed's own name for a reserved feed. Read-only; never reveals the name of any other feed.
|
|
845
|
+
* The feed's read id, from its read link: 22 random characters, one the feed's owner chose, or the feed's own name for a reserved feed. Read-only; never reveals the name of any other feed.
|
|
711
846
|
*/
|
|
712
847
|
readId: string;
|
|
713
848
|
};
|
|
@@ -732,7 +867,7 @@ export type ListReadNotesData = {
|
|
|
732
867
|
body?: never;
|
|
733
868
|
path: {
|
|
734
869
|
/**
|
|
735
|
-
* The feed's read id, from its read link: 22 random characters, or the feed's own name for a reserved feed. Read-only; never reveals the name of any other feed.
|
|
870
|
+
* The feed's read id, from its read link: 22 random characters, one the feed's owner chose, or the feed's own name for a reserved feed. Read-only; never reveals the name of any other feed.
|
|
736
871
|
*/
|
|
737
872
|
readId: string;
|
|
738
873
|
};
|
|
@@ -774,7 +909,7 @@ export type GetReadNoteData = {
|
|
|
774
909
|
body?: never;
|
|
775
910
|
path: {
|
|
776
911
|
/**
|
|
777
|
-
* The feed's read id, from its read link: 22 random characters, or the feed's own name for a reserved feed. Read-only; never reveals the name of any other feed.
|
|
912
|
+
* The feed's read id, from its read link: 22 random characters, one the feed's owner chose, or the feed's own name for a reserved feed. Read-only; never reveals the name of any other feed.
|
|
778
913
|
*/
|
|
779
914
|
readId: string;
|
|
780
915
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { AuthError, Client, ConfigError, InvalidRequestError, LimitReachedError, NotefeedError, NotFoundError, NoteTooLargeError, RateLimitedError, } from "./client.js";
|
|
2
|
-
export type { ClientOptions, Created, ErrorCode, Feed, FeedSettings,
|
|
2
|
+
export type { Attachment, ClientOptions, Created, ErrorCode, Feed, FeedSettings, Note } from "./client.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notefeed",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Client and CLI for notefeed, a self-hosted inbox for markdown notes that you read back as RSS.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Marcel Bruckner",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"url": "git+https://github.com/Marceltov/notefeed.git",
|
|
40
40
|
"directory": "packages/js"
|
|
41
41
|
},
|
|
42
|
-
"homepage": "https://docs.notefeed.me/clients/",
|
|
42
|
+
"homepage": "https://docs.notefeed.me/integrations/clients/",
|
|
43
43
|
"bugs": {
|
|
44
44
|
"url": "https://github.com/Marceltov/notefeed/issues"
|
|
45
45
|
},
|