@agent-native/core 0.119.6 → 0.120.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +19 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/index.ts +64 -3
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +45 -0
- package/corpus/core/src/client/use-chat-models.ts +45 -0
- package/corpus/core/src/server/analytics.ts +15 -0
- package/corpus/core/src/server/auth.ts +17 -13
- package/corpus/core/src/server/better-auth-instance.ts +13 -1
- package/corpus/core/src/templates/default/package.json +1 -1
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/assets/changelog/2026-07-23-new-chat-aligns-with-sidebar-controls.md +6 -0
- package/corpus/templates/assets/package.json +1 -1
- package/corpus/templates/brain/package.json +1 -1
- package/corpus/templates/calendar/package.json +1 -1
- package/corpus/templates/clips/package.json +1 -1
- package/corpus/templates/content/actions/add-database-item.ts +1 -0
- package/corpus/templates/content/actions/set-document-property.ts +0 -4
- package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +10 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +10 -4
- package/corpus/templates/content/app/components/editor/DocumentInfoPanel.tsx +5 -1
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +52 -33
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +16 -4
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +16 -7
- package/corpus/templates/content/app/hooks/use-create-page.ts +8 -3
- package/corpus/templates/content/app/hooks/use-document-properties.ts +1 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +3 -1
- package/corpus/templates/content/app/lib/optimistic-document.ts +29 -0
- package/corpus/templates/content/changelog/2026-07-21-editing-a-database-property-no-longer-refreshes-unrelated-co.md +6 -0
- package/corpus/templates/content/changelog/2026-07-23-new-database-ready.md +6 -0
- package/corpus/templates/content/package.json +1 -1
- package/corpus/templates/content/shared/api.ts +1 -0
- package/corpus/templates/design/package.json +1 -1
- package/corpus/templates/dispatch/package.json +1 -1
- package/corpus/templates/forms/.agents/skills/form-publishing/SKILL.md +9 -3
- package/corpus/templates/forms/AGENTS.md +11 -1
- package/corpus/templates/forms/README.md +24 -1
- package/corpus/templates/forms/actions/update-form.ts +12 -5
- package/corpus/templates/forms/app/components/builder/FieldPropertiesPanel.tsx +199 -1
- package/corpus/templates/forms/app/i18n/en-US.ts +13 -1
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +35 -4
- package/corpus/templates/forms/app/pages/FormFillPage.tsx +7 -16
- package/corpus/templates/forms/changelog/2026-07-23-forms-can-branch-into-follow-up-questions-and-route-response.md +6 -0
- package/corpus/templates/forms/package.json +1 -1
- package/corpus/templates/forms/server/handlers/submissions.ts +22 -31
- package/corpus/templates/forms/server/lib/integrations.ts +11 -2
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +26 -5
- package/corpus/templates/forms/server/lib/validate-fields.ts +48 -11
- package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/forms/shared/conditional.ts +67 -0
- package/corpus/templates/macros/package.json +1 -1
- package/corpus/templates/mail/package.json +1 -1
- package/corpus/templates/slides/package.json +1 -1
- package/dist/cli/index.js +53 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +35 -0
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/use-chat-models.d.ts +1 -0
- package/dist/client/use-chat-models.d.ts.map +1 -1
- package/dist/client/use-chat-models.js +33 -0
- package/dist/client/use-chat-models.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +9 -9
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/analytics.d.ts +8 -0
- package/dist/server/analytics.d.ts.map +1 -1
- package/dist/server/analytics.js +16 -0
- package/dist/server/analytics.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +5 -4
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts +8 -0
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +10 -1
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/default/package.json +1 -1
- package/package.json +1 -1
- package/src/cli/index.ts +64 -3
- package/src/client/MultiTabAssistantChat.tsx +45 -0
- package/src/client/use-chat-models.ts +45 -0
- package/src/server/analytics.ts +15 -0
- package/src/server/auth.ts +17 -13
- package/src/server/better-auth-instance.ts +13 -1
- package/src/templates/default/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.120.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5477352: Keep Dispatch's overview composer, full-page chat, and side chat on the same selected model.
|
|
8
|
+
- 5477352: Inject the configured Google Analytics tag into framework-owned login and signup pages.
|
|
9
|
+
|
|
10
|
+
## 0.120.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- 20ebb96: `agent-native dev --inspect` (and `--inspect-brk`, optionally `=<port>`) now
|
|
15
|
+
attaches the Node inspector to **only** the Nitro API-server process, on a
|
|
16
|
+
single known port (default 9229). It selects Nitro's `node-process` dev runner
|
|
17
|
+
so the server is a real, attachable process, and injects `NODE_OPTIONS` through
|
|
18
|
+
a Vite preload that runs before Vite's own startup — so Vite, pnpm, and the CLI
|
|
19
|
+
are never inspected and there is exactly one debugger target. Set
|
|
20
|
+
`NITRO_DEV_RUNNER` yourself to override the runner.
|
|
21
|
+
|
|
3
22
|
## 0.119.6
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.120.1",
|
|
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": {
|
|
@@ -276,6 +276,22 @@ function findViteBin(): string {
|
|
|
276
276
|
return findBinUpwards("vite") ?? "vite";
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
function findViteJsEntry(): string | null {
|
|
280
|
+
try {
|
|
281
|
+
const require = createRequire(path.join(process.cwd(), "package.json"));
|
|
282
|
+
const pkgJsonPath = require.resolve("vite/package.json");
|
|
283
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8")) as {
|
|
284
|
+
bin?: string | Record<string, string>;
|
|
285
|
+
};
|
|
286
|
+
const rel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.vite;
|
|
287
|
+
if (!rel) return null;
|
|
288
|
+
const entry = path.join(path.dirname(pkgJsonPath), rel);
|
|
289
|
+
return fs.existsSync(entry) ? entry : null;
|
|
290
|
+
} catch {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
279
295
|
function findTsxBin(): string {
|
|
280
296
|
const localTsx = path.resolve("node_modules/.bin/tsx");
|
|
281
297
|
if (fs.existsSync(localTsx)) return localTsx;
|
|
@@ -386,15 +402,31 @@ function isWorkspaceRoot(): boolean {
|
|
|
386
402
|
}
|
|
387
403
|
}
|
|
388
404
|
|
|
405
|
+
function extractNodeInspectFlag(args: string[]): {
|
|
406
|
+
inspectFlag: string | null;
|
|
407
|
+
rest: string[];
|
|
408
|
+
} {
|
|
409
|
+
const rest: string[] = [];
|
|
410
|
+
let inspectFlag: string | null = null;
|
|
411
|
+
for (const arg of args) {
|
|
412
|
+
if (/^--inspect(-brk)?(=.+)?$/.test(arg)) {
|
|
413
|
+
inspectFlag = arg;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
rest.push(arg);
|
|
417
|
+
}
|
|
418
|
+
return { inspectFlag, rest };
|
|
419
|
+
}
|
|
420
|
+
|
|
389
421
|
function run(
|
|
390
422
|
cmd: string,
|
|
391
423
|
cmdArgs: string[],
|
|
392
|
-
opts?: { stdio?: "inherit" | "pipe" },
|
|
424
|
+
opts?: { stdio?: "inherit" | "pipe"; env?: NodeJS.ProcessEnv },
|
|
393
425
|
) {
|
|
394
426
|
const child = spawn(cmd, cmdArgs, {
|
|
395
427
|
stdio: opts?.stdio ?? "inherit",
|
|
396
428
|
shell: process.platform === "win32",
|
|
397
|
-
env: process.env,
|
|
429
|
+
env: opts?.env ?? process.env,
|
|
398
430
|
});
|
|
399
431
|
child.on("exit", (code) => process.exit(code ?? 0));
|
|
400
432
|
// Forward signals to child so Cmd+C doesn't leave zombie processes holding ports
|
|
@@ -545,7 +577,36 @@ switch (command) {
|
|
|
545
577
|
break;
|
|
546
578
|
}
|
|
547
579
|
const vite = findViteBin();
|
|
548
|
-
|
|
580
|
+
const { inspectFlag, rest } = extractNodeInspectFlag(args);
|
|
581
|
+
if (!inspectFlag) {
|
|
582
|
+
run(vite, rest);
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
const viteJsEntry = findViteJsEntry();
|
|
586
|
+
if (!viteJsEntry) {
|
|
587
|
+
console.warn(
|
|
588
|
+
"[agent-native] Could not resolve Vite's JS entry; starting dev " +
|
|
589
|
+
"server without the debugger.",
|
|
590
|
+
);
|
|
591
|
+
run(vite, rest);
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
// Attach inspect flag to server process (not Vite or Nitro process)
|
|
595
|
+
const parsed = inspectFlag.match(/^--(inspect(?:-brk)?)(?:=(.+))?$/);
|
|
596
|
+
const kind = parsed?.[1] ?? "inspect";
|
|
597
|
+
const target = parsed?.[2] ?? "9229";
|
|
598
|
+
const directive = `--${kind}=${target}`;
|
|
599
|
+
const preload =
|
|
600
|
+
"data:text/javascript," +
|
|
601
|
+
encodeURIComponent(
|
|
602
|
+
`process.env.NODE_OPTIONS=((process.env.NODE_OPTIONS??"")+" ${directive}").trim();`,
|
|
603
|
+
);
|
|
604
|
+
const env = {
|
|
605
|
+
...process.env,
|
|
606
|
+
NITRO_DEV_RUNNER: process.env.NITRO_DEV_RUNNER ?? "node-process",
|
|
607
|
+
};
|
|
608
|
+
console.log(`[agent-native] API server debugger listening on ${target}`);
|
|
609
|
+
run(process.execPath, ["--import", preload, viteJsEntry, ...rest], { env });
|
|
549
610
|
break;
|
|
550
611
|
}
|
|
551
612
|
|
|
@@ -56,6 +56,7 @@ import { isTrustedFrameMessage } from "./frame.js";
|
|
|
56
56
|
import { RunStuckBanner } from "./RunStuckBanner.js";
|
|
57
57
|
import { callAction } from "./use-action.js";
|
|
58
58
|
import { useChangeVersion } from "./use-change-version.js";
|
|
59
|
+
import { CHAT_MODEL_SELECTION_CHANGED_EVENT } from "./use-chat-models.js";
|
|
59
60
|
import {
|
|
60
61
|
useChatThreads,
|
|
61
62
|
type ChatThreadScope,
|
|
@@ -138,6 +139,13 @@ function writeStoredModelSelection(key: string, selection: ModelSelection) {
|
|
|
138
139
|
if (typeof window === "undefined") return;
|
|
139
140
|
try {
|
|
140
141
|
window.localStorage.setItem(key, JSON.stringify(selection));
|
|
142
|
+
queueMicrotask(() => {
|
|
143
|
+
window.dispatchEvent(
|
|
144
|
+
new CustomEvent(CHAT_MODEL_SELECTION_CHANGED_EVENT, {
|
|
145
|
+
detail: { key },
|
|
146
|
+
}),
|
|
147
|
+
);
|
|
148
|
+
});
|
|
141
149
|
} catch {}
|
|
142
150
|
}
|
|
143
151
|
|
|
@@ -954,6 +962,43 @@ export function MultiTabAssistantChat({
|
|
|
954
962
|
const bumpModelSelectionVersion = useCallback(() => {
|
|
955
963
|
setModelSelectionVersion((version) => version + 1);
|
|
956
964
|
}, []);
|
|
965
|
+
|
|
966
|
+
useEffect(() => {
|
|
967
|
+
if (typeof window === "undefined") return;
|
|
968
|
+
|
|
969
|
+
const syncPersistedSelection = (event?: Event) => {
|
|
970
|
+
const detail = (event as CustomEvent<{ key?: string }> | undefined)
|
|
971
|
+
?.detail;
|
|
972
|
+
if (detail?.key && detail.key !== modelSelectionKey) return;
|
|
973
|
+
|
|
974
|
+
const next = readStoredModelSelection(modelSelectionKey);
|
|
975
|
+
if (!next) return;
|
|
976
|
+
|
|
977
|
+
const activeThreadId = activeThreadIdRef.current;
|
|
978
|
+
if (activeThreadId) {
|
|
979
|
+
threadModelRef.current.set(activeThreadId, next);
|
|
980
|
+
}
|
|
981
|
+
setPersistedModelSelection(next);
|
|
982
|
+
bumpModelSelectionVersion();
|
|
983
|
+
};
|
|
984
|
+
const handleStorage = (event: StorageEvent) => {
|
|
985
|
+
if (event.key === modelSelectionKey) syncPersistedSelection();
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
window.addEventListener(
|
|
989
|
+
CHAT_MODEL_SELECTION_CHANGED_EVENT,
|
|
990
|
+
syncPersistedSelection,
|
|
991
|
+
);
|
|
992
|
+
window.addEventListener("storage", handleStorage);
|
|
993
|
+
return () => {
|
|
994
|
+
window.removeEventListener(
|
|
995
|
+
CHAT_MODEL_SELECTION_CHANGED_EVENT,
|
|
996
|
+
syncPersistedSelection,
|
|
997
|
+
);
|
|
998
|
+
window.removeEventListener("storage", handleStorage);
|
|
999
|
+
};
|
|
1000
|
+
}, [bumpModelSelectionVersion, modelSelectionKey]);
|
|
1001
|
+
|
|
957
1002
|
const postMessageSubmissionsDisabled = props.composerDisabled === true;
|
|
958
1003
|
|
|
959
1004
|
const setContextInTab = useCallback(
|
|
@@ -42,6 +42,8 @@ interface Options {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const DEFAULT_STORAGE_KEY = "agent-native:chat-models:selection";
|
|
45
|
+
export const CHAT_MODEL_SELECTION_CHANGED_EVENT =
|
|
46
|
+
"agent-native:chat-model-selection-changed";
|
|
45
47
|
|
|
46
48
|
interface PersistedSelection {
|
|
47
49
|
model?: string;
|
|
@@ -63,6 +65,13 @@ function writePersisted(key: string | null, value: PersistedSelection) {
|
|
|
63
65
|
if (!key || typeof window === "undefined") return;
|
|
64
66
|
try {
|
|
65
67
|
window.localStorage.setItem(key, JSON.stringify(value));
|
|
68
|
+
queueMicrotask(() => {
|
|
69
|
+
window.dispatchEvent(
|
|
70
|
+
new CustomEvent(CHAT_MODEL_SELECTION_CHANGED_EVENT, {
|
|
71
|
+
detail: { key },
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
});
|
|
66
75
|
} catch {}
|
|
67
76
|
}
|
|
68
77
|
|
|
@@ -110,6 +119,42 @@ export function useChatModels({
|
|
|
110
119
|
};
|
|
111
120
|
}, [selectedEffort, selectedEngine, selectedModel]);
|
|
112
121
|
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (!storageKey || typeof window === "undefined") return;
|
|
124
|
+
|
|
125
|
+
const syncPersistedSelection = (event?: Event) => {
|
|
126
|
+
const detail = (event as CustomEvent<{ key?: string }> | undefined)
|
|
127
|
+
?.detail;
|
|
128
|
+
if (detail?.key && detail.key !== storageKey) return;
|
|
129
|
+
|
|
130
|
+
const next = readPersisted(storageKey);
|
|
131
|
+
if (!next.model) return;
|
|
132
|
+
|
|
133
|
+
hasExplicitSelectionRef.current = true;
|
|
134
|
+
setSelectedModel(next.model);
|
|
135
|
+
setSelectedEngine(next.engine ?? "");
|
|
136
|
+
setSelectedEffort(
|
|
137
|
+
resolveReasoningEffortSelection(next.model, next.effort),
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
const handleStorage = (event: StorageEvent) => {
|
|
141
|
+
if (event.key === storageKey) syncPersistedSelection();
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
window.addEventListener(
|
|
145
|
+
CHAT_MODEL_SELECTION_CHANGED_EVENT,
|
|
146
|
+
syncPersistedSelection,
|
|
147
|
+
);
|
|
148
|
+
window.addEventListener("storage", handleStorage);
|
|
149
|
+
return () => {
|
|
150
|
+
window.removeEventListener(
|
|
151
|
+
CHAT_MODEL_SELECTION_CHANGED_EVENT,
|
|
152
|
+
syncPersistedSelection,
|
|
153
|
+
);
|
|
154
|
+
window.removeEventListener("storage", handleStorage);
|
|
155
|
+
};
|
|
156
|
+
}, [storageKey]);
|
|
157
|
+
|
|
113
158
|
const onModelChange = useCallback(
|
|
114
159
|
(model: string, engine: string) => {
|
|
115
160
|
hasExplicitSelectionRef.current = true;
|
|
@@ -74,6 +74,21 @@ function getGaScript(): string | null {
|
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Add the configured analytics scripts to a complete HTML document.
|
|
79
|
+
*
|
|
80
|
+
* The normal app document is streamed through `wrapWithAnalytics`, but
|
|
81
|
+
* framework-owned documents such as `/signup` are returned as strings by the
|
|
82
|
+
* auth guard and need the same injection path.
|
|
83
|
+
*/
|
|
84
|
+
export function injectAnalyticsIntoHtml(html: string): string {
|
|
85
|
+
const script = getGaScript();
|
|
86
|
+
if (!script) return html;
|
|
87
|
+
const headCloseIdx = html.indexOf("</head>");
|
|
88
|
+
if (headCloseIdx === -1) return html;
|
|
89
|
+
return html.slice(0, headCloseIdx) + script + html.slice(headCloseIdx);
|
|
90
|
+
}
|
|
91
|
+
|
|
77
92
|
export function wrapWithAnalytics(body: ReadableStream): ReadableStream {
|
|
78
93
|
const scripts = [getGaScript()].filter(Boolean).join("");
|
|
79
94
|
if (!scripts) return body;
|
|
@@ -98,6 +98,7 @@ import {
|
|
|
98
98
|
type WorkspaceAppAudience,
|
|
99
99
|
} from "../shared/workspace-app-audience.js";
|
|
100
100
|
import { isValidWorkspaceAppIdFormat } from "../shared/workspace-app-id.js";
|
|
101
|
+
import { injectAnalyticsIntoHtml } from "./analytics.js";
|
|
101
102
|
import { signupAttributionFromCookieHeader } from "./attribution.js";
|
|
102
103
|
import { getBetterAuth, getBetterAuthSync } from "./better-auth-instance.js";
|
|
103
104
|
import type { BetterAuthConfig } from "./better-auth-instance.js";
|
|
@@ -1646,20 +1647,23 @@ function injectLoginSocialImageMeta(loginHtml: string, event: H3Event): string {
|
|
|
1646
1647
|
}
|
|
1647
1648
|
|
|
1648
1649
|
function loginHtmlResponse(loginHtml: string, event: H3Event): Response {
|
|
1649
|
-
return new Response(
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1650
|
+
return new Response(
|
|
1651
|
+
injectAnalyticsIntoHtml(injectLoginSocialImageMeta(loginHtml, event)),
|
|
1652
|
+
{
|
|
1653
|
+
status: 200,
|
|
1654
|
+
headers: {
|
|
1655
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
1656
|
+
// The sign-in document is part of the public server shell. Keep it on the
|
|
1657
|
+
// same long-fresh/long-SWR CDN policy as React Router SSR so hosted
|
|
1658
|
+
// template roots do not invoke origin just to render anonymous login UI.
|
|
1659
|
+
// The login markup is env-INDEPENDENT (a Google-only app always renders
|
|
1660
|
+
// a working button); the analytics script is public build configuration,
|
|
1661
|
+
// not user/session state. Never downgrade this to private/no-store.
|
|
1662
|
+
...DEFAULT_SSR_CACHE_HEADERS,
|
|
1663
|
+
"X-Robots-Tag": "noindex, nofollow",
|
|
1664
|
+
},
|
|
1661
1665
|
},
|
|
1662
|
-
|
|
1666
|
+
);
|
|
1663
1667
|
}
|
|
1664
1668
|
|
|
1665
1669
|
function isHtmlDocumentRequest(event: H3Event, pathname: string): boolean {
|
|
@@ -1109,6 +1109,18 @@ async function createBetterAuthInstance(
|
|
|
1109
1109
|
return auth as unknown as BetterAuthInstance;
|
|
1110
1110
|
}
|
|
1111
1111
|
|
|
1112
|
+
/**
|
|
1113
|
+
* Configure the local auth connection with the same write contention settings
|
|
1114
|
+
* as the shared app connection. Better Auth uses its own SQLite handle, so the
|
|
1115
|
+
* app connection's busy timeout does not protect first-run account creation.
|
|
1116
|
+
*/
|
|
1117
|
+
export function configureLocalSqlite(sqlite: {
|
|
1118
|
+
pragma(statement: string): unknown;
|
|
1119
|
+
}): void {
|
|
1120
|
+
sqlite.pragma("busy_timeout = 10000");
|
|
1121
|
+
sqlite.pragma("journal_mode = WAL");
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1112
1124
|
async function buildDatabaseConfig(
|
|
1113
1125
|
dialect: string,
|
|
1114
1126
|
): Promise<BetterAuthOptions["database"]> {
|
|
@@ -1182,7 +1194,7 @@ async function buildDatabaseConfig(
|
|
|
1182
1194
|
const { default: Database } = await import("better-sqlite3");
|
|
1183
1195
|
const filePath = url.replace(/^file:/, "");
|
|
1184
1196
|
const sqlite = new Database(filePath);
|
|
1185
|
-
sqlite
|
|
1197
|
+
configureLocalSqlite(sqlite);
|
|
1186
1198
|
const { drizzle } = await import("drizzle-orm/better-sqlite3");
|
|
1187
1199
|
const db = drizzle(sqlite, { schema: sqliteAuthSchema });
|
|
1188
1200
|
const { drizzleAdapter } = await import("better-auth/adapters/drizzle");
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
-
import { writeAppState } from "@agent-native/core/application-state";
|
|
3
2
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
4
3
|
import { and, eq } from "drizzle-orm";
|
|
5
4
|
import { z } from "zod";
|
|
@@ -87,7 +86,6 @@ export default defineAction({
|
|
|
87
86
|
now,
|
|
88
87
|
});
|
|
89
88
|
}
|
|
90
|
-
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
91
89
|
return {
|
|
92
90
|
documentId,
|
|
93
91
|
databaseId: database.id,
|
|
@@ -133,8 +131,6 @@ export default defineAction({
|
|
|
133
131
|
});
|
|
134
132
|
}
|
|
135
133
|
|
|
136
|
-
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
137
|
-
|
|
138
134
|
return {
|
|
139
135
|
documentId,
|
|
140
136
|
databaseId: database.id,
|
|
@@ -38,6 +38,7 @@ const BLOCK_FIELD_DRAG_THRESHOLD = 6;
|
|
|
38
38
|
|
|
39
39
|
interface DocumentBlockFieldsProps {
|
|
40
40
|
documentId: string;
|
|
41
|
+
databaseDocumentId: string;
|
|
41
42
|
canEdit: boolean;
|
|
42
43
|
/**
|
|
43
44
|
* The fully-wired collaborative body editor for the primary "Content" field.
|
|
@@ -238,6 +239,7 @@ export function blockFieldsRenderState(args: {
|
|
|
238
239
|
*/
|
|
239
240
|
export function DocumentBlockFields({
|
|
240
241
|
documentId,
|
|
242
|
+
databaseDocumentId,
|
|
241
243
|
canEdit,
|
|
242
244
|
primaryEditor,
|
|
243
245
|
}: DocumentBlockFieldsProps) {
|
|
@@ -303,6 +305,7 @@ export function DocumentBlockFields({
|
|
|
303
305
|
// field while SAVING to another across an identity change.
|
|
304
306
|
key={`${documentId}:${state.field.definition.id}`}
|
|
305
307
|
documentId={documentId}
|
|
308
|
+
databaseDocumentId={databaseDocumentId}
|
|
306
309
|
property={state.field}
|
|
307
310
|
canEdit={canEdit}
|
|
308
311
|
/>
|
|
@@ -318,6 +321,7 @@ export function DocumentBlockFields({
|
|
|
318
321
|
return (
|
|
319
322
|
<MultiBlockFields
|
|
320
323
|
documentId={documentId}
|
|
324
|
+
databaseDocumentId={databaseDocumentId}
|
|
321
325
|
canEdit={canEdit}
|
|
322
326
|
blockFields={state.fields}
|
|
323
327
|
primaryEditor={primaryEditor}
|
|
@@ -329,12 +333,14 @@ export function DocumentBlockFields({
|
|
|
329
333
|
|
|
330
334
|
function MultiBlockFields({
|
|
331
335
|
documentId,
|
|
336
|
+
databaseDocumentId,
|
|
332
337
|
canEdit,
|
|
333
338
|
blockFields,
|
|
334
339
|
primaryEditor,
|
|
335
340
|
t,
|
|
336
341
|
}: {
|
|
337
342
|
documentId: string;
|
|
343
|
+
databaseDocumentId: string;
|
|
338
344
|
canEdit: boolean;
|
|
339
345
|
blockFields: DocumentProperty[];
|
|
340
346
|
primaryEditor: ReactNode;
|
|
@@ -504,6 +510,7 @@ function MultiBlockFields({
|
|
|
504
510
|
// doc's edits to the old field's closure.
|
|
505
511
|
key={`${documentId}:${property.definition.id}`}
|
|
506
512
|
documentId={documentId}
|
|
513
|
+
databaseDocumentId={databaseDocumentId}
|
|
507
514
|
property={property}
|
|
508
515
|
canEdit={canEdit}
|
|
509
516
|
/>
|
|
@@ -789,14 +796,16 @@ export function useBlockFieldEditor({
|
|
|
789
796
|
*/
|
|
790
797
|
function AdditionalBlockEditor({
|
|
791
798
|
documentId,
|
|
799
|
+
databaseDocumentId,
|
|
792
800
|
property,
|
|
793
801
|
canEdit,
|
|
794
802
|
}: {
|
|
795
803
|
documentId: string;
|
|
804
|
+
databaseDocumentId: string;
|
|
796
805
|
property: DocumentProperty;
|
|
797
806
|
canEdit: boolean;
|
|
798
807
|
}) {
|
|
799
|
-
const setProperty = useSetDocumentProperty(documentId);
|
|
808
|
+
const setProperty = useSetDocumentProperty(documentId, databaseDocumentId);
|
|
800
809
|
const propertyId = property.definition.id;
|
|
801
810
|
const initialContent =
|
|
802
811
|
typeof property.value === "string" ? property.value : "";
|
|
@@ -80,6 +80,7 @@ import {
|
|
|
80
80
|
canWriteLinkedLocalSource,
|
|
81
81
|
writeDocumentToLinkedLocalSource,
|
|
82
82
|
} from "@/lib/local-content-source-files";
|
|
83
|
+
import { isDatabaseChoicePending } from "@/lib/optimistic-document";
|
|
83
84
|
import { cn } from "@/lib/utils";
|
|
84
85
|
|
|
85
86
|
import {
|
|
@@ -1519,6 +1520,10 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
|
|
|
1519
1520
|
);
|
|
1520
1521
|
const defaultIconKind = documentEditorDefaultIconKind(document);
|
|
1521
1522
|
const isDatabasePage = Boolean(document.database);
|
|
1523
|
+
const databaseChoicePending = isDatabaseChoicePending(
|
|
1524
|
+
document,
|
|
1525
|
+
createDatabase.isPending,
|
|
1526
|
+
);
|
|
1522
1527
|
const showNewDocumentTypeChooser =
|
|
1523
1528
|
canEdit &&
|
|
1524
1529
|
!isLocalFileDocument &&
|
|
@@ -1791,12 +1796,10 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
|
|
|
1791
1796
|
type="button"
|
|
1792
1797
|
variant="outline"
|
|
1793
1798
|
className="justify-start gap-2"
|
|
1794
|
-
disabled={
|
|
1795
|
-
!editorCanEdit || createDatabase.isPending
|
|
1796
|
-
}
|
|
1799
|
+
disabled={!editorCanEdit || databaseChoicePending}
|
|
1797
1800
|
onClick={() => void handleChooseDatabase()}
|
|
1798
1801
|
>
|
|
1799
|
-
{
|
|
1802
|
+
{databaseChoicePending ? (
|
|
1800
1803
|
<IconLoader2 className="animate-spin" />
|
|
1801
1804
|
) : (
|
|
1802
1805
|
<IconDatabase />
|
|
@@ -1867,6 +1870,9 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
|
|
|
1867
1870
|
return (
|
|
1868
1871
|
<DocumentBlockFields
|
|
1869
1872
|
documentId={documentId}
|
|
1873
|
+
databaseDocumentId={
|
|
1874
|
+
document.databaseMembership.databaseDocumentId
|
|
1875
|
+
}
|
|
1870
1876
|
canEdit={editorCanEdit}
|
|
1871
1877
|
primaryEditor={primaryEditor}
|
|
1872
1878
|
/>
|
|
@@ -32,7 +32,11 @@ export function DocumentInfoPanel({
|
|
|
32
32
|
onSave={onSaveDescription}
|
|
33
33
|
/>
|
|
34
34
|
{document.databaseMembership && !isLocalFileDocument ? (
|
|
35
|
-
<DocumentProperties
|
|
35
|
+
<DocumentProperties
|
|
36
|
+
documentId={document.id}
|
|
37
|
+
databaseDocumentId={document.databaseMembership.databaseDocumentId}
|
|
38
|
+
canEdit={canEdit}
|
|
39
|
+
/>
|
|
36
40
|
) : null}
|
|
37
41
|
</div>
|
|
38
42
|
);
|
|
@@ -165,6 +165,7 @@ function tWithFallback(
|
|
|
165
165
|
|
|
166
166
|
interface DocumentPropertiesProps {
|
|
167
167
|
documentId: string;
|
|
168
|
+
databaseDocumentId: string;
|
|
168
169
|
canEdit: boolean;
|
|
169
170
|
popoversPortalled?: boolean;
|
|
170
171
|
}
|
|
@@ -796,6 +797,7 @@ function scalarPlaceholder(type: DocumentPropertyType, t: TFunction) {
|
|
|
796
797
|
|
|
797
798
|
export function DocumentProperties({
|
|
798
799
|
documentId,
|
|
800
|
+
databaseDocumentId,
|
|
799
801
|
canEdit,
|
|
800
802
|
popoversPortalled = true,
|
|
801
803
|
}: DocumentPropertiesProps) {
|
|
@@ -826,6 +828,7 @@ export function DocumentProperties({
|
|
|
826
828
|
key={property.definition.id}
|
|
827
829
|
property={property}
|
|
828
830
|
documentId={documentId}
|
|
831
|
+
databaseDocumentId={databaseDocumentId}
|
|
829
832
|
canEdit={canEdit}
|
|
830
833
|
popoversPortalled={popoversPortalled}
|
|
831
834
|
t={t}
|
|
@@ -927,12 +930,14 @@ function HiddenPropertiesMenu({
|
|
|
927
930
|
function PropertyRow({
|
|
928
931
|
property,
|
|
929
932
|
documentId,
|
|
933
|
+
databaseDocumentId,
|
|
930
934
|
canEdit,
|
|
931
935
|
popoversPortalled,
|
|
932
936
|
t,
|
|
933
937
|
}: {
|
|
934
938
|
property: DocumentProperty;
|
|
935
939
|
documentId: string;
|
|
940
|
+
databaseDocumentId: string;
|
|
936
941
|
canEdit: boolean;
|
|
937
942
|
popoversPortalled: boolean;
|
|
938
943
|
t: TFunction;
|
|
@@ -978,6 +983,7 @@ function PropertyRow({
|
|
|
978
983
|
<PropertyValuePopover
|
|
979
984
|
property={property}
|
|
980
985
|
documentId={documentId}
|
|
986
|
+
databaseDocumentId={databaseDocumentId}
|
|
981
987
|
portalled={popoversPortalled}
|
|
982
988
|
>
|
|
983
989
|
{value}
|