@luxdb/sdk 1.3.0 → 1.4.2

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.
@@ -3,26 +3,59 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Lux = exports.TableSubscription = exports.TableQueryBuilder = void 0;
6
+ exports.Lux = exports.TableSubscription = exports.TableQueryBuilder = exports.createServerClient = exports.createBrowserClient = exports.LuxProjectClient = exports.createProjectClient = void 0;
7
7
  exports.createClient = createClient;
8
+ exports.createAuthClient = createAuthClient;
8
9
  const ioredis_1 = __importDefault(require("ioredis"));
10
+ const auth_1 = require("./auth");
11
+ const project_1 = require("./project");
12
+ Object.defineProperty(exports, "createProjectClient", { enumerable: true, get: function () { return project_1.createProjectClient; } });
13
+ Object.defineProperty(exports, "LuxProjectClient", { enumerable: true, get: function () { return project_1.LuxProjectClient; } });
9
14
  const namespaces_1 = require("./namespaces");
10
15
  const realtime_1 = require("./realtime");
11
16
  const table_1 = require("./table");
17
+ var browser_1 = require("./browser");
18
+ Object.defineProperty(exports, "createBrowserClient", { enumerable: true, get: function () { return browser_1.createBrowserClient; } });
19
+ var ssr_1 = require("./ssr");
20
+ Object.defineProperty(exports, "createServerClient", { enumerable: true, get: function () { return ssr_1.createServerClient; } });
12
21
  var table_2 = require("./table");
13
22
  Object.defineProperty(exports, "TableQueryBuilder", { enumerable: true, get: function () { return table_2.TableQueryBuilder; } });
14
23
  Object.defineProperty(exports, "TableSubscription", { enumerable: true, get: function () { return table_2.TableSubscription; } });
