@memberjunction/search-engine 5.32.0 → 5.34.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.
Files changed (87) hide show
  1. package/dist/generic/BaseReRanker.d.ts +164 -0
  2. package/dist/generic/BaseReRanker.d.ts.map +1 -0
  3. package/dist/generic/BaseReRanker.js +209 -0
  4. package/dist/generic/BaseReRanker.js.map +1 -0
  5. package/dist/generic/EntitySearchProvider.d.ts +51 -3
  6. package/dist/generic/EntitySearchProvider.d.ts.map +1 -1
  7. package/dist/generic/EntitySearchProvider.js +130 -17
  8. package/dist/generic/EntitySearchProvider.js.map +1 -1
  9. package/dist/generic/FullTextSearchProvider.d.ts +7 -2
  10. package/dist/generic/FullTextSearchProvider.d.ts.map +1 -1
  11. package/dist/generic/FullTextSearchProvider.js +25 -4
  12. package/dist/generic/FullTextSearchProvider.js.map +1 -1
  13. package/dist/generic/ISearchProvider.d.ts +44 -2
  14. package/dist/generic/ISearchProvider.d.ts.map +1 -1
  15. package/dist/generic/ISearchProvider.js +35 -1
  16. package/dist/generic/ISearchProvider.js.map +1 -1
  17. package/dist/generic/NoopReRanker.d.ts +28 -0
  18. package/dist/generic/NoopReRanker.d.ts.map +1 -0
  19. package/dist/generic/NoopReRanker.js +49 -0
  20. package/dist/generic/NoopReRanker.js.map +1 -0
  21. package/dist/generic/ScopeTemplateRenderer.d.ts +36 -0
  22. package/dist/generic/ScopeTemplateRenderer.d.ts.map +1 -0
  23. package/dist/generic/ScopeTemplateRenderer.js +110 -0
  24. package/dist/generic/ScopeTemplateRenderer.js.map +1 -0
  25. package/dist/generic/SearchEngine.d.ts +180 -12
  26. package/dist/generic/SearchEngine.d.ts.map +1 -1
  27. package/dist/generic/SearchEngine.js +737 -35
  28. package/dist/generic/SearchEngine.js.map +1 -1
  29. package/dist/generic/SearchFusion.d.ts +40 -6
  30. package/dist/generic/SearchFusion.d.ts.map +1 -1
  31. package/dist/generic/SearchFusion.js +139 -18
  32. package/dist/generic/SearchFusion.js.map +1 -1
  33. package/dist/generic/StorageSearchProvider.d.ts +9 -2
  34. package/dist/generic/StorageSearchProvider.d.ts.map +1 -1
  35. package/dist/generic/StorageSearchProvider.js +44 -12
  36. package/dist/generic/StorageSearchProvider.js.map +1 -1
  37. package/dist/generic/VectorSearchProvider.d.ts +9 -2
  38. package/dist/generic/VectorSearchProvider.d.ts.map +1 -1
  39. package/dist/generic/VectorSearchProvider.js +83 -13
  40. package/dist/generic/VectorSearchProvider.js.map +1 -1
  41. package/dist/generic/search.types.d.ts +206 -0
  42. package/dist/generic/search.types.d.ts.map +1 -1
  43. package/dist/index.d.ts +13 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +18 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/permissions/SearchScopePermissionResolver.d.ts +109 -0
  48. package/dist/permissions/SearchScopePermissionResolver.d.ts.map +1 -0
  49. package/dist/permissions/SearchScopePermissionResolver.js +159 -0
  50. package/dist/permissions/SearchScopePermissionResolver.js.map +1 -0
  51. package/dist/providers/AzureAISearchProvider.d.ts +37 -0
  52. package/dist/providers/AzureAISearchProvider.d.ts.map +1 -0
  53. package/dist/providers/AzureAISearchProvider.js +180 -0
  54. package/dist/providers/AzureAISearchProvider.js.map +1 -0
  55. package/dist/providers/ElasticsearchSearchProvider.d.ts +43 -0
  56. package/dist/providers/ElasticsearchSearchProvider.d.ts.map +1 -0
  57. package/dist/providers/ElasticsearchSearchProvider.js +200 -0
  58. package/dist/providers/ElasticsearchSearchProvider.js.map +1 -0
  59. package/dist/providers/OpenSearchSearchProvider.d.ts +36 -0
  60. package/dist/providers/OpenSearchSearchProvider.d.ts.map +1 -0
  61. package/dist/providers/OpenSearchSearchProvider.js +167 -0
  62. package/dist/providers/OpenSearchSearchProvider.js.map +1 -0
  63. package/dist/providers/TypesenseSearchProvider.d.ts +36 -0
  64. package/dist/providers/TypesenseSearchProvider.d.ts.map +1 -0
  65. package/dist/providers/TypesenseSearchProvider.js +161 -0
  66. package/dist/providers/TypesenseSearchProvider.js.map +1 -0
  67. package/dist/rerankers/BGEReRanker.d.ts +57 -0
  68. package/dist/rerankers/BGEReRanker.d.ts.map +1 -0
  69. package/dist/rerankers/BGEReRanker.js +193 -0
  70. package/dist/rerankers/BGEReRanker.js.map +1 -0
  71. package/dist/rerankers/CohereReRanker.d.ts +65 -0
  72. package/dist/rerankers/CohereReRanker.d.ts.map +1 -0
  73. package/dist/rerankers/CohereReRanker.js +155 -0
  74. package/dist/rerankers/CohereReRanker.js.map +1 -0
  75. package/dist/rerankers/OpenAIReRanker.d.ts +62 -0
  76. package/dist/rerankers/OpenAIReRanker.d.ts.map +1 -0
  77. package/dist/rerankers/OpenAIReRanker.js +197 -0
  78. package/dist/rerankers/OpenAIReRanker.js.map +1 -0
  79. package/dist/rerankers/RerankerBudgetGuard.d.ts +54 -0
  80. package/dist/rerankers/RerankerBudgetGuard.d.ts.map +1 -0
  81. package/dist/rerankers/RerankerBudgetGuard.js +67 -0
  82. package/dist/rerankers/RerankerBudgetGuard.js.map +1 -0
  83. package/dist/rerankers/VoyageReRanker.d.ts +59 -0
  84. package/dist/rerankers/VoyageReRanker.d.ts.map +1 -0
  85. package/dist/rerankers/VoyageReRanker.js +184 -0
  86. package/dist/rerankers/VoyageReRanker.js.map +1 -0
  87. package/package.json +13 -8
