@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,49 @@
1
+ /**
2
+ * BindingResolutionService (decision 004 §2.2 + §3.2): the one facade every
3
+ * render path calls. Wave 1 = page-level needs + repeater row sets; wave N =
4
+ * per-item template needs, batched across ALL items of ALL repeaters at that
5
+ * depth — depth+1 loader calls for nesting depth d, never per-row I/O.
6
+ * Records binding->page dependencies as a by-product (invariant F).
7
+ */
8
+ import { type ContentType, type ContentTypeId, type DataView, type Instance, type Locale, type Page, type RepeaterNode, type ScopeStack, type TimeZone } from "@batterai/bind-core";
9
+ import type { ContentSource } from "../content-source/content-source";
10
+ import type { Viewer } from "../content-source/viewer";
11
+ import type { BindingDependencyGraph } from "../cache/dependency-graph";
12
+ export interface RenderRequestCtx {
13
+ readonly locale: Locale;
14
+ readonly fallbackLocale?: Locale;
15
+ readonly timeZone: TimeZone;
16
+ readonly viewer: Viewer;
17
+ }
18
+ export interface RepeaterRender {
19
+ readonly node: RepeaterNode;
20
+ readonly items: readonly Instance[];
21
+ readonly itemStacks: readonly ScopeStack[];
22
+ readonly cursor?: string;
23
+ readonly hasMore: boolean;
24
+ }
25
+ export interface ResolvedPage {
26
+ readonly view: DataView;
27
+ readonly pageStack: ScopeStack;
28
+ readonly repeaters: ReadonlyMap<string, RepeaterRender>;
29
+ readonly loaderCalls: readonly string[];
30
+ }
31
+ export declare class BindingResolutionService {
32
+ private readonly source;
33
+ private readonly contentTypes;
34
+ private readonly depGraph?;
35
+ constructor(source: ContentSource, contentTypes: ReadonlyMap<ContentTypeId, ContentType>, depGraph?: BindingDependencyGraph | undefined);
36
+ private ctxOf;
37
+ resolvePage(page: Page, ctx: RenderRequestCtx, detail?: {
38
+ contentTypeId: ContentTypeId;
39
+ instanceId: Instance["id"];
40
+ }): Promise<ResolvedPage>;
41
+ /** Infinite-scroll append (Phase 7): resolve ONE top-level repeater's next
42
+ * page through the SAME loader/query machinery as the initial render —
43
+ * never a bespoke lighter path that could drift (invariant A append). */
44
+ resolveNextPage(page: Page, repeaterId: string, cursor: string, ctx: RenderRequestCtx): Promise<{
45
+ render: RepeaterRender;
46
+ view: DataView;
47
+ } | undefined>;
48
+ }
49
+ //# sourceMappingURL=resolution-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolution-service.d.ts","sourceRoot":"","sources":["../../src/resolver/resolution-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,aAAa,EAElB,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,IAAI,EAGT,KAAK,YAAY,EAEjB,KAAK,UAAU,EACf,KAAK,QAAQ,EACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAWxE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,SAAS,UAAU,EAAE,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACxD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAcD,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAFT,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,WAAW,CAAC,aAAa,EAAE,WAAW,CAAC,EACrD,QAAQ,CAAC,EAAE,sBAAsB,YAAA;IAGpD,OAAO,CAAC,KAAK;IAUP,WAAW,CACf,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,gBAAgB,EACrB,MAAM,CAAC,EAAE;QAAE,aAAa,EAAE,aAAa,CAAC;QAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;KAAE,GACpE,OAAO,CAAC,YAAY,CAAC;IAqHxB;;6EAEyE;IACnE,eAAe,CACnB,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC;QAAE,MAAM,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,GAAG,SAAS,CAAC;CA2BnE"}
@@ -0,0 +1,193 @@
1
+ /**
2
+ * BindingResolutionService (decision 004 §2.2 + §3.2): the one facade every
3
+ * render path calls. Wave 1 = page-level needs + repeater row sets; wave N =
4
+ * per-item template needs, batched across ALL items of ALL repeaters at that
5
+ * depth — depth+1 loader calls for nesting depth d, never per-row I/O.
6
+ * Records binding->page dependencies as a by-product (invariant F).
7
+ */
8
+ import { planDataNeeds, } from "@batterai/bind-core";
9
+ import { buildDetailStack, buildPageStack, fillStructural, itemStack, } from "../scope-engine/build-stack.js";
10
+ import { applyRepeaterQuery } from "../scope-engine/repeater-query.js";
11
+ import { HostDataLoader } from "./data-loader.js";
12
+ function collectRepeaters(nodes, out = []) {
13
+ for (const n of nodes) {
14
+ if (n.kind === "repeater") {
15
+ out.push(n);
16
+ collectRepeaters([n.itemTemplate], out);
17
+ }
18
+ else if (n.children) {
19
+ collectRepeaters(n.children, out);
20
+ }
21
+ }
22
+ return out;
23
+ }
24
+ export class BindingResolutionService {
25
+ source;
26
+ contentTypes;
27
+ depGraph;
28
+ constructor(source, contentTypes, depGraph) {
29
+ this.source = source;
30
+ this.contentTypes = contentTypes;
31
+ this.depGraph = depGraph;
32
+ }
33
+ ctxOf(ctx) {
34
+ // No viewer/scope cross-check (decision 012, invariant D4): data scoping is
35
+ // delegated to the CMS and the resolve pipeline carries no owner brand.
36
+ return {
37
+ locale: ctx.locale,
38
+ fallbackLocale: ctx.fallbackLocale,
39
+ timeZone: ctx.timeZone,
40
+ };
41
+ }
42
+ async resolvePage(page, ctx, detail) {
43
+ const planCtx = this.ctxOf(ctx);
44
+ const structural = detail
45
+ ? buildDetailStack(page, detail.contentTypeId)
46
+ : buildPageStack(page);
47
+ const loader = new HostDataLoader(this.source, ctx.viewer, this.contentTypes);
48
+ const needKeyOf = (node) => HostDataLoader.needKey({ kind: "instances", contentTypeId: node.boundType, query: node.query });
49
+ // ---- wave 1: page-level needs + the detail instance ------------------
50
+ const waveOneNeeds = [];
51
+ if (detail) {
52
+ waveOneNeeds.push({
53
+ kind: "instance",
54
+ contentTypeId: detail.contentTypeId,
55
+ instanceId: detail.instanceId,
56
+ });
57
+ }
58
+ const topRepeaters = collectRepeaters(page.root).filter((r) => !isNested(r, page.root));
59
+ for (const rep of topRepeaters) {
60
+ waveOneNeeds.push({ kind: "instances", contentTypeId: rep.boundType, query: rep.query });
61
+ }
62
+ // Page-level (non-repeater) block needs against the (partially) filled stack.
63
+ const filledHoles = new Map();
64
+ let view = await loader.load(waveOneNeeds, planCtx);
65
+ if (detail) {
66
+ const inst = view.getInstance(detail.contentTypeId, detail.instanceId);
67
+ if (!inst) {
68
+ // Loader contract already threw UnsatisfiableNeed if missing; this is
69
+ // a type-level guard only.
70
+ throw new Error("detail instance missing after load");
71
+ }
72
+ const ct = this.contentTypes.get(detail.contentTypeId);
73
+ const holeId = structural.find((f) => f.kind === "instance-hole").scopeId;
74
+ filledHoles.set(holeId, { instance: inst, contentType: ct });
75
+ this.depGraph?.recordDependency(page.id, {
76
+ kind: "instance",
77
+ contentTypeId: detail.contentTypeId,
78
+ instanceId: detail.instanceId,
79
+ });
80
+ }
81
+ const pageStack = fillStructural(structural, filledHoles);
82
+ // Plan the non-repeater needs of the page tree now that the root stack is real.
83
+ const pageNeeds = [];
84
+ for (const node of page.root) {
85
+ pageNeeds.push(...planDataNeeds(node, pageStack, planCtx));
86
+ }
87
+ if (pageNeeds.length > 0)
88
+ view = await loader.load(pageNeeds, planCtx);
89
+ // ---- wave 2..depth+1: repeater items, batched per depth --------------
90
+ const repeaters = new Map();
91
+ let frontier = topRepeaters.map((node) => ({
92
+ node,
93
+ parent: structural,
94
+ }));
95
+ while (frontier.length > 0) {
96
+ const next = [];
97
+ const waveNeeds = [];
98
+ const perRepeater = [];
99
+ for (const entry of frontier) {
100
+ const { node } = entry;
101
+ const ct = this.contentTypes.get(node.boundType);
102
+ if (!ct)
103
+ throw new Error(`unknown content type ${node.boundType}`);
104
+ const raw = view.getInstancePage(node.boundType, needKeyOf(node)) ?? [];
105
+ // Invariant I: filter -> sort -> pageSize against candidates.
106
+ const items = applyRepeaterQuery(raw, node.query, (inst, i) => itemStack(entry.parent, node, i, inst, ct), view);
107
+ const stacks = items.map((inst, i) => itemStack(entry.parent, node, i, inst, ct));
108
+ perRepeater.push({ entry, items, stacks });
109
+ this.depGraph?.recordDependency(page.id, {
110
+ kind: "instance",
111
+ contentTypeId: node.boundType,
112
+ instanceId: "*", // content-type-granular (conservative)
113
+ });
114
+ // Union the item templates' needs across ALL items (batched wave).
115
+ items.forEach((_inst, i) => {
116
+ waveNeeds.push(...planDataNeeds(node.itemTemplate, stacks[i], planCtx));
117
+ });
118
+ // Discover nested repeaters for the next depth.
119
+ for (const nested of collectRepeaters([node.itemTemplate]).filter((r) => r !== node)) {
120
+ waveNeeds.push({ kind: "instances", contentTypeId: nested.boundType, query: nested.query });
121
+ next.push({ node: nested, parent: entry.parent });
122
+ }
123
+ }
124
+ if (waveNeeds.length > 0)
125
+ view = await loader.load(waveNeeds, planCtx);
126
+ for (const { entry, items, stacks } of perRepeater) {
127
+ const meta = loader.pageMeta(needKeyOf(entry.node));
128
+ repeaters.set(entry.node.id, {
129
+ node: entry.node,
130
+ items,
131
+ itemStacks: stacks,
132
+ cursor: meta?.cursor,
133
+ hasMore: meta?.hasMore ?? false,
134
+ });
135
+ }
136
+ frontier = next;
137
+ }
138
+ return { view, pageStack, repeaters, loaderCalls: loader.callLog };
139
+ }
140
+ /** Infinite-scroll append (Phase 7): resolve ONE top-level repeater's next
141
+ * page through the SAME loader/query machinery as the initial render —
142
+ * never a bespoke lighter path that could drift (invariant A append). */
143
+ async resolveNextPage(page, repeaterId, cursor, ctx) {
144
+ const planCtx = this.ctxOf(ctx);
145
+ const node = collectRepeaters(page.root).find((r) => r.id === repeaterId);
146
+ if (!node)
147
+ return undefined;
148
+ const structural = buildPageStack(page);
149
+ const loader = new HostDataLoader(this.source, ctx.viewer, this.contentTypes);
150
+ const need = {
151
+ kind: "instances",
152
+ contentTypeId: node.boundType,
153
+ query: node.query,
154
+ cursor,
155
+ };
156
+ let view = await loader.load([need], planCtx);
157
+ const ct = this.contentTypes.get(node.boundType);
158
+ if (!ct)
159
+ throw new Error(`unknown content type ${node.boundType}`);
160
+ const raw = view.getInstancePage(node.boundType, HostDataLoader.needKey(need)) ?? [];
161
+ const items = applyRepeaterQuery(raw, node.query, (inst, i) => itemStack(structural, node, i, inst, ct), view);
162
+ const stacks = items.map((inst, i) => itemStack(structural, node, i, inst, ct));
163
+ const waveNeeds = [];
164
+ items.forEach((_inst, i) => waveNeeds.push(...planDataNeeds(node.itemTemplate, stacks[i], planCtx)));
165
+ if (waveNeeds.length > 0)
166
+ view = await loader.load(waveNeeds, planCtx);
167
+ const meta = loader.pageMeta(HostDataLoader.needKey(need));
168
+ return {
169
+ render: { node, items, itemStacks: stacks, cursor: meta?.cursor, hasMore: meta?.hasMore ?? false },
170
+ view,
171
+ };
172
+ }
173
+ }
174
+ function isNested(rep, roots) {
175
+ // A repeater is nested iff it appears inside another repeater's itemTemplate.
176
+ const walk = (nodes, insideRepeater) => {
177
+ for (const n of nodes) {
178
+ if (n.kind === "repeater") {
179
+ if (n === rep)
180
+ return insideRepeater;
181
+ if (walk([n.itemTemplate], true))
182
+ return true;
183
+ }
184
+ else if (n.children) {
185
+ if (walk(n.children, insideRepeater))
186
+ return true;
187
+ }
188
+ }
189
+ return false;
190
+ };
191
+ return walk(roots, false);
192
+ }
193
+ //# sourceMappingURL=resolution-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolution-service.js","sourceRoot":"","sources":["../../src/resolver/resolution-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * RouteResolver (decision 004 §6): path -> page | detail+instance,
3
+ * preview-vs-public modes, real 404 semantics. Public mode reads ONLY
4
+ * published snapshots. Tenancy is delegated to the CMS (decision 012): the
5
+ * resolver never gates on identity — the CMS authorizes each delegated read.
6
+ */
7
+ import type { ContentTypeId, InstanceId, Page, PageId } from "@batterai/bind-core";
8
+ import type { Viewer } from "../content-source/viewer";
9
+ import type { ContentSource } from "../content-source/content-source";
10
+ import type { LifecycleStore } from "../lifecycle/lifecycle-store";
11
+ import type { PageMetadataModel } from "../seo/metadata";
12
+ import type { CanvasPage } from "@batterai/contract";
13
+ export type RequestMode = "public" | "preview";
14
+ export type RouteMatch = {
15
+ readonly kind: "page";
16
+ readonly page: Page;
17
+ readonly metadata?: PageMetadataModel;
18
+ } | {
19
+ readonly kind: "detail";
20
+ readonly page: Page;
21
+ readonly metadata?: PageMetadataModel;
22
+ readonly contentTypeId: ContentTypeId;
23
+ readonly instanceId: InstanceId;
24
+ } | {
25
+ readonly kind: "not-found";
26
+ } | {
27
+ readonly kind: "forbidden";
28
+ readonly reason: string;
29
+ };
30
+ /** The lifecycle unit: page tree + its metadata versioned TOGETHER (decision
31
+ * 004 §7.1 — a rollback can't leave stale bound metadata behind). */
32
+ export interface PageRecord {
33
+ readonly page: Page;
34
+ readonly metadata?: PageMetadataModel;
35
+ /** Canvas-owned page fields versioned in the same durable collaboration
36
+ * record as the ordered block tree. CMS authority fields never belong here. */
37
+ readonly canvasSettings?: Omit<CanvasPage, "id" | "root">;
38
+ }
39
+ export interface DetailRoutePattern {
40
+ /** e.g. "/nieuws/" — a path prefix; the remainder is the instance slug/id. */
41
+ readonly prefix: string;
42
+ readonly contentTypeId: ContentTypeId;
43
+ readonly detailPageId: PageId;
44
+ /** Which field carries the slug; "id" means the raw instance id. */
45
+ readonly slugField: string;
46
+ }
47
+ export interface RouteRegistry {
48
+ /** Static page routes: path -> pageId. */
49
+ readonly pages: ReadonlyMap<string, PageId>;
50
+ readonly detailPatterns: readonly DetailRoutePattern[];
51
+ }
52
+ export interface RouteResolverDeps {
53
+ readonly registry: RouteRegistry | undefined;
54
+ readonly lifecycle: LifecycleStore<PageRecord>;
55
+ readonly source: ContentSource;
56
+ /** Resolve a slug to an instance id (adapter-backed; "id" slugField skips). */
57
+ readonly findBySlug: (contentTypeId: ContentTypeId, slugField: string, slug: string, viewer: Viewer) => Promise<InstanceId | undefined>;
58
+ }
59
+ export declare function createRouteResolver(deps: RouteResolverDeps): {
60
+ resolve(path: string, mode: RequestMode, viewer: Viewer): Promise<RouteMatch>;
61
+ /** Public-mode routable URLs — the sitemap's data source. Draft-only and
62
+ * unpublished content never appears. */
63
+ listPublicRoutes(viewer: Viewer): AsyncIterable<{
64
+ path: string;
65
+ lastModified: string;
66
+ }>;
67
+ /** page-id -> public href lookup for portable internal links. Detail
68
+ * routes remain instance-driven; this helper only maps static page ids
69
+ * present in the route registry. */
70
+ hrefForPage(pageId: PageId): string | undefined;
71
+ };
72
+ export type RouteResolver = ReturnType<typeof createRouteResolver>;
73
+ //# sourceMappingURL=route-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-resolver.d.ts","sourceRoot":"","sources":["../../src/routing/route-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ function instanceLastModified(inst, fallback) {
2
+ for (const field of ["updatedAt", "publishedAt", "date"]) {
3
+ const value = inst.values[field];
4
+ if (typeof value === "string" && !Number.isNaN(Date.parse(value)))
5
+ return value;
6
+ }
7
+ return fallback;
8
+ }
9
+ export function createRouteResolver(deps) {
10
+ async function loadPage(pageId, mode, viewer) {
11
+ if (mode === "preview") {
12
+ // Preview requires an authenticated viewer — checked by resolve().
13
+ return ((await deps.lifecycle.getDraft({ kind: "page", pageId }, viewer)) ??
14
+ (await deps.lifecycle.getPublished({ kind: "page", pageId }))?.data);
15
+ }
16
+ // Public: ONLY published snapshots; never a draft fallback.
17
+ return (await deps.lifecycle.getPublished({ kind: "page", pageId }))?.data;
18
+ }
19
+ return {
20
+ async resolve(path, mode, viewer) {
21
+ // No identity gate here (decision 012, invariant D4): every Viewer carries
22
+ // a delegated CMS session, and the CMS authorizes each draft/published
23
+ // read (getDraft/getPublished delegate the session). battery-editor is not
24
+ // a second authority — it never refuses on its own.
25
+ const registry = deps.registry;
26
+ if (!registry)
27
+ return { kind: "not-found" };
28
+ const normalized = path !== "/" && path.endsWith("/") ? path.slice(0, -1) : path;
29
+ const pageId = registry.pages.get(normalized);
30
+ if (pageId) {
31
+ const record = await loadPage(pageId, mode, viewer);
32
+ if (!record)
33
+ return { kind: "not-found" }; // never published => 404, not an empty shell
34
+ return { kind: "page", page: record.page, metadata: record.metadata };
35
+ }
36
+ for (const pattern of registry.detailPatterns) {
37
+ if (!normalized.startsWith(pattern.prefix))
38
+ continue;
39
+ const slug = normalized.slice(pattern.prefix.length);
40
+ if (!slug || slug.includes("/"))
41
+ continue;
42
+ const instanceId = pattern.slugField === "id"
43
+ ? slug
44
+ : await deps.findBySlug(pattern.contentTypeId, pattern.slugField, slug, viewer);
45
+ if (!instanceId)
46
+ return { kind: "not-found" };
47
+ const exists = await deps.source.getInstance(pattern.contentTypeId, instanceId, viewer);
48
+ if (!exists)
49
+ return { kind: "not-found" };
50
+ const record = await loadPage(pattern.detailPageId, mode, viewer);
51
+ if (!record)
52
+ return { kind: "not-found" };
53
+ return {
54
+ kind: "detail",
55
+ page: record.page,
56
+ metadata: record.metadata,
57
+ contentTypeId: pattern.contentTypeId,
58
+ instanceId,
59
+ };
60
+ }
61
+ return { kind: "not-found" };
62
+ },
63
+ /** Public-mode routable URLs — the sitemap's data source. Draft-only and
64
+ * unpublished content never appears. */
65
+ async *listPublicRoutes(viewer) {
66
+ const registry = deps.registry;
67
+ if (!registry)
68
+ return;
69
+ for (const [path, pageId] of registry.pages) {
70
+ const snap = await deps.lifecycle.getPublished({ kind: "page", pageId });
71
+ if (snap)
72
+ yield { path, lastModified: snap.publishedAt };
73
+ }
74
+ for (const pattern of registry.detailPatterns) {
75
+ const detailPublished = await deps.lifecycle.getPublished({
76
+ kind: "page",
77
+ pageId: pattern.detailPageId,
78
+ });
79
+ if (!detailPublished)
80
+ continue;
81
+ let cursor;
82
+ do {
83
+ const page = await deps.source.listInstances(pattern.contentTypeId, { pageSize: 100 }, cursor, viewer);
84
+ for (const inst of page.items) {
85
+ const slug = pattern.slugField === "id"
86
+ ? inst.id
87
+ : String(inst.values[pattern.slugField] ?? "");
88
+ if (slug) {
89
+ yield {
90
+ path: `${pattern.prefix}${slug}`,
91
+ lastModified: instanceLastModified(inst, detailPublished.publishedAt),
92
+ };
93
+ }
94
+ }
95
+ cursor = page.hasMore ? page.cursor : undefined;
96
+ } while (cursor);
97
+ }
98
+ },
99
+ /** page-id -> public href lookup for portable internal links. Detail
100
+ * routes remain instance-driven; this helper only maps static page ids
101
+ * present in the route registry. */
102
+ hrefForPage(pageId) {
103
+ const registry = deps.registry;
104
+ if (!registry)
105
+ return undefined;
106
+ for (const [path, candidate] of registry.pages) {
107
+ if (candidate === pageId)
108
+ return path;
109
+ }
110
+ return undefined;
111
+ },
112
+ };
113
+ }
114
+ //# sourceMappingURL=route-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-resolver.js","sourceRoot":"","sources":["../../src/routing/route-resolver.ts"],"names":[],"mappings":"AAiEA,SAAS,oBAAoB,CAAC,IAA4D,EAAE,QAAgB;IAC1G,KAAK,MAAM,KAAK,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,CAAC;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IAClF,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAuB;IACzD,KAAK,UAAU,QAAQ,CACrB,MAAc,EACd,IAAiB,EACjB,MAAc;QAEd,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,mEAAmE;YACnE,OAAO,CACL,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC;gBACjE,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,IAAI,CACpE,CAAC;QACJ,CAAC;QACD,4DAA4D;QAC5D,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC;IAC7E,CAAC;IAED,OAAO;QACL,KAAK,CAAC,OAAO,CACX,IAAY,EACZ,IAAiB,EACjB,MAAc;YAEd,2EAA2E;YAC3E,uEAAuE;YACvE,2EAA2E;YAC3E,oDAAoD;YACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC/B,IAAI,CAAC,QAAQ;gBAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YAE5C,MAAM,UAAU,GAAG,IAAI,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAEjF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC9C,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;gBACpD,IAAI,CAAC,MAAM;oBAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,6CAA6C;gBACxF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;YACxE,CAAC;YAED,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;gBAC9C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC;oBAAE,SAAS;gBACrD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACrD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAC1C,MAAM,UAAU,GACd,OAAO,CAAC,SAAS,KAAK,IAAI;oBACxB,CAAC,CAAE,IAAmB;oBACtB,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;gBACpF,IAAI,CAAC,UAAU;oBAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;gBAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;gBACxF,IAAI,CAAC,MAAM;oBAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;gBAC1C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;gBAClE,IAAI,CAAC,MAAM;oBAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;gBAC1C,OAAO;oBACL,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,aAAa,EAAE,OAAO,CAAC,aAAa;oBACpC,UAAU;iBACX,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;QAC/B,CAAC;QAED;gDACwC;QACxC,KAAK,CAAC,CAAC,gBAAgB,CACrB,MAAc;YAEd,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC/B,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACtB,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAC5C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBACzE,IAAI,IAAI;oBAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;YAC3D,CAAC;YACD,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;gBAC9C,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC;oBACxD,IAAI,EAAE,MAAM;oBACZ,MAAM,EAAE,OAAO,CAAC,YAAY;iBAC7B,CAAC,CAAC;gBACH,IAAI,CAAC,eAAe;oBAAE,SAAS;gBAC/B,IAAI,MAA0B,CAAC;gBAC/B,GAAG,CAAC;oBACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAC1C,OAAO,CAAC,aAAa,EACrB,EAAE,QAAQ,EAAE,GAAG,EAAE,EACjB,MAAM,EACN,MAAM,CACP,CAAC;oBACF,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;wBAC9B,MAAM,IAAI,GACR,OAAO,CAAC,SAAS,KAAK,IAAI;4BACxB,CAAC,CAAE,IAAI,CAAC,EAAa;4BACrB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;wBACnD,IAAI,IAAI,EAAE,CAAC;4BACT,MAAM;gCACJ,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,EAAE;gCAChC,YAAY,EAAE,oBAAoB,CAAC,IAAI,EAAE,eAAe,CAAC,WAAW,CAAC;6BACtE,CAAC;wBACJ,CAAC;oBACH,CAAC;oBACD,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;gBAClD,CAAC,QAAQ,MAAM,EAAE;YACnB,CAAC;QACH,CAAC;QAED;;4CAEoC;QACpC,WAAW,CAAC,MAAc;YACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC/B,IAAI,CAAC,QAAQ;gBAAE,OAAO,SAAS,CAAC;YAChC,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAC/C,IAAI,SAAS,KAAK,MAAM;oBAAE,OAAO,IAAI,CAAC;YACxC,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Structural scope-stack construction (decision 004 §2.1). Pure sync tree
3
+ * shaping; instances attach in the planning waves. StructuralStack is a
4
+ * distinct type so the compiler forces callers through the fill functions
5
+ * before treating a stack as resolve()-ready.
6
+ */
7
+ import { type ContentType, type ContentTypeId, type Instance, type Page, type RepeaterNode, type ScopeId, type ScopeStack } from "@batterai/bind-core";
8
+ export type StructuralFrame = {
9
+ readonly kind: "page";
10
+ readonly scopeId: ScopeId;
11
+ readonly page: Page;
12
+ } | {
13
+ readonly kind: "repeater-hole";
14
+ readonly scopeId: ScopeId;
15
+ readonly node: RepeaterNode;
16
+ } | {
17
+ readonly kind: "instance-hole";
18
+ readonly scopeId: ScopeId;
19
+ readonly contentTypeId: ContentTypeId;
20
+ };
21
+ export type StructuralStack = readonly StructuralFrame[];
22
+ /**
23
+ * STABLE TEMPLATE SCOPE IDS (host convention, surfaced during the Phase-5
24
+ * build — a gap decision 004 didn't name): templates are authored ONCE but
25
+ * render for MANY instances, so the frame scopeId an authored chip matches
26
+ * must be stable across instances. Sentinels:
27
+ * detail template's instance frame : instance:<ct>:@detail
28
+ * repeater item's instance frame : <repeaterScope>>>>instance:<ct>:@item
29
+ * The frame's DATA varies per item; its identity does not. bind-core's exact
30
+ * findFrame match is untouched — this is purely how the host names frames.
31
+ */
32
+ export declare function detailScopeId(contentTypeId: ContentTypeId): ScopeId;
33
+ export declare function itemScopeId(repeaterScope: ScopeId, contentTypeId: ContentTypeId): ScopeId;
34
+ export declare function buildPageStack(page: Page): StructuralStack;
35
+ /** Detail-template root: page frame + one instance hole under the STABLE
36
+ * detail scope id (authored chips match every routed instance). */
37
+ export declare function buildDetailStack(page: Page, contentTypeId: ContentTypeId): StructuralStack;
38
+ /** Per-item descent for a repeater: repeater frame (index) + instance frame
39
+ * under the STABLE item scope id. Nested repeaters chain via the parent's
40
+ * item scope so ids never collide across nesting levels. */
41
+ export declare function itemStack(parent: StructuralStack, node: RepeaterNode, index: number, instance: Instance, contentType: ContentType): ScopeStack;
42
+ /** Fill a structural stack whose holes have known data into a real ScopeStack.
43
+ * Holes without data are DROPPED — resolve() throwing MissingData on a ref
44
+ * into a dropped frame is the honest failure, never a fabricated frame. */
45
+ export declare function fillStructural(stack: StructuralStack, instances?: ReadonlyMap<ScopeId, {
46
+ instance: Instance;
47
+ contentType: ContentType;
48
+ }>): ScopeStack;
49
+ //# sourceMappingURL=build-stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-stack.d.ts","sourceRoot":"","sources":["../../src/scope-engine/build-stack.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,IAAI,EACT,KAAK,YAAY,EAEjB,KAAK,OAAO,EACZ,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GACzE;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAC1F;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAA;CAAE,CAAC;AAEzG,MAAM,MAAM,eAAe,GAAG,SAAS,eAAe,EAAE,CAAC;AAEzD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAEnE;AAED,wBAAgB,WAAW,CAAC,aAAa,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,GAAG,OAAO,CAEzF;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,eAAe,CAE1D;AAED;mEACmE;AACnE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,aAAa,GAAG,eAAe,CAK1F;AAED;;4DAE4D;AAC5D,wBAAgB,SAAS,CACvB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,WAAW,GACvB,UAAU,CAcZ;AAED;;2EAE2E;AAC3E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,eAAe,EACtB,SAAS,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,WAAW,CAAA;CAAE,CAAC,GACjF,UAAU,CAmBZ"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Structural scope-stack construction (decision 004 §2.1). Pure sync tree
3
+ * shaping; instances attach in the planning waves. StructuralStack is a
4
+ * distinct type so the compiler forces callers through the fill functions
5
+ * before treating a stack as resolve()-ready.
6
+ */
7
+ import { instanceScopeId, nestedScopeId, pageScopeId, } from "@batterai/bind-core";
8
+ /**
9
+ * STABLE TEMPLATE SCOPE IDS (host convention, surfaced during the Phase-5
10
+ * build — a gap decision 004 didn't name): templates are authored ONCE but
11
+ * render for MANY instances, so the frame scopeId an authored chip matches
12
+ * must be stable across instances. Sentinels:
13
+ * detail template's instance frame : instance:<ct>:@detail
14
+ * repeater item's instance frame : <repeaterScope>>>>instance:<ct>:@item
15
+ * The frame's DATA varies per item; its identity does not. bind-core's exact
16
+ * findFrame match is untouched — this is purely how the host names frames.
17
+ */
18
+ export function detailScopeId(contentTypeId) {
19
+ return instanceScopeId(contentTypeId, "@detail");
20
+ }
21
+ export function itemScopeId(repeaterScope, contentTypeId) {
22
+ return nestedScopeId(repeaterScope, instanceScopeId(contentTypeId, "@item"));
23
+ }
24
+ export function buildPageStack(page) {
25
+ return [{ kind: "page", scopeId: pageScopeId(page.id), page }];
26
+ }
27
+ /** Detail-template root: page frame + one instance hole under the STABLE
28
+ * detail scope id (authored chips match every routed instance). */
29
+ export function buildDetailStack(page, contentTypeId) {
30
+ return [
31
+ { kind: "page", scopeId: pageScopeId(page.id), page },
32
+ { kind: "instance-hole", scopeId: detailScopeId(contentTypeId), contentTypeId },
33
+ ];
34
+ }
35
+ /** Per-item descent for a repeater: repeater frame (index) + instance frame
36
+ * under the STABLE item scope id. Nested repeaters chain via the parent's
37
+ * item scope so ids never collide across nesting levels. */
38
+ export function itemStack(parent, node, index, instance, contentType) {
39
+ const parentInstance = [...parent].reverse().find((f) => f.kind === "instance-hole");
40
+ const repScope = parentInstance ? nestedScopeId(parentInstance.scopeId, node.scopeId) : node.scopeId;
41
+ const filled = fillStructural(parent);
42
+ return [
43
+ ...filled,
44
+ { kind: "repeater", scopeId: repScope, index },
45
+ {
46
+ kind: "instance",
47
+ scopeId: itemScopeId(repScope, instance.contentTypeId),
48
+ instance,
49
+ contentType,
50
+ },
51
+ ];
52
+ }
53
+ /** Fill a structural stack whose holes have known data into a real ScopeStack.
54
+ * Holes without data are DROPPED — resolve() throwing MissingData on a ref
55
+ * into a dropped frame is the honest failure, never a fabricated frame. */
56
+ export function fillStructural(stack, instances) {
57
+ const out = [];
58
+ for (const f of stack) {
59
+ if (f.kind === "page")
60
+ out.push({ kind: "page", scopeId: f.scopeId, page: f.page });
61
+ else if (f.kind === "instance-hole") {
62
+ const hit = instances?.get(f.scopeId);
63
+ if (hit) {
64
+ out.push({
65
+ kind: "instance",
66
+ scopeId: f.scopeId,
67
+ instance: hit.instance,
68
+ contentType: hit.contentType,
69
+ });
70
+ }
71
+ }
72
+ // repeater-holes never appear in a resolve-ready stack; itemStack builds
73
+ // real repeater frames per item.
74
+ }
75
+ return out;
76
+ }
77
+ //# sourceMappingURL=build-stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-stack.js","sourceRoot":"","sources":["../../src/scope-engine/build-stack.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,GASZ,MAAM,qBAAqB,CAAC;AAS7B;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,aAA4B;IACxD,OAAO,eAAe,CAAC,aAAa,EAAE,SAA2B,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,aAAsB,EAAE,aAA4B;IAC9E,OAAO,aAAa,CAAC,aAAa,EAAE,eAAe,CAAC,aAAa,EAAE,OAAyB,CAAC,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAU;IACvC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AACjE,CAAC;AAED;mEACmE;AACnE,MAAM,UAAU,gBAAgB,CAAC,IAAU,EAAE,aAA4B;IACvE,OAAO;QACL,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE;QACrD,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,aAAa,EAAE;KAChF,CAAC;AACJ,CAAC;AAED;;4DAE4D;AAC5D,MAAM,UAAU,SAAS,CACvB,MAAuB,EACvB,IAAkB,EAClB,KAAa,EACb,QAAkB,EAClB,WAAwB;IAExB,MAAM,cAAc,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;IACrG,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,OAAO;QACL,GAAG,MAAM;QACT,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC9C;YACE,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,aAAa,CAAC;YACtD,QAAQ;YACR,WAAW;SACZ;KACF,CAAC;AACJ,CAAC;AAED;;2EAE2E;AAC3E,MAAM,UAAU,cAAc,CAC5B,KAAsB,EACtB,SAAkF;IAElF,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;aAC/E,IAAI,CAAC,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACtC,IAAI,GAAG,EAAE,CAAC;gBACR,GAAG,CAAC,IAAI,CAAC;oBACP,IAAI,EAAE,UAAU;oBAChB,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,WAAW,EAAE,GAAG,CAAC,WAAW;iBAC7B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,yEAAyE;QACzE,iCAAiC;IACnC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Invariant-I filter/sort/limit (decision 004 §2.3): the authoritative,
3
+ * adapter-independent truth pass using bind-core's evaluators — one engine,
4
+ * never N adapter dialects. Order of operations is contract: filter -> sort
5
+ * -> slice(pageSize) — pagination never truncates a filtered set early.
6
+ */
7
+ import { type DataView, type Instance, type RepeaterQuery, type ScopeStack } from "@batterai/bind-core";
8
+ export declare function applyRepeaterQuery(candidates: readonly Instance[], query: RepeaterQuery, itemStackFor: (instance: Instance, index: number) => ScopeStack, data: DataView): readonly Instance[];
9
+ //# sourceMappingURL=repeater-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repeater-query.d.ts","sourceRoot":"","sources":["../../src/scope-engine/repeater-query.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAE7B,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,SAAS,QAAQ,EAAE,EAC/B,KAAK,EAAE,aAAa,EACpB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,UAAU,EAC/D,IAAI,EAAE,QAAQ,GACb,SAAS,QAAQ,EAAE,CAgCrB"}