@ambasada/village 0.8.2 → 0.8.3

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 (3) hide show
  1. package/dist/cli.mjs +78 -40
  2. package/dist/index.js +49 -13
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -85,6 +85,7 @@ __export(src_exports, {
85
85
  parseCheckpoint: () => parseCheckpoint,
86
86
  parseVerifierKey: () => parseVerifierKey,
87
87
  pngInfo: () => pngInfo,
88
+ probeManifest: () => probeManifest,
88
89
  publicJwkOf: () => publicJwkOf,
89
90
  randomId: () => randomId,
90
91
  rootFromLeafHashes: () => rootFromLeafHashes,
@@ -1266,6 +1267,34 @@ var USER_AGENT = "ambasada-check/1 (+https://ambasada.id)";
1266
1267
  async function get(fetchImpl, url) {
1267
1268
  return fetchImpl(url, { redirect: "manual", signal: AbortSignal.timeout(FETCH_TIMEOUT_MS), headers: { "user-agent": USER_AGENT, accept: "*/*" } });
1268
1269
  }
1270
+ async function probeManifest(origin, fetchImpl = fetch) {
1271
+ try {
1272
+ const res = await get(fetchImpl, new URL(MANIFEST_URL_PATH, origin).toString());
1273
+ if (res.status !== 200) {
1274
+ await res.body?.cancel();
1275
+ return { on: false, entry: null };
1276
+ }
1277
+ const m = JSON.parse(await res.text());
1278
+ if (!m || typeof m !== "object" || m.schema !== MANIFEST_SCHEMA && m.schema !== 1) return { on: false, entry: null };
1279
+ return { on: true, entry: typeof m.entry === "string" && m.entry.startsWith("/") ? m.entry : "/" };
1280
+ } catch {
1281
+ return { on: false, entry: null };
1282
+ }
1283
+ }
1284
+ async function checkPage(fetchImpl, url, report, html) {
1285
+ try {
1286
+ const started = Date.now();
1287
+ const res = await get(fetchImpl, url);
1288
+ const text = res.status === 200 && html ? await res.text() : "";
1289
+ if (!html) await res.body?.cancel();
1290
+ const ms = Date.now() - started;
1291
+ if (res.status !== 200) report.problems.push({ code: "entry_status", message: `${url} answered ${res.status}${res.headers.get("location") ? ` \u2192 ${res.headers.get("location")}` : ""}; ${html ? "the entry" : "the app"} must answer 200 without redirects` });
1292
+ else if (html) merge(report, checkHtml(text));
1293
+ if (ms > SLOW_ENTRY_MS) report.warnings.push({ code: "slow_entry", message: `${url} took ${ms} ms; aim for under ${SLOW_ENTRY_MS} ms${html ? " (the splash stays until ready())" : ""}` });
1294
+ } catch (error) {
1295
+ report.problems.push({ code: "entry_fetch", message: `could not fetch ${url}: ${error.message}` });
1296
+ }
1297
+ }
1269
1298
  async function checkRemote(url, fetchImpl = fetch, options = {}) {
1270
1299
  const report = { ok: true, target: url, problems: [], warnings: [], notes: [] };
1271
1300
  let origin;
@@ -1279,8 +1308,23 @@ async function checkRemote(url, fetchImpl = fetch, options = {}) {
1279
1308
  if (origin.protocol !== "https:") report.problems.push({ code: "not_https", message: "the app must be served over https" });
1280
1309
  let manifest = null;
1281
1310
  const manifestUrl = new URL(MANIFEST_URL_PATH, origin).toString();
1311
+ if (options.miniapp === false) {
1312
+ report.miniapp = { on: false, entry: null };
1313
+ report.notes.push("mini app: off");
1314
+ await checkPage(fetchImpl, new URL("/", origin).toString(), report, false);
1315
+ report.ok = report.problems.length === 0;
1316
+ return report;
1317
+ }
1282
1318
  try {
1283
1319
  let res = await get(fetchImpl, manifestUrl);
1320
+ if (res.status === 404 && options.miniapp === void 0) {
1321
+ await res.body?.cancel();
1322
+ report.miniapp = { on: false, entry: null };
1323
+ report.notes.push(`mini app: off (no ${MANIFEST_URL_PATH})`);
1324
+ await checkPage(fetchImpl, new URL("/", origin).toString(), report, false);
1325
+ report.ok = report.problems.length === 0;
1326
+ return report;
1327
+ }
1284
1328
  if (res.status >= 300 && res.status < 400) {
1285
1329
  const location = res.headers.get("location") ?? "";
1286
1330
  if (res.status === 307 && HOSTED_MANIFEST.test(location)) {
@@ -1302,18 +1346,8 @@ async function checkRemote(url, fetchImpl = fetch, options = {}) {
1302
1346
  report.problems.push({ code: "manifest_fetch", message: `could not fetch ${manifestUrl}: ${error.message}` });
1303
1347
  }
1304
1348
  const entryPath = typeof manifest?.entry === "string" && manifest.entry.startsWith("/") ? manifest.entry : "/";
1305
- const entryUrl = new URL(entryPath, origin).toString();
1306
- try {
1307
- const started = Date.now();
1308
- const res = await get(fetchImpl, entryUrl);
1309
- const html = res.status === 200 ? await res.text() : "";
1310
- const ms = Date.now() - started;
1311
- if (res.status !== 200) report.problems.push({ code: "entry_status", message: `${entryUrl} answered ${res.status}${res.headers.get("location") ? ` \u2192 ${res.headers.get("location")}` : ""}; the entry must answer 200 without redirects` });
1312
- else merge(report, checkHtml(html));
1313
- if (ms > SLOW_ENTRY_MS) report.warnings.push({ code: "slow_entry", message: `${entryUrl} took ${ms} ms; the splash stays until ready(), aim for under ${SLOW_ENTRY_MS} ms` });
1314
- } catch (error) {
1315
- report.problems.push({ code: "entry_fetch", message: `could not fetch ${entryUrl}: ${error.message}` });
1316
- }
1349
+ report.miniapp = { on: true, entry: entryPath };
1350
+ await checkPage(fetchImpl, new URL(entryPath, origin).toString(), report, true);
1317
1351
  if (typeof manifest?.icon === "string" && manifest.icon) {
1318
1352
  const iconUrl = new URL(manifest.icon, origin).toString();
1319
1353
  try {
@@ -1381,9 +1415,11 @@ function summarizeCheck(report, now = /* @__PURE__ */ new Date()) {
1381
1415
  ...report.warnings.map((w) => ({ ...w, level: "warning", label: findingLabel(w.code) }))
1382
1416
  ];
1383
1417
  const needs = [...new Set(issues.filter((i) => i.level === "problem").map((i) => i.label))];
1384
- return { ok: report.ok, issues, needs, checked_at: now.toISOString() };
1418
+ const miniapp = report.miniapp ? { miniapp: report.miniapp.on ? "on" : "off", entry: report.miniapp.entry } : {};
1419
+ return { ok: report.ok, issues, needs, checked_at: now.toISOString(), ...miniapp };
1385
1420
  }
1386
1421
  function listingLine(tier, check) {
1422
+ if (check?.miniapp === "off") return "Mini app: off";
1387
1423
  if (tier === "verified") return "Verified";
1388
1424
  if (tier === "listed") return "Listed for the event";
1389
1425
  if (tier === "unlisted") return check?.needs.length ? `Unlisted \xB7 needs: ${check.needs.join(", ")}` : "Unlisted";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ambasada/village",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "The Hack A Ton add-on for Ambasada apps: TON checkout, receipts, offerings, LLM, the agent, the market; CLI commands and the local bank.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -27,7 +27,7 @@
27
27
  "node": ">=22"
28
28
  },
29
29
  "dependencies": {
30
- "@ambasada/sdk": "^0.8.2"
30
+ "@ambasada/sdk": "^0.8.3"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^22.20.5",
@@ -35,7 +35,7 @@
35
35
  "typescript": "~5.9.3",
36
36
  "vitest": "^4.1.12",
37
37
  "@ambasada/protocol": "0.1.0",
38
- "@ambasada/cli": "0.8.2"
38
+ "@ambasada/cli": "0.8.3"
39
39
  },
40
40
  "author": "CENTYSO (https://centyso.com)",
41
41
  "homepage": "https://ambasada.id",