@superblocksteam/sdk 2.0.153-next.2 → 2.0.153

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 (26) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/cli-replacement/dev-s3-restore.test.mjs +0 -1
  3. package/dist/cli-replacement/dev-s3-restore.test.mjs.map +1 -1
  4. package/dist/cli-replacement/dev-startup-git-before-dbfs-order.test.mjs +7 -59
  5. package/dist/cli-replacement/dev-startup-git-before-dbfs-order.test.mjs.map +1 -1
  6. package/dist/cli-replacement/dev.d.mts.map +1 -1
  7. package/dist/cli-replacement/dev.mjs +1 -35
  8. package/dist/cli-replacement/dev.mjs.map +1 -1
  9. package/dist/cli-replacement/install-packages.npm-registry.test.mjs +0 -30
  10. package/dist/cli-replacement/install-packages.npm-registry.test.mjs.map +1 -1
  11. package/package.json +6 -6
  12. package/src/cli-replacement/dev-s3-restore.test.mts +0 -1
  13. package/src/cli-replacement/dev-startup-git-before-dbfs-order.test.mts +58 -129
  14. package/src/cli-replacement/dev.mts +0 -42
  15. package/src/cli-replacement/install-packages.npm-registry.test.mts +0 -43
  16. package/tsconfig.tsbuildinfo +1 -1
  17. package/dist/cli-replacement/npm-install-cpu.d.mts +0 -15
  18. package/dist/cli-replacement/npm-install-cpu.d.mts.map +0 -1
  19. package/dist/cli-replacement/npm-install-cpu.mjs +0 -79
  20. package/dist/cli-replacement/npm-install-cpu.mjs.map +0 -1
  21. package/dist/cli-replacement/npm-install-cpu.test.d.mts +0 -2
  22. package/dist/cli-replacement/npm-install-cpu.test.d.mts.map +0 -1
  23. package/dist/cli-replacement/npm-install-cpu.test.mjs +0 -84
  24. package/dist/cli-replacement/npm-install-cpu.test.mjs.map +0 -1
  25. package/src/cli-replacement/npm-install-cpu.mts +0 -86
  26. package/src/cli-replacement/npm-install-cpu.test.mts +0 -131
