bosia 0.8.15 → 0.8.16

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.
package/README.md CHANGED
@@ -18,7 +18,7 @@ File-based routing inspired by SvelteKit, built on top of the Bun runtime and El
18
18
  - **Dev server with HMR** — file watcher + SSE browser reload, no page blink
19
19
  - **Tailwind CSS v4** — compiled at build time, shadcn-inspired design tokens out of the box
20
20
  - **CLI** — `bosia create`, `bosia dev`, `bosia build`, `bosia add`, `bosia feat`
21
- - **UI registry** — shadcn-style, copy-paste source you own: **60 components**, **100+ blocks**, **10 pages**, **19 themes**
21
+ - **UI registry** — shadcn-style, copy-paste source you own: **61 components**, **162 blocks**, **23 pages**, **19 themes**
22
22
 
23
23
  ## UI Registry
24
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bosia",
3
- "version": "0.8.15",
3
+ "version": "0.8.16",
4
4
  "type": "module",
5
5
  "description": "A fast, batteries-included fullstack framework — SSR · Svelte 5 Runes · Bun · ElysiaJS. File-based routing No Node.js, no Vite, no adapters.",
6
6
  "keywords": [
package/src/core/env.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync } from "fs";
1
+ import { existsSync, readdirSync, readFileSync } from "fs";
2
2
  import { join } from "path";
3
3
 
4
4
  // ─── Framework-reserved vars ─────────────────────────────
@@ -26,6 +26,9 @@ const FRAMEWORK_VARS = new Set([
26
26
  /** Valid JS/TS identifier: starts with letter/underscore, then alphanumeric/underscore. */
27
27
  const VALID_ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
28
28
 
29
+ /** `.env`, `.env.production`, `.env.example`, … — but not `.envrc` or other `.env`-prefixed files. */
30
+ const ENV_FILE_RE = /^\.env(\..+)?$/;
31
+
29
32
  /** Process escape sequences in double-quoted values. */
30
33
  function processEscapes(raw: string): string {
31
34
  return raw.replace(/\\(.)/g, (_, ch) => {
@@ -119,9 +122,13 @@ export function parseEnvFile(content: string, filename?: string): Record<string,
119
122
  * 3. .env.[mode]
120
123
  * 4. .env.[mode].local
121
124
  *
125
+ * Variable *names* additionally come from every other `.env*` file present
126
+ * (`.env.production`, `.env.example`, …) with an empty value, so the `$env`
127
+ * export surface is the same in dev, prod and CI.
128
+ *
122
129
  * @param mode "development" | "production"
123
130
  * @param dir directory to look in (defaults to cwd)
124
- * @returns merged env record (only vars from .env files, excluding framework vars)
131
+ * @returns merged env record (only vars declared in .env files, excluding framework vars)
125
132
  */
126
133
  export function loadEnv(mode: string, dir?: string): Record<string, string> {
127
134
  const root = dir ?? process.cwd();
@@ -143,12 +150,24 @@ export function loadEnv(mode: string, dir?: string): Record<string, string> {
143
150
  console.log(`✓ Loaded ${loaded.join(", ")}`);
144
151
  }
145
152
 
153
+ // Names come from every `.env*` present, values only from the mode's files.
154
+ // Keeps the $env export surface identical in dev, prod and CI — mode decides
155
+ // only which names carry a value. A malformed off-mode file throws here
156
+ // rather than at deploy time.
157
+ const declaredNames = new Set(Object.keys(merged));
158
+ for (const entry of readdirSync(root, { withFileTypes: true })) {
159
+ if (!entry.isFile() || !ENV_FILE_RE.test(entry.name) || files.includes(entry.name)) continue;
160
+ const parsed = parseEnvFile(readFileSync(join(root, entry.name), "utf-8"), entry.name);
161
+ for (const key of Object.keys(parsed)) declaredNames.add(key);
162
+ }
163
+
146
164
  // Track declared keys so html.ts only exposes .env-declared PUBLIC_* vars
147
- for (const key of Object.keys(merged)) {
165
+ for (const key of declaredNames) {
148
166
  _declaredKeys.add(key);
149
167
  }
150
168
 
151
- // Apply to process.env — system env wins (don't overwrite existing)
169
+ // Apply to process.env — system env wins (don't overwrite existing).
170
+ // Name-only keys stay absent so `process.env.X ?? default` still falls back.
152
171
  for (const [key, value] of Object.entries(merged)) {
153
172
  if (!(key in process.env)) {
154
173
  process.env[key] = value;
@@ -157,9 +176,9 @@ export function loadEnv(mode: string, dir?: string): Record<string, string> {
157
176
 
158
177
  // Return only non-framework vars
159
178
  const result: Record<string, string> = {};
160
- for (const [key, value] of Object.entries(merged)) {
179
+ for (const key of declaredNames) {
161
180
  if (!FRAMEWORK_VARS.has(key)) {
162
- result[key] = process.env[key] ?? value;
181
+ result[key] = process.env[key] ?? merged[key] ?? "";
163
182
  }
164
183
  }
165
184