feat(data): PdR registration via inbox, notifications, public discovery, protected store
Polyfill-enabled features (T02). All NextGraph I/O goes through @ng-eventually/client (docs/inbox/storeRegistry); no direct @ng-org. - Shapes: FpMeetingPoint + FpNotification are now real SHEX shapes with ORM bindings (previously app-TS-only, unpersisted). - Registration (registration.ts, new): joinEvent persists a Participation + deposits to the host's inbox + creates a Notification (from = registrant if connected, anonymous otherwise). leaveEvent deletes the Participation authoritatively via SPARQL DELETE-WHERE (sweep by event+user AND by subject, then re-query to confirm) — the désinscription CRDT-resurrection bug is fixed: the reactive delete is applied only once the broker confirms 0 remaining. - Public discovery: useNgData fans out over every account's public docs so a user sees others' public events without a connection (dedup union). - Cap attribution: createEntityDoc declares the ReadCap (open + makePublic/ grantRead per scope), activating the per-document read filter. - Protected store (T02.h): the default path now reads/writes shareable domain entities in the native protected store (did🆖${protected_store_id}) instead of private — verified openable against the broker — matching the per-wallet target. Private still anchors the shim/inbox + settings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -176,4 +176,129 @@ export const festipodShapesSchema: Schema = {
|
||||
},
|
||||
],
|
||||
},
|
||||
"http://festipod.org/MeetingPoint": {
|
||||
iri: "http://festipod.org/MeetingPoint",
|
||||
predicates: [
|
||||
{
|
||||
dataTypes: [
|
||||
{
|
||||
valType: "iri",
|
||||
literals: ["http://festipod.org/MeetingPoint"],
|
||||
},
|
||||
],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 1,
|
||||
iri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
|
||||
readablePredicate: "@type",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "iri" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 1,
|
||||
iri: "http://festipod.org/event",
|
||||
readablePredicate: "event",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "iri" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 1,
|
||||
iri: "http://festipod.org/host",
|
||||
readablePredicate: "host",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "string" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 1,
|
||||
iri: "http://festipod.org/title",
|
||||
readablePredicate: "title",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "string" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 0,
|
||||
iri: "http://festipod.org/description",
|
||||
readablePredicate: "description",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "string" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 0,
|
||||
iri: "http://festipod.org/place",
|
||||
readablePredicate: "place",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "string" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 0,
|
||||
iri: "http://festipod.org/time",
|
||||
readablePredicate: "time",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "string" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 0,
|
||||
iri: "http://festipod.org/inbox",
|
||||
readablePredicate: "inbox",
|
||||
},
|
||||
],
|
||||
},
|
||||
"http://festipod.org/Notification": {
|
||||
iri: "http://festipod.org/Notification",
|
||||
predicates: [
|
||||
{
|
||||
dataTypes: [
|
||||
{
|
||||
valType: "iri",
|
||||
literals: ["http://festipod.org/Notification"],
|
||||
},
|
||||
],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 1,
|
||||
iri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
|
||||
readablePredicate: "@type",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "iri" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 1,
|
||||
iri: "http://festipod.org/recipient",
|
||||
readablePredicate: "recipient",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "string" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 1,
|
||||
iri: "http://festipod.org/type",
|
||||
readablePredicate: "type",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "iri" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 0,
|
||||
iri: "http://festipod.org/ref",
|
||||
readablePredicate: "ref",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "string" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 0,
|
||||
iri: "http://festipod.org/payload",
|
||||
readablePredicate: "payload",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "string" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 1,
|
||||
iri: "http://festipod.org/timestamp",
|
||||
readablePredicate: "timestamp",
|
||||
},
|
||||
{
|
||||
dataTypes: [{ valType: "boolean" }],
|
||||
maxCardinality: 1,
|
||||
minCardinality: 1,
|
||||
iri: "http://festipod.org/isRead",
|
||||
readablePredicate: "isRead",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { ShapeType } from "@ng-org/shex-orm";
|
||||
import { festipodShapesSchema } from "./festipodShapes.schema";
|
||||
import type { FpEvent, FpUserProfile, FpParticipation } from "./festipodShapes.typings";
|
||||
import type {
|
||||
FpEvent,
|
||||
FpUserProfile,
|
||||
FpParticipation,
|
||||
FpMeetingPoint,
|
||||
FpNotification,
|
||||
} from "./festipodShapes.typings";
|
||||
|
||||
// ShapeTypes for festipodShapes
|
||||
export const FpEventShapeType: ShapeType<FpEvent> = {
|
||||
@@ -15,3 +21,11 @@ export const FpParticipationShapeType: ShapeType<FpParticipation> = {
|
||||
schema: festipodShapesSchema,
|
||||
shape: "http://festipod.org/Participation",
|
||||
};
|
||||
export const FpMeetingPointShapeType: ShapeType<FpMeetingPoint> = {
|
||||
schema: festipodShapesSchema,
|
||||
shape: "http://festipod.org/MeetingPoint",
|
||||
};
|
||||
export const FpNotificationShapeType: ShapeType<FpNotification> = {
|
||||
schema: festipodShapesSchema,
|
||||
shape: "http://festipod.org/Notification",
|
||||
};
|
||||
|
||||
@@ -161,3 +161,117 @@ export interface FpParticipation {
|
||||
*/
|
||||
isConfirmed: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* MeetingPoint Type
|
||||
*/
|
||||
export interface FpMeetingPoint {
|
||||
/**
|
||||
* The graph IRI.
|
||||
*/
|
||||
readonly "@graph": IRI;
|
||||
/**
|
||||
* The subject IRI.
|
||||
*/
|
||||
readonly "@id": IRI;
|
||||
/**
|
||||
* Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type
|
||||
*/
|
||||
"@type": "http://festipod.org/MeetingPoint";
|
||||
/**
|
||||
* Reference to the public event this meeting point is anchored to
|
||||
*
|
||||
* Original IRI: http://festipod.org/event
|
||||
*/
|
||||
event: IRI;
|
||||
/**
|
||||
* Reference to the user hosting the meeting point
|
||||
*
|
||||
* Original IRI: http://festipod.org/host
|
||||
*/
|
||||
host: IRI;
|
||||
/**
|
||||
* The title of the meeting point
|
||||
*
|
||||
* Original IRI: http://festipod.org/title
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* A description of the meeting point
|
||||
*
|
||||
* Original IRI: http://festipod.org/description
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Where participants gather (e.g. 'Entrée principale, sous l'horloge')
|
||||
*
|
||||
* Original IRI: http://festipod.org/place
|
||||
*/
|
||||
place?: string;
|
||||
/**
|
||||
* When participants gather (display string)
|
||||
*
|
||||
* Original IRI: http://festipod.org/time
|
||||
*/
|
||||
time?: string;
|
||||
/**
|
||||
* NURI of the meeting point's inbox (wired in T02.b/c)
|
||||
*
|
||||
* Original IRI: http://festipod.org/inbox
|
||||
*/
|
||||
inbox?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification Type
|
||||
*/
|
||||
export interface FpNotification {
|
||||
/**
|
||||
* The graph IRI.
|
||||
*/
|
||||
readonly "@graph": IRI;
|
||||
/**
|
||||
* The subject IRI.
|
||||
*/
|
||||
readonly "@id": IRI;
|
||||
/**
|
||||
* Original IRI: http://www.w3.org/1999/02/22-rdf-syntax-ns#type
|
||||
*/
|
||||
"@type": "http://festipod.org/Notification";
|
||||
/**
|
||||
* Reference to the user receiving the notification
|
||||
*
|
||||
* Original IRI: http://festipod.org/recipient
|
||||
*/
|
||||
recipient: IRI;
|
||||
/**
|
||||
* Notification kind (e.g. 'new-participant', 'meeting-point-join')
|
||||
*
|
||||
* Original IRI: http://festipod.org/type
|
||||
*/
|
||||
type: string;
|
||||
/**
|
||||
* Reference to the subject resource (meeting point, participation, user)
|
||||
*
|
||||
* Original IRI: http://festipod.org/ref
|
||||
*/
|
||||
ref?: IRI;
|
||||
/**
|
||||
* Opaque JSON payload for the notification
|
||||
*
|
||||
* Original IRI: http://festipod.org/payload
|
||||
*/
|
||||
payload?: string;
|
||||
/**
|
||||
* ISO8601 timestamp when the notification was created
|
||||
*
|
||||
* Original IRI: http://festipod.org/timestamp
|
||||
*/
|
||||
timestamp: string;
|
||||
/**
|
||||
* Whether the recipient has read the notification
|
||||
*
|
||||
* Original IRI: http://festipod.org/isRead
|
||||
*/
|
||||
isRead: boolean;
|
||||
}
|
||||
|
||||
@@ -47,3 +47,37 @@ fp:Participation {
|
||||
fp:isConfirmed xsd:boolean
|
||||
// rdfs:comment "Whether the participation is confirmed" ;
|
||||
}
|
||||
|
||||
fp:MeetingPoint {
|
||||
a [fp:MeetingPoint] ;
|
||||
fp:event IRI
|
||||
// rdfs:comment "Reference to the public event this meeting point is anchored to" ;
|
||||
fp:host IRI
|
||||
// rdfs:comment "Reference to the user hosting the meeting point" ;
|
||||
fp:title xsd:string
|
||||
// rdfs:comment "The title of the meeting point" ;
|
||||
fp:description xsd:string ?
|
||||
// rdfs:comment "A description of the meeting point" ;
|
||||
fp:place xsd:string ?
|
||||
// rdfs:comment "Where participants gather (e.g. 'Entrée principale, sous l'horloge')" ;
|
||||
fp:time xsd:string ?
|
||||
// rdfs:comment "When participants gather (display string)" ;
|
||||
fp:inbox xsd:string ?
|
||||
// rdfs:comment "NURI of the meeting point's inbox (wired in T02.b/c)" ;
|
||||
}
|
||||
|
||||
fp:Notification {
|
||||
a [fp:Notification] ;
|
||||
fp:recipient IRI
|
||||
// rdfs:comment "Reference to the user receiving the notification" ;
|
||||
fp:type xsd:string
|
||||
// rdfs:comment "Notification kind (e.g. 'new-participant', 'meeting-point-join')" ;
|
||||
fp:ref IRI ?
|
||||
// rdfs:comment "Reference to the subject resource (meeting point, participation, user)" ;
|
||||
fp:payload xsd:string ?
|
||||
// rdfs:comment "Opaque JSON payload for the notification" ;
|
||||
fp:timestamp xsd:string
|
||||
// rdfs:comment "ISO8601 timestamp when the notification was created" ;
|
||||
fp:isRead xsd:boolean
|
||||
// rdfs:comment "Whether the recipient has read the notification" ;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user