wcstack 1.22.6
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/LICENSE +21 -0
- package/README.md +304 -0
- package/package.json +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wcstack
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# wcstack
|
|
2
|
+
|
|
3
|
+
**wcstack** is a set of 41 zero-dependency Web Components packages: reactive data binding, declarative SPA routing, and 30+ Web APIs exposed as HTML tags. No build step, no bundler, no framework runtime. One CDN `<script>` tag per package.
|
|
4
|
+
|
|
5
|
+
Project site: **https://wcstack.github.io** · Source: **https://github.com/wcstack/wcstack**
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Read this first if you are an AI coding agent
|
|
10
|
+
|
|
11
|
+
This file is a complete, self-contained guide to writing a correct wcstack app. Read it top to bottom, then **verify what you wrote**:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @wcstack/lint index.html # exit code 0 = clean. Iterate until it exits 0.
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Do not guess at syntax that is not documented here. wcstack has little presence in training data, so invented syntax will look plausible and be wrong. Two rules cover most failures:
|
|
18
|
+
|
|
19
|
+
- **Filters transform values. They never attach to event handlers.**
|
|
20
|
+
- **State must be reassigned, not mutated in place.**
|
|
21
|
+
|
|
22
|
+
Both are spelled out under [What does not work](#what-does-not-work).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Do not install this package
|
|
27
|
+
|
|
28
|
+
This package contains documentation only. wcstack is buildless — load what you need from a CDN:
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<script type="module" src="https://esm.run/@wcstack/state/auto"></script>
|
|
32
|
+
<script type="module" src="https://esm.run/@wcstack/router/auto"></script>
|
|
33
|
+
<script type="module" src="https://esm.run/@wcstack/fetch/auto"></script>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Each `/auto` script registers its custom elements and does nothing else. No initialization call, no bootstrap. Tags activate when the browser parses them.
|
|
37
|
+
|
|
38
|
+
If you do want npm packages, install the individual ones (`@wcstack/state`, `@wcstack/router`, …) rather than this one.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## A complete working app
|
|
43
|
+
|
|
44
|
+
This is a full todo app. Save it as `index.html` and open it in a browser — nothing else is required.
|
|
45
|
+
|
|
46
|
+
```html
|
|
47
|
+
<!DOCTYPE html>
|
|
48
|
+
<html lang="en">
|
|
49
|
+
<head>
|
|
50
|
+
<meta charset="UTF-8">
|
|
51
|
+
<title>Todo</title>
|
|
52
|
+
<script type="module" src="https://esm.run/@wcstack/state/auto"></script>
|
|
53
|
+
<style>
|
|
54
|
+
.done { text-decoration: line-through; color: #999; }
|
|
55
|
+
</style>
|
|
56
|
+
</head>
|
|
57
|
+
<body>
|
|
58
|
+
|
|
59
|
+
<wcs-state>
|
|
60
|
+
<script type="module">
|
|
61
|
+
export default {
|
|
62
|
+
// ---- data ----
|
|
63
|
+
todos: [
|
|
64
|
+
{ id: 1, text: "Read the guide", done: false }
|
|
65
|
+
],
|
|
66
|
+
nextId: 2,
|
|
67
|
+
draft: "",
|
|
68
|
+
filter: "all",
|
|
69
|
+
|
|
70
|
+
// ---- computed: plain getters, recalculated automatically ----
|
|
71
|
+
get visible() {
|
|
72
|
+
if (this.filter === "active") return this.todos.filter(t => !t.done);
|
|
73
|
+
if (this.filter === "done") return this.todos.filter(t => t.done);
|
|
74
|
+
return this.todos;
|
|
75
|
+
},
|
|
76
|
+
get remaining() {
|
|
77
|
+
return this.todos.filter(t => !t.done).length;
|
|
78
|
+
},
|
|
79
|
+
get isEmpty() {
|
|
80
|
+
return this.todos.length === 0;
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// ---- methods: always REASSIGN, never mutate ----
|
|
84
|
+
add() {
|
|
85
|
+
const text = this.draft.trim();
|
|
86
|
+
if (!text) return;
|
|
87
|
+
this.todos = [...this.todos, { id: this.nextId, text, done: false }];
|
|
88
|
+
this.nextId = this.nextId + 1;
|
|
89
|
+
this.draft = "";
|
|
90
|
+
},
|
|
91
|
+
toggle() {
|
|
92
|
+
// Inside a `for:` loop, the current row is readable by wildcard path.
|
|
93
|
+
const id = this["visible.*.id"];
|
|
94
|
+
this.todos = this.todos.map(t => t.id === id ? { ...t, done: !t.done } : t);
|
|
95
|
+
},
|
|
96
|
+
remove() {
|
|
97
|
+
const id = this["visible.*.id"];
|
|
98
|
+
this.todos = this.todos.filter(t => t.id !== id);
|
|
99
|
+
},
|
|
100
|
+
showAll() { this.filter = "all"; },
|
|
101
|
+
showActive() { this.filter = "active"; },
|
|
102
|
+
showDone() { this.filter = "done"; }
|
|
103
|
+
};
|
|
104
|
+
</script>
|
|
105
|
+
</wcs-state>
|
|
106
|
+
|
|
107
|
+
<form data-wcs="onsubmit#prevent: add">
|
|
108
|
+
<input data-wcs="value: draft" placeholder="What needs doing?">
|
|
109
|
+
<button type="submit">Add</button>
|
|
110
|
+
</form>
|
|
111
|
+
|
|
112
|
+
<ul>
|
|
113
|
+
<template data-wcs="for: visible">
|
|
114
|
+
<li>
|
|
115
|
+
<input type="checkbox" data-wcs="checked#ro: .done; onchange: toggle">
|
|
116
|
+
<span data-wcs="textContent: .text; class.done: .done"></span>
|
|
117
|
+
<button type="button" data-wcs="onclick: remove">x</button>
|
|
118
|
+
</li>
|
|
119
|
+
</template>
|
|
120
|
+
</ul>
|
|
121
|
+
|
|
122
|
+
<template data-wcs="if: isEmpty">
|
|
123
|
+
<p>Nothing yet.</p>
|
|
124
|
+
</template>
|
|
125
|
+
|
|
126
|
+
<p><span data-wcs="textContent: remaining"></span> remaining</p>
|
|
127
|
+
|
|
128
|
+
<button type="button" data-wcs="onclick: showAll">All</button>
|
|
129
|
+
<button type="button" data-wcs="onclick: showActive">Active</button>
|
|
130
|
+
<button type="button" data-wcs="onclick: showDone">Done</button>
|
|
131
|
+
|
|
132
|
+
</body>
|
|
133
|
+
</html>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Note `checked#ro:` on the checkbox. Without `#ro`, the two-way binding writes `.done` back on `input`, and the `onchange` handler flips it again — a double toggle that nets to nothing. When a handler is the single writer, mark the reflection read-only.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Binding syntax
|
|
141
|
+
|
|
142
|
+
State and UI are connected by **path strings only**. There are no hooks, selectors, or per-element binding objects.
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
property[#modifier]: path[@state][|filter[|filter(args)]...]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Multiple bindings are separated by `;`:
|
|
149
|
+
|
|
150
|
+
```html
|
|
151
|
+
<div data-wcs="textContent: count; class.over: count|gt(10)"></div>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Properties
|
|
155
|
+
|
|
156
|
+
| Property | Meaning |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `value` | Element value (two-way on inputs) |
|
|
159
|
+
| `checked` | Checkbox / radio state (two-way) |
|
|
160
|
+
| `textContent` / `text` | Text content |
|
|
161
|
+
| `html` | innerHTML |
|
|
162
|
+
| `class.NAME` | Toggle one CSS class |
|
|
163
|
+
| `style.PROP` | Set one style property |
|
|
164
|
+
| `attr.NAME` | Set an attribute (SVG-aware) |
|
|
165
|
+
| `radio` | Radio group (two-way) |
|
|
166
|
+
| `checkbox` | Checkbox group bound to an array (two-way) |
|
|
167
|
+
| `onclick`, `on*` | Event handler |
|
|
168
|
+
|
|
169
|
+
### Modifiers
|
|
170
|
+
|
|
171
|
+
| Modifier | Meaning |
|
|
172
|
+
|---|---|
|
|
173
|
+
| `#ro` | Read-only — disables the two-way write-back |
|
|
174
|
+
| `#prevent` | `event.preventDefault()` on handlers |
|
|
175
|
+
| `#stop` | `event.stopPropagation()` on handlers |
|
|
176
|
+
| `#onchange` | Use `change` instead of `input` for two-way binding |
|
|
177
|
+
| `#init=element` | The element owns the initial value (use with `<wcs-storage>` and monitors) |
|
|
178
|
+
|
|
179
|
+
Combine after one `#`, comma separated: `value#ro,init=none: path`.
|
|
180
|
+
|
|
181
|
+
### Paths
|
|
182
|
+
|
|
183
|
+
| Form | Meaning |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `count`, `user.name` | Plain property path |
|
|
186
|
+
| `items.*.price` | Wildcard — the current row inside a `for:` loop |
|
|
187
|
+
| `.price` | Shorthand for the current row's property inside a loop |
|
|
188
|
+
| `path@cart` | Read from a *named* state element (`<wcs-state name="cart">`) |
|
|
189
|
+
|
|
190
|
+
### Structural directives
|
|
191
|
+
|
|
192
|
+
Always on a `<template>` element:
|
|
193
|
+
|
|
194
|
+
```html
|
|
195
|
+
<template data-wcs="for: items"> ... </template>
|
|
196
|
+
<template data-wcs="if: isReady"> ... </template>
|
|
197
|
+
<template data-wcs="elseif: isLoading"> ... </template>
|
|
198
|
+
<template data-wcs="else:"> ... </template>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Computed values
|
|
202
|
+
|
|
203
|
+
Plain getters. Wildcard getters compute per row, and `$getAll` aggregates across rows:
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
get "cart.items.*.subtotal"() {
|
|
207
|
+
return this["cart.items.*.price"] * this["cart.items.*.quantity"];
|
|
208
|
+
},
|
|
209
|
+
get "cart.total"() {
|
|
210
|
+
return this.$getAll("cart.items.*.subtotal", []).reduce((a, b) => a + b, 0);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Event handlers
|
|
215
|
+
|
|
216
|
+
Handlers receive the event, then the loop indexes:
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
removeItem(event, index) {
|
|
220
|
+
// `index` is the loop position — correct only when the template iterates
|
|
221
|
+
// the same array you are mutating. If you loop over a FILTERED getter,
|
|
222
|
+
// identify the row by id via the wildcard path instead (see the app above).
|
|
223
|
+
this.items = this.items.toSpliced(index, 1);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## What does not work
|
|
230
|
+
|
|
231
|
+
These are the mistakes that actually occur. Each has a working replacement.
|
|
232
|
+
|
|
233
|
+
```html
|
|
234
|
+
<!-- Filters transform VALUES. An event handler never takes a filter. -->
|
|
235
|
+
BAD: <input data-wcs="onkeydown: add|enter">
|
|
236
|
+
GOOD: <input data-wcs="onkeydown: add"> <!-- check event.key inside add() -->
|
|
237
|
+
|
|
238
|
+
<!-- Structural directives require a <template>. -->
|
|
239
|
+
BAD: <div data-wcs="for: items"> ... </div>
|
|
240
|
+
GOOD: <template data-wcs="for: items"> ... </template>
|
|
241
|
+
|
|
242
|
+
<!-- `{{ }}` outside a template causes FOUC. -->
|
|
243
|
+
BAD: <p>{{ count }}</p>
|
|
244
|
+
GOOD: <p><span data-wcs="textContent: count"></span></p>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
// State must be REASSIGNED. In-place mutation is not tracked.
|
|
249
|
+
BAD: this.items.push(x);
|
|
250
|
+
BAD: this.items[0] = x;
|
|
251
|
+
BAD: this.user.name = "new";
|
|
252
|
+
GOOD: this.items = [...this.items, x];
|
|
253
|
+
GOOD: this["items.0"] = x;
|
|
254
|
+
GOOD: this["user.name"] = "new";
|
|
255
|
+
|
|
256
|
+
// Immutable array methods are the idiom.
|
|
257
|
+
this.items = this.items.toSpliced(index, 1);
|
|
258
|
+
this.items = this.items.map(t => t.id === id ? { ...t, done: true } : t);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Verify before you finish
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# Check any HTML against the data-wcs contract. No install, no config.
|
|
267
|
+
npx @wcstack/lint index.html
|
|
268
|
+
|
|
269
|
+
# Errors only, for a generate-validate-fix loop:
|
|
270
|
+
npx @wcstack/lint --errors-only index.html
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Exit code `0` means clean, `1` means at least one error-severity finding, `2` means a usage or read failure. Diagnostics carry stable `wcs/*` codes and `source:line:col` ranges.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## The rest of the stack
|
|
278
|
+
|
|
279
|
+
`<wcs-state>` is one package. Every other capability is a tag that speaks the same binding protocol, so they compose without glue code.
|
|
280
|
+
|
|
281
|
+
| Package | Tag | Role |
|
|
282
|
+
|---|---|---|
|
|
283
|
+
| `@wcstack/state` | `<wcs-state>` | Reactive state + `data-wcs` binding |
|
|
284
|
+
| `@wcstack/router` | `<wcs-router>` | Declarative SPA routing (Navigation API) |
|
|
285
|
+
| `@wcstack/autoloader` | — | Import-Map-driven auto-registration of components |
|
|
286
|
+
| `@wcstack/signals` | — | Signals core (`signal` / `computed` / `effect`), JS-first alternative |
|
|
287
|
+
| `@wcstack/fetch` | `<wcs-fetch>` | HTTP with automatic re-fetch on dependency change |
|
|
288
|
+
| `@wcstack/storage` | `<wcs-storage>` | localStorage / sessionStorage |
|
|
289
|
+
| `@wcstack/websocket` | `<wcs-ws>` | WebSocket |
|
|
290
|
+
| `@wcstack/sse` | `<wcs-sse>` | Server-Sent Events |
|
|
291
|
+
| `@wcstack/lint` | — | Static-contract validator CLI |
|
|
292
|
+
| `@wcstack/devtools` | — | In-page inspector overlay |
|
|
293
|
+
|
|
294
|
+
Plus 25+ more wrapping camera, speech, geolocation, notifications, clipboard, sensors, observers, and other Web APIs. Full catalog: https://wcstack.github.io
|
|
295
|
+
|
|
296
|
+
### Deeper references
|
|
297
|
+
|
|
298
|
+
- **Agent skill** (complete binding syntax, router skeletons, tag catalog): https://github.com/wcstack/wcstack-skill
|
|
299
|
+
- **Repository guide for agents**: https://github.com/wcstack/wcstack/blob/main/AGENTS.md
|
|
300
|
+
- **Per-package docs**: `npm view @wcstack/state readme`, `npm view @wcstack/router readme`, …
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wcstack",
|
|
3
|
+
"version": "1.22.6",
|
|
4
|
+
"description": "Entry point for wcstack - 41 zero-dependency Web Components packages. Reactive data binding, declarative routing and 30+ Web APIs as HTML tags. Buildless: one CDN script tag per package. Run `npm view wcstack readme` for the full authoring guide.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"web-components",
|
|
7
|
+
"custom-elements",
|
|
8
|
+
"reactive",
|
|
9
|
+
"data-binding",
|
|
10
|
+
"state-management",
|
|
11
|
+
"router",
|
|
12
|
+
"spa",
|
|
13
|
+
"buildless",
|
|
14
|
+
"zero-dependencies",
|
|
15
|
+
"declarative",
|
|
16
|
+
"wcstack"
|
|
17
|
+
],
|
|
18
|
+
"author": "mogera551",
|
|
19
|
+
"homepage": "https://wcstack.github.io",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/wcstack/wcstack.git",
|
|
23
|
+
"directory": "packages/wcstack"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/wcstack/wcstack/issues"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"files": [
|
|
30
|
+
"README.md"
|
|
31
|
+
]
|
|
32
|
+
}
|