mongodb-livedata-server 0.0.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/README.md +63 -0
- package/dist/livedata_server.js +9 -0
- package/dist/meteor/binary-heap/max_heap.js +186 -0
- package/dist/meteor/binary-heap/min_heap.js +17 -0
- package/dist/meteor/binary-heap/min_max_heap.js +48 -0
- package/dist/meteor/callback-hook/hook.js +78 -0
- package/dist/meteor/ddp/crossbar.js +136 -0
- package/dist/meteor/ddp/heartbeat.js +77 -0
- package/dist/meteor/ddp/livedata_server.js +403 -0
- package/dist/meteor/ddp/method-invocation.js +72 -0
- package/dist/meteor/ddp/random-stream.js +100 -0
- package/dist/meteor/ddp/session-collection-view.js +106 -0
- package/dist/meteor/ddp/session-document-view.js +82 -0
- package/dist/meteor/ddp/session.js +570 -0
- package/dist/meteor/ddp/stream_server.js +181 -0
- package/dist/meteor/ddp/subscription.js +347 -0
- package/dist/meteor/ddp/utils.js +104 -0
- package/dist/meteor/ddp/writefence.js +111 -0
- package/dist/meteor/diff-sequence/diff.js +257 -0
- package/dist/meteor/ejson/ejson.js +569 -0
- package/dist/meteor/ejson/stringify.js +119 -0
- package/dist/meteor/ejson/utils.js +42 -0
- package/dist/meteor/id-map/id_map.js +92 -0
- package/dist/meteor/mongo/caching_change_observer.js +94 -0
- package/dist/meteor/mongo/doc_fetcher.js +53 -0
- package/dist/meteor/mongo/geojson_utils.js +41 -0
- package/dist/meteor/mongo/live_connection.js +264 -0
- package/dist/meteor/mongo/live_cursor.js +57 -0
- package/dist/meteor/mongo/minimongo_common.js +2002 -0
- package/dist/meteor/mongo/minimongo_matcher.js +217 -0
- package/dist/meteor/mongo/minimongo_sorter.js +268 -0
- package/dist/meteor/mongo/observe_driver_utils.js +73 -0
- package/dist/meteor/mongo/observe_multiplexer.js +228 -0
- package/dist/meteor/mongo/oplog-observe-driver.js +919 -0
- package/dist/meteor/mongo/oplog_tailing.js +352 -0
- package/dist/meteor/mongo/oplog_v2_converter.js +126 -0
- package/dist/meteor/mongo/polling_observe_driver.js +195 -0
- package/dist/meteor/mongo/synchronous-cursor.js +261 -0
- package/dist/meteor/mongo/synchronous-queue.js +110 -0
- package/dist/meteor/ordered-dict/ordered_dict.js +198 -0
- package/dist/meteor/random/AbstractRandomGenerator.js +92 -0
- package/dist/meteor/random/AleaRandomGenerator.js +90 -0
- package/dist/meteor/random/NodeRandomGenerator.js +42 -0
- package/dist/meteor/random/createAleaGenerator.js +32 -0
- package/dist/meteor/random/createRandom.js +22 -0
- package/dist/meteor/random/main.js +12 -0
- package/livedata_server.ts +3 -0
- package/meteor/LICENSE +28 -0
- package/meteor/binary-heap/max_heap.ts +225 -0
- package/meteor/binary-heap/min_heap.ts +15 -0
- package/meteor/binary-heap/min_max_heap.ts +53 -0
- package/meteor/callback-hook/hook.ts +85 -0
- package/meteor/ddp/crossbar.ts +148 -0
- package/meteor/ddp/heartbeat.ts +97 -0
- package/meteor/ddp/livedata_server.ts +473 -0
- package/meteor/ddp/method-invocation.ts +86 -0
- package/meteor/ddp/random-stream.ts +102 -0
- package/meteor/ddp/session-collection-view.ts +119 -0
- package/meteor/ddp/session-document-view.ts +92 -0
- package/meteor/ddp/session.ts +708 -0
- package/meteor/ddp/stream_server.ts +204 -0
- package/meteor/ddp/subscription.ts +392 -0
- package/meteor/ddp/utils.ts +119 -0
- package/meteor/ddp/writefence.ts +130 -0
- package/meteor/diff-sequence/diff.ts +295 -0
- package/meteor/ejson/ejson.ts +601 -0
- package/meteor/ejson/stringify.ts +122 -0
- package/meteor/ejson/utils.ts +38 -0
- package/meteor/id-map/id_map.ts +84 -0
- package/meteor/mongo/caching_change_observer.ts +120 -0
- package/meteor/mongo/doc_fetcher.ts +52 -0
- package/meteor/mongo/geojson_utils.ts +42 -0
- package/meteor/mongo/live_connection.ts +302 -0
- package/meteor/mongo/live_cursor.ts +79 -0
- package/meteor/mongo/minimongo_common.ts +2440 -0
- package/meteor/mongo/minimongo_matcher.ts +275 -0
- package/meteor/mongo/minimongo_sorter.ts +331 -0
- package/meteor/mongo/observe_driver_utils.ts +79 -0
- package/meteor/mongo/observe_multiplexer.ts +256 -0
- package/meteor/mongo/oplog-observe-driver.ts +1049 -0
- package/meteor/mongo/oplog_tailing.ts +414 -0
- package/meteor/mongo/oplog_v2_converter.ts +124 -0
- package/meteor/mongo/polling_observe_driver.ts +247 -0
- package/meteor/mongo/synchronous-cursor.ts +293 -0
- package/meteor/mongo/synchronous-queue.ts +119 -0
- package/meteor/ordered-dict/ordered_dict.ts +229 -0
- package/meteor/random/AbstractRandomGenerator.ts +99 -0
- package/meteor/random/AleaRandomGenerator.ts +96 -0
- package/meteor/random/NodeRandomGenerator.ts +37 -0
- package/meteor/random/createAleaGenerator.ts +31 -0
- package/meteor/random/createRandom.ts +19 -0
- package/meteor/random/main.ts +8 -0
- package/package.json +30 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SynchronousCursor = exports._createSynchronousCursor = void 0;
|
|
4
|
+
const ejson_1 = require("../ejson/ejson");
|
|
5
|
+
const oplog_tailing_1 = require("./oplog_tailing");
|
|
6
|
+
function _createSynchronousCursor(db, cursorDescription, options) {
|
|
7
|
+
const { useTransform } = options || {};
|
|
8
|
+
var collection = db.collection(cursorDescription.collectionName);
|
|
9
|
+
var cursorOptions = cursorDescription.options;
|
|
10
|
+
var mongoOptions = {
|
|
11
|
+
sort: cursorOptions.sort,
|
|
12
|
+
limit: cursorOptions.limit,
|
|
13
|
+
skip: cursorOptions.skip,
|
|
14
|
+
projection: cursorOptions.projection,
|
|
15
|
+
readPreference: cursorOptions.readPreference,
|
|
16
|
+
numberOfRetries: undefined
|
|
17
|
+
};
|
|
18
|
+
// Do we want a tailable cursor (which only works on capped collections)?
|
|
19
|
+
if (cursorOptions.tailable) {
|
|
20
|
+
mongoOptions.numberOfRetries = -1;
|
|
21
|
+
}
|
|
22
|
+
var dbCursor = collection.find(cursorDescription.selector, mongoOptions);
|
|
23
|
+
// Do we want a tailable cursor (which only works on capped collections)?
|
|
24
|
+
if (cursorOptions.tailable) {
|
|
25
|
+
// We want a tailable cursor...
|
|
26
|
+
dbCursor.addCursorFlag("tailable", true);
|
|
27
|
+
// ... and for the server to wait a bit if any getMore has no data (rather
|
|
28
|
+
// than making us put the relevant sleeps in the client)...
|
|
29
|
+
dbCursor.addCursorFlag("awaitData", true);
|
|
30
|
+
// And if this is on the oplog collection and the cursor specifies a 'ts',
|
|
31
|
+
// then set the undocumented oplog replay flag, which does a special scan to
|
|
32
|
+
// find the first document (instead of creating an index on ts). This is a
|
|
33
|
+
// very hard-coded Mongo flag which only works on the oplog collection and
|
|
34
|
+
// only works with the ts field.
|
|
35
|
+
if (cursorDescription.collectionName === oplog_tailing_1.OPLOG_COLLECTION &&
|
|
36
|
+
cursorDescription.selector.ts) {
|
|
37
|
+
dbCursor.addCursorFlag("oplogReplay", true);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (typeof cursorOptions.maxTimeMs !== 'undefined') {
|
|
41
|
+
dbCursor = dbCursor.maxTimeMS(cursorOptions.maxTimeMs);
|
|
42
|
+
}
|
|
43
|
+
if (typeof cursorOptions.hint !== 'undefined') {
|
|
44
|
+
dbCursor = dbCursor.hint(cursorOptions.hint);
|
|
45
|
+
}
|
|
46
|
+
return new SynchronousCursor(dbCursor, cursorDescription, { useTransform });
|
|
47
|
+
}
|
|
48
|
+
exports._createSynchronousCursor = _createSynchronousCursor;
|
|
49
|
+
;
|
|
50
|
+
class SynchronousCursor {
|
|
51
|
+
constructor(_dbCursor, _cursorDescription, options) {
|
|
52
|
+
this._dbCursor = _dbCursor;
|
|
53
|
+
this._cursorDescription = _cursorDescription;
|
|
54
|
+
var self = this;
|
|
55
|
+
if (options.useTransform && _cursorDescription.options.transform) {
|
|
56
|
+
self._transform = wrapTransform(_cursorDescription.options.transform);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
self._transform = null;
|
|
60
|
+
}
|
|
61
|
+
self._visitedIds = new Set();
|
|
62
|
+
}
|
|
63
|
+
// Returns a Promise for the next object from the cursor, skipping those whose
|
|
64
|
+
// IDs we've already seen and replacing Mongo atoms with Meteor atoms.
|
|
65
|
+
async _nextObjectPromise() {
|
|
66
|
+
var self = this;
|
|
67
|
+
while (true) {
|
|
68
|
+
var doc = await this._dbCursor.next();
|
|
69
|
+
if (!doc)
|
|
70
|
+
return null;
|
|
71
|
+
if (!self._cursorDescription.options.tailable && doc.hasOwnProperty('_id')) {
|
|
72
|
+
// Did Mongo give us duplicate documents in the same cursor? If so,
|
|
73
|
+
// ignore this one. (Do this before the transform, since transform might
|
|
74
|
+
// return some unrelated value.) We don't do this for tailable cursors,
|
|
75
|
+
// because we want to maintain O(1) memory usage. And if there isn't _id
|
|
76
|
+
// for some reason (maybe it's the oplog), then we don't do this either.
|
|
77
|
+
// (Be careful to do this for falsey but existing _id, though.)
|
|
78
|
+
if (self._visitedIds.has(doc._id))
|
|
79
|
+
continue;
|
|
80
|
+
self._visitedIds.add(doc._id);
|
|
81
|
+
}
|
|
82
|
+
if (self._transform)
|
|
83
|
+
doc = self._transform(doc);
|
|
84
|
+
return doc;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Returns a promise which is resolved with the next object (like with
|
|
88
|
+
// _nextObjectPromise) or rejected if the cursor doesn't return within
|
|
89
|
+
// timeoutMS ms.
|
|
90
|
+
async _nextObjectPromiseWithTimeout(timeoutMS) {
|
|
91
|
+
if (!timeoutMS) {
|
|
92
|
+
return this._nextObjectPromise();
|
|
93
|
+
}
|
|
94
|
+
const nextObjectPromise = this._nextObjectPromise();
|
|
95
|
+
const timeoutErr = new Error('Client-side timeout waiting for next object');
|
|
96
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
reject(timeoutErr);
|
|
99
|
+
}, timeoutMS);
|
|
100
|
+
});
|
|
101
|
+
return Promise.race([nextObjectPromise, timeoutPromise])
|
|
102
|
+
.catch((err) => {
|
|
103
|
+
if (err === timeoutErr) {
|
|
104
|
+
this._dbCursor.close();
|
|
105
|
+
}
|
|
106
|
+
throw err;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
close() {
|
|
110
|
+
this._dbCursor.close();
|
|
111
|
+
}
|
|
112
|
+
async forEach(callback, thisArg) {
|
|
113
|
+
var self = this;
|
|
114
|
+
// Get back to the beginning.
|
|
115
|
+
self._rewind();
|
|
116
|
+
// We implement the loop ourself instead of using self._dbCursor.each,
|
|
117
|
+
// because "each" will call its callback outside of a fiber which makes it
|
|
118
|
+
// much more complex to make this function synchronous.
|
|
119
|
+
var index = 0;
|
|
120
|
+
while (true) {
|
|
121
|
+
var doc = await self._nextObjectPromise();
|
|
122
|
+
if (!doc)
|
|
123
|
+
return;
|
|
124
|
+
callback.call(thisArg, doc, index++, self);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
_rewind() {
|
|
128
|
+
var self = this;
|
|
129
|
+
// known to be synchronous
|
|
130
|
+
self._dbCursor.rewind();
|
|
131
|
+
self._visitedIds = new Set();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.SynchronousCursor = SynchronousCursor;
|
|
135
|
+
// Wrap a transform function to return objects that have the _id field
|
|
136
|
+
// of the untransformed document. This ensures that subsystems such as
|
|
137
|
+
// the observe-sequence package that call `observe` can keep track of
|
|
138
|
+
// the documents identities.
|
|
139
|
+
//
|
|
140
|
+
// - Require that it returns objects
|
|
141
|
+
// - If the return value has an _id field, verify that it matches the
|
|
142
|
+
// original _id field
|
|
143
|
+
// - If the return value doesn't have an _id field, add it back.
|
|
144
|
+
function wrapTransform(transform) {
|
|
145
|
+
if (!transform)
|
|
146
|
+
return null;
|
|
147
|
+
// No need to doubly-wrap transforms.
|
|
148
|
+
if (transform.__wrappedTransform__)
|
|
149
|
+
return transform;
|
|
150
|
+
const wrapped = doc => {
|
|
151
|
+
if (!doc.hasOwnProperty('_id')) {
|
|
152
|
+
// XXX do we ever have a transform on the oplog's collection? because that
|
|
153
|
+
// collection has no _id.
|
|
154
|
+
throw new Error('can only transform documents with _id');
|
|
155
|
+
}
|
|
156
|
+
const id = doc._id;
|
|
157
|
+
const transformed = transform(doc);
|
|
158
|
+
if (transformed.hasOwnProperty('_id')) {
|
|
159
|
+
if (!(0, ejson_1.equals)(transformed._id, id)) {
|
|
160
|
+
throw new Error('transformed document can\'t have different _id');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
transformed._id = id;
|
|
165
|
+
}
|
|
166
|
+
return transformed;
|
|
167
|
+
};
|
|
168
|
+
wrapped.__wrappedTransform__ = true;
|
|
169
|
+
return wrapped;
|
|
170
|
+
}
|
|
171
|
+
;
|
|
172
|
+
/*
|
|
173
|
+
forEach(callback, thisArg) {
|
|
174
|
+
var self = this;
|
|
175
|
+
|
|
176
|
+
// Get back to the beginning.
|
|
177
|
+
self._rewind();
|
|
178
|
+
|
|
179
|
+
// We implement the loop ourself instead of using self._dbCursor.each,
|
|
180
|
+
// because "each" will call its callback outside of a fiber which makes it
|
|
181
|
+
// much more complex to make this function synchronous.
|
|
182
|
+
var index = 0;
|
|
183
|
+
while (true) {
|
|
184
|
+
var doc = self._nextObject();
|
|
185
|
+
if (!doc) return;
|
|
186
|
+
callback.call(thisArg, doc, index++, self._selfForIteration);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// XXX Allow overlapping callback executions if callback yields.
|
|
191
|
+
map(callback, thisArg) {
|
|
192
|
+
var self = this;
|
|
193
|
+
var res = [];
|
|
194
|
+
self.forEach(function (doc, index) {
|
|
195
|
+
res.push(callback.call(thisArg, doc, index, self._selfForIteration));
|
|
196
|
+
});
|
|
197
|
+
return res;
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
_rewind() {
|
|
201
|
+
var self = this;
|
|
202
|
+
|
|
203
|
+
// known to be synchronous
|
|
204
|
+
self._dbCursor.rewind();
|
|
205
|
+
|
|
206
|
+
self._visitedIds = new LocalCollection._IdMap;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Mostly usable for tailable cursors.
|
|
210
|
+
close() {
|
|
211
|
+
var self = this;
|
|
212
|
+
|
|
213
|
+
self._dbCursor.close();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
fetch() {
|
|
217
|
+
var self = this;
|
|
218
|
+
return self.map(_.identity);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// This method is NOT wrapped in Cursor.
|
|
222
|
+
getRawObjects(ordered) {
|
|
223
|
+
var self = this;
|
|
224
|
+
if (ordered) {
|
|
225
|
+
return self.fetch();
|
|
226
|
+
} else {
|
|
227
|
+
var results = new LocalCollection._IdMap;
|
|
228
|
+
self.forEach(function (doc) {
|
|
229
|
+
results.set(doc._id, doc);
|
|
230
|
+
});
|
|
231
|
+
return results;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
[Symbol.iterator]() {
|
|
236
|
+
var self = this;
|
|
237
|
+
|
|
238
|
+
// Get back to the beginning.
|
|
239
|
+
self._rewind();
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
next() {
|
|
243
|
+
const doc = self._nextObject();
|
|
244
|
+
return doc ? {
|
|
245
|
+
value: doc
|
|
246
|
+
} : {
|
|
247
|
+
done: true
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
[Symbol.asyncIterator]() {
|
|
254
|
+
const syncResult = this[Symbol.iterator]();
|
|
255
|
+
return {
|
|
256
|
+
async next() {
|
|
257
|
+
return Promise.resolve(syncResult.next());
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
*/
|
|
@@ -0,0 +1,110 @@
|
|
|
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._SynchronousQueue = void 0;
|
|
7
|
+
const double_ended_queue_1 = __importDefault(require("double-ended-queue"));
|
|
8
|
+
class _SynchronousQueue {
|
|
9
|
+
constructor() {
|
|
10
|
+
// List of tasks to run (not including a currently-running task if any). Each
|
|
11
|
+
// is an object with field 'task' (the task function to run) and 'future' (the
|
|
12
|
+
// Future associated with the blocking runTask call that queued it, or null if
|
|
13
|
+
// called from queueTask).
|
|
14
|
+
this._taskHandles = new double_ended_queue_1.default();
|
|
15
|
+
// This is true if self._run() is either currently executing or scheduled to
|
|
16
|
+
// do so soon.
|
|
17
|
+
this._runningOrRunScheduled = false;
|
|
18
|
+
// This is true if we're currently draining. While we're draining, a further
|
|
19
|
+
// drain is a noop, to prevent infinite loops. "drain" is a heuristic type
|
|
20
|
+
// operation, that has a meaning like unto "what a naive person would expect
|
|
21
|
+
// when modifying a table from an observe"
|
|
22
|
+
this._draining = false;
|
|
23
|
+
}
|
|
24
|
+
async runTask(task) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
var handle = {
|
|
27
|
+
task: task,
|
|
28
|
+
name: task.name,
|
|
29
|
+
runTaskResolve: resolve,
|
|
30
|
+
runTaskReject: reject
|
|
31
|
+
};
|
|
32
|
+
this._taskHandles.push(handle);
|
|
33
|
+
this._scheduleRun();
|
|
34
|
+
// Yield. We'll get back here after the task is run (and will throw if the
|
|
35
|
+
// task throws).
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
queueTask(task) {
|
|
39
|
+
var self = this;
|
|
40
|
+
self._taskHandles.push({
|
|
41
|
+
task: task,
|
|
42
|
+
name: task.name
|
|
43
|
+
});
|
|
44
|
+
self._scheduleRun();
|
|
45
|
+
// No need to block.
|
|
46
|
+
}
|
|
47
|
+
async flush() {
|
|
48
|
+
var self = this;
|
|
49
|
+
await self.runTask(async () => { });
|
|
50
|
+
}
|
|
51
|
+
async drain() {
|
|
52
|
+
var self = this;
|
|
53
|
+
if (self._draining)
|
|
54
|
+
return;
|
|
55
|
+
self._draining = true;
|
|
56
|
+
while (!self._taskHandles.isEmpty()) {
|
|
57
|
+
await self.flush();
|
|
58
|
+
}
|
|
59
|
+
self._draining = false;
|
|
60
|
+
}
|
|
61
|
+
;
|
|
62
|
+
_scheduleRun() {
|
|
63
|
+
// Already running or scheduled? Do nothing.
|
|
64
|
+
if (this._runningOrRunScheduled)
|
|
65
|
+
return;
|
|
66
|
+
this._runningOrRunScheduled = true;
|
|
67
|
+
setImmediate(async () => {
|
|
68
|
+
await this._run();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
;
|
|
72
|
+
async _run() {
|
|
73
|
+
var self = this;
|
|
74
|
+
if (!self._runningOrRunScheduled)
|
|
75
|
+
throw new Error("expected to be _runningOrRunScheduled");
|
|
76
|
+
if (self._taskHandles.isEmpty()) {
|
|
77
|
+
// Done running tasks! Don't immediately schedule another run, but
|
|
78
|
+
// allow future tasks to do so.
|
|
79
|
+
self._runningOrRunScheduled = false;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
var taskHandle = self._taskHandles.shift();
|
|
83
|
+
// Run the task.
|
|
84
|
+
var exception = undefined;
|
|
85
|
+
try {
|
|
86
|
+
await taskHandle.task();
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
if (taskHandle.runTaskReject) {
|
|
90
|
+
// We'll throw this exception through runTask.
|
|
91
|
+
exception = err;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
console.error("Exception in queued task", err);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Soon, run the next task, if there is any.
|
|
98
|
+
self._runningOrRunScheduled = false;
|
|
99
|
+
self._scheduleRun();
|
|
100
|
+
// If this was queued with runTask, let the runTask call return (throwing if
|
|
101
|
+
// the task threw).
|
|
102
|
+
if (taskHandle.runTaskReject) {
|
|
103
|
+
if (exception)
|
|
104
|
+
taskHandle.runTaskReject(exception);
|
|
105
|
+
else
|
|
106
|
+
taskHandle.runTaskResolve();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports._SynchronousQueue = _SynchronousQueue;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file defines an ordered dictionary abstraction that is useful for
|
|
3
|
+
// maintaining a dataset backed by observeChanges. It supports ordering items
|
|
4
|
+
// by specifying the item they now come before.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OrderedDict = void 0;
|
|
7
|
+
// The implementation is a dictionary that contains nodes of a doubly-linked
|
|
8
|
+
// list as its values.
|
|
9
|
+
// constructs a new element struct
|
|
10
|
+
// next and prev are whole elements, not keys.
|
|
11
|
+
function element(key, value, next, prev) {
|
|
12
|
+
return {
|
|
13
|
+
key: key,
|
|
14
|
+
value: value,
|
|
15
|
+
next: next,
|
|
16
|
+
prev: prev
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
class OrderedDict {
|
|
20
|
+
constructor(...args) {
|
|
21
|
+
this._dict = Object.create(null);
|
|
22
|
+
this._first = null;
|
|
23
|
+
this._last = null;
|
|
24
|
+
this._size = 0;
|
|
25
|
+
if (typeof args[0] === 'function') {
|
|
26
|
+
this._stringify = args.shift();
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
this._stringify = function (x) { return x; };
|
|
30
|
+
}
|
|
31
|
+
args.forEach(kv => this.putBefore(kv[0], kv[1], null));
|
|
32
|
+
}
|
|
33
|
+
// the "prefix keys with a space" thing comes from here
|
|
34
|
+
// https://github.com/documentcloud/underscore/issues/376#issuecomment-2815649
|
|
35
|
+
_k(key) {
|
|
36
|
+
return " " + this._stringify(key);
|
|
37
|
+
}
|
|
38
|
+
empty() {
|
|
39
|
+
return !this._first;
|
|
40
|
+
}
|
|
41
|
+
size() {
|
|
42
|
+
return this._size;
|
|
43
|
+
}
|
|
44
|
+
_linkEltIn(elt) {
|
|
45
|
+
if (!elt.next) {
|
|
46
|
+
elt.prev = this._last;
|
|
47
|
+
if (this._last)
|
|
48
|
+
this._last.next = elt;
|
|
49
|
+
this._last = elt;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
elt.prev = elt.next.prev;
|
|
53
|
+
elt.next.prev = elt;
|
|
54
|
+
if (elt.prev)
|
|
55
|
+
elt.prev.next = elt;
|
|
56
|
+
}
|
|
57
|
+
if (this._first === null || this._first === elt.next)
|
|
58
|
+
this._first = elt;
|
|
59
|
+
}
|
|
60
|
+
_linkEltOut(elt) {
|
|
61
|
+
if (elt.next)
|
|
62
|
+
elt.next.prev = elt.prev;
|
|
63
|
+
if (elt.prev)
|
|
64
|
+
elt.prev.next = elt.next;
|
|
65
|
+
if (elt === this._last)
|
|
66
|
+
this._last = elt.prev;
|
|
67
|
+
if (elt === this._first)
|
|
68
|
+
this._first = elt.next;
|
|
69
|
+
}
|
|
70
|
+
putBefore(key, item, before) {
|
|
71
|
+
if (this._dict[this._k(key)])
|
|
72
|
+
throw new Error("Item " + key + " already present in OrderedDict");
|
|
73
|
+
var elt = before ?
|
|
74
|
+
element(key, item, this._dict[this._k(before)]) :
|
|
75
|
+
element(key, item, null);
|
|
76
|
+
if (typeof elt.next === "undefined")
|
|
77
|
+
throw new Error("could not find item to put this one before");
|
|
78
|
+
this._linkEltIn(elt);
|
|
79
|
+
this._dict[this._k(key)] = elt;
|
|
80
|
+
this._size++;
|
|
81
|
+
}
|
|
82
|
+
append(key, item) {
|
|
83
|
+
this.putBefore(key, item, null);
|
|
84
|
+
}
|
|
85
|
+
remove(key) {
|
|
86
|
+
var elt = this._dict[this._k(key)];
|
|
87
|
+
if (typeof elt === "undefined")
|
|
88
|
+
throw new Error("Item " + key + " not present in OrderedDict");
|
|
89
|
+
this._linkEltOut(elt);
|
|
90
|
+
this._size--;
|
|
91
|
+
delete this._dict[this._k(key)];
|
|
92
|
+
return elt.value;
|
|
93
|
+
}
|
|
94
|
+
get(key) {
|
|
95
|
+
if (this.has(key)) {
|
|
96
|
+
return this._dict[this._k(key)].value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
has(key) {
|
|
100
|
+
return Object.prototype.hasOwnProperty.call(this._dict, this._k(key));
|
|
101
|
+
}
|
|
102
|
+
// Iterate through the items in this dictionary in order, calling
|
|
103
|
+
// iter(value, key, index) on each one.
|
|
104
|
+
// Stops whenever iter returns OrderedDict.BREAK, or after the last element.
|
|
105
|
+
forEach(iter, context = null) {
|
|
106
|
+
var i = 0;
|
|
107
|
+
var elt = this._first;
|
|
108
|
+
while (elt !== null) {
|
|
109
|
+
var b = iter.call(context, elt.value, elt.key, i);
|
|
110
|
+
if (b === OrderedDict.BREAK)
|
|
111
|
+
return;
|
|
112
|
+
elt = elt.next;
|
|
113
|
+
i++;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
first() {
|
|
117
|
+
if (this.empty()) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
return this._first.key;
|
|
121
|
+
}
|
|
122
|
+
firstValue() {
|
|
123
|
+
if (this.empty()) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
return this._first.value;
|
|
127
|
+
}
|
|
128
|
+
last() {
|
|
129
|
+
if (this.empty()) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
return this._last.key;
|
|
133
|
+
}
|
|
134
|
+
lastValue() {
|
|
135
|
+
if (this.empty()) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
return this._last.value;
|
|
139
|
+
}
|
|
140
|
+
prev(key) {
|
|
141
|
+
if (this.has(key)) {
|
|
142
|
+
var elt = this._dict[this._k(key)];
|
|
143
|
+
if (elt.prev)
|
|
144
|
+
return elt.prev.key;
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
next(key) {
|
|
149
|
+
if (this.has(key)) {
|
|
150
|
+
var elt = this._dict[this._k(key)];
|
|
151
|
+
if (elt.next)
|
|
152
|
+
return elt.next.key;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
moveBefore(key, before) {
|
|
157
|
+
var elt = this._dict[this._k(key)];
|
|
158
|
+
var eltBefore = before ? this._dict[this._k(before)] : null;
|
|
159
|
+
if (typeof elt === "undefined") {
|
|
160
|
+
throw new Error("Item to move is not present");
|
|
161
|
+
}
|
|
162
|
+
if (typeof eltBefore === "undefined") {
|
|
163
|
+
throw new Error("Could not find element to move this one before");
|
|
164
|
+
}
|
|
165
|
+
if (eltBefore === elt.next) // no moving necessary
|
|
166
|
+
return;
|
|
167
|
+
// remove from its old place
|
|
168
|
+
this._linkEltOut(elt);
|
|
169
|
+
// patch into its new place
|
|
170
|
+
elt.next = eltBefore;
|
|
171
|
+
this._linkEltIn(elt);
|
|
172
|
+
}
|
|
173
|
+
// Linear, sadly.
|
|
174
|
+
indexOf(key) {
|
|
175
|
+
var ret = null;
|
|
176
|
+
this.forEach((v, k, i) => {
|
|
177
|
+
if (this._k(k) === this._k(key)) {
|
|
178
|
+
ret = i;
|
|
179
|
+
return OrderedDict.BREAK;
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
});
|
|
183
|
+
return ret;
|
|
184
|
+
}
|
|
185
|
+
_checkRep() {
|
|
186
|
+
Object.keys(this._dict).forEach(k => {
|
|
187
|
+
const v = this._dict[k];
|
|
188
|
+
if (v.next === v) {
|
|
189
|
+
throw new Error("Next is a loop");
|
|
190
|
+
}
|
|
191
|
+
if (v.prev === v) {
|
|
192
|
+
throw new Error("Prev is a loop");
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
exports.OrderedDict = OrderedDict;
|
|
198
|
+
OrderedDict.BREAK = { "break": true };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// We use cryptographically strong PRNGs (crypto.getRandomBytes() on the server,
|
|
3
|
+
// window.crypto.getRandomValues() in the browser) when available. If these
|
|
4
|
+
// PRNGs fail, we fall back to the Alea PRNG, which is not cryptographically
|
|
5
|
+
// strong, and we seed it with various sources such as the date, Math.random,
|
|
6
|
+
// and window size on the client. When using crypto.getRandomValues(), our
|
|
7
|
+
// primitive is hexString(), from which we construct fraction(). When using
|
|
8
|
+
// window.crypto.getRandomValues() or alea, the primitive is fraction and we use
|
|
9
|
+
// that to construct hex string.
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
const UNMISTAKABLE_CHARS = '23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz';
|
|
12
|
+
const BASE64_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +
|
|
13
|
+
'0123456789-_';
|
|
14
|
+
// `type` is one of `RandomGenerator.Type` as defined below.
|
|
15
|
+
//
|
|
16
|
+
// options:
|
|
17
|
+
// - seeds: (required, only for RandomGenerator.Type.ALEA) an array
|
|
18
|
+
// whose items will be `toString`ed and used as the seed to the Alea
|
|
19
|
+
// algorithm
|
|
20
|
+
class RandomGenerator {
|
|
21
|
+
/**
|
|
22
|
+
* @name Random.fraction
|
|
23
|
+
* @summary Return a number between 0 and 1, like `Math.random`.
|
|
24
|
+
* @locus Anywhere
|
|
25
|
+
*/
|
|
26
|
+
fraction() {
|
|
27
|
+
throw new Error(`Unknown random generator type`);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* @name Random.hexString
|
|
31
|
+
* @summary Return a random string of `n` hexadecimal digits.
|
|
32
|
+
* @locus Anywhere
|
|
33
|
+
* @param {Number} n Length of the string
|
|
34
|
+
*/
|
|
35
|
+
hexString(digits) {
|
|
36
|
+
return this._randomString(digits, '0123456789abcdef');
|
|
37
|
+
}
|
|
38
|
+
_randomString(charsCount, alphabet) {
|
|
39
|
+
let result = '';
|
|
40
|
+
for (let i = 0; i < charsCount; i++) {
|
|
41
|
+
result += this.choice(alphabet);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @name Random.id
|
|
47
|
+
* @summary Return a unique identifier, such as `"Jjwjg6gouWLXhMGKW"`, that is
|
|
48
|
+
* likely to be unique in the whole world.
|
|
49
|
+
* @locus Anywhere
|
|
50
|
+
* @param {Number} [n] Optional length of the identifier in characters
|
|
51
|
+
* (defaults to 17)
|
|
52
|
+
*/
|
|
53
|
+
id(charsCount) {
|
|
54
|
+
// 17 characters is around 96 bits of entropy, which is the amount of
|
|
55
|
+
// state in the Alea PRNG.
|
|
56
|
+
if (charsCount === undefined) {
|
|
57
|
+
charsCount = 17;
|
|
58
|
+
}
|
|
59
|
+
return this._randomString(charsCount, UNMISTAKABLE_CHARS);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* @name Random.secret
|
|
63
|
+
* @summary Return a random string of printable characters with 6 bits of
|
|
64
|
+
* entropy per character. Use `Random.secret` for security-critical secrets
|
|
65
|
+
* that are intended for machine, rather than human, consumption.
|
|
66
|
+
* @locus Anywhere
|
|
67
|
+
* @param {Number} [n] Optional length of the secret string (defaults to 43
|
|
68
|
+
* characters, or 256 bits of entropy)
|
|
69
|
+
*/
|
|
70
|
+
secret(charsCount) {
|
|
71
|
+
// Default to 256 bits of entropy, or 43 characters at 6 bits per
|
|
72
|
+
// character.
|
|
73
|
+
if (charsCount === undefined) {
|
|
74
|
+
charsCount = 43;
|
|
75
|
+
}
|
|
76
|
+
return this._randomString(charsCount, BASE64_CHARS);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* @name Random.choice
|
|
80
|
+
* @summary Return a random element of the given array or string.
|
|
81
|
+
* @locus Anywhere
|
|
82
|
+
* @param {Array|String} arrayOrString Array or string to choose from
|
|
83
|
+
*/
|
|
84
|
+
choice(arrayOrString) {
|
|
85
|
+
const index = Math.floor(this.fraction() * arrayOrString.length);
|
|
86
|
+
if (typeof arrayOrString === 'string') {
|
|
87
|
+
return arrayOrString.substr(index, 1);
|
|
88
|
+
}
|
|
89
|
+
return arrayOrString[index];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.default = RandomGenerator;
|