Skip to content

Commit c0f7f6e

Browse files
just-be-devclaude
andcommitted
fix(micro): make page live from R2, fix anchor bug, remove stale code
- Replace build-time content collection with live R2 reads in micro.astro, [id].astro, and rss.xml.ts so new posts appear immediately without a rebuild - Remove micro-loader.ts and micro collection from content.config.ts/schemas.ts - Fix #post-${id} anchor URL in browse.ts (was not matching article id attribute) - Rename shadowed clack prompts variable in browse.ts find() callback - Add title field to RSS items (truncated content excerpt) - Show syndicated platform names as links instead of raw URLs - Replace all emojis in gen-url-manifest.ts console output with plain text - Fix misleading comment about anchor URL format in gen-url-manifest.ts - Update README to remove stale D1/Drizzle references Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 18d69ee commit c0f7f6e

File tree

8 files changed

+86
-117
lines changed

8 files changed

+86
-117
lines changed

packages/micro/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ CLI tool for creating and managing micro blog posts.
1010
- Automatic syndication to Bluesky and Twitter
1111
- Delete posts
1212
- Open posts in browser
13-
- D1 database integration via Drizzle ORM
13+
- R2 JSONL storage via HTTP API
1414

1515
## Installation
1616

@@ -52,7 +52,7 @@ Creates a post directly from the command line.
5252
## Requirements
5353

5454
- Bun >= 1.0.0
55-
- Cloudflare D1 database configured in wrangler.toml
55+
- A running instance of the site with `MICRO_BUCKET` (R2) and `MICRO_SECRET` configured
5656

5757
## Social Media Syndication
5858

@@ -92,9 +92,9 @@ To get Twitter API credentials:
9292

9393
### Syndication Behavior
9494

95-
- Posts are created in the database first, ensuring they're saved even if syndication fails
95+
- Posts are saved to R2 first, ensuring they're preserved even if syndication fails
9696
- Syndication attempts are made to all configured platforms
97-
- The `syndicated_to` field in the database tracks which platforms received the post
97+
- The `syndicatedTo` field tracks which platforms received the post
9898
- Syndication failures are logged but don't prevent post creation
9999
- Rate limits and authentication errors are handled gracefully with clear error messages
100100

@@ -104,4 +104,4 @@ To test syndication without real API credentials, simply don't set the environme
104104

105105
## Development
106106

107-
The CLI uses Wrangler's `getPlatformProxy` to access the local D1 database. Make sure you have a `wrangler.toml` configured with a D1 database binding named `DB`.
107+
The CLI talks to the site's HTTP API. Set `MICRO_SECRET` to authenticate and `MICRO_SITE_URL` to point at a local dev server (defaults to `https://just-be.dev`).

packages/micro/src/browse.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export async function browse() {
7878
continue;
7979
}
8080

81-
const selectedPost = posts.find((p) => p.id === selectedId);
81+
const selectedPost = posts.find((post) => post.id === selectedId);
8282
if (!selectedPost) continue;
8383

