vyft 0.2.0-alpha → 0.4.0-alpha

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 (56) hide show
  1. package/README.md +5 -16
  2. package/dist/build.d.ts +1 -0
  3. package/dist/build.js +9 -4
  4. package/dist/cli.js +648 -43
  5. package/dist/context.d.ts +39 -0
  6. package/dist/context.js +101 -0
  7. package/dist/docker.d.ts +24 -12
  8. package/dist/docker.js +299 -389
  9. package/dist/exec.d.ts +1 -1
  10. package/dist/exec.js +2 -2
  11. package/dist/index.d.ts +4 -1
  12. package/dist/index.js +5 -1
  13. package/dist/init.js +19 -2
  14. package/dist/interpolate.d.ts +11 -0
  15. package/dist/interpolate.js +11 -0
  16. package/dist/local/dev.d.ts +31 -0
  17. package/dist/local/dev.js +109 -0
  18. package/dist/local/index.d.ts +2 -0
  19. package/dist/local/index.js +2 -0
  20. package/dist/local/runtime.d.ts +61 -0
  21. package/dist/local/runtime.js +391 -0
  22. package/dist/proxy.d.ts +16 -0
  23. package/dist/proxy.js +0 -0
  24. package/dist/resource.d.ts +104 -1
  25. package/dist/resource.js +11 -1
  26. package/dist/runtime.d.ts +11 -1
  27. package/dist/services/index.d.ts +26 -0
  28. package/dist/services/index.js +35 -0
  29. package/dist/services/minio.d.ts +36 -0
  30. package/dist/services/minio.js +53 -0
  31. package/dist/services/mongo.d.ts +28 -0
  32. package/dist/services/mongo.js +45 -0
  33. package/dist/services/mysql.d.ts +28 -0
  34. package/dist/services/mysql.js +44 -0
  35. package/dist/services/nats.d.ts +26 -0
  36. package/dist/services/nats.js +38 -0
  37. package/dist/services/postgres.d.ts +28 -0
  38. package/dist/services/postgres.js +45 -0
  39. package/dist/services/rabbitmq.d.ts +28 -0
  40. package/dist/services/rabbitmq.js +44 -0
  41. package/dist/services/redis.d.ts +28 -0
  42. package/dist/services/redis.js +49 -0
  43. package/dist/services/storage.d.ts +39 -0
  44. package/dist/services/storage.js +94 -0
  45. package/dist/swarm/factories.d.ts +9 -2
  46. package/dist/swarm/factories.js +9 -32
  47. package/dist/swarm/index.d.ts +11 -2
  48. package/dist/swarm/proxy.d.ts +24 -0
  49. package/dist/swarm/proxy.js +339 -0
  50. package/dist/swarm/types.d.ts +11 -21
  51. package/dist/symbols.d.ts +7 -0
  52. package/dist/symbols.js +3 -0
  53. package/package.json +4 -5
  54. package/templates/fullstack/package.json +2 -6
  55. package/templates/fullstack/vyft.config.ts +13 -28
  56. package/templates/fullstack/compose.yaml +0 -14
