@stacksjs/stx 0.2.151 → 0.2.152

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.
@@ -38,6 +38,22 @@ export declare function findComponentTags(html: string, tagPattern: RegExp, skip
38
38
  * A legacy HTML-entity-escaped JSON format is still accepted as a fallback so
39
39
  * that any externally-produced markup keeps working.
40
40
  */
41
+ /**
42
+ * Decode the entities in a static attribute value.
43
+ *
44
+ * An attribute in a template is HTML source, so `title="Tom & Jerry"` names
45
+ * the string `Tom & Jerry`, and a component that receives it as a prop should
46
+ * see that string rather than the encoding of it.
47
+ *
48
+ * The narrowness is the point. This runs on attribute values, at parse time, and
49
+ * on nothing else: a value that reached a component any other way - from the
50
+ * component's own script, from the parent's context, from a `:bound="expression"`
51
+ * - was never encoded, so decoding it can only destroy escaping somebody meant.
52
+ * That is exactly what a blanket "this looks like HTML, unescape it" pass over
53
+ * the component context used to do, and it turned escaped user text back into
54
+ * live markup.
55
+ */
56
+ export declare function decodeAttributeEntities(value: string): string;
41
57
  export declare function decodeStxProp(attrValue: string, options?: { debug?: boolean }): unknown;
42
58
  /**
43
59
  * Parse multiline attributes from a component tag
@@ -113,6 +113,9 @@ export function findComponentTags(html, tagPattern, skipTags) {
113
113
  }
114
114
  return matches;
115
115
  }
116
+ export function decodeAttributeEntities(value) {
117
+ return value.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#0?39;/g, "'").replace(/&#x27;/gi, "'").replace(/&amp;/g, "&");
118
+ }
116
119
  export function decodeStxProp(attrValue, options) {
117
120
  try {
118
121
  const json = Buffer.from(attrValue, "base64").toString("utf8");
@@ -606,7 +609,7 @@ export async function processCustomElements(template, context, filePath, compone
606
609
  props[attrName] = result;
607
610
  }
608
611
  } else
609
- props[attrName] = attrValue;
612
+ props[attrName] = decodeAttributeEntities(attrValue);
610
613
  }
611
614
  const processedContent = await renderComponentWithSlot(componentPath, props, tag.content, componentsDir, context, filePath, options, processedComponents, dependencies);
612
615
  result = result.substring(0, tag.startIndex) + processedContent + result.substring(tag.endIndex);
@@ -1,6 +1,6 @@
1
1
  import { registry } from "./component-registry.js";
2
2
  import { registerBuiltins } from "./builtins/index.js";
3
- import { decodeStxProp, findComponentTags, parseMultilineAttributes, pascalToKebab, restoreStashedScripts, stashScriptElements } from "./component-processing.js";
3
+ import { decodeAttributeEntities, decodeStxProp, findComponentTags, parseMultilineAttributes, pascalToKebab, restoreStashedScripts, stashScriptElements } from "./component-processing.js";
4
4
  import { maskAtElementPosition, matchHtmlComment } from "./html-masking.js";
5
5
  import { renderComponentWithSlot, userComponentFileExists } from "./utils.js";
6
6
  import { createSafeFunction, isExpressionSafe, safeEvaluateObject } from "./safe-evaluator.js";
@@ -287,7 +287,7 @@ function parseComponentProps(rawProps, context, options) {
287
287
  }
288
288
  continue;
289
289
  }
290
- setStatic(attrName, attrValue === BOOLEAN_ATTRIBUTE_SENTINEL ? "true" : attrValue);
290
+ setStatic(attrName, attrValue === BOOLEAN_ATTRIBUTE_SENTINEL ? "true" : decodeAttributeEntities(attrValue));
291
291
  }
292
292
  return resolved;
293
293
  }
@@ -63,7 +63,7 @@ export declare function useDeviceMotion(): DeviceMotionRef;
63
63
  * })
64
64
  * ```
65
65
  */
66
- export declare function useParallax(): void;
66
+ export declare function useParallax(): ParallaxRef;
67
67
  /**
68
68
  * Device Orientation & Motion Composables
69
69
  *
@@ -101,6 +101,13 @@ export declare interface DeviceOrientationRef {
101
101
  isSupported: () => boolean
102
102
  requestPermission: () => Promise<boolean>
103
103
  }
104
+ /** Handle returned by {@link useParallax}. */
105
+ export declare interface ParallaxRef {
106
+ get: () => { x: number, y: number }
107
+ subscribe: (fn: (state: { x: number, y: number }) => void) => () => void
108
+ isSupported: () => boolean
109
+ requestPermission: () => Promise<boolean>
110
+ }
104
111
  export declare interface DeviceMotionRef {
105
112
  get: () => DeviceMotionState
106
113
  subscribe: (fn: (state: DeviceMotionState) => void) => () => void
@@ -40,7 +40,7 @@ export declare function useEventSource<T = unknown>(url: string | URL, options?:
40
40
  * })
41
41
  * ```
42
42
  */
43
- export declare function useSSE<T = unknown>(url: string): void;
43
+ export declare function useSSE<T = unknown>(url: string): SSERef<T>;
44
44
  export declare interface EventSourceState<T = unknown> {
45
45
  status: EventSourceStatus
46
46
  data: T | null
@@ -61,6 +61,19 @@ export declare interface EventSourceOptions {
61
61
  onMessage?: (event: MessageEvent) => void
62
62
  onError?: (event: Event) => void
63
63
  }
64
+ /** Handle returned by {@link useSSE}. */
65
+ export declare interface SSERef<T = unknown> {
66
+ data: {
67
+ get: () => T | null
68
+ subscribe: (fn: (data: T | null) => void) => () => void
69
+ }
70
+ status: {
71
+ get: () => EventSourceStatus
72
+ subscribe: (fn: (status: EventSourceStatus) => void) => () => void
73
+ }
74
+ close: () => void
75
+ open: () => void
76
+ }
64
77
  export declare interface EventSourceRef<T = unknown> {
65
78
  get: () => EventSourceState<T>
66
79
  subscribe: (fn: (state: EventSourceState<T>) => void) => () => void
@@ -69,7 +69,7 @@ export declare function hexToHsl(hex: string): { h: number, s: number, l: number
69
69
  export declare function useColorHistory(options?: {
70
70
  maxHistory?: number
71
71
  onPick?: (color: string) => void
72
- }): void;
72
+ }): ColorHistoryRef;
73
73
  /**
74
74
  * EyeDropper Composables
75
75
  *
@@ -89,6 +89,15 @@ export declare interface EyeDropperOptions {
89
89
  onAbort?: () => void
90
90
  onError?: (error: Error) => void
91
91
  }
92
+ /** Handle returned by {@link useColorHistory}. */
93
+ export declare interface ColorHistoryRef {
94
+ get: () => { current: string | null, history: string[] }
95
+ subscribe: (fn: (state: { current: string | null, history: string[] }) => void) => () => void
96
+ pick: () => Promise<string | null>
97
+ clearHistory: () => void
98
+ removeFromHistory: (color: string) => void
99
+ isSupported: () => boolean
100
+ }
92
101
  export declare interface EyeDropperRef {
93
102
  get: () => EyeDropperState
94
103
  subscribe: (fn: (state: EyeDropperState) => void) => () => void
@@ -32,7 +32,7 @@ export declare function useIdle(options?: IdleOptions): IdleRef;
32
32
  * })
33
33
  * ```
34
34
  */
35
- export declare function useIdleState(timeout?: number): void;
35
+ export declare function useIdleState(timeout?: number): IdleStateRef;
36
36
  /**
37
37
  * Track user's last active timestamp
38
38
  *
@@ -45,7 +45,7 @@ export declare function useIdleState(timeout?: number): void;
45
45
  * })
46
46
  * ```
47
47
  */
48
- export declare function useLastActive(): void;
48
+ export declare function useLastActive(): LastActiveRef;
49
49
  /**
50
50
  * Auto-logout after idle timeout
51
51
  *
@@ -57,7 +57,7 @@ export declare function useLastActive(): void;
57
57
  * })
58
58
  * ```
59
59
  */
60
- export declare function useAutoLogout(timeout: number, onLogout: () => void, options?: { warningTime?: number, onWarning?: (remainingTime: number) => void }): void;
60
+ export declare function useAutoLogout(timeout: number, onLogout: () => void, options?: { warningTime?: number, onWarning?: (remainingTime: number) => void }): AutoLogoutRef;
61
61
  /**
62
62
  * Idle Detection Composables
63
63
  *
@@ -82,3 +82,21 @@ export declare interface IdleRef {
82
82
  reset: () => void
83
83
  isIdle: () => boolean
84
84
  }
85
+ /** Narrowed view of {@link IdleRef} exposing only the idle flag. */
86
+ export declare interface IdleStateRef {
87
+ get: () => boolean
88
+ subscribe: (fn: (isIdle: boolean) => void) => () => void
89
+ reset: () => void
90
+ }
91
+ /** Narrowed view of {@link IdleRef} exposing only the last-active timestamp. */
92
+ export declare interface LastActiveRef {
93
+ get: () => number
94
+ subscribe: (fn: (timestamp: number) => void) => () => void
95
+ reset: () => void
96
+ }
97
+ /** Handle returned by {@link useAutoLogout}. */
98
+ export declare interface AutoLogoutRef {
99
+ get: () => IdleState
100
+ subscribe: (fn: (state: IdleState) => void) => () => void
101
+ reset: () => void
102
+ }
@@ -20,12 +20,18 @@
20
20
  export declare function useStorage<T>(key: string, defaultValue: T, options?: UseStorageOptions<T>): StorageRef<T>;
21
21
  /**
22
22
  * Shorthand for useStorage with localStorage
23
+ *
24
+ * The return type is annotated rather than inferred on purpose: the shipped
25
+ * declarations are emitted by bun-plugin-dtsx, which resolves an unannotated
26
+ * export to `void`. Consumers importing from `@stacksjs/stx/composables` then
27
+ * got `Property 'value' does not exist on type 'void'` for a function that
28
+ * plainly returns a StorageRef. See stacksjs/stx#1796.
23
29
  */
24
- export declare function useLocalStorage<T>(key: string, defaultValue: T, options?: Omit<UseStorageOptions<T>, 'storage'>): void;
30
+ export declare function useLocalStorage<T>(key: string, defaultValue: T, options?: Omit<UseStorageOptions<T>, 'storage'>): StorageRef<T>;
25
31
  /**
26
32
  * Shorthand for useStorage with sessionStorage
27
33
  */
28
- export declare function useSessionStorage<T>(key: string, defaultValue: T, options?: Omit<UseStorageOptions<T>, 'storage'>): void;
34
+ export declare function useSessionStorage<T>(key: string, defaultValue: T, options?: Omit<UseStorageOptions<T>, 'storage'>): StorageRef<T>;
29
35
  /**
30
36
  * Clear all items from storage
31
37
  */
@@ -36,7 +36,7 @@ export declare function useTextSelection(options?: TextSelectionOptions): TextSe
36
36
  * })
