@olonjs/cli 3.0.139 → 3.0.141

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.
@@ -1649,6 +1649,149 @@ cat << 'END_OF_FILE_CONTENT' > "index.html"
1649
1649
  </html>
1650
1650
 
1651
1651
 
1652
+ END_OF_FILE_CONTENT
1653
+ echo "Creating middleware.ts..."
1654
+ cat << 'END_OF_FILE_CONTENT' > "middleware.ts"
1655
+ export const config = {
1656
+ matcher: ['/admin', '/admin/:path*'],
1657
+ };
1658
+
1659
+ const COOKIE_NAME = '__olonjs_admin_session';
1660
+ const COOKIE_MAX_AGE = 3600;
1661
+ const JWT_MAX_AGE_SECONDS = 300;
1662
+
1663
+ function base64urlToBase64(str: string): string {
1664
+ return str.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (str.length % 4)) % 4);
1665
+ }
1666
+
1667
+ function base64ToArrayBuffer(b64: string): ArrayBuffer {
1668
+ const binary = atob(b64);
1669
+ const bytes = new Uint8Array(binary.length);
1670
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
1671
+ return bytes.buffer;
1672
+ }
1673
+
1674
+ function pemToArrayBuffer(pem: string): ArrayBuffer {
1675
+ // Handle both actual newlines and literal \n sequences (common in Vercel env vars)
1676
+ const b64 = pem
1677
+ .replace(/-----BEGIN PUBLIC KEY-----/g, '')
1678
+ .replace(/-----END PUBLIC KEY-----/g, '')
1679
+ .replace(/\\n/g, '')
1680
+ .replace(/\s+/g, '');
1681
+ return base64ToArrayBuffer(b64);
1682
+ }
1683
+
1684
+ async function importPublicKey(pem: string): Promise<CryptoKey> {
1685
+ return crypto.subtle.importKey(
1686
+ 'spki',
1687
+ pemToArrayBuffer(pem),
1688
+ { name: 'ECDSA', namedCurve: 'P-256' },
1689
+ false,
1690
+ ['verify'],
1691
+ );
1692
+ }
1693
+
1694
+ interface JwtPayload {
1695
+ sub?: string;
1696
+ iat?: number;
1697
+ exp?: number;
1698
+ }
1699
+
1700
+ async function verifyAdminJwt(
1701
+ token: string,
1702
+ publicKey: CryptoKey,
1703
+ options: { checkExp: boolean } = { checkExp: true },
1704
+ ): Promise<boolean> {
1705
+ try {
1706
+ const parts = token.split('.');
1707
+ if (parts.length !== 3) return false;
1708
+ const [headerB64, payloadB64, signatureB64] = parts;
1709
+
1710
+ const payload = JSON.parse(atob(base64urlToBase64(payloadB64))) as JwtPayload;
1711
+ if (payload.sub !== 'admin-access') return false;
1712
+
1713
+ if (options.checkExp) {
1714
+ const now = Math.floor(Date.now() / 1000);
1715
+ if (typeof payload.exp !== 'number' || payload.exp < now) return false;
1716
+ if (typeof payload.iat !== 'number' || now - payload.iat > JWT_MAX_AGE_SECONDS) return false;
1717
+ }
1718
+
1719
+ const message = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
1720
+ const signature = base64ToArrayBuffer(base64urlToBase64(signatureB64));
1721
+ return crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, publicKey, signature, message);
1722
+ } catch {
1723
+ return false;
1724
+ }
1725
+ }
1726
+
1727
+ function parseCookies(header: string | null): Record<string, string> {
1728
+ if (!header) return {};
1729
+ return Object.fromEntries(
1730
+ header.split(';').map((c) => {
1731
+ const [k, ...v] = c.trim().split('=');
1732
+ return [k.trim(), decodeURIComponent(v.join('='))];
1733
+ }),
1734
+ );
1735
+ }
1736
+
1737
+ function deny(hint: string): Response {
1738
+ console.error(`[admin-middleware] 401 reason: ${hint}`);
1739
+ return new Response('Unauthorized', { status: 401 });
1740
+ }
1741
+
1742
+ export default async function middleware(request: Request): Promise<Response | undefined> {
1743
+ if (!process.env.VERCEL_ENV) return undefined;
1744
+
1745
+ const publicKeyPem = process.env.ADMIN_PUBLIC_KEY;
1746
+ if (!publicKeyPem) return deny('missing_public_key');
1747
+
1748
+ let publicKey: CryptoKey;
1749
+ try {
1750
+ publicKey = await importPublicKey(publicKeyPem);
1751
+ } catch (e) {
1752
+ return deny(`key_import_failed: ${e instanceof Error ? e.message : String(e)}`);
1753
+ }
1754
+
1755
+ const url = new URL(request.url);
1756
+ const cookies = parseCookies(request.headers.get('Cookie'));
1757
+
1758
+ // 1. Session cookie — signature check only; session lifetime governed by cookie Max-Age.
1759
+ // The JWT stored in the cookie was valid at issue time; exp is intentionally skipped
1760
+ // to allow 1h sessions without requiring the private key in the Edge Runtime.
1761
+ // parseCookies already decodes values — no extra decodeURIComponent needed here
1762
+ const sessionToken = cookies[COOKIE_NAME];
1763
+ if (sessionToken) {
1764
+ const cookieValid = await verifyAdminJwt(sessionToken, publicKey, { checkExp: false });
1765
+ console.error(`[admin-middleware] cookie check: present=${!!sessionToken} valid=${cookieValid}`);
1766
+ if (cookieValid) return undefined;
1767
+ }
1768
+
1769
+ // 2. Bearer token in Authorization header — full validation including exp
1770
+ const authHeader = request.headers.get('Authorization') ?? '';
1771
+ const bearerToken = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null;
1772
+ if (bearerToken && (await verifyAdminJwt(bearerToken, publicKey))) {
1773
+ return undefined;
1774
+ }
1775
+
1776
+ // 3. Token as query param — full validation; set session cookie and redirect to clean URL
1777
+ const queryToken = url.searchParams.get('token');
1778
+ if (queryToken && (await verifyAdminJwt(queryToken, publicKey))) {
1779
+ const cleanUrl = new URL(url.toString());
1780
+ cleanUrl.searchParams.delete('token');
1781
+ return new Response(null, {
1782
+ status: 302,
1783
+ headers: {
1784
+ Location: cleanUrl.toString(),
1785
+ // SameSite=Lax required: navigation originates from a cross-site platform (app.olon.it)
1786
+ // SameSite=Strict would block the cookie in the redirect follow-up request
1787
+ 'Set-Cookie': `${COOKIE_NAME}=${encodeURIComponent(queryToken)}; Path=/admin; HttpOnly; Secure; SameSite=Lax; Max-Age=${COOKIE_MAX_AGE}`,
1788
+ },
1789
+ });
1790
+ }
1791
+
1792
+ return deny('token_invalid');
1793
+ }
1794
+
1652
1795
  END_OF_FILE_CONTENT
