alinea 2.0.0-preview.7 → 2.0.0-preview.9

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.
Files changed (301) hide show
  1. package/dist/.init/content/pages/welcome.json +1 -1
  2. package/dist/LICENSES.md +103 -103
  3. package/dist/adapter/next/cms.d.ts +18 -4
  4. package/dist/adapter/next/cms.js +141 -36
  5. package/dist/adapter/next/handler.d.ts +5 -1
  6. package/dist/adapter/next/handler.js +77 -17
  7. package/dist/adapter/next/node.d.ts +5 -0
  8. package/dist/adapter/next/node.js +16 -0
  9. package/dist/adapter/next/previews.d.ts +4 -1
  10. package/dist/adapter/next/previews.js +13 -1
  11. package/dist/adapter/next/renderStats.d.ts +45 -0
  12. package/dist/adapter/next/renderStats.js +142 -0
  13. package/dist/adapter/next/syncCheck.d.ts +8 -2
  14. package/dist/adapter/next/syncCheck.js +5 -11
  15. package/dist/adapter/next/with-alinea.js +6 -20
  16. package/dist/backend/HandleAction.d.ts +1 -1
  17. package/dist/backend/HandleAction.js +1 -1
  18. package/dist/backend/Handler.d.ts +2 -2
  19. package/dist/backend/Handler.js +45 -47
  20. package/dist/backend/api/CreateBackend.js +15 -3
  21. package/dist/backend/api/DatabaseApi.js +22 -135
  22. package/dist/backend/api/ProxyMedia.d.ts +2 -3
  23. package/dist/backend/api/ProxyMedia.js +7 -22
  24. package/dist/backend/api/database.js +15 -3
  25. package/dist/backend/resolver/ParsePreview.d.ts +2 -2
  26. package/dist/backend/router/NodeHandler.js +2 -0
  27. package/dist/backend/store/GeneratedDatabase.d.ts +8 -0
  28. package/dist/backend/store/GeneratedDatabase.js +33 -0
  29. package/dist/backend/store/GeneratedDatabaseNode.d.ts +4 -0
  30. package/dist/backend/store/GeneratedDatabaseNode.js +35 -0
  31. package/dist/backend/util/ORM.js +4 -2
  32. package/dist/bundled.d.ts +1565 -468
  33. package/dist/chunks/bun-sqlite-5SHXN3AT.js +18 -0
  34. package/dist/chunks/chunk-2BJ2IFQM.js +1085 -0
  35. package/dist/chunks/chunk-2PIFHEYR.js +364 -0
  36. package/dist/chunks/chunk-66YZ47TY.js +94 -0
  37. package/dist/chunks/chunk-76VMZTLN.js +55 -0
  38. package/dist/chunks/chunk-AIU6CMVT.js +495 -0
  39. package/dist/chunks/chunk-ASYKC6IE.js +103 -0
  40. package/dist/chunks/chunk-ATF42T2B.js +35 -0
  41. package/dist/chunks/{chunk-6P4UVRPH.js → chunk-DWR5A5KL.js} +6 -1
  42. package/dist/chunks/{chunk-NWQXN4SB.js → chunk-ETBQTP2S.js} +40 -29
  43. package/dist/chunks/chunk-ETWOY4AL.js +100 -0
  44. package/dist/chunks/chunk-K62JP6HR.js +22 -0
  45. package/dist/chunks/chunk-MOWR65LM.js +610 -0
  46. package/dist/chunks/chunk-N47Y62CI.js +396 -0
  47. package/dist/chunks/chunk-QBPYWU6C.js +24 -0
  48. package/dist/chunks/{chunk-X47OVDF3.js → chunk-QJGGYF6E.js} +356 -1000
  49. package/dist/chunks/chunk-QSN6RH6X.js +94 -0
  50. package/dist/chunks/{chunk-I4GC3E65.js → chunk-UPY2RUZF.js} +0 -1
  51. package/dist/chunks/chunk-XCZXR3C2.js +112 -0
  52. package/dist/chunks/chunk-XTWYBWP2.js +81 -0
  53. package/dist/chunks/node-sqlite-XVNTNMCY.js +18 -0
  54. package/dist/cli/Generate.js +74 -64
  55. package/dist/cli/Serve.js +1 -1
  56. package/dist/cli/bin.js +6 -1
  57. package/dist/cli/generate/CopyStaticFiles.js +17 -14
  58. package/dist/cli/generate/DevDB.d.ts +22 -5
  59. package/dist/cli/generate/DevDB.js +138 -24
  60. package/dist/cli/serve/CreateLocalServer.js +22 -4
  61. package/dist/cli/serve/DevServer.d.ts +2 -0
  62. package/dist/cli/serve/DevServer.js +7 -2
  63. package/dist/cli/serve/LiveReload.d.ts +1 -1
  64. package/dist/cli/serve/LiveReload.js +3 -0
  65. package/dist/cloud/CloudRemote.js +2 -1
  66. package/dist/components/Breadcrumbs.js +2 -2
  67. package/dist/components/Calendar.js +7 -7
  68. package/dist/components/Checkbox.js +1 -1
  69. package/dist/components/ColorSwatchPicker.js +2 -2
  70. package/dist/components/ComboBox.js +14 -14
  71. package/dist/components/DateField.js +1 -1
  72. package/dist/components/DatePicker.js +6 -6
  73. package/dist/components/DateRangePicker.js +4 -4
  74. package/dist/components/Disclosure.js +8 -8
  75. package/dist/components/Label.js +6 -6
  76. package/dist/components/List.js +33 -33
  77. package/dist/components/Menu.js +2 -2
  78. package/dist/components/Modal.js +2 -2
  79. package/dist/components/MultipleSelect.js +23 -23
  80. package/dist/components/NumberField.js +6 -6
  81. package/dist/components/ProgressCircle.js +3 -3
  82. package/dist/components/SearchField.js +8 -8
  83. package/dist/components/Select.js +8 -8
  84. package/dist/components/Surface.js +3 -3
  85. package/dist/components/Switch.js +2 -2
  86. package/dist/components/Table.js +7 -7
  87. package/dist/components/Tabs.js +2 -2
  88. package/dist/components/TagGroup.js +2 -2
  89. package/dist/components/TextField.js +2 -2
  90. package/dist/components/TimeField.js +3 -3
  91. package/dist/components/Toolbar.js +2 -2
  92. package/dist/components/Tooltip.js +4 -4
  93. package/dist/components/Tree.d.ts +2 -1
  94. package/dist/components/Tree.js +6 -6
  95. package/dist/core/Client.d.ts +1 -4
  96. package/dist/core/Client.js +5 -7
  97. package/dist/core/Connection.d.ts +4 -1
  98. package/dist/core/Connection.js +3 -1
  99. package/dist/core/Document.js +15 -3
  100. package/dist/core/Entry.d.ts +3 -0
  101. package/dist/core/Entry.js +4 -0
  102. package/dist/core/EntrySeed.d.ts +19 -0
  103. package/dist/core/EntrySeed.js +136 -0
  104. package/dist/core/Field.d.ts +7 -0
  105. package/dist/core/Field.js +4 -0
  106. package/dist/core/Filter.d.ts +40 -2
  107. package/dist/core/Filter.js +145 -0
  108. package/dist/core/Graph.d.ts +9 -0
  109. package/dist/core/ListRow.d.ts +1 -0
  110. package/dist/core/ListRow.js +1 -0
  111. package/dist/core/Reference.d.ts +1 -0
  112. package/dist/core/Root.js +1 -1
  113. package/dist/core/Scope.d.ts +2 -0
  114. package/dist/core/Scope.js +16 -1
  115. package/dist/core/Type.d.ts +2 -1
  116. package/dist/core/Type.js +13 -1
  117. package/dist/core/db/EntryAliases.js +1 -11
  118. package/dist/core/db/EntryReference.d.ts +0 -6
  119. package/dist/core/db/IndexEvent.d.ts +2 -1
  120. package/dist/core/db/IndexEvent.js +11 -3
  121. package/dist/core/db/LinkResolver.d.ts +6 -6
  122. package/dist/core/db/LinkResolver.js +0 -52
  123. package/dist/core/db/LocalStore.d.ts +27 -0
  124. package/dist/core/field/ListField.js +19 -0
  125. package/dist/core/field/RecordField.js +8 -0
  126. package/dist/core/field/RichTextField.js +42 -0
  127. package/dist/core/field/UnionField.js +8 -0
  128. package/dist/core/source/FSSource.d.ts +14 -2
  129. package/dist/core/source/FSSource.js +58 -13
  130. package/dist/core/source/GitUtils.d.ts +4 -0
  131. package/dist/core/source/GitUtils.js +9 -10
  132. package/dist/core/source/GithubSource.js +60 -21
  133. package/dist/core/source/IndexedDBSource.d.ts +2 -1
  134. package/dist/core/source/IndexedDBSource.js +67 -53
  135. package/dist/core/source/MemorySource.js +5 -3
  136. package/dist/core/source/OverlaySource.d.ts +5 -2
  137. package/dist/core/source/OverlaySource.js +31 -3
  138. package/dist/core/source/Source.d.ts +3 -0
  139. package/dist/core/source/Source.js +15 -2
  140. package/dist/core/source/Tree.d.ts +10 -7
  141. package/dist/core/source/Tree.js +118 -70
  142. package/dist/core/source/Utils.js +6 -3
  143. package/dist/core/util/Arrays.d.ts +1 -0
  144. package/dist/core/util/Arrays.js +10 -0
  145. package/dist/core/util/Async.d.ts +7 -0
  146. package/dist/core/util/Async.js +15 -0
  147. package/dist/core/util/Checks.js +1 -1
  148. package/dist/core/util/EntryFilenames.d.ts +22 -1
  149. package/dist/core/util/EntryFilenames.js +56 -2
  150. package/dist/core/util/IndexedDB.d.ts +4 -0
  151. package/dist/core/util/IndexedDB.js +20 -0
  152. package/dist/core/util/Paths.js +5 -1
  153. package/dist/dashboard/App.js +3 -3
  154. package/dist/dashboard/ViewKeys.d.ts +1 -0
  155. package/dist/dashboard/ViewKeys.js +1 -0
  156. package/dist/dashboard/app/AccessDenied.js +2 -2
  157. package/dist/dashboard/app/ActivityStatus.js +14 -14
  158. package/dist/dashboard/app/AppShell.js +2 -2
  159. package/dist/dashboard/app/AuthView.js +12 -12
  160. package/dist/dashboard/app/CompactField.js +11 -11
  161. package/dist/dashboard/app/DashboardErrorBoundary.js +6 -6
  162. package/dist/dashboard/app/DashboardLayout.js +3 -3
  163. package/dist/dashboard/app/EntryHeader.js +8 -8
  164. package/dist/dashboard/app/EntryReferences.js +8 -8
  165. package/dist/dashboard/app/EntrySidebar.js +15 -15
  166. package/dist/dashboard/app/EntrySidebarPreview.js +7 -7
  167. package/dist/dashboard/app/EntryTranslationBanner.js +5 -5
  168. package/dist/dashboard/app/Explorer.js +39 -39
  169. package/dist/dashboard/app/ExplorerCards.js +31 -31
  170. package/dist/dashboard/app/ExplorerFileCard.js +19 -19
  171. package/dist/dashboard/app/ExplorerList.js +10 -10
  172. package/dist/dashboard/app/ExplorerModal.js +5 -5
  173. package/dist/dashboard/app/ExplorerTable.js +47 -47
  174. package/dist/dashboard/app/LinkPicker.js +5 -5
  175. package/dist/dashboard/app/LocaleMenu.js +4 -4
  176. package/dist/dashboard/app/SidebarTree.js +76 -40
  177. package/dist/dashboard/app/WorkspaceMenu.js +9 -9
  178. package/dist/dashboard/app/WorkspaceRoots.js +22 -22
  179. package/dist/dashboard/app/editor/FileEditor.js +28 -28
  180. package/dist/dashboard/app/editor/FilePreview.js +6 -6
  181. package/dist/dashboard/app/modals/CreateEntry.js +3 -3
  182. package/dist/dashboard/app/pages/EntryPage.js +18 -18
  183. package/dist/dashboard/app/pages/RootPage.js +3 -3
  184. package/dist/dashboard/app/pages/SplashPage.js +45 -45
  185. package/dist/dashboard/app/pages/UsersPage.js +34 -34
  186. package/dist/dashboard/app/ui/DashboardModal.js +18 -18
  187. package/dist/dashboard/app/ui/Rail.js +5 -5
  188. package/dist/dashboard/app/ui/Sidebar.js +2 -2
  189. package/dist/dashboard/atoms/config.d.ts +4 -4
  190. package/dist/dashboard/atoms/entry.d.ts +8 -9
  191. package/dist/dashboard/atoms/entry.js +34 -4
  192. package/dist/dashboard/atoms/explorer.d.ts +2 -0
  193. package/dist/dashboard/atoms/explorer.js +7 -2
  194. package/dist/dashboard/boot/Boot.js +2 -3
  195. package/dist/dashboard/boot/DashboardWorker.d.ts +3 -3
  196. package/dist/dashboard/boot/DashboardWorker.js +82 -27
  197. package/dist/dashboard/boot/LoadWorker.js +2 -3
  198. package/dist/dashboard/boot/WorkerDB.d.ts +2 -2
  199. package/dist/dashboard/icons.d.ts +2 -0
  200. package/dist/dashboard/icons.js +40 -0
  201. package/dist/dashboard/plugins/alineaFixturePlugin.js +14 -3
  202. package/dist/database/BrowserEntryStore.d.ts +42 -0
  203. package/dist/database/BrowserEntryStore.js +182 -0
  204. package/dist/database/DatabaseSource.d.ts +13 -0
  205. package/dist/database/DatabaseSource.js +25 -0
  206. package/dist/database/DatabaseTables.d.ts +38 -0
  207. package/dist/database/DatabaseTables.js +53 -0
  208. package/dist/database/EntryDatabase.d.ts +97 -0
  209. package/dist/database/EntryDatabase.js +528 -0
  210. package/dist/database/EntryStore.d.ts +58 -0
  211. package/dist/database/EntryStore.js +230 -0
  212. package/dist/database/EntryTransaction.d.ts +33 -0
  213. package/dist/database/EntryTransaction.js +907 -0
  214. package/dist/database/EntryUrlAliases.d.ts +2 -0
  215. package/dist/database/EntryUrlAliases.js +69 -0
  216. package/dist/database/LocalDB.d.ts +39 -0
  217. package/dist/database/LocalDB.js +79 -0
  218. package/dist/database/Version.d.ts +9 -0
  219. package/dist/database/Version.js +13 -0
  220. package/dist/database/driver/DatabaseHandle.d.ts +17 -0
  221. package/dist/database/driver/DatabaseHandle.js +0 -0
  222. package/dist/database/driver/OverlayExtension.browser.d.ts +2 -0
  223. package/dist/database/driver/OverlayExtension.browser.js +9 -0
  224. package/dist/database/driver/OverlayExtension.d.ts +1 -0
  225. package/dist/database/driver/OverlayExtension.js +7 -0
  226. package/dist/database/driver/RuntimeDatabase.d.ts +13 -0
  227. package/dist/database/driver/RuntimeDatabase.js +77 -0
  228. package/dist/database/driver/WasmDatabase.d.ts +29 -0
  229. package/dist/database/driver/WasmDatabase.js +46 -0
  230. package/dist/database/entry/EntryData.d.ts +13 -0
  231. package/dist/database/entry/EntryData.js +51 -0
  232. package/dist/database/entry/EntryTable.d.ts +189 -0
  233. package/dist/database/entry/EntryTable.js +186 -0
  234. package/dist/database/query/Condition.d.ts +8 -0
  235. package/dist/database/query/Condition.js +117 -0
  236. package/dist/database/query/EntryQuery.d.ts +44 -0
  237. package/dist/database/query/EntryQuery.js +412 -0
  238. package/dist/database/query/EntryReferences.d.ts +5 -0
  239. package/dist/database/query/EntryReferences.js +70 -0
  240. package/dist/database/query/Relation.d.ts +11 -0
  241. package/dist/database/query/Relation.js +113 -0
  242. package/dist/database/query/ResolveQuery.d.ts +12 -0
  243. package/dist/database/query/ResolveQuery.js +189 -0
  244. package/dist/database/query/Search.d.ts +38 -0
  245. package/dist/database/query/Search.js +149 -0
  246. package/dist/database/sync/Derive.d.ts +8 -0
  247. package/dist/database/sync/Derive.js +270 -0
  248. package/dist/database/sync/EntryParser.d.ts +8 -0
  249. package/dist/database/sync/EntryParser.js +90 -0
  250. package/dist/database/sync/EntrySyncer.d.ts +25 -0
  251. package/dist/database/sync/EntrySyncer.js +89 -0
  252. package/dist/database/sync/Ingest.d.ts +21 -0
  253. package/dist/database/sync/Ingest.js +145 -0
  254. package/dist/database/sync/SyncQueries.d.ts +160 -0
  255. package/dist/database/sync/SyncQueries.js +216 -0
  256. package/dist/field/check/CheckField.view.js +2 -2
  257. package/dist/field/code/CodeField.view.js +10 -10
  258. package/dist/field/link/LinkField.js +23 -0
  259. package/dist/field/link/LinkField.view.js +42 -41
  260. package/dist/field/list/ListField.view.js +172 -150
  261. package/dist/field/metadata/MetadataField.d.ts +28 -3
  262. package/dist/field/metadata/MetadataField.js +44 -7
  263. package/dist/field/metadata/MetadataField.view.d.ts +5 -0
  264. package/dist/field/metadata/MetadataField.view.js +31 -16
  265. package/dist/field/richtext/RichTextDocument.js +4 -3
  266. package/dist/field/richtext/RichTextExtensions.d.ts +1 -1
  267. package/dist/field/richtext/RichTextExtensions.js +33 -29
  268. package/dist/field/richtext/RichTextField.view.js +17 -8
  269. package/dist/field/richtext/RichTextInsertMenu.js +2 -2
  270. package/dist/field/views.js +2 -0
  271. package/dist/next.cjs +6 -20
  272. package/dist/next.d.ts +4 -3
  273. package/dist/next.edge.d.ts +1 -1
  274. package/dist/next.js +1 -2
  275. package/dist/picker/entry/EntryPicker.js +2 -1
  276. package/dist/picker/url/UrlPicker.js +1 -0
  277. package/dist/preview/widget.d.ts +45 -0
  278. package/dist/preview/widget.js +96 -1
  279. package/dist/query.d.ts +28 -0
  280. package/package.json +6 -1
  281. package/dist/adapter/next/DevRemote.d.ts +0 -3
  282. package/dist/adapter/next/DevRemote.js +0 -45
  283. package/dist/backend/store/GeneratedSource.d.ts +0 -2
  284. package/dist/backend/store/GeneratedSource.js +0 -21
  285. package/dist/chunks/chunk-G6E35DMJ.js +0 -1997
  286. package/dist/core/db/EntryDB.d.ts +0 -19
  287. package/dist/core/db/EntryDB.js +0 -48
  288. package/dist/core/db/EntryIndex.d.ts +0 -143
  289. package/dist/core/db/EntryIndex.js +0 -2773
  290. package/dist/core/db/EntryReferenceIndex.d.ts +0 -21
  291. package/dist/core/db/EntryReferenceIndex.js +0 -131
  292. package/dist/core/db/EntryResolver.d.ts +0 -56
  293. package/dist/core/db/EntryResolver.js +0 -699
  294. package/dist/core/db/EntryTarget.d.ts +0 -5
  295. package/dist/core/db/EntryTransaction.d.ts +0 -31
  296. package/dist/core/db/EntryTransaction.js +0 -956
  297. package/dist/core/db/LocalDB.d.ts +0 -42
  298. package/dist/core/db/LocalDB.js +0 -127
  299. package/dist/core/db/TestDB.d.ts +0 -19
  300. package/dist/core/db/TestDB.js +0 -50
  301. /package/dist/core/db/{EntryTarget.js → LocalStore.js} +0 -0
