@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
@@ -0,0 +1,73 @@
1
+ export function registerWriteCases(target, hooks) {
2
+ const { test, expect } = hooks;
3
+ test("CAS: stale precondition refused; fresh precondition accepted (then restored)", async () => {
4
+ const page = await target.source.listInstances(target.contentTypeId, { pageSize: 1 }, undefined, target.editor);
5
+ const inst = page.items[0];
6
+ const subject = { kind: "instance", contentTypeId: target.contentTypeId, instanceId: inst.id };
7
+ const current = await target.source.getVersion(subject, target.editor);
8
+ expect(current).toBeDefined();
9
+ try {
10
+ let staleRefused = false;
11
+ try {
12
+ await target.source.writeInstanceField(target.contentTypeId, inst.id, target.writableField, inst.values[target.writableField], "v-stale-never-issued", target.editor);
13
+ }
14
+ catch {
15
+ staleRefused = true;
16
+ }
17
+ expect(staleRefused).toBe(true);
18
+ const result = await target.source.writeInstanceField(target.contentTypeId, inst.id, target.writableField, inst.values[target.writableField], current, target.editor);
19
+ expect(result.version).toBeDefined();
20
+ expect(result.version).not.toBe(current);
21
+ // Tolerance calibration (locked decision §1.3): quality metadata is read
22
+ // by the KIT, never branched on by consumers. A version-token adapter
23
+ // must mint a DISTINCT token for two immediately-consecutive writes
24
+ // (raceWindowMs 0); a timestamp adapter is exempted from the rapid-double-
25
+ // write distinctness check WITHIN its declared window — the declaration is
26
+ // the documented tolerance, not a silent pass.
27
+ if (target.source.capabilities.concurrency.strategy === "version-token") {
28
+ const second = await target.source.writeInstanceField(target.contentTypeId, inst.id, target.writableField, inst.values[target.writableField], result.version, target.editor);
29
+ expect(second.version).not.toBe(result.version);
30
+ }
31
+ }
32
+ finally {
33
+ await target.cleanup();
34
+ }
35
+ });
36
+ test("create (plan §2.6): native creates {id, version} that roundtrips; skipped when create:'none'", async () => {
37
+ // The create case is capability-gated (I-5): a 'none' adapter never
38
+ // reaches createInstance, so the conformance kit must NOT call it —
39
+ // skip with a trivial pass. Also skip when no create fixture is wired
40
+ // (keeps read-only conformance targets green).
41
+ if (target.source.capabilities.create.mode === "absent" || target.create === undefined) {
42
+ expect(target.source.capabilities.create).toBe(target.source.capabilities.create);
43
+ return;
44
+ }
45
+ const { initialValues, expectCreated } = target.create;
46
+ // create:'native' MUST implement the (optional) createInstance method —
47
+ // declaring the capability without the method is a conformance failure.
48
+ // Bind to a local so TS narrowing survives the await inside the try.
49
+ const createInstance = target.source.createInstance?.bind(target.source);
50
+ if (!createInstance) {
51
+ throw new Error("adapter declares create:'native' but does not implement createInstance");
52
+ }
53
+ try {
54
+ const created = await createInstance(target.contentTypeId, initialValues, target.editor);
55
+ expect(typeof created.id).toBe("string");
56
+ expect(created.version).toBeDefined();
57
+ // The record must be readable at its new id, with initialValues
58
+ // merged over schema defaults (invariant: what you created is there).
59
+ const fetched = await target.source.getInstance(target.contentTypeId, created.id, target.editor);
60
+ expect(fetched).toBeDefined();
61
+ expect(fetched.values[expectCreated.field]).toEqual(expectCreated.value);
62
+ // The returned version is a live CAS token for the created record —
63
+ // getVersion agrees, so the editor session continues CAS-guarded.
64
+ const subject = { kind: "instance", contentTypeId: target.contentTypeId, instanceId: created.id };
65
+ const version = await target.source.getVersion(subject, target.editor);
66
+ expect(version).toBe(created.version);
67
+ }
68
+ finally {
69
+ await target.cleanup();
70
+ }
71
+ });
72
+ }
73
+ //# sourceMappingURL=cases-writes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cases-writes.js","sourceRoot":"","sources":["../../../src/content-source/conformance/cases-writes.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,kBAAkB,CAAC,MAAyB,EAAE,KAAuB;IACnF,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAE/B,IAAI,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC9F,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAChH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,UAAmB,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;QACxG,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACvE,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,IAAI,YAAY,GAAG,KAAK,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,MAAM,CAAC,kBAAkB,CACpC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,aAAa,EACnD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,sBAA+B,EAAE,MAAM,CAAC,MAAM,CAClF,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY,GAAG,IAAI,CAAC;YACtB,CAAC;YACD,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,kBAAkB,CACnD,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,aAAa,EACnD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAC1D,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzC,yEAAyE;YACzE,sEAAsE;YACtE,oEAAoE;YACpE,2EAA2E;YAC3E,2EAA2E;YAC3E,+CAA+C;YAC/C,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,QAAQ,KAAK,eAAe,EAAE,CAAC;gBACxE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,kBAAkB,CACnD,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,aAAa,EACnD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CACjE,CAAC;gBACF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CACF,8FAA8F,EAC9F,KAAK,IAAI,EAAE;QACT,oEAAoE;QACpE,oEAAoE;QACpE,sEAAsE;QACtE,+CAA+C;QAC/C,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACvF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAClF,OAAO;QACT,CAAC;QACD,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC;QACvD,wEAAwE;QACxE,wEAAwE;QACxE,qEAAqE;QACrE,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzE,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YACzF,MAAM,CAAC,OAAQ,OAAO,CAAC,EAAa,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;YAEtC,gEAAgE;YAChE,sEAAsE;YACtE,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YACjG,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;YAC9B,MAAM,CAAC,OAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAE1E,oEAAoE;YACpE,kEAAkE;YAClE,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,UAAmB,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;YAC3G,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YACvE,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Shared assertion helper for delegated-auth calibration (decision 012 phase
3
+ * 1). Every case file that branches on `auth.mode === "delegated"` checks the
4
+ * SAME shape — the call rejects with an error whose `name === "SessionRequired"`
5
+ * (the kit's per-adapter-class convention). Extracted so
6
+ * cases-reads/-isolation/-permissions/-delegation don't fork the assertion.
7
+ */
8
+ import type { ExpectFn } from "./target";
9
+ export declare function expectSessionRequired(expect: ExpectFn, fn: () => Promise<unknown>): Promise<void>;
10
+ //# sourceMappingURL=session-required.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-required.d.ts","sourceRoot":"","sources":["../../../src/content-source/conformance/session-required.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAQvG"}
@@ -0,0 +1,11 @@
1
+ export async function expectSessionRequired(expect, fn) {
2
+ let name;
3
+ try {
4
+ await fn();
5
+ }
6
+ catch (e) {
7
+ name = e instanceof Error ? e.name : undefined;
8
+ }
9
+ expect(name).toBe("SessionRequired");
10
+ }
11
+ //# sourceMappingURL=session-required.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-required.js","sourceRoot":"","sources":["../../../src/content-source/conformance/session-required.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAAgB,EAAE,EAA0B;IACtF,IAAI,IAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,EAAE,EAAE,CAAC;IACb,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACjD,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Kit-internal hostile input for the D1 behavioral cases (decision 012 phase 2).
3
+ *
4
+ * Public delivery is explicit via `publicDeliveryViewer`. This helper is only
5
+ * the hostile bare-sessionless input for D1: runtime attackers don't respect
6
+ * types, so delegated adapters must refuse it before I/O.
7
+ */
8
+ import type { Viewer } from "../viewer";
9
+ import type { ConformanceTarget } from "./target";
10
+ /** A bare viewer with NO session or public-delivery marker. */
11
+ export declare function sessionlessViewer(target: ConformanceTarget): Viewer;
12
+ //# sourceMappingURL=sessionless-viewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionless-viewer.d.ts","sourceRoot":"","sources":["../../../src/content-source/conformance/sessionless-viewer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,5 @@
1
+ /** A bare viewer with NO session or public-delivery marker. */
2
+ export function sessionlessViewer(target) {
3
+ return { locale: target.editor.locale };
4
+ }
5
+ //# sourceMappingURL=sessionless-viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionless-viewer.js","sourceRoot":"","sources":["../../../src/content-source/conformance/sessionless-viewer.ts"],"names":[],"mappings":"AAUA,+DAA+D;AAC/D,MAAM,UAAU,iBAAiB,CAAC,MAAyB;IACzD,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAuB,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Conformance-kit shared types (extracted from conformance-kit.ts for A6).
3
+ * The `ConformanceTarget` interface every adapter fixture implements, plus the
4
+ * framework-agnostic hook types the kit runs against (bun:test and any future
5
+ * runner). Re-exported from conformance-kit.ts so importers stay unchanged.
6
+ */
7
+ import type { InstanceId } from "@batterai/bind-core";
8
+ import type { ContentSource, EntityProvider } from "../content-source";
9
+ import type { CanvasAction } from "../permissions";
10
+ import type { Viewer } from "../viewer";
11
+ import type { ContentTypeId, Locale } from "@batterai/bind-core";
12
+ export interface ConformanceTarget {
13
+ readonly name: string;
14
+ readonly source: ContentSource;
15
+ readonly provider: EntityProvider;
16
+ readonly editor: Viewer;
17
+ /** A content type with >= 5 instances and a reference field. */
18
+ readonly contentTypeId: ContentTypeId;
19
+ readonly referenceField: string;
20
+ /** A sortable scalar field whose ascending order differs from id order. */
21
+ readonly sortField: string;
22
+ /** A field that must NEVER flow into Canvas — stripped by unconditional
23
+ * restrictedFields projection for EVERY viewer (decision 012 phase 2). Must
24
+ * be distinct from sort/reference/writable/absent fields (those stay visible). */
25
+ readonly restrictedField: string;
26
+ /** A writable field for CAS tests (mutations restored by the harness). */
27
+ readonly writableField: string;
28
+ /** A field DECLARED in the ContentType projection that no document carries
29
+ * (schema-drift; §6 null/missing cell): the CMS PoCs enforce required-ness
30
+ * on every stored field, so real-world absence enters via projection, and
31
+ * adapters must carry it as ABSENT — never "" or a fabricated value. */
32
+ readonly absentField: string;
33
+ /** Insert a NEW instance mid-pagination (concurrent-insert stability). */
34
+ insertBetween(afterId: InstanceId): Promise<InstanceId>;
35
+ /** Cleanup for insertBetween/write mutations (snapshot/reset discipline §12). */
36
+ cleanup(): Promise<void>;
37
+ /** Create-conformance fixture (plan §2.6). Present ONLY for adapters whose
38
+ * source declares `create: 'native'`; the create case skips itself when the
39
+ * capability is 'none' OR this fixture is absent. `initialValues` is the
40
+ * record to create; `expectCreated` names one field->value pair that MUST be
41
+ * present on the created instance after a getInstance roundtrip (merge check).
42
+ * Ids created here are cleaned up via `cleanup()`. */
43
+ readonly create?: {
44
+ readonly initialValues: Readonly<Record<string, unknown>>;
45
+ readonly expectCreated: {
46
+ readonly field: string;
47
+ readonly value: unknown;
48
+ };
49
+ };
50
+ /** Draft/publish isolation fixture (G3). Present ONLY for adapters whose
51
+ * source declares `draftPublish: 'native' | 'bridged'` AND that wire a real
52
+ * draft layer; the isolation case skips itself when the capability is absent
53
+ * OR this fixture is missing (keeps draft-less targets green, mirroring the
54
+ * `create` skip). `writeDraft` performs one draft edit and reports where it
55
+ * landed: the PUBLISHED read (public viewer) MUST still see `publishedValue`
56
+ * (the draft is invisible — the isolation claim), while the DRAFT read
57
+ * (editor viewer) MUST see `draftValue`. Mutations are undone via `cleanup()`. */
58
+ readonly draftPublish?: {
59
+ writeDraft(): Promise<{
60
+ readonly instanceId: InstanceId;
61
+ readonly field: string;
62
+ /** What a PUBLISHED (public-viewer) read must still return — never the draft. */
63
+ readonly publishedValue: unknown;
64
+ /** What a DRAFT (editor-viewer) read must return — the pending edit. */
65
+ readonly draftValue: unknown;
66
+ }>;
67
+ };
68
+ /** Localization fixture (Wave 1, Task 4.1). Present ONLY when the adapter
69
+ * declares a strategy other than 'none'; the case skips (trivially passes)
70
+ * otherwise — and the proven-absent discipline applies: strategy 'none' +
71
+ * fixture present is a misdeclaration the case fails loudly. `locales` are two
72
+ * locales with KNOWN different values of `localizedField` on `instanceId`;
73
+ * `expected` is that field's value per locale (same list position). */
74
+ readonly localization?: {
75
+ readonly localizedField: string;
76
+ readonly locales: readonly [Locale, Locale];
77
+ readonly expected: readonly [unknown, unknown];
78
+ readonly instanceId: InstanceId;
79
+ };
80
+ /** Native-ACL fidelity fixture (decision 012 phase 4, delegation-fidelity leg).
81
+ * Present ONLY for targets declaring `acl.document: "native"`; the fidelity
82
+ * case skips it otherwise (projected targets — Strapi/local — legitimately
83
+ * grant a uniform base, so asserting a CMS-derived grant there would be wrong).
84
+ * `expectAllowed` names EXACTLY the Canvas actions the CMS grants `editor` on
85
+ * `contentTypeId`; permit's grant for each action must equal
86
+ * `expectAllowed.includes(action)`. Keep it a plain list — the denial-mapping
87
+ * detail is proven by the adapter's unit tests, not here. */
88
+ readonly nativeAcl?: {
89
+ readonly expectAllowed: readonly CanvasAction[];
90
+ };
91
+ }
92
+ export type TestFn = (name: string, fn: () => Promise<void> | void) => void;
93
+ export type ExpectFn = (actual: unknown) => {
94
+ toBe(v: unknown): void;
95
+ toBeDefined(): void;
96
+ toBeUndefined(): void;
97
+ toEqual(v: unknown): void;
98
+ toBeGreaterThanOrEqual(n: number): void;
99
+ toContain(v: unknown): void;
100
+ not: {
101
+ toBe(v: unknown): void;
102
+ toContain(v: unknown): void;
103
+ };
104
+ };
105
+ /** The framework-agnostic hooks a case group needs (describe/test/expect). */
106
+ export interface ConformanceHooks {
107
+ describe: (name: string, fn: () => void) => void;
108
+ test: TestFn;
109
+ expect: ExpectFn;
110
+ }
111
+ //# sourceMappingURL=target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../../src/content-source/conformance/target.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target.js","sourceRoot":"","sources":["../../../src/content-source/conformance/target.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { ConformanceHooks, ConformanceTarget } from "./conformance/target";
2
+ export type { ConformanceTarget } from "./conformance/target";
3
+ export declare function runConformanceKit(target: ConformanceTarget, hooks: ConformanceHooks): void;
4
+ //# sourceMappingURL=conformance-kit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance-kit.d.ts","sourceRoot":"","sources":["../../src/content-source/conformance-kit.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEhF,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAkB1F"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * THE adapter conformance kit (plan §7 + §8 Phase 6): one suite, every
3
+ * adapter. "Vendor-neutral" is proven by running THIS — same assertions
4
+ * against local, Payload, and Strapi (zero L0–L2 forks). Lives in host so
5
+ * adapters (L3) can import it without a lateral adapter→adapter edge.
6
+ *
7
+ * Framework-agnostic: takes describe/test/expect hooks so bun:test and any
8
+ * future runner can host it.
9
+ *
10
+ * A6 decomposition: the `ConformanceTarget` interface + hook types live in
11
+ * `./conformance/target`, and the (order-sensitive) case bodies live in
12
+ * `./conformance/cases-reads`, `-writes`, `-isolation`. This module is the thin
13
+ * assembler that wires the ONE `describe` and calls the case groups in the
14
+ * SAME order adapters snapshot/depend on. `ConformanceTarget` is re-exported so
15
+ * importers (adapter tests, the host barrel) stay unchanged.
16
+ */
17
+ import { registerReadCases } from "./conformance/cases-reads.js";
18
+ import { registerWriteCases } from "./conformance/cases-writes.js";
19
+ import { registerPermissionCases } from "./conformance/cases-permissions.js";
20
+ import { registerIsolationCases } from "./conformance/cases-isolation.js";
21
+ import { registerDelegationCases } from "./conformance/cases-delegation.js";
22
+ import { registerFidelityCases } from "./conformance/cases-fidelity.js";
23
+ export function runConformanceKit(target, hooks) {
24
+ const { describe } = hooks;
25
+ describe(`conformance: ${target.name}`, () => {
26
+ // Registration order IS the case order the suite runs and adapters depend
27
+ // on: reads (capabilities/describe/restricted-field/pagination) → writes
28
+ // (CAS/create) → permissions (permit + hidden==refused
29
+ // agreement) → isolation (draft/publish G3) → delegation (auth.mode ===
30
+ // "delegated" only; a no-op for every ambient adapter today, last). The
31
+ // fidelity leg (valid session honored / CMS refusal surfaces / native-ACL
32
+ // grant match) runs after delegation, also delegated-only.
33
+ registerReadCases(target, hooks);
34
+ registerWriteCases(target, hooks);
35
+ registerPermissionCases(target, hooks);
36
+ registerIsolationCases(target, hooks);
37
+ registerDelegationCases(target, hooks);
38
+ registerFidelityCases(target, hooks);
39
+ });
40
+ }
41
+ //# sourceMappingURL=conformance-kit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance-kit.js","sourceRoot":"","sources":["../../src/content-source/conformance-kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAKrE,MAAM,UAAU,iBAAiB,CAAC,MAAyB,EAAE,KAAuB;IAClF,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAE3B,QAAQ,CAAC,gBAAgB,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE;QAC3C,0EAA0E;QAC1E,yEAAyE;QACzE,uDAAuD;QACvD,wEAAwE;QACxE,wEAAwE;QACxE,0EAA0E;QAC1E,2DAA2D;QAC3D,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACjC,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAClC,uBAAuB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACvC,sBAAsB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACtC,uBAAuB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACvC,qBAAqB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * The multi-CMS seam (decision 004 §1): EntityProvider (schema + ACLs) and
3
+ * ContentSource (instances + writes + capabilities). "Support CMS N" = a new
4
+ * adapter implementing these — zero edits to L0–L2.
5
+ */
6
+ import type { ContentType, ContentTypeId, Instance, InstanceId, RepeaterQuery } from "@batterai/bind-core";
7
+ import type { FieldAccess, Viewer } from "./viewer";
8
+ import type { ActionGrants, PermissionSubject } from "./permissions";
9
+ import type { FieldSchema } from "@batterai/bind-core";
10
+ import type { ContentVersionToken, VersionedSubject } from "../lifecycle/version-token";
11
+ export interface DescribedField {
12
+ readonly schema: FieldSchema;
13
+ readonly access: FieldAccess;
14
+ }
15
+ export interface DescribedContentType {
16
+ readonly contentType: ContentType;
17
+ /** Invariant E's output: a field the viewer can't read is ABSENT here —
18
+ * never present-but-flagged (existence must not leak). */
19
+ readonly readableFields: readonly DescribedField[];
20
+ }
21
+ export interface EntityProvider {
22
+ /** The Build tab's ONLY data source; published-vs-draft aware. */
23
+ describe(contentTypeId: ContentTypeId, viewer: Viewer): Promise<DescribedContentType>;
24
+ }
25
+ export interface InstancePage {
26
+ readonly items: readonly Instance[];
27
+ readonly cursor?: string;
28
+ readonly hasMore: boolean;
29
+ }
30
+ export type PushdownOp = "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "and" | "or";
31
+ /** Quality axis for concurrency: strategy is metadata for host/conformance —
32
+ * consumers never branch on it. raceWindowMs is the DECLARED worst-case window
33
+ * in which two same-subject writes can interleave undetected: 0 for a real
34
+ * version token; > 0 for a timestamp bridge (Strapi updatedAt = 1000ms —
35
+ * second-resolution comparisons plus clock skew, declared not hidden). */
36
+ export interface ConcurrencyCapability {
37
+ readonly strategy: "version-token" | "timestamp";
38
+ readonly raceWindowMs: number;
39
+ }
40
+ /** Access-control provenance per axis (decision 004 declared-fallback). */
41
+ export interface AclCapability {
42
+ /** 'native' = the CMS's own access control answers permit(); 'projected' =
43
+ * the adapter derives grants from its own config (declared fallback). */
44
+ readonly document: "native" | "projected";
45
+ readonly field: "native" | "projected";
46
+ }
47
+ /** How the adapter authenticates to the CMS (decision 012 + 013).
48
+ * - "delegated": authoring/write/preview operations run as the end user
49
+ * carried in viewer.session. Read methods may also serve an explicit
50
+ * public-delivery viewer with no Authorization header; a bare missing
51
+ * session is still refused BEFORE any CMS I/O.
52
+ * - "ambient": the adapter has its own ambient identity (in-memory/dev
53
+ * harness); sessionless viewers are served. Real CMS adapters must be
54
+ * "delegated" (decision 012). */
55
+ export interface AuthCapability {
56
+ readonly mode: "delegated" | "ambient";
57
+ }
58
+ /** Adapters declare what they can do — gaps are BRIDGED or reported, never
59
+ * silently assumed (plan Phase 6/6.5 bridging as a first-class type). Consumers
60
+ * branch ONLY on presence (present/absent) and permission (allowed/denied);
61
+ * native-vs-bridged and the tolerance parameters are observability/conformance
62
+ * metadata, never a consumer switch (locked decision 2026-07-07 §1.3). */
63
+ export interface AdapterCapabilities {
64
+ readonly concurrency: ConcurrencyCapability;
65
+ readonly pagination: {
66
+ readonly mode: "keyset" | "bridged-keyset";
67
+ };
68
+ readonly populate: {
69
+ readonly maxDepth: number;
70
+ };
71
+ readonly acl: AclCapability;
72
+ readonly auth: AuthCapability;
73
+ readonly draftPublish: {
74
+ readonly mode: "native" | "bridged" | "absent";
75
+ };
76
+ readonly localization: {
77
+ readonly strategy: "field-level" | "entry-level" | "none";
78
+ };
79
+ /** Enumerated ops the adapter can push down to its native query layer.
80
+ * Empty array = no pushdown (filter/sort correct per returned page only —
81
+ * the documented invariant-I tolerance). Replaces the old flat native|none flag. */
82
+ readonly pushdown: {
83
+ readonly ops: readonly PushdownOp[];
84
+ };
85
+ /** Create-on-first-write support (plan §2.6). 'native' = the adapter's CMS
86
+ * create path is wired; 'absent' = instance-less detail pages render
87
+ * read-only-with-reason at pre-focus time (I-5), never a save-time surprise.
88
+ * Declared, never assumed (decision 004: gaps are declared). */
89
+ readonly create: {
90
+ readonly mode: "native" | "absent";
91
+ };
92
+ /** Wave-2 axes, declared now so the vocabulary is closed and 'absent' is
93
+ * provable from day one (no adapter may omit them). */
94
+ readonly history: {
95
+ readonly mode: "native" | "absent";
96
+ };
97
+ readonly bulkWrite: {
98
+ readonly mode: "native" | "bridged" | "absent";
99
+ };
100
+ readonly preview: {
101
+ readonly mode: "native" | "absent";
102
+ };
103
+ }
104
+ export interface WriteResult {
105
+ readonly version: ContentVersionToken;
106
+ }
107
+ /** Result of createInstance (plan §2.6): the real instance id the record was
108
+ * created under, plus the CAS token so the editor session continues
109
+ * CAS-guarded against the created record (the draft->real instance swap). */
110
+ export interface CreateResult {
111
+ readonly id: InstanceId;
112
+ readonly version: ContentVersionToken;
113
+ }
114
+ /** Canonical refusal for decision 012 delegated auth: thrown BEFORE any CMS
115
+ * I/O when `viewer.session` is absent or unusable. Adapters MAY throw this
116
+ * directly, or their own equivalent whose `name` the conformance kit
117
+ * recognizes as `"SessionRequired"`. Only meaningful for adapters declaring
118
+ * `capabilities.auth.mode === "delegated"`. */
119
+ export declare class SessionRequired extends Error {
120
+ constructor();
121
+ }
122
+ export interface ContentSource {
123
+ readonly capabilities: AdapterCapabilities;
124
+ /** Host-decided per-viewer action grants (locked decision 2026-07-07 §1.2):
125
+ * Canvas renders an action iff capability AND permit agree; every mutation is
126
+ * re-checked at the adapter regardless (permit is discovery, not the gate).
127
+ * Decision 012 phase 2: battery-editor owns no tenancy/access clamp — permit
128
+ * reflects only the capability axis; the CMS is the sole access authority.
129
+ * Delegated adapters refuse a bare sessionless viewer before any I/O. Reason
130
+ * REQUIRED on every denial (invariant P). */
131
+ permit(subject: PermissionSubject, viewer: Viewer): Promise<ActionGrants>;
132
+ /** Cursor/keyset — never offset; stable under concurrent inserts. */
133
+ listInstances(contentTypeId: ContentTypeId, query: RepeaterQuery, cursor: string | undefined, viewer: Viewer): Promise<InstancePage>;
134
+ getInstance(contentTypeId: ContentTypeId, instanceId: InstanceId, viewer: Viewer): Promise<Instance | undefined>;
135
+ /** THE batched reference primitive: ONE call per (contentType) group per
136
+ * wave — never per-row I/O (invariant C; suite C counts these). */
137
+ getInstancesByIds(contentTypeId: ContentTypeId, instanceIds: readonly InstanceId[], viewer: Viewer): Promise<readonly Instance[]>;
138
+ /** Current version token for CAS reads (edit-session open). */
139
+ getVersion(subject: VersionedSubject, viewer: Viewer): Promise<ContentVersionToken | undefined>;
140
+ /** Field write with CAS precondition per capabilities.concurrency. Adapters throw
141
+ * their own stale/authz errors; the host wraps them (decision 004 §5.3). */
142
+ writeInstanceField(contentTypeId: ContentTypeId, instanceId: InstanceId, field: string, value: unknown, expectedVersion: ContentVersionToken | undefined, viewer: Viewer): Promise<WriteResult>;
143
+ /** Create a NEW instance (plan §2.6): the first commit on an instance-less
144
+ * detail page. `initialValues` are merged OVER the content type's schema
145
+ * defaults (FieldSchema.defaultValue). Returns the real id + version so the
146
+ * host swaps the real instance into the page-entity frame and continues the
147
+ * session CAS-guarded. Only meaningful when capabilities.create.mode === 'native';
148
+ * a 'none' adapter degrades to read-only-with-reason before this is ever
149
+ * reached (I-5) and OMITS this method entirely (optional); the host and
150
+ * conformance kit null-check before calling. */
151
+ createInstance?(contentTypeId: ContentTypeId, initialValues: Readonly<Record<string, unknown>>, viewer: Viewer): Promise<CreateResult>;
152
+ }
153
+ //# sourceMappingURL=content-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-source.d.ts","sourceRoot":"","sources":["../../src/content-source/content-source.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /** Canonical refusal for decision 012 delegated auth: thrown BEFORE any CMS
2
+ * I/O when `viewer.session` is absent or unusable. Adapters MAY throw this
3
+ * directly, or their own equivalent whose `name` the conformance kit
4
+ * recognizes as `"SessionRequired"`. Only meaningful for adapters declaring
5
+ * `capabilities.auth.mode === "delegated"`. */
6
+ export class SessionRequired extends Error {
7
+ constructor() {
8
+ super("viewer.session is required for delegated-auth adapters");
9
+ this.name = "SessionRequired";
10
+ }
11
+ }
12
+ //# sourceMappingURL=content-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-source.js","sourceRoot":"","sources":["../../src/content-source/content-source.ts"],"names":[],"mappings":"AAiHA;;;;+CAI+C;AAC/C,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC;QACE,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Permission-discovery surface (locked decision 2026-07-07 §1.2): two axes gate
3
+ * every Canvas action — capability (can this host do X at all) × permission (may
4
+ * THIS viewer do X now). `permit` answers the permission axis per-request; the
5
+ * host re-checks every mutation regardless (permit is discovery, not the gate).
6
+ * Neutral core — the descriptor types + the shared grant math live here in host
7
+ * (L2); native/projected behavior lives in the adapters (L3).
8
+ */
9
+ import type { ContentTypeId, InstanceId } from "@batterai/bind-core";
10
+ export type CanvasAction = "read" | "update" | "create" | "delete" | "publish";
11
+ /** Invariant-P shape: reason REQUIRED when !allowed — Canvas hides the action
12
+ * and can explain why; a bare false is a conformance failure. */
13
+ export interface ActionGrant {
14
+ readonly allowed: boolean;
15
+ readonly reason?: string;
16
+ }
17
+ export type ActionGrants = Readonly<Record<CanvasAction, ActionGrant>>;
18
+ /** instanceId absent = type-level question (create, list). */
19
+ export interface PermissionSubject {
20
+ readonly contentTypeId: ContentTypeId;
21
+ readonly instanceId?: InstanceId;
22
+ }
23
+ export declare const CANVAS_ACTIONS: readonly CanvasAction[];
24
+ /**
25
+ * Shared grant math so the three adapters don't fork the same capability-
26
+ * coupling rules (§2.2). An adapter computes its NATIVE or PROJECTED base
27
+ * grants, then this clamps them by the capability invariants every host must
28
+ * satisfy:
29
+ * - an absent `create` capability forces `create` denied-with-reason;
30
+ * - an absent `draftPublish` capability forces `publish` denied-with-reason.
31
+ *
32
+ * Decision 012 phase 2: the viewer-kind (public) clamp is GONE. battery-editor
33
+ * is not a second authority — every request carries a delegated CMS session
34
+ * (delegated adapters refuse sessionless before I/O; that stays) and the CMS
35
+ * decides what that session may write. We only reflect what the host can do at
36
+ * all (the capability axis). Pure function — trivially testable without I/O.
37
+ */
38
+ export declare function clampGrants(base: ActionGrants, capabilities: {
39
+ readonly create: {
40
+ readonly mode: "native" | "absent";
41
+ };
42
+ readonly draftPublish: {
43
+ readonly mode: "native" | "bridged" | "absent";
44
+ };
45
+ }): ActionGrants;
46
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/content-source/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAErE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE/E;iEACiE;AACjE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;AAEvE,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;CAClC;AAED,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAMjD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,YAAY,EAClB,YAAY,EAAE;IACZ,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAA;KAAE,CAAC;IACxD,QAAQ,CAAC,YAAY,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;KAAE,CAAC;CAC3E,GACA,YAAY,CAUd"}
@@ -0,0 +1,33 @@
1
+ export const CANVAS_ACTIONS = [
2
+ "read",
3
+ "update",
4
+ "create",
5
+ "delete",
6
+ "publish",
7
+ ];
8
+ /**
9
+ * Shared grant math so the three adapters don't fork the same capability-
10
+ * coupling rules (§2.2). An adapter computes its NATIVE or PROJECTED base
11
+ * grants, then this clamps them by the capability invariants every host must
12
+ * satisfy:
13
+ * - an absent `create` capability forces `create` denied-with-reason;
14
+ * - an absent `draftPublish` capability forces `publish` denied-with-reason.
15
+ *
16
+ * Decision 012 phase 2: the viewer-kind (public) clamp is GONE. battery-editor
17
+ * is not a second authority — every request carries a delegated CMS session
18
+ * (delegated adapters refuse sessionless before I/O; that stays) and the CMS
19
+ * decides what that session may write. We only reflect what the host can do at
20
+ * all (the capability axis). Pure function — trivially testable without I/O.
21
+ */
22
+ export function clampGrants(base, capabilities) {
23
+ const deny = (reason) => ({ allowed: false, reason });
24
+ const grants = { ...base };
25
+ if (capabilities.create.mode === "absent") {
26
+ grants.create = deny("create is not supported by this host");
27
+ }
28
+ if (capabilities.draftPublish.mode === "absent") {
29
+ grants.publish = deny("draft/publish is not supported by this host");
30
+ }
31
+ return grants;
32
+ }
33
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/content-source/permissions.ts"],"names":[],"mappings":"AA2BA,MAAM,CAAC,MAAM,cAAc,GAA4B;IACrD,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,SAAS;CACV,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CACzB,IAAkB,EAClB,YAGC;IAED,MAAM,IAAI,GAAG,CAAC,MAAc,EAAe,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC3B,IAAI,YAAY,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,sCAAsC,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,YAAY,CAAC,YAAY,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,6CAA6C,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}