@specific.dev/cli 0.1.37

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 (204) hide show
  1. package/dist/admin/404/index.html +1 -0
  2. package/dist/admin/404.html +1 -0
  3. package/dist/admin/__next.__PAGE__.txt +9 -0
  4. package/dist/admin/__next._full.txt +20 -0
  5. package/dist/admin/__next._head.txt +6 -0
  6. package/dist/admin/__next._index.txt +5 -0
  7. package/dist/admin/__next._tree.txt +4 -0
  8. package/dist/admin/_next/static/0zkv3YeV6IWOWVWE1S1A1/_buildManifest.js +11 -0
  9. package/dist/admin/_next/static/0zkv3YeV6IWOWVWE1S1A1/_clientMiddlewareManifest.json +1 -0
  10. package/dist/admin/_next/static/0zkv3YeV6IWOWVWE1S1A1/_ssgManifest.js +1 -0
  11. package/dist/admin/_next/static/1o2O2QPOKhuZyxcNaCjq8/_buildManifest.js +11 -0
  12. package/dist/admin/_next/static/1o2O2QPOKhuZyxcNaCjq8/_clientMiddlewareManifest.json +1 -0
  13. package/dist/admin/_next/static/1o2O2QPOKhuZyxcNaCjq8/_ssgManifest.js +1 -0
  14. package/dist/admin/_next/static/2ZtqtFX2EkuM82mTM7NWT/_buildManifest.js +11 -0
  15. package/dist/admin/_next/static/2ZtqtFX2EkuM82mTM7NWT/_clientMiddlewareManifest.json +1 -0
  16. package/dist/admin/_next/static/2ZtqtFX2EkuM82mTM7NWT/_ssgManifest.js +1 -0
  17. package/dist/admin/_next/static/2vLF5bo4ZtUfhs8bdQBoF/_buildManifest.js +11 -0
  18. package/dist/admin/_next/static/2vLF5bo4ZtUfhs8bdQBoF/_clientMiddlewareManifest.json +1 -0
  19. package/dist/admin/_next/static/2vLF5bo4ZtUfhs8bdQBoF/_ssgManifest.js +1 -0
  20. package/dist/admin/_next/static/8_8s51m7RbLVvbIechG-b/_buildManifest.js +11 -0
  21. package/dist/admin/_next/static/8_8s51m7RbLVvbIechG-b/_clientMiddlewareManifest.json +1 -0
  22. package/dist/admin/_next/static/8_8s51m7RbLVvbIechG-b/_ssgManifest.js +1 -0
  23. package/dist/admin/_next/static/C4Y44KtLnP684m57gN3Ga/_buildManifest.js +11 -0
  24. package/dist/admin/_next/static/C4Y44KtLnP684m57gN3Ga/_clientMiddlewareManifest.json +1 -0
  25. package/dist/admin/_next/static/C4Y44KtLnP684m57gN3Ga/_ssgManifest.js +1 -0
  26. package/dist/admin/_next/static/DBeh36kAJnZBeBMJSPBW8/_buildManifest.js +11 -0
  27. package/dist/admin/_next/static/DBeh36kAJnZBeBMJSPBW8/_clientMiddlewareManifest.json +1 -0
  28. package/dist/admin/_next/static/DBeh36kAJnZBeBMJSPBW8/_ssgManifest.js +1 -0
  29. package/dist/admin/_next/static/REKEUu6DP2t99jKSAqNOu/_buildManifest.js +11 -0
  30. package/dist/admin/_next/static/REKEUu6DP2t99jKSAqNOu/_clientMiddlewareManifest.json +1 -0
  31. package/dist/admin/_next/static/REKEUu6DP2t99jKSAqNOu/_ssgManifest.js +1 -0
  32. package/dist/admin/_next/static/TDGT3mkKtTe0w_CxujLxI/_buildManifest.js +11 -0
  33. package/dist/admin/_next/static/TDGT3mkKtTe0w_CxujLxI/_clientMiddlewareManifest.json +1 -0
  34. package/dist/admin/_next/static/TDGT3mkKtTe0w_CxujLxI/_ssgManifest.js +1 -0
  35. package/dist/admin/_next/static/ZHMJ-g7mAlAMt_2uCXeEk/_buildManifest.js +11 -0
  36. package/dist/admin/_next/static/ZHMJ-g7mAlAMt_2uCXeEk/_clientMiddlewareManifest.json +1 -0
  37. package/dist/admin/_next/static/ZHMJ-g7mAlAMt_2uCXeEk/_ssgManifest.js +1 -0
  38. package/dist/admin/_next/static/a6JDAB-CdvWPvS4sBgJji/_buildManifest.js +11 -0
  39. package/dist/admin/_next/static/a6JDAB-CdvWPvS4sBgJji/_clientMiddlewareManifest.json +1 -0
  40. package/dist/admin/_next/static/a6JDAB-CdvWPvS4sBgJji/_ssgManifest.js +1 -0
  41. package/dist/admin/_next/static/abFUeBpymhlx-IxygVnM9/_buildManifest.js +11 -0
  42. package/dist/admin/_next/static/abFUeBpymhlx-IxygVnM9/_clientMiddlewareManifest.json +1 -0
  43. package/dist/admin/_next/static/abFUeBpymhlx-IxygVnM9/_ssgManifest.js +1 -0
  44. package/dist/admin/_next/static/chunks/0afa5e999b6c353a.js +1 -0
  45. package/dist/admin/_next/static/chunks/1584f10ea1cebcb2.js +4 -0
  46. package/dist/admin/_next/static/chunks/195bbec70cfcd241.js +2 -0
  47. package/dist/admin/_next/static/chunks/2583656ea9ac4ad6.js +5 -0
  48. package/dist/admin/_next/static/chunks/465f799faf41e6df.js +1 -0
  49. package/dist/admin/_next/static/chunks/4ab079bdcb131778.js +5 -0
  50. package/dist/admin/_next/static/chunks/4b5ae5ebb2087f0d.js +2 -0
  51. package/dist/admin/_next/static/chunks/605800ff25160d05.js +1 -0
  52. package/dist/admin/_next/static/chunks/656b870f0567ed5f.js +1 -0
  53. package/dist/admin/_next/static/chunks/71098a6cd6181738.css +3 -0
  54. package/dist/admin/_next/static/chunks/806bdb8e4a6a9b95.js +4 -0
  55. package/dist/admin/_next/static/chunks/895a6f91f0b479fb.js +2 -0
  56. package/dist/admin/_next/static/chunks/9032f4a1aac1ca5d.css +3 -0
  57. package/dist/admin/_next/static/chunks/a28af2dc6f5fbaad.js +1 -0
  58. package/dist/admin/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  59. package/dist/admin/_next/static/chunks/a6dad97d9634a72d.js.map +1 -0
  60. package/dist/admin/_next/static/chunks/b4205fc2f84bda68.css +3 -0
  61. package/dist/admin/_next/static/chunks/c1a750c25bc8d092.js +1 -0
  62. package/dist/admin/_next/static/chunks/c3d30f6f144dca51.js +2 -0
  63. package/dist/admin/_next/static/chunks/cbf55ce8731457ae.js +2 -0
  64. package/dist/admin/_next/static/chunks/d2be314c3ece3fbe.js +1 -0
  65. package/dist/admin/_next/static/chunks/de6af6d8adf8b50a.js +5 -0
  66. package/dist/admin/_next/static/chunks/f0c001244d275aab.js +5 -0
  67. package/dist/admin/_next/static/chunks/fde89fd76ad6a3d0.css +3 -0
  68. package/dist/admin/_next/static/chunks/ff1a16fafef87110.js +1 -0
  69. package/dist/admin/_next/static/chunks/turbopack-a3d691c83d4b1778.js +4 -0
  70. package/dist/admin/_next/static/chunks/turbopack-e5185af8e7c716ca.js +4 -0
  71. package/dist/admin/_next/static/dcjrfD44GuB6g8bZ6BcFm/_buildManifest.js +11 -0
  72. package/dist/admin/_next/static/dcjrfD44GuB6g8bZ6BcFm/_clientMiddlewareManifest.json +1 -0
  73. package/dist/admin/_next/static/dcjrfD44GuB6g8bZ6BcFm/_ssgManifest.js +1 -0
  74. package/dist/admin/_next/static/fTEa8Scx922n-dMuqN3Vc/_buildManifest.js +11 -0
  75. package/dist/admin/_next/static/fTEa8Scx922n-dMuqN3Vc/_clientMiddlewareManifest.json +1 -0
  76. package/dist/admin/_next/static/fTEa8Scx922n-dMuqN3Vc/_ssgManifest.js +1 -0
  77. package/dist/admin/_next/static/kZNwyhHft01wPtJ_AvqQT/_buildManifest.js +11 -0
  78. package/dist/admin/_next/static/kZNwyhHft01wPtJ_AvqQT/_clientMiddlewareManifest.json +1 -0
  79. package/dist/admin/_next/static/kZNwyhHft01wPtJ_AvqQT/_ssgManifest.js +1 -0
  80. package/dist/admin/_next/static/kyVInC6N3DY6NWnjkLQ4J/_buildManifest.js +11 -0
  81. package/dist/admin/_next/static/kyVInC6N3DY6NWnjkLQ4J/_clientMiddlewareManifest.json +1 -0
  82. package/dist/admin/_next/static/kyVInC6N3DY6NWnjkLQ4J/_ssgManifest.js +1 -0
  83. package/dist/admin/_next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  84. package/dist/admin/_next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  85. package/dist/admin/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  86. package/dist/admin/_next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  87. package/dist/admin/_next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  88. package/dist/admin/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  89. package/dist/admin/_next/static/media/favicon.0b3bf435.ico +0 -0
  90. package/dist/admin/_next/static/twedXBdhyxpjS68UQoNAc/_buildManifest.js +11 -0
  91. package/dist/admin/_next/static/twedXBdhyxpjS68UQoNAc/_clientMiddlewareManifest.json +1 -0
  92. package/dist/admin/_next/static/twedXBdhyxpjS68UQoNAc/_ssgManifest.js +1 -0
  93. package/dist/admin/_not-found/__next._full.txt +14 -0
  94. package/dist/admin/_not-found/__next._head.txt +6 -0
  95. package/dist/admin/_not-found/__next._index.txt +5 -0
  96. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +5 -0
  97. package/dist/admin/_not-found/__next._not-found.txt +4 -0
  98. package/dist/admin/_not-found/__next._tree.txt +2 -0
  99. package/dist/admin/_not-found/index.html +1 -0
  100. package/dist/admin/_not-found/index.txt +14 -0
  101. package/dist/admin/databases/__next._full.txt +20 -0
  102. package/dist/admin/databases/__next._head.txt +6 -0
  103. package/dist/admin/databases/__next._index.txt +5 -0
  104. package/dist/admin/databases/__next._tree.txt +4 -0
  105. package/dist/admin/databases/__next.databases.__PAGE__.txt +9 -0
  106. package/dist/admin/databases/__next.databases.txt +4 -0
  107. package/dist/admin/databases/index.html +1 -0
  108. package/dist/admin/databases/index.txt +20 -0
  109. package/dist/admin/favicon.ico +0 -0
  110. package/dist/admin/file.svg +1 -0
  111. package/dist/admin/globe.svg +1 -0
  112. package/dist/admin/index.html +1 -0
  113. package/dist/admin/index.txt +20 -0
  114. package/dist/admin/next.svg +1 -0
  115. package/dist/admin/vercel.svg +1 -0
  116. package/dist/admin/window.svg +1 -0
  117. package/dist/cli.d.ts +3 -0
  118. package/dist/cli.d.ts.map +1 -0
  119. package/dist/cli.js +190523 -0
  120. package/dist/cli.js.map +1 -0
  121. package/dist/commands/check.d.ts +2 -0
  122. package/dist/commands/check.d.ts.map +1 -0
  123. package/dist/commands/check.js +104 -0
  124. package/dist/commands/check.js.map +1 -0
  125. package/dist/commands/clean.d.ts +2 -0
  126. package/dist/commands/clean.d.ts.map +1 -0
  127. package/dist/commands/clean.js +70 -0
  128. package/dist/commands/clean.js.map +1 -0
  129. package/dist/commands/deploy.d.ts +2 -0
  130. package/dist/commands/deploy.d.ts.map +1 -0
  131. package/dist/commands/deploy.js +11 -0
  132. package/dist/commands/deploy.js.map +1 -0
  133. package/dist/commands/dev.d.ts +2 -0
  134. package/dist/commands/dev.d.ts.map +1 -0
  135. package/dist/commands/dev.js +398 -0
  136. package/dist/commands/dev.js.map +1 -0
  137. package/dist/commands/docs.d.ts +2 -0
  138. package/dist/commands/docs.d.ts.map +1 -0
  139. package/dist/commands/docs.js +32 -0
  140. package/dist/commands/docs.js.map +1 -0
  141. package/dist/commands/exec.d.ts +2 -0
  142. package/dist/commands/exec.d.ts.map +1 -0
  143. package/dist/commands/exec.js +178 -0
  144. package/dist/commands/exec.js.map +1 -0
  145. package/dist/commands/init.d.ts +2 -0
  146. package/dist/commands/init.d.ts.map +1 -0
  147. package/dist/commands/init.js +339 -0
  148. package/dist/commands/init.js.map +1 -0
  149. package/dist/commands/psql.d.ts +2 -0
  150. package/dist/commands/psql.d.ts.map +1 -0
  151. package/dist/commands/psql.js +53 -0
  152. package/dist/commands/psql.js.map +1 -0
  153. package/dist/commands/secrets.d.ts +3 -0
  154. package/dist/commands/secrets.d.ts.map +1 -0
  155. package/dist/commands/secrets.js +136 -0
  156. package/dist/commands/secrets.js.map +1 -0
  157. package/dist/docs/builds.md +71 -0
  158. package/dist/docs/index.md +30 -0
  159. package/dist/docs/integrations/drizzle.md +83 -0
  160. package/dist/docs/integrations/nextjs.md +45 -0
  161. package/dist/docs/integrations/prisma.md +110 -0
  162. package/dist/docs/postgres.md +41 -0
  163. package/dist/docs/redis.md +33 -0
  164. package/dist/docs/secrets-config.md +141 -0
  165. package/dist/docs/services.md +325 -0
  166. package/dist/docs/storage.md +62 -0
  167. package/dist/docs/sync.md +66 -0
  168. package/dist/lib/dev/database-manager.d.ts +17 -0
  169. package/dist/lib/dev/database-manager.d.ts.map +1 -0
  170. package/dist/lib/dev/database-manager.js +114 -0
  171. package/dist/lib/dev/database-manager.js.map +1 -0
  172. package/dist/lib/dev/env-resolver.d.ts +14 -0
  173. package/dist/lib/dev/env-resolver.d.ts.map +1 -0
  174. package/dist/lib/dev/env-resolver.js +109 -0
  175. package/dist/lib/dev/env-resolver.js.map +1 -0
  176. package/dist/lib/dev/http-proxy.d.ts +11 -0
  177. package/dist/lib/dev/http-proxy.d.ts.map +1 -0
  178. package/dist/lib/dev/http-proxy.js +165 -0
  179. package/dist/lib/dev/http-proxy.js.map +1 -0
  180. package/dist/lib/dev/index.d.ts +11 -0
  181. package/dist/lib/dev/index.d.ts.map +1 -0
  182. package/dist/lib/dev/index.js +7 -0
  183. package/dist/lib/dev/index.js.map +1 -0
  184. package/dist/lib/dev/instance-state.d.ts +45 -0
  185. package/dist/lib/dev/instance-state.d.ts.map +1 -0
  186. package/dist/lib/dev/instance-state.js +213 -0
  187. package/dist/lib/dev/instance-state.js.map +1 -0
  188. package/dist/lib/dev/port-allocator.d.ts +5 -0
  189. package/dist/lib/dev/port-allocator.d.ts.map +1 -0
  190. package/dist/lib/dev/port-allocator.js +21 -0
  191. package/dist/lib/dev/port-allocator.js.map +1 -0
  192. package/dist/lib/dev/service-runner.d.ts +16 -0
  193. package/dist/lib/dev/service-runner.d.ts.map +1 -0
  194. package/dist/lib/dev/service-runner.js +61 -0
  195. package/dist/lib/dev/service-runner.js.map +1 -0
  196. package/dist/lib/secrets/index.d.ts +2 -0
  197. package/dist/lib/secrets/index.d.ts.map +1 -0
  198. package/dist/lib/secrets/index.js +2 -0
  199. package/dist/lib/secrets/index.js.map +1 -0
  200. package/dist/lib/secrets/parser.d.ts +37 -0
  201. package/dist/lib/secrets/parser.d.ts.map +1 -0
  202. package/dist/lib/secrets/parser.js +116 -0
  203. package/dist/lib/secrets/parser.js.map +1 -0
  204. package/package.json +54 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"psql.d.ts","sourceRoot":"","sources":["../../src/commands/psql.tsx"],"names":[],"mappings":"AAGA,wBAAsB,WAAW,CAAC,YAAY,CAAC,EAAE,MAAM,iBA4DtD"}
