@tsctl/cli 0.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/README.md +735 -0
- package/bin/tsctl.js +2 -0
- package/package.json +65 -0
- package/src/__tests__/analyticsrules.test.ts +303 -0
- package/src/__tests__/apikeys.test.ts +223 -0
- package/src/__tests__/apply.test.ts +245 -0
- package/src/__tests__/client.test.ts +48 -0
- package/src/__tests__/collection-advanced.test.ts +274 -0
- package/src/__tests__/config-loader.test.ts +217 -0
- package/src/__tests__/curationsets.test.ts +190 -0
- package/src/__tests__/helpers.ts +17 -0
- package/src/__tests__/import-drift.test.ts +231 -0
- package/src/__tests__/migrate-advanced.test.ts +197 -0
- package/src/__tests__/migrate.test.ts +220 -0
- package/src/__tests__/plan-new-resources.test.ts +258 -0
- package/src/__tests__/plan.test.ts +337 -0
- package/src/__tests__/presets.test.ts +97 -0
- package/src/__tests__/resources.test.ts +592 -0
- package/src/__tests__/setup.ts +77 -0
- package/src/__tests__/state.test.ts +312 -0
- package/src/__tests__/stemmingdictionaries.test.ts +111 -0
- package/src/__tests__/stopwords.test.ts +109 -0
- package/src/__tests__/synonymsets.test.ts +170 -0
- package/src/__tests__/types.test.ts +416 -0
- package/src/apply/index.ts +336 -0
- package/src/cli/index.ts +1106 -0
- package/src/client/index.ts +55 -0
- package/src/config/loader.ts +158 -0
- package/src/index.ts +45 -0
- package/src/migrate/index.ts +220 -0
- package/src/plan/index.ts +1333 -0
- package/src/resources/alias.ts +59 -0
- package/src/resources/analyticsrule.ts +134 -0
- package/src/resources/apikey.ts +203 -0
- package/src/resources/collection.ts +424 -0
- package/src/resources/curationset.ts +155 -0
- package/src/resources/index.ts +11 -0
- package/src/resources/override.ts +174 -0
- package/src/resources/preset.ts +83 -0
- package/src/resources/stemmingdictionary.ts +103 -0
- package/src/resources/stopword.ts +100 -0
- package/src/resources/synonym.ts +152 -0
- package/src/resources/synonymset.ts +144 -0
- package/src/state/index.ts +206 -0
- package/src/types/index.ts +451 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Field Types
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
export const FieldTypeSchema = z.enum([
|
|
8
|
+
"string",
|
|
9
|
+
"string[]",
|
|
10
|
+
"int32",
|
|
11
|
+
"int32[]",
|
|
12
|
+
"int64",
|
|
13
|
+
"int64[]",
|
|
14
|
+
"float",
|
|
15
|
+
"float[]",
|
|
16
|
+
"bool",
|
|
17
|
+
"bool[]",
|
|
18
|
+
"geopoint",
|
|
19
|
+
"geopoint[]",
|
|
20
|
+
"geopolygon",
|
|
21
|
+
"object",
|
|
22
|
+
"object[]",
|
|
23
|
+
"auto",
|
|
24
|
+
"string*",
|
|
25
|
+
"image",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
export type FieldType = z.infer<typeof FieldTypeSchema>;
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Collection Field Schema
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
export const FieldSchema = z.object({
|
|
35
|
+
name: z.string(),
|
|
36
|
+
type: FieldTypeSchema,
|
|
37
|
+
optional: z.boolean().optional(),
|
|
38
|
+
facet: z.boolean().optional(),
|
|
39
|
+
index: z.boolean().optional(),
|
|
40
|
+
sort: z.boolean().optional(),
|
|
41
|
+
infix: z.boolean().optional(),
|
|
42
|
+
locale: z.string().optional(),
|
|
43
|
+
stem: z.boolean().optional(),
|
|
44
|
+
store: z.boolean().optional(),
|
|
45
|
+
num_dim: z.number().optional(),
|
|
46
|
+
vec_dist: z.enum(["cosine", "ip"]).optional(),
|
|
47
|
+
reference: z.string().optional(),
|
|
48
|
+
range_index: z.boolean().optional(),
|
|
49
|
+
stem_dictionary: z.string().optional(),
|
|
50
|
+
truncate_len: z.number().optional(),
|
|
51
|
+
token_separators: z.array(z.string()).optional(),
|
|
52
|
+
symbols_to_index: z.array(z.string()).optional(),
|
|
53
|
+
embed: z
|
|
54
|
+
.object({
|
|
55
|
+
from: z.array(z.string()),
|
|
56
|
+
model_config: z.object({
|
|
57
|
+
model_name: z.string(),
|
|
58
|
+
api_key: z.string().optional(),
|
|
59
|
+
indexing_prefix: z.string().optional(),
|
|
60
|
+
query_prefix: z.string().optional(),
|
|
61
|
+
// GCP Vertex AI configuration
|
|
62
|
+
project_id: z.string().optional(),
|
|
63
|
+
service_account: z
|
|
64
|
+
.object({
|
|
65
|
+
client_email: z.string(),
|
|
66
|
+
private_key: z.string(),
|
|
67
|
+
})
|
|
68
|
+
.optional(),
|
|
69
|
+
// GCP OAuth configuration (alternative to service_account)
|
|
70
|
+
access_token: z.string().optional(),
|
|
71
|
+
refresh_token: z.string().optional(),
|
|
72
|
+
client_id: z.string().optional(),
|
|
73
|
+
client_secret: z.string().optional(),
|
|
74
|
+
}),
|
|
75
|
+
})
|
|
76
|
+
.optional(),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export type Field = z.infer<typeof FieldSchema>;
|
|
80
|
+
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// Collection Schema
|
|
83
|
+
// ============================================================================
|
|
84
|
+
|
|
85
|
+
export const CollectionConfigSchema = z.object({
|
|
86
|
+
name: z.string(),
|
|
87
|
+
fields: z.array(FieldSchema),
|
|
88
|
+
default_sorting_field: z.string().optional(),
|
|
89
|
+
token_separators: z.array(z.string()).optional(),
|
|
90
|
+
symbols_to_index: z.array(z.string()).optional(),
|
|
91
|
+
enable_nested_fields: z.boolean().optional(),
|
|
92
|
+
// Typesense 30.0+: Link to global synonym sets
|
|
93
|
+
synonym_sets: z.array(z.string()).optional(),
|
|
94
|
+
// Typesense 30.0+: Link to global curation sets
|
|
95
|
+
curation_sets: z.array(z.string()).optional(),
|
|
96
|
+
// Custom metadata object
|
|
97
|
+
metadata: z.record(z.unknown()).optional(),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export type CollectionConfig = z.infer<typeof CollectionConfigSchema>;
|
|
101
|
+
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// Alias Schema
|
|
104
|
+
// ============================================================================
|
|
105
|
+
|
|
106
|
+
export const AliasConfigSchema = z.object({
|
|
107
|
+
name: z.string(),
|
|
108
|
+
collection: z.string(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
export type AliasConfig = z.infer<typeof AliasConfigSchema>;
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Synonym Schema (Legacy - per-collection, for Typesense < 30.0)
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
export const SynonymConfigSchema = z.object({
|
|
118
|
+
id: z.string(),
|
|
119
|
+
collection: z.string(),
|
|
120
|
+
synonyms: z.array(z.string()).optional(),
|
|
121
|
+
root: z.string().optional(),
|
|
122
|
+
symbols_to_index: z.array(z.string()).optional(),
|
|
123
|
+
locale: z.string().optional(),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
export type SynonymConfig = z.infer<typeof SynonymConfigSchema>;
|
|
127
|
+
|
|
128
|
+
// ============================================================================
|
|
129
|
+
// Synonym Set Schema (Typesense 30.0+ - global, reusable synonym sets)
|
|
130
|
+
// ============================================================================
|
|
131
|
+
|
|
132
|
+
export const SynonymSetItemSchema = z.object({
|
|
133
|
+
id: z.string(),
|
|
134
|
+
synonyms: z.array(z.string()).optional(),
|
|
135
|
+
root: z.string().optional(),
|
|
136
|
+
symbols_to_index: z.array(z.string()).optional(),
|
|
137
|
+
locale: z.string().optional(),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
export type SynonymSetItem = z.infer<typeof SynonymSetItemSchema>;
|
|
141
|
+
|
|
142
|
+
export const SynonymSetConfigSchema = z.object({
|
|
143
|
+
name: z.string(),
|
|
144
|
+
items: z.array(SynonymSetItemSchema),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
export type SynonymSetConfig = z.infer<typeof SynonymSetConfigSchema>;
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// Override/Curation Schema (planned)
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
export const OverrideRuleSchema = z.object({
|
|
154
|
+
query: z.string().optional(),
|
|
155
|
+
match: z.enum(["exact", "contains"]).optional(),
|
|
156
|
+
filter_by: z.string().optional(),
|
|
157
|
+
tags: z.array(z.string()).optional(),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
export const OverrideConfigSchema = z.object({
|
|
161
|
+
id: z.string(),
|
|
162
|
+
collection: z.string(),
|
|
163
|
+
rule: OverrideRuleSchema,
|
|
164
|
+
includes: z
|
|
165
|
+
.array(
|
|
166
|
+
z.object({
|
|
167
|
+
id: z.string(),
|
|
168
|
+
position: z.number(),
|
|
169
|
+
})
|
|
170
|
+
)
|
|
171
|
+
.optional(),
|
|
172
|
+
excludes: z
|
|
173
|
+
.array(
|
|
174
|
+
z.object({
|
|
175
|
+
id: z.string(),
|
|
176
|
+
})
|
|
177
|
+
)
|
|
178
|
+
.optional(),
|
|
179
|
+
filter_by: z.string().optional(),
|
|
180
|
+
sort_by: z.string().optional(),
|
|
181
|
+
replace_query: z.string().optional(),
|
|
182
|
+
remove_matched_tokens: z.boolean().optional(),
|
|
183
|
+
filter_curated_hits: z.boolean().optional(),
|
|
184
|
+
effective_from_ts: z.number().optional(),
|
|
185
|
+
effective_to_ts: z.number().optional(),
|
|
186
|
+
stop_processing: z.boolean().optional(),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
export type OverrideConfig = z.infer<typeof OverrideConfigSchema>;
|
|
190
|
+
|
|
191
|
+
// ============================================================================
|
|
192
|
+
// Analytics Rule Schema
|
|
193
|
+
// ============================================================================
|
|
194
|
+
|
|
195
|
+
export const AnalyticsRuleParamsSchema = z.object({
|
|
196
|
+
destination_collection: z.string().optional(),
|
|
197
|
+
// v30+: structured source/destination
|
|
198
|
+
source: z
|
|
199
|
+
.object({
|
|
200
|
+
collections: z.array(z.string()).optional(),
|
|
201
|
+
events: z
|
|
202
|
+
.array(
|
|
203
|
+
z.object({
|
|
204
|
+
type: z.string(),
|
|
205
|
+
name: z.string(),
|
|
206
|
+
weight: z.number().optional(),
|
|
207
|
+
})
|
|
208
|
+
)
|
|
209
|
+
.optional(),
|
|
210
|
+
})
|
|
211
|
+
.optional(),
|
|
212
|
+
destination: z
|
|
213
|
+
.object({
|
|
214
|
+
collection: z.string(),
|
|
215
|
+
})
|
|
216
|
+
.optional(),
|
|
217
|
+
limit: z.number().optional(),
|
|
218
|
+
capture_search_requests: z.boolean().optional(),
|
|
219
|
+
meta_fields: z.array(z.string()).optional(),
|
|
220
|
+
expand_query: z.boolean().optional(),
|
|
221
|
+
counter_field: z.string().optional(),
|
|
222
|
+
weight: z.number().optional(),
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
export const AnalyticsRuleConfigSchema = z.object({
|
|
226
|
+
name: z.string(),
|
|
227
|
+
type: z.enum(["popular_queries", "nohits_queries", "counter", "log"]),
|
|
228
|
+
collection: z.string(),
|
|
229
|
+
event_type: z.enum(["search", "click", "conversion", "visit", "custom"]),
|
|
230
|
+
rule_tag: z.string().optional(),
|
|
231
|
+
params: AnalyticsRuleParamsSchema.optional(),
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
export type AnalyticsRuleConfig = z.infer<typeof AnalyticsRuleConfigSchema>;
|
|
235
|
+
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// API Key Schema
|
|
238
|
+
// ============================================================================
|
|
239
|
+
|
|
240
|
+
export const ApiKeyConfigSchema = z.object({
|
|
241
|
+
description: z.string(),
|
|
242
|
+
actions: z.array(z.string()),
|
|
243
|
+
collections: z.array(z.string()),
|
|
244
|
+
value: z.string().optional(), // Only used when creating, not stored in state
|
|
245
|
+
expires_at: z.number().optional(),
|
|
246
|
+
autodelete: z.boolean().optional(),
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
export type ApiKeyConfig = z.infer<typeof ApiKeyConfigSchema>;
|
|
250
|
+
|
|
251
|
+
// ============================================================================
|
|
252
|
+
// Stopword Set Schema
|
|
253
|
+
// ============================================================================
|
|
254
|
+
|
|
255
|
+
export const StopwordSetConfigSchema = z.object({
|
|
256
|
+
id: z.string(),
|
|
257
|
+
stopwords: z.array(z.string()),
|
|
258
|
+
locale: z.string().optional(),
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
export type StopwordSetConfig = z.infer<typeof StopwordSetConfigSchema>;
|
|
262
|
+
|
|
263
|
+
// ============================================================================
|
|
264
|
+
// Preset Schema
|
|
265
|
+
// ============================================================================
|
|
266
|
+
|
|
267
|
+
export const PresetConfigSchema = z.object({
|
|
268
|
+
name: z.string(),
|
|
269
|
+
value: z.record(z.unknown()),
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
export type PresetConfig = z.infer<typeof PresetConfigSchema>;
|
|
273
|
+
|
|
274
|
+
// ============================================================================
|
|
275
|
+
// Curation Set Schema (Typesense 30.0+ - global curation rules)
|
|
276
|
+
// ============================================================================
|
|
277
|
+
|
|
278
|
+
export const CurationRuleSchema = z.object({
|
|
279
|
+
query: z.string().optional(),
|
|
280
|
+
match: z.enum(["exact", "contains"]).optional(),
|
|
281
|
+
filter_by: z.string().optional(),
|
|
282
|
+
tags: z.array(z.string()).optional(),
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
export const CurationItemSchema = z.object({
|
|
286
|
+
id: z.string(),
|
|
287
|
+
rule: CurationRuleSchema.optional(),
|
|
288
|
+
includes: z
|
|
289
|
+
.array(
|
|
290
|
+
z.object({
|
|
291
|
+
id: z.string(),
|
|
292
|
+
position: z.number(),
|
|
293
|
+
})
|
|
294
|
+
)
|
|
295
|
+
.optional(),
|
|
296
|
+
excludes: z
|
|
297
|
+
.array(
|
|
298
|
+
z.object({
|
|
299
|
+
id: z.string(),
|
|
300
|
+
})
|
|
301
|
+
)
|
|
302
|
+
.optional(),
|
|
303
|
+
filter_by: z.string().optional(),
|
|
304
|
+
sort_by: z.string().optional(),
|
|
305
|
+
replace_query: z.string().optional(),
|
|
306
|
+
remove_matched_tokens: z.boolean().optional(),
|
|
307
|
+
filter_curated_hits: z.boolean().optional(),
|
|
308
|
+
stop_processing: z.boolean().optional(),
|
|
309
|
+
metadata: z.record(z.unknown()).optional(),
|
|
310
|
+
effective_from_ts: z.number().optional(),
|
|
311
|
+
effective_to_ts: z.number().optional(),
|
|
312
|
+
diversity: z
|
|
313
|
+
.object({
|
|
314
|
+
similarity_metric: z.array(
|
|
315
|
+
z.object({
|
|
316
|
+
field: z.string(),
|
|
317
|
+
method: z.enum(["jaccard", "equality", "vector_distance"]),
|
|
318
|
+
weight: z.number().optional(),
|
|
319
|
+
})
|
|
320
|
+
),
|
|
321
|
+
})
|
|
322
|
+
.optional(),
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
export type CurationItem = z.infer<typeof CurationItemSchema>;
|
|
326
|
+
|
|
327
|
+
export const CurationSetConfigSchema = z.object({
|
|
328
|
+
name: z.string(),
|
|
329
|
+
items: z.array(CurationItemSchema),
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
export type CurationSetConfig = z.infer<typeof CurationSetConfigSchema>;
|
|
333
|
+
|
|
334
|
+
// ============================================================================
|
|
335
|
+
// Stemming Dictionary Schema
|
|
336
|
+
// ============================================================================
|
|
337
|
+
|
|
338
|
+
export const StemmingWordSchema = z.object({
|
|
339
|
+
word: z.string(),
|
|
340
|
+
root: z.string(),
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
export type StemmingWord = z.infer<typeof StemmingWordSchema>;
|
|
344
|
+
|
|
345
|
+
export const StemmingDictionaryConfigSchema = z.object({
|
|
346
|
+
id: z.string(),
|
|
347
|
+
words: z.array(StemmingWordSchema),
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
export type StemmingDictionaryConfig = z.infer<typeof StemmingDictionaryConfigSchema>;
|
|
351
|
+
|
|
352
|
+
// ============================================================================
|
|
353
|
+
// Full Config Schema
|
|
354
|
+
// ============================================================================
|
|
355
|
+
|
|
356
|
+
export const TypesenseConfigSchema = z.object({
|
|
357
|
+
collections: z.array(CollectionConfigSchema).optional(),
|
|
358
|
+
aliases: z.array(AliasConfigSchema).optional(),
|
|
359
|
+
// Legacy per-collection synonyms (Typesense < 30.0)
|
|
360
|
+
synonyms: z.array(SynonymConfigSchema).optional(),
|
|
361
|
+
// Global synonym sets (Typesense 30.0+)
|
|
362
|
+
synonymSets: z.array(SynonymSetConfigSchema).optional(),
|
|
363
|
+
overrides: z.array(OverrideConfigSchema).optional(),
|
|
364
|
+
// Global curation sets (Typesense 30.0+)
|
|
365
|
+
curationSets: z.array(CurationSetConfigSchema).optional(),
|
|
366
|
+
analyticsRules: z.array(AnalyticsRuleConfigSchema).optional(),
|
|
367
|
+
apiKeys: z.array(ApiKeyConfigSchema).optional(),
|
|
368
|
+
stopwords: z.array(StopwordSetConfigSchema).optional(),
|
|
369
|
+
presets: z.array(PresetConfigSchema).optional(),
|
|
370
|
+
stemmingDictionaries: z.array(StemmingDictionaryConfigSchema).optional(),
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
export type TypesenseConfig = z.infer<typeof TypesenseConfigSchema>;
|
|
374
|
+
|
|
375
|
+
// ============================================================================
|
|
376
|
+
// Resource Types for State Management
|
|
377
|
+
// ============================================================================
|
|
378
|
+
|
|
379
|
+
export type ResourceType = "collection" | "alias" | "synonym" | "synonymSet" | "override" | "curationSet" | "analyticsRule" | "apiKey" | "stopword" | "preset" | "stemmingDictionary";
|
|
380
|
+
|
|
381
|
+
export interface ResourceIdentifier {
|
|
382
|
+
type: ResourceType;
|
|
383
|
+
name: string;
|
|
384
|
+
/** For resources scoped to a collection (synonyms, overrides) */
|
|
385
|
+
collection?: string;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export interface ManagedResource {
|
|
389
|
+
identifier: ResourceIdentifier;
|
|
390
|
+
config: CollectionConfig | AliasConfig | SynonymConfig | SynonymSetConfig | OverrideConfig | CurationSetConfig | AnalyticsRuleConfig | ApiKeyConfig | StopwordSetConfig | PresetConfig | StemmingDictionaryConfig;
|
|
391
|
+
checksum: string;
|
|
392
|
+
lastUpdated: string;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export interface State {
|
|
396
|
+
version: string;
|
|
397
|
+
resources: ManagedResource[];
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ============================================================================
|
|
401
|
+
// Plan Types
|
|
402
|
+
// ============================================================================
|
|
403
|
+
|
|
404
|
+
export type ChangeAction = "create" | "update" | "delete" | "no-change";
|
|
405
|
+
|
|
406
|
+
export interface ResourceChange {
|
|
407
|
+
action: ChangeAction;
|
|
408
|
+
identifier: ResourceIdentifier;
|
|
409
|
+
before?: unknown;
|
|
410
|
+
after?: unknown;
|
|
411
|
+
diff?: string;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export interface Plan {
|
|
415
|
+
changes: ResourceChange[];
|
|
416
|
+
hasChanges: boolean;
|
|
417
|
+
summary: {
|
|
418
|
+
create: number;
|
|
419
|
+
update: number;
|
|
420
|
+
delete: number;
|
|
421
|
+
noChange: number;
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ============================================================================
|
|
426
|
+
// Connection Config
|
|
427
|
+
// ============================================================================
|
|
428
|
+
|
|
429
|
+
export const ConnectionConfigSchema = z.object({
|
|
430
|
+
nodes: z.array(
|
|
431
|
+
z.object({
|
|
432
|
+
host: z.string(),
|
|
433
|
+
port: z.number(),
|
|
434
|
+
protocol: z.enum(["http", "https"]),
|
|
435
|
+
})
|
|
436
|
+
),
|
|
437
|
+
apiKey: z.string(),
|
|
438
|
+
connectionTimeoutSeconds: z.number().optional(),
|
|
439
|
+
retryIntervalSeconds: z.number().optional(),
|
|
440
|
+
numRetries: z.number().optional(),
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
export type ConnectionConfig = z.infer<typeof ConnectionConfigSchema>;
|
|
444
|
+
|
|
445
|
+
// ============================================================================
|
|
446
|
+
// Define Config Helper
|
|
447
|
+
// ============================================================================
|
|
448
|
+
|
|
449
|
+
export function defineConfig(config: TypesenseConfig): TypesenseConfig {
|
|
450
|
+
return TypesenseConfigSchema.parse(config);
|
|
451
|
+
}
|