@stacksjs/defaults 0.75.111 → 0.75.113

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.
@@ -83,3 +83,13 @@ contract. `drivers/singlestore.ts` owns the experimental backend.
83
83
  Retained tests: `ttl.test.ts`, `getorset-timeout.test.ts`,
84
84
  `tagged-flush-race.test.ts`, `redis-contract.test.ts` and
85
85
  `cache-factory.test.ts` under `core/cache/tests/`.
86
+
87
+ ## Representation-aware keys
88
+
89
+ `cacheKey(namespace, dependencies)` creates a deterministic key from named
90
+ primitive dependencies. Include owner/tenant scope, content version and every
91
+ setting affecting the result (for example privacy, locale or theme). Property
92
+ order does not change it; delimiter-containing strings cannot collide. Null
93
+ and undefined remain distinct. This constructs identity; it does not observe
94
+ settings, grant authorization or invalidate already issued public URLs.
95
+ The narrow `@stacksjs/cache/key` entry is pure and browser-bundleable.
@@ -313,3 +313,18 @@ is the helper for when the count is genuinely what you want.
313
313
 
314
314
  > Touching a money path? `/stacks-tdd` for the seam to test it at, and
315
315
  > `/stacks-review` before it merges. `/stacks-payments` covers the Stripe side.
316
+
317
+ ## Input and inventory abstractions
318
+
319
+ `parseSalesTaxSetup(body, { maxRate?, maxRates? })` requires an explicit rates
320
+ array, rejects coerced prices/text and duplicate normalized component codes,
321
+ and accepts [] only as deliberate removal. HQ-style stores can set maxRate
322
+ to 30; the general default is 100. This validates configuration, not the legal
323
+ rate applicable to a sale. `parseTaxRateWriteData` is also used by native tax
324
+ store/update/bulk operations so false form booleans cannot select a default.
325
+
326
+ `restockedQuantity(current, units, maximum?)` adds positive whole units,
327
+ refusing untracked stock and overflow. `commerce.products.items.restock(id, units)`
328
+ uses that rule under a row lock and reads back on its own connection;
329
+ `updateInventory` validates absolute quantities through the same boundary.
330
+ Pure narrow entries are commerce/inventory and commerce/tax-input.
@@ -283,3 +283,11 @@ now().startOfMonth().toDateString() // first of current month
283
283
  - `endOfMonth()` uses the `new Date(year, month + 1, 0)` trick to find the last day
284
284
  - Application timezone should be configured in `config/app.ts`
285
285
  - Prefer `@stacksjs/datetime` over raw `Date` for framework consistency
286
+
287
+ ## Site and organization clocks
288
+
289
+ `localDateTime(instant, ...timeZones)` resolves the first valid named zone, then
290
+ UTC, and returns `{ date, time, timeZone }` from one instant. Supply site and
291
+ organization zones in that order. Missing/invalid zones fall through; an
292
+ invalid instant throws. The output uses YYYY-MM-DD and HH:mm and is suitable
293
+ for comparing local schedule dates and end times, not for elapsed durations.
@@ -23,7 +23,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
23
23
  | `@stacksjs/browser-extension` | [`stacks-browser-extension`](../stacks-browser-extension/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/browser-extension/src/index.ts) | 7 |
24
24
  | `@stacksjs/buddy` | [`stacks-buddy`](../stacks-buddy/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/src/index.ts) | 142 |
25
25
  | `@stacksjs/build` | [`stacks-build`](../stacks-build/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/build/src/index.ts) | 2 |
26
- | `@stacksjs/cache` | [`stacks-cache`](../stacks-cache/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/src/index.ts) | 10 |
26
+ | `@stacksjs/cache` | [`stacks-cache`](../stacks-cache/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/src/index.ts) | 11 |
27
27
  | `@stacksjs/calendar-api` | [`stacks-calendar`](../stacks-calendar/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/src/index.ts) | 2 |
28
28
  | `@stacksjs/charts` | [`stacks-charts`](../stacks-charts/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/charts/src/index.ts) | 3 |
29
29
  | `@stacksjs/chat` | [`stacks-chat`](../stacks-chat/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/chat/src/index.ts) | 5 |
