@celsoft/platform 0.0.0-stage → 0.1.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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/cloudflare.d.ts +14 -0
  3. package/dist/adapters/cloudflare.js +78 -0
  4. package/dist/adapters/cloudflare.js.map +1 -0
  5. package/dist/adapters/github.d.ts +9 -0
  6. package/dist/adapters/github.js +44 -0
  7. package/dist/adapters/github.js.map +1 -0
  8. package/dist/adapters/neon.d.ts +11 -0
  9. package/dist/adapters/neon.js +49 -0
  10. package/dist/adapters/neon.js.map +1 -0
  11. package/dist/adapters/process.d.ts +14 -0
  12. package/dist/adapters/process.js +46 -0
  13. package/dist/adapters/process.js.map +1 -0
  14. package/dist/cli.d.ts +2 -0
  15. package/dist/cli.js +136 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/config.d.ts +23 -0
  18. package/dist/config.js +66 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/fakes.d.ts +53 -0
  21. package/dist/fakes.js +89 -0
  22. package/dist/fakes.js.map +1 -0
  23. package/dist/init.d.ts +17 -0
  24. package/dist/init.js +77 -0
  25. package/dist/init.js.map +1 -0
  26. package/dist/lifecycle.d.ts +19 -0
  27. package/dist/lifecycle.js +89 -0
  28. package/dist/lifecycle.js.map +1 -0
  29. package/dist/ports.d.ts +48 -0
  30. package/dist/ports.js +2 -0
  31. package/dist/ports.js.map +1 -0
  32. package/dist/run.d.ts +9 -0
  33. package/dist/run.js +29 -0
  34. package/dist/run.js.map +1 -0
  35. package/dist/runtime/index.d.ts +22 -0
  36. package/dist/runtime/index.js +29 -0
  37. package/dist/runtime/index.js.map +1 -0
  38. package/dist/secrets.d.ts +3 -0
  39. package/dist/secrets.js +35 -0
  40. package/dist/secrets.js.map +1 -0
  41. package/dist/target.d.ts +15 -0
  42. package/dist/target.js +15 -0
  43. package/dist/target.js.map +1 -0
  44. package/dist/workflow.d.ts +7 -0
  45. package/dist/workflow.js +45 -0
  46. package/dist/workflow.js.map +1 -0
  47. package/dist/wrangler-config.d.ts +9 -0
  48. package/dist/wrangler-config.js +17 -0
  49. package/dist/wrangler-config.js.map +1 -0
  50. package/dist/write-schema.d.ts +1 -0
  51. package/dist/write-schema.js +4 -0
  52. package/dist/write-schema.js.map +1 -0
  53. package/package.json +32 -4
  54. package/schema.json +70 -0
  55. package/README.md +0 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sean O'Dell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,14 @@
