@bitmagic/cli 0.1.60-dev.2 → 0.1.60-dev.4

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 (67) hide show
  1. package/README.md +67 -3
  2. package/dist/atomic-json.d.ts +18 -0
  3. package/dist/atomic-json.js +39 -0
  4. package/dist/atomic-json.js.map +1 -0
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/commands/cover.js +2 -1
  7. package/dist/commands/cover.js.map +1 -1
  8. package/dist/commands/dev.js +79 -35
  9. package/dist/commands/dev.js.map +1 -1
  10. package/dist/commands/publish.d.ts +2 -0
  11. package/dist/commands/publish.js +1 -1
  12. package/dist/commands/publish.js.map +1 -1
  13. package/dist/commands/reload.d.ts +1 -1
  14. package/dist/commands/reload.js +14 -5
  15. package/dist/commands/reload.js.map +1 -1
  16. package/dist/commands/verify.js +134 -118
  17. package/dist/commands/verify.js.map +1 -1
  18. package/dist/editor/motion-panel.d.ts +2 -2
  19. package/dist/editor/motion-panel.js +27 -5
  20. package/dist/editor/motion-panel.js.map +1 -1
  21. package/dist/editor/reload-bus.d.ts +11 -23
  22. package/dist/editor/reload-bus.js +55 -8
  23. package/dist/editor/reload-bus.js.map +1 -1
  24. package/dist/editor/server.d.ts +10 -0
  25. package/dist/editor/server.js +46 -0
  26. package/dist/editor/server.js.map +1 -1
  27. package/dist/editor/shell-page.d.ts +10 -0
  28. package/dist/editor/shell-page.js +58 -3
  29. package/dist/editor/shell-page.js.map +1 -1
  30. package/dist/editor/tab-reuse.d.ts +23 -0
  31. package/dist/editor/tab-reuse.js +61 -0
  32. package/dist/editor/tab-reuse.js.map +1 -0
  33. package/dist/local-port.d.ts +8 -1
  34. package/dist/local-port.js +9 -2
  35. package/dist/local-port.js.map +1 -1
  36. package/dist/machine-lock.d.ts +56 -0
  37. package/dist/machine-lock.js +213 -0
  38. package/dist/machine-lock.js.map +1 -0
  39. package/dist/port-lane.d.ts +27 -0
  40. package/dist/port-lane.js +36 -1
  41. package/dist/port-lane.js.map +1 -1
  42. package/dist/process-alive.d.ts +13 -0
  43. package/dist/process-alive.js +24 -0
  44. package/dist/process-alive.js.map +1 -0
  45. package/dist/project/dev-handle.d.ts +6 -34
  46. package/dist/project/dev-handle.js +9 -59
  47. package/dist/project/dev-handle.js.map +1 -1
  48. package/dist/project/dev-ports.d.ts +67 -0
  49. package/dist/project/dev-ports.js +204 -0
  50. package/dist/project/dev-ports.js.map +1 -0
  51. package/dist/project/game-name.d.ts +9 -0
  52. package/dist/project/game-name.js +21 -0
  53. package/dist/project/game-name.js.map +1 -0
  54. package/dist/project/jobs.js +2 -1
  55. package/dist/project/jobs.js.map +1 -1
  56. package/dist/scaffold/project-files.js +8 -8
  57. package/dist/scaffold/project-files.js.map +1 -1
  58. package/dist/smoke/run.d.ts +4 -0
  59. package/dist/smoke/run.js +5 -2
  60. package/dist/smoke/run.js.map +1 -1
  61. package/dist/verify/iterate.d.ts +5 -0
  62. package/dist/verify/iterate.js +23 -20
  63. package/dist/verify/iterate.js.map +1 -1
  64. package/dist/verify/lock.d.ts +32 -0
  65. package/dist/verify/lock.js +54 -0
  66. package/dist/verify/lock.js.map +1 -0
  67. package/package.json +4 -4
