unoverse 0.1.211 → 0.1.213
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/lib/login.mjs +3 -3
- package/lib/publish.mjs +18 -6
- package/lib/ui.mjs +1 -1
- package/lib/workspace.mjs +5 -4
- package/operator/docker-compose.yml +0 -1
- package/operator/infra/aws/main.tf +3 -6
- package/operator/infra/aws/outputs.tf +0 -1
- package/operator/infra/digitalocean/main.tf +5 -7
- package/operator/infra/digitalocean/outputs.tf +0 -1
- package/package.json +1 -1
- package/vendor/base/lint/design/index.mjs +9 -0
package/lib/login.mjs
CHANGED
|
@@ -174,7 +174,7 @@ export async function login(universeInput, { log = console.log } = {}) {
|
|
|
174
174
|
if (!config.authEnabled) {
|
|
175
175
|
throw new Error(
|
|
176
176
|
`${origin} has authentication switched off, so there is no account to sign in with.\n` +
|
|
177
|
-
` To deploy there, set UNOVERSE_TOKEN to a
|
|
177
|
+
` To deploy there, set UNOVERSE_TOKEN to a publish key minted on the universe itself.`,
|
|
178
178
|
);
|
|
179
179
|
}
|
|
180
180
|
if (!config.issuer) throw new Error(`${origin} has no identity provider configured.`);
|
|
@@ -257,13 +257,13 @@ export async function tokenFor(origin, { interactive = true, log = console.log }
|
|
|
257
257
|
} else if (!config.authEnabled) {
|
|
258
258
|
throw new Error(
|
|
259
259
|
`${origin} has authentication switched off.\n` +
|
|
260
|
-
` To deploy there, set UNOVERSE_TOKEN to a
|
|
260
|
+
` To deploy there, set UNOVERSE_TOKEN to a publish key minted on the universe itself.`,
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
if (!interactive || !process.stdin.isTTY) {
|
|
265
265
|
throw new Error(
|
|
266
|
-
`No credential for ${origin}. In CI, set UNOVERSE_TOKEN to a
|
|
266
|
+
`No credential for ${origin}. In CI, set UNOVERSE_TOKEN to a publish key.\n` +
|
|
267
267
|
` At a terminal, run: unoverse login ${origin}`,
|
|
268
268
|
);
|
|
269
269
|
}
|
package/lib/publish.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* unoverse
|
|
2
|
+
* unoverse deploy studio: ship an asset workspace's items to its universe.
|
|
3
3
|
*
|
|
4
4
|
* The terminal face of @unoverse-platform/base items/publish. The order is the safety:
|
|
5
5
|
* LINT, then COMPARE (a dry-run per item against the universe), then a plan and a
|
|
@@ -49,7 +49,7 @@ const ask = async (question) => {
|
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* `unoverse
|
|
52
|
+
* `unoverse deploy studio [project] [--to <universe> | --env <name>] [--dry-run] [--yes]`
|
|
53
53
|
* Run from anywhere inside an asset workspace. bin/unoverse.mjs routes here only when
|
|
54
54
|
* the nearest marker up from cwd is a design home.
|
|
55
55
|
*/
|
|
@@ -59,7 +59,9 @@ export async function publish(args) {
|
|
|
59
59
|
const i = args.indexOf(`--${name}`);
|
|
60
60
|
return i >= 0 ? args[i + 1] : undefined;
|
|
61
61
|
};
|
|
62
|
-
|
|
62
|
+
// An option's value is never the project: `--env uat` must not read "uat" as one.
|
|
63
|
+
const optionValues = new Set(["to", "env"].map(opt).filter(Boolean));
|
|
64
|
+
const positional = args.find((a) => !a.startsWith("--") && !optionValues.has(a));
|
|
63
65
|
|
|
64
66
|
const ws = findWorkspace();
|
|
65
67
|
if (ws?.kind !== "assets") die("no design/ folder here or above. Run this inside your workspace.");
|
|
@@ -81,10 +83,20 @@ export async function publish(args) {
|
|
|
81
83
|
die(`no project "${project}" in ${designRoot}. Found: ${projects.join(", ") || "none"}`);
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
// ── which universe: --to overrides,
|
|
85
|
-
//
|
|
86
|
+
// ── which universe: --to overrides, --env names one from unoverse.yaml's
|
|
87
|
+
// `environments:`, `universe:` is the default, the login answers, and only a
|
|
88
|
+
// machine that knows nothing asks ─────────────────────────────────────────
|
|
86
89
|
const config = await readConfig(ws.root);
|
|
87
|
-
|
|
90
|
+
const envName = opt("env");
|
|
91
|
+
let envUniverse;
|
|
92
|
+
if (envName && !opt("to")) {
|
|
93
|
+
envUniverse = config.environments?.[envName];
|
|
94
|
+
if (!envUniverse) {
|
|
95
|
+
const known = Object.keys(config.environments ?? {});
|
|
96
|
+
die(`no environment "${envName}" in unoverse.yaml. ${known.length ? `Found: ${known.join(", ")}` : "Add one:\n\n environments:\n " + envName + ": https://your-universe.example"}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let universe = opt("to") ?? envUniverse ?? config.universe;
|
|
88
100
|
if (!universe) {
|
|
89
101
|
// THE LOGIN ALREADY NAMED THE UNIVERSE. `unoverse login` takes an address and
|
|
90
102
|
// keeps a session for it, so a machine signed in to exactly one universe has
|
package/lib/ui.mjs
CHANGED
|
@@ -22,7 +22,7 @@ export async function ask(rl, q) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export const OPTIONS = [
|
|
25
|
-
["Studio", "Components, agents and
|
|
25
|
+
["Studio", "Components, apps, agents and nodes", "Most people start here"],
|
|
26
26
|
["Universe", "Run the platform yourself, on your own infrastructure"],
|
|
27
27
|
["Client", "A client accelerator that talks to unoverse"],
|
|
28
28
|
];
|
package/lib/workspace.mjs
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* reason studio keeps one: this package is plain node and cannot import base's TypeScript.
|
|
7
7
|
* Keep the three in step.
|
|
8
8
|
*
|
|
9
|
-
* `unoverse.yaml` at the workspace root is the COMMITTED project config
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* `unoverse.yaml` at the workspace root is the COMMITTED project config: `universe:`, the
|
|
10
|
+
* default deploy target, and `environments:`, named targets (`uat`, `production`) that CI
|
|
11
|
+
* picks with `--env`. It is config, not a secret: the whole team and CI deploy to the same
|
|
12
|
+
* places, which is exactly why it belongs in the repo. Credentials never live here (they
|
|
13
|
+
* are per machine in ~/.unoverse, see session.mjs, or a CI secret in UNOVERSE_TOKEN).
|
|
13
14
|
*/
|
|
14
15
|
import { existsSync, renameSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
16
|
import { join, resolve } from "node:path";
|
|
@@ -275,7 +275,6 @@ services:
|
|
|
275
275
|
# Connection budget (INFRASTRUCTURE.md, Postgres law) — per-size values
|
|
276
276
|
# rendered by the infra Terraform; unset in local dev = code defaults (10).
|
|
277
277
|
- DB_POOL_ENGINE=${DB_POOL_ENGINE:-}
|
|
278
|
-
- DB_POOL_ENGINE_LEGACY=${DB_POOL_ENGINE_LEGACY:-}
|
|
279
278
|
# Encrypts stored credentials at rest. WITHOUT this the engine falls back to a
|
|
280
279
|
# key committed in source (SECURITY.md § Credential encryption at rest) — set it
|
|
281
280
|
# in every deployment: openssl rand -base64 32
|
|
@@ -83,24 +83,21 @@ locals {
|
|
|
83
83
|
instance = "t3.xlarge"
|
|
84
84
|
pg = "db.t4g.small"
|
|
85
85
|
redis = "cache.t4g.micro"
|
|
86
|
-
pool_engine =
|
|
87
|
-
pool_legacy = 4
|
|
86
|
+
pool_engine = 12
|
|
88
87
|
pool_memory = 4
|
|
89
88
|
}
|
|
90
89
|
medium = { # 8 vCPU / 32 GB
|
|
91
90
|
instance = "m6i.2xlarge"
|
|
92
91
|
pg = "db.t4g.medium"
|
|
93
92
|
redis = "cache.t4g.small"
|
|
94
|
-
pool_engine =
|
|
95
|
-
pool_legacy = 8
|
|
93
|
+
pool_engine = 28
|
|
96
94
|
pool_memory = 10
|
|
97
95
|
}
|
|
98
96
|
large = { # 8 vCPU / 64 GB — memory-optimized: the engine is ONE event loop
|
|
99
97
|
instance = "r6i.2xlarge"
|
|
100
98
|
pg = "db.m6g.large"
|
|
101
99
|
redis = "cache.t4g.medium"
|
|
102
|
-
pool_engine =
|
|
103
|
-
pool_legacy = 12
|
|
100
|
+
pool_engine = 52
|
|
104
101
|
pool_memory = 20
|
|
105
102
|
}
|
|
106
103
|
}
|
|
@@ -102,7 +102,6 @@ output "env_production" {
|
|
|
102
102
|
|
|
103
103
|
# Connection budget (INFRASTRUCTURE.md, size = ${var.size})
|
|
104
104
|
DB_POOL_ENGINE=${local.s.pool_engine}
|
|
105
|
-
DB_POOL_ENGINE_LEGACY=${local.s.pool_legacy}
|
|
106
105
|
DB_POOL_MEMORY=${local.s.pool_memory}
|
|
107
106
|
|
|
108
107
|
# Credential encryption at rest — per-deployment, generated by Terraform.
|
|
@@ -27,9 +27,9 @@ locals {
|
|
|
27
27
|
droplet = "s-4vcpu-16gb-amd" # 4 vCPU / 16 GB — the POC box
|
|
28
28
|
pg = "db-s-1vcpu-1gb" # ~22 backend connections (~19 usable) — hence the pool
|
|
29
29
|
redis = "db-s-1vcpu-1gb"
|
|
30
|
-
# Connection budget: engine
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
# Connection budget: engine 12 / memory 4 (+2 ops, +1 reserve = 19). The engine took the
|
|
31
|
+
# removed legacy pool's 4 on 2026-09-29 (docs/migrations/DATABASE_ROOT_CAUSE.md).
|
|
32
|
+
pool_engine = 12
|
|
33
33
|
pool_memory = 4
|
|
34
34
|
pgbouncer = 17 # backend connections the managed pool holds (leaves 5 direct: migrations + ops)
|
|
35
35
|
}
|
|
@@ -37,8 +37,7 @@ locals {
|
|
|
37
37
|
droplet = "g-8vcpu-32gb"
|
|
38
38
|
pg = "db-s-2vcpu-4gb"
|
|
39
39
|
redis = "db-s-1vcpu-2gb"
|
|
40
|
-
pool_engine =
|
|
41
|
-
pool_legacy = 8
|
|
40
|
+
pool_engine = 28
|
|
42
41
|
pool_memory = 10
|
|
43
42
|
pgbouncer = 40
|
|
44
43
|
}
|
|
@@ -46,8 +45,7 @@ locals {
|
|
|
46
45
|
droplet = "m-8vcpu-64gb" # memory-optimized: the engine is ONE event loop — RAM, not cores
|
|
47
46
|
pg = "db-s-4vcpu-8gb"
|
|
48
47
|
redis = "db-s-2vcpu-4gb"
|
|
49
|
-
pool_engine =
|
|
50
|
-
pool_legacy = 12
|
|
48
|
+
pool_engine = 52
|
|
51
49
|
pool_memory = 20
|
|
52
50
|
pgbouncer = 80
|
|
53
51
|
}
|
|
@@ -95,7 +95,6 @@ output "env_production" {
|
|
|
95
95
|
|
|
96
96
|
# Connection budget (INFRASTRUCTURE.md, size = ${var.size})
|
|
97
97
|
DB_POOL_ENGINE=${local.s.pool_engine}
|
|
98
|
-
DB_POOL_ENGINE_LEGACY=${local.s.pool_legacy}
|
|
99
98
|
DB_POOL_MEMORY=${local.s.pool_memory}
|
|
100
99
|
|
|
101
100
|
# Credential encryption at rest — per-deployment, generated by Terraform.
|
package/package.json
CHANGED
|
@@ -924,6 +924,15 @@ function componentNamesForFile(file) {
|
|
|
924
924
|
const org = projectSegs.join("/");
|
|
925
925
|
const cached = componentNamesCache.get(org);
|
|
926
926
|
if (cached !== undefined) return cached;
|
|
927
|
+
// A PARTIAL SET IS WORSE THAN NO SET (the app-sizes trap below, same cause). A developer's
|
|
928
|
+
// project has no design system on disk, so DS falls back to DESIGN_ROOT and only the org's
|
|
929
|
+
// own names would be left: every marketplace name a preview uses (`card`) then reads as
|
|
930
|
+
// unknown, and a fresh Studio workspace failed lint on its own sample. Unverifiable here,
|
|
931
|
+
// so not asserted.
|
|
932
|
+
if (DS === DESIGN_ROOT) {
|
|
933
|
+
componentNamesCache.set(org, null);
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
927
936
|
const dirs = [
|
|
928
937
|
join(DS, "components"),
|
|
929
938
|
join(DESIGN_ROOT, ...projectSegs, "components"),
|