@typicalday/firegraph 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.
Files changed (48) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +527 -0
  3. package/bin/firegraph.mjs +129 -0
  4. package/dist/chunk-KFA7G37W.js +443 -0
  5. package/dist/chunk-KFA7G37W.js.map +1 -0
  6. package/dist/chunk-YLGXLEUE.js +47 -0
  7. package/dist/chunk-YLGXLEUE.js.map +1 -0
  8. package/dist/client-Bk2Cm6xv.d.cts +131 -0
  9. package/dist/client-Bk2Cm6xv.d.ts +131 -0
  10. package/dist/codegen/index.cjs +81 -0
  11. package/dist/codegen/index.cjs.map +1 -0
  12. package/dist/codegen/index.d.cts +2 -0
  13. package/dist/codegen/index.d.ts +2 -0
  14. package/dist/codegen/index.js +7 -0
  15. package/dist/codegen/index.js.map +1 -0
  16. package/dist/editor/client/assets/index-DJJ_b0jI.js +411 -0
  17. package/dist/editor/client/assets/index-Q0QBYrMV.css +1 -0
  18. package/dist/editor/client/index.html +16 -0
  19. package/dist/editor/server/index.mjs +49597 -0
  20. package/dist/index-CG3R68Hu.d.cts +414 -0
  21. package/dist/index-CG3R68Hu.d.ts +414 -0
  22. package/dist/index.cjs +1953 -0
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.cts +186 -0
  25. package/dist/index.d.ts +186 -0
  26. package/dist/index.js +1569 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/query-client/index.cjs +484 -0
  29. package/dist/query-client/index.cjs.map +1 -0
  30. package/dist/query-client/index.d.cts +15 -0
  31. package/dist/query-client/index.d.ts +15 -0
  32. package/dist/query-client/index.js +17 -0
  33. package/dist/query-client/index.js.map +1 -0
  34. package/dist/react.cjs +85 -0
  35. package/dist/react.cjs.map +1 -0
  36. package/dist/react.d.cts +44 -0
  37. package/dist/react.d.ts +44 -0
  38. package/dist/react.js +60 -0
  39. package/dist/react.js.map +1 -0
  40. package/dist/svelte.cjs +90 -0
  41. package/dist/svelte.cjs.map +1 -0
  42. package/dist/svelte.d.cts +46 -0
  43. package/dist/svelte.d.ts +46 -0
  44. package/dist/svelte.js +65 -0
  45. package/dist/svelte.js.map +1 -0
  46. package/dist/views-DL60k0cf.d.cts +91 -0
  47. package/dist/views-DL60k0cf.d.ts +91 -0
  48. package/package.json +122 -0
