@ericminassian/plat 0.1.0 → 0.2.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/dist/build.d.ts +4 -1
- package/dist/build.js +4 -7
- package/dist/build.js.map +1 -1
- package/dist/commands/common.d.ts +2 -2
- package/dist/commands/common.js +2 -2
- package/dist/commands/common.js.map +1 -1
- package/dist/commands/lifecycle.js +4 -3
- package/dist/commands/lifecycle.js.map +1 -1
- package/dist/commands/manage.js +8 -8
- package/dist/commands/manage.js.map +1 -1
- package/dist/commands/observe.js +5 -5
- package/dist/commands/observe.js.map +1 -1
- package/dist/deploy.js +2 -2
- package/dist/deploy.js.map +1 -1
- package/dist/dev/crons.d.ts +1 -1
- package/dist/dev/index.js +1 -1
- package/dist/dev/index.js.map +1 -1
- package/dist/dev/local-stage.js +1 -1
- package/dist/dev/router.js +1 -1
- package/dist/dev/router.js.map +1 -1
- package/dist/dev/test.js +1 -1
- package/dist/dev/test.js.map +1 -1
- package/dist/init.d.ts +7 -0
- package/dist/init.js +15 -3
- package/dist/init.js.map +1 -1
- package/dist/project.d.ts +6 -7
- package/dist/project.js +78 -21
- package/dist/project.js.map +1 -1
- package/dist/util/esbuild.d.ts +2 -0
- package/dist/util/esbuild.js +9 -0
- package/dist/util/esbuild.js.map +1 -0
- package/package.json +4 -4
- package/src/build.ts +7 -10
- package/src/commands/common.ts +3 -3
- package/src/commands/lifecycle.ts +4 -3
- package/src/commands/manage.ts +8 -8
- package/src/commands/observe.ts +5 -5
- package/src/deploy.ts +2 -2
- package/src/dev/crons.ts +1 -1
- package/src/dev/index.ts +1 -1
- package/src/dev/local-stage.ts +1 -1
- package/src/dev/router.ts +1 -1
- package/src/dev/test.ts +1 -1
- package/src/init.ts +16 -3
- package/src/project.ts +76 -22
- package/src/util/esbuild.ts +10 -0
- package/template/AGENTS.md +23 -18
- package/template/plat.config.ts +31 -0
- package/template/src/server.ts +3 -3
- package/template/src/worker.ts +5 -5
- package/template/tsconfig.json +1 -1
- package/template/platform.json +0 -18
package/dist/init.d.ts
CHANGED
|
@@ -19,5 +19,12 @@ export interface InitResult {
|
|
|
19
19
|
files: string[];
|
|
20
20
|
}
|
|
21
21
|
export declare function initApp(opts: InitOptions): InitResult;
|
|
22
|
+
/**
|
|
23
|
+
* pnpm 10+ refuses to install (ERR_PNPM_IGNORED_BUILDS) until a dependency's
|
|
24
|
+
* install script is allowed, and esbuild (used by `plat build`) has one. It's
|
|
25
|
+
* written here rather than shipped in templates/app: a pnpm-workspace.yaml
|
|
26
|
+
* there would make that folder its own workspace inside this monorepo.
|
|
27
|
+
*/
|
|
28
|
+
export declare const PNPM_WORKSPACE = "# esbuild (used by `plat build`) installs its native binary with a build script.\nallowBuilds:\n esbuild: true\n";
|
|
22
29
|
export declare function replaceAppName(content: string, name: string): string;
|
|
23
30
|
export declare function rewritePackageJson(content: string, name: string, range: string): string;
|
package/dist/init.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync, } from 'node:fs';
|
|
2
2
|
import { basename, dirname, extname, join, relative, resolve } from 'node:path';
|
|
3
|
-
import { validateAppName } from '@ericminassian/platform-core';
|
|
3
|
+
import { APP_CONFIG_FILE, validateAppName } from '@ericminassian/platform-core';
|
|
4
4
|
import { cliPackageRoot, cliVersion } from './project.js';
|
|
5
5
|
import { CliError, EXIT } from './util/output.js';
|
|
6
6
|
/** The app name used throughout `templates/app`; replaced on `plat init`. */
|
|
@@ -28,10 +28,10 @@ const TEXT_EXT = new Set([
|
|
|
28
28
|
/** Published CLI: `template/` inside the package. In this repo: `templates/app`. */
|
|
29
29
|
export function templateDir() {
|
|
30
30
|
const packaged = join(cliPackageRoot(), 'template');
|
|
31
|
-
if (existsSync(join(packaged,
|
|
31
|
+
if (existsSync(join(packaged, APP_CONFIG_FILE)))
|
|
32
32
|
return packaged;
|
|
33
33
|
const repo = resolve(cliPackageRoot(), '..', '..', 'templates', 'app');
|
|
34
|
-
if (existsSync(join(repo,
|
|
34
|
+
if (existsSync(join(repo, APP_CONFIG_FILE)))
|
|
35
35
|
return repo;
|
|
36
36
|
throw new CliError('app template not found (broken CLI install?)');
|
|
37
37
|
}
|
|
@@ -50,8 +50,20 @@ export function initApp(opts) {
|
|
|
50
50
|
const src = opts.template ?? templateDir();
|
|
51
51
|
const range = opts.versionRange ?? `^${cliVersion()}`;
|
|
52
52
|
const files = copyTree(src, dir, (rel, content) => rel === 'package.json' ? rewritePackageJson(content, name, range) : replaceAppName(content, name));
|
|
53
|
+
writeFileSync(join(dir, 'pnpm-workspace.yaml'), PNPM_WORKSPACE);
|
|
54
|
+
files.push('pnpm-workspace.yaml');
|
|
53
55
|
return { dir, name, files: files.sort() };
|
|
54
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* pnpm 10+ refuses to install (ERR_PNPM_IGNORED_BUILDS) until a dependency's
|
|
59
|
+
* install script is allowed, and esbuild (used by `plat build`) has one. It's
|
|
60
|
+
* written here rather than shipped in templates/app: a pnpm-workspace.yaml
|
|
61
|
+
* there would make that folder its own workspace inside this monorepo.
|
|
62
|
+
*/
|
|
63
|
+
export const PNPM_WORKSPACE = `# esbuild (used by \`plat build\`) installs its native binary with a build script.
|
|
64
|
+
allowBuilds:
|
|
65
|
+
esbuild: true
|
|
66
|
+
`;
|
|
55
67
|
export function replaceAppName(content, name) {
|
|
56
68
|
return content.split(TEMPLATE_APP_NAME).join(name);
|
|
57
69
|
}
|
package/dist/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/dist/project.d.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { type ResolvedManifest } from '@ericminassian/platform-core';
|
|
2
|
-
export declare const MANIFEST_FILE = "platform.json";
|
|
3
2
|
export interface Project {
|
|
4
|
-
/** Directory containing
|
|
3
|
+
/** Directory containing plat.config.ts. */
|
|
5
4
|
root: string;
|
|
6
|
-
/**
|
|
5
|
+
/** The config's default export as JSON: exactly what is validated, built and deployed. */
|
|
7
6
|
raw: Record<string, unknown>;
|
|
8
7
|
manifest: ResolvedManifest;
|
|
9
8
|
}
|
|
10
|
-
/** Walk up from `cwd` to the nearest
|
|
9
|
+
/** Walk up from `cwd` to the nearest plat.config.ts. */
|
|
11
10
|
export declare function findProjectRoot(cwd: string): string | null;
|
|
12
|
-
export declare function loadProject(cwd: string): Project
|
|
13
|
-
/** App name from `--app`, else the nearest
|
|
14
|
-
export declare function resolveAppName(opt: string | undefined, cwd?: string): string
|
|
11
|
+
export declare function loadProject(cwd: string): Promise<Project>;
|
|
12
|
+
/** App name from `--app`, else the nearest plat.config.ts. */
|
|
13
|
+
export declare function resolveAppName(opt: string | undefined, cwd?: string): Promise<string>;
|
|
15
14
|
export declare function cliVersion(): string;
|
|
16
15
|
export declare function cliPackageRoot(): string;
|
|
17
16
|
/**
|
package/dist/project.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
2
10
|
import { createRequire } from 'node:module';
|
|
3
11
|
import { dirname, join, resolve } from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { ManifestError, parseManifest } from '@ericminassian/platform-core';
|
|
12
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
13
|
+
import { APP_CONFIG_FILE, ManifestError, parseManifest, } from '@ericminassian/platform-core';
|
|
14
|
+
import { build as esbuild } from 'esbuild';
|
|
15
|
+
import { buildFailureList } from './util/esbuild.js';
|
|
6
16
|
import { CliError, EXIT } from './util/output.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
17
|
+
/** Apps configured with JSON before `plat.config.ts`; only detected to say how to move. */
|
|
18
|
+
const LEGACY_MANIFEST_FILE = 'platform.json';
|
|
19
|
+
/** Walk up from `cwd` to the nearest directory containing `name`. */
|
|
20
|
+
function findUp(cwd, name) {
|
|
10
21
|
let dir = resolve(cwd);
|
|
11
22
|
for (;;) {
|
|
12
|
-
if (existsSync(join(dir,
|
|
23
|
+
if (existsSync(join(dir, name)))
|
|
13
24
|
return dir;
|
|
14
25
|
const parent = dirname(dir);
|
|
15
26
|
if (parent === dir)
|
|
@@ -17,23 +28,30 @@ export function findProjectRoot(cwd) {
|
|
|
17
28
|
dir = parent;
|
|
18
29
|
}
|
|
19
30
|
}
|
|
20
|
-
|
|
31
|
+
/** Walk up from `cwd` to the nearest plat.config.ts. */
|
|
32
|
+
export function findProjectRoot(cwd) {
|
|
33
|
+
return findUp(cwd, APP_CONFIG_FILE);
|
|
34
|
+
}
|
|
35
|
+
export async function loadProject(cwd) {
|
|
21
36
|
const root = findProjectRoot(cwd);
|
|
22
37
|
if (!root) {
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
const legacy = findUp(cwd, LEGACY_MANIFEST_FILE);
|
|
39
|
+
throw new CliError(`no ${APP_CONFIG_FILE} found in ${cwd} or its parents`, {
|
|
40
|
+
hint: legacy
|
|
41
|
+
? `${join(legacy, LEGACY_MANIFEST_FILE)} is no longer read: move its fields into ${APP_CONFIG_FILE} as \`export default defineApp({ ... })\` from '@ericminassian/platform/config'`
|
|
42
|
+
: 'run this inside an app, or create one with `plat init <dir> --name <app>`',
|
|
25
43
|
exitCode: EXIT.usage,
|
|
26
44
|
});
|
|
27
45
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
catch (err) {
|
|
33
|
-
throw new CliError(`${MANIFEST_FILE} is not valid JSON: ${err.message}`, {
|
|
46
|
+
const exported = await importConfig(root);
|
|
47
|
+
if (exported === null || typeof exported !== 'object' || Array.isArray(exported)) {
|
|
48
|
+
throw new CliError(`${APP_CONFIG_FILE} must default-export the app config`, {
|
|
49
|
+
hint: "export default defineApp({ name: '...' }) from '@ericminassian/platform/config'",
|
|
34
50
|
exitCode: EXIT.usage,
|
|
35
51
|
});
|
|
36
52
|
}
|
|
53
|
+
// The platform receives JSON, so validate (and later deploy) the JSON form.
|
|
54
|
+
const raw = JSON.parse(JSON.stringify(exported));
|
|
37
55
|
try {
|
|
38
56
|
return { root, raw, manifest: parseManifest(raw) };
|
|
39
57
|
}
|
|
@@ -43,18 +61,57 @@ export function loadProject(cwd) {
|
|
|
43
61
|
throw err;
|
|
44
62
|
}
|
|
45
63
|
}
|
|
46
|
-
/**
|
|
47
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Bundles the config (its relative imports included, packages left external)
|
|
66
|
+
* into `.plat/` and imports that, like Vite does for vite.config.ts. This
|
|
67
|
+
* works whatever the app's package `type` is, and a fresh file name means a
|
|
68
|
+
* changed config is always re-read.
|
|
69
|
+
*/
|
|
70
|
+
async function importConfig(root) {
|
|
71
|
+
const outfile = join(root, '.plat', `config-${process.pid}-${Date.now()}.mjs`);
|
|
72
|
+
try {
|
|
73
|
+
await esbuild({
|
|
74
|
+
entryPoints: [join(root, APP_CONFIG_FILE)],
|
|
75
|
+
outfile,
|
|
76
|
+
bundle: true,
|
|
77
|
+
packages: 'external',
|
|
78
|
+
platform: 'node',
|
|
79
|
+
format: 'esm',
|
|
80
|
+
target: 'node24',
|
|
81
|
+
sourcemap: 'inline',
|
|
82
|
+
logLevel: 'silent',
|
|
83
|
+
absWorkingDir: root,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
throw new CliError(`could not compile ${APP_CONFIG_FILE}:${buildFailureList(err)}`, {
|
|
88
|
+
exitCode: EXIT.usage,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(outfile).href)));
|
|
93
|
+
return mod.default;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
throw new CliError(`${APP_CONFIG_FILE} threw while loading: ${err.message}`, {
|
|
97
|
+
exitCode: EXIT.usage,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
rmSync(outfile, { force: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** App name from `--app`, else the nearest plat.config.ts. */
|
|
105
|
+
export async function resolveAppName(opt, cwd = process.cwd()) {
|
|
48
106
|
if (opt)
|
|
49
107
|
return opt;
|
|
50
|
-
|
|
51
|
-
if (!root) {
|
|
108
|
+
if (!findProjectRoot(cwd)) {
|
|
52
109
|
throw new CliError('no app given', {
|
|
53
110
|
hint: 'pass --app <name> or run inside an app directory',
|
|
54
111
|
exitCode: EXIT.usage,
|
|
55
112
|
});
|
|
56
113
|
}
|
|
57
|
-
return loadProject(
|
|
114
|
+
return (await loadProject(cwd)).manifest.name;
|
|
58
115
|
}
|
|
59
116
|
export function cliVersion() {
|
|
60
117
|
return cliPackageJson().version ?? '0.0.0';
|
package/dist/project.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** An esbuild failure as `file:line: message` items (or the error's message), for a CliError list. */
|
|
2
|
+
export function buildFailureList(err) {
|
|
3
|
+
const errors = err.errors;
|
|
4
|
+
const lines = errors?.length
|
|
5
|
+
? errors.map((e) => (e.location ? `${e.location.file}:${e.location.line}: ${e.text}` : e.text))
|
|
6
|
+
: [err.message];
|
|
7
|
+
return `\n - ${lines.join('\n - ')}`;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=esbuild.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esbuild.js","sourceRoot":"","sources":["../../src/util/esbuild.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,MAAM,MAAM,GACV,GACD,CAAC,MAAM,CAAC;IACT,MAAM,KAAK,GAAG,MAAM,EAAE,MAAM;QAC1B,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/F,CAAC,CAAC,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,SAAS,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;AACzC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ericminassian/plat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI for the platform: init, dev (AWS Emulator), build, deploy, logs, metrics, errors, data.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"@aws-sdk/client-s3": "^3.1169.0",
|
|
35
35
|
"@aws-sdk/client-sqs": "^3.1169.0",
|
|
36
36
|
"@aws-sdk/client-ssm": "^3.1169.0",
|
|
37
|
-
"@ericminassian/platform": "0.
|
|
38
|
-
"@ericminassian/platform-core": "0.
|
|
39
|
-
"@ericminassian/platform-engine": "0.
|
|
37
|
+
"@ericminassian/platform": "0.2.0",
|
|
38
|
+
"@ericminassian/platform-core": "0.2.0",
|
|
39
|
+
"@ericminassian/platform-engine": "0.2.0",
|
|
40
40
|
"commander": "^15.1.0",
|
|
41
41
|
"cron-parser": "^5.10.6",
|
|
42
42
|
"esbuild": "^0.28.2",
|
package/src/build.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node
|
|
|
3
3
|
import { delimiter, join, resolve } from 'node:path';
|
|
4
4
|
import type { ArtifactFile, ResolvedManifest } from '@ericminassian/platform-core';
|
|
5
5
|
import { build as esbuild, type Plugin } from 'esbuild';
|
|
6
|
-
import { cliVersion, loadProject } from './project.ts';
|
|
6
|
+
import { cliVersion, loadProject, type Project } from './project.ts';
|
|
7
|
+
import { buildFailureList } from './util/esbuild.ts';
|
|
7
8
|
import { CliError, EXIT, step } from './util/output.ts';
|
|
8
9
|
import { dirExists, zipDirectory } from './util/zip.ts';
|
|
9
10
|
|
|
@@ -12,6 +13,8 @@ export const SDK_WORKER = '@ericminassian/platform/worker';
|
|
|
12
13
|
|
|
13
14
|
export interface BuildOptions {
|
|
14
15
|
cwd: string;
|
|
16
|
+
/** Already-loaded project (skips evaluating plat.config.ts again). */
|
|
17
|
+
project?: Project;
|
|
15
18
|
/** Default `<app>/.plat/build`. */
|
|
16
19
|
outDir?: string;
|
|
17
20
|
/** Skip `web.build` (e.g. `plat dev`, which runs the dev server instead). */
|
|
@@ -38,10 +41,10 @@ export interface BuildResult {
|
|
|
38
41
|
* - `server.zip` → `index.mjs` (+ map): the app's fetch handler wrapped by the SDK server
|
|
39
42
|
* - `worker.zip` → `index.mjs` (+ map): `export const handler` for the worker Lambda
|
|
40
43
|
* - `static.zip` → the built frontend
|
|
41
|
-
* - `manifest.json` → `{ manifest, resolved, build }`
|
|
44
|
+
* - `manifest.json` → `{ manifest, resolved, build }` (`manifest` is plat.config.ts evaluated to JSON)
|
|
42
45
|
*/
|
|
43
46
|
export async function buildApp(opts: BuildOptions): Promise<BuildResult> {
|
|
44
|
-
const project = loadProject(opts.cwd);
|
|
47
|
+
const project = opts.project ?? (await loadProject(opts.cwd));
|
|
45
48
|
const { root, manifest, raw } = project;
|
|
46
49
|
const outDir = resolve(opts.outDir ?? join(root, '.plat', 'build'));
|
|
47
50
|
const want = (part: 'server' | 'worker' | 'static') => !opts.only || opts.only.includes(part);
|
|
@@ -198,13 +201,7 @@ async function bundle(o: {
|
|
|
198
201
|
absWorkingDir: o.root,
|
|
199
202
|
});
|
|
200
203
|
} catch (err) {
|
|
201
|
-
|
|
202
|
-
err as { errors?: Array<{ text: string; location?: { file: string; line: number } | null }> }
|
|
203
|
-
).errors;
|
|
204
|
-
const detail = errors
|
|
205
|
-
?.map((e) => (e.location ? `${e.location.file}:${e.location.line}: ${e.text}` : e.text))
|
|
206
|
-
.join('\n - ');
|
|
207
|
-
throw new CliError(`bundling failed${detail ? `:\n - ${detail}` : `: ${(err as Error).message}`}`);
|
|
204
|
+
throw new CliError(`bundling failed:${buildFailureList(err)}`);
|
|
208
205
|
}
|
|
209
206
|
return o.outDir;
|
|
210
207
|
}
|
package/src/commands/common.ts
CHANGED
|
@@ -17,9 +17,9 @@ export function withCommon(cmd: Command): Command {
|
|
|
17
17
|
.addOption(new Option('--api-url <url>', 'platform API base URL').env('PLATFORM_API_URL'));
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/** `--app` (defaults to
|
|
20
|
+
/** `--app` (defaults to plat.config.ts) and `--stage` (defaults to prod). */
|
|
21
21
|
export function withTarget(cmd: Command, stageDefault: string | false = 'prod'): Command {
|
|
22
|
-
cmd.option('--app <name>', 'app name (default: from
|
|
22
|
+
cmd.option('--app <name>', 'app name (default: from plat.config.ts)');
|
|
23
23
|
if (stageDefault) cmd.option('--stage <stage>', 'stage', stageDefault);
|
|
24
24
|
else cmd.option('--stage <stage>', 'stage');
|
|
25
25
|
return withCommon(cmd);
|
|
@@ -29,7 +29,7 @@ export function apiFor(opts: CommonOpts): ApiClient {
|
|
|
29
29
|
return new ApiClient(opts.apiUrl ? { baseUrl: opts.apiUrl } : {});
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export function appFor(opts: CommonOpts): string {
|
|
32
|
+
export function appFor(opts: CommonOpts): Promise<string> {
|
|
33
33
|
return resolveAppName(opts.app);
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -28,10 +28,11 @@ export function registerLifecycle(program: Command): void {
|
|
|
28
28
|
'',
|
|
29
29
|
' next:',
|
|
30
30
|
rel === '.' ? '' : ` cd ${rel}`,
|
|
31
|
-
' pnpm install',
|
|
31
|
+
' pnpm install # creates the lockfile CI installs from',
|
|
32
32
|
' pnpm dev # local, on the AWS Emulator',
|
|
33
|
-
`
|
|
34
|
-
`
|
|
33
|
+
` plat apps create ${v.name} --repo <owner>/${v.name} # register before the first push`,
|
|
34
|
+
` git init && git add -A && git commit -m "Initial commit"`,
|
|
35
|
+
` gh repo create <owner>/${v.name} --private --source . --push # CI deploys https://${v.name}.<domain>`,
|
|
35
36
|
]
|
|
36
37
|
.filter(Boolean)
|
|
37
38
|
.join('\n'),
|
package/src/commands/manage.ts
CHANGED
|
@@ -15,7 +15,7 @@ export function registerManage(program: Command): void {
|
|
|
15
15
|
|
|
16
16
|
withTarget(secrets.command('list').description('secret names for a stage (and shared ones)')).action(
|
|
17
17
|
action(async (opts: TargetOpts) => {
|
|
18
|
-
const list = await apiFor(opts).listSecrets(appFor(opts), { stage: opts.stage });
|
|
18
|
+
const list = await apiFor(opts).listSecrets(await appFor(opts), { stage: opts.stage });
|
|
19
19
|
result(list, (l) =>
|
|
20
20
|
table(
|
|
21
21
|
l.map((s) => ({
|
|
@@ -40,7 +40,7 @@ export function registerManage(program: Command): void {
|
|
|
40
40
|
const v = value ?? (process.stdin.isTTY ? await prompt(`value for ${name}: `) : readStdin());
|
|
41
41
|
if (!v) throw new CliError('empty secret value', { exitCode: EXIT.usage });
|
|
42
42
|
const info = await apiFor(opts).setSecret(
|
|
43
|
-
appFor(opts),
|
|
43
|
+
await appFor(opts),
|
|
44
44
|
name,
|
|
45
45
|
opts.shared ? { value: v, shared: true } : { value: v, stage: opts.stage },
|
|
46
46
|
);
|
|
@@ -61,7 +61,7 @@ export function registerManage(program: Command): void {
|
|
|
61
61
|
action(async (name: string, opts: TargetOpts & { shared?: boolean }) => {
|
|
62
62
|
checkSecretName(name);
|
|
63
63
|
await apiFor(opts).deleteSecret(
|
|
64
|
-
appFor(opts),
|
|
64
|
+
await appFor(opts),
|
|
65
65
|
name,
|
|
66
66
|
opts.shared ? { shared: true } : { stage: opts.stage },
|
|
67
67
|
);
|
|
@@ -72,7 +72,7 @@ export function registerManage(program: Command): void {
|
|
|
72
72
|
// ------------------------------------------------------------ deployments & stages
|
|
73
73
|
withTarget(program.command('deployments').description('recent deployments'), false).action(
|
|
74
74
|
action(async (opts: CommonOpts & { stage?: string }) => {
|
|
75
|
-
const list = await apiFor(opts).listDeployments(appFor(opts), opts.stage);
|
|
75
|
+
const list = await apiFor(opts).listDeployments(await appFor(opts), opts.stage);
|
|
76
76
|
result(list, (l) =>
|
|
77
77
|
table(
|
|
78
78
|
l.map((d) => ({
|
|
@@ -96,7 +96,7 @@ export function registerManage(program: Command): void {
|
|
|
96
96
|
|
|
97
97
|
withTarget(program.command('stages').description('stages of an app'), false).action(
|
|
98
98
|
action(async (opts: CommonOpts) => {
|
|
99
|
-
const list = await apiFor(opts).listStages(appFor(opts));
|
|
99
|
+
const list = await apiFor(opts).listStages(await appFor(opts));
|
|
100
100
|
result(list, (l) =>
|
|
101
101
|
table(
|
|
102
102
|
l.map((s) => ({
|
|
@@ -118,7 +118,7 @@ export function registerManage(program: Command): void {
|
|
|
118
118
|
.option('--to <deploymentId>', 'a specific earlier deployment'),
|
|
119
119
|
).action(
|
|
120
120
|
action(async (opts: TargetOpts & { to?: string }) => {
|
|
121
|
-
const r = await apiFor(opts).rollback(appFor(opts), opts.stage, opts.to);
|
|
121
|
+
const r = await apiFor(opts).rollback(await appFor(opts), opts.stage, opts.to);
|
|
122
122
|
result(r, () => `${pc.green('✓')} rolled back ${opts.stage}`);
|
|
123
123
|
}),
|
|
124
124
|
);
|
|
@@ -139,7 +139,7 @@ export function registerManage(program: Command): void {
|
|
|
139
139
|
exitCode: EXIT.usage,
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
|
-
const app = appFor(opts);
|
|
142
|
+
const app = await appFor(opts);
|
|
143
143
|
await confirmByTyping(`destroy ${app}/${opts.stage}?`, opts.stage, opts.yes);
|
|
144
144
|
await apiFor(opts).destroyStage(app, opts.stage);
|
|
145
145
|
result(
|
|
@@ -153,7 +153,7 @@ export function registerManage(program: Command): void {
|
|
|
153
153
|
const cron = program.command('cron').description('scheduled jobs');
|
|
154
154
|
withTarget(cron.command('run <name>').description('run a cron now on a deployed stage')).action(
|
|
155
155
|
action(async (name: string, opts: TargetOpts) => {
|
|
156
|
-
await apiFor(opts).runCron(appFor(opts), name, opts.stage);
|
|
156
|
+
await apiFor(opts).runCron(await appFor(opts), name, opts.stage);
|
|
157
157
|
result(
|
|
158
158
|
{ cron: name, stage: opts.stage, triggered: true },
|
|
159
159
|
() => `${pc.green('✓')} triggered ${name} on ${opts.stage}`,
|
package/src/commands/observe.ts
CHANGED
|
@@ -32,7 +32,7 @@ export function registerObserve(program: Command): void {
|
|
|
32
32
|
) => {
|
|
33
33
|
const keep = (l: LogLine) => opts.runtime || l.level !== 'system';
|
|
34
34
|
const api = apiFor(opts);
|
|
35
|
-
const app = appFor(opts);
|
|
35
|
+
const app = await appFor(opts);
|
|
36
36
|
if (opts.follow) {
|
|
37
37
|
const ac = new AbortController();
|
|
38
38
|
process.once('SIGINT', () => ac.abort());
|
|
@@ -74,7 +74,7 @@ export function registerObserve(program: Command): void {
|
|
|
74
74
|
).action(
|
|
75
75
|
action(async (opts: TargetOpts & { since: string }) => {
|
|
76
76
|
const api = apiFor(opts);
|
|
77
|
-
const app = appFor(opts);
|
|
77
|
+
const app = await appFor(opts);
|
|
78
78
|
const range = sinceRange(opts.since);
|
|
79
79
|
const [metrics, routes, jobs] = await Promise.all([
|
|
80
80
|
api.metrics(app, { stage: opts.stage, ...range }),
|
|
@@ -150,7 +150,7 @@ export function registerObserve(program: Command): void {
|
|
|
150
150
|
|
|
151
151
|
withTarget(program.command('errors').description('grouped errors for a stage')).action(
|
|
152
152
|
action(async (opts: TargetOpts) => {
|
|
153
|
-
const groups = await apiFor(opts).errors(appFor(opts), { stage: opts.stage });
|
|
153
|
+
const groups = await apiFor(opts).errors(await appFor(opts), { stage: opts.stage });
|
|
154
154
|
result(groups, (g) =>
|
|
155
155
|
table(
|
|
156
156
|
g.map((e) => ({
|
|
@@ -173,7 +173,7 @@ export function registerObserve(program: Command): void {
|
|
|
173
173
|
.description('every event of one request/job across web, queues and worker'),
|
|
174
174
|
).action(
|
|
175
175
|
action(async (traceId: string, opts: TargetOpts) => {
|
|
176
|
-
const t = await apiFor(opts).trace(appFor(opts), traceId, { stage: opts.stage });
|
|
176
|
+
const t = await apiFor(opts).trace(await appFor(opts), traceId, { stage: opts.stage });
|
|
177
177
|
result(t, (v) => formatTrace(v.events));
|
|
178
178
|
}),
|
|
179
179
|
);
|
|
@@ -200,7 +200,7 @@ export function registerObserve(program: Command): void {
|
|
|
200
200
|
},
|
|
201
201
|
) => {
|
|
202
202
|
const api = apiFor(opts);
|
|
203
|
-
const app = appFor(opts);
|
|
203
|
+
const app = await appFor(opts);
|
|
204
204
|
if (opts.store) {
|
|
205
205
|
const r = await api.files(app, {
|
|
206
206
|
stage: opts.stage,
|
package/src/deploy.ts
CHANGED
|
@@ -31,11 +31,11 @@ export interface DeployResult {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export async function deploy(api: ApiClient, opts: DeployOptions): Promise<DeployResult> {
|
|
34
|
-
const project = loadProject(opts.cwd);
|
|
34
|
+
const project = await loadProject(opts.cwd);
|
|
35
35
|
const stage = deriveStage(opts.stage);
|
|
36
36
|
const app = project.manifest.name;
|
|
37
37
|
|
|
38
|
-
const { files } = await buildApp({ cwd: project.root });
|
|
38
|
+
const { files } = await buildApp({ cwd: project.root, project });
|
|
39
39
|
// The API always issues an upload for manifest.json; `files` lists the optional bundles.
|
|
40
40
|
const fileNames = (Object.keys(files) as ArtifactFile[]).filter((f) => f !== 'manifest.json');
|
|
41
41
|
const git = gitInfo(project.root);
|
package/src/dev/crons.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { CronExpressionParser } from 'cron-parser';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Local stand-in for EventBridge Scheduler (the emulator creates schedules but
|
|
5
|
-
* never fires them). Same expressions as
|
|
5
|
+
* never fires them). Same expressions as plat.config.ts: 5-field cron in UTC
|
|
6
6
|
* (or the cron's timezone) or `rate(n unit)`.
|
|
7
7
|
*/
|
|
8
8
|
export interface LocalCron {
|
package/src/dev/index.ts
CHANGED
|
@@ -35,7 +35,7 @@ export const DEV_CONTROL_PREFIX = '/__plat_dev/';
|
|
|
35
35
|
* worker (real Node, tsx watch) ← dispatcher (emulator SQS + EventBridge tap + local crons)
|
|
36
36
|
*/
|
|
37
37
|
export async function runDev(opts: DevOptions): Promise<void> {
|
|
38
|
-
const { root, manifest } = loadProject(opts.cwd);
|
|
38
|
+
const { root, manifest } = await loadProject(opts.cwd);
|
|
39
39
|
const children: ChildProcess[] = [];
|
|
40
40
|
const cleanups: Array<() => void | Promise<void>> = [];
|
|
41
41
|
let stage: LocalStage | undefined;
|
package/src/dev/local-stage.ts
CHANGED
|
@@ -136,7 +136,7 @@ export function emulatorClientConfig(conn: EmulatorConnection) {
|
|
|
136
136
|
/**
|
|
137
137
|
* `.env.local` (KEY=VALUE, not committed) → SecureString parameters, so
|
|
138
138
|
* `secrets.get()` works locally exactly as deployed. Only names declared in
|
|
139
|
-
*
|
|
139
|
+
* plat.config.ts are loaded.
|
|
140
140
|
*/
|
|
141
141
|
async function seedSecrets(conn: EmulatorConnection, bindings: Bindings, root: string): Promise<void> {
|
|
142
142
|
const values = readDotEnv(join(root, '.env.local'));
|
package/src/dev/router.ts
CHANGED
|
@@ -50,7 +50,7 @@ export function localDecision(state: LocalRouterState, uri: string): LocalDecisi
|
|
|
50
50
|
if (state.webDev) return { kind: 'webdev', target: state.webDev };
|
|
51
51
|
if (state.webDevStarting) return { kind: 'unavailable', reason: 'the frontend dev server is starting' };
|
|
52
52
|
if (!state.staticDir)
|
|
53
|
-
return { kind: 'unavailable', reason: 'no frontend: set web.static or web.dev in
|
|
53
|
+
return { kind: 'unavailable', reason: 'no frontend: set web.static or web.dev in plat.config.ts' };
|
|
54
54
|
const prefix = `/${state.record.s ?? ''}`;
|
|
55
55
|
const rel = d.key.startsWith(prefix) ? d.key.slice(prefix.length) : d.key;
|
|
56
56
|
const file = safeJoin(state.staticDir, decodeURIComponent(rel));
|
package/src/dev/test.ts
CHANGED
|
@@ -16,7 +16,7 @@ export const INTEGRATION_CONFIG = 'vitest.integration.config.ts';
|
|
|
16
16
|
* `vitest.integration.config.ts` when present.
|
|
17
17
|
*/
|
|
18
18
|
export async function runTests(opts: { cwd: string; args: string[] }): Promise<number> {
|
|
19
|
-
const { root, manifest } = loadProject(opts.cwd);
|
|
19
|
+
const { root, manifest } = await loadProject(opts.cwd);
|
|
20
20
|
step('starting the AWS Emulator');
|
|
21
21
|
let stage: Awaited<ReturnType<typeof startLocalStage>>;
|
|
22
22
|
try {
|
package/src/init.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
writeFileSync,
|
|
9
9
|
} from 'node:fs';
|
|
10
10
|
import { basename, dirname, extname, join, relative, resolve } from 'node:path';
|
|
11
|
-
import { validateAppName } from '@ericminassian/platform-core';
|
|
11
|
+
import { APP_CONFIG_FILE, validateAppName } from '@ericminassian/platform-core';
|
|
12
12
|
import { cliPackageRoot, cliVersion } from './project.ts';
|
|
13
13
|
import { CliError, EXIT } from './util/output.ts';
|
|
14
14
|
|
|
@@ -39,9 +39,9 @@ const TEXT_EXT = new Set([
|
|
|
39
39
|
/** Published CLI: `template/` inside the package. In this repo: `templates/app`. */
|
|
40
40
|
export function templateDir(): string {
|
|
41
41
|
const packaged = join(cliPackageRoot(), 'template');
|
|
42
|
-
if (existsSync(join(packaged,
|
|
42
|
+
if (existsSync(join(packaged, APP_CONFIG_FILE))) return packaged;
|
|
43
43
|
const repo = resolve(cliPackageRoot(), '..', '..', 'templates', 'app');
|
|
44
|
-
if (existsSync(join(repo,
|
|
44
|
+
if (existsSync(join(repo, APP_CONFIG_FILE))) return repo;
|
|
45
45
|
throw new CliError('app template not found (broken CLI install?)');
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -76,9 +76,22 @@ export function initApp(opts: InitOptions): InitResult {
|
|
|
76
76
|
const files = copyTree(src, dir, (rel, content) =>
|
|
77
77
|
rel === 'package.json' ? rewritePackageJson(content, name, range) : replaceAppName(content, name),
|
|
78
78
|
);
|
|
79
|
+
writeFileSync(join(dir, 'pnpm-workspace.yaml'), PNPM_WORKSPACE);
|
|
80
|
+
files.push('pnpm-workspace.yaml');
|
|
79
81
|
return { dir, name, files: files.sort() };
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
/**
|
|
85
|
+
* pnpm 10+ refuses to install (ERR_PNPM_IGNORED_BUILDS) until a dependency's
|
|
86
|
+
* install script is allowed, and esbuild (used by `plat build`) has one. It's
|
|
87
|
+
* written here rather than shipped in templates/app: a pnpm-workspace.yaml
|
|
88
|
+
* there would make that folder its own workspace inside this monorepo.
|
|
89
|
+
*/
|
|
90
|
+
export const PNPM_WORKSPACE = `# esbuild (used by \`plat build\`) installs its native binary with a build script.
|
|
91
|
+
allowBuilds:
|
|
92
|
+
esbuild: true
|
|
93
|
+
`;
|
|
94
|
+
|
|
82
95
|
export function replaceAppName(content: string, name: string): string {
|
|
83
96
|
return content.split(TEMPLATE_APP_NAME).join(name);
|
|
84
97
|
}
|