@stacksjs/defaults 0.75.145 → 0.75.147
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-native/CATALOG.md +7 -7
- package/ai/skills/stacks-notifications/SKILL.md +36 -3
- package/ai/skills/stacks-sms/SKILL.md +19 -3
- package/ide/vscode/package.json +1 -1
- package/package.json +2 -2
- package/resources/components/Email/EmailLayout.stx +5 -1
- package/resources/components/NativeComponents.test.ts +3 -1
- package/resources/components/NativeNavBar.stx +5 -2
- package/resources/emails/notification.stx +16 -4
|
@@ -35,7 +35,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
|
|
|
35
35
|
| `@stacksjs/composables` | [`stacks-composables`](../stacks-composables/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/composables/src/index.ts) | 18 |
|
|
36
36
|
| `@stacksjs/config` | [`stacks-config`](../stacks-config/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/config/src/index.ts) | 15 |
|
|
37
37
|
| `@stacksjs/cron` | [`stacks-cron`](../stacks-cron/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cron/src/index.ts) | 4 |
|
|
38
|
-
| `@stacksjs/database` | [`stacks-database`](../stacks-database/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/index.ts) |
|
|
38
|
+
| `@stacksjs/database` | [`stacks-database`](../stacks-database/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/index.ts) | 115 |
|
|
39
39
|
| `@stacksjs/datetime` | [`stacks-datetime`](../stacks-datetime/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/datetime/src/index.ts) | 12 |
|
|
40
40
|
| `@stacksjs/defaults` | [`stacks-build`](../stacks-build/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/defaults/package.json) | 1 |
|
|
41
41
|
| `@stacksjs/deploy` | [`stacks-deploy`](../stacks-deploy/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/deploy/package.json) | 1 |
|
|
@@ -63,7 +63,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
|
|
|
63
63
|
| `@stacksjs/mobile` | [`stacks-mobile`](../stacks-mobile/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/mobile/src/index.ts) | 12 |
|
|
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) |
|
|
66
|
+
| `@stacksjs/notifications` | [`stacks-notifications`](../stacks-notifications/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/index.ts) | 14 |
|
|
67
67
|
| `@stacksjs/objects` | [`stacks-objects`](../stacks-objects/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/objects/src/index.ts) | 4 |
|
|
68
68
|
| `@stacksjs/orm` | [`stacks-orm`](../stacks-orm/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/orm/src/index.ts) | 60 |
|
|
69
69
|
| `@stacksjs/pagination` | [`stacks-pagination`](../stacks-pagination/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/pagination/src/index.ts) | 0 |
|
|
@@ -351,7 +351,7 @@ Declared runtime exports: `BUN_BROKEN_MYSQL_POOL_MESSAGE`, `BUN_BROKEN_POSTGRES_
|
|
|
351
351
|
|
|
352
352
|
Local source modules: [src/affected-rows.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/affected-rows.ts), [src/auth-tables.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/auth-tables.ts), [src/broken-pool.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/broken-pool.ts), [src/custom/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/custom/index.ts), [src/database.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/database.ts), [src/datetime-columns.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/datetime-columns.ts), [src/ddl-constraints.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/ddl-constraints.ts), [src/defaults.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/defaults.ts), [src/dialect.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/dialect.ts), [src/driver-config.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/driver-config.ts), [src/drivers/dynamodb.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/drivers/dynamodb.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/drivers/index.ts), [src/ensure-database.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/ensure-database.ts), [src/erd.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/erd.ts), [src/fk-audit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/fk-audit.ts), [src/framework-renames.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/framework-renames.ts), [src/hand-written-migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/hand-written-migrations.ts), [src/ignored-migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/ignored-migrations.ts), [src/index-shape.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/index-shape.ts), [src/managed-columns.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/managed-columns.ts), [src/migration-dialect.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/migration-dialect.ts), [src/migration-ledger.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/migration-ledger.ts), [src/migration-path.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/migration-path.ts), [src/migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/migrations.ts), [src/model-sources.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/model-sources.ts), [src/notification-tables.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/notification-tables.ts), [src/package-migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/package-migrations.ts), [src/package-models.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/package-models.ts), [src/query-logger.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/query-logger.ts), [src/rbac-tables.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/rbac-tables.ts), [src/relation-columns.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/relation-columns.ts), [src/replicas.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/replicas.ts), [src/row-lock.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/row-lock.ts), [src/safe-migrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/safe-migrations.ts), [src/schema-align.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/schema-align.ts), [src/schema-drift.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/schema-drift.ts), [src/seeder.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/seeder.ts), [src/shadowed-models.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/shadowed-models.ts), [src/sql-helpers.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/sql-helpers.ts), [src/trait-tables.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/trait-tables.ts), [src/transaction-context.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/transaction-context.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/types.ts), [src/unique-audit.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/unique-audit.ts), [src/utc-defaults.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/utc-defaults.ts), [src/utils.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/utils.ts), [src/uuid-columns.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/uuid-columns.ts), [src/vschema.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/src/vschema.ts).
|
|
353
353
|
|
|
354
|
-
Evidence directory: [
|
|
354
|
+
Evidence directory: [115 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/database/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
355
355
|
|
|
356
356
|
### @stacksjs/datetime
|
|
357
357
|
|
|
@@ -607,11 +607,11 @@ Evidence directory: [7 retained tests](https://github.com/stacksjs/stacks/blob/m
|
|
|
607
607
|
|
|
608
608
|
Guidance: [`stacks-notifications`](../stacks-notifications/SKILL.md). Source: [storage/framework/core/notifications/src/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/index.ts).
|
|
609
609
|
|
|
610
|
-
Declared runtime exports: `BroadcastNotificationDriver`, `DEFAULT_SMS_OPT_OUT`, `DEFAULT_UNSUBSCRIBE_NOTICE`, `DatabaseNotificationDriver`, `SMS_SENDER_MAX_LENGTH`, `accountNotificationTemplates`, `bulkSetPreferences`, `commerceNotificationTemplates`, `createNotificationTemplates`, `defineNotificationTemplates`, `emailParagraphs`, `ensureSuccessfulEmailResult`, `ensureSuccessfulNotificationResults`, `filterChannelsByPreferences`, `getNotificationPreferences`, `interpolateNotificationText`, `listUnsubscribeHeaders`, `makeDeliveryRecord`, `notification`, `notificationChannel`, `notificationEmailHtml`, `notificationEmailMessage`, `notificationEmailText`, `notificationSmsBody`, `notificationTemplateVariables`, `notificationUnsubscribeLinks`, `notificationUnsubscribeToken`, `notify`, `parseNotificationUnsubscribeToken`, `recordNotificationDelivery`, `renderNotificationTemplate`, `resolveDeliveryRecipient`, `resolveNotificationLocale`, `setNotificationPreference`, `smsSenderPrefix`, `subscriptionNotificationTemplates`, `unsubscribeTokenFromRequest`, `unsubscribeWithToken`, `useBroadcast`, `useChat`, `useDatabase`, `useEmail`, `useNotification`, `usePush`, `useSMS`, `wherePreferenceCategory`.
|
|
610
|
+
Declared runtime exports: `BroadcastNotificationDriver`, `DEFAULT_SMS_OPT_OUT`, `DEFAULT_UNSUBSCRIBE_NOTICE`, `DatabaseNotificationDriver`, `NOTIFICATION_FRAME_STRINGS`, `SMS_SENDER_MAX_LENGTH`, `accountNotificationTemplates`, `bulkSetPreferences`, `commerceNotificationTemplates`, `createNotificationTemplates`, `defineNotificationTemplates`, `emailParagraphs`, `ensureSuccessfulEmailResult`, `ensureSuccessfulNotificationResults`, `filterChannelsByPreferences`, `getNotificationPreferences`, `interpolateNotificationText`, `listUnsubscribeHeaders`, `makeDeliveryRecord`, `notification`, `notificationChannel`, `notificationEmailHtml`, `notificationEmailMessage`, `notificationEmailText`, `notificationFrameStrings`, `notificationLanguageTag`, `notificationSmsBody`, `notificationTemplateVariables`, `notificationUnsubscribeLinks`, `notificationUnsubscribeToken`, `notify`, `parseNotificationUnsubscribeToken`, `recordNotificationDelivery`, `renderNotificationTemplate`, `resolveDeliveryRecipient`, `resolveNotificationLocale`, `setNotificationPreference`, `smsSenderPrefix`, `subscriptionNotificationTemplates`, `unsubscribeTokenFromRequest`, `unsubscribeWithToken`, `useBroadcast`, `useChat`, `useDatabase`, `useEmail`, `useNotification`, `usePush`, `useSMS`, `wherePreferenceCategory`.
|
|
611
611
|
|
|
612
|
-
Local source modules: [src/delivery.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/delivery.ts), [src/drivers/broadcast.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/drivers/broadcast.ts), [src/drivers/database.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/drivers/database.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/drivers/index.ts), [src/email-body.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/email-body.ts), [src/preferences.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/preferences.ts), [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).
|
|
612
|
+
Local source modules: [src/delivery.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/delivery.ts), [src/drivers/broadcast.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/drivers/broadcast.ts), [src/drivers/database.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/drivers/database.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/drivers/index.ts), [src/email-body.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/src/email-body.ts), [src/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/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).
|
|
613
613
|
|
|
614
|
-
Evidence directory: [
|
|
614
|
+
Evidence directory: [14 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/notifications/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
615
615
|
|
|
616
616
|
### @stacksjs/objects
|
|
617
617
|
|
|
@@ -807,7 +807,7 @@ Guidance: [`stacks-sms`](../stacks-sms/SKILL.md). Source: [storage/framework/cor
|
|
|
807
807
|
|
|
808
808
|
Declared runtime exports: `SMS`, `TwilioDriver`, `VonageDriver`, `createTwilioDriver`, `createVonageDriver`.
|
|
809
809
|
|
|
810
|
-
Local source modules: [src/compliance.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/compliance.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/drivers/index.ts), [src/drivers/twilio.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/drivers/twilio.ts), [src/drivers/vonage.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/drivers/vonage.ts), [src/inbound.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/inbound.ts), [src/opt-out.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/opt-out.ts), [src/sms.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/sms.ts).
|
|
810
|
+
Local source modules: [src/compliance.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/compliance.ts), [src/drivers/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/drivers/index.ts), [src/drivers/twilio.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/drivers/twilio.ts), [src/drivers/vonage.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/drivers/vonage.ts), [src/inbound.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/inbound.ts), [src/keywords.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/keywords.ts), [src/opt-out.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/opt-out.ts), [src/sms.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/src/sms.ts).
|
|
811
811
|
|
|
812
812
|
Evidence directory: [5 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/sms/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
|
|
813
813
|
|
|
@@ -41,8 +41,9 @@ optional `error`. A resolved call does not imply every channel succeeded.
|
|
|
41
41
|
| push | pushTokens, a string or string array | `@stacksjs/push`, default Expo |
|
|
42
42
|
| broadcast | broadcastChannel or userId | `@stacksjs/realtime` |
|
|
43
43
|
|
|
44
|
-
Payload: `{ body, subject?, data?, action? }`. Email treats
|
|
45
|
-
renders it through the framework layout
|
|
44
|
+
Payload: `{ body, subject?, data?, action?, locale?, strings? }`. Email treats
|
|
45
|
+
body as text and renders it through the framework layout (see "The frame's
|
|
46
|
+
language" for `locale`). Use the exported `NotificationAction`
|
|
46
47
|
type for an action link. The broadcast fallback is `private-user.{userId}` or
|
|
47
48
|
public `notifications` when no user/channel is supplied. Its delivery is
|
|
48
49
|
best-effort; a missing realtime server is not a durable notification queue.
|
|
@@ -119,6 +120,37 @@ body already starts with it); pass it whenever several businesses share one
|
|
|
119
120
|
sending number, so the person can tell who wrote. STOP only becomes an opt-out the app sees when inbound
|
|
120
121
|
SMS is wired up; see `stacks-sms`.
|
|
121
122
|
|
|
123
|
+
## The frame's language
|
|
124
|
+
|
|
125
|
+
The subject, body and button label are the caller's. The words around them
|
|
126
|
+
are the frame's: the transactional footer ("This is an automated message from
|
|
127
|
+
{app}. Please do not reply to this email."), the copyright line, the marketing
|
|
128
|
+
notice, the **Unsubscribe** / **Email preferences** labels (also in the text
|
|
129
|
+
part), the button label when the action has none ("Open") and a marketing
|
|
130
|
+
text's opt-out line. `payload.locale` puts all of them in the reader's
|
|
131
|
+
language and sets the email's `<html lang>`:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const language = user.locale // 'de', 'de-AT', 'en', ...
|
|
135
|
+
await notify({ userId, email }, { ...templates.render('order_receipt', variables, language), locale: language }, ['email'])
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- Built in: `en` and `de` (`NOTIFICATION_FRAME_STRINGS`). Fallback like the
|
|
139
|
+
templates: `de-AT` → `de` → `en`. No locale renders exactly the English the
|
|
140
|
+
frame always had (a fixture test holds it byte for byte).
|
|
141
|
+
- Another language: pass the whole set as `payload.strings`
|
|
142
|
+
(`NotificationFrameStrings`: `automatedNotice`, `copyright`,
|
|
143
|
+
`unsubscribeNotice`, `unsubscribe`, `preferences`, `action`, `smsOptOut`). A
|
|
144
|
+
partial set overrides only what it names. `{app}` and `{year}` are filled in.
|
|
145
|
+
- Precedence: `unsubscribe.notice` and `payload.smsOptOut` still win over the
|
|
146
|
+
frame's notice and opt-out line.
|
|
147
|
+
- The builders take the same fields: `notificationEmailMessage`,
|
|
148
|
+
`notificationEmailHtml`, `notificationEmailText`, `notificationSmsBody`;
|
|
149
|
+
`notificationFrameStrings(locale, overrides)` resolves a set.
|
|
150
|
+
- An app's own `resources/emails/notification.stx` must read the new props
|
|
151
|
+
(`lang`, `automatedNotice`, `copyrightNotice`, `unsubscribeLabel`,
|
|
152
|
+
`preferencesLabel`) to be localized; `<EmailLayout lang>` sets `<html lang>`.
|
|
153
|
+
|
|
122
154
|
## Templates: one catalog of localized messages
|
|
123
155
|
|
|
124
156
|
`@stacksjs/notifications/templates` (pure, browser-safe; also exported from the
|
|
@@ -200,7 +232,8 @@ delivery log, not extra columns on the inbox.
|
|
|
200
232
|
signatures. `preferences.ts`, `delivery.ts` and `drivers/database.ts`
|
|
201
233
|
own their persistence; the query builder is bun-query-builder.
|
|
202
234
|
`notifications.test.ts`, `delivery-tracking.test.ts`,
|
|
203
|
-
`broadcast.test.ts` and `
|
|
235
|
+
`broadcast.test.ts`, `email-body.test.ts` and `frame-locale.test.ts` cover
|
|
236
|
+
retained behavior; `frame-strings.ts` holds the frame's languages.
|
|
204
237
|
For provider status, consult `config/src/capabilities.ts` and each channel
|
|
205
238
|
skill; a constructed send request is not a live delivery assertion.
|
|
206
239
|
|
|
@@ -393,7 +393,10 @@ interface VerificationResult {
|
|
|
393
393
|
STOP/START/HELP, persists opt-out or opt-in, and returns the reply an inbound
|
|
394
394
|
webhook should send. `optOutPhone`, `optInPhone`,
|
|
395
395
|
`isPhoneOptedOut` and `normalizePhone` use the persistent
|
|
396
|
-
`sms_opt_outs` table. Apply its model migration first.
|
|
396
|
+
`sms_opt_outs` table. Apply its model migration first. `optOutPhone` is one
|
|
397
|
+
insert-or-ignore on the unique `phone` (`ON CONFLICT DO NOTHING`, `INSERT
|
|
398
|
+
IGNORE` on MySQL), so repeated or concurrent STOPs leave one row and keep the
|
|
399
|
+
first opt-out's time and reason. Sending through the
|
|
397
400
|
generic notification SMS channel checks opt-outs; inspect the selected direct
|
|
398
401
|
send path before assuming a low-level provider call applies that policy.
|
|
399
402
|
|
|
@@ -403,6 +406,15 @@ normalizes its payload. `classifySmsIntent/smsComplianceReply` are pure helpers;
|
|
|
403
406
|
they do not themselves persist consent. Use the persistent handleInboundSms
|
|
404
407
|
workflow when consent state must affect future sends.
|
|
405
408
|
|
|
409
|
+
Keywords are recognised in every language `SMS_KEYWORDS` has, on every
|
|
410
|
+
message (`matchSmsKeyword(body)` returns the kind and the language): English
|
|
411
|
+
`STOP`/`STOPALL`/`UNSUBSCRIBE`/`CANCEL`/`END`/`QUIT`, `START`/`YES`/`UNSTOP`,
|
|
412
|
+
`HELP`/`INFO`, and German `STOPP`/`ABMELDEN`/`ABBESTELLEN`, `STARTEN`,
|
|
413
|
+
`HILFE`. Only the first word counts, in any case. `classifyInboundSms`,
|
|
414
|
+
`handleInboundSms` and `classifySmsIntent` all use these lists, and
|
|
415
|
+
`inboundSmsReply(kind, { locale })` answers in the keyword's language. Add a
|
|
416
|
+
language by adding its keywords and replies in `src/keywords.ts`.
|
|
417
|
+
|
|
406
418
|
For a Twilio incoming-message webhook, `handleTwilioInbound(request, options)`
|
|
407
419
|
does the whole job and returns the TwiML `Response`: it reads the form body,
|
|
408
420
|
verifies `X-Twilio-Signature` with `TWILIO_AUTH_TOKEN` against `APP_URL` plus
|
|
@@ -411,8 +423,12 @@ URL is not what Twilio signed), records STOP/START in `sms_opt_outs`, then
|
|
|
411
423
|
calls `onConsentChange({ phone, kind })` so the app can switch its own
|
|
412
424
|
preferences, and `onMessage(message)` for every verified text. Without a token
|
|
413
425
|
it answers 503, and a bad signature 403, changing nothing. It does not reply to
|
|
414
|
-
keywords unless
|
|
415
|
-
|
|
426
|
+
keywords unless asked, because Twilio's own opt-out handling already answers
|
|
427
|
+
the English STOP/START/HELP and a second reply arrives twice. `reply:
|
|
428
|
+
'unhandled'` confirms only the keywords Twilio does not know (STOPP and the
|
|
429
|
+
other German ones), in their language, so every keyword gets exactly one
|
|
430
|
+
confirmation; `reply: true` answers all of them. `onConsentChange` and
|
|
431
|
+
`onMessage` receive the keyword's `locale`. Twilio Advanced
|
|
416
432
|
Opt-Out's `OptOutType` field wins over the keyword list (`inboundSmsKind`), so
|
|
417
433
|
custom keywords configured there count. `inboundSmsReply(kind)` is the reply
|
|
418
434
|
copy `handleInboundSms` uses. Route it with `skipCsrf()` and point the number's
|
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.147",
|
|
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.147",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@iconify-json/f7": "^1.2.2",
|
|
57
57
|
"@iconify-json/hugeicons": "^1.2.35",
|
|
58
|
-
"@stacksjs/mobile": "0.75.
|
|
58
|
+
"@stacksjs/mobile": "0.75.147",
|
|
59
59
|
"@stacksjs/sanitizer": "^0.2.422",
|
|
60
60
|
"@xterm/addon-fit": "^0.11.0",
|
|
61
61
|
"@xterm/xterm": "^6.0.0",
|
|
@@ -37,6 +37,7 @@ interface EmailLayoutProps {
|
|
|
37
37
|
brandUrl?: string
|
|
38
38
|
accent?: string
|
|
39
39
|
footer?: string
|
|
40
|
+
lang?: string
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
const props = defineProps<EmailLayoutProps>()
|
|
@@ -49,6 +50,9 @@ const brand = props.brand || ''
|
|
|
49
50
|
const brandUrl = props.brandUrl || ''
|
|
50
51
|
const accent = props.accent || '#3451b2'
|
|
51
52
|
const footer = props.footer || ''
|
|
53
|
+
// The language the email is written in (`<html lang>`), for screen readers
|
|
54
|
+
// and mail clients' translate offers.
|
|
55
|
+
const lang = props.lang || 'en'
|
|
52
56
|
const fontStack = '-apple-system,BlinkMacSystemFont,\'Segoe UI\',Roboto,\'Helvetica Neue\',Arial,sans-serif'
|
|
53
57
|
|
|
54
58
|
const responsiveCss = [
|
|
@@ -73,7 +77,7 @@ const responsiveCss = [
|
|
|
73
77
|
</script>
|
|
74
78
|
|
|
75
79
|
<!DOCTYPE html>
|
|
76
|
-
<html lang="
|
|
80
|
+
<html lang="{{ lang }}">
|
|
77
81
|
<head>
|
|
78
82
|
<meta charset="UTF-8">
|
|
79
83
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
@@ -84,7 +84,9 @@ describe('Native components', () => {
|
|
|
84
84
|
it('backs out through history, and sticks within the page rather than its wrapper', () => {
|
|
85
85
|
const bar = read('NativeNavBar')
|
|
86
86
|
expect(bar).toContain('if (goBack()) event.preventDefault()')
|
|
87
|
-
expect(bar).toContain('[data-stx-scope]:has(> .native-nav)
|
|
87
|
+
expect(bar).toContain('[data-stx-scope]:has(> .native-nav),')
|
|
88
|
+
// A header component wrapping the bar adds a wrapper of its own.
|
|
89
|
+
expect(bar).toContain('[data-stx-scope]:has(> [data-stx-scope] > .native-nav) { display: contents; }')
|
|
88
90
|
expect(bar).toContain("useReactiveProp<string>('title', '')")
|
|
89
91
|
})
|
|
90
92
|
|
|
@@ -105,9 +105,12 @@ function tapBack(event: MouseEvent): void {
|
|
|
105
105
|
<style>
|
|
106
106
|
/* The element stx wraps each component in is a box too, as tall as the bar
|
|
107
107
|
and its title; the bar stuck inside it and scrolled away with the title.
|
|
108
|
-
Both boxes step aside so the bar sticks to the page
|
|
108
|
+
Both boxes step aside so the bar sticks to the page, and so does the
|
|
109
|
+
wrapper of an app's own header component around this one (HQ's
|
|
110
|
+
MobileHeader): one box more, and the bar scrolled away again. */
|
|
109
111
|
.native-nav,
|
|
110
|
-
[data-stx-scope]:has(> .native-nav)
|
|
112
|
+
[data-stx-scope]:has(> .native-nav),
|
|
113
|
+
[data-stx-scope]:has(> [data-stx-scope] > .native-nav) { display: contents; }
|
|
111
114
|
|
|
112
115
|
.native-nav-bar {
|
|
113
116
|
position: sticky;
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
// mailers (stacksjs/stacks#1901); an app's own resources/emails/notification
|
|
7
7
|
// wins over this one. A notification with `unsubscribe` (marketing) says why
|
|
8
8
|
// it came and links to Unsubscribe in the footer instead of "do not reply".
|
|
9
|
+
// Every fixed word of the frame comes in as a prop, so a `locale` passed to
|
|
10
|
+
// notify() makes the whole email that language.
|
|
9
11
|
const appName = props.appName || 'Stacks'
|
|
10
12
|
const primaryColor = props.primaryColor || '#3451b2'
|
|
11
13
|
const subject = props.subject || appName
|
|
@@ -17,9 +19,19 @@ const unsubscribeUrl = props.unsubscribeUrl || ''
|
|
|
17
19
|
const unsubscribeNotice = props.unsubscribeNotice || 'You are receiving this because these emails are turned on in your settings.'
|
|
18
20
|
const preferencesUrl = props.preferencesUrl || ''
|
|
19
21
|
const year = props.year || new Date().getFullYear()
|
|
22
|
+
// The frame's fixed words, in the reader's language when notify() was given
|
|
23
|
+
// a `locale` (see NotificationFrameStrings in @stacksjs/notifications). The
|
|
24
|
+
// English here is only for a render without them. `{app}` and `{year}` are
|
|
25
|
+
// filled in below.
|
|
26
|
+
const lang = props.lang || 'en'
|
|
27
|
+
const fill = text => String(text).split('{app}').join(appName).split('{year}').join(String(year))
|
|
28
|
+
const automatedNotice = fill(props.automatedNotice || 'This is an automated message from {app}. Please do not reply to this email.')
|
|
29
|
+
const copyrightNotice = fill(props.copyrightNotice || '{year} {app}. All rights reserved.')
|
|
30
|
+
const unsubscribeLabel = props.unsubscribeLabel || 'Unsubscribe'
|
|
31
|
+
const preferencesLabel = props.preferencesLabel || 'Email preferences'
|
|
20
32
|
</script>
|
|
21
33
|
|
|
22
|
-
<EmailLayout title="{{ subject }}" preheader="{{ preheader }}">
|
|
34
|
+
<EmailLayout title="{{ subject }}" preheader="{{ preheader }}" lang="{{ lang }}">
|
|
23
35
|
<template #header>
|
|
24
36
|
<tr>
|
|
25
37
|
<td style="background-color:{{ primaryColor }};padding:30px 40px;text-align:center;">
|
|
@@ -42,11 +54,11 @@ const year = props.year || new Date().getFullYear()
|
|
|
42
54
|
<td style="padding:20px 40px;background-color:#f9fafb;border-top:1px solid #e5e7eb;">
|
|
43
55
|
@if(unsubscribeUrl)
|
|
44
56
|
<p style="margin:0;color:#6b7280;font-size:12px;text-align:center;">{{ unsubscribeNotice }}</p>
|
|
45
|
-
<p style="margin:8px 0 0 0;color:#6b7280;font-size:12px;text-align:center;"><a href="{{ unsubscribeUrl }}" style="color:#6b7280;text-decoration:underline;">
|
|
57
|
+
<p style="margin:8px 0 0 0;color:#6b7280;font-size:12px;text-align:center;"><a href="{{ unsubscribeUrl }}" style="color:#6b7280;text-decoration:underline;">{{ unsubscribeLabel }}</a>@if(preferencesUrl) · <a href="{{ preferencesUrl }}" style="color:#6b7280;text-decoration:underline;">{{ preferencesLabel }}</a>@endif</p>
|
|
46
58
|
@else
|
|
47
|
-
<p style="margin:0;color:#6b7280;font-size:12px;text-align:center;">
|
|
59
|
+
<p style="margin:0;color:#6b7280;font-size:12px;text-align:center;">{{ automatedNotice }}</p>
|
|
48
60
|
@endif
|
|
49
|
-
<p style="margin:10px 0 0 0;color:#9ca3af;font-size:11px;text-align:center;">© {{
|
|
61
|
+
<p style="margin:10px 0 0 0;color:#9ca3af;font-size:11px;text-align:center;">© {{ copyrightNotice }}</p>
|
|
50
62
|
</td>
|
|
51
63
|
</tr>
|
|
52
64
|
</template>
|