cirrojs 0.0.15 → 0.0.17

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/CHANGELOG.md CHANGED
@@ -13,6 +13,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
13
13
 
14
14
  ## [Unreleased]
15
15
 
16
+ ## [0.0.17] - 2026-06-29
17
+
18
+ ### Added
19
+ - The CLI now sets the `CIRRO_COMMAND` environment variable during rendering: `"build"` under `cirro build` and `"dev"` under `cirro dev`. Pages can read `process.env.CIRRO_COMMAND` at render time to vary their output between development and build, for example to emit a `<meta http-equiv="Content-Security-Policy">` element only in build.
20
+
21
+ ## [0.0.16] - 2026-06-29
22
+
23
+ ### Changed
24
+ - The layout option types (`StackOpt`, `ClusterOpt`, `CenterOpt`, `GridOpt`, `SwitcherOpt`, `SidebarOpt`, `CoverOpt`, `FrameOpt`, `ReelOpt`, `BoxOpt`) now type their CSS-derived fields with the matching `Properties` value types instead of `string`, enabling keyword autocompletion. Affected fields: `gap`, `CenterOpt.max`/`gutters`, `SidebarOpt.sideWidth`/`contentMin`, `CoverOpt.minHeight`/`padding`, `FrameOpt.ratio`, `ReelOpt.itemWidth`/`height`, and `BoxOpt.padding`/`border`. The `GridOpt.min`, `SwitcherOpt.threshold`, and `ImposterOpt.margin` fields remain `string` as they have no single corresponding CSS property.
25
+
16
26
  ## [0.0.15] - 2026-06-29
17
27
 
18
28
  ### Added
@@ -127,7 +137,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
127
137
  ## 0.0.1 - 2026-06-15
128
138
  - initial release
129
139
 
130
- [Unreleased]: https://github.com/osawa-naotaka/cirro/compare/v0.0.15...HEAD
140
+ [Unreleased]: https://github.com/osawa-naotaka/cirro/compare/v0.0.17...HEAD
141
+ [0.0.17]: https://github.com/osawa-naotaka/cirro/compare/v0.0.16...v0.0.17
142
+ [0.0.16]: https://github.com/osawa-naotaka/cirro/compare/v0.0.15...v0.0.16
131
143
  [0.0.15]: https://github.com/osawa-naotaka/cirro/compare/v0.0.14...v0.0.15
132
144
  [0.0.14]: https://github.com/osawa-naotaka/cirro/compare/v0.0.13...v0.0.14
133
145
  [0.0.13]: https://github.com/osawa-naotaka/cirro/compare/v0.0.12...v0.0.13
