@ai-matrx/associations 0.4.0 → 0.5.1
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/CHANGELOG.md +69 -0
- package/README.md +3 -3
- package/dist/core/index.cjs +246 -3
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +67 -3
- package/dist/core/index.d.ts +67 -3
- package/dist/core/index.js +246 -3
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +7 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -3
- package/dist/index.d.ts +124 -3
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +538 -53
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +253 -5
- package/dist/react/index.d.ts +253 -5
- package/dist/react/index.js +490 -5
- package/dist/react/index.js.map +1 -1
- package/package.json +3 -3
package/dist/core/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityOverlayMap, EntityTypeToken, EntityOverlayEntry, ErrorSink, AssociationsRpcError, AssociationsDataSource, AssociationsIdentity, AssociationTargetType, AssociationsRpcResult, AssociationEdge, AssociationTargetEdge, AssociationSourceEdge, CategoryDimension, PlatformCategory, UserStateKind, UserEntityState, AssociationsEntry, CategoriesEntry, AssociationsConfig } from '../index.cjs';
|
|
1
|
+
import { EntityOverlayMap, EntityTypeToken, EntityOverlayEntry, ErrorSink, AssociationsRpcError, AssociationsDataSource, AssociationsIdentity, AssociationTargetType, AssociationsRpcResult, AssociationEdge, AssociationTargetEdge, AssociationSourceEdge, CategoryDimension, PlatformCategory, UserStateKind, UserEntityState, PlatformComment, AssociationsEntry, CategoriesEntry, CommentsEntry, AssociationsConfig } from '../index.cjs';
|
|
2
2
|
import 'react';
|
|
3
3
|
|
|
4
4
|
/** The universal ownership column post-2026-reorg. */
|
|
@@ -474,6 +474,28 @@ interface AssociationHelpersApi {
|
|
|
474
474
|
}
|
|
475
475
|
declare function createAssociationHelpers(service: AssociationsServiceApi): AssociationHelpersApi;
|
|
476
476
|
|
|
477
|
+
interface AddCommentArgs {
|
|
478
|
+
/** Registered entity-type token the comment hangs on (writes-strict, C18). */
|
|
479
|
+
entityType: string;
|
|
480
|
+
entityId: string;
|
|
481
|
+
body: string;
|
|
482
|
+
/** Reply target — an existing comment's id. Top-level when omitted. */
|
|
483
|
+
parentId?: string | null;
|
|
484
|
+
/** Org override; the RPC resolves task-org/personal-org when omitted. */
|
|
485
|
+
orgId?: string | null;
|
|
486
|
+
}
|
|
487
|
+
interface CommentsServiceApi {
|
|
488
|
+
listForEntity(entityType: string, entityId: string): Promise<AssociationsRpcResult<{
|
|
489
|
+
comments: PlatformComment[];
|
|
490
|
+
}>>;
|
|
491
|
+
add(args: AddCommentArgs): Promise<AssociationsRpcResult<{
|
|
492
|
+
id: string;
|
|
493
|
+
}>>;
|
|
494
|
+
edit(id: string, body: string): Promise<AssociationsRpcResult<null>>;
|
|
495
|
+
remove(id: string): Promise<AssociationsRpcResult<null>>;
|
|
496
|
+
}
|
|
497
|
+
declare function createCommentsService(deps: CoreDeps): CommentsServiceApi;
|
|
498
|
+
|
|
477
499
|
/** Cache key for one association endpoint. */
|
|
478
500
|
declare function associationsKey(type: string, id: string): string;
|
|
479
501
|
interface AssociationWriteResult {
|
|
@@ -488,6 +510,12 @@ interface CategoryMutationResult {
|
|
|
488
510
|
id?: string;
|
|
489
511
|
error?: string;
|
|
490
512
|
}
|
|
513
|
+
interface CommentMutationResult {
|
|
514
|
+
ok: boolean;
|
|
515
|
+
/** Set on a successful `addComment`. */
|
|
516
|
+
id?: string;
|
|
517
|
+
error?: string;
|
|
518
|
+
}
|
|
491
519
|
interface AssociationsStore {
|
|
492
520
|
/** Stable, sync read of one endpoint's entry (idle default when uncached). */
|
|
493
521
|
getEdges(type: string, id: string): AssociationsEntry;
|
|
@@ -531,7 +559,7 @@ interface AssociationsStore {
|
|
|
531
559
|
}): Promise<AssociationWriteResult>;
|
|
532
560
|
/** Reset one endpoint to idle and notify its subscribers. */
|
|
533
561
|
invalidate(type: string, id: string): void;
|
|
534
|
-
/** Reset every association endpoint
|
|
562
|
+
/** Reset every association endpoint, category facet, and comment thread. */
|
|
535
563
|
invalidateAll(): void;
|
|
536
564
|
getCategories(dimension: CategoryDimension): CategoriesEntry;
|
|
537
565
|
loadCategories(dimension: CategoryDimension, opts?: {
|
|
@@ -566,6 +594,41 @@ interface AssociationsStore {
|
|
|
566
594
|
dimension: CategoryDimension;
|
|
567
595
|
id: string;
|
|
568
596
|
}): Promise<CategoryMutationResult>;
|
|
597
|
+
/** Stable, sync read of one entity's comment thread (idle when uncached). */
|
|
598
|
+
getComments(type: string, id: string): CommentsEntry;
|
|
599
|
+
/**
|
|
600
|
+
* Lazy load of every comment on `${type}:${id}` (oldest→newest; thread by
|
|
601
|
+
* `parentId`). Deduped per key; `status === "ready"` short-circuits unless
|
|
602
|
+
* `force`.
|
|
603
|
+
*/
|
|
604
|
+
loadComments(type: string, id: string, opts?: {
|
|
605
|
+
force?: boolean;
|
|
606
|
+
}): Promise<void>;
|
|
607
|
+
/** External-store contract for one entity's comment thread. */
|
|
608
|
+
subscribeComments(key: string, cb: () => void): () => void;
|
|
609
|
+
/** Post a comment (or a reply); on success force-reloads the thread. */
|
|
610
|
+
addComment(args: {
|
|
611
|
+
entityType: string;
|
|
612
|
+
entityId: string;
|
|
613
|
+
body: string;
|
|
614
|
+
parentId?: string | null;
|
|
615
|
+
orgId?: string | null;
|
|
616
|
+
}): Promise<CommentMutationResult>;
|
|
617
|
+
/** Edit a comment body (author-only in the RPC); reloads the thread. */
|
|
618
|
+
editComment(args: {
|
|
619
|
+
entityType: string;
|
|
620
|
+
entityId: string;
|
|
621
|
+
id: string;
|
|
622
|
+
body: string;
|
|
623
|
+
}): Promise<CommentMutationResult>;
|
|
624
|
+
/** Soft-delete a comment (author/org member in the RPC); reloads the thread. */
|
|
625
|
+
deleteComment(args: {
|
|
626
|
+
entityType: string;
|
|
627
|
+
entityId: string;
|
|
628
|
+
id: string;
|
|
629
|
+
}): Promise<CommentMutationResult>;
|
|
630
|
+
/** Reset one entity's comment thread to idle and notify its subscribers. */
|
|
631
|
+
invalidateComments(type: string, id: string): void;
|
|
569
632
|
/** Batched title resolution + the session cache + prime. */
|
|
570
633
|
titles: TitlesServiceApi;
|
|
571
634
|
/** Per-user favorite/pinned/hidden state (`ues_*`). */
|
|
@@ -586,6 +649,7 @@ interface AssociationsStore {
|
|
|
586
649
|
services: {
|
|
587
650
|
associations: AssociationsServiceApi;
|
|
588
651
|
categories: CategoriesServiceApi;
|
|
652
|
+
comments: CommentsServiceApi;
|
|
589
653
|
};
|
|
590
654
|
/** Merge more host overlay entries in (icons/routes/candidate loaders). */
|
|
591
655
|
registerEntityOverlay: EntityRegistry["registerEntityOverlay"];
|
|
@@ -676,4 +740,4 @@ interface CategoryHierarchy {
|
|
|
676
740
|
*/
|
|
677
741
|
declare function buildCategoryHierarchy(categories: PlatformCategory[]): CategoryHierarchy;
|
|
678
742
|
|
|
679
|
-
export { type AddAssociationArgs, type AssertDemandedSchemaOptions, type AssociationGuards, type AssociationHelpersApi, type AssociationWriteResult, type AssociationsServiceApi, type AssociationsStore, type CandidateRecord, type CandidatesResult, type CandidatesServiceApi, type CategoriesServiceApi, type CategoryHierarchy, type CategoryHierarchyItem, type CategoryMutationResult, type ContainerRef, type ContentRole, type ConversationFileLink, type CoreDeps, type CreateEntityRowArgs, DEFAULT_ORG_COLUMN, DEFAULT_OWNER_COLUMN, type DemandedSchemaReport, type EdgeAttrs, type EdgeSpec, type EntityInfo, type EntityRef, type EntityRegistry, type EntityRowResult, type EntityRowsServiceApi, type FavoritesServiceApi, type ListCandidatesArgs, NON_CONTENT_SOURCE_TYPES, type PgErrorMapper, type RpcResultHelpers, SELF_TEST_MISSING_RPC, type SearchAcrossTokensArgs, type TitlesServiceApi, type UniversalCandidate, assertDemandedSchema, associationsKey, buildCategoryHierarchy, createAssociationGuards, createAssociationHelpers, createAssociationsService, createAssociationsStore, createCandidatesService, createCategoriesService, createEntityRegistry, createEntityRowsService, createFavoritesService, createRpcResultHelpers, createTitlesService, entityTitleCacheKey, err, firstError, isContentRole, isContentSourceEdge, isMembershipMetadata, isTransportFailure, isUuid, ok, resolveEntityToken };
|
|
743
|
+
export { type AddAssociationArgs, type AddCommentArgs, type AssertDemandedSchemaOptions, type AssociationGuards, type AssociationHelpersApi, type AssociationWriteResult, type AssociationsServiceApi, type AssociationsStore, type CandidateRecord, type CandidatesResult, type CandidatesServiceApi, type CategoriesServiceApi, type CategoryHierarchy, type CategoryHierarchyItem, type CategoryMutationResult, type CommentMutationResult, type CommentsServiceApi, type ContainerRef, type ContentRole, type ConversationFileLink, type CoreDeps, type CreateEntityRowArgs, DEFAULT_ORG_COLUMN, DEFAULT_OWNER_COLUMN, type DemandedSchemaReport, type EdgeAttrs, type EdgeSpec, type EntityInfo, type EntityRef, type EntityRegistry, type EntityRowResult, type EntityRowsServiceApi, type FavoritesServiceApi, type ListCandidatesArgs, NON_CONTENT_SOURCE_TYPES, type PgErrorMapper, type RpcResultHelpers, SELF_TEST_MISSING_RPC, type SearchAcrossTokensArgs, type TitlesServiceApi, type UniversalCandidate, assertDemandedSchema, associationsKey, buildCategoryHierarchy, createAssociationGuards, createAssociationHelpers, createAssociationsService, createAssociationsStore, createCandidatesService, createCategoriesService, createCommentsService, createEntityRegistry, createEntityRowsService, createFavoritesService, createRpcResultHelpers, createTitlesService, entityTitleCacheKey, err, firstError, isContentRole, isContentSourceEdge, isMembershipMetadata, isTransportFailure, isUuid, ok, resolveEntityToken };
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityOverlayMap, EntityTypeToken, EntityOverlayEntry, ErrorSink, AssociationsRpcError, AssociationsDataSource, AssociationsIdentity, AssociationTargetType, AssociationsRpcResult, AssociationEdge, AssociationTargetEdge, AssociationSourceEdge, CategoryDimension, PlatformCategory, UserStateKind, UserEntityState, AssociationsEntry, CategoriesEntry, AssociationsConfig } from '../index.js';
|
|
1
|
+
import { EntityOverlayMap, EntityTypeToken, EntityOverlayEntry, ErrorSink, AssociationsRpcError, AssociationsDataSource, AssociationsIdentity, AssociationTargetType, AssociationsRpcResult, AssociationEdge, AssociationTargetEdge, AssociationSourceEdge, CategoryDimension, PlatformCategory, UserStateKind, UserEntityState, PlatformComment, AssociationsEntry, CategoriesEntry, CommentsEntry, AssociationsConfig } from '../index.js';
|
|
2
2
|
import 'react';
|
|
3
3
|
|
|
4
4
|
/** The universal ownership column post-2026-reorg. */
|
|
@@ -474,6 +474,28 @@ interface AssociationHelpersApi {
|
|
|
474
474
|
}
|
|
475
475
|
declare function createAssociationHelpers(service: AssociationsServiceApi): AssociationHelpersApi;
|
|
476
476
|
|
|
477
|
+
interface AddCommentArgs {
|
|
478
|
+
/** Registered entity-type token the comment hangs on (writes-strict, C18). */
|
|
479
|
+
entityType: string;
|
|
480
|
+
entityId: string;
|
|
481
|
+
body: string;
|
|
482
|
+
/** Reply target — an existing comment's id. Top-level when omitted. */
|
|
483
|
+
parentId?: string | null;
|
|
484
|
+
/** Org override; the RPC resolves task-org/personal-org when omitted. */
|
|
485
|
+
orgId?: string | null;
|
|
486
|
+
}
|
|
487
|
+
interface CommentsServiceApi {
|
|
488
|
+
listForEntity(entityType: string, entityId: string): Promise<AssociationsRpcResult<{
|
|
489
|
+
comments: PlatformComment[];
|
|
490
|
+
}>>;
|
|
491
|
+
add(args: AddCommentArgs): Promise<AssociationsRpcResult<{
|
|
492
|
+
id: string;
|
|
493
|
+
}>>;
|
|
494
|
+
edit(id: string, body: string): Promise<AssociationsRpcResult<null>>;
|
|
495
|
+
remove(id: string): Promise<AssociationsRpcResult<null>>;
|
|
496
|
+
}
|
|
497
|
+
declare function createCommentsService(deps: CoreDeps): CommentsServiceApi;
|
|
498
|
+
|
|
477
499
|
/** Cache key for one association endpoint. */
|
|
478
500
|
declare function associationsKey(type: string, id: string): string;
|
|
479
501
|
interface AssociationWriteResult {
|
|
@@ -488,6 +510,12 @@ interface CategoryMutationResult {
|
|
|
488
510
|
id?: string;
|
|
489
511
|
error?: string;
|
|
490
512
|
}
|
|
513
|
+
interface CommentMutationResult {
|
|
514
|
+
ok: boolean;
|
|
515
|
+
/** Set on a successful `addComment`. */
|
|
516
|
+
id?: string;
|
|
517
|
+
error?: string;
|
|
518
|
+
}
|
|
491
519
|
interface AssociationsStore {
|
|
492
520
|
/** Stable, sync read of one endpoint's entry (idle default when uncached). */
|
|
493
521
|
getEdges(type: string, id: string): AssociationsEntry;
|
|
@@ -531,7 +559,7 @@ interface AssociationsStore {
|
|
|
531
559
|
}): Promise<AssociationWriteResult>;
|
|
532
560
|
/** Reset one endpoint to idle and notify its subscribers. */
|
|
533
561
|
invalidate(type: string, id: string): void;
|
|
534
|
-
/** Reset every association endpoint
|
|
562
|
+
/** Reset every association endpoint, category facet, and comment thread. */
|
|
535
563
|
invalidateAll(): void;
|
|
536
564
|
getCategories(dimension: CategoryDimension): CategoriesEntry;
|
|
537
565
|
loadCategories(dimension: CategoryDimension, opts?: {
|
|
@@ -566,6 +594,41 @@ interface AssociationsStore {
|
|
|
566
594
|
dimension: CategoryDimension;
|
|
567
595
|
id: string;
|
|
568
596
|
}): Promise<CategoryMutationResult>;
|
|
597
|
+
/** Stable, sync read of one entity's comment thread (idle when uncached). */
|
|
598
|
+
getComments(type: string, id: string): CommentsEntry;
|
|
599
|
+
/**
|
|
600
|
+
* Lazy load of every comment on `${type}:${id}` (oldest→newest; thread by
|
|
601
|
+
* `parentId`). Deduped per key; `status === "ready"` short-circuits unless
|
|
602
|
+
* `force`.
|
|
603
|
+
*/
|
|
604
|
+
loadComments(type: string, id: string, opts?: {
|
|
605
|
+
force?: boolean;
|
|
606
|
+
}): Promise<void>;
|
|
607
|
+
/** External-store contract for one entity's comment thread. */
|
|
608
|
+
subscribeComments(key: string, cb: () => void): () => void;
|
|
609
|
+
/** Post a comment (or a reply); on success force-reloads the thread. */
|
|
610
|
+
addComment(args: {
|
|
611
|
+
entityType: string;
|
|
612
|
+
entityId: string;
|
|
613
|
+
body: string;
|
|
614
|
+
parentId?: string | null;
|
|
615
|
+
orgId?: string | null;
|
|
616
|
+
}): Promise<CommentMutationResult>;
|
|
617
|
+
/** Edit a comment body (author-only in the RPC); reloads the thread. */
|
|
618
|
+
editComment(args: {
|
|
619
|
+
entityType: string;
|
|
620
|
+
entityId: string;
|
|
621
|
+
id: string;
|
|
622
|
+
body: string;
|
|
623
|
+
}): Promise<CommentMutationResult>;
|
|
624
|
+
/** Soft-delete a comment (author/org member in the RPC); reloads the thread. */
|
|
625
|
+
deleteComment(args: {
|
|
626
|
+
entityType: string;
|
|
627
|
+
entityId: string;
|
|
628
|
+
id: string;
|
|
629
|
+
}): Promise<CommentMutationResult>;
|
|
630
|
+
/** Reset one entity's comment thread to idle and notify its subscribers. */
|
|
631
|
+
invalidateComments(type: string, id: string): void;
|
|
569
632
|
/** Batched title resolution + the session cache + prime. */
|
|
570
633
|
titles: TitlesServiceApi;
|
|
571
634
|
/** Per-user favorite/pinned/hidden state (`ues_*`). */
|
|
@@ -586,6 +649,7 @@ interface AssociationsStore {
|
|
|
586
649
|
services: {
|
|
587
650
|
associations: AssociationsServiceApi;
|
|
588
651
|
categories: CategoriesServiceApi;
|
|
652
|
+
comments: CommentsServiceApi;
|
|
589
653
|
};
|
|
590
654
|
/** Merge more host overlay entries in (icons/routes/candidate loaders). */
|
|
591
655
|
registerEntityOverlay: EntityRegistry["registerEntityOverlay"];
|
|
@@ -676,4 +740,4 @@ interface CategoryHierarchy {
|
|
|
676
740
|
*/
|
|
677
741
|
declare function buildCategoryHierarchy(categories: PlatformCategory[]): CategoryHierarchy;
|
|
678
742
|
|
|
679
|
-
export { type AddAssociationArgs, type AssertDemandedSchemaOptions, type AssociationGuards, type AssociationHelpersApi, type AssociationWriteResult, type AssociationsServiceApi, type AssociationsStore, type CandidateRecord, type CandidatesResult, type CandidatesServiceApi, type CategoriesServiceApi, type CategoryHierarchy, type CategoryHierarchyItem, type CategoryMutationResult, type ContainerRef, type ContentRole, type ConversationFileLink, type CoreDeps, type CreateEntityRowArgs, DEFAULT_ORG_COLUMN, DEFAULT_OWNER_COLUMN, type DemandedSchemaReport, type EdgeAttrs, type EdgeSpec, type EntityInfo, type EntityRef, type EntityRegistry, type EntityRowResult, type EntityRowsServiceApi, type FavoritesServiceApi, type ListCandidatesArgs, NON_CONTENT_SOURCE_TYPES, type PgErrorMapper, type RpcResultHelpers, SELF_TEST_MISSING_RPC, type SearchAcrossTokensArgs, type TitlesServiceApi, type UniversalCandidate, assertDemandedSchema, associationsKey, buildCategoryHierarchy, createAssociationGuards, createAssociationHelpers, createAssociationsService, createAssociationsStore, createCandidatesService, createCategoriesService, createEntityRegistry, createEntityRowsService, createFavoritesService, createRpcResultHelpers, createTitlesService, entityTitleCacheKey, err, firstError, isContentRole, isContentSourceEdge, isMembershipMetadata, isTransportFailure, isUuid, ok, resolveEntityToken };
|
|
743
|
+
export { type AddAssociationArgs, type AddCommentArgs, type AssertDemandedSchemaOptions, type AssociationGuards, type AssociationHelpersApi, type AssociationWriteResult, type AssociationsServiceApi, type AssociationsStore, type CandidateRecord, type CandidatesResult, type CandidatesServiceApi, type CategoriesServiceApi, type CategoryHierarchy, type CategoryHierarchyItem, type CategoryMutationResult, type CommentMutationResult, type CommentsServiceApi, type ContainerRef, type ContentRole, type ConversationFileLink, type CoreDeps, type CreateEntityRowArgs, DEFAULT_ORG_COLUMN, DEFAULT_OWNER_COLUMN, type DemandedSchemaReport, type EdgeAttrs, type EdgeSpec, type EntityInfo, type EntityRef, type EntityRegistry, type EntityRowResult, type EntityRowsServiceApi, type FavoritesServiceApi, type ListCandidatesArgs, NON_CONTENT_SOURCE_TYPES, type PgErrorMapper, type RpcResultHelpers, SELF_TEST_MISSING_RPC, type SearchAcrossTokensArgs, type TitlesServiceApi, type UniversalCandidate, assertDemandedSchema, associationsKey, buildCategoryHierarchy, createAssociationGuards, createAssociationHelpers, createAssociationsService, createAssociationsStore, createCandidatesService, createCategoriesService, createCommentsService, createEntityRegistry, createEntityRowsService, createFavoritesService, createRpcResultHelpers, createTitlesService, entityTitleCacheKey, err, firstError, isContentRole, isContentSourceEdge, isMembershipMetadata, isTransportFailure, isUuid, ok, resolveEntityToken };
|
package/dist/core/index.js
CHANGED
|
@@ -641,6 +641,7 @@ var ENTITY_TYPE_METADATA = {
|
|
|
641
641
|
"workflow": { token: "workflow", schema: "workflow", table: "definition", label: "Workflow", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: true, titleColumn: "name", contentRole: "utility", referenceCategory: null },
|
|
642
642
|
"workflow_card": { token: "workflow_card", schema: "workflow", table: "card", label: "Workflow Card", baseTier: 1, isComponent: true, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
|
|
643
643
|
"workflow_checkpoint": { token: "workflow_checkpoint", schema: "workflow", table: "checkpoint", label: "Workflow Checkpoint", baseTier: 1, isComponent: true, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
|
|
644
|
+
"workflow_comparison": { token: "workflow_comparison", schema: "workflow", table: "comparison", label: "Workflow Comparison", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
|
|
644
645
|
"workflow_definition_version": { token: "workflow_definition_version", schema: "workflow", table: "definition_version", label: "Workflow Definition Version", baseTier: 1, isComponent: true, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: "name", contentRole: null, referenceCategory: null },
|
|
645
646
|
"workflow_idempotency": { token: "workflow_idempotency", schema: "workflow", table: "idempotency", label: "Workflow Idempotency", baseTier: 1, isComponent: true, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
|
|
646
647
|
"workflow_job": { token: "workflow_job", schema: "workflow", table: "job", label: "Workflow Job", baseTier: 1, isComponent: true, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
|
|
@@ -1297,6 +1298,7 @@ var ENTITY_TYPE_TOKENS = [
|
|
|
1297
1298
|
"workflow",
|
|
1298
1299
|
"workflow_card",
|
|
1299
1300
|
"workflow_checkpoint",
|
|
1301
|
+
"workflow_comparison",
|
|
1300
1302
|
"workflow_definition_version",
|
|
1301
1303
|
"workflow_idempotency",
|
|
1302
1304
|
"workflow_job",
|
|
@@ -2661,6 +2663,122 @@ function createAssociationHelpers(service) {
|
|
|
2661
2663
|
};
|
|
2662
2664
|
}
|
|
2663
2665
|
|
|
2666
|
+
// src/core/commentsService.ts
|
|
2667
|
+
function toComment(row) {
|
|
2668
|
+
return {
|
|
2669
|
+
id: row.id,
|
|
2670
|
+
orgId: row.organization_id ?? null,
|
|
2671
|
+
entityType: row.entity_type,
|
|
2672
|
+
entityId: row.entity_id,
|
|
2673
|
+
parentId: row.parent_id ?? null,
|
|
2674
|
+
body: row.body,
|
|
2675
|
+
createdAt: row.created_at,
|
|
2676
|
+
updatedAt: row.updated_at,
|
|
2677
|
+
createdBy: row.created_by ?? null,
|
|
2678
|
+
author: {
|
|
2679
|
+
email: row.author_email ?? null,
|
|
2680
|
+
displayName: row.author_display_name ?? null,
|
|
2681
|
+
avatarUrl: row.author_avatar_url ?? null
|
|
2682
|
+
}
|
|
2683
|
+
};
|
|
2684
|
+
}
|
|
2685
|
+
function createCommentsService(deps) {
|
|
2686
|
+
const { dataSource, identity, guards } = deps;
|
|
2687
|
+
const { ok: ok2, err: err2, mapPgError, mapPgErrorPair } = deps.rpc;
|
|
2688
|
+
return {
|
|
2689
|
+
/**
|
|
2690
|
+
* All comments on `${entityType}:${entityId}`, org-filtered by RLS
|
|
2691
|
+
* inside the RPC, ordered oldest→newest. Build the thread from each
|
|
2692
|
+
* row's `parentId` (top-level comments have `parentId === null`).
|
|
2693
|
+
*/
|
|
2694
|
+
async listForEntity(entityType, entityId) {
|
|
2695
|
+
try {
|
|
2696
|
+
identity.requireUserId();
|
|
2697
|
+
const token = guards.normalizeEntityToken(entityType);
|
|
2698
|
+
const invalid = firstError(
|
|
2699
|
+
guards.checkToken("entityType", token),
|
|
2700
|
+
guards.checkUuid("entityId", entityId)
|
|
2701
|
+
);
|
|
2702
|
+
if (invalid) return { ok: false, error: invalid };
|
|
2703
|
+
const { data, error } = await dataSource.rpc("cmt_list", {
|
|
2704
|
+
p_entity_type: token,
|
|
2705
|
+
p_entity_id: entityId
|
|
2706
|
+
});
|
|
2707
|
+
if (error) return err2(...mapPgErrorPair(error));
|
|
2708
|
+
const rows = Array.isArray(data) ? data : [];
|
|
2709
|
+
return ok2({ comments: rows.map(toComment) });
|
|
2710
|
+
} catch (e) {
|
|
2711
|
+
return { ok: false, error: mapPgError(e) };
|
|
2712
|
+
}
|
|
2713
|
+
},
|
|
2714
|
+
/**
|
|
2715
|
+
* Post `body` on `${entityType}:${entityId}`; pass `parentId` to reply.
|
|
2716
|
+
* Returns the new comment id.
|
|
2717
|
+
*/
|
|
2718
|
+
async add(args) {
|
|
2719
|
+
try {
|
|
2720
|
+
identity.requireUserId();
|
|
2721
|
+
const token = guards.normalizeEntityToken(args.entityType);
|
|
2722
|
+
const invalid = firstError(
|
|
2723
|
+
guards.checkToken("entityType", token),
|
|
2724
|
+
guards.checkUuid("entityId", args.entityId),
|
|
2725
|
+
args.parentId != null ? guards.checkUuid("parentId", args.parentId) : null
|
|
2726
|
+
);
|
|
2727
|
+
if (invalid) return { ok: false, error: invalid };
|
|
2728
|
+
if (!args.body || args.body.trim().length === 0) {
|
|
2729
|
+
return err2("invalid_argument", "comment body must be non-empty");
|
|
2730
|
+
}
|
|
2731
|
+
const { data, error } = await dataSource.rpc("cmt_add", {
|
|
2732
|
+
p_entity_type: token,
|
|
2733
|
+
p_entity_id: args.entityId,
|
|
2734
|
+
p_body: args.body,
|
|
2735
|
+
p_parent_id: args.parentId ?? void 0,
|
|
2736
|
+
p_org_id: args.orgId ?? void 0
|
|
2737
|
+
});
|
|
2738
|
+
if (error) return err2(...mapPgErrorPair(error));
|
|
2739
|
+
if (!data || typeof data !== "string") {
|
|
2740
|
+
return err2("internal", "cmt_add returned no comment id");
|
|
2741
|
+
}
|
|
2742
|
+
return ok2({ id: data });
|
|
2743
|
+
} catch (e) {
|
|
2744
|
+
return { ok: false, error: mapPgError(e) };
|
|
2745
|
+
}
|
|
2746
|
+
},
|
|
2747
|
+
/** Replace a comment's body. Author-only; the RPC rejects everyone else. */
|
|
2748
|
+
async edit(id, body) {
|
|
2749
|
+
try {
|
|
2750
|
+
identity.requireUserId();
|
|
2751
|
+
const invalid = guards.checkUuid("commentId", id);
|
|
2752
|
+
if (invalid) return { ok: false, error: invalid };
|
|
2753
|
+
if (!body || body.trim().length === 0) {
|
|
2754
|
+
return err2("invalid_argument", "comment body must be non-empty");
|
|
2755
|
+
}
|
|
2756
|
+
const { error } = await dataSource.rpc("cmt_edit", {
|
|
2757
|
+
p_id: id,
|
|
2758
|
+
p_body: body
|
|
2759
|
+
});
|
|
2760
|
+
if (error) return err2(...mapPgErrorPair(error));
|
|
2761
|
+
return ok2(null);
|
|
2762
|
+
} catch (e) {
|
|
2763
|
+
return { ok: false, error: mapPgError(e) };
|
|
2764
|
+
}
|
|
2765
|
+
},
|
|
2766
|
+
/** Soft-delete a comment. Author or org member; enforced server-side. */
|
|
2767
|
+
async remove(id) {
|
|
2768
|
+
try {
|
|
2769
|
+
identity.requireUserId();
|
|
2770
|
+
const invalid = guards.checkUuid("commentId", id);
|
|
2771
|
+
if (invalid) return { ok: false, error: invalid };
|
|
2772
|
+
const { error } = await dataSource.rpc("cmt_delete", { p_id: id });
|
|
2773
|
+
if (error) return err2(...mapPgErrorPair(error));
|
|
2774
|
+
return ok2(null);
|
|
2775
|
+
} catch (e) {
|
|
2776
|
+
return { ok: false, error: mapPgError(e) };
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2664
2782
|
// src/core/store.ts
|
|
2665
2783
|
function associationsKey(type, id) {
|
|
2666
2784
|
return `${type}:${id}`;
|
|
@@ -2677,6 +2795,12 @@ var IDLE_CATEGORIES = Object.freeze({
|
|
|
2677
2795
|
fetchedAt: null,
|
|
2678
2796
|
error: null
|
|
2679
2797
|
});
|
|
2798
|
+
var IDLE_COMMENTS = Object.freeze({
|
|
2799
|
+
status: "idle",
|
|
2800
|
+
comments: Object.freeze([]),
|
|
2801
|
+
fetchedAt: null,
|
|
2802
|
+
error: null
|
|
2803
|
+
});
|
|
2680
2804
|
function createAssociationsStore(config) {
|
|
2681
2805
|
if (!config || typeof config.dataSource?.rpc !== "function") {
|
|
2682
2806
|
throw new Error(
|
|
@@ -2710,12 +2834,16 @@ function createAssociationsStore(config) {
|
|
|
2710
2834
|
const titles = createTitlesService(deps, candidates);
|
|
2711
2835
|
const entityRows = createEntityRowsService(deps, titles);
|
|
2712
2836
|
const helpers = createAssociationHelpers(associations);
|
|
2837
|
+
const comments = createCommentsService(deps);
|
|
2713
2838
|
const assocByKey = /* @__PURE__ */ new Map();
|
|
2714
2839
|
const assocSubs = /* @__PURE__ */ new Map();
|
|
2715
2840
|
const assocInFlight = /* @__PURE__ */ new Map();
|
|
2716
2841
|
const catByDim = /* @__PURE__ */ new Map();
|
|
2717
2842
|
const catSubs = /* @__PURE__ */ new Map();
|
|
2718
2843
|
const catInFlight = /* @__PURE__ */ new Map();
|
|
2844
|
+
const cmtByKey = /* @__PURE__ */ new Map();
|
|
2845
|
+
const cmtSubs = /* @__PURE__ */ new Map();
|
|
2846
|
+
const cmtInFlight = /* @__PURE__ */ new Map();
|
|
2719
2847
|
function notifyAssoc(key) {
|
|
2720
2848
|
const subs = assocSubs.get(key);
|
|
2721
2849
|
if (subs) for (const cb of [...subs]) cb();
|
|
@@ -2732,6 +2860,14 @@ function createAssociationsStore(config) {
|
|
|
2732
2860
|
catByDim.set(dimension, entry);
|
|
2733
2861
|
notifyCat(dimension);
|
|
2734
2862
|
}
|
|
2863
|
+
function notifyCmt(key) {
|
|
2864
|
+
const subs = cmtSubs.get(key);
|
|
2865
|
+
if (subs) for (const cb of [...subs]) cb();
|
|
2866
|
+
}
|
|
2867
|
+
function setCmt(key, entry) {
|
|
2868
|
+
cmtByKey.set(key, entry);
|
|
2869
|
+
notifyCmt(key);
|
|
2870
|
+
}
|
|
2735
2871
|
async function load(type, id, opts = {}) {
|
|
2736
2872
|
const force = opts.force ?? false;
|
|
2737
2873
|
if (!type || !id) return;
|
|
@@ -2821,6 +2957,49 @@ function createAssociationsStore(config) {
|
|
|
2821
2957
|
catInFlight.set(dimension, promise);
|
|
2822
2958
|
return promise;
|
|
2823
2959
|
}
|
|
2960
|
+
async function loadComments(type, id, opts = {}) {
|
|
2961
|
+
const force = opts.force ?? false;
|
|
2962
|
+
if (!type || !id) return;
|
|
2963
|
+
const key = associationsKey(type, id);
|
|
2964
|
+
const entry = cmtByKey.get(key);
|
|
2965
|
+
if (!force && entry?.status === "ready") return;
|
|
2966
|
+
const pending = cmtInFlight.get(key);
|
|
2967
|
+
if (!force && entry?.status === "loading" && pending) return pending;
|
|
2968
|
+
const prev = cmtByKey.get(key);
|
|
2969
|
+
setCmt(key, {
|
|
2970
|
+
status: "loading",
|
|
2971
|
+
comments: prev?.comments ?? [],
|
|
2972
|
+
fetchedAt: prev?.fetchedAt ?? null,
|
|
2973
|
+
// Retain the visible failure while a retry is pending (same contract
|
|
2974
|
+
// as the association cache).
|
|
2975
|
+
error: prev?.error ?? null
|
|
2976
|
+
});
|
|
2977
|
+
const promise = (async () => {
|
|
2978
|
+
try {
|
|
2979
|
+
const res = await comments.listForEntity(type, id);
|
|
2980
|
+
if (isAssociationsRpcErr(res)) {
|
|
2981
|
+
const before = cmtByKey.get(key);
|
|
2982
|
+
setCmt(key, {
|
|
2983
|
+
status: "error",
|
|
2984
|
+
comments: before?.comments ?? [],
|
|
2985
|
+
fetchedAt: before?.fetchedAt ?? null,
|
|
2986
|
+
error: res.error.message
|
|
2987
|
+
});
|
|
2988
|
+
} else {
|
|
2989
|
+
setCmt(key, {
|
|
2990
|
+
status: "ready",
|
|
2991
|
+
comments: res.data.comments,
|
|
2992
|
+
fetchedAt: Date.now(),
|
|
2993
|
+
error: null
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
} finally {
|
|
2997
|
+
cmtInFlight.delete(key);
|
|
2998
|
+
}
|
|
2999
|
+
})();
|
|
3000
|
+
cmtInFlight.set(key, promise);
|
|
3001
|
+
return promise;
|
|
3002
|
+
}
|
|
2824
3003
|
return {
|
|
2825
3004
|
// ── associations ─────────────────────────────────────────────────────
|
|
2826
3005
|
getEdges(type, id) {
|
|
@@ -2891,6 +3070,9 @@ function createAssociationsStore(config) {
|
|
|
2891
3070
|
const dims = [...catByDim.keys()];
|
|
2892
3071
|
catByDim.clear();
|
|
2893
3072
|
for (const dim of dims) notifyCat(dim);
|
|
3073
|
+
const cmtKeys = [...cmtByKey.keys()];
|
|
3074
|
+
cmtByKey.clear();
|
|
3075
|
+
for (const key of cmtKeys) notifyCmt(key);
|
|
2894
3076
|
},
|
|
2895
3077
|
// ── categories ───────────────────────────────────────────────────────
|
|
2896
3078
|
getCategories(dimension) {
|
|
@@ -2965,6 +3147,52 @@ function createAssociationsStore(config) {
|
|
|
2965
3147
|
await loadCategories(args.dimension, { force: true });
|
|
2966
3148
|
return { ok: true, id: res.data.id };
|
|
2967
3149
|
},
|
|
3150
|
+
// ── comments (W6) ────────────────────────────────────────────────────
|
|
3151
|
+
getComments(type, id) {
|
|
3152
|
+
if (!type || !id) return IDLE_COMMENTS;
|
|
3153
|
+
return cmtByKey.get(associationsKey(type, id)) ?? IDLE_COMMENTS;
|
|
3154
|
+
},
|
|
3155
|
+
loadComments,
|
|
3156
|
+
subscribeComments(key, cb) {
|
|
3157
|
+
let subs = cmtSubs.get(key);
|
|
3158
|
+
if (!subs) {
|
|
3159
|
+
subs = /* @__PURE__ */ new Set();
|
|
3160
|
+
cmtSubs.set(key, subs);
|
|
3161
|
+
}
|
|
3162
|
+
subs.add(cb);
|
|
3163
|
+
return () => {
|
|
3164
|
+
subs.delete(cb);
|
|
3165
|
+
if (subs.size === 0) cmtSubs.delete(key);
|
|
3166
|
+
};
|
|
3167
|
+
},
|
|
3168
|
+
async addComment(args) {
|
|
3169
|
+
const res = await comments.add(args);
|
|
3170
|
+
if (isAssociationsRpcErr(res)) {
|
|
3171
|
+
return { ok: false, error: res.error.message };
|
|
3172
|
+
}
|
|
3173
|
+
await loadComments(args.entityType, args.entityId, { force: true });
|
|
3174
|
+
return { ok: true, id: res.data.id };
|
|
3175
|
+
},
|
|
3176
|
+
async editComment(args) {
|
|
3177
|
+
const res = await comments.edit(args.id, args.body);
|
|
3178
|
+
if (isAssociationsRpcErr(res)) {
|
|
3179
|
+
return { ok: false, error: res.error.message };
|
|
3180
|
+
}
|
|
3181
|
+
await loadComments(args.entityType, args.entityId, { force: true });
|
|
3182
|
+
return { ok: true, id: args.id };
|
|
3183
|
+
},
|
|
3184
|
+
async deleteComment(args) {
|
|
3185
|
+
const res = await comments.remove(args.id);
|
|
3186
|
+
if (isAssociationsRpcErr(res)) {
|
|
3187
|
+
return { ok: false, error: res.error.message };
|
|
3188
|
+
}
|
|
3189
|
+
await loadComments(args.entityType, args.entityId, { force: true });
|
|
3190
|
+
return { ok: true, id: args.id };
|
|
3191
|
+
},
|
|
3192
|
+
invalidateComments(type, id) {
|
|
3193
|
+
const key = associationsKey(type, id);
|
|
3194
|
+
if (cmtByKey.delete(key)) notifyCmt(key);
|
|
3195
|
+
},
|
|
2968
3196
|
// ── seams ────────────────────────────────────────────────────────────
|
|
2969
3197
|
titles,
|
|
2970
3198
|
favorites,
|
|
@@ -2972,7 +3200,7 @@ function createAssociationsStore(config) {
|
|
|
2972
3200
|
entityRows,
|
|
2973
3201
|
helpers,
|
|
2974
3202
|
registry,
|
|
2975
|
-
services: { associations, categories },
|
|
3203
|
+
services: { associations, categories, comments },
|
|
2976
3204
|
registerEntityOverlay: registry.registerEntityOverlay,
|
|
2977
3205
|
errorSink,
|
|
2978
3206
|
identity: config.identity
|
|
@@ -3003,7 +3231,11 @@ var DEMANDED_RPC_NAMES = [
|
|
|
3003
3231
|
"ues_list",
|
|
3004
3232
|
"ues_get_bulk",
|
|
3005
3233
|
"ues_touch",
|
|
3006
|
-
"reference_search_candidates"
|
|
3234
|
+
"reference_search_candidates",
|
|
3235
|
+
"cmt_list",
|
|
3236
|
+
"cmt_add",
|
|
3237
|
+
"cmt_edit",
|
|
3238
|
+
"cmt_delete"
|
|
3007
3239
|
];
|
|
3008
3240
|
|
|
3009
3241
|
// src/core/assertDemandedSchema.ts
|
|
@@ -3068,7 +3300,17 @@ var PROBE_ARGS = {
|
|
|
3068
3300
|
ues_get_bulk: { p_entity_type: "__probe__", p_entity_ids: [BAD_UUID] },
|
|
3069
3301
|
ues_touch: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
|
|
3070
3302
|
// candidates
|
|
3071
|
-
reference_search_candidates: { p_token: "__probe__", p_limit: 1 }
|
|
3303
|
+
reference_search_candidates: { p_token: "__probe__", p_limit: 1 },
|
|
3304
|
+
// comments (W6) — every fn takes a uuid arg, so the unparseable sentinel
|
|
3305
|
+
// guarantees 22P02 before any SECURITY DEFINER body runs (no write occurs).
|
|
3306
|
+
cmt_list: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
|
|
3307
|
+
cmt_add: {
|
|
3308
|
+
p_entity_type: "__probe__",
|
|
3309
|
+
p_entity_id: BAD_UUID,
|
|
3310
|
+
p_body: "__probe__"
|
|
3311
|
+
},
|
|
3312
|
+
cmt_edit: { p_id: BAD_UUID, p_body: "__probe__" },
|
|
3313
|
+
cmt_delete: { p_id: BAD_UUID }
|
|
3072
3314
|
};
|
|
3073
3315
|
function isMissingFunctionError(error) {
|
|
3074
3316
|
if (!error || typeof error !== "object") return false;
|
|
@@ -3222,6 +3464,7 @@ export {
|
|
|
3222
3464
|
createAssociationsStore,
|
|
3223
3465
|
createCandidatesService,
|
|
3224
3466
|
createCategoriesService,
|
|
3467
|
+
createCommentsService,
|
|
3225
3468
|
createEntityRegistry,
|
|
3226
3469
|
createEntityRowsService,
|
|
3227
3470
|
createFavoritesService,
|