@stacksjs/buddy 0.70.179 → 0.70.181

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.
@@ -33,6 +33,7 @@ export * from './migrate-project';
33
33
  export * from './outdated';
34
34
  export * from './phone';
35
35
  export * from './ports';
36
+ export * from './link';
36
37
  export * from './prepublish';
37
38
  export * from './projects';
38
39
  export * from './protocol';
@@ -33,6 +33,7 @@ export * from "./migrate-project";
33
33
  export * from "./outdated";
34
34
  export * from "./phone";
35
35
  export * from "./ports";
36
+ export * from "./link";
36
37
  export * from "./prepublish";
37
38
  export * from "./projects";
38
39
  export * from "./protocol";
@@ -0,0 +1,2 @@
1
+ import type { CLI } from '@stacksjs/types';
2
+ export declare function link(buddy: CLI): void;
@@ -0,0 +1,147 @@
1
+ import { existsSync, lstatSync, readdirSync, realpathSync } from "node:fs";
2
+ import fs from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import process from "node:process";
6
+ import { italic, log } from "@stacksjs/cli";
7
+ import { ExitCode } from "@stacksjs/types";
8
+ function recordPath() {
9
+ return join(process.cwd(), "storage/framework/runtime/linked-core.json");
10
+ }
11
+ function readRecord() {
12
+ try {
13
+ return JSON.parse(fs.readFileSync(recordPath(), "utf-8"));
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+ function writeRecord(record) {
19
+ fs.mkdirSync(join(process.cwd(), "storage/framework/runtime"), { recursive: !0 });
20
+ fs.writeFileSync(recordPath(), `${JSON.stringify(record, null, 2)}
21
+ `);
22
+ }
23
+ function resolveFrameworkPath(explicit) {
24
+ const candidates = [
25
+ explicit,
26
+ process.env.STACKS_FRAMEWORK_PATH,
27
+ resolve(process.cwd(), "../stacks"),
28
+ join(homedir(), "Code/stacks")
29
+ ].filter(Boolean);
30
+ for (const candidate of candidates) {
31
+ const full = resolve(candidate);
32
+ if (existsSync(join(full, "storage/framework/core")))
33
+ return full;
34
+ }
35
+ return null;
36
+ }
37
+ function corePackages(framework) {
38
+ const coreDir = join(framework, "storage/framework/core"), found = new Map;
39
+ for (const entry of readdirSync(coreDir, { withFileTypes: !0 })) {
40
+ if (!entry.isDirectory())
41
+ continue;
42
+ const dir = join(coreDir, entry.name), manifest = join(dir, "package.json");
43
+ if (!existsSync(manifest))
44
+ continue;
45
+ try {
46
+ const { name } = JSON.parse(fs.readFileSync(manifest, "utf-8"));
47
+ if (name?.startsWith("@stacksjs/"))
48
+ found.set(name, dir);
49
+ } catch {}
50
+ }
51
+ return found;
52
+ }
53
+ function normalize(name) {
54
+ return `@stacksjs/${name.replace(/^@stacksjs\//, "").replace(/^core\//, "")}`;
55
+ }
56
+ function isSymlink(path) {
57
+ try {
58
+ return lstatSync(path).isSymbolicLink();
59
+ } catch {
60
+ return !1;
61
+ }
62
+ }
63
+ export function link(buddy) {
64
+ buddy.command("link:core [...packages]", "Point this app's @stacksjs/* at a local framework checkout").option("--path <path>", "Framework checkout to link against").option("--all", "Link every core package the app has installed", { default: !1 }).example("buddy link:core auth router").example("buddy link:core --all --path ../stacks").action(async (packages, options) => {
65
+ const framework = resolveFrameworkPath(options?.path);
66
+ if (!framework) {
67
+ log.error("No framework checkout found.");
68
+ log.info("Pass one with `--path <dir>` or set STACKS_FRAMEWORK_PATH.");
69
+ process.exit(ExitCode.FatalError);
70
+ }
71
+ const available = corePackages(framework), modulesDir = join(process.cwd(), "node_modules"), wanted = (packages?.length ?? 0) > 0 ? packages.map(normalize) : [...available.keys()].filter((name) => existsSync(join(modulesDir, name)) || options?.all);
72
+ if (wanted.length === 0) {
73
+ log.info("Nothing to link: no installed @stacksjs/* packages found in this project.");
74
+ await log.flush();
75
+ process.exit(ExitCode.Success);
76
+ }
77
+ const linked = [], unbuilt = [];
78
+ for (const name of wanted) {
79
+ const source = available.get(name);
80
+ if (!source) {
81
+ log.warn(`${name} is not in ${italic(framework)} \u2014 skipped.`);
82
+ continue;
83
+ }
84
+ const target = join(modulesDir, name);
85
+ if (isSymlink(target) && realpathSync(target) === realpathSync(source)) {
86
+ linked.push(name);
87
+ continue;
88
+ }
89
+ fs.rmSync(target, { recursive: !0, force: !0 });
90
+ fs.mkdirSync(join(target, ".."), { recursive: !0 });
91
+ fs.symlinkSync(source, target, "dir");
92
+ linked.push(name);
93
+ if (!existsSync(join(source, "dist")))
94
+ unbuilt.push(name);
95
+ }
96
+ writeRecord({ framework, packages: linked });
97
+ log.success(`Linked ${linked.length} package${linked.length === 1 ? "" : "s"} to ${italic(framework)}`);
98
+ if (unbuilt.length > 0) {
99
+ log.warn(`Not built yet: ${unbuilt.join(", ")}`);
100
+ log.info("An app imports the build output, so each linked package needs `bun build.ts` in its directory.");
101
+ }
102
+ log.info("Undo with `buddy unlink:core`.");
103
+ await log.flush();
104
+ process.exit(ExitCode.Success);
105
+ });
106
+ buddy.command("unlink:core [...packages]", "Go back to the installed @stacksjs/* packages").option("--all", "Unlink everything that was linked", { default: !1 }).example("buddy unlink:core").action(async (packages, _options) => {
107
+ const record = readRecord(), modulesDir = join(process.cwd(), "node_modules"), wanted = (packages?.length ?? 0) > 0 ? packages.map(normalize) : record?.packages ?? readdirSync(join(modulesDir, "@stacksjs"), { withFileTypes: !0 }).filter((entry) => entry.isSymbolicLink()).map((entry) => `@stacksjs/${entry.name}`);
108
+ let removed = 0;
109
+ const unlinked = new Set;
110
+ for (const name of wanted) {
111
+ const target = join(modulesDir, name);
112
+ if (!isSymlink(target))
113
+ continue;
114
+ fs.rmSync(target, { force: !0 });
115
+ unlinked.add(name);
116
+ removed++;
117
+ }
118
+ const survivors = (record?.packages ?? []).filter((name) => !unlinked.has(name));
119
+ if (survivors.length > 0)
120
+ writeRecord({ framework: record.framework, packages: survivors });
121
+ else
122
+ fs.rmSync(recordPath(), { force: !0 });
123
+ if (removed === 0) {
124
+ log.info("Nothing was linked.");
125
+ await log.flush();
126
+ process.exit(ExitCode.Success);
127
+ }
128
+ log.info(`Unlinked ${removed} package${removed === 1 ? "" : "s"}; reinstalling the published copies...`);
129
+ if (await Bun.spawn(["bun", "install", "--force"], { cwd: process.cwd(), stdout: "inherit", stderr: "inherit" }).exited !== 0) {
130
+ log.error("`bun install` failed. The links are gone; re-run the install once the failure is resolved.");
131
+ process.exit(ExitCode.FatalError);
132
+ }
133
+ for (const name of survivors) {
134
+ const source = join(record.framework, "storage/framework/core", name.replace("@stacksjs/", "")), target = join(modulesDir, name);
135
+ if (!existsSync(source))
136
+ continue;
137
+ fs.rmSync(target, { recursive: !0, force: !0 });
138
+ fs.symlinkSync(source, target, "dir");
139
+ }
140
+ if (survivors.length > 0)
141
+ log.success(`Unlinked ${removed}; ${survivors.length} package${survivors.length === 1 ? "" : "s"} still linked.`);
142
+ else
143
+ log.success("This project is back on the published packages.");
144
+ await log.flush();
145
+ process.exit(ExitCode.Success);
146
+ });
147
+ }
@@ -274,10 +274,24 @@ async function unvendorFramework(force) {
274
274
  await fs.promises.writeFile(bunfigPath, next);
275
275
  }
276
276
  await fs.promises.rm(coreDir, { recursive: !0, force: !0 });
277
+ const scopedDir = resolve(process.cwd(), "node_modules/@stacksjs");
278
+ let danglingRemoved = 0;
279
+ if (existsSync(scopedDir))
280
+ for (const entry of await readdir(scopedDir, { withFileTypes: !0 })) {
281
+ if (!entry.isSymbolicLink())
282
+ continue;
283
+ const link = resolve(scopedDir, entry.name);
284
+ if (existsSync(link))
285
+ continue;
286
+ await fs.promises.rm(link, { force: !0 });
287
+ danglingRemoved++;
288
+ }
277
289
  log.success(`Removed ${italic(rel(coreDir))}`);
278
290
  log.info(`package.json now depends on ${depName}@${range}`);
279
291
  if (repointed > 0)
280
292
  log.info(`Repointed ${repointed} workspace: range${repointed === 1 ? "" : "s"} to ${range}`);
293
+ if (danglingRemoved > 0)
294
+ log.info(`Removed ${danglingRemoved} node_modules symlink${danglingRemoved === 1 ? "" : "s"} left pointing into it`);
281
295
  if (rewrittenScripts > 0)
282
296
  log.info(`Repointed ${rewrittenScripts} package.json script${rewrittenScripts === 1 ? "" : "s"} to ./buddy`);
283
297
  if (rewrittenPreloads > 0)
@@ -5,10 +5,20 @@ import process from "node:process";
5
5
  import { log } from "@stacksjs/cli";
6
6
  globalThis.requestContext = {
7
7
  cookie(name) {
8
+ const snapshot = globalThis.__stxServeContext;
9
+ if (snapshot?.cookies && name in snapshot.cookies)
10
+ return snapshot.cookies[name] ?? null;
8
11
  return globalThis.__stxServeCookies?.[name] ?? null;
9
12
  },
10
13
  url() {
11
- return globalThis.__stxServeSearch ?? "";
14
+ const snapshot = globalThis.__stxServeContext;
15
+ return snapshot?.url || snapshot?.search || globalThis.__stxServeSearch || "";
16
+ },
17
+ search() {
18
+ return globalThis.__stxServeContext?.search ?? globalThis.__stxServeSearch ?? "";
19
+ },
20
+ locale() {
21
+ return globalThis.__stxServeContext?.locale ?? "en";
12
22
  }
13
23
  };
14
24
  function parseCookies(req) {
@@ -77,6 +77,8 @@ const commandRegistry = {
77
77
  package: { path: "./commands/package.js", exportName: "packageCommands" },
78
78
  phone: { path: "./commands/phone.js", exportName: "phone" },
79
79
  ports: { path: "./commands/ports.js", exportName: "ports" },
80
+ "link:core": { path: "./commands/link.js", exportName: "link" },
81
+ "unlink:core": { path: "./commands/link.js", exportName: "link" },
80
82
  prepublish: { path: "./commands/prepublish.js", exportName: "prepublish" },
81
83
  projects: { path: "./commands/projects.js", exportName: "projects" },
82
84
  publish: { path: "./commands/publish.js", exportName: "publish" },
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/buddy",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.179",
5
+ "version": "0.70.181",
6
6
  "description": "Meet Buddy. The Stacks runtime.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -91,52 +91,52 @@
91
91
  "prepublishOnly": "bun run build"
92
92
  },
93
93
  "dependencies": {
94
- "@stacksjs/actions": "^0.70.179",
95
- "@stacksjs/ai": "^0.70.179",
96
- "@stacksjs/alias": "^0.70.179",
97
- "@stacksjs/arrays": "^0.70.179",
98
- "@stacksjs/auth": "^0.70.179",
99
- "@stacksjs/build": "^0.70.179",
100
- "@stacksjs/cache": "^0.70.179",
101
- "@stacksjs/cli": "^0.70.179",
94
+ "@stacksjs/actions": "^0.70.180",
95
+ "@stacksjs/ai": "^0.70.180",
96
+ "@stacksjs/alias": "^0.70.180",
97
+ "@stacksjs/arrays": "^0.70.180",
98
+ "@stacksjs/auth": "^0.70.180",
99
+ "@stacksjs/build": "^0.70.180",
100
+ "@stacksjs/cache": "^0.70.180",
101
+ "@stacksjs/cli": "^0.70.180",
102
102
  "@stacksjs/clapp": "^0.2.12",
103
- "@stacksjs/cloud": "^0.70.179",
104
- "@stacksjs/collections": "^0.70.179",
105
- "@stacksjs/config": "^0.70.179",
106
- "@stacksjs/database": "^0.70.179",
107
- "@stacksjs/desktop": "^0.70.179",
108
- "@stacksjs/dns": "^0.70.179",
109
- "@stacksjs/email": "^0.70.179",
110
- "@stacksjs/enums": "^0.70.179",
111
- "@stacksjs/error-handling": "^0.70.179",
112
- "@stacksjs/events": "^0.70.179",
113
- "@stacksjs/git": "^0.70.179",
103
+ "@stacksjs/cloud": "^0.70.180",
104
+ "@stacksjs/collections": "^0.70.180",
105
+ "@stacksjs/config": "^0.70.180",
106
+ "@stacksjs/database": "^0.70.180",
107
+ "@stacksjs/desktop": "^0.70.180",
108
+ "@stacksjs/dns": "^0.70.180",
109
+ "@stacksjs/email": "^0.70.180",
110
+ "@stacksjs/enums": "^0.70.180",
111
+ "@stacksjs/error-handling": "^0.70.180",
112
+ "@stacksjs/events": "^0.70.180",
113
+ "@stacksjs/git": "^0.70.180",
114
114
  "@stacksjs/gitit": "^0.2.5",
115
- "@stacksjs/health": "^0.70.179",
115
+ "@stacksjs/health": "^0.70.180",
116
116
  "@stacksjs/dnsx": "^0.2.3",
117
117
  "@stacksjs/httx": "^0.1.10",
118
- "@stacksjs/lint": "^0.70.179",
119
- "@stacksjs/logging": "^0.70.179",
120
- "@stacksjs/notifications": "^0.70.179",
121
- "@stacksjs/objects": "^0.70.179",
122
- "@stacksjs/orm": "^0.70.179",
123
- "@stacksjs/path": "^0.70.179",
124
- "@stacksjs/skills": "^0.70.179",
125
- "@stacksjs/payments": "^0.70.179",
126
- "@stacksjs/realtime": "^0.70.179",
127
- "@stacksjs/router": "^0.70.179",
118
+ "@stacksjs/lint": "^0.70.180",
119
+ "@stacksjs/logging": "^0.70.180",
120
+ "@stacksjs/notifications": "^0.70.180",
121
+ "@stacksjs/objects": "^0.70.180",
122
+ "@stacksjs/orm": "^0.70.180",
123
+ "@stacksjs/path": "^0.70.180",
124
+ "@stacksjs/skills": "^0.70.180",
125
+ "@stacksjs/payments": "^0.70.180",
126
+ "@stacksjs/realtime": "^0.70.180",
127
+ "@stacksjs/router": "^0.70.180",
128
128
  "@stacksjs/rpx": "^0.11.31",
129
- "@stacksjs/search-engine": "^0.70.179",
130
- "@stacksjs/security": "^0.70.179",
131
- "@stacksjs/server": "^0.70.179",
132
- "@stacksjs/storage": "^0.70.179",
133
- "@stacksjs/strings": "^0.70.179",
134
- "@stacksjs/testing": "^0.70.179",
135
- "@stacksjs/tunnel": "^0.70.179",
136
- "@stacksjs/types": "^0.70.179",
137
- "@stacksjs/ui": "^0.70.179",
138
- "@stacksjs/utils": "^0.70.179",
139
- "@stacksjs/validation": "^0.70.179",
129
+ "@stacksjs/search-engine": "^0.70.180",
130
+ "@stacksjs/security": "^0.70.180",
131
+ "@stacksjs/server": "^0.70.180",
132
+ "@stacksjs/storage": "^0.70.180",
133
+ "@stacksjs/strings": "^0.70.180",
134
+ "@stacksjs/testing": "^0.70.180",
135
+ "@stacksjs/tunnel": "^0.70.180",
136
+ "@stacksjs/types": "^0.70.180",
137
+ "@stacksjs/ui": "^0.70.180",
138
+ "@stacksjs/utils": "^0.70.180",
139
+ "@stacksjs/validation": "^0.70.180",
140
140
  "@stacksjs/ts-cloud": "^0.7.60",
141
141
  "ajv": "^8.20.0",
142
142
  "ajv-formats": "^3.0.1"