@stacksjs/buddy 0.70.225 → 0.70.226

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.
@@ -1,49 +1,10 @@
1
+ import { startProductionServer } from '../production-server';
1
2
  import type { CLI } from '@stacksjs/types';
2
- /**
3
- * Resolve the project's includes directory.
4
- *
5
- * A configured `config/stx.ts#partialsDir` wins outright. The convention list
6
- * below is only a fallback for apps that never set one, and it cannot stand in
7
- * for the config: the candidates are probed for existence in order, so an app
8
- * that keeps its includes in `resources/components` but also has an unrelated
9
- * `resources/partials` directory silently resolved to the wrong one and every
10
- * `@include` failed with ENOENT at runtime.
11
- *
12
- * The configured value is relative to the stx root (`resources`), matching how
13
- * stx itself reads it, but an app-root-relative path is accepted too so either
14
- * spelling resolves.
15
- */
16
- export declare function resolveUserPartialsPath(cwd?: unknown, configuredDir?: string): string | undefined;
17
- /**
18
- * Read `partialsDir` off the app's stx config. Failure is non-fatal: without
19
- * it {@link resolveUserPartialsPath} falls back to the conventions.
20
- */
21
- export declare function loadStxPartialsDir(cwd?: unknown): Promise<string | undefined>;
22
- export declare function startProductionServer(options?: { port?: string | number, verbose?: boolean }): Promise<void>;
23
3
  export declare function serve(buddy: CLI): void;
24
- /**
25
- * `buddy serve:api` — boot the production API server (bun-router routes).
26
- *
27
- * The twin of `buddy serve`: where that serves the STX frontend, this runs the
28
- * loopback API the frontend proxies `/api` + non-GET requests to. The entry
29
- * (`@stacksjs/actions/serve/api`) is resolved through the module graph, so it
30
- * works whether the framework is vendored at `storage/framework/core` OR only
31
- * installed under `node_modules/@stacksjs/actions`. This keeps deployments from
32
- * having to hardcode a `storage/framework/core/...` path in their `start`
33
- * command — `./buddy serve:api` resolves the framework wherever it lives.
34
- */
35
4
  export declare function serveApi(buddy: CLI): void;
36
- /**
37
- * `buddy preview` — serve a finished static build locally.
38
- *
39
- * The twin of `buddy serve`, for the other kind of output: where that renders
40
- * stx pages and proxies an API, this hands over pre-built files exactly as a
41
- * static host would. It exists so checking a build before shipping it does not
42
- * require a `preview.ts` in every project's root, hand-rolled per app and
43
- * quietly drifting from how the host actually behaves.
44
- *
45
- * Extensionless paths resolve to `.html`, matching the "pretty URLs" behaviour
46
- * of Netlify, Vercel and Cloudflare Pages, so a link that works here works
47
- * there.
48
- */
49
5
  export declare function preview(buddy: CLI): void;
6
+ export {
7
+ loadStxPartialsDir,
8
+ resolveUserPartialsPath,
9
+ startProductionServer,
10
+ } from '../production-server';
@@ -1,168 +1,14 @@
1
- import { ExitCode } from "@stacksjs/types";
2
1
  import { existsSync } from "node:fs";
3
- import { homedir } from "node:os";
4
- import { dirname, join } from "node:path";
2
+ import { join } from "node:path";
5
3
  import process from "node:process";
6
4
  import { log } from "@stacksjs/logging";
