@luxdb/sdk 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,17 +1,9 @@
1
- import Redis, { RedisOptions } from 'ioredis';
1
+ import Redis, { type RedisOptions } from 'ioredis';
2
2
  export interface VSearchResult {
3
3
  key: string;
4
4
  similarity: number;
5
5
  metadata?: Record<string, unknown>;
6
6
  }
7
- export interface VSearchOptions {
8
- k: number;
9
- filter?: {
10
- key: string;
11
- value: string;
12
- };
13
- meta?: boolean;
14
- }
15
7
  export interface TSSample {
16
8
  timestamp: number;
17
9
  value: number;
@@ -36,8 +28,66 @@ export interface KSubEvent {
36
28
  key: string;
37
29
  operation: string;
38
30
  }
31
+ export interface TableRow {
32
+ id: number;
33
+ [field: string]: unknown;
34
+ }
35
+ declare class TableQueryBuilder {
36
+ private client;
37
+ private name;
38
+ private conditions;
39
+ private orderField?;
40
+ private orderDir?;
41
+ private limitCount?;
42
+ private joinTable?;
43
+ constructor(client: Lux, name: string);
44
+ where(field: string, op: '=' | '!=' | '>' | '<' | '>=' | '<=', value: string | number | boolean): this;
45
+ orderBy(field: string, dir?: 'asc' | 'desc'): this;
46
+ limit(n: number): this;
47
+ join(table: string): this;
48
+ run(): Promise<TableRow[]>;
49
+ insert(data: Record<string, unknown>): Promise<number>;
50
+ update(id: number, data: Record<string, unknown>): Promise<string>;
51
+ delete(...ids: number[]): Promise<number>;
52
+ }
53
+ declare class VectorNamespace {
54
+ private client;
55
+ constructor(client: Lux);
56
+ set(key: string, vector: number[], metadata?: Record<string, unknown>): Promise<string>;
57
+ get(key: string): Promise<{
58
+ dims: number;
59
+ vector: number[];
60
+ metadata?: Record<string, unknown>;
61
+ } | null>;
62
+ search(query: number[], options: {
63
+ topK: number;
64
+ filter?: {
65
+ key: string;
66
+ value: string;
67
+ };
68
+ meta?: boolean;
69
+ }): Promise<VSearchResult[]>;
70
+ count(): Promise<number>;
71
+ }
72
+ declare class TimeSeriesNamespace {
73
+ private client;
74
+ constructor(client: Lux);
75
+ add(key: string, value: number, options?: {
76
+ timestamp?: number | '*';
77
+ retention?: number;
78
+ labels?: Record<string, string>;
79
+ }): Promise<number>;
80
+ get(key: string): Promise<TSSample | null>;
81
+ range(key: string, from: number | '-', to: number | '+', options?: TSRangeOptions): Promise<TSSample[]>;
82
+ mrange(from: number | '-', to: number | '+', filter: string, options?: TSRangeOptions): Promise<TSMRangeResult[]>;
83
+ info(key: string): Promise<Record<string, unknown>>;
84
+ }
39
85
  export declare class Lux extends Redis {
86
+ vectors: VectorNamespace;
87
+ timeseries: TimeSeriesNamespace;
40
88
  constructor(options?: RedisOptions | string);
89
+ table(name: string): TableQueryBuilder;
90
+ _tquery(args: string[]): Promise<TableRow[]>;
41
91
  vset(key: string, vector: number[], options?: {
42
92
  metadata?: Record<string, unknown>;
43
93
  ex?: number;
@@ -48,7 +98,14 @@ export declare class Lux extends Redis {
48
98
  vector: number[];
49
99
  metadata?: Record<string, unknown>;
50
100
  } | null>;
51
- vsearch(query: number[], options: VSearchOptions): Promise<VSearchResult[]>;
101
+ vsearch(query: number[], options: {
102
+ k: number;
103
+ filter?: {
104
+ key: string;
105
+ value: string;
106
+ };
107
+ meta?: boolean;
108
+ }): Promise<VSearchResult[]>;
52
109
  vcard(): Promise<number>;
53
110
  tsadd(key: string, timestamp: number | '*', value: number, options?: TSAddOptions): Promise<number>;
54
111
  tsmadd(...entries: [string, number | '*', number][]): Promise<string>;
package/dist/index.js CHANGED
@@ -5,10 +5,139 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Lux = void 0;
7
7
  const ioredis_1 = __importDefault(require("ioredis"));
8
+ class TableQueryBuilder {
9
+ constructor(client, name) {
10
+ this.conditions = [];
11
+ this.client = client;
12
+ this.name = name;
13
+ }
14
+ where(field, op, value) {
15
+ const v = typeof value === 'string' ? `'${value}'` : String(value);
16
+ this.conditions.push(`${field} ${op} ${v}`);
17
+ return this;
18
+ }
19
+ orderBy(field, dir = 'asc') {
20
+ this.orderField = field;
21
+ this.orderDir = dir.toUpperCase();
22
+ return this;
23
+ }
24
+ limit(n) {
25
+ this.limitCount = n;
26
+ return this;
27
+ }
28
+ join(table) {
29
+ this.joinTable = table;
30
+ return this;
31
+ }
32
+ async run() {
33
+ const args = [this.name];
34
+ if (this.conditions.length) {
35
+ args.push('WHERE', this.conditions.join(' AND '));
36
+ }
37
+ if (this.orderField) {
38
+ args.push('ORDER', 'BY', this.orderField, this.orderDir || 'ASC');
39
+ }
40
+ if (this.limitCount != null) {
41
+ args.push('LIMIT', String(this.limitCount));
42
+ }
43
+ if (this.joinTable) {
44
+ args.push('JOIN', this.joinTable);
45
+ }
46
+ return this.client._tquery(args);
47
+ }
48
+ async insert(data) {
49
+ const args = [this.name];
50
+ for (const [k, v] of Object.entries(data)) {
51
+ args.push(k, String(v));
52
+ }
53
+ const result = await this.client.call('TINSERT', ...args);
54
+ return parseInt(result, 10) || 0;
55
+ }
56
+ async update(id, data) {
57
+ const args = [this.name, id];
58
+ for (const [k, v] of Object.entries(data)) {
59
+ args.push(k, String(v));
60
+ }
61
+ return this.client.call('TUPDATE', ...args);
62
+ }
63
+ async delete(...ids) {
64
+ return this.client.call('TDEL', this.name, ...ids);
65
+ }
66
+ }
67
+ class VectorNamespace {
68
+ constructor(client) {
69
+ this.client = client;
70
+ }
71
+ async set(key, vector, metadata) {
72
+ const args = [key, vector.length, ...vector];
73
+ if (metadata) {
74
+ args.push('META', JSON.stringify(metadata));
75
+ }
76
+ return this.client.call('VSET', ...args);
77
+ }
78
+ async get(key) {
79
+ return this.client.vget(key);
80
+ }
81
+ async search(query, options) {
82
+ return this.client.vsearch(query, { k: options.topK, filter: options.filter, meta: options.meta ?? true });
83
+ }
84
+ async count() {
85
+ return this.client.vcard();
86
+ }
87
+ }
88
+ class TimeSeriesNamespace {
89
+ constructor(client) {
90
+ this.client = client;
91
+ }
92
+ async add(key, value, options) {
93
+ return this.client.tsadd(key, options?.timestamp ?? '*', value, { retention: options?.retention, labels: options?.labels });
94
+ }
95
+ async get(key) {
96
+ return this.client.tsget(key);
97
+ }
98
+ async range(key, from, to, options) {
99
+ return this.client.tsrange(key, from, to, options);
100
+ }
101
+ async mrange(from, to, filter, options) {
102
+ return this.client.tsmrange(from, to, filter, options);
103
+ }
104
+ async info(key) {
105
+ return this.client.tsinfo(key);
106
+ }
107
+ }
8
108
  class Lux extends ioredis_1.default {
9
109
  constructor(options) {
10
110
  super(options);
111
+ this.vectors = new VectorNamespace(this);
112
+ this.timeseries = new TimeSeriesNamespace(this);
11
113
  }
114
+ table(name) {
115
+ return new TableQueryBuilder(this, name);
116
+ }
117
+ async _tquery(args) {
118
+ const result = await this.call('TQUERY', ...args);
119
+ if (!result || !Array.isArray(result))
120
+ return [];
121
+ const rows = [];
122
+ for (const item of result) {
123
+ if (Array.isArray(item) && item.length >= 2) {
124
+ const row = { id: 0 };
125
+ for (let i = 0; i < item.length - 1; i += 2) {
126
+ const key = String(item[i]);
127
+ const val = item[i + 1];
128
+ if (key === 'id') {
129
+ row.id = parseInt(val, 10);
130
+ }
131
+ else {
132
+ row[key] = val;
133
+ }
134
+ }
135
+ rows.push(row);
136
+ }
137
+ }
138
+ return rows;
139
+ }
140
+ // Vector methods (keep for backward compat)
12
141
  async vset(key, vector, options) {
13
142
  const args = [key, vector.length, ...vector];
14
143
  if (options?.metadata) {
@@ -23,7 +152,7 @@ class Lux extends ioredis_1.default {
23
152
  return this.call('VSET', ...args);
24
153
  }
25
154
  async vget(key) {
26
- const result = (await this.call('VGET', key));
155
+ const result = await this.call('VGET', key);
27
156
  if (!result || !Array.isArray(result))
28
157
  return null;
29
158
  const dims = parseInt(result[0], 10);
@@ -49,16 +178,13 @@ class Lux extends ioredis_1.default {
49
178
  if (options.meta) {
50
179
  args.push('META');
51
180
  }
52
- const result = (await this.call('VSEARCH', ...args));
181
+ const result = await this.call('VSEARCH', ...args);
53
182
  if (!result || !Array.isArray(result))
54
183
  return [];
55
184
  const results = [];
56
185
  for (const item of result) {
57
186
  if (Array.isArray(item)) {
58
- const entry = {
59
- key: item[0],
60
- similarity: parseFloat(item[1]),
61
- };
187
+ const entry = { key: item[0], similarity: parseFloat(item[1]) };
62
188
  if (options.meta && item[2]) {
63
189
  try {
64
190
  entry.metadata = JSON.parse(item[2]);
@@ -75,6 +201,7 @@ class Lux extends ioredis_1.default {
75
201
  async vcard() {
76
202
  return this.call('VCARD');
77
203
  }
204
+ // Time series methods (keep for backward compat)
78
205
  async tsadd(key, timestamp, value, options) {
79
206
  const args = [key, timestamp === '*' ? '*' : timestamp, value];
80
207
  if (options?.retention != null) {
@@ -96,7 +223,7 @@ class Lux extends ioredis_1.default {
96
223
  return this.call('TSMADD', ...args);
97
224
  }
98
225
  async tsget(key) {
99
- const result = (await this.call('TSGET', key));
226
+ const result = await this.call('TSGET', key);
100
227
  if (!result || !Array.isArray(result) || result.length < 2)
101
228
  return null;
102
229
  return { timestamp: parseInt(result[0], 10), value: parseFloat(result[1]) };
@@ -106,13 +233,10 @@ class Lux extends ioredis_1.default {
106
233
  if (options?.aggregation) {
107
234
  args.push('AGGREGATION', options.aggregation.type, options.aggregation.bucketSize);
108
235
  }
109
- const result = (await this.call('TSRANGE', ...args));
236
+ const result = await this.call('TSRANGE', ...args);
110
237
  if (!result || !Array.isArray(result))
111
238
  return [];
112
- return result.map((pair) => ({
113
- timestamp: parseInt(pair[0], 10),
114
- value: parseFloat(pair[1]),
115
- }));
239
+ return result.map((pair) => ({ timestamp: parseInt(pair[0], 10), value: parseFloat(pair[1]) }));
116
240
  }
117
241
  async tsmrange(from, to, filter, options) {
118
242
  const args = [from === '-' ? '-' : from, to === '+' ? '+' : to];
@@ -120,16 +244,15 @@ class Lux extends ioredis_1.default {
120
244
  args.push('AGGREGATION', options.aggregation.type, options.aggregation.bucketSize);
121
245
  }
122
246
  args.push('FILTER', filter);
123
- const result = (await this.call('TSMRANGE', ...args));
247
+ const result = await this.call('TSMRANGE', ...args);
124
248
  if (!result || !Array.isArray(result))
125
249
  return [];
126
250
  return result.map((series) => {
127
251
  const labels = {};
128
252
  if (Array.isArray(series[1])) {
129
253
  for (const pair of series[1]) {
130
- if (Array.isArray(pair) && pair.length >= 2) {
254
+ if (Array.isArray(pair) && pair.length >= 2)
131
255
  labels[pair[0]] = pair[1];
132
- }
133
256
  }
134
257
  }
135
258
  const samples = Array.isArray(series[2])
@@ -139,7 +262,7 @@ class Lux extends ioredis_1.default {
139
262
  });
140
263
  }
141
264
  async tsinfo(key) {
142
- const result = (await this.call('TSINFO', key));
265
+ const result = await this.call('TSINFO', key);
143
266
  if (!result || !Array.isArray(result))
144
267
  return {};
145
268
  const info = {};
@@ -160,10 +283,10 @@ class Lux extends ioredis_1.default {
160
283
  }
161
284
  return info;
162
285
  }
286
+ // Realtime key subscriptions
163
287
  ksub(patterns, handler) {
164
288
  const sub = this.duplicate();
165
289
  sub.on('error', () => { });
166
- const origReturnReply = sub.returnReply?.bind(sub);
167
290
  const dataHandler = sub._dataHandler || sub.dataHandler;
168
291
  if (dataHandler && dataHandler.returnReply) {
169
292
  const origReturn = dataHandler.returnReply.bind(dataHandler);
@@ -191,9 +314,7 @@ class Lux extends ioredis_1.default {
191
314
  sub.call('KSUB', ...patterns);
192
315
  return {
193
316
  connection: sub,
194
- unsubscribe() {
195
- sub.disconnect();
196
- },
317
+ unsubscribe() { sub.disconnect(); },
197
318
  };
198
319
  }
199
320
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@luxdb/sdk",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Lux SDK - ioredis extended with vector search, time series, and realtime key subscriptions",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": ["dist"],
8
8
  "scripts": {
9
9
  "build": "tsc",
10
- "prepublishOnly": "npm run build"
10
+ "prepublishOnly": "bun run build"
11
11
  },
12
12
  "dependencies": {
13
13
  "ioredis": "^5.0.0"