@olonjs/cli 3.0.83 → 3.0.84

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.
@@ -596,7 +596,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
596
596
  "@tiptap/extension-link": "^2.11.5",
597
597
  "@tiptap/react": "^2.11.5",
598
598
  "@tiptap/starter-kit": "^2.11.5",
599
- "@olonjs/core": "^1.0.71",
599
+ "@olonjs/core": "^1.0.72",
600
600
  "clsx": "^2.1.1",
601
601
  "lucide-react": "^0.474.0",
602
602
  "react": "^19.0.0",
@@ -1780,102 +1780,6 @@ function App() {
1780
1780
  export default App;
1781
1781
 
1782
1782
 
1783
- END_OF_FILE_CONTENT
1784
- # SKIP: src/App.tsx:Zone.Identifier is binary and cannot be embedded as text.
1785
- echo "Creating src/App_.tsx..."
1786
- cat << 'END_OF_FILE_CONTENT' > "src/App_.tsx"
1787
- import { useState, useEffect } from 'react';
1788
- import { JsonPagesEngine } from '@jsonpages/core';
1789
- import type { LibraryImageEntry } from '@jsonpages/core';
1790
- import { ComponentRegistry } from '@/lib/ComponentRegistry';
1791
- import { SECTION_SCHEMAS } from '@/lib/schemas';
1792
- import { addSectionConfig } from '@/lib/addSectionConfig';
1793
- import { getHydratedData } from '@/lib/draftStorage';
1794
- import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
1795
- import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
1796
-
1797
- import siteData from '@/data/config/site.json';
1798
- import themeData from '@/data/config/theme.json';
1799
- import menuData from '@/data/config/menu.json';
1800
- import { getFilePages } from '@/lib/getFilePages';
1801
-
1802
- import fontsCss from './fonts.css?inline';
1803
- import tenantCss from './index.css?inline';
1804
-
1805
- const themeConfig = themeData as unknown as ThemeConfig;
1806
- const menuConfig = menuData as unknown as MenuConfig;
1807
- const TENANT_ID = 'alpha';
1808
-
1809
- const filePages = getFilePages();
1810
- const fileSiteConfig = siteData as unknown as SiteConfig;
1811
- const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
1812
-
1813
- function getInitialData() {
1814
- return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
1815
- }
1816
-
1817
- function App() {
1818
- const [{ pages, siteConfig }] = useState(getInitialData);
1819
- const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
1820
-
1821
- useEffect(() => {
1822
- fetch('/api/list-assets')
1823
- .then((r) => (r.ok ? r.json() : []))
1824
- .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
1825
- .catch(() => setAssetsManifest([]));
1826
- }, []);
1827
-
1828
- const config: JsonPagesConfig = {
1829
- tenantId: TENANT_ID,
1830
- registry: ComponentRegistry as JsonPagesConfig['registry'],
1831
- schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
1832
- pages,
1833
- siteConfig,
1834
- themeConfig,
1835
- menuConfig,
1836
- themeCss: { tenant: fontsCss + '\n' + tenantCss },
1837
- addSection: addSectionConfig,
1838
- persistence: {
1839
- async saveToFile(state: ProjectState, slug: string): Promise<void> {
1840
- const res = await fetch('/api/save-to-file', {
1841
- method: 'POST',
1842
- headers: { 'Content-Type': 'application/json' },
1843
- body: JSON.stringify({ projectState: state, slug }),
1844
- });
1845
- const body = (await res.json().catch(() => ({}))) as { error?: string };
1846
- if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
1847
- },
1848
- },
1849
- assets: {
1850
- assetsBaseUrl: '/assets',
1851
- assetsManifest,
1852
- async onAssetUpload(file: File): Promise<string> {
1853
- if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
1854
- if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
1855
- const base64 = await new Promise<string>((resolve, reject) => {
1856
- const reader = new FileReader();
1857
- reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
1858
- reader.onerror = () => reject(reader.error);
1859
- reader.readAsDataURL(file);
1860
- });
1861
- const res = await fetch('/api/upload-asset', {
1862
- method: 'POST',
1863
- headers: { 'Content-Type': 'application/json' },
1864
- body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
1865
- });
1866
- const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
1867
- if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
1868
- if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
1869
- return body.url;
1870
- },
1871
- },
1872
- };
1873
-
1874
- return <JsonPagesEngine config={config} />;
1875
- }
1876
-
1877
- export default App;
1878
-
1879
1783
  END_OF_FILE_CONTENT
1880
1784
  mkdir -p "src/components"
1881
1785
  echo "Creating src/components/NotFound.tsx..."
@@ -9394,24 +9298,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
9394
9298
 
9395
9299
 
9396
9300
 
9397
- END_OF_FILE_CONTENT
9398
- echo "Creating src/main_.tsx..."
9399
- cat << 'END_OF_FILE_CONTENT' > "src/main_.tsx"
9400
- import '@/types'; // TBP: load type augmentation from capsule-driven types
9401
- import React from 'react';
9402
- import ReactDOM from 'react-dom/client';
9403
- import App from './App';
9404
- // ... resto del file
9405
-
9406
- ReactDOM.createRoot(document.getElementById('root')!).render(
9407
- <React.StrictMode>
9408
- <App />
9409
- </React.StrictMode>
9410
- );
9411
-
9412
-
9413
-
9414
-
9415
9301
  END_OF_FILE_CONTENT
9416
9302
  # SKIP: src/registry-types.ts is binary and cannot be embedded as text.
9417
9303
  mkdir -p "src/server"
@@ -596,7 +596,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
596
596
  "@tiptap/extension-link": "^2.11.5",
597
597
  "@tiptap/react": "^2.11.5",
598
598
  "@tiptap/starter-kit": "^2.11.5",
599
- "@olonjs/core": "^1.0.71",
599
+ "@olonjs/core": "^1.0.72",
600
600
  "clsx": "^2.1.1",
601
601
  "lucide-react": "^0.474.0",
602
602
  "react": "^19.0.0",
@@ -1144,264 +1144,6 @@ function App() {
1144
1144
 
1145
1145
  export default App;
1146
1146
 
1147
- END_OF_FILE_CONTENT
1148
- echo "Creating src/App_.tsx..."
1149
- cat << 'END_OF_FILE_CONTENT' > "src/App_.tsx"
1150
- import { useState, useEffect } from 'react';
1151
- import { JsonPagesEngine } from '@jsonpages/core';
1152
- import type { LibraryImageEntry } from '@jsonpages/core';
1153
- import { ComponentRegistry } from '@/lib/ComponentRegistry';
1154
- import { SECTION_SCHEMAS } from '@/lib/schemas';
1155
- import { addSectionConfig } from '@/lib/addSectionConfig';
1156
- import { getHydratedData } from '@/lib/draftStorage';
1157
- import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
1158
- import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
1159
-
1160
- import siteData from '@/data/config/site.json';
1161
- import themeData from '@/data/config/theme.json';
1162
- import menuData from '@/data/config/menu.json';
1163
- import { getFilePages } from '@/lib/getFilePages';
1164
-
1165
- import fontsCss from './fonts.css?inline';
1166
- import tenantCss from './index.css?inline';
1167
-
1168
- const themeConfig = themeData as unknown as ThemeConfig;
1169
- const menuConfig = menuData as unknown as MenuConfig;
1170
- const TENANT_ID = 'alpha';
1171
-
1172
- const filePages = getFilePages();
1173
- const fileSiteConfig = siteData as unknown as SiteConfig;
1174
- const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
1175
-
1176
- function getInitialData() {
1177
- return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
1178
- }
1179
-
1180
- function App() {
1181
- const [{ pages, siteConfig }] = useState(getInitialData);
1182
- const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
1183
-
1184
- useEffect(() => {
1185
- fetch('/api/list-assets')
1186
- .then((r) => (r.ok ? r.json() : []))
1187
- .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
1188
- .catch(() => setAssetsManifest([]));
1189
- }, []);
1190
-
1191
- const config: JsonPagesConfig = {
1192
- tenantId: TENANT_ID,
1193
- registry: ComponentRegistry as JsonPagesConfig['registry'],
1194
- schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
1195
- pages,
1196
- siteConfig,
1197
- themeConfig,
1198
- menuConfig,
1199
- themeCss: { tenant: fontsCss + '\n' + tenantCss },
1200
- addSection: addSectionConfig,
1201
- persistence: {
1202
- async saveToFile(state: ProjectState, slug: string): Promise<void> {
1203
- const res = await fetch('/api/save-to-file', {
1204
- method: 'POST',
1205
- headers: { 'Content-Type': 'application/json' },
1206
- body: JSON.stringify({ projectState: state, slug }),
1207
- });
1208
- const body = (await res.json().catch(() => ({}))) as { error?: string };
1209
- if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
1210
- },
1211
- },
1212
- assets: {
1213
- assetsBaseUrl: '/assets',
1214
- assetsManifest,
1215
- async onAssetUpload(file: File): Promise<string> {
1216
- if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
1217
- if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
1218
- const base64 = await new Promise<string>((resolve, reject) => {
1219
- const reader = new FileReader();
1220
- reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
1221
- reader.onerror = () => reject(reader.error);
1222
- reader.readAsDataURL(file);
1223
- });
1224
- const res = await fetch('/api/upload-asset', {
1225
- method: 'POST',
1226
- headers: { 'Content-Type': 'application/json' },
1227
- body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
1228
- });
1229
- const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
1230
- if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
1231
- if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
1232
- return body.url;
1233
- },
1234
- },
1235
- };
1236
-
1237
- return <JsonPagesEngine config={config} />;
1238
- }
1239
-
1240
- export default App;
1241
-
1242
- END_OF_FILE_CONTENT
1243
- echo "Creating src/_App.tsx..."
1244
- cat << 'END_OF_FILE_CONTENT' > "src/_App.tsx"
1245
- /**
1246
- * Thin Entry Point (Tenant).
1247
- * Data from getHydratedData (file-backed or draft); assets from public/assets/images.
1248
- * Supports Hybrid Persistence: Local Filesystem (Dev) or Cloud Bridge (Prod).
1249
- */
1250
- import { useState, useEffect } from 'react';
1251
- import { JsonPagesEngine } from '@jsonpages/core';
1252
- import type { LibraryImageEntry } from '@jsonpages/core';
1253
- import { ComponentRegistry } from '@/lib/ComponentRegistry';
1254
- import { SECTION_SCHEMAS } from '@/lib/schemas';
1255
- import { addSectionConfig } from '@/lib/addSectionConfig';
1256
- import { getHydratedData } from '@/lib/draftStorage';
1257
- import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
1258
- import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
1259
-
1260
- import siteData from '@/data/config/site.json';
1261
- import themeData from '@/data/config/theme.json';
1262
- import menuData from '@/data/config/menu.json';
1263
- import { getFilePages } from '@/lib/getFilePages';
1264
-
1265
- import fontsCss from './fonts.css?inline';
1266
- import tenantCss from './index.css?inline';
1267
-
1268
- // Cloud Configuration (Injected by Vercel/Netlify Env Vars)
1269
- const CLOUD_API_URL = import.meta.env.VITE_JSONPAGES_CLOUD_URL;
1270
- const CLOUD_API_KEY = import.meta.env.VITE_JSONPAGES_API_KEY;
1271
-
1272
- const themeConfig = themeData as unknown as ThemeConfig;
1273
- const menuConfig = menuData as unknown as MenuConfig;
1274
- const TENANT_ID = 'alpha';
1275
-
1276
- const filePages = getFilePages();
1277
- const fileSiteConfig = siteData as unknown as SiteConfig;
1278
- const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
1279
-
1280
- function getInitialData() {
1281
- return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
1282
- }
1283
-
1284
- function App() {
1285
- const [{ pages, siteConfig }] = useState(getInitialData);
1286
- const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
1287
-
1288
- useEffect(() => {
1289
- // In Cloud mode, listing assets might be different or disabled for MVP
1290
- // For now, we keep the local fetch which will fail gracefully on Vercel (404)
1291
- fetch('/api/list-assets')
1292
- .then((r) => (r.ok ? r.json() : []))
1293
- .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
1294
- .catch(() => setAssetsManifest([]));
1295
- }, []);
1296
-
1297
- console.log("🔍 DEBUG ENV:", {
1298
- URL: import.meta.env.VITE_JSONPAGES_CLOUD_URL,
1299
- KEY: import.meta.env.VITE_JSONPAGES_API_KEY ? "PRESENT" : "MISSING"
1300
- });
1301
- const config: JsonPagesConfig = {
1302
- tenantId: TENANT_ID,
1303
- registry: ComponentRegistry as JsonPagesConfig['registry'],
1304
- schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
1305
- pages,
1306
- siteConfig,
1307
- themeConfig,
1308
- menuConfig,
1309
- themeCss: { tenant: fontsCss + '\n' + tenantCss },
1310
- addSection: addSectionConfig,
1311
- persistence: {
1312
- async saveToFile(state: ProjectState, slug: string): Promise<void> {
1313
-
1314
- // ☁️ SCENARIO A: CLOUD BRIDGE (Production)
1315
- if (CLOUD_API_URL && CLOUD_API_KEY) {
1316
- console.log(`☁️ Saving ${slug} via Cloud Bridge...`);
1317
-
1318
- const res = await fetch(`${CLOUD_API_URL}/save`, {
1319
- method: 'POST',
1320
- headers: {
1321
- 'Content-Type': 'application/json',
1322
- 'Authorization': `Bearer ${CLOUD_API_KEY}`
1323
- },
1324
- body: JSON.stringify({
1325
- // Mapping logical slug to physical path in repo
1326
- path: `src/data/pages/${slug}.json`,
1327
- // We save the page config specifically
1328
- content: state.page,
1329
- message: `Content update for ${slug} via Visual Editor`
1330
- }),
1331
- });
1332
-
1333
- if (!res.ok) {
1334
- const err = await res.json().catch(() => ({}));
1335
- throw new Error(err.error || `Cloud save failed: ${res.status}`);
1336
- }
1337
- return;
1338
- }
1339
-
1340
- // 💻 SCENARIO B: LOCAL FILESYSTEM (Development)
1341
- console.log(`💻 Saving ${slug} to Local Filesystem...`);
1342
- const res = await fetch('/api/save-to-file', {
1343
- method: 'POST',
1344
- headers: { 'Content-Type': 'application/json' },
1345
- body: JSON.stringify({ projectState: state, slug }),
1346
- });
1347
-
1348
- const body = (await res.json().catch(() => ({}))) as { error?: string };
1349
- if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
1350
- },
1351
- },
1352
- assets: {
1353
- assetsBaseUrl: '/assets',
1354
- assetsManifest,
1355
- async onAssetUpload(file: File): Promise<string> {
1356
- // Note: Asset upload in Cloud Mode requires the R2 Bridge (Next Step in Roadmap)
1357
- // For now, this works in Local Mode.
1358
- if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
1359
- if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
1360
-
1361
- const base64 = await new Promise<string>((resolve, reject) => {
1362
- const reader = new FileReader();
1363
- reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
1364
- reader.onerror = () => reject(reader.error);
1365
- reader.readAsDataURL(file);
1366
- });
1367
-
1368
- const res = await fetch('/api/upload-asset', {
1369
- method: 'POST',
1370
- headers: { 'Content-Type': 'application/json' },
1371
- body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
1372
- });
1373
-
1374
- const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
1375
- if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
1376
- if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
1377
- return body.url;
1378
- },
1379
- },
1380
- };
1381
-
1382
- return <JsonPagesEngine config={config} />;
1383
- }
1384
-
1385
- export default App;
1386
-
1387
- END_OF_FILE_CONTENT
1388
- echo "Creating src/_main.tsx..."
1389
- cat << 'END_OF_FILE_CONTENT' > "src/_main.tsx"
1390
- import '@/types'; // TBP: load type augmentation from capsule-driven types
1391
- import React from 'react';
1392
- import ReactDOM from 'react-dom/client';
1393
- import App from './App';
1394
- // ... resto del file
1395
-
1396
- ReactDOM.createRoot(document.getElementById('root')!).render(
1397
- <React.StrictMode>
1398
- <App />
1399
- </React.StrictMode>
1400
- );
1401
-
1402
-
1403
-
1404
-
1405
1147
  END_OF_FILE_CONTENT
1406
1148
  mkdir -p "src/components"
1407
1149
  echo "Creating src/components/NotFound.tsx..."
@@ -10933,24 +10675,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
10933
10675
 
10934
10676
 
10935
10677
 
10936
- END_OF_FILE_CONTENT
10937
- echo "Creating src/main_.tsx..."
10938
- cat << 'END_OF_FILE_CONTENT' > "src/main_.tsx"
10939
- import '@/types'; // TBP: load type augmentation from capsule-driven types
10940
- import React from 'react';
10941
- import ReactDOM from 'react-dom/client';
10942
- import App from './App';
10943
- // ... resto del file
10944
-
10945
- ReactDOM.createRoot(document.getElementById('root')!).render(
10946
- <React.StrictMode>
10947
- <App />
10948
- </React.StrictMode>
10949
- );
10950
-
10951
-
10952
-
10953
-
10954
10678
  END_OF_FILE_CONTENT
10955
10679
  # SKIP: src/registry-types.ts is binary and cannot be embedded as text.
10956
10680
  mkdir -p "src/server"
@@ -596,7 +596,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
596
596
  "@tiptap/extension-link": "^2.11.5",
597
597
  "@tiptap/react": "^2.11.5",
598
598
  "@tiptap/starter-kit": "^2.11.5",
599
- "@olonjs/core": "^1.0.71",
599
+ "@olonjs/core": "^1.0.72",
600
600
  "clsx": "^2.1.1",
601
601
  "lucide-react": "^0.474.0",
602
602
  "react": "^19.0.0",
@@ -1780,102 +1780,6 @@ function App() {
1780
1780
  export default App;
1781
1781
 
1782
1782
 
1783
- END_OF_FILE_CONTENT
1784
- # SKIP: src/App.tsx:Zone.Identifier is binary and cannot be embedded as text.
1785
- echo "Creating src/App_.tsx..."
1786
- cat << 'END_OF_FILE_CONTENT' > "src/App_.tsx"
1787
- import { useState, useEffect } from 'react';
1788
- import { JsonPagesEngine } from '@jsonpages/core';
1789
- import type { LibraryImageEntry } from '@jsonpages/core';
1790
- import { ComponentRegistry } from '@/lib/ComponentRegistry';
1791
- import { SECTION_SCHEMAS } from '@/lib/schemas';
1792
- import { addSectionConfig } from '@/lib/addSectionConfig';
1793
- import { getHydratedData } from '@/lib/draftStorage';
1794
- import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
1795
- import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
1796
-
1797
- import siteData from '@/data/config/site.json';
1798
- import themeData from '@/data/config/theme.json';
1799
- import menuData from '@/data/config/menu.json';
1800
- import { getFilePages } from '@/lib/getFilePages';
1801
-
1802
- import fontsCss from './fonts.css?inline';
1803
- import tenantCss from './index.css?inline';
1804
-
1805
- const themeConfig = themeData as unknown as ThemeConfig;
1806
- const menuConfig = menuData as unknown as MenuConfig;
1807
- const TENANT_ID = 'alpha';
1808
-
1809
- const filePages = getFilePages();
1810
- const fileSiteConfig = siteData as unknown as SiteConfig;
1811
- const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
1812
-
1813
- function getInitialData() {
1814
- return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
1815
- }
1816
-
1817
- function App() {
1818
- const [{ pages, siteConfig }] = useState(getInitialData);
1819
- const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
1820
-
1821
- useEffect(() => {
1822
- fetch('/api/list-assets')
1823
- .then((r) => (r.ok ? r.json() : []))
1824
- .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
1825
- .catch(() => setAssetsManifest([]));
1826
- }, []);
1827
-
1828
- const config: JsonPagesConfig = {
1829
- tenantId: TENANT_ID,
1830
- registry: ComponentRegistry as JsonPagesConfig['registry'],
1831
- schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
1832
- pages,
1833
- siteConfig,
1834
- themeConfig,
1835
- menuConfig,
1836
- themeCss: { tenant: fontsCss + '\n' + tenantCss },
1837
- addSection: addSectionConfig,
1838
- persistence: {
1839
- async saveToFile(state: ProjectState, slug: string): Promise<void> {
1840
- const res = await fetch('/api/save-to-file', {
1841
- method: 'POST',
1842
- headers: { 'Content-Type': 'application/json' },
1843
- body: JSON.stringify({ projectState: state, slug }),
1844
- });
1845
- const body = (await res.json().catch(() => ({}))) as { error?: string };
1846
- if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
1847
- },
1848
- },
1849
- assets: {
1850
- assetsBaseUrl: '/assets',
1851
- assetsManifest,
1852
- async onAssetUpload(file: File): Promise<string> {
1853
- if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
1854
- if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
1855
- const base64 = await new Promise<string>((resolve, reject) => {
1856
- const reader = new FileReader();
1857
- reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
1858
- reader.onerror = () => reject(reader.error);
1859
- reader.readAsDataURL(file);
1860
- });
1861
- const res = await fetch('/api/upload-asset', {
1862
- method: 'POST',
1863
- headers: { 'Content-Type': 'application/json' },
1864
- body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
1865
- });
1866
- const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
1867
- if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
1868
- if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
1869
- return body.url;
1870
- },
1871
- },
1872
- };
1873
-
1874
- return <JsonPagesEngine config={config} />;
1875
- }
1876
-
1877
- export default App;
1878
-
1879
1783
  END_OF_FILE_CONTENT
1880
1784
  mkdir -p "src/components"
1881
1785
  echo "Creating src/components/NotFound.tsx..."
@@ -9394,24 +9298,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
9394
9298
 
9395
9299
 
9396
9300
 
9397
- END_OF_FILE_CONTENT
9398
- echo "Creating src/main_.tsx..."
9399
- cat << 'END_OF_FILE_CONTENT' > "src/main_.tsx"
9400
- import '@/types'; // TBP: load type augmentation from capsule-driven types
9401
- import React from 'react';
9402
- import ReactDOM from 'react-dom/client';
9403
- import App from './App';
9404
- // ... resto del file
9405
-
9406
- ReactDOM.createRoot(document.getElementById('root')!).render(
9407
- <React.StrictMode>
9408
- <App />
9409
- </React.StrictMode>
9410
- );
9411
-
9412
-
9413
-
9414
-
9415
9301
  END_OF_FILE_CONTENT
9416
9302
  # SKIP: src/registry-types.ts is binary and cannot be embedded as text.
9417
9303
  mkdir -p "src/server"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olonjs/cli",
3
- "version": "3.0.83",
3
+ "version": "3.0.84",
4
4
  "description": "The Sovereign CLI Engine for OlonJS.",
5
5
  "type": "module",
6
6
  "bin": {