@stacksjs/defaults 0.75.422 → 0.75.431
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-alias/SKILL.md +8 -0
- package/ai/skills/stacks-arrays/SKILL.md +3 -1
- package/ai/skills/stacks-build/SKILL.md +3 -0
- 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-database/SKILL.md +10 -0
- package/ai/skills/stacks-desktop/SKILL.md +7 -0
- package/ai/skills/stacks-events/SKILL.md +6 -2
- package/ai/skills/stacks-i18n/SKILL.md +9 -10
- package/ai/skills/stacks-native/CATALOG.md +70 -62
- package/ai/skills/stacks-notifications/SKILL.md +11 -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-queue/SKILL.md +7 -0
- package/ai/skills/stacks-slug/SKILL.md +7 -1
- package/ai/skills/stacks-strings/SKILL.md +19 -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/Actions/InstallAction.ts +3 -3
- 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 +2 -2
- package/resources/views/cms/blocks/form.stx +31 -36
- package/routes/forms.ts +14 -5
|
@@ -41,6 +41,14 @@ into another package's unpublished types is the problem. Avoid the former rule
|
|
|
41
41
|
that all relative imports are forbidden, since framework code itself uses them
|
|
42
42
|
to keep internal modules and cycle boundaries explicit.
|
|
43
43
|
|
|
44
|
+
The five source tsconfigs use manifest-derived package names and public exports,
|
|
45
|
+
not namespace-to-directory wildcards. Run
|
|
46
|
+
`bun .github/scripts/generate-workspace-aliases.ts` after changing a workspace
|
|
47
|
+
manifest and use `--check` to verify freshness. The owning helpers and source
|
|
48
|
+
exceptions are documented in `core/alias/README.md`. External packages such as
|
|
49
|
+
STX's `@stacksjs/desktop` keep normal package resolution; `core/desktop` owns
|
|
50
|
+
`@stacksjs/desktop-build`.
|
|
51
|
+
|
|
44
52
|
## Verification
|
|
45
53
|
|
|
46
54
|
Source: `core/alias/src/index.ts`, active tsconfig.json and the build/runtime
|
|
@@ -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
|
|
@@ -86,6 +86,9 @@ buddy build --server --verbose --project [name]
|
|
|
86
86
|
|
|
87
87
|
## Standard Build Pattern
|
|
88
88
|
|
|
89
|
+
For workspace dependency ordering, failure propagation and umbrella builds, read
|
|
90
|
+
[the build package's workspace contract](../../../../core/build/README.md#workspace-builds).
|
|
91
|
+
|
|
89
92
|
For a library barrel or a package with public subpaths, preserve the module
|
|
90
93
|
graph with the shared transpiler. Identifier-minifying bundles have produced
|
|
91
94
|
invalid re-export bindings on supported Bun versions. Separate bundles also
|
|
@@ -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
|
|
@@ -384,3 +384,13 @@ canonical numeric string value, so padded numeric inputs cannot reverse lock ord
|
|
|
384
384
|
transaction handle and choose ownership filters; reads, writes and validation
|
|
385
385
|
remain on that handle. This prevents opposite input order from creating a
|
|
386
386
|
lock-order cycle during a transfer, merge or multi-item stock reservation.
|
|
387
|
+
|
|
388
|
+
## Row leases for external operations
|
|
389
|
+
|
|
390
|
+
`claimRowLease(table, ownerScope, { token, heartbeat }, ttlMs)` from
|
|
391
|
+
`@stacksjs/database/runtime` claims model-owned nullable token and heartbeat
|
|
392
|
+
columns atomically. It returns null when busy, or a lease with `renew()`,
|
|
393
|
+
`release()`, `assertOwned()` and `commit(work)`. Renew periodically during I/O.
|
|
394
|
+
Keep network work outside `commit`; commit locks and revalidates the token before
|
|
395
|
+
writing one bounded batch. An expired worker cannot commit or release a successor.
|
|
396
|
+
A lease's unique owner scope must include the resource binding where applicable.
|
|
@@ -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'
|
|
@@ -26,10 +26,14 @@ Use the native event bus for process-local application and model events. Use
|
|
|
26
26
|
downstream work determines the response.
|
|
27
27
|
|
|
28
28
|
Payloadless events are supported by the emitter. Exact handlers receive the
|
|
29
|
-
payload; wildcard and glob handlers receive `(eventName, payload)`.
|
|
29
|
+
payload; wildcard and glob handlers receive `(eventName, payload)`. In a glob,
|
|
30
|
+
`*` is any run of characters and `.` any one character (`*.created` matches
|
|
31
|
+
`user:created`); every other character is literal.
|
|
30
32
|
|
|
31
33
|
`emitter.on(name, handler, { priority })` sorts higher priorities first
|
|
32
|
-
within each handler bucket.
|
|
34
|
+
within each handler bucket. A priority belongs to that one registration, so
|
|
35
|
+
one handler can run first for one event and last for another. Equal priority
|
|
36
|
+
preserves registration order.
|
|
33
37
|
Exact handlers run before glob buckets, then `*` handlers. Snapshotting keeps
|
|
34
38
|
subscription changes during a dispatch from corrupting iteration.
|
|
35
39
|
|
|
@@ -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.
|