@@ -1,15 +0,0 @@
1
- /**
2
- * Sum the cpu time of children this process has already reaped, in seconds.
3
- *
4
- * Returns null when the line cannot be read as procfs output, so a platform
5
- * without procfs omits the measurement rather than reporting zero cpu.
6
- */
7
- export declare function parseReapedChildCpuSeconds(stat: string): number | null;
8
- /**
9
- * Read the reaped-child cpu total for this process.
10
- *
11
- * Never throws: this brackets a real install, and no telemetry read may turn a
12
- * completed install into a failure.
13
- */
14
- export declare function readReapedChildCpuSeconds(statPath?: string): Promise<number | null>;
15
- //# sourceMappingURL=npm-install-cpu.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"npm-install-cpu.d.mts","sourceRoot":"","sources":["../../src/cli-replacement/npm-install-cpu.mts"],"names":[],"mappings":"AA4CA;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBtE;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,GAAE,MAAuB,GAChC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB"}
@@ -1,79 +0,0 @@
1
- /**
2
- * npm's `--timing` breakdown says which phase an install spent its time in,
3
- * but not whether the pod was working or waiting. Both readings are consistent
4
- * with the same 19 second `reify:unpack`: npm streaming tarballs over the
5
- * network, or npm burning cpu it cannot get enough of. Live edit pods request
6
- * 200m of cpu and set no ceiling, so under contention they get a small
7
- * proportional share of the machine, and the usual throttling metric never
8
- * fires because there is no quota to throttle against.
9
- *
10
- * Comparing cpu time against wall time separates the two. An install that
11
- * burns roughly its wall time in cpu is compute-bound and the share it gets
12
- * matters; one that burns a fraction of it is waiting on the network or the
13
- * disk, and cpu is a dead end.
14
- *
15
- * Linux tracks this per process in `/proc/self/stat`: `cutime` and `cstime`
16
- * are the user and system time of children this process has already waited
17
- * for. Bracketing an awaited spawn with two reads gives that spawn's cpu time
18
- * without instrumenting npm itself.
19
- *
20
- * The figure is a ceiling rather than an exact attribution: any other child
21
- * the dev-server happens to reap inside the same window lands in the same
22
- * counters. Nothing else it spawns is remotely as expensive as an install, so
23
- * the reading is dominated by npm, but a few hundred milliseconds of git in
24
- * there is expected and does not change what the number is used for.
25
- */
26
- import nodeFs from "node:fs/promises";
27
- /**
28
- * `cutime`/`cstime` are in clock ticks. The kernel reports procfs times in
29
- * USER_HZ, which is 100 on Linux regardless of the kernel's internal tick
30
- * rate, and there is no way to read `sysconf(_SC_CLK_TCK)` from Node.
31
- */
32
- const USER_HZ = 100;
33
- const PROC_SELF_STAT = "/proc/self/stat";
34
- /**
35
- * Field offsets counted from `state`, which is the first field after the
36
- * executable name. `/proc/[pid]/stat` numbers `state` as field 3, so a
37
- * one-indexed field N from the man page sits at N - 3 here.
38
- */
39
- const CUTIME_OFFSET = 13;
40
- const CSTIME_OFFSET = 14;
41
- /**
42
- * Sum the cpu time of children this process has already reaped, in seconds.
43
- *
44
- * Returns null when the line cannot be read as procfs output, so a platform
45
- * without procfs omits the measurement rather than reporting zero cpu.
46
- */
47
- export function parseReapedChildCpuSeconds(stat) {
48
- // The executable name is unescaped and may itself contain spaces and
49
- // parentheses, so the positional fields start after its LAST closing paren.
50
- const commEnd = stat.lastIndexOf(")");
51
- if (commEnd === -1) {
52
- return null;
53
- }
54
- const fields = stat
55
- .slice(commEnd + 1)
56
- .trim()
57
- .split(/\s+/);
58
- const cutime = Number(fields[CUTIME_OFFSET]);
59
- const cstime = Number(fields[CSTIME_OFFSET]);
60
- if (!Number.isFinite(cutime) || !Number.isFinite(cstime)) {
61
- return null;
62
- }
63
- return (cutime + cstime) / USER_HZ;
64
- }
65
- /**
66
- * Read the reaped-child cpu total for this process.
67
- *
68
- * Never throws: this brackets a real install, and no telemetry read may turn a
69
- * completed install into a failure.
70
- */
71
- export async function readReapedChildCpuSeconds(statPath = PROC_SELF_STAT) {
72
- try {
73
- return parseReapedChildCpuSeconds(await nodeFs.readFile(statPath, "utf8"));
74
- }
75
- catch {
76
- return null;
77
- }
78
- }
79
- //# sourceMappingURL=npm-install-cpu.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"npm-install-cpu.mjs","sourceRoot":"","sources":["../../src/cli-replacement/npm-install-cpu.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,MAAM,MAAM,kBAAkB,CAAC;AAEtC;;;;GAIG;AACH,MAAM,OAAO,GAAG,GAAG,CAAC;AAEpB,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAEzC;;;;GAIG;AACH,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAAY;IACrD,qEAAqE;IACrE,4EAA4E;IAC5E,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,IAAI;SAChB,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;SAClB,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC,CAAC;IAChB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,WAAmB,cAAc;IAEjC,IAAI,CAAC;QACH,OAAO,0BAA0B,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=npm-install-cpu.test.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"npm-install-cpu.test.d.mts","sourceRoot":"","sources":["../../src/cli-replacement/npm-install-cpu.test.mts"],"names":[],"mappings":""}
@@ -1,84 +0,0 @@
1
- import nodeFs from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
- import { parseReapedChildCpuSeconds, readReapedChildCpuSeconds, } from "./npm-install-cpu.mjs";
6
- /**
7
- * Build a `/proc/self/stat` line. Real ones are a single space-separated line
8
- * whose second field is the executable name in parentheses; everything after
9
- * it is positional. Only utime/stime (fields 14 and 15) and cutime/cstime
10
- * (fields 16 and 17) matter here, so the rest is filler.
11
- */
12
- function statLine({ comm = "node", utime = 0, stime = 0, cutime = 0, cstime = 0, trailing = 20, }) {
13
- const beforeTimes = [
14
- "R", // 3 state
15
- "1", // 4 ppid
16
- "1", // 5 pgrp
17
- "1", // 6 session
18
- "0", // 7 tty_nr
19
- "-1", // 8 tpgid
20
- "4194304", // 9 flags
21
- "0", // 10 minflt
22
- "0", // 11 cminflt
23
- "0", // 12 majflt
24
- "0", // 13 cmajflt
25
- ];
26
- const fields = [
27
- ...beforeTimes,
28
- String(utime),
29
- String(stime),
30
- String(cutime),
31
- String(cstime),
32
- ...Array.from({ length: trailing }, () => "0"),
33
- ];
34
- return `42 (${comm}) ${fields.join(" ")}\n`;
35
- }
36
- let tmpDir;
37
- beforeEach(async () => {
38
- tmpDir = await nodeFs.mkdtemp(path.join(os.tmpdir(), "npm-install-cpu-"));
39
- });
40
- afterEach(async () => {
41
- await nodeFs.rm(tmpDir, { force: true, recursive: true });
42
- });
43
- describe("parseReapedChildCpuSeconds", () => {
44
- it("sums the child user and system columns and converts clock ticks to seconds", () => {
45
- // 250 + 150 ticks at 100 ticks per second.
46
- expect(parseReapedChildCpuSeconds(statLine({ cutime: 250, cstime: 150 }))).toBe(4);
47
- });
48
- it("ignores the process's own cpu columns", () => {
49
- // utime/stime are this process's; only cutime/cstime cover reaped children.
50
- expect(parseReapedChildCpuSeconds(statLine({ utime: 9999, stime: 9999, cutime: 100, cstime: 0 }))).toBe(1);
51
- });
52
- it("reads a zero as a real measurement rather than as missing", () => {
53
- // An install that spawned nothing genuinely burned no child cpu, which is
54
- // exactly the case worth telling apart from a slow one.
55
- expect(parseReapedChildCpuSeconds(statLine({}))).toBe(0);
56
- });
57
- it("handles an executable name containing spaces and parentheses", () => {
58
- // The comm field is not escaped, so splitting on spaces from the left
59
- // shifts every positional field after it.
60
- expect(parseReapedChildCpuSeconds(statLine({ comm: "my (weird) proc", cutime: 100, cstime: 100 }))).toBe(2);
61
- });
62
- it("returns null when the line has no closing parenthesis", () => {
63
- expect(parseReapedChildCpuSeconds("42 node R 1 1 1")).toBeNull();
64
- });
65
- it("returns null when the line stops before the child cpu columns", () => {
66
- expect(parseReapedChildCpuSeconds("42 (node) R 1 1 1")).toBeNull();
67
- });
68
- it("returns null when a child cpu column is not a number", () => {
69
- expect(parseReapedChildCpuSeconds(statLine({ cutime: 100 }).replace(" 100 0", " x 0"))).toBeNull();
70
- });
71
- });
72
- describe("readReapedChildCpuSeconds", () => {
73
- it("reads the measurement from the given path", async () => {
74
- const file = path.join(tmpDir, "stat");
75
- await nodeFs.writeFile(file, statLine({ cutime: 500, cstime: 500 }));
76
- await expect(readReapedChildCpuSeconds(file)).resolves.toBe(10);
77
- });
78
- it("returns null when the file does not exist", async () => {
79
- // macOS and any other non-procfs platform: the attribute is omitted
80
- // rather than reported as zero cpu.
81
- await expect(readReapedChildCpuSeconds(path.join(tmpDir, "missing"))).resolves.toBeNull();
82
- });
83
- });
84
- //# sourceMappingURL=npm-install-cpu.test.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"npm-install-cpu.test.mjs","sourceRoot":"","sources":["../../src/cli-replacement/npm-install-cpu.test.mts"],"names":[],"mappings":"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"}
@@ -1,86 +0,0 @@
1
- /**
2
- * npm's `--timing` breakdown says which phase an install spent its time in,
3
- * but not whether the pod was working or waiting. Both readings are consistent
4
- * with the same 19 second `reify:unpack`: npm streaming tarballs over the
5
- * network, or npm burning cpu it cannot get enough of. Live edit pods request
6
- * 200m of cpu and set no ceiling, so under contention they get a small
7
- * proportional share of the machine, and the usual throttling metric never
8
- * fires because there is no quota to throttle against.
9
- *
10
- * Comparing cpu time against wall time separates the two. An install that
11
- * burns roughly its wall time in cpu is compute-bound and the share it gets
12
- * matters; one that burns a fraction of it is waiting on the network or the
13
- * disk, and cpu is a dead end.
14
- *
15
- * Linux tracks this per process in `/proc/self/stat`: `cutime` and `cstime`
16
- * are the user and system time of children this process has already waited
17
- * for. Bracketing an awaited spawn with two reads gives that spawn's cpu time
18
- * without instrumenting npm itself.
19
- *
20
- * The figure is a ceiling rather than an exact attribution: any other child
21
- * the dev-server happens to reap inside the same window lands in the same
22
- * counters. Nothing else it spawns is remotely as expensive as an install, so
23
- * the reading is dominated by npm, but a few hundred milliseconds of git in
24
- * there is expected and does not change what the number is used for.
25
- */
26
- import nodeFs from "node:fs/promises";
27
-
28
- /**
29
- * `cutime`/`cstime` are in clock ticks. The kernel reports procfs times in
30
- * USER_HZ, which is 100 on Linux regardless of the kernel's internal tick
31
- * rate, and there is no way to read `sysconf(_SC_CLK_TCK)` from Node.
32
- */
33
- const USER_HZ = 100;
34
-
35
- const PROC_SELF_STAT = "/proc/self/stat";
36
-
37
- /**
38
- * Field offsets counted from `state`, which is the first field after the
39
- * executable name. `/proc/[pid]/stat` numbers `state` as field 3, so a
40
- * one-indexed field N from the man page sits at N - 3 here.
41
- */
42
- const CUTIME_OFFSET = 13;
43
- const CSTIME_OFFSET = 14;
44
-
45
- /**
46
- * Sum the cpu time of children this process has already reaped, in seconds.
47
- *
48
- * Returns null when the line cannot be read as procfs output, so a platform
49
- * without procfs omits the measurement rather than reporting zero cpu.
50
- */
51
- export function parseReapedChildCpuSeconds(stat: string): number | null {
52
- // The executable name is unescaped and may itself contain spaces and
53
- // parentheses, so the positional fields start after its LAST closing paren.
54
- const commEnd = stat.lastIndexOf(")");
55
- if (commEnd === -1) {
56
- return null;
57
- }
58
-
59
- const fields = stat
60
- .slice(commEnd + 1)
61
- .trim()
62
- .split(/\s+/);
63
- const cutime = Number(fields[CUTIME_OFFSET]);
64
- const cstime = Number(fields[CSTIME_OFFSET]);
65
- if (!Number.isFinite(cutime) || !Number.isFinite(cstime)) {
66
- return null;
67
- }
68
-
69
- return (cutime + cstime) / USER_HZ;
70
- }
71
-
72
- /**
73
- * Read the reaped-child cpu total for this process.
74
- *
75
- * Never throws: this brackets a real install, and no telemetry read may turn a
76
- * completed install into a failure.
77
- */
78
- export async function readReapedChildCpuSeconds(
79
- statPath: string = PROC_SELF_STAT,
80
- ): Promise<number | null> {
81
- try {
82
- return parseReapedChildCpuSeconds(await nodeFs.readFile(statPath, "utf8"));
83
- } catch {
84
- return null;
85
- }
86
- }
@@ -1,131 +0,0 @@
1
- import nodeFs from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
-
7
- import {
8
- parseReapedChildCpuSeconds,
9
- readReapedChildCpuSeconds,
10
- } from "./npm-install-cpu.mjs";
11
-
12
- /**
13
- * Build a `/proc/self/stat` line. Real ones are a single space-separated line
14
- * whose second field is the executable name in parentheses; everything after
15
- * it is positional. Only utime/stime (fields 14 and 15) and cutime/cstime
16
- * (fields 16 and 17) matter here, so the rest is filler.
17
- */
18
- function statLine({
19
- comm = "node",
20
- utime = 0,
21
- stime = 0,
22
- cutime = 0,
23
- cstime = 0,
24
- trailing = 20,
25
- }: {
26
- comm?: string;
27
- utime?: number;
28
- stime?: number;
29
- cutime?: number;
30
- cstime?: number;
31
- trailing?: number;
32
- }): string {
33
- const beforeTimes = [
34
- "R", // 3 state
35
- "1", // 4 ppid
36
- "1", // 5 pgrp
37
- "1", // 6 session
38
- "0", // 7 tty_nr
39
- "-1", // 8 tpgid
40
- "4194304", // 9 flags
41
- "0", // 10 minflt
42
- "0", // 11 cminflt
43
- "0", // 12 majflt
44
- "0", // 13 cmajflt
45
- ];
46
- const fields = [
47
- ...beforeTimes,
48
- String(utime),
49
- String(stime),
50
- String(cutime),
51
- String(cstime),
52
- ...Array.from({ length: trailing }, () => "0"),
53
- ];
54
- return `42 (${comm}) ${fields.join(" ")}\n`;
55
- }
56
-
57
- let tmpDir: string;
58
-
59
- beforeEach(async () => {
60
- tmpDir = await nodeFs.mkdtemp(path.join(os.tmpdir(), "npm-install-cpu-"));
61
- });
62
-
63
- afterEach(async () => {
64
- await nodeFs.rm(tmpDir, { force: true, recursive: true });
65
- });
66
-
67
- describe("parseReapedChildCpuSeconds", () => {
68
- it("sums the child user and system columns and converts clock ticks to seconds", () => {
69
- // 250 + 150 ticks at 100 ticks per second.
70
- expect(
71
- parseReapedChildCpuSeconds(statLine({ cutime: 250, cstime: 150 })),
72
- ).toBe(4);
73
- });
74
-
75
- it("ignores the process's own cpu columns", () => {
76
- // utime/stime are this process's; only cutime/cstime cover reaped children.
77
- expect(
78
- parseReapedChildCpuSeconds(
79
- statLine({ utime: 9999, stime: 9999, cutime: 100, cstime: 0 }),
80
- ),
81
- ).toBe(1);
82
- });
83
-
84
- it("reads a zero as a real measurement rather than as missing", () => {
85
- // An install that spawned nothing genuinely burned no child cpu, which is
86
- // exactly the case worth telling apart from a slow one.
87
- expect(parseReapedChildCpuSeconds(statLine({}))).toBe(0);
88
- });
89
-
90
- it("handles an executable name containing spaces and parentheses", () => {
91
- // The comm field is not escaped, so splitting on spaces from the left
92
- // shifts every positional field after it.
93
- expect(
94
- parseReapedChildCpuSeconds(
95
- statLine({ comm: "my (weird) proc", cutime: 100, cstime: 100 }),
96
- ),
97
- ).toBe(2);
98
- });
99
-
100
- it("returns null when the line has no closing parenthesis", () => {
101
- expect(parseReapedChildCpuSeconds("42 node R 1 1 1")).toBeNull();
102
- });
103
-
104
- it("returns null when the line stops before the child cpu columns", () => {
105
- expect(parseReapedChildCpuSeconds("42 (node) R 1 1 1")).toBeNull();
106
- });
107
-
108
- it("returns null when a child cpu column is not a number", () => {
109
- expect(
110
- parseReapedChildCpuSeconds(
111
- statLine({ cutime: 100 }).replace(" 100 0", " x 0"),
112
- ),
113
- ).toBeNull();
114
- });
115
- });
116
-
117
- describe("readReapedChildCpuSeconds", () => {
118
- it("reads the measurement from the given path", async () => {
119
- const file = path.join(tmpDir, "stat");
120
- await nodeFs.writeFile(file, statLine({ cutime: 500, cstime: 500 }));
121
- await expect(readReapedChildCpuSeconds(file)).resolves.toBe(10);
122
- });
123
-
124
- it("returns null when the file does not exist", async () => {
125
- // macOS and any other non-procfs platform: the attribute is omitted
126
- // rather than reported as zero cpu.
127
- await expect(
128
- readReapedChildCpuSeconds(path.join(tmpDir, "missing")),
129
- ).resolves.toBeNull();
130
- });
131
- });