@stacksjs/stx 0.2.674 → 0.2.676

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.
Files changed (244) hide show
  1. package/dist/{chunk-szevd7d5.js → chunk-028e9fty.js} +2 -2
  2. package/dist/{chunk-rr527jt6.js → chunk-078qg8r5.js} +2 -2
  3. package/dist/chunk-0gk78x3r.js +2 -0
  4. package/dist/{chunk-fw413k9q.js → chunk-1b9thkg5.js} +2 -2
  5. package/dist/chunk-1e73whqh.js +2 -0
  6. package/dist/chunk-1rztwjdj.js +130 -0
  7. package/dist/{chunk-srq9fqq8.js → chunk-245sas8r.js} +38 -38
  8. package/dist/chunk-248a88mc.js +2 -0
  9. package/dist/chunk-2nqtmp8c.js +2 -0
  10. package/dist/chunk-2pga7zzb.js +4 -0
  11. package/dist/{chunk-bkkpq8dx.js → chunk-304dbbsw.js} +10 -10
  12. package/dist/{chunk-z22zy22p.js → chunk-34e8e8te.js} +2 -2
  13. package/dist/{chunk-md2jn67b.js → chunk-374nhgyf.js} +2 -2
  14. package/dist/chunk-3bfegbpw.js +2 -0
  15. package/dist/chunk-3gdt16xa.js +2 -0
  16. package/dist/chunk-3wsm9g9c.js +2 -0
  17. package/dist/chunk-3xx9d934.js +2 -0
  18. package/dist/{chunk-ham5x7ts.js → chunk-4fhbh04h.js} +4 -4
  19. package/dist/{chunk-5vt48yce.js → chunk-4jnv2v5e.js} +9 -9
  20. package/dist/chunk-4rk5qnqf.js +24 -0
  21. package/dist/chunk-526mf3fr.js +4 -0
  22. package/dist/chunk-54zkkxmb.js +2 -0
  23. package/dist/{chunk-kdebh22m.js → chunk-551s7ha0.js} +10 -10
  24. package/dist/chunk-5v7vm8cv.js +14 -0
  25. package/dist/{chunk-rdr09efy.js → chunk-5z0q3qv3.js} +5 -5
  26. package/dist/{chunk-6sjtyd0k.js → chunk-5z8mq37c.js} +4 -4
  27. package/dist/{chunk-ce6zddpf.js → chunk-6beg47t4.js} +2 -2
  28. package/dist/{chunk-trz8z4j4.js → chunk-6bwca55g.js} +37 -37
  29. package/dist/chunk-6kam8r5y.js +2 -0
  30. package/dist/{chunk-tddnb5bv.js → chunk-6ycyaf5g.js} +2 -2
  31. package/dist/chunk-707sx5sk.js +20 -0
  32. package/dist/chunk-78d5tn4r.js +2 -0
  33. package/dist/{chunk-pb6erpj3.js → chunk-78x52gz3.js} +5 -5
  34. package/dist/{chunk-zp79zkkd.js → chunk-78yjmbbs.js} +2 -2
  35. package/dist/{chunk-v21bhhjb.js → chunk-7j7h1eg3.js} +1 -1
  36. package/dist/{chunk-zmg1peyg.js → chunk-8aq07a1j.js} +3 -3
  37. package/dist/chunk-8p92r2sc.js +2 -0
  38. package/dist/chunk-909ymsjz.js +2 -0
  39. package/dist/{chunk-r7fdw2v5.js → chunk-97h9ga3b.js} +3 -3
  40. package/dist/chunk-9859r1d5.js +2 -0
  41. package/dist/{chunk-25zmy8q6.js → chunk-9wdd7eq6.js} +2 -2
  42. package/dist/{chunk-p6qd7ad0.js → chunk-a2zb5fgg.js} +2 -2
  43. package/dist/chunk-arh1qgmp.js +2 -0
  44. package/dist/chunk-b18x0v21.js +2 -0
  45. package/dist/chunk-b23vscaq.js +2 -0
  46. package/dist/{chunk-4b9s7wgy.js → chunk-be2rswkw.js} +2 -2
  47. package/dist/{chunk-qz5b8ed4.js → chunk-bh19ezq0.js} +5 -5
  48. package/dist/chunk-bm42rsaa.js +2 -0
  49. package/dist/{chunk-cbjabv1w.js → chunk-bpj387yx.js} +6 -6
  50. package/dist/chunk-c4enpsj8.js +2 -0
  51. package/dist/chunk-cdbaanmx.js +2 -0
  52. package/dist/chunk-cfm6a679.js +2 -0
  53. package/dist/chunk-cs8wq7nv.js +2 -0
  54. package/dist/{chunk-hegfgw27.js → chunk-cxgremn0.js} +5 -5
  55. package/dist/chunk-d2xf5hh9.js +2 -0
  56. package/dist/{chunk-rym0309c.js → chunk-d4ch6exz.js} +3 -3
  57. package/dist/chunk-dda8pdya.js +2 -0
  58. package/dist/{chunk-7adznted.js → chunk-dera8fmc.js} +2 -2
  59. package/dist/chunk-dmd7mgjw.js +3 -0
  60. package/dist/chunk-ds91e0fe.js +2 -0
  61. package/dist/{chunk-hgry3mjd.js → chunk-dxh45gve.js} +1 -1
  62. package/dist/chunk-e492je6q.js +2 -0
  63. package/dist/{chunk-7j1zgrnf.js → chunk-ehzd1yc6.js} +9 -9
  64. package/dist/chunk-etz2dmec.js +2 -0
  65. package/dist/{chunk-4s8zacxg.js → chunk-f26n6y9p.js} +4 -4
  66. package/dist/{chunk-4w6baw62.js → chunk-fbrgz3s8.js} +2 -2
  67. package/dist/chunk-fd72t30s.js +2 -0
  68. package/dist/{chunk-sqn3y9qt.js → chunk-fdvfzknd.js} +2 -2
  69. package/dist/chunk-ft3f9nj0.js +2 -0
  70. package/dist/{chunk-dgc1a3yt.js → chunk-fzc98qgn.js} +7 -7
  71. package/dist/{chunk-s0vtjyyj.js → chunk-g12ht81a.js} +2 -2
  72. package/dist/chunk-g6gqja52.js +2 -0
  73. package/dist/{chunk-hb3edeq9.js → chunk-ga5kjf9b.js} +6 -6
  74. package/dist/chunk-ggwvnpwj.js +2 -0
  75. package/dist/{chunk-hsmcbh0p.js → chunk-ghm9f13q.js} +5 -5
  76. package/dist/chunk-gx6wxmbv.js +2 -0
  77. package/dist/chunk-gz5rvwgx.js +2 -0
  78. package/dist/chunk-hamk3dky.js +2 -0
  79. package/dist/{chunk-vayhx642.js → chunk-haz2ezyk.js} +2 -2
  80. package/dist/chunk-hh58k0nh.js +3 -0
  81. package/dist/chunk-hqj28381.js +3 -0
  82. package/dist/chunk-j60jye3e.js +25 -0
  83. package/dist/chunk-jjet6qxq.js +2 -0
  84. package/dist/chunk-jk7chxkt.js +2 -0
  85. package/dist/{chunk-d09q93yf.js → chunk-jv021x8e.js} +2 -2
  86. package/dist/{chunk-pg2v1pb5.js → chunk-k06n5bjp.js} +3 -3
  87. package/dist/{chunk-35ezh7de.js → chunk-k72jqz6a.js} +3 -3
  88. package/dist/chunk-kn34esxw.js +2 -0
  89. package/dist/chunk-kqbpkg8n.js +2 -0
  90. package/dist/{chunk-aet1pgw7.js → chunk-krcd3039.js} +74 -74
  91. package/dist/{chunk-asezs2n3.js → chunk-kxj8e1gd.js} +6 -6
  92. package/dist/{chunk-5zhp87wv.js → chunk-mabja59q.js} +2 -2
  93. package/dist/chunk-mf19qxgv.js +3 -0
  94. package/dist/chunk-mjsebtg6.js +2 -0
  95. package/dist/{chunk-fd49fahd.js → chunk-mn47s2ef.js} +5 -5
  96. package/dist/{chunk-nj343mw2.js → chunk-mq915g3j.js} +5 -5
  97. package/dist/chunk-n63r9b4f.js +2 -0
  98. package/dist/{chunk-j6m8xqh3.js → chunk-n8wvqnfc.js} +2 -2
  99. package/dist/chunk-n94ts3t9.js +21 -0
  100. package/dist/{chunk-w7t2cfgy.js → chunk-p27ptd9f.js} +65 -65
  101. package/dist/{chunk-t0k16vqv.js → chunk-p9az13b2.js} +2 -2
  102. package/dist/chunk-pwn0j1vq.js +2 -0
  103. package/dist/chunk-q1jcf6vm.js +2 -0
  104. package/dist/{chunk-am2w363p.js → chunk-q3gactm1.js} +4 -4
  105. package/dist/chunk-q3nnaxf9.js +2 -0
  106. package/dist/{chunk-w3d98907.js → chunk-q4rejyd2.js} +2 -2
  107. package/dist/{chunk-hz4x7h0n.js → chunk-qbzqary0.js} +5 -5
  108. package/dist/{chunk-a4p18ybc.js → chunk-qr7a378v.js} +3 -3
  109. package/dist/{chunk-bsajk82t.js → chunk-qwdcts9w.js} +19 -19
  110. package/dist/chunk-rc79yyqx.js +2 -0
  111. package/dist/{chunk-yx3dpdwc.js → chunk-rfc5pdv2.js} +3 -3
  112. package/dist/chunk-rnws0dh3.js +2 -0
  113. package/dist/chunk-rxxajm31.js +31 -0
  114. package/dist/{chunk-thceyzj6.js → chunk-rza1p6e4.js} +2 -2
  115. package/dist/chunk-s73z8zss.js +11 -0
  116. package/dist/chunk-s8qe247r.js +2 -0
  117. package/dist/{chunk-d1a86qx8.js → chunk-sptewgky.js} +1 -1
  118. package/dist/{chunk-mwnhdsbp.js → chunk-sqh3tvtw.js} +2 -2
  119. package/dist/{chunk-6s7tvmqp.js → chunk-sr3hmrah.js} +2 -2
  120. package/dist/chunk-sswy3tfc.js +2 -0
  121. package/dist/chunk-swxznm5v.js +2 -0
  122. package/dist/{chunk-9dtbvjpp.js → chunk-tmqrbs98.js} +31 -20
  123. package/dist/chunk-tst8p27s.js +12 -0
  124. package/dist/chunk-tz09earb.js +2 -0
  125. package/dist/{chunk-fbmth6xv.js → chunk-vay5v6sj.js} +13 -13
  126. package/dist/{chunk-ck2e3e47.js → chunk-vd5w1jaz.js} +3 -3
  127. package/dist/chunk-vewt3464.js +2 -0
  128. package/dist/{chunk-xghcsg3f.js → chunk-vg1pg6yt.js} +1 -1
  129. package/dist/{chunk-s2wptfm1.js → chunk-wav0xbnb.js} +2 -2
  130. package/dist/chunk-whzvx5w5.js +2 -0
  131. package/dist/chunk-wk1gt95z.js +2 -0
  132. package/dist/{chunk-t9kxhe46.js → chunk-wr1gm35v.js} +1 -1
  133. package/dist/{chunk-faakv1p6.js → chunk-wscnjtss.js} +2 -2
  134. package/dist/{chunk-t79jfpv6.js → chunk-x4q4bx3q.js} +2 -2
  135. package/dist/chunk-x63f2ywm.js +2 -0
  136. package/dist/{chunk-y1pcdeg9.js → chunk-x7wr720m.js} +5 -5
  137. package/dist/chunk-xk7cp691.js +2 -0
  138. package/dist/chunk-xna6b7km.js +2 -0
  139. package/dist/{chunk-v6peecfp.js → chunk-xnx06qqt.js} +2 -2
  140. package/dist/chunk-xw4rm5zj.js +2 -0
  141. package/dist/{chunk-5qtthtg3.js → chunk-y34tyrpb.js} +7 -7
  142. package/dist/chunk-yar7fwrb.js +2 -0
  143. package/dist/{chunk-2zzc7006.js → chunk-yn9kvsk1.js} +2 -2
  144. package/dist/chunk-yt33wzqk.js +2 -0
  145. package/dist/chunk-z35ftpqp.js +421 -0
  146. package/dist/{chunk-4bgjrrvz.js → chunk-z7ghjs7w.js} +1 -1
  147. package/dist/chunk-zq7xbc1k.js +2 -0
  148. package/dist/cli.js +22 -22
  149. package/dist/client/signals-for-runtime.js +16 -5
  150. package/dist/craft-entry.js +2 -2
  151. package/dist/craft.js +2 -2
  152. package/dist/native/compiler/headwind-to-style.js +13 -1
  153. package/dist/native/compiler/html-to-ir.d.ts +2 -0
  154. package/dist/native/compiler/html-to-ir.js +9 -0
  155. package/dist/native/compiler/ir.d.ts +1 -0
  156. package/dist/native/compiler/render-screen.js +4 -3
  157. package/dist/native/compiler/style-config.d.ts +3 -0
  158. package/dist/native/compiler/style-config.js +5 -0
  159. package/dist/native/components/primitives.d.ts +2 -0
  160. package/dist/native/event-names.js +2 -0
  161. package/dist/native/prop-names.js +1 -0
  162. package/dist/native/runtime/craft-bridge.js +15 -8
  163. package/dist/pwa.js +3 -3
  164. package/dist/signal-processing.js +75 -16
  165. package/dist/ssg-static-paths.d.ts +12 -0
  166. package/dist/ssg-static-paths.js +62 -0
  167. package/dist/ssg.d.ts +2 -4
  168. package/dist/ssg.js +43 -47
  169. package/dist/utils.js +1 -1
  170. package/package.json +2 -2
  171. package/dist/chunk-0jwdcqj8.js +0 -2
  172. package/dist/chunk-0mwdq478.js +0 -2
  173. package/dist/chunk-0nfqgv4n.js +0 -4
  174. package/dist/chunk-1y6egkty.js +0 -2
  175. package/dist/chunk-280p4q7a.js +0 -2
  176. package/dist/chunk-2881tcqt.js +0 -2
  177. package/dist/chunk-2a775q7x.js +0 -2
  178. package/dist/chunk-2asdbd5e.js +0 -2
  179. package/dist/chunk-34yj93hh.js +0 -2
  180. package/dist/chunk-40tswfe7.js +0 -21
  181. package/dist/chunk-41de209e.js +0 -2
  182. package/dist/chunk-506mv7hb.js +0 -3
  183. package/dist/chunk-5s1ay9mv.js +0 -2
  184. package/dist/chunk-7kznee1g.js +0 -2
  185. package/dist/chunk-7mvz972c.js +0 -12
  186. package/dist/chunk-80zgyaqe.js +0 -2
  187. package/dist/chunk-84tqb1rn.js +0 -2
  188. package/dist/chunk-8azm7nha.js +0 -2
  189. package/dist/chunk-902qnyg0.js +0 -2
  190. package/dist/chunk-9n6jwcpb.js +0 -2
  191. package/dist/chunk-a6y8td0d.js +0 -2
  192. package/dist/chunk-a7kt1ts4.js +0 -2
  193. package/dist/chunk-a81y8g40.js +0 -2
  194. package/dist/chunk-aft1zc84.js +0 -2
  195. package/dist/chunk-bawq4fqy.js +0 -31
  196. package/dist/chunk-bdp2r0v4.js +0 -2
  197. package/dist/chunk-brtvm8mb.js +0 -2
  198. package/dist/chunk-bvzr9zqt.js +0 -2
  199. package/dist/chunk-cbvgtf3m.js +0 -2
  200. package/dist/chunk-cfydbaeq.js +0 -2
  201. package/dist/chunk-dq8c3h4n.js +0 -2
  202. package/dist/chunk-e36pne7n.js +0 -2
  203. package/dist/chunk-e62qfexp.js +0 -2
  204. package/dist/chunk-eq5jzaj5.js +0 -2
  205. package/dist/chunk-f9zyf9n6.js +0 -421
  206. package/dist/chunk-fb7t1nn9.js +0 -24
  207. package/dist/chunk-fr39emcz.js +0 -2
  208. package/dist/chunk-fsq13amm.js +0 -2
  209. package/dist/chunk-fxh12vqd.js +0 -2
  210. package/dist/chunk-gcc9fa33.js +0 -2
  211. package/dist/chunk-h6t2ney6.js +0 -2
  212. package/dist/chunk-hvtzw1jh.js +0 -2
  213. package/dist/chunk-j8g5cvvm.js +0 -2
  214. package/dist/chunk-jzjpyyw2.js +0 -2
  215. package/dist/chunk-k1b8x73j.js +0 -2
  216. package/dist/chunk-nw9bdk2n.js +0 -2
  217. package/dist/chunk-p4jkswz8.js +0 -2
  218. package/dist/chunk-p4r90vqf.js +0 -2
  219. package/dist/chunk-p8bgy5tt.js +0 -2
  220. package/dist/chunk-pqqe2b6m.js +0 -11
  221. package/dist/chunk-q6nwcav4.js +0 -2
  222. package/dist/chunk-qmqavdz2.js +0 -2
  223. package/dist/chunk-qpw72bty.js +0 -2
  224. package/dist/chunk-qwnh1kj6.js +0 -2
  225. package/dist/chunk-qy9m6qdn.js +0 -2
  226. package/dist/chunk-qychcdsm.js +0 -2
  227. package/dist/chunk-rj3w1ama.js +0 -2
  228. package/dist/chunk-rv39s5az.js +0 -20
  229. package/dist/chunk-sf0r963g.js +0 -2
  230. package/dist/chunk-ss820yys.js +0 -2
  231. package/dist/chunk-t4j9g2vh.js +0 -2
  232. package/dist/chunk-trh7s6k3.js +0 -130
  233. package/dist/chunk-ttemwrbx.js +0 -2
  234. package/dist/chunk-twqzn21x.js +0 -14
  235. package/dist/chunk-v14rq8b4.js +0 -3
  236. package/dist/chunk-vdxg7e1y.js +0 -4
  237. package/dist/chunk-vq513cxx.js +0 -2
  238. package/dist/chunk-vvq8xe3n.js +0 -2
  239. package/dist/chunk-w8t31bn9.js +0 -2
  240. package/dist/chunk-x1kz4d7j.js +0 -2
  241. package/dist/chunk-xaa36b0x.js +0 -3
  242. package/dist/chunk-xcsyqwxf.js +0 -25
  243. package/dist/chunk-xn9jrjzh.js +0 -2
  244. package/dist/chunk-z97bwx8m.js +0 -2
