xmemory 1.0.1 → 2.0.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 +205 -3
- package/dist/client.d.ts +39 -32
- package/dist/client.js +186 -118
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/instance.d.ts +15 -0
- package/dist/instance.js +68 -0
- package/dist/types.d.ts +97 -25
- package/dist/types.js +28 -5
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,8 +1,210 @@
|
|
|
1
|
-
#
|
|
1
|
+
# xmemory
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript/JavaScript client library for the [Xmemory](https://xmemory.ai) API.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install xmemory
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { XmemoryClient } from "xmemory";
|
|
15
|
+
|
|
16
|
+
const xm = new XmemoryClient({
|
|
17
|
+
url: "https://api.xmemory.ai", // or set XMEM_API_URL env var
|
|
18
|
+
token: "<your-token>", // or set XMEM_AUTH_TOKEN env var
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Write and read from an existing instance
|
|
22
|
+
const inst = xm.instance("<your-instance-id>");
|
|
23
|
+
|
|
24
|
+
await inst.write("Alice is a software engineer who loves TypeScript.");
|
|
25
|
+
const result = await inst.read("What does Alice do?");
|
|
26
|
+
console.log(result.reader_result);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
| Parameter | Env var | Default | Description |
|
|
32
|
+
|-------------|-------------------|----------------------------|----------------------------------------|
|
|
33
|
+
| `url` | `XMEM_API_URL` | `https://api.xmemory.ai` | Base URL of the Xmemory API |
|
|
34
|
+
| `token` | `XMEM_AUTH_TOKEN` | `undefined` | Bearer token for authentication |
|
|
35
|
+
| `timeoutMs` | — | `60000` | Default request timeout in milliseconds |
|
|
36
|
+
|
|
37
|
+
## Creating a client
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { XmemoryClient, xmemoryInstance } from "xmemory";
|
|
41
|
+
|
|
42
|
+
// Option 1: constructor (no health check)
|
|
43
|
+
const xm1 = new XmemoryClient({ token: "..." });
|
|
44
|
+
|
|
45
|
+
// Option 2: factory with health check
|
|
46
|
+
const xm2 = await XmemoryClient.create({ token: "..." });
|
|
47
|
+
|
|
48
|
+
// Option 3: convenience function (same as Option 2)
|
|
49
|
+
const xm3 = await xmemoryInstance({ token: "..." });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Admin operations
|
|
53
|
+
|
|
54
|
+
All cluster and instance management lives under `client.admin`.
|
|
55
|
+
|
|
56
|
+
### Clusters
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const clusters = await xm.admin.listClusters();
|
|
60
|
+
const cluster = await xm.admin.getCluster(clusterId);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Create an instance
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { SchemaType } from "xmemory";
|
|
67
|
+
|
|
68
|
+
const inst = await xm.admin.createInstance(
|
|
69
|
+
clusterId,
|
|
70
|
+
"my-instance",
|
|
71
|
+
schemaYml,
|
|
72
|
+
SchemaType.YML,
|
|
73
|
+
{ description: "User profiles" },
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// inst is an InstanceHandle — use it directly for data operations
|
|
77
|
+
await inst.write("Alice joined the team.");
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### List and get instances
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const instances = await xm.admin.listInstances();
|
|
84
|
+
const info = await xm.admin.getInstance(instanceId);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Schema operations
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const schema = await xm.admin.getInstanceSchema(instanceId);
|
|
91
|
+
await xm.admin.updateInstanceSchema(instanceId, newYml, SchemaType.YML);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Generate schema
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const result = await xm.admin.generateSchema(clusterId, "Track user profiles and preferences");
|
|
98
|
+
console.log(result.data_schema);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Update metadata and delete
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
await xm.admin.updateInstanceMetadata(instanceId, "new-name", "new description");
|
|
105
|
+
const deletedIds = await xm.admin.deleteInstance(instanceId);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Instance data operations
|
|
109
|
+
|
|
110
|
+
Get a handle to an instance and use it for reads, writes, and extractions.
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
const inst = xm.instance("<instance-id>");
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### `inst.write(text, options?)` → `WriteResult`
|
|
117
|
+
|
|
118
|
+
Extract and store structured objects from text.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const result = await inst.write("Bob is a designer based in Berlin.");
|
|
122
|
+
console.log(result.write_id, result.trace_id);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Options: `{ extractionLogic?, diffEngine?, timeoutMs? }` — `extractionLogic` defaults to `"deep"`.
|
|
126
|
+
|
|
127
|
+
### `inst.writeAsync(text, options?)` → `AsyncWriteResult`
|
|
128
|
+
|
|
129
|
+
Start an asynchronous write. Returns a `write_id` for tracking.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const { write_id } = await inst.writeAsync("Carol manages the London office.");
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `inst.writeStatus(writeId, options?)` → `WriteStatusResult`
|
|
136
|
+
|
|
137
|
+
Poll the status of an async write.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
const status = await inst.writeStatus(write_id);
|
|
141
|
+
console.log(status.write_status); // "queued" | "processing" | "completed" | "failed" | "not_found"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `inst.read(query, options?)` → `ReadResult`
|
|
145
|
+
|
|
146
|
+
Query the instance.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const result = await inst.read("Who is on the team?");
|
|
150
|
+
console.log(result.reader_result);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Options: `{ readMode?, traceId?, timeoutMs? }` — `readMode` defaults to `"single-answer"`.
|
|
154
|
+
|
|
155
|
+
### `inst.extract(text, options?)` → `ExtractResult`
|
|
156
|
+
|
|
157
|
+
Extract objects from text without storing them.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
const result = await inst.extract("Dave is an engineer in Tokyo.");
|
|
161
|
+
console.log(result.objects_extracted);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### `inst.getSchema(options?)` → `InstanceSchemaInfo`
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const schema = await inst.getSchema();
|
|
168
|
+
console.log(schema.data_schema);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Error handling
|
|
172
|
+
|
|
173
|
+
All errors throw `XmemoryAPIError`. Health check failures throw `XmemoryHealthCheckError` (a subclass).
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { XmemoryClient, XmemoryAPIError, XmemoryHealthCheckError } from "xmemory";
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const xm = await XmemoryClient.create({ token: "..." });
|
|
180
|
+
} catch (e) {
|
|
181
|
+
if (e instanceof XmemoryHealthCheckError) {
|
|
182
|
+
console.error("Server unreachable:", e.message);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
await inst.read("query");
|
|
188
|
+
} catch (e) {
|
|
189
|
+
if (e instanceof XmemoryAPIError) {
|
|
190
|
+
console.error(`API error (HTTP ${e.status}): ${e.message}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## All timeouts are per-request
|
|
196
|
+
|
|
197
|
+
Every method accepts an optional `timeoutMs` in its options bag, overriding the client default.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const result = await inst.read("query", { timeoutMs: 120_000 });
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Mastra integration
|
|
206
|
+
|
|
207
|
+
You can also use `xmemory` as an MCP server within [Mastra.ai](https://mastra.ai).
|
|
6
208
|
|
|
7
209
|
First, create a local Mastra instance:
|
|
8
210
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,37 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* XmemoryClient — main entry point for the xmemory API.
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
import { InstanceHandle } from "./instance.js";
|
|
5
|
+
import { type ClusterInfo, type CreateInstanceOptions, type GenerateSchemaOptions, type GenerateSchemaResult, type InstanceInfo, type InstanceSchemaInfo, type RequestOptions, type SchemaTypeValue, type XmemoryClientOptions } from "./types.js";
|
|
6
|
+
export interface AdminNamespace {
|
|
7
|
+
listClusters(options?: RequestOptions & {
|
|
8
|
+
ids?: string[];
|
|
9
|
+
}): Promise<ClusterInfo[]>;
|
|
10
|
+
getCluster(clusterId: string, options?: RequestOptions): Promise<ClusterInfo>;
|
|
11
|
+
createInstance(clusterId: string, name: string, schemaText: string, schemaType: SchemaTypeValue, options?: CreateInstanceOptions): Promise<InstanceHandle>;
|
|
12
|
+
listInstances(options?: RequestOptions & {
|
|
13
|
+
ids?: string[];
|
|
14
|
+
}): Promise<InstanceInfo[]>;
|
|
15
|
+
getInstance(instanceId: string, options?: RequestOptions): Promise<InstanceInfo>;
|
|
16
|
+
deleteInstance(instanceId: string, options?: RequestOptions): Promise<string[]>;
|
|
17
|
+
getInstanceSchema(instanceId: string, options?: RequestOptions): Promise<InstanceSchemaInfo>;
|
|
18
|
+
updateInstanceSchema(instanceId: string, schemaText: string, schemaType: SchemaTypeValue, options?: RequestOptions): Promise<InstanceInfo>;
|
|
19
|
+
updateInstanceMetadata(instanceId: string, name: string, description: string, options?: RequestOptions): Promise<InstanceInfo>;
|
|
20
|
+
generateSchema(clusterId: string, schemaDescription: string, options?: GenerateSchemaOptions): Promise<GenerateSchemaResult>;
|
|
15
21
|
}
|
|
16
22
|
export declare class XmemoryClient {
|
|
17
|
-
readonly
|
|
18
|
-
readonly
|
|
19
|
-
readonly
|
|
20
|
-
|
|
21
|
-
constructor(options?:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
private readonly _baseUrl;
|
|
24
|
+
private readonly _timeoutMs;
|
|
25
|
+
private readonly _token;
|
|
26
|
+
readonly admin: AdminNamespace;
|
|
27
|
+
constructor(options?: XmemoryClientOptions);
|
|
28
|
+
/** Factory that performs a health check before returning the client. */
|
|
29
|
+
static create(options?: XmemoryClientOptions): Promise<XmemoryClient>;
|
|
30
|
+
/** Return an InstanceHandle scoped to the given instance ID. */
|
|
31
|
+
instance(instanceId: string): InstanceHandle;
|
|
32
|
+
/** GET /healthz — throws XmemoryHealthCheckError on failure. */
|
|
33
|
+
checkHealth(): Promise<void>;
|
|
34
|
+
private _fetch;
|
|
35
|
+
private _headers;
|
|
36
|
+
/** Core request — returns the raw API wrapper `{ids, items, errors}`. */
|
|
37
|
+
private _request;
|
|
38
|
+
private _requestOne;
|
|
39
|
+
private _requestList;
|
|
40
|
+
private _requestIds;
|
|
41
|
+
private _buildAdmin;
|
|
33
42
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
*/
|
|
37
|
-
export declare function xmemoryInstance(options?: XmemoryInstanceOptions): Promise<XmemoryClient>;
|
|
43
|
+
/** Convenience factory — creates a client with a health check. */
|
|
44
|
+
export declare function xmemoryInstance(options?: XmemoryClientOptions): Promise<XmemoryClient>;
|
package/dist/client.js
CHANGED
|
@@ -1,140 +1,208 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* XmemoryClient — main entry point for the xmemory API.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { InstanceHandle } from "./instance.js";
|
|
5
|
+
import { XmemoryAPIError, XmemoryHealthCheckError, buildInstanceSchema, } from "./types.js";
|
|
6
|
+
const DEFAULT_BASE_URL = "https://api.xmemory.ai";
|
|
6
7
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
super(message);
|
|
11
|
-
this.status = status;
|
|
12
|
-
this.name = "XmemoryAPIError";
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
async function getEnv(name) {
|
|
16
|
-
if (typeof process !== "undefined" && process.env) {
|
|
17
|
-
return process.env[name];
|
|
18
|
-
}
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
async function fetchWithTimeout(url, options) {
|
|
22
|
-
const { timeoutMs = DEFAULT_TIMEOUT_MS, ...fetchOptions } = options;
|
|
23
|
-
const controller = new AbortController();
|
|
24
|
-
const id = setTimeout(() => controller.abort(), timeoutMs);
|
|
25
|
-
try {
|
|
26
|
-
const res = await fetch(url, {
|
|
27
|
-
...fetchOptions,
|
|
28
|
-
signal: controller.signal,
|
|
29
|
-
});
|
|
30
|
-
return res;
|
|
31
|
-
}
|
|
32
|
-
finally {
|
|
33
|
-
clearTimeout(id);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
async function postJson(baseUrl, path, body, token, timeoutMs) {
|
|
37
|
-
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
38
|
-
const headers = {
|
|
39
|
-
Accept: "application/json",
|
|
40
|
-
"Content-Type": "application/json",
|
|
41
|
-
};
|
|
42
|
-
if (token) {
|
|
43
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
44
|
-
}
|
|
45
|
-
const res = await fetchWithTimeout(url, {
|
|
46
|
-
method: "POST",
|
|
47
|
-
headers,
|
|
48
|
-
body: JSON.stringify(body),
|
|
49
|
-
timeoutMs,
|
|
50
|
-
});
|
|
51
|
-
const raw = await res.text();
|
|
52
|
-
let payload;
|
|
53
|
-
try {
|
|
54
|
-
payload = raw ? JSON.parse(raw) : {};
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
throw new XmemoryAPIError(`Invalid JSON from server (${res.status})`, res.status);
|
|
58
|
-
}
|
|
59
|
-
if (typeof payload === "object" && payload !== null && payload.status === "error") {
|
|
60
|
-
const err = payload;
|
|
61
|
-
const msg = err.error_message || err.error || String(payload);
|
|
62
|
-
throw new XmemoryAPIError(`${path} failed: ${msg}`, res.status);
|
|
63
|
-
}
|
|
64
|
-
if (!res.ok) {
|
|
65
|
-
throw new XmemoryAPIError(`HTTP ${res.status}: ${raw.slice(0, 200)}`, res.status);
|
|
66
|
-
}
|
|
67
|
-
return payload;
|
|
68
|
-
}
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Client
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
69
11
|
export class XmemoryClient {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
12
|
+
_baseUrl;
|
|
13
|
+
_timeoutMs;
|
|
14
|
+
_token;
|
|
15
|
+
admin;
|
|
74
16
|
constructor(options = {}) {
|
|
75
|
-
|
|
76
|
-
this.
|
|
77
|
-
this.
|
|
17
|
+
const env = typeof process !== "undefined" ? process.env : undefined;
|
|
18
|
+
this._baseUrl = (options.url ?? env?.XMEM_API_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
19
|
+
this._token = options.token ?? env?.XMEM_AUTH_TOKEN;
|
|
20
|
+
this._timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
21
|
+
this.admin = this._buildAdmin();
|
|
78
22
|
}
|
|
23
|
+
/** Factory that performs a health check before returning the client. */
|
|
79
24
|
static async create(options = {}) {
|
|
80
|
-
const
|
|
81
|
-
const token = options.token ?? (await getEnv("XMEM_AUTH_TOKEN"));
|
|
82
|
-
const client = new XmemoryClient({ ...options, url, token });
|
|
25
|
+
const client = new XmemoryClient(options);
|
|
83
26
|
await client.checkHealth();
|
|
84
27
|
return client;
|
|
85
28
|
}
|
|
29
|
+
/** Return an InstanceHandle scoped to the given instance ID. */
|
|
30
|
+
instance(instanceId) {
|
|
31
|
+
return new InstanceHandle(instanceId, this._requestOne.bind(this));
|
|
32
|
+
}
|
|
33
|
+
/** GET /healthz — throws XmemoryHealthCheckError on failure. */
|
|
86
34
|
async checkHealth() {
|
|
87
|
-
const url = `${this.
|
|
88
|
-
|
|
89
|
-
method: "GET",
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (!res.ok) {
|
|
94
|
-
throw new XmemoryAPIError(`Health check failed: ${res.status} at ${url}`, res.status);
|
|
35
|
+
const url = `${this._baseUrl}/healthz`;
|
|
36
|
+
try {
|
|
37
|
+
const res = await this._fetch(url, { method: "GET" }, this._timeoutMs);
|
|
38
|
+
if (!res.ok) {
|
|
39
|
+
throw new XmemoryHealthCheckError(`Health check failed: HTTP ${res.status} at ${url}`, res.status);
|
|
40
|
+
}
|
|
95
41
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
throw new
|
|
42
|
+
catch (err) {
|
|
43
|
+
if (err instanceof XmemoryHealthCheckError)
|
|
44
|
+
throw err;
|
|
45
|
+
throw new XmemoryHealthCheckError(`Health check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
100
46
|
}
|
|
101
|
-
return this.instanceId;
|
|
102
47
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
48
|
+
// -----------------------------------------------------------------------
|
|
49
|
+
// Private: HTTP layer
|
|
50
|
+
// -----------------------------------------------------------------------
|
|
51
|
+
async _fetch(url, init, timeoutMs) {
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
54
|
+
try {
|
|
55
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
clearTimeout(timer);
|
|
110
59
|
}
|
|
111
|
-
return response.status === "ok";
|
|
112
|
-
}
|
|
113
|
-
async write(text, options) {
|
|
114
|
-
const iid = this.requireInstanceId("write");
|
|
115
|
-
const timeoutMs = options?.timeout != null ? options.timeout * 1000 : this.timeoutMs;
|
|
116
|
-
return postJson(this.baseUrl, "/write", {
|
|
117
|
-
instance_id: iid,
|
|
118
|
-
text,
|
|
119
|
-
extraction_logic: "deep",
|
|
120
|
-
}, this.token, timeoutMs);
|
|
121
60
|
}
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
61
|
+
_headers(hasBody = false) {
|
|
62
|
+
const h = {
|
|
63
|
+
Accept: "application/json",
|
|
64
|
+
};
|
|
65
|
+
if (hasBody)
|
|
66
|
+
h["Content-Type"] = "application/json";
|
|
67
|
+
if (this._token)
|
|
68
|
+
h["Authorization"] = `Bearer ${this._token}`;
|
|
69
|
+
return h;
|
|
70
|
+
}
|
|
71
|
+
/** Core request — returns the raw API wrapper `{ids, items, errors}`. */
|
|
72
|
+
async _request(method, path, options) {
|
|
73
|
+
let url = `${this._baseUrl}${path}`;
|
|
74
|
+
if (options?.params) {
|
|
75
|
+
const sp = new URLSearchParams();
|
|
76
|
+
for (const [key, val] of Object.entries(options.params)) {
|
|
77
|
+
if (Array.isArray(val)) {
|
|
78
|
+
for (const v of val)
|
|
79
|
+
sp.append(key, v);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
sp.append(key, val);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const qs = sp.toString();
|
|
86
|
+
if (qs)
|
|
87
|
+
url += `?${qs}`;
|
|
88
|
+
}
|
|
89
|
+
const timeoutMs = options?.timeoutMs ?? this._timeoutMs;
|
|
90
|
+
const hasBody = !!(options?.body && method !== "GET");
|
|
91
|
+
const init = { method, headers: this._headers(hasBody) };
|
|
92
|
+
if (hasBody) {
|
|
93
|
+
init.body = JSON.stringify(options.body);
|
|
94
|
+
}
|
|
95
|
+
const res = await this._fetch(url, init, timeoutMs);
|
|
96
|
+
const raw = await res.text();
|
|
97
|
+
let payload;
|
|
98
|
+
try {
|
|
99
|
+
payload = raw ? JSON.parse(raw) : {};
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
throw new XmemoryAPIError(`Invalid JSON from server (${res.status}): ${raw.slice(0, 200)}`, res.status);
|
|
103
|
+
}
|
|
104
|
+
if (!res.ok) {
|
|
105
|
+
const msg = typeof payload === "object" && payload !== null && "message" in payload
|
|
106
|
+
? String(payload.message)
|
|
107
|
+
: raw.slice(0, 200);
|
|
108
|
+
throw new XmemoryAPIError(`HTTP ${res.status}: ${msg}`, res.status);
|
|
109
|
+
}
|
|
110
|
+
const response = payload;
|
|
111
|
+
if (response.errors?.length) {
|
|
112
|
+
const first = response.errors[0];
|
|
113
|
+
throw new XmemoryAPIError(`API error: ${first.message} (${first.code})`, res.status);
|
|
114
|
+
}
|
|
115
|
+
return response;
|
|
130
116
|
}
|
|
131
|
-
|
|
132
|
-
|
|
117
|
+
async _requestOne(method, path, options) {
|
|
118
|
+
const response = await this._request(method, path, options);
|
|
119
|
+
const items = response.items ?? [];
|
|
120
|
+
if (items.length === 0) {
|
|
121
|
+
throw new XmemoryAPIError(`Expected one item from ${method} ${path}, got none`);
|
|
122
|
+
}
|
|
123
|
+
if (items.length > 1) {
|
|
124
|
+
throw new XmemoryAPIError(`Expected one item from ${method} ${path}, got ${items.length}`);
|
|
125
|
+
}
|
|
126
|
+
return items[0];
|
|
127
|
+
}
|
|
128
|
+
async _requestList(method, path, options) {
|
|
129
|
+
const response = await this._request(method, path, options);
|
|
130
|
+
return (response.items ?? []);
|
|
131
|
+
}
|
|
132
|
+
async _requestIds(method, path, options) {
|
|
133
|
+
const response = await this._request(method, path, options);
|
|
134
|
+
return response.ids ?? [];
|
|
135
|
+
}
|
|
136
|
+
// -----------------------------------------------------------------------
|
|
137
|
+
// Private: build admin namespace
|
|
138
|
+
// -----------------------------------------------------------------------
|
|
139
|
+
_buildAdmin() {
|
|
140
|
+
return {
|
|
141
|
+
listClusters: async (options) => {
|
|
142
|
+
const params = options?.ids ? { ids: options.ids } : undefined;
|
|
143
|
+
return this._requestList("GET", "/clusters", { params, timeoutMs: options?.timeoutMs });
|
|
144
|
+
},
|
|
145
|
+
getCluster: async (clusterId, options) => {
|
|
146
|
+
return this._requestOne("GET", `/clusters/${clusterId}`, {
|
|
147
|
+
timeoutMs: options?.timeoutMs,
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
createInstance: async (clusterId, name, schemaText, schemaType, options) => {
|
|
151
|
+
const body = {
|
|
152
|
+
name,
|
|
153
|
+
instance_schema: buildInstanceSchema(schemaText, schemaType),
|
|
154
|
+
};
|
|
155
|
+
if (options?.description != null)
|
|
156
|
+
body.description = options.description;
|
|
157
|
+
if (options?.schemaDescription != null)
|
|
158
|
+
body.schema_description = options.schemaDescription;
|
|
159
|
+
const info = await this._requestOne("POST", `/clusters/${clusterId}/instances`, {
|
|
160
|
+
body,
|
|
161
|
+
timeoutMs: options?.timeoutMs,
|
|
162
|
+
});
|
|
163
|
+
return this.instance(info.id);
|
|
164
|
+
},
|
|
165
|
+
listInstances: async (options) => {
|
|
166
|
+
const params = options?.ids ? { ids: options.ids } : undefined;
|
|
167
|
+
return this._requestList("GET", "/instances", { params, timeoutMs: options?.timeoutMs });
|
|
168
|
+
},
|
|
169
|
+
getInstance: async (instanceId, options) => {
|
|
170
|
+
return this._requestOne("GET", `/instances/${instanceId}`, {
|
|
171
|
+
timeoutMs: options?.timeoutMs,
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
deleteInstance: async (instanceId, options) => {
|
|
175
|
+
return this._requestIds("DELETE", `/instances/${instanceId}`, {
|
|
176
|
+
timeoutMs: options?.timeoutMs,
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
getInstanceSchema: async (instanceId, options) => {
|
|
180
|
+
return this._requestOne("GET", `/instances/${instanceId}/schema`, {
|
|
181
|
+
timeoutMs: options?.timeoutMs,
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
updateInstanceSchema: async (instanceId, schemaText, schemaType, options) => {
|
|
185
|
+
return this._requestOne("PUT", `/instances/${instanceId}/schema`, {
|
|
186
|
+
body: { instance_schema: buildInstanceSchema(schemaText, schemaType) },
|
|
187
|
+
timeoutMs: options?.timeoutMs,
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
updateInstanceMetadata: async (instanceId, name, description, options) => {
|
|
191
|
+
return this._requestOne("PUT", `/instances/${instanceId}`, {
|
|
192
|
+
body: { name, description },
|
|
193
|
+
timeoutMs: options?.timeoutMs,
|
|
194
|
+
});
|
|
195
|
+
},
|
|
196
|
+
generateSchema: async (clusterId, schemaDescription, options) => {
|
|
197
|
+
const body = { schema_description: schemaDescription };
|
|
198
|
+
if (options?.currentYmlSchema != null)
|
|
199
|
+
body.current_yml_schema = options.currentYmlSchema;
|
|
200
|
+
return this._requestOne("POST", `/clusters/${clusterId}/instances/generate_schema`, { body, timeoutMs: options?.timeoutMs });
|
|
201
|
+
},
|
|
202
|
+
};
|
|
133
203
|
}
|
|
134
204
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Create an xmemory client.
|
|
137
|
-
*/
|
|
205
|
+
/** Convenience factory — creates a client with a health check. */
|
|
138
206
|
export async function xmemoryInstance(options = {}) {
|
|
139
207
|
return XmemoryClient.create(options);
|
|
140
208
|
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xmemory — TypeScript client for the xmemory API.
|
|
3
|
+
*/
|
|
4
|
+
export { XmemoryClient, xmemoryInstance } from "./client.js";
|
|
5
|
+
export type { AdminNamespace } from "./client.js";
|
|
6
|
+
export { InstanceHandle } from "./instance.js";
|
|
7
|
+
export { XmemoryAPIError, XmemoryHealthCheckError } from "./types.js";
|
|
8
|
+
export { SchemaType } from "./types.js";
|
|
9
|
+
export type { SchemaTypeValue, ExtractionLogic, ReadMode, WriteQueueStatus } from "./types.js";
|
|
10
|
+
export type { XmemoryClientOptions, RequestOptions, ReadOptions, WriteOptions, ExtractOptions, CreateInstanceOptions, GenerateSchemaOptions, } from "./types.js";
|
|
11
|
+
export type { ClusterInfo, InstanceInfo, InstanceSchemaInfo, ReadResult, WriteResult, AsyncWriteResult, WriteStatusResult, ExtractResult, GenerateSchemaResult, } from "./types.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xmemory — TypeScript client for the xmemory API.
|
|
3
|
+
*/
|
|
4
|
+
// Client
|
|
5
|
+
export { XmemoryClient, xmemoryInstance } from "./client.js";
|
|
6
|
+
// Instance handle
|
|
7
|
+
export { InstanceHandle } from "./instance.js";
|
|
8
|
+
// Error classes
|
|
9
|
+
export { XmemoryAPIError, XmemoryHealthCheckError } from "./types.js";
|
|
10
|
+
// Enums
|
|
11
|
+
export { SchemaType } from "./types.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InstanceHandle — scoped data operations on a single xmemory instance.
|
|
3
|
+
*/
|
|
4
|
+
import type { AsyncWriteResult, ExtractOptions, ExtractResult, InstanceSchemaInfo, ReadOptions, ReadResult, RequestOneFn, RequestOptions, WriteOptions, WriteResult, WriteStatusResult } from "./types.js";
|
|
5
|
+
export declare class InstanceHandle {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
private readonly _requestOne;
|
|
8
|
+
constructor(id: string, requestOne: RequestOneFn);
|
|
9
|
+
read(query: string, options?: ReadOptions): Promise<ReadResult>;
|
|
10
|
+
write(text: string, options?: WriteOptions): Promise<WriteResult>;
|
|
11
|
+
writeAsync(text: string, options?: WriteOptions): Promise<AsyncWriteResult>;
|
|
12
|
+
writeStatus(writeId: string, options?: RequestOptions): Promise<WriteStatusResult>;
|
|
13
|
+
extract(text: string, options?: ExtractOptions): Promise<ExtractResult>;
|
|
14
|
+
getSchema(options?: RequestOptions): Promise<InstanceSchemaInfo>;
|
|
15
|
+
}
|
package/dist/instance.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InstanceHandle — scoped data operations on a single xmemory instance.
|
|
3
|
+
*/
|
|
4
|
+
export class InstanceHandle {
|
|
5
|
+
id;
|
|
6
|
+
_requestOne;
|
|
7
|
+
constructor(id, requestOne) {
|
|
8
|
+
this.id = id;
|
|
9
|
+
this._requestOne = requestOne;
|
|
10
|
+
}
|
|
11
|
+
async read(query, options) {
|
|
12
|
+
const body = {
|
|
13
|
+
query,
|
|
14
|
+
mode: options?.readMode ?? "single-answer",
|
|
15
|
+
};
|
|
16
|
+
if (options?.traceId != null)
|
|
17
|
+
body.trace_id = options.traceId;
|
|
18
|
+
return this._requestOne("POST", `/instances/${this.id}/read`, {
|
|
19
|
+
body,
|
|
20
|
+
timeoutMs: options?.timeoutMs,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async write(text, options) {
|
|
24
|
+
const body = {
|
|
25
|
+
text,
|
|
26
|
+
extraction_logic: options?.extractionLogic ?? "deep",
|
|
27
|
+
};
|
|
28
|
+
if (options?.diffEngine != null)
|
|
29
|
+
body.diff_engine = options.diffEngine;
|
|
30
|
+
return this._requestOne("POST", `/instances/${this.id}/write`, {
|
|
31
|
+
body,
|
|
32
|
+
timeoutMs: options?.timeoutMs,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async writeAsync(text, options) {
|
|
36
|
+
const body = {
|
|
37
|
+
text,
|
|
38
|
+
extraction_logic: options?.extractionLogic ?? "deep",
|
|
39
|
+
};
|
|
40
|
+
if (options?.diffEngine != null)
|
|
41
|
+
body.diff_engine = options.diffEngine;
|
|
42
|
+
return this._requestOne("POST", `/instances/${this.id}/write_async`, {
|
|
43
|
+
body,
|
|
44
|
+
timeoutMs: options?.timeoutMs,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async writeStatus(writeId, options) {
|
|
48
|
+
return this._requestOne("POST", `/instances/${this.id}/write_status`, {
|
|
49
|
+
body: { write_id: writeId },
|
|
50
|
+
timeoutMs: options?.timeoutMs,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async extract(text, options) {
|
|
54
|
+
const body = {
|
|
55
|
+
text,
|
|
56
|
+
extraction_logic: options?.extractionLogic ?? "deep",
|
|
57
|
+
};
|
|
58
|
+
return this._requestOne("POST", `/instances/${this.id}/extract`, {
|
|
59
|
+
body,
|
|
60
|
+
timeoutMs: options?.timeoutMs,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async getSchema(options) {
|
|
64
|
+
return this._requestOne("GET", `/instances/${this.id}/schema`, {
|
|
65
|
+
timeoutMs: options?.timeoutMs,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,39 +1,111 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Types for the xmemory API.
|
|
2
|
+
* Types, enums, and error classes for the xmemory API.
|
|
3
3
|
*/
|
|
4
4
|
export declare const SchemaType: {
|
|
5
5
|
readonly YML: 0;
|
|
6
6
|
readonly JSON: 1;
|
|
7
7
|
};
|
|
8
8
|
export type SchemaTypeValue = (typeof SchemaType)[keyof typeof SchemaType];
|
|
9
|
-
export
|
|
10
|
-
|
|
9
|
+
export type ExtractionLogic = "fast" | "regular" | "deep";
|
|
10
|
+
export type ReadMode = "single-answer" | "raw-tables" | "xresponse";
|
|
11
|
+
export type WriteQueueStatus = "queued" | "processing" | "completed" | "failed" | "not_found";
|
|
12
|
+
export declare class XmemoryAPIError extends Error {
|
|
13
|
+
readonly status?: number | undefined;
|
|
14
|
+
constructor(message: string, status?: number | undefined);
|
|
11
15
|
}
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
instance_id?: string | null;
|
|
15
|
-
error_message?: string | null;
|
|
16
|
+
export declare class XmemoryHealthCheckError extends XmemoryAPIError {
|
|
17
|
+
constructor(message: string, status?: number);
|
|
16
18
|
}
|
|
17
|
-
export interface
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
export interface ClusterInfo {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly org_id: string;
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly description: string | null;
|
|
21
24
|
}
|
|
22
|
-
export interface
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
export interface InstanceInfo {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly cluster_id: string;
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly description: string | null;
|
|
30
|
+
readonly data_schema: Record<string, unknown> | null;
|
|
25
31
|
}
|
|
26
|
-
export interface
|
|
27
|
-
|
|
28
|
-
query: string;
|
|
29
|
-
mode?: "single-answer" | "raw-tables" | "xresponse";
|
|
32
|
+
export interface InstanceSchemaInfo {
|
|
33
|
+
readonly data_schema: Record<string, unknown>;
|
|
30
34
|
}
|
|
31
|
-
export interface
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
export interface ReadResult {
|
|
36
|
+
readonly trace_id: string | null;
|
|
37
|
+
readonly reader_result: unknown;
|
|
34
38
|
}
|
|
35
|
-
export interface
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
export interface WriteResult {
|
|
40
|
+
readonly write_id: string;
|
|
41
|
+
readonly trace_id: string | null;
|
|
42
|
+
readonly cleaned_objects: unknown;
|
|
43
|
+
readonly diff_plan: unknown;
|
|
39
44
|
}
|
|
45
|
+
export interface AsyncWriteResult {
|
|
46
|
+
readonly write_id: string;
|
|
47
|
+
}
|
|
48
|
+
export interface WriteStatusResult {
|
|
49
|
+
readonly write_id: string;
|
|
50
|
+
readonly write_status: WriteQueueStatus;
|
|
51
|
+
readonly error_detail: string | null;
|
|
52
|
+
readonly completed_at: string | null;
|
|
53
|
+
}
|
|
54
|
+
export interface ExtractResult {
|
|
55
|
+
readonly trace_id: string | null;
|
|
56
|
+
readonly objects_extracted: unknown;
|
|
57
|
+
}
|
|
58
|
+
export interface GenerateSchemaResult {
|
|
59
|
+
readonly data_schema: Record<string, unknown>;
|
|
60
|
+
}
|
|
61
|
+
export interface XmemoryClientOptions {
|
|
62
|
+
url?: string;
|
|
63
|
+
token?: string;
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
}
|
|
66
|
+
export interface RequestOptions {
|
|
67
|
+
timeoutMs?: number;
|
|
68
|
+
}
|
|
69
|
+
export interface ReadOptions {
|
|
70
|
+
readMode?: ReadMode;
|
|
71
|
+
traceId?: string;
|
|
72
|
+
timeoutMs?: number;
|
|
73
|
+
}
|
|
74
|
+
export interface WriteOptions {
|
|
75
|
+
extractionLogic?: ExtractionLogic;
|
|
76
|
+
diffEngine?: boolean;
|
|
77
|
+
timeoutMs?: number;
|
|
78
|
+
}
|
|
79
|
+
export interface ExtractOptions {
|
|
80
|
+
extractionLogic?: ExtractionLogic;
|
|
81
|
+
timeoutMs?: number;
|
|
82
|
+
}
|
|
83
|
+
export interface CreateInstanceOptions {
|
|
84
|
+
description?: string;
|
|
85
|
+
schemaDescription?: string;
|
|
86
|
+
timeoutMs?: number;
|
|
87
|
+
}
|
|
88
|
+
export interface GenerateSchemaOptions {
|
|
89
|
+
currentYmlSchema?: string;
|
|
90
|
+
timeoutMs?: number;
|
|
91
|
+
}
|
|
92
|
+
export interface ApiError {
|
|
93
|
+
readonly code: string;
|
|
94
|
+
readonly message: string;
|
|
95
|
+
readonly field?: string;
|
|
96
|
+
readonly resource_id?: string;
|
|
97
|
+
}
|
|
98
|
+
export interface RawApiResponse {
|
|
99
|
+
readonly ids?: string[];
|
|
100
|
+
readonly items?: unknown[];
|
|
101
|
+
readonly errors?: ApiError[];
|
|
102
|
+
}
|
|
103
|
+
export interface InternalRequestOptions {
|
|
104
|
+
body?: Record<string, unknown>;
|
|
105
|
+
params?: Record<string, string | string[]>;
|
|
106
|
+
timeoutMs?: number;
|
|
107
|
+
}
|
|
108
|
+
export type RequestOneFn = <T>(method: string, path: string, options?: InternalRequestOptions) => Promise<T>;
|
|
109
|
+
export type RequestListFn = <T>(method: string, path: string, options?: InternalRequestOptions) => Promise<T[]>;
|
|
110
|
+
export type RequestIdsFn = (method: string, path: string, options?: InternalRequestOptions) => Promise<string[]>;
|
|
111
|
+
export declare function buildInstanceSchema(schemaText: string, schemaType: SchemaTypeValue): Record<string, unknown>;
|
package/dist/types.js
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Types for the xmemory API.
|
|
2
|
+
* Types, enums, and error classes for the xmemory API.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Enums
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
export const SchemaType = { YML: 0, JSON: 1 };
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Error classes
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
export class XmemoryAPIError extends Error {
|
|
12
|
+
status;
|
|
13
|
+
constructor(message, status) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.status = status;
|
|
16
|
+
this.name = "XmemoryAPIError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class XmemoryHealthCheckError extends XmemoryAPIError {
|
|
20
|
+
constructor(message, status) {
|
|
21
|
+
super(message, status);
|
|
22
|
+
this.name = "XmemoryHealthCheckError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function buildInstanceSchema(schemaText, schemaType) {
|
|
26
|
+
if (schemaType === SchemaType.YML) {
|
|
27
|
+
return { yml: { value: schemaText } };
|
|
28
|
+
}
|
|
29
|
+
return { json_schema: { value: schemaText } };
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xmemory",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "xmemory",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"xmemory"
|
|
@@ -16,14 +16,15 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"author": "Dima Korolev",
|
|
18
18
|
"type": "module",
|
|
19
|
-
"main": "dist/
|
|
20
|
-
"types": "dist/
|
|
19
|
+
"main": "dist/index.js",
|
|
20
|
+
"types": "dist/index.d.ts",
|
|
21
21
|
"files": [
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc",
|
|
26
26
|
"test": "tsc --noEmit && node --import tsx test.ts",
|
|
27
|
+
"test:types": "tsc --noEmit",
|
|
27
28
|
"prepublishOnly": "npm run build"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|