@open-mercato/shared 0.4.5-develop-0b66ecfdd4 → 0.4.5-develop-2289152f60

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.
@@ -0,0 +1,11 @@
1
+ import { toOptionalString } from "./string/coerce.js";
2
+ function trimToUndefined(value) {
3
+ if (typeof value !== "string") return void 0;
4
+ const trimmed = value.trim();
5
+ return trimmed.length > 0 ? trimmed : void 0;
6
+ }
7
+ export {
8
+ toOptionalString,
9
+ trimToUndefined
10
+ };
11
+ //# sourceMappingURL=string.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/string.ts"],
4
+ "sourcesContent": ["export { toOptionalString } from './string/coerce'\n\nexport function trimToUndefined(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\n"],
5
+ "mappings": "AAAA,SAAS,wBAAwB;AAE1B,SAAS,gBAAgB,OAAoC;AAClE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;",
6
+ "names": []
7
+ }
@@ -1,4 +1,4 @@
1
- const APP_VERSION = "0.4.5-develop-0b66ecfdd4";
1
+ const APP_VERSION = "0.4.5-develop-2289152f60";
2
2
  const appVersion = APP_VERSION;
3
3
  export {
4
4
  APP_VERSION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/version.ts"],
4
- "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.5-develop-0b66ecfdd4'\nexport const appVersion = APP_VERSION\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.5-develop-2289152f60'\nexport const appVersion = APP_VERSION\n"],
5
5
  "mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/modules/search.ts"],
