primitive-app 3.2.0-alpha.16 → 3.2.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,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=`.primitive`,m=`config.json`,h=`local.json`,g=`primitive/config.json`,_=`.primitive/local.json`;function v(e,t){return`Found ${e} but no ${t}: this project still uses the retired layout under ${p}/, which is never read.`}function y(e){return`No ${g} found in ${e} or any parent directory. Primitive commands run inside a Primitive project: it is where the CLI reads the environment, the app that environment names, and the configuration tree from. Run 'primitive init' to create one.`}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 another Primitive 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=(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`,v(e,(0,a.join)((0,a.dirname)((0,a.dirname)(e)),f,m)),e);return(0,t.existsSync)(e)?e:null}let r=(0,a.resolve)(e.cwd??process.cwd());for(;;){let e=(0,a.join)(r,f,m);if((0,t.existsSync)(e))return e;let n=(0,a.join)(r,p,m);if((0,t.existsSync)(n))throw new b(`stale-layout`,v(n,e),n);let i=(0,a.dirname)(r);if(i===r)return null;r=i}}function E(e){let t=(0,a.dirname)(e);return(0,a.basename)(t)===`primitive`?(0,a.dirname)(t):t}function D(e){return(0,a.join)(E(e),p,h)}function O(e){return e instanceof Error?e.message:String(e)}function k(e){if(!(0,t.existsSync)(e))throw new b(`missing-config`,y((0,a.dirname)(e)),e);let n;try{n=(0,t.readFileSync)(e,`utf-8`)}catch(t){throw new b(`malformed-config`,`Failed to read ${g} (${e}): ${O(t)}`,e)}let r;try{r=JSON.parse(n)}catch(t){throw new b(`malformed-config`,`Failed to parse ${g} (${e}): ${O(t)}`,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 o={};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);o[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:o}}function A(e){let n=D(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(O(e))}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??T({cwd:e.cwd,env:t});if(!n)throw new b(`missing-config`,y(e.cwd??process.cwd()));let r=E(n),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:D(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;
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`;function v(e,t){return`Found ${e} but no ${t}: this project still uses the retired layout under ${p}/, which is never read.`}function y(e){return`No ${g} found in ${e} or any parent directory. Primitive commands run inside a Primitive project: it is where the CLI reads the environment, the app that environment names, and the configuration tree from. Run 'primitive init' to create one.`}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 another Primitive 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=(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`,v(e,(0,a.join)((0,a.dirname)((0,a.dirname)(e)),f,m)),e);return(0,t.existsSync)(e)?e:null}let r=(0,a.resolve)(e.cwd??process.cwd());for(;;){let e=(0,a.join)(r,f,m);if((0,t.existsSync)(e))return e;let n=(0,a.join)(r,p,m);if((0,t.existsSync)(n))throw new b(`stale-layout`,v(n,e),n);let i=(0,a.dirname)(r);if(i===r)return null;r=i}}function E(e){let t=(0,a.dirname)(e);return(0,a.basename)(t)===`primitive`?(0,a.dirname)(t):t}function D(e){return(0,a.join)(E(e),p,h)}function O(e){return e instanceof Error?e.message:String(e)}function k(e){if(!(0,t.existsSync)(e))throw new b(`missing-config`,y((0,a.dirname)(e)),e);let n;try{n=(0,t.readFileSync)(e,`utf-8`)}catch(t){throw new b(`malformed-config`,`Failed to read ${g} (${e}): ${O(t)}`,e)}let r;try{r=JSON.parse(n)}catch(t){throw new b(`malformed-config`,`Failed to parse ${g} (${e}): ${O(t)}`,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 o={};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);o[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:o}}var A=/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;function j(e,n){let r=D(e);if(!(0,t.existsSync)(r))return{selectedEnvironment:null,environments:{}};let i=e=>{throw new b(`corrupt-local-state`,`${_} (${r}) is unreadable: ${e}. Delete the file or re-run 'primitive env use <name>'.`,r)},a;try{a=JSON.parse((0,t.readFileSync)(r,`utf-8`))}catch(e){return i(O(e))}if(!a||typeof a!=`object`||Array.isArray(a))return i(`expected a JSON object`);let o=a,s=null,c=o.selectedEnvironment;if(c!=null&&c!==``){if(typeof c!=`string`)return i(`"selectedEnvironment" must be a string`);s=c}return{selectedEnvironment:s,environments:M(o.environments,r,n)}}function M(e,t,n){let r={};if(e===void 0)return r;if(!e||typeof e!=`object`||Array.isArray(e))throw new b(`corrupt-local-state`,`${_} (${t}) is unreadable: "environments" must be an object keyed by environment name. Fix or remove "environments" in ${_}.`,t);let i=(e,n)=>{throw new b(`corrupt-local-state`,`${_} (${t}): machine-local environment "${e}" ${n}. Fix or remove the "${e}" entry under "environments" in ${_}.`,t)},a=n?Object.keys(n.environments):null;for(let[t,n]of Object.entries(e)){A.test(t)||i(t,`is not a valid environment name (letters, digits, dashes and underscores, starting with a letter or digit)`),(!n||typeof n!=`object`||Array.isArray(n))&&i(t,`must be an object`);let e=n;(typeof e.apiUrl!=`string`||!e.apiUrl.trim())&&i(t,`has no "apiUrl" string`);let o=C(e.appId);o||i(t,`has no "appId" naming the app it selects`),(typeof e.tree!=`string`||!e.tree.trim())&&i(t,`has no "tree" naming the committed environment whose configuration tree it uses`);let s=e.tree.trim();a&&(a.includes(t)&&i(t,`has the name of a committed environment in ${g}; a machine-local environment needs a name of its own`),a.includes(s)||i(t,`has "tree": "${s}", which is not an environment in ${g} (available: ${a.join(`, `)||`(none)`})`)),r[t]={apiUrl:e.apiUrl.trim().replace(/\/$/,``),appId:o,appName:typeof e.appName==`string`?e.appName:void 0,tree:s}}return r}function N(e,t){return Object.prototype.hasOwnProperty.call(e.environments,t)}function P(e,t){let n=Object.keys(e.environments),r=Object.keys(t.localEnvironments??{}),i=[...n,...r.map(e=>`${e} (local)`)].join(`, `)||`(none)`,a=t.configPath?`${g} (${t.configPath})`:g,o=(n,o,s,c=!1)=>{if(!N(e,n)&&(c||!r.includes(n)))throw new b(`unknown-environment`,`Environment "${n}" is not defined in ${a} (selected ${s}). Available: ${i}`,t.configPath);return{name:n,source:o}};if(t.explicitEnvName)return o(t.explicitEnvName,`explicit`,`explicitly`);if(t.envVarName)return o(t.envVarName,`env-var`,`via PRIMITIVE_ENV`);if(t.localSelection)return o(t.localSelection,`local`,`in ${_}`);if(e.defaultEnvironment)return o(e.defaultEnvironment,`default`,`as "defaultEnvironment"`,!0);if(n.length===1)return{name:n[0],source:`sole`};throw n.length===0?new b(`no-selection`,`${a} 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 ${a}, or export PRIMITIVE_ENV. Available: ${i}`,t.configPath)}function F(e={}){let t=e.env??process.env,n=e.configPath??T({cwd:e.cwd,env:t});if(!n)throw new b(`missing-config`,y(e.cwd??process.cwd()));let r=E(n),i=k(n),a=j(n,i),{name:o,source:s}=P(i,{explicitEnvName:e.explicitEnvName||null,envVarName:t.PRIMITIVE_ENV||null,localSelection:a.selectedEnvironment,localEnvironments:a.environments,configPath:n}),c={name:o,source:s,configPath:n,localStatePath:D(n),projectRoot:r},l=N(i,o)?void 0:a.environments[o];if(l)return{...c,apiUrl:l.apiUrl,wsUrl:w(l.apiUrl),appId:l.appId,appName:l.appName,webUrl:void 0,description:void 0,tree:l.tree,local:!0};let u=i.environments[o];return{...c,apiUrl:u.apiUrl,wsUrl:w(u.apiUrl),appId:u.appId,appName:u.appName,webUrl:u.webUrl,description:u.description,tree:o,local:!1}}var I=[`VITE_APP_ID`,`VITE_API_URL`,`VITE_WS_URL`,`VITE_APP_NAME`],L=`VITE_EXPECTED_PRIMITIVE_ENV`,R=new Map;function z(e,t){let n=process.env[e];n!==void 0&&n!==``&&R.get(e)!==n||(process.env[e]=t,R.set(e,t))}function B(e={}){return F({cwd:e.root,env:e.env,explicitEnvName:e.primitiveEnv??null})}function V(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 H(e,t){let n=e?.envDir;return n===!1?null:typeof n==`string`&&n!==``?(0,a.resolve)(t,n):t}function U(e,t){let n=process.env[L];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=J((0,a.join)(t,n))[L];if(e!==void 0)return{value:e,where:n}}return null}function W(e,t,n){let r=n.local?`"${n.name}" (machine-local, tree "${n.tree}")`:`"${n.name}"`;return[`[primitive-env] Wrong pair: vite mode "${e}" is declared to run against Primitive environment "${t.value}", but ${r} resolved.`,`[primitive-env] expected: ${t.value} (${L} in ${t.where}; a machine-local environment whose tree is ${t.value} also pairs)`,`[primitive-env] resolved: ${n.name} (selected: ${n.source}${n.local?`; machine-local, tree ${n.tree}`:``})`,`[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 ${L} 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 G(e,t,n){let r=U(e,t);if(r&&r.value!==``&&r.value!==n.name&&!(n.local&&r.value===n.tree))throw Error(W(e,r,n))}function K(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=q(u,l),f={};for(let e of I){let t=process.env[e],n=t!==void 0&&R.get(e)!==t?t:d[e];n!==void 0&&n!==``&&(f[e]=n)}let p;try{p=F({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=H(s,l);G(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 I){if(f[e]!==void 0){g.push(e);continue}let t=m[e];t!==void 0&&(h[`import.meta.env.${e}`]=JSON.stringify(t),z(e,t))}return z(`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(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}},configResolved(e){if(!o||!t)return;let n=H(e,o.root),r=e?.mode??o.mode;n===o.dir&&r===o.mode||(G(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 q(e,t){let n=[`.env`,`.env.local`,`.env.${e}`,`.env.${e}.local`],r={};for(let e of n)Object.assign(r,J((0,a.join)(t,e)));return r}function J(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=K,exports.resolvePrimitiveEnv=B;
@@ -166,85 +166,139 @@ function M(n) {
166
166
  environments: s
167
167
  };
168
168
  }
169
- function N(n) {
170
- let r = A(n);
171
- if (!e(r)) return null;
172
- let i = (e) => {
173
- throw new C("corrupt-local-state", `${b} (${r}) is unreadable: ${e}. Delete the file or re-run 'primitive env use <name>'.`, r);
174
- }, a;
169
+ var N = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
170
+ function P(n, r) {
171
+ let i = A(n);
172
+ if (!e(i)) return {
173
+ selectedEnvironment: null,
174
+ environments: {}
175
+ };
176
+ let a = (e) => {
177
+ throw new C("corrupt-local-state", `${b} (${i}) is unreadable: ${e}. Delete the file or re-run 'primitive env use <name>'.`, i);
178
+ }, o;
175
179
  try {
176
- a = JSON.parse(t(r, "utf-8"));
180
+ o = JSON.parse(t(i, "utf-8"));
177
181
  } catch (e) {
178
- return i(j(e));
182
+ return a(j(e));
183
+ }
184
+ if (!o || typeof o != "object" || Array.isArray(o)) return a("expected a JSON object");
185
+ let s = o, c = null, l = s.selectedEnvironment;
186
+ if (l != null && l !== "") {
187
+ if (typeof l != "string") return a("\"selectedEnvironment\" must be a string");
188
+ c = l;
179
189
  }
180
- if (!a || typeof a != "object" || Array.isArray(a)) return i("expected a JSON object");
181
- let o = a.selectedEnvironment;
182
- return o == null || o === "" ? null : typeof o == "string" ? o : i("\"selectedEnvironment\" must be a string");
190
+ return {
191
+ selectedEnvironment: c,
192
+ environments: F(s.environments, i, r)
193
+ };
194
+ }
195
+ function F(e, t, n) {
196
+ let r = {};
197
+ if (e === void 0) return r;
198
+ if (!e || typeof e != "object" || Array.isArray(e)) throw new C("corrupt-local-state", `${b} (${t}) is unreadable: "environments" must be an object keyed by environment name. Fix or remove "environments" in ${b}.`, t);
199
+ let i = (e, n) => {
200
+ throw new C("corrupt-local-state", `${b} (${t}): machine-local environment "${e}" ${n}. Fix or remove the "${e}" entry under "environments" in ${b}.`, t);
201
+ }, a = n ? Object.keys(n.environments) : null;
202
+ for (let [t, n] of Object.entries(e)) {
203
+ N.test(t) || i(t, "is not a valid environment name (letters, digits, dashes and underscores, starting with a letter or digit)"), (!n || typeof n != "object" || Array.isArray(n)) && i(t, "must be an object");
204
+ let e = n;
205
+ (typeof e.apiUrl != "string" || !e.apiUrl.trim()) && i(t, "has no \"apiUrl\" string");
206
+ let o = E(e.appId);
207
+ o || i(t, "has no \"appId\" naming the app it selects"), (typeof e.tree != "string" || !e.tree.trim()) && i(t, "has no \"tree\" naming the committed environment whose configuration tree it uses");
208
+ let s = e.tree.trim();
209
+ a && (a.includes(t) && i(t, `has the name of a committed environment in ${y}; a machine-local environment needs a name of its own`), a.includes(s) || i(t, `has "tree": "${s}", which is not an environment in ${y} (available: ${a.join(", ") || "(none)"})`)), r[t] = {
210
+ apiUrl: e.apiUrl.trim().replace(/\/$/, ""),
211
+ appId: o,
212
+ appName: typeof e.appName == "string" ? e.appName : void 0,
213
+ tree: s
214
+ };
215
+ }
216
+ return r;
183
217
  }
184
- function P(e, t) {
185
- let n = Object.keys(e.environments), r = n.join(", ") || "(none)", i = t.configPath ? `${y} (${t.configPath})` : y, a = (n, a, o) => {
186
- if (!e.environments[n]) throw new C("unknown-environment", `Environment "${n}" is not defined in ${i} (selected ${o}). Available: ${r}`, t.configPath);
218
+ function I(e, t) {
219
+ return Object.prototype.hasOwnProperty.call(e.environments, t);
220
+ }
221
+ function L(e, t) {
222
+ let n = Object.keys(e.environments), r = Object.keys(t.localEnvironments ?? {}), i = [...n, ...r.map((e) => `${e} (local)`)].join(", ") || "(none)", a = t.configPath ? `${y} (${t.configPath})` : y, o = (n, o, s, c = !1) => {
223
+ if (!I(e, n) && (c || !r.includes(n))) throw new C("unknown-environment", `Environment "${n}" is not defined in ${a} (selected ${s}). Available: ${i}`, t.configPath);
187
224
  return {
188
225
  name: n,
189
- source: a
226
+ source: o
190
227
  };
191
228
  };
192
- if (t.explicitEnvName) return a(t.explicitEnvName, "explicit", "explicitly");
193
- if (t.envVarName) return a(t.envVarName, "env-var", "via PRIMITIVE_ENV");
194
- if (t.localSelection) return a(t.localSelection, "local", `in ${b}`);
195
- if (e.defaultEnvironment) return a(e.defaultEnvironment, "default", "as \"defaultEnvironment\"");
229
+ if (t.explicitEnvName) return o(t.explicitEnvName, "explicit", "explicitly");
230
+ if (t.envVarName) return o(t.envVarName, "env-var", "via PRIMITIVE_ENV");
231
+ if (t.localSelection) return o(t.localSelection, "local", `in ${b}`);
232
+ if (e.defaultEnvironment) return o(e.defaultEnvironment, "default", "as \"defaultEnvironment\"", !0);
196
233
  if (n.length === 1) return {
197
234
  name: n[0],
198
235
  source: "sole"
199
236
  };
200
- 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);
237
+ throw n.length === 0 ? new C("no-selection", `${a} 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 ${a}, or export PRIMITIVE_ENV. Available: ${i}`, t.configPath);
201
238
  }
202
- function F(e = {}) {
239
+ function R(e = {}) {
203
240
  let t = e.env ?? process.env, n = e.configPath ?? O({
204
241
  cwd: e.cwd,
205
242
  env: t
206
243
  });
207
244
  if (!n) throw new C("missing-config", S(e.cwd ?? process.cwd()));
208
- let r = k(n), i = M(n), { name: a, source: o } = P(i, {
245
+ let r = k(n), i = M(n), a = P(n, i), { name: o, source: s } = L(i, {
209
246
  explicitEnvName: e.explicitEnvName || null,
210
247
  envVarName: t.PRIMITIVE_ENV || null,
211
- localSelection: N(n),
248
+ localSelection: a.selectedEnvironment,
249
+ localEnvironments: a.environments,
212
250
  configPath: n
213
- }), s = i.environments[a];
214
- return {
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,
251
+ }), c = {
252
+ name: o,
253
+ source: s,
223
254
  configPath: n,
224
255
  localStatePath: A(n),
225
256
  projectRoot: r
257
+ }, l = I(i, o) ? void 0 : a.environments[o];
258
+ if (l) return {
259
+ ...c,
260
+ apiUrl: l.apiUrl,
261
+ wsUrl: D(l.apiUrl),
262
+ appId: l.appId,
263
+ appName: l.appName,
264
+ webUrl: void 0,
265
+ description: void 0,
266
+ tree: l.tree,
267
+ local: !0
268
+ };
269
+ let u = i.environments[o];
270
+ return {
271
+ ...c,
272
+ apiUrl: u.apiUrl,
273
+ wsUrl: D(u.apiUrl),
274
+ appId: u.appId,
275
+ appName: u.appName,
276
+ webUrl: u.webUrl,
277
+ description: u.description,
278
+ tree: o,
279
+ local: !1
226
280
  };
227
281
  }
228
282
  //#endregion
229
283
  //#region src/dev-tools/vite-plugin/primitive-env.ts
230
- var I = [
284
+ var z = [
231
285
  "VITE_APP_ID",
232
286
  "VITE_API_URL",
233
287
  "VITE_WS_URL",
234
288
  "VITE_APP_NAME"
235
- ], L = "VITE_EXPECTED_PRIMITIVE_ENV", R = /* @__PURE__ */ new Map();
236
- function z(e, t) {
289
+ ], B = "VITE_EXPECTED_PRIMITIVE_ENV", V = /* @__PURE__ */ new Map();
290
+ function H(e, t) {
237
291
  let n = process.env[e];
238
- n !== void 0 && n !== "" && R.get(e) !== n || (process.env[e] = t, R.set(e, t));
292
+ n !== void 0 && n !== "" && V.get(e) !== n || (process.env[e] = t, V.set(e, t));
239
293
  }
240
- function B(e = {}) {
241
- return F({
294
+ function U(e = {}) {
295
+ return R({
242
296
  cwd: e.root,
243
297
  env: e.env,
244
298
  explicitEnvName: e.primitiveEnv ?? null
245
299
  });
246
300
  }
247
- function V(e, t, n) {
301
+ function W(e, t, n) {
248
302
  let r = (e) => n.includes(e) ? " (overridden)" : "", i = [
249
303
  `[primitive-env] Primitive environment: ${e.name} (selected: ${e.source})`,
250
304
  `[primitive-env] apiUrl: ${t.apiUrl}${r("VITE_API_URL")}`,
@@ -255,12 +309,12 @@ function V(e, t, n) {
255
309
  ];
256
310
  return n.length > 0 && i.push(`[primitive-env] overridden by the environment: ${n.join(", ")}`), i.join("\n");
257
311
  }
258
- function H(e, t) {
312
+ function G(e, t) {
259
313
  let n = e?.envDir;
260
314
  return n === !1 ? null : typeof n == "string" && n !== "" ? c(t, n) : t;
261
315
  }
262
- function U(e, t) {
263
- let n = process.env[L];
316
+ function K(e, t) {
317
+ let n = process.env[B];
264
318
  if (n !== void 0 && n !== "") return {
265
319
  value: n,
266
320
  where: "the process environment"
@@ -272,7 +326,7 @@ function U(e, t) {
272
326
  ".env.local",
273
327
  ".env"
274
328
  ]) {
275
- let e = J(s(t, n))[L];
329
+ let e = Z(s(t, n))[B];
276
330
  if (e !== void 0) return {
277
331
  value: e,
278
332
  where: n
@@ -280,37 +334,38 @@ function U(e, t) {
280
334
  }
281
335
  return null;
282
336
  }
283
- function W(e, t, n) {
337
+ function q(e, t, n) {
338
+ let r = n.local ? `"${n.name}" (machine-local, tree "${n.tree}")` : `"${n.name}"`;
284
339
  return [
285
- `[primitive-env] Wrong pair: vite mode "${e}" is declared to run against Primitive environment "${t.value}", but "${n.name}" resolved.`,
286
- `[primitive-env] expected: ${t.value} (${L} in ${t.where})`,
287
- `[primitive-env] resolved: ${n.name} (selected: ${n.source})`,
340
+ `[primitive-env] Wrong pair: vite mode "${e}" is declared to run against Primitive environment "${t.value}", but ${r} resolved.`,
341
+ `[primitive-env] expected: ${t.value} (${B} in ${t.where}; a machine-local environment whose tree is ${t.value} also pairs)`,
342
+ `[primitive-env] resolved: ${n.name} (selected: ${n.source}${n.local ? `; machine-local, tree ${n.tree}` : ""})`,
288
343
  `[primitive-env] config: ${n.configPath}`,
289
344
  "",
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}.`,
345
+ `Run the pair you meant — PRIMITIVE_ENV=${t.value} <command> --mode ${e}, or 'primitive env use ${t.value}' — or change/remove ${B} in ${t.where}.`,
291
346
  "",
292
347
  `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.`
293
348
  ].join("\n");
294
349
  }
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));
350
+ function J(e, t, n) {
351
+ let r = K(e, t);
352
+ if (r && r.value !== "" && r.value !== n.name && !(n.local && r.value === n.tree)) throw Error(q(e, r, n));
298
353
  }
299
- function K(e = {}) {
354
+ function Y(e = {}) {
300
355
  let t = null, n = null, r, i = [], a = !1, o = null;
301
356
  return {
302
357
  name: "primitive-env",
303
358
  enforce: "pre",
304
359
  config(s, c) {
305
360
  a = c?.command === "serve", o = null;
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];
361
+ let l = s?.root ?? process.cwd(), u = c?.mode ?? "development", d = X(u, l), f = {};
362
+ for (let e of z) {
363
+ let t = process.env[e], n = t !== void 0 && V.get(e) !== t ? t : d[e];
309
364
  n !== void 0 && n !== "" && (f[e] = n);
310
365
  }
311
366
  let p;
312
367
  try {
313
- p = F({
368
+ p = R({
314
369
  cwd: l,
315
370
  explicitEnvName: e.primitiveEnv ?? null
316
371
  });
@@ -319,8 +374,8 @@ function K(e = {}) {
319
374
  throw e;
320
375
  }
321
376
  if (!(f.VITE_APP_ID && f.VITE_API_URL)) {
322
- let e = H(s, l);
323
- G(u, e, p), o = {
377
+ let e = G(s, l);
378
+ J(u, e, p), o = {
324
379
  root: l,
325
380
  mode: u,
326
381
  dir: e
@@ -333,25 +388,25 @@ function K(e = {}) {
333
388
  VITE_WS_URL: p.wsUrl,
334
389
  VITE_APP_NAME: p.appName
335
390
  }, h = { "import.meta.env.VITE_PRIMITIVE_ENV": JSON.stringify(p.name) }, g = [];
336
- for (let e of I) {
391
+ for (let e of z) {
337
392
  if (f[e] !== void 0) {
338
393
  g.push(e);
339
394
  continue;
340
395
  }
341
396
  let t = m[e];
342
- t !== void 0 && (h[`import.meta.env.${e}`] = JSON.stringify(t), z(e, t));
397
+ t !== void 0 && (h[`import.meta.env.${e}`] = JSON.stringify(t), H(e, t));
343
398
  }
344
- return z("VITE_PRIMITIVE_ENV", p.name), i = g, n = {
399
+ return H("VITE_PRIMITIVE_ENV", p.name), i = g, n = {
345
400
  apiUrl: f.VITE_API_URL ?? p.apiUrl,
346
401
  wsUrl: f.VITE_WS_URL ?? p.wsUrl,
347
402
  appId: f.VITE_APP_ID ?? p.appId,
348
403
  appName: r
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 };
404
+ }, e.logResolved !== !1 && console.log(W(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 };
350
405
  },
351
406
  configResolved(e) {
352
407
  if (!o || !t) return;
353
- let n = H(e, o.root), r = e?.mode ?? o.mode;
354
- n === o.dir && r === o.mode || (G(r, n, t), o = {
408
+ let n = G(e, o.root), r = e?.mode ?? o.mode;
409
+ n === o.dir && r === o.mode || (J(r, n, t), o = {
355
410
  root: o.root,
356
411
  mode: r,
357
412
  dir: n
@@ -374,17 +429,17 @@ function K(e = {}) {
374
429
  }
375
430
  };
376
431
  }
377
- function q(e, t) {
432
+ function X(e, t) {
378
433
  let n = [
379
434
  ".env",
380
435
  ".env.local",
381
436
  `.env.${e}`,
382
437
  `.env.${e}.local`
383
438
  ], r = {};
384
- for (let e of n) Object.assign(r, J(s(t, e)));
439
+ for (let e of n) Object.assign(r, Z(s(t, e)));
385
440
  return r;
386
441
  }
387
- function J(n) {
442
+ function Z(n) {
388
443
  if (!e(n)) return {};
389
444
  let r = {};
390
445
  for (let e of t(n, "utf-8").split(/\r?\n/)) {
@@ -398,4 +453,4 @@ function J(n) {
398
453
  return r;
399
454
  }
400
455
  //#endregion
401
- export { C as PrimitiveEnvError, m as primitiveDevTools, K as primitiveEnv, B as resolvePrimitiveEnv };
456
+ export { C as PrimitiveEnvError, m as primitiveDevTools, Y as primitiveEnv, U as resolvePrimitiveEnv };