@loomwork/blueprint-runtime 0.0.0-stage → 0.1.1

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 (117) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +83 -2
  4. package/dist/Blueprint.d.ts +14 -0
  5. package/dist/Blueprint.d.ts.map +1 -0
  6. package/dist/Blueprint.js +18 -0
  7. package/dist/Blueprint.js.map +1 -0
  8. package/dist/RendererRegistry.d.ts +9 -0
  9. package/dist/RendererRegistry.d.ts.map +1 -0
  10. package/dist/RendererRegistry.js +39 -0
  11. package/dist/RendererRegistry.js.map +1 -0
  12. package/dist/catalog.d.ts +129 -0
  13. package/dist/catalog.d.ts.map +1 -0
  14. package/dist/catalog.js +435 -0
  15. package/dist/catalog.js.map +1 -0
  16. package/dist/coverage.d.ts +18 -0
  17. package/dist/coverage.d.ts.map +1 -0
  18. package/dist/coverage.js +22 -0
  19. package/dist/coverage.js.map +1 -0
  20. package/dist/customWidget.d.ts +10 -0
  21. package/dist/customWidget.d.ts.map +1 -0
  22. package/dist/customWidget.js +34 -0
  23. package/dist/customWidget.js.map +1 -0
  24. package/dist/esm/Blueprint.js +15 -0
  25. package/dist/esm/Blueprint.js.map +1 -0
  26. package/dist/esm/RendererRegistry.js +35 -0
  27. package/dist/esm/RendererRegistry.js.map +1 -0
  28. package/dist/esm/catalog.js +430 -0
  29. package/dist/esm/catalog.js.map +1 -0
  30. package/dist/esm/coverage.js +19 -0
  31. package/dist/esm/coverage.js.map +1 -0
  32. package/dist/esm/customWidget.js +31 -0
  33. package/dist/esm/customWidget.js.map +1 -0
  34. package/dist/esm/identity.js +116 -0
  35. package/dist/esm/identity.js.map +1 -0
  36. package/dist/esm/index.js +15 -0
  37. package/dist/esm/index.js.map +1 -0
  38. package/dist/esm/irVersion.js +150 -0
  39. package/dist/esm/irVersion.js.map +1 -0
  40. package/dist/esm/layout.js +63 -0
  41. package/dist/esm/layout.js.map +1 -0
  42. package/dist/esm/normalize.js +115 -0
  43. package/dist/esm/normalize.js.map +1 -0
  44. package/dist/esm/pins.js +82 -0
  45. package/dist/esm/pins.js.map +1 -0
  46. package/dist/esm/traverseBlueprint.js +38 -0
  47. package/dist/esm/traverseBlueprint.js.map +1 -0
  48. package/dist/esm/traverseBlueprintAsync.js +48 -0
  49. package/dist/esm/traverseBlueprintAsync.js.map +1 -0
  50. package/dist/esm/types.js +2 -0
  51. package/dist/esm/types.js.map +1 -0
  52. package/dist/esm/widgetStatus.js +137 -0
  53. package/dist/esm/widgetStatus.js.map +1 -0
  54. package/dist/esm/widgets.js +105 -0
  55. package/dist/esm/widgets.js.map +1 -0
  56. package/dist/identity.d.ts +58 -0
  57. package/dist/identity.d.ts.map +1 -0
  58. package/dist/identity.js +122 -0
  59. package/dist/identity.js.map +1 -0
  60. package/dist/index.d.ts +24 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +61 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/irVersion.d.ts +23 -0
  65. package/dist/irVersion.d.ts.map +1 -0
  66. package/dist/irVersion.js +153 -0
  67. package/dist/irVersion.js.map +1 -0
  68. package/dist/layout.d.ts +57 -0
  69. package/dist/layout.d.ts.map +1 -0
  70. package/dist/layout.js +71 -0
  71. package/dist/layout.js.map +1 -0
  72. package/dist/normalize.d.ts +29 -0
  73. package/dist/normalize.d.ts.map +1 -0
  74. package/dist/normalize.js +126 -0
  75. package/dist/normalize.js.map +1 -0
  76. package/dist/pins.d.ts +27 -0
  77. package/dist/pins.d.ts.map +1 -0
  78. package/dist/pins.js +86 -0
  79. package/dist/pins.js.map +1 -0
  80. package/dist/traverseBlueprint.d.ts +27 -0
  81. package/dist/traverseBlueprint.d.ts.map +1 -0
  82. package/dist/traverseBlueprint.js +40 -0
  83. package/dist/traverseBlueprint.js.map +1 -0
  84. package/dist/traverseBlueprintAsync.d.ts +18 -0
  85. package/dist/traverseBlueprintAsync.d.ts.map +1 -0
  86. package/dist/traverseBlueprintAsync.js +51 -0
  87. package/dist/traverseBlueprintAsync.js.map +1 -0
  88. package/dist/types.d.ts +176 -0
  89. package/dist/types.d.ts.map +1 -0
  90. package/dist/types.js +3 -0
  91. package/dist/types.js.map +1 -0
  92. package/dist/widgetStatus.d.ts +66 -0
  93. package/dist/widgetStatus.d.ts.map +1 -0
  94. package/dist/widgetStatus.js +141 -0
  95. package/dist/widgetStatus.js.map +1 -0
  96. package/dist/widgets.d.ts +101 -0
  97. package/dist/widgets.d.ts.map +1 -0
  98. package/dist/widgets.js +115 -0
  99. package/dist/widgets.js.map +1 -0
  100. package/package.json +60 -3
  101. package/src/Blueprint.ts +28 -0
  102. package/src/RendererRegistry.ts +38 -0
  103. package/src/catalog.ts +638 -0
  104. package/src/coverage.ts +52 -0
  105. package/src/customWidget.ts +42 -0
  106. package/src/identity.ts +178 -0
  107. package/src/index.ts +84 -0
  108. package/src/irVersion.ts +195 -0
  109. package/src/layout.ts +105 -0
  110. package/src/normalize.ts +130 -0
  111. package/src/pins.ts +84 -0
  112. package/src/traverseBlueprint.bench.ts +47 -0
  113. package/src/traverseBlueprint.ts +94 -0
  114. package/src/traverseBlueprintAsync.ts +92 -0
  115. package/src/types.ts +195 -0
  116. package/src/widgetStatus.ts +231 -0
  117. package/src/widgets.ts +213 -0
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BLUEPRINT_IR_SHAPE = exports.BLUEPRINT_IR_VERSION = void 0;
4
+ // ── The Blueprint as a stable rendering IR (ADR 0096) ─────────────────────────
5
+ //
6
+ // A `Blueprint` used to be a value one app authored and one renderer drew. Once an external
7
+ // framework COMPILES to it, its shape is an interface others depend on, and a field added or
8
+ // renamed here is a breaking change in a repository nothing in this workspace builds.
9
+ //
10
+ // So the shape is declared twice, on purpose. The types below are the truth; the manifests are a
11
+ // second spelling that `tsc` checks against them exhaustively — `AllKeys<T>` makes every key
12
+ // required, and `satisfies` on an object literal rejects an excess key — so any change to
13
+ // `Widget`, `Layout` or `Blueprint` fails the build until the manifest agrees. `irVersion.test.ts`
14
+ // then pins a digest of the manifests to BLUEPRINT_IR_VERSION, so the manifest cannot change
15
+ // without the version changing either. Neither half is sufficient alone.
16
+ //
17
+ // What this guards: field presence, field optionality (a required↔optional flip fails the
18
+ // build), the WidgetKind literals, the layout `type` literals. What it does not: a field's type
19
+ // being narrowed or widened, nor a `readonly` change. A consumer's typecheck catches those.
20
+ //
21
+ // Adding a Layout variant means adding its field manifest here too; `Record<Layout["type"], true>`
22
+ // catches the variant, not its fields.
23
+ exports.BLUEPRINT_IR_VERSION = "2.5.0";
24
+ const WIDGET = {
25
+ _tag: "required",
26
+ id: "required",
27
+ kind: "required",
28
+ title: "optional",
29
+ props: "required",
30
+ binding: "optional",
31
+ source: "optional",
32
+ };
33
+ const DATA_BINDING = {
34
+ projectionId: "required",
35
+ path: "required",
36
+ status: "optional",
37
+ sensitivity: "optional",
38
+ };
39
+ const BINDING_STATUS_REF = {
40
+ path: "required",
41
+ };
42
+ const BINDING_STATUS = {
43
+ state: "required",
44
+ seq: "optional",
45
+ finding: "optional",
46
+ };
47
+ // The NESTED shapes, manifested in their own right. `KeyShape<BindingStatus>` only records that
48
+ // `finding` exists and is optional — a key added to, removed from or flipped inside the finding
49
+ // (or a `degradedFrom` entry) is a change to the compile target that the parent manifest cannot
50
+ // see. A consumer reads `finding.code`/`d.type`, so those key sets are part of the contract.
51
+ const BINDING_FINDING = {
52
+ code: "required",
53
+ message: "required",
54
+ };
55
+ const WIDGET_SOURCE = {
56
+ type: "required",
57
+ version: "required",
58
+ degradedFrom: "optional",
59
+ redacted: "optional",
60
+ };
61
+ const DEGRADED_FROM = {
62
+ type: "required",
63
+ version: "required",
64
+ };
65
+ const GRID_CELL = {
66
+ span: "optional",
67
+ row: "optional",
68
+ rowSpan: "optional",
69
+ column: "optional",
70
+ node: "required",
71
+ };
72
+ const BINDING_STATES = {
73
+ pending: true,
74
+ ready: true,
75
+ stale: true,
76
+ error: true,
77
+ empty: true,
78
+ };
79
+ const GRID_LAYOUT = {
80
+ type: "required",
81
+ id: "optional",
82
+ columns: "required",
83
+ gap: "optional",
84
+ title: "optional",
85
+ role: "optional",
86
+ children: "required",
87
+ };
88
+ const FLEX_LAYOUT = {
89
+ type: "required",
90
+ id: "optional",
91
+ direction: "required",
92
+ gap: "optional",
93
+ title: "optional",
94
+ role: "optional",
95
+ children: "required",
96
+ };
97
+ const STACK_LAYOUT = {
98
+ type: "required",
99
+ id: "optional",
100
+ gap: "optional",
101
+ title: "optional",
102
+ role: "optional",
103
+ children: "required",
104
+ };
105
+ const BLUEPRINT = {
106
+ _tag: "required",
107
+ irVersion: "optional",
108
+ id: "required",
109
+ title: "required",
110
+ layout: "required",
111
+ projections: "required",
112
+ projectionVersions: "optional",
113
+ };
114
+ const WIDGET_KINDS = {
115
+ MetricCard: true,
116
+ Timeline: true,
117
+ ApprovalCard: true,
118
+ StatusBadge: true,
119
+ ProgressBar: true,
120
+ DataTable: true,
121
+ AlertCard: true,
122
+ StreamingText: true,
123
+ };
124
+ const LAYOUT_TYPES = { grid: true, flex: true, stack: true };
125
+ // Field manifests emit `"<key>:<required|optional>"` so an optional↔required flip changes the
126
+ // digest even though the key set itself is unchanged. The two literal-union manifests (kinds,
127
+ // layout types) have no optionality to capture, so they stay plain key lists.
128
+ const keysOf = (manifest) => Object.entries(manifest)
129
+ .map(([key, shape]) => `${key}:${shape}`)
130
+ .sort();
131
+ const plainKeysOf = (manifest) => Object.keys(manifest).sort();
132
+ /**
133
+ * The shape of the Blueprint IR at `BLUEPRINT_IR_VERSION`, as sorted key sets. Digest this
134
+ * (`fnv1a64(canonicalJson(BLUEPRINT_IR_SHAPE))`) to compare two builds' IR without a type checker.
135
+ */
136
+ exports.BLUEPRINT_IR_SHAPE = {
137
+ widget: keysOf(WIDGET),
138
+ dataBinding: keysOf(DATA_BINDING),
139
+ bindingStatusRef: keysOf(BINDING_STATUS_REF),
140
+ bindingStatus: keysOf(BINDING_STATUS),
141
+ bindingFinding: keysOf(BINDING_FINDING),
142
+ widgetSource: keysOf(WIDGET_SOURCE),
143
+ degradedFrom: keysOf(DEGRADED_FROM),
144
+ gridCell: keysOf(GRID_CELL),
145
+ gridLayout: keysOf(GRID_LAYOUT),
146
+ flexLayout: keysOf(FLEX_LAYOUT),
147
+ stackLayout: keysOf(STACK_LAYOUT),
148
+ blueprint: keysOf(BLUEPRINT),
149
+ widgetKinds: plainKeysOf(WIDGET_KINDS),
150
+ layoutTypes: plainKeysOf(LAYOUT_TYPES),
151
+ bindingStates: plainKeysOf(BINDING_STATES),
152
+ };
153
+ //# sourceMappingURL=irVersion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"irVersion.js","sourceRoot":"","sources":["../src/irVersion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { FlexLayout, GridCell, GridLayout, Layout, StackLayout, Widget } from "./types";
2
+ /**
3
+ * Whether `value` is a well-formed layout role (ADR 0099 D2): `/^[a-z][a-z0-9-]{0,63}$/`. A
4
+ * renderer looks a container's role up only when this holds; anything else — absent, empty,
5
+ * a non-string, `"Stepped"`, a 65-character string — draws the plain container, exactly as an
6
+ * unknown role does. It is the ONE spelling of the rule, so a compiler's schema and a
7
+ * renderer's lookup cannot drift apart.
8
+ */
9
+ export declare function isLayoutRole(value: unknown): value is string;
10
+ /**
11
+ * Whether `value` is a well-formed layout id (IR 2.4.0, ADR 0101): the same rule as `Widget.id`,
12
+ * a non-empty string — no charset, no length cap. Anything else — absent, `""`, a number, `null`
13
+ * — is treated as ABSENT by every reader: the container is keyed positionally, the identity walk
14
+ * skips it, and it collides with nothing. It is the ONE spelling of the rule, so a compiler's
15
+ * schema and a renderer's keying cannot drift apart (as `isLayoutRole` is for roles).
16
+ */
17
+ export declare function isLayoutId(value: unknown): value is string;
18
+ /** `isLayoutId` on a node's `id`, narrowing the node. Internal. */
19
+ export declare function hasLayoutId(layout: unknown): layout is {
20
+ id: string;
21
+ };
22
+ export interface GridOptions {
23
+ /** This container's stable identity, one namespace with widget ids (IR 2.4.0, ADR 0101). */
24
+ id?: string;
25
+ columns: number;
26
+ gap?: number;
27
+ /** A section heading (IR 2.1.0). */
28
+ title?: string;
29
+ /** What this container is to a renderer that knows it (IR 2.2.0; see `isLayoutRole`). */
30
+ role?: string;
31
+ children: Array<GridCell | Widget | Layout>;
32
+ }
33
+ export declare function grid(options: GridOptions): GridLayout;
34
+ export interface FlexOptions {
35
+ /** This container's stable identity, one namespace with widget ids (IR 2.4.0, ADR 0101). */
36
+ id?: string;
37
+ direction?: "row" | "column";
38
+ gap?: number;
39
+ /** A section heading (IR 2.1.0). */
40
+ title?: string;
41
+ /** What this container is to a renderer that knows it (IR 2.2.0; see `isLayoutRole`). */
42
+ role?: string;
43
+ children: Array<Widget | Layout>;
44
+ }
45
+ export declare function flex(options: FlexOptions): FlexLayout;
46
+ export interface StackOptions {
47
+ /** This container's stable identity, one namespace with widget ids (IR 2.4.0, ADR 0101). */
48
+ id?: string;
49
+ gap?: number;
50
+ /** A section heading (IR 2.1.0). */
51
+ title?: string;
52
+ /** What this container is to a renderer that knows it (IR 2.2.0; see `isLayoutRole`). */
53
+ role?: string;
54
+ children: Array<Widget | Layout>;
55
+ }
56
+ export declare function stack(options: StackOptions): StackLayout;
57
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAQ7F;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE5D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE1D;AAED,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAErE;AAED,MAAM,WAAW,WAAW;IAC1B,4FAA4F;IAC5F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC;CAC7C;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAUrD;AAED,MAAM,WAAW,WAAW;IAC1B,4FAA4F;IAC5F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CAClC;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAUrD;AAED,MAAM,WAAW,YAAY;IAC3B,4FAA4F;IAC5F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CAClC;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW,CASxD"}
package/dist/layout.js ADDED
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isLayoutRole = isLayoutRole;
4
+ exports.isLayoutId = isLayoutId;
5
+ exports.hasLayoutId = hasLayoutId;
6
+ exports.grid = grid;
7
+ exports.flex = flex;
8
+ exports.stack = stack;
9
+ const normalize_1 = require("./normalize");
10
+ // ADR 0099 D2 — a role is a registry KEY, so it has a key's shape: a lowercase ASCII letter,
11
+ // then lowercase letters, digits or hyphens, at most 64 characters in all. No `_`, `.` or
12
+ // uppercase, so `__proto__`/`constructor` can never be one and a role never needs escaping in a
13
+ // CSS attribute selector (`[data-container-role=stepped]`) or a log line.
14
+ const LAYOUT_ROLE = /^[a-z][a-z0-9-]{0,63}$/;
15
+ /**
16
+ * Whether `value` is a well-formed layout role (ADR 0099 D2): `/^[a-z][a-z0-9-]{0,63}$/`. A
17
+ * renderer looks a container's role up only when this holds; anything else — absent, empty,
18
+ * a non-string, `"Stepped"`, a 65-character string — draws the plain container, exactly as an
19
+ * unknown role does. It is the ONE spelling of the rule, so a compiler's schema and a
20
+ * renderer's lookup cannot drift apart.
21
+ */
22
+ function isLayoutRole(value) {
23
+ return typeof value === "string" && LAYOUT_ROLE.test(value);
24
+ }
25
+ /**
26
+ * Whether `value` is a well-formed layout id (IR 2.4.0, ADR 0101): the same rule as `Widget.id`,
27
+ * a non-empty string — no charset, no length cap. Anything else — absent, `""`, a number, `null`
28
+ * — is treated as ABSENT by every reader: the container is keyed positionally, the identity walk
29
+ * skips it, and it collides with nothing. It is the ONE spelling of the rule, so a compiler's
30
+ * schema and a renderer's keying cannot drift apart (as `isLayoutRole` is for roles).
31
+ */
32
+ function isLayoutId(value) {
33
+ return typeof value === "string" && value !== "";
34
+ }
35
+ /** `isLayoutId` on a node's `id`, narrowing the node. Internal. */
36
+ function hasLayoutId(layout) {
37
+ return isLayoutId(layout?.id);
38
+ }
39
+ function grid(options) {
40
+ return {
41
+ type: "grid",
42
+ ...(options.id !== undefined && { id: options.id }),
43
+ columns: options.columns,
44
+ ...(options.gap !== undefined && { gap: options.gap }),
45
+ ...(options.title !== undefined && { title: options.title }),
46
+ ...(options.role !== undefined && { role: options.role }),
47
+ children: options.children.map(normalize_1.asCell),
48
+ };
49
+ }
50
+ function flex(options) {
51
+ return {
52
+ type: "flex",
53
+ ...(options.id !== undefined && { id: options.id }),
54
+ direction: options.direction ?? "row",
55
+ ...(options.gap !== undefined && { gap: options.gap }),
56
+ ...(options.title !== undefined && { title: options.title }),
57
+ ...(options.role !== undefined && { role: options.role }),
58
+ children: options.children,
59
+ };
60
+ }
61
+ function stack(options) {
62
+ return {
63
+ type: "stack",
64
+ ...(options.id !== undefined && { id: options.id }),
65
+ ...(options.gap !== undefined && { gap: options.gap }),
66
+ ...(options.title !== undefined && { title: options.title }),
67
+ ...(options.role !== undefined && { role: options.role }),
68
+ children: options.children,
69
+ };
70
+ }
71
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":";;AAgBA,oCAEC;AASD,gCAEC;AAGD,kCAEC;AAcD,oBAUC;AAcD,oBAUC;AAaD,sBASC;AAxGD,2CAAqC;AAGrC,6FAA6F;AAC7F,0FAA0F;AAC1F,gGAAgG;AAChG,0EAA0E;AAC1E,MAAM,WAAW,GAAG,wBAAwB,CAAC;AAE7C;;;;;;GAMG;AACH,SAAgB,YAAY,CAAC,KAAc;IACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,UAAU,CAAC,KAAc;IACvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC;AACnD,CAAC;AAED,mEAAmE;AACnE,SAAgB,WAAW,CAAC,MAAe;IACzC,OAAO,UAAU,CAAE,MAA8C,EAAE,EAAE,CAAC,CAAC;AACzE,CAAC;AAcD,SAAgB,IAAI,CAAC,OAAoB;IACvC,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,GAAG,CAAC,OAAO,CAAC,EAAE,KAAK,SAAS,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;QACnD,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;QACtD,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QAC5D,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;QACzD,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,kBAAM,CAAC;KACvC,CAAC;AACJ,CAAC;AAcD,SAAgB,IAAI,CAAC,OAAoB;IACvC,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,GAAG,CAAC,OAAO,CAAC,EAAE,KAAK,SAAS,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;QACnD,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK;QACrC,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;QACtD,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QAC5D,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;QACzD,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC;AACJ,CAAC;AAaD,SAAgB,KAAK,CAAC,OAAqB;IACzC,OAAO;QACL,IAAI,EAAE,OAAO;QACb,GAAG,CAAC,OAAO,CAAC,EAAE,KAAK,SAAS,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;QACnD,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;QACtD,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QAC5D,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;QACzD,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { BindingStatus, Blueprint, GridCell, Layout, Widget } from "./types";
2
+ export declare const IR_VERSION_LEGACY = "1.0.0";
3
+ export declare function resolvePath(state: unknown, path: string): unknown;
4
+ export declare const isWidget: (n: unknown) => n is Widget;
5
+ /** The one place a legacy bare grid child becomes a cell. Every walker calls this; nothing else branches on the union. */
6
+ export declare function asCell(child: GridCell | Widget | Layout): GridCell;
7
+ /** An explicit-span cell, optionally placed (IR 2.1.0). Only the placement fields given are written. */
8
+ export declare function cell(span: number, node: Widget | Layout, placement?: Pick<GridCell, "row" | "rowSpan" | "column">): GridCell;
9
+ export declare function blueprintIrVersion(bp: Blueprint): string;
10
+ /**
11
+ * Every IR version this build reads AS-IS. 2.1.0 (ADR 0098) and 2.2.0 (ADR 0099) only added
12
+ * optional fields, and 2.3.0 (ADR 0100) only a `BindingState` literal (a runtime status value
13
+ * no artifact carries), 2.4.0 (ADR 0101) only an optional layout `id`, and 2.5.0 (ADR 0097
14
+ * amendment) only an optional `source.redacted`, so a 2.0.0 through 2.4.0 artifact is already a valid current-major artifact: re-stamping it would change its
15
+ * content address with no change to its shape.
16
+ *
17
+ * Add the outgoing version here on every minor bump.
18
+ */
19
+ export declare const IR_VERSIONS_READ_AS_IS: ReadonlySet<string>;
20
+ /**
21
+ * A current-major artifact from any readable one. Identity (same reference) on 2.0.0, 2.1.0,
22
+ * 2.2.0, 2.3.0, 2.4.0 and 2.5.0 input; a 1.0.0 (or unstamped) artifact is wrapped and stamped. Never
23
+ * mutates its input.
24
+ */
25
+ export declare function upgradeBlueprint(bp: Blueprint): Blueprint;
26
+ export declare function isBindingStatus(v: unknown): v is BindingStatus;
27
+ /** Status lives in the SAME projection state as the value; a malformed value is `undefined`, never a throw. */
28
+ export declare function resolveWidgetStatus(widget: Widget, stateMap: Map<string, unknown>): BindingStatus | undefined;
29
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAElF,eAAO,MAAM,iBAAiB,UAAU,CAAC;AAYzC,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAYjE;AAED,eAAO,MAAM,QAAQ,GAAI,GAAG,OAAO,KAAG,CAAC,IAAI,MACyC,CAAC;AAIrF,0HAA0H;AAC1H,wBAAgB,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAElE;AACD,wGAAwG;AACxG,wBAAgB,IAAI,CAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,SAAS,GAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAM,GAC3D,QAAQ,CAQV;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CAExD;AAeD;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAOrD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,SAAS,CAGzD;AAMD,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,aAAa,CAgB9D;AAED,+GAA+G;AAC/G,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,aAAa,GAAG,SAAS,CAK3B"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IR_VERSIONS_READ_AS_IS = exports.isWidget = exports.IR_VERSION_LEGACY = void 0;
4
+ exports.resolvePath = resolvePath;
5
+ exports.asCell = asCell;
6
+ exports.cell = cell;
7
+ exports.blueprintIrVersion = blueprintIrVersion;
8
+ exports.upgradeBlueprint = upgradeBlueprint;
9
+ exports.isBindingStatus = isBindingStatus;
10
+ exports.resolveWidgetStatus = resolveWidgetStatus;
11
+ const irVersion_1 = require("./irVersion");
12
+ exports.IR_VERSION_LEGACY = "1.0.0";
13
+ // ── Path resolution ───────────────────────────────────────────────────────────
14
+ //
15
+ // Lives here (not traverseBlueprint.ts) to avoid an import cycle: traverseBlueprint imports
16
+ // `asCell` from this module, and `resolveWidgetStatus` below needs `resolvePath`. Re-exported
17
+ // from traverseBlueprint.ts unchanged for existing consumers.
18
+ // Resolve a dot-path (e.g. "a.b.c") against a projection state. Null-safe and
19
+ // generic — no widget/UI coupling — so any live rendering surface can build a
20
+ // slice selector for `ProjectionRuntime.subscribeSelector` without duplicating
21
+ // this traversal (see useWidgetValue). An empty path binds to the whole state.
22
+ function resolvePath(state, path) {
23
+ // An empty path binds the widget to the whole projection surface (the root),
24
+ // not a nested field — otherwise `"".split(".")` yields `[""]` and dereferences
25
+ // a non-existent "" key, silently returning undefined.
26
+ if (path === "")
27
+ return state;
28
+ const parts = path.split(".");
29
+ let current = state;
30
+ for (const part of parts) {
31
+ if (current === null || current === undefined)
32
+ return undefined;
33
+ current = current[part];
34
+ }
35
+ return current;
36
+ }
37
+ const isWidget = (n) => typeof n === "object" && n !== null && n._tag === "Widget";
38
+ exports.isWidget = isWidget;
39
+ const isCell = (n) => typeof n === "object" && n !== null && "node" in n && !("_tag" in n) && !("type" in n);
40
+ /** The one place a legacy bare grid child becomes a cell. Every walker calls this; nothing else branches on the union. */
41
+ function asCell(child) {
42
+ return isCell(child) ? child : { span: 1, node: child };
43
+ }
44
+ /** An explicit-span cell, optionally placed (IR 2.1.0). Only the placement fields given are written. */
45
+ function cell(span, node, placement = {}) {
46
+ return {
47
+ span,
48
+ node,
49
+ ...(placement.row !== undefined && { row: placement.row }),
50
+ ...(placement.rowSpan !== undefined && { rowSpan: placement.rowSpan }),
51
+ ...(placement.column !== undefined && { column: placement.column }),
52
+ };
53
+ }
54
+ function blueprintIrVersion(bp) {
55
+ return bp.irVersion ?? exports.IR_VERSION_LEGACY;
56
+ }
57
+ function upgradeLayout(layout) {
58
+ if (layout.type === "grid") {
59
+ return {
60
+ ...layout,
61
+ children: layout.children.map((c) => {
62
+ const k = asCell(c);
63
+ return (0, exports.isWidget)(k.node) ? k : { ...k, node: upgradeLayout(k.node) };
64
+ }),
65
+ };
66
+ }
67
+ return { ...layout, children: layout.children.map((c) => ((0, exports.isWidget)(c) ? c : upgradeLayout(c))) };
68
+ }
69
+ /**
70
+ * Every IR version this build reads AS-IS. 2.1.0 (ADR 0098) and 2.2.0 (ADR 0099) only added
71
+ * optional fields, and 2.3.0 (ADR 0100) only a `BindingState` literal (a runtime status value
72
+ * no artifact carries), 2.4.0 (ADR 0101) only an optional layout `id`, and 2.5.0 (ADR 0097
73
+ * amendment) only an optional `source.redacted`, so a 2.0.0 through 2.4.0 artifact is already a valid current-major artifact: re-stamping it would change its
74
+ * content address with no change to its shape.
75
+ *
76
+ * Add the outgoing version here on every minor bump.
77
+ */
78
+ exports.IR_VERSIONS_READ_AS_IS = new Set([
79
+ "2.0.0",
80
+ "2.1.0",
81
+ "2.2.0",
82
+ "2.3.0",
83
+ "2.4.0",
84
+ irVersion_1.BLUEPRINT_IR_VERSION,
85
+ ]);
86
+ /**
87
+ * A current-major artifact from any readable one. Identity (same reference) on 2.0.0, 2.1.0,
88
+ * 2.2.0, 2.3.0, 2.4.0 and 2.5.0 input; a 1.0.0 (or unstamped) artifact is wrapped and stamped. Never
89
+ * mutates its input.
90
+ */
91
+ function upgradeBlueprint(bp) {
92
+ if (bp.irVersion !== undefined && exports.IR_VERSIONS_READ_AS_IS.has(bp.irVersion))
93
+ return bp;
94
+ return { ...bp, irVersion: irVersion_1.BLUEPRINT_IR_VERSION, layout: upgradeLayout(bp.layout) };
95
+ }
96
+ // `empty` since IR 2.3.0 (ADR 0100). A reader built before it rejects `{ state: "empty" }` here,
97
+ // so the status resolves to `undefined` and that reader's matrix falls back to loading/ready —
98
+ // never to the error row (ADR 0100 D4).
99
+ const STATES = new Set(["pending", "ready", "stale", "error", "empty"]);
100
+ function isBindingStatus(v) {
101
+ if (typeof v !== "object" || v === null)
102
+ return false;
103
+ const s = v;
104
+ if (typeof s.state !== "string" || !STATES.has(s.state))
105
+ return false;
106
+ if (s.seq !== undefined && typeof s.seq !== "number")
107
+ return false;
108
+ if (s.finding !== undefined) {
109
+ const f = s.finding;
110
+ if (typeof f !== "object" ||
111
+ f === null ||
112
+ typeof f.code !== "string" ||
113
+ typeof f.message !== "string")
114
+ return false;
115
+ }
116
+ return true;
117
+ }
118
+ /** Status lives in the SAME projection state as the value; a malformed value is `undefined`, never a throw. */
119
+ function resolveWidgetStatus(widget, stateMap) {
120
+ const b = widget.binding;
121
+ if (b?.status === undefined)
122
+ return undefined;
123
+ const raw = resolvePath(stateMap.get(b.projectionId), b.status.path);
124
+ return isBindingStatus(raw) ? raw : undefined;
125
+ }
126
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"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"}
package/dist/pins.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import type { PinReport, VersionPins } from "@loomwork/core";
2
+ import type { Blueprint } from "./types";
3
+ /**
4
+ * The versions a blueprint recorded for the projections it binds to.
5
+ *
6
+ * Only ids the blueprint actually DECLARES are returned. A pin for an id absent from
7
+ * `projections` freezes nothing this view reads, and counting it would pad the comparison
8
+ * with agreements that carry no information — the denominator problem `PinReport.compared`
9
+ * exists to make visible.
10
+ */
11
+ export declare function blueprintPins(blueprint: Blueprint): VersionPins;
12
+ /**
13
+ * Does this blueprint still mean what it meant when it was composed?
14
+ *
15
+ * `current` is what the runtime about to hydrate it declares —
16
+ * `projectionRuntime.pins?.() ?? {}`. Findings, never a throw: a drift can be a deliberate
17
+ * re-promotion or a silent re-definition, and only the caller can tell.
18
+ *
19
+ * The comparison is scoped to the blueprint's own `projections` in BOTH directions, and
20
+ * every declared projection is entered on the recorded side even when nothing pinned it
21
+ * (as `""`, which `checkVersionPins` reads as "declared nothing"). That is deliberate:
22
+ * projections this view never binds are none of its business, and a binding that nobody
23
+ * pinned must produce a visible `unpinned` finding rather than vanishing from a report that
24
+ * would then read `ok: true` over an empty comparison.
25
+ */
26
+ export declare function checkBlueprintPins(blueprint: Blueprint, current: VersionPins): PinReport;
27
+ //# sourceMappingURL=pins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pins.d.ts","sourceRoot":"","sources":["../src/pins.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAoBzC;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW,CAS/D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,SAAS,CASxF"}
package/dist/pins.js ADDED
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.blueprintPins = blueprintPins;
4
+ exports.checkBlueprintPins = checkBlueprintPins;
5
+ const core_1 = require("@loomwork/core");
6
+ // ── The blueprint spelling of the binding-semantics pin (ADR 0095) ────────────
7
+ //
8
+ // `validateBlueprint` checks a composition against a catalogue; `preservesNodes` checks one
9
+ // tree against another. Neither can see the failure this covers, because it is not in the
10
+ // tree at all: a `DataBinding` names a projection, the projection is live code registered
11
+ // under that name, and the content address that makes a promoted view "the same view"
12
+ // freezes the NAME.
13
+ //
14
+ // So a view can be byte-identical to the one an operator promoted, hydrate cleanly, validate
15
+ // cleanly, and render a number that means something else — because the fold behind
16
+ // `latency` was edited, or its window moved from rolling to fixed, since the day it was
17
+ // composed.
18
+ //
19
+ // The comparison itself is `@loomwork/core`'s `checkVersionPins`, over `id -> version` maps
20
+ // rather than blueprints: its other consumer (`ReadModelService.checkPins`) has no tree.
21
+ // These two functions are the blueprint case spelled once, the way `blueprintNodeIds` /
22
+ // `preservesBlueprintNodes` spell it for identity.
23
+ /**
24
+ * The versions a blueprint recorded for the projections it binds to.
25
+ *
26
+ * Only ids the blueprint actually DECLARES are returned. A pin for an id absent from
27
+ * `projections` freezes nothing this view reads, and counting it would pad the comparison
28
+ * with agreements that carry no information — the denominator problem `PinReport.compared`
29
+ * exists to make visible.
30
+ */
31
+ function blueprintPins(blueprint) {
32
+ const recorded = blueprint.projectionVersions;
33
+ if (recorded === undefined)
34
+ return emptyPins();
35
+ const pins = emptyPins();
36
+ for (const projectionId of blueprint.projections) {
37
+ const version = recordedVersion(recorded, projectionId);
38
+ if (version !== undefined)
39
+ pins[projectionId] = version;
40
+ }
41
+ return pins;
42
+ }
43
+ /**
44
+ * Does this blueprint still mean what it meant when it was composed?
45
+ *
46
+ * `current` is what the runtime about to hydrate it declares —
47
+ * `projectionRuntime.pins?.() ?? {}`. Findings, never a throw: a drift can be a deliberate
48
+ * re-promotion or a silent re-definition, and only the caller can tell.
49
+ *
50
+ * The comparison is scoped to the blueprint's own `projections` in BOTH directions, and
51
+ * every declared projection is entered on the recorded side even when nothing pinned it
52
+ * (as `""`, which `checkVersionPins` reads as "declared nothing"). That is deliberate:
53
+ * projections this view never binds are none of its business, and a binding that nobody
54
+ * pinned must produce a visible `unpinned` finding rather than vanishing from a report that
55
+ * would then read `ok: true` over an empty comparison.
56
+ */
57
+ function checkBlueprintPins(blueprint, current) {
58
+ const recorded = emptyPins();
59
+ const scoped = emptyPins();
60
+ for (const projectionId of blueprint.projections) {
61
+ recorded[projectionId] = recordedVersion(blueprint.projectionVersions, projectionId) ?? "";
62
+ const now = recordedVersion(current, projectionId);
63
+ if (now !== undefined)
64
+ scoped[projectionId] = now;
65
+ }
66
+ return (0, core_1.checkVersionPins)(recorded, scoped);
67
+ }
68
+ /**
69
+ * A pin map with NO prototype, built rather than written into `{}`.
70
+ *
71
+ * A blueprint is data — the whole premise of ADR 0088 is that a model may have composed it
72
+ * — so a `projectionId` is an untrusted string, and `{}["__proto__"] = "1"` sets a
73
+ * prototype instead of a key: the pin vanishes, `Object.keys` never sees it, and the
74
+ * binding drops out of the report entirely with no finding. Silent omission is precisely
75
+ * what this seam exists to prevent, so the maps it builds cannot have a prototype to hit.
76
+ */
77
+ function emptyPins() {
78
+ return Object.create(null);
79
+ }
80
+ /** An OWN-property read, so an inherited member never answers for a recorded version. */
81
+ function recordedVersion(pins, id) {
82
+ if (pins === undefined || !Object.hasOwn(pins, id))
83
+ return undefined;
84
+ return pins[id];
85
+ }
86
+ //# sourceMappingURL=pins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pins.js","sourceRoot":"","sources":["../src/pins.ts"],"names":[],"mappings":";;AA8BA,sCASC;AAgBD,gDASC;AAhED,yCAAkD;AAIlD,iFAAiF;AACjF,EAAE;AACF,4FAA4F;AAC5F,0FAA0F;AAC1F,0FAA0F;AAC1F,sFAAsF;AACtF,oBAAoB;AACpB,EAAE;AACF,6FAA6F;AAC7F,mFAAmF;AACnF,wFAAwF;AACxF,YAAY;AACZ,EAAE;AACF,4FAA4F;AAC5F,yFAAyF;AACzF,wFAAwF;AACxF,mDAAmD;AAEnD;;;;;;;GAOG;AACH,SAAgB,aAAa,CAAC,SAAoB;IAChD,MAAM,QAAQ,GAAG,SAAS,CAAC,kBAAkB,CAAC;IAC9C,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,SAAS,EAAE,CAAC;IAC/C,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,KAAK,MAAM,YAAY,IAAI,SAAS,CAAC,WAAW,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QACxD,IAAI,OAAO,KAAK,SAAS;YAAE,IAAI,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,kBAAkB,CAAC,SAAoB,EAAE,OAAoB;IAC3E,MAAM,QAAQ,GAAG,SAAS,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,KAAK,MAAM,YAAY,IAAI,SAAS,CAAC,WAAW,EAAE,CAAC;QACjD,QAAQ,CAAC,YAAY,CAAC,GAAG,eAAe,CAAC,SAAS,CAAC,kBAAkB,EAAE,YAAY,CAAC,IAAI,EAAE,CAAC;QAC3F,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACnD,IAAI,GAAG,KAAK,SAAS;YAAE,MAAM,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC;IACpD,CAAC;IACD,OAAO,IAAA,uBAAgB,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,SAAS;IAChB,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;AACvD,CAAC;AAED,yFAAyF;AACzF,SAAS,eAAe,CAAC,IAA6B,EAAE,EAAU;IAChE,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IACrE,OAAO,IAAI,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { resolvePath } from "./normalize";
2
+ import type { Layout, Widget, WidgetAction, WidgetRendererProps } from "./types";
3
+ export { resolvePath };
4
+ export interface ContainerContext<TNode> {
5
+ layout: Layout;
6
+ children: TNode[];
7
+ /**
8
+ * Grid containers only: each child with the columns it occupies (default 1), in document
9
+ * order, plus its explicit placement (IR 2.1.0) — `row`/`rowSpan`/`column` are present only
10
+ * when the cell sets them.
11
+ */
12
+ cells?: Array<{
13
+ span: number;
14
+ row?: number;
15
+ rowSpan?: number;
16
+ column?: number;
17
+ child: TNode;
18
+ }>;
19
+ }
20
+ export declare function resolveWidgetValue(widget: Widget, stateMap: Map<string, unknown>): unknown;
21
+ export interface BlueprintTraversalOptions<TNode> {
22
+ renderWidget: (ctx: WidgetRendererProps) => TNode;
23
+ renderContainer: (ctx: ContainerContext<TNode>) => TNode;
24
+ onAction?: (action: WidgetAction) => void;
25
+ }
26
+ export declare function traverseBlueprint<TNode>(node: Layout | Widget, stateMap: Map<string, unknown>, options: BlueprintTraversalOptions<TNode>): TNode;
27
+ //# sourceMappingURL=traverseBlueprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverseBlueprint.d.ts","sourceRoot":"","sources":["../src/traverseBlueprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,WAAW,EAAuB,MAAM,aAAa,CAAC;AACjF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAKjF,OAAO,EAAE,WAAW,EAAE,CAAC;AAIvB,MAAM,WAAW,gBAAgB,CAAC,KAAK;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;CAChG;AAID,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAI1F;AA4BD,MAAM,WAAW,yBAAyB,CAAC,KAAK;IAC9C,YAAY,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,KAAK,CAAC;IAClD,eAAe,EAAE,CAAC,GAAG,EAAE,gBAAgB,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC;IACzD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;CAC3C;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EACrC,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,yBAAyB,CAAC,KAAK,CAAC,GACxC,KAAK,CA4BP"}