@thebaycloud/cli 1.0.0 → 1.1.0
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/CHANGELOG.md +75 -0
- package/LICENSE +34 -0
- package/README.md +30 -13
- package/index.js +612 -88
- package/lib/brand.js +4 -1
- package/lib/check.js +15 -2
- package/lib/confirm.js +1 -1
- package/lib/draft.js +1 -1
- package/lib/home.js +46 -0
- package/lib/resolver.js +3 -3
- package/lib/rows.js +61 -0
- package/lib/share-args.js +93 -0
- package/lib/who.js +4 -3
- package/package.json +10 -10
- package/vendor/README.md +1 -1
- package/vendor/detector.js +2 -2
- package/vendor/inputs.json +2 -1
- package/vendor/resolve.js +9 -7
package/lib/brand.js
CHANGED
|
@@ -33,7 +33,10 @@ const fs = require("fs");
|
|
|
33
33
|
const BRAND = "Bay";
|
|
34
34
|
const CLI = "bay";
|
|
35
35
|
const DOMAIN = "thebay.cloud";
|
|
36
|
-
|
|
36
|
+
// Derived from DOMAIN, which said `thebay.cloud` on the line above while this
|
|
37
|
+
// said `supersonic.cv`. Both answer, so nothing broke — which is exactly why it
|
|
38
|
+
// went unnoticed, and why every `bay` install was talking to the retiring name.
|
|
39
|
+
const DEFAULT_URL = `https://app.${DOMAIN}`;
|
|
37
40
|
|
|
38
41
|
const NEW_DIR = path.join(os.homedir(), ".bay");
|
|
39
42
|
const OLD_DIR = path.join(os.homedir(), ".supersonic");
|
package/lib/check.js
CHANGED
|
@@ -203,7 +203,20 @@ function renderService(s) {
|
|
|
203
203
|
// has no start command, no health path and no request timeout, and printing
|
|
204
204
|
// the defaults for all three would describe a service that is not deployed.
|
|
205
205
|
if (!s.serviceless) {
|
|
206
|
-
|
|
206
|
+
// Keyed off the AUTHOR's Dockerfile, not off the lane.
|
|
207
|
+
//
|
|
208
|
+
// Asking `s.lane === "container"` was right while there were four lanes.
|
|
209
|
+
// There are two. `container` now means "an image is built and a node runs
|
|
210
|
+
// it" whether the author committed a Dockerfile or `generateDockerfile`
|
|
211
|
+
// wrote one FROM THIS `start` COMMAND — so the old condition answered "the
|
|
212
|
+
// Dockerfile's own CMD" for every non-static service, about a repository
|
|
213
|
+
// with no Dockerfile in it, hiding a command the config states in plain
|
|
214
|
+
// text. In the one command whose whole job is to say what will run.
|
|
215
|
+
//
|
|
216
|
+
// `s.dockerfile` is the author's file and only ever that; resolve.ts draws
|
|
217
|
+
// the same line. The `image` phase above already names it, so this line
|
|
218
|
+
// does not repeat the path.
|
|
219
|
+
lines.push(phase("start", s.dockerfile
|
|
207
220
|
? "the Dockerfile's own CMD"
|
|
208
221
|
: s.start || (s.runs.length ? "— (the web process below)" : "— (nothing to run: this lane needs one)")));
|
|
209
222
|
// The web process's own health check is what the deploy probes, so it is
|
|
@@ -271,7 +284,7 @@ function renderCheck(configFilename, app, problems, warnings) {
|
|
|
271
284
|
lines.push(head.startsWith("✕") ? head : `✕ ${head}`, ...rest);
|
|
272
285
|
}
|
|
273
286
|
lines.push("");
|
|
274
|
-
lines.push(`${problems.length} problem${problems.length === 1 ? "" : "s"} — nothing was
|
|
287
|
+
lines.push(`${problems.length} problem${problems.length === 1 ? "" : "s"} — nothing was shipped, and nothing would be.`);
|
|
275
288
|
} else {
|
|
276
289
|
lines.push("✓ nothing here fails before the build. No GCP was touched.");
|
|
277
290
|
}
|
package/lib/confirm.js
CHANGED
|
@@ -36,7 +36,7 @@ function deletionRefusal(app, args) {
|
|
|
36
36
|
|
|
37
37
|
return [
|
|
38
38
|
`${app} would be deleted, and so would its DATA: the database, the storage`,
|
|
39
|
-
"bucket, the images and the
|
|
39
|
+
"bucket, the images and the ship history all go with it.",
|
|
40
40
|
"",
|
|
41
41
|
"This cannot be undone, and there is no prompt to say yes to — run",
|
|
42
42
|
` bay delete ${app} --yes`,
|
package/lib/draft.js
CHANGED
|
@@ -478,7 +478,7 @@ function renderDraft(configFilename, config, unknowns) {
|
|
|
478
478
|
// confidence answer. Presenting its output as a finding rather than a draft is
|
|
479
479
|
// how that answer became a deploy.
|
|
480
480
|
lines.push("This is a draft, from a detector that has been confidently wrong before.");
|
|
481
|
-
lines.push(`Read it, fix what is wrong, then:
|
|
481
|
+
lines.push(`Read it, fix what is wrong, then: bay check`);
|
|
482
482
|
return lines;
|
|
483
483
|
}
|
|
484
484
|
|
package/lib/home.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Where the session lives, and what the variables are called, across a rename.
|
|
5
|
+
*
|
|
6
|
+
* Both answers changed at once — `~/.supersonic` became `~/.bay`, `SUPERSONIC_*`
|
|
7
|
+
* became `BAY_*` — and both have the same failure mode if they change carelessly:
|
|
8
|
+
* the CLI comes up signed out. Not broken, not erroring; signed out, with a
|
|
9
|
+
* perfectly good token sitting in a file it stopped looking at, and an agent
|
|
10
|
+
* mid-task being told to open a browser.
|
|
11
|
+
*
|
|
12
|
+
* Pure, and taking the home directory and an existence check as arguments, so
|
|
13
|
+
* the migration can be tested without touching anyone's real home.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The config directory: the new name if it is there, the old name if IT is, the
|
|
18
|
+
* new name otherwise.
|
|
19
|
+
*
|
|
20
|
+
* Deliberately not "read both and merge". Two files that disagree about which
|
|
21
|
+
* account you are would be resolved by whichever line ran first, and the symptom
|
|
22
|
+
* — deploying to the wrong account — is one nobody would trace back to here. One
|
|
23
|
+
* directory wins outright, and an existing user keeps writing to the file they
|
|
24
|
+
* already have.
|
|
25
|
+
*/
|
|
26
|
+
function configDirIn(home, exists, join) {
|
|
27
|
+
const next = join(home, ".bay");
|
|
28
|
+
const prev = join(home, ".supersonic");
|
|
29
|
+
if (exists(next)) return next;
|
|
30
|
+
if (exists(prev)) return prev;
|
|
31
|
+
return next;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* `BAY_<NAME>`, falling back to `SUPERSONIC_<NAME>`.
|
|
36
|
+
*
|
|
37
|
+
* The new name wins when both are set: somebody who exported both is midway
|
|
38
|
+
* through a migration, and the value they added last is the one they mean. An
|
|
39
|
+
* empty string is treated as unset, which is what a shell gives you for an
|
|
40
|
+
* exported-but-never-assigned variable.
|
|
41
|
+
*/
|
|
42
|
+
function envVarFrom(env, name) {
|
|
43
|
+
return (env["BAY_" + name] || env["SUPERSONIC_" + name] || "").trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { configDirIn, envVarFrom };
|
package/lib/resolver.js
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Loaded lazily and through a named function rather than at require() time: the
|
|
13
13
|
* bundle is generated, an old global install can be missing it, and a missing
|
|
14
|
-
* bundle must fail `
|
|
15
|
-
* crashing `
|
|
14
|
+
* bundle must fail `bay check` with a sentence naming the fix rather than
|
|
15
|
+
* crashing `bay logs`, which does not use it at all.
|
|
16
16
|
*/
|
|
17
17
|
const path = require("node:path");
|
|
18
18
|
|
|
@@ -37,7 +37,7 @@ function resolver() {
|
|
|
37
37
|
* The stack detector, as the async `Detect` that inferAppConfig expects.
|
|
38
38
|
*
|
|
39
39
|
* Synchronous in the CLI — there is one process, one repo, and nothing else
|
|
40
|
-
* waiting on the event loop. The control plane spawns the
|
|
40
|
+
* waiting on the event loop. The control plane spawns the detector as a
|
|
41
41
|
* subprocess for the same job, which is why the interface is a promise at all;
|
|
42
42
|
* matching the interface rather than the transport is what lets both callers
|
|
43
43
|
* share inferAppConfig unchanged.
|
package/lib/rows.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Query results as a table a terminal can hold.
|
|
5
|
+
*
|
|
6
|
+
* `bay db <app> <table>` returns real production rows, and a row is not a
|
|
7
|
+
* shape this CLI controls: one column is an id, the next is a 4KB blob of JSON,
|
|
8
|
+
* and printing them raw turns a 12-row answer into four screens of wrapped text
|
|
9
|
+
* with no visible columns at all. So every cell is flattened to one line and
|
|
10
|
+
* clipped, and the width is the widest cell that survived.
|
|
11
|
+
*
|
|
12
|
+
* Clipping is marked with `…` rather than being silent. A truncated value that
|
|
13
|
+
* looks whole is worse than no value: it is the one a person copies.
|
|
14
|
+
*
|
|
15
|
+
* `--json` exists for everything this drops. That is the contract — this is the
|
|
16
|
+
* human reading, `--json` is the data.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** null and undefined are not the string "null" — they are the empty cell. */
|
|
20
|
+
function cell(v, max) {
|
|
21
|
+
if (v === null || v === undefined) return "";
|
|
22
|
+
const s = typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
23
|
+
// Tabs and newlines inside a value would break the alignment of every row
|
|
24
|
+
// after them, so they become a single space before anything is measured.
|
|
25
|
+
const flat = s.replace(/\s+/g, " ").trim();
|
|
26
|
+
return flat.length > max ? flat.slice(0, max - 1) + "…" : flat;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string[]} columns
|
|
31
|
+
* @param {object[]} rows
|
|
32
|
+
* @param {{max?: number}} [opts] max characters per cell (default 40)
|
|
33
|
+
* @returns {string[]} lines: a header, a rule, then one line per row
|
|
34
|
+
*/
|
|
35
|
+
function renderTable(columns, rows, opts = {}) {
|
|
36
|
+
const max = opts.max ?? 40;
|
|
37
|
+
const cols = columns || [];
|
|
38
|
+
if (!cols.length) return [];
|
|
39
|
+
|
|
40
|
+
const body = (rows || []).map((r) =>
|
|
41
|
+
// Arrays as well as objects: `SELECT` results arrive keyed by column name,
|
|
42
|
+
// but a caller holding tuples should not have to convert them first.
|
|
43
|
+
cols.map((c, i) => cell(Array.isArray(r) ? r[i] : r?.[c], max))
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const width = cols.map((c, i) =>
|
|
47
|
+
Math.max(cell(c, max).length, ...body.map((r) => r[i].length), 1)
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// The last column is not padded. Trailing spaces are invisible until somebody
|
|
51
|
+
// pipes this into a diff, and every line would carry them.
|
|
52
|
+
const line = (parts) => parts.map((p, i) => (i === parts.length - 1 ? p : p.padEnd(width[i]))).join(" ").trimEnd();
|
|
53
|
+
|
|
54
|
+
return [
|
|
55
|
+
line(cols.map((c) => cell(c, max))),
|
|
56
|
+
line(width.map((w) => "─".repeat(w))),
|
|
57
|
+
...body.map(line),
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = { renderTable, cell };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What `bay share` was asked to do, decided before anything is sent.
|
|
5
|
+
*
|
|
6
|
+
* Access is the one command in this CLI where a misread argument does damage
|
|
7
|
+
* that is not visible afterwards: `share app add acme.com` meant as "everyone at
|
|
8
|
+
* my company" and read as an email address would fail loudly, which is fine —
|
|
9
|
+
* but read the other way round it would let a whole domain into an app the
|
|
10
|
+
* owner meant to send to one person, and nothing on screen would look wrong.
|
|
11
|
+
* So the reading is a pure function with tests, and the network call is what
|
|
12
|
+
* happens after it.
|
|
13
|
+
*
|
|
14
|
+
* The two audiences are told apart by shape, not by a flag:
|
|
15
|
+
*
|
|
16
|
+
* ada@acme.com an address -> one person, invited by email
|
|
17
|
+
* @acme.com a rule -> everyone whose address ends in it
|
|
18
|
+
* acme.com the same rule -> the @ is how people write it, not a requirement
|
|
19
|
+
*
|
|
20
|
+
* A bare word with no dot is neither, and is refused rather than guessed at.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** The three visibilities the control plane accepts, in the order they widen. */
|
|
24
|
+
const VISIBILITIES = ["private", "shared", "public"];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} who
|
|
28
|
+
* @returns {{audience: "email"|"domain", value: string}|null}
|
|
29
|
+
*/
|
|
30
|
+
function audienceOf(who) {
|
|
31
|
+
const w = String(who ?? "").trim().toLowerCase();
|
|
32
|
+
if (!w) return null;
|
|
33
|
+
|
|
34
|
+
// An @ anywhere but the front makes it an address. RFC 5321 caps a mailbox at
|
|
35
|
+
// 254 characters; past that it is not an address that could ever be delivered
|
|
36
|
+
// to, and the server refuses it — better said here, where the reason fits on
|
|
37
|
+
// one line.
|
|
38
|
+
if (w.includes("@") && !w.startsWith("@")) {
|
|
39
|
+
if (w.length > 254 || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(w)) return null;
|
|
40
|
+
return { audience: "email", value: w };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const domain = w.replace(/^@/, "");
|
|
44
|
+
if (!/^[a-z0-9-]+(\.[a-z0-9-]+)+$/.test(domain)) return null;
|
|
45
|
+
return { audience: "domain", value: domain };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string[]} rest the arguments after the app name
|
|
50
|
+
* @returns {{kind: "show"}
|
|
51
|
+
* |{kind: "visibility", visibility: string}
|
|
52
|
+
* |{kind: "add"|"remove", audience: "email"|"domain", value: string}
|
|
53
|
+
* |{kind: "error", why: string}}
|
|
54
|
+
*/
|
|
55
|
+
function parseShare(rest) {
|
|
56
|
+
const [verb, ...targets] = (rest || []).filter((t) => t !== undefined && t !== null);
|
|
57
|
+
if (!verb) return { kind: "show" };
|
|
58
|
+
|
|
59
|
+
const v = String(verb).trim().toLowerCase();
|
|
60
|
+
if (VISIBILITIES.includes(v)) return { kind: "visibility", visibility: v };
|
|
61
|
+
|
|
62
|
+
if (v === "add" || v === "remove" || v === "rm") {
|
|
63
|
+
const kind = v === "rm" ? "remove" : v;
|
|
64
|
+
if (!targets.length) {
|
|
65
|
+
return { kind: "error", why: `who? usage: bay share <app> ${kind} <email|@domain>` };
|
|
66
|
+
}
|
|
67
|
+
const audience = audienceOf(targets[0]);
|
|
68
|
+
if (!audience) {
|
|
69
|
+
return {
|
|
70
|
+
kind: "error",
|
|
71
|
+
why: `"${targets[0]}" is neither an email address nor a domain — write ada@acme.com for one person, or @acme.com for everyone there`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return { kind, ...audience };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
kind: "error",
|
|
79
|
+
why: `unknown: share ${v}\nusage: bay share <app> [private|shared|public | add <email|@domain> | remove <email|@domain>]`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The request body for an add/remove, in the shape the control plane's share
|
|
85
|
+
* route reads. Kept beside the parser because the four field names — addEmail,
|
|
86
|
+
* addDomain, removeEmail, removeDomain — are the only thing connecting the two.
|
|
87
|
+
*/
|
|
88
|
+
function shareBody(action) {
|
|
89
|
+
const suffix = action.audience === "email" ? "Email" : "Domain";
|
|
90
|
+
return { [action.kind + suffix]: action.value };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { parseShare, audienceOf, shareBody, VISIBILITIES };
|
package/lib/who.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Who is shipping, as declared — never as inferred.
|
|
4
4
|
*
|
|
5
|
-
* An agent sets BAY_WHO=agent (SUPERSONIC_WHO still
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* An agent sets BAY_WHO=agent (SUPERSONIC_WHO is still read, because it is what
|
|
6
|
+
* the agent prompts written before the rename say). Nothing else is consulted: a
|
|
7
|
+
* TTY check would call CI an agent, and the platform would then draw a figure
|
|
8
|
+
* that was never there. The absence of a name is a fact; a wrong name is a lie.
|
|
8
9
|
*/
|
|
9
10
|
function whoHeader(env) {
|
|
10
11
|
const v = String(env.BAY_WHO || env.SUPERSONIC_WHO || "").trim().toLowerCase();
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thebaycloud/cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Ship & debug Bay apps from your coding agent \u2014 the agent-native CLI.",
|
|
5
5
|
"bin": {
|
|
6
|
-
"bay": "index.js",
|
|
7
|
-
"supersonic": "index.js"
|
|
6
|
+
"bay": "./index.js",
|
|
7
|
+
"supersonic": "./index.js"
|
|
8
8
|
},
|
|
9
9
|
"type": "commonjs",
|
|
10
10
|
"engines": {
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
"bundle-detector": "node scripts/bundle-detector.mjs",
|
|
22
22
|
"bundle-resolver": "node scripts/bundle-resolver.mjs",
|
|
23
23
|
"bundle": "npm run bundle-detector && npm run bundle-resolver",
|
|
24
|
-
"test": "node --test
|
|
24
|
+
"test": "node --test",
|
|
25
25
|
"prepublishOnly": "npm run bundle && npm test"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
28
28
|
"deploy",
|
|
29
29
|
"cloud",
|
|
30
30
|
"gcp",
|
|
31
|
+
"cloud-run",
|
|
31
32
|
"vibecoding",
|
|
32
|
-
"bay"
|
|
33
|
-
"coding-agent"
|
|
33
|
+
"bay"
|
|
34
34
|
],
|
|
35
35
|
"homepage": "https://thebay.cloud",
|
|
36
36
|
"repository": {
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"directory": "packages/cli"
|
|
40
40
|
},
|
|
41
41
|
"license": "MIT",
|
|
42
|
-
"bugs": {
|
|
43
|
-
"url": "https://github.com/The-Red-Onion/supersonic/issues"
|
|
44
|
-
},
|
|
45
42
|
"publishConfig": {
|
|
46
43
|
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/thebaycloud/bay/issues"
|
|
47
47
|
}
|
|
48
48
|
}
|
package/vendor/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Generated. Do not edit — `npm run bundle` rebuilds all of it.
|
|
2
2
|
|
|
3
|
-
- detector.js — scripts/bundle-detector.mjs, from
|
|
3
|
+
- detector.js — scripts/bundle-detector.mjs, from packages/detector
|
|
4
4
|
- resolve.js — scripts/bundle-resolver.mjs, from apps/web/lib
|
|
5
5
|
- inputs.json — every repository file esbuild inlined into each bundle, from its metafile.
|
|
6
6
|
test/vendor.test.js hashes these to prove the bundles are not stale.
|
package/vendor/detector.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// supersonic-vendor-stamp
|
|
1
|
+
// supersonic-vendor-stamp 90d7b95c32307760
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -349,7 +349,7 @@ function provisionPlan(s) {
|
|
|
349
349
|
plan.push("Apply security baseline (secret scan, rate limits, WAF)");
|
|
350
350
|
plan.push("Enable daily backups");
|
|
351
351
|
if (s.secretsNeeded.length) plan.push(`Ask user for secrets: ${s.secretsNeeded.join(", ")}`);
|
|
352
|
-
plan.push(`Deploy to Cloud Run on :${s.port} \u2192 <slug>.
|
|
352
|
+
plan.push(`Deploy to Cloud Run on :${s.port} \u2192 <slug>.thebay.cloud`);
|
|
353
353
|
return plan;
|
|
354
354
|
}
|
|
355
355
|
function cloneToTemp(url) {
|
package/vendor/inputs.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"detector.js": [
|
|
3
3
|
"apps/web/lib/plan-deps.ts",
|
|
4
|
-
"
|
|
4
|
+
"packages/detector/src/index.ts"
|
|
5
5
|
],
|
|
6
6
|
"resolve.js": [
|
|
7
7
|
"apps/web/lib/app-config.ts",
|
|
8
8
|
"apps/web/lib/db-address.ts",
|
|
9
9
|
"apps/web/lib/detect.ts",
|
|
10
10
|
"apps/web/lib/dockerfile-context.ts",
|
|
11
|
+
"apps/web/lib/env-owner.ts",
|
|
11
12
|
"apps/web/lib/infer-services.ts",
|
|
12
13
|
"apps/web/lib/lanes.ts",
|
|
13
14
|
"apps/web/lib/plan-deps.ts",
|
package/vendor/resolve.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// supersonic-vendor-stamp
|
|
1
|
+
// supersonic-vendor-stamp 60282bc736f6efbe
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -118,15 +118,17 @@ function withScale(over) {
|
|
|
118
118
|
return { ...DEFAULT_SCALE, ...declared };
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
// lib/env-owner.ts
|
|
122
|
+
var ALWAYS_OWNED_PREFIXES = [/^BAY_/, /^SUPERSONIC_/];
|
|
123
|
+
var ALWAYS_OWNED_EXACT = /* @__PURE__ */ new Set(["PORT"]);
|
|
124
|
+
var DATABASE_OWNED_PREFIXES = [/^POSTGRES_/, /^PG/, /^DB_/];
|
|
125
|
+
var DATABASE_OWNED_EXACT = new Set(databaseEnvNames());
|
|
126
|
+
|
|
121
127
|
// lib/app-config.ts
|
|
122
128
|
var CONFIG_FILENAME = "supersonic.json";
|
|
123
129
|
var ConfigError = class extends Error {
|
|
124
130
|
};
|
|
125
131
|
var LANGUAGES = /* @__PURE__ */ new Set(["node", "python", "static", "other"]);
|
|
126
|
-
var ALWAYS_OWNED_PREFIXES = [/^SUPERSONIC_/];
|
|
127
|
-
var ALWAYS_OWNED_EXACT = /* @__PURE__ */ new Set(["PORT"]);
|
|
128
|
-
var DATABASE_OWNED_PREFIXES = [/^POSTGRES_/, /^PG/, /^DB_/];
|
|
129
|
-
var DATABASE_OWNED_EXACT = new Set(databaseEnvNames());
|
|
130
132
|
function platformOwned(name, database) {
|
|
131
133
|
if (ALWAYS_OWNED_EXACT.has(name) || ALWAYS_OWNED_PREFIXES.some((re) => re.test(name))) return true;
|
|
132
134
|
if (database?.provider !== "managed") return false;
|
|
@@ -576,14 +578,14 @@ function runtimeMismatch(manifests) {
|
|
|
576
578
|
if (py) {
|
|
577
579
|
const need = lowestAccepted(py);
|
|
578
580
|
if (need && below(RUNTIME_VERSIONS.python, need)) {
|
|
579
|
-
return `this app needs Python ${py} and the
|
|
581
|
+
return `this app needs Python ${py} and the platform has ${RUNTIME_VERSIONS.python} \u2014 widen requires-python in pyproject.toml to accept ${RUNTIME_VERSIONS.python}, or wait for the platform to move. Nothing in the code can fix this one.`;
|
|
580
582
|
}
|
|
581
583
|
}
|
|
582
584
|
const engines = manifests.packageJson?.engines?.node;
|
|
583
585
|
if (engines) {
|
|
584
586
|
const need = lowestAccepted(engines);
|
|
585
587
|
if (need && below(RUNTIME_VERSIONS.node, need)) {
|
|
586
|
-
return `this app needs Node ${engines} and the
|
|
588
|
+
return `this app needs Node ${engines} and the platform has ${RUNTIME_VERSIONS.node} \u2014 widen engines.node in package.json to accept ${RUNTIME_VERSIONS.node}, or wait for the platform to move. Nothing in the code can fix this one.`;
|
|
587
589
|
}
|
|
588
590
|
}
|
|
589
591
|
return null;
|