apps 0.0.1-beta.17 → 0.0.1-beta.18

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/js/ui/serving.js CHANGED
@@ -19,6 +19,96 @@ import {
19
19
  } from "../chunk-CDL5PM2Q.js";
20
20
  import "../chunk-CZ7CSFO4.js";
21
21
 
22
+ // src/implementation/ui-account-notice.ts
23
+ var escape = (text) => text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
24
+ var accountProblemBlocks = (problem) => problem.reason === "missing" || problem.reason === "removed" || problem.reason === "incompatible" || problem.reason === "credentials_rejected";
25
+ var accountProblemText = (problem) => {
26
+ switch (problem.reason) {
27
+ case "missing":
28
+ return `No ${problem.provider} account chosen`;
29
+ case "removed":
30
+ return `${problem.provider} account was removed or is no longer shared`;
31
+ case "incompatible":
32
+ return `Chosen account can't be used as the ${problem.provider} account`;
33
+ case "credentials_rejected":
34
+ return `${problem.account} (${problem.provider}): sign-in rejected at last check`;
35
+ case "forbidden":
36
+ return `${problem.account} (${problem.provider}): missing permission at last check`;
37
+ case "upstream_unavailable":
38
+ return `${problem.account} (${problem.provider}): service unavailable at last check`;
39
+ case "check_failed":
40
+ return `${problem.account} (${problem.provider}): last check failed`;
41
+ }
42
+ };
43
+ var accountBlockedPage = (notice) => {
44
+ const title = `${escape(notice.app)} can't open`;
45
+ const items = notice.problems.map(
46
+ (problem) => `<li class="${accountProblemBlocks(problem) ? "bad" : "warn"}">${escape(accountProblemText(problem))}</li>`
47
+ ).join("");
48
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${title}</title><style>
49
+ :root{color-scheme:light dark;font-family:ui-sans-serif,system-ui,sans-serif;color:#18181b;background:#fafafa}
50
+ @media (prefers-color-scheme:dark){:root{color:#f4f4f5;background:#18181b}main{background:#27272a!important;border-color:#3f3f46!important}a{color:inherit!important;border-color:#52525b!important}a.primary{background:#f4f4f5!important;color:#18181b!important}p{color:#a1a1aa!important}}
51
+ body{margin:0;min-height:100vh;display:grid;place-items:center;padding:24px;box-sizing:border-box}
52
+ main{max-width:480px;width:100%;background:#fff;border:1px solid #e4e4e7;border-radius:12px;padding:28px}
53
+ h1{margin:0 0 8px;font-size:20px}p{margin:0 0 16px;color:#52525b;line-height:1.5}
54
+ ul{margin:0 0 24px;padding:0;list-style:none;display:grid;gap:8px}
55
+ li{display:flex;gap:10px;align-items:baseline;line-height:1.4}li::before{content:"";flex:none;width:8px;height:8px;border-radius:50%;transform:translateY(-1px)}
56
+ li.bad::before{background:#dc2626}li.warn::before{background:#f59e0b}
57
+ .actions{display:flex;flex-wrap:wrap;gap:8px}
58
+ a{padding:8px 14px;border-radius:8px;border:1px solid #d4d4d8;color:#18181b;text-decoration:none;font-weight:500}
59
+ a.primary{background:#18181b;border-color:#18181b;color:#fff}
60
+ </style></head><body><main><h1>${title}</h1><p>This profile's accounts need attention before the app can work.</p><ul>${items}</ul><div class="actions"><a class="primary" href="${escape(notice.fix)}">Fix accounts</a><a href="${escape(notice.choose)}">Open with another profile</a></div></main></body></html>`;
61
+ };
62
+ var installAccountNotice = () => {
63
+ const data = document.getElementById("executor-account-notice")?.textContent;
64
+ if (!data) return;
65
+ const notice = JSON.parse(data);
66
+ if (typeof notice !== "object" || notice === null || !("items" in notice) || !("fix" in notice))
67
+ return;
68
+ const items = notice.items;
69
+ const fix = notice.fix;
70
+ if (!Array.isArray(items) || typeof fix !== "string") return;
71
+ const show = () => {
72
+ const host = document.createElement("div");
73
+ const root = host.attachShadow({ mode: "open" });
74
+ const style = document.createElement("style");
75
+ style.textContent = `aside{position:fixed;right:16px;bottom:16px;z-index:2147483647;max-width:360px;box-sizing:border-box;padding:14px 16px;border-radius:10px;border:1px solid #fcd34d;background:#fffbeb;color:#451a03;font:14px/1.45 ui-sans-serif,system-ui,sans-serif;box-shadow:0 8px 24px rgb(0 0 0/.12)}strong{display:block;margin-bottom:4px}ul{margin:0 0 10px;padding-left:18px}div{display:flex;gap:12px;align-items:center}a{color:inherit;font-weight:600}button{margin-left:auto;font:inherit;background:none;border:0;padding:0;color:inherit;text-decoration:underline;cursor:pointer}`;
76
+ const card = document.createElement("aside");
77
+ card.setAttribute("role", "status");
78
+ card.setAttribute("aria-label", "Account warning");
79
+ const title = document.createElement("strong");
80
+ title.textContent = "Some things might not work";
81
+ const list = document.createElement("ul");
82
+ for (const item of items) {
83
+ const entry = document.createElement("li");
84
+ entry.textContent = String(item);
85
+ list.append(entry);
86
+ }
87
+ const actions = document.createElement("div");
88
+ const link = document.createElement("a");
89
+ link.href = fix;
90
+ link.target = "_top";
91
+ link.textContent = "Review accounts";
92
+ const close = document.createElement("button");
93
+ close.type = "button";
94
+ close.textContent = "Dismiss";
95
+ close.addEventListener("click", () => host.remove());
96
+ actions.append(link, close);
97
+ card.append(title, list, actions);
98
+ root.append(style, card);
99
+ document.documentElement.append(host);
100
+ };
101
+ if (document.readyState === "loading") {
102
+ document.addEventListener("DOMContentLoaded", show, { once: true });
103
+ } else {
104
+ show();
105
+ }
106
+ };
107
+ var accountNoticeBootstrap = (notice) => `<script type="application/json" id="executor-account-notice">${JSON.stringify({
108
+ items: notice.problems.map(accountProblemText),
109
+ fix: notice.fix
110
+ }).replaceAll("<", "\\u003c")}</script><script>(${installAccountNotice.toString()})()</script>`;
111
+
22
112
  // src/implementation/ui-errors.ts
23
113
  var installAppFailureUI = () => {
24
114
  let activeDialog;
@@ -214,6 +304,13 @@ var appDocument = (options) => Effect_exports.gen(function* () {
214
304
  });
215
305
  if (pathname.includes(".") && pathname !== "/index.html")
216
306
  return HttpServerResponse_exports.empty({ status: 404 });
307
+ const notice = options.accounts;
308
+ if (notice?.problems.some(accountProblemBlocks))
309
+ return HttpServerResponse_exports.text(accountBlockedPage(notice), {
310
+ status: 409,
311
+ contentType: "text/html",
312
+ headers: appPrivateHeaders
313
+ });
217
314
  const document2 = yield* options.asset("index.html");
218
315
  if (document2 === void 0)
219
316
  return HttpServerResponse_exports.text("This app has no UI.", {
@@ -228,7 +325,7 @@ var appDocument = (options) => Effect_exports.gen(function* () {
228
325
  const telemetry = yield* CurrentTelemetryConfig;
229
326
  const attribute = (text) => text.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;");
230
327
  const metadata = telemetry === void 0 ? "" : `<meta name="executor-build" content="${attribute(telemetry.version)}"><meta name="executor-environment" content="${attribute(telemetry.environment)}">`;
231
- const boot = `${metadata}<base href="/_executor/assets/${attribute(options.deployment)}/"><script type="application/json" id="executor-context">${context}</script>${appFailureBootstrap}<script src="/_executor/watch.js" defer></script>`;
328
+ const boot = `${metadata}<base href="/_executor/assets/${attribute(options.deployment)}/"><script type="application/json" id="executor-context">${context}</script>${appFailureBootstrap}${notice === void 0 || notice.problems.length === 0 ? "" : accountNoticeBootstrap(notice)}<script src="/_executor/watch.js" defer></script>`;
232
329
  return HttpServerResponse_exports.text(
233
330
  new TextDecoder().decode(document2.body).replace("<!--executor-ui-->", boot),
234
331
  { contentType: "text/html", headers: appPrivateHeaders }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apps",
3
- "version": "0.0.1-beta.17",
3
+ "version": "0.0.1-beta.18",
4
4
  "description": "TypeScript apps with typed tools, accounts, storage, and UI.",
5
5
  "type": "module",
6
6
  "sideEffects": false,