Ignore:
Timestamp:
08/10/2026 06:54:54 AM (4 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
75f2897
Parents:
7e53594
Message:

Standaardvormen naast het dialect: AS2 en FEP waar die bestaan

shaer-nmw. De client-legs spraken Klonkt-dialect waar de standaard het al
regelt. Nu dragen ze de standaardvorm ERNAAST:

  • attributedTo als ingesloten Person (naam, preferredUsername, icon, url, FEP-9098 emoji in tag) in plaats van alleen shaer:author;
  • een boost is een echte Announce met zijn actor, niet shaer:booster;
  • FEP-044f quote als de geciteerde Note zelf, met eigen attributedTo;
  • AS2 preview (Page met url, name, image) voor de linkkaart.

Weten we niets van de persoon, dan blijft attributedTo de kale URI: een leeg
object zou beweren dat we hem kennen.

De shaer:-velden blijven voorlopig staan. Ze weghalen is de tweede helft van
de bead en kan pas als de apps in het veld zijn bijgewerkt.

De toets is niet "onze app snapt het" maar of een GENERIEKE AP-lezer er iets
aan heeft; test/standard-shapes.test.js kijkt daarom naar geen enkel
shaer:-veld.

Co-Authored-By: Claude Opus 5 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r7e53594 r0db3c72  
    48084808
    48094809/** The stored external-embed card, for the C2S read. */
     4810// ── Standaardvormen in plaats van eigen dialect (shaer-nmw) ───────
     4811//
     4812// Robins waarschuwing: geen Klonkt/Shaer-dialect schrijven waar AS2 of een FEP
     4813// het al regelt. Vier eigen properties hadden een standaard naast zich staan,
     4814// en deze helpers zijn die standaard -- een definitie per vorm, zodat de tien
     4815// plekken die ze emitten niet elk hun eigen variant krijgen.
     4816//
     4817// De oude shaer:-velden blijven er voorlopig NAAST staan. Een app in het veld
     4818// leest ze nog, en een leeg scherm is een duurdere fout dan een dubbel veld;
     4819// ze gaan eruit als de clients om zijn (tweede helft van shaer-nmw).
     4820
     4821/** FEP-9098 Emoji-tags uit een {shortcode: url}-kaart. */
     4822function emojiTagsFromMap(emojis) {
     4823  const uit = Object.entries(emojis || {})
     4824    .filter(([naam, url]) => naam && url)
     4825    .map(([naam, url]) => ({ type: 'Emoji', name: naam, icon: { type: 'Image', url } }));
     4826  return uit.length ? uit : undefined;
     4827}
     4828
     4829/**
     4830 * Een actor als INGESLOTEN OBJECT voor `attributedTo` / `actor`.
     4831 *
     4832 * AS2 staat toe dat attributedTo een object is in plaats van een URI, en dan
     4833 * heeft ELKE client er wat aan -- niet alleen de onze, die er shaer:author
     4834 * naast kreeg. `preferredUsername` is de lokale naam; een lezer leidt de handle
     4835 * af uit die naam plus de host van de id, precies zoals wij serverkant ook
     4836 * doen. Weten we niets van de persoon, dan blijft het de kale URI: een leeg
     4837 * object zou beweren dat we hem kennen.
     4838 */
     4839export function actorObject(uri, info) {
     4840  if (!uri) return undefined;
     4841  const iets = info && (info.name || info.handle || info.icon || info.url);
     4842  if (!iets) return uri;
     4843  const o = { id: uri, type: 'Person' };
     4844  if (info.name) o.name = info.name;
     4845  const lokaal = String(info.handle || '').replace(/^@/, '').split('@')[0];
     4846  if (lokaal) o.preferredUsername = lokaal;
     4847  if (info.icon) o.icon = { type: 'Image', url: info.icon };
     4848  if (info.url) o.url = info.url;
     4849  const tags = emojiTagsFromMap(info.emojis);
     4850  if (tags) o.tag = tags;
     4851  return o;
     4852}
     4853
     4854/**
     4855 * De linkkaart als AS2 `preview` (core: "identifies an entity that provides a
     4856 * preview of this object"). Een Page met url, name en image IS een kaart; daar
     4857 * hoefde shaer:embed nooit voor te bestaan.
     4858 *
     4859 * Wat WEL van ons blijft is de spelerpagina: dat die alleen meegaat als de
     4860 * guardians de poort openden is FEP-633c-gedrag en heeft geen AS2-tegenhanger.
     4861 */
     4862export function previewObject(embedJson, { playback = false } = {}) {
     4863  const e = timelineEmbed(embedJson, { playback });
     4864  if (!e) return undefined;
     4865  const thumb = (e.media || []).find((m) => m && m.url);
     4866  const p = { type: 'Page', url: e.url };
     4867  if (e.title) p.name = e.title;
     4868  if (thumb) p.image = { type: 'Image', url: thumb.url };
     4869  if (e.author && (e.author.name || e.author.handle)) {
     4870    p.attributedTo = { type: 'Person', name: e.author.name || e.author.handle };
     4871  }
     4872  if (e['shaer:playerUrl']) p['shaer:playerUrl'] = e['shaer:playerUrl'];
     4873  if (e['shaer:playable']) p['shaer:playable'] = e['shaer:playable'];
     4874  return p;
     4875}
     4876
     4877/**
     4878 * De geciteerde post als OBJECT in `quote` (FEP-044f staat toe dat quote het
     4879 * object zelf is, niet alleen een URI). De opgeslagen momentopname wordt hier
     4880 * een echte Note, met de auteur als ingesloten actor -- dus geen tweede eigen
     4881 * property voor iets dat de FEP al kan.
     4882 */
     4883export function quoteObject(quoteJson) {
     4884  const q = timelineQuote(quoteJson);
     4885  if (!q) return undefined;
     4886  const note = { type: 'Note', id: q.url, url: q.url };
     4887  if (q.content) note.content = q.content;
     4888  if (q.published) note.published = q.published;
     4889  if (q.author) {
     4890    note.attributedTo = actorObject(q.author.url || q.url, {
     4891      name: q.author.name, handle: q.author.handle, icon: q.author.icon,
     4892    });
     4893  }
     4894  const media = (q.media || []).filter((m) => m && m.url)
     4895    .map((m) => ({ type: 'Document', mediaType: m.type || undefined, url: m.url }));
     4896  if (media.length) note.attachment = media;
     4897  const tags = emojiTagsFromMap(q.emojis);
     4898  if (tags) note.tag = tags;
     4899  return note;
     4900}
     4901
    48104902export function timelineEmbed(embedJson, { playback = false } = {}) {
    48114903  try {
     
    61886280  getNotifications, listBlocks, isBlockedAny, blockTarget, unblock,
    61896281  deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker,
    6190   sendMaybe304, etagFor, onGuardian, wakeGuardian, onGuardianshipEvent, proposeGate, getReplyUris, getThread, filterThreadToCircle, gateAttachments, stripEmojiTags, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio,
     6282  sendMaybe304, etagFor, onGuardian, wakeGuardian, onGuardianshipEvent, proposeGate, getReplyUris, getThread, filterThreadToCircle, gateAttachments, stripEmojiTags, actorObject, previewObject, quoteObject, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio,
    61916283  linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower, listConnections,
    61926284  noteVisibility, belongsInTimeline, playerUrlFor, isRejectedObject, rejectInteraction, interactionReportTarget,
Note: See TracChangeset for help on using the changeset viewer.