package/dist/cli.js CHANGED
@@ -1 +1 @@
1
- import{r as e}from"./css-D28QH0EJ.js";import{Fragment as t,createElement as n}from"react";import{renderToStaticMarkup as r}from"react-dom/server";import{dirname as i,extname as a,join as o,resolve as s}from"node:path";import{mkdir as c,readFile as l,writeFile as u}from"node:fs/promises";import{build as d,createServer as f,createServerModuleRunner as p}from"vite";import{createServer as m}from"node:http";function h(e){let t=[];for(let n of e)switch(n.type){case`static`:t.push({type:`html`,path:n.path,cssPath:o(n.path,`index.css`),render:()=>n.component({params:{}})}),t.push({type:`css`,path:o(n.path,`index.css`),render:()=>n.component({params:{}})});break;case`dynamic`:for(let e of n.getStaticPaths())t.push({type:`html`,path:n.path(e),cssPath:n.cssPath,render:()=>n.component({params:e})});t.push({type:`css`,path:n.cssPath,render:()=>n.component({params:n.getStaticPaths()[0]})});break;case`file`:t.push({type:`file`,path:n.path,ext:a(n.path),render:()=>n.component({params:{}})});break}return t}function g(e){return e===`/`?`index.html`:`${e.replace(/^\/+|\/+$/g,``)}/index.html`}function _(e){return e===`/`?`index.css`:`${e.replace(/^\/+|\/+$/g,``)}`}function v(e,r,i){return n(t,null,e,n(`script`,{async:!0,type:`module`,src:r,key:`cirro-client`}),n(`link`,{rel:`stylesheet`,href:i,precedence:`default`,key:`cirro-css`}))}function y(e){for(let t of e.plugins){let e=t.api;if(t.name===`cirro`&&e?.options)return e.options}throw Error(`cirro: plugin not found in Vite config (did you add cirro() to plugins?)`)}async function b(){await d();let t=await f({server:{middlewareMode:!0,hmr:!1},appType:`custom`}),n=p(t.environments.ssr);try{let a=t.config,d=y(a),f=a.root,p=s(f,a.build.outDir),m=s(f,d.routes),b=JSON.parse(await l(o(p,`.vite/manifest.json`),`utf-8`))[`virtual:cirro/client`];if(!b)throw Error(`cirro: manifest entry "virtual:cirro/client" not found`);let x=`/${b.file}`,{routes:S,runWithRegistry:C}=await n.import(m);for(let t of h(S))switch(t.type){case`css`:{let{registry:n}=C(()=>r(t.render())),a=e(n),s=o(p,_(t.path));await c(i(s),{recursive:!0}),await u(s,a),console.log(`wrote ${s} (url: ${t.path})`);break}case`html`:{let{result:e}=C(()=>`<!DOCTYPE html>${r(v(t.render(),x,t.cssPath))}`),n=o(p,g(t.path));await c(i(n),{recursive:!0}),await u(n,e),console.log(`wrote ${n} (url: ${t.path})`);break}case`file`:{let e=t.render(),n=o(p,t.path);await c(i(n),{recursive:!0}),await u(n,e),console.log(`wrote ${n} (url: ${t.path})`);break}}}finally{await t.close()}}const x=[[`.aac`,`audio/aac`],[`.abw`,`application/x-abiword`],[`.arc`,`application/x-freearc`],[`.avi`,`video/x-msvideo`],[`.azw`,`application/vnd.amazon.ebook`],[`.bin`,`application/octet-stream`],[`.bmp`,`image/bmp`],[`.bz`,`application/x-bzip`],[`.bz2`,`application/x-bzip2`],[`.csh`,`application/x-csh`],[`.css`,`text/css`],[`.csv`,`text/csv`],[`.doc`,`application/msword`],[`.docx`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`],[`.eot`,`application/vnd.ms-fontobject`],[`.epub`,`application/epub+zip`],[`.gz`,`application/gzip`],[`.gif`,`image/gif`],[`.htm`,`text/html`],[`.html`,`text/html`],[`.ico`,`image/vnd.microsoft.icon`],[`.ics`,`text/calendar`],[`.jar`,`application/java-archive`],[`.jpeg`,`image/jpeg`],[`.jpg`,`image/jpeg`],[`.js`,`text/javascript`],[`.json`,`application/json`],[`.jsonld`,`application/ld+json`],[`.mid`,`audio/midi`],[`.midi`,`audio/x-midi`],[`.mjs`,`text/javascript`],[`.mp3`,`audio/mpeg`],[`.mpeg`,`video/mpeg`],[`.mpkg`,`application/vnd.apple.installer+xml`],[`.odp`,`application/vnd.oasis.opendocument.presentation`],[`.ods`,`application/vnd.oasis.opendocument.spreadsheet`],[`.odt`,`application/vnd.oasis.opendocument.text`],[`.oga`,`audio/ogg`],[`.ogv`,`video/ogg`],[`.ogx`,`application/ogg`],[`.opus`,`audio/opus`],[`.otf`,`font/otf`],[`.png`,`image/png`],[`.pdf`,`application/pdf`],[`.php`,`application/x-httpd-php`],[`.ppt`,`application/vnd.ms-powerpoint`],[`.pptx`,`application/vnd.openxmlformats-officedocument.presentationml.presentation`],[`.rar`,`application/vnd.rar`],[`.rtf`,`application/rtf`],[`.sh`,`application/x-sh`],[`.svg`,`image/svg+xml`],[`.swf`,`application/x-shockwave-flash`],[`.tar`,`application/x-tar`],[`.tif`,`image/tiff`],[`.tiff`,`image/tiff`],[`.ts`,`video/mp2t`],[`.ttf`,`font/ttf`],[`.txt`,`text/plain`],[`.vsd`,`application/vnd.visio`],[`.wav`,`audio/wav`],[`.weba`,`audio/webm`],[`.webm`,`video/webm`],[`.webp`,`image/webp`],[`.woff`,`font/woff`],[`.woff2`,`font/woff2`],[`.xhtml`,`application/xhtml+xml`],[`.xls`,`application/vnd.ms-excel`],[`.xlsx`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`],[`.xml`,`application/xml`],[`.xul`,`application/vnd.mozilla.xul+xml`],[`.zip`,`application/zip`],[`.3gp`,`video/3gpp`],[`.3g2`,`video/3gpp2`],[`.7z`,`application/x-7z-compressed`]];function S(e){return x.find(t=>t[0]===e)?.[1]||`text/plain`}function C(e,t){let n=[t,t.replaceAll(`\\`,`/`)];for(let t of Object.values(e.environments)){let e=t.moduleGraph;if(!e)continue;let r=new Set,i=t=>{if(!r.has(t)){r.add(t),e.invalidateModule(t);for(let e of t.importers)i(e)}};for(let t of n){let n=e.getModulesByFile(t);if(n)for(let e of n)i(e)}}}async function w(t=5173){let n=await f({server:{middlewareMode:!0},appType:`custom`}),a=p(n.environments.ssr),o=y(n.config),c=n.config.root,l=s(c,o.routes),u=i(s(c,o.islands)).replaceAll(`\\`,`/`),d=m((t,i)=>{function o(e,t){i.statusCode=200,i.setHeader(`Content-Type`,S(e)),i.end(t)}function s(e){i.statusCode=404,i.setHeader(`Content-Type`,S(e)),e===`.html`?i.end(`<!DOCTYPE html><meta charset="utf-8"><h1>404 Not Found</h1>`):i.end()}n.middlewares(t,i,async()=>{let c=t.url??`/`,u=new URL(c,`http://localhost`).pathname.replace(/\/+$/,``)||`/`;try{let{routes:t,runWithRegistry:i}=await a.import(l),d=h(t).find(e=>e.path===u);if(d===void 0){s(`.html`);return}switch(d.type){case`html`:{let{result:e}=i(()=>`<!DOCTYPE html>${r(v(d.render(),`/@id/__x00__virtual:cirro/client`,d.cssPath))}`);o(`.html`,await n.transformIndexHtml(c,e));break}case`css`:{let{registry:t}=i(()=>r(d.render()));o(`.css`,e(t));break}case`file`:{let e=d.render();o(d.ext,e);break}}return}catch(e){i.statusCode=500,e instanceof Error?i.end(e.stack):i.end(String(e))}})}),g=`${s(c,o.watchDir??`./src`).replaceAll(`\\`,`/`).replace(/\/+$/,``)}/`;n.watcher.on(`change`,e=>{let t=e.replaceAll(`\\`,`/`);t.startsWith(u)||t.startsWith(g)&&(C(n,e),n.ws.send({type:`full-reload`}))}),d.listen(t,()=>{console.log(`cirro dev: http://localhost:${t}`)})}async function T(e){let t=e[0];t===`dev`?await w():t===`build`?await b():(console.error(`usage: cirro <dev|build>`),process.exit(1))}export{T as main};
1
+ import{r as e}from"./css-D28QH0EJ.js";import{Fragment as t,createElement as n}from"react";import{renderToStaticMarkup as r}from"react-dom/server";import{dirname as i,extname as a,join as o,resolve as s}from"node:path";import{mkdir as c,readFile as l,writeFile as u}from"node:fs/promises";import{build as d,createServer as f,createServerModuleRunner as p}from"vite";import{createServer as m}from"node:http";function h(e){let t=[];for(let n of e)switch(n.type){case`static`:t.push({type:`html`,path:n.path,cssPath:o(n.path,`index.css`),render:()=>n.component({params:{}})}),t.push({type:`css`,path:o(n.path,`index.css`),render:()=>n.component({params:{}})});break;case`dynamic`:for(let e of n.getStaticPaths())t.push({type:`html`,path:n.path(e),cssPath:n.cssPath,render:()=>n.component({params:e})});t.push({type:`css`,path:n.cssPath,render:()=>n.component({params:n.getStaticPaths()[0]})});break;case`file`:t.push({type:`file`,path:n.path,ext:a(n.path),render:()=>n.component({params:{}})});break}return t}function g(e){return e===`/`?`index.html`:`${e.replace(/^\/+|\/+$/g,``)}/index.html`}function _(e){return e===`/`?`index.css`:`${e.replace(/^\/+|\/+$/g,``)}`}function v(e,r,i){return n(t,null,e,n(`script`,{async:!0,type:`module`,src:r,key:`cirro-client`}),n(`link`,{rel:`stylesheet`,href:i,precedence:`default`,key:`cirro-css`}))}function y(e){for(let t of e.plugins){let e=t.api;if(t.name===`cirro`&&e?.options)return e.options}throw Error(`cirro: plugin not found in Vite config (did you add cirro() to plugins?)`)}async function b(){process.env.CIRRO_COMMAND=`build`,await d();let t=await f({server:{middlewareMode:!0,hmr:!1},appType:`custom`}),n=p(t.environments.ssr);try{let a=t.config,d=y(a),f=a.root,p=s(f,a.build.outDir),m=s(f,d.routes),b=JSON.parse(await l(o(p,`.vite/manifest.json`),`utf-8`))[`virtual:cirro/client`];if(!b)throw Error(`cirro: manifest entry "virtual:cirro/client" not found`);let x=`/${b.file}`,{routes:S,runWithRegistry:C}=await n.import(m);for(let t of h(S))switch(t.type){case`css`:{let{registry:n}=C(()=>r(t.render())),a=e(n),s=o(p,_(t.path));await c(i(s),{recursive:!0}),await u(s,a),console.log(`wrote ${s} (url: ${t.path})`);break}case`html`:{let{result:e}=C(()=>`<!DOCTYPE html>${r(v(t.render(),x,t.cssPath))}`),n=o(p,g(t.path));await c(i(n),{recursive:!0}),await u(n,e),console.log(`wrote ${n} (url: ${t.path})`);break}case`file`:{let e=t.render(),n=o(p,t.path);await c(i(n),{recursive:!0}),await u(n,e),console.log(`wrote ${n} (url: ${t.path})`);break}}}finally{await t.close()}}const x=[[`.aac`,`audio/aac`],[`.abw`,`application/x-abiword`],[`.arc`,`application/x-freearc`],[`.avi`,`video/x-msvideo`],[`.azw`,`application/vnd.amazon.ebook`],[`.bin`,`application/octet-stream`],[`.bmp`,`image/bmp`],[`.bz`,`application/x-bzip`],[`.bz2`,`application/x-bzip2`],[`.csh`,`application/x-csh`],[`.css`,`text/css`],[`.csv`,`text/csv`],[`.doc`,`application/msword`],[`.docx`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`],[`.eot`,`application/vnd.ms-fontobject`],[`.epub`,`application/epub+zip`],[`.gz`,`application/gzip`],[`.gif`,`image/gif`],[`.htm`,`text/html`],[`.html`,`text/html`],[`.ico`,`image/vnd.microsoft.icon`],[`.ics`,`text/calendar`],[`.jar`,`application/java-archive`],[`.jpeg`,`image/jpeg`],[`.jpg`,`image/jpeg`],[`.js`,`text/javascript`],[`.json`,`application/json`],[`.jsonld`,`application/ld+json`],[`.mid`,`audio/midi`],[`.midi`,`audio/x-midi`],[`.mjs`,`text/javascript`],[`.mp3`,`audio/mpeg`],[`.mpeg`,`video/mpeg`],[`.mpkg`,`application/vnd.apple.installer+xml`],[`.odp`,`application/vnd.oasis.opendocument.presentation`],[`.ods`,`application/vnd.oasis.opendocument.spreadsheet`],[`.odt`,`application/vnd.oasis.opendocument.text`],[`.oga`,`audio/ogg`],[`.ogv`,`video/ogg`],[`.ogx`,`application/ogg`],[`.opus`,`audio/opus`],[`.otf`,`font/otf`],[`.png`,`image/png`],[`.pdf`,`application/pdf`],[`.php`,`application/x-httpd-php`],[`.ppt`,`application/vnd.ms-powerpoint`],[`.pptx`,`application/vnd.openxmlformats-officedocument.presentationml.presentation`],[`.rar`,`application/vnd.rar`],[`.rtf`,`application/rtf`],[`.sh`,`application/x-sh`],[`.svg`,`image/svg+xml`],[`.swf`,`application/x-shockwave-flash`],[`.tar`,`application/x-tar`],[`.tif`,`image/tiff`],[`.tiff`,`image/tiff`],[`.ts`,`video/mp2t`],[`.ttf`,`font/ttf`],[`.txt`,`text/plain`],[`.vsd`,`application/vnd.visio`],[`.wav`,`audio/wav`],[`.weba`,`audio/webm`],[`.webm`,`video/webm`],[`.webp`,`image/webp`],[`.woff`,`font/woff`],[`.woff2`,`font/woff2`],[`.xhtml`,`application/xhtml+xml`],[`.xls`,`application/vnd.ms-excel`],[`.xlsx`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`],[`.xml`,`application/xml`],[`.xul`,`application/vnd.mozilla.xul+xml`],[`.zip`,`application/zip`],[`.3gp`,`video/3gpp`],[`.3g2`,`video/3gpp2`],[`.7z`,`application/x-7z-compressed`]];function S(e){return x.find(t=>t[0]===e)?.[1]||`text/plain`}function C(e,t){let n=[t,t.replaceAll(`\\`,`/`)];for(let t of Object.values(e.environments)){let e=t.moduleGraph;if(!e)continue;let r=new Set,i=t=>{if(!r.has(t)){r.add(t),e.invalidateModule(t);for(let e of t.importers)i(e)}};for(let t of n){let n=e.getModulesByFile(t);if(n)for(let e of n)i(e)}}}async function w(t=5173){process.env.CIRRO_COMMAND=`dev`;let n=await f({server:{middlewareMode:!0},appType:`custom`}),a=p(n.environments.ssr),o=y(n.config),c=n.config.root,l=s(c,o.routes),u=i(s(c,o.islands)).replaceAll(`\\`,`/`),d=m((t,i)=>{function o(e,t){i.statusCode=200,i.setHeader(`Content-Type`,S(e)),i.end(t)}function s(e){i.statusCode=404,i.setHeader(`Content-Type`,S(e)),e===`.html`?i.end(`<!DOCTYPE html><meta charset="utf-8"><h1>404 Not Found</h1>`):i.end()}n.middlewares(t,i,async()=>{let c=t.url??`/`,u=new URL(c,`http://localhost`).pathname.replace(/\/+$/,``)||`/`;try{let{routes:t,runWithRegistry:i}=await a.import(l),d=h(t).find(e=>e.path===u);if(d===void 0){s(`.html`);return}switch(d.type){case`html`:{let{result:e}=i(()=>`<!DOCTYPE html>${r(v(d.render(),`/@id/__x00__virtual:cirro/client`,d.cssPath))}`);o(`.html`,await n.transformIndexHtml(c,e));break}case`css`:{let{registry:t}=i(()=>r(d.render()));o(`.css`,e(t));break}case`file`:{let e=d.render();o(d.ext,e);break}}return}catch(e){i.statusCode=500,e instanceof Error?i.end(e.stack):i.end(String(e))}})}),g=`${s(c,o.watchDir??`./src`).replaceAll(`\\`,`/`).replace(/\/+$/,``)}/`;n.watcher.on(`change`,e=>{let t=e.replaceAll(`\\`,`/`);t.startsWith(u)||t.startsWith(g)&&(C(n,e),n.ws.send({type:`full-reload`}))}),d.listen(t,()=>{console.log(`cirro dev: http://localhost:${t}`)})}async function T(e){let t=e[0];t===`dev`?await w():t===`build`?await b():(console.error(`usage: cirro <dev|build>`),process.exit(1))}export{T as main};
package/dist/layout.d.ts CHANGED
@@ -5,24 +5,24 @@ import { ComponentPropsWithoutRef, ReactNode } from "react";
5
5
  //#region src/layout.d.ts
