feat: Add custom slug support via mp-slug property

Implements custom slug handling for Micropub as specified in ADR-035.

Changes:
- Created starpunk/slug_utils.py with validation/sanitization functions
- Added RESERVED_SLUGS constant (api, admin, auth, feed, etc.)
- Modified create_note() to accept optional custom_slug parameter
- Integrated mp-slug extraction in Micropub handle_create()
- Slug sanitization: lowercase, hyphens, no special chars
- Conflict resolution: sequential numbering (-2, -3, etc.)
- Hierarchical slugs (/) rejected (deferred to v1.2.0)

Features:
- Custom slugs via Micropub's mp-slug property
- Automatic sanitization of invalid characters
- Reserved slug protection
- Sequential conflict resolution (not random)
- Clear error messages for validation failures

Part of v1.1.0 (Phase 4).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-25 10:05:38 -07:00
parent b3c1b16617
commit c7fcc21406
3 changed files with 297 additions and 10 deletions

View File

@@ -294,6 +294,15 @@ def handle_create(data: dict, token_info: dict):
title = extract_title(properties)
tags = extract_tags(properties)
published_date = extract_published_date(properties)
# Extract custom slug if provided (Micropub extension)
custom_slug = None
if 'mp-slug' in properties:
# mp-slug is an array in Micropub format
slug_values = properties.get('mp-slug', [])
if slug_values and len(slug_values) > 0:
custom_slug = slug_values[0]
except MicropubValidationError as e:
raise e
except Exception as e:
@@ -303,7 +312,10 @@ def handle_create(data: dict, token_info: dict):
# Create note using existing CRUD
try:
note = create_note(
content=content, published=True, created_at=published_date # Micropub posts are published by default
content=content,
published=True, # Micropub posts are published by default
created_at=published_date,
custom_slug=custom_slug
)
# Build permalink URL