@specific.dev/cli 0.1.91 → 0.1.93

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 (73) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +2 -2
  4. package/dist/admin/__next.!KGRlZmF1bHQp.txt +5 -5
  5. package/dist/admin/__next._full.txt +8 -8
  6. package/dist/admin/__next._head.txt +1 -1
  7. package/dist/admin/__next._index.txt +3 -3
  8. package/dist/admin/__next._tree.txt +1 -1
  9. package/dist/admin/_next/static/chunks/42de6b5004222d9f.js +1 -0
  10. package/dist/admin/_next/static/chunks/{091449dbc4d3eb97.js → df799a08b7ff15f9.js} +2 -2
  11. package/dist/admin/_not-found/__next._full.txt +3 -3
  12. package/dist/admin/_not-found/__next._head.txt +1 -1
  13. package/dist/admin/_not-found/__next._index.txt +3 -3
  14. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  15. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  16. package/dist/admin/_not-found/__next._tree.txt +1 -1
  17. package/dist/admin/_not-found/index.html +1 -1
  18. package/dist/admin/_not-found/index.txt +3 -3
  19. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +2 -2
  20. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
  21. package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +5 -5
  22. package/dist/admin/databases/__next._full.txt +8 -8
  23. package/dist/admin/databases/__next._head.txt +1 -1
  24. package/dist/admin/databases/__next._index.txt +3 -3
  25. package/dist/admin/databases/__next._tree.txt +1 -1
  26. package/dist/admin/databases/index.html +1 -1
  27. package/dist/admin/databases/index.txt +8 -8
  28. package/dist/admin/fullscreen/__next._full.txt +4 -4
  29. package/dist/admin/fullscreen/__next._head.txt +1 -1
  30. package/dist/admin/fullscreen/__next._index.txt +3 -3
  31. package/dist/admin/fullscreen/__next._tree.txt +1 -1
  32. package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +2 -2
  33. package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
  34. package/dist/admin/fullscreen/databases/__next._full.txt +4 -4
  35. package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
  36. package/dist/admin/fullscreen/databases/__next._index.txt +3 -3
  37. package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
  38. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +2 -2
  39. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
  40. package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
  41. package/dist/admin/fullscreen/databases/index.html +1 -1
  42. package/dist/admin/fullscreen/databases/index.txt +4 -4
  43. package/dist/admin/fullscreen/index.html +1 -1
  44. package/dist/admin/fullscreen/index.txt +4 -4
  45. package/dist/admin/index.html +1 -1
  46. package/dist/admin/index.txt +8 -8
  47. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +2 -2
  48. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
  49. package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +5 -5
  50. package/dist/admin/mail/__next._full.txt +8 -8
  51. package/dist/admin/mail/__next._head.txt +1 -1
  52. package/dist/admin/mail/__next._index.txt +3 -3
  53. package/dist/admin/mail/__next._tree.txt +1 -1
  54. package/dist/admin/mail/index.html +1 -1
  55. package/dist/admin/mail/index.txt +8 -8
  56. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +5 -5
  57. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +2 -2
  58. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
  59. package/dist/admin/workflows/__next._full.txt +8 -8
  60. package/dist/admin/workflows/__next._head.txt +1 -1
  61. package/dist/admin/workflows/__next._index.txt +3 -3
  62. package/dist/admin/workflows/__next._tree.txt +1 -1
  63. package/dist/admin/workflows/index.html +1 -1
  64. package/dist/admin/workflows/index.txt +8 -8
  65. package/dist/cli.js +489 -2062
  66. package/dist/docs/postgres/reshape/index.md +6 -9
  67. package/dist/docs/services.md +49 -5
  68. package/package.json +1 -2
  69. package/dist/admin/_next/static/chunks/153355cea359ee0f.js +0 -1
  70. package/dist/postinstall.js +0 -182912
  71. /package/dist/admin/_next/static/{NRTSfx4J_pKuSA3c36hNC → bHtcCLWKGqkBj4NJ2_XcY}/_buildManifest.js +0 -0
  72. /package/dist/admin/_next/static/{NRTSfx4J_pKuSA3c36hNC → bHtcCLWKGqkBj4NJ2_XcY}/_clientMiddlewareManifest.json +0 -0
  73. /package/dist/admin/_next/static/{NRTSfx4J_pKuSA3c36hNC → bHtcCLWKGqkBj4NJ2_XcY}/_ssgManifest.js +0 -0
