using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using GameCult.Caching; using GameCult.Caching.MessagePack; using MessagePack; using R3; namespace GameCult.Mesh { /// /// Describes the route CultMesh used or should prefer for a typed operation, query, pointer, or native view. /// public enum CultMeshLocalityKind { /// Let CultMesh choose the fastest valid route. Automatic, /// The target is in the same process. InProcess, /// The target is available through a shared CultCache slab or native shared memory view. SharedMemory, /// The target is reachable through local inter-process transport. Ipc, /// The target is reachable through a network Verse route. Network, /// The target is hosted in or exposed to a WebAssembly runtime. Wasm } /// /// Names a CultMesh locality decision without exposing transport plumbing to application code. /// public sealed class CultMeshRouteHint { /// Creates a route hint. public CultMeshRouteHint(CultMeshLocalityKind kind = CultMeshLocalityKind.Automatic, string? description = null) { Kind = kind; Description = description; } /// Gets the requested or observed locality kind. public CultMeshLocalityKind Kind { get; } /// Gets optional human-facing route diagnostics. public string? Description { get; } /// Gets the default automatic route hint. public static CultMeshRouteHint Automatic { get; } = new(); } /// /// Flat, transport-friendly fields for a CultMesh route hint. /// public sealed class CultMeshRouteRecord { /// Creates flat route fields. public CultMeshRouteRecord(string? kind = null, string? description = null) { Kind = kind ?? ""; Description = description ?? ""; } /// Gets the serialized route locality kind. public string Kind { get; } /// Gets the serialized route description. public string Description { get; } /// Creates flat route fields from a route hint. public static CultMeshRouteRecord FromRoute(CultMeshRouteHint? routeHint) { var route = routeHint ?? CultMeshRouteHint.Automatic; return new CultMeshRouteRecord(route.Kind.ToString(), route.Description); } /// Rehydrates flat route fields into a route hint. public CultMeshRouteHint ToRoute(CultMeshRouteHint? fallback = null) { var fallbackRoute = fallback ?? CultMeshRouteHint.Automatic; return new CultMeshRouteHint( ParseKind(Kind, fallbackRoute.Kind), string.IsNullOrWhiteSpace(Description) ? fallbackRoute.Description : Description); } private static CultMeshLocalityKind ParseKind(string? value, CultMeshLocalityKind fallback) { if (string.IsNullOrWhiteSpace(value)) return fallback; if (Enum.TryParse(value, ignoreCase: true, out var parsed)) return parsed; var normalized = value.Replace("-", "", StringComparison.Ordinal); foreach (CultMeshLocalityKind candidate in Enum.GetValues(typeof(CultMeshLocalityKind))) { if (string.Equals(candidate.ToString(), normalized, StringComparison.OrdinalIgnoreCase)) return candidate; } return fallback; } } /// /// Shared Verse invocation context used by generated domain sugar. /// public sealed class CultMeshVerseContext { /// Creates a Verse context. public CultMeshVerseContext( string verseId, string runtimeId, CultMeshRouteHint? routeHint = null, IEnumerable? claims = null) { VerseId = RequireNonEmpty(verseId, nameof(verseId)); RuntimeId = RequireNonEmpty(runtimeId, nameof(runtimeId)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; Claims = claims?.ToArray() ?? Array.Empty(); } /// Gets the semantic Verse id. public string VerseId { get; } /// Gets the runtime using the Verse. public string RuntimeId { get; } /// Gets the preferred route for generated operations and queries. public CultMeshRouteHint RouteHint { get; } /// Gets authority claims carried by generated operations. public IReadOnlyList Claims { get; } /// Returns a copy with one additional authority claim. public CultMeshVerseContext WithClaim(CultMeshAuthorityClaim claim) { if (claim == null) throw new ArgumentNullException(nameof(claim)); return new CultMeshVerseContext(VerseId, RuntimeId, RouteHint, Claims.Concat(new[] { claim })); } /// Returns a copy with the supplied route hint. public CultMeshVerseContext WithRoute(CultMeshRouteHint routeHint) { if (routeHint == null) throw new ArgumentNullException(nameof(routeHint)); return new CultMeshVerseContext(VerseId, RuntimeId, routeHint, Claims); } /// Creates a typed operation context from the Verse context. public CultMeshOperationContext OperationContext(string? idempotencyKey = null) { return new CultMeshOperationContext(RuntimeId, Claims, RouteHint, idempotencyKey); } /// Creates a typed query context from the Verse context. public CultMeshQueryContext QueryContext() { return new CultMeshQueryContext(RuntimeId, RouteHint); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Managed Verse handle that generated domain packages can bind to. /// public sealed class CultMeshVerse { /// Creates a managed Verse handle. public CultMeshVerse(CultMeshVerseContext context) { Context = context ?? throw new ArgumentNullException(nameof(context)); } /// Gets the Verse context shared by generated domain sugar. public CultMeshVerseContext Context { get; } /// Gets the semantic Verse id. public string VerseId => Context.VerseId; /// Gets the runtime using the Verse. public string RuntimeId => Context.RuntimeId; /// Binds this Verse to a generated domain facade. public TSchema Use(Func schemaFactory) { if (schemaFactory == null) throw new ArgumentNullException(nameof(schemaFactory)); return schemaFactory(Context); } /// Returns a Verse handle with one additional authority claim. public CultMeshVerse WithClaim(CultMeshAuthorityClaim claim) { return new CultMeshVerse(Context.WithClaim(claim)); } /// Returns a Verse handle with the supplied route hint. public CultMeshVerse WithRoute(CultMeshRouteHint routeHint) { return new CultMeshVerse(Context.WithRoute(routeHint)); } /// Creates a typed operation context from this Verse. public CultMeshOperationContext OperationContext(string? idempotencyKey = null) { return Context.OperationContext(idempotencyKey); } /// Creates a typed query context from this Verse. public CultMeshQueryContext QueryContext() { return Context.QueryContext(); } /// Binds an operation handle to this Verse. public CultMeshBoundOperationHandle BindOperation( CultMeshOperationHandle operation) { return new CultMeshBoundOperationHandle(Context, operation); } /// Binds a query surface to this Verse. public CultMeshBoundQuerySurface BindQuery( CultMeshQuerySurface query) { return new CultMeshBoundQuerySurface(Context, query); } /// Binds a live feed to this Verse. public CultMeshBoundLiveFeed BindLiveFeed( CultMeshLiveFeed feed) { return new CultMeshBoundLiveFeed(Context, feed); } /// Binds a state pointer to this Verse. public CultMeshBoundStatePointer BindStatePointer( CultMeshStatePointer pointer) { return new CultMeshBoundStatePointer(Context, pointer); } /// Binds a mutable state pointer to this Verse. public CultMeshBoundMutableStatePointer BindMutableStatePointer( CultMeshMutableStatePointer pointer) { return new CultMeshBoundMutableStatePointer(Context, pointer); } } /// /// Describes a runtime authority claim attached to a typed operation. /// public sealed class CultMeshAuthorityClaim { /// Creates an authority claim. public CultMeshAuthorityClaim(string role, string? shardId = null, string? leaseId = null) { Role = RequireNonEmpty(role, nameof(role)); ShardId = shardId; LeaseId = leaseId; } /// Gets the role or claim kind being asserted. public string Role { get; } /// Gets the optional shard covered by the claim. public string? ShardId { get; } /// Gets the optional lease id backing the claim. public string? LeaseId { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Context supplied when invoking a typed CultMesh operation. /// public sealed class CultMeshOperationContext { /// Creates an operation context. public CultMeshOperationContext( string runtimeId, IEnumerable? claims = null, CultMeshRouteHint? routeHint = null, string? idempotencyKey = null) { RuntimeId = RequireNonEmpty(runtimeId, nameof(runtimeId)); Claims = claims?.ToArray() ?? Array.Empty(); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; IdempotencyKey = idempotencyKey; } /// Gets the runtime invoking the operation. public string RuntimeId { get; } /// Gets authority claims attached to the operation. public IReadOnlyList Claims { get; } /// Gets the preferred route for the operation. public CultMeshRouteHint RouteHint { get; } /// Gets an optional operation idempotency key. public string? IdempotencyKey { get; } /// Creates an operation context for a runtime. public static CultMeshOperationContext ForRuntime(string runtimeId) { return new CultMeshOperationContext(runtimeId); } /// Returns a copy with one additional authority claim. public CultMeshOperationContext WithClaim(CultMeshAuthorityClaim claim) { if (claim == null) throw new ArgumentNullException(nameof(claim)); return new CultMeshOperationContext(RuntimeId, Claims.Concat(new[] { claim }), RouteHint, IdempotencyKey); } /// Returns a copy with the supplied route hint. public CultMeshOperationContext WithRoute(CultMeshRouteHint routeHint) { if (routeHint == null) throw new ArgumentNullException(nameof(routeHint)); return new CultMeshOperationContext(RuntimeId, Claims, routeHint, IdempotencyKey); } /// Returns a copy with the supplied idempotency key. public CultMeshOperationContext WithIdempotencyKey(string idempotencyKey) { return new CultMeshOperationContext( RuntimeId, Claims, RouteHint, RequireNonEmpty(idempotencyKey, nameof(idempotencyKey))); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Receipt returned by a typed CultMesh operation. /// public sealed class CultMeshOperationReceipt { /// Creates an operation receipt. public CultMeshOperationReceipt( string operationId, bool accepted, CultMeshRouteHint? route = null, string? diagnostic = null) { OperationId = RequireNonEmpty(operationId, nameof(operationId)); Accepted = accepted; Route = route ?? CultMeshRouteHint.Automatic; Diagnostic = diagnostic; } /// Gets the semantic operation id. public string OperationId { get; } /// Gets whether the operation was accepted by the target authority boundary. public bool Accepted { get; } /// Gets the route used for the operation. public CultMeshRouteHint Route { get; } /// Gets optional operation diagnostics. public string? Diagnostic { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Method-shaped handle for a typed CultMesh operation. /// public sealed class CultMeshOperationHandle { private readonly Func> _invoke; /// Creates an operation handle. public CultMeshOperationHandle( string operationId, Func> invoke) { OperationId = RequireNonEmpty(operationId, nameof(operationId)); _invoke = invoke ?? throw new ArgumentNullException(nameof(invoke)); } /// Gets the semantic operation id. public string OperationId { get; } /// Invokes the typed operation. public Task InvokeAsync(TRequest request, CultMeshOperationContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return _invoke(request, context); } /// Invokes the typed operation for one runtime using default context. public Task InvokeAsync(TRequest request, string runtimeId) { return InvokeAsync(request, CultMeshOperationContext.ForRuntime(runtimeId)); } /// Binds this operation to a Verse context. public CultMeshBoundOperationHandle Bind(CultMeshVerseContext context) { return new CultMeshBoundOperationHandle(context, this); } /// Binds this operation to a Verse. public CultMeshBoundOperationHandle Bind(CultMeshVerse verse) { if (verse == null) throw new ArgumentNullException(nameof(verse)); return Bind(verse.Context); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Operation handle pre-bound to a Verse so generated domain sugar can invoke without context plumbing. /// public sealed class CultMeshBoundOperationHandle { /// Creates a Verse-bound operation handle. public CultMeshBoundOperationHandle( CultMeshVerseContext context, CultMeshOperationHandle operation) { Context = context ?? throw new ArgumentNullException(nameof(context)); Operation = operation ?? throw new ArgumentNullException(nameof(operation)); } /// Gets the bound Verse context. public CultMeshVerseContext Context { get; } /// Gets the underlying operation handle. public CultMeshOperationHandle Operation { get; } /// Gets the semantic operation id. public string OperationId => Operation.OperationId; /// Invokes the operation through the bound Verse. public Task InvokeAsync(TRequest request, string? idempotencyKey = null) { return Operation.InvokeAsync(request, Context.OperationContext(idempotencyKey)); } } /// /// Context supplied when executing a typed CultMesh query surface. /// public sealed class CultMeshQueryContext { /// Creates a query context. public CultMeshQueryContext(string runtimeId, CultMeshRouteHint? routeHint = null) { RuntimeId = RequireNonEmpty(runtimeId, nameof(runtimeId)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; } /// Gets the runtime executing the query. public string RuntimeId { get; } /// Gets the preferred route for the query. public CultMeshRouteHint RouteHint { get; } /// Creates a query context for a runtime. public static CultMeshQueryContext ForRuntime(string runtimeId) { return new CultMeshQueryContext(runtimeId); } /// Returns a copy with the supplied route hint. public CultMeshQueryContext WithRoute(CultMeshRouteHint routeHint) { if (routeHint == null) throw new ArgumentNullException(nameof(routeHint)); return new CultMeshQueryContext(RuntimeId, routeHint); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Fluent builder for typed operation context. This is sugar over , /// intended for call sites that should read like native Verse operations instead of transport setup. /// public sealed class CultMeshOperationContextBuilder { private readonly string _runtimeId; private readonly List _claims = new(); private CultMeshRouteHint _routeHint = CultMeshRouteHint.Automatic; private string? _idempotencyKey; /// Creates a builder for one runtime. public CultMeshOperationContextBuilder(string runtimeId) { _runtimeId = RequireNonEmpty(runtimeId, nameof(runtimeId)); } /// Adds an authority claim. public CultMeshOperationContextBuilder Claim(string role, string? shardId = null, string? leaseId = null) { _claims.Add(new CultMeshAuthorityClaim(role, shardId, leaseId)); return this; } /// Adds an authority claim. public CultMeshOperationContextBuilder Claim(CultMeshAuthorityClaim claim) { _claims.Add(claim ?? throw new ArgumentNullException(nameof(claim))); return this; } /// Adds many authority claims. public CultMeshOperationContextBuilder Claims(IEnumerable claims) { if (claims == null) throw new ArgumentNullException(nameof(claims)); _claims.AddRange(claims); return this; } /// Sets the preferred route hint. public CultMeshOperationContextBuilder Route(CultMeshLocalityKind kind, string? description = null) { _routeHint = new CultMeshRouteHint(kind, description); return this; } /// Sets the operation idempotency key. public CultMeshOperationContextBuilder Idempotency(string idempotencyKey) { _idempotencyKey = RequireNonEmpty(idempotencyKey, nameof(idempotencyKey)); return this; } /// Creates the immutable operation context. public CultMeshOperationContext Build() { return new CultMeshOperationContext(_runtimeId, _claims, _routeHint, _idempotencyKey); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Fluent builder for typed query context. /// public sealed class CultMeshQueryContextBuilder { private readonly string _runtimeId; private CultMeshRouteHint _routeHint = CultMeshRouteHint.Automatic; /// Creates a builder for one runtime. public CultMeshQueryContextBuilder(string runtimeId) { _runtimeId = RequireNonEmpty(runtimeId, nameof(runtimeId)); } /// Sets the preferred route hint. public CultMeshQueryContextBuilder Route(CultMeshLocalityKind kind, string? description = null) { _routeHint = new CultMeshRouteHint(kind, description); return this; } /// Creates the immutable query context. public CultMeshQueryContext Build() { return new CultMeshQueryContext(_runtimeId, _routeHint); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Typed derived-state query surface. /// public sealed class CultMeshQuerySurface { private readonly Func> _execute; private readonly Func>? _watch; /// Creates a query surface. public CultMeshQuerySurface( string queryId, Func> execute, Func>? watch = null, IEnumerable? sources = null, CultMeshRouteHint? routeHint = null) { QueryId = RequireNonEmpty(queryId, nameof(queryId)); _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _watch = watch; Sources = sources?.ToArray() ?? Array.Empty(); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; } /// Gets the semantic query id. public string QueryId { get; } /// Gets the typed state sources this query depends on, when known. public IReadOnlyList Sources { get; } /// Gets the preferred or observed route for the query. public CultMeshRouteHint RouteHint { get; } /// Executes the query once. public Task ExecuteAsync(TParameters parameters, CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return _execute(parameters, context); } /// Executes the query once for one runtime using default context. public Task ExecuteAsync(TParameters parameters, string runtimeId) { return ExecuteAsync(parameters, CultMeshQueryContext.ForRuntime(runtimeId)); } /// Watches query results when the surface supports reactive execution. public Observable Watch(TParameters parameters, CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); if (_watch == null) { throw new NotSupportedException($"Query surface '{QueryId}' does not support reactive watches."); } return _watch(parameters, context); } /// Binds this query surface to a Verse context. public CultMeshBoundQuerySurface Bind(CultMeshVerseContext context) { return new CultMeshBoundQuerySurface(context, this); } /// Binds this query surface to a Verse. public CultMeshBoundQuerySurface Bind(CultMeshVerse verse) { if (verse == null) throw new ArgumentNullException(nameof(verse)); return Bind(verse.Context); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Query surface pre-bound to a Verse so generated domain sugar can execute without context plumbing. /// public sealed class CultMeshBoundQuerySurface { /// Creates a Verse-bound query surface. public CultMeshBoundQuerySurface( CultMeshVerseContext context, CultMeshQuerySurface query) { Context = context ?? throw new ArgumentNullException(nameof(context)); Query = query ?? throw new ArgumentNullException(nameof(query)); } /// Gets the bound Verse context. public CultMeshVerseContext Context { get; } /// Gets the underlying query surface. public CultMeshQuerySurface Query { get; } /// Gets the semantic query id. public string QueryId => Query.QueryId; /// Gets the typed state sources this query depends on, when known. public IReadOnlyList Sources => Query.Sources; /// Gets the preferred or observed route for the query. public CultMeshRouteHint RouteHint => Query.RouteHint; /// Executes the query through the bound Verse. public Task ExecuteAsync(TParameters parameters) { return Query.ExecuteAsync(parameters, Context.QueryContext()); } /// Watches query results through the bound Verse. public Observable Watch(TParameters parameters) { return Query.Watch(parameters, Context.QueryContext()); } } /// /// Typed live view surface for composed client snapshots. /// public sealed class CultMeshLiveFeed { private readonly Func> _snapshot; private readonly Func>? _watch; /// Creates a live feed surface. public CultMeshLiveFeed( string feedId, Func> snapshot, Func>? watch = null, IEnumerable? sources = null, CultMeshRouteHint? routeHint = null) { FeedId = RequireNonEmpty(feedId, nameof(feedId)); _snapshot = snapshot ?? throw new ArgumentNullException(nameof(snapshot)); _watch = watch; Sources = sources?.ToArray() ?? Array.Empty(); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; } /// Gets the semantic feed id. public string FeedId { get; } /// Gets the typed state sources this live feed depends on, when known. public IReadOnlyList Sources { get; } /// Gets the preferred or observed route for the feed. public CultMeshRouteHint RouteHint { get; } /// Reads one coherent feed snapshot. public Task SnapshotAsync(TParameters parameters, CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return _snapshot(parameters, ResolveContext(context)); } /// Reads one coherent feed snapshot for one runtime using default context. public Task SnapshotAsync(TParameters parameters, string runtimeId) { return SnapshotAsync(parameters, CultMeshQueryContext.ForRuntime(runtimeId)); } /// Watches coherent feed snapshots when the feed supports reactive execution. public Observable Watch(TParameters parameters, CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); if (_watch == null) { throw new NotSupportedException($"Live feed '{FeedId}' does not support reactive watches."); } return _watch(parameters, ResolveContext(context)); } /// Binds this live feed to a Verse context. public CultMeshBoundLiveFeed Bind(CultMeshVerseContext context) { return new CultMeshBoundLiveFeed(context, this); } /// Binds this live feed to a Verse. public CultMeshBoundLiveFeed Bind(CultMeshVerse verse) { if (verse == null) throw new ArgumentNullException(nameof(verse)); return Bind(verse.Context); } private CultMeshQueryContext ResolveContext(CultMeshQueryContext context) { return context.RouteHint.Kind == CultMeshLocalityKind.Automatic && RouteHint.Kind != CultMeshLocalityKind.Automatic ? context.WithRoute(RouteHint) : context; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Live feed pre-bound to a Verse so generated domain sugar can snapshot or watch without context plumbing. /// public sealed class CultMeshBoundLiveFeed { /// Creates a Verse-bound live feed. public CultMeshBoundLiveFeed( CultMeshVerseContext context, CultMeshLiveFeed feed) { Context = context ?? throw new ArgumentNullException(nameof(context)); Feed = feed ?? throw new ArgumentNullException(nameof(feed)); } /// Gets the bound Verse context. public CultMeshVerseContext Context { get; } /// Gets the underlying live feed. public CultMeshLiveFeed Feed { get; } /// Gets the semantic feed id. public string FeedId => Feed.FeedId; /// Gets the typed state sources this live feed depends on, when known. public IReadOnlyList Sources => Feed.Sources; /// Gets the preferred or observed route for the feed. public CultMeshRouteHint RouteHint => Feed.RouteHint; /// Reads one coherent feed snapshot through the bound Verse. public Task SnapshotAsync(TParameters parameters) { return Feed.SnapshotAsync(parameters, Context.QueryContext()); } /// Watches coherent feed snapshots through the bound Verse. public Observable Watch(TParameters parameters) { return Feed.Watch(parameters, Context.QueryContext()); } } /// /// Empty parameter object for document handles that expose one typed document as a live feed. /// public readonly struct CultMeshDocumentQueryParameters { /// Gets the singleton empty document query. public static CultMeshDocumentQueryParameters Empty { get; } = new(); } /// /// Inspectable typed document handle exposed by CultMesh. /// public interface ICultMeshDocumentHandle { /// Gets the CLR document type presented by this handle. Type DocumentType { get; } /// Gets the semantic document id. string DocumentId { get; } /// Gets the stable CultCache schema name. string SchemaName { get; } /// Gets the stable CultCache schema version. string SchemaVersion { get; } /// Gets the preferred or observed route for document access. CultMeshRouteHint RouteHint { get; } /// Gets typed state sources this document handle depends on, when known. IReadOnlyList Sources { get; } /// Gets whether this handle can replace the underlying document value. bool CanReplace { get; } /// Gets whether this handle can submit a client prediction for the underlying document value. bool CanSubmitPrediction { get; } /// Gets whether this handle can accept a transparent document mutation. bool CanSet { get; } /// Creates a same-schema alias presentation for another CLR document type. CultMeshDocumentHandle AsSchemaAlias() where TAlias : class; } /// /// Typed reactive document handle with schema-aware alias conversion. /// public sealed class CultMeshDocumentHandle : ICultMeshDocumentHandle where TDocument : class { private static readonly CultDocumentDescriptor Descriptor = CultDocumentRegistry.Shared.GetRequired(); private readonly CultMeshBoundLiveFeed _feed; private readonly Func? _replace; private readonly Func? _submitPrediction; /// Creates a document handle from a Verse-bound live feed. public CultMeshDocumentHandle( CultMeshBoundLiveFeed feed, Func? replace = null, Func? submitPrediction = null) { _feed = feed ?? throw new ArgumentNullException(nameof(feed)); _replace = replace; _submitPrediction = submitPrediction; } /// Gets the semantic document id. public string DocumentId => _feed.FeedId; /// Gets the CLR document type presented by this handle. public Type DocumentType => typeof(TDocument); /// Gets the stable CultCache schema name. public string SchemaName => Descriptor.SchemaName; /// Gets the stable CultCache schema version. public string SchemaVersion => Descriptor.SchemaVersion; /// Gets the content-derived schema identifier. public string SchemaId => Descriptor.SchemaId; /// Gets the preferred or observed route for document access. public CultMeshRouteHint RouteHint => _feed.RouteHint; /// Gets typed state sources this document handle depends on, when known. public IReadOnlyList Sources => _feed.Sources; /// Gets the bound Verse context. public CultMeshVerseContext Context => _feed.Context; /// Gets the underlying live feed used by this document handle. public CultMeshLiveFeed Feed => _feed.Feed; /// Gets whether this handle can replace the underlying document value. public bool CanReplace => _replace != null; /// Gets whether this handle can submit a client prediction for the underlying document value. public bool CanSubmitPrediction => _submitPrediction != null; /// Gets whether this handle can accept a transparent document mutation. public bool CanSet => CanSubmitPrediction || CanReplace; /// Reads one coherent document snapshot. public Task LatestAsync() { return _feed.SnapshotAsync(CultMeshDocumentQueryParameters.Empty); } /// Reads the latest document snapshot synchronously for host APIs that cannot be async. public TDocument Latest() { return LatestAsync().ConfigureAwait(false).GetAwaiter().GetResult(); } /// Watches coherent document snapshots. public Observable Watch() { return _feed.Watch(CultMeshDocumentQueryParameters.Empty); } /// Subscribes to coherent document snapshots. public IDisposable Watch(Action onNext) { if (onNext == null) throw new ArgumentNullException(nameof(onNext)); return Watch().Subscribe(onNext); } /// Replaces the underlying document when this handle is backed by mutable state. public Task ReplaceAsync(TDocument value) { if (value == null) throw new ArgumentNullException(nameof(value)); if (_replace == null) { throw new NotSupportedException($"Document handle '{DocumentId}' is read-only."); } return _replace(value); } /// Submits a locally predicted document value when this handle is backed by client-authoritative state. public Task SubmitPredictionAsync(TDocument value) { if (value == null) throw new ArgumentNullException(nameof(value)); if (_submitPrediction == null) { throw new NotSupportedException($"Document handle '{DocumentId}' does not accept client predictions."); } return _submitPrediction(value); } /// /// Sets the document value through the configured authority shape. /// Prediction-backed documents publish a prediction; mutable documents replace authoritatively. /// public Task SetAsync(TDocument value) { if (value == null) throw new ArgumentNullException(nameof(value)); if (_submitPrediction != null) return _submitPrediction(value); if (_replace != null) return _replace(value); throw new NotSupportedException($"Document handle '{DocumentId}' does not accept mutations."); } /// Reads, updates, and sets the document value through the configured authority shape. public async Task UpdateAsync(Func update) { if (update == null) throw new ArgumentNullException(nameof(update)); var next = update(await LatestAsync().ConfigureAwait(false)); await SetAsync(next).ConfigureAwait(false); return next; } /// Reads, updates, and sets the document value through the configured authority shape. public async Task UpdateAsync(Func> update) { if (update == null) throw new ArgumentNullException(nameof(update)); var next = await update(await LatestAsync().ConfigureAwait(false)).ConfigureAwait(false); await SetAsync(next).ConfigureAwait(false); return next; } /// /// Creates a managed reactive document mirror whose local edits can be coalesced into one prediction or replacement. /// public async Task> ReactiveAsync( CultMeshReactiveDocumentOptions? options = null) { var current = CloneDocument(await LatestAsync().ConfigureAwait(false)); var reactive = new CultMeshReactiveDocument(this, current, options); reactive.Start(); return reactive; } /// /// Creates a managed reactive document mirror synchronously for host APIs that cannot be async. /// public CultMeshReactiveDocument Reactive(CultMeshReactiveDocumentOptions? options = null) { return ReactiveAsync(options).ConfigureAwait(false).GetAwaiter().GetResult(); } /// Creates a same-schema alias presentation for another CLR document type. public CultMeshDocumentHandle AsSchemaAlias() where TAlias : class { var aliasDescriptor = CultDocumentRegistry.Shared.GetRequired(); if (!string.Equals(Descriptor.SchemaName, aliasDescriptor.SchemaName, StringComparison.Ordinal) || !string.Equals(Descriptor.SchemaVersion, aliasDescriptor.SchemaVersion, StringComparison.Ordinal)) { throw new InvalidOperationException( $"Document type {typeof(TAlias).FullName} uses schema '{aliasDescriptor.SchemaName}' " + $"version '{aliasDescriptor.SchemaVersion}', but handle '{DocumentId}' exposes " + $"schema '{SchemaName}' version '{SchemaVersion}'."); } var aliasFeed = new CultMeshLiveFeed( DocumentId, async (parameters, context) => ConvertDocument( await Feed.SnapshotAsync(parameters, context).ConfigureAwait(false)), (parameters, context) => Feed .Watch(parameters, context) .Select(ConvertDocument), Sources, RouteHint); Func? replace = _replace == null ? null : value => _replace(ConvertDocument(value)); Func? submitPrediction = _submitPrediction == null ? null : value => _submitPrediction(ConvertDocument(value)); return new CultMeshDocumentHandle(aliasFeed.Bind(Context), replace, submitPrediction); } internal static TDocumentValue CloneDocument(TDocumentValue document) where TDocumentValue : class { if (document == null) throw new ArgumentNullException(nameof(document)); var payload = CultDocumentMessagePackSerialization.SerializeUntyped(document, typeof(TDocumentValue)); return (TDocumentValue)CultDocumentMessagePackSerialization.DeserializeUntyped( typeof(TDocumentValue), payload); } internal static TTarget ConvertDocument(TSource document) where TSource : class where TTarget : class { if (document == null) throw new ArgumentNullException(nameof(document)); if (document is TTarget alreadyTyped) { return alreadyTyped; } var payload = CultDocumentMessagePackSerialization.SerializeUntyped(document, typeof(TSource)); return (TTarget)CultDocumentMessagePackSerialization.DeserializeUntyped(typeof(TTarget), payload); } } /// /// Configures a managed CultMesh reactive document mirror. /// public sealed class CultMeshReactiveDocumentOptions { /// /// Gets or sets the write coalescing window. The default approximates a frame boundary for non-Unity hosts. /// public TimeSpan FlushDelay { get; set; } = TimeSpan.FromMilliseconds(16); /// Gets or sets whether canonical snapshots replace dirty local predictions immediately. public bool ReplaceDirtyCurrentOnCanonicalSnapshot { get; set; } } /// /// Captures a canonical snapshot that arrived while the local reactive document had an outstanding prediction. /// public sealed class CultMeshReactiveDocumentReconciliation where TDocument : class { internal CultMeshReactiveDocumentReconciliation( TDocument canonical, TDocument predicted, DateTimeOffset receivedAt) { Canonical = canonical; Predicted = predicted; ReceivedAt = receivedAt; } /// Gets the authoritative canonical document snapshot. public TDocument Canonical { get; } /// Gets the locally predicted document snapshot that was active when the canonical value arrived. public TDocument Predicted { get; } /// Gets when the canonical snapshot was received. public DateTimeOffset ReceivedAt { get; } } /// /// Managed typed document mirror that keeps an editable current value and coalesces local edits into CultMesh mutations. /// public sealed class CultMeshReactiveDocument : IDisposable where TDocument : class { private readonly CultMeshDocumentHandle _document; private readonly CultMeshReactiveDocumentOptions _options; private readonly object _gate = new(); private IDisposable? _subscription; private Timer? _timer; private bool _dirty; private bool _flushQueued; private bool _flushing; private bool _disposed; internal CultMeshReactiveDocument( CultMeshDocumentHandle document, TDocument current, CultMeshReactiveDocumentOptions? options) { _document = document ?? throw new ArgumentNullException(nameof(document)); Current = current ?? throw new ArgumentNullException(nameof(current)); _options = options ?? new CultMeshReactiveDocumentOptions(); } /// Gets the underlying document handle. public CultMeshDocumentHandle Document => _document; /// Gets the editable local document value. public TDocument Current { get; private set; } /// Gets whether local edits are waiting to be sent. public bool IsDirty { get { lock (_gate) return _dirty; } } /// Gets the most recent reconciliation snapshot, when a canonical value arrived during local prediction. public CultMeshReactiveDocumentReconciliation? Reconciliation { get; private set; } internal void Start() { _subscription = _document.Watch(ApplyCanonicalSnapshot); } /// Marks the current value dirty and schedules a coalesced prediction or replacement. public void MarkDirty() { ThrowIfDisposed(); lock (_gate) { _dirty = true; ScheduleFlushLocked(); } } /// Mutates the current value and schedules a coalesced prediction or replacement. public TDocument Update(Action update) { if (update == null) throw new ArgumentNullException(nameof(update)); ThrowIfDisposed(); lock (_gate) { update(Current); _dirty = true; ScheduleFlushLocked(); return Current; } } /// Replaces the current local value and schedules a coalesced prediction or replacement. public TDocument SetCurrent(TDocument value) { if (value == null) throw new ArgumentNullException(nameof(value)); ThrowIfDisposed(); lock (_gate) { Current = CultMeshDocumentHandle.CloneDocument(value); _dirty = true; ScheduleFlushLocked(); return Current; } } /// Reads a fresh canonical snapshot and adopts it as the current value. public async Task RefreshAsync() { ThrowIfDisposed(); var latest = CultMeshDocumentHandle.CloneDocument( await _document.LatestAsync().ConfigureAwait(false)); lock (_gate) { Current = latest; _dirty = false; Reconciliation = null; return Current; } } /// Immediately sends the latest local dirty value, if any, through the document authority shape. public async Task FlushAsync() { TDocument predicted; lock (_gate) { ThrowIfDisposed(); if (!_dirty) return; if (_flushing) { _flushQueued = true; return; } _timer?.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); _flushing = true; _dirty = false; predicted = CultMeshDocumentHandle.CloneDocument(Current); } try { await _document.SetAsync(predicted).ConfigureAwait(false); } finally { var shouldFlushAgain = false; lock (_gate) { _flushing = false; shouldFlushAgain = _flushQueued || _dirty; _flushQueued = false; } if (shouldFlushAgain) await FlushAsync().ConfigureAwait(false); } } /// Clears the most recent reconciliation metadata. public void ClearReconciliation() { ThrowIfDisposed(); Reconciliation = null; } /// public void Dispose() { lock (_gate) { if (_disposed) return; _disposed = true; } _subscription?.Dispose(); _timer?.Dispose(); } private void ApplyCanonicalSnapshot(TDocument canonical) { if (canonical == null) return; lock (_gate) { if (_disposed) return; var nextCanonical = CultMeshDocumentHandle.CloneDocument(canonical); if (_dirty || _flushing) { Reconciliation = new CultMeshReactiveDocumentReconciliation( nextCanonical, CultMeshDocumentHandle.CloneDocument(Current), DateTimeOffset.UtcNow); if (!_options.ReplaceDirtyCurrentOnCanonicalSnapshot) return; } Current = nextCanonical; Reconciliation = null; } } private void ScheduleFlushLocked() { if (_options.FlushDelay <= TimeSpan.Zero) { _ = Task.Run(FlushAsync); return; } _timer ??= new Timer(_ => _ = Task.Run(FlushAsync)); _timer.Change(_options.FlushDelay, Timeout.InfiniteTimeSpan); } private void ThrowIfDisposed() { if (_disposed) throw new ObjectDisposedException(nameof(CultMeshReactiveDocument)); } } /// /// Schema-aware lookup table for typed CultMesh document handles. /// public sealed class CultMeshDocumentCatalog { private readonly IReadOnlyList _documents; private readonly IReadOnlyDictionary _documentsByType; private readonly IReadOnlyDictionary _documentsBySchema; /// Creates a document catalog from known handles. public CultMeshDocumentCatalog(IEnumerable documents) { if (documents == null) throw new ArgumentNullException(nameof(documents)); var documentList = documents .Where(document => document != null) .ToArray(); var byType = new Dictionary(); var bySchema = new Dictionary(StringComparer.Ordinal); foreach (var document in documentList) { byType[document.DocumentType] = document; if (!string.IsNullOrWhiteSpace(document.SchemaVersion)) bySchema[document.SchemaVersion] = document; if (!string.IsNullOrWhiteSpace(document.SchemaName)) bySchema[document.SchemaName] = document; } _documents = documentList; _documentsByType = byType; _documentsBySchema = bySchema; } /// Gets the handles in this catalog. public IReadOnlyList Documents => _documents; /// Looks up a document handle by shared schema name or schema version. public bool TryGetDocumentBySchema( string schema, out ICultMeshDocumentHandle document) { if (!string.IsNullOrWhiteSpace(schema) && _documentsBySchema.TryGetValue(schema, out document!)) { return true; } document = null!; return false; } /// Looks up a document handle by shared schema name or schema version. public ICultMeshDocumentHandle DocumentBySchema(string schema) { if (TryGetDocumentBySchema(schema, out var document)) return document; throw new NotSupportedException( $"CultMesh document catalog does not expose a document for schema '{schema}'."); } /// Looks up a typed document handle by CLR type or same-schema alias. public bool TryGetDocument( out CultMeshDocumentHandle document) where TDocument : class { if (_documentsByType.TryGetValue(typeof(TDocument), out var untypedDocument) && untypedDocument is CultMeshDocumentHandle typedDocument) { document = typedDocument; return true; } var descriptor = CultDocumentRegistry.Shared.GetRequired(); if (_documentsBySchema.TryGetValue(descriptor.SchemaVersion, out var schemaDocument) || _documentsBySchema.TryGetValue(descriptor.SchemaName, out schemaDocument!)) { if (schemaDocument is CultMeshDocumentHandle schemaTypedDocument) { document = schemaTypedDocument; return true; } document = schemaDocument.AsSchemaAlias(); return true; } document = null!; return false; } /// Looks up a typed document handle by CLR type or same-schema alias. public CultMeshDocumentHandle Document() where TDocument : class { if (TryGetDocument(out var document)) return document; throw new NotSupportedException( $"CultMesh document catalog does not expose a document for {typeof(TDocument).FullName}."); } /// Reads one typed document snapshot by CLR type or same-schema alias. public Task LatestAsync() where TDocument : class { return Document().LatestAsync(); } /// Reads one typed document synchronously by CLR type or same-schema alias. public TDocument Latest() where TDocument : class { return Document().Latest(); } /// Gets whether one typed document can be replaced by CLR type or same-schema alias. public bool CanReplace() where TDocument : class { return TryGetDocument(out var document) && document.CanReplace; } /// Replaces one typed document by CLR type or same-schema alias. public Task ReplaceAsync(TDocument value) where TDocument : class { return Document().ReplaceAsync(value); } /// Gets whether one typed document can submit client predictions by CLR type or same-schema alias. public bool CanSubmitPrediction() where TDocument : class { return TryGetDocument(out var document) && document.CanSubmitPrediction; } /// Gets whether one typed document can accept transparent mutations by CLR type or same-schema alias. public bool CanSet() where TDocument : class { return TryGetDocument(out var document) && document.CanSet; } /// Submits a client prediction for one typed document by CLR type or same-schema alias. public Task SubmitPredictionAsync(TDocument value) where TDocument : class { return Document().SubmitPredictionAsync(value); } /// Sets one typed document through its configured authority shape by CLR type or same-schema alias. public Task SetAsync(TDocument value) where TDocument : class { return Document().SetAsync(value); } /// Reads, updates, and sets one typed document through its configured authority shape. public Task UpdateAsync(Func update) where TDocument : class { return Document().UpdateAsync(update); } /// Reads, updates, and sets one typed document through its configured authority shape. public Task UpdateAsync(Func> update) where TDocument : class { return Document().UpdateAsync(update); } /// Watches one typed document by CLR type or same-schema alias. public Observable Watch() where TDocument : class { return Document().Watch(); } /// Subscribes to one typed document by CLR type or same-schema alias. public IDisposable Watch(Action onNext) where TDocument : class { return Document().Watch(onNext); } /// Creates a managed reactive document mirror by CLR type or same-schema alias. public Task> ReactiveAsync( CultMeshReactiveDocumentOptions? options = null) where TDocument : class { return Document().ReactiveAsync(options); } /// Creates a managed reactive document mirror synchronously by CLR type or same-schema alias. public CultMeshReactiveDocument Reactive( CultMeshReactiveDocumentOptions? options = null) where TDocument : class { return Document().Reactive(options); } } /// /// Describes how a CultMesh collection document changed. /// public enum CultMeshCollectionChangeKind { /// A document was added. Added, /// A document was updated. Updated, /// A document was removed. Removed, /// A local prediction was published before authority accepted it. Predicted, /// A predicted document was reconciled with authority. Reconciled, /// A document was accepted through schema migration. SchemaMigrated, /// A document change was rejected. Rejected } /// /// One typed document change observed through a CultMesh collection handle. /// public sealed class CultMeshCollectionChange where TDocument : class { /// Creates a collection change. public CultMeshCollectionChange( CultMeshCollectionChangeKind kind, CultRecordKey key, string schemaId, TDocument? document, TDocument? previousDocument, string? message = null) { Kind = kind; Key = key; SchemaId = schemaId ?? ""; Document = document; PreviousDocument = previousDocument; Message = message; } /// Gets the change kind. public CultMeshCollectionChangeKind Kind { get; } /// Gets the changed record key. public CultRecordKey Key { get; } /// Gets the schema id of the changed document. public string SchemaId { get; } /// Gets the current document, when present. public TDocument? Document { get; } /// Gets the previous document, when present. public TDocument? PreviousDocument { get; } /// Gets an optional rejection or diagnostic message. public string? Message { get; } } /// /// Typed reactive collection handle with schema-aware alias conversion. /// public sealed class CultMeshCollectionHandle where TDocument : class { private static readonly CultDocumentDescriptor Descriptor = CultDocumentRegistry.Shared.GetRequired(); private readonly Func>> _latest; private readonly Func>> _watchChanges; /// Creates a collection handle. public CultMeshCollectionHandle( string collectionId, Func>> latest, Func>> watchChanges, IEnumerable? sources = null, CultMeshRouteHint? routeHint = null) { CollectionId = string.IsNullOrWhiteSpace(collectionId) ? throw new ArgumentException("Value must be non-empty.", nameof(collectionId)) : collectionId; _latest = latest ?? throw new ArgumentNullException(nameof(latest)); _watchChanges = watchChanges ?? throw new ArgumentNullException(nameof(watchChanges)); Sources = sources?.ToArray() ?? Array.Empty(); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; } /// Gets the semantic collection id. public string CollectionId { get; } /// Gets the CLR document type presented by this collection. public Type DocumentType => typeof(TDocument); /// Gets the stable CultCache schema name. public string SchemaName => Descriptor.SchemaName; /// Gets the stable CultCache schema version. public string SchemaVersion => Descriptor.SchemaVersion; /// Gets the content-derived schema identifier. public string SchemaId => Descriptor.SchemaId; /// Gets the preferred or observed route for collection access. public CultMeshRouteHint RouteHint { get; } /// Gets typed state sources this collection handle depends on, when known. public IReadOnlyList Sources { get; } /// Reads one coherent collection snapshot. public Task> LatestAsync() { return _latest(); } /// Watches typed collection changes. public Observable> WatchChanges() { return _watchChanges(); } /// Subscribes to typed collection changes. public IDisposable WatchChanges(Action> onNext) { if (onNext == null) throw new ArgumentNullException(nameof(onNext)); return WatchChanges().Subscribe(onNext); } /// Creates a same-schema alias presentation for another CLR document type. public CultMeshCollectionHandle AsSchemaAlias() where TAlias : class { var aliasDescriptor = CultDocumentRegistry.Shared.GetRequired(); if (!string.Equals(Descriptor.SchemaName, aliasDescriptor.SchemaName, StringComparison.Ordinal) || !string.Equals(Descriptor.SchemaVersion, aliasDescriptor.SchemaVersion, StringComparison.Ordinal)) { throw new InvalidOperationException( $"Document type {typeof(TAlias).FullName} uses schema '{aliasDescriptor.SchemaName}' " + $"version '{aliasDescriptor.SchemaVersion}', but collection '{CollectionId}' exposes " + $"schema '{SchemaName}' version '{SchemaVersion}'."); } return new CultMeshCollectionHandle( CollectionId, async () => (await LatestAsync().ConfigureAwait(false)) .Select(ConvertDocument) .ToArray(), () => WatchChanges().Select(change => new CultMeshCollectionChange( change.Kind, change.Key, change.SchemaId, change.Document == null ? null : ConvertDocument(change.Document), change.PreviousDocument == null ? null : ConvertDocument(change.PreviousDocument), change.Message)), Sources, RouteHint); } private static TTarget ConvertDocument(TSource document) where TSource : class where TTarget : class { if (document == null) throw new ArgumentNullException(nameof(document)); if (document is TTarget alreadyTyped) { return alreadyTyped; } var payload = CultDocumentMessagePackSerialization.SerializeUntyped(document, typeof(TSource)); return (TTarget)CultDocumentMessagePackSerialization.DeserializeUntyped(typeof(TTarget), payload); } } /// /// Inspectable metadata for a typed live feed surface. /// public sealed class CultMeshLiveFeedDiagnostic { /// Creates live feed diagnostics. public CultMeshLiveFeedDiagnostic( string feedId, CultMeshRouteHint? routeHint = null, IEnumerable? sources = null) { FeedId = RequireNonEmpty(feedId, nameof(feedId)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; Sources = sources?.ToArray() ?? Array.Empty(); } /// Gets the semantic feed id. public string FeedId { get; } /// Gets the preferred or observed route for the feed. public CultMeshRouteHint RouteHint { get; } /// Gets the typed state sources this live feed depends on, when known. public IReadOnlyList Sources { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Inspectable metadata for a typed query surface. /// public sealed class CultMeshQuerySurfaceDiagnostic { /// Creates query surface diagnostics. public CultMeshQuerySurfaceDiagnostic( string queryId, CultMeshRouteHint? routeHint = null, IEnumerable? sources = null) { QueryId = RequireNonEmpty(queryId, nameof(queryId)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; Sources = sources?.ToArray() ?? Array.Empty(); } /// Gets the semantic query id. public string QueryId { get; } /// Gets the preferred or observed route for the query. public CultMeshRouteHint RouteHint { get; } /// Gets the typed state sources this query depends on, when known. public IReadOnlyList Sources { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Inspectable metadata for a typed operation handle. /// public sealed class CultMeshOperationHandleDiagnostic { /// Creates operation handle diagnostics. public CultMeshOperationHandleDiagnostic(string operationId) { OperationId = RequireNonEmpty(operationId, nameof(operationId)); } /// Gets the semantic operation id. public string OperationId { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Inspectable metadata for a typed state pointer. /// public sealed class CultMeshStatePointerDiagnostic { /// Creates state pointer diagnostics. public CultMeshStatePointerDiagnostic( string pointerId, CultMeshRouteHint? routeHint = null, IEnumerable? sources = null) { PointerId = RequireNonEmpty(pointerId, nameof(pointerId)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; Sources = sources?.ToArray() ?? Array.Empty(); } /// Gets the semantic pointer id. public string PointerId { get; } /// Gets the preferred or observed route for pointer resolution. public CultMeshRouteHint RouteHint { get; } /// Gets typed state sources this pointer depends on, when known. public IReadOnlyList Sources { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Inspectable metadata for a typed projection recipe. /// public sealed class CultMeshProjectionRecipeDiagnostic { /// Creates projection recipe diagnostics. public CultMeshProjectionRecipeDiagnostic( string projectionId, CultMeshRouteHint? routeHint = null, IEnumerable? sources = null) { ProjectionId = RequireNonEmpty(projectionId, nameof(projectionId)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; Sources = sources?.ToArray() ?? Array.Empty(); } /// Gets the semantic projection id. public string ProjectionId { get; } /// Gets the preferred or observed route for the projection. public CultMeshRouteHint RouteHint { get; } /// Gets the typed state sources this projection depends on, when known. public IReadOnlyList Sources { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Names the kind of typed surface described in a CultMesh surface catalog. /// public enum CultMeshSurfaceKind { /// A typed derived-state query surface. Query, /// A reusable typed projection recipe. ProjectionRecipe, /// A coherent live view feed. LiveFeed, /// A typed operation handle. Operation, /// A typed reactive document handle. Document, /// A typed reactive collection handle. Collection, /// A typed pointer to Verse state. StatePointer, /// A native slice or slab view. NativeSliceView } /// /// One inspectable typed surface advertised by a runtime. /// public sealed class CultMeshSurfaceDiagnostic { /// Creates a surface diagnostic. public CultMeshSurfaceDiagnostic( CultMeshSurfaceKind kind, string surfaceId, CultMeshRouteHint? routeHint = null, IEnumerable? sources = null) { Kind = kind; SurfaceId = RequireNonEmpty(surfaceId, nameof(surfaceId)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; Sources = sources?.ToArray() ?? Array.Empty(); } /// Gets the kind of surface. public CultMeshSurfaceKind Kind { get; } /// Gets the semantic surface id. public string SurfaceId { get; } /// Gets the preferred or observed route for the surface. public CultMeshRouteHint RouteHint { get; } /// Gets typed state sources this surface depends on, when known. public IReadOnlyList Sources { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Inspectable catalog of typed surfaces exposed by a runtime or generated binding. /// public sealed class CultMeshSurfaceCatalogDiagnostic { /// Creates a surface catalog diagnostic. public CultMeshSurfaceCatalogDiagnostic( string catalogId, IEnumerable surfaces) { CatalogId = RequireNonEmpty(catalogId, nameof(catalogId)); Surfaces = (surfaces ?? throw new ArgumentNullException(nameof(surfaces))).ToArray(); } /// Gets the semantic catalog id. public string CatalogId { get; } /// Gets the advertised typed surfaces. public IReadOnlyList Surfaces { get; } /// Finds a surface by semantic id. public CultMeshSurfaceDiagnostic? Find(string surfaceId) { if (string.IsNullOrWhiteSpace(surfaceId)) return null; return Surfaces.FirstOrDefault(surface => string.Equals(surface.SurfaceId, surfaceId, StringComparison.Ordinal)); } /// Finds surfaces by kind in catalog order. public IReadOnlyList FindByKind(CultMeshSurfaceKind kind) { return Surfaces.Where(surface => surface.Kind == kind).ToArray(); } /// Groups surfaces by kind for generated bindings and tooling. public CultMeshSurfaceCatalogIndexDiagnostic IndexByKind() { return new CultMeshSurfaceCatalogIndexDiagnostic(this); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Kind-indexed view of a typed surface catalog for generated bindings, UI runtimes, and tools. /// public sealed class CultMeshSurfaceCatalogIndexDiagnostic { /// Creates a kind-indexed catalog diagnostic. public CultMeshSurfaceCatalogIndexDiagnostic(CultMeshSurfaceCatalogDiagnostic catalog) { if (catalog == null) throw new ArgumentNullException(nameof(catalog)); CatalogId = catalog.CatalogId; Queries = catalog.FindByKind(CultMeshSurfaceKind.Query); ProjectionRecipes = catalog.FindByKind(CultMeshSurfaceKind.ProjectionRecipe); LiveFeeds = catalog.FindByKind(CultMeshSurfaceKind.LiveFeed); Operations = catalog.FindByKind(CultMeshSurfaceKind.Operation); Documents = catalog.FindByKind(CultMeshSurfaceKind.Document); Collections = catalog.FindByKind(CultMeshSurfaceKind.Collection); StatePointers = catalog.FindByKind(CultMeshSurfaceKind.StatePointer); NativeSliceViews = catalog.FindByKind(CultMeshSurfaceKind.NativeSliceView); } /// Gets the semantic catalog id. public string CatalogId { get; } /// Gets typed derived-state query surfaces. public IReadOnlyList Queries { get; } /// Gets reusable typed projection recipes. public IReadOnlyList ProjectionRecipes { get; } /// Gets coherent live view feeds. public IReadOnlyList LiveFeeds { get; } /// Gets typed operation handles. public IReadOnlyList Operations { get; } /// Gets typed reactive document handles. public IReadOnlyList Documents { get; } /// Gets typed reactive collection handles. public IReadOnlyList Collections { get; } /// Gets typed pointers to Verse state. public IReadOnlyList StatePointers { get; } /// Gets native slice or slab views. public IReadOnlyList NativeSliceViews { get; } } /// /// Options for a polling watch fallback over a typed query or live feed snapshot. /// public sealed class CultMeshPollingWatchOptions { /// Creates polling watch options. public CultMeshPollingWatchOptions( TimeSpan? interval = null, bool emitInitial = true, IEqualityComparer? comparer = null) { Interval = interval ?? TimeSpan.FromMilliseconds(50); if (Interval <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(interval), "Polling interval must be positive."); } EmitInitial = emitInitial; Comparer = comparer ?? EqualityComparer.Default; } /// Gets the polling interval. public TimeSpan Interval { get; } /// Gets whether the first sampled value should be emitted. public bool EmitInitial { get; } /// Gets the equality comparer used to suppress unchanged samples. public IEqualityComparer Comparer { get; } } /// /// Timer-backed fallback watcher for runtimes that do not yet have native reactive transport. /// internal sealed class CultMeshPollingWatcher : IDisposable { private readonly Func> _sample; private readonly CultMeshPollingWatchOptions _options; private readonly Subject _subject = new(); private readonly Timer _timer; private readonly object _gate = new(); private readonly TParameters _parameters; private readonly CultMeshQueryContext _context; private TResult? _last; private bool _hasLast; private bool _running; private bool _disposed; public CultMeshPollingWatcher( Func> sample, TParameters parameters, CultMeshQueryContext context, CultMeshPollingWatchOptions options) { _sample = sample ?? throw new ArgumentNullException(nameof(sample)); _parameters = parameters; _context = context ?? throw new ArgumentNullException(nameof(context)); _options = options ?? throw new ArgumentNullException(nameof(options)); _timer = new Timer(OnTimer, null, TimeSpan.Zero, _options.Interval); } public Observable Observable => _subject; public void Dispose() { lock (_gate) { if (_disposed) { return; } _disposed = true; } _timer.Dispose(); _subject.Dispose(); } private void OnTimer(object? _) { lock (_gate) { if (_disposed || _running) { return; } _running = true; } _ = SampleAsync(); } private async Task SampleAsync() { try { var next = await _sample(_parameters, _context).ConfigureAwait(false); var shouldEmit = false; lock (_gate) { if (_disposed) { return; } shouldEmit = _hasLast ? !_options.Comparer.Equals(_last!, next) : _options.EmitInitial; _last = next; _hasLast = true; } if (shouldEmit) { _subject.OnNext(next); } } finally { lock (_gate) { _running = false; } } } } internal sealed class CultMeshCompositeDisposable : IDisposable { private readonly IDisposable[] _disposables; private int _disposed; public CultMeshCompositeDisposable(params IDisposable[] disposables) { _disposables = disposables ?? throw new ArgumentNullException(nameof(disposables)); } public void Dispose() { if (Interlocked.Exchange(ref _disposed, 1) != 0) { return; } foreach (var disposable in _disposables) { disposable.Dispose(); } } } /// /// Typed pointer to Verse state that UI surfaces and tools can resolve and watch. /// public sealed class CultMeshStatePointer { private readonly Func> _resolve; private readonly Func> _watch; /// Creates a state pointer. public CultMeshStatePointer( string pointerId, Func> resolve, Func> watch, CultMeshRouteHint? routeHint = null, IEnumerable? sources = null) : this( pointerId, _ => (resolve ?? throw new ArgumentNullException(nameof(resolve)))(), _ => (watch ?? throw new ArgumentNullException(nameof(watch)))(), routeHint, sources) { } /// Creates a state pointer that can resolve through a Verse query context. public CultMeshStatePointer( string pointerId, Func> resolve, Func> watch, CultMeshRouteHint? routeHint = null, IEnumerable? sources = null) { PointerId = RequireNonEmpty(pointerId, nameof(pointerId)); _resolve = resolve ?? throw new ArgumentNullException(nameof(resolve)); _watch = watch ?? throw new ArgumentNullException(nameof(watch)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; Sources = sources?.ToArray() ?? Array.Empty(); } /// Gets the semantic pointer id. public string PointerId { get; } /// Gets the preferred or observed route for pointer resolution. public CultMeshRouteHint RouteHint { get; } /// Gets typed state sources this pointer depends on, when known. public IReadOnlyList Sources { get; } /// Resolves the pointer once. public Task ResolveAsync() { return ResolveAsync(CultMeshQueryContext.ForRuntime("local")); } /// Resolves the pointer once through a query context. public Task ResolveAsync(CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return _resolve(ResolveContext(context)); } /// Resolves the pointer once for one runtime using default context. public Task ResolveAsync(string runtimeId) { return ResolveAsync(CultMeshQueryContext.ForRuntime(runtimeId)); } /// Watches resolved values for this pointer. public Observable Watch() { return Watch(CultMeshQueryContext.ForRuntime("local")); } /// Watches resolved values for this pointer through a query context. public Observable Watch(CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return _watch(ResolveContext(context)); } /// Binds this pointer to a Verse context. public CultMeshBoundStatePointer Bind(CultMeshVerseContext context) { return new CultMeshBoundStatePointer(context, this); } /// Binds this pointer to a Verse. public CultMeshBoundStatePointer Bind(CultMeshVerse verse) { if (verse == null) throw new ArgumentNullException(nameof(verse)); return Bind(verse.Context); } private CultMeshQueryContext ResolveContext(CultMeshQueryContext context) { return context.RouteHint.Kind == CultMeshLocalityKind.Automatic && RouteHint.Kind != CultMeshLocalityKind.Automatic ? context.WithRoute(RouteHint) : context; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// State pointer pre-bound to a Verse so UI/tool runtimes can resolve state without context plumbing. /// public sealed class CultMeshBoundStatePointer { /// Creates a Verse-bound state pointer. public CultMeshBoundStatePointer( CultMeshVerseContext context, CultMeshStatePointer pointer) { Context = context ?? throw new ArgumentNullException(nameof(context)); Pointer = pointer ?? throw new ArgumentNullException(nameof(pointer)); } /// Gets the bound Verse context. public CultMeshVerseContext Context { get; } /// Gets the underlying state pointer. public CultMeshStatePointer Pointer { get; } /// Gets the semantic pointer id. public string PointerId => Pointer.PointerId; /// Gets typed state sources this pointer depends on, when known. public IReadOnlyList Sources => Pointer.Sources; /// Gets the preferred or observed route for pointer resolution. public CultMeshRouteHint RouteHint => Pointer.RouteHint; /// Resolves the pointer through the bound Verse. public Task ResolveAsync() { return Pointer.ResolveAsync(Context.QueryContext()); } /// Watches the pointer through the bound Verse. public Observable Watch() { return Pointer.Watch(Context.QueryContext()); } } /// /// Typed pointer to Verse state that UI surfaces and tools can resolve, watch, and replace. /// public sealed class CultMeshMutableStatePointer { private readonly Func> _resolve; private readonly Func> _watch; private readonly Func _replace; /// Creates a mutable state pointer. public CultMeshMutableStatePointer( string pointerId, Func> resolve, Func> watch, Func replace, CultMeshRouteHint? routeHint = null, IEnumerable? sources = null) : this( pointerId, _ => (resolve ?? throw new ArgumentNullException(nameof(resolve)))(), _ => (watch ?? throw new ArgumentNullException(nameof(watch)))(), (_, value) => (replace ?? throw new ArgumentNullException(nameof(replace)))(value), routeHint, sources) { } /// Creates a mutable state pointer that can operate through a Verse query context. public CultMeshMutableStatePointer( string pointerId, Func> resolve, Func> watch, Func replace, CultMeshRouteHint? routeHint = null, IEnumerable? sources = null) { PointerId = RequireNonEmpty(pointerId, nameof(pointerId)); _resolve = resolve ?? throw new ArgumentNullException(nameof(resolve)); _watch = watch ?? throw new ArgumentNullException(nameof(watch)); _replace = replace ?? throw new ArgumentNullException(nameof(replace)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; Sources = sources?.ToArray() ?? Array.Empty(); } /// Gets the semantic pointer id. public string PointerId { get; } /// Gets the preferred or observed route for pointer access. public CultMeshRouteHint RouteHint { get; } /// Gets typed state sources this pointer depends on, when known. public IReadOnlyList Sources { get; } /// Resolves the pointer once. public Task ResolveAsync() { return ResolveAsync(CultMeshQueryContext.ForRuntime("local")); } /// Resolves the pointer once. public Task ReadAsync() { return ResolveAsync(); } /// Resolves the pointer once through a query context. public Task ResolveAsync(CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return _resolve(ResolveContext(context)); } /// Resolves the pointer once through a query context. public Task ReadAsync(CultMeshQueryContext context) { return ResolveAsync(context); } /// Resolves the pointer once for one runtime using default context. public Task ResolveAsync(string runtimeId) { return ResolveAsync(CultMeshQueryContext.ForRuntime(runtimeId)); } /// Resolves the pointer once for one runtime using default context. public Task ReadAsync(string runtimeId) { return ResolveAsync(runtimeId); } /// Replaces the pointed state value. public Task ReplaceAsync(T value) { return ReplaceAsync(value, CultMeshQueryContext.ForRuntime("local")); } /// Replaces the pointed state value through a query context. public Task ReplaceAsync(T value, CultMeshQueryContext context) { if (value == null) throw new ArgumentNullException(nameof(value)); if (context == null) throw new ArgumentNullException(nameof(context)); return _replace(ResolveContext(context), value); } /// Replaces the pointed state value for one runtime using default context. public Task ReplaceAsync(T value, string runtimeId) { return ReplaceAsync(value, CultMeshQueryContext.ForRuntime(runtimeId)); } /// Watches resolved values for this pointer. public Observable Watch() { return Watch(CultMeshQueryContext.ForRuntime("local")); } /// Watches resolved values for this pointer through a query context. public Observable Watch(CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return _watch(ResolveContext(context)); } /// Views this mutable pointer as a read/watch state pointer. public CultMeshStatePointer AsStatePointer() { return new CultMeshStatePointer( PointerId, (Func>)ResolveAsync, (Func>)Watch, RouteHint, Sources); } /// Binds this pointer to a Verse context. public CultMeshBoundMutableStatePointer Bind(CultMeshVerseContext context) { return new CultMeshBoundMutableStatePointer(context, this); } /// Binds this pointer to a Verse. public CultMeshBoundMutableStatePointer Bind(CultMeshVerse verse) { if (verse == null) throw new ArgumentNullException(nameof(verse)); return Bind(verse.Context); } private CultMeshQueryContext ResolveContext(CultMeshQueryContext context) { return context.RouteHint.Kind == CultMeshLocalityKind.Automatic && RouteHint.Kind != CultMeshLocalityKind.Automatic ? context.WithRoute(RouteHint) : context; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Mutable state pointer pre-bound to a Verse so UI/tool runtimes can read, watch, and replace state without context plumbing. /// public sealed class CultMeshBoundMutableStatePointer { /// Creates a Verse-bound mutable state pointer. public CultMeshBoundMutableStatePointer( CultMeshVerseContext context, CultMeshMutableStatePointer pointer) { Context = context ?? throw new ArgumentNullException(nameof(context)); Pointer = pointer ?? throw new ArgumentNullException(nameof(pointer)); } /// Gets the bound Verse context. public CultMeshVerseContext Context { get; } /// Gets the underlying mutable state pointer. public CultMeshMutableStatePointer Pointer { get; } /// Gets the semantic pointer id. public string PointerId => Pointer.PointerId; /// Gets typed state sources this pointer depends on, when known. public IReadOnlyList Sources => Pointer.Sources; /// Gets the preferred or observed route for pointer access. public CultMeshRouteHint RouteHint => Pointer.RouteHint; /// Resolves the pointer through the bound Verse. public Task ResolveAsync() { return Pointer.ResolveAsync(Context.QueryContext()); } /// Resolves the pointer through the bound Verse. public Task ReadAsync() { return ResolveAsync(); } /// Replaces the pointer value through the bound Verse. public Task ReplaceAsync(T value) { return Pointer.ReplaceAsync(value, Context.QueryContext()); } /// Watches the pointer through the bound Verse. public Observable Watch() { return Pointer.Watch(Context.QueryContext()); } /// Views this mutable pointer as a read/watch state pointer bound to the same Verse. public CultMeshBoundStatePointer AsStatePointer() { return new CultMeshBoundStatePointer(Context, Pointer.AsStatePointer()); } } /// /// Describes one typed source consumed by a projection recipe. /// public sealed class CultMeshProjectionSource { /// Creates a projection source descriptor. public CultMeshProjectionSource(string sourceId, string? schemaId = null, string? description = null) { SourceId = RequireNonEmpty(sourceId, nameof(sourceId)); SchemaId = schemaId; Description = description; } /// Gets the semantic source id, such as a state pointer, document handle, or native view id. public string SourceId { get; } /// Gets the optional schema id for the source. public string? SchemaId { get; } /// Gets optional human-facing source diagnostics. public string? Description { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Resolves string state references embedded in portable UI/tool surfaces through a named CultMesh resolver. /// public sealed class CultMeshStateRefResolver { private readonly Func _resolve; /// Creates a state-reference resolver. public CultMeshStateRefResolver( string resolverId, Func resolve, IEnumerable? sources = null, CultMeshRouteHint? routeHint = null) { ResolverId = RequireNonEmpty(resolverId, nameof(resolverId)); _resolve = resolve ?? throw new ArgumentNullException(nameof(resolve)); Sources = sources?.ToArray() ?? Array.Empty(); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; } /// Gets the semantic resolver id. public string ResolverId { get; } /// Gets the source state read by the resolver. public IReadOnlyList Sources { get; } /// Gets the preferred route for resolving state refs. public CultMeshRouteHint RouteHint { get; } /// Gets an empty resolver that always returns an empty string. public static CultMeshStateRefResolver Empty { get; } = new("cultmesh.state_refs.empty", (_stateRef, _context) => ""); /// Resolves a state ref using a local query context. public string Resolve(string stateRef) { return Resolve(stateRef, CultMeshQueryContext.ForRuntime("local")); } /// Resolves a state ref using a runtime id. public string Resolve(string stateRef, string runtimeId) { return Resolve(stateRef, CultMeshQueryContext.ForRuntime(runtimeId)); } /// Resolves a state ref using an explicit query context. public string Resolve(string stateRef, CultMeshQueryContext context) { if (string.IsNullOrWhiteSpace(stateRef)) return ""; if (context == null) throw new ArgumentNullException(nameof(context)); return _resolve(stateRef, ResolveContext(context)) ?? ""; } /// Attempts to resolve a non-empty value. public bool TryResolve(string stateRef, out string value) { value = Resolve(stateRef); return !string.IsNullOrEmpty(value); } /// Attempts to resolve a non-empty value. public bool TryResolve(string stateRef, CultMeshQueryContext context, out string value) { value = Resolve(stateRef, context); return !string.IsNullOrEmpty(value); } /// Creates a resolver that falls back to another resolver when this resolver returns empty. public CultMeshStateRefResolver Or(CultMeshStateRefResolver fallback) { if (fallback == null) throw new ArgumentNullException(nameof(fallback)); return new CultMeshStateRefResolver( ResolverId + "|" + fallback.ResolverId, (stateRef, context) => { var value = Resolve(stateRef, context); return string.IsNullOrEmpty(value) ? fallback.Resolve(stateRef, context) : value; }, Sources.Concat(fallback.Sources), RouteHint.Kind == CultMeshLocalityKind.Automatic ? fallback.RouteHint : RouteHint); } /// Returns the legacy function shape for existing surface renderers. public Func AsFunc() { return Resolve; } private CultMeshQueryContext ResolveContext(CultMeshQueryContext context) { if (context.RouteHint.Kind != CultMeshLocalityKind.Automatic || RouteHint.Kind == CultMeshLocalityKind.Automatic) return context; return context.WithRoute(RouteHint); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Diagnostics for a named CultMesh state-reference resolver. /// public sealed class CultMeshStateRefResolverDiagnostic { /// Creates a state-reference resolver diagnostic. public CultMeshStateRefResolverDiagnostic( string resolverId, CultMeshRouteHint routeHint, IEnumerable? sources = null) { ResolverId = RequireNonEmpty(resolverId, nameof(resolverId)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; Sources = sources?.ToArray() ?? Array.Empty(); } /// Gets the semantic resolver id. public string ResolverId { get; } /// Gets the preferred route for resolving refs. public CultMeshRouteHint RouteHint { get; } /// Gets source state used by the resolver. public IReadOnlyList Sources { get; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Describes one UI/tool surface property bound to a typed CultMesh state pointer. /// public sealed class CultMeshStateBindingDescriptor { /// Creates a state binding descriptor. public CultMeshStateBindingDescriptor( string targetProp, string pointerId, string? sourceId = null, string? schemaId = null, CultMeshRouteHint? routeHint = null) { TargetProp = RequireNonEmpty(targetProp, nameof(targetProp)); PointerId = RequireNonEmpty(pointerId, nameof(pointerId)); SourceId = sourceId; SchemaId = schemaId; RouteHint = routeHint ?? CultMeshRouteHint.Automatic; } /// Gets the component property receiving the resolved state value. public string TargetProp { get; } /// Gets the semantic CultMesh state pointer id. public string PointerId { get; } /// Gets the optional source document, cache, native view, or daemon source id. public string? SourceId { get; } /// Gets the optional schema id for the source state. public string? SchemaId { get; } /// Gets the preferred route for resolving and watching the binding. public CultMeshRouteHint RouteHint { get; } /// Creates a binding from an existing state pointer. public static CultMeshStateBindingDescriptor FromPointer( string targetProp, CultMeshStatePointer pointer) { if (pointer == null) throw new ArgumentNullException(nameof(pointer)); var source = pointer.Sources.FirstOrDefault(); return new CultMeshStateBindingDescriptor( targetProp, pointer.PointerId, source?.SourceId, source?.SchemaId, pointer.RouteHint); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Transport-friendly fields for a typed state binding descriptor. /// public sealed class CultMeshStateBindingRecord { /// Creates a state binding record. public CultMeshStateBindingRecord( string? targetProp = null, string? pointerId = null, string? sourceId = null, string? schemaId = null, string? routeKind = null, string? routeDescription = null) { TargetProp = string.IsNullOrWhiteSpace(targetProp) ? "value" : targetProp!; PointerId = pointerId ?? ""; SourceId = sourceId ?? ""; SchemaId = schemaId ?? ""; RouteKind = routeKind ?? ""; RouteDescription = routeDescription ?? ""; } /// Gets the component property receiving the resolved state value. public string TargetProp { get; } /// Gets the semantic CultMesh state pointer id. public string PointerId { get; } /// Gets the source document, cache, native view, or daemon source id. public string SourceId { get; } /// Gets the source schema id. public string SchemaId { get; } /// Gets the flattened route kind. public string RouteKind { get; } /// Gets the flattened route description. public string RouteDescription { get; } /// Creates transport-friendly fields from a state binding descriptor. public static CultMeshStateBindingRecord FromBinding(CultMeshStateBindingDescriptor? binding) { var route = CultMeshRouteRecord.FromRoute(binding?.RouteHint); return new CultMeshStateBindingRecord( binding?.TargetProp, binding?.PointerId, binding?.SourceId, binding?.SchemaId, route.Kind, route.Description); } /// Rehydrates this record as a state binding descriptor. public CultMeshStateBindingDescriptor ToBinding( CultMeshRouteHint? fallbackRouteHint = null, string? fallbackTargetProp = null) { var targetProp = string.IsNullOrWhiteSpace(TargetProp) ? (string.IsNullOrWhiteSpace(fallbackTargetProp) ? "value" : fallbackTargetProp!) : TargetProp; var pointerId = string.IsNullOrWhiteSpace(PointerId) ? $"{targetProp}.unknown" : PointerId; var route = new CultMeshRouteRecord(RouteKind, RouteDescription) .ToRoute(fallbackRouteHint ?? CultMeshRouteHint.Automatic); return new CultMeshStateBindingDescriptor( targetProp, pointerId, SourceId, SchemaId, route); } } /// /// Describes one UI/tool command bound to a typed CultMesh operation. /// public sealed class CultMeshOperationBindingDescriptor { /// Creates an operation binding descriptor. public CultMeshOperationBindingDescriptor( string operationId, string? label = null, string? schemaId = null, CultMeshRouteHint? routeHint = null) { OperationId = RequireNonEmpty(operationId, nameof(operationId)); Label = label ?? ""; SchemaId = schemaId ?? ""; RouteHint = routeHint ?? CultMeshRouteHint.Automatic; } /// Gets the semantic CultMesh operation id. public string OperationId { get; } /// Gets the human-facing command label. public string Label { get; } /// Gets the optional request schema id for the operation. public string SchemaId { get; } /// Gets the preferred route for invoking the operation. public CultMeshRouteHint RouteHint { get; } /// Creates a binding from an existing typed operation handle. public static CultMeshOperationBindingDescriptor FromOperation( CultMeshOperationHandle operation, string? label = null, string? schemaId = null, CultMeshRouteHint? routeHint = null) { if (operation == null) throw new ArgumentNullException(nameof(operation)); return new CultMeshOperationBindingDescriptor( operation.OperationId, label, schemaId, routeHint); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Transport-friendly fields for a typed operation binding descriptor. /// public sealed class CultMeshOperationBindingRecord { /// Creates an operation binding record. public CultMeshOperationBindingRecord( string? operationId = null, string? label = null, string? schemaId = null, string? routeKind = null, string? routeDescription = null) { OperationId = operationId ?? ""; Label = label ?? ""; SchemaId = schemaId ?? ""; RouteKind = routeKind ?? ""; RouteDescription = routeDescription ?? ""; } /// Gets the semantic CultMesh operation id. public string OperationId { get; } /// Gets the human-facing command label. public string Label { get; } /// Gets the optional request schema id for the operation. public string SchemaId { get; } /// Gets the flattened route kind. public string RouteKind { get; } /// Gets the flattened route description. public string RouteDescription { get; } /// Creates transport-friendly fields from an operation binding descriptor. public static CultMeshOperationBindingRecord FromBinding(CultMeshOperationBindingDescriptor? binding) { var route = CultMeshRouteRecord.FromRoute(binding?.RouteHint); return new CultMeshOperationBindingRecord( binding?.OperationId, binding?.Label, binding?.SchemaId, route.Kind, route.Description); } /// Rehydrates this record as an operation binding descriptor. public CultMeshOperationBindingDescriptor ToBinding( CultMeshRouteHint? fallbackRouteHint = null, string? fallbackOperationId = null) { var operationId = string.IsNullOrWhiteSpace(OperationId) ? (fallbackOperationId ?? "") : OperationId; var route = new CultMeshRouteRecord(RouteKind, RouteDescription) .ToRoute(fallbackRouteHint ?? CultMeshRouteHint.Automatic); return new CultMeshOperationBindingDescriptor( operationId, Label, SchemaId, route); } } /// /// Describes one concrete invocation of a typed CultMesh operation. /// public sealed class CultMeshOperationInvocationDescriptor { /// Creates an operation invocation descriptor. public CultMeshOperationInvocationDescriptor( string operationId, string? schemaId = null, CultMeshRouteHint? routeHint = null, string? idempotencyKey = null) { OperationId = RequireNonEmpty(operationId, nameof(operationId)); SchemaId = schemaId ?? ""; RouteHint = routeHint ?? CultMeshRouteHint.Automatic; IdempotencyKey = idempotencyKey; } /// Gets the semantic CultMesh operation id being invoked. public string OperationId { get; } /// Gets the optional request schema id for the operation. public string SchemaId { get; } /// Gets the preferred route for invoking the operation. public CultMeshRouteHint RouteHint { get; } /// Gets an optional caller-provided idempotency key. public string? IdempotencyKey { get; } /// Creates an invocation from an advertised operation binding. public static CultMeshOperationInvocationDescriptor FromBinding( CultMeshOperationBindingDescriptor binding, string? idempotencyKey = null) { if (binding == null) throw new ArgumentNullException(nameof(binding)); return new CultMeshOperationInvocationDescriptor( binding.OperationId, binding.SchemaId, binding.RouteHint, idempotencyKey); } /// Creates an invocation from an existing typed operation handle. public static CultMeshOperationInvocationDescriptor FromOperation( CultMeshOperationHandle operation, string? schemaId = null, CultMeshRouteHint? routeHint = null, string? idempotencyKey = null) { if (operation == null) throw new ArgumentNullException(nameof(operation)); return new CultMeshOperationInvocationDescriptor( operation.OperationId, schemaId, routeHint, idempotencyKey); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Flat, transport-friendly fields for a concrete CultMesh operation invocation. /// [MessagePackObject] public sealed class CultMeshOperationInvocationRecord { /// Creates flat operation invocation fields. [SerializationConstructor] public CultMeshOperationInvocationRecord( string? operationId = null, string? schemaId = null, string? routeKind = null, string? routeDescription = null, string? idempotencyKey = null) { OperationId = operationId ?? ""; SchemaId = schemaId ?? ""; RouteKind = routeKind ?? ""; RouteDescription = routeDescription ?? ""; IdempotencyKey = idempotencyKey ?? ""; } /// Gets the semantic CultMesh operation id. [Key(0)] public string OperationId { get; } /// Gets the optional request schema id. [Key(1)] public string SchemaId { get; } /// Gets the serialized route locality kind. [Key(2)] public string RouteKind { get; } /// Gets the serialized route description. [Key(3)] public string RouteDescription { get; } /// Gets the serialized idempotency key. [Key(4)] public string IdempotencyKey { get; } /// Creates flat fields from a typed invocation descriptor. public static CultMeshOperationInvocationRecord FromInvocation( CultMeshOperationInvocationDescriptor? invocation, string? fallbackOperationId = null, string? fallbackSchemaId = null, CultMeshRouteHint? fallbackRouteHint = null, string? fallbackIdempotencyKey = null) { var route = CultMeshRouteRecord.FromRoute(invocation?.RouteHint ?? fallbackRouteHint); return new CultMeshOperationInvocationRecord( string.IsNullOrWhiteSpace(invocation?.OperationId) ? fallbackOperationId : invocation!.OperationId, string.IsNullOrWhiteSpace(invocation?.SchemaId) ? fallbackSchemaId : invocation!.SchemaId, route.Kind, route.Description, string.IsNullOrWhiteSpace(invocation?.IdempotencyKey) ? fallbackIdempotencyKey : invocation!.IdempotencyKey); } /// Rehydrates the flat fields into a typed invocation descriptor. public CultMeshOperationInvocationDescriptor ToInvocation( string? fallbackOperationId = null, string? fallbackSchemaId = null, CultMeshRouteHint? fallbackRouteHint = null, string? fallbackIdempotencyKey = null) { var operationId = string.IsNullOrWhiteSpace(OperationId) ? fallbackOperationId : OperationId; if (string.IsNullOrWhiteSpace(operationId)) throw new InvalidOperationException("CultMesh operation invocation fields do not contain an operation id."); var route = new CultMeshRouteRecord(RouteKind, RouteDescription) .ToRoute(fallbackRouteHint); return new CultMeshOperationInvocationDescriptor( operationId!, string.IsNullOrWhiteSpace(SchemaId) ? fallbackSchemaId : SchemaId, route, string.IsNullOrWhiteSpace(IdempotencyKey) ? fallbackIdempotencyKey : IdempotencyKey); } } /// /// Shared payload value carried by one concrete CultMesh operation invocation. /// public sealed class CultMeshOperationPayload : IReadOnlyDictionary { private static readonly IReadOnlyDictionary EmptyFields = new Dictionary(0, StringComparer.Ordinal); private readonly IReadOnlyDictionary _fields; /// Creates an empty operation payload. public CultMeshOperationPayload() : this(EmptyFields) { } /// Creates an operation payload from string-compatible fields. public CultMeshOperationPayload(IEnumerable>? fields) { if (fields == null) { _fields = EmptyFields; return; } var copy = new Dictionary(StringComparer.Ordinal); foreach (var field in fields) { if (string.IsNullOrWhiteSpace(field.Key)) continue; copy[field.Key] = field.Value ?? ""; } _fields = copy; } /// Gets the empty operation payload. public static CultMeshOperationPayload Empty { get; } = new(); /// Gets the payload field names. public IEnumerable Keys => _fields.Keys; /// Gets the payload field values. public IEnumerable Values => _fields.Values; /// Gets the number of payload fields. public int Count => _fields.Count; /// Gets a payload field by name. public string this[string key] => _fields[key]; /// Gets whether the payload contains a field. public bool ContainsKey(string key) { return _fields.ContainsKey(key); } /// Tries to read a raw string field. public bool TryGetValue(string key, out string value) { return _fields.TryGetValue(key, out value); } /// Reads a raw string field with a default value. public string GetString(string key, string defaultValue = "") { return _fields.TryGetValue(key, out var value) ? value ?? defaultValue : defaultValue; } /// Reads an integer field using invariant parsing. public int GetInt32(string key, int defaultValue = 0) { return int.TryParse(GetString(key), NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) ? value : defaultValue; } /// Reads a floating point field using invariant parsing. public double GetDouble(string key, double defaultValue = 0) { return double.TryParse(GetString(key), NumberStyles.Float, CultureInfo.InvariantCulture, out var value) ? value : defaultValue; } /// Reads a boolean field using common CultMesh surface spellings. public bool GetBoolean(string key, bool defaultValue = false) { var value = GetString(key); if (string.Equals(value, "true", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "1", StringComparison.Ordinal) || string.Equals(value, "yes", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "on", StringComparison.OrdinalIgnoreCase)) return true; if (string.Equals(value, "false", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "0", StringComparison.Ordinal) || string.Equals(value, "no", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "off", StringComparison.OrdinalIgnoreCase)) return false; return defaultValue; } /// Returns a copy with one field overwritten. public CultMeshOperationPayload With(string key, string value) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Value must be non-empty.", nameof(key)); var copy = new Dictionary(_fields, StringComparer.Ordinal) { [key] = value ?? "" }; return new CultMeshOperationPayload(copy); } /// Copies payload fields into a mutable dictionary. public Dictionary ToDictionary(IEqualityComparer? comparer = null) { var copy = new Dictionary(comparer ?? StringComparer.Ordinal); foreach (var field in _fields) copy[field.Key] = field.Value ?? ""; return copy; } /// Creates a payload from string-compatible fields. public static CultMeshOperationPayload FromStrings(IEnumerable>? fields) { return new CultMeshOperationPayload(fields); } /// public IEnumerator> GetEnumerator() { return _fields.GetEnumerator(); } /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } /// /// Names a reusable projection from typed source state into derived state. /// public sealed class CultMeshProjectionRecipe { private readonly Func> _project; private readonly Func>? _watch; /// Creates a projection recipe. public CultMeshProjectionRecipe( string projectionId, IEnumerable sources, Func> project, CultMeshRouteHint? routeHint = null, Func>? watch = null) { ProjectionId = RequireNonEmpty(projectionId, nameof(projectionId)); Sources = sources?.ToArray() ?? throw new ArgumentNullException(nameof(sources)); _project = project ?? throw new ArgumentNullException(nameof(project)); RouteHint = routeHint ?? CultMeshRouteHint.Automatic; _watch = watch; } /// Gets the semantic projection id. public string ProjectionId { get; } /// Gets the typed state sources this projection depends on. public IReadOnlyList Sources { get; } /// Gets the preferred or observed route for the projection. public CultMeshRouteHint RouteHint { get; } /// Projects derived state once. public Task ProjectAsync(TParameters parameters, CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return _project(parameters, ResolveContext(context)); } /// Projects derived state once for one runtime using default context. public Task ProjectAsync(TParameters parameters, string runtimeId) { return ProjectAsync(parameters, CultMeshQueryContext.ForRuntime(runtimeId)); } /// Watches projection results when the recipe supports reactive execution. public Observable Watch(TParameters parameters, CultMeshQueryContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); if (_watch == null) { throw new NotSupportedException($"Projection recipe '{ProjectionId}' does not support reactive watches."); } return _watch(parameters, ResolveContext(context)); } /// Views this projection recipe as a typed query surface. public CultMeshQuerySurface AsQuerySurface() { return new CultMeshQuerySurface( ProjectionId, ProjectAsync, _watch == null ? null : Watch, Sources, RouteHint); } private CultMeshQueryContext ResolveContext(CultMeshQueryContext context) { return context.RouteHint.Kind == CultMeshLocalityKind.Automatic && RouteHint.Kind != CultMeshLocalityKind.Automatic ? context.WithRoute(RouteHint) : context; } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Describes one column in a native CultMesh slice view. /// public sealed class CultMeshNativeSliceColumn { /// Creates a native slice column descriptor. public CultMeshNativeSliceColumn(string name, string valueType, int elementSizeBytes) { Name = RequireNonEmpty(name, nameof(name)); ValueType = RequireNonEmpty(valueType, nameof(valueType)); if (elementSizeBytes <= 0) { throw new ArgumentOutOfRangeException(nameof(elementSizeBytes), "Element size must be positive."); } ElementSizeBytes = elementSizeBytes; } /// Gets the column name. public string Name { get; } /// Gets the schema value type for each element. public string ValueType { get; } /// Gets the size of one element in bytes. public int ElementSizeBytes { get; } /// Creates a native slice column descriptor for an unmanaged CLR value type. public static CultMeshNativeSliceColumn For(string name) where TValue : unmanaged { return new CultMeshNativeSliceColumn(name, typeof(TValue).FullName ?? typeof(TValue).Name, SizeOf()); } private static int SizeOf() where TValue : unmanaged { return System.Runtime.InteropServices.Marshal.SizeOf(); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Describes a typed native slice view exposed by CultMesh. /// public sealed class CultMeshNativeSliceViewDescriptor { /// Creates a native slice view descriptor. public CultMeshNativeSliceViewDescriptor( string viewId, string schemaId, int rowCount, IEnumerable columns, CultMeshRouteHint? route = null, string? nativeHandle = null) { ViewId = RequireNonEmpty(viewId, nameof(viewId)); SchemaId = RequireNonEmpty(schemaId, nameof(schemaId)); if (rowCount < 0) { throw new ArgumentOutOfRangeException(nameof(rowCount), "Row count cannot be negative."); } RowCount = rowCount; Columns = columns?.ToArray() ?? throw new ArgumentNullException(nameof(columns)); Route = route ?? CultMeshRouteHint.Automatic; NativeHandle = nativeHandle; } /// Gets the semantic view id. public string ViewId { get; } /// Gets the schema id for the rows in this view. public string SchemaId { get; } /// Gets the number of rows in this view. public int RowCount { get; } /// Gets column descriptors in view order. public IReadOnlyList Columns { get; } /// Gets the route that exposes this view. public CultMeshRouteHint Route { get; } /// Gets an optional runtime-specific native handle. public string? NativeHandle { get; } /// Gets the estimated byte stride for one row when all columns are densely packed. public int DenseRowStrideBytes => Columns.Sum(column => column.ElementSizeBytes); /// Finds a column descriptor by name. public CultMeshNativeSliceColumn? FindColumn(string name) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value must be non-empty.", nameof(name)); return Columns.FirstOrDefault(column => string.Equals(column.Name, name, StringComparison.Ordinal)); } private static string RequireNonEmpty(string value, string paramName) { return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value must be non-empty.", paramName) : value; } } /// /// Inspectable metadata for a native slice view. /// public sealed class CultMeshNativeSliceViewDiagnostic { /// Creates native slice view diagnostics. public CultMeshNativeSliceViewDiagnostic(CultMeshNativeSliceViewDescriptor view) { if (view == null) throw new ArgumentNullException(nameof(view)); ViewId = view.ViewId; SchemaId = view.SchemaId; RowCount = view.RowCount; Columns = view.Columns.ToArray(); Route = view.Route; NativeHandle = view.NativeHandle; DenseRowStrideBytes = view.DenseRowStrideBytes; } /// Gets the semantic view id. public string ViewId { get; } /// Gets the schema id for rows in this view. public string SchemaId { get; } /// Gets the row count advertised by this view. public int RowCount { get; } /// Gets column descriptors in view order. public IReadOnlyList Columns { get; } /// Gets the route that exposes this view. public CultMeshRouteHint Route { get; } /// Gets an optional runtime-specific native handle. public string? NativeHandle { get; } /// Gets the estimated byte stride for one densely packed row. public int DenseRowStrideBytes { get; } } }