@@ -1,6 +1,6 @@
1
1
  import { DENSITY_FONT_SIZES, DENSITY_SPACING, SEMANTIC_TOKENS, SHAPE_TOKENS, tokenVariable } from "../../theme-tokens.js";
2
2
  import { nativeDensitySpacing, nativeDensityType, nativeShapeRadius } from "./design-tokens.js";
3
- import { nativeBreakpoint, nativeShortcut, nativeThemeColor } from "./style-config.js";
3
+ import { nativeBreakpoint, nativeShortcut, nativeThemeColor, nativeThemeFont } from "./style-config.js";
4
4
  import { parseNativeStyle } from "./inline-style.js";
5
5
  const colors = {
6
6
  transparent: "transparent",
@@ -712,6 +712,18 @@ const classParsers = {
712
712
  "overflow-x-scroll": () => !0,
713
713
  "overflow-y-auto": () => !0,
714
714
  "overflow-y-scroll": () => !0,
715
+ "font-sans": (_, style) => {
716
+ style.fontFamily = nativeThemeFont("sans") ?? "sans-serif";
717
+ return !0;
718
+ },
719
+ "font-serif": (_, style) => {
720
+ style.fontFamily = nativeThemeFont("serif") ?? "serif";
721
+ return !0;
722
+ },
723
+ "font-mono": (_, style) => {
724
+ style.fontFamily = nativeThemeFont("mono") ?? "monospace";
725
+ return !0;
726
+ },
715
727
  "font-thin": (_, style) => {
716
728
  style.fontWeight = "100";
717
729
  return !0;
@@ -21,6 +21,8 @@ export declare function mustacheBinding(text: string): string | null;
21
21
  export declare function translateHtmlToIR(html: string, options?: TranslateOptions): Promise<TranslationResult>;
22
22
  /** The same translation, wrapped in an `STXDocument` for the renderers. */
23
23
  export declare function translateHtmlToDocument(html: string, options?: TranslateOptions & { script?: STXDocument['script'] }): Promise<{ document: STXDocument, diagnostics: TranslationDiagnostic[] }>;
24
+ /** Explicit website-only markup; unlike chrome this does not configure app navigation. */
25
+ export declare function excludeNativeSubtrees(html: string): Promise<string>;
24
26
  /**
25
27
  * Every event name at least one renderer dispatches on.
26
28
  *
@@ -22,6 +22,7 @@ export const NATIVE_EVENTS = new Set([
22
22
  "onEndReached",
23
23
  "onError",
24
24
  "onFocus",
25
+ "onPageSelected",
25
26
  "onLayout",
26
27
  "onLoad",
27
28
  "onLoadEnd",
@@ -248,6 +249,7 @@ function settle(frame, diagnostics) {
248
249
  return node;
249
250
  }
250
251
  export async function translateHtmlToIR(html, options = {}) {
252
+ html = await excludeNativeSubtrees(html);
251
253
  const diagnostics = [], stylesheetTheme = await extractStylesheetTheme(html, (name) => diagnostics.push({ kind: "unsupported-theme-rule", tag: "style", name })), stylesheetVariables = createStyleVariables(stylesheetTheme.light).computed(), usedClasses = new Set([...html.matchAll(/\sclass(?:name)?\s*=\s*"([^"]*)"/gi)].flatMap((match) => match[1].split(/\s+/))), stylesheetRules = await extractStylesheetRules(html, (name, classes) => {
252
254
  if (!classes.length || classes.some((name) => usedClasses.has(name)))
253
255
  diagnostics.push({ kind: "unsupported-style-rule", tag: "style", name });
@@ -566,3 +568,10 @@ export async function translateHtmlToDocument(html, options = {}) {
566
568
  diagnostics
567
569
  };
568
570
  }
571
+ export async function excludeNativeSubtrees(html) {
572
+ if (!/\bdata-native-exclude\b/i.test(html))
573
+ return html;
574
+ return new HTMLRewriter().on("[data-native-exclude]", { element(element) {
575
+ element.remove();
576
+ } }).transform(new Response(html)).text();
577
+ }
@@ -296,6 +296,7 @@ export type STXEventType = | 'onPress'
296
296
  | 'onMomentumScrollBegin'
297
297
  | 'onMomentumScrollEnd'
298
298
  // Layout events
299
+ | 'onPageSelected'
299
300
  | 'onLayout'
300
301
  // List events
301
302
  | 'onEndReached'
@@ -5,7 +5,7 @@ import { classifyAttribute, extractBindingManifest } from "../../binding-manifes
5
5
  import { processDirectives } from "../../process.js";
6
6
  import { processScriptSetup } from "../../signal-processing.js";
7
7
  import { appThemeCSS } from "./app-theme.js";
8
- import { translateHtmlToDocument } from "./html-to-ir.js";
8
+ import { excludeNativeSubtrees, translateHtmlToDocument } from "./html-to-ir.js";
9
9
  import { extractClientScript } from "./client-script.js";
10
10
  import { extractLayoutChrome } from "./layout-chrome.js";
11
11
  import { extractGeneratedSetupScript, extractNativeClientScripts } from "./setup-script.js";
@@ -20,6 +20,7 @@ export function nativePrimitivesDir() {
20
20
  return candidates[0];
21
21
  }
22
22
  export async function compileScreenSource(source, filePath, options = {}) {
23
+ source = await excludeNativeSubtrees(source);
23
24
  const root = options.root ?? path.dirname(filePath), loaded = await loadStxConfig(root), sourceRoot = typeof loaded.root === "string" && loaded.root ? path.resolve(root, loaded.root) : root, fromRoot = (dir, fallback) => dir ? path.isAbsolute(dir) ? dir : path.resolve(sourceRoot, dir) : path.join(sourceRoot, fallback), config = {
24
25
  root,
25
26
  layoutsDir: fromRoot(loaded.layoutsDir, "layouts"),
@@ -29,11 +30,11 @@ export async function compileScreenSource(source, filePath, options = {}) {
29
30
  buildMode: "serve",
30
31
  cache: !1,
31
32
  _targetComponentDirs: [nativePrimitivesDir()],
32
- _targetComponentFallbackNames: ["Button", "Checkbox", "Switch", "TextInput"],
33
+ _targetComponentFallbackNames: ["Button", "Checkbox", "Switch", "TextInput", "Video"],
33
34
  _nativeHostAttrs: !0,
34
35
  skipEventDirectives: !0,
35
36
  _pluginComponentDirs: loaded._pluginComponentDirs ?? []
36
- }, context = { ...options.context ?? {}, __stx_inject_css: !1, __stx_native_screen: !0 }, dependencies = new Set([filePath]), renderedHtml = await processDirectives(source, context, filePath, config, dependencies), { html: content, chrome } = await extractLayoutChrome(renderedHtml), { html, manifest } = extractBindingManifest(content), theme = await appThemeCSS(root), { document, diagnostics } = await translateHtmlToDocument(theme ? `<style>${theme}</style>${html}` : html, {
37
+ }, context = { ...options.context ?? {}, __stx_inject_css: !1, __stx_native_screen: !0 }, dependencies = new Set([filePath]), renderedHtml = await processDirectives(source, context, filePath, config, dependencies), { html: content, chrome } = await extractLayoutChrome(await excludeNativeSubtrees(renderedHtml)), { html, manifest } = extractBindingManifest(await excludeNativeSubtrees(content)), theme = await appThemeCSS(root), { document, diagnostics } = await translateHtmlToDocument(theme ? `<style>${theme}</style>${html}` : html, {
37
38
  source: filePath,
38
39
  script: extractClientScript(source)
39
40
  });
@@ -13,6 +13,8 @@ export declare function nativeShortcut(name: string): string | undefined;
13
13
  export declare function nativeThemeColor(name: string): string | undefined;
14
14
  /** A breakpoint's min-width in points, or undefined when `name` is not one. */
15
15
  export declare function nativeBreakpoint(name: string): number | undefined;
16
+ /** The app font stack; generic families are resolved by the platform host. */
17
+ export declare function nativeThemeFont(name: string): string | undefined;
16
18
  /**
17
19
  * The app's own utility vocabulary, for native styles.
18
20
  *
@@ -28,5 +30,6 @@ export declare const DEFAULT_BREAKPOINTS: Readonly<Record<string, number>>;
28
30
  export declare interface NativeStyleConfig {
29
31
  shortcuts?: Record<string, string>
30
32
  colors?: Record<string, unknown>
33
+ fontFamily?: Record<string, string | string[]>
31
34
  breakpoints?: Record<string, number>
32
35
  }
@@ -33,6 +33,7 @@ function resolve(config) {
33
33
  return {
34
34
  shortcuts: new Map(Object.entries(config.shortcuts ?? {}).filter(([, value]) => typeof value === "string")),
35
35
  colors: flattenColors(config.colors),
36
+ fontFamily: new Map(Object.entries(config.fontFamily ?? {}).map(([name, value]) => [name, Array.isArray(value) ? value.join(", ") : value])),
36
37
  breakpoints: new Map(Object.entries({ ...DEFAULT_BREAKPOINTS, ...breakpointsOf(config.breakpoints) }))
37
38
  };
38
39
  }
@@ -56,6 +57,7 @@ export function nativeStyleConfigFrom(cssConfig) {
56
57
  const theme = cssConfig.theme ?? {}, shortcuts = Object.fromEntries(Object.entries(cssConfig.shortcuts ?? {}).filter(([, value]) => typeof value === "string"));
57
58
  return {
58
59
  ...Object.keys(shortcuts).length ? { shortcuts } : {},
60
+ ...theme.fontFamily ? { fontFamily: JSON.parse(JSON.stringify(theme.fontFamily)) } : {},
59
61
  ...theme.colors ? { colors: JSON.parse(JSON.stringify(theme.colors)) } : {},
60
62
  ...theme.screens ? { breakpoints: breakpointsOf(theme.screens) } : {}
61
63
  };
@@ -69,3 +71,6 @@ export function nativeThemeColor(name) {
69
71
  export function nativeBreakpoint(name) {
70
72
  return active().breakpoints.get(name);
71
73
  }
74
+ export function nativeThemeFont(name) {
75
+ return active().fontFamily.get(name);
76
+ }
@@ -81,6 +81,8 @@ export declare interface ScrollViewProps extends ViewProps {
81
81
  refreshControl?: RefreshControlProps
82
82
  stickyHeaderIndices?: number[]
83
83
  scrollTarget?: string | { id: string, key?: string | number, animated?: boolean } | null
84
+ pageIndex?: number
85
+ onPageSelected?: (event: { nativeEvent: { index: number, count: number } }) => void
84
86
  pagingEnabled?: boolean
85
87
  scrollEnabled?: boolean
86
88
  }
@@ -32,6 +32,8 @@ export const NATIVE_EVENT_NAMES = {
32
32
  momentumscrollbegin: "onMomentumScrollBegin",
33
33
  "momentum-scroll-end": "onMomentumScrollEnd",
34
34
  momentumscrollend: "onMomentumScrollEnd",
35
+ "page-selected": "onPageSelected",
36
+ pageselected: "onPageSelected",
35
37
  refresh: "onRefresh",
36
38
  layout: "onLayout",
37
39
  "end-reached": "onEndReached",
@@ -43,6 +43,7 @@ const NATIVE_PROP_NAMES = [
43
43
  "numberOfLines",
44
44
  "numColumns",
45
45
  "onEndReachedThreshold",
46
+ "pageIndex",
46
47
  "pagingEnabled",
47
48
  "passwordRules",
48
49
  "placeholderTextColor",
@@ -1,9 +1,10 @@
1
- import { STXBridge } from "../bridge/protocol.js";
1
+ import { NativeCapabilityError, STXBridge } from "../bridge/protocol.js";
2
2
  import { createEventSourceClass, createStreamingBody } from "./event-stream.js";
3
3
  import { abortError, isNativeDocument, NativeFormData, nativeFormParts, updateNativeDocumentVisibility } from "./jsc-globals.js";
4
4
  import { buttonsKey, changedTabItems, chromePath, hostButton, mergeChromeButtons } from "./navigation-chrome.js";
5
5
  import { installNativeNavigation, nativeRouteParams } from "./web-navigation.js";
6
6
  import { installNativeWebStorage } from "./web-storage.js";
7
+ const permissionFallbacks = new WeakSet;
7
8
  function capabilityFlags(capabilities) {
8
9
  return {
9
10
  desktop: capabilities.has("desktop"),
@@ -549,13 +550,6 @@ export function installCraftBridge(protocol, nativeBridge, target = globalThis,
549
550
  openSettings: () => request("Push", "openSettings", []).then(Boolean)
550
551
  };
551
552
  craft.registerPush = craft.push.register;
552
- craft.permissions = Object.assign({}, craft.permissions, {
553
- check: (permission) => permission === "notifications" ? craft.push.getPermissionStatus() : Promise.resolve("undetermined"),
554
- request: (permission) => permission === "notifications" ? craft.push.requestPermission() : Promise.resolve("undetermined"),
555
- openSettings: () => craft.push.openSettings().then(() => {
556
- return;
557
- })
558
- });
559
553
  protocol.on("PUSH_TOKEN", ({ payload }) => {
560
554
  const token = String(payload?.token || "");
561
555
  if (!token)
@@ -565,6 +559,19 @@ export function installCraftBridge(protocol, nativeBridge, target = globalThis,
565
559
  target.dispatchEvent(new target.CustomEvent("craftPushToken", { detail: { token } }));
566
560
  });
567
561
  }
562
+ const permissions = craft.permissions ??= {}, unsupportedPermission = (method, permission) => Promise.reject(new NativeCapabilityError("PERMISSION_UNSUPPORTED", `Native permissions.${method}${permission === void 0 ? "" : `("${permission}")`} is not supported by this host`)), permissionDefaults = {
563
+ check: (permission) => capabilities.has("push") && permission === "notifications" ? craft.push.getPermissionStatus() : unsupportedPermission("check", permission),
564
+ request: (permission) => capabilities.has("push") && permission === "notifications" ? craft.push.requestPermission() : unsupportedPermission("request", permission),
565
+ openSettings: () => capabilities.has("push") ? craft.push.openSettings().then(() => {
566
+ return;
567
+ }) : unsupportedPermission("openSettings")
568
+ };
569
+ for (const [method, fallback] of Object.entries(permissionDefaults)) {
570
+ if (typeof permissions[method] === "function" && !permissionFallbacks.has(permissions[method]))
571
+ continue;
572
+ permissions[method] = fallback;
573
+ permissionFallbacks.add(fallback);
574
+ }
568
575
  if (capabilities.has("health")) {
569
576
  craft.health = {
570
577
  requestAuthorization: (types, options = {}) => ask("Health", "requestAuthorization", [types, options]),
package/dist/pwa.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @bun
2
- import{Ts,jt,vc,mu}from"./chunk-6sjtyd0k.js";import{Es,Jo,pu,fu,gu}from"./chunk-dgc1a3yt.js";import{hc,yc,bc,uu,du}from"./chunk-d09q93yf.js";import{ze,wn,Yo}from"./chunk-35ezh7de.js";import{hu,yu,bu,vu,$s,Gp}from"./chunk-5zhp87wv.js";import B from"fs";import w from"path";import O from"fs";import u from"path";var v=[72,96,128,144,152,192,384,512],S=[120,152,167,180],P=[16,32];async function M(n,t){let e={success:!0,generatedFiles:[],errors:[],warnings:[]},r=n.pwa?.icons;if(!r?.src)return e.errors.push("No source icon specified in pwa.icons.src"),e.success=!1,e;let o=u.isAbsolute(r.src)?r.src:u.resolve(process.cwd(),r.src);if(!await Bun.file(o).exists())return e.errors.push(`Source icon not found: ${o}`),e.success=!1,e;let a=u.resolve(t,r.outputDir||"pwa-icons");try{O.mkdirSync(a,{recursive:!0})}catch{return e.errors.push(`Failed to create output directory: ${a}`),e.success=!1,e}let s=r.sizes||v,l=r.generateWebP??!0,p=r.generateAppleIcons??!0;try{let c;try{c=await import("sharp")}catch{return e.errors.push(`sharp is not installed. Install it with: bun add sharp
3
- Icon generation requires the sharp package for image processing.`),e.success=!1,e}let g=await c.default(o).metadata();if(g.width&&g.width<512)e.warnings.push(`Source icon is ${g.width}x${g.height}. Recommend using at least 512x512 for best quality.`);for(let i of s){let d=u.join(a,`icon-${i}x${i}.png`);if(await c.default(o).resize(i,i,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(d),e.generatedFiles.push(d),l){let x=u.join(a,`icon-${i}x${i}.webp`);await c.default(o).resize(i,i,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).webp({quality:90}).toFile(x),e.generatedFiles.push(x)}}if(p){for(let d of S){let x=u.join(a,`apple-touch-icon-${d}x${d}.png`);await c.default(o).resize(d,d,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile(x),e.generatedFiles.push(x)}let i=u.join(a,"apple-touch-icon.png");await c.default(o).resize(180,180,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile(i),e.generatedFiles.push(i)}for(let i of P){let d=u.join(a,`favicon-${i}x${i}.png`);await c.default(o).resize(i,i,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(d),e.generatedFiles.push(d)}let f=512,y=u.join(a,`icon-maskable-${f}x${f}.png`),m=Math.floor(f*0.1),k=f-m*2;await c.default(o).resize(k,k,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).extend({top:m,bottom:m,left:m,right:m,background:{r:255,g:255,b:255,alpha:1}}).png().toFile(y),e.generatedFiles.push(y)}catch(c){let h=c instanceof Error?c.message:String(c);e.errors.push(`Icon generation failed: ${h}`),e.success=!1}return e}function N(n){let t=n.sizes||v,e=n.outputDir?`/${n.outputDir}`:"/pwa-icons",r=n.generateWebP??!0,o=n.generateAppleIcons??!0,a=[];for(let s of t)if(a.push(`${e}/icon-${s}x${s}.png`),r)a.push(`${e}/icon-${s}x${s}.webp`);if(o){a.push(`${e}/apple-touch-icon.png`);for(let s of S)a.push(`${e}/apple-touch-icon-${s}x${s}.png`)}for(let s of P)a.push(`${e}/favicon-${s}x${s}.png`);return a.push(`${e}/icon-maskable-512x512.png`),a}async function I(){try{return await import("sharp"),!0}catch{return!1}}function A(n,t,e,r){let o=[],a=r.join(" ");for(let s of n)if(o.push({src:`${t}/icon-${s}x${s}.png`,sizes:`${s}x${s}`,type:"image/png",purpose:a}),e)o.push({src:`${t}/icon-${s}x${s}.webp`,sizes:`${s}x${s}`,type:"image/webp",purpose:a});return o}function C(n){return n.map((t)=>({name:t.name,short_name:t.shortName,description:t.description,url:t.url,icons:t.icons?.map((e)=>({src:e.src,sizes:e.sizes,type:e.type}))}))}function T(n){let t=n.pwa?.shareTarget;if(!t?.enabled)return;let e={action:t.action||"/share",method:t.method||"POST",enctype:t.enctype||"multipart/form-data",params:{title:t.params?.title||"title",text:t.params?.text||"text",url:t.params?.url||"url"}};if(t.acceptFiles&&t.acceptFiles.length>0)e.params.files=t.acceptFiles.map((r)=>({name:r.name,accept:r.accept}));return e}function H(n){let t=n.pwa?.fileHandlers;if(!t?.enabled||!t.accept)return;let e=[{action:t.action||"/",accept:t.accept,launch_type:t.launchType||"single-client"}];if(t.icons)e[0].icons=t.icons.map((r)=>({src:r.src,sizes:r.sizes,type:r.type}));return e}function W(n){let t=n.pwa?.protocolHandlers;if(!t||t.length===0)return;return t.map((e)=>({protocol:e.protocol,url:e.url}))}function j(n){let t=n.pwa;if(!t?.enabled||!t.manifest)return null;let{manifest:e,icons:r}=t,o=r?.sizes||[72,96,128,144,152,192,384,512],a=r?.outputDir?`/${r.outputDir}`:"/pwa-icons",s=r?.generateWebP??!0,l=r?.purpose||["any","maskable"],p={name:e.name,short_name:e.shortName||e.name,description:e.description,start_url:e.startUrl||"/",display:e.display||"standalone",display_override:e.displayOverride,orientation:e.orientation,theme_color:e.themeColor,background_color:e.backgroundColor,scope:e.scope||"/",lang:e.lang,dir:e.dir,id:e.id,categories:e.categories,icons:A(o,a,s,l)};if(e.shortcuts&&e.shortcuts.length>0)p.shortcuts=C(e.shortcuts);if(e.screenshots&&e.screenshots.length>0)p.screenshots=e.screenshots.map((f)=>({src:f.src,sizes:f.sizes,type:f.type,label:f.label,form_factor:f.platform}));let c=T(n);if(c)p.share_target=c;let h=H(n);if(h)p.file_handlers=h;let g=W(n);if(g)p.protocol_handlers=g;if(e.launchHandler)p.launch_handler={client_mode:e.launchHandler};if(e.handleLinks)p.handle_links=e.handleLinks;if(e.edgeSidePanel?.enabled)p.edge_side_panel={preferred_width:e.edgeSidePanel.preferredWidth};return p}function _(n){let t=j(n);if(!t)return"";let e=JSON.parse(JSON.stringify(t));return JSON.stringify(e,null,2)}function q(n,t){return{...t,...n,shortcuts:n.shortcuts||t.shortcuts,screenshots:n.screenshots||t.screenshots,categories:n.categories||t.categories}}function D(n){let t=[],e=n.pwa;if(!e?.enabled)return t;let r=e.manifest;if(!r)return t.push("PWA manifest configuration is missing"),t;if(!r.name)t.push("Manifest name is required");let o=["fullscreen","standalone","minimal-ui","browser"];if(r.display&&!o.includes(r.display))t.push(`Invalid display mode: ${r.display}. Must be one of: ${o.join(", ")}`);let a=["any","natural","landscape","landscape-primary","landscape-secondary","portrait","portrait-primary","portrait-secondary"];if(r.orientation&&!a.includes(r.orientation))t.push(`Invalid orientation: ${r.orientation}`);let s=/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;if(r.themeColor&&!s.test(r.themeColor))t.push(`Invalid theme color: ${r.themeColor}. Must be a valid hex color.`);if(r.backgroundColor&&!s.test(r.backgroundColor))t.push(`Invalid background color: ${r.backgroundColor}. Must be a valid hex color.`);if(e.shareTarget?.enabled){if(!e.shareTarget.action)t.push("Share target action URL is required")}if(e.fileHandlers?.enabled){if(!e.fileHandlers.accept||Object.keys(e.fileHandlers.accept).length===0)t.push("File handlers must specify accepted MIME types")}if(e.protocolHandlers)for(let l of e.protocolHandlers){if(!l.protocol)t.push("Protocol handler missing protocol");if(!l.url||!l.url.includes("%s"))t.push(`Protocol handler URL must contain %s placeholder: ${l.protocol}`)}return t}import z from"path";async function F(n){let t=n.pwa?.offline;if(!t?.enabled)return"";if(t.page){let e=z.isAbsolute(t.page)?t.page:z.resolve(process.cwd(),t.page);if(await Bun.file(e).exists())try{let{processDirectives:r}=await import("./chunk-jzjpyyw2.js"),o=await Bun.file(e).text();return await r(o,{},e,n,new Set)}catch(r){console.warn(`[PWA] Failed to process custom offline page: ${r}`)}else console.warn(`[PWA] Custom offline page not found: ${e}`)}return E(n)}function E(n){let t=n.pwa?.offline,e=n.pwa?.manifest,r=t?.fallbackTitle||"You are offline",o=t?.fallbackMessage||"Please check your internet connection and try again.",a=e?.themeColor||"#4f46e5",s=e?.shortName||e?.name||"App";return`<!DOCTYPE html>
2
+ import{Rs,Bt,xc,yu}from"./chunk-5z8mq37c.js";import{$s,Zo,gu,mu,hu}from"./chunk-fzc98qgn.js";import{bc,vc,wc,pu,fu}from"./chunk-jv021x8e.js";import{He,Sn,Qo}from"./chunk-k72jqz6a.js";import{bu,vu,wu,xu,As,Xp}from"./chunk-mabja59q.js";import B from"fs";import w from"path";import O from"fs";import u from"path";var v=[72,96,128,144,152,192,384,512],S=[120,152,167,180],P=[16,32];async function M(n,t){let e={success:!0,generatedFiles:[],errors:[],warnings:[]},r=n.pwa?.icons;if(!r?.src)return e.errors.push("No source icon specified in pwa.icons.src"),e.success=!1,e;let o=u.isAbsolute(r.src)?r.src:u.resolve(process.cwd(),r.src);if(!await Bun.file(o).exists())return e.errors.push(`Source icon not found: ${o}`),e.success=!1,e;let a=u.resolve(t,r.outputDir||"pwa-icons");try{O.mkdirSync(a,{recursive:!0})}catch{return e.errors.push(`Failed to create output directory: ${a}`),e.success=!1,e}let s=r.sizes||v,l=r.generateWebP??!0,p=r.generateAppleIcons??!0;try{let c;try{c=await import("sharp")}catch{return e.errors.push(`sharp is not installed. Install it with: bun add sharp
3
+ Icon generation requires the sharp package for image processing.`),e.success=!1,e}let g=await c.default(o).metadata();if(g.width&&g.width<512)e.warnings.push(`Source icon is ${g.width}x${g.height}. Recommend using at least 512x512 for best quality.`);for(let i of s){let d=u.join(a,`icon-${i}x${i}.png`);if(await c.default(o).resize(i,i,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(d),e.generatedFiles.push(d),l){let x=u.join(a,`icon-${i}x${i}.webp`);await c.default(o).resize(i,i,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).webp({quality:90}).toFile(x),e.generatedFiles.push(x)}}if(p){for(let d of S){let x=u.join(a,`apple-touch-icon-${d}x${d}.png`);await c.default(o).resize(d,d,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile(x),e.generatedFiles.push(x)}let i=u.join(a,"apple-touch-icon.png");await c.default(o).resize(180,180,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile(i),e.generatedFiles.push(i)}for(let i of P){let d=u.join(a,`favicon-${i}x${i}.png`);await c.default(o).resize(i,i,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(d),e.generatedFiles.push(d)}let f=512,y=u.join(a,`icon-maskable-${f}x${f}.png`),m=Math.floor(f*0.1),k=f-m*2;await c.default(o).resize(k,k,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).extend({top:m,bottom:m,left:m,right:m,background:{r:255,g:255,b:255,alpha:1}}).png().toFile(y),e.generatedFiles.push(y)}catch(c){let h=c instanceof Error?c.message:String(c);e.errors.push(`Icon generation failed: ${h}`),e.success=!1}return e}function N(n){let t=n.sizes||v,e=n.outputDir?`/${n.outputDir}`:"/pwa-icons",r=n.generateWebP??!0,o=n.generateAppleIcons??!0,a=[];for(let s of t)if(a.push(`${e}/icon-${s}x${s}.png`),r)a.push(`${e}/icon-${s}x${s}.webp`);if(o){a.push(`${e}/apple-touch-icon.png`);for(let s of S)a.push(`${e}/apple-touch-icon-${s}x${s}.png`)}for(let s of P)a.push(`${e}/favicon-${s}x${s}.png`);return a.push(`${e}/icon-maskable-512x512.png`),a}async function I(){try{return await import("sharp"),!0}catch{return!1}}function A(n,t,e,r){let o=[],a=r.join(" ");for(let s of n)if(o.push({src:`${t}/icon-${s}x${s}.png`,sizes:`${s}x${s}`,type:"image/png",purpose:a}),e)o.push({src:`${t}/icon-${s}x${s}.webp`,sizes:`${s}x${s}`,type:"image/webp",purpose:a});return o}function C(n){return n.map((t)=>({name:t.name,short_name:t.shortName,description:t.description,url:t.url,icons:t.icons?.map((e)=>({src:e.src,sizes:e.sizes,type:e.type}))}))}function T(n){let t=n.pwa?.shareTarget;if(!t?.enabled)return;let e={action:t.action||"/share",method:t.method||"POST",enctype:t.enctype||"multipart/form-data",params:{title:t.params?.title||"title",text:t.params?.text||"text",url:t.params?.url||"url"}};if(t.acceptFiles&&t.acceptFiles.length>0)e.params.files=t.acceptFiles.map((r)=>({name:r.name,accept:r.accept}));return e}function H(n){let t=n.pwa?.fileHandlers;if(!t?.enabled||!t.accept)return;let e=[{action:t.action||"/",accept:t.accept,launch_type:t.launchType||"single-client"}];if(t.icons)e[0].icons=t.icons.map((r)=>({src:r.src,sizes:r.sizes,type:r.type}));return e}function W(n){let t=n.pwa?.protocolHandlers;if(!t||t.length===0)return;return t.map((e)=>({protocol:e.protocol,url:e.url}))}function j(n){let t=n.pwa;if(!t?.enabled||!t.manifest)return null;let{manifest:e,icons:r}=t,o=r?.sizes||[72,96,128,144,152,192,384,512],a=r?.outputDir?`/${r.outputDir}`:"/pwa-icons",s=r?.generateWebP??!0,l=r?.purpose||["any","maskable"],p={name:e.name,short_name:e.shortName||e.name,description:e.description,start_url:e.startUrl||"/",display:e.display||"standalone",display_override:e.displayOverride,orientation:e.orientation,theme_color:e.themeColor,background_color:e.backgroundColor,scope:e.scope||"/",lang:e.lang,dir:e.dir,id:e.id,categories:e.categories,icons:A(o,a,s,l)};if(e.shortcuts&&e.shortcuts.length>0)p.shortcuts=C(e.shortcuts);if(e.screenshots&&e.screenshots.length>0)p.screenshots=e.screenshots.map((f)=>({src:f.src,sizes:f.sizes,type:f.type,label:f.label,form_factor:f.platform}));let c=T(n);if(c)p.share_target=c;let h=H(n);if(h)p.file_handlers=h;let g=W(n);if(g)p.protocol_handlers=g;if(e.launchHandler)p.launch_handler={client_mode:e.launchHandler};if(e.handleLinks)p.handle_links=e.handleLinks;if(e.edgeSidePanel?.enabled)p.edge_side_panel={preferred_width:e.edgeSidePanel.preferredWidth};return p}function _(n){let t=j(n);if(!t)return"";let e=JSON.parse(JSON.stringify(t));return JSON.stringify(e,null,2)}function q(n,t){return{...t,...n,shortcuts:n.shortcuts||t.shortcuts,screenshots:n.screenshots||t.screenshots,categories:n.categories||t.categories}}function D(n){let t=[],e=n.pwa;if(!e?.enabled)return t;let r=e.manifest;if(!r)return t.push("PWA manifest configuration is missing"),t;if(!r.name)t.push("Manifest name is required");let o=["fullscreen","standalone","minimal-ui","browser"];if(r.display&&!o.includes(r.display))t.push(`Invalid display mode: ${r.display}. Must be one of: ${o.join(", ")}`);let a=["any","natural","landscape","landscape-primary","landscape-secondary","portrait","portrait-primary","portrait-secondary"];if(r.orientation&&!a.includes(r.orientation))t.push(`Invalid orientation: ${r.orientation}`);let s=/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;if(r.themeColor&&!s.test(r.themeColor))t.push(`Invalid theme color: ${r.themeColor}. Must be a valid hex color.`);if(r.backgroundColor&&!s.test(r.backgroundColor))t.push(`Invalid background color: ${r.backgroundColor}. Must be a valid hex color.`);if(e.shareTarget?.enabled){if(!e.shareTarget.action)t.push("Share target action URL is required")}if(e.fileHandlers?.enabled){if(!e.fileHandlers.accept||Object.keys(e.fileHandlers.accept).length===0)t.push("File handlers must specify accepted MIME types")}if(e.protocolHandlers)for(let l of e.protocolHandlers){if(!l.protocol)t.push("Protocol handler missing protocol");if(!l.url||!l.url.includes("%s"))t.push(`Protocol handler URL must contain %s placeholder: ${l.protocol}`)}return t}import z from"path";async function F(n){let t=n.pwa?.offline;if(!t?.enabled)return"";if(t.page){let e=z.isAbsolute(t.page)?t.page:z.resolve(process.cwd(),t.page);if(await Bun.file(e).exists())try{let{processDirectives:r}=await import("./chunk-wk1gt95z.js"),o=await Bun.file(e).text();return await r(o,{},e,n,new Set)}catch(r){console.warn(`[PWA] Failed to process custom offline page: ${r}`)}else console.warn(`[PWA] Custom offline page not found: ${e}`)}return E(n)}function E(n){let t=n.pwa?.offline,e=n.pwa?.manifest,r=t?.fallbackTitle||"You are offline",o=t?.fallbackMessage||"Please check your internet connection and try again.",a=e?.themeColor||"#4f46e5",s=e?.shortName||e?.name||"App";return`<!DOCTYPE html>
4
4
  <html lang="en">
5
5
  <head>
6
6
  <meta charset="UTF-8">
@@ -233,4 +233,4 @@ Icon generation requires the sharp package for image processing.`),e.success=!1,
233
233
  }, 5000);
234
234
  </script>
235
235
  </body>
236
- </html>`}function b(n){return n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}async function re(n,t){let e={success:!0,files:{icons:[]},errors:[],warnings:[]},r=n.pwa;if(!r?.enabled)return e;try{B.mkdirSync(t,{recursive:!0})}catch{return e.errors.push(`Failed to create output directory: ${t}`),e.success=!1,e}try{let a=_(n);if(a){let s=w.join(t,"manifest.json");await Bun.write(s,a),e.files.manifest=s}}catch(a){let s=a instanceof Error?a.message:String(a);e.errors.push(`Failed to generate manifest.json: ${s}`),e.success=!1}if(r.icons?.src)if(await I()){let s=await M(n,t);if(e.files.icons=s.generatedFiles,e.warnings.push(...s.warnings),!s.success)e.errors.push(...s.errors),e.success=!1}else e.warnings.push("sharp is not installed. Skipping icon generation. Install with: bun add sharp");if(r.offline?.enabled)try{let a=await F(n);if(a){let s=w.join(t,"offline.html");await Bun.write(s,a),e.files.offlinePage=s}}catch(a){let s=a instanceof Error?a.message:String(a);e.errors.push(`Failed to generate offline page: ${s}`),e.success=!1}let o;if(r.precache?.enabled){let a=ze(t,n);o={fileCount:a.fileCount,totalSize:a.totalSize},e.precacheStats=o}try{let a=Ts(n,t);if(a){let s=jt(n),l=w.join(t,s);await Bun.write(l,a),e.files.serviceWorker=l}}catch(a){let s=a instanceof Error?a.message:String(a);e.errors.push(`Failed to generate service worker: ${s}`),e.success=!1}return e}function ae(n,t){return Jo(n,t)}function se(n){return n.pwa?.enabled===!0}export{S as APPLE_TOUCH_ICON_SIZES,v as DEFAULT_ICON_SIZES,P as FAVICON_SIZES,yc as auditBuildOutput,hc as auditConfig,re as buildPwaAssets,bc as calculateAuditResult,du as formatAuditResult,Yo as formatSize,j as generateManifest,_ as generateManifestJson,fu as generateManifestLink,F as generateOfflinePage,ze as generatePrecacheManifest,wn as generatePrecacheManifestJs,M as generatePwaIcons,Es as generatePwaTags,Ts as generateServiceWorker,mu as generateServiceWorkerAuto,pu as generateSwRegistrationScript,gu as generateThemeColorMeta,N as getExpectedIconPaths,jt as getServiceWorkerFileName,Jo as injectPwaTags,se as isPwaEnabled,I as isSharpAvailable,vc as isWorkboxEnabled,q as mergeManifestConfig,ae as processPwaHtml,$s as pwaDirectives,hu as pwaInstallDirective,bu as pwaOfflineDirective,vu as pwaPushDirective,yu as pwaUpdateDirective,Gp as registerPwaDirectives,uu as runPwaAudit,D as validateManifest};
236
+ </html>`}function b(n){return n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}async function re(n,t){let e={success:!0,files:{icons:[]},errors:[],warnings:[]},r=n.pwa;if(!r?.enabled)return e;try{B.mkdirSync(t,{recursive:!0})}catch{return e.errors.push(`Failed to create output directory: ${t}`),e.success=!1,e}try{let a=_(n);if(a){let s=w.join(t,"manifest.json");await Bun.write(s,a),e.files.manifest=s}}catch(a){let s=a instanceof Error?a.message:String(a);e.errors.push(`Failed to generate manifest.json: ${s}`),e.success=!1}if(r.icons?.src)if(await I()){let s=await M(n,t);if(e.files.icons=s.generatedFiles,e.warnings.push(...s.warnings),!s.success)e.errors.push(...s.errors),e.success=!1}else e.warnings.push("sharp is not installed. Skipping icon generation. Install with: bun add sharp");if(r.offline?.enabled)try{let a=await F(n);if(a){let s=w.join(t,"offline.html");await Bun.write(s,a),e.files.offlinePage=s}}catch(a){let s=a instanceof Error?a.message:String(a);e.errors.push(`Failed to generate offline page: ${s}`),e.success=!1}let o;if(r.precache?.enabled){let a=He(t,n);o={fileCount:a.fileCount,totalSize:a.totalSize},e.precacheStats=o}try{let a=Rs(n,t);if(a){let s=Bt(n),l=w.join(t,s);await Bun.write(l,a),e.files.serviceWorker=l}}catch(a){let s=a instanceof Error?a.message:String(a);e.errors.push(`Failed to generate service worker: ${s}`),e.success=!1}return e}function ae(n,t){return Zo(n,t)}function se(n){return n.pwa?.enabled===!0}export{S as APPLE_TOUCH_ICON_SIZES,v as DEFAULT_ICON_SIZES,P as FAVICON_SIZES,vc as auditBuildOutput,bc as auditConfig,re as buildPwaAssets,wc as calculateAuditResult,fu as formatAuditResult,Qo as formatSize,j as generateManifest,_ as generateManifestJson,mu as generateManifestLink,F as generateOfflinePage,He as generatePrecacheManifest,Sn as generatePrecacheManifestJs,M as generatePwaIcons,$s as generatePwaTags,Rs as generateServiceWorker,yu as generateServiceWorkerAuto,gu as generateSwRegistrationScript,hu as generateThemeColorMeta,N as getExpectedIconPaths,Bt as getServiceWorkerFileName,Zo as injectPwaTags,se as isPwaEnabled,I as isSharpAvailable,xc as isWorkboxEnabled,q as mergeManifestConfig,ae as processPwaHtml,As as pwaDirectives,bu as pwaInstallDirective,wu as pwaOfflineDirective,xu as pwaPushDirective,vu as pwaUpdateDirective,Xp as registerPwaDirectives,pu as runPwaAudit,D as validateManifest};
@@ -447,32 +447,91 @@ function nextSiblingHasElse(template, fromIndex) {
447
447
  export function preEvalLiteralReactiveIfs(template) {
448
448
  if (!template.includes(":if") && !template.includes(":show") && !template.includes("x-if") && !template.includes("x-show") && !template.includes("@if=") && !template.includes("@show="))
449
449
  return template;
450
- const skipRanges = [], rawTagRe = /<(script|style)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
451
- let rawMatch;
452
- while ((rawMatch = rawTagRe.exec(template)) !== null)
453
- skipRanges.push([rawMatch.index, rawMatch.index + rawMatch[0].length]);
454
- const inSkipRange = (i) => skipRanges.some(([s, e]) => i >= s && i < e), tagWithLiteralIf = /<([a-zA-Z][\w-]*)\b((?:\s+[^=\s>]+(?:=(?:"[^"]*"|'[^']*'|[^\s>]*))?)*?)\s+(:if|:show|x-if|x-show|@if|@show)\s*=\s*"([^"]*)"((?:\s+[^=\s>]+(?:=(?:"[^"]*"|'[^']*'|[^\s>]*))?)*\s*)(\/?)>/g, replacements = [];
455
- let m;
456
- while ((m = tagWithLiteralIf.exec(template)) !== null) {
457
- if (inSkipRange(m.index))
458
- continue;
459
- const [full, tag, beforeAttrs, attrName, value, afterAttrs, selfClose] = m, verdict = evalLiteralCondition(value);
450
+ const candidates = [], tagName = /<([a-zA-Z][\w-]*)/y, whitespace = /\s/, conditionalNames = new Set([":if", ":show", "x-if", "x-show", "@if", "@show"]);
451
+ scanAtElementPosition(template, (html, start) => {
452
+ tagName.lastIndex = start;
453
+ const name = tagName.exec(html);
454
+ if (!name || /^(?:script|style)$/i.test(name[1]))
455
+ return -1;
456
+ let i = tagName.lastIndex, conditional;
457
+ while (i < html.length) {
458
+ const beforeSpace = i;
459
+ while (i < html.length && whitespace.test(html[i]))
460
+ i++;
461
+ let closing = i;
462
+ if (html[closing] === "/") {
463
+ closing++;
464
+ while (closing < html.length && whitespace.test(html[closing]))
465
+ closing++;
466
+ }
467
+ if (html[closing] === ">") {
468
+ if (conditional)
469
+ candidates.push({ start, end: closing + 1, tag: name[1], ...conditional });
470
+ return -1;
471
+ }
472
+ if (i === beforeSpace)
473
+ return -1;
474
+ const attrStart = i;
475
+ while (i < html.length && !whitespace.test(html[i]) && html[i] !== "=" && html[i] !== ">")
476
+ i++;
477
+ if (i === attrStart)
478
+ return -1;
479
+ const attrName = html.slice(attrStart, i);
480
+ if (attrName.startsWith("@") && attrName.includes("("))
481
+ return -1;
482
+ const nameEnd = i;
483
+ let valueStart = i;
484
+ while (valueStart < html.length && whitespace.test(html[valueStart]))
485
+ valueStart++;
486
+ if (html[valueStart] !== "=") {
487
+ i = nameEnd;
488
+ continue;
489
+ }
490
+ valueStart++;
491
+ while (valueStart < html.length && whitespace.test(html[valueStart]))
492
+ valueStart++;
493
+ const quote = html[valueStart];
494
+ if (quote === '"' || quote === "'") {
495
+ const end = html.indexOf(quote, valueStart + 1);
496
+ if (end === -1)
497
+ return -1;
498
+ if (!conditional && quote === '"' && conditionalNames.has(attrName))
499
+ conditional = {
500
+ attrName,
501
+ attrStart: beforeSpace,
502
+ attrEnd: end + 1,
503
+ value: html.slice(valueStart + 1, end)
504
+ };
505
+ i = end + 1;
506
+ } else {
507
+ i = valueStart;
508
+ while (i < html.length && !whitespace.test(html[i]) && html[i] !== ">")
509
+ i++;
510
+ }
511
+ }
512
+ return -1;
513
+ });
514
+ const replacements = [];
515
+ let skippedThrough = -1;
516
+ for (const candidate of candidates) {
517
+ const { start: startIdx, end: startTagEnd, tag, attrName, value } = candidate;
518
+ if (startIdx < skippedThrough)
519
+ continue;
520
+ const verdict = evalLiteralCondition(value);
460
521
  if (verdict === void 0)
461
522
  continue;
462
- const startIdx = m.index, startTagEnd = startIdx + full.length, elementEnd = !!selfClose || /\/\s*>$/.test(full) || VOID_ELEMENT_TAGS.has(tag.toLowerCase()) ? startTagEnd : findElementEnd(template, startIdx, tag, startTagEnd);
523
+ const elementEnd = /\/\s*>$/.test(template.slice(startIdx, startTagEnd)) || VOID_ELEMENT_TAGS.has(tag.toLowerCase()) ? startTagEnd : findElementEnd(template, startIdx, tag, startTagEnd);
463
524
  if (elementEnd === -1)
464
525
  continue;
465
- if ((attrName === ":if" || attrName === "x-if" || attrName === "@if") && nextSiblingHasElse(template, elementEnd)) {
466
- tagWithLiteralIf.lastIndex = startTagEnd;
526
+ if ((attrName === ":if" || attrName === "x-if" || attrName === "@if") && nextSiblingHasElse(template, elementEnd))
467
527
  continue;
468
- }
469
528
  if (verdict === !0) {
470
- const rebuiltStartTag = `<${tag}${beforeAttrs}${afterAttrs}${selfClose}>`;
529
+ const rebuiltStartTag = template.slice(startIdx, candidate.attrStart) + template.slice(candidate.attrEnd, startTagEnd);
471
530
  replacements.push({ start: startIdx, end: startTagEnd, replacement: rebuiltStartTag });
472
531
  continue;
473
532
  }
474
533
  replacements.push({ start: startIdx, end: elementEnd, replacement: "" });
475
- tagWithLiteralIf.lastIndex = elementEnd;
534
+ skippedThrough = elementEnd;
476
535
  }
477
536
  if (replacements.length === 0)
478
537
  return template;
@@ -0,0 +1,12 @@
1
+ import type { StaticPath, StaticPathsResult } from './ssg';
2
+ /** Define static paths for dynamic routes (used in templates). */
3
+ export declare function defineStaticPaths(pathsFn: () => Promise<StaticPath[]> | StaticPath[]): () => Promise<StaticPathsResult>;
4
+ /**
5
+ * Preserve failure provenance instead of collapsing it into "hook absent".
6
+ * Hook evaluation remains isolated from the page's other server declarations;
7
+ * this does not add imported/module-closure evaluation to the SSG.
8
+ */
9
+ export declare function readStaticPaths(filePath: string): Promise<StaticPathsOutcome>;
10
+ export type StaticPathsOutcome = | { kind: 'absent' }
11
+ | { kind: 'ok', value: StaticPathsResult }
12
+ | { kind: 'error', phase: 'read' | 'parse' | 'execute' | 'result', error: Error }
@@ -0,0 +1,62 @@
1
+ import { classifyAllScripts } from "./script-classifier.js";
2
+ import { declaresClientIdentifier } from "./client-declarations.js";
3
+ import { bracketDepths, stripCommentsAndLiterals } from "./strip-literals.js";
4
+ import { extractBalancedExpression, findMatchingDelimiter } from "./parser/tokenizer.js";
5
+ export function defineStaticPaths(pathsFn) {
6
+ return async () => ({ paths: await pathsFn(), fallback: !1 });
7
+ }
8
+ let transpiler;
9
+ function hookExpression(code) {
10
+ const masked = stripCommentsAndLiterals(code), { depths } = bracketDepths(masked), declarations = /\b(?:async\s+)?function\s+getStaticPaths\s*\(/g;
11
+ let match;
12
+ while ((match = declarations.exec(masked)) !== null) {
13
+ if (depths[match.index] !== 0)
14
+ continue;
15
+ const paramsEnd = findMatchingDelimiter(masked, "(", ")", declarations.lastIndex - 1), bodyStart = masked.indexOf("{", paramsEnd + 1), bodyEnd = findMatchingDelimiter(masked, "{", "}", bodyStart);
16
+ if (paramsEnd < 0 || bodyStart < 0 || bodyEnd < 0)
17
+ break;
18
+ return code.slice(match.index, bodyEnd + 1);
19
+ }
20
+ const variables = /\b(?:const|let|var)\s+getStaticPaths\s*=/g;
21
+ while ((match = variables.exec(masked)) !== null) {
22
+ if (depths[match.index] !== 0)
23
+ continue;
24
+ const end = extractBalancedExpression(masked, variables.lastIndex).endPos;
25
+ return code.slice(variables.lastIndex, end).trim();
26
+ }
27
+ throw SyntaxError("Could not extract getStaticPaths; use a top-level function or const declaration");
28
+ }
29
+ export async function readStaticPaths(filePath) {
30
+ let phase = "read";
31
+ try {
32
+ const content = await Bun.file(filePath).text();
33
+ phase = "parse";
34
+ const scripts = classifyAllScripts(content), serverScripts = [...scripts.server, ...scripts.client, ...scripts.signals].filter((script) => /(?:^|\s)server(?:\s|=|$)/i.test(stripCommentsAndLiterals(script.attrs))).sort((a, b) => content.indexOf(a.fullTag) - content.indexOf(b.fullTag));
35
+ for (const script of serverScripts) {
36
+ if (!/\bgetStaticPaths\b/.test(stripCommentsAndLiterals(script.content)))
37
+ continue;
38
+ const code = (transpiler ??= new Bun.Transpiler({ loader: "ts", target: "bun" })).transformSync(script.content);
39
+ if (!/\bgetStaticPaths\b/.test(stripCommentsAndLiterals(code)))
40
+ continue;
41
+ const expression = hookExpression(code);
42
+ if (/\bdefineStaticPaths\b/.test(stripCommentsAndLiterals(expression)) && declaresClientIdentifier(code, "defineStaticPaths"))
43
+ throw SyntaxError("Cannot evaluate a module-local defineStaticPaths binding during isolated hook extraction");
44
+ phase = "execute";
45
+ const hook = Function("defineStaticPaths", `return (${expression})`)(defineStaticPaths);
46
+ if (typeof hook !== "function")
47
+ throw TypeError("getStaticPaths must be a function");
48
+ const returned = await hook();
49
+ phase = "result";
50
+ const value = Array.isArray(returned) ? { paths: returned } : returned;
51
+ if (!value || typeof value !== "object" || !("paths" in value) || !Array.isArray(value.paths))
52
+ throw TypeError("getStaticPaths must return { paths: [...] } or an array of paths");
53
+ for (const entry of value.paths)
54
+ if (!entry || typeof entry !== "object" || !entry.params || typeof entry.params !== "object" || Array.isArray(entry.params))
55
+ throw TypeError("Each getStaticPaths path must contain a params object");
56
+ return { kind: "ok", value };
57
+ }
58
+ return { kind: "absent" };
59
+ } catch (error) {
60
+ return { kind: "error", phase, error: error instanceof Error ? error : Error(String(error)) };
61
+ }
62
+ }
package/dist/ssg.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference path="./stx.d.ts" />
2
+ import { defineStaticPaths } from './ssg-static-paths';
2
3
  import type { StxOptions } from './types';
3
4
  /**
4
5
  * Whether a page declares an `action` — the export that receives its own POST.
@@ -34,10 +35,6 @@ export declare function createISRHandler(options?: SSGConfig): {
34
35
  revalidate: (route: string) => Promise<void>
35
36
  invalidate: (route: string | RegExp) => Promise<void>
36
37
  };
37
- /**
38
- * Define static paths for dynamic routes (used in templates)
39
- */
40
- export declare function defineStaticPaths(pathsFn: () => Promise<StaticPath[]> | StaticPath[]): () => Promise<StaticPathsResult>;
41
38
  /**
42
39
  * Create a markdown content loader
43
40
  */
@@ -193,6 +190,7 @@ declare class FileISRCache implements ISRCache {
193
190
  invalidatePattern(pattern: RegExp): Promise<void>;
194
191
  needsRevalidation(route: string): Promise<boolean>;
195
192
  }
193
+ export { defineStaticPaths } from './ssg-static-paths';
196
194
  // =============================================================================
197
195
  // Exports
198
196
  // =============================================================================