6
6
  interface LayoutDefaults {
7
7
  gap: string;
8
- stackGap?: string;
9
- clusterGap?: string;
8
+ stackGap?: Properties["gap"];
9
+ clusterGap?: Properties["gap"];
10
10
  clusterWrap?: Properties["flex_wrap"];
11
- gridGap?: string;
12
- switcherGap?: string;
13
- sidebarGap?: string;
11
+ gridGap?: Properties["gap"];
12
+ switcherGap?: Properties["gap"];
13
+ sidebarGap?: Properties["gap"];
14
14
  clusterJustify: Properties["justify_content"];
15
15
  clusterAlign: Properties["align_items"];
16
- centerMax: string;
17
- centerGutters?: string;
16
+ centerMax: Properties["max_inline_size"];
17
+ centerGutters?: Properties["padding_inline"];
18
18
  gridMin: string;
19
19
  switcherThreshold: string;
20
20
  switcherLimit: number;
21
- sidebarContentMin: string;
22
- sidebarSideWidth: string;
23
- coverMinHeight: string;
24
- frameRatio: string;
25
- boxPadding?: string;
21
+ sidebarContentMin: Properties["min_inline_size"];
22
+ sidebarSideWidth: Properties["flex_basis"];
23
+ coverMinHeight: Properties["min_block_size"];
24
+ frameRatio: Properties["aspect_ratio"];
25
+ boxPadding?: Properties["padding"];
26
26
  }
