regor 1.4.5 → 1.4.7

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.
@@ -49,6 +49,7 @@ var Regor = (() => {
49
49
  onUnmounted: () => onUnmounted,
50
50
  pause: () => pause,
51
51
  persist: () => persist,
52
+ pval: () => pval,
52
53
  raw: () => raw,
53
54
  ref: () => ref,
54
55
  removeNode: () => removeNode,
@@ -205,7 +206,180 @@ var Regor = (() => {
205
206
  context.unmounted?.();
206
207
  };
207
208
 
209
+ // src/log/warnings.ts
210
+ var warnings = {
211
+ [8 /* ModelRequiresRef */]: (el) => `Model binding requires a ref at ${el.outerHTML}`,
212
+ [7 /* ModelNotSupportOnElement */]: (el) => `Model binding is not supported on ${el.tagName} element at ${el.outerHTML}`,
213
+ [0 /* MissingBindingExpression */]: (name, el) => `${name} binding expression is missing at ${el.outerHTML}`,
214
+ [1 /* InvalidForExpression */]: (name, forPath, el) => `invalid ${name} expression: ${forPath} at ${el.outerHTML}`,
215
+ [2 /* BindingRequiresObjectExpressions */]: (name, el) => `${name} requires object expression at ${el.outerHTML}`,
216
+ [3 /* KeyIsEmpty */]: (name, el) => `${name} binder: key is empty on ${el.outerHTML}.`,
217
+ [4 /* PropertyAssignmentFailed */]: (key, tag, value, e) => ({
218
+ msg: `Failed setting prop "${key}" on <${tag.toLowerCase()}>: value ${value} is invalid.`,
219
+ args: [e]
220
+ }),
221
+ [5 /* MissingEventType */]: (name, el) => `${name} binding missing event type at ${el.outerHTML}`,
222
+ [6 /* ErrorLog */]: (msg, e) => ({ msg, args: [e] })
223
+ };
224
+ var warning = (type, ...args) => {
225
+ const msg = warnings[type];
226
+ const item = isFunction(msg) ? msg.call(warnings, ...args) : msg;
227
+ const handler = warningHandler.warning;
228
+ if (!handler) return;
229
+ if (isString(item)) handler(item);
230
+ else handler(item, ...item.args);
231
+ };
232
+ var warningHandler = {
233
+ warning: console.warn
234
+ };
235
+
236
+ // src/reactivity/refSymbols.ts
237
+ var refSymbol = Symbol("ref");
238
+ var srefSymbol = Symbol("sref");
239
+ var rawSymbol = Symbol("raw");
240
+
241
+ // src/reactivity/isRef.ts
242
+ var isRef = (value) => {
243
+ return value != null && value[srefSymbol] === 1;
244
+ };
245
+
246
+ // src/app/propValidators.ts
247
+ var PropValidationError = class extends Error {
248
+ propPath;
249
+ detail;
250
+ constructor(propPath, detail) {
251
+ super(detail);
252
+ this.name = "PropValidationError";
253
+ this.propPath = propPath;
254
+ this.detail = detail;
255
+ }
256
+ };
257
+ var fail = (name, message) => {
258
+ throw new PropValidationError(name, `${message}.`);
259
+ };
260
+ var describeValue = (value) => {
261
+ if (value === null) return "null";
262
+ if (value === void 0) return "undefined";
263
+ if (typeof value === "string") return "string";
264
+ if (typeof value === "number") return "number";
265
+ if (typeof value === "boolean") return "boolean";
266
+ if (typeof value === "bigint") return "bigint";
267
+ if (typeof value === "symbol") return "symbol";
268
+ if (typeof value === "function") return "function";
269
+ if (isArray(value)) return "array";
270
+ if (value instanceof Date) return "Date";
271
+ if (value instanceof RegExp) return "RegExp";
272
+ if (value instanceof Map) return "Map";
273
+ if (value instanceof Set) return "Set";
274
+ const ctorName = value?.constructor?.name;
275
+ return ctorName && ctorName !== "Object" ? ctorName : "object";
276
+ };
277
+ var formatLiteral = (value) => {
278
+ if (typeof value === "string") return `"${value}"`;
279
+ if (typeof value === "number" || typeof value === "boolean") {
280
+ return String(value);
281
+ }
282
+ if (value === null) return "null";
283
+ if (value === void 0) return "undefined";
284
+ return describeValue(value);
285
+ };
286
+ var isString2 = (value, name) => {
287
+ if (typeof value !== "string") fail(name, "expected string");
288
+ };
289
+ var isNumber = (value, name) => {
290
+ if (typeof value !== "number") fail(name, "expected number");
291
+ };
292
+ var isBoolean = (value, name) => {
293
+ if (typeof value !== "boolean") fail(name, "expected boolean");
294
+ };
295
+ var isClass = (ctor) => {
296
+ return (value, name) => {
297
+ if (!(value instanceof ctor)) {
298
+ fail(name, `expected instance of ${ctor.name || "provided class"}`);
299
+ }
300
+ };
301
+ };
302
+ var optional = (validator) => {
303
+ return (value, name, head) => {
304
+ if (value === void 0) return;
305
+ validator(value, name, head);
306
+ };
307
+ };
308
+ var nullable = (validator) => {
309
+ return (value, name, head) => {
310
+ if (value === null) return;
311
+ validator(value, name, head);
312
+ };
313
+ };
314
+ var oneOf = (values) => {
315
+ return (value, name) => {
316
+ if (values.includes(value)) return;
317
+ fail(
318
+ name,
319
+ `expected one of ${values.map((x) => formatLiteral(x)).join(", ")}`
320
+ );
321
+ };
322
+ };
323
+ var arrayOf = (validator) => {
324
+ return (value, name, head) => {
325
+ if (!isArray(value)) fail(name, "expected array");
326
+ const items = value;
327
+ for (let i = 0; i < items.length; ++i) {
328
+ validator(items[i], `${name}[${i}]`, head);
329
+ }
330
+ };
331
+ };
332
+ var shape = (schema) => {
333
+ return (value, name, head) => {
334
+ if (!isObject(value)) fail(name, "expected object");
335
+ const record = value;
336
+ for (const key in schema) {
337
+ const validator = schema[key];
338
+ validator(record[key], `${name}.${key}`, head);
339
+ }
340
+ };
341
+ };
342
+ var refOf = (validator) => {
343
+ return (value, name, head) => {
344
+ if (!isRef(value)) fail(name, "expected ref");
345
+ const refValue = value;
346
+ validator(refValue(), `${name}.value`, head);
347
+ };
348
+ };
349
+ var pval = {
350
+ fail,
351
+ isString: isString2,
352
+ isNumber,
353
+ isBoolean,
354
+ isClass,
355
+ optional,
356
+ nullable,
357
+ oneOf,
358
+ arrayOf,
359
+ shape,
360
+ refOf
361
+ };
362
+
208
363
  // src/app/ComponentHead.ts
364
+ var formatComponentValidationError = (element, propName, error) => {
365
+ const tagName = element.tagName?.toLowerCase?.() || "unknown";
366
+ const finalPropName = error instanceof PropValidationError ? error.propPath : propName;
367
+ const detail = error instanceof PropValidationError ? error.detail : error instanceof Error ? error.message : String(error);
368
+ if (error instanceof Error) {
369
+ return new Error(
370
+ `Invalid prop "${finalPropName}" on <${tagName}>: ${detail}`,
371
+ {
372
+ cause: error
373
+ }
374
+ );
375
+ }
376
+ return new Error(
377
+ `Invalid prop "${finalPropName}" on <${tagName}>: ${detail}`,
378
+ {
379
+ cause: error
380
+ }
381
+ );
382
+ };
209
383
  var ComponentHead = class {
210
384
  /**
211
385
  * Values provided by parent for this component instance.
@@ -293,12 +467,18 @@ var Regor = (() => {
293
467
  * @internal
294
468
  */
295
469
  __element;
296
- constructor(props, element, ctx, start, end) {
470
+ /**
471
+ * Runtime behavior used when `validateProps(...)` encounters invalid input.
472
+ * Defaults to `'throw'`.
473
+ */
474
+ __propValidationMode;
475
+ constructor(props, element, ctx, start, end, propValidationMode) {
297
476
  this.props = props;
298
477
  this.__element = element;
299
478
  this.ctx = ctx;
300
479
  this.start = start;
301
480
  this.end = end;
481
+ this.__propValidationMode = propValidationMode;
302
482
  }
303
483
  /**
304
484
  * Emits a custom DOM event from the component host element.
@@ -377,6 +557,61 @@ var Regor = (() => {
377
557
  `${constructor} was not found in the context stack at occurrence ${occurrence}.`
378
558
  );
379
559
  }
560
+ /**
561
+ * Validates selected incoming props using assertion-style validators.
562
+ *
563
+ * Only keys listed in `schema` are checked. Validation throws immediately
564
+ * on the first invalid prop and does not mutate `head.props`.
565
+ *
566
+ * The schema is keyed from `head.props`, so editor completion can suggest
567
+ * known prop names while still allowing you to validate only a subset.
568
+ *
569
+ * Validators typically come from `pval`, but custom user validators are also
570
+ * supported. Custom validators may throw their own `Error`, though `pval.fail(...)`
571
+ * is recommended so nested validators can preserve the exact failing prop path.
572
+ *
573
+ * Example:
574
+ * ```ts
575
+ * head.validateProps({
576
+ * title: pval.isString,
577
+ * count: pval.optional(pval.isNumber),
578
+ * })
579
+ * ```
580
+ *
581
+ * Example with a custom validator:
582
+ * ```ts
583
+ * const isNonEmptyString: PropValidator<string> = (value, name) => {
584
+ * if (typeof value !== 'string' || value.trim() === '') {
585
+ * pval.fail(name, 'expected non-empty string')
586
+ * }
587
+ * }
588
+ * ```
589
+ *
590
+ * @param schema - Validators to apply to selected incoming props.
591
+ */
592
+ validateProps(schema) {
593
+ if (this.__propValidationMode === "off") return;
594
+ const props = this.props;
595
+ for (const name in schema) {
596
+ const validator = schema[name];
597
+ if (!validator) continue;
598
+ const validateProp = validator;
599
+ try {
600
+ validateProp(props[name], name, this);
601
+ } catch (error) {
602
+ const enrichedError = formatComponentValidationError(
603
+ this.__element,
604
+ name,
605
+ error
606
+ );
607
+ if (this.__propValidationMode === "warn") {
608
+ warningHandler.warning(enrichedError.message, enrichedError);
609
+ continue;
610
+ }
611
+ throw enrichedError;
612
+ }
613
+ }
614
+ }
380
615
  /**
381
616
  * Unmounts this component instance by removing nodes between `start` and `end`
382
617
  * and calling unmount lifecycle handlers for captured contexts.
@@ -410,33 +645,6 @@ var Regor = (() => {
410
645
  getBindData(node).unbinders.push(unbinder);
411
646
  };
412
647
 
413
- // src/log/warnings.ts
414
- var warnings = {
415
- [8 /* ModelRequiresRef */]: (el) => `Model binding requires a ref at ${el.outerHTML}`,
416
- [7 /* ModelNotSupportOnElement */]: (el) => `Model binding is not supported on ${el.tagName} element at ${el.outerHTML}`,
417
- [0 /* MissingBindingExpression */]: (name, el) => `${name} binding expression is missing at ${el.outerHTML}`,
418
- [1 /* InvalidForExpression */]: (name, forPath, el) => `invalid ${name} expression: ${forPath} at ${el.outerHTML}`,
419
- [2 /* BindingRequiresObjectExpressions */]: (name, el) => `${name} requires object expression at ${el.outerHTML}`,
420
- [3 /* KeyIsEmpty */]: (name, el) => `${name} binder: key is empty on ${el.outerHTML}.`,
421
- [4 /* PropertyAssignmentFailed */]: (key, tag, value, e) => ({
422
- msg: `Failed setting prop "${key}" on <${tag.toLowerCase()}>: value ${value} is invalid.`,
423
- args: [e]
424
- }),
425
- [5 /* MissingEventType */]: (name, el) => `${name} binding missing event type at ${el.outerHTML}`,
426
- [6 /* ErrorLog */]: (msg, e) => ({ msg, args: [e] })
427
- };
428
- var warning = (type, ...args) => {
429
- const msg = warnings[type];
430
- const item = isFunction(msg) ? msg.call(warnings, ...args) : msg;
431
- const handler = warningHandler.warning;
432
- if (!handler) return;
433
- if (isString(item)) handler(item);
434
- else handler(item, ...item.args);
435
- };
436
- var warningHandler = {
437
- warning: console.warn
438
- };
439
-
440
648
  // src/bind/switch.ts
441
649
  var switches = {};
442
650
  var switchCounter = {};
@@ -840,16 +1048,6 @@ var Regor = (() => {
840
1048
  return scopeSymbol2 in value;
841
1049
  };
842
1050
 
843
- // src/reactivity/refSymbols.ts
844
- var refSymbol = Symbol("ref");
845
- var srefSymbol = Symbol("sref");
846
- var rawSymbol = Symbol("raw");
847
-
848
- // src/reactivity/isRef.ts
849
- var isRef = (value) => {
850
- return value != null && value[srefSymbol] === 1;
851
- };
852
-
853
1051
  // src/directives/context.ts
854
1052
  var contextDirective = {
855
1053
  collectRefObj: true,
@@ -1417,7 +1615,8 @@ var Regor = (() => {
1417
1615
  component,
1418
1616
  capturedContext,
1419
1617
  startOfComponent,
1420
- endOfComponent
1618
+ endOfComponent,
1619
+ binder.__config.propValidationMode
1421
1620
  );
1422
1621
  const componentCtx2 = useScope(() => {
1423
1622
  return registeredComponent.context(head2) ?? {};
@@ -2933,11 +3132,11 @@ var Regor = (() => {
2933
3132
  }
2934
3133
  return;
2935
3134
  }
2936
- const isBoolean = key in booleanAttributes;
2937
- if (isNullOrUndefined(value) || isBoolean && !includeBooleanAttr(value)) {
3135
+ const isBoolean2 = key in booleanAttributes;
3136
+ if (isNullOrUndefined(value) || isBoolean2 && !includeBooleanAttr(value)) {
2938
3137
  el.removeAttribute(key);
2939
3138
  } else {
2940
- el.setAttribute(key, isBoolean ? "" : value);
3139
+ el.setAttribute(key, isBoolean2 ? "" : value);
2941
3140
  }
2942
3141
  };
2943
3142
 
@@ -3187,7 +3386,7 @@ var Regor = (() => {
3187
3386
  var handleInputAndTextArea = (el, flags, getModelRef, parsedValue) => {
3188
3387
  const isLazy = flags.lazy;
3189
3388
  const eventType = isLazy ? "change" : "input";
3190
- const isNumber = isNumberInput(el);
3389
+ const isNumber2 = isNumberInput(el);
3191
3390
  const trimmer = () => {
3192
3391
  if (!flags.trim && !getFlags(parsedValue()[1]).trim) return;
3193
3392
  el.value = el.value.trim();
@@ -3217,7 +3416,7 @@ var Regor = (() => {
3217
3416
  if (!target || target.composing) return;
3218
3417
  let value = target.value;
3219
3418
  const flags2 = getFlags(parsedValue()[1]);
3220
- if (isNumber || flags2.number || flags2.int) {
3419
+ if (isNumber2 || flags2.number || flags2.int) {
3221
3420
  if (flags2.int) {
3222
3421
  value = parseInt(value);
3223
3422
  } else {
@@ -3831,6 +4030,15 @@ var Regor = (() => {
3831
4030
  forGrowThreshold = 10;
3832
4031
  globalContext;
3833
4032
  useInterpolation = true;
4033
+ /**
4034
+ * Controls how `head.validateProps(...)` behaves when a validator fails.
4035
+ *
4036
+ * - `'throw'` (default): rethrows the validation error immediately.
4037
+ * - `'warn'`: forwards the validation error to `warningHandler.warning(...)`
4038
+ * and continues.
4039
+ * - `'off'`: skips runtime prop validation entirely.
4040
+ */
4041
+ propValidationMode = "throw";
3834
4042
  constructor(globalContext) {
3835
4043
  this.setDirectives("r-");
3836
4044
  if (globalContext) {
@@ -4522,12 +4730,12 @@ var Regor = (() => {
4522
4730
  this.__gobbleSpaces();
4523
4731
  let ch = this.__code;
4524
4732
  while (ch === PERIOD_CODE || ch === OBRACK_CODE || ch === OPAREN_CODE || ch === QUMARK_CODE) {
4525
- let optional;
4733
+ let optional2;
4526
4734
  if (ch === QUMARK_CODE) {
4527
4735
  if (this.__expr.charCodeAt(this.__index + 1) !== PERIOD_CODE) {
4528
4736
  break;
4529
4737
  }
4530
- optional = true;
4738
+ optional2 = true;
4531
4739
  this.__index += 2;
4532
4740
  this.__gobbleSpaces();
4533
4741
  ch = this.__code;
@@ -4553,7 +4761,7 @@ var Regor = (() => {
4553
4761
  callee: node
4554
4762
  };
4555
4763
  } else {
4556
- if (optional) {
4764
+ if (optional2) {
4557
4765
  this.__index--;
4558
4766
  }
4559
4767
  this.__gobbleSpaces();
@@ -4564,7 +4772,7 @@ var Regor = (() => {
4564
4772
  property: this.__gobbleIdentifier()
4565
4773
  };
4566
4774
  }
4567
- if (optional) {
4775
+ if (optional2) {
4568
4776
  node.optional = true;
4569
4777
  }
4570
4778
  this.__gobbleSpaces();