24
+ function createAuthNamespace(redis, options) {
25
+ const client = new auth_1.LuxAuthClient(options);
26
+ const redisAuth = ((...args) => {
27
+ return redis.call('AUTH', ...args);
28
+ });
29
+ return new Proxy(redisAuth, {
30
+ get(target, prop, receiver) {
31
+ if (prop in client) {
32
+ const value = client[prop];
33
+ return typeof value === 'function' ? value.bind(client) : value;
34
+ }
35
+ return Reflect.get(target, prop, receiver);
36
+ },
37
+ set(_target, prop, value) {
38
+ client[prop] = value;
39
+ return true;
40
+ },
41
+ });
42
+ }
15
43
  class Lux extends ioredis_1.default {
16
44
  constructor(options) {
45
+ let authOptions = {};
17
46
  if (typeof options === 'string') {
18
- if (options.startsWith('rediss://') || options.startsWith('luxs://')) {
19
- throw new Error('TLS is not yet supported');
20
- }
21
- options = options.replace(/^lux:\/\//, 'redis://');
47
+ options = options.replace(/^luxs:\/\//, 'rediss://').replace(/^lux:\/\//, 'redis://');
48
+ }
49
+ else if (options) {
50
+ const { httpUrl, apiKey, authToken, fetch: fetchImpl, ...redisOptions } = options;
51
+ authOptions = { httpUrl, apiKey, authToken, fetch: fetchImpl };
52
+ options = redisOptions;
22
53
  }
23
54
  super(options);
24
55
  this.vectors = new namespaces_1.VectorNamespace(this);
25
56
  this.timeseries = new namespaces_1.TimeSeriesNamespace(this);
57
+ this.auth = createAuthNamespace(this, authOptions);
58
+ this.authApi = this.auth;
26
59
  }
27
60
  table(name, options) {
28
61
  return new table_1.TableQueryBuilder(this, name, options);
@@ -239,7 +272,13 @@ class Lux extends ioredis_1.default {
239
272
  }
240
273
  }
241
274
  exports.Lux = Lux;
242
- function createClient(options) {
243
- return new Lux(options);
275
+ function createClient(optionsOrUrl, key, projectOptions) {
276
+ if (typeof optionsOrUrl === 'string' && typeof key === 'string') {
277
+ return (0, project_1.createProjectClient)({ ...(projectOptions ?? {}), url: optionsOrUrl, key });
278
+ }
279
+ return new Lux(optionsOrUrl);
280
+ }
281
+ function createAuthClient(options) {
282
+ return new auth_1.LuxAuthClient(options);
244
283
  }
245
284
  exports.default = Lux;
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,313 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LuxProjectMutationBuilder = exports.LuxProjectInsertBuilder = exports.LuxProjectSelectBuilder = exports.LuxProjectTable = exports.LuxProjectClient = void 0;
4
+ exports.createProjectClient = createProjectClient;
5
+ exports.createClient = createClient;
6
+ const auth_1 = require("./auth");
7
+ const utils_1 = require("./utils");
8
+ class LuxProjectClient {
9
+ constructor(options) {
10
+ this.url = options.url.replace(/\/+$/, '');
11
+ this.key = options.key;
12
+ this.fetchImpl = resolveFetch(options.fetch);
13
+ this.auth = new auth_1.LuxAuthClient({
14
+ ...options.auth,
15
+ httpUrl: this.url,
16
+ apiKey: this.key,
17
+ fetch: this.fetchImpl,
18
+ });
19
+ }
20
+ table(name) {
21
+ return new LuxProjectTable(this, name);
22
+ }
23
+ async ping() {
24
+ return this.request('GET', '/ping');
25
+ }
26
+ async createTable(name, columns) {
27
+ return this.request('POST', '/tables', { name, columns });
28
+ }
29
+ async exec(command) {
30
+ return this.request('POST', '/exec', { command });
31
+ }
32
+ async vectorSet(key, vector, metadata) {
33
+ return this.request('POST', `/vectors/${encodeURIComponent(key)}`, { vector, metadata });
34
+ }
35
+ async vectorSearch(options) {
36
+ return this.request('POST', '/vectors/search', {
37
+ vector: options.vector,
38
+ k: options.k ?? 10,
39
+ filter: options.filter,
40
+ filter_value: options.filter_value,
41
+ });
42
+ }
43
+ async tsAdd(key, value, options) {
44
+ return this.request('POST', `/ts/${encodeURIComponent(key)}`, {
45
+ timestamp: options?.timestamp ?? '*',
46
+ value,
47
+ labels: options?.labels,
48
+ retention: options?.retention,
49
+ });
50
+ }
51
+ async tsRange(key, options) {
52
+ const params = new URLSearchParams();
53
+ if (options?.from != null)
54
+ params.set('from', String(options.from));
55
+ if (options?.to != null)
56
+ params.set('to', String(options.to));
57
+ if (options?.count != null)
58
+ params.set('count', String(options.count));
59
+ const query = params.toString();
60
+ return this.request('GET', `/ts/${encodeURIComponent(key)}${query ? `?${query}` : ''}`);
61
+ }
62
+ async request(method, path, body) {
63
+ try {
64
+ const accessToken = await this.auth.getAccessToken();
65
+ const headers = {
66
+ Accept: 'application/json',
67
+ apikey: this.key,
68
+ Authorization: `Bearer ${accessToken ?? this.key}`,
69
+ };
70
+ const init = { method, headers };
71
+ if (body !== undefined) {
72
+ headers['Content-Type'] = 'application/json';
73
+ init.body = JSON.stringify(body);
74
+ }
75
+ const response = await this.fetchImpl(`${this.url}${path}`, init);
76
+ const text = await response.text();
77
+ const payload = text ? JSON.parse(text) : {};
78
+ if (!response.ok) {
79
+ return (0, utils_1.err)('LUX_PROJECT_REQUEST_ERROR', payload?.error || `Lux request failed with HTTP ${response.status}`, { status: response.status, payload });
80
+ }
81
+ return (0, utils_1.ok)(payload);
82
+ }
83
+ catch (error) {
84
+ return (0, utils_1.err)('LUX_PROJECT_REQUEST_ERROR', 'Lux request failed', (0, utils_1.toLuxError)(error));
85
+ }
86
+ }
87
+ }
88
+ exports.LuxProjectClient = LuxProjectClient;
89
+ class LuxProjectTable {
90
+ constructor(client, name) {
91
+ this.client = client;
92
+ this.name = name;
93
+ }
94
+ select(columns = '*') {
95
+ return new LuxProjectSelectBuilder(this.client, this.name, columns);
96
+ }
97
+ insert(rowOrRows) {
98
+ return new LuxProjectInsertBuilder(this.client, this.name, rowOrRows);
99
+ }
100
+ update(patch) {
101
+ return new LuxProjectMutationBuilder(this.client, this.name, 'PATCH', patch);
102
+ }
103
+ delete() {
104
+ return new LuxProjectMutationBuilder(this.client, this.name, 'DELETE');
105
+ }
106
+ async count() {
107
+ const result = await this.client.request('GET', `/tables/${encodeURIComponent(this.name)}/count`);
108
+ if (result.error)
109
+ return result;
110
+ return (0, utils_1.ok)(unwrapResult(result.data) ?? 0);
111
+ }
112
+ }
113
+ exports.LuxProjectTable = LuxProjectTable;
114
+ class LuxProjectThenable {
115
+ then(onfulfilled, onrejected) {
116
+ return this.execute().then(onfulfilled, onrejected);
117
+ }
118
+ catch(onrejected) {
119
+ return this.execute().catch(onrejected);
120
+ }
121
+ finally(onfinally) {
122
+ return this.execute().finally(onfinally ?? undefined);
123
+ }
124
+ }
125
+ class LuxProjectFilterBuilder extends LuxProjectThenable {
126
+ constructor(client, tableName) {
127
+ super();
128
+ this.client = client;
129
+ this.tableName = tableName;
130
+ this.filters = [];
131
+ }
132
+ eq(column, value) {
133
+ return this.addFilter(column, 'eq', value);
134
+ }
135
+ neq(column, value) {
136
+ return this.addFilter(column, 'neq', value);
137
+ }
138
+ gt(column, value) {
139
+ return this.addFilter(column, 'gt', value);
140
+ }
141
+ gte(column, value) {
142
+ return this.addFilter(column, 'gte', value);
143
+ }
144
+ lt(column, value) {
145
+ return this.addFilter(column, 'lt', value);
146
+ }
147
+ lte(column, value) {
148
+ return this.addFilter(column, 'lte', value);
149
+ }
150
+ is(column, value) {
151
+ return this.addFilter(column, 'is', value);
152
+ }
153
+ addFilter(column, operator, value) {
154
+ this.filters.push({ column, operator, value });
155
+ return this;
156
+ }
157
+ filteredQueryParams() {
158
+ const params = new URLSearchParams();
159
+ if (this.filters.length)
160
+ params.set('where', filtersToWhere(this.filters));
161
+ if (this.orderBy) {
162
+ params.set('order', `${this.orderBy.column} ${this.orderBy.ascending ? 'ASC' : 'DESC'}`);
163
+ }
164
+ if (this.limitCount != null)
165
+ params.set('limit', String(this.limitCount));
166
+ if (this.offsetCount != null)
167
+ params.set('offset', String(this.offsetCount));
168
+ return params;
169
+ }
170
+ }
171
+ class LuxProjectSelectBuilder extends LuxProjectFilterBuilder {
172
+ constructor(client, tableName, columns) {
173
+ super(client, tableName);
174
+ this.columns = columns;
175
+ this.expectSingle = false;
176
+ }
177
+ order(column, options = {}) {
178
+ this.orderBy = { column, ascending: options.ascending ?? true };
179
+ return this;
180
+ }
181
+ limit(count) {
182
+ this.limitCount = count;
183
+ return this;
184
+ }
185
+ range(from, to) {
186
+ this.offsetCount = from;
187
+ this.limitCount = Math.max(0, to - from + 1);
188
+ return this;
189
+ }
190
+ single() {
191
+ this.expectSingle = true;
192
+ if (this.limitCount == null)
193
+ this.limitCount = 1;
194
+ return this;
195
+ }
196
+ async execute() {
197
+ const params = this.filteredQueryParams();
198
+ if (this.columns && this.columns !== '*')
199
+ params.set('select', this.columns);
200
+ const query = params.toString();
201
+ const result = await this.client.request('GET', `/tables/${encodeURIComponent(this.tableName)}${query ? `?${query}` : ''}`);
202
+ if (result.error)
203
+ return result;
204
+ const rows = unwrapRows(result.data);
205
+ if (!this.expectSingle) {
206
+ return (0, utils_1.ok)(rows);
207
+ }
208
+ if (rows.length === 0) {
209
+ return (0, utils_1.err)('NOT_FOUND', `No rows found in table '${this.tableName}'`);
210
+ }
211
+ return (0, utils_1.ok)(rows[0]);
212
+ }
213
+ }
214
+ exports.LuxProjectSelectBuilder = LuxProjectSelectBuilder;
215
+ class LuxProjectInsertBuilder extends LuxProjectThenable {
216
+ constructor(client, tableName, rowOrRows) {
217
+ super();
218
+ this.client = client;
219
+ this.tableName = tableName;
220
+ this.rowOrRows = rowOrRows;
221
+ }
222
+ async execute() {
223
+ if (!Array.isArray(this.rowOrRows)) {
224
+ return this.client.request('POST', `/tables/${encodeURIComponent(this.tableName)}`, this.rowOrRows);
225
+ }
226
+ const results = [];
227
+ for (const row of this.rowOrRows) {
228
+ const result = await this.client.request('POST', `/tables/${encodeURIComponent(this.tableName)}`, row);
229
+ if (result.error)
230
+ return result;
231
+ results.push(result.data);
232
+ }
233
+ return (0, utils_1.ok)(results);
234
+ }
235
+ }
236
+ exports.LuxProjectInsertBuilder = LuxProjectInsertBuilder;
237
+ class LuxProjectMutationBuilder extends LuxProjectFilterBuilder {
238
+ constructor(client, tableName, method, body) {
239
+ super(client, tableName);
240
+ this.method = method;
241
+ this.body = body;
242
+ }
243
+ async execute() {
244
+ if (this.filters.length === 0) {
245
+ return (0, utils_1.err)('MISSING_FILTER', `${this.method === 'PATCH' ? 'update' : 'delete'}() requires at least one filter`);
246
+ }
247
+ const params = this.filteredQueryParams();
248
+ const query = params.toString();
249
+ return this.client.request(this.method, `/tables/${encodeURIComponent(this.tableName)}${query ? `?${query}` : ''}`, this.body);
250
+ }
251
+ }
252
+ exports.LuxProjectMutationBuilder = LuxProjectMutationBuilder;
253
+ function unwrapRows(payload) {
254
+ if (Array.isArray(payload))
255
+ return payload;
256
+ if (payload && typeof payload === 'object' && Array.isArray(payload.result)) {
257
+ return payload.result;
258
+ }
259
+ return [];
260
+ }
261
+ function unwrapResult(payload) {
262
+ if (payload && typeof payload === 'object' && 'result' in payload) {
263
+ return payload.result;
264
+ }
265
+ return payload;
266
+ }
267
+ function normalizeWhere(where) {
268
+ return where.trim().replace(/\s*(>=|<=|!=|=|>|<)\s*/g, ' $1 ');
269
+ }
270
+ function filtersToWhere(filters) {
271
+ return filters.map((filter) => {
272
+ const op = filterOperatorToWhere(filter.operator);
273
+ return normalizeWhere(`${filter.column} ${op} ${formatWhereValue(filter.value)}`);
274
+ }).join(' AND ');
275
+ }
276
+ function filterOperatorToWhere(operator) {
277
+ switch (operator) {
278
+ case 'eq':
279
+ case 'is':
280
+ return '=';
281
+ case 'neq':
282
+ return '!=';
283
+ case 'gt':
284
+ return '>';
285
+ case 'gte':
286
+ return '>=';
287
+ case 'lt':
288
+ return '<';
289
+ case 'lte':
290
+ return '<=';
291
+ }
292
+ }
293
+ function formatWhereValue(value) {
294
+ if (value === null)
295
+ return '';
296
+ return String(value);
297
+ }
298
+ function createProjectClient(options) {
299
+ return new LuxProjectClient(options);
300
+ }
301
+ function createClient(url, key, options = {}) {
302
+ return new LuxProjectClient({ ...options, url, key });
303
+ }
304
+ function resolveFetch(fetchImpl) {
305
+ const candidate = fetchImpl ?? globalThis.fetch;
306
+ if (!candidate) {
307
+ throw new Error('Lux project client requires a fetch implementation');
308
+ }
309
+ if (typeof globalThis !== 'undefined' && candidate === globalThis.fetch) {
310
+ return candidate.bind(globalThis);
311
+ }
312
+ return candidate;
313
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createServerClient = createServerClient;
4
+ const project_1 = require("./project");
5
+ const DEFAULT_COOKIE = 'lux-auth-session';
6
+ function createServerClient(url, key, options) {
7
+ const storageKey = options.auth?.storageKey ?? DEFAULT_COOKIE;
8
+ const cookieOptions = options.auth?.cookieOptions ?? {
9
+ path: '/',
10
+ sameSite: 'lax',
11
+ };
12
+ const { cookieOptions: _cookieOptions, ...authOptions } = options.auth ?? {};
13
+ return (0, project_1.createClient)(url, key, {
14
+ fetch: options.fetch,
15
+ auth: {
16
+ persistSession: true,
17
+ autoRefreshToken: false,
18
+ ...authOptions,
19
+ storageKey,
20
+ storage: cookieStorage(options.cookies, cookieOptions),
21
+ },
22
+ });
23
+ }
24
+ function cookieStorage(cookies, options) {
25
+ return {
26
+ async getItem(key) {
27
+ return await cookies.get(key) ?? null;
28
+ },
29
+ async setItem(key, value) {
30
+ if (!cookies.set)
31
+ return;
32
+ await cookies.set(key, value, options);
33
+ },
34
+ async removeItem(key) {
35
+ if (!cookies.remove)
36
+ return;
37
+ await cookies.remove(key, { ...options, maxAge: 0, expires: new Date(0) });
38
+ },
39
+ };
40
+ }
@@ -195,11 +195,32 @@ class TableQueryBuilder {
195
195
  this.conditions.push({ field, op, value });
196
196
  return this;
197
197
  }
198
+ eq(field, value) {
199
+ return this.where(field, '=', value);
200
+ }
201
+ neq(field, value) {
202
+ return this.where(field, '!=', value);
203
+ }
204
+ gt(field, value) {
205
+ return this.where(field, '>', value);
206
+ }
207
+ gte(field, value) {
208
+ return this.where(field, '>=', value);
209
+ }
210
+ lt(field, value) {
211
+ return this.where(field, '<', value);
212
+ }
213
+ lte(field, value) {
214
+ return this.where(field, '<=', value);
215
+ }
198
216
  orderBy(field, dir = 'asc') {
199
217
  this.orderField = field;
200
218
  this.orderDir = dir.toUpperCase();
201
219
  return this;
202
220
  }
221
+ order(field, options = {}) {
222
+ return this.orderBy(field, options.ascending === false ? 'desc' : 'asc');
223
+ }
203
224
  limit(n) {
204
225
  this.limitCount = n;
205
226
  return this;
@@ -284,6 +305,9 @@ class TableQueryBuilder {
284
305
  return (0, utils_1.err)('TSELECT_ERROR', `Failed to query table '${this.name}'`, (0, utils_1.toLuxError)(error));
285
306
  }
286
307
  }
308
+ then(onfulfilled, onrejected) {
309
+ return this.run().then(onfulfilled, onrejected);
310
+ }
287
311
  async insert(data) {
288
312
  try {
289
313
  if (this.schema) {
@@ -300,35 +324,28 @@ class TableQueryBuilder {
300
324
  return (0, utils_1.err)('TINSERT_ERROR', `Failed to insert into '${this.name}'`, (0, utils_1.toLuxError)(error));
301
325
  }
302
326
  }
303
- async update(id, data) {
327
+ async update(idOrData, data) {
304
328
  try {
305
- const args = [this.name, 'SET'];
306
- for (const [k, v] of Object.entries(data)) {
307
- args.push(k, String(v));
329
+ const hasExplicitId = data !== undefined;
330
+ const patch = hasExplicitId ? data : idOrData;
331
+ if (!hasExplicitId && this.conditions.length === 0) {
332
+ return (0, utils_1.err)('MISSING_WHERE', 'update requires at least one filter');
308
333
  }
309
- args.push('WHERE', 'id', '=', String(id));
310
- const result = await this.client.call('TUPDATE', ...args);
311
- return (0, utils_1.ok)(Number(result) || 0);
312
- }
313
- catch (error) {
314
- return (0, utils_1.err)('TUPDATE_ERROR', `Failed to update '${this.name}'`, (0, utils_1.toLuxError)(error));
315
- }
316
- }
317
- async updateWhere(data) {
318
- if (this.conditions.length === 0) {
319
- return (0, utils_1.err)('MISSING_WHERE', 'updateWhere requires at least one where() condition');
320
- }
321
- try {
322
334
  const args = [this.name, 'SET'];
323
- for (const [k, v] of Object.entries(data)) {
335
+ for (const [k, v] of Object.entries(patch)) {
324
336
  args.push(k, String(v));
325
337
  }
326
338
  args.push('WHERE');
327
- for (let i = 0; i < this.conditions.length; i++) {
328
- const cond = this.conditions[i];
329
- args.push(cond.field, cond.op, String(cond.value));
330
- if (i < this.conditions.length - 1) {
331
- args.push('AND');
339
+ if (hasExplicitId) {
340
+ args.push('id', '=', String(idOrData));
341
+ }
342
+ else {
343
+ for (let i = 0; i < this.conditions.length; i++) {
344
+ const cond = this.conditions[i];
345
+ args.push(cond.field, cond.op, String(cond.value));
346
+ if (i < this.conditions.length - 1) {
347
+ args.push('AND');
348
+ }
332
349
  }
333
350
  }
334
351
  const result = await this.client.call('TUPDATE', ...args);
@@ -340,6 +357,21 @@ class TableQueryBuilder {
340
357
  }
341
358
  async delete(...ids) {
342
359
  try {
360
+ if (ids.length === 0) {
361
+ if (this.conditions.length === 0) {
362
+ return (0, utils_1.err)('MISSING_WHERE', 'delete requires at least one filter');
363
+ }
364
+ const args = ['FROM', this.name, 'WHERE'];
365
+ for (let i = 0; i < this.conditions.length; i++) {
366
+ const cond = this.conditions[i];
367
+ args.push(cond.field, cond.op, String(cond.value));
368
+ if (i < this.conditions.length - 1) {
369
+ args.push('AND');
370
+ }
371
+ }
372
+ const result = await this.client.call('TDELETE', ...args);
373
+ return (0, utils_1.ok)(Number(result) || 0);
374
+ }
343
375
  let deleted = 0;
344
376
  for (const id of ids) {
345
377
  const result = await this.client.call('TDELETE', 'FROM', this.name, 'WHERE', 'id', '=', String(id));
@@ -351,26 +383,6 @@ class TableQueryBuilder {
351
383
  return (0, utils_1.err)('TDELETE_ERROR', `Failed to delete from '${this.name}'`, (0, utils_1.toLuxError)(error));
352
384
  }
353
385
  }
354
- async deleteWhere() {
355
- if (this.conditions.length === 0) {
356
- return (0, utils_1.err)('MISSING_WHERE', 'deleteWhere requires at least one where() condition');
357
- }
358
- try {
359
- const args = ['FROM', this.name, 'WHERE'];
360
- for (let i = 0; i < this.conditions.length; i++) {
361
- const cond = this.conditions[i];
362
- args.push(cond.field, cond.op, String(cond.value));
363
- if (i < this.conditions.length - 1) {
364
- args.push('AND');
365
- }
366
- }
367
- const result = await this.client.call('TDELETE', ...args);
368
- return (0, utils_1.ok)(Number(result) || 0);
369
- }
370
- catch (error) {
371
- return (0, utils_1.err)('TDELETE_ERROR', `Failed to delete from '${this.name}'`, (0, utils_1.toLuxError)(error));
372
- }
373
- }
374
386
  subscribe() {
375
387
  if (this.similarityClause) {
376
388
  return new TableSubscription(this.client, this.name, (extra) => this.buildSelectArgs(extra), {