primitive-app 3.1.0-alpha.17 → 3.1.0-alpha.18

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.
@@ -65,5 +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){return e.startsWith(`https://`)?`wss://`+e.slice(8):e.startsWith(`http://`)?`ws://`+e.slice(7):e}function y(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 b(e){let t=(0,a.dirname)(e);return(0,a.basename)(t)===`.primitive`?(0,a.dirname)(t):t}function x(e){return(0,a.join)((0,a.dirname)(e),m)}function S(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);a[t]={apiUrl:r.apiUrl.replace(/\/$/,``),appId:typeof r.appId==`string`?r.appId:void 0,appName:typeof r.appName==`string`?r.appName:void 0,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 C(e){let n=x(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 w(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 T(e={}){let t=e.env??process.env,n=e.configPath??y({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=S(n),{name:i,source:a}=w(r,{explicitEnvName:e.explicitEnvName||null,envVarName:t.PRIMITIVE_ENV||null,localSelection:C(n),configPath:n}),o=r.environments[i];if(e.requireAppId&&!o.appId)throw new _(`missing-app-id`,`Environment "${i}" in ${h} (${n}) has no "appId", which this build needs. Add one with 'primitive env add ${i} --api-url ${o.apiUrl} --app-id <id>' or edit the config.`,n);return{name:i,apiUrl:o.apiUrl,wsUrl:v(o.apiUrl),appId:o.appId,appName:o.appName,description:o.description,source:a,configPath:n,localStatePath:x(n),projectRoot:b(n)}}var E=[`VITE_APP_ID`,`VITE_API_URL`,`VITE_WS_URL`,`VITE_APP_NAME`],D=new Map;function O(e,t){let n=process.env[e];n!==void 0&&n!==``&&D.get(e)!==n||(process.env[e]=t,D.set(e,t))}function k(e={}){return T({cwd:e.root,env:e.env,explicitEnvName:e.primitiveEnv??null,requireAppId:e.requireAppId})}function A(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 j(e={}){let t=null,n=null,r,i=[],a=!1;return{name:`primitive-env`,enforce:`pre`,config(o,s){a=s?.command===`serve`;let c=o?.root??process.cwd(),l=M(s?.mode??`development`,c),u={};for(let e of E){let t=process.env[e],n=t!==void 0&&D.get(e)!==t?t:l[e];n!==void 0&&n!==``&&(u[e]=n)}let d;try{d=T({cwd:c,explicitEnvName:e.primitiveEnv??null,requireAppId:!u.VITE_APP_ID})}catch(e){if(e instanceof _&&e.kind===`missing-config`&&u.VITE_APP_ID&&u.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=u.VITE_APP_NAME,{};throw e}t=d,r=u.VITE_APP_NAME??d.appName;let f={VITE_APP_ID:d.appId,VITE_API_URL:d.apiUrl,VITE_WS_URL:d.wsUrl,VITE_APP_NAME:d.appName},p={"import.meta.env.VITE_PRIMITIVE_ENV":JSON.stringify(d.name)},m=[];for(let e of E){if(u[e]!==void 0){m.push(e);continue}let t=f[e];t!==void 0&&(p[`import.meta.env.${e}`]=JSON.stringify(t),O(e,t))}return O(`VITE_PRIMITIVE_ENV`,d.name),i=m,n={apiUrl:u.VITE_API_URL??d.apiUrl,wsUrl:u.VITE_WS_URL??d.wsUrl,appId:u.VITE_APP_ID??d.appId,appName:r},e.logResolved!==!1&&console.log(A(d,n,m)),m.length>0&&console.warn(`[primitive-env] Overridden by an explicit value in .env or the shell: ${m.join(`, `)}. Remove it to use .primitive/config.json (deploys reject these outright).`),{define:p}},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 M(e,t){let n=[`.env`,`.env.local`,`.env.${e}`,`.env.${e}.local`],r={};for(let e of n)Object.assign(r,N((0,a.join)(t,e)));return r}function N(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();i=i.startsWith(`"`)&&i.endsWith(`"`)&&i.length>1||i.startsWith(`'`)&&i.endsWith(`'`)&&i.length>1?i.slice(1,-1):i.replace(/\s+#.*$/,``).trim(),n[t[1]]=i}return n}exports.PrimitiveEnvError=_,exports.primitiveDevTools=d,exports.primitiveEnv=j,exports.resolvePrimitiveEnv=k;
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){return e.startsWith(`https://`)?`wss://`+e.slice(8):e.startsWith(`http://`)?`ws://`+e.slice(7):e}function y(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 b(e){let t=(0,a.dirname)(e);return(0,a.basename)(t)===`.primitive`?(0,a.dirname)(t):t}function x(e){return(0,a.join)((0,a.dirname)(e),m)}function S(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);a[t]={apiUrl:r.apiUrl.replace(/\/$/,``),appId:typeof r.appId==`string`?r.appId:void 0,appName:typeof r.appName==`string`?r.appName:void 0,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 C(e){let n=x(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 w(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 T(e={}){let t=e.env??process.env,n=e.configPath??y({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=S(n),{name:i,source:a}=w(r,{explicitEnvName:e.explicitEnvName||null,envVarName:t.PRIMITIVE_ENV||null,localSelection:C(n),configPath:n}),o=r.environments[i];if(e.requireAppId&&!o.appId)throw new _(`missing-app-id`,`Environment "${i}" in ${h} (${n}) has no "appId", which this build needs. Add one with 'primitive env add ${i} --api-url ${o.apiUrl} --app-id <id>' or edit the config.`,n);return{name:i,apiUrl:o.apiUrl,wsUrl:v(o.apiUrl),appId:o.appId,appName:o.appName,description:o.description,source:a,configPath:n,localStatePath:x(n),projectRoot:b(n)}}var E=[`VITE_APP_ID`,`VITE_API_URL`,`VITE_WS_URL`,`VITE_APP_NAME`],D=`VITE_EXPECTED_PRIMITIVE_ENV`,O=new Map;function k(e,t){let n=process.env[e];n!==void 0&&n!==``&&O.get(e)!==n||(process.env[e]=t,O.set(e,t))}function A(e={}){return T({cwd:e.root,env:e.env,explicitEnvName:e.primitiveEnv??null,requireAppId:e.requireAppId})}function j(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 M(e,t){let n=e?.envDir;return n===!1?null:typeof n==`string`&&n!==``?(0,a.resolve)(t,n):t}function N(e,t){let n=process.env[D];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=R((0,a.join)(t,n))[D];if(e!==void 0)return{value:e,where:n}}return null}function P(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} (${D} 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 ${D} 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 F(e,t,n){let r=N(e,t);if(r&&r.value!==``&&r.value!==n.name)throw Error(P(e,r,n))}function I(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=L(u,l),f={};for(let e of E){let t=process.env[e],n=t!==void 0&&O.get(e)!==t?t:d[e];n!==void 0&&n!==``&&(f[e]=n)}let p;try{p=T({cwd:l,explicitEnvName:e.primitiveEnv??null,requireAppId:!f.VITE_APP_ID})}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=M(s,l);F(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 E){if(f[e]!==void 0){g.push(e);continue}let t=m[e];t!==void 0&&(h[`import.meta.env.${e}`]=JSON.stringify(t),k(e,t))}return k(`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(j(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=M(e,o.root),r=e?.mode??o.mode;n===o.dir&&r===o.mode||(F(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 L(e,t){let n=[`.env`,`.env.local`,`.env.${e}`,`.env.${e}.local`],r={};for(let e of n)Object.assign(r,R((0,a.join)(t,e)));return r}function R(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=I,exports.resolvePrimitiveEnv=A;
@@ -198,12 +198,12 @@ var k = [
198
198
  "VITE_API_URL",
199
199
  "VITE_WS_URL",
200
200
  "VITE_APP_NAME"
201
- ], A = /* @__PURE__ */ new Map();
202
- function j(e, t) {
201
+ ], A = "VITE_EXPECTED_PRIMITIVE_ENV", j = /* @__PURE__ */ new Map();
202
+ function M(e, t) {
203
203
  let n = process.env[e];
204
- n !== void 0 && n !== "" && A.get(e) !== n || (process.env[e] = t, A.set(e, t));
204
+ n !== void 0 && n !== "" && j.get(e) !== n || (process.env[e] = t, j.set(e, t));
205
205
  }
206
- function M(e = {}) {
206
+ function N(e = {}) {
207
207
  return O({
208
208
  cwd: e.root,
209
209
  env: e.env,
@@ -211,7 +211,7 @@ function M(e = {}) {
211
211
  requireAppId: e.requireAppId
212
212
  });
213
213
  }
214
- function N(e, t, n) {
214
+ function P(e, t, n) {
215
215
  let r = (e) => n.includes(e) ? " (overridden)" : "", i = [
216
216
  `[primitive-env] Primitive environment: ${e.name} (selected: ${e.source})`,
217
217
  `[primitive-env] apiUrl: ${t.apiUrl}${r("VITE_API_URL")}`,
@@ -222,50 +222,108 @@ function N(e, t, n) {
222
222
  ];
223
223
  return n.length > 0 && i.push(`[primitive-env] overridden by the environment: ${n.join(", ")}`), i.join("\n");
224
224
  }
225
- function P(e = {}) {
226
- let t = null, n = null, r, i = [], a = !1;
225
+ function F(e, t) {
226
+ let n = e?.envDir;
227
+ return n === !1 ? null : typeof n == "string" && n !== "" ? c(t, n) : t;
228
+ }
229
+ function I(e, t) {
230
+ let n = process.env[A];
231
+ if (n !== void 0 && n !== "") return {
232
+ value: n,
233
+ where: "the process environment"
234
+ };
235
+ if (t === null) return null;
236
+ for (let n of [
237
+ `.env.${e}.local`,
238
+ `.env.${e}`,
239
+ ".env.local",
240
+ ".env"
241
+ ]) {
242
+ let e = V(s(t, n))[A];
243
+ if (e !== void 0) return {
244
+ value: e,
245
+ where: n
246
+ };
247
+ }
248
+ return null;
249
+ }
250
+ function L(e, t, n) {
251
+ return [
252
+ `[primitive-env] Wrong pair: vite mode "${e}" is declared to run against Primitive environment "${t.value}", but "${n.name}" resolved.`,
253
+ `[primitive-env] expected: ${t.value} (${A} in ${t.where})`,
254
+ `[primitive-env] resolved: ${n.name} (selected: ${n.source})`,
255
+ `[primitive-env] config: ${n.configPath}`,
256
+ "",
257
+ `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}.`,
258
+ "",
259
+ `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.`
260
+ ].join("\n");
261
+ }
262
+ function R(e, t, n) {
263
+ let r = I(e, t);
264
+ if (r && r.value !== "" && r.value !== n.name) throw Error(L(e, r, n));
265
+ }
266
+ function z(e = {}) {
267
+ let t = null, n = null, r, i = [], a = !1, o = null;
227
268
  return {
228
269
  name: "primitive-env",
229
270
  enforce: "pre",
230
- config(o, s) {
231
- a = s?.command === "serve";
232
- let c = o?.root ?? process.cwd(), l = F(s?.mode ?? "development", c), u = {};
271
+ config(s, c) {
272
+ a = c?.command === "serve", o = null;
273
+ let l = s?.root ?? process.cwd(), u = c?.mode ?? "development", d = B(u, l), f = {};
233
274
  for (let e of k) {
234
- let t = process.env[e], n = t !== void 0 && A.get(e) !== t ? t : l[e];
235
- n !== void 0 && n !== "" && (u[e] = n);
275
+ let t = process.env[e], n = t !== void 0 && j.get(e) !== t ? t : d[e];
276
+ n !== void 0 && n !== "" && (f[e] = n);
236
277
  }
237
- let d;
278
+ let p;
238
279
  try {
239
- d = O({
240
- cwd: c,
280
+ p = O({
281
+ cwd: l,
241
282
  explicitEnvName: e.primitiveEnv ?? null,
242
- requireAppId: !u.VITE_APP_ID
283
+ requireAppId: !f.VITE_APP_ID
243
284
  });
244
285
  } catch (e) {
245
- if (e instanceof b && e.kind === "missing-config" && u.VITE_APP_ID && u.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 = u.VITE_APP_NAME, {};
286
+ 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, {};
246
287
  throw e;
247
288
  }
248
- t = d, r = u.VITE_APP_NAME ?? d.appName;
249
- let f = {
250
- VITE_APP_ID: d.appId,
251
- VITE_API_URL: d.apiUrl,
252
- VITE_WS_URL: d.wsUrl,
253
- VITE_APP_NAME: d.appName
254
- }, p = { "import.meta.env.VITE_PRIMITIVE_ENV": JSON.stringify(d.name) }, m = [];
289
+ if (!(f.VITE_APP_ID && f.VITE_API_URL)) {
290
+ let e = F(s, l);
291
+ R(u, e, p), o = {
292
+ root: l,
293
+ mode: u,
294
+ dir: e
295
+ };
296
+ }
297
+ t = p, r = f.VITE_APP_NAME ?? p.appName;
298
+ let m = {
299
+ VITE_APP_ID: p.appId,
300
+ VITE_API_URL: p.apiUrl,
301
+ VITE_WS_URL: p.wsUrl,
302
+ VITE_APP_NAME: p.appName
303
+ }, h = { "import.meta.env.VITE_PRIMITIVE_ENV": JSON.stringify(p.name) }, g = [];
255
304
  for (let e of k) {
256
- if (u[e] !== void 0) {
257
- m.push(e);
305
+ if (f[e] !== void 0) {
306
+ g.push(e);
258
307
  continue;
259
308
  }
260
- let t = f[e];
261
- t !== void 0 && (p[`import.meta.env.${e}`] = JSON.stringify(t), j(e, t));
309
+ let t = m[e];
310
+ t !== void 0 && (h[`import.meta.env.${e}`] = JSON.stringify(t), M(e, t));
262
311
  }
263
- return j("VITE_PRIMITIVE_ENV", d.name), i = m, n = {
264
- apiUrl: u.VITE_API_URL ?? d.apiUrl,
265
- wsUrl: u.VITE_WS_URL ?? d.wsUrl,
266
- appId: u.VITE_APP_ID ?? d.appId,
312
+ return M("VITE_PRIMITIVE_ENV", p.name), i = g, n = {
313
+ apiUrl: f.VITE_API_URL ?? p.apiUrl,
314
+ wsUrl: f.VITE_WS_URL ?? p.wsUrl,
315
+ appId: f.VITE_APP_ID ?? p.appId,
267
316
  appName: r
268
- }, e.logResolved !== !1 && console.log(N(d, n, m)), m.length > 0 && console.warn(`[primitive-env] Overridden by an explicit value in .env or the shell: ${m.join(", ")}. Remove it to use .primitive/config.json (deploys reject these outright).`), { define: p };
317
+ }, 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 };
318
+ },
319
+ configResolved(e) {
320
+ if (!o || !t) return;
321
+ let n = F(e, o.root), r = e?.mode ?? o.mode;
322
+ n === o.dir && r === o.mode || (R(r, n, t), o = {
323
+ root: o.root,
324
+ mode: r,
325
+ dir: n
326
+ });
269
327
  },
270
328
  transformIndexHtml() {
271
329
  if (!a || !t || !n) return [];
@@ -284,17 +342,17 @@ function P(e = {}) {
284
342
  }
285
343
  };
286
344
  }
287
- function F(e, t) {
345
+ function B(e, t) {
288
346
  let n = [
289
347
  ".env",
290
348
  ".env.local",
291
349
  `.env.${e}`,
292
350
  `.env.${e}.local`
293
351
  ], r = {};
294
- for (let e of n) Object.assign(r, I(s(t, e)));
352
+ for (let e of n) Object.assign(r, V(s(t, e)));
295
353
  return r;
296
354
  }
297
- function I(n) {
355
+ function V(n) {
298
356
  if (!e(n)) return {};
299
357
  let r = {};
300
358
  for (let e of t(n, "utf-8").split(/\r?\n/)) {
@@ -302,10 +360,10 @@ function I(n) {
302
360
  if (!t || t.startsWith("#")) continue;
303
361
  let n = t.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
304
362
  if (!n) continue;
305
- let i = n[2].trim();
306
- i = i.startsWith("\"") && i.endsWith("\"") && i.length > 1 || i.startsWith("'") && i.endsWith("'") && i.length > 1 ? i.slice(1, -1) : i.replace(/\s+#.*$/, "").trim(), r[n[1]] = i;
363
+ let i = n[2].trim(), a = i[0], o = a === "\"" || a === "'" || a === "`" ? i.indexOf(a, 1) : -1;
364
+ r[n[1]] = o === -1 ? i.replace(/\s+#.*$/, "").trim() : i.slice(1, o);
307
365
  }
308
366
  return r;
309
367
  }
310
368
  //#endregion
311
- export { b as PrimitiveEnvError, m as primitiveDevTools, P as primitiveEnv, M as resolvePrimitiveEnv };
369
+ export { b as PrimitiveEnvError, m as primitiveDevTools, z as primitiveEnv, N as resolvePrimitiveEnv };
@@ -19,6 +19,14 @@
19
19
  * the sole environment. That order lives in ONE place: the generated copy of
20
20
  * the CLI's resolver core sitting beside this file.
21
21
  *
22
+ * ── pairing a mode with an environment ────────────────────────────────────
23
+ * The vite mode and the Primitive environment are independent axes. An app
24
+ * whose `.env.<mode>` carries backend-coupled values can pin the combination
25
+ * by declaring `VITE_EXPECTED_PRIMITIVE_ENV` in that file; a run that resolves
26
+ * a different environment stops here, at config time — which covers `vite
27
+ * dev`, `vite build`, the deploy's build, and the headless vitest suite alike.
28
+ * Absent declaration: the axes stay fully independent.
29
+ *
22
30
  * ── the escape hatch ──────────────────────────────────────────────────────
23
31
  * An explicit `VITE_*` identity key in a `.env` file or the process
24
32
  * environment still wins, with a warning naming it. That keeps pure-env CI
@@ -77,5 +85,13 @@ export declare function primitiveEnv(options?: PrimitiveEnvOptions): PrimitiveEn
77
85
  * build and the deploy can never disagree about what a `.env` file says.
78
86
  */
79
87
  export declare function loadDotEnv(mode: string, root: string): Record<string, string>;
80
- /** Parses one `.env` file. Missing file → no keys. */
88
+ /**
89
+ * Parses one `.env` file. Missing file → no keys.
90
+ *
91
+ * Quoting follows what Vite's own loader does, because a value read
92
+ * differently here than there is exactly the kind of disagreement this plugin
93
+ * exists to remove: a quoted value ends at its closing quote — a `#` inside it
94
+ * is content, and anything after it on the line is a comment — while an
95
+ * unquoted value ends at the first inline comment.
96
+ */
81
97
  export declare function parseDotEnvFile(path: string): Record<string, string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primitive-app",
3
- "version": "3.1.0-alpha.17",
3
+ "version": "3.1.0-alpha.18",
4
4
  "license": "UNLICENSED",
5
5
  "author": "Primitive LLC",
6
6
  "type": "module",