1
+ import type { Cloudflare, Hyperdrive, Zone } from "../ports.js";
2
+ export declare class CloudflareApi implements Cloudflare {
3
+ private readonly client;
4
+ private account;
5
+ constructor(apiToken: string);
6
+ accountId(): Promise<string>;
7
+ findZone(hostname: string): Promise<Zone | undefined>;
8
+ findHyperdrive(name: string): Promise<Hyperdrive | undefined>;
9
+ createHyperdrive(name: string, connectionUri: string): Promise<Hyperdrive>;
10
+ updateHyperdrive(id: string, connectionUri: string): Promise<Hyperdrive>;
11
+ deleteHyperdrive(id: string): Promise<void>;
12
+ findWorker(name: string): Promise<boolean>;
13
+ deleteWorker(name: string): Promise<void>;
14
+ }
@@ -0,0 +1,78 @@
1
+ import CloudflareClient from "cloudflare";
2
+ import { originOf } from "../target.js";
3
+ export class CloudflareApi {
4
+ client;
5
+ account;
6
+ constructor(apiToken) {
7
+ this.client = new CloudflareClient({ apiToken });
8
+ }
9
+ async accountId() {
10
+ if (this.account)
11
+ return this.account;
12
+ const accounts = [];
13
+ for await (const account of this.client.accounts.list())
14
+ accounts.push(account);
15
+ if (accounts.length !== 1) {
16
+ throw new Error(`the Cloudflare token must reach exactly one account, but reaches ${accounts.length}; set Account Resources to a single account`);
17
+ }
18
+ this.account = accounts[0].id;
19
+ return this.account;
20
+ }
21
+ async findZone(hostname) {
22
+ const labels = hostname.split(".");
23
+ for (let i = 0; i < labels.length - 1; i++) {
24
+ const name = labels.slice(i).join(".");
25
+ const page = await this.client.zones.list({ name });
26
+ const zone = page.result[0];
27
+ if (zone)
28
+ return { id: zone.id, name: zone.name };
29
+ }
30
+ return undefined;
31
+ }
32
+ async findHyperdrive(name) {
33
+ const account_id = await this.accountId();
34
+ for await (const config of this.client.hyperdrive.configs.list({ account_id })) {
35
+ if (config.name === name) {
36
+ const origin = config.origin;
37
+ return { id: config.id, name, origin: `${origin.user}@${origin.host}/${origin.database}`, cached: !config.caching?.disabled };
38
+ }
39
+ }
40
+ return undefined;
41
+ }
42
+ async createHyperdrive(name, connectionUri) {
43
+ const account_id = await this.accountId();
44
+ const created = await this.client.hyperdrive.configs.create({ account_id, name, origin: originParams(connectionUri), caching: { disabled: true } });
45
+ return { id: created.id, name, origin: originOf(connectionUri), cached: false };
46
+ }
47
+ async updateHyperdrive(id, connectionUri) {
48
+ const account_id = await this.accountId();
49
+ const updated = await this.client.hyperdrive.configs.edit(id, { account_id, origin: originParams(connectionUri), caching: { disabled: true } });
50
+ return { id, name: updated.name, origin: originOf(connectionUri), cached: false };
51
+ }
52
+ async deleteHyperdrive(id) {
53
+ await this.client.hyperdrive.configs.delete(id, { account_id: await this.accountId() });
54
+ }
55
+ async findWorker(name) {
56
+ const account_id = await this.accountId();
57
+ for await (const script of this.client.workers.scripts.list({ account_id })) {
58
+ if (script.id === name)
59
+ return true;
60
+ }
61
+ return false;
62
+ }
63
+ async deleteWorker(name) {
64
+ await this.client.workers.scripts.delete(name, { account_id: await this.accountId(), force: true });
65
+ }
66
+ }
67
+ function originParams(connectionUri) {
68
+ const url = new URL(connectionUri);
69
+ return {
70
+ scheme: "postgresql",
71
+ host: url.hostname,
72
+ port: Number(url.port || 5432),
73
+ database: url.pathname.slice(1),
74
+ user: decodeURIComponent(url.username),
75
+ password: decodeURIComponent(url.password),
76
+ };
77
+ }
78
+ //# sourceMappingURL=cloudflare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../src/adapters/cloudflare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { GitHub } from "../ports.js";
2
+ export declare class GhCli implements GitHub {
3
+ private readonly repoRoot;
4
+ constructor(repoRoot: string);
5
+ setSecret(name: string, value: string): Promise<void>;
6
+ ensureEnvironment(name: string): Promise<void>;
7
+ private gh;
8
+ }
9
+ export declare function commentPreviewLink(env: NodeJS.ProcessEnv, pr: number, url: string, sha?: string): Promise<void>;
@@ -0,0 +1,44 @@
1
+ import { spawn } from "node:child_process";
2
+ export class GhCli {
3
+ repoRoot;
4
+ constructor(repoRoot) {
5
+ this.repoRoot = repoRoot;
6
+ }
7
+ async setSecret(name, value) {
8
+ await this.gh(["secret", "set", name], value);
9
+ }
10
+ async ensureEnvironment(name) {
11
+ const repo = (await this.gh(["repo", "view", "--json", "nameWithOwner", "--jq", ".nameWithOwner"])).trim();
12
+ await this.gh(["api", "--method", "PUT", `repos/${repo}/environments/${name}`, "--silent"]);
13
+ }
14
+ gh(args, stdin) {
15
+ return new Promise((resolve, reject) => {
16
+ const child = spawn("gh", args, { cwd: this.repoRoot, stdio: ["pipe", "pipe", "inherit"] });
17
+ let out = "";
18
+ child.stdout.on("data", (chunk) => (out += chunk.toString()));
19
+ child.on("error", (error) => reject(new Error(`could not run gh, the GitHub CLI (${error.message}); install it and run \`gh auth login\``)));
20
+ child.on("exit", (code) => (code === 0 ? resolve(out) : reject(new Error(`gh ${args[0]} ${args[1]} exited with code ${code}`))));
21
+ child.stdin.end(stdin ?? "");
22
+ });
23
+ }
24
+ }
25
+ const MARKER = "<!-- platform-preview -->";
26
+ export async function commentPreviewLink(env, pr, url, sha) {
27
+ const token = env.GITHUB_TOKEN;
28
+ const repo = env.GITHUB_REPOSITORY;
29
+ if (!token || !repo)
30
+ return;
31
+ const api = (path, init) => fetch(`https://api.github.com/repos/${repo}/${path}`, {
32
+ ...init,
33
+ headers: { authorization: `Bearer ${token}`, accept: "application/vnd.github+json", "content-type": "application/json" },
34
+ });
35
+ const body = `${MARKER}\nPreview: ${url}\n\nCommit ${(sha ?? env.GITHUB_SHA)?.slice(0, 7) ?? ""}`;
36
+ const comments = (await (await api(`issues/${pr}/comments?per_page=100`)).json());
37
+ const existing = comments.find((c) => c.body?.startsWith(MARKER));
38
+ const response = existing
39
+ ? await api(`issues/comments/${existing.id}`, { method: "PATCH", body: JSON.stringify({ body }) })
40
+ : await api(`issues/${pr}/comments`, { method: "POST", body: JSON.stringify({ body }) });
41
+ if (!response.ok)
42
+ throw new Error(`could not comment the preview link on PR ${pr}: ${response.status}`);
43
+ }
44
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/adapters/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Branch, Neon } from "../ports.js";
2
+ export declare class NeonApi implements Neon {
3
+ private readonly api;
4
+ constructor(apiKey: string);
5
+ defaultBranch(project: string): Promise<Branch>;
6
+ findBranch(project: string, name: string): Promise<Branch | undefined>;
7
+ listBranches(project: string, namePrefix: string): Promise<Branch[]>;
8
+ createBranch(project: string, name: string, parentId: string, compute: boolean): Promise<Branch>;
9
+ deleteBranch(project: string, branchId: string): Promise<void>;
10
+ connectionUri(project: string, branchId: string): Promise<string>;
11
+ }
@@ -0,0 +1,49 @@
1
+ import { createApiClient, EndpointType } from "@neondatabase/api-client";
2
+ export class NeonApi {
3
+ api;
4
+ constructor(apiKey) {
5
+ this.api = createApiClient({ apiKey });
6
+ }
7
+ async defaultBranch(project) {
8
+ const { data } = await this.api.listProjectBranches({ projectId: project });
9
+ const branch = data.branches.find((b) => b.default);
10
+ if (!branch)
11
+ throw new Error(`Neon project ${project} has no default branch`);
12
+ return { id: branch.id, name: branch.name };
13
+ }
14
+ async findBranch(project, name) {
15
+ const { data } = await this.api.listProjectBranches({ projectId: project, search: name });
16
+ const branch = data.branches.find((b) => b.name === name);
17
+ return branch && { id: branch.id, name: branch.name };
18
+ }
19
+ async listBranches(project, namePrefix) {
20
+ const { data } = await this.api.listProjectBranches({ projectId: project, search: namePrefix });
21
+ return data.branches.filter((b) => b.name.startsWith(namePrefix)).map((b) => ({ id: b.id, name: b.name }));
22
+ }
23
+ async createBranch(project, name, parentId, compute) {
24
+ const { data } = await this.api.createProjectBranch(project, {
25
+ branch: { name, parent_id: parentId },
26
+ endpoints: compute ? [{ type: EndpointType.ReadWrite }] : [],
27
+ });
28
+ return { id: data.branch.id, name: data.branch.name };
29
+ }
30
+ async deleteBranch(project, branchId) {
31
+ await this.api.deleteProjectBranch({ projectId: project, branchId });
32
+ }
33
+ async connectionUri(project, branchId) {
34
+ const { data } = await this.api.listProjectBranchDatabases(project, branchId);
35
+ const database = data.databases[0];
36
+ if (!database)
37
+ throw new Error(`Neon branch ${branchId} has no database`);
38
+ const { data: uri } = await this.api.getConnectionUri({
39
+ projectId: project,
40
+ branch_id: branchId,
41
+ database_name: database.name,
42
+ role_name: database.owner_name,
43
+ // Hyperdrive does its own pooling, so it needs Neon's direct endpoint.
44
+ pooled: false,
45
+ });
46
+ return uri.uri;
47
+ }
48
+ }
49
+ //# sourceMappingURL=neon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neon.js","sourceRoot":"","sources":["../../src/adapters/neon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Migrator, Wrangler } from "../ports.js";
2
+ export declare class WranglerCli implements Wrangler {
3
+ private readonly appDir;
4
+ private readonly env;
5
+ constructor(appDir: string, env: {
6
+ CLOUDFLARE_API_TOKEN: string;
7
+ });
8
+ deploy(config: object, secrets: Record<string, string>): Promise<void>;
9
+ }
10
+ export declare class ShellMigrator implements Migrator {
11
+ private readonly appDir;
12
+ constructor(appDir: string);
13
+ migrate(command: string, databaseUrl: string): Promise<void>;
14
+ }
@@ -0,0 +1,46 @@
1
+ import { spawn } from "node:child_process";
2
+ import { rm, writeFile } from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, join } from "node:path";
5
+ export class WranglerCli {
6
+ appDir;
7
+ env;
8
+ constructor(appDir, env) {
9
+ this.appDir = appDir;
10
+ this.env = env;
11
+ }
12
+ async deploy(config, secrets) {
13
+ const configFile = join(this.appDir, ".platform-wrangler.json");
14
+ const secretsFile = join(this.appDir, ".platform-secrets.json");
15
+ await writeFile(configFile, JSON.stringify(config, null, 2));
16
+ await writeFile(secretsFile, JSON.stringify(secrets), { mode: 0o600 });
17
+ try {
18
+ const wrangler = join(dirname(createRequire(import.meta.url).resolve("wrangler/package.json")), "bin", "wrangler.js");
19
+ await run("wrangler deploy", process.execPath, [wrangler, "deploy", "--config", configFile, "--secrets-file", secretsFile], {
20
+ cwd: this.appDir,
21
+ env: { ...process.env, ...this.env },
22
+ });
23
+ }
24
+ finally {
25
+ await rm(secretsFile, { force: true });
26
+ await rm(configFile, { force: true });
27
+ }
28
+ }
29
+ }
30
+ export class ShellMigrator {
31
+ appDir;
32
+ constructor(appDir) {
33
+ this.appDir = appDir;
34
+ }
35
+ async migrate(command, databaseUrl) {
36
+ await run(`migrations (${command})`, command, [], { cwd: this.appDir, env: { ...process.env, DATABASE_URL: databaseUrl }, shell: true });
37
+ }
38
+ }
39
+ function run(label, command, args, options) {
40
+ return new Promise((resolve, reject) => {
41
+ const child = spawn(command, args, { ...options, stdio: "inherit" });
42
+ child.on("error", reject);
43
+ child.on("exit", (code) => (code === 0 ? resolve() : reject(new Error(`${label} exited with code ${code}`))));
44
+ });
45
+ }
46
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/adapters/process.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from "node:child_process";
3
+ import { readFile } from "node:fs/promises";
4
+ import { resolve } from "node:path";
5
+ import { parseArgs } from "node:util";
6
+ import { CloudflareApi } from "./adapters/cloudflare.js";
7
+ import { commentPreviewLink, GhCli } from "./adapters/github.js";
8
+ import { NeonApi } from "./adapters/neon.js";
9
+ import { ShellMigrator, WranglerCli } from "./adapters/process.js";
10
+ import { ConfigError, loadConfig } from "./config.js";
11
+ import { checkWorkflow, init } from "./init.js";
12
+ import { cleanup, deploy, PreflightError } from "./lifecycle.js";
13
+ import { Run } from "./run.js";
14
+ import { loadSecrets } from "./secrets.js";
15
+ const usage = `usage: platform <command> [--dry-run] [--dir <app dir>]
16
+
17
+ commands:
18
+ init set up GitHub secrets, Environments and the deploy workflow
19
+ deploy deploy prod
20
+ preview <pr> create or update the preview for a pull request
21
+ cleanup <pr> delete the preview for a pull request
22
+ ci run the command for the current GitHub Actions event`;
23
+ async function main(argv) {
24
+ const { values, positionals } = parseArgs({
25
+ args: argv,
26
+ allowPositionals: true,
27
+ options: { "dry-run": { type: "boolean", default: false }, dir: { type: "string", default: "." }, help: { type: "boolean", short: "h" } },
28
+ });
29
+ if (values.help || positionals.length === 0) {
30
+ console.log(usage);
31
+ return;
32
+ }
33
+ const appDir = resolve(values.dir);
34
+ const dryRun = values["dry-run"];
35
+ const inCi = positionals[0] === "ci";
36
+ const command = inCi ? await commandForGitHubEvent() : parseCommand(positionals);
37
+ if (!command) {
38
+ console.log("nothing to do for this event");
39
+ return;
40
+ }
41
+ if (command.name === "init") {
42
+ const config = await loadConfig(appDir);
43
+ await init({
44
+ repoRoot: repoRoot(appDir),
45
+ appDir,
46
+ config,
47
+ run: new Run(dryRun, (line) => console.log(line)),
48
+ connect: (tokens) => ({
49
+ cloudflare: new CloudflareApi(tokens.CLOUDFLARE_API_TOKEN),
50
+ neon: new NeonApi(tokens.NEON_API_KEY ?? ""),
51
+ github: new GhCli(repoRoot(appDir)),
52
+ }),
53
+ });
54
+ return;
55
+ }
56
+ if (inCi)
57
+ await checkWorkflow(repoRoot(appDir), appDir);
58
+ const ctx = await context(appDir, dryRun);
59
+ if (command.name === "cleanup") {
60
+ await cleanup(ctx, command.pr);
61
+ return;
62
+ }
63
+ const { hostname } = await deploy(ctx, command.name === "deploy" ? { kind: "prod" } : { kind: "preview", pr: command.pr });
64
+ if (dryRun)
65
+ return;
66
+ console.log(`\nlive at https://${hostname}`);
67
+ if (inCi && command.name === "preview") {
68
+ await commentPreviewLink(process.env, command.pr, `https://${hostname}`, command.sha);
69
+ console.log("\nprod dry run with this change:");
70
+ await deploy({ ...ctx, run: new Run(true, (line) => console.log(line)) }, { kind: "prod" });
71
+ }
72
+ }
73
+ function parseCommand([name, pr]) {
74
+ if (name === "init" || name === "deploy")
75
+ return { name };
76
+ if (name === "preview" || name === "cleanup")
77
+ return { name, pr: prNumber(pr) };
78
+ throw new UsageError(`unknown command ${name}\n\n${usage}`);
79
+ }
80
+ async function commandForGitHubEvent() {
81
+ const name = process.env.GITHUB_EVENT_NAME;
82
+ const path = process.env.GITHUB_EVENT_PATH;
83
+ if (!name || !path)
84
+ throw new UsageError("`platform ci` runs only inside GitHub Actions");
85
+ const event = JSON.parse(await readFile(path, "utf8"));
86
+ if (name === "push")
87
+ return { name: "deploy" };
88
+ if (name === "pull_request")
89
+ return event.action === "closed" ? { name: "cleanup", pr: event.number } : { name: "preview", pr: event.number, sha: event.pull_request?.head?.sha };
90
+ return undefined;
91
+ }
92
+ async function context(appDir, dryRun) {
93
+ const config = await loadConfig(appDir);
94
+ const secrets = await loadSecrets(appDir);
95
+ const cloudflareToken = requireToken(secrets, "CLOUDFLARE_API_TOKEN");
96
+ const neonKey = config.database ? requireToken(secrets, "NEON_API_KEY") : "";
97
+ return {
98
+ config,
99
+ secrets,
100
+ run: new Run(dryRun, (line) => console.log(line)),
101
+ ports: {
102
+ cloudflare: new CloudflareApi(cloudflareToken),
103
+ neon: new NeonApi(neonKey),
104
+ wrangler: new WranglerCli(appDir, { CLOUDFLARE_API_TOKEN: cloudflareToken }),
105
+ migrator: new ShellMigrator(appDir),
106
+ },
107
+ };
108
+ }
109
+ function repoRoot(dir) {
110
+ try {
111
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd: dir, encoding: "utf8" }).trim();
112
+ }
113
+ catch {
114
+ throw new PreflightError(`${dir} is not inside a git repository`);
115
+ }
116
+ }
117
+ function requireToken(secrets, name) {
118
+ const value = secrets[name];
119
+ if (!value)
120
+ throw new PreflightError(`${name} is not set; add it to .env.secrets, or to the repo's GitHub secrets in CI`);
121
+ return value;
122
+ }
123
+ function prNumber(value) {
124
+ const pr = Number(value);
125
+ if (!Number.isInteger(pr) || pr <= 0)
126
+ throw new UsageError(`expected a pull request number, got ${value ?? "nothing"}`);
127
+ return pr;
128
+ }
129
+ class UsageError extends Error {
130
+ }
131
+ main(process.argv.slice(2)).catch((error) => {
132
+ const known = error instanceof ConfigError || error instanceof PreflightError || error instanceof UsageError;
133
+ console.error(`✗ ${known ? error.message : error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
134
+ process.exit(1);
135
+ });
136
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ export declare const CONFIG_FILE = "platform.jsonc";
3
+ export declare const CONFIG_VERSION = 1;
4
+ export declare const configSchema: z.ZodObject<{
5
+ $schema: z.ZodOptional<z.ZodString>;
6
+ version: z.ZodLiteral<1>;
7
+ name: z.ZodString;
8
+ hostname: z.ZodString;
9
+ worker: z.ZodString;
10
+ compatibilityDate: z.ZodString;
11
+ frontend: z.ZodOptional<z.ZodString>;
12
+ database: z.ZodOptional<z.ZodObject<{
13
+ neonProject: z.ZodString;
14
+ migrate: z.ZodString;
15
+ }, z.core.$strict>>;
16
+ secrets: z.ZodDefault<z.ZodArray<z.ZodString>>;
17
+ }, z.core.$strict>;
18
+ export type Config = z.output<typeof configSchema>;
19
+ export declare class ConfigError extends Error {
20
+ }
21
+ export declare function parseConfig(text: string, source?: string): Config;
22
+ export declare function loadConfig(dir: string): Promise<Config>;
23
+ export declare function configJsonSchema(): object;
package/dist/config.js ADDED
@@ -0,0 +1,66 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { parse, printParseErrorCode } from "jsonc-parser";
4
+ import { z } from "zod";
5
+ export const CONFIG_FILE = "platform.jsonc";
6
+ export const CONFIG_VERSION = 1;
7
+ const hostname = z
8
+ .string()
9
+ .regex(/^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/, "must be a lowercase hostname such as app.example.com");
10
+ export const configSchema = z
11
+ .object({
12
+ $schema: z.string().optional(),
13
+ version: z.literal(CONFIG_VERSION),
14
+ // Leaves room for "-pr-<number>" within Cloudflare's 63-character Worker name limit.
15
+ name: z.string().regex(/^[a-z][a-z0-9-]{0,49}$/, "must start with a letter and use only a-z, 0-9 and -, at most 50 characters"),
16
+ hostname,
17
+ worker: z.string().min(1).describe("Path to the Worker's entry module, relative to platform.jsonc"),
18
+ compatibilityDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be a date such as 2026-10-01"),
19
+ frontend: z.string().min(1).optional().describe("Directory holding the built static files"),
20
+ database: z
21
+ .object({
22
+ neonProject: z.string().regex(/^[a-z]+-[a-z]+-\d+$/, "must be a Neon project id such as rapid-bonus-11035110, not a branch id"),
23
+ migrate: z.string().min(1).describe("Command that runs migrations against $DATABASE_URL"),
24
+ })
25
+ .strict()
26
+ .optional(),
27
+ secrets: z.array(z.string().regex(/^[A-Z][A-Z0-9_]*$/, "must be an UPPER_SNAKE_CASE name")).default([]),
28
+ })
29
+ .strict();
30
+ export class ConfigError extends Error {
31
+ }
32
+ export function parseConfig(text, source = CONFIG_FILE) {
33
+ const errors = [];
34
+ const raw = parse(text, errors, { allowTrailingComma: true });
35
+ if (errors.length > 0) {
36
+ const first = errors[0];
37
+ throw new ConfigError(`${source}: ${printParseErrorCode(first.error)} at offset ${first.offset}`);
38
+ }
39
+ if (isObject(raw) && typeof raw.version === "number" && raw.version < CONFIG_VERSION) {
40
+ throw new ConfigError(`${source}: version ${raw.version} is older than this platform's ${CONFIG_VERSION}; run \`platform upgrade\``);
41
+ }
42
+ const result = configSchema.safeParse(raw);
43
+ if (!result.success) {
44
+ const lines = result.error.issues.map((issue) => ` ${issue.path.join(".") || "(root)"}: ${issue.message}`);
45
+ throw new ConfigError(`${source} is invalid:\n${lines.join("\n")}`);
46
+ }
47
+ return result.data;
48
+ }
49
+ export async function loadConfig(dir) {
50
+ const path = join(dir, CONFIG_FILE);
51
+ let text;
52
+ try {
53
+ text = await readFile(path, "utf8");
54
+ }
55
+ catch {
56
+ throw new ConfigError(`${path} not found; run \`platform init\``);
57
+ }
58
+ return parseConfig(text, path);
59
+ }
60
+ export function configJsonSchema() {
61
+ return z.toJSONSchema(configSchema, { io: "input" });
62
+ }
63
+ function isObject(value) {
64
+ return typeof value === "object" && value !== null;
65
+ }
66
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { Branch, Cloudflare, Hyperdrive, Migrator, Neon, Wrangler, Zone } from "./ports.js";
2
+ export declare class FakeNeon implements Neon {
3
+ branches: Branch[];
4
+ private next;
5
+ defaultBranch(): Promise<Branch>;
6
+ findBranch(_project: string, name: string): Promise<Branch | undefined>;
7
+ listBranches(_project: string, namePrefix: string): Promise<Branch[]>;
8
+ createBranch(_project: string, name: string): Promise<{
9
+ id: string;
10
+ name: string;
11
+ }>;
12
+ deleteBranch(_project: string, branchId: string): Promise<void>;
13
+ connectionUri(_project: string, branchId: string): Promise<string>;
14
+ }
15
+ export declare class FakeCloudflare implements Cloudflare {
16
+ zones: Zone[];
17
+ hyperdrives: Hyperdrive[];
18
+ workers: Set<string>;
19
+ private next;
20
+ accountId(): Promise<string>;
21
+ findZone(hostname: string): Promise<Zone | undefined>;
22
+ findHyperdrive(name: string): Promise<Hyperdrive | undefined>;
23
+ createHyperdrive(name: string, uri: string): Promise<{
24
+ id: string;
25
+ name: string;
26
+ origin: string;
27
+ cached: boolean;
28
+ }>;
29
+ updateHyperdrive(id: string, uri: string): Promise<Hyperdrive>;
30
+ deleteHyperdrive(id: string): Promise<void>;
31
+ findWorker(name: string): Promise<boolean>;
32
+ deleteWorker(name: string): Promise<void>;
33
+ }
34
+ export declare class FakeWrangler implements Wrangler {
35
+ private readonly cloudflare;
36
+ deploys: {
37
+ config: Record<string, unknown>;
38
+ secrets: Record<string, string>;
39
+ }[];
40
+ constructor(cloudflare: FakeCloudflare);
41
+ deploy(config: object, secrets: Record<string, string>): Promise<void>;
42
+ }
43
+ export declare class FakeMigrator implements Migrator {
44
+ runs: string[];
45
+ failWith: Error | undefined;
46
+ migrate(_command: string, databaseUrl: string): Promise<void>;
47
+ }
48
+ export declare function fakePorts(): {
49
+ neon: FakeNeon;
50
+ cloudflare: FakeCloudflare;
51
+ wrangler: FakeWrangler;
52
+ migrator: FakeMigrator;
53
+ };