apostil 0.1.2 → 0.1.3

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 CHANGED
@@ -40,7 +40,7 @@ This will:
40
40
  npm run dev
41
41
  ```
42
42
 
43
- Press `C` on any page to start commenting. That's it.
43
+ Press `C` on any page to start commenting. On your first comment, you'll be prompted to enter your name — this is stored locally and used for all future comments. Click anywhere to place a pin, type your comment, and press `Enter` to save.
44
44
 
45
45
  ## How It Works
46
46
 
@@ -53,7 +53,13 @@ Comments are stored as JSON files in `.apostil/`:
53
53
  └── dashboard--settings.json
54
54
  ```
55
55
 
56
- The wrapper auto-detects the current page from `usePathname()` and loads the corresponding comments. Every page in your app gets commenting automatically.
56
+ The wrapper auto-detects the current page from `usePathname()` and loads the corresponding comments. Every page in your app gets commenting automatically. Comments persist across page refreshes and dev server restarts.
57
+
58
+ Click a pin to open its thread — you can reply to existing comments or resolve the thread. Resolved threads stay accessible but are visually distinguished.
59
+
60
+ ### Shareable Links
61
+
62
+ Apostil supports hash-based thread links. Append `#apostil-<threadId>` to any URL to deep-link directly to a comment thread. The sidebar's **All Pages** view uses this to navigate across pages and open the target thread automatically.
57
63
 
58
64
  ## Modes
59
65
 
@@ -78,7 +84,7 @@ npm uninstall apostil
78
84
 
79
85
  | Key | Action |
80
86
  |-----|--------|
81
- | `C` | Toggle comment mode |
87
+ | `C` | Toggle comment mode (modifier keys like `Cmd+C` / `Ctrl+C` are not intercepted) |
82
88
  | `Escape` | Cancel unsaved comment / exit comment mode |
83
89
  | `Enter` | Submit comment |
84
90
 