@@ -10,43 +10,90 @@ import {
10
10
  import {
11
11
  createHandler as createCoreHandler
12
12
  } from "alinea/backend/Handler";
13
- import { generatedSource } from "alinea/backend/store/GeneratedSource";
13
+ import { HandleAction } from "alinea/backend/HandleAction";
14
14
  import { JWTPreviews } from "alinea/backend/util/JWTPreviews";
15
15
  import { CloudRemote } from "alinea/cloud/CloudRemote";
16
+ import { Client } from "alinea/core/Client";
16
17
  import { Config } from "alinea/core/Config";
17
- import { LocalDB } from "alinea/core/db/LocalDB";
18
+ import {
19
+ developmentKeyHeader,
20
+ forwardedMutationHeader
21
+ } from "alinea/core/Connection";
22
+ import { HttpError } from "alinea/core/HttpError";
18
23
  import { trace } from "alinea/core/Trace";
19
24
  import { NextCMS } from "./cms.js";
20
25
  import { requestContext } from "./context.js";
21
- import { createDevRemote } from "./DevRemote.js";
22
26
  import { revalidateContentSha } from "./syncCheck.js";
23
- function createHandler(input) {
27
+ function createHandlerWithDatabase(input, openGeneratedDatabase) {
24
28
  const options = input instanceof NextCMS ? { cms: input } : input;
25
29
  const config = options.cms.config;
26
30
  const backend = typeof options.backend === "function" ? options.backend : options.backend ? backendFromOptions(options.backend) : (context) => new CloudRemote(context, config);
27
- const remote = (context) => context.isDev ? createDevRemote(context, config) : backend(context, config);
31
+ const remote = (context) => backend(context, config);
28
32
  const span = trace(config, "alinea.next.handler.db");
29
33
  const db = PLazy.from(
30
34
  () => span(async () => {
31
- const source = await generatedSource;
32
- const db2 = new LocalDB(config, source);
33
- await db2.sync();
34
- return db2;
35
+ if (process.env.NEXT_RUNTIME === "edge")
36
+ throw new Error(
37
+ "The Alinea handler is not supported in Edge runtime environments."
38
+ );
39
+ if (!openGeneratedDatabase)
40
+ throw new Error(
41
+ "A generated database loader is required. Import createHandler from 'alinea/next'."
42
+ );
43
+ return openGeneratedDatabase(config);
35
44
  })
36
45
  );
46
+ const afterCommit = async (context) => {
47
+ await revalidateContentSha();
48
+ await options.afterCommit?.(context);
49
+ };
37
50
  const handleBackend = createCoreHandler({
38
51
  ...options,
39
52
  remote,
40
53
  db,
41
- // Revalidate the shared content sha before the user-provided hook so
42
- // page revalidation triggered there already sees the fresh sha when
43
- // RSC renders resolve. Invalidation never throws (failures are
44
- // swallowed inside revalidateContentSha).
45
- afterCommit: async (context) => {
46
- await revalidateContentSha();
47
- await options.afterCommit?.(context);
48
- }
54
+ afterCommit
49
55
  });
56
+ async function handleDevelopment(request, context) {
57
+ const action = new URL(request.url).searchParams.get("action");
58
+ if (action !== HandleAction.Mutate || request.method !== "POST")
59
+ return failure(404, "Answered by the Alinea dev server");
60
+ const developmentKey = request.headers.get(developmentKeyHeader);
61
+ if (!context.apiKey || developmentKey !== context.apiKey)
62
+ return failure(401, "Invalid development credentials");
63
+ let mutations;
64
+ try {
65
+ mutations = await request.json();
66
+ } catch {
67
+ return failure(400, "Expected JSON");
68
+ }
69
+ if (!Array.isArray(mutations)) return failure(400, "Expected mutations");
70
+ const adjusted = await options.beforeCommit?.({ mutations });
71
+ if (adjusted) mutations = [...adjusted];
72
+ const devServer = new Client({
73
+ config,
74
+ url: context.handlerUrl.href,
75
+ applyAuth(init) {
76
+ const authorized = context.applyAuth?.(init) ?? init ?? {};
77
+ const headers = new Headers(authorized.headers);
78
+ headers.set(forwardedMutationHeader, "true");
79
+ return { ...authorized, headers };
80
+ }
81
+ });
82
+ let sha;
83
+ try {
84
+ ;
85
+ ({ sha } = await devServer.mutate(mutations));
86
+ } catch (error) {
87
+ if (error instanceof HttpError) return failure(error.code, error.message);
88
+ throw error;
89
+ }
90
+ try {
91
+ await afterCommit({ mutations, sha });
92
+ } catch (error) {
93
+ console.error("Alinea afterCommit hook failed", error);
94
+ }
95
+ return Response.json({ sha });
96
+ }
50
97
  const handle = async (request) => {
51
98
  const url = new URL(request.url);
52
99
  const context = await requestContext(config, request);
@@ -58,6 +105,11 @@ function createHandler(input) {
58
105
  backendUrl.pathname = handlerPath;
59
106
  backendUrl.searchParams.set("file", rewrittenFile);
60
107
  backendUrl.searchParams.set("delivery", "proxy");
108
+ if (context.isDev) {
109
+ const devUrl = new URL(context.handlerUrl);
110
+ devUrl.search = backendUrl.search;
111
+ return Response.redirect(devUrl, 307);
112
+ }
61
113
  return await handleBackend(new Request(backendUrl, request), context);
62
114
  }
63
115
  if (url.pathname !== handlerPath)
@@ -85,6 +137,7 @@ function createHandler(input) {
85
137
  headers: { location: String(location) }
86
138
  });
87
139
  }
140
+ if (context.isDev) return await handleDevelopment(request, context);
88
141
  return await handleBackend(request, context);
89
142
  } catch (error) {
90
143
  console.error(error);
@@ -93,6 +146,9 @@ function createHandler(input) {
93
146
  };
94
147
  return handle;
95
148
  }
149
+ function failure(status, error) {
150
+ return Response.json({ success: false, error }, { status });
151
+ }
96
152
  function handlerPathname(config, requestUrl) {
97
153
  return new URL(Config.handlerUrl(config), requestUrl).pathname;
98
154
  }
@@ -108,7 +164,11 @@ function rewrittenFilePath(config, request) {
108
164
  return;
109
165
  }
110
166
  }
