js-dos 0.80.25

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.
Files changed (119) hide show
  1. package/.eslintrc.json +43 -0
  2. package/.github/workflows/build.yml +43 -0
  3. package/.gitmodules +4 -0
  4. package/README.md +24 -0
  5. package/index.html +106 -0
  6. package/package.json +48 -0
  7. package/postcss.config.cjs +8 -0
  8. package/src/app.css +14 -0
  9. package/src/base.css +10 -0
  10. package/src/components/checkbox.tsx +41 -0
  11. package/src/components/close-button.tsx +17 -0
  12. package/src/components/components.css +28 -0
  13. package/src/components/dos-option-checkbox.tsx +72 -0
  14. package/src/components/dos-option-select.tsx +94 -0
  15. package/src/components/dos-option-slider.tsx +35 -0
  16. package/src/components/error.tsx +16 -0
  17. package/src/components/loading.tsx +26 -0
  18. package/src/components/lock.tsx +10 -0
  19. package/src/components/select.tsx +33 -0
  20. package/src/components/slider.tsx +69 -0
  21. package/src/download-file.ts +18 -0
  22. package/src/frame/account-frame.tsx +155 -0
  23. package/src/frame/editor/defaults.ts +344 -0
  24. package/src/frame/editor/editor-conf-frame.tsx +42 -0
  25. package/src/frame/editor/editor-frame.css +55 -0
  26. package/src/frame/editor/editor-fs-frame.tsx +315 -0
  27. package/src/frame/frame.css +127 -0
  28. package/src/frame/frame.tsx +34 -0
  29. package/src/frame/host-cache-frame.tsx +124 -0
  30. package/src/frame/image-rendering-frame.tsx +8 -0
  31. package/src/frame/network-frame.tsx +157 -0
  32. package/src/frame/quick-save-frame.tsx +95 -0
  33. package/src/frame/settings-frame.tsx +14 -0
  34. package/src/frame/stats-frame.tsx +82 -0
  35. package/src/host/bundle-storage.ts +91 -0
  36. package/src/host/fullscreen.ts +36 -0
  37. package/src/host/lcache.ts +203 -0
  38. package/src/host/lstorage.ts +156 -0
  39. package/src/i18n.ts +167 -0
  40. package/src/index.css +7 -0
  41. package/src/load.ts +139 -0
  42. package/src/login/login.css +9 -0
  43. package/src/login/login.tsx +77 -0
  44. package/src/main.tsx +195 -0
  45. package/src/non-serializable-store.ts +35 -0
  46. package/src/public/types.ts +39 -0
  47. package/src/sidebar/account-button.tsx +32 -0
  48. package/src/sidebar/cloud-save-button.tsx +89 -0
  49. package/src/sidebar/diskette-icon.tsx +14 -0
  50. package/src/sidebar/fullscreen-button.tsx +44 -0
  51. package/src/sidebar/network-button.tsx +39 -0
  52. package/src/sidebar/sidebar-button.tsx +243 -0
  53. package/src/sidebar/sidebar.css +47 -0
  54. package/src/sidebar/sidebar.tsx +30 -0
  55. package/src/sockdrive/LICENSE +674 -0
  56. package/src/sockdrive/README.md +22 -0
  57. package/src/sockdrive/SDL2/LRUCache11.hpp +240 -0
  58. package/src/sockdrive/SDL2/sockdrive.cpp +207 -0
  59. package/src/sockdrive/include/sockdrive.h +23 -0
  60. package/src/sockdrive/js/.eslintrc.json +43 -0
  61. package/src/sockdrive/js/README.md +70 -0
  62. package/src/sockdrive/js/package.json +29 -0
  63. package/src/sockdrive/js/src/fatfs/chains.js +318 -0
  64. package/src/sockdrive/js/src/fatfs/dir.js +385 -0
  65. package/src/sockdrive/js/src/fatfs/helpers.js +210 -0
  66. package/src/sockdrive/js/src/fatfs/index.js +606 -0
  67. package/src/sockdrive/js/src/fatfs/structs.js +255 -0
  68. package/src/sockdrive/js/src/fatfs/vol.js +166 -0
  69. package/src/sockdrive/js/src/sockdrive/cache.ts +133 -0
  70. package/src/sockdrive/js/src/sockdrive/drive.ts +364 -0
  71. package/src/sockdrive/js/src/sockdrive/lru.ts +221 -0
  72. package/src/sockdrive/js/src/sockdrive/types.ts +10 -0
  73. package/src/sockdrive/js/src/sockdrive-fat.d.ts +64 -0
  74. package/src/sockdrive/js/src/sockdrive-fat.ts +228 -0
  75. package/src/sockdrive/js/src/sockdrive-native.ts +98 -0
  76. package/src/sockdrive/js/src/test/test.ts +186 -0
  77. package/src/sockdrive/js/tsconfig.json +11 -0
  78. package/src/sockdrive/js/webpack.config.js +68 -0
  79. package/src/sockdrive/js/yarn.lock +1808 -0
  80. package/src/sockdrive/lz4/lz4.c +2773 -0
  81. package/src/sockdrive/lz4/lz4.h +862 -0
  82. package/src/store/auth.ts +104 -0
  83. package/src/store/dos.ts +284 -0
  84. package/src/store/editor.ts +59 -0
  85. package/src/store/storage.ts +30 -0
  86. package/src/store/ui.ts +183 -0
  87. package/src/store.ts +27 -0
  88. package/src/ui.tsx +103 -0
  89. package/src/v8/changes.ts +56 -0
  90. package/src/v8/config.ts +18 -0
  91. package/src/v8/subscriptions.ts +62 -0
  92. package/src/vite-env.d.ts +2 -0
  93. package/src/window/dos/controls/keyboard.ts +32 -0
  94. package/src/window/dos/controls/keys.ts +331 -0
  95. package/src/window/dos/controls/mouse/mount.ts +99 -0
  96. package/src/window/dos/controls/mouse/mouse-locked.ts +51 -0
  97. package/src/window/dos/controls/mouse/mouse-not-locked.ts +84 -0
  98. package/src/window/dos/controls/mouse/mouse-swipe.ts +57 -0
  99. package/src/window/dos/controls/mouse/pointer.ts +85 -0
  100. package/src/window/dos/controls/mouse.ts +21 -0
  101. package/src/window/dos/dos-dhry2.tsx +191 -0
  102. package/src/window/dos/dos-runtime.tsx +185 -0
  103. package/src/window/dos/dos-window.tsx +82 -0
  104. package/src/window/dos/render/canvas.ts +65 -0
  105. package/src/window/dos/render/resize.ts +31 -0
  106. package/src/window/dos/render/webgl.ts +173 -0
  107. package/src/window/dos/sound/audio-node.ts +148 -0
  108. package/src/window/error-window.tsx +9 -0
  109. package/src/window/loading-window.tsx +54 -0
  110. package/src/window/prerun-window.tsx +98 -0
  111. package/src/window/select-window.tsx +118 -0
  112. package/src/window/window.css +78 -0
  113. package/src/window/window.tsx +49 -0
  114. package/src/ws/ws-sockdrive.ts +102 -0
  115. package/src/ws/ws-transport-layer.ts +457 -0
  116. package/tailwind.config.cjs +48 -0
  117. package/tsconfig.json +32 -0
  118. package/tsconfig.node.json +9 -0
  119. package/vite.config.ts +22 -0
