kopular 0.12.0 → 0.13.0

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/LLM.md CHANGED
@@ -365,9 +365,20 @@ email.Valid(); // bool — Error.Value == null
365
365
  **No array-of-validators parameter** — KopScript has no array-of-function-values type
366
366
  (`((T) => string?)[]` doesn't parse: the parser reads a second `(...) => ...` immediately
367
367
  after the first as a nested function type, not an array element type, and errors expecting
368
- `=>`). Combine checks as an if-chain in one lambda instead (see the `email` example above)
369
- — this is the same reason `FormField<T>`'s constructor takes exactly one validator
370
- function, not a list.
368
+ `=>`). Combine checks as an if-chain in one lambda (see the `email` example above), or via
369
+ the fixed-arity `CombineValidators2<T>`/`CombineValidators3<T>` free functions (generic,
370
+ inference-only — see `Kop`'s own "Generics" docs for why they're *free* functions, not
371
+ `Validators` static methods):
372
+ ```ks
373
+ CombineValidators2((string v) => Validators.Required(v), (string v) => Validators.Email(v))
374
+ // -> a single (string) => string? — Required first, Email only if that passes
375
+ ```
376
+ Fixed-arity (2, 3 — add more the same way if a form ever needs to chain further), not a
377
+ general `Validators.All(...)`, for the same array-of-function-values reason above.
378
+
379
+ **No DOM binding** — wiring `.Value` to a real `<input>` is a plain `addEventListener`
380
+ call in your own `Render()`, the same as any other event handler; there is no
381
+ `[(ngModel)]`-equivalent.
371
382
 
372
383
  **No DOM binding** — wiring `.Value` to a real `<input>` is a plain `addEventListener`
373
384
  call in your own `Render()`, the same as any other event handler; there is no
package/README.md CHANGED
@@ -447,10 +447,21 @@ constructor exactly like `Counter`'s own `state<number>`, to re-render when they
447
447
  A validator is a plain `(T) => string?` — `null` means valid, the same convention
448
448
  KopScript's own nullable types use elsewhere. **There's no array-of-validators
449
449
  constructor parameter** — KopScript has no syntax for an array of function values — so
450
- combining more than one check (as `email` does above) is just an `if`-chain in one
451
- lambda, not a combinator API. `Validators` ships the handful of checks almost every form
452
- needs (`Required`, `MinLength`, `MaxLength`, `Email`, `Min`, `Max`), each returning its
453
- own message; write your own validator function for anything more specific.
450
+ combining more than one check (as `email` does above) is an `if`-chain in one lambda, or,
451
+ for the common case of just chaining a couple of already-built validators with no custom
452
+ logic of their own, the `CombineValidators2`/`CombineValidators3` free functions:
453
+
454
+ ```ks
455
+ FormField<string> email = new FormField<string>("",
456
+ CombineValidators2((string v) => Validators.Required(v), (string v) => Validators.Email(v)));
457
+ ```
458
+
459
+ Free functions, not `Validators` methods, and fixed-arity (2 and 3) rather than a general
460
+ `Validators.All(...)` — a class's own static method can't introduce a new type parameter
461
+ beyond the class's own (see "Generics" in `Kop`'s own docs), so a *generic* combinator has
462
+ to live as a free function instead. `Validators` ships the handful of checks almost every
463
+ form needs (`Required`, `MinLength`, `MaxLength`, `Email`, `Min`, `Max`), each returning
464
+ its own message; write your own validator function for anything more specific.
454
465
 
455
466
  **No two-way data binding** — wiring `Value` to a real `<input>` is the
456
467
  `addEventListener` call shown above, the same manual pattern `Counter` already uses for
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kopular",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Kopular: a small component framework for KopScript — components, reactive state, constructor-injected services, routing, real compiled templates, and HTTP, with no DI container",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/forms.js CHANGED
@@ -13,6 +13,28 @@ class __KopState {
13
13
  }
14
14
  }
15
15
 
