@monimesl/dot-simulator 0.1.0-alpha.15 → 0.1.0-alpha.16

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.
@@ -70,7 +70,8 @@ var HostCallRunner = class {
70
70
  this.#succeed(id, await entry(call));
71
71
  return;
72
72
  }
73
- await this.#permissions.check(call.method, entry.permission, call.signal);
73
+ const permission = typeof entry.permission === "function" ? entry.permission(call.params) : entry.permission;
74
+ await this.#permissions.check(call.method, permission, call.signal);
74
75
  this.#succeed(id, await entry.handle(call));
75
76
  } catch (error) {
76
77
  this.#fail(id, error);
@@ -270,7 +271,7 @@ function startHost({ logic, ui, manifest, runtimeVersion, systemContext, handler
270
271
  }
271
272
  //#endregion
272
273
  //#region src/versions.ts
273
- const SIMULATOR_VERSIONS = { runtimeVersion: "0.1.0-alpha.15" };
274
+ const SIMULATOR_VERSIONS = { runtimeVersion: "0.1.0-alpha.16" };
274
275
  //#endregion
275
276
  //#region src/paths.ts
276
277
  const CLIENT_PATH = "/__dot/";
@@ -279,6 +280,7 @@ const HTTP_PATH = `${CLIENT_PATH}network/http/`;
279
280
  const FILE_PATH = `${CLIENT_PATH}storage/file/`;
280
281
  const CACHE_PATH = `${CLIENT_PATH}storage/cache/`;
281
282
  const SECRET_PATH = `${CLIENT_PATH}storage/secret/`;
283
+ const MEDIA_PATH = `${CLIENT_PATH}media/`;
282
284
  const TEMPORARY_FILES_PATH = `${CLIENT_PATH}files/`;
283
285
  const WEBVIEW_URL = `/dot/ui/index.html`;
284
286
  //#endregion
@@ -303,6 +305,7 @@ function simulatorPage(appName) {
303
305
  </select>
304
306
  <button id="back" type="button" title="The system back button">◀ Back</button>
305
307
  <button id="reload" type="button">Reload</button>
308
+ <button id="messages" type="button" aria-pressed="true" aria-controls="messages-panel" title="Show or hide the messages between the dot's two layers">Messages</button>
306
309
  </header>
307
310
  <main>
308
311
  <section class="stage">
@@ -324,7 +327,7 @@ function simulatorPage(appName) {
324
327
  <span class="home-indicator"></span>
325
328
  </div>
326
329
  </section>
327
- <aside>
330
+ <aside id="messages-panel">
328
331
  <div class="panel-head">
329
332
  <span>Messages</span>
330
333
  <button id="clear" type="button">Clear</button>
@@ -368,6 +371,7 @@ const STYLES = `
368
371
  .dialog { width: 270px; border-radius: 14px; overflow: hidden; background: var(--dialog); color: var(--screen-text); text-align: center; font: 13px/1.35 system-ui, sans-serif; }
369
372
  .dialog-body { display: flex; flex-direction: column; gap: 4px; padding: 18px 16px 16px; }
370
373
  .dialog-title { font-size: 17px; font-weight: 600; }
374
+ .dialog-input { margin-top: 10px; padding: 7px 8px; border: 1px solid var(--dialog-line); border-radius: 7px; background: var(--screen); color: var(--screen-text); font: inherit; text-align: left; }
371
375
  .dialog-actions { display: flex; border-top: 1px solid var(--dialog-line); }
372
376
  .dialog-actions.stacked { flex-direction: column; }
373
377
  .dialog-actions button { flex: 1; margin: 0; padding: 11px; border: 0; border-radius: 0; background: transparent; color: #0a84ff; font-size: 17px; }
@@ -391,6 +395,8 @@ const STYLES = `
391
395
  .home-indicator { position: absolute; bottom: 8px; left: 50%; width: 134px; height: 5px; margin-left: -67px; border-radius: 3px; background: var(--screen-text); pointer-events: none; }
392
396
  #error { max-width: 640px; width: 100%; margin: 0; padding: 12px; white-space: pre-wrap; overflow-wrap: anywhere; color: #fff; background: var(--danger); border-radius: 8px; }
393
397
  aside { width: 380px; display: flex; flex-direction: column; background: var(--panel); border-left: 1px solid var(--border); }
398
+ aside[hidden] { display: none; }
399
+ #messages[aria-pressed="true"] { border-color: var(--to-service); color: var(--to-service); }
394
400
  .panel-head { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; border-bottom: 1px solid var(--border); font-weight: 600; }
395
401
  #log { flex: 1; overflow: auto; margin: 0; padding: 0; list-style: none; font: 12px/1.4 ui-monospace, monospace; }
396
402
  #log li { border-bottom: 1px solid var(--border); padding: 4px 12px; }
@@ -1197,6 +1203,28 @@ function bufferEncoding(name) {
1197
1203
  function failed(message) {
1198
1204
  return new HostError("failed", message);
1199
1205
  }
1206
+ function tooLargeMessage(name) {
1207
+ return `"${name}" is larger than 25 MB`;
1208
+ }
1209
+ //#endregion
1210
+ //#region src/media.ts
1211
+ async function runMediaCall(operation, params, files) {
1212
+ const { name, data, contentType } = params ?? {};
1213
+ if (typeof name !== "string" || typeof data !== "string") throw new HostError("invalid", "A file needs a name and its data");
1214
+ const bytes = Buffer.from(data, "base64");
1215
+ switch (operation) {
1216
+ case "photo": return keep(files, name, "image/jpeg", bytes);
1217
+ case "file":
1218
+ if (bytes.length > 26214400) throw new HostError("invalid", tooLargeMessage(name));
1219
+ return keep(files, name, typeof contentType === "string" ? contentType : "application/octet-stream", bytes);
1220
+ default: throw new HostError("unsupported", `The simulator has no "${operation}" media call`);
1221
+ }
1222
+ }
1223
+ async function keep(files, name, contentType, bytes) {
1224
+ const { id, path } = await files.newPath();
1225
+ await writeFile(path, bytes);
1226
+ return files.add(id, path, name, contentType);
1227
+ }
1200
1228
  //#endregion
1201
1229
  //#region src/temporary-files.ts
1202
1230
  var TemporaryFiles = class {
@@ -1323,6 +1351,11 @@ async function startSimulator({ dotDir, reactHostDir, name = "Dot", port = DEFAU
1323
1351
  answer(request, response, (params) => runSecretCall(operation, params, storage.secrets));
1324
1352
  return;
1325
1353
  }
1354
+ if (pathname.startsWith(MEDIA_PATH) && request.method === "POST") {
1355
+ const operation = pathname.slice(MEDIA_PATH.length);
1356
+ answer(request, response, (params) => runMediaCall(operation, params, files));
1357
+ return;
1358
+ }
1326
1359
  if (pathname.startsWith(TEMPORARY_FILES_PATH)) {
1327
1360
  serveTemporaryFile(response, files, pathname.slice(TEMPORARY_FILES_PATH.length));
1328
1361
  return;