@@ -0,0 +1,109 @@
1
+ import { UserInfo } from '@memberjunction/core';
2
+ import type { MJSearchScopePermissionEntity, MJAIAgentEntity } from '@memberjunction/core-entities';
3
+ /**
4
+ * Permission level granted on a SearchScope. None is an explicit deny that
5
+ * overrides role grants for that scope/principal pair. The resolver collapses
6
+ * multiple grants into the highest level (Manage > Search > Read > None).
7
+ */
8
+ export type SearchScopePermissionLevel = 'None' | 'Read' | 'Search' | 'Manage';
9
+ /**
10
+ * Where the resolver picked up the effective permission. Used for audit
11
+ * logging and explanatory error messages.
12
+ */
13
+ export type SearchScopePermissionSource = 'DirectGrant' | 'RoleGrant' | 'AgentUnscopedAll' | 'AgentNone' | 'AgentAssignedNotListed' | 'NoGrant';
14
+ export interface EffectivePermission {
15
+ /** True when the principal can at least read the scope's metadata. */
16
+ readonly Allowed: boolean;
17
+ /** Highest level granted, or 'None' when no grant applies. */
18
+ readonly Level: SearchScopePermissionLevel;
19
+ /** Where the decision came from. */
20
+ readonly Source: SearchScopePermissionSource;
21
+ /** Human-readable explanation suitable for audit logs and error messages. */
22
+ readonly Reason: string;
23
+ /**
24
+ * Renders the effective permission as a SQL predicate fragment that can
25
+ * be ANDed into a provider's WHERE clause. Returns '1=1' when the
26
+ * permission allows unrestricted access for this scope, '1=0' when it
27
+ * rejects entirely. Providers that translate to non-SQL DSLs should
28
+ * inspect Allowed/Level instead — the predicate is for SQL Server-backed
29
+ * providers and is intentionally trivial today; richer per-scope filters
30
+ * live in the SearchScope.ScopeConfig template.
31
+ */
32
+ toSqlPredicate(): string;
33
+ }
34
+ /**
35
+ * Inputs required to resolve a permission. The resolver is server-side only
36
+ * and never reaches into request context — caller passes the identities it
37
+ * has already established.
38
+ */
39
+ export interface ResolvePermissionInput {
40
+ /**
41
+ * The acting user. Required even when an agent is invoking the search,
42
+ * because agent-mediated calls still authenticate as a user and any
43
+ * direct/role grants on that user ID still apply.
44
+ */
45
+ User: UserInfo;
46
+ /** The SearchScope being authorized. */
47
+ SearchScopeID: string;
48
+ /**
49
+ * The agent on whose behalf the search runs, or null for human-driven
50
+ * searches. When set, the agent's SearchScopeAccess column gates the
51
+ * fallback paths.
52
+ */
53
+ Agent: MJAIAgentEntity | null;
54
+ /**
55
+ * Optional ContextUser for RunView calls. Server-side code must always
56
+ * pass this to enforce data isolation; it is the same UserInfo as `User`
57
+ * unless the caller is impersonating.
58
+ */
59
+ ContextUser?: UserInfo;
60
+ }
61
+ /**
62
+ * Resolves the effective SearchScope permission for a (user, scope, agent)
63
+ * triple.
64
+ *
65
+ * Resolution order (later steps only run if the earlier did not produce a
66
+ * definitive answer):
67
+ *
68
+ * 1. Agent.SearchScopeAccess === 'None' → reject (explicit agent-side deny).
69
+ * 2. Direct grant: a SearchScopePermission row with UserID = user.ID and
70
+ * SearchScopeID = scope.ID. PermissionLevel = 'None' is an explicit
71
+ * deny that short-circuits and rejects regardless of role grants.
72
+ * 3. Role grants: SearchScopePermission rows where RoleID is in the
73
+ * user's UserRoles. The highest non-None level wins. None entries are
74
+ * ignored at the role level (see comment below).
75
+ * 4. Agent.SearchScopeAccess === 'All' → allow at Search level (lets
76
+ * trusted agents act across all scopes when no user-side grant exists).
77
+ * 5. No grant → reject.
78
+ *
79
+ * The user-direct-None rule (step 2) is intentional: an admin who explicitly
80
+ * denies a user on a scope should not have that decision overridden by a
81
+ * role membership the user happens to also hold. Role-level None entries
82
+ * are not authoritative because they are usually authored as a placeholder
83
+ * (e.g., to make a row exist before granting it later) and would create
84
+ * surprising lockouts when a user joins a role.
85
+ */
86
+ export declare class SearchScopePermissionResolver {
87
+ /**
88
+ * Resolves the effective permission. All UUID comparisons go through
89
+ * UUIDsEqual to remain case-insensitive across SQL Server / PostgreSQL.
90
+ */
91
+ ResolveEffectivePermission(input: ResolvePermissionInput): Promise<EffectivePermission>;
92
+ /**
93
+ * Checks whether the given scope is in the agent's assigned-scope list
94
+ * via __mj.AIAgentSearchScope. Used to enforce the SearchScopeAccess='Assigned'
95
+ * deny-list rule. Returns true when at least one matching row exists with
96
+ * Status='Active'; false otherwise.
97
+ */
98
+ protected isScopeAssignedToAgent(agentID: string, searchScopeID: string, contextUser: UserInfo): Promise<boolean>;
99
+ /**
100
+ * Loads all SearchScopePermission rows scoped to the given SearchScope.
101
+ * Caller-supplied ContextUser ensures the RunView runs under the same
102
+ * identity the rest of the request is using.
103
+ */
104
+ protected loadPermissionsForScope(searchScopeID: string, contextUser: UserInfo): Promise<MJSearchScopePermissionEntity[]>;
105
+ /** Bundles the result fields together with a closure-bound toSqlPredicate. */
106
+ private buildResult;
107
+ }
108
+ export declare const DefaultSearchScopePermissionResolver: SearchScopePermissionResolver;
109
+ //# sourceMappingURL=SearchScopePermissionResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchScopePermissionResolver.d.ts","sourceRoot":"","sources":["../../src/permissions/SearchScopePermissionResolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,KAAK,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEpG;;;;GAIG;AACH,MAAM,MAAM,0BAA0B,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/E;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GACjC,aAAa,GACb,WAAW,GACX,kBAAkB,GAClB,WAAW,GACX,wBAAwB,GACxB,SAAS,CAAC;AAEhB,MAAM,WAAW,mBAAmB;IAChC,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,0BAA0B,CAAC;IAC3C,oCAAoC;IACpC,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAC;IAC7C,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,cAAc,IAAI,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACnC;;;;OAIG;IACH,IAAI,EAAE,QAAQ,CAAC;IACf,wCAAwC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;CAC1B;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,6BAA6B;IACtC;;;OAGG;IACU,0BAA0B,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAmEpG;;;;;OAKG;cACa,sBAAsB,CAClC,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC,OAAO,CAAC;IAmBnB;;;;OAIG;cACa,uBAAuB,CACnC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC,6BAA6B,EAAE,CAAC;IAqB3C,8EAA8E;IAC9E,OAAO,CAAC,WAAW;CActB;AAID,eAAO,MAAM,oCAAoC,+BAAsC,CAAC"}
@@ -0,0 +1,159 @@
1
+ import { RunView } from '@memberjunction/core';
2
+ import { UUIDsEqual } from '@memberjunction/global';
3
+ const LEVEL_RANK = {
4
+ None: 0,
5
+ Read: 1,
6
+ Search: 2,
7
+ Manage: 3,
8
+ };
9
+ /**
10
+ * Picks the highest level between two grants. None is treated as the lowest
11
+ * (a None row from a role does not deny when a Read row exists for the user
12
+ * directly — the explicit deny is only authoritative when it is the user's
13
+ * own direct grant; see resolution-order rules below).
14
+ */
15
+ function highestLevel(a, b) {
16
+ return LEVEL_RANK[a] >= LEVEL_RANK[b] ? a : b;
17
+ }
18
+ /**
19
+ * Resolves the effective SearchScope permission for a (user, scope, agent)
20
+ * triple.
21
+ *
22
+ * Resolution order (later steps only run if the earlier did not produce a
23
+ * definitive answer):
24
+ *
25
+ * 1. Agent.SearchScopeAccess === 'None' → reject (explicit agent-side deny).
26
+ * 2. Direct grant: a SearchScopePermission row with UserID = user.ID and
27
+ * SearchScopeID = scope.ID. PermissionLevel = 'None' is an explicit
28
+ * deny that short-circuits and rejects regardless of role grants.
29
+ * 3. Role grants: SearchScopePermission rows where RoleID is in the
30
+ * user's UserRoles. The highest non-None level wins. None entries are
31
+ * ignored at the role level (see comment below).
32
+ * 4. Agent.SearchScopeAccess === 'All' → allow at Search level (lets
33
+ * trusted agents act across all scopes when no user-side grant exists).
34
+ * 5. No grant → reject.
35
+ *
36
+ * The user-direct-None rule (step 2) is intentional: an admin who explicitly
37
+ * denies a user on a scope should not have that decision overridden by a
38
+ * role membership the user happens to also hold. Role-level None entries
39
+ * are not authoritative because they are usually authored as a placeholder
40
+ * (e.g., to make a row exist before granting it later) and would create
41
+ * surprising lockouts when a user joins a role.
42
+ */
43
+ export class SearchScopePermissionResolver {
44
+ /**
45
+ * Resolves the effective permission. All UUID comparisons go through
46
+ * UUIDsEqual to remain case-insensitive across SQL Server / PostgreSQL.
47
+ */
48
+ async ResolveEffectivePermission(input) {
49
+ const { User, SearchScopeID, Agent } = input;
50
+ const contextUser = input.ContextUser ?? User;
51
+ // Step 1: agent-side explicit deny short-circuits everything.
52
+ if (Agent && Agent.SearchScopeAccess === 'None') {
53
+ return this.buildResult(false, 'None', 'AgentNone', `Agent '${Agent.Name}' has SearchScopeAccess='None'; refused without consulting per-scope grants.`);
54
+ }
55
+ // Step 1b: agent-side Assigned restriction. When SearchScopeAccess='Assigned'
56
+ // the agent can ONLY use scopes listed in __mj.AIAgentSearchScope for
57
+ // this agent. If the scope isn't in that list, deny early before
58
+ // consulting per-user grants — this is a deny-list, not a grant.
59
+ if (Agent && Agent.SearchScopeAccess === 'Assigned') {
60
+ const isListed = await this.isScopeAssignedToAgent(Agent.ID, SearchScopeID, contextUser);
61
+ if (!isListed) {
62
+ return this.buildResult(false, 'None', 'AgentAssignedNotListed', `Agent '${Agent.Name}' has SearchScopeAccess='Assigned' and this scope is not in its assigned scope list; refused with ACCESS_DENIED.`);
63
+ }
64
+ // Falls through to user/role checks; Assigned restricts but does
65
+ // not grant — the user must still have a per-scope grant.
66
+ }
67
+ // Load all SearchScopePermission rows for this scope. We pull the
68
+ // whole set (typically small per scope) and filter in JS so we can
69
+ // apply the user-direct-None short-circuit deterministically.
70
+ const rows = await this.loadPermissionsForScope(SearchScopeID, contextUser);
71
+ // Step 2: direct grant for this user (highest priority).
72
+ const userGrants = rows.filter(r => r.UserID && UUIDsEqual(r.UserID, User.ID));
73
+ if (userGrants.length > 0) {
74
+ // If any user-direct row is None, that is an explicit deny.
75
+ if (userGrants.some(r => r.PermissionLevel === 'None')) {
76
+ return this.buildResult(false, 'None', 'DirectGrant', `User '${User.Name}' has an explicit None grant on this scope; refused.`);
77
+ }
78
+ const level = userGrants.reduce((acc, r) => highestLevel(acc, r.PermissionLevel), 'None');
79
+ return this.buildResult(true, level, 'DirectGrant', `User '${User.Name}' has a direct grant at level '${level}' on this scope.`);
80
+ }
81
+ // Step 3: role grants. Match any of the user's roles to a row's RoleID.
82
+ const userRoleIds = User.UserRoles?.map(ur => ur.RoleID) ?? [];
83
+ const roleGrants = rows.filter(r => r.RoleID
84
+ && userRoleIds.some(uid => UUIDsEqual(uid, r.RoleID))
85
+ && r.PermissionLevel !== 'None');
86
+ if (roleGrants.length > 0) {
87
+ const level = roleGrants.reduce((acc, r) => highestLevel(acc, r.PermissionLevel), 'None');
88
+ return this.buildResult(true, level, 'RoleGrant', `User '${User.Name}' inherits level '${level}' on this scope through role membership.`);
89
+ }
90
+ // Step 4: agent fallback. SearchScopeAccess='All' lets trusted agents
91
+ // operate across scopes when the user has no per-scope grant.
92
+ if (Agent && Agent.SearchScopeAccess === 'All') {
93
+ return this.buildResult(true, 'Search', 'AgentUnscopedAll', `Agent '${Agent.Name}' has SearchScopeAccess='All'; granting 'Search' as a fallback for this scope.`);
94
+ }
95
+ // Step 5: no grant.
96
+ return this.buildResult(false, 'None', 'NoGrant', `User '${User.Name}' has no direct grant, no qualifying role grant, and no agent-side fallback for this scope.`);
97
+ }
98
+ /**
99
+ * Checks whether the given scope is in the agent's assigned-scope list
100
+ * via __mj.AIAgentSearchScope. Used to enforce the SearchScopeAccess='Assigned'
101
+ * deny-list rule. Returns true when at least one matching row exists with
102
+ * Status='Active'; false otherwise.
103
+ */
104
+ async isScopeAssignedToAgent(agentID, searchScopeID, contextUser) {
105
+ const rv = new RunView();
106
+ const result = await rv.RunView({
107
+ EntityName: 'MJ: AI Agent Search Scopes',
108
+ ExtraFilter: `AgentID='${agentID}' AND SearchScopeID='${searchScopeID}' AND Status='Active'`,
109
+ Fields: ['ID'],
110
+ ResultType: 'simple',
111
+ // Same fail-closed semantics as loadPermissionsForScope: a stale
112
+ // cache must never let an Assigned-mode agent reach a scope it
113
+ // shouldn't.
114
+ BypassCache: true,
115
+ }, contextUser);
116
+ if (!result.Success) {
117
+ throw new Error(`SearchScopePermissionResolver: failed to load AIAgentSearchScope rows for agent ${agentID}, scope ${searchScopeID}: ${result.ErrorMessage}`);
118
+ }
119
+ return (result.Results?.length ?? 0) > 0;
120
+ }
121
+ /**
122
+ * Loads all SearchScopePermission rows scoped to the given SearchScope.
123
+ * Caller-supplied ContextUser ensures the RunView runs under the same
124
+ * identity the rest of the request is using.
125
+ */
126
+ async loadPermissionsForScope(searchScopeID, contextUser) {
127
+ const rv = new RunView();
128
+ const result = await rv.RunView({
129
+ EntityName: 'MJ: Search Scope Permissions',
130
+ ExtraFilter: `SearchScopeID='${searchScopeID}'`,
131
+ ResultType: 'simple',
132
+ // Permission decisions must NEVER read stale cache — a freshly-revoked grant
133
+ // or a freshly-granted permission must take effect immediately. Skipping
134
+ // the server-side RunView cache adds one DB query per resolver call but
135
+ // eliminates the security-correctness risk of a delayed permission update.
136
+ BypassCache: true,
137
+ }, contextUser);
138
+ if (!result.Success) {
139
+ // Fail closed: an unreadable permissions table cannot be treated
140
+ // as "no permissions exist".
141
+ throw new Error(`SearchScopePermissionResolver: failed to load permissions for scope ${searchScopeID}: ${result.ErrorMessage}`);
142
+ }
143
+ return result.Results ?? [];
144
+ }
145
+ /** Bundles the result fields together with a closure-bound toSqlPredicate. */
146
+ buildResult(allowed, level, source, reason) {
147
+ return {
148
+ Allowed: allowed,
149
+ Level: level,
150
+ Source: source,
151
+ Reason: reason,
152
+ toSqlPredicate: () => allowed ? '1=1' : '1=0',
153
+ };
154
+ }
155
+ }
156
+ // Hint to consumers: keep one resolver per request unless you genuinely
157
+ // need a different policy. The class is stateless; instantiation is cheap.
158
+ export const DefaultSearchScopePermissionResolver = new SearchScopePermissionResolver();
159
+ //# sourceMappingURL=SearchScopePermissionResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchScopePermissionResolver.js","sourceRoot":"","sources":["../../src/permissions/SearchScopePermissionResolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @fileoverview Azure AI Search provider for the SearchEngine (P5.3).
3
+ *
4
+ * Calls Azure AI Search's `docs/search` REST endpoint directly via fetch.
5
+ * Azure AI Search's filter syntax is OData ($filter), passed through as a
6
+ * pre-rendered string from the scope's `MetadataFilter`. Permission /
7
+ * tenant push-down lives entirely in that filter — no post-fusion filtering.
8
+ *
9
+ * **Connection options** (from `SearchProviderConfig.ProviderConfig`):
10
+ * - `serviceName` — Azure AI Search service short name (combined with
11
+ * `.search.windows.net`); OR
12
+ * - `endpoint` — full HTTPS endpoint URL (e.g. `https://my.search.windows.net`)
13
+ * - `apiKey` — query or admin key (sent via `api-key` header)
14
+ * - `apiVersion` — API version, default `2024-07-01`
15
+ * - `defaultIndex` — fallback index when no scope ExternalIndexes
16
+ * - `defaultSearchFields` — comma-separated `searchFields` (when omitted,
17
+ * Azure searches all searchable fields)
18
+ *
19
+ * @module @memberjunction/search-engine
20
+ */
21
+ import { UserInfo } from '@memberjunction/core';
22
+ import { BaseSearchProvider, SearchProviderConfig } from '../generic/ISearchProvider.js';
23
+ import { SearchSource, SearchFilters, SearchResultItem, ScopeConstraints } from '../generic/search.types.js';
24
+ export declare class AzureAISearchProvider extends BaseSearchProvider {
25
+ readonly SourceType: SearchSource;
26
+ private parsedConfig;
27
+ private available;
28
+ Initialize(config: SearchProviderConfig, contextUser: UserInfo): Promise<void>;
29
+ CheckAvailability(_contextUser: UserInfo): Promise<void>;
30
+ IsAvailable(): boolean;
31
+ Search(query: string, topK: number, _filters: SearchFilters | undefined, _contextUser: UserInfo, scopeConstraints?: ScopeConstraints): Promise<SearchResultItem[]>;
32
+ /** Resolve the Azure search service base URL from the parsed config. */
33
+ private resolveEndpoint;
34
+ }
35
+ /** Tree-shake prevention helper. */
36
+ export declare function LoadAzureAISearchProvider(): void;
37
+ //# sourceMappingURL=AzureAISearchProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AzureAISearchProvider.d.ts","sourceRoot":"","sources":["../../src/providers/AzureAISearchProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACtF,OAAO,EACH,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,yBAAyB,CAAC;AAoBjC,qBACa,qBAAsB,SAAQ,kBAAkB;IACzD,SAAgB,UAAU,EAAE,YAAY,CAAc;IAEtD,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,SAAS,CAAS;IAEJ,UAAU,CAAC,MAAM,EAAE,oBAAoB,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAS9E,iBAAiB,CAAC,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB9D,WAAW,IAAI,OAAO;IAIzB,MAAM,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,aAAa,GAAG,SAAS,EACnC,YAAY,EAAE,QAAQ,EACtB,gBAAgB,CAAC,EAAE,gBAAgB,GACpC,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAsF9B,wEAAwE;IACxE,OAAO,CAAC,eAAe;CAS1B;AAED,oCAAoC;AACpC,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD"}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * @fileoverview Azure AI Search provider for the SearchEngine (P5.3).
3
+ *
4
+ * Calls Azure AI Search's `docs/search` REST endpoint directly via fetch.
5
+ * Azure AI Search's filter syntax is OData ($filter), passed through as a
6
+ * pre-rendered string from the scope's `MetadataFilter`. Permission /
7
+ * tenant push-down lives entirely in that filter — no post-fusion filtering.
8
+ *
9
+ * **Connection options** (from `SearchProviderConfig.ProviderConfig`):
10
+ * - `serviceName` — Azure AI Search service short name (combined with
11
+ * `.search.windows.net`); OR
12
+ * - `endpoint` — full HTTPS endpoint URL (e.g. `https://my.search.windows.net`)
13
+ * - `apiKey` — query or admin key (sent via `api-key` header)
14
+ * - `apiVersion` — API version, default `2024-07-01`
15
+ * - `defaultIndex` — fallback index when no scope ExternalIndexes
16
+ * - `defaultSearchFields` — comma-separated `searchFields` (when omitted,
17
+ * Azure searches all searchable fields)
18
+ *
19
+ * @module @memberjunction/search-engine
20
+ */
21
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
22
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
23
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
24
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
25
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
26
+ };
27
+ import { LogError } from '@memberjunction/core';
28
+ import { RegisterClass } from '@memberjunction/global';
29
+ import { BaseSearchProvider } from '../generic/ISearchProvider.js';
30
+ let AzureAISearchProvider = class AzureAISearchProvider extends BaseSearchProvider {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.SourceType = 'fulltext';
34
+ this.parsedConfig = null;
35
+ this.available = false;
36
+ }
37
+ async Initialize(config, contextUser) {
38
+ await super.Initialize(config, contextUser);
39
+ this.parsedConfig = config.ProviderConfig ?? null;
40
+ if (!this.parsedConfig?.apiKey || (!this.parsedConfig.endpoint && !this.parsedConfig.serviceName)) {
41
+ LogError('AzureAISearchProvider: ProviderConfig must include apiKey and either endpoint or serviceName.');
42
+ return;
43
+ }
44
+ }
45
+ async CheckAvailability(_contextUser) {
46
+ if (!this.parsedConfig?.apiKey) {
47
+ this.available = false;
48
+ return;
49
+ }
50
+ const endpoint = this.resolveEndpoint();
51
+ if (!endpoint) {
52
+ this.available = false;
53
+ return;
54
+ }
55
+ try {
56
+ // Probe the service-level endpoint with the api-version param. A 200/401 both
57
+ // confirm the host is reachable; we treat any HTTP response as "service is up
58
+ // and credential is at least the right shape" — Azure returns 401 if the key
59
+ // is wrong, which IS reachable.
60
+ const url = `${endpoint}/?api-version=${encodeURIComponent(this.parsedConfig.apiVersion ?? '2024-07-01')}`;
61
+ const response = await fetch(url, { method: 'GET', headers: { 'api-key': this.parsedConfig.apiKey } });
62
+ this.available = response.ok || response.status === 401;
63
+ if (!this.available) {
64
+ LogError(`AzureAISearchProvider: probe returned ${response.status}`);
65
+ }
66
+ }
67
+ catch (err) {
68
+ const msg = err instanceof Error ? err.message : String(err);
69
+ LogError(`AzureAISearchProvider: probe failed — ${msg}`);
70
+ this.available = false;
71
+ }
72
+ }
73
+ IsAvailable() {
74
+ return this.available;
75
+ }
76
+ async Search(query, topK, _filters, _contextUser, scopeConstraints) {
77
+ if (!this.parsedConfig?.apiKey)
78
+ return [];
79
+ const endpoint = this.resolveEndpoint();
80
+ if (!endpoint)
81
+ return [];
82
+ const effectiveQuery = scopeConstraints?.QueryTransforms?.[this.SourceType] ?? query;
83
+ const apiVersion = this.parsedConfig.apiVersion ?? '2024-07-01';
84
+ const scopedAzureRows = scopeConstraints?.ExternalIndexes
85
+ ?.filter(r => r.IndexType === 'AzureAISearch' && r.ExternalIndexName) ?? [];
86
+ const targets = scopedAzureRows.length > 0
87
+ ? scopedAzureRows
88
+ : (this.parsedConfig.defaultIndex
89
+ ? [{
90
+ IndexType: 'AzureAISearch',
91
+ ExternalIndexName: this.parsedConfig.defaultIndex,
92
+ MetadataFilter: undefined,
93
+ }]
94
+ : []);
95
+ if (targets.length === 0)
96
+ return [];
97
+ const requests = targets.map(async (idx) => {
98
+ const url = `${endpoint}/indexes/${encodeURIComponent(idx.ExternalIndexName)}/docs/search?api-version=${encodeURIComponent(apiVersion)}`;
99
+ const body = {
100
+ search: effectiveQuery,
101
+ top: topK,
102
+ };
103
+ if (this.parsedConfig?.defaultSearchFields) {
104
+ body.searchFields = this.parsedConfig.defaultSearchFields;
105
+ }
106
+ if (idx.MetadataFilter && typeof idx.MetadataFilter === 'string') {
107
+ body.filter = idx.MetadataFilter;
108
+ }
109
+ try {
110
+ const response = await fetch(url, {
111
+ method: 'POST',
112
+ headers: { 'api-key': this.parsedConfig.apiKey, 'Content-Type': 'application/json' },
113
+ body: JSON.stringify(body),
114
+ });
115
+ if (!response.ok) {
116
+ const text = await response.text().catch(() => '');
117
+ LogError(`AzureAISearchProvider: ${idx.ExternalIndexName} returned ${response.status}: ${text || response.statusText}`);
118
+ return { index: idx.ExternalIndexName, hits: [] };
119
+ }
120
+ const json = await response.json();
121
+ return { index: idx.ExternalIndexName, hits: json.value ?? [] };
122
+ }
123
+ catch (err) {
124
+ const msg = err instanceof Error ? err.message : String(err);
125
+ LogError(`AzureAISearchProvider: ${idx.ExternalIndexName} fetch failed — ${msg}`);
126
+ return { index: idx.ExternalIndexName, hits: [] };
127
+ }
128
+ });
129
+ const results = await Promise.all(requests);
130
+ const merged = results.flatMap(r => r.hits.map(h => ({ index: r.index, hit: h })));
131
+ if (merged.length === 0)
132
+ return [];
133
+ const topScore = Math.max(1, ...merged.map(m => m.hit['@search.score']));
134
+ return merged
135
+ .sort((a, b) => b.hit['@search.score'] - a.hit['@search.score'])
136
+ .slice(0, topK)
137
+ .map((m) => {
138
+ const id = String(m.hit['id'] ?? m.hit['Id'] ?? m.hit['ID'] ?? '');
139
+ const title = String(m.hit['title'] ?? m.hit['name'] ?? m.hit['Name'] ?? id);
140
+ const snippet = String(m.hit['content'] ?? m.hit['description'] ?? '');
141
+ const normalized = m.hit['@search.score'] / topScore;
142
+ return {
143
+ ID: `azs-${m.index}-${id}`,
144
+ EntityName: m.index,
145
+ RecordID: id,
146
+ SourceType: this.SourceType,
147
+ ResultType: 'entity-record',
148
+ Title: title.slice(0, 200),
149
+ Snippet: snippet.slice(0, 500),
150
+ Score: normalized,
151
+ ScoreBreakdown: { FullText: normalized },
152
+ Tags: [],
153
+ MatchedAt: new Date(),
154
+ EntityIcon: 'fa-solid fa-cloud',
155
+ RecordName: title.slice(0, 200),
156
+ RawMetadata: JSON.stringify({ index: m.index, id, _rawScore: m.hit['@search.score'] }),
157
+ };
158
+ });
159
+ }
160
+ /** Resolve the Azure search service base URL from the parsed config. */
161
+ resolveEndpoint() {
162
+ if (this.parsedConfig?.endpoint) {
163
+ return this.parsedConfig.endpoint.replace(/\/$/, '');
164
+ }
165
+ if (this.parsedConfig?.serviceName) {
166
+ return `https://${this.parsedConfig.serviceName}.search.windows.net`;
167
+ }
168
+ return null;
169
+ }
170
+ };
171
+ AzureAISearchProvider = __decorate([
172
+ RegisterClass(BaseSearchProvider, 'AzureAISearchProvider')
173
+ ], AzureAISearchProvider);
174
+ export { AzureAISearchProvider };
175
+ /** Tree-shake prevention helper. */
176
+ export function LoadAzureAISearchProvider() {
177
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
178
+ const _ref = AzureAISearchProvider;
179
+ }
180
+ //# sourceMappingURL=AzureAISearchProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AzureAISearchProvider.js","sourceRoot":"","sources":["../../src/providers/AzureAISearchProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @fileoverview Elasticsearch search provider for the SearchEngine (P5.1).
3
+ *
4
+ * Translates a query plus a SearchScope's `ExternalIndexes` constraint into an
5
+ * Elasticsearch search request. Each `MJ: Search Scope External Indexes` row
6
+ * with `IndexType='Elasticsearch'` and a non-empty `ExternalIndexName` becomes
7
+ * a target index in the request. The scope's rendered `MetadataFilter` (a JSON
8
+ * object the scope author authored as ES filter DSL) is composed into the
9
+ * top-level `bool.filter` so permission predicates and tenant-scoping push down
10
+ * to the engine — no post-fusion filtering.
11
+ *
12
+ * **Connection.** Reads from `SearchProviderConfig.ProviderConfig`:
13
+ * - `node` — Elasticsearch node URL (e.g. `https://es.example.internal:9200`)
14
+ * - `apiKey` — base64-encoded API key (preferred)
15
+ * - `username` + `password` — basic auth fallback
16
+ * - `cloudId` — Elastic Cloud deployment ID, when used in place of `node`
17
+ * - `defaultField` — field to match against when the scope/provider doesn't
18
+ * specify one (default: `content`)
19
+ *
20
+ * **Optional peer dep.** `@elastic/elasticsearch` is in `optionalDependencies`
21
+ * and loaded via dynamic import per CLAUDE.md rule #8 case 2 — consumers who
22
+ * don't use Elasticsearch don't pay the bundle cost.
23
+ *
24
+ * @module @memberjunction/search-engine
25
+ */
26
+ import { UserInfo } from '@memberjunction/core';
27
+ import { BaseSearchProvider, SearchProviderConfig } from '../generic/ISearchProvider.js';
28
+ import { SearchSource, SearchFilters, SearchResultItem, ScopeConstraints } from '../generic/search.types.js';
29
+ export declare class ElasticsearchSearchProvider extends BaseSearchProvider {
30
+ readonly SourceType: SearchSource;
31
+ private client;
32
+ private available;
33
+ private parsedConfig;
34
+ Initialize(config: SearchProviderConfig, contextUser: UserInfo): Promise<void>;
35
+ CheckAvailability(_contextUser: UserInfo): Promise<void>;
36
+ IsAvailable(): boolean;
37
+ Search(query: string, topK: number, _filters: SearchFilters | undefined, _contextUser: UserInfo, scopeConstraints?: ScopeConstraints): Promise<SearchResultItem[]>;
38
+ }
39
+ /** Tree-shake prevention helper. */
40
+ export declare function LoadElasticsearchSearchProvider(): void;
41
+ /** Test-only: clear the memoized SDK loader so each test installs its own mock. */
42
+ export declare function __resetElasticsearchClientLoaderForTests(): void;
43
+ //# sourceMappingURL=ElasticsearchSearchProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElasticsearchSearchProvider.d.ts","sourceRoot":"","sources":["../../src/providers/ElasticsearchSearchProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAuB,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACtF,OAAO,EACH,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,yBAAyB,CAAC;AA8DjC,qBACa,2BAA4B,SAAQ,kBAAkB;IAC/D,SAAgB,UAAU,EAAE,YAAY,CAAc;IAEtD,OAAO,CAAC,MAAM,CAAwC;IACtD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAA4C;IAE1C,UAAU,CAAC,MAAM,EAAE,oBAAoB,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B9E,iBAAiB,CAAC,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAe9D,WAAW,IAAI,OAAO;IAIzB,MAAM,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,aAAa,GAAG,SAAS,EACnC,YAAY,EAAE,QAAQ,EACtB,gBAAgB,CAAC,EAAE,gBAAgB,GACpC,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAiFjC;AAED,oCAAoC;AACpC,wBAAgB,+BAA+B,IAAI,IAAI,CAGtD;AAED,mFAAmF;AACnF,wBAAgB,wCAAwC,IAAI,IAAI,CAE/D"}