@stacksjs/defaults 0.75.421 → 0.75.423
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/ai/skills/stacks-arrays/SKILL.md +3 -1
- package/ai/skills/stacks-cache/SKILL.md +13 -9
- package/ai/skills/stacks-calendar/SKILL.md +20 -12
- package/ai/skills/stacks-chat/SKILL.md +22 -9
- package/ai/skills/stacks-desktop/SKILL.md +7 -0
- package/ai/skills/stacks-i18n/SKILL.md +9 -10
- package/ai/skills/stacks-native/CATALOG.md +26 -26
- package/ai/skills/stacks-notifications/SKILL.md +6 -2
- package/ai/skills/stacks-objects/SKILL.md +6 -1
- package/ai/skills/stacks-payments/SKILL.md +5 -3
- package/ai/skills/stacks-push/SKILL.md +14 -9
- package/ai/skills/stacks-strings/SKILL.md +16 -10
- package/ai/skills/stacks-testing/SKILL.md +5 -1
- package/ai/skills/stacks-utils/SKILL.md +1 -1
- package/ai/skills/stacks-validation/SKILL.md +20 -4
- package/ai/skills/stacks-whois/SKILL.md +5 -4
- package/app/Actions/Dashboard/Content/post-input.ts +1 -1
- package/app/Models/Content/Post.ts +1 -1
- package/app/Models/Error.ts +1 -1
- package/app/Models/Log.ts +1 -1
- package/app/Models/PaymentMethod.ts +3 -3
- package/app/Models/PaymentProduct.ts +1 -1
- package/app/Models/Subscription.ts +1 -1
- package/app/Models/commerce/PrintDevice.ts +1 -1
- package/app/Models/commerce/Receipt.ts +1 -1
- package/app/Models/realtime/Websocket.ts +2 -2
- package/ide/vscode/package.json +1 -1
- package/package.json +4 -4
|
@@ -198,10 +198,12 @@ zScore([1, 2, 3, 4], 3) // 0.447...
|
|
|
198
198
|
percentile([1, 2, 3, 4, 5], 50) // 3 -- interpolated percentile
|
|
199
199
|
percentile([1, 2, 3, 4], 25) // 1.75 -- linear interpolation
|
|
200
200
|
// Note: takes percentile value (0-100), uses linear interpolation
|
|
201
|
+
// Throws for an empty array (like median) and a RangeError outside 0-100
|
|
201
202
|
|
|
202
203
|
interquartileRange([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
203
204
|
// Q3 median - Q1 median
|
|
204
|
-
//
|
|
205
|
+
// Sorts, splits at the midpoint, computes the median of each half
|
|
206
|
+
// One number gives 0; an empty array throws
|
|
205
207
|
|
|
206
208
|
covariance([1, 2, 3, 4], [1, 2, 3, 4]) // 1.25 -- positive correlation
|
|
207
209
|
covariance([1, 2, 3, 4], [4, 3, 2, 1]) // -1.25 -- negative correlation
|
|
@@ -22,10 +22,11 @@ const products = await cache.mget<{ name: string }>(['product:42', 'product:43']
|
|
|
22
22
|
// mget returns a record keyed by cache key, not an array.
|
|
23
23
|
~~~
|
|
24
24
|
|
|
25
|
-
- TTL is in seconds. Zero means forever; omitted TTL uses the
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
- TTL is in seconds. Zero means forever; an omitted TTL uses the store's
|
|
26
|
+
default. For the `cache` export that is `ttl` from config/cache.ts (3600 as
|
|
27
|
+
shipped) on every driver, memory included, so a value set with no TTL
|
|
28
|
+
expires after an hour. The `memory` export is a separate in-process
|
|
29
|
+
instance with no default expiry.
|
|
29
30
|
- `setForever` stores without expiry; `getTtl` reads remaining TTL and
|
|
30
31
|
`ttl(key, seconds)` updates it. Missing values are undefined.
|
|
31
32
|
- `mset([{ key, value, ttl? }])` writes entries. `del(keyOrKeys)` and
|
|
@@ -60,11 +61,14 @@ deploys, pointing `path` at a file the deploy preserves (the app's own SQLite
|
|
|
60
61
|
database works; the table is the driver's alone). `createCache(driver,
|
|
61
62
|
options?)` selects one of memory, redis, singlestore or sqlite.
|
|
62
63
|
|
|
63
|
-
The `cache` export is the driver `config/cache.ts` selects
|
|
64
|
-
|
|
65
|
-
and once the app's config has
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
The `cache` export is the driver `config/cache.ts` selects, with its `prefix`
|
|
65
|
+
and `ttl` applied on every driver (memory also takes `maxKeys`, `useClones`
|
|
66
|
+
and `drivers.memory`), resolved on first use and once the app's config has
|
|
67
|
+
loaded. It is the shared `memory` instance only when the config sets none of
|
|
68
|
+
those. The shipped config sets them, so there `cache` and `memory` are two
|
|
69
|
+
stores: a value written through one is not visible through the other.
|
|
70
|
+
`cacheFromConfig(settings?)` builds the same choice explicitly;
|
|
71
|
+
`resetConfiguredCache()` forgets it.
|
|
68
72
|
`config/cache.ts` reads `REDIS_URL`, `REDIS_HOST`, `REDIS_PORT`,
|
|
69
73
|
`REDIS_USERNAME`, `REDIS_PASSWORD` and `REDIS_DB`, as `config/queue.ts` does.
|
|
70
74
|
|
|
@@ -38,11 +38,13 @@ title, required allDay and optional description/address/timezone; the root entry
|
|
|
38
38
|
does not re-export that type. Infer a literal or read src/types.ts when extending
|
|
39
39
|
the package.
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
Single-event exports write a timed event as UTC instants (`...Z`), so a link
|
|
42
|
+
means the same on every host; `timezone` only sets the zone Google's compose
|
|
43
|
+
screen displays it in. All-day dates are the UTC calendar dates of from/to.
|
|
44
|
+
The ICS body escapes and folds its text as the feed builder does. Its UID
|
|
45
|
+
derives from the event's instants and title, so editing those changes the
|
|
46
|
+
UID. Do not promise subscription update semantics from a generated
|
|
47
|
+
single-event link; use the feed builder for stable records.
|
|
46
48
|
|
|
47
49
|
## Native subscribable feeds
|
|
48
50
|
|
|
@@ -59,13 +61,19 @@ control, even when the feed declares a suggested interval.
|
|
|
59
61
|
|
|
60
62
|
## Recurrence
|
|
61
63
|
|
|
62
|
-
parseRRule(value) returns RecurrenceRule or null for
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
parseRRule(value) returns a RecurrenceRule, or null for any rule it cannot
|
|
65
|
+
follow exactly: an unsupported frequency or part (BYMONTH, BYSETPOS, BYWEEKNO,
|
|
66
|
+
BYYEARDAY, BYHOUR...), a combination RFC 5545 rules out or this subset does
|
|
67
|
+
not cover, or a value it cannot read. Null means "do not expand this here",
|
|
68
|
+
never "expanded without the part". expandRecurrence(rule, dtstart, windowStart,
|
|
69
|
+
windowEnd) returns matching UTC start instants, capped at 1000.
|
|
70
|
+
|
|
71
|
+
The subset: DAILY/WEEKLY/MONTHLY/YEARLY, INTERVAL, COUNT or UNTIL, WKST, BYDAY
|
|
72
|
+
(a filter for DAILY and WEEKLY; within a month, `MO` is every Monday and `2TU`
|
|
73
|
+
or `-1FR` an ordinal) and BYMONTHDAY for DAILY or MONTHLY (`-1` is the last
|
|
74
|
+
day). Weeks start on WKST, Monday by default, as clients expand them. YEARLY
|
|
75
|
+
repeats dtstart's date, skipping years without it (Feb 29). Callers own
|
|
76
|
+
timezone interpretation and DST policy.
|
|
69
77
|
|
|
70
78
|
## Sources and evidence
|
|
71
79
|
|
|
@@ -95,7 +95,7 @@ abstract class BaseChatDriver implements ChatDriver {
|
|
|
95
95
|
|
|
96
96
|
```typescript
|
|
97
97
|
interface ChatMessage {
|
|
98
|
-
to
|
|
98
|
+
to?: string | string[] // a bot token's channel or user ID; omit for a webhook
|
|
99
99
|
from?: { id?: string, name?: string, avatar?: string }
|
|
100
100
|
subject?: string // title for rich messages
|
|
101
101
|
content?: string // plain text content
|
|
@@ -181,8 +181,14 @@ interface SlackAttachment {
|
|
|
181
181
|
|
|
182
182
|
### Slack Sending Modes
|
|
183
183
|
|
|
184
|
-
1. **Webhook mode**: If `config.webhookUrl` is set, sends via `POST` to webhook URL. Payload includes `text`, `username`, `mrkdwn: true`. If `message.template` exists, builds Block Kit blocks.
|
|
185
|
-
2. **Bot token mode**: If `config.botToken` is set, sends via Slack API `https://slack.com/api/chat.postMessage` with `Authorization: Bearer <token
|
|
184
|
+
1. **Webhook mode**: If `config.webhookUrl` is set, sends via `POST` to webhook URL. Payload includes `text`, `username`, `mrkdwn: true`. If `message.template` exists, builds Block Kit blocks. Needs no `message.to`.
|
|
185
|
+
2. **Bot token mode**: If `config.botToken` is set, sends via Slack API `https://slack.com/api/chat.postMessage` with `Authorization: Bearer <token>`, to the channel in `message.to` (required). Returns `ts` (timestamp) as message ID.
|
|
186
|
+
|
|
187
|
+
`send()` treats `content` as the plain text `ChatMessage` says it is: `&`, `<`
|
|
188
|
+
and `>` are escaped (`escapeSlackText`), so `<!channel>` or a disguised
|
|
189
|
+
`<url|label>` in user text arrives as literal text instead of pinging the
|
|
190
|
+
channel or hiding a link. Plain URLs still link. `sendWebhook(url, text)` is
|
|
191
|
+
raw, for Slack markup written on purpose.
|
|
186
192
|
3. Throws `Error('Slack not configured: provide webhookUrl or botToken')` if neither is set.
|
|
187
193
|
|
|
188
194
|
### Direct Webhook Function
|
|
@@ -247,8 +253,15 @@ interface DiscordMessage {
|
|
|
247
253
|
|
|
248
254
|
### Discord Sending Modes
|
|
249
255
|
|
|
250
|
-
1. **Webhook mode**: POST to `config.webhookUrl`. Handles `204` (empty success) responses. Returns `id` from response.
|
|
251
|
-
2. **Bot token mode**: POST to `https://discord.com/api/v10/channels/{channelId}/messages` with `Authorization: Bot <token>`. Uses `message.to` as the channel ID.
|
|
256
|
+
1. **Webhook mode**: POST to `config.webhookUrl`. Handles `204` (empty success) responses. Returns `id` from response. Needs no `message.to`.
|
|
257
|
+
2. **Bot token mode**: POST to `https://discord.com/api/v10/channels/{channelId}/messages` with `Authorization: Bot <token>`. Uses `message.to` as the channel ID (required).
|
|
258
|
+
|
|
259
|
+
Every payload carries `allowed_mentions`, by default `{ parse: ['users', 'roles'] }`
|
|
260
|
+
(`DEFAULT_ALLOWED_MENTIONS`): people and roles named by ID ping, `@everyone`
|
|
261
|
+
and `@here` in content never do. `sendWebhook()`'s `allowedMentions` replaces
|
|
262
|
+
the default, and its camelCase options are sent under Discord's names
|
|
263
|
+
(`avatar_url`, `allowed_mentions`) by `discordPayload()`. With a subject or
|
|
264
|
+
template the body goes in the embed only, not also as `content`.
|
|
252
265
|
|
|
253
266
|
### Embed Building
|
|
254
267
|
When `message.subject` or `message.template` is set, `buildEmbed()` creates an embed with:
|
|
@@ -457,10 +470,10 @@ All three drivers implement `sendWithRetry()`:
|
|
|
457
470
|
|
|
458
471
|
Direct send returns ChatResult, with success false for a handled provider failure;
|
|
459
472
|
inspect it before persisting sent state. Awaiting a send only proves the promise
|
|
460
|
-
settled.
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
473
|
+
settled. A webhook send needs no `to`; a bot-token send needs the channel in
|
|
474
|
+
`to`. notify()'s chat channel sends the body as `content` and `chatRecipient`
|
|
475
|
+
as `to`, through `recipient.chatDriver` or `config.notification.chat.default`,
|
|
476
|
+
and a failed send is a failed NotifyResult.
|
|
464
477
|
|
|
465
478
|
Settings resolve per send after app config readiness, with explicit configure
|
|
466
479
|
overrides taking precedence. Retained request/config coverage lives under
|
|
@@ -133,6 +133,13 @@ Write `app/Desktop/launcher.ts` and `build:desktop` compiles that instead, the
|
|
|
133
133
|
same way anything under `app/` overrides its framework default. `DESKTOP_URL`
|
|
134
134
|
and `APP_URL` then become optional, because the launcher decides what to open.
|
|
135
135
|
|
|
136
|
+
Every launcher, the framework's or yours, compiles through
|
|
137
|
+
`desktopLauncherCompileArgs()` to bytecode as an ES module: parsing the bundle
|
|
138
|
+
was most of a launcher's start (Browser's agent reached its window host in
|
|
139
|
+
379 ms as source, 146 ms as bytecode), for an executable about a quarter
|
|
140
|
+
larger. Pass `bytecode: false` only to compare. Compile any helper binary of
|
|
141
|
+
your own with the same function rather than hand-written `bun build` flags.
|
|
142
|
+
|
|
136
143
|
```ts
|
|
137
144
|
// app/Desktop/launcher.ts - compiled to Contents/MacOS/<AppName>
|
|
138
145
|
import { dirname, join } from 'node:path'
|
|
@@ -21,7 +21,7 @@ i18n/src/
|
|
|
21
21
|
├── translator.ts # I18n class, global API (t, tc, te, tm)
|
|
22
22
|
├── formatter.ts # Locale-aware formatting (dates, numbers, currency, lists)
|
|
23
23
|
├── loader.ts # File-based translation loader (JSON, YAML, JS/TS)
|
|
24
|
-
└── pluralization.ts # CLDR plural rules
|
|
24
|
+
└── pluralization.ts # CLDR plural rules, from the runtime's Intl.PluralRules
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## Core Types
|
|
@@ -101,14 +101,13 @@ tc('items', 5) // '5 items'
|
|
|
101
101
|
|
|
102
102
|
### Supported Languages
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
| Full (zero/one/two/few/many/other) | ar, cy |
|
|
104
|
+
Every language the runtime's CLDR data covers, through `Intl.PluralRules`,
|
|
105
|
+
for the full locale (`pt` counts 0 as `one`, `pt-PT` does not). A language it
|
|
106
|
+
has no data for says `other` for every number; `hasPluralRule(locale)` tells
|
|
107
|
+
you which. `addPluralRule(locale, rule)` overrides a language. The rules were
|
|
108
|
+
a hand-written table of 33 languages until it turned out to leave out Catalan,
|
|
109
|
+
Croatian, Serbian, Bulgarian and `nb` (they said `other` for 1) and to get
|
|
110
|
+
Hebrew's `two` and Latvian's `zero` wrong.
|
|
112
111
|
|
|
113
112
|
```typescript
|
|
114
113
|
import { getPluralCategory, addPluralRule, hasPluralRule } from '@stacksjs/i18n'
|
|
@@ -218,6 +217,6 @@ default:
|
|
|
218
217
|
- **YAML parsed with Bun.YAML** - not a third-party parser
|
|
219
218
|
- **Missing keys return the key itself** - not an error, not empty string
|
|
220
219
|
- **Pluralization uses `|` separator** - not ICU MessageFormat
|
|
221
|
-
- **CLDR rules
|
|
220
|
+
- **CLDR rules come from the runtime** - `Intl.PluralRules`, no data shipped and no table to fall out of date
|
|
222
221
|
- **HTML escaping is opt-in** - `escapeValues: true` in config
|
|
223
222
|
- **Formatting uses Intl APIs** - delegates to `Intl.DateTimeFormat`, `Intl.NumberFormat`, etc.
|
|
@@ -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) | 1 |
|
|
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) | 9 |
|
|
19
|
-
| `@stacksjs/arrays` | [`stacks-arrays`](../stacks-arrays/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/arrays/src/index.ts) |
|
|
19
|
+
| `@stacksjs/arrays` | [`stacks-arrays`](../stacks-arrays/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/arrays/src/index.ts) | 7 |
|
|
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) | 125 |
|
|
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 |
|
|
@@ -24,9 +24,9 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
|
|
|
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) | 177 |
|
|
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) | 5 |
|
|
26
26
|
| `@stacksjs/cache` | [`stacks-cache`](../stacks-cache/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/src/index.ts) | 23 |
|
|
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) |
|
|
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) | 5 |
|
|
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
|
-
| `@stacksjs/chat` | [`stacks-chat`](../stacks-chat/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/chat/src/index.ts) |
|
|
29
|
+
| `@stacksjs/chat` | [`stacks-chat`](../stacks-chat/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/chat/src/index.ts) | 6 |
|
|
30
30
|
| `@stacksjs/cli` | [`stacks-cli`](../stacks-cli/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cli/src/index.ts) | 13 |
|
|
31
31
|
| `@stacksjs/cloud` | [`stacks-cloud`](../stacks-cloud/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cloud/src/index.ts) | 10 |
|
|
32
32
|
| `@stacksjs/cms` | [`stacks-cms`](../stacks-cms/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cms/src/index.ts) | 5 |
|
|
@@ -56,20 +56,20 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
|
|
|
56
56
|
| `@stacksjs/github` | [`stacks-git`](../stacks-git/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/github/src/index.ts) | 8 |
|
|
57
57
|
| `@stacksjs/health` | [`stacks-health`](../stacks-health/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/health/src/index.ts) | 2 |
|
|
58
58
|
| `@stacksjs/http` | [`stacks-http`](../stacks-http/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/http/src/index.ts) | 2 |
|
|
59
|
-
| `@stacksjs/i18n` | [`stacks-i18n`](../stacks-i18n/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/src/index.ts) |
|
|
59
|
+
| `@stacksjs/i18n` | [`stacks-i18n`](../stacks-i18n/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/src/index.ts) | 5 |
|
|
60
60
|
| `@stacksjs/image` | [`stacks-image`](../stacks-image/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/image/src/index.ts) | 3 |
|
|
61
61
|
| `@stacksjs/lint` | [`stacks-lint`](../stacks-lint/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/lint/src/index.ts) | 3 |
|
|
62
62
|
| `@stacksjs/logging` | [`stacks-logging`](../stacks-logging/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/logging/src/index.ts) | 14 |
|
|
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) | 23 |
|
|
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) |
|
|
67
|
-
| `@stacksjs/objects` | [`stacks-objects`](../stacks-objects/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/src/index.ts) |
|
|
66
|
+
| `@stacksjs/notifications` | [`stacks-notifications`](../stacks-notifications/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/index.ts) | 17 |
|
|
67
|
+
| `@stacksjs/objects` | [`stacks-objects`](../stacks-objects/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/src/index.ts) | 5 |
|
|
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) | 75 |
|
|
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 |
|
|
71
|
-
| `@stacksjs/payments` | [`stacks-payments`](../stacks-payments/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/index.ts) |
|
|
72
|
-
| `@stacksjs/push` | [`stacks-push`](../stacks-push/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/push/src/index.ts) |
|
|
71
|
+
| `@stacksjs/payments` | [`stacks-payments`](../stacks-payments/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/index.ts) | 38 |
|
|
72
|
+
| `@stacksjs/push` | [`stacks-push`](../stacks-push/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/push/src/index.ts) | 8 |
|
|
73
73
|
| `@stacksjs/query-builder` | [`stacks-query-builder`](../stacks-query-builder/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/query-builder/src/index.ts) | 6 |
|
|
74
74
|
| `@stacksjs/queue` | [`stacks-queue`](../stacks-queue/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/queue/src/index.ts) | 49 |
|
|
75
75
|
| `@stacksjs/realtime` | [`stacks-realtime`](../stacks-realtime/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/realtime/src/index.ts) | 12 |
|
|
@@ -87,16 +87,16 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
|
|
|
87
87
|
| `@stacksjs/sms` | [`stacks-sms`](../stacks-sms/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/index.ts) | 5 |
|
|
88
88
|
| `@stacksjs/socials` | [`stacks-socials`](../stacks-socials/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/socials/src/index.ts) | 17 |
|
|
89
89
|
| `@stacksjs/storage` | [`stacks-storage`](../stacks-storage/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/storage/src/index.ts) | 47 |
|
|
90
|
-
| `@stacksjs/strings` | [`stacks-strings`](../stacks-strings/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/strings/src/index.ts) |
|
|
91
|
-
| `@stacksjs/testing` | [`stacks-testing`](../stacks-testing/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/index.ts) |
|
|
90
|
+
| `@stacksjs/strings` | [`stacks-strings`](../stacks-strings/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/strings/src/index.ts) | 10 |
|
|
91
|
+
| `@stacksjs/testing` | [`stacks-testing`](../stacks-testing/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/index.ts) | 22 |
|
|
92
92
|
| `@stacksjs/tinker` | [`stacks-repl`](../stacks-repl/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/tinker/src/index.ts) | 1 |
|
|
93
93
|
| `@stacksjs/tunnel` | [`stacks-tunnel`](../stacks-tunnel/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/tunnel/src/index.ts) | 1 |
|
|
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) |
|
|
97
|
+
| `@stacksjs/validation` | [`stacks-validation`](../stacks-validation/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/index.ts) | 19 |
|
|
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
|
-
| `@stacksjs/whois` | [`stacks-whois`](../stacks-whois/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/whois/src/index.ts) |
|
|
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
|
|
|
101
101
|
## Source surfaces
|
|
102
102
|
|
|
@@ -167,7 +167,7 @@ Declared runtime exports: `arr`.
|
|
|
167
167
|
|
|
168
168
|
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).
|
|
169
169
|
|
|
170
|
-
Evidence directory: [
|
|
170
|
+
Evidence directory: [7 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.
|
|
171
171
|
|
|
172
172
|
### @stacksjs/audio
|
|
173
173
|
|
|
@@ -245,7 +245,7 @@ Declared runtime exports: `buildCalendarFeed`, `calendarFeedHeaders`, `escapeIcs
|
|
|
245
245
|
|
|
246
246
|
Local source modules: [src/feed.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/src/feed.ts), [src/generators/google.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/src/generators/google.ts), [src/generators/ics.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/src/generators/ics.ts), [src/generators/weboutlook.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/src/generators/weboutlook.ts), [src/generators/yahoo.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/src/generators/yahoo.ts), [src/recurrence.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/src/recurrence.ts).
|
|
247
247
|
|
|
248
|
-
Evidence directory: [
|
|
248
|
+
Evidence directory: [5 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
249
249
|
|
|
250
250
|
### @stacksjs/charts
|
|
251
251
|
|
|
@@ -265,7 +265,7 @@ Declared runtime exports: `MessagingError`, `configureDiscord`, `configureSlack`
|
|
|
265
265
|
|
|
266
266
|
Local source modules: [src/drivers/discord.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/chat/src/drivers/discord.ts), [src/drivers/slack.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/chat/src/drivers/slack.ts), [src/drivers/teams.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/chat/src/drivers/teams.ts), [src/inbox/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/chat/src/inbox/index.ts).
|
|
267
267
|
|
|
268
|
-
Evidence directory: [
|
|
268
|
+
Evidence directory: [6 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/chat/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
269
269
|
|
|
270
270
|
### @stacksjs/cli
|
|
271
271
|
|
|
@@ -551,7 +551,7 @@ Declared runtime exports: `I18n`, `addTranslations`, `applyRequestLocale`, `crea
|
|
|
551
551
|
|
|
552
552
|
Local source modules: [src/bootstrap.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/src/bootstrap.ts), [src/formatter.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/src/formatter.ts), [src/loader.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/src/loader.ts), [src/locale-switch.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/src/locale-switch.ts), [src/pluralization.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/src/pluralization.ts), [src/translator.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/src/translator.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/src/types.ts).
|
|
553
553
|
|
|
554
|
-
Evidence directory: [
|
|
554
|
+
Evidence directory: [5 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/i18n/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
555
555
|
|
|
556
556
|
### @stacksjs/image
|
|
557
557
|
|
|
@@ -611,11 +611,11 @@ Evidence directory: [7 retained tests](https://github.com/stacksjs/stacks/blob/m
|
|
|
611
611
|
|
|
612
612
|
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).
|
|
613
613
|
|
|
614
|
-
Declared runtime exports: `BroadcastNotificationDriver`, `DEFAULT_SMS_OPT_OUT`, `DEFAULT_UNSUBSCRIBE_NOTICE`, `DatabaseNotificationDriver`, `NOTIFICATION_FRAME_STRINGS`, `SMS_SENDER_MAX_LENGTH`, `accountNotificationTemplates`, `advanceSequence`, `bulkSetPreferences`, `commerceNotificationTemplates`, `createNotificationTemplates`, `defineNotificationTemplates`, `emailParagraphs`, `ensureSuccessfulEmailResult`, `ensureSuccessfulNotificationResults`, `filterChannelsByPreferences`, `getNotificationPreferences`, `interpolateNotificationText`, `isQuietTime`, `listUnsubscribeHeaders`, `makeDeliveryRecord`, `nextSendableTime`, `notification`, `notificationChannel`, `notificationEmailHtml`, `notificationEmailMessage`, `notificationEmailText`, `notificationFrameStrings`, `notificationLanguageTag`, `notificationSmsBody`, `notificationTemplateVariables`, `notificationUnsubscribeLinks`, `notificationUnsubscribeToken`, `notify`, `parseNotificationUnsubscribeToken`, `recordNotificationDelivery`, `renderNotificationTemplate`, `resolveDeliveryRecipient`, `resolveNotificationLocale`, `sequenceTimeline`, `setNotificationPreference`, `smsSenderPrefix`, `subscriptionNotificationTemplates`, `unsubscribeTokenFromRequest`, `unsubscribeWithToken`, `useBroadcast`, `useChat`, `useDatabase`, `useEmail`, `useNotification`, `usePush`, `useSMS`, `wherePreferenceCategory`.
|
|
614
|
+
Declared runtime exports: `BroadcastNotificationDriver`, `DEFAULT_SMS_OPT_OUT`, `DEFAULT_UNSUBSCRIBE_NOTICE`, `DatabaseNotificationDriver`, `NOTIFICATION_FRAME_STRINGS`, `SMS_SENDER_MAX_LENGTH`, `accountNotificationTemplates`, `advanceSequence`, `bulkSetPreferences`, `commerceNotificationTemplates`, `configuredChatDriver`, `createNotificationTemplates`, `defineNotificationTemplates`, `emailParagraphs`, `ensureSuccessfulEmailResult`, `ensureSuccessfulNotificationResults`, `filterChannelsByPreferences`, `getNotificationPreferences`, `interpolateNotificationText`, `isQuietTime`, `listUnsubscribeHeaders`, `makeDeliveryRecord`, `nextSendableTime`, `notification`, `notificationChannel`, `notificationEmailHtml`, `notificationEmailMessage`, `notificationEmailText`, `notificationFrameStrings`, `notificationLanguageTag`, `notificationSmsBody`, `notificationTemplateVariables`, `notificationUnsubscribeLinks`, `notificationUnsubscribeToken`, `notify`, `parseNotificationUnsubscribeToken`, `recordNotificationDelivery`, `renderNotificationTemplate`, `resolveDeliveryRecipient`, `resolveNotificationLocale`, `sequenceTimeline`, `setNotificationPreference`, `smsSenderPrefix`, `subscriptionNotificationTemplates`, `unsubscribeTokenFromRequest`, `unsubscribeWithToken`, `useBroadcast`, `useChat`, `useDatabase`, `useEmail`, `useNotification`, `usePush`, `useSMS`, `wherePreferenceCategory`.
|
|
615
615
|
|
|
616
616
|
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/frame-strings.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/frame-strings.ts), [src/preferences.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/preferences.ts), [src/sequences.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/sequences.ts), [src/templates.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/templates.ts), [src/unsubscribe.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/unsubscribe.ts).
|
|
617
617
|
|
|
618
|
-
Evidence directory: [
|
|
618
|
+
Evidence directory: [17 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.
|
|
619
619
|
|
|
620
620
|
### @stacksjs/objects
|
|
621
621
|
|
|
@@ -625,7 +625,7 @@ Declared runtime exports: `clearUndefined`, `deepMerge`, `hasOwnProperty`, `isKe
|
|
|
625
625
|
|
|
626
626
|
Local source modules: [src/transform.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/src/transform.ts).
|
|
627
627
|
|
|
628
|
-
Evidence directory: [
|
|
628
|
+
Evidence directory: [5 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.
|
|
629
629
|
|
|
630
630
|
### @stacksjs/orm
|
|
631
631
|
|
|
@@ -661,7 +661,7 @@ Declared runtime exports: `Payment`, `freshIdempotencyKey`, `stacksIdempotencyKe
|
|
|
661
661
|
|
|
662
662
|
Local source modules: [src/billable/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/billable/index.ts), [src/checkout-outcome.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/checkout-outcome.ts), [src/connect/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/connect/index.ts), [src/direct-debit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/direct-debit.ts), [src/driver/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/driver/index.ts), [src/drivers/stripe.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/drivers/stripe.ts), [src/idempotency.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/idempotency.ts), [src/payment.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/payment.ts), [src/subscription-state.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/subscription-state.ts), [src/terminal.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/src/terminal.ts).
|
|
663
663
|
|
|
664
|
-
Evidence directory: [
|
|
664
|
+
Evidence directory: [38 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/payments/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
665
665
|
|
|
666
666
|
### @stacksjs/push
|
|
667
667
|
|
|
@@ -671,7 +671,7 @@ Declared runtime exports: `apns`, `buildVapidHeaders`, `configureAPNS`, `configu
|
|
|
671
671
|
|
|
672
672
|
Local source modules: [src/drivers/apns.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/push/src/drivers/apns.ts), [src/drivers/expo.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/push/src/drivers/expo.ts), [src/drivers/fcm.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/push/src/drivers/fcm.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/push/src/drivers/index.ts), [src/drivers/web-push.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/push/src/drivers/web-push.ts).
|
|
673
673
|
|
|
674
|
-
Evidence directory: [
|
|
674
|
+
Evidence directory: [8 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/push/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
675
675
|
|
|
676
676
|
### @stacksjs/query-builder
|
|
677
677
|
|
|
@@ -843,7 +843,7 @@ Declared runtime exports: `string`.
|
|
|
843
843
|
|
|
844
844
|
Local source modules: [src/string.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/strings/src/string.ts).
|
|
845
845
|
|
|
846
|
-
Evidence directory: [
|
|
846
|
+
Evidence directory: [10 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/strings/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
847
847
|
|
|
848
848
|
### @stacksjs/testing
|
|
849
849
|
|
|
@@ -853,7 +853,7 @@ Read the entrypoint and package export map for re-exported or type-only surfaces
|
|
|
853
853
|
|
|
854
854
|
Local source modules: [src/console.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/console.ts), [src/disposable-database.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/disposable-database.ts), [src/events.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/events.ts), [src/feature.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/feature.ts), [src/isolated.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/isolated.ts), [src/mail.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/mail.ts), [src/schema.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/schema.ts), [src/service.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/service.ts), [src/time.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/src/time.ts).
|
|
855
855
|
|
|
856
|
-
Evidence directory: [
|
|
856
|
+
Evidence directory: [22 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/testing/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
857
857
|
|
|
858
858
|
### @stacksjs/tinker
|
|
859
859
|
|
|
@@ -909,9 +909,9 @@ Guidance: [`stacks-validation`](../stacks-validation/SKILL.md). Source: [storage
|
|
|
909
909
|
|
|
910
910
|
Declared runtime exports: `applyConditionals`, `isArray`, `isBoolean`, `isFunction`, `isNull`, `isNullOrUndefined`, `isNumber`, `isObject`, `isString`, `isUndefined`, `objectWithContext`, `schema`, `shouldApplyConditional`, `withConditionals`.
|
|
911
911
|
|
|
912
|
-
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).
|
|
912
|
+
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).
|
|
913
913
|
|
|
914
|
-
Evidence directory: [
|
|
914
|
+
Evidence directory: [19 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.
|
|
915
915
|
|
|
916
916
|
### @stacksjs/video
|
|
917
917
|
|
|
@@ -931,4 +931,4 @@ Declared runtime exports: `SocksClient`, `WhoIsParser`, `batchWhois`, `findWhoIs
|
|
|
931
931
|
|
|
932
932
|
Local source modules: [src/constants.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/whois/src/constants.ts), [src/socks.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/whois/src/socks.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/whois/src/types.ts), [src/utils.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/whois/src/utils.ts).
|
|
933
933
|
|
|
934
|
-
Evidence directory: [
|
|
934
|
+
Evidence directory: [5 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/whois/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
@@ -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 | chatRecipient
|
|
39
|
+
| chat | chatRecipient for a bot token's channel; none for a webhook | `@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` |
|
|
@@ -48,7 +48,11 @@ type for an action link. The broadcast fallback is `private-user.{userId}` or
|
|
|
48
48
|
public `notifications` when no user/channel is supplied. Its delivery is
|
|
49
49
|
best-effort; a missing realtime server is not a durable notification queue.
|
|
50
50
|
Read `stacks-chat` to configure transport credentials. Notify maps body to
|
|
51
|
-
ChatMessage.content and chatRecipient to ChatMessage.to
|
|
51
|
+
ChatMessage.content and chatRecipient to ChatMessage.to, through
|
|
52
|
+
`recipient.chatDriver`, else `config.notification.chat.default` (`msTeams` is
|
|
53
|
+
Teams), else Slack. A webhook needs no chatRecipient, so `notify({}, payload,
|
|
54
|
+
['chat'])` posts to the configured webhook; a bot-token send without one fails
|
|
55
|
+
naming the field. Email, SMS, chat and
|
|
52
56
|
push structured failures and broadcast delivered:false produce failed
|
|
53
57
|
NotifyResult entries. Provider acceptance is not a final delivery receipt.
|
|
54
58
|
`ensureSuccessfulNotificationResults(results)` throws for an empty result list
|
|
@@ -210,10 +210,15 @@ Retained evidence: `core/objects/tests/transform.test.ts`.
|
|
|
210
210
|
- `objectPick` creates a NEW object -- original is not affected.
|
|
211
211
|
- `objectMap` creates a NEW object via `Object.fromEntries`.
|
|
212
212
|
- `deepMerge` does NOT deeply merge arrays -- arrays from source replace arrays in target entirely.
|
|
213
|
+
- `deepMerge` merges only plain objects key by key: a Date, Map, Set or class instance from a source is assigned as
|
|
214
|
+
is, and a string, array or number in the target is replaced by a source's object.
|
|
213
215
|
- `deepMerge` uses `isObject()` from `@stacksjs/validation` combined with `!Array.isArray()` to determine mergeability.
|
|
216
|
+
- A `"__proto__"` key is data, never a prototype. `JSON.parse` and the router's request input keep one as an own key:
|
|
217
|
+
`deepMerge` skips it and merges only into the target's own properties, and the transform helpers keep it as an own
|
|
218
|
+
key (`Object.fromEntries`), so input that carries one cannot set a prototype.
|
|
214
219
|
- `objectMap` can filter entries by returning `undefined` from the callback -- filtered via `notNullish` from `@stacksjs/utils`.
|
|
215
220
|
- `objectKeys` return type uses a template literal type to handle non-string keys.
|
|
216
221
|
- `clearUndefined` only removes `undefined` -- `null` values are preserved.
|
|
217
222
|
- `hasOwnProperty` accepts `null`/`undefined` objects without throwing.
|
|
218
223
|
- `objectPick` with `omitUndefined=false` (default) includes keys even if their values are `undefined`, as long as the key exists in the object.
|
|
219
|
-
-
|
|
224
|
+
- `@stacksjs/utils` does not re-export these. Its `merge` / `defu` is a separate merge that builds a new object.
|
|
@@ -717,8 +717,10 @@ when the app declares no portal.
|
|
|
717
717
|
|
|
718
718
|
```typescript
|
|
719
719
|
Payment.formatAmount(2999, 'usd') // '$29.99' (uses Intl.NumberFormat)
|
|
720
|
-
Payment.
|
|
721
|
-
Payment.
|
|
720
|
+
Payment.formatAmount(1000, 'jpy') // '¥1,000': JPY has no minor unit
|
|
721
|
+
Payment.minorUnitExponent('jpy') // 0 (Stripe's table on the Stripe driver; ISO 4217 otherwise)
|
|
722
|
+
Payment.toCents(29.99) // 2999, two-decimal currencies only
|
|
723
|
+
Payment.toDollars(2999) // 29.99, two-decimal currencies only
|
|
722
724
|
```
|
|
723
725
|
|
|
724
726
|
### Client-Side Billing (useBillable)
|
|
@@ -918,7 +920,7 @@ The instance methods are exactly the keys of `createBillableMethods` in
|
|
|
918
920
|
its original raw payload before applying domain events.
|
|
919
921
|
- Stripe API keys MUST be in `.env` as `STRIPE_SECRET_KEY` and `STRIPE_PUBLISHABLE_KEY` -- never hardcode them in config files
|
|
920
922
|
- The Stripe client is created lazily, on first use: importing `@stacksjs/payments` without `STRIPE_SECRET_KEY` (or without the opt-in `stripe` package) is fine, and the first Stripe call throws naming what is missing
|
|
921
|
-
- All amounts are in cents
|
|
923
|
+
- All amounts are in the currency's minor unit: cents for USD, whole yen for JPY (Stripe's zero-decimal list), hundredths for ISK and UGX (Stripe's special cases). `toCents()` and `toDollars()` are for two-decimal currencies; `minorUnitExponent(currency)` says how many decimals an amount has, and `formatAmount()` uses it
|
|
922
924
|
- `Payment.charge()` (Stripe driver) creates AND confirms the PaymentIntent in one step
|
|
923
925
|
- `subscribe()` resolves the price via `lookup_key`, not a direct Stripe price ID
|
|
924
926
|
- `Payment.removePaymentMethod()` and the billable instance method `removePaymentMethod()` both take the provider's id (string); `managePaymentMethod.deletePaymentMethod()` takes the local row id (number)
|
|
@@ -42,8 +42,9 @@ Routes to the appropriate driver based on `options.driver` (default: `'expo'`).
|
|
|
42
42
|
|
|
43
43
|
- For **Expo**: Passes the notification directly to `expo.send()` with all fields mapped.
|
|
44
44
|
- For **FCM** with single token: Calls `fcm.send()` with notification mapped to `{ to, notification: { title, body }, data, priority }`.
|
|
45
|
-
- For **FCM** with multiple tokens: Calls `fcm.sendMulticast()` and returns an aggregated result with success count.
|
|
46
|
-
- FCM priority mapping: `'default'` is converted to `'normal'`; `'high'` and `'normal'` pass through.
|
|
45
|
+
- For **FCM** with multiple tokens: Calls `fcm.sendMulticast()` and returns an aggregated result with success count, and `data: { results }`, one per token in order, so a dead token (404 `UNREGISTERED`) can be told from a transient failure.
|
|
46
|
+
- FCM priority mapping: `'default'` is converted to `'normal'`; `'high'` and `'normal'` pass through. Unset means `high`, on Android and iOS alike.
|
|
47
|
+
- `data` is the same for every driver. FCM takes only strings, so a non-string value is sent as its JSON text (`{ orderId: 42 }` arrives as `"42"`).
|
|
47
48
|
|
|
48
49
|
## PushNotification Interface (index.ts)
|
|
49
50
|
|
|
@@ -128,12 +129,16 @@ async function send(message: ExpoPushMessage): Promise<PushResult>
|
|
|
128
129
|
```
|
|
129
130
|
|
|
130
131
|
- Endpoint: `https://exp.host/--/api/v2/push/send`
|
|
131
|
-
- Validates tokens with `isExpoPushToken()` before sending
|
|
132
|
-
|
|
132
|
+
- Validates tokens with `isExpoPushToken()` before sending; an invalid token
|
|
133
|
+
is warned about via `log.warn()`, not sent, and counted as a failure
|
|
133
134
|
- Returns early with failure if no valid tokens remain
|
|
134
135
|
- Creates one payload entry per token (fans out `to` array into individual messages)
|
|
136
|
+
- Sends them in requests of at most 100, one after another: Expo refuses a
|
|
137
|
+
larger request outright, so a send to 101 devices used to reach none. A
|
|
138
|
+
refused request fails its own messages and the rest still go
|
|
135
139
|
- Default `sound: 'default'`, default `priority: 'high'`
|
|
136
|
-
- Returns `PushResult` with `data: { tickets }
|
|
140
|
+
- Returns `PushResult` with `data: { tickets }`: one ticket per token in `to`,
|
|
141
|
+
in the same order, so each ticket can be matched to its token
|
|
137
142
|
- `success` is `true` only if zero tickets have `status: 'error'`
|
|
138
143
|
- `messageId` is the first ticket's `id` (if any)
|
|
139
144
|
|
|
@@ -147,13 +152,13 @@ Validates token format: matches `Expo(nent)?PushToken[...]` pattern OR alphanume
|
|
|
147
152
|
```typescript
|
|
148
153
|
async function getReceipts(ticketIds: string[]): Promise<Record<string, ExpoPushReceipt>>
|
|
149
154
|
```
|
|
150
|
-
Endpoint: `https://exp.host/--/api/v2/push/getReceipts`. Checks delivery status for previously sent notifications.
|
|
155
|
+
Endpoint: `https://exp.host/--/api/v2/push/getReceipts`. Checks delivery status for previously sent notifications, asking 1000 IDs per request (Expo's limit) and merging the answers.
|
|
151
156
|
|
|
152
157
|
#### sendBatch()
|
|
153
158
|
```typescript
|
|
154
159
|
async function sendBatch(messages: ExpoPushMessage[], chunkSize?: number): Promise<PushResult[]>
|
|
155
160
|
```
|
|
156
|
-
Sends
|
|
161
|
+
Sends several messages, `chunkSize` (default 100) at a time: chunks sequentially, messages within a chunk in parallel via `Promise.all`. Each message's own tokens are split into requests of 100 by `send()`.
|
|
157
162
|
|
|
158
163
|
### Exports
|
|
159
164
|
- `send`, `sendBatch`, `getReceipts`, `isExpoPushToken` functions
|
|
@@ -192,7 +197,7 @@ interface FCMMessage {
|
|
|
192
197
|
clickAction?: string
|
|
193
198
|
tag?: string
|
|
194
199
|
}
|
|
195
|
-
data?: Record<string,
|
|
200
|
+
data?: Record<string, unknown> // sent as strings: a non-string value as its JSON text
|
|
196
201
|
priority?: 'high' | 'normal'
|
|
197
202
|
ttl?: number
|
|
198
203
|
collapseKey?: string
|
|
@@ -221,7 +226,7 @@ endpoint it used to fall back to is gone.
|
|
|
221
226
|
1. Generates JWT with RS256 signing using the service account private key
|
|
222
227
|
2. Exchanges JWT for OAuth2 access token at `https://oauth2.googleapis.com/token`
|
|
223
228
|
3. Sends to `https://fcm.googleapis.com/v1/projects/{projectId}/messages:send`
|
|
224
|
-
4. Includes Android priority settings and APNS headers (`apns-priority: 10` for high, `5` for normal)
|
|
229
|
+
4. Includes Android priority settings and APNS headers (`apns-priority: 10` for high, `5` for normal); an unset priority is high on both
|
|
225
230
|
5. Supports `to` (token), `topic`, or `condition` targeting
|
|
226
231
|
|
|
227
232
|
**JWT generation details:**
|
|
@@ -100,9 +100,9 @@ interface TitleCaseOptions {
|
|
|
100
100
|
locale?: string | string[]
|
|
101
101
|
sentenceCase?: boolean // only capitalize first word + after terminators
|
|
102
102
|
sentenceTerminators?: Set<string> // default: '.', '!', '?'
|
|
103
|
-
titleTerminators?: Set<string> // default: '.', '!', '?', ':', '"', "'",
|
|
103
|
+
titleTerminators?: Set<string> // default: '.', '!', '?', ':', '"', "'", and a closing curly quote (U+201D)
|
|
104
104
|
smallWords?: Set<string> // words not capitalized (see SMALL_WORDS)
|
|
105
|
-
wordSeparators?: Set<string> // default:
|
|
105
|
+
wordSeparators?: Set<string> // default: em dash (U+2014), en dash (U+2013), '-', horizontal bar (U+2015), '/'
|
|
106
106
|
}
|
|
107
107
|
```
|
|
108
108
|
|
|
@@ -196,6 +196,7 @@ Extensive built-in character map covering:
|
|
|
196
196
|
- Vietnamese diacritics
|
|
197
197
|
- Currency symbols ($, EUR, GBP, YEN, etc.)
|
|
198
198
|
- Special characters (trademark, copyright, etc.)
|
|
199
|
+
- An en dash (U+2013) becomes a hyphen and so separates words; an em dash is still removed (#3112)
|
|
199
200
|
|
|
200
201
|
### Locale Support
|
|
201
202
|
Locale-specific character mappings for: `bg`, `de`, `es`, `fr`, `pt`, `uk`, `vi`, `da`, `nb`, `it`, `nl`, `sv`.
|
|
@@ -241,7 +242,7 @@ Native TypeScript implementations (no external validator dependency). All accept
|
|
|
241
242
|
### Core Validators
|
|
242
243
|
```typescript
|
|
243
244
|
isEmail('user@example.com') // true -- regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
244
|
-
isStrongPassword('Abc123!@#') // true -- min 8 chars, upper, lower, digit, symbol
|
|
245
|
+
isStrongPassword('Abc123!@#') // true -- min 8 chars, upper, lower, digit, symbol (any non-letter, non-number)
|
|
245
246
|
isAlphanumeric('abc123') // true -- /^[a-zA-Z0-9]+$/
|
|
246
247
|
isAlpha('abcdef') // true -- /^[a-zA-Z]+$/
|
|
247
248
|
isNumeric('123') // true -- /^[0-9]+$/ (whole numbers only)
|
|
@@ -256,16 +257,16 @@ isBase64('SGVsbG8=') // true -- btoa(atob(x)) === x
|
|
|
256
257
|
isBase32('MFZWI===') // true -- /^[A-Z2-7]+=*$/
|
|
257
258
|
isAscii('hello') // true -- /^[\x00-\x7F]*$/
|
|
258
259
|
isJSON('{"key":"value"}') // true -- JSON.parse succeeds
|
|
259
|
-
isHSL('hsl(0, 100%, 50%)') // true
|
|
260
|
+
isHSL('hsl(0, 100%, 50%)') // true -- comma syntax: hsl(hue, saturation%, lightness%)
|
|
260
261
|
isMimeType('text/html') // true -- /^[a-z]+\/[a-z0-9\-+.]+$/i
|
|
261
|
-
isDataURI('data:text/plain;base64,...') // true
|
|
262
|
+
isDataURI('data:text/plain;base64,...') // true -- RFC 2397: the media type and ;base64 are both optional
|
|
262
263
|
isJWT('eyJhbGciOiJIUzI1NiJ9.e30.c2ln') // true -- shape check only, not signature verification
|
|
263
264
|
```
|
|
264
265
|
|
|
265
266
|
### Network Validators
|
|
266
267
|
```typescript
|
|
267
268
|
isIP('192.168.1.1') // true -- IPv4 and IPv6
|
|
268
|
-
isIPRange('192.168.1.0/24') // true -- CIDR notation (IPv4: 0-32, IPv6: 0-128)
|
|
269
|
+
isIPRange('192.168.1.0/24') // true -- CIDR notation, a whole-number prefix (IPv4: 0-32, IPv6: 0-128)
|
|
269
270
|
isMACAddress('00:11:22:33:44:55') // true -- colon or hyphen separated
|
|
270
271
|
isFQDN('example.com') // true -- fully qualified domain name
|
|
271
272
|
```
|
|
@@ -282,7 +283,7 @@ isISSN('0378-5955') // true -- mod 11 check
|
|
|
282
283
|
|
|
283
284
|
### Geographic Validators
|
|
284
285
|
```typescript
|
|
285
|
-
isLatLong('40.7128,-74.0060') // true -- lat: -90..90, long: -180..180
|
|
286
|
+
isLatLong('40.7128,-74.0060') // true -- plain decimal numbers; lat: -90..90, long: -180..180
|
|
286
287
|
isLatitude('40.7128') // true
|
|
287
288
|
isLongitude('-74.0060') // true
|
|
288
289
|
isPostalCode('12345') // true -- US, UK, Canada, generic formats
|
|
@@ -291,8 +292,8 @@ isPostalCode('12345') // true -- US, UK, Canada, generic for
|
|
|
291
292
|
### Phone, Identity, Date Validators
|
|
292
293
|
```typescript
|
|
293
294
|
isMobilePhone('+1234567890') // true -- international format, min 7 digits
|
|
294
|
-
isUUID('550e8400-e29b-41d4-a716-446655440000') // true -- full value required
|
|
295
|
-
isISO8601('2024-01-01T00:00:00Z') // true --
|
|
295
|
+
isUUID('550e8400-e29b-41d4-a716-446655440000') // true -- versions 1-8 (RFC 9562), nil and max; full value required
|
|
296
|
+
isISO8601('2024-01-01T00:00:00Z') // true -- the day must exist in its month; any number of fraction digits
|
|
296
297
|
isIdentityCard('AB123456') // true -- generic 5-20 alphanumeric
|
|
297
298
|
isISRC('USRC17607839') // true
|
|
298
299
|
isISO31661Alpha2('US') // true
|
|
@@ -404,7 +405,12 @@ interface PluralizeOptions { count?, inclusive? }
|
|
|
404
405
|
```
|
|
405
406
|
|
|
406
407
|
## Gotchas
|
|
407
|
-
- Case conversion uses Unicode-aware word boundary detection (`\p{Ll}`, `\p{Lu}`) -- handles international characters
|
|
408
|
+
- Case conversion uses Unicode-aware word boundary detection (`\p{Ll}`, `\p{Lu}`) -- handles international characters.
|
|
409
|
+
Combining marks stay with their letter and digits from any script are kept, so `kebabCase('नमस्ते दुनिया')`
|
|
410
|
+
is `'नमस्ते-दुनिया'`
|
|
411
|
+
- `truncate(str, length, end)` never returns more than `length` characters, `end` included; with a `length`
|
|
412
|
+
shorter than `end` it returns as much of `end` as fits. It and `mask` count code points, so an emoji is one
|
|
413
|
+
character and is never cut in half
|
|
408
414
|
- `slug()` calls `slugify()` under the hood but defaults to `{ lower: true, strict: true }`; `slugify()` defaults to `{ lower: false, strict: false }`
|
|
409
415
|
- `pascalCase` and `camelCase` accept `PascalCaseOptions` with `mergeAmbiguousCharacters` to control number-prefixed word handling
|
|
410
416
|
- All case functions support `prefixCharacters`/`suffixCharacters` to preserve leading/trailing non-word characters
|
|
@@ -278,7 +278,11 @@ expect(pageAuditClean(report)).toBe(true) // no duplicate /api/ requests, failed
|
|
|
278
278
|
pattern holds with no listener stays held (a hanging request on purpose).
|
|
279
279
|
`matchesUrlPattern(pattern, url)` and `pausedRequestMatches(pattern, paused)` are Chrome's
|
|
280
280
|
matching rules (`*`, `?`, `\` escape; stage and resource type).
|
|
281
|
-
- `settle(cdp, { quietMs, timeoutMs })` resolves false (not throws) when a page never goes quiet.
|
|
281
|
+
- `settle(cdp, { quietMs, timeoutMs, includeSpeculative? })` resolves false (not throws) when a page never goes quiet.
|
|
282
|
+
By default all finite requests count. `includeSpeculative: false` uses `cdp.foregroundInFlight`,
|
|
283
|
+
excluding only Chrome `Prefetch` requests and explicit `Purpose` / `Sec-Purpose: prefetch`
|
|
284
|
+
declarations. The raw `inFlight` map and all HTTP, duplicate API, console and runtime audit
|
|
285
|
+
failures remain observable. `navigate` and `auditPage` accept the same option.
|
|
282
286
|
- `click(cdp, selector | { selector, text }, { timeoutMs? })` scrolls the unique target into view,
|
|
283
287
|
waits for layout, enabled state and an unobscured hit test, then uses Chrome's real mouse input.
|
|
284
288
|
Use it for journey controls instead of local coordinate-click helpers or DOM-dispatched clicks.
|
|
@@ -45,7 +45,7 @@ export { clamp, notNullish, retry, useOnline } from '@stacksjs/browser'
|
|
|
45
45
|
|
|
46
46
|
## Deep Merge (`merge.ts`)
|
|
47
47
|
|
|
48
|
-
Replacement for `defu`. Arrays are concatenated, objects are recursively merged, special objects (Date, RegExp, Error, Promise, Map, Set, WeakMap, WeakSet, ArrayBuffer views) are NOT deeply merged.
|
|
48
|
+
Replacement for `defu`. Arrays are concatenated, objects are recursively merged, special objects (Date, RegExp, Error, Promise, Map, Set, WeakMap, WeakSet, ArrayBuffer views) are NOT deeply merged. A `"__proto__"` key is skipped, as defu skips it, so a parsed request body cannot set the result's prototype.
|
|
49
49
|
|
|
50
50
|
```typescript
|
|
51
51
|
import { merge, mergeDefaults, createMerger, defu } from '@stacksjs/utils'
|
|
@@ -249,6 +249,17 @@ export default defineModel({
|
|
|
249
249
|
})
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
+
### Custom messages
|
|
253
|
+
|
|
254
|
+
A rule's `message` is one string for any failure of the field, or an object keyed
|
|
255
|
+
by rule name: `min`, `max`, `email`, `enum`, `required` and so on. `messageKeys(rule)`
|
|
256
|
+
lists the keys a validator takes; `timestamp()` and `date()` take `timestamp` and
|
|
257
|
+
`date`. A key named after the field covers failures no rule key matched, unless the
|
|
258
|
+
validator has a rule of that name (`email: { email: ... }` is the email rule's).
|
|
259
|
+
Action `validations`, `request.validate()`, `validateField()` and model writes all
|
|
260
|
+
apply them through `fieldErrorMessages()` from `@stacksjs/validation/messages`, after
|
|
261
|
+
validation; none of them sets ts-validation's process-wide message provider.
|
|
262
|
+
|
|
252
263
|
## Model Validation (`validator.ts`)
|
|
253
264
|
|
|
254
265
|
### `validateField(modelFile, params)`
|
|
@@ -402,6 +413,8 @@ they are. Inferred object shapes are native, not a hand-written request interfac
|
|
|
402
413
|
schema.file().required().image().mimeTypes([...]).maxBytes(n).minBytes(n).
|
|
403
414
|
extensions([...]).custom(fn) provides FileLike validation. Client MIME/extension
|
|
404
415
|
checks do not prove the bytes; use stacks-storage's MIME verification where needed.
|
|
416
|
+
An upload is a class instance (the router's `UploadedFile`); a plain object, which
|
|
417
|
+
is what a JSON body gives for the same field name, is refused.
|
|
405
418
|
|
|
406
419
|
registerRule/getCustomRule, unique(table,column,exceptId?), exists(table,column)
|
|
407
420
|
and validateFieldAsync support application async validation. Current unique/exists
|
|
@@ -437,9 +450,11 @@ validateModelInput(MyModel, { title })
|
|
|
437
450
|
|
|
438
451
|
Failures throw `InputValidationError` with `status: 422`. JSON must be an object;
|
|
439
452
|
text never coerces objects or silently truncates; numeric input rejects booleans,
|
|
440
|
-
non-finite values
|
|
453
|
+
non-finite values, unsafe whole numbers and anything but decimal notation (`0x10`
|
|
454
|
+
is not 16), and an identifier is decimal digits only. Optional text/number input returns
|
|
441
455
|
null, and absent boolean input returns undefined. Boolean input accepts JSON
|
|
442
|
-
booleans and the explicit form values `true`, `false`, `1`, `0` and their strings
|
|
456
|
+
booleans and the explicit form values `true`, `false`, `1`, `0` and their strings,
|
|
457
|
+
and a checkbox's `on` and `off`.
|
|
443
458
|
|
|
444
459
|
`validateModelInput` checks only supplied fields using `getDefinition()` rules.
|
|
445
460
|
The action still selects writable fields, requires creation fields and validates
|
|
@@ -449,9 +464,10 @@ Root and narrow input imports share the same error constructor.
|
|
|
449
464
|
## Reusable boundary and stored-value rules
|
|
450
465
|
|
|
451
466
|
`parseTextInput(value, field, required?, maxLength?)` rejects overlong text
|
|
452
|
-
rather than truncating it
|
|
467
|
+
rather than truncating it; `maxLength` counts characters, as a database column
|
|
468
|
+
does, not UTF-16 units. `parseEnumInput(value, field, choices)` requires
|
|
453
469
|
exact string membership; it never stringifies an object or boolean.
|
|
454
|
-
`storedBoolean(value, fallback)` decodes SQL/form 0/1 and
|
|
470
|
+
`storedBoolean(value, fallback)` decodes SQL/form 0/1, false/true and on/off; the
|
|
455
471
|
caller explicitly chooses the fallback for missing or corrupt stored data.
|
|
456
472
|
Partial model validation accepts attribute names and their snake-case column
|
|
457
473
|
spelling, including camel-case definitions.
|
|
@@ -133,11 +133,12 @@ When `outputData` is `null`, `parseData()` uses this default template:
|
|
|
133
133
|
}
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
The parser
|
|
137
|
-
-
|
|
138
|
-
-
|
|
136
|
+
The parser reads raw WHOIS text a line at a time:
|
|
137
|
+
- A line's field is everything before its first colon; it counts when it is a key in `outputData`
|
|
138
|
+
- The value is everything after that colon, as written, so `2024-08-14T07:01:34Z` and `https://...` keep their colons
|
|
139
|
+
- A field with nothing after its colon takes the next non-empty line as its value (registries that print `Registrar:` with the name below)
|
|
139
140
|
- Array-typed fields (like `Domain Status`) accumulate multiple values
|
|
140
|
-
- Parsing stops at `"Record maintained by"` or `">>>"`
|
|
141
|
+
- Parsing stops at a line starting with `"Record maintained by"` or `">>>"`
|
|
141
142
|
|
|
142
143
|
Custom parse templates: pass an object with the field names you want extracted. Use empty string `''` for single values, use `[]` for fields with multiple values.
|
|
143
144
|
|
|
@@ -59,7 +59,7 @@ function optionalId(value: unknown): number | null {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function booleanValue(value: unknown): boolean {
|
|
62
|
-
return storedBoolean(value
|
|
62
|
+
return storedBoolean(value, false)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export function invalidPostContent(payload: PostPayload): string | null {
|
|
@@ -216,7 +216,7 @@ export default defineModel({
|
|
|
216
216
|
validation: {
|
|
217
217
|
rule: schema.enum(['published', 'draft', 'archived']),
|
|
218
218
|
message: {
|
|
219
|
-
|
|
219
|
+
enum: 'Status must be either published, draft, or archived',
|
|
220
220
|
},
|
|
221
221
|
},
|
|
222
222
|
factory: faker => faker.helpers.arrayElement(['published', 'draft', 'archived']),
|
package/app/Models/Error.ts
CHANGED
package/app/Models/Log.ts
CHANGED
|
@@ -53,7 +53,7 @@ export default defineModel({
|
|
|
53
53
|
// (stacksjs/stacks#2996).
|
|
54
54
|
rule: schema.unix().required(),
|
|
55
55
|
message: {
|
|
56
|
-
|
|
56
|
+
unix: 'Timestamp must be a valid Unix timestamp',
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
59
|
factory: faker => faker.date.recent().getTime(),
|
|
@@ -50,7 +50,7 @@ export default defineModel({
|
|
|
50
50
|
validation: {
|
|
51
51
|
rule: schema.string().required().max(50),
|
|
52
52
|
message: {
|
|
53
|
-
|
|
53
|
+
string: 'brand must be a string',
|
|
54
54
|
required: 'brand is required',
|
|
55
55
|
},
|
|
56
56
|
},
|
|
@@ -63,7 +63,7 @@ export default defineModel({
|
|
|
63
63
|
validation: {
|
|
64
64
|
rule: schema.number().required(),
|
|
65
65
|
message: {
|
|
66
|
-
|
|
66
|
+
number: 'exp_month must be a number',
|
|
67
67
|
required: 'exp_month is required',
|
|
68
68
|
},
|
|
69
69
|
},
|
|
@@ -76,7 +76,7 @@ export default defineModel({
|
|
|
76
76
|
validation: {
|
|
77
77
|
rule: schema.number().required(),
|
|
78
78
|
message: {
|
|
79
|
-
|
|
79
|
+
number: 'exp_year must be a number',
|
|
80
80
|
required: 'exp_year is required',
|
|
81
81
|
},
|
|
82
82
|
},
|
|
@@ -33,7 +33,7 @@ export default defineModel({
|
|
|
33
33
|
validation: {
|
|
34
34
|
rule: schema.enum(['disconnection', 'error', 'success']),
|
|
35
35
|
message: {
|
|
36
|
-
|
|
36
|
+
enum: 'Type must be one of: disconnection, error, success',
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
factory: faker => faker.helpers.arrayElement(['success', 'success', 'success', 'disconnection', 'error']),
|
|
@@ -77,7 +77,7 @@ export default defineModel({
|
|
|
77
77
|
// and MySQL (stacksjs/stacks#2996).
|
|
78
78
|
rule: schema.unix(),
|
|
79
79
|
message: {
|
|
80
|
-
|
|
80
|
+
unix: 'Time must be a valid timestamp',
|
|
81
81
|
},
|
|
82
82
|
},
|
|
83
83
|
factory: faker => faker.date.recent().getTime(),
|
package/ide/vscode/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"publisher": "Stacks",
|
|
3
3
|
"name": "vscode-stacks",
|
|
4
4
|
"displayName": "Stacks",
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.423",
|
|
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.
|
|
5
|
+
"version": "0.75.423",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@iconify-json/f7": "^1.2.2",
|
|
47
47
|
"@iconify-json/hugeicons": "^1.2.35",
|
|
48
|
-
"@stacksjs/mobile": "0.75.
|
|
49
|
-
"@stacksjs/sanitizer": "0.2.
|
|
50
|
-
"@stacksjs/stx": "0.2.
|
|
48
|
+
"@stacksjs/mobile": "0.75.423",
|
|
49
|
+
"@stacksjs/sanitizer": "0.2.784",
|
|
50
|
+
"@stacksjs/stx": "0.2.784",
|
|
51
51
|
"@xterm/addon-fit": "^0.11.0",
|
|
52
52
|
"@xterm/xterm": "^6.0.0",
|
|
53
53
|
"ts-qr-codes": "^0.1.8"
|