@difizen/libro-kernel 0.0.2-alpha.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.
Files changed (188) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/es/basemanager.d.ts +94 -0
  4. package/es/basemanager.d.ts.map +1 -0
  5. package/es/basemanager.js +110 -0
  6. package/es/contents/contents-drive.d.ts +189 -0
  7. package/es/contents/contents-drive.d.ts.map +1 -0
  8. package/es/contents/contents-drive.js +792 -0
  9. package/es/contents/contents-manager.d.ts +229 -0
  10. package/es/contents/contents-manager.d.ts.map +1 -0
  11. package/es/contents/contents-manager.js +551 -0
  12. package/es/contents/contents-module.d.ts +3 -0
  13. package/es/contents/contents-module.d.ts.map +1 -0
  14. package/es/contents/contents-module.js +4 -0
  15. package/es/contents/contents-protocol.d.ts +487 -0
  16. package/es/contents/contents-protocol.d.ts.map +1 -0
  17. package/es/contents/contents-protocol.js +1 -0
  18. package/es/contents/index.d.ts +6 -0
  19. package/es/contents/index.d.ts.map +1 -0
  20. package/es/contents/index.js +5 -0
  21. package/es/contents/validate.d.ts +10 -0
  22. package/es/contents/validate.d.ts.map +1 -0
  23. package/es/contents/validate.js +22 -0
  24. package/es/index.d.ts +10 -0
  25. package/es/index.d.ts.map +1 -0
  26. package/es/index.js +9 -0
  27. package/es/index.less +0 -0
  28. package/es/kernel/comm.d.ts +92 -0
  29. package/es/kernel/comm.d.ts.map +1 -0
  30. package/es/kernel/comm.js +216 -0
  31. package/es/kernel/future.d.ts +178 -0
  32. package/es/kernel/future.d.ts.map +1 -0
  33. package/es/kernel/future.js +587 -0
  34. package/es/kernel/index.d.ts +8 -0
  35. package/es/kernel/index.d.ts.map +1 -0
  36. package/es/kernel/index.js +8 -0
  37. package/es/kernel/kernel-connection.d.ts +550 -0
  38. package/es/kernel/kernel-connection.d.ts.map +1 -0
  39. package/es/kernel/kernel-connection.js +1957 -0
  40. package/es/kernel/kernel-module.d.ts +3 -0
  41. package/es/kernel/kernel-module.d.ts.map +1 -0
  42. package/es/kernel/kernel-module.js +32 -0
  43. package/es/kernel/libro-kernel-manager.d.ts +69 -0
  44. package/es/kernel/libro-kernel-manager.d.ts.map +1 -0
  45. package/es/kernel/libro-kernel-manager.js +349 -0
  46. package/es/kernel/libro-kernel-protocol.d.ts +675 -0
  47. package/es/kernel/libro-kernel-protocol.d.ts.map +1 -0
  48. package/es/kernel/libro-kernel-protocol.js +60 -0
  49. package/es/kernel/libro-kernel-utils.d.ts +95 -0
  50. package/es/kernel/libro-kernel-utils.d.ts.map +1 -0
  51. package/es/kernel/libro-kernel-utils.js +130 -0
  52. package/es/kernel/libro-kernel.d.ts +14 -0
  53. package/es/kernel/libro-kernel.d.ts.map +1 -0
  54. package/es/kernel/libro-kernel.js +54 -0
  55. package/es/kernel/messages.d.ts +845 -0
  56. package/es/kernel/messages.d.ts.map +1 -0
  57. package/es/kernel/messages.js +457 -0
  58. package/es/kernel/restapi.d.ts +78 -0
  59. package/es/kernel/restapi.d.ts.map +1 -0
  60. package/es/kernel/restapi.js +367 -0
  61. package/es/kernel/serialize.d.ts +10 -0
  62. package/es/kernel/serialize.d.ts.map +1 -0
  63. package/es/kernel/serialize.js +214 -0
  64. package/es/kernel/validate.d.ts +15 -0
  65. package/es/kernel/validate.d.ts.map +1 -0
  66. package/es/kernel/validate.js +125 -0
  67. package/es/kernelspec/index.d.ts +5 -0
  68. package/es/kernelspec/index.d.ts.map +1 -0
  69. package/es/kernelspec/index.js +4 -0
  70. package/es/kernelspec/kernelspec-module.d.ts +3 -0
  71. package/es/kernelspec/kernelspec-module.d.ts.map +1 -0
  72. package/es/kernelspec/kernelspec-module.js +4 -0
  73. package/es/kernelspec/kernelspec.d.ts +33 -0
  74. package/es/kernelspec/kernelspec.d.ts.map +1 -0
  75. package/es/kernelspec/kernelspec.js +1 -0
  76. package/es/kernelspec/manager.d.ts +81 -0
  77. package/es/kernelspec/manager.d.ts.map +1 -0
  78. package/es/kernelspec/manager.js +248 -0
  79. package/es/kernelspec/restapi.d.ts +71 -0
  80. package/es/kernelspec/restapi.d.ts.map +1 -0
  81. package/es/kernelspec/restapi.js +107 -0
  82. package/es/kernelspec/validate.d.ts +10 -0
  83. package/es/kernelspec/validate.d.ts.map +1 -0
  84. package/es/kernelspec/validate.js +69 -0
  85. package/es/libro-kernel-connection-manager.d.ts +19 -0
  86. package/es/libro-kernel-connection-manager.d.ts.map +1 -0
  87. package/es/libro-kernel-connection-manager.js +142 -0
  88. package/es/module.d.ts +3 -0
  89. package/es/module.d.ts.map +1 -0
  90. package/es/module.js +9 -0
  91. package/es/page-config.d.ts +36 -0
  92. package/es/page-config.d.ts.map +1 -0
  93. package/es/page-config.js +129 -0
  94. package/es/protocol.d.ts +13 -0
  95. package/es/protocol.d.ts.map +1 -0
  96. package/es/protocol.js +8 -0
  97. package/es/server/connection-error.d.ts +36 -0
  98. package/es/server/connection-error.d.ts.map +1 -0
  99. package/es/server/connection-error.js +109 -0
  100. package/es/server/index.d.ts +6 -0
  101. package/es/server/index.d.ts.map +1 -0
  102. package/es/server/index.js +5 -0
  103. package/es/server/server-connection-protocol.d.ts +49 -0
  104. package/es/server/server-connection-protocol.d.ts.map +1 -0
  105. package/es/server/server-connection-protocol.js +0 -0
  106. package/es/server/server-connection.d.ts +25 -0
  107. package/es/server/server-connection.d.ts.map +1 -0
  108. package/es/server/server-connection.js +159 -0
  109. package/es/server/server-manager.d.ts +22 -0
  110. package/es/server/server-manager.d.ts.map +1 -0
  111. package/es/server/server-manager.js +163 -0
  112. package/es/server/server-module.d.ts +3 -0
  113. package/es/server/server-module.d.ts.map +1 -0
  114. package/es/server/server-module.js +4 -0
  115. package/es/session/index.d.ts +5 -0
  116. package/es/session/index.d.ts.map +1 -0
  117. package/es/session/index.js +4 -0
  118. package/es/session/libro-session-manager.d.ts +71 -0
  119. package/es/session/libro-session-manager.d.ts.map +1 -0
  120. package/es/session/libro-session-manager.js +539 -0
  121. package/es/session/libro-session-protocol.d.ts +50 -0
  122. package/es/session/libro-session-protocol.d.ts.map +1 -0
  123. package/es/session/libro-session-protocol.js +21 -0
  124. package/es/session/libro-session.d.ts +12 -0
  125. package/es/session/libro-session.d.ts.map +1 -0
  126. package/es/session/libro-session.js +19 -0
  127. package/es/session/restapi.d.ts +28 -0
  128. package/es/session/restapi.d.ts.map +1 -0
  129. package/es/session/restapi.js +214 -0
  130. package/es/session/session-module.d.ts +3 -0
  131. package/es/session/session-module.d.ts.map +1 -0
  132. package/es/session/session-module.js +18 -0
  133. package/es/session/validate.d.ts +14 -0
  134. package/es/session/validate.d.ts.map +1 -0
  135. package/es/session/validate.js +37 -0
  136. package/es/utils.d.ts +4 -0
  137. package/es/utils.d.ts.map +1 -0
  138. package/es/utils.js +29 -0
  139. package/es/validate-property.d.ts +2 -0
  140. package/es/validate-property.d.ts.map +1 -0
  141. package/es/validate-property.js +35 -0
  142. package/package.json +62 -0
  143. package/src/basemanager.ts +133 -0
  144. package/src/contents/contents-drive.ts +495 -0
  145. package/src/contents/contents-manager.ts +465 -0
  146. package/src/contents/contents-module.ts +6 -0
  147. package/src/contents/contents-protocol.ts +604 -0
  148. package/src/contents/index.ts +5 -0
  149. package/src/contents/validate.ts +29 -0
  150. package/src/index.tsx +9 -0
  151. package/src/kernel/comm.ts +220 -0
  152. package/src/kernel/future.ts +474 -0
  153. package/src/kernel/index.ts +7 -0
  154. package/src/kernel/kernel-connection.ts +1770 -0
  155. package/src/kernel/kernel-module.ts +50 -0
  156. package/src/kernel/libro-kernel-manager.ts +199 -0
  157. package/src/kernel/libro-kernel-protocol.ts +858 -0
  158. package/src/kernel/libro-kernel-utils.ts +152 -0
  159. package/src/kernel/libro-kernel.ts +39 -0
  160. package/src/kernel/messages.ts +1104 -0
  161. package/src/kernel/restapi.ts +183 -0
  162. package/src/kernel/serialize.ts +262 -0
  163. package/src/kernel/validate.ts +101 -0
  164. package/src/kernelspec/index.ts +5 -0
  165. package/src/kernelspec/kernelspec-module.ts +9 -0
  166. package/src/kernelspec/kernelspec.ts +37 -0
  167. package/src/kernelspec/manager.ts +173 -0
  168. package/src/kernelspec/restapi.ts +104 -0
  169. package/src/kernelspec/validate.ts +80 -0
  170. package/src/libro-kernel-connection-manager.ts +73 -0
  171. package/src/module.ts +19 -0
  172. package/src/page-config.ts +106 -0
  173. package/src/protocol.ts +24 -0
  174. package/src/server/connection-error.ts +60 -0
  175. package/src/server/index.ts +5 -0
  176. package/src/server/server-connection-protocol.ts +57 -0
  177. package/src/server/server-connection.ts +144 -0
  178. package/src/server/server-manager.ts +76 -0
  179. package/src/server/server-module.ts +9 -0
  180. package/src/session/index.ts +4 -0
  181. package/src/session/libro-session-manager.ts +377 -0
  182. package/src/session/libro-session-protocol.ts +61 -0
  183. package/src/session/libro-session.ts +33 -0
  184. package/src/session/restapi.ts +126 -0
  185. package/src/session/session-module.ts +26 -0
  186. package/src/session/validate.ts +39 -0
  187. package/src/utils.ts +28 -0
  188. package/src/validate-property.ts +38 -0
