@agent-native/core 0.84.59 → 0.84.60
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/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/server/ssr-handler.ts +14 -16
- package/corpus/templates/clips/app/i18n/ar-SA.ts +7 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +8 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +7 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +7 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +7 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +6 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +8 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +7 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +7 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +5 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +5 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +174 -28
- package/corpus/templates/clips/app/routes/record.tsx +74 -6
- package/dist/collab/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +14 -16
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.84.60
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4b6ca6c: Loosen the document script CSP allowances so hosted Google Tag Manager scripts and framework inline bootstrap scripts do not trigger CSP violations.
|
|
8
|
+
|
|
3
9
|
## 0.84.59
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.60",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -261,8 +261,7 @@ function applyDefaultSpeculationRulesHeader(
|
|
|
261
261
|
* Extract the plain JS body from a `<script ...>body</script>` string.
|
|
262
262
|
* Returns `null` if the input is falsy or has no recognisable `</script>` end.
|
|
263
263
|
* Used to compute the sha256 hash of framework-injected inline scripts so the
|
|
264
|
-
* hash can be listed in
|
|
265
|
-
* `'unsafe-inline'`.
|
|
264
|
+
* hash can be listed in app-owned `script-src` CSP directives.
|
|
266
265
|
*/
|
|
267
266
|
function extractScriptBody(scriptTag: string | null): string | null {
|
|
268
267
|
if (!scriptTag) return null;
|
|
@@ -547,13 +546,10 @@ function augmentExistingReportOnlyCspForFrameworkScripts(
|
|
|
547
546
|
*
|
|
548
547
|
* A third directive, `script-src`, is emitted via `Content-Security-Policy-
|
|
549
548
|
* Report-Only` rather than enforced when the app has no existing document CSP.
|
|
550
|
-
* The framework injects
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
* Google Tag Manager / GA4 from `GA_CSP_SCRIPT_HOSTS`. All of those are listed
|
|
555
|
-
* here so the report-only policy reflects the code the framework itself injects
|
|
556
|
-
* instead of reporting a violation on every page load.
|
|
549
|
+
* The framework injects inline scripts for analytics, Sentry, and template
|
|
550
|
+
* setup, and hosted apps need Google Tag Manager to load without noisy CSP
|
|
551
|
+
* diagnostics. The report-only policy is intentionally permissive for scripts:
|
|
552
|
+
* it includes `'unsafe-inline'` plus the known GA/GTM loader hosts.
|
|
557
553
|
*
|
|
558
554
|
* If an app or host already sends an enforced CSP with `script-src`,
|
|
559
555
|
* `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge only
|
|
@@ -577,20 +573,22 @@ function applyDocumentCsp(headers: Headers, sentryScript: string | null): void {
|
|
|
577
573
|
if (process.env.NODE_ENV !== "production") return;
|
|
578
574
|
if (process.env.AGENT_NATIVE_DISABLE_DOC_CSP === "1") return;
|
|
579
575
|
|
|
580
|
-
// script-src as Report-Only:
|
|
581
|
-
//
|
|
582
|
-
//
|
|
583
|
-
// itself injects, so listing them keeps the report-only policy from flagging
|
|
584
|
-
// GA on every page load (and keeps it safe to graduate to enforcement).
|
|
585
|
-
// Template theme-init hashes are NOT included here — see function comment.
|
|
576
|
+
// script-src as Report-Only: keep this deliberately loose so the framework's
|
|
577
|
+
// injected analytics and template bootstrap scripts do not look blocked in
|
|
578
|
+
// browser diagnostics.
|
|
586
579
|
const sentryBody = extractScriptBody(sentryScript);
|
|
587
580
|
const sentryHash = sentryBody ? computeInlineScriptHash(sentryBody) : null;
|
|
588
581
|
const gaInlineBody = getGaInlineConfigScriptBody();
|
|
589
582
|
const gaHash = gaInlineBody ? computeInlineScriptHash(gaInlineBody) : null;
|
|
590
583
|
const gaHosts = gaInlineBody ? [...GA_CSP_SCRIPT_HOSTS] : [];
|
|
591
|
-
const gaScriptSrcTokens = [
|
|
584
|
+
const gaScriptSrcTokens = [
|
|
585
|
+
"'unsafe-inline'",
|
|
586
|
+
...(gaHash ? [gaHash] : []),
|
|
587
|
+
...gaHosts,
|
|
588
|
+
];
|
|
592
589
|
const scriptSrcTokens = [
|
|
593
590
|
"'self'",
|
|
591
|
+
"'unsafe-inline'",
|
|
594
592
|
...(sentryHash ? [sentryHash] : []),
|
|
595
593
|
...(gaHash ? [gaHash] : []),
|
|
596
594
|
...gaHosts,
|
|
@@ -637,6 +637,11 @@ const messages = {
|
|
|
637
637
|
s3SecretAccessKeyLabel: "مفتاح الوصول السري",
|
|
638
638
|
s3RegionLabel: "المنطقة",
|
|
639
639
|
s3PublicBaseUrlLabel: "عنوان URL الأساسي العام",
|
|
640
|
+
s3UrlInvalid:
|
|
641
|
+
"يجب أن يكون عنوان URL صالحًا (مثال: https://s3.us-east-1.amazonaws.com)",
|
|
642
|
+
s3BucketInvalid:
|
|
643
|
+
"يجب أن يتكون اسم الحاوية من 3 إلى 63 حرفًا صغيرًا أو رقمًا أو شرطة",
|
|
644
|
+
s3RegionInvalid: 'يجب أن تكون منطقة صالحة (مثال: us-east-1) أو "auto"',
|
|
640
645
|
apiSetup: "إعداد الذكاء الاصطناعي",
|
|
641
646
|
apiSetupDescription:
|
|
642
647
|
"صِل الذكاء الاصطناعي باستخدام أرصدة Builder.io المجانية أو مفاتيح LLM الخاصة بك.",
|
|
@@ -651,6 +656,8 @@ const messages = {
|
|
|
651
656
|
providerKeysSet: "تم تعيين {{count}}",
|
|
652
657
|
checkingProviderKeys: "جار فحص مفاتيح المزود…",
|
|
653
658
|
keySet: "تم التعيين",
|
|
659
|
+
keyCleared: "تم مسح بيانات اعتماد التخزين",
|
|
660
|
+
clearAllS3: "مسح بيانات الاعتماد",
|
|
654
661
|
replaceKey: "استبدال المفتاح…",
|
|
655
662
|
pasteProviderKey: "الصق مفتاح مزود أولًا.",
|
|
656
663
|
apiKeySaved: "تم حفظ مفتاح API",
|
|
@@ -662,6 +662,12 @@ Alle sichtbaren Änderungen für Clips-Nutzer werden hier dokumentiert. Du kanns
|
|
|
662
662
|
s3SecretAccessKeyLabel: "Geheimer Zugriffsschlüssel",
|
|
663
663
|
s3RegionLabel: "Übersetzt: Region",
|
|
664
664
|
s3PublicBaseUrlLabel: "Öffentliche Basis-URL",
|
|
665
|
+
s3UrlInvalid:
|
|
666
|
+
"Muss eine gültige URL sein (z. B. https://s3.us-east-1.amazonaws.com)",
|
|
667
|
+
s3BucketInvalid:
|
|
668
|
+
"Bucket-Name muss 3–63 Kleinbuchstaben, Zahlen oder Bindestriche enthalten",
|
|
669
|
+
s3RegionInvalid:
|
|
670
|
+
'Muss eine gültige Region sein (z. B. us-east-1) oder "auto"',
|
|
665
671
|
apiSetup: "KI-Einrichtung",
|
|
666
672
|
apiSetupDescription:
|
|
667
673
|
"Verbinde KI mit kostenlosen Builder.io-Credits oder deinen eigenen LLM-Schlüsseln.",
|
|
@@ -676,6 +682,8 @@ Alle sichtbaren Änderungen für Clips-Nutzer werden hier dokumentiert. Du kanns
|
|
|
676
682
|
providerKeysSet: "{{count}} gesetzt",
|
|
677
683
|
checkingProviderKeys: "Anbieter-Schlüssel werden geprüft…",
|
|
678
684
|
keySet: "Gesetzt",
|
|
685
|
+
keyCleared: "Speicher-Anmeldedaten gelöscht",
|
|
686
|
+
clearAllS3: "Anmeldedaten löschen",
|
|
679
687
|
replaceKey: "Schlüssel ersetzen…",
|
|
680
688
|
pasteProviderKey: "Füge zuerst einen Anbieter-Schlüssel ein.",
|
|
681
689
|
apiKeySaved: "API-Schlüssel gespeichert",
|
|
@@ -637,6 +637,11 @@ All notable user-facing changes to Clips are documented here. Open it any time f
|
|
|
637
637
|
s3SecretAccessKeyLabel: "Secret access key",
|
|
638
638
|
s3RegionLabel: "Region",
|
|
639
639
|
s3PublicBaseUrlLabel: "Public base URL",
|
|
640
|
+
s3UrlInvalid:
|
|
641
|
+
"Must be a valid URL (e.g. https://s3.us-east-1.amazonaws.com)",
|
|
642
|
+
s3BucketInvalid:
|
|
643
|
+
"Bucket name must be 3–63 lowercase letters, numbers, or hyphens",
|
|
644
|
+
s3RegionInvalid: 'Must be a valid region (e.g. us-east-1) or "auto"',
|
|
640
645
|
apiSetup: "AI setup",
|
|
641
646
|
apiSetupDescription:
|
|
642
647
|
"Connect AI with Builder.io free credits or your own LLM keys.",
|
|
@@ -651,6 +656,8 @@ All notable user-facing changes to Clips are documented here. Open it any time f
|
|
|
651
656
|
providerKeysSet: "{{count}} set",
|
|
652
657
|
checkingProviderKeys: "Checking provider keys…",
|
|
653
658
|
keySet: "Set",
|
|
659
|
+
keyCleared: "Storage credentials cleared",
|
|
660
|
+
clearAllS3: "Clear credentials",
|
|
654
661
|
replaceKey: "Replace key…",
|
|
655
662
|
pasteProviderKey: "Paste a provider key first.",
|
|
656
663
|
apiKeySaved: "API key saved",
|
|
@@ -654,6 +654,11 @@ Todos los cambios visibles para los usuarios de Clips se documentan aquí. Puede
|
|
|
654
654
|
s3SecretAccessKeyLabel: "Clave de acceso secreta",
|
|
655
655
|
s3RegionLabel: "Región",
|
|
656
656
|
s3PublicBaseUrlLabel: "URL base pública",
|
|
657
|
+
s3UrlInvalid:
|
|
658
|
+
"Debe ser una URL válida (p. ej. https://s3.us-east-1.amazonaws.com)",
|
|
659
|
+
s3BucketInvalid:
|
|
660
|
+
"El nombre del bucket debe tener 3–63 letras minúsculas, números o guiones",
|
|
661
|
+
s3RegionInvalid: 'Debe ser una región válida (p. ej. us-east-1) o "auto"',
|
|
657
662
|
apiSetup: "Configuración de IA",
|
|
658
663
|
apiSetupDescription:
|
|
659
664
|
"Conecta IA con créditos gratis de Builder.io o tus propias claves LLM.",
|
|
@@ -668,6 +673,8 @@ Todos los cambios visibles para los usuarios de Clips se documentan aquí. Puede
|
|
|
668
673
|
providerKeysSet: "{{count}} configuradas",
|
|
669
674
|
checkingProviderKeys: "Comprobando claves de proveedor…",
|
|
670
675
|
keySet: "Configurada",
|
|
676
|
+
keyCleared: "Credenciales de almacenamiento borradas",
|
|
677
|
+
clearAllS3: "Borrar credenciales",
|
|
671
678
|
replaceKey: "Reemplazar clave…",
|
|
672
679
|
pasteProviderKey: "Pega primero una clave de proveedor.",
|
|
673
680
|
apiKeySaved: "Clave de API guardada",
|
|
@@ -655,6 +655,11 @@ Tous les changements visibles par les utilisateurs de Clips sont documentés ici
|
|
|
655
655
|
s3SecretAccessKeyLabel: "Clé d’accès secrète",
|
|
656
656
|
s3RegionLabel: "Région",
|
|
657
657
|
s3PublicBaseUrlLabel: "URL de base publique",
|
|
658
|
+
s3UrlInvalid:
|
|
659
|
+
"Doit être une URL valide (ex. https://s3.us-east-1.amazonaws.com)",
|
|
660
|
+
s3BucketInvalid:
|
|
661
|
+
"Le nom du bucket doit contenir 3–63 lettres minuscules, chiffres ou tirets",
|
|
662
|
+
s3RegionInvalid: 'Doit être une région valide (ex. us-east-1) ou "auto"',
|
|
658
663
|
apiSetup: "Configuration IA",
|
|
659
664
|
apiSetupDescription:
|
|
660
665
|
"Connectez l’IA avec les crédits gratuits Builder.io ou vos propres clés LLM.",
|
|
@@ -669,6 +674,8 @@ Tous les changements visibles par les utilisateurs de Clips sont documentés ici
|
|
|
669
674
|
providerKeysSet: "{{count}} définies",
|
|
670
675
|
checkingProviderKeys: "Vérification des clés fournisseur…",
|
|
671
676
|
keySet: "Définie",
|
|
677
|
+
keyCleared: "Identifiants de stockage effacés",
|
|
678
|
+
clearAllS3: "Effacer les identifiants",
|
|
672
679
|
replaceKey: "Remplacer la clé…",
|
|
673
680
|
pasteProviderKey: "Collez d’abord une clé fournisseur.",
|
|
674
681
|
apiKeySaved: "Clé API enregistrée",
|
|
@@ -635,6 +635,10 @@ Clips में उपयोगकर्ताओं को दिखने व
|
|
|
635
635
|
s3SecretAccessKeyLabel: "गुप्त एक्सेस कुंजी",
|
|
636
636
|
s3RegionLabel: "क्षेत्र",
|
|
637
637
|
s3PublicBaseUrlLabel: "सार्वजनिक बेस URL",
|
|
638
|
+
s3UrlInvalid:
|
|
639
|
+
"एक मान्य URL होना चाहिए (उदा. https://s3.us-east-1.amazonaws.com)",
|
|
640
|
+
s3BucketInvalid: "बकेट नाम 3–63 लोअरकेस अक्षर, अंक या हाइफ़न होने चाहिए",
|
|
641
|
+
s3RegionInvalid: 'एक मान्य क्षेत्र (उदा. us-east-1) या "auto" होना चाहिए',
|
|
638
642
|
apiSetup: "AI सेटअप",
|
|
639
643
|
apiSetupDescription:
|
|
640
644
|
"Builder.io मुफ्त क्रेडिट या अपनी LLM keys के साथ AI कनेक्ट करें.",
|
|
@@ -649,6 +653,8 @@ Clips में उपयोगकर्ताओं को दिखने व
|
|
|
649
653
|
providerKeysSet: "{{count}} सेट",
|
|
650
654
|
checkingProviderKeys: "प्रोवाइडर कीज़ जाँची जा रही हैं…",
|
|
651
655
|
keySet: "सेट",
|
|
656
|
+
keyCleared: "स्टोरेज क्रेडेंशियल साफ़ किए गए",
|
|
657
|
+
clearAllS3: "क्रेडेंशियल साफ़ करें",
|
|
652
658
|
replaceKey: "की बदलें…",
|
|
653
659
|
pasteProviderKey: "पहले प्रोवाइडर की पेस्ट करें।",
|
|
654
660
|
apiKeySaved: "API की सहेजी गई",
|
|
@@ -649,6 +649,12 @@ Clips のユーザー向けの主な変更はここに記録されます。コ
|
|
|
649
649
|
s3SecretAccessKeyLabel: "シークレットアクセスキー",
|
|
650
650
|
s3RegionLabel: "リージョン",
|
|
651
651
|
s3PublicBaseUrlLabel: "公開ベース URL",
|
|
652
|
+
s3UrlInvalid:
|
|
653
|
+
"有効な URL を入力してください(例: https://s3.us-east-1.amazonaws.com)",
|
|
654
|
+
s3BucketInvalid:
|
|
655
|
+
"バケット名は 3〜63 文字の小文字、数字、またはハイフンで指定してください",
|
|
656
|
+
s3RegionInvalid:
|
|
657
|
+
'有効なリージョン(例: us-east-1)または "auto" を入力してください',
|
|
652
658
|
apiSetup: "AI 設定",
|
|
653
659
|
apiSetupDescription:
|
|
654
660
|
"Builder.io の無料クレジット、または自分の LLM キーで AI を接続します。",
|
|
@@ -663,6 +669,8 @@ Clips のユーザー向けの主な変更はここに記録されます。コ
|
|
|
663
669
|
providerKeysSet: "{{count}} 件設定済み",
|
|
664
670
|
checkingProviderKeys: "プロバイダーキーを確認中…",
|
|
665
671
|
keySet: "設定済み",
|
|
672
|
+
keyCleared: "ストレージ認証情報をクリアしました",
|
|
673
|
+
clearAllS3: "認証情報をクリア",
|
|
666
674
|
replaceKey: "キーを置換…",
|
|
667
675
|
pasteProviderKey: "先にプロバイダーキーを貼り付けてください。",
|
|
668
676
|
apiKeySaved: "API キーを保存しました",
|
|
@@ -641,6 +641,11 @@ Clips의 모든 사용자 대상 변경 사항은 여기에 기록됩니다. 명
|
|
|
641
641
|
s3SecretAccessKeyLabel: "비밀 액세스 키",
|
|
642
642
|
s3RegionLabel: "리전",
|
|
643
643
|
s3PublicBaseUrlLabel: "공개 기본 URL",
|
|
644
|
+
s3UrlInvalid:
|
|
645
|
+
"유효한 URL이어야 합니다 (예: https://s3.us-east-1.amazonaws.com)",
|
|
646
|
+
s3BucketInvalid:
|
|
647
|
+
"버킷 이름은 3–63자의 소문자, 숫자 또는 하이픈이어야 합니다",
|
|
648
|
+
s3RegionInvalid: '유효한 리전(예: us-east-1) 또는 "auto"이어야 합니다',
|
|
644
649
|
apiSetup: "AI 설정",
|
|
645
650
|
apiSetupDescription:
|
|
646
651
|
"Builder.io 무료 크레딧 또는 직접 보유한 LLM 키로 AI를 연결하세요.",
|
|
@@ -655,6 +660,8 @@ Clips의 모든 사용자 대상 변경 사항은 여기에 기록됩니다. 명
|
|
|
655
660
|
providerKeysSet: "{{count}}개 설정됨",
|
|
656
661
|
checkingProviderKeys: "제공자 키 확인 중…",
|
|
657
662
|
keySet: "설정됨",
|
|
663
|
+
keyCleared: "스토리지 자격 증명이 삭제되었습니다",
|
|
664
|
+
clearAllS3: "자격 증명 삭제",
|
|
658
665
|
replaceKey: "키 바꾸기…",
|
|
659
666
|
pasteProviderKey: "먼저 제공자 키를 붙여넣으세요.",
|
|
660
667
|
apiKeySaved: "API 키가 저장됨",
|
|
@@ -652,6 +652,11 @@ Todas as mudanças visíveis para usuários do Clips são documentadas aqui. Voc
|
|
|
652
652
|
s3SecretAccessKeyLabel: "Chave de acesso secreta",
|
|
653
653
|
s3RegionLabel: "Região",
|
|
654
654
|
s3PublicBaseUrlLabel: "URL base pública",
|
|
655
|
+
s3UrlInvalid:
|
|
656
|
+
"Deve ser uma URL válida (ex.: https://s3.us-east-1.amazonaws.com)",
|
|
657
|
+
s3BucketInvalid:
|
|
658
|
+
"O nome do bucket deve ter 3–63 letras minúsculas, números ou hifens",
|
|
659
|
+
s3RegionInvalid: 'Deve ser uma região válida (ex.: us-east-1) ou "auto"',
|
|
655
660
|
apiSetup: "Configuração de IA",
|
|
656
661
|
apiSetupDescription:
|
|
657
662
|
"Conecte IA com créditos grátis da Builder.io ou suas próprias chaves LLM.",
|
|
@@ -666,6 +671,8 @@ Todas as mudanças visíveis para usuários do Clips são documentadas aqui. Voc
|
|
|
666
671
|
providerKeysSet: "{{count}} configuradas",
|
|
667
672
|
checkingProviderKeys: "Verificando chaves de provedor…",
|
|
668
673
|
keySet: "Configurada",
|
|
674
|
+
keyCleared: "Credenciais de armazenamento limpas",
|
|
675
|
+
clearAllS3: "Limpar credenciais",
|
|
669
676
|
replaceKey: "Substituir chave…",
|
|
670
677
|
pasteProviderKey: "Cole primeiro uma chave de provedor.",
|
|
671
678
|
apiKeySaved: "Chave de API salva",
|
|
@@ -614,6 +614,9 @@ Clips 中所有面向用户的重要更改都会记录在这里。你可以随
|
|
|
614
614
|
s3SecretAccessKeyLabel: "秘密访问密钥",
|
|
615
615
|
s3RegionLabel: "区域",
|
|
616
616
|
s3PublicBaseUrlLabel: "公共基础 URL",
|
|
617
|
+
s3UrlInvalid: "必须是有效的 URL(例如 https://s3.us-east-1.amazonaws.com)",
|
|
618
|
+
s3BucketInvalid: "存储桶名称必须为 3–63 个小写字母、数字或连字符",
|
|
619
|
+
s3RegionInvalid: '必须是有效的区域(例如 us-east-1)或 "auto"',
|
|
617
620
|
apiSetup: "AI 设置",
|
|
618
621
|
apiSetupDescription: "使用 Builder.io 免费额度或你自己的 LLM 密钥连接 AI。",
|
|
619
622
|
builderEasySetup: "Builder.io 免费额度",
|
|
@@ -626,6 +629,8 @@ Clips 中所有面向用户的重要更改都会记录在这里。你可以随
|
|
|
626
629
|
providerKeysSet: "已设置 {{count}} 个",
|
|
627
630
|
checkingProviderKeys: "正在检查提供方密钥…",
|
|
628
631
|
keySet: "已设置",
|
|
632
|
+
keyCleared: "存储凭证已清除",
|
|
633
|
+
clearAllS3: "清除凭证",
|
|
629
634
|
replaceKey: "替换密钥…",
|
|
630
635
|
pasteProviderKey: "请先粘贴提供方密钥。",
|
|
631
636
|
apiKeySaved: "API 密钥已保存",
|
|
@@ -607,6 +607,9 @@ const messages = {
|
|
|
607
607
|
s3SecretAccessKeyLabel: "秘密存取金鑰",
|
|
608
608
|
s3RegionLabel: "區域",
|
|
609
609
|
s3PublicBaseUrlLabel: "公開基礎 URL",
|
|
610
|
+
s3UrlInvalid: "必須是有效的 URL(例如 https://s3.us-east-1.amazonaws.com)",
|
|
611
|
+
s3BucketInvalid: "儲存貯體名稱必須為 3–63 個小寫字母、數字或連字號",
|
|
612
|
+
s3RegionInvalid: '必須是有效的區域(例如 us-east-1)或 "auto"',
|
|
610
613
|
apiSetup: "AI 設定",
|
|
611
614
|
apiSetupDescription: "使用 Builder.io 免費額度或您自己的 LLM 金鑰連線 AI。",
|
|
612
615
|
builderEasySetup: "Builder.io 免費額度",
|
|
@@ -619,6 +622,8 @@ const messages = {
|
|
|
619
622
|
providerKeysSet: "已設定 {{count}} 個",
|
|
620
623
|
checkingProviderKeys: "正在檢查提供方金鑰…",
|
|
621
624
|
keySet: "已設定",
|
|
625
|
+
keyCleared: "儲存憑證已清除",
|
|
626
|
+
clearAllS3: "清除憑證",
|
|
622
627
|
replaceKey: "替換金鑰…",
|
|
623
628
|
pasteProviderKey: "請先貼上提供方金鑰。",
|
|
624
629
|
apiKeySaved: "API 金鑰已儲存",
|
|
@@ -399,19 +399,23 @@ export default function SettingsIndexRoute() {
|
|
|
399
399
|
const [transcriptCleanupEnabled, setTranscriptCleanupEnabled] =
|
|
400
400
|
useState(true);
|
|
401
401
|
const [s3Values, setS3Values] = useState<Record<string, string>>({});
|
|
402
|
+
const [s3Errors, setS3Errors] = useState<Record<string, string>>({});
|
|
403
|
+
const [clearingS3, setClearingS3] = useState(false);
|
|
402
404
|
const [s3Expanded, setS3Expanded] = useState(false);
|
|
403
405
|
const [apiKeysExpanded, setApiKeysExpanded] = useState(false);
|
|
404
406
|
const [apiKeyValues, setApiKeyValues] = useState<Record<string, string>>({});
|
|
405
407
|
const [apiKeyStatus, setApiKeyStatus] = useState<Record<string, boolean>>({});
|
|
408
|
+
const [secretLast4, setSecretLast4] = useState<Record<string, string>>({});
|
|
406
409
|
const [apiKeyStatusLoading, setApiKeyStatusLoading] = useState(true);
|
|
407
410
|
const [savingApiKey, setSavingApiKey] = useState<string | null>(null);
|
|
408
411
|
|
|
409
412
|
const refreshApiKeyStatus = useCallback(async () => {
|
|
410
413
|
setApiKeyStatusLoading(true);
|
|
411
414
|
try {
|
|
412
|
-
const [envRes, secretsRes] = await Promise.all([
|
|
415
|
+
const [envRes, secretsRes, adhocRes] = await Promise.all([
|
|
413
416
|
fetch(agentNativePath("/_agent-native/env-status")),
|
|
414
417
|
fetch(agentNativePath("/_agent-native/secrets")),
|
|
418
|
+
fetch(agentNativePath("/_agent-native/secrets/adhoc")),
|
|
415
419
|
]);
|
|
416
420
|
const envData = envRes.ok
|
|
417
421
|
? ((await envRes.json()) as Array<{
|
|
@@ -425,12 +429,24 @@ export default function SettingsIndexRoute() {
|
|
|
425
429
|
status?: string;
|
|
426
430
|
}>)
|
|
427
431
|
: [];
|
|
432
|
+
const adhocData = adhocRes.ok
|
|
433
|
+
? ((await adhocRes.json()) as Array<{
|
|
434
|
+
name: string;
|
|
435
|
+
last4?: string;
|
|
436
|
+
}>)
|
|
437
|
+
: [];
|
|
428
438
|
const next = Object.fromEntries(
|
|
429
439
|
envData.map((entry) => [entry.key, Boolean(entry.configured)]),
|
|
430
440
|
);
|
|
431
441
|
for (const entry of secretsData) {
|
|
432
442
|
next[entry.key] = entry.status === "set";
|
|
433
443
|
}
|
|
444
|
+
const nextLast4: Record<string, string> = {};
|
|
445
|
+
for (const entry of adhocData) {
|
|
446
|
+
next[entry.name] = true;
|
|
447
|
+
if (entry.last4) nextLast4[entry.name] = entry.last4;
|
|
448
|
+
}
|
|
449
|
+
setSecretLast4(nextLast4);
|
|
434
450
|
setApiKeyStatus(next);
|
|
435
451
|
} catch {
|
|
436
452
|
setApiKeyStatus({});
|
|
@@ -477,7 +493,36 @@ export default function SettingsIndexRoute() {
|
|
|
477
493
|
}
|
|
478
494
|
}
|
|
479
495
|
|
|
496
|
+
function validateS3Values(
|
|
497
|
+
values: Record<string, string>,
|
|
498
|
+
): Record<string, string> {
|
|
499
|
+
const errors: Record<string, string> = {};
|
|
500
|
+
const urlFields = ["S3_ENDPOINT", "S3_PUBLIC_BASE_URL"];
|
|
501
|
+
for (const key of urlFields) {
|
|
502
|
+
const val = (values[key] ?? "").trim();
|
|
503
|
+
if (!val) continue;
|
|
504
|
+
try {
|
|
505
|
+
const parsed = new URL(val);
|
|
506
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
507
|
+
errors[key] = t("settings.s3UrlInvalid");
|
|
508
|
+
}
|
|
509
|
+
} catch {
|
|
510
|
+
errors[key] = t("settings.s3UrlInvalid");
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
const bucket = (values["S3_BUCKET"] ?? "").trim();
|
|
514
|
+
if (bucket && !/^[a-z0-9][a-z0-9\-.]{1,61}[a-z0-9]$/.test(bucket)) {
|
|
515
|
+
errors["S3_BUCKET"] = t("settings.s3BucketInvalid");
|
|
516
|
+
}
|
|
517
|
+
return errors;
|
|
518
|
+
}
|
|
519
|
+
|
|
480
520
|
async function handleSaveS3Storage() {
|
|
521
|
+
const validationErrors = validateS3Values(s3Values);
|
|
522
|
+
if (Object.keys(validationErrors).length > 0) {
|
|
523
|
+
setS3Errors(validationErrors);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
481
526
|
const s3Configured = storageStatus.data?.activeProvider?.id === "s3";
|
|
482
527
|
const missing = s3Configured
|
|
483
528
|
? []
|
|
@@ -499,7 +544,7 @@ export default function SettingsIndexRoute() {
|
|
|
499
544
|
...current,
|
|
500
545
|
S3_SECRET_ACCESS_KEY: "",
|
|
501
546
|
}));
|
|
502
|
-
await storageStatus.refetch();
|
|
547
|
+
await Promise.all([storageStatus.refetch(), refreshApiKeyStatus()]);
|
|
503
548
|
toast.success(t("settings.storageSaved"));
|
|
504
549
|
} catch (err) {
|
|
505
550
|
toast.error(
|
|
@@ -510,6 +555,51 @@ export default function SettingsIndexRoute() {
|
|
|
510
555
|
}
|
|
511
556
|
}
|
|
512
557
|
|
|
558
|
+
async function handleClearAllS3() {
|
|
559
|
+
setClearingS3(true);
|
|
560
|
+
try {
|
|
561
|
+
const results = await Promise.all(
|
|
562
|
+
S3_STORAGE_FIELDS.filter((field) => apiKeyStatus[field.key]).map(
|
|
563
|
+
async (field) => {
|
|
564
|
+
const res = await fetch(
|
|
565
|
+
agentNativePath(
|
|
566
|
+
`/_agent-native/secrets/adhoc/${encodeURIComponent(field.key)}`,
|
|
567
|
+
),
|
|
568
|
+
{ method: "DELETE" },
|
|
569
|
+
);
|
|
570
|
+
if (!res.ok) {
|
|
571
|
+
const body = (await res.json().catch(() => null)) as {
|
|
572
|
+
error?: string;
|
|
573
|
+
} | null;
|
|
574
|
+
throw new Error(
|
|
575
|
+
body?.error ?? `Failed to clear ${field.key} (${res.status})`,
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
const body = (await res.json().catch(() => null)) as {
|
|
579
|
+
removed?: boolean;
|
|
580
|
+
} | null;
|
|
581
|
+
return { key: field.key, removed: body?.removed !== false };
|
|
582
|
+
},
|
|
583
|
+
),
|
|
584
|
+
);
|
|
585
|
+
const failed = results.filter((r) => !r.removed).map((r) => r.key);
|
|
586
|
+
if (failed.length > 0) {
|
|
587
|
+
throw new Error(
|
|
588
|
+
`Could not remove: ${failed.join(", ")}. You may not have permission.`,
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
setS3Values({});
|
|
592
|
+
await Promise.all([refreshApiKeyStatus(), storageStatus.refetch()]);
|
|
593
|
+
toast.success(t("settings.keyCleared"));
|
|
594
|
+
} catch (err) {
|
|
595
|
+
toast.error(
|
|
596
|
+
err instanceof Error ? err.message : t("settings.saveFailed"),
|
|
597
|
+
);
|
|
598
|
+
} finally {
|
|
599
|
+
setClearingS3(false);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
513
603
|
async function handleSaveApiKey(key: string) {
|
|
514
604
|
const value = (apiKeyValues[key] ?? "").trim();
|
|
515
605
|
if (!value) {
|
|
@@ -785,34 +875,90 @@ export default function SettingsIndexRoute() {
|
|
|
785
875
|
<CollapsibleContent>
|
|
786
876
|
<div className="space-y-4 border-t border-border px-3 py-4">
|
|
787
877
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
788
|
-
{S3_STORAGE_FIELDS.map((field) =>
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
878
|
+
{S3_STORAGE_FIELDS.map((field) => {
|
|
879
|
+
const configured = Boolean(
|
|
880
|
+
apiKeyStatus[field.key],
|
|
881
|
+
);
|
|
882
|
+
const last4 = secretLast4[field.key];
|
|
883
|
+
return (
|
|
884
|
+
<div key={field.key} className="space-y-1.5">
|
|
885
|
+
<div className="flex items-center justify-between gap-2">
|
|
886
|
+
<Label htmlFor={field.key}>
|
|
887
|
+
{t(field.labelKey)}
|
|
888
|
+
</Label>
|
|
889
|
+
{configured ? (
|
|
890
|
+
<span className="flex items-center gap-1 text-[10px] font-medium text-primary">
|
|
891
|
+
<IconCheck className="h-3 w-3" />
|
|
892
|
+
{last4
|
|
893
|
+
? `••••${last4}`
|
|
894
|
+
: t("settings.keySet")}
|
|
895
|
+
</span>
|
|
896
|
+
) : null}
|
|
897
|
+
</div>
|
|
898
|
+
<Input
|
|
899
|
+
id={field.key}
|
|
900
|
+
type={
|
|
901
|
+
"secret" in field && field.secret
|
|
902
|
+
? "password"
|
|
903
|
+
: "text"
|
|
904
|
+
}
|
|
905
|
+
value={s3Values[field.key] ?? ""}
|
|
906
|
+
onChange={(event) => {
|
|
907
|
+
setS3Values((current) => ({
|
|
908
|
+
...current,
|
|
909
|
+
[field.key]: event.target.value,
|
|
910
|
+
}));
|
|
911
|
+
if (s3Errors[field.key]) {
|
|
912
|
+
setS3Errors((current) => {
|
|
913
|
+
const next = { ...current };
|
|
914
|
+
delete next[field.key];
|
|
915
|
+
return next;
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
}}
|
|
919
|
+
placeholder={
|
|
920
|
+
configured
|
|
921
|
+
? t("settings.replaceKey")
|
|
922
|
+
: field.placeholder
|
|
923
|
+
}
|
|
924
|
+
autoComplete="off"
|
|
925
|
+
disabled={savingStorage}
|
|
926
|
+
className={
|
|
927
|
+
s3Errors[field.key]
|
|
928
|
+
? "border-destructive"
|
|
929
|
+
: undefined
|
|
930
|
+
}
|
|
931
|
+
/>
|
|
932
|
+
{s3Errors[field.key] ? (
|
|
933
|
+
<p className="text-[11px] text-destructive">
|
|
934
|
+
{s3Errors[field.key]}
|
|
935
|
+
</p>
|
|
936
|
+
) : null}
|
|
937
|
+
</div>
|
|
938
|
+
);
|
|
939
|
+
})}
|
|
813
940
|
</div>
|
|
814
941
|
|
|
815
|
-
<div className="flex justify-end">
|
|
942
|
+
<div className="flex items-center justify-end gap-2">
|
|
943
|
+
{S3_STORAGE_FIELDS.some(
|
|
944
|
+
(field) => apiKeyStatus[field.key],
|
|
945
|
+
) ? (
|
|
946
|
+
<Button
|
|
947
|
+
type="button"
|
|
948
|
+
variant="ghost"
|
|
949
|
+
size="sm"
|
|
950
|
+
onClick={handleClearAllS3}
|
|
951
|
+
disabled={clearingS3 || savingStorage}
|
|
952
|
+
className="text-muted-foreground hover:text-destructive"
|
|
953
|
+
>
|
|
954
|
+
{clearingS3 ? (
|
|
955
|
+
<IconLoader2 className="h-4 w-4 animate-spin" />
|
|
956
|
+
) : (
|
|
957
|
+
<IconTrash className="h-4 w-4" />
|
|
958
|
+
)}
|
|
959
|
+
{t("settings.clearAllS3")}
|
|
960
|
+
</Button>
|
|
961
|
+
) : null}
|
|
816
962
|
<Button
|
|
817
963
|
onClick={handleSaveS3Storage}
|
|
818
964
|
disabled={
|
|
@@ -462,6 +462,16 @@ function uploadTooLargeMessage(size: number, detail?: string): string {
|
|
|
462
462
|
)}) after automatic compression. Trim or export a shorter copy and upload again.`;
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
+
/** Pre-upload size rejection for a picked file — no compression has been
|
|
466
|
+
* attempted yet, so the message must not imply it has. */
|
|
467
|
+
function fileTooLargeMessage(size: number): string {
|
|
468
|
+
return `This file is too large to upload (${formatMb(
|
|
469
|
+
size,
|
|
470
|
+
)}, limit is ${formatMb(
|
|
471
|
+
MAX_UPLOAD_BYTES,
|
|
472
|
+
)}). Trim it or export a shorter copy and try again.`;
|
|
473
|
+
}
|
|
474
|
+
|
|
465
475
|
function isUploadFailureError(error: string): boolean {
|
|
466
476
|
return (
|
|
467
477
|
isUploadSizeError(error) ||
|
|
@@ -471,7 +481,7 @@ function isUploadFailureError(error: string): boolean {
|
|
|
471
481
|
|
|
472
482
|
function friendlyRecordingErrorMessage(error: string): string {
|
|
473
483
|
if (isUploadSizeError(error)) {
|
|
474
|
-
return `This video is too large for Clips
|
|
484
|
+
return `This video is too large for Clips. Trim or export a shorter copy under ${formatMb(
|
|
475
485
|
MAX_UPLOAD_BYTES,
|
|
476
486
|
)} and upload again.`;
|
|
477
487
|
}
|
|
@@ -835,6 +845,12 @@ export default function RecordRoute() {
|
|
|
835
845
|
const [compressionProgress, setCompressionProgress] = useState<number | null>(
|
|
836
846
|
null,
|
|
837
847
|
);
|
|
848
|
+
// Fraction (0-1) of upload chunks confirmed sent so far. Chunks are fixed-size
|
|
849
|
+
// slices of the already-recorded blob, so chunksSent / totalChunks is a
|
|
850
|
+
// truthful proxy for bytes uploaded — not simulated. Null means the total
|
|
851
|
+
// chunk count isn't known yet (e.g. the brief live-streaming remainder
|
|
852
|
+
// upload), so the overlay falls back to an indeterminate spinner.
|
|
853
|
+
const [uploadProgress, setUploadProgress] = useState<number | null>(null);
|
|
838
854
|
|
|
839
855
|
const queryClient = useQueryClient();
|
|
840
856
|
const { isDesktopApp } = useDesktopPromo();
|
|
@@ -1103,19 +1119,29 @@ export default function RecordRoute() {
|
|
|
1103
1119
|
// upload — applies whether or not we just came from
|
|
1104
1120
|
// compressing.
|
|
1105
1121
|
setCompressionProgress(null);
|
|
1122
|
+
// Reset upload progress at the start of each upload attempt so
|
|
1123
|
+
// a retry doesn't briefly show the previous attempt's percent.
|
|
1124
|
+
setUploadProgress(null);
|
|
1106
1125
|
// Always sync the UI back to "uploading"; if we were already
|
|
1107
1126
|
// there from doStop's pre-stop transition, this is a no-op.
|
|
1108
1127
|
setUiState("uploading");
|
|
1109
1128
|
}
|
|
1110
1129
|
},
|
|
1111
|
-
onChunk: ({ index,
|
|
1130
|
+
onChunk: ({ index, total }) => {
|
|
1131
|
+
// `total` is only known once the full recording is sliced into
|
|
1132
|
+
// fixed-size chunks after stop(); the live per-chunk uploads
|
|
1133
|
+
// during recording report `total: null` and don't drive this bar.
|
|
1134
|
+
const fraction = total ? (index + 1) / total : null;
|
|
1135
|
+
setUploadProgress(fraction);
|
|
1112
1136
|
const recordingId = pendingRef.current?.id;
|
|
1113
1137
|
if (!recordingId) return;
|
|
1138
|
+
// Only expose a percentage here — this state is agent-visible, and
|
|
1139
|
+
// chunk/byte counts are an internal transport detail, not
|
|
1140
|
+
// something to surface to the user.
|
|
1114
1141
|
void writeAppState(`recording-upload-${recordingId}`, {
|
|
1115
1142
|
recordingId,
|
|
1116
1143
|
status: "uploading",
|
|
1117
|
-
|
|
1118
|
-
lastChunkBytes: bytes,
|
|
1144
|
+
progress: fraction !== null ? Math.round(fraction * 100) : null,
|
|
1119
1145
|
updatedAt: new Date().toISOString(),
|
|
1120
1146
|
}).catch(() => {});
|
|
1121
1147
|
},
|
|
@@ -1378,6 +1404,7 @@ export default function RecordRoute() {
|
|
|
1378
1404
|
setError(null);
|
|
1379
1405
|
setUiState("uploading");
|
|
1380
1406
|
setCompressionProgress(null);
|
|
1407
|
+
setUploadProgress(null);
|
|
1381
1408
|
|
|
1382
1409
|
const acceptedMime = new Set([
|
|
1383
1410
|
"video/mp4",
|
|
@@ -1406,6 +1433,22 @@ export default function RecordRoute() {
|
|
|
1406
1433
|
return;
|
|
1407
1434
|
}
|
|
1408
1435
|
|
|
1436
|
+
// Fail fast on oversized files before we probe metadata, attempt
|
|
1437
|
+
// compression, or open the upload session — no point spending time or
|
|
1438
|
+
// chunking bytes for a file the server will reject anyway. Uses the
|
|
1439
|
+
// same MAX_UPLOAD_BYTES ceiling as the (currently compression-gated)
|
|
1440
|
+
// post-compression check below and the server chunk/finalize routes.
|
|
1441
|
+
if (file.size > MAX_UPLOAD_BYTES) {
|
|
1442
|
+
const message = fileTooLargeMessage(file.size);
|
|
1443
|
+
if (fileUploadAbortRef.current === abort) {
|
|
1444
|
+
fileUploadAbortRef.current = null;
|
|
1445
|
+
}
|
|
1446
|
+
setError(message);
|
|
1447
|
+
setUiState("error");
|
|
1448
|
+
toast.error(message);
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1409
1452
|
let createdId: string | null = null;
|
|
1410
1453
|
try {
|
|
1411
1454
|
const status = await fetchVideoStorageStatus();
|
|
@@ -1628,6 +1671,7 @@ export default function RecordRoute() {
|
|
|
1628
1671
|
unknown
|
|
1629
1672
|
> | null) ?? null;
|
|
1630
1673
|
}
|
|
1674
|
+
setUploadProgress((i + 1) / totalChunks);
|
|
1631
1675
|
}
|
|
1632
1676
|
|
|
1633
1677
|
setUiState("complete");
|
|
@@ -1699,6 +1743,7 @@ export default function RecordRoute() {
|
|
|
1699
1743
|
fileUploadAbortRef.current = null;
|
|
1700
1744
|
}
|
|
1701
1745
|
setCompressionProgress(null);
|
|
1746
|
+
setUploadProgress(null);
|
|
1702
1747
|
}
|
|
1703
1748
|
},
|
|
1704
1749
|
[markStorageConfigured, navigate, probeVideoMetadata],
|
|
@@ -1863,6 +1908,7 @@ export default function RecordRoute() {
|
|
|
1863
1908
|
setCameraStream(null);
|
|
1864
1909
|
setPreviewStream(null);
|
|
1865
1910
|
setCompressionProgress(null);
|
|
1911
|
+
setUploadProgress(null);
|
|
1866
1912
|
setUiState("complete");
|
|
1867
1913
|
if (result.waitingForStorage) {
|
|
1868
1914
|
toast.info(t("recordRoute.recordingReadyToUpload"), {
|
|
@@ -2006,6 +2052,7 @@ export default function RecordRoute() {
|
|
|
2006
2052
|
|
|
2007
2053
|
setError(null);
|
|
2008
2054
|
setCompressionProgress(null);
|
|
2055
|
+
setUploadProgress(null);
|
|
2009
2056
|
setUiState("uploading");
|
|
2010
2057
|
try {
|
|
2011
2058
|
const retryResult = await engine.retryUpload();
|
|
@@ -2022,6 +2069,7 @@ export default function RecordRoute() {
|
|
|
2022
2069
|
body: JSON.stringify({ reason: message }),
|
|
2023
2070
|
}).catch(() => {});
|
|
2024
2071
|
setCompressionProgress(null);
|
|
2072
|
+
setUploadProgress(null);
|
|
2025
2073
|
setError(message);
|
|
2026
2074
|
setUiState("error");
|
|
2027
2075
|
toast.error(t("recordRoute.uploadFailed"), {
|
|
@@ -2085,6 +2133,7 @@ export default function RecordRoute() {
|
|
|
2085
2133
|
setPreviewStream(null);
|
|
2086
2134
|
setIsPaused(false);
|
|
2087
2135
|
setUiState("idle");
|
|
2136
|
+
setUploadProgress(null);
|
|
2088
2137
|
pendingRef.current = null;
|
|
2089
2138
|
engineRef.current = null;
|
|
2090
2139
|
}, [extensionCapture, liveTranscription]);
|
|
@@ -2427,7 +2476,9 @@ export default function RecordRoute() {
|
|
|
2427
2476
|
users wonder if the app froze). */}
|
|
2428
2477
|
{(uiState === "uploading" || uiState === "compressing") && (
|
|
2429
2478
|
<div className="fixed inset-0 z-[120] flex flex-col items-center justify-center gap-3 bg-black/70 text-white backdrop-blur">
|
|
2430
|
-
|
|
2479
|
+
{!(uiState === "uploading" && uploadProgress !== null) && (
|
|
2480
|
+
<Spinner className="h-10 w-10 text-white/70" />
|
|
2481
|
+
)}
|
|
2431
2482
|
{uiState === "compressing" ? (
|
|
2432
2483
|
<>
|
|
2433
2484
|
<div className="text-sm">
|
|
@@ -2441,7 +2492,24 @@ export default function RecordRoute() {
|
|
|
2441
2492
|
</div>
|
|
2442
2493
|
</>
|
|
2443
2494
|
) : (
|
|
2444
|
-
|
|
2495
|
+
<>
|
|
2496
|
+
<div className="text-sm">{t("recordRoute.savingRecording")}</div>
|
|
2497
|
+
{uploadProgress !== null && (
|
|
2498
|
+
<div className="flex w-48 flex-col items-center gap-1">
|
|
2499
|
+
<div className="h-1.5 w-full overflow-hidden rounded-full bg-white/20">
|
|
2500
|
+
<div
|
|
2501
|
+
className="h-full rounded-full bg-white transition-all"
|
|
2502
|
+
style={{
|
|
2503
|
+
width: `${Math.min(100, Math.max(0, Math.round(uploadProgress * 100)))}%`,
|
|
2504
|
+
}}
|
|
2505
|
+
/>
|
|
2506
|
+
</div>
|
|
2507
|
+
<div className="text-[11px] text-white/50">
|
|
2508
|
+
{Math.round(uploadProgress * 100)}%
|
|
2509
|
+
</div>
|
|
2510
|
+
</div>
|
|
2511
|
+
)}
|
|
2512
|
+
</>
|
|
2445
2513
|
)}
|
|
2446
2514
|
<button
|
|
2447
2515
|
onClick={doCancel}
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -41,8 +41,8 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
|
|
|
41
41
|
* Body: { text: string, fieldName?: string, requestSource?: string }
|
|
42
42
|
*/
|
|
43
43
|
export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
44
|
-
text?: undefined;
|
|
45
44
|
ok?: undefined;
|
|
45
|
+
text?: undefined;
|
|
46
46
|
error: string;
|
|
47
47
|
} | {
|
|
48
48
|
error?: undefined;
|
|
@@ -42,16 +42,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
44
|
ok?: undefined;
|
|
45
|
-
error?: undefined;
|
|
46
45
|
summary: import("./types.js").TraceSummary;
|
|
47
46
|
spans: import("./types.js").TraceSpan[];
|
|
47
|
+
error?: undefined;
|
|
48
48
|
id?: undefined;
|
|
49
49
|
} | {
|
|
50
50
|
ok?: undefined;
|
|
51
|
-
error?: undefined;
|
|
52
51
|
summary?: undefined;
|
|
53
52
|
spans?: undefined;
|
|
54
53
|
id: string;
|
|
54
|
+
error?: undefined;
|
|
55
55
|
} | {
|
|
56
56
|
ok?: undefined;
|
|
57
57
|
summary?: undefined;
|
|
@@ -59,10 +59,10 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
59
59
|
error: any;
|
|
60
60
|
id?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
64
|
ok: boolean;
|
|
65
|
+
error?: undefined;
|
|
66
66
|
id?: undefined;
|
|
67
67
|
}>>;
|
|
68
68
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -49,8 +49,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
49
49
|
}>;
|
|
50
50
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
51
51
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
52
|
-
error: string;
|
|
53
52
|
ok?: undefined;
|
|
53
|
+
error: string;
|
|
54
54
|
} | {
|
|
55
55
|
error?: undefined;
|
|
56
56
|
ok: boolean;
|
|
@@ -27,11 +27,11 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
-
error?: undefined;
|
|
31
30
|
ok: boolean;
|
|
32
31
|
key: string;
|
|
33
32
|
baseUrlKey?: string;
|
|
34
33
|
scope: AgentEngineApiKeyScope;
|
|
34
|
+
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
37
37
|
//# sourceMappingURL=agent-engine-api-key-route.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-handler.d.ts","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"AA8CA,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"ssr-handler.d.ts","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"AA8CA,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AAooBpC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,2FAgE5E"}
|
|
@@ -195,8 +195,7 @@ function applyDefaultSpeculationRulesHeader(headers, status, basePath) {
|
|
|
195
195
|
* Extract the plain JS body from a `<script ...>body</script>` string.
|
|
196
196
|
* Returns `null` if the input is falsy or has no recognisable `</script>` end.
|
|
197
197
|
* Used to compute the sha256 hash of framework-injected inline scripts so the
|
|
198
|
-
* hash can be listed in
|
|
199
|
-
* `'unsafe-inline'`.
|
|
198
|
+
* hash can be listed in app-owned `script-src` CSP directives.
|
|
200
199
|
*/
|
|
201
200
|
function extractScriptBody(scriptTag) {
|
|
202
201
|
if (!scriptTag)
|
|
@@ -394,13 +393,10 @@ function augmentExistingReportOnlyCspForFrameworkScripts(policy, options) {
|
|
|
394
393
|
*
|
|
395
394
|
* A third directive, `script-src`, is emitted via `Content-Security-Policy-
|
|
396
395
|
* Report-Only` rather than enforced when the app has no existing document CSP.
|
|
397
|
-
* The framework injects
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
* Google Tag Manager / GA4 from `GA_CSP_SCRIPT_HOSTS`. All of those are listed
|
|
402
|
-
* here so the report-only policy reflects the code the framework itself injects
|
|
403
|
-
* instead of reporting a violation on every page load.
|
|
396
|
+
* The framework injects inline scripts for analytics, Sentry, and template
|
|
397
|
+
* setup, and hosted apps need Google Tag Manager to load without noisy CSP
|
|
398
|
+
* diagnostics. The report-only policy is intentionally permissive for scripts:
|
|
399
|
+
* it includes `'unsafe-inline'` plus the known GA/GTM loader hosts.
|
|
404
400
|
*
|
|
405
401
|
* If an app or host already sends an enforced CSP with `script-src`,
|
|
406
402
|
* `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge only
|
|
@@ -425,20 +421,22 @@ function applyDocumentCsp(headers, sentryScript) {
|
|
|
425
421
|
return;
|
|
426
422
|
if (process.env.AGENT_NATIVE_DISABLE_DOC_CSP === "1")
|
|
427
423
|
return;
|
|
428
|
-
// script-src as Report-Only:
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
// itself injects, so listing them keeps the report-only policy from flagging
|
|
432
|
-
// GA on every page load (and keeps it safe to graduate to enforcement).
|
|
433
|
-
// Template theme-init hashes are NOT included here — see function comment.
|
|
424
|
+
// script-src as Report-Only: keep this deliberately loose so the framework's
|
|
425
|
+
// injected analytics and template bootstrap scripts do not look blocked in
|
|
426
|
+
// browser diagnostics.
|
|
434
427
|
const sentryBody = extractScriptBody(sentryScript);
|
|
435
428
|
const sentryHash = sentryBody ? computeInlineScriptHash(sentryBody) : null;
|
|
436
429
|
const gaInlineBody = getGaInlineConfigScriptBody();
|
|
437
430
|
const gaHash = gaInlineBody ? computeInlineScriptHash(gaInlineBody) : null;
|
|
438
431
|
const gaHosts = gaInlineBody ? [...GA_CSP_SCRIPT_HOSTS] : [];
|
|
439
|
-
const gaScriptSrcTokens = [
|
|
432
|
+
const gaScriptSrcTokens = [
|
|
433
|
+
"'unsafe-inline'",
|
|
434
|
+
...(gaHash ? [gaHash] : []),
|
|
435
|
+
...gaHosts,
|
|
436
|
+
];
|
|
440
437
|
const scriptSrcTokens = [
|
|
441
438
|
"'self'",
|
|
439
|
+
"'unsafe-inline'",
|
|
442
440
|
...(sentryHash ? [sentryHash] : []),
|
|
443
441
|
...(gaHash ? [gaHash] : []),
|
|
444
442
|
...gaHosts,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-handler.js","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineEventHandler } from \"h3\";\n/**\n * Shared SSR catch-all handler for React Router framework mode.\n *\n * Templates wire this up via:\n *\n * // server/routes/[...page].get.ts\n * import { createH3SSRHandler } from \"@agent-native/core/server/ssr-handler\";\n * export default createH3SSRHandler(\n * () => import(\"virtual:react-router/server-build\"),\n * );\n *\n * The `getBuild` callback MUST live in the template's own source so Vite's\n * @react-router/dev plugin can resolve the `virtual:` module. Pulling the\n * import into core (e.g. via a re-export) puts it in node_modules where\n * Vite's SSR externalizer leaves it untouched and Node's ESM loader rejects\n * the unknown scheme — silently 302'ing every request to \"/\".\n */\nimport { createRequestHandler } from \"react-router\";\n\nimport {\n DEFAULT_SSR_CACHE_HEADERS,\n DEFAULT_SPECULATION_RULES_PATH,\n} from \"../shared/cache-control.js\";\nimport {\n AGENT_NATIVE_SOCIAL_IMAGE_ALT,\n AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT,\n AGENT_NATIVE_SOCIAL_IMAGE_PATH,\n AGENT_NATIVE_SOCIAL_IMAGE_TYPE,\n AGENT_NATIVE_SOCIAL_IMAGE_WIDTH,\n withAgentNativeSocialImageCacheBuster,\n} from \"../shared/social-meta.js\";\nimport {\n GA_CSP_CONNECT_HOSTS,\n GA_CSP_IMG_HOSTS,\n GA_CSP_SCRIPT_HOSTS,\n getGaInlineConfigScriptBody,\n} from \"./analytics.js\";\nimport {\n getAppBasePathFromViteEnv,\n stripAppBasePath as canonicalStripAppBasePath,\n} from \"./app-base-path.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { computeInlineScriptHash } from \"./security-headers.js\";\nimport { getSentryClientConfigScript } from \"./sentry-config.js\";\n\nexport {\n DEFAULT_SSR_CACHE_HEADERS,\n DEFAULT_SPECULATION_RULES_HEADER,\n DEFAULT_SSR_CACHE_CONTROL,\n} from \"../shared/cache-control.js\";\n\nfunction getAppBasePath(): string {\n return getAppBasePathFromViteEnv();\n}\n\nfunction stripAppBasePath(pathname: string): string {\n return canonicalStripAppBasePath(pathname, getAppBasePath());\n}\n\nfunction stripBasePath(pathname: string, basePath: string): string {\n if (!basePath) return pathname;\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(`${basePath}/`)) {\n return pathname.slice(basePath.length) || \"/\";\n }\n return pathname;\n}\n\nfunction requestWithPathname(\n request: Request,\n pathname: string,\n basePath: string,\n): Request {\n const url = new URL(request.url);\n let changed = false;\n if (basePath && pathname === \"/__manifest\") {\n const paths = url.searchParams.get(\"paths\");\n if (paths) {\n const strippedPaths = paths\n .split(\",\")\n .map((path) => stripBasePath(path, basePath))\n .join(\",\");\n if (strippedPaths !== paths) {\n url.searchParams.set(\"paths\", strippedPaths);\n changed = true;\n }\n }\n }\n if (url.pathname !== pathname) {\n url.pathname = pathname;\n changed = true;\n }\n if (!changed) return request;\n const init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers: request.headers,\n signal: request.signal,\n };\n if (request.body && ![\"GET\", \"HEAD\"].includes(request.method.toUpperCase())) {\n init.body = request.body;\n init.duplex = \"half\";\n }\n return new Request(url, init);\n}\n\nfunction prefixMountedPath(path: string, basePath: string): string {\n if (!basePath || !path.startsWith(\"/\") || path.startsWith(\"//\")) return path;\n if (path === basePath || path.startsWith(`${basePath}/`)) return path;\n return `${basePath}${path}`;\n}\n\nfunction prefixMountedHtml(html: string, basePath: string): string {\n if (!basePath) return html;\n return html\n .replace(\n /\\b(href|src|action|formaction|poster)=([\"'])(\\/(?!\\/)[^\"']*)\\2/g,\n (_match, attr: string, quote: string, path: string) =>\n `${attr}=${quote}${prefixMountedPath(path, basePath)}${quote}`,\n )\n .replace(/url\\(([\"']?)(\\/(?!\\/)[^)'\" ]+)\\1\\)/g, (_match, quote, path) => {\n const q = quote || \"\";\n return `url(${q}${prefixMountedPath(path, basePath)}${q})`;\n });\n}\n\nfunction injectHeadScript(html: string, script: string | null): string {\n if (!script) return html;\n const headCloseIdx = html.indexOf(\"</head>\");\n if (headCloseIdx === -1) return html;\n return html.slice(0, headCloseIdx) + script + html.slice(headCloseIdx);\n}\n\nconst OG_IMAGE_META_RE = /<meta\\b(?=[^>]*\\bproperty=([\"'])og:image\\1)[^>]*>/i;\nconst TWITTER_CARD_META_RE =\n /<meta\\b(?=[^>]*\\bname=([\"'])twitter:card\\1)[^>]*>/i;\nconst TWITTER_IMAGE_META_RE =\n /<meta\\b(?=[^>]*\\bname=([\"'])twitter:image\\1)[^>]*>/i;\n\nfunction defaultSocialImageUrl(requestUrl: string, basePath: string): string {\n return withAgentNativeSocialImageCacheBuster(\n new URL(\n prefixMountedPath(AGENT_NATIVE_SOCIAL_IMAGE_PATH, basePath),\n requestUrl,\n ).toString(),\n );\n}\n\nfunction injectDefaultSocialImageMeta(html: string, imageUrl: string): string {\n const headCloseIdx = html.indexOf(\"</head>\");\n if (headCloseIdx === -1) return html;\n\n const hasAnySocialImage =\n OG_IMAGE_META_RE.test(html) || TWITTER_IMAGE_META_RE.test(html);\n const tags: string[] = [];\n\n if (!hasAnySocialImage) {\n tags.push(`<meta property=\"og:image\" content=\"${imageUrl}\">`);\n tags.push(`<meta property=\"og:image:secure_url\" content=\"${imageUrl}\">`);\n tags.push(\n `<meta property=\"og:image:type\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_TYPE}\">`,\n );\n tags.push(\n `<meta property=\"og:image:width\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_WIDTH}\">`,\n );\n tags.push(\n `<meta property=\"og:image:height\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT}\">`,\n );\n tags.push(\n `<meta property=\"og:image:alt\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_ALT}\">`,\n );\n }\n if (!TWITTER_CARD_META_RE.test(html)) {\n tags.push(`<meta name=\"twitter:card\" content=\"summary_large_image\">`);\n }\n if (!hasAnySocialImage) {\n tags.push(`<meta name=\"twitter:image\" content=\"${imageUrl}\">`);\n tags.push(\n `<meta name=\"twitter:image:alt\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_ALT}\">`,\n );\n }\n\n if (tags.length === 0) return html;\n return html.slice(0, headCloseIdx) + tags.join(\"\") + html.slice(headCloseIdx);\n}\n\nfunction isSsrHtmlOrDataResponse(\n headers: Headers,\n status: number,\n pathname: string,\n): boolean {\n if (status < 200 || status >= 400) return false;\n const contentType = headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n if (contentType.includes(\"text/html\")) return true;\n return pathname.endsWith(\".data\") && contentType.includes(\"text/x-script\");\n}\n\n/**\n * Apply the SSR cache policy to the response headers.\n *\n * ┌──────────────────────────────────────────────────────────────────────────┐\n * │ SSR IS A PUBLIC, HARD-CDN-CACHED SHELL — SERVED IDENTICALLY TO EVERYONE. │\n * │ │\n * │ Every SSR HTML / React Router `.data` response gets the same public │\n * │ stale-while-revalidate policy for ALL visitors, authenticated or not, so │\n * │ the edge serves one shared copy and never stampedes origin. │\n * │ │\n * │ DO NOT reintroduce per-user / cookie-based cache variation here (no │\n * │ `private`, no `no-store`, no `Vary: Cookie`, no \"authenticated → don't │\n * │ cache\" branch). That makes pages uncacheable for every logged-in visitor, │\n * │ which is slow and expensive — exactly the regression this guardrail │\n * │ prevents. The reason it is SAFE to hard-cache is that the SSR response is │\n * │ impersonal: `createH3SSRHandler` renders without reading the request's │\n * │ session/cookies, so there is no per-user data baked into the HTML. ALL │\n * │ per-user state (who's logged in, private records, access checks) is │\n * │ resolved CLIENT-SIDE after load. Keep it that way: if you need the SSR │\n * │ output to differ per user, the fix is to move that work client-side, not │\n * │ to disable caching here. │\n * └──────────────────────────────────────────────────────────────────────────┘\n */\nfunction applyDefaultSsrCacheHeader(\n headers: Headers,\n status: number,\n pathname: string,\n) {\n if (!isSsrHtmlOrDataResponse(headers, status, pathname)) return;\n\n // Netlify Functions/proxies are not cached by default. Set all three cache\n // headers: Cache-Control for browsers, CDN-Cache-Control for generic CDNs,\n // and Netlify-CDN-Cache-Control (with durable) so Netlify's shared cache\n // actually serves SSR HTML/.data from the edge instead of forwarding every\n // request to origin — for every visitor, authenticated or not.\n for (const [name, value] of Object.entries(DEFAULT_SSR_CACHE_HEADERS)) {\n headers.set(name, value);\n }\n}\n\nfunction applyDefaultSpeculationRulesHeader(\n headers: Headers,\n status: number,\n basePath: string,\n) {\n if (status < 200 || status >= 400) return;\n if (headers.has(\"speculation-rules\")) return;\n\n const contentType = headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n if (!contentType.includes(\"text/html\")) return;\n\n // Cloudflare Speed Brain injects its own Speculation-Rules header when the\n // origin omits one. Those browser prefetches carry `Sec-Purpose: prefetch`,\n // and Cloudflare refuses cache-ineligible dynamic pages with a 503 before\n // the request can reach Netlify/origin. We publish an explicit no-op ruleset\n // by default so Cloudflare does not inject its edge prefetch rules. Preserve\n // an app-provided Speculation-Rules header above if a template deliberately\n // owns this behavior.\n const rulesPath = prefixMountedPath(DEFAULT_SPECULATION_RULES_PATH, basePath);\n headers.set(\"speculation-rules\", `\"${rulesPath}\"`);\n}\n\n/**\n * Extract the plain JS body from a `<script ...>body</script>` string.\n * Returns `null` if the input is falsy or has no recognisable `</script>` end.\n * Used to compute the sha256 hash of framework-injected inline scripts so the\n * hash can be listed in the `script-src` CSP directive without relying on\n * `'unsafe-inline'`.\n */\nfunction extractScriptBody(scriptTag: string | null): string | null {\n if (!scriptTag) return null;\n const start = scriptTag.indexOf(\">\") + 1;\n const end = scriptTag.lastIndexOf(\"</script>\");\n if (start <= 0 || end < start) return null;\n return scriptTag.slice(start, end);\n}\n\ntype CspDirective = {\n name: string;\n tokens: string[];\n};\n\nconst CSP_DIRECTIVES_WITH_VALUE_TOKENS = new Set([\n \"base-uri\",\n \"block-all-mixed-content\",\n \"child-src\",\n \"connect-src\",\n \"default-src\",\n \"fenced-frame-src\",\n \"font-src\",\n \"form-action\",\n \"frame-ancestors\",\n \"frame-src\",\n \"img-src\",\n \"manifest-src\",\n \"media-src\",\n \"navigate-to\",\n \"object-src\",\n \"plugin-types\",\n \"prefetch-src\",\n \"referrer\",\n \"reflected-xss\",\n \"require-sri-for\",\n \"require-trusted-types-for\",\n \"report-to\",\n \"report-uri\",\n \"sandbox\",\n \"script-src\",\n \"script-src-attr\",\n \"script-src-elem\",\n \"style-src\",\n \"style-src-attr\",\n \"style-src-elem\",\n \"trusted-types\",\n \"upgrade-insecure-requests\",\n \"webrtc\",\n \"worker-src\",\n]);\n\nfunction hasCommaJoinedCspPolicies(policy: string): boolean {\n let commaIndex = policy.indexOf(\",\");\n while (commaIndex !== -1) {\n const afterComma = policy.slice(commaIndex + 1);\n const directive = /^\\s+([a-z][a-z0-9-]*)(?=\\s|;|$)/i.exec(afterComma)?.[1];\n if (\n directive &&\n CSP_DIRECTIVES_WITH_VALUE_TOKENS.has(directive.toLowerCase())\n ) {\n return true;\n }\n commaIndex = policy.indexOf(\",\", commaIndex + 1);\n }\n return false;\n}\n\nfunction parseCsp(policy: string): CspDirective[] {\n return policy\n .split(\";\")\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => {\n const [name = \"\", ...tokens] = part.split(/\\s+/);\n return { name: name.toLowerCase(), tokens };\n })\n .filter((directive) => directive.name);\n}\n\nfunction serializeCsp(directives: CspDirective[]): string {\n return directives\n .map((directive) =>\n [directive.name, ...directive.tokens].filter(Boolean).join(\" \"),\n )\n .join(\"; \");\n}\n\nfunction appendCspTokens(\n tokens: string[],\n additions: readonly string[],\n): string[] {\n if (!additions.length) return tokens;\n const next = tokens.filter((token) => token !== \"'none'\");\n const seen = new Set(next);\n for (const token of additions) {\n if (!token || seen.has(token)) continue;\n next.push(token);\n seen.add(token);\n }\n return next;\n}\n\nfunction findCspDirective(\n directives: CspDirective[],\n name: string,\n): CspDirective | undefined {\n return directives.find((directive) => directive.name === name);\n}\n\nfunction appendToExistingOrDefaultCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): void {\n if (!additions.length) return;\n const existing = findCspDirective(directives, name);\n if (existing) {\n existing.tokens = appendCspTokens(existing.tokens, additions);\n return;\n }\n\n const defaultSrc = findCspDirective(directives, \"default-src\");\n if (!defaultSrc) return;\n directives.push({\n name,\n tokens: appendCspTokens([...defaultSrc.tokens], additions),\n });\n}\n\nfunction appendToExistingCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): void {\n const existing = findCspDirective(directives, name);\n if (!existing) return;\n existing.tokens = appendCspTokens(existing.tokens, additions);\n}\n\nfunction ensureCspDirective(\n directives: CspDirective[],\n name: string,\n tokens: readonly string[],\n): void {\n if (findCspDirective(directives, name)) return;\n directives.push({ name, tokens: [...tokens] });\n}\n\nfunction hasStrictNonceScriptPolicy(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) => token === \"'strict-dynamic'\" || token.startsWith(\"'nonce-\"),\n );\n}\n\nfunction appendToScriptCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): boolean {\n const existing = findCspDirective(directives, name);\n if (existing) {\n if (hasStrictNonceScriptPolicy(existing.tokens)) return false;\n existing.tokens = appendCspTokens(existing.tokens, additions);\n return true;\n }\n\n const defaultSrc = findCspDirective(directives, \"default-src\");\n if (!defaultSrc || hasStrictNonceScriptPolicy(defaultSrc.tokens)) {\n return false;\n }\n directives.push({\n name,\n tokens: appendCspTokens([...defaultSrc.tokens], additions),\n });\n return true;\n}\n\nfunction appendToEffectiveScriptElementCspDirective(\n directives: CspDirective[],\n additions: readonly string[],\n): boolean {\n const scriptSrcElem = findCspDirective(directives, \"script-src-elem\");\n if (scriptSrcElem) {\n if (hasStrictNonceScriptPolicy(scriptSrcElem.tokens)) return false;\n scriptSrcElem.tokens = appendCspTokens(scriptSrcElem.tokens, additions);\n return true;\n }\n\n return appendToScriptCspDirective(directives, \"script-src\", additions);\n}\n\nfunction augmentExistingEnforcedCspForFrameworkScripts(\n policy: string,\n options: {\n gaScriptSrcTokens: readonly string[];\n gaEnabled: boolean;\n },\n): string {\n // Multiple CSP headers are surfaced by Headers.get() as one comma-joined\n // string. CSP is not a comma-list header, so serializing a parsed combined\n // value would turn two policies into one invalid policy. Leave those headers\n // app-owned; a comma inside a source/report URL is still safe to parse.\n if (hasCommaJoinedCspPolicies(policy)) return policy;\n\n const directives = parseCsp(policy);\n if (!directives.length) return policy;\n\n if (options.gaEnabled) {\n const addedScriptElement = appendToEffectiveScriptElementCspDirective(\n directives,\n options.gaScriptSrcTokens,\n );\n if (addedScriptElement) {\n appendToExistingOrDefaultCspDirective(\n directives,\n \"connect-src\",\n GA_CSP_CONNECT_HOSTS,\n );\n appendToExistingOrDefaultCspDirective(\n directives,\n \"img-src\",\n GA_CSP_IMG_HOSTS,\n );\n }\n }\n\n ensureCspDirective(directives, \"object-src\", [\"'none'\"]);\n ensureCspDirective(directives, \"base-uri\", [\"'self'\"]);\n\n return serializeCsp(directives);\n}\n\nfunction augmentExistingReportOnlyCspForFrameworkScripts(\n policy: string,\n options: {\n scriptSrcTokens: readonly string[];\n gaEnabled: boolean;\n },\n): string {\n if (hasCommaJoinedCspPolicies(policy)) return policy;\n\n const directives = parseCsp(policy);\n if (!directives.length) return policy;\n\n appendToExistingOrDefaultCspDirective(\n directives,\n \"script-src\",\n options.scriptSrcTokens,\n );\n appendToExistingCspDirective(\n directives,\n \"script-src-elem\",\n options.scriptSrcTokens,\n );\n\n if (options.gaEnabled) {\n appendToExistingOrDefaultCspDirective(\n directives,\n \"connect-src\",\n GA_CSP_CONNECT_HOSTS,\n );\n appendToExistingOrDefaultCspDirective(\n directives,\n \"img-src\",\n GA_CSP_IMG_HOSTS,\n );\n }\n\n return serializeCsp(directives);\n}\n\n/**\n * Apply a Content-Security-Policy header to HTML document responses.\n *\n * Two directives are always enforced in production:\n *\n * - `object-src 'none'` — disables Flash / Java / PDF plugin execution,\n * which are a reliable code-execution vector even in modern browsers.\n * - `base-uri 'self'` — prevents a `<base href=\"...\">` injection from\n * hijacking all relative URLs in the document (a common attack target when\n * user-controlled content reaches the HTML).\n *\n * A third directive, `script-src`, is emitted via `Content-Security-Policy-\n * Report-Only` rather than enforced when the app has no existing document CSP.\n * The framework injects deterministic inline scripts (the Sentry config block,\n * whose hash is computed once at process startup from the resolved env vars,\n * and — when `GA_MEASUREMENT_ID` is set — the gtag config block, whose hash is\n * derived from the same string `wrapWithAnalytics` embeds). It also loads\n * Google Tag Manager / GA4 from `GA_CSP_SCRIPT_HOSTS`. All of those are listed\n * here so the report-only policy reflects the code the framework itself injects\n * instead of reporting a violation on every page load.\n *\n * If an app or host already sends an enforced CSP with `script-src`,\n * `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge only\n * GA-specific allowances into existing host/hash policies. Strict nonce or\n * `strict-dynamic` script policies stay app-owned because blindly appending\n * hashes or hosts would widen the policy without reliably loading our injected\n * scripts.\n *\n * Templates additionally render a theme-init inline script whose exact content\n * varies by template (default theme param, custom docs variant, etc.) and which\n * is rendered by React Router, not this handler, so its hash is not available\n * here. Shipping script-src as Report-Only surfaces the remaining violations\n * without breaking template customisations; teams can graduate to enforcement\n * once their hashes are enumerated.\n *\n * Skipped in development (`NODE_ENV !== 'production'`) so HMR eval and Vite\n * dev-server injects are never blocked. Set `AGENT_NATIVE_DISABLE_DOC_CSP=1`\n * to opt out in production for a template with exotic needs.\n */\nfunction applyDocumentCsp(headers: Headers, sentryScript: string | null): void {\n if (process.env.NODE_ENV !== \"production\") return;\n if (process.env.AGENT_NATIVE_DISABLE_DOC_CSP === \"1\") return;\n\n // script-src as Report-Only: list 'self', the framework-injected inline\n // script hashes (Sentry config + gtag config), and the Google Analytics /\n // Tag Manager loader hosts. These are exactly the scripts the framework\n // itself injects, so listing them keeps the report-only policy from flagging\n // GA on every page load (and keeps it safe to graduate to enforcement).\n // Template theme-init hashes are NOT included here — see function comment.\n const sentryBody = extractScriptBody(sentryScript);\n const sentryHash = sentryBody ? computeInlineScriptHash(sentryBody) : null;\n const gaInlineBody = getGaInlineConfigScriptBody();\n const gaHash = gaInlineBody ? computeInlineScriptHash(gaInlineBody) : null;\n const gaHosts = gaInlineBody ? [...GA_CSP_SCRIPT_HOSTS] : [];\n const gaScriptSrcTokens = [...(gaHash ? [gaHash] : []), ...gaHosts];\n const scriptSrcTokens = [\n \"'self'\",\n ...(sentryHash ? [sentryHash] : []),\n ...(gaHash ? [gaHash] : []),\n ...gaHosts,\n ];\n\n const cspAugmentOptions = {\n scriptSrcTokens,\n gaScriptSrcTokens,\n gaEnabled: Boolean(gaInlineBody),\n };\n const existing = headers.get(\"content-security-policy\") ?? \"\";\n if (!existing) {\n headers.set(\n \"content-security-policy\",\n \"object-src 'none'; base-uri 'self'\",\n );\n } else {\n headers.set(\n \"content-security-policy\",\n augmentExistingEnforcedCspForFrameworkScripts(\n existing,\n cspAugmentOptions,\n ),\n );\n }\n\n const scriptSrc = `script-src ${scriptSrcTokens.join(\" \")}`;\n const existingRo = headers.get(\"content-security-policy-report-only\") ?? \"\";\n if (!existingRo) {\n headers.set(\"content-security-policy-report-only\", scriptSrc);\n } else {\n headers.set(\n \"content-security-policy-report-only\",\n augmentExistingReportOnlyCspForFrameworkScripts(\n existingRo,\n cspAugmentOptions,\n ),\n );\n }\n}\n\nfunction isFrameworkOrAssetPath(pathname: string): boolean {\n return (\n pathname.startsWith(\"/.well-known/\") ||\n pathname.startsWith(\"/_agent_native/\") ||\n pathname.startsWith(\"/_agent-native/\") ||\n pathname.startsWith(\"/api/\") ||\n pathname.startsWith(\"/@vite/\") ||\n pathname.startsWith(\"/@id/\") ||\n pathname.startsWith(\"/@fs/\") ||\n pathname === \"/@react-refresh\" ||\n pathname === \"/__vite_ping\" ||\n pathname === \"/__open-in-editor\" ||\n pathname === \"/favicon.ico\" ||\n pathname === \"/favicon.png\" ||\n (/\\.\\w+$/.test(pathname) && !pathname.endsWith(\".data\"))\n );\n}\n\nasync function rewriteMountedResponse(\n response: Response,\n basePath: string,\n pathname: string,\n requestUrl: string,\n): Promise<Response> {\n const sentryClientConfigScript = getSentryClientConfigScript();\n const headers = new Headers(response.headers);\n applyDefaultSsrCacheHeader(headers, response.status, pathname);\n applyDefaultSpeculationRulesHeader(headers, response.status, basePath);\n\n const location = headers.get(\"location\");\n if (location?.startsWith(\"/\") && !location.startsWith(\"//\")) {\n headers.set(\"location\", prefixMountedPath(location, basePath));\n }\n\n const contentType = headers.get(\"content-type\") ?? \"\";\n if (!contentType.toLowerCase().includes(\"text/html\") || !response.body) {\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n const html = await response.text();\n headers.delete(\"content-length\");\n applyDocumentCsp(headers, sentryClientConfigScript);\n return new Response(\n injectHeadScript(\n injectDefaultSocialImageMeta(\n prefixMountedHtml(html, basePath),\n defaultSocialImageUrl(requestUrl, basePath),\n ),\n sentryClientConfigScript,\n ),\n {\n status: response.status,\n statusText: response.statusText,\n headers,\n },\n );\n}\n\n/**\n * Create an h3 catch-all that hands page routes to React Router and\n * returns 404 for framework / asset paths that React Router doesn't own.\n */\nexport function createH3SSRHandler(getBuild: () => Promise<unknown> | unknown) {\n const handler = createRequestHandler(getBuild as any);\n return defineEventHandler(async (event) => {\n const basePath = getAppBasePath();\n const p = stripAppBasePath(event.url.pathname);\n if (isFrameworkOrAssetPath(p)) {\n return new Response(null, { status: 404 });\n }\n try {\n const request = requestWithPathname(event.req as Request, p, basePath);\n // SSR renders an IMPERSONAL public shell — we deliberately do NOT read the\n // request's session/cookies here, and pin an explicitly anonymous request\n // context. That keeps the SSR HTML/.data identical for every visitor so it\n // can be hard-cached at the CDN for everyone (see applyDefaultSsrCacheHeader).\n //\n // Consequence: SSR loaders that call `getRequestUserEmail()` / `accessFilter()`\n // always see the unauthenticated branch and render public content only. Any\n // per-user view (private records, share-grant access, who's logged in) MUST\n // be resolved CLIENT-SIDE after load, never baked into SSR. Do not re-pin the\n // session here to \"fix\" a per-user page — that silently makes the page\n // uncacheable and/or leaks one user's data into another's cached copy.\n const ctx = { userEmail: undefined, orgId: undefined };\n if (request.method === \"HEAD\") {\n const getRequest = new Request(request.url, {\n method: \"GET\",\n headers: request.headers,\n signal: request.signal,\n });\n const response = await runWithRequestContext(ctx, () =>\n handler(getRequest),\n );\n return await rewriteMountedResponse(\n new Response(null, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n }),\n basePath,\n p,\n request.url,\n );\n }\n return await rewriteMountedResponse(\n await runWithRequestContext(ctx, () => handler(request)),\n basePath,\n p,\n request.url,\n );\n } catch (err) {\n // Log the full stack server-side, but never leak it to the client.\n // Stack traces expose file paths, library versions, and code structure\n // that aid reconnaissance attacks. In dev we surface the message text\n // so devtools shows something useful; in prod we return a bare 500.\n console.error(\"[ssr-handler] SSR error:\", err);\n const isProd = process.env.NODE_ENV === \"production\";\n const body = isProd\n ? \"Internal Server Error\"\n : `Internal Server Error: ${(err as Error)?.message ?? err}`;\n return new Response(body, {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ssr-handler.js","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineEventHandler } from \"h3\";\n/**\n * Shared SSR catch-all handler for React Router framework mode.\n *\n * Templates wire this up via:\n *\n * // server/routes/[...page].get.ts\n * import { createH3SSRHandler } from \"@agent-native/core/server/ssr-handler\";\n * export default createH3SSRHandler(\n * () => import(\"virtual:react-router/server-build\"),\n * );\n *\n * The `getBuild` callback MUST live in the template's own source so Vite's\n * @react-router/dev plugin can resolve the `virtual:` module. Pulling the\n * import into core (e.g. via a re-export) puts it in node_modules where\n * Vite's SSR externalizer leaves it untouched and Node's ESM loader rejects\n * the unknown scheme — silently 302'ing every request to \"/\".\n */\nimport { createRequestHandler } from \"react-router\";\n\nimport {\n DEFAULT_SSR_CACHE_HEADERS,\n DEFAULT_SPECULATION_RULES_PATH,\n} from \"../shared/cache-control.js\";\nimport {\n AGENT_NATIVE_SOCIAL_IMAGE_ALT,\n AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT,\n AGENT_NATIVE_SOCIAL_IMAGE_PATH,\n AGENT_NATIVE_SOCIAL_IMAGE_TYPE,\n AGENT_NATIVE_SOCIAL_IMAGE_WIDTH,\n withAgentNativeSocialImageCacheBuster,\n} from \"../shared/social-meta.js\";\nimport {\n GA_CSP_CONNECT_HOSTS,\n GA_CSP_IMG_HOSTS,\n GA_CSP_SCRIPT_HOSTS,\n getGaInlineConfigScriptBody,\n} from \"./analytics.js\";\nimport {\n getAppBasePathFromViteEnv,\n stripAppBasePath as canonicalStripAppBasePath,\n} from \"./app-base-path.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { computeInlineScriptHash } from \"./security-headers.js\";\nimport { getSentryClientConfigScript } from \"./sentry-config.js\";\n\nexport {\n DEFAULT_SSR_CACHE_HEADERS,\n DEFAULT_SPECULATION_RULES_HEADER,\n DEFAULT_SSR_CACHE_CONTROL,\n} from \"../shared/cache-control.js\";\n\nfunction getAppBasePath(): string {\n return getAppBasePathFromViteEnv();\n}\n\nfunction stripAppBasePath(pathname: string): string {\n return canonicalStripAppBasePath(pathname, getAppBasePath());\n}\n\nfunction stripBasePath(pathname: string, basePath: string): string {\n if (!basePath) return pathname;\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(`${basePath}/`)) {\n return pathname.slice(basePath.length) || \"/\";\n }\n return pathname;\n}\n\nfunction requestWithPathname(\n request: Request,\n pathname: string,\n basePath: string,\n): Request {\n const url = new URL(request.url);\n let changed = false;\n if (basePath && pathname === \"/__manifest\") {\n const paths = url.searchParams.get(\"paths\");\n if (paths) {\n const strippedPaths = paths\n .split(\",\")\n .map((path) => stripBasePath(path, basePath))\n .join(\",\");\n if (strippedPaths !== paths) {\n url.searchParams.set(\"paths\", strippedPaths);\n changed = true;\n }\n }\n }\n if (url.pathname !== pathname) {\n url.pathname = pathname;\n changed = true;\n }\n if (!changed) return request;\n const init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers: request.headers,\n signal: request.signal,\n };\n if (request.body && ![\"GET\", \"HEAD\"].includes(request.method.toUpperCase())) {\n init.body = request.body;\n init.duplex = \"half\";\n }\n return new Request(url, init);\n}\n\nfunction prefixMountedPath(path: string, basePath: string): string {\n if (!basePath || !path.startsWith(\"/\") || path.startsWith(\"//\")) return path;\n if (path === basePath || path.startsWith(`${basePath}/`)) return path;\n return `${basePath}${path}`;\n}\n\nfunction prefixMountedHtml(html: string, basePath: string): string {\n if (!basePath) return html;\n return html\n .replace(\n /\\b(href|src|action|formaction|poster)=([\"'])(\\/(?!\\/)[^\"']*)\\2/g,\n (_match, attr: string, quote: string, path: string) =>\n `${attr}=${quote}${prefixMountedPath(path, basePath)}${quote}`,\n )\n .replace(/url\\(([\"']?)(\\/(?!\\/)[^)'\" ]+)\\1\\)/g, (_match, quote, path) => {\n const q = quote || \"\";\n return `url(${q}${prefixMountedPath(path, basePath)}${q})`;\n });\n}\n\nfunction injectHeadScript(html: string, script: string | null): string {\n if (!script) return html;\n const headCloseIdx = html.indexOf(\"</head>\");\n if (headCloseIdx === -1) return html;\n return html.slice(0, headCloseIdx) + script + html.slice(headCloseIdx);\n}\n\nconst OG_IMAGE_META_RE = /<meta\\b(?=[^>]*\\bproperty=([\"'])og:image\\1)[^>]*>/i;\nconst TWITTER_CARD_META_RE =\n /<meta\\b(?=[^>]*\\bname=([\"'])twitter:card\\1)[^>]*>/i;\nconst TWITTER_IMAGE_META_RE =\n /<meta\\b(?=[^>]*\\bname=([\"'])twitter:image\\1)[^>]*>/i;\n\nfunction defaultSocialImageUrl(requestUrl: string, basePath: string): string {\n return withAgentNativeSocialImageCacheBuster(\n new URL(\n prefixMountedPath(AGENT_NATIVE_SOCIAL_IMAGE_PATH, basePath),\n requestUrl,\n ).toString(),\n );\n}\n\nfunction injectDefaultSocialImageMeta(html: string, imageUrl: string): string {\n const headCloseIdx = html.indexOf(\"</head>\");\n if (headCloseIdx === -1) return html;\n\n const hasAnySocialImage =\n OG_IMAGE_META_RE.test(html) || TWITTER_IMAGE_META_RE.test(html);\n const tags: string[] = [];\n\n if (!hasAnySocialImage) {\n tags.push(`<meta property=\"og:image\" content=\"${imageUrl}\">`);\n tags.push(`<meta property=\"og:image:secure_url\" content=\"${imageUrl}\">`);\n tags.push(\n `<meta property=\"og:image:type\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_TYPE}\">`,\n );\n tags.push(\n `<meta property=\"og:image:width\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_WIDTH}\">`,\n );\n tags.push(\n `<meta property=\"og:image:height\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT}\">`,\n );\n tags.push(\n `<meta property=\"og:image:alt\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_ALT}\">`,\n );\n }\n if (!TWITTER_CARD_META_RE.test(html)) {\n tags.push(`<meta name=\"twitter:card\" content=\"summary_large_image\">`);\n }\n if (!hasAnySocialImage) {\n tags.push(`<meta name=\"twitter:image\" content=\"${imageUrl}\">`);\n tags.push(\n `<meta name=\"twitter:image:alt\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_ALT}\">`,\n );\n }\n\n if (tags.length === 0) return html;\n return html.slice(0, headCloseIdx) + tags.join(\"\") + html.slice(headCloseIdx);\n}\n\nfunction isSsrHtmlOrDataResponse(\n headers: Headers,\n status: number,\n pathname: string,\n): boolean {\n if (status < 200 || status >= 400) return false;\n const contentType = headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n if (contentType.includes(\"text/html\")) return true;\n return pathname.endsWith(\".data\") && contentType.includes(\"text/x-script\");\n}\n\n/**\n * Apply the SSR cache policy to the response headers.\n *\n * ┌──────────────────────────────────────────────────────────────────────────┐\n * │ SSR IS A PUBLIC, HARD-CDN-CACHED SHELL — SERVED IDENTICALLY TO EVERYONE. │\n * │ │\n * │ Every SSR HTML / React Router `.data` response gets the same public │\n * │ stale-while-revalidate policy for ALL visitors, authenticated or not, so │\n * │ the edge serves one shared copy and never stampedes origin. │\n * │ │\n * │ DO NOT reintroduce per-user / cookie-based cache variation here (no │\n * │ `private`, no `no-store`, no `Vary: Cookie`, no \"authenticated → don't │\n * │ cache\" branch). That makes pages uncacheable for every logged-in visitor, │\n * │ which is slow and expensive — exactly the regression this guardrail │\n * │ prevents. The reason it is SAFE to hard-cache is that the SSR response is │\n * │ impersonal: `createH3SSRHandler` renders without reading the request's │\n * │ session/cookies, so there is no per-user data baked into the HTML. ALL │\n * │ per-user state (who's logged in, private records, access checks) is │\n * │ resolved CLIENT-SIDE after load. Keep it that way: if you need the SSR │\n * │ output to differ per user, the fix is to move that work client-side, not │\n * │ to disable caching here. │\n * └──────────────────────────────────────────────────────────────────────────┘\n */\nfunction applyDefaultSsrCacheHeader(\n headers: Headers,\n status: number,\n pathname: string,\n) {\n if (!isSsrHtmlOrDataResponse(headers, status, pathname)) return;\n\n // Netlify Functions/proxies are not cached by default. Set all three cache\n // headers: Cache-Control for browsers, CDN-Cache-Control for generic CDNs,\n // and Netlify-CDN-Cache-Control (with durable) so Netlify's shared cache\n // actually serves SSR HTML/.data from the edge instead of forwarding every\n // request to origin — for every visitor, authenticated or not.\n for (const [name, value] of Object.entries(DEFAULT_SSR_CACHE_HEADERS)) {\n headers.set(name, value);\n }\n}\n\nfunction applyDefaultSpeculationRulesHeader(\n headers: Headers,\n status: number,\n basePath: string,\n) {\n if (status < 200 || status >= 400) return;\n if (headers.has(\"speculation-rules\")) return;\n\n const contentType = headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n if (!contentType.includes(\"text/html\")) return;\n\n // Cloudflare Speed Brain injects its own Speculation-Rules header when the\n // origin omits one. Those browser prefetches carry `Sec-Purpose: prefetch`,\n // and Cloudflare refuses cache-ineligible dynamic pages with a 503 before\n // the request can reach Netlify/origin. We publish an explicit no-op ruleset\n // by default so Cloudflare does not inject its edge prefetch rules. Preserve\n // an app-provided Speculation-Rules header above if a template deliberately\n // owns this behavior.\n const rulesPath = prefixMountedPath(DEFAULT_SPECULATION_RULES_PATH, basePath);\n headers.set(\"speculation-rules\", `\"${rulesPath}\"`);\n}\n\n/**\n * Extract the plain JS body from a `<script ...>body</script>` string.\n * Returns `null` if the input is falsy or has no recognisable `</script>` end.\n * Used to compute the sha256 hash of framework-injected inline scripts so the\n * hash can be listed in app-owned `script-src` CSP directives.\n */\nfunction extractScriptBody(scriptTag: string | null): string | null {\n if (!scriptTag) return null;\n const start = scriptTag.indexOf(\">\") + 1;\n const end = scriptTag.lastIndexOf(\"</script>\");\n if (start <= 0 || end < start) return null;\n return scriptTag.slice(start, end);\n}\n\ntype CspDirective = {\n name: string;\n tokens: string[];\n};\n\nconst CSP_DIRECTIVES_WITH_VALUE_TOKENS = new Set([\n \"base-uri\",\n \"block-all-mixed-content\",\n \"child-src\",\n \"connect-src\",\n \"default-src\",\n \"fenced-frame-src\",\n \"font-src\",\n \"form-action\",\n \"frame-ancestors\",\n \"frame-src\",\n \"img-src\",\n \"manifest-src\",\n \"media-src\",\n \"navigate-to\",\n \"object-src\",\n \"plugin-types\",\n \"prefetch-src\",\n \"referrer\",\n \"reflected-xss\",\n \"require-sri-for\",\n \"require-trusted-types-for\",\n \"report-to\",\n \"report-uri\",\n \"sandbox\",\n \"script-src\",\n \"script-src-attr\",\n \"script-src-elem\",\n \"style-src\",\n \"style-src-attr\",\n \"style-src-elem\",\n \"trusted-types\",\n \"upgrade-insecure-requests\",\n \"webrtc\",\n \"worker-src\",\n]);\n\nfunction hasCommaJoinedCspPolicies(policy: string): boolean {\n let commaIndex = policy.indexOf(\",\");\n while (commaIndex !== -1) {\n const afterComma = policy.slice(commaIndex + 1);\n const directive = /^\\s+([a-z][a-z0-9-]*)(?=\\s|;|$)/i.exec(afterComma)?.[1];\n if (\n directive &&\n CSP_DIRECTIVES_WITH_VALUE_TOKENS.has(directive.toLowerCase())\n ) {\n return true;\n }\n commaIndex = policy.indexOf(\",\", commaIndex + 1);\n }\n return false;\n}\n\nfunction parseCsp(policy: string): CspDirective[] {\n return policy\n .split(\";\")\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => {\n const [name = \"\", ...tokens] = part.split(/\\s+/);\n return { name: name.toLowerCase(), tokens };\n })\n .filter((directive) => directive.name);\n}\n\nfunction serializeCsp(directives: CspDirective[]): string {\n return directives\n .map((directive) =>\n [directive.name, ...directive.tokens].filter(Boolean).join(\" \"),\n )\n .join(\"; \");\n}\n\nfunction appendCspTokens(\n tokens: string[],\n additions: readonly string[],\n): string[] {\n if (!additions.length) return tokens;\n const next = tokens.filter((token) => token !== \"'none'\");\n const seen = new Set(next);\n for (const token of additions) {\n if (!token || seen.has(token)) continue;\n next.push(token);\n seen.add(token);\n }\n return next;\n}\n\nfunction findCspDirective(\n directives: CspDirective[],\n name: string,\n): CspDirective | undefined {\n return directives.find((directive) => directive.name === name);\n}\n\nfunction appendToExistingOrDefaultCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): void {\n if (!additions.length) return;\n const existing = findCspDirective(directives, name);\n if (existing) {\n existing.tokens = appendCspTokens(existing.tokens, additions);\n return;\n }\n\n const defaultSrc = findCspDirective(directives, \"default-src\");\n if (!defaultSrc) return;\n directives.push({\n name,\n tokens: appendCspTokens([...defaultSrc.tokens], additions),\n });\n}\n\nfunction appendToExistingCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): void {\n const existing = findCspDirective(directives, name);\n if (!existing) return;\n existing.tokens = appendCspTokens(existing.tokens, additions);\n}\n\nfunction ensureCspDirective(\n directives: CspDirective[],\n name: string,\n tokens: readonly string[],\n): void {\n if (findCspDirective(directives, name)) return;\n directives.push({ name, tokens: [...tokens] });\n}\n\nfunction hasStrictNonceScriptPolicy(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) => token === \"'strict-dynamic'\" || token.startsWith(\"'nonce-\"),\n );\n}\n\nfunction appendToScriptCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): boolean {\n const existing = findCspDirective(directives, name);\n if (existing) {\n if (hasStrictNonceScriptPolicy(existing.tokens)) return false;\n existing.tokens = appendCspTokens(existing.tokens, additions);\n return true;\n }\n\n const defaultSrc = findCspDirective(directives, \"default-src\");\n if (!defaultSrc || hasStrictNonceScriptPolicy(defaultSrc.tokens)) {\n return false;\n }\n directives.push({\n name,\n tokens: appendCspTokens([...defaultSrc.tokens], additions),\n });\n return true;\n}\n\nfunction appendToEffectiveScriptElementCspDirective(\n directives: CspDirective[],\n additions: readonly string[],\n): boolean {\n const scriptSrcElem = findCspDirective(directives, \"script-src-elem\");\n if (scriptSrcElem) {\n if (hasStrictNonceScriptPolicy(scriptSrcElem.tokens)) return false;\n scriptSrcElem.tokens = appendCspTokens(scriptSrcElem.tokens, additions);\n return true;\n }\n\n return appendToScriptCspDirective(directives, \"script-src\", additions);\n}\n\nfunction augmentExistingEnforcedCspForFrameworkScripts(\n policy: string,\n options: {\n gaScriptSrcTokens: readonly string[];\n gaEnabled: boolean;\n },\n): string {\n // Multiple CSP headers are surfaced by Headers.get() as one comma-joined\n // string. CSP is not a comma-list header, so serializing a parsed combined\n // value would turn two policies into one invalid policy. Leave those headers\n // app-owned; a comma inside a source/report URL is still safe to parse.\n if (hasCommaJoinedCspPolicies(policy)) return policy;\n\n const directives = parseCsp(policy);\n if (!directives.length) return policy;\n\n if (options.gaEnabled) {\n const addedScriptElement = appendToEffectiveScriptElementCspDirective(\n directives,\n options.gaScriptSrcTokens,\n );\n if (addedScriptElement) {\n appendToExistingOrDefaultCspDirective(\n directives,\n \"connect-src\",\n GA_CSP_CONNECT_HOSTS,\n );\n appendToExistingOrDefaultCspDirective(\n directives,\n \"img-src\",\n GA_CSP_IMG_HOSTS,\n );\n }\n }\n\n ensureCspDirective(directives, \"object-src\", [\"'none'\"]);\n ensureCspDirective(directives, \"base-uri\", [\"'self'\"]);\n\n return serializeCsp(directives);\n}\n\nfunction augmentExistingReportOnlyCspForFrameworkScripts(\n policy: string,\n options: {\n scriptSrcTokens: readonly string[];\n gaEnabled: boolean;\n },\n): string {\n if (hasCommaJoinedCspPolicies(policy)) return policy;\n\n const directives = parseCsp(policy);\n if (!directives.length) return policy;\n\n appendToExistingOrDefaultCspDirective(\n directives,\n \"script-src\",\n options.scriptSrcTokens,\n );\n appendToExistingCspDirective(\n directives,\n \"script-src-elem\",\n options.scriptSrcTokens,\n );\n\n if (options.gaEnabled) {\n appendToExistingOrDefaultCspDirective(\n directives,\n \"connect-src\",\n GA_CSP_CONNECT_HOSTS,\n );\n appendToExistingOrDefaultCspDirective(\n directives,\n \"img-src\",\n GA_CSP_IMG_HOSTS,\n );\n }\n\n return serializeCsp(directives);\n}\n\n/**\n * Apply a Content-Security-Policy header to HTML document responses.\n *\n * Two directives are always enforced in production:\n *\n * - `object-src 'none'` — disables Flash / Java / PDF plugin execution,\n * which are a reliable code-execution vector even in modern browsers.\n * - `base-uri 'self'` — prevents a `<base href=\"...\">` injection from\n * hijacking all relative URLs in the document (a common attack target when\n * user-controlled content reaches the HTML).\n *\n * A third directive, `script-src`, is emitted via `Content-Security-Policy-\n * Report-Only` rather than enforced when the app has no existing document CSP.\n * The framework injects inline scripts for analytics, Sentry, and template\n * setup, and hosted apps need Google Tag Manager to load without noisy CSP\n * diagnostics. The report-only policy is intentionally permissive for scripts:\n * it includes `'unsafe-inline'` plus the known GA/GTM loader hosts.\n *\n * If an app or host already sends an enforced CSP with `script-src`,\n * `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge only\n * GA-specific allowances into existing host/hash policies. Strict nonce or\n * `strict-dynamic` script policies stay app-owned because blindly appending\n * hashes or hosts would widen the policy without reliably loading our injected\n * scripts.\n *\n * Templates additionally render a theme-init inline script whose exact content\n * varies by template (default theme param, custom docs variant, etc.) and which\n * is rendered by React Router, not this handler, so its hash is not available\n * here. Shipping script-src as Report-Only surfaces the remaining violations\n * without breaking template customisations; teams can graduate to enforcement\n * once their hashes are enumerated.\n *\n * Skipped in development (`NODE_ENV !== 'production'`) so HMR eval and Vite\n * dev-server injects are never blocked. Set `AGENT_NATIVE_DISABLE_DOC_CSP=1`\n * to opt out in production for a template with exotic needs.\n */\nfunction applyDocumentCsp(headers: Headers, sentryScript: string | null): void {\n if (process.env.NODE_ENV !== \"production\") return;\n if (process.env.AGENT_NATIVE_DISABLE_DOC_CSP === \"1\") return;\n\n // script-src as Report-Only: keep this deliberately loose so the framework's\n // injected analytics and template bootstrap scripts do not look blocked in\n // browser diagnostics.\n const sentryBody = extractScriptBody(sentryScript);\n const sentryHash = sentryBody ? computeInlineScriptHash(sentryBody) : null;\n const gaInlineBody = getGaInlineConfigScriptBody();\n const gaHash = gaInlineBody ? computeInlineScriptHash(gaInlineBody) : null;\n const gaHosts = gaInlineBody ? [...GA_CSP_SCRIPT_HOSTS] : [];\n const gaScriptSrcTokens = [\n \"'unsafe-inline'\",\n ...(gaHash ? [gaHash] : []),\n ...gaHosts,\n ];\n const scriptSrcTokens = [\n \"'self'\",\n \"'unsafe-inline'\",\n ...(sentryHash ? [sentryHash] : []),\n ...(gaHash ? [gaHash] : []),\n ...gaHosts,\n ];\n\n const cspAugmentOptions = {\n scriptSrcTokens,\n gaScriptSrcTokens,\n gaEnabled: Boolean(gaInlineBody),\n };\n const existing = headers.get(\"content-security-policy\") ?? \"\";\n if (!existing) {\n headers.set(\n \"content-security-policy\",\n \"object-src 'none'; base-uri 'self'\",\n );\n } else {\n headers.set(\n \"content-security-policy\",\n augmentExistingEnforcedCspForFrameworkScripts(\n existing,\n cspAugmentOptions,\n ),\n );\n }\n\n const scriptSrc = `script-src ${scriptSrcTokens.join(\" \")}`;\n const existingRo = headers.get(\"content-security-policy-report-only\") ?? \"\";\n if (!existingRo) {\n headers.set(\"content-security-policy-report-only\", scriptSrc);\n } else {\n headers.set(\n \"content-security-policy-report-only\",\n augmentExistingReportOnlyCspForFrameworkScripts(\n existingRo,\n cspAugmentOptions,\n ),\n );\n }\n}\n\nfunction isFrameworkOrAssetPath(pathname: string): boolean {\n return (\n pathname.startsWith(\"/.well-known/\") ||\n pathname.startsWith(\"/_agent_native/\") ||\n pathname.startsWith(\"/_agent-native/\") ||\n pathname.startsWith(\"/api/\") ||\n pathname.startsWith(\"/@vite/\") ||\n pathname.startsWith(\"/@id/\") ||\n pathname.startsWith(\"/@fs/\") ||\n pathname === \"/@react-refresh\" ||\n pathname === \"/__vite_ping\" ||\n pathname === \"/__open-in-editor\" ||\n pathname === \"/favicon.ico\" ||\n pathname === \"/favicon.png\" ||\n (/\\.\\w+$/.test(pathname) && !pathname.endsWith(\".data\"))\n );\n}\n\nasync function rewriteMountedResponse(\n response: Response,\n basePath: string,\n pathname: string,\n requestUrl: string,\n): Promise<Response> {\n const sentryClientConfigScript = getSentryClientConfigScript();\n const headers = new Headers(response.headers);\n applyDefaultSsrCacheHeader(headers, response.status, pathname);\n applyDefaultSpeculationRulesHeader(headers, response.status, basePath);\n\n const location = headers.get(\"location\");\n if (location?.startsWith(\"/\") && !location.startsWith(\"//\")) {\n headers.set(\"location\", prefixMountedPath(location, basePath));\n }\n\n const contentType = headers.get(\"content-type\") ?? \"\";\n if (!contentType.toLowerCase().includes(\"text/html\") || !response.body) {\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n const html = await response.text();\n headers.delete(\"content-length\");\n applyDocumentCsp(headers, sentryClientConfigScript);\n return new Response(\n injectHeadScript(\n injectDefaultSocialImageMeta(\n prefixMountedHtml(html, basePath),\n defaultSocialImageUrl(requestUrl, basePath),\n ),\n sentryClientConfigScript,\n ),\n {\n status: response.status,\n statusText: response.statusText,\n headers,\n },\n );\n}\n\n/**\n * Create an h3 catch-all that hands page routes to React Router and\n * returns 404 for framework / asset paths that React Router doesn't own.\n */\nexport function createH3SSRHandler(getBuild: () => Promise<unknown> | unknown) {\n const handler = createRequestHandler(getBuild as any);\n return defineEventHandler(async (event) => {\n const basePath = getAppBasePath();\n const p = stripAppBasePath(event.url.pathname);\n if (isFrameworkOrAssetPath(p)) {\n return new Response(null, { status: 404 });\n }\n try {\n const request = requestWithPathname(event.req as Request, p, basePath);\n // SSR renders an IMPERSONAL public shell — we deliberately do NOT read the\n // request's session/cookies here, and pin an explicitly anonymous request\n // context. That keeps the SSR HTML/.data identical for every visitor so it\n // can be hard-cached at the CDN for everyone (see applyDefaultSsrCacheHeader).\n //\n // Consequence: SSR loaders that call `getRequestUserEmail()` / `accessFilter()`\n // always see the unauthenticated branch and render public content only. Any\n // per-user view (private records, share-grant access, who's logged in) MUST\n // be resolved CLIENT-SIDE after load, never baked into SSR. Do not re-pin the\n // session here to \"fix\" a per-user page — that silently makes the page\n // uncacheable and/or leaks one user's data into another's cached copy.\n const ctx = { userEmail: undefined, orgId: undefined };\n if (request.method === \"HEAD\") {\n const getRequest = new Request(request.url, {\n method: \"GET\",\n headers: request.headers,\n signal: request.signal,\n });\n const response = await runWithRequestContext(ctx, () =>\n handler(getRequest),\n );\n return await rewriteMountedResponse(\n new Response(null, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n }),\n basePath,\n p,\n request.url,\n );\n }\n return await rewriteMountedResponse(\n await runWithRequestContext(ctx, () => handler(request)),\n basePath,\n p,\n request.url,\n );\n } catch (err) {\n // Log the full stack server-side, but never leak it to the client.\n // Stack traces expose file paths, library versions, and code structure\n // that aid reconnaissance attacks. In dev we surface the message text\n // so devtools shows something useful; in prod we return a bare 500.\n console.error(\"[ssr-handler] SSR error:\", err);\n const isProd = process.env.NODE_ENV === \"production\";\n const body = isProd\n ? \"Internal Server Error\"\n : `Internal Server Error: ${(err as Error)?.message ?? err}`;\n return new Response(body, {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.60",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|