@stacksjs/buddy 0.70.256 → 0.70.258

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.
@@ -43,6 +43,74 @@ export declare function resolveDeployEnvValues(environment: 'production' | 'stag
43
43
  * the process env).
44
44
  */
45
45
  export declare function mergeSiteDeployEnv(sites: Record<string, any>, resolvedDeployEnv: Record<string, string>): Record<string, any>;
46
+ /**
47
+ * The SQLite file a site will open, relative to its release root — or `null`
48
+ * when the site is not on SQLite, or its database already lives outside the
49
+ * release tree (an absolute `DB_DATABASE_PATH` is the operator saying "I have
50
+ * placed this somewhere persistent myself").
51
+ *
52
+ * Mirrors config/database.ts' resolution order. `DB_DATABASE` is only a path
53
+ * when it looks like one — for every other driver it is a database NAME, and
54
+ * linking `stacks` as a file would be nonsense.
55
+ */
56
+ export declare function siteSqlitePath(siteEnv?: Record<string, any>): string | null;
57
+ /**
58
+ * Does the loaded ts-cloud actually keep persistent state the way
59
+ * {@link applyPersistentStatePaths} assumes?
60
+ *
61
+ * Declaring shared paths against a ts-cloud that lacks these is worse than not
62
+ * declaring them at all — it is the difference between "the database dies with
63
+ * its release" and "the deploy places an empty database over the live one and
64
+ * migrates into it". So this probes the two capabilities the declaration
65
+ * depends on and lets the caller refuse the deploy.
66
+ *
67
+ * Probed by generating a script and reading what it DOES, not by a version
68
+ * number or an exported symbol name: a version range goes stale the moment
69
+ * someone installs a fork, and an internal helper can be renamed without
70
+ * changing behaviour.
71
+ *
72
+ * @param buildSiteDeployScript ts-cloud's script builder, as loaded at runtime.
73
+ */
74
+ export declare function tsCloudPersistentStateSupport(buildSiteDeployScript: unknown): { ok: boolean, missing: string[] };
75
+ /**
76
+ * Where a project's app database lives on the box: one directory per project,
77
+ * outside every site's release tree.
78
+ *
79
+ * Deliberately derived from the project slug ALONE. ts-cloud installs each site
80
+ * under `/var/www/<slug>-<site>`, so anything derived from the site name gives
81
+ * each site a database of its own — which is exactly how `main` and `api` ended
82
+ * up on separate files, with only `main` ever migrated. Two sites of one project
83
+ * cannot disagree about a path that does not mention which site is asking.
84
+ */
85
+ export declare function projectDatabaseTarget(slug: string, relativePath: string): string;
86
+ /**
87
+ * Declare the paths a Stacks server-app WRITES at runtime as ts-cloud shared
88
+ * paths, so they are symlinked into each release instead of dying with it.
89
+ *
90
+ * Two separate failures are closed here.
91
+ *
92
+ * **A release owns the database.** `releases/<sha>/database/stacks.sqlite` was a
93
+ * real file inside the release the deploy had just unpacked: `migrate` (in
94
+ * preStart) built a brand new empty database, the release went live against it,
95
+ * and every production row written since the previous deploy was gone the moment
96
+ * the old release was pruned — silently, with nothing in the deploy output to
97
+ * say so. Verified on a live box: a sentinel row was gone after the next deploy.
98
+ *
99
+ * **A site owns the database.** Each site installs under its own base, so `main`
100
+ * and `api` both opened `database/stacks.sqlite` relative to their OWN release
101
+ * — two files, only one of them ever migrated. The database is therefore pointed
102
+ * at a project-level target ({@link projectDatabaseTarget}) that no site's name
103
+ * appears in, so siblings share one file by construction. Only the site that
104
+ * runs `migrate` may create or seed it; the rest link at it (`seed: false`), so
105
+ * deploy order cannot decide whose data wins.
106
+ *
107
+ * Logs stay site-scoped — `main` and `api` are different services and their logs
108
+ * should not interleave.
109
+ *
110
+ * Additive: a site's own `sharedPaths` in config/cloud.ts always survive, so an
111
+ * app can persist its own state (upload dirs, generated assets) the same way.
112
+ */
113
+ export declare function applyPersistentStatePaths(sites: Record<string, any>, slug: string): Record<string, any>;
46
114
  /**
47
115
  * Make the site model environment-aware. For a non-production environment that
48
116
  * declares a `domainPrefix` (staging → `staging`, development → `dev`), every
@@ -54,10 +122,16 @@ export declare function mergeSiteDeployEnv(sites: Record<string, any>, resolvedD
54
122
  */