@@ -70,14 +70,11 @@ service "api" {
70
70
 
71
71
  Place migration files in your migrations directory. Files are processed in **lexical order** (e.g., `001_first.toml` before `002_second.toml`).
72
72
 
73
- **IMPORTANT:** You MUST run `specific reshape check` every time you create or edit a migration file. This validates the migration syntax and catches errors before the watcher applies them. Without this step, invalid migrations will silently fail to apply.
73
+ **IMPORTANT:** You MUST run `specific check` every time you create or edit a migration file. This validates both your `specific.hcl` configuration and all migration files, catching errors before the watcher applies them. Without this step, invalid migrations will silently fail to apply.
74
74
 
75
75
  ```bash
76
- # Validate all migration files
77
- specific reshape check
78
-
79
- # Validate migrations for a specific database
80
- specific reshape check main
76
+ # Validate specific.hcl and all migration files
77
+ specific check
81
78
  ```
82
79
 
83
80
  Example migration (`migrations/001_create_users.toml`):
@@ -130,12 +127,12 @@ specific reshape abort main
130
127
  ## Development Workflow
131
128
 
132
129
  1. Write your migration file in the migrations directory
133
- 2. Run `specific reshape check` to validate the migration syntax
130
+ 2. Run `specific check` to validate both `specific.hcl` and migration syntax
134
131
  3. `specific dev` automatically applies it
135
- 4. Iterate on the migration - run `specific reshape check` after **every** change
132
+ 4. Iterate on the migration - run `specific check` after **every** change
136
133
  5. When ready, add a new migration file to "lock in" the previous one
137
134
 
138
- **Remember:** Always run `specific reshape check` after any change to a migration file. This is required to catch syntax errors before they are applied.
135
+ **Remember:** Always run `specific check` after any change to a migration file or `specific.hcl`. This validates everything in one step.
139
136
 
140
137
  Completed migrations are made read-only to prevent accidental changes. If you need to modify a completed migration, you'll need to manually change the file permissions first.
141
138
 
@@ -22,7 +22,13 @@ service "web" {
22
22
 
23
23
  env = {
24
24
  PORT = port
25
- API_URL = service.api.public_url
25
+ API_URL = "https://${service.api.public_url}"
26
+ }
27
+
28
+ dev {
29
+ env = {
30
+ API_URL = "http://${service.api.public_url}"
31
+ }
26
32
  }
27
33
  }
28
34
 
@@ -143,10 +149,34 @@ service "worker" {
143
149
 
144
150
  Available service reference attributes:
145
151
 
146
- - `service.<name>.private_url` - Internal URL without scheme (host and port)
152
+ - `service.<name>.private_url` - Internal URL without scheme (e.g., `localhost:3001`). Does NOT include `http://` or `https://`.
147
153
  - `service.<name>.host` - Host only (e.g., `localhost`)
148
- - `service.<name>.port` - Port only (e.g., `3000`)
149
- - `service.<name>.public_url` - Public URL without scheme (host and port). Only available for endpoints with `public = true`. Served over HTTPS.
154
+ - `service.<name>.port` - Port only (e.g., `3001`)
155
+ - `service.<name>.public_url` - Public URL without scheme (e.g., `my-app.specific.app` in production, `localhost:3001` in dev). Does NOT include `http://` or `https://`. Only available for endpoints with `public = true`.
156
+
157
+ **Important:** `public_url` and `private_url` do NOT include a scheme. In production, services are served over HTTPS. In local development, services use plain HTTP. When you need to construct a full URL, use string interpolation with the correct scheme for each environment. Use the `dev { env = {} }` block to override with `http://` locally:
158
+
159
+ ```hcl
160
+ service "web" {
161
+ build = build.web
162
+ command = "npm start"
163
+
164
+ endpoint {
165
+ public = true
166
+ }
167
+
168
+ env = {
169
+ PORT = port
170
+ API_URL = "https://${service.api.public_url}"
171
+ }
172
+
173
+ dev {
174
+ env = {
175
+ API_URL = "http://${service.api.public_url}"
176
+ }
177
+ }
178
+ }
179
+ ```
150
180
 
151
181
  For services with multiple named endpoints, you must specify the endpoint:
152
182
 
@@ -186,7 +216,13 @@ service "api" {
186
216
 
187
217
  env = {
188
218
  PORT = port
189
- CORS_ORIGIN = service.web.public_url
219
+ CORS_ORIGIN = "https://${service.web.public_url}"
220
+ }
221
+
222
+ dev {
223
+ env = {
224
+ CORS_ORIGIN = "http://${service.web.public_url}"
225
+ }
190
226
  }
191
227
  }
192
228
  ```
@@ -315,11 +351,19 @@ service "worker" {
315
351
  CUSTOM_DB = "host=${postgres.main.host} port=${postgres.main.port}"
316
352
  PUBLIC_API = "https://${service.api.public_url}/v1"
317
353
  }
354
+
355
+ dev {
356
+ env = {
357
+ PUBLIC_API = "http://${service.api.public_url}/v1"
358
+ }
359
+ }
318
360
  }
319
361
  ```
320
362
 
321
363
  All resource references (services, postgres, redis, storage, port, endpoint) can be used inside interpolated strings. Secret and config references cannot be interpolated — they must be used as standalone values.
322
364
 
365
+ Since `public_url` and `private_url` do not include a scheme, you should use `https://` in the top-level `env` (for production) and override with `http://` in the `dev { env = {} }` block (for local development). The `dev` env block is merged with the top-level env, so you only need to override the variables that differ.
366
+
323
367
  ## Service dev configuration
324
368
 
325
369
  Override how a service runs in development. If the referenced build has no `dev` block, it is skipped.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specific.dev/cli",
3
- "version": "0.1.91",
3
+ "version": "0.1.93",
4
4
  "description": "CLI for Specific infrastructure-as-code",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -17,7 +17,6 @@
17
17
  "link:dev": "npm run build:dev && npm link",
18
18
  "link:staging": "npm run build:staging && npm link",
19
19
  "link:prod": "npm run build && npm link",
20
- "postinstall": "node dist/postinstall.js || true",
21
20
  "build:binary": "npx tsx scripts/build-binary.ts"
22
21
  },
23
22
  "keywords": [
@@ -1 +0,0 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,12283,35014,e=>{"use strict";var t=e.i(90795),r=e.i(59369);function o(){let e=window.location.hostname;return"localhost"===e||"127.0.0.1"===e}function n(){let e=window.location.hostname,t=".spcf.localhost";if("localhost"===e||"127.0.0.1"===e||"spcf.localhost"===e)return"default";if(e.endsWith(t)){let r=e.slice(0,-t.length);if(r&&!r.includes("."))return r}return null}function s(e,t){return o()&&t?`http://localhost:${t}`:"default"===e?"https://__drizzle_gateway.spcf.localhost":`https://__drizzle_gateway.${e}.spcf.localhost`}function a(e,t){return o()&&t?`http://localhost:${t}`:"default"===e?"https://__temporal.spcf.localhost":`https://__temporal.${e}.spcf.localhost`}e.s(["getDrizzleGatewayUrl",()=>s,"getInstanceKey",()=>n,"getTemporalUiUrl",()=>a],35014);let l=(0,r.createContext)(void 0);function i({children:e}){let[o,i]=(0,r.useState)(null),[c,u]=(0,r.useState)(null),[m,d]=(0,r.useState)(!1),h=(0,r.useMemo)(()=>n(),[]);(0,r.useEffect)(()=>{async function e(){try{let e=await fetch("/api/state");if(!e.ok)throw Error("Failed to fetch state");let t=await e.json();i(t),d(!0),u(null)}catch(e){e instanceof TypeError&&"Failed to fetch"===e.message?u("specific dev is not running. Restart it to reconnect."):u(e instanceof Error?e.message:"Unknown error"),d(!1)}}e();let t=setInterval(e,2e3);return()=>clearInterval(t)},[]);let f=o?.resources.some(e=>"postgres"===e.type)??!1,p=(0,r.useMemo)(()=>h?s(h,o?.drizzleGatewayPort):null,[h,o?.drizzleGatewayPort]),y=o?.hasTemporal??!1,v=(0,r.useMemo)(()=>h?a(h,o?.temporalUiPort):null,[h,o?.temporalUiPort]),g=o?.hasMail??!1,w=o?.mailApiUrl??null,T=o?.projectId??null,b=(0,r.useMemo)(()=>({state:o,error:c,connected:m,instanceKey:h,hasDatabases:f,drizzleGatewayUrl:p,hasTemporal:y,temporalUiUrl:v,hasMail:g,mailApiUrl:w,projectId:T}),[o,c,m,h,f,p,y,v,g,w,T]);return(0,t.jsx)(l.Provider,{value:b,children:e})}function c(){let e=(0,r.useContext)(l);if(void 0===e)throw Error("useDevState must be used within a DevStateProvider");return e}e.s(["DevStateProvider",()=>i,"useDevState",()=>c],12283)},70932,e=>{"use strict";var t=e.i(59369),r=(e,t,r,o,n,s,a,l)=>{let i=document.documentElement,c=["light","dark"];function u(t){var r;(Array.isArray(e)?e:[e]).forEach(e=>{let r="class"===e,o=r&&s?n.map(e=>s[e]||e):n;r?(i.classList.remove(...o),i.classList.add(s&&s[t]?s[t]:t)):i.setAttribute(e,t)}),r=t,l&&c.includes(r)&&(i.style.colorScheme=r)}if(o)u(o);else try{let e=localStorage.getItem(t)||r,o=a&&"system"===e?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e;u(o)}catch(e){}},o=["light","dark"],n="(prefers-color-scheme: dark)",s="u"<typeof window,a=t.createContext(void 0),l={setTheme:e=>{},themes:[]},i=()=>{var e;return null!=(e=t.useContext(a))?e:l},c=e=>t.useContext(a)?t.createElement(t.Fragment,null,e.children):t.createElement(m,{...e}),u=["light","dark"],m=({forcedTheme:e,disableTransitionOnChange:r=!1,enableSystem:s=!0,enableColorScheme:l=!0,storageKey:i="theme",themes:c=u,defaultTheme:m=s?"system":"light",attribute:y="data-theme",value:v,children:g,nonce:w,scriptProps:T})=>{let[b,S]=t.useState(()=>h(i,m)),[E,C]=t.useState(()=>"system"===b?p():b),P=v?Object.values(v):c,k=t.useCallback(e=>{let t=e;if(!t)return;"system"===e&&s&&(t=p());let n=v?v[t]:t,a=r?f(w):null,i=document.documentElement,c=e=>{"class"===e?(i.classList.remove(...P),n&&i.classList.add(n)):e.startsWith("data-")&&(n?i.setAttribute(e,n):i.removeAttribute(e))};if(Array.isArray(y)?y.forEach(c):c(y),l){let e=o.includes(m)?m:null,r=o.includes(t)?t:e;i.style.colorScheme=r}null==a||a()},[w]),_=t.useCallback(e=>{let t="function"==typeof e?e(b):e;S(t);try{localStorage.setItem(i,t)}catch(e){}},[b]),z=t.useCallback(t=>{C(p(t)),"system"===b&&s&&!e&&k("system")},[b,e]);t.useEffect(()=>{let e=window.matchMedia(n);return e.addListener(z),z(e),()=>e.removeListener(z)},[z]),t.useEffect(()=>{let e=e=>{e.key===i&&(e.newValue?S(e.newValue):_(m))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)},[_]),t.useEffect(()=>{k(null!=e?e:b)},[e,b]);let A=t.useMemo(()=>({theme:b,setTheme:_,forcedTheme:e,resolvedTheme:"system"===b?E:b,themes:s?[...c,"system"]:c,systemTheme:s?E:void 0}),[b,_,e,E,s,c]);return t.createElement(a.Provider,{value:A},t.createElement(d,{forcedTheme:e,storageKey:i,attribute:y,enableSystem:s,enableColorScheme:l,defaultTheme:m,value:v,themes:c,nonce:w,scriptProps:T}),g)},d=t.memo(({forcedTheme:e,storageKey:o,attribute:n,enableSystem:s,enableColorScheme:a,defaultTheme:l,value:i,themes:c,nonce:u,scriptProps:m})=>{let d=JSON.stringify([n,o,l,e,c,i,s,a]).slice(1,-1);return t.createElement("script",{...m,suppressHydrationWarning:!0,nonce:"u"<typeof window?u:"",dangerouslySetInnerHTML:{__html:`(${r.toString()})(${d})`}})}),h=(e,t)=>{let r;if(!s){try{r=localStorage.getItem(e)||void 0}catch(e){}return r||t}},f=e=>{let t=document.createElement("style");return e&&t.setAttribute("nonce",e),t.appendChild(document.createTextNode("*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}")),document.head.appendChild(t),()=>{window.getComputedStyle(document.body),setTimeout(()=>{document.head.removeChild(t)},1)}},p=e=>(e||(e=window.matchMedia(n)),e.matches?"dark":"light");e.s(["ThemeProvider",()=>c,"useTheme",()=>i])},49311,e=>{"use strict";var t=e.i(90795),r=e.i(70932);function o({children:e,...o}){return(0,t.jsx)(r.ThemeProvider,{...o,children:e})}e.s(["ThemeProvider",()=>o])}]);