@@ -0,0 +1,443 @@
1
+ // src/query-client/client.ts
2
+ import http from "http";
3
+
4
+ // src/query-client/shaping.ts
5
+ function summarizeRecord(r) {
6
+ if (!r) return null;
7
+ const out = { type: r.aType, uid: r.aUid };
8
+ const data = r.data;
9
+ if (data && typeof data === "object" && Object.keys(data).length > 0) {
10
+ out.data = data;
11
+ }
12
+ return out;
13
+ }
14
+ function summarizeEdge(r) {
15
+ if (!r) return null;
16
+ const out = {
17
+ fromType: r.aType,
18
+ fromUid: r.aUid,
19
+ relation: r.axbType,
20
+ toType: r.bType,
21
+ toUid: r.bUid
22
+ };
23
+ const data = r.data;
24
+ if (data && typeof data === "object" && Object.keys(data).length > 0) {
25
+ out.data = data;
26
+ }
27
+ return out;
28
+ }
29
+
30
+ // src/query-client/config.ts
31
+ import { readFileSync } from "fs";
32
+ import { join } from "path";
33
+ var CONFIG_FILES = ["firegraph.config.ts", "firegraph.config.js", "firegraph.config.mjs"];
34
+ var DEFAULT_PORT = 3884;
35
+ function readEditorPort(cwd) {
36
+ const dir = cwd ?? process.cwd();
37
+ for (const name of CONFIG_FILES) {
38
+ try {
39
+ const content = readFileSync(join(dir, name), "utf8");
40
+ const editorBlock = content.match(/editor\s*:\s*\{[^}]*\}/s)?.[0] ?? "";
41
+ const portMatch = editorBlock.match(/port\s*:\s*(\d+)/);
42
+ if (portMatch) return parseInt(portMatch[1], 10);
43
+ } catch {
44
+ continue;
45
+ }
46
+ }
47
+ return DEFAULT_PORT;
48
+ }
49
+
50
+ // src/query-client/client.ts
51
+ var QueryClientError = class extends Error {
52
+ constructor(message, code) {
53
+ super(message);
54
+ this.code = code;
55
+ this.name = "QueryClientError";
56
+ }
57
+ };
58
+ function requireString(value, name) {
59
+ if (typeof value !== "string" || value.length === 0) {
60
+ throw new QueryClientError(`${name} must be a non-empty string`, "VALIDATION_ERROR");
61
+ }
62
+ }
63
+ function clampInt(value, min, max, fallback) {
64
+ if (value == null) return fallback;
65
+ if (!Number.isInteger(value)) {
66
+ throw new QueryClientError(`limit must be an integer`, "VALIDATION_ERROR");
67
+ }
68
+ return Math.max(min, Math.min(max, value));
69
+ }
70
+ function validateSortDir(dir) {
71
+ if (dir != null && dir !== "asc" && dir !== "desc") {
72
+ throw new QueryClientError(`sortDir must be 'asc' or 'desc'`, "VALIDATION_ERROR");
73
+ }
74
+ }
75
+ function httpGet(url) {
76
+ return new Promise((resolve, reject) => {
77
+ http.get(url, (res) => {
78
+ let body = "";
79
+ res.on("data", (c) => body += c);
80
+ res.on("end", () => resolve(body));
81
+ }).on("error", (err) => {
82
+ reject(new QueryClientError(`Connection failed: ${err.message}`, "CONNECTION_FAILED"));
83
+ });
84
+ });
85
+ }
86
+ function httpPost(url, payload) {
87
+ const parsed = new URL(url);
88
+ return new Promise((resolve, reject) => {
89
+ const req = http.request(
90
+ {
91
+ hostname: parsed.hostname,
92
+ port: parsed.port,
93
+ path: parsed.pathname,
94
+ method: "POST",
95
+ headers: {
96
+ "Content-Type": "application/json",
97
+ "Content-Length": Buffer.byteLength(payload)
98
+ }
99
+ },
100
+ (res) => {
101
+ let body = "";
102
+ res.on("data", (c) => body += c);
103
+ res.on("end", () => resolve(body));
104
+ }
105
+ );
106
+ req.on("error", (err) => {
107
+ reject(new QueryClientError(`Connection failed: ${err.message}`, "CONNECTION_FAILED"));
108
+ });
109
+ req.write(payload);
110
+ req.end();
111
+ });
112
+ }
113
+ function parseTrpcResponse(raw, procedure) {
114
+ let parsed;
115
+ try {
116
+ parsed = JSON.parse(raw);
117
+ } catch {
118
+ throw new QueryClientError(
119
+ `Invalid JSON from ${procedure}: ${raw.slice(0, 200)}`,
120
+ "SERVER_ERROR"
121
+ );
122
+ }
123
+ if (parsed.error) {
124
+ const msg = typeof parsed.error === "object" && parsed.error !== null ? parsed.error.message ?? JSON.stringify(parsed.error) : String(parsed.error);
125
+ throw new QueryClientError(`Server error from ${procedure}: ${msg}`, "SERVER_ERROR");
126
+ }
127
+ return parsed.result?.data ?? parsed;
128
+ }
129
+ var QueryClient = class {
130
+ baseUrl;
131
+ constructor(options) {
132
+ const host = options?.host ?? "localhost";
133
+ const port = options?.port ?? readEditorPort();
134
+ this.baseUrl = `http://${host}:${port}/api/trpc`;
135
+ }
136
+ async query(procedure, input) {
137
+ const qs = input != null ? `?input=${encodeURIComponent(JSON.stringify(input))}` : "";
138
+ const url = `${this.baseUrl}/${procedure}${qs}`;
139
+ const raw = await httpGet(url);
140
+ return parseTrpcResponse(raw, procedure);
141
+ }
142
+ async mutate(procedure, input) {
143
+ const url = `${this.baseUrl}/${procedure}`;
144
+ const raw = await httpPost(url, JSON.stringify(input));
145
+ return parseTrpcResponse(raw, procedure);
146
+ }
147
+ // --- Public API ---
148
+ async getSchema() {
149
+ const data = await this.query("getSchema");
150
+ return {
151
+ nodeTypes: (data.nodeTypes ?? []).map(
152
+ (t) => typeof t === "object" && t !== null ? t.type : t
153
+ ),
154
+ edgeTypes: (data.edgeTypes ?? []).map((t) => {
155
+ const e = t;
156
+ return {
157
+ relation: e.axbType,
158
+ from: e.aType,
159
+ to: e.bType,
160
+ inverseLabel: e.inverseLabel ?? null
161
+ };
162
+ })
163
+ };
164
+ }
165
+ async getNodeDetail(input) {
166
+ requireString(input.uid, "uid");
167
+ const data = await this.query("getNodeDetail", { uid: input.uid });
168
+ return {
169
+ node: summarizeRecord(data.node),
170
+ outEdges: (data.outEdges ?? []).map(summarizeEdge).filter(Boolean),
171
+ inEdges: (data.inEdges ?? []).map(summarizeEdge).filter(Boolean)
172
+ };
173
+ }
174
+ async getNodes(input) {
175
+ const limit = clampInt(input.limit, 1, 200, 25);
176
+ validateSortDir(input.sortDir);
177
+ const data = await this.query("getNodes", {
178
+ type: input.type,
179
+ limit,
180
+ startAfter: input.startAfter,
181
+ sortBy: input.sortBy,
182
+ sortDir: input.sortDir,
183
+ where: input.where
184
+ });
185
+ return {
186
+ nodes: (data.nodes ?? []).map(summarizeRecord).filter(Boolean),
187
+ hasMore: data.hasMore ?? false,
188
+ nextCursor: data.nextCursor
189
+ };
190
+ }
191
+ async getEdges(input) {
192
+ const hasFilter = input.aType || input.aUid || input.axbType || input.bType || input.bUid || input.where && input.where.length > 0;
193
+ if (!hasFilter) {
194
+ throw new QueryClientError(
195
+ "getEdges requires at least one filter field (aType, aUid, axbType, bType, bUid, or where)",
196
+ "VALIDATION_ERROR"
197
+ );
198
+ }
199
+ const limit = clampInt(input.limit, 1, 200, 25);
200
+ validateSortDir(input.sortDir);
201
+ const data = await this.query("getEdges", {
202
+ aType: input.aType,
203
+ aUid: input.aUid,
204
+ axbType: input.axbType,
205
+ bType: input.bType,
206
+ bUid: input.bUid,
207
+ limit,
208
+ startAfter: input.startAfter,
209
+ sortBy: input.sortBy,
210
+ sortDir: input.sortDir,
211
+ where: input.where
212
+ });
213
+ return {
214
+ edges: (data.edges ?? []).map(summarizeEdge).filter(Boolean),
215
+ hasMore: data.hasMore ?? false,
216
+ nextCursor: data.nextCursor
217
+ };
218
+ }
219
+ async traverse(input) {
220
+ requireString(input.startUid, "startUid");
221
+ if (!input.hops || input.hops.length === 0) {
222
+ throw new QueryClientError("traverse requires at least one hop", "VALIDATION_ERROR");
223
+ }
224
+ for (let i = 0; i < input.hops.length; i++) {
225
+ const hop = input.hops[i];
226
+ requireString(hop.axbType, `hops[${i}].axbType`);
227
+ if (hop.direction != null && hop.direction !== "forward" && hop.direction !== "reverse") {
228
+ throw new QueryClientError(
229
+ `hops[${i}].direction must be 'forward' or 'reverse'`,
230
+ "VALIDATION_ERROR"
231
+ );
232
+ }
233
+ if (hop.limit != null && (!Number.isInteger(hop.limit) || hop.limit < 1)) {
234
+ throw new QueryClientError(
235
+ `hops[${i}].limit must be a positive integer`,
236
+ "VALIDATION_ERROR"
237
+ );
238
+ }
239
+ }
240
+ if (input.maxReads != null && (!Number.isInteger(input.maxReads) || input.maxReads < 1)) {
241
+ throw new QueryClientError("maxReads must be a positive integer", "VALIDATION_ERROR");
242
+ }
243
+ if (input.concurrency != null && (!Number.isInteger(input.concurrency) || input.concurrency < 1)) {
244
+ throw new QueryClientError("concurrency must be a positive integer", "VALIDATION_ERROR");
245
+ }
246
+ const data = await this.mutate("traverse", input);
247
+ return {
248
+ hops: (data.hops ?? []).map((h) => ({
249
+ relation: h.axbType,
250
+ direction: h.direction,
251
+ depth: h.depth,
252
+ edgeCount: (h.edges ?? []).length,
253
+ edges: (h.edges ?? []).map(summarizeEdge).filter(Boolean),
254
+ truncated: h.truncated ?? false
255
+ })),
256
+ totalReads: data.totalReads ?? 0,
257
+ truncated: data.truncated ?? false
258
+ };
259
+ }
260
+ async search(input) {
261
+ requireString(input.q, "q");
262
+ const limit = clampInt(input.limit, 1, 50, 20);
263
+ const data = await this.query("search", { q: input.q, limit });
264
+ return {
265
+ results: (data.results ?? []).map((r) => {
266
+ const base = summarizeRecord(r);
267
+ if (!base) return null;
268
+ return {
269
+ ...base,
270
+ matchType: r._matchType ?? null
271
+ };
272
+ }).filter(Boolean)
273
+ };
274
+ }
275
+ };
276
+
277
+ // src/query-client/cli.ts
278
+ function parseFlags(args) {
279
+ const flags = {};
280
+ const positional = [];
281
+ for (let i = 0; i < args.length; i++) {
282
+ if (args[i].startsWith("--")) {
283
+ const key = args[i].slice(2);
284
+ const next = args[i + 1];
285
+ if (next && !next.startsWith("--")) {
286
+ flags[key] = next;
287
+ i++;
288
+ } else {
289
+ flags[key] = "true";
290
+ }
291
+ } else {
292
+ positional.push(args[i]);
293
+ }
294
+ }
295
+ return { flags, positional };
296
+ }
297
+ async function runQueryCli(argv) {
298
+ const command = argv[0];
299
+ const rest = argv.slice(1);
300
+ const { flags, positional } = parseFlags(rest);
301
+ const port = flags.port ? parseInt(flags.port, 10) : void 0;
302
+ const host = flags.host ?? void 0;
303
+ const client = new QueryClient({ port, host });
304
+ try {
305
+ let result;
306
+ switch (command) {
307
+ case "schema":
308
+ result = await client.getSchema();
309
+ break;
310
+ case "get":
311
+ if (!positional[0]) {
312
+ die("Usage: firegraph query get <uid>");
313
+ }
314
+ result = await client.getNodeDetail({ uid: positional[0] });
315
+ break;
316
+ case "find-nodes": {
317
+ if (!positional[0]) {
318
+ die("Usage: firegraph query find-nodes <type> [--limit N]");
319
+ }
320
+ result = await client.getNodes({
321
+ type: positional[0],
322
+ limit: flags.limit ? parseInt(flags.limit, 10) : void 0
323
+ });
324
+ break;
325
+ }
326
+ case "find-edges": {
327
+ result = await client.getEdges({
328
+ aType: flags.aType,
329
+ aUid: flags.aUid,
330
+ axbType: flags.axbType,
331
+ bType: flags.bType,
332
+ bUid: flags.bUid,
333
+ limit: flags.limit ? parseInt(flags.limit, 10) : void 0
334
+ });
335
+ break;
336
+ }
337
+ case "traverse": {
338
+ const jsonStr = positional.join(" ");
339
+ if (!jsonStr) {
340
+ die(
341
+ `Usage: firegraph query traverse '<JSON>'
342
+
343
+ JSON shape:
344
+ {
345
+ "startUid": "nodeUid",
346
+ "hops": [
347
+ {
348
+ "axbType": "relationName",
349
+ "direction": "forward" | "reverse",
350
+ "limit": 10,
351
+ "aType": "filterSourceType",
352
+ "bType": "filterTargetType",
353
+ "orderBy": { "field": "data.name", "direction": "asc" },
354
+ "where": [{ "field": "data.status", "op": "==", "value": "active" }]
355
+ }
356
+ ],
357
+ "maxReads": 100,
358
+ "concurrency": 5
359
+ }`
360
+ );
361
+ }
362
+ let input;
363
+ try {
364
+ input = JSON.parse(jsonStr);
365
+ } catch {
366
+ die(`Invalid JSON: ${jsonStr.slice(0, 200)}`);
367
+ }
368
+ result = await client.traverse(input);
369
+ break;
370
+ }
371
+ case "search":
372
+ if (!positional[0]) {
373
+ die("Usage: firegraph query search <query>");
374
+ }
375
+ result = await client.search({ q: positional.join(" ") });
376
+ break;
377
+ case "--help":
378
+ case "-h":
379
+ case void 0:
380
+ printHelp();
381
+ return;
382
+ default:
383
+ die(
384
+ `Unknown query command: ${command}
385
+ Commands: schema, get, find-nodes, find-edges, traverse, search`
386
+ );
387
+ }
388
+ console.log(JSON.stringify(result, null, 2));
389
+ } catch (err) {
390
+ if (err instanceof QueryClientError) {
391
+ console.error(JSON.stringify({ error: err.message, code: err.code }));
392
+ } else {
393
+ console.error(JSON.stringify({ error: err.message }));
394
+ }
395
+ process.exit(1);
396
+ }
397
+ }
398
+ function printHelp() {
399
+ console.log("");
400
+ console.log(" Usage: firegraph query <command> [options]");
401
+ console.log("");
402
+ console.log(" Commands:");
403
+ console.log(" schema Get graph schema (node types + edge types)");
404
+ console.log(" get <uid> Get node detail with edges");
405
+ console.log(" find-nodes <type> [--limit N] List nodes of a type");
406
+ console.log(" find-edges [filters] List edges matching filters");
407
+ console.log(" traverse '<JSON>' Multi-hop graph traversal");
408
+ console.log(" search <query> Search nodes by text");
409
+ console.log("");
410
+ console.log(" Global options:");
411
+ console.log(" --port <number> Editor server port (default: auto-detect from config)");
412
+ console.log(" --host <string> Editor server host (default: localhost)");
413
+ console.log("");
414
+ console.log(" find-edges filters:");
415
+ console.log(" --aType <type> Filter by source type");
416
+ console.log(" --aUid <uid> Filter by source UID");
417
+ console.log(" --axbType <rel> Filter by relation type");
418
+ console.log(" --bType <type> Filter by target type");
419
+ console.log(" --bUid <uid> Filter by target UID");
420
+ console.log(" --limit <N> Max results (1-200, default 25)");
421
+ console.log("");
422
+ console.log(" Examples:");
423
+ console.log(" npx firegraph query schema");
424
+ console.log(" npx firegraph query get Kj7vNq2mP9xR4wL1tY8s3");
425
+ console.log(" npx firegraph query find-nodes task --limit 10");
426
+ console.log(" npx firegraph query find-edges --aUid Kj7vNq2mP9xR4wL1tY8s3 --axbType hasTask");
427
+ console.log(' npx firegraph query search "John Doe"');
428
+ console.log("");
429
+ }
430
+ function die(msg) {
431
+ console.error(msg);
432
+ process.exit(1);
433
+ }
434
+
435
+ export {
436
+ summarizeRecord,
437
+ summarizeEdge,
438
+ readEditorPort,
439
+ QueryClientError,
440
+ QueryClient,
441
+ runQueryCli
442
+ };
443
+ //# sourceMappingURL=chunk-KFA7G37W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/query-client/client.ts","../src/query-client/shaping.ts","../src/query-client/config.ts","../src/query-client/cli.ts"],"sourcesContent":["import http from 'node:http';\nimport type {\n QueryClientOptions,\n SchemaResult,\n GetNodeDetailInput,\n NodeDetailResult,\n GetNodesInput,\n GetNodesResult,\n GetEdgesInput,\n GetEdgesResult,\n TraverseInput,\n TraverseResult,\n TraverseHopResult,\n SearchInput,\n SearchResult,\n SummarizedRecord,\n SummarizedEdge,\n} from './types.js';\nimport { summarizeRecord, summarizeEdge } from './shaping.js';\nimport { readEditorPort } from './config.js';\n\n// --- Error ---\n\nexport type QueryClientErrorCode = 'VALIDATION_ERROR' | 'CONNECTION_FAILED' | 'SERVER_ERROR';\n\nexport class QueryClientError extends Error {\n constructor(\n message: string,\n public readonly code: QueryClientErrorCode,\n ) {\n super(message);\n this.name = 'QueryClientError';\n }\n}\n\n// --- Validation helpers ---\n\nfunction requireString(value: unknown, name: string): asserts value is string {\n if (typeof value !== 'string' || value.length === 0) {\n throw new QueryClientError(`${name} must be a non-empty string`, 'VALIDATION_ERROR');\n }\n}\n\nfunction clampInt(value: number | undefined, min: number, max: number, fallback: number): number {\n if (value == null) return fallback;\n if (!Number.isInteger(value)) {\n throw new QueryClientError(`limit must be an integer`, 'VALIDATION_ERROR');\n }\n return Math.max(min, Math.min(max, value));\n}\n\nfunction validateSortDir(dir: string | undefined): void {\n if (dir != null && dir !== 'asc' && dir !== 'desc') {\n throw new QueryClientError(`sortDir must be 'asc' or 'desc'`, 'VALIDATION_ERROR');\n }\n}\n\n// --- HTTP helpers ---\n\nfunction httpGet(url: string): Promise<string> {\n return new Promise((resolve, reject) => {\n http\n .get(url, (res) => {\n let body = '';\n res.on('data', (c: string) => (body += c));\n res.on('end', () => resolve(body));\n })\n .on('error', (err) => {\n reject(new QueryClientError(`Connection failed: ${err.message}`, 'CONNECTION_FAILED'));\n });\n });\n}\n\nfunction httpPost(url: string, payload: string): Promise<string> {\n const parsed = new URL(url);\n return new Promise((resolve, reject) => {\n const req = http.request(\n {\n hostname: parsed.hostname,\n port: parsed.port,\n path: parsed.pathname,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(payload),\n },\n },\n (res) => {\n let body = '';\n res.on('data', (c: string) => (body += c));\n res.on('end', () => resolve(body));\n },\n );\n req.on('error', (err) => {\n reject(new QueryClientError(`Connection failed: ${err.message}`, 'CONNECTION_FAILED'));\n });\n req.write(payload);\n req.end();\n });\n}\n\nfunction parseTrpcResponse(raw: string, procedure: string): unknown {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new QueryClientError(\n `Invalid JSON from ${procedure}: ${raw.slice(0, 200)}`,\n 'SERVER_ERROR',\n );\n }\n if (parsed.error) {\n const msg =\n typeof parsed.error === 'object' && parsed.error !== null\n ? (parsed.error as Record<string, unknown>).message ?? JSON.stringify(parsed.error)\n : String(parsed.error);\n throw new QueryClientError(`Server error from ${procedure}: ${msg}`, 'SERVER_ERROR');\n }\n return (parsed.result as Record<string, unknown>)?.data ?? parsed;\n}\n\n// --- Client ---\n\nexport class QueryClient {\n private readonly baseUrl: string;\n\n constructor(options?: QueryClientOptions) {\n const host = options?.host ?? 'localhost';\n const port = options?.port ?? readEditorPort();\n this.baseUrl = `http://${host}:${port}/api/trpc`;\n }\n\n private async query(procedure: string, input?: unknown): Promise<unknown> {\n const qs =\n input != null ? `?input=${encodeURIComponent(JSON.stringify(input))}` : '';\n const url = `${this.baseUrl}/${procedure}${qs}`;\n const raw = await httpGet(url);\n return parseTrpcResponse(raw, procedure);\n }\n\n private async mutate(procedure: string, input: unknown): Promise<unknown> {\n const url = `${this.baseUrl}/${procedure}`;\n const raw = await httpPost(url, JSON.stringify(input));\n return parseTrpcResponse(raw, procedure);\n }\n\n // --- Public API ---\n\n async getSchema(): Promise<SchemaResult> {\n const data = (await this.query('getSchema')) as Record<string, unknown>;\n return {\n nodeTypes: ((data.nodeTypes as unknown[]) ?? []).map(\n (t) => (typeof t === 'object' && t !== null ? (t as Record<string, unknown>).type : t) as string,\n ),\n edgeTypes: ((data.edgeTypes as unknown[]) ?? []).map((t) => {\n const e = t as Record<string, unknown>;\n return {\n relation: e.axbType as string,\n from: e.aType as string,\n to: e.bType as string,\n inverseLabel: (e.inverseLabel as string) ?? null,\n };\n }),\n };\n }\n\n async getNodeDetail(input: GetNodeDetailInput): Promise<NodeDetailResult> {\n requireString(input.uid, 'uid');\n const data = (await this.query('getNodeDetail', { uid: input.uid })) as Record<string, unknown>;\n return {\n node: summarizeRecord(data.node as Record<string, unknown> | null),\n outEdges: ((data.outEdges as Record<string, unknown>[]) ?? []).map(summarizeEdge).filter(Boolean) as SummarizedEdge[],\n inEdges: ((data.inEdges as Record<string, unknown>[]) ?? []).map(summarizeEdge).filter(Boolean) as SummarizedEdge[],\n };\n }\n\n async getNodes(input: GetNodesInput): Promise<GetNodesResult> {\n const limit = clampInt(input.limit, 1, 200, 25);\n validateSortDir(input.sortDir);\n const data = (await this.query('getNodes', {\n type: input.type,\n limit,\n startAfter: input.startAfter,\n sortBy: input.sortBy,\n sortDir: input.sortDir,\n where: input.where,\n })) as Record<string, unknown>;\n return {\n nodes: ((data.nodes as Record<string, unknown>[]) ?? []).map(summarizeRecord).filter(Boolean) as SummarizedRecord[],\n hasMore: (data.hasMore as boolean) ?? false,\n nextCursor: data.nextCursor as string | null | undefined,\n };\n }\n\n async getEdges(input: GetEdgesInput): Promise<GetEdgesResult> {\n const hasFilter = input.aType || input.aUid || input.axbType || input.bType || input.bUid || (input.where && input.where.length > 0);\n if (!hasFilter) {\n throw new QueryClientError(\n 'getEdges requires at least one filter field (aType, aUid, axbType, bType, bUid, or where)',\n 'VALIDATION_ERROR',\n );\n }\n const limit = clampInt(input.limit, 1, 200, 25);\n validateSortDir(input.sortDir);\n const data = (await this.query('getEdges', {\n aType: input.aType,\n aUid: input.aUid,\n axbType: input.axbType,\n bType: input.bType,\n bUid: input.bUid,\n limit,\n startAfter: input.startAfter,\n sortBy: input.sortBy,\n sortDir: input.sortDir,\n where: input.where,\n })) as Record<string, unknown>;\n return {\n edges: ((data.edges as Record<string, unknown>[]) ?? []).map(summarizeEdge).filter(Boolean) as SummarizedEdge[],\n hasMore: (data.hasMore as boolean) ?? false,\n nextCursor: data.nextCursor as string | null | undefined,\n };\n }\n\n async traverse(input: TraverseInput): Promise<TraverseResult> {\n requireString(input.startUid, 'startUid');\n if (!input.hops || input.hops.length === 0) {\n throw new QueryClientError('traverse requires at least one hop', 'VALIDATION_ERROR');\n }\n for (let i = 0; i < input.hops.length; i++) {\n const hop = input.hops[i];\n requireString(hop.axbType, `hops[${i}].axbType`);\n if (hop.direction != null && hop.direction !== 'forward' && hop.direction !== 'reverse') {\n throw new QueryClientError(\n `hops[${i}].direction must be 'forward' or 'reverse'`,\n 'VALIDATION_ERROR',\n );\n }\n if (hop.limit != null && (!Number.isInteger(hop.limit) || hop.limit < 1)) {\n throw new QueryClientError(\n `hops[${i}].limit must be a positive integer`,\n 'VALIDATION_ERROR',\n );\n }\n }\n if (input.maxReads != null && (!Number.isInteger(input.maxReads) || input.maxReads < 1)) {\n throw new QueryClientError('maxReads must be a positive integer', 'VALIDATION_ERROR');\n }\n if (input.concurrency != null && (!Number.isInteger(input.concurrency) || input.concurrency < 1)) {\n throw new QueryClientError('concurrency must be a positive integer', 'VALIDATION_ERROR');\n }\n\n const data = (await this.mutate('traverse', input)) as Record<string, unknown>;\n return {\n hops: ((data.hops as Record<string, unknown>[]) ?? []).map((h): TraverseHopResult => ({\n relation: h.axbType as string,\n direction: h.direction as string,\n depth: h.depth as number,\n edgeCount: ((h.edges as unknown[]) ?? []).length,\n edges: ((h.edges as Record<string, unknown>[]) ?? []).map(summarizeEdge).filter(Boolean) as SummarizedEdge[],\n truncated: (h.truncated as boolean) ?? false,\n })),\n totalReads: (data.totalReads as number) ?? 0,\n truncated: (data.truncated as boolean) ?? false,\n };\n }\n\n async search(input: SearchInput): Promise<SearchResult> {\n requireString(input.q, 'q');\n const limit = clampInt(input.limit, 1, 50, 20);\n const data = (await this.query('search', { q: input.q, limit })) as Record<string, unknown>;\n return {\n results: ((data.results as Record<string, unknown>[]) ?? []).map((r) => {\n const base = summarizeRecord(r);\n if (!base) return null;\n return {\n ...base,\n matchType: (r._matchType as string) ?? null,\n };\n }).filter(Boolean) as (SummarizedRecord & { matchType: string | null })[],\n };\n }\n}\n","import type { SummarizedRecord, SummarizedEdge } from './types.js';\n\nexport function summarizeRecord(r: Record<string, unknown> | null): SummarizedRecord | null {\n if (!r) return null;\n const out: SummarizedRecord = { type: r.aType as string, uid: r.aUid as string };\n const data = r.data as Record<string, unknown> | undefined;\n if (data && typeof data === 'object' && Object.keys(data).length > 0) {\n out.data = data;\n }\n return out;\n}\n\nexport function summarizeEdge(r: Record<string, unknown> | null): SummarizedEdge | null {\n if (!r) return null;\n const out: SummarizedEdge = {\n fromType: r.aType as string,\n fromUid: r.aUid as string,\n relation: r.axbType as string,\n toType: r.bType as string,\n toUid: r.bUid as string,\n };\n const data = r.data as Record<string, unknown> | undefined;\n if (data && typeof data === 'object' && Object.keys(data).length > 0) {\n out.data = data;\n }\n return out;\n}\n","import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst CONFIG_FILES = ['firegraph.config.ts', 'firegraph.config.js', 'firegraph.config.mjs'];\nconst DEFAULT_PORT = 3884;\n\n/**\n * Read the editor port from firegraph config files using regex.\n * Zero-dependency — no jiti needed.\n */\nexport function readEditorPort(cwd?: string): number {\n const dir = cwd ?? process.cwd();\n for (const name of CONFIG_FILES) {\n try {\n const content = readFileSync(join(dir, name), 'utf8');\n const editorBlock = content.match(/editor\\s*:\\s*\\{[^}]*\\}/s)?.[0] ?? '';\n const portMatch = editorBlock.match(/port\\s*:\\s*(\\d+)/);\n if (portMatch) return parseInt(portMatch[1], 10);\n } catch {\n continue;\n }\n }\n return DEFAULT_PORT;\n}\n","import { QueryClient, QueryClientError } from './client.js';\nimport type { TraverseInput } from './types.js';\n\n// --- Argument parsing ---\n\ninterface ParsedArgs {\n flags: Record<string, string>;\n positional: string[];\n}\n\nfunction parseFlags(args: string[]): ParsedArgs {\n const flags: Record<string, string> = {};\n const positional: string[] = [];\n for (let i = 0; i < args.length; i++) {\n if (args[i].startsWith('--')) {\n const key = args[i].slice(2);\n const next = args[i + 1];\n if (next && !next.startsWith('--')) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = 'true';\n }\n } else {\n positional.push(args[i]);\n }\n }\n return { flags, positional };\n}\n\n// --- CLI runner ---\n\nexport async function runQueryCli(argv: string[]): Promise<void> {\n const command = argv[0];\n const rest = argv.slice(1);\n const { flags, positional } = parseFlags(rest);\n\n const port = flags.port ? parseInt(flags.port, 10) : undefined;\n const host = flags.host ?? undefined;\n const client = new QueryClient({ port, host });\n\n try {\n let result: unknown;\n\n switch (command) {\n case 'schema':\n result = await client.getSchema();\n break;\n\n case 'get':\n if (!positional[0]) {\n die('Usage: firegraph query get <uid>');\n }\n result = await client.getNodeDetail({ uid: positional[0] });\n break;\n\n case 'find-nodes': {\n if (!positional[0]) {\n die('Usage: firegraph query find-nodes <type> [--limit N]');\n }\n result = await client.getNodes({\n type: positional[0],\n limit: flags.limit ? parseInt(flags.limit, 10) : undefined,\n });\n break;\n }\n\n case 'find-edges': {\n result = await client.getEdges({\n aType: flags.aType,\n aUid: flags.aUid,\n axbType: flags.axbType,\n bType: flags.bType,\n bUid: flags.bUid,\n limit: flags.limit ? parseInt(flags.limit, 10) : undefined,\n });\n break;\n }\n\n case 'traverse': {\n const jsonStr = positional.join(' ');\n if (!jsonStr) {\n die(\n 'Usage: firegraph query traverse \\'<JSON>\\'\\n\\n' +\n 'JSON shape:\\n' +\n '{\\n' +\n ' \"startUid\": \"nodeUid\",\\n' +\n ' \"hops\": [\\n' +\n ' {\\n' +\n ' \"axbType\": \"relationName\",\\n' +\n ' \"direction\": \"forward\" | \"reverse\",\\n' +\n ' \"limit\": 10,\\n' +\n ' \"aType\": \"filterSourceType\",\\n' +\n ' \"bType\": \"filterTargetType\",\\n' +\n ' \"orderBy\": { \"field\": \"data.name\", \"direction\": \"asc\" },\\n' +\n ' \"where\": [{ \"field\": \"data.status\", \"op\": \"==\", \"value\": \"active\" }]\\n' +\n ' }\\n' +\n ' ],\\n' +\n ' \"maxReads\": 100,\\n' +\n ' \"concurrency\": 5\\n' +\n '}',\n );\n }\n let input: TraverseInput;\n try {\n input = JSON.parse(jsonStr) as TraverseInput;\n } catch {\n die(`Invalid JSON: ${jsonStr.slice(0, 200)}`);\n }\n result = await client.traverse(input!);\n break;\n }\n\n case 'search':\n if (!positional[0]) {\n die('Usage: firegraph query search <query>');\n }\n result = await client.search({ q: positional.join(' ') });\n break;\n\n case '--help':\n case '-h':\n case undefined:\n printHelp();\n return;\n\n default:\n die(\n `Unknown query command: ${command}\\n` +\n 'Commands: schema, get, find-nodes, find-edges, traverse, search',\n );\n }\n\n console.log(JSON.stringify(result, null, 2));\n } catch (err) {\n if (err instanceof QueryClientError) {\n console.error(JSON.stringify({ error: err.message, code: err.code }));\n } else {\n console.error(JSON.stringify({ error: (err as Error).message }));\n }\n process.exit(1);\n }\n}\n\nfunction printHelp(): void {\n console.log('');\n console.log(' Usage: firegraph query <command> [options]');\n console.log('');\n console.log(' Commands:');\n console.log(' schema Get graph schema (node types + edge types)');\n console.log(' get <uid> Get node detail with edges');\n console.log(' find-nodes <type> [--limit N] List nodes of a type');\n console.log(' find-edges [filters] List edges matching filters');\n console.log(' traverse \\'<JSON>\\' Multi-hop graph traversal');\n console.log(' search <query> Search nodes by text');\n console.log('');\n console.log(' Global options:');\n console.log(' --port <number> Editor server port (default: auto-detect from config)');\n console.log(' --host <string> Editor server host (default: localhost)');\n console.log('');\n console.log(' find-edges filters:');\n console.log(' --aType <type> Filter by source type');\n console.log(' --aUid <uid> Filter by source UID');\n console.log(' --axbType <rel> Filter by relation type');\n console.log(' --bType <type> Filter by target type');\n console.log(' --bUid <uid> Filter by target UID');\n console.log(' --limit <N> Max results (1-200, default 25)');\n console.log('');\n console.log(' Examples:');\n console.log(' npx firegraph query schema');\n console.log(' npx firegraph query get Kj7vNq2mP9xR4wL1tY8s3');\n console.log(' npx firegraph query find-nodes task --limit 10');\n console.log(' npx firegraph query find-edges --aUid Kj7vNq2mP9xR4wL1tY8s3 --axbType hasTask');\n console.log(' npx firegraph query search \"John Doe\"');\n console.log('');\n}\n\nfunction die(msg: string): never {\n console.error(msg);\n process.exit(1);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,47 @@
1
+ // src/codegen/index.ts
2
+ function pascalCase(s) {
3
+ return s.replace(
4
+ /(^|[^a-zA-Z0-9])([a-zA-Z])/g,
5
+ (_, _sep, ch) => ch.toUpperCase()
6
+ );
7
+ }
8
+ async function generateTypes(discovery, options = {}) {
9
+ const { compile } = await import("json-schema-to-typescript");
10
+ const { banner = true } = options;
11
+ const chunks = [];
12
+ if (banner) {
13
+ chunks.push(
14
+ "// Auto-generated by firegraph codegen \u2014 do not edit manually\n"
15
+ );
16
+ }
17
+ const sortedNodes = [...discovery.nodes.entries()].sort(
18
+ ([a], [b]) => a.localeCompare(b)
19
+ );
20
+ const sortedEdges = [...discovery.edges.entries()].sort(
21
+ ([a], [b]) => a.localeCompare(b)
22
+ );
23
+ for (const [name, entity] of sortedNodes) {
24
+ const typeName = `${pascalCase(name)}Data`;
25
+ const ts = await compile(entity.schema, typeName, {
26
+ bannerComment: "",
27
+ additionalProperties: false
28
+ });
29
+ chunks.push(ts.trim());
30
+ chunks.push("");
31
+ }
32
+ for (const [name, entity] of sortedEdges) {
33
+ const typeName = `${pascalCase(name)}EdgeData`;
34
+ const ts = await compile(entity.schema, typeName, {
35
+ bannerComment: "",
36
+ additionalProperties: false
37
+ });
38
+ chunks.push(ts.trim());
39
+ chunks.push("");
40
+ }
41
+ return chunks.join("\n").trimEnd() + "\n";
42
+ }
43
+
44
+ export {
45
+ generateTypes
46
+ };
47
+ //# sourceMappingURL=chunk-YLGXLEUE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/codegen/index.ts"],"sourcesContent":["/**\n * Code generation — produces TypeScript type definitions from JSON Schema\n * files discovered via the entity folder convention.\n *\n * Uses `json-schema-to-typescript` to compile each entity's `schema.json`\n * into a TypeScript interface.\n *\n * Naming convention:\n * - Nodes: `{PascalName}Data` (e.g. `TaskData`)\n * - Edges: `{PascalName}EdgeData` (e.g. `HasStepEdgeData`)\n */\n\nimport type { DiscoveryResult } from '../types.js';\n\nfunction pascalCase(s: string): string {\n return s.replace(/(^|[^a-zA-Z0-9])([a-zA-Z])/g, (_, _sep, ch) =>\n ch.toUpperCase(),\n );\n}\n\nexport interface CodegenOptions {\n /** Add banner comment at top of output. Defaults to true. */\n banner?: boolean;\n}\n\n/**\n * Generate TypeScript type definitions from a DiscoveryResult.\n * Returns the full file content as a string.\n */\nexport async function generateTypes(\n discovery: DiscoveryResult,\n options: CodegenOptions = {},\n): Promise<string> {\n // Lazy-load to avoid requiring this dep at runtime for non-codegen usage\n const { compile } = await import('json-schema-to-typescript');\n\n const { banner = true } = options;\n const chunks: string[] = [];\n\n if (banner) {\n chunks.push(\n '// Auto-generated by firegraph codegen — do not edit manually\\n',\n );\n }\n\n // Sort for deterministic output\n const sortedNodes = [...discovery.nodes.entries()].sort(([a], [b]) =>\n a.localeCompare(b),\n );\n const sortedEdges = [...discovery.edges.entries()].sort(([a], [b]) =>\n a.localeCompare(b),\n );\n\n for (const [name, entity] of sortedNodes) {\n const typeName = `${pascalCase(name)}Data`;\n const ts = await compile(entity.schema as any, typeName, {\n bannerComment: '',\n additionalProperties: false,\n });\n chunks.push(ts.trim());\n chunks.push('');\n }\n\n for (const [name, entity] of sortedEdges) {\n const typeName = `${pascalCase(name)}EdgeData`;\n const ts = await compile(entity.schema as any, typeName, {\n bannerComment: '',\n additionalProperties: false,\n });\n chunks.push(ts.trim());\n chunks.push('');\n }\n\n return chunks.join('\\n').trimEnd() + '\\n';\n}\n"],"mappings":";AAcA,SAAS,WAAW,GAAmB;AACrC,SAAO,EAAE;AAAA,IAAQ;AAAA,IAA+B,CAAC,GAAG,MAAM,OACxD,GAAG,YAAY;AAAA,EACjB;AACF;AAWA,eAAsB,cACpB,WACA,UAA0B,CAAC,GACV;AAEjB,QAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,2BAA2B;AAE5D,QAAM,EAAE,SAAS,KAAK,IAAI;AAC1B,QAAM,SAAmB,CAAC;AAE1B,MAAI,QAAQ;AACV,WAAO;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAGA,QAAM,cAAc,CAAC,GAAG,UAAU,MAAM,QAAQ,CAAC,EAAE;AAAA,IAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAC9D,EAAE,cAAc,CAAC;AAAA,EACnB;AACA,QAAM,cAAc,CAAC,GAAG,UAAU,MAAM,QAAQ,CAAC,EAAE;AAAA,IAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAC9D,EAAE,cAAc,CAAC;AAAA,EACnB;AAEA,aAAW,CAAC,MAAM,MAAM,KAAK,aAAa;AACxC,UAAM,WAAW,GAAG,WAAW,IAAI,CAAC;AACpC,UAAM,KAAK,MAAM,QAAQ,OAAO,QAAe,UAAU;AAAA,MACvD,eAAe;AAAA,MACf,sBAAsB;AAAA,IACxB,CAAC;AACD,WAAO,KAAK,GAAG,KAAK,CAAC;AACrB,WAAO,KAAK,EAAE;AAAA,EAChB;AAEA,aAAW,CAAC,MAAM,MAAM,KAAK,aAAa;AACxC,UAAM,WAAW,GAAG,WAAW,IAAI,CAAC;AACpC,UAAM,KAAK,MAAM,QAAQ,OAAO,QAAe,UAAU;AAAA,MACvD,eAAe;AAAA,MACf,sBAAsB;AAAA,IACxB,CAAC;AACD,WAAO,KAAK,GAAG,KAAK,CAAC;AACrB,WAAO,KAAK,EAAE;AAAA,EAChB;AAEA,SAAO,OAAO,KAAK,IAAI,EAAE,QAAQ,IAAI;AACvC;","names":[]}