1653
1796
  echo "Creating package.json..."
1654
1797
  cat << 'END_OF_FILE_CONTENT' > "package.json"
@@ -1661,9 +1804,9 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
1661
1804
  "dev": "vite",
1662
1805
  "dev:clean": "vite --force",
1663
1806
  "verify:webmcp": "node scripts/webmcp-feature-check.mjs",
1664
- "prebuild": "node scripts/sync-pages-to-public.mjs && node scripts/generate-llms-txt.mjs",
1807
+ "prebuild": "node scripts/sync-pages-to-public.mjs && node scripts/generate-llms-txt.mjs && node scripts/bake.mjs && node scripts/sitemap.mjs && node scripts/robots.mjs",
1665
1808
  "build": "tsc && vite build",
1666
- "dist": "bash ./src2Code.sh --template alpha src .cursor vercel.json components.json index.html tsconfig.json package.json tsconfig.node.json vite.config.ts scripts ",
1809
+ "dist": "bash ./src2Code.sh --template alpha src .cursor vercel.json components.json index.html tsconfig.json package.json tsconfig.node.json vite.config.ts scripts middleware.ts",
1667
1810
  "preview": "vite preview",
1668
1811
  "bake:email": "tsx scripts/bake-email.tsx",
1669
1812
  "bakemail": "npm run bake:email --",
@@ -1674,7 +1817,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
1674
1817
  "@tiptap/extension-link": "^2.11.5",
1675
1818
  "@tiptap/react": "^2.11.5",
1676
1819
  "@tiptap/starter-kit": "^2.11.5",
1677
- "@olonjs/core": "^1.1.3",
1820
+ "@olonjs/core": "^1.1.5",
1678
1821
  "class-variance-authority": "^0.7.1",
1679
1822
  "clsx": "^2.1.1",
1680
1823
  "lucide-react": "^0.474.0",
@@ -2239,6 +2382,149 @@ const outPath = path.join(rootDir, 'public', 'llms.txt');
2239
2382
  fs.writeFileSync(outPath, llmsTxt, 'utf-8');
2240
2383
  console.log('[generate-llms-txt] Written -> public/llms.txt');
2241
2384
 