package/dist/exec.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  import type { Logger } from "pino";
2
- export declare function exec(command: string, cwd: string, env?: Record<string, string>, log?: Logger): Promise<void>;
2
+ export declare function exec(command: string, cwd: string, env?: Record<string, string>, log?: Logger, silent?: boolean): Promise<void>;
package/dist/exec.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { spawn } from "node:child_process";
2
- export async function exec(command, cwd, env, log) {
2
+ export async function exec(command, cwd, env, log, silent) {
3
3
  const start = performance.now();
4
4
  log?.debug({ command, cwd }, "process spawning");
5
5
  return new Promise((resolve, reject) => {
6
6
  const proc = spawn(command, {
7
7
  cwd,
8
- stdio: "inherit",
8
+ stdio: silent ? "pipe" : "inherit",
9
9
  shell: true,
10
10
  env: env ? { ...process.env, ...env } : undefined,
11
11
  });
package/dist/index.d.ts CHANGED
@@ -2,5 +2,8 @@ export { interpolate } from "./interpolate.js";
2
2
  export type { EnvValue, HealthCheckConfig, Interpolation, Reference, Resource, ResourceLimits, ResourceType, Secret, SecretConfig, Service, ServiceConfig, Site, SiteConfig, Volume, VolumeConfig, } from "./resource.js";
3
3
  export { isInterpolation, isReference, isSecret, validateId, validateRoute, } from "./resource.js";
4
4
  export type { Runtime } from "./runtime.js";
5
+ export type { BackupConfig, Bucket, Minio, MinioConfig, Mongo, MongoConfig, Mysql, MysqlConfig, Nats, NatsConfig, Postgres, PostgresConfig, Rabbitmq, RabbitmqConfig, Redis, RedisConfig, Storage, StorageConfig, } from "./services/index.js";
6
+ export { isManaged } from "./services/index.js";
5
7
  export type { RuntimeMeta, RuntimeRef } from "./symbols.js";
6
- export { VYFT_RUNTIME } from "./symbols.js";
8
+ export { VYFT_MANAGED, VYFT_RUNTIME } from "./symbols.js";
9
+ export declare const service: (id: string, config: import("./swarm/types.js").SwarmServiceConfig) => import("./resource.js").Service, secret: (id: string, config?: import("./resource.js").SecretConfig) => import("./resource.js").Secret, volume: (id: string, config?: import("./resource.js").VolumeConfig) => import("./resource.js").Volume, site: (id: string, config: import("./resource.js").SiteConfig) => import("./resource.js").Site, postgres: (id: string, config?: import("./index.js").PostgresConfig) => import("./index.js").Postgres, mysql: (id: string, config?: import("./index.js").MysqlConfig) => import("./index.js").Mysql, redis: (id: string, config?: import("./index.js").RedisConfig) => import("./index.js").Redis, rabbitmq: (id: string, config?: import("./index.js").RabbitmqConfig) => import("./index.js").Rabbitmq, nats: (id: string, config?: import("./index.js").NatsConfig) => import("./index.js").Nats, mongo: (id: string, config?: import("./index.js").MongoConfig) => import("./index.js").Mongo, minio: (id: string, config?: import("./index.js").MinioConfig) => import("./index.js").Minio, storage: (id: string, config?: import("./index.js").StorageConfig) => import("./index.js").Storage;
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
1
  export { interpolate } from "./interpolate.js";
2
2
  export { isInterpolation, isReference, isSecret, validateId, validateRoute, } from "./resource.js";
3
- export { VYFT_RUNTIME } from "./symbols.js";
3
+ export { isManaged } from "./services/index.js";
4
+ export { VYFT_MANAGED, VYFT_RUNTIME } from "./symbols.js";
5
+ import { createFactories } from "./swarm/factories.js";
6
+ const factories = createFactories({});
7
+ export const { service, secret, volume, site, postgres, mysql, redis, rabbitmq, nats, mongo, minio, storage, } = factories;
package/dist/init.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { execSync } from "node:child_process";
1
2
  import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
2
3
  import path from "node:path";
3
4
  import { fileURLToPath } from "node:url";
@@ -90,11 +91,27 @@ export async function init(directory) {
90
91
  await writeFile(destPath, content);
91
92
  log.step(relativePath);
92
93
  }
94
+ const ua = process.env.npm_config_user_agent ?? "";
95
+ const pm = ua.startsWith("pnpm")
96
+ ? "pnpm"
97
+ : ua.startsWith("bun")
98
+ ? "bun"
99
+ : "npm";
100
+ const install = await confirm({
101
+ message: `Install dependencies with ${pm}?`,
102
+ });
103
+ if (isCancel(install))
104
+ return;
105
+ if (install) {
106
+ log.step(`Running ${pm} install...`);
107
+ execSync(`${pm} install`, { cwd: targetDir, stdio: "inherit" });
108
+ }
93
109
  outro("Project created");
94
110
  console.log();
95
111
  console.log("Next steps:");
96
112
  console.log(` cd ${path.relative(process.cwd(), targetDir)}`);
97
- console.log(" pnpm install");
98
- console.log(" pnpm dev");
113
+ if (!install)
114
+ console.log(` ${pm} install`);
115
+ console.log(` ${pm} dev`);
99
116
  console.log();
100
117
  }
@@ -1,2 +1,13 @@
1
1
  import type { Interpolation, Reference } from "./resource.js";
2
+ /**
3
+ * Tagged template literal for building strings that contain {@link Reference} values
4
+ * (e.g. secrets) which are resolved at deploy time.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const connStr = interpolate`postgres://user:${dbPassword}@${db.host}:5432/app`;
9
+ * ```
10
+ *
11
+ * @throws If none of the interpolated values are a {@link Reference}.
12
+ */
2
13
  export declare function interpolate(strings: TemplateStringsArray, ...values: Array<Reference | string>): Interpolation;
@@ -1,4 +1,15 @@
1
1
  import { isReference } from "./resource.js";
2
+ /**
3
+ * Tagged template literal for building strings that contain {@link Reference} values
4
+ * (e.g. secrets) which are resolved at deploy time.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const connStr = interpolate`postgres://user:${dbPassword}@${db.host}:5432/app`;
9
+ * ```
10
+ *
11
+ * @throws If none of the interpolated values are a {@link Reference}.
12
+ */
2
13
  export function interpolate(strings, ...values) {
3
14
  const hasRef = values.some(isReference);
4
15
  if (!hasRef) {
@@ -0,0 +1,31 @@
1
+ import type { EnvValue, Service } from "../resource.js";
2
+ export interface DevProcess {
3
+ service: Service;
4
+ command: string;
5
+ cwd: string;
6
+ env: Record<string, string>;
7
+ }
8
+ type LogHandler = (service: string, stream: "stdout" | "stderr", text: string) => void;
9
+ /**
10
+ * Manages user service child processes for local development.
11
+ */
12
+ export declare class DevRunner {
13
+ private processes;
14
+ private onLog;
15
+ constructor(opts?: {
16
+ onLog?: LogHandler;
17
+ });
18
+ start(devProcesses: DevProcess[]): void;
19
+ stop(): void;
20
+ }
21
+ /**
22
+ * Resolve env vars for a dev process.
23
+ *
24
+ * - Secrets → generated plaintext value
25
+ * - Interpolations → fully resolved, with known service hosts remapped to localhost
26
+ * - Plain strings → as-is, with known service hosts remapped to localhost
27
+ */
28
+ export declare function resolveDevEnv(env: Record<string, EnvValue>, secretValues: Map<string, string>, hostRemap: Map<string, string>): Record<string, string>;
29
+ /** Detect the project's package manager from lock files. */
30
+ export declare function detectPackageManager(projectRoot: string): string;
31
+ export {};
@@ -0,0 +1,109 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { isInterpolation, isReference } from "../resource.js";
5
+ /**
6
+ * Manages user service child processes for local development.
7
+ */
8
+ export class DevRunner {
9
+ processes = [];
10
+ onLog;
11
+ constructor(opts) {
12
+ this.onLog = opts?.onLog;
13
+ }
14
+ start(devProcesses) {
15
+ for (const dp of devProcesses) {
16
+ const child = spawn(dp.command, {
17
+ cwd: dp.cwd,
18
+ env: { ...process.env, ...dp.env },
19
+ shell: true,
20
+ stdio: ["ignore", "pipe", "pipe"],
21
+ });
22
+ const name = dp.service.id;
23
+ child.stdout?.on("data", (chunk) => {
24
+ for (const line of chunk.toString().split("\n")) {
25
+ if (line)
26
+ this.onLog?.(name, "stdout", line);
27
+ }
28
+ });
29
+ child.stderr?.on("data", (chunk) => {
30
+ for (const line of chunk.toString().split("\n")) {
31
+ if (line)
32
+ this.onLog?.(name, "stderr", line);
33
+ }
34
+ });
35
+ child.on("exit", (code) => {
36
+ this.onLog?.(name, "stderr", `process exited with code ${code}`);
37
+ });
38
+ this.processes.push(child);
39
+ }
40
+ }
41
+ stop() {
42
+ for (const child of this.processes) {
43
+ child.kill("SIGTERM");
44
+ }
45
+ this.processes = [];
46
+ }
47
+ }
48
+ /**
49
+ * Resolve env vars for a dev process.
50
+ *
51
+ * - Secrets → generated plaintext value
52
+ * - Interpolations → fully resolved, with known service hosts remapped to localhost
53
+ * - Plain strings → as-is, with known service hosts remapped to localhost
54
+ */
55
+ export function resolveDevEnv(env, secretValues, hostRemap) {
56
+ const result = {};
57
+ for (const [key, value] of Object.entries(env)) {
58
+ if (typeof value === "string") {
59
+ result[key] = remapHosts(value, hostRemap);
60
+ }
61
+ else if (isReference(value)) {
62
+ const secretValue = secretValues.get(value.id);
63
+ if (!secretValue) {
64
+ throw new Error(`Secret ${value.id} not yet generated`);
65
+ }
66
+ result[key] = secretValue;
67
+ }
68
+ else if (isInterpolation(value)) {
69
+ const parts = [];
70
+ for (let i = 0; i < value.strings.length; i++) {
71
+ parts.push(value.strings[i]);
72
+ if (i < value.values.length) {
73
+ const v = value.values[i];
74
+ if (isReference(v)) {
75
+ const secretValue = secretValues.get(v.id);
76
+ if (!secretValue) {
77
+ throw new Error(`Secret ${v.id} not yet generated`);
78
+ }
79
+ parts.push(secretValue);
80
+ }
81
+ else {
82
+ parts.push(remapHosts(v, hostRemap));
83
+ }
84
+ }
85
+ }
86
+ result[key] = parts.join("");
87
+ }
88
+ }
89
+ return result;
90
+ }
91
+ function remapHosts(value, hostRemap) {
92
+ let result = value;
93
+ for (const [from, to] of hostRemap) {
94
+ result = result.replaceAll(from, to);
95
+ }
96
+ return result;
97
+ }
98
+ /** Detect the project's package manager from lock files. */
99
+ export function detectPackageManager(projectRoot) {
100
+ if (existsSync(path.join(projectRoot, "bun.lock")))
101
+ return "bun";
102
+ if (existsSync(path.join(projectRoot, "bun.lockb")))
103
+ return "bun";
104
+ if (existsSync(path.join(projectRoot, "pnpm-lock.yaml")))
105
+ return "pnpm";
106
+ if (existsSync(path.join(projectRoot, "yarn.lock")))
107
+ return "yarn";
108
+ return "npm";
109
+ }
@@ -0,0 +1,2 @@
1
+ export { DevRunner, detectPackageManager, resolveDevEnv } from "./dev.js";
2
+ export { LocalRuntime } from "./runtime.js";
@@ -0,0 +1,2 @@
1
+ export { DevRunner, detectPackageManager, resolveDevEnv } from "./dev.js";
2
+ export { LocalRuntime } from "./runtime.js";
@@ -0,0 +1,61 @@
1
+ import type { Logger } from "pino";
2
+ import type { EnvValue, Secret, Service, Volume } from "../resource.js";
3
+ /**
4
+ * Docker runtime for local development.
5
+ *
6
+ * Runs managed infrastructure (databases, caches, etc.) as plain Docker
7
+ * containers with ports published to localhost. Secrets are generated
8
+ * in-memory and injected as environment variables.
9
+ */
10
+ export declare class LocalRuntime {
11
+ private docker;
12
+ private project;
13
+ private log;
14
+ private secretValues;
15
+ private containers;
16
+ private networkName;
17
+ constructor(project: string, opts?: {
18
+ parentLogger?: Logger;
19
+ });
20
+ /** Create bridge network for inter-container DNS. */
21
+ ensureInfrastructure(): Promise<void>;
22
+ /** Generate a random secret value and store it in memory. */
23
+ createSecret(secret: Secret): void;
24
+ /** Get the generated value for a secret. */
25
+ getSecretValue(id: string): string | undefined;
26
+ /** Create a Docker volume for persistent data. */
27
+ createVolume(volume: Volume): Promise<void>;
28
+ /**
29
+ * Resolve env vars for a managed service container.
30
+ *
31
+ * Secrets become plain `KEY=<value>` env vars.
32
+ * Interpolations are fully resolved with generated secret values.
33
+ */
34
+ resolveEnv(env: Record<string, EnvValue>): string[];
35
+ /**
36
+ * Build an entrypoint wrapper that writes secret values to /run/secrets/
37
+ * before exec'ing the original entrypoint.
38
+ *
39
+ * This is needed because some factory images (e.g. redis) read passwords
40
+ * from `/run/secrets/*-password` via shell glob.
41
+ */
42
+ private buildSecretInit;
43
+ /** Pull, create, and start a container for a managed service. */
44
+ createService(service: Service): Promise<number>;
45
+ /** Wait for a container to pass its health check. */
46
+ waitForHealthy(id: string, timeoutMs?: number): Promise<void>;
47
+ /** Stream logs from a container. */
48
+ containerLogs(id: string): AsyncGenerator<{
49
+ stream: "stdout" | "stderr";
50
+ text: string;
51
+ }>;
52
+ /** Stop and remove all managed containers (keep volumes). */
53
+ stop(): Promise<void>;
54
+ /** List running dev containers for this project. */
55
+ listContainers(): Promise<Array<{
56
+ id: string;
57
+ name: string;
58
+ containerId: string;
59
+ }>>;
60
+ private removeContainer;
61
+ }