@hailer/mcp 1.1.12 → 1.1.13
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/CHANGELOG.md +0 -7
- package/{.claude → dist}/CLAUDE.md +2 -2
- package/dist/app.js +18 -5
- package/dist/bot/bot-config.d.ts +10 -1
- package/dist/bot/bot-config.js +64 -3
- package/dist/bot/bot-manager.d.ts +2 -0
- package/dist/bot/bot-manager.js +9 -2
- package/dist/bot/bot.d.ts +33 -0
- package/dist/bot/bot.js +461 -160
- package/dist/bot/services/message-classifier.js +17 -0
- package/dist/bot/services/permission-guard.d.ts +52 -0
- package/dist/bot/services/permission-guard.js +149 -0
- package/dist/bot/services/types.d.ts +5 -0
- package/dist/bot/services/typing-indicator.d.ts +6 -1
- package/dist/bot/services/typing-indicator.js +19 -3
- package/dist/cli.js +0 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.js +43 -0
- package/dist/core.js +3 -6
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/mcp/UserContextCache.d.ts +5 -0
- package/dist/mcp/UserContextCache.js +51 -19
- package/dist/mcp/hailer-clients.d.ts +19 -1
- package/dist/mcp/hailer-clients.js +158 -24
- package/dist/mcp/session-store.d.ts +68 -0
- package/dist/mcp/session-store.js +169 -0
- package/dist/mcp/signal-handler.js +2 -0
- package/dist/mcp/tool-registry.d.ts +17 -4
- package/dist/mcp/tool-registry.js +37 -7
- package/dist/mcp/tools/activity.js +99 -7
- package/dist/mcp/tools/app-scaffold.js +304 -336
- package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
- package/dist/mcp/tools/bot-config/constants.js +94 -0
- package/dist/mcp/tools/bot-config/core.d.ts +253 -0
- package/dist/mcp/tools/bot-config/core.js +2456 -0
- package/dist/mcp/tools/bot-config/index.d.ts +10 -0
- package/dist/mcp/tools/bot-config/index.js +59 -0
- package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
- package/dist/mcp/tools/bot-config/tools.js +15 -0
- package/dist/mcp/tools/bot-config/types.d.ts +50 -0
- package/dist/mcp/tools/bot-config/types.js +6 -0
- package/dist/mcp/tools/bug-fixer-tools.d.ts +45 -0
- package/dist/mcp/tools/bug-fixer-tools.js +1096 -0
- package/dist/mcp/tools/company.d.ts +9 -0
- package/dist/mcp/tools/company.js +88 -0
- package/dist/mcp/tools/discussion.js +68 -0
- package/dist/mcp/tools/document.d.ts +11 -0
- package/dist/mcp/tools/document.js +741 -0
- package/dist/mcp/tools/investigate.d.ts +9 -0
- package/dist/mcp/tools/investigate.js +254 -0
- package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
- package/dist/mcp/tools/workflow-permissions.js +204 -0
- package/dist/mcp/tools/workflow.js +57 -18
- package/dist/mcp/utils/index.d.ts +2 -0
- package/dist/mcp/utils/index.js +12 -1
- package/dist/mcp/utils/role-utils.d.ts +74 -0
- package/dist/mcp/utils/role-utils.js +151 -0
- package/dist/mcp/utils/types.d.ts +43 -1
- package/dist/mcp/utils/types.js +14 -0
- package/dist/mcp/webhook-handler.d.ts +4 -0
- package/dist/mcp/webhook-handler.js +8 -0
- package/dist/mcp-server.d.ts +23 -2
- package/dist/mcp-server.js +639 -127
- package/dist/plugins/vipunen/client.d.ts +150 -0
- package/dist/plugins/vipunen/client.js +535 -0
- package/dist/plugins/vipunen/config/schema-config.json +19 -0
- package/dist/plugins/vipunen/config/schema-doc.json +22 -0
- package/dist/plugins/vipunen/index.d.ts +41 -0
- package/dist/plugins/vipunen/index.js +88 -0
- package/dist/plugins/vipunen/tools.d.ts +26 -0
- package/dist/plugins/vipunen/tools.js +501 -0
- package/dist/stdio-server.d.ts +14 -0
- package/dist/stdio-server.js +101 -0
- package/package.json +2 -1
- package/.claude/agents/agent-ada-skill-builder.md +0 -94
- package/.claude/agents/agent-alejandro-function-fields.md +0 -342
- package/.claude/agents/agent-bjorn-config-audit.md +0 -103
- package/.claude/agents/agent-builder-agent-creator.md +0 -130
- package/.claude/agents/agent-code-simplifier.md +0 -53
- package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
- package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
- package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
- package/.claude/agents/agent-helga-workflow-config.md +0 -204
- package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
- package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
- package/.claude/agents/agent-ivan-monolith.md +0 -154
- package/.claude/agents/agent-kenji-data-reader.md +0 -86
- package/.claude/agents/agent-lars-code-inspector.md +0 -102
- package/.claude/agents/agent-marco-mockup-builder.md +0 -110
- package/.claude/agents/agent-marcus-api-documenter.md +0 -323
- package/.claude/agents/agent-marketplace-publisher.md +0 -280
- package/.claude/agents/agent-marketplace-reviewer.md +0 -309
- package/.claude/agents/agent-permissions-handler.md +0 -208
- package/.claude/agents/agent-simple-writer.md +0 -48
- package/.claude/agents/agent-svetlana-code-review.md +0 -171
- package/.claude/agents/agent-tanya-test-runner.md +0 -333
- package/.claude/agents/agent-ui-designer.md +0 -100
- package/.claude/agents/agent-viktor-sql-insights.md +0 -212
- package/.claude/agents/agent-web-search.md +0 -55
- package/.claude/agents/agent-yevgeni-discussions.md +0 -45
- package/.claude/agents/agent-zara-zapier.md +0 -159
- package/.claude/commands/app-squad.md +0 -135
- package/.claude/commands/audit-squad.md +0 -158
- package/.claude/commands/autoplan.md +0 -563
- package/.claude/commands/cleanup-squad.md +0 -98
- package/.claude/commands/config-squad.md +0 -106
- package/.claude/commands/crud-squad.md +0 -87
- package/.claude/commands/data-squad.md +0 -97
- package/.claude/commands/debug-squad.md +0 -303
- package/.claude/commands/doc-squad.md +0 -65
- package/.claude/commands/handoff.md +0 -137
- package/.claude/commands/health.md +0 -49
- package/.claude/commands/help.md +0 -29
- package/.claude/commands/help:agents.md +0 -151
- package/.claude/commands/help:commands.md +0 -78
- package/.claude/commands/help:faq.md +0 -79
- package/.claude/commands/help:plugins.md +0 -50
- package/.claude/commands/help:skills.md +0 -93
- package/.claude/commands/help:tools.md +0 -75
- package/.claude/commands/hotfix-squad.md +0 -112
- package/.claude/commands/integration-squad.md +0 -82
- package/.claude/commands/janitor-squad.md +0 -167
- package/.claude/commands/learn-auto.md +0 -120
- package/.claude/commands/learn.md +0 -120
- package/.claude/commands/mcp-list.md +0 -27
- package/.claude/commands/onboard-squad.md +0 -140
- package/.claude/commands/plan-workspace.md +0 -732
- package/.claude/commands/prd.md +0 -130
- package/.claude/commands/project-status.md +0 -82
- package/.claude/commands/publish.md +0 -138
- package/.claude/commands/recap.md +0 -69
- package/.claude/commands/restore.md +0 -64
- package/.claude/commands/review-squad.md +0 -152
- package/.claude/commands/save.md +0 -24
- package/.claude/commands/stats.md +0 -19
- package/.claude/commands/swarm.md +0 -210
- package/.claude/commands/tool-builder.md +0 -39
- package/.claude/commands/ws-pull.md +0 -44
- package/.claude/hooks/_shared-memory.cjs +0 -305
- package/.claude/hooks/_utils.cjs +0 -108
- package/.claude/hooks/agent-failure-detector.cjs +0 -383
- package/.claude/hooks/agent-usage-logger.cjs +0 -204
- package/.claude/hooks/app-edit-guard.cjs +0 -494
- package/.claude/hooks/auto-learn.cjs +0 -304
- package/.claude/hooks/bash-guard.cjs +0 -272
- package/.claude/hooks/builder-mode-manager.cjs +0 -354
- package/.claude/hooks/bulk-activity-guard.cjs +0 -271
- package/.claude/hooks/context-watchdog.cjs +0 -230
- package/.claude/hooks/delegation-reminder.cjs +0 -465
- package/.claude/hooks/design-system-lint.cjs +0 -271
- package/.claude/hooks/post-scaffold-hook.cjs +0 -181
- package/.claude/hooks/prompt-guard.cjs +0 -354
- package/.claude/hooks/publish-template-guard.cjs +0 -147
- package/.claude/hooks/session-start.cjs +0 -35
- package/.claude/hooks/shared-memory-writer.cjs +0 -147
- package/.claude/hooks/skill-injector.cjs +0 -140
- package/.claude/hooks/skill-usage-logger.cjs +0 -258
- package/.claude/hooks/src-edit-guard.cjs +0 -240
- package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
- package/.claude/settings.json +0 -257
- package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
- package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
- package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
- package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
- package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
- package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
- package/.claude/skills/agent-structure/SKILL.md +0 -98
- package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
- package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
- package/.claude/skills/delegation-routing/SKILL.md +0 -202
- package/.claude/skills/frontend-design/SKILL.md +0 -254
- package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
- package/.claude/skills/hailer-api-client/SKILL.md +0 -518
- package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
- package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
- package/.claude/skills/hailer-design-system/SKILL.md +0 -235
- package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
- package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
- package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
- package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
- package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
- package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
- package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
- package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
- package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
- package/.claude/skills/integration-patterns/SKILL.md +0 -421
- package/.claude/skills/json-only-output/SKILL.md +0 -72
- package/.claude/skills/lsp-setup/SKILL.md +0 -160
- package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
- package/.claude/skills/optional-parameters/SKILL.md +0 -72
- package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
- package/.claude/skills/testing-patterns/SKILL.md +0 -630
- package/.claude/skills/tool-builder/SKILL.md +0 -250
- package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
- package/.claude/skills/tool-response-verification/SKILL.md +0 -92
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
- package/.mcp.json +0 -13
- package/.opencode/agent/agent-ada-skill-builder.md +0 -35
- package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
- package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
- package/.opencode/agent/agent-builder-agent-creator.md +0 -39
- package/.opencode/agent/agent-code-simplifier.md +0 -31
- package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
- package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
- package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
- package/.opencode/agent/agent-helga-workflow-config.md +0 -203
- package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
- package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
- package/.opencode/agent/agent-ivan-monolith.md +0 -46
- package/.opencode/agent/agent-kenji-data-reader.md +0 -53
- package/.opencode/agent/agent-lars-code-inspector.md +0 -28
- package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
- package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
- package/.opencode/agent/agent-marketplace-publisher.md +0 -44
- package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
- package/.opencode/agent/agent-permissions-handler.md +0 -50
- package/.opencode/agent/agent-simple-writer.md +0 -45
- package/.opencode/agent/agent-svetlana-code-review.md +0 -39
- package/.opencode/agent/agent-tanya-test-runner.md +0 -57
- package/.opencode/agent/agent-ui-designer.md +0 -56
- package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
- package/.opencode/agent/agent-web-search.md +0 -42
- package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
- package/.opencode/agent/agent-zara-zapier.md +0 -53
- package/.opencode/commands/app-squad.md +0 -135
- package/.opencode/commands/audit-squad.md +0 -158
- package/.opencode/commands/autoplan.md +0 -563
- package/.opencode/commands/cleanup-squad.md +0 -98
- package/.opencode/commands/config-squad.md +0 -106
- package/.opencode/commands/crud-squad.md +0 -87
- package/.opencode/commands/data-squad.md +0 -97
- package/.opencode/commands/debug-squad.md +0 -303
- package/.opencode/commands/doc-squad.md +0 -65
- package/.opencode/commands/handoff.md +0 -137
- package/.opencode/commands/health.md +0 -49
- package/.opencode/commands/help-agents.md +0 -151
- package/.opencode/commands/help-commands.md +0 -32
- package/.opencode/commands/help-faq.md +0 -29
- package/.opencode/commands/help-plugins.md +0 -28
- package/.opencode/commands/help-skills.md +0 -7
- package/.opencode/commands/help-tools.md +0 -40
- package/.opencode/commands/help.md +0 -28
- package/.opencode/commands/hotfix-squad.md +0 -112
- package/.opencode/commands/integration-squad.md +0 -82
- package/.opencode/commands/janitor-squad.md +0 -167
- package/.opencode/commands/learn-auto.md +0 -120
- package/.opencode/commands/learn.md +0 -120
- package/.opencode/commands/mcp-list.md +0 -27
- package/.opencode/commands/onboard-squad.md +0 -140
- package/.opencode/commands/plan-workspace.md +0 -732
- package/.opencode/commands/prd.md +0 -131
- package/.opencode/commands/project-status.md +0 -82
- package/.opencode/commands/publish.md +0 -138
- package/.opencode/commands/recap.md +0 -69
- package/.opencode/commands/restore.md +0 -64
- package/.opencode/commands/review-squad.md +0 -152
- package/.opencode/commands/save.md +0 -24
- package/.opencode/commands/stats.md +0 -19
- package/.opencode/commands/swarm.md +0 -210
- package/.opencode/commands/tool-builder.md +0 -39
- package/.opencode/commands/ws-pull.md +0 -44
- package/.opencode/opencode.json +0 -28
- package/SESSION-HANDOFF.md +0 -68
- package/inbox/2026-03-04-bot-config-patterns.md +0 -24
- package/scripts/postinstall.cjs +0 -64
- package/scripts/test-hal-tools.ts +0 -154
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weaviate HTTP Client
|
|
3
|
+
*
|
|
4
|
+
* Lightweight client that talks to Weaviate's GraphQL and REST APIs directly.
|
|
5
|
+
* No heavy SDK dependency — just fetch calls with proper auth headers.
|
|
6
|
+
*/
|
|
7
|
+
export interface WeaviateClientConfig {
|
|
8
|
+
host: string;
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface WhereFilter {
|
|
12
|
+
path?: string[];
|
|
13
|
+
operator: string;
|
|
14
|
+
valueText?: string;
|
|
15
|
+
valueInt?: number;
|
|
16
|
+
valueBoolean?: boolean;
|
|
17
|
+
valueDate?: string;
|
|
18
|
+
operands?: WhereFilter[];
|
|
19
|
+
}
|
|
20
|
+
export interface WeaviateObject {
|
|
21
|
+
[key: string]: any;
|
|
22
|
+
}
|
|
23
|
+
export interface WeaviateQueryResult {
|
|
24
|
+
objects: WeaviateObject[];
|
|
25
|
+
totalCount?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface WeaviateInsertResult {
|
|
28
|
+
uuid: string;
|
|
29
|
+
}
|
|
30
|
+
export interface WeaviateDeleteResult {
|
|
31
|
+
matches: number;
|
|
32
|
+
successful: number;
|
|
33
|
+
}
|
|
34
|
+
export interface WeaviateBatchInsertResult {
|
|
35
|
+
inserted: number;
|
|
36
|
+
errors: number;
|
|
37
|
+
errorDetails: Array<{
|
|
38
|
+
index: number;
|
|
39
|
+
error: string;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
export interface WeaviateExportResult {
|
|
43
|
+
objects: WeaviateObject[];
|
|
44
|
+
totalCount: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Thrown when Weaviate returns 401/403 — signals invalid server API key.
|
|
48
|
+
*/
|
|
49
|
+
export declare class WeaviateAuthError extends Error {
|
|
50
|
+
status: number;
|
|
51
|
+
constructor(status: number);
|
|
52
|
+
}
|
|
53
|
+
export declare class WeaviateClient {
|
|
54
|
+
private host;
|
|
55
|
+
private headers;
|
|
56
|
+
private static readonly SCHEMA_TTL_MS;
|
|
57
|
+
private schemaCache;
|
|
58
|
+
constructor(config: WeaviateClientConfig);
|
|
59
|
+
/**
|
|
60
|
+
* Shared fetch with auth-error and response-error handling.
|
|
61
|
+
* All HTTP calls go through this to avoid repeating 401/403 checks.
|
|
62
|
+
*/
|
|
63
|
+
private _fetch;
|
|
64
|
+
/**
|
|
65
|
+
* Execute a GraphQL query against Weaviate
|
|
66
|
+
*/
|
|
67
|
+
graphql(query: string, variables?: Record<string, any>): Promise<any>;
|
|
68
|
+
/**
|
|
69
|
+
* Hybrid search (BM25 + vector) — the main query tool
|
|
70
|
+
*/
|
|
71
|
+
hybridSearch(collection: string, query: string, targetProperties: string[], limit?: number, where?: WhereFilter): Promise<WeaviateQueryResult>;
|
|
72
|
+
/**
|
|
73
|
+
* Pure vector (nearText) search — for dedup/similarity detection
|
|
74
|
+
*/
|
|
75
|
+
nearTextSearch(collection: string, query: string, limit?: number, targetProperties?: string[]): Promise<WeaviateQueryResult>;
|
|
76
|
+
/**
|
|
77
|
+
* GraphQL Get with where filter only (no vector/BM25 search).
|
|
78
|
+
* Works on all collections including non-vectorized ones like VipunenConfig.
|
|
79
|
+
*/
|
|
80
|
+
filterQuery(collection: string, where: WhereFilter, properties?: string[], limit?: number): Promise<WeaviateQueryResult>;
|
|
81
|
+
/**
|
|
82
|
+
* Aggregate query grouped by a property
|
|
83
|
+
*/
|
|
84
|
+
aggregateGroupBy(collection: string, groupByProperty: string): Promise<Array<{
|
|
85
|
+
value: string;
|
|
86
|
+
count: number;
|
|
87
|
+
}>>;
|
|
88
|
+
/**
|
|
89
|
+
* Insert a single object into a collection
|
|
90
|
+
*/
|
|
91
|
+
insertObject(collection: string, properties: Record<string, any>): Promise<WeaviateInsertResult>;
|
|
92
|
+
/**
|
|
93
|
+
* Batch insert objects into a collection.
|
|
94
|
+
* Splits into batches and returns aggregate result.
|
|
95
|
+
*/
|
|
96
|
+
batchInsert(collection: string, objects: Array<Record<string, any>>, batchSize?: number): Promise<WeaviateBatchInsertResult>;
|
|
97
|
+
/**
|
|
98
|
+
* Batch delete objects matching a where filter
|
|
99
|
+
*/
|
|
100
|
+
batchDelete(collection: string, where: any): Promise<WeaviateDeleteResult>;
|
|
101
|
+
/**
|
|
102
|
+
* Delete objects where a property equals a specific value
|
|
103
|
+
*/
|
|
104
|
+
deleteByProperty(collection: string, property: string, value: string): Promise<WeaviateDeleteResult>;
|
|
105
|
+
/**
|
|
106
|
+
* Delete objects matching multiple AND conditions.
|
|
107
|
+
* Supports mixed types: string values use valueText, numbers use valueInt.
|
|
108
|
+
*/
|
|
109
|
+
deleteByMultipleProperties(collection: string, conditions: Array<{
|
|
110
|
+
property: string;
|
|
111
|
+
value: string | number;
|
|
112
|
+
}>): Promise<WeaviateDeleteResult>;
|
|
113
|
+
/**
|
|
114
|
+
* Get collection schema (for discovering properties). Cached per collection.
|
|
115
|
+
*/
|
|
116
|
+
getCollectionSchema(collection: string): Promise<any>;
|
|
117
|
+
/**
|
|
118
|
+
* Export all objects from a collection using cursor-based pagination.
|
|
119
|
+
*/
|
|
120
|
+
exportCollection(collection: string, properties?: string[], pageSize?: number): Promise<WeaviateExportResult>;
|
|
121
|
+
/**
|
|
122
|
+
* Create a collection with a given schema. Idempotent — returns existing if already present.
|
|
123
|
+
*/
|
|
124
|
+
createCollection(collection: string, schema: Record<string, any>): Promise<{
|
|
125
|
+
created: boolean;
|
|
126
|
+
objectCount: number;
|
|
127
|
+
}>;
|
|
128
|
+
/**
|
|
129
|
+
* Count objects in a collection. Validates collection name before querying.
|
|
130
|
+
*/
|
|
131
|
+
countCollection(collection: string): Promise<number>;
|
|
132
|
+
/**
|
|
133
|
+
* Delete a collection. Idempotent — no error if it doesn't exist.
|
|
134
|
+
*/
|
|
135
|
+
deleteCollection(collection: string): Promise<{
|
|
136
|
+
deleted: boolean;
|
|
137
|
+
}>;
|
|
138
|
+
/**
|
|
139
|
+
* Serialize a WhereFilter into Weaviate GraphQL inline syntax.
|
|
140
|
+
* Validates operators and paths to prevent injection.
|
|
141
|
+
*/
|
|
142
|
+
private serializeWhereFilter;
|
|
143
|
+
/**
|
|
144
|
+
* Escape special characters for GraphQL string literals
|
|
145
|
+
*/
|
|
146
|
+
private escapeGraphQL;
|
|
147
|
+
private validateCollectionName;
|
|
148
|
+
private validatePropertyNames;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Weaviate HTTP Client
|
|
4
|
+
*
|
|
5
|
+
* Lightweight client that talks to Weaviate's GraphQL and REST APIs directly.
|
|
6
|
+
* No heavy SDK dependency — just fetch calls with proper auth headers.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.WeaviateClient = exports.WeaviateAuthError = void 0;
|
|
10
|
+
const logger_1 = require("../../lib/logger");
|
|
11
|
+
const logger = (0, logger_1.createLogger)({ component: 'weaviate-client' });
|
|
12
|
+
// Validation patterns for GraphQL injection prevention
|
|
13
|
+
const COLLECTION_PATTERN = /^[A-Z][a-zA-Z0-9]*$/;
|
|
14
|
+
const PROPERTY_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
15
|
+
const VALID_WHERE_OPERATORS = new Set([
|
|
16
|
+
'Equal', 'NotEqual', 'GreaterThan', 'GreaterThanEqual',
|
|
17
|
+
'LessThan', 'LessThanEqual', 'Like', 'WithinGeoRange',
|
|
18
|
+
'IsNull', 'ContainsAny', 'ContainsAll', 'And', 'Or',
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* Thrown when Weaviate returns 401/403 — signals invalid server API key.
|
|
22
|
+
*/
|
|
23
|
+
class WeaviateAuthError extends Error {
|
|
24
|
+
status;
|
|
25
|
+
constructor(status) {
|
|
26
|
+
super(`Weaviate authentication failed (${status})`);
|
|
27
|
+
this.status = status;
|
|
28
|
+
this.name = 'WeaviateAuthError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.WeaviateAuthError = WeaviateAuthError;
|
|
32
|
+
class WeaviateClient {
|
|
33
|
+
host;
|
|
34
|
+
headers;
|
|
35
|
+
static SCHEMA_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
36
|
+
schemaCache = new Map();
|
|
37
|
+
constructor(config) {
|
|
38
|
+
// Remove trailing slash
|
|
39
|
+
this.host = config.host.replace(/\/$/, '');
|
|
40
|
+
this.headers = {
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
};
|
|
43
|
+
if (config.apiKey) {
|
|
44
|
+
this.headers['Authorization'] = `Bearer ${config.apiKey}`;
|
|
45
|
+
}
|
|
46
|
+
logger.debug('Weaviate client initialized', { host: this.host, hasApiKey: !!config.apiKey });
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Shared fetch with auth-error and response-error handling.
|
|
50
|
+
* All HTTP calls go through this to avoid repeating 401/403 checks.
|
|
51
|
+
*/
|
|
52
|
+
async _fetch(url, init) {
|
|
53
|
+
let response;
|
|
54
|
+
try {
|
|
55
|
+
response = await fetch(url, { ...init, headers: { ...this.headers, ...init.headers } });
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new Error(`Weaviate request to ${url} failed: ${err.message}`);
|
|
59
|
+
}
|
|
60
|
+
if (response.status === 401 || response.status === 403) {
|
|
61
|
+
throw new WeaviateAuthError(response.status);
|
|
62
|
+
}
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
const text = await response.text();
|
|
65
|
+
throw new Error(`Weaviate error (${response.status}): ${text}`);
|
|
66
|
+
}
|
|
67
|
+
return response;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Execute a GraphQL query against Weaviate
|
|
71
|
+
*/
|
|
72
|
+
async graphql(query, variables) {
|
|
73
|
+
const url = `${this.host}/v1/graphql`;
|
|
74
|
+
const body = { query };
|
|
75
|
+
if (variables) {
|
|
76
|
+
body.variables = variables;
|
|
77
|
+
}
|
|
78
|
+
const response = await this._fetch(url, {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
body: JSON.stringify(body),
|
|
81
|
+
});
|
|
82
|
+
const result = await response.json();
|
|
83
|
+
if (result.errors && result.errors.length > 0) {
|
|
84
|
+
throw new Error(`Weaviate GraphQL error: ${result.errors.map((e) => e.message).join(', ')}`);
|
|
85
|
+
}
|
|
86
|
+
return result.data;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Hybrid search (BM25 + vector) — the main query tool
|
|
90
|
+
*/
|
|
91
|
+
async hybridSearch(collection, query, targetProperties, limit = 5, where) {
|
|
92
|
+
this.validateCollectionName(collection);
|
|
93
|
+
this.validatePropertyNames(targetProperties);
|
|
94
|
+
const returnFields = targetProperties.join('\n ');
|
|
95
|
+
const safeLimit = Math.floor(Math.max(1, Math.min(25, limit)));
|
|
96
|
+
const whereClause = where ? `\n where: ${this.serializeWhereFilter(where)}` : '';
|
|
97
|
+
const gql = `{
|
|
98
|
+
Get {
|
|
99
|
+
${collection}(
|
|
100
|
+
hybrid: {
|
|
101
|
+
query: "${this.escapeGraphQL(query)}"
|
|
102
|
+
properties: [${targetProperties.map(p => `"${p}"`).join(', ')}]
|
|
103
|
+
alpha: 0.5
|
|
104
|
+
}
|
|
105
|
+
limit: ${safeLimit}${whereClause}
|
|
106
|
+
) {
|
|
107
|
+
${returnFields}
|
|
108
|
+
_additional {
|
|
109
|
+
score
|
|
110
|
+
id
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}`;
|
|
115
|
+
const data = await this.graphql(gql);
|
|
116
|
+
const objects = data?.Get?.[collection] || [];
|
|
117
|
+
return {
|
|
118
|
+
objects: objects.map((obj) => {
|
|
119
|
+
const { _additional, ...props } = obj;
|
|
120
|
+
return {
|
|
121
|
+
...props,
|
|
122
|
+
_score: parseFloat(_additional?.score || '0'),
|
|
123
|
+
_id: _additional?.id,
|
|
124
|
+
};
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Pure vector (nearText) search — for dedup/similarity detection
|
|
130
|
+
*/
|
|
131
|
+
async nearTextSearch(collection, query, limit = 5, targetProperties) {
|
|
132
|
+
this.validateCollectionName(collection);
|
|
133
|
+
// Use provided properties, or fall back to cached schema discovery
|
|
134
|
+
let propNames;
|
|
135
|
+
if (targetProperties) {
|
|
136
|
+
propNames = targetProperties;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const schema = await this.getCollectionSchema(collection);
|
|
140
|
+
propNames = schema.properties?.map((p) => p.name) || ['title', 'content'];
|
|
141
|
+
}
|
|
142
|
+
this.validatePropertyNames(propNames);
|
|
143
|
+
const returnFields = propNames.join('\n ');
|
|
144
|
+
const safeLimit = Math.floor(Math.max(1, Math.min(25, limit)));
|
|
145
|
+
const gql = `{
|
|
146
|
+
Get {
|
|
147
|
+
${collection}(
|
|
148
|
+
nearText: {
|
|
149
|
+
concepts: ["${this.escapeGraphQL(query)}"]
|
|
150
|
+
}
|
|
151
|
+
limit: ${safeLimit}
|
|
152
|
+
) {
|
|
153
|
+
${returnFields}
|
|
154
|
+
_additional {
|
|
155
|
+
certainty
|
|
156
|
+
distance
|
|
157
|
+
id
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}`;
|
|
162
|
+
const data = await this.graphql(gql);
|
|
163
|
+
const objects = data?.Get?.[collection] || [];
|
|
164
|
+
return {
|
|
165
|
+
objects: objects.map((obj) => {
|
|
166
|
+
const { _additional, ...rest } = obj;
|
|
167
|
+
return {
|
|
168
|
+
...rest,
|
|
169
|
+
_certainty: parseFloat(_additional?.certainty || '0'),
|
|
170
|
+
_distance: parseFloat(_additional?.distance || '0'),
|
|
171
|
+
_id: _additional?.id,
|
|
172
|
+
};
|
|
173
|
+
}),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* GraphQL Get with where filter only (no vector/BM25 search).
|
|
178
|
+
* Works on all collections including non-vectorized ones like VipunenConfig.
|
|
179
|
+
*/
|
|
180
|
+
async filterQuery(collection, where, properties, limit = 25) {
|
|
181
|
+
this.validateCollectionName(collection);
|
|
182
|
+
let propNames;
|
|
183
|
+
if (properties && properties.length > 0) {
|
|
184
|
+
propNames = properties;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
const schema = await this.getCollectionSchema(collection);
|
|
188
|
+
propNames = schema.properties?.map((p) => p.name) || ['title', 'content'];
|
|
189
|
+
}
|
|
190
|
+
this.validatePropertyNames(propNames);
|
|
191
|
+
const returnFields = propNames.join('\n ');
|
|
192
|
+
const safeLimit = Math.floor(Math.max(1, Math.min(100, limit)));
|
|
193
|
+
const whereClause = this.serializeWhereFilter(where);
|
|
194
|
+
const gql = `{
|
|
195
|
+
Get {
|
|
196
|
+
${collection}(
|
|
197
|
+
where: ${whereClause}
|
|
198
|
+
limit: ${safeLimit}
|
|
199
|
+
) {
|
|
200
|
+
${returnFields}
|
|
201
|
+
_additional {
|
|
202
|
+
id
|
|
203
|
+
creationTimeUnix
|
|
204
|
+
lastUpdateTimeUnix
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}`;
|
|
209
|
+
const data = await this.graphql(gql);
|
|
210
|
+
const objects = data?.Get?.[collection] || [];
|
|
211
|
+
return {
|
|
212
|
+
objects: objects.map((obj) => {
|
|
213
|
+
const { _additional, ...rest } = obj;
|
|
214
|
+
return {
|
|
215
|
+
...rest,
|
|
216
|
+
_id: _additional?.id,
|
|
217
|
+
_creationTimeUnix: _additional?.creationTimeUnix,
|
|
218
|
+
_lastUpdateTimeUnix: _additional?.lastUpdateTimeUnix,
|
|
219
|
+
};
|
|
220
|
+
}),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Aggregate query grouped by a property
|
|
225
|
+
*/
|
|
226
|
+
async aggregateGroupBy(collection, groupByProperty) {
|
|
227
|
+
this.validateCollectionName(collection);
|
|
228
|
+
this.validatePropertyNames([groupByProperty]);
|
|
229
|
+
const gql = `{
|
|
230
|
+
Aggregate {
|
|
231
|
+
${collection}(
|
|
232
|
+
groupBy: ["${groupByProperty}"]
|
|
233
|
+
) {
|
|
234
|
+
groupedBy {
|
|
235
|
+
value
|
|
236
|
+
}
|
|
237
|
+
meta {
|
|
238
|
+
count
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}`;
|
|
243
|
+
const data = await this.graphql(gql);
|
|
244
|
+
const groups = data?.Aggregate?.[collection] || [];
|
|
245
|
+
return groups.map((g) => ({
|
|
246
|
+
value: g.groupedBy?.value || '',
|
|
247
|
+
count: g.meta?.count || 0,
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Insert a single object into a collection
|
|
252
|
+
*/
|
|
253
|
+
async insertObject(collection, properties) {
|
|
254
|
+
this.validateCollectionName(collection);
|
|
255
|
+
const response = await this._fetch(`${this.host}/v1/objects`, {
|
|
256
|
+
method: 'POST',
|
|
257
|
+
body: JSON.stringify({ class: collection, properties }),
|
|
258
|
+
});
|
|
259
|
+
const result = await response.json();
|
|
260
|
+
if (!result?.id) {
|
|
261
|
+
throw new Error(`Weaviate insert response missing id field: ${JSON.stringify(result)}`);
|
|
262
|
+
}
|
|
263
|
+
return { uuid: result.id };
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Batch insert objects into a collection.
|
|
267
|
+
* Splits into batches and returns aggregate result.
|
|
268
|
+
*/
|
|
269
|
+
async batchInsert(collection, objects, batchSize = 20) {
|
|
270
|
+
this.validateCollectionName(collection);
|
|
271
|
+
const safeBatchSize = Math.floor(Math.max(1, Math.min(100, batchSize)));
|
|
272
|
+
let inserted = 0;
|
|
273
|
+
let errors = 0;
|
|
274
|
+
const errorDetails = [];
|
|
275
|
+
for (let i = 0; i < objects.length; i += safeBatchSize) {
|
|
276
|
+
const batch = objects.slice(i, i + safeBatchSize);
|
|
277
|
+
const weaviateObjects = batch.map(props => ({
|
|
278
|
+
class: collection,
|
|
279
|
+
properties: props,
|
|
280
|
+
}));
|
|
281
|
+
const response = await this._fetch(`${this.host}/v1/batch/objects`, {
|
|
282
|
+
method: 'POST',
|
|
283
|
+
body: JSON.stringify({ objects: weaviateObjects }),
|
|
284
|
+
});
|
|
285
|
+
const raw = await response.json();
|
|
286
|
+
const results = Array.isArray(raw) ? raw : [];
|
|
287
|
+
if (!Array.isArray(raw)) {
|
|
288
|
+
// Log warning but don't crash — treat as all-errors batch
|
|
289
|
+
logger.warn('batchInsert: unexpected non-array response from Weaviate batch API', { batch: i });
|
|
290
|
+
errors += batch.length;
|
|
291
|
+
for (let j = 0; j < batch.length; j++) {
|
|
292
|
+
errorDetails.push({ index: i + j, error: 'Unexpected non-array response from Weaviate batch API' });
|
|
293
|
+
}
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
for (let j = 0; j < results.length; j++) {
|
|
297
|
+
const r = results[j];
|
|
298
|
+
if (r.result?.errors?.error?.length > 0) {
|
|
299
|
+
errors++;
|
|
300
|
+
errorDetails.push({
|
|
301
|
+
index: i + j,
|
|
302
|
+
error: r.result.errors.error.map((e) => e.message).join(', '),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
inserted++;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return { inserted, errors, errorDetails };
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Batch delete objects matching a where filter
|
|
314
|
+
*/
|
|
315
|
+
async batchDelete(collection, where) {
|
|
316
|
+
this.validateCollectionName(collection);
|
|
317
|
+
const response = await this._fetch(`${this.host}/v1/batch/objects`, {
|
|
318
|
+
method: 'DELETE',
|
|
319
|
+
body: JSON.stringify({ match: { class: collection, where }, output: 'minimal' }),
|
|
320
|
+
});
|
|
321
|
+
const result = await response.json();
|
|
322
|
+
return {
|
|
323
|
+
matches: result.results?.matches || 0,
|
|
324
|
+
successful: result.results?.successful || 0,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Delete objects where a property equals a specific value
|
|
329
|
+
*/
|
|
330
|
+
async deleteByProperty(collection, property, value) {
|
|
331
|
+
return this.batchDelete(collection, {
|
|
332
|
+
operator: 'Equal',
|
|
333
|
+
path: [property],
|
|
334
|
+
valueText: value,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Delete objects matching multiple AND conditions.
|
|
339
|
+
* Supports mixed types: string values use valueText, numbers use valueInt.
|
|
340
|
+
*/
|
|
341
|
+
async deleteByMultipleProperties(collection, conditions) {
|
|
342
|
+
const operands = conditions.map(c => ({
|
|
343
|
+
operator: 'Equal',
|
|
344
|
+
path: [c.property],
|
|
345
|
+
...(typeof c.value === 'number'
|
|
346
|
+
? { valueInt: c.value }
|
|
347
|
+
: { valueText: c.value }),
|
|
348
|
+
}));
|
|
349
|
+
return this.batchDelete(collection, {
|
|
350
|
+
operator: 'And',
|
|
351
|
+
operands,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Get collection schema (for discovering properties). Cached per collection.
|
|
356
|
+
*/
|
|
357
|
+
async getCollectionSchema(collection) {
|
|
358
|
+
this.validateCollectionName(collection);
|
|
359
|
+
const cached = this.schemaCache.get(collection);
|
|
360
|
+
if (cached && cached.expiresAt > Date.now())
|
|
361
|
+
return cached.schema;
|
|
362
|
+
const response = await this._fetch(`${this.host}/v1/schema/${collection}`, { method: 'GET' });
|
|
363
|
+
const schema = await response.json();
|
|
364
|
+
this.schemaCache.set(collection, { schema, expiresAt: Date.now() + WeaviateClient.SCHEMA_TTL_MS });
|
|
365
|
+
return schema;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Export all objects from a collection using cursor-based pagination.
|
|
369
|
+
*/
|
|
370
|
+
async exportCollection(collection, properties, pageSize = 100) {
|
|
371
|
+
this.validateCollectionName(collection);
|
|
372
|
+
let propNames;
|
|
373
|
+
if (properties && properties.length > 0) {
|
|
374
|
+
propNames = properties;
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
const schema = await this.getCollectionSchema(collection);
|
|
378
|
+
propNames = schema.properties?.map((p) => p.name) || ['title', 'content'];
|
|
379
|
+
}
|
|
380
|
+
this.validatePropertyNames(propNames);
|
|
381
|
+
const returnFields = propNames.join('\n ');
|
|
382
|
+
const safePageSize = Math.floor(Math.max(1, Math.min(500, pageSize)));
|
|
383
|
+
const allObjects = [];
|
|
384
|
+
let cursor = null;
|
|
385
|
+
while (true) {
|
|
386
|
+
const afterClause = cursor ? `after: "${this.escapeGraphQL(cursor)}"` : '';
|
|
387
|
+
const gql = `{
|
|
388
|
+
Get {
|
|
389
|
+
${collection}(
|
|
390
|
+
limit: ${safePageSize}
|
|
391
|
+
${afterClause}
|
|
392
|
+
) {
|
|
393
|
+
${returnFields}
|
|
394
|
+
_additional {
|
|
395
|
+
id
|
|
396
|
+
creationTimeUnix
|
|
397
|
+
lastUpdateTimeUnix
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}`;
|
|
402
|
+
const data = await this.graphql(gql);
|
|
403
|
+
const objects = data?.Get?.[collection] || [];
|
|
404
|
+
if (objects.length === 0)
|
|
405
|
+
break;
|
|
406
|
+
for (const obj of objects) {
|
|
407
|
+
const { _additional, ...rest } = obj;
|
|
408
|
+
allObjects.push({
|
|
409
|
+
...rest,
|
|
410
|
+
_id: _additional?.id,
|
|
411
|
+
_creationTimeUnix: _additional?.creationTimeUnix,
|
|
412
|
+
_lastUpdateTimeUnix: _additional?.lastUpdateTimeUnix,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
const lastId = objects[objects.length - 1]?._additional?.id;
|
|
416
|
+
if (!lastId)
|
|
417
|
+
break; // no cursor available — cannot paginate further
|
|
418
|
+
cursor = lastId;
|
|
419
|
+
if (objects.length < safePageSize)
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
return { objects: allObjects, totalCount: allObjects.length };
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Create a collection with a given schema. Idempotent — returns existing if already present.
|
|
426
|
+
*/
|
|
427
|
+
async createCollection(collection, schema) {
|
|
428
|
+
this.validateCollectionName(collection);
|
|
429
|
+
// Check if already exists
|
|
430
|
+
try {
|
|
431
|
+
await this._fetch(`${this.host}/v1/schema/${collection}`, { method: 'GET' });
|
|
432
|
+
// Collection exists — count objects
|
|
433
|
+
const countData = await this.graphql(`{ Aggregate { ${collection} { meta { count } } } }`);
|
|
434
|
+
const count = countData?.Aggregate?.[collection]?.[0]?.meta?.count || 0;
|
|
435
|
+
return { created: false, objectCount: count };
|
|
436
|
+
}
|
|
437
|
+
catch (err) {
|
|
438
|
+
if (err instanceof WeaviateAuthError)
|
|
439
|
+
throw err;
|
|
440
|
+
if (err instanceof Error && err.message.includes('(404)')) {
|
|
441
|
+
// Collection doesn't exist — proceed to create
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
throw err; // network error or unexpected status
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const fullSchema = { class: collection, ...schema };
|
|
448
|
+
await this._fetch(`${this.host}/v1/schema`, {
|
|
449
|
+
method: 'POST',
|
|
450
|
+
body: JSON.stringify(fullSchema),
|
|
451
|
+
});
|
|
452
|
+
return { created: true, objectCount: 0 };
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Count objects in a collection. Validates collection name before querying.
|
|
456
|
+
*/
|
|
457
|
+
async countCollection(collection) {
|
|
458
|
+
this.validateCollectionName(collection);
|
|
459
|
+
const data = await this.graphql(`{ Aggregate { ${collection} { meta { count } } } }`);
|
|
460
|
+
return data?.Aggregate?.[collection]?.[0]?.meta?.count || 0;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Delete a collection. Idempotent — no error if it doesn't exist.
|
|
464
|
+
*/
|
|
465
|
+
async deleteCollection(collection) {
|
|
466
|
+
this.validateCollectionName(collection);
|
|
467
|
+
try {
|
|
468
|
+
await this._fetch(`${this.host}/v1/schema/${collection}`, { method: 'DELETE' });
|
|
469
|
+
return { deleted: true };
|
|
470
|
+
}
|
|
471
|
+
catch (err) {
|
|
472
|
+
if (err instanceof Error && err.message.includes('404')) {
|
|
473
|
+
return { deleted: false };
|
|
474
|
+
}
|
|
475
|
+
throw err;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Serialize a WhereFilter into Weaviate GraphQL inline syntax.
|
|
480
|
+
* Validates operators and paths to prevent injection.
|
|
481
|
+
*/
|
|
482
|
+
serializeWhereFilter(filter) {
|
|
483
|
+
if (!VALID_WHERE_OPERATORS.has(filter.operator)) {
|
|
484
|
+
throw new Error(`Invalid where operator: "${filter.operator}" — allowed: ${[...VALID_WHERE_OPERATORS].join(', ')}`);
|
|
485
|
+
}
|
|
486
|
+
const parts = [];
|
|
487
|
+
parts.push(`operator: ${filter.operator}`);
|
|
488
|
+
if (filter.path) {
|
|
489
|
+
this.validatePropertyNames(filter.path);
|
|
490
|
+
parts.push(`path: [${filter.path.map(p => `"${p}"`).join(', ')}]`);
|
|
491
|
+
}
|
|
492
|
+
if (filter.valueText !== undefined) {
|
|
493
|
+
parts.push(`valueText: "${this.escapeGraphQL(filter.valueText)}"`);
|
|
494
|
+
}
|
|
495
|
+
if (filter.valueInt !== undefined) {
|
|
496
|
+
parts.push(`valueInt: ${Math.floor(filter.valueInt)}`);
|
|
497
|
+
}
|
|
498
|
+
if (filter.valueBoolean !== undefined) {
|
|
499
|
+
parts.push(`valueBoolean: ${filter.valueBoolean}`);
|
|
500
|
+
}
|
|
501
|
+
if (filter.valueDate !== undefined) {
|
|
502
|
+
parts.push(`valueDate: "${this.escapeGraphQL(filter.valueDate)}"`);
|
|
503
|
+
}
|
|
504
|
+
if (filter.operands) {
|
|
505
|
+
const serializedOperands = filter.operands.map(op => this.serializeWhereFilter(op));
|
|
506
|
+
parts.push(`operands: [${serializedOperands.join(', ')}]`);
|
|
507
|
+
}
|
|
508
|
+
return `{ ${parts.join(', ')} }`;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Escape special characters for GraphQL string literals
|
|
512
|
+
*/
|
|
513
|
+
escapeGraphQL(str) {
|
|
514
|
+
return str
|
|
515
|
+
.replace(/\\/g, '\\\\')
|
|
516
|
+
.replace(/"/g, '\\"')
|
|
517
|
+
.replace(/\n/g, '\\n')
|
|
518
|
+
.replace(/\r/g, '\\r')
|
|
519
|
+
.replace(/\t/g, '\\t');
|
|
520
|
+
}
|
|
521
|
+
validateCollectionName(name) {
|
|
522
|
+
if (!COLLECTION_PATTERN.test(name)) {
|
|
523
|
+
throw new Error(`Invalid collection name: "${name}" — must match ${COLLECTION_PATTERN}`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
validatePropertyNames(names) {
|
|
527
|
+
for (const name of names) {
|
|
528
|
+
if (!PROPERTY_PATTERN.test(name)) {
|
|
529
|
+
throw new Error(`Invalid property name: "${name}" — must match ${PROPERTY_PATTERN}`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
exports.WeaviateClient = WeaviateClient;
|
|
535
|
+
//# sourceMappingURL=client.js.map
|