alinea 2.0.0-preview.4 → 2.0.0-preview.5
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/dist/.init/content/pages/welcome.json +1 -1
- package/dist/.init/src/cms.ts +1 -4
- package/dist/LICENSES.md +107 -107
- package/dist/adapter/next/cms.js +5 -1
- package/dist/adapter/next/handler.js +35 -6
- package/dist/adapter/next/previews.js +7 -1
- package/dist/adapter/next/syncCheck.d.ts +12 -0
- package/dist/adapter/next/syncCheck.js +59 -0
- package/dist/adapter/next/with-alinea.d.ts +0 -8
- package/dist/adapter/next/with-alinea.js +43 -19
- package/dist/adapter/vite/plugin.js +9 -4
- package/dist/backend/Handler.js +106 -0
- package/dist/backend/api/ProxyMedia.d.ts +3 -0
- package/dist/backend/api/ProxyMedia.js +39 -0
- package/dist/backend/api/S3Uploads.d.ts +1 -1
- package/dist/backend/router/Router.js +2 -0
- package/dist/backend/util/Syncable.js +1 -1
- package/dist/bundled.d.ts +164 -70
- package/dist/chunks/{chunk-XMOVGDFI.js → chunk-A6M3PNJT.js} +1 -1
- package/dist/cli/Generate.js +1 -3
- package/dist/cli/Serve.js +3 -2
- package/dist/cli/bin.js +1 -1
- package/dist/cli/generate/GenerateDashboard.js +1 -0
- package/dist/cli/serve/CreateLocalServer.d.ts +1 -1
- package/dist/cli/serve/CreateLocalServer.js +42 -34
- package/dist/cli/static/init/cms.js +1 -4
- package/dist/cli/util/ContentType.d.ts +1 -0
- package/dist/cli/util/ContentType.js +36 -0
- package/dist/cloud/CloudHandshake.d.ts +6 -0
- package/dist/cloud/CloudHandshake.js +67 -10
- package/dist/cloud/CloudRemote.js +24 -11
- package/dist/components/Calendar.js +4 -4
- package/dist/components/Checkbox.js +3 -3
- package/dist/components/ColorSwatchPicker.js +2 -2
- package/dist/components/ComboBox.js +9 -9
- package/dist/components/DateField.js +4 -4
- package/dist/components/DatePicker.js +3 -3
- package/dist/components/DateRangePicker.js +8 -8
- package/dist/components/Disclosure.js +7 -7
- package/dist/components/Label.js +4 -4
- package/dist/components/List.d.ts +1 -1
- package/dist/components/List.js +35 -34
- package/dist/components/Menu.js +4 -4
- package/dist/components/Modal.js +2 -2
- package/dist/components/MultipleSelect.js +23 -23
- package/dist/components/NumberField.js +6 -6
- package/dist/components/ProgressCircle.js +1 -1
- package/dist/components/SearchField.js +7 -7
- package/dist/components/Select.js +9 -9
- package/dist/components/Surface.js +2 -2
- package/dist/components/Table.js +7 -7
- package/dist/components/Tabs.js +2 -2
- package/dist/components/TagGroup.js +2 -2
- package/dist/components/TextField.js +6 -6
- package/dist/components/TimeField.js +3 -3
- package/dist/components/Toggle.js +2 -2
- package/dist/components/Toolbar.js +2 -2
- package/dist/components/Tooltip.js +2 -2
- package/dist/components/Tree.js +6 -6
- package/dist/core/Config.d.ts +10 -3
- package/dist/core/Config.js +16 -1
- package/dist/core/Graph.d.ts +2 -2
- package/dist/core/Role.js +16 -14
- package/dist/core/TextDoc.d.ts +2 -0
- package/dist/core/TextDoc.js +1 -0
- package/dist/core/Workspace.d.ts +6 -5
- package/dist/core/db/EntryIndex.d.ts +3 -0
- package/dist/core/db/EntryIndex.js +62 -0
- package/dist/core/db/EntryTransaction.js +33 -12
- package/dist/core/db/EntryUrlConflictError.js +1 -1
- package/dist/core/db/LinkResolver.d.ts +8 -2
- package/dist/core/db/LinkResolver.js +27 -5
- package/dist/core/field/RichTextField.js +16 -10
- package/dist/core/media/MediaLocation.d.ts +9 -15
- package/dist/core/media/MediaLocation.js +29 -19
- package/dist/core/media/MediaTypes.js +1 -2
- package/dist/dashboard/App.js +1 -1
- package/dist/dashboard/app/AccessDenied.js +2 -2
- package/dist/dashboard/app/ActivityStatus.js +14 -13
- package/dist/dashboard/app/AppShell.js +1 -1
- package/dist/dashboard/app/AuthView.js +9 -9
- package/dist/dashboard/app/Badge.js +2 -2
- package/dist/dashboard/app/CompactField.js +13 -13
- package/dist/dashboard/app/DashboardErrorBoundary.js +4 -4
- package/dist/dashboard/app/EntryFields.d.ts +3 -1
- package/dist/dashboard/app/EntryFields.js +11 -8
- package/dist/dashboard/app/EntryHeader.js +117 -85
- package/dist/dashboard/app/EntryHeaderActions.d.ts +21 -4
- package/dist/dashboard/app/EntryHeaderActions.js +48 -27
- package/dist/dashboard/app/EntryReferences.js +9 -9
- package/dist/dashboard/app/EntrySidebar.js +12 -12
- package/dist/dashboard/app/EntrySidebarPreview.js +11 -11
- package/dist/dashboard/app/EntryTranslationBanner.d.ts +3 -1
- package/dist/dashboard/app/EntryTranslationBanner.js +24 -21
- package/dist/dashboard/app/Explorer.d.ts +4 -2
- package/dist/dashboard/app/Explorer.js +50 -46
- package/dist/dashboard/app/ExplorerCards.js +28 -28
- package/dist/dashboard/app/ExplorerFileCard.js +19 -19
- package/dist/dashboard/app/ExplorerList.js +12 -13
- package/dist/dashboard/app/ExplorerModal.js +5 -5
- package/dist/dashboard/app/ExplorerTable.js +46 -46
- package/dist/dashboard/app/LinkPicker.js +5 -5
- package/dist/dashboard/app/LocaleMenu.d.ts +7 -0
- package/dist/dashboard/app/LocaleMenu.js +26 -10
- package/dist/dashboard/app/ReadOnlyBadge.d.ts +4 -0
- package/dist/dashboard/app/ReadOnlyBadge.js +33 -0
- package/dist/dashboard/app/SidebarTree.d.ts +3 -6
- package/dist/dashboard/app/SidebarTree.js +54 -49
- package/dist/dashboard/app/WorkspaceMenu.js +12 -12
- package/dist/dashboard/app/WorkspaceRoots.js +24 -24
- package/dist/dashboard/app/editor/FileEditor.d.ts +2 -3
- package/dist/dashboard/app/editor/FileEditor.js +31 -34
- package/dist/dashboard/app/editor/FilePreview.js +6 -6
- package/dist/dashboard/app/modals/CreateEntry.js +5 -5
- package/dist/dashboard/app/pages/EntryPage.js +73 -30
- package/dist/dashboard/app/pages/SplashPage.js +47 -47
- package/dist/dashboard/app/pages/UsersPage.js +34 -34
- package/dist/dashboard/app/ui/DashboardModal.js +18 -18
- package/dist/dashboard/app/ui/Rail.js +3 -3
- package/dist/dashboard/app/ui/Sidebar.js +2 -2
- package/dist/dashboard/atoms/activity.d.ts +0 -1
- package/dist/dashboard/atoms/activity.js +1 -2
- package/dist/dashboard/atoms/editor.d.ts +2 -1
- package/dist/dashboard/atoms/editor.js +4 -2
- package/dist/dashboard/atoms/entry.d.ts +2 -0
- package/dist/dashboard/atoms/entry.js +32 -14
- package/dist/dashboard/atoms/explorer.d.ts +4 -3
- package/dist/dashboard/atoms/explorer.js +61 -22
- package/dist/dashboard/atoms/link.d.ts +1 -1
- package/dist/dashboard/atoms/link.js +39 -55
- package/dist/dashboard/atoms/root.d.ts +7 -2
- package/dist/dashboard/atoms/root.js +51 -16
- package/dist/dashboard/atoms/upload.d.ts +1 -0
- package/dist/dashboard/atoms/upload.js +2 -1
- package/dist/dashboard/boot/DashboardWorker.js +1 -0
- package/dist/dashboard/fixture/cms.js +1 -4
- package/dist/dashboard/hook/UseSaveShortcut.d.ts +1 -0
- package/dist/dashboard/hook/UseSaveShortcut.js +24 -0
- package/dist/dashboard/hooks.d.ts +1 -1
- package/dist/dashboard/hooks.js +12 -3
- package/dist/field/code/CodeField.view.js +9 -9
- package/dist/field/link/ImageLink.js +1 -3
- package/dist/field/link/LinkField.view.js +84 -58
- package/dist/field/list/ListField.view.js +13 -13
- package/dist/field/metadata/MetadataField.d.ts +3 -3
- package/dist/field/metadata/MetadataField.view.js +24 -17
- package/dist/field/object/ObjectField.view.js +11 -4
- package/dist/field/richtext/PickTextLink.js +5 -2
- package/dist/field/richtext/ReferenceLink.d.ts +2 -0
- package/dist/field/richtext/ReferenceLink.js +5 -0
- package/dist/field/richtext/RichTextBlock.js +2 -2
- package/dist/field/richtext/RichTextExtensions.js +22 -22
- package/dist/field/richtext/RichTextField.view.js +4 -4
- package/dist/field/richtext/extensions/Link.d.ts +1 -0
- package/dist/field/richtext/extensions/Link.js +1 -0
- package/dist/field/time/TimeField.d.ts +0 -2
- package/dist/field/time/TimeField.view.js +1 -1
- package/dist/index.css +44 -38
- package/dist/next.cjs +96 -19
- package/dist/picker/entry/EntryPicker.js +10 -12
- package/dist/picker/entry/EntryReference.d.ts +2 -0
- package/dist/picker/entry/EntryReference.js +1 -0
- package/dist/types.d.ts +0 -1
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import "../../chunks/chunk-CQ52GEHY.js";
|
|
|
3
3
|
// src/adapter/vite/plugin.ts
|
|
4
4
|
import { nodeHandler } from "alinea/backend/router/NodeHandler";
|
|
5
5
|
import { createDevServer } from "alinea/cli/serve/DevServer";
|
|
6
|
+
import { Config } from "alinea/core/Config";
|
|
6
7
|
function createFallbackUrl(server) {
|
|
7
8
|
const address = server.httpServer?.address();
|
|
8
9
|
const protocol = server.config.server.https ? "https" : "http";
|
|
@@ -28,19 +29,23 @@ function waitForServerUrl(server) {
|
|
|
28
29
|
server.httpServer?.once("listening", resolveUrl);
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
|
-
function isAlineaRequest(request) {
|
|
32
|
+
function isAlineaRequest(request, adminPath) {
|
|
32
33
|
const url = new URL(request.url);
|
|
33
|
-
return url.pathname ===
|
|
34
|
+
return url.pathname === adminPath || url.pathname.startsWith(`${adminPath}/`) || url.pathname === "/api" || url.pathname.startsWith("/api/") || url.pathname === "/~dev" || url.pathname.startsWith("/~dev/") || url.search.startsWith("?/upload");
|
|
34
35
|
}
|
|
35
36
|
function alineaPlugin(dir) {
|
|
36
37
|
let devServer;
|
|
38
|
+
let adminPath = "/admin";
|
|
37
39
|
return {
|
|
38
40
|
name: "alinea-plugin",
|
|
39
41
|
configureServer(server) {
|
|
40
42
|
const serverUrl = waitForServerUrl(server);
|
|
41
43
|
const devServerPromise = createDevServer(dir, {
|
|
42
44
|
cmd: "dev",
|
|
43
|
-
dashboardUrl: serverUrl
|
|
45
|
+
dashboardUrl: serverUrl,
|
|
46
|
+
onAfterGenerate(_, config) {
|
|
47
|
+
adminPath = Config.adminPath(config);
|
|
48
|
+
}
|
|
44
49
|
}).then((result) => {
|
|
45
50
|
devServer = result;
|
|
46
51
|
return result;
|
|
@@ -50,7 +55,7 @@ function alineaPlugin(dir) {
|
|
|
50
55
|
});
|
|
51
56
|
server.middlewares.use(
|
|
52
57
|
nodeHandler(async (request) => {
|
|
53
|
-
if (!isAlineaRequest(request)) return void 0;
|
|
58
|
+
if (!isAlineaRequest(request, adminPath)) return void 0;
|
|
54
59
|
const localServer = await devServerPromise;
|
|
55
60
|
return localServer.handle(request);
|
|
56
61
|
})
|
package/dist/backend/Handler.js
CHANGED
|
@@ -17,16 +17,20 @@ import {
|
|
|
17
17
|
BLOB_SEQUENCE_CONTENT_TYPE,
|
|
18
18
|
encodeBlobSequence
|
|
19
19
|
} from "alinea/core/BlobTransport";
|
|
20
|
+
import { Config } from "alinea/core/Config";
|
|
20
21
|
import { developmentKeyHeader } from "alinea/core/Connection";
|
|
21
22
|
import { ErrorCode, HttpError } from "alinea/core/HttpError";
|
|
23
|
+
import { MediaLocation } from "alinea/core/media/MediaLocation";
|
|
22
24
|
import { assertUploadSize } from "alinea/core/media/UploadLimits";
|
|
23
25
|
import { Permission, Policy } from "alinea/core/Role";
|
|
24
26
|
import { getScope } from "alinea/core/Scope";
|
|
25
27
|
import { ShaMismatchError } from "alinea/core/source/ShaMismatchError";
|
|
26
28
|
import { base64 } from "alinea/core/util/Encoding";
|
|
27
29
|
import { isRecord } from "alinea/core/util/Objects";
|
|
30
|
+
import { isAbsolute, normalize } from "alinea/core/util/Paths";
|
|
28
31
|
import { InvalidCredentialsError, MissingCredentialsError } from "./Auth.js";
|
|
29
32
|
import { HandleAction } from "./HandleAction.js";
|
|
33
|
+
import { isRedirectStatus, proxyMediaUrl } from "./api/ProxyMedia.js";
|
|
30
34
|
import { applyPreview, decodePreviewRequest } from "./resolver/ParsePreview.js";
|
|
31
35
|
import { compressResponse } from "./router/Router.js";
|
|
32
36
|
import { createThrottledSync } from "./util/Syncable.js";
|
|
@@ -75,6 +79,73 @@ function createHandler({
|
|
|
75
79
|
}
|
|
76
80
|
});
|
|
77
81
|
}
|
|
82
|
+
if (!params.has("action") && params.has("file") && (request.method === "GET" || request.method === "HEAD")) {
|
|
83
|
+
const file = string(params.get("file"));
|
|
84
|
+
const proxy = params.get("delivery") === "proxy";
|
|
85
|
+
const normalized = normalize(file);
|
|
86
|
+
if (isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../"))
|
|
87
|
+
throw new HttpError(400, "Invalid file path");
|
|
88
|
+
await periodicSync(cnx);
|
|
89
|
+
const requestedUrl = Config.filePathname(cms.config, normalized);
|
|
90
|
+
const entry = local.index.findByUrl(
|
|
91
|
+
requestedUrl,
|
|
92
|
+
(entry2) => entry2.type === "MediaFile" && entry2.status === "published" && entry2.main
|
|
93
|
+
);
|
|
94
|
+
if (!entry) return new Response("Not found", { status: 404 });
|
|
95
|
+
if (entry.url !== requestedUrl && !proxy)
|
|
96
|
+
return new Response(null, {
|
|
97
|
+
status: 308,
|
|
98
|
+
headers: { location: entry.url }
|
|
99
|
+
});
|
|
100
|
+
const wasBuilt = local.index.initialSync?.has(entry.filePath) ?? false;
|
|
101
|
+
const previewUrl = entry.data.previewUrl;
|
|
102
|
+
const location = entry.data.location;
|
|
103
|
+
if (typeof location !== "string")
|
|
104
|
+
return new Response("Not found", { status: 404 });
|
|
105
|
+
const previewSource = typeof previewUrl === "string" && previewUrl ? new URL(previewUrl, request.url).href : void 0;
|
|
106
|
+
async function proxyMedia(source2, cacheControl, isAllowed) {
|
|
107
|
+
const previewEntryId = uploadPreviewEntryId(
|
|
108
|
+
source2,
|
|
109
|
+
context.handlerUrl
|
|
110
|
+
);
|
|
111
|
+
let response;
|
|
112
|
+
if (previewEntryId && cnx.previewUpload) {
|
|
113
|
+
response = await cnx.previewUpload(previewEntryId);
|
|
114
|
+
} else {
|
|
115
|
+
response = await proxyMediaUrl(
|
|
116
|
+
request,
|
|
117
|
+
new URL(source2, request.url),
|
|
118
|
+
isAllowed
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return mediaResponse(request, response, cacheControl);
|
|
122
|
+
}
|
|
123
|
+
if (!wasBuilt && previewSource) {
|
|
124
|
+
if (proxy) return proxyMedia(previewSource, "private, no-store");
|
|
125
|
+
return redirectFile(previewSource, "private, no-store");
|
|
126
|
+
}
|
|
127
|
+
const source = MediaLocation.sourceUrl(
|
|
128
|
+
cms.config,
|
|
129
|
+
entry.workspace,
|
|
130
|
+
location
|
|
131
|
+
);
|
|
132
|
+
if (!source || source === requestedUrl)
|
|
133
|
+
return new Response("Not found", { status: 404 });
|
|
134
|
+
if (!proxy) return redirectFile(source, "public, max-age=60");
|
|
135
|
+
const configuredBase = Config.baseUrl(
|
|
136
|
+
cms.config,
|
|
137
|
+
context.isDev ? "development" : "production"
|
|
138
|
+
);
|
|
139
|
+
const deliveryBase = configuredBase ?? context.handlerUrl;
|
|
140
|
+
const sourceUrl = new URL(source, deliveryBase);
|
|
141
|
+
const deliveryOrigin = new URL(deliveryBase).origin;
|
|
142
|
+
const handlerPath = context.handlerUrl.pathname;
|
|
143
|
+
return proxyMedia(
|
|
144
|
+
sourceUrl.href,
|
|
145
|
+
"public, max-age=60",
|
|
146
|
+
(url2) => url2.origin === deliveryOrigin && url2.pathname !== handlerPath && !url2.pathname.startsWith(`${handlerPath}/`)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
78
149
|
const action = params.get("action");
|
|
79
150
|
const expectJson = () => {
|
|
80
151
|
const acceptsJson = request.headers.get("accept")?.includes("application/json");
|
|
@@ -347,6 +418,41 @@ function createHandler({
|
|
|
347
418
|
}
|
|
348
419
|
};
|
|
349
420
|
}
|
|
421
|
+
function redirectFile(location, cacheControl) {
|
|
422
|
+
return new Response(null, {
|
|
423
|
+
status: 307,
|
|
424
|
+
headers: { "cache-control": cacheControl, location }
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
function uploadPreviewEntryId(previewSource, handlerUrl) {
|
|
428
|
+
const previewUrl = new URL(previewSource);
|
|
429
|
+
if (previewUrl.origin !== handlerUrl.origin || previewUrl.pathname !== handlerUrl.pathname || previewUrl.searchParams.get("action") !== HandleAction.Upload)
|
|
430
|
+
return;
|
|
431
|
+
return previewUrl.searchParams.get("entryId") ?? void 0;
|
|
432
|
+
}
|
|
433
|
+
function mediaResponse(request, upstream, cacheControl) {
|
|
434
|
+
if (isRedirectStatus(upstream.status))
|
|
435
|
+
return new Response("Media backend returned a redirect", { status: 502 });
|
|
436
|
+
const responseHeaders = new Headers({
|
|
437
|
+
"cache-control": upstream.ok || upstream.status === 304 ? cacheControl : "no-store"
|
|
438
|
+
});
|
|
439
|
+
for (const name of [
|
|
440
|
+
"accept-ranges",
|
|
441
|
+
"content-disposition",
|
|
442
|
+
"content-length",
|
|
443
|
+
"content-range",
|
|
444
|
+
"content-type",
|
|
445
|
+
"etag",
|
|
446
|
+
"last-modified"
|
|
447
|
+
]) {
|
|
448
|
+
const value = upstream.headers.get(name);
|
|
449
|
+
if (value) responseHeaders.set(name, value);
|
|
450
|
+
}
|
|
451
|
+
return new Response(request.method === "HEAD" ? null : upstream.body, {
|
|
452
|
+
status: upstream.status,
|
|
453
|
+
headers: responseHeaders
|
|
454
|
+
});
|
|
455
|
+
}
|
|
350
456
|
function parseUser(input) {
|
|
351
457
|
if (!isRecord(input)) throw new HttpError(400, "Expected user object");
|
|
352
458
|
const { sub, name, email, roles } = input;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import "../../chunks/chunk-CQ52GEHY.js";
|
|
2
|
+
|
|
3
|
+
// src/backend/api/ProxyMedia.ts
|
|
4
|
+
import { Headers, Response } from "@alinea/iso";
|
|
5
|
+
async function proxyMediaUrl(request, source, isAllowed = (url) => url.origin === source.origin) {
|
|
6
|
+
const headers = new Headers();
|
|
7
|
+
for (const name of [
|
|
8
|
+
"range",
|
|
9
|
+
"if-range",
|
|
10
|
+
"if-none-match",
|
|
11
|
+
"if-modified-since"
|
|
12
|
+
]) {
|
|
13
|
+
const value = request.headers.get(name);
|
|
14
|
+
if (value) headers.set(name, value);
|
|
15
|
+
}
|
|
16
|
+
let current = source;
|
|
17
|
+
for (let redirects = 0; ; redirects++) {
|
|
18
|
+
if (!isAllowed(current))
|
|
19
|
+
return new Response("Invalid media source", { status: 502 });
|
|
20
|
+
const response = await fetch(current, {
|
|
21
|
+
method: request.method,
|
|
22
|
+
headers,
|
|
23
|
+
redirect: "manual",
|
|
24
|
+
signal: request.signal
|
|
25
|
+
});
|
|
26
|
+
const location = response.headers.get("location");
|
|
27
|
+
if (!isRedirectStatus(response.status) || !location) return response;
|
|
28
|
+
if (redirects === 3)
|
|
29
|
+
return new Response("Too many media redirects", { status: 502 });
|
|
30
|
+
current = new URL(location, current);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function isRedirectStatus(status) {
|
|
34
|
+
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
isRedirectStatus,
|
|
38
|
+
proxyMediaUrl
|
|
39
|
+
};
|
|
@@ -195,6 +195,8 @@ function callHandler(handler, input) {
|
|
|
195
195
|
function compressResponse(request, response) {
|
|
196
196
|
const body = response.body;
|
|
197
197
|
if (!body) return response;
|
|
198
|
+
if (response.status === 206 || response.headers.has("content-range"))
|
|
199
|
+
return response;
|
|
198
200
|
const isCompressed = response.headers.get("content-encoding");
|
|
199
201
|
if (isCompressed) return response;
|
|
200
202
|
const accept = request.headers.get("accept-encoding");
|
|
@@ -5,7 +5,7 @@ import "../../chunks/chunk-CQ52GEHY.js";
|
|
|
5
5
|
|
|
6
6
|
// src/backend/util/Syncable.ts
|
|
7
7
|
function createThrottledSync() {
|
|
8
|
-
let lastSync =
|
|
8
|
+
let lastSync = 0;
|
|
9
9
|
const limit = pLimit(1);
|
|
10
10
|
return async function throttle(sync, intervalSeconds = 60) {
|
|
11
11
|
return limit(async () => {
|