@akanjs/devkit 3.1.1-rc.10 → 3.1.1-rc.11
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/akanApp/akanApp.host.test.ts +12 -0
- package/akanApp/akanApp.host.ts +12 -0
- package/akanConfig/akanConfig.test.ts +30 -0
- package/akanConfig/akanConfig.ts +10 -0
- package/akanConfig/productionLockfile.test.ts +31 -0
- package/akanConfig/productionLockfile.ts +2 -0
- package/commandDecorators/workspaceVersionCheck.test.ts +17 -7
- package/commandDecorators/workspaceVersionCheck.ts +4 -1
- package/csrE2e/ssrRegistry.e2e.test.ts +6 -1
- package/integration/devStability.integration.test.ts +9 -5
- package/mobile/desktopRelease.test.ts +1 -1
- package/mobile/desktopRelease.ts +3 -1
- package/package.json +2 -2
- package/platformTest/LinuxDockerTarget.ts +3 -0
- package/signalMutationIndex.test.ts +15 -0
- package/signalMutationIndex.ts +1 -1
- package/types.ts +1 -0
|
@@ -13,6 +13,18 @@ describe("readProcessRssBytes", () => {
|
|
|
13
13
|
expect(rssBytes).toBeLessThan(64 * 1024 * 1024 * 1024);
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
+
// The builder's own report triggers a recycle and this reading settles it, so the two must agree.
|
|
17
|
+
test.skipIf(process.platform !== "darwin")("reads what the process reports once it frees memory", async () => {
|
|
18
|
+
let held: number[][] | null = Array.from({ length: 64 }, (_, idx) => new Array<number>(500_000).fill(idx + 0.5));
|
|
19
|
+
expect(held.length).toBe(64);
|
|
20
|
+
held = null;
|
|
21
|
+
Bun.gc(true);
|
|
22
|
+
await Bun.sleep(500);
|
|
23
|
+
const rssBytes = await AkanAppHost.readProcessRssBytes(process.pid);
|
|
24
|
+
if (rssBytes === null) throw new Error("expected to read this process's own rss");
|
|
25
|
+
expect(Math.abs(rssBytes - process.memoryUsage.rss())).toBeLessThan(64 * 1024 * 1024);
|
|
26
|
+
});
|
|
27
|
+
|
|
16
28
|
// A 0 would read as "settled below the ceiling" and cancel a recycle that should happen.
|
|
17
29
|
test("returns null for a pid that does not exist", async () => {
|
|
18
30
|
expect(await AkanAppHost.readProcessRssBytes(2_147_483_646)).toBeNull();
|
package/akanApp/akanApp.host.ts
CHANGED
|
@@ -651,6 +651,13 @@ export class AkanAppHost {
|
|
|
651
651
|
["tasklist", "/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"],
|
|
652
652
|
AkanAppHost.#tasklistRssKb,
|
|
653
653
|
);
|
|
654
|
+
if (process.platform === "darwin") {
|
|
655
|
+
const footprintBytes = await AkanAppHost.#readRssKbVia(
|
|
656
|
+
["footprint", "-p", String(pid), "-f", "bytes"],
|
|
657
|
+
AkanAppHost.#footprintKb,
|
|
658
|
+
);
|
|
659
|
+
if (footprintBytes !== null) return footprintBytes;
|
|
660
|
+
}
|
|
654
661
|
const status = await Bun.file(`/proc/${pid}/status`)
|
|
655
662
|
.text()
|
|
656
663
|
.catch(() => null);
|
|
@@ -687,6 +694,11 @@ export class AkanAppHost {
|
|
|
687
694
|
const memUsage = /"([^"]*)"\s*$/m.exec(output)?.[1];
|
|
688
695
|
return memUsage ? Number(memUsage.replace(/\D/g, "")) : Number.NaN;
|
|
689
696
|
}
|
|
697
|
+
// The footprint is what `process.memoryUsage.rss()` reports on macOS; `ps` also counts pages freed for reuse.
|
|
698
|
+
static #footprintKb(output: string): number {
|
|
699
|
+
const bytes = /Footprint: (\d+) B\b/.exec(output)?.[1];
|
|
700
|
+
return bytes ? Number(bytes) / 1024 : Number.NaN;
|
|
701
|
+
}
|
|
690
702
|
#cancelRssRecycle(): void {
|
|
691
703
|
this.#rssRecycleReason = null;
|
|
692
704
|
this.#rssRecycleOver = null;
|
|
@@ -317,6 +317,36 @@ describe("AkanAppConfig", () => {
|
|
|
317
317
|
expect(() => brokenConfig.getProductionPackageJson()).toThrow("Dependency missing-lib not found");
|
|
318
318
|
});
|
|
319
319
|
|
|
320
|
+
test("carries the root's overrides and resolutions, so the image resolves what the workspace locked", () => {
|
|
321
|
+
const pinned = new AkanAppConfig(
|
|
322
|
+
app,
|
|
323
|
+
[],
|
|
324
|
+
{ ...packageJson, overrides: { "js-tokens": "2.0.0" }, resolutions: { ms: "2.0.0" } },
|
|
325
|
+
{},
|
|
326
|
+
baseDevEnv,
|
|
327
|
+
);
|
|
328
|
+
expect(pinned.getProductionPackageJson()).toMatchObject({
|
|
329
|
+
overrides: { "js-tokens": "2.0.0" },
|
|
330
|
+
resolutions: { ms: "2.0.0" },
|
|
331
|
+
});
|
|
332
|
+
const local = new AkanAppConfig(
|
|
333
|
+
app,
|
|
334
|
+
[],
|
|
335
|
+
{
|
|
336
|
+
...packageJson,
|
|
337
|
+
overrides: { "js-tokens": "2.0.0", patched: "file:./patches/patched", shared: "workspace:*" },
|
|
338
|
+
resolutions: { ms: "link:../ms" },
|
|
339
|
+
},
|
|
340
|
+
{},
|
|
341
|
+
baseDevEnv,
|
|
342
|
+
).getProductionPackageJson();
|
|
343
|
+
expect(local.overrides).toEqual({ "js-tokens": "2.0.0" });
|
|
344
|
+
expect(local).not.toHaveProperty("resolutions");
|
|
345
|
+
const unpinned = new AkanAppConfig(app, [], packageJson, {}, baseDevEnv).getProductionPackageJson();
|
|
346
|
+
expect(unpinned).not.toHaveProperty("overrides");
|
|
347
|
+
expect(unpinned).not.toHaveProperty("resolutions");
|
|
348
|
+
});
|
|
349
|
+
|
|
320
350
|
test("falls back to akanjs package versions for built-in runtime dependencies", () => {
|
|
321
351
|
const runtimeDependencies = {
|
|
322
352
|
...akanPackageJson.dependencies,
|
package/akanConfig/akanConfig.ts
CHANGED
|
@@ -776,7 +776,15 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
776
776
|
static get akanVersion() {
|
|
777
777
|
return getAkanPackageJson().version;
|
|
778
778
|
}
|
|
779
|
+
//? The image installs from the registry alone: a pin to a path or a workspace package resolves nowhere there.
|
|
780
|
+
static #registryPins(pins: Record<string, string> | undefined) {
|
|
781
|
+
if (!pins) return undefined;
|
|
782
|
+
const kept = Object.entries(pins).filter(([, spec]) => !/^(?:file:|link:|workspace:|portal:|[./~])/.test(spec));
|
|
783
|
+
return kept.length ? Object.fromEntries(kept) : undefined;
|
|
784
|
+
}
|
|
779
785
|
getProductionPackageJson(data: Partial<PackageJson> = {}): PackageJson {
|
|
786
|
+
const overrides = AkanAppConfig.#registryPins(this.rootPackageJson.overrides);
|
|
787
|
+
const resolutions = AkanAppConfig.#registryPins(this.rootPackageJson.resolutions);
|
|
780
788
|
return {
|
|
781
789
|
name: this.app.name,
|
|
782
790
|
description: this.app.name,
|
|
@@ -789,6 +797,8 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
789
797
|
]),
|
|
790
798
|
),
|
|
791
799
|
...(this.trustedDependencies.length ? { trustedDependencies: this.trustedDependencies } : {}),
|
|
800
|
+
...(overrides ? { overrides } : {}),
|
|
801
|
+
...(resolutions ? { resolutions } : {}),
|
|
792
802
|
...data,
|
|
793
803
|
};
|
|
794
804
|
}
|
|
@@ -70,6 +70,37 @@ describe("ProductionLockfile", () => {
|
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
test("keeps a version an override pinned outside its parent's range", async () => {
|
|
74
|
+
const overriddenLock = `{
|
|
75
|
+
"lockfileVersion": 1,
|
|
76
|
+
"configVersion": 1,
|
|
77
|
+
"workspaces": {
|
|
78
|
+
"": {
|
|
79
|
+
"name": "repo",
|
|
80
|
+
"dependencies": {
|
|
81
|
+
"loose-envify": "^1.4.0",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
"overrides": {
|
|
86
|
+
"js-tokens": "2.0.0",
|
|
87
|
+
},
|
|
88
|
+
"packages": {
|
|
89
|
+
"js-tokens": ["js-tokens@2.0.0", "", {}, "sha512-kk4RjoztHFeAlCNPdTr/uF04oN5z53L18LJfADBQ7YHkO3iR2rP2ZwOH0n33r2FRw0psRmjTDEkOWPE//zmujg=="],
|
|
90
|
+
|
|
91
|
+
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
await withWorkspace(overriddenLock, async (root) => {
|
|
96
|
+
const lock = await ProductionLockfile.prune(root, {
|
|
97
|
+
...productionPackageJson,
|
|
98
|
+
overrides: { "js-tokens": "2.0.0" },
|
|
99
|
+
});
|
|
100
|
+
expect(lock).toContain('"js-tokens": ["js-tokens@2.0.0"');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
73
104
|
test("refuses a workspace without a bun.lock, so the build can fall back to an unlocked install", async () => {
|
|
74
105
|
await withWorkspace(null, async (root) => {
|
|
75
106
|
await expect(ProductionLockfile.prune(root, productionPackageJson)).rejects.toThrow(
|
|
@@ -13,9 +13,11 @@ export class ProductionLockfile {
|
|
|
13
13
|
if (!(await workspaceLock.exists())) throw new Error(`the workspace root has no ${ProductionLockfile.fileName}`);
|
|
14
14
|
const dir = await mkdtemp(path.join(os.tmpdir(), "akan-production-lock-"));
|
|
15
15
|
try {
|
|
16
|
+
const npmrc = Bun.file(path.join(workspaceRoot, ".npmrc"));
|
|
16
17
|
await Promise.all([
|
|
17
18
|
Bun.write(path.join(dir, "package.json"), JSON.stringify(packageJson, null, 2)),
|
|
18
19
|
Bun.write(path.join(dir, ProductionLockfile.fileName), workspaceLock),
|
|
20
|
+
...((await npmrc.exists()) ? [Bun.write(path.join(dir, ".npmrc"), npmrc)] : []),
|
|
19
21
|
]);
|
|
20
22
|
const install = Bun.spawn([process.execPath, "install", "--lockfile-only", "--no-progress"], {
|
|
21
23
|
cwd: dir,
|
|
@@ -23,14 +23,24 @@ describe("WorkspaceVersionCheck", () => {
|
|
|
23
23
|
expect(WorkspaceVersionCheck.warning("3.1.1-rc.5", argv("sync", "demo"), await makeWorkspace({}))).toBeNull();
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
test("names both versions and the matching CLI with the same arguments", async () => {
|
|
26
|
+
test("names both versions and the matching CLI with the same arguments, quoted for the shell it runs in", async () => {
|
|
27
27
|
const root = await makeWorkspace({ akanjs: "3.1.0-rc.6" });
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const platform = Object.getOwnPropertyDescriptor(process, "platform");
|
|
29
|
+
const warningOn = (os: NodeJS.Platform) => {
|
|
30
|
+
Object.defineProperty(process, "platform", { value: os });
|
|
31
|
+
return WorkspaceVersionCheck.warning("3.1.0", argv("sync", "demo", "--title", "it's"), root);
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
const posix = warningOn("linux");
|
|
35
|
+
expect(posix).toContain("@akanjs/cli 3.1.0, but this workspace has akanjs 3.1.0-rc.6");
|
|
36
|
+
expect(posix).toContain(`Run the matching CLI: bunx -p @akanjs/cli@3.1.0-rc.6 akan sync demo --title 'it'\\''s'`);
|
|
37
|
+
expect(posix).toEndWith("together: akan update");
|
|
38
|
+
expect(warningOn("win32")).toContain(
|
|
39
|
+
`Run the matching CLI: bunx -p @akanjs/cli@3.1.0-rc.6 akan sync demo --title "it's"`,
|
|
40
|
+
);
|
|
41
|
+
} finally {
|
|
42
|
+
if (platform) Object.defineProperty(process, "platform", platform);
|
|
43
|
+
}
|
|
34
44
|
});
|
|
35
45
|
|
|
36
46
|
test("offers akan update with the CLI's prerelease tag only when the CLI is the newer one", async () => {
|
|
@@ -45,6 +45,9 @@ export class WorkspaceVersionCheck {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
static #quote(arg: string) {
|
|
48
|
-
|
|
48
|
+
if (/^[\w@%+=:,./-]+$/.test(arg)) return arg;
|
|
49
|
+
//? cmd and PowerShell take neither single quotes nor '\'' as POSIX shells do.
|
|
50
|
+
if (process.platform === "win32") return `"${arg.replaceAll('"', '\\"')}"`;
|
|
51
|
+
return `'${arg.replaceAll("'", `'\\''`)}'`;
|
|
49
52
|
}
|
|
50
53
|
}
|
|
@@ -61,7 +61,12 @@ describe.skipIf(!CsrE2eHarness.enabled)("SSR dev registry (minimal)", () => {
|
|
|
61
61
|
let ssr: CsrE2eHarness;
|
|
62
62
|
|
|
63
63
|
beforeAll(async () => {
|
|
64
|
-
ssr = await CsrE2eHarness.start({
|
|
64
|
+
ssr = await CsrE2eHarness.start({
|
|
65
|
+
app: "minimal",
|
|
66
|
+
port,
|
|
67
|
+
// The server-log assertions match verbose-level lines, which a workspace `.env` may or may not turn on.
|
|
68
|
+
env: { AKAN_DEV_SSR_ARM_DELAY_MS: "3000", AKAN_PUBLIC_LOG_LEVEL: "verbose" },
|
|
69
|
+
});
|
|
65
70
|
}, 240_000);
|
|
66
71
|
|
|
67
72
|
afterAll(async () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { AkanAppHost } from "../akanApp";
|
|
2
3
|
import { DevGeneratedIndexSync } from "../frontendBuild";
|
|
3
4
|
import { DevStabilityHarness, type DevStabilityHmrProbe } from "./devStabilityHarness";
|
|
4
5
|
|
|
@@ -467,7 +468,8 @@ export class FixtureService extends serve("fixture" as const, { serverMode: "bat
|
|
|
467
468
|
const harness = await createHarness();
|
|
468
469
|
const port = await harness.resolvePort();
|
|
469
470
|
// Child 0's deterministic ws port, held the way an orphaned replica from a killed run would hold it.
|
|
470
|
-
|
|
471
|
+
//? On the child's own 127.0.0.1: macOS lets a loopback bind share a port a wildcard listener holds.
|
|
472
|
+
const blocker = Bun.serve({ port: port + 10_000, hostname: "127.0.0.1", fetch: () => new Response("occupied") });
|
|
471
473
|
try {
|
|
472
474
|
const host = await harness.startHost();
|
|
473
475
|
await host.waitForLog(/falling back to an ephemeral port/);
|
|
@@ -583,9 +585,9 @@ describe("dev resource budgets", () => {
|
|
|
583
585
|
expect(await DevStabilityHarness.buildWorkerProcess(host.proc.pid)).toBeNull();
|
|
584
586
|
});
|
|
585
587
|
|
|
586
|
-
//? Between a fresh builder and one that has built a route, or the host stops enforcing it or never recycles.
|
|
587
|
-
//?
|
|
588
|
-
const CEILING_MB = process.platform === "
|
|
588
|
+
//? Between a fresh builder and one that has built a route, or the host stops enforcing it or never recycles. Linux
|
|
589
|
+
//? measured 101-115MiB fresh, 157-176MiB built and settled; macOS's footprint (Bun 1.4.4) 65MiB fresh, 109MiB built.
|
|
590
|
+
const CEILING_MB = process.platform === "darwin" ? 87 : 135;
|
|
589
591
|
|
|
590
592
|
budgetTest("recycles the builder at an unmeetable ceiling and keeps developing through it", async () => {
|
|
591
593
|
const harness = await createHarness();
|
|
@@ -635,9 +637,11 @@ describe("dev resource budgets", () => {
|
|
|
635
637
|
await host.waitForLogSince(mark, /pages-rebundle ok/, WAIT_MS).catch(() => undefined);
|
|
636
638
|
}
|
|
637
639
|
const settled = await DevStabilityHarness.builderProcess(host.proc.pid);
|
|
640
|
+
//? Read as the host reads it: on macOS `ps` also counts the pages the builder freed.
|
|
641
|
+
const settledRss = settled ? await AkanAppHost.readProcessRssBytes(settled.pid) : null;
|
|
638
642
|
const warned = /ceiling costs about one boot build per interval/.test(host.logs.join("").slice(start));
|
|
639
643
|
//? Past the interval (a slow machine) the next report recycles instead of warning: either answer settles it.
|
|
640
|
-
if ((
|
|
644
|
+
if ((settledRss ?? 0) >= CEILING_MB * MB && !warned)
|
|
641
645
|
await host.waitForLogSince(
|
|
642
646
|
settledFrom,
|
|
643
647
|
/ceiling costs about one boot build per interval|recycling builder pid=\d+|skipped: the builder fell to/,
|
|
@@ -381,7 +381,7 @@ describe("DesktopRelease signer", () => {
|
|
|
381
381
|
|
|
382
382
|
const windowsTool = (powershell: (out: string) => Promise<number>) =>
|
|
383
383
|
recordingTool(async (cmd, env) => {
|
|
384
|
-
if (cmd[0] === "tar.exe") {
|
|
384
|
+
if (cmd[0] === path.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe")) {
|
|
385
385
|
await Bun.write(path.join(extractedTo(cmd), cmd.at(-1) ?? ""), "MZ");
|
|
386
386
|
return { code: 0, output: "" };
|
|
387
387
|
}
|
package/mobile/desktopRelease.ts
CHANGED
|
@@ -245,7 +245,9 @@ Set-Content -LiteralPath $env:AKAN_SIGNER_OUT -Value $subject -Encoding UTF8`;
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
static async #subjectOf(run: SignatureTool, archive: string, tmp: string, exe: string) {
|
|
248
|
-
|
|
248
|
+
//? Windows' own bsdtar: a GNU tar ahead of it on PATH (Git's) takes `C:` for a remote host.
|
|
249
|
+
const tar = path.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
|
|
250
|
+
await run([tar, "-xzf", archive, "-C", tmp, exe]);
|
|
249
251
|
const extracted = path.join(tmp, exe);
|
|
250
252
|
if (!(await Bun.file(extracted).exists())) return { signer: null, unread: `the archive holds no ${exe}` };
|
|
251
253
|
const out = path.join(tmp, "signer.txt");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/devkit",
|
|
3
|
-
"version": "3.1.1-rc.
|
|
3
|
+
"version": "3.1.1-rc.11",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@earendil-works/pi-tui": "1.3.0",
|
|
49
49
|
"@inquirer/prompts": "^8.7.3",
|
|
50
50
|
"@tailwindcss/node": "^4.3.3",
|
|
51
|
-
"akanjs": "3.1.1-rc.
|
|
51
|
+
"akanjs": "3.1.1-rc.11",
|
|
52
52
|
"chalk": "^6.0.1",
|
|
53
53
|
"commander": "^15.0.0",
|
|
54
54
|
"dayjs": "^1.11.23",
|
|
@@ -40,6 +40,9 @@ export class LinuxDockerTarget extends PlatformTestTarget {
|
|
|
40
40
|
"run",
|
|
41
41
|
"-d",
|
|
42
42
|
"--rm",
|
|
43
|
+
//? An init as PID 1 reaps orphans as a real host does; under `sleep` a killed orphan stays a zombie that still
|
|
44
|
+
//? answers kill(pid, 0).
|
|
45
|
+
"--init",
|
|
43
46
|
"--name",
|
|
44
47
|
container,
|
|
45
48
|
"--cpus",
|
|
@@ -110,6 +110,21 @@ export class ItemEndpoint extends endpoint(srv.item, ({ query, mutation }) => ({
|
|
|
110
110
|
expect(index.isMutation("apps/third/page/x.tsx", "approveTicket")).toBe(false);
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
+
test("reads a lib page's call by its own lib's endpoint before another lib's of that name", () => {
|
|
114
|
+
const shared = fileOf(
|
|
115
|
+
"libs/shared/lib/_shared/shared.signal.ts",
|
|
116
|
+
`export class SharedEndpoint extends endpoint(srv.shared, ({ mutation }) => ({ syncFeed: mutation(Boolean, {}).exec(() => true) })) {}`,
|
|
117
|
+
);
|
|
118
|
+
const social = fileOf(
|
|
119
|
+
"libs/social/lib/_social/social.signal.ts",
|
|
120
|
+
`export class SocialEndpoint extends endpoint(srv.social, ({ query }) => ({ syncFeed: query(Boolean, {}).exec(() => true) })) {}`,
|
|
121
|
+
);
|
|
122
|
+
const index = new SignalMutationIndex([shared, social]);
|
|
123
|
+
expect(index.isMutation("libs/social/page/x.tsx", "syncFeed")).toBe(false);
|
|
124
|
+
expect(index.policyIndexOf("libs/social/page/x.tsx", "syncFeed")).toBeNull();
|
|
125
|
+
expect(index.isMutation("libs/shared/page/x.tsx", "syncFeed")).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
|
|
113
128
|
test("ignores signal tests and specs", () => {
|
|
114
129
|
const spec = fileOf(
|
|
115
130
|
"apps/demo/lib/ticket/ticket.signal.spec.ts",
|
package/signalMutationIndex.ts
CHANGED
|
@@ -34,7 +34,7 @@ export class SignalMutationIndex {
|
|
|
34
34
|
|
|
35
35
|
#shapeOf(consumerFile: string, name: string) {
|
|
36
36
|
const [root, owner] = consumerFile.split("/");
|
|
37
|
-
const own =
|
|
37
|
+
const own = this.#shapesByOwner.get(`${root}/${owner}`)?.get(name);
|
|
38
38
|
if (own) return own;
|
|
39
39
|
for (const [key, shapes] of this.#shapesByOwner) {
|
|
40
40
|
const shape = shapes.get(name);
|
package/types.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface PackageJson {
|
|
|
12
12
|
optionalDependencies?: Record<string, string>;
|
|
13
13
|
trustedDependencies?: string[];
|
|
14
14
|
overrides?: Record<string, string>;
|
|
15
|
+
resolutions?: Record<string, string>;
|
|
15
16
|
engines?: Record<string, string>;
|
|
16
17
|
exports?: Record<string, string | Record<string, string>>;
|
|
17
18
|
bun?: {
|