slskit-cli 1.0.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 +61 -0
- package/COMMAND.md +636 -0
- package/LICENSE +21 -0
- package/NOTICE.md +22 -0
- package/README.md +168 -0
- package/bin/slskit.js +3 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +25 -0
- package/dist/commands/base-command.d.ts +5 -0
- package/dist/commands/base-command.js +29 -0
- package/dist/commands/configure/action.d.ts +2 -0
- package/dist/commands/configure/action.js +120 -0
- package/dist/commands/configure/aws-cli.d.ts +16 -0
- package/dist/commands/configure/aws-cli.js +135 -0
- package/dist/commands/configure/index.d.ts +2 -0
- package/dist/commands/configure/index.js +20 -0
- package/dist/commands/configure/prompts.d.ts +8 -0
- package/dist/commands/configure/prompts.js +117 -0
- package/dist/commands/configure/types.d.ts +24 -0
- package/dist/commands/configure/types.js +6 -0
- package/dist/commands/deploy/action.d.ts +2 -0
- package/dist/commands/deploy/action.js +115 -0
- package/dist/commands/deploy/index.d.ts +2 -0
- package/dist/commands/deploy/index.js +23 -0
- package/dist/commands/deploy/sam-deploy.d.ts +10 -0
- package/dist/commands/deploy/sam-deploy.js +127 -0
- package/dist/commands/deploy/scope.d.ts +9 -0
- package/dist/commands/deploy/scope.js +34 -0
- package/dist/commands/deploy/types.d.ts +20 -0
- package/dist/commands/deploy/types.js +2 -0
- package/dist/commands/env/action.d.ts +11 -0
- package/dist/commands/env/action.js +274 -0
- package/dist/commands/env/dotenv.d.ts +6 -0
- package/dist/commands/env/dotenv.js +99 -0
- package/dist/commands/env/index.d.ts +2 -0
- package/dist/commands/env/index.js +76 -0
- package/dist/commands/env/keys.d.ts +8 -0
- package/dist/commands/env/keys.js +41 -0
- package/dist/commands/env/parameters.d.ts +7 -0
- package/dist/commands/env/parameters.js +63 -0
- package/dist/commands/env/templates.d.ts +3 -0
- package/dist/commands/env/templates.js +65 -0
- package/dist/commands/env/types.d.ts +21 -0
- package/dist/commands/env/types.js +2 -0
- package/dist/commands/function/action.d.ts +2 -0
- package/dist/commands/function/action.js +71 -0
- package/dist/commands/function/generator.d.ts +8 -0
- package/dist/commands/function/generator.js +58 -0
- package/dist/commands/function/index.d.ts +2 -0
- package/dist/commands/function/index.js +26 -0
- package/dist/commands/function/manifest.d.ts +5 -0
- package/dist/commands/function/manifest.js +57 -0
- package/dist/commands/function/prompts.d.ts +18 -0
- package/dist/commands/function/prompts.js +196 -0
- package/dist/commands/function/tooling.d.ts +2 -0
- package/dist/commands/function/tooling.js +43 -0
- package/dist/commands/function/types.d.ts +52 -0
- package/dist/commands/function/types.js +2 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +19 -0
- package/dist/commands/init/action.d.ts +2 -0
- package/dist/commands/init/action.js +36 -0
- package/dist/commands/init/content.d.ts +2 -0
- package/dist/commands/init/content.js +97 -0
- package/dist/commands/init/index.d.ts +2 -0
- package/dist/commands/init/index.js +30 -0
- package/dist/commands/init/manifest.d.ts +6 -0
- package/dist/commands/init/manifest.js +147 -0
- package/dist/commands/init/prompts.d.ts +6 -0
- package/dist/commands/init/prompts.js +155 -0
- package/dist/commands/init/scaffold.d.ts +7 -0
- package/dist/commands/init/scaffold.js +85 -0
- package/dist/commands/init/templates/helpers.d.ts +9 -0
- package/dist/commands/init/templates/helpers.js +42 -0
- package/dist/commands/init/templates/node.d.ts +4 -0
- package/dist/commands/init/templates/node.js +99 -0
- package/dist/commands/init/templates/project.d.ts +8 -0
- package/dist/commands/init/templates/project.js +239 -0
- package/dist/commands/init/templates/python.d.ts +4 -0
- package/dist/commands/init/templates/python.js +65 -0
- package/dist/commands/init/templates/sam.d.ts +4 -0
- package/dist/commands/init/templates/sam.js +369 -0
- package/dist/commands/init/templates/shared-code.d.ts +6 -0
- package/dist/commands/init/templates/shared-code.js +159 -0
- package/dist/commands/init/types.d.ts +47 -0
- package/dist/commands/init/types.js +75 -0
- package/dist/commands/rm/action.d.ts +2 -0
- package/dist/commands/rm/action.js +89 -0
- package/dist/commands/rm/index.d.ts +2 -0
- package/dist/commands/rm/index.js +25 -0
- package/dist/commands/rm/plan.d.ts +19 -0
- package/dist/commands/rm/plan.js +78 -0
- package/dist/commands/rm/prompts.d.ts +4 -0
- package/dist/commands/rm/prompts.js +107 -0
- package/dist/commands/rm/types.d.ts +7 -0
- package/dist/commands/rm/types.js +2 -0
- package/dist/commands/run/action.d.ts +2 -0
- package/dist/commands/run/action.js +171 -0
- package/dist/commands/run/index.d.ts +2 -0
- package/dist/commands/run/index.js +21 -0
- package/dist/commands/run/local-template.d.ts +7 -0
- package/dist/commands/run/local-template.js +51 -0
- package/dist/commands/run/manifest.d.ts +2 -0
- package/dist/commands/run/manifest.js +20 -0
- package/dist/commands/run/resources.d.ts +20 -0
- package/dist/commands/run/resources.js +93 -0
- package/dist/commands/run/sam-cli.d.ts +10 -0
- package/dist/commands/run/sam-cli.js +183 -0
- package/dist/commands/run/types.d.ts +18 -0
- package/dist/commands/run/types.js +2 -0
- package/dist/commands/run/watch.d.ts +11 -0
- package/dist/commands/run/watch.js +131 -0
- package/dist/core/context.d.ts +15 -0
- package/dist/core/context.js +41 -0
- package/dist/core/credentials.d.ts +10 -0
- package/dist/core/credentials.js +22 -0
- package/dist/core/environments.d.ts +46 -0
- package/dist/core/environments.js +161 -0
- package/dist/core/errors.d.ts +4 -0
- package/dist/core/errors.js +11 -0
- package/dist/core/flags-docs.d.ts +2 -0
- package/dist/core/flags-docs.js +63 -0
- package/dist/core/is-ci.d.ts +4 -0
- package/dist/core/is-ci.js +16 -0
- package/dist/core/logger.d.ts +4 -0
- package/dist/core/logger.js +19 -0
- package/dist/core/run.d.ts +1 -0
- package/dist/core/run.js +27 -0
- package/dist/core/scope.d.ts +33 -0
- package/dist/core/scope.js +108 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +9 -0
- package/dist/program.d.ts +2 -0
- package/dist/program.js +26 -0
- package/package.json +110 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resourceIdFor = resourceIdFor;
|
|
4
|
+
exports.classifyChange = classifyChange;
|
|
5
|
+
exports.planRebuild = planRebuild;
|
|
6
|
+
const helpers_js_1 = require("../init/templates/helpers.js");
|
|
7
|
+
// Per-service stacks put every function behind its application's stack, so the id sam
|
|
8
|
+
// build wants is the two joined. One shared API Gateway means a single flat template
|
|
9
|
+
// instead, where the function is addressed on its own.
|
|
10
|
+
function resourceIdFor(appName, functionName, sharedApi = false) {
|
|
11
|
+
const fn = `${(0, helpers_js_1.pascal)(functionName)}Function`;
|
|
12
|
+
return sharedApi ? fn : `${(0, helpers_js_1.pascal)(appName)}Stack/${fn}`;
|
|
13
|
+
}
|
|
14
|
+
function findFunction(applications, appName, functionName) {
|
|
15
|
+
return (applications.find((app) => app.name === appName)?.functions ?? []).some((fn) => fn.name === functionName);
|
|
16
|
+
}
|
|
17
|
+
// A service module is imported by the handler of the same name, which is the shape
|
|
18
|
+
// every generated project has. Anything that does not match that 1:1 convention
|
|
19
|
+
// falls back to a full build rather than guessing which functions import it.
|
|
20
|
+
function fromService(applications, segments, sharedApi) {
|
|
21
|
+
if (segments.length !== 2) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const [appName, file] = segments;
|
|
25
|
+
const functionName = file.replace(/\.[^.]+$/, "");
|
|
26
|
+
if (!findFunction(applications, appName, functionName)) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
kind: "function",
|
|
31
|
+
resourceId: resourceIdFor(appName, functionName, sharedApi),
|
|
32
|
+
label: `${appName}/${functionName}`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function fromHandler(applications, segments, sharedApi) {
|
|
36
|
+
if (segments.length < 2) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const [appName, functionName] = segments;
|
|
40
|
+
if (!findFunction(applications, appName, functionName)) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
kind: "function",
|
|
45
|
+
resourceId: resourceIdFor(appName, functionName, sharedApi),
|
|
46
|
+
label: `${appName}/${functionName}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function normalizeRelativePath(relativePath) {
|
|
50
|
+
return relativePath.replace(/\\/g, "/");
|
|
51
|
+
}
|
|
52
|
+
function classifyChange(applications, relativePath, sharedApi = false) {
|
|
53
|
+
const normalizedPath = normalizeRelativePath(relativePath);
|
|
54
|
+
const segments = normalizedPath.split("/").filter(Boolean);
|
|
55
|
+
const base = segments[segments.length - 1] ?? normalizedPath;
|
|
56
|
+
// Routes, memory, layers and parameter values are all read when sam starts.
|
|
57
|
+
// Service templates live in templates/ and are named after their service, so it is
|
|
58
|
+
// the directory that identifies them rather than the filename.
|
|
59
|
+
const isTemplate = base === "template.yaml" ||
|
|
60
|
+
(segments[0] === "templates" && /\.ya?ml$/.test(base));
|
|
61
|
+
if (isTemplate || base === "slskit.json" || base.startsWith(".env")) {
|
|
62
|
+
return { kind: "restart", label: base };
|
|
63
|
+
}
|
|
64
|
+
if (segments[0] !== "src") {
|
|
65
|
+
return { kind: "full", label: normalizedPath };
|
|
66
|
+
}
|
|
67
|
+
const area = segments[1];
|
|
68
|
+
const rest = segments.slice(2);
|
|
69
|
+
if (area === "functions") {
|
|
70
|
+
return (fromHandler(applications, rest, sharedApi) ?? { kind: "full", label: normalizedPath });
|
|
71
|
+
}
|
|
72
|
+
if (area === "services") {
|
|
73
|
+
return (fromService(applications, rest, sharedApi) ?? { kind: "full", label: normalizedPath });
|
|
74
|
+
}
|
|
75
|
+
// src/shared is published as a layer attached to every function in every stack.
|
|
76
|
+
return { kind: "full", label: normalizedPath };
|
|
77
|
+
}
|
|
78
|
+
// One decision for a whole batch of file events: the most disruptive change wins,
|
|
79
|
+
// and a batch of ordinary edits collapses to the set of functions to rebuild.
|
|
80
|
+
function planRebuild(changes) {
|
|
81
|
+
const labels = [...new Set(changes.map((change) => change.label))];
|
|
82
|
+
if (changes.some((change) => change.kind === "restart")) {
|
|
83
|
+
return { kind: "restart", resourceIds: [], labels };
|
|
84
|
+
}
|
|
85
|
+
if (changes.some((change) => change.kind === "full")) {
|
|
86
|
+
return { kind: "full", resourceIds: [], labels };
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
kind: "function",
|
|
90
|
+
resourceIds: [...new Set(changes.map((change) => change.resourceId))],
|
|
91
|
+
labels,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process";
|
|
2
|
+
export declare function ensureSamCliInstalled(): Promise<void>;
|
|
3
|
+
export interface BuildTarget {
|
|
4
|
+
template?: string;
|
|
5
|
+
buildDir?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function samBuild(cwd: string, target?: BuildTarget): void;
|
|
8
|
+
export declare function samBuildResource(cwd: string, resourceId: string, target?: BuildTarget): boolean;
|
|
9
|
+
export declare function samRebuild(cwd: string, target?: BuildTarget): boolean;
|
|
10
|
+
export declare function startLocalApi(cwd: string, port: number, parameterOverrides?: string[], template?: string): ChildProcess;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ensureSamCliInstalled = ensureSamCliInstalled;
|
|
7
|
+
exports.samBuild = samBuild;
|
|
8
|
+
exports.samBuildResource = samBuildResource;
|
|
9
|
+
exports.samRebuild = samRebuild;
|
|
10
|
+
exports.startLocalApi = startLocalApi;
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
13
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
+
const errors_js_1 = require("../../core/errors.js");
|
|
15
|
+
const logger_js_1 = require("../../core/logger.js");
|
|
16
|
+
const USE_SHELL = process.platform === "win32";
|
|
17
|
+
const GUIDE_URLS = {
|
|
18
|
+
darwin: "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli-mac.html",
|
|
19
|
+
linux: "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli-linux.html",
|
|
20
|
+
win32: "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli-windows.html",
|
|
21
|
+
};
|
|
22
|
+
const GENERAL_GUIDE_URL = "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html";
|
|
23
|
+
function platformGuideUrl() {
|
|
24
|
+
return GUIDE_URLS[process.platform] ?? GENERAL_GUIDE_URL;
|
|
25
|
+
}
|
|
26
|
+
function printManualGuide() {
|
|
27
|
+
logger_js_1.logger.info(`\nInstall AWS SAM CLI manually, then re-run "slskit run":\n ${platformGuideUrl()}\n\nVerify the install with: sam --version\n`);
|
|
28
|
+
}
|
|
29
|
+
function commandExists(command) {
|
|
30
|
+
const result = (0, node_child_process_1.spawnSync)(command, ["--version"], {
|
|
31
|
+
stdio: "ignore",
|
|
32
|
+
shell: USE_SHELL,
|
|
33
|
+
});
|
|
34
|
+
return !result.error;
|
|
35
|
+
}
|
|
36
|
+
function isSamCliAvailable() {
|
|
37
|
+
const result = (0, node_child_process_1.spawnSync)("sam", ["--version"], {
|
|
38
|
+
stdio: "ignore",
|
|
39
|
+
shell: USE_SHELL,
|
|
40
|
+
});
|
|
41
|
+
return !result.error && result.status === 0;
|
|
42
|
+
}
|
|
43
|
+
function installViaBrew() {
|
|
44
|
+
if (!commandExists("brew")) {
|
|
45
|
+
logger_js_1.logger.info("Homebrew was not found (https://brew.sh). Falling back to a manual install.");
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
logger_js_1.logger.info("\n> brew install aws/tap/aws-sam-cli");
|
|
49
|
+
const result = (0, node_child_process_1.spawnSync)("brew", ["install", "aws/tap/aws-sam-cli"], {
|
|
50
|
+
stdio: "inherit",
|
|
51
|
+
});
|
|
52
|
+
return !result.error && result.status === 0;
|
|
53
|
+
}
|
|
54
|
+
function installViaSnap() {
|
|
55
|
+
if (!commandExists("snap")) {
|
|
56
|
+
logger_js_1.logger.info("snap was not found. Falling back to a manual install.");
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
logger_js_1.logger.info("\n> sudo snap install aws-sam-cli --classic");
|
|
60
|
+
const result = (0, node_child_process_1.spawnSync)("sudo", ["snap", "install", "aws-sam-cli", "--classic"], {
|
|
61
|
+
stdio: "inherit",
|
|
62
|
+
});
|
|
63
|
+
return !result.error && result.status === 0;
|
|
64
|
+
}
|
|
65
|
+
function attemptInstall() {
|
|
66
|
+
if (process.platform === "darwin") {
|
|
67
|
+
return installViaBrew();
|
|
68
|
+
}
|
|
69
|
+
if (process.platform === "linux") {
|
|
70
|
+
return installViaSnap();
|
|
71
|
+
}
|
|
72
|
+
logger_js_1.logger.info(`Automatic install isn't available on "${process.platform}".`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
async function ensureSamCliInstalled() {
|
|
76
|
+
if (isSamCliAvailable()) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
logger_js_1.logger.info("AWS SAM CLI was not found on your PATH.");
|
|
80
|
+
if (!process.stdin.isTTY) {
|
|
81
|
+
printManualGuide();
|
|
82
|
+
throw new errors_js_1.CliError("AWS SAM CLI is required to run this project locally.");
|
|
83
|
+
}
|
|
84
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
85
|
+
const shouldInstall = await confirm({
|
|
86
|
+
message: "Install AWS SAM CLI now?",
|
|
87
|
+
default: true,
|
|
88
|
+
});
|
|
89
|
+
if (!shouldInstall) {
|
|
90
|
+
printManualGuide();
|
|
91
|
+
throw new errors_js_1.CliError("AWS SAM CLI is required to run this project locally.");
|
|
92
|
+
}
|
|
93
|
+
const installed = attemptInstall() && isSamCliAvailable();
|
|
94
|
+
if (!installed) {
|
|
95
|
+
printManualGuide();
|
|
96
|
+
throw new errors_js_1.CliError("AWS SAM CLI installation did not complete.");
|
|
97
|
+
}
|
|
98
|
+
logger_js_1.logger.info("\nAWS SAM CLI installed.\n");
|
|
99
|
+
}
|
|
100
|
+
function targetArgs(target) {
|
|
101
|
+
const args = [];
|
|
102
|
+
if (target.template) {
|
|
103
|
+
args.push("-t", target.template);
|
|
104
|
+
}
|
|
105
|
+
if (target.buildDir) {
|
|
106
|
+
args.push("--build-dir", target.buildDir);
|
|
107
|
+
}
|
|
108
|
+
return args;
|
|
109
|
+
}
|
|
110
|
+
function samBuild(cwd, target = {}) {
|
|
111
|
+
const args = ["build", ...targetArgs(target)];
|
|
112
|
+
logger_js_1.logger.info(`\n> sam ${args.join(" ")}`);
|
|
113
|
+
const result = (0, node_child_process_1.spawnSync)("sam", args, {
|
|
114
|
+
cwd,
|
|
115
|
+
stdio: "inherit",
|
|
116
|
+
shell: USE_SHELL,
|
|
117
|
+
});
|
|
118
|
+
if (result.error) {
|
|
119
|
+
throw new errors_js_1.CliError(`Failed to run sam build: ${result.error.message}`);
|
|
120
|
+
}
|
|
121
|
+
if (result.status !== 0) {
|
|
122
|
+
throw new errors_js_1.CliError(`sam build exited with code ${result.status ?? 1}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const PARTIAL_BUILD_DIR = node_path_1.default.join(".aws-sam", "slskit-partial");
|
|
126
|
+
// "sam build <ResourceId>" empties the whole build directory and puts back only the
|
|
127
|
+
// resource it was asked for, which would leave every other function unservable. So
|
|
128
|
+
// the scoped build goes to a scratch directory and just that one artifact is moved
|
|
129
|
+
// into place -- the running local API picks it up on the next request.
|
|
130
|
+
function samBuildResource(cwd, resourceId, target = {}) {
|
|
131
|
+
const scratch = node_path_1.default.join(cwd, PARTIAL_BUILD_DIR);
|
|
132
|
+
node_fs_1.default.rmSync(scratch, { recursive: true, force: true });
|
|
133
|
+
const args = ["build", resourceId, "--build-dir", PARTIAL_BUILD_DIR];
|
|
134
|
+
if (target.template) {
|
|
135
|
+
args.push("-t", target.template);
|
|
136
|
+
}
|
|
137
|
+
const result = (0, node_child_process_1.spawnSync)("sam", args, {
|
|
138
|
+
cwd,
|
|
139
|
+
stdio: "inherit",
|
|
140
|
+
shell: USE_SHELL,
|
|
141
|
+
});
|
|
142
|
+
if (result.error || result.status !== 0) {
|
|
143
|
+
node_fs_1.default.rmSync(scratch, { recursive: true, force: true });
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const built = node_path_1.default.join(scratch, resourceId);
|
|
147
|
+
const destination = node_path_1.default.join(cwd, target.buildDir ?? node_path_1.default.join(".aws-sam", "build"), resourceId);
|
|
148
|
+
if (!node_fs_1.default.existsSync(built)) {
|
|
149
|
+
node_fs_1.default.rmSync(scratch, { recursive: true, force: true });
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
node_fs_1.default.rmSync(destination, { recursive: true, force: true });
|
|
153
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(destination), { recursive: true });
|
|
154
|
+
node_fs_1.default.renameSync(built, destination);
|
|
155
|
+
node_fs_1.default.rmSync(scratch, { recursive: true, force: true });
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
// Unlike samBuild this never throws: a syntax error in a handler must not tear down
|
|
159
|
+
// a watch session that the next save would fix.
|
|
160
|
+
function samRebuild(cwd, target = {}) {
|
|
161
|
+
const result = (0, node_child_process_1.spawnSync)("sam", ["build", ...targetArgs(target)], {
|
|
162
|
+
cwd,
|
|
163
|
+
stdio: "inherit",
|
|
164
|
+
shell: USE_SHELL,
|
|
165
|
+
});
|
|
166
|
+
return !result.error && result.status === 0;
|
|
167
|
+
}
|
|
168
|
+
function startLocalApi(cwd, port, parameterOverrides = [], template) {
|
|
169
|
+
const overrideArgs = parameterOverrides.length > 0
|
|
170
|
+
? ["--parameter-overrides", ...parameterOverrides]
|
|
171
|
+
: [];
|
|
172
|
+
// Values are deliberately not logged: overrides carry secrets.
|
|
173
|
+
const overrideNote = parameterOverrides.length > 0
|
|
174
|
+
? ` --parameter-overrides (${parameterOverrides.length} value${parameterOverrides.length === 1 ? "" : "s"})`
|
|
175
|
+
: "";
|
|
176
|
+
const templateArgs = template ? ["-t", template] : [];
|
|
177
|
+
logger_js_1.logger.info(`\n> sam local start-api --port ${port}${overrideNote}`);
|
|
178
|
+
const child = (0, node_child_process_1.spawn)("sam", ["local", "start-api", "--port", String(port), ...templateArgs, ...overrideArgs], { cwd, stdio: "inherit", shell: USE_SHELL });
|
|
179
|
+
child.on("error", (error) => {
|
|
180
|
+
logger_js_1.logger.error(`Failed to run sam local start-api: ${error.message}`);
|
|
181
|
+
});
|
|
182
|
+
return child;
|
|
183
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface RunOptions {
|
|
2
|
+
/** Run every function in one service. */
|
|
3
|
+
service?: string;
|
|
4
|
+
/** Run one function on its own. */
|
|
5
|
+
function?: string;
|
|
6
|
+
/** Skip the scope prompt and run everything. */
|
|
7
|
+
all?: boolean;
|
|
8
|
+
port?: string | number;
|
|
9
|
+
build?: boolean;
|
|
10
|
+
env?: string;
|
|
11
|
+
watch?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface ProjectManifestFile {
|
|
14
|
+
name: string;
|
|
15
|
+
framework: {
|
|
16
|
+
id: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ApplicationView } from "./resources.js";
|
|
2
|
+
export interface WatchHandlers {
|
|
3
|
+
onFunctions: (resourceIds: string[], labels: string[]) => void;
|
|
4
|
+
onFull: (labels: string[]) => void;
|
|
5
|
+
onRestart: (labels: string[]) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface Watcher {
|
|
8
|
+
close: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function watchProject(cwd: string, applications: ApplicationView[], handlers: WatchHandlers, sharedApi?: boolean): Watcher;
|
|
11
|
+
export declare function describeChange(labels: string[]): string;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.watchProject = watchProject;
|
|
7
|
+
exports.describeChange = describeChange;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const resources_js_1 = require("./resources.js");
|
|
11
|
+
// Editors save in bursts -- a write, a rename, sometimes a companion file -- so
|
|
12
|
+
// events are collected for a moment and acted on once.
|
|
13
|
+
const DEBOUNCE_MS = 200;
|
|
14
|
+
const IGNORED_DIRS = new Set([".aws-sam", "node_modules", "dist", ".git", ".venv"]);
|
|
15
|
+
const WATCHED_EXTENSIONS = new Set([".ts", ".js", ".mjs", ".cjs", ".py", ".json", ".yaml", ".yml"]);
|
|
16
|
+
// Editors write temporary siblings while saving; acting on those would rebuild twice.
|
|
17
|
+
function isRelevant(relativePath) {
|
|
18
|
+
const base = node_path_1.default.basename(relativePath);
|
|
19
|
+
if (base.startsWith(".") && !base.startsWith(".env")) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (base.startsWith(".env")) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (base.endsWith("~") || base.endsWith(".swp") || base.endsWith(".tmp")) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return WATCHED_EXTENSIONS.has(node_path_1.default.extname(base));
|
|
29
|
+
}
|
|
30
|
+
function directoriesUnder(root) {
|
|
31
|
+
const found = [];
|
|
32
|
+
const walk = (dir) => {
|
|
33
|
+
found.push(dir);
|
|
34
|
+
let entries;
|
|
35
|
+
try {
|
|
36
|
+
entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
if (entry.isDirectory() && !IGNORED_DIRS.has(entry.name)) {
|
|
43
|
+
walk(node_path_1.default.join(dir, entry.name));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
walk(root);
|
|
48
|
+
return found;
|
|
49
|
+
}
|
|
50
|
+
// Watches the project's sources plus the files that decide how sam is started, and
|
|
51
|
+
// hands each debounced batch to the caller as one decision.
|
|
52
|
+
function watchProject(cwd, applications, handlers, sharedApi = false) {
|
|
53
|
+
const watchers = [];
|
|
54
|
+
const batch = [];
|
|
55
|
+
let timer;
|
|
56
|
+
let closed = false;
|
|
57
|
+
const flush = () => {
|
|
58
|
+
timer = undefined;
|
|
59
|
+
if (closed || batch.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const plan = (0, resources_js_1.planRebuild)(batch.splice(0, batch.length));
|
|
63
|
+
if (plan.kind === "restart") {
|
|
64
|
+
handlers.onRestart(plan.labels);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (plan.kind === "full") {
|
|
68
|
+
handlers.onFull(plan.labels);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
handlers.onFunctions(plan.resourceIds, plan.labels);
|
|
72
|
+
};
|
|
73
|
+
const record = (relativePath) => {
|
|
74
|
+
if (closed || !isRelevant(relativePath)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
batch.push((0, resources_js_1.classifyChange)(applications, relativePath, sharedApi));
|
|
78
|
+
if (timer) {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
}
|
|
81
|
+
timer = setTimeout(flush, DEBOUNCE_MS);
|
|
82
|
+
};
|
|
83
|
+
const watchDir = (dir, recursive) => {
|
|
84
|
+
try {
|
|
85
|
+
const watcher = node_fs_1.default.watch(dir, { recursive }, (_event, filename) => {
|
|
86
|
+
if (!filename) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
record(node_path_1.default.relative(cwd, node_path_1.default.join(dir, filename.toString())));
|
|
90
|
+
});
|
|
91
|
+
watchers.push(watcher);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
for (const name of ["src", "templates"]) {
|
|
99
|
+
const dir = node_path_1.default.join(cwd, name);
|
|
100
|
+
if (!node_fs_1.default.existsSync(dir)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
// Recursive watching is not available on every platform, so each directory is
|
|
104
|
+
// watched individually when it is not.
|
|
105
|
+
if (!watchDir(dir, true)) {
|
|
106
|
+
for (const child of directoriesUnder(dir)) {
|
|
107
|
+
watchDir(child, false);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// The project root itself, for template.yaml, slskit.json and the .env files. Not
|
|
112
|
+
// recursive: everything below it that matters is already covered above.
|
|
113
|
+
watchDir(cwd, false);
|
|
114
|
+
return {
|
|
115
|
+
close: () => {
|
|
116
|
+
closed = true;
|
|
117
|
+
if (timer) {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
}
|
|
120
|
+
for (const watcher of watchers) {
|
|
121
|
+
watcher.close();
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function describeChange(labels) {
|
|
127
|
+
if (labels.length <= 2) {
|
|
128
|
+
return labels.join(", ");
|
|
129
|
+
}
|
|
130
|
+
return `${labels.slice(0, 2).join(", ")} and ${labels.length - 2} more`;
|
|
131
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface GlobalOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
debug?: boolean;
|
|
4
|
+
silent?: boolean;
|
|
5
|
+
json?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface CliContext {
|
|
8
|
+
cwd: string;
|
|
9
|
+
debug: boolean;
|
|
10
|
+
silent: boolean;
|
|
11
|
+
json: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function getContext(): CliContext;
|
|
14
|
+
export declare function resetContext(): void;
|
|
15
|
+
export declare function applyGlobalOptions(options: GlobalOptions): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getContext = getContext;
|
|
7
|
+
exports.resetContext = resetContext;
|
|
8
|
+
exports.applyGlobalOptions = applyGlobalOptions;
|
|
9
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const errors_js_1 = require("./errors.js");
|
|
12
|
+
function defaultContext() {
|
|
13
|
+
return {
|
|
14
|
+
cwd: process.cwd(),
|
|
15
|
+
debug: false,
|
|
16
|
+
silent: false,
|
|
17
|
+
json: false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
let context = defaultContext();
|
|
21
|
+
function getContext() {
|
|
22
|
+
return context;
|
|
23
|
+
}
|
|
24
|
+
function resetContext() {
|
|
25
|
+
context = defaultContext();
|
|
26
|
+
}
|
|
27
|
+
function applyGlobalOptions(options) {
|
|
28
|
+
if (options.cwd) {
|
|
29
|
+
const resolved = node_path_1.default.resolve(options.cwd);
|
|
30
|
+
if (!node_fs_1.default.existsSync(resolved) || !node_fs_1.default.statSync(resolved).isDirectory()) {
|
|
31
|
+
throw new errors_js_1.CliError(`Working directory "${options.cwd}" does not exist.`);
|
|
32
|
+
}
|
|
33
|
+
process.chdir(resolved);
|
|
34
|
+
}
|
|
35
|
+
context = {
|
|
36
|
+
cwd: process.cwd(),
|
|
37
|
+
debug: Boolean(options.debug),
|
|
38
|
+
silent: Boolean(options.silent),
|
|
39
|
+
json: Boolean(options.json),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ProfileSource = "flag" | "config" | "env" | "none";
|
|
2
|
+
export interface ResolvedProfile {
|
|
3
|
+
profile: string | undefined;
|
|
4
|
+
source: ProfileSource;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveAwsProfile(options: {
|
|
7
|
+
flag?: string;
|
|
8
|
+
stored?: string;
|
|
9
|
+
env?: NodeJS.ProcessEnv;
|
|
10
|
+
}): ResolvedProfile;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveAwsProfile = resolveAwsProfile;
|
|
4
|
+
// Resolution order is conservative on purpose: a leftover AWS_PROFILE in the
|
|
5
|
+
// shell must not override the profile this project stored in slskit.json.
|
|
6
|
+
// An explicit --profile always wins.
|
|
7
|
+
function resolveAwsProfile(options) {
|
|
8
|
+
const env = options.env ?? process.env;
|
|
9
|
+
const fromFlag = options.flag?.trim();
|
|
10
|
+
if (fromFlag) {
|
|
11
|
+
return { profile: fromFlag, source: "flag" };
|
|
12
|
+
}
|
|
13
|
+
const fromStore = options.stored?.trim();
|
|
14
|
+
if (fromStore) {
|
|
15
|
+
return { profile: fromStore, source: "config" };
|
|
16
|
+
}
|
|
17
|
+
const fromEnv = env.AWS_PROFILE?.trim();
|
|
18
|
+
if (fromEnv) {
|
|
19
|
+
return { profile: fromEnv, source: "env" };
|
|
20
|
+
}
|
|
21
|
+
return { profile: undefined, source: "none" };
|
|
22
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const MANIFEST_FILE = "slskit.json";
|
|
2
|
+
export declare const LEGACY_MANIFEST_FILE = "sless.json";
|
|
3
|
+
export declare function manifestPathFor(cwd: string): string | undefined;
|
|
4
|
+
export declare const DEFAULT_ENVIRONMENT = "dev";
|
|
5
|
+
export declare const APP_ENVIRONMENT_KEY = "APP_ENVIRONMENT";
|
|
6
|
+
export declare const APP_ENVIRONMENT_PARAM = "AppEnvironment";
|
|
7
|
+
export declare const FUNCTION_NAME_MAX = 64;
|
|
8
|
+
export interface EnvVarDef {
|
|
9
|
+
value?: string;
|
|
10
|
+
secret?: boolean;
|
|
11
|
+
ssm?: string;
|
|
12
|
+
}
|
|
13
|
+
export type EnvVarSource = "value" | "secret" | "ssm";
|
|
14
|
+
export interface EnvironmentConfig {
|
|
15
|
+
region?: string;
|
|
16
|
+
profile?: string;
|
|
17
|
+
stackName: string;
|
|
18
|
+
variables?: Record<string, EnvVarDef>;
|
|
19
|
+
}
|
|
20
|
+
export interface EnvironmentsConfig {
|
|
21
|
+
default: string;
|
|
22
|
+
list: Record<string, EnvironmentConfig>;
|
|
23
|
+
}
|
|
24
|
+
export interface ProjectManifest {
|
|
25
|
+
name: string;
|
|
26
|
+
framework?: {
|
|
27
|
+
id?: string;
|
|
28
|
+
};
|
|
29
|
+
environments?: EnvironmentsConfig;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
export declare function readManifest(cwd: string, command?: string): ProjectManifest;
|
|
33
|
+
export declare function writeManifest(cwd: string, manifest: ProjectManifest): void;
|
|
34
|
+
export declare function parseEnvironmentName(value: string | undefined, label?: string): string | undefined;
|
|
35
|
+
export declare function environmentNames(manifest: ProjectManifest): string[];
|
|
36
|
+
export declare function defaultEnvironment(manifest: ProjectManifest): string;
|
|
37
|
+
export declare function resolveEnvironmentName(manifest: ProjectManifest, requested?: string): string;
|
|
38
|
+
export declare function requireEnvironment(manifest: ProjectManifest, name: string): EnvironmentConfig;
|
|
39
|
+
export declare function stackNameFor(projectName: string, environment: string): string;
|
|
40
|
+
export declare function functionNameFor(projectName: string, environment: string, functionName: string): string;
|
|
41
|
+
export declare function assertFunctionNameFits(projectName: string, environment: string, functionName: string): void;
|
|
42
|
+
export declare function envVarSource(def: EnvVarDef): EnvVarSource;
|
|
43
|
+
export declare function allEnvKeys(manifest: {
|
|
44
|
+
environments?: EnvironmentsConfig;
|
|
45
|
+
}): string[];
|
|
46
|
+
export declare function writeEnvironment(cwd: string, manifest: ProjectManifest, name: string, config: EnvironmentConfig): void;
|