@agent-native/core 0.124.3 → 0.124.4
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/README.md +1 -1
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +10 -0
- package/corpus/core/docs/content/cloneable-saas.mdx +9 -9
- package/corpus/core/docs/content/creating-templates.mdx +2 -2
- package/corpus/core/docs/content/deployment.mdx +23 -0
- package/corpus/core/docs/content/faq.mdx +4 -4
- package/corpus/core/docs/content/key-concepts.mdx +1 -1
- package/corpus/core/docs/content/multi-app-workspace.mdx +1 -1
- package/corpus/core/docs/content/syncing-template-changes.mdx +1 -1
- package/corpus/core/docs/content/template-analytics-developers.mdx +2 -2
- package/corpus/core/docs/content/template-analytics.mdx +2 -2
- package/corpus/core/docs/content/template-assets-developers.mdx +3 -3
- package/corpus/core/docs/content/template-assets.mdx +2 -2
- package/corpus/core/docs/content/template-brain-developers.mdx +2 -2
- package/corpus/core/docs/content/template-brain.mdx +1 -1
- package/corpus/core/docs/content/template-calendar-developers.mdx +1 -1
- package/corpus/core/docs/content/template-calendar.mdx +1 -1
- package/corpus/core/docs/content/template-chat.mdx +1 -1
- package/corpus/core/docs/content/template-clips-developers.mdx +4 -4
- package/corpus/core/docs/content/template-clips.mdx +2 -2
- package/corpus/core/docs/content/template-content-developers.mdx +2 -2
- package/corpus/core/docs/content/template-content.mdx +1 -1
- package/corpus/core/docs/content/template-design-brand-and-figma.mdx +1 -1
- package/corpus/core/docs/content/template-design-developers.mdx +2 -2
- package/corpus/core/docs/content/template-design.mdx +2 -2
- package/corpus/core/docs/content/template-dispatch-developers.mdx +5 -5
- package/corpus/core/docs/content/template-forms-developers.mdx +1 -1
- package/corpus/core/docs/content/template-forms.mdx +1 -1
- package/corpus/core/docs/content/template-mail-developers.mdx +2 -2
- package/corpus/core/docs/content/template-mail.mdx +2 -2
- package/corpus/core/docs/content/template-plan-developers.mdx +3 -3
- package/corpus/core/docs/content/template-plan.mdx +3 -3
- package/corpus/core/docs/content/template-slides-developers.mdx +4 -4
- package/corpus/core/docs/content/template-slides.mdx +1 -1
- package/corpus/core/docs/content/what-is-agent-native.mdx +5 -5
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/auth-policy.ts +1 -1
- package/corpus/core/src/agent/production-agent.ts +14 -1
- package/corpus/core/src/agent/run-manager.ts +6 -13
- package/corpus/core/src/cli/index.ts +11 -1
- package/corpus/core/src/client/route-chunk-recovery.ts +18 -7
- package/corpus/core/src/db/client.ts +7 -6
- package/corpus/core/src/db/migrations.ts +2 -1
- package/corpus/core/src/deploy/build.ts +290 -4
- package/corpus/core/src/deploy/nitro-preset.ts +50 -0
- package/corpus/core/src/integrations/pending-tasks-retry-job.ts +2 -1
- package/corpus/core/src/scripts/call-agent.ts +4 -2
- package/corpus/core/src/server/agent-discovery.ts +1 -0
- package/corpus/core/src/server/agent-run-context.ts +13 -3
- package/corpus/core/src/server/request-context.ts +2 -0
- package/corpus/core/src/shared/cloudflare-globals.d.ts +2 -1
- package/corpus/core/src/shared/runtime.ts +2 -0
- package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +29 -0
- package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/de-DE.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/en-US.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/es-ES.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +6 -6
- package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
- package/corpus/core/src/vite/client.ts +20 -1
- package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +9 -2
- package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +3 -0
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -1
- package/corpus/templates/analytics/app/global.css +10 -9
- package/corpus/templates/analytics/app/hooks/use-chart-tooltip-portal.ts +59 -18
- package/corpus/templates/analytics/app/hooks/use-dashboard-revisions.ts +14 -2
- package/corpus/templates/analytics/app/i18n-data.ts +102 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +232 -2
- package/corpus/templates/analytics/changelog/2026-07-27-chart-tooltips-stay-beside-the-cursor-when-they-extend-beyon.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-chat-no-longer-claims-a-data-source-is-disconnected-when-it-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-dashboard-changes-can-be-undone-with-cmd-z-and-actions-menu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-dashboard-redo-with-cmd-shift-z-and-actions-menu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-dashboard-skeletons-are-subtler-and-smoother.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +61 -20
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -3
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +7 -32
- package/corpus/templates/calendar/changelog/2026-07-27-calendar-s-sidebar-now-uses-a-cleaner-layout-with-fewer-util.md +6 -0
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +14 -6
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +32 -37
- package/corpus/templates/content/changelog/2026-07-27-content-sidebar-page-lists-now-load-with-quiet-skeletons-and.md +6 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +16 -18
- package/corpus/templates/design/changelog/2026-07-27-collapsed-sidebar-keeps-the-agent-native-logo.md +6 -0
- package/corpus/templates/dispatch/AGENTS.md +7 -2
- package/corpus/templates/dispatch/actions/view-screen.ts +3 -2
- package/corpus/templates/dispatch/changelog/2026-07-17-curated-template-remixes.md +1 -1
- package/corpus/templates/dispatch/changelog/2026-07-27-creating-an-app-from-a-curated-template-now-uses-clearer-mor.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-27-thread-debug-accepts-request-run-ids.md +6 -0
- package/corpus/templates/slides/actions/create-design-system.ts +10 -2
- package/corpus/templates/slides/actions/import-file.ts +173 -1
- package/corpus/templates/slides/actions/list-slide-comments.ts +16 -1
- package/corpus/templates/slides/actions/update-design-system.ts +13 -2
- package/corpus/templates/slides/app/components/editor/AssetLibraryPanel.tsx +35 -12
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +36 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +3 -0
- package/corpus/templates/slides/app/lib/dynamic-import.ts +63 -0
- package/corpus/templates/slides/app/lib/export-pdf-client.ts +3 -2
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +5 -2
- package/corpus/templates/slides/app/pages/DesignSystems.tsx +9 -1
- package/corpus/templates/slides/server/db/schema.ts +11 -0
- package/corpus/templates/slides/server/handlers/assets.ts +64 -19
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +38 -0
- package/corpus/templates/slides/server/plugins/db.ts +20 -0
- package/corpus/templates/slides/shared/design-system-validation.ts +62 -0
- package/dist/a2a/auth-policy.js +1 -1
- package/dist/a2a/auth-policy.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +2 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +4 -10
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/cli/index.js +8 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/client/route-chunk-recovery.d.ts.map +1 -1
- package/dist/client/route-chunk-recovery.js +16 -7
- package/dist/client/route-chunk-recovery.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +4 -2
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +2 -1
- package/dist/db/migrations.js.map +1 -1
- package/dist/deploy/build.d.ts +25 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +227 -4
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/nitro-preset.d.ts +8 -0
- package/dist/deploy/nitro-preset.d.ts.map +1 -0
- package/dist/deploy/nitro-preset.js +36 -0
- package/dist/deploy/nitro-preset.js.map +1 -0
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +2 -1
- package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +4 -2
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js +1 -0
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/agent-run-context.d.ts.map +1 -1
- package/dist/server/agent-run-context.js +10 -3
- package/dist/server/agent-run-context.js.map +1 -1
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/runtime.d.ts.map +1 -1
- package/dist/shared/runtime.js +2 -0
- package/dist/shared/runtime.js.map +1 -1
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +29 -0
- package/dist/templates/default/app/i18n/ar-SA.ts +6 -6
- package/dist/templates/default/app/i18n/de-DE.ts +6 -6
- package/dist/templates/default/app/i18n/en-US.ts +6 -6
- package/dist/templates/default/app/i18n/es-ES.ts +6 -6
- package/dist/templates/default/app/i18n/fr-FR.ts +6 -6
- package/dist/templates/default/app/i18n/hi-IN.ts +6 -6
- package/dist/templates/default/app/i18n/ja-JP.ts +6 -6
- package/dist/templates/default/app/i18n/ko-KR.ts +6 -6
- package/dist/templates/default/app/i18n/pt-BR.ts +6 -6
- package/dist/templates/default/app/i18n/zh-CN.ts +6 -6
- package/dist/templates/default/app/i18n/zh-TW.ts +6 -6
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
- package/dist/vite/client.d.ts +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +16 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/cloneable-saas.mdx +9 -9
- package/docs/content/creating-templates.mdx +2 -2
- package/docs/content/deployment.mdx +23 -0
- package/docs/content/faq.mdx +4 -4
- package/docs/content/key-concepts.mdx +1 -1
- package/docs/content/multi-app-workspace.mdx +1 -1
- package/docs/content/syncing-template-changes.mdx +1 -1
- package/docs/content/template-analytics-developers.mdx +2 -2
- package/docs/content/template-analytics.mdx +2 -2
- package/docs/content/template-assets-developers.mdx +3 -3
- package/docs/content/template-assets.mdx +2 -2
- package/docs/content/template-brain-developers.mdx +2 -2
- package/docs/content/template-brain.mdx +1 -1
- package/docs/content/template-calendar-developers.mdx +1 -1
- package/docs/content/template-calendar.mdx +1 -1
- package/docs/content/template-chat.mdx +1 -1
- package/docs/content/template-clips-developers.mdx +4 -4
- package/docs/content/template-clips.mdx +2 -2
- package/docs/content/template-content-developers.mdx +2 -2
- package/docs/content/template-content.mdx +1 -1
- package/docs/content/template-design-brand-and-figma.mdx +1 -1
- package/docs/content/template-design-developers.mdx +2 -2
- package/docs/content/template-design.mdx +2 -2
- package/docs/content/template-dispatch-developers.mdx +5 -5
- package/docs/content/template-forms-developers.mdx +1 -1
- package/docs/content/template-forms.mdx +1 -1
- package/docs/content/template-mail-developers.mdx +2 -2
- package/docs/content/template-mail.mdx +2 -2
- package/docs/content/template-plan-developers.mdx +3 -3
- package/docs/content/template-plan.mdx +3 -3
- package/docs/content/template-slides-developers.mdx +4 -4
- package/docs/content/template-slides.mdx +1 -1
- package/docs/content/what-is-agent-native.mdx +5 -5
- package/package.json +1 -1
- package/src/a2a/auth-policy.ts +1 -1
- package/src/agent/production-agent.ts +14 -1
- package/src/agent/run-manager.ts +6 -13
- package/src/cli/index.ts +11 -1
- package/src/client/route-chunk-recovery.ts +18 -7
- package/src/db/client.ts +7 -6
- package/src/db/migrations.ts +2 -1
- package/src/deploy/build.ts +290 -4
- package/src/deploy/nitro-preset.ts +50 -0
- package/src/integrations/pending-tasks-retry-job.ts +2 -1
- package/src/scripts/call-agent.ts +4 -2
- package/src/server/agent-discovery.ts +1 -0
- package/src/server/agent-run-context.ts +13 -3
- package/src/server/request-context.ts +2 -0
- package/src/shared/cloudflare-globals.d.ts +2 -1
- package/src/shared/runtime.ts +2 -0
- package/src/templates/default/.agents/skills/secrets/SKILL.md +29 -0
- package/src/templates/default/app/i18n/ar-SA.ts +6 -6
- package/src/templates/default/app/i18n/de-DE.ts +6 -6
- package/src/templates/default/app/i18n/en-US.ts +6 -6
- package/src/templates/default/app/i18n/es-ES.ts +6 -6
- package/src/templates/default/app/i18n/fr-FR.ts +6 -6
- package/src/templates/default/app/i18n/hi-IN.ts +6 -6
- package/src/templates/default/app/i18n/ja-JP.ts +6 -6
- package/src/templates/default/app/i18n/ko-KR.ts +6 -6
- package/src/templates/default/app/i18n/pt-BR.ts +6 -6
- package/src/templates/default/app/i18n/zh-CN.ts +6 -6
- package/src/templates/default/app/i18n/zh-TW.ts +6 -6
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
- package/src/vite/client.ts +20 -1
- /package/corpus/templates/slides/server/routes/api/assets/{[filename].delete.ts → [id].delete.ts} +0 -0
|
@@ -322,19 +322,19 @@ Agent-native isn't just for developers. Because the agent can edit the app's own
|
|
|
322
322
|
|
|
323
323
|
The vision: fewer handoffs, one person doing the work of a small team.
|
|
324
324
|
|
|
325
|
-
##
|
|
325
|
+
## Start from a template and customize {#fork-and-customize}
|
|
326
326
|
|
|
327
|
-
Agent-native apps follow a
|
|
327
|
+
Agent-native apps follow a start-from-a-working-app model. You start from a **template** — Calendar, Content, Slides, Analytics, Mail, Clips, Design, Forms, Dispatch — and make it yours. Each one is a complete, working SaaS product, not a blank scaffold:
|
|
328
328
|
|
|
329
329
|
1. Pick a template on [agent-native.com/templates](/templates)
|
|
330
330
|
2. Use it immediately as a hosted app (e.g. mail.agent-native.com)
|
|
331
|
-
3.
|
|
331
|
+
3. Create your own app from the template when you want to customize — "connect our Stripe account," "add a cohort chart"
|
|
332
332
|
4. The agent modifies the code to match your needs
|
|
333
|
-
5. Deploy your
|
|
333
|
+
5. Deploy your app to your own domain — or stay on agent-native.com
|
|
334
334
|
|
|
335
335
|
Because it's _your_ app, not shared infrastructure, the agent can safely evolve the code. Your app keeps improving as you use it. See [Templates](/docs/cloneable-saas) for the full story.
|
|
336
336
|
|
|
337
|
-
Not ready to
|
|
337
|
+
Not ready to start from a whole template? You can also try agent-native by adding a **skill** to a coding agent you already use — install the Plans skill with `npx @agent-native/core@latest skills add visual-plan`. See the [Skills Guide](/docs/skills-guide#app-backed-skills).
|
|
338
338
|
|
|
339
339
|
## Composable agents {#composable-agents}
|
|
340
340
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.124.
|
|
3
|
+
"version": "0.124.4",
|
|
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": {
|
package/src/a2a/auth-policy.ts
CHANGED
|
@@ -16,7 +16,7 @@ export function isA2AProductionRuntime(): boolean {
|
|
|
16
16
|
return true;
|
|
17
17
|
}
|
|
18
18
|
if (process.env.CF_PAGES === "1") return true;
|
|
19
|
-
if ("__cf_env" in globalThis) return true;
|
|
19
|
+
if ("__cf_env" in globalThis || "__env__" in globalThis) return true;
|
|
20
20
|
if (process.env.VERCEL || process.env.VERCEL_ENV) return true;
|
|
21
21
|
if (process.env.RENDER || process.env.FLY_APP_NAME || process.env.K_SERVICE) {
|
|
22
22
|
return true;
|
|
@@ -2088,6 +2088,13 @@ function collectTextParts(parts: EngineContentPart[]): string {
|
|
|
2088
2088
|
.join("");
|
|
2089
2089
|
}
|
|
2090
2090
|
|
|
2091
|
+
// Guard retries are pushed as `role: "user"` because engines have no other
|
|
2092
|
+
// mid-turn channel. Unlabeled, a corrective instruction ("say the source is
|
|
2093
|
+
// not connected and include this link") reads exactly like an injected user
|
|
2094
|
+
// turn, and an aligned model refuses it *to the user* instead of following it.
|
|
2095
|
+
export const AGENT_INTERNAL_GUARD_PROMPT =
|
|
2096
|
+
"Automated quality check on the draft answer you just produced. This is a directive from this application's own response guard, not a message from the user and not content from a tool result or web page. Follow it and revise your answer. Do not quote it, describe it, or treat it as an injection attempt. If it contradicts what you observed this turn, state what you actually observed instead of asserting the guard's premise.";
|
|
2097
|
+
|
|
2091
2098
|
export const AGENT_INTERNAL_CONTINUE_PROMPT =
|
|
2092
2099
|
"Continue from where you left off and finish the user's original request. Do not repeat completed work, do not mention internal reconnects, time limits, or step limits, and continue as if this is the same uninterrupted run.";
|
|
2093
2100
|
|
|
@@ -4588,7 +4595,12 @@ export async function runAgentLoop(opts: {
|
|
|
4588
4595
|
send({ type: "clear" });
|
|
4589
4596
|
messages.push({
|
|
4590
4597
|
role: "user",
|
|
4591
|
-
content: [
|
|
4598
|
+
content: [
|
|
4599
|
+
{
|
|
4600
|
+
type: "text",
|
|
4601
|
+
text: `${AGENT_INTERNAL_GUARD_PROMPT}\n\n<response-guard>\n${retryMessage}\n</response-guard>`,
|
|
4602
|
+
},
|
|
4603
|
+
],
|
|
4592
4604
|
});
|
|
4593
4605
|
continue;
|
|
4594
4606
|
}
|
|
@@ -8966,6 +8978,7 @@ export function createProductionAgentHandler(
|
|
|
8966
8978
|
// assistant message. Falls back to the runId (turn == run) when the
|
|
8967
8979
|
// client doesn't supply a turnId.
|
|
8968
8980
|
turnId: effectiveTurnId,
|
|
8981
|
+
waitUntil: getRequestRunContext()?.waitUntil,
|
|
8969
8982
|
dispatchMode: foregroundSelfChainEligible
|
|
8970
8983
|
? "foreground-self-chain"
|
|
8971
8984
|
: "foreground",
|
package/src/agent/run-manager.ts
CHANGED
|
@@ -348,6 +348,8 @@ function shouldCaptureRunError(err: unknown): boolean {
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
export interface StartRunOptions {
|
|
351
|
+
/** Keep a request-scoped serverless invocation alive for this run. */
|
|
352
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
351
353
|
/** Optional internal run chunk budget. When reached, the framework emits an
|
|
352
354
|
* auto-continuation signal instead of a user-facing timeout. Leave unset for
|
|
353
355
|
* no framework-imposed run timeout. */
|
|
@@ -1344,19 +1346,10 @@ export function startRun(
|
|
|
1344
1346
|
});
|
|
1345
1347
|
runPromise.then(resolveFinalized, rejectFinalized);
|
|
1346
1348
|
|
|
1347
|
-
//
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
__cf_ctx?: { waitUntil(promise: Promise<unknown>): void };
|
|
1352
|
-
}
|
|
1353
|
-
).__cf_ctx;
|
|
1354
|
-
if (cfCtx?.waitUntil) {
|
|
1355
|
-
cfCtx.waitUntil(runPromise);
|
|
1356
|
-
}
|
|
1357
|
-
} catch {
|
|
1358
|
-
// Not on Workers — ignore
|
|
1359
|
-
}
|
|
1349
|
+
// Keep the originating request alive when its runtime supports background
|
|
1350
|
+
// work. The callback is passed through request context so concurrent Worker
|
|
1351
|
+
// requests cannot overwrite one another through a global binding.
|
|
1352
|
+
options?.waitUntil?.(runPromise);
|
|
1360
1353
|
|
|
1361
1354
|
return run;
|
|
1362
1355
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -8,6 +8,10 @@ import { fileURLToPath } from "url";
|
|
|
8
8
|
|
|
9
9
|
import * as Sentry from "@sentry/node";
|
|
10
10
|
|
|
11
|
+
import {
|
|
12
|
+
clearAgentNativeNitroPresetMarker,
|
|
13
|
+
resolveAgentNativeNitroPreset,
|
|
14
|
+
} from "../deploy/nitro-preset.js";
|
|
11
15
|
import { resolveDeployPostBuildInvocation } from "./deploy-build.js";
|
|
12
16
|
import { shouldTrackCliRun } from "./telemetry-routing.js";
|
|
13
17
|
import { createCliTelemetry } from "./telemetry.js";
|
|
@@ -696,6 +700,7 @@ switch (command) {
|
|
|
696
700
|
}
|
|
697
701
|
|
|
698
702
|
if (isReactRouterFramework()) {
|
|
703
|
+
clearAgentNativeNitroPresetMarker();
|
|
699
704
|
validateReactRouterBuildDependencies();
|
|
700
705
|
const rr = findReactRouterBin();
|
|
701
706
|
console.log("Building (React Router framework mode)...");
|
|
@@ -709,15 +714,20 @@ switch (command) {
|
|
|
709
714
|
// Post-build: framework-mode apps also need a Nitro server bundle for
|
|
710
715
|
// `agent-native start` and for serverless presets.
|
|
711
716
|
if (isReactRouterFramework()) {
|
|
717
|
+
const configuredNitroPreset = resolveAgentNativeNitroPreset();
|
|
718
|
+
const deployEnv = configuredNitroPreset
|
|
719
|
+
? { ...process.env, NITRO_PRESET: configuredNitroPreset }
|
|
720
|
+
: process.env;
|
|
712
721
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
713
722
|
const deployBuild = resolveDeployPostBuildInvocation({
|
|
714
723
|
cliDir: __dirname,
|
|
724
|
+
env: deployEnv,
|
|
715
725
|
findTsxBin,
|
|
716
726
|
});
|
|
717
727
|
if (deployBuild) {
|
|
718
728
|
await runBuildStep(deployBuild.command, deployBuild.args, {
|
|
719
729
|
label: "deploy-build",
|
|
720
|
-
env:
|
|
730
|
+
env: deployEnv,
|
|
721
731
|
});
|
|
722
732
|
} else {
|
|
723
733
|
console.warn(
|
|
@@ -190,16 +190,27 @@ function recoverToIntendedNavigation(
|
|
|
190
190
|
state: RouteChunkRecoveryState,
|
|
191
191
|
): boolean {
|
|
192
192
|
const target = getFreshIntendedNavigation(state, win.location.href);
|
|
193
|
-
|
|
193
|
+
const sameCurrentTarget =
|
|
194
|
+
!target &&
|
|
195
|
+
state.intendedHref === win.location.href &&
|
|
196
|
+
Date.now() - state.intendedAt <= INTENDED_NAV_MAX_AGE_MS
|
|
197
|
+
? state.intendedHref
|
|
198
|
+
: null;
|
|
199
|
+
const recoveryTarget = target ?? sameCurrentTarget;
|
|
200
|
+
if (!recoveryTarget) return false;
|
|
194
201
|
state.recovering = true;
|
|
195
|
-
state.recoveryHref =
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
|
|
202
|
+
state.recoveryHref = recoveryTarget;
|
|
203
|
+
// Keep the desktop shell mounted, but replace only the route that failed to
|
|
204
|
+
// load. A current-page reload remains suppressed below when there is no
|
|
205
|
+
// intended cross-route destination to recover.
|
|
206
|
+
if (isAgentNativeDesktop(win)) {
|
|
207
|
+
hardNavigate(win, recoveryTarget);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
199
210
|
try {
|
|
200
|
-
win.history.replaceState(win.history.state, "",
|
|
211
|
+
win.history.replaceState(win.history.state, "", recoveryTarget);
|
|
201
212
|
} catch {}
|
|
202
|
-
hardNavigate(win,
|
|
213
|
+
hardNavigate(win, recoveryTarget);
|
|
203
214
|
return true;
|
|
204
215
|
}
|
|
205
216
|
|
package/src/db/client.ts
CHANGED
|
@@ -65,11 +65,11 @@ export interface DbExecConfig {
|
|
|
65
65
|
/** Read the request-scoped Cloudflare binding without requiring every
|
|
66
66
|
* consuming app's TypeScript program to include core's ambient Worker globals. */
|
|
67
67
|
export function getCloudflareD1Binding(): unknown {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
const runtime = globalThis as typeof globalThis & {
|
|
69
|
+
__cf_env?: { DB?: unknown };
|
|
70
|
+
__env__?: { DB?: unknown };
|
|
71
|
+
};
|
|
72
|
+
return runtime.__cf_env?.DB ?? runtime.__env__?.DB;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// ---------------------------------------------------------------------------
|
|
@@ -451,7 +451,8 @@ export function getDialect(): Dialect {
|
|
|
451
451
|
return _dialect;
|
|
452
452
|
}
|
|
453
453
|
|
|
454
|
-
// Don't cache the fallthrough — on CF Workers, env bindings (__cf_env)
|
|
454
|
+
// Don't cache the fallthrough — on CF Workers, env bindings (__cf_env/__env__)
|
|
455
|
+
// aren't
|
|
455
456
|
// available at import time. If we cache "sqlite" here, D1 will never be
|
|
456
457
|
// detected once the bindings are set in the fetch handler.
|
|
457
458
|
return "sqlite";
|
package/src/db/migrations.ts
CHANGED
|
@@ -682,7 +682,8 @@ export function runMigrations(
|
|
|
682
682
|
!!globalThis.process?.env?.NETLIFY ||
|
|
683
683
|
!!globalThis.process?.env?.AWS_LAMBDA_FUNCTION_NAME ||
|
|
684
684
|
!!globalThis.process?.env?.VERCEL ||
|
|
685
|
-
"__cf_env" in globalThis
|
|
685
|
+
"__cf_env" in globalThis ||
|
|
686
|
+
"__env__" in globalThis;
|
|
686
687
|
if (typeof globalThis.process?.exit === "function" && !isServerless) {
|
|
687
688
|
process.exit(1);
|
|
688
689
|
}
|
package/src/deploy/build.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Supported presets:
|
|
12
12
|
* - cloudflare_pages: Outputs dist/ with _worker.js for Cloudflare Pages
|
|
13
|
+
* - cloudflare_module: Outputs a native Cloudflare Worker under .output/server
|
|
13
14
|
*
|
|
14
15
|
* Usage: node deploy/build.js (called automatically by `agent-native build`)
|
|
15
16
|
*/
|
|
@@ -73,6 +74,171 @@ import {
|
|
|
73
74
|
|
|
74
75
|
const cwd = process.cwd();
|
|
75
76
|
const preset = process.env.NITRO_PRESET || "node";
|
|
77
|
+
export const CLOUDFLARE_MODULE_PRESETS = [
|
|
78
|
+
"cloudflare_module",
|
|
79
|
+
"cloudflare-module",
|
|
80
|
+
] as const;
|
|
81
|
+
|
|
82
|
+
export function isCloudflareModulePreset(targetPreset: string): boolean {
|
|
83
|
+
return (CLOUDFLARE_MODULE_PRESETS as readonly string[]).includes(
|
|
84
|
+
targetPreset,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const CLOUDFLARE_MODULE_WORKER_ENTRY = "worker.mjs";
|
|
89
|
+
|
|
90
|
+
export function generateCloudflareModuleWorkerEntry(): string {
|
|
91
|
+
return `let handler;
|
|
92
|
+
|
|
93
|
+
export * from "./index.mjs";
|
|
94
|
+
|
|
95
|
+
async function loadHandler() {
|
|
96
|
+
handler ??= (await import("./index.mjs")).default;
|
|
97
|
+
return handler;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function initializeBindings(env) {
|
|
101
|
+
if (!env) return;
|
|
102
|
+
globalThis.__cf_env = env;
|
|
103
|
+
globalThis.__env__ = env;
|
|
104
|
+
globalThis.process = globalThis.process || { env: {} };
|
|
105
|
+
globalThis.process.env = globalThis.process.env || {};
|
|
106
|
+
for (const [key, value] of Object.entries(env)) {
|
|
107
|
+
if (typeof value === "string") globalThis.process.env[key] = value;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default {
|
|
112
|
+
async fetch(request, env, ctx) {
|
|
113
|
+
if (typeof ctx?.waitUntil === "function") {
|
|
114
|
+
request.waitUntil = ctx.waitUntil.bind(ctx);
|
|
115
|
+
}
|
|
116
|
+
initializeBindings(env);
|
|
117
|
+
return (await loadHandler()).fetch(request, env, ctx);
|
|
118
|
+
},
|
|
119
|
+
async scheduled(controller, env, ctx) {
|
|
120
|
+
initializeBindings(env);
|
|
121
|
+
return (await loadHandler()).scheduled?.(controller, env, ctx);
|
|
122
|
+
},
|
|
123
|
+
async email(message, env, ctx) {
|
|
124
|
+
initializeBindings(env);
|
|
125
|
+
return (await loadHandler()).email?.(message, env, ctx);
|
|
126
|
+
},
|
|
127
|
+
async queue(batch, env, ctx) {
|
|
128
|
+
initializeBindings(env);
|
|
129
|
+
return (await loadHandler()).queue?.(batch, env, ctx);
|
|
130
|
+
},
|
|
131
|
+
async tail(traces, env, ctx) {
|
|
132
|
+
initializeBindings(env);
|
|
133
|
+
return (await loadHandler()).tail?.(traces, env, ctx);
|
|
134
|
+
},
|
|
135
|
+
async trace(traces, env, ctx) {
|
|
136
|
+
initializeBindings(env);
|
|
137
|
+
return (await loadHandler()).trace?.(traces, env, ctx);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function patchCloudflareModuleNitroEntry(code: string): string {
|
|
144
|
+
const factoryMatch = code.match(
|
|
145
|
+
/function ([A-Za-z_$][\w$]*)\(e\)\{let ([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*)\(\),([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*)\(\);return\{async fetch\(([A-Za-z_$][\w$]*),([A-Za-z_$][\w$]*),([A-Za-z_$][\w$]*)\)\{/,
|
|
146
|
+
);
|
|
147
|
+
if (!factoryMatch) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
"[deploy] Could not find Nitro's Cloudflare module handler factory",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const [
|
|
154
|
+
,
|
|
155
|
+
factoryName,
|
|
156
|
+
handlerName,
|
|
157
|
+
handlerFactoryName,
|
|
158
|
+
hooksName,
|
|
159
|
+
hooksFactoryName,
|
|
160
|
+
requestName,
|
|
161
|
+
envName,
|
|
162
|
+
contextName,
|
|
163
|
+
] = factoryMatch;
|
|
164
|
+
const eagerInitialization = `let ${handlerName}=${handlerFactoryName}(),${hooksName}=${hooksFactoryName}();`;
|
|
165
|
+
if (!code.includes(eagerInitialization)) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`[deploy] Nitro's ${factoryName} handler changed its initialization shape`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const bindingMatch = code.match(
|
|
172
|
+
new RegExp(
|
|
173
|
+
`globalThis\\.__env__=${envName},([A-Za-z_$][\\w$]*)\\(${requestName},\\{env:${envName},context:${contextName}\\}\\);?`,
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
if (!bindingMatch) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`[deploy] Nitro's ${factoryName} handler does not initialize Cloudflare bindings as expected`,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
const bindingInitialization = bindingMatch[0];
|
|
182
|
+
|
|
183
|
+
let patched = code.replace(
|
|
184
|
+
eagerInitialization,
|
|
185
|
+
`let ${handlerName},${hooksName};`,
|
|
186
|
+
);
|
|
187
|
+
patched = patched.replace(
|
|
188
|
+
bindingInitialization,
|
|
189
|
+
`${bindingInitialization}${handlerName}??=${handlerFactoryName}();`,
|
|
190
|
+
);
|
|
191
|
+
const hookCall = `${hooksName}.callHook(`;
|
|
192
|
+
if (!patched.includes(hookCall)) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`[deploy] Nitro's ${factoryName} handler has no Cloudflare lifecycle hooks`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
patched = patched
|
|
198
|
+
.split(hookCall)
|
|
199
|
+
.join(`(${hooksName}??=${hooksFactoryName}()).callHook(`);
|
|
200
|
+
|
|
201
|
+
return patched;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function configureCloudflareModuleWorkerOutput(serverDir: string): void {
|
|
205
|
+
const configPath = path.join(serverDir, "wrangler.json");
|
|
206
|
+
if (!fs.existsSync(configPath)) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`[deploy] Nitro did not generate ${configPath} for cloudflare_module`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const nitroEntryPath = path.join(serverDir, "index.mjs");
|
|
212
|
+
if (!fs.existsSync(nitroEntryPath)) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`[deploy] Nitro did not generate ${nitroEntryPath} for cloudflare_module`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8")) as {
|
|
219
|
+
main?: string;
|
|
220
|
+
compatibility_flags?: unknown;
|
|
221
|
+
[key: string]: unknown;
|
|
222
|
+
};
|
|
223
|
+
config.main = CLOUDFLARE_MODULE_WORKER_ENTRY;
|
|
224
|
+
const compatibilityFlags = Array.isArray(config.compatibility_flags)
|
|
225
|
+
? config.compatibility_flags.filter(
|
|
226
|
+
(flag): flag is string => typeof flag === "string",
|
|
227
|
+
)
|
|
228
|
+
: [];
|
|
229
|
+
config.compatibility_flags = [
|
|
230
|
+
...new Set([...compatibilityFlags, "nodejs_compat"]),
|
|
231
|
+
];
|
|
232
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
233
|
+
fs.writeFileSync(
|
|
234
|
+
nitroEntryPath,
|
|
235
|
+
patchCloudflareModuleNitroEntry(fs.readFileSync(nitroEntryPath, "utf8")),
|
|
236
|
+
);
|
|
237
|
+
fs.writeFileSync(
|
|
238
|
+
path.join(serverDir, CLOUDFLARE_MODULE_WORKER_ENTRY),
|
|
239
|
+
generateCloudflareModuleWorkerEntry(),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
76
242
|
export const NITRO_RUNTIME_IGNORE_PATTERNS = [
|
|
77
243
|
"**/*.spec.ts",
|
|
78
244
|
"**/*.spec.tsx",
|
|
@@ -893,7 +1059,9 @@ function requestWithMountedApiPrefixStripped(request) {
|
|
|
893
1059
|
return request;
|
|
894
1060
|
}
|
|
895
1061
|
url.pathname = strippedPathname;
|
|
896
|
-
|
|
1062
|
+
const rewritten = new Request(url, request);
|
|
1063
|
+
rewritten.waitUntil = request.waitUntil;
|
|
1064
|
+
return rewritten;
|
|
897
1065
|
}
|
|
898
1066
|
|
|
899
1067
|
function prefixMountedPath(path, basePath) {
|
|
@@ -1347,8 +1515,10 @@ ${
|
|
|
1347
1515
|
|
|
1348
1516
|
export default {
|
|
1349
1517
|
async fetch(request, env, ctx) {
|
|
1350
|
-
//
|
|
1351
|
-
if (ctx
|
|
1518
|
+
// Attach the request-scoped continuation hook before any URL rewrite.
|
|
1519
|
+
if (typeof ctx?.waitUntil === "function") {
|
|
1520
|
+
request.waitUntil = ctx.waitUntil.bind(ctx);
|
|
1521
|
+
}
|
|
1352
1522
|
if (env) {
|
|
1353
1523
|
globalThis.process = globalThis.process || { env: {} };
|
|
1354
1524
|
globalThis.process.env = globalThis.process.env || {};
|
|
@@ -2959,6 +3129,23 @@ export function bundleYjsRuntimeForServerlessOutput(
|
|
|
2959
3129
|
);
|
|
2960
3130
|
}
|
|
2961
3131
|
|
|
3132
|
+
walkServerJavaScriptFiles(serverDir, (filePath) => {
|
|
3133
|
+
const bundledImport = path
|
|
3134
|
+
.relative(path.dirname(filePath), bundledYjsPath)
|
|
3135
|
+
.split(path.sep)
|
|
3136
|
+
.join("/");
|
|
3137
|
+
const relativeBundledImport = bundledImport.startsWith(".")
|
|
3138
|
+
? bundledImport
|
|
3139
|
+
: `./${bundledImport}`;
|
|
3140
|
+
const source = fs.readFileSync(filePath, "utf-8");
|
|
3141
|
+
const rewritten = source.replace(
|
|
3142
|
+
/(\b(?:from\s*|import\s*\(\s*|import\s*))(["'])\.\.?\/(?:\.\.\/)*_libs\/yjs\.mjs\2/g,
|
|
3143
|
+
(_match, importPrefix: string, quote: string) =>
|
|
3144
|
+
`${importPrefix}${quote}${relativeBundledImport}${quote}`,
|
|
3145
|
+
);
|
|
3146
|
+
if (rewritten !== source) fs.writeFileSync(filePath, rewritten);
|
|
3147
|
+
});
|
|
3148
|
+
|
|
2962
3149
|
return bareImports;
|
|
2963
3150
|
}
|
|
2964
3151
|
|
|
@@ -3601,6 +3788,53 @@ const BROWSER_ONLY_SERVER_LIBS = [
|
|
|
3601
3788
|
"mermaid",
|
|
3602
3789
|
];
|
|
3603
3790
|
|
|
3791
|
+
/**
|
|
3792
|
+
* Packages that Nitro can discover through the shared server graph but that
|
|
3793
|
+
* cannot be evaluated in a Cloudflare Worker. Keep the network-capable SDKs
|
|
3794
|
+
* real; these are limited to Node/native/browser-runtime packages whose
|
|
3795
|
+
* server-side paths already fail closed when the capability is unavailable.
|
|
3796
|
+
*/
|
|
3797
|
+
export const CLOUDFLARE_MODULE_STUB_MODULES = [
|
|
3798
|
+
"@napi-rs/canvas",
|
|
3799
|
+
"@resvg/resvg-js",
|
|
3800
|
+
"@sentry/node",
|
|
3801
|
+
"@sparticuz/chromium-min",
|
|
3802
|
+
"better-sqlite3",
|
|
3803
|
+
"chartjs-node-canvas",
|
|
3804
|
+
"chokidar",
|
|
3805
|
+
"fsevents",
|
|
3806
|
+
"node-pty",
|
|
3807
|
+
"playwright",
|
|
3808
|
+
"playwright-core",
|
|
3809
|
+
] as const;
|
|
3810
|
+
|
|
3811
|
+
/**
|
|
3812
|
+
* Mirror the fail-closed package stubs used by the Pages bundler in Nitro's
|
|
3813
|
+
* Rolldown graph. Without this, the native module preset emits a valid module
|
|
3814
|
+
* graph that still crashes at Worker cold start when a Node-only optional
|
|
3815
|
+
* dependency is evaluated.
|
|
3816
|
+
*/
|
|
3817
|
+
export function createCloudflareModuleStubPlugin() {
|
|
3818
|
+
const stubbed = new Set<string>(CLOUDFLARE_MODULE_STUB_MODULES);
|
|
3819
|
+
const stubIdPrefix = "\0agent-native-cloudflare-module-stub:";
|
|
3820
|
+
|
|
3821
|
+
return {
|
|
3822
|
+
name: "agent-native-cloudflare-module-stub",
|
|
3823
|
+
resolveId(id: string) {
|
|
3824
|
+
const packageName = id.startsWith("@")
|
|
3825
|
+
? id.split("/").slice(0, 2).join("/")
|
|
3826
|
+
: id.split("/")[0];
|
|
3827
|
+
if (!stubbed.has(packageName) || id !== packageName) return null;
|
|
3828
|
+
return `${stubIdPrefix}${packageName}`;
|
|
3829
|
+
},
|
|
3830
|
+
load(id: string) {
|
|
3831
|
+
if (!id.startsWith(stubIdPrefix)) return null;
|
|
3832
|
+
const packageName = id.slice(stubIdPrefix.length);
|
|
3833
|
+
return CLOUDFLARE_WORKER_STUB_MODULES[packageName] ?? null;
|
|
3834
|
+
},
|
|
3835
|
+
};
|
|
3836
|
+
}
|
|
3837
|
+
|
|
3604
3838
|
/**
|
|
3605
3839
|
* Dependencies Nitro itself must bundle outside the controlled serverless
|
|
3606
3840
|
* output pass. Netlify, Vercel, and Lambda keep Yjs external through Nitro;
|
|
@@ -3811,7 +4045,12 @@ export default bundle;
|
|
|
3811
4045
|
...(preset === "netlify" || preset === "vercel" || preset === "aws-lambda"
|
|
3812
4046
|
? { external: ["yjs"] }
|
|
3813
4047
|
: {}),
|
|
3814
|
-
plugins: [
|
|
4048
|
+
plugins: [
|
|
4049
|
+
...(preset.startsWith("cloudflare")
|
|
4050
|
+
? [createCloudflareModuleStubPlugin()]
|
|
4051
|
+
: []),
|
|
4052
|
+
createBrowserOnlyServerStubPlugin(),
|
|
4053
|
+
],
|
|
3815
4054
|
},
|
|
3816
4055
|
...(providedPluginsNitroPlugin
|
|
3817
4056
|
? { plugins: [providedPluginsNitroPlugin] }
|
|
@@ -3833,6 +4072,10 @@ export default bundle;
|
|
|
3833
4072
|
cwd,
|
|
3834
4073
|
});
|
|
3835
4074
|
|
|
4075
|
+
if (isCloudflareModulePreset(preset)) {
|
|
4076
|
+
configureCloudflareModuleWorkerOutput(nitro.options.output.serverDir);
|
|
4077
|
+
}
|
|
4078
|
+
|
|
3836
4079
|
if (preset === "netlify" || preset === "vercel" || preset === "aws-lambda") {
|
|
3837
4080
|
copyInstalledLibsqlNativePackages(nitro.options.output.serverDir);
|
|
3838
4081
|
copyInstalledResvgPackages(nitro.options.output.serverDir);
|
|
@@ -3841,6 +4084,10 @@ export default bundle;
|
|
|
3841
4084
|
bundleYjsRuntimeForServerlessOutput(nitro.options.output.serverDir, cwd);
|
|
3842
4085
|
}
|
|
3843
4086
|
|
|
4087
|
+
if (isCloudflareModulePreset(preset)) {
|
|
4088
|
+
bundleYjsRuntimeForServerlessOutput(nitro.options.output.serverDir, cwd);
|
|
4089
|
+
}
|
|
4090
|
+
|
|
3844
4091
|
if (preset === "netlify") {
|
|
3845
4092
|
emitSingleTemplateNetlifyKeepWarmFunction(cwd);
|
|
3846
4093
|
|
|
@@ -3949,8 +4196,43 @@ export default bundle;
|
|
|
3949
4196
|
if (bareImports.size > 0) {
|
|
3950
4197
|
const libsDir = path.join(outputDir, "_libs");
|
|
3951
4198
|
fs.mkdirSync(libsDir, { recursive: true });
|
|
4199
|
+
function rewriteExternalImports(mod: string, outFile: string) {
|
|
4200
|
+
function rewriteImports(dir: string) {
|
|
4201
|
+
if (!fs.existsSync(dir)) return;
|
|
4202
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
4203
|
+
const p = path.join(dir, entry.name);
|
|
4204
|
+
if (entry.isDirectory()) {
|
|
4205
|
+
rewriteImports(p);
|
|
4206
|
+
continue;
|
|
4207
|
+
}
|
|
4208
|
+
if (!entry.name.endsWith(".mjs") && !entry.name.endsWith(".js"))
|
|
4209
|
+
continue;
|
|
4210
|
+
const code = fs.readFileSync(p, "utf8");
|
|
4211
|
+
const relPath = path
|
|
4212
|
+
.relative(path.dirname(p), outFile)
|
|
4213
|
+
.replace(/\\/g, "/");
|
|
4214
|
+
const importPath = relPath.startsWith(".")
|
|
4215
|
+
? relPath
|
|
4216
|
+
: "./" + relPath;
|
|
4217
|
+
const escaped = mod.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4218
|
+
const re = new RegExp(`from["']${escaped}["']`, "g");
|
|
4219
|
+
const rewritten = code.replace(re, `from"${importPath}"`);
|
|
4220
|
+
if (rewritten !== code) fs.writeFileSync(p, rewritten);
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
rewriteImports(outputDir);
|
|
4224
|
+
}
|
|
3952
4225
|
for (const mod of bareImports) {
|
|
3953
4226
|
const outFile = path.join(libsDir, `${mod.replace(/[/@]/g, "_")}.mjs`);
|
|
4227
|
+
// Nitro may already have emitted a correctly minified module wrapper
|
|
4228
|
+
// for this dependency. Replacing that wrapper with an esbuild CJS
|
|
4229
|
+
// adapter loses its public export aliases and makes otherwise valid
|
|
4230
|
+
// sibling chunks fail during Worker module linking.
|
|
4231
|
+
if (fs.existsSync(outFile)) {
|
|
4232
|
+
console.log(`[deploy] Retaining Nitro external: ${mod}`);
|
|
4233
|
+
rewriteExternalImports(mod, outFile);
|
|
4234
|
+
continue;
|
|
4235
|
+
}
|
|
3954
4236
|
try {
|
|
3955
4237
|
// Resolve the module — check workspace node_modules and pnpm store
|
|
3956
4238
|
let resolvedMod = mod;
|
|
@@ -4244,6 +4526,10 @@ async function main() {
|
|
|
4244
4526
|
// as multi-module Workers. Use the custom esbuild-based bundler.
|
|
4245
4527
|
await buildCloudflarePages();
|
|
4246
4528
|
break;
|
|
4529
|
+
case "cloudflare_module":
|
|
4530
|
+
case "cloudflare-module":
|
|
4531
|
+
await buildWithNitro();
|
|
4532
|
+
break;
|
|
4247
4533
|
default:
|
|
4248
4534
|
// All other presets (netlify, vercel, deno_deploy, aws-lambda, etc.)
|
|
4249
4535
|
// are handled natively by Nitro's build API.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export const AGENT_NATIVE_NITRO_PRESET_MARKER = path.join(
|
|
5
|
+
".agent-native",
|
|
6
|
+
"nitro-preset",
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
function markerPath(cwd: string): string {
|
|
10
|
+
return path.join(cwd, AGENT_NATIVE_NITRO_PRESET_MARKER);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function clearAgentNativeNitroPresetMarker(cwd = process.cwd()): void {
|
|
14
|
+
fs.rmSync(markerPath(cwd), { force: true });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function writeAgentNativeNitroPresetMarker(
|
|
18
|
+
preset: string,
|
|
19
|
+
cwd = process.cwd(),
|
|
20
|
+
): void {
|
|
21
|
+
const normalizedPreset = preset.trim();
|
|
22
|
+
if (!normalizedPreset) return;
|
|
23
|
+
|
|
24
|
+
const filePath = markerPath(cwd);
|
|
25
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
26
|
+
fs.writeFileSync(filePath, `${normalizedPreset}\n`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resolveAgentNativeNitroPreset({
|
|
30
|
+
cwd = process.cwd(),
|
|
31
|
+
env = process.env,
|
|
32
|
+
}: {
|
|
33
|
+
cwd?: string;
|
|
34
|
+
env?: NodeJS.ProcessEnv;
|
|
35
|
+
} = {}): string | undefined {
|
|
36
|
+
const environmentPreset = env.NITRO_PRESET?.trim();
|
|
37
|
+
if (environmentPreset) return environmentPreset;
|
|
38
|
+
|
|
39
|
+
const filePath = markerPath(cwd);
|
|
40
|
+
try {
|
|
41
|
+
const marker = fs.readFileSync(filePath, "utf8").trim();
|
|
42
|
+
if (!marker) {
|
|
43
|
+
throw new Error(`Agent-Native Nitro preset marker is empty: ${filePath}`);
|
|
44
|
+
}
|
|
45
|
+
return marker;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -323,7 +323,8 @@ function getProcessingStuckAfterMs(): number {
|
|
|
323
323
|
process.env.NETLIFY ||
|
|
324
324
|
process.env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
325
325
|
process.env.VERCEL ||
|
|
326
|
-
"__cf_env" in globalThis
|
|
326
|
+
"__cf_env" in globalThis ||
|
|
327
|
+
"__env__" in globalThis
|
|
327
328
|
) {
|
|
328
329
|
return SERVERLESS_PROCESSING_STUCK_AFTER_MS;
|
|
329
330
|
}
|
|
@@ -77,12 +77,14 @@ function parseTimeoutMs(value: string | undefined): number | undefined {
|
|
|
77
77
|
function isServerlessHost(): boolean {
|
|
78
78
|
// Detection mirrors db/migrations.ts:297-301. On Cloudflare Workers/Pages,
|
|
79
79
|
// `process.env` is shimmed and CF_PAGES isn't reliably populated at runtime —
|
|
80
|
-
// the canonical signal is the `__cf_env` global injected by
|
|
80
|
+
// the canonical signal is the `__cf_env`/`__env__` global injected by the
|
|
81
|
+
// Cloudflare runtime adapter.
|
|
81
82
|
return (
|
|
82
83
|
!!process.env.NETLIFY ||
|
|
83
84
|
!!process.env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
84
85
|
!!process.env.VERCEL ||
|
|
85
|
-
"__cf_env" in globalThis
|
|
86
|
+
"__cf_env" in globalThis ||
|
|
87
|
+
"__env__" in globalThis
|
|
86
88
|
);
|
|
87
89
|
}
|
|
88
90
|
|