@ts-cloud/core 0.7.10 → 0.7.12

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/dist/index.js CHANGED
@@ -22337,27 +22337,44 @@ function apexOf(domain) {
22337
22337
  const parts = domain.split(".").filter(Boolean);
22338
22338
  return parts.length <= 2 ? domain : parts.slice(-2).join(".");
22339
22339
  }
22340
- function resolveDashboardDomain(config, environment, explicit) {
22341
- if (explicit)
22342
- return explicit;
22340
+ function collectDomains(config, environment) {
22343
22341
  const candidates = [];
22342
+ const dnsDomain = config.infrastructure?.dns?.domain;
22343
+ if (dnsDomain)
22344
+ candidates.push(dnsDomain);
22345
+ if (environment && config.environments?.[environment]?.domain)
22346
+ candidates.push(config.environments[environment].domain);
22344
22347
  for (const site of Object.values(config.sites ?? {})) {
22345
- const d = site?.domain;
22348
+ const s = site;
22349
+ if (!s || s.redirect)
22350
+ continue;
22351
+ const d = s.domain;
22346
22352
  if (typeof d === "string")
22347
22353
  candidates.push(d);
22348
22354
  else if (Array.isArray(d))
22349
22355
  candidates.push(...d.filter((x) => typeof x === "string"));
22350
22356
  }
22351
- if (environment && config.environments?.[environment]?.domain)
22352
- candidates.push(config.environments[environment].domain);
22353
- const dnsDomain = config.infrastructure?.dns?.domain;
22354
- if (dnsDomain)
22355
- candidates.push(dnsDomain);
22356
- const base = candidates.find((d) => d && !d.startsWith("dashboard."));
22357
+ return candidates.filter((d) => d && !d.startsWith("dashboard."));
22358
+ }
22359
+ function resolveDashboardDomain(config, environment, explicit) {
22360
+ if (explicit)
22361
+ return explicit;
22362
+ const base = collectDomains(config, environment)[0];
22357
22363
  if (!base)
22358
22364
  return null;
22359
22365
  return `dashboard.${apexOf(base)}`;
22360
22366
  }
22367
+ function resolveDashboardDomains(config, environment, explicit) {
22368
+ if (explicit)
22369
+ return [explicit];
22370
+ const apexes = [];
22371
+ for (const d of collectDomains(config, environment)) {
22372
+ const apex = apexOf(d);
22373
+ if (!apexes.includes(apex))
22374
+ apexes.push(apex);
22375
+ }
22376
+ return apexes.map((apex) => `dashboard.${apex}`);
22377
+ }
22361
22378
  function hasManagementDashboardSite(config) {
22362
22379
  return Object.entries(config.sites ?? {}).some(([name, site]) => {
22363
22380
  if (!site)
@@ -22366,37 +22383,53 @@ function hasManagementDashboardSite(config) {
22366
22383
  return name === "dashboard" || root === "ui/dist" || root.endsWith("/ui/dist") || root.endsWith("/dist/ui");
22367
22384
  });
22368
22385
  }
22369
- function resolveManagementDashboardSite(config, environment, opts) {
22386
+ var MANAGEMENT_DASHBOARD_SITE_PREFIX = "dashboard-";
22387
+ function dashboardSiteName(domain, primary) {
22388
+ if (primary)
22389
+ return "dashboard";
22390
+ return MANAGEMENT_DASHBOARD_SITE_PREFIX + domain.replace(/^dashboard\./, "").replace(/\./g, "-");
22391
+ }
22392
+ function isManagementDashboardSiteName(name) {
22393
+ return name === "dashboard" || name.startsWith(MANAGEMENT_DASHBOARD_SITE_PREFIX);
22394
+ }
22395
+ function resolveManagementDashboardSites(config, environment, opts) {
22370
22396
  if (hasManagementDashboardSite(config))
22371
- return null;
22372
- const domain = resolveDashboardDomain(config, environment, opts.domain);
22373
- if (!domain)
22374
- return null;
22397
+ return [];
22375
22398
  const auth2 = opts.password ? { auth: { username: opts.username || "admin", password: opts.password, realm: opts.realm } } : {};
22399
+ const build = opts.build === false || opts.build === undefined ? {} : { build: opts.build };
22376
22400
  if (opts.live) {
22401
+ const domain = resolveDashboardDomain(config, environment, opts.domain);
22402
+ if (!domain)
22403
+ return [];
22377
22404
  const port = opts.port ?? 7676;
22378
- const site2 = {
22405
+ const site = {
22379
22406
  root: opts.uiRoot,
22380
22407
  deploy: "server",
22381
22408
  domain,
22382
22409
  start: `cloud dashboard:serve --box --host 127.0.0.1 --port ${port}`,
22383
22410
  port,
22384
22411
  ssl: { provider: "letsencrypt" },
22385
- ...opts.build === false || opts.build === undefined ? {} : { build: opts.build },
22412
+ ...build,
22386
22413
  ...auth2
22387
22414
  };
22388
- return { name: "dashboard", site: site2 };
22415
+ return [{ name: "dashboard", site }];
22389
22416
  }
22390
- const site = {
22391
- root: opts.uiRoot,
22392
- deploy: "server",
22393
- type: "static",
22394
- domain,
22395
- ssl: { provider: "letsencrypt" },
22396
- ...opts.build === false || opts.build === undefined ? {} : { build: opts.build },
22397
- ...auth2
22398
- };
22399
- return { name: "dashboard", site };
22417
+ const domains = resolveDashboardDomains(config, environment, opts.domain);
22418
+ return domains.map((domain, i) => ({
22419
+ name: dashboardSiteName(domain, i === 0),
22420
+ site: {
22421
+ root: opts.uiRoot,
22422
+ deploy: "server",
22423
+ type: "static",
22424
+ domain,
22425
+ ssl: { provider: "letsencrypt" },
22426
+ ...build,
22427
+ ...auth2
22428
+ }
22429
+ }));
22430
+ }
22431
+ function resolveManagementDashboardSite(config, environment, opts) {
22432
+ return resolveManagementDashboardSites(config, environment, opts)[0] ?? null;
22400
22433
  }
22401
22434
  // src/presets/extend.ts
22402
22435
  function deepMerge(target, source) {
@@ -47207,9 +47240,11 @@ export {
47207
47240
  resolveQueues,
47208
47241
  resolveQueueNames,
47209
47242
  resolveProjectStackName,
47243
+ resolveManagementDashboardSites,
47210
47244
  resolveManagementDashboardSite,
47211
47245
  resolveDeploymentMode,
47212
47246
  resolveDeployBucketName,
47247
+ resolveDashboardDomains,
47213
47248
  resolveDashboardDomain,
47214
47249
  resolveCredentials,
47215
47250
  resolveCloudProvider,
@@ -47255,6 +47290,7 @@ export {
47255
47290
  isWebCryptoAvailable,
47256
47291
  isValidRegion,
47257
47292
  isNodeCryptoAvailable,
47293
+ isManagementDashboardSiteName,
47258
47294
  isLocalDevelopment,
47259
47295
  isLikelyTypo,
47260
47296
  imageScanningManager,
@@ -47479,6 +47515,7 @@ export {
47479
47515
  MigrationManager,
47480
47516
  MetricsManager,
47481
47517
  Messaging,
47518
+ MANAGEMENT_DASHBOARD_SITE_PREFIX,
47482
47519
  MANAGED_NODE_VERSIONS,
47483
47520
  LogsManager,
47484
47521
  LambdaVersionsManager,
@@ -35,17 +35,48 @@ export interface ManagementDashboardOptions {
35
35
  port?: number;
36
36
  }
37
37
  /**
38
- * Derive a dashboard hostname (`dashboard.<apex>`) from the project's configured
39
- * domains. Prefers `explicit`, then any site domain, then the environment domain,
40
- * then `infrastructure.dns.domain`. Returns null when nothing is available.
38
+ * Derive the PRIMARY dashboard hostname (`dashboard.<apex>`) for the project.
39
+ * Prefers `explicit`, else the project's canonical domain (`infrastructure.dns.domain`,
40
+ * then the environment domain), else the first site domain. Returns null when
41
+ * nothing is available.
41
42
  */
42
43
  export declare function resolveDashboardDomain(config: Pick<CloudConfig, 'sites' | 'environments' | 'infrastructure'>, environment?: EnvironmentType, explicit?: string): string | null;
44
+ /**
45
+ * Derive EVERY dashboard hostname the project should expose — one
46
+ * `dashboard.<apex>` per distinct registrable apex across all configured
47
+ * domains, so each site gets a dashboard on its own domain. Deduped by apex,
48
+ * order-preserving (the first-seen apex leads, and stays the "primary").
49
+ *
50
+ * An `explicit` domain (e.g. `TS_CLOUD_UI_DOMAIN`) pins a single host and
51
+ * suppresses the per-apex fan-out — an operator asking for one host gets one.
52
+ */
53
+ export declare function resolveDashboardDomains(config: Pick<CloudConfig, 'sites' | 'environments' | 'infrastructure'>, environment?: EnvironmentType, explicit?: string): string[];
43
54
  /** Does the config already define the management dashboard as a site? */
44
55
  export declare function hasManagementDashboardSite(config: Pick<CloudConfig, 'sites'>): boolean;
56
+ /** Prefix under which non-primary per-apex dashboards are keyed. */
57
+ export declare const MANAGEMENT_DASHBOARD_SITE_PREFIX = "dashboard-";
58
+ /** Is `name` a management-dashboard site key (the primary or a per-apex one)? */
59
+ export declare function isManagementDashboardSiteName(name: string): boolean;
60
+ /**
61
+ * Build EVERY management-dashboard site to auto-inject on a server deploy — one
62
+ * per distinct apex domain (each site's own `dashboard.<apex>` host), all sharing
63
+ * the same UI artifact and Basic-auth credentials. Returns an empty array when no
64
+ * domain resolves (the static-site model is domain-routed) or the user already
65
+ * configured a dashboard site by hand.
66
+ *
67
+ * Live mode stays single-host: a box-mode service binds one loopback port, so
68
+ * fanning it out per apex would collide. Per-apex dashboards are a static-model
69
+ * feature (the default) — the same files served on each domain.
70
+ */
71
+ export declare function resolveManagementDashboardSites(config: Pick<CloudConfig, 'sites' | 'environments' | 'infrastructure'>, environment: EnvironmentType, opts: ManagementDashboardOptions): Array<{
72
+ name: string;
73
+ site: SiteConfig;
74
+ }>;
45
75
  /**
46
- * Build the management-dashboard site to auto-inject on server deploys, or null
47
- * when no domain can be resolved (the static-site model is domain-routed) or it
48
- * is already configured.
76
+ * Build the primary management-dashboard site (the `dashboard.<apex>` on the
77
+ * project's first domain), or null when none resolves / one is already
78
+ * configured. Thin wrapper over {@link resolveManagementDashboardSites} kept for
79
+ * callers that only want the single primary host.
49
80
  */
50
81
  export declare function resolveManagementDashboardSite(config: Pick<CloudConfig, 'sites' | 'environments' | 'infrastructure'>, environment: EnvironmentType, opts: ManagementDashboardOptions): {
51
82
  name: string;
package/dist/types.d.ts CHANGED
@@ -7,6 +7,16 @@ export interface CloudProviderConfig {
7
7
  * @default 'aws'
8
8
  */
9
9
  provider?: 'aws' | 'hetzner';
10
+ /**
11
+ * Attach this project's sites to a box owned by ANOTHER project instead of
12
+ * provisioning one. Set to the owner project's `project.slug` (e.g. `'stacks'`);
13
+ * the deploy targets the owner's `<slug>-<environment>-app` server, ships only
14
+ * this project's sites, and adds its own additive rpx `sites.d/<slug>.json`
15
+ * fragment + DNS — never touching the owner's box lifecycle, firewall, or other
16
+ * tenants. The owner provisions and manages the shared box; attachers only
17
+ * deploy onto it. Requires read access via the same `HCLOUD_TOKEN`.
18
+ */
19
+ attachTo?: string;
10
20
  }
11
21
  /**
12
22
  * Object storage provider selection.
@@ -1260,7 +1270,7 @@ export interface BucketConfig {
1260
1270
  }
1261
1271
  export interface DatabaseConfig {
1262
1272
  type?: 'rds' | 'dynamodb';
1263
- engine?: 'postgres' | 'mysql' | 'mariadb';
1273
+ engine?: 'postgres' | 'mysql' | 'mariadb' | 'singlestore';
1264
1274
  instanceType?: string;
1265
1275
  /** Database/schema name to create (e.g. `forge`). */
1266
1276
  name?: string;
@@ -1270,8 +1280,13 @@ export interface DatabaseConfig {
1270
1280
  password?: string;
1271
1281
  /** Hostname for a managed/external database (default `127.0.0.1` on-box). */
1272
1282
  host?: string;
1273
- /** Port (defaults: mysql/mariadb 3306, postgres 5432). */
1283
+ /** Port (defaults: mysql/mariadb/singlestore 3306, postgres 5432). */
1274
1284
  port?: number;
1285
+ /**
1286
+ * Require TLS to the database. Defaults on for managed SingleStore (Helios),
1287
+ * off for on-box engines. Set explicitly to override.
1288
+ */
1289
+ ssl?: boolean;
1275
1290
  /**
1276
1291
  * Additional database users to create beyond the app {@link username}
1277
1292
  * (the Forge Database Users feature). Each can be granted full or read-only
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-cloud/core",
3
- "version": "0.7.10",
3
+ "version": "0.7.12",
4
4
  "type": "module",
5
5
  "description": "Core CloudFormation generation library for ts-cloud",
6
6
  "author": "Chris Breuer <chris@stacksjs.com>",
@@ -31,7 +31,7 @@
31
31
  "typecheck": "tsc --noEmit"
32
32
  },
33
33
  "dependencies": {
34
- "@ts-cloud/aws-types": "0.7.10"
34
+ "@ts-cloud/aws-types": "0.7.12"
35
35
  },
36
36
  "devDependencies": {
37
37
  "typescript": "^5.9.3"