@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 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EACL,MAAM,EACN,IAAI,EACJ,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,QAAQ,EACR,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,WAAW,EACX,SAAS,EACT,SAAS,EACT,aAAa,GACd,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAK3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAQ3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,150 @@
1
+ // ── The Blueprint as a stable rendering IR (ADR 0096) ─────────────────────────
2
+ //
3
+ // A `Blueprint` used to be a value one app authored and one renderer drew. Once an external
4
+ // framework COMPILES to it, its shape is an interface others depend on, and a field added or
5
+ // renamed here is a breaking change in a repository nothing in this workspace builds.
6
+ //
7
+ // So the shape is declared twice, on purpose. The types below are the truth; the manifests are a
8
+ // second spelling that `tsc` checks against them exhaustively — `AllKeys<T>` makes every key
9
+ // required, and `satisfies` on an object literal rejects an excess key — so any change to
10
+ // `Widget`, `Layout` or `Blueprint` fails the build until the manifest agrees. `irVersion.test.ts`
11
+ // then pins a digest of the manifests to BLUEPRINT_IR_VERSION, so the manifest cannot change
12
+ // without the version changing either. Neither half is sufficient alone.
13
+ //
14
+ // What this guards: field presence, field optionality (a required↔optional flip fails the
15
+ // build), the WidgetKind literals, the layout `type` literals. What it does not: a field's type
16
+ // being narrowed or widened, nor a `readonly` change. A consumer's typecheck catches those.
17
+ //
18
+ // Adding a Layout variant means adding its field manifest here too; `Record<Layout["type"], true>`
19
+ // catches the variant, not its fields.
20
+ export const BLUEPRINT_IR_VERSION = "2.5.0";
21
+ const WIDGET = {
22
+ _tag: "required",
23
+ id: "required",
24
+ kind: "required",
25
+ title: "optional",
26
+ props: "required",
27
+ binding: "optional",
28
+ source: "optional",
29
+ };
30
+ const DATA_BINDING = {
31
+ projectionId: "required",
32
+ path: "required",
33
+ status: "optional",
34
+ sensitivity: "optional",
35
+ };
36
+ const BINDING_STATUS_REF = {
37
+ path: "required",
38
+ };
39
+ const BINDING_STATUS = {
40
+ state: "required",
41
+ seq: "optional",
42
+ finding: "optional",
43
+ };
44
+ // The NESTED shapes, manifested in their own right. `KeyShape<BindingStatus>` only records that
45
+ // `finding` exists and is optional — a key added to, removed from or flipped inside the finding
46
+ // (or a `degradedFrom` entry) is a change to the compile target that the parent manifest cannot
47
+ // see. A consumer reads `finding.code`/`d.type`, so those key sets are part of the contract.
48
+ const BINDING_FINDING = {
49
+ code: "required",
50
+ message: "required",
51
+ };
52
+ const WIDGET_SOURCE = {
53
+ type: "required",
54
+ version: "required",
55
+ degradedFrom: "optional",
56
+ redacted: "optional",
57
+ };
58
+ const DEGRADED_FROM = {
59
+ type: "required",
60
+ version: "required",
61
+ };
62
+ const GRID_CELL = {
63
+ span: "optional",
64
+ row: "optional",
65
+ rowSpan: "optional",
66
+ column: "optional",
67
+ node: "required",
68
+ };
69
+ const BINDING_STATES = {
70
+ pending: true,
71
+ ready: true,
72
+ stale: true,
73
+ error: true,
74
+ empty: true,
75
+ };
76
+ const GRID_LAYOUT = {
77
+ type: "required",
78
+ id: "optional",
79
+ columns: "required",
80
+ gap: "optional",
81
+ title: "optional",
82
+ role: "optional",
83
+ children: "required",
84
+ };
85
+ const FLEX_LAYOUT = {
86
+ type: "required",
87
+ id: "optional",
88
+ direction: "required",
89
+ gap: "optional",
90
+ title: "optional",
91
+ role: "optional",
92
+ children: "required",
93
+ };
94
+ const STACK_LAYOUT = {
95
+ type: "required",
96
+ id: "optional",
97
+ gap: "optional",
98
+ title: "optional",
99
+ role: "optional",
100
+ children: "required",
101
+ };
102
+ const BLUEPRINT = {
103
+ _tag: "required",
104
+ irVersion: "optional",
105
+ id: "required",
106
+ title: "required",
107
+ layout: "required",
108
+ projections: "required",
109
+ projectionVersions: "optional",
110
+ };
111
+ const WIDGET_KINDS = {
112
+ MetricCard: true,
113
+ Timeline: true,
114
+ ApprovalCard: true,
115
+ StatusBadge: true,
116
+ ProgressBar: true,
117
+ DataTable: true,
118
+ AlertCard: true,
119
+ StreamingText: true,
120
+ };
121
+ const LAYOUT_TYPES = { grid: true, flex: true, stack: true };
122
+ // Field manifests emit `"<key>:<required|optional>"` so an optional↔required flip changes the
123
+ // digest even though the key set itself is unchanged. The two literal-union manifests (kinds,
124
+ // layout types) have no optionality to capture, so they stay plain key lists.
125
+ const keysOf = (manifest) => Object.entries(manifest)
126
+ .map(([key, shape]) => `${key}:${shape}`)
127
+ .sort();
128
+ const plainKeysOf = (manifest) => Object.keys(manifest).sort();
129
+ /**
130
+ * The shape of the Blueprint IR at `BLUEPRINT_IR_VERSION`, as sorted key sets. Digest this
131
+ * (`fnv1a64(canonicalJson(BLUEPRINT_IR_SHAPE))`) to compare two builds' IR without a type checker.
132
+ */
133
+ export const BLUEPRINT_IR_SHAPE = {
134
+ widget: keysOf(WIDGET),
135
+ dataBinding: keysOf(DATA_BINDING),
136
+ bindingStatusRef: keysOf(BINDING_STATUS_REF),
137
+ bindingStatus: keysOf(BINDING_STATUS),
138
+ bindingFinding: keysOf(BINDING_FINDING),
139
+ widgetSource: keysOf(WIDGET_SOURCE),
140
+ degradedFrom: keysOf(DEGRADED_FROM),
141
+ gridCell: keysOf(GRID_CELL),
142
+ gridLayout: keysOf(GRID_LAYOUT),
143
+ flexLayout: keysOf(FLEX_LAYOUT),
144
+ stackLayout: keysOf(STACK_LAYOUT),
145
+ blueprint: keysOf(BLUEPRINT),
146
+ widgetKinds: plainKeysOf(WIDGET_KINDS),
147
+ layoutTypes: plainKeysOf(LAYOUT_TYPES),
148
+ bindingStates: plainKeysOf(BINDING_STATES),
149
+ };
150
+ //# 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,63 @@
1
+ import { asCell } from "./normalize";
2
+ // ADR 0099 D2 — a role is a registry KEY, so it has a key's shape: a lowercase ASCII letter,
3
+ // then lowercase letters, digits or hyphens, at most 64 characters in all. No `_`, `.` or
4
+ // uppercase, so `__proto__`/`constructor` can never be one and a role never needs escaping in a
5
+ // CSS attribute selector (`[data-container-role=stepped]`) or a log line.
6
+ const LAYOUT_ROLE = /^[a-z][a-z0-9-]{0,63}$/;
7
+ /**
8
+ * Whether `value` is a well-formed layout role (ADR 0099 D2): `/^[a-z][a-z0-9-]{0,63}$/`. A
9
+ * renderer looks a container's role up only when this holds; anything else — absent, empty,
10
+ * a non-string, `"Stepped"`, a 65-character string — draws the plain container, exactly as an
11
+ * unknown role does. It is the ONE spelling of the rule, so a compiler's schema and a
12
+ * renderer's lookup cannot drift apart.
13
+ */
14
+ export function isLayoutRole(value) {
15
+ return typeof value === "string" && LAYOUT_ROLE.test(value);
16
+ }
17
+ /**
18
+ * Whether `value` is a well-formed layout id (IR 2.4.0, ADR 0101): the same rule as `Widget.id`,
19
+ * a non-empty string — no charset, no length cap. Anything else — absent, `""`, a number, `null`
20
+ * — is treated as ABSENT by every reader: the container is keyed positionally, the identity walk
21
+ * skips it, and it collides with nothing. It is the ONE spelling of the rule, so a compiler's
22
+ * schema and a renderer's keying cannot drift apart (as `isLayoutRole` is for roles).
23
+ */
24
+ export function isLayoutId(value) {
25
+ return typeof value === "string" && value !== "";
26
+ }
27
+ /** `isLayoutId` on a node's `id`, narrowing the node. Internal. */
28
+ export function hasLayoutId(layout) {
29
+ return isLayoutId(layout?.id);
30
+ }
31
+ export function grid(options) {
32
+ return {
33
+ type: "grid",
34
+ ...(options.id !== undefined && { id: options.id }),
35
+ columns: options.columns,
36
+ ...(options.gap !== undefined && { gap: options.gap }),
37
+ ...(options.title !== undefined && { title: options.title }),
38
+ ...(options.role !== undefined && { role: options.role }),
39
+ children: options.children.map(asCell),
40
+ };
41
+ }
42
+ export function flex(options) {
43
+ return {
44
+ type: "flex",
45
+ ...(options.id !== undefined && { id: options.id }),
46
+ direction: options.direction ?? "row",
47
+ ...(options.gap !== undefined && { gap: options.gap }),
48
+ ...(options.title !== undefined && { title: options.title }),
49
+ ...(options.role !== undefined && { role: options.role }),
50
+ children: options.children,
51
+ };
52
+ }
53
+ export function stack(options) {
54
+ return {
55
+ type: "stack",
56
+ ...(options.id !== undefined && { id: options.id }),
57
+ ...(options.gap !== undefined && { gap: options.gap }),
58
+ ...(options.title !== undefined && { title: options.title }),
59
+ ...(options.role !== undefined && { role: options.role }),
60
+ children: options.children,
61
+ };
62
+ }
63
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,6FAA6F;AAC7F,0FAA0F;AAC1F,gGAAgG;AAChG,0EAA0E;AAC1E,MAAM,WAAW,GAAG,wBAAwB,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC;AACnD,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,WAAW,CAAC,MAAe;IACzC,OAAO,UAAU,CAAE,MAA8C,EAAE,EAAE,CAAC,CAAC;AACzE,CAAC;AAcD,MAAM,UAAU,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,MAAM,CAAC;KACvC,CAAC;AACJ,CAAC;AAcD,MAAM,UAAU,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,MAAM,UAAU,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,115 @@
1
+ import { BLUEPRINT_IR_VERSION } from "./irVersion";
2
+ export const IR_VERSION_LEGACY = "1.0.0";
3
+ // ── Path resolution ───────────────────────────────────────────────────────────
4
+ //
5
+ // Lives here (not traverseBlueprint.ts) to avoid an import cycle: traverseBlueprint imports
6
+ // `asCell` from this module, and `resolveWidgetStatus` below needs `resolvePath`. Re-exported
7
+ // from traverseBlueprint.ts unchanged for existing consumers.
8
+ // Resolve a dot-path (e.g. "a.b.c") against a projection state. Null-safe and
9
+ // generic — no widget/UI coupling — so any live rendering surface can build a
10
+ // slice selector for `ProjectionRuntime.subscribeSelector` without duplicating
11
+ // this traversal (see useWidgetValue). An empty path binds to the whole state.
12
+ export function resolvePath(state, path) {
13
+ // An empty path binds the widget to the whole projection surface (the root),
14
+ // not a nested field — otherwise `"".split(".")` yields `[""]` and dereferences
15
+ // a non-existent "" key, silently returning undefined.
16
+ if (path === "")
17
+ return state;
18
+ const parts = path.split(".");
19
+ let current = state;
20
+ for (const part of parts) {
21
+ if (current === null || current === undefined)
22
+ return undefined;
23
+ current = current[part];
24
+ }
25
+ return current;
26
+ }
27
+ export const isWidget = (n) => typeof n === "object" && n !== null && n._tag === "Widget";
28
+ const isCell = (n) => typeof n === "object" && n !== null && "node" in n && !("_tag" in n) && !("type" in n);
29
+ /** The one place a legacy bare grid child becomes a cell. Every walker calls this; nothing else branches on the union. */
30
+ export function asCell(child) {
31
+ return isCell(child) ? child : { span: 1, node: child };
32
+ }
33
+ /** An explicit-span cell, optionally placed (IR 2.1.0). Only the placement fields given are written. */
34
+ export function cell(span, node, placement = {}) {
35
+ return {
36
+ span,
37
+ node,
38
+ ...(placement.row !== undefined && { row: placement.row }),
39
+ ...(placement.rowSpan !== undefined && { rowSpan: placement.rowSpan }),
40
+ ...(placement.column !== undefined && { column: placement.column }),
41
+ };
42
+ }
43
+ export function blueprintIrVersion(bp) {
44
+ return bp.irVersion ?? IR_VERSION_LEGACY;
45
+ }
46
+ function upgradeLayout(layout) {
47
+ if (layout.type === "grid") {
48
+ return {
49
+ ...layout,
50
+ children: layout.children.map((c) => {
51
+ const k = asCell(c);
52
+ return isWidget(k.node) ? k : { ...k, node: upgradeLayout(k.node) };
53
+ }),
54
+ };
55
+ }
56
+ return { ...layout, children: layout.children.map((c) => (isWidget(c) ? c : upgradeLayout(c))) };
57
+ }
58
+ /**
59
+ * Every IR version this build reads AS-IS. 2.1.0 (ADR 0098) and 2.2.0 (ADR 0099) only added
60
+ * optional fields, and 2.3.0 (ADR 0100) only a `BindingState` literal (a runtime status value
61
+ * no artifact carries), 2.4.0 (ADR 0101) only an optional layout `id`, and 2.5.0 (ADR 0097
62
+ * 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
63
+ * content address with no change to its shape.
64
+ *
65
+ * Add the outgoing version here on every minor bump.
66
+ */
67
+ export const IR_VERSIONS_READ_AS_IS = new Set([
68
+ "2.0.0",
69
+ "2.1.0",
70
+ "2.2.0",
71
+ "2.3.0",
72
+ "2.4.0",
73
+ BLUEPRINT_IR_VERSION,
74
+ ]);
75
+ /**
76
+ * A current-major artifact from any readable one. Identity (same reference) on 2.0.0, 2.1.0,
77
+ * 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
78
+ * mutates its input.
79
+ */
80
+ export function upgradeBlueprint(bp) {
81
+ if (bp.irVersion !== undefined && IR_VERSIONS_READ_AS_IS.has(bp.irVersion))
82
+ return bp;
83
+ return { ...bp, irVersion: BLUEPRINT_IR_VERSION, layout: upgradeLayout(bp.layout) };
84
+ }
85
+ // `empty` since IR 2.3.0 (ADR 0100). A reader built before it rejects `{ state: "empty" }` here,
86
+ // so the status resolves to `undefined` and that reader's matrix falls back to loading/ready —
87
+ // never to the error row (ADR 0100 D4).
88
+ const STATES = new Set(["pending", "ready", "stale", "error", "empty"]);
89
+ export function isBindingStatus(v) {
90
+ if (typeof v !== "object" || v === null)
91
+ return false;
92
+ const s = v;
93
+ if (typeof s.state !== "string" || !STATES.has(s.state))
94
+ return false;
95
+ if (s.seq !== undefined && typeof s.seq !== "number")
96
+ return false;
97
+ if (s.finding !== undefined) {
98
+ const f = s.finding;
99
+ if (typeof f !== "object" ||
100
+ f === null ||
101
+ typeof f.code !== "string" ||
102
+ typeof f.message !== "string")
103
+ return false;
104
+ }
105
+ return true;
106
+ }
107
+ /** Status lives in the SAME projection state as the value; a malformed value is `undefined`, never a throw. */
108
+ export function resolveWidgetStatus(widget, stateMap) {
109
+ const b = widget.binding;
110
+ if (b?.status === undefined)
111
+ return undefined;
112
+ const raw = resolvePath(stateMap.get(b.projectionId), b.status.path);
113
+ return isBindingStatus(raw) ? raw : undefined;
114
+ }
115
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/normalize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import { checkVersionPins } from "@loomwork/core";
2
+ // ── The blueprint spelling of the binding-semantics pin (ADR 0095) ────────────
3
+ //
4
+ // `validateBlueprint` checks a composition against a catalogue; `preservesNodes` checks one
5
+ // tree against another. Neither can see the failure this covers, because it is not in the
6
+ // tree at all: a `DataBinding` names a projection, the projection is live code registered
7
+ // under that name, and the content address that makes a promoted view "the same view"
8
+ // freezes the NAME.
9
+ //
10
+ // So a view can be byte-identical to the one an operator promoted, hydrate cleanly, validate
11
+ // cleanly, and render a number that means something else — because the fold behind
12
+ // `latency` was edited, or its window moved from rolling to fixed, since the day it was
13
+ // composed.
14
+ //
15
+ // The comparison itself is `@loomwork/core`'s `checkVersionPins`, over `id -> version` maps
16
+ // rather than blueprints: its other consumer (`ReadModelService.checkPins`) has no tree.
17
+ // These two functions are the blueprint case spelled once, the way `blueprintNodeIds` /
18
+ // `preservesBlueprintNodes` spell it for identity.
19
+ /**
20
+ * The versions a blueprint recorded for the projections it binds to.
21
+ *
22
+ * Only ids the blueprint actually DECLARES are returned. A pin for an id absent from
23
+ * `projections` freezes nothing this view reads, and counting it would pad the comparison
24
+ * with agreements that carry no information — the denominator problem `PinReport.compared`
25
+ * exists to make visible.
26
+ */
27
+ export function blueprintPins(blueprint) {
28
+ const recorded = blueprint.projectionVersions;
29
+ if (recorded === undefined)
30
+ return emptyPins();
31
+ const pins = emptyPins();
32
+ for (const projectionId of blueprint.projections) {
33
+ const version = recordedVersion(recorded, projectionId);
34
+ if (version !== undefined)
35
+ pins[projectionId] = version;
36
+ }
37
+ return pins;
38
+ }
39
+ /**
40
+ * Does this blueprint still mean what it meant when it was composed?
41
+ *
42
+ * `current` is what the runtime about to hydrate it declares —
43
+ * `projectionRuntime.pins?.() ?? {}`. Findings, never a throw: a drift can be a deliberate
44
+ * re-promotion or a silent re-definition, and only the caller can tell.
45
+ *
46
+ * The comparison is scoped to the blueprint's own `projections` in BOTH directions, and
47
+ * every declared projection is entered on the recorded side even when nothing pinned it
48
+ * (as `""`, which `checkVersionPins` reads as "declared nothing"). That is deliberate:
49
+ * projections this view never binds are none of its business, and a binding that nobody
50
+ * pinned must produce a visible `unpinned` finding rather than vanishing from a report that
51
+ * would then read `ok: true` over an empty comparison.
52
+ */
53
+ export function checkBlueprintPins(blueprint, current) {
54
+ const recorded = emptyPins();
55
+ const scoped = emptyPins();
56
+ for (const projectionId of blueprint.projections) {
57
+ recorded[projectionId] = recordedVersion(blueprint.projectionVersions, projectionId) ?? "";
58
+ const now = recordedVersion(current, projectionId);
59
+ if (now !== undefined)
60
+ scoped[projectionId] = now;
61
+ }
62
+ return checkVersionPins(recorded, scoped);
63
+ }
64
+ /**
65
+ * A pin map with NO prototype, built rather than written into `{}`.
66
+ *
67
+ * A blueprint is data — the whole premise of ADR 0088 is that a model may have composed it
68
+ * — so a `projectionId` is an untrusted string, and `{}["__proto__"] = "1"` sets a
69
+ * prototype instead of a key: the pin vanishes, `Object.keys` never sees it, and the
70
+ * binding drops out of the report entirely with no finding. Silent omission is precisely
71
+ * what this seam exists to prevent, so the maps it builds cannot have a prototype to hit.
72
+ */
73
+ function emptyPins() {
74
+ return Object.create(null);
75
+ }
76
+ /** An OWN-property read, so an inherited member never answers for a recorded version. */
77
+ function recordedVersion(pins, id) {
78
+ if (pins === undefined || !Object.hasOwn(pins, id))
79
+ return undefined;
80
+ return pins[id];
81
+ }
82
+ //# sourceMappingURL=pins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pins.js","sourceRoot":"","sources":["../../src/pins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;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,MAAM,UAAU,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,MAAM,UAAU,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,gBAAgB,CAAC,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,38 @@
1
+ import { asCell, isWidget, resolvePath, resolveWidgetStatus } from "./normalize";
2
+ // Re-exported unchanged for existing consumers; it now lives in normalize.ts to avoid an
3
+ // import cycle (normalize.ts's `resolveWidgetStatus` needs it, and this module imports
4
+ // `asCell`/`isWidget`/`resolveWidgetStatus` from normalize.ts as of Task 2).
5
+ export { resolvePath };
6
+ // ── Value resolution ──────────────────────────────────────────────────────────
7
+ export function resolveWidgetValue(widget, stateMap) {
8
+ if (!widget.binding)
9
+ return undefined;
10
+ const state = stateMap.get(widget.binding.projectionId);
11
+ return resolvePath(state, widget.binding.path);
12
+ }
13
+ export function traverseBlueprint(node, stateMap, options) {
14
+ const { renderWidget, renderContainer, onAction } = options;
15
+ if (isWidget(node)) {
16
+ const status = resolveWidgetStatus(node, stateMap);
17
+ return renderWidget({
18
+ widget: node,
19
+ value: resolveWidgetValue(node, stateMap),
20
+ ...(status !== undefined && { status }),
21
+ ...(onAction !== undefined && { onAction }),
22
+ });
23
+ }
24
+ const layout = node;
25
+ if (layout.type === "grid") {
26
+ const cells = layout.children.map(asCell).map((c) => ({
27
+ span: c.span ?? 1,
28
+ ...(c.row !== undefined && { row: c.row }),
29
+ ...(c.rowSpan !== undefined && { rowSpan: c.rowSpan }),
30
+ ...(c.column !== undefined && { column: c.column }),
31
+ child: traverseBlueprint(c.node, stateMap, options),
32
+ }));
33
+ return renderContainer({ layout, children: cells.map((c) => c.child), cells });
34
+ }
35
+ const children = layout.children.map((child) => traverseBlueprint(child, stateMap, options));
36
+ return renderContainer({ layout, children });
37
+ }
38
+ //# sourceMappingURL=traverseBlueprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverseBlueprint.js","sourceRoot":"","sources":["../../src/traverseBlueprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGjF,yFAAyF;AACzF,uFAAuF;AACvF,6EAA6E;AAC7E,OAAO,EAAE,WAAW,EAAE,CAAC;AAevB,iFAAiF;AAEjF,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,QAA8B;IAC/E,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACxD,OAAO,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACjD,CAAC;AAkCD,MAAM,UAAU,iBAAiB,CAC/B,IAAqB,EACrB,QAA8B,EAC9B,OAAyC;IAEzC,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAE5D,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACnD,OAAO,YAAY,CAAC;YAClB,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC;YACzC,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;YACvC,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,CAAC;SAC5C,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpD,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;YACjB,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;YAC1C,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YACtD,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;YACnD,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC;SACpD,CAAC,CAAC,CAAC;QACJ,OAAO,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAE7F,OAAO,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { throwIfAborted, yieldControl } from "@loomwork/scheduler";
2
+ import { asCell, isWidget, resolveWidgetStatus } from "./normalize";
3
+ import { resolveWidgetValue } from "./traverseBlueprint";
4
+ const DEFAULT_YIELD_EVERY = 64;
5
+ export async function traverseBlueprintAsync(node, stateMap, options) {
6
+ const { renderer, onAction, signal } = options;
7
+ const yieldTo = options.yieldTo ?? yieldControl;
8
+ const shouldYield = options.shouldYield ?? ((n) => n >= DEFAULT_YIELD_EVERY);
9
+ // Counter-based budget (no clock) so behavior is deterministic and testable.
10
+ let nodesSinceYield = 0;
11
+ const tick = async () => {
12
+ throwIfAborted(signal);
13
+ nodesSinceYield++;
14
+ if (shouldYield(nodesSinceYield)) {
15
+ nodesSinceYield = 0;
16
+ await yieldTo();
17
+ throwIfAborted(signal);
18
+ }
19
+ };
20
+ const walk = async (current) => {
21
+ await tick();
22
+ if (isWidget(current)) {
23
+ const status = resolveWidgetStatus(current, stateMap);
24
+ return renderer.createWidget({
25
+ widget: current,
26
+ value: resolveWidgetValue(current, stateMap),
27
+ ...(status !== undefined && { status }),
28
+ ...(onAction !== undefined && { onAction }),
29
+ });
30
+ }
31
+ const layout = current;
32
+ // Create the container FIRST so children can be committed into it as they
33
+ // resolve (incremental commit / progressive paint between yields).
34
+ const container = renderer.createContainer(layout);
35
+ // Only the iteration source changes for a grid (unwrap each child via `asCell`) — this
36
+ // walker has no `cells`/span surface of its own: the container is created and committed
37
+ // to before its children resolve, so there is nowhere to hand spans alongside them. A
38
+ // renderer that needs them can read `layout.children` itself in `createContainer`.
39
+ const nodes = layout.type === "grid" ? layout.children.map((c) => asCell(c).node) : layout.children;
40
+ for (const child of nodes) {
41
+ const childNode = await walk(child);
42
+ renderer.appendChild(container, childNode);
43
+ }
44
+ return container;
45
+ };
46
+ return walk(node);
47
+ }
48
+ //# sourceMappingURL=traverseBlueprintAsync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverseBlueprintAsync.js","sourceRoot":"","sources":["../../src/traverseBlueprintAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAmCzD,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAqB,EACrB,QAA8B,EAC9B,OAAqC;IAErC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,YAAY,CAAC;IAChD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,mBAAmB,CAAC,CAAC;IAE7E,6EAA6E;IAC7E,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;QACrC,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,eAAe,EAAE,CAAC;QAClB,IAAI,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC;YACjC,eAAe,GAAG,CAAC,CAAC;YACpB,MAAM,OAAO,EAAE,CAAC;YAChB,cAAc,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,EAAE,OAAwB,EAAkB,EAAE;QAC9D,MAAM,IAAI,EAAE,CAAC;QAEb,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACtD,OAAO,QAAQ,CAAC,YAAY,CAAC;gBAC3B,MAAM,EAAE,OAAO;gBACf,KAAK,EAAE,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC;gBAC5C,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;gBACvC,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,CAAC;aAC5C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC;QACvB,0EAA0E;QAC1E,mEAAmE;QACnE,MAAM,SAAS,GAAG,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACnD,uFAAuF;QACvF,wFAAwF;QACxF,sFAAsF;QACtF,mFAAmF;QACnF,MAAM,KAAK,GACT,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QACxF,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC;YACpC,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;IAEF,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC;AACpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}