@@ -0,0 +1,53 @@
1
+ import { spawn } from "child_process";
2
+ import { InstanceStateManager } from "../lib/dev/index.js";
3
+ export async function psqlCommand(databaseName) {
4
+ const stateManager = new InstanceStateManager(process.cwd());
5
+ await stateManager.cleanStaleState();
6
+ const existingInstances = await stateManager.getExistingInstances();
7
+ if (!existingInstances) {
8
+ console.error("Error: No running instances found. Run `specific dev` first.");
9
+ process.exit(1);
10
+ }
11
+ const availableDatabases = Object.keys(existingInstances.databases);
12
+ if (availableDatabases.length === 0) {
13
+ console.error("Error: No databases found in running instances.");
14
+ process.exit(1);
15
+ }
16
+ let targetDb;
17
+ if (databaseName) {
18
+ if (!existingInstances.databases[databaseName]) {
19
+ console.error(`Error: Database "${databaseName}" not found. Available: ${availableDatabases.join(", ")}`);
20
+ process.exit(1);
21
+ }
22
+ targetDb = databaseName;
23
+ }
24
+ else {
25
+ if (availableDatabases.length > 1) {
26
+ console.error(`Error: Multiple databases available. Specify one: ${availableDatabases.join(", ")}`);
27
+ process.exit(1);
28
+ }
29
+ targetDb = availableDatabases[0];
30
+ }
31
+ const dbState = existingInstances.databases[targetDb];
32
+ const child = spawn("psql", ["-h", dbState.host, "-p", String(dbState.port), "-U", dbState.user, "-d", dbState.dbName], {
33
+ cwd: process.cwd(),
34
+ env: {
35
+ ...process.env,
36
+ PGPASSWORD: dbState.password,
37
+ },
38
+ stdio: "inherit",
39
+ });
40
+ child.on("exit", (code, signal) => {
41
+ if (signal) {
42
+ process.kill(process.pid, signal);
43
+ }
44
+ else {
45
+ process.exit(code ?? 0);
46
+ }
47
+ });
48
+ child.on("error", (err) => {
49
+ console.error(`Error: Failed to start psql: ${err.message}`);
50
+ process.exit(1);
51
+ });
52
+ }
53
+ //# sourceMappingURL=psql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"psql.js","sourceRoot":"","sources":["../../src/commands/psql.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,YAAqB;IACrD,MAAM,YAAY,GAAG,IAAI,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7D,MAAM,YAAY,CAAC,eAAe,EAAE,CAAC;IACrC,MAAM,iBAAiB,GAAG,MAAM,YAAY,CAAC,oBAAoB,EAAE,CAAC;IAEpE,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,kBAAkB,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAEpE,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,QAAgB,CAAC;IAErB,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC;YAC/C,OAAO,CAAC,KAAK,CACX,oBAAoB,YAAY,2BAA2B,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3F,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,QAAQ,GAAG,YAAY,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,KAAK,CACX,qDAAqD,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACrF,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,QAAQ,GAAG,kBAAkB,CAAC,CAAC,CAAE,CAAC;IACpC,CAAC;IAED,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,QAAQ,CAAE,CAAC;IAEvD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE;QACtH,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,GAAG,EAAE;YACH,GAAG,OAAO,CAAC,GAAG;YACd,UAAU,EAAE,OAAO,CAAC,QAAQ;SAC7B;QACD,KAAK,EAAE,SAAS;KACjB,CAAC,CAAC;IAEH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;QAChC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACxB,OAAO,CAAC,KAAK,CAAC,gCAAgC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function secretsSetCommand(secretName: string): Promise<void>;
2
+ export declare function secretsCommand(action: string | undefined, secretName: string | undefined): Promise<void>;
3
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/commands/secrets.tsx"],"names":[],"mappings":"AAsHA,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,iBAQzD;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,iBAqB9F"}
@@ -0,0 +1,136 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { render, Text, Box, useInput, useApp } from "ink";
3
+ import * as fs from "fs";
4
+ import { SECRETS_FILE } from "../lib/secrets/index.js";
5
+ const HEADER_COMMENT = "# Do not commit this file - it contains secrets\n";
6
+ function SetSecretUI({ secretName }) {
7
+ const { exit } = useApp();
8
+ const [value, setValue] = useState("");
9
+ const [done, setDone] = useState(false);
10
+ const [error, setError] = useState(null);
11
+ useInput((input, key) => {
12
+ if (done)
13
+ return;
14
+ if (key.return) {
15
+ if (value.trim() === "") {
16
+ setError("Secret value cannot be empty");
17
+ return;
18
+ }
19
+ try {
20
+ // Escape quotes and backslashes in the value for HCL
21
+ const escapedValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
22
+ const hclLine = `${secretName} = "${escapedValue}"`;
23
+ // Read existing file or start fresh
24
+ let content = "";
25
+ let hasHeader = false;
26
+ if (fs.existsSync(SECRETS_FILE)) {
27
+ content = fs.readFileSync(SECRETS_FILE, "utf-8");
28
+ hasHeader = content.startsWith("#");
29
+ // Check if this secret already exists and update it
30
+ const lines = content.split("\n");
31
+ const newLines = [];
32
+ let found = false;
33
+ // Match: secretName = "..." or secretName="..."
34
+ const pattern = new RegExp(`^${secretName}\\s*=\\s*"`);
35
+ for (const line of lines) {
36
+ if (pattern.test(line.trim())) {
37
+ newLines.push(hclLine);
38
+ found = true;
39
+ }
40
+ else {
41
+ newLines.push(line);
42
+ }
43
+ }
44
+ if (found) {
45
+ fs.writeFileSync(SECRETS_FILE, newLines.join("\n"));
46
+ setDone(true);
47
+ return;
48
+ }
49
+ }
50
+ // Append new secret
51
+ let newContent = "";
52
+ if (!hasHeader && !content) {
53
+ newContent = HEADER_COMMENT + "\n";
54
+ }
55
+ else if (content) {
56
+ newContent = content.endsWith("\n") ? content : content + "\n";
57
+ }
58
+ newContent += `${hclLine}\n`;
59
+ fs.writeFileSync(SECRETS_FILE, newContent);
60
+ setDone(true);
61
+ }
62
+ catch (err) {
63
+ setError(err instanceof Error ? err.message : String(err));
64
+ }
65
+ }
66
+ else if (key.backspace || key.delete) {
67
+ setValue((prev) => prev.slice(0, -1));
68
+ }
69
+ else if (key.escape) {
70
+ exit();
71
+ }
72
+ else if (!key.ctrl && !key.meta && input) {
73
+ setValue((prev) => prev + input);
74
+ }
75
+ });
76
+ useEffect(() => {
77
+ if (done) {
78
+ const timer = setTimeout(() => exit(), 50);
79
+ return () => clearTimeout(timer);
80
+ }
81
+ }, [done, exit]);
82
+ if (error) {
83
+ return React.createElement(Text, { color: "red" },
84
+ "Error: ",
85
+ error);
86
+ }
87
+ if (done) {
88
+ return (React.createElement(Box, { flexDirection: "column" },
89
+ React.createElement(Text, { color: "green" },
90
+ "Secret '",
91
+ secretName,
92
+ "' saved to ",
93
+ SECRETS_FILE)));
94
+ }
95
+ return (React.createElement(Box, { flexDirection: "column" },
96
+ React.createElement(Text, null,
97
+ "Enter value for secret '",
98
+ secretName,
99
+ "':"),
100
+ React.createElement(Box, null,
101
+ React.createElement(Text, { color: "cyan" }, "> "),
102
+ React.createElement(Text, null, value.length > 0 ? "*".repeat(value.length) : ""),
103
+ React.createElement(Text, { color: "gray" }, "|")),
104
+ React.createElement(Text, { dimColor: true }, "(Press Enter to save, Esc to cancel)")));
105
+ }
106
+ export async function secretsSetCommand(secretName) {
107
+ if (!secretName) {
108
+ console.error("Error: Secret name required");
109
+ console.error("Usage: specific secrets set <name>");
110
+ process.exit(1);
111
+ }
112
+ render(React.createElement(SetSecretUI, { secretName: secretName }));
113
+ }
114
+ export async function secretsCommand(action, secretName) {
115
+ if (!action) {
116
+ console.error("Usage: specific secrets <command>");
117
+ console.error("");
118
+ console.error("Commands:");
119
+ console.error(" set <name> Set a secret value");
120
+ process.exit(1);
121
+ }
122
+ if (action === "set") {
123
+ if (!secretName) {
124
+ console.error("Error: Secret name required");
125
+ console.error("Usage: specific secrets set <name>");
126
+ process.exit(1);
127
+ }
128
+ await secretsSetCommand(secretName);
129
+ }
130
+ else {
131
+ console.error(`Unknown command: ${action}`);
132
+ console.error("Usage: specific secrets set <name>");
133
+ process.exit(1);
134
+ }
135
+ }
136
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/commands/secrets.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ # Builds
2
+
3
+ Builds define how to produce artifacts. Services reference builds.
4
+
5
+ ```hcl
6
+ build "api" {
7
+ base = "node"
8
+ command = "npm run build"
9
+ }
10
+ ```
11
+
12
+ ## Required fields
13
+
14
+ - `base` - Base environment. Supported: `"node"`, `"python"`, `"go"`, `"rust"`, `"java"`.
15
+
16
+ ## Optional fields
17
+
18
+ - `command` - Build command to run after dependencies are installed (e.g., `npm run build`, `go build -o api`).
19
+
20
+ ## Automatic dependency installation
21
+
22
+ Specific automatically installs dependencies before running your build command. Dependencies are cached in a separate Docker layer for faster builds when only source code changes.
23
+
24
+ | Base | Detected Files | Install Command |
25
+ |------|----------------|-----------------|
26
+ | `node` | `package-lock.json` | `npm ci` |
27
+ | `node` | `yarn.lock` | `yarn install --frozen-lockfile` |
28
+ | `node` | `pnpm-lock.yaml` | `pnpm install --frozen-lockfile` |
29
+ | `node` | `package.json` only | `npm install` |
30
+ | `python` | `requirements.txt` | `pip install -r requirements.txt` |
31
+ | `python` | `Pipfile.lock` | `pipenv install --deploy --system` |
32
+ | `python` | `poetry.lock` | `poetry install` |
33
+ | `python` | `pyproject.toml` | `pip install .` |
34
+ | `go` | `go.mod` | `go mod download` |
35
+
36
+ For simple projects that only need dependencies installed, you can omit the `command` field:
37
+
38
+ ```hcl
39
+ build "api" {
40
+ base = "node"
41
+ }
42
+ ```
43
+
44
+ ## Base details
45
+
46
+ For **node** and **python**, the entire working directory is available at runtime.
47
+
48
+ For **go**, **rust**, and **java**, a multi-stage build is used:
49
+
50
+ - **go**: The entire working directory is copied to the runtime image.
51
+ - **rust**: Only `target/release` is copied to the working directory in the runtime image.
52
+ - **java**: JAR files from `target/*.jar` or `build/libs/*.jar` are copied to `app.jar` in the working directory.
53
+
54
+ ## Dev configuration
55
+
56
+ Override the build command for local development. If no `dev` block is defined, the build is skipped in development.
57
+
58
+ ```hcl
59
+ build "spa" {
60
+ base = "node"
61
+ command = "npm run build"
62
+
63
+ dev {
64
+ command = "npm run build:watch"
65
+ }
66
+ }
67
+ ```
68
+
69
+ ---
70
+
71
+ Run `specific docs services` for service configuration.
@@ -0,0 +1,30 @@
1
+ # Specific
2
+
3
+ Infrastructure-as-code designed for coding agents. Define your infrastructure in `specific.hcl` alongside your application code. This will define how to build, deploy and run the full system in production, but also how to run it in development. Specific is fully generic and works with any programming language and framework without any SDKs. We use environments variables to pass information to the code.
4
+
5
+ This documentation is nested and references to other docs are in Markdown link format. Run `specific docs [PATH]` to read a docs page.
6
+
7
+ ## Workflow
8
+
9
+ 1. Create or edit `specific.hcl` in the project root
10
+ 2. ALWAYS run `specific check` to validate configuration and fix any issues
11
+ 3. Run yourself or instruct the user to run `specific exec [SERVICE] --- [COMMAND]` for one-off commands that need to be run in development, like database migrations or seeding. The command will be run with databases and other services started and env vars injected.
12
+
13
+ A full development environment can be started with `specific dev`. To deploy any changes, the user can run `specific deploy`.
14
+
15
+ ## Topics
16
+
17
+ - [Services](/services): define how to build, deploy and run the code in this project as services, both in development and production.
18
+ - [Secrets and configuration](/secrets-config): define secrets and configuration variables that the user may or should provide. These can be injected into services through environment variables.
19
+ - [Postgres](/postgres): define managed PostgreSQL databases that services depend on.
20
+ - [Sync](/sync): define real-time, partial synchronisation out of PostgreSQL into frontends or other services. Should be used to implement real-time and collaborative apps.
21
+ - [Storage](/storage): define S3-compatible object/blob storage for services to store files in.
22
+ - [Redis](/redis): define non-durable Redis-compatible databases for caching and more.
23
+
24
+ ## Common integrations
25
+
26
+ The following is a list of common frameworks and libraries with guidance on how to use them with Specific:
27
+
28
+ - [Next.js](/integrations/nextjs): full-stack React framework
29
+ - [Drizzle ORM](/integrations/drizzle): TypeScript ORM with type safety
30
+ - [Prisma](/integrations/prisma): TypeScript ORM with auto-generated client
@@ -0,0 +1,83 @@
1
+ # Drizzle ORM
2
+
3
+ Drizzle is a TypeScript ORM with a focus on type safety and performance. This guide covers using Drizzle with Specific Postgres.
4
+
5
+ ## Configuration
6
+
7
+ Pass the database URL to your service and always add a `pre_deploy` step to run database migrations.
8
+
9
+ ```hcl
10
+ build "api" {
11
+ base = "node"
12
+ command = "npm run build"
13
+ }
14
+
15
+ postgres "main" {}
16
+
17
+ service "api" {
18
+ build = build.api
19
+ command = "node dist/index.js"
20
+
21
+ endpoint {
22
+ public = true
23
+ }
24
+
25
+ env = {
26
+ PORT = port
27
+ DATABASE_URL = postgres.main.url
28
+ }
29
+
30
+ dev {
31
+ command = "npm run dev"
32
+ }
33
+
34
+ pre_deploy {
35
+ command = "npx drizzle-kit migrate"
36
+ }
37
+ }
38
+ ```
39
+
40
+ ## Drizzle configuration
41
+
42
+ Configure Drizzle to read the connection string from the environment in `drizzle.config.ts`:
43
+
44
+ ```typescript
45
+ import { defineConfig } from "drizzle-kit";
46
+
47
+ export default defineConfig({
48
+ schema: "./src/db/schema.ts",
49
+ out: "./drizzle",
50
+ dialect: "postgresql",
51
+ dbCredentials: {
52
+ url: process.env.DATABASE_URL!,
53
+ },
54
+ });
55
+ ```
56
+
57
+ ## Development
58
+
59
+ Use `specific exec` to push schema changes and generate migrations:
60
+
61
+ ```bash
62
+ # Push schema directly to local database (for rapid iteration)
63
+ specific exec api -- npx drizzle-kit push
64
+
65
+ # If user is relying on Drizzle migrations, generate them once done with schema changes
66
+ specific exec api -- npx drizzle-kit generate
67
+ ```
68
+
69
+ ## Database client
70
+
71
+ Create your database client reading from the environment:
72
+
73
+ ```typescript
74
+ import { drizzle } from "drizzle-orm/postgres-js";
75
+ import postgres from "postgres";
76
+
77
+ const client = postgres(process.env.DATABASE_URL!);
78
+ export const db = drizzle(client);
79
+ ```
80
+
81
+ ## Drizzle studio
82
+
83
+ Drizzle studio is not needed as `specific dev` includes a database viewer and editor in its admin interface.
@@ -0,0 +1,45 @@
1
+ # Next.js
2
+
3
+ Next.js is a full-stack React framework. This guide covers running Next.js with Specific.
4
+
5
+ ## Configuration
6
+
7
+ For smaller Docker images and faster cold starts, enable standalone output in `next.config.js`:
8
+
9
+ ```javascript
10
+ module.exports = {
11
+ output: "standalone",
12
+ };
13
+ ```
14
+
15
+ Then reference the standalone output to run the server:
16
+
17
+ ```hcl
18
+ build "web" {
19
+ base = "node"
20
+ command = "npm run build"
21
+ }
22
+
23
+ service "web" {
24
+ build = build.web
25
+ command = "node .next/standalone/server.js"
26
+
27
+ endpoint {
28
+ public = true
29
+ }
30
+
31
+ env = {
32
+ PORT = port
33
+ }
34
+
35
+ dev {
36
+ command = "npm run dev"
37
+ }
38
+ }
39
+ ```
40
+
41
+ Next.js reads `PORT` from the environment automatically, so no additional configuration is needed.
42
+
43
+ ## Important note on pre-rendering during builds
44
+
45
+ During a build, Next.js will load a lot of code to perform pre-rendering. That code can not reference any environments variables from `specific.hcl`, like database URLs or API keys from secrets, as those are not available during the build and will cause it to fail. Ensure all code that relies on these env vars DOES NOT get executed during the build phase.
@@ -0,0 +1,110 @@
1
+ # Prisma
2
+
3
+ Prisma is a TypeScript ORM with auto-generated client and migrations. This guide covers using Prisma with Specific.
4
+
5
+ ## Setup
6
+
7
+ Pass the database URL to your service and always add a `pre_deploy` step to run database migrations.
8
+
9
+ ```hcl
10
+ build "api" {
11
+ base = "node"
12
+ command = "npx prisma generate && npm run build"
13
+ }
14
+
15
+ service "api" {
16
+ build = build.api
17
+ command = "node dist/index.js"
18
+
19
+ endpoint {
20
+ public = true
21
+ }
22
+
23
+ env = {
24
+ PORT = port
25
+ DATABASE_URL = postgres.main.url
26
+ }
27
+
28
+ pre_deploy {
29
+ command = "npx prisma migrate deploy"
30
+ }
31
+
32
+ dev {
33
+ command = "npm run dev"
34
+ }
35
+ }
36
+
37
+ postgres "main" {}
38
+ ```
39
+
40
+ Note: The build command includes `prisma generate` to ensure the Prisma client is generated before building.
41
+
42
+ ## Prisma schema
43
+
44
+ Configure the datasource in `prisma/schema.prisma`:
45
+
46
+ ```prisma
47
+ datasource db {
48
+ provider = "postgresql"
49
+ url = env("DATABASE_URL")
50
+ }
51
+
52
+ generator client {
53
+ provider = "prisma-client-js"
54
+ }
55
+ ```
56
+
57
+ ## Development
58
+
59
+ Use `specific exec` to run migrations:
60
+
61
+ ```bash
62
+ # Create and apply a new migration
63
+ specific exec api -- npx prisma migrate dev --name add_users_table
64
+
65
+ # Apply pending migrations
66
+ specific exec api -- npx prisma migrate dev
67
+
68
+ # Push schema directly (for rapid prototyping)
69
+ specific exec api -- npx prisma db push
70
+ ```
71
+
72
+ To seed your database, add a seed script to `package.json`:
73
+
74
+ ```json
75
+ {
76
+ "prisma": {
77
+ "seed": "npx tsx prisma/seed.ts"
78
+ }
79
+ }
80
+ ```
81
+
82
+ Then run it:
83
+
84
+ ```bash
85
+ specific exec api -- npx prisma db seed
86
+ ```
87
+
88
+ ## Database client
89
+
90
+ Create a singleton Prisma client:
91
+
92
+ ```typescript
93
+ import { PrismaClient } from "@prisma/client";
94
+
95
+ const globalForPrisma = globalThis as unknown as {
96
+ prisma: PrismaClient | undefined;
97
+ };
98
+
99
+ export const prisma = globalForPrisma.prisma ?? new PrismaClient();
100
+
101
+ if (process.env.NODE_ENV !== "production") {
102
+ globalForPrisma.prisma = prisma;
103
+ }
104
+ ```
105
+
106
+ The singleton pattern prevents multiple client instances during hot reloading in development.
107
+
108
+ ## Prisma Studio
109
+
110
+ Prisma Studio is not necessary as `specific dev` includes a database viewer and editor in its admin interface.
@@ -0,0 +1,41 @@
1
+ # Postgres
2
+
3
+ Managed PostgreSQL database instances.
4
+
5
+ ```hcl
6
+ postgres "main" {}
7
+ ```
8
+
9
+ Reference postgres attributes in env blocks:
10
+
11
+ ```hcl
12
+ service "api" {
13
+ build = build.api
14
+ command = "./api"
15
+
16
+ endpoint {
17
+ public = true
18
+ }
19
+
20
+ env = {
21
+ DATABASE_URL = postgres.main.url
22
+ }
23
+ }
24
+
25
+ postgres "main" {}
26
+ ```
27
+
28
+ ## Available postgres attributes
29
+
30
+ - `url` - Full connection string (e.g., `postgres://user:pass@host:5432/dbname`)
31
+ - `host` - Database host
32
+ - `port` - Database port
33
+ - `user` - Database user
34
+ - `password` - Database password
35
+ - `name` - Database name
36
+
37
+ ---
38
+
39
+ Related topics:
40
+ - Run `specific docs sync` for real-time data synchronization
41
+ - Run `specific docs exec` for running migrations
@@ -0,0 +1,33 @@
1
+ # Redis
2
+
3
+ Managed Redis instances.
4
+
5
+ ```hcl
6
+ redis "cache" {}
7
+ ```
8
+
9
+ Reference redis attributes in env blocks:
10
+
11
+ ```hcl
12
+ service "api" {
13
+ build = build.api
14
+ command = "./api"
15
+
16
+ endpoint {
17
+ public = true
18
+ }
19
+
20
+ env = {
21
+ REDIS_URL = redis.cache.url
22
+ }
23
+ }
24
+
25
+ redis "cache" {}
26
+ ```
27
+
28
+ ## Available redis attributes
29
+
30
+ - `url` - Full connection string (e.g., `redis://:password@host:6379`)
31
+ - `host` - Redis host
32
+ - `port` - Redis port
33
+ - `password` - Redis password for authentication