@@ -0,0 +1,364 @@
1
+ import { Ptr, Stats } from "./types";
2
+ import { BlockCache, Cache } from "./cache";
3
+
4
+ interface Module {
5
+ HEAPU8: Uint8Array;
6
+ _decode_lz4_block?: (compressedSize: number,
7
+ decodedSize: number, ptr: Ptr) => number;
8
+ }
9
+
10
+ interface Request {
11
+ type: 1 | 2, // read | write
12
+ sector: number,
13
+ buffer: Ptr | Uint8Array,
14
+ resolve: (result: number) => void,
15
+ }
16
+
17
+ export class Drive {
18
+ module: Module;
19
+ sectorSize: number;
20
+ endpoint: string;
21
+ owner: string;
22
+ drive: string;
23
+ token: string;
24
+ stats: Stats;
25
+
26
+ socket: Promise<WebSocket> = Promise.resolve(null as any);
27
+ request: Request | null;
28
+
29
+ aheadRange: number;
30
+ aheadSize: number;
31
+ writeBuffer: Uint8Array;
32
+
33
+ readBuffer: Uint8Array;
34
+ readAheadBuffer: Ptr;
35
+ readAheadPos = 0;
36
+ readAheadCompressed = 0;
37
+ readStartedAt: number = 0;
38
+ decodeBuffer: Uint8Array;
39
+
40
+ cache: Cache;
41
+ cleanup = () => {/**/};
42
+
43
+ openFn = (read: boolean, write: boolean) => {/**/};
44
+ errorFn = (e: Error) => {/**/};
45
+
46
+ retries: number;
47
+
48
+ public constructor(endpoint: string,
49
+ owner: string,
50
+ drive: string,
51
+ token: string,
52
+ stats: Stats,
53
+ module: Module,
54
+ readAheadBuffer: Ptr,
55
+ aheadRange: number,
56
+ sectorSize = 512,
57
+ memoryLimit = 32 * 1024 * 1024) {
58
+ if (aheadRange > 255) {
59
+ throw new Error("Maximum aheadRange is 255");
60
+ }
61
+
62
+ this.sectorSize = sectorSize;
63
+ this.module = module;
64
+ this.endpoint = endpoint;
65
+ this.owner = owner;
66
+ this.drive = drive;
67
+ this.token = token;
68
+ this.request = null;
69
+ this.readBuffer = new Uint8Array(1 + 4 + 1);
70
+ this.writeBuffer = new Uint8Array(1 + 4 + this.sectorSize);
71
+ this.readAheadBuffer = readAheadBuffer;
72
+ this.stats = stats;
73
+ this.aheadRange = aheadRange;
74
+ this.aheadSize = aheadRange * this.sectorSize;
75
+ this.decodeBuffer = new Uint8Array(this.aheadSize);
76
+ this.cache = new BlockCache(this.sectorSize, aheadRange, memoryLimit);
77
+
78
+ this.retries = 3;
79
+
80
+ this.reconnect();
81
+ }
82
+
83
+ public onError(errorFn: (e: Error) => void) {
84
+ this.errorFn = errorFn;
85
+ }
86
+
87
+ public onOpen(openFn: (read: boolean, write: boolean) => void) {
88
+ this.openFn = openFn;
89
+ }
90
+
91
+
92
+ public reconnect(): void {
93
+ this.socket = new Promise<WebSocket>((resolve, reject) => {
94
+ const socket = new WebSocket(this.endpoint);
95
+ socket.binaryType = "arraybuffer";
96
+ const onMessage = this.onMessage.bind(this);
97
+ const onOpen = () => {
98
+ const onInit = (event: { data: string }) => {
99
+ socket.removeEventListener("message", onInit);
100
+ if (event.data === "write" || event.data === "read") {
101
+ socket.addEventListener("message", onMessage);
102
+ this.openFn(true, event.data === "write");
103
+ resolve(socket);
104
+ } else {
105
+ const error = new Error(event.data ?? "Unable to establish connection");
106
+ this.errorFn(error);
107
+ reject(error);
108
+ }
109
+ };
110
+ socket.addEventListener("message", onInit);
111
+ socket.send(this.owner + "&" + this.drive + "&" + this.token);
112
+ };
113
+ const onError = (e: Event) => {
114
+ console.error("Network error", e, "will reconnect");
115
+ cleanup();
116
+ socket.close();
117
+ this.retries--;
118
+ if (this.retries === 0) {
119
+ const error = new Error("Network problem");
120
+ this.errorFn(error);
121
+ reject(error);
122
+ } else {
123
+ setTimeout(this.reconnect.bind(this), 300);
124
+ }
125
+ };
126
+ socket.addEventListener("error", onError);
127
+ socket.addEventListener("open", onOpen);
128
+ const cleanup = function() {
129
+ socket.removeEventListener("message", onMessage);
130
+ socket.removeEventListener("open", onOpen);
131
+ socket.removeEventListener("error", onError);
132
+ };
133
+ this.cleanup = cleanup;
134
+ });
135
+
136
+ if (this.request !== null) {
137
+ this.executeRequest(this.request);
138
+ }
139
+ }
140
+
141
+ public read(sector: number, buffer: Ptr, sync: boolean): Promise<number> | number {
142
+ if (this.request !== null && this.request.type === 1) {
143
+ console.error("New read request while old one is still processed");
144
+ return sync ? 3 : Promise.resolve(3);
145
+ }
146
+
147
+ const cached = this.cache.read(sector);
148
+ if (cached !== null) {
149
+ if (sync) {
150
+ this.stats.cacheHit++;
151
+ }
152
+ this.request = null;
153
+ this.module.HEAPU8.set(cached, buffer);
154
+ return sync ? 0 : Promise.resolve(0);
155
+ } else if (sync) {
156
+ return 255;
157
+ }
158
+
159
+ this.stats.cacheMiss++;
160
+
161
+ return new Promise<number>((resolve) => {
162
+ this.request = {
163
+ type: 1,
164
+ sector,
165
+ buffer,
166
+ resolve,
167
+ };
168
+ this.executeRequest(this.request);
169
+ });
170
+ }
171
+
172
+ public write(sector: number, buffer: Ptr): number {
173
+ this.request = {
174
+ type: 2,
175
+ sector,
176
+ buffer: this.module.HEAPU8.slice(buffer, buffer + this.sectorSize),
177
+ resolve: () => {/**/},
178
+ };
179
+ this.cache.write(sector, this.request.buffer as Uint8Array);
180
+ this.executeRequest(this.request);
181
+
182
+ return 0;
183
+ }
184
+
185
+ public async close() {
186
+ try {
187
+ const socket = await this.socket;
188
+ await new Promise<void>((resolve) => {
189
+ const intervalId = setInterval(() => {
190
+ if (socket.bufferedAmount === 0) {
191
+ clearInterval(intervalId);
192
+ resolve();
193
+ }
194
+ }, 32);
195
+ });
196
+ this.cleanup();
197
+ socket.close();
198
+ } finally {
199
+ this.readAheadBuffer = 0;
200
+ }
201
+ }
202
+
203
+ private executeRequest(request: Request) {
204
+ this.socket.then((socket) => {
205
+ if (request.type === 1) {
206
+ const { sector } = request;
207
+ const origin = this.cache.getOrigin(sector);
208
+ this.readStartedAt = Date.now();
209
+ this.readAheadPos = 0;
210
+ this.readAheadCompressed = 0;
211
+
212
+ this.readBuffer[0] = 1; // read
213
+ this.readBuffer[1] = origin & 0xFF;
214
+ this.readBuffer[2] = (origin >> 8) & 0xFF;
215
+ this.readBuffer[3] = (origin >> 16) & 0xFF;
216
+ this.readBuffer[4] = (origin >> 24) & 0xFF;
217
+ this.readBuffer[5] = this.aheadRange;
218
+ socket.send(this.readBuffer.buffer);
219
+ } else {
220
+ const { sector, buffer, resolve } = request;
221
+ this.stats.write += this.sectorSize;
222
+ this.writeBuffer[0] = 2; // write
223
+ this.writeBuffer[1] = sector & 0xFF;
224
+ this.writeBuffer[2] = (sector >> 8) & 0xFF;
225
+ this.writeBuffer[3] = (sector >> 16) & 0xFF;
226
+ this.writeBuffer[4] = (sector >> 24) & 0xFF;
227
+ // TBD: maybe do not copy and send just slice ???
228
+ this.writeBuffer.set(buffer as Uint8Array, 5);
229
+ socket.send(this.writeBuffer.buffer);
230
+ resolve(0);
231
+ }
232
+ });
233
+ }
234
+
235
+ public onMessage(event: { data: ArrayBuffer }) {
236
+ if (this.request === null) {
237
+ console.error("Received message without request");
238
+ this.reconnect();
239
+ } else if (this.request.type === 2) {
240
+ console.error("Received read payload while write request");
241
+ this.reconnect();
242
+ } else if (event.data instanceof ArrayBuffer) {
243
+ let data = new Uint8Array(event.data);
244
+ if (this.readAheadCompressed === 0) {
245
+ this.readAheadCompressed = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24);
246
+ data = data.slice(4);
247
+ }
248
+
249
+ const { sector, buffer, resolve } = this.request;
250
+ const restLength = this.readAheadCompressed - this.readAheadPos;
251
+ if (data.byteLength > restLength || restLength < 0 || restLength > this.aheadSize) {
252
+ console.error("wrong read payload length " + data.byteLength + " instead of " + restLength);
253
+ resolve(3);
254
+ } else {
255
+ this.module.HEAPU8.set(data, this.readAheadBuffer + this.readAheadPos);
256
+ this.readAheadPos += data.byteLength;
257
+
258
+ if (this.readAheadPos == this.readAheadCompressed) {
259
+ let decodeResult = this.aheadSize;
260
+ if (this.readAheadCompressed != this.aheadSize) {
261
+ if (this.module._decode_lz4_block !== undefined) {
262
+ decodeResult = this.module._decode_lz4_block(
263
+ this.readAheadCompressed, this.aheadSize, this.readAheadBuffer);
264
+ } else {
265
+ const result = decodeLz4(this.module.HEAPU8.slice(this.readAheadBuffer,
266
+ this.readAheadBuffer + this.aheadSize), this.decodeBuffer, 0, 0);
267
+ if (result < 0) {
268
+ decodeResult = result;
269
+ } else {
270
+ this.module.HEAPU8.set(this.decodeBuffer, this.readAheadBuffer);
271
+ }
272
+ }
273
+ }
274
+
275
+ if (decodeResult != this.aheadSize) {
276
+ console.error("wrong decode result " + decodeResult + " should be " + this.aheadSize);
277
+ resolve(4);
278
+ } else {
279
+ const origin = this.cache.getOrigin(sector);
280
+ this.cache.create(origin,
281
+ this.module.HEAPU8.slice(this.readAheadBuffer, this.readAheadBuffer + this.aheadSize));
282
+ this.stats.cacheUsed = this.cache.memUsed();
283
+ const offset = sector - origin;
284
+ this.module.HEAPU8.set(
285
+ this.module.HEAPU8.slice(this.readAheadBuffer + offset * this.sectorSize,
286
+ this.readAheadBuffer + (offset + 1) * this.sectorSize),
287
+ buffer as number);
288
+ this.stats.read += this.readAheadCompressed;
289
+ this.stats.readTotalTime += Date.now() - this.readStartedAt;
290
+ this.request = null;
291
+ resolve(0);
292
+ }
293
+ }
294
+ }
295
+ } else {
296
+ console.error("Received non arraybuffer data");
297
+ this.reconnect();
298
+ }
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Decode a block. Assumptions: input contains all sequences of a
304
+ * chunk, output is large enough to receive the decoded data.
305
+ * If the output buffer is too small, an error will be thrown.
306
+ * If the returned value is negative, an error occured at the returned offset.
307
+ *
308
+ * @param {ArrayBufferView} input input data
309
+ * @param {ArrayBufferView} output output data
310
+ * @param {number=} sIdx
311
+ * @param {number=} eIdx
312
+ * @return {number} number of decoded bytes
313
+ * @private
314
+ */
315
+ function decodeLz4(input: Uint8Array, output: Uint8Array, sIdx: number, eIdx: number) {
316
+ sIdx = sIdx || 0;
317
+ eIdx = eIdx || (input.length - sIdx);
318
+ // Process each sequence in the incoming data
319
+ let i; let n; let j;
320
+ for (i = sIdx, n = eIdx, j = 0; i < n;) {
321
+ const token = input[i++];
322
+
323
+ // Literals
324
+ let literalsLength = (token >> 4);
325
+ if (literalsLength > 0) {
326
+ // length of literals
327
+ let l = literalsLength + 240;
328
+ while (l === 255) {
329
+ l = input[i++];
330
+ literalsLength += l;
331
+ }
332
+
333
+ // Copy the literals
334
+ const end = i + literalsLength;
335
+ while (i < end) output[j++] = input[i++];
336
+
337
+ // End of buffer?
338
+ if (i === n) return j;
339
+ }
340
+
341
+ // Match copy
342
+ // 2 bytes offset (little endian)
343
+ const offset = input[i++] | (input[i++] << 8);
344
+
345
+ // XXX 0 is an invalid offset value
346
+ if (offset === 0) return j;
347
+ if (offset > j) return -(i - 2);
348
+
349
+ // length of match copy
350
+ let matchLength = (token & 0xf);
351
+ let l = matchLength + 240;
352
+ while (l === 255) {
353
+ l = input[i++];
354
+ matchLength += l;
355
+ }
356
+
357
+ // Copy the match
358
+ let pos = j - offset; // position of the match copy in the current output
359
+ const end = j + matchLength + 4; // minmatch = 4
360
+ while (j < end) output[j++] = output[pos++];
361
+ }
362
+
363
+ return j;
364
+ };
@@ -0,0 +1,221 @@
1
+ /* eslint-disable */
2
+
3
+ // This code is from https://github.com/rsms/js-lru
4
+
5
+ /**
6
+ * A doubly linked list-based Least Recently Used (LRU) cache. Will keep most
7
+ * recently used items while discarding least recently used items when its limit
8
+ * is reached.
9
+ *
10
+ * Licensed under MIT. Copyright (c) 2010 Rasmus Andersson <http://hunch.se/>
11
+ * See README.md for details.
12
+ *
13
+ * Illustration of the design:
14
+ *
15
+ * entry entry entry entry
16
+ * ______ ______ ______ ______
17
+ * | head |.newer => | |.newer => | |.newer => | tail |
18
+ * | A | | B | | C | | D |
19
+ * |______| <= older.|______| <= older.|______| <= older.|______|
20
+ *
21
+ * removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added
22
+ */
23
+ const NEWER = Symbol("newer");
24
+ const OLDER = Symbol("older");
25
+
26
+ export class LRUMap {
27
+ size: number;
28
+ limit: number;
29
+ newest: any;
30
+ oldest: any;
31
+ _keymap: Map<any, any>;
32
+
33
+ constructor(limit: number, entries?: any) {
34
+ if (typeof limit !== "number") {
35
+ // called as (entries)
36
+ entries = limit;
37
+ limit = 0;
38
+ }
39
+
40
+ this.size = 0;
41
+ this.limit = limit;
42
+ this.oldest = this.newest = undefined;
43
+ this._keymap = new Map();
44
+
45
+ if (entries) {
46
+ this.assign(entries);
47
+ if (limit < 1) {
48
+ this.limit = this.size;
49
+ }
50
+ }
51
+ }
52
+
53
+ _markEntryAsUsed(entry: any) {
54
+ if (entry === this.newest) {
55
+ // Already the most recenlty used entry, so no need to update the list
56
+ return;
57
+ }
58
+ // HEAD--------------TAIL
59
+ // <.older .newer>
60
+ // <--- add direction --
61
+ // A B C <D> E
62
+ if (entry[NEWER]) {
63
+ if (entry === this.oldest) {
64
+ this.oldest = entry[NEWER];
65
+ }
66
+ entry[NEWER][OLDER] = entry[OLDER]; // C <-- E.
67
+ }
68
+ if (entry[OLDER]) {
69
+ entry[OLDER][NEWER] = entry[NEWER]; // C. --> E
70
+ }
71
+ entry[NEWER] = undefined; // D --x
72
+ entry[OLDER] = this.newest; // D. --> E
73
+ if (this.newest) {
74
+ this.newest[NEWER] = entry; // E. <-- D
75
+ }
76
+ this.newest = entry;
77
+ }
78
+
79
+ assign(entries: any) {
80
+ let entry; let limit = this.limit || Number.MAX_VALUE;
81
+ this._keymap.clear();
82
+ const it = entries[Symbol.iterator]();
83
+ for (let itv = it.next(); !itv.done; itv = it.next()) {
84
+ const e = new (Entry as any)(itv.value[0], itv.value[1]);
85
+ this._keymap.set(e.key, e);
86
+ if (!entry) {
87
+ this.oldest = e;
88
+ } else {
89
+ entry[NEWER] = e;
90
+ e[OLDER] = entry;
91
+ }
92
+ entry = e;
93
+ if (limit-- == 0) {
94
+ throw new Error("overflow");
95
+ }
96
+ }
97
+ this.newest = entry;
98
+ this.size = this._keymap.size;
99
+ }
100
+
101
+ get(key: any) {
102
+ // First, find our cache entry
103
+ const entry = this._keymap.get(key);
104
+ if (!entry) return; // Not cached. Sorry.
105
+ // As <key> was found in the cache, register it as being requested recently
106
+ this._markEntryAsUsed(entry);
107
+ return entry.value;
108
+ }
109
+
110
+ set(key: any, value: any) {
111
+ let entry = this._keymap.get(key);
112
+
113
+ if (entry) {
114
+ // update existing
115
+ entry.value = value;
116
+ this._markEntryAsUsed(entry);
117
+ return this;
118
+ }
119
+
120
+ // new entry
121
+ this._keymap.set(key, (entry = new (Entry as any)(key, value)));
122
+
123
+ if (this.newest) {
124
+ // link previous tail to the new tail (entry)
125
+ this.newest[NEWER] = entry;
126
+ entry[OLDER] = this.newest;
127
+ } else {
128
+ // we're first in -- yay
129
+ this.oldest = entry;
130
+ }
131
+
132
+ // add new entry to the end of the linked list -- it's now the freshest entry.
133
+ this.newest = entry;
134
+ ++this.size;
135
+ if (this.size > this.limit) {
136
+ // we hit the limit -- remove the head
137
+ this.shift();
138
+ }
139
+
140
+ return this;
141
+ }
142
+
143
+ shift() {
144
+ // todo: handle special case when limit == 1
145
+ const entry = this.oldest;
146
+ if (entry) {
147
+ if (this.oldest[NEWER]) {
148
+ // advance the list
149
+ this.oldest = this.oldest[NEWER];
150
+ this.oldest[OLDER] = undefined;
151
+ } else {
152
+ // the cache is exhausted
153
+ this.oldest = undefined;
154
+ this.newest = undefined;
155
+ }
156
+ // Remove last strong reference to <entry> and remove links from the purged
157
+ // entry being returned:
158
+ entry[NEWER] = entry[OLDER] = undefined;
159
+ this._keymap.delete(entry.key);
160
+ --this.size;
161
+ return [entry.key, entry.value];
162
+ }
163
+ }
164
+ }
165
+
166
+ function Entry(this: any, key: any, value: any) {
167
+ this.key = key;
168
+ this.value = value;
169
+ this[NEWER] = undefined;
170
+ this[OLDER] = undefined;
171
+ }
172
+
173
+
174
+ function EntryIterator(this: any, oldestEntry: any) {
175
+ this.entry = oldestEntry;
176
+ }
177
+ EntryIterator.prototype[Symbol.iterator] = function () {
178
+ return this;
179
+ };
180
+ EntryIterator.prototype.next = function () {
181
+ const ent = this.entry;
182
+ if (ent) {
183
+ this.entry = ent[NEWER];
184
+ return { done: false, value: [ent.key, ent.value] };
185
+ } else {
186
+ return { done: true, value: undefined };
187
+ }
188
+ };
189
+
190
+
191
+ function KeyIterator(this: any, oldestEntry: any) {
192
+ this.entry = oldestEntry;
193
+ }
194
+ KeyIterator.prototype[Symbol.iterator] = function () {
195
+ return this;
196
+ };
197
+ KeyIterator.prototype.next = function () {
198
+ const ent = this.entry;
199
+ if (ent) {
200
+ this.entry = ent[NEWER];
201
+ return { done: false, value: ent.key };
202
+ } else {
203
+ return { done: true, value: undefined };
204
+ }
205
+ };
206
+
207
+ function ValueIterator(this: any, oldestEntry: any) {
208
+ this.entry = oldestEntry;
209
+ }
210
+ ValueIterator.prototype[Symbol.iterator] = function () {
211
+ return this;
212
+ };
213
+ ValueIterator.prototype.next = function () {
214
+ const ent = this.entry;
215
+ if (ent) {
216
+ this.entry = ent[NEWER];
217
+ return { done: false, value: ent.value };
218
+ } else {
219
+ return { done: true, value: undefined };
220
+ }
221
+ };
@@ -0,0 +1,10 @@
1
+ export type Handle = number;
2
+ export type Ptr = number;
3
+ export interface Stats {
4
+ read: number,
5
+ write: number,
6
+ readTotalTime: number,
7
+ cacheHit: number,
8
+ cacheMiss: number,
9
+ cacheUsed: number,
10
+ };
@@ -0,0 +1,64 @@
1
+ interface Stats {
2
+ read: number,
3
+ write: number,
4
+ readTotalTime: number,
5
+ cacheHit: number,
6
+ cacheMiss: number,
7
+ cacheUsed: number,
8
+ }
9
+ interface StatsBase<T> {
10
+ isFile(): boolean;
11
+ isDirectory(): boolean;
12
+ isBlockDevice(): boolean;
13
+ isCharacterDevice(): boolean;
14
+ isSymbolicLink(): boolean;
15
+ isFIFO(): boolean;
16
+ isSocket(): boolean;
17
+ dev: T;
18
+ ino: T;
19
+ mode: T;
20
+ nlink: T;
21
+ uid: T;
22
+ gid: T;
23
+ rdev: T;
24
+ size: T;
25
+ blksize: T;
26
+ blocks: T;
27
+ atimeMs: T;
28
+ mtimeMs: T;
29
+ ctimeMs: T;
30
+ birthtimeMs: T;
31
+ atime: Date;
32
+ mtime: Date;
33
+ ctime: Date;
34
+ birthtime: Date;
35
+ }
36
+ interface Stats extends StatsBase<number> { }
37
+ type FileDescriptor = number;
38
+ export interface SockdriveInstance {
39
+ stats: Stats,
40
+ fs: {
41
+ mkdir: (path: string) => Promise<void>,
42
+ readdir: (path: string) => Promise<string[]>,
43
+ fopen: (path: string, flags: string | number, mode: number) => Promise<FileDescriptor>,
44
+ fread: (fd: FileDescriptor, buf: Uint8Array, offset: number,
45
+ length: number, pos: number | null) => Promise<number>,
46
+ fwrite: (fd: FileDescriptor, buf: Uint8Array, offset: number,
47
+ length: number, pos: number | null) => Promise<number>,
48
+ fstat: (fd: FileDescriptor) => Promise<Stats>,
49
+ fclose: (fd: FileDescriptor) => Promise<void>,
50
+ stat: (path: string) => Promise<Stats>,
51
+ isdir: (path: string) => Promise<boolean>,
52
+ exists: (path: string) => Promise<boolean>,
53
+ }
54
+ close: () => void,
55
+ }
56
+
57
+ export type CreateSockdriveFileSystem = (
58
+ endpoint: string,
59
+ ownerId: string,
60
+ driveId: string,
61
+ token: string,
62
+ onOpen: (read: boolean, write: boolean) => void,
63
+ onError: (e: Error) => void
64
+ ) => Promise<SockdriveInstance>;