167
+ function createHandler(input) {
168
+ return createHandlerWithDatabase(input);
169
+ }
111
170
  export {
112
171
  createHandler,
172
+ createHandlerWithDatabase,
113
173
  handlerPathname
114
174
  };
@@ -0,0 +1,5 @@
1
+ import type { Config } from 'alinea/core/Config';
2
+ import { NextCMS } from './cms.js';
3
+ import { type NextHandlerOptions } from './handler.js';
4
+ export declare function createCMS<Definition extends Config>(config: Definition): NextCMS<Definition>;
5
+ export declare function createHandler(input: NextCMS | NextHandlerOptions): (request: Request) => Promise<Response>;
@@ -0,0 +1,16 @@
1
+ import "../../chunks/chunk-CQ52GEHY.js";
2
+
3
+ // src/adapter/next/node.ts
4
+ import { generatedDatabase } from "alinea/backend/store/GeneratedDatabaseNode";
5
+ import { NextCMS } from "./cms.js";
6
+ import { createHandlerWithDatabase } from "./handler.js";
7
+ function createCMS(config) {
8
+ return new NextCMS(config, generatedDatabase);
9
+ }
10
+ function createHandler(input) {
11
+ return createHandlerWithDatabase(input, generatedDatabase);
12
+ }
13
+ export {
14
+ createCMS,
15
+ createHandler
16
+ };
@@ -1,10 +1,13 @@
1
+ import { type PreviewStats } from 'alinea/preview/widget';
1
2
  export interface NextPreviewsProps {
2
3
  dashboardUrl: string;
3
4
  widget?: boolean;
5
+ /** Resolves once the render's CMS queries settled. */
6
+ stats?: Promise<PreviewStats>;
4
7
  root?: string;
5
8
  workspace?: string;
6
9
  }
