@ai-matrx/associations 0.12.1 → 0.13.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 CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog — @ai-matrx/associations
2
2
 
3
+ ## 0.13.1
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/associations/v0.12.1..npm/associations/v0.13.1 -- apps/shared/associations`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.13.0
10
+
11
+ A search that could not look no longer says "no results".
12
+
13
+ * **Breaking:** `searchCandidatesAcrossTokens` returns `{ results, failures }` instead of a bare
14
+ array. `failures: TokenSearchFailure[]` (`{ token, error }`, registry order) lists every token
15
+ whose read failed — those tokens used to be screamed and silently dropped, so a search where
16
+ some or all tokens failed looked like "nothing matched". Result order and the per-token cap are
17
+ unchanged. New types `SearchAcrossTokensResult`, `TokenSearchFailure` (from `/core`).
18
+ * `useUniversalEntitySearch` also returns `failures: UniversalSearchFailure[]` (`token: null` =
19
+ the recents read failed), `error` (non-null only when nothing could be read — every token
20
+ failed, or the recents read failed and the fallback browse failed too) and
21
+ `status: "loading" | "error" | "ready"`. A failed recents read still falls back to browsing,
22
+ but is recorded in `failures` — never silently turned into a browse.
23
+ * New `SearchFailureNotice` + `describeSearchFailures` (`/react`): the one way a face shows the
24
+ hook's failures — the ErrorBox failure block when `error` is set, and a one-line
25
+ "Couldn't search Notes, Tasks" note above partial results. `UniversalAssociationPicker` and the
26
+ `AssociationPicker` recents strip render it.
27
+ * **Breaking:** `ownerId` removed from `ListCandidatesArgs`, `SearchAcrossTokensArgs`,
28
+ `UseUniversalEntitySearchArgs` and `UniversalAssociationPickerProps`. It was never applied —
29
+ `public.reference_search_candidates(p_token, p_search, p_limit, p_ids)` has no owner
30
+ parameter; visibility is the RPC's alone. An ignored filter is a lie.
31
+
32
+ **Consumer action:** read `.results` (and handle `.failures`) from
33
+ `searchCandidatesAcrossTokens`; stop passing `ownerId` to the candidate reads, the hook and
34
+ `UniversalAssociationPicker`; render `SearchFailureNotice` (or your own use of `error` /
35
+ `failures`) wherever you render `useUniversalEntitySearch` results.
36
+
3
37
  ## 0.12.1
4
38
 
5
39
  Re-cut of 0.12.0 — same package source. The 0.12.0 tag was cut from a commit whose workspace
@@ -1008,8 +1008,6 @@ interface CandidateRecord {
1008
1008
  }
1009
1009
  interface ListCandidatesArgs {
1010
1010
  token: EntityTypeToken;
1011
- /** Scope to this owner's rows when the entity declares an owner column. */
1012
- ownerId?: string | null;
1013
1011
  /** Case-insensitive title filter. */
1014
1012
  search?: string;
1015
1013
  limit?: number;
@@ -1028,12 +1026,25 @@ interface SearchAcrossTokensArgs {
1028
1026
  /** Tokens to search. Defaults to every curated token in the registry. */
1029
1027
  tokens?: EntityTypeToken[];
1030
1028
  search: string;
1031
- ownerId?: string | null;
1032
1029
  /** Cap per token — a universal search shows a few of everything. */
1033
1030
  perTokenLimit?: number;
1034
1031
  /** Max in-flight token queries. */
1035
1032
  concurrency?: number;
1036
1033
  }
1034
+ /** One token whose candidate read failed during a cross-token search. */
1035
+ interface TokenSearchFailure {
1036
+ token: EntityTypeToken;
1037
+ error: string;
1038
+ }
1039
+ /**
1040
+ * A cross-token search's honest answer: what could be read, AND what could
1041
+ * not. An empty `results` with non-empty `failures` is "we couldn't look",
1042
+ * never "there is nothing".
1043
+ */
1044
+ interface SearchAcrossTokensResult {
1045
+ results: UniversalCandidate[];
1046
+ failures: TokenSearchFailure[];
1047
+ }
1037
1048
  interface ReferenceSearchCandidatesFn {
1038
1049
  (args: {
1039
1050
  p_token: string;
@@ -1063,10 +1074,12 @@ interface CandidatesServiceApi {
1063
1074
  /**
1064
1075
  * ONE search over EVERY listable entity type — the "attach anything from
1065
1076
  * anywhere" primitive. Registry-driven client fan-out, capped and
1066
- * concurrency-limited. Per-token failures are screamed and skipped — one
1067
- * broken table never blanks the search.
1077
+ * concurrency-limited. One broken table never blanks the search: a failed
1078
+ * token is screamed (errorSink) AND reported in `failures` (registry
1079
+ * order), so a face can say what it could not search instead of showing
1080
+ * "no results".
1068
1081
  */
1069
- searchCandidatesAcrossTokens(args: SearchAcrossTokensArgs): Promise<UniversalCandidate[]>;
1082
+ searchCandidatesAcrossTokens(args: SearchAcrossTokensArgs): Promise<SearchAcrossTokensResult>;
1070
1083
  }
1071
1084
 
1072
1085
  interface TitlesServiceApi {
@@ -1630,8 +1643,6 @@ interface UniversalAssociationPickerProps {
1630
1643
  ok: boolean;
1631
1644
  error?: string;
1632
1645
  }>;
1633
- /** Owner filter for candidate reads (defaults to RLS-only). */
1634
- ownerId?: string | null;
1635
1646
  /** Org stamped onto rows created via the per-token "+ New" footer. */
1636
1647
  orgId?: string | null;
1637
1648
  /**
@@ -1008,8 +1008,6 @@ interface CandidateRecord {
1008
1008
  }
1009
1009
  interface ListCandidatesArgs {
1010
1010
  token: EntityTypeToken;
1011
- /** Scope to this owner's rows when the entity declares an owner column. */
1012
- ownerId?: string | null;
1013
1011
  /** Case-insensitive title filter. */
1014
1012
  search?: string;
1015
1013
  limit?: number;
@@ -1028,12 +1026,25 @@ interface SearchAcrossTokensArgs {
1028
1026
  /** Tokens to search. Defaults to every curated token in the registry. */
1029
1027
  tokens?: EntityTypeToken[];
1030
1028
  search: string;
1031
- ownerId?: string | null;
1032
1029
  /** Cap per token — a universal search shows a few of everything. */
1033
1030
  perTokenLimit?: number;
1034
1031
  /** Max in-flight token queries. */
1035
1032
  concurrency?: number;
1036
1033
  }
1034
+ /** One token whose candidate read failed during a cross-token search. */
1035
+ interface TokenSearchFailure {
1036
+ token: EntityTypeToken;
1037
+ error: string;
1038
+ }
1039
+ /**
1040
+ * A cross-token search's honest answer: what could be read, AND what could
1041
+ * not. An empty `results` with non-empty `failures` is "we couldn't look",
1042
+ * never "there is nothing".
1043
+ */
1044
+ interface SearchAcrossTokensResult {
1045
+ results: UniversalCandidate[];
1046
+ failures: TokenSearchFailure[];
1047
+ }
1037
1048
  interface ReferenceSearchCandidatesFn {
1038
1049
  (args: {
1039
1050
  p_token: string;
@@ -1063,10 +1074,12 @@ interface CandidatesServiceApi {
1063
1074
  /**
1064
1075
  * ONE search over EVERY listable entity type — the "attach anything from
1065
1076
  * anywhere" primitive. Registry-driven client fan-out, capped and
1066
- * concurrency-limited. Per-token failures are screamed and skipped — one
1067
- * broken table never blanks the search.
1077
+ * concurrency-limited. One broken table never blanks the search: a failed
1078
+ * token is screamed (errorSink) AND reported in `failures` (registry
1079
+ * order), so a face can say what it could not search instead of showing
1080
+ * "no results".
1068
1081
  */
1069
- searchCandidatesAcrossTokens(args: SearchAcrossTokensArgs): Promise<UniversalCandidate[]>;
1082
+ searchCandidatesAcrossTokens(args: SearchAcrossTokensArgs): Promise<SearchAcrossTokensResult>;
1070
1083
  }
1071
1084
 
1072
1085
  interface TitlesServiceApi {
@@ -1630,8 +1643,6 @@ interface UniversalAssociationPickerProps {
1630
1643
  ok: boolean;
1631
1644
  error?: string;
1632
1645
  }>;
1633
- /** Owner filter for candidate reads (defaults to RLS-only). */
1634
- ownerId?: string | null;
1635
1646
  /** Org stamped onto rows created via the per-token "+ New" footer. */
1636
1647
  orgId?: string | null;
1637
1648
  /**
@@ -3112,10 +3112,11 @@ function createCandidatesService(deps) {
3112
3112
  };
3113
3113
  }
3114
3114
  async function searchCandidatesAcrossTokens(args) {
3115
- const { tokens, search, ownerId, perTokenLimit = 5, concurrency = 6 } = args;
3115
+ const { tokens, search, perTokenLimit = 5, concurrency = 6 } = args;
3116
3116
  const list = tokens ?? registry.listableTokens();
3117
- if (list.length === 0) return [];
3117
+ if (list.length === 0) return { results: [], failures: [] };
3118
3118
  const out = [];
3119
+ const failures = [];
3119
3120
  let cursor = 0;
3120
3121
  async function worker() {
3121
3122
  while (cursor < list.length) {
@@ -3124,12 +3125,13 @@ function createCandidatesService(deps) {
3124
3125
  if (!token) continue;
3125
3126
  const res = await listAssociationCandidates({
3126
3127
  token,
3127
- ownerId: ownerId ?? null,
3128
3128
  search,
3129
3129
  limit: perTokenLimit
3130
3130
  });
3131
3131
  if (res.ok) {
3132
3132
  for (const c of res.data) out.push({ ...c, token });
3133
+ } else {
3134
+ failures.push({ token, error: res.error });
3133
3135
  }
3134
3136
  }
3135
3137
  }
@@ -3143,7 +3145,10 @@ function createCandidatesService(deps) {
3143
3145
  if (ta !== tb) return ta - tb;
3144
3146
  return a.title.localeCompare(b.title);
3145
3147
  });
3146
- return out;
3148
+ failures.sort(
3149
+ (a, b) => (tokenOrder.get(a.token) ?? 0) - (tokenOrder.get(b.token) ?? 0)
3150
+ );
3151
+ return { results: out, failures };
3147
3152
  }
3148
3153
  return {
3149
3154
  callReferenceSearchCandidates,