7
- globalThis.requestContext = {
8
- cookie(name) {
9
- const snapshot = globalThis.__stxServeContext;
10
- if (snapshot?.cookies && name in snapshot.cookies)
11
- return snapshot.cookies[name] ?? null;
12
- return globalThis.__stxServeCookies?.[name] ?? null;
13
- },
14
- url() {
15
- const snapshot = globalThis.__stxServeContext;
16
- return snapshot?.url || snapshot?.search || globalThis.__stxServeSearch || "";
17
- },
18
- search() {
19
- return globalThis.__stxServeContext?.search ?? globalThis.__stxServeSearch ?? "";
20
- },
21
- locale() {
22
- return globalThis.__stxServeContext?.locale ?? "en";
23
- }
24
- };
25
- function parseCookies(req) {
26
- const out = {}, header = req.headers.get("cookie") || "";
27
- if (!header)
28
- return out;
29
- for (const part of header.split(";")) {
30
- const trimmed = part.trim(), eq = trimmed.indexOf("=");
31
- if (eq === -1)
32
- continue;
33
- const k = trimmed.slice(0, eq).trim(), v = trimmed.slice(eq + 1).trim();
34
- if (!k)
35
- continue;
36
- try {
37
- out[k] = decodeURIComponent(v);
38
- } catch {
39
- out[k] = v;
40
- }
41
- }
42
- return out;
43
- }
44
- export function resolveUserPartialsPath(cwd = process.cwd(), configuredDir) {
45
- if (configuredDir) {
46
- const configured = [
47
- join(cwd, "resources", configuredDir),
48
- join(cwd, configuredDir)
49
- ].find((candidate) => existsSync(candidate));
50
- if (configured)
51
- return configured;
52
- }
53
- const existing = [
54
- "resources/partials",
55
- "resources/views/partials",
56
- "partials",
57
- "resources/components"
58
- ].filter((candidate) => existsSync(join(cwd, candidate)));
59
- if (existing.length === 0)
60
- return;
61
- const populated = existing.find((candidate) => containsTemplates(join(cwd, candidate)));
62
- return join(cwd, populated ?? existing[0]);
63
- }
64
- function containsTemplates(dir) {
65
- try {
66
- return [...new Bun.Glob("**/*.stx").scanSync({ cwd: dir, onlyFiles: !0 })].length > 0;
67
- } catch {
68
- return !1;
69
- }
70
- }
71
- export async function loadStxPartialsDir(cwd = process.cwd()) {
72
- const configPath = join(cwd, "config/stx.ts");
73
- if (!existsSync(configPath))
74
- return;
75
- try {
76
- const dir = (await import(configPath)).default?.partialsDir;
77
- return typeof dir === "string" && dir.length > 0 ? dir : void 0;
78
- } catch {
79
- return;
80
- }
81
- }
82
- function resolveCsrfMiddlewarePath() {
83
- const rel = "app/Middleware/Csrf.ts", vendored = join(process.cwd(), "storage/framework/defaults", rel);
84
- if (existsSync(vendored))
85
- return vendored;
86
- try {
87
- const pkgJson = Bun.resolveSync("@stacksjs/defaults/package.json", process.cwd());
88
- return `${pkgJson.slice(0, pkgJson.lastIndexOf("/"))}/${rel}`;
89
- } catch {
90
- return vendored;
91
- }
92
- }
93
- export async function startProductionServer(options) {
94
- if (options?.port)
95
- process.env.PORT = String(options.port);
96
- process.env.APP_ENV = process.env.APP_ENV || "production";
97
- const port = Number(process.env.PORT) || 3000, { config, overridesReady } = await import("@stacksjs/config");
98
- await overridesReady;
99
- const { injectGlobalAutoImports } = await import("@stacksjs/server");
100
- await injectGlobalAutoImports();
101
- let stxServe;
102
- const serveCandidates = [
103
- join(homedir(), "Code/Tools/stx/packages/bun-plugin/dist/serve.js"),
104
- join(process.cwd(), "pantry/bun-plugin-stx/dist/serve.js")
105
- ];
106
- for (const entry of serveCandidates)
107
- try {
108
- if (existsSync(entry)) {
109
- ({ serve: stxServe } = await import(entry));
110
- break;
111
- }
112
- } catch {}
113
- if (!stxServe)
114
- ({ serve: stxServe } = await import("bun-plugin-stx/serve"));
115
- const stxModule = await resolveVendoredStxModule(), { site: siteConfig, i18n: i18nConfig } = await loadStxSiteConfig(), userViewsPath = "resources/views", defaultsResources = resolveDefaultsResources(), defaultViewsPath = join(defaultsResources, "views"), userLayoutsPath = existsSync("resources/views/layouts") ? "resources/views/layouts" : "resources/layouts", userPartialsPath = resolveUserPartialsPath(process.cwd(), await loadStxPartialsDir()), apiBase = process.env.API_URL || `http://127.0.0.1:${Number(process.env.PORT_API) || config.ports?.api || 3008}`;
116
- log.info(`Starting production server on port ${port}...`);
117
- await stxServe({
118
- patterns: [userViewsPath, defaultViewsPath],
119
- port,
120
- autoIncrementPort: !1,
121
- reusePort: ["production", "staging", "development"].includes((process.env.APP_ENV || "").toLowerCase()),
122
- componentsDir: join(defaultsResources, "components"),
123
- layoutsDir: userLayoutsPath,
124
- ...userPartialsPath && { partialsDir: userPartialsPath },
125
- fallbackLayoutsDir: join(defaultsResources, "layouts"),
126
- fallbackPartialsDir: defaultViewsPath,
127
- quiet: options?.verbose !== !0,
128
- ...stxModule && { stxModule },
129
- ...i18nConfig && { i18n: i18nConfig },
130
- ...siteConfig?.url && { site: siteConfig },
131
- onRequest: async (req) => {
132
- const { maintenanceGate, isApiBoundRequest, proxyToBackend } = await import("@stacksjs/server"), gated = await maintenanceGate(req);
133
- if (gated)
134
- return gated;
135
- const url = new URL(req.url);
136
- if (isApiBoundRequest(req, url.pathname))
137
- try {
138
- return await proxyToBackend(req, apiBase);
139
- } catch (error) {
140
- log.error(`API proxy to ${apiBase} failed: ${error.message}`);
141
- return new Response("Bad Gateway", { status: 502 });
142
- }
143
- if (existsSync(join(process.cwd(), "resources/views/blog.stx"))) {
144
- const { renderBlogFeed } = await import("@stacksjs/actions/blog"), feed = await renderBlogFeed(req);
145
- if (feed)
146
- return feed;
147
- }
148
- globalThis.__stxServeSearch = url.search;
149
- globalThis.__stxServeCookies = parseCookies(req);
150
- return;
151
- },
152
- onResponse: async (req, response) => {
153
- const method = req.method.toUpperCase();
154
- if (method !== "GET" && method !== "HEAD")
155
- return;
156
- try {
157
- const { seedCsrfCookieIfMissing } = await import(resolveCsrfMiddlewarePath());
158
- return seedCsrfCookieIfMissing(req, response);
159
- } catch (error) {
160
- log.debug(`CSRF cookie seeding skipped: ${error.message}`);
161
- }
162
- }
163
- });
164
- log.success(`Production server listening on http://0.0.0.0:${port}`);
165
- }
5
+ import { ExitCode } from "@stacksjs/types";
6
+ import { startProductionServer } from "../production-server";
7
+ export {
8
+ loadStxPartialsDir,
9
+ resolveUserPartialsPath,
10
+ startProductionServer
11
+ } from "../production-server";
166
12
  export function serve(buddy) {
167
13
  buddy.command("serve", "Start the production HTTP server (STX views + /api proxy + coming-soon/maintenance gate)").option("-p, --port <port>", "Port to listen on (defaults to PORT env or 3000)").option("--verbose", "Enable verbose output", { default: !1 }).action(startProductionServer);
168
14
  }
