regor 1.4.4 → 1.4.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/README.md CHANGED
@@ -97,6 +97,132 @@ HTML:
97
97
  </div>
98
98
  ```
99
99
 
100
+ ## Component Props Validation
101
+
102
+ Regor components can validate incoming props at runtime inside `context(head)`.
103
+
104
+ This is opt-in and local to the component author:
105
+
106
+ - it does not change `defineComponent(...)`
107
+ - it validates only the keys you list
108
+ - it throws immediately on the first invalid prop
109
+ - it does not coerce values
110
+ - it does not mutate `head.props`
111
+
112
+ Use `head.validateProps(...)` together with `pval`:
113
+
114
+ ```ts
115
+ import { defineComponent, html, pval } from 'regor'
116
+
117
+ type EditorCard = {
118
+ title: string
119
+ count?: number
120
+ mode: 'create' | 'edit'
121
+ summary?: string
122
+ }
123
+
124
+ const editorCard = defineComponent<EditorCard>(
125
+ html`<article>{{ summary }}</article>`,
126
+ {
127
+ props: ['title', 'count', 'mode'],
128
+ context: (head) => {
129
+ head.validateProps({
130
+ title: pval.isString,
131
+ count: pval.optional(pval.isNumber),
132
+ mode: pval.oneOf(['create', 'edit'] as const),
133
+ })
134
+
135
+ return {
136
+ ...head.props,
137
+ summary: `${head.props.title}:${head.props.mode}:${head.props.count ?? 'none'}`,
138
+ }
139
+ },
140
+ },
141
+ )
142
+ ```
143
+
144
+ ### Built-in validators
145
+
146
+ ```ts
147
+ import { pval } from 'regor'
148
+
149
+ pval.isString
150
+ pval.isNumber
151
+ pval.isBoolean
152
+ pval.isClass(MyClass)
153
+ pval.optional(pval.isString)
154
+ pval.nullable(pval.isNumber)
155
+ pval.oneOf(['create', 'edit'] as const)
156
+ pval.arrayOf(pval.isString)
157
+ pval.shape({ title: pval.isString, count: pval.isNumber })
158
+ pval.refOf(pval.isString)
159
+ ```
160
+
161
+ ### Dynamic bindings and refs
162
+
163
+ Single-prop dynamic bindings like `:title="titleRef"` flow into component props as refs.
164
+ When validating those runtime values, use `pval.refOf(...)`:
165
+
166
+ ```ts
167
+ type CardProps = {
168
+ title: Ref<string>
169
+ summary?: string
170
+ }
171
+
172
+ const card = defineComponent<CardProps>(html`<h3>{{ summary }}</h3>`, {
173
+ props: ['title'],
174
+ context: (head) => {
175
+ head.validateProps({
176
+ title: pval.refOf(pval.isString),
177
+ })
178
+
179
+ return {
180
+ ...head.props,
181
+ summary: head.props.title(),
182
+ }
183
+ },
184
+ })
185
+ ```
186
+
187
+ For object-style `:context="{ ... }"` values, validate the plain runtime shape directly:
188
+
189
+ ```ts
190
+ head.validateProps({
191
+ meta: pval.shape({
192
+ slug: pval.isString,
193
+ }),
194
+ })
195
+ ```
196
+
197
+ ### Custom validators
198
+
199
+ Users can provide their own validators as long as they match the `PropValidator<T>` signature:
200
+
201
+ ```ts
202
+ import { type PropValidator } from 'regor'
203
+
204
+ const isNonEmptyString: PropValidator<string> = (value, name) => {
205
+ if (typeof value !== 'string' || value.trim() === '') {
206
+ throw new Error(`Invalid prop "${name}": expected non-empty string.`)
207
+ }
208
+ }
209
+
210
+ head.validateProps({
211
+ title: isNonEmptyString,
212
+ })
213
+ ```
214
+
215
+ Custom validators can also use the third `head` argument:
216
+
217
+ ```ts
218
+ const startsWithPrefix: PropValidator<string> = (value, name, head) => {
219
+ const ctx = head.requireContext(AppServices)
220
+ if (typeof value !== 'string' || !value.startsWith(ctx.prefix)) {
221
+ throw new Error(`Invalid prop "${name}": expected prefixed value.`)
222
+ }
223
+ }
224
+ ```
225
+
100
226
  ## Table Templates and Components
101
227
 
102
228
  Regor preprocesses table-related templates to keep markup valid when using
@@ -223,6 +349,7 @@ These directives empower you to create dynamic and interactive user interfaces,
223
349
 
224
350
  - **`createApp`** Similar to Vue's `createApp`, it initializes a Regor application instance.
225
351
  - **`defineComponent`** Creates a Regor component instance.
352
+ - **`pval`** Built-in component prop validators used with `head.validateProps(...)`.
226
353
  - **`toFragment`** Converts a JSON template to a document fragment.
227
354
  - **`toJsonTemplate`** Converts a DOM element to a JSON template.
228
355
 
package/dist/regor.d.ts CHANGED
@@ -1,5 +1,58 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
+ /**
4
+ * Assertion-style runtime validator used by `head.validateProps(...)`.
5
+ *
6
+ * A validator should throw when the value is invalid and return normally when
7
+ * the value satisfies the expected runtime contract.
8
+ *
9
+ * @typeParam TValue - Value type asserted by the validator when it succeeds.
10
+ * @param value - Raw incoming prop value.
11
+ * @param name - Prop name or nested path currently being validated.
12
+ * @param head - Current component head, useful for context-aware validation.
13
+ */
14
+ export type PropValidator<TValue = unknown> = (value: unknown, name: string, head: ComponentHead<any>) => asserts value is TValue;
15
+ export type ValidationSchemaLike = Record<string, PropValidator<any>>;
16
+ /**
17
+ * Validation schema shape suggested by `ComponentHead<T>.props`.
18
+ *
19
+ * Every key is optional so component authors can validate only the subset they
20
+ * care about. Editor completion is still driven by the known prop keys.
21
+ */
22
+ export type PropValidationSchemaFor<TProps extends object> = {
23
+ [TKey in keyof TProps]?: PropValidator<TProps[TKey]>;
24
+ };
25
+ /**
26
+ * Infers the asserted value types from a validation schema.
27
+ *
28
+ * Keys whose values are not validators are ignored.
29
+ */
30
+ export type InferPropValidationSchema<TSchema extends Record<string, unknown>> = {
31
+ [TKey in keyof TSchema as TSchema[TKey] extends PropValidator<any> ? TKey : never]: TSchema[TKey] extends PropValidator<infer TValue> ? TValue : never;
32
+ };
33
+ /**
34
+ * Built-in prop-validator namespace used with `head.validateProps(...)`.
35
+ *
36
+ * Example:
37
+ * ```ts
38
+ * head.validateProps({
39
+ * title: pval.isString,
40
+ * count: pval.optional(pval.isNumber),
41
+ * })
42
+ * ```
43
+ */
44
+ export declare const pval: {
45
+ readonly isString: PropValidator<string>;
46
+ readonly isNumber: PropValidator<number>;
47
+ readonly isBoolean: PropValidator<boolean>;
48
+ readonly isClass: <TValue extends object>(ctor: abstract new (...args: any[]) => TValue) => PropValidator<TValue>;
49
+ readonly optional: <TValue>(validator: PropValidator<TValue>) => PropValidator<TValue | undefined>;
50
+ readonly nullable: <TValue>(validator: PropValidator<TValue>) => PropValidator<TValue | null>;
51
+ readonly oneOf: <const TValue extends readonly unknown[]>(values: TValue) => PropValidator<TValue[number]>;
52
+ readonly arrayOf: <TValue>(validator: PropValidator<TValue>) => PropValidator<TValue[]>;
53
+ readonly shape: <TSchema extends ValidationSchemaLike>(schema: TSchema) => PropValidator<InferPropValidationSchema<TSchema>>;
54
+ readonly refOf: <TValue>(validator: PropValidator<TValue>) => PropValidator<AnyRef>;
55
+ };
3
56
  export type ContextClass<TValue extends object> = abstract new (...args: never[]) => TValue;
4
57
  /**
5
58
  * Runtime metadata passed to a component's `context(head)` factory.
@@ -170,6 +223,29 @@ export declare class ComponentHead<TContext extends IRegorContext | object = IRe
170
223
  * @throws Error when no matching instance exists at the requested occurrence.
171
224
  */
172
225
  requireContext<TValue extends object>(constructor: ContextClass<TValue>, occurrence?: number): TValue;
226
+ /**
227
+ * Validates selected incoming props using assertion-style validators.
228
+ *
229
+ * Only keys listed in `schema` are checked. Validation throws immediately
230
+ * on the first invalid prop and does not mutate `head.props`.
231
+ *
232
+ * The schema is keyed from `head.props`, so editor completion can suggest
233
+ * known prop names while still allowing you to validate only a subset.
234
+ *
235
+ * Validators typically come from `pval`, but custom user validators are also
236
+ * supported.
237
+ *
238
+ * Example:
239
+ * ```ts
240
+ * head.validateProps({
241
+ * title: pval.isString,
242
+ * count: pval.optional(pval.isNumber),
243
+ * })
244
+ * ```
245
+ *
246
+ * @param schema - Validators to apply to selected incoming props.
247
+ */
248
+ validateProps<TSchema extends PropValidationSchemaFor<TContext>>(schema: TSchema): asserts this is ComponentHead<TContext & InferPropValidationSchema<TSchema>>;
173
249
  /**
174
250
  * Unmounts this component instance by removing nodes between `start` and `end`
175
251
  * and calling unmount lifecycle handlers for captured contexts.
@@ -87,6 +87,7 @@ __export(index_exports, {
87
87
  onUnmounted: () => onUnmounted,
88
88
  pause: () => pause,
89
89
  persist: () => persist,
90
+ pval: () => pval,
90
91
  raw: () => raw,
91
92
  ref: () => ref,
92
93
  removeNode: () => removeNode,
@@ -418,6 +419,37 @@ var ComponentHead = class {
418
419
  `${constructor} was not found in the context stack at occurrence ${occurrence}.`
419
420
  );
420
421
  }
422
+ /**
423
+ * Validates selected incoming props using assertion-style validators.
424
+ *
425
+ * Only keys listed in `schema` are checked. Validation throws immediately
426
+ * on the first invalid prop and does not mutate `head.props`.
427
+ *
428
+ * The schema is keyed from `head.props`, so editor completion can suggest
429
+ * known prop names while still allowing you to validate only a subset.
430
+ *
431
+ * Validators typically come from `pval`, but custom user validators are also
432
+ * supported.
433
+ *
434
+ * Example:
435
+ * ```ts
436
+ * head.validateProps({
437
+ * title: pval.isString,
438
+ * count: pval.optional(pval.isNumber),
439
+ * })
440
+ * ```
441
+ *
442
+ * @param schema - Validators to apply to selected incoming props.
443
+ */
444
+ validateProps(schema) {
445
+ const props = this.props;
446
+ for (const name in schema) {
447
+ const validator = schema[name];
448
+ if (!validator) continue;
449
+ const validateProp = validator;
450
+ validateProp(props[name], name, this);
451
+ }
452
+ }
421
453
  /**
422
454
  * Unmounts this component instance by removing nodes between `start` and `end`
423
455
  * and calling unmount lifecycle handlers for captured contexts.
@@ -566,10 +598,13 @@ var IfBinder = class {
566
598
  }
567
599
  __bindAll(element) {
568
600
  const isIfElement = element.hasAttribute(this.__if);
569
- const elements = findElements(element, this.__ifSelector);
570
- for (const el of elements) {
571
- this.__bind(el);
572
- }
601
+ if (isIfElement) this.__bind(element);
602
+ this.__binder.__componentBinder.__forEachBindableDescendant(
603
+ element,
604
+ (el) => {
605
+ if (el.hasAttribute(this.__if)) this.__bind(el);
606
+ }
607
+ );
573
608
  return isIfElement;
574
609
  }
575
610
  __isProcessedOrMark(el) {
@@ -1361,8 +1396,16 @@ var ComponentBinder = class {
1361
1396
  }
1362
1397
  return false;
1363
1398
  }
1399
+ __isNamedSlotTemplateShortcut(node) {
1400
+ if (!isTemplate(node)) return false;
1401
+ const attributeNames = node.getAttributeNames();
1402
+ if (node.hasAttribute("name")) return true;
1403
+ return attributeNames.some((x) => x.startsWith("#"));
1404
+ }
1405
+ __isDefaultSlotTemplateShortcut(node) {
1406
+ return isTemplate(node) && node.getAttributeNames().length === 0;
1407
+ }
1364
1408
  __unwrapComponents(element) {
1365
- var _a;
1366
1409
  const binder = this.__binder;
1367
1410
  const parser = binder.__parser;
1368
1411
  const registeredComponents = binder.__config.__components;
@@ -1371,16 +1414,9 @@ var ComponentBinder = class {
1371
1414
  return;
1372
1415
  }
1373
1416
  const contextComponents = parser.__getComponents();
1374
- const contextComponentSelectors = parser.__getComponentSelectors();
1375
- const registeredSelector = this.__getRegisteredComponentSelector(registeredComponents);
1376
- const selector = [
1377
- ...registeredSelector ? [registeredSelector] : [],
1378
- ...contextComponentSelectors,
1379
- ...contextComponentSelectors.map(hyphenate)
1380
- ].join(",");
1417
+ const selector = this.__getComponentSelector();
1381
1418
  if (isNullOrWhitespace(selector)) return;
1382
- const list = element.querySelectorAll(selector);
1383
- const components = ((_a = element.matches) == null ? void 0 : _a.call(element, selector)) ? [element, ...list] : list;
1419
+ const components = this.__collectTopLevelComponentHosts(element, selector);
1384
1420
  for (const component of components) {
1385
1421
  if (component.hasAttribute(binder.__pre)) continue;
1386
1422
  const parent = component.parentNode;
@@ -1454,7 +1490,7 @@ var ComponentBinder = class {
1454
1490
  };
1455
1491
  const capturedContext = [...parser.__capture()];
1456
1492
  const createComponentCtx = () => {
1457
- var _a2;
1493
+ var _a;
1458
1494
  const props = getProps(component, capturedContext);
1459
1495
  const head2 = new ComponentHead(
1460
1496
  props,
@@ -1464,8 +1500,8 @@ var ComponentBinder = class {
1464
1500
  endOfComponent
1465
1501
  );
1466
1502
  const componentCtx2 = useScope(() => {
1467
- var _a3;
1468
- return (_a3 = registeredComponent.context(head2)) != null ? _a3 : {};
1503
+ var _a2;
1504
+ return (_a2 = registeredComponent.context(head2)) != null ? _a2 : {};
1469
1505
  }).context;
1470
1506
  if (head2.autoProps) {
1471
1507
  for (const [key, propsValue] of Object.entries(props)) {
@@ -1489,7 +1525,7 @@ var ComponentBinder = class {
1489
1525
  }
1490
1526
  } else componentCtx2[key] = propsValue;
1491
1527
  }
1492
- (_a2 = head2.onAutoPropsAssigned) == null ? void 0 : _a2.call(head2);
1528
+ (_a = head2.onAutoPropsAssigned) == null ? void 0 : _a.call(head2);
1493
1529
  }
1494
1530
  return { componentCtx: componentCtx2, head: head2 };
1495
1531
  };
@@ -1498,6 +1534,7 @@ var ComponentBinder = class {
1498
1534
  const len = childNodes.length;
1499
1535
  const isEmptyComponent = component.childNodes.length === 0;
1500
1536
  const expandSlot = (slot) => {
1537
+ var _a;
1501
1538
  const parent2 = slot.parentElement;
1502
1539
  let name = slot.name;
1503
1540
  if (isNullOrWhitespace(name)) {
@@ -1527,9 +1564,12 @@ var ComponentBinder = class {
1527
1564
  `template[name='${name}'], template[\\#${name}]`
1528
1565
  );