7
- export default function NextPreviews({ dashboardUrl, widget, root, workspace }: NextPreviewsProps): import("react").JSX.Element | null;
10
+ export default function NextPreviews({ dashboardUrl, widget, stats, root, workspace }: NextPreviewsProps): import("react").JSX.Element | null;
8
11
  /**
9
12
  * Wrapper around `router.refresh()` from `next/navigation` `useRouter()` to return Promise, and resolve after refresh completed
10
13
  * @returns Refresh function
@@ -11,12 +11,14 @@ import { jsx } from "react/jsx-runtime";
11
11
  function NextPreviews({
12
12
  dashboardUrl,
13
13
  widget,
14
+ stats,
14
15
  root,
15
16
  workspace
16
17
  }) {
17
18
  const refresh = useRouterRefresh();
18
19
  const [isLoading, setIsLoading] = useState(false);
19
20
  const [previewDisabled, setPreviewDisabled] = useState(false);
21
+ const [renderStats, setRenderStats] = useState();
20
22
  const pathname = usePathname();
21
23
  const adminUrl = new URL(dashboardUrl, location.origin);
22
24
  const host = window.parent !== window ? window.parent : window.opener;
@@ -43,13 +45,23 @@ function NextPreviews({
43
45
  useEffect(() => {
44
46
  if (widget) registerPreviewWidget();
45
47
  }, [widget]);
48
+ useEffect(() => {
49
+ let current = true;
50
+ stats?.then((value) => {
51
+ if (current) setRenderStats(JSON.stringify(value));
52
+ });
53
+ return () => {
54
+ current = false;
55
+ };
56
+ }, [stats]);
46
57
  if (!widget) return null;
47
58
  return /* @__PURE__ */ jsx(
48
59
  "alinea-preview",
49
60
  {
50
61
  adminUrl: String(adminUrl),
51
62
  editUrl: String(editUrl),
52
- livePreview: isPreviewing ? isLoading ? "loading" : previewDisabled ? "warning" : "connected" : void 0
63
+ livePreview: isPreviewing ? isLoading ? "loading" : previewDisabled ? "warning" : "connected" : void 0,
64
+ stats: renderStats
53
65
  }
54
66
  );
