@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.
Files changed (35) hide show
  1. package/README.md +70 -6
  2. package/dist/shared/{chunk-bjcpcq5j.js → chunk-2sth83bd.js} +1 -1
  3. package/dist/shared/{chunk-9e92w0wt.js → chunk-83g4h38e.js} +13 -0
  4. package/dist/shared/{chunk-2rs8a26p.js → chunk-8hsz5y4a.js} +92 -33
  5. package/dist/shared/{chunk-55tgkc7s.js → chunk-c30eg6wn.js} +1 -1
  6. package/dist/shared/{chunk-kg898f92.js → chunk-c9xxsrat.js} +7 -2
  7. package/dist/shared/{chunk-wn4gv1qd.js → chunk-dksg08fq.js} +1 -1
  8. package/dist/shared/{chunk-g4rch80a.js → chunk-h89w580h.js} +7 -0
  9. package/dist/shared/chunk-hw67ckr3.js +1212 -0
  10. package/dist/shared/{chunk-662f9zrb.js → chunk-j6qyxfdc.js} +7 -7
  11. package/dist/shared/{chunk-g1gf16fz.js → chunk-mj7b4t40.js} +107 -41
  12. package/dist/shared/{chunk-18jzqefd.js → chunk-nn9v1zmk.js} +4 -4
  13. package/dist/src/auth/public.d.ts +303 -0
  14. package/dist/src/auth/public.js +773 -0
  15. package/dist/src/css/public.js +22 -0
  16. package/dist/{form → src/form}/public.js +2 -2
  17. package/dist/{index.d.ts → src/index.d.ts} +218 -14
  18. package/dist/{index.js → src/index.js} +79 -229
  19. package/dist/{internals.d.ts → src/internals.d.ts} +265 -3
  20. package/dist/{internals.js → src/internals.js} +18 -10
  21. package/dist/{jsx-runtime → src/jsx-runtime}/index.js +1 -1
  22. package/dist/{query → src/query}/public.d.ts +3 -1
  23. package/dist/src/query/public.js +15 -0
  24. package/dist/{router → src/router}/public.d.ts +25 -4
  25. package/dist/{router → src/router}/public.js +9 -9
  26. package/dist/{test → src/test}/index.d.ts +12 -2
  27. package/dist/{test → src/test}/index.js +4 -4
  28. package/package.json +31 -25
  29. package/reactivity.json +67 -0
  30. package/dist/css/public.js +0 -22
  31. package/dist/query/public.js +0 -15
  32. package/dist/shared/chunk-9k2z3jfx.js +0 -528
  33. /package/dist/{css → src/css}/public.d.ts +0 -0
  34. /package/dist/{form → src/form}/public.d.ts +0 -0
  35. /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, '#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, '#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, selector, options?)` accepts:
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 |
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  injectCSS
3
- } from "./chunk-kg898f92.js";
3
+ } from "./chunk-c9xxsrat.js";
4
4
 
5
5
  // src/dom/animation.ts
6
6
  function onAnimationsComplete(el, callback) {
@@ -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 = currentScope;
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 = currentScope;
62
- currentScope = scope2;
70
+ const prevScope2 = setContextScope(scope2);
63
71
  try {
64
72
  fn();
65
73
  } finally {
66
74
  ctx._stack.pop();
67
- currentScope = prevScope2;
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 = currentScope;
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 = currentScope;
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
- currentScope = prevScope;
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
- if (currentScope?.has(key)) {
105
- return currentScope.get(key);
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 current = cleanupStack[cleanupStack.length - 1];
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 current = cleanupStack[cleanupStack.length - 1];
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
- cleanupStack.push(scope);
167
+ stack.push(scope);
142
168
  return scope;
143
169
  }
144
170
  function popScope() {
145
- cleanupStack.pop();
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 = currentSubscriber;
175
- currentSubscriber = null;
219
+ const prev = setSubscriber(null);
176
220
  try {
177
221
  return fn();
178
222
  } finally {
179
- currentSubscriber = prev;
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
- if (batchDepth > 0) {
192
- pendingEffects.set(subscriber._id, subscriber);
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 (pendingEffects.size > 0) {
199
- const queue = [...pendingEffects.values()];
200
- pendingEffects.clear();
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
- batchDepth++;
208
- try {
209
- fn();
210
- } finally {
211
- batchDepth--;
212
- if (batchDepth === 0) {
213
- flush();
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
- const check = typeof globalThis !== "undefined" && globalThis.__VERTZ_IS_SSR__;
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 };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  computed,
3
3
  signal
4
- } from "./chunk-2rs8a26p.js";
4
+ } from "./chunk-8hsz5y4a.js";
5
5
 
6
6
  // src/form/field-state.ts
7
7
  function createFieldState(_name, initialValue) {
@@ -3,7 +3,10 @@ import {
3
3
  __element,
4
4
  __enterChildren,
5
5
  __exitChildren
6
- } from "./chunk-662f9zrb.js";
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 = typeof globalThis.__SSR_URL__ === "string";
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,6 +1,6 @@
1
1
  import {
2
2
  domEffect
3
- } from "./chunk-2rs8a26p.js";
3
+ } from "./chunk-8hsz5y4a.js";
4
4
 
5
5
  // src/dom/attributes.ts
6
6
  function __attr(el, name, fn) {
@@ -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
  }