@converse/skeletor 0.0.9 → 2.0.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/README.md +5 -89
- package/dist/skeletor.d.ts +764 -0
- package/dist/skeletor.js +7568 -7747
- package/dist/skeletor.min.js +6 -11
- package/package.json +46 -30
- package/src/{collection.js → collection.ts} +232 -271
- package/src/drivers/{sessionStorage.js → sessionStorage.ts} +81 -93
- package/src/{eventemitter.js → eventemitter.ts} +44 -59
- package/src/{helpers.js → helpers.ts} +56 -69
- package/src/index.ts +84 -0
- package/src/{listening.js → listening.ts} +32 -27
- package/src/{model.js → model.ts} +143 -162
- package/src/{storage.js → storage.ts} +92 -49
- package/src/types.ts +189 -0
- package/src/utils/{events.js → events.ts} +98 -48
- package/dist/skeletor.js.map +0 -1
- package/dist/skeletor.min.js.map +0 -1
- package/src/element.js +0 -238
- package/src/index.js +0 -47
- package/src/types/collection.d.ts +0 -366
- package/src/types/collection.d.ts.map +0 -1
- package/src/types/drivers/sessionStorage.d.ts +0 -37
- package/src/types/drivers/sessionStorage.d.ts.map +0 -1
- package/src/types/element.d.ts +0 -115
- package/src/types/element.d.ts.map +0 -1
- package/src/types/eventemitter.d.ts +0 -93
- package/src/types/eventemitter.d.ts.map +0 -1
- package/src/types/helpers.d.ts +0 -62
- package/src/types/helpers.d.ts.map +0 -1
- package/src/types/index.d.ts +0 -19
- package/src/types/index.d.ts.map +0 -1
- package/src/types/listening.d.ts +0 -40
- package/src/types/listening.d.ts.map +0 -1
- package/src/types/model.d.ts +0 -230
- package/src/types/model.d.ts.map +0 -1
- package/src/types/storage.d.ts +0 -35
- package/src/types/storage.d.ts.map +0 -1
- package/src/types/utils/events.d.ts +0 -28
- package/src/types/utils/events.d.ts.map +0 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
import create from 'lodash-es/create
|
|
4
|
-
import extend from 'lodash-es/extend
|
|
5
|
-
import has from 'lodash-es/has
|
|
6
|
-
import result from 'lodash-es/result
|
|
1
|
+
// (c) 2010-2019 Jeremy Ashkenas and DocumentCloud
|
|
2
|
+
// (c) 2018-2025 JC Brand
|
|
3
|
+
import create from 'lodash-es/create';
|
|
4
|
+
import extend from 'lodash-es/extend';
|
|
5
|
+
import has from 'lodash-es/has';
|
|
6
|
+
import result from 'lodash-es/result';
|
|
7
|
+
import { Model } from './model';
|
|
8
|
+
import { Collection } from './collection';
|
|
9
|
+
import { type SyncOptions, SyncOperation } from './types';
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Custom error for indicating timeouts
|
|
@@ -11,12 +14,12 @@ import result from 'lodash-es/result.js';
|
|
|
11
14
|
*/
|
|
12
15
|
export class NotImplementedError extends Error {}
|
|
13
16
|
|
|
14
|
-
function S4() {
|
|
17
|
+
function S4(): string {
|
|
15
18
|
// Generate four random hex digits.
|
|
16
19
|
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
export function guid() {
|
|
22
|
+
export function guid(): string {
|
|
20
23
|
// Generate a pseudo-GUID by concatenating random hexadecimal.
|
|
21
24
|
return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4();
|
|
22
25
|
}
|
|
@@ -28,10 +31,13 @@ export function guid() {
|
|
|
28
31
|
// Similar to `goog.inherits`, but uses a hash of prototype properties and
|
|
29
32
|
// class properties to be extended.
|
|
30
33
|
//
|
|
31
|
-
export function inherits(
|
|
34
|
+
export function inherits<T extends new (...args: any[]) => any>(
|
|
35
|
+
protoProps: Record<string, any> | null,
|
|
36
|
+
staticProps?: Record<string, any>
|
|
37
|
+
): T {
|
|
32
38
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
33
39
|
const parent = this;
|
|
34
|
-
let child;
|
|
40
|
+
let child: T;
|
|
35
41
|
|
|
36
42
|
// The constructor function for the new subclass is either defined by you
|
|
37
43
|
// (the "constructor" property in your `extend` definition), or defaulted
|
|
@@ -39,9 +45,9 @@ export function inherits(protoProps, staticProps) {
|
|
|
39
45
|
if (protoProps && has(protoProps, 'constructor')) {
|
|
40
46
|
child = protoProps.constructor;
|
|
41
47
|
} else {
|
|
42
|
-
child = function () {
|
|
43
|
-
return parent.apply(this,
|
|
44
|
-
};
|
|
48
|
+
child = function (this: any, ...args: any[]) {
|
|
49
|
+
return parent.apply(this, args);
|
|
50
|
+
} as unknown as T;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
// Add static properties to the constructor function, if supplied.
|
|
@@ -54,23 +60,23 @@ export function inherits(protoProps, staticProps) {
|
|
|
54
60
|
|
|
55
61
|
// Set a convenience property in case the parent's prototype is needed
|
|
56
62
|
// later.
|
|
57
|
-
child.__super__ = parent.prototype;
|
|
63
|
+
(child as any).__super__ = parent.prototype;
|
|
58
64
|
|
|
59
65
|
return child;
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const wrapper = {
|
|
68
|
+
interface PromiseWrapper {
|
|
69
|
+
isResolved: boolean;
|
|
70
|
+
isPending: boolean;
|
|
71
|
+
isRejected: boolean;
|
|
72
|
+
resolve: ((value?: any) => void) | null;
|
|
73
|
+
reject: ((reason?: any) => void) | null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type ResolveablePromise = Promise<any> & PromiseWrapper;
|
|
77
|
+
|
|
78
|
+
export function getResolveablePromise(): ResolveablePromise {
|
|
79
|
+
const wrapper: PromiseWrapper = {
|
|
74
80
|
isResolved: false,
|
|
75
81
|
isPending: true,
|
|
76
82
|
isRejected: false,
|
|
@@ -78,16 +84,11 @@ export function getResolveablePromise() {
|
|
|
78
84
|
reject: null,
|
|
79
85
|
};
|
|
80
86
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
const promise = new Promise((resolve, reject) => {
|
|
88
|
+
wrapper.resolve = resolve;
|
|
89
|
+
wrapper.reject = reject;
|
|
90
|
+
}) as ResolveablePromise;
|
|
84
91
|
|
|
85
|
-
const promise = /** @type {ResolveablePromise} */ (
|
|
86
|
-
new Promise((resolve, reject) => {
|
|
87
|
-
wrapper.resolve = resolve;
|
|
88
|
-
wrapper.reject = reject;
|
|
89
|
-
})
|
|
90
|
-
);
|
|
91
92
|
Object.assign(promise, wrapper);
|
|
92
93
|
promise.then(
|
|
93
94
|
function (v) {
|
|
@@ -101,27 +102,27 @@ export function getResolveablePromise() {
|
|
|
101
102
|
promise.isPending = false;
|
|
102
103
|
promise.isRejected = true;
|
|
103
104
|
throw e;
|
|
104
|
-
}
|
|
105
|
+
}
|
|
105
106
|
);
|
|
106
107
|
return promise;
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
// Throw an error when a URL is needed, and none is supplied.
|
|
110
|
-
export function urlError() {
|
|
111
|
+
export function urlError(): never {
|
|
111
112
|
throw new Error('A "url" property or function must be specified');
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
// Wrap an optional error callback with a fallback error event.
|
|
115
|
-
export function wrapError(model
|
|
116
|
+
export function wrapError(model: Model<any> | Collection<any>, options: any): void {
|
|
116
117
|
const error = options.error;
|
|
117
|
-
options.error = function (resp) {
|
|
118
|
+
options.error = function (resp: any) {
|
|
118
119
|
if (error) error.call(options.context, model, resp, options);
|
|
119
120
|
model.trigger('error', model, resp, options);
|
|
120
121
|
};
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
// Map from CRUD to HTTP for our default `sync` implementation.
|
|
124
|
-
const methodMap = {
|
|
125
|
+
const methodMap: Record<string, string> = {
|
|
125
126
|
create: 'POST',
|
|
126
127
|
update: 'PUT',
|
|
127
128
|
patch: 'PATCH',
|
|
@@ -129,30 +130,11 @@ const methodMap = {
|
|
|
129
130
|
read: 'GET',
|
|
130
131
|
};
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
*/
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* @param {Model | Collection} model
|
|
140
|
-
*/
|
|
141
|
-
export function getSyncMethod(model) {
|
|
142
|
-
const store = result(model, 'browserStorage') || result(/** @type {Model} */(model).collection, 'browserStorage');
|
|
143
|
-
return store ? store.sync() : sync;
|
|
133
|
+
export function getSyncMethod(model: Model | Collection<any>): typeof sync & { __name__?: string } {
|
|
134
|
+
const store = result(model, 'browserStorage') || result((model as Model).collection, 'browserStorage');
|
|
135
|
+
return store ? (store as any).sync() : sync;
|
|
144
136
|
}
|
|
145
137
|
|
|
146
|
-
/**
|
|
147
|
-
* @typedef {Object} SyncOptions
|
|
148
|
-
* @property {string} [url]
|
|
149
|
-
* @property {any} [data]
|
|
150
|
-
* @property {any} [attrs]
|
|
151
|
-
* @property {Function} [success]
|
|
152
|
-
* @property {Function} [error]
|
|
153
|
-
* @property {any} [xhr]
|
|
154
|
-
*/
|
|
155
|
-
|
|
156
138
|
/**
|
|
157
139
|
* Override this function to change the manner in which Backbone persists
|
|
158
140
|
* models to the server. You will be passed the type of request, and the
|
|
@@ -164,12 +146,11 @@ export function getSyncMethod(model) {
|
|
|
164
146
|
* - Use `setTimeout` to batch rapid-fire updates into a single request.
|
|
165
147
|
* - Persist models via WebSockets instead of Ajax.
|
|
166
148
|
* - Persist models to browser storage
|
|
167
|
-
*
|
|
168
|
-
* @param {'create'|'update'|'patch'} method
|
|
169
|
-
* @param {import('./model.js').Model} model
|
|
170
|
-
* @param {SyncOptions} [options]
|
|
171
149
|
*/
|
|
172
|
-
|
|
150
|
+
/**
|
|
151
|
+
* @public
|
|
152
|
+
*/
|
|
153
|
+
export function sync(method: SyncOperation, model: Model | Collection<any>, options: SyncOptions = {}): Promise<any> {
|
|
173
154
|
let data = options.data;
|
|
174
155
|
|
|
175
156
|
// Ensure that we have the appropriate request data.
|
|
@@ -178,15 +159,21 @@ export function sync(method, model, options = {}) {
|
|
|
178
159
|
}
|
|
179
160
|
|
|
180
161
|
const type = methodMap[method];
|
|
181
|
-
const params = {
|
|
162
|
+
const params: RequestInit & { success?: any; error?: any } = {
|
|
182
163
|
method: type,
|
|
183
164
|
body: data ? JSON.stringify(data) : '',
|
|
165
|
+
headers: {
|
|
166
|
+
'Content-Type': 'application/json',
|
|
167
|
+
},
|
|
184
168
|
success: options.success,
|
|
185
169
|
error: options.error,
|
|
186
170
|
};
|
|
187
171
|
|
|
188
172
|
const url = options.url || result(model, 'url') || urlError();
|
|
189
|
-
const xhr =
|
|
173
|
+
const xhr = fetch(url, params);
|
|
174
|
+
if (options) {
|
|
175
|
+
options.xhr = xhr;
|
|
176
|
+
}
|
|
190
177
|
model.trigger('request', model, xhr, { ...params, xhr });
|
|
191
178
|
return xhr;
|
|
192
179
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* global global */
|
|
2
|
+
import { EventEmitter } from './eventemitter';
|
|
3
|
+
import Storage from './storage';
|
|
4
|
+
import { Collection } from './collection';
|
|
5
|
+
import { Model } from './model';
|
|
6
|
+
import { sync } from './helpers';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
interface SkeletorType {
|
|
12
|
+
Collection: typeof Collection;
|
|
13
|
+
EventEmitter: typeof EventEmitter;
|
|
14
|
+
Model: typeof Model;
|
|
15
|
+
sync: typeof sync;
|
|
16
|
+
VERSION?: string;
|
|
17
|
+
noConflict?: () => SkeletorType;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
const skeletor: SkeletorType = {
|
|
24
|
+
Collection,
|
|
25
|
+
EventEmitter,
|
|
26
|
+
Model,
|
|
27
|
+
sync,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Establish the root object, `window` (`self`) in the browser, or `global` on the server.
|
|
31
|
+
// We use `self` instead of `window` for `WebWorker` support.
|
|
32
|
+
const root =
|
|
33
|
+
(typeof self == 'object' && self.self === self && self) ||
|
|
34
|
+
(typeof global == 'object' && global.global === global && global);
|
|
35
|
+
|
|
36
|
+
// Current version of the library. Keep in sync with `package.json`.
|
|
37
|
+
skeletor.VERSION = '0.0.1';
|
|
38
|
+
|
|
39
|
+
// Save the previous value of the `Skeletor` variable, so that it can be
|
|
40
|
+
// restored later on, if `noConflict` is used.
|
|
41
|
+
const previousSkeletor = root.Skeletor;
|
|
42
|
+
|
|
43
|
+
// Runs Skeletor.js in *noConflict* mode, returning the `Skeletor` variable
|
|
44
|
+
// to its previous owner. Returns a reference to this Skeletor object.
|
|
45
|
+
/**
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
function noConflict() {
|
|
49
|
+
root.Skeletor = previousSkeletor;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
skeletor.noConflict = noConflict;
|
|
53
|
+
|
|
54
|
+
root.Skeletor = skeletor;
|
|
55
|
+
export default skeletor;
|
|
56
|
+
|
|
57
|
+
export { noConflict, Collection, EventEmitter, Model, Storage, sync };
|
|
58
|
+
|
|
59
|
+
export type { LocalForageWithExtensions } from './storage';
|
|
60
|
+
export type {
|
|
61
|
+
ClassConstructor,
|
|
62
|
+
CollectionOptions,
|
|
63
|
+
Comparator,
|
|
64
|
+
EventCallback,
|
|
65
|
+
EventCallbackMap,
|
|
66
|
+
EventContext,
|
|
67
|
+
EventHandlersMap,
|
|
68
|
+
EventListenerMap,
|
|
69
|
+
ModelAttributes,
|
|
70
|
+
ModelOptions,
|
|
71
|
+
ObjectListenedTo,
|
|
72
|
+
ObjectWithId,
|
|
73
|
+
Options,
|
|
74
|
+
SyncOperation,
|
|
75
|
+
SyncOptions,
|
|
76
|
+
EventHandler,
|
|
77
|
+
ListeningType,
|
|
78
|
+
IEventEmitter,
|
|
79
|
+
ListeningMap,
|
|
80
|
+
} from './types';
|
|
81
|
+
|
|
82
|
+
export type { CollectionIterator } from './collection';
|
|
83
|
+
export type { SkeletorType };
|
|
84
|
+
export type { EventEmitterObject } from './eventemitter';
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import { eventsApi, onApi, offApi } from './utils/events
|
|
1
|
+
import { eventsApi, onApi, offApi } from './utils/events';
|
|
2
|
+
import type {
|
|
3
|
+
EventCallback,
|
|
4
|
+
IEventEmitter,
|
|
5
|
+
EventCallbackMap,
|
|
6
|
+
EventsApiOptions,
|
|
7
|
+
ListeningType,
|
|
8
|
+
} from './types';
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* A listening class that tracks and cleans up memory bindings
|
|
5
12
|
* when all callbacks have been offed.
|
|
6
13
|
*/
|
|
7
|
-
class Listening {
|
|
14
|
+
class Listening implements ListeningType {
|
|
15
|
+
id: string;
|
|
16
|
+
listener: IEventEmitter;
|
|
17
|
+
obj: any;
|
|
18
|
+
interop: boolean;
|
|
19
|
+
count: number;
|
|
20
|
+
_events?: EventCallbackMap;
|
|
8
21
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @param {any} listener
|
|
13
|
-
* @param {any} obj
|
|
14
|
-
*/
|
|
15
|
-
constructor(listener, obj) {
|
|
16
|
-
this.id = listener._listenId;
|
|
22
|
+
constructor(listener: IEventEmitter, obj: any) {
|
|
23
|
+
this.id = listener._listenId!;
|
|
17
24
|
this.listener = listener;
|
|
18
25
|
this.obj = obj;
|
|
19
26
|
this.interop = true;
|
|
@@ -21,18 +28,14 @@ class Listening {
|
|
|
21
28
|
this._events = undefined;
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
* @param {Function} callback
|
|
27
|
-
* @param {any} context
|
|
28
|
-
* @param {Listening} _listening
|
|
29
|
-
*/
|
|
30
|
-
start(name, callback, context, _listening) {
|
|
31
|
-
this._events = eventsApi(onApi, this._events || {}, name, callback, {
|
|
31
|
+
start(name: string | EventCallbackMap, callback: EventCallback, context: any, _listening: ListeningType): this {
|
|
32
|
+
const options: EventsApiOptions = {
|
|
32
33
|
context: this.obj,
|
|
33
34
|
ctx: context,
|
|
34
35
|
listening: _listening,
|
|
35
|
-
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
this._events = eventsApi(onApi, this._events || {}, name, callback, options) as EventCallbackMap;
|
|
36
39
|
|
|
37
40
|
if (_listening) {
|
|
38
41
|
const listeners = this.obj._listeners || (this.obj._listeners = {});
|
|
@@ -51,16 +54,14 @@ class Listening {
|
|
|
51
54
|
* Uses an optimized counter if the listenee uses Backbone.Events.
|
|
52
55
|
* Otherwise, falls back to manual tracking to support events
|
|
53
56
|
* library interop.
|
|
54
|
-
* @param {string} name
|
|
55
|
-
* @param {Function} callback
|
|
56
57
|
*/
|
|
57
|
-
stop(name, callback) {
|
|
58
|
-
let cleanup;
|
|
58
|
+
stop(name: string | EventCallbackMap, callback: EventCallback): void {
|
|
59
|
+
let cleanup: boolean;
|
|
59
60
|
if (this.interop) {
|
|
60
61
|
this._events = eventsApi(offApi, this._events, name, callback, {
|
|
61
62
|
context: undefined,
|
|
62
63
|
listeners: undefined,
|
|
63
|
-
});
|
|
64
|
+
}) as EventCallbackMap;
|
|
64
65
|
cleanup = !this._events;
|
|
65
66
|
} else {
|
|
66
67
|
this.count--;
|
|
@@ -72,9 +73,13 @@ class Listening {
|
|
|
72
73
|
/**
|
|
73
74
|
* Cleans up memory bindings between the listener and the listenee.
|
|
74
75
|
*/
|
|
75
|
-
cleanup() {
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
cleanup(): void {
|
|
77
|
+
if (this.listener._listeningTo) {
|
|
78
|
+
delete this.listener._listeningTo[this.obj._listenId!];
|
|
79
|
+
}
|
|
80
|
+
if (!this.interop && this.obj._listeners) {
|
|
81
|
+
delete this.obj._listeners[this.id];
|
|
82
|
+
}
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
|