@semiont/api-client 0.4.7 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -143,6 +143,8 @@ const client = new SemiontApiClient({
143
143
 
144
144
  🛠️ **[Utilities Guide](./docs/Utilities.md)** - Text encoding, fuzzy anchoring, SVG utilities
145
145
 
146
+ 🗄️ **[Observable Stores](./docs/STORES.md)** - Reactive resource and annotation caches, EventBus-driven invalidation, React integration
147
+
146
148
  ## Key Features
147
149
 
148
150
  - **Two clients** - HTTP (`SemiontApiClient`) and EventBus (`EventBusClient`) for the same operations
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as _semiont_core from '@semiont/core';
2
- import { EntityType, GatheredContext, BaseUrl, Logger, ResourceId, AccessToken, AnnotationId, components, Email, paths, RefreshToken, GoogleCredential, ContentFormat, SearchQuery, CloneToken, Motivation, UserDID, JobId, EventBus, UserId } from '@semiont/core';
2
+ import { EntityType, BaseUrl, Logger, ResourceId, AccessToken, AnnotationId, components, EventBus, paths, Email, RefreshToken, GoogleCredential, ContentFormat, SearchQuery, CloneToken, Motivation, UserDID, JobId, UserId } from '@semiont/core';
3
3
  export { Logger, Selector, getFragmentSelector, getSvgSelector, getTextPositionSelector, validateSvgMarkup } from '@semiont/core';
4
+ import { Subscription, Observable } from 'rxjs';
4
5
  export { BoundingBox, ContentCache, FragmentSelector, JWTTokenSchema, LOCALES, LocaleInfo, MatchQuality, Point, SvgSelector, TextPosition, TextPositionSelector, TextQuoteSelector, ValidatedAnnotation, ValidationFailure, ValidationResult, ValidationSuccess, buildContentCache, createCircleSvg, createPolygonSvg, createRectangleSvg, decodeRepresentation, decodeWithCharset, extractBoundingBox, extractCharset, extractContext, findBestTextMatch, findTextWithContext, formatLocaleDisplay, getAllLocaleCodes, getAnnotationExactText, getBodySource, getBodyType, getChecksum, getCommentText, getCreator, getDerivedFrom, getExactText, getLanguage, getLocaleEnglishName, getLocaleInfo, getLocaleNativeName, getNodeEncoding, getPrimaryMediaType, getPrimaryRepresentation, getPrimarySelector, getResourceEntityTypes, getResourceId, getStorageUri, getTargetSelector, getTargetSource, getTextQuoteSelector, hasTargetSelector, isArchived, isAssessment, isBodyResolved, isComment, isDraft, isHighlight, isReference, isResolvedReference, isStubReference, isTag, isValidEmail, normalizeCoordinates, normalizeText, parseSvgSelector, scaleSvgToNative, validateAndCorrectOffsets, validateData, verifyPosition } from './utils/index.js';
5
6
 
6
7
  /**
@@ -203,14 +204,9 @@ type GatherAnnotationStreamRequest = components['schemas']['GatherAnnotationStre
203
204
  */
204
205
  type BindAnnotationStreamRequest = components['schemas']['BindAnnotationStreamRequest'];
205
206
  /**
206
- * Request body for bind search stream
207
+ * Request body for match search stream
207
208
  */
208
- interface BindSearchStreamRequest {
209
- referenceId: string;
210
- context: GatheredContext;
211
- limit?: number;
212
- useSemanticScoring?: boolean;
213
- }
209
+ type MatchSearchStreamRequest = components['schemas']['MatchSearchStreamRequest'];
214
210
  /**
215
211
  * SSE Client configuration
216
212
  */
@@ -532,7 +528,7 @@ declare class SSEClient {
532
528
  * @param options - Request options (auth token, eventBus)
533
529
  * @returns SSE stream controller
534
530
  */
535
- bindSearch(resourceId: ResourceId, request: BindSearchStreamRequest, options: SSERequestOptions): SSEStream;
531
+ matchSearch(resourceId: ResourceId, request: MatchSearchStreamRequest, options: SSERequestOptions): SSEStream;
536
532
  /**
537
533
  * Subscribe to resource events (long-lived stream)
538
534
  *
@@ -614,6 +610,208 @@ declare class SSEClient {
614
610
  }): SSEStream;
615
611
  }
616
612
 
613
+ /**
614
+ * FlowEngine — framework-agnostic flow orchestration
615
+ *
616
+ * Owns the subscription/SSE-bridge logic that was previously scattered across
617
+ * React hooks (useBindFlow, useYieldFlow, useMarkFlow, useContextGatherFlow,
618
+ * useResourceEvents, useAttentionStream).
619
+ *
620
+ * Each method accepts a `getToken` function called at event-handling time so
621
+ * the token is always fresh (the client is stateless; tokens rotate).
622
+ *
623
+ * Each method returns an RxJS Subscription. The caller is responsible for
624
+ * calling .unsubscribe() when the flow is no longer needed (e.g. in a
625
+ * React useEffect cleanup or on workspace teardown).
626
+ *
627
+ * No React imports. No DOM imports. Pure RxJS + EventBus.
628
+ */
629
+
630
+ type TokenGetter$2 = () => AccessToken | undefined;
631
+ declare class FlowEngine {
632
+ private readonly eventBus;
633
+ private readonly sse;
634
+ private readonly http;
635
+ constructor(eventBus: EventBus, sse: SSEClient, http: SemiontApiClient);
636
+ /**
637
+ * Activate the bind flow for a resource.
638
+ *
639
+ * @subscribes bind:update-body — calls SSE bindAnnotation
640
+ * @subscribes match:search-requested — calls SSE bindSearch
641
+ * @emits bind:body-updated, bind:body-update-failed
642
+ */
643
+ bind(rUri: ResourceId, getToken: TokenGetter$2): Subscription;
644
+ /**
645
+ * Activate the yield (generation) flow for a resource.
646
+ *
647
+ * @subscribes yield:request — calls SSE yieldResource
648
+ * @subscribes yield:finished — links generated resource back to the reference annotation via bind:update-body
649
+ * @subscribes job:cancel-requested (generation) — aborts in-flight stream
650
+ */
651
+ yield(_rUri: ResourceId, getToken: TokenGetter$2): Subscription;
652
+ /**
653
+ * Activate the mark (annotation CRUD + assist) flow for a resource.
654
+ *
655
+ * @subscribes mark:submit — HTTP markAnnotation
656
+ * @subscribes mark:delete — HTTP deleteAnnotation
657
+ * @subscribes mark:assist-request — SSE mark* (by motivation)
658
+ * @subscribes job:cancel-requested (annotation) — aborts in-flight assist
659
+ * @emits mark:created, mark:create-failed, mark:deleted, mark:delete-failed
660
+ */
661
+ mark(rUri: ResourceId, getToken: TokenGetter$2): Subscription;
662
+ /**
663
+ * Activate the gather-context flow for a resource.
664
+ *
665
+ * @subscribes gather:requested — calls SSE gatherAnnotation, threads correlationId
666
+ * @emits gather:complete (re-emitted from SSE gather:annotation-finished)
667
+ */
668
+ gatherContext(rUri: ResourceId, getToken: TokenGetter$2): Subscription;
669
+ /**
670
+ * Open the long-lived resource-events SSE stream.
671
+ * Returns a Subscription whose teardown closes the stream.
672
+ */
673
+ resourceEvents(rUri: ResourceId, getToken: TokenGetter$2): Subscription;
674
+ /**
675
+ * Open the long-lived participant attention SSE stream.
676
+ * Returns a Subscription whose teardown closes the stream.
677
+ */
678
+ attentionStream(getToken: TokenGetter$2): Subscription;
679
+ }
680
+
681
+ /**
682
+ * ResourceStore — per-workspace observable resource cache
683
+ *
684
+ * BehaviorSubject-backed store that:
685
+ * - Populates lazily on first subscribe (no up-front fetch)
686
+ * - Updates reactively when EventBus events arrive (no manual invalidation)
687
+ * - Is readable from outside React
688
+ *
689
+ * EventBus events handled:
690
+ * - yield:created → fetch new resource into map, invalidate lists
691
+ * - mark:archived → invalidate resource detail + lists
692
+ * - mark:unarchived → invalidate resource detail + lists
693
+ * - mark:entity-tag-added / mark:entity-tag-removed → invalidate resource detail
694
+ *
695
+ * Token: mutable — call setTokenGetter() from the React layer when auth changes.
696
+ */
697
+
698
+ type ResponseContent$2<T> = T extends {
699
+ responses: {
700
+ 200: {
701
+ content: {
702
+ 'application/json': infer R;
703
+ };
704
+ };
705
+ };
706
+ } ? R : never;
707
+ type ResourceDetail = ResponseContent$2<paths['/resources/{id}']['get']>;
708
+ type ResourceListResponse = ResponseContent$2<paths['/resources']['get']>;
709
+ type TokenGetter$1 = () => AccessToken | undefined;
710
+ declare class ResourceStore {
711
+ private readonly http;
712
+ /** Cache of individual resource details, keyed by ResourceId */
713
+ private readonly detail$;
714
+ /** Cache of list responses, keyed by a serialized options string */
715
+ private readonly list$;
716
+ /** Track in-flight fetches to avoid duplicate requests */
717
+ private readonly fetchingDetail;
718
+ private readonly fetchingList;
719
+ /** Memoized Observables — same instance returned for the same key */
720
+ private readonly detailObs$;
721
+ private readonly listObs$;
722
+ /** Mutable token getter — updated from the React layer when auth changes */
723
+ private getToken;
724
+ /** Update the token getter (called from React when auth token changes) */
725
+ setTokenGetter(getter: TokenGetter$1): void;
726
+ constructor(http: SemiontApiClient, eventBus: EventBus);
727
+ /**
728
+ * Get a single resource by ID as an Observable.
729
+ * Triggers a fetch if not cached.
730
+ */
731
+ get(id: ResourceId): Observable<ResourceDetail | undefined>;
732
+ /**
733
+ * List resources as an Observable.
734
+ * Triggers a fetch if not cached.
735
+ */
736
+ list(options?: {
737
+ limit?: number;
738
+ archived?: boolean;
739
+ }): Observable<ResourceListResponse | undefined>;
740
+ /** Invalidate and re-fetch a specific resource detail */
741
+ invalidateDetail(id: ResourceId): void;
742
+ /** Remove all list caches (triggers re-fetch on next subscribe) */
743
+ invalidateLists(): void;
744
+ private fetchDetail;
745
+ private fetchList;
746
+ }
747
+
748
+ /**
749
+ * AnnotationStore — per-workspace observable annotation cache
750
+ *
751
+ * BehaviorSubject-backed store that:
752
+ * - Populates lazily on first subscribe (no up-front fetch)
753
+ * - Updates reactively when EventBus events arrive (no manual invalidation)
754
+ * - Is readable from outside React
755
+ *
756
+ * EventBus events handled:
757
+ * - mark:deleted → remove from detail cache
758
+ * - mark:added → invalidate annotation list (SSE domain event; resourceId on BaseEvent)
759
+ * - mark:removed → invalidate annotation list + detail (SSE domain event)
760
+ * - mark:body-updated → invalidate annotation list + detail (SSE domain event)
761
+ * - mark:entity-tag-added / mark:entity-tag-removed → invalidate list for resource
762
+ *
763
+ * Token: mutable — call setTokenGetter() from the React layer when auth changes.
764
+ */
765
+
766
+ type ResponseContent$1<T> = T extends {
767
+ responses: {
768
+ 200: {
769
+ content: {
770
+ 'application/json': infer R;
771
+ };
772
+ };
773
+ };
774
+ } ? R : never;
775
+ type AnnotationsListResponse = ResponseContent$1<paths['/resources/{id}/annotations']['get']>;
776
+ type AnnotationDetail = ResponseContent$1<paths['/resources/{resourceId}/annotations/{annotationId}']['get']>;
777
+ type TokenGetter = () => AccessToken | undefined;
778
+ declare class AnnotationStore {
779
+ private readonly http;
780
+ /** Annotation list responses keyed by ResourceId */
781
+ private readonly list$;
782
+ /** Individual annotation details keyed by AnnotationId */
783
+ private readonly detail$;
784
+ /** Track in-flight fetches */
785
+ private readonly fetchingList;
786
+ private readonly fetchingDetail;
787
+ /** Memoized Observables — same instance returned for the same key */
788
+ private readonly listObs$;
789
+ private readonly detailObs$;
790
+ /** Mutable token getter — updated from the React layer when auth changes */
791
+ private getToken;
792
+ /** Update the token getter (called from React when auth token changes) */
793
+ setTokenGetter(getter: TokenGetter): void;
794
+ constructor(http: SemiontApiClient, eventBus: EventBus);
795
+ /**
796
+ * Get annotations for a resource as an Observable.
797
+ * Triggers a fetch if not cached.
798
+ */
799
+ listForResource(resourceId: ResourceId): Observable<AnnotationsListResponse | undefined>;
800
+ /**
801
+ * Get a single annotation detail as an Observable.
802
+ * Triggers a fetch if not cached.
803
+ */
804
+ get(resourceId: ResourceId, annotationId: AnnotationId): Observable<AnnotationDetail | undefined>;
805
+ /** Invalidate and re-fetch a resource's annotation list */
806
+ invalidateList(resourceId: ResourceId): void;
807
+ /** Invalidate a single annotation detail (re-fetched on next subscribe) */
808
+ invalidateDetail(annotationId: AnnotationId): void;
809
+ /** Remove an annotation from the detail cache without re-fetching */
810
+ private removeFromDetailCache;
811
+ private fetchList;
812
+ private fetchDetail;
813
+ }
814
+
617
815
  /**
618
816
  * Common API client for Semiont backend
619
817
  *
@@ -657,6 +855,8 @@ declare class APIError extends Error {
657
855
  }
658
856
  interface SemiontApiClientConfig {
659
857
  baseUrl: BaseUrl;
858
+ /** Per-workspace EventBus. Required — one bus per workspace, constructed externally. */
859
+ eventBus: EventBus;
660
860
  timeout?: number;
661
861
  retry?: number;
662
862
  logger?: Logger;
@@ -676,33 +876,40 @@ interface RequestOptions {
676
876
  */
677
877
  declare class SemiontApiClient {
678
878
  private http;
679
- private baseUrl;
879
+ readonly baseUrl: BaseUrl;
880
+ /** The workspace-scoped EventBus this client was constructed with. */
881
+ readonly eventBus: EventBus;
680
882
  private logger?;
681
883
  /**
682
884
  * SSE streaming client for real-time operations
683
885
  *
684
886
  * Separate from the main HTTP client to clearly mark streaming endpoints.
685
887
  * Uses native fetch() instead of ky for SSE support.
686
- *
687
- * @example
688
- * ```typescript
689
- * const stream = client.sse.detectAnnotations(
690
- * resourceId,
691
- * { entityTypes: ['Person', 'Organization'] },
692
- * { auth: accessToken }
693
- * );
694
- *
695
- * stream.onProgress((p) => console.log(p.message));
696
- * stream.onComplete((r) => console.log(`Found ${r.foundCount} entities`));
697
- * stream.close();
698
- * ```
699
888
  */
700
889
  readonly sse: SSEClient;
890
+ /**
891
+ * Framework-agnostic flow orchestration.
892
+ * Each method returns a Subscription; call .unsubscribe() to tear down.
893
+ */
894
+ readonly flows: FlowEngine;
895
+ /**
896
+ * Per-workspace observable stores for entity data.
897
+ * Call stores.resources.setTokenGetter() / stores.annotations.setTokenGetter()
898
+ * from the React layer when the auth token changes.
899
+ */
900
+ readonly stores: {
901
+ resources: ResourceStore;
902
+ annotations: AnnotationStore;
903
+ };
701
904
  constructor(config: SemiontApiClientConfig);
905
+ private authHeaders;
702
906
  authenticatePassword(email: Email, password: string, options?: RequestOptions): Promise<ResponseContent<paths['/api/tokens/password']['post']>>;
703
907
  refreshToken(token: RefreshToken, options?: RequestOptions): Promise<ResponseContent<paths['/api/tokens/refresh']['post']>>;
704
908
  authenticateGoogle(credential: GoogleCredential, options?: RequestOptions): Promise<ResponseContent<paths['/api/tokens/google']['post']>>;
705
909
  generateMCPToken(options?: RequestOptions): Promise<ResponseContent<paths['/api/tokens/mcp-generate']['post']>>;
910
+ getMediaToken(resourceId: ResourceId, options?: RequestOptions): Promise<{
911
+ token: string;
912
+ }>;
706
913
  getMe(options?: RequestOptions): Promise<ResponseContent<paths['/api/users/me']['get']>>;
707
914
  acceptTerms(options?: RequestOptions): Promise<ResponseContent<paths['/api/users/accept-terms']['post']>>;
708
915
  logout(options?: RequestOptions): Promise<ResponseContent<paths['/api/users/logout']['post']>>;
@@ -808,12 +1015,8 @@ declare class SemiontApiClient {
808
1015
  }>;
809
1016
  browseResources(limit?: number, archived?: boolean, query?: SearchQuery, options?: RequestOptions): Promise<ResponseContent<paths['/resources']['get']>>;
810
1017
  updateResource(id: ResourceId, data: RequestContent<paths['/resources/{id}']['patch']>, options?: RequestOptions): Promise<void>;
811
- getResourceEvents(id: ResourceId, options?: RequestOptions): Promise<{
812
- events: any[];
813
- }>;
814
- browseReferences(id: ResourceId, options?: RequestOptions): Promise<{
815
- referencedBy: any[];
816
- }>;
1018
+ getResourceEvents(id: ResourceId, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{id}/events']['get']>>;
1019
+ browseReferences(id: ResourceId, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{id}/referenced-by']['get']>>;
817
1020
  generateCloneToken(id: ResourceId, options?: RequestOptions): Promise<ResponseContent<paths['/resources/{id}/clone-with-token']['post']>>;
818
1021
  getResourceByToken(token: CloneToken, options?: RequestOptions): Promise<ResponseContent<paths['/api/clone-tokens/{token}']['get']>>;
819
1022
  createResourceFromToken(data: RequestContent<paths['/api/clone-tokens/create-resource']['post']>, options?: RequestOptions): Promise<{
@@ -882,6 +1085,7 @@ declare class SemiontApiClient {
882
1085
  message?: string;
883
1086
  result?: Record<string, unknown>;
884
1087
  }>;
1088
+ private parseSSEStream;
885
1089
  getJobStatus(id: JobId, options?: RequestOptions): Promise<ResponseContent<paths['/api/jobs/{id}']['get']>>;
886
1090
  /**
887
1091
  * Poll a job until it completes or fails
@@ -1009,4 +1213,4 @@ declare function isPdfMimeType(mimeType: string): boolean;
1009
1213
  type MimeCategory = 'text' | 'image' | 'unsupported';
1010
1214
  declare function getMimeCategory(mimeType: string): MimeCategory;
1011
1215
 
1012
- export { APIError, type AnnotateReferencesStreamRequest, type BindSearchStreamRequest, EventBusClient, type MimeCategory, type ReferenceDetectionProgress, type RequestOptions, SSEClient, type SSEClientConfig, type SSEStream, type SSEStreamConnected, SSE_STREAM_CONNECTED, SemiontApiClient, type SemiontApiClientConfig, type YieldProgress, type YieldResourceStreamRequest, getExtensionForMimeType, getMimeCategory, isImageMimeType, isPdfMimeType, isTextMimeType };
1216
+ export { APIError, type AnnotateReferencesStreamRequest, type AnnotationDetail, AnnotationStore, type AnnotationsListResponse, EventBusClient, FlowEngine, type MatchSearchStreamRequest, type MimeCategory, type ReferenceDetectionProgress, type RequestOptions, type ResourceDetail, type ResourceListResponse, ResourceStore, SSEClient, type SSEClientConfig, type SSEStream, type SSEStreamConnected, SSE_STREAM_CONNECTED, SemiontApiClient, type SemiontApiClientConfig, type YieldProgress, type YieldResourceStreamRequest, getExtensionForMimeType, getMimeCategory, isImageMimeType, isPdfMimeType, isTextMimeType };