1529
1566
  if (!compTemplate && name === "default") {
1530
- compTemplate = component.querySelector("template:not([name])");
1531
- if (compTemplate && compTemplate.getAttributeNames().filter((x) => x.startsWith("#")).length > 0)
1532
- compTemplate = null;
1567
+ const unnamedTemplates = component.querySelectorAll(
1568
+ "template:not([name])"
1569
+ );
1570
+ compTemplate = (_a = [...unnamedTemplates].find(
1571
+ (x) => this.__isDefaultSlotTemplateShortcut(x)
1572
+ )) != null ? _a : null;
1533
1573
  }
1534
1574
  const createSwitchContext = (childNodes2) => {
1535
1575
  if (!head.enableSwitch) return;
@@ -1565,7 +1605,7 @@ var ComponentBinder = class {
1565
1605
  return;
1566
1606
  }
1567
1607
  const childNodes2 = [...getChildNodes(component)].filter(
1568
- (x) => !isTemplate(x)
1608
+ (x) => !this.__isNamedSlotTemplateShortcut(x)
1569
1609
  );
1570
1610
  for (const slotChild of childNodes2) {
1571
1611
  parent2.insertBefore(slotChild, slot);
@@ -1651,6 +1691,65 @@ var ComponentBinder = class {
1651
1691
  parser.__scoped(capturedContext, bindComponent);
1652
1692
  }
1653
1693
  }
1694
+ __getComponentSelector() {
1695
+ const binder = this.__binder;
1696
+ const parser = binder.__parser;
1697
+ const registeredComponents = binder.__config.__components;
1698
+ const contextComponentSelectors = parser.__getComponentSelectors();
1699
+ const registeredSelector = this.__getRegisteredComponentSelector(registeredComponents);
1700
+ return [
1701
+ ...registeredSelector ? [registeredSelector] : [],
1702
+ ...contextComponentSelectors,
1703
+ ...contextComponentSelectors.map(hyphenate)
1704
+ ].join(",");
1705
+ }
1706
+ __collectTopLevelComponentHosts(root, selector) {
1707
+ var _a;
1708
+ const result = [];
1709
+ if (isNullOrWhitespace(selector)) return result;
1710
+ if ((_a = root.matches) == null ? void 0 : _a.call(root, selector)) return [root];
1711
+ const stack = this.__getChildElements(root).reverse();
1712
+ while (stack.length > 0) {
1713
+ const current = stack.pop();
1714
+ if (current.matches(selector)) {
1715
+ result.push(current);
1716
+ continue;
1717
+ }
1718
+ stack.push(...this.__getChildElements(current).reverse());
1719
+ }
1720
+ return result;
1721
+ }
1722
+ __forEachBindableDescendant(root, action) {
1723
+ const selector = this.__getComponentSelector();
1724
+ const stack = this.__getChildElements(root).reverse();
1725
+ while (stack.length > 0) {
1726
+ const current = stack.pop();
1727
+ action(current);
1728
+ if (!isNullOrWhitespace(selector) && current.matches(selector)) continue;
1729
+ stack.push(...this.__getChildElements(current).reverse());
1730
+ }
1731
+ }
1732
+ __getChildElements(root) {
1733
+ const children = root == null ? void 0 : root.children;
1734
+ if ((children == null ? void 0 : children.length) != null) {
1735
+ const result = [];
1736
+ for (let i = 0; i < children.length; ++i) {
1737
+ const child = children[i];
1738
+ if (isElement(child)) result.push(child);
1739
+ }
1740
+ return result;
1741
+ }
1742
+ const childNodes = root == null ? void 0 : root.childNodes;
1743
+ if ((childNodes == null ? void 0 : childNodes.length) != null) {
1744
+ const result = [];
1745
+ for (let i = 0; i < childNodes.length; ++i) {
1746
+ const child = childNodes[i];
1747
+ if (isElement(child)) result.push(child);
1748
+ }
1749
+ return result;
1750
+ }
1751
+ return [];
1752
+ }
1654
1753
  };