2385
+ END_OF_FILE_CONTENT
2386
+ echo "Creating scripts/robots.mjs..."
2387
+ cat << 'END_OF_FILE_CONTENT' > "scripts/robots.mjs"
2388
+ import fs from 'fs';
2389
+ import path from 'path';
2390
+ import { fileURLToPath } from 'url';
2391
+
2392
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
2393
+ const rootDir = path.resolve(__dirname, '..');
2394
+
2395
+ const baseUrl = process.env.VERCEL_PROJECT_PRODUCTION_URL
2396
+ ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
2397
+ : 'http://localhost:5173';
2398
+
2399
+ const robotsTxt = `User-agent: *
2400
+ Allow: /
2401
+ Disallow: /api/
2402
+
2403
+ User-agent: GPTBot
2404
+ User-agent: ChatGPT-User
2405
+ User-agent: ClaudeBot
2406
+ User-agent: Claude-Web
2407
+ User-agent: PerplexityBot
2408
+ User-agent: OAI-SearchBot
2409
+ Allow: /
2410
+ Allow: /*.json
2411
+ Allow: /schemas/
2412
+ Allow: /llms.txt
2413
+ Allow: /mcp-manifest.json
2414
+ Disallow: /api/
2415
+
2416
+ Sitemap: ${baseUrl}/sitemap.xml
2417
+ `;
2418
+
2419
+ const outPath = path.join(rootDir, 'public', 'robots.txt');
2420
+ fs.writeFileSync(outPath, robotsTxt, 'utf-8');
2421
+ console.log('[robots] Written -> public/robots.txt');
2422
+
2423
+ END_OF_FILE_CONTENT
2424
+ echo "Creating scripts/sitemap.mjs..."
2425
+ cat << 'END_OF_FILE_CONTENT' > "scripts/sitemap.mjs"
2426
+ import fs from 'fs';
2427
+ import path from 'path';
2428
+ import { fileURLToPath } from 'url';
2429
+
2430
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
2431
+ const rootDir = path.resolve(__dirname, '..');
2432
+
2433
+ const baseUrl = process.env.VERCEL_PROJECT_PRODUCTION_URL
2434
+ ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
2435
+ : 'http://localhost:5173';
2436
+
2437
+ function listJsonFilesRecursive(dir) {
2438
+ const items = fs.readdirSync(dir, { withFileTypes: true });
2439
+ const files = [];
2440
+ for (const item of items) {
2441
+ const fullPath = path.join(dir, item.name);
2442
+ if (item.isDirectory()) {
2443
+ files.push(...listJsonFilesRecursive(fullPath));
2444
+ continue;
2445
+ }
2446
+ if (item.isFile() && item.name.toLowerCase().endsWith('.json')) files.push(fullPath);
2447
+ }
2448
+ return files;
2449
+ }
2450
+
2451
+ function toW3CDate(date) {
2452
+ return date.toISOString().replace(/\.\d{3}Z$/, 'Z');
2453
+ }
2454
+
2455
+ function urlEntry({ loc, lastmod, changefreq, priority, comment }) {
2456
+ const lines = [];
2457
+ if (comment) lines.push(` <!-- ${comment} -->`);
2458
+ lines.push(` <url>`);
2459
+ lines.push(` <loc>${loc}</loc>`);
2460
+ lines.push(` <lastmod>${lastmod}</lastmod>`);
2461
+ lines.push(` <changefreq>${changefreq}</changefreq>`);
2462
+ lines.push(` <priority>${priority}</priority>`);
2463
+ lines.push(` </url>`);
2464
+ return lines.join('\n');
2465
+ }
2466
+
2467
+ function sectionComment(label) {
2468
+ const bar = '='.repeat(42);
2469
+ return [
2470
+ ` <!-- ${bar} -->`,
2471
+ ` <!-- ${label.padEnd(42)} -->`,
2472
+ ` <!-- ${bar} -->`,
2473
+ ].join('\n');
2474
+ }
2475
+
2476
+ const pagesDir = path.join(rootDir, 'src', 'data', 'pages');
2477
+ const buildTime = toW3CDate(new Date());
2478
+
2479
+ const pageFiles = listJsonFilesRecursive(pagesDir);
2480
+ const pages = pageFiles.map((fullPath) => {
2481
+ const slug = path
2482
+ .relative(pagesDir, fullPath)
2483
+ .replace(/\\/g, '/')
2484
+ .replace(/\.json$/i, '');
2485
+ const lastmod = toW3CDate(fs.statSync(fullPath).mtime);
2486
+ return { slug, lastmod };
2487
+ });
2488
+
2489
+ const entries = [];
2490
+
2491
+ entries.push(sectionComment('GLOBAL AGENT DISCOVERY NODES'));
2492
+ entries.push(
2493
+ urlEntry({ loc: `${baseUrl}/llms.txt`, lastmod: buildTime, changefreq: 'weekly', priority: '1.0' }),
2494
+ );
2495
+ entries.push(
2496
+ urlEntry({ loc: `${baseUrl}/mcp-manifest.json`, lastmod: buildTime, changefreq: 'weekly', priority: '1.0' }),
2497
+ );
2498
+
2499
+ for (const { slug, lastmod } of pages) {
2500
+ const humanPath = slug === 'home' ? '/' : `/${slug}`;
2501
+ const label = `PAGE: ${slug.toUpperCase()}`;
2502
+
2503
+ entries.push(sectionComment(label));
2504
+ entries.push(
2505
+ urlEntry({ loc: `${baseUrl}${humanPath}`, lastmod, changefreq: 'daily', priority: '0.9', comment: 'Human UI' }),
2506
+ );
2507
+ entries.push(
2508
+ urlEntry({ loc: `${baseUrl}/${slug}.json`, lastmod, changefreq: 'daily', priority: '0.9', comment: 'Machine Payload' }),
2509
+ );
2510
+ entries.push(
2511
+ urlEntry({ loc: `${baseUrl}/schemas/${slug}.schema.json`, lastmod: buildTime, changefreq: 'weekly', priority: '0.8', comment: 'Machine Contract (Schema)' }),
2512
+ );
2513
+ }
2514
+
2515
+ const xml = [
2516
+ `<?xml version="1.0" encoding="UTF-8"?>`,
2517
+ `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`,
2518
+ ``,
2519
+ entries.join('\n'),
2520
+ ``,
2521
+ `</urlset>`,
2522
+ ].join('\n');
2523
+
2524
+ const outPath = path.join(rootDir, 'public', 'sitemap.xml');
2525
+ fs.writeFileSync(outPath, xml, 'utf-8');
2526
+ console.log('[sitemap] Written -> public/sitemap.xml');
2527
+
2242
2528
  END_OF_FILE_CONTENT
2243
2529
  echo "Creating scripts/sync-pages-to-public.mjs..."