16
+ export function CombineValidators2(v1, v2) {
17
+ return (value) => {
18
+ let r1 = v1(value);
19
+ if ((r1 !== null)) {
20
+ return r1;
21
+ }
22
+ return v2(value);
23
+ };
24
+ }
25
+ export function CombineValidators3(v1, v2, v3) {
26
+ return (value) => {
27
+ let r1 = v1(value);
28
+ if ((r1 !== null)) {
29
+ return r1;
30
+ }
31
+ let r2 = v2(value);
32
+ if ((r2 !== null)) {
33
+ return r2;
34
+ }
35
+ return v3(value);
36
+ };
37
+ }
16
38
  export class FormField {
17
39
  constructor(initial, validate) {
18
40
  this.Value = new __KopState(initial);
package/src/forms.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"forms.js","sources":["forms.ks"],"sourcesContent":["// FormField<T>: a single form input's value, validation state, and touched\n// flag, built on the same state<T> reactivity Component already uses — no\n// new reactive primitive, and deliberately no DOM binding of its own\n// (wiring Value to a real <input> is one addEventListener call in your own\n// Render(), the same as Counter's own click handler — see Kopular's README).\n//\n// A validator is a plain (T) => string? function: null means valid, the\n// same \"null means nothing to report\" convention KopScript's nullable\n// types already use elsewhere. There's no array of validators — KopScript\n// has no syntax for an array of function values — so combining more than\n// one check is just an if-chain in one lambda:\n//\n// FormField<string> name = new FormField<string>(\"\", (string v) => {\n// string? required = Validators.Required(v);\n// if (required != null) { return required; }\n// return Validators.MaxLength(v, 40);\n// });\nclass FormField<T> {\n public state<T> Value;\n public state<string?> Error;\n public state<bool> Touched;\n\n constructor(T initial, (T) => string? validate) {\n this.Value = state(initial);\n this.Error = state(validate(initial));\n this.Touched = state(false);\n this.Value.Subscribe((T v) => { this.Error.Value = validate(v); });\n }\n\n // Call on blur — separate from Error so a fresh, untouched field with an\n // invalid initial value (e.g. Required on an empty string) doesn't show\n // an error message before the user has had a chance to type anything.\n public void Touch() {\n this.Touched.Value = true;\n }\n\n public bool Valid() {\n return this.Error.Value == null;\n }\n}\n\n// A small set of common checks, each returning an error message or null —\n// not a validation framework, just the handful of checks almost every form\n// needs, so most fields don't have to hand-write string-length arithmetic.\nclass Validators {\n public static string? Required(string value) {\n if (value.Trim().Length == 0) { return \"Required\"; }\n return null;\n }\n\n public static string? MinLength(string value, number min) {\n if (value.Length < min) { return $\"Must be at least {min} characters\"; }\n return null;\n }\n\n public static string? MaxLength(string value, number max) {\n if (value.Length > max) { return $\"Must be at most {max} characters\"; }\n return null;\n }\n\n public static string? Email(string value) {\n return match value {\n r\"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$\" => null,\n _ => \"Must be a valid email\"\n };\n }\n\n public static string? Min(number value, number min) {\n if (value < min) { return $\"Must be at least {min}\"; }\n return null;\n }\n\n public static string? Max(number value, number max) {\n if (value > max) { return $\"Must be at most {max}\"; }\n return null;\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAiBA;EAKE;IACa;IACA;IACE;IACO;EAA6B;;;;EAM5C;IACc;;;EAGd;IACL;;;AAOJ;EACgB;IACZ;MAAgC;;IAChC;;;EAGY;IACZ;MAA0B;;IAC1B;;;EAGY;IACZ;MAA0B;;IAC1B;;;EAGY;IACZ;;EACE;;;EACA;;;;;;EAIU;IACZ;MAAmB;;IACnB;;;EAGY;IACZ;MAAmB;;IACnB"}
1
+ {"version":3,"file":"forms.js","sources":["forms.ks"],"sourcesContent":["// FormField<T>: a single form input's value, validation state, and touched\n// flag, built on the same state<T> reactivity Component already uses — no\n// new reactive primitive, and deliberately no DOM binding of its own\n// (wiring Value to a real <input> is one addEventListener call in your own\n// Render(), the same as Counter's own click handler — see Kopular's README).\n//\n// A validator is a plain (T) => string? function: null means valid, the\n// same \"null means nothing to report\" convention KopScript's nullable\n// types already use elsewhere. There's no array of validators — KopScript\n// has no syntax for an array of function values — so combining more than\n// one check is either an if-chain in one lambda, hand-written:\n//\n// FormField<string> name = new FormField<string>(\"\", (string v) => {\n// string? required = Validators.Required(v);\n// if (required != null) { return required; }\n// return Validators.MaxLength(v, 40);\n// });\n//\n// or the CombineValidators2/CombineValidators3 free functions below, for\n// the common case of just chaining a couple of already-built validators\n// with no custom logic of their own:\n//\n// FormField<string> name = new FormField<string>(\"\",\n// CombineValidators2((string v) => Validators.Required(v), (string v) => Validators.MaxLength(v, 40)));\n//\n// Free functions, not static Validators methods, and fixed-arity (2 and 3),\n// not a general array-taking Validators.All(...): KopScript has no\n// array-of-function-values type to accept a variable-length list with, and\n// a generic function can only be a *free* function in v1 — a class's own\n// static method can't introduce a new type parameter of its own beyond the\n// class's (Validators itself isn't generic) — see Kop's README/LLM.md\n// \"Generics\" section for exactly that cut. Add CombineValidators4 etc. the\n// same way if a real form ever needs to chain more than three.\n(T) => string? CombineValidators2<T>((T) => string? v1, (T) => string? v2) {\n return (T value) => {\n string? r1 = v1(value);\n if (r1 != null) { return r1; }\n return v2(value);\n };\n}\n\n(T) => string? CombineValidators3<T>((T) => string? v1, (T) => string? v2, (T) => string? v3) {\n return (T value) => {\n string? r1 = v1(value);\n if (r1 != null) { return r1; }\n string? r2 = v2(value);\n if (r2 != null) { return r2; }\n return v3(value);\n };\n}\nclass FormField<T> {\n public state<T> Value;\n public state<string?> Error;\n public state<bool> Touched;\n\n constructor(T initial, (T) => string? validate) {\n this.Value = state(initial);\n this.Error = state(validate(initial));\n this.Touched = state(false);\n this.Value.Subscribe((T v) => { this.Error.Value = validate(v); });\n }\n\n // Call on blur — separate from Error so a fresh, untouched field with an\n // invalid initial value (e.g. Required on an empty string) doesn't show\n // an error message before the user has had a chance to type anything.\n public void Touch() {\n this.Touched.Value = true;\n }\n\n public bool Valid() {\n return this.Error.Value == null;\n }\n}\n\n// A small set of common checks, each returning an error message or null —\n// not a validation framework, just the handful of checks almost every form\n// needs, so most fields don't have to hand-write string-length arithmetic.\nclass Validators {\n public static string? Required(string value) {\n if (value.Trim().Length == 0) { return \"Required\"; }\n return null;\n }\n\n public static string? MinLength(string value, number min) {\n if (value.Length < min) { return $\"Must be at least {min} characters\"; }\n return null;\n }\n\n public static string? MaxLength(string value, number max) {\n if (value.Length > max) { return $\"Must be at most {max} characters\"; }\n return null;\n }\n\n public static string? Email(string value) {\n return match value {\n r\"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$\" => null,\n _ => \"Must be a valid email\"\n };\n }\n\n public static string? Min(number value, number min) {\n if (value < min) { return $\"Must be at least {min}\"; }\n return null;\n }\n\n public static string? Max(number value, number max) {\n if (value > max) { return $\"Must be at most {max}\"; }\n return null;\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAiCA;EACE;EACE;EACA;IAAkB;;EAClB;;;AAIJ;EACE;EACE;EACA;IAAkB;;EAClB;EACA;IAAkB;;EAClB;;;AAGJ;EAKE;IACa;IACA;IACE;IACO;EAA6B;;;;EAM5C;IACc;;;EAGd;IACL;;;AAOJ;EACgB;IACZ;MAAgC;;IAChC;;;EAGY;IACZ;MAA0B;;IAC1B;;;EAGY;IACZ;MAA0B;;IAC1B;;;EAGY;IACZ;;EACE;;;EACA;;;;;;EAIU;IACZ;MAAmB;;IACnB;;;EAGY;IACZ;MAAmB;;IACnB"}
package/src/forms.ks CHANGED
@@ -8,13 +8,46 @@
8
8
  // same "null means nothing to report" convention KopScript's nullable
9
9
  // types already use elsewhere. There's no array of validators — KopScript
10
10
  // has no syntax for an array of function values — so combining more than
11
- // one check is just an if-chain in one lambda:
11
+ // one check is either an if-chain in one lambda, hand-written:
12
12
  //
13
13
  // FormField<string> name = new FormField<string>("", (string v) => {
14
14
  // string? required = Validators.Required(v);
15
15
  // if (required != null) { return required; }
16
16
  // return Validators.MaxLength(v, 40);
17
17
  // });
18
+ //
19
+ // or the CombineValidators2/CombineValidators3 free functions below, for
20
+ // the common case of just chaining a couple of already-built validators
21
+ // with no custom logic of their own:
22
+ //
23
+ // FormField<string> name = new FormField<string>("",
24
+ // CombineValidators2((string v) => Validators.Required(v), (string v) => Validators.MaxLength(v, 40)));
25
+ //
26
+ // Free functions, not static Validators methods, and fixed-arity (2 and 3),
27
+ // not a general array-taking Validators.All(...): KopScript has no
28
+ // array-of-function-values type to accept a variable-length list with, and
29
+ // a generic function can only be a *free* function in v1 — a class's own
30
+ // static method can't introduce a new type parameter of its own beyond the
31
+ // class's (Validators itself isn't generic) — see Kop's README/LLM.md
32
+ // "Generics" section for exactly that cut. Add CombineValidators4 etc. the
33
+ // same way if a real form ever needs to chain more than three.
34
+ (T) => string? CombineValidators2<T>((T) => string? v1, (T) => string? v2) {
35
+ return (T value) => {
36
+ string? r1 = v1(value);
37
+ if (r1 != null) { return r1; }
38
+ return v2(value);
39
+ };
40
+ }
41
+
42
+ (T) => string? CombineValidators3<T>((T) => string? v1, (T) => string? v2, (T) => string? v3) {
43
+ return (T value) => {
44
+ string? r1 = v1(value);
45
+ if (r1 != null) { return r1; }
46
+ string? r2 = v2(value);
47
+ if (r2 != null) { return r2; }
48
+ return v3(value);
49
+ };
50
+ }
18
51
  class FormField<T> {
19
52
  public state<T> Value;
20
53
  public state<string?> Error;