@batterai/host 0.1.0

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 (148) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +21 -0
  3. package/README.md +23 -0
  4. package/dist/cache/dependency-graph.d.ts +18 -0
  5. package/dist/cache/dependency-graph.d.ts.map +1 -0
  6. package/dist/cache/dependency-graph.js +30 -0
  7. package/dist/cache/dependency-graph.js.map +1 -0
  8. package/dist/cache/render-cache.d.ts +39 -0
  9. package/dist/cache/render-cache.d.ts.map +1 -0
  10. package/dist/cache/render-cache.js +60 -0
  11. package/dist/cache/render-cache.js.map +1 -0
  12. package/dist/client/mount.d.ts +9 -0
  13. package/dist/client/mount.d.ts.map +1 -0
  14. package/dist/client/mount.js +69 -0
  15. package/dist/client/mount.js.map +1 -0
  16. package/dist/client.d.ts +2 -0
  17. package/dist/client.d.ts.map +1 -0
  18. package/dist/client.js +2 -0
  19. package/dist/client.js.map +1 -0
  20. package/dist/collab/in-memory-page-draft-store.d.ts +44 -0
  21. package/dist/collab/in-memory-page-draft-store.d.ts.map +1 -0
  22. package/dist/collab/in-memory-page-draft-store.js +217 -0
  23. package/dist/collab/in-memory-page-draft-store.js.map +1 -0
  24. package/dist/collab/page-doc-page-draft-store.d.ts +41 -0
  25. package/dist/collab/page-doc-page-draft-store.d.ts.map +1 -0
  26. package/dist/collab/page-doc-page-draft-store.js +328 -0
  27. package/dist/collab/page-doc-page-draft-store.js.map +1 -0
  28. package/dist/collab/page-draft-store.d.ts +152 -0
  29. package/dist/collab/page-draft-store.d.ts.map +1 -0
  30. package/dist/collab/page-draft-store.js +61 -0
  31. package/dist/collab/page-draft-store.js.map +1 -0
  32. package/dist/content-source/conformance/cases-delegation.d.ts +3 -0
  33. package/dist/content-source/conformance/cases-delegation.d.ts.map +1 -0
  34. package/dist/content-source/conformance/cases-delegation.js +55 -0
  35. package/dist/content-source/conformance/cases-delegation.js.map +1 -0
  36. package/dist/content-source/conformance/cases-fidelity.d.ts +3 -0
  37. package/dist/content-source/conformance/cases-fidelity.d.ts.map +1 -0
  38. package/dist/content-source/conformance/cases-fidelity.js +67 -0
  39. package/dist/content-source/conformance/cases-fidelity.js.map +1 -0
  40. package/dist/content-source/conformance/cases-isolation.d.ts +3 -0
  41. package/dist/content-source/conformance/cases-isolation.d.ts.map +1 -0
  42. package/dist/content-source/conformance/cases-isolation.js +57 -0
  43. package/dist/content-source/conformance/cases-isolation.js.map +1 -0
  44. package/dist/content-source/conformance/cases-permissions.d.ts +3 -0
  45. package/dist/content-source/conformance/cases-permissions.d.ts.map +1 -0
  46. package/dist/content-source/conformance/cases-permissions.js +50 -0
  47. package/dist/content-source/conformance/cases-permissions.js.map +1 -0
  48. package/dist/content-source/conformance/cases-reads.d.ts +3 -0
  49. package/dist/content-source/conformance/cases-reads.d.ts.map +1 -0
  50. package/dist/content-source/conformance/cases-reads.js +216 -0
  51. package/dist/content-source/conformance/cases-reads.js.map +1 -0
  52. package/dist/content-source/conformance/cases-writes.d.ts +11 -0
  53. package/dist/content-source/conformance/cases-writes.d.ts.map +1 -0
  54. package/dist/content-source/conformance/cases-writes.js +73 -0
  55. package/dist/content-source/conformance/cases-writes.js.map +1 -0
  56. package/dist/content-source/conformance/session-required.d.ts +10 -0
  57. package/dist/content-source/conformance/session-required.d.ts.map +1 -0
  58. package/dist/content-source/conformance/session-required.js +11 -0
  59. package/dist/content-source/conformance/session-required.js.map +1 -0
  60. package/dist/content-source/conformance/sessionless-viewer.d.ts +12 -0
  61. package/dist/content-source/conformance/sessionless-viewer.d.ts.map +1 -0
  62. package/dist/content-source/conformance/sessionless-viewer.js +5 -0
  63. package/dist/content-source/conformance/sessionless-viewer.js.map +1 -0
  64. package/dist/content-source/conformance/target.d.ts +111 -0
  65. package/dist/content-source/conformance/target.d.ts.map +1 -0
  66. package/dist/content-source/conformance/target.js +2 -0
  67. package/dist/content-source/conformance/target.js.map +1 -0
  68. package/dist/content-source/conformance-kit.d.ts +4 -0
  69. package/dist/content-source/conformance-kit.d.ts.map +1 -0
  70. package/dist/content-source/conformance-kit.js +41 -0
  71. package/dist/content-source/conformance-kit.js.map +1 -0
  72. package/dist/content-source/content-source.d.ts +153 -0
  73. package/dist/content-source/content-source.d.ts.map +1 -0
  74. package/dist/content-source/content-source.js +12 -0
  75. package/dist/content-source/content-source.js.map +1 -0
  76. package/dist/content-source/permissions.d.ts +46 -0
  77. package/dist/content-source/permissions.d.ts.map +1 -0
  78. package/dist/content-source/permissions.js +33 -0
  79. package/dist/content-source/permissions.js.map +1 -0
  80. package/dist/content-source/viewer.d.ts +48 -0
  81. package/dist/content-source/viewer.d.ts.map +1 -0
  82. package/dist/content-source/viewer.js +24 -0
  83. package/dist/content-source/viewer.js.map +1 -0
  84. package/dist/index.d.ts +30 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +30 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/lifecycle/cms-lifecycle-store.d.ts +72 -0
  89. package/dist/lifecycle/cms-lifecycle-store.d.ts.map +1 -0
  90. package/dist/lifecycle/cms-lifecycle-store.js +150 -0
  91. package/dist/lifecycle/cms-lifecycle-store.js.map +1 -0
  92. package/dist/lifecycle/lifecycle-store.d.ts +35 -0
  93. package/dist/lifecycle/lifecycle-store.d.ts.map +1 -0
  94. package/dist/lifecycle/lifecycle-store.js +69 -0
  95. package/dist/lifecycle/lifecycle-store.js.map +1 -0
  96. package/dist/lifecycle/version-token.d.ts +30 -0
  97. package/dist/lifecycle/version-token.d.ts.map +1 -0
  98. package/dist/lifecycle/version-token.js +22 -0
  99. package/dist/lifecycle/version-token.js.map +1 -0
  100. package/dist/messages.d.ts +26 -0
  101. package/dist/messages.d.ts.map +1 -0
  102. package/dist/messages.js +31 -0
  103. package/dist/messages.js.map +1 -0
  104. package/dist/render/image-usage-preview.d.ts +19 -0
  105. package/dist/render/image-usage-preview.d.ts.map +1 -0
  106. package/dist/render/image-usage-preview.js +25 -0
  107. package/dist/render/image-usage-preview.js.map +1 -0
  108. package/dist/render/image-usage.d.ts +32 -0
  109. package/dist/render/image-usage.d.ts.map +1 -0
  110. package/dist/render/image-usage.js +116 -0
  111. package/dist/render/image-usage.js.map +1 -0
  112. package/dist/render/page-renderer.d.ts +44 -0
  113. package/dist/render/page-renderer.d.ts.map +1 -0
  114. package/dist/render/page-renderer.js +115 -0
  115. package/dist/render/page-renderer.js.map +1 -0
  116. package/dist/resolver/data-loader.d.ts +49 -0
  117. package/dist/resolver/data-loader.d.ts.map +1 -0
  118. package/dist/resolver/data-loader.js +121 -0
  119. package/dist/resolver/data-loader.js.map +1 -0
  120. package/dist/resolver/resolution-service.d.ts +49 -0
  121. package/dist/resolver/resolution-service.d.ts.map +1 -0
  122. package/dist/resolver/resolution-service.js +193 -0
  123. package/dist/resolver/resolution-service.js.map +1 -0
  124. package/dist/routing/route-resolver.d.ts +73 -0
  125. package/dist/routing/route-resolver.d.ts.map +1 -0
  126. package/dist/routing/route-resolver.js +114 -0
  127. package/dist/routing/route-resolver.js.map +1 -0
  128. package/dist/scope-engine/build-stack.d.ts +49 -0
  129. package/dist/scope-engine/build-stack.d.ts.map +1 -0
  130. package/dist/scope-engine/build-stack.js +77 -0
  131. package/dist/scope-engine/build-stack.js.map +1 -0
  132. package/dist/scope-engine/repeater-query.d.ts +9 -0
  133. package/dist/scope-engine/repeater-query.d.ts.map +1 -0
  134. package/dist/scope-engine/repeater-query.js +39 -0
  135. package/dist/scope-engine/repeater-query.js.map +1 -0
  136. package/dist/seo/metadata.d.ts +30 -0
  137. package/dist/seo/metadata.d.ts.map +1 -0
  138. package/dist/seo/metadata.js +118 -0
  139. package/dist/seo/metadata.js.map +1 -0
  140. package/dist/seo/sitemap.d.ts +11 -0
  141. package/dist/seo/sitemap.d.ts.map +1 -0
  142. package/dist/seo/sitemap.js +20 -0
  143. package/dist/seo/sitemap.js.map +1 -0
  144. package/dist/write/write-path.d.ts +63 -0
  145. package/dist/write/write-path.d.ts.map +1 -0
  146. package/dist/write/write-path.js +139 -0
  147. package/dist/write/write-path.js.map +1 -0
  148. package/package.json +44 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ # @batterai/host