2244
2530
  cat << 'END_OF_FILE_CONTENT' > "scripts/sync-pages-to-public.mjs"
@@ -2582,20 +2868,18 @@ cat << 'END_OF_FILE_CONTENT' > "src/App.tsx"
2582
2868
  * Data from getHydratedData (file-backed or draft); assets from public/assets/images.
2583
2869
  * Supports Hybrid Persistence: Local Filesystem (Dev) or Cloud Bridge (Prod).
2584
2870
  */
2585
- import { useCallback, useEffect, useMemo, useRef, useState, Suspense, lazy } from 'react';
2586
- // Visitor path uses the runtime-only bundle (~28 KB gz), per ADR-0009 D7.
2587
- // The full @olonjs/core is loaded on demand only when the URL targets /admin.
2588
- import { OlonJSEngine } from '@olonjs/core/runtime';
2589
- import type { JsonPagesConfig, LibraryImageEntry, ProjectState } from '@olonjs/core/runtime';
2590
- import { normalizeBasePath, withBasePath } from '@olonjs/core/runtime';
2871
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2872
+ import { JsonPagesEngine } from '@olonjs/core';
2873
+ import type { JsonPagesConfig, LibraryImageEntry, ProjectState } from '@olonjs/core';
2874
+ import { normalizeBasePath, withBasePath } from '@olonjs/core';
2591
2875
  import { ComponentRegistry } from '@/lib/ComponentRegistry';
2592
2876
  import { SECTION_SCHEMAS, SECTION_SUBMISSION_SCHEMAS } from '@/lib/schemas';
2593
2877
  import { addSectionConfig } from '@/lib/addSectionConfig';
2594
2878
  import { getHydratedData } from '@/lib/draftStorage';
2595
2879
  import type { SiteConfig, ThemeConfig, MenuConfig, PageConfig } from '@/types';
2596
- import type { DeployPhase, StepId } from '@olonjs/core/runtime';
2597
- import { DEPLOY_STEPS } from '@olonjs/core/runtime';
2598
- import { startCloudSaveStream } from '@olonjs/core/runtime';
2880
+ import type { DeployPhase, StepId } from '@olonjs/core';
2881
+ import { DEPLOY_STEPS } from '@olonjs/core';
2882
+ import { startCloudSaveStream } from '@olonjs/core';
2599
2883
  import siteData from '@/data/config/site.json';
2600
2884
  import themeData from '@/data/config/theme.json';
2601
2885
  import menuData from '@/data/config/menu.json';
@@ -2605,23 +2889,9 @@ import { EmptyTenantView } from '@/components/empty-tenant';
2605
2889
  import { Skeleton } from '@/components/ui/skeleton';
2606
2890
  import { ThemeProvider } from '@/components/ThemeProvider';
2607
2891
  import { useOlonForms } from '@/lib/useOlonForms';
2608
- import { OlonFormsContext } from '@olonjs/core/runtime';
2892
+ import { OlonFormsContext } from '@olonjs/core';
2609
2893
  import { iconMap } from '@/lib/IconResolver';
2610
2894
 
2611
- /**
2612
- * Engine selector. Visitors get OlonJSEngine (~28 KB gz); /admin paths
2613
- * lazy-load the full JsonPagesEngine (~128 KB gz) which mounts the
2614
- * Studio editor surface. The check runs at module load time — once per
2615
- * page, before React mounts — so Vite statically splits the chunks.
2616
- * See ADR-0008 + ADR-0009 + docs/plans/core-studio-split.md (Task 3.1).
2617
- */
2618
- const isAdminPath =
2619
- typeof window !== 'undefined' && window.location.pathname.startsWith('/admin');
2620
-
2621
- const LazyJsonPagesEngine = lazy(() =>
2622
- import('@olonjs/core').then((m) => ({ default: m.JsonPagesEngine })),
2623
- );
2624
-
2625
2895
  import tenantCss from './index.css?inline';
2626
2896
 
2627
2897
  // Cloud Configuration (Injected by Vercel/Netlify Env Vars)
