@amxts/cli 0.2.1 → 0.3.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.
@@ -0,0 +1,64 @@
1
+ // The line that says a newer amxts is out: `amxts 0.2.5 is out: npx amxts
2
+ // upgrade`. Read from a file the last check wrote, so it costs a command
3
+ // nothing; once a day a check runs in a process of its own, left behind, and
4
+ // writes it again. Only in a terminal, not in CI; AMXTS_IGNORE_UPDATE_CHECK=1
5
+ // turns it off.
6
+ import { spawn } from 'node:child_process';
7
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { dirname, join } from 'node:path';
10
+ import process from 'node:process';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { compareVersions } from './core.mjs';
13
+ import { execAmxts } from './pm.mjs';
14
+ import { isCI } from './ui.mjs';
15
+
16
+ /** Where the last check is kept: `{ checked, latest }`. */
17
+ export const UPDATE_FILE = join(homedir(), '.cache', 'amxts', 'update.json');
18
+ const DAY = 24 * 60 * 60 * 1000;
19
+
20
+ function save(file, data) {
21
+ try {
22
+ mkdirSync(dirname(file), { recursive: true });
23
+ writeFileSync(file, `${JSON.stringify(data)}\n`);
24
+ } catch {}
25
+ }
26
+
27
+ /**
28
+ * The line about a newer core than the project's, or null; starts the day's
29
+ * check when the last one is older.
30
+ * @param {string} version the project's core
31
+ * @param {string} pm its package manager, for the words
32
+ * @param {{ env?: Record<string, string | undefined>, tty?: boolean, now?: number, file?: string, check?: (file: string) => void }} [options]
33
+ */
34
+ export function updateLine(version, pm, { env = process.env, tty = Boolean(process.stdout.isTTY), now = Date.now(), file = UPDATE_FILE, check = startCheck } = {}) {
35
+ if (env.AMXTS_IGNORE_UPDATE_CHECK === '1' || !tty || isCI(env)) return null;
36
+ let last = {};
37
+ try {
38
+ last = JSON.parse(readFileSync(file, 'utf8'));
39
+ } catch {}
40
+ if (!(now - (last.checked ?? 0) < DAY)) {
41
+ // Written first: a second command in the same minute does not check again.
42
+ save(file, { ...last, checked: now });
43
+ check(file);
44
+ }
45
+ return last.latest && compareVersions(last.latest, version) > 0 ? `amxts ${last.latest} is out: ${execAmxts(pm)} upgrade` : null;
46
+ }
47
+
48
+ /** Starts the check in a process of its own, which the command does not wait for. */
49
+ function startCheck(file) {
50
+ try {
51
+ spawn(process.execPath, [fileURLToPath(import.meta.url), file], { detached: true, stdio: 'ignore', windowsHide: true }).unref();
52
+ } catch {}
53
+ }
54
+
55
+ // The check itself: the latest @amxts/core on the registry npm is set to.
56
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
57
+ const file = process.argv[2];
58
+ const registry = (process.env.npm_config_registry || 'https://registry.npmjs.org/').replace(/\/?$/, '/');
59
+ try {
60
+ const response = await fetch(`${registry}@amxts/core/latest`, { signal: AbortSignal.timeout(10_000) });
61
+ const { version } = await response.json();
62
+ if (typeof version === 'string') save(file, { checked: Date.now(), latest: version });
63
+ } catch {}
64
+ }
package/src/upgrade.mjs CHANGED
@@ -240,6 +240,8 @@ export async function upgrade({ to, server = true, serverOnly = false, dryRun =
240
240
  : { ok: true, line: `the module of amxts ${version} goes in once @amxts/core ${version} is installed` };
241
241
  }
242
242
  console.log(['', ...summary(outcome)].join('\n'));
243
+ // A script that upgrades and goes on hears that the build failed.
244
+ if (outcome.build === 'failed') process.exitCode = 1;
243
245
  return outcome;
244
246
  }
245
247
 
@@ -18,41 +18,20 @@
18
18
  "**/node_modules/",
19
19
  "**/*.tgz",
20
20
  "**/bun.lock",
21
+ "**/bun.lockb",
21
22
  "**/package-lock.json",
22
23
  "**/pnpm-lock.yaml",
23
24
  "**/yarn.lock",
25
+ "**/.yarn",
24
26
  "**/.amxts/",
25
27
  "**/dist/",
26
- "**/node_modules",
27
- "**/dist",
28
- "**/bun.lockb",
29
- "**/output",
30
28
  "**/coverage",
31
- "**/temp",
32
- "**/.temp",
33
- "**/tmp",
34
- "**/.tmp",
35
- "**/.history",
36
- "**/.vitepress/cache",
37
- "**/.nuxt",
38
- "**/.next",
39
- "**/.svelte-kit",
40
- "**/.vercel",
41
- "**/.changeset",
42
- "**/.idea",
43
29
  "**/.cache",
44
- "**/.output",
45
- "**/.vite-inspect",
46
- "**/.yarn",
30
+ "**/__snapshots__",
47
31
  "**/CHANGELOG*.md",
48
32
  "**/LICENSE*",
