@phtngyn/cms 0.1.0-beta.1 → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +192 -9
  2. package/package.json +30 -15
  3. package/src/Configuration-Br7tE9Qt.d.ts +68 -0
  4. package/src/{Configuration-DyKo0sLS.js → Configuration-CA_jvQKG.js} +26 -147
  5. package/src/Configuration-CA_jvQKG.js.map +1 -0
  6. package/src/Configuration.d.ts +2 -44
  7. package/src/Configuration.js +0 -1
  8. package/src/Configuration.js.map +1 -1
  9. package/src/{DocumentEngine-DYObwMDY.js → DocumentEngine-CgiBQU4g.js} +75 -27
  10. package/src/DocumentEngine-CgiBQU4g.js.map +1 -0
  11. package/src/DocumentModel-BB9igGC7.d.ts +34 -0
  12. package/src/DocumentModel.d.ts +2 -34
  13. package/src/DocumentModel.js +13 -3
  14. package/src/DocumentModel.js.map +1 -1
  15. package/src/Forms-BsJMev5_.d.ts +74 -0
  16. package/src/Forms-CfcI3tPU.js +79 -0
  17. package/src/Forms-CfcI3tPU.js.map +1 -0
  18. package/src/Forms-DxbkU3_5.js +367 -0
  19. package/src/Forms-DxbkU3_5.js.map +1 -0
  20. package/src/Forms.d.ts +35 -0
  21. package/src/Forms.js +13 -0
  22. package/src/Forms.js.map +1 -0
  23. package/src/{HttpPaths-BbS21Jy8.js → HttpPaths-c9nLZ9H2.js} +6 -2
  24. package/src/HttpPaths-c9nLZ9H2.js.map +1 -0
  25. package/src/I18n-DZK4CkJF.d.ts +21 -0
  26. package/src/I18n.d.ts +2 -0
  27. package/src/I18n.js +1 -0
  28. package/src/Module.js +57 -11
  29. package/src/Module.js.map +1 -1
  30. package/src/S3MediaObjects-JKMAWyVB.js +2664 -0
  31. package/src/S3MediaObjects-JKMAWyVB.js.map +1 -0
  32. package/src/Seed.d.ts +6 -0
  33. package/src/Seed.js +34 -0
  34. package/src/Seed.js.map +1 -0
  35. package/src/Seo-402YumpT.d.ts +198 -0
  36. package/src/{connection-CBTY-TzB.js → connection-B-iz0Y9Y.js} +5 -7
  37. package/src/connection-B-iz0Y9Y.js.map +1 -0
  38. package/src/{installation-DeuBiFRE.js → installation-DgF2M1gL.js} +1005 -2242
  39. package/src/installation-DgF2M1gL.js.map +1 -0
  40. package/src/internal/authoring-catalog.js +10 -4
  41. package/src/internal/authoring-catalog.js.map +1 -1
  42. package/src/internal/authoring-compiler.js +0 -1
  43. package/src/internal/authoring-compiler.js.map +1 -1
  44. package/src/internal/authoring-metadata.js +4 -4
  45. package/src/internal/authoring-metadata.js.map +1 -1
  46. package/src/internal/cms-config.js +0 -1
  47. package/src/internal/cms-config.js.map +1 -1
  48. package/src/internal/configuration.js +23 -10
  49. package/src/internal/configuration.js.map +1 -1
  50. package/src/internal/document-model.js +65 -20
  51. package/src/internal/document-model.js.map +1 -1
  52. package/src/internal/form-capabilities.js +12 -0
  53. package/src/internal/form-capabilities.js.map +1 -0
  54. package/src/internal/i18n.js +47 -0
  55. package/src/internal/i18n.js.map +1 -0
  56. package/src/internal/studio-app.js +2 -2
  57. package/src/internal/studio-app.js.map +1 -1
  58. package/src/{location-DtYv2aes.js → location-CFKTeK1K.js} +2 -3
  59. package/src/location-CFKTeK1K.js.map +1 -0
  60. package/src/{metadata-DE1Mc2td.js → metadata-BEZPWh-3.js} +3 -72
  61. package/src/metadata-BEZPWh-3.js.map +1 -0
  62. package/src/{models-C3witQBK.js → models-BWm1pTAf.js} +3 -2
  63. package/src/models-BWm1pTAf.js.map +1 -0
  64. package/src/{models-BkMgeaOL.js → models-DwYioyNV.js} +203 -32
  65. package/src/models-DwYioyNV.js.map +1 -0
  66. package/src/runtime/blocks.js +22 -9
  67. package/src/runtime/blocks.js.map +1 -1
  68. package/src/runtime/collaboration.js.map +1 -1
  69. package/src/runtime/delivery/DocumentRenderer.js +7 -4
  70. package/src/runtime/delivery/DocumentRenderer.js.map +1 -1
  71. package/src/runtime/delivery/blocks.js +3 -1
  72. package/src/runtime/delivery/blocks.js.map +1 -1
  73. package/src/runtime/delivery/context.js +1 -1
  74. package/src/runtime/delivery/document-page.js +12 -12
  75. package/src/runtime/delivery/document-page.js.map +1 -1
  76. package/src/runtime/delivery/document.js +4 -2
  77. package/src/runtime/delivery/document.js.map +1 -1
  78. package/src/runtime/delivery/error.js.map +1 -1
  79. package/src/runtime/delivery/plugin.js +22 -2
  80. package/src/runtime/delivery/plugin.js.map +1 -1
  81. package/src/runtime/delivery/types.d.ts +5 -0
  82. package/src/runtime/delivery/useCmsDocumentSeo.d.ts +56 -0
  83. package/src/runtime/delivery/useCmsDocumentSeo.js +99 -0
  84. package/src/runtime/delivery/useCmsDocumentSeo.js.map +1 -0
  85. package/src/runtime/delivery/useDocument.js +2 -2
  86. package/src/runtime/delivery/useDocument.js.map +1 -1
  87. package/src/runtime/delivery/useDocumentQuery.js +2 -2
  88. package/src/runtime/delivery/useDocumentQuery.js.map +1 -1
  89. package/src/runtime/forms/document.js +38 -0
  90. package/src/runtime/forms/document.js.map +1 -0
  91. package/src/runtime/forms/useCmsForm.js +149 -0
  92. package/src/runtime/forms/useCmsForm.js.map +1 -0
  93. package/src/runtime/i18n/translator.js +22 -0
  94. package/src/runtime/i18n/translator.js.map +1 -0
  95. package/src/runtime/i18n/useCmsI18n.d.ts +9 -0
  96. package/src/runtime/i18n/useCmsI18n.js +29 -0
  97. package/src/runtime/i18n/useCmsI18n.js.map +1 -0
  98. package/src/runtime/installation.js +1 -1
  99. package/src/runtime/plugin.js +1 -1
  100. package/src/runtime/plugin.js.map +1 -1
  101. package/src/runtime/preview/comark-document.js +0 -1
  102. package/src/runtime/preview/comark-document.js.map +1 -1
  103. package/src/runtime/preview/connection.js +1 -1
  104. package/src/runtime/preview/controller.js +10 -3
  105. package/src/runtime/preview/controller.js.map +1 -1
  106. package/src/runtime/preview/overlay.js.map +1 -1
  107. package/src/runtime/preview/plugin.client.js +59 -48
  108. package/src/runtime/preview/plugin.client.js.map +1 -1
  109. package/src/runtime/preview/protocol.js.map +1 -1
  110. package/src/runtime/preview/store.js +1 -1
  111. package/src/runtime/preview/targets.js +0 -1
  112. package/src/runtime/preview/targets.js.map +1 -1
  113. package/src/runtime/sitemap-entries.js +17 -0
  114. package/src/runtime/sitemap-entries.js.map +1 -0
  115. package/src/runtime/sitemap.js +24 -0
  116. package/src/runtime/sitemap.js.map +1 -0
  117. package/src/runtime/studio-app.js.map +1 -1
  118. package/src/runtime/studio-entry.js +2 -2
  119. package/src/runtime/studio-entry.js.map +1 -1
  120. package/src/runtime/studio-navigation.js +1 -1
  121. package/src/scalars-B2lgGUnj.js +26 -0
  122. package/src/scalars-B2lgGUnj.js.map +1 -0
  123. package/src/{store-CfFFx9Ua.js → store-DjJs3I7k.js} +12 -7
  124. package/src/store-DjJs3I7k.js.map +1 -0
  125. package/studio/{CreateDocumentDialog--L9lTfx6.js → CreateDocumentDialog-CTeIFlPb.js} +44 -44
  126. package/studio/{collaboration-DUuu678R.js → collaboration-DcK2F6v9.js} +7 -6
  127. package/studio/{documentPlanning-BXX04Woe.js → documentPlanning-Bfv-d7OU.js} +7310 -6479
  128. package/studio/{editor-PexnXkJY.js → editor-Bzlh3zXt.js} +7965 -7955
  129. package/studio/host.css +1 -1
  130. package/studio/host.js +10416 -7981
  131. package/src/Configuration-DyKo0sLS.js.map +0 -1
  132. package/src/DocumentEngine-DYObwMDY.js.map +0 -1
  133. package/src/HttpPaths-BbS21Jy8.js.map +0 -1
  134. package/src/StudioHttp-jR2-bc01.js +0 -718
  135. package/src/StudioHttp-jR2-bc01.js.map +0 -1
  136. package/src/connection-CBTY-TzB.js.map +0 -1
  137. package/src/installation-DeuBiFRE.js.map +0 -1
  138. package/src/location-DtYv2aes.js.map +0 -1
  139. package/src/metadata-DE1Mc2td.js.map +0 -1
  140. package/src/models-BkMgeaOL.js.map +0 -1
  141. package/src/models-C3witQBK.js.map +0 -1
  142. package/src/store-CfFFx9Ua.js.map +0 -1