package/bin/apostil.js CHANGED
File without changes
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/adapters/localStorage.ts
21
+ var localStorage_exports = {};
22
+ __export(localStorage_exports, {
23
+ localStorageAdapter: () => localStorageAdapter
24
+ });
25
+ module.exports = __toCommonJS(localStorage_exports);
26
+ var STORAGE_PREFIX = "apostil-";
27
+ var localStorageAdapter = {
28
+ async load(pageId) {
29
+ if (typeof window === "undefined") return [];
30
+ try {
31
+ const raw = localStorage.getItem(`${STORAGE_PREFIX}${pageId}`);
32
+ return raw ? JSON.parse(raw) : [];
33
+ } catch {
34
+ return [];
35
+ }
36
+ },
37
+ async save(pageId, threads) {
38
+ if (typeof window === "undefined") return;
39
+ localStorage.setItem(`${STORAGE_PREFIX}${pageId}`, JSON.stringify(threads));
40
+ }
41
+ };
42
+ // Annotate the CommonJS export names for ESM import in node:
43
+ 0 && (module.exports = {
44
+ localStorageAdapter
45
+ });
46
+ //# sourceMappingURL=localStorage.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/localStorage.ts"],"sourcesContent":["import type { ApostilStorage, ApostilThread } from \"../types\";\n\nconst STORAGE_PREFIX = \"apostil-\";\n\nexport const localStorageAdapter: ApostilStorage = {\n async load(pageId: string): Promise<ApostilThread[]> {\n if (typeof window === \"undefined\") return [];\n try {\n const raw = localStorage.getItem(`${STORAGE_PREFIX}${pageId}`);\n return raw ? JSON.parse(raw) : [];\n } catch {\n return [];\n }\n },\n\n async save(pageId: string, threads: ApostilThread[]): Promise<void> {\n if (typeof window === \"undefined\") return;\n localStorage.setItem(`${STORAGE_PREFIX}${pageId}`, JSON.stringify(threads));\n },\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,IAAM,iBAAiB;AAEhB,IAAM,sBAAsC;AAAA,EACjD,MAAM,KAAK,QAA0C;AACnD,QAAI,OAAO,WAAW,YAAa,QAAO,CAAC;AAC3C,QAAI;AACF,YAAM,MAAM,aAAa,QAAQ,GAAG,cAAc,GAAG,MAAM,EAAE;AAC7D,aAAO,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAAA,IAClC,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,QAAgB,SAAyC;AAClE,QAAI,OAAO,WAAW,YAAa;AACnC,iBAAa,QAAQ,GAAG,cAAc,GAAG,MAAM,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,EAC5E;AACF;","names":[]}
@@ -0,0 +1,5 @@
1
+ import { A as ApostilStorage } from '../types-oQRt3lYH.cjs';
2
+
3
+ declare const localStorageAdapter: ApostilStorage;
4
+
5
+ export { localStorageAdapter };
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/adapters/nextjs.ts
31
+ var nextjs_exports = {};
32
+ __export(nextjs_exports, {
33
+ GET: () => GET,
34
+ POST: () => POST,
35
+ createNextjsHandler: () => createNextjsHandler
36
+ });
37
+ module.exports = __toCommonJS(nextjs_exports);
38
+ function createNextjsHandler(directory = ".apostil") {
39
+ return {
40
+ async GET(request) {
41
+ const { promises: fs } = await import("fs");
42
+ const path = await import("path");
43
+ const url = new URL(request.url);
44
+ const pageId = url.searchParams.get("pageId");
45
+ const dir = path.join(process.cwd(), directory);
46
+ if (!pageId) {
47
+ try {
48
+ const files = await fs.readdir(dir);
49
+ const pages = [];
50
+ for (const file2 of files) {
51
+ if (!file2.endsWith(".json")) continue;
52
+ const id = file2.replace(".json", "");
53
+ try {
54
+ const data = await fs.readFile(path.join(dir, file2), "utf-8");
55
+ const threads = JSON.parse(data);
56
+ if (Array.isArray(threads) && threads.length > 0) {
57
+ pages.push({ pageId: id, threads });
58
+ }
59
+ } catch {
60
+ }
61
+ }
62
+ pages.sort((a, b) => {
63
+ const aLatest = Math.max(...a.threads.map((t) => new Date(t.createdAt).getTime()));
64
+ const bLatest = Math.max(...b.threads.map((t) => new Date(t.createdAt).getTime()));
65
+ return bLatest - aLatest;
66
+ });
67
+ return Response.json(pages);
68
+ } catch {
69
+ return Response.json([]);
70
+ }
71
+ }
72
+ const safeName = pageId.replace(/[^a-zA-Z0-9_-]/g, "");
73
+ const file = path.join(dir, `${safeName}.json`);
74
+ try {
75
+ const data = await fs.readFile(file, "utf-8");
76
+ return Response.json(JSON.parse(data));
77
+ } catch {
78
+ return Response.json([]);
79
+ }
80
+ },
81
+ async POST(request) {
82
+ const { promises: fs } = await import("fs");
83
+ const path = await import("path");
84
+ const url = new URL(request.url);
85
+ const pageId = url.searchParams.get("pageId");
86
+ if (!pageId) {
87
+ return Response.json({ error: "Missing pageId" }, { status: 400 });
88
+ }
89
+ const dir = path.join(process.cwd(), directory);
90
+ const safeName = pageId.replace(/[^a-zA-Z0-9_-]/g, "");
91
+ const file = path.join(dir, `${safeName}.json`);
92
+ try {
93
+ await fs.mkdir(dir, { recursive: true });
94
+ const threads = await request.json();
95
+ await fs.writeFile(file, JSON.stringify(threads, null, 2), "utf-8");
96
+ return Response.json({ ok: true });
97
+ } catch (e) {
98
+ return Response.json({ error: String(e) }, { status: 500 });
99
+ }
100
+ }
101
+ };
102
+ }
103
+ var defaultHandler = createNextjsHandler();
104
+ var GET = defaultHandler.GET;
105
+ var POST = defaultHandler.POST;
106
+ // Annotate the CommonJS export names for ESM import in node:
107
+ 0 && (module.exports = {
108
+ GET,
109
+ POST,
110
+ createNextjsHandler
111
+ });
112
+ //# sourceMappingURL=nextjs.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/nextjs.ts"],"sourcesContent":["/**\n * Next.js API route handler for apostil comment storage.\n * Stores comments as JSON files in the project's .apostil/ directory.\n *\n * Usage:\n *\n * // app/api/apostil/route.ts\n * export { GET, POST } from \"apostil/adapters/nextjs\";\n *\n * Or with custom directory:\n * import { createNextjsHandler } from \"apostil/adapters/nextjs\";\n * const { GET, POST } = createNextjsHandler(\".my-comments\");\n * export { GET, POST };\n */\n\nexport function createNextjsHandler(directory: string = \".apostil\") {\n return {\n async GET(request: Request) {\n const { promises: fs } = await import(\"fs\");\n const path = await import(\"path\");\n\n const url = new URL(request.url);\n const pageId = url.searchParams.get(\"pageId\");\n const dir = path.join(process.cwd(), directory);\n\n // If no pageId, return ALL comments across all pages\n if (!pageId) {\n try {\n const files = await fs.readdir(dir);\n const pages: { pageId: string; threads: unknown[] }[] = [];\n for (const file of files) {\n if (!file.endsWith(\".json\")) continue;\n const id = file.replace(\".json\", \"\");\n try {\n const data = await fs.readFile(path.join(dir, file), \"utf-8\");\n const threads = JSON.parse(data);\n if (Array.isArray(threads) && threads.length > 0) {\n pages.push({ pageId: id, threads });\n }\n } catch {}\n }\n pages.sort((a, b) => {\n const aLatest = Math.max(...(a.threads as Array<{ createdAt: string }>).map((t) => new Date(t.createdAt).getTime()));\n const bLatest = Math.max(...(b.threads as Array<{ createdAt: string }>).map((t) => new Date(t.createdAt).getTime()));\n return bLatest - aLatest;\n });\n return Response.json(pages);\n } catch {\n return Response.json([]);\n }\n }\n\n // Single page\n const safeName = pageId.replace(/[^a-zA-Z0-9_-]/g, \"\");\n const file = path.join(dir, `${safeName}.json`);\n try {\n const data = await fs.readFile(file, \"utf-8\");\n return Response.json(JSON.parse(data));\n } catch {\n return Response.json([]);\n }\n },\n\n async POST(request: Request) {\n const { promises: fs } = await import(\"fs\");\n const path = await import(\"path\");\n\n const url = new URL(request.url);\n const pageId = url.searchParams.get(\"pageId\");\n if (!pageId) {\n return Response.json({ error: \"Missing pageId\" }, { status: 400 });\n }\n\n const dir = path.join(process.cwd(), directory);\n const safeName = pageId.replace(/[^a-zA-Z0-9_-]/g, \"\");\n const file = path.join(dir, `${safeName}.json`);\n\n try {\n await fs.mkdir(dir, { recursive: true });\n const threads = await request.json();\n await fs.writeFile(file, JSON.stringify(threads, null, 2), \"utf-8\");\n return Response.json({ ok: true });\n } catch (e) {\n return Response.json({ error: String(e) }, { status: 500 });\n }\n },\n };\n}\n\n// Default handler with \".apostil\" directory\nconst defaultHandler = createNextjsHandler();\nexport const GET = defaultHandler.GET;\nexport const POST = defaultHandler.POST;\n"],"mappings":"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","names":["file"]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Next.js API route handler for apostil comment storage.
3
+ * Stores comments as JSON files in the project's .apostil/ directory.
4
+ *
5
+ * Usage:
6
+ *
7
+ * // app/api/apostil/route.ts
8
+ * export { GET, POST } from "apostil/adapters/nextjs";
9
+ *
10
+ * Or with custom directory:
11
+ * import { createNextjsHandler } from "apostil/adapters/nextjs";
12
+ * const { GET, POST } = createNextjsHandler(".my-comments");
13
+ * export { GET, POST };
14
+ */
15
+ declare function createNextjsHandler(directory?: string): {
16
+ GET(request: Request): Promise<Response>;
17
+ POST(request: Request): Promise<Response>;
18
+ };
19
+ declare const GET: (request: Request) => Promise<Response>;
20
+ declare const POST: (request: Request) => Promise<Response>;
21
+
22
+ export { GET, POST, createNextjsHandler };
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/adapters/rest.ts
21
+ var rest_exports = {};
22
+ __export(rest_exports, {
23
+ createRestAdapter: () => createRestAdapter
24
+ });
25
+ module.exports = __toCommonJS(rest_exports);
26
+ function createRestAdapter(baseUrl) {
27
+ return {
28
+ async load(pageId) {
29
+ const url = `${baseUrl}?pageId=${encodeURIComponent(pageId)}`;
30
+ try {
31
+ const res = await fetch(url);
32
+ if (!res.ok) {
33
+ console.warn(`[apostil] load failed: ${res.status} ${res.statusText} \u2014 ${url}`);
34
+ return [];
35
+ }
36
+ const data = await res.json();
37
+ return data;
38
+ } catch (e) {
39
+ console.warn(`[apostil] load error:`, e, `\u2014 ${url}`);
40
+ return [];
41
+ }
42
+ },
43
+ async save(pageId, threads) {
44
+ const url = `${baseUrl}?pageId=${encodeURIComponent(pageId)}`;
45
+ try {
46
+ const res = await fetch(url, {
47
+ method: "POST",
48
+ headers: { "Content-Type": "application/json" },
49
+ body: JSON.stringify(threads)
50
+ });
51
+ if (!res.ok) {
52
+ console.warn(`[apostil] save failed: ${res.status} ${res.statusText} \u2014 ${url}`);
53
+ }
54
+ } catch (e) {
55
+ console.warn(`[apostil] save error:`, e, `\u2014 ${url}`);
56
+ }
57
+ }
58
+ };
59
+ }
60
+ // Annotate the CommonJS export names for ESM import in node:
61
+ 0 && (module.exports = {
62
+ createRestAdapter
63
+ });
64
+ //# sourceMappingURL=rest.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/rest.ts"],"sourcesContent":["import type { ApostilStorage, ApostilThread } from \"../types\";\n\n/**\n * REST API storage adapter.\n * Works with any backend that implements GET/POST for threads.\n */\nexport function createRestAdapter(baseUrl: string): ApostilStorage {\n return {\n async load(pageId: string): Promise<ApostilThread[]> {\n const url = `${baseUrl}?pageId=${encodeURIComponent(pageId)}`;\n try {\n const res = await fetch(url);\n if (!res.ok) {\n console.warn(`[apostil] load failed: ${res.status} ${res.statusText} — ${url}`);\n return [];\n }\n const data = await res.json();\n return data;\n } catch (e) {\n console.warn(`[apostil] load error:`, e, `— ${url}`);\n return [];\n }\n },\n\n async save(pageId: string, threads: ApostilThread[]): Promise<void> {\n const url = `${baseUrl}?pageId=${encodeURIComponent(pageId)}`;\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(threads),\n });\n if (!res.ok) {\n console.warn(`[apostil] save failed: ${res.status} ${res.statusText} — ${url}`);\n }\n } catch (e) {\n console.warn(`[apostil] save error:`, e, `— ${url}`);\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,SAAS,kBAAkB,SAAiC;AACjE,SAAO;AAAA,IACL,MAAM,KAAK,QAA0C;AACnD,YAAM,MAAM,GAAG,OAAO,WAAW,mBAAmB,MAAM,CAAC;AAC3D,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,YAAI,CAAC,IAAI,IAAI;AACX,kBAAQ,KAAK,0BAA0B,IAAI,MAAM,IAAI,IAAI,UAAU,WAAM,GAAG,EAAE;AAC9E,iBAAO,CAAC;AAAA,QACV;AACA,cAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,eAAO;AAAA,MACT,SAAS,GAAG;AACV,gBAAQ,KAAK,yBAAyB,GAAG,UAAK,GAAG,EAAE;AACnD,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,IAEA,MAAM,KAAK,QAAgB,SAAyC;AAClE,YAAM,MAAM,GAAG,OAAO,WAAW,mBAAmB,MAAM,CAAC;AAC3D,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,KAAK;AAAA,UAC3B,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC9B,CAAC;AACD,YAAI,CAAC,IAAI,IAAI;AACX,kBAAQ,KAAK,0BAA0B,IAAI,MAAM,IAAI,IAAI,UAAU,WAAM,GAAG,EAAE;AAAA,QAChF;AAAA,MACF,SAAS,GAAG;AACV,gBAAQ,KAAK,yBAAyB,GAAG,UAAK,GAAG,EAAE;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,9 @@
1
+ import { A as ApostilStorage } from '../types-oQRt3lYH.cjs';
2
+
3
+ /**
4
+ * REST API storage adapter.
5
+ * Works with any backend that implements GET/POST for threads.
6
+ */
7
+ declare function createRestAdapter(baseUrl: string): ApostilStorage;
8
+
9
+ export { createRestAdapter };
@@ -0,0 +1,293 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli/index.ts
27
+ var import_promises = __toESM(require("fs/promises"), 1);
28
+ var import_path = __toESM(require("path"), 1);
29
+ var args = process.argv.slice(2);
30
+ var command = args[0];
31
+ if (command === "init") {
32
+ const mode = parseMode(args.slice(1));
33
+ init(mode);
34
+ } else if (command === "remove") {
35
+ remove();
36
+ } else if (command === "help" || command === "--help" || command === "-h" || !command) {
37
+ printHelp();
38
+ } else {
39
+ console.log(` Unknown command: ${command}
40
+ `);
41
+ printHelp();
42
+ }
43
+ function parseMode(flags) {
44
+ if (flags.includes("--dev")) return "dev";
45
+ if (flags.includes("--public")) return "public";
46
+ return "personal";
47
+ }
48
+ function printHelp() {
49
+ console.log(`
50
+ apostil \u2014 Pin-and-comment feedback for React & Next.js
51
+
52
+ Usage:
53
+ npx apostil init [mode] Set up apostil in your Next.js project
54
+ npx apostil remove Remove apostil from your project
55
+ npx apostil help Show this help
56
+
57
+ Modes:
58
+ (default) Personal \u2014 local dev only, comments gitignored
59
+ --dev Dev + staging \u2014 comments gitignored, env-controlled
60
+ --public All environments \u2014 comments committed to git
61
+ `);
62
+ }
63
+ async function init(mode) {
64
+ const cwd = process.cwd();
65
+ const appDir = await findAppDir(cwd);
66
+ if (!appDir) {
67
+ console.log(" Could not find a Next.js app/ directory.");
68
+ console.log(" Make sure you're running this from your project root.\n");
69
+ process.exit(1);
70
+ }
71
+ const useSrc = appDir.includes("src/app");
72
+ const modeLabel = mode === "personal" ? "personal" : mode === "dev" ? "dev" : "public";
73
+ console.log(`
74
+ Setting up apostil (${modeLabel} mode)...
75
+ `);
76
+ const apiDir = import_path.default.join(appDir, "api", "apostil");
77
+ const apiFile = import_path.default.join(apiDir, "route.ts");
78
+ if (await fileExists(apiFile)) {
79
+ console.log(" \u2713 API route already exists");
80
+ } else {
81
+ await import_promises.default.mkdir(apiDir, { recursive: true });
82
+ await import_promises.default.writeFile(
83
+ apiFile,
84
+ `export { GET, POST } from "apostil/adapters/nextjs";
85
+ `,
86
+ "utf-8"
87
+ );
88
+ console.log(` \u2713 Created ${rel(cwd, apiFile)}`);
89
+ }
90
+ const componentsDir = import_path.default.join(cwd, useSrc ? "src/components" : "components");
91
+ const wrapperFile = import_path.default.join(componentsDir, "apostil-wrapper.tsx");
92
+ await import_promises.default.mkdir(componentsDir, { recursive: true });
93
+ await import_promises.default.writeFile(wrapperFile, getWrapperComponent(mode), "utf-8");
94
+ console.log(` \u2713 Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);
95
+ const commentsDir = import_path.default.join(cwd, ".apostil");
96
+ await import_promises.default.mkdir(commentsDir, { recursive: true });
97
+ console.log(" \u2713 Created .apostil/ directory");
98
+ const gitignorePath = import_path.default.join(cwd, ".gitignore");
99
+ let gitignore = "";
100
+ try {
101
+ gitignore = await import_promises.default.readFile(gitignorePath, "utf-8");
102
+ } catch {
103
+ }
104
+ if (mode === "public") {
105
+ if (gitignore.includes(".apostil")) {
106
+ console.log(" \u2713 .gitignore unchanged (public mode \u2014 comments will be committed)");
107
+ } else {
108
+ console.log(" \u2713 Comments will be committed to git (public mode)");
109
+ }
110
+ } else {
111
+ if (!gitignore.includes(".apostil")) {
112
+ const entry = "\n# Apostil comments\n.apostil/\n";
113
+ await import_promises.default.appendFile(gitignorePath, entry, "utf-8");
114
+ console.log(" \u2713 Added .apostil/ to .gitignore");
115
+ } else {
116
+ console.log(" \u2713 .gitignore already configured");
117
+ }
118
+ }
119
+ const layoutInjected = await injectIntoLayout(appDir, useSrc);
120
+ if (layoutInjected) {
121
+ console.log(" \u2713 Added <ApostilWrapper> to root layout");
122
+ }
123
+ console.log("\n Done! Run your dev server and press C to start commenting.\n");
124
+ }
125
+ async function remove() {
126
+ const cwd = process.cwd();
127
+ const appDir = await findAppDir(cwd);
128
+ const useSrc = appDir?.includes("src/app") ?? false;
129
+ console.log("\n Removing apostil...\n");
130
+ if (appDir) {
131
+ const apiDir = import_path.default.join(appDir, "api", "apostil");
132
+ if (await fileExists(import_path.default.join(apiDir, "route.ts"))) {
133
+ await import_promises.default.rm(apiDir, { recursive: true });
134
+ console.log(" \u2713 Removed API route");
135
+ }
136
+ }
137
+ const componentsDir = import_path.default.join(cwd, useSrc ? "src/components" : "components");
138
+ const wrapperFile = import_path.default.join(componentsDir, "apostil-wrapper.tsx");
139
+ if (await fileExists(wrapperFile)) {
140
+ await import_promises.default.rm(wrapperFile);
141
+ console.log(" \u2713 Removed wrapper component");
142
+ }
143
+ if (appDir) {
144
+ const unwrapped = await removeFromLayout(appDir);
145
+ if (unwrapped) {
146
+ console.log(" \u2713 Removed <ApostilWrapper> from root layout");
147
+ }
148
+ }
149
+ const commentsDir = import_path.default.join(cwd, ".apostil");
150
+ if (await fileExists(commentsDir)) {
151
+ await import_promises.default.rm(commentsDir, { recursive: true });
152
+ console.log(" \u2713 Removed .apostil/ directory");
153
+ }
154
+ const gitignorePath = import_path.default.join(cwd, ".gitignore");
155
+ try {
156
+ let gitignore = await import_promises.default.readFile(gitignorePath, "utf-8");
157
+ gitignore = gitignore.replace(/\n?# Apostil comments\n\.apostil\/\n?/g, "");
158
+ await import_promises.default.writeFile(gitignorePath, gitignore, "utf-8");
159
+ console.log(" \u2713 Cleaned .gitignore");
160
+ } catch {
161
+ }
162
+ console.log(`
163
+ Done! Now run: npm uninstall apostil
164
+ `);
165
+ }
166
+ function getWrapperComponent(mode) {
167
+ const envGuard = mode === "personal" ? `
168
+ // Personal mode \u2014 only active in local development
169
+ if (process.env.NODE_ENV !== "development") {
170
+ return <>{children}</>;
171
+ }
172
+ ` : mode === "dev" ? `
173
+ // Dev mode \u2014 active in dev + staging, disabled in production
174
+ // Set NEXT_PUBLIC_APOSTIL=true to force on in any environment
175
+ const forceOn = process.env.NEXT_PUBLIC_APOSTIL === "true";
176
+ if (process.env.NODE_ENV === "production" && !forceOn) {
177
+ return <>{children}</>;
178
+ }
179
+ ` : `
180
+ // Public mode \u2014 active in all environments
181
+ // Set NEXT_PUBLIC_APOSTIL=false to disable
182
+ if (process.env.NEXT_PUBLIC_APOSTIL === "false") {
183
+ return <>{children}</>;
184
+ }
185
+ `;
186
+ return `"use client";
187
+
188
+ import { usePathname } from "next/navigation";
189
+ import {
190
+ ApostilProvider,
191
+ CommentOverlay,
192
+ CommentToggle,
193
+ CommentSidebar,
194
+ } from "apostil";
195
+
196
+ export function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}
197
+ const pathname = usePathname();
198
+ const pageId = pathname.replace(/\\//g, "--").replace(/^--/, "") || "home";
199
+
200
+ return (
201
+ <ApostilProvider pageId={pageId}>
202
+ {children}
203
+ <CommentOverlay />
204
+ <CommentSidebar />
205
+ <CommentToggle />
206
+ </ApostilProvider>
207
+ );
208
+ }
209
+ `;
210
+ }
211
+ async function injectIntoLayout(appDir, useSrc) {
212
+ const layoutPath = await findLayout(appDir);
213
+ if (!layoutPath) return false;
214
+ let content = await import_promises.default.readFile(layoutPath, "utf-8");
215
+ if (content.includes("ApostilWrapper")) {
216
+ console.log(" \u2713 Layout already has <ApostilWrapper>");
217
+ return false;
218
+ }
219
+ const importPath = useSrc ? "@/components/apostil-wrapper" : "../components/apostil-wrapper";
220
+ const importLine = `import { ApostilWrapper } from "${importPath}";
221
+ `;
222
+ const importRegex = /^import\s.+$/gm;
223
+ let lastImportIndex = 0;
224
+ let match;
225
+ while ((match = importRegex.exec(content)) !== null) {
226
+ lastImportIndex = match.index + match[0].length;
227
+ }
228
+ if (lastImportIndex > 0) {
229
+ content = content.slice(0, lastImportIndex) + "\n" + importLine + content.slice(lastImportIndex);
230
+ } else {
231
+ const useDirective = content.match(/^["']use (client|server)["'];?\n/);
232
+ const insertAt = useDirective ? useDirective[0].length : 0;
233
+ content = content.slice(0, insertAt) + importLine + content.slice(insertAt);
234
+ }
235
+ const bodyChildrenRegex = /(<body[^>]*>)([\s\S]*?)(\{[\s]*children[\s]*\})([\s\S]*?)(<\/body>)/;
236
+ const bodyMatch = content.match(bodyChildrenRegex);
237
+ if (bodyMatch) {
238
+ content = content.replace(
239
+ bodyChildrenRegex,
240
+ `$1$2<ApostilWrapper>$3</ApostilWrapper>$4$5`
241
+ );
242
+ } else {
243
+ const childrenRegex = /(\{[\s]*children[\s]*\})/;
244
+ if (childrenRegex.test(content)) {
245
+ content = content.replace(childrenRegex, `<ApostilWrapper>$1</ApostilWrapper>`);
246
+ } else {
247
+ console.log(" \u26A0 Could not find {children} in layout \u2014 add <ApostilWrapper> manually");
248
+ return false;
249
+ }
250
+ }
251
+ await import_promises.default.writeFile(layoutPath, content, "utf-8");
252
+ return true;
253
+ }
254
+ async function removeFromLayout(appDir) {
255
+ const layoutPath = await findLayout(appDir);
256
+ if (!layoutPath) return false;
257
+ let content = await import_promises.default.readFile(layoutPath, "utf-8");
258
+ if (!content.includes("ApostilWrapper")) return false;
259
+ content = content.replace(/import\s*\{[^}]*ApostilWrapper[^}]*\}\s*from\s*["'][^"']+["'];?\n?/g, "");
260
+ content = content.replace(/<ApostilWrapper>([\s\S]*?)<\/ApostilWrapper>/g, "$1");
261
+ await import_promises.default.writeFile(layoutPath, content, "utf-8");
262
+ return true;
263
+ }
264
+ async function findAppDir(cwd) {
265
+ for (const candidate of ["src/app", "app"]) {
266
+ const dir = import_path.default.join(cwd, candidate);
267
+ try {
268
+ const stat = await import_promises.default.stat(dir);
269
+ if (stat.isDirectory()) return dir;
270
+ } catch {
271
+ }
272
+ }
273
+ return null;
274
+ }
275
+ async function findLayout(appDir) {
276
+ for (const ext of ["tsx", "jsx", "ts", "js"]) {
277
+ const file = import_path.default.join(appDir, `layout.${ext}`);
278
+ if (await fileExists(file)) return file;
279
+ }
280
+ return null;
281
+ }
282
+ async function fileExists(p) {
283
+ try {
284
+ await import_promises.default.stat(p);
285
+ return true;
286
+ } catch {
287
+ return false;
288
+ }
289
+ }
290
+ function rel(cwd, filePath) {
291
+ return import_path.default.relative(cwd, filePath);
292
+ }
293
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\n\ntype Mode = \"personal\" | \"dev\" | \"public\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nif (command === \"init\") {\n const mode = parseMode(args.slice(1));\n init(mode);\n} else if (command === \"remove\") {\n remove();\n} else if (command === \"help\" || command === \"--help\" || command === \"-h\" || !command) {\n printHelp();\n} else {\n console.log(` Unknown command: ${command}\\n`);\n printHelp();\n}\n\nfunction parseMode(flags: string[]): Mode {\n if (flags.includes(\"--dev\")) return \"dev\";\n if (flags.includes(\"--public\")) return \"public\";\n return \"personal\";\n}\n\nfunction printHelp() {\n console.log(`\n apostil — Pin-and-comment feedback for React & Next.js\n\n Usage:\n npx apostil init [mode] Set up apostil in your Next.js project\n npx apostil remove Remove apostil from your project\n npx apostil help Show this help\n\n Modes:\n (default) Personal — local dev only, comments gitignored\n --dev Dev + staging — comments gitignored, env-controlled\n --public All environments — comments committed to git\n`);\n}\n\nasync function init(mode: Mode) {\n const cwd = process.cwd();\n\n // Detect Next.js app directory\n const appDir = await findAppDir(cwd);\n if (!appDir) {\n console.log(\" Could not find a Next.js app/ directory.\");\n console.log(\" Make sure you're running this from your project root.\\n\");\n process.exit(1);\n }\n\n // Detect src/ prefix\n const useSrc = appDir.includes(\"src/app\");\n\n const modeLabel = mode === \"personal\" ? \"personal\" : mode === \"dev\" ? \"dev\" : \"public\";\n console.log(`\\n Setting up apostil (${modeLabel} mode)...\\n`);\n\n // 1. Create API route\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n const apiFile = path.join(apiDir, \"route.ts\");\n if (await fileExists(apiFile)) {\n console.log(\" ✓ API route already exists\");\n } else {\n await fs.mkdir(apiDir, { recursive: true });\n await fs.writeFile(\n apiFile,\n `export { GET, POST } from \"apostil/adapters/nextjs\";\\n`,\n \"utf-8\"\n );\n console.log(` ✓ Created ${rel(cwd, apiFile)}`);\n }\n\n // 2. Create wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n await fs.mkdir(componentsDir, { recursive: true });\n await fs.writeFile(wrapperFile, getWrapperComponent(mode), \"utf-8\");\n console.log(` ✓ Created ${rel(cwd, wrapperFile)} (${modeLabel} mode)`);\n\n // 3. Create .apostil/ directory for comment storage\n const commentsDir = path.join(cwd, \".apostil\");\n await fs.mkdir(commentsDir, { recursive: true });\n console.log(\" ✓ Created .apostil/ directory\");\n\n // 4. Handle .gitignore based on mode\n const gitignorePath = path.join(cwd, \".gitignore\");\n let gitignore = \"\";\n try {\n gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n } catch {}\n\n if (mode === \"public\") {\n // Public mode: don't gitignore comments — they get committed\n if (gitignore.includes(\".apostil\")) {\n console.log(\" ✓ .gitignore unchanged (public mode — comments will be committed)\");\n } else {\n console.log(\" ✓ Comments will be committed to git (public mode)\");\n }\n } else {\n // Personal & dev: gitignore comments\n if (!gitignore.includes(\".apostil\")) {\n const entry = \"\\n# Apostil comments\\n.apostil/\\n\";\n await fs.appendFile(gitignorePath, entry, \"utf-8\");\n console.log(\" ✓ Added .apostil/ to .gitignore\");\n } else {\n console.log(\" ✓ .gitignore already configured\");\n }\n }\n\n // 5. Inject wrapper into root layout\n const layoutInjected = await injectIntoLayout(appDir, useSrc);\n if (layoutInjected) {\n console.log(\" ✓ Added <ApostilWrapper> to root layout\");\n }\n\n console.log(\"\\n Done! Run your dev server and press C to start commenting.\\n\");\n}\n\nasync function remove() {\n const cwd = process.cwd();\n const appDir = await findAppDir(cwd);\n const useSrc = appDir?.includes(\"src/app\") ?? false;\n\n console.log(\"\\n Removing apostil...\\n\");\n\n // 1. Remove API route\n if (appDir) {\n const apiDir = path.join(appDir, \"api\", \"apostil\");\n if (await fileExists(path.join(apiDir, \"route.ts\"))) {\n await fs.rm(apiDir, { recursive: true });\n console.log(\" ✓ Removed API route\");\n }\n }\n\n // 2. Remove wrapper component\n const componentsDir = path.join(cwd, useSrc ? \"src/components\" : \"components\");\n const wrapperFile = path.join(componentsDir, \"apostil-wrapper.tsx\");\n if (await fileExists(wrapperFile)) {\n await fs.rm(wrapperFile);\n console.log(\" ✓ Removed wrapper component\");\n }\n\n // 3. Remove wrapper from layout\n if (appDir) {\n const unwrapped = await removeFromLayout(appDir);\n if (unwrapped) {\n console.log(\" ✓ Removed <ApostilWrapper> from root layout\");\n }\n }\n\n // 4. Remove .apostil/ directory\n const commentsDir = path.join(cwd, \".apostil\");\n if (await fileExists(commentsDir)) {\n await fs.rm(commentsDir, { recursive: true });\n console.log(\" ✓ Removed .apostil/ directory\");\n }\n\n // 5. Remove from .gitignore\n const gitignorePath = path.join(cwd, \".gitignore\");\n try {\n let gitignore = await fs.readFile(gitignorePath, \"utf-8\");\n gitignore = gitignore.replace(/\\n?# Apostil comments\\n\\.apostil\\/\\n?/g, \"\");\n await fs.writeFile(gitignorePath, gitignore, \"utf-8\");\n console.log(\" ✓ Cleaned .gitignore\");\n } catch {}\n\n console.log(`\n Done! Now run: npm uninstall apostil\n`);\n}\n\n// --- Wrapper component template ---\n\nfunction getWrapperComponent(mode: Mode): string {\n const envGuard = mode === \"personal\"\n ? `\n // Personal mode — only active in local development\n if (process.env.NODE_ENV !== \"development\") {\n return <>{children}</>;\n }\n`\n : mode === \"dev\"\n ? `\n // Dev mode — active in dev + staging, disabled in production\n // Set NEXT_PUBLIC_APOSTIL=true to force on in any environment\n const forceOn = process.env.NEXT_PUBLIC_APOSTIL === \"true\";\n if (process.env.NODE_ENV === \"production\" && !forceOn) {\n return <>{children}</>;\n }\n`\n : `\n // Public mode — active in all environments\n // Set NEXT_PUBLIC_APOSTIL=false to disable\n if (process.env.NEXT_PUBLIC_APOSTIL === \"false\") {\n return <>{children}</>;\n }\n`;\n\n return `\"use client\";\n\nimport { usePathname } from \"next/navigation\";\nimport {\n ApostilProvider,\n CommentOverlay,\n CommentToggle,\n CommentSidebar,\n} from \"apostil\";\n\nexport function ApostilWrapper({ children }: { children: React.ReactNode }) {${envGuard}\n const pathname = usePathname();\n const pageId = pathname.replace(/\\\\//g, \"--\").replace(/^--/, \"\") || \"home\";\n\n return (\n <ApostilProvider pageId={pageId}>\n {children}\n <CommentOverlay />\n <CommentSidebar />\n <CommentToggle />\n </ApostilProvider>\n );\n}\n`;\n}\n\n// --- Layout injection ---\n\nasync function injectIntoLayout(appDir: string, useSrc: boolean): Promise<boolean> {\n const layoutPath = await findLayout(appDir);\n if (!layoutPath) return false;\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n // Skip if already injected\n if (content.includes(\"ApostilWrapper\")) {\n console.log(\" ✓ Layout already has <ApostilWrapper>\");\n return false;\n }\n\n // Add import at the top (after existing imports or \"use\" directives)\n const importPath = useSrc ? \"@/components/apostil-wrapper\" : \"../components/apostil-wrapper\";\n const importLine = `import { ApostilWrapper } from \"${importPath}\";\\n`;\n\n // Find the last import statement and insert after it\n const importRegex = /^import\\s.+$/gm;\n let lastImportIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(content)) !== null) {\n lastImportIndex = match.index + match[0].length;\n }\n\n if (lastImportIndex > 0) {\n content = content.slice(0, lastImportIndex) + \"\\n\" + importLine + content.slice(lastImportIndex);\n } else {\n // No imports found, add at the top (after \"use client\" or \"use server\" if present)\n const useDirective = content.match(/^[\"']use (client|server)[\"'];?\\n/);\n const insertAt = useDirective ? useDirective[0].length : 0;\n content = content.slice(0, insertAt) + importLine + content.slice(insertAt);\n }\n\n // Wrap {children} with <ApostilWrapper>\n // Handle both {children} and { children } patterns inside <body>\n const bodyChildrenRegex = /(<body[^>]*>)([\\s\\S]*?)(\\{[\\s]*children[\\s]*\\})([\\s\\S]*?)(<\\/body>)/;\n const bodyMatch = content.match(bodyChildrenRegex);\n\n if (bodyMatch) {\n content = content.replace(\n bodyChildrenRegex,\n `$1$2<ApostilWrapper>$3</ApostilWrapper>$4$5`\n );\n } else {\n // Fallback: try to find {children} anywhere and wrap it\n const childrenRegex = /(\\{[\\s]*children[\\s]*\\})/;\n if (childrenRegex.test(content)) {\n content = content.replace(childrenRegex, `<ApostilWrapper>$1</ApostilWrapper>`);\n } else {\n console.log(\" ⚠ Could not find {children} in layout — add <ApostilWrapper> manually\");\n return false;\n }\n }\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n return true;\n}\n\nasync function removeFromLayout(appDir: string): Promise<boolean> {\n const layoutPath = await findLayout(appDir);\n if (!layoutPath) return false;\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n if (!content.includes(\"ApostilWrapper\")) return false;\n\n // Remove import line\n content = content.replace(/import\\s*\\{[^}]*ApostilWrapper[^}]*\\}\\s*from\\s*[\"'][^\"']+[\"'];?\\n?/g, \"\");\n\n // Unwrap <ApostilWrapper>{children}</ApostilWrapper>\n content = content.replace(/<ApostilWrapper>([\\s\\S]*?)<\\/ApostilWrapper>/g, \"$1\");\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n return true;\n}\n\n// --- Helpers ---\n\nasync function findAppDir(cwd: string): Promise<string | null> {\n for (const candidate of [\"src/app\", \"app\"]) {\n const dir = path.join(cwd, candidate);\n try {\n const stat = await fs.stat(dir);\n if (stat.isDirectory()) return dir;\n } catch {}\n }\n return null;\n}\n\nasync function findLayout(appDir: string): Promise<string | null> {\n for (const ext of [\"tsx\", \"jsx\", \"ts\", \"js\"]) {\n const file = path.join(appDir, `layout.${ext}`);\n if (await fileExists(file)) return file;\n }\n return null;\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction rel(cwd: string, filePath: string): string {\n return path.relative(cwd, filePath);\n}\n"],"mappings":"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","names":["path","fs"]}
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node