@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.
- package/README.md +67 -3
- package/dist/atomic-json.d.ts +18 -0
- package/dist/atomic-json.js +39 -0
- package/dist/atomic-json.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/commands/cover.js +2 -1
- package/dist/commands/cover.js.map +1 -1
- package/dist/commands/dev.js +79 -35
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.js +1 -1
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/reload.d.ts +1 -1
- package/dist/commands/reload.js +14 -5
- package/dist/commands/reload.js.map +1 -1
- package/dist/commands/verify.js +134 -118
- package/dist/commands/verify.js.map +1 -1
- package/dist/editor/motion-panel.d.ts +2 -2
- package/dist/editor/motion-panel.js +27 -5
- package/dist/editor/motion-panel.js.map +1 -1
- package/dist/editor/reload-bus.d.ts +11 -23
- package/dist/editor/reload-bus.js +55 -8
- package/dist/editor/reload-bus.js.map +1 -1
- package/dist/editor/server.d.ts +10 -0
- package/dist/editor/server.js +46 -0
- package/dist/editor/server.js.map +1 -1
- package/dist/editor/shell-page.d.ts +10 -0
- package/dist/editor/shell-page.js +58 -3
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/editor/tab-reuse.d.ts +23 -0
- package/dist/editor/tab-reuse.js +61 -0
- package/dist/editor/tab-reuse.js.map +1 -0
- package/dist/local-port.d.ts +8 -1
- package/dist/local-port.js +9 -2
- package/dist/local-port.js.map +1 -1
- package/dist/machine-lock.d.ts +56 -0
- package/dist/machine-lock.js +213 -0
- package/dist/machine-lock.js.map +1 -0
- package/dist/port-lane.d.ts +27 -0
- package/dist/port-lane.js +36 -1
- package/dist/port-lane.js.map +1 -1
- package/dist/process-alive.d.ts +13 -0
- package/dist/process-alive.js +24 -0
- package/dist/process-alive.js.map +1 -0
- package/dist/project/dev-handle.d.ts +6 -34
- package/dist/project/dev-handle.js +9 -59
- package/dist/project/dev-handle.js.map +1 -1
- package/dist/project/dev-ports.d.ts +67 -0
- package/dist/project/dev-ports.js +204 -0
- package/dist/project/dev-ports.js.map +1 -0
- package/dist/project/game-name.d.ts +9 -0
- package/dist/project/game-name.js +21 -0
- package/dist/project/game-name.js.map +1 -0
- package/dist/project/jobs.js +2 -1
- package/dist/project/jobs.js.map +1 -1
- package/dist/scaffold/project-files.js +8 -8
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/smoke/run.d.ts +4 -0
- package/dist/smoke/run.js +5 -2
- package/dist/smoke/run.js.map +1 -1
- package/dist/verify/iterate.d.ts +5 -0
- package/dist/verify/iterate.js +23 -20
- package/dist/verify/iterate.js.map +1 -1
- package/dist/verify/lock.d.ts +32 -0
- package/dist/verify/lock.js +54 -0
- package/dist/verify/lock.js.map +1 -0
- 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(
|
|
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
|
package/dist/port-lane.js.map
CHANGED
|
@@ -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,
|
|
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`
|
|
31
|
-
*
|
|
32
|
-
*
|
|
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).
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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`
|
|
48
|
-
*
|
|
49
|
-
*
|
|
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).
|
|
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":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
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"}
|
package/dist/project/jobs.js
CHANGED
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
*/
|
|
49
49
|
import * as fs from 'fs';
|
|
50
50
|
import * as path from 'path';
|
|
51
|
-
import {
|
|
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;
|