@toolplex/app-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +98 -0
- package/README.md +193 -0
- package/dist/auth.d.ts +3 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +17 -0
- package/dist/auth.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/parsing.d.ts +4 -0
- package/dist/parsing.d.ts.map +1 -0
- package/dist/parsing.js +76 -0
- package/dist/parsing.js.map +1 -0
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +33 -0
- package/dist/plugin.js.map +1 -0
- package/dist/routes/actions.d.ts +4 -0
- package/dist/routes/actions.d.ts.map +1 -0
- package/dist/routes/actions.js +19 -0
- package/dist/routes/actions.js.map +1 -0
- package/dist/routes/context.d.ts +4 -0
- package/dist/routes/context.d.ts.map +1 -0
- package/dist/routes/context.js +70 -0
- package/dist/routes/context.js.map +1 -0
- package/dist/routes/data.d.ts +4 -0
- package/dist/routes/data.d.ts.map +1 -0
- package/dist/routes/data.js +23 -0
- package/dist/routes/data.js.map +1 -0
- package/dist/routes/pages.d.ts +8 -0
- package/dist/routes/pages.d.ts.map +1 -0
- package/dist/routes/pages.js +24 -0
- package/dist/routes/pages.js.map +1 -0
- package/dist/types.d.ts +216 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +5 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +77 -0
- package/dist/validation.js.map +1 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
License text copyright (c) 2025 ToolPlex LLC. All Rights Reserved.
|
|
2
|
+
"Business Source License" is a trademark of MariaDB Corporation Ab.
|
|
3
|
+
|
|
4
|
+
Parameters
|
|
5
|
+
|
|
6
|
+
Licensor: ToolPlex LLC
|
|
7
|
+
Licensed Work: ToolPlex App Server, as published in this repository. The Licensed Work is (c) 2025
|
|
8
|
+
ToolPlex LLC.
|
|
9
|
+
Additional Use Grant: You may make production use of the Licensed Work, provided
|
|
10
|
+
your use does not involve offering it or derivative works on a
|
|
11
|
+
hosted or embedded basis to compete with ToolPlex's paid platform
|
|
12
|
+
or services. For purposes of this license:
|
|
13
|
+
|
|
14
|
+
A "competitive offering" is a product or service that is offered
|
|
15
|
+
to third parties on a paid basis (including through paid support),
|
|
16
|
+
and that significantly overlaps with ToolPlex's platform, including:
|
|
17
|
+
tool discovery, tool execution, agent control, or workflow/playbook
|
|
18
|
+
reuse.
|
|
19
|
+
|
|
20
|
+
"Product" means software made available to end users or used to
|
|
21
|
+
operate hosted services.
|
|
22
|
+
|
|
23
|
+
"Embedded" includes incorporating source or binary components of
|
|
24
|
+
the Licensed Work into another product or requiring the Licensed
|
|
25
|
+
Work for that product to function.
|
|
26
|
+
|
|
27
|
+
"Internal use" within your organization is permitted, even for
|
|
28
|
+
commercial purposes, as long as it is not redistributed or offered
|
|
29
|
+
to others.
|
|
30
|
+
|
|
31
|
+
"API Integration Restrictions": Use of the ToolPlex API or services
|
|
32
|
+
to build, enhance, or operate a competitive offering is prohibited,
|
|
33
|
+
regardless of whether the Licensed Work is directly incorporated.
|
|
34
|
+
|
|
35
|
+
"Derivative Works": Includes any modification, adaptation, or custom
|
|
36
|
+
implementation based on the Licensed Work, including but not limited
|
|
37
|
+
to custom clients for ToolPlex API interaction. While permitted under
|
|
38
|
+
this license, such derivatives may present technical challenges and
|
|
39
|
+
are not officially supported.
|
|
40
|
+
|
|
41
|
+
"Research and Academic Use": Non-commercial research, educational,
|
|
42
|
+
and academic use is permitted regardless of competitive overlap,
|
|
43
|
+
provided results are not used to develop commercial competitive
|
|
44
|
+
offerings.
|
|
45
|
+
|
|
46
|
+
"Official Client Recommendation": While custom clients are permitted
|
|
47
|
+
under this license, ToolPlex strongly recommends using the official
|
|
48
|
+
client to ensure compatibility, security, and optimal performance.
|
|
49
|
+
|
|
50
|
+
Hosting or using the Licensed Work for internal purposes within an
|
|
51
|
+
organization is not considered a competitive offering. ToolPlex
|
|
52
|
+
considers your organization to include all of your affiliates under
|
|
53
|
+
common control.
|
|
54
|
+
|
|
55
|
+
Change Date: None
|
|
56
|
+
Change License: MPL 2.0
|
|
57
|
+
|
|
58
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
59
|
+
please contact support@toolplex.ai.
|
|
60
|
+
|
|
61
|
+
Notice
|
|
62
|
+
|
|
63
|
+
Business Source License 1.1
|
|
64
|
+
|
|
65
|
+
Terms
|
|
66
|
+
|
|
67
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
68
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
69
|
+
Licensor may make an Additional Use Grant, above, permitting limited production use.
|
|
70
|
+
|
|
71
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
72
|
+
currently in effect as described in this License, you must purchase a
|
|
73
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
74
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
75
|
+
|
|
76
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
77
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
78
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
79
|
+
for each version of the Licensed Work released by Licensor.
|
|
80
|
+
|
|
81
|
+
You must conspicuously display this License on each original or modified copy
|
|
82
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
83
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
84
|
+
License apply to your use of that work.
|
|
85
|
+
|
|
86
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
87
|
+
terminate your rights under this License for the current and all other
|
|
88
|
+
versions of the Licensed Work.
|
|
89
|
+
|
|
90
|
+
This License does not grant you any right in any trademark or logo of
|
|
91
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
92
|
+
Licensor as expressly required by this License).
|
|
93
|
+
|
|
94
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
95
|
+
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
96
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
97
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
98
|
+
TITLE.
|
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# @toolplex/app-server
|
|
2
|
+
|
|
3
|
+
Fastify plugin for serving [ToolPlex](https://toolplex.ai) App Pages. Define page layouts, data handlers, and actions — the plugin generates the HTTP endpoints that power interactive pages in the ToolPlex desktop and mobile apps.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @toolplex/app-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Fastify 5+.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import Fastify from 'fastify';
|
|
17
|
+
import { registerAppPages } from '@toolplex/app-server';
|
|
18
|
+
|
|
19
|
+
const server = Fastify();
|
|
20
|
+
|
|
21
|
+
await server.register(registerAppPages, {
|
|
22
|
+
authToken: process.env.TOOLPLEX_APP_TOKEN,
|
|
23
|
+
|
|
24
|
+
pages: {
|
|
25
|
+
'production-report': {
|
|
26
|
+
title: 'Production Report',
|
|
27
|
+
filters: [
|
|
28
|
+
{ key: 'month', type: 'dropdown', options: ['2026-01', '2026-02', '2026-03'] },
|
|
29
|
+
{ key: 'department', type: 'dropdown', options: ['socks', 'accessories'] },
|
|
30
|
+
],
|
|
31
|
+
sections: [
|
|
32
|
+
{ type: 'card-row', source: 'kpis' },
|
|
33
|
+
{ type: 'table', source: 'production', rowKey: 'id', columns: [
|
|
34
|
+
{ key: 'department', label: 'Department' },
|
|
35
|
+
{ key: 'units', label: 'Units', format: 'integer' },
|
|
36
|
+
{ key: 'rate', label: 'Fulfillment', format: 'percent' },
|
|
37
|
+
{ key: 'status', label: 'Status', format: { type: 'status', colors: { on_track: 'green', behind: 'yellow' } } },
|
|
38
|
+
]},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
resources: {
|
|
44
|
+
production: {
|
|
45
|
+
fetch: async ({ page, pageSize, sort, filters }) => {
|
|
46
|
+
const rows = await db.query('SELECT * FROM production WHERE ...', filters);
|
|
47
|
+
const total = await db.count('production');
|
|
48
|
+
return { rows, total };
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
kpis: {
|
|
52
|
+
fetch: async () => ({
|
|
53
|
+
rows: [
|
|
54
|
+
{ label: 'Fulfillment', value: 0.87, format: 'percent' },
|
|
55
|
+
{ label: 'Units', value: 12400, format: 'integer' },
|
|
56
|
+
],
|
|
57
|
+
total: 2,
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
actions: {},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await server.listen({ port: 3100 });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Generated Endpoints
|
|
69
|
+
|
|
70
|
+
| Route | Method | Description |
|
|
71
|
+
|-------|--------|-------------|
|
|
72
|
+
| `/pages` | GET | List all page definitions |
|
|
73
|
+
| `/pages/:pageId` | GET | Single page definition |
|
|
74
|
+
| `/data/:resource` | GET | Paginated data (query params: `page`, `pageSize`, `sort`, filters) |
|
|
75
|
+
| `/actions/:action` | POST | Execute an action (`{ ids, params, filters }`) |
|
|
76
|
+
| `/context/:resource` | GET | Agent context for a resource |
|
|
77
|
+
| `/context/page/:pageId` | GET | Agent context for an entire page |
|
|
78
|
+
|
|
79
|
+
All routes require `Authorization: Bearer <token>` matching the configured `authToken`.
|
|
80
|
+
|
|
81
|
+
## Page Definition
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
{
|
|
85
|
+
title: string;
|
|
86
|
+
filters?: Filter[]; // Dropdown, text, or date filters
|
|
87
|
+
actions?: Action[]; // Toolbar or inline row actions
|
|
88
|
+
suggestions?: string[]; // Ghost suggestions for the agent sidebar
|
|
89
|
+
sections: (Section | Section[])[]; // Layout — single = full width, array = side-by-side grid
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Sections
|
|
94
|
+
|
|
95
|
+
Sections render top-to-bottom. Wrap sections in an array for side-by-side layout using a 12-column grid:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
sections: [
|
|
99
|
+
{ type: 'card-row', source: 'kpis' }, // Full width
|
|
100
|
+
[ // Side by side
|
|
101
|
+
{ type: 'table', source: 'data', rowKey: 'id', span: 8, columns: [...] },
|
|
102
|
+
{ type: 'card-column', source: 'detail', span: 4 },
|
|
103
|
+
],
|
|
104
|
+
]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Section types:**
|
|
108
|
+
- `card-row` — Horizontal row of metric cards
|
|
109
|
+
- `card-column` — Vertical stack of cards (useful as a sidebar)
|
|
110
|
+
- `table` — Paginated, sortable data grid with row selection
|
|
111
|
+
|
|
112
|
+
Tables support a `detail` field for a slide-out drawer:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
{ type: 'table', source: 'orders', rowKey: 'id', columns: [...],
|
|
116
|
+
detail: { source: 'order_detail' } }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Column Formatting
|
|
120
|
+
|
|
121
|
+
Simple formats as strings, rich formats as objects:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
{ key: 'amount', label: 'Amount', format: 'currency' } // $1,234.00
|
|
125
|
+
{ key: 'rate', label: 'Rate', format: 'percent' } // 87.5%
|
|
126
|
+
{ key: 'active', label: 'Active', format: 'boolean' } // ✓ / ✗
|
|
127
|
+
{ key: 'status', label: 'Status', format: { type: 'status', // Colored badge
|
|
128
|
+
colors: { active: 'green', pending: 'yellow' } } }
|
|
129
|
+
{ key: 'change', label: 'YoY', format: { type: 'delta', // +12.3% / -5.1%
|
|
130
|
+
format: 'percent' } }
|
|
131
|
+
{ key: 'done', label: 'Done', format: { type: 'progress' } } // Progress bar
|
|
132
|
+
{ key: 'url', label: 'Link', format: { type: 'link' } } // Clickable URL
|
|
133
|
+
{ key: 'photo', label: '', format: { type: 'image', width: 32 } } // Thumbnail
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Actions
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
actions: [
|
|
140
|
+
// Inline — button on each row
|
|
141
|
+
{ label: 'Approve', action: 'approve', placement: 'inline' },
|
|
142
|
+
|
|
143
|
+
// Toolbar — operates on checkbox-selected rows
|
|
144
|
+
{ label: 'Export', action: 'export_csv', placement: 'toolbar', selection_required: true },
|
|
145
|
+
|
|
146
|
+
// Global — no row selection needed
|
|
147
|
+
{ label: 'Refresh', action: 'refresh_data', placement: 'toolbar' },
|
|
148
|
+
]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Detail Drawer
|
|
152
|
+
|
|
153
|
+
When a table has `detail: { source: 'order_detail' }`, clicking a row opens a slide-out panel. The detail resource returns typed blocks:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
order_detail: {
|
|
157
|
+
fetch: async ({ selection }) => ({
|
|
158
|
+
rows: [
|
|
159
|
+
{ type: 'header', value: 'Order #1234' },
|
|
160
|
+
{ type: 'field', label: 'Customer', value: 'Acme Corp' },
|
|
161
|
+
{ type: 'field', label: 'Total', value: 1250, format: 'currency' },
|
|
162
|
+
{ type: 'list', label: 'Notes', items: [{ label: 'Rush delivery requested' }] },
|
|
163
|
+
{ type: 'table', label: 'Line Items', columns: [...], rows: [...] },
|
|
164
|
+
{ type: 'image', label: 'Receipt', url: 'https://...' },
|
|
165
|
+
],
|
|
166
|
+
total: 1,
|
|
167
|
+
}),
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Handler Contracts
|
|
172
|
+
|
|
173
|
+
**Fetch** — receives `{ page, pageSize, sort?, filters?, selection? }`, returns `{ rows, total }`. The plugin wraps the response with pagination metadata.
|
|
174
|
+
|
|
175
|
+
**Action** — receives `{ ids, params, filters }`, returns `{ affected, message?, data? }`. `ids` can be empty for global actions.
|
|
176
|
+
|
|
177
|
+
**Context** — receives `{ filters?, selection? }`, returns `{ summary, selection?, suggestions? }`. Used by the ToolPlex agent to understand what's on screen.
|
|
178
|
+
|
|
179
|
+
## Validation
|
|
180
|
+
|
|
181
|
+
The plugin validates configuration at startup. Misconfigurations throw immediately with descriptive errors:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
@toolplex/app-server configuration errors:
|
|
185
|
+
- Page "orders": table section with source "orders" is missing required "rowKey"
|
|
186
|
+
- Page "orders": references action "approve" but no action handler is defined
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Fetch and action handler responses are validated at runtime — missing `rows`, wrong `total` type, etc.
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
See [LICENSE](./LICENSE) for details.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5D,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,IAEhD,SAAS,cAAc,EACvB,OAAO,YAAY,KAClB,OAAO,CAAC,IAAI,CAAC,CAiBjB"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function createAuthHook(expectedToken) {
|
|
2
|
+
return async function verifyBearerToken(request, reply) {
|
|
3
|
+
const header = request.headers.authorization;
|
|
4
|
+
if (!header?.startsWith("Bearer ")) {
|
|
5
|
+
reply
|
|
6
|
+
.code(401)
|
|
7
|
+
.send({ error: "Missing or malformed Authorization header" });
|
|
8
|
+
return reply;
|
|
9
|
+
}
|
|
10
|
+
const token = header.slice(7);
|
|
11
|
+
if (token !== expectedToken) {
|
|
12
|
+
reply.code(401).send({ error: "Invalid token" });
|
|
13
|
+
return reply;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAAC,aAAqB;IAClD,OAAO,KAAK,UAAU,iBAAiB,CACrC,OAAuB,EACvB,KAAmB;QAEnB,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAE7C,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACnC,KAAK;iBACF,IAAI,CAAC,GAAG,CAAC;iBACT,IAAI,CAAC,EAAE,KAAK,EAAE,2CAA2C,EAAE,CAAC,CAAC;YAChE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAE9B,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;YACjD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { registerAppPages } from "./plugin.js";
|
|
2
|
+
export type { AppServerConfig, ResourceDefinition, PageDefinition, Section, CardRowSection, CardColumnSection, TableSection, Column, Filter, Action, ColumnFormat, SimpleFormat, RichFormat, StatusFormat, DeltaFormat, LinkFormat, ImageFormat, ProgressFormat, FetchRequest, FetchResponse, ActionRequest, ActionResponse, ContextRequest, ContextResponse, PageContextRequest, PaginatedResponse, CardData, DetailBlock, DetailHeader, DetailField, DetailList, DetailTable, DetailImage, FetchHandler, ActionHandler, ContextHandler, PageContextHandler, SortSpec, Selection, } from "./types.js";
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,YAAY,EAEV,eAAe,EACf,kBAAkB,EAGlB,cAAc,EACd,OAAO,EACP,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,MAAM,EACN,MAAM,EACN,MAAM,EAGN,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,EAGd,YAAY,EACZ,aAAa,EACb,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAGjB,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EAGX,YAAY,EACZ,aAAa,EACb,cAAc,EACd,kBAAkB,EAGlB,QAAQ,EACR,SAAS,GACV,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FetchRequest, ContextRequest } from "./types.js";
|
|
2
|
+
export declare function parseFetchParams(query: Record<string, string | undefined>): FetchRequest;
|
|
3
|
+
export declare function parseContextParams(query: Record<string, string | undefined>): ContextRequest;
|
|
4
|
+
//# sourceMappingURL=parsing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parsing.d.ts","sourceRoot":"","sources":["../src/parsing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAuB,MAAM,YAAY,CAAC;AAUpF,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACxC,YAAY,CAQd;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACxC,cAAc,CAKhB"}
|
package/dist/parsing.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const DEFAULT_PAGE = 1;
|
|
2
|
+
const DEFAULT_PAGE_SIZE = 50;
|
|
3
|
+
const MAX_PAGE_SIZE = 500;
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Parse query params into typed request objects
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
export function parseFetchParams(query) {
|
|
8
|
+
const page = clampInt(query.page, DEFAULT_PAGE, 1);
|
|
9
|
+
const pageSize = clampInt(query.pageSize, DEFAULT_PAGE_SIZE, 1, MAX_PAGE_SIZE);
|
|
10
|
+
const sort = parseSort(query.sort);
|
|
11
|
+
const filters = parseFilters(query);
|
|
12
|
+
const selection = parseSelection(query.selection);
|
|
13
|
+
return { page, pageSize, sort, filters, selection };
|
|
14
|
+
}
|
|
15
|
+
export function parseContextParams(query) {
|
|
16
|
+
return {
|
|
17
|
+
filters: parseFilters(query),
|
|
18
|
+
selection: parseSelection(query.selection),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Helpers
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
function clampInt(raw, fallback, min, max) {
|
|
25
|
+
if (!raw)
|
|
26
|
+
return fallback;
|
|
27
|
+
const n = parseInt(raw, 10);
|
|
28
|
+
if (isNaN(n))
|
|
29
|
+
return fallback;
|
|
30
|
+
if (n < min)
|
|
31
|
+
return min;
|
|
32
|
+
if (max !== undefined && n > max)
|
|
33
|
+
return max;
|
|
34
|
+
return n;
|
|
35
|
+
}
|
|
36
|
+
function parseSort(raw) {
|
|
37
|
+
if (!raw)
|
|
38
|
+
return undefined;
|
|
39
|
+
const [key, dir] = raw.split(",");
|
|
40
|
+
if (!key)
|
|
41
|
+
return undefined;
|
|
42
|
+
return {
|
|
43
|
+
key,
|
|
44
|
+
direction: dir === "desc" ? "desc" : "asc",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function parseSelection(raw) {
|
|
48
|
+
if (!raw)
|
|
49
|
+
return undefined;
|
|
50
|
+
try {
|
|
51
|
+
const parsed = JSON.parse(raw);
|
|
52
|
+
if (!Array.isArray(parsed.ids))
|
|
53
|
+
return undefined;
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extract filter params from query string.
|
|
62
|
+
* Reserved keys (page, pageSize, sort, selection) are excluded.
|
|
63
|
+
*/
|
|
64
|
+
const RESERVED_KEYS = new Set(["page", "pageSize", "sort", "selection"]);
|
|
65
|
+
function parseFilters(query) {
|
|
66
|
+
const filters = {};
|
|
67
|
+
let hasFilters = false;
|
|
68
|
+
for (const [key, value] of Object.entries(query)) {
|
|
69
|
+
if (RESERVED_KEYS.has(key) || value === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
filters[key] = value;
|
|
72
|
+
hasFilters = true;
|
|
73
|
+
}
|
|
74
|
+
return hasFilters ? filters : undefined;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=parsing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parsing.js","sourceRoot":"","sources":["../src/parsing.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B,8EAA8E;AAC9E,gDAAgD;AAChD,8EAA8E;AAE9E,MAAM,UAAU,gBAAgB,CAC9B,KAAyC;IAEzC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,iBAAiB,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAElD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAAyC;IAEzC,OAAO;QACL,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC;QAC5B,SAAS,EAAE,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,QAAQ,CACf,GAAuB,EACvB,QAAgB,EAChB,GAAW,EACX,GAAY;IAEZ,IAAI,CAAC,GAAG;QAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,CAAC,GAAG,GAAG;QAAE,OAAO,GAAG,CAAC;IACxB,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,GAAG,GAAG;QAAE,OAAO,GAAG,CAAC;IAC7C,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,SAAS,CAAC,GAAuB;IACxC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,OAAO;QACL,GAAG;QACH,SAAS,EAAE,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;KAC3C,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,GAAuB;IAC7C,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;QAC5C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QACjD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAEzE,SAAS,YAAY,CACnB,KAAyC;IAEzC,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAC5D,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACrB,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,OAAO,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1C,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { AppServerConfig } from "./types.js";
|
|
3
|
+
declare function appServerPlugin(fastify: FastifyInstance, config: AppServerConfig): Promise<void>;
|
|
4
|
+
export declare const registerAppPages: typeof appServerPlugin;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAQlD,iBAAe,eAAe,CAC5B,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,eAAO,MAAM,gBAAgB,wBAG3B,CAAC"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fp from "fastify-plugin";
|
|
2
|
+
import { validateConfig } from "./validation.js";
|
|
3
|
+
import { createAuthHook } from "./auth.js";
|
|
4
|
+
import { registerPageRoutes } from "./routes/pages.js";
|
|
5
|
+
import { registerDataRoutes } from "./routes/data.js";
|
|
6
|
+
import { registerActionRoutes } from "./routes/actions.js";
|
|
7
|
+
import { registerContextRoutes } from "./routes/context.js";
|
|
8
|
+
async function appServerPlugin(fastify, config) {
|
|
9
|
+
// Validate everything at startup — fail fast on misconfiguration
|
|
10
|
+
validateConfig(config);
|
|
11
|
+
// Apply bearer token auth to all routes in this plugin scope
|
|
12
|
+
const authHook = createAuthHook(config.authToken);
|
|
13
|
+
fastify.addHook("onRequest", authHook);
|
|
14
|
+
// Register route groups
|
|
15
|
+
registerPageRoutes(fastify, config);
|
|
16
|
+
registerDataRoutes(fastify, config);
|
|
17
|
+
registerActionRoutes(fastify, config);
|
|
18
|
+
registerContextRoutes(fastify, config);
|
|
19
|
+
// Catch handler errors and return structured responses
|
|
20
|
+
fastify.setErrorHandler(async (error, _request, reply) => {
|
|
21
|
+
const status = error.statusCode ?? 500;
|
|
22
|
+
const message = status >= 500
|
|
23
|
+
? "Internal server error in app-server handler"
|
|
24
|
+
: error.message;
|
|
25
|
+
fastify.log.error({ err: error, statusCode: status }, "app-server handler error");
|
|
26
|
+
return reply.code(status).send({ error: message });
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export const registerAppPages = fp(appServerPlugin, {
|
|
30
|
+
name: "@toolplex/app-server",
|
|
31
|
+
fastify: ">=5.0.0",
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAGhC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,KAAK,UAAU,eAAe,CAC5B,OAAwB,EACxB,MAAuB;IAEvB,iEAAiE;IACjE,cAAc,CAAC,MAAM,CAAC,CAAC;IAEvB,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClD,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAEvC,wBAAwB;IACxB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpC,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpC,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAEvC,uDAAuD;IACvD,OAAO,CAAC,eAAe,CAAC,KAAK,EAAE,KAAsC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QACxF,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC;QACvC,MAAM,OAAO,GACX,MAAM,IAAI,GAAG;YACX,CAAC,CAAC,6CAA6C;YAC/C,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;QAEpB,OAAO,CAAC,GAAG,CAAC,KAAK,CACf,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,EAClC,0BAA0B,CAC3B,CAAC;QAEF,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC,eAAe,EAAE;IAClD,IAAI,EAAE,sBAAsB;IAC5B,OAAO,EAAE,SAAS;CACnB,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/routes/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,eAAe,GACtB,IAAI,CAwBN"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { validateActionResponse } from "../validation.js";
|
|
2
|
+
export function registerActionRoutes(fastify, config) {
|
|
3
|
+
fastify.post("/actions/:action", async (request, reply) => {
|
|
4
|
+
const { action } = request.params;
|
|
5
|
+
const handler = config.actions[action];
|
|
6
|
+
if (!handler) {
|
|
7
|
+
return reply.code(404).send({ error: `Action "${action}" not found` });
|
|
8
|
+
}
|
|
9
|
+
const { ids, params, filters } = request.body ?? {};
|
|
10
|
+
const response = await handler({
|
|
11
|
+
ids: Array.isArray(ids) ? ids : [],
|
|
12
|
+
params: params ?? {},
|
|
13
|
+
filters: filters ?? {},
|
|
14
|
+
});
|
|
15
|
+
validateActionResponse(action, response);
|
|
16
|
+
return reply.send(response);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/routes/actions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,UAAU,oBAAoB,CAClC,OAAwB,EACxB,MAAuB;IAEvB,OAAO,CAAC,IAAI,CAGT,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC9C,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,MAAM,aAAa,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QAEpD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC;YAC7B,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;YAClC,MAAM,EAAE,MAAM,IAAI,EAAE;YACpB,OAAO,EAAE,OAAO,IAAI,EAAE;SACvB,CAAC,CAAC;QAEH,sBAAsB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAEzC,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/routes/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAA4B,MAAM,aAAa,CAAC;AAG7E,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,eAAe,GACtB,IAAI,CA6CN"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { parseContextParams } from "../parsing.js";
|
|
2
|
+
export function registerContextRoutes(fastify, config) {
|
|
3
|
+
// Resource-level context
|
|
4
|
+
fastify.get("/context/:resource", async (request, reply) => {
|
|
5
|
+
const { resource } = request.params;
|
|
6
|
+
const definition = config.resources[resource];
|
|
7
|
+
if (!definition?.context) {
|
|
8
|
+
return reply
|
|
9
|
+
.code(404)
|
|
10
|
+
.send({ error: `No context handler for resource "${resource}"` });
|
|
11
|
+
}
|
|
12
|
+
const params = parseContextParams(request.query);
|
|
13
|
+
const response = await definition.context(params);
|
|
14
|
+
return reply.send(response);
|
|
15
|
+
});
|
|
16
|
+
// Page-level context
|
|
17
|
+
fastify.get("/context/page/:pageId", async (request, reply) => {
|
|
18
|
+
const { pageId } = request.params;
|
|
19
|
+
const page = config.pages[pageId];
|
|
20
|
+
if (!page) {
|
|
21
|
+
return reply.code(404).send({ error: `Page "${pageId}" not found` });
|
|
22
|
+
}
|
|
23
|
+
const params = parseContextParams(request.query);
|
|
24
|
+
const sectionSources = uniqueSources(page.sections);
|
|
25
|
+
// If page has a dedicated context handler, use it
|
|
26
|
+
if (page.context) {
|
|
27
|
+
const response = await page.context({ ...params, sections: sectionSources });
|
|
28
|
+
return reply.send(response);
|
|
29
|
+
}
|
|
30
|
+
// Fallback: aggregate resource-level context handlers
|
|
31
|
+
const response = await aggregateContext(config, sectionSources, params);
|
|
32
|
+
return reply.send(response);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Helpers
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
function uniqueSources(sections) {
|
|
39
|
+
const sources = new Set();
|
|
40
|
+
for (const entry of sections) {
|
|
41
|
+
const items = Array.isArray(entry) ? entry : [entry];
|
|
42
|
+
for (const section of items) {
|
|
43
|
+
sources.add(section.source);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return [...sources];
|
|
47
|
+
}
|
|
48
|
+
async function aggregateContext(config, sources, params) {
|
|
49
|
+
const summaries = [];
|
|
50
|
+
let selectionText;
|
|
51
|
+
const suggestions = [];
|
|
52
|
+
for (const source of sources) {
|
|
53
|
+
const handler = config.resources[source]?.context;
|
|
54
|
+
if (!handler)
|
|
55
|
+
continue;
|
|
56
|
+
const result = await handler(params);
|
|
57
|
+
if (result.summary)
|
|
58
|
+
summaries.push(result.summary);
|
|
59
|
+
if (result.selection && !selectionText)
|
|
60
|
+
selectionText = result.selection;
|
|
61
|
+
if (result.suggestions)
|
|
62
|
+
suggestions.push(...result.suggestions);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
summary: summaries.join(" "),
|
|
66
|
+
selection: selectionText,
|
|
67
|
+
suggestions: suggestions.length > 0 ? suggestions : undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/routes/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/routes/data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAC;AAItE,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,eAAe,GACtB,IAAI,CA2BN"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { parseFetchParams } from "../parsing.js";
|
|
2
|
+
import { validateFetchResponse } from "../validation.js";
|
|
3
|
+
export function registerDataRoutes(fastify, config) {
|
|
4
|
+
fastify.get("/data/:resource", async (request, reply) => {
|
|
5
|
+
const { resource } = request.params;
|
|
6
|
+
const definition = config.resources[resource];
|
|
7
|
+
if (!definition) {
|
|
8
|
+
return reply.code(404).send({ error: `Resource "${resource}" not found` });
|
|
9
|
+
}
|
|
10
|
+
const params = parseFetchParams(request.query);
|
|
11
|
+
const response = await definition.fetch(params);
|
|
12
|
+
validateFetchResponse(resource, response);
|
|
13
|
+
const result = {
|
|
14
|
+
rows: response.rows,
|
|
15
|
+
total: response.total,
|
|
16
|
+
page: params.page,
|
|
17
|
+
pageSize: params.pageSize,
|
|
18
|
+
totalPages: Math.ceil(response.total / params.pageSize),
|
|
19
|
+
};
|
|
20
|
+
return reply.send(result);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../../src/routes/data.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,MAAM,UAAU,kBAAkB,CAChC,OAAwB,EACxB,MAAuB;IAEvB,OAAO,CAAC,GAAG,CAGR,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC7C,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACpC,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAE9C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,QAAQ,aAAa,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAEhD,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAE1C,MAAM,MAAM,GAAsB;YAChC,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC;SACxD,CAAC;QAEF,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { AppServerConfig } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Serves page definitions. Context handlers are stripped since they're
|
|
5
|
+
* server-side functions that can't be serialized to JSON.
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerPageRoutes(fastify: FastifyInstance, config: AppServerConfig): void;
|
|
8
|
+
//# sourceMappingURL=pages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/routes/pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnE;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,eAAe,GACtB,IAAI,CAiBN"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serves page definitions. Context handlers are stripped since they're
|
|
3
|
+
* server-side functions that can't be serialized to JSON.
|
|
4
|
+
*/
|
|
5
|
+
export function registerPageRoutes(fastify, config) {
|
|
6
|
+
const serialized = buildSerializablePages(config);
|
|
7
|
+
fastify.get("/pages", async (_request, reply) => {
|
|
8
|
+
return reply.send(serialized);
|
|
9
|
+
});
|
|
10
|
+
fastify.get("/pages/:pageId", async (request, reply) => {
|
|
11
|
+
const page = serialized.find((p) => p.id === request.params.pageId);
|
|
12
|
+
if (!page) {
|
|
13
|
+
return reply.code(404).send({ error: "Page not found" });
|
|
14
|
+
}
|
|
15
|
+
return reply.send(page);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function buildSerializablePages(config) {
|
|
19
|
+
return Object.entries(config.pages).map(([id, page]) => {
|
|
20
|
+
const { context: _context, ...rest } = page;
|
|
21
|
+
return { id, ...rest };
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=pages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/routes/pages.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAwB,EACxB,MAAuB;IAEvB,MAAM,UAAU,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAElD,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC9C,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CACT,gBAAgB,EAChB,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CACF,CAAC;AACJ,CAAC;AAQD,SAAS,sBAAsB,CAC7B,MAAuB;IAEvB,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;QACrD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;QAC5C,OAAO,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
export interface PageDefinition {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
filters?: Filter[];
|
|
5
|
+
actions?: Action[];
|
|
6
|
+
suggestions?: string[];
|
|
7
|
+
sections: (Section | Section[])[];
|
|
8
|
+
context?: PageContextHandler;
|
|
9
|
+
}
|
|
10
|
+
export type Section = CardRowSection | CardColumnSection | TableSection;
|
|
11
|
+
export interface CardRowSection {
|
|
12
|
+
type: "card-row";
|
|
13
|
+
source: string;
|
|
14
|
+
span?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface CardColumnSection {
|
|
17
|
+
type: "card-column";
|
|
18
|
+
source: string;
|
|
19
|
+
span?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface TableSection {
|
|
22
|
+
type: "table";
|
|
23
|
+
source: string;
|
|
24
|
+
rowKey: string;
|
|
25
|
+
columns: Column[];
|
|
26
|
+
span?: number;
|
|
27
|
+
detail?: {
|
|
28
|
+
source: string;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface Column {
|
|
32
|
+
key: string;
|
|
33
|
+
label: string;
|
|
34
|
+
format?: ColumnFormat;
|
|
35
|
+
width?: number;
|
|
36
|
+
}
|
|
37
|
+
export type ColumnFormat = SimpleFormat | RichFormat;
|
|
38
|
+
/** String shorthands — cover 90% of columns */
|
|
39
|
+
export type SimpleFormat = "text" | "integer" | "number" | "percent" | "currency" | "date" | "boolean";
|
|
40
|
+
/** Object format — richer visual treatment */
|
|
41
|
+
export type RichFormat = StatusFormat | DeltaFormat | LinkFormat | ImageFormat | ProgressFormat;
|
|
42
|
+
/** Colored badge — maps cell values to colors */
|
|
43
|
+
export interface StatusFormat {
|
|
44
|
+
type: "status";
|
|
45
|
+
colors?: Record<string, string>;
|
|
46
|
+
}
|
|
47
|
+
/** Signed delta — green positive, red negative */
|
|
48
|
+
export interface DeltaFormat {
|
|
49
|
+
type: "delta";
|
|
50
|
+
format?: "number" | "percent";
|
|
51
|
+
}
|
|
52
|
+
/** Clickable URL */
|
|
53
|
+
export interface LinkFormat {
|
|
54
|
+
type: "link";
|
|
55
|
+
label?: string;
|
|
56
|
+
}
|
|
57
|
+
/** Inline thumbnail */
|
|
58
|
+
export interface ImageFormat {
|
|
59
|
+
type: "image";
|
|
60
|
+
width?: number;
|
|
61
|
+
height?: number;
|
|
62
|
+
}
|
|
63
|
+
/** Progress bar for 0–1 values */
|
|
64
|
+
export interface ProgressFormat {
|
|
65
|
+
type: "progress";
|
|
66
|
+
}
|
|
67
|
+
export interface Filter {
|
|
68
|
+
key: string;
|
|
69
|
+
type: "dropdown" | "text" | "date";
|
|
70
|
+
label?: string;
|
|
71
|
+
options?: string[];
|
|
72
|
+
options_source?: string;
|
|
73
|
+
default?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface Action {
|
|
76
|
+
label: string;
|
|
77
|
+
action: string;
|
|
78
|
+
params?: Record<string, unknown>;
|
|
79
|
+
confirm?: string;
|
|
80
|
+
placement?: "toolbar" | "inline";
|
|
81
|
+
selection_required?: boolean;
|
|
82
|
+
}
|
|
83
|
+
export interface FetchRequest {
|
|
84
|
+
page: number;
|
|
85
|
+
pageSize: number;
|
|
86
|
+
sort?: SortSpec;
|
|
87
|
+
filters?: Record<string, string>;
|
|
88
|
+
selection?: Selection;
|
|
89
|
+
}
|
|
90
|
+
export interface FetchResponse {
|
|
91
|
+
rows: Record<string, unknown>[];
|
|
92
|
+
total: number;
|
|
93
|
+
}
|
|
94
|
+
export interface ActionRequest {
|
|
95
|
+
ids: (string | number)[];
|
|
96
|
+
params: Record<string, unknown>;
|
|
97
|
+
filters: Record<string, string>;
|
|
98
|
+
}
|
|
99
|
+
export interface ActionResponse {
|
|
100
|
+
affected: number;
|
|
101
|
+
message?: string;
|
|
102
|
+
data?: Record<string, unknown>;
|
|
103
|
+
}
|
|
104
|
+
export interface ContextRequest {
|
|
105
|
+
filters?: Record<string, string>;
|
|
106
|
+
selection?: Selection;
|
|
107
|
+
}
|
|
108
|
+
export interface ContextResponse {
|
|
109
|
+
summary: string;
|
|
110
|
+
selection?: string;
|
|
111
|
+
suggestions?: string[];
|
|
112
|
+
}
|
|
113
|
+
export interface PageContextRequest extends ContextRequest {
|
|
114
|
+
sections: string[];
|
|
115
|
+
}
|
|
116
|
+
export type FetchHandler = (req: FetchRequest) => Promise<FetchResponse>;
|
|
117
|
+
export type ActionHandler = (req: ActionRequest) => Promise<ActionResponse>;
|
|
118
|
+
export type ContextHandler = (req: ContextRequest) => Promise<ContextResponse>;
|
|
119
|
+
export type PageContextHandler = (req: PageContextRequest) => Promise<ContextResponse>;
|
|
120
|
+
export interface ResourceDefinition {
|
|
121
|
+
fetch: FetchHandler;
|
|
122
|
+
context?: ContextHandler;
|
|
123
|
+
}
|
|
124
|
+
export interface AppServerConfig {
|
|
125
|
+
authToken: string;
|
|
126
|
+
pages: Record<string, Omit<PageDefinition, "id">>;
|
|
127
|
+
resources: Record<string, ResourceDefinition>;
|
|
128
|
+
actions: Record<string, ActionHandler>;
|
|
129
|
+
}
|
|
130
|
+
export interface SortSpec {
|
|
131
|
+
key: string;
|
|
132
|
+
direction: "asc" | "desc";
|
|
133
|
+
}
|
|
134
|
+
export interface Selection {
|
|
135
|
+
type: "row" | "rows";
|
|
136
|
+
ids?: (string | number)[];
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Card resources use the same FetchHandler as tables, but each row
|
|
140
|
+
* should match this shape. The renderer reads these fields to display
|
|
141
|
+
* metric cards.
|
|
142
|
+
*
|
|
143
|
+
* Example:
|
|
144
|
+
* { label: "Open Issues", value: 45, format: "integer" }
|
|
145
|
+
* { label: "Completion", value: 0.71, format: "percent" }
|
|
146
|
+
* { label: "Revenue", value: 12500, format: "currency" }
|
|
147
|
+
*/
|
|
148
|
+
export interface CardData {
|
|
149
|
+
label: string;
|
|
150
|
+
value: number | string;
|
|
151
|
+
format?: ColumnFormat;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Detail resources use the same FetchHandler, but each row should be a
|
|
155
|
+
* typed block. The drawer renderer uses the `type` field to pick the
|
|
156
|
+
* right component for each block.
|
|
157
|
+
*
|
|
158
|
+
* Example response from a detail fetch handler:
|
|
159
|
+
* rows: [
|
|
160
|
+
* { type: "header", value: "Issue #1705: Unmatched Customer" },
|
|
161
|
+
* { type: "field", label: "Raw Name", value: "ACJDM SALES CORP." },
|
|
162
|
+
* { type: "field", label: "Confidence", value: 0.92, format: "percent" },
|
|
163
|
+
* { type: "list", label: "Candidate Matches", items: [
|
|
164
|
+
* { label: "ACJDM Sales Corporation", value: 0.92, format: "percent", id: "c_441" },
|
|
165
|
+
* { label: "ACJDM Trading", value: 0.78, format: "percent", id: "c_209" },
|
|
166
|
+
* ]},
|
|
167
|
+
* { type: "table", label: "Sample Transactions", columns: [...], rows: [...] },
|
|
168
|
+
* ]
|
|
169
|
+
*/
|
|
170
|
+
export type DetailBlock = DetailHeader | DetailField | DetailList | DetailTable | DetailImage;
|
|
171
|
+
export interface DetailHeader {
|
|
172
|
+
type: "header";
|
|
173
|
+
value: string;
|
|
174
|
+
}
|
|
175
|
+
export interface DetailField {
|
|
176
|
+
type: "field";
|
|
177
|
+
label: string;
|
|
178
|
+
value: string | number | boolean | null;
|
|
179
|
+
format?: ColumnFormat;
|
|
180
|
+
}
|
|
181
|
+
export interface DetailList {
|
|
182
|
+
type: "list";
|
|
183
|
+
label: string;
|
|
184
|
+
items: {
|
|
185
|
+
label: string;
|
|
186
|
+
value?: string | number;
|
|
187
|
+
format?: ColumnFormat;
|
|
188
|
+
id?: string | number;
|
|
189
|
+
}[];
|
|
190
|
+
}
|
|
191
|
+
export interface DetailTable {
|
|
192
|
+
type: "table";
|
|
193
|
+
label: string;
|
|
194
|
+
columns: {
|
|
195
|
+
key: string;
|
|
196
|
+
label: string;
|
|
197
|
+
format?: ColumnFormat;
|
|
198
|
+
}[];
|
|
199
|
+
rows: Record<string, unknown>[];
|
|
200
|
+
}
|
|
201
|
+
export interface DetailImage {
|
|
202
|
+
type: "image";
|
|
203
|
+
label?: string;
|
|
204
|
+
url: string;
|
|
205
|
+
alt?: string;
|
|
206
|
+
width?: number;
|
|
207
|
+
height?: number;
|
|
208
|
+
}
|
|
209
|
+
export interface PaginatedResponse {
|
|
210
|
+
rows: Record<string, unknown>[];
|
|
211
|
+
total: number;
|
|
212
|
+
page: number;
|
|
213
|
+
pageSize: number;
|
|
214
|
+
totalPages: number;
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,EAAE,CAAC;IAClC,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,MAAM,OAAO,GAAG,cAAc,GAAG,iBAAiB,GAAG,YAAY,CAAC;AAExE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,UAAU,CAAC;AAErD,+CAA+C;AAC/C,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,SAAS,GACT,QAAQ,GACR,SAAS,GACT,UAAU,GACV,MAAM,GACN,SAAS,CAAC;AAEd,8CAA8C;AAC9C,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,WAAW,GACX,UAAU,GACV,WAAW,GACX,cAAc,CAAC;AAEnB,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAEjC;AAED,kDAAkD;AAClD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC/B;AAED,oBAAoB;AACpB,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,uBAAuB;AACvB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kCAAkC;AAClC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAKjC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAMD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAMD,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AACzE,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;AAC5E,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;AAC/E,MAAM,MAAM,kBAAkB,GAAG,CAC/B,GAAG,EAAE,kBAAkB,KACpB,OAAO,CAAC,eAAe,CAAC,CAAC;AAM9B,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAMD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC;IAClD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC9C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACxC;AAMD,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CAC3B;AAMD;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,WAAW,GACX,UAAU,GACV,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE,YAAY,CAAC;QACtB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;KACtB,EAAE,CAAC;CACL;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,YAAY,CAAA;KAAE,EAAE,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Page Definition — the JSON schema that describes a page's layout and behavior
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AppServerConfig, FetchResponse, ActionResponse } from "./types.js";
|
|
2
|
+
export declare function validateConfig(config: AppServerConfig): void;
|
|
3
|
+
export declare function validateFetchResponse(resource: string, response: unknown): asserts response is FetchResponse;
|
|
4
|
+
export declare function validateActionResponse(action: string, response: unknown): asserts response is ActionResponse;
|
|
5
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAEf,aAAa,EACb,cAAc,EACf,MAAM,YAAY,CAAC;AAMpB,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAuD5D;AAMD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,QAAQ,IAAI,aAAa,CAuBnC;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,QAAQ,IAAI,cAAc,CAgBpC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Startup validation — fail fast with descriptive errors
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
export function validateConfig(config) {
|
|
5
|
+
const errors = [];
|
|
6
|
+
for (const [pageId, page] of Object.entries(config.pages)) {
|
|
7
|
+
const sections = flattenSections(page.sections);
|
|
8
|
+
for (const section of sections) {
|
|
9
|
+
// Every section source must have a resource handler
|
|
10
|
+
if (!config.resources[section.source]) {
|
|
11
|
+
errors.push(`Page "${pageId}": section references resource "${section.source}" but no fetch handler is defined`);
|
|
12
|
+
}
|
|
13
|
+
// Table sections must have rowKey
|
|
14
|
+
if (section.type === "table" && !section.rowKey) {
|
|
15
|
+
errors.push(`Page "${pageId}": table section with source "${section.source}" is missing required "rowKey"`);
|
|
16
|
+
}
|
|
17
|
+
// Table detail source must reference a resource
|
|
18
|
+
if (section.type === "table" && section.detail) {
|
|
19
|
+
if (!config.resources[section.detail.source]) {
|
|
20
|
+
errors.push(`Page "${pageId}": table detail references resource "${section.detail.source}" but no fetch handler is defined`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Every action must have a handler
|
|
25
|
+
for (const action of page.actions ?? []) {
|
|
26
|
+
if (!config.actions[action.action]) {
|
|
27
|
+
errors.push(`Page "${pageId}": references action "${action.action}" but no action handler is defined`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Filter options_source must reference a resource
|
|
31
|
+
for (const filter of page.filters ?? []) {
|
|
32
|
+
if (filter.options_source && !config.resources[filter.options_source]) {
|
|
33
|
+
errors.push(`Page "${pageId}": filter "${filter.key}" has options_source "${filter.options_source}" but no resource handler is defined`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (errors.length > 0) {
|
|
38
|
+
throw new Error(`@toolplex/app-server configuration errors:\n - ${errors.join("\n - ")}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Runtime response validation
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
export function validateFetchResponse(resource, response) {
|
|
45
|
+
if (!response || typeof response !== "object") {
|
|
46
|
+
throw new ResponseValidationError(resource, "fetch handler must return an object");
|
|
47
|
+
}
|
|
48
|
+
const r = response;
|
|
49
|
+
if (!Array.isArray(r.rows)) {
|
|
50
|
+
throw new ResponseValidationError(resource, 'fetch handler must return "rows" as an array');
|
|
51
|
+
}
|
|
52
|
+
if (typeof r.total !== "number" || r.total < 0) {
|
|
53
|
+
throw new ResponseValidationError(resource, 'fetch handler must return "total" as a non-negative number');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function validateActionResponse(action, response) {
|
|
57
|
+
if (!response || typeof response !== "object") {
|
|
58
|
+
throw new ResponseValidationError(action, "action handler must return an object");
|
|
59
|
+
}
|
|
60
|
+
const r = response;
|
|
61
|
+
if (typeof r.affected !== "number") {
|
|
62
|
+
throw new ResponseValidationError(action, 'action handler must return "affected" as a number');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Helpers
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
function flattenSections(sections) {
|
|
69
|
+
return sections.flatMap((s) => (Array.isArray(s) ? s : [s]));
|
|
70
|
+
}
|
|
71
|
+
class ResponseValidationError extends Error {
|
|
72
|
+
constructor(name, detail) {
|
|
73
|
+
super(`@toolplex/app-server [${name}]: ${detail}`);
|
|
74
|
+
this.name = "ResponseValidationError";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toolplex/app-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Fastify plugin for serving ToolPlex App Pages — page definitions, paginated data, actions, and agent context",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": ["dist"],
|
|
9
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/toolplex/app-server"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/toolplex/app-server#readme",
|
|
15
|
+
"keywords": ["toolplex", "fastify", "fastify-plugin", "app-server", "pages", "dashboard"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"build:watch": "tsc --watch",
|
|
19
|
+
"clean": "rm -rf dist",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"lint": "eslint 'src/**/*.ts'",
|
|
22
|
+
"lint:fix": "eslint --fix 'src/**/*.ts'",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"fastify-plugin": "^5.0.1"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"fastify": ">=5.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@eslint/js": "^9.24.0",
|
|
33
|
+
"@types/node": "^20.5.0",
|
|
34
|
+
"eslint": "^9.24.0",
|
|
35
|
+
"fastify": "^5.2.2",
|
|
36
|
+
"tsx": "^4.19.2",
|
|
37
|
+
"typescript": "^5.0.0",
|
|
38
|
+
"typescript-eslint": "^8.29.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
}
|
|
46
|
+
}
|