@restforgejs/mcp-server 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +149 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/env-parser.d.ts +30 -0
- package/dist/lib/env-parser.js +150 -0
- package/dist/lib/env-parser.js.map +1 -0
- package/dist/lib/exec.d.ts +29 -0
- package/dist/lib/exec.js +38 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +220 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/codegen/create-dashboard.d.ts +2 -0
- package/dist/tools/codegen/create-dashboard.js +256 -0
- package/dist/tools/codegen/create-dashboard.js.map +1 -0
- package/dist/tools/codegen/create-endpoint.d.ts +2 -0
- package/dist/tools/codegen/create-endpoint.js +263 -0
- package/dist/tools/codegen/create-endpoint.js.map +1 -0
- package/dist/tools/codegen/dbschema-generate-ddl.d.ts +2 -0
- package/dist/tools/codegen/dbschema-generate-ddl.js +187 -0
- package/dist/tools/codegen/dbschema-generate-ddl.js.map +1 -0
- package/dist/tools/codegen/dbschema-init.d.ts +2 -0
- package/dist/tools/codegen/dbschema-init.js +158 -0
- package/dist/tools/codegen/dbschema-init.js.map +1 -0
- package/dist/tools/codegen/dbschema-introspect.d.ts +2 -0
- package/dist/tools/codegen/dbschema-introspect.js +241 -0
- package/dist/tools/codegen/dbschema-introspect.js.map +1 -0
- package/dist/tools/codegen/dbschema-migrate.d.ts +2 -0
- package/dist/tools/codegen/dbschema-migrate.js +219 -0
- package/dist/tools/codegen/dbschema-migrate.js.map +1 -0
- package/dist/tools/codegen/dbschema-models.d.ts +2 -0
- package/dist/tools/codegen/dbschema-models.js +146 -0
- package/dist/tools/codegen/dbschema-models.js.map +1 -0
- package/dist/tools/codegen/dbschema-validate.d.ts +2 -0
- package/dist/tools/codegen/dbschema-validate.js +153 -0
- package/dist/tools/codegen/dbschema-validate.js.map +1 -0
- package/dist/tools/codegen/describe-table.d.ts +2 -0
- package/dist/tools/codegen/describe-table.js +259 -0
- package/dist/tools/codegen/describe-table.js.map +1 -0
- package/dist/tools/codegen/diff-payload.d.ts +2 -0
- package/dist/tools/codegen/diff-payload.js +165 -0
- package/dist/tools/codegen/diff-payload.js.map +1 -0
- package/dist/tools/codegen/generate-payload.d.ts +2 -0
- package/dist/tools/codegen/generate-payload.js +145 -0
- package/dist/tools/codegen/generate-payload.js.map +1 -0
- package/dist/tools/codegen/get-dashboard-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-dashboard-catalog.js +213 -0
- package/dist/tools/codegen/get-dashboard-catalog.js.map +1 -0
- package/dist/tools/codegen/get-dbschema-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-dbschema-catalog.js +244 -0
- package/dist/tools/codegen/get-dbschema-catalog.js.map +1 -0
- package/dist/tools/codegen/get-field-validation-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-field-validation-catalog.js +186 -0
- package/dist/tools/codegen/get-field-validation-catalog.js.map +1 -0
- package/dist/tools/codegen/get-query-declarative-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-query-declarative-catalog.js +200 -0
- package/dist/tools/codegen/get-query-declarative-catalog.js.map +1 -0
- package/dist/tools/codegen/index.d.ts +2 -0
- package/dist/tools/codegen/index.js +43 -0
- package/dist/tools/codegen/index.js.map +1 -0
- package/dist/tools/codegen/list-tables.d.ts +2 -0
- package/dist/tools/codegen/list-tables.js +220 -0
- package/dist/tools/codegen/list-tables.js.map +1 -0
- package/dist/tools/codegen/sync-payload.d.ts +2 -0
- package/dist/tools/codegen/sync-payload.js +177 -0
- package/dist/tools/codegen/sync-payload.js.map +1 -0
- package/dist/tools/codegen/validate-dashboard-payload.d.ts +2 -0
- package/dist/tools/codegen/validate-dashboard-payload.js +239 -0
- package/dist/tools/codegen/validate-dashboard-payload.js.map +1 -0
- package/dist/tools/codegen/validate-payload.d.ts +2 -0
- package/dist/tools/codegen/validate-payload.js +166 -0
- package/dist/tools/codegen/validate-payload.js.map +1 -0
- package/dist/tools/codegen/validate-sql.d.ts +2 -0
- package/dist/tools/codegen/validate-sql.js +270 -0
- package/dist/tools/codegen/validate-sql.js.map +1 -0
- package/dist/tools/health/index.d.ts +2 -0
- package/dist/tools/health/index.js +5 -0
- package/dist/tools/health/index.js.map +1 -0
- package/dist/tools/health/ping.d.ts +2 -0
- package/dist/tools/health/ping.js +68 -0
- package/dist/tools/health/ping.js.map +1 -0
- package/dist/tools/runtime/check-launcher-exists.d.ts +2 -0
- package/dist/tools/runtime/check-launcher-exists.js +111 -0
- package/dist/tools/runtime/check-launcher-exists.js.map +1 -0
- package/dist/tools/runtime/check-status.d.ts +2 -0
- package/dist/tools/runtime/check-status.js +417 -0
- package/dist/tools/runtime/check-status.js.map +1 -0
- package/dist/tools/runtime/detect-config.d.ts +2 -0
- package/dist/tools/runtime/detect-config.js +130 -0
- package/dist/tools/runtime/detect-config.js.map +1 -0
- package/dist/tools/runtime/detect-project.d.ts +2 -0
- package/dist/tools/runtime/detect-project.js +132 -0
- package/dist/tools/runtime/detect-project.js.map +1 -0
- package/dist/tools/runtime/generate-launcher.d.ts +2 -0
- package/dist/tools/runtime/generate-launcher.js +438 -0
- package/dist/tools/runtime/generate-launcher.js.map +1 -0
- package/dist/tools/runtime/index.d.ts +2 -0
- package/dist/tools/runtime/index.js +15 -0
- package/dist/tools/runtime/index.js.map +1 -0
- package/dist/tools/runtime/validate-preflight.d.ts +2 -0
- package/dist/tools/runtime/validate-preflight.js +209 -0
- package/dist/tools/runtime/validate-preflight.js.map +1 -0
- package/dist/tools/setup/create-folder.d.ts +2 -0
- package/dist/tools/setup/create-folder.js +138 -0
- package/dist/tools/setup/create-folder.js.map +1 -0
- package/dist/tools/setup/get-config-schema.d.ts +2 -0
- package/dist/tools/setup/get-config-schema.js +158 -0
- package/dist/tools/setup/get-config-schema.js.map +1 -0
- package/dist/tools/setup/get-init-template.d.ts +2 -0
- package/dist/tools/setup/get-init-template.js +130 -0
- package/dist/tools/setup/get-init-template.js.map +1 -0
- package/dist/tools/setup/index.d.ts +2 -0
- package/dist/tools/setup/index.js +21 -0
- package/dist/tools/setup/index.js.map +1 -0
- package/dist/tools/setup/init-config.d.ts +2 -0
- package/dist/tools/setup/init-config.js +120 -0
- package/dist/tools/setup/init-config.js.map +1 -0
- package/dist/tools/setup/install-package.d.ts +2 -0
- package/dist/tools/setup/install-package.js +133 -0
- package/dist/tools/setup/install-package.js.map +1 -0
- package/dist/tools/setup/read-env.d.ts +2 -0
- package/dist/tools/setup/read-env.js +138 -0
- package/dist/tools/setup/read-env.js.map +1 -0
- package/dist/tools/setup/update-env.d.ts +2 -0
- package/dist/tools/setup/update-env.js +176 -0
- package/dist/tools/setup/update-env.js.map +1 -0
- package/dist/tools/setup/validate-config.d.ts +2 -0
- package/dist/tools/setup/validate-config.js +138 -0
- package/dist/tools/setup/validate-config.js.map +1 -0
- package/dist/tools/setup/write-env.d.ts +2 -0
- package/dist/tools/setup/write-env.js +168 -0
- package/dist/tools/setup/write-env.js.map +1 -0
- package/package.json +60 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { registerHealthTools } from './tools/health/index.js';
|
|
4
|
+
import { registerSetupTools } from './tools/setup/index.js';
|
|
5
|
+
import { registerCodegenTools } from './tools/codegen/index.js';
|
|
6
|
+
import { registerRuntimeTools } from './tools/runtime/index.js';
|
|
7
|
+
const SERVER_NAME = 'restforge-mcp';
|
|
8
|
+
const SERVER_VERSION = '1.0.0';
|
|
9
|
+
const SERVER_INSTRUCTIONS = `
|
|
10
|
+
This MCP server manages RESTForge backend project configuration.
|
|
11
|
+
|
|
12
|
+
WHEN TO PREFER THESE TOOLS over generic filesystem operations:
|
|
13
|
+
- The user asks about project configuration, settings, env values, or "what's set up"
|
|
14
|
+
- The user asks vague questions like "apa yang sudah di-set", "cek project",
|
|
15
|
+
"tampilkan konfigurasi", "is this configured", "what's the status"
|
|
16
|
+
- The user mentions "parameter", "config", "setup", "konfigurasi" in any form
|
|
17
|
+
- The current working directory contains @restforgejs/platform in node_modules
|
|
18
|
+
|
|
19
|
+
In these cases, prefer using RESTForge tools (setup_read_env, etc.) to inspect
|
|
20
|
+
the actual state, rather than reading files manually with filesystem tools or
|
|
21
|
+
guessing from directory structure.
|
|
22
|
+
|
|
23
|
+
If the configuration file does not exist, that is meaningful information —
|
|
24
|
+
the precondition response will tell you, and you should pass that information
|
|
25
|
+
to the user as a question about whether to set up the initial config.
|
|
26
|
+
|
|
27
|
+
Do not enumerate what "should" be in a backend project (folders like src, routes,
|
|
28
|
+
models, etc.). RESTForge has its own conventions; do not assume generic Express
|
|
29
|
+
or NestJS structure.
|
|
30
|
+
|
|
31
|
+
PROACTIVE USAGE:
|
|
32
|
+
When the user works in a project that contains @restforgejs/platform in node_modules,
|
|
33
|
+
prefer these tools over generic file/bash operations for the following:
|
|
34
|
+
- Reading or writing config/db-connection.env
|
|
35
|
+
- Validating config (license, DB, redis, kafka connections)
|
|
36
|
+
- Generating, validating, diffing, or syncing payload JSON files
|
|
37
|
+
- Looking up the field validation catalog before editing payload constraints
|
|
38
|
+
- Installing or upgrading the @restforgejs/platform package
|
|
39
|
+
|
|
40
|
+
Detection signals for "this is a RESTForge project":
|
|
41
|
+
- Folder contains node_modules/@restforgejs/platform, OR
|
|
42
|
+
- Folder contains config/db-connection.env, OR
|
|
43
|
+
- Folder contains a payload/ directory with .json files, OR
|
|
44
|
+
- The user explicitly mentions "RESTForge", "restforge", "@restforgejs/platform", or
|
|
45
|
+
related keywords (payload, codegen, db-connection, license)
|
|
46
|
+
|
|
47
|
+
When uncertain whether a folder is a RESTForge project, call setup_read_env
|
|
48
|
+
first to inspect the config file content — it's a cheap local file read with
|
|
49
|
+
no network calls. Don't assume from filenames alone. (setup_validate_config
|
|
50
|
+
exists too, but it actively connects to the database and license server, so
|
|
51
|
+
reserve it for explicit validation requests, not initial inspection.)
|
|
52
|
+
|
|
53
|
+
Do not silently fall back to generic file/bash operations for the categories
|
|
54
|
+
above. If a request requires an operation outside these tools' scope (e.g.
|
|
55
|
+
DDL changes — see LAYER BOUNDARY below; running, stopping, or restarting the
|
|
56
|
+
server — see RUNTIME LIFECYCLE BOUNDARY below), state that explicitly to the
|
|
57
|
+
user before proceeding.
|
|
58
|
+
|
|
59
|
+
RUNTIME LIFECYCLE BOUNDARY:
|
|
60
|
+
For any user request involving starting, stopping, or restarting the
|
|
61
|
+
RESTForge runtime server (e.g. "run my app", "jalankan server", "start
|
|
62
|
+
restforge", "stop server", "restart server"), you MUST use the runtime_*
|
|
63
|
+
tools — specifically 'runtime_generate_launcher' to scaffold a launcher
|
|
64
|
+
script. Do NOT use the Bash tool to spawn 'npx restforge', 'pm2 start <app>',
|
|
65
|
+
or equivalent processes. Server processes spawned via Bash become children of
|
|
66
|
+
this AI session and will be killed when the session closes (process tree dies
|
|
67
|
+
with the parent).
|
|
68
|
+
|
|
69
|
+
The correct flow is: detect project (runtime_detect_project) → detect config
|
|
70
|
+
(runtime_detect_config) → validate preflight (runtime_validate_preflight) →
|
|
71
|
+
generate launcher (runtime_generate_launcher) → tell the user to execute the
|
|
72
|
+
generated script themselves. The user's terminal, not the AI session, must
|
|
73
|
+
own the running server process. This applies even when the request looks
|
|
74
|
+
short ("just run it") or when running in the background via Bash seems
|
|
75
|
+
convenient — the lifecycle constraint is absolute.
|
|
76
|
+
|
|
77
|
+
Stopping or restarting an already-running server: tell the user the exact
|
|
78
|
+
command or file to execute (e.g. "run server-stop.bat" or "pm2 restart
|
|
79
|
+
<project>"). Do NOT spawn the stop/restart command via Bash on behalf of the
|
|
80
|
+
user. Read-only inspection — 'runtime_check_status', reading the PID file,
|
|
81
|
+
calling 'pm2 jlist' — is allowed because it does not mutate the server
|
|
82
|
+
lifecycle.
|
|
83
|
+
|
|
84
|
+
If the user explicitly insists on a one-off background run despite the
|
|
85
|
+
warning ("I know it will die, just run it for now"), state plainly that the
|
|
86
|
+
process will terminate when this session ends, then comply only as a last
|
|
87
|
+
resort. Default behaviour: refuse Bash-based start and route through the
|
|
88
|
+
runtime_* tools.
|
|
89
|
+
|
|
90
|
+
LAYER BOUNDARY:
|
|
91
|
+
The codegen and setup tools manage application-layer behavior in payload JSON
|
|
92
|
+
files and generated model code. They do NOT modify database DDL (tables,
|
|
93
|
+
columns, indexes, foreign keys, CHECK constraints, UNIQUE constraints).
|
|
94
|
+
|
|
95
|
+
When the user uses SQL DDL terminology — NOT NULL, UNIQUE, CHECK, REFERENCES,
|
|
96
|
+
ALTER TABLE, CREATE INDEX, DEFAULT (in DDL context) — do not automatically map
|
|
97
|
+
to payload validation. Clarify which layer the user wants:
|
|
98
|
+
(a) Application-layer validation in payload (e.g. required, unique, min,
|
|
99
|
+
maxLength, pattern, enum) — handled by these tools
|
|
100
|
+
(b) Database-level DDL changes — out of scope; suggest direct SQL or a
|
|
101
|
+
migration tool
|
|
102
|
+
|
|
103
|
+
Both layers can co-exist for the same field. They serve different purposes:
|
|
104
|
+
DDL enforces at storage level (rejects with database error); payload validation
|
|
105
|
+
provides structured HTTP 400 responses with custom error messages before the
|
|
106
|
+
request reaches the database. Confirm intent before taking action — don't
|
|
107
|
+
conflate the two layers.
|
|
108
|
+
|
|
109
|
+
PROJECT FILE TAXONOMY:
|
|
110
|
+
The RESTForge ecosystem uses three categories of declarative definition files.
|
|
111
|
+
When the user mentions these abbreviations (case-insensitive), recognise and
|
|
112
|
+
route them as follows:
|
|
113
|
+
|
|
114
|
+
- SDF (Schema Definition File): JavaScript factory function (.js) at
|
|
115
|
+
schema/<table>.js that declares table structure for dbschema-kit. Source
|
|
116
|
+
for 'schema migrate' (apply DDL to database) and produced by
|
|
117
|
+
'schema introspect' (reverse-engineer database).
|
|
118
|
+
Example: <project>/schema/category.js
|
|
119
|
+
|
|
120
|
+
- RDF (Resource Definition File): JSON file (.json) at
|
|
121
|
+
payload/<resource>.json that declares a backend REST API endpoint (CRUD +
|
|
122
|
+
datatables/view/export queries) for the RESTForge generator.
|
|
123
|
+
Example: <project>/payload/category.json
|
|
124
|
+
|
|
125
|
+
- UDF (UI Definition File): JSON file (.json) at payload/NN-<name>.json in
|
|
126
|
+
the frontend project that declares page/component structure for the UI
|
|
127
|
+
generator.
|
|
128
|
+
Example: <frontend-project>/payload/01-category.json
|
|
129
|
+
|
|
130
|
+
Routing for SDF requests:
|
|
131
|
+
- create / init / scaffold -> codegen_dbschema_init
|
|
132
|
+
- validate -> codegen_dbschema_validate
|
|
133
|
+
- list models / show structural summary -> codegen_dbschema_models
|
|
134
|
+
- generate DDL (preview or to file) -> codegen_dbschema_generate_ddl
|
|
135
|
+
- reverse-engineer from database -> codegen_dbschema_introspect
|
|
136
|
+
- apply to database (DESTRUCTIVE) -> codegen_dbschema_migrate
|
|
137
|
+
- lookup syntax / defineModel API spec -> codegen_get_dbschema_catalog
|
|
138
|
+
|
|
139
|
+
Routing for RDF requests:
|
|
140
|
+
- generate from database table -> codegen_generate_payload
|
|
141
|
+
- validate against current database -> codegen_validate_payload
|
|
142
|
+
- diff against database -> codegen_diff_payload
|
|
143
|
+
- sync (merge changes back to JSON) -> codegen_sync_payload
|
|
144
|
+
- generate endpoint module from RDF -> codegen_create_endpoint
|
|
145
|
+
- lookup field validation spec -> codegen_get_field_validation_catalog
|
|
146
|
+
- lookup query declarative spec -> codegen_get_query_declarative_catalog
|
|
147
|
+
|
|
148
|
+
UDF authoring and generation belongs to the frontend generator toolchain,
|
|
149
|
+
which is a separate workflow from this MCP server. This server currently
|
|
150
|
+
covers the SDF (database) layer and the RDF (backend API) layer; the UDF
|
|
151
|
+
layer is handled by the frontend project's own generator. When the user
|
|
152
|
+
asks about UDF — e.g. "generate UDF for category", "scaffold UI from
|
|
153
|
+
payload", "buatkan UDF untuk halaman X" — recognise UDF as a valid
|
|
154
|
+
first-class concept in the ecosystem and respond constructively:
|
|
155
|
+
|
|
156
|
+
1. Confirm in plain language that you understood the UDF intent (e.g.
|
|
157
|
+
"you want to generate the frontend UI definition for category").
|
|
158
|
+
2. Explain helpfully that the UDF generation step lives in the frontend
|
|
159
|
+
project and is run by its dedicated generator, separate from this
|
|
160
|
+
server's surface.
|
|
161
|
+
3. Suggest concrete next steps in the user's frontend project: locate
|
|
162
|
+
the payload/NN-<name>.json file, run the frontend generator there,
|
|
163
|
+
and verify the generated output.
|
|
164
|
+
4. Offer continued assistance for the SDF and RDF layers (which this
|
|
165
|
+
server DOES cover) — for example, you can still help draft the
|
|
166
|
+
underlying RDF that the frontend will consume.
|
|
167
|
+
|
|
168
|
+
Tone guidance: frame UDF support as "this stage lives in another part of
|
|
169
|
+
the workflow" rather than "this is unsupported" or "I cannot help". UDF
|
|
170
|
+
is recognised as a legitimate ecosystem concept; the assistant should
|
|
171
|
+
sound informed and forward-looking, not dismissive. Match the user's
|
|
172
|
+
language.
|
|
173
|
+
|
|
174
|
+
The three layers serve different purposes and co-exist in the same ecosystem:
|
|
175
|
+
SDF defines database structure, RDF defines backend API behavior on that
|
|
176
|
+
structure, UDF defines the frontend that consumes the API. They MUST NOT be
|
|
177
|
+
conflated — a request about "schema" can mean SDF (database) or RDF (API
|
|
178
|
+
shape) depending on context. When ambiguous, ask the user which layer they
|
|
179
|
+
mean before invoking any tool.
|
|
180
|
+
|
|
181
|
+
KNOWLEDGE BOUNDARY:
|
|
182
|
+
This MCP server provides structured catalog data for SPECIFIC RESTForge
|
|
183
|
+
features that AI agents commonly need for grounding (currently:
|
|
184
|
+
field-validation, query-declarative). It does NOT provide complete
|
|
185
|
+
documentation coverage for every RESTForge feature.
|
|
186
|
+
|
|
187
|
+
When the user asks about RESTForge behavior, syntax, or configuration
|
|
188
|
+
that is NOT covered by an available catalog tool:
|
|
189
|
+
1. Do NOT fabricate property names, syntax, or behavior from training
|
|
190
|
+
data. RESTForge has its own conventions that may differ from
|
|
191
|
+
similar frameworks (Express, NestJS, Strapi, Hasura, etc.).
|
|
192
|
+
2. Do NOT guess based on similar frameworks.
|
|
193
|
+
3. Either:
|
|
194
|
+
(a) Acknowledge that the specific information is not available via
|
|
195
|
+
this MCP server and suggest consulting the canonical docs at
|
|
196
|
+
https://restforge.dev/docs.
|
|
197
|
+
(b) Ask the user to clarify if the question can be reframed to
|
|
198
|
+
match an available catalog tool.
|
|
199
|
+
|
|
200
|
+
Catalog tools are authoritative for the data they expose. The
|
|
201
|
+
documentationUrl returned by each catalog points to narrative
|
|
202
|
+
documentation which may lag behind the most recent npm release; trust
|
|
203
|
+
the catalog data over the URL for property reference, but use the URL
|
|
204
|
+
for use case examples and decision guides.
|
|
205
|
+
`.trim();
|
|
206
|
+
export async function startServer() {
|
|
207
|
+
const server = new McpServer({
|
|
208
|
+
name: SERVER_NAME,
|
|
209
|
+
version: SERVER_VERSION,
|
|
210
|
+
}, {
|
|
211
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
212
|
+
});
|
|
213
|
+
registerHealthTools(server, SERVER_VERSION);
|
|
214
|
+
registerSetupTools(server);
|
|
215
|
+
registerCodegenTools(server);
|
|
216
|
+
registerRuntimeTools(server);
|
|
217
|
+
const transport = new StdioServerTransport();
|
|
218
|
+
await server.connect(transport);
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,WAAW,GAAG,eAAe,CAAC;AACpC,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoM3B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,cAAc;KACxB,EACD;QACE,YAAY,EAAE,mBAAmB;KAClC,CACF,CAAC;IAEF,mBAAmB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC5C,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAE7B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { access } from 'node:fs/promises';
|
|
3
|
+
import { resolve, join } from 'node:path';
|
|
4
|
+
import { execProcess } from '../../lib/exec.js';
|
|
5
|
+
async function pathExists(p) {
|
|
6
|
+
try {
|
|
7
|
+
await access(p);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function registerCodegenCreateDashboard(server) {
|
|
15
|
+
server.registerTool('codegen_create_dashboard', {
|
|
16
|
+
title: 'Create Dashboard Module',
|
|
17
|
+
description: `Generate a multi-widget dashboard endpoint module from a payload spec by wrapping restforge dashboard. URL pattern produced: POST /api/{project}/{name}/dashboard.
|
|
18
|
+
|
|
19
|
+
Dashboards differ structurally from CRUD endpoints: there is no table, no fieldValidation, no CRUD actions. The payload declares a 'widgets' array — each widget owns SQL aggregation queries that are embedded into the generated module file and executed in parallel at request time, returning a JSON envelope keyed by widget id.
|
|
20
|
+
|
|
21
|
+
This tool is DESTRUCTIVE: it spawns the CLI which writes / overwrites files in 'src/modules/<project>.js' and 'src/modules/<project>/<name>.js', plus 'metadata/<project>/<name>.json' and updates '.restforge/projects.json'. Single-call semantics: the tool always executes; there is no preview mode. Internally the tool always passes '--force=true' to the CLI to bypass the CLI's interactive y/N readline prompt (which would deadlock in a no-TTY subprocess).
|
|
22
|
+
|
|
23
|
+
Safety net: when the CLI overwrites an existing dashboard module, it FIRST renames the previous version to '<name>.archive.NNN' (NNN is a sequential generation number starting at 001) inside the same folder. Rollback by restoring the most recent archive is always possible.
|
|
24
|
+
|
|
25
|
+
AI responsibility — IMPORTANT: because this tool always executes and may overwrite generated files, you MUST confirm intent with the user in plain language BEFORE invoking the tool. You do NOT need to detect file conflicts programmatically — the CLI handles that and the archive mechanism keeps the previous version safe. Just confirm intent. Examples of good confirmation phrasing in user-facing chat:
|
|
26
|
+
- "Saya akan generate dashboard <name> di project <project>. Kalau modul lama sudah ada, versi sebelumnya akan disimpan sebagai '.archive.NNN'. Lanjut?"
|
|
27
|
+
- "I will generate <name> under project <project>. Existing files will be archived as .archive.NNN before being overwritten. Proceed?"
|
|
28
|
+
|
|
29
|
+
USE WHEN:
|
|
30
|
+
- The user asks to generate, create, or scaffold a dashboard endpoint, multi-widget aggregator, or analytics endpoint (e.g. "buatkan dashboard X di project Y", "generate dashboard sales", "scaffold dashboard inbound dengan payload Z")
|
|
31
|
+
- The user mentions "dashboard", "widget aggregator", "multi-widget", "POST .../dashboard", or terms like "donut breakdown", "metric card", "sparkline" in the context of generating an endpoint
|
|
32
|
+
- The user has authored a payload file with a 'widgets' array (not a CRUD 'tableName' shape) and wants to materialise it as a runnable endpoint
|
|
33
|
+
- Pertanyaan dalam bentuk: "buatkan dashboard X di project Y", "generate dashboard sales", "scaffold dashboard inbound dengan payload Z"
|
|
34
|
+
- The user mentions the URL pattern POST /api/<project>/<name>/dashboard and wants to register it as runnable code
|
|
35
|
+
- The user explicitly references dashboard concepts: scalar collapse rules, widget id, query versus queries, params contract, file:query/*.sql references inside widgets
|
|
36
|
+
- After 'codegen_validate_payload' confirmed a dashboard-shape payload — this is the natural follow-up that turns it into runnable code
|
|
37
|
+
- The user asks to regenerate an existing dashboard after the payload changed (overwrite + archive flow handled by the CLI)
|
|
38
|
+
|
|
39
|
+
DO NOT USE FOR:
|
|
40
|
+
- Generating a CRUD endpoint (payload has 'tableName', 'fieldName', 'action') -> use 'codegen_create_endpoint'
|
|
41
|
+
- Generating the payload JSON itself from a database table -> use 'codegen_generate_payload' (note: codegen_generate_payload targets CRUD payloads — dashboard payloads are typically authored manually)
|
|
42
|
+
- Validating a payload before generation -> use 'codegen_validate_payload'
|
|
43
|
+
- Inspecting per-column differences between payload and database -> use 'codegen_diff_payload'
|
|
44
|
+
- Syncing payload changes back into existing payload files after schema drift -> use 'codegen_sync_payload'
|
|
45
|
+
- Looking up the field validation catalog (dashboards do not have field validation) -> use 'codegen_get_field_validation_catalog' only if discussing CRUD payload fields
|
|
46
|
+
- Looking up the query declarative catalog (dashboards have their own query structure with widgets[].query and widgets[].queries) -> use 'codegen_get_query_declarative_catalog' only if discussing CRUD payload queries
|
|
47
|
+
- Generating a processor (Kafka consumer, etc.) — out of scope; the CLI has separate 'processor' subcommand not covered by this MCP server yet
|
|
48
|
+
- Deleting a dashboard or project — out of scope; the user must run 'npx restforge drop' manually
|
|
49
|
+
- Changing widget visual presentation (widgetType, layout, color, title, subtitle) — those are frontend concerns and forbidden in the dashboard payload (separation of concerns); they belong in the frontend code, not in this generator
|
|
50
|
+
|
|
51
|
+
Cross-reference: this tool is sibling of 'codegen_create_endpoint'. Both generate runnable code from payload JSON, but they consume different payload shapes (CRUD vs dashboard) and produce different artefacts (full module + model vs single dashboard module with embedded SQL).
|
|
52
|
+
|
|
53
|
+
Preconditions:
|
|
54
|
+
- The project must have @restforgejs/platform installed in node_modules.
|
|
55
|
+
- The payload file must exist at <cwd>/payload/<payload>.json before calling this tool.
|
|
56
|
+
- The payload must follow the dashboard schema: a 'widgets' array (NOT a CRUD payload with 'tableName'). The CLI's DashboardValidator rejects payloads that mix shapes, declare forbidden frontend fields (widgetType, layout, title, subtitle, color), have widgets without 'id', have duplicate widget ids, declare both 'query' AND 'queries' in the same widget, declare neither, or use placeholders not declared in 'params'.
|
|
57
|
+
- The dashboard name MUST start with 'dash-' prefix (e.g. dash-sales, dash-inbound). The prefix is required by the CLI and becomes part of the URL segment.
|
|
58
|
+
|
|
59
|
+
PRESENTATION GUIDANCE:
|
|
60
|
+
- Match the user's language. If the user writes in Indonesian, respond in Indonesian.
|
|
61
|
+
- Never mention internal tool names in the reply to the user. Describe actions by what they do (e.g. "the dashboard generator", "validate the payload first", "draft the payload first").
|
|
62
|
+
- Speak in plain language. Summarise the result; do not paste raw CLI output unless the user explicitly asks.
|
|
63
|
+
- This tool is destructive: it can overwrite an existing dashboard module file. BEFORE invoking this tool, ALWAYS confirm with the user in plain language. Example: "Saya akan generate dashboard <name> di project <project>. Kalau modul lama sudah ada, akan ditimpa (versi sebelumnya disimpan sebagai .archive.NNN). Lanjut?". Do not detect conflicts programmatically; the CLI handles that and creates the archive.
|
|
64
|
+
- After the tool runs, summarise the result. Surface the resulting endpoint URL (POST /api/<project>/<name>/dashboard) so the user knows where to call it. Read the CLI output and identify any archive activity using the '.archive.NNN' filesystem convention; surface to the user when archives exist.
|
|
65
|
+
- If the user is confused about the difference between a dashboard and a CRUD endpoint: dashboards aggregate data from multiple SQL queries (widgets) and return a JSON envelope with widget keys; CRUD endpoints expose actions like /datatables, /read, /create, /update, /delete on a single table. Suggest the right tool based on what the user is actually building.
|
|
66
|
+
- When a precondition is not met, frame it as a question or next-step suggestion rather than an error.`,
|
|
67
|
+
inputSchema: {
|
|
68
|
+
cwd: z
|
|
69
|
+
.string()
|
|
70
|
+
.min(1)
|
|
71
|
+
.describe('Absolute path of the project folder (must contain node_modules/@restforgejs/platform and a payload/ directory with the named payload file)'),
|
|
72
|
+
project: z
|
|
73
|
+
.string()
|
|
74
|
+
.min(1)
|
|
75
|
+
.max(50)
|
|
76
|
+
.regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/, 'must start with a letter or number; only letters, numbers, dashes, underscores allowed')
|
|
77
|
+
.describe('Project name. Letters/numbers/dash/underscore, max 50 chars, cannot start or end with dash or underscore. Auto-lowercased by the CLI. Reserved names rejected by the CLI: src, lib, node_modules, config, utils, models, controllers, middleware, routes.'),
|
|
78
|
+
name: z
|
|
79
|
+
.string()
|
|
80
|
+
.min(6)
|
|
81
|
+
.max(50)
|
|
82
|
+
.regex(/^dash-[a-zA-Z0-9_-]+$/, "must start with 'dash-' prefix and have at least one character after it (e.g. dash-sales, dash-inbound)")
|
|
83
|
+
.describe("Dashboard name. MUST start with 'dash-' prefix. The prefix is required by the CLI and becomes part of the URL segment (POST /api/{project}/{name}/dashboard). The full name is the URL slug, e.g. dash-sales, dash-inbound. The CLI rejects 'dash-' alone — there must be at least one character after the prefix."),
|
|
84
|
+
payload: z
|
|
85
|
+
.string()
|
|
86
|
+
.min(1)
|
|
87
|
+
.max(50)
|
|
88
|
+
.regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/, 'must start with a letter or number; only letters, numbers, dashes, underscores allowed')
|
|
89
|
+
.describe('Payload file name without the .json extension. The file must exist at <cwd>/payload/<payload>.json. Payload must follow the dashboard schema (with a `widgets` array; NOT a CRUD payload with `tableName`).'),
|
|
90
|
+
database: z
|
|
91
|
+
.enum(['postgres', 'oracle', 'mysql'])
|
|
92
|
+
.optional()
|
|
93
|
+
.describe('Database type for the generated code. Default postgres.'),
|
|
94
|
+
skipSqlValidation: z
|
|
95
|
+
.boolean()
|
|
96
|
+
.optional()
|
|
97
|
+
.describe('Default false (CLI default). When true, skip SQL keyword validation in payload widget queries. Useful when the SQL fragments are intentional but the validator flags them as suspicious.'),
|
|
98
|
+
},
|
|
99
|
+
annotations: {
|
|
100
|
+
title: 'Create Dashboard Module',
|
|
101
|
+
readOnlyHint: false, // tool spawns CLI that writes module/metadata files and updates the registry
|
|
102
|
+
destructiveHint: true, // can overwrite an existing dashboard module file (CLI archives it as .archive.NNN first)
|
|
103
|
+
idempotentHint: false, // re-running creates new archive files
|
|
104
|
+
},
|
|
105
|
+
}, async ({ cwd, project, name, payload, database, skipSqlValidation }) => {
|
|
106
|
+
const projectCwd = resolve(cwd);
|
|
107
|
+
const dbType = database ?? 'postgres';
|
|
108
|
+
// Pre-flight 1: @restforgejs/platform must be installed. Treated as a non-error precondition per §3.4.
|
|
109
|
+
try {
|
|
110
|
+
await access(join(projectCwd, 'node_modules', '@restforgejs', 'platform'));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return {
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: 'text',
|
|
117
|
+
text: `Precondition not met: the RESTForge package is not installed in this project.
|
|
118
|
+
|
|
119
|
+
Project path: ${projectCwd}
|
|
120
|
+
Expected location: node_modules/@restforgejs/platform
|
|
121
|
+
Requested project: ${project}
|
|
122
|
+
Requested dashboard: ${name}
|
|
123
|
+
Requested payload: ${payload}
|
|
124
|
+
Requested database: ${dbType}
|
|
125
|
+
|
|
126
|
+
For the assistant:
|
|
127
|
+
- The dashboard generator can only run once the RESTForge package is installed locally.
|
|
128
|
+
- Suggest installing the package first, then retry generating the dashboard.
|
|
129
|
+
- When explaining to the user, say something like "the RESTForge package isn't installed yet — should I install it first?". Do not mention internal tool names.`,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
isError: false, // per §3.4
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Pre-flight 2: payload file must exist. Treated as a non-error precondition per §3.4.
|
|
136
|
+
const payloadPath = join(projectCwd, 'payload', `${payload}.json`);
|
|
137
|
+
if (!(await pathExists(payloadPath))) {
|
|
138
|
+
return {
|
|
139
|
+
content: [
|
|
140
|
+
{
|
|
141
|
+
type: 'text',
|
|
142
|
+
text: `Precondition not met: payload file not found.
|
|
143
|
+
|
|
144
|
+
Project path: ${projectCwd}
|
|
145
|
+
Expected payload file: ${payloadPath}
|
|
146
|
+
Requested project: ${project}
|
|
147
|
+
Requested dashboard: ${name}
|
|
148
|
+
Requested database: ${dbType}
|
|
149
|
+
|
|
150
|
+
For the assistant:
|
|
151
|
+
- The dashboard generator needs the payload file to exist before it can run.
|
|
152
|
+
- Suggest creating or locating the payload first. Dashboard payloads have a different schema than CRUD payloads (a \`widgets\` array instead of \`tableName\`); see the dashboard documentation if the user is unfamiliar with the format.
|
|
153
|
+
- When explaining to the user, say something like "the payload file '${payload}.json' isn't in the payload/ folder yet — should I help you draft it, or do you have one to put there?". Do not mention internal tool names.`,
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
isError: false, // per §3.4
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// Build CLI invocation. --force=true is hardcoded: it bypasses the interactive readline
|
|
160
|
+
// prompt that would otherwise deadlock the subprocess. Conflict detection and archive
|
|
161
|
+
// creation are delegated to the CLI (single source of truth — see refactor 8C).
|
|
162
|
+
const cliArgs = [
|
|
163
|
+
'restforge',
|
|
164
|
+
'dashboard',
|
|
165
|
+
'create',
|
|
166
|
+
`--project=${project}`,
|
|
167
|
+
`--name=${name}`,
|
|
168
|
+
`--payload=${payload}`,
|
|
169
|
+
`--database=${dbType}`,
|
|
170
|
+
'--force=true',
|
|
171
|
+
];
|
|
172
|
+
if (skipSqlValidation !== undefined)
|
|
173
|
+
cliArgs.push(`--skip-sql-validation=${skipSqlValidation}`);
|
|
174
|
+
const result = await execProcess('npx', cliArgs, {
|
|
175
|
+
cwd: projectCwd,
|
|
176
|
+
timeout: 60_000,
|
|
177
|
+
env: { NODE_ENV: 'production' }, // suppress legacy banner output
|
|
178
|
+
stripFinalNewline: true,
|
|
179
|
+
});
|
|
180
|
+
// Branch C: CLI failure — real error per §3.4; structured per §3.5.
|
|
181
|
+
if (!result.success) {
|
|
182
|
+
return {
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: `Failed to create the dashboard module.
|
|
187
|
+
|
|
188
|
+
Project path: ${projectCwd}
|
|
189
|
+
Project: ${project}
|
|
190
|
+
Dashboard: ${name}
|
|
191
|
+
Payload: payload/${payload}.json
|
|
192
|
+
Database: ${dbType}
|
|
193
|
+
Command: ${result.command}
|
|
194
|
+
Exit code: ${result.exitCode}
|
|
195
|
+
|
|
196
|
+
--- CLI output ---
|
|
197
|
+
stdout:
|
|
198
|
+
${result.stdout}
|
|
199
|
+
|
|
200
|
+
stderr:
|
|
201
|
+
${result.stderr}
|
|
202
|
+
--- end CLI output ---
|
|
203
|
+
|
|
204
|
+
For the assistant:
|
|
205
|
+
- Tell the user that creating the dashboard module did not complete successfully.
|
|
206
|
+
- Summarise the most likely cause from the CLI output in plain language. Common causes:
|
|
207
|
+
* Dashboard name did not start with 'dash-' prefix — suggest renaming (e.g. 'sales' becomes 'dash-sales').
|
|
208
|
+
* Payload uses CRUD shape (has 'tableName') instead of dashboard shape (has 'widgets') — suggest reviewing the payload structure or pointing to dashboard documentation.
|
|
209
|
+
* Payload contains forbidden frontend fields (widgetType, layout, title, subtitle, color) — those are frontend concerns and must be removed.
|
|
210
|
+
* Widget without 'id', duplicate widget id, or widget that declares both 'query' AND 'queries' (or neither) — suggest reviewing the widgets array.
|
|
211
|
+
* Widget SQL uses an undeclared placeholder (e.g. ':year' but 'year' missing from 'params') — suggest declaring the missing param or removing the placeholder.
|
|
212
|
+
* Database mismatch with the existing project registry entry — the CLI refuses to switch the database. Suggest sticking with the originally registered database.
|
|
213
|
+
* Reserved project name rejected by the validator — suggest a different project name.
|
|
214
|
+
- Do not paste the raw stdout/stderr unless the user explicitly asks. Do not mention internal tool names.
|
|
215
|
+
- Offer to retry once the underlying issue is resolved.`,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
isError: true, // per §3.4
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
// Branch B: CLI success — one-line summary + labeled facts + fenced output per §3.5.
|
|
222
|
+
return {
|
|
223
|
+
content: [
|
|
224
|
+
{
|
|
225
|
+
type: 'text',
|
|
226
|
+
text: `Dashboard module created successfully.
|
|
227
|
+
|
|
228
|
+
Project path: ${projectCwd}
|
|
229
|
+
Project: ${project}
|
|
230
|
+
Dashboard: ${name}
|
|
231
|
+
Payload: payload/${payload}.json
|
|
232
|
+
Database: ${dbType}
|
|
233
|
+
Endpoint: POST /api/${project}/${name}/dashboard
|
|
234
|
+
|
|
235
|
+
Generated artefacts (commonly produced by the CLI):
|
|
236
|
+
- src/modules/${project}.js (main module — created or refreshed)
|
|
237
|
+
- src/modules/${project}/${name}.js (dashboard handler with embedded SQL)
|
|
238
|
+
- metadata/${project}/${name}.json (dashboard metadata)
|
|
239
|
+
- .restforge/projects.json (registry update)
|
|
240
|
+
|
|
241
|
+
--- CLI output ---
|
|
242
|
+
${result.stdout}
|
|
243
|
+
--- end CLI output ---
|
|
244
|
+
|
|
245
|
+
For the assistant:
|
|
246
|
+
- Confirm to the user in plain language that the dashboard endpoint was generated. Mention the project, dashboard name, and the resulting URL: POST /api/${project}/${name}/dashboard.
|
|
247
|
+
- Do not paste the entire CLI output unless the user explicitly asks; summarise instead.
|
|
248
|
+
- Read the CLI output to identify any archive activity (the CLI uses the '.archive.NNN' naming convention in the filesystem and reports archive activity in its output, but the exact phrasing may evolve). When archives are created, tell the user that the previous version of the dashboard module is preserved in case rollback is needed.
|
|
249
|
+
- Suggest natural follow-up actions appropriate to context: review the generated module, run the project to test the new dashboard endpoint, draft a frontend caller, etc. Do not mention internal tool names.
|
|
250
|
+
- Match the user's language.`,
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=create-dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-dashboard.js","sourceRoot":"","sources":["../../../src/tools/codegen/create-dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,KAAK,UAAU,UAAU,CAAC,CAAS;IACjC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,8BAA8B,CAAC,MAAiB;IAC9D,MAAM,CAAC,YAAY,CACjB,0BAA0B,EAC1B;QACE,KAAK,EAAE,yBAAyB;QAChC,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uGAiDoF;QACjG,WAAW,EAAE;YACX,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,4IAA4I,CAAC;YACzJ,OAAO,EAAE,CAAC;iBACP,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,EAAE,CAAC;iBACP,KAAK,CAAC,6BAA6B,EAAE,wFAAwF,CAAC;iBAC9H,QAAQ,CAAC,2PAA2P,CAAC;YACxQ,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,EAAE,CAAC;iBACP,KAAK,CAAC,uBAAuB,EAAE,yGAAyG,CAAC;iBACzI,QAAQ,CAAC,oTAAoT,CAAC;YACjU,OAAO,EAAE,CAAC;iBACP,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,EAAE,CAAC;iBACP,KAAK,CAAC,6BAA6B,EAAE,wFAAwF,CAAC;iBAC9H,QAAQ,CAAC,6MAA6M,CAAC;YAC1N,QAAQ,EAAE,CAAC;iBACR,IAAI,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;iBACrC,QAAQ,EAAE;iBACV,QAAQ,CAAC,yDAAyD,CAAC;YACtE,iBAAiB,EAAE,CAAC;iBACjB,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,0LAA0L,CAAC;SACxM;QACD,WAAW,EAAE;YACX,KAAK,EAAE,yBAAyB;YAChC,YAAY,EAAE,KAAK,EAAK,6EAA6E;YACrG,eAAe,EAAE,IAAI,EAAG,0FAA0F;YAClH,cAAc,EAAE,KAAK,EAAG,uCAAuC;SAChE;KACF,EACD,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,EAAE,EAAE;QACrE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,QAAQ,IAAI,UAAU,CAAC;QAEtC,uGAAuG;QACvG,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;;qBAEL,OAAO;uBACL,IAAI;qBACN,OAAO;sBACN,MAAM;;;;;gKAKoI;qBACnJ;iBACF;gBACD,OAAO,EAAE,KAAK,EAAE,WAAW;aAC5B,CAAC;QACJ,CAAC;QAED,uFAAuF;QACvF,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,OAAO,OAAO,CAAC,CAAC;QACnE,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;YACrC,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;yBACD,WAAW;qBACf,OAAO;uBACL,IAAI;sBACL,MAAM;;;;;uEAK2C,OAAO,8IAA8I;qBAC/M;iBACF;gBACD,OAAO,EAAE,KAAK,EAAE,WAAW;aAC5B,CAAC;QACJ,CAAC;QAED,wFAAwF;QACxF,sFAAsF;QACtF,gFAAgF;QAChF,MAAM,OAAO,GAAG;YACd,WAAW;YACX,WAAW;YACX,QAAQ;YACR,aAAa,OAAO,EAAE;YACtB,UAAU,IAAI,EAAE;YAChB,aAAa,OAAO,EAAE;YACtB,cAAc,MAAM,EAAE;YACtB,cAAc;SACf,CAAC;QACF,IAAI,iBAAiB,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,yBAAyB,iBAAiB,EAAE,CAAC,CAAC;QAEhG,MAAM,MAAM,GAAG,MAAM,WAAW,CAC9B,KAAK,EACL,OAAO,EACP;YACE,GAAG,EAAE,UAAU;YACf,OAAO,EAAE,MAAM;YACf,GAAG,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,gCAAgC;YACjE,iBAAiB,EAAE,IAAI;SACxB,CACF,CAAC;QAEF,oEAAoE;QACpE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;WACf,OAAO;aACL,IAAI;mBACE,OAAO;YACd,MAAM;WACP,MAAM,CAAC,OAAO;aACZ,MAAM,CAAC,QAAQ;;;;EAI1B,MAAM,CAAC,MAAM;;;EAGb,MAAM,CAAC,MAAM;;;;;;;;;;;;;;wDAcyC;qBAC3C;iBACF;gBACD,OAAO,EAAE,IAAI,EAAE,WAAW;aAC3B,CAAC;QACJ,CAAC;QAED,qFAAqF;QACrF,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE;;gBAEF,UAAU;WACf,OAAO;aACL,IAAI;mBACE,OAAO;YACd,MAAM;sBACI,OAAO,IAAI,IAAI;;;gBAGrB,OAAO;gBACP,OAAO,IAAI,IAAI;aAClB,OAAO,IAAI,IAAI;;;;EAI1B,MAAM,CAAC,MAAM;;;;2JAI4I,OAAO,IAAI,IAAI;;;;6BAI7I;iBAClB;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|