@launchfile/macos-dev 0.1.3

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 (158) hide show
  1. package/CLAUDE.md +37 -0
  2. package/dist/__tests__/dry-run.test.d.ts +2 -0
  3. package/dist/__tests__/dry-run.test.d.ts.map +1 -0
  4. package/dist/__tests__/dry-run.test.js +129 -0
  5. package/dist/__tests__/dry-run.test.js.map +1 -0
  6. package/dist/__tests__/env-writer.test.d.ts +2 -0
  7. package/dist/__tests__/env-writer.test.d.ts.map +1 -0
  8. package/dist/__tests__/env-writer.test.js +127 -0
  9. package/dist/__tests__/env-writer.test.js.map +1 -0
  10. package/dist/__tests__/lockfile-detect.test.d.ts +2 -0
  11. package/dist/__tests__/lockfile-detect.test.d.ts.map +1 -0
  12. package/dist/__tests__/lockfile-detect.test.js +75 -0
  13. package/dist/__tests__/lockfile-detect.test.js.map +1 -0
  14. package/dist/__tests__/port-allocator.test.d.ts +2 -0
  15. package/dist/__tests__/port-allocator.test.d.ts.map +1 -0
  16. package/dist/__tests__/port-allocator.test.js +53 -0
  17. package/dist/__tests__/port-allocator.test.js.map +1 -0
  18. package/dist/__tests__/secret-generator.test.d.ts +2 -0
  19. package/dist/__tests__/secret-generator.test.d.ts.map +1 -0
  20. package/dist/__tests__/secret-generator.test.js +26 -0
  21. package/dist/__tests__/secret-generator.test.js.map +1 -0
  22. package/dist/__tests__/state.test.d.ts +2 -0
  23. package/dist/__tests__/state.test.d.ts.map +1 -0
  24. package/dist/__tests__/state.test.js +30 -0
  25. package/dist/__tests__/state.test.js.map +1 -0
  26. package/dist/cli.d.ts +12 -0
  27. package/dist/cli.d.ts.map +1 -0
  28. package/dist/cli.js +69 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/env-writer.d.ts +46 -0
  31. package/dist/env-writer.d.ts.map +1 -0
  32. package/dist/env-writer.js +175 -0
  33. package/dist/env-writer.js.map +1 -0
  34. package/dist/health.d.ts +12 -0
  35. package/dist/health.d.ts.map +1 -0
  36. package/dist/health.js +72 -0
  37. package/dist/health.js.map +1 -0
  38. package/dist/index.d.ts +9 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +8 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/lockfile-detect.d.ts +14 -0
  43. package/dist/lockfile-detect.d.ts.map +1 -0
  44. package/dist/lockfile-detect.js +40 -0
  45. package/dist/lockfile-detect.js.map +1 -0
  46. package/dist/port-allocator.d.ts +21 -0
  47. package/dist/port-allocator.d.ts.map +1 -0
  48. package/dist/port-allocator.js +78 -0
  49. package/dist/port-allocator.js.map +1 -0
  50. package/dist/prereqs.d.ts +10 -0
  51. package/dist/prereqs.d.ts.map +1 -0
  52. package/dist/prereqs.js +22 -0
  53. package/dist/prereqs.js.map +1 -0
  54. package/dist/process-manager.d.ts +43 -0
  55. package/dist/process-manager.d.ts.map +1 -0
  56. package/dist/process-manager.js +188 -0
  57. package/dist/process-manager.js.map +1 -0
  58. package/dist/provider.d.ts +26 -0
  59. package/dist/provider.d.ts.map +1 -0
  60. package/dist/provider.js +342 -0
  61. package/dist/provider.js.map +1 -0
  62. package/dist/resources/index.d.ts +10 -0
  63. package/dist/resources/index.d.ts.map +1 -0
  64. package/dist/resources/index.js +23 -0
  65. package/dist/resources/index.js.map +1 -0
  66. package/dist/resources/mysql.d.ts +16 -0
  67. package/dist/resources/mysql.d.ts.map +1 -0
  68. package/dist/resources/mysql.js +64 -0
  69. package/dist/resources/mysql.js.map +1 -0
  70. package/dist/resources/postgres.d.ts +22 -0
  71. package/dist/resources/postgres.d.ts.map +1 -0
  72. package/dist/resources/postgres.js +98 -0
  73. package/dist/resources/postgres.js.map +1 -0
  74. package/dist/resources/redis.d.ts +16 -0
  75. package/dist/resources/redis.d.ts.map +1 -0
  76. package/dist/resources/redis.js +42 -0
  77. package/dist/resources/redis.js.map +1 -0
  78. package/dist/resources/sqlite.d.ts +16 -0
  79. package/dist/resources/sqlite.d.ts.map +1 -0
  80. package/dist/resources/sqlite.js +38 -0
  81. package/dist/resources/sqlite.js.map +1 -0
  82. package/dist/resources/types.d.ts +40 -0
  83. package/dist/resources/types.d.ts.map +1 -0
  84. package/dist/resources/types.js +8 -0
  85. package/dist/resources/types.js.map +1 -0
  86. package/dist/runtimes/bun.d.ts +11 -0
  87. package/dist/runtimes/bun.d.ts.map +1 -0
  88. package/dist/runtimes/bun.js +20 -0
  89. package/dist/runtimes/bun.js.map +1 -0
  90. package/dist/runtimes/index.d.ts +8 -0
  91. package/dist/runtimes/index.d.ts.map +1 -0
  92. package/dist/runtimes/index.js +18 -0
  93. package/dist/runtimes/index.js.map +1 -0
  94. package/dist/runtimes/node.d.ts +11 -0
  95. package/dist/runtimes/node.d.ts.map +1 -0
  96. package/dist/runtimes/node.js +70 -0
  97. package/dist/runtimes/node.js.map +1 -0
  98. package/dist/runtimes/python.d.ts +11 -0
  99. package/dist/runtimes/python.d.ts.map +1 -0
  100. package/dist/runtimes/python.js +38 -0
  101. package/dist/runtimes/python.js.map +1 -0
  102. package/dist/runtimes/ruby.d.ts +11 -0
  103. package/dist/runtimes/ruby.d.ts.map +1 -0
  104. package/dist/runtimes/ruby.js +51 -0
  105. package/dist/runtimes/ruby.js.map +1 -0
  106. package/dist/runtimes/types.d.ts +13 -0
  107. package/dist/runtimes/types.d.ts.map +1 -0
  108. package/dist/runtimes/types.js +5 -0
  109. package/dist/runtimes/types.js.map +1 -0
  110. package/dist/secret-generator.d.ts +11 -0
  111. package/dist/secret-generator.d.ts.map +1 -0
  112. package/dist/secret-generator.js +26 -0
  113. package/dist/secret-generator.js.map +1 -0
  114. package/dist/shell.d.ts +25 -0
  115. package/dist/shell.d.ts.map +1 -0
  116. package/dist/shell.js +46 -0
  117. package/dist/shell.js.map +1 -0
  118. package/dist/state.d.ts +35 -0
  119. package/dist/state.d.ts.map +1 -0
  120. package/dist/state.js +62 -0
  121. package/dist/state.js.map +1 -0
  122. package/dist/storage.d.ts +16 -0
  123. package/dist/storage.d.ts.map +1 -0
  124. package/dist/storage.js +48 -0
  125. package/dist/storage.js.map +1 -0
  126. package/package.json +41 -0
  127. package/src/__tests__/dry-run.test.ts +157 -0
  128. package/src/__tests__/env-writer.test.ts +156 -0
  129. package/src/__tests__/lockfile-detect.test.ts +74 -0
  130. package/src/__tests__/port-allocator.test.ts +59 -0
  131. package/src/__tests__/secret-generator.test.ts +31 -0
  132. package/src/__tests__/state.test.ts +33 -0
  133. package/src/cli.ts +78 -0
  134. package/src/env-writer.ts +231 -0
  135. package/src/health.ts +80 -0
  136. package/src/index.ts +9 -0
  137. package/src/lockfile-detect.ts +48 -0
  138. package/src/port-allocator.ts +97 -0
  139. package/src/prereqs.ts +32 -0
  140. package/src/process-manager.ts +242 -0
  141. package/src/provider.ts +415 -0
  142. package/src/resources/index.ts +29 -0
  143. package/src/resources/mysql.ts +98 -0
  144. package/src/resources/postgres.ts +133 -0
  145. package/src/resources/redis.ts +58 -0
  146. package/src/resources/sqlite.ts +52 -0
  147. package/src/resources/types.ts +47 -0
  148. package/src/runtimes/bun.ts +25 -0
  149. package/src/runtimes/index.ts +24 -0
  150. package/src/runtimes/node.ts +71 -0
  151. package/src/runtimes/python.ts +42 -0
  152. package/src/runtimes/ruby.ts +55 -0
  153. package/src/runtimes/types.ts +16 -0
  154. package/src/secret-generator.ts +32 -0
  155. package/src/shell.ts +70 -0
  156. package/src/state.ts +92 -0
  157. package/src/storage.ts +67 -0
  158. package/tsconfig.json +18 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AActD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAE5E;AAED,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Resource provisioner registry.
