Changeset 780a7c6 in Klonkt for src/services/guardianship/relations.js
- Timestamp:
- 07/24/2026 07:44:15 PM (7 weeks ago)
- Branches:
- main
- Children:
- b5924eb
- Parents:
- c26cc18
- File:
-
- 1 edited
-
src/services/guardianship/relations.js (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/guardianship/relations.js
rc26cc18 r780a7c6 1 1 /** 2 * Guardianship (FEP-633c) — the ward ↔ guardian relations (ap_guardianships). 3 * 4 * Every row is one relation seen from a LOCAL site: role 'guardian' means the 5 * site guards `other_uri` (a ward, possibly remote); role 'ward' means 6 * `other_uri` guards the site. A local ward with a local guardian yields two 7 * rows, one per perspective — intentional, each side reads its own. 8 * 9 * The handshake (spec §3): the guardian-candidate — and only the candidate — 10 * Offers a Relationship {subject: ward, relationship: shaer:Guardian, 11 * object: candidate}; the ward Accepts (or Rejects). Status walks 12 * 'offered' → 'accepted'; a Reject deletes the row. 2 * Guardianship (FEP-633c) — the COMMITTED ward ↔ guardian relations 3 * (ap_guardianships). Pending offers live in offers.js; a row here means the 4 * handshake committed (§3.1.4). Every row is one relation seen from a LOCAL 5 * site: role 'guardian' = the site guards other_uri; role 'ward' = other_uri 6 * guards the site. 13 7 */ 14 8 import db from '../../config/database.js'; … … 18 12 if (!_s) { 19 13 _s = { 20 ins: db.prepare(`INSERT OR IGNOREINTO ap_guardianships (slug, role, other_uri, other_handle, status, offer_id, created_at)21 VALUES (?,?,?,?,?,?,CURRENT_TIMESTAMP)`),22 accept: db.prepare(`UPDATE ap_guardianships SET status='accepted' WHERE slug=? AND role=? AND other_uri=?`),14 commit: db.prepare(`INSERT INTO ap_guardianships (slug, role, other_uri, other_handle, status, offer_id, created_at) 15 VALUES (?,?,?,?, 'accepted', ?, CURRENT_TIMESTAMP) 16 ON CONFLICT(slug, role, other_uri) DO UPDATE SET status='accepted', offer_id=excluded.offer_id`), 23 17 del: db.prepare('DELETE FROM ap_guardianships WHERE slug=? AND role=? AND other_uri=?'), 24 bySlugRole: db.prepare( 'SELECT * FROM ap_guardianships WHERE slug=? AND role=? ORDER BY created_at DESC'),18 bySlugRole: db.prepare("SELECT * FROM ap_guardianships WHERE slug=? AND role=? AND status='accepted' ORDER BY created_at DESC"), 25 19 one: db.prepare('SELECT * FROM ap_guardianships WHERE slug=? AND role=? AND other_uri=?'), 26 byOffer: db.prepare('SELECT * FROM ap_guardianships WHERE offer_id=?'),27 20 }; 28 21 } … … 33 26 34 27 /** Accepted guardian URIs of a local ward (feeds shaer:guardians). */ 35 export function listGuardians(slug) { 36 return stmts().bySlugRole.all(slug, 'ward').filter((r) => r.status === 'accepted'); 28 export function listGuardians(slug) { return stmts().bySlugRole.all(slug, 'ward'); } 29 30 /** Accepted wards of a local guardian (the wards queue). */ 31 export function listWards(slug) { return stmts().bySlugRole.all(slug, 'guardian'); } 32 33 /** A site is a guardian once it stands in any accepted guardian relation. */ 34 export function isGuardian(slug) { return listWards(slug).length > 0; } 35 36 export function getRelation(slug, role, otherUri) { return stmts().one.get(slug, role, otherUri); } 37 38 // ── Writes (only the handshake commit lands here) ──────────────────────── 39 40 /** The local ward gains a guardian (commit, §3.1.4). */ 41 export function commitGuardianForWard(wardSlug, guardianUri, { handle = null, offerId = null } = {}) { 42 stmts().commit.run(wardSlug, 'ward', guardianUri, handle, offerId); 43 return stmts().one.get(wardSlug, 'ward', guardianUri); 37 44 } 38 45 39 /** All ward relations of a local guardian (accepted + pending offers). */ 40 export function listWards(slug) { 41 return stmts().bySlugRole.all(slug, 'guardian'); 46 /** The local guardian gains a ward (commit, §3.1.4). */ 47 export function commitWardForGuardian(guardianSlug, wardUri, { handle = null, offerId = null } = {}) { 48 stmts().commit.run(guardianSlug, 'guardian', wardUri, handle, offerId); 49 return stmts().one.get(guardianSlug, 'guardian', wardUri); 42 50 } 43 51 44 /** Pending offers where the local site is a party (either side). */ 45 export function listOffers(slug) { 46 return [...stmts().bySlugRole.all(slug, 'guardian'), ...stmts().bySlugRole.all(slug, 'ward')] 47 .filter((r) => r.status === 'offered'); 48 } 49 50 /** A site is a guardian once it stands in any guardian-side relation. */ 51 export function isGuardian(slug) { 52 return stmts().bySlugRole.all(slug, 'guardian').length > 0; 53 } 54 55 export function getRelation(slug, role, otherUri) { return stmts().one.get(slug, role, otherUri); } 56 export function findByOfferId(offerId) { return offerId ? stmts().byOffer.all(offerId) : []; } 57 58 // ── Writes (the handshake walks through these) ─────────────────────────── 59 60 /** Record an outgoing/incoming Offer on the local side with `role`. */ 61 export function recordOffer(slug, role, otherUri, { handle = null, offerId = null } = {}) { 62 stmts().ins.run(slug, role, otherUri, handle, 'offered', offerId); 63 return stmts().one.get(slug, role, otherUri); 64 } 65 66 /** The ward said yes (or our own offer was accepted): relation becomes real. */ 67 export function acceptRelation(slug, role, otherUri) { 68 stmts().accept.run(slug, role, otherUri); 69 return stmts().one.get(slug, role, otherUri); 70 } 71 72 /** Reject / retract / end a relation: the row disappears. */ 52 /** End a relation locally (Undo, §3.2 — federation of the Undo is Fase 4). */ 73 53 export function removeRelation(slug, role, otherUri) { 74 54 stmts().del.run(slug, role, otherUri); … … 79 59 80 60 /** 81 * The guardianship properties for a local actor doc. `id` is the actor URI.82 * - shaer:guardians: accepted guardians of this ward (omitted when none )61 * Guardianship props for a local actor doc. `id` is the actor URI. 62 * - shaer:guardians: accepted guardians of this ward (omitted when none, §2.1) 83 63 * - shaer:isGuardian: true once the site guards anyone 84 * - shaer:queues: the owner-only dashboard collections (always advertised, 85 * like `blocked`: clients discover, the routes enforce auth) 64 * - shaer:queues: the owner-only dashboard collections 65 * 66 * §1 mutual exclusion: a ward (has guardians) is never a guardian, so 67 * shaer:isGuardian is suppressed if guardians exist; the offer path already 68 * bars a ward from offering. 86 69 */ 87 70 export function actorProps(id, slug) { … … 94 77 }; 95 78 const guardians = listGuardians(slug).map((r) => r.other_uri); 96 if (guardians.length) props['shaer:guardians'] = guardians; 97 if (isGuardian(slug)) props['shaer:isGuardian'] = true; 79 if (guardians.length) { 80 props['shaer:guardians'] = guardians; // a ward 81 } else if (isGuardian(slug)) { 82 props['shaer:isGuardian'] = true; // a guardian (never both, §1) 83 } 98 84 return props; 99 85 } 100 86 101 87 export default { 102 listGuardians, listWards, listOffers, isGuardian, getRelation, findByOfferId,103 recordOffer, acceptRelation, removeRelation, actorProps,88 listGuardians, listWards, isGuardian, getRelation, 89 commitGuardianForWard, commitWardForGuardian, removeRelation, actorProps, 104 90 };
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)