@converse/skeletor 3.1.4 → 3.1.6
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/CHANGES.md +18 -0
- package/README.md +9 -3
- package/dist/skeletor.d.ts +22 -3
- package/dist/skeletor.esm.js +267 -242
- package/dist/skeletor.esm.js.map +1 -1
- package/dist/skeletor.js +267 -242
- package/dist/skeletor.js.map +1 -1
- package/dist/skeletor.min.js +4 -4
- package/dist/skeletor.node.d.ts +956 -0
- package/dist/skeletor.node.js +269 -243
- package/dist/skeletor.node.mjs +269 -243
- package/package.json +6 -2
- package/src/declarations.d.ts +11 -0
- package/src/drivers/nodeSQLiteStorage.ts +3 -7
- package/src/drivers/types.ts +8 -2
- package/src/index.ts +1 -0
- package/src/storage.ts +33 -28
package/package.json
CHANGED
|
@@ -10,15 +10,18 @@
|
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
12
|
"node": {
|
|
13
|
+
"types": "./dist/skeletor.node.d.ts",
|
|
13
14
|
"require": "./dist/skeletor.node.js",
|
|
14
15
|
"import": "./dist/skeletor.node.mjs"
|
|
15
16
|
},
|
|
16
17
|
"default": {
|
|
18
|
+
"types": "./dist/skeletor.d.ts",
|
|
17
19
|
"import": "./dist/skeletor.esm.js",
|
|
18
20
|
"require": "./dist/skeletor.js"
|
|
19
21
|
}
|
|
20
22
|
},
|
|
21
23
|
"./node": {
|
|
24
|
+
"types": "./dist/skeletor.node.d.ts",
|
|
22
25
|
"require": "./dist/skeletor.node.js",
|
|
23
26
|
"import": "./dist/skeletor.node.mjs"
|
|
24
27
|
}
|
|
@@ -36,7 +39,7 @@
|
|
|
36
39
|
"contributors": [
|
|
37
40
|
"JC Brand"
|
|
38
41
|
],
|
|
39
|
-
"version": "3.1.
|
|
42
|
+
"version": "3.1.6",
|
|
40
43
|
"engines": {
|
|
41
44
|
"node": ">=22.0.0"
|
|
42
45
|
},
|
|
@@ -47,7 +50,7 @@
|
|
|
47
50
|
},
|
|
48
51
|
"scripts": {
|
|
49
52
|
"build": "npm run prebuild && npm run build:declarations && rollup -c",
|
|
50
|
-
"build:declarations": "npm run build:types && api-extractor run --local && rm -rf dist/defs",
|
|
53
|
+
"build:declarations": "npm run build:types && api-extractor run --local && api-extractor run --local -c api-extractor.node.json && rm -rf dist/defs",
|
|
51
54
|
"build:types": "tsc -p tsconfig.declarations.json",
|
|
52
55
|
"check": "npm run check:types && npm run test && npm run test:node && npm run lint",
|
|
53
56
|
"check:types": "tsc --noEmit",
|
|
@@ -72,6 +75,7 @@
|
|
|
72
75
|
"dist/skeletor.node.mjs",
|
|
73
76
|
"dist/skeletor.d.ts",
|
|
74
77
|
"dist/skeletor.d.ts.map",
|
|
78
|
+
"dist/skeletor.node.d.ts",
|
|
75
79
|
"src/**/*.ts"
|
|
76
80
|
],
|
|
77
81
|
"dependencies": {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@converse/localforage-getitems` ships its types as an ambient script that
|
|
3
|
+
* declares the *unscoped* module name (`localforage-getitems`), left over from
|
|
4
|
+
* the package it was forked from. TypeScript therefore resolves the scoped
|
|
5
|
+
* specifier to a `typings/index.d.ts` that isn't a module (TS2306), so declare
|
|
6
|
+
* the shape we use here instead.
|
|
7
|
+
*/
|
|
8
|
+
declare module '@converse/localforage-getitems' {
|
|
9
|
+
export function extendPrototype<T>(localforage: T): T;
|
|
10
|
+
export const extendPrototypeResult: boolean;
|
|
11
|
+
}
|
|
@@ -9,6 +9,7 @@ const DEFAULT_BASE_DIR = '.skeletor-storage';
|
|
|
9
9
|
/**
|
|
10
10
|
* SQLite-backed implementation of StorageDriver.
|
|
11
11
|
* Uses a single table with key/value columns, storing values as JSON strings.
|
|
12
|
+
* @public
|
|
12
13
|
*/
|
|
13
14
|
export class NodeSQLiteStorage implements StorageDriver {
|
|
14
15
|
private db: DatabaseSync;
|
|
@@ -27,11 +28,6 @@ export class NodeSQLiteStorage implements StorageDriver {
|
|
|
27
28
|
return Promise.resolve();
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
debouncedSetItems?: {
|
|
31
|
-
(items: Record<string, any>): Promise<void>;
|
|
32
|
-
flush?: () => void;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
31
|
constructor(name: string, storageDir?: string, inMemory?: boolean) {
|
|
36
32
|
if (inMemory) {
|
|
37
33
|
this.db = new DatabaseSync(':memory:');
|
|
@@ -116,7 +112,7 @@ export class NodeSQLiteStorage implements StorageDriver {
|
|
|
116
112
|
return Promise.resolve(rows.map((r) => r.key));
|
|
117
113
|
}
|
|
118
114
|
|
|
119
|
-
setItems(items: Record<string, any>): Promise<
|
|
115
|
+
setItems(items: Record<string, any>): Promise<Record<string, any>> {
|
|
120
116
|
this.db.exec('BEGIN IMMEDIATE');
|
|
121
117
|
try {
|
|
122
118
|
for (const [key, value] of Object.entries(items)) {
|
|
@@ -137,7 +133,7 @@ export class NodeSQLiteStorage implements StorageDriver {
|
|
|
137
133
|
}
|
|
138
134
|
throw e;
|
|
139
135
|
}
|
|
140
|
-
return Promise.resolve();
|
|
136
|
+
return Promise.resolve(items);
|
|
141
137
|
}
|
|
142
138
|
|
|
143
139
|
getItems(keys: string[]): Promise<Record<string, any>> {
|
package/src/drivers/types.ts
CHANGED
|
@@ -11,10 +11,16 @@ export interface StorageDriver {
|
|
|
11
11
|
length(): Promise<number>;
|
|
12
12
|
key(keyIndex: number): Promise<string | null>;
|
|
13
13
|
keys(): Promise<string[]>;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Write several items in one go. A successful write is signalled by the
|
|
16
|
+
* promise settling, not by what it resolves with: drivers may resolve with
|
|
17
|
+
* the written items (localForage does) or with nothing, and callers must
|
|
18
|
+
* treat both as success.
|
|
19
|
+
*/
|
|
20
|
+
setItems?(items: Record<string, any>): Promise<Record<string, any> | void>;
|
|
15
21
|
getItems?(keys: string[]): Promise<Record<string, any>>;
|
|
16
22
|
debouncedSetItems?: {
|
|
17
|
-
(items: Record<string, any>): Promise<void>;
|
|
23
|
+
(items: Record<string, any>): Promise<Record<string, any> | void>;
|
|
18
24
|
flush?: () => void;
|
|
19
25
|
};
|
|
20
26
|
}
|
package/src/index.ts
CHANGED
|
@@ -63,6 +63,7 @@ export { noConflict, Collection, EventEmitter, Model, PersistentStorage, Browser
|
|
|
63
63
|
export { getStorage } from './helpers';
|
|
64
64
|
|
|
65
65
|
export type { StorageDriver, StoreType } from './drivers/types';
|
|
66
|
+
export type { NodeStorageConstructor } from './storage';
|
|
66
67
|
export type {
|
|
67
68
|
ClassConstructor,
|
|
68
69
|
CollectionOptions,
|
package/src/storage.ts
CHANGED
|
@@ -8,7 +8,7 @@ import localForage from 'localforage';
|
|
|
8
8
|
import mergebounce from 'mergebounce';
|
|
9
9
|
import sessionStorageWrapper from './drivers/sessionStorage';
|
|
10
10
|
import { extendPrototype as extendPrototypeWithSetItems } from 'localforage-setitems';
|
|
11
|
-
import { extendPrototype as extendPrototypeWithGetItems } from '@converse/localforage-getitems
|
|
11
|
+
import { extendPrototype as extendPrototypeWithGetItems } from '@converse/localforage-getitems';
|
|
12
12
|
import { guid } from './helpers';
|
|
13
13
|
import type { Model } from './model';
|
|
14
14
|
import type { Collection } from './collection';
|
|
@@ -36,8 +36,13 @@ const registered = new WeakSet<PersistentStorage>();
|
|
|
36
36
|
/**
|
|
37
37
|
* Constructor for a Node.js-only storage driver. Registered by the
|
|
38
38
|
* `@converse/skeletor/node` entry point via {@link PersistentStorage.nodeStorage}.
|
|
39
|
+
* @public
|
|
39
40
|
*/
|
|
40
|
-
type NodeStorageConstructor = new (
|
|
41
|
+
export type NodeStorageConstructor = new (
|
|
42
|
+
name: string,
|
|
43
|
+
storageDir?: string,
|
|
44
|
+
inMemory?: boolean,
|
|
45
|
+
) => StorageDriver;
|
|
41
46
|
|
|
42
47
|
/**
|
|
43
48
|
* @public
|
|
@@ -60,6 +65,19 @@ class PersistentStorage {
|
|
|
60
65
|
// bundle; the `@converse/skeletor/node` entry sets it.
|
|
61
66
|
static nodeStorage: NodeStorageConstructor | null = null;
|
|
62
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Directory the `'node'` store type writes its database files to. The driver's
|
|
70
|
+
* own default is relative, so it resolves against `process.cwd()` and a process
|
|
71
|
+
* started from two directories ends up with two separate sets of state. Set an
|
|
72
|
+
* absolute path (`$XDG_STATE_HOME/<app>`, say) to pin it. Left `null`, the
|
|
73
|
+
* driver's default applies.
|
|
74
|
+
*
|
|
75
|
+
* Only the `'node'` string store type reads this; passing a driver instance to
|
|
76
|
+
* the constructor means you've already chosen a directory yourself.
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
static nodeStorageDir: string | null = null;
|
|
80
|
+
|
|
63
81
|
/** Register a storage instance with a weak reference. @public */
|
|
64
82
|
static register(instance: PersistentStorage): void {
|
|
65
83
|
if (registered.has(instance)) return;
|
|
@@ -128,7 +146,7 @@ class PersistentStorage {
|
|
|
128
146
|
"Import from '@converse/skeletor/node' instead of '@converse/skeletor'.",
|
|
129
147
|
);
|
|
130
148
|
}
|
|
131
|
-
this.store = new NodeSQLiteStorage(this.name);
|
|
149
|
+
this.store = new NodeSQLiteStorage(this.name, PersistentStorage.nodeStorageDir ?? undefined);
|
|
132
150
|
if (batchedWrites) {
|
|
133
151
|
this.store.debouncedSetItems = mergebounce((items: Record<string, any>) => this.store.setItems!(items), 50, {
|
|
134
152
|
'promise': true,
|
|
@@ -155,9 +173,11 @@ class PersistentStorage {
|
|
|
155
173
|
async clear(): Promise<void> {
|
|
156
174
|
await this.storeInitialized;
|
|
157
175
|
await this.store.removeItem(this.name).catch((e) => console.error(e));
|
|
176
|
+
|
|
158
177
|
const re = new RegExp(`^${this.name}-`);
|
|
159
178
|
const keys = await this.store.keys();
|
|
160
179
|
const removed_keys = keys.filter((k) => re.test(k));
|
|
180
|
+
|
|
161
181
|
await Promise.all(removed_keys.map((k) => this.store.removeItem(k).catch((e) => console.error(e))));
|
|
162
182
|
}
|
|
163
183
|
|
|
@@ -231,31 +251,16 @@ class PersistentStorage {
|
|
|
231
251
|
}
|
|
232
252
|
}
|
|
233
253
|
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
} else if (method === 'read' && !caughtError) {
|
|
245
|
-
// A read that finds no record is a normal empty result (first run /
|
|
246
|
-
// empty storage), not an error. Resolve with `null` so the callback
|
|
247
|
-
// and promise forms of fetch() agree and a first-run read never
|
|
248
|
-
// rejects.
|
|
249
|
-
if (options && options.success) {
|
|
250
|
-
options.success(null, options);
|
|
251
|
-
}
|
|
252
|
-
} else {
|
|
253
|
-
// A genuine failure (storage threw, or a write produced no result):
|
|
254
|
-
// surface a real Error so consumers get a stack and `instanceof Error`
|
|
255
|
-
// holds.
|
|
256
|
-
if (options && options.error) {
|
|
257
|
-
options.error(caughtError ?? new Error('Record Not Found'));
|
|
258
|
-
}
|
|
254
|
+
if (caughtError) {
|
|
255
|
+
options?.error?.(caughtError);
|
|
256
|
+
} else if (options?.success) {
|
|
257
|
+
// When storing, we don't pass back the response (which is the set
|
|
258
|
+
// attributes returned from localforage) because Skeletor sets them
|
|
259
|
+
// again on the model and due to the async nature of localforage it can
|
|
260
|
+
// cause stale attributes to be set on a model after it's been updated
|
|
261
|
+
// in the meantime.
|
|
262
|
+
const data = method === 'read' ? (resp ?? null) : null;
|
|
263
|
+
options.success(data, options);
|
|
259
264
|
}
|
|
260
265
|
}
|
|
261
266
|
localSync.__name__ = 'localSync';
|