notefeed 0.26.0 → 0.28.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 +6 -4
- package/dist/cli.js +9 -8
- package/dist/client.d.ts +6 -3
- package/dist/client.js +11 -4
- package/dist/generated/sdk.gen.d.ts +3 -3
- package/dist/generated/sdk.gen.js +3 -3
- package/dist/generated/types.gen.d.ts +24 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@ export NOTEFEED_URL=https://notes.example.com NOTEFEED_FEED=homelab-7f3k2q9x4m8w
|
|
|
8
8
|
npx notefeed post "# Backup finished"
|
|
9
9
|
backup.sh 2>&1 | npx notefeed post -
|
|
10
10
|
npx notefeed notes --limit 5
|
|
11
|
-
npx notefeed edit
|
|
12
|
-
npx notefeed delete
|
|
11
|
+
npx notefeed edit Vq3xH8mTn2KfLw0aZr9bYe "# Backup finished, verified"
|
|
12
|
+
npx notefeed delete Vq3xH8mTn2KfLw0aZr9bYe
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
```js
|
|
@@ -18,11 +18,13 @@ import { Client } from "notefeed";
|
|
|
18
18
|
const client = new Client({ url: "https://notes.example.com", feed: "homelab-7f3k2q9x4m8wz" }); // password: "..." if the instance has one
|
|
19
19
|
const created = await client.post("# Deploy done\nversion 1.4.2");
|
|
20
20
|
console.log(created.url, created.read_url);
|
|
21
|
-
for await (const note of client.notes()) console.log(note.
|
|
21
|
+
for await (const note of client.notes()) console.log(note.date, note.title); // newest first, page by page
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
`read_url` is `null` for a feed without a read link (rare; see the operations docs).
|
|
25
25
|
|
|
26
|
+
Every note has a date, now unless you give one: `client.post(text, { date: "2025-03-01T09:30:00Z" })` (a `Date`, an ISO 8601 date-time with its offset, or a date alone for midnight UTC; any time from 1970 to 9999), or `notefeed post "…" --date 2025-03-01`. The note takes its place in the feed by that date. A date in the future schedules the note: readers see it from then on. `await client.update(id, { date })` (`notefeed update <id> --date …`) moves a note.
|
|
27
|
+
|
|
26
28
|
`Client.fromEnv()` reads `NOTEFEED_URL` / `NOTEFEED_FEED` / `NOTEFEED_PASSWORD`; so does the `notefeed` command, unless given `--url` / `--feed` / `--password`.
|
|
27
29
|
|
|
28
30
|
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.
|
|
@@ -31,6 +33,6 @@ A posted note can be changed or removed: `await client.edit(id, content)` replac
|
|
|
31
33
|
|
|
32
34
|
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
35
|
|
|
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, a picture that is too large is a `NoteTooLargeError`, and an instance that takes no pictures (`NOTEFEED_IMAGE_UPLOADS=0`) answers every picture with an `ImagesOffError`. The server checks the bytes against the declared type. `await client.update(id, { title, alt })` changes a note's title
|
|
36
|
+
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, a picture that is too large is a `NoteTooLargeError`, and an instance that takes no pictures (`NOTEFEED_IMAGE_UPLOADS=0`) answers every picture with an `ImagesOffError`. The server checks the bytes against the declared type. `await client.update(id, { title, alt, date })` changes a note's title, a picture's alt text or a note's date, 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
37
|
|
|
36
38
|
Full documentation: https://docs.notefeed.me/integrations/clients/
|
package/dist/cli.js
CHANGED
|
@@ -7,9 +7,9 @@ import { Client, ConfigError, NotefeedError } from "./client.js";
|
|
|
7
7
|
class UsageError extends Error {
|
|
8
8
|
}
|
|
9
9
|
const USAGE = [
|
|
10
|
-
"usage: notefeed post <text | - | --file PATH> [--type MEDIA_TYPE] [--title TITLE] [--tag TAG]... [--attach PATH]... [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
10
|
+
"usage: notefeed post <text | - | --file PATH> [--type MEDIA_TYPE] [--title TITLE] [--tag TAG]... [--attach PATH]... [--date WHEN] [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
11
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]",
|
|
12
|
+
" notefeed update <id> [--title TITLE] [--alt ALT] [--date WHEN] [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
13
13
|
" notefeed delete <id> [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
14
14
|
" notefeed notes [--limit N] [--tag TAG] [--json] [--url URL] [--feed FEED] [--password PASSWORD]",
|
|
15
15
|
].join("\n");
|
|
@@ -30,6 +30,7 @@ export async function main(argv, io = process) {
|
|
|
30
30
|
title: { type: "string" },
|
|
31
31
|
alt: { type: "string" },
|
|
32
32
|
tag: { type: "string", multiple: true },
|
|
33
|
+
date: { type: "string" },
|
|
33
34
|
attach: { type: "string", multiple: true },
|
|
34
35
|
limit: { type: "string" },
|
|
35
36
|
json: { type: "boolean" },
|
|
@@ -53,7 +54,7 @@ export async function main(argv, io = process) {
|
|
|
53
54
|
const noText = attachments.length > 0 && text === undefined && !values.file;
|
|
54
55
|
const input = noText ? { content: null, type: undefined, name: undefined } : await read(text, values.file, values.type, io);
|
|
55
56
|
// 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
|
+
const note = await client(values).post(input.content, { type: input.type, title: values.title, tags: values.tag, date: values.date, name: attachments.length ? undefined : input.name, attachments });
|
|
57
58
|
io.stdout.write(`${[...(noText ? [] : [note.url]), ...note.attachments.map((a) => a.url)].join("\n")}\n`);
|
|
58
59
|
return 0;
|
|
59
60
|
}
|
|
@@ -64,9 +65,9 @@ export async function main(argv, io = process) {
|
|
|
64
65
|
return 0;
|
|
65
66
|
}
|
|
66
67
|
if (command === "update" && text !== undefined && !rest.length) {
|
|
67
|
-
if (values.title === undefined && values.alt === undefined)
|
|
68
|
-
throw new UsageError("give --title and/or --
|
|
69
|
-
const note = await client(values).update(text, { title: values.title, alt: values.alt });
|
|
68
|
+
if (values.title === undefined && values.alt === undefined && values.date === undefined)
|
|
69
|
+
throw new UsageError("give --title, --alt and/or --date");
|
|
70
|
+
const note = await client(values).update(text, { title: values.title, alt: values.alt, date: values.date });
|
|
70
71
|
io.stdout.write(`${note.url}\n`);
|
|
71
72
|
return 0;
|
|
72
73
|
}
|
|
@@ -81,9 +82,9 @@ export async function main(argv, io = process) {
|
|
|
81
82
|
let left = limit;
|
|
82
83
|
for await (const n of client(values).notes({ pageSize: Math.min(limit, 100), tag: values.tag?.[0] })) {
|
|
83
84
|
// To the second, UTC: the same form as the Python CLI, so scripts read either the same way.
|
|
84
|
-
const when = new Date(n.
|
|
85
|
+
const when = new Date(n.date).toISOString().replace(/\.\d+Z$/, "Z");
|
|
85
86
|
// --json: exactly the documented fields, like the Python CLI, even if the server adds more.
|
|
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,
|
|
87
|
+
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, date: when, url: n.url };
|
|
87
88
|
io.stdout.write(values.json ? `${JSON.stringify(fields)}\n` : `${when} ${n.title || n.id} ${n.url}\n`);
|
|
88
89
|
if (--left === 0)
|
|
89
90
|
break;
|
package/dist/client.d.ts
CHANGED
|
@@ -67,14 +67,15 @@ export declare class Client {
|
|
|
67
67
|
*
|
|
68
68
|
* `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
|
|
69
69
|
* letters, digits, `-`, `_`, `.`, `:`; not verified). `title` is its title (at most 100 characters, one line); left out, it is taken from the
|
|
70
|
-
* text. `alt` is a picture's alternative text, `name` the file's original name. `
|
|
70
|
+
* text. `alt` is a picture's alternative text, `name` the file's original name. `date` is the note's date, if not now: a
|
|
71
|
+
* Date or an ISO 8601 date-time with its offset (`2026-10-01T09:30:00Z`) or a date alone (midnight UTC), from 1970 to 9999; a future date schedules the note: readers see it from then on. `readId` is the read id the feed gets when this post creates it
|
|
71
72
|
* (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).
|
|
72
73
|
* `licenseKey` goes with `feedPassword` on the post that creates the feed, on an instance that sells licenses: a feed password is for
|
|
73
74
|
* licensed feeds, and the new feed gets the license (a `license_required` error without the key; ignored on an existing feed).
|
|
74
75
|
*
|
|
75
76
|
* `attachments` are pictures posted with the note in one request: `` in the markdown shows one (a picture the text never refers to is
|
|
76
77
|
* added at the end), and `Created.attachments` lists them in order. `tags` label the pictures too. `content` may be `null` when there are
|
|
77
|
-
* attachments: only the pictures are posted, `title` and `
|
|
78
|
+
* attachments: only the pictures are posted, `title`, `tags` and `date` go on each, and the result is the first picture plus `attachments`. Checked
|
|
78
79
|
* here, before anything is sent (ConfigError): a name given twice, an attachment that is not a picture, content that is not markdown, `alt` or
|
|
79
80
|
* `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
|
|
80
81
|
* InvalidRequestError, `attachment "<name>": ...`, and whenever the server refuses any part (its message names it) nothing is posted.
|
|
@@ -87,6 +88,7 @@ export declare class Client {
|
|
|
87
88
|
tags?: string[];
|
|
88
89
|
alt?: string;
|
|
89
90
|
name?: string;
|
|
91
|
+
date?: string | Date;
|
|
90
92
|
readId?: string;
|
|
91
93
|
licenseKey?: string;
|
|
92
94
|
attachments?: Attachment[];
|
|
@@ -100,10 +102,11 @@ export declare class Client {
|
|
|
100
102
|
feedPassword?: string;
|
|
101
103
|
type?: string;
|
|
102
104
|
}): Promise<Note>;
|
|
103
|
-
/** Set a note's title and/or
|
|
105
|
+
/** Set a note's title, alt text (for pictures) and/or date (as in post(); a future one schedules the note); `""` removes a title or alt, so a markdown note's title follows its text again. Same options as post(). */
|
|
104
106
|
update(id: string, meta: {
|
|
105
107
|
title?: string;
|
|
106
108
|
alt?: string;
|
|
109
|
+
date?: string | Date;
|
|
107
110
|
}, options?: {
|
|
108
111
|
feed?: string;
|
|
109
112
|
feedPassword?: string;
|
package/dist/client.js
CHANGED
|
@@ -68,6 +68,8 @@ const FEED_RE = /^[a-z0-9_-]{1,64}$/; // same rule as the server; reserved names
|
|
|
68
68
|
// 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
|
|
69
69
|
// latin1 characters, which is what the server reads back.
|
|
70
70
|
const headerValue = (text) => Array.from(new TextEncoder().encode(text), (b) => String.fromCharCode(b)).join("");
|
|
71
|
+
// X-Note-Date: a Date as its UTC time, a string as given (the server checks it).
|
|
72
|
+
const dateString = (at) => (typeof at === "string" ? at : at.toISOString());
|
|
71
73
|
// What a post sends: a string is markdown; bytes and Blobs need a media type (a Blob's own, if it has one).
|
|
72
74
|
function asFile(content, type) {
|
|
73
75
|
if (typeof content === "string")
|
|
@@ -115,14 +117,15 @@ export class Client {
|
|
|
115
117
|
*
|
|
116
118
|
* `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
|
|
117
119
|
* letters, digits, `-`, `_`, `.`, `:`; not verified). `title` is its title (at most 100 characters, one line); left out, it is taken from the
|
|
118
|
-
* text. `alt` is a picture's alternative text, `name` the file's original name. `
|
|
120
|
+
* text. `alt` is a picture's alternative text, `name` the file's original name. `date` is the note's date, if not now: a
|
|
121
|
+
* Date or an ISO 8601 date-time with its offset (`2026-10-01T09:30:00Z`) or a date alone (midnight UTC), from 1970 to 9999; a future date schedules the note: readers see it from then on. `readId` is the read id the feed gets when this post creates it
|
|
119
122
|
* (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).
|
|
120
123
|
* `licenseKey` goes with `feedPassword` on the post that creates the feed, on an instance that sells licenses: a feed password is for
|
|
121
124
|
* licensed feeds, and the new feed gets the license (a `license_required` error without the key; ignored on an existing feed).
|
|
122
125
|
*
|
|
123
126
|
* `attachments` are pictures posted with the note in one request: `` in the markdown shows one (a picture the text never refers to is
|
|
124
127
|
* added at the end), and `Created.attachments` lists them in order. `tags` label the pictures too. `content` may be `null` when there are
|
|
125
|
-
* attachments: only the pictures are posted, `title` and `
|
|
128
|
+
* attachments: only the pictures are posted, `title`, `tags` and `date` go on each, and the result is the first picture plus `attachments`. Checked
|
|
126
129
|
* here, before anything is sent (ConfigError): a name given twice, an attachment that is not a picture, content that is not markdown, `alt` or
|
|
127
130
|
* `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
|
|
128
131
|
* InvalidRequestError, `attachment "<name>": ...`, and whenever the server refuses any part (its message names it) nothing is posted.
|
|
@@ -156,6 +159,7 @@ export class Client {
|
|
|
156
159
|
"Content-Type": null, // fetch sets multipart/form-data with its boundary
|
|
157
160
|
...(own.title && { "X-Note-Title": headerValue(own.title) }),
|
|
158
161
|
...(own.tags?.length && { "X-Note-Tags": own.tags.join(",") }),
|
|
162
|
+
...(own.date && { "X-Note-Date": dateString(own.date) }),
|
|
159
163
|
...(own.readId && { "X-Read-Id": own.readId }),
|
|
160
164
|
...(own.licenseKey && { "X-License-Key": own.licenseKey }),
|
|
161
165
|
};
|
|
@@ -172,6 +176,7 @@ export class Client {
|
|
|
172
176
|
...(options.tags?.length && { "X-Note-Tags": options.tags.join(",") }),
|
|
173
177
|
...(options.alt && { "X-Note-Alt": headerValue(options.alt) }),
|
|
174
178
|
...(options.name && { "X-Note-Name": headerValue(options.name) }),
|
|
179
|
+
...(options.date && { "X-Note-Date": dateString(options.date) }),
|
|
175
180
|
...(options.readId && { "X-Read-Id": options.readId }),
|
|
176
181
|
...(options.licenseKey && { "X-License-Key": options.licenseKey }),
|
|
177
182
|
};
|
|
@@ -184,10 +189,12 @@ export class Client {
|
|
|
184
189
|
const headers = { ...this.opts(options.feedPassword).headers, "Content-Type": type };
|
|
185
190
|
return this.call(editNote({ client: this.api, path: { feed, id }, body, headers, signal: AbortSignal.timeout(this.timeoutMs) }));
|
|
186
191
|
}
|
|
187
|
-
/** Set a note's title and/or
|
|
192
|
+
/** Set a note's title, alt text (for pictures) and/or date (as in post(); a future one schedules the note); `""` removes a title or alt, so a markdown note's title follows its text again. Same options as post(). */
|
|
188
193
|
async update(id, meta, options = {}) {
|
|
189
194
|
const feed = this.feedFor(options.feed);
|
|
190
|
-
|
|
195
|
+
const { date, ...rest } = meta;
|
|
196
|
+
const body = { ...rest, ...(date !== undefined && { date: dateString(date) }) };
|
|
197
|
+
return this.call(patchNote({ client: this.api, path: { feed, id }, body, ...this.opts(options.feedPassword) }));
|
|
191
198
|
}
|
|
192
199
|
/** Remove a note for good. The feed stays, even with no notes left. Same options as post(). */
|
|
193
200
|
async delete(id, options = {}) {
|
|
@@ -22,7 +22,7 @@ export declare const listNotes: <ThrowOnError extends boolean = false>(options:
|
|
|
22
22
|
/**
|
|
23
23
|
* Post a note
|
|
24
24
|
*
|
|
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. Where licenses are sold, a feed password needs one: the post that creates the feed protected brings the license key in `X-License-Key`, or is refused with `402`. 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
|
|
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. Where licenses are sold, a feed password needs one: the post that creates the feed protected brings the license key in `X-License-Key`, or is refused with `402`. 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), `X-Note-Date` (where it is placed in the feed: a past date backdates it, a future one schedules it). 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, `X-Note-Tags` on every note, `X-Note-Date` on the text note (its pictures take the milliseconds just before it); 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,9 +36,9 @@ 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
|
-
* Change a note's title
|
|
39
|
+
* Change a note's title, alt text or date
|
|
40
40
|
*
|
|
41
|
-
* Sets the note's title
|
|
41
|
+
* Sets the note's title, alt text (only for images) and/or date. An empty title or alt removes it: a markdown note's title follows its text again. A new date moves the note in the feed, with its attachments; a date after now schedules it. Id and links stay. 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
43
|
export declare const patchNote: <ThrowOnError extends boolean = false>(options: Options<PatchNoteData, ThrowOnError>) => RequestResult<PatchNoteResponses, PatchNoteErrors, ThrowOnError>;
|
|
44
44
|
/**
|
|
@@ -13,7 +13,7 @@ export const listNotes = (options) => (options.client ?? client).get({
|
|
|
13
13
|
/**
|
|
14
14
|
* Post a note
|
|
15
15
|
*
|
|
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. Where licenses are sold, a feed password needs one: the post that creates the feed protected brings the license key in `X-License-Key`, or is refused with `402`. 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
|
|
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. Where licenses are sold, a feed password needs one: the post that creates the feed protected brings the license key in `X-License-Key`, or is refused with `402`. 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), `X-Note-Date` (where it is placed in the feed: a past date backdates it, a future one schedules it). 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, `X-Note-Tags` on every note, `X-Note-Date` on the text note (its pictures take the milliseconds just before it); 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
19
|
bodySerializer: null,
|
|
@@ -44,9 +44,9 @@ export const getNote = (options) => (options.client ?? client).get({
|
|
|
44
44
|
...options
|
|
45
45
|
});
|
|
46
46
|
/**
|
|
47
|
-
* Change a note's title
|
|
47
|
+
* Change a note's title, alt text or date
|
|
48
48
|
*
|
|
49
|
-
* Sets the note's title
|
|
49
|
+
* Sets the note's title, alt text (only for images) and/or date. An empty title or alt removes it: a markdown note's title follows its text again. A new date moves the note in the feed, with its attachments; a date after now schedules it. Id and links stay. 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.
|
|
50
50
|
*/
|
|
51
51
|
export const patchNote = (options) => (options.client ?? client).patch({
|
|
52
52
|
security: [{ scheme: 'bearer', type: 'http' }],
|
|
@@ -3,7 +3,7 @@ export type ClientOptions = {
|
|
|
3
3
|
};
|
|
4
4
|
export type Note = {
|
|
5
5
|
/**
|
|
6
|
-
* The note's id:
|
|
6
|
+
* The note's id: 22 random characters (base64url) for notes made here, older notes may have another form; any name without a dot for a file placed by hand. It says nothing about the note's date
|
|
7
7
|
*/
|
|
8
8
|
id: string;
|
|
9
9
|
/**
|
|
@@ -39,9 +39,13 @@ export type Note = {
|
|
|
39
39
|
*/
|
|
40
40
|
name?: string;
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
42
|
+
* Where the note is placed in the feed (UTC): when it was posted, unless a date was given or changed; lists are ordered by it
|
|
43
43
|
*/
|
|
44
|
-
|
|
44
|
+
date: string;
|
|
45
|
+
/**
|
|
46
|
+
* Present, and true, when the note's date is after now: it is stored, and readers see it from its date on. Absent for every other note
|
|
47
|
+
*/
|
|
48
|
+
scheduled?: true;
|
|
45
49
|
/**
|
|
46
50
|
* The note's page in the web UI
|
|
47
51
|
*/
|
|
@@ -124,7 +128,7 @@ export type Posted = {
|
|
|
124
128
|
};
|
|
125
129
|
export type NoteEdited = {
|
|
126
130
|
/**
|
|
127
|
-
* The note's id:
|
|
131
|
+
* The note's id: 22 random characters (base64url) for notes made here, older notes may have another form; any name without a dot for a file placed by hand. It says nothing about the note's date
|
|
128
132
|
*/
|
|
129
133
|
id: string;
|
|
130
134
|
/**
|
|
@@ -160,9 +164,13 @@ export type NoteEdited = {
|
|
|
160
164
|
*/
|
|
161
165
|
name?: string;
|
|
162
166
|
/**
|
|
163
|
-
*
|
|
167
|
+
* Where the note is placed in the feed (UTC): when it was posted, unless a date was given or changed; lists are ordered by it
|
|
168
|
+
*/
|
|
169
|
+
date: string;
|
|
170
|
+
/**
|
|
171
|
+
* Present, and true, when the note's date is after now: it is stored, and readers see it from its date on. Absent for every other note
|
|
164
172
|
*/
|
|
165
|
-
|
|
173
|
+
scheduled?: true;
|
|
166
174
|
/**
|
|
167
175
|
* The note's page in the web UI
|
|
168
176
|
*/
|
|
@@ -213,6 +221,10 @@ export type NoteMeta = {
|
|
|
213
221
|
* Alternative text of an image note, at most 500 characters, one line (control and text-direction override characters are refused)
|
|
214
222
|
*/
|
|
215
223
|
alt?: string;
|
|
224
|
+
/**
|
|
225
|
+
* An ISO 8601 date and time with its offset (`2026-10-01T09:30:00Z`, `2026-10-01T11:30:00+02:00`) or a date alone (midnight UTC). When the note is placed in the feed, from 1970 to 9999. A time after now schedules it: readers see it from then on. The note's attachments move with it.
|
|
226
|
+
*/
|
|
227
|
+
date?: string;
|
|
216
228
|
};
|
|
217
229
|
export type PasswordJson = {
|
|
218
230
|
/**
|
|
@@ -390,6 +402,10 @@ export type PostNoteData = {
|
|
|
390
402
|
* For an image body: the picture's original file name, kept with the note (at most 200 characters)
|
|
391
403
|
*/
|
|
392
404
|
'X-Note-Name'?: string;
|
|
405
|
+
/**
|
|
406
|
+
* An ISO 8601 date and time with its offset (`2026-10-01T09:30:00Z`, `2026-10-01T11:30:00+02:00`) or a date alone (midnight UTC). When the note is placed in the feed, from 1970 to 9999. A time after now schedules it: readers see it from then on. Empty is now.
|
|
407
|
+
*/
|
|
408
|
+
'X-Note-Date'?: string;
|
|
393
409
|
};
|
|
394
410
|
path: {
|
|
395
411
|
/**
|
|
@@ -402,7 +418,7 @@ export type PostNoteData = {
|
|
|
402
418
|
};
|
|
403
419
|
export type PostNoteErrors = {
|
|
404
420
|
/**
|
|
405
|
-
* Invalid or reserved feed name; a blank note; a bad title, alt text or
|
|
421
|
+
* Invalid or reserved feed name; a blank note; a bad title, alt text, tags or `X-Note-Date`; `X-Note-Created` (the header's name before 0.28.0); a new password that is not printable ASCII; a license key that is none; 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`
|
|
406
422
|
*/
|
|
407
423
|
400: Error;
|
|
408
424
|
/**
|
|
@@ -583,7 +599,7 @@ export type PatchNoteData = {
|
|
|
583
599
|
};
|
|
584
600
|
export type PatchNoteErrors = {
|
|
585
601
|
/**
|
|
586
|
-
* Invalid or reserved feed name; nothing to change; a bad title
|
|
602
|
+
* Invalid or reserved feed name; nothing to change; a bad title, alt text or date; alt for a note that has none; bad JSON
|
|
587
603
|
*/
|
|
588
604
|
400: Error;
|
|
589
605
|
/**
|
package/package.json
CHANGED