@miragon/mcp-toolkit-core 0.10.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 (174) hide show
  1. package/README.md +32 -0
  2. package/dist/engine/context-builder.d.ts +5 -0
  3. package/dist/engine/context-builder.d.ts.map +1 -0
  4. package/dist/engine/context-builder.js +22 -0
  5. package/dist/engine/context-builder.js.map +1 -0
  6. package/dist/engine/pipeline-executor.d.ts +21 -0
  7. package/dist/engine/pipeline-executor.d.ts.map +1 -0
  8. package/dist/engine/pipeline-executor.js +80 -0
  9. package/dist/engine/pipeline-executor.js.map +1 -0
  10. package/dist/framework/active-modules.d.ts +30 -0
  11. package/dist/framework/active-modules.d.ts.map +1 -0
  12. package/dist/framework/active-modules.js +44 -0
  13. package/dist/framework/active-modules.js.map +1 -0
  14. package/dist/framework/catalogue.d.ts +148 -0
  15. package/dist/framework/catalogue.d.ts.map +1 -0
  16. package/dist/framework/catalogue.js +132 -0
  17. package/dist/framework/catalogue.js.map +1 -0
  18. package/dist/framework/dashboard-store.d.ts +136 -0
  19. package/dist/framework/dashboard-store.d.ts.map +1 -0
  20. package/dist/framework/dashboard-store.js +301 -0
  21. package/dist/framework/dashboard-store.js.map +1 -0
  22. package/dist/framework/layout-schemas.d.ts +106 -0
  23. package/dist/framework/layout-schemas.d.ts.map +1 -0
  24. package/dist/framework/layout-schemas.js +67 -0
  25. package/dist/framework/layout-schemas.js.map +1 -0
  26. package/dist/framework/layout-types.d.ts +25 -0
  27. package/dist/framework/layout-types.d.ts.map +1 -0
  28. package/dist/framework/layout-types.js +7 -0
  29. package/dist/framework/layout-types.js.map +1 -0
  30. package/dist/framework/manifest.d.ts +65 -0
  31. package/dist/framework/manifest.d.ts.map +1 -0
  32. package/dist/framework/manifest.js +47 -0
  33. package/dist/framework/manifest.js.map +1 -0
  34. package/dist/framework/render-view.d.ts +53 -0
  35. package/dist/framework/render-view.d.ts.map +1 -0
  36. package/dist/framework/render-view.js +73 -0
  37. package/dist/framework/render-view.js.map +1 -0
  38. package/dist/framework/view-builders.d.ts +79 -0
  39. package/dist/framework/view-builders.d.ts.map +1 -0
  40. package/dist/framework/view-builders.js +113 -0
  41. package/dist/framework/view-builders.js.map +1 -0
  42. package/dist/i18n/index.d.ts +3 -0
  43. package/dist/i18n/index.d.ts.map +1 -0
  44. package/dist/i18n/index.js +6 -0
  45. package/dist/i18n/index.js.map +1 -0
  46. package/dist/i18n/translator.d.ts +59 -0
  47. package/dist/i18n/translator.d.ts.map +1 -0
  48. package/dist/i18n/translator.js +48 -0
  49. package/dist/i18n/translator.js.map +1 -0
  50. package/dist/index.d.ts +31 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +27 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/middleware/org-gate.d.ts +22 -0
  55. package/dist/middleware/org-gate.d.ts.map +1 -0
  56. package/dist/middleware/org-gate.js +28 -0
  57. package/dist/middleware/org-gate.js.map +1 -0
  58. package/dist/middleware/role-filter.d.ts +79 -0
  59. package/dist/middleware/role-filter.d.ts.map +1 -0
  60. package/dist/middleware/role-filter.js +95 -0
  61. package/dist/middleware/role-filter.js.map +1 -0
  62. package/dist/registry/app-loader.d.ts +35 -0
  63. package/dist/registry/app-loader.d.ts.map +1 -0
  64. package/dist/registry/app-loader.js +55 -0
  65. package/dist/registry/app-loader.js.map +1 -0
  66. package/dist/registry/step-registry.d.ts +27 -0
  67. package/dist/registry/step-registry.d.ts.map +1 -0
  68. package/dist/registry/step-registry.js +53 -0
  69. package/dist/registry/step-registry.js.map +1 -0
  70. package/dist/registry/widget-registry.d.ts +15 -0
  71. package/dist/registry/widget-registry.d.ts.map +1 -0
  72. package/dist/registry/widget-registry.js +28 -0
  73. package/dist/registry/widget-registry.js.map +1 -0
  74. package/dist/rest/client.d.ts +3 -0
  75. package/dist/rest/client.d.ts.map +1 -0
  76. package/dist/rest/client.js +155 -0
  77. package/dist/rest/client.js.map +1 -0
  78. package/dist/rest/errors.d.ts +14 -0
  79. package/dist/rest/errors.d.ts.map +1 -0
  80. package/dist/rest/errors.js +21 -0
  81. package/dist/rest/errors.js.map +1 -0
  82. package/dist/rest/index.d.ts +6 -0
  83. package/dist/rest/index.d.ts.map +1 -0
  84. package/dist/rest/index.js +4 -0
  85. package/dist/rest/index.js.map +1 -0
  86. package/dist/rest/tool.d.ts +60 -0
  87. package/dist/rest/tool.d.ts.map +1 -0
  88. package/dist/rest/tool.js +81 -0
  89. package/dist/rest/tool.js.map +1 -0
  90. package/dist/rest/types.d.ts +66 -0
  91. package/dist/rest/types.d.ts.map +1 -0
  92. package/dist/rest/types.js +2 -0
  93. package/dist/rest/types.js.map +1 -0
  94. package/dist/tools/app-resource-uri.d.ts +28 -0
  95. package/dist/tools/app-resource-uri.d.ts.map +1 -0
  96. package/dist/tools/app-resource-uri.js +30 -0
  97. package/dist/tools/app-resource-uri.js.map +1 -0
  98. package/dist/tools/backend-registry.d.ts +111 -0
  99. package/dist/tools/backend-registry.d.ts.map +1 -0
  100. package/dist/tools/backend-registry.js +154 -0
  101. package/dist/tools/backend-registry.js.map +1 -0
  102. package/dist/tools/create-framework-app.d.ts +147 -0
  103. package/dist/tools/create-framework-app.d.ts.map +1 -0
  104. package/dist/tools/create-framework-app.js +111 -0
  105. package/dist/tools/create-framework-app.js.map +1 -0
  106. package/dist/tools/index.d.ts +22 -0
  107. package/dist/tools/index.d.ts.map +1 -0
  108. package/dist/tools/index.js +15 -0
  109. package/dist/tools/index.js.map +1 -0
  110. package/dist/tools/register-catalogue-tool.d.ts +22 -0
  111. package/dist/tools/register-catalogue-tool.d.ts.map +1 -0
  112. package/dist/tools/register-catalogue-tool.js +45 -0
  113. package/dist/tools/register-catalogue-tool.js.map +1 -0
  114. package/dist/tools/register-dashboard-tools.d.ts +32 -0
  115. package/dist/tools/register-dashboard-tools.d.ts.map +1 -0
  116. package/dist/tools/register-dashboard-tools.js +149 -0
  117. package/dist/tools/register-dashboard-tools.js.map +1 -0
  118. package/dist/tools/register-framework-tools.d.ts +83 -0
  119. package/dist/tools/register-framework-tools.d.ts.map +1 -0
  120. package/dist/tools/register-framework-tools.js +154 -0
  121. package/dist/tools/register-framework-tools.js.map +1 -0
  122. package/dist/tools/register-tool.d.ts +45 -0
  123. package/dist/tools/register-tool.d.ts.map +1 -0
  124. package/dist/tools/register-tool.js +82 -0
  125. package/dist/tools/register-tool.js.map +1 -0
  126. package/dist/tools/register-widget-tool.d.ts +51 -0
  127. package/dist/tools/register-widget-tool.d.ts.map +1 -0
  128. package/dist/tools/register-widget-tool.js +45 -0
  129. package/dist/tools/register-widget-tool.js.map +1 -0
  130. package/dist/tools/tool-call-name.d.ts +53 -0
  131. package/dist/tools/tool-call-name.d.ts.map +1 -0
  132. package/dist/tools/tool-call-name.js +92 -0
  133. package/dist/tools/tool-call-name.js.map +1 -0
  134. package/dist/tools/with-tool-errors.d.ts +16 -0
  135. package/dist/tools/with-tool-errors.d.ts.map +1 -0
  136. package/dist/tools/with-tool-errors.js +28 -0
  137. package/dist/tools/with-tool-errors.js.map +1 -0
  138. package/dist/types/app.d.ts +44 -0
  139. package/dist/types/app.d.ts.map +1 -0
  140. package/dist/types/app.js +2 -0
  141. package/dist/types/app.js.map +1 -0
  142. package/dist/types/config.d.ts +15 -0
  143. package/dist/types/config.d.ts.map +1 -0
  144. package/dist/types/config.js +2 -0
  145. package/dist/types/config.js.map +1 -0
  146. package/dist/types/context.d.ts +18 -0
  147. package/dist/types/context.d.ts.map +1 -0
  148. package/dist/types/context.js +2 -0
  149. package/dist/types/context.js.map +1 -0
  150. package/dist/types/index.d.ts +10 -0
  151. package/dist/types/index.d.ts.map +1 -0
  152. package/dist/types/index.js +2 -0
  153. package/dist/types/index.js.map +1 -0
  154. package/dist/types/meta.d.ts +99 -0
  155. package/dist/types/meta.d.ts.map +1 -0
  156. package/dist/types/meta.js +66 -0
  157. package/dist/types/meta.js.map +1 -0
  158. package/dist/types/pipeline.d.ts +9 -0
  159. package/dist/types/pipeline.d.ts.map +1 -0
  160. package/dist/types/pipeline.js +2 -0
  161. package/dist/types/pipeline.js.map +1 -0
  162. package/dist/types/step.d.ts +51 -0
  163. package/dist/types/step.d.ts.map +1 -0
  164. package/dist/types/step.js +2 -0
  165. package/dist/types/step.js.map +1 -0
  166. package/dist/types/view-data.d.ts +67 -0
  167. package/dist/types/view-data.d.ts.map +1 -0
  168. package/dist/types/view-data.js +2 -0
  169. package/dist/types/view-data.js.map +1 -0
  170. package/dist/types/widget.d.ts +57 -0
  171. package/dist/types/widget.d.ts.map +1 -0
  172. package/dist/types/widget.js +2 -0
  173. package/dist/types/widget.js.map +1 -0
  174. package/package.json +50 -0
