feat: add absorb command — merge source entity into target
DB: Absorb() merges body (newline-separated), unions tags, demotes
crystallized sources, soft-deletes source. Rejects crystallized targets.
API: POST /api/entities/:id/absorb { source_id }
CLI: nib absorb <target> <source> with prefix ID resolution
Web: absorb button on fluid entities, 'a' keyboard shortcut,
source picker modal
This commit is contained in:
@@ -304,6 +304,50 @@ func demoteEntity(store *db.Store) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
type AbsorbRequest struct {
|
||||
SourceID string `json:"source_id"`
|
||||
}
|
||||
|
||||
func absorbEntity(store *db.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
var req AbsorbRequest
|
||||
if !decodeJSON(w, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.SourceID == "" {
|
||||
writeError(w, http.StatusBadRequest, "invalid_input", "source_id is required")
|
||||
return
|
||||
}
|
||||
if req.SourceID == id {
|
||||
writeError(w, http.StatusBadRequest, "invalid_input", "target and source must be different entities")
|
||||
return
|
||||
}
|
||||
|
||||
if err := store.Absorb(id, req.SourceID); err != nil {
|
||||
if err == db.ErrNotFound {
|
||||
writeError(w, http.StatusNotFound, "not_found", "target or source entity not found")
|
||||
return
|
||||
}
|
||||
if err == db.ErrTargetCrystallized {
|
||||
writeError(w, http.StatusBadRequest, "invalid_absorb", "target is crystallized — demote first")
|
||||
return
|
||||
}
|
||||
writeError(w, http.StatusInternalServerError, "internal", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
e, err := store.Get(id)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "internal", err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, entityToResponse(e))
|
||||
}
|
||||
}
|
||||
|
||||
func useEntity(store *db.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
Reference in New Issue
Block a user