1655
1754
 
1656
1755
  // src/bind/DirectiveCollector.ts
@@ -1743,10 +1842,10 @@ var DirectiveCollector = class {
1743
1842
  };
1744
1843
  processNode(element);
1745
1844
  if (!isRecursive || !element.firstElementChild) return map;
1746
- const nodes = element.querySelectorAll("*");
1747
- for (const node of nodes) {
1748
- processNode(node);
1749
- }
1845
+ this.__binder.__componentBinder.__forEachBindableDescendant(
1846
+ element,
1847
+ processNode
1848
+ );
1750
1849
  return map;
1751
1850
  }
1752
1851
  };
@@ -1764,17 +1863,19 @@ var DynamicBinder = class {
1764
1863
  constructor(binder) {
1765
1864
  __publicField(this, "__binder");
1766
1865
  __publicField(this, "__is");
1767
- __publicField(this, "__isSelector");
1768
1866
  this.__binder = binder;
1769
1867
  this.__is = binder.__config.__builtInNames.is;
1770
- this.__isSelector = toSelector(this.__is) + ", [is]";
1771
1868
  }
1772
1869
  __bindAll(element) {
1773
1870
  const isComponentElement = element.hasAttribute(this.__is);
1774
- const elements = findElements(element, this.__isSelector);
1775
- for (const el of elements) {
1776
- this.__bind(el);
1777
- }
1871
+ if (isComponentElement || element.hasAttribute("is"))
1872
+ this.__bind(element);
1873
+ this.__binder.__componentBinder.__forEachBindableDescendant(
1874
+ element,
1875
+ (el) => {
1876
+ if (el.hasAttribute(this.__is) || el.hasAttribute("is")) this.__bind(el);
1877
+ }
1878
+ );
1778
1879
  return isComponentElement;
1779
1880
  }
1780
1881
  __bind(el) {
@@ -2251,10 +2352,13 @@ var _ForBinder = class _ForBinder {
2251
2352
  }
2252
2353
  __bindAll(element) {
2253
2354
  const isForElement = element.hasAttribute(this.__for);
2254
- const elements = findElements(element, this.__forSelector);
2255
- for (const el of elements) {
2256
- this.__bindFor(el);
2257
- }
2355
+ if (isForElement) this.__bindFor(element);
2356
+ this.__binder.__componentBinder.__forEachBindableDescendant(
2357
+ element,
2358
+ (el) => {
2359
+ if (el.hasAttribute(this.__for)) this.__bindFor(el);
2360
+ }
2361
+ );
2258
2362
  return isForElement;
2259
2363
  }
2260
2364
  __isProcessedOrMark(el) {
@@ -2931,11 +3035,11 @@ var patchAttribute = (el, key, value, previousKey) => {
2931
3035
  }
2932
3036
  return;
2933
3037
  }
2934
- const isBoolean = key in booleanAttributes;
2935
- if (isNullOrUndefined(value) || isBoolean && !includeBooleanAttr(value)) {
3038
+ const isBoolean2 = key in booleanAttributes;
3039
+ if (isNullOrUndefined(value) || isBoolean2 && !includeBooleanAttr(value)) {
2936
3040
  el.removeAttribute(key);
2937
3041
  } else {
2938
- el.setAttribute(key, isBoolean ? "" : value);
3042
+ el.setAttribute(key, isBoolean2 ? "" : value);
2939
3043
  }
2940
3044
  };
2941
3045
 
@@ -3185,7 +3289,7 @@ var decimalSeparators = /[.,' ·٫]/;
3185
3289
  var handleInputAndTextArea = (el, flags, getModelRef, parsedValue) => {
3186
3290
  const isLazy = flags.lazy;
3187
3291
  const eventType = isLazy ? "change" : "input";
3188
- const isNumber = isNumberInput(el);
3292
+ const isNumber2 = isNumberInput(el);
3189
3293
  const trimmer = () => {
3190
3294
  if (!flags.trim && !getFlags(parsedValue()[1]).trim) return;
3191
3295
  el.value = el.value.trim();
@@ -3215,7 +3319,7 @@ var handleInputAndTextArea = (el, flags, getModelRef, parsedValue) => {
3215
3319
  if (!target || target.composing) return;
3216
3320
  let value = target.value;
3217
3321
  const flags2 = getFlags(parsedValue()[1]);
3218
- if (isNumber || flags2.number || flags2.int) {
3322
+ if (isNumber2 || flags2.number || flags2.int) {
3219
3323
  if (flags2.int) {
3220
3324
  value = parseInt(value);
3221
3325
  } else {
@@ -4530,12 +4634,12 @@ var Jsep = class {
4530
4634
  this.__gobbleSpaces();
4531
4635
  let ch = this.__code;
4532
4636
  while (ch === PERIOD_CODE || ch === OBRACK_CODE || ch === OPAREN_CODE || ch === QUMARK_CODE) {
4533
- let optional;
4637
+ let optional2;
4534
4638
  if (ch === QUMARK_CODE) {
4535
4639
  if (this.__expr.charCodeAt(this.__index + 1) !== PERIOD_CODE) {
4536
4640
  break;
4537
4641
  }
4538
- optional = true;
4642
+ optional2 = true;
4539
4643
  this.__index += 2;
4540
4644
  this.__gobbleSpaces();
4541
4645
  ch = this.__code;
@@ -4561,7 +4665,7 @@ var Jsep = class {
4561
4665
  callee: node
4562
4666
  };
4563
4667
  } else {
4564
- if (optional) {
4668
+ if (optional2) {
4565
4669
  this.__index--;
4566
4670
  }
4567
4671
  this.__gobbleSpaces();
@@ -4572,7 +4676,7 @@ var Jsep = class {
4572
4676
  property: this.__gobbleIdentifier()
4573
4677
  };
4574
4678
  }
4575
- if (optional) {
4679
+ if (optional2) {
4576
4680
  node.optional = true;
4577
4681
  }
4578
4682
  this.__gobbleSpaces();
@@ -6102,6 +6206,113 @@ var defineComponent = (template, options = {}) => {
6102
6206
  };
6103
6207
  };
6104
6208
 
6209
+ // src/app/propValidators.ts
6210
+ var fail = (name, message) => {
6211
+ throw new Error(`Invalid prop "${name}": ${message}.`);
6212
+ };
6213
+ var describeValue = (value) => {
6214
+ var _a;
6215
+ if (value === null) return "null";
6216
+ if (value === void 0) return "undefined";
6217
+ if (typeof value === "string") return "string";
6218
+ if (typeof value === "number") return "number";
6219
+ if (typeof value === "boolean") return "boolean";
6220
+ if (typeof value === "bigint") return "bigint";
6221
+ if (typeof value === "symbol") return "symbol";
6222
+ if (typeof value === "function") return "function";
6223
+ if (isArray(value)) return "array";
6224
+ if (value instanceof Date) return "Date";
6225
+ if (value instanceof RegExp) return "RegExp";
6226
+ if (value instanceof Map) return "Map";
6227
+ if (value instanceof Set) return "Set";
6228
+ const ctorName = (_a = value == null ? void 0 : value.constructor) == null ? void 0 : _a.name;
6229
+ return ctorName && ctorName !== "Object" ? ctorName : "object";
6230
+ };
6231
+ var formatLiteral = (value) => {
6232
+ if (typeof value === "string") return `"${value}"`;
6233
+ if (typeof value === "number" || typeof value === "boolean") {
6234
+ return String(value);
6235
+ }
6236
+ if (value === null) return "null";
6237
+ if (value === void 0) return "undefined";
6238
+ return describeValue(value);
6239
+ };
6240
+ var isString2 = (value, name) => {
6241
+ if (typeof value !== "string") fail(name, "expected string");
6242
+ };
6243
+ var isNumber = (value, name) => {
6244
+ if (typeof value !== "number") fail(name, "expected number");
6245
+ };
6246
+ var isBoolean = (value, name) => {
6247
+ if (typeof value !== "boolean") fail(name, "expected boolean");
6248
+ };
6249
+ var isClass = (ctor) => {
6250
+ return (value, name) => {
6251
+ if (!(value instanceof ctor)) {
6252
+ fail(name, `expected instance of ${ctor.name || "provided class"}`);
6253
+ }
6254
+ };
6255
+ };
6256
+ var optional = (validator) => {
6257
+ return (value, name, head) => {
6258
+ if (value === void 0) return;
6259
+ validator(value, name, head);
6260
+ };
6261
+ };
6262
+ var nullable = (validator) => {
6263
+ return (value, name, head) => {
6264
+ if (value === null) return;
6265
+ validator(value, name, head);
6266
+ };
6267
+ };
6268
+ var oneOf = (values) => {
6269
+ return (value, name) => {
6270
+ if (values.includes(value)) return;
6271
+ fail(
6272
+ name,
6273
+ `expected one of ${values.map((x) => formatLiteral(x)).join(", ")}`
6274
+ );
6275
+ };
6276
+ };
6277
+ var arrayOf = (validator) => {
6278
+ return (value, name, head) => {
6279
+ if (!isArray(value)) fail(name, "expected array");
6280
+ const items = value;
6281
+ for (let i = 0; i < items.length; ++i) {
6282
+ validator(items[i], `${name}[${i}]`, head);
6283
+ }
6284
+ };
6285
+ };
6286
+ var shape = (schema) => {
6287
+ return (value, name, head) => {
6288
+ if (!isObject(value)) fail(name, "expected object");
6289
+ const record = value;
6290
+ for (const key in schema) {
6291
+ const validator = schema[key];
6292
+ validator(record[key], `${name}.${key}`, head);
6293
+ }
6294
+ };
6295
+ };
6296
+ var refOf = (validator) => {
6297
+ return (value, name, head) => {
6298
+ if (!isRef(value)) fail(name, "expected ref");
6299
+ const refValue = value;
6300
+ validator(refValue(), `${name}.value`, head);
6301
+ };
6302
+ };
6303
+ var pval = {
6304
+ isString: isString2,
6305
+ isNumber,
6306
+ isBoolean,
6307
+ isClass,
6308
+ optional,
6309
+ nullable,
6310
+ oneOf,
6311
+ arrayOf,
6312
+ shape,
6313
+ refOf
6314
+ };
6315
+
6105
6316
  // src/composition/ContextRegistry.ts
6106
6317
  var ContextRegistry = class {
6107
6318
  constructor() {