package/dist/port-lane.js CHANGED
@@ -49,6 +49,37 @@ export const DEV_LANE = {
49
49
  export function portLaneFor(tag) {
50
50
  return tag === 'dev' ? DEV_LANE : RELEASE_LANE;
51
51
  }
52
+ /**
53
+ * How many projects a lane holds at fixed addresses — see `slotPorts`.
54
+ *
55
+ * Nine, because a slot is ten ports wide and the lane's defaults sit at x10: slots 0-8 span
56
+ * x10-x92, and a tenth would run past the lane's end. Nine games being developed at once on one
57
+ * machine is a lot; a tenth falls back to the scan, and says so.
58
+ */
59
+ export const DEV_PORT_SLOTS = 9;
60
+ /**
61
+ * The three ports a project in `slot` of `lane` serves on.
62
+ *
63
+ * `bitmagic dev` used to start every project at the lane's defaults and drift to a random port in
64
+ * the lane when they were busy — so which project answered on 3011 depended on start order,
65
+ * bookmarks went stale, and a `bitmagic reload` falling back to the default could reach the wrong
66
+ * project. A slot is the same numbers, one tens-digit further along per project: slot 0 is the
67
+ * defaults every doc names (3010/3011/3012), slot 1 is 3020/3021/3022, and so on. The hundreds
68
+ * digit still says which install, the tens digit now says which project, and the units digit still
69
+ * reads game / shell / phone. Which slot a project holds is recorded per machine in
70
+ * `~/.bitmagic/dev-ports.json` (project/dev-ports.ts).
71
+ */
72
+ export function slotPorts(lane, slot) {
73
+ if (!Number.isInteger(slot) || slot < 0 || slot >= DEV_PORT_SLOTS) {
74
+ throw new RangeError(`dev port slot must be 0-${DEV_PORT_SLOTS - 1}, got ${slot}`);
75
+ }
76
+ const offset = 10 * slot;
77
+ return {
78
+ gamePort: lane.gamePort + offset,
79
+ editorPort: lane.editorPort + offset,
80
+ mobilePort: lane.mobilePort + offset,
81
+ };
82
+ }
52
83
  /**
53
84
  * This install's lane.
54
85
  *
@@ -61,6 +92,10 @@ export function portLaneFor(tag) {
61
92
  * before this existed. Failing closed here keeps the numbers the docs promise.
62
93
  */
63
94
  export function currentPortLane(version = currentCliVersion()) {
64
- return portLaneFor(updateTagFor(version));
95
+ return portLaneFor(currentLineTag(version));
96
+ }
97
+ /** Which published line this install is on — the key the port registry files a project under. */
98
+ export function currentLineTag(version = currentCliVersion()) {
99
+ return updateTagFor(version);
65
100
  }
66
101
  //# sourceMappingURL=port-lane.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"port-lane.js","sourceRoot":"","sources":["../src/port-lane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAkB,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAc5D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAa;IACpC,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAa;IAChC,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF,8CAA8C;AAC9C,MAAM,UAAU,WAAW,CAAC,GAAc;IACxC,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC;AACjD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB,iBAAiB,EAAE;IACnE,OAAO,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"port-lane.js","sourceRoot":"","sources":["../src/port-lane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAkB,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAc5D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAa;IACpC,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAa;IAChC,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF,8CAA8C;AAC9C,MAAM,UAAU,WAAW,CAAC,GAAc;IACxC,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC;AACjD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,IAAc,EAAE,IAAY;IACpD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,cAAc,EAAE,CAAC;QAClE,MAAM,IAAI,UAAU,CAAC,2BAA2B,cAAc,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC;IACzB,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,QAAQ,GAAG,MAAM;QAChC,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,MAAM;QACpC,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,MAAM;KACrC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB,iBAAiB,EAAE;IACnE,OAAO,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,cAAc,CAAC,UAAkB,iBAAiB,EAAE;IAClE,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Is the process that wrote a record still running?
3
+ *
4
+ * Signal 0 performs the permission and existence checks without delivering anything. EPERM means
5
+ * the process exists but belongs to someone else — alive, as far as this question goes. Works on
6
+ * Windows too, where Node maps it onto an existence check.
7
+ *
8
+ * Its own module because three unrelated records ask it: `.bitmagic/dev.json` (project/dev-handle),
9
+ * `.bitmagic/jobs/` (project/jobs) and the machine-wide locks in `~/.bitmagic/locks/`
10
+ * (machine-lock). The lock cannot import dev-handle for it — dev-handle reads the port registry,
11
+ * which takes the lock, and that would be a cycle.
12
+ */
13
+ export declare function isProcessAlive(pid: number): boolean;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Is the process that wrote a record still running?
3
+ *
4
+ * Signal 0 performs the permission and existence checks without delivering anything. EPERM means
5
+ * the process exists but belongs to someone else — alive, as far as this question goes. Works on
6
+ * Windows too, where Node maps it onto an existence check.
7
+ *
8
+ * Its own module because three unrelated records ask it: `.bitmagic/dev.json` (project/dev-handle),
9
+ * `.bitmagic/jobs/` (project/jobs) and the machine-wide locks in `~/.bitmagic/locks/`
10
+ * (machine-lock). The lock cannot import dev-handle for it — dev-handle reads the port registry,
11
+ * which takes the lock, and that would be a cycle.
12
+ */
13
+ export function isProcessAlive(pid) {
14
+ if (pid <= 0)
15
+ return false;
16
+ try {
17
+ process.kill(pid, 0);
18
+ return true;
19
+ }
20
+ catch (error) {
21
+ return error.code === 'EPERM';
22
+ }
23
+ }
24
+ //# sourceMappingURL=process-alive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-alive.js","sourceRoot":"","sources":["../src/process-alive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAQ,KAA2B,CAAC,IAAI,KAAK,OAAO,CAAC;IACvD,CAAC;AACH,CAAC"}
@@ -14,49 +14,21 @@ export interface DevHandle {
14
14
  */
15
15
  mobileUrl?: string;
16
16
  }
17
- /**
18
- * Is the process that wrote a record still running?
19
- *
20
- * Signal 0 performs the permission and existence checks without delivering anything. EPERM means
21
- * the process exists but belongs to someone else — alive, as far as this question goes. Shared with
22
- * `project/jobs.ts`, which asks it of a generation rather than of a server.
23
- */
24
- export declare function isProcessAlive(pid: number): boolean;
17
+ export { isProcessAlive } from '../process-alive.js';
25
18
  /** A `bitmagic dev` currently serving this project, or null. */
26
19
  export declare function readLiveDevHandle(root: string): DevHandle | null;
27
20
  /**
28
21
  * The editor port, owned here rather than by any one command.
29
22
  *
30
- * `dev` binds it, `reload` falls back to it when there is no handle to read, and the probe below
31
- * uses it for the same reason. It was three separate literals; a fourth reader would have made
32
- * four. Whoever changes the default changes it once.
23
+ * `dev` names it in its help text and it is what slot 0 of the port registry resolves to
24
+ * (project/dev-ports.ts). Nothing falls back to it any more: a project that has never run `dev`
25
+ * has no port to probe, and probing the lane default reached whichever OTHER project happened to
26
+ * hold slot 0 — `reload` from project B reloading project A's tab.
33
27
  *
34
28
  * A function rather than a constant since the number depends on which release line is running
35
- * (port-lane.ts). That matters most for the two readers BELOW a missing handle: a dev-line `reload`
36
- * that fell back to a fixed 3011 would probe — and reload — a release-line `bitmagic dev`, serving
37
- * somebody else's project in another window.
29
+ * (port-lane.ts).
38
30
  */
39
31
  export declare function defaultEditorPort(): number;
40
- /**
41
- * Is a `bitmagic dev` serving this project — asked the way `reload` asks it?
42
- *
43
- * There were two answers to this question and they disagreed in the field: `reload` probes the
44
- * port (recorded, else the default), while the "nobody can see this happen" nudge only read
45
- * `.bitmagic/dev.json`. A server from a build predating that file therefore reloaded tabs on
46
- * request while every generation in the same minute announced that nothing was watching. That
47
- * particular cause fixes itself on restart; two detections drifting apart does not, so there is
48
- * now one.
49
- *
50
- * The handle is still the fast path, and deliberately: it answers without touching the network,
51
- * and a live pid holding it is not something a probe can contradict. The probe is what the file's
52
- * ABSENCE means — which is "no record", never "no server".
53
- *
54
- * `GET /api/state` is the probe because it is the cheapest read-only route the dev view serves.
55
- * Reaching for `/api/reload` would answer the same question by doing the thing being asked about.
56
- */
57
- export declare function isDevServerRunning(root: string, deps?: {
58
- fetch?: typeof globalThis.fetch;
59
- }): Promise<boolean>;
60
32
  export declare function devHandlePath(root: string): string;
61
33
  export declare function writeDevHandle(root: string, handle: DevHandle): void;
62
34
  /** Best-effort: a handle we cannot remove is a stale file, never a failed shutdown. */
@@ -18,24 +18,10 @@
18
18
  import * as fs from 'fs';
19
19
  import * as path from 'path';
20
20
  import { currentPortLane } from '../port-lane.js';
21
- /**
22
- * Is the process that wrote a record still running?
23
- *
24
- * Signal 0 performs the permission and existence checks without delivering anything. EPERM means
25
- * the process exists but belongs to someone else — alive, as far as this question goes. Shared with
26
- * `project/jobs.ts`, which asks it of a generation rather than of a server.
27
- */
28
- export function isProcessAlive(pid) {
29
- if (pid <= 0)
30
- return false;
31
- try {
32
- process.kill(pid, 0);
33
- return true;
34
- }
35
- catch (error) {
36
- return error.code === 'EPERM';
37
- }
38
- }
21
+ import { isProcessAlive } from '../process-alive.js';
22
+ // Re-exported for the callers that always found it here; it moved so the machine lock could use it
23
+ // without importing this module (see process-alive.ts).
24
+ export { isProcessAlive } from '../process-alive.js';
39
25
  /** A `bitmagic dev` currently serving this project, or null. */
40
26
  export function readLiveDevHandle(root) {
41
27
  const handle = readDevHandle(root);
@@ -44,53 +30,17 @@ export function readLiveDevHandle(root) {
44
30
  /**
45
31
  * The editor port, owned here rather than by any one command.
46
32
  *
47
- * `dev` binds it, `reload` falls back to it when there is no handle to read, and the probe below
48
- * uses it for the same reason. It was three separate literals; a fourth reader would have made
49
- * four. Whoever changes the default changes it once.
33
+ * `dev` names it in its help text and it is what slot 0 of the port registry resolves to
34
+ * (project/dev-ports.ts). Nothing falls back to it any more: a project that has never run `dev`
35
+ * has no port to probe, and probing the lane default reached whichever OTHER project happened to
36
+ * hold slot 0 — `reload` from project B reloading project A's tab.
50
37
  *
51
38
  * A function rather than a constant since the number depends on which release line is running
52
- * (port-lane.ts). That matters most for the two readers BELOW a missing handle: a dev-line `reload`
53
- * that fell back to a fixed 3011 would probe — and reload — a release-line `bitmagic dev`, serving
54
- * somebody else's project in another window.
39
+ * (port-lane.ts).
55
40
  */
56
41
  export function defaultEditorPort() {
57
42
  return currentPortLane().editorPort;
58
43
  }
59
- /** Long enough for a loopback GET, short enough that a wedged server cannot stall a command. */
60
- const PROBE_TIMEOUT_MS = 2_000;
61
- /**
62
- * Is a `bitmagic dev` serving this project — asked the way `reload` asks it?
63
- *
64
- * There were two answers to this question and they disagreed in the field: `reload` probes the
65
- * port (recorded, else the default), while the "nobody can see this happen" nudge only read
66
- * `.bitmagic/dev.json`. A server from a build predating that file therefore reloaded tabs on
67
- * request while every generation in the same minute announced that nothing was watching. That
68
- * particular cause fixes itself on restart; two detections drifting apart does not, so there is
69
- * now one.
70
- *
71
- * The handle is still the fast path, and deliberately: it answers without touching the network,
72
- * and a live pid holding it is not something a probe can contradict. The probe is what the file's
73
- * ABSENCE means — which is "no record", never "no server".
74
- *
75
- * `GET /api/state` is the probe because it is the cheapest read-only route the dev view serves.
76
- * Reaching for `/api/reload` would answer the same question by doing the thing being asked about.
77
- */
78
- export async function isDevServerRunning(root, deps = {}) {
79
- if (readLiveDevHandle(root) !== null)
80
- return true;
81
- const doFetch = deps.fetch ?? globalThis.fetch;
82
- const port = readDevHandle(root)?.editorPort ?? defaultEditorPort();
83
- try {
84
- const response = await doFetch(`http://127.0.0.1:${port}/api/state`, {
85
- signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
86
- });
87
- return response.ok;
88
- }
89
- catch {
90
- // Nothing listening, or something that is not a dev view. Either way: not running.
91
- return false;
92
- }
93
- }
94
44
  export function devHandlePath(root) {
95
45
  return path.join(root, '.bitmagic', 'dev.json');
96
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dev-handle.js","sourceRoot":"","sources":["../../src/project/dev-handle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dev-handle.js","sourceRoot":"","sources":["../../src/project/dev-handle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAmBrD,mGAAmG;AACnG,wDAAwD;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,gEAAgE;AAChE,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,MAAM,KAAK,IAAI,IAAI,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACvE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,eAAe,EAAE,CAAC,UAAU,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,MAAiB;IAC5D,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,aAAa;IACf,CAAC;AACH,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxF,MAAM,MAAM,GAAG,MAAiC,CAAC;IACjD,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC9F,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,EAAE,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;KACrD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { type MachineLockDeps } from '../machine-lock.js';
2
+ import { type PortLane } from '../port-lane.js';
3
+ import type { UpdateTag } from '../update/notice.js';
4
+ export interface DevSlotEntry {
5
+ slot: number;
6
+ /** Epoch ms of the last `bitmagic dev` here — what decides who is evicted when the lane is full. */
7
+ lastUsedAt: number;
8
+ }
9
+ export declare function devPortsPath(baseDir?: string): string;
10
+ /** The key a project is filed under: its real path, so a symlinked folder is the same project. */
11
+ export declare function projectKey(root: string): string;
12
+ export interface DevPortsReadOptions {
13
+ baseDir?: string;
14
+ tag?: UpdateTag;
15
+ }
16
+ /** The slot this project holds on this install's line, or null when it has never run `dev`. */
17
+ export declare function readDevSlot(root: string, options?: DevPortsReadOptions): number | null;
18
+ /**
19
+ * Every port some project on this machine has been promised in this lane.
20
+ *
21
+ * What `reserveLanePort` (local-port.ts) keeps its throwaway ports away from: a verify that
22
+ * happened to take 3021 while project B's dev was down would push B's next `dev` into a drift,
23
+ * which is the instability the registry exists to end. `excludeRoot` leaves one project's own
24
+ * ports out — `dev` wants to avoid its NEIGHBOURS' slots, not its own.
25
+ */
26
+ export declare function registeredSlotPorts(lane: PortLane, options?: DevPortsReadOptions & {
27
+ excludeRoot?: string;
28
+ }): Set<number>;
29
+ export interface ClaimDevSlotDeps extends DevPortsReadOptions {
30
+ now?: () => number;
31
+ /** Whether a `bitmagic dev` is serving `root` right now — an entry that must not be evicted. */
32
+ isLive?: (root: string) => boolean;
33
+ lock?: MachineLockDeps;
34
+ }
35
+ /**
36
+ * The slot this project holds, taking one if it has none.
37
+ *
38
+ * An existing entry is kept and touched. A new project gets the lowest free slot. When every slot
39
+ * is held, the entry least recently used whose project has no live `dev` is evicted — a deleted
40
+ * project has no handle and simply ages out — and a lane where all nine are live right now yields
41
+ * null, which `dev` answers with the old drifting scan rather than with a refusal.
42
+ */
43
+ export declare function claimDevSlot(root: string, deps?: ClaimDevSlotDeps): Promise<number | null>;
44
+ /**
45
+ * Where a `bitmagic dev` for this project would be listening, or null when there is no reason to
46
+ * think one exists.
47
+ *
48
+ * The recorded handle first: it is what `dev` actually bound, explicit `--editor-port` included.
49
+ * Then the project's slot, which is where a `dev` started without flags lands. Then NOTHING — not
50
+ * the lane default. A project that has never run `dev` has no port of its own, and probing the
51
+ * default reached whichever other project holds slot 0.
52
+ */
53
+ export declare function preferredEditorPort(root: string, options?: DevPortsReadOptions): number | null;
54
+ /**
55
+ * Is a `bitmagic dev` serving this project — asked the way `reload` asks it?
56
+ *
57
+ * The handle is the fast path, and deliberately: it answers without touching the network, and a
58
+ * live pid holding it is not something a probe can contradict. A stale handle (a `dev` killed
59
+ * with SIGKILL leaves one) still names the likeliest port, so that is probed; so is the project's
60
+ * slot. `GET /api/state` is the probe because it is the cheapest read-only route the dev view
61
+ * serves, and it names the game it serves, so a server answering for some OTHER project on the
62
+ * same port is "not running", not "running".
63
+ */
64
+ export declare function isDevServerRunning(root: string, deps?: {
65
+ fetch?: typeof globalThis.fetch;
66
+ gameId?: string;
67
+ } & DevPortsReadOptions): Promise<boolean>;
@@ -0,0 +1,204 @@
1
+ /**
2
+ * `~/.bitmagic/dev-ports.json` — which fixed ports each project on this machine serves on.
3
+ *
4
+ * A creator making several games at once runs `bitmagic dev` in several folders. Before this
5
+ * existed every one of them started at the lane's defaults and drifted to a random port when
6
+ * those were busy, so which project answered on 3011 depended on start order, bookmarks pointed
7
+ * at the wrong game after a restart, and a `bitmagic reload` with no handle to read fell back to
8
+ * the default — reloading whichever OTHER project held it. The registry gives each project a
9
+ * slot (port-lane.ts `slotPorts`) the first time `dev` runs there, and the same slot every time
10
+ * after: the project in the 3020s stays in the 3020s.
11
+ *
12
+ * Per machine rather than per project, because the promise is about this machine's ports; per
13
+ * install line, because a dev-line and a release-line CLI have separate lanes and the slot is an
14
+ * offset into one. Keyed by the project's real path — the folder is the identity a creator has,
15
+ * and a copied project is a second project.
16
+ *
17
+ * "Remember what this project last bound" was the simpler design and does not work: B's first
18
+ * run while A is down takes 3010/3011 because they are free, both projects then remember the
19
+ * same pair, and they take turns evicting each other for ever. Exclusion between projects is the
20
+ * whole feature, and once the file excludes it is a slot table whichever way it is spelled.
21
+ *
22
+ * Allocation is a read-modify-write of one file under the machine lock (machine-lock.ts); two
23
+ * `dev`s starting in two folders at once must not both take slot 1. Reads need no lock — the file
24
+ * is replaced atomically (atomic-json.ts).
25
+ */
26
+ import * as fs from 'fs';
27
+ import * as path from 'path';
28
+ import { readJsonFile, writeJsonAtomic } from '../atomic-json.js';
29
+ import { defaultBaseDir } from '../config/credentials.js';
30
+ import { withMachineLock } from '../machine-lock.js';
31
+ import { DEV_PORT_SLOTS, currentLineTag, portLaneFor, slotPorts, } from '../port-lane.js';
32
+ import { readDevHandle, readLiveDevHandle } from './dev-handle.js';
33
+ export function devPortsPath(baseDir = defaultBaseDir()) {
34
+ return path.join(baseDir, 'dev-ports.json');
35
+ }
36
+ /** The key a project is filed under: its real path, so a symlinked folder is the same project. */
37
+ export function projectKey(root) {
38
+ try {
39
+ return fs.realpathSync.native(root);
40
+ }
41
+ catch {
42
+ return path.resolve(root);
43
+ }
44
+ }
45
+ function emptyRegistry() {
46
+ return { version: 1, lanes: { latest: {}, dev: {} } };
47
+ }
48
+ function isEntry(value) {
49
+ if (typeof value !== 'object' || value === null)
50
+ return false;
51
+ const v = value;
52
+ return Number.isInteger(v.slot) && v.slot >= 0 && v.slot < DEV_PORT_SLOTS
53
+ && typeof v.lastUsedAt === 'number';
54
+ }
55
+ /** The registry, or an empty one for a file that is missing, malformed or from another version. */
56
+ function readRegistry(baseDir) {
57
+ const parsed = readJsonFile(devPortsPath(baseDir));
58
+ if (typeof parsed !== 'object' || parsed === null)
59
+ return emptyRegistry();
60
+ const record = parsed;
61
+ if (record.version !== 1 || typeof record.lanes !== 'object' || record.lanes === null)
62
+ return emptyRegistry();
63
+ const lanes = record.lanes;
64
+ const registry = emptyRegistry();
65
+ for (const tag of ['latest', 'dev']) {
66
+ const lane = lanes[tag];
67
+ if (typeof lane !== 'object' || lane === null)
68
+ continue;
69
+ for (const [root, entry] of Object.entries(lane)) {
70
+ if (isEntry(entry))
71
+ registry.lanes[tag][root] = { slot: entry.slot, lastUsedAt: entry.lastUsedAt };
72
+ }
73
+ }
74
+ return registry;
75
+ }
76
+ /** The slot this project holds on this install's line, or null when it has never run `dev`. */
77
+ export function readDevSlot(root, options = {}) {
78
+ const registry = readRegistry(options.baseDir ?? defaultBaseDir());
79
+ const entry = registry.lanes[options.tag ?? currentLineTag()][projectKey(root)];
80
+ return entry === undefined ? null : entry.slot;
81
+ }
82
+ /**
83
+ * Every port some project on this machine has been promised in this lane.
84
+ *
85
+ * What `reserveLanePort` (local-port.ts) keeps its throwaway ports away from: a verify that
86
+ * happened to take 3021 while project B's dev was down would push B's next `dev` into a drift,
87
+ * which is the instability the registry exists to end. `excludeRoot` leaves one project's own
88
+ * ports out — `dev` wants to avoid its NEIGHBOURS' slots, not its own.
89
+ */
90
+ export function registeredSlotPorts(lane, options = {}) {
91
+ const registry = readRegistry(options.baseDir ?? defaultBaseDir());
92
+ const skip = options.excludeRoot === undefined ? null : projectKey(options.excludeRoot);
93
+ const ports = new Set();
94
+ for (const [root, entry] of Object.entries(registry.lanes[options.tag ?? currentLineTag()])) {
95
+ if (root === skip)
96
+ continue;
97
+ const slot = slotPorts(lane, entry.slot);
98
+ ports.add(slot.gamePort);
99
+ ports.add(slot.editorPort);
100
+ ports.add(slot.mobilePort);
101
+ }
102
+ return ports;
103
+ }
104
+ /**
105
+ * The slot this project holds, taking one if it has none.
106
+ *
107
+ * An existing entry is kept and touched. A new project gets the lowest free slot. When every slot
108
+ * is held, the entry least recently used whose project has no live `dev` is evicted — a deleted
109
+ * project has no handle and simply ages out — and a lane where all nine are live right now yields
110
+ * null, which `dev` answers with the old drifting scan rather than with a refusal.
111
+ */
112
+ export async function claimDevSlot(root, deps = {}) {
113
+ const baseDir = deps.baseDir ?? defaultBaseDir();
114
+ const tag = deps.tag ?? currentLineTag();
115
+ const now = deps.now ?? Date.now;
116
+ const isLive = deps.isLive ?? ((r) => readLiveDevHandle(r) !== null);
117
+ const key = projectKey(root);
118
+ return withMachineLock('dev-ports', { root, command: 'dev' }, async () => {
119
+ const registry = readRegistry(baseDir);
120
+ const lane = registry.lanes[tag];
121
+ const existing = lane[key];
122
+ if (existing !== undefined) {
123
+ existing.lastUsedAt = now();
124
+ writeJsonAtomic(devPortsPath(baseDir), registry);
125
+ return existing.slot;
126
+ }
127
+ const taken = new Set(Object.values(lane).map((entry) => entry.slot));
128
+ let slot = null;
129
+ for (let candidate = 0; candidate < DEV_PORT_SLOTS; candidate += 1) {
130
+ if (!taken.has(candidate)) {
131
+ slot = candidate;
132
+ break;
133
+ }
134
+ }
135
+ if (slot === null) {
136
+ const evictable = Object.entries(lane)
137
+ .filter(([other]) => !isLive(other))
138
+ .sort(([, a], [, b]) => a.lastUsedAt - b.lastUsedAt);
139
+ const victim = evictable[0];
140
+ if (victim === undefined)
141
+ return null;
142
+ slot = victim[1].slot;
143
+ delete lane[victim[0]];
144
+ }
145
+ lane[key] = { slot, lastUsedAt: now() };
146
+ writeJsonAtomic(devPortsPath(baseDir), registry);
147
+ return slot;
148
+ }, { baseDir, ...deps.lock });
149
+ }
150
+ /**
151
+ * Where a `bitmagic dev` for this project would be listening, or null when there is no reason to
152
+ * think one exists.
153
+ *
154
+ * The recorded handle first: it is what `dev` actually bound, explicit `--editor-port` included.
155
+ * Then the project's slot, which is where a `dev` started without flags lands. Then NOTHING — not
156
+ * the lane default. A project that has never run `dev` has no port of its own, and probing the
157
+ * default reached whichever other project holds slot 0.
158
+ */
159
+ export function preferredEditorPort(root, options = {}) {
160
+ const handle = readDevHandle(root);
161
+ if (handle !== null)
162
+ return handle.editorPort;
163
+ const slot = readDevSlot(root, options);
164
+ if (slot === null)
165
+ return null;
166
+ return slotPorts(portLaneFor(options.tag ?? currentLineTag()), slot).editorPort;
167
+ }
168
+ /** Long enough for a loopback GET, short enough that a wedged server cannot stall a command. */
169
+ const PROBE_TIMEOUT_MS = 2_000;
170
+ /**
171
+ * Is a `bitmagic dev` serving this project — asked the way `reload` asks it?
172
+ *
173
+ * The handle is the fast path, and deliberately: it answers without touching the network, and a
174
+ * live pid holding it is not something a probe can contradict. A stale handle (a `dev` killed
175
+ * with SIGKILL leaves one) still names the likeliest port, so that is probed; so is the project's
176
+ * slot. `GET /api/state` is the probe because it is the cheapest read-only route the dev view
177
+ * serves, and it names the game it serves, so a server answering for some OTHER project on the
178
+ * same port is "not running", not "running".
179
+ */
180
+ export async function isDevServerRunning(root, deps = {}) {
181
+ if (readLiveDevHandle(root) !== null)
182
+ return true;
183
+ const port = preferredEditorPort(root, deps);
184
+ if (port === null)
185
+ return false;
186
+ const doFetch = deps.fetch ?? globalThis.fetch;
187
+ try {
188
+ const response = await doFetch(`http://127.0.0.1:${port}/api/state`, {
189
+ signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
190
+ });
191
+ if (!response.ok)
192
+ return false;
193
+ if (deps.gameId === undefined)
194
+ return true;
195
+ const body = (await response.json().catch(() => null));
196
+ // A server from before /api/state carried the id cannot be told apart; believe it.
197
+ return body === null || typeof body.gameId !== 'string' || body.gameId === deps.gameId;
198
+ }
199
+ catch {
200
+ // Nothing listening, or something that is not a dev view. Either way: not running.
201
+ return false;
202
+ }
203
+ }
204
+ //# sourceMappingURL=dev-ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-ports.js","sourceRoot":"","sources":["../../src/project/dev-ports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * What to call this game where a human is looking — the dev view's bar and its browser tab.
3
+ *
4
+ * `gameName` in `src/work/game.json` is what bitmagic.ai catalogs the game as, so it is the name
5
+ * the creator chose. A project that has not named its game yet is called by its folder, which is
6
+ * the name the creator already uses in every terminal. Never the gameId: that is shown beside it,
7
+ * but nobody tells two tabs apart by a hash.
8
+ */
9
+ export declare function readGameName(root: string): string;
@@ -0,0 +1,21 @@
1
+ import * as path from 'path';
2
+ import { readJsonFile } from '../atomic-json.js';
3
+ import { aliasSourceDir } from '../scaffold/aliases.js';
4
+ /**
5
+ * What to call this game where a human is looking — the dev view's bar and its browser tab.
6
+ *
7
+ * `gameName` in `src/work/game.json` is what bitmagic.ai catalogs the game as, so it is the name
8
+ * the creator chose. A project that has not named its game yet is called by its folder, which is
9
+ * the name the creator already uses in every terminal. Never the gameId: that is shown beside it,
10
+ * but nobody tells two tabs apart by a hash.
11
+ */
12
+ export function readGameName(root) {
13
+ const parsed = readJsonFile(path.join(root, aliasSourceDir('work'), 'game.json'));
14
+ if (typeof parsed === 'object' && parsed !== null) {
15
+ const name = parsed.gameName;
16
+ if (typeof name === 'string' && name.trim() !== '')
17
+ return name.trim();
18
+ }
19
+ return path.basename(root);
20
+ }
21
+ //# sourceMappingURL=game-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-name.js","sourceRoot":"","sources":["../../src/project/game-name.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;IAClF,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAClD,MAAM,IAAI,GAAI,MAAkC,CAAC,QAAQ,CAAC;QAC1D,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC"}
@@ -48,7 +48,8 @@
48
48
  */
49
49
  import * as fs from 'fs';
50
50
  import * as path from 'path';
51
- import { isDevServerRunning, isProcessAlive } from './dev-handle.js';
51
+ import { isProcessAlive } from '../process-alive.js';
52
+ import { isDevServerRunning } from './dev-ports.js';
52
53
  export const JOBS_DIR = path.join('.bitmagic', 'jobs');
53
54
  /** Failed records are kept for the agent to read back — but not forever. */
54
55
  const FAILED_RECORD_TTL_MS = 24 * 60 * 60 * 1000;