@@ -176,6 +176,21 @@ def sha256_file(path: pathlib.Path) -> str:
176176def main ():
177177 ensure_src ()
178178
179+ # Load previous index (for new/recent detection) BEFORE we overwrite it
180+ prev_full : Dict [str , Any ] = {}
181+ prev_by_slug : Dict [str , Dict [str , Any ]] = {}
182+ prev_index_path = API / "index.json"
183+ if prev_index_path .exists ():
184+ try :
185+ prev_full = json .loads (prev_index_path .read_text ())
186+ except Exception as exc :
187+ sys .stderr .write (f"[emit_static_api] warning: failed to parse previous index.json: { exc } \n " )
188+ prev_full = {}
189+ if prev_full :
190+ for prev_item in get_items (prev_full ):
191+ prev_slug = item_slug (prev_item )
192+ prev_by_slug [prev_slug ] = prev_item
193+
179194 # 1) Full dataset passthrough
180195 dst_index = copy_index ()
181196 data_full = json .loads (dst_index .read_text ())
@@ -189,6 +204,17 @@ def main():
189204 # 3) Aggregate stats (using normalized, expanded versions)
190205 items = get_items (data_full )
191206
207+ # Determine change state for each item vs previous index
208+ change_map : Dict [str , str ] = {}
209+ if prev_by_slug :
210+ for it in items :
211+ slug = item_slug (it )
212+ prev_it = prev_by_slug .get (slug )
213+ if prev_it is None :
214+ change_map [slug ] = "new"
215+ elif prev_it != it :
216+ change_map [slug ] = "updated"
217+
192218 byType = {
193219 "plugin" : len (plugins ),
194220 "theme" : len (themes ),
@@ -204,14 +230,15 @@ def main():
204230 versions = item_versions (it ) # normalized to canonical list
205231 creators = item_creators (it )
206232 slug = item_slug (it )
233+ change_state = change_map .get (slug , "" )
207234
208235 for v in versions :
209236 versions_present .add (v )
210237 byVersion [v ] = byVersion .get (v , 0 ) + 1
211238 for c in creators :
212239 creators_count [c ] = creators_count .get (c , 0 ) + 1
213240
214- enriched .append ({** it , "_slug" : slug , "_versions" : versions , "_creators" : creators })
241+ enriched .append ({** it , "_slug" : slug , "_versions" : versions , "_creators" : creators , "_change" : change_state })
215242
216243 # 4) stats.json
217244 stats = {
@@ -235,6 +262,9 @@ def main():
235262
236263 # 7) Per-item docs + expose normalized fields to make consumption easier
237264 search_index : List [Dict [str , Any ]] = []
265+ new_items_docs : List [Dict [str , Any ]] = []
266+ recent_items_docs : List [Dict [str , Any ]] = []
267+
238268 for it in enriched :
239269 slug = it ["_slug" ]
240270 doc = {k : v for k , v in it .items () if not k .startswith ("_" )} # preserve original shape
@@ -250,6 +280,12 @@ def main():
250280 if it ["_creators" ]:
251281 doc ["creator_slug" ] = it ["_creators" ][0 ].lower ()
252282
283+ change_state = it .get ("_change" ) or ""
284+ if change_state == "new" :
285+ new_items_docs .append (doc )
286+ elif change_state == "updated" :
287+ recent_items_docs .append (doc )
288+
253289 # write per-item
254290 item_path = API / "items" / f"{ slugify (slug )} .json"
255291 write_json (item_path , doc )
@@ -290,7 +326,11 @@ def main():
290326 # 10) search-index.json (compact for client-side search)
291327 write_json (API / "search-index.json" , search_index )
292328
293- # 11) manifest.json (sizes + sha256)
329+ # 11) new/recent activity endpoints
330+ write_json (API / "new.json" , new_items_docs )
331+ write_json (API / "recent.json" , recent_items_docs )
332+
333+ # 12) manifest.json (sizes + sha256)
294334 manifest_entries = []
295335 for p in WRITTEN :
296336 # Only include files under API dir
0 commit comments