alinea 2.0.0-preview.10 → 2.0.0-preview.12
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/.mcp/later/dev.json +1 -1
- package/dist/.mcp/online/dev.json +1 -1
- package/dist/adapter/next/handler.js +6 -4
- package/dist/backend/Handler.d.ts +3 -0
- package/dist/backend/Handler.js +5 -5
- package/dist/backend/api/OAuth2.js +21 -26
- package/dist/backend/util/JWKS.d.ts +4 -0
- package/dist/backend/util/JWKS.js +40 -0
- package/dist/bundled.d.ts +129 -31
- package/dist/chunks/{chunk-HXXBAY3B.js → chunk-OHGAVV4G.js} +1 -1
- package/dist/cli/Mcp.js +1 -1
- package/dist/cli/Serve.d.ts +1 -0
- package/dist/cli/Serve.js +5 -2
- package/dist/cli/bin.js +3 -2
- package/dist/cli/generate/GenerateDashboard.js +17 -5
- package/dist/cli/serve/DevServer.d.ts +2 -0
- package/dist/cli/serve/DevServer.js +14 -2
- package/dist/cli/serve/mcp/DevMcp.js +1 -1
- package/dist/cli/static/dashboard/entry.js +2 -1
- package/dist/cloud/CloudHandshake.js +9 -46
- package/dist/cloud/CloudRemote.js +1 -1
- package/dist/components/Alert.js +2 -2
- package/dist/components/Badge.js +3 -3
- package/dist/components/Breadcrumbs.js +5 -5
- package/dist/components/Button.js +2 -2
- package/dist/components/Calendar.js +17 -17
- package/dist/components/Checkbox.js +7 -7
- package/dist/components/Collapsible.js +3 -3
- package/dist/components/ComboBox.js +8 -8
- package/dist/components/Command.js +7 -7
- package/dist/components/ContentCard.js +25 -25
- package/dist/components/ContentGrid.js +8 -8
- package/dist/components/DataList.js +2 -2
- package/dist/components/Dialog.js +6 -6
- package/dist/components/DropZone.js +3 -3
- package/dist/components/DropdownMenu.js +9 -9
- package/dist/components/Empty.js +1 -1
- package/dist/components/Field.js +7 -7
- package/dist/components/List.js +26 -26
- package/dist/components/MediaPreview.js +3 -3
- package/dist/components/MultipleSelect.js +19 -19
- package/dist/components/NavRail.js +5 -5
- package/dist/components/NumberField.js +1 -1
- package/dist/components/Page.js +5 -5
- package/dist/components/PreviewFrame.js +6 -6
- package/dist/components/RadioGroup.js +6 -6
- package/dist/components/Resizable.js +1 -1
- package/dist/components/SearchField.js +4 -4
- package/dist/components/Select.js +8 -8
- package/dist/components/Sidebar.js +4 -4
- package/dist/components/SortableList.js +15 -15
- package/dist/components/Spinner.js +3 -3
- package/dist/components/Surface.js +3 -3
- package/dist/components/Switch.js +5 -5
- package/dist/components/Table.js +40 -40
- package/dist/components/Tabs.js +4 -4
- package/dist/components/TagGroup.js +8 -8
- package/dist/components/TextField.js +8 -8
- package/dist/components/TimeField.js +6 -6
- package/dist/components/Toolbar.js +2 -2
- package/dist/components/Tree.js +21 -21
- package/dist/components/internal/DateControl.js +9 -9
- package/dist/components/internal/ListBoxOption.js +7 -7
- package/dist/components/internal/SelectionCheckbox.js +3 -3
- package/dist/components/internal/ToggleButton.js +4 -4
- package/dist/core/Role.d.ts +4 -5
- package/dist/core/Role.js +55 -54
- package/dist/core/Scope.d.ts +2 -0
- package/dist/core/Scope.js +36 -2
- package/dist/core/Type.d.ts +7 -4
- package/dist/core/Type.js +5 -1
- package/dist/core/Validation.js +3 -3
- package/dist/core/media/CompressPdf.browser.js +1 -1
- package/dist/core/media/CompressPdf.js +1 -1
- package/dist/core/media/CreatePreview.browser.js +1 -1
- package/dist/core/util/JWT.d.ts +1 -1
- package/dist/dashboard/app/ActivityStatus.js +12 -12
- package/dist/dashboard/app/AuthView.js +4 -4
- package/dist/dashboard/app/CompactField.js +15 -15
- package/dist/dashboard/app/CroppedImage.js +2 -2
- package/dist/dashboard/app/DashboardErrorBoundary.js +2 -2
- package/dist/dashboard/app/EntryHeader.js +5 -5
- package/dist/dashboard/app/EntryReferences.js +3 -3
- package/dist/dashboard/app/EntrySidebar.js +12 -12
- package/dist/dashboard/app/EntrySidebarPreview.js +2 -2
- package/dist/dashboard/app/Explorer.js +32 -39
- package/dist/dashboard/app/ExplorerBatchActions.js +3 -3
- package/dist/dashboard/app/ExplorerControls.js +5 -5
- package/dist/dashboard/app/ExplorerList.js +4 -4
- package/dist/dashboard/app/ExplorerModal.js +3 -3
- package/dist/dashboard/app/ExplorerPickerContent.d.ts +1 -1
- package/dist/dashboard/app/ExplorerPickerContent.js +17 -3
- package/dist/dashboard/app/ExplorerTable.js +7 -7
- package/dist/dashboard/app/ImageEditor.js +25 -25
- package/dist/dashboard/app/ImagePicker.js +3 -2
- package/dist/dashboard/app/LinkPicker.js +6 -5
- package/dist/dashboard/app/LocaleMenu.js +7 -7
- package/dist/dashboard/app/MediaPreviewCell.js +3 -3
- package/dist/dashboard/app/OverviewCell.js +7 -7
- package/dist/dashboard/app/PendingUploadsView.js +4 -4
- package/dist/dashboard/app/SidebarTree.js +21 -23
- package/dist/dashboard/app/WorkspaceMenu.js +7 -7
- package/dist/dashboard/app/WorkspaceRoots.js +9 -9
- package/dist/dashboard/app/editor/FileEditor.js +20 -20
- package/dist/dashboard/app/modals/CreateEntry.js +5 -5
- package/dist/dashboard/app/pages/EntryPage.js +8 -8
- package/dist/dashboard/app/pages/SplashPage.js +37 -37
- package/dist/dashboard/app/pages/UsersPage.js +12 -12
- package/dist/dashboard/app/ui/DashboardModal.js +15 -15
- package/dist/dashboard/atoms/config.d.ts +1 -1
- package/dist/dashboard/atoms/entry.d.ts +11 -4
- package/dist/dashboard/atoms/entry.js +103 -86
- package/dist/dashboard/atoms/entryTable.js +2 -1
- package/dist/dashboard/atoms/explorer.d.ts +17 -2
- package/dist/dashboard/atoms/explorer.js +38 -16
- package/dist/dashboard/atoms/link.d.ts +2 -0
- package/dist/dashboard/atoms/link.js +6 -3
- package/dist/dashboard/atoms/root.d.ts +15 -3
- package/dist/dashboard/atoms/root.js +111 -52
- package/dist/dashboard/boot/BootProd.d.ts +6 -1
- package/dist/dashboard/boot/BootProd.js +2 -2
- package/dist/dashboard/boot/DashboardWorker.js +51 -15
- package/dist/dashboard/cms/EntryTable.js +2 -2
- package/dist/database/BrowserEntryStore.d.ts +47 -9
- package/dist/database/BrowserEntryStore.js +137 -66
- package/dist/database/EntryTransaction.js +17 -6
- package/dist/field/check/CheckField.view.js +2 -2
- package/dist/field/code/CodeField.view.js +11 -11
- package/dist/field/link/LinkField.view.js +33 -32
- package/dist/field/list/ListField.view.js +4 -4
- package/dist/field/metadata/MetadataField.view.js +16 -16
- package/dist/field/richtext/RichTextBlock.js +2 -2
- package/dist/field/richtext/RichTextExtensions.js +31 -31
- package/dist/field/richtext/RichTextField.view.js +5 -5
- package/docs/deploy.md +1 -1
- package/docs/roles-permissions.md +7 -7
- package/llms-full.txt +8 -8
- package/package.json +1 -1
- /package/dist/chunks/{dist-UDQZOKRG.js → dist-OU2RRDGP.js} +0 -0
package/dist/.mcp/later/dev.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"url":"http://localhost:1","pid":
|
|
1
|
+
{"url":"http://localhost:1","pid":2819}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"url":"http://localhost:
|
|
1
|
+
{"url":"http://localhost:40589","pid":2819}
|
|
@@ -67,8 +67,6 @@ function createHandlerWithDatabase(input, openGeneratedDatabase) {
|
|
|
67
67
|
return failure(400, "Expected JSON");
|
|
68
68
|
}
|
|
69
69
|
if (!Array.isArray(mutations)) return failure(400, "Expected mutations");
|
|
70
|
-
const adjusted = await options.beforeCommit?.({ mutations });
|
|
71
|
-
if (adjusted) mutations = [...adjusted];
|
|
72
70
|
const devServer = new Client({
|
|
73
71
|
config,
|
|
74
72
|
url: context.handlerUrl.href,
|
|
@@ -80,15 +78,19 @@ function createHandlerWithDatabase(input, openGeneratedDatabase) {
|
|
|
80
78
|
}
|
|
81
79
|
});
|
|
82
80
|
let sha;
|
|
81
|
+
let user;
|
|
83
82
|
try {
|
|
84
|
-
;
|
|
83
|
+
user = await devServer.user();
|
|
84
|
+
if (!user) return failure(401, "Unauthorized");
|
|
85
|
+
const adjusted = await options.beforeCommit?.({ mutations, user });
|
|
86
|
+
if (adjusted) mutations = [...adjusted];
|
|
85
87
|
({ sha } = await devServer.mutate(mutations));
|
|
86
88
|
} catch (error) {
|
|
87
89
|
if (error instanceof HttpError) return failure(error.code, error.message);
|
|
88
90
|
throw error;
|
|
89
91
|
}
|
|
90
92
|
try {
|
|
91
|
-
await afterCommit({ mutations, sha });
|
|
93
|
+
await afterCommit({ mutations, sha, user });
|
|
92
94
|
} catch (error) {
|
|
93
95
|
console.error("Alinea afterCommit hook failed", error);
|
|
94
96
|
}
|
|
@@ -2,12 +2,15 @@ import type { CMS } from 'alinea/core/CMS';
|
|
|
2
2
|
import type { AuthedContext, RemoteConnection, RequestContext } from 'alinea/core/Connection';
|
|
3
3
|
import type { LocalStore } from 'alinea/core/db/LocalStore';
|
|
4
4
|
import type { Mutation } from 'alinea/core/db/Mutation';
|
|
5
|
+
import type { User } from 'alinea/core/User';
|
|
5
6
|
export interface Handler {
|
|
6
7
|
(request: Request, context: RequestContext): Promise<Response>;
|
|
7
8
|
}
|
|
8
9
|
export type HookResponse<T = void> = void | T | Promise<void | T>;
|
|
9
10
|
export interface BeforeCommitContext {
|
|
10
11
|
mutations: ReadonlyArray<Mutation>;
|
|
12
|
+
/** The user committing the mutations */
|
|
13
|
+
user: User;
|
|
11
14
|
}
|
|
12
15
|
export interface AfterCommitContext extends BeforeCommitContext {
|
|
13
16
|
sha: string;
|
package/dist/backend/Handler.js
CHANGED
|
@@ -273,7 +273,10 @@ function createHandler({
|
|
|
273
273
|
const policy = await user.policy;
|
|
274
274
|
let mutations = await body;
|
|
275
275
|
await local.syncWith(cnx);
|
|
276
|
-
const adjusted = await hooks.beforeCommit?.({
|
|
276
|
+
const adjusted = await hooks.beforeCommit?.({
|
|
277
|
+
mutations,
|
|
278
|
+
user: user.claims
|
|
279
|
+
});
|
|
277
280
|
if (adjusted) mutations = adjusted;
|
|
278
281
|
const attempt = async (retry = 0) => {
|
|
279
282
|
if (retry > 0) await local.syncWith(cnx);
|
|
@@ -297,10 +300,7 @@ function createHandler({
|
|
|
297
300
|
};
|
|
298
301
|
const sha = await attempt();
|
|
299
302
|
try {
|
|
300
|
-
await hooks.afterCommit?.({
|
|
301
|
-
mutations,
|
|
302
|
-
sha
|
|
303
|
-
});
|
|
303
|
+
await hooks.afterCommit?.({ mutations, sha, user: user.claims });
|
|
304
304
|
} catch (error) {
|
|
305
305
|
console.error("Alinea afterCommit hook failed", error);
|
|
306
306
|
}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parse
|
|
3
3
|
} from "../../chunks/chunk-BYJRQTFR.js";
|
|
4
|
-
import {
|
|
5
|
-
PLazy
|
|
6
|
-
} from "../../chunks/chunk-IKINPSS5.js";
|
|
7
4
|
import "../../chunks/chunk-CQ52GEHY.js";
|
|
8
5
|
|
|
9
6
|
// src/backend/api/OAuth2.ts
|
|
10
7
|
import { AuthResultType } from "alinea/cloud/AuthResult";
|
|
11
8
|
import { Config } from "alinea/core/Config";
|
|
12
|
-
import { HttpError } from "alinea/core/HttpError";
|
|
9
|
+
import { ErrorCode, HttpError } from "alinea/core/HttpError";
|
|
13
10
|
import { createId } from "alinea/core/Id";
|
|
14
11
|
import { outcome } from "alinea/core/Outcome";
|
|
15
12
|
import { assert } from "alinea/core/util/Assert";
|
|
@@ -459,6 +456,7 @@ import {
|
|
|
459
456
|
MissingCredentialsError
|
|
460
457
|
} from "../Auth.js";
|
|
461
458
|
import { router } from "../router/Router.js";
|
|
459
|
+
import { jwks } from "../util/JWKS.js";
|
|
462
460
|
var COOKIE_VERIFIER = "alinea.cv";
|
|
463
461
|
var COOKIE_ACCESS_TOKEN = "alinea.at";
|
|
464
462
|
var COOKIE_REFRESH_TOKEN = "alinea.rt";
|
|
@@ -466,7 +464,7 @@ var OAuth2 = class {
|
|
|
466
464
|
#context;
|
|
467
465
|
#config;
|
|
468
466
|
#client;
|
|
469
|
-
#
|
|
467
|
+
#jwksUri;
|
|
470
468
|
#validateClaims;
|
|
471
469
|
constructor(context, config, options) {
|
|
472
470
|
assert(
|
|
@@ -489,19 +487,16 @@ var OAuth2 = class {
|
|
|
489
487
|
return response;
|
|
490
488
|
})
|
|
491
489
|
});
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
};
|
|
504
|
-
this.#jwks = PLazy.from(loadJwks);
|
|
490
|
+
this.#jwksUri = options.jwksUri;
|
|
491
|
+
}
|
|
492
|
+
async #key(token) {
|
|
493
|
+
const { header } = decode(token);
|
|
494
|
+
const keys = await jwks(this.#jwksUri, header.kid).catch((cause) => {
|
|
495
|
+
throw new HttpError(ErrorCode.ServiceUnavailable, "Remote unavailable", {
|
|
496
|
+
cause
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
return selectKey(keys, header);
|
|
505
500
|
}
|
|
506
501
|
get #redirectUri() {
|
|
507
502
|
const url = new URL(this.#context.handlerUrl);
|
|
@@ -612,11 +607,10 @@ var OAuth2 = class {
|
|
|
612
607
|
[COOKIE_ACCESS_TOKEN]: accessToken,
|
|
613
608
|
[COOKIE_REFRESH_TOKEN]: refreshToken
|
|
614
609
|
} = parse(cookieHeader);
|
|
615
|
-
const jwks = await this.#jwks;
|
|
616
610
|
try {
|
|
617
611
|
if (!accessToken)
|
|
618
612
|
throw new MissingCredentialsError("Missing access token cookie");
|
|
619
|
-
const key =
|
|
613
|
+
const key = await this.#key(accessToken);
|
|
620
614
|
const user = await verify(accessToken, key);
|
|
621
615
|
await this.#validateClaims(user);
|
|
622
616
|
assert(user.exp, "Missing exp claim in access token");
|
|
@@ -626,13 +620,15 @@ var OAuth2 = class {
|
|
|
626
620
|
return { ...ctx, user, token: accessToken };
|
|
627
621
|
} catch (error) {
|
|
628
622
|
if (!refreshToken) throw error;
|
|
623
|
+
if (error instanceof HttpError && error.code === ErrorCode.ServiceUnavailable)
|
|
624
|
+
throw error;
|
|
629
625
|
const token = { accessToken, refreshToken, expiresAt: null };
|
|
630
626
|
const newToken = await this.#client.refreshToken(token).catch((cause) => {
|
|
631
627
|
throw new InvalidCredentialsError("Failed to refresh token", {
|
|
632
628
|
cause: [cause, error]
|
|
633
629
|
});
|
|
634
630
|
});
|
|
635
|
-
const key =
|
|
631
|
+
const key = await this.#key(newToken.accessToken);
|
|
636
632
|
const user = await verify(newToken.accessToken, key, {
|
|
637
633
|
clockTolerance: 30
|
|
638
634
|
}).catch((cause) => {
|
|
@@ -653,13 +649,12 @@ var OAuth2 = class {
|
|
|
653
649
|
}
|
|
654
650
|
}
|
|
655
651
|
};
|
|
656
|
-
function selectKey(
|
|
657
|
-
const { header } = decode(token);
|
|
652
|
+
function selectKey(jwks2, header) {
|
|
658
653
|
const kid = header.kid;
|
|
659
654
|
const alg = header.alg;
|
|
660
655
|
if (!kid) {
|
|
661
|
-
if (
|
|
662
|
-
const key2 =
|
|
656
|
+
if (jwks2.length === 1) {
|
|
657
|
+
const key2 = jwks2[0];
|
|
663
658
|
if (key2.alg && alg && key2.alg !== alg) {
|
|
664
659
|
throw new Error(
|
|
665
660
|
`Algorithm mismatch: token uses ${alg}, but key specifies ${key2.alg}`
|
|
@@ -671,7 +666,7 @@ function selectKey(jwks, token) {
|
|
|
671
666
|
'Token is missing "kid" header, and JWKS contains multiple keys'
|
|
672
667
|
);
|
|
673
668
|
}
|
|
674
|
-
const key =
|
|
669
|
+
const key = jwks2.find((k) => k.kid === kid);
|
|
675
670
|
if (!key) throw new Error(`No key found for kid: ${kid}`);
|
|
676
671
|
if (key.alg && alg && key.alg !== alg)
|
|
677
672
|
throw new Error(
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import "../../chunks/chunk-CQ52GEHY.js";
|
|
2
|
+
|
|
3
|
+
// src/backend/util/JWKS.ts
|
|
4
|
+
import { HttpError } from "alinea/core/HttpError";
|
|
5
|
+
import { isRecord } from "alinea/core/util/Objects";
|
|
6
|
+
var lifetime = 10 * 60 * 1e3;
|
|
7
|
+
var refreshInterval = 60 * 1e3;
|
|
8
|
+
var cache = /* @__PURE__ */ new Map();
|
|
9
|
+
async function jwks(uri, kid) {
|
|
10
|
+
const cached = cache.get(uri);
|
|
11
|
+
if (!cached || Date.now() >= cached.expires) return load(uri, cached);
|
|
12
|
+
const keys = await cached.keys;
|
|
13
|
+
if (!kid || keys.some((key) => key.kid === kid)) return keys;
|
|
14
|
+
const current = cache.get(uri);
|
|
15
|
+
if (current && current !== cached) return current.keys;
|
|
16
|
+
if (Date.now() - cached.loaded < refreshInterval) return keys;
|
|
17
|
+
return load(uri, cached);
|
|
18
|
+
}
|
|
19
|
+
function load(uri, previous) {
|
|
20
|
+
const fetched = fetch(uri).then(async (response) => {
|
|
21
|
+
if (!response.ok) throw new HttpError(response.status);
|
|
22
|
+
const body = await response.json();
|
|
23
|
+
if (!isRecord(body) || !Array.isArray(body.keys))
|
|
24
|
+
throw new Error("Invalid signing keys");
|
|
25
|
+
return body.keys.filter(isKey);
|
|
26
|
+
});
|
|
27
|
+
const keys = previous ? fetched.catch(() => previous.keys) : fetched;
|
|
28
|
+
const entry = { keys, expires: Date.now() + lifetime, loaded: Date.now() };
|
|
29
|
+
cache.set(uri, entry);
|
|
30
|
+
keys.catch(() => {
|
|
31
|
+
if (cache.get(uri) === entry) cache.delete(uri);
|
|
32
|
+
});
|
|
33
|
+
return fetched;
|
|
34
|
+
}
|
|
35
|
+
function isKey(value) {
|
|
36
|
+
return isRecord(value) && typeof value.kid === "string";
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
jwks
|
|
40
|
+
};
|