@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.
Files changed (52) hide show
  1. package/dist/build.d.ts +4 -1
  2. package/dist/build.js +4 -7
  3. package/dist/build.js.map +1 -1
  4. package/dist/commands/common.d.ts +2 -2
  5. package/dist/commands/common.js +2 -2
  6. package/dist/commands/common.js.map +1 -1
  7. package/dist/commands/lifecycle.js +4 -3
  8. package/dist/commands/lifecycle.js.map +1 -1
  9. package/dist/commands/manage.js +8 -8
  10. package/dist/commands/manage.js.map +1 -1
  11. package/dist/commands/observe.js +5 -5
  12. package/dist/commands/observe.js.map +1 -1
  13. package/dist/deploy.js +2 -2
  14. package/dist/deploy.js.map +1 -1
  15. package/dist/dev/crons.d.ts +1 -1
  16. package/dist/dev/index.js +1 -1
  17. package/dist/dev/index.js.map +1 -1
  18. package/dist/dev/local-stage.js +1 -1
  19. package/dist/dev/router.js +1 -1
  20. package/dist/dev/router.js.map +1 -1
  21. package/dist/dev/test.js +1 -1
  22. package/dist/dev/test.js.map +1 -1
  23. package/dist/init.d.ts +7 -0
  24. package/dist/init.js +15 -3
  25. package/dist/init.js.map +1 -1
  26. package/dist/project.d.ts +6 -7
  27. package/dist/project.js +78 -21
  28. package/dist/project.js.map +1 -1
  29. package/dist/util/esbuild.d.ts +2 -0
  30. package/dist/util/esbuild.js +9 -0
  31. package/dist/util/esbuild.js.map +1 -0
  32. package/package.json +4 -4
  33. package/src/build.ts +7 -10
  34. package/src/commands/common.ts +3 -3
  35. package/src/commands/lifecycle.ts +4 -3
  36. package/src/commands/manage.ts +8 -8
  37. package/src/commands/observe.ts +5 -5
  38. package/src/deploy.ts +2 -2
  39. package/src/dev/crons.ts +1 -1
  40. package/src/dev/index.ts +1 -1
  41. package/src/dev/local-stage.ts +1 -1
  42. package/src/dev/router.ts +1 -1
  43. package/src/dev/test.ts +1 -1
  44. package/src/init.ts +16 -3
  45. package/src/project.ts +76 -22
  46. package/src/util/esbuild.ts +10 -0
  47. package/template/AGENTS.md +23 -18
  48. package/template/plat.config.ts +31 -0
  49. package/template/src/server.ts +3 -3
  50. package/template/src/worker.ts +5 -5
  51. package/template/tsconfig.json +1 -1
  52. 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, 'platform.json')))
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, 'platform.json')))
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":"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"}
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 platform.json. */
3
+ /** Directory containing plat.config.ts. */
5
4
  root: string;
6
- /** platform.json as written. */
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 platform.json. */
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 platform.json. */
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
- import { existsSync, readFileSync } from 'node:fs';
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
- export const MANIFEST_FILE = 'platform.json';
8
- /** Walk up from `cwd` to the nearest platform.json. */
9
- export function findProjectRoot(cwd) {
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, MANIFEST_FILE)))
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
- export function loadProject(cwd) {
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
- throw new CliError(`no ${MANIFEST_FILE} found in ${cwd} or its parents`, {
24
- hint: 'run this inside an app, or create one with `plat init <dir> --name <app>`',
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
- let raw;
29
- try {
30
- raw = JSON.parse(readFileSync(join(root, MANIFEST_FILE), 'utf8'));
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
- /** App name from `--app`, else the nearest platform.json. */
47
- export function resolveAppName(opt, cwd = process.cwd()) {
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
- const root = findProjectRoot(cwd);
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(root).manifest.name;
114
+ return (await loadProject(cwd)).manifest.name;
58
115
  }
59
116
  export function cliVersion() {
60
117
  return cliPackageJson().version ?? '0.0.0';
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** An esbuild failure as `file:line: message` items (or the error's message), for a CliError list. */
2
+ export declare function buildFailureList(err: unknown): string;
@@ -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.1.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.1.0",
38
- "@ericminassian/platform-core": "0.1.0",
39
- "@ericminassian/platform-engine": "0.1.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
- const errors = (
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
  }
@@ -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 platform.json) and `--stage` (defaults to prod). */
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 platform.json)');
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
- ` gh repo create <owner>/${v.name} --private --source . --push`,
34
- ` plat apps create ${v.name} --repo <owner>/${v.name}`,
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'),
@@ -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}`,
@@ -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 platform.json: 5-field cron in UTC
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;
@@ -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
- * platform.json are loaded.
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 platform.json' };
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, 'platform.json'))) return packaged;
42
+ if (existsSync(join(packaged, APP_CONFIG_FILE))) return packaged;
43
43
  const repo = resolve(cliPackageRoot(), '..', '..', 'templates', 'app');
44
- if (existsSync(join(repo, 'platform.json'))) return 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
  }