55
123
  export declare function applyEnvironmentToSites(sites: Record<string, any>, environment: string, config: any): Record<string, any>;
56
124
  /**
57
- * Load the ts-cloud driver used by Buddy. Published installs use the declared
58
- * package dependency. Framework and ts-cloud development can point at a built
59
- * local module with TS_CLOUD_MODULE, avoiding stale nested dependency copies
60
- * while still exercising the exact Buddy deploy path.
125
+ * Resolve the app's direct ts-cloud dependency before Buddy's own dependency.
126
+ * Package managers may retain a stale nested copy below Buddy even after the
127
+ * app explicitly updates ts-cloud; resolving from the project manifest keeps
128
+ * the deploy engine selected by the application authoritative.
129
+ */
130
+ export declare function resolveProjectTsCloudModule(projectRoot?: unknown): string | undefined;
131
+ /**
132
+ * Load the ts-cloud driver used by Buddy. An explicit TS_CLOUD_MODULE remains
133
+ * available for framework development; published apps otherwise use their
134
+ * direct dependency before falling back to Buddy's declared dependency.
61
135
  */
62
136
  export declare function loadTsCloudDeployApi(): Promise<typeof import('@stacksjs/ts-cloud')>;
63
137
  /**
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { isAbsolute, join, relative, resolve } from "node:path";
3
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
4
4
  import process from "node:process";
5
5
  import { pathToFileURL } from "node:url";
6
6
  import { runAction } from "@stacksjs/actions";
@@ -479,6 +479,74 @@ export function mergeSiteDeployEnv(sites, resolvedDeployEnv) {
479
479
  return [siteName, { ...site, env: { ...base, ...site.env || {} } }];
480
480
  }));
481
481
  }
482
+ function looksLikeSqliteFile(value) {
483
+ return typeof value === "string" && /\.(?:sqlite3?|db)$/i.test(value.trim());
484
+ }
485
+ export function siteSqlitePath(siteEnv = {}) {
486
+ if (String(siteEnv.DB_CONNECTION ?? "sqlite").trim().toLowerCase() !== "sqlite")
487
+ return null;
488
+ const configured = typeof siteEnv.DB_DATABASE_PATH === "string" && siteEnv.DB_DATABASE_PATH.trim() ? siteEnv.DB_DATABASE_PATH : looksLikeSqliteFile(siteEnv.DB_DATABASE) ? siteEnv.DB_DATABASE : "database/stacks.sqlite", path = String(configured).trim().replace(/^\.\//, "");
489
+ if (!path || path.startsWith("/") || path.startsWith("~") || path.split("/").includes(".."))
490
+ return null;
491
+ return path;
492
+ }
493
+ export function tsCloudPersistentStateSupport(buildSiteDeployScript) {
494
+ const missing = [];
495
+ if (typeof buildSiteDeployScript !== "function")
496
+ return { ok: !1, missing: ["buildSiteDeployScript"] };
497
+ const target = "/var/www/probe-shared/database/probe.sqlite";
498
+ let script = "";
499
+ try {
500
+ script = buildSiteDeployScript({
501
+ siteName: "probe",
502
+ slug: "probe",
503
+ appDir: "/var/www/probe-probe",
504
+ artifactFetch: [],
505
+ releaseId: "probe",
506
+ execStart: "/bin/true",
507
+ envEntries: {},
508
+ port: 3000,
509
+ sharedPaths: [{ path: "database/probe.sqlite", target, seed: !0 }]
510
+ }).join(`
511
+ `);
512
+ } catch {
513
+ return { ok: !1, missing: ["shared paths with an explicit target"] };
514
+ }
515
+ if (!(script.includes("cp -a") && script.includes("/current/")))
516
+ missing.push("adoption of existing state into shared/");
517
+ if (!script.includes(`ln -sfn ${target} `))
518
+ missing.push("shared paths with an explicit target");
519
+ return { ok: missing.length === 0, missing };
520
+ }
521
+ export function projectDatabaseTarget(slug, relativePath) {
522
+ return `/var/www/${slug}-shared/${relativePath}`;
523
+ }
524
+ function runsMigrations(site) {
525
+ return Array.isArray(site?.preStart) && site.preStart.some((cmd) => typeof cmd === "string" && /\bmigrate\b/.test(cmd));
526
+ }
527
+ export function applyPersistentStatePaths(sites, slug) {
528
+ const isServerApp = (site) => !!site && typeof site.start === "string", appSites = Object.entries(sites).filter(([, site]) => isServerApp(site)), owner = (appSites.find(([, site]) => runsMigrations(site)) ?? appSites[0])?.[0], out = {};
529
+ for (const [name, site] of Object.entries(sites)) {
530
+ if (!isServerApp(site)) {
531
+ out[name] = site;
532
+ continue;
533
+ }
534
+ const sqlite = siteSqlitePath(site.env || {}), framework = [
535
+ "storage/logs"
536
+ ];
537
+ if (sqlite)
538
+ framework.unshift({
539
+ path: sqlite,
540
+ target: projectDatabaseTarget(slug, sqlite),
541
+ seed: name === owner
542
+ });
543
+ const declared = Array.isArray(site.sharedPaths) ? site.sharedPaths.filter(Boolean) : [], byPath = new Map;
544
+ for (const entry of [...declared, ...framework])
545
+ byPath.set(typeof entry === "string" ? entry : entry.path, entry);
546
+ out[name] = { ...site, sharedPaths: [...byPath.values()] };
547
+ }
548
+ return out;
549
+ }
482
550
  function prefixHostForEnv(host, prefix) {
483
551
  if (host.startsWith(`${prefix}.`) || host.startsWith(`www.${prefix}.`))
484
552
  return host;
@@ -546,7 +614,13 @@ async function deployToHetzner(tsCloudConfig, deployEnv, options) {
546
614
  log.info("Generate one with: ssh-keygen -t ed25519");
547
615
  process.exit(ExitCode.FatalError);
548
616
  }
549
- const { createCloudDriver, deployAllComputeSites, ensureManagementDashboard, resolveSiteKind } = await loadTsCloudDeployApi();
617
+ const { createCloudDriver, deployAllComputeSites, ensureManagementDashboard, resolveSiteKind, buildSiteDeployScript } = await loadTsCloudDeployApi(), support = tsCloudPersistentStateSupport(buildSiteDeployScript);
618
+ if (!support.ok) {
619
+ log.error("This ts-cloud cannot keep your database across deploys, and deploying anyway would destroy it.");
620
+ log.error(`Missing: ${support.missing.join(", ")}.`);
621
+ log.error("Upgrade @stacksjs/ts-cloud (bun update @stacksjs/ts-cloud), or point TS_CLOUD_MODULE at a build that has it.");
622
+ process.exit(ExitCode.FatalError);
623
+ }
550
624
  try {
551
625
  await runHetznerDeploy({ tsCloudConfig, environment, verbose, docker: options.docker === !0, createCloudDriver, deployAllComputeSites, ensureManagementDashboard, resolveSiteKind, onlySite: options.site || void 0, persistedAttachBox });
552
626
  } catch (err) {
@@ -555,10 +629,22 @@ async function deployToHetzner(tsCloudConfig, deployEnv, options) {
555
629
  process.exit(ExitCode.FatalError);
556
630
  }
557
631
  }
632
+ export function resolveProjectTsCloudModule(projectRoot = process.cwd()) {
633
+ const manifestPath = join(projectRoot, "node_modules", "@stacksjs", "ts-cloud", "package.json");
634
+ if (!existsSync(manifestPath))
635
+ return;
636
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8")), rootExport = manifest.exports?.["."], entry = manifest.module ?? (typeof rootExport === "string" ? rootExport : rootExport?.import);
637
+ if (!entry)
638
+ return;
639
+ const modulePath = resolve(dirname(manifestPath), entry);
640
+ return existsSync(modulePath) ? modulePath : void 0;
641
+ }
558
642
  export async function loadTsCloudDeployApi() {
559
643
  const requested = process.env.TS_CLOUD_MODULE?.trim();
560
- if (!requested)
561
- return import("@stacksjs/ts-cloud");
644
+ if (!requested) {
645
+ const projectModule = resolveProjectTsCloudModule();
646
+ return projectModule ? import(pathToFileURL(projectModule).href) : import("@stacksjs/ts-cloud");
647
+ }
562
648
  const modulePath = isAbsolute(requested) ? requested : resolve(process.cwd(), requested);
563
649
  if (!existsSync(modulePath))
564
650
  throw Error(`TS_CLOUD_MODULE points to a missing module: ${modulePath}`);
@@ -865,7 +951,7 @@ async function runHetznerDeploy(args) {
865
951
  }
866
952
  if (docker)
867
953
  await buildContainerImageWithPantry({ slug, sites, verbose });
868
- const resolvedDeployEnv = await resolveDeployEnvValues(environment, tsCloudConfig), sitesWithResolvedEnv = mergeSiteDeployEnv(sites, resolvedDeployEnv);
954
+ const resolvedDeployEnv = await resolveDeployEnvValues(environment, tsCloudConfig), sitesWithResolvedEnv = applyPersistentStatePaths(mergeSiteDeployEnv(sites, resolvedDeployEnv), slug);
869
955
  for (const [envKey, envValue] of Object.entries(resolvedDeployEnv))
870
956
  if (process.env[envKey] === void 0)
871
957
  process.env[envKey] = envValue;
@@ -60,6 +60,20 @@ export function generate(buddy) {
60
60
  log.warn(`[generate:db-types] ${e.file}: ${e.error}`);
61
61
  log.info(`[generate:db-types] resolved ${result.tables.length} table(s)`);
62
62
  });
63
+ buddy.command("generate:vschema", "Derive a Vitess VSchema from your models (writes database/vschema.json)").option("--dry-run", "Print the VSchema without writing it", { default: !1 }).option("--out [path]", "Where to write the VSchema", { default: "database/vschema.json" }).action(async (options) => {
64
+ const { generateVSchema } = await import("@stacksjs/actions"), result = await generateVSchema({ dryRun: options.dryRun, out: options.out });
65
+ if (!result.ok) {
66
+ console.error(`
67
+ \u274C ${result.error}
68
+ `);
69
+ process.exit(ExitCode.FatalError);
70
+ }
71
+ console.log(result.report);
72
+ if (options.dryRun)
73
+ console.log(JSON.stringify(result.vschema, null, 2));
74
+ else
75
+ log.success(`Wrote ${result.path} (${result.tableCount} tables)`);
76
+ });
63
77
  buddy.command("generate:entries", descriptions.entries).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
64
78
  log.debug("Running `buddy generate:entries` ...", options);
65
79
  await generateLibEntries(options);
@@ -8,6 +8,39 @@ async function runStyleAction(entry, label, options) {
8
8
  process.exit(ExitCode.FatalError);
9
9
  }
10
10
  }
11
+ async function runStxChecks(startTime) {
12
+ const { runStxLint } = await import("@stacksjs/actions"), report = await runStxLint(), out = [""];
13
+ for (const r of report.results)
14
+ if (r.status === "fail") {
15
+ out.push(` FAIL ${r.label}`);
16
+ out.push(` ${r.count} found, baseline ${r.baseline}${r.why ? ` (${r.why})` : ""}`);
17
+ for (const line of r.detail.slice(0, 8))
18
+ out.push(` ${line}`);
19
+ } else if (r.status === "loosened")
20
+ out.push(` DROP ${r.label}: ${r.count} < baseline ${r.baseline} \u2014 lower it in config/lint.ts`);
21
+ else
22
+ out.push(` ok ${r.label}${r.baseline > 0 ? ` (${r.count}, held)` : ""}`);
23
+ if (report.distMissing)
24
+ out.push("", " note: no build output found \u2014 the dist checks did not run. Run `./buddy build` first.");
25
+ if (report.loosened > 0) {
26
+ out.push("", " Current counts, for config/lint.ts:");
27
+ for (const [id, count] of Object.entries(report.counts).sort(([a], [b]) => a.localeCompare(b)))
28
+ out.push(` '${id}': ${count},`);
29
+ }
30
+ console.log(out.join(`
31
+ `));
32
+ if (report.failed > 0) {
33
+ log.error(`${report.failed} stx check(s) failed.`);
34
+ await outro("stx checks failed", { startTime, useSeconds: !0 });
35
+ process.exit(ExitCode.FatalError);
36
+ }
37
+ if (report.loosened > 0) {
38
+ log.warn(`No regressions, but ${report.loosened} baseline(s) are now stale.`);
39
+ await outro("stx baselines stale", { startTime, useSeconds: !0 });
40
+ process.exit(ExitCode.FatalError);
41
+ }
42
+ await outro("All stx checks pass", { startTime, useSeconds: !0 });
43
+ }
11
44
  export function lint(buddy) {
12
45
  const descriptions = {
13
46
  lint: "Automagically lints your project codebase",
@@ -15,11 +48,16 @@ export function lint(buddy) {
15
48
  format: "Format your project codebase",
16
49
  formatCheck: "Check formatting without making changes",
17
50
  project: "Target a specific project",
51
+ stx: "Run the stx conformance checks instead of code style",
18
52
  verbose: "Enable verbose output"
19
53
  };
20
- buddy.command("lint", descriptions.lint).option("-f, --fix", descriptions.lintFix, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
54
+ buddy.command("lint", descriptions.lint).option("-f, --fix", descriptions.lintFix, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--stx", descriptions.stx, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
21
55
  log.debug("Running `buddy lint` ...", options);
22
56
  const startTime = await intro("buddy lint");
57
+ if (options.stx) {
58
+ await runStxChecks(startTime);
59
+ return;
60
+ }
23
61
  await runStyleAction(options.fix ? "lintFix" : "lintProject", "lint");
24
62
  await outro("Linted your project", { startTime, useSeconds: !0 });
25
63
  });
@@ -6,7 +6,7 @@ import { hasTTY, isCI } from "@stacksjs/env";
6
6
  import { appPath, frameworkPath, frameworkRuntimePath, projectPath } from "@stacksjs/path";
7
7
  import { ExitCode } from "@stacksjs/types";
8
8
  import { preflightDatabase } from "../database-preflight";
9
- import { DIALECT_OVERRIDE_ENV, auditMigrationCorpus, formatMigrationDialectError, stripSqlNoise } from "@stacksjs/database";
9
+ import { DDL_CONSTRAINT_OVERRIDE_ENV, DIALECT_OVERRIDE_ENV, auditDdlConstraints, auditMigrationCorpus, dialectCapabilities, formatDdlConstraintError, formatMigrationDialectError, stripSqlNoise } from "@stacksjs/database";
10
10
  let _runAction;
11
11
  async function runAction(...args) {
12
12
  if (!_runAction)
@@ -85,15 +85,21 @@ function validateModelsExist() {
85
85
  return { valid: !0 };
86
86
  }
87
87
  function validateMigrationDialect() {
88
- if (process.env[DIALECT_OVERRIDE_ENV] === "1")
89
- return { valid: !0 };
90
- const driver = String(process.env.DB_CONNECTION || "sqlite").toLowerCase(), target = driver === "singlestore" ? "mysql" : driver;
91
- if (target !== "sqlite" && target !== "postgres" && target !== "mysql")
92
- return { valid: !0 };
93
- const dir = projectPath("database/migrations"), audit = auditMigrationCorpus({ dir, target });
94
- if (audit.empty || audit.incompatible.length === 0)
95
- return { valid: !0 };
96
- return { valid: !1, error: formatMigrationDialectError(audit, target, "database/migrations") };
88
+ const driver = String(process.env.DB_CONNECTION || "sqlite").toLowerCase(), dir = projectPath("database/migrations");
89
+ if (process.env[DIALECT_OVERRIDE_ENV] !== "1") {
90
+ const caps = dialectCapabilities(driver), target = caps.wire === "mysql" ? "mysql" : caps.wire === "postgres" ? "postgres" : "sqlite";
91
+ if (driver === "sqlite" || driver === "postgres" || caps.wire === "mysql") {
92
+ const audit = auditMigrationCorpus({ dir, target });
93
+ if (!audit.empty && audit.incompatible.length > 0)
94
+ return { valid: !1, error: formatMigrationDialectError(audit, target, "database/migrations") };
95
+ }
96
+ }
97
+ if (process.env[DDL_CONSTRAINT_OVERRIDE_ENV] !== "1") {
98
+ const constraints = auditDdlConstraints({ dir, dialect: driver });
99
+ if (!constraints.empty && constraints.violations.length > 0)
100
+ return { valid: !1, error: formatDdlConstraintError(constraints, driver, "database/migrations") };
101
+ }
102
+ return { valid: !0 };
97
103
  }
98
104
  async function reportMissingForeignKeys() {
99
105
  try {
@@ -289,14 +295,20 @@ export function migrate(buddy) {
289
295
  log.error("Model migrations failed \u2014 applying notification/RBAC table guarantees before exiting.");
290
296
  if (options.auth !== !1)
291
297
  try {
292
- const { migrateNotificationTables, migrateRbacTables } = await import("@stacksjs/database"), notifResult = await migrateNotificationTables({ verbose: options.verbose });
298
+ const { ensureUtcDatetimeColumns, migrateNotificationTables, migrateRbacTables, migrateTraitTables } = await import("@stacksjs/database"), notifResult = await migrateNotificationTables({ verbose: options.verbose });
293
299
  if (!notifResult.success)
294
300
  log.error(`Failed to migrate notification tables: ${notifResult.error}`);
295
301
  const rbacResult = await migrateRbacTables({ verbose: options.verbose });
296
302
  if (!rbacResult.success)
297
303
  log.error(`Failed to migrate RBAC tables: ${rbacResult.error}`);
304
+ const traitResult = await migrateTraitTables({ verbose: options.verbose });
305
+ if (!traitResult.success)
306
+ log.error(`Failed to migrate polymorphic trait tables: ${traitResult.error}`);
307
+ const datetimeResult = await ensureUtcDatetimeColumns({ verbose: options.verbose });
308
+ if (!datetimeResult.success)
309
+ log.error(`Failed to convert TIMESTAMP columns to DATETIME: ${datetimeResult.error}`);
298
310
  } catch (error) {
299
- log.error("Failed to migrate notification/RBAC tables:", error);
311
+ log.error("Failed to migrate notification/RBAC/trait tables:", error);
300
312
  }
301
313
  if (result.isErr) {
302
314
  await outro("While running the migrate command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
@@ -371,7 +383,7 @@ export function migrate(buddy) {
371
383
  if (options.auth !== !1) {
372
384
  log.debug("Migrating auth tables...");
373
385
  try {
374
- const { migrateAuthTables, migrateNotificationTables, migrateRbacTables } = await import("@stacksjs/database"), authResult = await migrateAuthTables({ verbose: options.verbose });
386
+ const { migrateAuthTables, migrateNotificationTables, migrateRbacTables, migrateTraitTables } = await import("@stacksjs/database"), authResult = await migrateAuthTables({ verbose: options.verbose });
375
387
  if (!authResult.success)
376
388
  log.error(`Failed to migrate auth tables: ${authResult.error}`);
377
389
  const notifResult = await migrateNotificationTables({ verbose: options.verbose });
@@ -380,8 +392,11 @@ export function migrate(buddy) {
380
392
  const rbacResult = await migrateRbacTables({ verbose: options.verbose });
381
393
  if (!rbacResult.success)
382
394
  log.error(`Failed to migrate RBAC tables: ${rbacResult.error}`);
395
+ const traitResult = await migrateTraitTables({ verbose: options.verbose });
396
+ if (!traitResult.success)
397
+ log.error(`Failed to migrate polymorphic trait tables: ${traitResult.error}`);
383
398
  } catch (error) {
384
- log.error("Failed to migrate auth/notification/RBAC tables:", error);
399
+ log.error("Failed to migrate auth/notification/RBAC/trait tables:", error);
385
400
  }
386
401
  }
387
402
  try {
@@ -1,35 +1 @@
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
- /**` proxy target, or `null` when it cannot be
22
- * known safely.
23
- *
24
- * `API_URL` and `PORT_API` are explicit operator intent and always win. The
25
- * framework-wide `ports.api` default is only trusted *outside* deployed
26
- * environments: locally one app owns the machine, so `127.0.0.1:3008` really is
27
- * its own API. On a deployed box that assumption does not hold — ts-cloud runs
28
- * many SSR sites side by side, each on its own port, and an unconfigured
29
- * default resolves to whichever tenant happens to own it.
30
- *
31
- * Returning `null` makes the caller answer 502. That is the safe failure: the
32
- * alternative is proxying authenticated requests into another app's process.
33
- */
34
- export declare function resolveApiBase(configuredPort?: number, env?: NodeJS.ProcessEnv): string | null;
35
- export declare function startProductionServer(options?: { port?: string | number, verbose?: boolean }): Promise<void>;
1
+ export {};
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.256",
5
+ "version": "0.70.258",
6
6
  "description": "Meet Buddy. The Stacks runtime.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -88,54 +88,54 @@
