apiblaze 0.21.40 → 0.21.41

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 (2) hide show
  1. package/dist/index.js +29 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__
34
34
  var version;
35
35
  var init_package = __esm({
36
36
  "package.json"() {
37
- version = "0.21.40";
37
+ version = "0.21.41";
38
38
  }
39
39
  });
40
40
 
@@ -394,12 +394,13 @@ __export(api_exports, {
394
394
  });
395
395
  function countRun(opts = {}) {
396
396
  if (/^(1|true|yes)$/i.test((process.env.DO_NOT_TRACK ?? "").trim())) return;
397
+ if (opts.event !== void 0 && !/^[a-z0-9-]{1,48}$/.test(opts.event)) return;
397
398
  try {
398
399
  const child = (0, import_child_process.spawn)(process.execPath, ["-e", COUNT_RUN_CHILD], {
399
400
  detached: true,
400
401
  stdio: "ignore",
401
402
  windowsHide: true,
402
- env: { ...process.env, ABZ_COUNT_URL: opts.url ?? `${PUBLIC_API_BASE}/cli/hello`, ABZ_COUNT_HEADERS: JSON.stringify(cliHeaders()) }
403
+ env: { ...process.env, ABZ_COUNT_URL: (opts.url ?? `${PUBLIC_API_BASE}/cli/hello`) + (opts.event ? `/${opts.event}` : ""), ABZ_COUNT_HEADERS: JSON.stringify(cliHeaders()) }
403
404
  });
404
405
  child.on("error", () => {
405
406
  });
@@ -7720,7 +7721,7 @@ async function portFree(port) {
7720
7721
  }
7721
7722
  async function freePort(from, avoid = []) {
7722
7723
  for (let p = from; p < from + 200; p++) if (!avoid.includes(p) && await portFree(p)) return p;
7723
- throw new DemoError("No free port for the demo app.", "Close another local server, then run again.");
7724
+ throw new DemoError("No free port for the demo app.", "Close another local server, then run again.", "no-free-port");
7724
7725
  }
7725
7726
  function readRegistry() {
7726
7727
  try {
@@ -7901,19 +7902,19 @@ async function createProxy2(name, backendPort2, openapi, tenantName) {
7901
7902
  return await createProxyAnonymous(body);
7902
7903
  } catch (err) {
7903
7904
  const status2 = err.status;
7904
- if (status2 === 429) throw new DemoError("Too many demos were started from this network in the last hour.", `Wait a few minutes, then run: ${LATEST} demo`);
7905
- if (status2 && status2 >= 500) throw new DemoError(`${B.product} is having trouble right now.`, `Try again in a minute: ${LATEST} demo`);
7906
- throw new DemoError(`Could not create the demo (${err.message}).`, `Run again: ${LATEST} demo`);
7905
+ if (status2 === 429) throw new DemoError("Too many demos were started from this network in the last hour.", `Wait a few minutes, then run: ${LATEST} demo`, "create-429");
7906
+ if (status2 && status2 >= 500) throw new DemoError(`${B.product} is having trouble right now.`, `Try again in a minute: ${LATEST} demo`, "create-5xx");
7907
+ throw new DemoError(`Could not create the demo (${err.message}).`, `Run again: ${LATEST} demo`, "create-failed");
7907
7908
  }
7908
7909
  }
7909
7910
  async function waitTunnel(dir) {
7910
7911
  for (let i = 0; i < 200; i++) {
7911
7912
  const st = loadState(dir);
7912
- if (st?.error) throw new DemoError(st.error, `Run again: ${LATEST} demo`);
7913
+ if (st?.error) throw new DemoError(st.error, `Run again: ${LATEST} demo`, "tunnel-error");
7913
7914
  if (st?.ready) return;
7914
7915
  await new Promise((r) => setTimeout(r, 100));
7915
7916
  }
7916
- throw new DemoError("The tunnel did not open in time.", `Try: ${LATEST} demo restart`);
7917
+ throw new DemoError("The tunnel did not open in time.", `Try: ${LATEST} demo restart`, "tunnel-timeout");
7917
7918
  }
7918
7919
  async function waitThrough(apiUrl, serverKey) {
7919
7920
  let last = 0;
@@ -7922,13 +7923,13 @@ async function waitThrough(apiUrl, serverKey) {
7922
7923
  const r = await fetch(`${apiUrl}/reservations`, { headers: { "x-api-key": serverKey, "x-end-user-id": "ana" } });
7923
7924
  last = r.status;
7924
7925
  if (r.status === 200) return;
7925
- if (r.status === 402) throw new DemoError(`${B.product} refused the demo's free calls. That is a problem on our side, not yours.`, `Try again in a few minutes: ${LATEST} demo`);
7926
+ if (r.status === 402) throw new DemoError(`${B.product} refused the demo's free calls. That is a problem on our side, not yours.`, `Try again in a few minutes: ${LATEST} demo`, "trial-402");
7926
7927
  } catch (e) {
7927
7928
  if (e instanceof DemoError) throw e;
7928
7929
  }
7929
7930
  await new Promise((r) => setTimeout(r, 1e3));
7930
7931
  }
7931
- throw new DemoError(`The demo is running but ${B.product} cannot reach it yet (last answer ${last}).`, `Run again: ${LATEST} demo`);
7932
+ throw new DemoError(`The demo is running but ${B.product} cannot reach it yet (last answer ${last}).`, `Run again: ${LATEST} demo`, `cannot-reach-${last}`);
7932
7933
  }
7933
7934
  async function confirmStep(opts, question) {
7934
7935
  if (opts.yes || !interactive()) return true;
@@ -8003,9 +8004,10 @@ function routesOf(openapi) {
8003
8004
  async function runDemo(opts = {}) {
8004
8005
  if (opts.serve) return serveTunnel(import_path5.default.resolve(opts.serve));
8005
8006
  try {
8006
- await startDemo(opts);
8007
+ countRun({ event: await startDemo(opts) });
8007
8008
  } catch (err) {
8008
8009
  const e = err instanceof DemoError ? err : new DemoError(`The demo could not start (${err.message}).`, `Run again: ${LATEST} demo`);
8010
+ countRun({ event: `demo-failed-${e.why}` });
8009
8011
  await stopDemos().catch(() => 0);
8010
8012
  console.log(`${import_chalk24.default.red("\u2717")} ${e.message}`);
8011
8013
  console.log(` ${e.fix}`);
@@ -8061,9 +8063,10 @@ async function startDemo(opts) {
8061
8063
  const cdDir = rel === "." ? "" : `cd ${rel} && `;
8062
8064
  const shell = (c, note = "") => console.log(` ${import_chalk24.default.dim("$")} ${import_chalk24.default.cyan(c)}${note ? ` ${import_chalk24.default.dim(note)}` : ""}`);
8063
8065
  const ok = (text, note = "") => console.log(` ${import_chalk24.default.green("\u2713")} ${text}${note ? ` ${import_chalk24.default.dim(note)}` : ""}`);
8064
- const stopHere = (lines) => {
8066
+ const stopHere = (step, lines) => {
8065
8067
  console.log();
8066
8068
  for (const l of lines) console.log(` ${l}`);
8069
+ return `demo-declined-${step}`;
8067
8070
  };
8068
8071
  titleCard(["Restaurant Reservation Service Demo", `ResiResi, with ${B.product} in front`]);
8069
8072
  await stopDemos();
@@ -8090,16 +8093,16 @@ paths:`));
8090
8093
  for (const r of routesOf(openapi)) console.log(` ${r.method.padEnd(7)}${r.path.padEnd(21)}${import_chalk24.default.dim(r.summary)}`);
8091
8094
  console.log(import_chalk24.default.dim(" No login, no API keys, no permission checks in its code. It only checks one shared backend secret (in .env.local)."));
8092
8095
  if (!await confirmStep(opts, `Ok to serve the backend on localhost:${backendPort2}?`)) {
8093
- return stopHere([
8096
+ return stopHere("backend", [
8094
8097
  `Stopped before starting anything. ResiResi is written to ${rel}.`,
8095
8098
  `Start it any time: ${cdDir}npm run start ${import_chalk24.default.dim("\xB7")} or run the whole demo again: ${LATEST} demo`
8096
8099
  ]);
8097
8100
  }
8098
8101
  shell(`${cdDir}npm run start`, "npm run stop \xB7 npm run restart");
8099
- if (!appCtl(dir, "--background")) throw new DemoError("The ResiResi app could not start.", `Run again: ${LATEST} demo`);
8102
+ if (!appCtl(dir, "--background")) throw new DemoError("The ResiResi app could not start.", `Run again: ${LATEST} demo`, "app-start");
8100
8103
  registerDemo(dir);
8101
8104
  const appUp = await busy("Starting the reservation backend and its front-end\u2026", () => waitPorts([backendPort2, directPort]), 1800);
8102
- if (!appUp) throw new DemoError(`The ResiResi app did not start (see ${import_path5.default.join(rel, STATE_DIR, "app.log")}).`, `Run again: ${LATEST} demo`);
8105
+ if (!appUp) throw new DemoError(`The ResiResi app did not start (see ${import_path5.default.join(rel, STATE_DIR, "app.log")}).`, `Run again: ${LATEST} demo`, "app-not-up");
8103
8106
  ok(`${import_chalk24.default.bold("Reservation backend")}: localhost:${backendPort2}`, "unprotected");
8104
8107
  ok(`${import_chalk24.default.bold("Unprotected reservation front-end")}, going directly to the reservation backend: ${import_chalk24.default.bold(`http://localhost:${directPort}`)}`);
8105
8108
  console.log(" Book a table directly, the way the unprotected front-end does:");
@@ -8108,7 +8111,7 @@ paths:`));
8108
8111
  console.log(import_chalk24.default.cyan(` -H "x-abz-user-id: ana" -H "x-abz-tenant-id: ${ninoTenant}" \\`));
8109
8112
  console.log(import_chalk24.default.cyan(` -H "content-type: application/json" -d '{"time":"19:30","guests":2}'`));
8110
8113
  if (!await confirmStep(opts, `Ok to put an ${B.product} proxy and MCP in front of the reservation backend?`)) {
8111
- return stopHere([
8114
+ return stopHere("proxy", [
8112
8115
  `Stopped before ${B.product}. ResiResi keeps running on this laptop: http://localhost:${directPort} ${import_chalk24.default.dim(`(${cdDir}npm run stop stops it)`)}`,
8113
8116
  `Run the whole demo again any time: ${LATEST} demo`
8114
8117
  ]);
@@ -8127,7 +8130,7 @@ paths:`));
8127
8130
  const rr = r;
8128
8131
  const tenant3 = rr.tenant ?? tenantFromPortal(rr.portal ?? rr.devPortal) ?? "";
8129
8132
  const missing = [!cpKey2 && "workspace key", !teamId2 && "team", !serverKey2 && "API key", !tenant3 && "tenant"].filter(Boolean);
8130
- if (missing.length) throw new DemoError(`${B.product} did not return everything the demo needs (${missing.join(", ")}).`, `Run again: ${LATEST} demo`);
8133
+ if (missing.length) throw new DemoError(`${B.product} did not return everything the demo needs (${missing.join(", ")}).`, `Run again: ${LATEST} demo`, "setup-incomplete");
8131
8134
  const createSecs2 = ((Date.now() - tCreate) / 1e3).toFixed(1);
8132
8135
  adoptWorkspace(cpKey2, teamId2, rr.claim_code);
8133
8136
  const cp = (p, init) => cpFetch(cpKey2, p, init).then(() => true).catch(() => false);
@@ -8149,7 +8152,7 @@ paths:`));
8149
8152
  return { limited: lim, secretOk: sec };
8150
8153
  })()
8151
8154
  ]);
8152
- if (!secretOk) throw new DemoError(`Could not give ${B.product} the backend secret.`, `Run again: ${LATEST} demo`);
8155
+ if (!secretOk) throw new DemoError(`Could not give ${B.product} the backend secret.`, `Run again: ${LATEST} demo`, "secret-failed");
8153
8156
  return { cpKey: cpKey2, teamId: teamId2, serverKey: serverKey2, project: project2, version: version3, tenant: tenant3, gino: gino2, createSecs: createSecs2, adminOk: adminOk2, ginaOk: ginaOk2, limited: limited2, claimCode: rr.claim_code, applied: rr.secure?.applied ?? [] };
8154
8157
  });
8155
8158
  const { cpKey, teamId, serverKey, project, version: version2, tenant: tenant2, gino, createSecs, adminOk, ginaOk, limited } = setup;
@@ -8201,7 +8204,7 @@ paths:`));
8201
8204
  }
8202
8205
  ]);
8203
8206
  if (!await confirmStep(opts, `Ok to create a tunnel allowing ${project} to forward requests to localhost:${backendPort2}?`)) {
8204
- return stopHere([
8207
+ return stopHere("tunnel", [
8205
8208
  `Stopped before the tunnel. The proxy exists but cannot reach this laptop yet.`,
8206
8209
  `Open the tunnel any time: ${LATEST} demo start`
8207
8210
  ]);
@@ -8216,7 +8219,7 @@ paths:`));
8216
8219
  ok(`${import_chalk24.default.bold("Tunnel")}: the public address reaches localhost:${backendPort2} on this laptop`, `checked with a real call \xB7 ${Math.round((Date.now() - t0) / 1e3)} s so far`);
