@stacksjs/sanitizer 0.2.5 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,356 +5,71 @@
5
5
  [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
6
6
  [![npm downloads][npm-downloads-src]][npm-downloads-href]
7
7
 
8
- # stx
8
+ # @stacksjs/sanitizer
9
9
 
10
- A modern templating engine with Vue-like Single File Components, Laravel Blade directives, and Bun-powered performance.
10
+ A fast, native Bun-powered HTML sanitizer with DOMPurify-like features. Protection against XSS and malicious content.
11
11
 
12
- ## Features
13
-
14
- - **Vue-like SFC** - `<script>`, `<template>`, `<style>` structure
15
- - **Auto-imported Components** - Use `<Card />` directly, no imports needed
16
- - **Two-way Binding** - `x-model` and `x-text` for reactive forms
17
- - **Blade Directives** - `@if`, `@foreach`, `@layout`, `@section`
18
- - **Props & Slots** - Pass data and content to components
19
- - **200K+ Icons** - Built-in Iconify integration
20
- - **Custom Directives** - Extend with your own directives
21
-
22
- ## Quick Start
12
+ ## Installation
23
13
 
24
14
  ```bash
25
- bun add bun-plugin-stx
26
- ```
27
-
28
- ```toml
29
- # bunfig.toml
30
- preload = ["bun-plugin-stx"]
31
- ```
32
-
33
- ## Single File Components
34
-
35
- STX components use a Vue-like structure:
36
-
37
- ```html
38
- <!-- components/Greeting.stx -->
39
- <script server>
40
- // Server-side only - used for SSR, stripped from output
41
- const name = props.name || 'World'
42
- const time = new Date().toLocaleTimeString()
43
- </script>
44
-
45
- <template>
46
- <div class="greeting">
47
- <h1>Hello, {{ name }}!</h1>
48
- <p>Current time: {{ time }}</p>
49
- <slot />
50
- </div>
51
- </template>
52
-
53
- <style>
54
- .greeting {
55
- padding: 2rem;
56
- background: #f5f5f5;
57
- }
58
- </style>
59
- ```
60
-
61
- ### Script Types
62
-
63
- | Type | Behavior |
64
- |------|----------|
65
- | `<script server>` | SSR only - extracted for variables, stripped from output |
66
- | `<script client>` | Client only - preserved for browser, skips server evaluation |
67
- | `<script>` | Both - runs on server AND preserved for client |
68
-
69
- ## Components
70
-
71
- Components in `components/` are auto-imported using PascalCase:
72
-
73
- ```html
74
- <!-- pages/home.stx -->
75
- <Header />
76
-
77
- <main>
78
- <UserCard name="John" role="Admin" />
79
- <Card title="Welcome">
80
- <p>This goes into the slot!</p>
81
- </Card>
82
- </main>
83
-
84
- <Footer />
85
- ```
86
-
87
- ### Props
88
-
89
- Pass data to components via attributes:
90
-
91
- ```html
92
- <!-- String prop -->
93
- <Card title="Hello" />
94
-
95
- <!-- Expression binding with : -->
96
- <Card :count="items.length" :active="isActive" />
97
-
98
- <!-- Mustache interpolation -->
99
- <Card title="{{ userName }}" />
100
- ```
101
-
102
- Access props in components:
103
-
104
- ```html
105
- <script server>
106
- const title = props.title || 'Default'
107
- const count = props.count || 0
108
- </script>
109
-
110
- <template>
111
- <h1>{{ title }}</h1>
112
- <p>Count: {{ count }}</p>
113
- </template>
114
- ```
115
-
116
- ### Slots
117
-
118
- Use `<slot />` to inject content:
119
-
120
- ```html
121
- <!-- components/Card.stx -->
122
- <template>
123
- <div class="card">
124
- <h2>{{ props.title }}</h2>
125
- <slot />
126
- </div>
127
- </template>
128
- ```
129
-
130
- ```html
131
- <!-- Usage -->
132
- <Card title="News">
133
- <p>This content appears in the slot!</p>
134
- </Card>
135
- ```
136
-
137
- ### Explicit Imports
138
-
139
- For components outside `components/`, use `@import`:
140
-
141
- ```html
142
- @import('layouts/Sidebar')
143
- @import('shared/Button', 'shared/Modal')
144
-
145
- <Sidebar />
146
- <Button label="Click me" />
147
- ```
148
-
149
- ## Layouts
150
-
151
- Wrap pages with common structure using `@layout`:
152
-
153
- ```html
154
- <!-- layouts/default.stx -->
155
- <!DOCTYPE html>
156
- <html>
157
- <head>
158
- <title>{{ title || 'My App' }}</title>
159
- </head>
160
- <body>
161
- <Header />
162
- <main>
163
- @yield('content')
164
- </main>
165
- <Footer />
166
- </body>
167
- </html>
168
- ```
169
-
170
- ```html
171
- <!-- pages/about.stx -->
172
- @layout('default')
173
-
174
- @section('content')
175
- <h1>About Us</h1>
176
- <p>Welcome to our site.</p>
177
- @endsection
178
- ```
179
-
180
- ## Two-Way Binding (x-element)
181
-
182
- For reactive forms, use x-element directives:
183
-
184
- ```html
185
- <div x-data="{ message: '', count: 0 }">
186
- <!-- Two-way binding -->
187
- <input x-model="message" placeholder="Type here..." />
188
-
189
- <!-- Reactive display -->
190
- <p>You typed: <span x-text="message"></span></p>
191
-
192
- <!-- Event handling -->
193
- <button @click="count++">Increment</button>
194
- <button @click="count--">Decrement</button>
195
- <span x-text="count"></span>
196
- </div>
15
+ bun add @stacksjs/sanitizer
197
16
  ```
198
17
 
199
- | Directive | Purpose |
200
- |-----------|---------|
201
- | `x-data` | Define reactive scope |
202
- | `x-model` | Two-way binding for inputs |
203
- | `x-text` | Reactive text content |
204
- | `@click` | Event handling |
205
-
206
- ## Template Directives
207
-
208
- ### Conditionals
209
-
210
- ```html
211
- @if (user.isAdmin)
212
- <AdminPanel />
213
- @elseif (user.isEditor)
214
- <EditorTools />
215
- @else
216
- <UserView />
217
- @endif
218
- ```
219
-
220
- ### Loops
221
-
222
- ```html
223
- @foreach (items as item)
224
- <li>{{ item.name }}</li>
225
- @endforeach
226
-
227
- @for (let i = 0; i < 5; i++)
228
- <li>Item {{ i }}</li>
229
- @endfor
230
- ```
231
-
232
- ### Auth Guards
233
-
234
- ```html
235
- @auth
236
- <p>Welcome back, {{ user.name }}!</p>
237
- @endauth
238
-
239
- @guest
240
- <a href="/login">Please log in</a>
241
- @endguest
242
- ```
243
-
244
- ### Output
245
-
246
- ```html
247
- <!-- Escaped (safe) -->
248
- {{ userInput }}
249
-
250
- <!-- Raw HTML (trusted content only) -->
251
- {!! trustedHtml !!}
252
- ```
253
-
254
- ## Custom Directives
255
-
256
- Register custom directives in your build:
18
+ ## Usage
257
19
 
258
20
  ```typescript
259
- import { stxPlugin, type CustomDirective } from 'bun-plugin-stx'
260
-
261
- const uppercase: CustomDirective = {
262
- name: 'uppercase',
263
- handler: (content, params) => params[0]?.toUpperCase() || content.toUpperCase()
264
- }
265
-
266
- const wrap: CustomDirective = {
267
- name: 'wrap',
268
- hasEndTag: true,
269
- handler: (content, params) => `<div class="${params[0] || 'wrapper'}">${content}</div>`
270
- }
21
+ import { sanitize } from '@stacksjs/sanitizer'
271
22
 
272
- Bun.build({
273
- entrypoints: ['./src/index.stx'],
274
- plugins: [stxPlugin({
275
- customDirectives: [uppercase, wrap]
276
- })]
277
- })
23
+ // Basic sanitization
24
+ const clean = sanitize('<script>alert("xss")</script><p>Hello</p>')
25
+ // => '<p>Hello</p>'
278
26
  ```
279
27
 
280
- ```html
281
- <!-- Usage -->
282
- <p>@uppercase('hello world')</p>
28
+ ### Presets
283
29
 
284
- @wrap('container')
285
- <p>Wrapped content</p>
286
- @endwrap
287
- ```
30
+ ```typescript
31
+ import { sanitize, strict, basic, relaxed, markdown } from '@stacksjs/sanitizer'
288
32
 
289
- ## Icons
33
+ // Strict - minimal tags allowed
34
+ sanitize(html, strict)
290
35
 
291
- 200K+ icons via Iconify:
36
+ // Basic - common formatting tags
37
+ sanitize(html, basic)
292
38
 
293
- ```html
294
- <HomeIcon size="24" />
295
- <SearchIcon size="20" color="#333" />
296
- ```
39
+ // Relaxed - most HTML allowed
40
+ sanitize(html, relaxed)
297
41
 
298
- ```bash
299
- bun stx iconify list
300
- bun stx iconify generate material-symbols
42
+ // Markdown - optimized for markdown output
43
+ sanitize(html, markdown)
301
44
  ```
302
45
 
303
- ## Complete Example
304
-
305
- ```html
306
- <!-- components/TodoApp.stx -->
307
- <script server>
308
- const title = props.title || 'My Todos'
309
- </script>
46
+ ### Utilities
310
47
 
311
- <template>
312
- <div class="todo-app" x-data="{ todos: [], newTodo: '' }">
313
- <h1>{{ title }}</h1>
48
+ ```typescript
49
+ import { isSafe, escape, stripTags } from '@stacksjs/sanitizer'
314
50
 
315
- <form @submit.prevent="todos.push({ text: newTodo, done: false }); newTodo = ''">
316
- <input x-model="newTodo" placeholder="Add todo..." />
317
- <button type="submit">Add</button>
318
- </form>
51
+ // Check if HTML is safe
52
+ isSafe('<p>Hello</p>') // true
319
53
 
320
- @if (initialTodos)
321
- <ul>
322
- @foreach (initialTodos as todo)
323
- <li>{{ todo.text }}</li>
324
- @endforeach
325
- </ul>
326
- @endif
327
- </div>
328
- </template>
54
+ // Escape HTML entities
55
+ escape('<script>') // '&lt;script&gt;'
329
56
 
330
- <style>
331
- .todo-app {
332
- max-width: 400px;
333
- margin: 0 auto;
334
- }
335
- </style>
57
+ // Strip all HTML tags
58
+ stripTags('<p>Hello <b>world</b></p>') // 'Hello world'
336
59
  ```
337
60
 
338
61
  ## Documentation
339
62
 
340
63
  - [Full Documentation](https://stx.sh)
341
- - [Syntax Highlighting Guide](./docs/STX_SYNTAX_HIGHLIGHTING.md)
342
- - [Examples](./examples/)
343
-
344
- ## Testing
345
-
346
- ```bash
347
- bun test
348
- ```
349
64
 
350
65
  ## License
351
66
 
352
67
  MIT
353
68
 
354
69
  <!-- Badges -->
355
- [npm-version-src]: https://img.shields.io/npm/v/@stacksjs/stx?style=flat-square
356
- [npm-version-href]: https://npmjs.com/package/@stacksjs/stx
357
- [npm-downloads-src]: https://img.shields.io/npm/dm/@stacksjs/stx?style=flat-square
358
- [npm-downloads-href]: https://npmjs.com/package/@stacksjs/stx
70
+ [npm-version-src]: https://img.shields.io/npm/v/@stacksjs/sanitizer?style=flat-square
71
+ [npm-version-href]: https://npmjs.com/package/@stacksjs/sanitizer
72
+ [npm-downloads-src]: https://img.shields.io/npm/dm/@stacksjs/sanitizer?style=flat-square
73
+ [npm-downloads-href]: https://npmjs.com/package/@stacksjs/sanitizer
359
74
  [github-actions-src]: https://img.shields.io/github/actions/workflow/status/stacksjs/stx/ci.yml?style=flat-square&branch=main
360
75
  [github-actions-href]: https://github.com/stacksjs/stx/actions?query=workflow%3Aci
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { sanitize } from './sanitizer';
2
2
  export * from './presets';
3
3
  export { basic, getPreset, markdown, relaxed, strict } from './presets';
4
4
  export * from './sanitizer';
5
+ // Re-export for convenience
5
6
  export { escape, isSafe, sanitize, sanitizeWithInfo, stripTags } from './sanitizer';
6
7
  export * from './types';
7
- export default sanitize;
8
+ export default sanitize;
package/dist/presets.d.ts CHANGED
@@ -18,4 +18,4 @@ export declare const relaxed: SanitizerOptions;
18
18
  /**
19
19
  * Markdown preset - Optimized for markdown-generated HTML
20
20
  */
21
- export declare const markdown: SanitizerOptions;
21
+ export declare const markdown: SanitizerOptions;
@@ -18,4 +18,4 @@ export declare function stripTags(html: string): string;
18
18
  /**
19
19
  * Escape HTML for safe display
20
20
  */
21
- export declare function escape(text: string): string;
21
+ export declare function escape(text: string): string;
package/dist/types.d.ts CHANGED
@@ -25,4 +25,4 @@ export declare interface SanitizeResult {
25
25
  /**
26
26
  * Preset configurations
27
27
  */
28
- export type SanitizerPreset = 'strict' | 'basic' | 'relaxed' | 'markdown'
28
+ export type SanitizerPreset = 'strict' | 'basic' | 'relaxed' | 'markdown';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/sanitizer",
3
3
  "type": "module",
4
- "version": "0.2.5",
4
+ "version": "0.2.8",
5
5
  "description": "A fast, native Bun-powered HTML sanitizer with DOMPurify-like features. Protection against XSS and malicious content.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",