freshjots 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Goran Arsov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # freshjots — JavaScript
2
+
3
+ Tiny JavaScript client for the [Fresh Jots](https://freshjots.com) API.
4
+ One file, zero dependencies (uses Node 18's global `fetch`).
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ npm install freshjots
10
+ ```
11
+
12
+ (Or `pnpm add freshjots`, `yarn add freshjots`, `bun add freshjots`.)
13
+
14
+ ## Use
15
+
16
+ ```js
17
+ import { Client } from "freshjots";
18
+
19
+ // Reads FRESHJOTS_TOKEN from the environment by default.
20
+ const client = new Client();
21
+
22
+ // Append text to a note (creates it if missing).
23
+ await client.append("cron-jobs-prod", "backup ok");
24
+
25
+ // Read a note's body.
26
+ const note = await client.note("cron-jobs-prod");
27
+ console.log(note.plain_body);
28
+
29
+ // List your notes.
30
+ const notes = await client.notes();
31
+ for (const n of notes) console.log(`${n.filename}\t${n.title}`);
32
+
33
+ // Create a new note explicitly (errors if the filename is taken).
34
+ await client.create({ filename: "research-2026-q2", body: "Initial outline." });
35
+ ```
36
+
37
+ The whole API is four methods: `notes()`, `note(filename)`,
38
+ `create({ filename, body, title })`, `append(filename, text)`.
39
+
40
+ ## TypeScript
41
+
42
+ Types ship with the package — no `@types/freshjots` needed, no `.d.ts`
43
+ to hand-write. Just import the typed surface:
44
+
45
+ ```ts
46
+ import { Client, ApiError, type Note, type ApiErrorCode } from "freshjots";
47
+
48
+ const client = new Client();
49
+ const note: Note = await client.note("cron-jobs-prod");
50
+ // ^? Note — full editor autocomplete on plain_body, byte_size, etc.
51
+
52
+ try {
53
+ await client.append("log", "ok");
54
+ } catch (e) {
55
+ if (e instanceof ApiError) {
56
+ const code: ApiErrorCode = e.code; // narrowed union; exhaustive switches work
57
+ }
58
+ }
59
+ ```
60
+
61
+ Requires TypeScript 4.5 or later (anything that understands the `"types"`
62
+ field in `package.json`'s `"exports"` map).
63
+
64
+ ## Errors
65
+
66
+ Any non-2xx response throws `ApiError` with `status`, `code`, `message`,
67
+ and (when present) `details`:
68
+
69
+ ```js
70
+ import { ApiError } from "freshjots";
71
+
72
+ try {
73
+ await client.append("huge", "x".repeat(5_000_000));
74
+ } catch (e) {
75
+ if (e instanceof ApiError) {
76
+ console.log(`${e.status} ${e.code}: ${e.message}`);
77
+ // 413 content_too_large: body exceeds the per-note 3 MB cap
78
+ } else {
79
+ throw e;
80
+ }
81
+ }
82
+ ```
83
+
84
+ Stable error codes: `unauthenticated`, `forbidden`, `not_found`,
85
+ `validation_failed`, `cap_exceeded`, `storage_cap_exceeded`,
86
+ `content_too_large`, `content_type_mismatch`, `rate_limited`. Full list:
87
+ <https://freshjots.com/docs>.
88
+
89
+ ## Auth
90
+
91
+ Mint a token at <https://freshjots.com/settings/api_tokens> (Dev or
92
+ Dev-pro tier required). Set it once:
93
+
94
+ ```sh
95
+ export FRESHJOTS_TOKEN=<your-token>
96
+ ```
97
+
98
+ Or pass explicitly:
99
+
100
+ ```js
101
+ new Client({ token: "mn_…" })
102
+ ```
103
+
104
+ ## Requirements
105
+
106
+ Node.js 18 or later. Older versions don't have global `fetch`.
107
+
108
+ ## License
109
+
110
+ MIT.
package/index.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ // Type definitions for freshjots
2
+ // Project: https://github.com/Goran-Arsov/freshjots-js
3
+ // License: MIT
4
+
5
+ declare module "freshjots" {
6
+ export const VERSION: string;
7
+
8
+ /** Full note envelope returned by `note()` and `create()`. */
9
+ export interface Note {
10
+ id: number;
11
+ filename: string;
12
+ title: string;
13
+ format: "plain" | "rich";
14
+ folder_id: number | null;
15
+ pinned: boolean;
16
+ append_only: boolean;
17
+ append_deadline_hours: number | null;
18
+ alert_email: string | null;
19
+ last_appended_at: string | null;
20
+ alerted_at: string | null;
21
+ webhook_url: string | null;
22
+ webhook_failure_count: number;
23
+ webhook_disabled_at: string | null;
24
+ plain_body: string;
25
+ byte_size: number;
26
+ }
27
+
28
+ /** Summary projection returned by `notes()` (list view). */
29
+ export interface NoteSummary {
30
+ id: number;
31
+ filename: string;
32
+ title: string;
33
+ format: "plain" | "rich";
34
+ last_appended_at: string | null;
35
+ plain_body_excerpt?: string;
36
+ byte_size?: number;
37
+ }
38
+
39
+ /** Stable error codes — safe to branch on. */
40
+ export type ApiErrorCode =
41
+ | "unauthenticated"
42
+ | "forbidden"
43
+ | "not_found"
44
+ | "validation_failed"
45
+ | "cap_exceeded"
46
+ | "storage_cap_exceeded"
47
+ | "content_too_large"
48
+ | "content_type_mismatch"
49
+ | "rate_limited"
50
+ | "unknown";
51
+
52
+ export class ApiError extends Error {
53
+ status: number;
54
+ code: ApiErrorCode;
55
+ details?: unknown;
56
+ constructor(opts: { status: number; code: ApiErrorCode; message: string; details?: unknown });
57
+ }
58
+
59
+ export interface ClientOptions {
60
+ token?: string;
61
+ baseUrl?: string;
62
+ }
63
+
64
+ export interface CreateInput {
65
+ filename: string;
66
+ body?: string;
67
+ title?: string;
68
+ }
69
+
70
+ export class Client {
71
+ token: string;
72
+ baseUrl: string;
73
+ constructor(options?: ClientOptions);
74
+ notes(): Promise<NoteSummary[]>;
75
+ note(filename: string): Promise<Note>;
76
+ create(input: CreateInput): Promise<Note>;
77
+ append(filename: string, text: string): Promise<true>;
78
+ }
79
+ }
package/index.js ADDED
@@ -0,0 +1,82 @@
1
+ // Tiny client for the Fresh Jots API (https://freshjots.com/docs).
2
+ //
3
+ // Usage:
4
+ //
5
+ // import { Client } from "freshjots";
6
+ // const client = new Client(); // reads FRESHJOTS_TOKEN from env
7
+ // await client.append("cron-jobs-prod", "backup ok");
8
+ // const note = await client.note("cron-jobs-prod");
9
+ // console.log(note.plain_body);
10
+ //
11
+ // Requires Node 18+ (uses global fetch). All methods throw ApiError on
12
+ // non-2xx responses, with the code/status/details from the API's stable
13
+ // error envelope.
14
+
15
+ export const VERSION = "0.1.0";
16
+ const DEFAULT_BASE_URL = "https://freshjots.com/api/v1";
17
+
18
+ export class ApiError extends Error {
19
+ constructor({ status, code, message, details }) {
20
+ super(message);
21
+ this.name = "ApiError";
22
+ this.status = status;
23
+ this.code = code;
24
+ this.details = details;
25
+ }
26
+ }
27
+
28
+ export class Client {
29
+ constructor({ token, baseUrl = DEFAULT_BASE_URL } = {}) {
30
+ this.token = token || process.env.FRESHJOTS_TOKEN;
31
+ if (!this.token) {
32
+ throw new Error("FRESHJOTS_TOKEN missing — pass {token} or set the env var");
33
+ }
34
+ this.baseUrl = baseUrl;
35
+ }
36
+
37
+ async notes() {
38
+ return (await this._request("GET", "/notes")).notes;
39
+ }
40
+
41
+ async note(filename) {
42
+ const path = `/notes/by-filename/${encodeURIComponent(filename)}`;
43
+ return (await this._request("GET", path)).note;
44
+ }
45
+
46
+ async create({ filename, body = "", title }) {
47
+ const note = { filename, plain_body: body, format: "plain" };
48
+ if (title) note.title = title;
49
+ return (await this._request("POST", "/notes", { note })).note;
50
+ }
51
+
52
+ async append(filename, text) {
53
+ const path = `/notes/by-filename/${encodeURIComponent(filename)}/append`;
54
+ await this._request("POST", path, { text });
55
+ return true;
56
+ }
57
+
58
+ async _request(method, path, body) {
59
+ const headers = { Authorization: `Bearer ${this.token}` };
60
+ if (body !== undefined) headers["Content-Type"] = "application/json";
61
+
62
+ const res = await fetch(`${this.baseUrl}${path}`, {
63
+ method,
64
+ headers,
65
+ body: body !== undefined ? JSON.stringify(body) : undefined,
66
+ });
67
+
68
+ const text = await res.text();
69
+ const data = text ? JSON.parse(text) : {};
70
+
71
+ if (!res.ok) {
72
+ const err = data.error || {};
73
+ throw new ApiError({
74
+ status: res.status,
75
+ code: err.code || "unknown",
76
+ message: err.message || "request failed",
77
+ details: err.details,
78
+ });
79
+ }
80
+ return data;
81
+ }
82
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "freshjots",
3
+ "version": "0.2.0",
4
+ "description": "Tiny JavaScript client for the Fresh Jots API. Append-only notebooks for cron jobs, deploy scripts, and bots.",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "default": "./index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "index.js",
16
+ "index.d.ts",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "scripts": {
24
+ "test": "node --test"
25
+ },
26
+ "keywords": [
27
+ "freshjots",
28
+ "notes",
29
+ "api",
30
+ "cli",
31
+ "cron",
32
+ "logging"
33
+ ],
34
+ "author": "Goran Arsov <arsphy@yahoo.com>",
35
+ "license": "MIT",
36
+ "homepage": "https://github.com/Goran-Arsov/freshjots-js#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/Goran-Arsov/freshjots-js/issues"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/Goran-Arsov/freshjots-js.git"
43
+ }
44
+ }