37
37
  * ```
38
38
  */
39
- export declare function useElementTextSelection(element: Element | null): void;
39
+ export declare function useElementTextSelection(element: Element | null): ElementTextSelectionRef;
40
40
  /**
41
41
  * Create a selection popup that appears when text is selected
42
42
  *
@@ -56,7 +56,7 @@ export declare function useSelectionPopup(options: {
56
56
  onShow?: (text: string, position: { x: number, y: number }) => void
57
57
  onHide?: () => void
58
58
  minLength?: number
59
- }): void;
59
+ }): SelectionPopupRef;
60
60
  /**
61
61
  * Copy selected text to clipboard with custom formatting
62
62
  *
@@ -71,7 +71,7 @@ export declare function useSelectionPopup(options: {
71
71
  * copy.copyWithPrefix('> ')
72
72
  * ```
73
73
  */
74
- export declare function useCopySelection(): void;
74
+ export declare function useCopySelection(): CopySelectionRef;
75
75
  /**
76
76
  * Text Selection Composables
77
77
  *
@@ -87,6 +87,30 @@ export declare interface TextSelectionOptions {
87
87
  onSelect?: (state: TextSelectionState) => void
88
88
  onClear?: () => void
89
89
  }
90
+ /** Handle returned by {@link useElementTextSelection}. */
91
+ export declare interface ElementTextSelectionRef {
92
+ get: () => string
93
+ subscribe: (fn: (text: string) => void) => () => void
94
+ clear: () => void
95
+ selectAll: () => void
96
+ }
97
+ /** Handle returned by {@link useSelectionPopup}. */
98
+ export declare interface SelectionPopupRef {
99
+ get: () => TextSelectionState
100
+ subscribe: (fn: (state: TextSelectionState) => void) => () => void
101
+ isShowing: () => boolean
102
+ }
103
+ /** Handle returned by {@link useCopySelection}. */
104
+ export declare interface CopySelectionRef {
105
+ get: () => TextSelectionState
106
+ subscribe: (fn: (state: TextSelectionState) => void) => () => void
107
+ copy: () => Promise<boolean>
108
+ copyAsMarkdown: () => Promise<boolean>
109
+ copyWithPrefix: (prefix: string) => Promise<boolean>
110
+ copyWithWrapper: (start: string, end: string) => Promise<boolean>
111
+ getText: () => string
112
+ clear: () => void
113
+ }
90
114
  export declare interface TextSelectionRef {
91
115
  get: () => TextSelectionState
92
116
  subscribe: (fn: (state: TextSelectionState) => void) => () => void
@@ -43,7 +43,7 @@ export declare function useWakeLock(): WakeLockRef;
43
43
  * })
44
44
  * ```
45
45
  */
46
- export declare function useAutoWakeLock(): void;
46
+ export declare function useAutoWakeLock(): WakeLockRef;
47
47
  /**
48
48
  * Wake lock that only activates while a condition is true
49
49
  *
@@ -62,7 +62,7 @@ export declare function useAutoWakeLock(): void;
62
62
  * wakeLock.check() // Releases wake lock
63
63
  * ```
64
64
  */
65
- export declare function useConditionalWakeLock(condition: () => boolean): void;
65
+ export declare function useConditionalWakeLock(condition: () => boolean): WakeLockRef & { check: () => Promise<void> };
66
66
  /**
67
67
  * Wake Lock Composables
68
68
  *