@vertz/ui 0.2.12 → 0.2.14
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 +70 -6
- package/dist/shared/{chunk-bjcpcq5j.js → chunk-2sth83bd.js} +1 -1
- package/dist/shared/{chunk-9e92w0wt.js → chunk-83g4h38e.js} +13 -0
- package/dist/shared/{chunk-2rs8a26p.js → chunk-8hsz5y4a.js} +92 -33
- package/dist/shared/{chunk-55tgkc7s.js → chunk-c30eg6wn.js} +1 -1
- package/dist/shared/{chunk-kg898f92.js → chunk-c9xxsrat.js} +7 -2
- package/dist/shared/{chunk-wn4gv1qd.js → chunk-dksg08fq.js} +1 -1
- package/dist/shared/{chunk-g4rch80a.js → chunk-h89w580h.js} +7 -0
- package/dist/shared/chunk-hw67ckr3.js +1212 -0
- package/dist/shared/{chunk-662f9zrb.js → chunk-j6qyxfdc.js} +7 -7
- package/dist/shared/{chunk-g1gf16fz.js → chunk-mj7b4t40.js} +107 -41
- package/dist/shared/{chunk-18jzqefd.js → chunk-nn9v1zmk.js} +4 -4
- package/dist/src/auth/public.d.ts +303 -0
- package/dist/src/auth/public.js +773 -0
- package/dist/src/css/public.js +22 -0
- package/dist/{form → src/form}/public.js +2 -2
- package/dist/{index.d.ts → src/index.d.ts} +218 -14
- package/dist/{index.js → src/index.js} +79 -229
- package/dist/{internals.d.ts → src/internals.d.ts} +265 -3
- package/dist/{internals.js → src/internals.js} +18 -10
- package/dist/{jsx-runtime → src/jsx-runtime}/index.js +1 -1
- package/dist/{query → src/query}/public.d.ts +3 -1
- package/dist/src/query/public.js +15 -0
- package/dist/{router → src/router}/public.d.ts +25 -4
- package/dist/{router → src/router}/public.js +9 -9
- package/dist/{test → src/test}/index.d.ts +12 -2
- package/dist/{test → src/test}/index.js +4 -4
- package/package.json +31 -25
- package/reactivity.json +67 -0
- package/dist/css/public.js +0 -22
- package/dist/query/public.js +0 -15
- package/dist/shared/chunk-9k2z3jfx.js +0 -528
- /package/dist/{css → src/css}/public.d.ts +0 -0
- /package/dist/{form → src/form}/public.d.ts +0 -0
- /package/dist/{jsx-runtime → src/jsx-runtime}/index.d.ts +0 -0
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ function App() {
|
|
|
103
103
|
return <button onClick={() => count++}>Count: {count}</button>;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
const { unmount, root } = mount(App
|
|
106
|
+
const { unmount, root } = mount(App);
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
**With options:**
|
|
@@ -116,20 +116,17 @@ const theme = defineTheme({
|
|
|
116
116
|
colors: { primary: { 500: '#3b82f6' } },
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
-
mount(App,
|
|
119
|
+
mount(App, {
|
|
120
120
|
theme,
|
|
121
121
|
styles: ['body { margin: 0; }'],
|
|
122
122
|
onMount: (root) => console.log('Mounted to', root),
|
|
123
123
|
});
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
`mount(app,
|
|
126
|
+
`mount(app, options?)` accepts:
|
|
127
127
|
|
|
128
|
-
- `selector` — CSS selector string or `HTMLElement`
|
|
129
128
|
- `options.theme` — theme definition for CSS vars
|
|
130
129
|
- `options.styles` — global CSS strings to inject
|
|
131
|
-
- `options.hydration` — `'replace'` (default) or `false`
|
|
132
|
-
- `options.registry` — component registry for per-component hydration
|
|
133
130
|
- `options.onMount` — callback after mount completes
|
|
134
131
|
|
|
135
132
|
Returns a `MountHandle` with `unmount()` and `root`.
|
|
@@ -382,6 +379,63 @@ function App() {
|
|
|
382
379
|
|
|
383
380
|
---
|
|
384
381
|
|
|
382
|
+
## Access Control
|
|
383
|
+
|
|
384
|
+
Client-side entitlement checks using `@vertz/ui/auth`. The server computes what the user can do, embeds it in the JWT, and the client uses it to show/hide UI without network requests.
|
|
385
|
+
|
|
386
|
+
### `can()` -- check entitlements
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
import { can } from '@vertz/ui/auth';
|
|
390
|
+
|
|
391
|
+
function ProjectToolbar() {
|
|
392
|
+
const deleteCheck = can('project:delete');
|
|
393
|
+
|
|
394
|
+
return (
|
|
395
|
+
<div>
|
|
396
|
+
{deleteCheck.allowed && <button onClick={handleDelete}>Delete</button>}
|
|
397
|
+
{deleteCheck.reason === 'plan_required' && <span>Upgrade to unlock</span>}
|
|
398
|
+
{deleteCheck.meta?.limit && (
|
|
399
|
+
<span>{deleteCheck.meta.limit.remaining} uses left</span>
|
|
400
|
+
)}
|
|
401
|
+
</div>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
`can()` returns reactive properties: `allowed`, `reasons`, `reason`, `meta`, and `loading`. The compiler auto-unwraps signal values.
|
|
407
|
+
|
|
408
|
+
For entity-scoped checks, pass the entity as the second argument:
|
|
409
|
+
|
|
410
|
+
```tsx
|
|
411
|
+
function ProjectCard({ project }) {
|
|
412
|
+
const editCheck = can('project:edit', project);
|
|
413
|
+
// reads project.__access if available, falls back to global access set
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### `AccessGate` -- wait for access data
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
import { AccessContext, AccessGate, createAccessProvider } from '@vertz/ui/auth';
|
|
421
|
+
|
|
422
|
+
function App() {
|
|
423
|
+
const accessValue = createAccessProvider();
|
|
424
|
+
|
|
425
|
+
return (
|
|
426
|
+
<AccessContext.Provider value={accessValue}>
|
|
427
|
+
<AccessGate fallback={() => <Spinner />}>
|
|
428
|
+
{() => <Router />}
|
|
429
|
+
</AccessGate>
|
|
430
|
+
</AccessContext.Provider>
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
See the full [Access Control guide](https://vertz.dev/guides/ui/access-control) for SSR hydration, denial reasons, and usage limit display.
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
385
439
|
## Advanced
|
|
386
440
|
|
|
387
441
|
### Watch
|
|
@@ -596,6 +650,16 @@ onMount(() => {
|
|
|
596
650
|
| `parseSearchParams` | Parse URL search parameters |
|
|
597
651
|
| `useSearchParams` | Reactive search parameters |
|
|
598
652
|
|
|
653
|
+
### Auth (`@vertz/ui/auth`)
|
|
654
|
+
|
|
655
|
+
| Export | Description |
|
|
656
|
+
|---|---|
|
|
657
|
+
| `AccessContext` | Context for access set data |
|
|
658
|
+
| `can` | Check if the user has an entitlement |
|
|
659
|
+
| `AccessGate` | Gate rendering until access set loads |
|
|
660
|
+
| `createAccessProvider` | Bootstrap access context from SSR data |
|
|
661
|
+
| `useAccessContext` | Read the access context (throws without provider) |
|
|
662
|
+
|
|
599
663
|
### Testing (`@vertz/ui/test`)
|
|
600
664
|
|
|
601
665
|
| Export | Description |
|
|
@@ -42,6 +42,7 @@ function defineRoutes(map) {
|
|
|
42
42
|
component: config.component,
|
|
43
43
|
errorComponent: config.errorComponent,
|
|
44
44
|
loader: config.loader,
|
|
45
|
+
params: config.params,
|
|
45
46
|
pattern,
|
|
46
47
|
searchParams: config.searchParams
|
|
47
48
|
};
|
|
@@ -60,6 +61,17 @@ function matchRoute(routes, url) {
|
|
|
60
61
|
const leaf = matchRouteRecursive(routes, pathname, matched, allParams);
|
|
61
62
|
if (!leaf)
|
|
62
63
|
return null;
|
|
64
|
+
let parsedParams;
|
|
65
|
+
if (leaf.params) {
|
|
66
|
+
try {
|
|
67
|
+
const parseResult = leaf.params.parse(allParams);
|
|
68
|
+
if (!parseResult.ok)
|
|
69
|
+
return null;
|
|
70
|
+
parsedParams = parseResult.data;
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
63
75
|
let search = {};
|
|
64
76
|
for (const m of matched) {
|
|
65
77
|
if (m.route.searchParams) {
|
|
@@ -77,6 +89,7 @@ function matchRoute(routes, url) {
|
|
|
77
89
|
return {
|
|
78
90
|
matched,
|
|
79
91
|
params: allParams,
|
|
92
|
+
parsedParams,
|
|
80
93
|
route: leaf,
|
|
81
94
|
search,
|
|
82
95
|
searchParams
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// src/ssr/ssr-render-context.ts
|
|
2
|
+
var _ssrResolver = null;
|
|
3
|
+
function registerSSRResolver(resolver) {
|
|
4
|
+
_ssrResolver = resolver;
|
|
5
|
+
}
|
|
6
|
+
function getSSRContext() {
|
|
7
|
+
return _ssrResolver?.();
|
|
8
|
+
}
|
|
9
|
+
|
|
1
10
|
// src/component/context.ts
|
|
2
11
|
var currentScope = null;
|
|
3
12
|
function isSignalLike(value) {
|
|
@@ -54,29 +63,27 @@ function createContext(defaultValue, __stableId) {
|
|
|
54
63
|
Provider(valueOrProps, fn) {
|
|
55
64
|
if (fn !== undefined) {
|
|
56
65
|
const value2 = wrapSignalProps(valueOrProps);
|
|
57
|
-
const parentScope2 =
|
|
66
|
+
const parentScope2 = getContextScope();
|
|
58
67
|
const scope2 = parentScope2 ? new Map(parentScope2) : new Map;
|
|
59
68
|
scope2.set(asKey(ctx), value2);
|
|
60
69
|
ctx._stack.push(value2);
|
|
61
|
-
const prevScope2 =
|
|
62
|
-
currentScope = scope2;
|
|
70
|
+
const prevScope2 = setContextScope(scope2);
|
|
63
71
|
try {
|
|
64
72
|
fn();
|
|
65
73
|
} finally {
|
|
66
74
|
ctx._stack.pop();
|
|
67
|
-
|
|
75
|
+
setContextScope(prevScope2);
|
|
68
76
|
}
|
|
69
77
|
return;
|
|
70
78
|
}
|
|
71
79
|
const props = valueOrProps;
|
|
72
80
|
const { value: rawValue, children } = props;
|
|
73
81
|
const value = wrapSignalProps(rawValue);
|
|
74
|
-
const parentScope =
|
|
82
|
+
const parentScope = getContextScope();
|
|
75
83
|
const scope = parentScope ? new Map(parentScope) : new Map;
|
|
76
84
|
scope.set(asKey(ctx), value);
|
|
77
85
|
ctx._stack.push(value);
|
|
78
|
-
const prevScope =
|
|
79
|
-
currentScope = scope;
|
|
86
|
+
const prevScope = setContextScope(scope);
|
|
80
87
|
try {
|
|
81
88
|
const result = typeof children === "function" ? children() : children;
|
|
82
89
|
if (typeof process !== "undefined" && true && Array.isArray(result)) {
|
|
@@ -85,7 +92,7 @@ function createContext(defaultValue, __stableId) {
|
|
|
85
92
|
return result;
|
|
86
93
|
} finally {
|
|
87
94
|
ctx._stack.pop();
|
|
88
|
-
|
|
95
|
+
setContextScope(prevScope);
|
|
89
96
|
}
|
|
90
97
|
},
|
|
91
98
|
_default: defaultValue,
|
|
@@ -101,15 +108,25 @@ function useContext(ctx) {
|
|
|
101
108
|
return ctx._stack[ctx._stack.length - 1];
|
|
102
109
|
}
|
|
103
110
|
const key = asKey(ctx);
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
const scope = getContextScope();
|
|
112
|
+
if (scope?.has(key)) {
|
|
113
|
+
return scope.get(key);
|
|
106
114
|
}
|
|
107
115
|
return ctx._default;
|
|
108
116
|
}
|
|
109
117
|
function getContextScope() {
|
|
118
|
+
const ctx = getSSRContext();
|
|
119
|
+
if (ctx)
|
|
120
|
+
return ctx.contextScope;
|
|
110
121
|
return currentScope;
|
|
111
122
|
}
|
|
112
123
|
function setContextScope(scope) {
|
|
124
|
+
const ctx = getSSRContext();
|
|
125
|
+
if (ctx) {
|
|
126
|
+
const prev2 = ctx.contextScope;
|
|
127
|
+
ctx.contextScope = scope;
|
|
128
|
+
return prev2;
|
|
129
|
+
}
|
|
113
130
|
const prev = currentScope;
|
|
114
131
|
currentScope = scope;
|
|
115
132
|
return prev;
|
|
@@ -123,26 +140,36 @@ class DisposalScopeError extends Error {
|
|
|
123
140
|
}
|
|
124
141
|
}
|
|
125
142
|
var cleanupStack = [];
|
|
143
|
+
function getCleanupStack() {
|
|
144
|
+
const ctx = getSSRContext();
|
|
145
|
+
if (ctx)
|
|
146
|
+
return ctx.cleanupStack;
|
|
147
|
+
return cleanupStack;
|
|
148
|
+
}
|
|
126
149
|
function onCleanup(fn) {
|
|
127
|
-
const
|
|
150
|
+
const stack = getCleanupStack();
|
|
151
|
+
const current = stack[stack.length - 1];
|
|
128
152
|
if (!current) {
|
|
129
153
|
throw new DisposalScopeError;
|
|
130
154
|
}
|
|
131
155
|
current.push(fn);
|
|
132
156
|
}
|
|
133
157
|
function _tryOnCleanup(fn) {
|
|
134
|
-
const
|
|
158
|
+
const stack = getCleanupStack();
|
|
159
|
+
const current = stack[stack.length - 1];
|
|
135
160
|
if (current) {
|
|
136
161
|
current.push(fn);
|
|
137
162
|
}
|
|
138
163
|
}
|
|
139
164
|
function pushScope() {
|
|
165
|
+
const stack = getCleanupStack();
|
|
140
166
|
const scope = [];
|
|
141
|
-
|
|
167
|
+
stack.push(scope);
|
|
142
168
|
return scope;
|
|
143
169
|
}
|
|
144
170
|
function popScope() {
|
|
145
|
-
|
|
171
|
+
const stack = getCleanupStack();
|
|
172
|
+
stack.pop();
|
|
146
173
|
}
|
|
147
174
|
function runCleanups(cleanups) {
|
|
148
175
|
for (let i = cleanups.length - 1;i >= 0; i--) {
|
|
@@ -155,28 +182,45 @@ function runCleanups(cleanups) {
|
|
|
155
182
|
var currentSubscriber = null;
|
|
156
183
|
var readValueCallback = null;
|
|
157
184
|
function getSubscriber() {
|
|
185
|
+
const ctx = getSSRContext();
|
|
186
|
+
if (ctx)
|
|
187
|
+
return ctx.subscriber;
|
|
158
188
|
return currentSubscriber;
|
|
159
189
|
}
|
|
160
190
|
function setSubscriber(sub) {
|
|
191
|
+
const ctx = getSSRContext();
|
|
192
|
+
if (ctx) {
|
|
193
|
+
const prev2 = ctx.subscriber;
|
|
194
|
+
ctx.subscriber = sub;
|
|
195
|
+
return prev2;
|
|
196
|
+
}
|
|
161
197
|
const prev = currentSubscriber;
|
|
162
198
|
currentSubscriber = sub;
|
|
163
199
|
return prev;
|
|
164
200
|
}
|
|
165
201
|
function getReadValueCallback() {
|
|
202
|
+
const ctx = getSSRContext();
|
|
203
|
+
if (ctx)
|
|
204
|
+
return ctx.readValueCb;
|
|
166
205
|
return readValueCallback;
|
|
167
206
|
}
|
|
168
207
|
function setReadValueCallback(cb) {
|
|
208
|
+
const ctx = getSSRContext();
|
|
209
|
+
if (ctx) {
|
|
210
|
+
const prev2 = ctx.readValueCb;
|
|
211
|
+
ctx.readValueCb = cb;
|
|
212
|
+
return prev2;
|
|
213
|
+
}
|
|
169
214
|
const prev = readValueCallback;
|
|
170
215
|
readValueCallback = cb;
|
|
171
216
|
return prev;
|
|
172
217
|
}
|
|
173
218
|
function untrack(fn) {
|
|
174
|
-
const prev =
|
|
175
|
-
currentSubscriber = null;
|
|
219
|
+
const prev = setSubscriber(null);
|
|
176
220
|
try {
|
|
177
221
|
return fn();
|
|
178
222
|
} finally {
|
|
179
|
-
|
|
223
|
+
setSubscriber(prev);
|
|
180
224
|
}
|
|
181
225
|
}
|
|
182
226
|
|
|
@@ -188,37 +232,52 @@ function scheduleNotify(subscriber) {
|
|
|
188
232
|
subscriber._notify();
|
|
189
233
|
return;
|
|
190
234
|
}
|
|
191
|
-
|
|
192
|
-
|
|
235
|
+
const ctx = getSSRContext();
|
|
236
|
+
const depth = ctx ? ctx.batchDepth : batchDepth;
|
|
237
|
+
const effects = ctx ? ctx.pendingEffects : pendingEffects;
|
|
238
|
+
if (depth > 0) {
|
|
239
|
+
effects.set(subscriber._id, subscriber);
|
|
193
240
|
} else {
|
|
194
241
|
subscriber._notify();
|
|
195
242
|
}
|
|
196
243
|
}
|
|
197
|
-
function flush() {
|
|
198
|
-
while (
|
|
199
|
-
const queue = [...
|
|
200
|
-
|
|
244
|
+
function flush(effects) {
|
|
245
|
+
while (effects.size > 0) {
|
|
246
|
+
const queue = [...effects.values()];
|
|
247
|
+
effects.clear();
|
|
201
248
|
for (const sub of queue) {
|
|
202
249
|
sub._notify();
|
|
203
250
|
}
|
|
204
251
|
}
|
|
205
252
|
}
|
|
206
253
|
function batch(fn) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
254
|
+
const ctx = getSSRContext();
|
|
255
|
+
if (ctx) {
|
|
256
|
+
ctx.batchDepth++;
|
|
257
|
+
try {
|
|
258
|
+
fn();
|
|
259
|
+
} finally {
|
|
260
|
+
ctx.batchDepth--;
|
|
261
|
+
if (ctx.batchDepth === 0) {
|
|
262
|
+
flush(ctx.pendingEffects);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
batchDepth++;
|
|
267
|
+
try {
|
|
268
|
+
fn();
|
|
269
|
+
} finally {
|
|
270
|
+
batchDepth--;
|
|
271
|
+
if (batchDepth === 0) {
|
|
272
|
+
flush(pendingEffects);
|
|
273
|
+
}
|
|
214
274
|
}
|
|
215
275
|
}
|
|
216
276
|
}
|
|
217
277
|
|
|
218
278
|
// src/runtime/signal.ts
|
|
219
279
|
function isSSR() {
|
|
220
|
-
|
|
221
|
-
return typeof check === "function" ? check() : false;
|
|
280
|
+
return getSSRContext() !== undefined;
|
|
222
281
|
}
|
|
223
282
|
var COLLECTOR_KEY = Symbol.for("vertz:signal-collector-stack");
|
|
224
283
|
var _global = globalThis;
|
|
@@ -413,4 +472,4 @@ function lifecycleEffect(fn) {
|
|
|
413
472
|
return dispose;
|
|
414
473
|
}
|
|
415
474
|
|
|
416
|
-
export { createContext, useContext, getContextScope, setContextScope, DisposalScopeError, onCleanup, _tryOnCleanup, pushScope, popScope, runCleanups, setReadValueCallback, untrack, batch, startSignalCollection, stopSignalCollection, signal, computed, domEffect, lifecycleEffect };
|
|
475
|
+
export { registerSSRResolver, getSSRContext, createContext, useContext, getContextScope, setContextScope, DisposalScopeError, onCleanup, _tryOnCleanup, pushScope, popScope, runCleanups, setReadValueCallback, untrack, batch, startSignalCollection, stopSignalCollection, signal, computed, domEffect, lifecycleEffect };
|
|
@@ -3,7 +3,10 @@ import {
|
|
|
3
3
|
__element,
|
|
4
4
|
__enterChildren,
|
|
5
5
|
__exitChildren
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-j6qyxfdc.js";
|
|
7
|
+
import {
|
|
8
|
+
getSSRContext
|
|
9
|
+
} from "./chunk-8hsz5y4a.js";
|
|
7
10
|
|
|
8
11
|
// src/component/children.ts
|
|
9
12
|
var MAX_RESOLVE_DEPTH = 100;
|
|
@@ -618,10 +621,12 @@ var vertzSheets = new Set;
|
|
|
618
621
|
function injectCSS(cssText) {
|
|
619
622
|
if (!cssText)
|
|
620
623
|
return;
|
|
621
|
-
const isSSR =
|
|
624
|
+
const isSSR = getSSRContext() !== undefined;
|
|
622
625
|
if (!isSSR && injectedCSS.has(cssText))
|
|
623
626
|
return;
|
|
624
627
|
injectedCSS.add(cssText);
|
|
628
|
+
if (isSSR)
|
|
629
|
+
return;
|
|
625
630
|
if (typeof document === "undefined")
|
|
626
631
|
return;
|
|
627
632
|
if (typeof CSSStyleSheet !== "undefined" && document.adoptedStyleSheets !== undefined) {
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSSRContext
|
|
3
|
+
} from "./chunk-8hsz5y4a.js";
|
|
4
|
+
|
|
1
5
|
// src/dom/dom-adapter.ts
|
|
2
6
|
function createDOMAdapter() {
|
|
3
7
|
return {
|
|
@@ -21,6 +25,9 @@ function isRenderNode(value) {
|
|
|
21
25
|
}
|
|
22
26
|
var currentAdapter = null;
|
|
23
27
|
function getAdapter() {
|
|
28
|
+
const ctx = getSSRContext();
|
|
29
|
+
if (ctx)
|
|
30
|
+
return ctx.adapter;
|
|
24
31
|
if (!currentAdapter) {
|
|
25
32
|
currentAdapter = createDOMAdapter();
|
|
26
33
|
}
|