2
+
3
+ ## 0.1.0
4
+
5
+ - Baseline host runtime with CMS-backed lifecycle/page documents, CAS write path, routing, rendering, SEO, cache, and collab draft store seams.
6
+ - Ships as Node-compatible ESM with declaration files under `dist/`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Batterai.eu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # @batterai/host
2
+
3
+ Host/runtime services for CMS-backed Battery pages.
4
+
5
+ ## What It Provides
6
+
7
+ - Content-source and entity-provider seams.
8
+ - PageDoc lifecycle storage with compare-and-swap.
9
+ - Route resolution, metadata, sitemap, rendering, and render-cache helpers.
10
+ - Binding resolution and repeater query execution.
11
+ - Draft-store adapters used by collaboration and MCP paths.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install @batterai/host
17
+ ```
18
+
19
+ Requires Node.js `>=20.11`.
20
+
21
+ ## Storage Boundary
22
+
23
+ The host layer is the storage boundary. Durable pages, page trees, content type values, media references, drafts, and published snapshots must be stored in the host CMS through adapters or host-provided ports. Canvas state is UI/session state only.
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Binding->page dependency graph (decision 004 §6.4; invariant F). Built as a
3
+ * by-product of resolvePage; consumed by publish()/mutate() to invalidate.
4
+ * Repeater deps are content-type-granular (conservative over-invalidation,
5
+ * documented); detail-template deps are per-instance-precise.
6
+ */
7
+ import type { PageId } from "@batterai/bind-core";
8
+ import { type VersionedSubject } from "../lifecycle/version-token";
9
+ export interface BindingDependencyGraph {
10
+ recordDependency(page: PageId, subject: VersionedSubject): void;
11
+ dependenciesOf(page: PageId): readonly VersionedSubject[];
12
+ /** Returns invalidated pages — staleness is surfaced, never silent. */
13
+ invalidate(subject: VersionedSubject): readonly PageId[];
14
+ /** Subscribe for cache eviction wiring. */
15
+ onInvalidate(cb: (pages: readonly PageId[], subject: VersionedSubject) => void): void;
16
+ }
17
+ export declare function createInMemoryDependencyGraph(): BindingDependencyGraph;
18
+ //# sourceMappingURL=dependency-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../../src/cache/dependency-graph.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAE/E,MAAM,WAAW,sBAAsB;IACrC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,gBAAgB,EAAE,CAAC;IAC1D,uEAAuE;IACvE,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,MAAM,EAAE,CAAC;IACzD,2CAA2C;IAC3C,YAAY,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI,CAAC;CACvF;AAED,wBAAgB,6BAA6B,IAAI,sBAAsB,CAyBtE"}
@@ -0,0 +1,30 @@
1
+ import { subjectKey } from "../lifecycle/version-token.js";
2
+ export function createInMemoryDependencyGraph() {
3
+ const bySubject = new Map();
4
+ const byPage = new Map();
5
+ const listeners = [];
6
+ return {
7
+ recordDependency(page, subject) {
8
+ const key = subjectKey(subject);
9
+ if (!bySubject.has(key))
10
+ bySubject.set(key, new Set());
11
+ bySubject.get(key).add(page);
12
+ if (!byPage.has(page))
13
+ byPage.set(page, new Map());
14
+ byPage.get(page).set(key, subject);
15
+ },
16
+ dependenciesOf(page) {
17
+ return [...(byPage.get(page)?.values() ?? [])];
18
+ },
19
+ invalidate(subject) {
20
+ const pages = [...(bySubject.get(subjectKey(subject)) ?? [])];
21
+ for (const cb of listeners)
22
+ cb(pages, subject);
23
+ return pages;
24
+ },
25
+ onInvalidate(cb) {
26
+ listeners.push(cb);
27
+ },
28
+ };
29
+ }
30
+ //# sourceMappingURL=dependency-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-graph.js","sourceRoot":"","sources":["../../src/cache/dependency-graph.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAyB,MAAM,4BAA4B,CAAC;AAW/E,MAAM,UAAU,6BAA6B;IAC3C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyC,CAAC;IAChE,MAAM,SAAS,GAAyE,EAAE,CAAC;IAE3F,OAAO;QACL,gBAAgB,CAAC,IAAI,EAAE,OAAO;YAC5B,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;YAChC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;YACvD,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACtC,CAAC;QACD,cAAc,CAAC,IAAI;YACjB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACjD,CAAC;QACD,UAAU,CAAC,OAAO;YAChB,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC9D,KAAK,MAAM,EAAE,IAAI,SAAS;gBAAE,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC/C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,YAAY,CAAC,EAAE;YACb,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Render cache (decision 004 §6.3; invariant F): key = (locale, timeZone,
3
+ * route, mode, contentVersion joined-hash). Preview and public NEVER share
4
+ * entries. Data scoping is delegated to the CMS (decision 012); a cache
5
+ * instance is per-deployment, so the key carries no owner brand.
6
+ * Metadata is inside the cached HTML — one artifact, one invalidation path.
7
+ */
8
+ import type { Locale, PageId, TimeZone } from "@batterai/bind-core";
9
+ import type { RequestMode } from "../routing/route-resolver";
10
+ import type { BindingDependencyGraph } from "./dependency-graph";
11
+ import type { LifecycleStore } from "../lifecycle/lifecycle-store";
12
+ export interface RenderCacheKey {
13
+ readonly locale: Locale;
14
+ readonly timeZone: TimeZone;
15
+ readonly route: string;
16
+ readonly mode: RequestMode;
17
+ readonly contentVersion: string;
18
+ /** The rendered page — eviction routes through the dependency graph's
19
+ * subject->pages mapping, so every entry must carry its page identity. */
20
+ readonly pageId: PageId;
21
+ }
22
+ export interface CachedRender {
23
+ readonly html: string;
24
+ readonly cachedAt: string;
25
+ }
26
+ /** Joined-hash of every dependency's current version — NOT a global counter
27
+ * (a global counter would invalidate the whole site on any edit). */
28
+ export declare function contentVersionFor(page: PageId, depGraph: BindingDependencyGraph, lifecycle: LifecycleStore<unknown>): Promise<string>;
29
+ export interface RenderCache {
30
+ get(key: RenderCacheKey): CachedRender | undefined;
31
+ set(key: RenderCacheKey, value: CachedRender): void;
32
+ evictByPage(page: PageId): void;
33
+ readonly stats: {
34
+ hits: number;
35
+ misses: number;
36
+ };
37
+ }
38
+ export declare function createInMemoryRenderCache(depGraph?: BindingDependencyGraph): RenderCache;
39
+ //# sourceMappingURL=render-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-cache.d.ts","sourceRoot":"","sources":["../../src/cache/render-cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;8EAC0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAeD;qEACqE;AACrE,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,sBAAsB,EAChC,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,GACjC,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,GAAG,EAAE,cAAc,GAAG,YAAY,GAAG,SAAS,CAAC;IACnD,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,sBAAsB,GAAG,WAAW,CA8BxF"}
@@ -0,0 +1,60 @@
1
+ import { subjectKey } from "../lifecycle/version-token.js";
2
+ function keyString(k) {
3
+ return `${k.locale}|${k.timeZone}|${k.mode}|${k.route}|${k.contentVersion}`;
4
+ }
5
+ /* Eviction correctness note (Phase 7 fix): instance subjects are CMS-owned —
6
+ * the lifecycle store has no version for them, so the contentVersion half of
7
+ * the key CANNOT change on a CMS write. For writes that flow THROUGH the host
8
+ * (editor mutate, form block) the dependency graph fires onInvalidate with
9
+ * the precise page list and we evict those pages' entries here. Out-of-band
10
+ * CMS edits (e.g. directly in the Payload admin) need adapter webhook
11
+ * eventing -> depGraph.invalidate — plan §11 backlog; until then such edits
12
+ * surface only when a page's own version bumps. Documented, not silent. */
13
+ /** Joined-hash of every dependency's current version — NOT a global counter
14
+ * (a global counter would invalidate the whole site on any edit). */
15
+ export async function contentVersionFor(page, depGraph, lifecycle) {
16
+ const parts = [];
17
+ for (const subject of depGraph.dependenciesOf(page)) {
18
+ const v = await lifecycle.currentVersion(subject);
19
+ parts.push(`${subjectKey(subject)}=${v ?? "none"}`);
20
+ }
21
+ const pageV = await lifecycle.currentVersion({ kind: "page", pageId: page });
22
+ parts.push(`self=${pageV ?? "none"}`);
23
+ return parts.sort().join(";");
24
+ }
25
+ export function createInMemoryRenderCache(depGraph) {
26
+ const entries = new Map();
27
+ const byPage = new Map();
28
+ const stats = { hits: 0, misses: 0 };
29
+ const cache = {
30
+ get(key) {
31
+ const hit = entries.get(keyString(key));
32
+ if (hit)
33
+ stats.hits += 1;
34
+ else
35
+ stats.misses += 1;
36
+ return hit;
37
+ },
38
+ set(key, value) {
39
+ const k = keyString(key);
40
+ entries.set(k, value);
41
+ if (!byPage.has(key.pageId))
42
+ byPage.set(key.pageId, new Set());
43
+ byPage.get(key.pageId).add(k);
44
+ },
45
+ evictByPage(page) {
46
+ for (const k of byPage.get(page) ?? [])
47
+ entries.delete(k);
48
+ byPage.delete(page);
49
+ },
50
+ stats,
51
+ };
52
+ // Wire graph invalidation -> eviction: the graph knows precisely which
53
+ // pages depend on the mutated subject (see the eviction note above).
54
+ depGraph?.onInvalidate((pages) => {
55
+ for (const page of pages)
56
+ cache.evictByPage(page);
57
+ });
58
+ return cache;
59
+ }
60
+ //# sourceMappingURL=render-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-cache.js","sourceRoot":"","sources":["../../src/cache/render-cache.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAoBxD,SAAS,SAAS,CAAC,CAAiB;IAClC,OAAO,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;AAC9E,CAAC;AAED;;;;;;;2EAO2E;AAE3E;qEACqE;AACrE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,QAAgC,EAChC,SAAkC;IAElC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAClD,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7E,KAAK,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,MAAM,EAAE,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AASD,MAAM,UAAU,yBAAyB,CAAC,QAAiC;IACzE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;IAChD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAErC,MAAM,KAAK,GAAgB;QACzB,GAAG,CAAC,GAAG;YACL,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;YACxC,IAAI,GAAG;gBAAE,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;;gBACpB,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;YACvB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,GAAG,CAAC,GAAG,EAAE,KAAK;YACZ,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAgB,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAgB,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;YACnF,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAgB,CAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC;QACD,WAAW,CAAC,IAAI;YACd,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAc,CAAC,IAAI,EAAE;gBAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpE,MAAM,CAAC,MAAM,CAAC,IAAc,CAAC,CAAC;QAChC,CAAC;QACD,KAAK;KACN,CAAC;IACF,uEAAuE;IACvE,qEAAqE;IACrE,QAAQ,EAAE,YAAY,CAAC,CAAC,KAAK,EAAE,EAAE;QAC/B,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface ClientMountOptions {
2
+ /** Fragment endpoint: returns { rowsHtml, cursor?, hasMore } as JSON. */
3
+ readonly fragmentUrl: (repeaterId: string, cursor: string) => string;
4
+ readonly messages?: {
5
+ readonly loadMoreRetry?: string;
6
+ };
7
+ }
8
+ export declare function mountInfiniteScroll(root: ParentNode, opts: ClientMountOptions): () => void;
9
+ //# sourceMappingURL=mount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/client/mount.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,QAAQ,CAAC,WAAW,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;KACjC,CAAC;CACH;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kBAAkB,GAAG,MAAM,IAAI,CAuD1F"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Host client mount (decision 004 §0.4/§8.2 — THE one DOM-touching module in
3
+ * @batterai/host). Wires:
4
+ * - infinite scroll: IntersectionObserver on [data-load-more] sentinels ->
5
+ * fetch the fragment endpoint -> append SERVER-RENDERED rows verbatim
6
+ * (same render path as the initial page — invariant A's append clause).
7
+ * Fetch failures render a VISIBLE retry affordance, never a silent stop.
8
+ */
9
+ import { hostMessage } from "../messages.js";
10
+ export function mountInfiniteScroll(root, opts) {
11
+ const observer = new IntersectionObserver(async (entries) => {
12
+ for (const entry of entries) {
13
+ if (!entry.isIntersecting)
14
+ continue;
15
+ const sentinel = entry.target;
16
+ observer.unobserve(sentinel);
17
+ const repeaterId = sentinel.dataset.repeaterId;
18
+ const cursor = sentinel.dataset.cursor;
19
+ try {
20
+ const url = opts.fragmentUrl(repeaterId, cursor);
21
+ // TRUST BOUNDARY (invariant H; reviewed in Phase 10's security pass):
22
+ // the fragment endpoint is SAME-ORIGIN and host-owned; its rowsHtml is
23
+ // produced by the identical server render path as the initial page —
24
+ // every bound value already passed bind-core's context-aware escaper
25
+ // and Lit's auto-escaping. Appending those bytes VERBATIM is the
26
+ // invariant-A append guarantee (sanitizing/re-rendering here would
27
+ // break byte-equality with a full server render). Cross-origin URLs
28
+ // are refused outright.
29
+ if (new URL(url, location.href).origin !== location.origin) {
30
+ throw new Error("fragment endpoint must be same-origin");
31
+ }
32
+ const res = await fetch(url);
33
+ if (!res.ok)
34
+ throw new Error(`fragment ${res.status}`);
35
+ const body = (await res.json());
36
+ for (const row of body.rowsHtml) {
37
+ sentinel.insertAdjacentHTML("beforebegin", row); // our own escaper-hardened server bytes, verbatim
38
+ }
39
+ if (body.hasMore && body.cursor) {
40
+ sentinel.dataset.cursor = body.cursor;
41
+ observer.observe(sentinel);
42
+ }
43
+ else {
44
+ sentinel.remove();
45
+ }
46
+ }
47
+ catch (e) {
48
+ // VISIBLE failure + retry — never a silently dead list (plan §0 rule 4).
49
+ sentinel.replaceChildren();
50
+ const retry = document.createElement("button");
51
+ retry.type = "button";
52
+ retry.className = "be-load-more-retry";
53
+ retry.setAttribute("role", "alert");
54
+ retry.textContent = opts.messages?.loadMoreRetry ?? hostMessage("en", "loadMore.retry");
55
+ retry.addEventListener("click", () => {
56
+ sentinel.replaceChildren();
57
+ observer.observe(sentinel);
58
+ });
59
+ sentinel.appendChild(retry);
60
+ void e;
61
+ }
62
+ }
63
+ });
64
+ for (const sentinel of root.querySelectorAll("[data-load-more]")) {
65
+ observer.observe(sentinel);
66
+ }
67
+ return () => observer.disconnect();
68
+ }
69
+ //# sourceMappingURL=mount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mount.js","sourceRoot":"","sources":["../../src/client/mount.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./client/mount";
2
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./client/mount.js";
2
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { Viewer } from "../content-source/viewer";
2
+ import { type ContentVersionToken, type PublishedSnapshot } from "../lifecycle/version-token";
3
+ import type { PageRecord } from "../routing/route-resolver";
4
+ import { type CollaborationAuditEvent, type DurableAcceptedOp, type PageAccessAction, type PageDraftRef, type PageDraftStore } from "./page-draft-store";
5
+ interface DedupeEntry {
6
+ readonly seq: number;
7
+ readonly opDigest: string;
8
+ }
9
+ export interface InMemoryStoredPageDraftRoom<TRecord, TOpPayload> {
10
+ readonly ref: PageDraftRef;
11
+ record: TRecord;
12
+ draftVersion: ContentVersionToken | undefined;
13
+ draftVersionCounter: number;
14
+ publishVersionCounter: number;
15
+ seq: number;
16
+ epoch: string;
17
+ compactedThroughSeq: number;
18
+ flushedThroughSeq: number;
19
+ readonly ops: DurableAcceptedOp<TOpPayload>[];
20
+ readonly dedupe: Map<string, DedupeEntry>;
21
+ readonly audit: CollaborationAuditEvent<TOpPayload>[];
22
+ readonly snapshots: PublishedSnapshot<TRecord>[];
23
+ }
24
+ export interface InMemoryPageDraftStoreBacking<TRecord = PageRecord, TOpPayload = unknown> {
25
+ readonly rooms: Map<string, InMemoryStoredPageDraftRoom<TRecord, TOpPayload>>;
26
+ epochCounter: number;
27
+ }
28
+ export type InMemoryPageDraftAuthorizer = (viewer: Viewer, ref: PageDraftRef, action: PageAccessAction) => Promise<true | string> | true | string;
29
+ export interface InitialPageDraft<TRecord = PageRecord> {
30
+ readonly ref: PageDraftRef;
31
+ readonly record: TRecord;
32
+ readonly draftVersion?: ContentVersionToken;
33
+ readonly epoch?: string;
34
+ }
35
+ export interface InMemoryPageDraftStoreOptions<TRecord = PageRecord, TOpPayload = unknown> {
36
+ readonly backing?: InMemoryPageDraftStoreBacking<TRecord, TOpPayload>;
37
+ readonly drafts?: readonly InitialPageDraft<TRecord>[];
38
+ readonly authorize?: InMemoryPageDraftAuthorizer;
39
+ readonly now?: () => string;
40
+ }
41
+ export declare function createInMemoryPageDraftStoreBacking<TRecord = PageRecord, TOpPayload = unknown>(): InMemoryPageDraftStoreBacking<TRecord, TOpPayload>;
42
+ export declare function createInMemoryPageDraftStore<TRecord = PageRecord, TOpPayload = unknown>(opts?: InMemoryPageDraftStoreOptions<TRecord, TOpPayload>): PageDraftStore<TRecord, TOpPayload>;
43
+ export {};
44
+ //# sourceMappingURL=in-memory-page-draft-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-page-draft-store.d.ts","sourceRoot":"","sources":["../../src/collab/in-memory-page-draft-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,217 @@
1
+ import { versionNumber, versionToken, } from "../lifecycle/version-token.js";
2
+ import { PageDraftAccessDenied, PageDraftEpochMismatch, PageDraftOpsCompacted, PageDraftRoomNotFound, PageDraftSeqMismatch, PageDraftVersionConflict, } from "./page-draft-store.js";
3
+ export function createInMemoryPageDraftStoreBacking() {
4
+ return { rooms: new Map(), epochCounter: 0 };
5
+ }
6
+ export function createInMemoryPageDraftStore(opts = {}) {
7
+ const backing = opts.backing ?? createInMemoryPageDraftStoreBacking();
8
+ const now = opts.now ?? (() => new Date().toISOString());
9
+ const authorize = opts.authorize ?? defaultAuthorize;
10
+ function cloneRef(ref) {
11
+ return { pageId: ref.pageId };
12
+ }
13
+ function roomKey(ref) {
14
+ return `${ref.pageId}`;
15
+ }
16
+ function nextEpoch() {
17
+ backing.epochCounter += 1;
18
+ return `epoch-${String(backing.epochCounter).padStart(6, "0")}`;
19
+ }
20
+ function roomFor(ref) {
21
+ const room = backing.rooms.get(roomKey(ref));
22
+ if (!room)
23
+ throw new PageDraftRoomNotFound(ref);
24
+ return room;
25
+ }
26
+ function roomForEpoch(ref) {
27
+ const room = roomFor(ref);
28
+ if (room.epoch !== ref.epoch)
29
+ throw new PageDraftEpochMismatch(ref.epoch, room.epoch);
30
+ return room;
31
+ }
32
+ function makeRoom(seed) {
33
+ const draftVersionCounter = seed.draftVersion ? versionNumber(seed.draftVersion) : 0;
34
+ return {
35
+ ref: cloneRef(seed.ref),
36
+ record: structuredClone(seed.record),
37
+ draftVersion: seed.draftVersion,
38
+ draftVersionCounter,
39
+ publishVersionCounter: 0,
40
+ seq: 0,
41
+ epoch: seed.epoch ?? nextEpoch(),
42
+ compactedThroughSeq: 0,
43
+ flushedThroughSeq: 0,
44
+ ops: [],
45
+ dedupe: new Map(),
46
+ audit: [],
47
+ snapshots: [],
48
+ };
49
+ }
50
+ for (const seed of opts.drafts ?? []) {
51
+ const key = roomKey(seed.ref);
52
+ if (!backing.rooms.has(key))
53
+ backing.rooms.set(key, makeRoom(seed));
54
+ }
55
+ async function assertAllowed(viewer, ref, action) {
56
+ const result = await authorize(viewer, ref, action);
57
+ if (result !== true)
58
+ throw new PageDraftAccessDenied(ref, action, result);
59
+ if (!backing.rooms.has(roomKey(ref)))
60
+ throw new PageDraftRoomNotFound(ref);
61
+ }
62
+ return {
63
+ async assertPageAccess(viewer, ref, action) {
64
+ const result = await authorize(viewer, ref, action);
65
+ if (result !== true)
66
+ return { ok: false, reason: result };
67
+ if (!backing.rooms.has(roomKey(ref)))
68
+ return { ok: false, reason: "page not found" };
69
+ return { ok: true };
70
+ },
71
+ async openDraftRoom(viewer, ref) {
72
+ await assertAllowed(viewer, ref, "read");
73
+ const room = roomFor(ref);
74
+ return {
75
+ ref: cloneRef(room.ref),
76
+ record: structuredClone(room.record),
77
+ draftVersion: room.draftVersion,
78
+ baseSeq: room.flushedThroughSeq,
79
+ epoch: room.epoch,
80
+ compactedThroughSeq: room.compactedThroughSeq,
81
+ };
82
+ },
83
+ async appendAcceptedOp(roomRef, op) {
84
+ const room = roomForEpoch(roomRef);
85
+ const dedupeKey = `${room.epoch}:${op.sessionId}:${op.clientOpId}`;
86
+ const existing = room.dedupe.get(dedupeKey);
87
+ if (existing) {
88
+ if (existing.opDigest !== op.opDigest) {
89
+ return {
90
+ kind: "op-replay-conflict",
91
+ existingSeq: existing.seq,
92
+ existingDigest: existing.opDigest,
93
+ attemptedDigest: op.opDigest,
94
+ };
95
+ }
96
+ return { kind: "duplicate", seq: existing.seq, opDigest: existing.opDigest };
97
+ }
98
+ const seq = room.seq + 1;
99
+ room.seq = seq;
100
+ const entry = {
101
+ ...structuredClone(op),
102
+ seq,
103
+ epoch: room.epoch,
104
+ };
105
+ room.ops.push(entry);
106
+ room.audit.push({
107
+ eventId: `${room.epoch}:${seq}`,
108
+ pageId: String(room.ref.pageId),
109
+ roomEpoch: room.epoch,
110
+ roomSeq: seq,
111
+ clientOpId: op.clientOpId,
112
+ sessionId: op.sessionId,
113
+ actor: structuredClone(op.actor),
114
+ operationKind: String(op.op?.t ?? "unknown"),
115
+ operation: structuredClone(op.auditOperation),
116
+ target: structuredClone(op.auditTarget),
117
+ beforeDigest: op.beforeDigest,
118
+ afterDigest: op.afterDigest,
119
+ acceptedAt: op.acceptedAt,
120
+ source: "collaboration",
121
+ });
122
+ room.dedupe.set(dedupeKey, { seq, opDigest: op.opDigest });
123
+ return { kind: "appended", seq };
124
+ },
125
+ async readOpsSince(roomRef, seq) {
126
+ const room = roomForEpoch(roomRef);
127
+ if (seq < room.compactedThroughSeq) {
128
+ throw new PageDraftOpsCompacted(seq, room.compactedThroughSeq);
129
+ }
130
+ return room.ops.filter((op) => op.seq > seq).map((op) => structuredClone(op));
131
+ },
132
+ async readAuditEvents(ref) {
133
+ return structuredClone(roomFor(ref).audit);
134
+ },
135
+ async flushDraft(roomRef, input) {
136
+ const room = roomForEpoch(roomRef);
137
+ if (input.expectedSeq !== room.seq)
138
+ throw new PageDraftSeqMismatch(input.expectedSeq, room.seq);
139
+ if (input.previousDraftVersion !== undefined &&
140
+ input.previousDraftVersion !== room.draftVersion) {
141
+ throw new PageDraftVersionConflict(input.previousDraftVersion, room.draftVersion);
142
+ }
143
+ room.record = structuredClone(input.record);
144
+ room.draftVersionCounter += 1;
145
+ room.draftVersion = versionToken(room.draftVersionCounter);
146
+ room.flushedThroughSeq = input.expectedSeq;
147
+ for (let index = 0; index < room.audit.length; index += 1) {
148
+ const event = room.audit[index];
149
+ if (event.roomSeq <= input.expectedSeq && !event.persistedAt) {
150
+ room.audit[index] = { ...event, persistedAt: now() };
151
+ }
152
+ }
153
+ return { draftVersion: room.draftVersion, flushedThroughSeq: input.expectedSeq };
154
+ },
155
+ async compactOps(roomRef, throughSeq) {
156
+ const room = roomForEpoch(roomRef);
157
+ if (throughSeq > room.flushedThroughSeq) {
158
+ throw new PageDraftSeqMismatch(throughSeq, room.flushedThroughSeq);
159
+ }
160
+ if (throughSeq <= room.compactedThroughSeq)
161
+ return;
162
+ room.compactedThroughSeq = throughSeq;
163
+ const retained = room.ops.filter((op) => op.seq > throughSeq);
164
+ room.ops.length = 0;
165
+ room.ops.push(...retained);
166
+ for (const [key, entry] of [...room.dedupe]) {
167
+ if (entry.seq <= throughSeq)
168
+ room.dedupe.delete(key);
169
+ }
170
+ },
171
+ async publishDraft(viewer, roomRef, input) {
172
+ await assertAllowed(viewer, roomRef, "publish");
173
+ const room = roomForEpoch(roomRef);
174
+ if (input.expectedSeq !== room.seq || input.expectedSeq !== room.flushedThroughSeq) {
175
+ throw new PageDraftSeqMismatch(input.expectedSeq, room.flushedThroughSeq);
176
+ }
177
+ if (input.expectedDraftVersion !== room.draftVersion) {
178
+ throw new PageDraftVersionConflict(input.expectedDraftVersion, room.draftVersion);
179
+ }
180
+ room.publishVersionCounter += 1;
181
+ const snapshot = {
182
+ subject: { kind: "page", pageId: room.ref.pageId },
183
+ version: versionToken(room.publishVersionCounter),
184
+ publishedAt: now(),
185
+ data: structuredClone(room.record),
186
+ };
187
+ room.snapshots.push(snapshot);
188
+ for (let index = 0; index < room.audit.length; index += 1) {
189
+ const event = room.audit[index];
190
+ if (event.roomSeq <= room.seq && !event.releaseId) {
191
+ room.audit[index] = {
192
+ ...event,
193
+ persistedAt: event.persistedAt ?? snapshot.publishedAt,
194
+ releaseId: String(snapshot.version),
195
+ publishedAt: snapshot.publishedAt,
196
+ };
197
+ }
198
+ }
199
+ room.compactedThroughSeq = room.seq;
200
+ room.ops.length = 0;
201
+ room.dedupe.clear();
202
+ room.epoch = nextEpoch();
203
+ return { snapshot: structuredClone(snapshot), newEpoch: room.epoch };
204
+ },
205
+ };
206
+ }
207
+ function defaultAuthorize(viewer, _ref, _action) {
208
+ // battery-editor is not the access authority (decision 012, invariant D4):
209
+ // no identity-kind gate. The CMS authorizes real draft operations via
210
+ // the delegated session. We keep ONLY the behavioral D1 floor — a sessionless
211
+ // viewer never reaches a draft room (types forbid it; runtime attackers do
212
+ // not respect types).
213
+ if (viewer.session === undefined)
214
+ return "session required";
215
+ return true;
216
+ }
217
+ //# sourceMappingURL=in-memory-page-draft-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-page-draft-store.js","sourceRoot":"","sources":["../../src/collab/in-memory-page-draft-store.ts"],"names":[],"mappings":"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"}