@open-mercato/shared 0.4.2-canary-c02407ff85
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/build.mjs +101 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/api/crud.js +47 -0
- package/dist/lib/api/crud.js.map +7 -0
- package/dist/lib/api/scoped.js +140 -0
- package/dist/lib/api/scoped.js.map +7 -0
- package/dist/lib/auth/jwt.js +34 -0
- package/dist/lib/auth/jwt.js.map +7 -0
- package/dist/lib/auth/server.js +157 -0
- package/dist/lib/auth/server.js.map +7 -0
- package/dist/lib/boolean.js +22 -0
- package/dist/lib/boolean.js.map +7 -0
- package/dist/lib/bootstrap/appResolver.js +43 -0
- package/dist/lib/bootstrap/appResolver.js.map +7 -0
- package/dist/lib/bootstrap/dynamicLoader.js +108 -0
- package/dist/lib/bootstrap/dynamicLoader.js.map +7 -0
- package/dist/lib/bootstrap/factory.js +59 -0
- package/dist/lib/bootstrap/factory.js.map +7 -0
- package/dist/lib/bootstrap/index.js +11 -0
- package/dist/lib/bootstrap/index.js.map +7 -0
- package/dist/lib/bootstrap/types.js +1 -0
- package/dist/lib/bootstrap/types.js.map +7 -0
- package/dist/lib/cache/segments.js +36 -0
- package/dist/lib/cache/segments.js.map +7 -0
- package/dist/lib/cli/progress.js +46 -0
- package/dist/lib/cli/progress.js.map +7 -0
- package/dist/lib/commands/command-bus.js +285 -0
- package/dist/lib/commands/command-bus.js.map +7 -0
- package/dist/lib/commands/customFieldSnapshots.js +66 -0
- package/dist/lib/commands/customFieldSnapshots.js.map +7 -0
- package/dist/lib/commands/helpers.js +98 -0
- package/dist/lib/commands/helpers.js.map +7 -0
- package/dist/lib/commands/index.js +8 -0
- package/dist/lib/commands/index.js.map +7 -0
- package/dist/lib/commands/operationMetadata.js +32 -0
- package/dist/lib/commands/operationMetadata.js.map +7 -0
- package/dist/lib/commands/registry.js +43 -0
- package/dist/lib/commands/registry.js.map +7 -0
- package/dist/lib/commands/scope.js +44 -0
- package/dist/lib/commands/scope.js.map +7 -0
- package/dist/lib/commands/types.js +8 -0
- package/dist/lib/commands/types.js.map +7 -0
- package/dist/lib/crud/cache-stats.js +98 -0
- package/dist/lib/crud/cache-stats.js.map +7 -0
- package/dist/lib/crud/cache.js +175 -0
- package/dist/lib/crud/cache.js.map +7 -0
- package/dist/lib/crud/custom-fields-client.js +52 -0
- package/dist/lib/crud/custom-fields-client.js.map +7 -0
- package/dist/lib/crud/custom-fields.js +467 -0
- package/dist/lib/crud/custom-fields.js.map +7 -0
- package/dist/lib/crud/errors.js +24 -0
- package/dist/lib/crud/errors.js.map +7 -0
- package/dist/lib/crud/exporters.js +154 -0
- package/dist/lib/crud/exporters.js.map +7 -0
- package/dist/lib/crud/factory.js +1311 -0
- package/dist/lib/crud/factory.js.map +7 -0
- package/dist/lib/crud/types.js +1 -0
- package/dist/lib/crud/types.js.map +7 -0
- package/dist/lib/custom-fields/normalize.js +36 -0
- package/dist/lib/custom-fields/normalize.js.map +7 -0
- package/dist/lib/data/engine.js +396 -0
- package/dist/lib/data/engine.js.map +7 -0
- package/dist/lib/db/escapeLikePattern.js +5 -0
- package/dist/lib/db/escapeLikePattern.js.map +7 -0
- package/dist/lib/db/mikro.js +82 -0
- package/dist/lib/db/mikro.js.map +7 -0
- package/dist/lib/di/container.js +94 -0
- package/dist/lib/di/container.js.map +7 -0
- package/dist/lib/email/send.js +12 -0
- package/dist/lib/email/send.js.map +7 -0
- package/dist/lib/encryption/aes.js +58 -0
- package/dist/lib/encryption/aes.js.map +7 -0
- package/dist/lib/encryption/customFieldValues.js +49 -0
- package/dist/lib/encryption/customFieldValues.js.map +7 -0
- package/dist/lib/encryption/entityFields.js +26 -0
- package/dist/lib/encryption/entityFields.js.map +7 -0
- package/dist/lib/encryption/entityIds.js +80 -0
- package/dist/lib/encryption/entityIds.js.map +7 -0
- package/dist/lib/encryption/find.js +45 -0
- package/dist/lib/encryption/find.js.map +7 -0
- package/dist/lib/encryption/indexDoc.js +69 -0
- package/dist/lib/encryption/indexDoc.js.map +7 -0
- package/dist/lib/encryption/kms.js +282 -0
- package/dist/lib/encryption/kms.js.map +7 -0
- package/dist/lib/encryption/subscriber.js +330 -0
- package/dist/lib/encryption/subscriber.js.map +7 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js +252 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js.map +7 -0
- package/dist/lib/encryption/toggles.js +18 -0
- package/dist/lib/encryption/toggles.js.map +7 -0
- package/dist/lib/entities/naming.js +9 -0
- package/dist/lib/entities/naming.js.map +7 -0
- package/dist/lib/entities/system-entities.js +43 -0
- package/dist/lib/entities/system-entities.js.map +7 -0
- package/dist/lib/frontend/organizationEvents.js +41 -0
- package/dist/lib/frontend/organizationEvents.js.map +7 -0
- package/dist/lib/frontend/useOrganizationScope.js +32 -0
- package/dist/lib/frontend/useOrganizationScope.js.map +7 -0
- package/dist/lib/hotkeys/index.js +128 -0
- package/dist/lib/hotkeys/index.js.map +7 -0
- package/dist/lib/i18n/app-dictionaries.js +17 -0
- package/dist/lib/i18n/app-dictionaries.js.map +7 -0
- package/dist/lib/i18n/config.js +7 -0
- package/dist/lib/i18n/config.js.map +7 -0
- package/dist/lib/i18n/context.js +50 -0
- package/dist/lib/i18n/context.js.map +7 -0
- package/dist/lib/i18n/server.js +68 -0
- package/dist/lib/i18n/server.js.map +7 -0
- package/dist/lib/i18n/translate.js +45 -0
- package/dist/lib/i18n/translate.js.map +7 -0
- package/dist/lib/indexers/error-log.js +82 -0
- package/dist/lib/indexers/error-log.js.map +7 -0
- package/dist/lib/indexers/status-log.js +80 -0
- package/dist/lib/indexers/status-log.js.map +7 -0
- package/dist/lib/lib/auth/jwt.js +34 -0
- package/dist/lib/lib/auth/jwt.js.map +7 -0
- package/dist/lib/lib/auth/server.js +77 -0
- package/dist/lib/lib/auth/server.js.map +7 -0
- package/dist/lib/lib/email/send.js +12 -0
- package/dist/lib/lib/email/send.js.map +7 -0
- package/dist/lib/lib/i18n/config.js +7 -0
- package/dist/lib/lib/i18n/config.js.map +7 -0
- package/dist/lib/lib/i18n/context.js +31 -0
- package/dist/lib/lib/i18n/context.js.map +7 -0
- package/dist/lib/lib/utils.js +9 -0
- package/dist/lib/lib/utils.js.map +7 -0
- package/dist/lib/location/countries.js +68 -0
- package/dist/lib/location/countries.js.map +7 -0
- package/dist/lib/modules/index.js +6 -0
- package/dist/lib/modules/index.js.map +7 -0
- package/dist/lib/modules/registry.js +18 -0
- package/dist/lib/modules/registry.js.map +7 -0
- package/dist/lib/openapi/crud.js +137 -0
- package/dist/lib/openapi/crud.js.map +7 -0
- package/dist/lib/openapi/generator.js +1131 -0
- package/dist/lib/openapi/generator.js.map +7 -0
- package/dist/lib/openapi/index.js +10 -0
- package/dist/lib/openapi/index.js.map +7 -0
- package/dist/lib/openapi/sanitize.js +110 -0
- package/dist/lib/openapi/sanitize.js.map +7 -0
- package/dist/lib/openapi/types.js +1 -0
- package/dist/lib/openapi/types.js.map +7 -0
- package/dist/lib/profiler/index.js +258 -0
- package/dist/lib/profiler/index.js.map +7 -0
- package/dist/lib/query/engine.js +729 -0
- package/dist/lib/query/engine.js.map +7 -0
- package/dist/lib/query/join-utils.js +195 -0
- package/dist/lib/query/join-utils.js.map +7 -0
- package/dist/lib/query/types.js +9 -0
- package/dist/lib/query/types.js.map +7 -0
- package/dist/lib/search/config.js +32 -0
- package/dist/lib/search/config.js.map +7 -0
- package/dist/lib/search/tokenize.js +34 -0
- package/dist/lib/search/tokenize.js.map +7 -0
- package/dist/lib/slugify.js +24 -0
- package/dist/lib/slugify.js.map +7 -0
- package/dist/lib/testing/bootstrap.js +51 -0
- package/dist/lib/testing/bootstrap.js.map +7 -0
- package/dist/lib/testing/index.js +17 -0
- package/dist/lib/testing/index.js.map +7 -0
- package/dist/lib/testing/renderWithProviders.js +15 -0
- package/dist/lib/testing/renderWithProviders.js.map +7 -0
- package/dist/lib/url.js +12 -0
- package/dist/lib/url.js.map +7 -0
- package/dist/lib/utils.js +13 -0
- package/dist/lib/utils.js.map +7 -0
- package/dist/lib/version.js +7 -0
- package/dist/lib/version.js.map +7 -0
- package/dist/modules/dashboard/widgets.js +1 -0
- package/dist/modules/dashboard/widgets.js.map +7 -0
- package/dist/modules/dsl.js +30 -0
- package/dist/modules/dsl.js.map +7 -0
- package/dist/modules/entities/kinds.js +22 -0
- package/dist/modules/entities/kinds.js.map +7 -0
- package/dist/modules/entities/options.js +26 -0
- package/dist/modules/entities/options.js.map +7 -0
- package/dist/modules/entities/validation.js +102 -0
- package/dist/modules/entities/validation.js.map +7 -0
- package/dist/modules/entities/validators.js +88 -0
- package/dist/modules/entities/validators.js.map +7 -0
- package/dist/modules/entities.js +1 -0
- package/dist/modules/entities.js.map +7 -0
- package/dist/modules/navigation/sidebarPreferences.js +50 -0
- package/dist/modules/navigation/sidebarPreferences.js.map +7 -0
- package/dist/modules/perspectives/types.js +1 -0
- package/dist/modules/perspectives/types.js.map +7 -0
- package/dist/modules/registry.js +96 -0
- package/dist/modules/registry.js.map +7 -0
- package/dist/modules/search.js +15 -0
- package/dist/modules/search.js.map +7 -0
- package/dist/modules/vector.js +1 -0
- package/dist/modules/vector.js.map +7 -0
- package/dist/modules/widgets/injection-loader.js +180 -0
- package/dist/modules/widgets/injection-loader.js.map +7 -0
- package/dist/modules/widgets/injection.js +1 -0
- package/dist/modules/widgets/injection.js.map +7 -0
- package/dist/security/features.js +23 -0
- package/dist/security/features.js.map +7 -0
- package/dist/types/pg.d.js +1 -0
- package/dist/types/pg.d.js.map +7 -0
- package/dist/types/react-email.d.js +1 -0
- package/dist/types/react-email.d.js.map +7 -0
- package/dist/types/resend.d.js +1 -0
- package/dist/types/resend.d.js.map +7 -0
- package/jest.config.cjs +22 -0
- package/package.json +88 -0
- package/src/index.ts +0 -0
- package/src/lib/api/__tests__/scoped.test.ts +38 -0
- package/src/lib/api/crud.ts +59 -0
- package/src/lib/api/scoped.ts +239 -0
- package/src/lib/auth/jwt.ts +39 -0
- package/src/lib/auth/server.ts +199 -0
- package/src/lib/boolean.ts +17 -0
- package/src/lib/bootstrap/appResolver.ts +85 -0
- package/src/lib/bootstrap/dynamicLoader.ts +177 -0
- package/src/lib/bootstrap/factory.ts +108 -0
- package/src/lib/bootstrap/index.ts +23 -0
- package/src/lib/bootstrap/types.ts +31 -0
- package/src/lib/cache/segments.ts +56 -0
- package/src/lib/cli/progress.ts +55 -0
- package/src/lib/commands/__tests__/command-bus.test.ts +84 -0
- package/src/lib/commands/__tests__/helpers.test.ts +42 -0
- package/src/lib/commands/command-bus.ts +349 -0
- package/src/lib/commands/customFieldSnapshots.ts +86 -0
- package/src/lib/commands/helpers.ts +143 -0
- package/src/lib/commands/index.ts +4 -0
- package/src/lib/commands/operationMetadata.ts +40 -0
- package/src/lib/commands/registry.ts +46 -0
- package/src/lib/commands/scope.ts +59 -0
- package/src/lib/commands/types.ts +63 -0
- package/src/lib/crud/__tests__/crud-factory.test.ts +333 -0
- package/src/lib/crud/__tests__/custom-fields.test.ts +150 -0
- package/src/lib/crud/cache-stats.ts +127 -0
- package/src/lib/crud/cache.ts +205 -0
- package/src/lib/crud/custom-fields-client.ts +54 -0
- package/src/lib/crud/custom-fields.ts +607 -0
- package/src/lib/crud/errors.ts +23 -0
- package/src/lib/crud/exporters.ts +188 -0
- package/src/lib/crud/factory.ts +1622 -0
- package/src/lib/crud/types.ts +29 -0
- package/src/lib/custom-fields/normalize.ts +45 -0
- package/src/lib/data/engine.ts +562 -0
- package/src/lib/db/escapeLikePattern.ts +2 -0
- package/src/lib/db/mikro.ts +100 -0
- package/src/lib/di/container.ts +105 -0
- package/src/lib/email/send.ts +18 -0
- package/src/lib/encryption/__tests__/customFieldValues.test.ts +63 -0
- package/src/lib/encryption/__tests__/indexDoc.test.ts +115 -0
- package/src/lib/encryption/aes.ts +64 -0
- package/src/lib/encryption/customFieldValues.ts +67 -0
- package/src/lib/encryption/entityFields.ts +39 -0
- package/src/lib/encryption/entityIds.ts +107 -0
- package/src/lib/encryption/find.ts +81 -0
- package/src/lib/encryption/indexDoc.ts +104 -0
- package/src/lib/encryption/kms.ts +337 -0
- package/src/lib/encryption/subscriber.ts +416 -0
- package/src/lib/encryption/tenantDataEncryptionService.ts +313 -0
- package/src/lib/encryption/toggles.ts +15 -0
- package/src/lib/entities/naming.ts +6 -0
- package/src/lib/entities/system-entities.ts +43 -0
- package/src/lib/frontend/organizationEvents.ts +55 -0
- package/src/lib/frontend/useOrganizationScope.ts +30 -0
- package/src/lib/hotkeys/index.ts +168 -0
- package/src/lib/i18n/app-dictionaries.ts +18 -0
- package/src/lib/i18n/config.ts +4 -0
- package/src/lib/i18n/context.tsx +66 -0
- package/src/lib/i18n/server.ts +74 -0
- package/src/lib/i18n/translate.ts +54 -0
- package/src/lib/indexers/error-log.ts +106 -0
- package/src/lib/indexers/status-log.ts +119 -0
- package/src/lib/lib/auth/jwt.ts +39 -0
- package/src/lib/lib/auth/server.ts +94 -0
- package/src/lib/lib/email/send.ts +18 -0
- package/src/lib/lib/i18n/config.ts +4 -0
- package/src/lib/lib/i18n/context.tsx +38 -0
- package/src/lib/lib/utils.ts +6 -0
- package/src/lib/location/countries.ts +97 -0
- package/src/lib/modules/index.ts +1 -0
- package/src/lib/modules/registry.ts +18 -0
- package/src/lib/openapi/crud.ts +218 -0
- package/src/lib/openapi/generator.ts +1311 -0
- package/src/lib/openapi/index.ts +4 -0
- package/src/lib/openapi/sanitize.ts +137 -0
- package/src/lib/openapi/types.ts +79 -0
- package/src/lib/profiler/index.ts +371 -0
- package/src/lib/query/__tests__/engine.test.ts +274 -0
- package/src/lib/query/engine.ts +837 -0
- package/src/lib/query/join-utils.ts +238 -0
- package/src/lib/query/types.ts +121 -0
- package/src/lib/search/config.ts +49 -0
- package/src/lib/search/tokenize.ts +45 -0
- package/src/lib/slugify.ts +28 -0
- package/src/lib/testing/bootstrap.ts +124 -0
- package/src/lib/testing/index.ts +15 -0
- package/src/lib/testing/renderWithProviders.tsx +31 -0
- package/src/lib/url.ts +12 -0
- package/src/lib/utils.ts +17 -0
- package/src/lib/version.ts +5 -0
- package/src/modules/__tests__/dsl.test.ts +35 -0
- package/src/modules/__tests__/registry.test.ts +300 -0
- package/src/modules/dashboard/widgets.ts +57 -0
- package/src/modules/dsl.ts +32 -0
- package/src/modules/entities/__tests__/validation.test.ts +52 -0
- package/src/modules/entities/kinds.ts +20 -0
- package/src/modules/entities/options.ts +36 -0
- package/src/modules/entities/validation.ts +118 -0
- package/src/modules/entities/validators.ts +93 -0
- package/src/modules/entities.ts +102 -0
- package/src/modules/navigation/sidebarPreferences.ts +62 -0
- package/src/modules/perspectives/types.ts +40 -0
- package/src/modules/registry.ts +249 -0
- package/src/modules/search.ts +325 -0
- package/src/modules/vector.ts +122 -0
- package/src/modules/widgets/__tests__/injection.test.ts +48 -0
- package/src/modules/widgets/injection-loader.ts +235 -0
- package/src/modules/widgets/injection.ts +120 -0
- package/src/security/features.ts +22 -0
- package/src/types/pg.d.ts +2 -0
- package/src/types/react-email.d.ts +2 -0
- package/src/types/resend.d.ts +2 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import type { EntityId } from './entities'
|
|
2
|
+
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Strategy Identifiers
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Built-in strategy identifiers plus extensible string for third-party strategies.
|
|
9
|
+
*/
|
|
10
|
+
export type SearchStrategyId = 'tokens' | 'vector' | 'fulltext' | (string & {})
|
|
11
|
+
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Result Types
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Presenter metadata for displaying search results in UI (Cmd+K, global search).
|
|
18
|
+
*/
|
|
19
|
+
export type SearchResultPresenter = {
|
|
20
|
+
title: string
|
|
21
|
+
subtitle?: string
|
|
22
|
+
icon?: string
|
|
23
|
+
badge?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Deep link rendered next to a search result.
|
|
28
|
+
*/
|
|
29
|
+
export type SearchResultLink = {
|
|
30
|
+
href: string
|
|
31
|
+
label: string
|
|
32
|
+
kind?: 'primary' | 'secondary'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A single search result returned by a strategy.
|
|
37
|
+
*/
|
|
38
|
+
export type SearchResult = {
|
|
39
|
+
/** Entity type identifier, e.g., 'customers:customer_person_profile' */
|
|
40
|
+
entityId: EntityId
|
|
41
|
+
/** Record primary key */
|
|
42
|
+
recordId: string
|
|
43
|
+
/** Relevance score (normalized 0-1 range preferred, but RRF scores may exceed 1) */
|
|
44
|
+
score: number
|
|
45
|
+
/** Which strategy produced this result */
|
|
46
|
+
source: SearchStrategyId
|
|
47
|
+
/** Optional presenter for quick display */
|
|
48
|
+
presenter?: SearchResultPresenter
|
|
49
|
+
/** Primary URL when result is clicked */
|
|
50
|
+
url?: string
|
|
51
|
+
/** Additional action links */
|
|
52
|
+
links?: SearchResultLink[]
|
|
53
|
+
/** Extra metadata from the strategy */
|
|
54
|
+
metadata?: Record<string, unknown>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// =============================================================================
|
|
58
|
+
// Search Options
|
|
59
|
+
// =============================================================================
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Options passed to SearchService.search()
|
|
63
|
+
*/
|
|
64
|
+
export type SearchOptions = {
|
|
65
|
+
/** Tenant isolation - required */
|
|
66
|
+
tenantId: string
|
|
67
|
+
/** Optional organization filter */
|
|
68
|
+
organizationId?: string | null
|
|
69
|
+
/** Filter to specific entity types */
|
|
70
|
+
entityTypes?: EntityId[]
|
|
71
|
+
/** Use only specific strategies (defaults to all available) */
|
|
72
|
+
strategies?: SearchStrategyId[]
|
|
73
|
+
/** Maximum results per strategy before merging */
|
|
74
|
+
limit?: number
|
|
75
|
+
/** Offset for pagination */
|
|
76
|
+
offset?: number
|
|
77
|
+
/** How to combine results: 'or' merges all, 'and' requires match in all strategies */
|
|
78
|
+
combineMode?: 'or' | 'and'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// =============================================================================
|
|
82
|
+
// Indexable Record
|
|
83
|
+
// =============================================================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A record prepared for indexing across all strategies.
|
|
87
|
+
*/
|
|
88
|
+
export type IndexableRecord = {
|
|
89
|
+
/** Entity type identifier */
|
|
90
|
+
entityId: EntityId
|
|
91
|
+
/** Record primary key */
|
|
92
|
+
recordId: string
|
|
93
|
+
/** Tenant for isolation */
|
|
94
|
+
tenantId: string
|
|
95
|
+
/** Optional organization for additional filtering */
|
|
96
|
+
organizationId?: string | null
|
|
97
|
+
/** All fields from the record (strategies will filter based on their needs) */
|
|
98
|
+
fields: Record<string, unknown>
|
|
99
|
+
/** Optional presenter for result display */
|
|
100
|
+
presenter?: SearchResultPresenter
|
|
101
|
+
/** Primary URL for the record */
|
|
102
|
+
url?: string
|
|
103
|
+
/** Additional action links */
|
|
104
|
+
links?: SearchResultLink[]
|
|
105
|
+
/** Text content for embedding (from buildSource, used by vector strategy) */
|
|
106
|
+
text?: string | string[]
|
|
107
|
+
/** Source object for checksum calculation (change detection) */
|
|
108
|
+
checksumSource?: unknown
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// Strategy Interface
|
|
113
|
+
// =============================================================================
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Interface that all search strategies must implement.
|
|
117
|
+
* Following the cache module's strategy pattern.
|
|
118
|
+
*/
|
|
119
|
+
export interface SearchStrategy {
|
|
120
|
+
/** Unique strategy identifier */
|
|
121
|
+
readonly id: SearchStrategyId
|
|
122
|
+
|
|
123
|
+
/** Human-readable name for debugging/logging */
|
|
124
|
+
readonly name: string
|
|
125
|
+
|
|
126
|
+
/** Priority for result merging (higher = more prominent in results) */
|
|
127
|
+
readonly priority: number
|
|
128
|
+
|
|
129
|
+
/** Check if strategy is available and configured */
|
|
130
|
+
isAvailable(): Promise<boolean>
|
|
131
|
+
|
|
132
|
+
/** Initialize strategy resources (lazy, called on first use) */
|
|
133
|
+
ensureReady(): Promise<void>
|
|
134
|
+
|
|
135
|
+
/** Execute a search query */
|
|
136
|
+
search(query: string, options: SearchOptions): Promise<SearchResult[]>
|
|
137
|
+
|
|
138
|
+
/** Index a record */
|
|
139
|
+
index(record: IndexableRecord): Promise<void>
|
|
140
|
+
|
|
141
|
+
/** Delete a record from the index */
|
|
142
|
+
delete(entityId: EntityId, recordId: string, tenantId: string): Promise<void>
|
|
143
|
+
|
|
144
|
+
/** Bulk index multiple records (optional optimization) */
|
|
145
|
+
bulkIndex?(records: IndexableRecord[]): Promise<void>
|
|
146
|
+
|
|
147
|
+
/** Purge all records for an entity type (optional) */
|
|
148
|
+
purge?(entityId: EntityId, tenantId: string): Promise<void>
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// =============================================================================
|
|
152
|
+
// Service Configuration
|
|
153
|
+
// =============================================================================
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Configuration for result merging across strategies.
|
|
157
|
+
*/
|
|
158
|
+
export type ResultMergeConfig = {
|
|
159
|
+
/** How to handle duplicate results: 'highest_score' | 'first' | 'merge_scores' */
|
|
160
|
+
duplicateHandling: 'highest_score' | 'first' | 'merge_scores'
|
|
161
|
+
/** Weight multipliers per strategy (e.g., { meilisearch: 1.2, tokens: 0.8 }) */
|
|
162
|
+
strategyWeights?: Record<SearchStrategyId, number>
|
|
163
|
+
/** Minimum score threshold to include in results */
|
|
164
|
+
minScore?: number
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Callback function to enrich search results with presenter data.
|
|
169
|
+
* Used to load presenter from database when not available from search strategy.
|
|
170
|
+
*/
|
|
171
|
+
export type PresenterEnricherFn = (
|
|
172
|
+
results: SearchResult[],
|
|
173
|
+
tenantId: string,
|
|
174
|
+
organizationId?: string | null,
|
|
175
|
+
) => Promise<SearchResult[]>
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Options for creating a SearchService instance.
|
|
179
|
+
*/
|
|
180
|
+
export type SearchServiceOptions = {
|
|
181
|
+
/** Array of strategy instances */
|
|
182
|
+
strategies?: SearchStrategy[]
|
|
183
|
+
/** Default strategies to use when not specified in search options */
|
|
184
|
+
defaultStrategies?: SearchStrategyId[]
|
|
185
|
+
/** Fallback strategy when others fail */
|
|
186
|
+
fallbackStrategy?: SearchStrategyId
|
|
187
|
+
/** Configuration for merging results from multiple strategies */
|
|
188
|
+
mergeConfig?: ResultMergeConfig
|
|
189
|
+
/** Callback to enrich results with presenter data from database */
|
|
190
|
+
presenterEnricher?: PresenterEnricherFn
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// =============================================================================
|
|
194
|
+
// Module Configuration (for modules defining searchable entities)
|
|
195
|
+
// =============================================================================
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Context passed to buildSource, formatResult, resolveUrl, and resolveLinks.
|
|
199
|
+
*/
|
|
200
|
+
export type SearchBuildContext = {
|
|
201
|
+
/** The record being indexed */
|
|
202
|
+
record: Record<string, unknown>
|
|
203
|
+
/** Custom fields for the record */
|
|
204
|
+
customFields: Record<string, unknown>
|
|
205
|
+
/** Organization ID if applicable */
|
|
206
|
+
organizationId?: string | null
|
|
207
|
+
/** Tenant ID */
|
|
208
|
+
tenantId?: string | null
|
|
209
|
+
/** DI container for resolving dependencies */
|
|
210
|
+
container?: unknown
|
|
211
|
+
/** Query engine for loading related records (optional, used by buildSource for entity hydration) */
|
|
212
|
+
queryEngine?: unknown
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Source data for indexing a record.
|
|
217
|
+
*/
|
|
218
|
+
export type SearchIndexSource = {
|
|
219
|
+
/** Text content for keyword/fuzzy search (single string or array of chunks) */
|
|
220
|
+
text: string | string[]
|
|
221
|
+
/** Optional structured fields for filtering */
|
|
222
|
+
fields?: Record<string, unknown>
|
|
223
|
+
/** Presenter for quick display in search results */
|
|
224
|
+
presenter?: SearchResultPresenter
|
|
225
|
+
/** Deep links for the result */
|
|
226
|
+
links?: SearchResultLink[]
|
|
227
|
+
/** Source object used for checksum calculation (change detection) */
|
|
228
|
+
checksumSource?: unknown
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Policy defining how fields should be handled for search indexing.
|
|
233
|
+
*/
|
|
234
|
+
export type SearchFieldPolicy = {
|
|
235
|
+
/** Fields safe to send to external providers (fuzzy searchable) */
|
|
236
|
+
searchable?: string[]
|
|
237
|
+
/** Fields for hash-based search only (encrypted/sensitive) */
|
|
238
|
+
hashOnly?: string[]
|
|
239
|
+
/** Fields to exclude from all search */
|
|
240
|
+
excluded?: string[]
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Configuration for a single searchable entity within a module.
|
|
245
|
+
*/
|
|
246
|
+
export type SearchEntityConfig = {
|
|
247
|
+
/** Entity identifier, e.g., 'customers:customer_person_profile' */
|
|
248
|
+
entityId: EntityId
|
|
249
|
+
/** Enable/disable search for this entity (default: true) */
|
|
250
|
+
enabled?: boolean
|
|
251
|
+
/** Override strategies for this specific entity */
|
|
252
|
+
strategies?: SearchStrategyId[]
|
|
253
|
+
/** Priority for result ordering (higher = more prominent) */
|
|
254
|
+
priority?: number
|
|
255
|
+
/** Build searchable content from record */
|
|
256
|
+
buildSource?: (ctx: SearchBuildContext) => Promise<SearchIndexSource | null> | SearchIndexSource | null
|
|
257
|
+
/** Format result for display in Cmd+K */
|
|
258
|
+
formatResult?: (ctx: SearchBuildContext) => Promise<SearchResultPresenter | null> | SearchResultPresenter | null
|
|
259
|
+
/** Resolve primary URL when result is clicked */
|
|
260
|
+
resolveUrl?: (ctx: SearchBuildContext) => Promise<string | null> | string | null
|
|
261
|
+
/** Resolve additional action links */
|
|
262
|
+
resolveLinks?: (ctx: SearchBuildContext) => Promise<SearchResultLink[] | null> | SearchResultLink[] | null
|
|
263
|
+
/** Define which fields are searchable vs hash-only */
|
|
264
|
+
fieldPolicy?: SearchFieldPolicy
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Module-level search configuration (defined in search.ts files).
|
|
269
|
+
*/
|
|
270
|
+
export type SearchModuleConfig = {
|
|
271
|
+
/** Default strategies for all entities in this module */
|
|
272
|
+
defaultStrategies?: SearchStrategyId[]
|
|
273
|
+
/** Entity configurations */
|
|
274
|
+
entities: SearchEntityConfig[]
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// =============================================================================
|
|
278
|
+
// Event Payloads (for indexer events)
|
|
279
|
+
// =============================================================================
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Payload for search.index_record events.
|
|
283
|
+
*/
|
|
284
|
+
export type SearchIndexPayload = {
|
|
285
|
+
entityId: EntityId
|
|
286
|
+
recordId: string
|
|
287
|
+
tenantId: string
|
|
288
|
+
organizationId?: string | null
|
|
289
|
+
record: Record<string, unknown>
|
|
290
|
+
customFields?: Record<string, unknown>
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Payload for search.delete_record events.
|
|
295
|
+
*/
|
|
296
|
+
export type SearchDeletePayload = {
|
|
297
|
+
entityId: EntityId
|
|
298
|
+
recordId: string
|
|
299
|
+
tenantId: string
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// =============================================================================
|
|
303
|
+
// Global Registry for Search Module Configs
|
|
304
|
+
// =============================================================================
|
|
305
|
+
|
|
306
|
+
let _searchModuleConfigs: SearchModuleConfig[] | null = null
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Register search module configurations globally.
|
|
310
|
+
* Called during app bootstrap with configs from search.generated.ts.
|
|
311
|
+
*/
|
|
312
|
+
export function registerSearchModuleConfigs(configs: SearchModuleConfig[]): void {
|
|
313
|
+
if (_searchModuleConfigs !== null && process.env.NODE_ENV === 'development') {
|
|
314
|
+
console.debug('[Bootstrap] Search module configs re-registered (this may occur during HMR)')
|
|
315
|
+
}
|
|
316
|
+
_searchModuleConfigs = configs
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Get registered search module configurations.
|
|
321
|
+
* Returns empty array if not registered (search module may not be enabled).
|
|
322
|
+
*/
|
|
323
|
+
export function getSearchModuleConfigs(): SearchModuleConfig[] {
|
|
324
|
+
return _searchModuleConfigs ?? []
|
|
325
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { EntityId } from './entities'
|
|
2
|
+
import type { QueryEngine } from '../lib/query/types'
|
|
3
|
+
|
|
4
|
+
export type VectorDriverId = 'pgvector' | 'qdrant' | 'chromadb'
|
|
5
|
+
|
|
6
|
+
export type VectorLinkDescriptor = {
|
|
7
|
+
href: string
|
|
8
|
+
label?: string
|
|
9
|
+
icon?: string
|
|
10
|
+
kind?: 'primary' | 'secondary'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type VectorResultPresenter = {
|
|
14
|
+
title: string
|
|
15
|
+
subtitle?: string
|
|
16
|
+
icon?: string
|
|
17
|
+
badge?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type VectorIndexSource = {
|
|
21
|
+
/**
|
|
22
|
+
* Text or text chunks passed to the embedding model. Provide multiple chunks for larger payloads.
|
|
23
|
+
*/
|
|
24
|
+
input: string | string[]
|
|
25
|
+
/**
|
|
26
|
+
* Optional metadata persisted alongside the embedding for quick display.
|
|
27
|
+
*/
|
|
28
|
+
presenter?: VectorResultPresenter | null
|
|
29
|
+
/**
|
|
30
|
+
* Optional deep links rendered next to the result.
|
|
31
|
+
*/
|
|
32
|
+
links?: VectorLinkDescriptor[] | null
|
|
33
|
+
/**
|
|
34
|
+
* Additional payload mirrored into the driver metadata column (JSONB).
|
|
35
|
+
*/
|
|
36
|
+
payload?: Record<string, unknown> | null
|
|
37
|
+
/**
|
|
38
|
+
* Source object used when computing a checksum. Defaults to the combination of record and custom fields.
|
|
39
|
+
*/
|
|
40
|
+
checksumSource?: unknown
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type VectorBuildContext = {
|
|
44
|
+
record: Record<string, any>
|
|
45
|
+
customFields: Record<string, any>
|
|
46
|
+
organizationId?: string | null
|
|
47
|
+
tenantId?: string | null
|
|
48
|
+
queryEngine?: QueryEngine
|
|
49
|
+
container?: unknown
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type VectorEntityConfig = {
|
|
53
|
+
entityId: EntityId
|
|
54
|
+
enabled?: boolean
|
|
55
|
+
driverId?: VectorDriverId
|
|
56
|
+
priority?: number
|
|
57
|
+
/**
|
|
58
|
+
* Optional builder that returns the string payload to embed plus supplemental metadata.
|
|
59
|
+
* When omitted the service will stringify the record (including custom fields).
|
|
60
|
+
*/
|
|
61
|
+
buildSource?: (ctx: VectorBuildContext) => Promise<VectorIndexSource | null> | VectorIndexSource | null
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the primary admin URL for this record. When omitted the service expects a link inside `links`.
|
|
64
|
+
*/
|
|
65
|
+
resolveUrl?: (ctx: VectorBuildContext) => Promise<string | null> | string | null
|
|
66
|
+
/**
|
|
67
|
+
* Format the presenter displayed inside global search overlays.
|
|
68
|
+
*/
|
|
69
|
+
formatResult?: (ctx: VectorBuildContext) => Promise<VectorResultPresenter | null> | VectorResultPresenter | null
|
|
70
|
+
/**
|
|
71
|
+
* Provide extra deep links rendered next to the search result.
|
|
72
|
+
*/
|
|
73
|
+
resolveLinks?: (ctx: VectorBuildContext) => Promise<VectorLinkDescriptor[] | null> | VectorLinkDescriptor[] | null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type VectorModuleConfig = {
|
|
77
|
+
defaultDriverId?: VectorDriverId
|
|
78
|
+
entities: VectorEntityConfig[]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type VectorQueryRequest = {
|
|
82
|
+
query: string
|
|
83
|
+
tenantId: string
|
|
84
|
+
organizationId?: string | null
|
|
85
|
+
limit?: number
|
|
86
|
+
driverId?: VectorDriverId
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type VectorSearchHit = {
|
|
90
|
+
entityId: EntityId
|
|
91
|
+
recordId: string
|
|
92
|
+
score: number
|
|
93
|
+
url?: string | null
|
|
94
|
+
presenter?: VectorResultPresenter | null
|
|
95
|
+
links?: VectorLinkDescriptor[] | null
|
|
96
|
+
driverId: VectorDriverId
|
|
97
|
+
metadata?: Record<string, unknown> | null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type VectorIndexEntry = {
|
|
101
|
+
entityId: EntityId
|
|
102
|
+
recordId: string
|
|
103
|
+
driverId: VectorDriverId
|
|
104
|
+
tenantId: string
|
|
105
|
+
organizationId?: string | null
|
|
106
|
+
checksum: string
|
|
107
|
+
url?: string | null
|
|
108
|
+
presenter?: VectorResultPresenter | null
|
|
109
|
+
links?: VectorLinkDescriptor[] | null
|
|
110
|
+
payload?: Record<string, unknown> | null
|
|
111
|
+
metadata?: Record<string, unknown> | null
|
|
112
|
+
resultTitle: string
|
|
113
|
+
resultSubtitle?: string | null
|
|
114
|
+
resultIcon?: string | null
|
|
115
|
+
resultBadge?: string | null
|
|
116
|
+
resultSnapshot?: string | null
|
|
117
|
+
primaryLinkHref?: string | null
|
|
118
|
+
primaryLinkLabel?: string | null
|
|
119
|
+
createdAt: string
|
|
120
|
+
updatedAt: string
|
|
121
|
+
score?: number | null
|
|
122
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from '@jest/globals'
|
|
5
|
+
import type { InjectionWidgetMetadata, InjectionWidgetModule, ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'
|
|
6
|
+
|
|
7
|
+
describe('Widget Injection Types', () => {
|
|
8
|
+
it('should allow defining widget metadata', () => {
|
|
9
|
+
const metadata: InjectionWidgetMetadata = {
|
|
10
|
+
id: 'test.widget',
|
|
11
|
+
title: 'Test Widget',
|
|
12
|
+
description: 'A test widget',
|
|
13
|
+
features: ['test.feature'],
|
|
14
|
+
priority: 100,
|
|
15
|
+
enabled: true,
|
|
16
|
+
}
|
|
17
|
+
expect(metadata.id).toBe('test.widget')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should allow defining injection table', () => {
|
|
21
|
+
const table: ModuleInjectionTable = {
|
|
22
|
+
'crud-form:test': 'test.widget',
|
|
23
|
+
'crud-form:test2': ['widget1', 'widget2'],
|
|
24
|
+
'crud-form:grouped': [{ widgetId: 'widget3', kind: 'group', groupLabel: 'Extra fields' }],
|
|
25
|
+
}
|
|
26
|
+
expect(table['crud-form:test']).toBe('test.widget')
|
|
27
|
+
expect(Array.isArray(table['crud-form:test2'])).toBe(true)
|
|
28
|
+
const grouped = table['crud-form:grouped'] as any[]
|
|
29
|
+
expect(grouped[0].groupLabel).toBe('Extra fields')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should type event handlers correctly', () => {
|
|
33
|
+
const widget: Partial<InjectionWidgetModule<any, any>> = {
|
|
34
|
+
eventHandlers: {
|
|
35
|
+
onLoad: async (context) => {
|
|
36
|
+
expect(context).toBeDefined()
|
|
37
|
+
},
|
|
38
|
+
onBeforeSave: async (data, context) => {
|
|
39
|
+
expect(data).toBeDefined()
|
|
40
|
+
expect(context).toBeDefined()
|
|
41
|
+
return true
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
expect(widget.eventHandlers?.onLoad).toBeDefined()
|
|
46
|
+
expect(widget.eventHandlers?.onBeforeSave).toBeDefined()
|
|
47
|
+
})
|
|
48
|
+
})
|