@semiont/api-client 0.1.0-build.6
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 +85 -0
- package/dist/__tests__/client.test.d.ts +28 -0
- package/dist/__tests__/client.test.d.ts.map +1 -0
- package/dist/__tests__/client.test.js +567 -0
- package/dist/__tests__/client.test.js.map +1 -0
- package/dist/__tests__/sse-client.test.d.ts +7 -0
- package/dist/__tests__/sse-client.test.d.ts.map +1 -0
- package/dist/__tests__/sse-client.test.js +421 -0
- package/dist/__tests__/sse-client.test.js.map +1 -0
- package/dist/__tests__/sse-stream.test.d.ts +7 -0
- package/dist/__tests__/sse-stream.test.d.ts.map +1 -0
- package/dist/__tests__/sse-stream.test.js +394 -0
- package/dist/__tests__/sse-stream.test.js.map +1 -0
- package/dist/__tests__/svg-selectors.test.d.ts +5 -0
- package/dist/__tests__/svg-selectors.test.d.ts.map +1 -0
- package/dist/__tests__/svg-selectors.test.js +124 -0
- package/dist/__tests__/svg-selectors.test.js.map +1 -0
- package/dist/branded-types.d.ts +70 -0
- package/dist/branded-types.d.ts.map +1 -0
- package/dist/branded-types.js +62 -0
- package/dist/branded-types.js.map +1 -0
- package/dist/client.d.ts +243 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +460 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/mime-utils.d.ts +27 -0
- package/dist/mime-utils.d.ts.map +1 -0
- package/dist/mime-utils.js +49 -0
- package/dist/mime-utils.js.map +1 -0
- package/dist/sse/index.d.ts +343 -0
- package/dist/sse/index.d.ts.map +1 -0
- package/dist/sse/index.js +404 -0
- package/dist/sse/index.js.map +1 -0
- package/dist/sse/stream.d.ts +58 -0
- package/dist/sse/stream.d.ts.map +1 -0
- package/dist/sse/stream.js +187 -0
- package/dist/sse/stream.js.map +1 -0
- package/dist/sse/types.d.ts +295 -0
- package/dist/sse/types.d.ts.map +1 -0
- package/dist/sse/types.js +10 -0
- package/dist/sse/types.js.map +1 -0
- package/dist/types.d.ts +3177 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/annotations.d.ts +191 -0
- package/dist/utils/annotations.d.ts.map +1 -0
- package/dist/utils/annotations.js +404 -0
- package/dist/utils/annotations.js.map +1 -0
- package/dist/utils/events.d.ts +74 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/events.js +329 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +28 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/locales.d.ts +31 -0
- package/dist/utils/locales.d.ts.map +1 -0
- package/dist/utils/locales.js +83 -0
- package/dist/utils/locales.js.map +1 -0
- package/dist/utils/resources.d.ts +34 -0
- package/dist/utils/resources.d.ts.map +1 -0
- package/dist/utils/resources.js +63 -0
- package/dist/utils/resources.js.map +1 -0
- package/dist/utils/validation.d.ts +57 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +89 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# @semiont/api-client
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [Semiont](https://github.com/The-AI-Alliance/semiont) - a knowledge management system for semantic annotations, AI-powered entity detection, and collaborative document analysis.
|
|
4
|
+
|
|
5
|
+
## What is Semiont?
|
|
6
|
+
|
|
7
|
+
Semiont lets you:
|
|
8
|
+
|
|
9
|
+
- **Store and manage documents** (text, markdown, code)
|
|
10
|
+
- **Create semantic annotations** using W3C Web Annotation standard
|
|
11
|
+
- **Link and reference** between documents
|
|
12
|
+
- **Track provenance** with event sourcing
|
|
13
|
+
- **Collaborate in real-time** via SSE streams
|
|
14
|
+
- **Detect entities** using AI (people, organizations, concepts)
|
|
15
|
+
- **Retrieve context** for LLMs via graph traversal
|
|
16
|
+
- **Generate new documents** from annotations with AI
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @semiont/api-client
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Prerequisites**: Semiont backend running. See [Running the Backend](../../apps/backend/README.md#quick-start) for setup.
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { SemiontApiClient, baseUrl, email, resourceUri } from '@semiont/api-client';
|
|
30
|
+
|
|
31
|
+
const client = new SemiontApiClient({
|
|
32
|
+
baseUrl: baseUrl('http://localhost:4000'),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Authenticate (local development mode)
|
|
36
|
+
await client.authenticateLocal(email('user@example.com'));
|
|
37
|
+
|
|
38
|
+
// Create a text document
|
|
39
|
+
const { resource } = await client.createResource({
|
|
40
|
+
name: 'My Document',
|
|
41
|
+
file: Buffer.from('The quick brown fox jumps over the lazy dog.'),
|
|
42
|
+
format: 'text/plain',
|
|
43
|
+
entityTypes: ['example']
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
console.log('Created resource:', resource['@id']);
|
|
47
|
+
|
|
48
|
+
// Detect entities with AI
|
|
49
|
+
const stream = client.sse.detectAnnotations(resourceUri(resource['@id']), {
|
|
50
|
+
entityTypes: ['Animal', 'Color']
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
stream.onProgress((p) => console.log(`Scanning: ${p.currentEntityType}`));
|
|
54
|
+
stream.onComplete((result) => console.log(`Found ${result.foundCount} entities`));
|
|
55
|
+
|
|
56
|
+
// Get annotations
|
|
57
|
+
const annotations = await client.getResourceAnnotations(resourceUri(resource['@id']));
|
|
58
|
+
console.log('Annotations:', annotations.annotations.length);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Documentation
|
|
62
|
+
|
|
63
|
+
📚 **[Usage Guide](./docs/Usage.md)** - Authentication, resources, annotations, SSE streaming
|
|
64
|
+
|
|
65
|
+
📖 **[API Reference](./docs/API-Reference.md)** - Complete method documentation
|
|
66
|
+
|
|
67
|
+
## Key Features
|
|
68
|
+
|
|
69
|
+
- **Type-safe** - Generated from OpenAPI spec with branded types
|
|
70
|
+
- **W3C compliant** - Web Annotation standard
|
|
71
|
+
- **Real-time** - SSE streaming for long operations
|
|
72
|
+
- **Framework-agnostic** - Works everywhere JavaScript runs
|
|
73
|
+
|
|
74
|
+
## Use Cases
|
|
75
|
+
|
|
76
|
+
- ✅ MCP servers and AI integrations
|
|
77
|
+
- ✅ Frontend applications (wrap with React hooks)
|
|
78
|
+
- ✅ CLI tools and automation scripts
|
|
79
|
+
- ✅ Third-party integrations
|
|
80
|
+
|
|
81
|
+
❌ **Not for backend internal code** - Use [`@semiont/core`](../core/) instead
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
Apache-2.0
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for SemiontApiClient with mocked HTTP
|
|
3
|
+
*/
|
|
4
|
+
export {};
|
|
5
|
+
/**
|
|
6
|
+
* Example usage documentation
|
|
7
|
+
*
|
|
8
|
+
* Archive a resource:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* await client.updateResource(resourceUri, { archived: true });
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Unarchive a resource:
|
|
14
|
+
* ```typescript
|
|
15
|
+
* await client.updateResource(resourceUri, { archived: false });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* List active resources only:
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const active = await client.listResources(20, false);
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* List archived resources only:
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const archived = await client.listResources(20, true);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
//# sourceMappingURL=client.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/client.test.ts"],"names":[],"mappings":"AAAA;;GAEG;;AA+pBH;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
|
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for SemiontApiClient with mocked HTTP
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const vitest_1 = require("vitest");
|
|
10
|
+
// Mock ky module
|
|
11
|
+
vitest_1.vi.mock('ky', () => ({
|
|
12
|
+
default: {
|
|
13
|
+
create: vitest_1.vi.fn(),
|
|
14
|
+
},
|
|
15
|
+
}));
|
|
16
|
+
const ky_1 = __importDefault(require("ky"));
|
|
17
|
+
const client_1 = require("../client");
|
|
18
|
+
const branded_types_1 = require("../branded-types");
|
|
19
|
+
(0, vitest_1.describe)('SemiontApiClient - Archive Operations', () => {
|
|
20
|
+
let client;
|
|
21
|
+
let mockKy;
|
|
22
|
+
const testBaseUrl = (0, branded_types_1.baseUrl)('http://localhost:4000');
|
|
23
|
+
const testResourceUri = `${testBaseUrl}/resources/test-resource-id`;
|
|
24
|
+
(0, vitest_1.beforeEach)(() => {
|
|
25
|
+
// Create mock ky instance with chainable methods
|
|
26
|
+
mockKy = {
|
|
27
|
+
get: vitest_1.vi.fn(),
|
|
28
|
+
post: vitest_1.vi.fn(),
|
|
29
|
+
patch: vitest_1.vi.fn(),
|
|
30
|
+
put: vitest_1.vi.fn(),
|
|
31
|
+
delete: vitest_1.vi.fn(),
|
|
32
|
+
};
|
|
33
|
+
// Mock ky.create to return our mock instance
|
|
34
|
+
vitest_1.vi.mocked(ky_1.default.create).mockReturnValue(mockKy);
|
|
35
|
+
client = new client_1.SemiontApiClient({
|
|
36
|
+
baseUrl: testBaseUrl,
|
|
37
|
+
timeout: 10000,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
(0, vitest_1.describe)('updateResource - archive operations', () => {
|
|
41
|
+
(0, vitest_1.test)('should archive a resource', async () => {
|
|
42
|
+
const mockResponse = {
|
|
43
|
+
resource: {
|
|
44
|
+
id: 'test-resource-id',
|
|
45
|
+
name: 'Test Resource',
|
|
46
|
+
archived: true,
|
|
47
|
+
entityTypes: [],
|
|
48
|
+
},
|
|
49
|
+
annotations: [],
|
|
50
|
+
entityReferences: [],
|
|
51
|
+
};
|
|
52
|
+
vitest_1.vi.mocked(mockKy.patch).mockReturnValue({
|
|
53
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
54
|
+
});
|
|
55
|
+
const result = await client.updateResource(testResourceUri, {
|
|
56
|
+
archived: true,
|
|
57
|
+
});
|
|
58
|
+
(0, vitest_1.expect)(result.resource.archived).toBe(true);
|
|
59
|
+
(0, vitest_1.expect)(mockKy.patch).toHaveBeenCalledWith(testResourceUri, vitest_1.expect.objectContaining({
|
|
60
|
+
json: { archived: true },
|
|
61
|
+
}));
|
|
62
|
+
});
|
|
63
|
+
(0, vitest_1.test)('should unarchive a resource', async () => {
|
|
64
|
+
const mockResponse = {
|
|
65
|
+
resource: {
|
|
66
|
+
id: 'test-resource-id',
|
|
67
|
+
name: 'Test Resource',
|
|
68
|
+
archived: false,
|
|
69
|
+
entityTypes: [],
|
|
70
|
+
},
|
|
71
|
+
annotations: [],
|
|
72
|
+
entityReferences: [],
|
|
73
|
+
};
|
|
74
|
+
vitest_1.vi.mocked(mockKy.patch).mockReturnValue({
|
|
75
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
76
|
+
});
|
|
77
|
+
const result = await client.updateResource(testResourceUri, {
|
|
78
|
+
archived: false,
|
|
79
|
+
});
|
|
80
|
+
(0, vitest_1.expect)(result.resource.archived).toBe(false);
|
|
81
|
+
(0, vitest_1.expect)(mockKy.patch).toHaveBeenCalledWith(testResourceUri, vitest_1.expect.objectContaining({
|
|
82
|
+
json: { archived: false },
|
|
83
|
+
}));
|
|
84
|
+
});
|
|
85
|
+
(0, vitest_1.test)('should update entity types and archive in single operation', async () => {
|
|
86
|
+
const mockResponse = {
|
|
87
|
+
resource: {
|
|
88
|
+
id: 'test-resource-id',
|
|
89
|
+
name: 'Test Resource',
|
|
90
|
+
archived: true,
|
|
91
|
+
entityTypes: ['article', 'draft'],
|
|
92
|
+
},
|
|
93
|
+
annotations: [],
|
|
94
|
+
entityReferences: [],
|
|
95
|
+
};
|
|
96
|
+
vitest_1.vi.mocked(mockKy.patch).mockReturnValue({
|
|
97
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
98
|
+
});
|
|
99
|
+
const result = await client.updateResource(testResourceUri, {
|
|
100
|
+
archived: true,
|
|
101
|
+
entityTypes: ['article', 'draft'],
|
|
102
|
+
});
|
|
103
|
+
(0, vitest_1.expect)(result.resource.archived).toBe(true);
|
|
104
|
+
(0, vitest_1.expect)(result.resource.entityTypes).toEqual(['article', 'draft']);
|
|
105
|
+
(0, vitest_1.expect)(mockKy.patch).toHaveBeenCalledWith(testResourceUri, vitest_1.expect.objectContaining({
|
|
106
|
+
json: {
|
|
107
|
+
archived: true,
|
|
108
|
+
entityTypes: ['article', 'draft'],
|
|
109
|
+
},
|
|
110
|
+
}));
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
(0, vitest_1.describe)('listResources - filter by archived', () => {
|
|
114
|
+
(0, vitest_1.test)('should list only active resources', async () => {
|
|
115
|
+
const mockResponse = {
|
|
116
|
+
resources: [
|
|
117
|
+
{ id: 'res1', name: 'Active 1', archived: false },
|
|
118
|
+
{ id: 'res2', name: 'Active 2', archived: false },
|
|
119
|
+
],
|
|
120
|
+
total: 2,
|
|
121
|
+
};
|
|
122
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
123
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
124
|
+
});
|
|
125
|
+
const result = await client.listResources(10, false);
|
|
126
|
+
(0, vitest_1.expect)(result.resources).toHaveLength(2);
|
|
127
|
+
(0, vitest_1.expect)(result.resources.every(r => !r.archived)).toBe(true);
|
|
128
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(`${testBaseUrl}/resources`, vitest_1.expect.objectContaining({
|
|
129
|
+
searchParams: vitest_1.expect.any(URLSearchParams),
|
|
130
|
+
}));
|
|
131
|
+
});
|
|
132
|
+
(0, vitest_1.test)('should list only archived resources', async () => {
|
|
133
|
+
const mockResponse = {
|
|
134
|
+
resources: [
|
|
135
|
+
{ id: 'res3', name: 'Archived 1', archived: true },
|
|
136
|
+
],
|
|
137
|
+
total: 1,
|
|
138
|
+
};
|
|
139
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
140
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
141
|
+
});
|
|
142
|
+
const result = await client.listResources(10, true);
|
|
143
|
+
(0, vitest_1.expect)(result.resources).toHaveLength(1);
|
|
144
|
+
(0, vitest_1.expect)(result.resources.every(r => r.archived)).toBe(true);
|
|
145
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(`${testBaseUrl}/resources`, vitest_1.expect.objectContaining({
|
|
146
|
+
searchParams: vitest_1.expect.any(URLSearchParams),
|
|
147
|
+
}));
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
(0, vitest_1.describe)('Entity Detection and Jobs', () => {
|
|
151
|
+
(0, vitest_1.test)('should get job status', async () => {
|
|
152
|
+
const mockResponse = {
|
|
153
|
+
jobId: 'job-123',
|
|
154
|
+
type: 'detection',
|
|
155
|
+
status: 'running',
|
|
156
|
+
userId: 'user-1',
|
|
157
|
+
created: '2024-01-01T00:00:00Z',
|
|
158
|
+
progress: {
|
|
159
|
+
current: 50,
|
|
160
|
+
total: 100,
|
|
161
|
+
message: 'Processing entities...',
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
165
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
166
|
+
});
|
|
167
|
+
const result = await client.getJobStatus((0, branded_types_1.jobId)('job-123'));
|
|
168
|
+
(0, vitest_1.expect)(result.status).toBe('running');
|
|
169
|
+
(0, vitest_1.expect)(result.jobId).toBe('job-123');
|
|
170
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(`${testBaseUrl}/api/jobs/job-123`);
|
|
171
|
+
});
|
|
172
|
+
(0, vitest_1.test)('should poll job until complete', async () => {
|
|
173
|
+
const responses = [
|
|
174
|
+
{ jobId: 'job-123', status: 'pending', type: 'detection', created: '2024-01-01T00:00:00Z', userId: 'user-1' },
|
|
175
|
+
{ jobId: 'job-123', status: 'running', type: 'detection', created: '2024-01-01T00:00:00Z', userId: 'user-1' },
|
|
176
|
+
{ jobId: 'job-123', status: 'complete', type: 'detection', created: '2024-01-01T00:00:00Z', userId: 'user-1', result: { detected: 5 } },
|
|
177
|
+
];
|
|
178
|
+
let callCount = 0;
|
|
179
|
+
vitest_1.vi.mocked(mockKy.get).mockImplementation(() => ({
|
|
180
|
+
json: vitest_1.vi.fn().mockResolvedValue(responses[callCount++]),
|
|
181
|
+
}));
|
|
182
|
+
const progressCalls = [];
|
|
183
|
+
const result = await client.pollJobUntilComplete((0, branded_types_1.jobId)('job-123'), {
|
|
184
|
+
interval: 10, // Fast polling for tests
|
|
185
|
+
onProgress: (status) => progressCalls.push(status),
|
|
186
|
+
});
|
|
187
|
+
(0, vitest_1.expect)(result.status).toBe('complete');
|
|
188
|
+
(0, vitest_1.expect)(progressCalls).toHaveLength(3);
|
|
189
|
+
(0, vitest_1.expect)(progressCalls[0].status).toBe('pending');
|
|
190
|
+
(0, vitest_1.expect)(progressCalls[1].status).toBe('running');
|
|
191
|
+
(0, vitest_1.expect)(progressCalls[2].status).toBe('complete');
|
|
192
|
+
});
|
|
193
|
+
(0, vitest_1.test)('should timeout when polling takes too long', async () => {
|
|
194
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
195
|
+
json: vitest_1.vi.fn().mockResolvedValue({
|
|
196
|
+
jobId: 'job-123',
|
|
197
|
+
status: 'running',
|
|
198
|
+
type: 'detection',
|
|
199
|
+
created: '2024-01-01T00:00:00Z',
|
|
200
|
+
userId: 'user-1',
|
|
201
|
+
}),
|
|
202
|
+
});
|
|
203
|
+
await (0, vitest_1.expect)(client.pollJobUntilComplete((0, branded_types_1.jobId)('job-123'), {
|
|
204
|
+
interval: 10,
|
|
205
|
+
timeout: 50, // Very short timeout for testing
|
|
206
|
+
})).rejects.toThrow('Job polling timeout after 50ms');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
(0, vitest_1.describe)('LLM Context Operations', () => {
|
|
210
|
+
(0, vitest_1.test)('should get resource LLM context with default options', async () => {
|
|
211
|
+
const mockResponse = {
|
|
212
|
+
mainResource: {
|
|
213
|
+
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
214
|
+
'@id': 'test-resource-id',
|
|
215
|
+
name: 'Test Resource',
|
|
216
|
+
content: 'Full content here',
|
|
217
|
+
},
|
|
218
|
+
relatedResources: [],
|
|
219
|
+
annotations: [],
|
|
220
|
+
graph: { nodes: [], edges: [] },
|
|
221
|
+
};
|
|
222
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
223
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
224
|
+
});
|
|
225
|
+
const result = await client.getResourceLLMContext(testResourceUri);
|
|
226
|
+
(0, vitest_1.expect)(result.mainResource.name).toBe('Test Resource');
|
|
227
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(`${testResourceUri}/llm-context`, vitest_1.expect.objectContaining({
|
|
228
|
+
searchParams: vitest_1.expect.any(URLSearchParams),
|
|
229
|
+
}));
|
|
230
|
+
});
|
|
231
|
+
(0, vitest_1.test)('should get resource LLM context with custom options', async () => {
|
|
232
|
+
const mockResponse = {
|
|
233
|
+
mainResource: {
|
|
234
|
+
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
235
|
+
'@id': 'test-resource-id',
|
|
236
|
+
name: 'Test Resource',
|
|
237
|
+
},
|
|
238
|
+
relatedResources: [],
|
|
239
|
+
annotations: [],
|
|
240
|
+
graph: { nodes: [], edges: [] },
|
|
241
|
+
};
|
|
242
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
243
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
244
|
+
});
|
|
245
|
+
const result = await client.getResourceLLMContext(testResourceUri, {
|
|
246
|
+
depth: 3,
|
|
247
|
+
maxResources: 15,
|
|
248
|
+
includeContent: true,
|
|
249
|
+
includeSummary: true,
|
|
250
|
+
});
|
|
251
|
+
(0, vitest_1.expect)(result.mainResource).toBeDefined();
|
|
252
|
+
const call = vitest_1.vi.mocked(mockKy.get).mock.calls[0];
|
|
253
|
+
const searchParams = call[1]?.searchParams;
|
|
254
|
+
(0, vitest_1.expect)(searchParams.get('depth')).toBe('3');
|
|
255
|
+
(0, vitest_1.expect)(searchParams.get('maxResources')).toBe('15');
|
|
256
|
+
(0, vitest_1.expect)(searchParams.get('includeContent')).toBe('true');
|
|
257
|
+
(0, vitest_1.expect)(searchParams.get('includeSummary')).toBe('true');
|
|
258
|
+
});
|
|
259
|
+
(0, vitest_1.test)('should get annotation LLM context with default options', async () => {
|
|
260
|
+
const annotationId = 'ann-123';
|
|
261
|
+
const mockResponse = {
|
|
262
|
+
annotation: {
|
|
263
|
+
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
264
|
+
type: 'Annotation',
|
|
265
|
+
id: 'ann-123',
|
|
266
|
+
motivation: 'highlighting',
|
|
267
|
+
target: { source: testResourceUri },
|
|
268
|
+
body: [],
|
|
269
|
+
},
|
|
270
|
+
sourceResource: {
|
|
271
|
+
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
272
|
+
'@id': 'source-id',
|
|
273
|
+
name: 'Source Resource',
|
|
274
|
+
},
|
|
275
|
+
targetResource: null,
|
|
276
|
+
sourceContext: {
|
|
277
|
+
before: 'Text before',
|
|
278
|
+
selected: 'Selected text',
|
|
279
|
+
after: 'Text after',
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
283
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
284
|
+
});
|
|
285
|
+
const result = await client.getAnnotationLLMContext(testResourceUri, annotationId);
|
|
286
|
+
(0, vitest_1.expect)(result.annotation.id).toBe('ann-123');
|
|
287
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(`${testResourceUri}/annotations/${annotationId}/llm-context`, vitest_1.expect.objectContaining({
|
|
288
|
+
searchParams: vitest_1.expect.any(URLSearchParams),
|
|
289
|
+
}));
|
|
290
|
+
});
|
|
291
|
+
(0, vitest_1.test)('should get annotation LLM context with custom options', async () => {
|
|
292
|
+
const annotationId = 'ann-123';
|
|
293
|
+
const mockResponse = {
|
|
294
|
+
annotation: {
|
|
295
|
+
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
296
|
+
type: 'Annotation',
|
|
297
|
+
id: 'ann-123',
|
|
298
|
+
motivation: 'linking',
|
|
299
|
+
target: { source: testResourceUri },
|
|
300
|
+
body: [],
|
|
301
|
+
},
|
|
302
|
+
sourceResource: {
|
|
303
|
+
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
304
|
+
'@id': 'source-id',
|
|
305
|
+
name: 'Source',
|
|
306
|
+
},
|
|
307
|
+
targetResource: null,
|
|
308
|
+
sourceContext: {
|
|
309
|
+
before: 'Limited',
|
|
310
|
+
selected: 'text',
|
|
311
|
+
after: 'here',
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
315
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
316
|
+
});
|
|
317
|
+
const result = await client.getAnnotationLLMContext(testResourceUri, annotationId, {
|
|
318
|
+
contextWindow: 500,
|
|
319
|
+
});
|
|
320
|
+
(0, vitest_1.expect)(result.sourceContext).toBeDefined();
|
|
321
|
+
const call = vitest_1.vi.mocked(mockKy.get).mock.calls[0];
|
|
322
|
+
const searchParams = call[1]?.searchParams;
|
|
323
|
+
(0, vitest_1.expect)(searchParams.get('contextWindow')).toBe('500');
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
(0, vitest_1.describe)('User Operations', () => {
|
|
327
|
+
(0, vitest_1.test)('should logout user', async () => {
|
|
328
|
+
const mockResponse = {
|
|
329
|
+
message: 'Logged out successfully',
|
|
330
|
+
};
|
|
331
|
+
vitest_1.vi.mocked(mockKy.post).mockReturnValue({
|
|
332
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
333
|
+
});
|
|
334
|
+
const result = await client.logout();
|
|
335
|
+
(0, vitest_1.expect)(result.message).toBe('Logged out successfully');
|
|
336
|
+
(0, vitest_1.expect)(mockKy.post).toHaveBeenCalledWith(`${testBaseUrl}/api/users/logout`);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
(0, vitest_1.describe)('System Status', () => {
|
|
340
|
+
(0, vitest_1.test)('should get system status', async () => {
|
|
341
|
+
const mockResponse = {
|
|
342
|
+
status: 'healthy',
|
|
343
|
+
version: '1.0.0',
|
|
344
|
+
features: {
|
|
345
|
+
semanticContent: 'enabled',
|
|
346
|
+
collaboration: 'enabled',
|
|
347
|
+
rbac: 'disabled',
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
351
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
352
|
+
});
|
|
353
|
+
const result = await client.getStatus();
|
|
354
|
+
(0, vitest_1.expect)(result.version).toBe('1.0.0');
|
|
355
|
+
(0, vitest_1.expect)(result.features.semanticContent).toBe('enabled');
|
|
356
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(`${testBaseUrl}/api/status`);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
(0, vitest_1.describe)('Entity Types Bulk Operations', () => {
|
|
360
|
+
(0, vitest_1.test)('should add multiple entity types at once', async () => {
|
|
361
|
+
const mockResponse = {
|
|
362
|
+
success: true,
|
|
363
|
+
entityTypes: ['concept', 'person', 'organization'],
|
|
364
|
+
};
|
|
365
|
+
vitest_1.vi.mocked(mockKy.post).mockReturnValue({
|
|
366
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
367
|
+
});
|
|
368
|
+
const result = await client.addEntityTypesBulk([(0, branded_types_1.entityType)('concept'), (0, branded_types_1.entityType)('person'), (0, branded_types_1.entityType)('organization')]);
|
|
369
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
370
|
+
(0, vitest_1.expect)(result.entityTypes).toHaveLength(3);
|
|
371
|
+
(0, vitest_1.expect)(mockKy.post).toHaveBeenCalledWith(`${testBaseUrl}/api/entity-types/bulk`, vitest_1.expect.objectContaining({
|
|
372
|
+
json: { tags: [(0, branded_types_1.entityType)('concept'), (0, branded_types_1.entityType)('person'), (0, branded_types_1.entityType)('organization')] },
|
|
373
|
+
}));
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
(0, vitest_1.describe)('Annotation History', () => {
|
|
377
|
+
(0, vitest_1.test)('should get annotation event history', async () => {
|
|
378
|
+
const annotationUri = `${testResourceUri}/annotations/ann-123`;
|
|
379
|
+
const mockResponse = {
|
|
380
|
+
events: [
|
|
381
|
+
{
|
|
382
|
+
id: 'evt-1',
|
|
383
|
+
type: 'highlight.created',
|
|
384
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
385
|
+
userId: 'user-1',
|
|
386
|
+
resourceId: 'test-resource-id',
|
|
387
|
+
payload: { highlightId: 'ann-123' },
|
|
388
|
+
metadata: {
|
|
389
|
+
sequenceNumber: 1,
|
|
390
|
+
prevEventHash: 'hash-0',
|
|
391
|
+
checksum: 'checksum-1',
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: 'evt-2',
|
|
396
|
+
type: 'highlight.updated',
|
|
397
|
+
timestamp: '2024-01-01T00:01:00Z',
|
|
398
|
+
userId: 'user-1',
|
|
399
|
+
resourceId: 'test-resource-id',
|
|
400
|
+
payload: { highlightId: 'ann-123' },
|
|
401
|
+
metadata: {
|
|
402
|
+
sequenceNumber: 2,
|
|
403
|
+
prevEventHash: 'hash-1',
|
|
404
|
+
checksum: 'checksum-2',
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
total: 2,
|
|
409
|
+
annotationId: 'ann-123',
|
|
410
|
+
resourceId: 'test-resource-id',
|
|
411
|
+
};
|
|
412
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
413
|
+
json: vitest_1.vi.fn().mockResolvedValue(mockResponse),
|
|
414
|
+
});
|
|
415
|
+
const result = await client.getAnnotationHistory(annotationUri);
|
|
416
|
+
(0, vitest_1.expect)(result.total).toBe(2);
|
|
417
|
+
(0, vitest_1.expect)(result.events).toHaveLength(2);
|
|
418
|
+
(0, vitest_1.expect)(result.annotationId).toBe('ann-123');
|
|
419
|
+
(0, vitest_1.expect)(result.events[0].metadata.sequenceNumber).toBe(1);
|
|
420
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(`${annotationUri}/history`);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
(0, vitest_1.describe)('W3C Content Negotiation', () => {
|
|
424
|
+
(0, vitest_1.test)('should get resource representation with default accept header', async () => {
|
|
425
|
+
const mockText = '# Hello World\n\nThis is markdown content.';
|
|
426
|
+
const mockBuffer = new TextEncoder().encode(mockText).buffer;
|
|
427
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
428
|
+
headers: {
|
|
429
|
+
get: vitest_1.vi.fn((header) => header === 'content-type' ? 'text/plain' : null)
|
|
430
|
+
},
|
|
431
|
+
arrayBuffer: vitest_1.vi.fn().mockResolvedValue(mockBuffer),
|
|
432
|
+
});
|
|
433
|
+
const result = await client.getResourceRepresentation(testResourceUri);
|
|
434
|
+
(0, vitest_1.expect)(result.data).toBeInstanceOf(ArrayBuffer);
|
|
435
|
+
(0, vitest_1.expect)(result.contentType).toBe('text/plain');
|
|
436
|
+
(0, vitest_1.expect)(new TextDecoder().decode(result.data)).toBe(mockText);
|
|
437
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(testResourceUri, vitest_1.expect.objectContaining({
|
|
438
|
+
headers: {
|
|
439
|
+
Accept: 'text/plain',
|
|
440
|
+
},
|
|
441
|
+
}));
|
|
442
|
+
});
|
|
443
|
+
(0, vitest_1.test)('should get resource representation with custom accept header', async () => {
|
|
444
|
+
const mockMarkdown = '# Title\n\n## Section\n\nContent here.';
|
|
445
|
+
const mockBuffer = new TextEncoder().encode(mockMarkdown).buffer;
|
|
446
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
447
|
+
headers: {
|
|
448
|
+
get: vitest_1.vi.fn((header) => header === 'content-type' ? 'text/markdown' : null)
|
|
449
|
+
},
|
|
450
|
+
arrayBuffer: vitest_1.vi.fn().mockResolvedValue(mockBuffer),
|
|
451
|
+
});
|
|
452
|
+
const result = await client.getResourceRepresentation(testResourceUri, {
|
|
453
|
+
accept: 'text/markdown',
|
|
454
|
+
});
|
|
455
|
+
(0, vitest_1.expect)(result.data).toBeInstanceOf(ArrayBuffer);
|
|
456
|
+
(0, vitest_1.expect)(result.contentType).toBe('text/markdown');
|
|
457
|
+
(0, vitest_1.expect)(new TextDecoder().decode(result.data)).toBe(mockMarkdown);
|
|
458
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(testResourceUri, vitest_1.expect.objectContaining({
|
|
459
|
+
headers: {
|
|
460
|
+
Accept: 'text/markdown',
|
|
461
|
+
},
|
|
462
|
+
}));
|
|
463
|
+
});
|
|
464
|
+
(0, vitest_1.test)('should get resource representation with text/plain', async () => {
|
|
465
|
+
const mockText = 'Hello World';
|
|
466
|
+
const mockBuffer = new TextEncoder().encode(mockText).buffer;
|
|
467
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
468
|
+
headers: {
|
|
469
|
+
get: vitest_1.vi.fn((header) => header === 'content-type' ? 'text/plain; charset=utf-8' : null)
|
|
470
|
+
},
|
|
471
|
+
arrayBuffer: vitest_1.vi.fn().mockResolvedValue(mockBuffer),
|
|
472
|
+
});
|
|
473
|
+
const result = await client.getResourceRepresentation(testResourceUri, {
|
|
474
|
+
accept: 'text/plain',
|
|
475
|
+
});
|
|
476
|
+
(0, vitest_1.expect)(result.data).toBeInstanceOf(ArrayBuffer);
|
|
477
|
+
(0, vitest_1.expect)(result.contentType).toBe('text/plain; charset=utf-8');
|
|
478
|
+
(0, vitest_1.expect)(new TextDecoder().decode(result.data)).toBe(mockText);
|
|
479
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(testResourceUri, vitest_1.expect.objectContaining({
|
|
480
|
+
headers: {
|
|
481
|
+
Accept: 'text/plain',
|
|
482
|
+
},
|
|
483
|
+
}));
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
(0, vitest_1.describe)('Streaming Content Negotiation', () => {
|
|
487
|
+
(0, vitest_1.test)('should get resource representation as stream', async () => {
|
|
488
|
+
const mockData = new Uint8Array([1, 2, 3, 4, 5]);
|
|
489
|
+
const mockStream = new ReadableStream({
|
|
490
|
+
start(controller) {
|
|
491
|
+
controller.enqueue(mockData);
|
|
492
|
+
controller.close();
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
496
|
+
headers: {
|
|
497
|
+
get: vitest_1.vi.fn((header) => header === 'content-type' ? 'video/mp4' : null)
|
|
498
|
+
},
|
|
499
|
+
body: mockStream,
|
|
500
|
+
});
|
|
501
|
+
const result = await client.getResourceRepresentationStream(testResourceUri, {
|
|
502
|
+
accept: 'video/mp4',
|
|
503
|
+
});
|
|
504
|
+
(0, vitest_1.expect)(result.stream).toBeInstanceOf(ReadableStream);
|
|
505
|
+
(0, vitest_1.expect)(result.contentType).toBe('video/mp4');
|
|
506
|
+
(0, vitest_1.expect)(mockKy.get).toHaveBeenCalledWith(testResourceUri, vitest_1.expect.objectContaining({
|
|
507
|
+
headers: {
|
|
508
|
+
Accept: 'video/mp4',
|
|
509
|
+
},
|
|
510
|
+
}));
|
|
511
|
+
// Verify stream can be consumed
|
|
512
|
+
const reader = result.stream.getReader();
|
|
513
|
+
const { value, done } = await reader.read();
|
|
514
|
+
(0, vitest_1.expect)(done).toBe(false);
|
|
515
|
+
(0, vitest_1.expect)(value).toEqual(mockData);
|
|
516
|
+
});
|
|
517
|
+
(0, vitest_1.test)('should throw error if response body is null', async () => {
|
|
518
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
519
|
+
headers: {
|
|
520
|
+
get: vitest_1.vi.fn(() => 'text/plain')
|
|
521
|
+
},
|
|
522
|
+
body: null,
|
|
523
|
+
});
|
|
524
|
+
await (0, vitest_1.expect)(client.getResourceRepresentationStream(testResourceUri)).rejects.toThrow('Response body is null - cannot create stream');
|
|
525
|
+
});
|
|
526
|
+
(0, vitest_1.test)('should use default content type if header missing', async () => {
|
|
527
|
+
const mockStream = new ReadableStream({
|
|
528
|
+
start(controller) {
|
|
529
|
+
controller.close();
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
vitest_1.vi.mocked(mockKy.get).mockReturnValue({
|
|
533
|
+
headers: {
|
|
534
|
+
get: vitest_1.vi.fn(() => null)
|
|
535
|
+
},
|
|
536
|
+
body: mockStream,
|
|
537
|
+
});
|
|
538
|
+
const result = await client.getResourceRepresentationStream(testResourceUri);
|
|
539
|
+
(0, vitest_1.expect)(result.contentType).toBe('application/octet-stream');
|
|
540
|
+
(0, vitest_1.expect)(result.stream).toBeInstanceOf(ReadableStream);
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
/**
|
|
545
|
+
* Example usage documentation
|
|
546
|
+
*
|
|
547
|
+
* Archive a resource:
|
|
548
|
+
* ```typescript
|
|
549
|
+
* await client.updateResource(resourceUri, { archived: true });
|
|
550
|
+
* ```
|
|
551
|
+
*
|
|
552
|
+
* Unarchive a resource:
|
|
553
|
+
* ```typescript
|
|
554
|
+
* await client.updateResource(resourceUri, { archived: false });
|
|
555
|
+
* ```
|
|
556
|
+
*
|
|
557
|
+
* List active resources only:
|
|
558
|
+
* ```typescript
|
|
559
|
+
* const active = await client.listResources(20, false);
|
|
560
|
+
* ```
|
|
561
|
+
*
|
|
562
|
+
* List archived resources only:
|
|
563
|
+
* ```typescript
|
|
564
|
+
* const archived = await client.listResources(20, true);
|
|
565
|
+
* ```
|
|
566
|
+
*/
|
|
567
|
+
//# sourceMappingURL=client.test.js.map
|