pubblue 0.1.1 → 0.3.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.
Files changed (2) hide show
  1. package/dist/index.js +91 -53
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { createInterface } from "readline/promises";
7
7
  import { Command } from "commander";
8
8
 
9
9
  // src/lib/api.ts
10
- var PublishApiClient = class {
10
+ var PubApiClient = class {
11
11
  constructor(baseUrl, apiKey) {
12
12
  this.baseUrl = baseUrl;
13
13
  this.apiKey = apiKey;
@@ -28,28 +28,44 @@ var PublishApiClient = class {
28
28
  }
29
29
  return data;
30
30
  }
31
- async publish(opts) {
32
- return this.request("/api/v1/publish", {
31
+ async create(opts) {
32
+ return this.request("/api/v1/publications", {
33
33
  method: "POST",
34
34
  body: JSON.stringify(opts)
35
35
  });
36
36
  }
37
37
  async get(slug) {
38
- const data = await this.request(`/api/v1/publications?slug=${encodeURIComponent(slug)}`);
38
+ const data = await this.request(`/api/v1/publications/${encodeURIComponent(slug)}`);
39
39
  return data.publication;
40
40
  }
41
+ async listPage(cursor, limit) {
42
+ const params = new URLSearchParams();
43
+ if (cursor) params.set("cursor", cursor);
44
+ if (limit) params.set("limit", String(limit));
45
+ const qs = params.toString();
46
+ return this.request(`/api/v1/publications${qs ? `?${qs}` : ""}`);
47
+ }
41
48
  async list() {
42
- const data = await this.request("/api/v1/publications");
43
- return data.publications;
49
+ const all = [];
50
+ let cursor;
51
+ do {
52
+ const result = await this.listPage(cursor, 100);
53
+ all.push(...result.publications);
54
+ cursor = result.hasMore ? result.cursor : void 0;
55
+ } while (cursor);
56
+ return all;
44
57
  }
45
58
  async update(opts) {
46
- return this.request("/api/v1/publications", {
59
+ const { slug, newSlug, ...rest } = opts;
60
+ const body = { ...rest };
61
+ if (newSlug) body.slug = newSlug;
62
+ return this.request(`/api/v1/publications/${encodeURIComponent(slug)}`, {
47
63
  method: "PATCH",
48
- body: JSON.stringify(opts)
64
+ body: JSON.stringify(body)
49
65
  });
50
66
  }
51
67
  async remove(slug) {
52
- await this.request(`/api/v1/publications?slug=${encodeURIComponent(slug)}`, {
68
+ await this.request(`/api/v1/publications/${encodeURIComponent(slug)}`, {
53
69
  method: "DELETE"
54
70
  });
55
71
  }
@@ -113,7 +129,7 @@ function getConfig(homeDir) {
113
129
  var program = new Command();
114
130
  function createClient() {
115
131
  const config = getConfig();
116
- return new PublishApiClient(config.baseUrl, config.apiKey);
132
+ return new PubApiClient(config.baseUrl, config.apiKey);
117
133
  }
118
134
  async function readFromStdin() {
119
135
  const chunks = [];
@@ -122,10 +138,6 @@ async function readFromStdin() {
122
138
  }
123
139
  return Buffer.concat(chunks).toString("utf-8").trim();
124
140
  }
125
- function printPublishResult(result) {
126
- const verb = result.updated ? "Updated" : "Published";
127
- console.log(`${verb}: ${result.url}`);
128
- }
129
141
  function formatVisibility(isPublic) {
130
142
  return isPublic ? "public" : "private";
131
143
  }
@@ -160,7 +172,18 @@ async function resolveConfigureApiKey(opts) {
160
172
  }
161
173
  return readApiKeyFromPrompt();
162
174
  }
163
- program.name("pubblue").description("Publish static content and get shareable URLs").version("0.1.0");
175
+ function readFile(filePath) {
176
+ const resolved = path2.resolve(filePath);
177
+ if (!fs2.existsSync(resolved)) {
178
+ console.error(`File not found: ${resolved}`);
179
+ process.exit(1);
180
+ }
181
+ return {
182
+ content: fs2.readFileSync(resolved, "utf-8"),
183
+ basename: path2.basename(resolved)
184
+ };
185
+ }
186
+ program.name("pubblue").description("Publish static content and get shareable URLs").version("0.3.0");
164
187
  program.command("configure").description("Configure the CLI with your API key").option("--api-key <key>", "Your API key (less secure: appears in shell history)").option("--api-key-stdin", "Read API key from stdin").action(async (opts) => {
165
188
  try {
166
189
  const apiKey = await resolveConfigureApiKey(opts);
@@ -172,60 +195,74 @@ program.command("configure").description("Configure the CLI with your API key").
172
195
  process.exit(1);
173
196
  }
174
197
  });
175
- program.command("publish").description("Publish a file").argument("<file>", "Path to the file to publish").option("--slug <slug>", "Custom slug for the URL").option("--title <title>", "Title for the publication").option("--private", "Make the publication private").action(async (file, opts) => {
176
- const client = createClient();
177
- const filePath = path2.resolve(file);
178
- if (!fs2.existsSync(filePath)) {
179
- console.error(`File not found: ${filePath}`);
180
- process.exit(1);
181
- }
182
- const content = fs2.readFileSync(filePath, "utf-8");
183
- const filename = path2.basename(filePath);
184
- const result = await client.publish({
185
- filename,
186
- content,
187
- title: opts.title,
188
- slug: opts.slug,
189
- isPublic: !opts.private
190
- });
191
- printPublishResult(result);
192
- });
193
- program.command("publish-content").description("Publish content directly from stdin or argument").requiredOption("--filename <name>", "Filename (determines content type, e.g. page.html)").option("--content <content>", "Content string (if not provided, reads from stdin)").option("--slug <slug>", "Custom slug for the URL").option("--title <title>", "Title for the publication").option("--private", "Make the publication private").action(
194
- async (opts) => {
198
+ program.command("create").description("Create a new publication").argument("[file]", "Path to the file (reads stdin if omitted)").option("--slug <slug>", "Custom slug for the URL").option("--title <title>", "Title for the publication").option("--public", "Make the publication public (default: private)").option("--private", "Make the publication private (this is the default)").option("--expires <duration>", "Auto-delete after duration (e.g. 1h, 24h, 7d)").action(
199
+ async (fileArg, opts) => {
195
200
  const client = createClient();
196
- const content = opts.content ?? await readFromStdin();
197
- const result = await client.publish({
198
- filename: opts.filename,
201
+ let content;
202
+ let filename;
203
+ if (fileArg) {
204
+ const file = readFile(fileArg);
205
+ content = file.content;
206
+ filename = file.basename;
207
+ } else {
208
+ content = await readFromStdin();
209
+ }
210
+ const result = await client.create({
199
211
  content,
212
+ filename,
200
213
  title: opts.title,
201
214
  slug: opts.slug,
202
- isPublic: !opts.private
215
+ isPublic: opts.public ?? false,
216
+ expiresIn: opts.expires
203
217
  });
204
- printPublishResult(result);
218
+ console.log(`Created: ${result.url}`);
219
+ if (result.expiresAt) {
220
+ console.log(` Expires: ${new Date(result.expiresAt).toISOString()}`);
221
+ }
205
222
  }
206
223
  );
207
- program.command("get").description("Get details of a publication").argument("<slug>", "Slug of the publication").action(async (slug) => {
224
+ program.command("get").description("Get details of a publication").argument("<slug>", "Slug of the publication").option("--content", "Output raw content to stdout (no metadata, pipeable)").action(async (slug, opts) => {
208
225
  const client = createClient();
209
226
  const pub = await client.get(slug);
227
+ if (opts.content) {
228
+ process.stdout.write(pub.content);
229
+ return;
230
+ }
210
231
  console.log(` Slug: ${pub.slug}`);
211
- console.log(` File: ${pub.filename}`);
212
232
  console.log(` Type: ${pub.contentType}`);
213
233
  if (pub.title) console.log(` Title: ${pub.title}`);
214
234
  console.log(` Status: ${formatVisibility(pub.isPublic)}`);
235
+ if (pub.expiresAt) console.log(` Expires: ${new Date(pub.expiresAt).toISOString()}`);
215
236
  console.log(` Created: ${new Date(pub.createdAt).toLocaleDateString()}`);
216
237
  console.log(` Updated: ${new Date(pub.updatedAt).toLocaleDateString()}`);
217
238
  console.log(` Size: ${pub.content.length} bytes`);
218
239
  });
219
- program.command("update").description("Update publication metadata").argument("<slug>", "Slug of the publication to update").option("--title <title>", "New title").option("--public", "Make the publication public").option("--private", "Make the publication private").action(async (slug, opts) => {
220
- const client = createClient();
221
- let isPublic;
222
- if (opts.public) isPublic = true;
223
- else if (opts.private) isPublic = false;
224
- const result = await client.update({ slug, title: opts.title, isPublic });
225
- console.log(`Updated: ${result.slug}`);
226
- if (result.title) console.log(` Title: ${result.title}`);
227
- console.log(` Status: ${formatVisibility(result.isPublic)}`);
228
- });
240
+ program.command("update").description("Update a publication's content and/or metadata").argument("<slug>", "Slug of the publication to update").option("--file <file>", "New content from file").option("--title <title>", "New title").option("--public", "Make the publication public").option("--private", "Make the publication private").option("--slug <newSlug>", "Rename the slug").action(
241
+ async (slug, opts) => {
242
+ const client = createClient();
243
+ let content;
244
+ let filename;
245
+ if (opts.file) {
246
+ const file = readFile(opts.file);
247
+ content = file.content;
248
+ filename = file.basename;
249
+ }
250
+ let isPublic;
251
+ if (opts.public) isPublic = true;
252
+ else if (opts.private) isPublic = false;
253
+ const result = await client.update({
254
+ slug,
255
+ content,
256
+ filename,
257
+ title: opts.title,
258
+ isPublic,
259
+ newSlug: opts.slug
260
+ });
261
+ console.log(`Updated: ${result.slug}`);
262
+ if (result.title) console.log(` Title: ${result.title}`);
263
+ console.log(` Status: ${formatVisibility(result.isPublic)}`);
264
+ }
265
+ );
229
266
  program.command("list").description("List your publications").action(async () => {
230
267
  const client = createClient();
231
268
  const pubs = await client.list();
@@ -235,8 +272,9 @@ program.command("list").description("List your publications").action(async () =>
235
272
  }
236
273
  for (const pub of pubs) {
237
274
  const date = new Date(pub.createdAt).toLocaleDateString();
275
+ const expires = pub.expiresAt ? ` expires:${new Date(pub.expiresAt).toISOString()}` : "";
238
276
  console.log(
239
- ` ${pub.slug} ${pub.filename} [${pub.contentType}] ${formatVisibility(pub.isPublic)} ${date}`
277
+ ` ${pub.slug} [${pub.contentType}] ${formatVisibility(pub.isPublic)} ${date}${expires}`
240
278
  );
241
279
  }
242
280
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pubblue",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "CLI tool for publishing static content via pub.blue",
5
5
  "type": "module",
6
6
  "bin": {