@@ -3683,12 +3953,8 @@ function App() {
3683
3953
  {shouldRenderEngine ? (
3684
3954
  isTenantEmpty ? (
3685
3955
  <EmptyTenantView />
3686
- ) : isAdminPath ? (
3687
- <Suspense fallback={null}>
3688
- <LazyJsonPagesEngine config={config} />
3689
- </Suspense>
3690
3956
  ) : (
3691
- <OlonJSEngine config={config} />
3957
+ <JsonPagesEngine config={config} />
3692
3958
  )
3693
3959
  ) : null}
3694
3960
  {isCloudMode && (contentMode === 'error' || contentFallback?.reasonCode === 'CLOUD_REFRESH_FAILED') ? (
@@ -3864,7 +4130,7 @@ END_OF_FILE_CONTENT
3864
4130
  echo "Creating src/components/empty-tenant/schema.ts..."
3865
4131
  cat << 'END_OF_FILE_CONTENT' > "src/components/empty-tenant/schema.ts"
3866
4132
  import { z } from 'zod';
3867
- import { BaseSectionData } from '@olonjs/core/runtime';
4133
+ import { BaseSectionData } from '@olonjs/core';
3868
4134
 
3869
4135
  export const EmptyTenantSchema = BaseSectionData.extend({
3870
4136
  title: z.string().optional().describe('ui:text'),
@@ -3887,7 +4153,7 @@ mkdir -p "src/components/form-demo"
3887
4153
  echo "Creating src/components/form-demo/View.tsx..."
3888
4154
  cat << 'END_OF_FILE_CONTENT' > "src/components/form-demo/View.tsx"
3889
4155
  import { Icon } from '@/lib/IconResolver';
3890
- import { useFormState } from '@olonjs/core/runtime';
4156
+ import { useFormState } from '@olonjs/core';
3891
4157
  import type { FormDemoData } from './types';
3892
4158
 
3893
4159
  type FormDemoViewProps = {
@@ -4051,7 +4317,7 @@ END_OF_FILE_CONTENT
4051
4317
  echo "Creating src/components/form-demo/schema.ts..."
4052
4318
  cat << 'END_OF_FILE_CONTENT' > "src/components/form-demo/schema.ts"
4053
4319
  import { z } from 'zod';
4054
- import { BaseSectionData, WithFormRecipient } from '@olonjs/core/runtime';
4320
+ import { BaseSectionData, WithFormRecipient } from '@olonjs/core';
4055
4321
 
4056
4322
  export const FormDemoSchema = BaseSectionData.merge(WithFormRecipient).extend({
4057
4323
  icon: z.string().optional().describe('ui:icon-picker'),
@@ -4090,7 +4356,7 @@ END_OF_FILE_CONTENT
4090
4356
  mkdir -p "src/components/save-drawer"
4091
4357
  echo "Creating src/components/save-drawer/DeployConnector.tsx..."
4092
4358
  cat << 'END_OF_FILE_CONTENT' > "src/components/save-drawer/DeployConnector.tsx"
4093
- import type { StepState } from '@olonjs/core/runtime';
4359
+ import type { StepState } from '@olonjs/core';
4094
4360
 
4095
4361
  interface DeployConnectorProps {
4096
4362
  fromState: StepState;
@@ -4136,7 +4402,7 @@ END_OF_FILE_CONTENT
4136
4402
  echo "Creating src/components/save-drawer/DeployNode.tsx..."
4137
4403
  cat << 'END_OF_FILE_CONTENT' > "src/components/save-drawer/DeployNode.tsx"
4138
4404
  import type { CSSProperties } from 'react';
4139
- import type { DeployStep, StepState } from '@olonjs/core/runtime';
4405
+ import type { DeployStep, StepState } from '@olonjs/core';
4140
4406
 
4141
4407
  interface DeployNodeProps {
4142
4408
  step: DeployStep;
@@ -4224,8 +4490,8 @@ echo "Creating src/components/save-drawer/DopaDrawer.tsx..."
4224
4490
  cat << 'END_OF_FILE_CONTENT' > "src/components/save-drawer/DopaDrawer.tsx"
4225
4491
  import { useEffect, useMemo, useState } from 'react';
4226
4492
  import { createPortal } from 'react-dom';
4227
- import type { StepId, StepState } from '@olonjs/core/runtime';
4228
- import { DEPLOY_STEPS } from '@olonjs/core/runtime';
4493
+ import type { StepId, StepState } from '@olonjs/core';
4494
+ import { DEPLOY_STEPS } from '@olonjs/core';
4229
4495
  import fontsCss from '@/fonts.css?inline';
4230
4496
  import saverStyleCss from './saverStyle.css?inline';
4231
4497
  import { DeployNode } from './DeployNode';
@@ -5432,7 +5698,7 @@ import {
5432
5698
  Code2, Quote, SquareCode,
5433
5699
  Link2, Unlink2, ImagePlus, Eraser,
5434
5700
  } from 'lucide-react';
5435
- import { STUDIO_EVENTS, useConfig, useStudio } from '@olonjs/core/runtime';
5701
+ import { STUDIO_EVENTS, useConfig, useStudio } from '@olonjs/core';
5436
5702
  import type { TiptapData, TiptapSettings } from './types';
5437
5703
 
5438
5704
  // ── UI primitives ─────────────────────────────────────────────────
@@ -5755,7 +6021,7 @@ END_OF_FILE_CONTENT
5755
6021
  echo "Creating src/components/tiptap/schema.ts..."
5756
6022
  cat << 'END_OF_FILE_CONTENT' > "src/components/tiptap/schema.ts"
5757
6023
  import { z } from 'zod';
5758
- import { BaseSectionData } from '@olonjs/core/runtime';
6024
+ import { BaseSectionData } from '@olonjs/core';
5759
6025
 
5760
6026
  export const TiptapSchema = BaseSectionData.extend({
5761
6027
  content: z.string().default('').describe('ui:editorial-markdown'),
@@ -5776,7 +6042,7 @@ END_OF_FILE_CONTENT
5776
6042
  mkdir -p "src/components/ui"
5777
6043
  echo "Creating src/components/ui/OlonMark.tsx..."
5778
6044
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/OlonMark.tsx"
5779
- import { cn } from '@olonjs/core/runtime'
6045
+ import { cn } from '@olonjs/core'
5780
6046
 
5781
6047
  interface OlonMarkProps {
5782
6048
  size?: number
@@ -5866,7 +6132,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/accordion.tsx"
5866
6132
  import * as React from "react"
5867
6133
  import { Accordion as AccordionPrimitive } from "radix-ui"
5868
6134
 
5869
- import { cn } from "@olonjs/core/runtime"
6135
+ import { cn } from "@olonjs/core"
5870
6136
  import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
5871
6137
 
5872
6138
  function Accordion({
@@ -5970,7 +6236,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/avatar.tsx"
5970
6236
  import * as React from "react"
5971
6237
  import { Avatar as AvatarPrimitive } from "radix-ui"
5972
6238
 
5973
- import { cn } from "@olonjs/core/runtime"
6239
+ import { cn } from "@olonjs/core"
5974
6240
 
5975
6241
  function Avatar({
5976
6242
  className,
@@ -6085,7 +6351,7 @@ echo "Creating src/components/ui/badge.tsx..."
6085
6351
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/badge.tsx"
6086
6352
  import * as React from 'react'
6087
6353
  import { cva, type VariantProps } from 'class-variance-authority'
6088
- import { cn } from '@olonjs/core/runtime'
6354
+ import { cn } from '@olonjs/core'
6089
6355
 
6090
6356
  const badgeVariants = cva(
6091
6357
  'inline-flex items-center px-2.5 py-0.5 text-xs font-medium transition-colors',
@@ -6120,7 +6386,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/breadcrumb.tsx"
6120
6386
  import * as React from "react"
6121
6387
  import { Slot } from "radix-ui"
6122
6388
 
6123
- import { cn } from "@olonjs/core/runtime"
6389
+ import { cn } from "@olonjs/core"
6124
6390
  import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
6125
6391
 
6126
6392
  function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
@@ -6247,7 +6513,7 @@ echo "Creating src/components/ui/button.tsx..."
6247
6513
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/button.tsx"
6248
6514
  import * as React from 'react'
6249
6515
  import { cva, type VariantProps } from 'class-variance-authority'
6250
- import { cn } from '@olonjs/core/runtime'
6516
+ import { cn } from '@olonjs/core'
6251
6517
 
6252
6518
  const buttonVariants = cva(
6253
6519
  'inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 shrink-0',
@@ -6308,7 +6574,7 @@ END_OF_FILE_CONTENT
6308
6574
  echo "Creating src/components/ui/card.tsx..."
6309
6575
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/card.tsx"
6310
6576
  import * as React from 'react'
6311
- import { cn } from '@olonjs/core/runtime'
6577
+ import { cn } from '@olonjs/core'
6312
6578
 
6313
6579
  const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
6314
6580
  ({ className, ...props }, ref) => (
@@ -6367,7 +6633,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/checkbox.tsx"
6367
6633
  import * as React from "react"
6368
6634
  import { Checkbox as CheckboxPrimitive } from "radix-ui"
6369
6635
 
6370
- import { cn } from "@olonjs/core/runtime"
6636
+ import { cn } from "@olonjs/core"
6371
6637
  import { CheckIcon } from "lucide-react"
6372
6638
 
6373
6639
  function Checkbox({
@@ -6407,7 +6673,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/dialog.tsx"
6407
6673
  import * as React from "react"
6408
6674
  import { Dialog as DialogPrimitive } from "radix-ui"
6409
6675
 
6410
- import { cn } from "@olonjs/core/runtime"
6676
+ import { cn } from "@olonjs/core"
6411
6677
  import { Button } from "@/components/ui/button"
6412
6678
  import { XIcon } from "lucide-react"
6413
6679
 
@@ -6579,7 +6845,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/dropdown-menu.tsx"
6579
6845
  import * as React from "react"
6580
6846
  import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
6581
6847
 
6582
- import { cn } from "@olonjs/core/runtime"
6848
+ import { cn } from "@olonjs/core"
6583
6849
  import { CheckIcon, ChevronRightIcon } from "lucide-react"
6584
6850
 
6585
6851
  function DropdownMenu({
@@ -6854,7 +7120,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/hover-card.tsx"
6854
7120
  import * as React from "react"
6855
7121
  import { HoverCard as HoverCardPrimitive } from "radix-ui"
6856
7122
 
6857
- import { cn } from "@olonjs/core/runtime"
7123
+ import { cn } from "@olonjs/core"
6858
7124
 
6859
7125
  function HoverCard({
6860
7126
  ...props
@@ -6900,7 +7166,7 @@ END_OF_FILE_CONTENT
6900
7166
  echo "Creating src/components/ui/input.tsx..."
6901
7167
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/input.tsx"
6902
7168
  import * as React from 'react'
6903
- import { cn } from '@olonjs/core/runtime'
7169
+ import { cn } from '@olonjs/core'
6904
7170
 
6905
7171
  export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
6906
7172
 
@@ -6931,7 +7197,7 @@ END_OF_FILE_CONTENT
6931
7197
  echo "Creating src/components/ui/label.tsx..."
6932
7198
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/label.tsx"
6933
7199
  import * as React from 'react'
6934
- import { cn } from '@olonjs/core/runtime'
7200
+ import { cn } from '@olonjs/core'
6935
7201
 
6936
7202
  const Label = React.forwardRef<
6937
7203
  HTMLLabelElement,
@@ -6955,7 +7221,7 @@ import * as React from "react"
6955
7221
  import { cva } from "class-variance-authority"
6956
7222
  import { NavigationMenu as NavigationMenuPrimitive } from "radix-ui"
6957
7223
 
6958
- import { cn } from "@olonjs/core/runtime"
7224
+ import { cn } from "@olonjs/core"
6959
7225
  import { ChevronDownIcon } from "lucide-react"
6960
7226
 
6961
7227
  function NavigationMenu({
@@ -7124,7 +7390,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/progress.tsx"
7124
7390
  import * as React from "react"
7125
7391
  import { Progress as ProgressPrimitive } from "radix-ui"
7126
7392
 
7127
- import { cn } from "@olonjs/core/runtime"
7393
+ import { cn } from "@olonjs/core"
7128
7394
 
7129
7395
  function Progress({
7130
7396
  className,
@@ -7159,7 +7425,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/scroll-area.tsx"
7159
7425
  import * as React from "react"
7160
7426
  import { ScrollArea as ScrollAreaPrimitive } from "radix-ui"
7161
7427
 
7162
- import { cn } from "@olonjs/core/runtime"
7428
+ import { cn } from "@olonjs/core"
7163
7429
 
7164
7430
  function ScrollArea({
7165
7431
  className,
@@ -7218,7 +7484,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/select.tsx"
7218
7484
  import * as React from "react"
7219
7485
  import { Select as SelectPrimitive } from "radix-ui"
7220
7486
 
7221
- import { cn } from "@olonjs/core/runtime"
7487
+ import { cn } from "@olonjs/core"
7222
7488
  import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
7223
7489
 
7224
7490
  function Select({
@@ -7604,7 +7870,7 @@ END_OF_FILE_CONTENT
7604
7870
  echo "Creating src/components/ui/separator.tsx..."
7605
7871
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/separator.tsx"
7606
7872
  import * as React from 'react'
7607
- import { cn } from '@olonjs/core/runtime'
7873
+ import { cn } from '@olonjs/core'
7608
7874
 
7609
7875
  const Separator = React.forwardRef<
7610
7876
  HTMLDivElement,
@@ -7633,7 +7899,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/sheet.tsx"
7633
7899
  import * as React from "react"
7634
7900
  import { Dialog as SheetPrimitive } from "radix-ui"
7635
7901
 
7636
- import { cn } from "@olonjs/core/runtime"
7902
+ import { cn } from "@olonjs/core"
7637
7903
  import { Button } from "@/components/ui/button"
7638
7904
  import { XIcon } from "lucide-react"
7639
7905
 
@@ -7781,7 +8047,7 @@ END_OF_FILE_CONTENT
7781
8047
  # SKIP: src/components/ui/sheet.tsx:Zone.Identifier is binary and cannot be embedded as text.
7782
8048
  echo "Creating src/components/ui/skeleton.tsx..."
7783
8049
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/skeleton.tsx"
7784
- import { cn } from '@olonjs/core/runtime';
8050
+ import { cn } from '@olonjs/core';
7785
8051
  import type { HTMLAttributes } from 'react';
7786
8052
 
7787
8053
  function Skeleton({
@@ -7805,7 +8071,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/switch.tsx"
7805
8071
  import * as React from "react"
7806
8072
  import { Switch as SwitchPrimitive } from "radix-ui"
7807
8073
 
7808
- import { cn } from "@olonjs/core/runtime"
8074
+ import { cn } from "@olonjs/core"
7809
8075
 
7810
8076
  function Switch({
7811
8077
  className,
@@ -7841,7 +8107,7 @@ echo "Creating src/components/ui/table.tsx..."
7841
8107
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/table.tsx"
7842
8108
  import * as React from "react"
7843
8109
 
7844
- import { cn } from "@olonjs/core/runtime"
8110
+ import { cn } from "@olonjs/core"
7845
8111
 
7846
8112
  function Table({ className, ...props }: React.ComponentProps<"table">) {
7847
8113
  return (
@@ -7965,7 +8231,7 @@ import * as React from "react"
7965
8231
  import { cva, type VariantProps } from "class-variance-authority"
7966
8232
  import { Tabs as TabsPrimitive } from "radix-ui"
7967
8233
 
7968
- import { cn } from "@olonjs/core/runtime"
8234
+ import { cn } from "@olonjs/core"
7969
8235
 
7970
8236
  function Tabs({
7971
8237
  className,
@@ -8057,7 +8323,7 @@ echo "Creating src/components/ui/textarea.tsx..."
8057
8323
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/textarea.tsx"
8058
8324
  import * as React from "react"
8059
8325
 
8060
- import { cn } from "@olonjs/core/runtime"
8326
+ import { cn } from "@olonjs/core"
8061
8327
 
8062
8328
  function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
8063
8329
  return (
@@ -8086,7 +8352,7 @@ import * as React from "react"
8086
8352
  import { type VariantProps } from "class-variance-authority"
8087
8353
  import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui"
8088
8354
 
8089
- import { cn } from "@olonjs/core/runtime"
8355
+ import { cn } from "@olonjs/core"
8090
8356
  import { toggleVariants } from "@/components/ui/toggle"
8091
8357
 
8092
8358
  const ToggleGroupContext = React.createContext<
@@ -8181,7 +8447,7 @@ import * as React from "react"
8181
8447
  import { cva, type VariantProps } from "class-variance-authority"
8182
8448
  import { Toggle as TogglePrimitive } from "radix-ui"
8183
8449
 
8184
- import { cn } from "@olonjs/core/runtime"
8450
+ import { cn } from "@olonjs/core"
8185
8451
 
8186
8452
  const toggleVariants = cva(
8187
8453
  "group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
@@ -8233,7 +8499,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/tooltip.tsx"
8233
8499
  import * as React from "react"
8234
8500
  import { Tooltip as TooltipPrimitive } from "radix-ui"
8235
8501
 
8236
- import { cn } from "@olonjs/core/runtime"
8502
+ import { cn } from "@olonjs/core"
8237
8503
 
8238
8504
  function TooltipProvider({
8239
8505
  delayDuration = 0,
@@ -8936,7 +9202,7 @@ echo "Creating src/entry-ssg.tsx..."
8936
9202
  cat << 'END_OF_FILE_CONTENT' > "src/entry-ssg.tsx"
8937
9203
  import { renderToString } from 'react-dom/server';
8938
9204
  import { StaticRouter } from 'react-router-dom/server';
8939
- import { ConfigProvider, PageRenderer, StudioProvider, resolveRuntimeConfig } from '@olonjs/core/runtime';
9205
+ import { ConfigProvider, PageRenderer, StudioProvider, resolveRuntimeConfig } from '@olonjs/core';
8940
9206
  import type { JsonPagesConfig, PageConfig, SiteConfig, ThemeConfig } from '@/types';
8941
9207
  import { ThemeProvider } from '@/components/ThemeProvider';
8942
9208
  import { ComponentRegistry } from '@/lib/ComponentRegistry';
@@ -10144,7 +10410,7 @@ export const Icon: React.FC<IconProps> = ({ name, size = 20, className }) => {
10144
10410
  END_OF_FILE_CONTENT
10145
10411
  echo "Creating src/lib/addSectionConfig.ts..."
10146
10412
  cat << 'END_OF_FILE_CONTENT' > "src/lib/addSectionConfig.ts"
10147
- import type { AddSectionConfig } from '@olonjs/core/runtime';
10413
+ import type { AddSectionConfig } from '@olonjs/core';
10148
10414
 
10149
10415
  const addableSectionTypes = ['empty-tenant', 'form-demo'] as const;
10150
10416
 
@@ -10285,7 +10551,7 @@ export {
10285
10551
  BaseSectionSettingsSchema,
10286
10552
  CtaSchema,
10287
10553
  ImageSelectionSchema,
10288
- } from '@olonjs/core/runtime';
10554
+ } from '@olonjs/core';
10289
10555
 
10290
10556
  END_OF_FILE_CONTENT
10291
10557
  echo "Creating src/lib/useFormSubmit.ts..."
@@ -10380,7 +10646,7 @@ END_OF_FILE_CONTENT
10380
10646
  echo "Creating src/lib/useOlonForms.ts..."
10381
10647
  cat << 'END_OF_FILE_CONTENT' > "src/lib/useOlonForms.ts"
10382
10648
  import { useCallback, useEffect, useState } from 'react';
10383
- import type { FormState } from '@olonjs/core/runtime';
10649
+ import type { FormState } from '@olonjs/core';
10384
10650
 
10385
10651
  const API_BASE =
10386
10652
  (import.meta.env.VITE_OLONJS_CLOUD_URL as string | undefined) ??
@@ -10581,12 +10847,6 @@ export type SectionComponentPropsMap = {
10581
10847
  'form-demo': { data: FormDemoData; settings?: FormDemoSettings };
10582
10848
  };
10583
10849
 
10584
- // MTRP module augmentation. TypeScript treats `@olonjs/core` and
10585
- // `@olonjs/core/runtime` as separate module identifiers (different
10586
- // import specifiers). After ADR-0009 the tenant imports JsonPagesConfig
10587
- // from `/runtime` for the visitor path, so the section registries must
10588
- // be augmented for *both* identifiers, otherwise PageConfig.sections
10589
- // resolves to a generic FallbackSection on the runtime side.
10590
10850
  declare module '@olonjs/core' {
10591
10851
  export interface SectionDataRegistry {
10592
10852
  'empty-tenant': EmptyTenantData;
@@ -10598,26 +10858,7 @@ declare module '@olonjs/core' {
10598
10858
  }
10599
10859
  }
10600
10860
 
10601
- declare module '@olonjs/core/runtime' {
10602
- export interface SectionDataRegistry {
10603
- 'empty-tenant': EmptyTenantData;
10604
- 'form-demo': FormDemoData;
10605
- }
10606
- export interface SectionSettingsRegistry {
10607
- 'empty-tenant': EmptyTenantSettings;
10608
- 'form-demo': FormDemoSettings;
10609
- }
10610
- }
10611
-
10612
- // ADR-0009 D7: tenant types re-export from the runtime subpath, NOT the
10613
- // full @olonjs/core. This is a value re-export — even though every
10614
- // consumer in this codebase only does `import type`, Vite's static
10615
- // graph treats `export *` as a runtime dependency edge. Pointing it at
10616
- // '@olonjs/core' would pull the full Studio bundle (AdminSidebar,
10617
- // FormFactory, StudioStage, admin-skin) into the visitor main chunk.
10618
- // Pointing it at '@olonjs/core/runtime' anchors the graph to the
10619
- // runtime-only bundle (~28 KB gz) so the visitor entry stays clean.
10620
- export * from '@olonjs/core/runtime';
10861
+ export * from '@olonjs/core';
10621
10862
 
10622
10863
  END_OF_FILE_CONTENT
10623
10864
  echo "Creating src/vite-env.d.ts..."