8217
8220
  printAddresses(dir);
8218
8221
  if (!await confirmStep(opts, "Ok to launch the protected website, which will use the proxy to reach the backend?")) {
8219
- return stopHere([
8222
+ return stopHere("website", [
8220
8223
  `Stopped before the protected website. The API and MCP addresses above work now.`,
8221
8224
  `Start the website any time: ${cdDir}npm run restart`
8222
8225
  ]);
@@ -8229,12 +8232,13 @@ paths:`));
8229
8232
  appCtl(dir, "--background");
8230
8233
  return waitPorts([appPort, directPort, backendPort2]);
8231
8234
  }, 1500);
8232
- if (!siteUp) throw new DemoError(`The protected website did not start (see ${import_path5.default.join(rel, STATE_DIR, "app.log")}).`, `Try: ${cdDir}npm run restart`);
8235
+ if (!siteUp) throw new DemoError(`The protected website did not start (see ${import_path5.default.join(rel, STATE_DIR, "app.log")}).`, `Try: ${cdDir}npm run restart`, "site-not-up");
8233
8236
  ok(`${import_chalk24.default.bold("Protected reservation website")}, going through the ${B.product} proxy ${apiUrl}: ${import_chalk24.default.bold(`http://localhost:${appPort}`)}`);
8234
8237
  ok(`${import_chalk24.default.bold("Unprotected reservation front-end")}, still going directly to the backend: http://localhost:${directPort}`, "the switch at the top of the page goes between them");
8235
8238
  console.log();
8236
8239
  console.log(import_chalk24.default.dim(` Logs: ${logsCommand(readEnvFile(import_path5.default.join(dir, ".env.local")))} \xB7 Stop the tunnel: ${LATEST} demo stop \xB7 Your own app: ${LATEST} dev --port <port>`));
8237
8240
  await offerBrowserAndLogs(dir, opts);
8241
+ return "demo-ready";
8238
8242
  }
8239
8243
  function printAddresses(dir) {
8240
8244
  const env = readEnvFile(import_path5.default.join(dir, ".env.local"));
@@ -8266,10 +8270,12 @@ async function runDemoStart(opts = {}) {
8266
8270
  await spawnTunnel(dir, state);
8267
8271
  } catch (err) {
8268
8272
  const e = err instanceof DemoError ? err : new DemoError(`The tunnel could not start (${err.message}).`, `Start over: ${LATEST} demo`);
8273
+ countRun({ event: `demo-restart-failed-${e.why}` });
8269
8274
  console.log(`${import_chalk24.default.red("\u2717")} ${e.message}`);
8270
8275
  console.log(` ${e.fix}`);
8271
8276
  process.exit(1);
8272
8277
  }
8278
+ countRun({ event: "demo-restarted" });
8273
8279
  console.log(`${import_chalk24.default.green("\u2713")} Tunnel open: the public address reaches localhost:${env.BACKEND_PORT} again ${import_chalk24.default.dim(`(${Math.round((Date.now() - t0) / 1e3)} s)`)}`);
8274
8280
  printAddresses(dir);
8275
8281
  const appUp = await fetch(`http://127.0.0.1:${env.APP_PORT}/api/config`).then(() => true).catch(() => false);
@@ -8373,9 +8379,10 @@ var init_demo = __esm({
8373
8379
  DEMO_GINO_ADMIN_EMAIL = "gina@ginopizza.example";
8374
8380
  RULE_SENTENCE = "Only the person who made a reservation can change or cancel it";
8375
8381
  DemoError = class extends Error {
8376
- constructor(message, fix) {
8382
+ constructor(message, fix, why = "unexpected") {
8377
8383
  super(message);
8378
8384
  this.fix = fix;
8385
+ this.why = why;
8379
8386
  }
8380
8387
  };
8381
8388
  ADJ = ["brave", "calm", "clever", "eager", "happy", "jolly", "lucky", "merry", "noble", "quick", "sunny", "swift", "witty", "zesty", "bright", "gentle"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.21.40",
3
+ "version": "0.21.41",
4
4
  "description": "APIblaze CLI — Chat with your APIs, Manage your API keys, users and groups with the APIblaze serverless proxy",
5
5
  "keywords": [
6
6
  "apiblaze",