@luxdb/sdk 1.2.1 → 1.4.1
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/cjs/auth.js +504 -0
- package/dist/cjs/browser.js +14 -0
- package/dist/{index.js → cjs/index.js} +73 -109
- package/dist/cjs/namespaces.js +46 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/project.js +313 -0
- package/dist/cjs/realtime.js +88 -0
- package/dist/cjs/ssr.js +40 -0
- package/dist/cjs/table.js +396 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/utils.js +17 -0
- package/dist/esm/auth.js +500 -0
- package/dist/esm/browser.js +11 -0
- package/dist/esm/index.js +273 -0
- package/dist/esm/namespaces.js +41 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/project.js +303 -0
- package/dist/esm/realtime.js +84 -0
- package/dist/esm/ssr.js +37 -0
- package/dist/esm/table.js +391 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils.js +12 -0
- package/dist/types/auth.d.ts +163 -0
- package/dist/types/browser.d.ts +4 -0
- package/dist/types/index.d.ts +62 -0
- package/dist/types/namespaces.d.ts +56 -0
- package/dist/types/project.d.ts +121 -0
- package/dist/types/realtime.d.ts +16 -0
- package/dist/types/ssr.d.ts +22 -0
- package/dist/types/table.d.ts +92 -0
- package/dist/types/types.d.ts +70 -0
- package/dist/types/utils.d.ts +4 -0
- package/package.json +38 -4
- package/dist/index.d.ts +0 -121
package/dist/cjs/ssr.js
ADDED
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TableQueryBuilder = exports.TableSubscription = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
class TableSubscription {
|
|
6
|
+
constructor(client, table, selectArgsBuilder, initError = null) {
|
|
7
|
+
this.handlers = {
|
|
8
|
+
change: [],
|
|
9
|
+
insert: [],
|
|
10
|
+
update: [],
|
|
11
|
+
delete: [],
|
|
12
|
+
error: [],
|
|
13
|
+
};
|
|
14
|
+
this.knownRows = new Map();
|
|
15
|
+
this.unsubscribeFn = null;
|
|
16
|
+
this.client = client;
|
|
17
|
+
this.table = table;
|
|
18
|
+
this.selectArgsBuilder = selectArgsBuilder;
|
|
19
|
+
this.initError = initError;
|
|
20
|
+
void this.start();
|
|
21
|
+
}
|
|
22
|
+
on(event, handler) {
|
|
23
|
+
this.handlers[event].push(handler);
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
async unsubscribe() {
|
|
27
|
+
if (this.unsubscribeFn) {
|
|
28
|
+
this.unsubscribeFn();
|
|
29
|
+
this.unsubscribeFn = null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
emitError(error) {
|
|
33
|
+
for (const handler of this.handlers.error) {
|
|
34
|
+
handler({ type: 'error', table: this.table, error });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
emitChange(event) {
|
|
38
|
+
for (const handler of this.handlers.change)
|
|
39
|
+
handler(event);
|
|
40
|
+
for (const handler of this.handlers[event.type])
|
|
41
|
+
handler(event);
|
|
42
|
+
}
|
|
43
|
+
extractPkFromKey(key) {
|
|
44
|
+
const prefix = `_t:${this.table}:row:`;
|
|
45
|
+
if (!key.startsWith(prefix))
|
|
46
|
+
return null;
|
|
47
|
+
return key.slice(prefix.length);
|
|
48
|
+
}
|
|
49
|
+
async fetchMatches(extra) {
|
|
50
|
+
const args = this.selectArgsBuilder(extra);
|
|
51
|
+
const rows = await this.client._tselect(args);
|
|
52
|
+
return rows;
|
|
53
|
+
}
|
|
54
|
+
async start() {
|
|
55
|
+
if (this.initError) {
|
|
56
|
+
this.emitError(this.initError);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const initial = await this.fetchMatches();
|
|
61
|
+
for (const row of initial) {
|
|
62
|
+
if (row.id == null)
|
|
63
|
+
continue;
|
|
64
|
+
this.knownRows.set(String(row.id), row);
|
|
65
|
+
}
|
|
66
|
+
const pattern = `_t:${this.table}:row:*`;
|
|
67
|
+
this.unsubscribeFn = await this.client._subscribePattern(pattern, (raw) => {
|
|
68
|
+
void this.handleRawChange(raw);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
this.emitError((0, utils_1.toLuxError)(error, 'LUX_SUBSCRIBE_INIT_ERROR'));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async handleRawChange(raw) {
|
|
76
|
+
const pk = this.extractPkFromKey(raw.key);
|
|
77
|
+
if (!pk)
|
|
78
|
+
return;
|
|
79
|
+
try {
|
|
80
|
+
const previous = this.knownRows.get(pk) ?? null;
|
|
81
|
+
const rows = await this.fetchMatches([{ field: 'id', op: '=', value: pk }]);
|
|
82
|
+
const next = rows[0] ?? null;
|
|
83
|
+
if (!previous && !next)
|
|
84
|
+
return;
|
|
85
|
+
if (!previous && next) {
|
|
86
|
+
this.knownRows.set(pk, next);
|
|
87
|
+
this.emitChange({
|
|
88
|
+
type: 'insert',
|
|
89
|
+
table: this.table,
|
|
90
|
+
pk,
|
|
91
|
+
operation: raw.operation,
|
|
92
|
+
new: next,
|
|
93
|
+
old: null,
|
|
94
|
+
raw,
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (previous && !next) {
|
|
99
|
+
this.knownRows.delete(pk);
|
|
100
|
+
this.emitChange({
|
|
101
|
+
type: 'delete',
|
|
102
|
+
table: this.table,
|
|
103
|
+
pk,
|
|
104
|
+
operation: raw.operation,
|
|
105
|
+
new: null,
|
|
106
|
+
old: previous,
|
|
107
|
+
raw,
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (!previous || !next)
|
|
112
|
+
return;
|
|
113
|
+
this.knownRows.set(pk, next);
|
|
114
|
+
const changed = Object.keys(next).filter((key) => previous[key] !== next[key]);
|
|
115
|
+
this.emitChange({
|
|
116
|
+
type: 'update',
|
|
117
|
+
table: this.table,
|
|
118
|
+
pk,
|
|
119
|
+
operation: raw.operation,
|
|
120
|
+
new: next,
|
|
121
|
+
old: previous,
|
|
122
|
+
changed,
|
|
123
|
+
raw,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
this.emitError((0, utils_1.toLuxError)(error, 'LUX_SUBSCRIBE_EVENT_ERROR'));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
exports.TableSubscription = TableSubscription;
|
|
132
|
+
class TableQueryBuilder {
|
|
133
|
+
constructor(client, name, options) {
|
|
134
|
+
this.conditions = [];
|
|
135
|
+
this.selectClause = '*';
|
|
136
|
+
this.expectSingle = false;
|
|
137
|
+
this.client = client;
|
|
138
|
+
this.name = name;
|
|
139
|
+
this.schema = options?.schema;
|
|
140
|
+
}
|
|
141
|
+
validateRow(row) {
|
|
142
|
+
if (!this.schema)
|
|
143
|
+
return row;
|
|
144
|
+
if (this.schema.safeParse) {
|
|
145
|
+
const parsed = this.schema.safeParse(row);
|
|
146
|
+
if (!parsed.success) {
|
|
147
|
+
throw new Error('row failed schema validation');
|
|
148
|
+
}
|
|
149
|
+
return parsed.data;
|
|
150
|
+
}
|
|
151
|
+
if (this.schema.parse) {
|
|
152
|
+
return this.schema.parse(row);
|
|
153
|
+
}
|
|
154
|
+
return row;
|
|
155
|
+
}
|
|
156
|
+
buildSelectArgs(extra) {
|
|
157
|
+
const args = [this.selectClause, 'FROM', this.name];
|
|
158
|
+
const allConditions = extra ? [...this.conditions, ...extra] : this.conditions;
|
|
159
|
+
if (this.joinClause) {
|
|
160
|
+
args.push('JOIN', this.joinClause.table, this.joinClause.alias, 'ON', this.joinClause.onLeft, '=', this.joinClause.onRight);
|
|
161
|
+
}
|
|
162
|
+
if (allConditions.length) {
|
|
163
|
+
args.push('WHERE');
|
|
164
|
+
for (let i = 0; i < allConditions.length; i++) {
|
|
165
|
+
const cond = allConditions[i];
|
|
166
|
+
args.push(cond.field, cond.op, String(cond.value));
|
|
167
|
+
if (i < allConditions.length - 1) {
|
|
168
|
+
args.push('AND');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (this.orderField) {
|
|
173
|
+
args.push('ORDER', 'BY', this.orderField, this.orderDir || 'ASC');
|
|
174
|
+
}
|
|
175
|
+
if (this.limitCount != null) {
|
|
176
|
+
args.push('LIMIT', String(this.limitCount));
|
|
177
|
+
}
|
|
178
|
+
if (this.offsetCount != null) {
|
|
179
|
+
args.push('OFFSET', String(this.offsetCount));
|
|
180
|
+
}
|
|
181
|
+
return args;
|
|
182
|
+
}
|
|
183
|
+
select(columns = '*') {
|
|
184
|
+
this.selectClause = columns;
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
single() {
|
|
188
|
+
this.expectSingle = true;
|
|
189
|
+
if (this.limitCount == null) {
|
|
190
|
+
this.limitCount = 1;
|
|
191
|
+
}
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
where(field, op, value) {
|
|
195
|
+
this.conditions.push({ field, op, value });
|
|
196
|
+
return this;
|
|
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
|
+
}
|
|
216
|
+
orderBy(field, dir = 'asc') {
|
|
217
|
+
this.orderField = field;
|
|
218
|
+
this.orderDir = dir.toUpperCase();
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
order(field, options = {}) {
|
|
222
|
+
return this.orderBy(field, options.ascending === false ? 'desc' : 'asc');
|
|
223
|
+
}
|
|
224
|
+
limit(n) {
|
|
225
|
+
this.limitCount = n;
|
|
226
|
+
return this;
|
|
227
|
+
}
|
|
228
|
+
offset(n) {
|
|
229
|
+
this.offsetCount = n;
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
join(table, alias, onLeft, onRight) {
|
|
233
|
+
this.joinClause = { table, alias, onLeft, onRight };
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
similar(field, vector, options) {
|
|
237
|
+
this.similarityClause = {
|
|
238
|
+
field,
|
|
239
|
+
vector,
|
|
240
|
+
k: options.k,
|
|
241
|
+
filter: options.filter,
|
|
242
|
+
};
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
parseSimilarityPk(result, field) {
|
|
246
|
+
const metadata = result.metadata;
|
|
247
|
+
if (metadata && typeof metadata === 'object') {
|
|
248
|
+
for (const key of ['id', 'pk', 'row_id']) {
|
|
249
|
+
const value = metadata[key];
|
|
250
|
+
if (value != null)
|
|
251
|
+
return String(value);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const expectedPrefix = `${this.name}:${field}:`;
|
|
255
|
+
if (result.key.startsWith(expectedPrefix)) {
|
|
256
|
+
return result.key.slice(expectedPrefix.length);
|
|
257
|
+
}
|
|
258
|
+
const segments = result.key.split(':');
|
|
259
|
+
if (segments.length > 0) {
|
|
260
|
+
return segments[segments.length - 1] || null;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
async run() {
|
|
265
|
+
try {
|
|
266
|
+
let rows = [];
|
|
267
|
+
if (this.similarityClause) {
|
|
268
|
+
if (this.joinClause) {
|
|
269
|
+
return (0, utils_1.err)('SIMILAR_JOIN_UNSUPPORTED', 'similar(...) cannot be combined with join(...) yet');
|
|
270
|
+
}
|
|
271
|
+
const similarResults = await this.client.vsearch(this.similarityClause.vector, {
|
|
272
|
+
k: this.similarityClause.k,
|
|
273
|
+
filter: this.similarityClause.filter,
|
|
274
|
+
meta: true,
|
|
275
|
+
});
|
|
276
|
+
for (const match of similarResults) {
|
|
277
|
+
const pk = this.parseSimilarityPk(match, this.similarityClause.field);
|
|
278
|
+
if (!pk)
|
|
279
|
+
continue;
|
|
280
|
+
const args = this.buildSelectArgs([{ field: 'id', op: '=', value: pk }]);
|
|
281
|
+
const one = await this.client._tselect(args);
|
|
282
|
+
if (one.length === 0)
|
|
283
|
+
continue;
|
|
284
|
+
rows.push({ ...one[0], _similarity: match.similarity });
|
|
285
|
+
}
|
|
286
|
+
if (this.offsetCount != null || this.limitCount != null) {
|
|
287
|
+
const start = this.offsetCount ?? 0;
|
|
288
|
+
const end = this.limitCount != null ? start + this.limitCount : undefined;
|
|
289
|
+
rows = rows.slice(start, end);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
rows = await this.client._tselect(this.buildSelectArgs());
|
|
294
|
+
}
|
|
295
|
+
const validated = rows.map((row) => this.validateRow(row));
|
|
296
|
+
if (this.expectSingle) {
|
|
297
|
+
if (validated.length === 0) {
|
|
298
|
+
return (0, utils_1.err)('NOT_FOUND', `No rows found in table '${this.name}'`);
|
|
299
|
+
}
|
|
300
|
+
return (0, utils_1.ok)(validated[0]);
|
|
301
|
+
}
|
|
302
|
+
return (0, utils_1.ok)(validated);
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
return (0, utils_1.err)('TSELECT_ERROR', `Failed to query table '${this.name}'`, (0, utils_1.toLuxError)(error));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
then(onfulfilled, onrejected) {
|
|
309
|
+
return this.run().then(onfulfilled, onrejected);
|
|
310
|
+
}
|
|
311
|
+
async insert(data) {
|
|
312
|
+
try {
|
|
313
|
+
if (this.schema) {
|
|
314
|
+
this.validateRow(data);
|
|
315
|
+
}
|
|
316
|
+
const args = [this.name];
|
|
317
|
+
for (const [k, v] of Object.entries(data)) {
|
|
318
|
+
args.push(k, String(v));
|
|
319
|
+
}
|
|
320
|
+
const result = await this.client.call('TINSERT', ...args);
|
|
321
|
+
return (0, utils_1.ok)(parseInt(result, 10) || 0);
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
return (0, utils_1.err)('TINSERT_ERROR', `Failed to insert into '${this.name}'`, (0, utils_1.toLuxError)(error));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
async update(idOrData, data) {
|
|
328
|
+
try {
|
|
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');
|
|
333
|
+
}
|
|
334
|
+
const args = [this.name, 'SET'];
|
|
335
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
336
|
+
args.push(k, String(v));
|
|
337
|
+
}
|
|
338
|
+
args.push('WHERE');
|
|
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
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const result = await this.client.call('TUPDATE', ...args);
|
|
352
|
+
return (0, utils_1.ok)(Number(result) || 0);
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
return (0, utils_1.err)('TUPDATE_ERROR', `Failed to update '${this.name}'`, (0, utils_1.toLuxError)(error));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async delete(...ids) {
|
|
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
|
+
}
|
|
375
|
+
let deleted = 0;
|
|
376
|
+
for (const id of ids) {
|
|
377
|
+
const result = await this.client.call('TDELETE', 'FROM', this.name, 'WHERE', 'id', '=', String(id));
|
|
378
|
+
deleted += Number(result) || 0;
|
|
379
|
+
}
|
|
380
|
+
return (0, utils_1.ok)(deleted);
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
return (0, utils_1.err)('TDELETE_ERROR', `Failed to delete from '${this.name}'`, (0, utils_1.toLuxError)(error));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
subscribe() {
|
|
387
|
+
if (this.similarityClause) {
|
|
388
|
+
return new TableSubscription(this.client, this.name, (extra) => this.buildSelectArgs(extra), {
|
|
389
|
+
code: 'SIMILAR_SUBSCRIBE_UNSUPPORTED',
|
|
390
|
+
message: 'subscribe() is not supported on similar(...) queries yet',
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return new TableSubscription(this.client, this.name, (extra) => this.buildSelectArgs(extra));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
exports.TableQueryBuilder = TableQueryBuilder;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ok = ok;
|
|
4
|
+
exports.err = err;
|
|
5
|
+
exports.toLuxError = toLuxError;
|
|
6
|
+
function ok(data) {
|
|
7
|
+
return { data, error: null };
|
|
8
|
+
}
|
|
9
|
+
function err(code, message, details) {
|
|
10
|
+
return { data: null, error: { code, message, details } };
|
|
11
|
+
}
|
|
12
|
+
function toLuxError(error, fallbackCode = 'LUX_SDK_ERROR') {
|
|
13
|
+
if (typeof error === 'object' && error && 'message' in error) {
|
|
14
|
+
return { code: fallbackCode, message: String(error.message), details: error };
|
|
15
|
+
}
|
|
16
|
+
return { code: fallbackCode, message: String(error) };
|
|
17
|
+
}
|