@phtngyn/cms 0.1.0-beta.0 → 0.1.0-beta.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/README.md +72 -1
- package/package.json +5 -5
- package/src/Authoring.d.ts +13 -1
- package/src/{AuthoringSchema-BQ5kbhVB.js → Configuration-DyKo0sLS.js} +595 -367
- package/src/Configuration-DyKo0sLS.js.map +1 -0
- package/src/{DocumentEngine-Da0kWotL.js → DocumentEngine-DYObwMDY.js} +44 -66
- package/src/DocumentEngine-DYObwMDY.js.map +1 -0
- package/src/DocumentModel.d.ts +4 -0
- package/src/DocumentModel.js.map +1 -1
- package/src/{HttpPaths-86qLGB90.js → HttpPaths-BbS21Jy8.js} +4 -2
- package/src/{HttpPaths-86qLGB90.js.map → HttpPaths-BbS21Jy8.js.map} +1 -1
- package/src/Module.js +27 -76
- package/src/Module.js.map +1 -1
- package/src/{StudioHttp-BTLLooh0.js → StudioHttp-jR2-bc01.js} +44 -242
- package/src/StudioHttp-jR2-bc01.js.map +1 -0
- package/src/{connection-ZGtL54qY.js → connection-CBTY-TzB.js} +3 -3
- package/src/{connection-ZGtL54qY.js.map → connection-CBTY-TzB.js.map} +1 -1
- package/src/{installation-BNKaVjni.js → installation-DeuBiFRE.js} +488 -236
- package/src/installation-DeuBiFRE.js.map +1 -0
- package/src/internal/authoring-catalog.js +147 -0
- package/src/internal/authoring-catalog.js.map +1 -0
- package/src/internal/authoring-compiler.js +2 -1
- package/src/internal/authoring-compiler.js.map +1 -1
- package/src/internal/authoring-metadata.js +67 -48
- package/src/internal/authoring-metadata.js.map +1 -1
- package/src/internal/configuration.js +5 -6
- package/src/internal/configuration.js.map +1 -1
- package/src/internal/document-model.js +3 -2
- package/src/internal/document-model.js.map +1 -1
- package/src/internal/studio-app.js +66 -0
- package/src/internal/studio-app.js.map +1 -0
- package/src/location-DtYv2aes.js +36 -0
- package/src/location-DtYv2aes.js.map +1 -0
- package/src/{metadata-Czvi3E4K.js → metadata-DE1Mc2td.js} +2 -2
- package/src/{metadata-Czvi3E4K.js.map → metadata-DE1Mc2td.js.map} +1 -1
- package/src/{models-Citx8jBi.js → models-BkMgeaOL.js} +252 -3
- package/src/models-BkMgeaOL.js.map +1 -0
- package/src/{models-DS5ClMsH.js → models-C3witQBK.js} +8 -31
- package/src/models-C3witQBK.js.map +1 -0
- package/src/runtime/blocks.js +17 -0
- package/src/runtime/blocks.js.map +1 -0
- package/src/runtime/collaboration.js +9 -4
- package/src/runtime/collaboration.js.map +1 -1
- package/src/runtime/delivery/DocumentRenderer.js +6 -3
- package/src/runtime/delivery/DocumentRenderer.js.map +1 -1
- package/src/runtime/delivery/blocks.js +6 -0
- package/src/runtime/delivery/blocks.js.map +1 -0
- package/src/runtime/delivery/context.js +12 -0
- package/src/runtime/delivery/context.js.map +1 -0
- package/src/runtime/delivery/document-page.js +5 -3
- package/src/runtime/delivery/document-page.js.map +1 -1
- package/src/runtime/delivery/document.js +18 -2
- package/src/runtime/delivery/document.js.map +1 -1
- package/src/runtime/delivery/error.js +11 -0
- package/src/runtime/delivery/error.js.map +1 -0
- package/src/runtime/delivery/plugin.js +21 -0
- package/src/runtime/delivery/plugin.js.map +1 -0
- package/src/runtime/delivery/useDocument.js +6 -3
- package/src/runtime/delivery/useDocument.js.map +1 -1
- package/src/runtime/delivery/useDocumentQuery.js +6 -3
- package/src/runtime/delivery/useDocumentQuery.js.map +1 -1
- package/src/runtime/installation.js +1 -1
- package/src/runtime/plugin.js +3 -3
- package/src/runtime/plugin.js.map +1 -1
- package/src/runtime/preview/connection.js +1 -1
- package/src/runtime/preview/controller.js +29 -10
- package/src/runtime/preview/controller.js.map +1 -1
- package/src/runtime/preview/overlay.js +65 -0
- package/src/runtime/preview/overlay.js.map +1 -0
- package/src/runtime/preview/plugin.client.js +21 -34
- package/src/runtime/preview/plugin.client.js.map +1 -1
- package/src/runtime/preview/preview.css +72 -4
- package/src/runtime/preview/store.js +1 -1
- package/src/runtime/studio-app.js +5 -1
- package/src/runtime/studio-app.js.map +1 -1
- package/src/runtime/studio-entry.js +36 -0
- package/src/runtime/studio-entry.js.map +1 -0
- package/src/runtime/studio-navigation.js +11 -0
- package/src/runtime/studio-navigation.js.map +1 -0
- package/src/{store-Nl4uxEXx.js → store-CfFFx9Ua.js} +3 -3
- package/src/{store-Nl4uxEXx.js.map → store-CfFFx9Ua.js.map} +1 -1
- package/studio/CreateDocumentDialog--L9lTfx6.js +256 -0
- package/studio/collaboration-DUuu678R.js +4565 -0
- package/studio/documentPlanning-BXX04Woe.js +28989 -0
- package/studio/dom-to-image-more.min-D6jGGdrW.js +877 -0
- package/studio/editor-PexnXkJY.js +30817 -0
- package/studio/host.css +3 -0
- package/studio/host.js +44179 -0
- package/studio/rolldown-runtime-B0aSnxlc.js +20 -0
- package/dist/studio/assets/ActivityEntryRow-D--fRvWI.js +0 -1
- package/dist/studio/assets/ActivityView-D-XBBUHb.js +0 -1
- package/dist/studio/assets/CreateDocumentDialog-CH8UH1QM.js +0 -1
- package/dist/studio/assets/DocumentsView-CxRpmAxt.js +0 -34
- package/dist/studio/assets/DropdownMenu-BSb1l7fr.js +0 -1
- package/dist/studio/assets/Input-DVao38h7.js +0 -3
- package/dist/studio/assets/MediaView-BHrPIjxB.js +0 -1
- package/dist/studio/assets/Modal-VNrXGa5C.js +0 -1
- package/dist/studio/assets/OverviewView-DfP6-mLZ.js +0 -1
- package/dist/studio/assets/Select-BJZIs9sY.js +0 -1
- package/dist/studio/assets/SettingsView-CerHe-ua.js +0 -1
- package/dist/studio/assets/Skeleton-CWJNi3nS.js +0 -1
- package/dist/studio/assets/StudioPageHeader-DP9HDAF2.js +0 -1
- package/dist/studio/assets/UsersView-cP3REkH2.js +0 -1
- package/dist/studio/assets/collaboration-BLnCzfvV.js +0 -4
- package/dist/studio/assets/context-dY4fNbn3.js +0 -1
- package/dist/studio/assets/editor-CE07wW83.js +0 -137
- package/dist/studio/assets/gateway-BAyQgCse.js +0 -10
- package/dist/studio/assets/index-CjoaPLhZ.js +0 -28
- package/dist/studio/assets/index-s2rPqCmP.css +0 -2
- package/dist/studio/assets/render-BkxbK2St.js +0 -83
- package/dist/studio/assets/rolldown-runtime-DK3Fl9T5.js +0 -1
- package/dist/studio/assets/useMediaLibrary-D7qR7aBf.js +0 -1
- package/dist/studio/index.html +0 -76
- package/src/AuthoringSchema-BQ5kbhVB.js.map +0 -1
- package/src/Configuration-BGYFD2N1.js +0 -150
- package/src/Configuration-BGYFD2N1.js.map +0 -1
- package/src/DocumentEngine-Da0kWotL.js.map +0 -1
- package/src/StudioHttp-BTLLooh0.js.map +0 -1
- package/src/installation-BNKaVjni.js.map +0 -1
- package/src/internal/component-metadata.js +0 -37
- package/src/internal/component-metadata.js.map +0 -1
- package/src/models-Citx8jBi.js.map +0 -1
- package/src/models-DS5ClMsH.js.map +0 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as
|
|
3
|
-
import { a as
|
|
4
|
-
import {
|
|
5
|
-
import { c as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { Cause, Clock, Context, Crypto, Data, DateTime, Deferred, Duration, Effect, Encoding, Exit, Fiber, Layer, Option, Predicate, PubSub, Pull, Queue, Redacted, Ref, Result, Schedule, Schema, Scope, Semaphore, Stream } from "effect";
|
|
9
|
-
import { HttpRouter, HttpServer, HttpServerRequest, HttpServerResponse, Multipart } from "effect/unstable/http";
|
|
10
|
-
import { HttpApiBuilder, HttpApiMiddleware } from "effect/unstable/httpapi";
|
|
1
|
+
import { c as STUDIO_API_PATH, d as STUDIO_COLLABORATION_PATH, i as CMS_LIVENESS_PATH, s as CMS_READINESS_PATH, t as CMS_AUTHENTICATION_PATH } from "./HttpPaths-BbS21Jy8.js";
|
|
2
|
+
import { $ as MediaName, A as RemoveDocumentInput, B as StudioActor, C as MarkdownNode, Ct as Slug, D as PublishedDocumentPage, F as AccessDenied, G as MediaByteSize, H as StudioRequestContext, I as AuthenticationRequired, J as MediaCursor, K as MediaCleanupReport, L as DOCUMENT_ACTIONS, M as UndeleteDocumentInput, N as UnpublishDocumentInput, P as UpdateDocumentInput, Q as MediaInvalid, R as MEDIA_ACTIONS, S as DuplicateDocumentInput, T as PublishDocumentInput, U as ImageMimeType, V as StudioEntryDenied, W as MediaAsset, X as MediaId, Y as MediaFolder, Z as MediaIntegrityReport, _ as DocumentSource, _t as DocumentVersion, a as DOCUMENT_REVISION_ACTIONS, at as MediaReferenced, b as DocumentSurround, bt as RelativeDocumentPath, c as DocumentImportInput, ct as MediaUpload, et as MediaNotFound, f as DocumentMutationResult, g as DocumentRevisionAction, gt as DocumentId, h as DocumentQueryScalar, ht as DocumentHistoryCursor, it as MediaReference, j as RestoreDocumentInput, l as DocumentImportReport, mt as DocumentGeneration, n as CmsMarkdownDocument, nt as MediaPage, o as DeletedDocument, ot as MediaStatus, p as DocumentNavigationItem, pt as RequestId, q as MediaConflict, st as MediaTooLarge, tt as MediaObjectKey, vt as EditorNodeId, w as MoveDocumentInput, wt as SourcePath, x as DocumentValidationResult, xt as RevisionId, y as DocumentSummary, yt as PublicPath, z as Role } from "./models-BkMgeaOL.js";
|
|
3
|
+
import { a as normalizeDocumentRelativePath, c as resolveDocumentAddress, i as localizeDocumentPath, n as referenceTarget, o as normalizePublicPath, r as DocumentCompilationFailed, s as parseDocumentAddress, t as createDocumentEngine } from "./DocumentEngine-DYObwMDY.js";
|
|
4
|
+
import { A as ACTIVITY_ACTIONS, B as ActivityTargetKind, C as CollaborationTicket, D as DocumentRevision, E as DocumentHistoryInvalidCursor, F as ActivityEntry, G as DocumentVersionConflict, H as DocumentNotFound, I as ActivityEntryDraft, K as PublishedDocumentNotFound, L as ActivityId, M as ActivityContext, N as ActivityCursor, O as DocumentRevisionComparison, P as ActivityCursorInvalid, R as ActivityPage, S as CollaborationServerMessage, T as DocumentHistoryComparisonTooLarge, U as DocumentPathConflict, V as DocumentMediaUnavailable, W as DocumentValidationFailed, _ as UserNotFound, b as UserSelfRoleChangeDenied, c as StudioInternalError, d as InitializationProofInvalid, f as InitializationState, g as UserAlreadyExists, h as UserAdministrationDenied, i as StudioBadRequest, j as ActivityAction, k as DocumentRevisionPage, l as studioInternalError, m as User, n as StudioApi, o as StudioRequestConflict, p as CreateUserInput, r as StudioAuthentication, s as StudioSchemaError, t as CurrentStudioRequest, u as InitializationAlreadyCompleted, v as UserPage, w as StateVector, x as CollaborationClientMessage, y as UserSelfRemovalDenied, z as ActivityTarget } from "./StudioHttp-jR2-bc01.js";
|
|
5
|
+
import { a as createEditorNodeId, c as createCmsComarkRenderData, d as toComarkDocument, i as withoutEditorMetadata, o as editorNodeId, r as identifiedAttributes, t as assertCanonicalEditorContent, u as mutableJsonObject } from "./metadata-DE1Mc2td.js";
|
|
6
|
+
import { C as finiteNumberAttribute, D as projectCmsEditorComponentAttributes, S as compactJsonAttributes, T as encodeCmsEditorComponentAttributes, _ as comarkTagForEditorMark, b as encodeEditorImageAttributes, d as EditorRenderFailed, g as EDITOR_MARK_TYPES, h as comarkToEditor, u as EditorDocumentInvalid, v as isEditorMark, w as nonEmptyStringAttribute, x as normalizeEditorImageLayout, y as EDITOR_IMAGE_LAYOUT_ATTRIBUTE } from "./Configuration-DyKo0sLS.js";
|
|
7
|
+
import { Cause, Clock, Context, Crypto, Data, DateTime, Deferred, Effect, Encoding, Exit, Fiber, Layer, Option, Predicate, PubSub, Pull, Queue, Redacted, Ref, Result, Schedule, Schema, Scope, Semaphore, Stream } from "effect";
|
|
11
8
|
import { Extension, Node, getSchema, mergeAttributes } from "@tiptap/core";
|
|
12
9
|
import { prosemirrorJSONToYDoc, ySyncPluginKey, yXmlFragmentToProseMirrorRootNode } from "@tiptap/y-tiptap";
|
|
13
|
-
import
|
|
10
|
+
import { NodeStream } from "@effect/platform-node-shared";
|
|
11
|
+
import { HttpRouter, HttpServer, HttpServerRequest, HttpServerResponse, Multipart } from "effect/unstable/http";
|
|
12
|
+
import { HttpApiBuilder, HttpApiMiddleware } from "effect/unstable/httpapi";
|
|
13
|
+
import * as NodeCrypto$1 from "@effect/platform-node-shared/NodeCrypto";
|
|
14
14
|
import { betterAuth } from "better-auth";
|
|
15
15
|
import { createAccessControl } from "better-auth/plugins/access";
|
|
16
16
|
import { admin } from "better-auth/plugins/admin";
|
|
@@ -23,7 +23,6 @@ import StarterKit from "@tiptap/starter-kit";
|
|
|
23
23
|
import * as Y from "yjs";
|
|
24
24
|
import { Doc, applyUpdate, encodeStateAsUpdate, encodeStateVector } from "yjs";
|
|
25
25
|
import { CopyObjectCommand, CreateBucketCommand, DeleteObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
26
|
-
import { Readable } from "node:stream";
|
|
27
26
|
import { PgClient } from "@effect/sql-pg";
|
|
28
27
|
import { isDeepStrictEqual } from "node:util";
|
|
29
28
|
import { and, asc, count, desc, eq, inArray, lt, lte, ne, or, sql } from "drizzle-orm";
|
|
@@ -1434,16 +1433,65 @@ const renderEditorMarkdown = Effect.fn("EditorDocument.renderMarkdown")((documen
|
|
|
1434
1433
|
});
|
|
1435
1434
|
});
|
|
1436
1435
|
|
|
1436
|
+
//#endregion
|
|
1437
|
+
//#region ../document/src/internal/document-engine/relocate-links.ts
|
|
1438
|
+
/** Rewrites only semantic page links, preserving bindings and unrelated content. */
|
|
1439
|
+
function rewriteDocumentRoutes(document, destinations, components = []) {
|
|
1440
|
+
let changed = false;
|
|
1441
|
+
function link(value) {
|
|
1442
|
+
const target = referenceTarget("a", /* @__PURE__ */ new Map([["href", value]]), document.path, void 0);
|
|
1443
|
+
if (target?.kind !== "document" || !Predicate.isString(value)) return value;
|
|
1444
|
+
const destination = destinations.get(target.path);
|
|
1445
|
+
const sourceMoved = destinations.has(document.path);
|
|
1446
|
+
if (!destination && (!sourceMoved || value.startsWith("/"))) return value;
|
|
1447
|
+
const suffix = value.match(/[?#].*$/u)?.[0] ?? "";
|
|
1448
|
+
const next = `${destination ?? target.path}${suffix}`;
|
|
1449
|
+
if (next !== value) changed = true;
|
|
1450
|
+
return next;
|
|
1451
|
+
}
|
|
1452
|
+
function properties(fields, values) {
|
|
1453
|
+
const result = mutableJsonObject(values);
|
|
1454
|
+
for (const field of fields) {
|
|
1455
|
+
const value = values[field.name] === void 0 ? field.defaultValue : values[field.name];
|
|
1456
|
+
if (field.input === "link") {
|
|
1457
|
+
const next = link(value);
|
|
1458
|
+
if (next !== void 0 && next !== value) result[field.name] = next;
|
|
1459
|
+
} else if (field.fields) {
|
|
1460
|
+
const next = Array.isArray(value) ? value.map((item) => Schema.is(Schema.JsonObject)(item) ? properties(field.fields ?? [], item) : item) : Schema.is(Schema.JsonObject)(value) ? properties(field.fields, value) : value;
|
|
1461
|
+
if (next !== void 0 && JSON.stringify(next) !== JSON.stringify(value)) result[field.name] = next;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
return result;
|
|
1465
|
+
}
|
|
1466
|
+
function visit(node, renderData) {
|
|
1467
|
+
if (Predicate.isString(node) || node[0] === null) return node;
|
|
1468
|
+
const [tag, attributes, ...children] = node;
|
|
1469
|
+
const projection = projectCmsEditorComponentAttributes(attributes, renderData);
|
|
1470
|
+
let props = properties(components.find((component) => component.name.toLowerCase() === tag.toLowerCase())?.authoring.props ?? [], projection.props);
|
|
1471
|
+
if (tag === "a") {
|
|
1472
|
+
const href = link(projection.props.href);
|
|
1473
|
+
if (href !== void 0) props = Object.assign({}, props, { href });
|
|
1474
|
+
}
|
|
1475
|
+
return [
|
|
1476
|
+
tag,
|
|
1477
|
+
JSON.stringify(props) === JSON.stringify(projection.props) ? attributes : encodeCmsEditorComponentAttributes(props, projection.bindings),
|
|
1478
|
+
...children.map((child) => visit(child, projection.renderData))
|
|
1479
|
+
];
|
|
1480
|
+
}
|
|
1481
|
+
const nodes = document.body.nodes.map((node) => visit(node, createCmsComarkRenderData(document.body)));
|
|
1482
|
+
return changed ? Object.assign({}, document.body, { nodes }) : void 0;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1437
1485
|
//#endregion
|
|
1438
1486
|
//#region ../document/src/DocumentPlanning.ts
|
|
1439
1487
|
/** Derives one canonical Source path and public path without performing persistence. */
|
|
1440
1488
|
function planDocumentCreation(configuration, input) {
|
|
1441
1489
|
return Result.gen(function* () {
|
|
1442
1490
|
const collection = configuration.types.find((candidate) => candidate.collection === input.collection);
|
|
1443
|
-
if (!collection) return yield* invalid$
|
|
1444
|
-
if (!collection.canCreate) return yield* invalid$
|
|
1491
|
+
if (!collection) return yield* invalid$2(`Document collection "${input.collection}" does not exist.`, "collection");
|
|
1492
|
+
if (!collection.canCreate) return yield* invalid$2(`Document collection "${input.collection}" does not allow creation.`, "collection");
|
|
1445
1493
|
const title = input.title.trim();
|
|
1446
|
-
if (!title) return yield* invalid$
|
|
1494
|
+
if (!title) return yield* invalid$2("A document title is required.", "title");
|
|
1447
1495
|
const locale = yield* resolveLocale(configuration, input.locale);
|
|
1448
1496
|
const address = yield* parseDocumentAddress(collection.addressPattern).pipe(Result.mapError(addressInvalid));
|
|
1449
1497
|
const relativePath = yield* resolveRelativePath(collection, input.location);
|
|
@@ -1485,10 +1533,18 @@ const createInitialDocumentSource = Effect.fn("DocumentPlanning.createInitialSou
|
|
|
1485
1533
|
function planDocumentMove(configuration, document, location) {
|
|
1486
1534
|
return Result.gen(function* () {
|
|
1487
1535
|
const collection = configuration.types.find((candidate) => candidate.collection === document.collection);
|
|
1488
|
-
if (!collection) return yield* invalid$
|
|
1536
|
+
if (!collection) return yield* invalid$2(`Document collection "${document.collection}" does not exist.`, "documentId");
|
|
1489
1537
|
return documentSourcePath(collection, yield* resolveRelativePath(collection, location), document.locale);
|
|
1490
1538
|
});
|
|
1491
1539
|
}
|
|
1540
|
+
/** Derives a collection-relative hierarchy path from a canonical document summary. */
|
|
1541
|
+
function documentRelativePath(collection, entry) {
|
|
1542
|
+
let sourcePath = entry.sourcePath;
|
|
1543
|
+
if (entry.document.locale && sourcePath.startsWith(`${entry.document.locale}/`)) sourcePath = sourcePath.slice(entry.document.locale.length + 1);
|
|
1544
|
+
if (collection.directory !== "." && sourcePath.startsWith(`${collection.directory}/`)) sourcePath = sourcePath.slice(collection.directory.length + 1);
|
|
1545
|
+
sourcePath = sourcePath.replace(/\.(?:md|mdc)$/iu, "");
|
|
1546
|
+
return sourcePath.toLocaleLowerCase() === "index" ? void 0 : sourcePath.replace(/\/index$/iu, "");
|
|
1547
|
+
}
|
|
1492
1548
|
function documentSourcePath(collection, relativePath, locale) {
|
|
1493
1549
|
const sourceName = relativePath ? `${relativePath}.md` : "index.md";
|
|
1494
1550
|
return SourcePath.make([
|
|
@@ -1518,13 +1574,13 @@ function setNestedValue(target, path, value) {
|
|
|
1518
1574
|
const isJsonObject$4 = Schema.is(Schema.JsonObject);
|
|
1519
1575
|
function resolveLocale(configuration, requested) {
|
|
1520
1576
|
const locales = configuration.locales;
|
|
1521
|
-
if (!locales) return requested === void 0 ? Result.succeed(void 0) : invalid$
|
|
1577
|
+
if (!locales) return requested === void 0 ? Result.succeed(void 0) : invalid$2(`Document locale "${requested}" is not configured.`, "locale");
|
|
1522
1578
|
const locale = requested ?? locales.defaultLocale;
|
|
1523
|
-
return locales.locales.includes(locale) ? Result.succeed(locale) : invalid$
|
|
1579
|
+
return locales.locales.includes(locale) ? Result.succeed(locale) : invalid$2(`Document locale "${locale}" does not exist.`, "locale");
|
|
1524
1580
|
}
|
|
1525
1581
|
function resolveRelativePath(collection, location) {
|
|
1526
|
-
if (location.kind === "home") return collection.addressPattern === "/:path*" ? Result.succeed("") : invalid$
|
|
1527
|
-
if (location.parent !== void 0 && !collection.hierarchy) return invalid$
|
|
1582
|
+
if (location.kind === "home") return collection.addressPattern === "/:path*" ? Result.succeed("") : invalid$2("Only a root catch-all collection can use the home location.", "location");
|
|
1583
|
+
if (location.parent !== void 0 && !collection.hierarchy) return invalid$2(`Document collection "${collection.collection}" does not allow hierarchy.`, "location.parent");
|
|
1528
1584
|
return normalizeDocumentRelativePath([location.parent, location.slug].filter((value) => value !== void 0).join("/")).pipe(Result.mapError((error) => addressInvalid({
|
|
1529
1585
|
...error,
|
|
1530
1586
|
field: "location"
|
|
@@ -1533,7 +1589,7 @@ function resolveRelativePath(collection, location) {
|
|
|
1533
1589
|
function addressInvalid(error) {
|
|
1534
1590
|
return new DocumentValidationFailed({ issues: [validationIssue(error.message, error.field)] });
|
|
1535
1591
|
}
|
|
1536
|
-
function invalid$
|
|
1592
|
+
function invalid$2(message, field) {
|
|
1537
1593
|
return Result.fail(new DocumentValidationFailed({ issues: [validationIssue(message, field)] }));
|
|
1538
1594
|
}
|
|
1539
1595
|
function validationIssue(message, field) {
|
|
@@ -1548,6 +1604,18 @@ function validationIssue(message, field) {
|
|
|
1548
1604
|
severity: "error"
|
|
1549
1605
|
};
|
|
1550
1606
|
}
|
|
1607
|
+
/** Updates route-owned frontmatter when relocating a page in a slug-addressed collection. */
|
|
1608
|
+
const relocateDocumentMarkdown = Effect.fn("DocumentPlanning.relocateMarkdown")(function* (configuration, document, location, markdown) {
|
|
1609
|
+
const collection = configuration.types.find((type) => type.collection === document.collection);
|
|
1610
|
+
if (!collection || !collection.addressPattern.includes(":slug") || location.kind === "home") return markdown;
|
|
1611
|
+
return yield* renderEditorMarkdown({
|
|
1612
|
+
...document.body,
|
|
1613
|
+
frontmatter: {
|
|
1614
|
+
...document.body.frontmatter,
|
|
1615
|
+
slug: location.slug
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
});
|
|
1551
1619
|
|
|
1552
1620
|
//#endregion
|
|
1553
1621
|
//#region ../server/src/internal/documents/document-state.ts
|
|
@@ -1633,6 +1701,7 @@ function authoringEntries(state, query) {
|
|
|
1633
1701
|
if (query.collection !== void 0 && document.collection !== query.collection) return [];
|
|
1634
1702
|
if (query.locale !== void 0 && document.locale !== query.locale) return [];
|
|
1635
1703
|
return [DocumentSummary.make({
|
|
1704
|
+
...browseMetadata(state, source),
|
|
1636
1705
|
document,
|
|
1637
1706
|
sourcePath: source.sourcePath,
|
|
1638
1707
|
version: source.version
|
|
@@ -1641,6 +1710,9 @@ function authoringEntries(state, query) {
|
|
|
1641
1710
|
}
|
|
1642
1711
|
function deletedEntries(state) {
|
|
1643
1712
|
return state.snapshot.deleted.map((source) => DeletedDocument.make({
|
|
1713
|
+
...source.title ? { title: source.title } : void 0,
|
|
1714
|
+
...source.path ? { path: source.path } : void 0,
|
|
1715
|
+
...source.trashBatchId ? { trashBatchId: source.trashBatchId } : void 0,
|
|
1644
1716
|
documentId: source.documentId,
|
|
1645
1717
|
deletedAt: source.deletedAt,
|
|
1646
1718
|
sourcePath: source.sourcePath,
|
|
@@ -1651,6 +1723,7 @@ function entryById(state, documentId) {
|
|
|
1651
1723
|
const source = state.activeById.get(documentId);
|
|
1652
1724
|
const document = state.authoredById.get(documentId);
|
|
1653
1725
|
return source && document ? DocumentSummary.make({
|
|
1726
|
+
...browseMetadata(state, source),
|
|
1654
1727
|
document,
|
|
1655
1728
|
sourcePath: source.sourcePath,
|
|
1656
1729
|
version: source.version
|
|
@@ -1668,12 +1741,189 @@ function compilationIssues(error) {
|
|
|
1668
1741
|
function errorMessage(cause) {
|
|
1669
1742
|
return cause instanceof Error && cause.message ? cause.message : "Markdown could not be parsed.";
|
|
1670
1743
|
}
|
|
1744
|
+
function browseMetadata(state, source) {
|
|
1745
|
+
const live = state.published.documents.find((document) => document.id === source.documentId);
|
|
1746
|
+
const metadata = {};
|
|
1747
|
+
if (source.createdBy) metadata.createdBy = source.createdBy;
|
|
1748
|
+
if (source.editedBy) metadata.editedBy = source.editedBy;
|
|
1749
|
+
if (source.editedAt) metadata.editedAt = source.editedAt;
|
|
1750
|
+
if (source.publishedVersion) metadata.publishedVersion = source.publishedVersion;
|
|
1751
|
+
if (live) metadata.publishedPath = live.path;
|
|
1752
|
+
return metadata;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
//#endregion
|
|
1756
|
+
//#region ../server/src/internal/documents/document-tree-mutation.ts
|
|
1757
|
+
/** Selects by route boundaries, never by title or a raw prefix such as /about matching /about-us. */
|
|
1758
|
+
function subtreeIds(state, documentId) {
|
|
1759
|
+
const parent = state.authoredById.get(documentId);
|
|
1760
|
+
if (!parent) return [documentId];
|
|
1761
|
+
return state.authored.documents.filter((page) => page.id === parent.id || parent.key !== "/" && page.collection === parent.collection && page.locale === parent.locale && page.key.startsWith(`${parent.key}/`)).map((page) => page.id);
|
|
1762
|
+
}
|
|
1763
|
+
const prepareTreeMutation = Effect.fn("Documents.prepareTreeMutation")(function* (dependencies, state, command) {
|
|
1764
|
+
const { configuration, engine } = dependencies;
|
|
1765
|
+
const input = command.input;
|
|
1766
|
+
const root = command.action === "document.undelete" ? state.snapshot.deleted.find((source) => source.documentId === input.documentId) : state.activeById.get(input.documentId);
|
|
1767
|
+
if (!root) return yield* new DocumentNotFound({ documentId: input.documentId });
|
|
1768
|
+
if (root.version !== input.expectedVersion) return yield* new DocumentVersionConflict({
|
|
1769
|
+
documentId: root.documentId,
|
|
1770
|
+
actualVersion: root.version,
|
|
1771
|
+
expectedVersion: input.expectedVersion
|
|
1772
|
+
});
|
|
1773
|
+
let sources;
|
|
1774
|
+
if (command.action === "document.undelete") {
|
|
1775
|
+
const deleted = state.snapshot.deleted.find((source) => source.documentId === root.documentId);
|
|
1776
|
+
const prefix = root.sourcePath.replace(/(?:\/index)?\.(?:md|mdc)$/iu, "");
|
|
1777
|
+
sources = state.snapshot.deleted.filter((source) => source.documentId === root.documentId || deleted && deleted.trashBatchId && source.trashBatchId === deleted.trashBatchId && source.sourcePath.startsWith(`${prefix}/`));
|
|
1778
|
+
} else {
|
|
1779
|
+
const ids = new Set(command.action === "document.duplicate" && !command.input.includeChildren ? [root.documentId] : subtreeIds(state, root.documentId));
|
|
1780
|
+
sources = state.snapshot.active.filter((source) => ids.has(source.documentId));
|
|
1781
|
+
}
|
|
1782
|
+
sources = [root, ...sources.filter((source) => source.documentId !== root.documentId)];
|
|
1783
|
+
const plans = [];
|
|
1784
|
+
const parent = state.authoredById.get(root.documentId);
|
|
1785
|
+
for (const source of sources) {
|
|
1786
|
+
if (command.action === "document.remove" || command.action === "document.undelete") {
|
|
1787
|
+
const page = state.authoredById.get(source.documentId);
|
|
1788
|
+
plans.push({
|
|
1789
|
+
...command.action === "document.remove" ? {
|
|
1790
|
+
trashBatchId: root.version,
|
|
1791
|
+
title: page?.title,
|
|
1792
|
+
path: page?.path
|
|
1793
|
+
} : void 0,
|
|
1794
|
+
action: command.action,
|
|
1795
|
+
documentId: source.documentId,
|
|
1796
|
+
expectedVersion: source.version,
|
|
1797
|
+
markdown: source.markdown,
|
|
1798
|
+
sourcePath: source.sourcePath
|
|
1799
|
+
});
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1802
|
+
const page = state.authoredById.get(source.documentId);
|
|
1803
|
+
const collection = configuration.types.find((type) => type.collection === page?.collection);
|
|
1804
|
+
if (!page || !parent || !collection) return yield* new DocumentNotFound({ documentId: source.documentId });
|
|
1805
|
+
if (command.action === "document.duplicate" && !collection.canCreate) return yield* invalid$1("This collection does not allow new pages.");
|
|
1806
|
+
const destination = command.input.location;
|
|
1807
|
+
if (parent.key === "/" && command.action === "document.move") return yield* invalid$1("The home page cannot be moved.");
|
|
1808
|
+
const rootRelative = documentRelativePath(collection, {
|
|
1809
|
+
document: parent,
|
|
1810
|
+
sourcePath: root.sourcePath
|
|
1811
|
+
}) ?? "";
|
|
1812
|
+
const nextRelative = destination.kind === "home" ? "" : [destination.parent, destination.slug].filter(Boolean).join("/");
|
|
1813
|
+
if (rootRelative && (nextRelative === rootRelative || nextRelative.startsWith(`${rootRelative}/`))) return yield* invalid$1("Choose a destination outside this page and its subpages.");
|
|
1814
|
+
const relative = documentRelativePath(collection, {
|
|
1815
|
+
document: page,
|
|
1816
|
+
sourcePath: source.sourcePath
|
|
1817
|
+
}) ?? "";
|
|
1818
|
+
const segments = `${nextRelative}${source.documentId === root.documentId ? "" : relative.slice(rootRelative.length)}`.split("/").filter(Boolean);
|
|
1819
|
+
const slug = segments.pop();
|
|
1820
|
+
const location = slug ? Object.assign({
|
|
1821
|
+
kind: "entry",
|
|
1822
|
+
slug: yield* decodeSlug(slug)
|
|
1823
|
+
}, segments.length ? { parent: yield* decodeParent(segments.join("/")) } : void 0) : { kind: "home" };
|
|
1824
|
+
const sourcePath = yield* Effect.fromResult(planDocumentMove(configuration, page, location));
|
|
1825
|
+
const markdown = yield* relocateDocumentMarkdown(configuration, page, location, source.markdown).pipe(Effect.mapError(() => invalid$1("The page could not be copied to its new URL.")));
|
|
1826
|
+
if (command.action === "document.duplicate") {
|
|
1827
|
+
const documentId = yield* command.nextDocumentId;
|
|
1828
|
+
plans.push({
|
|
1829
|
+
action: "document.create",
|
|
1830
|
+
documentId,
|
|
1831
|
+
markdown,
|
|
1832
|
+
sourcePath
|
|
1833
|
+
});
|
|
1834
|
+
} else plans.push({
|
|
1835
|
+
action: "document.move",
|
|
1836
|
+
documentId: source.documentId,
|
|
1837
|
+
expectedVersion: source.version,
|
|
1838
|
+
markdown,
|
|
1839
|
+
sourcePath,
|
|
1840
|
+
publishedVersion: source.publishedVersion
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
const changed = new Set(plans.map((plan) => plan.documentId));
|
|
1844
|
+
const nextSources = authoredSources(state.snapshot).filter((source) => !changed.has(source.documentId));
|
|
1845
|
+
if (command.action !== "document.remove") for (const plan of plans) {
|
|
1846
|
+
if (nextSources.some((source) => source.sourcePath === plan.sourcePath)) return yield* new DocumentPathConflict({ sourcePath: plan.sourcePath });
|
|
1847
|
+
nextSources.push({
|
|
1848
|
+
documentId: plan.documentId,
|
|
1849
|
+
draft: true,
|
|
1850
|
+
markdown: plan.markdown,
|
|
1851
|
+
sourcePath: plan.sourcePath
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
if (command.action === "document.move" || command.action === "document.duplicate") {
|
|
1855
|
+
const preliminary = yield* engine.compileAll({
|
|
1856
|
+
sources: nextSources,
|
|
1857
|
+
previous: state.authored
|
|
1858
|
+
});
|
|
1859
|
+
const destinations = /* @__PURE__ */ new Map();
|
|
1860
|
+
for (let index = 0; index < sources.length; index++) {
|
|
1861
|
+
const original = sources[index];
|
|
1862
|
+
const plan = plans[index];
|
|
1863
|
+
const oldPage = original && state.authoredById.get(original.documentId);
|
|
1864
|
+
const newPage = plan && preliminary.documents.find((document) => document.id === plan.documentId);
|
|
1865
|
+
if (oldPage && newPage) destinations.set(oldPage.path, newPage.path);
|
|
1866
|
+
}
|
|
1867
|
+
const originals = command.action === "document.move" ? state.snapshot.active : sources;
|
|
1868
|
+
for (const source of originals) {
|
|
1869
|
+
const page = state.authoredById.get(source.documentId);
|
|
1870
|
+
if (!page) continue;
|
|
1871
|
+
const body = rewriteDocumentRoutes(page, destinations, dependencies.components);
|
|
1872
|
+
if (!body) continue;
|
|
1873
|
+
const index = command.action === "document.duplicate" ? sources.findIndex((item) => item.documentId === source.documentId) : plans.findIndex((plan) => plan.documentId === source.documentId);
|
|
1874
|
+
const existing = index >= 0 ? plans[index] : void 0;
|
|
1875
|
+
const projected = existing && preliminary.documents.find((document) => document.id === existing.documentId);
|
|
1876
|
+
const markdown = yield* renderEditorMarkdown(Object.assign({}, body, { frontmatter: projected?.body.frontmatter ?? body.frontmatter })).pipe(Effect.mapError(() => invalid$1("Page links could not be updated.")));
|
|
1877
|
+
if (existing) plans[index] = {
|
|
1878
|
+
...existing,
|
|
1879
|
+
markdown
|
|
1880
|
+
};
|
|
1881
|
+
else plans.push({
|
|
1882
|
+
action: "document.update",
|
|
1883
|
+
documentId: source.documentId,
|
|
1884
|
+
expectedVersion: source.version,
|
|
1885
|
+
sourcePath: source.sourcePath,
|
|
1886
|
+
markdown,
|
|
1887
|
+
publishedVersion: source.publishedVersion
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
for (const plan of plans) {
|
|
1891
|
+
const index = nextSources.findIndex((source) => source.documentId === plan.documentId);
|
|
1892
|
+
const updated = {
|
|
1893
|
+
documentId: plan.documentId,
|
|
1894
|
+
sourcePath: plan.sourcePath,
|
|
1895
|
+
markdown: plan.markdown,
|
|
1896
|
+
draft: true
|
|
1897
|
+
};
|
|
1898
|
+
if (index >= 0) nextSources[index] = updated;
|
|
1899
|
+
else nextSources.push(updated);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
const first = plans[0];
|
|
1903
|
+
if (!first) return yield* Effect.die(/* @__PURE__ */ new Error("Empty subtree mutation."));
|
|
1904
|
+
return {
|
|
1905
|
+
authored: yield* compileValidCatalog(engine, nextSources, state.authored),
|
|
1906
|
+
published: command.action === "document.remove" ? yield* compileValidCatalog(engine, publishedSources(state.snapshot).filter((source) => !changed.has(source.documentId)), state.published) : state.published,
|
|
1907
|
+
commitPlan: first,
|
|
1908
|
+
additionalPlans: plans.slice(1)
|
|
1909
|
+
};
|
|
1910
|
+
});
|
|
1911
|
+
const decodeSlug = (value) => Schema.decodeEffect(Slug)(value).pipe(Effect.mapError(() => invalid$1("Invalid URL ending.")));
|
|
1912
|
+
const decodeParent = (value) => Schema.decodeEffect(RelativeDocumentPath)(value).pipe(Effect.mapError(() => invalid$1("Invalid parent URL.")));
|
|
1913
|
+
function invalid$1(message) {
|
|
1914
|
+
return new DocumentValidationFailed({ issues: [{
|
|
1915
|
+
code: "document.invalid",
|
|
1916
|
+
severity: "error",
|
|
1917
|
+
message
|
|
1918
|
+
}] });
|
|
1919
|
+
}
|
|
1671
1920
|
|
|
1672
1921
|
//#endregion
|
|
1673
1922
|
//#region ../server/src/internal/documents/document-lifecycle.ts
|
|
1674
1923
|
const PreparedDocumentMutation = Data.taggedEnum();
|
|
1675
1924
|
const prepareDocumentMutation = Effect.fn("DocumentLifecycle.prepare")(function* (dependencies, state, command) {
|
|
1676
|
-
const { engine,
|
|
1925
|
+
const { engine, repository } = dependencies;
|
|
1926
|
+
if (command.action === "document.move" || command.action === "document.remove" || command.action === "document.undelete" || command.action === "document.duplicate") return PreparedDocumentMutation.Commit(yield* prepareTreeMutation(dependencies, state, command));
|
|
1677
1927
|
const authored = authoredSources(state.snapshot);
|
|
1678
1928
|
const published = publishedSources(state.snapshot);
|
|
1679
1929
|
switch (command.action) {
|
|
@@ -1718,47 +1968,6 @@ const prepareDocumentMutation = Effect.fn("DocumentLifecycle.prepare")(function*
|
|
|
1718
1968
|
}
|
|
1719
1969
|
});
|
|
1720
1970
|
}
|
|
1721
|
-
case "document.move": {
|
|
1722
|
-
const current = yield* requireActive(state, command.input.documentId);
|
|
1723
|
-
yield* requireVersion(current, command.input.expectedVersion);
|
|
1724
|
-
const entry = entryById(state, current.documentId);
|
|
1725
|
-
if (!entry) return yield* Effect.die(/* @__PURE__ */ new Error("Active document is missing from the authored catalog."));
|
|
1726
|
-
const sourcePath = yield* Effect.fromResult(planDocumentMove(configuration, entry.document, command.input.location));
|
|
1727
|
-
yield* ensurePathAvailable(state, sourcePath, current.documentId);
|
|
1728
|
-
const nextAuthored = replaceSource(authored, {
|
|
1729
|
-
documentId: current.documentId,
|
|
1730
|
-
draft: true,
|
|
1731
|
-
markdown: current.markdown,
|
|
1732
|
-
sourcePath
|
|
1733
|
-
});
|
|
1734
|
-
return PreparedDocumentMutation.Commit({
|
|
1735
|
-
authored: yield* compileValidCatalog(engine, nextAuthored, state.authored),
|
|
1736
|
-
published: state.published,
|
|
1737
|
-
commitPlan: {
|
|
1738
|
-
action: command.action,
|
|
1739
|
-
documentId: current.documentId,
|
|
1740
|
-
expectedVersion: command.input.expectedVersion,
|
|
1741
|
-
markdown: current.markdown,
|
|
1742
|
-
publishedVersion: current.publishedVersion,
|
|
1743
|
-
sourcePath
|
|
1744
|
-
}
|
|
1745
|
-
});
|
|
1746
|
-
}
|
|
1747
|
-
case "document.remove": {
|
|
1748
|
-
const current = yield* requireActive(state, command.input.documentId);
|
|
1749
|
-
yield* requireVersion(current, command.input.expectedVersion);
|
|
1750
|
-
return PreparedDocumentMutation.Commit({
|
|
1751
|
-
authored: yield* compileValidCatalog(engine, withoutDocument(authored, current.documentId), state.authored),
|
|
1752
|
-
published: yield* compileValidCatalog(engine, withoutDocument(published, current.documentId), state.published),
|
|
1753
|
-
commitPlan: {
|
|
1754
|
-
action: command.action,
|
|
1755
|
-
documentId: current.documentId,
|
|
1756
|
-
expectedVersion: command.input.expectedVersion,
|
|
1757
|
-
markdown: current.markdown,
|
|
1758
|
-
sourcePath: current.sourcePath
|
|
1759
|
-
}
|
|
1760
|
-
});
|
|
1761
|
-
}
|
|
1762
1971
|
case "document.restore": {
|
|
1763
1972
|
const current = yield* requireActive(state, command.input.documentId);
|
|
1764
1973
|
yield* requireVersion(current, command.input.expectedVersion);
|
|
@@ -1783,25 +1992,24 @@ const prepareDocumentMutation = Effect.fn("DocumentLifecycle.prepare")(function*
|
|
|
1783
1992
|
}
|
|
1784
1993
|
});
|
|
1785
1994
|
}
|
|
1786
|
-
case "document.
|
|
1787
|
-
const
|
|
1788
|
-
yield* requireVersion(
|
|
1789
|
-
yield* ensurePathAvailable(state, deleted.sourcePath);
|
|
1790
|
-
const nextAuthored = [...authored, {
|
|
1791
|
-
documentId: deleted.documentId,
|
|
1792
|
-
draft: true,
|
|
1793
|
-
markdown: deleted.markdown,
|
|
1794
|
-
sourcePath: deleted.sourcePath
|
|
1795
|
-
}];
|
|
1995
|
+
case "document.unpublish": {
|
|
1996
|
+
const current = yield* requireActive(state, command.input.documentId);
|
|
1997
|
+
yield* requireVersion(current, command.input.expectedVersion);
|
|
1796
1998
|
return PreparedDocumentMutation.Commit({
|
|
1797
|
-
authored: yield* compileValidCatalog(engine,
|
|
1798
|
-
|
|
1999
|
+
authored: yield* compileValidCatalog(engine, replaceSource(authored, {
|
|
2000
|
+
documentId: current.documentId,
|
|
2001
|
+
draft: true,
|
|
2002
|
+
markdown: current.markdown,
|
|
2003
|
+
sourcePath: current.sourcePath
|
|
2004
|
+
}), state.authored),
|
|
2005
|
+
published: yield* compileValidCatalog(engine, withoutDocument(published, current.documentId), state.published),
|
|
1799
2006
|
commitPlan: {
|
|
1800
2007
|
action: command.action,
|
|
1801
|
-
documentId:
|
|
1802
|
-
expectedVersion:
|
|
1803
|
-
markdown:
|
|
1804
|
-
sourcePath:
|
|
2008
|
+
documentId: current.documentId,
|
|
2009
|
+
expectedVersion: current.version,
|
|
2010
|
+
markdown: current.markdown,
|
|
2011
|
+
sourcePath: current.sourcePath,
|
|
2012
|
+
revisionId: current.revisionId
|
|
1805
2013
|
}
|
|
1806
2014
|
});
|
|
1807
2015
|
}
|
|
@@ -1812,6 +2020,7 @@ const prepareDocumentMutation = Effect.fn("DocumentLifecycle.prepare")(function*
|
|
|
1812
2020
|
const entry = entryById(state, current.documentId);
|
|
1813
2021
|
const result = {
|
|
1814
2022
|
action: "document.publish",
|
|
2023
|
+
affectedDocumentIds: [current.documentId],
|
|
1815
2024
|
documentId: current.documentId,
|
|
1816
2025
|
generation: state.snapshot.generation,
|
|
1817
2026
|
revisionId: current.revisionId,
|
|
@@ -1856,10 +2065,6 @@ function requireActive(state, documentId) {
|
|
|
1856
2065
|
const source = state.activeById.get(documentId);
|
|
1857
2066
|
return source ? Effect.succeed(source) : Effect.fail(new DocumentNotFound({ documentId }));
|
|
1858
2067
|
}
|
|
1859
|
-
function requireDeleted(state, documentId) {
|
|
1860
|
-
const source = state.snapshot.deleted.find((candidate) => candidate.documentId === documentId);
|
|
1861
|
-
return source ? Effect.succeed(source) : Effect.fail(new DocumentNotFound({ documentId }));
|
|
1862
|
-
}
|
|
1863
2068
|
function requireVersion(source, expectedVersion) {
|
|
1864
2069
|
return source.version === expectedVersion ? Effect.void : Effect.fail(new DocumentVersionConflict({
|
|
1865
2070
|
actualVersion: source.version,
|
|
@@ -2101,24 +2306,24 @@ const compareLines = Effect.fn("Documents.history.compareLines")(function* (befo
|
|
|
2101
2306
|
});
|
|
2102
2307
|
|
|
2103
2308
|
//#endregion
|
|
2104
|
-
//#region ../server/src/internal/documents/
|
|
2105
|
-
function
|
|
2106
|
-
const read = Effect.fn("Documents.
|
|
2107
|
-
const document =
|
|
2309
|
+
//#region ../server/src/internal/documents/document-delivery.ts
|
|
2310
|
+
function documentDelivery(configuration, catalog) {
|
|
2311
|
+
const read = Effect.fn("Documents.delivery.read")(function* (lookup) {
|
|
2312
|
+
const document = findDocument(yield* catalog, lookup);
|
|
2108
2313
|
return Option.isSome(document) ? document.value : yield* new PublishedDocumentNotFound({ lookup });
|
|
2109
2314
|
});
|
|
2110
|
-
const query = Effect.fn("Documents.
|
|
2315
|
+
const query = Effect.fn("Documents.delivery.query")(function* (input) {
|
|
2111
2316
|
return queryDocuments((yield* catalog).documents, input, {
|
|
2112
2317
|
limit: input.limit,
|
|
2113
2318
|
offset: input.offset ?? 0
|
|
2114
2319
|
});
|
|
2115
2320
|
});
|
|
2116
|
-
const navigation = Effect.fn("Documents.
|
|
2321
|
+
const navigation = Effect.fn("Documents.delivery.navigation")(function* (input) {
|
|
2117
2322
|
return createNavigation((yield* catalog).documents, input, configuration);
|
|
2118
2323
|
});
|
|
2119
|
-
const surround = Effect.fn("Documents.
|
|
2324
|
+
const surround = Effect.fn("Documents.delivery.surround")(function* (lookup, input) {
|
|
2120
2325
|
const current = yield* catalog;
|
|
2121
|
-
const document =
|
|
2326
|
+
const document = findDocument(current, lookup);
|
|
2122
2327
|
if (Option.isNone(document)) return yield* new PublishedDocumentNotFound({ lookup });
|
|
2123
2328
|
const surroundQuery = Object.assign({}, { collection: input.collection ?? document.value.collection }, input.locale === void 0 && document.value.locale === void 0 ? void 0 : { locale: input.locale ?? document.value.locale }, input.where === void 0 ? void 0 : { where: input.where }, input.sort === void 0 ? void 0 : { sort: input.sort });
|
|
2124
2329
|
const page = queryDocuments(current.documents, surroundQuery);
|
|
@@ -2136,7 +2341,7 @@ function publishedDocuments(configuration, catalog) {
|
|
|
2136
2341
|
surround
|
|
2137
2342
|
};
|
|
2138
2343
|
}
|
|
2139
|
-
function
|
|
2344
|
+
function findDocument(catalog, lookup) {
|
|
2140
2345
|
switch (lookup.kind) {
|
|
2141
2346
|
case "id": return Option.fromNullishOr(catalog.documents.find((document) => document.id === lookup.documentId));
|
|
2142
2347
|
case "path": return catalog.findByPath(lookup.path);
|
|
@@ -2188,7 +2393,8 @@ function createNavigation(documents, query, configuration) {
|
|
|
2188
2393
|
const resolved = navigationItem(document);
|
|
2189
2394
|
item.title = resolved.title;
|
|
2190
2395
|
item.path = resolved.path;
|
|
2191
|
-
item.locale = resolved.locale;
|
|
2396
|
+
if (resolved.locale !== void 0) item.locale = resolved.locale;
|
|
2397
|
+
else delete item.locale;
|
|
2192
2398
|
item.documentId = resolved.documentId;
|
|
2193
2399
|
orderByItem.set(item, navigationOrder(document));
|
|
2194
2400
|
}
|
|
@@ -2201,14 +2407,13 @@ function createNavigation(documents, query, configuration) {
|
|
|
2201
2407
|
function navigationItem(document) {
|
|
2202
2408
|
const navigation = document.data.navigation;
|
|
2203
2409
|
const configuredTitle = Schema.is(Schema.JsonObject)(navigation) ? navigation.title : void 0;
|
|
2204
|
-
return {
|
|
2410
|
+
return Object.assign({
|
|
2205
2411
|
children: [],
|
|
2206
2412
|
documentId: document.id,
|
|
2207
2413
|
key: document.key,
|
|
2208
|
-
locale: document.locale,
|
|
2209
2414
|
path: document.path,
|
|
2210
2415
|
title: Schema.is(Schema.String)(configuredTitle) && configuredTitle.trim() ? configuredTitle.trim() : document.title
|
|
2211
|
-
};
|
|
2416
|
+
}, document.locale === void 0 ? void 0 : { locale: document.locale });
|
|
2212
2417
|
}
|
|
2213
2418
|
function navigationOrder(document) {
|
|
2214
2419
|
const navigation = document.data.navigation;
|
|
@@ -2286,7 +2491,8 @@ const makeDocuments = Effect.fn("Documents.make")(function* (configuration, opti
|
|
|
2286
2491
|
const dependencies = {
|
|
2287
2492
|
engine,
|
|
2288
2493
|
configuration,
|
|
2289
|
-
repository
|
|
2494
|
+
repository,
|
|
2495
|
+
components: options.components
|
|
2290
2496
|
};
|
|
2291
2497
|
const refreshUnlocked = Effect.fn("Documents.refreshUnlocked")(function* (force = false) {
|
|
2292
2498
|
const current = yield* Ref.get(state);
|
|
@@ -2300,13 +2506,63 @@ const makeDocuments = Effect.fn("Documents.make")(function* (configuration, opti
|
|
|
2300
2506
|
return yield* Effect.gen(function* () {
|
|
2301
2507
|
yield* refreshUnlocked();
|
|
2302
2508
|
const current = yield* Ref.get(state);
|
|
2509
|
+
if (command.action === "document.move" || command.action === "document.remove" || command.action === "document.duplicate") {
|
|
2510
|
+
const ids = command.action === "document.duplicate" && !command.input.includeChildren ? [command.input.documentId] : subtreeIds(current, command.input.documentId);
|
|
2511
|
+
for (const documentId of ids) yield* accessControl.require({
|
|
2512
|
+
actor: activity.actor,
|
|
2513
|
+
requestId: activity.requestId
|
|
2514
|
+
}, {
|
|
2515
|
+
action: command.action,
|
|
2516
|
+
resource: {
|
|
2517
|
+
kind: "document",
|
|
2518
|
+
documentId
|
|
2519
|
+
}
|
|
2520
|
+
});
|
|
2521
|
+
if (command.action === "document.duplicate") {
|
|
2522
|
+
const source = current.authoredById.get(command.input.documentId);
|
|
2523
|
+
if (source) yield* accessControl.require({
|
|
2524
|
+
actor: activity.actor,
|
|
2525
|
+
requestId: activity.requestId
|
|
2526
|
+
}, {
|
|
2527
|
+
action: "document.create",
|
|
2528
|
+
resource: {
|
|
2529
|
+
kind: "collection",
|
|
2530
|
+
collection: source.collection,
|
|
2531
|
+
locale: source.locale
|
|
2532
|
+
}
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2303
2536
|
const prepared = yield* prepareDocumentMutation(dependencies, current, command);
|
|
2537
|
+
if (command.action === "document.move" && prepared._tag === "Commit") {
|
|
2538
|
+
for (const plan of prepared.additionalPlans ?? []) if (plan.action === "document.update") yield* accessControl.require({
|
|
2539
|
+
actor: activity.actor,
|
|
2540
|
+
requestId: activity.requestId
|
|
2541
|
+
}, {
|
|
2542
|
+
action: "document.update",
|
|
2543
|
+
resource: {
|
|
2544
|
+
kind: "document",
|
|
2545
|
+
documentId: plan.documentId
|
|
2546
|
+
}
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
if (command.action === "document.undelete" && prepared._tag === "Commit") for (const plan of [prepared.commitPlan, ...prepared.additionalPlans ?? []]) yield* accessControl.require({
|
|
2550
|
+
actor: activity.actor,
|
|
2551
|
+
requestId: activity.requestId
|
|
2552
|
+
}, {
|
|
2553
|
+
action: "document.undelete",
|
|
2554
|
+
resource: {
|
|
2555
|
+
kind: "document",
|
|
2556
|
+
documentId: plan.documentId
|
|
2557
|
+
}
|
|
2558
|
+
});
|
|
2304
2559
|
if (prepared._tag === "Unchanged") return prepared.result;
|
|
2305
2560
|
const committed = yield* repository.commit({
|
|
2306
2561
|
activity,
|
|
2307
2562
|
plan: prepared.commitPlan,
|
|
2563
|
+
additionalPlans: prepared.additionalPlans,
|
|
2308
2564
|
expectedGeneration: current.snapshot.generation,
|
|
2309
|
-
mediaReferences: projectMediaReferences(prepared.authored, prepared.published,
|
|
2565
|
+
mediaReferences: [prepared.commitPlan, ...prepared.additionalPlans ?? []].flatMap((plan) => projectMediaReferences(prepared.authored, prepared.published, plan.documentId))
|
|
2310
2566
|
});
|
|
2311
2567
|
const next = makeDocumentState(prepared.authored, prepared.published, committed.snapshot);
|
|
2312
2568
|
yield* Ref.set(state, next);
|
|
@@ -2342,10 +2598,6 @@ const makeDocuments = Effect.fn("Documents.make")(function* (configuration, opti
|
|
|
2342
2598
|
sourcePath: plan.sourcePath
|
|
2343
2599
|
}, activity, input.reason);
|
|
2344
2600
|
});
|
|
2345
|
-
const generation = semaphore.withPermits(1)(Effect.gen(function* () {
|
|
2346
|
-
yield* refreshUnlocked();
|
|
2347
|
-
return (yield* Ref.get(state)).snapshot.generation;
|
|
2348
|
-
}));
|
|
2349
2601
|
const importSources = (input, activity) => importDocumentSources({
|
|
2350
2602
|
current: (sourcePath) => semaphore.withPermits(1)(Effect.gen(function* () {
|
|
2351
2603
|
yield* refreshUnlocked();
|
|
@@ -2354,6 +2606,15 @@ const makeDocuments = Effect.fn("Documents.make")(function* (configuration, opti
|
|
|
2354
2606
|
nextDocumentId: crypto.randomUUIDv4.pipe(Effect.orDie, Effect.map((value) => DocumentId.make(value))),
|
|
2355
2607
|
runMutation
|
|
2356
2608
|
}, input, activity);
|
|
2609
|
+
const duplicate = Effect.fn("Documents.duplicate")((input, activity) => runMutation({
|
|
2610
|
+
action: "document.duplicate",
|
|
2611
|
+
input,
|
|
2612
|
+
nextDocumentId: crypto.randomUUIDv4.pipe(Effect.orDie, Effect.map((value) => DocumentId.make(value)))
|
|
2613
|
+
}, activity, input.reason));
|
|
2614
|
+
const unpublish = Effect.fn("Documents.unpublish")((input, activity) => runMutation({
|
|
2615
|
+
action: "document.unpublish",
|
|
2616
|
+
input
|
|
2617
|
+
}, activity, input.reason));
|
|
2357
2618
|
const move = Effect.fn("Documents.move")((input, activity) => runMutation({
|
|
2358
2619
|
action: "document.move",
|
|
2359
2620
|
input
|
|
@@ -2404,7 +2665,7 @@ const makeDocuments = Effect.fn("Documents.make")(function* (configuration, opti
|
|
|
2404
2665
|
return yield* validateDocumentUpdate(engine, yield* Ref.get(state), input);
|
|
2405
2666
|
})));
|
|
2406
2667
|
const history = documentHistoryCapability(accessControl, repository);
|
|
2407
|
-
const delivery =
|
|
2668
|
+
const delivery = documentDelivery(configuration, published);
|
|
2408
2669
|
const document = (documentId) => ({
|
|
2409
2670
|
documentId,
|
|
2410
2671
|
kind: "document"
|
|
@@ -2427,6 +2688,8 @@ const makeDocuments = Effect.fn("Documents.make")(function* (configuration, opti
|
|
|
2427
2688
|
historyCompare: history.compare,
|
|
2428
2689
|
historyList: history.list,
|
|
2429
2690
|
historyRead: history.read,
|
|
2691
|
+
duplicate: (context, input) => requireThen(context, "document.duplicate", document(input.documentId), () => duplicate(input, requestActivity(context, input.reason))),
|
|
2692
|
+
unpublish: (context, input) => requireThen(context, "document.unpublish", document(input.documentId), () => unpublish(input, requestActivity(context, input.reason))),
|
|
2430
2693
|
move: (context, input) => requireThen(context, "document.move", document(input.documentId), () => move(input, requestActivity(context, input.reason))),
|
|
2431
2694
|
publish: (context, input) => requireThen(context, "document.publish", document(input.documentId), () => publish(input, requestActivity(context, input.reason))),
|
|
2432
2695
|
read: (context, documentId) => requireThen(context, "document.read", document(documentId), () => read(documentId)),
|
|
@@ -2437,9 +2700,12 @@ const makeDocuments = Effect.fn("Documents.make")(function* (configuration, opti
|
|
|
2437
2700
|
update: (context, input) => requireThen(context, "document.update", document(input.documentId), () => update(input, requestActivity(context, input.reason))),
|
|
2438
2701
|
validate: (context, input) => requireThen(context, "document.validate", document(input.documentId), () => validate(input))
|
|
2439
2702
|
});
|
|
2703
|
+
const draftDelivery = documentDelivery(configuration, authored);
|
|
2440
2704
|
const previews = DocumentPreviews.of({
|
|
2441
|
-
|
|
2442
|
-
|
|
2705
|
+
read: (context, lookup) => requireThen(context, "document.browse", { kind: "catalog" }, () => draftDelivery.read(lookup)),
|
|
2706
|
+
query: (context, query) => requireThen(context, "document.browse", { kind: "catalog" }, () => draftDelivery.query(query)),
|
|
2707
|
+
navigation: (context, query) => requireThen(context, "document.browse", { kind: "catalog" }, () => draftDelivery.navigation(query)),
|
|
2708
|
+
surround: (context, lookup, query) => requireThen(context, "document.browse", { kind: "catalog" }, () => draftDelivery.surround(lookup, query))
|
|
2443
2709
|
});
|
|
2444
2710
|
const publishedService = PublishedDocuments.of({
|
|
2445
2711
|
navigation: delivery.navigation,
|
|
@@ -2496,9 +2762,6 @@ function loadSynchronizedState(repository, engine) {
|
|
|
2496
2762
|
operation: "Documents.synchronizeMediaReferences"
|
|
2497
2763
|
}) : error));
|
|
2498
2764
|
}
|
|
2499
|
-
function commandDocumentId(command) {
|
|
2500
|
-
return command.action === "document.create" ? command.documentId : command.input.documentId;
|
|
2501
|
-
}
|
|
2502
2765
|
function generationIsAfter(candidate, current) {
|
|
2503
2766
|
return BigInt(candidate) > BigInt(current);
|
|
2504
2767
|
}
|
|
@@ -3220,7 +3483,7 @@ function extractGranularState(content) {
|
|
|
3220
3483
|
const nodeId = optionalEditorNodeId(node.attrs?.cmsNodeId);
|
|
3221
3484
|
if (nodeId === void 0) return;
|
|
3222
3485
|
const values = Object.assign({}, prefixValues(optionalJsonObject(node.attrs?.props), PROPS_PREFIX), prefixValues(optionalJsonObject(node.attrs?.cmsProps), CMS_PROPS_PREFIX));
|
|
3223
|
-
if (Object.
|
|
3486
|
+
if (Object.hasOwn(node.attrs ?? {}, "props") || Object.hasOwn(node.attrs ?? {}, "cmsProps")) properties.set(nodeId, values);
|
|
3224
3487
|
});
|
|
3225
3488
|
return {
|
|
3226
3489
|
frontmatter,
|
|
@@ -3245,7 +3508,7 @@ function flatten(value) {
|
|
|
3245
3508
|
}
|
|
3246
3509
|
function unflatten(value) {
|
|
3247
3510
|
const result = {};
|
|
3248
|
-
for (const [encodedPath, child] of Object.entries(value)) {
|
|
3511
|
+
for (const [encodedPath, child] of Object.entries(value).sort(([left], [right]) => decodePath(JSON.parse(left)).length - decodePath(JSON.parse(right)).length || left.localeCompare(right))) {
|
|
3249
3512
|
const path = decodePath(JSON.parse(encodedPath));
|
|
3250
3513
|
if (path.length === 0) continue;
|
|
3251
3514
|
let current = result;
|
|
@@ -3268,10 +3531,10 @@ function mapValues(map) {
|
|
|
3268
3531
|
return decodeJsonObject$1(Object.fromEntries([...map.entries()].map(([key, value]) => [key, structuredClone(decodeJson$1(value))])));
|
|
3269
3532
|
}
|
|
3270
3533
|
function prefixValues(value, prefix) {
|
|
3271
|
-
return decodeJsonObject$1(Object.fromEntries(Object.entries(value).map(([key, child]) => [`${prefix}${key}`, structuredClone(child)])));
|
|
3534
|
+
return decodeJsonObject$1(Object.fromEntries(Object.entries(flatten(encodeLists(value))).map(([key, child]) => [`${prefix}${key}`, structuredClone(child)])));
|
|
3272
3535
|
}
|
|
3273
3536
|
function prefixedValues(value, prefix) {
|
|
3274
|
-
return decodeJsonObject$1(Object.fromEntries(Object.entries(value).filter(([key]) => key.startsWith(prefix)).map(([key, child]) => [key.slice(prefix.length), structuredClone(child)])));
|
|
3537
|
+
return decodeJsonObject$1(decodeLists(unflatten(Object.fromEntries(Object.entries(value).filter(([key]) => key.startsWith(prefix)).map(([key, child]) => [key.slice(prefix.length).startsWith("[") ? key.slice(prefix.length) : JSON.stringify([key.slice(prefix.length)]), structuredClone(child)])))));
|
|
3275
3538
|
}
|
|
3276
3539
|
function optionalJsonObject(value) {
|
|
3277
3540
|
return value === null || value === void 0 ? {} : structuredClone(decodeJsonObject$1(value));
|
|
@@ -3291,6 +3554,33 @@ function define(target, key, value) {
|
|
|
3291
3554
|
writable: true
|
|
3292
3555
|
});
|
|
3293
3556
|
}
|
|
3557
|
+
const listMarker = "\0cmsList";
|
|
3558
|
+
function encodeLists(value) {
|
|
3559
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => {
|
|
3560
|
+
if (Array.isArray(child) && child.every((item) => isJsonObject(item) && Predicate.isString(item.id))) return [key, {
|
|
3561
|
+
[listMarker]: true,
|
|
3562
|
+
items: Object.fromEntries(child.flatMap((item, order) => isJsonObject(item) && Predicate.isString(item.id) ? [[item.id, {
|
|
3563
|
+
order,
|
|
3564
|
+
value: encodeLists(item)
|
|
3565
|
+
}]] : []))
|
|
3566
|
+
}];
|
|
3567
|
+
return [key, isJsonObject(child) ? encodeLists(child) : child];
|
|
3568
|
+
}));
|
|
3569
|
+
}
|
|
3570
|
+
function decodeLists(value) {
|
|
3571
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => {
|
|
3572
|
+
if (!isJsonObject(child)) return [key, child];
|
|
3573
|
+
if (child[listMarker] === true) return [key, (isJsonObject(child.items) ? Object.entries(child.items) : []).flatMap(([id, item]) => {
|
|
3574
|
+
if (!isJsonObject(item) || !isJsonObject(item.value) || item.value.id !== id || !Predicate.isNumber(item.order)) return [];
|
|
3575
|
+
return [{
|
|
3576
|
+
id,
|
|
3577
|
+
order: item.order,
|
|
3578
|
+
value: decodeLists(item.value)
|
|
3579
|
+
}];
|
|
3580
|
+
}).sort((left, right) => left.order - right.order || left.id.localeCompare(right.id)).map((item) => item.value)];
|
|
3581
|
+
return [key, decodeLists(child)];
|
|
3582
|
+
}));
|
|
3583
|
+
}
|
|
3294
3584
|
|
|
3295
3585
|
//#endregion
|
|
3296
3586
|
//#region ../document/src/internal/collaboration/document.ts
|
|
@@ -3644,6 +3934,7 @@ const startWorkingDocumentReconciliation = Effect.fn("WorkingDocuments.startReco
|
|
|
3644
3934
|
return [true, {
|
|
3645
3935
|
...current,
|
|
3646
3936
|
mutation: result,
|
|
3937
|
+
sweepRequired: current.sweepRequired || (result.affectedDocumentIds?.length ?? 0) > 1,
|
|
3647
3938
|
target: result.generation
|
|
3648
3939
|
}];
|
|
3649
3940
|
})) yield* Queue.offer(wakeups, void 0);
|
|
@@ -4956,6 +5247,10 @@ const makeInstallation = Effect.fn("Installation.make")(function* (bootstrap, ha
|
|
|
4956
5247
|
ready: true
|
|
4957
5248
|
}, false));
|
|
4958
5249
|
if (pathname === CMS_READINESS_PATH) return runPromise(health).then((status) => healthResponse(status, true));
|
|
5250
|
+
if (pathname.startsWith(`${STUDIO_API_PATH}/preview/`)) return handle(request).then((response) => {
|
|
5251
|
+
response.headers.set("cache-control", "private, no-store");
|
|
5252
|
+
return response;
|
|
5253
|
+
});
|
|
4959
5254
|
return handle(request);
|
|
4960
5255
|
},
|
|
4961
5256
|
health,
|
|
@@ -5672,65 +5967,6 @@ function activityFailure(operation) {
|
|
|
5672
5967
|
});
|
|
5673
5968
|
}
|
|
5674
5969
|
|
|
5675
|
-
//#endregion
|
|
5676
|
-
//#region ../server/src/internal/preview/preview-sessions.ts
|
|
5677
|
-
const DEFAULT_LIFETIME_MILLIS = 3e5;
|
|
5678
|
-
const MAXIMUM_TOKEN_LENGTH = 4096;
|
|
5679
|
-
const PreviewSessionClaims = Schema.Struct({
|
|
5680
|
-
actorId: StudioActor.fields.id,
|
|
5681
|
-
editorOrigin: PreviewOrigin,
|
|
5682
|
-
expiresAt: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
5683
|
-
previewOrigin: PreviewOrigin,
|
|
5684
|
-
sessionId: PreviewSessionId
|
|
5685
|
-
});
|
|
5686
|
-
var PreviewSessionConfigurationInvalid = class extends Schema.TaggedError()("Preview.SessionConfigurationInvalid", { message: Schema.NonEmptyString }) {};
|
|
5687
|
-
var PreviewSessions = class extends Context.Service()("@internal/server/PreviewSessions") {};
|
|
5688
|
-
function previewSessionsLayer(secret, options) {
|
|
5689
|
-
return Layer.effect(PreviewSessions, makePreviewSessions(secret, options));
|
|
5690
|
-
}
|
|
5691
|
-
const makePreviewSessions = Effect.fn("PreviewSessions.make")(function* (secret, options) {
|
|
5692
|
-
const lifetimeMillis = options.lifetimeMillis ?? DEFAULT_LIFETIME_MILLIS;
|
|
5693
|
-
if (!Number.isSafeInteger(lifetimeMillis) || lifetimeMillis <= 0) return yield* new PreviewSessionConfigurationInvalid({ message: "The preview session lifetime must be positive." });
|
|
5694
|
-
const crypto = yield* Crypto.Crypto;
|
|
5695
|
-
const tokens = yield* makeSignedTokenCodec(secret, {
|
|
5696
|
-
configuration: () => new PreviewSessionConfigurationInvalid({ message: "The preview session secret must be at least 32 bytes and usable for signing." }),
|
|
5697
|
-
invalid: () => new PreviewSessionInvalid({ reason: "invalid" })
|
|
5698
|
-
});
|
|
5699
|
-
const issue = Effect.fn("PreviewSessions.issue")(function* (actorId, editorOrigin, previewOrigin) {
|
|
5700
|
-
if (!options.allowedPreviewOrigins.has(previewOrigin)) return yield* invalidSession("origin");
|
|
5701
|
-
const expiresAt = (yield* Clock.currentTimeMillis) + lifetimeMillis;
|
|
5702
|
-
const claims = PreviewSessionClaims.make({
|
|
5703
|
-
actorId,
|
|
5704
|
-
editorOrigin,
|
|
5705
|
-
expiresAt,
|
|
5706
|
-
previewOrigin,
|
|
5707
|
-
sessionId: PreviewSessionId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie))
|
|
5708
|
-
});
|
|
5709
|
-
const token = yield* Schema.encodeEffect(Schema.fromJsonString(PreviewSessionClaims))(claims).pipe(Effect.mapError(() => new PreviewSessionInvalid({ reason: "invalid" })), Effect.flatMap(tokens.sign), Effect.mapError(() => new PreviewSessionInvalid({ reason: "invalid" })));
|
|
5710
|
-
return PreviewSession.make({
|
|
5711
|
-
editorOrigin,
|
|
5712
|
-
expiresAt: DateTime.makeUnsafe(expiresAt),
|
|
5713
|
-
previewOrigin,
|
|
5714
|
-
sessionId: claims.sessionId,
|
|
5715
|
-
token
|
|
5716
|
-
});
|
|
5717
|
-
});
|
|
5718
|
-
const verify = Effect.fn("PreviewSessions.verify")(function* (token, previewOrigin) {
|
|
5719
|
-
const payload = yield* tokens.verify(token, MAXIMUM_TOKEN_LENGTH).pipe(Effect.mapError(() => new PreviewSessionInvalid({ reason: "invalid" })));
|
|
5720
|
-
const claims = yield* Schema.decodeEffect(Schema.fromJsonString(PreviewSessionClaims))(payload).pipe(Effect.mapError(() => new PreviewSessionInvalid({ reason: "invalid" })));
|
|
5721
|
-
if (claims.previewOrigin !== previewOrigin) return yield* invalidSession("origin");
|
|
5722
|
-
if (claims.expiresAt <= (yield* Clock.currentTimeMillis)) return yield* invalidSession("expired");
|
|
5723
|
-
return claims;
|
|
5724
|
-
});
|
|
5725
|
-
return PreviewSessions.of({
|
|
5726
|
-
issue,
|
|
5727
|
-
verify
|
|
5728
|
-
});
|
|
5729
|
-
});
|
|
5730
|
-
function invalidSession(reason) {
|
|
5731
|
-
return Effect.fail(new PreviewSessionInvalid({ reason }));
|
|
5732
|
-
}
|
|
5733
|
-
|
|
5734
5970
|
//#endregion
|
|
5735
5971
|
//#region ../server/src/internal/initialization/initialization.ts
|
|
5736
5972
|
const InitializationUnavailableError = InitializationUnavailable;
|
|
@@ -6065,7 +6301,7 @@ function mapUnexpectedError(effect) {
|
|
|
6065
6301
|
//#region ../platform-node/src/internal/http/collaboration.ts
|
|
6066
6302
|
const PublicTicketError = Schema.Union([AccessDenied, DocumentNotFound]);
|
|
6067
6303
|
const isPublicTicketError = Schema.is(PublicTicketError);
|
|
6068
|
-
const collaborationApiLayer = HttpApiBuilder.group(StudioApi, "collaboration", (handlers) => Effect.gen(function* () {
|
|
6304
|
+
const collaborationApiLayer = (authoringRevision) => HttpApiBuilder.group(StudioApi, "collaboration", (handlers) => Effect.gen(function* () {
|
|
6069
6305
|
const accessControl = yield* AccessControl;
|
|
6070
6306
|
const documents = yield* WorkingDocuments;
|
|
6071
6307
|
const tickets = yield* StudioRealtimeTickets;
|
|
@@ -6090,6 +6326,7 @@ const collaborationApiLayer = HttpApiBuilder.group(StudioApi, "collaboration", (
|
|
|
6090
6326
|
ref: opened.ref
|
|
6091
6327
|
});
|
|
6092
6328
|
return CollaborationTicket.make({
|
|
6329
|
+
authoringRevision,
|
|
6093
6330
|
authoredStateVector: encodeRealtimeStateVector(opened.authoredStateVector),
|
|
6094
6331
|
documentName: roomName,
|
|
6095
6332
|
source: opened.source,
|
|
@@ -6124,7 +6361,13 @@ const documentsApiLayer = HttpApiBuilder.group(StudioApi, "documents", (handlers
|
|
|
6124
6361
|
}))).pipe(mapMutationError)).handle("validate", ({ params, payload }) => withStudioRequest((context) => documents.validate(context, UpdateDocumentInput.make({
|
|
6125
6362
|
documentId: params.documentId,
|
|
6126
6363
|
...payload
|
|
6127
|
-
}))).pipe(preservePublicErrors((error) => isAccessDenied$1(error) || isDocumentNotFound$1(error) || isDocumentVersionConflict(error)))).handle("
|
|
6364
|
+
}))).pipe(preservePublicErrors((error) => isAccessDenied$1(error) || isDocumentNotFound$1(error) || isDocumentVersionConflict(error)))).handle("duplicate", ({ params, payload }) => withStudioRequest((context) => documents.duplicate(context, DuplicateDocumentInput.make({
|
|
6365
|
+
documentId: params.documentId,
|
|
6366
|
+
...payload
|
|
6367
|
+
}))).pipe(mapMutationError)).handle("unpublish", ({ params, payload }) => withStudioRequest((context) => documents.unpublish(context, UnpublishDocumentInput.make({
|
|
6368
|
+
documentId: params.documentId,
|
|
6369
|
+
...payload
|
|
6370
|
+
}))).pipe(mapMutationError)).handle("move", ({ params, payload }) => withStudioRequest((context) => documents.move(context, MoveDocumentInput.make({
|
|
6128
6371
|
documentId: params.documentId,
|
|
6129
6372
|
...payload
|
|
6130
6373
|
}))).pipe(mapMutationError)).handle("publish", ({ params, payload }) => withStudioRequest((context) => documents.publish(context, PublishDocumentInput.make({
|
|
@@ -6227,43 +6470,11 @@ function invalidMultipart() {
|
|
|
6227
6470
|
|
|
6228
6471
|
//#endregion
|
|
6229
6472
|
//#region ../platform-node/src/internal/http/preview.ts
|
|
6230
|
-
const
|
|
6231
|
-
const
|
|
6232
|
-
|
|
6233
|
-
const
|
|
6234
|
-
|
|
6235
|
-
return handlers.handle("issue", ({ payload }) => Effect.gen(function* () {
|
|
6236
|
-
const context = yield* CurrentStudioRequest;
|
|
6237
|
-
const origin = yield* requestOrigin;
|
|
6238
|
-
return yield* previewSessions.issue(context.actor.id, origin, payload.previewOrigin);
|
|
6239
|
-
}).pipe(preservePublicErrors(isPreviewSessionInvalid)));
|
|
6240
|
-
}));
|
|
6241
|
-
const delivery = HttpApiBuilder.group(StudioApi, "previewDelivery", (handlers) => Effect.gen(function* () {
|
|
6242
|
-
const documents = yield* DocumentPreviews;
|
|
6243
|
-
const previewSessions = yield* PreviewSessions;
|
|
6244
|
-
const verify = (token) => requestOrigin.pipe(Effect.flatMap((origin) => previewSessions.verify(token, origin)));
|
|
6245
|
-
return handlers.handle("resolve", ({ payload }) => Effect.gen(function* () {
|
|
6246
|
-
yield* verify(payload.token);
|
|
6247
|
-
const document = yield* documents.read(payload.lookup);
|
|
6248
|
-
return Option.isSome(document) ? document.value : yield* new PreviewDraftNotFound({ lookup: payload.lookup });
|
|
6249
|
-
}).pipe(preservePublicErrors(isPreviewResolvePublicError))).handle("invalidations", ({ payload }) => Effect.gen(function* () {
|
|
6250
|
-
const claims = yield* verify(payload.token);
|
|
6251
|
-
const now = yield* Clock.currentTimeMillis;
|
|
6252
|
-
const expiresIn = Math.max(0, claims.expiresAt - now);
|
|
6253
|
-
return generationInvalidations(documents, options.invalidationInterval ?? "1 second").pipe(Stream.interruptWhen(Effect.sleep(Duration.millis(expiresIn))));
|
|
6254
|
-
}).pipe(preservePublicErrors(isPreviewSessionInvalid)));
|
|
6255
|
-
}));
|
|
6256
|
-
return Layer.merge(sessions, delivery);
|
|
6257
|
-
}
|
|
6258
|
-
function generationInvalidations(documents, interval) {
|
|
6259
|
-
return Stream.make(0).pipe(Stream.concat(Stream.fromSchedule(Schedule.spaced(interval))), Stream.mapEffect(() => documents.generation), Stream.changes, Stream.drop(1), Stream.map((generation) => PreviewInvalidation.make({
|
|
6260
|
-
generation,
|
|
6261
|
-
type: "document-invalidated"
|
|
6262
|
-
})), Stream.catch(() => Stream.empty));
|
|
6263
|
-
}
|
|
6264
|
-
const requestOrigin = HttpServerRequest.HttpServerRequest.pipe(Effect.flatMap((request) => {
|
|
6265
|
-
const origin = request.headers.origin;
|
|
6266
|
-
return origin === void 0 ? Effect.fail(new PreviewSessionInvalid({ reason: "origin" })) : Schema.decodeEffect(PreviewOrigin)(origin).pipe(Effect.mapError(() => new PreviewSessionInvalid({ reason: "origin" })));
|
|
6473
|
+
const publicError = Schema.is(Schema.Union([AccessDenied, PublishedDocumentNotFound]));
|
|
6474
|
+
const denied = Schema.is(AccessDenied);
|
|
6475
|
+
const previewApiLayer = HttpApiBuilder.group(StudioApi, "previewDelivery", (handlers) => Effect.gen(function* () {
|
|
6476
|
+
const documents = yield* DocumentPreviews;
|
|
6477
|
+
return handlers.handle("read", ({ payload }) => CurrentStudioRequest.pipe(Effect.flatMap((context) => documents.read(context, payload)), preservePublicErrors(publicError))).handle("query", ({ payload }) => CurrentStudioRequest.pipe(Effect.flatMap((context) => documents.query(context, payload)), preservePublicErrors(denied))).handle("navigation", ({ payload }) => CurrentStudioRequest.pipe(Effect.flatMap((context) => documents.navigation(context, payload)), preservePublicErrors(denied))).handle("surround", ({ payload }) => CurrentStudioRequest.pipe(Effect.flatMap((context) => documents.surround(context, payload.lookup, payload.query)), preservePublicErrors(publicError)));
|
|
6267
6478
|
}));
|
|
6268
6479
|
|
|
6269
6480
|
//#endregion
|
|
@@ -6321,9 +6532,9 @@ const userAdministrationHttpLayer = HttpApiBuilder.group(StudioApi, "users", (ha
|
|
|
6321
6532
|
|
|
6322
6533
|
//#endregion
|
|
6323
6534
|
//#region ../platform-node/src/internal/http/api.ts
|
|
6324
|
-
function studioApiLayer(bootstrap) {
|
|
6535
|
+
function studioApiLayer(bootstrap, authoringRevision) {
|
|
6325
6536
|
const configuration = HttpApiBuilder.group(StudioApi, "configuration", (handlers) => handlers.handle("bootstrap", () => Effect.succeed(bootstrap)));
|
|
6326
|
-
return authenticatedRouterHandlers(Layer.mergeAll(configuration, documentsApiLayer, mediaApiLayer(bootstrap.media.enabled, bootstrap.media.maxUploadBytes), collaborationApiLayer, deliveryApiLayer,
|
|
6537
|
+
return authenticatedRouterHandlers(Layer.mergeAll(configuration, documentsApiLayer, mediaApiLayer(bootstrap.media.enabled, bootstrap.media.maxUploadBytes), collaborationApiLayer(authoringRevision), deliveryApiLayer, previewApiLayer, administrationApiLayer, userAdministrationHttpLayer).pipe(Layer.provideMerge(studioSchemaErrorLayer)));
|
|
6327
6538
|
}
|
|
6328
6539
|
function authenticatedRouterHandlers(handlers) {
|
|
6329
6540
|
return handlers;
|
|
@@ -6335,7 +6546,7 @@ function productModulesLayer(options) {
|
|
|
6335
6546
|
const accessControl = defaultAccessControlLayer;
|
|
6336
6547
|
const authoredDocuments = documentsLayer(options.bootstrap.documents, {
|
|
6337
6548
|
...options.documentEngine,
|
|
6338
|
-
components: options.documentEngine.components ??
|
|
6549
|
+
components: options.documentEngine.components ?? [],
|
|
6339
6550
|
mediaBaseUrl: options.documentEngine.mediaBaseUrl ?? "https://media.invalid"
|
|
6340
6551
|
}).pipe(Layer.provide(accessControl));
|
|
6341
6552
|
const documentAccess = Layer.merge(authoredDocuments, accessControl);
|
|
@@ -6375,7 +6586,10 @@ function makeInstallationLayer(validated) {
|
|
|
6375
6586
|
const core = options.composition.core;
|
|
6376
6587
|
const productOptions = {
|
|
6377
6588
|
bootstrap: options.bootstrap,
|
|
6378
|
-
documentEngine: {
|
|
6589
|
+
documentEngine: {
|
|
6590
|
+
documentModel: options.documentModel,
|
|
6591
|
+
components: options.authoring.components
|
|
6592
|
+
},
|
|
6379
6593
|
workingDocuments: options.workingDocuments
|
|
6380
6594
|
};
|
|
6381
6595
|
const enabledProductModules = (media) => productModulesWithMediaLayer({
|
|
@@ -6384,7 +6598,6 @@ function makeInstallationLayer(validated) {
|
|
|
6384
6598
|
}).pipe(Layer.provide(Layer.merge(core, media.objects)));
|
|
6385
6599
|
const productModules = validated.media.enabled ? enabledProductModules(validated.media.configuration) : productModulesLayer(productOptions).pipe(Layer.provide(core));
|
|
6386
6600
|
const authentication = studioAuthenticationLayer.pipe(Layer.provide(core));
|
|
6387
|
-
const previewSessions = previewSessionsLayer(options.studioSecret, { allowedPreviewOrigins: options.allowedPreviewOrigins }).pipe(Layer.provide(core));
|
|
6388
6601
|
const initialization = initializationLayer({
|
|
6389
6602
|
initialSources: options.initialSources,
|
|
6390
6603
|
initializationToken: options.initializationToken
|
|
@@ -6393,8 +6606,8 @@ function makeInstallationLayer(validated) {
|
|
|
6393
6606
|
const tickets = studioRealtimeTicketsLayer(options.studioSecret).pipe(Layer.provide(core));
|
|
6394
6607
|
const roomHost = studioRealtimeRoomHostLayer().pipe(Layer.provide(productModules));
|
|
6395
6608
|
const gateway = studioRealtimeGatewayLayer.pipe(Layer.provide(Layer.mergeAll(productModules, roomHost, tickets)));
|
|
6396
|
-
const httpDependencies = Layer.mergeAll(core, productModules, gateway, initialization,
|
|
6397
|
-
const handlers = studioApiLayer(options.bootstrap).pipe(Layer.provide(httpDependencies));
|
|
6609
|
+
const httpDependencies = Layer.mergeAll(core, productModules, gateway, initialization, roomHost, users).pipe(Layer.provideMerge(authentication), Layer.provideMerge(tickets));
|
|
6610
|
+
const handlers = studioApiLayer(options.bootstrap, options.authoring.revision).pipe(Layer.provide(httpDependencies));
|
|
6398
6611
|
const httpApi = HttpApiBuilder.layer(StudioApi).pipe(Layer.provide(handlers), Layer.provide(HttpServer.layerServices));
|
|
6399
6612
|
return validated.media.enabled ? installationHttpLayer(options.bootstrap, httpApi, (context) => Effect.flatMap(MediaLibrary, (library) => library.cleanup(context))).pipe(Layer.provide(httpDependencies)) : installationHttpLayer(options.bootstrap, httpApi, () => Effect.succeed(MediaCleanupReport.make({ removedObjectKeys: [] }))).pipe(Layer.provide(httpDependencies));
|
|
6400
6613
|
}
|
|
@@ -6443,7 +6656,7 @@ const installCms$1 = Effect.fn("CmsInstallation.install")(function* (options) {
|
|
|
6443
6656
|
});
|
|
6444
6657
|
function createCmsInstallationLayer(options) {
|
|
6445
6658
|
return installationLayer({
|
|
6446
|
-
|
|
6659
|
+
authoring: options.authoring,
|
|
6447
6660
|
bootstrap: options.bootstrap,
|
|
6448
6661
|
composition: options.composition,
|
|
6449
6662
|
studioSecret: options.studioSecret,
|
|
@@ -6520,7 +6733,7 @@ function makeAwsS3MediaObjectClient(client) {
|
|
|
6520
6733
|
}))
|
|
6521
6734
|
}))),
|
|
6522
6735
|
putObject: (input) => request("MediaObjectStore.S3.putObject", (signal) => client.send(new PutObjectCommand({
|
|
6523
|
-
Body:
|
|
6736
|
+
Body: NodeStream.toReadableNever(Stream.fromAsyncIterable(input.body, (cause) => cause)),
|
|
6524
6737
|
Bucket: input.bucket,
|
|
6525
6738
|
CacheControl: input.cacheControl,
|
|
6526
6739
|
ContentLength: input.contentLength,
|
|
@@ -7285,6 +7498,13 @@ const embeddedMigrations = [
|
|
|
7285
7498
|
folderMillis: 178875937e4,
|
|
7286
7499
|
hash: "e442485fa6c2d611e3dae508a12d245481bc5ec8f2dd4f5d853910df34484787",
|
|
7287
7500
|
name: "20260907053610_fancy_lady_deathstrike"
|
|
7501
|
+
},
|
|
7502
|
+
{
|
|
7503
|
+
sql: ["ALTER TABLE \"activity_entries\" DROP CONSTRAINT \"activity_entries_action_check\", ADD CONSTRAINT \"activity_entries_action_check\" CHECK (\"action\" in ('document.created', 'document.updated', 'document.moved', 'document.trashed', 'document.revision.restored', 'document.untrashed', 'document.published', 'document.unpublished', 'media.uploaded', 'media.replaced', 'media.removed', 'media.cleaned', 'user.created', 'user.removed', 'user.role.changed', 'user.sessions.revoked', 'authentication.signed-in', 'authentication.signed-out', 'authentication.password.changed', 'authentication.password.reset'));", "\nALTER TABLE \"pending_changes\" DROP CONSTRAINT \"pending_changes_action_check\", ADD CONSTRAINT \"pending_changes_action_check\" CHECK (\"action\" in ('document.created', 'document.updated', 'document.moved', 'document.trashed', 'document.revision.restored', 'document.untrashed', 'document.published', 'document.unpublished', 'media.uploaded', 'media.replaced', 'media.removed', 'media.cleaned', 'user.created', 'user.removed', 'user.role.changed', 'user.sessions.revoked', 'authentication.signed-in', 'authentication.signed-out', 'authentication.password.changed', 'authentication.password.reset'));"],
|
|
7504
|
+
bps: true,
|
|
7505
|
+
folderMillis: 1788866265e3,
|
|
7506
|
+
hash: "24c56a37796caf8d76c0a97128a382b31e71808aa5b0ca7e5bac96795f9e16c7",
|
|
7507
|
+
name: "20260908111745_classy_johnny_storm"
|
|
7288
7508
|
}
|
|
7289
7509
|
];
|
|
7290
7510
|
|
|
@@ -7425,7 +7645,11 @@ const applyDocumentCommitPlan = Effect.fn("DocumentStore.Drizzle.applyCommitPlan
|
|
|
7425
7645
|
return;
|
|
7426
7646
|
}
|
|
7427
7647
|
case "document.remove": {
|
|
7428
|
-
const
|
|
7648
|
+
const metadata = {};
|
|
7649
|
+
if (plan.trashBatchId) metadata.trashBatchId = plan.trashBatchId;
|
|
7650
|
+
if (plan.title) metadata.title = plan.title;
|
|
7651
|
+
if (plan.path) metadata.path = plan.path;
|
|
7652
|
+
const revisionId = yield* appendRevision(transaction, plan, activity, metadata);
|
|
7429
7653
|
yield* transaction.update(documentsTable).set({
|
|
7430
7654
|
deletedAt: transactionTimestamp,
|
|
7431
7655
|
updatedAt: transactionTimestamp
|
|
@@ -7454,6 +7678,9 @@ const applyDocumentCommitPlan = Effect.fn("DocumentStore.Drizzle.applyCommitPlan
|
|
|
7454
7678
|
}).where(eq(documentHeads.documentId, plan.documentId));
|
|
7455
7679
|
return;
|
|
7456
7680
|
}
|
|
7681
|
+
case "document.unpublish":
|
|
7682
|
+
yield* transaction.update(documentHeads).set({ publishedRevisionId: null }).where(eq(documentHeads.documentId, plan.documentId));
|
|
7683
|
+
return;
|
|
7457
7684
|
case "document.publish":
|
|
7458
7685
|
yield* transaction.update(documentHeads).set({ publishedRevisionId: plan.revisionId }).where(eq(documentHeads.documentId, plan.documentId));
|
|
7459
7686
|
return;
|
|
@@ -7505,6 +7732,7 @@ function activityActionForDocumentPlan(action) {
|
|
|
7505
7732
|
case "document.remove": return "document.trashed";
|
|
7506
7733
|
case "document.restore": return "document.revision.restored";
|
|
7507
7734
|
case "document.undelete": return "document.untrashed";
|
|
7735
|
+
case "document.unpublish": return "document.unpublished";
|
|
7508
7736
|
case "document.publish": return "document.published";
|
|
7509
7737
|
}
|
|
7510
7738
|
}
|
|
@@ -7567,6 +7795,7 @@ const DocumentMediaReferenceInsert = createInsertSchema(documentMediaReferences,
|
|
|
7567
7795
|
//#region ../sql-pg/src/internal/documents/records.ts
|
|
7568
7796
|
const DocumentSnapshotRow = Schema.Struct({
|
|
7569
7797
|
authored: Schema.NullOr(DocumentRevisionSelect),
|
|
7798
|
+
created: Schema.optionalKey(Schema.NullOr(DocumentRevisionSelect)),
|
|
7570
7799
|
document: DocumentSelect,
|
|
7571
7800
|
head: Schema.NullOr(DocumentHeadSelect),
|
|
7572
7801
|
published: Schema.NullOr(DocumentRevisionSelect)
|
|
@@ -7583,11 +7812,18 @@ const decodeDocumentSnapshot = Effect.fn("DocumentStore.Drizzle.decodeSnapshot")
|
|
|
7583
7812
|
if (documentIds.has(row.document.id)) return yield* documentPersistenceError("DocumentStore.Drizzle.decodeSnapshot", /* @__PURE__ */ new Error(`Documents persistence returned duplicate document "${row.document.id}".`));
|
|
7584
7813
|
documentIds.add(row.document.id);
|
|
7585
7814
|
yield* validateSnapshotRow(row.document, row.head, row.authored, row.published);
|
|
7586
|
-
const authored = sourceRecord(row.authored, row.head.publishedRevisionId ?? void 0);
|
|
7815
|
+
const authored = Object.assign({}, sourceRecord(row.authored, row.head.publishedRevisionId ?? void 0), row.created ? { createdBy: revisionActor(row.created) } : void 0);
|
|
7587
7816
|
if (row.document.deletedAt === null) {
|
|
7588
7817
|
active.push(authored);
|
|
7589
7818
|
if (row.published) published.push(sourceRecord(row.published, row.published.id));
|
|
7590
|
-
} else
|
|
7819
|
+
} else {
|
|
7820
|
+
const details = yield* Schema.decodeEffect(Schema.Struct({
|
|
7821
|
+
title: DeletedDocument.fields.title,
|
|
7822
|
+
path: DeletedDocument.fields.path,
|
|
7823
|
+
trashBatchId: DeletedDocument.fields.trashBatchId
|
|
7824
|
+
}))(row.authored.activityDetails).pipe(Effect.mapError((cause) => documentPersistenceError("DocumentStore.decodeTrash", cause)));
|
|
7825
|
+
deleted.push(Object.assign({}, authored, details, { deletedAt: row.document.deletedAt }));
|
|
7826
|
+
}
|
|
7591
7827
|
}
|
|
7592
7828
|
active.sort(compareSources);
|
|
7593
7829
|
deleted.sort(compareSources);
|
|
@@ -7643,6 +7879,8 @@ function validateSnapshotRow(document, head, authored, published) {
|
|
|
7643
7879
|
}
|
|
7644
7880
|
function sourceRecord(revision, publishedRevisionId) {
|
|
7645
7881
|
const record = {
|
|
7882
|
+
editedAt: revision.createdAt,
|
|
7883
|
+
editedBy: revisionActor(revision),
|
|
7646
7884
|
documentId: revision.documentId,
|
|
7647
7885
|
markdown: revision.markdown,
|
|
7648
7886
|
revisionId: revision.id,
|
|
@@ -7661,6 +7899,14 @@ function documentPersistenceError(operation, cause) {
|
|
|
7661
7899
|
operation
|
|
7662
7900
|
});
|
|
7663
7901
|
}
|
|
7902
|
+
function revisionActor(revision) {
|
|
7903
|
+
return {
|
|
7904
|
+
id: revision.actorId,
|
|
7905
|
+
name: revision.actorName,
|
|
7906
|
+
email: revision.actorEmail,
|
|
7907
|
+
roles: revision.actorRoles
|
|
7908
|
+
};
|
|
7909
|
+
}
|
|
7664
7910
|
|
|
7665
7911
|
//#endregion
|
|
7666
7912
|
//#region ../sql-pg/src/internal/database/media-reference-lock.ts
|
|
@@ -7766,6 +8012,7 @@ function validMediaObjectKey(column) {
|
|
|
7766
8012
|
//#endregion
|
|
7767
8013
|
//#region ../sql-pg/src/internal/documents/document-store.ts
|
|
7768
8014
|
const authoredRevision = alias(documentRevisions, "authored_revision");
|
|
8015
|
+
const createdRevision = alias(documentRevisions, "created_revision");
|
|
7769
8016
|
const publishedRevision = alias(documentRevisions, "published_revision");
|
|
7770
8017
|
const RepositoryDomainError = Schema.Union([
|
|
7771
8018
|
DocumentGenerationConflict,
|
|
@@ -7800,19 +8047,26 @@ const documentStoreLayer = Layer.effect(DocumentStore, Effect.gen(function* () {
|
|
|
7800
8047
|
});
|
|
7801
8048
|
}
|
|
7802
8049
|
const state = yield* Schema.decodeUnknownEffect(CmsStateSelect)(stateRow);
|
|
7803
|
-
const
|
|
7804
|
-
const
|
|
7805
|
-
yield*
|
|
8050
|
+
const plans = [input.plan, ...input.additionalPlans ?? []];
|
|
8051
|
+
const references = plans.flatMap((plan) => input.mediaReferences.filter((reference) => reference.documentId === plan.documentId));
|
|
8052
|
+
if (references.length !== input.mediaReferences.length) return yield* persistenceFailure$2("DocumentStore.commit", /* @__PURE__ */ new Error("Unexpected media reference owner."));
|
|
8053
|
+
yield* lockMediaReferenceKeys(transaction, references.map((reference) => reference.objectKey));
|
|
7806
8054
|
yield* rejectDeletingMedia(transaction, references);
|
|
7807
8055
|
const activity = input.activity ?? systemActivity;
|
|
7808
|
-
|
|
7809
|
-
|
|
8056
|
+
for (const plan of plans) {
|
|
8057
|
+
yield* applyDocumentCommitPlan(transaction, plan, activity);
|
|
8058
|
+
yield* replaceDocumentMediaReferences(transaction, plan.documentId, references.filter((reference) => reference.documentId === plan.documentId));
|
|
8059
|
+
}
|
|
7810
8060
|
const snapshot = yield* readSnapshot(transaction, state.generation);
|
|
8061
|
+
for (const plan of plans) {
|
|
8062
|
+
const affected = yield* sourceForMutation(snapshot, plan.documentId, plan.action === "document.remove");
|
|
8063
|
+
yield* recordDocumentActivity(transaction, affected, plan, activity);
|
|
8064
|
+
}
|
|
7811
8065
|
const source = yield* sourceForMutation(snapshot, input.plan.documentId, input.plan.action === "document.remove");
|
|
7812
|
-
yield* recordDocumentActivity(transaction, source, input.plan, activity);
|
|
7813
8066
|
return {
|
|
7814
8067
|
mutation: DocumentMutationResult.make({
|
|
7815
8068
|
action: input.plan.action,
|
|
8069
|
+
affectedDocumentIds: plans.map((plan) => plan.documentId),
|
|
7816
8070
|
documentId: source.documentId,
|
|
7817
8071
|
generation: snapshot.generation,
|
|
7818
8072
|
revisionId: source.revisionId,
|
|
@@ -7901,16 +8155,17 @@ const recordDocumentActivity = Effect.fn("DocumentStore.Drizzle.recordActivity")
|
|
|
7901
8155
|
targetId: source.documentId,
|
|
7902
8156
|
targetKind: "document"
|
|
7903
8157
|
};
|
|
7904
|
-
if (plan.action !== "document.publish") values.id = source.revisionId;
|
|
8158
|
+
if (plan.action !== "document.publish" && plan.action !== "document.unpublish") values.id = source.revisionId;
|
|
7905
8159
|
yield* database.insert(activityEntries).values(values);
|
|
7906
8160
|
});
|
|
7907
8161
|
const readSnapshot = Effect.fn("DocumentStore.Drizzle.readSnapshot")(function* (database, generation) {
|
|
7908
8162
|
const rows = yield* database.select({
|
|
7909
8163
|
authored: authoredRevision,
|
|
8164
|
+
created: createdRevision,
|
|
7910
8165
|
document: documentsTable,
|
|
7911
8166
|
head: documentHeads,
|
|
7912
8167
|
published: publishedRevision
|
|
7913
|
-
}).from(documentsTable).leftJoin(documentHeads, eq(documentHeads.documentId, documentsTable.id)).leftJoin(authoredRevision, and(eq(authoredRevision.documentId, documentHeads.documentId), eq(authoredRevision.id, documentHeads.authoredRevisionId))).leftJoin(publishedRevision, and(eq(publishedRevision.documentId, documentHeads.documentId), eq(publishedRevision.id, documentHeads.publishedRevisionId)));
|
|
8168
|
+
}).from(documentsTable).leftJoin(createdRevision, and(eq(createdRevision.documentId, documentsTable.id), eq(createdRevision.action, "document.create"))).leftJoin(documentHeads, eq(documentHeads.documentId, documentsTable.id)).leftJoin(authoredRevision, and(eq(authoredRevision.documentId, documentHeads.documentId), eq(authoredRevision.id, documentHeads.authoredRevisionId))).leftJoin(publishedRevision, and(eq(publishedRevision.documentId, documentHeads.documentId), eq(publishedRevision.id, documentHeads.publishedRevisionId)));
|
|
7914
8169
|
return yield* decodeDocumentSnapshot(rows, generation);
|
|
7915
8170
|
});
|
|
7916
8171
|
const readGeneration = Effect.fn("DocumentStore.Drizzle.readGeneration")(function* (database) {
|
|
@@ -7933,10 +8188,6 @@ function mapSynchronizationFailure(operation) {
|
|
|
7933
8188
|
return documentPersistenceError(operation, cause);
|
|
7934
8189
|
}));
|
|
7935
8190
|
}
|
|
7936
|
-
const referencesForDocument = Effect.fn("DocumentStore.Drizzle.referencesForDocument")(function* (documentId, references) {
|
|
7937
|
-
if (references.some((reference) => reference.documentId !== documentId)) return yield* persistenceFailure$2("DocumentStore.Drizzle.referencesForDocument", /* @__PURE__ */ new Error(`The media-reference projection does not belong exclusively to document "${documentId}".`));
|
|
7938
|
-
return uniqueMediaReferences(references);
|
|
7939
|
-
});
|
|
7940
8191
|
const rejectDeletingMedia = Effect.fn("DocumentStore.Drizzle.rejectDeletingMedia")(function* (transaction, references) {
|
|
7941
8192
|
const keys = [...new Set(references.map((reference) => reference.objectKey))];
|
|
7942
8193
|
if (keys.length === 0) return;
|
|
@@ -9078,7 +9329,7 @@ function postgresLayer(options) {
|
|
|
9078
9329
|
//#region src/runtime/installation.ts
|
|
9079
9330
|
/** Composes the private providers selected by the Nuxt product adapter. */
|
|
9080
9331
|
const installCms = Effect.fn("NuxtCmsInstallation.install")(function* (options) {
|
|
9081
|
-
const crypto = NodeCrypto.layer;
|
|
9332
|
+
const crypto = NodeCrypto$1.layer;
|
|
9082
9333
|
const postgres = postgresLayer({ url: Redacted.make(options.databaseUrl) });
|
|
9083
9334
|
const identity = Layer.unwrap(Effect.acquireRelease(Effect.sync(() => new Pool({ connectionString: options.databaseUrl })), (pool) => Effect.promise(() => pool.end()).pipe(Effect.asVoid)).pipe(Effect.map((pool) => betterAuthLayer({
|
|
9084
9335
|
baseURL: options.baseUrl,
|
|
@@ -9088,6 +9339,7 @@ const installCms = Effect.fn("NuxtCmsInstallation.install")(function* (options)
|
|
|
9088
9339
|
const core = Layer.mergeAll(crypto, postgres, identity);
|
|
9089
9340
|
return yield* installCms$1({
|
|
9090
9341
|
baseUrl: options.baseUrl,
|
|
9342
|
+
authoring: options.authoring,
|
|
9091
9343
|
bootstrap: options.bootstrap,
|
|
9092
9344
|
cleanup: options.cleanup.enabled ? {
|
|
9093
9345
|
initialDelayMillis: options.cleanup.initialDelayMillis,
|
|
@@ -9125,4 +9377,4 @@ function toS3MediaObjectStoreOptions(options) {
|
|
|
9125
9377
|
|
|
9126
9378
|
//#endregion
|
|
9127
9379
|
export { toS3MediaObjectStoreOptions as n, installCms as t };
|
|
9128
|
-
//# sourceMappingURL=installation-
|
|
9380
|
+
//# sourceMappingURL=installation-DeuBiFRE.js.map
|