primitive-app 3.2.0-alpha.2 → 3.2.0-alpha.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.
@@ -12,20 +12,39 @@
12
12
  * gate if this committed copy drifts from the source. Edit the CLI file and
13
13
  * regenerate; edits made here are overwritten.
14
14
  */
15
- /** Schema version of `.primitive/config.json` this core understands. */
15
+ /** Schema version of `primitive/config.json` this core understands. */
16
16
  export declare const PRIMITIVE_CONFIG_VERSION = 1;
17
17
  /** Schema version of `.primitive/local.json` this core writes and reads. */
18
18
  export declare const PRIMITIVE_LOCAL_STATE_VERSION = 1;
19
- export declare const PROJECT_CONFIG_DIR = ".primitive";
19
+ /**
20
+ * The two directories, split apart by #3153 so one name never means two
21
+ * things: `primitive/` is the COMMITTED tree (the config and every
22
+ * environment's TOML), `.primitive/` is MACHINE-LOCAL state (credentials, this
23
+ * machine's selection, snapshot backups) and is gitignored whole.
24
+ */
25
+ export declare const PROJECT_TREE_DIR = "primitive";
26
+ export declare const PROJECT_LOCAL_STATE_DIR = ".primitive";
20
27
  export declare const PROJECT_CONFIG_FILENAME = "config.json";
21
28
  export declare const LOCAL_STATE_FILENAME = "local.json";
22
- export declare const PROJECT_CONFIG_DISPLAY_NAME = ".primitive/config.json";
29
+ export declare const PROJECT_CONFIG_DISPLAY_NAME = "primitive/config.json";
23
30
  export declare const LOCAL_STATE_DISPLAY_NAME = ".primitive/local.json";
31
+ /** The docs-site slug of the migration procedure (#3155 publishes the page). */
32
+ export declare const MIGRATION_GUIDE_SLUG = "getting-started/cli-project-migration";
33
+ /**
34
+ * The one refusal for a project still holding the pre-#3153 layout.
35
+ *
36
+ * There is no fallback read: a tool that quietly read `.primitive/sync/` after
37
+ * the move would let a half-migrated project look healthy while push and pull
38
+ * disagreed about which tree is the truth. So every reader — this core, the
39
+ * generated Vite-plugin copy, the Swift pre-build script — stops here and names
40
+ * the document that says what to move where.
41
+ */
42
+ export declare function staleLayoutMessage(detail: string): string;
24
43
  /**
25
44
  * Error kinds callers branch on. Every failure is one of these — no reader is
26
45
  * ever left guessing what a broken config meant.
27
46
  */
