@thingd/cli 0.32.0 → 0.32.2
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/dist/index.js +3 -3
- package/dist/install.js +1 -1
- package/package.json +3 -4
- package/dist/dashboard/public/assets/index-B-Y-3-0l.js +0 -2
- package/dist/dashboard/public/assets/index-B5YhpIl3.js +0 -2
- package/dist/dashboard/public/assets/index-BnFclxvN.css +0 -1
- package/dist/dashboard/public/assets/index-BtA9rnyI.js +0 -2
- package/dist/dashboard/public/assets/index-BzLTzidY.js +0 -2
- package/dist/dashboard/public/assets/index-D8yUCdOQ.js +0 -2
- package/dist/dashboard/public/assets/index-fQywB2df.js +0 -2
- package/dist/dashboard/public/assets/index-kZdrdi3K.css +0 -1
- package/dist/dashboard/public/favicon.svg +0 -1
- package/dist/dashboard/public/icons.svg +0 -24
- package/dist/mcp/audit.d.ts +0 -27
- package/dist/mcp/audit.d.ts.map +0 -1
- package/dist/mcp/audit.js +0 -36
- package/dist/mcp/result.d.ts +0 -3
- package/dist/mcp/result.d.ts.map +0 -1
- package/dist/mcp/result.js +0 -10
- package/dist/mcp/server.d.ts +0 -19
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -51
- package/dist/mcp/tools.d.ts +0 -10
- package/dist/mcp/tools.d.ts.map +0 -1
- package/dist/mcp/tools.js +0 -568
- package/dist/rest/helpers.d.ts +0 -17
- package/dist/rest/helpers.d.ts.map +0 -1
- package/dist/rest/helpers.js +0 -55
- package/dist/rest/server.d.ts +0 -4
- package/dist/rest/server.d.ts.map +0 -1
- package/dist/rest/server.js +0 -317
package/dist/rest/server.js
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
import { parseFilter, parseIntParam, parseSortBy, readBody, sendData, sendDataList, sendError, } from "./helpers.js";
|
|
2
|
-
function matchRoute(pathname, pattern) {
|
|
3
|
-
const patternParts = pattern.split("/");
|
|
4
|
-
const pathParts = pathname.split("/");
|
|
5
|
-
if (patternParts.length !== pathParts.length) {
|
|
6
|
-
return null;
|
|
7
|
-
}
|
|
8
|
-
const match = {};
|
|
9
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
10
|
-
const pp = patternParts[i] ?? "";
|
|
11
|
-
const xp = pathParts[i] ?? "";
|
|
12
|
-
if (pp.startsWith(":")) {
|
|
13
|
-
const key = pp.slice(1);
|
|
14
|
-
match[key] = xp;
|
|
15
|
-
}
|
|
16
|
-
else if (pp !== xp) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return match;
|
|
21
|
-
}
|
|
22
|
-
export async function handleRestRequest(db, req, res, pathname) {
|
|
23
|
-
const method = req.method ?? "GET";
|
|
24
|
-
const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
|
|
25
|
-
try {
|
|
26
|
-
// ─── Health ──────────────────────────────────────────────────
|
|
27
|
-
if (pathname === "/v1/health" && method === "GET") {
|
|
28
|
-
const [objects, events, links, queues, collections, streams] = await Promise.all([
|
|
29
|
-
db.countObjects(),
|
|
30
|
-
db.countEvents(),
|
|
31
|
-
db.countLinks(),
|
|
32
|
-
db.listQueues(),
|
|
33
|
-
db.listCollections(),
|
|
34
|
-
db.listStreams(),
|
|
35
|
-
]);
|
|
36
|
-
sendData(res, {
|
|
37
|
-
status: "ok",
|
|
38
|
-
version: "0.31.0",
|
|
39
|
-
counts: {
|
|
40
|
-
objects,
|
|
41
|
-
events,
|
|
42
|
-
links,
|
|
43
|
-
queues: queues.length,
|
|
44
|
-
collections: collections.length,
|
|
45
|
-
streams: streams.length,
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
// ─── Counts ──────────────────────────────────────────────────
|
|
51
|
-
if (pathname === "/v1/counts/objects" && method === "GET") {
|
|
52
|
-
sendData(res, { count: await db.countObjects() });
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (pathname === "/v1/counts/events" && method === "GET") {
|
|
56
|
-
sendData(res, { count: await db.countEvents() });
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (pathname === "/v1/counts/links" && method === "GET") {
|
|
60
|
-
sendData(res, { count: await db.countLinks() });
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
// ─── Collections / Streams / Queues ──────────────────────────
|
|
64
|
-
if (pathname === "/v1/collections" && method === "GET") {
|
|
65
|
-
sendDataList(res, await db.listCollections());
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
if (pathname === "/v1/streams" && method === "GET") {
|
|
69
|
-
sendDataList(res, await db.listStreams());
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (pathname === "/v1/queues" && method === "GET") {
|
|
73
|
-
sendDataList(res, await db.listQueues());
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
// ─── Objects ─────────────────────────────────────────────────
|
|
77
|
-
// GET /v1/objects?collection=...&filter.x=...&sortBy=...&limit=...&offset=...
|
|
78
|
-
if (pathname === "/v1/objects" && method === "GET") {
|
|
79
|
-
const collection = url.searchParams.get("collection");
|
|
80
|
-
if (!collection) {
|
|
81
|
-
sendError(res, 400, "bad_request", "Query parameter 'collection' is required");
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const filter = parseFilter(url.searchParams);
|
|
85
|
-
const sortBy = parseSortBy(url.searchParams);
|
|
86
|
-
const limit = parseIntParam(url.searchParams.get("limit"));
|
|
87
|
-
const offset = parseIntParam(url.searchParams.get("offset"));
|
|
88
|
-
const objects = await db.listObjects(collection, { filter, sortBy, limit, offset });
|
|
89
|
-
sendDataList(res, objects);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
// PUT /v1/objects/:collection/:id
|
|
93
|
-
const objMatch = matchRoute(pathname, "/v1/objects/:collection/:id");
|
|
94
|
-
if (objMatch?.collection && objMatch?.id && method === "PUT") {
|
|
95
|
-
const body = JSON.parse(await readBody(req));
|
|
96
|
-
body.id = objMatch.id;
|
|
97
|
-
const result = await db.put(objMatch.collection, body);
|
|
98
|
-
sendData(res, result);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
// GET /v1/objects/:collection/:id
|
|
102
|
-
if (objMatch?.collection && objMatch?.id && method === "GET") {
|
|
103
|
-
const result = await db.get(objMatch.collection, objMatch.id);
|
|
104
|
-
if (!result) {
|
|
105
|
-
sendError(res, 404, "not_found", `Object '${objMatch.id}' not found in collection '${objMatch.collection}'`);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
sendData(res, result);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
// DELETE /v1/objects/:collection/:id
|
|
112
|
-
if (objMatch?.collection && objMatch?.id && method === "DELETE") {
|
|
113
|
-
const result = await db.delete(objMatch.collection, objMatch.id);
|
|
114
|
-
sendData(res, result);
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
// PUT /v1/objects/batch?collection=...
|
|
118
|
-
if (pathname === "/v1/objects/batch" && method === "PUT") {
|
|
119
|
-
const collection = url.searchParams.get("collection");
|
|
120
|
-
if (!collection) {
|
|
121
|
-
sendError(res, 400, "bad_request", "Query parameter 'collection' is required");
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const body = JSON.parse(await readBody(req));
|
|
125
|
-
const objects = Array.isArray(body) ? body : body.objects;
|
|
126
|
-
if (!Array.isArray(objects)) {
|
|
127
|
-
sendError(res, 400, "bad_request", "Body must be an array or { objects: [...] }");
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const result = await db.putBatch(collection, objects);
|
|
131
|
-
sendData(res, result);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
// DELETE /v1/objects/batch?collection=...
|
|
135
|
-
if (pathname === "/v1/objects/batch" && method === "DELETE") {
|
|
136
|
-
const collection = url.searchParams.get("collection");
|
|
137
|
-
if (!collection) {
|
|
138
|
-
sendError(res, 400, "bad_request", "Query parameter 'collection' is required");
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
const body = JSON.parse(await readBody(req));
|
|
142
|
-
const ids = Array.isArray(body) ? body : body.ids;
|
|
143
|
-
if (!Array.isArray(ids)) {
|
|
144
|
-
sendError(res, 400, "bad_request", "Body must be an array or { ids: [...] }");
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const count = await db.deleteBatch(collection, ids);
|
|
148
|
-
sendData(res, { deleted: count });
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
// ─── Search ──────────────────────────────────────────────────
|
|
152
|
-
if (pathname === "/v1/search" && method === "POST") {
|
|
153
|
-
const body = JSON.parse(await readBody(req));
|
|
154
|
-
if (!body.query) {
|
|
155
|
-
sendError(res, 400, "bad_request", "Field 'query' is required");
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const results = await db.search(body.query, {
|
|
159
|
-
collections: body.collections,
|
|
160
|
-
limit: body.limit,
|
|
161
|
-
filter: body.filter,
|
|
162
|
-
});
|
|
163
|
-
sendData(res, results);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
// ─── Events ──────────────────────────────────────────────────
|
|
167
|
-
// POST /v1/events/:stream
|
|
168
|
-
const streamMatch = matchRoute(pathname, "/v1/events/:stream");
|
|
169
|
-
if (streamMatch?.stream && method === "POST") {
|
|
170
|
-
const body = JSON.parse(await readBody(req));
|
|
171
|
-
if (!body.type) {
|
|
172
|
-
sendError(res, 400, "bad_request", "Field 'type' is required");
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
const event = await db.events.append(streamMatch.stream, body);
|
|
176
|
-
sendData(res, event);
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
// GET /v1/events?stream=...&fromSequence=...&limit=...
|
|
180
|
-
if (pathname === "/v1/events" && method === "GET") {
|
|
181
|
-
const stream = url.searchParams.get("stream") ?? undefined;
|
|
182
|
-
const fromSequence = parseIntParam(url.searchParams.get("fromSequence"));
|
|
183
|
-
const limit = parseIntParam(url.searchParams.get("limit"));
|
|
184
|
-
const events = await db.events.list(stream, { fromSequence, limit });
|
|
185
|
-
sendDataList(res, events);
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
// ─── Queues ──────────────────────────────────────────────────
|
|
189
|
-
// POST /v1/queues/:queue/push
|
|
190
|
-
const pushMatch = matchRoute(pathname, "/v1/queues/:queue/push");
|
|
191
|
-
if (pushMatch?.queue && method === "POST") {
|
|
192
|
-
const body = JSON.parse(await readBody(req));
|
|
193
|
-
const job = await db.queue(pushMatch.queue).push(body.payload ?? body, {
|
|
194
|
-
idempotencyKey: body.idempotencyKey,
|
|
195
|
-
maxAttempts: body.maxAttempts,
|
|
196
|
-
delayMs: body.delayMs,
|
|
197
|
-
});
|
|
198
|
-
sendData(res, job);
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
// POST /v1/queues/:queue/claim
|
|
202
|
-
const claimMatch = matchRoute(pathname, "/v1/queues/:queue/claim");
|
|
203
|
-
if (claimMatch?.queue && method === "POST") {
|
|
204
|
-
const body = JSON.parse(await readBody(req));
|
|
205
|
-
const job = await db.queue(claimMatch.queue).claim({
|
|
206
|
-
leaseMs: body.leaseMs,
|
|
207
|
-
});
|
|
208
|
-
if (!job) {
|
|
209
|
-
sendData(res, null);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
sendData(res, job);
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
// POST /v1/queues/:queue/ack
|
|
216
|
-
const ackMatch = matchRoute(pathname, "/v1/queues/:queue/ack");
|
|
217
|
-
if (ackMatch?.queue && method === "POST") {
|
|
218
|
-
const body = JSON.parse(await readBody(req));
|
|
219
|
-
const result = await db.queue(ackMatch.queue).ack(body.jobId);
|
|
220
|
-
if (!result.ok) {
|
|
221
|
-
sendError(res, 400, result.reason, `Ack failed: ${result.reason}`);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
sendData(res, result.job);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
// POST /v1/queues/:queue/nack
|
|
228
|
-
const nackMatch = matchRoute(pathname, "/v1/queues/:queue/nack");
|
|
229
|
-
if (nackMatch?.queue && method === "POST") {
|
|
230
|
-
const body = JSON.parse(await readBody(req));
|
|
231
|
-
const result = await db.queue(nackMatch.queue).nack(body.jobId, {
|
|
232
|
-
delayMs: body.delayMs,
|
|
233
|
-
error: body.error,
|
|
234
|
-
});
|
|
235
|
-
if (!result.ok) {
|
|
236
|
-
sendError(res, 400, result.reason, `Nack failed: ${result.reason}`);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
sendData(res, result.job);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
// GET /v1/queues/:queue/jobs
|
|
243
|
-
const jobsMatch = matchRoute(pathname, "/v1/queues/:queue/jobs");
|
|
244
|
-
if (jobsMatch?.queue && method === "GET") {
|
|
245
|
-
const jobs = await db.queue(jobsMatch.queue).list();
|
|
246
|
-
sendDataList(res, jobs);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
// GET /v1/queues/:queue/dead
|
|
250
|
-
const deadMatch = matchRoute(pathname, "/v1/queues/:queue/dead");
|
|
251
|
-
if (deadMatch?.queue && method === "GET") {
|
|
252
|
-
const jobs = await db.queue(deadMatch.queue).dead();
|
|
253
|
-
sendDataList(res, jobs);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
// ─── Links ───────────────────────────────────────────────────
|
|
257
|
-
// POST /v1/links
|
|
258
|
-
if (pathname === "/v1/links" && method === "POST") {
|
|
259
|
-
const body = JSON.parse(await readBody(req));
|
|
260
|
-
if (!body.fromRef || !body.linkType || !body.toRef) {
|
|
261
|
-
sendError(res, 400, "bad_request", "Fields 'fromRef', 'linkType', 'toRef' are required");
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
const link = await db.links.create(body.fromRef, body.linkType, body.toRef, body.weight, body.metadataJson);
|
|
265
|
-
sendData(res, link);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
// GET /v1/links?id=...
|
|
269
|
-
if (pathname === "/v1/links" && method === "GET") {
|
|
270
|
-
const id = url.searchParams.get("id");
|
|
271
|
-
if (id) {
|
|
272
|
-
const link = await db.links.get(id);
|
|
273
|
-
if (!link) {
|
|
274
|
-
sendError(res, 404, "not_found", `Link '${id}' not found`);
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
sendData(res, link);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
// Neighbors query
|
|
281
|
-
const reference = url.searchParams.get("reference");
|
|
282
|
-
if (reference) {
|
|
283
|
-
const direction = url.searchParams.get("direction") ?? "Both";
|
|
284
|
-
const linkType = url.searchParams.get("linkType") ?? undefined;
|
|
285
|
-
const limit = parseIntParam(url.searchParams.get("limit"));
|
|
286
|
-
const neighbors = await db.links.neighbors(reference, direction, { linkType, limit });
|
|
287
|
-
sendDataList(res, neighbors);
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
sendError(res, 400, "bad_request", "Query parameter 'id' or 'reference' is required");
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
// DELETE /v1/links/:id
|
|
294
|
-
const linkDeleteMatch = matchRoute(pathname, "/v1/links/:id");
|
|
295
|
-
if (linkDeleteMatch?.id && method === "DELETE") {
|
|
296
|
-
const deleted = await db.links.delete(linkDeleteMatch.id);
|
|
297
|
-
sendData(res, { deleted });
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
// GET /v1/links/:id
|
|
301
|
-
if (linkDeleteMatch?.id && method === "GET") {
|
|
302
|
-
const link = await db.links.get(linkDeleteMatch.id);
|
|
303
|
-
if (!link) {
|
|
304
|
-
sendError(res, 404, "not_found", `Link '${linkDeleteMatch.id}' not found`);
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
sendData(res, link);
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
// ─── 404 ─────────────────────────────────────────────────────
|
|
311
|
-
sendError(res, 404, "not_found", `No route for ${method} ${pathname}`);
|
|
312
|
-
}
|
|
313
|
-
catch (err) {
|
|
314
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
315
|
-
sendError(res, 500, "internal_error", message);
|
|
316
|
-
}
|
|
317
|
-
}
|