primitive-app 3.2.0-alpha.1 → 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;
@@ -33,7 +52,12 @@ export declare class PrimitiveEnvError extends Error {
33
52
  }
34
53
  export interface CoreEnvironmentEntry {
35
54
  apiUrl: string;
36
- appId?: string;
55
+ /**
56
+ * The one app this environment names (#3152). Required: selecting an
57
+ * environment selects exactly one app, so there is no per-machine "current
58
+ * app" for a second reader to disagree with.
59
+ */
60
+ appId: string;
37
61
  appName?: string;
38
62
  /**
39
63
  * The app's web counterpart for this environment, as a normalized origin
@@ -75,6 +99,21 @@ export interface CoreEnvironmentEntry {
75
99
  * two agree.
76
100
  */
77
101
  export declare function normalizeWebOrigin(value: unknown): string | undefined;
102
+ /**
103
+ * The one remedy for an environment that names no app (#3152), shared by every
104
+ * reader that can hit it: this core, the CLI's `validateProjectConfig`, the
105
+ * Swift template's pre-build script, and the Vue template's cf-deploy.
106
+ *
107
+ * Two constraints on the wording, both test-asserted:
108
+ *
109
+ * - it never says `primitive env add <name>`, which REFUSES an environment
110
+ * name that already exists — the shape this error is always about;
111
+ * - it names no command the failure itself blocks. The CLI validates the
112
+ * project config before any command parses, so every `primitive` command
113
+ * run inside the broken project fails with this same error. The only
114
+ * invocation it may offer is one it says out loud to run elsewhere.
115
+ */
116
+ export declare function missingAppIdMessage(name: string, configPath?: string): string;
78
117
  export interface CoreProjectConfig {
79
118
  version: number;
80
119
  defaultEnvironment?: string;
@@ -87,7 +126,8 @@ export interface ResolvedPrimitiveEnv {
87
126
  apiUrl: string;
88
127
  /** Derived from apiUrl by scheme swap — never authored separately. */
89
128
  wsUrl: string;
90
- appId?: string;
129
+ /** The app this environment names. Always present — see #3152. */
130
+ appId: string;
91
131
  appName?: string;
92
132
  /** The app's web counterpart for the SELECTED environment, if it has one. */
93
133
  webUrl?: string;
@@ -106,11 +146,6 @@ export interface ResolveOptions {
106
146
  explicitEnvName?: string | null;
107
147
  /** Use this config file instead of discovering one. */
108
148
  configPath?: string;
109
- /**
110
- * Identity consumers (the Vite plugin, cf-deploy, the Swift script) cannot
111
- * work without an app ID; plain CLI resolution can, and always could.
112
- */
113
- requireAppId?: boolean;
114
149
  }
115
150
  /**
116
151
  * Derives the WebSocket URL from the API URL. Every apiUrl/wsUrl pair in
@@ -119,22 +154,35 @@ export interface ResolveOptions {
119
154
  */
120
155
  export declare function deriveWsUrl(apiUrl: string): string;
121
156
  /**
122
- * 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
123
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.
124
170
  */
125
171
  export declare function findPrimitiveConfigPath(options?: {
126
172
  cwd?: string;
127
173
  env?: Record<string, string | undefined>;
128
174
  }): string | null;
129
175
  /**
130
- * 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
131
177
  * containing directory when the config was pointed at directly (a fixture via
132
- * PRIMITIVE_PROJECT_CONFIG, which is not inside a `.primitive/` directory).
178
+ * PRIMITIVE_PROJECT_CONFIG, which is not inside a `primitive/` directory).
133
179
  */
134
180
  export declare function projectRootForConfigPath(configPath: string): string;
135
181
  /**
136
- * Local state lives beside the config it belongs to, so a fixture-pointed
137
- * 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.
138
186
  */
139
187
  export declare function localStatePathForConfigPath(configPath: string): string;
140
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){return e.startsWith(`https://`)?`wss://`+e.slice(8):e.startsWith(`http://`)?`ws://`+e.slice(7):e}function b(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 x(e){let t=(0,a.dirname)(e);return(0,a.basename)(t)===`.primitive`?(0,a.dirname)(t):t}function S(e){return(0,a.join)((0,a.dirname)(e),m)}function C(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,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 w(e){let n=S(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 T(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 E(e={}){let t=e.env??process.env,n=e.configPath??b({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=C(n),{name:i,source:a}=T(r,{explicitEnvName:e.explicitEnvName||null,envVarName:t.PRIMITIVE_ENV||null,localSelection:w(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:y(o.apiUrl),appId:o.appId,appName:o.appName,webUrl:o.webUrl,description:o.description,source:a,configPath:n,localStatePath:S(n),projectRoot:x(n)}}var D=[`VITE_APP_ID`,`VITE_API_URL`,`VITE_WS_URL`,`VITE_APP_NAME`],O=`VITE_EXPECTED_PRIMITIVE_ENV`,k=new Map;function A(e,t){let n=process.env[e];n!==void 0&&n!==``&&k.get(e)!==n||(process.env[e]=t,k.set(e,t))}function j(e={}){return E({cwd:e.root,env:e.env,explicitEnvName:e.primitiveEnv??null,requireAppId:e.requireAppId})}function M(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 N(e,t){let n=e?.envDir;return n===!1?null:typeof n==`string`&&n!==``?(0,a.resolve)(t,n):t}function P(e,t){let n=process.env[O];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=z((0,a.join)(t,n))[O];if(e!==void 0)return{value:e,where:n}}return null}function F(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} (${O} 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 ${O} 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 I(e,t,n){let r=P(e,t);if(r&&r.value!==``&&r.value!==n.name)throw Error(F(e,r,n))}function L(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=R(u,l),f={};for(let e of D){let t=process.env[e],n=t!==void 0&&k.get(e)!==t?t:d[e];n!==void 0&&n!==``&&(f[e]=n)}let p;try{p=E({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=N(s,l);I(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 D){if(f[e]!==void 0){g.push(e);continue}let t=m[e];t!==void 0&&(h[`import.meta.env.${e}`]=JSON.stringify(t),A(e,t))}return A(`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(M(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=N(e,o.root),r=e?.mode??o.mode;n===o.dir&&r===o.mode||(I(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 R(e,t){let n=[`.env`,`.env.local`,`.env.${e}`,`.env.${e}.local`],r={};for(let e of n)Object.assign(r,z((0,a.join)(t,e)));return r}function z(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=L,exports.resolvePrimitiveEnv=j;
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,60 +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) {
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.`;
91
+ }
92
+ function E(e) {
93
+ if (typeof e == "string") return e.trim() || void 0;
94
+ }
95
+ function D(e) {
86
96
  return e.startsWith("https://") ? "wss://" + e.slice(8) : e.startsWith("http://") ? "ws://" + e.slice(7) : e;
87
97
  }
88
- function C(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 = {}) {
89
105
  let n = (t.env ?? process.env).PRIMITIVE_PROJECT_CONFIG;
90
106
  if (n) {
91
107
  let t = c(n);
92
- 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;
93
110
  }
94
111
  let r = c(t.cwd ?? process.cwd());
95
112
  for (;;) {
96
- let t = s(r, h, g);
97
- if (e(t)) return t;
113
+ let t = s(r, h, _);
114
+ if (O(r), e(t)) return t;
98
115
  let n = o(r);
99
116
  if (n === r) return null;
100
117
  r = n;
101
118
  }
102
119
  }
103
- function w(e) {
120
+ function A(e) {
104
121
  let t = o(e);
105
- return a(t) === ".primitive" ? o(t) : t;
122
+ return a(t) === "primitive" ? o(t) : t;
106
123
  }
107
- function T(e) {
108
- return s(o(e), _);
124
+ function j(e) {
125
+ return s(A(e), g, v);
109
126
  }
110
- function E(n) {
111
- 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);
112
129
  let r;
113
130
  try {
114
131
  r = t(n, "utf-8");
115
132
  } catch (e) {
116
- 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);
117
134
  }
118
135
  let i;
119
136
  try {
120
137
  i = JSON.parse(r);
121
138
  } catch (e) {
122
- 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);
123
140
  }
124
- 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);
125
142
  let a = i;
126
- if (typeof a.version != "number" || !Number.isInteger(a.version)) throw new b("malformed-config", `${v} (${n}) is missing required integer field "version".`, n);
127
- if (a.version !== 1) throw new b("unsupported-version", `${v} (${n}) has version ${a.version}, but this tool understands version 1.`, n);
128
- 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);
129
146
  let o = {};
130
147
  for (let [e, t] of Object.entries(a.environments)) {
131
- 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);
132
149
  let r = t;
133
- 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);
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);
134
153
  o[e] = {
135
154
  apiUrl: r.apiUrl.replace(/\/$/, ""),
136
- appId: typeof r.appId == "string" ? r.appId : void 0,
155
+ appId: i,
137
156
  appName: typeof r.appName == "string" ? r.appName : void 0,
138
- webUrl: x(r.webUrl),
157
+ webUrl: w(r.webUrl),
139
158
  description: typeof r.description == "string" ? r.description : void 0
140
159
  };
141
160
  }
@@ -145,11 +164,11 @@ function E(n) {
145
164
  environments: o
146
165
  };
147
166
  }
148
- function D(n) {
149
- let r = T(n);
167
+ function N(n) {
168
+ let r = j(n);
150
169
  if (!e(r)) return null;
151
170
  let i = (e) => {
152
- 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);
153
172
  }, a;
154
173
  try {
155
174
  a = JSON.parse(t(r, "utf-8"));
@@ -160,9 +179,9 @@ function D(n) {
160
179
  let o = a.selectedEnvironment;
161
180
  return o == null || o === "" ? null : typeof o == "string" ? o : i("\"selectedEnvironment\" must be a string");
162
181
  }
163
- function O(e, t) {
164
- let n = Object.keys(e.environments), r = n.join(", ") || "(none)", i = t.configPath ? `${v} (${t.configPath})` : v, a = (n, a, o) => {
165
- 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);
166
185
  return {
167
186
  name: n,
168
187
  source: a
@@ -170,62 +189,62 @@ function O(e, t) {
170
189
  };
171
190
  if (t.explicitEnvName) return a(t.explicitEnvName, "explicit", "explicitly");
172
191
  if (t.envVarName) return a(t.envVarName, "env-var", "via PRIMITIVE_ENV");
173
- if (t.localSelection) return a(t.localSelection, "local", `in ${y}`);
192
+ if (t.localSelection) return a(t.localSelection, "local", `in ${b}`);
174
193
  if (e.defaultEnvironment) return a(e.defaultEnvironment, "default", "as \"defaultEnvironment\"");
175
194
  if (n.length === 1) return {
176
195
  name: n[0],
177
196
  source: "sole"
178
197
  };
179
- 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);
180
199
  }
181
- function k(e = {}) {
182
- let t = e.env ?? process.env, n = e.configPath ?? C({
200
+ function F(e = {}) {
201
+ let t = e.env ?? process.env, n = e.configPath ?? k({
183
202
  cwd: e.cwd,
184
203
  env: t
185
204
  });
186
- 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.`);
187
- let r = E(n), { name: i, source: a } = O(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, {
188
209
  explicitEnvName: e.explicitEnvName || null,
189
210
  envVarName: t.PRIMITIVE_ENV || null,
190
- localSelection: D(n),
211
+ localSelection: N(n),
191
212
  configPath: n
192
- }), o = r.environments[i];
193
- if (e.requireAppId && !o.appId) throw new b("missing-app-id", `Environment "${i}" in ${v} (${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);
213
+ }), s = i.environments[a];
194
214
  return {
195
- name: i,
196
- apiUrl: o.apiUrl,
197
- wsUrl: S(o.apiUrl),
198
- appId: o.appId,
199
- appName: o.appName,
200
- webUrl: o.webUrl,
201
- description: o.description,
202
- 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,
203
223
  configPath: n,
204
- localStatePath: T(n),
205
- projectRoot: w(n)
224
+ localStatePath: j(n),
225
+ projectRoot: r
206
226
  };
207
227
  }
208
228
  //#endregion
209
229
  //#region src/dev-tools/vite-plugin/primitive-env.ts
210
- var A = [
230
+ var I = [
211
231
  "VITE_APP_ID",
212
232
  "VITE_API_URL",
213
233
  "VITE_WS_URL",
214
234
  "VITE_APP_NAME"
215
- ], j = "VITE_EXPECTED_PRIMITIVE_ENV", M = /* @__PURE__ */ new Map();
216
- function N(e, t) {
235
+ ], L = "VITE_EXPECTED_PRIMITIVE_ENV", R = /* @__PURE__ */ new Map();
236
+ function z(e, t) {
217
237
  let n = process.env[e];
218
- n !== void 0 && n !== "" && M.get(e) !== n || (process.env[e] = t, M.set(e, t));
238
+ n !== void 0 && n !== "" && R.get(e) !== n || (process.env[e] = t, R.set(e, t));
219
239
  }
220
- function P(e = {}) {
221
- return k({
240
+ function B(e = {}) {
241
+ return F({
222
242
  cwd: e.root,
223
243
  env: e.env,
224
- explicitEnvName: e.primitiveEnv ?? null,
225
- requireAppId: e.requireAppId
244
+ explicitEnvName: e.primitiveEnv ?? null
226
245
  });
227
246
  }
228
- function F(e, t, n) {
247
+ function V(e, t, n) {
229
248
  let r = (e) => n.includes(e) ? " (overridden)" : "", i = [
230
249
  `[primitive-env] Primitive environment: ${e.name} (selected: ${e.source})`,
231
250
  `[primitive-env] apiUrl: ${t.apiUrl}${r("VITE_API_URL")}`,
@@ -236,12 +255,12 @@ function F(e, t, n) {
236
255
  ];
237
256
  return n.length > 0 && i.push(`[primitive-env] overridden by the environment: ${n.join(", ")}`), i.join("\n");
238
257
  }
239
- function I(e, t) {
258
+ function H(e, t) {
240
259
  let n = e?.envDir;
241
260
  return n === !1 ? null : typeof n == "string" && n !== "" ? c(t, n) : t;
242
261
  }
243
- function L(e, t) {
244
- let n = process.env[j];
262
+ function U(e, t) {
263
+ let n = process.env[L];
245
264
  if (n !== void 0 && n !== "") return {
246
265
  value: n,
247
266
  where: "the process environment"
@@ -253,7 +272,7 @@ function L(e, t) {
253
272
  ".env.local",
254
273
  ".env"
255
274
  ]) {
256
- let e = H(s(t, n))[j];
275
+ let e = J(s(t, n))[L];
257
276
  if (e !== void 0) return {
258
277
  value: e,
259
278
  where: n
@@ -261,48 +280,47 @@ function L(e, t) {
261
280
  }
262
281
  return null;
263
282
  }
264
- function R(e, t, n) {
283
+ function W(e, t, n) {
265
284
  return [
266
285
  `[primitive-env] Wrong pair: vite mode "${e}" is declared to run against Primitive environment "${t.value}", but "${n.name}" resolved.`,
267
- `[primitive-env] expected: ${t.value} (${j} in ${t.where})`,
286
+ `[primitive-env] expected: ${t.value} (${L} in ${t.where})`,
268
287
  `[primitive-env] resolved: ${n.name} (selected: ${n.source})`,
269
288
  `[primitive-env] config: ${n.configPath}`,
270
289
  "",
271
- `Run the pair you meant — PRIMITIVE_ENV=${t.value} <command> --mode ${e}, or 'primitive env use ${t.value}' — or change/remove ${j} 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}.`,
272
291
  "",
273
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.`
274
293
  ].join("\n");
275
294
  }
276
- function z(e, t, n) {
277
- let r = L(e, t);
278
- if (r && r.value !== "" && r.value !== n.name) throw Error(R(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));
279
298
  }
280
- function B(e = {}) {
299
+ function K(e = {}) {
281
300
  let t = null, n = null, r, i = [], a = !1, o = null;
282
301
  return {
283
302
  name: "primitive-env",
284
303
  enforce: "pre",
285
304
  config(s, c) {
286
305
  a = c?.command === "serve", o = null;
287
- let l = s?.root ?? process.cwd(), u = c?.mode ?? "development", d = V(u, l), f = {};
288
- for (let e of A) {
289
- let t = process.env[e], n = t !== void 0 && M.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];
290
309
  n !== void 0 && n !== "" && (f[e] = n);
291
310
  }
292
311
  let p;
293
312
  try {
294
- p = k({
313
+ p = F({
295
314
  cwd: l,
296
- explicitEnvName: e.primitiveEnv ?? null,
297
- requireAppId: !f.VITE_APP_ID
315
+ explicitEnvName: e.primitiveEnv ?? null
298
316
  });
299
317
  } catch (e) {
300
- 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, {};
301
319
  throw e;
302
320
  }
303
321
  if (!(f.VITE_APP_ID && f.VITE_API_URL)) {
304
- let e = I(s, l);
305
- z(u, e, p), o = {
322
+ let e = H(s, l);
323
+ G(u, e, p), o = {
306
324
  root: l,
307
325
  mode: u,
308
326
  dir: e
@@ -315,25 +333,25 @@ function B(e = {}) {
315
333
  VITE_WS_URL: p.wsUrl,
316
334
  VITE_APP_NAME: p.appName
317
335
  }, h = { "import.meta.env.VITE_PRIMITIVE_ENV": JSON.stringify(p.name) }, g = [];
318
- for (let e of A) {
336
+ for (let e of I) {
319
337
  if (f[e] !== void 0) {
320
338
  g.push(e);
321
339
  continue;
322
340
  }
323
341
  let t = m[e];
324
- t !== void 0 && (h[`import.meta.env.${e}`] = JSON.stringify(t), N(e, t));
342
+ t !== void 0 && (h[`import.meta.env.${e}`] = JSON.stringify(t), z(e, t));
325
343
  }
326
- return N("VITE_PRIMITIVE_ENV", p.name), i = g, n = {
344
+ return z("VITE_PRIMITIVE_ENV", p.name), i = g, n = {
327
345
  apiUrl: f.VITE_API_URL ?? p.apiUrl,
328
346
  wsUrl: f.VITE_WS_URL ?? p.wsUrl,
329
347
  appId: f.VITE_APP_ID ?? p.appId,
330
348
  appName: r
331
- }, e.logResolved !== !1 && console.log(F(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 };
332
350
  },
333
351
  configResolved(e) {
334
352
  if (!o || !t) return;
335
- let n = I(e, o.root), r = e?.mode ?? o.mode;
336
- n === o.dir && r === o.mode || (z(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 = {
337
355
  root: o.root,
338
356
  mode: r,
339
357
  dir: n
@@ -356,17 +374,17 @@ function B(e = {}) {
356
374
  }
357
375
  };
358
376
  }
359
- function V(e, t) {
377
+ function q(e, t) {
360
378
  let n = [
361
379
  ".env",
362
380
  ".env.local",
363
381
  `.env.${e}`,
364
382
  `.env.${e}.local`
365
383
  ], r = {};
366
- for (let e of n) Object.assign(r, H(s(t, e)));
384
+ for (let e of n) Object.assign(r, J(s(t, e)));
367
385
  return r;
368
386
  }
369
- function H(n) {
387
+ function J(n) {
370
388
  if (!e(n)) return {};
371
389
  let r = {};
372
390
  for (let e of t(n, "utf-8").split(/\r?\n/)) {
@@ -380,4 +398,4 @@ function H(n) {
380
398
  return r;
381
399
  }
382
400
  //#endregion
383
- export { b as PrimitiveEnvError, m as primitiveDevTools, B as primitiveEnv, P 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:
@@ -45,8 +45,6 @@ export interface ResolvePrimitiveEnvOptions {
45
45
  env?: Record<string, string | undefined>;
46
46
  /** An explicit environment name (e.g. from `--primitive-env`). */
47
47
  primitiveEnv?: string | null;
48
- /** Fail when the resolved environment has no `appId`. */
49
- requireAppId?: boolean;
50
48
  }
51
49
  /**
52
50
  * Resolves the Primitive environment for a project. A thin wrapper over the