28
- export type PrimitiveEnvErrorKind = "missing-config" | "malformed-config" | "unsupported-version" | "corrupt-local-state" | "unknown-environment" | "no-selection" | "missing-app-id";
47
+ export type PrimitiveEnvErrorKind = "missing-config" | "malformed-config" | "unsupported-version" | "corrupt-local-state" | "unknown-environment" | "no-selection" | "missing-app-id" | "stale-layout";
29
48
  export declare class PrimitiveEnvError extends Error {
30
49
  readonly kind: PrimitiveEnvErrorKind;
31
50
  readonly path?: string;
@@ -135,22 +154,35 @@ export interface ResolveOptions {
135
154
  */
136
155
  export declare function deriveWsUrl(apiUrl: string): string;
137
156
  /**
138
- * Finds `.primitive/config.json` by walking up from `cwd`, or returns the
157
+ * Finds `primitive/config.json` by walking up from `cwd`, or returns the
139
158
  * PRIMITIVE_PROJECT_CONFIG override when it is set (and exists).
159
+ *
160
+ * The override is checked for provenance BEFORE the file is read (#3153, F2):
161
+ * it bypasses the walk, so without this an old-layout config could be handed
162
+ * straight to a reader and defeat the cutover.
163
+ *
164
+ * Every return is checked against the root it implies, not just the levels the
165
+ * walk passed through: this is the one function every reader goes through —
166
+ * `resolvePrimitiveEnv` and the CLI's own `loadProjectConfig`/`findProjectRoot`
167
+ * alike — so a half-migrated project must be refused HERE, or the commands that
168
+ * never call `resolvePrimitiveEnv` would read the new tree with the old one
169
+ * still beside it.
140
170
  */
141
171
  export declare function findPrimitiveConfigPath(options?: {
142
172
  cwd?: string;
143
173
  env?: Record<string, string | undefined>;
144
174
  }): string | null;
145
175
  /**
146
- * The project root for a config path: the parent of `.primitive/`, or the
176
+ * The project root for a config path: the parent of `primitive/`, or the
147
177
  * containing directory when the config was pointed at directly (a fixture via
148
- * PRIMITIVE_PROJECT_CONFIG, which is not inside a `.primitive/` directory).
178
+ * PRIMITIVE_PROJECT_CONFIG, which is not inside a `primitive/` directory).
149
179
  */
150
180
  export declare function projectRootForConfigPath(configPath: string): string;
151
181
  /**
152
- * Local state lives beside the config it belongs to, so a fixture-pointed
153
- * config never picks up a real project's selection and vice versa.
182
+ * Machine-local state lives under the project root's `.primitive/`, not beside
183
+ * the config the config is committed now, and the two must not share a
184
+ * directory. A fixture-pointed bare config still gets its own `.primitive/`
185
+ * in its own directory, so it never picks up a real project's selection.
154
186
  */
155
187
  export declare function localStatePathForConfigPath(configPath: string): string;
156
188
  /**
@@ -65,6 +65,6 @@ const observer = new MutationObserver(async (mutations, obs) => {
65
65
  });
66
66
 
67
67
  observer.observe(document.body, { childList: true, subtree: true });
68
- `},transformIndexHtml(e){if(!h||!d)return e;let t=`<style data-primitive-devtools-sonner>${g}</style>`;return e.replace(`</body>`,`${t}\n<script type="module" src="/@id/__x00__virtual:primitive-devtools-init"><\/script>\n</body>`)},hotUpdate({file:e,server:t}){if(e.endsWith(`.primitive-test.ts`)){let e=t.moduleGraph.getModuleById(c);e&&(t.moduleGraph.invalidateModule(e),t.ws.send({type:`full-reload`}))}}}}var f=`.primitive`,p=`config.json`,m=`local.json`,h=`.primitive/config.json`,g=`.primitive/local.json`,_=class extends Error{kind;path;constructor(e,t,n){super(t),this.name=`PrimitiveEnvError`,this.kind=e,this.path=n}};function v(e){if(typeof e!=`string`)return;let t=e.trim();if(!t)return;let n;try{n=new URL(t)}catch{return}if(!(n.protocol!==`https:`&&n.protocol!==`http:`)&&!(n.protocol===`http:`&&n.hostname!==`localhost`&&n.hostname!==`127.0.0.1`)&&!(n.username||n.password)&&!(n.pathname!==`/`&&n.pathname!==``)&&!(n.search||n.hash))return n.origin}function y(e,t){return`Environment "${e}" in ${t?`${h} (${t})`:h} has no "appId". Every environment names exactly one app — add "appId": "<app-id>" to that environment in ${h}. Find the app id in the Primitive admin UI, with 'primitive apps list' run from a directory outside this project, or as the residual "currentAppId" in .primitive/credentials.json if this environment was ever pointed at an app by the retired per-machine app selection.`}function b(e){if(typeof e==`string`)return e.trim()||void 0}function x(e){return e.startsWith(`https://`)?`wss://`+e.slice(8):e.startsWith(`http://`)?`ws://`+e.slice(7):e}function S(e={}){let n=(e.env??process.env).PRIMITIVE_PROJECT_CONFIG;if(n){let e=(0,a.resolve)(n);return(0,t.existsSync)(e)?e:null}let r=(0,a.resolve)(e.cwd??process.cwd());for(;;){let e=(0,a.join)(r,f,p);if((0,t.existsSync)(e))return e;let n=(0,a.dirname)(r);if(n===r)return null;r=n}}function C(e){let t=(0,a.dirname)(e);return(0,a.basename)(t)===`.primitive`?(0,a.dirname)(t):t}function w(e){return(0,a.join)((0,a.dirname)(e),m)}function T(e){if(!(0,t.existsSync)(e))throw new _(`missing-config`,`No ${h} found at ${e}. Run 'primitive init' to create one.`,e);let n;try{n=(0,t.readFileSync)(e,`utf-8`)}catch(t){throw new _(`malformed-config`,`Failed to read ${h} (${e}): ${t.message}`,e)}let r;try{r=JSON.parse(n)}catch(t){throw new _(`malformed-config`,`Failed to parse ${h} (${e}): ${t.message}`,e)}if(!r||typeof r!=`object`||Array.isArray(r))throw new _(`malformed-config`,`${h} (${e}) must contain a JSON object at the top level.`,e);let i=r;if(typeof i.version!=`number`||!Number.isInteger(i.version))throw new _(`malformed-config`,`${h} (${e}) is missing required integer field "version".`,e);if(i.version!==1)throw new _(`unsupported-version`,`${h} (${e}) has version ${i.version}, but this tool understands version 1.`,e);if(!i.environments||typeof i.environments!=`object`||Array.isArray(i.environments))throw new _(`malformed-config`,`${h} (${e}) is missing required "environments" object.`,e);let a={};for(let[t,n]of Object.entries(i.environments)){if(!n||typeof n!=`object`||Array.isArray(n))throw new _(`malformed-config`,`Environment "${t}" must be an object in ${h} (${e}).`,e);let r=n;if(typeof r.apiUrl!=`string`||!r.apiUrl)throw new _(`malformed-config`,`Environment "${t}" must have a non-empty "apiUrl" string in ${h} (${e}).`,e);let i=b(r.appId);if(!i)throw new _(`missing-app-id`,y(t,e),e);a[t]={apiUrl:r.apiUrl.replace(/\/$/,``),appId:i,appName:typeof r.appName==`string`?r.appName:void 0,webUrl:v(r.webUrl),description:typeof r.description==`string`?r.description:void 0}}return{version:i.version,defaultEnvironment:typeof i.defaultEnvironment==`string`&&i.defaultEnvironment?i.defaultEnvironment:void 0,environments:a}}function E(e){let n=w(e);if(!(0,t.existsSync)(n))return null;let r=e=>{throw new _(`corrupt-local-state`,`${g} (${n}) is unreadable: ${e}. Delete the file or re-run 'primitive env use <name>'.`,n)},i;try{i=JSON.parse((0,t.readFileSync)(n,`utf-8`))}catch(e){return r(e.message)}if(!i||typeof i!=`object`||Array.isArray(i))return r(`expected a JSON object`);let a=i.selectedEnvironment;return a==null||a===``?null:typeof a==`string`?a:r(`"selectedEnvironment" must be a string`)}function D(e,t){let n=Object.keys(e.environments),r=n.join(`, `)||`(none)`,i=t.configPath?`${h} (${t.configPath})`:h,a=(n,a,o)=>{if(!e.environments[n])throw new _(`unknown-environment`,`Environment "${n}" is not defined in ${i} (selected ${o}). Available: ${r}`,t.configPath);return{name:n,source:a}};if(t.explicitEnvName)return a(t.explicitEnvName,`explicit`,`explicitly`);if(t.envVarName)return a(t.envVarName,`env-var`,`via PRIMITIVE_ENV`);if(t.localSelection)return a(t.localSelection,`local`,`in ${g}`);if(e.defaultEnvironment)return a(e.defaultEnvironment,`default`,`as "defaultEnvironment"`);if(n.length===1)return{name:n[0],source:`sole`};throw n.length===0?new _(`no-selection`,`${i} has no environments defined. Run 'primitive env add <name>' or 'primitive init'.`,t.configPath):new _(`no-selection`,`No environment selected. Run 'primitive env use <name>', set "defaultEnvironment" in ${i}, or export PRIMITIVE_ENV. Available: ${r}`,t.configPath)}function O(e={}){let t=e.env??process.env,n=e.configPath??S({cwd:e.cwd,env:t});if(!n)throw new _(`missing-config`,`No ${h} found in ${e.cwd??process.cwd()} or any parent directory. Run 'primitive init' to create one, or 'primitive env add <name>' to add an environment.`);let r=T(n),{name:i,source:a}=D(r,{explicitEnvName:e.explicitEnvName||null,envVarName:t.PRIMITIVE_ENV||null,localSelection:E(n),configPath:n}),o=r.environments[i];return{name:i,apiUrl:o.apiUrl,wsUrl:x(o.apiUrl),appId:o.appId,appName:o.appName,webUrl:o.webUrl,description:o.description,source:a,configPath:n,localStatePath:w(n),projectRoot:C(n)}}var k=[`VITE_APP_ID`,`VITE_API_URL`,`VITE_WS_URL`,`VITE_APP_NAME`],A=`VITE_EXPECTED_PRIMITIVE_ENV`,j=new Map;function M(e,t){let n=process.env[e];n!==void 0&&n!==``&&j.get(e)!==n||(process.env[e]=t,j.set(e,t))}function N(e={}){return O({cwd:e.root,env:e.env,explicitEnvName:e.primitiveEnv??null})}function P(e,t,n){let r=e=>n.includes(e)?` (overridden)`:``,i=[`[primitive-env] Primitive environment: ${e.name} (selected: ${e.source})`,`[primitive-env] apiUrl: ${t.apiUrl}${r(`VITE_API_URL`)}`,`[primitive-env] wsUrl: ${t.wsUrl}${r(`VITE_WS_URL`)}`,`[primitive-env] appId: ${t.appId??`(unset)`}${r(`VITE_APP_ID`)}`,`[primitive-env] appName: ${t.appName??`(unset)`}${r(`VITE_APP_NAME`)}`,`[primitive-env] config: ${e.configPath}`];return n.length>0&&i.push(`[primitive-env] overridden by the environment: ${n.join(`, `)}`),i.join(`
69
- `)}function F(e,t){let n=e?.envDir;return n===!1?null:typeof n==`string`&&n!==``?(0,a.resolve)(t,n):t}function I(e,t){let n=process.env[A];if(n!==void 0&&n!==``)return{value:n,where:`the process environment`};if(t===null)return null;for(let n of[`.env.${e}.local`,`.env.${e}`,`.env.local`,`.env`]){let e=V((0,a.join)(t,n))[A];if(e!==void 0)return{value:e,where:n}}return null}function L(e,t,n){return[`[primitive-env] Wrong pair: vite mode "${e}" is declared to run against Primitive environment "${t.value}", but "${n.name}" resolved.`,`[primitive-env] expected: ${t.value} (${A} in ${t.where})`,`[primitive-env] resolved: ${n.name} (selected: ${n.source})`,`[primitive-env] config: ${n.configPath}`,``,`Run the pair you meant — PRIMITIVE_ENV=${t.value} <command> --mode ${e}, or 'primitive env use ${t.value}' — or change/remove ${A} in ${t.where}.`,``,`A mode declares this when its .env.${e} carries values coupled to one backend, so continuing would run this environment's identity with another's configuration.`].join(`
70
- `)}function R(e,t,n){let r=I(e,t);if(r&&r.value!==``&&r.value!==n.name)throw Error(L(e,r,n))}function z(e={}){let t=null,n=null,r,i=[],a=!1,o=null;return{name:`primitive-env`,enforce:`pre`,config(s,c){a=c?.command===`serve`,o=null;let l=s?.root??process.cwd(),u=c?.mode??`development`,d=B(u,l),f={};for(let e of k){let t=process.env[e],n=t!==void 0&&j.get(e)!==t?t:d[e];n!==void 0&&n!==``&&(f[e]=n)}let p;try{p=O({cwd:l,explicitEnvName:e.primitiveEnv??null})}catch(e){if(e instanceof _&&e.kind===`missing-config`&&f.VITE_APP_ID&&f.VITE_API_URL)return console.warn(`[primitive-env] No .primitive/config.json found; using VITE_APP_ID / VITE_API_URL from the environment. Run 'primitive init' to make the project the source of truth.`),r=f.VITE_APP_NAME,{};throw e}if(!(f.VITE_APP_ID&&f.VITE_API_URL)){let e=F(s,l);R(u,e,p),o={root:l,mode:u,dir:e}}t=p,r=f.VITE_APP_NAME??p.appName;let m={VITE_APP_ID:p.appId,VITE_API_URL:p.apiUrl,VITE_WS_URL:p.wsUrl,VITE_APP_NAME:p.appName},h={"import.meta.env.VITE_PRIMITIVE_ENV":JSON.stringify(p.name)},g=[];for(let e of k){if(f[e]!==void 0){g.push(e);continue}let t=m[e];t!==void 0&&(h[`import.meta.env.${e}`]=JSON.stringify(t),M(e,t))}return M(`VITE_PRIMITIVE_ENV`,p.name),i=g,n={apiUrl:f.VITE_API_URL??p.apiUrl,wsUrl:f.VITE_WS_URL??p.wsUrl,appId:f.VITE_APP_ID??p.appId,appName:r},e.logResolved!==!1&&console.log(P(p,n,g)),g.length>0&&console.warn(`[primitive-env] Overridden by an explicit value in .env or the shell: ${g.join(`, `)}. Remove it to use .primitive/config.json (deploys reject these outright).`),{define:h}},configResolved(e){if(!o||!t)return;let n=F(e,o.root),r=e?.mode??o.mode;n===o.dir&&r===o.mode||(R(r,n,t),o={root:o.root,mode:r,dir:n})},transformIndexHtml(){if(!a||!t||!n)return[];let e=`%c[primitive] env=${t.name} api=${n.apiUrl} app=${n.appId??`(unset)`} (${t.configPath})`+(i.length>0?` overridden: ${i.join(`, `)}`:``);return[{tag:`script`,injectTo:`head`,children:`console.info(${JSON.stringify(e)}, "color:#888");`}]},appName(){return r},resolved(){return t}}}function B(e,t){let n=[`.env`,`.env.local`,`.env.${e}`,`.env.${e}.local`],r={};for(let e of n)Object.assign(r,V((0,a.join)(t,e)));return r}function V(e){if(!(0,t.existsSync)(e))return{};let n={};for(let r of(0,t.readFileSync)(e,`utf-8`).split(/\r?\n/)){let e=r.trim();if(!e||e.startsWith(`#`))continue;let t=e.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);if(!t)continue;let i=t[2].trim(),a=i[0],o=a===`"`||a===`'`||a==="`"?i.indexOf(a,1):-1;n[t[1]]=o===-1?i.replace(/\s+#.*$/,``).trim():i.slice(1,o)}return n}exports.PrimitiveEnvError=_,exports.primitiveDevTools=d,exports.primitiveEnv=z,exports.resolvePrimitiveEnv=N;
68
+ `},transformIndexHtml(e){if(!h||!d)return e;let t=`<style data-primitive-devtools-sonner>${g}</style>`;return e.replace(`</body>`,`${t}\n<script type="module" src="/@id/__x00__virtual:primitive-devtools-init"><\/script>\n</body>`)},hotUpdate({file:e,server:t}){if(e.endsWith(`.primitive-test.ts`)){let e=t.moduleGraph.getModuleById(c);e&&(t.moduleGraph.invalidateModule(e),t.ws.send({type:`full-reload`}))}}}}var f=`primitive`,p=`.primitive`,m=`config.json`,h=`local.json`,g=`primitive/config.json`,_=`.primitive/local.json`,v=`getting-started/cli-project-migration`;function y(e){return`${e} The Primitive configuration tree moved out of the hidden directory: the config is now ${g} and each environment's TOML is at primitive/<env>/, while .primitive/ keeps only machine-local state. Nothing is read from either tree until the project is migrated — follow ${v}.`}var b=class extends Error{kind;path;constructor(e,t,n){super(t),this.name=`PrimitiveEnvError`,this.kind=e,this.path=n}};function x(e){if(typeof e!=`string`)return;let t=e.trim();if(!t)return;let n;try{n=new URL(t)}catch{return}if(!(n.protocol!==`https:`&&n.protocol!==`http:`)&&!(n.protocol===`http:`&&n.hostname!==`localhost`&&n.hostname!==`127.0.0.1`)&&!(n.username||n.password)&&!(n.pathname!==`/`&&n.pathname!==``)&&!(n.search||n.hash))return n.origin}function S(e,t){return`Environment "${e}" in ${t?`${g} (${t})`:g} has no "appId". Every environment names exactly one app — add "appId": "<app-id>" to that environment in ${g}. Find the app id in the Primitive admin UI, with 'primitive apps list' run from a directory outside this project, or as the residual "currentAppId" in .primitive/credentials.json if this environment was ever pointed at an app by the retired per-machine app selection.`}function C(e){if(typeof e==`string`)return e.trim()||void 0}function w(e){return e.startsWith(`https://`)?`wss://`+e.slice(8):e.startsWith(`http://`)?`ws://`+e.slice(7):e}function T(e){let n=(0,a.join)(e,p,m);if((0,t.existsSync)(n))throw new b(`stale-layout`,y(`${n} still exists.`),n);let r=(0,a.join)(e,p,`sync`);if((0,t.existsSync)(r))throw new b(`stale-layout`,y(`${r} still exists.`),r)}function E(e={}){let n=(e.env??process.env).PRIMITIVE_PROJECT_CONFIG;if(n){let e=(0,a.resolve)(n);if((0,a.basename)((0,a.dirname)(e))===`.primitive`)throw new b(`stale-layout`,y(`PRIMITIVE_PROJECT_CONFIG points at ${e}, inside a ${p}/ directory.`),e);return(0,t.existsSync)(e)?(T(D(e)),e):null}let r=(0,a.resolve)(e.cwd??process.cwd());for(;;){let e=(0,a.join)(r,f,m);if(T(r),(0,t.existsSync)(e))return e;let n=(0,a.dirname)(r);if(n===r)return null;r=n}}function D(e){let t=(0,a.dirname)(e);return(0,a.basename)(t)===`primitive`?(0,a.dirname)(t):t}function O(e){return(0,a.join)(D(e),p,h)}function k(e){if(!(0,t.existsSync)(e))throw new b(`missing-config`,`No ${g} found at ${e}. Run 'primitive init' to create one.`,e);let n;try{n=(0,t.readFileSync)(e,`utf-8`)}catch(t){throw new b(`malformed-config`,`Failed to read ${g} (${e}): ${t.message}`,e)}let r;try{r=JSON.parse(n)}catch(t){throw new b(`malformed-config`,`Failed to parse ${g} (${e}): ${t.message}`,e)}if(!r||typeof r!=`object`||Array.isArray(r))throw new b(`malformed-config`,`${g} (${e}) must contain a JSON object at the top level.`,e);let i=r;if(typeof i.version!=`number`||!Number.isInteger(i.version))throw new b(`malformed-config`,`${g} (${e}) is missing required integer field "version".`,e);if(i.version!==1)throw new b(`unsupported-version`,`${g} (${e}) has version ${i.version}, but this tool understands version 1.`,e);if(!i.environments||typeof i.environments!=`object`||Array.isArray(i.environments))throw new b(`malformed-config`,`${g} (${e}) is missing required "environments" object.`,e);let a={};for(let[t,n]of Object.entries(i.environments)){if(!n||typeof n!=`object`||Array.isArray(n))throw new b(`malformed-config`,`Environment "${t}" must be an object in ${g} (${e}).`,e);let r=n;if(typeof r.apiUrl!=`string`||!r.apiUrl)throw new b(`malformed-config`,`Environment "${t}" must have a non-empty "apiUrl" string in ${g} (${e}).`,e);let i=C(r.appId);if(!i)throw new b(`missing-app-id`,S(t,e),e);a[t]={apiUrl:r.apiUrl.replace(/\/$/,``),appId:i,appName:typeof r.appName==`string`?r.appName:void 0,webUrl:x(r.webUrl),description:typeof r.description==`string`?r.description:void 0}}return{version:i.version,defaultEnvironment:typeof i.defaultEnvironment==`string`&&i.defaultEnvironment?i.defaultEnvironment:void 0,environments:a}}function A(e){let n=O(e);if(!(0,t.existsSync)(n))return null;let r=e=>{throw new b(`corrupt-local-state`,`${_} (${n}) is unreadable: ${e}. Delete the file or re-run 'primitive env use <name>'.`,n)},i;try{i=JSON.parse((0,t.readFileSync)(n,`utf-8`))}catch(e){return r(e.message)}if(!i||typeof i!=`object`||Array.isArray(i))return r(`expected a JSON object`);let a=i.selectedEnvironment;return a==null||a===``?null:typeof a==`string`?a:r(`"selectedEnvironment" must be a string`)}function j(e,t){let n=Object.keys(e.environments),r=n.join(`, `)||`(none)`,i=t.configPath?`${g} (${t.configPath})`:g,a=(n,a,o)=>{if(!e.environments[n])throw new b(`unknown-environment`,`Environment "${n}" is not defined in ${i} (selected ${o}). Available: ${r}`,t.configPath);return{name:n,source:a}};if(t.explicitEnvName)return a(t.explicitEnvName,`explicit`,`explicitly`);if(t.envVarName)return a(t.envVarName,`env-var`,`via PRIMITIVE_ENV`);if(t.localSelection)return a(t.localSelection,`local`,`in ${_}`);if(e.defaultEnvironment)return a(e.defaultEnvironment,`default`,`as "defaultEnvironment"`);if(n.length===1)return{name:n[0],source:`sole`};throw n.length===0?new b(`no-selection`,`${i} has no environments defined. Run 'primitive env add <name>' or 'primitive init'.`,t.configPath):new b(`no-selection`,`No environment selected. Run 'primitive env use <name>', set "defaultEnvironment" in ${i}, or export PRIMITIVE_ENV. Available: ${r}`,t.configPath)}function M(e={}){let t=e.env??process.env,n=e.configPath??E({cwd:e.cwd,env:t});if(!n)throw new b(`missing-config`,`No ${g} found in ${e.cwd??process.cwd()} or any parent directory. Run 'primitive init' to create one, or 'primitive env add <name>' to add an environment.`);let r=D(n);T(r);let i=k(n),{name:a,source:o}=j(i,{explicitEnvName:e.explicitEnvName||null,envVarName:t.PRIMITIVE_ENV||null,localSelection:A(n),configPath:n}),s=i.environments[a];return{name:a,apiUrl:s.apiUrl,wsUrl:w(s.apiUrl),appId:s.appId,appName:s.appName,webUrl:s.webUrl,description:s.description,source:o,configPath:n,localStatePath:O(n),projectRoot:r}}var N=[`VITE_APP_ID`,`VITE_API_URL`,`VITE_WS_URL`,`VITE_APP_NAME`],P=`VITE_EXPECTED_PRIMITIVE_ENV`,F=new Map;function I(e,t){let n=process.env[e];n!==void 0&&n!==``&&F.get(e)!==n||(process.env[e]=t,F.set(e,t))}function L(e={}){return M({cwd:e.root,env:e.env,explicitEnvName:e.primitiveEnv??null})}function R(e,t,n){let r=e=>n.includes(e)?` (overridden)`:``,i=[`[primitive-env] Primitive environment: ${e.name} (selected: ${e.source})`,`[primitive-env] apiUrl: ${t.apiUrl}${r(`VITE_API_URL`)}`,`[primitive-env] wsUrl: ${t.wsUrl}${r(`VITE_WS_URL`)}`,`[primitive-env] appId: ${t.appId??`(unset)`}${r(`VITE_APP_ID`)}`,`[primitive-env] appName: ${t.appName??`(unset)`}${r(`VITE_APP_NAME`)}`,`[primitive-env] config: ${e.configPath}`];return n.length>0&&i.push(`[primitive-env] overridden by the environment: ${n.join(`, `)}`),i.join(`
69
+ `)}function z(e,t){let n=e?.envDir;return n===!1?null:typeof n==`string`&&n!==``?(0,a.resolve)(t,n):t}function B(e,t){let n=process.env[P];if(n!==void 0&&n!==``)return{value:n,where:`the process environment`};if(t===null)return null;for(let n of[`.env.${e}.local`,`.env.${e}`,`.env.local`,`.env`]){let e=G((0,a.join)(t,n))[P];if(e!==void 0)return{value:e,where:n}}return null}function V(e,t,n){return[`[primitive-env] Wrong pair: vite mode "${e}" is declared to run against Primitive environment "${t.value}", but "${n.name}" resolved.`,`[primitive-env] expected: ${t.value} (${P} in ${t.where})`,`[primitive-env] resolved: ${n.name} (selected: ${n.source})`,`[primitive-env] config: ${n.configPath}`,``,`Run the pair you meant — PRIMITIVE_ENV=${t.value} <command> --mode ${e}, or 'primitive env use ${t.value}' — or change/remove ${P} in ${t.where}.`,``,`A mode declares this when its .env.${e} carries values coupled to one backend, so continuing would run this environment's identity with another's configuration.`].join(`
70
+ `)}function H(e,t,n){let r=B(e,t);if(r&&r.value!==``&&r.value!==n.name)throw Error(V(e,r,n))}function U(e={}){let t=null,n=null,r,i=[],a=!1,o=null;return{name:`primitive-env`,enforce:`pre`,config(s,c){a=c?.command===`serve`,o=null;let l=s?.root??process.cwd(),u=c?.mode??`development`,d=W(u,l),f={};for(let e of N){let t=process.env[e],n=t!==void 0&&F.get(e)!==t?t:d[e];n!==void 0&&n!==``&&(f[e]=n)}let p;try{p=M({cwd:l,explicitEnvName:e.primitiveEnv??null})}catch(e){if(e instanceof b&&e.kind===`missing-config`&&f.VITE_APP_ID&&f.VITE_API_URL)return console.warn(`[primitive-env] No primitive/config.json found; using VITE_APP_ID / VITE_API_URL from the environment. Run 'primitive init' to make the project the source of truth.`),r=f.VITE_APP_NAME,{};throw e}if(!(f.VITE_APP_ID&&f.VITE_API_URL)){let e=z(s,l);H(u,e,p),o={root:l,mode:u,dir:e}}t=p,r=f.VITE_APP_NAME??p.appName;let m={VITE_APP_ID:p.appId,VITE_API_URL:p.apiUrl,VITE_WS_URL:p.wsUrl,VITE_APP_NAME:p.appName},h={"import.meta.env.VITE_PRIMITIVE_ENV":JSON.stringify(p.name)},g=[];for(let e of N){if(f[e]!==void 0){g.push(e);continue}let t=m[e];t!==void 0&&(h[`import.meta.env.${e}`]=JSON.stringify(t),I(e,t))}return I(`VITE_PRIMITIVE_ENV`,p.name),i=g,n={apiUrl:f.VITE_API_URL??p.apiUrl,wsUrl:f.VITE_WS_URL??p.wsUrl,appId:f.VITE_APP_ID??p.appId,appName:r},e.logResolved!==!1&&console.log(R(p,n,g)),g.length>0&&console.warn(`[primitive-env] Overridden by an explicit value in .env or the shell: ${g.join(`, `)}. Remove it to use primitive/config.json (deploys reject these outright).`),{define:h}},configResolved(e){if(!o||!t)return;let n=z(e,o.root),r=e?.mode??o.mode;n===o.dir&&r===o.mode||(H(r,n,t),o={root:o.root,mode:r,dir:n})},transformIndexHtml(){if(!a||!t||!n)return[];let e=`%c[primitive] env=${t.name} api=${n.apiUrl} app=${n.appId??`(unset)`} (${t.configPath})`+(i.length>0?` overridden: ${i.join(`, `)}`:``);return[{tag:`script`,injectTo:`head`,children:`console.info(${JSON.stringify(e)}, "color:#888");`}]},appName(){return r},resolved(){return t}}}function W(e,t){let n=[`.env`,`.env.local`,`.env.${e}`,`.env.${e}.local`],r={};for(let e of n)Object.assign(r,G((0,a.join)(t,e)));return r}function G(e){if(!(0,t.existsSync)(e))return{};let n={};for(let r of(0,t.readFileSync)(e,`utf-8`).split(/\r?\n/)){let e=r.trim();if(!e||e.startsWith(`#`))continue;let t=e.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);if(!t)continue;let i=t[2].trim(),a=i[0],o=a===`"`||a===`'`||a==="`"?i.indexOf(a,1):-1;n[t[1]]=o===-1?i.replace(/\s+#.*$/,``).trim():i.slice(1,o)}return n}exports.PrimitiveEnvError=b,exports.primitiveDevTools=d,exports.primitiveEnv=U,exports.resolvePrimitiveEnv=L;
@@ -63,14 +63,18 @@ export const keyboardShortcut = ${JSON.stringify(s)};
63
63
  }
64
64
  };
65
65
  }
66
- var h = ".primitive", g = "config.json", _ = "local.json", v = ".primitive/config.json", y = ".primitive/local.json", b = class extends Error {
66
+ var h = "primitive", g = ".primitive", _ = "config.json", v = "local.json", y = "primitive/config.json", b = ".primitive/local.json", x = "getting-started/cli-project-migration";
67
+ function S(e) {
68
+ return `${e} The Primitive configuration tree moved out of the hidden directory: the config is now ${y} and each environment's TOML is at primitive/<env>/, while .primitive/ keeps only machine-local state. Nothing is read from either tree until the project is migrated — follow ${x}.`;
69
+ }
70
+ var C = class extends Error {
67
71
  kind;
68
72
  path;
69
73
  constructor(e, t, n) {
70
74
  super(t), this.name = "PrimitiveEnvError", this.kind = e, this.path = n;
71
75
  }
72
76
  };
73
- function x(e) {
77
+ function w(e) {
74
78
  if (typeof e != "string") return;
75
79
  let t = e.trim();
76
80
  if (!t) return;
@@ -82,68 +86,75 @@ function x(e) {
82
86
  }
83
87
  if (!(n.protocol !== "https:" && n.protocol !== "http:") && !(n.protocol === "http:" && n.hostname !== "localhost" && n.hostname !== "127.0.0.1") && !(n.username || n.password) && !(n.pathname !== "/" && n.pathname !== "") && !(n.search || n.hash)) return n.origin;
84
88
  }
85
- function S(e, t) {
86
- return `Environment "${e}" in ${t ? `${v} (${t})` : v} has no "appId". Every environment names exactly one app — add "appId": "<app-id>" to that environment in ${v}. Find the app id in the Primitive admin UI, with 'primitive apps list' run from a directory outside this project, or as the residual "currentAppId" in .primitive/credentials.json if this environment was ever pointed at an app by the retired per-machine app selection.`;
89
+ function T(e, t) {
90
+ return `Environment "${e}" in ${t ? `${y} (${t})` : y} has no "appId". Every environment names exactly one app — add "appId": "<app-id>" to that environment in ${y}. Find the app id in the Primitive admin UI, with 'primitive apps list' run from a directory outside this project, or as the residual "currentAppId" in .primitive/credentials.json if this environment was ever pointed at an app by the retired per-machine app selection.`;
87
91
  }
88
- function C(e) {
92
+ function E(e) {
89
93
  if (typeof e == "string") return e.trim() || void 0;
90
94
  }
91
- function w(e) {
95
+ function D(e) {
92
96
  return e.startsWith("https://") ? "wss://" + e.slice(8) : e.startsWith("http://") ? "ws://" + e.slice(7) : e;
93
97
  }
94
- function T(t = {}) {
98
+ function O(t) {
99
+ let n = s(t, g, _);
100
+ if (e(n)) throw new C("stale-layout", S(`${n} still exists.`), n);
101
+ let r = s(t, g, "sync");
102
+ if (e(r)) throw new C("stale-layout", S(`${r} still exists.`), r);
103
+ }
104
+ function k(t = {}) {
95
105
  let n = (t.env ?? process.env).PRIMITIVE_PROJECT_CONFIG;
96
106
  if (n) {
97
107
  let t = c(n);
98
- return e(t) ? t : null;
108
+ if (a(o(t)) === ".primitive") throw new C("stale-layout", S(`PRIMITIVE_PROJECT_CONFIG points at ${t}, inside a ${g}/ directory.`), t);
109
+ return e(t) ? (O(A(t)), t) : null;
99
110
  }
100
111
  let r = c(t.cwd ?? process.cwd());
101
112
  for (;;) {
102
- let t = s(r, h, g);
103
- if (e(t)) return t;
113
+ let t = s(r, h, _);
114
+ if (O(r), e(t)) return t;
104
115
  let n = o(r);
105
116
  if (n === r) return null;
106
117
  r = n;
107
118
  }
108
119
  }
109
- function E(e) {
120
+ function A(e) {
110
121
  let t = o(e);
111
- return a(t) === ".primitive" ? o(t) : t;
122
+ return a(t) === "primitive" ? o(t) : t;
112
123
  }
113
- function D(e) {
114
- return s(o(e), _);
124
+ function j(e) {
125
+ return s(A(e), g, v);
115
126
  }
116
- function O(n) {
117
- if (!e(n)) throw new b("missing-config", `No ${v} found at ${n}. Run 'primitive init' to create one.`, n);
127
+ function M(n) {
128
+ if (!e(n)) throw new C("missing-config", `No ${y} found at ${n}. Run 'primitive init' to create one.`, n);
118
129
  let r;
119
130
  try {
120
131
  r = t(n, "utf-8");
121
132
  } catch (e) {
122
- throw new b("malformed-config", `Failed to read ${v} (${n}): ${e.message}`, n);
133
+ throw new C("malformed-config", `Failed to read ${y} (${n}): ${e.message}`, n);
123
134
  }
124
135
  let i;
125
136
  try {
126
137
  i = JSON.parse(r);
127
138
  } catch (e) {
128
- throw new b("malformed-config", `Failed to parse ${v} (${n}): ${e.message}`, n);
139
+ throw new C("malformed-config", `Failed to parse ${y} (${n}): ${e.message}`, n);
129
140
  }
130
- if (!i || typeof i != "object" || Array.isArray(i)) throw new b("malformed-config", `${v} (${n}) must contain a JSON object at the top level.`, n);
141
+ if (!i || typeof i != "object" || Array.isArray(i)) throw new C("malformed-config", `${y} (${n}) must contain a JSON object at the top level.`, n);
131
142
  let a = i;
132
- if (typeof a.version != "number" || !Number.isInteger(a.version)) throw new b("malformed-config", `${v} (${n}) is missing required integer field "version".`, n);
133
- if (a.version !== 1) throw new b("unsupported-version", `${v} (${n}) has version ${a.version}, but this tool understands version 1.`, n);
134
- if (!a.environments || typeof a.environments != "object" || Array.isArray(a.environments)) throw new b("malformed-config", `${v} (${n}) is missing required "environments" object.`, n);
143
+ if (typeof a.version != "number" || !Number.isInteger(a.version)) throw new C("malformed-config", `${y} (${n}) is missing required integer field "version".`, n);
144
+ if (a.version !== 1) throw new C("unsupported-version", `${y} (${n}) has version ${a.version}, but this tool understands version 1.`, n);
145
+ if (!a.environments || typeof a.environments != "object" || Array.isArray(a.environments)) throw new C("malformed-config", `${y} (${n}) is missing required "environments" object.`, n);
135
146
  let o = {};
136
147
  for (let [e, t] of Object.entries(a.environments)) {
137
- if (!t || typeof t != "object" || Array.isArray(t)) throw new b("malformed-config", `Environment "${e}" must be an object in ${v} (${n}).`, n);
148
+ if (!t || typeof t != "object" || Array.isArray(t)) throw new C("malformed-config", `Environment "${e}" must be an object in ${y} (${n}).`, n);
138
149
  let r = t;
139
- if (typeof r.apiUrl != "string" || !r.apiUrl) throw new b("malformed-config", `Environment "${e}" must have a non-empty "apiUrl" string in ${v} (${n}).`, n);
140
- let i = C(r.appId);
141
- if (!i) throw new b("missing-app-id", S(e, n), n);
150
+ if (typeof r.apiUrl != "string" || !r.apiUrl) throw new C("malformed-config", `Environment "${e}" must have a non-empty "apiUrl" string in ${y} (${n}).`, n);
151
+ let i = E(r.appId);
152
+ if (!i) throw new C("missing-app-id", T(e, n), n);
142
153
  o[e] = {
143
154
  apiUrl: r.apiUrl.replace(/\/$/, ""),
144
155
  appId: i,
145
156
  appName: typeof r.appName == "string" ? r.appName : void 0,
146
- webUrl: x(r.webUrl),
157
+ webUrl: w(r.webUrl),
147
158
  description: typeof r.description == "string" ? r.description : void 0
148
159
  };
149
160
  }
@@ -153,11 +164,11 @@ function O(n) {
153
164
  environments: o
154
165
  };
155
166
  }
156
- function k(n) {
157
- let r = D(n);
167
+ function N(n) {
168
+ let r = j(n);
158
169
  if (!e(r)) return null;
159
170
  let i = (e) => {
160
- throw new b("corrupt-local-state", `${y} (${r}) is unreadable: ${e}. Delete the file or re-run 'primitive env use <name>'.`, r);
171
+ throw new C("corrupt-local-state", `${b} (${r}) is unreadable: ${e}. Delete the file or re-run 'primitive env use <name>'.`, r);
161
172
  }, a;
162
173
  try {
163
174
  a = JSON.parse(t(r, "utf-8"));
@@ -168,9 +179,9 @@ function k(n) {
168
179
  let o = a.selectedEnvironment;
169
180
  return o == null || o === "" ? null : typeof o == "string" ? o : i("\"selectedEnvironment\" must be a string");
170
181
  }
171
- function A(e, t) {
172
- let n = Object.keys(e.environments), r = n.join(", ") || "(none)", i = t.configPath ? `${v} (${t.configPath})` : v, a = (n, a, o) => {
173
- if (!e.environments[n]) throw new b("unknown-environment", `Environment "${n}" is not defined in ${i} (selected ${o}). Available: ${r}`, t.configPath);
182
+ function P(e, t) {
183
+ let n = Object.keys(e.environments), r = n.join(", ") || "(none)", i = t.configPath ? `${y} (${t.configPath})` : y, a = (n, a, o) => {
184
+ if (!e.environments[n]) throw new C("unknown-environment", `Environment "${n}" is not defined in ${i} (selected ${o}). Available: ${r}`, t.configPath);
174
185
  return {
175
186
  name: n,
176
187
  source: a
@@ -178,60 +189,62 @@ function A(e, t) {
178
189
  };
179
190
  if (t.explicitEnvName) return a(t.explicitEnvName, "explicit", "explicitly");
180
191
  if (t.envVarName) return a(t.envVarName, "env-var", "via PRIMITIVE_ENV");
181
- if (t.localSelection) return a(t.localSelection, "local", `in ${y}`);
192
+ if (t.localSelection) return a(t.localSelection, "local", `in ${b}`);
182
193
  if (e.defaultEnvironment) return a(e.defaultEnvironment, "default", "as \"defaultEnvironment\"");
183
194
  if (n.length === 1) return {
184
195
  name: n[0],
185
196
  source: "sole"
186
197
  };
187
- throw n.length === 0 ? new b("no-selection", `${i} has no environments defined. Run 'primitive env add <name>' or 'primitive init'.`, t.configPath) : new b("no-selection", `No environment selected. Run 'primitive env use <name>', set "defaultEnvironment" in ${i}, or export PRIMITIVE_ENV. Available: ${r}`, t.configPath);
198
+ throw n.length === 0 ? new C("no-selection", `${i} has no environments defined. Run 'primitive env add <name>' or 'primitive init'.`, t.configPath) : new C("no-selection", `No environment selected. Run 'primitive env use <name>', set "defaultEnvironment" in ${i}, or export PRIMITIVE_ENV. Available: ${r}`, t.configPath);
188
199
  }
189
- function j(e = {}) {
190
- let t = e.env ?? process.env, n = e.configPath ?? T({
200
+ function F(e = {}) {
201
+ let t = e.env ?? process.env, n = e.configPath ?? k({
191
202
  cwd: e.cwd,
192
203
  env: t
193
204
  });
194
- if (!n) throw new b("missing-config", `No ${v} found in ${e.cwd ?? process.cwd()} or any parent directory. Run 'primitive init' to create one, or 'primitive env add <name>' to add an environment.`);
195
- let r = O(n), { name: i, source: a } = A(r, {
205
+ if (!n) throw new C("missing-config", `No ${y} found in ${e.cwd ?? process.cwd()} or any parent directory. Run 'primitive init' to create one, or 'primitive env add <name>' to add an environment.`);
206
+ let r = A(n);
207
+ O(r);
208
+ let i = M(n), { name: a, source: o } = P(i, {
196
209
  explicitEnvName: e.explicitEnvName || null,
197
210
  envVarName: t.PRIMITIVE_ENV || null,
198
- localSelection: k(n),
211
+ localSelection: N(n),
199
212
  configPath: n
200
- }), o = r.environments[i];
213
+ }), s = i.environments[a];
201
214
  return {
202
- name: i,
203
- apiUrl: o.apiUrl,
204
- wsUrl: w(o.apiUrl),
205
- appId: o.appId,
206
- appName: o.appName,
207
- webUrl: o.webUrl,
208
- description: o.description,
209
- source: a,
215
+ name: a,
216
+ apiUrl: s.apiUrl,
217
+ wsUrl: D(s.apiUrl),
218
+ appId: s.appId,
219
+ appName: s.appName,
220
+ webUrl: s.webUrl,
221
+ description: s.description,
222
+ source: o,
210
223
  configPath: n,
211
- localStatePath: D(n),
212
- projectRoot: E(n)
224
+ localStatePath: j(n),
225
+ projectRoot: r
213
226
  };
214
227
  }
215
228
  //#endregion
216
229
  //#region src/dev-tools/vite-plugin/primitive-env.ts
217
- var M = [
230
+ var I = [
218
231
  "VITE_APP_ID",
219
232
  "VITE_API_URL",
220
233
  "VITE_WS_URL",
221
234
  "VITE_APP_NAME"
222
- ], N = "VITE_EXPECTED_PRIMITIVE_ENV", P = /* @__PURE__ */ new Map();
223
- function F(e, t) {
235
+ ], L = "VITE_EXPECTED_PRIMITIVE_ENV", R = /* @__PURE__ */ new Map();
236
+ function z(e, t) {
224
237
  let n = process.env[e];
225
- n !== void 0 && n !== "" && P.get(e) !== n || (process.env[e] = t, P.set(e, t));
238
+ n !== void 0 && n !== "" && R.get(e) !== n || (process.env[e] = t, R.set(e, t));
226
239
  }
227
- function I(e = {}) {
228
- return j({
240
+ function B(e = {}) {
241
+ return F({
229
242
  cwd: e.root,
230
243
  env: e.env,
231
244
  explicitEnvName: e.primitiveEnv ?? null
232
245
  });
233
246
  }
234
- function L(e, t, n) {
247
+ function V(e, t, n) {
235
248
  let r = (e) => n.includes(e) ? " (overridden)" : "", i = [
236
249
  `[primitive-env] Primitive environment: ${e.name} (selected: ${e.source})`,
237
250
  `[primitive-env] apiUrl: ${t.apiUrl}${r("VITE_API_URL")}`,
@@ -242,12 +255,12 @@ function L(e, t, n) {
242
255
  ];
243
256
  return n.length > 0 && i.push(`[primitive-env] overridden by the environment: ${n.join(", ")}`), i.join("\n");
244
257
  }
245
- function R(e, t) {
258
+ function H(e, t) {
246
259
  let n = e?.envDir;
247
260
  return n === !1 ? null : typeof n == "string" && n !== "" ? c(t, n) : t;
248
261
  }
249
- function z(e, t) {
250
- let n = process.env[N];
262
+ function U(e, t) {
263
+ let n = process.env[L];
251
264
  if (n !== void 0 && n !== "") return {
252
265
  value: n,
253
266
  where: "the process environment"
@@ -259,7 +272,7 @@ function z(e, t) {
259
272
  ".env.local",
260
273
  ".env"
261
274
  ]) {
262
- let e = W(s(t, n))[N];
275
+ let e = J(s(t, n))[L];
263
276
  if (e !== void 0) return {
264
277
  value: e,
265
278
  where: n
@@ -267,47 +280,47 @@ function z(e, t) {
267
280
  }
268
281
  return null;
269
282
  }
270
- function B(e, t, n) {
283
+ function W(e, t, n) {
271
284
  return [
272
285
  `[primitive-env] Wrong pair: vite mode "${e}" is declared to run against Primitive environment "${t.value}", but "${n.name}" resolved.`,
273
- `[primitive-env] expected: ${t.value} (${N} in ${t.where})`,
286
+ `[primitive-env] expected: ${t.value} (${L} in ${t.where})`,
274
287
  `[primitive-env] resolved: ${n.name} (selected: ${n.source})`,
275
288
  `[primitive-env] config: ${n.configPath}`,
276
289
  "",
277
- `Run the pair you meant — PRIMITIVE_ENV=${t.value} <command> --mode ${e}, or 'primitive env use ${t.value}' — or change/remove ${N} in ${t.where}.`,
290
+ `Run the pair you meant — PRIMITIVE_ENV=${t.value} <command> --mode ${e}, or 'primitive env use ${t.value}' — or change/remove ${L} in ${t.where}.`,
278
291
  "",
279
292
  `A mode declares this when its .env.${e} carries values coupled to one backend, so continuing would run this environment's identity with another's configuration.`
280
293
  ].join("\n");
281
294
  }
282
- function V(e, t, n) {
283
- let r = z(e, t);
284
- if (r && r.value !== "" && r.value !== n.name) throw Error(B(e, r, n));
295
+ function G(e, t, n) {
296
+ let r = U(e, t);
297
+ if (r && r.value !== "" && r.value !== n.name) throw Error(W(e, r, n));
285
298
  }
286
- function H(e = {}) {
299
+ function K(e = {}) {
287
300
  let t = null, n = null, r, i = [], a = !1, o = null;
288
301
  return {
289
302
  name: "primitive-env",
290
303
  enforce: "pre",
291
304
  config(s, c) {
292
305
  a = c?.command === "serve", o = null;
293
- let l = s?.root ?? process.cwd(), u = c?.mode ?? "development", d = U(u, l), f = {};
294
- for (let e of M) {
295
- let t = process.env[e], n = t !== void 0 && P.get(e) !== t ? t : d[e];
306
+ let l = s?.root ?? process.cwd(), u = c?.mode ?? "development", d = q(u, l), f = {};
307
+ for (let e of I) {
308
+ let t = process.env[e], n = t !== void 0 && R.get(e) !== t ? t : d[e];
296
309
  n !== void 0 && n !== "" && (f[e] = n);
297
310
  }
298
311
  let p;
299
312
  try {
300
- p = j({
313
+ p = F({
301
314
  cwd: l,
302
315
  explicitEnvName: e.primitiveEnv ?? null
303
316
  });
304
317
  } catch (e) {
305
- if (e instanceof b && e.kind === "missing-config" && f.VITE_APP_ID && f.VITE_API_URL) return console.warn("[primitive-env] No .primitive/config.json found; using VITE_APP_ID / VITE_API_URL from the environment. Run 'primitive init' to make the project the source of truth."), r = f.VITE_APP_NAME, {};
318
+ if (e instanceof C && e.kind === "missing-config" && f.VITE_APP_ID && f.VITE_API_URL) return console.warn("[primitive-env] No primitive/config.json found; using VITE_APP_ID / VITE_API_URL from the environment. Run 'primitive init' to make the project the source of truth."), r = f.VITE_APP_NAME, {};
306
319
  throw e;
307
320
  }
308
321
  if (!(f.VITE_APP_ID && f.VITE_API_URL)) {
309
- let e = R(s, l);
310
- V(u, e, p), o = {
322
+ let e = H(s, l);
323
+ G(u, e, p), o = {
311
324
  root: l,
312
325
  mode: u,
313
326
  dir: e
@@ -320,25 +333,25 @@ function H(e = {}) {
320
333
  VITE_WS_URL: p.wsUrl,
321
334
  VITE_APP_NAME: p.appName
322
335
  }, h = { "import.meta.env.VITE_PRIMITIVE_ENV": JSON.stringify(p.name) }, g = [];
323
- for (let e of M) {
336
+ for (let e of I) {
324
337
  if (f[e] !== void 0) {
325
338
  g.push(e);
326
339
  continue;
327
340
  }
328
341
  let t = m[e];
329
- t !== void 0 && (h[`import.meta.env.${e}`] = JSON.stringify(t), F(e, t));
342
+ t !== void 0 && (h[`import.meta.env.${e}`] = JSON.stringify(t), z(e, t));
330
343
  }
331
- return F("VITE_PRIMITIVE_ENV", p.name), i = g, n = {
344
+ return z("VITE_PRIMITIVE_ENV", p.name), i = g, n = {
332
345
  apiUrl: f.VITE_API_URL ?? p.apiUrl,
333
346
  wsUrl: f.VITE_WS_URL ?? p.wsUrl,
334
347
  appId: f.VITE_APP_ID ?? p.appId,
335
348
  appName: r
336
- }, e.logResolved !== !1 && console.log(L(p, n, g)), g.length > 0 && console.warn(`[primitive-env] Overridden by an explicit value in .env or the shell: ${g.join(", ")}. Remove it to use .primitive/config.json (deploys reject these outright).`), { define: h };
349
+ }, e.logResolved !== !1 && console.log(V(p, n, g)), g.length > 0 && console.warn(`[primitive-env] Overridden by an explicit value in .env or the shell: ${g.join(", ")}. Remove it to use primitive/config.json (deploys reject these outright).`), { define: h };
337
350
  },
338
351
  configResolved(e) {
339
352
  if (!o || !t) return;
340
- let n = R(e, o.root), r = e?.mode ?? o.mode;
341
- n === o.dir && r === o.mode || (V(r, n, t), o = {
353
+ let n = H(e, o.root), r = e?.mode ?? o.mode;
354
+ n === o.dir && r === o.mode || (G(r, n, t), o = {
342
355
  root: o.root,
343
356
  mode: r,
344
357
  dir: n
@@ -361,17 +374,17 @@ function H(e = {}) {
361
374
  }
362
375
  };
363
376
  }
364
- function U(e, t) {
377
+ function q(e, t) {
365
378
  let n = [
366
379
  ".env",
367
380
  ".env.local",
368
381
  `.env.${e}`,
369
382
  `.env.${e}.local`
370
383
  ], r = {};
371
- for (let e of n) Object.assign(r, W(s(t, e)));
384
+ for (let e of n) Object.assign(r, J(s(t, e)));
372
385
  return r;
373
386
  }
374
- function W(n) {
387
+ function J(n) {
375
388
  if (!e(n)) return {};
376
389
  let r = {};
377
390
  for (let e of t(n, "utf-8").split(/\r?\n/)) {
@@ -385,4 +398,4 @@ function W(n) {
385
398
  return r;
386
399
  }
387
400
  //#endregion
388
- export { b as PrimitiveEnvError, m as primitiveDevTools, H as primitiveEnv, I as resolvePrimitiveEnv };
401
+ export { C as PrimitiveEnvError, m as primitiveDevTools, K as primitiveEnv, B as resolvePrimitiveEnv };
@@ -2,7 +2,7 @@
2
2
  * `primitiveEnv()` — Primitive environment configuration for a Vite app (#2873).
3
3
  *
4
4
  * A scaffolded app used to type its backend URL and app ID twice: once in
5
- * `.primitive/config.json` (which every `primitive` command reads) and again in
5
+ * `primitive/config.json` (which every `primitive` command reads) and again in
6
6
  * `.env` / `.env.production`. This plugin removes the second copy. It resolves
7
7
  * the selected Primitive environment at config time and `define`s the keys the
8
8
  * app already reads: