@agent-native/core 0.96.0 → 0.97.0
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 +6 -0
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +39 -0
- package/corpus/core/docs/content/audit-log.mdx +2 -1
- package/corpus/core/docs/content/automations.mdx +7 -7
- package/corpus/core/docs/content/creating-templates.mdx +1 -1
- package/corpus/core/docs/content/internationalization.mdx +2 -1
- package/corpus/core/docs/content/locales/ar-SA/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/ar-SA/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/ar-SA/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/ar-SA/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
- package/corpus/core/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/ar-SA/security.mdx +8 -0
- package/corpus/core/docs/content/locales/de-DE/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/de-DE/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/de-DE/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/de-DE/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
- package/corpus/core/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/de-DE/security.mdx +8 -0
- package/corpus/core/docs/content/locales/es-ES/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/es-ES/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/es-ES/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/es-ES/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
- package/corpus/core/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/es-ES/security.mdx +8 -0
- package/corpus/core/docs/content/locales/fr-FR/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/fr-FR/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/fr-FR/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/fr-FR/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
- package/corpus/core/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
- package/corpus/core/docs/content/locales/fr-FR/security.mdx +8 -0
- package/corpus/core/docs/content/locales/hi-IN/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/hi-IN/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/hi-IN/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/hi-IN/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
- package/corpus/core/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/hi-IN/security.mdx +8 -0
- package/corpus/core/docs/content/locales/ja-JP/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/ja-JP/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/ja-JP/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/ja-JP/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
- package/corpus/core/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
- package/corpus/core/docs/content/locales/ja-JP/security.mdx +8 -0
- package/corpus/core/docs/content/locales/ko-KR/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/ko-KR/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/ko-KR/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/ko-KR/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
- package/corpus/core/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
- package/corpus/core/docs/content/locales/ko-KR/security.mdx +8 -0
- package/corpus/core/docs/content/locales/pt-BR/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/pt-BR/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/pt-BR/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/pt-BR/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
- package/corpus/core/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/pt-BR/security.mdx +8 -0
- package/corpus/core/docs/content/locales/zh-CN/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/zh-CN/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/zh-CN/getting-started.mdx +6 -4
- package/corpus/core/docs/content/locales/zh-CN/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
- package/corpus/core/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
- package/corpus/core/docs/content/locales/zh-CN/security.mdx +8 -0
- package/corpus/core/docs/content/locales/zh-TW/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/zh-TW/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/zh-TW/getting-started.mdx +6 -4
- package/corpus/core/docs/content/locales/zh-TW/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
- package/corpus/core/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/zh-TW/security.mdx +8 -0
- package/corpus/core/docs/content/package-lifecycle.mdx +51 -0
- package/corpus/core/docs/content/sandbox-adapters.mdx +16 -5
- package/corpus/core/package.json +9 -2
- package/corpus/core/src/a2a/auth-policy.ts +33 -0
- package/corpus/core/src/agent/durable-background.ts +15 -7
- package/corpus/core/src/agent/run-store.ts +13 -4
- package/corpus/core/src/audit/actions/export-audit-events.ts +162 -0
- package/corpus/core/src/audit/store.ts +8 -3
- package/corpus/core/src/audit/types.ts +3 -0
- package/corpus/core/src/cli/doctor.ts +396 -0
- package/corpus/core/src/cli/index.ts +56 -0
- package/corpus/core/src/cli/info.ts +3 -3
- package/corpus/core/src/cli/package-lifecycle.ts +821 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +9 -9
- package/corpus/core/src/guards/db-tool-scoping.ts +129 -0
- package/corpus/core/src/guards/index.ts +23 -0
- package/corpus/core/src/guards/no-drizzle-push.ts +116 -0
- package/corpus/core/src/guards/no-env-credentials.ts +229 -0
- package/corpus/core/src/guards/no-env-mutation.ts +114 -0
- package/corpus/core/src/guards/no-localhost-fallback.ts +124 -0
- package/corpus/core/src/guards/no-unscoped-credentials.ts +227 -0
- package/corpus/core/src/guards/no-unscoped-queries.ts +834 -0
- package/corpus/core/src/guards/scan-utils.ts +105 -0
- package/corpus/core/src/guards/types.ts +30 -0
- package/corpus/core/src/org/accept-pending.ts +9 -1
- package/corpus/core/src/org/migrations.ts +41 -0
- package/corpus/core/src/package-lifecycle/index.ts +1 -0
- package/corpus/core/src/package-lifecycle/manifest.ts +120 -0
- package/corpus/core/src/provider-api/custom-registry.ts +56 -0
- package/corpus/core/src/provider-api/index.ts +2 -0
- package/corpus/core/src/secrets/storage.ts +47 -27
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +54 -12
- package/corpus/core/src/server/core-routes-plugin.ts +30 -14
- package/corpus/core/src/server/csrf.ts +24 -5
- package/corpus/core/src/server/framework-request-handler.ts +34 -0
- package/corpus/core/src/server/google-realtime-session.ts +1 -36
- package/corpus/core/src/server/poll.ts +24 -2
- package/corpus/core/src/server/realtime-voice.ts +9 -0
- package/corpus/core/src/server/request-origin.ts +63 -0
- package/corpus/core/src/server/transcribe-voice.ts +1 -50
- package/corpus/core/src/templates/default/package.json +2 -2
- package/corpus/core/src/templates/headless/package.json +0 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
- package/corpus/core/src/templates/workspace-core/package.json +0 -1
- package/corpus/core/src/templates/workspace-root/package.json +0 -1
- package/corpus/core/src/triggers/actions.ts +11 -3
- package/corpus/core/src/triggers/types.ts +7 -1
- package/corpus/templates/analytics/actions/compose-dashboard.ts +65 -37
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +73 -24
- package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +16 -13
- package/corpus/templates/analytics/actions/update-dashboard.ts +53 -42
- package/corpus/templates/analytics/changelog/2026-07-10-dashboard-edits-from-the-agent-and-the-ui-at-the-same-time-n.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-timed-out-bigquery-jobs-are-now-cancelled-so-they-stop-consu.md +6 -0
- package/corpus/templates/analytics/package.json +1 -2
- package/corpus/templates/analytics/server/lib/bigquery.ts +3 -2
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +154 -11
- package/corpus/templates/assets/package.json +1 -2
- package/corpus/templates/brain/package.json +1 -2
- package/corpus/templates/calendar/package.json +1 -2
- package/corpus/templates/chat/package.json +1 -2
- package/corpus/templates/clips/changelog/2026-07-10-atomic-view-tracking.md +6 -0
- package/corpus/templates/clips/chrome-extension/package.json +3 -2
- package/corpus/templates/clips/desktop/package.json +6 -4
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +26 -13
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +4 -1
- package/corpus/templates/clips/package.json +1 -2
- package/corpus/templates/clips/server/db/schema.ts +29 -17
- package/corpus/templates/clips/server/plugins/db.ts +8 -0
- package/corpus/templates/clips/server/routes/api/view-event.post.ts +260 -105
- package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +16 -0
- package/corpus/templates/content/AGENTS.md +72 -62
- package/corpus/templates/content/actions/_content-database-source-adapters.ts +84 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +206 -96
- package/corpus/templates/content/actions/_notion-database-source-adapter.ts +216 -0
- package/corpus/templates/content/actions/_position-utils.ts +76 -0
- package/corpus/templates/content/actions/_property-utils.ts +31 -20
- package/corpus/templates/content/actions/add-database-item.ts +52 -39
- package/corpus/templates/content/actions/attach-content-database-source.ts +95 -11
- package/corpus/templates/content/actions/change-content-database-source-role.ts +24 -1
- package/corpus/templates/content/actions/configure-document-property.ts +36 -27
- package/corpus/templates/content/actions/create-content-database.ts +40 -30
- package/corpus/templates/content/actions/create-document.ts +39 -32
- package/corpus/templates/content/actions/duplicate-document-property.ts +37 -28
- package/corpus/templates/content/actions/list-notion-database-sources.ts +80 -0
- package/corpus/templates/content/actions/move-document.ts +64 -48
- package/corpus/templates/content/actions/refresh-content-database-source.ts +42 -3
- package/corpus/templates/content/actions/share-local-file-document.ts +36 -27
- package/corpus/templates/content/actions/suggest-source-join-key.ts +39 -16
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +89 -23
- package/corpus/templates/content/app/components/editor/database/settings.tsx +1 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +18 -1
- package/corpus/templates/content/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/content/app/i18n-data.ts +40 -0
- package/corpus/templates/content/changelog/2026-07-10-failed-row-creation-and-source-attach-now-show-an-error-inst.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-items-added-at-the-same-time-no-longer-end-up-with-identical.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-notion-databases-can-add-read-only-details-to-content-tables.md +6 -0
- package/corpus/templates/content/package.json +1 -2
- package/corpus/templates/content/parity/matrix.md +25 -25
- package/corpus/templates/content/parity/matrix.ts +1 -0
- package/corpus/templates/content/server/routes/api/documents/[id]/move.patch.ts +43 -26
- package/corpus/templates/content/shared/api.ts +16 -1
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +8 -4
- package/corpus/templates/design/AGENTS.md +12 -2
- package/corpus/templates/design/DEVELOPING.md +10 -6
- package/corpus/templates/design/actions/apply-visual-edit.ts +191 -6
- package/corpus/templates/design/actions/duplicate-design.ts +31 -25
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +5 -1
- package/corpus/templates/design/changelog/2026-07-10-duplicating-a-design-can-no-longer-produce-a-copy-with-missi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-local-visual-edits-can-now-save-safe-leaf-text-class-and.md +6 -0
- package/corpus/templates/design/package.json +1 -1
- package/corpus/templates/design/shared/local-jsx-visual-edit.ts +388 -0
- package/corpus/templates/dispatch/package.json +1 -2
- package/corpus/templates/forms/actions/patch-form-fields.ts +82 -44
- package/corpus/templates/forms/changelog/2026-07-10-simultaneous-edits-to-different-form-fields-no-longer-overwr.md +6 -0
- package/corpus/templates/forms/package.json +1 -2
- package/corpus/templates/macros/package.json +1 -2
- package/corpus/templates/mail/app/components/email/EmailThread.tsx +3 -1
- package/corpus/templates/mail/changelog/2026-07-10-fixed-calendar-rsvp-buttons-in-emails-firing-duplicate-respo.md +6 -0
- package/corpus/templates/mail/package.json +1 -2
- package/corpus/templates/plan/actions/restore-plan-version.ts +126 -96
- package/corpus/templates/plan/changelog/2026-07-10-restoring-a-plan-version-can-no-longer-leave-a-plan-half-res.md +6 -0
- package/corpus/templates/plan/package.json +1 -2
- package/corpus/templates/plan/server/plans.ts +9 -2
- package/corpus/templates/slides/actions/list-decks.ts +28 -0
- package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +41 -26
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +4 -8
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +2 -4
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -8
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +5 -12
- package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +3 -6
- package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +3 -6
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -5
- package/corpus/templates/slides/app/context/DeckContext.tsx +47 -4
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -30
- package/corpus/templates/slides/app/pages/Index.tsx +2 -3
- package/corpus/templates/slides/app/root.tsx +0 -5
- package/corpus/templates/slides/changelog/2026-07-10-deck-lists-load-much-faster-and-no-longer-re-download-slide-.md +6 -0
- package/corpus/templates/slides/package.json +1 -2
- package/dist/a2a/auth-policy.d.ts +19 -0
- package/dist/a2a/auth-policy.d.ts.map +1 -1
- package/dist/a2a/auth-policy.js +32 -0
- package/dist/a2a/auth-policy.js.map +1 -1
- package/dist/agent/durable-background.d.ts +8 -5
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +15 -8
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +9 -4
- package/dist/agent/run-store.js.map +1 -1
- package/dist/audit/actions/export-audit-events.d.ts +27 -0
- package/dist/audit/actions/export-audit-events.d.ts.map +1 -0
- package/dist/audit/actions/export-audit-events.js +144 -0
- package/dist/audit/actions/export-audit-events.js.map +1 -0
- package/dist/audit/store.d.ts +1 -0
- package/dist/audit/store.d.ts.map +1 -1
- package/dist/audit/store.js +8 -3
- package/dist/audit/store.js.map +1 -1
- package/dist/audit/types.d.ts +3 -0
- package/dist/audit/types.d.ts.map +1 -1
- package/dist/audit/types.js.map +1 -1
- package/dist/cli/doctor.d.ts +83 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +277 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.js +50 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/info.js +3 -3
- package/dist/cli/info.js.map +1 -1
- package/dist/cli/package-lifecycle.d.ts +50 -0
- package/dist/cli/package-lifecycle.d.ts.map +1 -0
- package/dist/cli/package-lifecycle.js +560 -0
- package/dist/cli/package-lifecycle.js.map +1 -0
- package/dist/client/agent-chat-adapter.js +7 -3
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/guards/db-tool-scoping.d.ts +38 -0
- package/dist/guards/db-tool-scoping.d.ts.map +1 -0
- package/dist/guards/db-tool-scoping.js +114 -0
- package/dist/guards/db-tool-scoping.js.map +1 -0
- package/dist/guards/index.d.ts +23 -0
- package/dist/guards/index.d.ts.map +1 -0
- package/dist/guards/index.js +19 -0
- package/dist/guards/index.js.map +1 -0
- package/dist/guards/no-drizzle-push.d.ts +14 -0
- package/dist/guards/no-drizzle-push.d.ts.map +1 -0
- package/dist/guards/no-drizzle-push.js +97 -0
- package/dist/guards/no-drizzle-push.js.map +1 -0
- package/dist/guards/no-env-credentials.d.ts +36 -0
- package/dist/guards/no-env-credentials.d.ts.map +1 -0
- package/dist/guards/no-env-credentials.js +204 -0
- package/dist/guards/no-env-credentials.js.map +1 -0
- package/dist/guards/no-env-mutation.d.ts +23 -0
- package/dist/guards/no-env-mutation.d.ts.map +1 -0
- package/dist/guards/no-env-mutation.js +90 -0
- package/dist/guards/no-env-mutation.js.map +1 -0
- package/dist/guards/no-localhost-fallback.d.ts +30 -0
- package/dist/guards/no-localhost-fallback.d.ts.map +1 -0
- package/dist/guards/no-localhost-fallback.js +105 -0
- package/dist/guards/no-localhost-fallback.js.map +1 -0
- package/dist/guards/no-unscoped-credentials.d.ts +28 -0
- package/dist/guards/no-unscoped-credentials.d.ts.map +1 -0
- package/dist/guards/no-unscoped-credentials.js +199 -0
- package/dist/guards/no-unscoped-credentials.js.map +1 -0
- package/dist/guards/no-unscoped-queries.d.ts +32 -0
- package/dist/guards/no-unscoped-queries.d.ts.map +1 -0
- package/dist/guards/no-unscoped-queries.js +720 -0
- package/dist/guards/no-unscoped-queries.js.map +1 -0
- package/dist/guards/scan-utils.d.ts +28 -0
- package/dist/guards/scan-utils.d.ts.map +1 -0
- package/dist/guards/scan-utils.js +92 -0
- package/dist/guards/scan-utils.js.map +1 -0
- package/dist/guards/types.d.ts +28 -0
- package/dist/guards/types.d.ts.map +1 -0
- package/dist/guards/types.js +11 -0
- package/dist/guards/types.js.map +1 -0
- package/dist/org/accept-pending.d.ts.map +1 -1
- package/dist/org/accept-pending.js +9 -1
- package/dist/org/accept-pending.js.map +1 -1
- package/dist/org/migrations.d.ts +7 -2
- package/dist/org/migrations.d.ts.map +1 -1
- package/dist/org/migrations.js +41 -0
- package/dist/org/migrations.js.map +1 -1
- package/dist/package-lifecycle/index.d.ts +2 -0
- package/dist/package-lifecycle/index.d.ts.map +1 -0
- package/dist/package-lifecycle/index.js +2 -0
- package/dist/package-lifecycle/index.js.map +1 -0
- package/dist/package-lifecycle/manifest.d.ts +24 -0
- package/dist/package-lifecycle/manifest.d.ts.map +1 -0
- package/dist/package-lifecycle/manifest.js +81 -0
- package/dist/package-lifecycle/manifest.js.map +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/custom-registry.d.ts +32 -1
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js +41 -1
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/provider-api/index.d.ts +1 -1
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +1 -1
- package/dist/provider-api/index.js.map +1 -1
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +45 -27
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +4 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +47 -13
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +23 -12
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/csrf.js +18 -5
- package/dist/server/csrf.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +34 -0
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/google-realtime-session.d.ts.map +1 -1
- package/dist/server/google-realtime-session.js +2 -34
- package/dist/server/google-realtime-session.js.map +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +24 -2
- package/dist/server/poll.js.map +1 -1
- package/dist/server/realtime-voice.d.ts.map +1 -1
- package/dist/server/realtime-voice.js +9 -0
- package/dist/server/realtime-voice.js.map +1 -1
- package/dist/server/request-origin.d.ts +10 -0
- package/dist/server/request-origin.d.ts.map +1 -0
- package/dist/server/request-origin.js +60 -0
- package/dist/server/request-origin.js.map +1 -0
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +2 -48
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/templates/default/package.json +2 -2
- package/dist/templates/headless/package.json +0 -1
- package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
- package/dist/templates/workspace-core/package.json +0 -1
- package/dist/templates/workspace-root/package.json +0 -1
- package/dist/triggers/actions.d.ts.map +1 -1
- package/dist/triggers/actions.js +8 -3
- package/dist/triggers/actions.js.map +1 -1
- package/dist/triggers/types.d.ts +7 -1
- package/dist/triggers/types.d.ts.map +1 -1
- package/dist/triggers/types.js.map +1 -1
- package/docs/content/audit-log.mdx +2 -1
- package/docs/content/automations.mdx +7 -7
- package/docs/content/creating-templates.mdx +1 -1
- package/docs/content/internationalization.mdx +2 -1
- package/docs/content/locales/ar-SA/audit-log.mdx +2 -1
- package/docs/content/locales/ar-SA/automations.mdx +7 -7
- package/docs/content/locales/ar-SA/getting-started.mdx +7 -1
- package/docs/content/locales/ar-SA/internationalization.mdx +1 -1
- package/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
- package/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/ar-SA/security.mdx +8 -0
- package/docs/content/locales/de-DE/audit-log.mdx +2 -1
- package/docs/content/locales/de-DE/automations.mdx +7 -7
- package/docs/content/locales/de-DE/getting-started.mdx +7 -1
- package/docs/content/locales/de-DE/internationalization.mdx +1 -1
- package/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
- package/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/de-DE/security.mdx +8 -0
- package/docs/content/locales/es-ES/audit-log.mdx +2 -1
- package/docs/content/locales/es-ES/automations.mdx +7 -7
- package/docs/content/locales/es-ES/getting-started.mdx +7 -1
- package/docs/content/locales/es-ES/internationalization.mdx +1 -1
- package/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
- package/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/es-ES/security.mdx +8 -0
- package/docs/content/locales/fr-FR/audit-log.mdx +2 -1
- package/docs/content/locales/fr-FR/automations.mdx +7 -7
- package/docs/content/locales/fr-FR/getting-started.mdx +7 -1
- package/docs/content/locales/fr-FR/internationalization.mdx +1 -1
- package/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
- package/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
- package/docs/content/locales/fr-FR/security.mdx +8 -0
- package/docs/content/locales/hi-IN/audit-log.mdx +2 -1
- package/docs/content/locales/hi-IN/automations.mdx +7 -7
- package/docs/content/locales/hi-IN/getting-started.mdx +7 -1
- package/docs/content/locales/hi-IN/internationalization.mdx +1 -1
- package/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
- package/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/hi-IN/security.mdx +8 -0
- package/docs/content/locales/ja-JP/audit-log.mdx +2 -1
- package/docs/content/locales/ja-JP/automations.mdx +7 -7
- package/docs/content/locales/ja-JP/getting-started.mdx +7 -1
- package/docs/content/locales/ja-JP/internationalization.mdx +1 -1
- package/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
- package/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
- package/docs/content/locales/ja-JP/security.mdx +8 -0
- package/docs/content/locales/ko-KR/audit-log.mdx +2 -1
- package/docs/content/locales/ko-KR/automations.mdx +7 -7
- package/docs/content/locales/ko-KR/getting-started.mdx +7 -1
- package/docs/content/locales/ko-KR/internationalization.mdx +1 -1
- package/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
- package/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
- package/docs/content/locales/ko-KR/security.mdx +8 -0
- package/docs/content/locales/pt-BR/audit-log.mdx +2 -1
- package/docs/content/locales/pt-BR/automations.mdx +7 -7
- package/docs/content/locales/pt-BR/getting-started.mdx +7 -1
- package/docs/content/locales/pt-BR/internationalization.mdx +1 -1
- package/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
- package/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/pt-BR/security.mdx +8 -0
- package/docs/content/locales/zh-CN/audit-log.mdx +2 -1
- package/docs/content/locales/zh-CN/automations.mdx +7 -7
- package/docs/content/locales/zh-CN/getting-started.mdx +6 -4
- package/docs/content/locales/zh-CN/internationalization.mdx +1 -1
- package/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
- package/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
- package/docs/content/locales/zh-CN/security.mdx +8 -0
- package/docs/content/locales/zh-TW/audit-log.mdx +2 -1
- package/docs/content/locales/zh-TW/automations.mdx +7 -7
- package/docs/content/locales/zh-TW/getting-started.mdx +6 -4
- package/docs/content/locales/zh-TW/internationalization.mdx +1 -1
- package/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
- package/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/zh-TW/security.mdx +8 -0
- package/docs/content/package-lifecycle.mdx +51 -0
- package/docs/content/sandbox-adapters.mdx +16 -5
- package/package.json +10 -3
- package/src/templates/default/package.json +2 -2
- package/src/templates/headless/package.json +0 -1
- package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
- package/src/templates/workspace-core/package.json +0 -1
- package/src/templates/workspace-root/package.json +0 -1
- package/corpus/templates/slides/app/components/ui/toast.tsx +0 -1
- package/corpus/templates/slides/app/components/ui/toaster.tsx +0 -1
- package/corpus/templates/slides/app/hooks/use-toast.ts +0 -1
|
@@ -76,6 +76,12 @@ import {
|
|
|
76
76
|
type ExistingBuilderSourceRowIdentity,
|
|
77
77
|
} from "./_builder-cms-source-adapter.js";
|
|
78
78
|
import { mergeBuilderCmsWriteSettingsIntoJson } from "./_builder-cms-write-settings.js";
|
|
79
|
+
import {
|
|
80
|
+
databaseItemsPositionScope,
|
|
81
|
+
documentsPositionScope,
|
|
82
|
+
propertyDefinitionsPositionScope,
|
|
83
|
+
withPositionLock,
|
|
84
|
+
} from "./_position-utils.js";
|
|
79
85
|
import { listPropertiesForDatabase, nanoid } from "./_property-utils.js";
|
|
80
86
|
import { isEffectivelyEmptyDocumentContent } from "./update-document.js";
|
|
81
87
|
|
|
@@ -189,7 +195,12 @@ function normalizeWriteOwner(
|
|
|
189
195
|
function normalizeSourceType(
|
|
190
196
|
value: string | null | undefined,
|
|
191
197
|
): ContentDatabaseSourceType {
|
|
192
|
-
if (
|
|
198
|
+
if (
|
|
199
|
+
value === "builder-cms" ||
|
|
200
|
+
value === "local-table" ||
|
|
201
|
+
value === "notion-database"
|
|
202
|
+
)
|
|
203
|
+
return value;
|
|
193
204
|
return "mock-local";
|
|
194
205
|
}
|
|
195
206
|
|
|
@@ -279,6 +290,7 @@ function sourceMetadataLabel(
|
|
|
279
290
|
) {
|
|
280
291
|
if (sourceType === "builder-cms") return `builder.cms.${sourceTable}`;
|
|
281
292
|
if (sourceType === "local-table") return `local.table.${sourceTable}`;
|
|
293
|
+
if (sourceType === "notion-database") return `notion.database.${sourceTable}`;
|
|
282
294
|
return `mock-local.${sourceTable}`;
|
|
283
295
|
}
|
|
284
296
|
|
|
@@ -3102,6 +3114,20 @@ export function serializeSourceMetadataRecord(args: {
|
|
|
3102
3114
|
args.builderModelFields ?? existingMetadata?.builderModelFields,
|
|
3103
3115
|
});
|
|
3104
3116
|
}
|
|
3117
|
+
if (args.sourceType === "notion-database") {
|
|
3118
|
+
return JSON.stringify({
|
|
3119
|
+
primaryKey: "id",
|
|
3120
|
+
titleField: "title",
|
|
3121
|
+
naturalKeyField: null,
|
|
3122
|
+
pushMode: "none",
|
|
3123
|
+
pushModeLabel: "Read only",
|
|
3124
|
+
pushModeDescription:
|
|
3125
|
+
"Notion database sources are read-only in this pilot.",
|
|
3126
|
+
notes:
|
|
3127
|
+
"Notion values are refreshed through the current user's OAuth connection; write-back is disabled.",
|
|
3128
|
+
label: sourceMetadataLabel(args.sourceType, args.sourceTable),
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3105
3131
|
return JSON.stringify({
|
|
3106
3132
|
primaryKey: "id",
|
|
3107
3133
|
titleField: "title",
|
|
@@ -3180,6 +3206,21 @@ export function sourceCapabilitiesForType(
|
|
|
3180
3206
|
readOnlyRefresh: true,
|
|
3181
3207
|
});
|
|
3182
3208
|
}
|
|
3209
|
+
if (sourceType === "notion-database") {
|
|
3210
|
+
return serializeSourceCapabilitiesRecord({
|
|
3211
|
+
canRefresh: true,
|
|
3212
|
+
canCreateChangeSets: false,
|
|
3213
|
+
canWriteFields: false,
|
|
3214
|
+
canWriteBody: false,
|
|
3215
|
+
canPush: false,
|
|
3216
|
+
canPull: true,
|
|
3217
|
+
canPublish: false,
|
|
3218
|
+
canDelete: false,
|
|
3219
|
+
canStageLocalRevision: false,
|
|
3220
|
+
liveWritesEnabled: false,
|
|
3221
|
+
readOnlyRefresh: true,
|
|
3222
|
+
});
|
|
3223
|
+
}
|
|
3183
3224
|
return serializeSourceCapabilitiesRecord();
|
|
3184
3225
|
}
|
|
3185
3226
|
|
|
@@ -4168,82 +4209,106 @@ export async function importBuilderCmsEntriesAsDatabaseItems(args: {
|
|
|
4168
4209
|
const existingTitles = new Set(
|
|
4169
4210
|
currentItems.map((row) => row.document.title.trim().toLowerCase()),
|
|
4170
4211
|
);
|
|
4171
|
-
const [maxDocPos] = await db
|
|
4172
|
-
.select({ max: sql<number>`COALESCE(MAX(position), -1)` })
|
|
4173
|
-
.from(schema.documents)
|
|
4174
|
-
.where(
|
|
4175
|
-
and(
|
|
4176
|
-
eq(schema.documents.ownerEmail, args.database.ownerEmail),
|
|
4177
|
-
eq(schema.documents.parentId, args.database.documentId),
|
|
4178
|
-
),
|
|
4179
|
-
);
|
|
4180
|
-
const [maxItemPos] = await db
|
|
4181
|
-
.select({ max: sql<number>`COALESCE(MAX(position), -1)` })
|
|
4182
|
-
.from(schema.contentDatabaseItems)
|
|
4183
|
-
.where(eq(schema.contentDatabaseItems.databaseId, args.database.id));
|
|
4184
|
-
|
|
4185
|
-
let nextDocPosition = (maxDocPos?.max ?? -1) + 1;
|
|
4186
|
-
let nextItemPosition = (maxItemPos?.max ?? -1) + 1;
|
|
4187
|
-
const documentRows: (typeof schema.documents.$inferInsert)[] = [];
|
|
4188
|
-
const itemRows: (typeof schema.contentDatabaseItems.$inferInsert)[] = [];
|
|
4189
|
-
|
|
4190
|
-
for (const entry of args.entries) {
|
|
4191
|
-
if (
|
|
4192
|
-
builderCmsEntryAlreadyRepresented({
|
|
4193
|
-
entry,
|
|
4194
|
-
sourceTable: args.sourceTable,
|
|
4195
|
-
existingSourceRows: args.existingSourceRows ?? [],
|
|
4196
|
-
})
|
|
4197
|
-
) {
|
|
4198
|
-
continue;
|
|
4199
|
-
}
|
|
4200
4212
|
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
databaseId: args.database.id,
|
|
4228
|
-
documentId,
|
|
4229
|
-
position: nextItemPosition++,
|
|
4230
|
-
bodyHydrationStatus: "pending",
|
|
4231
|
-
bodyHydrationError: null,
|
|
4232
|
-
createdAt: args.now,
|
|
4233
|
-
updatedAt: args.now,
|
|
4234
|
-
});
|
|
4235
|
-
importedEntriesByDocumentId.set(documentId, entry);
|
|
4236
|
-
}
|
|
4237
|
-
await db.transaction(async (tx) => {
|
|
4238
|
-
for (const chunk of chunks(documentRows, bulkChunkSizeForColumnCount(13))) {
|
|
4239
|
-
await tx.insert(schema.documents).values(chunk);
|
|
4240
|
-
}
|
|
4241
|
-
for (const chunk of chunks(itemRows, bulkChunkSizeForColumnCount(10))) {
|
|
4242
|
-
await tx.insert(schema.contentDatabaseItems).values(chunk);
|
|
4243
|
-
}
|
|
4244
|
-
});
|
|
4213
|
+
// Reads MAX(position) for both `documents` and `content_database_items`
|
|
4214
|
+
// then batch-inserts at MAX+1.. — serialize the whole read-through-write
|
|
4215
|
+
// span per scope so a concurrent import/add/move targeting the same parent
|
|
4216
|
+
// document or the same database can't read the same MAX (see
|
|
4217
|
+
// _position-utils.ts).
|
|
4218
|
+
return withPositionLock(
|
|
4219
|
+
documentsPositionScope(args.database.ownerEmail, args.database.documentId),
|
|
4220
|
+
() =>
|
|
4221
|
+
withPositionLock(
|
|
4222
|
+
databaseItemsPositionScope(args.database.id),
|
|
4223
|
+
async () => {
|
|
4224
|
+
const [maxDocPos] = await db
|
|
4225
|
+
.select({ max: sql<number>`COALESCE(MAX(position), -1)` })
|
|
4226
|
+
.from(schema.documents)
|
|
4227
|
+
.where(
|
|
4228
|
+
and(
|
|
4229
|
+
eq(schema.documents.ownerEmail, args.database.ownerEmail),
|
|
4230
|
+
eq(schema.documents.parentId, args.database.documentId),
|
|
4231
|
+
),
|
|
4232
|
+
);
|
|
4233
|
+
const [maxItemPos] = await db
|
|
4234
|
+
.select({ max: sql<number>`COALESCE(MAX(position), -1)` })
|
|
4235
|
+
.from(schema.contentDatabaseItems)
|
|
4236
|
+
.where(
|
|
4237
|
+
eq(schema.contentDatabaseItems.databaseId, args.database.id),
|
|
4238
|
+
);
|
|
4245
4239
|
|
|
4246
|
-
|
|
4240
|
+
let nextDocPosition = (maxDocPos?.max ?? -1) + 1;
|
|
4241
|
+
let nextItemPosition = (maxItemPos?.max ?? -1) + 1;
|
|
4242
|
+
const documentRows: (typeof schema.documents.$inferInsert)[] = [];
|
|
4243
|
+
const itemRows: (typeof schema.contentDatabaseItems.$inferInsert)[] =
|
|
4244
|
+
[];
|
|
4245
|
+
|
|
4246
|
+
for (const entry of args.entries) {
|
|
4247
|
+
if (
|
|
4248
|
+
builderCmsEntryAlreadyRepresented({
|
|
4249
|
+
entry,
|
|
4250
|
+
sourceTable: args.sourceTable,
|
|
4251
|
+
existingSourceRows: args.existingSourceRows ?? [],
|
|
4252
|
+
})
|
|
4253
|
+
) {
|
|
4254
|
+
continue;
|
|
4255
|
+
}
|
|
4256
|
+
|
|
4257
|
+
const title = entry.title.trim() || entry.id;
|
|
4258
|
+
const titleKey = title.toLowerCase();
|
|
4259
|
+
if (!args.skipTitleDedup && existingTitles.has(titleKey)) continue;
|
|
4260
|
+
existingTitles.add(titleKey);
|
|
4261
|
+
|
|
4262
|
+
const documentId = nanoid();
|
|
4263
|
+
const itemId = nanoid();
|
|
4264
|
+
documentRows.push({
|
|
4265
|
+
id: documentId,
|
|
4266
|
+
ownerEmail: args.database.ownerEmail,
|
|
4267
|
+
orgId: args.database.orgId,
|
|
4268
|
+
parentId: args.database.documentId,
|
|
4269
|
+
title,
|
|
4270
|
+
content: "",
|
|
4271
|
+
icon: null,
|
|
4272
|
+
position: nextDocPosition++,
|
|
4273
|
+
isFavorite: 0,
|
|
4274
|
+
hideFromSearch: databaseDocument?.hideFromSearch ?? 0,
|
|
4275
|
+
visibility: databaseDocument?.visibility ?? "private",
|
|
4276
|
+
createdAt: args.now,
|
|
4277
|
+
updatedAt: args.now,
|
|
4278
|
+
});
|
|
4279
|
+
itemRows.push({
|
|
4280
|
+
id: itemId,
|
|
4281
|
+
ownerEmail: args.database.ownerEmail,
|
|
4282
|
+
orgId: args.database.orgId,
|
|
4283
|
+
databaseId: args.database.id,
|
|
4284
|
+
documentId,
|
|
4285
|
+
position: nextItemPosition++,
|
|
4286
|
+
bodyHydrationStatus: "pending",
|
|
4287
|
+
bodyHydrationError: null,
|
|
4288
|
+
createdAt: args.now,
|
|
4289
|
+
updatedAt: args.now,
|
|
4290
|
+
});
|
|
4291
|
+
importedEntriesByDocumentId.set(documentId, entry);
|
|
4292
|
+
}
|
|
4293
|
+
await db.transaction(async (tx) => {
|
|
4294
|
+
for (const chunk of chunks(
|
|
4295
|
+
documentRows,
|
|
4296
|
+
bulkChunkSizeForColumnCount(13),
|
|
4297
|
+
)) {
|
|
4298
|
+
await tx.insert(schema.documents).values(chunk);
|
|
4299
|
+
}
|
|
4300
|
+
for (const chunk of chunks(
|
|
4301
|
+
itemRows,
|
|
4302
|
+
bulkChunkSizeForColumnCount(10),
|
|
4303
|
+
)) {
|
|
4304
|
+
await tx.insert(schema.contentDatabaseItems).values(chunk);
|
|
4305
|
+
}
|
|
4306
|
+
});
|
|
4307
|
+
|
|
4308
|
+
return { imported: itemRows.length, importedEntriesByDocumentId };
|
|
4309
|
+
},
|
|
4310
|
+
),
|
|
4311
|
+
);
|
|
4247
4312
|
}
|
|
4248
4313
|
|
|
4249
4314
|
export async function resyncBuilderCmsSourceSnapshot(args: {
|
|
@@ -4825,7 +4890,10 @@ export async function storeSecondarySourceRows(args: {
|
|
|
4825
4890
|
sourceDisplayKey:
|
|
4826
4891
|
entry.title?.trim() || `${args.sourceTable}-${index + 1}`,
|
|
4827
4892
|
sourceValuesJson: JSON.stringify(entry.sourceValues ?? {}),
|
|
4828
|
-
provenance:
|
|
4893
|
+
provenance:
|
|
4894
|
+
args.sourceType === "notion-database"
|
|
4895
|
+
? "Notion database read adapter"
|
|
4896
|
+
: "secondary source row",
|
|
4829
4897
|
syncState: "linked" as const,
|
|
4830
4898
|
freshness: "fresh" as const,
|
|
4831
4899
|
lastSyncedAt: args.now,
|
|
@@ -4844,6 +4912,7 @@ export async function storeSecondarySourceRows(args: {
|
|
|
4844
4912
|
export async function seedSecondarySourceFields(args: {
|
|
4845
4913
|
sourceId: string;
|
|
4846
4914
|
ownerEmail: string;
|
|
4915
|
+
sourceType?: ContentDatabaseSourceType;
|
|
4847
4916
|
modelFields: BuilderCmsModelFieldSummary[];
|
|
4848
4917
|
sampleEntry?: BuilderCmsSourceEntry;
|
|
4849
4918
|
now: string;
|
|
@@ -4909,7 +4978,10 @@ export async function seedSecondarySourceFields(args: {
|
|
|
4909
4978
|
mappingType: "property" as const,
|
|
4910
4979
|
writeOwner: "source" as const,
|
|
4911
4980
|
readOnly: 1,
|
|
4912
|
-
provenance:
|
|
4981
|
+
provenance:
|
|
4982
|
+
args.sourceType === "notion-database"
|
|
4983
|
+
? "Notion database read adapter"
|
|
4984
|
+
: "secondary source field",
|
|
4913
4985
|
freshness: "fresh" as const,
|
|
4914
4986
|
lastSyncedAt: args.now,
|
|
4915
4987
|
createdAt: existing?.createdAt ?? args.now,
|
|
@@ -5008,6 +5080,39 @@ export async function updateBuilderCmsSourceReadMetadata(args: {
|
|
|
5008
5080
|
.where(eq(schema.contentDatabaseSources.id, args.sourceId));
|
|
5009
5081
|
}
|
|
5010
5082
|
|
|
5083
|
+
export async function updateReadOnlySourceMetadata(args: {
|
|
5084
|
+
sourceId: string;
|
|
5085
|
+
sourceType: ContentDatabaseSourceType;
|
|
5086
|
+
sourceTable: string;
|
|
5087
|
+
fetchedAt: string;
|
|
5088
|
+
now: string;
|
|
5089
|
+
message: string | null;
|
|
5090
|
+
metadata?: Record<string, unknown>;
|
|
5091
|
+
syncState?: ContentDatabaseSourceSyncState;
|
|
5092
|
+
}) {
|
|
5093
|
+
const db = getDb();
|
|
5094
|
+
const [current] = await db
|
|
5095
|
+
.select({ metadataJson: schema.contentDatabaseSources.metadataJson })
|
|
5096
|
+
.from(schema.contentDatabaseSources)
|
|
5097
|
+
.where(eq(schema.contentDatabaseSources.id, args.sourceId))
|
|
5098
|
+
.limit(1);
|
|
5099
|
+
const existing =
|
|
5100
|
+
parseObject<Record<string, unknown>>(current?.metadataJson) ?? {};
|
|
5101
|
+
await db
|
|
5102
|
+
.update(schema.contentDatabaseSources)
|
|
5103
|
+
.set({
|
|
5104
|
+
syncState: args.syncState ?? (args.message ? "refreshing" : "linked"),
|
|
5105
|
+
freshness: args.message ? "stale" : "fresh",
|
|
5106
|
+
capabilitiesJson: sourceCapabilitiesForType(args.sourceType),
|
|
5107
|
+
metadataJson: JSON.stringify({ ...existing, ...(args.metadata ?? {}) }),
|
|
5108
|
+
lastRefreshedAt: args.now,
|
|
5109
|
+
lastSourceUpdatedAt: args.fetchedAt,
|
|
5110
|
+
lastError: null,
|
|
5111
|
+
updatedAt: args.now,
|
|
5112
|
+
})
|
|
5113
|
+
.where(eq(schema.contentDatabaseSources.id, args.sourceId));
|
|
5114
|
+
}
|
|
5115
|
+
|
|
5011
5116
|
export async function getExistingSource(databaseId: string) {
|
|
5012
5117
|
const db = getDb();
|
|
5013
5118
|
const [source] = await db
|
|
@@ -5154,26 +5259,31 @@ export async function ensureDatabaseSourceProperty(args: {
|
|
|
5154
5259
|
.set({ optionsJson, updatedAt: args.now })
|
|
5155
5260
|
.where(eq(schema.documentPropertyDefinitions.id, existing.id));
|
|
5156
5261
|
} else {
|
|
5157
|
-
const [maxPos] = await db
|
|
5158
|
-
.select({ max: sql<number>`COALESCE(MAX(position), -1)` })
|
|
5159
|
-
.from(schema.documentPropertyDefinitions)
|
|
5160
|
-
.where(
|
|
5161
|
-
eq(schema.documentPropertyDefinitions.databaseId, args.database.id),
|
|
5162
|
-
);
|
|
5163
5262
|
propertyId = crypto.randomUUID();
|
|
5164
|
-
await
|
|
5165
|
-
id
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5263
|
+
await withPositionLock(
|
|
5264
|
+
propertyDefinitionsPositionScope(args.database.id),
|
|
5265
|
+
async () => {
|
|
5266
|
+
const [maxPos] = await db
|
|
5267
|
+
.select({ max: sql<number>`COALESCE(MAX(position), -1)` })
|
|
5268
|
+
.from(schema.documentPropertyDefinitions)
|
|
5269
|
+
.where(
|
|
5270
|
+
eq(schema.documentPropertyDefinitions.databaseId, args.database.id),
|
|
5271
|
+
);
|
|
5272
|
+
await db.insert(schema.documentPropertyDefinitions).values({
|
|
5273
|
+
id: propertyId,
|
|
5274
|
+
ownerEmail: args.database.ownerEmail,
|
|
5275
|
+
orgId: args.database.orgId,
|
|
5276
|
+
databaseId: args.database.id,
|
|
5277
|
+
name: SOURCE_PROPERTY_NAME,
|
|
5278
|
+
type: "select",
|
|
5279
|
+
visibility: "always_show",
|
|
5280
|
+
optionsJson,
|
|
5281
|
+
position: (maxPos?.max ?? -1) + 1,
|
|
5282
|
+
createdAt: args.now,
|
|
5283
|
+
updatedAt: args.now,
|
|
5284
|
+
});
|
|
5285
|
+
},
|
|
5286
|
+
);
|
|
5177
5287
|
}
|
|
5178
5288
|
|
|
5179
5289
|
// A row's Source value IS its owning source id (= the option id); unsourced
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { getRequestUserEmail } from "@agent-native/core/server";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getNotionConnectionForOwner,
|
|
5
|
+
notionFetch,
|
|
6
|
+
} from "../server/lib/notion.js";
|
|
7
|
+
import type {
|
|
8
|
+
BuilderCmsModelFieldSummary,
|
|
9
|
+
DocumentPropertyValue,
|
|
10
|
+
} from "../shared/api.js";
|
|
11
|
+
import type { BuilderCmsSourceEntry } from "./_builder-cms-source-adapter.js";
|
|
12
|
+
import type { ContentDatabaseSourceReadResult } from "./_content-database-source-adapters.js";
|
|
13
|
+
|
|
14
|
+
const NOTION_SOURCE_PAGE_SIZE = 100;
|
|
15
|
+
const NOTION_SOURCE_MAX_PAGES = 5;
|
|
16
|
+
|
|
17
|
+
type NotionProperty = {
|
|
18
|
+
id?: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
type?: string;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type NotionPage = {
|
|
25
|
+
object?: string;
|
|
26
|
+
id: string;
|
|
27
|
+
url?: string;
|
|
28
|
+
last_edited_time?: string;
|
|
29
|
+
properties?: Record<string, NotionProperty>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function plainText(value: unknown): string {
|
|
33
|
+
return Array.isArray(value)
|
|
34
|
+
? value
|
|
35
|
+
.map((part) =>
|
|
36
|
+
typeof part === "object" && part && "plain_text" in part
|
|
37
|
+
? String((part as { plain_text?: unknown }).plain_text ?? "")
|
|
38
|
+
: "",
|
|
39
|
+
)
|
|
40
|
+
.join("")
|
|
41
|
+
: "";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const supportedTypes = new Set([
|
|
45
|
+
"title",
|
|
46
|
+
"rich_text",
|
|
47
|
+
"number",
|
|
48
|
+
"checkbox",
|
|
49
|
+
"url",
|
|
50
|
+
"email",
|
|
51
|
+
"phone_number",
|
|
52
|
+
"date",
|
|
53
|
+
"select",
|
|
54
|
+
"status",
|
|
55
|
+
"multi_select",
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
function sourceFieldType(type: string) {
|
|
59
|
+
if (!supportedTypes.has(type)) return `unsupported:${type || "unknown"}`;
|
|
60
|
+
if (type === "rich_text" || type === "title") return "text";
|
|
61
|
+
if (type === "phone_number") return "phone";
|
|
62
|
+
return type;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function propertyValue(property: NotionProperty): DocumentPropertyValue {
|
|
66
|
+
const type = property.type ?? "unknown";
|
|
67
|
+
if (!supportedTypes.has(type)) {
|
|
68
|
+
return `[Unsupported Notion property: ${type}]`;
|
|
69
|
+
}
|
|
70
|
+
if (type === "title" || type === "rich_text")
|
|
71
|
+
return plainText(property[type]);
|
|
72
|
+
if (type === "number")
|
|
73
|
+
return typeof property.number === "number" ? property.number : null;
|
|
74
|
+
if (type === "checkbox") return property.checkbox === true;
|
|
75
|
+
if (type === "url" || type === "email" || type === "phone_number") {
|
|
76
|
+
const value = property[type];
|
|
77
|
+
return typeof value === "string" ? value : null;
|
|
78
|
+
}
|
|
79
|
+
if (type === "date") {
|
|
80
|
+
const date = property.date as
|
|
81
|
+
| { start?: unknown; end?: unknown }
|
|
82
|
+
| null
|
|
83
|
+
| undefined;
|
|
84
|
+
return typeof date?.start === "string"
|
|
85
|
+
? {
|
|
86
|
+
start: date.start,
|
|
87
|
+
end: typeof date.end === "string" ? date.end : null,
|
|
88
|
+
includeTime: date.start.includes("T"),
|
|
89
|
+
}
|
|
90
|
+
: null;
|
|
91
|
+
}
|
|
92
|
+
if (type === "select" || type === "status") {
|
|
93
|
+
const option = property[type] as { name?: unknown } | null | undefined;
|
|
94
|
+
return typeof option?.name === "string" ? option.name : null;
|
|
95
|
+
}
|
|
96
|
+
if (type === "multi_select") {
|
|
97
|
+
return Array.isArray(property.multi_select)
|
|
98
|
+
? property.multi_select.flatMap((option) =>
|
|
99
|
+
typeof option === "object" &&
|
|
100
|
+
option &&
|
|
101
|
+
"name" in option &&
|
|
102
|
+
typeof (option as { name?: unknown }).name === "string"
|
|
103
|
+
? [(option as { name: string }).name]
|
|
104
|
+
: [],
|
|
105
|
+
)
|
|
106
|
+
: [];
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function titleFromProperties(properties: Record<string, NotionProperty>) {
|
|
112
|
+
const title = Object.values(properties).find(
|
|
113
|
+
(property) => property.type === "title",
|
|
114
|
+
);
|
|
115
|
+
return title ? plainText(title.title) || "Untitled" : "Untitled";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function readNotionDatabaseSource(args: {
|
|
119
|
+
sourceTable: string;
|
|
120
|
+
limit: number;
|
|
121
|
+
offset: number;
|
|
122
|
+
fullRefresh?: boolean;
|
|
123
|
+
}): Promise<ContentDatabaseSourceReadResult> {
|
|
124
|
+
if (args.offset !== 0) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
"Notion database sources use cursor pagination; numeric offsets are not supported.",
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
const userEmail = getRequestUserEmail();
|
|
130
|
+
if (!userEmail)
|
|
131
|
+
throw new Error("Notion database access requires a signed-in user.");
|
|
132
|
+
const connection = await getNotionConnectionForOwner(userEmail);
|
|
133
|
+
if (!connection)
|
|
134
|
+
throw new Error("Connect Notion before attaching a Notion database.");
|
|
135
|
+
|
|
136
|
+
const dataSource = await notionFetch<{
|
|
137
|
+
id: string;
|
|
138
|
+
title?: Array<{ plain_text?: string }>;
|
|
139
|
+
properties?: Record<string, NotionProperty>;
|
|
140
|
+
}>(
|
|
141
|
+
`/data_sources/${encodeURIComponent(args.sourceTable)}`,
|
|
142
|
+
connection.accessToken,
|
|
143
|
+
);
|
|
144
|
+
const fields: BuilderCmsModelFieldSummary[] = Object.entries(
|
|
145
|
+
dataSource.properties ?? {},
|
|
146
|
+
).map(([name, property]) => ({
|
|
147
|
+
name: property.id || name,
|
|
148
|
+
label: property.name || name,
|
|
149
|
+
type: sourceFieldType(property.type ?? "unknown"),
|
|
150
|
+
required: false,
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
const entries: BuilderCmsSourceEntry[] = [];
|
|
154
|
+
let cursor: string | undefined;
|
|
155
|
+
let hasMore = false;
|
|
156
|
+
const maxPages = args.fullRefresh ? NOTION_SOURCE_MAX_PAGES : 1;
|
|
157
|
+
for (let page = 0; page < maxPages; page++) {
|
|
158
|
+
const response = await notionFetch<{
|
|
159
|
+
results?: NotionPage[];
|
|
160
|
+
has_more?: boolean;
|
|
161
|
+
next_cursor?: string | null;
|
|
162
|
+
}>(
|
|
163
|
+
`/data_sources/${encodeURIComponent(args.sourceTable)}/query`,
|
|
164
|
+
connection.accessToken,
|
|
165
|
+
{
|
|
166
|
+
method: "POST",
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
page_size: Math.min(NOTION_SOURCE_PAGE_SIZE, args.limit),
|
|
169
|
+
...(cursor ? { start_cursor: cursor } : {}),
|
|
170
|
+
}),
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
for (const result of response.results ?? []) {
|
|
174
|
+
if (result.object && result.object !== "page") continue;
|
|
175
|
+
const properties = result.properties ?? {};
|
|
176
|
+
entries.push({
|
|
177
|
+
id: result.id,
|
|
178
|
+
model: args.sourceTable,
|
|
179
|
+
title: titleFromProperties(properties),
|
|
180
|
+
urlPath: result.url ?? "",
|
|
181
|
+
updatedAt: result.last_edited_time ?? "",
|
|
182
|
+
sourceValues: Object.fromEntries(
|
|
183
|
+
Object.entries(properties).map(([name, property]) => [
|
|
184
|
+
property.id || name,
|
|
185
|
+
propertyValue(property),
|
|
186
|
+
]),
|
|
187
|
+
),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
hasMore = response.has_more === true && !!response.next_cursor;
|
|
191
|
+
cursor = response.next_cursor ?? undefined;
|
|
192
|
+
if (!hasMore || entries.length >= args.limit) break;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const fetchedAt = new Date().toISOString();
|
|
196
|
+
return {
|
|
197
|
+
state: "live",
|
|
198
|
+
entries: entries.slice(0, args.limit),
|
|
199
|
+
fields,
|
|
200
|
+
fetchedAt,
|
|
201
|
+
message: hasMore
|
|
202
|
+
? `Loaded a bounded Notion snapshot; more rows remain after cursor ${cursor ? "present" : "missing"}.`
|
|
203
|
+
: null,
|
|
204
|
+
metadata: {
|
|
205
|
+
provider: "notion",
|
|
206
|
+
dataSourceId: dataSource.id,
|
|
207
|
+
dataSourceName: plainText(dataSource.title) || "Notion database",
|
|
208
|
+
hasMore,
|
|
209
|
+
nextCursor: cursor ?? null,
|
|
210
|
+
fetchedRowCount: Math.min(entries.length, args.limit),
|
|
211
|
+
unsupportedPropertyCount: fields.filter((field) =>
|
|
212
|
+
field.type.startsWith("unsupported:"),
|
|
213
|
+
).length,
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Serializes "read MAX(position) among siblings, then insert/update at
|
|
2
|
+
// MAX+1" sequences so two concurrent writers (agent + human, two browser
|
|
3
|
+
// tabs, an import job racing a manual add) never read the same MAX and
|
|
4
|
+
// persist duplicate `position` values. `position` columns in this app have
|
|
5
|
+
// no unique/sequence constraint (see templates/content/server/db/schema.ts),
|
|
6
|
+
// so ordering is only as stable as the read-then-insert stays atomic.
|
|
7
|
+
//
|
|
8
|
+
// Mirrors the per-deck lock in templates/slides/actions/patch-deck.ts: an
|
|
9
|
+
// in-process, globalThis-keyed promise chain per scope key. This only
|
|
10
|
+
// serializes writers within THIS process, but that is sufficient here
|
|
11
|
+
// because every write path already goes through the app's own action/route
|
|
12
|
+
// layer in the same server process — it is not a substitute for a DB-level
|
|
13
|
+
// unique constraint, but it closes the read-then-write race that produces
|
|
14
|
+
// duplicate positions today.
|
|
15
|
+
//
|
|
16
|
+
// Sites that already compute MAX(position) and insert inside the SAME
|
|
17
|
+
// `db.transaction()` (e.g. submit-content-database-form.ts,
|
|
18
|
+
// add-content-database-source-field-property.ts) do not need this helper —
|
|
19
|
+
// wrap new sites in the lock instead of introducing a second mechanism.
|
|
20
|
+
|
|
21
|
+
const LOCK_KEY = "__contentPositionLocks" as const;
|
|
22
|
+
type GlobalWithLocks = typeof globalThis & {
|
|
23
|
+
[LOCK_KEY]?: Map<string, Promise<unknown>>;
|
|
24
|
+
};
|
|
25
|
+
const globalRef = globalThis as GlobalWithLocks;
|
|
26
|
+
if (!globalRef[LOCK_KEY]) {
|
|
27
|
+
globalRef[LOCK_KEY] = new Map<string, Promise<unknown>>();
|
|
28
|
+
}
|
|
29
|
+
const positionLocks: Map<string, Promise<unknown>> = globalRef[LOCK_KEY]!;
|
|
30
|
+
|
|
31
|
+
/** Run `fn` serialized against any other call using the same `scopeKey`. */
|
|
32
|
+
export function withPositionLock<T>(
|
|
33
|
+
scopeKey: string,
|
|
34
|
+
fn: () => Promise<T>,
|
|
35
|
+
): Promise<T> {
|
|
36
|
+
const prev = positionLocks.get(scopeKey) ?? Promise.resolve();
|
|
37
|
+
const next = prev.then(fn, fn);
|
|
38
|
+
positionLocks.set(scopeKey, next);
|
|
39
|
+
next
|
|
40
|
+
.finally(() => {
|
|
41
|
+
if (positionLocks.get(scopeKey) === next) positionLocks.delete(scopeKey);
|
|
42
|
+
})
|
|
43
|
+
.catch(() => {});
|
|
44
|
+
return next;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Lock scope for `documents.position` siblings under one owner + parent
|
|
49
|
+
* (or one owner's root level when `parentId` is null/undefined). Every call
|
|
50
|
+
* site that computes MAX(position) over `documents` scoped by
|
|
51
|
+
* (ownerEmail, parentId) should share this key so concurrent inserts under
|
|
52
|
+
* the same parent — regardless of which action/route triggers them — are
|
|
53
|
+
* serialized against each other.
|
|
54
|
+
*/
|
|
55
|
+
export function documentsPositionScope(
|
|
56
|
+
ownerEmail: string,
|
|
57
|
+
parentId: string | null | undefined,
|
|
58
|
+
): string {
|
|
59
|
+
return `documents:${ownerEmail}:${parentId ?? "root"}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Lock scope for `content_database_items.position` rows within one
|
|
64
|
+
* database.
|
|
65
|
+
*/
|
|
66
|
+
export function databaseItemsPositionScope(databaseId: string): string {
|
|
67
|
+
return `contentDatabaseItems:${databaseId}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Lock scope for `document_property_definitions.position` rows within one
|
|
72
|
+
* database.
|
|
73
|
+
*/
|
|
74
|
+
export function propertyDefinitionsPositionScope(databaseId: string): string {
|
|
75
|
+
return `documentPropertyDefinitions:${databaseId}`;
|
|
76
|
+
}
|