88
88
  "prepublishOnly": "bun run build"
89
89
  },
90
90
  "dependencies": {
91
- "@stacksjs/actions": "^0.70.256",
92
- "@stacksjs/ai": "^0.70.256",
93
- "@stacksjs/alias": "^0.70.256",
94
- "@stacksjs/arrays": "^0.70.256",
95
- "@stacksjs/auth": "^0.70.256",
96
- "@stacksjs/build": "^0.70.256",
97
- "@stacksjs/cache": "^0.70.256",
98
- "@stacksjs/cli": "^0.70.256",
91
+ "@stacksjs/actions": "^0.70.258",
92
+ "@stacksjs/ai": "^0.70.258",
93
+ "@stacksjs/alias": "^0.70.258",
94
+ "@stacksjs/arrays": "^0.70.258",
95
+ "@stacksjs/auth": "^0.70.258",
96
+ "@stacksjs/build": "^0.70.258",
97
+ "@stacksjs/cache": "^0.70.258",
98
+ "@stacksjs/cli": "^0.70.258",
99
99
  "@stacksjs/clapp": "^0.2.12",
100
- "@stacksjs/cloud": "^0.70.256",
101
- "@stacksjs/collections": "^0.70.256",
102
- "@stacksjs/config": "^0.70.256",
103
- "@stacksjs/database": "^0.70.256",
104
- "@stacksjs/desktop-build": "^0.70.256",
105
- "@stacksjs/dns": "^0.70.256",
106
- "@stacksjs/email": "^0.70.256",
107
- "@stacksjs/enums": "^0.70.256",
108
- "@stacksjs/error-handling": "^0.70.256",
109
- "@stacksjs/events": "^0.70.256",
110
- "@stacksjs/git": "^0.70.256",
100
+ "@stacksjs/cloud": "^0.70.258",
101
+ "@stacksjs/collections": "^0.70.258",
102
+ "@stacksjs/config": "^0.70.258",
103
+ "@stacksjs/database": "^0.70.258",
104
+ "@stacksjs/desktop-build": "^0.70.258",
105
+ "@stacksjs/dns": "^0.70.258",
106
+ "@stacksjs/email": "^0.70.258",
107
+ "@stacksjs/enums": "^0.70.258",
108
+ "@stacksjs/error-handling": "^0.70.258",
109
+ "@stacksjs/events": "^0.70.258",
110
+ "@stacksjs/git": "^0.70.258",
111
111
  "@stacksjs/gitit": "^0.2.5",
112
- "@stacksjs/health": "^0.70.256",
112
+ "@stacksjs/health": "^0.70.258",
113
113
  "@stacksjs/dnsx": "^0.2.3",
114
114
  "@stacksjs/httx": "^0.1.10",
115
- "@stacksjs/image": "^0.70.256",
116
- "@stacksjs/lint": "^0.70.256",
117
- "@stacksjs/logging": "^0.70.256",
118
- "@stacksjs/notifications": "^0.70.256",
119
- "@stacksjs/objects": "^0.70.256",
120
- "@stacksjs/orm": "^0.70.256",
121
- "@stacksjs/path": "^0.70.256",
122
- "@stacksjs/skills": "^0.70.256",
123
- "@stacksjs/payments": "^0.70.256",
124
- "@stacksjs/realtime": "^0.70.256",
125
- "@stacksjs/router": "^0.70.256",
115
+ "@stacksjs/image": "^0.70.258",
116
+ "@stacksjs/lint": "^0.70.258",
117
+ "@stacksjs/logging": "^0.70.258",
118
+ "@stacksjs/notifications": "^0.70.258",
119
+ "@stacksjs/objects": "^0.70.258",
120
+ "@stacksjs/orm": "^0.70.258",
121
+ "@stacksjs/path": "^0.70.258",
122
+ "@stacksjs/skills": "^0.70.258",
123
+ "@stacksjs/payments": "^0.70.258",
124
+ "@stacksjs/realtime": "^0.70.258",
125
+ "@stacksjs/router": "^0.70.258",
126
126
  "@stacksjs/rpx": "^0.11.42",
127
- "@stacksjs/search-engine": "^0.70.256",
128
- "@stacksjs/security": "^0.70.256",
129
- "@stacksjs/server": "^0.70.256",
130
- "@stacksjs/storage": "^0.70.256",
131
- "@stacksjs/strings": "^0.70.256",
132
- "@stacksjs/testing": "^0.70.256",
133
- "@stacksjs/tunnel": "^0.70.256",
134
- "@stacksjs/types": "^0.70.256",
135
- "@stacksjs/ui": "^0.70.256",
136
- "@stacksjs/utils": "^0.70.256",
137
- "@stacksjs/validation": "^0.70.256",
138
- "@stacksjs/ts-cloud": "^0.7.92",
127
+ "@stacksjs/search-engine": "^0.70.258",
128
+ "@stacksjs/security": "^0.70.258",
129
+ "@stacksjs/server": "^0.70.258",
130
+ "@stacksjs/storage": "^0.70.258",
131
+ "@stacksjs/strings": "^0.70.258",
132
+ "@stacksjs/testing": "^0.70.258",
133
+ "@stacksjs/tunnel": "^0.70.258",
134
+ "@stacksjs/types": "^0.70.258",
135
+ "@stacksjs/ui": "^0.70.258",
136
+ "@stacksjs/utils": "^0.70.258",
137
+ "@stacksjs/validation": "^0.70.258",
138
+ "@stacksjs/ts-cloud": "^0.7.94",
139
139
  "ajv": "^8.20.0",
140
140
  "ajv-formats": "^3.0.1",
141
141
  "ts-pantry": "^0.11.0"