49
- "**/*.min.*",
50
- "**/__snapshots__",
51
- "**/vite.config.*.timestamp-*",
52
- "**/auto-import.d.ts",
53
- "**/auto-imports.d.ts",
54
- "**/components.d.ts",
55
- "**/.context",
33
+ "**/.history",
34
+ "**/.idea",
56
35
  "**/.claude",
57
36
  "**/.agents",
58
37
  "**/.*/skills"
@@ -21,5 +21,5 @@ let greeting = "";
21
21
 
22
22
  /** Greets the player in chat. */
23
23
  export function greet(player: Player) {
24
- print(player, `${greeting}, ${player.name}!`);
24
+ player.print(`${greeting}, ${player.name}!`);
25
25
  }
@@ -18,41 +18,20 @@
18
18
  "**/node_modules/",
19
19
  "**/*.tgz",
20
20
  "**/bun.lock",
21
+ "**/bun.lockb",
21
22
  "**/package-lock.json",
22
23
  "**/pnpm-lock.yaml",
23
24
  "**/yarn.lock",
25
+ "**/.yarn",
24
26
  "**/.amxts/",
25
27
  "**/dist/",
26
- "**/node_modules",
27
- "**/dist",
28
- "**/bun.lockb",
29
- "**/output",
30
28
  "**/coverage",
31
- "**/temp",
32
- "**/.temp",
33
- "**/tmp",
34
- "**/.tmp",
35
- "**/.history",
36
- "**/.vitepress/cache",
37
- "**/.nuxt",
38
- "**/.next",
39
- "**/.svelte-kit",
40
- "**/.vercel",
41
- "**/.changeset",
42
- "**/.idea",
43
29
  "**/.cache",
44
- "**/.output",
45
- "**/.vite-inspect",
46
- "**/.yarn",
30
+ "**/__snapshots__",
47
31
  "**/CHANGELOG*.md",
48
32
  "**/LICENSE*",
49
- "**/*.min.*",
50
- "**/__snapshots__",
51
- "**/vite.config.*.timestamp-*",
52
- "**/auto-import.d.ts",
53
- "**/auto-imports.d.ts",
54
- "**/components.d.ts",
55
- "**/.context",
33
+ "**/.history",
34
+ "**/.idea",
56
35
  "**/.claude",
57
36
  "**/.agents",
58
37
  "**/.*/skills"
@@ -14,12 +14,17 @@ const greeting = "Welcome";
14
14
  // #endif
15
15
 
16
16
  server.addCommand("/hp", ({ player }) => sayHp(player));
17
- server.addEventListener("putInServer", event => print(0, `${greeting}, ${event.player.name}!`));
17
+ server.addEventListener("putInServer", event => event.player.print(`${greeting}, ${event.player.name}!`));
18
18
  // #if menu-core
19
19
 
20
20
  // A menu made in code: its title and items can be functions of the player it is shown to.
21
21
  const hello = menus.create("HELLO", { title: ({ player }) => `Hello, ${player.name}` });
22
- hello.addItem({ title: "Wave", onSelect: ({ player }) => print(0, `${player.name} waves`) });
22
+ hello.addItem({
23
+ title: "Wave",
24
+ onSelect: ({ player }) => {
25
+ server.print(`${player.name} waves`);
26
+ },
27
+ });
23
28
  hello.addItem({
24
29
  title: ({ player }) => `Heal (${player.health} HP)`,
25
30
  visible: ({ player }) => player.health < 100,
@@ -34,7 +39,12 @@ server.addCommand("/menu", ({ player }) => hello.show(player));
34
39
 
35
40
  // A menu: its title and items can be functions of the player it is shown to.
36
41
  const hello = new Menu(({ player }) => `Hello, ${player.name}`);
37
- hello.addItem({ title: "Wave", onSelect: ({ player }) => print(0, `${player.name} waves`) });
42
+ hello.addItem({
43
+ title: "Wave",
44
+ onSelect: ({ player }) => {
45
+ server.print(`${player.name} waves`);
46
+ },
47
+ });
38
48
  hello.addItem({
39
49
  title: ({ player }) => `Heal (${player.health} HP)`,
40
50
  visible: ({ player }) => player.health < 100,
@@ -47,5 +57,5 @@ server.addCommand("/menu", ({ player }) => hello.show(player));
47
57
  // #endif
48
58
 
49
59
  function sayHp(player: Player) {
50
- print(player, `${player.name}, your HP: ${player.health}`);
60
+ player.print(`${player.name}, your HP: ${player.health}`);
51
61
  }
@@ -44,4 +44,15 @@ test("/menu heals a hurt player", async () => {
44
44
  menus.press(alice, 2);
45
45
  expect(alice.health).toBe(100);
46
46
  });
47
+
48
+ test("Wave in /menu tells everyone", async () => {
49
+ const server = await setup();
50
+ const menus = menusOf(server);
51
+ const alice = server.join("Alice");
52
+ const bob = server.join("Bob");
53
+
54
+ alice.say("/menu");
55
+ menus.press(alice, 1);
56
+ expect(bob.chat).toContain("Alice waves");
57
+ });
47
58
  // #endif