wawesome 0.0.7 → 0.0.9
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/README.md +19 -2
- package/dist/index.mjs +319 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,8 +52,9 @@ npx wawesome init --template stripe-webhook
|
|
|
52
52
|
|
|
53
53
|
This goes from nothing to a deployed Function in one command. The template is downloaded over
|
|
54
54
|
plain HTTP and unpacked — **git is not required**, on any platform — and its files land exactly as
|
|
55
|
-
they are published. Only
|
|
56
|
-
|
|
55
|
+
they are published. Only the `name` in `package.json`, the App and Function names in
|
|
56
|
+
`wawesome-function.json`, and the `wawesome` dependency are touched — the last so the project you
|
|
57
|
+
get depends on the CLI that scaffolded it rather than the one the template was released against.
|
|
57
58
|
|
|
58
59
|
Templates declare what they need rather than shipping placeholders, so the CLI then asks for each
|
|
59
60
|
environment variable the template requires — showing where in the provider's own dashboard to find
|
|
@@ -61,6 +62,22 @@ the value — stores them (secrets write-only), enables any outbound providers t
|
|
|
61
62
|
and deploys. If a value does not exist yet, leave it blank; the CLI tells you the `env set` command
|
|
62
63
|
to run once it does.
|
|
63
64
|
|
|
65
|
+
Dependencies are installed for you (with whatever package manager launched the CLI — `npx`, `pnpm
|
|
66
|
+
dlx`, `bun x`), so the types are there when you open the project and the bundler can resolve the
|
|
67
|
+
template's imports. Pass `--no-install` to do it yourself. A failed install never stops the flow;
|
|
68
|
+
the command to re-run is printed.
|
|
69
|
+
|
|
70
|
+
Before any of that is sent anywhere, the CLI shows you the **public URL your Function will have**
|
|
71
|
+
and offers to change your workspace address — the part of that URL that is yours rather than this
|
|
72
|
+
project's, and the same one `wawesome workspace show` prints. It is randomly minted at signup and
|
|
73
|
+
stays changeable until your first deploy, at which point it locks for good, because live URLs carry
|
|
74
|
+
it. Press enter to keep it. If it is already locked, the offer is not made and the reason is said
|
|
75
|
+
plainly.
|
|
76
|
+
|
|
77
|
+
Being logged in is checked **before** the first question, not at the deploy: an expired session
|
|
78
|
+
offers you a login there and then, and declining still leaves you the project plus the two commands
|
|
79
|
+
that finish it.
|
|
80
|
+
|
|
64
81
|
Run it in an empty directory: if any file would be overwritten, nothing is written at all and the
|
|
65
82
|
collision is named.
|
|
66
83
|
|
package/dist/index.mjs
CHANGED
|
@@ -5,8 +5,9 @@ import { build } from "esbuild";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import http from "node:http";
|
|
7
7
|
import readline from "node:readline";
|
|
8
|
+
import { confirm, select } from "@inquirer/prompts";
|
|
9
|
+
import { spawnSync } from "node:child_process";
|
|
8
10
|
import zlib from "node:zlib";
|
|
9
|
-
import { select } from "@inquirer/prompts";
|
|
10
11
|
//#region src/config.ts
|
|
11
12
|
/**
|
|
12
13
|
* Hardcoded Supabase project config for the wawesome.io platform.
|
|
@@ -150,6 +151,18 @@ async function buildJs(entryInput, options) {
|
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
//#endregion
|
|
154
|
+
//#region src/cli-version.ts
|
|
155
|
+
/**
|
|
156
|
+
* The version of the CLI that is running, read from the package it ships in.
|
|
157
|
+
*
|
|
158
|
+
* Compiled in at bundle time rather than resolved from disk, because `dist/` is
|
|
159
|
+
* what gets published and the manifest next to it is not where a `npx` cache,
|
|
160
|
+
* a global install, and a workspace checkout all agree it will be. Anything
|
|
161
|
+
* that has to name this version — `--version`, the dependency a scaffolded
|
|
162
|
+
* project pins — reads it here, so a release bumps one file.
|
|
163
|
+
*/
|
|
164
|
+
const CLI_VERSION = "0.0.9";
|
|
165
|
+
//#endregion
|
|
153
166
|
//#region src/prompt.ts
|
|
154
167
|
/**
|
|
155
168
|
* Open a prompt session on stdin, queueing lines as they arrive.
|
|
@@ -271,6 +284,34 @@ async function resolveWorkspace(creds) {
|
|
|
271
284
|
};
|
|
272
285
|
}
|
|
273
286
|
/**
|
|
287
|
+
* What a refused rename means for whoever asked for it.
|
|
288
|
+
*
|
|
289
|
+
* Shared by the two places that offer a rename, so they cannot drift into
|
|
290
|
+
* telling a user different things about the same reason — and `retryable` is
|
|
291
|
+
* what separates a name worth retyping from one that no name would fix.
|
|
292
|
+
*/
|
|
293
|
+
function renameAdvice(reason) {
|
|
294
|
+
switch (reason) {
|
|
295
|
+
case "taken":
|
|
296
|
+
case "reserved": return {
|
|
297
|
+
text: "Pick a different name.",
|
|
298
|
+
retryable: true
|
|
299
|
+
};
|
|
300
|
+
case "malformed": return {
|
|
301
|
+
text: "Use lowercase letters, numbers and single hyphens.",
|
|
302
|
+
retryable: true
|
|
303
|
+
};
|
|
304
|
+
case "locked": return {
|
|
305
|
+
text: "A deploy landed while this was running, which fixed the address for good.",
|
|
306
|
+
retryable: false
|
|
307
|
+
};
|
|
308
|
+
default: return {
|
|
309
|
+
text: "",
|
|
310
|
+
retryable: false
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
274
315
|
* Ask for the workspace's public address to become `slug`.
|
|
275
316
|
*
|
|
276
317
|
* The slug is sent exactly as typed. Sanitizing it the way an App slug is
|
|
@@ -288,9 +329,8 @@ async function renameTenantSlug(creds, slug) {
|
|
|
288
329
|
});
|
|
289
330
|
if (!res.ok) throw await asGatewayError(res, `Rename failed (HTTP ${res.status}).`);
|
|
290
331
|
const result = await res.json();
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
...stored,
|
|
332
|
+
writeCredentials({
|
|
333
|
+
...readCredentials() ?? creds,
|
|
294
334
|
tenant_slug: result.tenant_slug
|
|
295
335
|
});
|
|
296
336
|
return result;
|
|
@@ -806,6 +846,58 @@ async function envCommand(action, key, value, options) {
|
|
|
806
846
|
process.exit(1);
|
|
807
847
|
}
|
|
808
848
|
//#endregion
|
|
849
|
+
//#region src/install.ts
|
|
850
|
+
/** Package managers whose `install` this understands. */
|
|
851
|
+
const KNOWN_MANAGERS = [
|
|
852
|
+
"npm",
|
|
853
|
+
"pnpm",
|
|
854
|
+
"yarn",
|
|
855
|
+
"bun"
|
|
856
|
+
];
|
|
857
|
+
/**
|
|
858
|
+
* Which package manager to install with.
|
|
859
|
+
*
|
|
860
|
+
* Taken from the one that launched this process — `pnpm dlx wawesome init`
|
|
861
|
+
* should not leave a `package-lock.json` behind — and npm otherwise, since that
|
|
862
|
+
* is what `npx` is.
|
|
863
|
+
*/
|
|
864
|
+
function packageManager(userAgent = process.env.npm_config_user_agent) {
|
|
865
|
+
const name = (userAgent ?? "").split("/")[0];
|
|
866
|
+
return KNOWN_MANAGERS.includes(name) ? name : "npm";
|
|
867
|
+
}
|
|
868
|
+
function installDependencies(dir, options = {}) {
|
|
869
|
+
const manager = packageManager();
|
|
870
|
+
const command = `${manager} install`;
|
|
871
|
+
if (!fs.existsSync(path.join(dir, "package.json"))) return {
|
|
872
|
+
command,
|
|
873
|
+
ok: false
|
|
874
|
+
};
|
|
875
|
+
console.log(`\n[wawesome] Installing dependencies (${command})...`);
|
|
876
|
+
const result = spawnSync(manager, ["install"], {
|
|
877
|
+
cwd: dir,
|
|
878
|
+
stdio: options.verbose ? "inherit" : [
|
|
879
|
+
"ignore",
|
|
880
|
+
"ignore",
|
|
881
|
+
"pipe"
|
|
882
|
+
],
|
|
883
|
+
shell: process.platform === "win32"
|
|
884
|
+
});
|
|
885
|
+
if (result.status === 0) {
|
|
886
|
+
console.log("[wawesome] ✅ Dependencies installed.");
|
|
887
|
+
return {
|
|
888
|
+
command,
|
|
889
|
+
ok: true
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
const stderr = result.stderr?.toString().trim();
|
|
893
|
+
console.log(`[wawesome] ${command} did not finish. Run it yourself when you can.`);
|
|
894
|
+
if (stderr) console.log(`[wawesome] ${stderr.split("\n").slice(-3).join("\n[wawesome] ")}`);
|
|
895
|
+
return {
|
|
896
|
+
command,
|
|
897
|
+
ok: false
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
//#endregion
|
|
809
901
|
//#region src/provision.ts
|
|
810
902
|
/** The gateway's own words for a failure, or a plain HTTP status if it gave none. */
|
|
811
903
|
async function rejected(res, fallback) {
|
|
@@ -902,15 +994,61 @@ function applyTemplateIdentity(dir, identity) {
|
|
|
902
994
|
function: identity.functionName
|
|
903
995
|
}));
|
|
904
996
|
}
|
|
997
|
+
/** The package the CLI publishes itself as, and which a template depends on. */
|
|
998
|
+
const CLI_PACKAGE = "wawesome";
|
|
999
|
+
/**
|
|
1000
|
+
* Point the template's dependency on the CLI at the CLI that is scaffolding it.
|
|
1001
|
+
*
|
|
1002
|
+
* A template pins the version it was released against, and below 1.0 a caret
|
|
1003
|
+
* range is an exact pin — `^0.0.6` never resolves to `0.0.7`. So a template
|
|
1004
|
+
* whose repository has not been touched since installs an older CLI than the
|
|
1005
|
+
* one the user just ran, and ships types that predate the platform it is about
|
|
1006
|
+
* to deploy to. Rewriting it here keeps the two in step without a template
|
|
1007
|
+
* release for every CLI release.
|
|
1008
|
+
*
|
|
1009
|
+
* Only a declaration that already exists is rewritten: what a template depends
|
|
1010
|
+
* on is the template's business, and a template that does not use the CLI does
|
|
1011
|
+
* not acquire it here.
|
|
1012
|
+
*/
|
|
1013
|
+
function alignCliDependency(dir, version) {
|
|
1014
|
+
const file = path.join(dir, PACKAGE_FILE);
|
|
1015
|
+
const pkg = readJson(file);
|
|
1016
|
+
const wanted = `^${version}`;
|
|
1017
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
1018
|
+
const deps = pkg[field];
|
|
1019
|
+
if (!isRecord(deps)) continue;
|
|
1020
|
+
const current = deps[CLI_PACKAGE];
|
|
1021
|
+
if (typeof current !== "string" || current === wanted) continue;
|
|
1022
|
+
writeJson(file, {
|
|
1023
|
+
...pkg,
|
|
1024
|
+
[field]: {
|
|
1025
|
+
...deps,
|
|
1026
|
+
[CLI_PACKAGE]: wanted
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
return {
|
|
1030
|
+
from: current,
|
|
1031
|
+
to: wanted
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
function isRecord(value) {
|
|
1037
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1038
|
+
}
|
|
905
1039
|
function patchJson(file, patch) {
|
|
1040
|
+
writeJson(file, patch(readJson(file)));
|
|
1041
|
+
}
|
|
1042
|
+
function readJson(file) {
|
|
906
1043
|
if (!fs.existsSync(file)) throw new Error(`The template is missing ${path.basename(file)}, which scaffolding must patch.`);
|
|
907
|
-
let current;
|
|
908
1044
|
try {
|
|
909
|
-
|
|
1045
|
+
return JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
910
1046
|
} catch (err) {
|
|
911
1047
|
throw new Error(`The template's ${path.basename(file)} is not valid JSON: ${errorText(err)}`);
|
|
912
1048
|
}
|
|
913
|
-
|
|
1049
|
+
}
|
|
1050
|
+
function writeJson(file, content) {
|
|
1051
|
+
fs.writeFileSync(file, JSON.stringify(content, null, 2) + "\n", "utf-8");
|
|
914
1052
|
}
|
|
915
1053
|
/**
|
|
916
1054
|
* Which of the files a scaffold is about to write are already there.
|
|
@@ -1299,6 +1437,134 @@ async function promptForDeclaredEnv(session, declared) {
|
|
|
1299
1437
|
return answers;
|
|
1300
1438
|
}
|
|
1301
1439
|
/**
|
|
1440
|
+
* The stored credentials, if the platform still accepts them, with whatever it
|
|
1441
|
+
* said about the Tenant while answering.
|
|
1442
|
+
*
|
|
1443
|
+
* Only a refusal counts as an answer — that is the null. An unreachable gateway
|
|
1444
|
+
* says nothing about whether the credentials are good, and treating it as a dead
|
|
1445
|
+
* session would send a user who is merely offline through a login they do not
|
|
1446
|
+
* need, so it comes back as a session with nothing known about the Tenant.
|
|
1447
|
+
*/
|
|
1448
|
+
async function probeSession(creds) {
|
|
1449
|
+
try {
|
|
1450
|
+
return {
|
|
1451
|
+
creds,
|
|
1452
|
+
tenant: await fetchTenantDetails(creds)
|
|
1453
|
+
};
|
|
1454
|
+
} catch (err) {
|
|
1455
|
+
return err instanceof GatewayError && (err.status === 401 || err.status === 403) ? null : {
|
|
1456
|
+
creds,
|
|
1457
|
+
tenant: null
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Get a working session before the first question is asked.
|
|
1463
|
+
*
|
|
1464
|
+
* The credentials file existing is not the same as being logged in — a JWT
|
|
1465
|
+
* expires — and everything this command does after scaffolding needs a live
|
|
1466
|
+
* session. Checked here, the two answers a user can get are "log in now" and a
|
|
1467
|
+
* project that scaffolds without one. Checked where it used to be, at the first
|
|
1468
|
+
* write, the answer was an error *after* they had typed a webhook secret in.
|
|
1469
|
+
*
|
|
1470
|
+
* Returns null when there is no session to work with, which is a supported way
|
|
1471
|
+
* to finish: the files are still worth having.
|
|
1472
|
+
*/
|
|
1473
|
+
async function ensureSession(options) {
|
|
1474
|
+
const stored = readCredentials();
|
|
1475
|
+
if (stored) {
|
|
1476
|
+
const probed = await probeSession(stored);
|
|
1477
|
+
if (probed) return probed;
|
|
1478
|
+
}
|
|
1479
|
+
console.log(stored ? "\n[wawesome] Your session has expired." : "\n[wawesome] You're not logged in yet — that's what a deploy needs.");
|
|
1480
|
+
if (!isInteractive()) {
|
|
1481
|
+
console.log("[wawesome] Run 'wawesome login', then 'wawesome deploy'.");
|
|
1482
|
+
return null;
|
|
1483
|
+
}
|
|
1484
|
+
let wantsLogin;
|
|
1485
|
+
try {
|
|
1486
|
+
wantsLogin = await confirm({
|
|
1487
|
+
message: "Log in now?",
|
|
1488
|
+
default: true
|
|
1489
|
+
});
|
|
1490
|
+
} catch {
|
|
1491
|
+
console.log("[wawesome] Cancelled. Nothing was written.");
|
|
1492
|
+
process.exit(130);
|
|
1493
|
+
}
|
|
1494
|
+
if (!wantsLogin) return null;
|
|
1495
|
+
try {
|
|
1496
|
+
await login({
|
|
1497
|
+
api: options.api,
|
|
1498
|
+
verbose: options.verbose
|
|
1499
|
+
});
|
|
1500
|
+
} catch (err) {
|
|
1501
|
+
console.error(`[wawesome] Login failed: ${errorText(err)}`);
|
|
1502
|
+
return null;
|
|
1503
|
+
}
|
|
1504
|
+
const fresh = readCredentials();
|
|
1505
|
+
return fresh ? probeSession(fresh) : null;
|
|
1506
|
+
}
|
|
1507
|
+
/** Bound on the rename loop, so a name the gateway keeps refusing ends the offer. */
|
|
1508
|
+
const MAX_RENAME_ATTEMPTS = 3;
|
|
1509
|
+
/** Show the address the Function will answer on, once it is deployed. */
|
|
1510
|
+
function announceUrl(creds, slug, appSlug, functionName) {
|
|
1511
|
+
console.log("\n[wawesome] Your Function will answer on:\n");
|
|
1512
|
+
console.log(` \x1b[36m${publicInvokeUrl(creds.gateway_url, slug, appSlug, functionName)}\x1b[0m\n`);
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Show the URL, and offer to fix the workspace address while it can still be fixed.
|
|
1516
|
+
*
|
|
1517
|
+
* This is the last moment that address is free: it locks the first time a
|
|
1518
|
+
* version is promoted, which is what the deploy at the end of this flow does.
|
|
1519
|
+
* Asking here rather than at signup is the whole reason naming was deferred —
|
|
1520
|
+
* it is client-facing branding, and nobody chooses it well before seeing the
|
|
1521
|
+
* product work. The URL comes first so the offer is about something concrete.
|
|
1522
|
+
*
|
|
1523
|
+
* The lock is read from the gateway rather than guessed, so a user who cannot
|
|
1524
|
+
* rename is told why instead of being walked into a rejection. Nothing here can
|
|
1525
|
+
* fail the flow: the project is already on disk and the deploy still works under
|
|
1526
|
+
* the address the workspace has.
|
|
1527
|
+
*/
|
|
1528
|
+
async function offerWorkspaceAddress(session, creds, tenant, appSlug, functionName) {
|
|
1529
|
+
const current = tenant?.tenant_slug ?? creds.tenant_slug;
|
|
1530
|
+
if (!current) return;
|
|
1531
|
+
announceUrl(creds, current, appSlug, functionName);
|
|
1532
|
+
if (!tenant) {
|
|
1533
|
+
console.log(` '${current}' is your workspace address. Whether it can still be changed`);
|
|
1534
|
+
console.log(" is a question for the gateway, which did not answer.\n");
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
if (tenant.slug_locked) {
|
|
1538
|
+
console.log(` 🔒 '${current}' is fixed — a Function version has been promoted, and live URLs already carry it.\n`);
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
console.log(` '${current}' is your workspace address — the part of that URL that is yours`);
|
|
1542
|
+
console.log(" rather than this project's, and this is the moment to change it: it locks");
|
|
1543
|
+
console.log(" for good the first time you deploy, because live URLs carry it.");
|
|
1544
|
+
console.log(" Press enter to keep it.\n");
|
|
1545
|
+
let refusal;
|
|
1546
|
+
for (let attempt = 0; attempt < MAX_RENAME_ATTEMPTS; attempt++) {
|
|
1547
|
+
const answer = await session.ask(" Workspace address?", current);
|
|
1548
|
+
if (answer === current) {
|
|
1549
|
+
console.log(`[wawesome] Keeping '${current}'.`);
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
try {
|
|
1553
|
+
const renamed = await renameTenantSlug(creds, answer);
|
|
1554
|
+
console.log(`\n[wawesome] ✅ Workspace renamed to '${renamed.tenant_slug}'.`);
|
|
1555
|
+
announceUrl(creds, renamed.tenant_slug, appSlug, functionName);
|
|
1556
|
+
return;
|
|
1557
|
+
} catch (err) {
|
|
1558
|
+
console.log(`[wawesome] ${errorText(err)}`);
|
|
1559
|
+
refusal = err instanceof GatewayError ? err.reason : void 0;
|
|
1560
|
+
const { text, retryable } = renameAdvice(refusal);
|
|
1561
|
+
if (text) console.log(`[wawesome] ${text}`);
|
|
1562
|
+
if (!retryable) break;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
console.log(refusal === "locked" ? `[wawesome] Keeping '${current}'.` : `[wawesome] Keeping '${current}'. Change it later with: wawesome workspace rename <name>`);
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1302
1568
|
* Satisfy the template's declarations against the platform.
|
|
1303
1569
|
*
|
|
1304
1570
|
* The manifest declares; nothing here is template-specific. Each declaration
|
|
@@ -1367,10 +1633,11 @@ async function initFromTemplate(templateName, options) {
|
|
|
1367
1633
|
force: true
|
|
1368
1634
|
});
|
|
1369
1635
|
process.once("exit", sweepStaging);
|
|
1370
|
-
const session = openPromptSession();
|
|
1371
1636
|
let functionName;
|
|
1372
1637
|
let appSlug;
|
|
1373
1638
|
let manifest;
|
|
1639
|
+
let tenantSession;
|
|
1640
|
+
let answers = [];
|
|
1374
1641
|
try {
|
|
1375
1642
|
let files;
|
|
1376
1643
|
try {
|
|
@@ -1384,39 +1651,47 @@ async function initFromTemplate(templateName, options) {
|
|
|
1384
1651
|
}
|
|
1385
1652
|
const collisions = collidingPaths(cwd, files);
|
|
1386
1653
|
if (collisions.length > 0) fail(`${collisions.join(", ")} already exist${collisions.length === 1 ? "s" : ""} here.`, "Run this in an empty directory, or move those files aside first. Nothing was written.");
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
appSlug = await promptForSlug(session, "App slug", dirName);
|
|
1654
|
+
tenantSession = await ensureSession(options);
|
|
1655
|
+
const session = openPromptSession();
|
|
1390
1656
|
try {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1657
|
+
functionName = await promptForSlug(session, "Function name", readFunctionConfig(staging)?.function || dirName);
|
|
1658
|
+
console.log(" (an App groups the Functions of one project — its slug is part of the public URL)");
|
|
1659
|
+
appSlug = await promptForSlug(session, "App slug", dirName);
|
|
1660
|
+
let repinned = null;
|
|
1661
|
+
try {
|
|
1662
|
+
fs.cpSync(staging, cwd, { recursive: true });
|
|
1663
|
+
applyTemplateIdentity(cwd, {
|
|
1664
|
+
app: appSlug,
|
|
1665
|
+
functionName
|
|
1666
|
+
});
|
|
1667
|
+
repinned = alignCliDependency(cwd, CLI_VERSION);
|
|
1668
|
+
} catch (err) {
|
|
1669
|
+
fail(errorText(err), `Some of '${template.name}' may have been written to ${cwd}.`);
|
|
1670
|
+
}
|
|
1671
|
+
console.log(`\n[wawesome] ✅ Scaffolded ${files.length} files from '${template.name}'.`);
|
|
1672
|
+
if (repinned) console.log(`[wawesome] Using wawesome ${repinned.to} — the template pinned ${repinned.from}.`);
|
|
1673
|
+
if (tenantSession) await offerWorkspaceAddress(session, tenantSession.creds, tenantSession.tenant, appSlug, functionName);
|
|
1674
|
+
answers = tenantSession ? await promptForDeclaredEnv(session, manifest.env) : [];
|
|
1675
|
+
} finally {
|
|
1676
|
+
session.close();
|
|
1398
1677
|
}
|
|
1399
|
-
console.log(`\n[wawesome] ✅ Scaffolded ${files.length} files from '${template.name}'.`);
|
|
1400
1678
|
} finally {
|
|
1401
1679
|
sweepStaging();
|
|
1402
1680
|
process.off("exit", sweepStaging);
|
|
1403
1681
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
try {
|
|
1407
|
-
creds = readCredentials();
|
|
1408
|
-
if (creds) answers = await promptForDeclaredEnv(session, manifest.env);
|
|
1409
|
-
} finally {
|
|
1410
|
-
session.close();
|
|
1411
|
-
}
|
|
1412
|
-
if (!creds) {
|
|
1682
|
+
if (!tenantSession) {
|
|
1683
|
+
const installed = options.install === false ? null : installDependencies(cwd, { verbose: options.verbose });
|
|
1413
1684
|
console.log("\n[wawesome] 🎉 Project ready. Next steps:");
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1685
|
+
[
|
|
1686
|
+
...installed?.ok ? [] : [installed?.command ?? "npm install"],
|
|
1687
|
+
"wawesome login",
|
|
1688
|
+
"wawesome deploy"
|
|
1689
|
+
].forEach((step, index) => console.log(` ${index + 1}. ${step}`));
|
|
1690
|
+
console.log("");
|
|
1417
1691
|
return;
|
|
1418
1692
|
}
|
|
1419
|
-
await wireUp(creds, appSlug, manifest, answers);
|
|
1693
|
+
await wireUp(tenantSession.creds, appSlug, manifest, answers);
|
|
1694
|
+
if (options.install !== false) installDependencies(cwd, { verbose: options.verbose });
|
|
1420
1695
|
const result = await deploy(void 0, {
|
|
1421
1696
|
out: "dist/index.js",
|
|
1422
1697
|
verbose: options.verbose
|
|
@@ -1491,7 +1766,7 @@ export default {
|
|
|
1491
1766
|
typecheck: "tsc --noEmit"
|
|
1492
1767
|
},
|
|
1493
1768
|
devDependencies: {
|
|
1494
|
-
"wawesome":
|
|
1769
|
+
"wawesome": `^${CLI_VERSION}`,
|
|
1495
1770
|
typescript: "^7.0.0"
|
|
1496
1771
|
}
|
|
1497
1772
|
};
|
|
@@ -1524,10 +1799,14 @@ export default {
|
|
|
1524
1799
|
fs.writeFileSync(gitignorePath, "node_modules\ndist\n", "utf-8");
|
|
1525
1800
|
console.log("[wawesome] ✅ Created .gitignore");
|
|
1526
1801
|
}
|
|
1802
|
+
const installed = options.install === false ? null : installDependencies(cwd, { verbose: options.verbose });
|
|
1527
1803
|
console.log("\n[wawesome] 🎉 Project initialized! Next steps:");
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1804
|
+
[
|
|
1805
|
+
...installed?.ok ? [] : [installed?.command ?? "npm install"],
|
|
1806
|
+
"wawesome login",
|
|
1807
|
+
"wawesome deploy"
|
|
1808
|
+
].forEach((step, index) => console.log(` ${index + 1}. ${step}`));
|
|
1809
|
+
console.log("");
|
|
1531
1810
|
}
|
|
1532
1811
|
//#endregion
|
|
1533
1812
|
//#region src/version.ts
|
|
@@ -2228,20 +2507,8 @@ async function renameWorkspace(slug, options) {
|
|
|
2228
2507
|
} catch (err) {
|
|
2229
2508
|
if (err instanceof GatewayError) {
|
|
2230
2509
|
console.error(`\n[wawesome] \x1b[31m${err.message}\x1b[0m`);
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
console.error("[wawesome] Pick a different name.");
|
|
2234
|
-
break;
|
|
2235
|
-
case "reserved":
|
|
2236
|
-
console.error("[wawesome] Pick a different name.");
|
|
2237
|
-
break;
|
|
2238
|
-
case "malformed":
|
|
2239
|
-
console.error("[wawesome] Use lowercase letters, numbers and single hyphens.");
|
|
2240
|
-
break;
|
|
2241
|
-
case "locked":
|
|
2242
|
-
console.error("[wawesome] A deploy landed while this was running, which fixed the address for good.");
|
|
2243
|
-
break;
|
|
2244
|
-
}
|
|
2510
|
+
const { text } = renameAdvice(err.reason);
|
|
2511
|
+
if (text) console.error(`[wawesome] ${text}`);
|
|
2245
2512
|
console.error("");
|
|
2246
2513
|
process.exit(1);
|
|
2247
2514
|
return;
|
|
@@ -2279,7 +2546,7 @@ cli.command("logout", "Clear stored authentication credentials").action(() => lo
|
|
|
2279
2546
|
cli.command("whoami", "Show current login session info").action(() => whoami());
|
|
2280
2547
|
cli.command("workspace [action] [name]", "Show the workspace, or rename its public address").usage("workspace <action> [name]\n\nActions:\n show Show the workspace name, address, and whether it can still change\n rename <name> Change the public address, while nothing live depends on it").example("wawesome workspace").example("wawesome workspace rename northwind").option("-v, --verbose", "Enable verbose debug output").action((action, name, options) => workspaceCommand(action, name, options));
|
|
2281
2548
|
cli.command("templates [action]", "Browse the template catalog").usage("templates [action]\n\nActions:\n list (ls) Show every available template (default)").example("wawesome templates").example("wawesome templates list").option("--api <url>", "API URL (default: https://api.wawesome.io)").option("-v, --verbose", "Enable verbose debug output").action((action, options) => templatesCommand(action, options));
|
|
2282
|
-
cli.command("init", "Scaffold a new function project in the current directory").usage("init [options]\n\nWith --template, the project is fetched from the template catalog, wired up\nfrom what the template declares it needs, and deployed. Run 'wawesome templates'\nto see what is available.").example("wawesome init").example("wawesome init --template stripe-webhook").option("-t, --template <name>", "Scaffold from a catalog template and deploy it").option("--api <url>", "API URL (default: https://api.wawesome.io)").option("-v, --verbose", "Enable verbose debug output").action((options) => init(options));
|
|
2549
|
+
cli.command("init", "Scaffold a new function project in the current directory").usage("init [options]\n\nWith --template, the project is fetched from the template catalog, wired up\nfrom what the template declares it needs, and deployed. Run 'wawesome templates'\nto see what is available.").example("wawesome init").example("wawesome init --template stripe-webhook").option("-t, --template <name>", "Scaffold from a catalog template and deploy it").option("--api <url>", "API URL (default: https://api.wawesome.io)").option("--no-install", "Skip installing dependencies after scaffolding").option("-v, --verbose", "Enable verbose debug output").action((options) => init(options));
|
|
2283
2550
|
cli.command("logs [function-name-or-invocation-id]", "View invocation history, fetch log output, or follow live").usage(`logs [target] [options]
|
|
2284
2551
|
|
|
2285
2552
|
The target argument determines what the command does:
|
|
@@ -2302,7 +2569,7 @@ cli.command("logs [function-name-or-invocation-id]", "View invocation history, f
|
|
|
2302
2569
|
invocations — new output appears each time the function runs, no need to catch a
|
|
2303
2570
|
specific invocation. Press Ctrl-C to stop at any time.`).option("-f, --follow", "Stream live output (tail -f style). Follows a running invocation or waits for the next one. Ctrl-C to stop").option("-i, --invocation <id>", "Fetch stdout/stderr log body for a specific invocation ID").option("-a, --app <app>", "App slug override (defaults to wawesome-function.json)").option("-s, --status <status>", "Filter invocations by status (success, error, timeout, running)").option("--success", "Shorthand for --status success").option("--error", "Shorthand for --status error").option("--timeout", "Shorthand for --status timeout").option("--running", "Shorthand for --status running").option("-v, --verbose", "Enable verbose debug output").action((target, options) => logsCommand(target, options));
|
|
2304
2571
|
cli.help();
|
|
2305
|
-
cli.version(
|
|
2572
|
+
cli.version(CLI_VERSION);
|
|
2306
2573
|
cli.parse();
|
|
2307
2574
|
//#endregion
|
|
2308
2575
|
export {};
|