@stacksjs/defaults 0.75.85 → 0.75.87
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-ai/SKILL.md +7 -2
- package/ai/skills/stacks-api/SKILL.md +6 -0
- package/ai/skills/stacks-config/SKILL.md +18 -1
- package/ai/skills/stacks-payments/SKILL.md +3 -2
- package/bootstrap.ts +10 -1
- package/ide/vscode/package.json +1 -1
- package/package.json +3 -3
- package/resources/components/NativeComponents.test.ts +6 -0
- package/resources/components/NativeTabItem.stx +4 -1
- package/routes/paddle.ts +21 -0
- package/routes/payments.ts +3 -1
|
@@ -69,7 +69,8 @@ const result = await bedrock.chat([{ role: 'user', content: 'Hello' }], { system
|
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
- Every request goes through Bedrock's Converse API, so one shape serves Nova,
|
|
72
|
-
Claude and Llama; the model is configuration.
|
|
72
|
+
Claude and Llama; the model is configuration. `bedrock.streamChat()` and
|
|
73
|
+
`streamChatEvents()` stream through `converse-stream` (ts-cloud >= 0.16.44).
|
|
73
74
|
- Credentials come from the AWS credential chain, never config. Region:
|
|
74
75
|
`drivers.bedrock.region`, `AWS_REGION`, `AWS_DEFAULT_REGION`, us-east-1.
|
|
75
76
|
- Default model `amazon.nova-lite-v1:0`: on demand, no profile, no agreement.
|
|
@@ -96,7 +97,11 @@ messages.push(assistantTurn(result), toolResultsTurn(
|
|
|
96
97
|
- The `responseFormat` structured-output tool is internal: its JSON is
|
|
97
98
|
`content`, never a tool call.
|
|
98
99
|
- Ollama refuses `toolChoice: 'required'` / `{ name }`; it cannot force a call.
|
|
99
|
-
- `streamChat()`
|
|
100
|
+
- Streaming takes the same `tools`. `streamChat()` yields text and returns the
|
|
101
|
+
full `AIResult` (with `toolCalls`) when it ends; `streamChatEvents()` yields
|
|
102
|
+
`{ type: 'text' }`, `{ type: 'tool_call', call }` (whole, once its arguments
|
|
103
|
+
have streamed) and a final `{ type: 'done', result }`. Every driver,
|
|
104
|
+
Bedrock included.
|
|
100
105
|
|
|
101
106
|
## Provider-Neutral Client
|
|
102
107
|
|
|
@@ -799,6 +799,12 @@ Models with `traits.useApi` automatically get CRUD routes via ORM actions:
|
|
|
799
799
|
| PUT/PATCH | `/api/{model}/{id}` | Generated update | 200 |
|
|
800
800
|
| DELETE | `/api/{model}/{id}` | Generated destroy | 204 |
|
|
801
801
|
|
|
802
|
+
Which `useApi` models get these routes is `security.api.models` in
|
|
803
|
+
`config/security.ts` (`'all'` by default, `'own'`, `'none'`, or a list of model
|
|
804
|
+
names that may include `'own'`), overridden by `STACKS_MODEL_APIS`. `'all'`
|
|
805
|
+
includes the ~80 framework models, so an app that does not use commerce or
|
|
806
|
+
CMS should set `'own'`. The OpenAPI schemas follow the same selection.
|
|
807
|
+
|
|
802
808
|
Generated store and update handlers accept declared `fillable` attributes and
|
|
803
809
|
the foreign-key attributes derived from `belongsTo`. For example,
|
|
804
810
|
`belongsTo: ['Product', 'Customer']` accepts `productId` / `product_id` and
|
|
@@ -60,6 +60,23 @@ they name: `defineEvents` and `defineListener` from `@stacksjs/events`,
|
|
|
60
60
|
- `determineAppEnv(): 'dev' | 'stage' | 'prod' | string`
|
|
61
61
|
- `localUrl(): string` — local development URL
|
|
62
62
|
|
|
63
|
+
## Feature Flags
|
|
64
|
+
`feature(name)` from `@stacksjs/config` reads `enabled` on `config/<name>.ts`.
|
|
65
|
+
It accepts the installable bundles (`dashboard`, `commerce`, `cms`, `forms`,
|
|
66
|
+
`marketing`, `monitoring`, `realtime`, `queue`) plus two config gates: `auth`
|
|
67
|
+
(whether the ORM loads Team, Referral, Subscriber, Site and the other account
|
|
68
|
+
models; it does not mount the auth routes, `STACKS_DEFAULT_ROUTES` does) and
|
|
69
|
+
`email` (the email webhook route bundle). `./buddy features` lists both groups.
|
|
70
|
+
|
|
71
|
+
Any other name is a compile error, so a typo cannot silently read `false`. An
|
|
72
|
+
app's own flags are declared first:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
declare module '@stacksjs/config' {
|
|
76
|
+
interface AppFeatureFlags { 'new-checkout': true }
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
63
80
|
## All 44 Config Files
|
|
64
81
|
|
|
65
82
|
### Core App
|
|
@@ -104,7 +121,7 @@ they name: `defineEvents` and `defineListener` from `@stacksjs/events`,
|
|
|
104
121
|
### Security
|
|
105
122
|
| File | Type | Key Settings |
|
|
106
123
|
|------|------|-------------|
|
|
107
|
-
| `security.ts` | SecurityConfig | firewall (enabled, countryCodes, rateLimitPerMinute
|
|
124
|
+
| `security.ts` | SecurityConfig | api.rowScoping('deny'), api.models('all' / 'own' / 'none' / names; `STACKS_MODEL_APIS` overrides), firewall (enabled, countryCodes, rateLimitPerMinute) |
|
|
108
125
|
| `hashing.ts` | HashingConfig | driver('bcrypt'), bcrypt.rounds(12), argon2 config |
|
|
109
126
|
|
|
110
127
|
### Features
|
|
@@ -12,12 +12,13 @@ Full Stripe integration via the Payment facade. Uses Stripe API version `2026-01
|
|
|
12
12
|
|
|
13
13
|
## Payment Drivers (provider-neutral)
|
|
14
14
|
|
|
15
|
-
`paymentDriver()` (also `Payment.driver()`) returns the driver `config.payment.driver` selects: `stripe` (default) or `
|
|
15
|
+
`paymentDriver()` (also `Payment.driver()`) returns the driver `config.payment.driver` selects: `stripe` (default), `adyen` or `paddle`, or one added with `registerPaymentDriver(name, factory)`. Source: `storage/framework/core/payments/src/driver/`.
|
|
16
16
|
|
|
17
17
|
- Money is `{ amount, currency }` in minor units; `assertMoney` refuses floats before a request leaves.
|
|
18
18
|
- Methods: `customer`, `charge(payer, money, paymentMethodId)`, `createPayment` (browser completes: `clientConfirmation` is a Stripe client secret or an Adyen session), `refund`, `checkout(payer, { mode, lines, successUrl, ... })`, `paymentMethods`, `removePaymentMethod`, `subscribe`, `cancelSubscription`, `subscriptions`, `verifyWebhook({ payload, headers })` -> `PaymentEvent[]`, `acknowledgeWebhook()`.
|
|
19
19
|
- `checkout` copies `reference` and `metadata` onto the payment / subscription / setup intent it creates (Stripe: `payment_intent_data`, `subscription_data`, `setup_intent_data`), so their webhooks can be attributed. `allowPromotionCodes` and `automaticTax` are Stripe only; Adyen refuses them.
|
|
20
|
-
- Unsupported operations throw `PaymentUnsupportedError` (driver + operation). Adyen has no catalog, subscriptions, invoices, coupons or portal.
|
|
20
|
+
- Unsupported operations throw `PaymentUnsupportedError` (driver + operation). Adyen has no catalog, subscriptions, invoices, coupons or portal; Paddle has no server-side charge, server-side subscribe, setup checkout, promotion-code or trial option on a checkout, or separate cancel URL.
|
|
21
|
+
- Paddle (`driver: 'paddle'`, Billing API v1, merchant of record): customers found or created by email; every payment is a transaction completed in Paddle.js (`clientConfirmation: { provider: 'paddle', transactionId, successUrl? }`), so `charge` and `subscribe` are unsupported and subscriptions start at `checkout` in `subscription` mode with catalog prices; refunds are adjustments Paddle reviews (`pending`, settled by `adjustment.updated`; a refund's event id is the adjustment's, so a created-and-updated approval counts once); webhooks `Paddle-Signature` = HMAC-SHA256 hex of `<ts>:<raw body>` under the secret, 5 s tolerance. Paddle has no hosted page: `GET /payments/checkout` (mounted while the driver is `paddle`) is the default payment link page, reading `stacks_success_url` from the transaction for Paddle.js. Env: `PADDLE_API_KEY`, `PADDLE_ENVIRONMENT`, `PADDLE_CLIENT_TOKEN`, `PADDLE_WEBHOOK_SECRET`, `PADDLE_TAX_CATEGORY`.
|
|
21
22
|
- Adyen: Checkout API v72, `X-API-Key`; shopper reference `user-<id>` (no PII); full refunds go to `/reversals`; webhooks verified per item with HMAC-SHA256 over 8 colon-joined fields under the hex key, acknowledged with 202.
|
|
22
23
|
- `PaymentEvent` carries `provider` and `id` (together, the retry key), `reference` (the payment; for a refund, the payment refunded), `amount` (for a refund, that one refund - Stripe's cumulative `amount_refunded` is turned into a delta from `previous_attributes`) and `reason`. Adyen's id is `eventCode:pspReference:success`, since Adyen may resend a pair with the outcome changed.
|
|
23
24
|
- The Stripe driver delegates to the existing billable modules, so idempotency keys and `stripe_id` handling are unchanged.
|
package/bootstrap.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* @see storage/framework/core/router/src/route-loader.ts:loadFrameworkRoutes
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { feature } from '@stacksjs/config'
|
|
24
|
+
import { config, feature } from '@stacksjs/config'
|
|
25
25
|
import { frameworkPath } from '@stacksjs/path'
|
|
26
26
|
import { bundleMounts, DEFAULT_ROUTE_BUNDLE_FEATURES, route } from '@stacksjs/router'
|
|
27
27
|
import MaintenanceMiddleware from './app/Middleware/Maintenance'
|
|
@@ -160,6 +160,15 @@ if (mounts('payments')) {
|
|
|
160
160
|
await route.register(frameworkPath('defaults/routes/payments.ts'))
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
// The page a Paddle payment link opens: Paddle has no hosted checkout, so
|
|
164
|
+
// its default payment link has to point at a page of the app's that loads
|
|
165
|
+
// Paddle.js. Mounted while the app pays through Paddle, as part of the
|
|
166
|
+
// `payments` bundle when the app names its bundles.
|
|
167
|
+
const paymentDriverName = (config as { payment?: { driver?: string } }).payment?.driver
|
|
168
|
+
if (paymentDriverName === 'paddle' && (selection?.explicit ? selection.bundles.has('payments') : true)) {
|
|
169
|
+
await route.register(frameworkPath('defaults/routes/paddle.ts'))
|
|
170
|
+
}
|
|
171
|
+
|
|
163
172
|
// Social sign-in: `/auth/{provider}` + `/auth/{provider}/callback`
|
|
164
173
|
// (stacksjs/stacks#2276). An opt-in bundle, NOT part of the implicit default
|
|
165
174
|
// set or `all` — OAuth callback URLs in an app that configured no provider
|
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.87",
|
|
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.87",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@iconify-json/f7": "^1.2.2",
|
|
57
57
|
"@iconify-json/hugeicons": "^1.2.35",
|
|
58
|
-
"@stacksjs/mobile": "^0.75.
|
|
59
|
-
"@stacksjs/sanitizer": "^0.2.
|
|
58
|
+
"@stacksjs/mobile": "^0.75.87",
|
|
59
|
+
"@stacksjs/sanitizer": "^0.2.384",
|
|
60
60
|
"@xterm/addon-fit": "^0.11.0",
|
|
61
61
|
"@xterm/xterm": "^6.0.0",
|
|
62
62
|
"ts-qr-codes": "^0.1.8"
|
|
@@ -20,6 +20,12 @@ describe('Native components', () => {
|
|
|
20
20
|
expect(item).toContain('draggable="false"')
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
+
it('switches tabs instantly: prefetched, and without the cross-fade', () => {
|
|
24
|
+
const item = read('NativeTabItem')
|
|
25
|
+
expect(item).toContain('data-stx-prefetch="eager"')
|
|
26
|
+
expect(item).toContain('data-stx-transition="none"')
|
|
27
|
+
})
|
|
28
|
+
|
|
23
29
|
it('centres each tab in the bar rather than against its top border', () => {
|
|
24
30
|
// stx wraps each NativeTabItem in a scope box; the box stretched while
|
|
25
31
|
// the link inside kept its own height, so icons sat on the border.
|
|
@@ -22,7 +22,9 @@ const hasBadge = badge !== '' && badge !== 0 && badge !== '0'
|
|
|
22
22
|
long press, and a draggable link starts a native drag instead of
|
|
23
23
|
navigating. `data-stx-prefetch="eager"`: every tab's page is fetched once
|
|
24
24
|
the current one is idle, so switching tabs is served from the router's
|
|
25
|
-
cache, as instant as a native tab bar.
|
|
25
|
+
cache, as instant as a native tab bar. `data-stx-transition="none"`: and
|
|
26
|
+
swapped without the router's cross-fade, which iOS never draws between
|
|
27
|
+
tabs (it read as a quarter-second lag on every tap). -->
|
|
26
28
|
<StxLink
|
|
27
29
|
to="{{ href }}"
|
|
28
30
|
aria-label="{{ label }}"
|
|
@@ -31,6 +33,7 @@ const hasBadge = badge !== '' && badge !== 0 && badge !== '0'
|
|
|
31
33
|
class="native-tab-item"
|
|
32
34
|
draggable="false"
|
|
33
35
|
data-stx-prefetch="eager"
|
|
36
|
+
data-stx-transition="none"
|
|
34
37
|
>
|
|
35
38
|
<span class="native-tab-glyph">
|
|
36
39
|
<i aria-hidden="true" class="{{ icon }} native-tab-icon"></i>
|
package/routes/paddle.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { route } from '@stacksjs/router'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The page a Paddle payment link opens (stacksjs/stacks#665). Set Paddle's
|
|
5
|
+
* default payment link (Checkout > Checkout settings) to
|
|
6
|
+
* `https://<your app>/payments/checkout`: Paddle appends the transaction as
|
|
7
|
+
* `?_ptxn=`, and this page opens Paddle.js on it with the success page
|
|
8
|
+
* `Payment.checkout()` stored on the transaction.
|
|
9
|
+
*
|
|
10
|
+
* Mounted while `config.payment.driver` is `paddle`. An app route at the same
|
|
11
|
+
* path registers first and wins.
|
|
12
|
+
*/
|
|
13
|
+
route.get('/payments/checkout', async (request) => {
|
|
14
|
+
const { servePaddleCheckout } = await import('@stacksjs/payments')
|
|
15
|
+
return servePaddleCheckout(request)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
route.get('/payments/checkout.js', async () => {
|
|
19
|
+
const { servePaddleCheckoutScript } = await import('@stacksjs/payments')
|
|
20
|
+
return servePaddleCheckoutScript()
|
|
21
|
+
})
|
package/routes/payments.ts
CHANGED
|
@@ -6,7 +6,9 @@ import { route } from '@stacksjs/router'
|
|
|
6
6
|
*
|
|
7
7
|
* Point the provider at `POST /webhooks/payments`: Stripe's endpoint with its
|
|
8
8
|
* signing secret in `STRIPE_WEBHOOK_SECRET`, or Adyen's standard webhook with
|
|
9
|
-
* its HMAC key in `ADYEN_HMAC_KEY`,
|
|
9
|
+
* its HMAC key in `ADYEN_HMAC_KEY`, or a Paddle notification destination with
|
|
10
|
+
* its secret key in `PADDLE_WEBHOOK_SECRET`, whichever `config.payment.driver`
|
|
11
|
+
* names.
|
|
10
12
|
* Payments and refunds then reach their orders without app code; orders used
|
|
11
13
|
* to sit at PENDING whenever the webhook was the only news of a payment.
|
|
12
14
|
*
|