@stacksjs/defaults 0.75.445 → 0.75.447

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.
@@ -269,9 +269,11 @@ These standalone functions are also available through the root `arr` namespace:
269
269
  - `groupBy`, `keyBy`, `countBy` use selector functions and return typed records.
270
270
  Repeated keyBy keys overwrite earlier entries; groupBy accumulates them.
271
271
  - `sortBy(array, ...selectors)` copies before sorting, supports multiple keys,
272
- and accepts `{ by, order: 'desc' }` selectors.
272
+ and accepts `{ by, order: 'desc' }` selectors. Each selector runs once per element;
273
+ cached keys preserve stable ties.
273
274
  - `sumBy`, `meanBy`, `minBy`, `maxBy` aggregate selected values. `meanBy` returns
274
275
  undefined on an empty input; extrema return the selected element, not its value.
276
+ Extrema retain infinite endpoints and ignore NaN candidates; ties keep the first.
275
277
  - `zip`, `zipWith` stop at the shorter input; `unzip` splits pairs.
276
278
  - `difference` keeps first-input duplicates that are not excluded;
277
279
  `intersection` and `union` deduplicate by Set identity.
@@ -280,6 +282,12 @@ These standalone functions are also available through the root `arr` namespace:
280
282
  - `splitAt` supports negative indexes; `splitWhen` starts the second half at
281
283
  the first match; `times` requires a non-negative integer count.
282
284
 
285
+ `take`, `takeLast`, `drop` and `dropLast` truncate fractional counts toward zero,
286
+ clamp negatives/NaN to zero and treat Infinity as the entire array. Population
287
+ variance, standard deviation, covariance and z-scores use scaled centered arithmetic
288
+ for finite inputs. Standard deviation stays finite when only its square is outside
289
+ the number range; unrepresentable final results keep IEEE number behavior.
290
+
283
291
  Read `core/arrays/src/transform.ts` and `tests/transform.test.ts` for precise
284
292
  empty-input and selector behavior. These pure helpers are preferable to a new
285
293
  utility dependency for common native transformations.
@@ -258,6 +258,11 @@ offset; the named IANA zone converts it into an instant. `isoInZone` retains
258
258
  that zone's offset in serialized output rather than merely displaying a date.
259
259
  `addCalendarMonths` clones and clamps the day; ordinary `DateTime.addMonths`
260
260
  preserves native Date overflow instead. Do not interchange those contracts.
261
+ Calendar-month clamping keeps literal early years in local and UTC mode and throws
262
+ RangeError for an out-of-range result. A zero-month shift preserves the exact instant.
263
+ Named-zone conversion rejects impossible calendar and clock fields before applying
264
+ DST disambiguation. Era-aware offsets support year zero and BCE instants; `isoInZone`
265
+ retains seconds for expanded ISO years.
261
266
 