27
27
  interface LayoutTheme {
28
28
  css?: CssFnT;
@@ -38,46 +38,46 @@ interface CoverSlots {
38
38
  centered: string;
39
39
  }
40
40
  interface StackOpt {
41
- gap?: string;
41
+ gap?: Properties["gap"];
42
42
  }
43
43
  interface ClusterOpt {
44
- gap?: string;
44
+ gap?: Properties["gap"];
45
45
  wrap?: Properties["flex_wrap"];
46
46
  justify?: Properties["justify_content"];
47
47
  align?: Properties["align_items"];
48
48
  }
49
49
  interface CenterOpt {
50
- max?: string;
51
- gutters?: string;
50
+ max?: Properties["max_inline_size"];
51
+ gutters?: Properties["padding_inline"];
52
52
  intrinsic?: boolean;
53
53
  andText?: boolean;
54
54
  }
55
55
  interface GridOpt {
56
- gap?: string;
56
+ gap?: Properties["gap"];
57
57
  min?: string;
58
58
  }
59
59
  interface SwitcherOpt {
60
60
  threshold?: string;
61
- gap?: string;
61
+ gap?: Properties["gap"];
62
62
  limit?: number;
63
63
  }
64
64
  interface SidebarOpt {
65
- sideWidth?: string;
66
- contentMin?: string;
67
- gap?: string;
65
+ sideWidth?: Properties["flex_basis"];
66
+ contentMin?: Properties["min_inline_size"];
67
+ gap?: Properties["gap"];
68
68
  }
69
69
  interface CoverOpt {
70
- minHeight?: string;
71
- gap?: string;
72
- padding?: string;
70
+ minHeight?: Properties["min_block_size"];
71
+ gap?: Properties["gap"];
72
+ padding?: Properties["padding"];
73
73
  }
74
74
  interface FrameOpt {
75
- ratio?: string;
75
+ ratio?: Properties["aspect_ratio"];
76
76
  }
77
77
  interface ReelOpt {
78
- itemWidth?: string;
79
- height?: string;
80
- gap?: string;
78
+ itemWidth?: Properties["flex_basis"];
79
+ height?: Properties["block_size"];
80
+ gap?: Properties["gap"];
81
81
  }
82
82
  interface ImposterOpt {
83
83
  fixed?: boolean;
@@ -85,8 +85,8 @@ interface ImposterOpt {
85
85
  margin?: string;
86
86
  }
87
87
  interface BoxOpt {
88
- padding?: string;
89
- border?: string;
88
+ padding?: Properties["padding"];
89
+ border?: Properties["border"];
90
90
  }
91
91
  type ElementOpt = Omit<ComponentPropsWithoutRef<"div">, "style">;
92
92
  interface Layout {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cirrojs",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "React islands SSG with strict CSP (no unsafe-inline). Vite-based, MPA-first.",
5
5
  "license": "MIT",
6
6
  "type": "module",