@@ -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) | 15 |
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) | 4 |
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) | 106 |
39
- | `@stacksjs/datetime` | [`stacks-datetime`](../stacks-datetime/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/index.ts) | 10 |
39
+ | `@stacksjs/datetime` | [`stacks-datetime`](../stacks-datetime/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/index.ts) | 11 |
40
40
  | `@stacksjs/defaults` | [`stacks-build`](../stacks-build/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/defaults/package.json) | 1 |
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) | 11 |
@@ -63,9 +63,9 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
63
63
  | `@stacksjs/mobile` | [`stacks-mobile`](../stacks-mobile/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/mobile/src/index.ts) | 11 |
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
- | `@stacksjs/notifications` | [`stacks-notifications`](../stacks-notifications/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/index.ts) | 10 |
66
+ | `@stacksjs/notifications` | [`stacks-notifications`](../stacks-notifications/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/index.ts) | 11 |
67
67
  | `@stacksjs/objects` | [`stacks-objects`](../stacks-objects/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/src/index.ts) | 4 |
68
- | `@stacksjs/orm` | [`stacks-orm`](../stacks-orm/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/index.ts) | 59 |
68
+ | `@stacksjs/orm` | [`stacks-orm`](../stacks-orm/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/index.ts) | 60 |
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) | 9 |
71
71
  | `@stacksjs/payments` | [`stacks-payments`](../stacks-payments/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/index.ts) | 20 |
@@ -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) | 5 |
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) | 14 |
97
+ | `@stacksjs/validation` | [`stacks-validation`](../stacks-validation/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/index.ts) | 15 |
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) | 4 |
100
100
 