8484
await showPostActions({ siteUrl, secret }, selectedPost, posts);
@@ -125,7 +125,7 @@ async function showPostActions(
125125

126126
switch (action) {
127127
case "open-web": {
128-
const url = `${config.siteUrl}/micro#post-${post.id}`;
128+
const url = `${config.siteUrl}/micro#${post.id}`;
129129
console.log(`Opening: ${url}`);
130130
await Bun.spawn(["open", url]);
131131
break;

scripts/gen-url-manifest.ts

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ async function processMicroCollection(
4949
});
5050

5151
if (!env.MICRO_BUCKET) {
52-
console.log(" ⚠️ Skipping micro posts: MICRO_BUCKET not available\n");
52+
console.log(" [WARN] Skipping micro posts: MICRO_BUCKET not available\n");
5353
await dispose();
5454
return { processed: 0, skipped: 0, errors: 0 };
5555
}
@@ -58,7 +58,7 @@ async function processMicroCollection(
5858
await dispose();
5959

6060
if (!obj) {
61-
console.log(" ⚠️ Skipping micro posts: micro-posts.jsonl not found in R2\n");
61+
console.log(" [WARN] Skipping micro posts: micro-posts.jsonl not found in R2\n");
6262
return { processed: 0, skipped: 0, errors: 0 };
6363
}
6464

@@ -79,15 +79,15 @@ async function processMicroCollection(
7979
// Generate code from creation date with 'M' kind
8080
const code = Code.fromDate(createdAt, "M").toString().toLowerCase();
8181

82-
// Micro posts use anchor URLs: /micro#post-id
82+
// Micro posts use anchor URLs: /micro#<id>
8383
const urlPath = `/micro#${postId}`;
8484

8585
try {
8686
// Check if this slug appears twice in current run
8787
if (seenSlugsInCurrentRun.has(urlPath)) {
8888
const previousLocation = seenSlugsInCurrentRun.get(urlPath);
8989
console.error(
90-
` DUPLICATE: ${urlPath} appears in both ${previousLocation} and micro post ${postId}`,
90+
` [ERROR] DUPLICATE: ${urlPath} appears in both ${previousLocation} and micro post ${postId}`,
9191
);
9292
errorCount++;
9393
continue;
@@ -98,15 +98,15 @@ async function processMicroCollection(
9898
const existingCode = existingSlugToCode.get(urlPath);
9999
if (existingCode && existingCode !== code) {
100100
console.error(
101-
` CONFLICT: ${urlPath} is already assigned to code ${existingCode}, cannot reassign to ${code}`,
101+
` [ERROR] CONFLICT: ${urlPath} is already assigned to code ${existingCode}, cannot reassign to ${code}`,
102102
);
103103
errorCount++;
104104
continue;
105105
}
106106

107107
// Skip if already in manifest (idempotent)
108108
if (manifest.codes[code]?.includes(urlPath)) {
109-
console.log(` ${code} -> ${urlPath} (already exists)`);
109+
console.log(` [SKIP] ${code} -> ${urlPath} (already exists)`);
110110
processedCount++;
111111
continue;
112112
}
@@ -118,11 +118,11 @@ async function processMicroCollection(
118118
manifest.codes[code].push(urlPath);
119119
existingSlugToCode.set(urlPath, code);
120120

121-
console.log(` ${code} -> ${urlPath}`);
121+
console.log(` [OK] ${code} -> ${urlPath}`);
122122
processedCount++;
123123
} catch (error) {
124124
console.error(
125-
` Error processing micro post ${postId}:`,
125+
` [ERROR] Error processing micro post ${postId}:`,
126126
error instanceof Error ? error.message : error,
127127
);
128128
errorCount++;
@@ -137,7 +137,7 @@ async function processMicroCollection(
137137
};
138138
} catch (error) {
139139
console.error(
140-
` Failed to load micro posts: ${error instanceof Error ? error.message : String(error)}`,
140+
` [ERROR] Failed to load micro posts: ${error instanceof Error ? error.message : String(error)}`,
141141
);
142142
console.log();
143143
return { processed: 0, skipped: 0, errors: 1 };
@@ -157,7 +157,7 @@ function processCollection(
157157

158158
// Check if directory exists
159159
if (!existsSync(dirPath)) {
160-
console.log(`⚠️ Collection ${collection} does not exist, skipping...\n`);
160+
console.log(`[WARN] Collection ${collection} does not exist, skipping...\n`);
161161
return { processed: 0, skipped: 0, errors: 0 };
162162
}
163163

@@ -189,7 +189,7 @@ function processCollection(
189189

190190
// Check if code exists in frontmatter
191191
if (!data.code) {
192-
console.log(` Skipping ${filename} (no code in frontmatter)`);
192+
console.log(` [SKIP] Skipping ${filename} (no code in frontmatter)`);
193193
skippedCount++;
194194
continue;
195195
}
@@ -204,7 +204,9 @@ function processCollection(
204204
for (const customSlug of data.slugs) {
205205
// Validate that slug starts with / (full path from origin)
206206
if (!customSlug.startsWith("/")) {
207-
console.error(` ❌ Invalid slug in ${filename}: "${customSlug}" must start with "/"`);
207+
console.error(
208+
` [ERROR] Invalid slug in ${filename}: "${customSlug}" must start with "/"`,
209+
);
208210
errorCount++;
209211
continue;
210212
}
@@ -222,7 +224,7 @@ function processCollection(
222224
if (seenSlugsInCurrentRun.has(urlPath)) {
223225
const previousLocation = seenSlugsInCurrentRun.get(urlPath);
224226
console.error(
225-
` DUPLICATE: ${urlPath} appears in both ${previousLocation} and ${filename}`,
227+
` [ERROR] DUPLICATE: ${urlPath} appears in both ${previousLocation} and ${filename}`,
226228
);
227229
errorCount++;
228230
continue;
@@ -233,15 +235,15 @@ function processCollection(
233235
const existingCode = existingSlugToCode.get(urlPath);
234236
if (existingCode && existingCode !== code) {
235237
console.error(
236-
` CONFLICT: ${urlPath} is already assigned to code ${existingCode}, cannot reassign to ${code}`,
238+
` [ERROR] CONFLICT: ${urlPath} is already assigned to code ${existingCode}, cannot reassign to ${code}`,
237239
);
238240
errorCount++;
239241
continue;
240242
}
241243

242244
// Skip if already in manifest (idempotent)
243245
if (manifest.codes[code]?.includes(urlPath)) {
244-
console.log(` ${code} -> ${urlPath} (already exists)`);
246+
console.log(` [SKIP] ${code} -> ${urlPath} (already exists)`);
245247
processedCount++;
246248
continue;
247249
}
@@ -253,12 +255,12 @@ function processCollection(
253255
manifest.codes[code].push(urlPath);
254256
existingSlugToCode.set(urlPath, code);
255257

256-
console.log(` ${code} -> ${urlPath}`);
258+
console.log(` [OK] ${code} -> ${urlPath}`);
257259
processedCount++;
258260
}
259261
} catch (error) {
260262
console.error(
261-
` Error processing ${filename}:`,
263+
` [ERROR] Error processing ${filename}:`,
262264
error instanceof Error ? error.message : error,
263265
);
264266
errorCount++;
@@ -310,10 +312,10 @@ function loadExistingManifest(): UrlManifest {
310312
try {
311313
const content = readFileSync(MANIFEST_PATH, "utf-8");
312314
const parsed = YAML.parse(content) as UrlManifest;
313-
console.log(`📂 Loaded existing manifest with ${Object.keys(parsed.codes).length} codes\n`);
315+
console.log(`Loaded existing manifest with ${Object.keys(parsed.codes).length} codes\n`);
314316
return parsed;
315317
} catch (error) {
316-
console.warn(`⚠️ Failed to load existing manifest, starting fresh:`, error);
318+
console.warn(`[WARN] Failed to load existing manifest, starting fresh:`, error);
317319
return {
318320
version: "1.0",
319321
codes: {},
@@ -370,7 +372,7 @@ async function genUrlManifest(collections: string[]) {
370372

371373
// Exit with error if there were conflicts or errors
372374
if (totalErrors > 0) {
373-
console.error("\n❌ Manifest generation failed due to errors");
375+
console.error("\nManifest generation failed due to errors");
374376
console.error(` ${totalErrors} error(s) occurred`);
375377
process.exit(1);
376378
}
@@ -386,9 +388,9 @@ async function genUrlManifest(collections: string[]) {
386388
const yamlContent = formatManifestAsYAML(manifest);
387389
writeFileSync(MANIFEST_PATH, header + yamlContent + "\n");
388390
console.log("=".repeat(50));
389-
console.log(`\n✅ Manifest written to: ${MANIFEST_PATH}`);
391+
console.log(`\nManifest written to: ${MANIFEST_PATH}`);
390392
} catch (error) {
391-
console.error("Failed to write manifest:", error);
393+
console.error("Failed to write manifest:", error);
392394
process.exit(1);
393395
}
394396

src/content.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
tagsFileSchema,
1818
microSchema,
1919
} from "./content/schemas";
20-
import { microLoader } from "./loaders/micro-loader";
2120

2221
const blog = defineCollection({
2322
loader: glob({ base: "./src/content/blog", pattern: "**/*.{md,mdx}" }),

src/loaders/micro-loader.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)