@@ -0,0 +1,465 @@
1
+ import { PathExt } from '@difizen/libro-common';
2
+ import type { Event as ManaEvent } from '@difizen/mana-app';
3
+ import { inject, singleton } from '@difizen/mana-app';
4
+ import { Emitter } from '@difizen/mana-app';
5
+
6
+ import { Drive } from './contents-drive.js';
7
+ import type { IContentsDrive } from './contents-protocol.js';
8
+ import type {
9
+ IContentsChangedArgs,
10
+ IContentsFetchOptions,
11
+ IContentsManager,
12
+ IContentsModel,
13
+ IContentsRequestOptions,
14
+ IContentsCreateOptions,
15
+ IContentsCheckpointModel,
16
+ } from './contents-protocol.js';
17
+
18
+ /**
19
+ * A contents manager that passes file operations to the server.
20
+ * Multiple servers implementing the `IContentsDrive` interface can be
21
+ * attached to the contents manager, so that the same session can
22
+ * perform file operations on multiple backends.
23
+ *
24
+ * This includes checkpointing with the normal file operations.
25
+ */
26
+ @singleton()
27
+ export class ContentsManager implements IContentsManager {
28
+ constructor(@inject(Drive) defaultDrive: IContentsDrive) {
29
+ this.defaultDrive = defaultDrive;
30
+ this.defaultDrive.fileChanged((args) =>
31
+ this.onFileChanged(this.defaultDrive, args),
32
+ );
33
+ }
34
+
35
+ /**
36
+ * The server settings associated with the manager.
37
+ */
38
+ // serverSettings: ISettings;
39
+
40
+ /**
41
+ * A signal emitted when a file operation takes place.
42
+ */
43
+ get fileChanged(): ManaEvent<IContentsChangedArgs> {
44
+ // return this._fileChanged;
45
+ return this.fileChangedEmitter.event;
46
+ }
47
+
48
+ /**
49
+ * Test whether the manager has been disposed.
50
+ */
51
+ get isDisposed(): boolean {
52
+ return this._isDisposed;
53
+ }
54
+
55
+ /**
56
+ * Dispose of the resources held by the manager.
57
+ */
58
+ dispose(): void {
59
+ if (this.isDisposed) {
60
+ return;
61
+ }
62
+ this._isDisposed = true;
63
+ this.fileChangedEmitter.dispose();
64
+ }
65
+
66
+ /**
67
+ * Add an `IContentsDrive` to the manager.
68
+ */
69
+ addDrive(drive: IContentsDrive): void {
70
+ this.additionalDrives.set(drive.name, drive);
71
+ drive.fileChanged((args) => this.onFileChanged(drive, args));
72
+ }
73
+
74
+ /**
75
+ * Given a path, get a ModelDB.IFactory from the
76
+ * relevant backend. Returns `undefined` if the backend
77
+ * does not provide one.
78
+ */
79
+ // getModelDBFactory(path: string): ModelDB.IFactory | null {
80
+ // const [drive] = this.driveForPath(path);
81
+ // return drive?.modelDBFactory ?? null;
82
+ // }
83
+
84
+ /**
85
+ * Given a path of the form `drive:local/portion/of/it.txt`
86
+ * get the local part of it.
87
+ *
88
+ * @param path: the path.
89
+ *
90
+ * @returns The local part of the path.
91
+ */
92
+ localPath(path: string): string {
93
+ const parts = path.split('/');
94
+ const firstParts = parts[0].split(':');
95
+ if (firstParts.length === 1 || !this.additionalDrives.has(firstParts[0])) {
96
+ return PathExt.removeSlash(path);
97
+ }
98
+ return PathExt.join(firstParts.slice(1).join(':'), ...parts.slice(1));
99
+ }
100
+
101
+ /**
102
+ * Normalize a global path. Reduces '..' and '.' parts, and removes
103
+ * leading slashes from the local part of the path, while retaining
104
+ * the drive name if it exists.
105
+ *
106
+ * @param path: the path.
107
+ *
108
+ * @returns The normalized path.
109
+ */
110
+ normalize(path: string): string {
111
+ const parts = path.split(':');
112
+ if (parts.length === 1) {
113
+ return PathExt.normalize(path);
114
+ }
115
+ return `${parts[0]}:${PathExt.normalize(parts.slice(1).join(':'))}`;
116
+ }
117
+
118
+ /**
119
+ * Resolve a global path, starting from the root path. Behaves like
120
+ * posix-path.resolve, with 3 differences:
121
+ * - will never prepend cwd
122
+ * - if root has a drive name, the result is prefixed with "<drive>:"
123
+ * - before adding drive name, leading slashes are removed
124
+ *
125
+ * @param path: the path.
126
+ *
127
+ * @returns The normalized path.
128
+ */
129
+ resolvePath(root: string, path: string): string {
130
+ const driveName = this.driveName(root);
131
+ const localPath = this.localPath(root);
132
+ const resolved = PathExt.resolve('/', localPath, path);
133
+ return driveName ? `${driveName}:${resolved}` : resolved;
134
+ }
135
+
136
+ /**
137
+ * Given a path of the form `drive:local/portion/of/it.txt`
138
+ * get the name of the drive. If the path is missing
139
+ * a drive portion, returns an empty string.
140
+ *
141
+ * @param path: the path.
142
+ *
143
+ * @returns The drive name for the path, or the empty string.
144
+ */
145
+ driveName(path: string): string {
146
+ const parts = path.split('/');
147
+ const firstParts = parts[0].split(':');
148
+ if (firstParts.length === 1) {
149
+ return '';
150
+ }
151
+ if (this.additionalDrives.has(firstParts[0])) {
152
+ return firstParts[0];
153
+ }
154
+ return '';
155
+ }
156
+
157
+ /**
158
+ * Get a file or directory.
159
+ *
160
+ * @param path: The path to the file.
161
+ *
162
+ * @param options: The options used to fetch the file.
163
+ *
164
+ * @returns A promise which resolves with the file content.
165
+ */
166
+ get(path: string, options?: IContentsFetchOptions): Promise<IContentsModel> {
167
+ const [drive, localPath] = this.driveForPath(path);
168
+ return drive.get(localPath, options).then((contentsModel) => {
169
+ const listing: IContentsModel[] = [];
170
+ if (contentsModel.type === 'directory' && contentsModel.content) {
171
+ for (const item of contentsModel.content) {
172
+ listing.push({ ...item, path: this.toGlobalPath(drive, item.path) });
173
+ }
174
+ return {
175
+ ...contentsModel,
176
+ path: this.toGlobalPath(drive, localPath),
177
+ content: listing,
178
+ } as IContentsModel;
179
+ } else {
180
+ return {
181
+ ...contentsModel,
182
+ path: this.toGlobalPath(drive, localPath),
183
+ } as IContentsModel;
184
+ }
185
+ });
186
+ }
187
+
188
+ /**
189
+ * Get an encoded download url given a file path.
190
+ *
191
+ * @param path - An absolute POSIX file path on the server.
192
+ *
193
+ * #### Notes
194
+ * It is expected that the path contains no relative paths.
195
+ *
196
+ * The returned URL may include a query parameter.
197
+ */
198
+ getDownloadUrl(path: string, options?: IContentsRequestOptions): Promise<string> {
199
+ const [drive, localPath] = this.driveForPath(path);
200
+ return drive.getDownloadUrl(localPath, options);
201
+ }
202
+
203
+ /**
204
+ * Create a new untitled file or directory in the specified directory path.
205
+ *
206
+ * @param options: The options used to create the file.
207
+ *
208
+ * @returns A promise which resolves with the created file content when the
209
+ * file is created.
210
+ */
211
+ newUntitled(options: IContentsCreateOptions = {}): Promise<IContentsModel> {
212
+ if (options.path) {
213
+ const globalPath = this.normalize(options.path);
214
+ const [drive, localPath] = this.driveForPath(globalPath);
215
+ return drive
216
+ .newUntitled({ ...options, path: localPath })
217
+ .then((contentsModel) => {
218
+ return {
219
+ ...contentsModel,
220
+ path: PathExt.join(globalPath, contentsModel.name),
221
+ } as IContentsModel;
222
+ });
223
+ } else {
224
+ return this.defaultDrive.newUntitled(options);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Delete a file.
230
+ *
231
+ * @param path - The path to the file.
232
+ *
233
+ * @returns A promise which resolves when the file is deleted.
234
+ */
235
+ delete(path: string, options?: IContentsRequestOptions): Promise<void> {
236
+ const [drive, localPath] = this.driveForPath(path);
237
+ return drive.delete(localPath, options);
238
+ }
239
+
240
+ /**
241
+ * Rename a file or directory.
242
+ *
243
+ * @param path - The original file path.
244
+ *
245
+ * @param newPath - The new file path.
246
+ *
247
+ * @returns A promise which resolves with the new file contents model when
248
+ * the file is renamed.
249
+ */
250
+ rename(
251
+ path: string,
252
+ newPath: string,
253
+ options?: IContentsRequestOptions,
254
+ ): Promise<IContentsModel> {
255
+ const [drive1, path1] = this.driveForPath(path);
256
+ const [drive2, path2] = this.driveForPath(newPath);
257
+ if (drive1 !== drive2) {
258
+ throw Error('ContentsManager: renaming files must occur within a Drive');
259
+ }
260
+ return drive1.rename(path1, path2, options).then((contentsModel) => {
261
+ return {
262
+ ...contentsModel,
263
+ path: this.toGlobalPath(drive1, path2),
264
+ } as IContentsModel;
265
+ });
266
+ }
267
+
268
+ /**
269
+ * Save a file.
270
+ *
271
+ * @param path - The desired file path.
272
+ *
273
+ * @param options - Optional overrides to the model.
274
+ *
275
+ * @returns A promise which resolves with the file content model when the
276
+ * file is saved.
277
+ *
278
+ * #### Notes
279
+ * Ensure that `model.content` is populated for the file.
280
+ */
281
+ save(path: string, options: Partial<IContentsModel> = {}): Promise<IContentsModel> {
282
+ const globalPath = this.normalize(path);
283
+ const [drive, localPath] = this.driveForPath(path);
284
+ return drive
285
+ .save(localPath, { ...options, path: localPath })
286
+ .then((contentsModel) => {
287
+ return { ...contentsModel, path: globalPath } as IContentsModel;
288
+ });
289
+ }
290
+
291
+ /**
292
+ * Copy a file into a given directory.
293
+ *
294
+ * @param path - The original file path.
295
+ *
296
+ * @param toDir - The destination directory path.
297
+ *
298
+ * @returns A promise which resolves with the new contents model when the
299
+ * file is copied.
300
+ *
301
+ * #### Notes
302
+ * The server will select the name of the copied file.
303
+ */
304
+ copy(
305
+ fromFile: string,
306
+ toDir: string,
307
+ options?: IContentsRequestOptions,
308
+ ): Promise<IContentsModel> {
309
+ const [drive1, path1] = this.driveForPath(fromFile);
310
+ const [drive2, path2] = this.driveForPath(toDir);
311
+ if (drive1 === drive2) {
312
+ return drive1.copy(path1, path2, options).then((contentsModel) => {
313
+ return {
314
+ ...contentsModel,
315
+ path: this.toGlobalPath(drive1, contentsModel.path),
316
+ } as IContentsModel;
317
+ });
318
+ } else {
319
+ throw Error('Copying files between drives is not currently implemented');
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Create a checkpoint for a file.
325
+ *
326
+ * @param path - The path of the file.
327
+ *
328
+ * @returns A promise which resolves with the new checkpoint model when the
329
+ * checkpoint is created.
330
+ */
331
+ createCheckpoint(
332
+ path: string,
333
+ options?: IContentsRequestOptions,
334
+ ): Promise<IContentsCheckpointModel> {
335
+ const [drive, localPath] = this.driveForPath(path);
336
+ return drive.createCheckpoint(localPath, options);
337
+ }
338
+
339
+ /**
340
+ * List available checkpoints for a file.
341
+ *
342
+ * @param path - The path of the file.
343
+ *
344
+ * @returns A promise which resolves with a list of checkpoint models for
345
+ * the file.
346
+ */
347
+ listCheckpoints(
348
+ path: string,
349
+ options?: IContentsRequestOptions,
350
+ ): Promise<IContentsCheckpointModel[]> {
351
+ const [drive, localPath] = this.driveForPath(path);
352
+ return drive.listCheckpoints(localPath, options);
353
+ }
354
+
355
+ /**
356
+ * Restore a file to a known checkpoint state.
357
+ *
358
+ * @param path - The path of the file.
359
+ *
360
+ * @param checkpointID - The id of the checkpoint to restore.
361
+ *
362
+ * @returns A promise which resolves when the checkpoint is restored.
363
+ */
364
+ restoreCheckpoint(
365
+ path: string,
366
+ checkpointID: string,
367
+ options?: IContentsRequestOptions,
368
+ ): Promise<void> {
369
+ const [drive, localPath] = this.driveForPath(path);
370
+ return drive.restoreCheckpoint(localPath, checkpointID, options);
371
+ }
372
+
373
+ /**
374
+ * Delete a checkpoint for a file.
375
+ *
376
+ * @param path - The path of the file.
377
+ *
378
+ * @param checkpointID - The id of the checkpoint to delete.
379
+ *
380
+ * @returns A promise which resolves when the checkpoint is deleted.
381
+ */
382
+ deleteCheckpoint(
383
+ path: string,
384
+ checkpointID: string,
385
+ options?: IContentsRequestOptions,
386
+ ): Promise<void> {
387
+ const [drive, localPath] = this.driveForPath(path);
388
+ return drive.deleteCheckpoint(localPath, checkpointID, options);
389
+ }
390
+
391
+ /**
392
+ * Given a drive and a local path, construct a fully qualified
393
+ * path. The inverse of `driveForPath`.
394
+ *
395
+ * @param drive: an `IContentsDrive`.
396
+ *
397
+ * @param localPath: the local path on the drive.
398
+ *
399
+ * @returns the fully qualified path.
400
+ */
401
+ protected toGlobalPath(drive: IContentsDrive, localPath: string): string {
402
+ if (drive === this.defaultDrive) {
403
+ return PathExt.removeSlash(localPath);
404
+ } else {
405
+ return `${drive.name}:${PathExt.removeSlash(localPath)}`;
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Given a path, get the `IContentsDrive to which it refers,
411
+ * where the path satisfies the pattern
412
+ * `'driveName:path/to/file'`. If there is no `driveName`
413
+ * prepended to the path, it returns the default drive.
414
+ *
415
+ * @param path: a path to a file.
416
+ *
417
+ * @returns A tuple containing an `IContentsDrive` object for the path,
418
+ * and a local path for that drive.
419
+ */
420
+ protected driveForPath(path: string): [IContentsDrive, string] {
421
+ const driveName = this.driveName(path);
422
+ const localPath = this.localPath(path);
423
+ if (driveName) {
424
+ return [this.additionalDrives.get(driveName)!, localPath];
425
+ } else {
426
+ return [this.defaultDrive, localPath];
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Respond to fileChanged signals from the drives attached to
432
+ * the manager. This prepends the drive name to the path if necessary,
433
+ * and then forwards the signal.
434
+ */
435
+ protected onFileChanged(sender: IContentsDrive, args: IContentsChangedArgs) {
436
+ if (sender === this.defaultDrive) {
437
+ this.fileChangedEmitter.fire(args);
438
+ } else {
439
+ let newValue: Partial<IContentsModel> | null = null;
440
+ let oldValue: Partial<IContentsModel> | null = null;
441
+ if (args.newValue?.path) {
442
+ newValue = {
443
+ ...args.newValue,
444
+ path: this.toGlobalPath(sender, args.newValue.path),
445
+ };
446
+ }
447
+ if (args.oldValue?.path) {
448
+ oldValue = {
449
+ ...args.oldValue,
450
+ path: this.toGlobalPath(sender, args.oldValue.path),
451
+ };
452
+ }
453
+ this.fileChangedEmitter.fire({
454
+ type: args.type,
455
+ newValue,
456
+ oldValue,
457
+ });
458
+ }
459
+ }
460
+
461
+ protected _isDisposed = false;
462
+ protected additionalDrives = new Map<string, IContentsDrive>();
463
+ protected defaultDrive: IContentsDrive;
464
+ protected fileChangedEmitter = new Emitter<IContentsChangedArgs>();
465
+ }
@@ -0,0 +1,6 @@
1
+ import { ManaModule } from '@difizen/mana-app';
2
+
3
+ import { Drive } from './contents-drive.js';
4
+ import { ContentsManager } from './contents-manager.js';
5
+
6
+ export const LibroContentsModule = ManaModule.create().register(Drive, ContentsManager);