4
- "sourcesContent": ["import type { EntityId } from './entities'\n\n// =============================================================================\n// Strategy Identifiers\n// =============================================================================\n\n/**\n * Built-in strategy identifiers plus extensible string for third-party strategies.\n */\nexport type SearchStrategyId = 'tokens' | 'vector' | 'fulltext' | (string & {})\n\n// =============================================================================\n// Result Types\n// =============================================================================\n\n/**\n * Presenter metadata for displaying search results in UI (Cmd+K, global search).\n */\nexport type SearchResultPresenter = {\n title: string\n subtitle?: string\n icon?: string\n badge?: string\n}\n\n/**\n * Deep link rendered next to a search result.\n */\nexport type SearchResultLink = {\n href: string\n label: string\n kind?: 'primary' | 'secondary'\n}\n\n/**\n * A single search result returned by a strategy.\n */\nexport type SearchResult = {\n /** Entity type identifier, e.g., 'customers:customer_person_profile' */\n entityId: EntityId\n /** Record primary key */\n recordId: string\n /** Relevance score (normalized 0-1 range preferred, but RRF scores may exceed 1) */\n score: number\n /** Which strategy produced this result */\n source: SearchStrategyId\n /** Optional presenter for quick display */\n presenter?: SearchResultPresenter\n /** Primary URL when result is clicked */\n url?: string\n /** Additional action links */\n links?: SearchResultLink[]\n /** Extra metadata from the strategy */\n metadata?: Record<string, unknown>\n}\n\n// =============================================================================\n// Search Options\n// =============================================================================\n\n/**\n * Options passed to SearchService.search()\n */\nexport type SearchOptions = {\n /** Tenant isolation - required */\n tenantId: string\n /** Optional organization filter */\n organizationId?: string | null\n /** Filter to specific entity types */\n entityTypes?: EntityId[]\n /** Use only specific strategies (defaults to all available) */\n strategies?: SearchStrategyId[]\n /** Maximum results per strategy before merging */\n limit?: number\n /** Offset for pagination */\n offset?: number\n /** How to combine results: 'or' merges all, 'and' requires match in all strategies */\n combineMode?: 'or' | 'and'\n}\n\n// =============================================================================\n// Indexable Record\n// =============================================================================\n\n/**\n * A record prepared for indexing across all strategies.\n */\nexport type IndexableRecord = {\n /** Entity type identifier */\n entityId: EntityId\n /** Record primary key */\n recordId: string\n /** Tenant for isolation */\n tenantId: string\n /** Optional organization for additional filtering */\n organizationId?: string | null\n /** All fields from the record (strategies will filter based on their needs) */\n fields: Record<string, unknown>\n /** Optional presenter for result display */\n presenter?: SearchResultPresenter\n /** Primary URL for the record */\n url?: string\n /** Additional action links */\n links?: SearchResultLink[]\n /** Text content for embedding (from buildSource, used by vector strategy) */\n text?: string | string[]\n /** Source object for checksum calculation (change detection) */\n checksumSource?: unknown\n}\n\n// =============================================================================\n// Strategy Interface\n// =============================================================================\n\n/**\n * Interface that all search strategies must implement.\n * Following the cache module's strategy pattern.\n */\nexport interface SearchStrategy {\n /** Unique strategy identifier */\n readonly id: SearchStrategyId\n\n /** Human-readable name for debugging/logging */\n readonly name: string\n\n /** Priority for result merging (higher = more prominent in results) */\n readonly priority: number\n\n /** Check if strategy is available and configured */\n isAvailable(): Promise<boolean>\n\n /** Initialize strategy resources (lazy, called on first use) */\n ensureReady(): Promise<void>\n\n /** Execute a search query */\n search(query: string, options: SearchOptions): Promise<SearchResult[]>\n\n /** Index a record */\n index(record: IndexableRecord): Promise<void>\n\n /** Delete a record from the index */\n delete(entityId: EntityId, recordId: string, tenantId: string): Promise<void>\n\n /** Bulk index multiple records (optional optimization) */\n bulkIndex?(records: IndexableRecord[]): Promise<void>\n\n /** Purge all records for an entity type (optional) */\n purge?(entityId: EntityId, tenantId: string): Promise<void>\n}\n\n// =============================================================================\n// Service Configuration\n// =============================================================================\n\n/**\n * Configuration for result merging across strategies.\n */\nexport type ResultMergeConfig = {\n /** How to handle duplicate results: 'highest_score' | 'first' | 'merge_scores' */\n duplicateHandling: 'highest_score' | 'first' | 'merge_scores'\n /** Weight multipliers per strategy (e.g., { meilisearch: 1.2, tokens: 0.8 }) */\n strategyWeights?: Record<SearchStrategyId, number>\n /** Minimum score threshold to include in results */\n minScore?: number\n}\n\n/**\n * Callback function to enrich search results with presenter data.\n * Used to load presenter from database when not available from search strategy.\n */\nexport type PresenterEnricherFn = (\n results: SearchResult[],\n tenantId: string,\n organizationId?: string | null,\n) => Promise<SearchResult[]>\n\n/**\n * Options for creating a SearchService instance.\n */\nexport type SearchServiceOptions = {\n /** Array of strategy instances */\n strategies?: SearchStrategy[]\n /** Default strategies to use when not specified in search options */\n defaultStrategies?: SearchStrategyId[]\n /** Fallback strategy when others fail */\n fallbackStrategy?: SearchStrategyId\n /** Configuration for merging results from multiple strategies */\n mergeConfig?: ResultMergeConfig\n /** Callback to enrich results with presenter data from database */\n presenterEnricher?: PresenterEnricherFn\n}\n\n// =============================================================================\n// Module Configuration (for modules defining searchable entities)\n// =============================================================================\n\n/**\n * Context passed to buildSource, formatResult, resolveUrl, and resolveLinks.\n */\nexport type SearchBuildContext = {\n /** The record being indexed */\n record: Record<string, unknown>\n /** Custom fields for the record */\n customFields: Record<string, unknown>\n /** Organization ID if applicable */\n organizationId?: string | null\n /** Tenant ID */\n tenantId?: string | null\n /** DI container for resolving dependencies */\n container?: unknown\n /** Query engine for loading related records (optional, used by buildSource for entity hydration) */\n queryEngine?: unknown\n}\n\n/**\n * Source data for indexing a record.\n */\nexport type SearchIndexSource = {\n /** Text content for keyword/fuzzy search (single string or array of chunks) */\n text: string | string[]\n /** Optional structured fields for filtering */\n fields?: Record<string, unknown>\n /** Presenter for quick display in search results */\n presenter?: SearchResultPresenter\n /** Deep links for the result */\n links?: SearchResultLink[]\n /** Source object used for checksum calculation (change detection) */\n checksumSource?: unknown\n}\n\n/**\n * Policy defining how fields should be handled for search indexing.\n */\nexport type SearchFieldPolicy = {\n /** Fields safe to send to external providers (fuzzy searchable) */\n searchable?: string[]\n /** Fields for hash-based search only (encrypted/sensitive) */\n hashOnly?: string[]\n /** Fields to exclude from all search */\n excluded?: string[]\n}\n\n/**\n * Configuration for a single searchable entity within a module.\n */\nexport type SearchEntityConfig = {\n /** Entity identifier, e.g., 'customers:customer_person_profile' */\n entityId: EntityId\n /** Enable/disable search for this entity (default: true) */\n enabled?: boolean\n /** Override strategies for this specific entity */\n strategies?: SearchStrategyId[]\n /** Priority for result ordering (higher = more prominent) */\n priority?: number\n /** Build searchable content from record */\n buildSource?: (ctx: SearchBuildContext) => Promise<SearchIndexSource | null> | SearchIndexSource | null\n /** Format result for display in Cmd+K */\n formatResult?: (ctx: SearchBuildContext) => Promise<SearchResultPresenter | null> | SearchResultPresenter | null\n /** Resolve primary URL when result is clicked */\n resolveUrl?: (ctx: SearchBuildContext) => Promise<string | null> | string | null\n /** Resolve additional action links */\n resolveLinks?: (ctx: SearchBuildContext) => Promise<SearchResultLink[] | null> | SearchResultLink[] | null\n /** Define which fields are searchable vs hash-only */\n fieldPolicy?: SearchFieldPolicy\n}\n\n/**\n * Module-level search configuration (defined in search.ts files).\n */\nexport type SearchModuleConfig = {\n /** Default strategies for all entities in this module */\n defaultStrategies?: SearchStrategyId[]\n /** Entity configurations */\n entities: SearchEntityConfig[]\n}\n\n// =============================================================================\n// Event Payloads (for indexer events)\n// =============================================================================\n\n/**\n * Payload for search.index_record events.\n */\nexport type SearchIndexPayload = {\n entityId: EntityId\n recordId: string\n tenantId: string\n organizationId?: string | null\n record: Record<string, unknown>\n customFields?: Record<string, unknown>\n}\n\n/**\n * Payload for search.delete_record events.\n */\nexport type SearchDeletePayload = {\n entityId: EntityId\n recordId: string\n tenantId: string\n}\n\n// =============================================================================\n// Global Registry for Search Module Configs\n// =============================================================================\n\nlet _searchModuleConfigs: SearchModuleConfig[] | null = null\n\n/**\n * Register search module configurations globally.\n * Called during app bootstrap with configs from search.generated.ts.\n */\nexport function registerSearchModuleConfigs(configs: SearchModuleConfig[]): void {\n if (_searchModuleConfigs !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Search module configs re-registered (this may occur during HMR)')\n }\n _searchModuleConfigs = configs\n}\n\n/**\n * Get registered search module configurations.\n * Returns empty array if not registered (search module may not be enabled).\n */\nexport function getSearchModuleConfigs(): SearchModuleConfig[] {\n return _searchModuleConfigs ?? []\n}\n"],
5
- "mappings": "AAiTA,IAAI,uBAAoD;AAMjD,SAAS,4BAA4B,SAAqC;AAC/E,MAAI,yBAAyB,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC3E,YAAQ,MAAM,6EAA6E;AAAA,EAC7F;AACA,yBAAuB;AACzB;AAMO,SAAS,yBAA+C;AAC7D,SAAO,wBAAwB,CAAC;AAClC;",
4
+ "sourcesContent": ["import type { EntityId } from './entities'\n\n// =============================================================================\n// Strategy Identifiers\n// =============================================================================\n\n/**\n * Built-in strategy identifiers plus extensible string for third-party strategies.\n */\nexport type SearchStrategyId = 'tokens' | 'vector' | 'fulltext' | (string & {})\n\n// =============================================================================\n// Result Types\n// =============================================================================\n\n/**\n * Presenter metadata for displaying search results in UI (Cmd+K, global search).\n */\nexport type SearchResultPresenter = {\n title: string\n subtitle?: string\n icon?: string\n badge?: string\n}\n\n/**\n * Deep link rendered next to a search result.\n */\nexport type SearchResultLink = {\n href: string\n label: string\n kind?: 'primary' | 'secondary'\n}\n\n/**\n * A single search result returned by a strategy.\n */\nexport type SearchResult = {\n /** Entity type identifier, e.g., 'customers:customer_person_profile' */\n entityId: EntityId\n /** Record primary key */\n recordId: string\n /** Relevance score (normalized 0-1 range preferred, but RRF scores may exceed 1) */\n score: number\n /** Which strategy produced this result */\n source: SearchStrategyId\n /** Optional presenter for quick display */\n presenter?: SearchResultPresenter\n /** Primary URL when result is clicked */\n url?: string\n /** Additional action links */\n links?: SearchResultLink[]\n /** Extra metadata from the strategy */\n metadata?: Record<string, unknown>\n}\n\n// =============================================================================\n// Search Options\n// =============================================================================\n\n/**\n * Options passed to SearchService.search()\n */\nexport type SearchOptions = {\n /** Tenant isolation - required */\n tenantId: string\n /**\n * Optional organization filter.\n * - `string` restricts results to that organization only.\n * - `undefined` or `null` means no organization filter (tenant-wide).\n */\n organizationId?: string | null\n /** Filter to specific entity types */\n entityTypes?: EntityId[]\n /** Use only specific strategies (defaults to all available) */\n strategies?: SearchStrategyId[]\n /** Maximum results per strategy before merging */\n limit?: number\n /** Offset for pagination */\n offset?: number\n /** How to combine results: 'or' merges all, 'and' requires match in all strategies */\n combineMode?: 'or' | 'and'\n}\n\n// =============================================================================\n// Indexable Record\n// =============================================================================\n\n/**\n * A record prepared for indexing across all strategies.\n */\nexport type IndexableRecord = {\n /** Entity type identifier */\n entityId: EntityId\n /** Record primary key */\n recordId: string\n /** Tenant for isolation */\n tenantId: string\n /** Optional organization for additional filtering */\n organizationId?: string | null\n /** All fields from the record (strategies will filter based on their needs) */\n fields: Record<string, unknown>\n /** Optional presenter for result display */\n presenter?: SearchResultPresenter\n /** Primary URL for the record */\n url?: string\n /** Additional action links */\n links?: SearchResultLink[]\n /** Text content for embedding (from buildSource, used by vector strategy) */\n text?: string | string[]\n /** Source object for checksum calculation (change detection) */\n checksumSource?: unknown\n}\n\n// =============================================================================\n// Strategy Interface\n// =============================================================================\n\n/**\n * Interface that all search strategies must implement.\n * Following the cache module's strategy pattern.\n */\nexport interface SearchStrategy {\n /** Unique strategy identifier */\n readonly id: SearchStrategyId\n\n /** Human-readable name for debugging/logging */\n readonly name: string\n\n /** Priority for result merging (higher = more prominent in results) */\n readonly priority: number\n\n /** Check if strategy is available and configured */\n isAvailable(): Promise<boolean>\n\n /** Initialize strategy resources (lazy, called on first use) */\n ensureReady(): Promise<void>\n\n /** Execute a search query */\n search(query: string, options: SearchOptions): Promise<SearchResult[]>\n\n /** Index a record */\n index(record: IndexableRecord): Promise<void>\n\n /** Delete a record from the index */\n delete(entityId: EntityId, recordId: string, tenantId: string): Promise<void>\n\n /** Bulk index multiple records (optional optimization) */\n bulkIndex?(records: IndexableRecord[]): Promise<void>\n\n /** Purge all records for an entity type (optional) */\n purge?(entityId: EntityId, tenantId: string): Promise<void>\n}\n\n// =============================================================================\n// Service Configuration\n// =============================================================================\n\n/**\n * Configuration for result merging across strategies.\n */\nexport type ResultMergeConfig = {\n /** How to handle duplicate results: 'highest_score' | 'first' | 'merge_scores' */\n duplicateHandling: 'highest_score' | 'first' | 'merge_scores'\n /** Weight multipliers per strategy (e.g., { meilisearch: 1.2, tokens: 0.8 }) */\n strategyWeights?: Record<SearchStrategyId, number>\n /** Minimum score threshold to include in results */\n minScore?: number\n}\n\n/**\n * Callback function to enrich search results with presenter data.\n * Used to load presenter from database when not available from search strategy.\n */\nexport type PresenterEnricherFn = (\n results: SearchResult[],\n tenantId: string,\n organizationId?: string | null,\n) => Promise<SearchResult[]>\n\n/**\n * Options for creating a SearchService instance.\n */\nexport type SearchServiceOptions = {\n /** Array of strategy instances */\n strategies?: SearchStrategy[]\n /** Default strategies to use when not specified in search options */\n defaultStrategies?: SearchStrategyId[]\n /** Fallback strategy when others fail */\n fallbackStrategy?: SearchStrategyId\n /** Configuration for merging results from multiple strategies */\n mergeConfig?: ResultMergeConfig\n /** Callback to enrich results with presenter data from database */\n presenterEnricher?: PresenterEnricherFn\n}\n\n// =============================================================================\n// Module Configuration (for modules defining searchable entities)\n// =============================================================================\n\n/**\n * Context passed to buildSource, formatResult, resolveUrl, and resolveLinks.\n */\nexport type SearchBuildContext = {\n /** The record being indexed */\n record: Record<string, unknown>\n /** Custom fields for the record */\n customFields: Record<string, unknown>\n /** Organization ID if applicable */\n organizationId?: string | null\n /** Tenant ID */\n tenantId?: string | null\n /** DI container for resolving dependencies */\n container?: unknown\n /** Query engine for loading related records (optional, used by buildSource for entity hydration) */\n queryEngine?: unknown\n}\n\n/**\n * Source data for indexing a record.\n */\nexport type SearchIndexSource = {\n /** Text content for keyword/fuzzy search (single string or array of chunks) */\n text: string | string[]\n /** Optional structured fields for filtering */\n fields?: Record<string, unknown>\n /** Presenter for quick display in search results */\n presenter?: SearchResultPresenter\n /** Deep links for the result */\n links?: SearchResultLink[]\n /** Source object used for checksum calculation (change detection) */\n checksumSource?: unknown\n}\n\n/**\n * Policy defining how fields should be handled for search indexing.\n */\nexport type SearchFieldPolicy = {\n /** Fields safe to send to external providers (fuzzy searchable) */\n searchable?: string[]\n /** Fields for hash-based search only (encrypted/sensitive) */\n hashOnly?: string[]\n /** Fields to exclude from all search */\n excluded?: string[]\n}\n\n/**\n * Configuration for a single searchable entity within a module.\n */\nexport type SearchEntityConfig = {\n /** Entity identifier, e.g., 'customers:customer_person_profile' */\n entityId: EntityId\n /** Enable/disable search for this entity (default: true) */\n enabled?: boolean\n /** Override strategies for this specific entity */\n strategies?: SearchStrategyId[]\n /** Priority for result ordering (higher = more prominent) */\n priority?: number\n /** Build searchable content from record */\n buildSource?: (ctx: SearchBuildContext) => Promise<SearchIndexSource | null> | SearchIndexSource | null\n /** Format result for display in Cmd+K */\n formatResult?: (ctx: SearchBuildContext) => Promise<SearchResultPresenter | null> | SearchResultPresenter | null\n /** Resolve primary URL when result is clicked */\n resolveUrl?: (ctx: SearchBuildContext) => Promise<string | null> | string | null\n /** Resolve additional action links */\n resolveLinks?: (ctx: SearchBuildContext) => Promise<SearchResultLink[] | null> | SearchResultLink[] | null\n /** Define which fields are searchable vs hash-only */\n fieldPolicy?: SearchFieldPolicy\n}\n\n/**\n * Module-level search configuration (defined in search.ts files).\n */\nexport type SearchModuleConfig = {\n /** Default strategies for all entities in this module */\n defaultStrategies?: SearchStrategyId[]\n /** Entity configurations */\n entities: SearchEntityConfig[]\n}\n\n// =============================================================================\n// Event Payloads (for indexer events)\n// =============================================================================\n\n/**\n * Payload for search.index_record events.\n */\nexport type SearchIndexPayload = {\n entityId: EntityId\n recordId: string\n tenantId: string\n organizationId?: string | null\n record: Record<string, unknown>\n customFields?: Record<string, unknown>\n}\n\n/**\n * Payload for search.delete_record events.\n */\nexport type SearchDeletePayload = {\n entityId: EntityId\n recordId: string\n tenantId: string\n}\n\n// =============================================================================\n// Global Registry for Search Module Configs\n// =============================================================================\n\nlet _searchModuleConfigs: SearchModuleConfig[] | null = null\n\n/**\n * Register search module configurations globally.\n * Called during app bootstrap with configs from search.generated.ts.\n */\nexport function registerSearchModuleConfigs(configs: SearchModuleConfig[]): void {\n if (_searchModuleConfigs !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Search module configs re-registered (this may occur during HMR)')\n }\n _searchModuleConfigs = configs\n}\n\n/**\n * Get registered search module configurations.\n * Returns empty array if not registered (search module may not be enabled).\n */\nexport function getSearchModuleConfigs(): SearchModuleConfig[] {\n return _searchModuleConfigs ?? []\n}\n"],
5
+ "mappings": "AAqTA,IAAI,uBAAoD;AAMjD,SAAS,4BAA4B,SAAqC;AAC/E,MAAI,yBAAyB,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC3E,YAAQ,MAAM,6EAA6E;AAAA,EAC7F;AACA,yBAAuB;AACzB;AAMO,SAAS,yBAA+C;AAC7D,SAAO,wBAAwB,CAAC;AAClC;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/shared",
3
- "version": "0.4.5-develop-0b66ecfdd4",
3
+ "version": "0.4.5-develop-2289152f60",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -0,0 +1,8 @@
1
+ export { toOptionalString } from './string/coerce'
2
+
3
+ export function trimToUndefined(value: unknown): string | undefined {
4
+ if (typeof value !== 'string') return undefined
5
+ const trimmed = value.trim()
6
+ return trimmed.length > 0 ? trimmed : undefined
7
+ }
8
+
@@ -64,7 +64,11 @@ export type SearchResult = {
64
64
  export type SearchOptions = {
65
65
  /** Tenant isolation - required */
66
66
  tenantId: string
67
- /** Optional organization filter */
67
+ /**
68
+ * Optional organization filter.
69
+ * - `string` restricts results to that organization only.
70
+ * - `undefined` or `null` means no organization filter (tenant-wide).
71
+ */
68
72
  organizationId?: string | null
69
73
  /** Filter to specific entity types */
70
74
  entityTypes?: EntityId[]