@signaldb/svelte 2.0.0-beta.18 → 2.0.0-beta.19

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.
@@ -10,7 +10,8 @@ const modify_1 = __importDefault(require("./utils/modify"));
10
10
  const queryId_1 = __importDefault(require("./utils/queryId"));
11
11
  const isEqual_1 = __importDefault(require("./utils/isEqual"));
12
12
  const getIndexInfo_1 = __importDefault(require("./getIndexInfo"));
13
- const getMatchingKeys_1 = __importDefault(require("./utils/getMatchingKeys"));
13
+ const storageIndexQuery_1 = __importDefault(require("./utils/storageIndexQuery"));
14
+ const idIndexQuery_1 = __importDefault(require("./utils/idIndexQuery"));
14
15
  const sortItems_1 = __importDefault(require("./utils/sortItems"));
15
16
  const projectItems_1 = __importDefault(require("./utils/projectItems"));
16
17
  const incrementalQueryUpdate_1 = __importDefault(require("./utils/incrementalQueryUpdate"));
@@ -343,15 +344,15 @@ class AsyncDataAdapter {
343
344
  const storageAdapter = this.storageAdapters.get(collectionName);
344
345
  if (!storageAdapter)
345
346
  throw new Error(`No persistence adapter for collection ${collectionName}`);
346
- if (selector != null
347
- && Object.keys(selector).length === 1
348
- && 'id' in selector
349
- && typeof selector.id !== 'object') {
350
- return {
351
- matched: true,
352
- ids: [selector.id].filter(Boolean),
353
- optimizedSelector: {},
354
- };
347
+ if (selector != null && Object.keys(selector).length === 1 && 'id' in selector) {
348
+ const idResult = (0, idIndexQuery_1.default)(selector);
349
+ if (idResult.matched) {
350
+ return {
351
+ matched: true,
352
+ ids: idResult.ids.filter(Boolean),
353
+ optimizedSelector: {},
354
+ };
355
+ }
355
356
  }
356
357
  if (selector == null) {
357
358
  return {
@@ -361,45 +362,7 @@ class AsyncDataAdapter {
361
362
  };
362
363
  }
363
364
  const indices = this.collectionIndices.get(collectionName) ?? [];
364
- return (0, getIndexInfo_1.default)(indices.map(field => async (flatSelector) => {
365
- if (!Object.hasOwnProperty.call(flatSelector, field))
366
- return { matched: false };
367
- const index = await storageAdapter.readIndex(field);
368
- const fieldSelector = flatSelector[field];
369
- const filtersForNull = fieldSelector == null || fieldSelector.$exists === false;
370
- const keys = filtersForNull
371
- ? { include: null, exclude: [...index.keys()].filter(key => key != null) }
372
- : (0, getMatchingKeys_1.default)(field, flatSelector);
373
- if (keys.include == null && keys.exclude == null)
374
- return { matched: false };
375
- // Build included ids
376
- let includedIds = [];
377
- if (keys.include == null) {
378
- for (const set of index.values())
379
- for (const pos of set)
380
- includedIds.push(pos);
381
- }
382
- else {
383
- for (const key of keys.include) {
384
- const idSet = index.get(key);
385
- if (idSet)
386
- for (const id of idSet)
387
- includedIds.push(id);
388
- }
389
- }
390
- // Apply exclusions
391
- if (keys.exclude != null) {
392
- const excludeIds = new Set();
393
- for (const key of keys.exclude) {
394
- const idSet = index.get(key);
395
- if (idSet)
396
- for (const id of idSet)
397
- excludeIds.add(id);
398
- }
399
- includedIds = includedIds.filter(pos => !excludeIds.has(pos));
400
- }
401
- return { matched: true, ids: includedIds, fields: [field], keepSelector: filtersForNull };
402
- }), selector);
365
+ return (0, getIndexInfo_1.default)(indices.map(field => (0, storageIndexQuery_1.default)(storageAdapter, field)), selector);
403
366
  }
404
367
  async queryItems(collectionName, selector) {
405
368
  const storage = this.storageAdapters.get(collectionName);
@@ -9,7 +9,8 @@ const modify_1 = __importDefault(require("./utils/modify"));
9
9
  const queryId_1 = __importDefault(require("./utils/queryId"));
10
10
  const isEqual_1 = __importDefault(require("./utils/isEqual"));
11
11
  const getIndexInfo_1 = __importDefault(require("./getIndexInfo"));
12
- const getMatchingKeys_1 = __importDefault(require("./utils/getMatchingKeys"));
12
+ const storageIndexQuery_1 = __importDefault(require("./utils/storageIndexQuery"));
13
+ const idIndexQuery_1 = __importDefault(require("./utils/idIndexQuery"));
13
14
  const sortItems_1 = __importDefault(require("./utils/sortItems"));
14
15
  const project_1 = __importDefault(require("./utils/project"));
15
16
  /**
@@ -388,58 +389,17 @@ class AutoFetchDataAdapter {
388
389
  const storage = this.storageAdapters.get(collectionName);
389
390
  if (!storage)
390
391
  throw new Error(`No persistence adapter for collection ${collectionName}`);
391
- // Fast path: { id: <scalar> }
392
- if (selector != null && Object.keys(selector).length === 1 && 'id' in selector && typeof selector.id !== 'object') {
393
- return { matched: true, ids: [selector.id], optimizedSelector: {} };
392
+ // `id` needs no declared index — `readIds` is exactly that lookup.
393
+ if (selector != null && Object.keys(selector).length === 1 && 'id' in selector) {
394
+ const idResult = (0, idIndexQuery_1.default)(selector);
395
+ if (idResult.matched)
396
+ return { matched: true, ids: idResult.ids, optimizedSelector: {} };
394
397
  }
395
398
  if (selector == null) {
396
399
  return { matched: false, ids: [], optimizedSelector: {} };
397
400
  }
398
401
  const indices = this.collectionIndices.get(collectionName) ?? [];
399
- return (0, getIndexInfo_1.default)(indices.map(field => async (flatSelector) => {
400
- if (!Object.hasOwnProperty.call(flatSelector, field))
401
- return { matched: false };
402
- const index = await storage.readIndex(field);
403
- const fieldSelector = flatSelector[field];
404
- const filtersForNull = fieldSelector == null || fieldSelector.$exists === false;
405
- const keys = filtersForNull
406
- ? { include: null, exclude: [...index.keys()].filter(key => key != null) }
407
- : (0, getMatchingKeys_1.default)(field, flatSelector);
408
- if (keys.include == null && keys.exclude == null)
409
- return { matched: false };
410
- // Build included ids
411
- let includedIds = [];
412
- if (keys.include == null) {
413
- for (const set of index.values())
414
- for (const pos of set)
415
- includedIds.push(pos);
416
- }
417
- else {
418
- for (const key of keys.include) {
419
- const idSet = index.get(key);
420
- if (idSet)
421
- for (const id of idSet)
422
- includedIds.push(id);
423
- }
424
- }
425
- // Apply exclusions
426
- if (keys.exclude != null) {
427
- const excludeIds = new Set();
428
- for (const key of keys.exclude) {
429
- const idSet = index.get(key);
430
- if (idSet)
431
- for (const id of idSet)
432
- excludeIds.add(id);
433
- }
434
- includedIds = includedIds.filter(pos => !excludeIds.has(pos));
435
- }
436
- return {
437
- matched: true,
438
- ids: includedIds,
439
- fields: [field],
440
- keepSelector: filtersForNull,
441
- };
442
- }), selector);
402
+ return (0, getIndexInfo_1.default)(indices.map(field => (0, storageIndexQuery_1.default)(storage, field)), selector);
443
403
  }
444
404
  async queryItems(collectionName, selector) {
445
405
  const storage = this.storageAdapters.get(collectionName);
@@ -15,6 +15,7 @@ const incrementalQueryUpdate_1 = __importDefault(require("./utils/incrementalQue
15
15
  const queryDelta_1 = require("./utils/queryDelta");
16
16
  const queryId_1 = __importDefault(require("./utils/queryId"));
17
17
  const serializeValue_1 = __importDefault(require("./utils/serializeValue"));
18
+ const idIndexQuery_1 = __importDefault(require("./utils/idIndexQuery"));
18
19
  const sortItems_1 = __importDefault(require("./utils/sortItems"));
19
20
  /**
20
21
  * Checks if there are any pending updates in the given changeset.
@@ -77,15 +78,16 @@ class DefaultDataAdapter {
77
78
  });
78
79
  }
79
80
  getIndexInfo(collection, selector) {
80
- if (selector != null
81
- && Object.keys(selector).length === 1
82
- && 'id' in selector
83
- && typeof selector.id !== 'object') {
84
- return {
85
- matched: true,
86
- ids: [(0, serializeValue_1.default)(selector.id)],
87
- optimizedSelector: {},
88
- };
81
+ if (selector != null && Object.keys(selector).length === 1 && 'id' in selector) {
82
+ const idResult = (0, idIndexQuery_1.default)(selector);
83
+ if (idResult.matched) {
84
+ return {
85
+ // This adapter keys its in-memory map by `serializeValue(id)`.
86
+ matched: true,
87
+ ids: idResult.ids.map(id => (0, serializeValue_1.default)(id)),
88
+ optimizedSelector: {},
89
+ };
90
+ }
89
91
  }
90
92
  if (selector == null) {
91
93
  return {
@@ -9,7 +9,8 @@ const modify_1 = __importDefault(require("./utils/modify"));
9
9
  const queryId_1 = __importDefault(require("./utils/queryId"));
10
10
  const isEqual_1 = __importDefault(require("./utils/isEqual"));
11
11
  const getIndexInfo_1 = __importDefault(require("./getIndexInfo"));
12
- const getMatchingKeys_1 = __importDefault(require("./utils/getMatchingKeys"));
12
+ const idIndexQuery_1 = __importDefault(require("./utils/idIndexQuery"));
13
+ const storageIndexQuery_1 = __importDefault(require("./utils/storageIndexQuery"));
13
14
  const sortItems_1 = __importDefault(require("./utils/sortItems"));
14
15
  const projectItems_1 = __importDefault(require("./utils/projectItems"));
15
16
  const compact_1 = __importDefault(require("./utils/compact"));
@@ -96,15 +97,18 @@ class WorkerDataAdapterHost {
96
97
  const storageAdapter = this.storageAdapters.get(collectionName);
97
98
  if (!storageAdapter)
98
99
  throw new Error(`No persistence adapter for collection ${collectionName}`);
99
- if (selector != null
100
- && Object.keys(selector).length === 1
101
- && 'id' in selector
102
- && typeof selector.id !== 'object') {
103
- return {
104
- matched: true,
105
- ids: (0, compact_1.default)([selector.id]),
106
- optimizedSelector: {},
107
- };
100
+ // `id` needs no declared index: `readIds` is exactly the lookup it
101
+ // describes, so every inclusive form of it is answered here rather than by
102
+ // reading the whole collection (utils/idIndexQuery.ts).
103
+ if (selector != null && Object.keys(selector).length === 1 && 'id' in selector) {
104
+ const idResult = (0, idIndexQuery_1.default)(selector);
105
+ if (idResult.matched) {
106
+ return {
107
+ matched: true,
108
+ ids: (0, compact_1.default)(idResult.ids),
109
+ optimizedSelector: {},
110
+ };
111
+ }
108
112
  }
109
113
  if (selector == null) {
110
114
  return {
@@ -114,59 +118,9 @@ class WorkerDataAdapterHost {
114
118
  };
115
119
  }
116
120
  const indices = this.collectionIndices.get(collectionName) ?? [];
117
- return (0, getIndexInfo_1.default)(indices.map(field => async (flatSelector) => {
118
- if (!Object.hasOwnProperty.call(flatSelector, field)) {
119
- // If the field is not present in the selector, we can't optimize
120
- return { matched: false };
121
- }
122
- const index = await storageAdapter.readIndex(field);
123
- const fieldSelector = flatSelector[field];
124
- const filteresForNull = fieldSelector == null || fieldSelector.$exists === false;
125
- const keys = filteresForNull
126
- ? { include: null, exclude: [...index.keys()].filter(key => key != null) }
127
- : (0, getMatchingKeys_1.default)(field, flatSelector);
128
- if (keys.include == null && keys.exclude == null)
129
- return { matched: false };
130
- // Accumulate included positions
131
- let includedIds = [];
132
- if (keys.include == null) {
133
- for (const set of index.values()) {
134
- for (const pos of set) {
135
- includedIds.push(pos);
136
- }
137
- }
138
- }
139
- else {
140
- for (const key of keys.include) {
141
- const idSet = index.get(key);
142
- if (idSet) {
143
- for (const id of idSet) {
144
- includedIds.push(id);
145
- }
146
- }
147
- }
148
- }
149
- // If exclusion is specified, build a single set of all positions to exclude.
150
- if (keys.exclude != null) {
151
- const excludeIds = new Set();
152
- for (const key of keys.exclude) {
153
- const idSet = index.get(key);
154
- if (idSet) {
155
- for (const id of idSet) {
156
- excludeIds.add(id);
157
- }
158
- }
159
- }
160
- // Filter out any position that exists in the exclude set.
161
- includedIds = includedIds.filter(pos => !excludeIds.has(pos));
162
- }
163
- return {
164
- matched: true,
165
- ids: includedIds,
166
- fields: [field],
167
- keepSelector: filteresForNull,
168
- };
169
- }), selector);
121
+ // `id` first, and always present: it needs no declared index, because
122
+ // `readIds` is exactly the lookup it describes (utils/idIndexQuery.ts).
123
+ return (0, getIndexInfo_1.default)(indices.map(field => (0, storageIndexQuery_1.default)(storageAdapter, field)), selector);
170
124
  }
171
125
  async queryItems(collectionName, selector) {
172
126
  const storageAdapter = this.storageAdapters.get(collectionName);
@@ -135,12 +135,24 @@ function getIndexInfo(queryFunctions, selector) {
135
135
  let hasNonIndexField = false;
136
136
  const matchedBefore = matched;
137
137
  const idsBefore = ids;
138
+ // The branches of an $or union with each other, but the $or as a whole
139
+ // *intersects* with what the flat fields and $and already narrowed down
140
+ // to. Collecting the branches separately is what keeps those two apart —
141
+ // folding them into `ids` turns `{ a: 1, $or: [...] }` into a union and
142
+ // returns items matching neither half of the selector, with an empty
143
+ // optimized selector that leaves nothing to filter them out again.
144
+ let orIds = [];
145
+ let orMatched = false;
138
146
  const process$or = ($orNew) => {
139
147
  if (hasNonIndexField || ($orNew && $orNew.length > 0)) {
140
148
  newSelector.$or = $or;
141
149
  matched = matchedBefore;
142
150
  ids = idsBefore;
143
151
  }
152
+ else if (orMatched) {
153
+ ids = matchedBefore ? (0, intersection_1.default)(idsBefore, orIds) : orIds;
154
+ matched = true;
155
+ }
144
156
  return {
145
157
  matched,
146
158
  ids: ids || [],
@@ -150,8 +162,8 @@ function getIndexInfo(queryFunctions, selector) {
150
162
  const $orNewOrPromise = Array.isArray($or)
151
163
  ? optimizeLogicGate(queryFunctions, $or, (match, selIds) => {
152
164
  if (match) {
153
- ids = [...new Set([...ids, ...selIds])];
154
- matched = true;
165
+ orIds = [...new Set([...orIds, ...selIds])];
166
+ orMatched = true;
155
167
  }
156
168
  else {
157
169
  hasNonIndexField = true;
@@ -12,7 +12,15 @@ export default interface StorageAdapter<T extends {
12
12
  readIds(positions: I[]): Promise<T[]>;
13
13
  createIndex(field: string): Promise<void>;
14
14
  dropIndex(field: string): Promise<void>;
15
- readIndex(field: string): Promise<Map<any, Set<I>>>;
15
+ /**
16
+ * The index, keyed by `serializeValue(value)` — not by the raw field value.
17
+ *
18
+ * SignalDB looks an index up with the serialized form, because that is what
19
+ * makes `3`, `'3'` and `new Date(...)` comparable as map keys at all. An
20
+ * adapter that stores its backend's own keys instead answers nothing for
21
+ * every non-string field, and everything for a `$ne` on one.
22
+ */
23
+ readIndex(field: string): Promise<Map<string | null, Set<I>>>;
16
24
  insert(items: T[]): Promise<void>;
17
25
  replace(items: T[]): Promise<void>;
18
26
  remove(items: T[]): Promise<void>;
@@ -0,0 +1,20 @@
1
+ import type { BaseItem } from '../Collection/types';
2
+ import type { IndexResult } from '../types/IndexProvider';
3
+ import type { FlatSelector } from '../types/Selector';
4
+ /**
5
+ * Resolves a selector on `id` into the ids it names, without consulting an index.
6
+ *
7
+ * `id` is the one field every storage adapter can look up directly — that is what
8
+ * `readIds` is — so a query on it never needs an index to be declared and never
9
+ * needs the whole collection to be read. This behaves like an index provider that
10
+ * happens to need no stored index, because the ids are already in the selector.
11
+ *
12
+ * Only inclusive forms can be answered this way. `$ne`/`$nin` describe everything
13
+ * except* something, which cannot be enumerated without knowing every id, so they
14
+ * report no match and take the ordinary path.
15
+ * @template T - The type of the items in the collection.
16
+ * @template I - The type of the unique identifier for the items.
17
+ * @param selector - The flat selector to resolve.
18
+ * @returns An index result naming the matched ids, or `{ matched: false }`.
19
+ */
20
+ export default function idIndexQuery<T extends BaseItem<I> = BaseItem, I = any>(selector: FlatSelector<T>): IndexResult<I>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = idIndexQuery;
7
+ const isFieldExpression_1 = __importDefault(require("./isFieldExpression"));
8
+ /**
9
+ * Resolves a selector on `id` into the ids it names, without consulting an index.
10
+ *
11
+ * `id` is the one field every storage adapter can look up directly — that is what
12
+ * `readIds` is — so a query on it never needs an index to be declared and never
13
+ * needs the whole collection to be read. This behaves like an index provider that
14
+ * happens to need no stored index, because the ids are already in the selector.
15
+ *
16
+ * Only inclusive forms can be answered this way. `$ne`/`$nin` describe everything
17
+ * except* something, which cannot be enumerated without knowing every id, so they
18
+ * report no match and take the ordinary path.
19
+ * @template T - The type of the items in the collection.
20
+ * @template I - The type of the unique identifier for the items.
21
+ * @param selector - The flat selector to resolve.
22
+ * @returns An index result naming the matched ids, or `{ matched: false }`.
23
+ */
24
+ function idIndexQuery(selector) {
25
+ if (selector == null || !Object.hasOwnProperty.call(selector, 'id'))
26
+ return { matched: false };
27
+ const fieldSelector = selector.id;
28
+ if (fieldSelector == null || fieldSelector instanceof RegExp)
29
+ return { matched: false };
30
+ if ((0, isFieldExpression_1.default)(fieldSelector)) {
31
+ const values = fieldSelector.$in;
32
+ if (!Array.isArray(values) || values.length <= 0)
33
+ return { matched: false };
34
+ return {
35
+ matched: true,
36
+ ids: values,
37
+ fields: ['id'],
38
+ keepSelector: false,
39
+ };
40
+ }
41
+ if (typeof fieldSelector === 'object')
42
+ return { matched: false };
43
+ return {
44
+ matched: true,
45
+ ids: [fieldSelector],
46
+ fields: ['id'],
47
+ keepSelector: false,
48
+ };
49
+ }
@@ -0,0 +1,20 @@
1
+ import type { BaseItem } from '../Collection/types';
2
+ import type StorageAdapter from '../types/StorageAdapter';
3
+ import type { AsynchronousQueryFunction } from '../types/IndexProvider';
4
+ /**
5
+ * Builds the index provider a data adapter uses to narrow a selector down through
6
+ * a storage adapter's index.
7
+ *
8
+ * Every adapter that keeps its data in a `StorageAdapter` needs exactly this, and
9
+ * each of them used to carry its own copy — three transcriptions of one set of
10
+ * rules about null, `$exists`, inclusion and exclusion, which is how they drift
11
+ * apart without anyone noticing. The index is keyed by `serializeValue(value)`,
12
+ * which is what `getMatchingKeys` produces, so the two only agree while they stay
13
+ * in one place.
14
+ * @template T - The type of the items in the collection.
15
+ * @template I - The type of the unique identifier for the items.
16
+ * @param storage - The storage adapter holding the index.
17
+ * @param field - The indexed field this provider answers for.
18
+ * @returns A query function for `getIndexInfo`.
19
+ */
20
+ export default function storageIndexQuery<T extends BaseItem<I>, I = any>(storage: Pick<StorageAdapter<T, I>, 'readIndex'>, field: string): AsynchronousQueryFunction<T, I>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = storageIndexQuery;
7
+ const getMatchingKeys_1 = __importDefault(require("./getMatchingKeys"));
8
+ /**
9
+ * Builds the index provider a data adapter uses to narrow a selector down through
10
+ * a storage adapter's index.
11
+ *
12
+ * Every adapter that keeps its data in a `StorageAdapter` needs exactly this, and
13
+ * each of them used to carry its own copy — three transcriptions of one set of
14
+ * rules about null, `$exists`, inclusion and exclusion, which is how they drift
15
+ * apart without anyone noticing. The index is keyed by `serializeValue(value)`,
16
+ * which is what `getMatchingKeys` produces, so the two only agree while they stay
17
+ * in one place.
18
+ * @template T - The type of the items in the collection.
19
+ * @template I - The type of the unique identifier for the items.
20
+ * @param storage - The storage adapter holding the index.
21
+ * @param field - The indexed field this provider answers for.
22
+ * @returns A query function for `getIndexInfo`.
23
+ */
24
+ function storageIndexQuery(storage, field) {
25
+ return async (flatSelector) => {
26
+ if (!Object.hasOwnProperty.call(flatSelector, field)) {
27
+ // The field is not in the selector, so this index says nothing about it.
28
+ return { matched: false };
29
+ }
30
+ const index = await storage.readIndex(field);
31
+ const fieldSelector = flatSelector[field];
32
+ // A query for null, or for the field being absent, is the one case the index
33
+ // cannot answer by naming keys — it is answered by naming every key it is
34
+ // *not*, and the selector has to stay for the matcher to confirm it.
35
+ const filtersForNull = fieldSelector == null || fieldSelector.$exists === false;
36
+ const keys = filtersForNull
37
+ ? { include: null, exclude: [...index.keys()].filter(key => key != null) }
38
+ : (0, getMatchingKeys_1.default)(field, flatSelector);
39
+ if (keys.include == null && keys.exclude == null)
40
+ return { matched: false };
41
+ let includedIds = [];
42
+ if (keys.include == null) {
43
+ for (const set of index.values()) {
44
+ for (const id of set)
45
+ includedIds.push(id);
46
+ }
47
+ }
48
+ else {
49
+ for (const key of keys.include) {
50
+ const idSet = index.get(key);
51
+ if (idSet) {
52
+ for (const id of idSet)
53
+ includedIds.push(id);
54
+ }
55
+ }
56
+ }
57
+ if (keys.exclude != null) {
58
+ const excludedIds = new Set();
59
+ for (const key of keys.exclude) {
60
+ const idSet = index.get(key);
61
+ if (idSet) {
62
+ for (const id of idSet)
63
+ excludedIds.add(id);
64
+ }
65
+ }
66
+ includedIds = includedIds.filter(id => !excludedIds.has(id));
67
+ }
68
+ return {
69
+ matched: true,
70
+ ids: includedIds,
71
+ fields: [field],
72
+ keepSelector: filtersForNull,
73
+ };
74
+ };
75
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@signaldb/svelte",
3
3
  "type": "module",
4
- "version": "2.0.0-beta.18",
4
+ "version": "2.0.0-beta.19",
5
5
  "scripts": {
6
6
  "build": "tsc -d --noEmit false",
7
7
  "analyze-bundle": "bundle-analyzer ./dist --upload-token=$BUNDLE_ANALYZER_UPLOAD_TOKEN --bundle-name=@signaldb/svelte",