@basaltkit/search 1.0.0 → 1.2.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Machize Contributors
3
+ Copyright (c) 2026 Basalt Contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.d.ts CHANGED
@@ -103,6 +103,9 @@ declare class MeilisearchError extends BasaltError {
103
103
  readonly httpStatus: number;
104
104
  constructor(httpStatus: number, message: string);
105
105
  }
106
+ declare class SearchFilterFieldError extends BasaltError {
107
+ constructor(field: string);
108
+ }
106
109
  interface MeilisearchDriverOptions {
107
110
  host: string;
108
111
  apiKey?: string;
@@ -156,7 +159,14 @@ interface SearchPluginOptions {
156
159
  indexes?: IndexDefinition[];
157
160
  /** Rules keeping indexes in sync with domain hooks. */
158
161
  sync?: SyncRule[];
162
+ /**
163
+ * Throw if an index fails to register at boot. Default `false`: a search
164
+ * backend that's down or misconfigured logs a warning and the app boots
165
+ * anyway (search stays degraded until the backend is reachable), so an outage
166
+ * never blocks unrelated work — including CLI commands that don't use search.
167
+ */
168
+ failOnRegisterError?: boolean;
159
169
  }
160
170
  declare function searchPlugin(options?: SearchPluginOptions): _basaltkit_core.BasaltPlugin<unknown>;
161
171
 
162
- export { type IndexDefinition, MeilisearchDriver, type MeilisearchDriverOptions, MeilisearchError, MemorySearchDriver, SEARCH, Search, type SearchDocument, type SearchDriver, type SearchHit, type SearchOptions, type SearchPluginOptions, type SearchQuery, type SearchResult, type SyncRule, TenantRequiredError, defineIndex, searchPlugin, syncRule };
172
+ export { type IndexDefinition, MeilisearchDriver, type MeilisearchDriverOptions, MeilisearchError, MemorySearchDriver, SEARCH, Search, type SearchDocument, type SearchDriver, SearchFilterFieldError, type SearchHit, type SearchOptions, type SearchPluginOptions, type SearchQuery, type SearchResult, type SyncRule, TenantRequiredError, defineIndex, searchPlugin, syncRule };
package/dist/index.js CHANGED
@@ -128,6 +128,12 @@ var MeilisearchError = class extends BasaltError2 {
128
128
  }
129
129
  httpStatus;
130
130
  };
131
+ var SearchFilterFieldError = class extends BasaltError2 {
132
+ constructor(field) {
133
+ super("SEARCH_INVALID_FILTER_FIELD", `Invalid filter field name: ${JSON.stringify(field)}.`);
134
+ }
135
+ };
136
+ var SAFE_FILTER_FIELD = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
131
137
  var primaryKey = (tenantId, id) => Buffer.from(`${tenantId}::${id}`).toString("base64url");
132
138
  var quote = (value) => JSON.stringify(value);
133
139
  var MeilisearchDriver = class {
@@ -182,6 +188,7 @@ var MeilisearchDriver = class {
182
188
  buildFilter(tenantId, filters) {
183
189
  const parts = [`tenantId = ${quote(tenantId)}`];
184
190
  for (const [field, value] of Object.entries(filters ?? {})) {
191
+ if (!SAFE_FILTER_FIELD.test(field)) throw new SearchFilterFieldError(field);
185
192
  parts.push(Array.isArray(value) ? `${field} IN [${value.map(quote).join(", ")}]` : `${field} = ${quote(value)}`);
186
193
  }
187
194
  return parts.join(" AND ");
@@ -221,7 +228,18 @@ function searchPlugin(options = {}) {
221
228
  async boot({ container, hooks }) {
222
229
  const search = container.get(SEARCH);
223
230
  if (driver.register) {
224
- for (const index of options.indexes ?? []) await driver.register(index);
231
+ for (const index of options.indexes ?? []) {
232
+ try {
233
+ await driver.register(index);
234
+ } catch (error) {
235
+ if (options.failOnRegisterError) throw error;
236
+ console.warn(
237
+ `[basalt:search] could not register index "${index.name}": ${String(
238
+ error?.message ?? error
239
+ )} \u2014 search is degraded until the backend is reachable.`
240
+ );
241
+ }
242
+ }
225
243
  }
226
244
  for (const rule of options.sync ?? []) {
227
245
  hooks.on(rule.hook, async (payload) => {
@@ -243,6 +261,7 @@ export {
243
261
  MemorySearchDriver,
244
262
  SEARCH,
245
263
  Search,
264
+ SearchFilterFieldError,
246
265
  TenantRequiredError,
247
266
  defineIndex,
248
267
  searchPlugin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basaltkit/search",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Full-text search for Basalt: tenant-scoped indexing and querying with a pluggable driver (in-memory for dev/test, Meilisearch for production) and automatic sync from domain events.",
5
5
  "license": "MIT",
6
6
  "type": "module",