55
67
  }
@@ -0,0 +1,45 @@
1
+ import type { GraphQuery } from 'alinea/core/Graph';
2
+ import type { PreviewDatabase, PreviewStat, PreviewStatement, PreviewStats } from 'alinea/preview/widget';
3
+ /** Collects the CMS queries and syncs of one draft render. */
4
+ export declare class RenderStats {
5
+ #private;
6
+ rows: Array<PreviewStat>;
7
+ statements: number;
8
+ sqlMs: number;
9
+ slowest: Array<PreviewStatement>;
10
+ /** The bundled database that answered, once a query reached it. */
11
+ database?: PreviewDatabase;
12
+ readonly startedAt: number;
13
+ /** Add a row and count it as pending until `run` settles; see `timed`. */
14
+ track<T>(stat: Omit<PreviewStat, 'durationMs'>, run: (row: PreviewStat) => Promise<T>): Promise<T>;
15
+ /**
16
+ * Time a database query without the time it waited behind the others:
17
+ * queries of a render run one at a time in the order they were called, so
18
+ * one starts executing once the previous one finished, or when it was
19
+ * called if that is later.
20
+ */
21
+ executed<T>(row: PreviewStat, run: () => Promise<T>): Promise<T>;
22
+ /**
23
+ * Count a statement towards the render, and towards the row that ran it:
24
+ * a sync in progress, or else the query executing, which is the earliest
25
+ * one called that has not finished (see `executed`).
26
+ */
27
+ statement(durationMs: number, sql?: string): void;
28
+ /**
29
+ * Resolves once nothing ran for `quietMs`, so queries of server components
30
+ * that render after the caller are counted, or after `maxMs` regardless.
31
+ */
32
+ settled(quietMs?: number, maxMs?: number): Promise<PreviewStats>;
33
+ }
34
+ /**
35
+ * A statement short enough to read in a table, such as
36
+ * `select … from alinea_entry_index where (visible = ? and id in (?))`.
37
+ */
38
+ export declare function summarizeSql(sql: string, maxLength?: number): string;
39
+ /**
40
+ * Time the work a row stands for from call to answer, such as a sync or a
41
+ * query the handler answers, without the sync it waited for.
42
+ */
43
+ export declare function timed<T>(row: PreviewStat | undefined, run: () => Promise<T>): Promise<T>;
44
+ /** A short description such as `first(type, url=/about)`. */
45
+ export declare function summarizeQuery(query: GraphQuery): string;
@@ -0,0 +1,142 @@
1
+ import "../../chunks/chunk-CQ52GEHY.js";
2
+
3
+ // src/adapter/next/renderStats.ts
4
+ var slowestCount = 5;
5
+ var RenderStats = class {
6
+ rows = [];
7
+ statements = 0;
8
+ sqlMs = 0;
9
+ slowest = [];
10
+ /** The bundled database that answered, once a query reached it. */
11
+ database;
12
+ startedAt = Date.now();
13
+ #pending = 0;
14
+ #idleSince = Date.now();
15
+ #renderStart = Number.POSITIVE_INFINITY;
16
+ #renderEnd = Number.NEGATIVE_INFINITY;
17
+ #executedUntil = Number.NEGATIVE_INFINITY;
18
+ /** Syncs in progress: their statements run before any query's. */
19
+ #syncing = [];
20
+ /** Queries executing or waiting to, in the order they run. */
21
+ #executing = [];
22
+ /** Add a row and count it as pending until `run` settles; see `timed`. */
23
+ async track(stat, run) {
24
+ const row = { ...stat, durationMs: 0 };
25
+ this.rows.push(row);
26
+ this.#pending++;
27
+ this.#renderStart = Math.min(this.#renderStart, performance.now());
28
+ if (row.kind === "sync") this.#syncing.push(row);
29
+ try {
30
+ return await run(row);
31
+ } finally {
32
+ if (row.kind === "sync") remove(this.#syncing, row);
33
+ this.#pending--;
34
+ this.#idleSince = Date.now();
35
+ this.#renderEnd = Math.max(this.#renderEnd, performance.now());
36
+ }
37
+ }
38
+ /**
39
+ * Time a database query without the time it waited behind the others:
40
+ * queries of a render run one at a time in the order they were called, so
41
+ * one starts executing once the previous one finished, or when it was
42
+ * called if that is later.
43
+ */
44
+ async executed(row, run) {
45
+ const start = performance.now();
46
+ this.#executing.push(row);
47
+ try {
48
+ return await run();
49
+ } finally {
50
+ remove(this.#executing, row);
51
+ const end = performance.now();
52
+ row.durationMs = end - Math.max(start, this.#executedUntil);
53
+ this.#executedUntil = end;
54
+ }
55
+ }
56
+ /**
57
+ * Count a statement towards the render, and towards the row that ran it:
58
+ * a sync in progress, or else the query executing, which is the earliest
59
+ * one called that has not finished (see `executed`).
60
+ */
61
+ statement(durationMs, sql) {
62
+ this.statements++;
63
+ this.sqlMs += durationMs;
64
+ const row = this.#syncing.at(-1) ?? this.#executing[0];
65
+ if (row) {
66
+ row.statements = (row.statements ?? 0) + 1;
67
+ row.sqlMs = (row.sqlMs ?? 0) + durationMs;
68
+ }
69
+ if (sql === void 0) return;
70
+ const { slowest } = this;
71
+ if (slowest.length >= slowestCount && durationMs <= slowest[slowest.length - 1].durationMs)
72
+ return;
73
+ slowest.push({ sql: summarizeSql(sql), durationMs, query: row?.summary });
74
+ slowest.sort((a, b) => b.durationMs - a.durationMs);
75
+ slowest.length = Math.min(slowest.length, slowestCount);
76
+ }
77
+ /**
78
+ * Resolves once nothing ran for `quietMs`, so queries of server components
79
+ * that render after the caller are counted, or after `maxMs` regardless.
80
+ */
81
+ async settled(quietMs = 50, maxMs = 5e3) {
82
+ const deadline = Date.now() + maxMs;
83
+ for (let now = Date.now(); now < deadline; now = Date.now()) {
84
+ const wait = this.#pending > 0 ? quietMs : this.#idleSince + quietMs - now;
85
+ if (wait <= 0) break;
86
+ await new Promise(
87
+ (resolve) => setTimeout(resolve, Math.min(wait, deadline - now))
88
+ );
89
+ }
90
+ const { rows, statements, sqlMs, slowest, database } = this;
91
+ const renderMs = Math.max(0, this.#renderEnd - this.#renderStart);
92
+ return {
93
+ rows: rows.map((row) => ({ ...row })),
94
+ statements,
95
+ sqlMs,
96
+ renderMs,
97
+ slowest: [...slowest],
98
+ database
99
+ };
100
+ }
101
+ };
102
+ function summarizeSql(sql, maxLength = 200) {
103
+ const text = sql.replace(/\s+/g, " ").replaceAll('"', "").replace(/^select .*? from /i, "select \u2026 from ").replace(/\b\w+\.(\w+)/g, "$1").trim();
104
+ return text.length > maxLength ? `${text.slice(0, maxLength - 1)}\u2026` : text;
105
+ }
106
+ async function timed(row, run) {
107
+ if (!row) return run();
108
+ const start = performance.now();
109
+ try {
110
+ return await run();
111
+ } finally {
112
+ row.durationMs += performance.now() - start;
113
+ }
114
+ }
115
+ var unlisted = /* @__PURE__ */ new Set([
116
+ "first",
117
+ "get",
118
+ "count",
119
+ "select",
120
+ "include",
121
+ "status",
122
+ "preview",
123
+ "syncInterval",
124
+ "disableSync"
125
+ ]);
126
+ function summarizeQuery(query) {
127
+ const mode = query.count ? "count" : query.get ? "get" : query.first ? "first" : "find";
128
+ const parts = Object.entries(query).filter(([key, value]) => !unlisted.has(key) && value !== void 0).map(
129
+ ([key, value]) => typeof value === "string" || typeof value === "number" ? `${key}=${value}` : key
130
+ );
131
+ return `${mode}(${parts.join(", ")})`;
132
+ }
133
+ function remove(items, item) {
134
+ const index = items.indexOf(item);
135
+ if (index !== -1) items.splice(index, 1);
136
+ }
137
+ export {
138
+ RenderStats,
139
+ summarizeQuery,
140
+ summarizeSql,
141
+ timed
142
+ };
@@ -1,5 +1,10 @@
1
1
  import type { Client } from 'alinea/core/Client';
2
- import type { LocalDB } from 'alinea/core/db/LocalDB';
2
+ import type { SyncOptions } from 'alinea/core/db/LocalStore';
3
+ import type { RemoteSource } from 'alinea/core/source/Source';
4
+ interface SyncableDB {
5
+ readonly sha: string | Promise<string>;
6
+ syncWith(source: RemoteSource, options?: SyncOptions): Promise<string>;
7
+ }
3
8
  export declare const CONTENT_SHA_TAG = "alinea-content-sha";
4
9
  export declare function revalidateContentSha(): Promise<void>;
5
10
  /**
@@ -9,4 +14,5 @@ export declare function revalidateContentSha(): Promise<void>;
9
14
  * Returns false when the sha could not be determined, so the caller falls
10
15
  * back to its existing throttle behavior.
11
16
  */
12
- export declare function syncIfStale(db: LocalDB, client: Client, syncInterval?: number): Promise<boolean>;
17
+ export declare function syncIfStale(db: SyncableDB, client: Client, syncInterval?: number, options?: SyncOptions): Promise<boolean>;
18
+ export {};
@@ -1,10 +1,8 @@
1
1
  import "../../chunks/chunk-CQ52GEHY.js";
2
2
 
3
3
  // src/adapter/next/syncCheck.ts
4
- import { ReadonlyTree } from "alinea/core/source/Tree";
5
4
  var CONTENT_SHA_TAG = "alinea-content-sha";
6
5
  var SHA_REVALIDATE_SECONDS = 60;
7
- var SENTINEL_SHA = ReadonlyTree.EMPTY.sha;
8
6
  var inflight;
9
7
  function latestSha(client) {
10
8
  if (inflight) return inflight;
@@ -17,18 +15,14 @@ async function loadLatestSha(client) {
17
15
  try {
18
16
  const { unstable_cache } = await import("next/cache.js");
19
17
  const getSha = unstable_cache(
20
- async () => {
21
- const tree = await client.getTreeIfDifferent(SENTINEL_SHA);
22
- return tree?.sha;
23
- },
18
+ () => client.getSha(),
24
19
  ["alinea-content-sha"],
25
20
  { revalidate: SHA_REVALIDATE_SECONDS, tags: [CONTENT_SHA_TAG] }
26
21
  );
27
22
  return await getSha();
28
23
  } catch {
29
24
  try {
30
- const tree = await client.getTreeIfDifferent(SENTINEL_SHA);
31
- return tree?.sha;
25
+ return await client.getSha();
32
26
  } catch {
33
27
  return void 0;
34
28
  }
@@ -41,15 +35,15 @@ async function revalidateContentSha() {
41
35
  } catch {
42
36
  }
43
37
  }
44
- async function syncIfStale(db, client, syncInterval) {
38
+ async function syncIfStale(db, client, syncInterval, options) {
45
39
  if (syncInterval === Number.POSITIVE_INFINITY) return true;
46
40
  if (syncInterval === 0) {
47
- await db.syncWith(client);
41
+ await db.syncWith(client, options);
48
42
  return true;
49
43
  }
50
44
  const expected = await latestSha(client);
51
45
  if (!expected) return false;
52
- if (expected !== db.sha) await db.syncWith(client);
46
+ if (expected !== await db.sha) await db.syncWith(client, options);
53
47
  return true;
54
48
  }
55
49
  export {
@@ -27,7 +27,6 @@ function withAlinea(config = {}) {
27
27
  } catch {
28
28
  console.warn("Alinea could not determine Next.js version, assuming 15+");
29
29
  }
30
- const redirects = adminPath ? createRedirects(config, adminPath) : config.redirects;
31
30
  const rewrites = adminPath ? createRewrites(config, adminPath, settings.handlerUrl) : config.rewrites;
32
31
  const images = adminPath ? createImages(config, adminPath) : config.images;
33
32
  const env = settings ? {
@@ -42,10 +41,10 @@ function withAlinea(config = {}) {
42
41
  ...config.experimental,
43
42
  serverComponentsExternalPackages: [
44
43
  ...config.experimental?.serverComponentsExternalPackages ?? [],
45
- "@alinea/generated"
44
+ "@alinea/generated",
45
+ "@alinea/sqlite-wasm"
46
46
  ]
47
47
  },
48
- redirects,
49
48
  rewrites,
50
49
  images,
51
50
  env
@@ -54,9 +53,11 @@ function withAlinea(config = {}) {
54
53
  ...config,
55
54
  serverExternalPackages: [
56
55
  ...config.serverExternalPackages ?? [],
57
- "@alinea/generated"
56
+ "@alinea/generated",
57
+ // Loads its native extension from the package directory, which file
58
+ // tracing then includes.
59
+ "@alinea/sqlite-wasm"
58
60
  ],
59
- redirects,
60
61
  rewrites,
61
62
  images,
62
63
  env
@@ -82,21 +83,6 @@ function createImages(config, adminPath) {
82
83
  ]
83
84
  };
84
85
  }
85
- function createRedirects(config, adminPath) {
86
- const dev = process.env.ALINEA_DEV_SERVER;
87
- if (!dev) return config.redirects;
88
- return async () => {
89
- const existing = config.redirects ? await config.redirects() : [];
90
- return [
91
- ...existing,
92
- {
93
- permanent: true,
94
- source: `${adminPath}/~dev`,
95
- destination: `${dev}/~dev`
96
- }
97
- ];
98
- };
99
- }
100
86
  var emptyRewrites = {
101
87
  beforeFiles: [],
102
88
  afterFiles: [],
@@ -11,6 +11,6 @@ export declare enum HandleAction {
11
11
  Mutate = "mutate",
12
12
  Upload = "upload",
13
13
  Tree = "tree",
14
- Commit = "commit",
14
+ Sha = "sha",
15
15
  Blob = "blob"
16
16
  }
@@ -14,7 +14,7 @@ var HandleAction = /* @__PURE__ */ ((HandleAction2) => {
14
14
  HandleAction2["Mutate"] = "mutate";
15
15
  HandleAction2["Upload"] = "upload";
16
16
  HandleAction2["Tree"] = "tree";
17
- HandleAction2["Commit"] = "commit";
17
+ HandleAction2["Sha"] = "sha";
18
18
  HandleAction2["Blob"] = "blob";
19
19
  return HandleAction2;
20
20
  })(HandleAction || {});
@@ -1,6 +1,6 @@
1
1
  import type { CMS } from 'alinea/core/CMS';
2
2
  import type { AuthedContext, RemoteConnection, RequestContext } from 'alinea/core/Connection';
3
- import { LocalDB } from 'alinea/core/db/LocalDB';
3
+ import type { LocalStore } from 'alinea/core/db/LocalStore';
4
4
  import type { Mutation } from 'alinea/core/db/Mutation';
5
5
  export interface Handler {
6
6
  (request: Request, context: RequestContext): Promise<Response>;
@@ -18,7 +18,7 @@ export interface HandlerHooks {
18
18
  }
19
19
  export interface HandlerOptions extends HandlerHooks {
20
20
  cms: CMS;
21
- db: LocalDB | Promise<LocalDB>;
21
+ db: LocalStore | Promise<LocalStore>;
22
22
  remote?: (context: RequestContext) => RemoteConnection;
23
23
  forwardMutations?(request: Request, context: AuthedContext): Promise<Response | undefined>;
24
24
  }