@@ -0,0 +1,2664 @@
1
+ import { c as RevisionId, d as SourcePath, f as nonNegativeInt, i as DocumentVersion, n as DocumentHistoryCursor, o as PublicPath, r as DocumentId, s as RelativeDocumentPath, t as DocumentGeneration, u as Slug } from "./scalars-B2lgGUnj.js";
2
+ import { i as FormResult, o as FormSubmission } from "./Forms-CfcI3tPU.js";
3
+ import { a as encodeCmsEditorComponentAttributes, d as createCmsComarkRenderData, m as toComarkDocument, p as mutableJsonObject, s as projectCmsEditorComponentAttributes } from "./Forms-DxbkU3_5.js";
4
+ import { $ as DOCUMENT_ACTIONS, A as RemoveDocumentInput, B as SeoCapabilities, Ct as MediaTooLarge, D as PublishedDocumentPage, E as PublishedDocumentLookup, H as SeoConfirmationRequired, I as GlobalSeoSettings, J as SeoUrl, K as SeoSnapshot, L as PublishSeoInput, M as UndeleteDocumentInput, O as PublishedDocumentQuery, P as UpdateDocumentInput, Q as AuthenticationRequired, R as SaveSeoInput, S as DuplicateDocumentInput, T as PublishDocumentInput, U as SeoMediaUnavailable, V as SeoConfiguration, W as SeoMutationResult, X as SitemapEntry, Y as SeoVersionConflict, Z as AccessDenied, _ as DocumentSource, _t as MediaObjectKey, at as ImageMimeType, b as DocumentSurround, bt as MediaReference, ct as MediaCleanupReport, d as DocumentIssue, dt as MediaFolder, et as MEDIA_ACTIONS, f as DocumentMutationResult, ft as MediaId, g as DocumentRevisionAction, gt as MediaNotFound, h as DocumentQueryScalar, i as CreateDocumentInput, j as RestoreDocumentInput, k as PublishedDocumentSurroundQuery, kt as RequestId, l as DocumentImportReport, lt as MediaConflict, m as DocumentNavigationQuery, mt as MediaInvalid, n as CmsMarkdownDocument, nt as StudioActor, o as DeletedDocument, ot as MediaAsset, p as DocumentNavigationItem, pt as MediaIntegrityReport, q as SeoState, r as CompiledDocument, rt as StudioEntryDenied, s as DocumentBrowseQuery, st as MediaByteSize, tt as Role, vt as MediaPage, w as MoveDocumentInput, wt as MediaUpload, x as DocumentValidationResult, xt as MediaReferenced, y as DocumentSummary, yt as MediaQuery, z as SeoCanonicalOrigin } from "./models-DwYioyNV.js";
5
+ import { u as STUDIO_API_PATH } from "./HttpPaths-c9nLZ9H2.js";
6
+ import { i as withoutEditorMetadata } from "./metadata-BEZPWh-3.js";
7
+ import { d as EditorRenderFailed, i as MediaConfiguration, r as DocumentConfiguration } from "./Configuration-CA_jvQKG.js";
8
+ 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-CgiBQU4g.js";
9
+ import { Context, Crypto, Data, Effect, Encoding, Layer, Option, Predicate, PubSub, Redacted, Ref, Result, Schedule, Schema, Semaphore, Stream, flow } from "effect";
10
+ import { NodeStream } from "@effect/platform-node-shared";
11
+ import { Multipart } from "effect/unstable/http";
12
+ import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema } from "effect/unstable/httpapi";
13
+ import { renderMarkdown } from "comark/render";
14
+ import { diffArrays } from "diff";
15
+ import { CopyObjectCommand, CreateBucketCommand, DeleteObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
16
+
17
+ //#region ../protocol/src/internal/documents/errors.ts
18
+ var DocumentNotFound = class extends Schema.TaggedError()("Document.NotFound", {
19
+ documentId: DocumentId,
20
+ revisionId: Schema.optionalKey(RevisionId)
21
+ }) {};
22
+ var DocumentPathConflict = class extends Schema.TaggedError()("Document.PathConflict", { sourcePath: SourcePath }) {};
23
+ var DocumentVersionConflict = class extends Schema.TaggedError()("Document.VersionConflict", {
24
+ documentId: DocumentId,
25
+ expectedVersion: DocumentVersion,
26
+ actualVersion: DocumentVersion
27
+ }) {};
28
+ var DocumentMediaUnavailable = class extends Schema.TaggedError()("Document.MediaUnavailable", {
29
+ documentId: DocumentId,
30
+ mediaKeys: Schema.Array(MediaObjectKey).check(Schema.isMinLength(1))
31
+ }) {};
32
+ var DocumentValidationFailed = class extends Schema.TaggedError()("Document.ValidationFailed", { issues: Schema.Array(DocumentIssue) }) {};
33
+ var PublishedDocumentNotFound = class extends Schema.TaggedError()("PublishedDocument.NotFound", { lookup: PublishedDocumentLookup }) {};
34
+
35
+ //#endregion
36
+ //#region ../protocol/src/Activity.ts
37
+ /**
38
+ * Immutable completed Activity facts. Pages are ordered newest-first; cursors are opaque,
39
+ * stable continuation tokens and an invalid cursor is a caller-visible failure.
40
+ */
41
+ const ActivityId = Schema.String.check(Schema.isUUID()).pipe(Schema.brand("ActivityId"));
42
+ const ActivityCursor = Schema.NonEmptyString.pipe(Schema.brand("ActivityCursor"));
43
+ const ACTIVITY_ACTIONS = [
44
+ "document.created",
45
+ "document.updated",
46
+ "document.moved",
47
+ "document.trashed",
48
+ "document.revision.restored",
49
+ "document.untrashed",
50
+ "document.published",
51
+ "document.unpublished",
52
+ "media.uploaded",
53
+ "media.replaced",
54
+ "media.removed",
55
+ "media.cleaned",
56
+ "seo.saved",
57
+ "seo.published",
58
+ "user.created",
59
+ "user.removed",
60
+ "user.role.changed",
61
+ "user.sessions.revoked",
62
+ "authentication.signed-in",
63
+ "authentication.signed-out",
64
+ "authentication.password.changed",
65
+ "authentication.password.reset"
66
+ ];
67
+ const ActivityAction = Schema.Literals(ACTIVITY_ACTIONS);
68
+ const DocumentActivityTarget = Schema.Struct({
69
+ kind: Schema.tag("document"),
70
+ documentId: DocumentId
71
+ });
72
+ const MediaActivityTarget = Schema.Struct({
73
+ kind: Schema.tag("media"),
74
+ mediaId: MediaId
75
+ });
76
+ const MediaLibraryActivityTarget = Schema.Struct({ kind: Schema.tag("media-library") });
77
+ const ActivityTarget = Schema.Union([
78
+ DocumentActivityTarget,
79
+ MediaActivityTarget,
80
+ MediaLibraryActivityTarget,
81
+ Schema.Struct({ kind: Schema.tag("seo") }),
82
+ Schema.Struct({
83
+ kind: Schema.tag("user"),
84
+ userId: Schema.NonEmptyString
85
+ })
86
+ ]).pipe(Schema.toTaggedUnion("kind"));
87
+ const ActivityTargetKind = Schema.Literals([
88
+ "document",
89
+ "media",
90
+ "media-library",
91
+ "seo",
92
+ "user"
93
+ ]);
94
+ const ActivityContext = Schema.Struct({
95
+ actor: StudioActor,
96
+ requestId: RequestId,
97
+ reason: Schema.optionalKey(Schema.NonEmptyString.check(Schema.isMaxLength(1e3)))
98
+ }).annotate({ identifier: "ActivityContext" });
99
+ const ActivityEntry = Schema.Struct({
100
+ id: ActivityId,
101
+ action: ActivityAction,
102
+ actor: StudioActor,
103
+ requestId: RequestId,
104
+ reason: ActivityContext.fields.reason,
105
+ target: ActivityTarget,
106
+ details: Schema.JsonObject,
107
+ occurredAt: Schema.DateTimeUtcFromString
108
+ }).annotate({ identifier: "ActivityEntry" });
109
+ const ActivityEntryDraft = Schema.Struct({
110
+ action: ActivityAction,
111
+ context: ActivityContext,
112
+ target: ActivityTarget,
113
+ details: Schema.JsonObject
114
+ }).annotate({ identifier: "ActivityEntryDraft" });
115
+ const ActivityQuery = Schema.Struct({
116
+ action: Schema.optionalKey(ActivityAction),
117
+ actorId: Schema.optionalKey(StudioActor.fields.id),
118
+ cursor: Schema.optionalKey(ActivityCursor),
119
+ limit: Schema.optionalKey(Schema.Int.check(Schema.isBetween({
120
+ minimum: 1,
121
+ maximum: 100
122
+ }))),
123
+ requestId: Schema.optionalKey(RequestId),
124
+ targetId: Schema.optionalKey(Schema.NonEmptyString),
125
+ targetKind: Schema.optionalKey(ActivityTargetKind)
126
+ }).annotate({ identifier: "ActivityQuery" });
127
+ const ActivityPage = Schema.Struct({
128
+ entries: Schema.Array(ActivityEntry),
129
+ nextCursor: Schema.optionalKey(ActivityCursor),
130
+ total: nonNegativeInt
131
+ }).annotate({ identifier: "ActivityPage" });
132
+ var ActivityCursorInvalid = class extends Schema.TaggedError()("Activity.CursorInvalid", {}) {};
133
+
134
+ //#endregion
135
+ //#region ../protocol/src/internal/documents/history.ts
136
+ const DocumentRevisionSummary = Schema.Struct({
137
+ action: DocumentRevisionAction,
138
+ activity: ActivityEntry,
139
+ createdAt: Schema.DateTimeUtcFromString,
140
+ documentId: DocumentId,
141
+ revisionId: RevisionId,
142
+ sourcePath: SourcePath,
143
+ version: DocumentVersion
144
+ }).annotate({ identifier: "DocumentRevisionSummary" });
145
+ const DocumentRevision = Schema.Struct({
146
+ ...DocumentRevisionSummary.fields,
147
+ markdown: Schema.String
148
+ }).annotate({ identifier: "DocumentRevision" });
149
+ const DocumentHistoryQuery = Schema.Struct({
150
+ documentId: DocumentId,
151
+ cursor: Schema.optionalKey(DocumentHistoryCursor),
152
+ limit: Schema.optionalKey(Schema.Int.check(Schema.isBetween({
153
+ minimum: 1,
154
+ maximum: 100
155
+ })))
156
+ }).annotate({ identifier: "DocumentHistoryQuery" });
157
+ const DocumentRevisionPage = Schema.Struct({
158
+ revisions: Schema.Array(DocumentRevisionSummary),
159
+ nextCursor: Schema.optionalKey(DocumentHistoryCursor),
160
+ total: nonNegativeInt
161
+ }).annotate({ identifier: "DocumentRevisionPage" });
162
+ const LineNumber = Schema.Int.check(Schema.isGreaterThan(0));
163
+ const DocumentRevisionChange = Schema.Union([
164
+ Schema.Struct({
165
+ kind: Schema.tag("equal"),
166
+ beforeLine: LineNumber,
167
+ afterLine: LineNumber,
168
+ line: Schema.String
169
+ }),
170
+ Schema.Struct({
171
+ kind: Schema.tag("added"),
172
+ afterLine: LineNumber,
173
+ line: Schema.String
174
+ }),
175
+ Schema.Struct({
176
+ kind: Schema.tag("removed"),
177
+ beforeLine: LineNumber,
178
+ line: Schema.String
179
+ })
180
+ ]).pipe(Schema.toTaggedUnion("kind"));
181
+ const CompareDocumentRevisionsInput = Schema.Struct({
182
+ documentId: DocumentId,
183
+ fromRevisionId: RevisionId,
184
+ toRevisionId: RevisionId
185
+ }).annotate({ identifier: "CompareDocumentRevisionsInput" });
186
+ const DocumentRevisionComparison = Schema.Struct({
187
+ from: DocumentRevision,
188
+ to: DocumentRevision,
189
+ changes: Schema.Array(DocumentRevisionChange)
190
+ }).annotate({ identifier: "DocumentRevisionComparison" });
191
+ var DocumentHistoryInvalidCursor = class extends Schema.TaggedError()("DocumentHistory.InvalidCursor", {}) {};
192
+ var DocumentHistoryComparisonTooLarge = class extends Schema.TaggedError()("DocumentHistory.ComparisonTooLarge", {
193
+ maximumEditLength: Schema.Int.check(Schema.isGreaterThan(0)),
194
+ maximumLines: Schema.Int.check(Schema.isGreaterThan(0))
195
+ }) {};
196
+
197
+ //#endregion
198
+ //#region ../protocol/src/Collaboration.ts
199
+ /**
200
+ * Collaboration handshake and WebSocket envelopes. Command results correlate by RequestId;
201
+ * checkpoints carry the durable state vector needed for retry and recovery.
202
+ */
203
+ const StateVector = Schema.String.check(Schema.isBase64()).pipe(Schema.brand("StateVector"));
204
+ const HttpErrorStatus = Schema.Int.check(Schema.isBetween({
205
+ minimum: 400,
206
+ maximum: 599
207
+ }));
208
+ const CollaborationUser = Schema.Struct({
209
+ color: Schema.NonEmptyString,
210
+ id: Schema.NonEmptyString,
211
+ name: Schema.NonEmptyString
212
+ });
213
+ const CollaborationTicket = Schema.Struct({
214
+ authoringRevision: Schema.NonEmptyString,
215
+ authoredStateVector: StateVector,
216
+ documentName: Schema.NonEmptyString,
217
+ source: DocumentSource,
218
+ token: Schema.NonEmptyString,
219
+ url: Schema.NonEmptyString,
220
+ user: CollaborationUser,
221
+ workingStateVector: StateVector
222
+ }).annotate({ identifier: "CollaborationTicket" });
223
+ const CollaborationPreview = DocumentValidationResult.pipe(Schema.fieldsAssign({
224
+ markdown: Schema.String,
225
+ preview: Schema.Struct({
226
+ body: CmsMarkdownDocument,
227
+ fallbacks: Schema.Record(Schema.String, Schema.String)
228
+ })
229
+ })).annotate({ identifier: "CollaborationPreview" });
230
+ const CheckpointMessage = Schema.Struct({ type: Schema.tag("checkpoint") });
231
+ const PreviewMessage = Schema.Struct({
232
+ type: Schema.tag("preview"),
233
+ requestId: RequestId
234
+ });
235
+ const PublishMessage = Schema.Struct({
236
+ type: Schema.tag("publish"),
237
+ requestId: RequestId
238
+ });
239
+ const CollaborationClientMessage = Schema.Union([
240
+ CheckpointMessage,
241
+ PreviewMessage,
242
+ PublishMessage
243
+ ]).pipe(Schema.toTaggedUnion("type"));
244
+ const requestFailure = {
245
+ error: Schema.NonEmptyString,
246
+ statusCode: HttpErrorStatus
247
+ };
248
+ const RequestFailure = Schema.Struct({
249
+ status: Schema.tag("failure"),
250
+ ...requestFailure
251
+ });
252
+ const PreviewOutcome = Schema.Union([Schema.Struct({
253
+ status: Schema.tag("success"),
254
+ result: CollaborationPreview
255
+ }), RequestFailure]).pipe(Schema.toTaggedUnion("status"));
256
+ const PublishCheckpoint = Schema.Struct({
257
+ authoredStateVector: StateVector,
258
+ result: Schema.optionalKey(DocumentMutationResult)
259
+ });
260
+ const PublishOutcome = Schema.Union([Schema.Struct({
261
+ status: Schema.tag("success"),
262
+ authoredStateVector: StateVector,
263
+ result: DocumentMutationResult
264
+ }), Schema.Struct({
265
+ status: Schema.tag("failure"),
266
+ ...requestFailure,
267
+ checkpoint: Schema.optionalKey(PublishCheckpoint)
268
+ })]).pipe(Schema.toTaggedUnion("status"));
269
+ const WorkingSavedMessage = Schema.Struct({
270
+ type: Schema.tag("working-saved"),
271
+ workingStateVector: StateVector
272
+ });
273
+ const WorkingSaveErrorMessage = Schema.Struct({
274
+ type: Schema.tag("working-save-error"),
275
+ error: Schema.NonEmptyString
276
+ });
277
+ const PreviewResultMessage = Schema.Struct({
278
+ type: Schema.tag("preview-result"),
279
+ requestId: RequestId,
280
+ outcome: PreviewOutcome
281
+ });
282
+ const PublishResultMessage = Schema.Struct({
283
+ type: Schema.tag("publish-result"),
284
+ requestId: RequestId,
285
+ outcome: PublishOutcome
286
+ });
287
+ const CollaborationServerMessage = Schema.Union([
288
+ WorkingSavedMessage,
289
+ WorkingSaveErrorMessage,
290
+ PreviewResultMessage,
291
+ PublishResultMessage
292
+ ]).pipe(Schema.toTaggedUnion("type"));
293
+
294
+ //#endregion
295
+ //#region ../protocol/src/Users.ts
296
+ /**
297
+ * User administration inputs, offset-paginated directory results, and caller-visible command
298
+ * failures. Commands carry no idempotency key; clients resolve ambiguous outcomes by listing again.
299
+ */
300
+ const UserEmail = Schema.NonEmptyString.check(Schema.isMaxLength(320), Schema.isPattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/u)).annotate({ identifier: "UserEmail" });
301
+ const UserName = Schema.NonEmptyString.check(Schema.isMaxLength(200)).annotate({ identifier: "UserName" });
302
+ const UserPassword = Schema.NonEmptyString.check(Schema.isMinLength(8), Schema.isMaxLength(200)).annotate({ identifier: "UserPassword" });
303
+ const User = Schema.Struct({
304
+ id: Schema.NonEmptyString,
305
+ email: UserEmail,
306
+ name: UserName,
307
+ role: Role,
308
+ emailVerified: Schema.Boolean,
309
+ banned: Schema.Boolean,
310
+ createdAt: Schema.DateTimeUtcFromString,
311
+ updatedAt: Schema.DateTimeUtcFromString
312
+ }).annotate({ identifier: "User" });
313
+ const UserPage = Schema.Struct({
314
+ users: Schema.Array(User),
315
+ total: nonNegativeInt
316
+ }).annotate({ identifier: "UserPage" });
317
+ const UserQuery = Schema.Struct({
318
+ limit: Schema.optionalKey(Schema.Int.check(Schema.isBetween({
319
+ minimum: 1,
320
+ maximum: 100
321
+ }))),
322
+ offset: Schema.optionalKey(nonNegativeInt),
323
+ search: Schema.optionalKey(Schema.NonEmptyString.check(Schema.isMaxLength(200)))
324
+ }).annotate({ identifier: "UserQuery" });
325
+ const CreateUserInput = Schema.Struct({
326
+ email: UserEmail,
327
+ name: UserName,
328
+ password: UserPassword,
329
+ role: Role
330
+ }).annotate({ identifier: "CreateUserInput" });
331
+ var UserAlreadyExists = class extends Schema.TaggedError()("User.AlreadyExists", { email: UserEmail }) {};
332
+ var UserNotFound = class extends Schema.TaggedError()("User.NotFound", { userId: User.fields.id }) {};
333
+ var UserSelfRemovalDenied = class extends Schema.TaggedError()("User.SelfRemovalDenied", { userId: User.fields.id }) {};
334
+ var UserSelfRoleChangeDenied = class extends Schema.TaggedError()("User.SelfRoleChangeDenied", { userId: User.fields.id }) {};
335
+ var UserAdministrationDenied = class extends Schema.TaggedError()("UserAdministration.Denied", {}) {};
336
+
337
+ //#endregion
338
+ //#region ../protocol/src/Initialization.ts
339
+ const InitializationInput = Schema.Struct({
340
+ email: UserEmail,
341
+ initializationToken: Schema.NonEmptyString.check(Schema.isMaxLength(512)),
342
+ name: UserName,
343
+ password: UserPassword
344
+ }).annotate({ identifier: "InitializationInput" });
345
+ const InitializationState = Schema.Struct({ needsAdministrator: Schema.Boolean }).annotate({ identifier: "InitializationState" });
346
+ var InitializationAlreadyCompleted = class extends Schema.TaggedError()("Initialization.AlreadyCompleted", {}) {};
347
+ var InitializationProofInvalid = class extends Schema.TaggedError()("Initialization.ProofInvalid", {}) {};
348
+
349
+ //#endregion
350
+ //#region ../protocol/src/internal/http/failures.ts
351
+ var StudioInternalError = class extends Schema.TaggedError()("Studio.InternalError", { message: Schema.Literal("The request could not be completed.") }) {};
352
+ function studioInternalError() {
353
+ return new StudioInternalError({ message: "The request could not be completed." });
354
+ }
355
+
356
+ //#endregion
357
+ //#region ../protocol/src/internal/http/delivery.ts
358
+ const DeliverySurroundPayload = Schema.Struct({
359
+ lookup: PublishedDocumentLookup,
360
+ query: PublishedDocumentSurroundQuery
361
+ }).annotate({ identifier: "DeliverySurroundPayload" });
362
+ const InternalError$1 = StudioInternalError.pipe(HttpApiSchema.status(500));
363
+ const PublishedNotFound = PublishedDocumentNotFound.pipe(HttpApiSchema.status(404));
364
+ const Forbidden$1 = AccessDenied.pipe(HttpApiSchema.status(403));
365
+ const PublishedDeliveryApi = HttpApiGroup.make("delivery").add(HttpApiEndpoint.post("read", "/read", {
366
+ payload: PublishedDocumentLookup,
367
+ success: CompiledDocument,
368
+ error: [PublishedNotFound, InternalError$1]
369
+ }), HttpApiEndpoint.post("query", "/query", {
370
+ payload: PublishedDocumentQuery,
371
+ success: PublishedDocumentPage,
372
+ error: InternalError$1
373
+ }), HttpApiEndpoint.post("navigation", "/navigation", {
374
+ payload: DocumentNavigationQuery,
375
+ success: Schema.Array(DocumentNavigationItem),
376
+ error: InternalError$1
377
+ }), HttpApiEndpoint.post("surround", "/surround", {
378
+ payload: DeliverySurroundPayload,
379
+ success: DocumentSurround,
380
+ error: [PublishedNotFound, InternalError$1]
381
+ }), HttpApiEndpoint.get("seo", "/seo", {
382
+ success: SeoSnapshot,
383
+ error: InternalError$1
384
+ }), HttpApiEndpoint.get("sitemap", "/sitemap", {
385
+ success: Schema.Array(SitemapEntry),
386
+ error: InternalError$1
387
+ })).prefix("/delivery");
388
+ const PreviewDeliveryApi = HttpApiGroup.make("previewDelivery").add(HttpApiEndpoint.post("read", "/read", {
389
+ payload: PublishedDocumentLookup,
390
+ success: CompiledDocument,
391
+ error: [
392
+ PublishedNotFound,
393
+ InternalError$1,
394
+ Forbidden$1
395
+ ]
396
+ }), HttpApiEndpoint.post("query", "/query", {
397
+ payload: PublishedDocumentQuery,
398
+ success: PublishedDocumentPage,
399
+ error: [InternalError$1, Forbidden$1]
400
+ }), HttpApiEndpoint.post("navigation", "/navigation", {
401
+ payload: DocumentNavigationQuery,
402
+ success: Schema.Array(DocumentNavigationItem),
403
+ error: [InternalError$1, Forbidden$1]
404
+ }), HttpApiEndpoint.post("surround", "/surround", {
405
+ payload: DeliverySurroundPayload,
406
+ success: DocumentSurround,
407
+ error: [
408
+ PublishedNotFound,
409
+ InternalError$1,
410
+ Forbidden$1
411
+ ]
412
+ }), HttpApiEndpoint.get("seo", "/seo", {
413
+ success: SeoSnapshot,
414
+ error: [InternalError$1, Forbidden$1]
415
+ })).prefix("/preview");
416
+
417
+ //#endregion
418
+ //#region ../protocol/src/StudioHttp.ts
419
+ /**
420
+ * Studio HTTP paths, middleware contracts, payloads, and the composed CMS API. Every expected
421
+ * caller failure is a tagged schema with an explicit status; unclassified failures become the
422
+ * deliberately opaque `StudioInternalError`.
423
+ */
424
+ const ApiVersion = Schema.NonEmptyString.pipe(Schema.brand("ApiVersion"));
425
+ const StudioBootstrap = Schema.Struct({
426
+ basePath: PublicPath,
427
+ documents: DocumentConfiguration,
428
+ media: MediaConfiguration,
429
+ seo: Schema.optionalKey(SeoConfiguration),
430
+ version: ApiVersion
431
+ }).annotate({ identifier: "StudioBootstrap" });
432
+ var StudioBadRequest = class extends Schema.TaggedError()("Studio.BadRequest", { kind: Schema.Literals([
433
+ "Headers",
434
+ "Params",
435
+ "Payload",
436
+ "Query"
437
+ ]) }) {};
438
+ var StudioRequestConflict = class extends Schema.TaggedError()("Studio.RequestConflict", { retryable: Schema.Literal(true) }) {};
439
+ var StudioSchemaError = class extends HttpApiMiddleware.Service()("@internal/protocol/StudioSchemaError", { error: [StudioBadRequest.pipe(HttpApiSchema.status(400)), StudioInternalError.pipe(HttpApiSchema.status(500))] }) {};
440
+ var CurrentStudioRequest = class extends Context.Service()("@internal/protocol/CurrentStudioRequest") {};
441
+ var StudioAuthentication = class extends HttpApiMiddleware.Service()("@internal/protocol/StudioAuthentication", { error: [
442
+ AuthenticationRequired.pipe(HttpApiSchema.status(401)),
443
+ StudioEntryDenied.pipe(HttpApiSchema.status(403)),
444
+ StudioInternalError.pipe(HttpApiSchema.status(500))
445
+ ] }) {};
446
+ const ValidateDocumentPayload = Schema.Struct({
447
+ expectedVersion: UpdateDocumentInput.fields.expectedVersion,
448
+ markdown: UpdateDocumentInput.fields.markdown
449
+ });
450
+ const UpdateDocumentPayload = ValidateDocumentPayload.pipe(Schema.fieldsAssign({ reason: UpdateDocumentInput.fields.reason }));
451
+ const MoveDocumentPayload = Schema.Struct({
452
+ expectedVersion: MoveDocumentInput.fields.expectedVersion,
453
+ location: MoveDocumentInput.fields.location,
454
+ reason: MoveDocumentInput.fields.reason
455
+ });
456
+ const PublishDocumentPayload = Schema.Struct({
457
+ expectedVersion: PublishDocumentInput.fields.expectedVersion,
458
+ reason: PublishDocumentInput.fields.reason
459
+ });
460
+ const RemoveDocumentPayload = Schema.Struct({
461
+ expectedVersion: RemoveDocumentInput.fields.expectedVersion,
462
+ reason: RemoveDocumentInput.fields.reason
463
+ });
464
+ const RestoreDocumentPayload = Schema.Struct({
465
+ expectedVersion: RestoreDocumentInput.fields.expectedVersion,
466
+ revisionId: RestoreDocumentInput.fields.revisionId,
467
+ reason: RestoreDocumentInput.fields.reason
468
+ });
469
+ const UndeleteDocumentPayload = Schema.Struct({
470
+ expectedVersion: UndeleteDocumentInput.fields.expectedVersion,
471
+ reason: UndeleteDocumentInput.fields.reason
472
+ });
473
+ const ChangeRolePayload = Schema.Struct({ role: Role }).annotate({ identifier: "ChangeRolePayload" });
474
+ const MediaUploadQuery = Schema.Struct({
475
+ declaredByteSize: MediaUpload.fields.declaredByteSize,
476
+ folder: Schema.optionalKey(MediaFolder)
477
+ }).annotate({ identifier: "MediaUploadQuery" });
478
+ const MediaUploadPayload = Schema.Struct({ file: Multipart.SingleFileSchema }).pipe(HttpApiSchema.asMultipartStream({ maxParts: 1 }));
479
+ const NotFound = DocumentNotFound.pipe(HttpApiSchema.status(404));
480
+ const VersionConflict = DocumentVersionConflict.pipe(HttpApiSchema.status(409));
481
+ const Conflict = Schema.Union([
482
+ DocumentMediaUnavailable,
483
+ DocumentPathConflict,
484
+ DocumentVersionConflict
485
+ ]).pipe(HttpApiSchema.status(409));
486
+ const ValidationFailed = DocumentValidationFailed.pipe(HttpApiSchema.status(422));
487
+ const RequestConflict = StudioRequestConflict.pipe(HttpApiSchema.status(409));
488
+ const InternalError = StudioInternalError.pipe(HttpApiSchema.status(500));
489
+ const Forbidden = AccessDenied.pipe(HttpApiSchema.status(403));
490
+ const MutationErrors = [
491
+ NotFound,
492
+ Conflict,
493
+ ValidationFailed,
494
+ RequestConflict,
495
+ Forbidden,
496
+ InternalError
497
+ ];
498
+ const MediaNotFoundResponse = MediaNotFound.pipe(HttpApiSchema.status(404));
499
+ const MediaInvalidQuery = MediaInvalid.pipe(HttpApiSchema.status(400));
500
+ const MediaInvalidUpload = MediaInvalid.pipe(HttpApiSchema.status(422));
501
+ const MediaTooLargeResponse = MediaTooLarge.pipe(HttpApiSchema.status(413));
502
+ const MediaReferencedResponse = MediaReferenced.pipe(HttpApiSchema.status(409));
503
+ const MediaConflictResponse = MediaConflict.pipe(HttpApiSchema.status(409));
504
+ const InvalidActivityCursor = ActivityCursorInvalid.pipe(HttpApiSchema.status(400));
505
+ const InvalidHistoryCursor = DocumentHistoryInvalidCursor.pipe(HttpApiSchema.status(400));
506
+ const HistoryComparisonTooLarge = DocumentHistoryComparisonTooLarge.pipe(HttpApiSchema.status(422));
507
+ const AlreadyInitialized = InitializationAlreadyCompleted.pipe(HttpApiSchema.status(409));
508
+ const InitializationProofRejected = InitializationProofInvalid.pipe(HttpApiSchema.status(403));
509
+ const UserAdministrationForbidden = UserAdministrationDenied.pipe(HttpApiSchema.status(403));
510
+ const UserDuplicate = UserAlreadyExists.pipe(HttpApiSchema.status(409));
511
+ const MissingUser = UserNotFound.pipe(HttpApiSchema.status(404));
512
+ const UserSelfRemoval = UserSelfRemovalDenied.pipe(HttpApiSchema.status(409));
513
+ const UserSelfRoleChange = UserSelfRoleChangeDenied.pipe(HttpApiSchema.status(409));
514
+ const SeoConflict = SeoVersionConflict.pipe(HttpApiSchema.status(409));
515
+ const SeoConfirmation = SeoConfirmationRequired.pipe(HttpApiSchema.status(409));
516
+ const SeoMediaInvalid = SeoMediaUnavailable.pipe(HttpApiSchema.status(422));
517
+ const StudioDocumentsApi = HttpApiGroup.make("documents").add(HttpApiEndpoint.get("browse", "/", {
518
+ query: DocumentBrowseQuery,
519
+ success: Schema.Array(DocumentSummary),
520
+ error: [Forbidden, InternalError]
521
+ }), HttpApiEndpoint.get("trash", "/trash", {
522
+ success: Schema.Array(DeletedDocument),
523
+ error: [Forbidden, InternalError]
524
+ }), HttpApiEndpoint.get("read", "/:documentId", {
525
+ params: { documentId: DocumentId },
526
+ success: DocumentSource,
527
+ error: [
528
+ NotFound,
529
+ Forbidden,
530
+ InternalError
531
+ ]
532
+ }), HttpApiEndpoint.post("create", "/", {
533
+ payload: CreateDocumentInput,
534
+ success: DocumentMutationResult.pipe(HttpApiSchema.status(201)),
535
+ error: MutationErrors
536
+ }), HttpApiEndpoint.put("update", "/:documentId", {
537
+ params: { documentId: DocumentId },
538
+ payload: UpdateDocumentPayload,
539
+ success: DocumentMutationResult,
540
+ error: MutationErrors
541
+ }), HttpApiEndpoint.post("validate", "/:documentId/validate", {
542
+ params: { documentId: DocumentId },
543
+ payload: ValidateDocumentPayload,
544
+ success: DocumentValidationResult,
545
+ error: [
546
+ NotFound,
547
+ VersionConflict,
548
+ Forbidden,
549
+ InternalError
550
+ ]
551
+ }), HttpApiEndpoint.post("move", "/:documentId/move", {
552
+ params: { documentId: DocumentId },
553
+ payload: MoveDocumentPayload,
554
+ success: DocumentMutationResult,
555
+ error: MutationErrors
556
+ }), HttpApiEndpoint.post("duplicate", "/:documentId/duplicate", {
557
+ params: { documentId: DocumentId },
558
+ payload: Schema.Struct({
559
+ ...MoveDocumentPayload.fields,
560
+ includeChildren: DuplicateDocumentInput.fields.includeChildren
561
+ }),
562
+ success: DocumentMutationResult,
563
+ error: MutationErrors
564
+ }), HttpApiEndpoint.post("unpublish", "/:documentId/unpublish", {
565
+ params: { documentId: DocumentId },
566
+ payload: PublishDocumentPayload,
567
+ success: DocumentMutationResult,
568
+ error: MutationErrors
569
+ }), HttpApiEndpoint.post("publish", "/:documentId/publish", {
570
+ params: { documentId: DocumentId },
571
+ payload: PublishDocumentPayload,
572
+ success: DocumentMutationResult,
573
+ error: MutationErrors
574
+ }), HttpApiEndpoint.post("remove", "/:documentId/remove", {
575
+ params: { documentId: DocumentId },
576
+ payload: RemoveDocumentPayload,
577
+ success: DocumentMutationResult,
578
+ error: MutationErrors
579
+ }), HttpApiEndpoint.post("restore", "/:documentId/restore", {
580
+ params: { documentId: DocumentId },
581
+ payload: RestoreDocumentPayload,
582
+ success: DocumentMutationResult,
583
+ error: MutationErrors
584
+ }), HttpApiEndpoint.post("undelete", "/:documentId/undelete", {
585
+ params: { documentId: DocumentId },
586
+ payload: UndeleteDocumentPayload,
587
+ success: DocumentMutationResult,
588
+ error: MutationErrors
589
+ })).middleware(StudioAuthentication).prefix("/documents");
590
+ const StudioMediaApi = HttpApiGroup.make("media").add(HttpApiEndpoint.get("search", "/", {
591
+ query: MediaQuery,
592
+ success: MediaPage,
593
+ error: [
594
+ MediaInvalidQuery,
595
+ Forbidden,
596
+ InternalError
597
+ ]
598
+ }), HttpApiEndpoint.post("upload", "/", {
599
+ query: MediaUploadQuery,
600
+ payload: MediaUploadPayload,
601
+ success: MediaAsset.pipe(HttpApiSchema.status(201)),
602
+ error: [
603
+ MediaInvalidUpload,
604
+ MediaTooLargeResponse,
605
+ Forbidden,
606
+ InternalError
607
+ ]
608
+ }), HttpApiEndpoint.put("replace", "/:mediaId", {
609
+ params: { mediaId: MediaId },
610
+ query: MediaUploadQuery,
611
+ payload: MediaUploadPayload,
612
+ success: MediaAsset,
613
+ error: [
614
+ MediaNotFoundResponse,
615
+ MediaConflictResponse,
616
+ MediaInvalidUpload,
617
+ MediaTooLargeResponse,
618
+ Forbidden,
619
+ InternalError
620
+ ]
621
+ }), HttpApiEndpoint.post("cleanup", "/cleanup", {
622
+ success: MediaCleanupReport,
623
+ error: [Forbidden, InternalError]
624
+ }), HttpApiEndpoint.get("integrity", "/integrity", {
625
+ success: MediaIntegrityReport,
626
+ error: [Forbidden, InternalError]
627
+ }), HttpApiEndpoint.get("get", "/:mediaId", {
628
+ params: { mediaId: MediaId },
629
+ success: MediaAsset,
630
+ error: [
631
+ MediaNotFoundResponse,
632
+ Forbidden,
633
+ InternalError
634
+ ]
635
+ }), HttpApiEndpoint.delete("remove", "/:mediaId", {
636
+ params: { mediaId: MediaId },
637
+ success: HttpApiSchema.NoContent,
638
+ error: [
639
+ MediaNotFoundResponse,
640
+ MediaConflictResponse,
641
+ MediaReferencedResponse,
642
+ Forbidden,
643
+ InternalError
644
+ ]
645
+ })).middleware(StudioAuthentication).prefix("/media");
646
+ const StudioConfigurationApi = HttpApiGroup.make("configuration").add(HttpApiEndpoint.get("bootstrap", "/bootstrap", { success: StudioBootstrap })).middleware(StudioAuthentication);
647
+ const StudioSessionApi = HttpApiGroup.make("session").add(HttpApiEndpoint.get("current", "/session", { success: StudioActor })).middleware(StudioAuthentication);
648
+ const InitializationApi = HttpApiGroup.make("initialization").add(HttpApiEndpoint.get("status", "/initialization", {
649
+ success: InitializationState,
650
+ error: InternalError
651
+ }), HttpApiEndpoint.post("claim", "/initialization", {
652
+ payload: InitializationInput,
653
+ success: User.pipe(HttpApiSchema.status(201)),
654
+ error: [
655
+ AlreadyInitialized,
656
+ InitializationProofRejected,
657
+ InternalError
658
+ ]
659
+ }));
660
+ const StudioDiagnosticsApi = HttpApiGroup.make("diagnostics").add(HttpApiEndpoint.get("documents", "/diagnostics/documents", {
661
+ success: Schema.Array(DocumentIssue),
662
+ error: [Forbidden, InternalError]
663
+ })).middleware(StudioAuthentication);
664
+ const StudioHistoryApi = HttpApiGroup.make("history").add(HttpApiEndpoint.get("list", "/", {
665
+ query: DocumentHistoryQuery,
666
+ success: DocumentRevisionPage,
667
+ error: [
668
+ InvalidHistoryCursor,
669
+ Forbidden,
670
+ InternalError
671
+ ]
672
+ }), HttpApiEndpoint.get("read", "/:documentId/:revisionId", {
673
+ params: {
674
+ documentId: DocumentId,
675
+ revisionId: DocumentRevision.fields.revisionId
676
+ },
677
+ success: DocumentRevision,
678
+ error: [
679
+ NotFound,
680
+ Forbidden,
681
+ InternalError
682
+ ]
683
+ }), HttpApiEndpoint.post("compare", "/compare", {
684
+ payload: CompareDocumentRevisionsInput,
685
+ success: DocumentRevisionComparison,
686
+ error: [
687
+ NotFound,
688
+ HistoryComparisonTooLarge,
689
+ Forbidden,
690
+ InternalError
691
+ ]
692
+ })).middleware(StudioAuthentication).prefix("/history");
693
+ const StudioActivityApi = HttpApiGroup.make("activity").add(HttpApiEndpoint.get("list", "/", {
694
+ query: ActivityQuery,
695
+ success: ActivityPage,
696
+ error: [
697
+ InvalidActivityCursor,
698
+ Forbidden,
699
+ InternalError
700
+ ]
701
+ })).middleware(StudioAuthentication).prefix("/activity");
702
+ const StudioSeoApi = HttpApiGroup.make("seo").add(HttpApiEndpoint.get("read", "/", {
703
+ success: SeoState,
704
+ error: [Forbidden, InternalError]
705
+ }), HttpApiEndpoint.put("save", "/", {
706
+ payload: SaveSeoInput,
707
+ success: SeoMutationResult,
708
+ error: [
709
+ SeoConflict,
710
+ SeoConfirmation,
711
+ SeoMediaInvalid,
712
+ Forbidden,
713
+ InternalError
714
+ ]
715
+ }), HttpApiEndpoint.post("publish", "/publish", {
716
+ payload: PublishSeoInput,
717
+ success: SeoMutationResult,
718
+ error: [
719
+ SeoConflict,
720
+ SeoConfirmation,
721
+ SeoMediaInvalid,
722
+ Forbidden,
723
+ InternalError
724
+ ]
725
+ })).middleware(StudioAuthentication).prefix("/seo");
726
+ const StudioUserAdministrationApi = HttpApiGroup.make("users").add(HttpApiEndpoint.get("list", "/", {
727
+ query: UserQuery,
728
+ success: UserPage,
729
+ error: [UserAdministrationForbidden, InternalError]
730
+ }), HttpApiEndpoint.post("create", "/", {
731
+ payload: CreateUserInput,
732
+ success: User.pipe(HttpApiSchema.status(201)),
733
+ error: [
734
+ UserDuplicate,
735
+ UserAdministrationForbidden,
736
+ InternalError
737
+ ]
738
+ }), HttpApiEndpoint.post("changeRole", "/:userId/role", {
739
+ params: { userId: User.fields.id },
740
+ payload: ChangeRolePayload,
741
+ success: User,
742
+ error: [
743
+ MissingUser,
744
+ UserSelfRoleChange,
745
+ UserAdministrationForbidden,
746
+ InternalError
747
+ ]
748
+ }), HttpApiEndpoint.post("revokeSessions", "/:userId/sessions/revoke", {
749
+ params: { userId: User.fields.id },
750
+ success: User,
751
+ error: [
752
+ MissingUser,
753
+ UserAdministrationForbidden,
754
+ InternalError
755
+ ]
756
+ }), HttpApiEndpoint.delete("remove", "/:userId", {
757
+ params: { userId: User.fields.id },
758
+ success: User,
759
+ error: [
760
+ MissingUser,
761
+ UserSelfRemoval,
762
+ UserAdministrationForbidden,
763
+ InternalError
764
+ ]
765
+ })).middleware(StudioAuthentication).prefix("/users");
766
+ const StudioCollaborationApi = HttpApiGroup.make("collaboration").add(HttpApiEndpoint.post("ticket", "/:documentId/ticket", {
767
+ params: { documentId: DocumentId },
768
+ success: CollaborationTicket,
769
+ error: [
770
+ NotFound,
771
+ Forbidden,
772
+ InternalError
773
+ ]
774
+ })).middleware(StudioAuthentication).prefix("/collaboration");
775
+ const FormsApi = HttpApiGroup.make("forms").add(HttpApiEndpoint.post("submit", "/submit", {
776
+ payload: FormSubmission,
777
+ success: FormResult
778
+ })).prefix("/forms");
779
+ var StudioApi = class extends HttpApi.make("StudioApi").add(FormsApi, InitializationApi, PublishedDeliveryApi, StudioConfigurationApi, StudioSessionApi, StudioDiagnosticsApi, PreviewDeliveryApi.middleware(StudioAuthentication), StudioDocumentsApi, StudioHistoryApi, StudioActivityApi, StudioSeoApi, StudioUserAdministrationApi, StudioMediaApi, StudioCollaborationApi).middleware(StudioSchemaError).prefix(STUDIO_API_PATH) {};
780
+
781
+ //#endregion
782
+ //#region ../document/src/Seo.ts
783
+ function resolveDocumentSeo({ document, global, capabilities }) {
784
+ const authored = document.seo;
785
+ const baseTitle = authored?.title ?? document.title;
786
+ const title = global.titleTemplate?.replaceAll("%s", baseTitle) ?? baseTitle;
787
+ const description = authored?.description ?? document.description ?? global.defaultDescription;
788
+ const socialTitle = authored?.socialTitle ?? global.socialTitle ?? title;
789
+ const socialDescription = authored?.socialDescription ?? global.socialDescription ?? description;
790
+ const socialImage = authored?.socialImage ?? global.socialImage;
791
+ const canonicalUrl = authored?.canonicalUrl ?? SeoUrl.make(new URL(document.path, `${global.canonicalOrigin}/`).href);
792
+ const structuredData = resolveStructuredData(authored?.structuredData, capabilities);
793
+ const identity = resolveIdentity(global.identity, capabilities);
794
+ return {
795
+ title,
796
+ ...description === void 0 ? void 0 : { description },
797
+ socialTitle,
798
+ ...socialDescription === void 0 ? void 0 : { socialDescription },
799
+ ...socialImage === void 0 ? void 0 : { socialImage },
800
+ canonicalUrl,
801
+ index: global.index && (authored?.index ?? true),
802
+ follow: global.follow && (authored?.follow ?? true),
803
+ ...structuredData === void 0 ? void 0 : { structuredData },
804
+ ...identity === void 0 ? void 0 : { identity }
805
+ };
806
+ }
807
+ function resolveStructuredData(authored, capabilities) {
808
+ if (authored && capabilities.structuredDataTypes.includes(authored.type)) return authored;
809
+ return capabilities.structuredDataTypes.includes("WebPage") ? { type: "WebPage" } : void 0;
810
+ }
811
+ function resolveIdentity(identity, capabilities) {
812
+ return identity && capabilities.identityTypes.includes(identity.type) ? identity : void 0;
813
+ }
814
+
815
+ //#endregion
816
+ //#region ../server/src/internal/access-control.ts
817
+ const VIEWER_ACTIONS = /* @__PURE__ */ new Set([
818
+ "document.browse",
819
+ "document.read",
820
+ "document.trash",
821
+ "document.history",
822
+ "media.read",
823
+ "media.search",
824
+ "media.integrity"
825
+ ]);
826
+ const EDITOR_ACTIONS = /* @__PURE__ */ new Set([...DOCUMENT_ACTIONS, ...MEDIA_ACTIONS.filter((action) => action !== "media.cleanup")]);
827
+ var AccessControl = class extends Context.Service()("@internal/server/AccessControl") {};
828
+ const defaultAccessControlLayer = Layer.succeed(AccessControl, AccessControl.of({ require: Effect.fn("AccessControl.require")(function* (context, decision) {
829
+ if (isAllowed(context, decision.action)) return;
830
+ return yield* new AccessDenied({
831
+ action: decision.action,
832
+ actorId: context.actor.id,
833
+ resource: decision.resource
834
+ });
835
+ }) }));
836
+ function isAllowed(context, action) {
837
+ return context.actor.roles.some((role) => {
838
+ if (role === "administrator") return true;
839
+ return role === "editor" && EDITOR_ACTIONS.has(action) || role === "viewer" && VIEWER_ACTIONS.has(action);
840
+ });
841
+ }
842
+
843
+ //#endregion
844
+ //#region ../document/src/internal/editor/render.ts
845
+ const renderEditorMarkdown = Effect.fn("EditorDocument.renderMarkdown")((document) => {
846
+ return Effect.tryPromise({
847
+ try: () => renderMarkdown(toComarkDocument(withoutEditorMetadata(document))),
848
+ catch: (cause) => new EditorRenderFailed({ cause })
849
+ });
850
+ });
851
+
852
+ //#endregion
853
+ //#region ../document/src/internal/document-engine/relocate-links.ts
854
+ /** Rewrites only semantic page links, preserving bindings and unrelated content. */
855
+ function rewriteDocumentRoutes(document, destinations, components = []) {
856
+ let changed = false;
857
+ function link(value) {
858
+ const target = referenceTarget("a", /* @__PURE__ */ new Map([["href", value]]), document.path, void 0);
859
+ if (target?.kind !== "document" || !Predicate.isString(value)) return value;
860
+ const destination = destinations.get(target.path);
861
+ const sourceMoved = destinations.has(document.path);
862
+ if (!destination && (!sourceMoved || value.startsWith("/"))) return value;
863
+ const suffix = value.match(/[?#].*$/u)?.[0] ?? "";
864
+ const next = `${destination ?? target.path}${suffix}`;
865
+ if (next !== value) changed = true;
866
+ return next;
867
+ }
868
+ function properties(fields, values) {
869
+ const result = mutableJsonObject(values);
870
+ for (const field of fields) {
871
+ const value = values[field.name] === void 0 ? field.defaultValue : values[field.name];
872
+ if (field.input === "link") {
873
+ const next = link(value);
874
+ if (next !== void 0 && next !== value) result[field.name] = next;
875
+ } else if (field.fields) {
876
+ 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;
877
+ if (next !== void 0 && JSON.stringify(next) !== JSON.stringify(value)) result[field.name] = next;
878
+ }
879
+ }
880
+ return result;
881
+ }
882
+ function visit(node, renderData) {
883
+ if (Predicate.isString(node) || node[0] === null) return node;
884
+ const [tag, attributes, ...children] = node;
885
+ const projection = projectCmsEditorComponentAttributes(attributes, renderData);
886
+ let props = properties(components.find((component) => component.name.toLowerCase() === tag.toLowerCase())?.authoring.props ?? [], projection.props);
887
+ if (tag === "a") {
888
+ const href = link(projection.props.href);
889
+ if (href !== void 0) props = Object.assign({}, props, { href });
890
+ }
891
+ return [
892
+ tag,
893
+ JSON.stringify(props) === JSON.stringify(projection.props) ? attributes : encodeCmsEditorComponentAttributes(props, projection.bindings),
894
+ ...children.map((child) => visit(child, projection.renderData))
895
+ ];
896
+ }
897
+ const nodes = document.body.nodes.map((node) => visit(node, createCmsComarkRenderData(document.body)));
898
+ return changed ? Object.assign({}, document.body, { nodes }) : void 0;
899
+ }
900
+
901
+ //#endregion
902
+ //#region ../document/src/DocumentPlanning.ts
903
+ function planDocumentCreation(configuration, input) {
904
+ return Result.gen(function* () {
905
+ const collection = configuration.types.find((candidate) => candidate.collection === input.collection);
906
+ if (!collection) return yield* invalid$1(`Document collection "${input.collection}" does not exist.`, "collection");
907
+ const title = input.title.trim();
908
+ if (!title) return yield* invalid$1("A document title is required.", "title");
909
+ const locale = yield* resolveLocale(configuration, input.locale);
910
+ const address = yield* parseDocumentAddress(collection.addressPattern).pipe(Result.mapError(addressInvalid));
911
+ const relativePath = yield* resolveRelativePath(collection, input.location);
912
+ const data = Object.fromEntries(address.tokens.flatMap((token) => {
913
+ if (token.name === "slug" && relativePath) return [[token.name, relativePath.split("/").at(-1) ?? relativePath]];
914
+ return [];
915
+ }));
916
+ const relativeKey = yield* normalizePublicPath(relativePath).pipe(Result.mapError(addressInvalid));
917
+ const key = yield* resolveDocumentAddress(address, relativeKey, data).pipe(Result.mapError(addressInvalid));
918
+ const sourcePath = documentSourcePath(collection, relativePath, locale);
919
+ const path = localizeDocumentPath(key, locale, configuration.locales);
920
+ return Object.assign({
921
+ collection,
922
+ data,
923
+ key,
924
+ path,
925
+ sourcePath,
926
+ title
927
+ }, locale === void 0 ? void 0 : { locale });
928
+ });
929
+ }
930
+ const createInitialDocumentSource = Effect.fn("DocumentPlanning.createInitialSource")(function* (plan) {
931
+ const body = CmsMarkdownDocument.make({
932
+ frontmatter: initialFrontmatter(plan),
933
+ meta: {},
934
+ nodes: [[
935
+ "h1",
936
+ {},
937
+ plan.title
938
+ ]]
939
+ });
940
+ return {
941
+ body,
942
+ markdown: yield* renderEditorMarkdown(body)
943
+ };
944
+ });
945
+ function planDocumentMove(configuration, document, location) {
946
+ return Result.gen(function* () {
947
+ const collection = configuration.types.find((candidate) => candidate.collection === document.collection);
948
+ if (!collection) return yield* invalid$1(`Document collection "${document.collection}" does not exist.`, "documentId");
949
+ return documentSourcePath(collection, yield* resolveRelativePath(collection, location), document.locale);
950
+ });
951
+ }
952
+ function documentRelativePath(collection, entry) {
953
+ let sourcePath = entry.sourcePath;
954
+ if (entry.document.locale && sourcePath.startsWith(`${entry.document.locale}/`)) sourcePath = sourcePath.slice(entry.document.locale.length + 1);
955
+ if (collection.directory !== "." && sourcePath.startsWith(`${collection.directory}/`)) sourcePath = sourcePath.slice(collection.directory.length + 1);
956
+ sourcePath = sourcePath.replace(/\.(?:md|mdc)$/iu, "");
957
+ return sourcePath.toLocaleLowerCase() === "index" ? void 0 : sourcePath.replace(/\/index$/iu, "");
958
+ }
959
+ function documentSourcePath(collection, relativePath, locale) {
960
+ const sourceName = relativePath ? `${relativePath}.md` : "index.md";
961
+ return SourcePath.make([
962
+ locale,
963
+ collection.directory === "." ? void 0 : collection.directory,
964
+ sourceName
965
+ ].filter((value) => value !== void 0).join("/"));
966
+ }
967
+ function initialFrontmatter(plan) {
968
+ let frontmatter = {
969
+ ...plan.data,
970
+ title: plan.title
971
+ };
972
+ for (const field of plan.collection.authoringFields) if (field.defaultValue !== void 0 && nestedValue$1(frontmatter, field.path) === void 0) frontmatter = setNestedValue(frontmatter, field.path.split("."), field.defaultValue);
973
+ return frontmatter;
974
+ }
975
+ function nestedValue$1(value, path) {
976
+ return path.split(".").reduce((current, segment) => isJsonObject(current) ? current[segment] : void 0, value);
977
+ }
978
+ function setNestedValue(target, path, value) {
979
+ const [segment, ...rest] = path;
980
+ if (!segment) return target;
981
+ if (rest.length === 0) return Object.assign({}, target, { [segment]: value });
982
+ const current = target[segment];
983
+ return Object.assign({}, target, { [segment]: setNestedValue(isJsonObject(current) ? current : {}, rest, value) });
984
+ }
985
+ const isJsonObject = Schema.is(Schema.JsonObject);
986
+ function resolveLocale(configuration, requested) {
987
+ const locales = configuration.locales;
988
+ if (!locales) return requested === void 0 ? Result.succeed(void 0) : invalid$1(`Document locale "${requested}" is not configured.`, "locale");
989
+ const locale = requested ?? locales.defaultLocale;
990
+ return locales.locales.includes(locale) ? Result.succeed(locale) : invalid$1(`Document locale "${locale}" does not exist.`, "locale");
991
+ }
992
+ function resolveRelativePath(collection, location) {
993
+ if (location.kind === "home") return collection.addressPattern === "/:path*" ? Result.succeed("") : invalid$1("Only a root catch-all collection can use the home location.", "location");
994
+ if (location.parent !== void 0 && !collection.hierarchy) return invalid$1(`Document collection "${collection.collection}" does not allow hierarchy.`, "location.parent");
995
+ return normalizeDocumentRelativePath([location.parent, location.slug].filter((value) => value !== void 0).join("/")).pipe(Result.mapError((error) => addressInvalid({
996
+ ...error,
997
+ field: "location"
998
+ })));
999
+ }
1000
+ function addressInvalid(error) {
1001
+ return new DocumentValidationFailed({ issues: [validationIssue(error.message, error.field)] });
1002
+ }
1003
+ function invalid$1(message, field) {
1004
+ return Result.fail(new DocumentValidationFailed({ issues: [validationIssue(message, field)] }));
1005
+ }
1006
+ function validationIssue(message, field) {
1007
+ return field === void 0 ? {
1008
+ code: "document.invalid",
1009
+ message,
1010
+ severity: "error"
1011
+ } : {
1012
+ code: "document.invalid",
1013
+ field,
1014
+ message,
1015
+ severity: "error"
1016
+ };
1017
+ }
1018
+ /** Updates route-owned frontmatter when relocating a page in a slug-addressed collection. */
1019
+ const relocateDocumentMarkdown = Effect.fn("DocumentPlanning.relocateMarkdown")(function* (configuration, document, location, markdown) {
1020
+ const collection = configuration.types.find((type) => type.collection === document.collection);
1021
+ if (!collection || !collection.addressPattern.includes(":slug") || location.kind === "home") return markdown;
1022
+ return yield* renderEditorMarkdown({
1023
+ ...document.body,
1024
+ frontmatter: {
1025
+ ...document.body.frontmatter,
1026
+ slug: location.slug
1027
+ }
1028
+ });
1029
+ });
1030
+
1031
+ //#endregion
1032
+ //#region ../server/src/internal/documents/document-state.ts
1033
+ const isDocumentCompilationFailed = Schema.is(DocumentCompilationFailed);
1034
+ function makeDocumentState(authored, published, snapshot) {
1035
+ const activeById = /* @__PURE__ */ new Map();
1036
+ for (const source of snapshot.active) {
1037
+ if (activeById.has(source.documentId)) throw new Error(`Document "${source.documentId}" appears more than once in the repository snapshot.`);
1038
+ activeById.set(source.documentId, source);
1039
+ }
1040
+ const authoredById = /* @__PURE__ */ new Map();
1041
+ for (const document of authored.documents) {
1042
+ if (authoredById.has(document.id)) throw new Error(`Compiled document "${document.id}" appears more than once in the authored catalog.`);
1043
+ if (!activeById.has(document.id)) throw new Error(`Compiled document "${document.id}" is missing from the repository snapshot.`);
1044
+ authoredById.set(document.id, document);
1045
+ }
1046
+ for (const documentId of activeById.keys()) if (!authoredById.has(documentId)) throw new Error(`Compiled document "${documentId}" is missing from the repository snapshot.`);
1047
+ return {
1048
+ activeById,
1049
+ authored,
1050
+ authoredById,
1051
+ published,
1052
+ snapshot
1053
+ };
1054
+ }
1055
+ const compileDocumentState = Effect.fn("DocumentState.compile")(function* (engine, snapshot, previous) {
1056
+ return makeDocumentState(yield* engine.compileAll({
1057
+ previous: previous?.authored,
1058
+ sources: authoredSources(snapshot)
1059
+ }), yield* engine.compileAll({
1060
+ previous: previous?.published,
1061
+ sources: publishedSources(snapshot)
1062
+ }), snapshot);
1063
+ });
1064
+ function authoredSources(snapshot) {
1065
+ return snapshot.active.map((source) => ({
1066
+ documentId: source.documentId,
1067
+ draft: source.publishedVersion !== source.version,
1068
+ markdown: source.markdown,
1069
+ sourcePath: source.sourcePath
1070
+ }));
1071
+ }
1072
+ function publishedSources(snapshot) {
1073
+ return snapshot.published.map((source) => ({
1074
+ documentId: source.documentId,
1075
+ draft: false,
1076
+ markdown: source.markdown,
1077
+ publishedAt: source.editedAt,
1078
+ sourcePath: source.sourcePath
1079
+ }));
1080
+ }
1081
+ const compileValidCatalog = Effect.fn("DocumentState.compileValid")(function* (engine, sources, previous) {
1082
+ const result = yield* Effect.result(engine.compileAll({
1083
+ previous,
1084
+ sources
1085
+ }));
1086
+ if (Result.isFailure(result)) return yield* new DocumentValidationFailed({ issues: compilationIssues(result.failure) });
1087
+ const errors = result.success.issues.filter((issue) => issue.severity === "error");
1088
+ if (errors.length > 0) return yield* new DocumentValidationFailed({ issues: errors });
1089
+ return result.success;
1090
+ });
1091
+ const validateDocumentSources = Effect.fn("DocumentState.validate")(function* (engine, sources, sourcePath, previous) {
1092
+ const result = yield* Effect.result(engine.compileAll({
1093
+ previous,
1094
+ sources
1095
+ }));
1096
+ if (Result.isFailure(result)) return DocumentValidationResult.make({
1097
+ issues: compilationIssues(result.failure),
1098
+ valid: false
1099
+ });
1100
+ const catalog = result.success;
1101
+ const document = catalog.documents.find((candidate) => candidate.source.path === sourcePath);
1102
+ const issues = catalog.issues;
1103
+ const validation = {
1104
+ issues,
1105
+ valid: document !== void 0 && !issues.some((issue) => issue.severity === "error")
1106
+ };
1107
+ return DocumentValidationResult.make(Object.assign(validation, document === void 0 ? void 0 : { document }));
1108
+ });
1109
+ function authoringEntries(state, query) {
1110
+ return state.snapshot.active.flatMap((source) => {
1111
+ const document = state.authoredById.get(source.documentId);
1112
+ if (!document) throw new Error(`Compiled document "${source.documentId}" is missing from the repository snapshot.`);
1113
+ if (query.collection !== void 0 && document.collection !== query.collection) return [];
1114
+ if (query.locale !== void 0 && document.locale !== query.locale) return [];
1115
+ return [DocumentSummary.make({
1116
+ ...browseMetadata(state, source),
1117
+ document,
1118
+ sourcePath: source.sourcePath,
1119
+ version: source.version
1120
+ })];
1121
+ });
1122
+ }
1123
+ function deletedEntries(state) {
1124
+ return state.snapshot.deleted.map((source) => DeletedDocument.make({
1125
+ ...source.title ? { title: source.title } : void 0,
1126
+ ...source.path ? { path: source.path } : void 0,
1127
+ ...source.trashBatchId ? { trashBatchId: source.trashBatchId } : void 0,
1128
+ documentId: source.documentId,
1129
+ deletedAt: source.deletedAt,
1130
+ sourcePath: source.sourcePath,
1131
+ version: source.version
1132
+ }));
1133
+ }
1134
+ function entryById(state, documentId) {
1135
+ const source = state.activeById.get(documentId);
1136
+ const document = state.authoredById.get(documentId);
1137
+ return source && document ? DocumentSummary.make({
1138
+ ...browseMetadata(state, source),
1139
+ document,
1140
+ sourcePath: source.sourcePath,
1141
+ version: source.version
1142
+ }) : void 0;
1143
+ }
1144
+ function compilationIssues(error) {
1145
+ if (isDocumentCompilationFailed(error)) return error.issues;
1146
+ return [{
1147
+ code: "markdown.parse",
1148
+ message: errorMessage(error.cause),
1149
+ severity: "error",
1150
+ sourcePath: error.sourcePath
1151
+ }];
1152
+ }
1153
+ function errorMessage(cause) {
1154
+ return cause instanceof Error && cause.message ? cause.message : "Markdown could not be parsed.";
1155
+ }
1156
+ function browseMetadata(state, source) {
1157
+ const live = state.published.documents.find((document) => document.id === source.documentId);
1158
+ const metadata = {};
1159
+ if (source.createdBy) metadata.createdBy = source.createdBy;
1160
+ if (source.editedBy) metadata.editedBy = source.editedBy;
1161
+ if (source.editedAt) metadata.editedAt = source.editedAt;
1162
+ if (source.publishedVersion) metadata.publishedVersion = source.publishedVersion;
1163
+ if (live) metadata.publishedPath = live.path;
1164
+ return metadata;
1165
+ }
1166
+
1167
+ //#endregion
1168
+ //#region ../server/src/internal/documents/document-tree-mutation.ts
1169
+ /** Selects by route boundaries, never by title or a raw prefix such as /about matching /about-us. */
1170
+ function subtreeIds(state, documentId) {
1171
+ const parent = state.authoredById.get(documentId);
1172
+ if (!parent) return [documentId];
1173
+ 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);
1174
+ }
1175
+ const prepareTreeMutation = Effect.fn("Documents.prepareTreeMutation")(function* (dependencies, state, command) {
1176
+ const { configuration, engine } = dependencies;
1177
+ const input = command.input;
1178
+ const root = command.action === "document.undelete" ? state.snapshot.deleted.find((source) => source.documentId === input.documentId) : state.activeById.get(input.documentId);
1179
+ if (!root) return yield* new DocumentNotFound({ documentId: input.documentId });
1180
+ if (root.version !== input.expectedVersion) return yield* new DocumentVersionConflict({
1181
+ documentId: root.documentId,
1182
+ actualVersion: root.version,
1183
+ expectedVersion: input.expectedVersion
1184
+ });
1185
+ let sources;
1186
+ if (command.action === "document.undelete") {
1187
+ const deleted = state.snapshot.deleted.find((source) => source.documentId === root.documentId);
1188
+ sources = state.snapshot.deleted.filter((source) => source.documentId === root.documentId || deleted && deleted.trashBatchId && source.trashBatchId === deleted.trashBatchId && deleted.path !== void 0 && source.path?.startsWith(`${deleted.path}/`));
1189
+ } else {
1190
+ const ids = new Set(command.action === "document.duplicate" && !command.input.includeChildren ? [root.documentId] : subtreeIds(state, root.documentId));
1191
+ sources = state.snapshot.active.filter((source) => ids.has(source.documentId));
1192
+ }
1193
+ sources = [root, ...sources.filter((source) => source.documentId !== root.documentId)];
1194
+ const plans = [];
1195
+ const parent = state.authoredById.get(root.documentId);
1196
+ for (const source of sources) {
1197
+ if (command.action === "document.remove" || command.action === "document.undelete") {
1198
+ const page = state.authoredById.get(source.documentId);
1199
+ plans.push({
1200
+ ...command.action === "document.remove" ? {
1201
+ trashBatchId: root.version,
1202
+ title: page?.title,
1203
+ path: page?.path
1204
+ } : void 0,
1205
+ action: command.action,
1206
+ documentId: source.documentId,
1207
+ expectedVersion: source.version,
1208
+ markdown: source.markdown,
1209
+ sourcePath: source.sourcePath
1210
+ });
1211
+ continue;
1212
+ }
1213
+ const page = state.authoredById.get(source.documentId);
1214
+ const collection = configuration.types.find((type) => type.collection === page?.collection);
1215
+ if (!page || !parent || !collection) return yield* new DocumentNotFound({ documentId: source.documentId });
1216
+ if (command.action === "document.duplicate" && !collection.canCreate) return yield* invalid("This collection does not allow new pages.");
1217
+ const destination = command.input.location;
1218
+ if (parent.key === "/" && command.action === "document.move") return yield* invalid("The home page cannot be moved.");
1219
+ const rootRelative = documentRelativePath(collection, {
1220
+ document: parent,
1221
+ sourcePath: root.sourcePath
1222
+ }) ?? "";
1223
+ const nextRelative = destination.kind === "home" ? "" : [destination.parent, destination.slug].filter(Boolean).join("/");
1224
+ if (rootRelative && (nextRelative === rootRelative || nextRelative.startsWith(`${rootRelative}/`))) return yield* invalid("Choose a destination outside this page and its subpages.");
1225
+ const relative = documentRelativePath(collection, {
1226
+ document: page,
1227
+ sourcePath: source.sourcePath
1228
+ }) ?? "";
1229
+ const segments = `${nextRelative}${source.documentId === root.documentId ? "" : relative.slice(rootRelative.length)}`.split("/").filter(Boolean);
1230
+ const slug = segments.pop();
1231
+ const location = slug ? Object.assign({
1232
+ kind: "entry",
1233
+ slug: yield* decodeSlug(slug)
1234
+ }, segments.length ? { parent: yield* decodeParent(segments.join("/")) } : void 0) : { kind: "home" };
1235
+ const sourcePath = yield* Effect.fromResult(planDocumentMove(configuration, page, location));
1236
+ const markdown = yield* relocateDocumentMarkdown(configuration, page, location, source.markdown).pipe(Effect.mapError(() => invalid("The page could not be copied to its new URL.")));
1237
+ if (command.action === "document.duplicate") {
1238
+ const documentId = yield* command.nextDocumentId;
1239
+ plans.push({
1240
+ action: "document.create",
1241
+ documentId,
1242
+ markdown,
1243
+ sourcePath
1244
+ });
1245
+ } else plans.push({
1246
+ action: "document.move",
1247
+ documentId: source.documentId,
1248
+ expectedVersion: source.version,
1249
+ markdown,
1250
+ sourcePath,
1251
+ publishedVersion: source.publishedVersion
1252
+ });
1253
+ }
1254
+ const changed = new Set(plans.map((plan) => plan.documentId));
1255
+ const nextSources = authoredSources(state.snapshot).filter((source) => !changed.has(source.documentId));
1256
+ if (command.action !== "document.remove") for (const plan of plans) {
1257
+ if (nextSources.some((source) => source.sourcePath === plan.sourcePath)) return yield* new DocumentPathConflict({ sourcePath: plan.sourcePath });
1258
+ nextSources.push({
1259
+ documentId: plan.documentId,
1260
+ draft: true,
1261
+ markdown: plan.markdown,
1262
+ sourcePath: plan.sourcePath
1263
+ });
1264
+ }
1265
+ if (command.action === "document.move" || command.action === "document.duplicate") {
1266
+ const preliminary = yield* engine.compileAll({
1267
+ sources: nextSources,
1268
+ previous: state.authored
1269
+ });
1270
+ const destinations = /* @__PURE__ */ new Map();
1271
+ for (let index = 0; index < sources.length; index++) {
1272
+ const original = sources[index];
1273
+ const plan = plans[index];
1274
+ const oldPage = original && state.authoredById.get(original.documentId);
1275
+ const newPage = plan && preliminary.documents.find((document) => document.id === plan.documentId);
1276
+ if (oldPage && newPage) destinations.set(oldPage.path, newPage.path);
1277
+ }
1278
+ const originals = command.action === "document.move" ? state.snapshot.active : sources;
1279
+ for (const source of originals) {
1280
+ const page = state.authoredById.get(source.documentId);
1281
+ if (!page) continue;
1282
+ const body = rewriteDocumentRoutes(page, destinations, dependencies.components);
1283
+ if (!body) continue;
1284
+ const index = command.action === "document.duplicate" ? sources.findIndex((item) => item.documentId === source.documentId) : plans.findIndex((plan) => plan.documentId === source.documentId);
1285
+ const existing = index >= 0 ? plans[index] : void 0;
1286
+ const projected = existing && preliminary.documents.find((document) => document.id === existing.documentId);
1287
+ const markdown = yield* renderEditorMarkdown(Object.assign({}, body, { frontmatter: projected?.body.frontmatter ?? body.frontmatter })).pipe(Effect.mapError(() => invalid("Page links could not be updated.")));
1288
+ if (existing) plans[index] = {
1289
+ ...existing,
1290
+ markdown
1291
+ };
1292
+ else plans.push({
1293
+ action: "document.update",
1294
+ documentId: source.documentId,
1295
+ expectedVersion: source.version,
1296
+ sourcePath: source.sourcePath,
1297
+ markdown,
1298
+ publishedVersion: source.publishedVersion
1299
+ });
1300
+ }
1301
+ for (const plan of plans) {
1302
+ const index = nextSources.findIndex((source) => source.documentId === plan.documentId);
1303
+ const updated = {
1304
+ documentId: plan.documentId,
1305
+ sourcePath: plan.sourcePath,
1306
+ markdown: plan.markdown,
1307
+ draft: true
1308
+ };
1309
+ if (index >= 0) nextSources[index] = updated;
1310
+ else nextSources.push(updated);
1311
+ }
1312
+ }
1313
+ const first = plans[0];
1314
+ if (!first) return yield* Effect.die(/* @__PURE__ */ new Error("Empty subtree mutation."));
1315
+ return {
1316
+ authored: yield* compileValidCatalog(engine, nextSources, state.authored),
1317
+ published: command.action === "document.remove" ? yield* compileValidCatalog(engine, publishedSources(state.snapshot).filter((source) => !changed.has(source.documentId)), state.published) : state.published,
1318
+ commitPlan: first,
1319
+ additionalPlans: plans.slice(1)
1320
+ };
1321
+ });
1322
+ const decodeSlug = (value) => Schema.decodeEffect(Slug)(value).pipe(Effect.mapError(() => invalid("Invalid URL ending.")));
1323
+ const decodeParent = (value) => Schema.decodeEffect(RelativeDocumentPath)(value).pipe(Effect.mapError(() => invalid("Invalid parent URL.")));
1324
+ function invalid(message) {
1325
+ return new DocumentValidationFailed({ issues: [{
1326
+ code: "document.invalid",
1327
+ severity: "error",
1328
+ message
1329
+ }] });
1330
+ }
1331
+
1332
+ //#endregion
1333
+ //#region ../server/src/internal/documents/document-lifecycle.ts
1334
+ const PreparedDocumentMutation = Data.taggedEnum();
1335
+ const prepareDocumentMutation = Effect.fn("DocumentLifecycle.prepare")(function* (dependencies, state, command) {
1336
+ const { engine, repository } = dependencies;
1337
+ 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));
1338
+ const authored = authoredSources(state.snapshot);
1339
+ const published = publishedSources(state.snapshot);
1340
+ switch (command.action) {
1341
+ case "document.create": {
1342
+ yield* ensurePathAvailable(state, command.sourcePath);
1343
+ const nextAuthored = [...authored, {
1344
+ documentId: command.documentId,
1345
+ draft: true,
1346
+ markdown: command.markdown,
1347
+ sourcePath: command.sourcePath
1348
+ }];
1349
+ return PreparedDocumentMutation.Commit({
1350
+ authored: yield* compileValidCatalog(engine, nextAuthored, state.authored),
1351
+ published: state.published,
1352
+ commitPlan: {
1353
+ action: command.action,
1354
+ documentId: command.documentId,
1355
+ markdown: command.markdown,
1356
+ sourcePath: command.sourcePath
1357
+ }
1358
+ });
1359
+ }
1360
+ case "document.update": {
1361
+ const current = yield* requireActive(state, command.input.documentId);
1362
+ yield* requireVersion(current, command.input.expectedVersion);
1363
+ const nextAuthored = replaceSource(authored, {
1364
+ documentId: current.documentId,
1365
+ draft: true,
1366
+ markdown: command.input.markdown,
1367
+ sourcePath: current.sourcePath
1368
+ });
1369
+ return PreparedDocumentMutation.Commit({
1370
+ authored: yield* compileValidCatalog(engine, nextAuthored, state.authored),
1371
+ published: state.published,
1372
+ commitPlan: {
1373
+ action: command.action,
1374
+ documentId: current.documentId,
1375
+ expectedVersion: command.input.expectedVersion,
1376
+ markdown: command.input.markdown,
1377
+ publishedVersion: current.publishedVersion,
1378
+ sourcePath: current.sourcePath
1379
+ }
1380
+ });
1381
+ }
1382
+ case "document.restore": {
1383
+ const current = yield* requireActive(state, command.input.documentId);
1384
+ yield* requireVersion(current, command.input.expectedVersion);
1385
+ const historical = yield* repository.revision(current.documentId, command.input.revisionId);
1386
+ const nextAuthored = replaceSource(authored, {
1387
+ documentId: current.documentId,
1388
+ draft: true,
1389
+ markdown: historical.markdown,
1390
+ sourcePath: current.sourcePath
1391
+ });
1392
+ return PreparedDocumentMutation.Commit({
1393
+ authored: yield* compileValidCatalog(engine, nextAuthored, state.authored),
1394
+ published: state.published,
1395
+ commitPlan: {
1396
+ action: command.action,
1397
+ documentId: current.documentId,
1398
+ expectedVersion: command.input.expectedVersion,
1399
+ markdown: historical.markdown,
1400
+ publishedVersion: current.publishedVersion,
1401
+ revisionId: command.input.revisionId,
1402
+ sourcePath: current.sourcePath
1403
+ }
1404
+ });
1405
+ }
1406
+ case "document.unpublish": {
1407
+ const current = yield* requireActive(state, command.input.documentId);
1408
+ yield* requireVersion(current, command.input.expectedVersion);
1409
+ return PreparedDocumentMutation.Commit({
1410
+ authored: yield* compileValidCatalog(engine, replaceSource(authored, {
1411
+ documentId: current.documentId,
1412
+ draft: true,
1413
+ markdown: current.markdown,
1414
+ sourcePath: current.sourcePath
1415
+ }), state.authored),
1416
+ published: yield* compileValidCatalog(engine, withoutDocument(published, current.documentId), state.published),
1417
+ commitPlan: {
1418
+ action: command.action,
1419
+ documentId: current.documentId,
1420
+ expectedVersion: current.version,
1421
+ markdown: current.markdown,
1422
+ sourcePath: current.sourcePath,
1423
+ revisionId: current.revisionId
1424
+ }
1425
+ });
1426
+ }
1427
+ case "document.publish": {
1428
+ const current = yield* requireActive(state, command.input.documentId);
1429
+ yield* requireVersion(current, command.input.expectedVersion);
1430
+ if (current.publishedVersion === current.version) {
1431
+ const entry = entryById(state, current.documentId);
1432
+ const result = {
1433
+ action: "document.publish",
1434
+ affectedDocumentIds: [current.documentId],
1435
+ documentId: current.documentId,
1436
+ generation: state.snapshot.generation,
1437
+ revisionId: current.revisionId,
1438
+ sourcePath: current.sourcePath,
1439
+ version: current.version
1440
+ };
1441
+ return PreparedDocumentMutation.Unchanged({ result: DocumentMutationResult.make(Object.assign(result, entry === void 0 ? void 0 : { entry })) });
1442
+ }
1443
+ const source = {
1444
+ documentId: current.documentId,
1445
+ draft: false,
1446
+ markdown: current.markdown,
1447
+ sourcePath: current.sourcePath
1448
+ };
1449
+ return PreparedDocumentMutation.Commit({
1450
+ authored: yield* compileValidCatalog(engine, replaceSource(authored, source), state.authored),
1451
+ published: yield* compileValidCatalog(engine, replaceSource(published, source), state.published),
1452
+ commitPlan: {
1453
+ action: command.action,
1454
+ documentId: current.documentId,
1455
+ expectedVersion: command.input.expectedVersion,
1456
+ markdown: current.markdown,
1457
+ revisionId: current.revisionId,
1458
+ sourcePath: current.sourcePath
1459
+ }
1460
+ });
1461
+ }
1462
+ }
1463
+ });
1464
+ const validateDocumentUpdate = Effect.fn("DocumentLifecycle.validateUpdate")(function* (engine, state, input) {
1465
+ const current = yield* requireActive(state, input.documentId);
1466
+ yield* requireVersion(current, input.expectedVersion);
1467
+ const source = {
1468
+ documentId: current.documentId,
1469
+ draft: true,
1470
+ markdown: input.markdown,
1471
+ sourcePath: current.sourcePath
1472
+ };
1473
+ return yield* validateDocumentSources(engine, replaceSource(authoredSources(state.snapshot), source), current.sourcePath, state.authored);
1474
+ });
1475
+ function requireActive(state, documentId) {
1476
+ const source = state.activeById.get(documentId);
1477
+ return source ? Effect.succeed(source) : Effect.fail(new DocumentNotFound({ documentId }));
1478
+ }
1479
+ function requireVersion(source, expectedVersion) {
1480
+ return source.version === expectedVersion ? Effect.void : Effect.fail(new DocumentVersionConflict({
1481
+ actualVersion: source.version,
1482
+ documentId: source.documentId,
1483
+ expectedVersion
1484
+ }));
1485
+ }
1486
+ function ensurePathAvailable(state, sourcePath, exceptDocumentId) {
1487
+ return state.snapshot.active.some((source) => source.sourcePath === sourcePath && source.documentId !== exceptDocumentId) ? Effect.fail(new DocumentPathConflict({ sourcePath })) : Effect.void;
1488
+ }
1489
+ function replaceSource(sources, source) {
1490
+ return [...sources.filter((candidate) => candidate.documentId !== source.documentId), source];
1491
+ }
1492
+ function withoutDocument(sources, documentId) {
1493
+ return sources.filter((source) => source.documentId !== documentId);
1494
+ }
1495
+
1496
+ //#endregion
1497
+ //#region ../server/src/internal/documents/document-media-references.ts
1498
+ function projectMediaReferences(authored, published, documentId) {
1499
+ const references = [...referencesFromCatalog(authored, documentId), ...referencesFromCatalog(published, documentId)];
1500
+ return [...new Map(references.map((reference) => [referenceIdentity(reference), reference])).values()];
1501
+ }
1502
+ function referencesFromCatalog(catalog, documentId) {
1503
+ return catalog.references.all.filter((reference) => reference.target.kind === "media" && (documentId === void 0 || reference.source.documentId === documentId)).map(toMediaReference);
1504
+ }
1505
+ function toMediaReference(reference) {
1506
+ if (reference.target.kind !== "media") throw new Error("Expected a media reference.");
1507
+ return MediaReference.make({
1508
+ documentId: reference.source.documentId,
1509
+ ...reference.target.mediaId ? { mediaId: reference.target.mediaId } : void 0,
1510
+ nodePath: reference.nodePath,
1511
+ objectKey: MediaObjectKey.make(reference.target.key),
1512
+ path: reference.source.path,
1513
+ sourcePath: reference.source.sourcePath
1514
+ });
1515
+ }
1516
+ function referenceIdentity(reference) {
1517
+ return `${reference.documentId}:${reference.sourcePath}:${reference.nodePath.join(".")}:${reference.objectKey}`;
1518
+ }
1519
+
1520
+ //#endregion
1521
+ //#region ../server/src/internal/documents/document-mutation.ts
1522
+ /** Authorize every affected document before the adapter commits the approved plan atomically. */
1523
+ const commitDocumentMutation = Effect.fn("Documents.commitMutation")(function* (accessControl, dependencies, current, command, activity) {
1524
+ if (command.action === "document.move" || command.action === "document.remove" || command.action === "document.duplicate") {
1525
+ const ids = command.action === "document.duplicate" && !command.input.includeChildren ? [command.input.documentId] : subtreeIds(current, command.input.documentId);
1526
+ for (const documentId of ids) yield* accessControl.require({
1527
+ actor: activity.actor,
1528
+ requestId: activity.requestId
1529
+ }, {
1530
+ action: command.action,
1531
+ resource: {
1532
+ kind: "document",
1533
+ documentId
1534
+ }
1535
+ });
1536
+ if (command.action === "document.duplicate") {
1537
+ const source = current.authoredById.get(command.input.documentId);
1538
+ if (source) yield* accessControl.require({
1539
+ actor: activity.actor,
1540
+ requestId: activity.requestId
1541
+ }, {
1542
+ action: "document.create",
1543
+ resource: {
1544
+ kind: "collection",
1545
+ collection: source.collection,
1546
+ locale: source.locale
1547
+ }
1548
+ });
1549
+ }
1550
+ }
1551
+ const prepared = yield* prepareDocumentMutation(dependencies, current, command);
1552
+ if (command.action === "document.move" && Predicate.isTagged("Commit")(prepared)) {
1553
+ for (const plan of prepared.additionalPlans ?? []) if (plan.action === "document.update") yield* accessControl.require({
1554
+ actor: activity.actor,
1555
+ requestId: activity.requestId
1556
+ }, {
1557
+ action: "document.update",
1558
+ resource: {
1559
+ kind: "document",
1560
+ documentId: plan.documentId
1561
+ }
1562
+ });
1563
+ }
1564
+ if (command.action === "document.undelete" && Predicate.isTagged("Commit")(prepared)) for (const plan of [prepared.commitPlan, ...prepared.additionalPlans ?? []]) yield* accessControl.require({
1565
+ actor: activity.actor,
1566
+ requestId: activity.requestId
1567
+ }, {
1568
+ action: "document.undelete",
1569
+ resource: {
1570
+ kind: "document",
1571
+ documentId: plan.documentId
1572
+ }
1573
+ });
1574
+ if (Predicate.isTagged("Unchanged")(prepared)) return {
1575
+ kind: "unchanged",
1576
+ result: prepared.result
1577
+ };
1578
+ const committed = yield* dependencies.repository.commit({
1579
+ activity,
1580
+ plan: prepared.commitPlan,
1581
+ additionalPlans: prepared.additionalPlans,
1582
+ expectedGeneration: current.snapshot.generation,
1583
+ mediaReferences: [prepared.commitPlan, ...prepared.additionalPlans ?? []].flatMap((plan) => projectMediaReferences(prepared.authored, prepared.published, plan.documentId))
1584
+ });
1585
+ const next = makeDocumentState(prepared.authored, prepared.published, committed.snapshot);
1586
+ const entry = entryById(next, committed.mutation.documentId);
1587
+ return {
1588
+ kind: "committed",
1589
+ next,
1590
+ result: DocumentMutationResult.make(Object.assign({}, committed.mutation, entry === void 0 ? void 0 : { entry }))
1591
+ };
1592
+ });
1593
+
1594
+ //#endregion
1595
+ //#region ../server/src/internal/documents/document-import.ts
1596
+ const importDocumentSources = Effect.fn("Documents.importSources")(function* (dependencies, input, activity) {
1597
+ const report = {
1598
+ created: 0,
1599
+ preserved: 0,
1600
+ published: 0,
1601
+ unchanged: 0,
1602
+ updated: 0
1603
+ };
1604
+ for (const source of input.sources) {
1605
+ const plan = planDocumentImport(yield* dependencies.current(source.sourcePath), source, input.mode);
1606
+ switch (plan.kind) {
1607
+ case "create": {
1608
+ const created = yield* dependencies.runMutation({
1609
+ action: "document.create",
1610
+ documentId: yield* dependencies.nextDocumentId,
1611
+ markdown: source.markdown,
1612
+ sourcePath: source.sourcePath
1613
+ }, activity, input.reason);
1614
+ report.created += 1;
1615
+ if (plan.publish) {
1616
+ yield* publish(dependencies, created, input, activity);
1617
+ report.published += 1;
1618
+ }
1619
+ break;
1620
+ }
1621
+ case "preserve":
1622
+ case "unchanged":
1623
+ report[plan.kind === "preserve" ? "preserved" : "unchanged"] += 1;
1624
+ break;
1625
+ case "publish":
1626
+ yield* publish(dependencies, plan.current, input, activity);
1627
+ report.published += 1;
1628
+ break;
1629
+ case "update": {
1630
+ const updated = yield* dependencies.runMutation({
1631
+ action: "document.update",
1632
+ input: {
1633
+ documentId: plan.current.documentId,
1634
+ expectedVersion: plan.current.version,
1635
+ markdown: source.markdown,
1636
+ reason: input.reason
1637
+ }
1638
+ }, activity, input.reason);
1639
+ report.updated += 1;
1640
+ if (plan.publish) {
1641
+ yield* publish(dependencies, updated, input, activity);
1642
+ report.published += 1;
1643
+ }
1644
+ break;
1645
+ }
1646
+ }
1647
+ }
1648
+ return DocumentImportReport.make(report);
1649
+ });
1650
+ function planDocumentImport(current, source, mode) {
1651
+ if (!current) return {
1652
+ kind: "create",
1653
+ publish: source.published
1654
+ };
1655
+ if (current.markdown !== source.markdown) return mode === "overwrite" ? {
1656
+ current,
1657
+ kind: "update",
1658
+ publish: source.published
1659
+ } : { kind: "preserve" };
1660
+ if (source.published && current.publishedVersion !== current.version) return {
1661
+ current,
1662
+ kind: "publish"
1663
+ };
1664
+ return { kind: "unchanged" };
1665
+ }
1666
+ function publish(dependencies, source, input, activity) {
1667
+ return dependencies.runMutation({
1668
+ action: "document.publish",
1669
+ input: {
1670
+ documentId: source.documentId,
1671
+ expectedVersion: source.version,
1672
+ reason: input.reason
1673
+ }
1674
+ }, activity, input.reason);
1675
+ }
1676
+
1677
+ //#endregion
1678
+ //#region ../server/src/internal/documents/document-store.ts
1679
+ var DocumentStore = class extends Context.Service()("@internal/server/DocumentStore") {};
1680
+
1681
+ //#endregion
1682
+ //#region ../server/src/internal/documents/document-failures.ts
1683
+ var DocumentPersistenceError = class extends Schema.TaggedError()("Documents.PersistenceError", {
1684
+ operation: Schema.NonEmptyString,
1685
+ cause: Schema.Defect()
1686
+ }) {};
1687
+ var DocumentGenerationConflict = class extends Schema.TaggedError()("Documents.GenerationConflict", {
1688
+ expectedGeneration: DocumentGeneration,
1689
+ actualGeneration: DocumentGeneration
1690
+ }) {};
1691
+
1692
+ //#endregion
1693
+ //#region ../server/src/internal/cursor.ts
1694
+ function cursorCodec(schema, makeCursor, invalid) {
1695
+ const decodeJson = Schema.decodeUnknownResult(schema);
1696
+ const encodeJson = flow(Schema.encodeResult(schema), Result.getOrThrow);
1697
+ return {
1698
+ decode: (cursor) => {
1699
+ const decoded = Encoding.decodeBase64UrlString(cursor);
1700
+ if (Result.isFailure(decoded)) return Result.fail(invalid());
1701
+ return decodeJson(decoded.success).pipe(Result.mapError(invalid));
1702
+ },
1703
+ encode: (payload) => makeCursor(Encoding.encodeBase64Url(encodeJson(payload)))
1704
+ };
1705
+ }
1706
+
1707
+ //#endregion
1708
+ //#region ../server/src/internal/documents/document-history.ts
1709
+ const CursorPayload = Schema.Struct({
1710
+ createdAt: Schema.DateTimeUtcFromString,
1711
+ revisionId: RevisionId
1712
+ });
1713
+ const CursorJson = Schema.fromJsonString(CursorPayload);
1714
+ const cursors = cursorCodec(CursorJson, (value) => DocumentHistoryCursor.make(value), () => new DocumentHistoryInvalidCursor());
1715
+ function documentHistoryCapability(accessControl, repository) {
1716
+ const requireHistory = (context, documentId) => accessControl.require(context, {
1717
+ action: "document.history",
1718
+ resource: {
1719
+ documentId,
1720
+ kind: "document"
1721
+ }
1722
+ });
1723
+ const read = Effect.fn("Documents.history.read")(function* (context, documentId, revisionId) {
1724
+ yield* requireHistory(context, documentId);
1725
+ return yield* repository.revision(documentId, revisionId);
1726
+ });
1727
+ const list = Effect.fn("Documents.history.list")(function* (context, query) {
1728
+ yield* requireHistory(context, query.documentId);
1729
+ const cursor = query.cursor ? yield* Effect.fromResult(cursors.decode(query.cursor)) : void 0;
1730
+ const page = yield* repository.revisions({
1731
+ cursor,
1732
+ documentId: query.documentId,
1733
+ limit: query.limit ?? 25
1734
+ });
1735
+ const output = {
1736
+ revisions: page.revisions.map(({ markdown: _markdown, ...revision }) => revision),
1737
+ total: page.total
1738
+ };
1739
+ return page.next ? DocumentRevisionPage.make({
1740
+ ...output,
1741
+ nextCursor: cursors.encode(page.next)
1742
+ }) : DocumentRevisionPage.make(output);
1743
+ });
1744
+ return {
1745
+ compare: Effect.fn("Documents.history.compare")(function* (context, input) {
1746
+ yield* requireHistory(context, input.documentId);
1747
+ const from = yield* repository.revision(input.documentId, input.fromRevisionId);
1748
+ const to = yield* repository.revision(input.documentId, input.toRevisionId);
1749
+ const changes = yield* compareLines(from.markdown, to.markdown);
1750
+ return DocumentRevisionComparison.make({
1751
+ changes,
1752
+ from,
1753
+ to
1754
+ });
1755
+ }),
1756
+ list,
1757
+ read
1758
+ };
1759
+ }
1760
+ const MAXIMUM_COMPARISON_LINES = 1e4;
1761
+ const MAXIMUM_EDIT_LENGTH = 2e3;
1762
+ const comparisonTooLarge = () => new DocumentHistoryComparisonTooLarge({
1763
+ maximumEditLength: MAXIMUM_EDIT_LENGTH,
1764
+ maximumLines: MAXIMUM_COMPARISON_LINES
1765
+ });
1766
+ const compareLines = Effect.fn("Documents.history.compareLines")(function* (beforeMarkdown, afterMarkdown) {
1767
+ const before = beforeMarkdown.split("\n");
1768
+ const after = afterMarkdown.split("\n");
1769
+ if (before.length > MAXIMUM_COMPARISON_LINES || after.length > MAXIMUM_COMPARISON_LINES) return yield* comparisonTooLarge();
1770
+ const diff = diffArrays(before, after, {
1771
+ maxEditLength: MAXIMUM_EDIT_LENGTH,
1772
+ oneChangePerToken: true,
1773
+ timeout: 100
1774
+ });
1775
+ if (!diff) return yield* comparisonTooLarge();
1776
+ const changes = [];
1777
+ let beforeLine = 1;
1778
+ let afterLine = 1;
1779
+ for (const change of diff) for (const line of change.value) if (change.removed) {
1780
+ changes.push({
1781
+ beforeLine,
1782
+ kind: "removed",
1783
+ line
1784
+ });
1785
+ beforeLine += 1;
1786
+ } else if (change.added) {
1787
+ changes.push({
1788
+ afterLine,
1789
+ kind: "added",
1790
+ line
1791
+ });
1792
+ afterLine += 1;
1793
+ } else {
1794
+ changes.push({
1795
+ afterLine,
1796
+ beforeLine,
1797
+ kind: "equal",
1798
+ line
1799
+ });
1800
+ beforeLine += 1;
1801
+ afterLine += 1;
1802
+ }
1803
+ return changes;
1804
+ });
1805
+
1806
+ //#endregion
1807
+ //#region ../server/src/internal/documents/document-delivery.ts
1808
+ function documentDelivery(configuration, catalog) {
1809
+ const read = Effect.fn("Documents.delivery.read")(function* (lookup) {
1810
+ const document = findDocument(yield* catalog, lookup);
1811
+ return Option.isSome(document) ? document.value : yield* new PublishedDocumentNotFound({ lookup });
1812
+ });
1813
+ const query = Effect.fn("Documents.delivery.query")(function* (input) {
1814
+ return queryDocuments((yield* catalog).documents, input, {
1815
+ limit: input.limit,
1816
+ offset: input.offset ?? 0
1817
+ });
1818
+ });
1819
+ const navigation = Effect.fn("Documents.delivery.navigation")(function* (input) {
1820
+ return createNavigation((yield* catalog).documents, input, configuration);
1821
+ });
1822
+ const surround = Effect.fn("Documents.delivery.surround")(function* (lookup, input) {
1823
+ const current = yield* catalog;
1824
+ const document = findDocument(current, lookup);
1825
+ if (Option.isNone(document)) return yield* new PublishedDocumentNotFound({ lookup });
1826
+ 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 });
1827
+ const page = queryDocuments(current.documents, surroundQuery);
1828
+ const index = page.items.findIndex((candidate) => candidate.id === document.value.id);
1829
+ return DocumentSurround.make({
1830
+ previous: index > 0 ? page.items[index - 1] ?? null : null,
1831
+ next: index >= 0 ? page.items[index + 1] ?? null : null
1832
+ });
1833
+ });
1834
+ return {
1835
+ diagnostics: catalog.pipe(Effect.map((current) => current.issues)),
1836
+ navigation,
1837
+ query,
1838
+ read,
1839
+ surround
1840
+ };
1841
+ }
1842
+ function findDocument(catalog, lookup) {
1843
+ switch (lookup.kind) {
1844
+ case "id": return Option.fromNullishOr(catalog.documents.find((document) => document.id === lookup.documentId));
1845
+ case "path": return catalog.findByPath(lookup.path);
1846
+ case "key": return catalog.findByKey(lookup.key, lookup.locale).pipe(Option.filter((document) => lookup.collection === void 0 || document.collection === lookup.collection));
1847
+ }
1848
+ }
1849
+ function queryDocuments(documents, query, page) {
1850
+ const filtered = documents.filter((document) => {
1851
+ if (query.collection !== void 0 && document.collection !== query.collection) return false;
1852
+ if (query.locale !== void 0 && document.locale !== query.locale) return false;
1853
+ return Object.entries(query.where ?? {}).every(([field, expected]) => matchesValue(documentField(document, field), expected));
1854
+ });
1855
+ const sorted = query.sort && query.sort.length > 0 ? sortDocuments(filtered, query.sort) : filtered;
1856
+ const items = page ? sorted.slice(page.offset, page.offset + page.limit) : sorted;
1857
+ return PublishedDocumentPage.make({
1858
+ items,
1859
+ total: filtered.length
1860
+ });
1861
+ }
1862
+ function createNavigation(documents, query, configuration) {
1863
+ const locale = query.locale ?? configuration.locales?.defaultLocale;
1864
+ const candidates = documents.filter((document) => (query.collection === void 0 || document.collection === query.collection) && (locale === void 0 || document.locale === locale) && document.data.navigation !== false);
1865
+ const roots = [];
1866
+ const itemsByKey = /* @__PURE__ */ new Map();
1867
+ const orderByItem = /* @__PURE__ */ new Map();
1868
+ for (const document of candidates) {
1869
+ if (document.key === "/") {
1870
+ const item = navigationItem(document);
1871
+ roots.push(item);
1872
+ orderByItem.set(item, Number.NEGATIVE_INFINITY);
1873
+ continue;
1874
+ }
1875
+ const segments = document.key.slice(1).split("/");
1876
+ let parent;
1877
+ for (let index = 0; index < segments.length; index += 1) {
1878
+ const key = PublicPath.make(`/${segments.slice(0, index + 1).join("/")}`);
1879
+ let item = itemsByKey.get(key);
1880
+ if (!item) {
1881
+ item = {
1882
+ children: [],
1883
+ key,
1884
+ title: titleFromSegment(segments[index] ?? "")
1885
+ };
1886
+ itemsByKey.set(key, item);
1887
+ if (parent) parent.children.push(item);
1888
+ else roots.push(item);
1889
+ }
1890
+ if (index === segments.length - 1) {
1891
+ const resolved = navigationItem(document);
1892
+ item.title = resolved.title;
1893
+ item.path = resolved.path;
1894
+ if (resolved.locale !== void 0) item.locale = resolved.locale;
1895
+ else delete item.locale;
1896
+ item.documentId = resolved.documentId;
1897
+ orderByItem.set(item, navigationOrder(document));
1898
+ }
1899
+ parent = item;
1900
+ }
1901
+ }
1902
+ sortNavigation(roots, orderByItem);
1903
+ return roots.map((item) => DocumentNavigationItem.make(item));
1904
+ }
1905
+ function navigationItem(document) {
1906
+ const navigation = document.data.navigation;
1907
+ const configuredTitle = Schema.is(Schema.JsonObject)(navigation) ? navigation.title : void 0;
1908
+ return Object.assign({
1909
+ children: [],
1910
+ documentId: document.id,
1911
+ key: document.key,
1912
+ path: document.path,
1913
+ title: Schema.is(Schema.String)(configuredTitle) && configuredTitle.trim() ? configuredTitle.trim() : document.title
1914
+ }, document.locale === void 0 ? void 0 : { locale: document.locale });
1915
+ }
1916
+ function navigationOrder(document) {
1917
+ const navigation = document.data.navigation;
1918
+ const order = (Schema.is(Schema.JsonObject)(navigation) ? navigation.order : void 0) ?? document.data.order;
1919
+ return Schema.is(Schema.Finite)(order) ? order : Number.POSITIVE_INFINITY;
1920
+ }
1921
+ function sortNavigation(items, orderByItem) {
1922
+ items.sort((left, right) => {
1923
+ return (orderByItem.get(left) ?? Number.POSITIVE_INFINITY) - (orderByItem.get(right) ?? Number.POSITIVE_INFINITY) || left.title.localeCompare(right.title);
1924
+ });
1925
+ items.forEach((item) => sortNavigation(item.children, orderByItem));
1926
+ }
1927
+ function documentField(document, field) {
1928
+ switch (field) {
1929
+ case "collection": return document.collection;
1930
+ case "description": return document.description;
1931
+ case "id": return document.id;
1932
+ case "key": return document.key;
1933
+ case "locale": return document.locale;
1934
+ case "path": return document.path;
1935
+ case "source.path": return document.source.path;
1936
+ case "title": return document.title;
1937
+ default: return nestedValue(document.data, field.startsWith("data.") ? field.slice(5) : field);
1938
+ }
1939
+ }
1940
+ function nestedValue(value, path) {
1941
+ return path.split(".").reduce((current, segment) => {
1942
+ if (!Schema.is(Schema.JsonObject)(current)) return void 0;
1943
+ return current[segment];
1944
+ }, value);
1945
+ }
1946
+ function matchesValue(actual, expected) {
1947
+ const expectedValues = Array.isArray(expected) ? expected : [expected];
1948
+ return (Array.isArray(actual) ? actual : [actual]).some((value) => Schema.is(DocumentQueryScalar)(value) && expectedValues.includes(value));
1949
+ }
1950
+ function sortDocuments(documents, sorts) {
1951
+ return documents.toSorted((left, right) => {
1952
+ for (const sort of sorts) {
1953
+ const comparison = compareValues(documentField(left, sort.field), documentField(right, sort.field));
1954
+ if (comparison !== 0) return sort.direction === "desc" ? -comparison : comparison;
1955
+ }
1956
+ return 0;
1957
+ });
1958
+ }
1959
+ function compareValues(left, right) {
1960
+ if (left === right) return 0;
1961
+ if (left === void 0 || left === null) return 1;
1962
+ if (right === void 0 || right === null) return -1;
1963
+ if (Schema.is(Schema.Finite)(left) && Schema.is(Schema.Finite)(right)) return left - right;
1964
+ const leftText = Schema.is(Schema.String)(left) ? left : JSON.stringify(left) ?? "";
1965
+ const rightText = Schema.is(Schema.String)(right) ? right : JSON.stringify(right) ?? "";
1966
+ return leftText.localeCompare(rightText);
1967
+ }
1968
+ function titleFromSegment(segment) {
1969
+ return segment.replace(/[-_]+/gu, " ").replace(/^./u, (character) => character.toUpperCase());
1970
+ }
1971
+
1972
+ //#endregion
1973
+ //#region ../server/src/internal/documents/documents.ts
1974
+ const isDocumentGenerationConflict = Schema.is(DocumentGenerationConflict);
1975
+ const isDocumentMediaUnavailable = Schema.is(DocumentMediaUnavailable);
1976
+ const CONTENT_MUTATION_BUFFER_CAPACITY = 16;
1977
+ var Documents = class extends Context.Service()("@internal/server/Documents") {};
1978
+ var DocumentPreviews = class extends Context.Service()("@internal/server/DocumentPreviews") {};
1979
+ var PublishedDocuments = class extends Context.Service()("@internal/server/PublishedDocuments") {};
1980
+ var DocumentMaintenance = class extends Context.Service()("@internal/server/internal/DocumentMaintenance") {};
1981
+ const makeDocuments = Effect.fn("Documents.make")(function* (configuration, options) {
1982
+ const repository = yield* DocumentStore;
1983
+ const accessControl = yield* AccessControl;
1984
+ const crypto = yield* Crypto.Crypto;
1985
+ const engine = yield* createDocumentEngine(configuration, options);
1986
+ const state = yield* Ref.make(yield* loadSynchronizedState(repository, engine));
1987
+ const semaphore = yield* Semaphore.make(1);
1988
+ const mutationEvents = yield* Effect.acquireRelease(PubSub.sliding(CONTENT_MUTATION_BUFFER_CAPACITY), PubSub.shutdown);
1989
+ const dependencies = {
1990
+ engine,
1991
+ configuration,
1992
+ repository,
1993
+ components: options.components
1994
+ };
1995
+ const refreshUnlocked = Effect.fn("Documents.refreshUnlocked")(function* (force = false) {
1996
+ const current = yield* Ref.get(state);
1997
+ const snapshot = force ? yield* loadRequired(repository) : yield* repository.load(current.snapshot.generation);
1998
+ if (!snapshot || !generationIsAfter(snapshot.generation, current.snapshot.generation)) return current.snapshot.generation;
1999
+ const refreshed = yield* compileDocumentState(engine, snapshot, current);
2000
+ yield* Ref.set(state, refreshed);
2001
+ return refreshed.snapshot.generation;
2002
+ });
2003
+ const executeUnlocked = Effect.fn("Documents.execute")(function* (command, activity) {
2004
+ return yield* Effect.gen(function* () {
2005
+ yield* refreshUnlocked();
2006
+ const current = yield* Ref.get(state);
2007
+ const outcome = yield* commitDocumentMutation(accessControl, dependencies, current, command, activity);
2008
+ if (outcome.kind === "unchanged") return outcome.result;
2009
+ yield* Ref.set(state, outcome.next);
2010
+ yield* PubSub.publish(mutationEvents, outcome.result);
2011
+ return outcome.result;
2012
+ }).pipe(Effect.tapError((error) => isDocumentGenerationConflict(error) ? refreshUnlocked(true) : Effect.void), Effect.retry({
2013
+ schedule: Schedule.recurs(3),
2014
+ while: isDocumentGenerationConflict
2015
+ }));
2016
+ });
2017
+ const runMutation = Effect.fn("Documents.runMutation")(function* (command, activity, reason) {
2018
+ const resolvedActivity = enrichActivity(activity, reason);
2019
+ return yield* semaphore.withPermits(1)(executeUnlocked(command, resolvedActivity));
2020
+ });
2021
+ const authored = semaphore.withPermits(1)(Effect.gen(function* () {
2022
+ yield* refreshUnlocked();
2023
+ return (yield* Ref.get(state)).authored;
2024
+ })).pipe(Effect.withSpan("Documents.authored"));
2025
+ const browse = Effect.fn("Documents.browse")((query) => semaphore.withPermits(1)(Effect.gen(function* () {
2026
+ yield* refreshUnlocked();
2027
+ return authoringEntries(yield* Ref.get(state), query);
2028
+ })));
2029
+ const create = Effect.fn("Documents.create")(function* (input, activity) {
2030
+ const collection = configuration.types.find((candidate) => candidate.collection === input.collection);
2031
+ if (collection && !collection.canCreate) return yield* new DocumentValidationFailed({ issues: [{
2032
+ code: "document.invalid",
2033
+ field: "collection",
2034
+ message: `Document collection "${input.collection}" does not allow creation.`,
2035
+ severity: "error"
2036
+ }] });
2037
+ const plan = yield* Effect.fromResult(planDocumentCreation(configuration, input));
2038
+ const source = yield* createInitialDocumentSource(plan);
2039
+ const documentId = DocumentId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie));
2040
+ return yield* runMutation({
2041
+ action: "document.create",
2042
+ documentId,
2043
+ markdown: source.markdown,
2044
+ sourcePath: plan.sourcePath
2045
+ }, activity, input.reason);
2046
+ });
2047
+ const importSources = (input, activity) => importDocumentSources({
2048
+ current: (sourcePath) => semaphore.withPermits(1)(Effect.gen(function* () {
2049
+ yield* refreshUnlocked();
2050
+ return (yield* Ref.get(state)).snapshot.active.find((candidate) => candidate.sourcePath === sourcePath);
2051
+ })),
2052
+ nextDocumentId: crypto.randomUUIDv4.pipe(Effect.orDie, Effect.map((value) => DocumentId.make(value))),
2053
+ runMutation
2054
+ }, input, activity);
2055
+ const duplicate = Effect.fn("Documents.duplicate")((input, activity) => runMutation({
2056
+ action: "document.duplicate",
2057
+ input,
2058
+ nextDocumentId: crypto.randomUUIDv4.pipe(Effect.orDie, Effect.map((value) => DocumentId.make(value)))
2059
+ }, activity, input.reason));
2060
+ const unpublish = Effect.fn("Documents.unpublish")((input, activity) => runMutation({
2061
+ action: "document.unpublish",
2062
+ input
2063
+ }, activity, input.reason));
2064
+ const move = Effect.fn("Documents.move")((input, activity) => runMutation({
2065
+ action: "document.move",
2066
+ input
2067
+ }, activity, input.reason));
2068
+ const subscribeMutations = Effect.gen(function* () {
2069
+ const subscription = yield* PubSub.subscribe(mutationEvents);
2070
+ const baseline = yield* semaphore.withPermits(1)(Ref.get(state).pipe(Effect.map((current) => current.snapshot.generation)));
2071
+ return {
2072
+ baseline,
2073
+ mutations: Stream.fromSubscription(subscription).pipe(Stream.filter((mutation) => generationIsAfter(mutation.generation, baseline)))
2074
+ };
2075
+ }).pipe(Effect.withSpan("Documents.subscribeMutations"));
2076
+ const publish = Effect.fn("Documents.publish")((input, activity) => runMutation({
2077
+ action: "document.publish",
2078
+ input
2079
+ }, activity, input.reason));
2080
+ const published = semaphore.withPermits(1)(Effect.gen(function* () {
2081
+ yield* refreshUnlocked();
2082
+ return (yield* Ref.get(state)).published;
2083
+ })).pipe(Effect.withSpan("Documents.published"));
2084
+ const read = Effect.fn("Documents.read")((documentId) => semaphore.withPermits(1)(Effect.gen(function* () {
2085
+ yield* refreshUnlocked();
2086
+ return (yield* Ref.get(state)).activeById.get(documentId) || (yield* new DocumentNotFound({ documentId }));
2087
+ })));
2088
+ const refresh = semaphore.withPermits(1)(refreshUnlocked()).pipe(Effect.withSpan("Documents.refresh"));
2089
+ const remove = Effect.fn("Documents.remove")((input, activity) => runMutation({
2090
+ action: "document.remove",
2091
+ input
2092
+ }, activity, input.reason));
2093
+ const restore = Effect.fn("Documents.restore")((input, activity) => runMutation({
2094
+ action: "document.restore",
2095
+ input
2096
+ }, activity, input.reason));
2097
+ const trash = semaphore.withPermits(1)(Effect.gen(function* () {
2098
+ yield* refreshUnlocked();
2099
+ return deletedEntries(yield* Ref.get(state));
2100
+ })).pipe(Effect.withSpan("Documents.trash"));
2101
+ const undelete = Effect.fn("Documents.undelete")((input, activity) => runMutation({
2102
+ action: "document.undelete",
2103
+ input
2104
+ }, activity, input.reason));
2105
+ const update = Effect.fn("Documents.update")((input, activity) => runMutation({
2106
+ action: "document.update",
2107
+ input
2108
+ }, activity, input.reason));
2109
+ const validate = Effect.fn("Documents.validate")((input) => semaphore.withPermits(1)(Effect.gen(function* () {
2110
+ yield* refreshUnlocked();
2111
+ return yield* validateDocumentUpdate(engine, yield* Ref.get(state), input);
2112
+ })));
2113
+ const history = documentHistoryCapability(accessControl, repository);
2114
+ const delivery = documentDelivery(configuration, published);
2115
+ const document = (documentId) => ({
2116
+ documentId,
2117
+ kind: "document"
2118
+ });
2119
+ const requireThen = (context, action, resource, effect) => accessControl.require(context, {
2120
+ action,
2121
+ resource
2122
+ }).pipe(Effect.andThen(effect));
2123
+ const documents = Documents.of({
2124
+ browse: (context, query) => requireThen(context, "document.browse", query.collection ? {
2125
+ collection: query.collection,
2126
+ kind: "collection",
2127
+ locale: query.locale
2128
+ } : { kind: "catalog" }, () => browse(query)),
2129
+ create: (context, input) => requireThen(context, "document.create", {
2130
+ collection: input.collection,
2131
+ kind: "collection",
2132
+ locale: input.locale
2133
+ }, () => create(input, requestActivity$1(context, input.reason))),
2134
+ historyCompare: history.compare,
2135
+ historyList: history.list,
2136
+ historyRead: history.read,
2137
+ duplicate: (context, input) => requireThen(context, "document.duplicate", document(input.documentId), () => duplicate(input, requestActivity$1(context, input.reason))),
2138
+ unpublish: (context, input) => requireThen(context, "document.unpublish", document(input.documentId), () => unpublish(input, requestActivity$1(context, input.reason))),
2139
+ move: (context, input) => requireThen(context, "document.move", document(input.documentId), () => move(input, requestActivity$1(context, input.reason))),
2140
+ publish: (context, input) => requireThen(context, "document.publish", document(input.documentId), () => publish(input, requestActivity$1(context, input.reason))),
2141
+ read: (context, documentId) => requireThen(context, "document.read", document(documentId), () => read(documentId)),
2142
+ remove: (context, input) => requireThen(context, "document.remove", document(input.documentId), () => remove(input, requestActivity$1(context, input.reason))),
2143
+ restore: (context, input) => requireThen(context, "document.restore", document(input.documentId), () => restore(input, requestActivity$1(context, input.reason))),
2144
+ trash: (context) => requireThen(context, "document.trash", { kind: "catalog" }, () => trash),
2145
+ undelete: (context, input) => requireThen(context, "document.undelete", document(input.documentId), () => undelete(input, requestActivity$1(context, input.reason))),
2146
+ update: (context, input) => requireThen(context, "document.update", document(input.documentId), () => update(input, requestActivity$1(context, input.reason))),
2147
+ validate: (context, input) => requireThen(context, "document.validate", document(input.documentId), () => validate(input))
2148
+ });
2149
+ const draftDelivery = documentDelivery(configuration, authored);
2150
+ const previews = DocumentPreviews.of({
2151
+ read: (context, lookup) => requireThen(context, "document.browse", { kind: "catalog" }, () => draftDelivery.read(lookup)),
2152
+ query: (context, query) => requireThen(context, "document.browse", { kind: "catalog" }, () => draftDelivery.query(query)),
2153
+ navigation: (context, query) => requireThen(context, "document.browse", { kind: "catalog" }, () => draftDelivery.navigation(query)),
2154
+ surround: (context, lookup, query) => requireThen(context, "document.browse", { kind: "catalog" }, () => draftDelivery.surround(lookup, query))
2155
+ });
2156
+ const publishedService = PublishedDocuments.of({
2157
+ navigation: delivery.navigation,
2158
+ query: delivery.query,
2159
+ read: delivery.read,
2160
+ snapshot: published.pipe(Effect.map((catalog) => catalog.documents)),
2161
+ surround: delivery.surround,
2162
+ diagnostics: (context) => accessControl.require(context, {
2163
+ action: "document.browse",
2164
+ resource: { kind: "catalog" }
2165
+ }).pipe(Effect.andThen(delivery.diagnostics))
2166
+ });
2167
+ return {
2168
+ documents,
2169
+ maintenance: DocumentMaintenance.of({
2170
+ importSources,
2171
+ mediaReferences: (documentId) => semaphore.withPermits(1)(Effect.gen(function* () {
2172
+ yield* refreshUnlocked();
2173
+ const current = yield* Ref.get(state);
2174
+ return projectMediaReferences(current.authored, current.published, documentId);
2175
+ })),
2176
+ publish,
2177
+ read,
2178
+ refresh,
2179
+ subscribeMutations,
2180
+ validate
2181
+ }),
2182
+ previews,
2183
+ publishedDocuments: publishedService
2184
+ };
2185
+ });
2186
+ function documentsLayer(configuration, options) {
2187
+ return Layer.effectContext(makeDocuments(configuration, options).pipe(Effect.map(({ documents, maintenance, previews, publishedDocuments }) => Context.empty().pipe(Context.add(Documents, documents), Context.add(DocumentMaintenance, maintenance), Context.add(DocumentPreviews, previews), Context.add(PublishedDocuments, publishedDocuments)))));
2188
+ }
2189
+ function loadRequired(repository) {
2190
+ return repository.load().pipe(Effect.filterOrFail((snapshot) => snapshot !== void 0, () => new DocumentPersistenceError({
2191
+ cause: /* @__PURE__ */ new Error("DocumentStore returned no initial snapshot."),
2192
+ operation: "DocumentStore.load"
2193
+ })));
2194
+ }
2195
+ function loadSynchronizedState(repository, engine) {
2196
+ return Effect.gen(function* () {
2197
+ const snapshot = yield* loadRequired(repository);
2198
+ const state = yield* compileDocumentState(engine, snapshot);
2199
+ yield* repository.synchronizeMediaReferences({
2200
+ expectedGeneration: snapshot.generation,
2201
+ references: projectMediaReferences(state.authored, state.published)
2202
+ });
2203
+ return state;
2204
+ }).pipe(Effect.retry({
2205
+ schedule: Schedule.recurs(3),
2206
+ while: isDocumentGenerationConflict
2207
+ }), Effect.mapError((error) => isDocumentGenerationConflict(error) || isDocumentMediaUnavailable(error) ? new DocumentPersistenceError({
2208
+ cause: error,
2209
+ operation: "Documents.synchronizeMediaReferences"
2210
+ }) : error));
2211
+ }
2212
+ function generationIsAfter(candidate, current) {
2213
+ return BigInt(candidate) > BigInt(current);
2214
+ }
2215
+ function enrichActivity(activity, reason) {
2216
+ return ActivityContext.make(Object.assign({}, activity, reason === void 0 ? void 0 : { reason }));
2217
+ }
2218
+ function requestActivity$1(context, reason) {
2219
+ return ActivityContext.make(Object.assign({
2220
+ actor: context.actor,
2221
+ requestId: context.requestId
2222
+ }, reason === void 0 ? void 0 : { reason }));
2223
+ }
2224
+
2225
+ //#endregion
2226
+ //#region ../server/src/internal/seo/seo-store.ts
2227
+ var SeoPersistenceError = class extends Schema.TaggedError()("Seo.PersistenceError", {
2228
+ operation: Schema.NonEmptyString,
2229
+ cause: Schema.Defect()
2230
+ }) {};
2231
+ var SeoStore = class extends Context.Service()("@internal/server/SeoStore") {};
2232
+
2233
+ //#endregion
2234
+ //#region ../server/src/internal/seo/seo.ts
2235
+ var Seo = class extends Context.Service()("@internal/server/Seo") {};
2236
+ var PublishedSeo = class extends Context.Service()("@internal/server/PublishedSeo") {};
2237
+ var PreviewSeo = class extends Context.Service()("@internal/server/PreviewSeo") {};
2238
+ var SeoDelivery = class extends Context.Service()("@internal/server/SeoDelivery") {};
2239
+ function makeDefaultSeoOptions(baseUrl) {
2240
+ const url = new URL(baseUrl);
2241
+ return {
2242
+ capabilities: SeoCapabilities.make({
2243
+ generatedSocialImageTemplateIds: [],
2244
+ identityTypes: [
2245
+ "Person",
2246
+ "Organization",
2247
+ "LocalBusiness"
2248
+ ],
2249
+ structuredDataTypes: ["WebPage", "Article"]
2250
+ }),
2251
+ defaults: GlobalSeoSettings.make({
2252
+ canonicalOrigin: SeoCanonicalOrigin.make(url.origin),
2253
+ follow: true,
2254
+ index: true,
2255
+ siteName: url.hostname
2256
+ })
2257
+ };
2258
+ }
2259
+ const seoLayer = (options) => Layer.effectContext(Effect.gen(function* () {
2260
+ const accessControl = yield* AccessControl;
2261
+ const store = yield* SeoStore;
2262
+ const load = Effect.fn("Seo.load")(function* () {
2263
+ return (yield* store.load) ?? (yield* store.initialize(options.defaults));
2264
+ });
2265
+ const state = Effect.fn("Seo.state")(function* () {
2266
+ return makeState(yield* load(), options.capabilities);
2267
+ });
2268
+ const requireAdministrator = (context, action) => accessControl.require(context, {
2269
+ action,
2270
+ resource: { kind: "seo" }
2271
+ });
2272
+ const read = Effect.fn("Seo.read")(function* (context) {
2273
+ yield* requireAdministrator(context, "seo.read");
2274
+ return yield* state();
2275
+ });
2276
+ const save = Effect.fn("Seo.save")(function* (context, input) {
2277
+ yield* requireAdministrator(context, "seo.save");
2278
+ const snapshot = yield* store.save({
2279
+ activity: requestActivity(context, input.reason),
2280
+ expectedVersion: input.expectedVersion,
2281
+ settings: input.settings
2282
+ });
2283
+ return SeoMutationResult.make({
2284
+ action: "seo.save",
2285
+ state: makeState(snapshot, options.capabilities)
2286
+ });
2287
+ });
2288
+ const publish = Effect.fn("Seo.publish")(function* (context, input) {
2289
+ yield* requireAdministrator(context, "seo.publish");
2290
+ const snapshot = yield* load();
2291
+ const confirmationChanges = productionChanges(snapshot.published?.settings ?? options.defaults, snapshot.working.settings);
2292
+ if (confirmationChanges.length > 0 && input.confirmedSiteWideChange !== true) return yield* new SeoConfirmationRequired({ changes: confirmationChanges });
2293
+ const published = yield* store.publish({
2294
+ activity: requestActivity(context, input.reason),
2295
+ expectedVersion: input.expectedVersion
2296
+ });
2297
+ return SeoMutationResult.make({
2298
+ action: "seo.publish",
2299
+ state: makeState(published, options.capabilities)
2300
+ });
2301
+ });
2302
+ const seo = Seo.of({
2303
+ publish,
2304
+ read,
2305
+ save
2306
+ });
2307
+ const publishedSeo = PublishedSeo.of({ read: load().pipe(Effect.map((snapshot) => SeoSnapshot.make({
2308
+ capabilities: options.capabilities,
2309
+ revision: snapshot.published ?? snapshot.working
2310
+ }))) });
2311
+ const previewSeo = PreviewSeo.of({ read: load().pipe(Effect.map((snapshot) => SeoSnapshot.make({
2312
+ capabilities: options.capabilities,
2313
+ revision: snapshot.working
2314
+ }))) });
2315
+ return Context.empty().pipe(Context.add(Seo, seo), Context.add(PublishedSeo, publishedSeo), Context.add(PreviewSeo, previewSeo));
2316
+ }));
2317
+ const seoDeliveryLayer = Layer.effect(SeoDelivery, Effect.gen(function* () {
2318
+ const previews = yield* PreviewSeo;
2319
+ const published = yield* PublishedSeo;
2320
+ const documents = yield* PublishedDocuments;
2321
+ const resolveWith = (document, snapshot) => CompiledDocument.make(Object.assign({}, document, { resolvedSeo: resolveDocumentSeo({
2322
+ capabilities: snapshot.capabilities,
2323
+ document,
2324
+ global: snapshot.revision.settings
2325
+ }) }));
2326
+ const publishedDocument = Effect.fn("SeoDelivery.publishedDocument")((document) => published.read.pipe(Effect.map((snapshot) => resolveWith(document, snapshot))));
2327
+ const previewDocument = Effect.fn("SeoDelivery.previewDocument")((document) => previews.read.pipe(Effect.map((snapshot) => resolveWith(document, snapshot))));
2328
+ const sitemap = Effect.gen(function* () {
2329
+ const snapshot = yield* published.read;
2330
+ const resolved = (yield* documents.snapshot).flatMap((document) => {
2331
+ const resolvedDocument = resolveWith(document, snapshot);
2332
+ return resolvedDocument.resolvedSeo?.index === true && resolvedDocument.source.publishedAt !== void 0 ? [resolvedDocument] : [];
2333
+ });
2334
+ const localesByKey = /* @__PURE__ */ new Map();
2335
+ for (const document of resolved) {
2336
+ if (document.locale === void 0) continue;
2337
+ const alternatives = localesByKey.get(document.key) ?? [];
2338
+ alternatives.push({
2339
+ locale: document.locale,
2340
+ loc: document.path
2341
+ });
2342
+ localesByKey.set(document.key, alternatives);
2343
+ }
2344
+ return resolved.flatMap((document) => {
2345
+ const publishedAt = document.source.publishedAt;
2346
+ if (publishedAt === void 0) return [];
2347
+ const alternatives = localesByKey.get(document.key);
2348
+ const entry = {
2349
+ loc: document.path,
2350
+ lastmod: publishedAt
2351
+ };
2352
+ return [SitemapEntry.make(alternatives && alternatives.length > 1 ? {
2353
+ ...entry,
2354
+ alternatives
2355
+ } : entry)];
2356
+ }).sort((left, right) => left.loc.localeCompare(right.loc));
2357
+ }).pipe(Effect.withSpan("SeoDelivery.sitemap"));
2358
+ return SeoDelivery.of({
2359
+ previewDocument,
2360
+ publishedDocument,
2361
+ sitemap
2362
+ });
2363
+ }));
2364
+ function makeState(snapshot, capabilities) {
2365
+ return SeoState.make(snapshot.published === void 0 ? {
2366
+ capabilities,
2367
+ working: snapshot.working
2368
+ } : {
2369
+ capabilities,
2370
+ published: snapshot.published,
2371
+ working: snapshot.working
2372
+ });
2373
+ }
2374
+ function requestActivity(context, reason) {
2375
+ return {
2376
+ actor: context.actor,
2377
+ requestId: context.requestId,
2378
+ ...reason === void 0 ? void 0 : { reason }
2379
+ };
2380
+ }
2381
+ function productionChanges(previous, next) {
2382
+ const changes = [];
2383
+ if (previous.canonicalOrigin !== next.canonicalOrigin) changes.push("canonicalOrigin");
2384
+ if (previous.index !== next.index || previous.follow !== next.follow) changes.push("indexability");
2385
+ return changes;
2386
+ }
2387
+
2388
+ //#endregion
2389
+ //#region ../server/src/internal/media/media-object-store.ts
2390
+ const mediaObjectPrefix = "media/";
2391
+ var MediaObjectStore = class extends Context.Service()("@internal/server/MediaObjectStore") {};
2392
+
2393
+ //#endregion
2394
+ //#region ../server/src/internal/media/media-failures.ts
2395
+ var MediaPersistenceError = class extends Schema.TaggedError()("Media.PersistenceError", {
2396
+ operation: Schema.NonEmptyString,
2397
+ cause: Schema.Defect()
2398
+ }) {};
2399
+ var MediaStorageError = class extends Schema.TaggedError()("Media.StorageError", {
2400
+ operation: Schema.NonEmptyString,
2401
+ cause: Schema.Defect()
2402
+ }) {};
2403
+
2404
+ //#endregion
2405
+ //#region ../s3/src/internal/client.ts
2406
+ var S3RequestError = class extends Schema.TaggedError()("S3.RequestError", {
2407
+ cause: Schema.Defect(),
2408
+ operation: Schema.NonEmptyString
2409
+ }) {};
2410
+ function openAwsS3MediaObjectClient(options) {
2411
+ const credentials = options.credentials ? {
2412
+ accessKeyId: options.credentials.accessKeyId,
2413
+ secretAccessKey: Redacted.value(options.credentials.secretAccessKey),
2414
+ sessionToken: options.credentials.sessionToken ? Redacted.value(options.credentials.sessionToken) : void 0
2415
+ } : void 0;
2416
+ return makeAwsS3MediaObjectClient(new S3Client({
2417
+ credentials,
2418
+ endpoint: options.endpoint,
2419
+ forcePathStyle: options.forcePathStyle,
2420
+ region: options.region,
2421
+ requestChecksumCalculation: "WHEN_REQUIRED"
2422
+ }));
2423
+ }
2424
+ function makeAwsS3MediaObjectClient(client) {
2425
+ const request = (operation, send) => Effect.tryPromise({
2426
+ catch: (cause) => new S3RequestError({
2427
+ cause,
2428
+ operation
2429
+ }),
2430
+ try: send
2431
+ });
2432
+ return {
2433
+ close: Effect.sync(() => client.destroy()),
2434
+ copyObject: (input) => request("MediaObjectStore.S3.copyObject", (signal) => client.send(new CopyObjectCommand({
2435
+ Bucket: input.bucket,
2436
+ CacheControl: input.cacheControl,
2437
+ ContentType: input.contentType,
2438
+ CopySource: input.copySource,
2439
+ Key: input.key,
2440
+ MetadataDirective: "REPLACE"
2441
+ }), { abortSignal: signal })).pipe(Effect.asVoid),
2442
+ createBucket: (input) => request("MediaObjectStore.S3.createBucket", (signal) => client.send(new CreateBucketCommand({
2443
+ Bucket: input.bucket,
2444
+ CreateBucketConfiguration: input.locationConstraint ? { LocationConstraint: input.locationConstraint } : void 0
2445
+ }), { abortSignal: signal })).pipe(Effect.asVoid),
2446
+ deleteObject: (input) => request("MediaObjectStore.S3.deleteObject", (signal) => client.send(new DeleteObjectCommand({
2447
+ Bucket: input.bucket,
2448
+ Key: input.key
2449
+ }), { abortSignal: signal })).pipe(Effect.asVoid),
2450
+ headBucket: (input) => request("MediaObjectStore.S3.headBucket", (signal) => client.send(new HeadBucketCommand({ Bucket: input.bucket }), { abortSignal: signal })).pipe(Effect.asVoid),
2451
+ headObject: (input) => request("MediaObjectStore.S3.headObject", (signal) => client.send(new HeadObjectCommand({
2452
+ Bucket: input.bucket,
2453
+ Key: input.key
2454
+ }), { abortSignal: signal })).pipe(Effect.map((output) => ({
2455
+ contentLength: output.ContentLength,
2456
+ contentType: output.ContentType
2457
+ }))),
2458
+ listObjects: (input) => request("MediaObjectStore.S3.listObjects", (signal) => client.send(new ListObjectsV2Command({
2459
+ Bucket: input.bucket,
2460
+ ContinuationToken: input.continuationToken,
2461
+ Prefix: input.prefix
2462
+ }), { abortSignal: signal })).pipe(Effect.map((output) => ({
2463
+ isTruncated: output.IsTruncated === true,
2464
+ nextContinuationToken: output.NextContinuationToken,
2465
+ objects: (output.Contents ?? []).map((object) => ({
2466
+ key: object.Key,
2467
+ size: object.Size
2468
+ }))
2469
+ }))),
2470
+ putObject: (input) => request("MediaObjectStore.S3.putObject", (signal) => client.send(new PutObjectCommand({
2471
+ Body: NodeStream.toReadableNever(input.body),
2472
+ Bucket: input.bucket,
2473
+ CacheControl: input.cacheControl,
2474
+ ContentLength: input.contentLength,
2475
+ ContentType: input.contentType,
2476
+ Key: input.key
2477
+ }), { abortSignal: signal })).pipe(Effect.asVoid)
2478
+ };
2479
+ }
2480
+
2481
+ //#endregion
2482
+ //#region ../s3/src/internal/media-object-store.ts
2483
+ const replaceableMediaCacheControl = "public, max-age=0, must-revalidate";
2484
+ const isWriteBodyError = Schema.is(Schema.Union([MediaInvalid, MediaStorageError]));
2485
+ function s3MediaObjectStoreLayer(options) {
2486
+ return Layer.effect(MediaObjectStore, resolveOptions(options).pipe(Effect.flatMap((resolved) => Effect.acquireRelease(Effect.sync(() => openAwsS3MediaObjectClient(resolved)), (client) => client.close).pipe(Effect.tap((client) => provisionBucket(resolved, client)), Effect.map((client) => makeStore(resolved, client))))));
2487
+ }
2488
+ function makeStore(options, client) {
2489
+ return {
2490
+ list: listObjects(options.bucket, client),
2491
+ promote: (input) => promoteObject(options.bucket, input, client),
2492
+ put: (input) => putObject(options.bucket, input, client),
2493
+ remove: (objectKey) => providerRequest("MediaObjectStore.S3.remove", client.deleteObject({
2494
+ bucket: options.bucket,
2495
+ key: objectKey
2496
+ })),
2497
+ stat: (objectKey) => statObject(options.bucket, objectKey, client)
2498
+ };
2499
+ }
2500
+ function promoteObject(bucket, input, client) {
2501
+ if (input.byteSize > 5e9) return Effect.fail(storageError("MediaObjectStore.S3.promote", "The object exceeds the S3 single-copy size limit."));
2502
+ return statObject(bucket, input.sourceObjectKey, client).pipe(Effect.flatMap((source) => Option.isSome(source) && source.value.byteSize === input.byteSize && source.value.mimeType === input.mimeType ? Effect.void : Effect.fail(storageError("MediaObjectStore.S3.promote.source", "The staged object metadata does not match its intent."))), Effect.flatMap(() => providerRequest("MediaObjectStore.S3.promote.copy", client.copyObject({
2503
+ bucket,
2504
+ cacheControl: replaceableMediaCacheControl,
2505
+ contentType: input.mimeType,
2506
+ copySource: copySource(bucket, input.sourceObjectKey),
2507
+ key: input.destinationObjectKey
2508
+ }))), Effect.flatMap(() => statObject(bucket, input.destinationObjectKey, client)), Effect.flatMap((destination) => Option.isSome(destination) && destination.value.byteSize === input.byteSize && destination.value.mimeType === input.mimeType ? Effect.succeed(destination.value) : Effect.fail(storageError("MediaObjectStore.S3.promote.verify", "The promoted object metadata does not match its intent."))));
2509
+ }
2510
+ function copySource(bucket, objectKey) {
2511
+ return `/${encodeURIComponent(bucket)}/${objectKey.split("/").map(encodeURIComponent).join("/")}`;
2512
+ }
2513
+ function putObject(bucket, input, client) {
2514
+ return Effect.gen(function* () {
2515
+ const body = yield* validatedWriteBody(input);
2516
+ yield* client.putObject({
2517
+ body,
2518
+ bucket,
2519
+ cacheControl: replaceableMediaCacheControl,
2520
+ contentLength: input.byteSize,
2521
+ contentType: input.mimeType,
2522
+ key: input.objectKey
2523
+ }).pipe(Effect.catch((failure) => isWriteBodyError(failure.cause) ? Effect.fail(failure.cause) : Effect.fail(providerError("MediaObjectStore.S3.put", failure))));
2524
+ return {
2525
+ byteSize: input.byteSize,
2526
+ mimeType: input.mimeType,
2527
+ objectKey: input.objectKey
2528
+ };
2529
+ });
2530
+ }
2531
+ const validatedWriteBody = Effect.fn("MediaObjectStore.S3.validateWriteBody")(function* (input) {
2532
+ const byteLength = yield* Ref.make(0);
2533
+ return input.body.pipe(Stream.mapEffect((chunk) => Effect.gen(function* () {
2534
+ if ((yield* Ref.updateAndGet(byteLength, (current) => current + chunk.byteLength)) > input.byteSize) return yield* storageError("MediaObjectStore.S3.put.body", "The object body exceeds its declared byte size.");
2535
+ return chunk;
2536
+ })), Stream.concat(Stream.fromEffect(Ref.get(byteLength).pipe(Effect.flatMap((actual) => actual === input.byteSize ? Effect.void : Effect.fail(storageError("MediaObjectStore.S3.put.body", "The object body does not match its declared byte size."))))).pipe(Stream.drain)));
2537
+ });
2538
+ function statObject(bucket, objectKey, client) {
2539
+ return client.headObject({
2540
+ bucket,
2541
+ key: objectKey
2542
+ }).pipe(Effect.matchEffect({
2543
+ onFailure: (failure) => isMissingObject(failure.cause) ? Effect.succeed(Option.none()) : Effect.fail(providerError("MediaObjectStore.S3.stat", failure)),
2544
+ onSuccess: (output) => decodeStoredObject("MediaObjectStore.S3.stat", objectKey, output.contentLength, output.contentType).pipe(Effect.asSome)
2545
+ }));
2546
+ }
2547
+ function listObjects(bucket, client) {
2548
+ return Stream.paginate({ seenTokens: /* @__PURE__ */ new Set() }, (state) => providerRequest("MediaObjectStore.S3.list", client.listObjects({
2549
+ bucket,
2550
+ continuationToken: state.continuationToken,
2551
+ prefix: mediaObjectPrefix
2552
+ })).pipe(Effect.flatMap((page) => Effect.gen(function* () {
2553
+ return [yield* Effect.forEach(page.objects, decodeListedObject), yield* nextListState(state, page)];
2554
+ }))));
2555
+ }
2556
+ function decodeListedObject(object) {
2557
+ if (object.key === void 0) return Effect.fail(storageError("MediaObjectStore.S3.list", "The provider returned object metadata without a key."));
2558
+ return decodeStoredObject("MediaObjectStore.S3.list", object.key, object.size);
2559
+ }
2560
+ function nextListState(state, page) {
2561
+ if (!page.isTruncated) return Effect.succeedNone;
2562
+ const token = page.nextContinuationToken;
2563
+ if (!token || token === state.continuationToken || state.seenTokens.has(token)) return Effect.fail(storageError("MediaObjectStore.S3.list", "The provider returned an invalid continuation token."));
2564
+ return Effect.succeedSome({
2565
+ continuationToken: token,
2566
+ seenTokens: /* @__PURE__ */ new Set([...state.seenTokens, token])
2567
+ });
2568
+ }
2569
+ function decodeStoredObject(operation, objectKey, byteSize, contentType) {
2570
+ const decodedObjectKey = Schema.decodeOption(MediaObjectKey)(objectKey);
2571
+ const decodedByteSize = Schema.decodeUnknownOption(MediaByteSize)(byteSize);
2572
+ if (Option.isNone(decodedObjectKey) || Option.isNone(decodedByteSize)) return Effect.fail(storageError(operation, "The provider returned invalid object metadata."));
2573
+ const decodedMimeType = Schema.decodeUnknownOption(ImageMimeType)(contentType);
2574
+ const storedObject = {
2575
+ byteSize: decodedByteSize.value,
2576
+ objectKey: decodedObjectKey.value
2577
+ };
2578
+ return Effect.succeed(Option.isSome(decodedMimeType) ? {
2579
+ ...storedObject,
2580
+ mimeType: decodedMimeType.value
2581
+ } : storedObject);
2582
+ }
2583
+ function providerRequest(operation, request) {
2584
+ return request.pipe(Effect.mapError((failure) => providerError(operation, failure)));
2585
+ }
2586
+ function provisionBucket(options, client) {
2587
+ if (!options.provisionBucket) return Effect.void;
2588
+ return client.headBucket({ bucket: options.bucket }).pipe(Effect.catch((failure) => isMissingBucket(failure.cause) ? createMissingBucket(options, client) : Effect.fail(providerError("MediaObjectStore.S3.bucket.head", failure))));
2589
+ }
2590
+ function createMissingBucket(options, client) {
2591
+ return client.createBucket({
2592
+ bucket: options.bucket,
2593
+ locationConstraint: options.endpoint || options.region === "us-east-1" ? void 0 : options.region
2594
+ }).pipe(Effect.catch((failure) => isBucketAlreadyOwned(failure.cause) ? providerRequest("MediaObjectStore.S3.bucket.verify", client.headBucket({ bucket: options.bucket })) : Effect.fail(providerError("MediaObjectStore.S3.bucket.create", failure))));
2595
+ }
2596
+ const ProviderFailureDetails = Schema.Struct({
2597
+ $metadata: Schema.optionalKey(Schema.Struct({
2598
+ attempts: Schema.optionalKey(Schema.Finite),
2599
+ httpStatusCode: Schema.optionalKey(Schema.Finite)
2600
+ })),
2601
+ name: Schema.optionalKey(Schema.String)
2602
+ });
2603
+ const decodeProviderFailure = Schema.decodeUnknownOption(ProviderFailureDetails);
2604
+ function providerError(operation, failure) {
2605
+ const details = decodeProviderFailure(failure.cause);
2606
+ const diagnostics = [];
2607
+ if (Option.isSome(details)) {
2608
+ if (details.value.name) diagnostics.push(`name=${details.value.name}`);
2609
+ if (details.value.$metadata?.httpStatusCode !== void 0) diagnostics.push(`status=${details.value.$metadata.httpStatusCode}`);
2610
+ if (details.value.$metadata?.attempts !== void 0) diagnostics.push(`attempts=${details.value.$metadata.attempts}`);
2611
+ }
2612
+ return storageError(operation, `The S3 provider request failed${diagnostics.length > 0 ? ` (${diagnostics.join(", ")}).` : "."}`);
2613
+ }
2614
+ function isMissingObject(cause) {
2615
+ const details = decodeProviderFailure(cause);
2616
+ if (Option.isNone(details)) return false;
2617
+ return details.value.name === "NoSuchKey" || details.value.name === "NotFound";
2618
+ }
2619
+ function isMissingBucket(cause) {
2620
+ const details = decodeProviderFailure(cause);
2621
+ if (Option.isNone(details)) return false;
2622
+ return details.value.name === "NoSuchBucket" || details.value.$metadata?.httpStatusCode === 404;
2623
+ }
2624
+ function isBucketAlreadyOwned(cause) {
2625
+ const details = decodeProviderFailure(cause);
2626
+ return Option.isSome(details) && details.value.name === "BucketAlreadyOwnedByYou";
2627
+ }
2628
+ function resolveOptions(options) {
2629
+ const bucket = options.bucket.trim();
2630
+ const region = options.region.trim();
2631
+ const endpoint = options.endpoint?.trim();
2632
+ if (!bucket || !region || options.endpoint !== void 0 && !endpoint) return Effect.fail(storageError("MediaObjectStore.S3.configure", "The bucket, region, and configured endpoint must be non-empty."));
2633
+ if (options.credentials) {
2634
+ const secretAccessKey = Redacted.value(options.credentials.secretAccessKey);
2635
+ const sessionToken = options.credentials.sessionToken && Redacted.value(options.credentials.sessionToken);
2636
+ if (!options.credentials.accessKeyId.trim() || !secretAccessKey || sessionToken === "") return Effect.fail(storageError("MediaObjectStore.S3.configure", "The configured credentials must be non-empty."));
2637
+ }
2638
+ return Effect.succeed({
2639
+ ...options,
2640
+ bucket,
2641
+ credentials: options.credentials ? {
2642
+ ...options.credentials,
2643
+ accessKeyId: options.credentials.accessKeyId.trim()
2644
+ } : void 0,
2645
+ endpoint,
2646
+ forcePathStyle: options.forcePathStyle ?? false,
2647
+ provisionBucket: options.provisionBucket ?? false,
2648
+ region
2649
+ });
2650
+ }
2651
+ function storageError(operation, message) {
2652
+ return new MediaStorageError({
2653
+ cause: new Error(message),
2654
+ operation
2655
+ });
2656
+ }
2657
+
2658
+ //#endregion
2659
+ //#region ../s3/src/S3MediaObjects.ts
2660
+ const maxS3SingleCopyBytes = 5e9;
2661
+
2662
+ //#endregion
2663
+ export { DocumentRevision as $, StudioBadRequest as A, User as B, DocumentStore as C, CurrentStudioRequest as D, defaultAccessControlLayer as E, studioInternalError as F, UserSelfRemovalDenied as G, UserAlreadyExists as H, InitializationAlreadyCompleted as I, CollaborationServerMessage as J, UserSelfRoleChangeDenied as K, InitializationProofInvalid as L, StudioRequestConflict as M, StudioSchemaError as N, StudioApi as O, StudioInternalError as P, DocumentHistoryInvalidCursor as Q, InitializationState as R, DocumentPersistenceError as S, AccessControl as T, UserNotFound as U, UserAdministrationDenied as V, UserPage as W, StateVector as X, CollaborationTicket as Y, DocumentHistoryComparisonTooLarge as Z, Documents as _, mediaObjectPrefix as a, ActivityEntry as at, cursorCodec as b, Seo as c, ActivityPage as ct, seoDeliveryLayer as d, DocumentMediaUnavailable as dt, ACTIVITY_ACTIONS as et, seoLayer as f, DocumentNotFound as ft, DocumentPreviews as g, PublishedDocumentNotFound as gt, DocumentMaintenance as h, DocumentVersionConflict as ht, MediaObjectStore as i, ActivityCursorInvalid as it, StudioBootstrap as j, StudioAuthentication as k, SeoDelivery as l, ActivityTarget as lt, SeoStore as m, DocumentValidationFailed as mt, s3MediaObjectStoreLayer as n, ActivityContext as nt, PreviewSeo as o, ActivityEntryDraft as ot, SeoPersistenceError as p, DocumentPathConflict as pt, CollaborationClientMessage as q, MediaPersistenceError as r, ActivityCursor as rt, PublishedSeo as s, ActivityId as st, maxS3SingleCopyBytes as t, ActivityAction as tt, makeDefaultSeoOptions as u, ActivityTargetKind as ut, PublishedDocuments as v, renderEditorMarkdown as w, DocumentGenerationConflict as x, documentsLayer as y, CreateUserInput as z };
2664
+ //# sourceMappingURL=S3MediaObjects-JKMAWyVB.js.map