@ai-matrx/associations 0.3.0 → 0.5.0

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.
@@ -423,6 +423,7 @@ var ENTITY_TYPE_METADATA = {
423
423
  "platform_actor_session": { token: "platform_actor_session", schema: "platform", table: "actor_session", label: "Actor session", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
424
424
  "platform_actor_token": { token: "platform_actor_token", schema: "platform", table: "actor_token", label: "Actor token", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
425
425
  "platform_actor_token_event": { token: "platform_actor_token_event", schema: "platform", table: "actor_token_event", label: "Actor token event", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
426
+ "platform_continued_access": { token: "platform_continued_access", schema: "platform", table: "continued_access", label: "Continued Access", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
426
427
  "platform_outcome_event": { token: "platform_outcome_event", schema: "platform", table: "outcome_event", label: "Outcome Event", baseTier: 1, isComponent: false, isModule: false, isListed: true, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
427
428
  "platform_outsider_consumer": { token: "platform_outsider_consumer", schema: "platform", table: "outsider_consumer", label: "Outsider consumer", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
428
429
  "platform_saved_view": { token: "platform_saved_view", schema: "platform", table: "saved_view", label: "Saved view", baseTier: 1, isComponent: false, isModule: false, isListed: true, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
@@ -451,6 +452,7 @@ var ENTITY_TYPE_METADATA = {
451
452
  "research_tag": { token: "research_tag", schema: "research", table: "rs_tag", label: "Research Tag", baseTier: 1, isComponent: true, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: "name", contentRole: null, referenceCategory: null },
452
453
  "research_template": { token: "research_template", schema: "research", table: "rs_template", label: "Research Template", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: true, titleColumn: "name", contentRole: null, referenceCategory: null },
453
454
  "research_topic": { token: "research_topic", schema: "research", table: "rs_topic", label: "Research Topic", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: true, titleColumn: "name", contentRole: null, referenceCategory: null },
455
+ "route_manifest_entry": { token: "route_manifest_entry", schema: "platform", table: "route_manifest", label: "Route Manifest Entry", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
454
456
  "rulebook": { token: "rulebook", schema: "platform", table: "rulebook", label: "Rulebook", baseTier: 1, isComponent: false, isModule: false, isListed: true, scopeable: true, category: "Knowledge", referencePickable: true, titleColumn: "name", contentRole: "source", referenceCategory: null },
455
457
  "sandbox_instance": { token: "sandbox_instance", schema: "public", table: "sandbox_instances", label: "Sandbox Instance", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
456
458
  "sch_agent_task": { token: "sch_agent_task", schema: "scheduler", table: "sch_agent_task", label: "Agent Task Config", baseTier: 1, isComponent: true, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
@@ -529,6 +531,7 @@ var ENTITY_TYPE_METADATA = {
529
531
  "seo_topic": { token: "seo_topic", schema: "seo", table: "topic", label: "SEO Topic", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: "seo", referencePickable: true, titleColumn: "name", contentRole: null, referenceCategory: null },
530
532
  "seo_web_analytics_daily": { token: "seo_web_analytics_daily", schema: "seo", table: "web_analytics_daily", label: "Web Analytics Daily", baseTier: 1, isComponent: true, isModule: false, isListed: false, scopeable: true, category: "marketing", referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
531
533
  "shared_canvas_item": { token: "shared_canvas_item", schema: "canvas", table: "shared_canvas_items", label: "Shared Canvas Item", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: true, titleColumn: "title", contentRole: null, referenceCategory: null },
534
+ "short_link": { token: "short_link", schema: "platform", table: "short_links", label: "Short Link", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
532
535
  "skill": { token: "skill", schema: "skill", table: "definition", label: "Skill", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: "Skills", referencePickable: true, titleColumn: "label", contentRole: "utility", referenceCategory: null },
533
536
  "skill_render_definition": { token: "skill_render_definition", schema: "skill", table: "render_definition", label: "Skill Render Definition", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: "Skills", referencePickable: true, titleColumn: "label", contentRole: null, referenceCategory: null },
534
537
  "sms_consent": { token: "sms_consent", schema: "communication", table: "sms_consent", label: "SMS Consent", baseTier: 1, isComponent: false, isModule: false, isListed: false, scopeable: true, category: null, referencePickable: false, titleColumn: null, contentRole: null, referenceCategory: null },
@@ -1076,6 +1079,7 @@ var ENTITY_TYPE_TOKENS = [
1076
1079
  "platform_actor_session",
1077
1080
  "platform_actor_token",
1078
1081
  "platform_actor_token_event",
1082
+ "platform_continued_access",
1079
1083
  "platform_outcome_event",
1080
1084
  "platform_outsider_consumer",
1081
1085
  "platform_saved_view",
@@ -1104,6 +1108,7 @@ var ENTITY_TYPE_TOKENS = [
1104
1108
  "research_tag",
1105
1109
  "research_template",
1106
1110
  "research_topic",
1111
+ "route_manifest_entry",
1107
1112
  "rulebook",
1108
1113
  "sandbox_instance",
1109
1114
  "sch_agent_task",
@@ -1182,6 +1187,7 @@ var ENTITY_TYPE_TOKENS = [
1182
1187
  "seo_topic",
1183
1188
  "seo_web_analytics_daily",
1184
1189
  "shared_canvas_item",
1190
+ "short_link",
1185
1191
  "skill",
1186
1192
  "skill_render_definition",
1187
1193
  "sms_consent",
@@ -2655,6 +2661,122 @@ function createAssociationHelpers(service) {
2655
2661
  };
2656
2662
  }
2657
2663
 
2664
+ // src/core/commentsService.ts
2665
+ function toComment(row) {
2666
+ return {
2667
+ id: row.id,
2668
+ orgId: row.organization_id ?? null,
2669
+ entityType: row.entity_type,
2670
+ entityId: row.entity_id,
2671
+ parentId: row.parent_id ?? null,
2672
+ body: row.body,
2673
+ createdAt: row.created_at,
2674
+ updatedAt: row.updated_at,
2675
+ createdBy: row.created_by ?? null,
2676
+ author: {
2677
+ email: row.author_email ?? null,
2678
+ displayName: row.author_display_name ?? null,
2679
+ avatarUrl: row.author_avatar_url ?? null
2680
+ }
2681
+ };
2682
+ }
2683
+ function createCommentsService(deps) {
2684
+ const { dataSource, identity, guards } = deps;
2685
+ const { ok: ok2, err: err2, mapPgError, mapPgErrorPair } = deps.rpc;
2686
+ return {
2687
+ /**
2688
+ * All comments on `${entityType}:${entityId}`, org-filtered by RLS
2689
+ * inside the RPC, ordered oldest→newest. Build the thread from each
2690
+ * row's `parentId` (top-level comments have `parentId === null`).
2691
+ */
2692
+ async listForEntity(entityType, entityId) {
2693
+ try {
2694
+ identity.requireUserId();
2695
+ const token = guards.normalizeEntityToken(entityType);
2696
+ const invalid = firstError(
2697
+ guards.checkToken("entityType", token),
2698
+ guards.checkUuid("entityId", entityId)
2699
+ );
2700
+ if (invalid) return { ok: false, error: invalid };
2701
+ const { data, error } = await dataSource.rpc("cmt_list", {
2702
+ p_entity_type: token,
2703
+ p_entity_id: entityId
2704
+ });
2705
+ if (error) return err2(...mapPgErrorPair(error));
2706
+ const rows = Array.isArray(data) ? data : [];
2707
+ return ok2({ comments: rows.map(toComment) });
2708
+ } catch (e) {
2709
+ return { ok: false, error: mapPgError(e) };
2710
+ }
2711
+ },
2712
+ /**
2713
+ * Post `body` on `${entityType}:${entityId}`; pass `parentId` to reply.
2714
+ * Returns the new comment id.
2715
+ */
2716
+ async add(args) {
2717
+ try {
2718
+ identity.requireUserId();
2719
+ const token = guards.normalizeEntityToken(args.entityType);
2720
+ const invalid = firstError(
2721
+ guards.checkToken("entityType", token),
2722
+ guards.checkUuid("entityId", args.entityId),
2723
+ args.parentId != null ? guards.checkUuid("parentId", args.parentId) : null
2724
+ );
2725
+ if (invalid) return { ok: false, error: invalid };
2726
+ if (!args.body || args.body.trim().length === 0) {
2727
+ return err2("invalid_argument", "comment body must be non-empty");
2728
+ }
2729
+ const { data, error } = await dataSource.rpc("cmt_add", {
2730
+ p_entity_type: token,
2731
+ p_entity_id: args.entityId,
2732
+ p_body: args.body,
2733
+ p_parent_id: args.parentId ?? void 0,
2734
+ p_org_id: args.orgId ?? void 0
2735
+ });
2736
+ if (error) return err2(...mapPgErrorPair(error));
2737
+ if (!data || typeof data !== "string") {
2738
+ return err2("internal", "cmt_add returned no comment id");
2739
+ }
2740
+ return ok2({ id: data });
2741
+ } catch (e) {
2742
+ return { ok: false, error: mapPgError(e) };
2743
+ }
2744
+ },
2745
+ /** Replace a comment's body. Author-only; the RPC rejects everyone else. */
2746
+ async edit(id, body) {
2747
+ try {
2748
+ identity.requireUserId();
2749
+ const invalid = guards.checkUuid("commentId", id);
2750
+ if (invalid) return { ok: false, error: invalid };
2751
+ if (!body || body.trim().length === 0) {
2752
+ return err2("invalid_argument", "comment body must be non-empty");
2753
+ }
2754
+ const { error } = await dataSource.rpc("cmt_edit", {
2755
+ p_id: id,
2756
+ p_body: body
2757
+ });
2758
+ if (error) return err2(...mapPgErrorPair(error));
2759
+ return ok2(null);
2760
+ } catch (e) {
2761
+ return { ok: false, error: mapPgError(e) };
2762
+ }
2763
+ },
2764
+ /** Soft-delete a comment. Author or org member; enforced server-side. */
2765
+ async remove(id) {
2766
+ try {
2767
+ identity.requireUserId();
2768
+ const invalid = guards.checkUuid("commentId", id);
2769
+ if (invalid) return { ok: false, error: invalid };
2770
+ const { error } = await dataSource.rpc("cmt_delete", { p_id: id });
2771
+ if (error) return err2(...mapPgErrorPair(error));
2772
+ return ok2(null);
2773
+ } catch (e) {
2774
+ return { ok: false, error: mapPgError(e) };
2775
+ }
2776
+ }
2777
+ };
2778
+ }
2779
+
2658
2780
  // src/core/store.ts
2659
2781
  function associationsKey(type, id) {
2660
2782
  return `${type}:${id}`;
@@ -2671,6 +2793,12 @@ var IDLE_CATEGORIES = Object.freeze({
2671
2793
  fetchedAt: null,
2672
2794
  error: null
2673
2795
  });
2796
+ var IDLE_COMMENTS = Object.freeze({
2797
+ status: "idle",
2798
+ comments: Object.freeze([]),
2799
+ fetchedAt: null,
2800
+ error: null
2801
+ });
2674
2802
  function createAssociationsStore(config) {
2675
2803
  if (!config || typeof config.dataSource?.rpc !== "function") {
2676
2804
  throw new Error(
@@ -2704,12 +2832,16 @@ function createAssociationsStore(config) {
2704
2832
  const titles = createTitlesService(deps, candidates);
2705
2833
  const entityRows = createEntityRowsService(deps, titles);
2706
2834
  const helpers = createAssociationHelpers(associations);
2835
+ const comments = createCommentsService(deps);
2707
2836
  const assocByKey = /* @__PURE__ */ new Map();
2708
2837
  const assocSubs = /* @__PURE__ */ new Map();
2709
2838
  const assocInFlight = /* @__PURE__ */ new Map();
2710
2839
  const catByDim = /* @__PURE__ */ new Map();
2711
2840
  const catSubs = /* @__PURE__ */ new Map();
2712
2841
  const catInFlight = /* @__PURE__ */ new Map();
2842
+ const cmtByKey = /* @__PURE__ */ new Map();
2843
+ const cmtSubs = /* @__PURE__ */ new Map();
2844
+ const cmtInFlight = /* @__PURE__ */ new Map();
2713
2845
  function notifyAssoc(key) {
2714
2846
  const subs = assocSubs.get(key);
2715
2847
  if (subs) for (const cb of [...subs]) cb();
@@ -2726,6 +2858,14 @@ function createAssociationsStore(config) {
2726
2858
  catByDim.set(dimension, entry);
2727
2859
  notifyCat(dimension);
2728
2860
  }
2861
+ function notifyCmt(key) {
2862
+ const subs = cmtSubs.get(key);
2863
+ if (subs) for (const cb of [...subs]) cb();
2864
+ }
2865
+ function setCmt(key, entry) {
2866
+ cmtByKey.set(key, entry);
2867
+ notifyCmt(key);
2868
+ }
2729
2869
  async function load(type, id, opts = {}) {
2730
2870
  const force = opts.force ?? false;
2731
2871
  if (!type || !id) return;
@@ -2815,6 +2955,49 @@ function createAssociationsStore(config) {
2815
2955
  catInFlight.set(dimension, promise);
2816
2956
  return promise;
2817
2957
  }
2958
+ async function loadComments(type, id, opts = {}) {
2959
+ const force = opts.force ?? false;
2960
+ if (!type || !id) return;
2961
+ const key = associationsKey(type, id);
2962
+ const entry = cmtByKey.get(key);
2963
+ if (!force && entry?.status === "ready") return;
2964
+ const pending = cmtInFlight.get(key);
2965
+ if (!force && entry?.status === "loading" && pending) return pending;
2966
+ const prev = cmtByKey.get(key);
2967
+ setCmt(key, {
2968
+ status: "loading",
2969
+ comments: prev?.comments ?? [],
2970
+ fetchedAt: prev?.fetchedAt ?? null,
2971
+ // Retain the visible failure while a retry is pending (same contract
2972
+ // as the association cache).
2973
+ error: prev?.error ?? null
2974
+ });
2975
+ const promise = (async () => {
2976
+ try {
2977
+ const res = await comments.listForEntity(type, id);
2978
+ if (isAssociationsRpcErr(res)) {
2979
+ const before = cmtByKey.get(key);
2980
+ setCmt(key, {
2981
+ status: "error",
2982
+ comments: before?.comments ?? [],
2983
+ fetchedAt: before?.fetchedAt ?? null,
2984
+ error: res.error.message
2985
+ });
2986
+ } else {
2987
+ setCmt(key, {
2988
+ status: "ready",
2989
+ comments: res.data.comments,
2990
+ fetchedAt: Date.now(),
2991
+ error: null
2992
+ });
2993
+ }
2994
+ } finally {
2995
+ cmtInFlight.delete(key);
2996
+ }
2997
+ })();
2998
+ cmtInFlight.set(key, promise);
2999
+ return promise;
3000
+ }
2818
3001
  return {
2819
3002
  // ── associations ─────────────────────────────────────────────────────
2820
3003
  getEdges(type, id) {
@@ -2885,6 +3068,9 @@ function createAssociationsStore(config) {
2885
3068
  const dims = [...catByDim.keys()];
2886
3069
  catByDim.clear();
2887
3070
  for (const dim of dims) notifyCat(dim);
3071
+ const cmtKeys = [...cmtByKey.keys()];
3072
+ cmtByKey.clear();
3073
+ for (const key of cmtKeys) notifyCmt(key);
2888
3074
  },
2889
3075
  // ── categories ───────────────────────────────────────────────────────
2890
3076
  getCategories(dimension) {
@@ -2959,6 +3145,52 @@ function createAssociationsStore(config) {
2959
3145
  await loadCategories(args.dimension, { force: true });
2960
3146
  return { ok: true, id: res.data.id };
2961
3147
  },
3148
+ // ── comments (W6) ────────────────────────────────────────────────────
3149
+ getComments(type, id) {
3150
+ if (!type || !id) return IDLE_COMMENTS;
3151
+ return cmtByKey.get(associationsKey(type, id)) ?? IDLE_COMMENTS;
3152
+ },
3153
+ loadComments,
3154
+ subscribeComments(key, cb) {
3155
+ let subs = cmtSubs.get(key);
3156
+ if (!subs) {
3157
+ subs = /* @__PURE__ */ new Set();
3158
+ cmtSubs.set(key, subs);
3159
+ }
3160
+ subs.add(cb);
3161
+ return () => {
3162
+ subs.delete(cb);
3163
+ if (subs.size === 0) cmtSubs.delete(key);
3164
+ };
3165
+ },
3166
+ async addComment(args) {
3167
+ const res = await comments.add(args);
3168
+ if (isAssociationsRpcErr(res)) {
3169
+ return { ok: false, error: res.error.message };
3170
+ }
3171
+ await loadComments(args.entityType, args.entityId, { force: true });
3172
+ return { ok: true, id: res.data.id };
3173
+ },
3174
+ async editComment(args) {
3175
+ const res = await comments.edit(args.id, args.body);
3176
+ if (isAssociationsRpcErr(res)) {
3177
+ return { ok: false, error: res.error.message };
3178
+ }
3179
+ await loadComments(args.entityType, args.entityId, { force: true });
3180
+ return { ok: true, id: args.id };
3181
+ },
3182
+ async deleteComment(args) {
3183
+ const res = await comments.remove(args.id);
3184
+ if (isAssociationsRpcErr(res)) {
3185
+ return { ok: false, error: res.error.message };
3186
+ }
3187
+ await loadComments(args.entityType, args.entityId, { force: true });
3188
+ return { ok: true, id: args.id };
3189
+ },
3190
+ invalidateComments(type, id) {
3191
+ const key = associationsKey(type, id);
3192
+ if (cmtByKey.delete(key)) notifyCmt(key);
3193
+ },
2962
3194
  // ── seams ────────────────────────────────────────────────────────────
2963
3195
  titles,
2964
3196
  favorites,
@@ -2966,7 +3198,7 @@ function createAssociationsStore(config) {
2966
3198
  entityRows,
2967
3199
  helpers,
2968
3200
  registry,
2969
- services: { associations, categories },
3201
+ services: { associations, categories, comments },
2970
3202
  registerEntityOverlay: registry.registerEntityOverlay,
2971
3203
  errorSink,
2972
3204
  identity: config.identity
@@ -2997,7 +3229,11 @@ var DEMANDED_RPC_NAMES = [
2997
3229
  "ues_list",
2998
3230
  "ues_get_bulk",
2999
3231
  "ues_touch",
3000
- "reference_search_candidates"
3232
+ "reference_search_candidates",
3233
+ "cmt_list",
3234
+ "cmt_add",
3235
+ "cmt_edit",
3236
+ "cmt_delete"
3001
3237
  ];
3002
3238
 
3003
3239
  // src/core/assertDemandedSchema.ts
@@ -3062,7 +3298,17 @@ var PROBE_ARGS = {
3062
3298
  ues_get_bulk: { p_entity_type: "__probe__", p_entity_ids: [BAD_UUID] },
3063
3299
  ues_touch: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
3064
3300
  // candidates
3065
- reference_search_candidates: { p_token: "__probe__", p_limit: 1 }
3301
+ reference_search_candidates: { p_token: "__probe__", p_limit: 1 },
3302
+ // comments (W6) — every fn takes a uuid arg, so the unparseable sentinel
3303
+ // guarantees 22P02 before any SECURITY DEFINER body runs (no write occurs).
3304
+ cmt_list: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
3305
+ cmt_add: {
3306
+ p_entity_type: "__probe__",
3307
+ p_entity_id: BAD_UUID,
3308
+ p_body: "__probe__"
3309
+ },
3310
+ cmt_edit: { p_id: BAD_UUID, p_body: "__probe__" },
3311
+ cmt_delete: { p_id: BAD_UUID }
3066
3312
  };
3067
3313
  function isMissingFunctionError(error) {
3068
3314
  if (!error || typeof error !== "object") return false;
@@ -3138,6 +3384,70 @@ function isContentSourceEdge(sourceType, metadata) {
3138
3384
  if (isMembershipMetadata(metadata)) return false;
3139
3385
  return true;
3140
3386
  }
3387
+
3388
+ // src/core/categoryHierarchy.ts
3389
+ function buildCategoryHierarchy(categories) {
3390
+ const byId = new Map(categories.map((category) => [category.id, category]));
3391
+ const hasHierarchy = categories.some(
3392
+ (category) => category.parentId !== null && byId.has(category.parentId)
3393
+ );
3394
+ if (!hasHierarchy) {
3395
+ return {
3396
+ items: categories.map((category) => ({
3397
+ category,
3398
+ depth: 0,
3399
+ parent: null,
3400
+ displayName: category.name
3401
+ })),
3402
+ roots: categories.filter((category) => category.parentId === null),
3403
+ hasHierarchy: false
3404
+ };
3405
+ }
3406
+ const roots = categories.filter(
3407
+ (category) => category.parentId === null || !byId.has(category.parentId)
3408
+ );
3409
+ const childrenByParent = /* @__PURE__ */ new Map();
3410
+ for (const category of categories) {
3411
+ if (!category.parentId || !byId.has(category.parentId)) continue;
3412
+ const children = childrenByParent.get(category.parentId) ?? [];
3413
+ children.push(category);
3414
+ childrenByParent.set(category.parentId, children);
3415
+ }
3416
+ const items = [];
3417
+ const seen = /* @__PURE__ */ new Set();
3418
+ for (const root of roots) {
3419
+ items.push({
3420
+ category: root,
3421
+ depth: 0,
3422
+ parent: null,
3423
+ displayName: root.name
3424
+ });
3425
+ seen.add(root.id);
3426
+ for (const child of childrenByParent.get(root.id) ?? []) {
3427
+ items.push({
3428
+ category: child,
3429
+ depth: 1,
3430
+ parent: root,
3431
+ displayName: `${root.name} / ${child.name}`
3432
+ });
3433
+ seen.add(child.id);
3434
+ }
3435
+ }
3436
+ for (const category of categories) {
3437
+ if (seen.has(category.id)) continue;
3438
+ items.push({
3439
+ category,
3440
+ depth: 0,
3441
+ parent: null,
3442
+ displayName: category.name
3443
+ });
3444
+ }
3445
+ return {
3446
+ items,
3447
+ roots: categories.filter((category) => category.parentId === null),
3448
+ hasHierarchy: true
3449
+ };
3450
+ }
3141
3451
  export {
3142
3452
  DEFAULT_ORG_COLUMN,
3143
3453
  DEFAULT_OWNER_COLUMN,
@@ -3145,12 +3455,14 @@ export {
3145
3455
  SELF_TEST_MISSING_RPC,
3146
3456
  assertDemandedSchema,
3147
3457
  associationsKey,
3458
+ buildCategoryHierarchy,
3148
3459
  createAssociationGuards,
3149
3460
  createAssociationHelpers,
3150
3461
  createAssociationsService,
3151
3462
  createAssociationsStore,
3152
3463
  createCandidatesService,
3153
3464
  createCategoriesService,
3465
+ createCommentsService,
3154
3466
  createEntityRegistry,
3155
3467
  createEntityRowsService,
3156
3468
  createFavoritesService,