262
267
  `zonedTimeToUtc` resolves DST edges the same way in every zone (RFC 5545 and
263
268
  Temporal's `'compatible'` rule):
@@ -309,6 +314,7 @@ minutesUntil('2026-10-25', '09:00', 'Europe/Berlin', new Date('2026-10-24T22:00:
309
314
  - `minutesUntil` keeps the seconds of `now` as a fraction; round where you
310
315
  display or compare. Negative once the moment has passed.
311
316
  - `addDays` refuses a result past 9999-12-31.
317
+ - `todayIn` returns canonical dates from 0000 through 9999 and rejects BCE or expanded-year dates.
312
318
 
313
319
  ### Reading `DATE` columns: `parseSqlDate` (`@stacksjs/database`)
314
320
 
@@ -363,6 +369,8 @@ clockMinutes, addClockMinutes and timeWindowsCover. Recurrence validation
363
369
  rejects invalid weekday entries; expansion refuses excessive ranges and
364
370
  midnight-crossing durations instead of silently truncating them. Adjacent
365
371
  windows can cover one continuous slot. ScheduleInputError carries status 422.
372
+ Every supplied availability window is validated before checking coverage, even
373
+ when an earlier valid window already covers the slot.
366
374
 
367
375
  localDateRange(from, to, ...zones) converts inclusive calendar days to an
368
376
  exclusive-end UTC instant range in a selected valid zone, including DST days.
@@ -16,7 +16,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
16
16
  | `@stacksjs/alias` | [`stacks-alias`](../stacks-alias/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/alias/src/index.ts) | 3 |
17
17
  | `@stacksjs/analytics` | [`stacks-analytics`](../stacks-analytics/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/analytics/src/index.ts) | 6 |
18
18
  | `@stacksjs/api` | [`stacks-api`](../stacks-api/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/api/src/index.ts) | 12 |
19
- | `@stacksjs/arrays` | [`stacks-arrays`](../stacks-arrays/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/arrays/src/index.ts) | 10 |
19
+ | `@stacksjs/arrays` | [`stacks-arrays`](../stacks-arrays/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/arrays/src/index.ts) | 12 |
20
20
  | `@stacksjs/audio` | [`stacks-audio`](../stacks-audio/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/audio/src/index.ts) | 3 |
21
21
  | `@stacksjs/auth` | [`stacks-auth`](../stacks-auth/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/auth/src/index.ts) | 126 |
22
22
  | `@stacksjs/browser` | [`stacks-browser`](../stacks-browser/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/browser/src/index.ts) | 8 |
@@ -36,7 +36,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
36
36
  | `@stacksjs/config` | [`stacks-config`](../stacks-config/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/config/src/index.ts) | 16 |
37
37
  | `@stacksjs/cron` | [`stacks-cron`](../stacks-cron/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cron/src/index.ts) | 6 |
38
38
  | `@stacksjs/database` | [`stacks-database`](../stacks-database/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/index.ts) | 138 |
39
- | `@stacksjs/datetime` | [`stacks-datetime`](../stacks-datetime/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/index.ts) | 21 |
39
+ | `@stacksjs/datetime` | [`stacks-datetime`](../stacks-datetime/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/index.ts) | 22 |
40
40
  | `@stacksjs/defaults` | [`stacks-build`](../stacks-build/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/defaults/package.json) | 2 |
41
41
  | `@stacksjs/deploy` | [`stacks-deploy`](../stacks-deploy/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/deploy/package.json) | 1 |
42
42
  | `@stacksjs/desktop-build` | [`stacks-desktop`](../stacks-desktop/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/desktop/src/index.ts) | 15 |
@@ -64,7 +64,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
64
64
  | `@stacksjs/model-meta` | [`stacks-models`](../stacks-models/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/model-meta/src/index.ts) | 1 |
65
65
  | `@stacksjs/newsletter` | [`stacks-newsletter`](../stacks-newsletter/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/newsletter/src/index.ts) | 7 |
66
66
  | `@stacksjs/notifications` | [`stacks-notifications`](../stacks-notifications/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/index.ts) | 18 |
67
- | `@stacksjs/objects` | [`stacks-objects`](../stacks-objects/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/src/index.ts) | 6 |
67
+ | `@stacksjs/objects` | [`stacks-objects`](../stacks-objects/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/src/index.ts) | 8 |
68
68
  | `@stacksjs/orm` | [`stacks-orm`](../stacks-orm/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/index.ts) | 76 |
69
69
  | `@stacksjs/pagination` | [`stacks-pagination`](../stacks-pagination/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/pagination/src/index.ts) | 0 |
70
70
  | `@stacksjs/path` | [`stacks-path`](../stacks-path/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/path/src/index.ts) | 13 |
@@ -94,7 +94,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
94
94
  | `@stacksjs/types` | [`stacks-types`](../stacks-types/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/types/src/index.ts) | 2 |
95
95
  | `@stacksjs/ui` | [`stacks-ui`](../stacks-ui/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/src/index.ts) | 6 |
96
96
  | `@stacksjs/utils` | [`stacks-utils`](../stacks-utils/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/utils/src/index.ts) | 15 |
97
- | `@stacksjs/validation` | [`stacks-validation`](../stacks-validation/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/index.ts) | 22 |
97
+ | `@stacksjs/validation` | [`stacks-validation`](../stacks-validation/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/index.ts) | 25 |
98
98
  | `@stacksjs/video` | [`stacks-video`](../stacks-video/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/video/src/index.ts) | 3 |
99
99
  | `@stacksjs/whois` | [`stacks-whois`](../stacks-whois/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/whois/src/index.ts) | 5 |
100
100
 
@@ -169,7 +169,7 @@ Declared runtime exports: `arr`.
169
169
 
170
170
  Local source modules: [src/arr.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/arrays/src/arr.ts), [src/macro.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/arrays/src/macro.ts).
171
171
 
172
- Evidence directory: [10 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/arrays/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
172
+ Evidence directory: [12 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/arrays/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
173
173
 
174
174
  ### @stacksjs/audio
175
175
 
@@ -367,7 +367,7 @@ Declared runtime exports: `DateTime`, `addCalendarMonths`, `addDays`, `dateForma
367
367
 
368
368
  Local source modules: [src/calendar.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/calendar.ts), [src/format.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/format.ts), [src/local.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/local.ts), [src/now.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/now.ts), [src/parse.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/parse.ts), [src/schedule.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/schedule.ts), [src/zone.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/zone.ts).
369
369
 
370
- Evidence directory: [21 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
370
+ Evidence directory: [22 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
371
371
 
372
372
  ### @stacksjs/defaults
373
373
 
@@ -631,7 +631,7 @@ Declared runtime exports: `clearUndefined`, `deepMerge`, `hasOwnProperty`, `isKe
631
631
 
632
632
  Local source modules: [src/transform.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/src/transform.ts).
633
633
 
634
- Evidence directory: [6 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
634
+ Evidence directory: [8 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
635
635
 
636
636
  ### @stacksjs/orm
637
637
 
@@ -919,7 +919,7 @@ Declared runtime exports: `applyConditionals`, `isArray`, `isBoolean`, `isFuncti
919
919
 
920
920
  Local source modules: [src/input.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/input.ts), [src/messages.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/messages.ts), [src/reporter.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/reporter.ts), [src/schema.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/schema.ts), [src/validator.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/validator.ts).
921
921
 
922
- Evidence directory: [22 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
922
+ Evidence directory: [25 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
923
923
 
924
924
  ### @stacksjs/video
925
925
 
@@ -81,16 +81,10 @@ const result = deepMerge(target, source)
81
81
  const merged = deepMerge(defaults, userConfig, overrides)
82
82
  ```
83
83
 
84
- The `DeepMerge<T, S>` type (from `@stacksjs/types`) provides full type inference:
85
- ```typescript
86
- type DeepMerge<F, S> = MergeInsertions<{
87
- [K in keyof F | keyof S]: K extends keyof S & keyof F
88
- ? DeepMerge<F[K], S[K]>
89
- : K extends keyof S ? S[K]
90
- : K extends keyof F ? F[K]
91
- : never
92
- }>
93
- ```
84
+ The `DeepMerge<T, S>` type from `@stacksjs/types` preserves primitive, array and
85
+ built-in instance values while merging record keys recursively. A replaced Date
86
+ target no longer exposes Date methods. The authoritative type is in
87
+ `core/types/src/utils.ts`; verify consumers against built declarations.
94
88
 
95
89
  ### objectPick -- Pick Properties by Key
96
90
  ```typescript
@@ -153,7 +147,7 @@ if (isKeyOf(config, key)) {
153
147
 
154
148
  ### objectKeys -- Strictly Typed Object.keys
155
149
  ```typescript
156
- function objectKeys<T extends object>(obj: T): Array<`${keyof T & (string | number | boolean | null | undefined)}`>
150
+ function objectKeys<T extends object>(obj: T): Array<ObjectStringKey<T>>
157
151
  ```
158
152
 
159
153
  Returns `Object.keys(obj)` with a stricter return type than the built-in `Object.keys()` (which returns `string[]`).
@@ -166,10 +160,13 @@ const keys = objectKeys(user)
166
160
 
167
161
  ### objectEntries -- Strictly Typed Object.entries
168
162
  ```typescript
169
- function objectEntries<T extends object>(obj: T): Array<[keyof T, T[keyof T]]>
163
+ function objectEntries<T extends object>(obj: T): Array<ObjectStringEntry<T>>
170
164
  ```
171
165
 
172
166
  Returns `Object.entries(obj)` with a stricter return type than the built-in `Object.entries()` (which returns `[string, any][]`).
167
+ `ObjectStringKey<T>` stringifies numeric keys and excludes symbols;
168
+ `ObjectStringEntry<T>` pairs those keys with the visited values. Both functions
169
+ declare their return types explicitly so built declarations preserve the arrays.
173
170
 
174
171
  ```typescript
175
172
  const user = { name: 'John', age: 30 }
@@ -197,9 +194,14 @@ The root also exports `omit`, `pickBy`, `omitBy`, `mapValues`, `mapKeys`,
197
194
  `invert`, `isEmptyObject`, `getPath` and `setPath` from `transform.ts`.
198
195
  Use a path array such as `['items', 0, 'name']`, not an assumed dot-string
199
196
  parser. `getPath(source, path, fallback?)` reads a nested value;
197
+ `getPath` returns `unknown` even with a fallback; narrow a present value before use.
198
+ `mapValues` preserves optional properties and omits symbol keys in its result type,
199
+ matching its enumerable string-key traversal. The pure `@stacksjs/objects/transform`
200
+ entry ships both JavaScript and declarations.
200
201
  `setPath(source, path, value)` performs a persistent update by copying containers
201
202
  along the path and sharing unaffected siblings. Treat paths as application-owned
202
- inputs; this helper is not a path authorization boundary. Choose `objectPick` for key-based selection and
203
+ inputs; this helper is not a path authorization boundary. Array `length` updates
204
+ use native length semantics on the copy and reject invalid lengths. Choose `objectPick` for key-based selection and
203
205
  `pickBy` for predicate selection. Read transform signatures for callback
204
206
  argument order; `objectMap`'s `(key, value)` is not a universal convention.
205
207
  Retained evidence: `core/objects/tests/transform.test.ts`.
@@ -212,6 +214,7 @@ Retained evidence: `core/objects/tests/transform.test.ts`.
212
214
  - `deepMerge` does NOT deeply merge arrays -- arrays from source replace arrays in target entirely.
213
215
  - `deepMerge` merges only plain objects key by key: a Date, Map, Set or class instance from a source is assigned as
214
216
  is, and a string, array or number in the target is replaced by a source's object.
217
+ A non-plain nested target also gives way to a plain source record.
215
218
  - `deepMerge` uses `isObject()` from `@stacksjs/validation` combined with `!Array.isArray()` to determine mergeability.
216
219
  - A `"__proto__"` key is data, never a prototype. `JSON.parse` and the router's request input keep one as an own key:
217
220
  `deepMerge` skips it and merges only into the target's own properties, and the transform helpers keep it as an own
@@ -274,14 +274,17 @@ const result = await validateField('User', { name: 'John', email: 'john@example.
274
274
  ```
275
275
 
276
276
  Behavior:
277
- 1. Finds the model file by name in user models or framework defaults
277
+ 1. Finds exact model filenames or qualified paths recursively, preferring app overrides
278
278
  2. Extracts validation rules from model attributes
279
- 3. Converts attribute names to snake_case for validation
280
- 4. Sets custom error messages via `MessageProvider`
281
- 5. Validates using `schema.object(ruleObject).validate(params)`
279
+ 3. Accepts attribute and snake-case column names, preferring the attribute spelling when both are supplied
280
+ 4. Applies custom messages to reported failures without executing predicates again or replacing the shared provider
281
+ 5. Validates through the context-aware object schema
282
282
  6. Throws `HttpError(422)` with error JSON on validation failure
283
283
  7. Throws `HttpError(404)` if model not found
284
- 8. Skips validation for attributes with default values unless `isRequired` is true
284
+ 8. An optional default excuses absence, never a supplied invalid value
285
+
286
+ Ambiguous bare model names require a qualified path. Declared fields and conditional
287
+ siblings use own properties; inherited values never count as submitted input.
285
288
 
286
289
  ### `customValidate(attributes, params)`
287
290
 
@@ -314,13 +317,17 @@ isObjectNotEmpty(undefined) // false
314
317
 
315
318
  ## Error Reporter (`reporter.ts`)
316
319
 
317
- Process-local latest-error reporter for validation:
320
+ Validation operations start an async-local latest-error scope before their first await.
321
+ Successful operations clear prior failures; `getErrors()` returns an independent snapshot.
322
+ Standalone adapters that report directly create a scope with `withValidationContext()`:
318
323
 
319
324
  ```typescript
320
- import { reportError, getErrors } from '@stacksjs/validation'
325
+ import { reportError, getErrors, withValidationContext } from '@stacksjs/validation'
321
326
 
322
- reportError([{ message: 'Invalid', value: '', field: 'email' }])
323
- const errors = getErrors() // returns the latest MessageObject[]; reportError replaces it
327
+ withValidationContext(() => {
328
+ reportError([{ message: 'Invalid', value: '', field: 'email' }])
329
+ const errors = getErrors() // this scope's latest MessageObject[]
330
+ })
324
331
  ```
325
332
 
326
333
  Interface: `{ message: string, value: string, field: string }`
@@ -388,7 +395,7 @@ type ValidationString = ValidationRule
388
395
  - Numeric classification functions (`isEvenOrOdd`, etc.) return string defaults for non-numbers rather than throwing
389
396
  - Schema validators map to database column types -- `schema.integer()` creates an integer column, `schema.string()` creates a varchar, etc.
390
397
  - `validateField` converts attribute names to snake_case using `snakeCase()` from `@stacksjs/strings`
391
- - `validateField` skips attributes with default values unless `isRequired` is explicitly set
398
+ - `validateField` validates supplied values even when their attributes declare defaults
392
399
  - Validation error messages are customizable per-rule in model definitions via the `message` object
393
400
  - The schema export is Stacks' proxy over the underlying validation instance, with typed object/array/enum inference, file validation and conditionals; it is not the same object as the upstream v export
394
401
  - Custom validators can be added with `schema.custom<T>(fn, message)` for types not covered by built-in validators
@@ -397,16 +404,23 @@ type ValidationString = ValidationRule
397
404
 
398
405
  ## Request, conditional and file validation
399
406
 
400
- The root validate(requestOrRecord, rules) is async, returns the validated payload
407
+ The root validate(requestOrRecord, rules, options?) is async, returns the validated payload
401
408
  and throws HttpError(422) with normalized field errors on failure. Rules are
402
- validators or { rule, message? }; enhanced request.all supplies native input,
403
- otherwise query/body/route data are merged by request-validator.ts. Do not
404
- substitute a boolean check or ignore the returned validated data at a boundary.
409
+ validators or { rule, message? }. Plain records are application data, including URL,
410
+ query and params fields. Native Request bodies and enhanced request.all() are read
411
+ directly; jsonBody/formBody adapters remain supported. URL/query/params-only adapters
412
+ select { source: 'request' }; { source: 'data' } forces data interpretation.
413
+ Returned values contain declared supplied fields, recursively through object shapes
414
+ and array.each() elements. Opaque validators retain their accepted values. Use the
415
+ returned cleaned payload at the boundary, including action getValidated()/safe().
405
416
 
406
417
  Schema declarations can use the narrow `@stacksjs/validation/runtime` entry.
407
418
  The schema proxy adds typed object/array/enum inference and conditional rules:
408
419
  when(sibling, literalOrPredicate, refine) applies cross-field refinements through
409
- objectWithContext; sometimes has the underlying optional/empty-value semantics.
420
+ objectWithContext, action validations, model writes and validateModelInput.
421
+ Model contexts recognize attribute/column aliases. Creates include declared defaults;
422
+ partial updates validate supplied fields and siblings without loading stored values.
423
+ Refinements preserve reusable schemas. sometimes has the underlying optional/empty-value semantics.
410
424
  Read object-with-context.ts before treating presence-only rules as stricter than
411
425
  they are. Inferred object shapes are native, not a hand-written request interface.
412
426
 
@@ -417,19 +431,17 @@ An upload is a class instance (the router's `UploadedFile`); a plain object, whi
417
431
  is what a JSON body gives for the same field name, is refused.
418
432
 
419
433
  registerRule/getCustomRule, unique(table,column,exceptId?), exists(table,column)
420
- and validateFieldAsync support application async validation. Current unique/exists
421
- helpers return allowed when the database cannot be queried, so they are advisory
422
- validation, not authorization or a replacement for database constraints. Validate
434
+ and validateFieldAsync support application async validation. These helpers propagate query failures
435
+ instead of approving unchecked values. Keep operational failures separate from invalid input. Validate
423
436
  uniqueness at the durable write boundary and handle a uniqueness violation there.
424
437
 
425
- The latest-error reporter is shared process state; do not use getErrors as
426
- request-local concurrent state. Read errors on the returned/raised validation
427
- result instead. Local is.ts helpers and legacy rules/type declarations are not
438
+ Local is.ts helpers and legacy rules/type declarations are not
428
439
  automatically all public exports from the root.
429
440
 
430
441
  Source: `storage/framework/core/validation/src/index.ts`, runtime.ts,
431
442
  schema.ts, request-validator.ts, conditional.ts, file-validator.ts and validator.ts.
432
- Tests: conditional.test.ts, file-validator.test.ts, async-rules.test.ts,
443
+ Tests: conditional.test.ts, conditional-boundaries.test.ts, input-contracts.test.ts,
444
+ boundary-e2e.test.ts, file-validator.test.ts, async-rules.test.ts,
433
445
  runtime-entry.test.ts and type-inference.test-d.ts under core/validation/tests.
434
446
 
435
447
  ## Custom action input
@@ -456,6 +468,11 @@ null, and absent boolean input returns undefined. Boolean input accepts JSON
456
468
  booleans and the explicit form values `true`, `false`, `1`, `0` and their strings,
457
469
  and a checkbox's `on` and `off`.
458
470
 
471
+ Numeric bounds reject NaN and inverted intervals; -Infinity and Infinity explicitly
472
+ mean an unbounded lower/upper endpoint. Text maxLength requires a nonnegative safe
473
+ integer. Invalid configuration throws RangeError even for absent optional input.
474
+ Unsafe whole numbers are rejected whether or not integer-only input was requested.
475
+
459
476
  `validateModelInput` checks only supplied fields using `getDefinition()` rules.
460
477
  The action still selects writable fields, requires creation fields and validates
461
478
  relationships. Use the transaction callback executor for all atomic persistence.
@@ -2,7 +2,7 @@
2
2
  "publisher": "Stacks",
3
3
  "name": "vscode-stacks",
4
4
  "displayName": "Stacks",
5
- "version": "0.75.445",
5
+ "version": "0.75.447",
6
6
  "description": "Stacks, built into VS Code: stx templates, pickier linting and formatting, encrypted env files, the dev server preview and every buddy command. No other extensions required.",
7
7
  "license": "MIT",
8
8
  "funding": "https://github.com/sponsors/chrisbbreuer",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/defaults",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.445",
5
+ "version": "0.75.447",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/stacksjs/stacks.git",
@@ -45,7 +45,7 @@
45
45
  "dependencies": {
46
46
  "@iconify-json/f7": "^1.2.2",
47
47
  "@iconify-json/hugeicons": "^1.2.35",
48
- "@stacksjs/mobile": "0.75.445",
48
+ "@stacksjs/mobile": "0.75.447",
49
49
  "@stacksjs/sanitizer": "0.2.789",
50
50
  "@stacksjs/stx": "0.2.789",
51
51
  "@xterm/addon-fit": "^0.11.0",
@@ -3,12 +3,13 @@ interface AvatarProps {
3
3
  src?: string
4
4
  alt?: string
5
5
  name?: string
6
+ initials?: string
6
7
  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
7
8
  rounded?: 'sm' | 'md' | 'lg' | 'full'
8
9
  status?: 'online' | 'offline' | 'away' | 'busy'
9
10
  }
10
11
 
11
- const { src = '', alt = '', name = '', size = 'md', rounded = 'full', status = '' } = defineProps<AvatarProps>()
12
+ const { src = '', alt = '', name = '', initials: suppliedInitials = '', size = 'md', rounded = 'full', status = '' } = defineProps<AvatarProps>()
12
13
 
13
14
  const sizeClasses = {
14
15
  xs: 'h-5 w-5 text-[10px]',
@@ -50,10 +51,10 @@ function getInitials(fullName: string) {
50
51
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase()
51
52
  }
52
53
 
53
- const initials = getInitials(name)
54
+ const initials = suppliedInitials || getInitials(name)
54
55
 
55
56
  // Background colors for initials (consistent based on name)
56
- const bgColors = [
57
+ export const bgColors = [
57
58
  'bg-red-500/15 text-red-600 dark:bg-red-400/20 dark:text-red-400',
58
59
  'bg-orange-500/15 text-orange-600 dark:bg-orange-400/20 dark:text-orange-400',
59
60
  'bg-yellow-500/15 text-yellow-600 dark:bg-yellow-400/20 dark:text-yellow-400',
@@ -66,33 +67,42 @@ const bgColors = [
66
67
  'bg-pink-500/15 text-pink-600 dark:bg-pink-400/20 dark:text-pink-400'
67
68
  ]
68
69
 
69
- function getBgColorIndex(fullName: string) {
70
- if (!fullName) return 0
70
+ defineClientPayload({ bgColors })
71
+ </script>
72
+ <script client>
73
+ const avatarSrc = useReactiveProp('src', {{ src }})
74
+ const avatarAlt = useReactiveProp('alt', {{ alt }})
75
+ const avatarName = useReactiveProp('name', {{ name }})
76
+ const avatarInitials = useReactiveProp('initials', {{ suppliedInitials }})
77
+ const avatarDescription = derived(() => avatarAlt() || avatarName() || 'Avatar')
78
+ const avatarFallback = derived(() => {
79
+ if (avatarInitials()) return avatarInitials()
80
+ const parts = String(avatarName() || '').trim().split(/\s+/).filter(Boolean)
81
+ if (!parts.length) return ''
82
+ return (parts.length === 1 ? parts[0].slice(0, 2) : parts[0][0] + parts[parts.length - 1][0]).toUpperCase()
83
+ })
84
+ const avatarColor = derived(() => {
85
+ const name = String(avatarName() || '')
86
+ if (!name) return 'bg-black/[0.06] text-neutral-500 dark:bg-white/10 dark:text-neutral-400'
71
87
  let hash = 0
72
- for (let i = 0; i < fullName.length; i++) {
73
- hash = fullName.charCodeAt(i) + ((hash << 5) - hash)
74
- }
75
- return Math.abs(hash) % bgColors.length
76
- }
77
-
78
- const bgColor = name ? bgColors[getBgColorIndex(name)] : 'bg-black/[0.06] text-neutral-500 dark:bg-white/10 dark:text-neutral-400'
88
+ for (let i = 0; i < name.length; i++) hash = name.charCodeAt(i) + ((hash << 5) - hash)
89
+ const colors = bgColors
90
+ return colors[Math.abs(hash) % colors.length]
91
+ })
79
92
  </script>
80
93
  <div class="inline-flex relative flex-shrink-0">
81
- @if(src)
82
94
  <img
95
+ :if="avatarSrc"
83
96
  src="{{ src }}"
97
+ :src="avatarSrc"
84
98
  alt="{{ alt || name || 'Avatar' }}"
99
+ :alt="avatarDescription"
85
100
  class="object-cover {{ sizeClasses[size] }} {{ radiusClasses[rounded] }}"
86
101
  >
87
- @else
88
- <div class="inline-flex items-center justify-center font-medium {{ sizeClasses[size] }} {{ radiusClasses[rounded] }} {{ bgColor }}">
89
- @if(initials)
90
- <span>{{ initials }}</span>
91
- @else
92
- <span aria-hidden="true" class="h-1/2 w-1/2 text-neutral-400 i-hugeicons-user"></span>
93
- @endif
102
+ <div :if="!avatarSrc" :class="avatarColor" class="inline-flex items-center justify-center font-medium {{ sizeClasses[size] }} {{ radiusClasses[rounded] }}">
103
+ <span :if="avatarFallback" :text="avatarFallback">{{ initials }}</span>
104
+ <span :if="!avatarFallback" aria-hidden="true" class="h-1/2 w-1/2 text-neutral-400 i-hugeicons-user"></span>
94
105
  </div>
95
- @endif
96
106
 
97
107
  @if(status)
98
108
  <span class="absolute bottom-0 right-0 rounded-full ring-2 ring-white dark:ring-neutral-900 {{ statusColors[status] }} {{ statusSizes[size] }}"></span>