@@ -0,0 +1,136 @@
1
+ import type { PipelineStepRef } from "../types/pipeline.js";
2
+ import type { LayoutConfig } from "./layout-types.js";
3
+ /**
4
+ * Current on-disk schema version stamped onto every saved record. Bump this
5
+ * whenever the persisted shape changes in a way a loader would need to
6
+ * migrate. Records persisted before versioning carry no `schemaVersion`
7
+ * (treated as the implicit version 0).
8
+ */
9
+ export declare const DASHBOARD_SCHEMA_VERSION = 1;
10
+ /**
11
+ * Persisted dashboard = full `render-view` input plus identity and
12
+ * ownership metadata. The server generates `id`, `createdAt`, `updatedAt`;
13
+ * `userId` is copied from `ctx.auth.user.userId` on save when present.
14
+ */
15
+ export interface DashboardRecord {
16
+ id: string;
17
+ name: string;
18
+ description?: string;
19
+ /** Owner; omitted when the host boots without OAuth (global scope). */
20
+ userId?: string;
21
+ keys?: Record<string, unknown>;
22
+ steps?: PipelineStepRef[];
23
+ layout: LayoutConfig;
24
+ title?: string;
25
+ /**
26
+ * On-disk schema version, set to {@link DASHBOARD_SCHEMA_VERSION} on every
27
+ * save. Optional so records written before versioning still type-check;
28
+ * loaders treat its absence as version 0.
29
+ */
30
+ schemaVersion?: number;
31
+ createdAt: string;
32
+ updatedAt: string;
33
+ }
34
+ export interface DashboardSaveInput {
35
+ id?: string;
36
+ name: string;
37
+ description?: string;
38
+ userId?: string;
39
+ keys?: Record<string, unknown>;
40
+ steps?: PipelineStepRef[];
41
+ layout: LayoutConfig;
42
+ title?: string;
43
+ }
44
+ export interface DashboardListFilter {
45
+ userId?: string;
46
+ }
47
+ /** Summary view returned by `list` — full layout body deliberately omitted. */
48
+ export interface DashboardSummary {
49
+ id: string;
50
+ name: string;
51
+ description?: string;
52
+ title?: string;
53
+ updatedAt: string;
54
+ }
55
+ /**
56
+ * Persistence backing for dashboards. The framework is indifferent to how
57
+ * records are stored — consumers inject whichever implementation fits
58
+ * their deployment (in-memory for tests, filesystem for local dev, a DB-
59
+ * backed one for production).
60
+ *
61
+ * All methods accept an optional `userId` filter so implementations can
62
+ * enforce ownership. Implementations that don't need ownership (global
63
+ * scope) should simply ignore the filter.
64
+ *
65
+ * `save` carries the acting user as `input.userId`: updating an existing
66
+ * record owned by a different user is rejected with
67
+ * {@link DashboardOwnershipError}, and `input.userId` can never reassign an
68
+ * existing record's owner. Records persisted without a `userId` (global
69
+ * scope) stay writable by anyone, matching the read/delete convention.
70
+ */
71
+ export interface DashboardStore {
72
+ save(input: DashboardSaveInput): Promise<DashboardRecord>;
73
+ list(filter: DashboardListFilter): Promise<DashboardSummary[]>;
74
+ get(id: string, filter: DashboardListFilter): Promise<DashboardRecord | undefined>;
75
+ delete(id: string, filter: DashboardListFilter): Promise<boolean>;
76
+ }
77
+ /**
78
+ * Validate a value read back from persistence into a {@link DashboardRecord},
79
+ * or `undefined` when it isn't one this version can safely use.
80
+ *
81
+ * Untrusted disk state is otherwise cast straight to `DashboardRecord`, so a
82
+ * corrupt or partial file (e.g. a missing `updatedAt`) slips through and later
83
+ * crashes `list` (its `updatedAt.localeCompare` sort) or feeds garbage into
84
+ * `render-view`. This is the single fail-soft gate: a record that fails the
85
+ * schema — or carries a `schemaVersion` newer than this build understands — is
86
+ * rejected here so callers skip it instead of trusting it.
87
+ *
88
+ * Exported for unit testing.
89
+ */
90
+ export declare function parseDashboardRecord(raw: unknown): DashboardRecord | undefined;
91
+ /**
92
+ * Error thrown by `save()` when an update would touch a record the caller
93
+ * doesn't own, or would change a record's owner. Distinct from the
94
+ * fail-soft "not found" of `get`/`delete` (which return `undefined`/`false`)
95
+ * because `save` has no nullable return — a write that violates ownership is
96
+ * an explicit denial, not a silent no-op.
97
+ */
98
+ export declare class DashboardOwnershipError extends Error {
99
+ constructor(message: string);
100
+ }
101
+ /**
102
+ * Resolve the record to persist for a `save`, enforcing ownership on updates.
103
+ *
104
+ * - New record (no `existing`): returns `null`, signalling the caller to
105
+ * create from `input`.
106
+ * - Update (`existing` present): throws {@link DashboardOwnershipError} when
107
+ * `input.userId` doesn't own `existing`, otherwise returns the merged
108
+ * record with `id`, `createdAt`, and — critically — `existing.userId`
109
+ * preserved so `input.userId` can never reassign the owner.
110
+ *
111
+ * Records without a `userId` (global scope, e.g. a host booted without OAuth)
112
+ * are deliberately writable by anyone — same convention `ownedBy` uses for
113
+ * read/delete — so single-user and OAuth-less deployments keep working.
114
+ *
115
+ * Exported for unit testing; the store factories below are the public API.
116
+ */
117
+ export declare function resolveSavedRecord(existing: DashboardRecord | undefined, input: DashboardSaveInput, now: string): DashboardRecord | null;
118
+ /**
119
+ * Process-local in-memory store. Fine for tests and throwaway demos; loses
120
+ * everything on restart. Default when `createFrameworkApp` is called
121
+ * without an explicit `dashboardStore`.
122
+ */
123
+ export declare function createInMemoryDashboardStore(): DashboardStore;
124
+ export interface FileSystemDashboardStoreOptions {
125
+ /** Directory where `<id>.json` files are written. Created on first save. */
126
+ dir: string;
127
+ }
128
+ /**
129
+ * Dashboards stored as one JSON file per record under `dir`. Suitable for
130
+ * single-node deployments that need survival across restarts. Locking is
131
+ * advisory: concurrent writes of the same id can race — fine for the v1
132
+ * "single user clicking Save" workflow, not fine for multi-writer
133
+ * production.
134
+ */
135
+ export declare function createFileSystemDashboardStore(options: FileSystemDashboardStoreOptions): DashboardStore;
136
+ //# sourceMappingURL=dashboard-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-store.d.ts","sourceRoot":"","sources":["../../src/framework/dashboard-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrD;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAA;AAEzC;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;IACzB,MAAM,EAAE,YAAY,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;IACzB,MAAM,EAAE,YAAY,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IACzD,IAAI,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;IAC9D,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAA;IAClF,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAClE;AA2BD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAO9E;AAYD;;;;;;GAMG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,eAAe,GAAG,SAAS,EACrC,KAAK,EAAE,kBAAkB,EACzB,GAAG,EAAE,MAAM,GACV,eAAe,GAAG,IAAI,CAgBxB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,cAAc,CAuD7D;AAED,MAAM,WAAW,+BAA+B;IAC9C,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,cAAc,CA0GhB"}
@@ -0,0 +1,301 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { randomUUID } from "node:crypto";
4
+ import { z } from "zod";
5
+ import { layoutSchema } from "./layout-schemas.js";
6
+ /**
7
+ * Current on-disk schema version stamped onto every saved record. Bump this
8
+ * whenever the persisted shape changes in a way a loader would need to
9
+ * migrate. Records persisted before versioning carry no `schemaVersion`
10
+ * (treated as the implicit version 0).
11
+ */
12
+ export const DASHBOARD_SCHEMA_VERSION = 1;
13
+ const stepRefSchema = z.object({
14
+ id: z.string(),
15
+ step: z.string(),
16
+ optional: z.boolean().optional(),
17
+ });
18
+ /**
19
+ * Runtime shape of a persisted dashboard. Kept in sync with
20
+ * {@link DashboardRecord}; `layout` reuses {@link layoutSchema} so a stored
21
+ * record and a fresh `render-view` layout validate against the same contract.
22
+ */
23
+ const dashboardRecordSchema = z.object({
24
+ id: z.string(),
25
+ name: z.string(),
26
+ description: z.string().optional(),
27
+ userId: z.string().optional(),
28
+ keys: z.record(z.string(), z.unknown()).optional(),
29
+ steps: z.array(stepRefSchema).optional(),
30
+ layout: layoutSchema,
31
+ title: z.string().optional(),
32
+ schemaVersion: z.number().optional(),
33
+ createdAt: z.string(),
34
+ updatedAt: z.string(),
35
+ });
36
+ /**
37
+ * Validate a value read back from persistence into a {@link DashboardRecord},
38
+ * or `undefined` when it isn't one this version can safely use.
39
+ *
40
+ * Untrusted disk state is otherwise cast straight to `DashboardRecord`, so a
41
+ * corrupt or partial file (e.g. a missing `updatedAt`) slips through and later
42
+ * crashes `list` (its `updatedAt.localeCompare` sort) or feeds garbage into
43
+ * `render-view`. This is the single fail-soft gate: a record that fails the
44
+ * schema — or carries a `schemaVersion` newer than this build understands — is
45
+ * rejected here so callers skip it instead of trusting it.
46
+ *
47
+ * Exported for unit testing.
48
+ */
49
+ export function parseDashboardRecord(raw) {
50
+ const result = dashboardRecordSchema.safeParse(raw);
51
+ if (!result.success)
52
+ return undefined;
53
+ // A record written by a newer build may use fields/semantics this version
54
+ // can't honour — refuse it rather than silently mis-reading it.
55
+ if ((result.data.schemaVersion ?? 0) > DASHBOARD_SCHEMA_VERSION)
56
+ return undefined;
57
+ return result.data;
58
+ }
59
+ function nowIso() {
60
+ return new Date().toISOString();
61
+ }
62
+ function ownedBy(record, userId) {
63
+ if (!userId)
64
+ return true;
65
+ if (!record.userId)
66
+ return true;
67
+ return record.userId === userId;
68
+ }
69
+ /**
70
+ * Error thrown by `save()` when an update would touch a record the caller
71
+ * doesn't own, or would change a record's owner. Distinct from the
72
+ * fail-soft "not found" of `get`/`delete` (which return `undefined`/`false`)
73
+ * because `save` has no nullable return — a write that violates ownership is
74
+ * an explicit denial, not a silent no-op.
75
+ */
76
+ export class DashboardOwnershipError extends Error {
77
+ constructor(message) {
78
+ super(message);
79
+ this.name = "DashboardOwnershipError";
80
+ }
81
+ }
82
+ /**
83
+ * Resolve the record to persist for a `save`, enforcing ownership on updates.
84
+ *
85
+ * - New record (no `existing`): returns `null`, signalling the caller to
86
+ * create from `input`.
87
+ * - Update (`existing` present): throws {@link DashboardOwnershipError} when
88
+ * `input.userId` doesn't own `existing`, otherwise returns the merged
89
+ * record with `id`, `createdAt`, and — critically — `existing.userId`
90
+ * preserved so `input.userId` can never reassign the owner.
91
+ *
92
+ * Records without a `userId` (global scope, e.g. a host booted without OAuth)
93
+ * are deliberately writable by anyone — same convention `ownedBy` uses for
94
+ * read/delete — so single-user and OAuth-less deployments keep working.
95
+ *
96
+ * Exported for unit testing; the store factories below are the public API.
97
+ */
98
+ export function resolveSavedRecord(existing, input, now) {
99
+ if (!existing)
100
+ return null;
101
+ if (!ownedBy(existing, input.userId)) {
102
+ throw new DashboardOwnershipError(`Access denied: dashboard "${existing.id}" is owned by another user.`);
103
+ }
104
+ return {
105
+ ...existing,
106
+ ...stripUndefined(input),
107
+ id: existing.id,
108
+ userId: existing.userId,
109
+ schemaVersion: DASHBOARD_SCHEMA_VERSION,
110
+ createdAt: existing.createdAt,
111
+ updatedAt: now,
112
+ };
113
+ }
114
+ /**
115
+ * Process-local in-memory store. Fine for tests and throwaway demos; loses
116
+ * everything on restart. Default when `createFrameworkApp` is called
117
+ * without an explicit `dashboardStore`.
118
+ */
119
+ export function createInMemoryDashboardStore() {
120
+ const byId = new Map();
121
+ return {
122
+ save(input) {
123
+ // Resolve-then-compute so a synchronous ownership violation surfaces as
124
+ // a rejected promise (matching the `Promise<DashboardRecord>` contract
125
+ // and the filesystem store), not a throw escaping the call site.
126
+ return Promise.resolve().then(() => {
127
+ const now = nowIso();
128
+ const existing = input.id ? byId.get(input.id) : undefined;
129
+ const record = resolveSavedRecord(existing, input, now) ?? {
130
+ id: input.id ?? randomUUID(),
131
+ name: input.name,
132
+ description: input.description,
133
+ userId: input.userId,
134
+ keys: input.keys,
135
+ steps: input.steps,
136
+ layout: input.layout,
137
+ title: input.title,
138
+ schemaVersion: DASHBOARD_SCHEMA_VERSION,
139
+ createdAt: now,
140
+ updatedAt: now,
141
+ };
142
+ byId.set(record.id, record);
143
+ return record;
144
+ });
145
+ },
146
+ list(filter) {
147
+ const all = [...byId.values()].filter((r) => ownedBy(r, filter.userId));
148
+ all.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
149
+ return Promise.resolve(all.map((r) => ({
150
+ id: r.id,
151
+ name: r.name,
152
+ description: r.description,
153
+ title: r.title,
154
+ updatedAt: r.updatedAt,
155
+ })));
156
+ },
157
+ get(id, filter) {
158
+ const record = byId.get(id);
159
+ if (!record)
160
+ return Promise.resolve(undefined);
161
+ if (!ownedBy(record, filter.userId))
162
+ return Promise.resolve(undefined);
163
+ return Promise.resolve(record);
164
+ },
165
+ delete(id, filter) {
166
+ const record = byId.get(id);
167
+ if (!record)
168
+ return Promise.resolve(false);
169
+ if (!ownedBy(record, filter.userId))
170
+ return Promise.resolve(false);
171
+ byId.delete(id);
172
+ return Promise.resolve(true);
173
+ },
174
+ };
175
+ }
176
+ /**
177
+ * Dashboards stored as one JSON file per record under `dir`. Suitable for
178
+ * single-node deployments that need survival across restarts. Locking is
179
+ * advisory: concurrent writes of the same id can race — fine for the v1
180
+ * "single user clicking Save" workflow, not fine for multi-writer
181
+ * production.
182
+ */
183
+ export function createFileSystemDashboardStore(options) {
184
+ const { dir } = options;
185
+ const ensureDir = async () => {
186
+ await fs.mkdir(dir, { recursive: true });
187
+ };
188
+ const fileFor = (id) => path.join(dir, `${encodeURIComponent(id)}.json`);
189
+ const readRecord = async (id) => {
190
+ let raw;
191
+ try {
192
+ raw = await fs.readFile(fileFor(id), "utf-8");
193
+ }
194
+ catch (err) {
195
+ if (err.code === "ENOENT")
196
+ return undefined;
197
+ throw err;
198
+ }
199
+ // Corrupt/incompatible content is treated as "not present" (fail-soft) so a
200
+ // single bad file can't crash `get`/`save`; a real fs error above still
201
+ // throws.
202
+ let parsed;
203
+ try {
204
+ parsed = JSON.parse(raw);
205
+ }
206
+ catch {
207
+ console.warn(`[dashboard-store] Ignoring dashboard "${id}": file is not valid JSON.`);
208
+ return undefined;
209
+ }
210
+ const record = parseDashboardRecord(parsed);
211
+ if (!record) {
212
+ console.warn(`[dashboard-store] Ignoring dashboard "${id}": does not match the current record schema.`);
213
+ }
214
+ return record;
215
+ };
216
+ const writeRecord = async (record) => {
217
+ await ensureDir();
218
+ await fs.writeFile(fileFor(record.id), JSON.stringify(record, null, 2), "utf-8");
219
+ };
220
+ return {
221
+ async save(input) {
222
+ const now = nowIso();
223
+ const existing = input.id ? await readRecord(input.id) : undefined;
224
+ const record = resolveSavedRecord(existing, input, now) ?? {
225
+ id: input.id ?? randomUUID(),
226
+ name: input.name,
227
+ description: input.description,
228
+ userId: input.userId,
229
+ keys: input.keys,
230
+ steps: input.steps,
231
+ layout: input.layout,
232
+ title: input.title,
233
+ schemaVersion: DASHBOARD_SCHEMA_VERSION,
234
+ createdAt: now,
235
+ updatedAt: now,
236
+ };
237
+ await writeRecord(record);
238
+ return record;
239
+ },
240
+ async list(filter) {
241
+ await ensureDir();
242
+ const entries = await fs.readdir(dir);
243
+ const records = [];
244
+ for (const name of entries) {
245
+ if (!name.endsWith(".json"))
246
+ continue;
247
+ let parsed;
248
+ try {
249
+ parsed = JSON.parse(await fs.readFile(path.join(dir, name), "utf-8"));
250
+ }
251
+ catch {
252
+ // Not valid JSON — not one of our records. Skip.
253
+ continue;
254
+ }
255
+ // Validate before trusting: a partial record (e.g. missing `updatedAt`)
256
+ // would otherwise crash the sort below and take the whole listing down.
257
+ const record = parseDashboardRecord(parsed);
258
+ if (!record) {
259
+ console.warn(`[dashboard-store] Skipping "${name}": does not match the record schema.`);
260
+ continue;
261
+ }
262
+ if (ownedBy(record, filter.userId))
263
+ records.push(record);
264
+ }
265
+ records.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
266
+ return records.map((r) => ({
267
+ id: r.id,
268
+ name: r.name,
269
+ description: r.description,
270
+ title: r.title,
271
+ updatedAt: r.updatedAt,
272
+ }));
273
+ },
274
+ async get(id, filter) {
275
+ const record = await readRecord(id);
276
+ if (!record)
277
+ return undefined;
278
+ if (!ownedBy(record, filter.userId))
279
+ return undefined;
280
+ return record;
281
+ },
282
+ async delete(id, filter) {
283
+ const record = await readRecord(id);
284
+ if (!record)
285
+ return false;
286
+ if (!ownedBy(record, filter.userId))
287
+ return false;
288
+ await fs.rm(fileFor(id), { force: true });
289
+ return true;
290
+ },
291
+ };
292
+ }
293
+ function stripUndefined(obj) {
294
+ const out = {};
295
+ for (const [k, v] of Object.entries(obj)) {
296
+ if (v !== undefined)
297
+ out[k] = v;
298
+ }
299
+ return out;
300
+ }
301
+ //# sourceMappingURL=dashboard-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-store.js","sourceRoot":"","sources":["../../src/framework/dashboard-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Zod schemas for the layout shape passed to `render-view` / `refresh-view`.
4
+ *
5
+ * Kept next to the `LayoutConfig` TypeScript types (see `./layout-types.ts`) so
6
+ * the runtime validation and the static type of `LayoutConfig` stay in sync.
7
+ * The framework-tool registrar (`../tools/register-framework-tools.ts`) plugs
8
+ * these schemas into the mcp-use tool definitions.
9
+ */
10
+ export declare const rowSchema: z.ZodObject<{
11
+ row: z.ZodArray<z.ZodObject<{
12
+ widget: z.ZodString;
13
+ span: z.ZodOptional<z.ZodNumber>;
14
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
15
+ }, z.core.$strip>>;
16
+ }, z.core.$strip>;
17
+ export declare const layoutSchema: z.ZodUnion<readonly [z.ZodArray<z.ZodObject<{
18
+ row: z.ZodArray<z.ZodObject<{
19
+ widget: z.ZodString;
20
+ span: z.ZodOptional<z.ZodNumber>;
21
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
22
+ }, z.core.$strip>>;
23
+ }, z.core.$strip>>, z.ZodObject<{
24
+ rows: z.ZodArray<z.ZodObject<{
25
+ row: z.ZodArray<z.ZodObject<{
26
+ widget: z.ZodString;
27
+ span: z.ZodOptional<z.ZodNumber>;
28
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
29
+ }, z.core.$strip>>;
30
+ }, z.core.$strip>>;
31
+ }, z.core.$strip>, z.ZodObject<{
32
+ tabs: z.ZodArray<z.ZodObject<{
33
+ label: z.ZodString;
34
+ rows: z.ZodArray<z.ZodObject<{
35
+ row: z.ZodArray<z.ZodObject<{
36
+ widget: z.ZodString;
37
+ span: z.ZodOptional<z.ZodNumber>;
38
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
39
+ }, z.core.$strip>>;
40
+ }, z.core.$strip>>;
41
+ }, z.core.$strip>>;
42
+ }, z.core.$strip>]>;
43
+ /**
44
+ * Tool-input variant of {@link layoutSchema} for `render-view` /
45
+ * `refresh-view`: the three layout forms plus a JSON-string branch.
46
+ *
47
+ * Why: as a union, `layout` surfaces on the wire as a top-level `anyOf`
48
+ * WITHOUT its own `type` field. Some MCP hosts (observed with claude.ai)
49
+ * react to such properties by sending the argument as a JSON-encoded STRING —
50
+ * which the plain union rejects, failing the whole call. The string branch
51
+ * parses that form and pipes it through {@link layoutSchema}, so stringified
52
+ * calls validate exactly like the object form. The three object branches are
53
+ * reused from {@link layoutSchema} unchanged.
54
+ */
55
+ export declare const layoutInputSchema: z.ZodUnion<readonly [z.ZodArray<z.ZodObject<{
56
+ row: z.ZodArray<z.ZodObject<{
57
+ widget: z.ZodString;
58
+ span: z.ZodOptional<z.ZodNumber>;
59
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
60
+ }, z.core.$strip>>;
61
+ }, z.core.$strip>>, z.ZodObject<{
62
+ rows: z.ZodArray<z.ZodObject<{
63
+ row: z.ZodArray<z.ZodObject<{
64
+ widget: z.ZodString;
65
+ span: z.ZodOptional<z.ZodNumber>;
66
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
67
+ }, z.core.$strip>>;
68
+ }, z.core.$strip>>;
69
+ }, z.core.$strip>, z.ZodObject<{
70
+ tabs: z.ZodArray<z.ZodObject<{
71
+ label: z.ZodString;
72
+ rows: z.ZodArray<z.ZodObject<{
73
+ row: z.ZodArray<z.ZodObject<{
74
+ widget: z.ZodString;
75
+ span: z.ZodOptional<z.ZodNumber>;
76
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
77
+ }, z.core.$strip>>;
78
+ }, z.core.$strip>>;
79
+ }, z.core.$strip>>;
80
+ }, z.core.$strip>, z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<unknown, string>>, z.ZodUnion<readonly [z.ZodArray<z.ZodObject<{
81
+ row: z.ZodArray<z.ZodObject<{
82
+ widget: z.ZodString;
83
+ span: z.ZodOptional<z.ZodNumber>;
84
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
85
+ }, z.core.$strip>>;
86
+ }, z.core.$strip>>, z.ZodObject<{
87
+ rows: z.ZodArray<z.ZodObject<{
88
+ row: z.ZodArray<z.ZodObject<{
89
+ widget: z.ZodString;
90
+ span: z.ZodOptional<z.ZodNumber>;
91
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
92
+ }, z.core.$strip>>;
93
+ }, z.core.$strip>>;
94
+ }, z.core.$strip>, z.ZodObject<{
95
+ tabs: z.ZodArray<z.ZodObject<{
96
+ label: z.ZodString;
97
+ rows: z.ZodArray<z.ZodObject<{
98
+ row: z.ZodArray<z.ZodObject<{
99
+ widget: z.ZodString;
100
+ span: z.ZodOptional<z.ZodNumber>;
101
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
102
+ }, z.core.$strip>>;
103
+ }, z.core.$strip>>;
104
+ }, z.core.$strip>>;
105
+ }, z.core.$strip>]>>]>;
106
+ //# sourceMappingURL=layout-schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-schemas.d.ts","sourceRoot":"","sources":["../../src/framework/layout-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;GAOG;AAEH,eAAO,MAAM,SAAS;;;;;;iBAapB,CAAA;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;mBAeqC,CAAA;AAE9D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAuB3B,CAAA"}
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Zod schemas for the layout shape passed to `render-view` / `refresh-view`.
4
+ *
5
+ * Kept next to the `LayoutConfig` TypeScript types (see `./layout-types.ts`) so
6
+ * the runtime validation and the static type of `LayoutConfig` stay in sync.
7
+ * The framework-tool registrar (`../tools/register-framework-tools.ts`) plugs
8
+ * these schemas into the mcp-use tool definitions.
9
+ */
10
+ export const rowSchema = z.object({
11
+ row: z.array(z.object({
12
+ widget: z.string().describe("Widget ID from the framework manifest. Must be registered."),
13
+ span: z.number().optional().describe("Grid columns (1-12)"),
14
+ props: z
15
+ .record(z.string(), z.unknown())
16
+ .optional()
17
+ .describe("Optional per-instance props passed to the widget at render time. Lets the same widget appear multiple times in one view with different scoping (e.g. `{ processDefinitionKey: 'miraveloLeasing' }` for a per-process dashboard tab)."),
18
+ })),
19
+ });
20
+ export const layoutSchema = z
21
+ .union([
22
+ z.array(rowSchema).describe("Flat rows (legacy form)"),
23
+ z.object({ rows: z.array(rowSchema) }).describe("Explicit rows wrapper"),
24
+ z
25
+ .object({
26
+ tabs: z.array(z.object({
27
+ label: z.string().describe("Tab label"),
28
+ rows: z.array(rowSchema),
29
+ })),
30
+ })
31
+ .describe("Tabs — widgets grouped in tabs"),
32
+ ])
33
+ .describe("Widget layout: flat rows, { rows }, or { tabs }");
34
+ /**
35
+ * Tool-input variant of {@link layoutSchema} for `render-view` /
36
+ * `refresh-view`: the three layout forms plus a JSON-string branch.
37
+ *
38
+ * Why: as a union, `layout` surfaces on the wire as a top-level `anyOf`
39
+ * WITHOUT its own `type` field. Some MCP hosts (observed with claude.ai)
40
+ * react to such properties by sending the argument as a JSON-encoded STRING —
41
+ * which the plain union rejects, failing the whole call. The string branch
42
+ * parses that form and pipes it through {@link layoutSchema}, so stringified
43
+ * calls validate exactly like the object form. The three object branches are
44
+ * reused from {@link layoutSchema} unchanged.
45
+ */
46
+ export const layoutInputSchema = z
47
+ .union([
48
+ ...layoutSchema.def.options,
49
+ z
50
+ .string()
51
+ .transform((value, ctx) => {
52
+ try {
53
+ return JSON.parse(value);
54
+ }
55
+ catch {
56
+ ctx.addIssue({
57
+ code: "custom",
58
+ message: "layout was sent as a string but is not valid JSON",
59
+ });
60
+ return z.NEVER;
61
+ }
62
+ })
63
+ .pipe(layoutSchema)
64
+ .describe("Layout as a JSON-encoded string (some hosts serialize complex parameters this way); parsed and validated like the object form."),
65
+ ])
66
+ .describe("Widget layout: flat rows, { rows }, or { tabs } — or the same as a JSON-encoded string");
67
+ //# sourceMappingURL=layout-schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-schemas.js","sourceRoot":"","sources":["../../src/framework/layout-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,GAAG,EAAE,CAAC,CAAC,KAAK,CACV,CAAC,CAAC,MAAM,CAAC;QACP,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4DAA4D,CAAC;QACzF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QAC3D,KAAK,EAAE,CAAC;aACL,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;aAC/B,QAAQ,EAAE;aACV,QAAQ,CACP,sOAAsO,CACvO;KACJ,CAAC,CACH;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC;KAC1B,KAAK,CAAC;IACL,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IACtD,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC;IACxE,CAAC;SACE,MAAM,CAAC;QACN,IAAI,EAAE,CAAC,CAAC,KAAK,CACX,CAAC,CAAC,MAAM,CAAC;YACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;YACvC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;SACzB,CAAC,CACH;KACF,CAAC;SACD,QAAQ,CAAC,gCAAgC,CAAC;CAC9C,CAAC;KACD,QAAQ,CAAC,iDAAiD,CAAC,CAAA;AAE9D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,KAAK,CAAC;IACL,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO;IAC3B,CAAC;SACE,MAAM,EAAE;SACR,SAAS,CAAC,CAAC,KAAK,EAAE,GAAG,EAAW,EAAE;QACjC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,mDAAmD;aAC7D,CAAC,CAAA;YACF,OAAO,CAAC,CAAC,KAAK,CAAA;QAChB,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,YAAY,CAAC;SAClB,QAAQ,CACP,gIAAgI,CACjI;CACJ,CAAC;KACD,QAAQ,CACP,wFAAwF,CACzF,CAAA"}
@@ -0,0 +1,25 @@
1
+ export interface RowDef {
2
+ row: {
3
+ widget: string;
4
+ span?: number;
5
+ props?: Record<string, unknown>;
6
+ }[];
7
+ }
8
+ export type LayoutConfig = RowDef[] | {
9
+ rows: RowDef[];
10
+ } | {
11
+ tabs: {
12
+ label: string;
13
+ rows: RowDef[];
14
+ }[];
15
+ };
16
+ /** Normalizes any layout format to the canonical union type (rows or tabs object). */
17
+ export declare function normalizeLayout(layout: LayoutConfig): {
18
+ rows: RowDef[];
19
+ } | {
20
+ tabs: {
21
+ label: string;
22
+ rows: RowDef[];
23
+ }[];
24
+ };
25
+ //# sourceMappingURL=layout-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-types.d.ts","sourceRoot":"","sources":["../../src/framework/layout-types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,CAAA;CAC1E;AAED,MAAM,MAAM,YAAY,GACpB,MAAM,EAAE,GACR;IAAE,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;CAAE,CAAA;AAEjD,sFAAsF;AACtF,wBAAgB,eAAe,CAC7B,MAAM,EAAE,YAAY,GACnB;IAAE,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;CAAE,CAGpE"}