@iebh/tera-fy 1.6.0 → 1.7.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.
@@ -868,7 +868,7 @@ export default class TeraFyServer {
868
868
  }
869
869
  // }}}
870
870
 
871
- // Project files - selectProjectFile(), getProjectFiles(), getProjectFile(), setProjectFile() {{{
871
+ // Project files - selectProjectFile(), getProjectFiles(), getProjectFile(), createProjectFile(), deleteProjectFile(), setProjectFile() {{{
872
872
 
873
873
  /**
874
874
  * Data structure for a project file
@@ -914,7 +914,7 @@ export default class TeraFyServer {
914
914
  * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
915
915
  * @param {FileFilters} [options.filter] Optional file filters
916
916
  *
917
- * @returns {Promise<ProjectFile>} The eventually selected file
917
+ * @returns {Promise<ProjectFile>} The eventually selected file, if in save mode new files are created as stubs
918
918
  */
919
919
  selectProjectFile(options) {
920
920
  let settings = {
@@ -949,8 +949,8 @@ export default class TeraFyServer {
949
949
  filters: settings.filters,
950
950
  },
951
951
  componentEvents: {
952
- fileSave(filePath) {
953
- app.service('$prompt').close(true, filePath);
952
+ fileSave(file) {
953
+ app.service('$prompt').close(true, file);
954
954
  },
955
955
  fileSelect(file) {
956
956
  app.service('$prompt').close(true, file);
@@ -989,26 +989,91 @@ export default class TeraFyServer {
989
989
 
990
990
 
991
991
  /**
992
- * Fetch a project file
993
- * @param {String} path File path to read
994
- * @returns {Promise<Blob>} The eventual fetched file as a blob
992
+ * Fetch a project file by its name
993
+ *
994
+ * @param {String} name The name + relative directory path component
995
+ *
996
+ * @param {Object|String} [options] Additional options to mutate behaviour, if a string is given `options.subkey` is assumed
997
+ * @param {String} [options.subkey] If specified only the extracted subkey is returned rather than the full object
998
+ * @param {Boolean} [options.cache=true] Use the existing file cache if possible, set to false to force a refresh of files from the server first
999
+ *
1000
+ * @returns {Promise<ProjectFile>} The eventual fetched ProjectFile (or requested subkey)
995
1001
  */
996
- getProjectFile(path) {
997
- return app.service('$supabase').fileGet(path, {
998
- toast: false,
999
- });
1002
+ getProjectFile(name, options) {
1003
+ let settings = {
1004
+ subkey: null,
1005
+ cache: true,
1006
+ ...(typeof options == 'string' ? {subkey: options} : options),
1007
+ };
1008
+
1009
+ return Promise.resolve()
1010
+ .then(()=>
1011
+ app.service('$projects').activeFiles.length == 0 // If we have no files in the cache
1012
+ || !settings.cache // OR caching is disabled
1013
+ ? app.service('$projects').refreshFiles({ // Go refresh files first
1014
+ lazy: false,
1015
+ })
1016
+ : app.service('$projects').activeFiles // Otherwise use file cache
1017
+ )
1018
+ .then(files =>
1019
+ files.find(file =>
1020
+ file.name == name
1021
+ )
1022
+ )
1023
+ .then(file => file && settings.subkey ? file[settings.subkey] : file) // Provide subkey if asked
1024
+ }
1025
+
1026
+
1027
+ /**
1028
+ * Create a new file
1029
+ * This creates an empty file which can then be written to
1030
+ * This function also forces a local file list cache update
1031
+ *
1032
+ * @param {String} name The name + relative directory path component
1033
+ * @returns {Promise<ProjectFile>} The eventual ProjectFile created
1034
+ */
1035
+ createProjectFile(name) {
1036
+ return Promise.resolve()
1037
+ .then(()=> app.service('$supabase').fileUpload(app.service('$projects').convertRelativePath(name), {
1038
+ file: new Blob([''], {type: 'text/plain'}),
1039
+ mode: 'encoded',
1040
+ overwrite: false,
1041
+ multiple: false,
1042
+ toast: false,
1043
+ transcoders: false,
1044
+ }))
1045
+ .then(()=> this.getProjectFile(name, {
1046
+ cache: false, // Force cache to update, as this is a new file
1047
+ }))
1048
+ .then(file => file || Promise.reject(`Could not create new file "${name}"`))
1049
+ }
1050
+
1051
+
1052
+ /**
1053
+ * Remove a project file by its ID
1054
+ *
1055
+ * @param {String} id The File ID to remove
1056
+ *
1057
+ * @returns {Promise} A promise which resolves when the operation has completed
1058
+ */
1059
+ deleteProjectFile(id) {
1060
+ return app.service('$supabase').fileRemove(app.service('$projects').decodeFilePath(id))
1061
+ .then(()=> app.service('$projects').refreshFiles({ // Force a local file list update
1062
+ lazy: false,
1063
+ }))
1064
+ .then(()=> null)
1000
1065
  }
1001
1066
 
1002
1067
 
1003
1068
  /**
1004
1069
  * Replace a project files contents
1005
1070
  *
1006
- * @param {String} path File path to write
1071
+ * @param {String} id File to overwrite
1007
1072
  * @param {File|Blob|FormData|Object|Array} contents The new file contents
1008
1073
  * @returns {Promise} A promise which will resolve when the write operation has completed
1009
1074
  */
1010
- setProjectFile(path, contents) {
1011
- return app.service('$supabase').fileSet(path, contents, {
1075
+ setProjectFile(id, contents) {
1076
+ return app.service('$supabase').fileSet(app.service('$projects').decodeFilePath(id), contents, {
1012
1077
  overwrite: true,
1013
1078
  toast: false,
1014
1079
  });
@@ -1051,14 +1116,14 @@ export default class TeraFyServer {
1051
1116
 
1052
1117
  return app.service('$projects').promise()
1053
1118
  .then(()=> this.selectProjectFile(settings))
1054
- .then(selectedFile => this.getProjectLibrary(selectedFile.path, settings))
1119
+ .then(selectedFile => this.getProjectLibrary(selectedFile.id, settings))
1055
1120
  }
1056
1121
 
1057
1122
 
1058
1123
  /**
1059
1124
  * Fetch + convert a project file into a library of citations
1060
1125
  *
1061
- * @param {String} path File path to read, if omitted the contents of `options` are used to guess at a suitable file
1126
+ * @param {String} id File ID to read
1062
1127
  *
1063
1128
  * @param {Object} [options] Additional options to mutate behaviour
1064
1129
  * @param {String} [options.format='json'] Format for the file. ENUM: 'pojo' (return a parsed JS collection), 'blob' (raw JS Blob object), 'file' (named JS File object)
@@ -1068,7 +1133,7 @@ export default class TeraFyServer {
1068
1133
  *
1069
1134
  * @returns {Promise<Array<Ref>>|Promise<*>} A collection of references (default bevahiour) or a whatever format was requested
1070
1135
  */
1071
- getProjectLibrary(path, options) {
1136
+ getProjectLibrary(id, options) {
1072
1137
  let settings = {
1073
1138
  format: 'pojo',
1074
1139
  autoRequire: true,
@@ -1077,9 +1142,11 @@ export default class TeraFyServer {
1077
1142
  ...options,
1078
1143
  };
1079
1144
 
1145
+ let filePath = app.service('$projects').decodeFilePath(id);
1146
+
1080
1147
  return Promise.resolve()
1081
1148
  .then(()=> settings.autoRequire && this.requireProject())
1082
- .then(()=> app.service('$supabase').fileGet(path, {
1149
+ .then(()=> app.service('$supabase').fileGet(filePath, {
1083
1150
  toast: false,
1084
1151
  }))
1085
1152
  .then(blob => {
@@ -1089,7 +1156,7 @@ export default class TeraFyServer {
1089
1156
  return Reflib.uploadFile({
1090
1157
  file: new File(
1091
1158
  [blob],
1092
- app.service('$supabase')._parsePath(path).basename,
1159
+ app.service('$supabase')._parsePath(filePath).basename,
1093
1160
  ),
1094
1161
  });
1095
1162
  case 'blob':
@@ -1097,7 +1164,7 @@ export default class TeraFyServer {
1097
1164
  case 'file':
1098
1165
  return new File(
1099
1166
  [blob],
1100
- app.service('$supabase')._parsePath(path).basename,
1167
+ app.service('$supabase')._parsePath(filePath).basename,
1101
1168
  );
1102
1169
  default:
1103
1170
  throw new Error(`Unsupported library format "${settings.format}"`);
@@ -1109,23 +1176,23 @@ export default class TeraFyServer {
1109
1176
  /**
1110
1177
  * Save back a citation library from some input
1111
1178
  *
1112
- * @param {String} [path] File path to save back to, if omitted one will be prompted for
1179
+ * @param {String} [id] File ID to save back to, if omitted a file will be prompted for
1113
1180
  * @param {Array<RefLibRef>|Blob|File} [refs] Collection of references for the selected library or the raw Blob/File
1114
1181
  *
1115
1182
  * @param {Object} [options] Additional options to mutate behaviour
1116
- * @param {String} [options.path] Alternate method to specify the path to save as, if omitted one will be prompted for
1183
+ * @param {String} [options.id] Alternate method to specify the file ID to save as, if omitted one will be prompted for
1117
1184
  * @param {Array<RefLibRef>|Blob|File} [options.refs] Alternate method to specify the refs to save as an array or raw Blob/File
1118
1185
  * @param {String} [options.format='auto'] Input format used. ENUM: 'auto' (try to figure it out from context), 'pojo' (JS array of RefLib references), 'blob' (raw JS Blob object), 'file' (named JS File object)
1119
1186
  * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
1120
1187
  * @param {String|Array<String>} [options.hint] Hint(s) to store against the library. Generally corresponds to the current operation being performed - e.g. 'deduped'
1121
- * @param {String} [options.filename] Suggested filename if path is unspecified
1122
- * @param {String} [options.title='Save citation library'] Dialog title if path is unspecified and we need to prompt
1188
+ * @param {String} [options.filename] Suggested filename if `id` is unspecified
1189
+ * @param {String} [options.title='Save citation library'] Dialog title if `id` is unspecified and a prompt is necessary
1123
1190
  * @param {Boolean} [options.overwrite=true] Allow existing file upsert
1124
1191
  * @param {Object} [options.meta] Optional meta data to merge into the file data
1125
1192
  *
1126
1193
  * @returns {Promise} A promise which resolves when the save operation has completed
1127
1194
  */
1128
- setProjectLibrary(path, refs, options) {
1195
+ setProjectLibrary(id, refs, options) {
1129
1196
  let settings = {
1130
1197
  format: 'auto',
1131
1198
  autoRequire: true,
@@ -1135,17 +1202,18 @@ export default class TeraFyServer {
1135
1202
  overwrite: true,
1136
1203
  meta: null,
1137
1204
  ...(
1138
- typeof path == 'string' && Array.isArray(refs) ? {path, refs, ...options} // Called as (path, refs, options?)
1139
- : Array.isArray(path) || refs instanceof Blob || refs instanceof File ? {refs: path, ...refs} // Called as (refs, options?)
1140
- : path // Called as (options?)
1205
+ typeof id == 'string' && Array.isArray(refs) ? {id, refs, ...options} // Called as (id, refs, options?)
1206
+ : Array.isArray(id) || refs instanceof Blob || refs instanceof File ? {refs: id, ...refs} // Called as (refs, options?)
1207
+ : id // Called as (options?)
1141
1208
  )
1142
1209
  };
1143
1210
  if (!settings.refs) throw new Error('No refs to save');
1144
1211
 
1212
+ let filePath; // Eventual Supabase path to use
1145
1213
  return Promise.resolve()
1146
1214
  .then(()=> settings.autoRequire && this.requireProject())
1147
1215
  .then(()=> {
1148
- if (settings.path) return; // We already have a file path specified - skip
1216
+ if (settings.id) return; // We already have a file ID specified - skip
1149
1217
 
1150
1218
  // Prompt for a save filename
1151
1219
  return this.selectProjectFile({
@@ -1158,14 +1226,10 @@ export default class TeraFyServer {
1158
1226
  },
1159
1227
  autoRequire: false, // Handled above anyway
1160
1228
  })
1161
- .then(path => settings.path = path)
1229
+ .then(file => settings.id = file.id)
1162
1230
  })
1163
- .then(()=> { // Correct filename to a full path if needed
1164
- if (!/^\/.+\/.+/.test(settings.path)) { // Not already a qualified full path for a project
1165
- settings.path = `/projects/${app.service('$projects').active.id}/${settings.path}`;
1166
- }
1167
-
1168
- this.debug('INFO', 2, 'Save citation library as', path);
1231
+ .then(()=> { // Compute filePath
1232
+ filePath = app.service('$projects').decodeFilePath(settings.id);
1169
1233
  })
1170
1234
  .then(()=> {
1171
1235
  // Mutate settings.ref -> Blob or File format needed by Supabase
@@ -1184,12 +1248,12 @@ export default class TeraFyServer {
1184
1248
 
1185
1249
  // Get Reflib to encode the POJO into a Blob/File
1186
1250
  return Reflib.downloadFile(settings.refs, {
1187
- filename: settings.path,
1251
+ filename: app.service('$supabase')._parsePath(filePath).basename,
1188
1252
  promptDownload: false, // Just return the fileBlob we hand to Supabase
1189
1253
  })
1190
1254
  case 'blob':
1191
1255
  if (!(settings.refs instanceof Blob)) throw new Error("setProjectLibrary({format: 'blob'} but non-Blob provided as `refs`");
1192
- return new File([settings.refs], app.service('$supabase')._parsePath(settings.path).basename);
1256
+ return new File([settings.refs], app.service('$supabase')._parsePath(filePath).basename);
1193
1257
  case 'file':
1194
1258
  if (!(settings.ref instanceof File)) throw new Error("setProjectLibrary({format: 'file'} but non-File provided as `refs`");
1195
1259
  return settings.refs;
@@ -1197,12 +1261,10 @@ export default class TeraFyServer {
1197
1261
  throw new Error(`Unsupported library format "${settings.format}"`);
1198
1262
  }
1199
1263
  })
1200
- .then(fileBlob => app.service('$supabase').fileUpload(settings.path, {
1264
+ .then(fileBlob => app.service('$supabase').fileUpload(filePath, {
1201
1265
  file: fileBlob,
1202
- mode: 'encoded',
1203
1266
  overwrite: true,
1204
- meta: settings.meta,
1205
- transcoders: false, // We can skip transcoders as we are supplying the meta information above anyway
1267
+ mode: 'encoded',
1206
1268
  }))
1207
1269
  }
1208
1270
 
@@ -1222,7 +1284,7 @@ export default class TeraFyServer {
1222
1284
  }
1223
1285
  // }}}
1224
1286
 
1225
- // Webpages - setPageUrl() {{{
1287
+ // Webpages - setPageUrl(), setPageTitle {{{
1226
1288
 
1227
1289
  /**
1228
1290
  * Set an active tools URL (or path) so that it survives a refresh
@@ -1430,6 +1492,5 @@ export default class TeraFyServer {
1430
1492
  ...msg,
1431
1493
  );
1432
1494
  }
1433
-
1434
1495
  // }}}
1435
1496
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iebh/tera-fy",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "TERA website worker",
5
5
  "scripts": {
6
6
  "dev": "esbuild --platform=browser --format=esm --bundle lib/terafy.client.js --outfile=dist/terafy.js --minify --serve --servedir=.",