@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.
- package/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/cache/dependency-graph.d.ts +18 -0
- package/dist/cache/dependency-graph.d.ts.map +1 -0
- package/dist/cache/dependency-graph.js +30 -0
- package/dist/cache/dependency-graph.js.map +1 -0
- package/dist/cache/render-cache.d.ts +39 -0
- package/dist/cache/render-cache.d.ts.map +1 -0
- package/dist/cache/render-cache.js +60 -0
- package/dist/cache/render-cache.js.map +1 -0
- package/dist/client/mount.d.ts +9 -0
- package/dist/client/mount.d.ts.map +1 -0
- package/dist/client/mount.js +69 -0
- package/dist/client/mount.js.map +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/collab/in-memory-page-draft-store.d.ts +44 -0
- package/dist/collab/in-memory-page-draft-store.d.ts.map +1 -0
- package/dist/collab/in-memory-page-draft-store.js +217 -0
- package/dist/collab/in-memory-page-draft-store.js.map +1 -0
- package/dist/collab/page-doc-page-draft-store.d.ts +41 -0
- package/dist/collab/page-doc-page-draft-store.d.ts.map +1 -0
- package/dist/collab/page-doc-page-draft-store.js +328 -0
- package/dist/collab/page-doc-page-draft-store.js.map +1 -0
- package/dist/collab/page-draft-store.d.ts +152 -0
- package/dist/collab/page-draft-store.d.ts.map +1 -0
- package/dist/collab/page-draft-store.js +61 -0
- package/dist/collab/page-draft-store.js.map +1 -0
- package/dist/content-source/conformance/cases-delegation.d.ts +3 -0
- package/dist/content-source/conformance/cases-delegation.d.ts.map +1 -0
- package/dist/content-source/conformance/cases-delegation.js +55 -0
- package/dist/content-source/conformance/cases-delegation.js.map +1 -0
- package/dist/content-source/conformance/cases-fidelity.d.ts +3 -0
- package/dist/content-source/conformance/cases-fidelity.d.ts.map +1 -0
- package/dist/content-source/conformance/cases-fidelity.js +67 -0
- package/dist/content-source/conformance/cases-fidelity.js.map +1 -0
- package/dist/content-source/conformance/cases-isolation.d.ts +3 -0
- package/dist/content-source/conformance/cases-isolation.d.ts.map +1 -0
- package/dist/content-source/conformance/cases-isolation.js +57 -0
- package/dist/content-source/conformance/cases-isolation.js.map +1 -0
- package/dist/content-source/conformance/cases-permissions.d.ts +3 -0
- package/dist/content-source/conformance/cases-permissions.d.ts.map +1 -0
- package/dist/content-source/conformance/cases-permissions.js +50 -0
- package/dist/content-source/conformance/cases-permissions.js.map +1 -0
- package/dist/content-source/conformance/cases-reads.d.ts +3 -0
- package/dist/content-source/conformance/cases-reads.d.ts.map +1 -0
- package/dist/content-source/conformance/cases-reads.js +216 -0
- package/dist/content-source/conformance/cases-reads.js.map +1 -0
- package/dist/content-source/conformance/cases-writes.d.ts +11 -0
- package/dist/content-source/conformance/cases-writes.d.ts.map +1 -0
- package/dist/content-source/conformance/cases-writes.js +73 -0
- package/dist/content-source/conformance/cases-writes.js.map +1 -0
- package/dist/content-source/conformance/session-required.d.ts +10 -0
- package/dist/content-source/conformance/session-required.d.ts.map +1 -0
- package/dist/content-source/conformance/session-required.js +11 -0
- package/dist/content-source/conformance/session-required.js.map +1 -0
- package/dist/content-source/conformance/sessionless-viewer.d.ts +12 -0
- package/dist/content-source/conformance/sessionless-viewer.d.ts.map +1 -0
- package/dist/content-source/conformance/sessionless-viewer.js +5 -0
- package/dist/content-source/conformance/sessionless-viewer.js.map +1 -0
- package/dist/content-source/conformance/target.d.ts +111 -0
- package/dist/content-source/conformance/target.d.ts.map +1 -0
- package/dist/content-source/conformance/target.js +2 -0
- package/dist/content-source/conformance/target.js.map +1 -0
- package/dist/content-source/conformance-kit.d.ts +4 -0
- package/dist/content-source/conformance-kit.d.ts.map +1 -0
- package/dist/content-source/conformance-kit.js +41 -0
- package/dist/content-source/conformance-kit.js.map +1 -0
- package/dist/content-source/content-source.d.ts +153 -0
- package/dist/content-source/content-source.d.ts.map +1 -0
- package/dist/content-source/content-source.js +12 -0
- package/dist/content-source/content-source.js.map +1 -0
- package/dist/content-source/permissions.d.ts +46 -0
- package/dist/content-source/permissions.d.ts.map +1 -0
- package/dist/content-source/permissions.js +33 -0
- package/dist/content-source/permissions.js.map +1 -0
- package/dist/content-source/viewer.d.ts +48 -0
- package/dist/content-source/viewer.d.ts.map +1 -0
- package/dist/content-source/viewer.js +24 -0
- package/dist/content-source/viewer.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/cms-lifecycle-store.d.ts +72 -0
- package/dist/lifecycle/cms-lifecycle-store.d.ts.map +1 -0
- package/dist/lifecycle/cms-lifecycle-store.js +150 -0
- package/dist/lifecycle/cms-lifecycle-store.js.map +1 -0
- package/dist/lifecycle/lifecycle-store.d.ts +35 -0
- package/dist/lifecycle/lifecycle-store.d.ts.map +1 -0
- package/dist/lifecycle/lifecycle-store.js +69 -0
- package/dist/lifecycle/lifecycle-store.js.map +1 -0
- package/dist/lifecycle/version-token.d.ts +30 -0
- package/dist/lifecycle/version-token.d.ts.map +1 -0
- package/dist/lifecycle/version-token.js +22 -0
- package/dist/lifecycle/version-token.js.map +1 -0
- package/dist/messages.d.ts +26 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +31 -0
- package/dist/messages.js.map +1 -0
- package/dist/render/image-usage-preview.d.ts +19 -0
- package/dist/render/image-usage-preview.d.ts.map +1 -0
- package/dist/render/image-usage-preview.js +25 -0
- package/dist/render/image-usage-preview.js.map +1 -0
- package/dist/render/image-usage.d.ts +32 -0
- package/dist/render/image-usage.d.ts.map +1 -0
- package/dist/render/image-usage.js +116 -0
- package/dist/render/image-usage.js.map +1 -0
- package/dist/render/page-renderer.d.ts +44 -0
- package/dist/render/page-renderer.d.ts.map +1 -0
- package/dist/render/page-renderer.js +115 -0
- package/dist/render/page-renderer.js.map +1 -0
- package/dist/resolver/data-loader.d.ts +49 -0
- package/dist/resolver/data-loader.d.ts.map +1 -0
- package/dist/resolver/data-loader.js +121 -0
- package/dist/resolver/data-loader.js.map +1 -0
- package/dist/resolver/resolution-service.d.ts +49 -0
- package/dist/resolver/resolution-service.d.ts.map +1 -0
- package/dist/resolver/resolution-service.js +193 -0
- package/dist/resolver/resolution-service.js.map +1 -0
- package/dist/routing/route-resolver.d.ts +73 -0
- package/dist/routing/route-resolver.d.ts.map +1 -0
- package/dist/routing/route-resolver.js +114 -0
- package/dist/routing/route-resolver.js.map +1 -0
- package/dist/scope-engine/build-stack.d.ts +49 -0
- package/dist/scope-engine/build-stack.d.ts.map +1 -0
- package/dist/scope-engine/build-stack.js +77 -0
- package/dist/scope-engine/build-stack.js.map +1 -0
- package/dist/scope-engine/repeater-query.d.ts +9 -0
- package/dist/scope-engine/repeater-query.d.ts.map +1 -0
- package/dist/scope-engine/repeater-query.js +39 -0
- package/dist/scope-engine/repeater-query.js.map +1 -0
- package/dist/seo/metadata.d.ts +30 -0
- package/dist/seo/metadata.d.ts.map +1 -0
- package/dist/seo/metadata.js +118 -0
- package/dist/seo/metadata.js.map +1 -0
- package/dist/seo/sitemap.d.ts +11 -0
- package/dist/seo/sitemap.d.ts.map +1 -0
- package/dist/seo/sitemap.js +20 -0
- package/dist/seo/sitemap.js.map +1 -0
- package/dist/write/write-path.d.ts +63 -0
- package/dist/write/write-path.d.ts.map +1 -0
- package/dist/write/write-path.js +139 -0
- package/dist/write/write-path.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Viewer + per-field access (decision 004 §1.2; invariants D/E/P).
|
|
3
|
+
*
|
|
4
|
+
* Decision 012 (CMS identity delegation): battery-editor owns no access
|
|
5
|
+
* enforcement. A Viewer is just the CMS delivery context plus a `Locale`.
|
|
6
|
+
* Authoring uses an opaque delegated `CmsSession`; public delivery is an
|
|
7
|
+
* explicit sessionless mode. Adapters never parse identity, scope data by the
|
|
8
|
+
* caller's organization, or reason about roles (invariant D4). The legacy
|
|
9
|
+
* identity/scoping fields are removed (Phase 2/3).
|
|
10
|
+
*/
|
|
11
|
+
import type { Locale } from "@batterai/bind-core";
|
|
12
|
+
declare const SESSION_TOKEN: unique symbol;
|
|
13
|
+
/**
|
|
14
|
+
* Opaque end-user auth context the CMS understands. Battery-editor never
|
|
15
|
+
* parses it — only adapters read the bearer token, via `sessionBearerToken`,
|
|
16
|
+
* to authenticate CMS calls on the end user's behalf.
|
|
17
|
+
*/
|
|
18
|
+
export interface CmsSession {
|
|
19
|
+
readonly [SESSION_TOKEN]: string;
|
|
20
|
+
}
|
|
21
|
+
/** Build a `CmsSession` from a delegated bearer token. Throws on empty/blank input. */
|
|
22
|
+
export declare function cmsSessionFromToken(bearerToken: string): CmsSession;
|
|
23
|
+
/** Adapter-only accessor: extract the bearer token to authenticate a CMS call. */
|
|
24
|
+
export declare function sessionBearerToken(session: CmsSession): string;
|
|
25
|
+
export interface Viewer {
|
|
26
|
+
/** Opaque end-user auth context (delegated CMS session). Adapters forward it;
|
|
27
|
+
* battery-editor never parses it. Present for authoring/preview only. */
|
|
28
|
+
readonly session?: CmsSession;
|
|
29
|
+
/** Explicit public-delivery marker. A bare missing session is still invalid
|
|
30
|
+
* for delegated adapters; this opt-in mode is the only anonymous read path. */
|
|
31
|
+
readonly delivery?: "public";
|
|
32
|
+
readonly locale: Locale;
|
|
33
|
+
}
|
|
34
|
+
/** Build a `Viewer` from a delegated CMS session and a locale. */
|
|
35
|
+
export declare function delegatedViewer(session: CmsSession, locale: Locale): Viewer;
|
|
36
|
+
/** Build the genuine anonymous public-delivery viewer. */
|
|
37
|
+
export declare function publicDeliveryViewer(locale: Locale): Viewer;
|
|
38
|
+
export declare function isPublicDeliveryViewer(viewer: Viewer): boolean;
|
|
39
|
+
/** Per-field read/write permission. `*Reason` REQUIRED semantics on false —
|
|
40
|
+
* invariant P demands non-editable-with-reason, never bare unavailability. */
|
|
41
|
+
export interface FieldAccess {
|
|
42
|
+
readonly canRead: boolean;
|
|
43
|
+
readonly canWrite: boolean;
|
|
44
|
+
readonly readReason?: string;
|
|
45
|
+
readonly writeReason?: string;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=viewer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewer.d.ts","sourceRoot":"","sources":["../../src/content-source/viewer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,QAAA,MAAM,aAAa,eAAkC,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CAKnE;AAED,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAE9D;AAED,MAAM,WAAW,MAAM;IACrB;6EACyE;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAC9B;mFAC+E;IAC/E,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,0DAA0D;AAC1D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;8EAC8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const SESSION_TOKEN = Symbol("cmsSessionBearerToken");
|
|
2
|
+
/** Build a `CmsSession` from a delegated bearer token. Throws on empty/blank input. */
|
|
3
|
+
export function cmsSessionFromToken(bearerToken) {
|
|
4
|
+
if (bearerToken.trim().length === 0) {
|
|
5
|
+
throw new Error("cmsSessionFromToken: bearerToken must not be empty or blank");
|
|
6
|
+
}
|
|
7
|
+
return { [SESSION_TOKEN]: bearerToken };
|
|
8
|
+
}
|
|
9
|
+
/** Adapter-only accessor: extract the bearer token to authenticate a CMS call. */
|
|
10
|
+
export function sessionBearerToken(session) {
|
|
11
|
+
return session[SESSION_TOKEN];
|
|
12
|
+
}
|
|
13
|
+
/** Build a `Viewer` from a delegated CMS session and a locale. */
|
|
14
|
+
export function delegatedViewer(session, locale) {
|
|
15
|
+
return { session, locale };
|
|
16
|
+
}
|
|
17
|
+
/** Build the genuine anonymous public-delivery viewer. */
|
|
18
|
+
export function publicDeliveryViewer(locale) {
|
|
19
|
+
return { delivery: "public", locale };
|
|
20
|
+
}
|
|
21
|
+
export function isPublicDeliveryViewer(viewer) {
|
|
22
|
+
return viewer.delivery === "public" && viewer.session === undefined;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=viewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewer.js","sourceRoot":"","sources":["../../src/content-source/viewer.ts"],"names":[],"mappings":"AAYA,MAAM,aAAa,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC;AAWtD,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,EAAE,CAAC,aAAa,CAAC,EAAE,WAAW,EAAE,CAAC;AAC1C,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,kBAAkB,CAAC,OAAmB;IACpD,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC;AAChC,CAAC;AAYD,kEAAkE;AAClE,MAAM,UAAU,eAAe,CAAC,OAAmB,EAAE,MAAc;IACjE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAC7B,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC;AACtE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @batterai/host — the runtime (plan §4 L2; decision 004).
|
|
3
|
+
* Imports bind-core/contract/block-sdk/media only (depcruise-enforced).
|
|
4
|
+
* DOM-free except src/client/** (the one browser-mount seam).
|
|
5
|
+
*/
|
|
6
|
+
export * from "./content-source/viewer";
|
|
7
|
+
export * from "./content-source/content-source";
|
|
8
|
+
export * from "./content-source/permissions";
|
|
9
|
+
export * from "./content-source/conformance-kit";
|
|
10
|
+
export * from "./lifecycle/version-token";
|
|
11
|
+
export * from "./lifecycle/lifecycle-store";
|
|
12
|
+
export * from "./lifecycle/cms-lifecycle-store";
|
|
13
|
+
export * from "./cache/dependency-graph";
|
|
14
|
+
export * from "./cache/render-cache";
|
|
15
|
+
export * from "./messages";
|
|
16
|
+
export * from "./scope-engine/build-stack";
|
|
17
|
+
export * from "./scope-engine/repeater-query";
|
|
18
|
+
export * from "./resolver/data-loader";
|
|
19
|
+
export * from "./resolver/resolution-service";
|
|
20
|
+
export * from "./write/write-path";
|
|
21
|
+
export * from "./routing/route-resolver";
|
|
22
|
+
export * from "./seo/metadata";
|
|
23
|
+
export * from "./seo/sitemap";
|
|
24
|
+
export * from "./render/page-renderer";
|
|
25
|
+
export * from "./render/image-usage";
|
|
26
|
+
export * from "./render/image-usage-preview";
|
|
27
|
+
export * from "./collab/page-draft-store";
|
|
28
|
+
export * from "./collab/in-memory-page-draft-store";
|
|
29
|
+
export * from "./collab/page-doc-page-draft-store";
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qCAAqC,CAAC;AACpD,cAAc,oCAAoC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @batterai/host — the runtime (plan §4 L2; decision 004).
|
|
3
|
+
* Imports bind-core/contract/block-sdk/media only (depcruise-enforced).
|
|
4
|
+
* DOM-free except src/client/** (the one browser-mount seam).
|
|
5
|
+
*/
|
|
6
|
+
export * from "./content-source/viewer.js";
|
|
7
|
+
export * from "./content-source/content-source.js";
|
|
8
|
+
export * from "./content-source/permissions.js";
|
|
9
|
+
export * from "./content-source/conformance-kit.js";
|
|
10
|
+
export * from "./lifecycle/version-token.js";
|
|
11
|
+
export * from "./lifecycle/lifecycle-store.js";
|
|
12
|
+
export * from "./lifecycle/cms-lifecycle-store.js";
|
|
13
|
+
export * from "./cache/dependency-graph.js";
|
|
14
|
+
export * from "./cache/render-cache.js";
|
|
15
|
+
export * from "./messages.js";
|
|
16
|
+
export * from "./scope-engine/build-stack.js";
|
|
17
|
+
export * from "./scope-engine/repeater-query.js";
|
|
18
|
+
export * from "./resolver/data-loader.js";
|
|
19
|
+
export * from "./resolver/resolution-service.js";
|
|
20
|
+
export * from "./write/write-path.js";
|
|
21
|
+
export * from "./routing/route-resolver.js";
|
|
22
|
+
export * from "./seo/metadata.js";
|
|
23
|
+
export * from "./seo/sitemap.js";
|
|
24
|
+
export * from "./render/page-renderer.js";
|
|
25
|
+
export * from "./render/image-usage.js";
|
|
26
|
+
export * from "./render/image-usage-preview.js";
|
|
27
|
+
export * from "./collab/page-draft-store.js";
|
|
28
|
+
export * from "./collab/in-memory-page-draft-store.js";
|
|
29
|
+
export * from "./collab/page-doc-page-draft-store.js";
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qCAAqC,CAAC;AACpD,cAAc,oCAAoC,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CMS-backed lifecycle store (decision 009). Same contract + invariants as
|
|
3
|
+
* createInMemoryLifecycleStore (lifecycle-store.ts): drafts are mutable working
|
|
4
|
+
* copies, publishes are immutable snapshots, rollback is a NEW publish of old
|
|
5
|
+
* data, version tokens are monotonic. The difference: state lives in the CMS,
|
|
6
|
+
* reached through a small PageDocPort, so pages survive restarts
|
|
7
|
+
* — the durable home the in-memory store never was.
|
|
8
|
+
*
|
|
9
|
+
* Two deliberate deltas from the in-memory store (decision 009):
|
|
10
|
+
* - Version derivation is PER-SUBJECT (max persisted token + 1), not one
|
|
11
|
+
* process-global counter. The documented invariant is "monotonic per
|
|
12
|
+
* subject" (version-token.ts). The global counter's cross-subject
|
|
13
|
+
* uniqueness was incidental and no consumer relies on it: the render-cache
|
|
14
|
+
* key joins per-subject tokens, and CAS compares within a single subject.
|
|
15
|
+
* - It clones at BOTH boundaries — never mutating a doc it read from the port,
|
|
16
|
+
* and cloning every value it returns — because a port MAY hand back a shared
|
|
17
|
+
* instance (an in-memory fake does). The in-memory store could clone on
|
|
18
|
+
* write only; this one cannot.
|
|
19
|
+
*
|
|
20
|
+
* Every mutator is a read-modify-write with optimistic concurrency through
|
|
21
|
+
* PageDocPort.write(expectedDocVersion). A concrete CMS port must enforce that
|
|
22
|
+
* compare at the storage boundary; callers retry bounded conflicts here.
|
|
23
|
+
*/
|
|
24
|
+
import { type LifecycleEvents, type LifecycleStore } from "./lifecycle-store";
|
|
25
|
+
import { type ContentVersionToken, type PublishedSnapshot } from "./version-token";
|
|
26
|
+
/** The full persisted row for one subject: the mutable draft (if any) plus the
|
|
27
|
+
* immutable, append-only published history. Stored as ONE JSON document in the
|
|
28
|
+
* CMS (a Payload `document` json field / a Strapi `document` json attribute). */
|
|
29
|
+
export interface StoredPageDoc<T> {
|
|
30
|
+
readonly docVersion: number;
|
|
31
|
+
readonly draft?: T;
|
|
32
|
+
readonly snapshots: readonly PublishedSnapshot<T>[];
|
|
33
|
+
}
|
|
34
|
+
export interface PageDocWriteOptions {
|
|
35
|
+
readonly expectedDocVersion: number;
|
|
36
|
+
}
|
|
37
|
+
export declare class PageDocConflict extends Error {
|
|
38
|
+
readonly key: string;
|
|
39
|
+
readonly expectedDocVersion: number;
|
|
40
|
+
readonly actualDocVersion: number | undefined;
|
|
41
|
+
constructor(key: string, expectedDocVersion: number, actualDocVersion: number | undefined);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Minimal, CMS-agnostic persistence port. A concrete port is built for exactly
|
|
45
|
+
* one CMS instance; getPublished (the public read) has no viewer, so the port
|
|
46
|
+
* is bound to its backing store at construction. Contract:
|
|
47
|
+
* - read(key): the persisted doc or undefined. Caller-owned for reading; the
|
|
48
|
+
* store never mutates it in place.
|
|
49
|
+
* - readVersion(key): optional fast path for render-cache version checks.
|
|
50
|
+
* Concrete CMS ports should read a projected latest-version column/field,
|
|
51
|
+
* not hydrate the full JSON page document just to decide cache freshness.
|
|
52
|
+
* - write(key, doc, { expectedDocVersion }): upsert with compare-and-swap.
|
|
53
|
+
* Missing rows have version 0; successful writes persist doc.docVersion.
|
|
54
|
+
* The port persists a DEEP COPY (real CMS ports serialize to JSON; an
|
|
55
|
+
* in-memory fake MUST structuredClone) so the store can keep building fresh
|
|
56
|
+
* docs without the port aliasing them.
|
|
57
|
+
*/
|
|
58
|
+
export interface PageDocPort<T> {
|
|
59
|
+
read(key: string): Promise<StoredPageDoc<T> | undefined>;
|
|
60
|
+
readVersion?(key: string): Promise<ContentVersionToken | undefined>;
|
|
61
|
+
write(key: string, doc: StoredPageDoc<T>, options: PageDocWriteOptions): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
export interface CmsLifecycleStoreDeps<T> {
|
|
64
|
+
readonly port: PageDocPort<T>;
|
|
65
|
+
readonly events?: Partial<LifecycleEvents>;
|
|
66
|
+
readonly clock?: () => string;
|
|
67
|
+
/** Maximum published snapshots retained per subject. Defaults to 50 to keep
|
|
68
|
+
* CMS JSON documents bounded while preserving recent rollback history. */
|
|
69
|
+
readonly maxSnapshots?: number;
|
|
70
|
+
}
|
|
71
|
+
export declare function createCmsLifecycleStore<T>(deps: CmsLifecycleStoreDeps<T>): LifecycleStore<T>;
|
|
72
|
+
//# sourceMappingURL=cms-lifecycle-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cms-lifecycle-store.d.ts","sourceRoot":"","sources":["../../src/lifecycle/cms-lifecycle-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAC;AAEzB;;iFAEiF;AACjF,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACnB,QAAQ,CAAC,SAAS,EAAE,SAAS,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED,qBAAa,eAAgB,SAAQ,KAAK;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM;IAAE,QAAQ,CAAC,kBAAkB,EAAE,MAAM;IAAE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS;gBAA/F,GAAG,EAAE,MAAM,EAAW,kBAAkB,EAAE,MAAM,EAAW,gBAAgB,EAAE,MAAM,GAAG,SAAS;CAIrH;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACzD,WAAW,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC;IACpE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxF;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B;8EAC0E;IAC1E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAsH5F"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CMS-backed lifecycle store (decision 009). Same contract + invariants as
|
|
3
|
+
* createInMemoryLifecycleStore (lifecycle-store.ts): drafts are mutable working
|
|
4
|
+
* copies, publishes are immutable snapshots, rollback is a NEW publish of old
|
|
5
|
+
* data, version tokens are monotonic. The difference: state lives in the CMS,
|
|
6
|
+
* reached through a small PageDocPort, so pages survive restarts
|
|
7
|
+
* — the durable home the in-memory store never was.
|
|
8
|
+
*
|
|
9
|
+
* Two deliberate deltas from the in-memory store (decision 009):
|
|
10
|
+
* - Version derivation is PER-SUBJECT (max persisted token + 1), not one
|
|
11
|
+
* process-global counter. The documented invariant is "monotonic per
|
|
12
|
+
* subject" (version-token.ts). The global counter's cross-subject
|
|
13
|
+
* uniqueness was incidental and no consumer relies on it: the render-cache
|
|
14
|
+
* key joins per-subject tokens, and CAS compares within a single subject.
|
|
15
|
+
* - It clones at BOTH boundaries — never mutating a doc it read from the port,
|
|
16
|
+
* and cloning every value it returns — because a port MAY hand back a shared
|
|
17
|
+
* instance (an in-memory fake does). The in-memory store could clone on
|
|
18
|
+
* write only; this one cannot.
|
|
19
|
+
*
|
|
20
|
+
* Every mutator is a read-modify-write with optimistic concurrency through
|
|
21
|
+
* PageDocPort.write(expectedDocVersion). A concrete CMS port must enforce that
|
|
22
|
+
* compare at the storage boundary; callers retry bounded conflicts here.
|
|
23
|
+
*/
|
|
24
|
+
import { NothingToPublish, RollbackTargetMissing, } from "./lifecycle-store.js";
|
|
25
|
+
import { subjectKey, versionNumber, versionToken, } from "./version-token.js";
|
|
26
|
+
export class PageDocConflict extends Error {
|
|
27
|
+
key;
|
|
28
|
+
expectedDocVersion;
|
|
29
|
+
actualDocVersion;
|
|
30
|
+
constructor(key, expectedDocVersion, actualDocVersion) {
|
|
31
|
+
super(`PageDocConflict: ${key} expected docVersion ${expectedDocVersion}, got ${actualDocVersion ?? "missing"}`);
|
|
32
|
+
this.key = key;
|
|
33
|
+
this.expectedDocVersion = expectedDocVersion;
|
|
34
|
+
this.actualDocVersion = actualDocVersion;
|
|
35
|
+
this.name = "PageDocConflict";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function createCmsLifecycleStore(deps) {
|
|
39
|
+
const { port, events } = deps;
|
|
40
|
+
const clock = deps.clock ?? (() => new Date().toISOString());
|
|
41
|
+
const maxSnapshots = deps.maxSnapshots ?? 50;
|
|
42
|
+
function load(subject) {
|
|
43
|
+
return port.read(subjectKey(subject));
|
|
44
|
+
}
|
|
45
|
+
function normalize(doc) {
|
|
46
|
+
if (!doc)
|
|
47
|
+
return undefined;
|
|
48
|
+
return {
|
|
49
|
+
...doc,
|
|
50
|
+
docVersion: Number.isInteger(doc.docVersion) && doc.docVersion >= 0 ? doc.docVersion : 0,
|
|
51
|
+
snapshots: doc.snapshots ?? [],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function nextVersion(snapshots) {
|
|
55
|
+
// PER-SUBJECT max+1 (empty history => 1, matching the in-memory store's
|
|
56
|
+
// first token). Parse via versionNumber so the zero-pad width lives in one
|
|
57
|
+
// place; snapshots are appended in order so any read order is fine.
|
|
58
|
+
const maxN = snapshots.length ? Math.max(...snapshots.map((s) => versionNumber(s.version))) : 0;
|
|
59
|
+
return versionToken(maxN + 1);
|
|
60
|
+
}
|
|
61
|
+
function pruneSnapshots(snapshots) {
|
|
62
|
+
if (maxSnapshots <= 0)
|
|
63
|
+
return [];
|
|
64
|
+
return snapshots.slice(-maxSnapshots);
|
|
65
|
+
}
|
|
66
|
+
/** publish/rollback share this: append a NEW immutable snapshot of `data`,
|
|
67
|
+
* keep the existing draft, persist, and fire the invalidation hook. Never
|
|
68
|
+
* mutates `cur` (it may be the port's retained instance). */
|
|
69
|
+
async function mutate(subject, update) {
|
|
70
|
+
const key = subjectKey(subject);
|
|
71
|
+
let lastConflict;
|
|
72
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
73
|
+
const cur = normalize(await port.read(key));
|
|
74
|
+
const expectedDocVersion = cur?.docVersion ?? 0;
|
|
75
|
+
const next = update(cur);
|
|
76
|
+
try {
|
|
77
|
+
await port.write(key, { ...next.doc, docVersion: expectedDocVersion + 1 }, { expectedDocVersion });
|
|
78
|
+
return next.result;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error instanceof PageDocConflict) {
|
|
82
|
+
lastConflict = error;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw lastConflict ?? new PageDocConflict(key, -1, undefined);
|
|
89
|
+
}
|
|
90
|
+
function appended(subject, cur, data) {
|
|
91
|
+
const snap = {
|
|
92
|
+
subject,
|
|
93
|
+
version: nextVersion(cur.snapshots),
|
|
94
|
+
publishedAt: clock(),
|
|
95
|
+
data: structuredClone(data),
|
|
96
|
+
};
|
|
97
|
+
return { doc: { draft: cur.draft, snapshots: pruneSnapshots([...cur.snapshots, snap]) }, snapshot: snap };
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
async getDraft(subject) {
|
|
101
|
+
const doc = normalize(await load(subject));
|
|
102
|
+
return doc?.draft === undefined ? undefined : structuredClone(doc.draft);
|
|
103
|
+
},
|
|
104
|
+
async saveDraft(subject, data) {
|
|
105
|
+
await mutate(subject, (cur) => ({
|
|
106
|
+
doc: {
|
|
107
|
+
draft: structuredClone(data),
|
|
108
|
+
snapshots: cur?.snapshots ?? [],
|
|
109
|
+
},
|
|
110
|
+
result: undefined,
|
|
111
|
+
}));
|
|
112
|
+
},
|
|
113
|
+
async getPublished(subject) {
|
|
114
|
+
const snap = normalize(await load(subject))?.snapshots.at(-1);
|
|
115
|
+
return snap ? structuredClone(snap) : undefined;
|
|
116
|
+
},
|
|
117
|
+
async publish(subject) {
|
|
118
|
+
const snap = await mutate(subject, (cur) => {
|
|
119
|
+
if (!cur || cur.draft === undefined)
|
|
120
|
+
throw new NothingToPublish(subject);
|
|
121
|
+
const { doc, snapshot } = appended(subject, cur, cur.draft);
|
|
122
|
+
return { doc, result: snapshot };
|
|
123
|
+
});
|
|
124
|
+
events?.onVersionBump?.(subject, snap.version);
|
|
125
|
+
return structuredClone(snap);
|
|
126
|
+
},
|
|
127
|
+
async rollback(subject, toVersion) {
|
|
128
|
+
const snap = await mutate(subject, (cur) => {
|
|
129
|
+
const target = cur?.snapshots.find((s) => s.version === toVersion);
|
|
130
|
+
if (!cur || !target)
|
|
131
|
+
throw new RollbackTargetMissing(subject, toVersion);
|
|
132
|
+
const { doc, snapshot } = appended(subject, cur, target.data);
|
|
133
|
+
return { doc, result: snapshot };
|
|
134
|
+
});
|
|
135
|
+
events?.onVersionBump?.(subject, snap.version);
|
|
136
|
+
return structuredClone(snap);
|
|
137
|
+
},
|
|
138
|
+
async listSnapshots(subject) {
|
|
139
|
+
const doc = normalize(await load(subject));
|
|
140
|
+
return (doc?.snapshots ?? []).map((s) => structuredClone(s));
|
|
141
|
+
},
|
|
142
|
+
async currentVersion(subject) {
|
|
143
|
+
const key = subjectKey(subject);
|
|
144
|
+
if (port.readVersion)
|
|
145
|
+
return port.readVersion(key);
|
|
146
|
+
return normalize(await port.read(key))?.snapshots.at(-1)?.version;
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=cms-lifecycle-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cms-lifecycle-store.js","sourceRoot":"","sources":["../../src/lifecycle/cms-lifecycle-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-lifecycle substrate (decision 004 §4.2; invariant F + P precondition).
|
|
3
|
+
* Drafts are mutable working copies; ONLY publishes are immutable snapshots.
|
|
4
|
+
* Rollback is a publish of old data under a NEW token — history is never edited.
|
|
5
|
+
* Ships with an in-memory implementation (the Phase-5 gate's store; adapters
|
|
6
|
+
* may back it natively per capabilities.draftPublish in Phase 6/6.5).
|
|
7
|
+
*/
|
|
8
|
+
import type { Viewer } from "../content-source/viewer";
|
|
9
|
+
import { type ContentVersionToken, type PublishedSnapshot, type VersionedSubject } from "./version-token";
|
|
10
|
+
export declare class RollbackTargetMissing extends Error {
|
|
11
|
+
readonly subject: VersionedSubject;
|
|
12
|
+
readonly toVersion: ContentVersionToken;
|
|
13
|
+
constructor(subject: VersionedSubject, toVersion: ContentVersionToken);
|
|
14
|
+
}
|
|
15
|
+
export declare class NothingToPublish extends Error {
|
|
16
|
+
readonly subject: VersionedSubject;
|
|
17
|
+
constructor(subject: VersionedSubject);
|
|
18
|
+
}
|
|
19
|
+
export interface LifecycleStore<T> {
|
|
20
|
+
getDraft(subject: VersionedSubject, viewer: Viewer): Promise<T | undefined>;
|
|
21
|
+
saveDraft(subject: VersionedSubject, data: T, viewer: Viewer): Promise<void>;
|
|
22
|
+
/** The ONLY published read for public requests. undefined = 404, never an
|
|
23
|
+
* empty render (invariant F). */
|
|
24
|
+
getPublished(subject: VersionedSubject): Promise<PublishedSnapshot<T> | undefined>;
|
|
25
|
+
publish(subject: VersionedSubject, viewer: Viewer): Promise<PublishedSnapshot<T>>;
|
|
26
|
+
rollback(subject: VersionedSubject, toVersion: ContentVersionToken, viewer: Viewer): Promise<PublishedSnapshot<T>>;
|
|
27
|
+
listSnapshots(subject: VersionedSubject): Promise<readonly PublishedSnapshot<T>[]>;
|
|
28
|
+
currentVersion(subject: VersionedSubject): Promise<ContentVersionToken | undefined>;
|
|
29
|
+
}
|
|
30
|
+
export interface LifecycleEvents {
|
|
31
|
+
/** Fired on publish AND rollback — the dependency graph's invalidation hook. */
|
|
32
|
+
onVersionBump(subject: VersionedSubject, version: ContentVersionToken): void;
|
|
33
|
+
}
|
|
34
|
+
export declare function createInMemoryLifecycleStore<T>(events?: Partial<LifecycleEvents>, clock?: () => string): LifecycleStore<T>;
|
|
35
|
+
//# sourceMappingURL=lifecycle-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-store.d.ts","sourceRoot":"","sources":["../../src/lifecycle/lifecycle-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAC;AAEzB,qBAAa,qBAAsB,SAAQ,KAAK;IAClC,QAAQ,CAAC,OAAO,EAAE,gBAAgB;IAAE,QAAQ,CAAC,SAAS,EAAE,mBAAmB;gBAAlE,OAAO,EAAE,gBAAgB,EAAW,SAAS,EAAE,mBAAmB;CAIxF;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IAC7B,QAAQ,CAAC,OAAO,EAAE,gBAAgB;gBAAzB,OAAO,EAAE,gBAAgB;CAI/C;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5E,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;qCACiC;IACjC,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACnF,OAAO,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,QAAQ,CACN,OAAO,EAAE,gBAAgB,EACzB,SAAS,EAAE,mBAAmB,EAC9B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACnF,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,aAAa,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;CAC9E;AAED,wBAAgB,4BAA4B,CAAC,CAAC,EAC5C,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACjC,KAAK,GAAE,MAAM,MAAuC,GACnD,cAAc,CAAC,CAAC,CAAC,CAgDnB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { subjectKey, versionToken, } from "./version-token.js";
|
|
2
|
+
export class RollbackTargetMissing extends Error {
|
|
3
|
+
subject;
|
|
4
|
+
toVersion;
|
|
5
|
+
constructor(subject, toVersion) {
|
|
6
|
+
super(`rollback: no snapshot ${toVersion} for ${subjectKey(subject)}`);
|
|
7
|
+
this.subject = subject;
|
|
8
|
+
this.toVersion = toVersion;
|
|
9
|
+
this.name = "RollbackTargetMissing";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class NothingToPublish extends Error {
|
|
13
|
+
subject;
|
|
14
|
+
constructor(subject) {
|
|
15
|
+
super(`publish: no draft for ${subjectKey(subject)}`);
|
|
16
|
+
this.subject = subject;
|
|
17
|
+
this.name = "NothingToPublish";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function createInMemoryLifecycleStore(events, clock = () => new Date().toISOString()) {
|
|
21
|
+
const drafts = new Map();
|
|
22
|
+
const snapshots = new Map();
|
|
23
|
+
let counter = 0;
|
|
24
|
+
function bump(subject, data) {
|
|
25
|
+
counter += 1;
|
|
26
|
+
const snap = {
|
|
27
|
+
subject,
|
|
28
|
+
version: versionToken(counter),
|
|
29
|
+
publishedAt: clock(),
|
|
30
|
+
data: structuredClone(data), // immutability: never share the draft object
|
|
31
|
+
};
|
|
32
|
+
const list = snapshots.get(subjectKey(subject)) ?? [];
|
|
33
|
+
list.push(snap);
|
|
34
|
+
snapshots.set(subjectKey(subject), list);
|
|
35
|
+
events?.onVersionBump?.(subject, snap.version);
|
|
36
|
+
return snap;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
async getDraft(subject) {
|
|
40
|
+
return drafts.get(subjectKey(subject));
|
|
41
|
+
},
|
|
42
|
+
async saveDraft(subject, data) {
|
|
43
|
+
drafts.set(subjectKey(subject), structuredClone(data));
|
|
44
|
+
},
|
|
45
|
+
async getPublished(subject) {
|
|
46
|
+
return snapshots.get(subjectKey(subject))?.at(-1);
|
|
47
|
+
},
|
|
48
|
+
async publish(subject) {
|
|
49
|
+
const draft = drafts.get(subjectKey(subject));
|
|
50
|
+
if (draft === undefined)
|
|
51
|
+
throw new NothingToPublish(subject);
|
|
52
|
+
return bump(subject, draft);
|
|
53
|
+
},
|
|
54
|
+
async rollback(subject, toVersion) {
|
|
55
|
+
const target = snapshots.get(subjectKey(subject))?.find((s) => s.version === toVersion);
|
|
56
|
+
if (!target)
|
|
57
|
+
throw new RollbackTargetMissing(subject, toVersion);
|
|
58
|
+
// Rollback = a NEW snapshot of the old data; the target stays untouched.
|
|
59
|
+
return bump(subject, target.data);
|
|
60
|
+
},
|
|
61
|
+
async listSnapshots(subject) {
|
|
62
|
+
return [...(snapshots.get(subjectKey(subject)) ?? [])];
|
|
63
|
+
},
|
|
64
|
+
async currentVersion(subject) {
|
|
65
|
+
return snapshots.get(subjectKey(subject))?.at(-1)?.version;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=lifecycle-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-store.js","sourceRoot":"","sources":["../../src/lifecycle/lifecycle-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-version token + versioned subjects (decision 004 §4.1). MONOTONIC
|
|
3
|
+
* per subject; distinct from bind-core's grammar SchemaVersion.
|
|
4
|
+
*/
|
|
5
|
+
import type { ContentTypeId, InstanceId, PageId } from "@batterai/bind-core";
|
|
6
|
+
export type ContentVersionToken = string & {
|
|
7
|
+
readonly __brand: "ContentVersionToken";
|
|
8
|
+
};
|
|
9
|
+
export type VersionedSubject = {
|
|
10
|
+
readonly kind: "page";
|
|
11
|
+
readonly pageId: PageId;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "instance";
|
|
14
|
+
readonly contentTypeId: ContentTypeId;
|
|
15
|
+
readonly instanceId: InstanceId;
|
|
16
|
+
};
|
|
17
|
+
export declare function subjectKey(s: VersionedSubject): string;
|
|
18
|
+
export declare function versionToken(n: number): ContentVersionToken;
|
|
19
|
+
/** Inverse of versionToken: recover the numeric ordinal. The CMS-backed store
|
|
20
|
+
* (cms-lifecycle-store.ts) derives the next per-subject version from persisted
|
|
21
|
+
* snapshots instead of a live counter, so it needs to parse tokens back. */
|
|
22
|
+
export declare function versionNumber(t: ContentVersionToken): number;
|
|
23
|
+
export interface PublishedSnapshot<T> {
|
|
24
|
+
readonly subject: VersionedSubject;
|
|
25
|
+
readonly version: ContentVersionToken;
|
|
26
|
+
readonly publishedAt: string;
|
|
27
|
+
/** IMMUTABLE — a new publish creates a NEW snapshot; history is never edited. */
|
|
28
|
+
readonly data: T;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=version-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-token.d.ts","sourceRoot":"","sources":["../../src/lifecycle/version-token.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7E,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAA;CAAE,CAAC;AAEvF,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC;AAE1G,wBAAgB,UAAU,CAAC,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAEtD;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAG3D;AAED;;4EAE4E;AAC5E,wBAAgB,aAAa,CAAC,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAU5D;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;CAClB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function subjectKey(s) {
|
|
2
|
+
return s.kind === "page" ? `page:${s.pageId}` : `instance:${s.contentTypeId}:${s.instanceId}`;
|
|
3
|
+
}
|
|
4
|
+
export function versionToken(n) {
|
|
5
|
+
// Zero-padded so lexicographic order == numeric order (monotonic compare).
|
|
6
|
+
return `v${String(n).padStart(12, "0")}`;
|
|
7
|
+
}
|
|
8
|
+
/** Inverse of versionToken: recover the numeric ordinal. The CMS-backed store
|
|
9
|
+
* (cms-lifecycle-store.ts) derives the next per-subject version from persisted
|
|
10
|
+
* snapshots instead of a live counter, so it needs to parse tokens back. */
|
|
11
|
+
export function versionNumber(t) {
|
|
12
|
+
const raw = t;
|
|
13
|
+
if (!/^v\d+$/.test(raw)) {
|
|
14
|
+
throw new Error(`Invalid content version token: ${raw}`);
|
|
15
|
+
}
|
|
16
|
+
const n = Number(raw.slice(1));
|
|
17
|
+
if (!Number.isSafeInteger(n) || n < 1) {
|
|
18
|
+
throw new Error(`Invalid content version token: ${raw}`);
|
|
19
|
+
}
|
|
20
|
+
return n;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=version-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-token.js","sourceRoot":"","sources":["../../src/lifecycle/version-token.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,UAAU,CAAC,CAAmB;IAC5C,OAAO,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;AAChG,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,2EAA2E;IAC3E,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,EAAyB,CAAC;AAClE,CAAC;AAED;;4EAE4E;AAC5E,MAAM,UAAU,aAAa,CAAC,CAAsB;IAClD,MAAM,GAAG,GAAG,CAAW,CAAC;IACxB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Locale, type MessageParams } from "@batterai/contract";
|
|
2
|
+
export declare const HOST_MESSAGES_EN: {
|
|
3
|
+
readonly "binding.broken": "broken binding: {reason}";
|
|
4
|
+
readonly "block.unknown": "Unknown block type: {type}";
|
|
5
|
+
readonly "loadMore.retry": "Loading failed - try again";
|
|
6
|
+
readonly "metadata.brokenTitle": "Broken title binding";
|
|
7
|
+
readonly "repeater.empty": "No content in this list yet.";
|
|
8
|
+
readonly "route.previewAuthRequired": "preview requires a signed-in editor";
|
|
9
|
+
readonly "write.fieldUnreadable": "field is not readable for this user";
|
|
10
|
+
readonly "write.pageLoginRequired": "only signed-in editors can edit pages";
|
|
11
|
+
readonly "write.unsupportedInstanceValue": "UnsupportedInstanceWriteValue: a {kind} value cannot be written to a CMS field yet - only literal values are supported";
|
|
12
|
+
};
|
|
13
|
+
export declare const HOST_MESSAGES_NL: {
|
|
14
|
+
readonly "binding.broken": "binding kapot: {reason}";
|
|
15
|
+
readonly "block.unknown": "Onbekend bloktype: {type}";
|
|
16
|
+
readonly "loadMore.retry": "Laden mislukt - opnieuw proberen";
|
|
17
|
+
readonly "metadata.brokenTitle": "kapotte titel-binding";
|
|
18
|
+
readonly "repeater.empty": "Nog geen inhoud in deze lijst.";
|
|
19
|
+
readonly "route.previewAuthRequired": "preview vereist een ingelogde redacteur";
|
|
20
|
+
readonly "write.fieldUnreadable": "veld niet leesbaar voor deze gebruiker";
|
|
21
|
+
readonly "write.pageLoginRequired": "alleen ingelogde redacteuren kunnen pagina's bewerken";
|
|
22
|
+
readonly "write.unsupportedInstanceValue": "UnsupportedInstanceWriteValue: een {kind}-waarde kan nog niet naar een CMS-veld worden geschreven - alleen letterlijke waarden";
|
|
23
|
+
};
|
|
24
|
+
export type HostMessageKey = keyof typeof HOST_MESSAGES_EN;
|
|
25
|
+
export declare function hostMessage(locale: Locale | string | undefined, key: HostMessageKey, params?: MessageParams): string;
|
|
26
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,MAAM,EAAuB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE1G,eAAO,MAAM,gBAAgB;;;;;;;;;;CAWM,CAAC;AAEpC,eAAO,MAAM,gBAAgB;;;;;;;;;;CAWqC,CAAC;AAEnE,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,gBAAgB,CAAC;AAO3D,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAEpH"}
|