@@ -231,9 +231,9 @@ Guidance: [`stacks-cache`](../stacks-cache/SKILL.md). Source: [storage/framework
231
231
 
232
232
  Read the entrypoint and package export map for re-exported or type-only surfaces.
233
233
 
234
- Local source modules: [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/src/drivers/index.ts).
234
+ Local source modules: [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/src/drivers/index.ts), [src/key.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/src/key.ts).
235
235
 
236
- Evidence directory: [10 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
236
+ Evidence directory: [11 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
237
237
 
238
238
  ### @stacksjs/calendar-api
239
239
 
@@ -307,9 +307,9 @@ Evidence directory: [2 retained tests](https://github.com/stacksjs/stacks/blob/m
307
307
 
308
308
  Guidance: [`stacks-commerce`](../stacks-commerce/SKILL.md). Source: [storage/framework/core/commerce/src/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/index.ts).
309
309
 
310
- Declared runtime exports: `PriceFormatError`, `auctions`, `breakdownFor`, `carts`, `catalog`, `catalogImport`, `commerce`, `coupons`, `currencyExponent`, `customers`, `decimalToMinor`, `default`, `devices`, `errors`, `formatCurrency`, `formatMinor`, `giftCards`, `minorToDecimal`, `minorToInput`, `minorToMajor`, `money`, `moneyInputError`, `moneyInputStep`, `multiplierOf`, `orders`, `parseMoneyInput`, `payments`, `products`, `receipts`, `register`, `releases`, `restaurant`, `shippings`, `tax`, `waitlists`.
310
+ Declared runtime exports: `PriceFormatError`, `auctions`, `breakdownFor`, `carts`, `catalog`, `catalogImport`, `commerce`, `coupons`, `currencyExponent`, `customers`, `decimalToMinor`, `default`, `devices`, `errors`, `formatCurrency`, `formatMinor`, `giftCards`, `minorToDecimal`, `minorToInput`, `minorToMajor`, `money`, `moneyInputError`, `moneyInputStep`, `multiplierOf`, `orders`, `parseMoneyInput`, `parseSalesTaxSetup`, `parseTaxRateWriteData`, `payments`, `products`, `receipts`, `register`, `releases`, `restaurant`, `restockedQuantity`, `shippings`, `tax`, `waitlists`.
311
311
 
312
- Local source modules: [src/auctions/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/auctions/index.ts), [src/carts/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/carts/index.ts), [src/catalog.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/catalog.ts), [src/coupons/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/coupons/index.ts), [src/customers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/customers/index.ts), [src/devices/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/devices/index.ts), [src/errors/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/errors/index.ts), [src/gift-cards/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/gift-cards/index.ts), [src/imports/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/imports/index.ts), [src/money.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/money.ts), [src/orders/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/orders/index.ts), [src/payments/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/payments/index.ts), [src/products/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/products/index.ts), [src/receipts/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/receipts/index.ts), [src/register.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/register.ts), [src/releases.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/releases.ts), [src/sales-tax.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/sales-tax.ts), [src/shippings/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/shippings/index.ts), [src/tax/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/tax/index.ts), [src/waitlists/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/waitlists/index.ts), [src/waitlists/restaurant/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/waitlists/restaurant/index.ts).
312
+ Local source modules: [src/auctions/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/auctions/index.ts), [src/carts/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/carts/index.ts), [src/catalog.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/catalog.ts), [src/coupons/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/coupons/index.ts), [src/customers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/customers/index.ts), [src/devices/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/devices/index.ts), [src/errors/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/errors/index.ts), [src/gift-cards/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/gift-cards/index.ts), [src/imports/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/imports/index.ts), [src/inventory.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/inventory.ts), [src/money.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/money.ts), [src/orders/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/orders/index.ts), [src/payments/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/payments/index.ts), [src/products/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/products/index.ts), [src/receipts/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/receipts/index.ts), [src/register.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/register.ts), [src/releases.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/releases.ts), [src/sales-tax.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/sales-tax.ts), [src/shippings/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/shippings/index.ts), [src/tax-input.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/tax-input.ts), [src/tax/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/tax/index.ts), [src/waitlists/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/waitlists/index.ts), [src/waitlists/restaurant/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/src/waitlists/restaurant/index.ts).
313
313
 
314
314
  Evidence directory: [12 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/commerce/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
315
315
 
@@ -357,11 +357,11 @@ Evidence directory: [106 retained tests](https://github.com/stacksjs/stacks/blob
357
357
 
358
358
  Guidance: [`stacks-datetime`](../stacks-datetime/SKILL.md). Source: [storage/framework/core/datetime/src/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/index.ts).
359
359
 
360
- Declared runtime exports: `DateTime`, `addCalendarMonths`, `dateFormat`, `format`, `isoInZone`, `now`, `parse`, `zoneOffsetMinutes`, `zonedTimeToUtc`.
360
+ Declared runtime exports: `DateTime`, `addCalendarMonths`, `dateFormat`, `format`, `isoInZone`, `localDateTime`, `now`, `parse`, `zoneOffsetMinutes`, `zonedTimeToUtc`.
361
361
 
362
- 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/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/zone.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/zone.ts).
362
+ 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/zone.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/zone.ts).
363
363
 
364
- Evidence directory: [10 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.
364
+ Evidence directory: [11 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.
365
365
 
366
366
  ### @stacksjs/defaults
367
367
 
@@ -607,11 +607,11 @@ Evidence directory: [7 retained tests](https://github.com/stacksjs/stacks/blob/m
607
607
 
608
608
  Guidance: [`stacks-notifications`](../stacks-notifications/SKILL.md). Source: [storage/framework/core/notifications/src/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/index.ts).
609
609
 
610
- Declared runtime exports: `BroadcastNotificationDriver`, `DatabaseNotificationDriver`, `bulkSetPreferences`, `emailParagraphs`, `ensureSuccessfulEmailResult`, `filterChannelsByPreferences`, `getNotificationPreferences`, `makeDeliveryRecord`, `notification`, `notificationEmailHtml`, `notificationEmailText`, `notify`, `recordNotificationDelivery`, `resolveDeliveryRecipient`, `setNotificationPreference`, `useBroadcast`, `useChat`, `useDatabase`, `useEmail`, `useNotification`, `usePush`, `useSMS`, `wherePreferenceCategory`.
610
+ Declared runtime exports: `BroadcastNotificationDriver`, `DatabaseNotificationDriver`, `bulkSetPreferences`, `emailParagraphs`, `ensureSuccessfulEmailResult`, `ensureSuccessfulNotificationResults`, `filterChannelsByPreferences`, `getNotificationPreferences`, `makeDeliveryRecord`, `notification`, `notificationEmailHtml`, `notificationEmailText`, `notify`, `recordNotificationDelivery`, `resolveDeliveryRecipient`, `setNotificationPreference`, `useBroadcast`, `useChat`, `useDatabase`, `useEmail`, `useNotification`, `usePush`, `useSMS`, `wherePreferenceCategory`.
611
611
 
612
612
  Local source modules: [src/delivery.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/delivery.ts), [src/drivers/broadcast.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/drivers/broadcast.ts), [src/drivers/database.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/drivers/database.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/drivers/index.ts), [src/email-body.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/email-body.ts), [src/preferences.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/preferences.ts).
613
613
 
614
- Evidence directory: [10 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
614
+ Evidence directory: [11 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
615
615
 
616
616
  ### @stacksjs/objects
617
617
 
@@ -629,9 +629,9 @@ Guidance: [`stacks-orm`](../stacks-orm/SKILL.md). Source: [storage/framework/cor
629
629
 
630
630
  Declared runtime exports: `Activity`, `AnalyticsEvent`, `Auction`, `AuctionItem`, `Author`, `Automation`, `AutomationRun`, `BILLABLE_INSTANCE_METHODS`, `Bid`, `Board`, `BoardColumn`, `Campaign`, `CampaignSend`, `CampaignVariant`, `Card`, `CardAttachment`, `CardComment`, `Cart`, `CartItem`, `Categorizable`, `Category`, `Comment`, `CommunicationSuppression`, `ConsentEvent`, `Coupon`, `Courier`, `CourierPing`, `Customer`, `DeliveryRoute`, `DeliveryStop`, `Deployment`, `DigitalDelivery`, `EmailIdempotency`, `EmailList`, `EmailListSubscriber`, `EmailSuppression`, `EmailWebhookEvent`, `ErrorModel`, `FailedJob`, `FeedbackToken`, `Form`, `FormField`, `FormSubmission`, `GdprRequest`, `GiftCard`, `Job`, `Label`, `LicenseKey`, `Log`, `LoyaltyPoint`, `LoyaltyReward`, `MagicLinkToken`, `MailPreference`, `Manufacturer`, `Menu`, `MenuItem`, `Notification`, `NotificationDelivery`, `Order`, `OrderIdempotency`, `OrderItem`, `Page`, `PageRevision`, `Payment`, `PaymentMethod`, `PaymentProduct`, `PaymentTransaction`, `PaymentWebhookEvent`, `PersonalAccessToken`, `Pledge`, `Post`, `PrintDevice`, `Product`, `ProductUnit`, `ProductVariant`, `QueryLog`, `Receipt`, `Redirect`, `Referral`, `ReferralCode`, `Release`, `Request`, `Review`, `SenderDomain`, `ShippingMethod`, `ShippingRate`, `ShippingZone`, `Site`, `SiteDomain`, `SmsOptOut`, `SocialAccount`, `SocialPost`, `StorageItem`, `StorageItemTask`, `Subscriber`, `SubscriberEmail`, `Subscription`, `SubscriptionNotOwnedError`, `Tag`, `TaxRate`, `Team`, `TeamInvitation`, `TeamMember`, `Transaction`, `UsageEvent`, `User`, `WaitlistProduct`, `WaitlistRestaurant`, `Websocket`, `buildDatabaseSchema`, `collectEncryptedAttributes`, `createAuditMethods`, `createCategorizableMethods`, `createCommentableMethods`, `createLikeableMethods`, `createTaggableMethods`, `customerOwnership`, `decryptValue`, `encryptValue`, `enrichPaginatorUrls`, `filterFillable`, `getWritableFields`, `isBillable`, `isCursorPaginator`, `isEncrypted`, `isPaginator`, `isSimplePaginator`, `isUniqueViolation`, `mapWriteError`, `modelGlobalNames`, `ormReady`, `parentOwnership`, `parseCursor`, `renderDatabaseTypeFile`, `resolveCursorArgs`, `resolvePageArgs`, `selfOwnership`, `setAuditUser`, `siteOwnership`, `teamMembershipOwnership`, `toCursorPaginator`, `toPaginator`, `toSimplePaginator`, `toSnakeCaseKeys`.
631
631
 
632
- Local source modules: [src/auto-crud.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/auto-crud.ts), [src/batch-loader.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/batch-loader.ts), [src/db.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/db.ts), [src/define-model.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/define-model.ts), [src/extend-model.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/extend-model.ts), [src/gdpr/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/gdpr/index.ts), [src/generate-database-schema.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/generate-database-schema.ts), [src/model-types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/model-types.ts), [src/ownership.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/ownership.ts), [src/paginator-request.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/paginator-request.ts), [src/subquery.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/subquery.ts), [src/traits/audit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/audit.ts), [src/traits/billable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/billable.ts), [src/traits/categorizable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/categorizable.ts), [src/traits/commentable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/commentable.ts), [src/traits/likeable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/likeable.ts), [src/traits/taggable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/taggable.ts), [src/transaction.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/transaction.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/types.ts), [src/utils/compiled-binary.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/compiled-binary.ts), [src/utils/encrypted.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/encrypted.ts), [src/utils/prunable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/prunable.ts).
632
+ Local source modules: [src/auto-crud.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/auto-crud.ts), [src/batch-loader.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/batch-loader.ts), [src/db.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/db.ts), [src/define-model.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/define-model.ts), [src/extend-model.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/extend-model.ts), [src/gdpr/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/gdpr/index.ts), [src/generate-database-schema.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/generate-database-schema.ts), [src/locked-row.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/locked-row.ts), [src/model-types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/model-types.ts), [src/ownership.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/ownership.ts), [src/paginator-request.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/paginator-request.ts), [src/row-token.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/row-token.ts), [src/subquery.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/subquery.ts), [src/traits/audit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/audit.ts), [src/traits/billable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/billable.ts), [src/traits/categorizable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/categorizable.ts), [src/traits/commentable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/commentable.ts), [src/traits/likeable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/likeable.ts), [src/traits/taggable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/traits/taggable.ts), [src/transaction.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/transaction.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/types.ts), [src/utils/compiled-binary.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/compiled-binary.ts), [src/utils/encrypted.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/encrypted.ts), [src/utils/prunable.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/utils/prunable.ts).
633
633
 
634
- Evidence directory: [59 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
634
+ Evidence directory: [60 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
635
635
 
636
636
  ### @stacksjs/pagination
637
637
 
@@ -907,7 +907,7 @@ Declared runtime exports: `applyConditionals`, `isArray`, `isBoolean`, `isFuncti
907
907
 
908
908
  Local source modules: [src/input.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/input.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).
909
909
 
910
- Evidence directory: [14 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.
910
+ Evidence directory: [15 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.
911
911
 
912
912
  ### @stacksjs/video
913
913
 
@@ -36,7 +36,7 @@ optional `error`. A resolved call does not imply every channel succeeded.
36
36
  |---|---|---|
37
37
  | email | email | `@stacksjs/email` |
38
38
  | sms | phone | `@stacksjs/sms` |
39
- | chat | configured channel transport | `@stacksjs/chat` |
39
+ | chat | chatRecipient, a channel/user id or array | `@stacksjs/chat` |
40
40
  | database | userId | database notification inbox |
41
41
  | push | pushTokens, a string or string array | `@stacksjs/push`, default Expo |
42
42
  | broadcast | broadcastChannel or userId | `@stacksjs/realtime` |
@@ -46,16 +46,13 @@ renders it through the framework layout. Use the exported `NotificationAction`
46
46
  type for an action link. The broadcast fallback is `private-user.{userId}` or
47
47
  public `notifications` when no user/channel is supplied. Its delivery is
48
48
  best-effort; a missing realtime server is not a durable notification queue.
49
- Read `stacks-chat` before choosing notify's chat channel: its generic body
50
- payload does not supply a provider's channel recipient or configure credentials.
51
-
52
- Current notify result mapping marks a fulfilled channel call successful. Email
53
- explicitly checks EmailResult, but SMS/chat/push structured failures and a
54
- broadcast result with delivered false are not all normalized into failed
55
- NotifyResult entries. For provider-confirmed status, use the direct channel API
56
- and inspect its structured result. The generic chat body also differs from
57
- ChatMessage's to/content contract; use direct chat send with a valid message.
58
- Delivery logs inherit this result mapping and are not final receipt evidence.
49
+ Read `stacks-chat` to configure transport credentials. Notify maps body to
50
+ ChatMessage.content and chatRecipient to ChatMessage.to. Email, SMS, chat and
51
+ push structured failures and broadcast delivered:false produce failed
52
+ NotifyResult entries. Provider acceptance is not a final delivery receipt.
53
+ `ensureSuccessfulNotificationResults(results)` throws for an empty result list
54
+ or any failed channel, and accepts only a non-empty list of successes. Use it
55
+ when a queued worker must retry partial fan-out instead of marking it sent.
59
56
 
60
57
  ## Preferences and delivery tracking
61
58
 
@@ -86,8 +83,10 @@ and transport results rather than assuming a delivery log is provider proof.
86
83
  - `useEmail(driver?)` returns a sendable transport. Without a driver it uses
87
84
  the Mail singleton and `config/email.ts`. Unknown explicit drivers warn and
88
85
  fall back to that singleton.
89
- - `useSMS(driver?)` and `useChat(driver?)` select their driver modules
90
- (defaults Twilio and Slack); configure those packages before sending.
86
+ - `useSMS()` uses the configured SMS facade; `useSMS('twilio' | 'vonage')`
87
+ explicitly selects a provider after loading configuration. `useChat(driver?)`
88
+ selects Slack (default), Discord or Teams. Unknown drivers throw. Configure
89
+ those packages before sending.
91
90
  - `useDatabase()` returns DatabaseNotificationDriver.
92
91
  - `usePush()` returns the push namespace, and `useBroadcast()` the broadcast driver.
93
92
  - `useNotification(type?, driver?)`/`notification()` select email, sms, chat
@@ -107,10 +106,11 @@ const count = await inbox.unreadCount(7)
107
106
  await inbox.markAllAsRead(7)
108
107
  ~~~
109
108
 
110
- Also available: `getUserNotifications(userId)`, `markAsRead(id)`,
111
- `deleteNotification(id)` and `deleteAllNotifications(userId)`. These
112
- id-only mutation helpers do not enforce ownership themselves; scope access in
113
- the application's authenticated action before calling them.
109
+ Also available: `getUserNotifications(userId)`, `markAsRead(id, userId?)`,
110
+ `deleteNotification(id, userId?)` and `deleteAllNotifications(userId)`. These
111
+ single-row mutations accept an optional authenticated userId. Supply it to
112
+ enforce ownership in the mutation statement; id-only calls retain their trusted
113
+ internal-use behavior. Reads and mark-all/delete-all already require userId.
114
114
 
115
115
  Rows contain id, user_id, type, data (JSON string), read_at, created_at and
116
116
  updated_at. Parse data when reading; null read_at means unread. The native
@@ -139,3 +139,20 @@ Evidence includes `model-validation`, `mass-assignment`, `force-create-guarded`,
139
139
  `casting`, `belongs-to-many`, `observed-writes`, `model-policy-wiring`,
140
140
  `paginator-request`, and the inference type tests. Read their actual contracts
141
141
  when a workflow depends on a dialect, trait, or return shape.
142
+
143
+ ## Scoped locked operations and stored link credentials
144
+
145
+ `withLockedRow(table, scope, async (row, tx) => ..., { tx? })` locks and reads
146
+ a scoped row on one reserved connection. It returns null for a missing row.
147
+ Use the callback handle for every related read/write; `tx` reuses an enclosing
148
+ transaction. A scope must identify one row; empty or non-unique scopes are refused. This is the query-builder seam: model
149
+ casts, fillable filtering and lifecycle hooks remain the caller's responsibility.
150
+
151
+ `rowToken({ table, scope, column, action, tx?, timestampColumn? })` manages a
152
+ stored share/feed credential. Read returns the live token; enable preserves
153
+ it; rotate replaces it; disable clears it. Mutations serialize under the row
154
+ lock and join an enclosing transaction when supplied. Missing owners throw
155
+ `RowTokenNotFoundError` (404). The default timestamp column is updated_at;
156
+ pass false for a table without timestamps. Identifiers and scope must be
157
+ application-selected, and the caller must authorize that owner. This is for
158
+ stored public-link secrets, not hashed authentication tokens with expiry.
@@ -118,6 +118,8 @@ await sendBulk([msg1, msg2, msg3]) // returns SmsSendResult[]
118
118
  ```
119
119
 
120
120
  `send()` and all sending functions call `ensureConfig()` first, which lazily loads `config/sms.ts` via dynamic import.
121
+ Runtime `configure()` overrides survive that lazy load and subsequent `init()`
122
+ calls, including credentials supplied before the first send.
121
123
 
122
124
  ## Message Status & Info
123
125
 
@@ -432,3 +432,16 @@ booleans and the explicit form values `true`, `false`, `1`, `0` and their string
432
432
  The action still selects writable fields, requires creation fields and validates
433
433
  relationships. Use the transaction callback executor for all atomic persistence.
434
434
  Root and narrow input imports share the same error constructor.
435
+
436
+ ## Reusable boundary and stored-value rules
437
+
438
+ `parseTextInput(value, field, required?, maxLength?)` rejects overlong text
439
+ rather than truncating it. `parseEnumInput(value, field, choices)` requires
440
+ exact string membership; it never stringifies an object or boolean.
441
+ `storedBoolean(value, fallback)` decodes SQL/form 0/1 and false/true; the
442
+ caller explicitly chooses the fallback for missing or corrupt stored data.
443
+ Partial model validation accepts attribute names and their snake-case column
444
+ spelling, including camel-case definitions.
445
+
446
+ `positiveIdOrNull(value)` is the nullable adapter for optional identifiers and
447
+ record serializers; it uses the same safe-integer and type rules as parsePositiveId.
@@ -2,7 +2,7 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { describe, expect, test } from 'bun:test'
3
3
  import { commerceIdentifier, commerceMinorAmountError, commerceNotFound } from './commerce-action'
4
4
 
5
- function request(id: string): RequestInstance {
5
+ function request(id: unknown): RequestInstance {
6
6
  return {
7
7
  getParam: () => id,
8
8
  } as unknown as RequestInstance
@@ -11,6 +11,7 @@ function request(id: string): RequestInstance {
11
11
  describe('commerce action responses', () => {
12
12
  test('accepts safe positive route identifiers', () => {
13
13
  expect(commerceIdentifier(request('42'), 'Courier')).toEqual({ id: 42 })
14
+ for (const value of [true, {}, [], Number.MAX_SAFE_INTEGER + 1]) expect(commerceIdentifier(request(value), 'Courier').error?.status).toBe(422)
14
15
  })
15
16
 
16
17
  test('returns 422 for invalid identifiers', async () => {
@@ -1,4 +1,5 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
+ import { positiveIdOrNull } from '@stacksjs/validation/input'
2
3
  import { response } from '@stacksjs/router'
3
4
 
4
5
  export type CommerceIdentifierResult =
@@ -9,8 +10,8 @@ export function commerceIdentifier(
9
10
  request: RequestInstance,
10
11
  resource: string,
11
12
  ): CommerceIdentifierResult {
12
- const id = Number(request.getParam('id'))
13
- if (!Number.isSafeInteger(id) || id < 1) {
13
+ const id = positiveIdOrNull(request.getParam('id'))
14
+ if (id === null) {
14
15
  return {
15
16
  error: response.json({ message: `${resource} id must be a positive integer.` }, 422),
16
17
  }
@@ -1,4 +1,5 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
+ import { positiveIdOrNull } from '@stacksjs/validation/input'
2
3
  import { db } from '@stacksjs/database/runtime'
3
4
 
4
5
  /**
@@ -28,9 +29,7 @@ export function insertedId(result: unknown): number {
28
29
 
29
30
  /** The `{id}` route param, or 0 when it is not a usable row id. */
30
31
  export function rowId(request: RequestInstance): number {
31
- const id = Number(request.getParam('id'))
32
-
33
- return Number.isInteger(id) && id > 0 ? id : 0
32
+ return positiveIdOrNull(request.getParam('id')) ?? 0
34
33
  }
35
34
 
36
35
  /** Matches the slug the dashboard dialogs generate client-side. */
@@ -1,5 +1,6 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { describe, expect, test } from 'bun:test'
3
+ import { rowId } from './content-input'
3
4
  import { parseAuthorInput } from './author-input'
4
5
  import { parsePageInput, parsePublished } from './page-input'
5
6
 
@@ -43,3 +44,9 @@ describe('dashboard content record input', () => {
43
44
  expect(parsePublished(false)).toBe(false)
44
45
  })
45
46
  })
47
+
48
+
49
+ test('content identifiers use the shared safe-integer contract', () => {
50
+ for (const value of [true, {}, [], Number.MAX_SAFE_INTEGER + 1]) expect(rowId({ getParam: () => value } as unknown as RequestInstance)).toBe(0)
51
+ expect(rowId({ getParam: () => '42' } as unknown as RequestInstance)).toBe(42)
52
+ })
@@ -1,4 +1,5 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
+ import { positiveIdOrNull, storedBoolean } from '@stacksjs/validation/input'
2
3
  import { db } from '@stacksjs/database/runtime'
3
4
  import { insertedId, str, timestamp } from './content-input'
4
5
 
@@ -49,17 +50,16 @@ export async function findPost(id: number, database: typeof db = db): Promise<un
49
50
  function ids(value: unknown): number[] {
50
51
  const values = Array.isArray(value) ? value : value === undefined || value === null ? [] : [value]
51
52
  return [...new Set(values
52
- .map(item => Number(item))
53
- .filter(item => Number.isInteger(item) && item > 0))]
53
+ .map(positiveIdOrNull)
54
+ .filter((item): item is number => item !== null))]
54
55
  }
55
56
 
56
57
  function optionalId(value: unknown): number | null {
57
- const id = Number(value)
58
- return Number.isInteger(id) && id > 0 ? id : null
58
+ return positiveIdOrNull(value)
59
59
  }
60
60
 
61
61
  function booleanValue(value: unknown): boolean {
62
- return value === true || value === 1 || value === '1' || value === 'true' || value === 'on'
62
+ return storedBoolean(value === 'on' ? true : value, false)
63
63
  }
64
64
 
65
65
  export function invalidPostContent(payload: PostPayload): string | null {
@@ -15,6 +15,7 @@ describe('team records', () => {
15
15
  expect(parsePositiveId('42')).toBe(42)
16
16
  expect(parsePositiveId('0')).toBeNull()
17
17
  expect(parsePositiveId('4.2')).toBeNull()
18
+ for (const value of [true, false, {}, [], Number.MAX_SAFE_INTEGER + 1]) expect(parsePositiveId(value)).toBeNull()
18
19
  expect(normalizeInvitationEmail(' Teammate@Example.COM ')).toBe('teammate@example.com')
19
20
  expect(normalizeInvitationEmail('not-an-email')).toBeNull()
20
21
  expect(normalizeInvitationRole(' ADMIN ')).toBe('admin')
@@ -1,3 +1,4 @@
1
+ import { positiveIdOrNull } from '@stacksjs/validation/input'
1
2
  import { createHash, randomBytes } from 'node:crypto'
2
3
 
3
4
  export const TEAM_MEMBER_ROLES = ['owner', 'admin', 'member', 'viewer'] as const
@@ -9,8 +10,7 @@ export type TeamInvitationRole = typeof TEAM_INVITATION_ROLES[number]
9
10
  const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
10
11
 
11
12
  export function parsePositiveId(value: unknown): number | null {
12
- const id = Number(value)
13
- return Number.isInteger(id) && id > 0 ? id : null
13
+ return positiveIdOrNull(value)
14
14
  }
15
15
 
16
16
  export function changedRows(result: unknown): number {
@@ -2,7 +2,7 @@
2
2
  "publisher": "Stacks",
3
3
  "name": "vscode-stacks",
4
4
  "displayName": "Stacks",
5
- "version": "0.75.111",
5
+ "version": "0.75.113",
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.111",
5
+ "version": "0.75.113",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/stacksjs/stacks.git",
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@iconify-json/f7": "^1.2.2",
57
57
  "@iconify-json/hugeicons": "^1.2.35",
58
- "@stacksjs/mobile": "^0.75.111",
58
+ "@stacksjs/mobile": "^0.75.113",
59
59
  "@stacksjs/sanitizer": "^0.2.422",
60
60
  "@xterm/addon-fit": "^0.11.0",
61
61
  "@xterm/xterm": "^6.0.0",