3
+ *
4
+ * Maps resource type names to their provisioner implementations.
5
+ */
6
+ import { PostgresProvisioner } from "./postgres.js";
7
+ import { RedisProvisioner } from "./redis.js";
8
+ import { SqliteProvisioner } from "./sqlite.js";
9
+ import { MysqlProvisioner } from "./mysql.js";
10
+ const provisioners = {
11
+ postgres: new PostgresProvisioner(),
12
+ redis: new RedisProvisioner(),
13
+ sqlite: new SqliteProvisioner(),
14
+ mysql: new MysqlProvisioner(),
15
+ mariadb: new MysqlProvisioner(), // MariaDB is MySQL-compatible
16
+ };
17
+ export function getProvisioner(type) {
18
+ return provisioners[type];
19
+ }
20
+ export function supportedResourceTypes() {
21
+ return Object.keys(provisioners);
22
+ }
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,YAAY,GAAwC;IACzD,QAAQ,EAAE,IAAI,mBAAmB,EAAE;IACnC,KAAK,EAAE,IAAI,gBAAgB,EAAE;IAC7B,MAAM,EAAE,IAAI,iBAAiB,EAAE;IAC/B,KAAK,EAAE,IAAI,gBAAgB,EAAE;IAC7B,OAAO,EAAE,IAAI,gBAAgB,EAAE,EAAE,8BAA8B;CAC/D,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,IAAY;IAC1C,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,sBAAsB;IACrC,OAAO,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * MySQL resource provisioner via Homebrew.
3
+ */
4
+ import type { NormalizedRequirement } from "@launchfile/sdk";
5
+ import type { ResourceState } from "../state.js";
6
+ import type { ProvisionOpts, ResourceProperties, ResourceProvisioner } from "./types.js";
7
+ export declare class MysqlProvisioner implements ResourceProvisioner {
8
+ readonly type = "mysql";
9
+ isRunning(): Promise<boolean>;
10
+ provision(req: NormalizedRequirement, opts: ProvisionOpts, existingState?: ResourceState): Promise<{
11
+ properties: ResourceProperties;
12
+ state: ResourceState;
13
+ }>;
14
+ destroy(state: ResourceState): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=mysql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../src/resources/mysql.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAQzF,qBAAa,gBAAiB,YAAW,mBAAmB;IAC3D,QAAQ,CAAC,IAAI,WAAW;IAElB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAI7B,SAAS,CACd,GAAG,EAAE,qBAAqB,EAC1B,IAAI,EAAE,aAAa,EACnB,aAAa,CAAC,EAAE,aAAa,GAC3B,OAAO,CAAC;QAAE,UAAU,EAAE,kBAAkB,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAC;IAuD9D,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAelD"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * MySQL resource provisioner via Homebrew.
3
+ */
4
+ import { shell, shellOk } from "../shell.js";
5
+ import { generatePassword } from "../secret-generator.js";
6
+ const DEFAULT_PORT = 3306;
7
+ const DEFAULT_HOST = "localhost";
8
+ // Security: validate identifiers before interpolating into shell/SQL commands
9
+ const SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
10
+ export class MysqlProvisioner {
11
+ type = "mysql";
12
+ async isRunning() {
13
+ return shellOk("mysqladmin ping -h localhost --silent");
14
+ }
15
+ async provision(req, opts, existingState) {
16
+ if (!(await this.isRunning())) {
17
+ console.log(" Starting MySQL via brew...");
18
+ const started = await shellOk("brew services start mysql");
19
+ if (!started) {
20
+ await shell("brew install mysql");
21
+ await shell("brew services start mysql");
22
+ }
23
+ }
24
+ const resourceName = req.name ?? req.type;
25
+ const safeName = opts.appName.replace(/-/g, "_");
26
+ const dbName = existingState?.dbName ?? `launchfile_${safeName}`;
27
+ const user = existingState?.user ?? `launchfile_${safeName}`;
28
+ const password = existingState?.password ?? generatePassword();
29
+ const port = DEFAULT_PORT;
30
+ // Create database and user (idempotent)
31
+ await shell(`mysql -h ${DEFAULT_HOST} -u root -e "CREATE DATABASE IF NOT EXISTS \\\`${dbName}\\\`;"`, { allowFailure: true });
32
+ await shell(`mysql -h ${DEFAULT_HOST} -u root -e "CREATE USER IF NOT EXISTS '${user}'@'${DEFAULT_HOST}' IDENTIFIED BY '${password}';"`, { allowFailure: true });
33
+ await shell(`mysql -h ${DEFAULT_HOST} -u root -e "GRANT ALL PRIVILEGES ON \\\`${dbName}\\\`.* TO '${user}'@'${DEFAULT_HOST}';"`, { allowFailure: true });
34
+ const url = `mysql://${user}:${password}@${DEFAULT_HOST}:${port}/${dbName}`;
35
+ const properties = {
36
+ url,
37
+ host: DEFAULT_HOST,
38
+ port,
39
+ user,
40
+ password,
41
+ name: dbName,
42
+ };
43
+ const state = {
44
+ type: "mysql",
45
+ name: resourceName,
46
+ brewService: "mysql",
47
+ port,
48
+ dbName,
49
+ user,
50
+ password,
51
+ };
52
+ return { properties, state };
53
+ }
54
+ async destroy(state) {
55
+ // Security: state values come from disk (state.json) — validate before SQL interpolation
56
+ if (state.dbName && SAFE_IDENTIFIER.test(state.dbName)) {
57
+ await shell(`mysql -h ${DEFAULT_HOST} -u root -e "DROP DATABASE IF EXISTS \\\`${state.dbName}\\\`;"`, { allowFailure: true });
58
+ }
59
+ if (state.user && SAFE_IDENTIFIER.test(state.user)) {
60
+ await shell(`mysql -h ${DEFAULT_HOST} -u root -e "DROP USER IF EXISTS '${state.user}'@'${DEFAULT_HOST}';"`, { allowFailure: true });
61
+ }
62
+ }
63
+ }
64
+ //# sourceMappingURL=mysql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../src/resources/mysql.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Postgres resource provisioner via Homebrew.
3
+ *
4
+ * Strategy:
5
+ * - Check if postgres is already running via `brew services list`
6
+ * - If not, install and start via brew
7
+ * - Create an app-specific database and user
8
+ * - Return connection properties
9
+ */
10
+ import type { NormalizedRequirement } from "@launchfile/sdk";
11
+ import type { ResourceState } from "../state.js";
12
+ import type { ProvisionOpts, ResourceProperties, ResourceProvisioner } from "./types.js";
13
+ export declare class PostgresProvisioner implements ResourceProvisioner {
14
+ readonly type = "postgres";
15
+ isRunning(): Promise<boolean>;
16
+ provision(req: NormalizedRequirement, opts: ProvisionOpts, existingState?: ResourceState): Promise<{
17
+ properties: ResourceProperties;
18
+ state: ResourceState;
19
+ }>;
20
+ destroy(state: ResourceState): Promise<void>;
21
+ }
22
+ //# sourceMappingURL=postgres.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/resources/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AASzF,qBAAa,mBAAoB,YAAW,mBAAmB;IAC9D,QAAQ,CAAC,IAAI,cAAc;IAErB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAI7B,SAAS,CACd,GAAG,EAAE,qBAAqB,EAC1B,IAAI,EAAE,aAAa,EACnB,aAAa,CAAC,EAAE,aAAa,GAC3B,OAAO,CAAC;QAAE,UAAU,EAAE,kBAAkB,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAC;IAmF9D,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAelD"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Postgres resource provisioner via Homebrew.
3
+ *
4
+ * Strategy:
5
+ * - Check if postgres is already running via `brew services list`
6
+ * - If not, install and start via brew
7
+ * - Create an app-specific database and user
8
+ * - Return connection properties
9
+ */
10
+ import { shell, shellOk } from "../shell.js";
11
+ import { generatePassword } from "../secret-generator.js";
12
+ const DEFAULT_PORT = 5432;
13
+ const DEFAULT_HOST = "localhost";
14
+ // Security: validate identifiers before interpolating into shell/SQL commands.
15
+ // Only alphanumeric + underscore — safe for SQL identifiers and shell args.
16
+ const SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
17
+ export class PostgresProvisioner {
18
+ type = "postgres";
19
+ async isRunning() {
20
+ return shellOk("pg_isready -q");
21
+ }
22
+ async provision(req, opts, existingState) {
23
+ // Ensure postgres is running
24
+ if (!(await this.isRunning())) {
25
+ console.log(" Starting PostgreSQL via brew...");
26
+ // Try to start; if not installed, install first
27
+ const started = await shellOk("brew services start postgresql");
28
+ if (!started) {
29
+ // Try versioned formula
30
+ await shell("brew install postgresql@16");
31
+ await shell("brew services start postgresql@16");
32
+ }
33
+ }
34
+ // Wait for ready
35
+ await shell("pg_isready --timeout=10", { allowFailure: true });
36
+ // Determine database and user names
37
+ const resourceName = req.name ?? req.type;
38
+ const safeName = opts.appName.replace(/-/g, "_");
39
+ const dbName = existingState?.dbName ?? `launchfile_${safeName}`;
40
+ const user = existingState?.user ?? `launchfile_${safeName}`;
41
+ const password = existingState?.password ?? generatePassword();
42
+ const port = DEFAULT_PORT;
43
+ // Create user (idempotent)
44
+ await shell(`psql -h ${DEFAULT_HOST} -p ${port} postgres -c "DO \\$\\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${user}') THEN CREATE ROLE ${user} WITH LOGIN PASSWORD '${password}' CREATEDB; END IF; END \\$\\$;"`, { allowFailure: true });
45
+ // Create database (idempotent)
46
+ const dbExists = await shellOk(`psql -h ${DEFAULT_HOST} -p ${port} postgres -tAc "SELECT 1 FROM pg_database WHERE datname='${dbName}'"`);
47
+ if (!dbExists) {
48
+ await shell(`createdb -h ${DEFAULT_HOST} -p ${port} -O ${user} ${dbName}`, { allowFailure: true });
49
+ }
50
+ // Handle extensions from config
51
+ // Security: extension names come from Launchfile config (untrusted input).
52
+ // Validate each against SAFE_IDENTIFIER before interpolating into SQL.
53
+ const extensions = req.config?.extensions;
54
+ if (Array.isArray(extensions)) {
55
+ for (const ext of extensions) {
56
+ if (typeof ext !== "string")
57
+ continue;
58
+ if (!SAFE_IDENTIFIER.test(ext)) {
59
+ console.warn(` Skipping invalid extension name: ${ext}`);
60
+ continue;
61
+ }
62
+ await shell(`psql -h ${DEFAULT_HOST} -p ${port} ${dbName} -c "CREATE EXTENSION IF NOT EXISTS \\"${ext}\\";"`, { allowFailure: true });
63
+ }
64
+ }
65
+ const url = `postgresql://${user}:${password}@${DEFAULT_HOST}:${port}/${dbName}`;
66
+ const properties = {
67
+ url,
68
+ host: DEFAULT_HOST,
69
+ port,
70
+ user,
71
+ password,
72
+ name: dbName,
73
+ };
74
+ const state = {
75
+ type: "postgres",
76
+ name: resourceName,
77
+ brewService: "postgresql",
78
+ port,
79
+ dbName,
80
+ user,
81
+ password,
82
+ };
83
+ return { properties, state };
84
+ }
85
+ async destroy(state) {
86
+ // Security: state values come from disk (state.json) — validate before SQL interpolation
87
+ if (state.dbName && SAFE_IDENTIFIER.test(state.dbName)) {
88
+ await shell(`dropdb -h ${DEFAULT_HOST} --if-exists ${state.dbName}`, {
89
+ allowFailure: true,
90
+ });
91
+ }
92
+ if (state.user && SAFE_IDENTIFIER.test(state.user)) {
93
+ await shell(`psql -h ${DEFAULT_HOST} postgres -c "DROP ROLE IF EXISTS ${state.user};"`, { allowFailure: true });
94
+ }
95
+ // Don't stop the brew service — it's shared
96
+ }
97
+ }
98
+ //# sourceMappingURL=postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/resources/postgres.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Redis resource provisioner via Homebrew.
3
+ */
4
+ import type { NormalizedRequirement } from "@launchfile/sdk";
5
+ import type { ResourceState } from "../state.js";
6
+ import type { ProvisionOpts, ResourceProperties, ResourceProvisioner } from "./types.js";
7
+ export declare class RedisProvisioner implements ResourceProvisioner {
8
+ readonly type = "redis";
9
+ isRunning(): Promise<boolean>;
10
+ provision(req: NormalizedRequirement, _opts: ProvisionOpts, _existingState?: ResourceState): Promise<{
11
+ properties: ResourceProperties;
12
+ state: ResourceState;
13
+ }>;
14
+ destroy(_state: ResourceState): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=redis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/resources/redis.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAKzF,qBAAa,gBAAiB,YAAW,mBAAmB;IAC3D,QAAQ,CAAC,IAAI,WAAW;IAElB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAI7B,SAAS,CACd,GAAG,EAAE,qBAAqB,EAC1B,KAAK,EAAE,aAAa,EACpB,cAAc,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC;QAAE,UAAU,EAAE,kBAAkB,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAC;IA8B9D,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAInD"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Redis resource provisioner via Homebrew.
3
+ */
4
+ import { shell, shellOk } from "../shell.js";
5
+ const DEFAULT_PORT = 6379;
6
+ const DEFAULT_HOST = "localhost";
7
+ export class RedisProvisioner {
8
+ type = "redis";
9
+ async isRunning() {
10
+ return shellOk("redis-cli ping");
11
+ }
12
+ async provision(req, _opts, _existingState) {
13
+ if (!(await this.isRunning())) {
14
+ console.log(" Starting Redis via brew...");
15
+ const started = await shellOk("brew services start redis");
16
+ if (!started) {
17
+ await shell("brew install redis");
18
+ await shell("brew services start redis");
19
+ }
20
+ }
21
+ const port = DEFAULT_PORT;
22
+ const resourceName = req.name ?? req.type;
23
+ const properties = {
24
+ url: `redis://${DEFAULT_HOST}:${port}/0`,
25
+ host: DEFAULT_HOST,
26
+ port,
27
+ password: "",
28
+ };
29
+ const state = {
30
+ type: "redis",
31
+ name: resourceName,
32
+ brewService: "redis",
33
+ port,
34
+ };
35
+ return { properties, state };
36
+ }
37
+ async destroy(_state) {
38
+ // Redis is shared, don't stop the service
39
+ // Could flush a specific database prefix, but not worth the complexity
40
+ }
41
+ }
42
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/resources/redis.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAI7C,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,WAAW,CAAC;AAEjC,MAAM,OAAO,gBAAgB;IACnB,IAAI,GAAG,OAAO,CAAC;IAExB,KAAK,CAAC,SAAS;QACd,OAAO,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,SAAS,CACd,GAA0B,EAC1B,KAAoB,EACpB,cAA8B;QAE9B,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;YAC5C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,2BAA2B,CAAC,CAAC;YAC3D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,KAAK,CAAC,oBAAoB,CAAC,CAAC;gBAClC,MAAM,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAC1C,CAAC;QACF,CAAC;QAED,MAAM,IAAI,GAAG,YAAY,CAAC;QAC1B,MAAM,YAAY,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC;QAE1C,MAAM,UAAU,GAAuB;YACtC,GAAG,EAAE,WAAW,YAAY,IAAI,IAAI,IAAI;YACxC,IAAI,EAAE,YAAY;YAClB,IAAI;YACJ,QAAQ,EAAE,EAAE;SACZ,CAAC;QAEF,MAAM,KAAK,GAAkB;YAC5B,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,OAAO;YACpB,IAAI;SACJ,CAAC;QAEF,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAqB;QAClC,0CAA0C;QAC1C,uEAAuE;IACxE,CAAC;CACD"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * SQLite resource provisioner — just creates a directory for the DB file.
3
+ */
4
+ import type { NormalizedRequirement } from "@launchfile/sdk";
5
+ import type { ResourceState } from "../state.js";
6
+ import type { ProvisionOpts, ResourceProperties, ResourceProvisioner } from "./types.js";
7
+ export declare class SqliteProvisioner implements ResourceProvisioner {
8
+ readonly type = "sqlite";
9
+ isRunning(): Promise<boolean>;
10
+ provision(req: NormalizedRequirement, opts: ProvisionOpts): Promise<{
11
+ properties: ResourceProperties;
12
+ state: ResourceState;
13
+ }>;
14
+ destroy(state: ResourceState): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/resources/sqlite.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEzF,qBAAa,iBAAkB,YAAW,mBAAmB;IAC5D,QAAQ,CAAC,IAAI,YAAY;IAEnB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAI7B,SAAS,CACd,GAAG,EAAE,qBAAqB,EAC1B,IAAI,EAAE,aAAa,GACjB,OAAO,CAAC;QAAE,UAAU,EAAE,kBAAkB,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAC;IAyB9D,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAMlD"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * SQLite resource provisioner — just creates a directory for the DB file.
3
+ */
4
+ import { mkdir } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+ export class SqliteProvisioner {
7
+ type = "sqlite";
8
+ async isRunning() {
9
+ return true; // SQLite is always available
10
+ }
11
+ async provision(req, opts) {
12
+ const resourceName = req.name ?? req.type;
13
+ const safeName = opts.appName.replace(/-/g, "_");
14
+ const dataDir = join(opts.projectDir, ".launchfile", "data", "sqlite");
15
+ await mkdir(dataDir, { recursive: true });
16
+ const dbPath = join(dataDir, `${safeName}.db`);
17
+ const properties = {
18
+ url: `sqlite://${dbPath}`,
19
+ host: "",
20
+ port: 0,
21
+ path: dbPath,
22
+ };
23
+ const state = {
24
+ type: "sqlite",
25
+ name: resourceName,
26
+ port: 0,
27
+ dbName: dbPath,
28
+ };
29
+ return { properties, state };
30
+ }
31
+ async destroy(state) {
32
+ if (state.dbName) {
33
+ const { rm } = await import("node:fs/promises");
34
+ await rm(state.dbName, { force: true });
35
+ }
36
+ }
37
+ }
38
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/resources/sqlite.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAKjC,MAAM,OAAO,iBAAiB;IACpB,IAAI,GAAG,QAAQ,CAAC;IAEzB,KAAK,CAAC,SAAS;QACd,OAAO,IAAI,CAAC,CAAC,6BAA6B;IAC3C,CAAC;IAED,KAAK,CAAC,SAAS,CACd,GAA0B,EAC1B,IAAmB;QAEnB,MAAM,YAAY,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC;QAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACvE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE1C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;QAE/C,MAAM,UAAU,GAAuB;YACtC,GAAG,EAAE,YAAY,MAAM,EAAE;YACzB,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,CAAC;YACP,IAAI,EAAE,MAAM;SACZ,CAAC;QAEF,MAAM,KAAK,GAAkB;YAC5B,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,CAAC;YACP,MAAM,EAAE,MAAM;SACd,CAAC;QAEF,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAoB;QACjC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;YAChD,MAAM,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;IACF,CAAC;CACD"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Resource provisioner interface.
3
+ *
4
+ * Each resource type (postgres, redis, etc.) implements this interface
5
+ * to handle install, start, database creation, and teardown.
6
+ */
7
+ import type { NormalizedRequirement } from "@launchfile/sdk";
8
+ import type { ResourceState } from "../state.js";
9
+ /** Properties that a provisioned resource exposes for expression resolution */
10
+ export interface ResourceProperties {
11
+ url: string;
12
+ host: string;
13
+ port: number;
14
+ user?: string;
15
+ password?: string;
16
+ name?: string;
17
+ path?: string;
18
+ access_key?: string;
19
+ secret_key?: string;
20
+ bucket?: string;
21
+ region?: string;
22
+ [key: string]: string | number | undefined;
23
+ }
24
+ export interface ProvisionOpts {
25
+ appName: string;
26
+ projectDir: string;
27
+ }
28
+ export interface ResourceProvisioner {
29
+ readonly type: string;
30
+ /** Check if the service is already running */
31
+ isRunning(): Promise<boolean>;
32
+ /** Ensure the service is installed and running, create app-specific resources */
33
+ provision(req: NormalizedRequirement, opts: ProvisionOpts, existingState?: ResourceState): Promise<{
34
+ properties: ResourceProperties;
35
+ state: ResourceState;
36
+ }>;
37
+ /** Drop app-specific databases/users (destroy mode) */
38
+ destroy(state: ResourceState): Promise<void>;
39
+ }
40
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/resources/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,+EAA+E;AAC/E,MAAM,WAAW,kBAAkB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,8CAA8C;IAC9C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAE9B,iFAAiF;IACjF,SAAS,CACR,GAAG,EAAE,qBAAqB,EAC1B,IAAI,EAAE,aAAa,EACnB,aAAa,CAAC,EAAE,aAAa,GAC3B,OAAO,CAAC;QAAE,UAAU,EAAE,kBAAkB,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IAErE,uDAAuD;IACvD,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Resource provisioner interface.
3
+ *
4
+ * Each resource type (postgres, redis, etc.) implements this interface
5
+ * to handle install, start, database creation, and teardown.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/resources/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Bun runtime installer via Homebrew.
3
+ */
4
+ import type { RuntimeInstaller } from "./types.js";
5
+ export declare class BunInstaller implements RuntimeInstaller {
6
+ readonly runtime = "bun";
7
+ detectVersion(_projectDir: string): Promise<string | undefined>;
8
+ install(_version: string): Promise<void>;
9
+ shellEnv(_version: string): Promise<Record<string, string>>;
10
+ }
11
+ //# sourceMappingURL=bun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../../src/runtimes/bun.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,qBAAa,YAAa,YAAW,gBAAgB;IACpD,QAAQ,CAAC,OAAO,SAAS;IAEnB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAK/D,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAGjE"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Bun runtime installer via Homebrew.
3
+ */
4
+ import { shellOk, shell } from "../shell.js";
5
+ export class BunInstaller {
6
+ runtime = "bun";
7
+ async detectVersion(_projectDir) {
8
+ // Bun doesn't have a standard version file yet; use whatever is installed
9
+ return undefined;
10
+ }
11
+ async install(_version) {
12
+ if (!(await shellOk("which bun"))) {
13
+ await shell("brew install bun");
14
+ }
15
+ }
16
+ async shellEnv(_version) {
17
+ return {};
18
+ }
19
+ }
20
+ //# sourceMappingURL=bun.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun.js","sourceRoot":"","sources":["../../src/runtimes/bun.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,OAAO,YAAY;IACf,OAAO,GAAG,KAAK,CAAC;IAEzB,KAAK,CAAC,aAAa,CAAC,WAAmB;QACtC,0EAA0E;QAC1E,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAAgB;QAC7B,IAAI,CAAC,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;YACnC,MAAM,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAC9B,OAAO,EAAE,CAAC;IACX,CAAC;CACD"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Runtime installer registry.
3
+ */
4
+ import type { Runtime } from "@launchfile/sdk";
5
+ import type { RuntimeInstaller } from "./types.js";
6
+ export declare function getRuntimeInstaller(runtime: Runtime): RuntimeInstaller | undefined;
7
+ export type { RuntimeInstaller } from "./types.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtimes/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAcnD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAElF;AAED,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Runtime installer registry.
3
+ */
4
+ import { NodeInstaller } from "./node.js";
5
+ import { BunInstaller } from "./bun.js";
6
+ import { RubyInstaller } from "./ruby.js";
7
+ import { PythonInstaller } from "./python.js";
8
+ const installers = {
9
+ node: new NodeInstaller(),
10
+ bun: new BunInstaller(),
11
+ ruby: new RubyInstaller(),
12
+ python: new PythonInstaller(),
13
+ // go, rust, etc. can be added later
14
+ };
15
+ export function getRuntimeInstaller(runtime) {
16
+ return installers[runtime];
17
+ }
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtimes/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,UAAU,GAA+C;IAC9D,IAAI,EAAE,IAAI,aAAa,EAAE;IACzB,GAAG,EAAE,IAAI,YAAY,EAAE;IACvB,IAAI,EAAE,IAAI,aAAa,EAAE;IACzB,MAAM,EAAE,IAAI,eAAe,EAAE;IAC7B,oCAAoC;CACpC,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IACnD,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Node.js runtime installer via fnm (or nvm fallback).
3
+ */
4
+ import type { RuntimeInstaller } from "./types.js";
5
+ export declare class NodeInstaller implements RuntimeInstaller {
6
+ readonly runtime = "node";
7
+ detectVersion(projectDir: string): Promise<string | undefined>;
8
+ install(version: string): Promise<void>;
9
+ shellEnv(_version: string): Promise<Record<string, string>>;
10
+ }
11
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/runtimes/node.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,qBAAa,aAAc,YAAW,gBAAgB;IACrD,QAAQ,CAAC,OAAO,UAAU;IAEpB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAsB9D,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAavC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAWjE"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Node.js runtime installer via fnm (or nvm fallback).
3
+ */
4
+ import { readFile } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+ import { shell, shellOk } from "../shell.js";
7
+ export class NodeInstaller {
8
+ runtime = "node";
9
+ async detectVersion(projectDir) {
10
+ // Priority: .nvmrc > .node-version > package.json engines.node
11
+ for (const file of [".nvmrc", ".node-version"]) {
12
+ try {
13
+ const content = await readFile(join(projectDir, file), "utf8");
14
+ const version = content.trim();
15
+ if (version)
16
+ return version;
17
+ }
18
+ catch {
19
+ // File doesn't exist
20
+ }
21
+ }
22
+ try {
23
+ const pkg = JSON.parse(await readFile(join(projectDir, "package.json"), "utf8"));
24
+ if (pkg.engines?.node)
25
+ return pkg.engines.node;
26
+ }
27
+ catch {
28
+ // No package.json
29
+ }
30
+ return undefined;
31
+ }
32
+ async install(version) {
33
+ // Prefer fnm (faster), fall back to nvm
34
+ if (await shellOk("which fnm")) {
35
+ await shell(`fnm install ${version}`);
36
+ }
37
+ else if (await shellOk("which nvm")) {
38
+ await shell(`nvm install ${version}`);
39
+ }
40
+ else {
41
+ console.log(" Installing fnm via brew...");
42
+ await shell("brew install fnm");
43
+ await shell(`fnm install ${version}`);
44
+ }
45
+ }
46
+ async shellEnv(_version) {
47
+ if (await shellOk("which fnm")) {
48
+ try {
49
+ const result = await shell("fnm env --shell bash", { silent: true });
50
+ return parseShellEnv(result.stdout);
51
+ }
52
+ catch {
53
+ return {};
54
+ }
55
+ }
56
+ return {};
57
+ }
58
+ }
59
+ /** Parse `export KEY="VALUE"` lines from fnm env output */
60
+ function parseShellEnv(output) {
61
+ const env = {};
62
+ for (const line of output.split("\n")) {
63
+ const match = /^export\s+(\w+)="?([^"]*)"?/.exec(line);
64
+ if (match) {
65
+ env[match[1]] = match[2];
66
+ }
67
+ }
68
+ return env;
69
+ }
70
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/runtimes/node.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,OAAO,aAAa;IAChB,OAAO,GAAG,MAAM,CAAC;IAE1B,KAAK,CAAC,aAAa,CAAC,UAAkB;QACrC,+DAA+D;QAC/D,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,CAAC;YAChD,IAAI,CAAC;gBACJ,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;gBAC/D,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;gBAC/B,IAAI,OAAO;oBAAE,OAAO,OAAO,CAAC;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACR,qBAAqB;YACtB,CAAC;QACF,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YACjF,IAAI,GAAG,CAAC,OAAO,EAAE,IAAI;gBAAE,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACR,kBAAkB;QACnB,CAAC;QAED,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe;QAC5B,wCAAwC;QACxC,IAAI,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,MAAM,KAAK,CAAC,eAAe,OAAO,EAAE,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YACvC,MAAM,KAAK,CAAC,eAAe,OAAO,EAAE,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;YAC5C,MAAM,KAAK,CAAC,kBAAkB,CAAC,CAAC;YAChC,MAAM,KAAK,CAAC,eAAe,OAAO,EAAE,CAAC,CAAC;QACvC,CAAC;IACF,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAC9B,IAAI,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,sBAAsB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrE,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACrC,CAAC;YAAC,MAAM,CAAC;gBACR,OAAO,EAAE,CAAC;YACX,CAAC;QACF,CAAC;QACD,OAAO,EAAE,CAAC;IACX,CAAC;CACD;AAED,2DAA2D;AAC3D,SAAS,aAAa,CAAC,MAAc;IACpC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,KAAK,EAAE,CAAC;YACX,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QAC5B,CAAC;IACF,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Python runtime installer via pyenv.
3
+ */
4
+ import type { RuntimeInstaller } from "./types.js";
5
+ export declare class PythonInstaller implements RuntimeInstaller {
6
+ readonly runtime = "python";
7
+ detectVersion(projectDir: string): Promise<string | undefined>;
8
+ install(version: string): Promise<void>;
9
+ shellEnv(_version: string): Promise<Record<string, string>>;
10
+ }
11
+ //# sourceMappingURL=python.d.ts.map