wawesome 0.0.9 → 0.0.11
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 +15 -0
- package/dist/index.mjs +72 -23
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -200,6 +200,21 @@ Every project directory includes a `wawesome-function.json` file generated durin
|
|
|
200
200
|
`app` is the App this Function is deployed into, and it is client-facing — every deploy from this
|
|
201
201
|
directory is scoped to it.
|
|
202
202
|
|
|
203
|
+
### Reserved headers
|
|
204
|
+
|
|
205
|
+
`x-wawesome-*` belongs to the platform in both directions. It is stripped off the request before your
|
|
206
|
+
handler sees it, and off your response before the caller does — so **do not name a header of your own
|
|
207
|
+
on that prefix**: it is dropped silently rather than rejected, and you will not get an error telling
|
|
208
|
+
you why it vanished.
|
|
209
|
+
|
|
210
|
+
Three headers arrive or leave on it, and the stripping is what makes them worth trusting:
|
|
211
|
+
|
|
212
|
+
| Header | Direction | What it means |
|
|
213
|
+
| --- | --- | --- |
|
|
214
|
+
| `x-wawesome-forwarded-prefix` | inbound | The mount that was stripped from the path. Join it to the path you observe to rebuild the caller's URL. |
|
|
215
|
+
| `x-wawesome-invocation-id` | outbound | The id of this run — the key to fetch its logs with `npx wawesome logs --invocation <id>`. |
|
|
216
|
+
| `x-wawesome-error` | outbound | Present only when the platform failed, never when your Function did. Its *absence* means the status on the wire is yours. |
|
|
217
|
+
|
|
203
218
|
### Local Development / Gateway Overrides
|
|
204
219
|
|
|
205
220
|
If you are running a local gateway or self-hosted instance, you can configure your CLI Gateway URL using any of the
|
package/dist/index.mjs
CHANGED
|
@@ -161,7 +161,7 @@ async function buildJs(entryInput, options) {
|
|
|
161
161
|
* that has to name this version — `--version`, the dependency a scaffolded
|
|
162
162
|
* project pins — reads it here, so a release bumps one file.
|
|
163
163
|
*/
|
|
164
|
-
const CLI_VERSION = "0.0.
|
|
164
|
+
const CLI_VERSION = "0.0.11";
|
|
165
165
|
//#endregion
|
|
166
166
|
//#region src/prompt.ts
|
|
167
167
|
/**
|
|
@@ -335,10 +335,6 @@ async function renameTenantSlug(creds, slug) {
|
|
|
335
335
|
});
|
|
336
336
|
return result;
|
|
337
337
|
}
|
|
338
|
-
/** The address a deployed Function answers on, for a workspace and app. */
|
|
339
|
-
function publicInvokeUrl(gatewayUrl, tenantSlug, appSlug, functionName) {
|
|
340
|
-
return `${gatewayUrl}/v1/s/${encodeURIComponent(tenantSlug)}/apps/${encodeURIComponent(appSlug)}/functions/${encodeURIComponent(functionName)}/invoke`;
|
|
341
|
-
}
|
|
342
338
|
//#endregion
|
|
343
339
|
//#region src/auth.ts
|
|
344
340
|
/**
|
|
@@ -548,6 +544,22 @@ async function whoami() {
|
|
|
548
544
|
console.log(` Gateway: ${creds.gateway_url}\n`);
|
|
549
545
|
}
|
|
550
546
|
//#endregion
|
|
547
|
+
//#region ../shared/public-address.ts
|
|
548
|
+
const INVOCATION_PREFIX = "/x";
|
|
549
|
+
const SUBTREE_NOTE = "Every path beneath this address reaches the Function.";
|
|
550
|
+
function mountBase(origin, tenantSlug) {
|
|
551
|
+
return `${origin.replace(/\/+$/, "")}${INVOCATION_PREFIX}/${encodeURIComponent(tenantSlug)}`;
|
|
552
|
+
}
|
|
553
|
+
function publicAddress(origin, tenantSlug, appSlug, functionSlug) {
|
|
554
|
+
const base = `${mountBase(origin, tenantSlug)}/${encodeURIComponent(appSlug)}`;
|
|
555
|
+
if (functionSlug === "root") return base;
|
|
556
|
+
return `${base}/${encodeURIComponent(functionSlug)}`;
|
|
557
|
+
}
|
|
558
|
+
/** The same address with the app and function still to be chosen. */
|
|
559
|
+
function publicAddressTemplate(origin, tenantSlug) {
|
|
560
|
+
return `${mountBase(origin, tenantSlug)}/<app>/<function>`;
|
|
561
|
+
}
|
|
562
|
+
//#endregion
|
|
551
563
|
//#region src/deploy.ts
|
|
552
564
|
/**
|
|
553
565
|
* Deploy a function: build → upload JS to gateway → promote.
|
|
@@ -644,10 +656,10 @@ async function deploy(entryInput, options) {
|
|
|
644
656
|
}
|
|
645
657
|
process.exit(1);
|
|
646
658
|
}
|
|
647
|
-
let
|
|
659
|
+
let address = null;
|
|
648
660
|
try {
|
|
649
661
|
const { slug } = await resolveWorkspace(creds);
|
|
650
|
-
|
|
662
|
+
address = publicAddress(creds.gateway_url, slug, app, funcName);
|
|
651
663
|
} catch (err) {
|
|
652
664
|
if (isVerbose) console.log(`[wawesome:verbose] Could not resolve the workspace address: ${err instanceof Error ? err.message : err}`);
|
|
653
665
|
}
|
|
@@ -657,13 +669,16 @@ async function deploy(entryInput, options) {
|
|
|
657
669
|
console.log(`\n App: ${app}`);
|
|
658
670
|
console.log(` Function: ${funcName}`);
|
|
659
671
|
if (version !== void 0) console.log(` Version: ${version}`);
|
|
660
|
-
if (
|
|
672
|
+
if (address) {
|
|
673
|
+
console.log(`\n URL: \x1b[36m${address}\x1b[0m`);
|
|
674
|
+
console.log(` ${SUBTREE_NOTE}`);
|
|
675
|
+
}
|
|
661
676
|
console.log("======================================================\n");
|
|
662
677
|
return {
|
|
663
678
|
app,
|
|
664
679
|
functionName: funcName,
|
|
665
680
|
version,
|
|
666
|
-
|
|
681
|
+
address
|
|
667
682
|
};
|
|
668
683
|
}
|
|
669
684
|
//#endregion
|
|
@@ -1014,11 +1029,9 @@ function alignCliDependency(dir, version) {
|
|
|
1014
1029
|
const file = path.join(dir, PACKAGE_FILE);
|
|
1015
1030
|
const pkg = readJson(file);
|
|
1016
1031
|
const wanted = `^${version}`;
|
|
1017
|
-
for (const field of
|
|
1032
|
+
for (const { field, range } of declaredCliRanges(pkg)) {
|
|
1033
|
+
if (range === wanted) continue;
|
|
1018
1034
|
const deps = pkg[field];
|
|
1019
|
-
if (!isRecord(deps)) continue;
|
|
1020
|
-
const current = deps[CLI_PACKAGE];
|
|
1021
|
-
if (typeof current !== "string" || current === wanted) continue;
|
|
1022
1035
|
writeJson(file, {
|
|
1023
1036
|
...pkg,
|
|
1024
1037
|
[field]: {
|
|
@@ -1027,12 +1040,22 @@ function alignCliDependency(dir, version) {
|
|
|
1027
1040
|
}
|
|
1028
1041
|
});
|
|
1029
1042
|
return {
|
|
1030
|
-
from:
|
|
1043
|
+
from: range,
|
|
1031
1044
|
to: wanted
|
|
1032
1045
|
};
|
|
1033
1046
|
}
|
|
1034
1047
|
return null;
|
|
1035
1048
|
}
|
|
1049
|
+
/** Where a template declares the CLI, and at which range. */
|
|
1050
|
+
function declaredCliRanges(pkg) {
|
|
1051
|
+
return ["dependencies", "devDependencies"].flatMap((field) => {
|
|
1052
|
+
const deps = pkg[field];
|
|
1053
|
+
return isRecord(deps) && typeof deps[CLI_PACKAGE] === "string" ? [{
|
|
1054
|
+
field,
|
|
1055
|
+
range: deps[CLI_PACKAGE]
|
|
1056
|
+
}] : [];
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1036
1059
|
function isRecord(value) {
|
|
1037
1060
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1038
1061
|
}
|
|
@@ -1066,9 +1089,9 @@ function collidingPaths(dir, relativePaths) {
|
|
|
1066
1089
|
* not be resolved the message still prints, with the placeholder left visible
|
|
1067
1090
|
* rather than a confident "undefined".
|
|
1068
1091
|
*/
|
|
1069
|
-
function renderPostDeploy(postDeploy,
|
|
1070
|
-
if (!
|
|
1071
|
-
return postDeploy.split("{{url}}").join(
|
|
1092
|
+
function renderPostDeploy(postDeploy, address) {
|
|
1093
|
+
if (!address) return postDeploy;
|
|
1094
|
+
return postDeploy.split("{{url}}").join(address);
|
|
1072
1095
|
}
|
|
1073
1096
|
/**
|
|
1074
1097
|
* Normalise arbitrary input into a legal DNS label, returning an empty string
|
|
@@ -1509,7 +1532,8 @@ const MAX_RENAME_ATTEMPTS = 3;
|
|
|
1509
1532
|
/** Show the address the Function will answer on, once it is deployed. */
|
|
1510
1533
|
function announceUrl(creds, slug, appSlug, functionName) {
|
|
1511
1534
|
console.log("\n[wawesome] Your Function will answer on:\n");
|
|
1512
|
-
console.log(` \x1b[36m${
|
|
1535
|
+
console.log(` \x1b[36m${publicAddress(creds.gateway_url, slug, appSlug, functionName)}\x1b[0m`);
|
|
1536
|
+
console.log(` ${SUBTREE_NOTE}\n`);
|
|
1513
1537
|
}
|
|
1514
1538
|
/**
|
|
1515
1539
|
* Show the URL, and offer to fix the workspace address while it can still be fixed.
|
|
@@ -1697,7 +1721,7 @@ async function initFromTemplate(templateName, options) {
|
|
|
1697
1721
|
verbose: options.verbose
|
|
1698
1722
|
});
|
|
1699
1723
|
if (manifest.post_deploy) {
|
|
1700
|
-
console.log(renderPostDeploy(manifest.post_deploy, result.
|
|
1724
|
+
console.log(renderPostDeploy(manifest.post_deploy, result.address));
|
|
1701
1725
|
console.log("");
|
|
1702
1726
|
}
|
|
1703
1727
|
}
|
|
@@ -1714,7 +1738,10 @@ async function init(options) {
|
|
|
1714
1738
|
let functionName;
|
|
1715
1739
|
let appSlug;
|
|
1716
1740
|
try {
|
|
1717
|
-
|
|
1741
|
+
if (options.root) {
|
|
1742
|
+
functionName = "root";
|
|
1743
|
+
console.log(" Function name? root (locked by --root)");
|
|
1744
|
+
} else functionName = await promptForSlug(session, "Function name", dirName);
|
|
1718
1745
|
console.log(" (an App groups the Functions of one project — its slug is part of the public URL)");
|
|
1719
1746
|
appSlug = await promptForSlug(session, "App slug", dirName);
|
|
1720
1747
|
} finally {
|
|
@@ -1738,7 +1765,26 @@ async function init(options) {
|
|
|
1738
1765
|
if (fs.existsSync(indexPath)) console.log("[wawesome] src/index.ts already exists, skipping.");
|
|
1739
1766
|
else {
|
|
1740
1767
|
fs.mkdirSync(srcDir, { recursive: true });
|
|
1741
|
-
fs.writeFileSync(indexPath, `/**
|
|
1768
|
+
fs.writeFileSync(indexPath, options.root ? `/**
|
|
1769
|
+
* Root Function Router
|
|
1770
|
+
* Handles all requests that don't match a specific function path.
|
|
1771
|
+
*/
|
|
1772
|
+
export default {
|
|
1773
|
+
async fetch(request: Request): Promise<Response> {
|
|
1774
|
+
const url = new URL(request.url);
|
|
1775
|
+
|
|
1776
|
+
// Example router
|
|
1777
|
+
switch (url.pathname) {
|
|
1778
|
+
case "/":
|
|
1779
|
+
return Response.json({ message: "Welcome to the App Root!" });
|
|
1780
|
+
case "/health":
|
|
1781
|
+
return Response.json({ status: "ok" });
|
|
1782
|
+
default:
|
|
1783
|
+
return Response.json({ error: "Not Found" }, { status: 404 });
|
|
1784
|
+
}
|
|
1785
|
+
},
|
|
1786
|
+
};
|
|
1787
|
+
` : `/**
|
|
1742
1788
|
* Serverless Function Handler
|
|
1743
1789
|
* Standard HTTP fetch request handler
|
|
1744
1790
|
*/
|
|
@@ -2465,7 +2511,10 @@ async function showWorkspace(options) {
|
|
|
2465
2511
|
console.log(` Name: ${tenant.name}`);
|
|
2466
2512
|
console.log(` Address: ${tenant.tenant_slug}`);
|
|
2467
2513
|
console.log(` Tenant: ${tenant.id}`);
|
|
2468
|
-
if (options.verbose)
|
|
2514
|
+
if (options.verbose) {
|
|
2515
|
+
console.log(` URLs: ${publicAddressTemplate(creds.gateway_url, tenant.tenant_slug)}`);
|
|
2516
|
+
console.log(` ${SUBTREE_NOTE}`);
|
|
2517
|
+
}
|
|
2469
2518
|
if (tenant.slug_locked) console.log("\n 🔒 The address is fixed — a Function version has been promoted and live URLs carry it.");
|
|
2470
2519
|
else {
|
|
2471
2520
|
console.log("\n The address can still be changed: \x1B[36mwawesome workspace rename <name>\x1B[0m");
|
|
@@ -2546,7 +2595,7 @@ cli.command("logout", "Clear stored authentication credentials").action(() => lo
|
|
|
2546
2595
|
cli.command("whoami", "Show current login session info").action(() => whoami());
|
|
2547
2596
|
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));
|
|
2548
2597
|
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));
|
|
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));
|
|
2598
|
+
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("--root", "Generate a root function router template").option("-v, --verbose", "Enable verbose debug output").action((options) => init(options));
|
|
2550
2599
|
cli.command("logs [function-name-or-invocation-id]", "View invocation history, fetch log output, or follow live").usage(`logs [target] [options]
|
|
2551
2600
|
|
|
2552
2601
|
The target argument determines what the command does:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wawesome",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "CLI tool for building and deploying serverless functions on wawesome.io platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"build": "tsdown",
|
|
17
17
|
"dev": "tsdown --watch",
|
|
18
18
|
"test": "vitest run",
|
|
19
|
+
"test:templates": "vitest run --config vitest.live.config.ts",
|
|
19
20
|
"check": "publint --pack npm",
|
|
20
21
|
"changeset": "changeset"
|
|
21
22
|
},
|