@@ -180,84 +26,6 @@ function resolveApiEntry() {
180
26
  return vendored;
181
27
  return "@stacksjs/actions/serve/api";
182
28
  }
183
- function resolveDefaultsResources() {
184
- const vendored = "storage/framework/defaults/resources";
185
- if (existsSync(vendored))
186
- return vendored;
187
- try {
188
- const pkgJson = Bun.resolveSync("@stacksjs/defaults/package.json", process.cwd());
189
- return join(dirname(pkgJson), "resources");
190
- } catch {
191
- return vendored;
192
- }
193
- }
194
- async function resolveVendoredStxModule() {
195
- const candidates = [
196
- join(homedir(), "Code/Tools/stx/packages/stx/dist/index.js"),
197
- join(process.cwd(), "pantry/@stacksjs/stx/dist/index.js")
198
- ];
199
- for (const entry of candidates)
200
- try {
201
- if (existsSync(entry))
202
- return await import(entry);
203
- } catch {}
204
- try {
205
- return await import("@stacksjs/stx");
206
- } catch {}
207
- return;
208
- }
209
- function fallbackI18nFromSite(site) {
210
- const locales = site.i18n.locales, defaultLocale = site.i18n.defaultLocale ?? locales[0];
211
- return {
212
- locales,
213
- defaultLocale,
214
- labels: site.i18n.labels ?? Object.fromEntries(locales.map((c) => [c, c.toUpperCase()])),
215
- translations: {},
216
- pickerSelector: site.i18n.pickerSelector ?? "#lang-picker"
217
- };
218
- }
219
- async function resolveSiteI18n(site) {
220
- const resolverPaths = [
221
- join(homedir(), "Code/Tools/stx/packages/stx/src/site-builder/i18n.ts"),
222
- join(homedir(), "Code/Tools/stx/packages/stx/dist/index.js"),
223
- join(process.cwd(), "pantry/@stacksjs/stx/dist/index.js")
224
- ];
225
- for (const resolverPath of resolverPaths)
226
- try {
227
- if (!existsSync(resolverPath))
228
- continue;
229
- const resolved = await import(resolverPath);
230
- if (typeof resolved.resolveI18n !== "function")
231
- continue;
232
- const i18n = resolved.resolveI18n(site, process.cwd());
233
- if (i18n)
234
- return i18n;
235
- } catch {}
236
- try {
237
- const resolved = await import("@stacksjs/stx");
238
- if (typeof resolved.resolveI18n === "function") {
239
- const i18n = resolved.resolveI18n(site, process.cwd());
240
- if (i18n)
241
- return i18n;
242
- }
243
- } catch {}
244
- return fallbackI18nFromSite(site);
245
- }
246
- async function loadStxSiteConfig() {
247
- const sitePath = join(process.cwd(), "site.config.ts");
248
- if (!existsSync(sitePath))
249
- return {};
250
- try {
251
- const site = (await import(sitePath)).default;
252
- if (!site)
253
- return {};
254
- if (!site.i18n)
255
- return { site };
256
- const i18n = await resolveSiteI18n(site);
257
- return { site, i18n };
258
- } catch {}
259
- return {};
260
- }
261
29
  export function preview(buddy) {
262
30
  buddy.command("preview [dir]", "Serve a static build locally, the way a static host would").option("-p, --port <port>", "Port to listen on", { default: "3001" }).example("buddy preview").example("buddy preview dist --port 4000").action(async (dir, options) => {
263
31
  const root = dir || "dist", port = Number(options?.port) || 3001;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolve the project's includes directory.
3
+ *
4
+ * A configured `config/stx.ts#partialsDir` wins outright. The convention list
5
+ * below is only a fallback for apps that never set one, and it cannot stand in
6
+ * for the config: the candidates are probed for existence in order, so an app
7
+ * that keeps its includes in `resources/components` but also has an unrelated
8
+ * `resources/partials` directory silently resolved to the wrong one and every
9
+ * `@include` failed with ENOENT at runtime.
10
+ *
11
+ * The configured value is relative to the stx root (`resources`), matching how
12
+ * stx itself reads it, but an app-root-relative path is accepted too so either
13
+ * spelling resolves.
14
+ */
15
+ export declare function resolveUserPartialsPath(cwd?: unknown, configuredDir?: string): string | undefined;
16
+ /**
17
+ * Read `partialsDir` off the app's stx config. Failure is non-fatal: without
18
+ * it {@link resolveUserPartialsPath} falls back to the conventions.
19
+ */
20
+ export declare function loadStxPartialsDir(cwd?: unknown): Promise<string | undefined>;
21
+ export declare function startProductionServer(options?: { port?: string | number, verbose?: boolean }): Promise<void>;
@@ -0,0 +1,242 @@
1
+ import { existsSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import process from "node:process";
5
+ import { log } from "@stacksjs/logging";
6
+ globalThis.requestContext = {
7
+ cookie(name) {
8
+ const snapshot = globalThis.__stxServeContext;
9
+ if (snapshot?.cookies && name in snapshot.cookies)
10
+ return snapshot.cookies[name] ?? null;
11
+ return globalThis.__stxServeCookies?.[name] ?? null;
12
+ },
13
+ url() {
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";
22
+ }
23
+ };
24
+ function parseCookies(req) {
25
+ const out = {}, header = req.headers.get("cookie") || "";
26
+ if (!header)
27
+ return out;
28
+ for (const part of header.split(";")) {
29
+ const trimmed = part.trim(), eq = trimmed.indexOf("=");
30
+ if (eq === -1)
31
+ continue;
32
+ const k = trimmed.slice(0, eq).trim(), v = trimmed.slice(eq + 1).trim();
33
+ if (!k)
34
+ continue;
35
+ try {
36
+ out[k] = decodeURIComponent(v);
37
+ } catch {
38
+ out[k] = v;
39
+ }
40
+ }
41
+ return out;
42
+ }
43
+ export function resolveUserPartialsPath(cwd = process.cwd(), configuredDir) {
44
+ if (configuredDir) {
45
+ const configured = [
46
+ join(cwd, "resources", configuredDir),
47
+ join(cwd, configuredDir)
48
+ ].find((candidate) => existsSync(candidate));
49
+ if (configured)
50
+ return configured;
51
+ }
52
+ const existing = [
53
+ "resources/partials",
54
+ "resources/views/partials",
55
+ "partials",
56
+ "resources/components"
57
+ ].filter((candidate) => existsSync(join(cwd, candidate)));
58
+ if (existing.length === 0)
59
+ return;
60
+ const populated = existing.find((candidate) => containsTemplates(join(cwd, candidate)));
61
+ return join(cwd, populated ?? existing[0]);
62
+ }
63
+ function containsTemplates(dir) {
64
+ try {
65
+ return [...new Bun.Glob("**/*.stx").scanSync({ cwd: dir, onlyFiles: !0 })].length > 0;
66
+ } catch {
67
+ return !1;
68
+ }
69
+ }
70
+ export async function loadStxPartialsDir(cwd = process.cwd()) {
71
+ const configPath = join(cwd, "config/stx.ts");
72
+ if (!existsSync(configPath))
73
+ return;
74
+ try {
75
+ const dir = (await import(configPath)).default?.partialsDir;
76
+ return typeof dir === "string" && dir.length > 0 ? dir : void 0;
77
+ } catch {
78
+ return;
79
+ }
80
+ }
81
+ function resolveCsrfMiddlewarePath() {
82
+ const rel = "app/Middleware/Csrf.ts", vendored = join(process.cwd(), "storage/framework/defaults", rel);
83
+ if (existsSync(vendored))
84
+ return vendored;
85
+ try {
86
+ const pkgJson = Bun.resolveSync("@stacksjs/defaults/package.json", process.cwd());
87
+ return `${pkgJson.slice(0, pkgJson.lastIndexOf("/"))}/${rel}`;
88
+ } catch {
89
+ return vendored;
90
+ }
91
+ }
92
+ export async function startProductionServer(options) {
93
+ if (options?.port)
94
+ process.env.PORT = String(options.port);
95
+ process.env.APP_ENV = process.env.APP_ENV || "production";
96
+ const port = Number(process.env.PORT) || 3000, { config, overridesReady } = await import("@stacksjs/config");
97
+ await overridesReady;
98
+ const { injectGlobalAutoImports } = await import("@stacksjs/server");
99
+ await injectGlobalAutoImports();
100
+ let stxServe;
101
+ const serveCandidates = [
102
+ join(homedir(), "Code/Tools/stx/packages/bun-plugin/dist/serve.js"),
103
+ join(process.cwd(), "pantry/bun-plugin-stx/dist/serve.js")
104
+ ];
105
+ for (const entry of serveCandidates)
106
+ try {
107
+ if (existsSync(entry)) {
108
+ ({ serve: stxServe } = await import(entry));
109
+ break;
110
+ }
111
+ } catch {}
112
+ if (!stxServe)
113
+ ({ serve: stxServe } = await import("bun-plugin-stx/serve"));
114
+ const stxModule = await resolveVendoredStxModule(), { site: siteConfig, i18n: i18nConfig } = await loadStxSiteConfig(), userViewsPath = "resources/views", defaultsResources = resolveDefaultsResources(), defaultViewsPath = join(defaultsResources, "views"), userLayoutsPath = existsSync("resources/views/layouts") ? "resources/views/layouts" : "resources/layouts", userPartialsPath = resolveUserPartialsPath(process.cwd(), await loadStxPartialsDir()), apiBase = process.env.API_URL || `http://127.0.0.1:${Number(process.env.PORT_API) || config.ports?.api || 3008}`;
115
+ log.info(`Starting production server on port ${port}...`);
116
+ await stxServe({
117
+ patterns: [userViewsPath, defaultViewsPath],
118
+ port,
119
+ autoIncrementPort: !1,
120
+ reusePort: ["production", "staging", "development"].includes((process.env.APP_ENV || "").toLowerCase()),
121
+ componentsDir: join(defaultsResources, "components"),
122
+ layoutsDir: userLayoutsPath,
123
+ ...userPartialsPath && { partialsDir: userPartialsPath },
124
+ fallbackLayoutsDir: join(defaultsResources, "layouts"),
125
+ fallbackPartialsDir: defaultViewsPath,
126
+ quiet: options?.verbose !== !0,
127
+ ...stxModule && { stxModule },
128
+ ...i18nConfig && { i18n: i18nConfig },
129
+ ...siteConfig?.url && { site: siteConfig },
130
+ onRequest: async (req) => {
131
+ const { maintenanceGate, isApiBoundRequest, proxyToBackend } = await import("@stacksjs/server"), gated = await maintenanceGate(req);
132
+ if (gated)
133
+ return gated;
134
+ const url = new URL(req.url);
135
+ if (isApiBoundRequest(req, url.pathname))
136
+ try {
137
+ return await proxyToBackend(req, apiBase);
138
+ } catch (error) {
139
+ log.error(`API proxy to ${apiBase} failed: ${error.message}`);
140
+ return new Response("Bad Gateway", { status: 502 });
141
+ }
142
+ if (existsSync(join(process.cwd(), "resources/views/blog.stx"))) {
143
+ const { renderBlogFeed } = await import("@stacksjs/actions/blog"), feed = await renderBlogFeed(req);
144
+ if (feed)
145
+ return feed;
146
+ }
147
+ globalThis.__stxServeSearch = url.search;
148
+ globalThis.__stxServeCookies = parseCookies(req);
149
+ return;
150
+ },
151
+ onResponse: async (req, response) => {
152
+ const method = req.method.toUpperCase();
153
+ if (method !== "GET" && method !== "HEAD")
154
+ return;
155
+ try {
156
+ const { seedCsrfCookieIfMissing } = await import(resolveCsrfMiddlewarePath());
157
+ return seedCsrfCookieIfMissing(req, response);
158
+ } catch (error) {
159
+ log.debug(`CSRF cookie seeding skipped: ${error.message}`);
160
+ }
161
+ }
162
+ });
163
+ log.success(`Production server listening on http://0.0.0.0:${port}`);
164
+ }
165
+ function resolveDefaultsResources() {
166
+ const vendored = "storage/framework/defaults/resources";
167
+ if (existsSync(vendored))
168
+ return vendored;
169
+ try {
170
+ const pkgJson = Bun.resolveSync("@stacksjs/defaults/package.json", process.cwd());
171
+ return join(dirname(pkgJson), "resources");
172
+ } catch {
173
+ return vendored;
174
+ }
175
+ }
176
+ async function resolveVendoredStxModule() {
177
+ const candidates = [
178
+ join(homedir(), "Code/Tools/stx/packages/stx/dist/index.js"),
179
+ join(process.cwd(), "pantry/@stacksjs/stx/dist/index.js")
180
+ ];
181
+ for (const entry of candidates)
182
+ try {
183
+ if (existsSync(entry))
184
+ return await import(entry);
185
+ } catch {}
186
+ try {
187
+ return await import("@stacksjs/stx");
188
+ } catch {}
189
+ return;
190
+ }
191
+ function fallbackI18nFromSite(site) {
192
+ const locales = site.i18n.locales, defaultLocale = site.i18n.defaultLocale ?? locales[0];
193
+ return {
194
+ locales,
195
+ defaultLocale,
196
+ labels: site.i18n.labels ?? Object.fromEntries(locales.map((c) => [c, c.toUpperCase()])),
197
+ translations: {},
198
+ pickerSelector: site.i18n.pickerSelector ?? "#lang-picker"
199
+ };
200
+ }
201
+ async function resolveSiteI18n(site) {
202
+ const resolverPaths = [
203
+ join(homedir(), "Code/Tools/stx/packages/stx/src/site-builder/i18n.ts"),
204
+ join(homedir(), "Code/Tools/stx/packages/stx/dist/index.js"),
205
+ join(process.cwd(), "pantry/@stacksjs/stx/dist/index.js")
206
+ ];
207
+ for (const resolverPath of resolverPaths)
208
+ try {
209
+ if (!existsSync(resolverPath))
210
+ continue;
211
+ const resolved = await import(resolverPath);
212
+ if (typeof resolved.resolveI18n !== "function")
213
+ continue;
214
+ const i18n = resolved.resolveI18n(site, process.cwd());
215
+ if (i18n)
216
+ return i18n;
217
+ } catch {}
218
+ try {
219
+ const resolved = await import("@stacksjs/stx");
220
+ if (typeof resolved.resolveI18n === "function") {
221
+ const i18n = resolved.resolveI18n(site, process.cwd());
222
+ if (i18n)
223
+ return i18n;
224
+ }
225
+ } catch {}
226
+ return fallbackI18nFromSite(site);
227
+ }
228
+ async function loadStxSiteConfig() {
229
+ const sitePath = join(process.cwd(), "site.config.ts");
230
+ if (!existsSync(sitePath))
231
+ return {};
232
+ try {
233
+ const site = (await import(sitePath)).default;
234
+ if (!site)
235
+ return {};
236
+ if (!site.i18n)
237
+ return { site };
238
+ const i18n = await resolveSiteI18n(site);
239
+ return { site, i18n };
240
+ } catch {}
241
+ return {};
242
+ }
@@ -1,5 +1,5 @@
1
1
  import process from "node:process";
2
- import { startProductionServer } from "./commands/serve";
2
+ import { startProductionServer } from "./production-server";
3
3
  process.env.APP_ENV ||= "production";
4
4
  process.env.NODE_ENV ||= "production";
5
5
  await startProductionServer();
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.225",
5
+ "version": "0.70.226",
6
6
  "description": "Meet Buddy. The Stacks runtime.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [