@stacksjs/stx 0.2.119 → 0.2.121

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 (62) hide show
  1. package/dist/builtins/attrs.d.ts +16 -0
  2. package/dist/builtins/attrs.js +16 -0
  3. package/dist/builtins/safe-image.js +2 -9
  4. package/dist/builtins/stx-image.js +2 -9
  5. package/dist/builtins/stx-link.js +2 -9
  6. package/dist/cli.js +1869 -863
  7. package/dist/client-script-bundler.d.ts +9 -7
  8. package/dist/client-script-bundler.js +43 -4
  9. package/dist/client-script.d.ts +24 -0
  10. package/dist/client-script.js +59 -5
  11. package/dist/component-library.js +30 -12
  12. package/dist/component-registry.d.ts +1 -0
  13. package/dist/component-renderer.js +13 -5
  14. package/dist/composables/index.d.ts +1 -0
  15. package/dist/composables/index.js +1 -0
  16. package/dist/composables/use-scroll-lock.d.ts +13 -0
  17. package/dist/composables/use-scroll-lock.js +55 -0
  18. package/dist/composition-api.d.ts +1 -1
  19. package/dist/composition-api.js +13 -4
  20. package/dist/craft-bridge.d.ts +80 -1
  21. package/dist/craft-bridge.js +80 -1
  22. package/dist/craft-components.d.ts +77 -3
  23. package/dist/craft-components.js +138 -9
  24. package/dist/craft.js +12708 -118
  25. package/dist/dev-server/crosswind.d.ts +15 -1
  26. package/dist/dev-server/crosswind.js +12 -3
  27. package/dist/dev-server/serve-app.js +3 -3
  28. package/dist/document-shell.d.ts +17 -0
  29. package/dist/document-shell.js +72 -2
  30. package/dist/expressions.js +14 -7
  31. package/dist/head.d.ts +8 -0
  32. package/dist/head.js +11 -8
  33. package/dist/html-masking.d.ts +12 -0
  34. package/dist/html-masking.js +50 -15
  35. package/dist/index.d.ts +9 -1
  36. package/dist/index.js +9 -1
  37. package/dist/native-build.d.ts +6 -0
  38. package/dist/native-build.js +77 -60
  39. package/dist/process.js +11 -3
  40. package/dist/pwa.js +1232 -508
  41. package/dist/render.js +6 -1
  42. package/dist/runtime-globals.d.ts +7 -4
  43. package/dist/runtime-globals.js +10 -2
  44. package/dist/runtime-injection.js +15 -1
  45. package/dist/signal-processing.js +19 -28
  46. package/dist/signals-api.d.ts +2 -0
  47. package/dist/signals-api.js +9 -0
  48. package/dist/signals.js +785 -62
  49. package/dist/ssg.js +1176 -452
  50. package/dist/state-management.d.ts +1 -1
  51. package/dist/state-management.js +1 -1
  52. package/dist/strict-lint.d.ts +1 -1
  53. package/dist/strict-lint.js +1 -1
  54. package/dist/stx.d.ts +6 -0
  55. package/dist/style-scoping.js +10 -0
  56. package/dist/utils.js +5 -3
  57. package/dist/variable-extractor.js +14 -22
  58. package/dist/vue-template.js +3 -2
  59. package/dist/x-element.d.ts +2 -2
  60. package/dist/x-element.js +9 -8
  61. package/package.json +5 -5
  62. package/stx.d.ts +6 -0
@@ -46,7 +46,20 @@ export declare function rebuildCrosswindCSS(cwd: string): Promise<void>;
46
46
  */
47
47
  export declare function getCachedCSS(): string;
48
48
  /**
49
- * Extract all CSS class names from HTML content
49
+ * Extract all CSS class names from HTML content.
50
+ *
51
+ * This is the FALLBACK. `generateCrosswindCSS` prefers Crosswind's own
52
+ * `extractClasses`, and the difference is not cosmetic: this function only
53
+ * understands `class=""` and quoted literals inside `x-class` / `:class`, so
54
+ * every class that lives in code — a helper returning a class string, an icon
55
+ * keyed by status, anything inside a `<script client>` block — is invisible to
56
+ * it. Those classes silently generate no CSS and the element renders unstyled,
57
+ * which is what drove projects to pre-generate whole icon stylesheets and ship
58
+ * them alongside the page.
59
+ *
60
+ * Keeping a second, weaker copy of Crosswind's extraction rules in this package
61
+ * is what let the two drift apart in the first place. It stays only for the
62
+ * case where the installed Crosswind predates the export.
50
63
  */
51
64
  export declare function extractClassNames(htmlContent: string): Set<string>;
52
65
  /**
@@ -64,6 +77,7 @@ declare interface CrosswindModule {
64
77
  config: CrosswindConfig
65
78
  build?: (config: CrosswindConfig) => Promise<CrosswindBuildResult>
66
79
  defaultConfig?: CrosswindConfig
80
+ extractClasses?: (content: string) => Set<string>
67
81
  }
68
82
  declare interface CrosswindConfig {
69
83
  content?: string[]
@@ -44,7 +44,8 @@ async function tryImportCrosswind(importPath) {
44
44
  CSSGenerator: pkg.CSSGenerator,
45
45
  config: pkg.config,
46
46
  build: pkg.build,
47
- defaultConfig: pkg.defaultConfig
47
+ defaultConfig: pkg.defaultConfig,
48
+ extractClasses: pkg.extractClasses
48
49
  };
49
50
  } catch {}
50
51
  return null;
@@ -97,7 +98,15 @@ export async function loadCrosswind() {
97
98
  }
98
99
  }
99
100
  return null;
100
- }, projectModule = await loadFrom(findProjectCrosswindPaths());
101
+ };
102
+ if (process.env.CROSSWIND_SRC) {
103
+ const explicitPath = path.resolve(process.env.CROSSWIND_SRC), explicitModule = await loadFrom([explicitPath]);
104
+ if (!explicitModule)
105
+ throw Error(`CROSSWIND_SRC did not resolve to a Crosswind module: ${explicitPath}`);
106
+ crosswindModule = explicitModule;
107
+ return crosswindModule;
108
+ }
109
+ const projectModule = await loadFrom(findProjectCrosswindPaths());
101
110
  if (projectModule) {
102
111
  crosswindModule = projectModule;
103
112
  return crosswindModule;
@@ -243,7 +252,7 @@ export async function generateCrosswindCSS(htmlContent, appDir) {
243
252
  const hw = await loadCrosswind();
244
253
  if (!hw)
245
254
  return "";
246
- const classes = extractClassNames(htmlContent);
255
+ const classes = typeof hw.extractClasses === "function" ? hw.extractClasses(htmlContent) : extractClassNames(htmlContent);
247
256
  if (classes.size === 0)
248
257
  return "";
249
258
  const resolveRoot = appDir ? path.resolve(appDir) : process.cwd();
@@ -226,9 +226,9 @@ export async function serveApp(appDir = ".", options = {}) {
226
226
  const context = {
227
227
  __filename: route.filePath,
228
228
  __dirname: path.dirname(route.filePath),
229
- params: requestParams ?? {}
230
- }, { resetHead } = await import("../head.js");
231
- resetHead();
229
+ params: requestParams ?? {},
230
+ __stx_runtime_head: {}
231
+ };
232
232
  for (const scriptBody of serverScripts)
233
233
  try {
234
234
  await extractVariables(scriptBody, context, route.filePath);
@@ -6,6 +6,23 @@ import type { AppHeadConfig } from './types/config-types';
6
6
  * the runtime's late injection.
7
7
  */
8
8
  export declare function injectCloakStyle(html: string): string;
9
+ /**
10
+ * Pull the page's own `<link>` / `<style>` off the front of a fragment so the
11
+ * shell can put them in `<head>` where they belong.
12
+ *
13
+ * A fragment page (no `<html>/<head>/<body>` of its own) writes its font links
14
+ * and scoped styles at the top of the file. Without this they land verbatim in
15
+ * `<body>`, which *renders* fine — browsers honour `<link>` and `<style>` in
16
+ * body — but breaks SPA navigation: the router reconciles `head style` and
17
+ * `head link[rel=stylesheet]` only, so on a container-only swap the incoming
18
+ * page's CSS is never applied and the form/page paints unstyled. It looked
19
+ * correct on reload and wrong on navigation, which is what made it hard to see.
20
+ *
21
+ * Deliberately conservative: only the leading run is hoisted (whitespace,
22
+ * comments and `<script>` blocks are stepped over, never moved), so a `<style>`
23
+ * sitting next to the markup it styles further down the page is left alone.
24
+ */
25
+ export declare function hoistLeadingHeadTags(content: string): { body: string, hoisted: string[] };
9
26
  /**
10
27
  * Generate the full HTML document shell wrapping page content.
11
28
  *
@@ -8,6 +8,75 @@ export function injectCloakStyle(html) {
8
8
  return `${html.slice(0, headCloseIdx)}${CLOAK_STYLE}
9
9
  ${html.slice(headCloseIdx)}`;
10
10
  }
11
+ export function hoistLeadingHeadTags(content) {
12
+ const hoisted = [];
13
+ let i = 0;
14
+ while (i < content.length) {
15
+ const rest = content.slice(i), ws = rest.match(/^\s+/);
16
+ if (ws) {
17
+ i += ws[0].length;
18
+ continue;
19
+ }
20
+ if (rest.startsWith("<!--")) {
21
+ const end = content.indexOf("-->", i);
22
+ if (end === -1)
23
+ break;
24
+ i = end + 3;
25
+ continue;
26
+ }
27
+ const scriptOpen = rest.match(/^<script\b[^>]*>/i);
28
+ if (scriptOpen) {
29
+ const end = content.toLowerCase().indexOf("</script>", i + scriptOpen[0].length);
30
+ if (end === -1)
31
+ break;
32
+ i = end + 9;
33
+ continue;
34
+ }
35
+ const link = rest.match(/^<link\b[^>]*>/i);
36
+ if (link) {
37
+ hoisted.push(link[0]);
38
+ i += link[0].length;
39
+ continue;
40
+ }
41
+ const meta = rest.match(/^<meta\b[^>]*>/i);
42
+ if (meta) {
43
+ hoisted.push(meta[0]);
44
+ i += meta[0].length;
45
+ continue;
46
+ }
47
+ const style = rest.match(/^<style\b[^>]*>[\s\S]*?<\/style>/i);
48
+ if (style) {
49
+ hoisted.push(style[0]);
50
+ i += style[0].length;
51
+ continue;
52
+ }
53
+ break;
54
+ }
55
+ if (hoisted.length === 0)
56
+ return { body: content, hoisted };
57
+ let body = "", j = 0;
58
+ while (j < content.length && j < i) {
59
+ const rest = content.slice(j), link = rest.match(/^<link\b[^>]*>/i);
60
+ if (link && hoisted.includes(link[0])) {
61
+ j += link[0].length;
62
+ continue;
63
+ }
64
+ const meta = rest.match(/^<meta\b[^>]*>/i);
65
+ if (meta && hoisted.includes(meta[0])) {
66
+ j += meta[0].length;
67
+ continue;
68
+ }
69
+ const style = rest.match(/^<style\b[^>]*>[\s\S]*?<\/style>/i);
70
+ if (style && hoisted.includes(style[0])) {
71
+ j += style[0].length;
72
+ continue;
73
+ }
74
+ body += content[j];
75
+ j++;
76
+ }
77
+ body += content.slice(i);
78
+ return { body, hoisted };
79
+ }
11
80
  export function generateDocumentShell(content, headConfig = {}, options = {}) {
12
81
  const {
13
82
  title: configTitle = "stx App",
@@ -34,7 +103,7 @@ export function generateDocumentShell(content, headConfig = {}, options = {}) {
34
103
  return ` <script>${s.content}</script>`;
35
104
  return "";
36
105
  }).filter(Boolean).join(`
37
- `), bodyAttrStr = Object.entries(bodyAttrs).map(([k, v]) => ` ${k}="${v}"`).join(""), bodyClassStr = bodyClass ? ` class="${bodyClass}"` : "", headParts = [
106
+ `), bodyAttrStr = Object.entries(bodyAttrs).map(([k, v]) => ` ${k}="${v}"`).join(""), bodyClassStr = bodyClass ? ` class="${bodyClass}"` : "", { body: pageBody, hoisted: pageHeadTags } = hoistLeadingHeadTags(content), headParts = [
38
107
  metaTags,
39
108
  ` <title>${pageTitle}</title>`,
40
109
  ` ${CLOAK_STYLE}`,
@@ -42,10 +111,11 @@ export function generateDocumentShell(content, headConfig = {}, options = {}) {
42
111
  configHeadScripts,
43
112
  headRaw ? ` ${headRaw}` : "",
44
113
  ...(options.styles || []).map((s) => ` ${s}`),
114
+ ...pageHeadTags.map((s) => ` ${s}`),
45
115
  ...(options.headScripts || []).map((s) => ` ${s}`),
46
116
  options.headStack || ""
47
117
  ].filter(Boolean), bodyParts = [
48
- content,
118
+ pageBody,
49
119
  ...(options.bodyScripts || []).map((s) => s),
50
120
  options.bodyStack || ""
51
121
  ].filter(Boolean);
@@ -1,7 +1,7 @@
1
1
  import { createSafeFunction, isExpressionSafe, safeEvaluate } from "./safe-evaluator.js";
2
2
  import { createDetailedErrorMessage } from "./utils.js";
3
3
  import { createPlaceholder } from "./placeholder.js";
4
- import { maskAtElementPosition, matchScriptElement, matchStyleElement } from "./html-masking.js";
4
+ import { maskAtElementPosition, matchScriptElement, matchStyleElement, restoreStashedScripts, stashScriptElements } from "./html-masking.js";
5
5
  const customFilters = {}, translateRegexCache = new Map;
6
6
  function getTranslateRegex(key) {
7
7
  let re = translateRegexCache.get(key);
@@ -236,10 +236,12 @@ export function escapeHtml(unsafe) {
236
236
  return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
237
237
  }
238
238
  export function usesSignalsInScript(template) {
239
- const scriptRegex = /<script\b(?![^>]*\bserver\b)(?![^>]*\bsrc\s*=)[^>]*>([\s\S]*?)<\/script>/gi;
240
- let match;
241
- while ((match = scriptRegex.exec(template)) !== null) {
242
- const content = match[1];
239
+ const { scripts } = stashScriptElements(template);
240
+ for (const script of scripts) {
241
+ const match = script.match(/^<script\b([^>]*)>([\s\S]*?)<\/script\s*>$/i);
242
+ if (!match || /\bserver\b|\bsrc\s*=/.test(match[1]))
243
+ continue;
244
+ const content = match[2];
243
245
  if (/\b(state|derived|effect|ref|reactive|computed|watch|watchEffect)\s*(?:<[^>]*>)?\s*\(/.test(content))
244
246
  return !0;
245
247
  }
@@ -324,7 +326,11 @@ export function interpolateScriptExpressions(scriptBody, context) {
324
326
  return output;
325
327
  }
326
328
  export function interpolateScriptsInTemplate(template, context, options = {}) {
327
- return template.replace(/<script\b([^>]*)>([\s\S]*?)<\/script>/gi, (full, attrs, body) => {
329
+ const { output, scripts } = stashScriptElements(template), interpolatedScripts = scripts.map((full) => {
330
+ const match = full.match(/^<script\b([^>]*)>([\s\S]*?)<\/script\s*>$/i), opening = full.match(/^<script\b[^>]*>/i), closingStart = full.search(/<\/script\s*>$/i);
331
+ if (!match || !opening || closingStart === -1)
332
+ return full;
333
+ const attrs = match[1], body = match[2];
328
334
  if (/\bsrc\s*=/.test(attrs))
329
335
  return full;
330
336
  if (/\bdata-raw\b/.test(attrs))
@@ -334,8 +340,9 @@ export function interpolateScriptsInTemplate(template, context, options = {}) {
334
340
  if (options.skipServer && /\bserver\b/.test(attrs))
335
341
  return full;
336
342
  const interpolated = interpolateScriptExpressions(body, context);
337
- return `<script${attrs}>${interpolated}</script>`;
343
+ return opening[0] + interpolated + full.slice(closingStart);
338
344
  });
345
+ return restoreStashedScripts(output, interpolatedScripts);
339
346
  }
340
347
  export function processExpressions(template, context, filePath, options) {
341
348
  let output = template;
package/dist/head.d.ts CHANGED
@@ -31,6 +31,10 @@ export declare function resetHead(): void;
31
31
  * ```
32
32
  */
33
33
  export declare function useHead(config: HeadConfig): void;
34
+ /**
35
+ * Merge two head configs.
36
+ */
37
+ export declare function mergeHeadConfigs(base: HeadConfig, override: HeadConfig): HeadConfig;
34
38
  /**
35
39
  * Set SEO meta tags with a simplified API.
36
40
  *
@@ -45,6 +49,10 @@ export declare function useHead(config: HeadConfig): void;
45
49
  * ```
46
50
  */
47
51
  export declare function useSeoMeta(config: SeoMeta): void;
52
+ /**
53
+ * Convert SEO metadata into a head configuration without mutating global state.
54
+ */
55
+ export declare function seoMetaToHeadConfig(config: SeoMeta): HeadConfig;
48
56
  /**
49
57
  * Define page-level metadata.
50
58
  *
package/dist/head.js CHANGED
@@ -13,9 +13,9 @@ export function resetHead() {
13
13
  currentPageMeta = {};
14
14
  }
15
15
  export function useHead(config) {
16
- currentHead = mergeHead(currentHead, config);
16
+ currentHead = mergeHeadConfigs(currentHead, config);
17
17
  }
18
- function mergeHead(base, override) {
18
+ export function mergeHeadConfigs(base, override) {
19
19
  return {
20
20
  title: override.title ?? base.title,
21
21
  titleTemplate: override.titleTemplate ?? base.titleTemplate,
@@ -29,6 +29,9 @@ function mergeHead(base, override) {
29
29
  };
30
30
  }
31
31
  export function useSeoMeta(config) {
32
+ useHead(seoMetaToHeadConfig(config));
33
+ }
34
+ export function seoMetaToHeadConfig(config) {
32
35
  const meta = [];
33
36
  if (config.description)
34
37
  meta.push({ name: "description", content: config.description });
@@ -82,12 +85,12 @@ export function useSeoMeta(config) {
82
85
  const link = [];
83
86
  if (config.canonical)
84
87
  link.push({ rel: "canonical", href: config.canonical });
85
- useHead({
88
+ return {
86
89
  title: config.title,
87
90
  titleTemplate: config.titleTemplate,
88
91
  meta,
89
92
  link
90
- });
93
+ };
91
94
  }
92
95
  export function definePageMeta(config) {
93
96
  currentPageMeta = { ...currentPageMeta, ...config };
@@ -206,15 +209,15 @@ export function processHeadDirective(content) {
206
209
  }
207
210
  export function processTitleDirective(content, context) {
208
211
  return content.replace(/@title\s*\(\s*(?:(['"`])(.+?)\1|([^)]+))\s*\)/g, (_, quote, quotedTitle, unquotedTitle) => {
209
- const resolvedTitle = quote ? quotedTitle : context[(unquotedTitle || "").trim()] ?? unquotedTitle;
210
- useHead({ title: resolvedTitle });
212
+ const resolvedTitle = quote ? quotedTitle : context[(unquotedTitle || "").trim()] ?? unquotedTitle, existing = context.__stx_runtime_head ?? {};
213
+ context.__stx_runtime_head = mergeHeadConfigs(existing, { title: resolvedTitle });
211
214
  return "";
212
215
  });
213
216
  }
214
217
  export function processMetaDirective(content, context) {
215
218
  return content.replace(/@meta\s*\(\s*(['"`])(.+?)\1\s*,\s*(?:(['"`])(.+?)\3|([^)]+))\s*\)/g, (_, _q1, name, q2, quotedValue, unquotedValue) => {
216
- const value = q2 ? quotedValue : unquotedValue, resolvedValue = q2 ? value : context[(value || "").trim()] ?? value, meta = name.includes(":") ? { property: name, content: resolvedValue } : { name, content: resolvedValue };
217
- useHead({ meta: [meta] });
219
+ const value = q2 ? quotedValue : unquotedValue, resolvedValue = q2 ? value : context[(value || "").trim()] ?? value, meta = name.includes(":") ? { property: name, content: resolvedValue } : { name, content: resolvedValue }, existing = context.__stx_runtime_head ?? {};
220
+ context.__stx_runtime_head = mergeHeadConfigs(existing, { meta: [meta] });
218
221
  return "";
219
222
  });
220
223
  }
@@ -4,6 +4,13 @@ export declare function matchScriptElement(html: string, i: number): number;
4
4
  export declare function matchStyleElement(html: string, i: number): number;
5
5
  /** Matches an HTML comment `<!-- … -->` (through the first `-->`). */
6
6
  export declare function matchHtmlComment(html: string, i: number): number;
7
+ /**
8
+ * Find tokens that begin where an HTML element can actually start.
9
+ *
10
+ * The walk also treats comments and script/style raw-text bodies as opaque, so
11
+ * callers never see tag-like text nested inside those regions.
12
+ */
13
+ export declare function scanAtElementPosition(html: string, match: TokenMatcher): ElementPositionToken[];
7
14
  /**
8
15
  * Mask every token (per `match`) that begins at element position, replacing it
9
16
  * with `placeholder(token, index)`. Returns the rewritten string plus the ordered
@@ -22,6 +29,11 @@ export declare function maskAtElementPosition(html: string, match: TokenMatcher,
22
29
  export declare function stashScriptElements(html: string): { output: string, scripts: string[] };
23
30
  /** Restore scripts stashed by {@link stashScriptElements}. */
24
31
  export declare function restoreStashedScripts(html: string, scripts: string[]): string;
32
+ export declare interface ElementPositionToken {
33
+ start: number
34
+ end: number
35
+ token: string
36
+ }
25
37
  /**
26
38
  * Element-position-aware masking.
27
39
  *
@@ -24,15 +24,33 @@ export function matchHtmlComment(html, i) {
24
24
  return -1;
25
25
  return closeRel + 3;
26
26
  }
27
- export function maskAtElementPosition(html, match, placeholder) {
27
+ function matchOpaqueRegion(html, i) {
28
+ if (html.startsWith("<!--", i)) {
29
+ const end = matchHtmlComment(html, i);
30
+ return end === -1 ? html.length : end;
31
+ }
32
+ for (const [open, close] of [
33
+ [SCRIPT_OPEN, SCRIPT_CLOSE],
34
+ [STYLE_OPEN, STYLE_CLOSE]
35
+ ]) {
36
+ open.lastIndex = i;
37
+ const opening = open.exec(html);
38
+ if (!opening || opening.index !== i)
39
+ continue;
40
+ close.lastIndex = open.lastIndex;
41
+ const closing = close.exec(html);
42
+ return closing ? closing.index + closing[0].length : html.length;
43
+ }
44
+ return -1;
45
+ }
46
+ export function scanAtElementPosition(html, match) {
28
47
  const tokens = [];
29
- let output = "", i = 0;
48
+ let i = 0;
30
49
  const n = html.length;
31
50
  let inTag = !1, quote = null;
32
51
  while (i < n) {
33
52
  const ch = html[i];
34
53
  if (inTag) {
35
- output += ch;
36
54
  if (quote) {
37
55
  if (ch === quote)
38
56
  quote = null;
@@ -43,23 +61,40 @@ export function maskAtElementPosition(html, match, placeholder) {
43
61
  i++;
44
62
  continue;
45
63
  }
46
- if (ch === "<") {
47
- const end = match(html, i);
48
- if (end > i) {
49
- output += placeholder(html.slice(i, end), tokens.length);
50
- tokens.push(html.slice(i, end));
51
- i = end;
52
- continue;
53
- }
54
- if (/^<\/?[a-zA-Z]/.test(html.slice(i, i + 2)))
55
- inTag = !0;
56
- output += ch;
64
+ if (ch !== "<") {
57
65
  i++;
58
66
  continue;
59
67
  }
60
- output += ch;
68
+ const end = match(html, i);
69
+ if (end > i) {
70
+ tokens.push({ start: i, end, token: html.slice(i, end) });
71
+ i = end;
72
+ continue;
73
+ }
74
+ const opaqueEnd = matchOpaqueRegion(html, i);
75
+ if (opaqueEnd > i) {
76
+ i = opaqueEnd;
77
+ continue;
78
+ }
79
+ if (/^<\/?[a-zA-Z]/.test(html.slice(i, i + 2)))
80
+ inTag = !0;
61
81
  i++;
62
82
  }
83
+ return tokens;
84
+ }
85
+ export function maskAtElementPosition(html, match, placeholder) {
86
+ const matches = scanAtElementPosition(html, match);
87
+ if (matches.length === 0)
88
+ return { output: html, tokens: [] };
89
+ const tokens = matches.map((item) => item.token);
90
+ let output = "", cursor = 0;
91
+ for (let index = 0;index < matches.length; index++) {
92
+ const item = matches[index];
93
+ output += html.slice(cursor, item.start);
94
+ output += placeholder(item.token, index);
95
+ cursor = item.end;
96
+ }
97
+ output += html.slice(cursor);
63
98
  return { output, tokens };
64
99
  }
65
100
  export function stashScriptElements(html) {
package/dist/index.d.ts CHANGED
@@ -131,7 +131,15 @@ export * from './hydration';
131
131
  // ssg moved to stx/ssg
132
132
  export * from './ssr';
133
133
  // State management — explicit exports to avoid conflicts with runtime.ts and reactivity.ts
134
- export { defineStore, useStore, createStore, getStore, getStoreNames, hasStore } from './state-management';
134
+ export {
135
+ createStore,
136
+ defineStore,
137
+ getStore,
138
+ getStoreNames,
139
+ hasStore,
140
+ registerStoresClient,
141
+ useStore,
142
+ } from './state-management';
135
143
  // visual-testing moved to stx/visual-testing
136
144
  export * from './image-optimization/index';
137
145
  export * from './media/index';
package/dist/index.js CHANGED
@@ -84,7 +84,15 @@ export * from "./client-script.js";
84
84
  export * from "./component-hmr.js";
85
85
  export * from "./hydration.js";
86
86
  export * from "./ssr.js";
87
- export { defineStore, useStore, createStore, getStore, getStoreNames, hasStore } from "./state-management.js";
87
+ export {
88
+ createStore,
89
+ defineStore,
90
+ getStore,
91
+ getStoreNames,
92
+ hasStore,
93
+ registerStoresClient,
94
+ useStore
95
+ } from "./state-management.js";
88
96
  export * from "./image-optimization/index.js";
89
97
  export * from "./media/index.js";
90
98
  export * from "./build-views.js";
@@ -62,10 +62,16 @@ export declare interface NativeBuildConfig {
62
62
  }
63
63
  }
64
64
  systemTray?: boolean
65
+ menubarOnly?: boolean
66
+ infoPlist?: Record<string, string | number | boolean>
67
+ minimumSystemVersion?: string
68
+ category?: string
69
+ copyright?: string
65
70
  hotReload?: boolean
66
71
  devTools?: boolean
67
72
  craftPath?: string
68
73
  env?: Record<string, string>
74
+ context?: Record<string, unknown>
69
75
  verbose?: boolean
70
76
  }
71
77
  export declare interface NativeBuildResult {