@typicalday/firegraph 0.7.1 → 0.9.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 (63) hide show
  1. package/dist/backend.cjs +222 -0
  2. package/dist/backend.cjs.map +1 -0
  3. package/dist/backend.d.cts +121 -0
  4. package/dist/backend.d.ts +121 -0
  5. package/dist/backend.js +136 -0
  6. package/dist/backend.js.map +1 -0
  7. package/dist/chunk-5753Y42M.js +118 -0
  8. package/dist/chunk-5753Y42M.js.map +1 -0
  9. package/dist/chunk-EVUM6ORB.js +1575 -0
  10. package/dist/chunk-EVUM6ORB.js.map +1 -0
  11. package/dist/chunk-GLOVWKQH.js +94 -0
  12. package/dist/chunk-GLOVWKQH.js.map +1 -0
  13. package/dist/{chunk-KFA7G37W.js → chunk-SU4FNLC3.js} +32 -30
  14. package/dist/chunk-SU4FNLC3.js.map +1 -0
  15. package/dist/chunk-SZ6W4VAS.js +701 -0
  16. package/dist/chunk-SZ6W4VAS.js.map +1 -0
  17. package/dist/chunk-TYYPRVIE.js +57 -0
  18. package/dist/chunk-TYYPRVIE.js.map +1 -0
  19. package/dist/codegen/index.d.cts +25 -1
  20. package/dist/codegen/index.d.ts +25 -1
  21. package/dist/d1.cjs +2421 -0
  22. package/dist/d1.cjs.map +1 -0
  23. package/dist/d1.d.cts +54 -0
  24. package/dist/d1.d.ts +54 -0
  25. package/dist/d1.js +76 -0
  26. package/dist/d1.js.map +1 -0
  27. package/dist/do-sqlite.cjs +2424 -0
  28. package/dist/do-sqlite.cjs.map +1 -0
  29. package/dist/do-sqlite.d.cts +41 -0
  30. package/dist/do-sqlite.d.ts +41 -0
  31. package/dist/do-sqlite.js +79 -0
  32. package/dist/do-sqlite.js.map +1 -0
  33. package/dist/editor/client/assets/index-Bq2bfzeY.js +411 -0
  34. package/dist/editor/client/index.html +1 -1
  35. package/dist/editor/server/index.mjs +6524 -6355
  36. package/dist/index.cjs +2881 -2714
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.cts +259 -275
  39. package/dist/index.d.ts +259 -275
  40. package/dist/index.js +728 -2304
  41. package/dist/index.js.map +1 -1
  42. package/dist/query-client/index.cjs +30 -28
  43. package/dist/query-client/index.cjs.map +1 -1
  44. package/dist/query-client/index.d.cts +2 -2
  45. package/dist/query-client/index.d.ts +2 -2
  46. package/dist/query-client/index.js +1 -1
  47. package/dist/react.cjs +0 -1
  48. package/dist/react.cjs.map +1 -1
  49. package/dist/react.js +0 -1
  50. package/dist/react.js.map +1 -1
  51. package/dist/scope-path-BtajqNK5.d.ts +234 -0
  52. package/dist/scope-path-D2mNENJ-.d.cts +234 -0
  53. package/dist/serialization-ZZ7RSDRX.js +13 -0
  54. package/dist/serialization-ZZ7RSDRX.js.map +1 -0
  55. package/dist/svelte.cjs +0 -2
  56. package/dist/svelte.cjs.map +1 -1
  57. package/dist/svelte.js +0 -2
  58. package/dist/svelte.js.map +1 -1
  59. package/dist/{index-B9aodfYD.d.ts → types-DfWVTsMn.d.cts} +28 -26
  60. package/dist/{index-B9aodfYD.d.cts → types-DfWVTsMn.d.ts} +28 -26
  61. package/package.json +35 -1
  62. package/dist/chunk-KFA7G37W.js.map +0 -1
  63. package/dist/editor/client/assets/index-tyFcX6qG.js +0 -411
@@ -0,0 +1,234 @@
1
+ import { S as StoredGraphRecord, i as QueryFilter, y as QueryOptions, e as GraphReader, m as BulkOptions, C as CascadeResult, F as FindEdgesParams, o as BulkResult } from './types-DfWVTsMn.cjs';
2
+
3
+ declare class FiregraphError extends Error {
4
+ readonly code: string;
5
+ constructor(message: string, code: string);
6
+ }
7
+ declare class NodeNotFoundError extends FiregraphError {
8
+ constructor(uid: string);
9
+ }
10
+ declare class EdgeNotFoundError extends FiregraphError {
11
+ constructor(aUid: string, axbType: string, bUid: string);
12
+ }
13
+ declare class ValidationError extends FiregraphError {
14
+ readonly details?: unknown | undefined;
15
+ constructor(message: string, details?: unknown | undefined);
16
+ }
17
+ declare class RegistryViolationError extends FiregraphError {
18
+ constructor(aType: string, axbType: string, bType: string);
19
+ }
20
+ declare class InvalidQueryError extends FiregraphError {
21
+ constructor(message: string);
22
+ }
23
+ declare class TraversalError extends FiregraphError {
24
+ constructor(message: string);
25
+ }
26
+ declare class DynamicRegistryError extends FiregraphError {
27
+ constructor(message: string);
28
+ }
29
+ declare class QuerySafetyError extends FiregraphError {
30
+ constructor(message: string);
31
+ }
32
+ declare class RegistryScopeError extends FiregraphError {
33
+ constructor(aType: string, axbType: string, bType: string, scopePath: string, allowedIn: string[]);
34
+ }
35
+ declare class MigrationError extends FiregraphError {
36
+ constructor(message: string);
37
+ }
38
+ /**
39
+ * Thrown when a caller tries to perform an operation that would require
40
+ * atomicity across two physical storage backends — e.g. opening a routed
41
+ * subgraph client from inside a transaction callback. Cross-backend
42
+ * atomicity cannot be honoured by any of the underlying drivers (D1, DO
43
+ * SQLite, Firestore), so firegraph surfaces this as a typed error instead
44
+ * of silently confining the write to the base backend.
45
+ *
46
+ * Normally `TransactionBackend` and `BatchBackend` don't expose `subgraph()`
47
+ * at the type level, so this error is unreachable through well-typed code.
48
+ * It exists as a public catchable type for app code that needs to tolerate
49
+ * this case deliberately (e.g. dynamic code paths that bypass the type
50
+ * system) and as future-proofing if the interface ever grows a way to
51
+ * request a sub-scope inside a transaction.
52
+ */
53
+ declare class CrossBackendTransactionError extends FiregraphError {
54
+ constructor(message: string);
55
+ }
56
+
57
+ /**
58
+ * Backend abstraction for firegraph.
59
+ *
60
+ * `StorageBackend` is the single interface every storage driver implements.
61
+ * The Firestore backend wraps `@google-cloud/firestore`; the SQLite backend
62
+ * (shared by D1 and Durable Object SQLite) uses a parameterized SQL executor.
63
+ *
64
+ * `GraphClientImpl` and friends depend only on this interface — they have
65
+ * no direct knowledge of Firestore or SQLite.
66
+ */
67
+
68
+ /**
69
+ * Per-record write payload — backend-agnostic. Timestamps are not present;
70
+ * the backend supplies them via `serverTimestamp()` placeholders that it
71
+ * itself resolves at commit time.
72
+ */
73
+ interface WritableRecord {
74
+ aType: string;
75
+ aUid: string;
76
+ axbType: string;
77
+ bType: string;
78
+ bUid: string;
79
+ data: Record<string, unknown>;
80
+ /** Schema version (set by the writer when registry has migrations). */
81
+ v?: number;
82
+ }
83
+ /**
84
+ * Patch shape for `updateDoc`. Captures the two patterns that exist today:
85
+ * - `dataFields`: shallow merge under `data` (used by `updateNode`)
86
+ * - `replaceData`: full data replacement (used by migration write-back)
87
+ * - `v`: optional schema-version stamp
88
+ *
89
+ * `updatedAt` is always set by the backend.
90
+ */
91
+ interface UpdatePayload {
92
+ dataFields?: Record<string, unknown>;
93
+ replaceData?: Record<string, unknown>;
94
+ v?: number;
95
+ }
96
+ /**
97
+ * Read/write transaction adapter. Mirrors Firestore's transaction semantics:
98
+ * reads are snapshot-consistent; writes are issued inside the transaction
99
+ * and a rejection from any write aborts the surrounding `runTransaction`.
100
+ *
101
+ * Writes return `Promise<void>` so SQL drivers can surface row-level errors
102
+ * (constraint violations, malformed JSON paths) rather than swallowing them.
103
+ * Firestore implementations can resolve synchronously since the underlying
104
+ * `Transaction.set/update/delete` calls are themselves synchronous buffers.
105
+ */
106
+ interface TransactionBackend {
107
+ getDoc(docId: string): Promise<StoredGraphRecord | null>;
108
+ query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]>;
109
+ setDoc(docId: string, record: WritableRecord): Promise<void>;
110
+ updateDoc(docId: string, update: UpdatePayload): Promise<void>;
111
+ deleteDoc(docId: string): Promise<void>;
112
+ }
113
+ /**
114
+ * Atomic multi-write batch.
115
+ */
116
+ interface BatchBackend {
117
+ setDoc(docId: string, record: WritableRecord): void;
118
+ updateDoc(docId: string, update: UpdatePayload): void;
119
+ deleteDoc(docId: string): void;
120
+ commit(): Promise<void>;
121
+ }
122
+ /**
123
+ * The single storage abstraction.
124
+ *
125
+ * Each backend instance is scoped to a "graph location" — for Firestore
126
+ * that's a collection path; for SQLite it's a (table, scopePath) pair.
127
+ * `subgraph()` returns a child backend bound to a nested location.
128
+ */
129
+ interface StorageBackend {
130
+ /** Backend-internal location identifier (collection path or table name). */
131
+ readonly collectionPath: string;
132
+ /** Subgraph scope (empty string for root). */
133
+ readonly scopePath: string;
134
+ getDoc(docId: string): Promise<StoredGraphRecord | null>;
135
+ query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]>;
136
+ setDoc(docId: string, record: WritableRecord): Promise<void>;
137
+ updateDoc(docId: string, update: UpdatePayload): Promise<void>;
138
+ deleteDoc(docId: string): Promise<void>;
139
+ runTransaction<T>(fn: (tx: TransactionBackend) => Promise<T>): Promise<T>;
140
+ createBatch(): BatchBackend;
141
+ subgraph(parentNodeUid: string, name: string): StorageBackend;
142
+ removeNodeCascade(uid: string, reader: GraphReader, options?: BulkOptions): Promise<CascadeResult>;
143
+ bulkRemoveEdges(params: FindEdgesParams, reader: GraphReader, options?: BulkOptions): Promise<BulkResult>;
144
+ /**
145
+ * Find edges across all subgraphs sharing a given collection name.
146
+ * Optional — backends that can't support this should throw a clear error.
147
+ */
148
+ findEdgesGlobal?(params: FindEdgesParams, collectionName?: string): Promise<StoredGraphRecord[]>;
149
+ }
150
+
151
+ /**
152
+ * Storage-scope path utilities — materialized-path parsing helpers for the
153
+ * SQLite backend's `storageScope` string and for any custom backend that
154
+ * adopts the same encoding (e.g. a cross-DO routing layer that uses
155
+ * `storageScope` as a Durable Object name).
156
+ *
157
+ * **Storage-scope** (as produced by `SqliteBackendImpl`) interleaves parent
158
+ * UIDs with subgraph names:
159
+ *
160
+ * ```
161
+ * '' // root
162
+ * 'A/memories' // g.subgraph(A, 'memories')
163
+ * 'A/memories/B/context' // .subgraph(B, 'context') on the above
164
+ * ```
165
+ *
166
+ * The structure is the same as a Firestore collection path with the
167
+ * collection/doc segments reordered: each pair is `<uid>/<name>`, where
168
+ * `<uid>` is a node UID in the parent scope and `<name>` is the subgraph
169
+ * name. Use these helpers to decode that structure when building cross-
170
+ * backend routers (see `createRoutingBackend`).
171
+ *
172
+ * For Firestore paths (which begin with a collection segment), use
173
+ * `resolveAncestorCollection` / `isAncestorUid` from `./cross-graph.js`.
174
+ */
175
+ /**
176
+ * One segment of a materialized-path storage-scope — a `(uid, name)` pair
177
+ * produced by one `subgraph(uid, name)` call.
178
+ */
179
+ interface StorageScopeSegment {
180
+ /** Parent node UID at the enclosing scope. */
181
+ uid: string;
182
+ /** Subgraph name chosen by the caller (e.g. `'memories'`). */
183
+ name: string;
184
+ }
185
+ /**
186
+ * Parse a materialized-path storage-scope into its `(uid, name)` pairs.
187
+ *
188
+ * Returns `[]` for the root (`''`). Throws `Error('INVALID_SCOPE_PATH')`
189
+ * when the string has an odd number of segments (a corrupt path — every
190
+ * level contributes exactly two segments) or when any segment is empty.
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * parseStorageScope(''); // []
195
+ * parseStorageScope('A/memories'); // [{ uid: 'A', name: 'memories' }]
196
+ * parseStorageScope('A/memories/B/context'); // [{ uid: 'A', name: 'memories' }, { uid: 'B', name: 'context' }]
197
+ * ```
198
+ */
199
+ declare function parseStorageScope(scope: string): StorageScopeSegment[];
200
+ /**
201
+ * Resolve the ancestor **storage-scope** at which a given UID's node lives,
202
+ * by scanning a materialized-path storage-scope for that UID.
203
+ *
204
+ * Mirrors `resolveAncestorCollection()` from `./cross-graph.js` for
205
+ * Firestore paths, but operates on `storageScope` (no leading collection
206
+ * segment — segments are `<uid>/<name>` pairs).
207
+ *
208
+ * @returns The storage-scope at which the UID's node was added via
209
+ * `subgraph(uid, _)`, or `null` if the UID does not appear at a UID
210
+ * position in the path.
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * // Scope: 'A/memories/B/context'
215
+ * resolveAncestorScope('A/memories/B/context', 'A'); // '' (A was added at root)
216
+ * resolveAncestorScope('A/memories/B/context', 'B'); // 'A/memories'
217
+ * resolveAncestorScope('A/memories/B/context', 'X'); // null
218
+ * ```
219
+ */
220
+ declare function resolveAncestorScope(storageScope: string, uid: string): string | null;
221
+ /**
222
+ * Boolean shorthand for `resolveAncestorScope(scope, uid) !== null`.
223
+ */
224
+ declare function isAncestorScopeUid(storageScope: string, uid: string): boolean;
225
+ /**
226
+ * Join a parent storage-scope with a new `(uid, name)` pair, producing the
227
+ * storage-scope that `backend.subgraph(uid, name)` would use internally.
228
+ *
229
+ * This is the inverse of `parseStorageScope`'s per-segment semantics and is
230
+ * useful when computing DO names / shard keys from the router callback.
231
+ */
232
+ declare function appendStorageScope(parentScope: string, uid: string, name: string): string;
233
+
234
+ export { type BatchBackend as B, CrossBackendTransactionError as C, DynamicRegistryError as D, EdgeNotFoundError as E, FiregraphError as F, InvalidQueryError as I, MigrationError as M, NodeNotFoundError as N, QuerySafetyError as Q, RegistryScopeError as R, type StorageBackend as S, type TransactionBackend as T, type UpdatePayload as U, ValidationError as V, type WritableRecord as W, type StorageScopeSegment as a, appendStorageScope as b, RegistryViolationError as c, TraversalError as d, isAncestorScopeUid as i, parseStorageScope as p, resolveAncestorScope as r };
@@ -0,0 +1,13 @@
1
+ import {
2
+ SERIALIZATION_TAG,
3
+ deserializeFirestoreTypes,
4
+ isTaggedValue,
5
+ serializeFirestoreTypes
6
+ } from "./chunk-5753Y42M.js";
7
+ export {
8
+ SERIALIZATION_TAG,
9
+ deserializeFirestoreTypes,
10
+ isTaggedValue,
11
+ serializeFirestoreTypes
12
+ };
13
+ //# sourceMappingURL=serialization-ZZ7RSDRX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/svelte.cjs CHANGED
@@ -34,9 +34,7 @@ function wrapSvelte(Component, meta) {
34
34
  static viewName = meta.viewName;
35
35
  static description = meta.description;
36
36
  _data = {};
37
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
37
  _instance = null;
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
38
  _props = null;
41
39
  _mounted = false;
42
40
  set data(v) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/svelte.ts"],"sourcesContent":["/**\n * Svelte 5 adapter for firegraph views.\n *\n * Wraps a Svelte component into a Web Component (HTMLElement) that satisfies\n * the firegraph `ViewComponentClass` contract. Import from `firegraph/svelte`:\n *\n * @example\n * ```ts\n * import { wrapSvelte } from 'firegraph/svelte';\n * import TaskCard from './TaskCard.svelte';\n *\n * export default [\n * wrapSvelte(TaskCard, { viewName: 'card', description: 'Compact task card' }),\n * ];\n * ```\n */\n\nimport type { ViewComponentClass } from './views.js';\n\n/** Metadata required for every firegraph view. */\nexport interface ViewMeta {\n /** Short identifier (e.g. 'card', 'detail'). */\n viewName: string;\n /** Optional human-readable description. */\n description?: string;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyClass = { new (...args: any[]): any };\n\n/**\n * Get HTMLElement from the runtime environment.\n * Returns a base class for view elements — works in both browser and Node.js\n * (where HTMLElement may be shimmed by the editor server).\n */\nfunction getBaseClass(): AnyClass {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const g = globalThis as any;\n return g.HTMLElement ?? class {};\n}\n\n/**\n * Wrap a Svelte 5 component into a firegraph `ViewComponentClass`.\n *\n * The returned class extends `HTMLElement` and lazily imports `svelte` at\n * mount time. Svelte is resolved from the project's own `node_modules` —\n * firegraph does not bundle or depend on Svelte itself.\n *\n * The Svelte component should accept a `data` prop:\n * ```svelte\n * <script>\n * let { data } = $props();\n * </script>\n * ```\n *\n * Props are updated by mutating the props object — Svelte 5's reactivity\n * system picks up the changes automatically.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function wrapSvelte(Component: any, meta: ViewMeta): ViewComponentClass {\n const Base = getBaseClass();\n\n const Cls = class extends Base {\n static viewName = meta.viewName;\n static description = meta.description;\n\n _data: Record<string, unknown> = {};\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n _instance: any = null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n _props: any = null;\n _mounted = false;\n\n set data(v: Record<string, unknown>) {\n this._data = v;\n if (this._props) {\n // Svelte 5: mutating the props object triggers reactivity\n this._props.data = v;\n } else if (this._mounted) {\n this._mount();\n }\n }\n\n get data() {\n return this._data;\n }\n\n connectedCallback() {\n this._mounted = true;\n this._mount();\n }\n\n disconnectedCallback() {\n this._mounted = false;\n if (this._instance) {\n // Dynamic import — resolved from project's node_modules at runtime\n (Function('return import(\"svelte\")')() as Promise<{ unmount: Function }>).then(\n ({ unmount }) => {\n if (this._instance) {\n unmount(this._instance);\n this._instance = null;\n this._props = null;\n }\n },\n );\n }\n }\n\n async _mount() {\n // Dynamic import — resolved from project's node_modules at runtime\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const svelte: any = await (Function('return import(\"svelte\")')() as Promise<unknown>);\n if (!this._mounted) return; // disconnected while awaiting\n\n // Clean up previous instance\n if (this._instance) {\n svelte.unmount(this._instance);\n }\n\n // Svelte 5: pass a props object — mutating it later triggers re-renders\n this._props = { data: this._data };\n this._instance = svelte.mount(Component, {\n target: this,\n props: this._props,\n });\n }\n };\n\n return Cls as unknown as ViewComponentClass;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmCA,SAAS,eAAyB;AAEhC,QAAM,IAAI;AACV,SAAO,EAAE,eAAe,MAAM;AAAA,EAAC;AACjC;AAoBO,SAAS,WAAW,WAAgB,MAAoC;AAC7E,QAAM,OAAO,aAAa;AAE1B,QAAM,MAAM,cAAc,KAAK;AAAA,IAC7B,OAAO,WAAW,KAAK;AAAA,IACvB,OAAO,cAAc,KAAK;AAAA,IAE1B,QAAiC,CAAC;AAAA;AAAA,IAElC,YAAiB;AAAA;AAAA,IAEjB,SAAc;AAAA,IACd,WAAW;AAAA,IAEX,IAAI,KAAK,GAA4B;AACnC,WAAK,QAAQ;AACb,UAAI,KAAK,QAAQ;AAEf,aAAK,OAAO,OAAO;AAAA,MACrB,WAAW,KAAK,UAAU;AACxB,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA,IAEA,IAAI,OAAO;AACT,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,oBAAoB;AAClB,WAAK,WAAW;AAChB,WAAK,OAAO;AAAA,IACd;AAAA,IAEA,uBAAuB;AACrB,WAAK,WAAW;AAChB,UAAI,KAAK,WAAW;AAElB,QAAC,SAAS,yBAAyB,EAAE,EAAqC;AAAA,UACxE,CAAC,EAAE,QAAQ,MAAM;AACf,gBAAI,KAAK,WAAW;AAClB,sBAAQ,KAAK,SAAS;AACtB,mBAAK,YAAY;AACjB,mBAAK,SAAS;AAAA,YAChB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,SAAS;AAGb,YAAM,SAAc,MAAO,SAAS,yBAAyB,EAAE;AAC/D,UAAI,CAAC,KAAK,SAAU;AAGpB,UAAI,KAAK,WAAW;AAClB,eAAO,QAAQ,KAAK,SAAS;AAAA,MAC/B;AAGA,WAAK,SAAS,EAAE,MAAM,KAAK,MAAM;AACjC,WAAK,YAAY,OAAO,MAAM,WAAW;AAAA,QACvC,QAAQ;AAAA,QACR,OAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/svelte.ts"],"sourcesContent":["/**\n * Svelte 5 adapter for firegraph views.\n *\n * Wraps a Svelte component into a Web Component (HTMLElement) that satisfies\n * the firegraph `ViewComponentClass` contract. Import from `firegraph/svelte`:\n *\n * @example\n * ```ts\n * import { wrapSvelte } from 'firegraph/svelte';\n * import TaskCard from './TaskCard.svelte';\n *\n * export default [\n * wrapSvelte(TaskCard, { viewName: 'card', description: 'Compact task card' }),\n * ];\n * ```\n */\n\nimport type { ViewComponentClass } from './views.js';\n\n/** Metadata required for every firegraph view. */\nexport interface ViewMeta {\n /** Short identifier (e.g. 'card', 'detail'). */\n viewName: string;\n /** Optional human-readable description. */\n description?: string;\n}\n\ntype AnyClass = { new (...args: any[]): any };\n\n/**\n * Get HTMLElement from the runtime environment.\n * Returns a base class for view elements — works in both browser and Node.js\n * (where HTMLElement may be shimmed by the editor server).\n */\nfunction getBaseClass(): AnyClass {\n const g = globalThis as any;\n return g.HTMLElement ?? class {};\n}\n\n/**\n * Wrap a Svelte 5 component into a firegraph `ViewComponentClass`.\n *\n * The returned class extends `HTMLElement` and lazily imports `svelte` at\n * mount time. Svelte is resolved from the project's own `node_modules` —\n * firegraph does not bundle or depend on Svelte itself.\n *\n * The Svelte component should accept a `data` prop:\n * ```svelte\n * <script>\n * let { data } = $props();\n * </script>\n * ```\n *\n * Props are updated by mutating the props object — Svelte 5's reactivity\n * system picks up the changes automatically.\n */\n\nexport function wrapSvelte(Component: any, meta: ViewMeta): ViewComponentClass {\n const Base = getBaseClass();\n\n const Cls = class extends Base {\n static viewName = meta.viewName;\n static description = meta.description;\n\n _data: Record<string, unknown> = {};\n\n _instance: any = null;\n\n _props: any = null;\n _mounted = false;\n\n set data(v: Record<string, unknown>) {\n this._data = v;\n if (this._props) {\n // Svelte 5: mutating the props object triggers reactivity\n this._props.data = v;\n } else if (this._mounted) {\n this._mount();\n }\n }\n\n get data() {\n return this._data;\n }\n\n connectedCallback() {\n this._mounted = true;\n this._mount();\n }\n\n disconnectedCallback() {\n this._mounted = false;\n if (this._instance) {\n // Dynamic import — resolved from project's node_modules at runtime\n (Function('return import(\"svelte\")')() as Promise<{ unmount: Function }>).then(\n ({ unmount }) => {\n if (this._instance) {\n unmount(this._instance);\n this._instance = null;\n this._props = null;\n }\n },\n );\n }\n }\n\n async _mount() {\n // Dynamic import — resolved from project's node_modules at runtime\n\n const svelte: any = await (Function('return import(\"svelte\")')() as Promise<unknown>);\n if (!this._mounted) return; // disconnected while awaiting\n\n // Clean up previous instance\n if (this._instance) {\n svelte.unmount(this._instance);\n }\n\n // Svelte 5: pass a props object — mutating it later triggers re-renders\n this._props = { data: this._data };\n this._instance = svelte.mount(Component, {\n target: this,\n props: this._props,\n });\n }\n };\n\n return Cls as unknown as ViewComponentClass;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkCA,SAAS,eAAyB;AAChC,QAAM,IAAI;AACV,SAAO,EAAE,eAAe,MAAM;AAAA,EAAC;AACjC;AAoBO,SAAS,WAAW,WAAgB,MAAoC;AAC7E,QAAM,OAAO,aAAa;AAE1B,QAAM,MAAM,cAAc,KAAK;AAAA,IAC7B,OAAO,WAAW,KAAK;AAAA,IACvB,OAAO,cAAc,KAAK;AAAA,IAE1B,QAAiC,CAAC;AAAA,IAElC,YAAiB;AAAA,IAEjB,SAAc;AAAA,IACd,WAAW;AAAA,IAEX,IAAI,KAAK,GAA4B;AACnC,WAAK,QAAQ;AACb,UAAI,KAAK,QAAQ;AAEf,aAAK,OAAO,OAAO;AAAA,MACrB,WAAW,KAAK,UAAU;AACxB,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA,IAEA,IAAI,OAAO;AACT,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,oBAAoB;AAClB,WAAK,WAAW;AAChB,WAAK,OAAO;AAAA,IACd;AAAA,IAEA,uBAAuB;AACrB,WAAK,WAAW;AAChB,UAAI,KAAK,WAAW;AAElB,QAAC,SAAS,yBAAyB,EAAE,EAAqC;AAAA,UACxE,CAAC,EAAE,QAAQ,MAAM;AACf,gBAAI,KAAK,WAAW;AAClB,sBAAQ,KAAK,SAAS;AACtB,mBAAK,YAAY;AACjB,mBAAK,SAAS;AAAA,YAChB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,SAAS;AAGb,YAAM,SAAc,MAAO,SAAS,yBAAyB,EAAE;AAC/D,UAAI,CAAC,KAAK,SAAU;AAGpB,UAAI,KAAK,WAAW;AAClB,eAAO,QAAQ,KAAK,SAAS;AAAA,MAC/B;AAGA,WAAK,SAAS,EAAE,MAAM,KAAK,MAAM;AACjC,WAAK,YAAY,OAAO,MAAM,WAAW;AAAA,QACvC,QAAQ;AAAA,QACR,OAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
package/dist/svelte.js CHANGED
@@ -10,9 +10,7 @@ function wrapSvelte(Component, meta) {
10
10
  static viewName = meta.viewName;
11
11
  static description = meta.description;
12
12
  _data = {};
13
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
13
  _instance = null;
15
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
14
  _props = null;
17
15
  _mounted = false;
18
16
  set data(v) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/svelte.ts"],"sourcesContent":["/**\n * Svelte 5 adapter for firegraph views.\n *\n * Wraps a Svelte component into a Web Component (HTMLElement) that satisfies\n * the firegraph `ViewComponentClass` contract. Import from `firegraph/svelte`:\n *\n * @example\n * ```ts\n * import { wrapSvelte } from 'firegraph/svelte';\n * import TaskCard from './TaskCard.svelte';\n *\n * export default [\n * wrapSvelte(TaskCard, { viewName: 'card', description: 'Compact task card' }),\n * ];\n * ```\n */\n\nimport type { ViewComponentClass } from './views.js';\n\n/** Metadata required for every firegraph view. */\nexport interface ViewMeta {\n /** Short identifier (e.g. 'card', 'detail'). */\n viewName: string;\n /** Optional human-readable description. */\n description?: string;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyClass = { new (...args: any[]): any };\n\n/**\n * Get HTMLElement from the runtime environment.\n * Returns a base class for view elements — works in both browser and Node.js\n * (where HTMLElement may be shimmed by the editor server).\n */\nfunction getBaseClass(): AnyClass {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const g = globalThis as any;\n return g.HTMLElement ?? class {};\n}\n\n/**\n * Wrap a Svelte 5 component into a firegraph `ViewComponentClass`.\n *\n * The returned class extends `HTMLElement` and lazily imports `svelte` at\n * mount time. Svelte is resolved from the project's own `node_modules` —\n * firegraph does not bundle or depend on Svelte itself.\n *\n * The Svelte component should accept a `data` prop:\n * ```svelte\n * <script>\n * let { data } = $props();\n * </script>\n * ```\n *\n * Props are updated by mutating the props object — Svelte 5's reactivity\n * system picks up the changes automatically.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function wrapSvelte(Component: any, meta: ViewMeta): ViewComponentClass {\n const Base = getBaseClass();\n\n const Cls = class extends Base {\n static viewName = meta.viewName;\n static description = meta.description;\n\n _data: Record<string, unknown> = {};\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n _instance: any = null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n _props: any = null;\n _mounted = false;\n\n set data(v: Record<string, unknown>) {\n this._data = v;\n if (this._props) {\n // Svelte 5: mutating the props object triggers reactivity\n this._props.data = v;\n } else if (this._mounted) {\n this._mount();\n }\n }\n\n get data() {\n return this._data;\n }\n\n connectedCallback() {\n this._mounted = true;\n this._mount();\n }\n\n disconnectedCallback() {\n this._mounted = false;\n if (this._instance) {\n // Dynamic import — resolved from project's node_modules at runtime\n (Function('return import(\"svelte\")')() as Promise<{ unmount: Function }>).then(\n ({ unmount }) => {\n if (this._instance) {\n unmount(this._instance);\n this._instance = null;\n this._props = null;\n }\n },\n );\n }\n }\n\n async _mount() {\n // Dynamic import — resolved from project's node_modules at runtime\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const svelte: any = await (Function('return import(\"svelte\")')() as Promise<unknown>);\n if (!this._mounted) return; // disconnected while awaiting\n\n // Clean up previous instance\n if (this._instance) {\n svelte.unmount(this._instance);\n }\n\n // Svelte 5: pass a props object — mutating it later triggers re-renders\n this._props = { data: this._data };\n this._instance = svelte.mount(Component, {\n target: this,\n props: this._props,\n });\n }\n };\n\n return Cls as unknown as ViewComponentClass;\n}\n"],"mappings":";AAmCA,SAAS,eAAyB;AAEhC,QAAM,IAAI;AACV,SAAO,EAAE,eAAe,MAAM;AAAA,EAAC;AACjC;AAoBO,SAAS,WAAW,WAAgB,MAAoC;AAC7E,QAAM,OAAO,aAAa;AAE1B,QAAM,MAAM,cAAc,KAAK;AAAA,IAC7B,OAAO,WAAW,KAAK;AAAA,IACvB,OAAO,cAAc,KAAK;AAAA,IAE1B,QAAiC,CAAC;AAAA;AAAA,IAElC,YAAiB;AAAA;AAAA,IAEjB,SAAc;AAAA,IACd,WAAW;AAAA,IAEX,IAAI,KAAK,GAA4B;AACnC,WAAK,QAAQ;AACb,UAAI,KAAK,QAAQ;AAEf,aAAK,OAAO,OAAO;AAAA,MACrB,WAAW,KAAK,UAAU;AACxB,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA,IAEA,IAAI,OAAO;AACT,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,oBAAoB;AAClB,WAAK,WAAW;AAChB,WAAK,OAAO;AAAA,IACd;AAAA,IAEA,uBAAuB;AACrB,WAAK,WAAW;AAChB,UAAI,KAAK,WAAW;AAElB,QAAC,SAAS,yBAAyB,EAAE,EAAqC;AAAA,UACxE,CAAC,EAAE,QAAQ,MAAM;AACf,gBAAI,KAAK,WAAW;AAClB,sBAAQ,KAAK,SAAS;AACtB,mBAAK,YAAY;AACjB,mBAAK,SAAS;AAAA,YAChB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,SAAS;AAGb,YAAM,SAAc,MAAO,SAAS,yBAAyB,EAAE;AAC/D,UAAI,CAAC,KAAK,SAAU;AAGpB,UAAI,KAAK,WAAW;AAClB,eAAO,QAAQ,KAAK,SAAS;AAAA,MAC/B;AAGA,WAAK,SAAS,EAAE,MAAM,KAAK,MAAM;AACjC,WAAK,YAAY,OAAO,MAAM,WAAW;AAAA,QACvC,QAAQ;AAAA,QACR,OAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/svelte.ts"],"sourcesContent":["/**\n * Svelte 5 adapter for firegraph views.\n *\n * Wraps a Svelte component into a Web Component (HTMLElement) that satisfies\n * the firegraph `ViewComponentClass` contract. Import from `firegraph/svelte`:\n *\n * @example\n * ```ts\n * import { wrapSvelte } from 'firegraph/svelte';\n * import TaskCard from './TaskCard.svelte';\n *\n * export default [\n * wrapSvelte(TaskCard, { viewName: 'card', description: 'Compact task card' }),\n * ];\n * ```\n */\n\nimport type { ViewComponentClass } from './views.js';\n\n/** Metadata required for every firegraph view. */\nexport interface ViewMeta {\n /** Short identifier (e.g. 'card', 'detail'). */\n viewName: string;\n /** Optional human-readable description. */\n description?: string;\n}\n\ntype AnyClass = { new (...args: any[]): any };\n\n/**\n * Get HTMLElement from the runtime environment.\n * Returns a base class for view elements — works in both browser and Node.js\n * (where HTMLElement may be shimmed by the editor server).\n */\nfunction getBaseClass(): AnyClass {\n const g = globalThis as any;\n return g.HTMLElement ?? class {};\n}\n\n/**\n * Wrap a Svelte 5 component into a firegraph `ViewComponentClass`.\n *\n * The returned class extends `HTMLElement` and lazily imports `svelte` at\n * mount time. Svelte is resolved from the project's own `node_modules` —\n * firegraph does not bundle or depend on Svelte itself.\n *\n * The Svelte component should accept a `data` prop:\n * ```svelte\n * <script>\n * let { data } = $props();\n * </script>\n * ```\n *\n * Props are updated by mutating the props object — Svelte 5's reactivity\n * system picks up the changes automatically.\n */\n\nexport function wrapSvelte(Component: any, meta: ViewMeta): ViewComponentClass {\n const Base = getBaseClass();\n\n const Cls = class extends Base {\n static viewName = meta.viewName;\n static description = meta.description;\n\n _data: Record<string, unknown> = {};\n\n _instance: any = null;\n\n _props: any = null;\n _mounted = false;\n\n set data(v: Record<string, unknown>) {\n this._data = v;\n if (this._props) {\n // Svelte 5: mutating the props object triggers reactivity\n this._props.data = v;\n } else if (this._mounted) {\n this._mount();\n }\n }\n\n get data() {\n return this._data;\n }\n\n connectedCallback() {\n this._mounted = true;\n this._mount();\n }\n\n disconnectedCallback() {\n this._mounted = false;\n if (this._instance) {\n // Dynamic import — resolved from project's node_modules at runtime\n (Function('return import(\"svelte\")')() as Promise<{ unmount: Function }>).then(\n ({ unmount }) => {\n if (this._instance) {\n unmount(this._instance);\n this._instance = null;\n this._props = null;\n }\n },\n );\n }\n }\n\n async _mount() {\n // Dynamic import — resolved from project's node_modules at runtime\n\n const svelte: any = await (Function('return import(\"svelte\")')() as Promise<unknown>);\n if (!this._mounted) return; // disconnected while awaiting\n\n // Clean up previous instance\n if (this._instance) {\n svelte.unmount(this._instance);\n }\n\n // Svelte 5: pass a props object — mutating it later triggers re-renders\n this._props = { data: this._data };\n this._instance = svelte.mount(Component, {\n target: this,\n props: this._props,\n });\n }\n };\n\n return Cls as unknown as ViewComponentClass;\n}\n"],"mappings":";AAkCA,SAAS,eAAyB;AAChC,QAAM,IAAI;AACV,SAAO,EAAE,eAAe,MAAM;AAAA,EAAC;AACjC;AAoBO,SAAS,WAAW,WAAgB,MAAoC;AAC7E,QAAM,OAAO,aAAa;AAE1B,QAAM,MAAM,cAAc,KAAK;AAAA,IAC7B,OAAO,WAAW,KAAK;AAAA,IACvB,OAAO,cAAc,KAAK;AAAA,IAE1B,QAAiC,CAAC;AAAA,IAElC,YAAiB;AAAA,IAEjB,SAAc;AAAA,IACd,WAAW;AAAA,IAEX,IAAI,KAAK,GAA4B;AACnC,WAAK,QAAQ;AACb,UAAI,KAAK,QAAQ;AAEf,aAAK,OAAO,OAAO;AAAA,MACrB,WAAW,KAAK,UAAU;AACxB,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA,IAEA,IAAI,OAAO;AACT,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,oBAAoB;AAClB,WAAK,WAAW;AAChB,WAAK,OAAO;AAAA,IACd;AAAA,IAEA,uBAAuB;AACrB,WAAK,WAAW;AAChB,UAAI,KAAK,WAAW;AAElB,QAAC,SAAS,yBAAyB,EAAE,EAAqC;AAAA,UACxE,CAAC,EAAE,QAAQ,MAAM;AACf,gBAAI,KAAK,WAAW;AAClB,sBAAQ,KAAK,SAAS;AACtB,mBAAK,YAAY;AACjB,mBAAK,SAAS;AAAA,YAChB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,SAAS;AAGb,YAAM,SAAc,MAAO,SAAS,yBAAyB,EAAE;AAC/D,UAAI,CAAC,KAAK,SAAU;AAGpB,UAAI,KAAK,WAAW;AAClB,eAAO,QAAQ,KAAK,SAAS;AAAA,MAC/B;AAGA,WAAK,SAAS,EAAE,MAAM,KAAK,MAAM;AACjC,WAAK,YAAY,OAAO,MAAM,WAAW;AAAA,QACvC,QAAQ;AAAA,QACR,OAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
@@ -1,4 +1,4 @@
1
- import { Timestamp, FieldValue, WhereFilterOp } from '@google-cloud/firestore';
1
+ import { WhereFilterOp, Timestamp, FieldValue } from '@google-cloud/firestore';
2
2
 
3
3
  /**
4
4
  * Firegraph Configuration — project-level config file support.
@@ -16,6 +16,7 @@ import { Timestamp, FieldValue, WhereFilterOp } from '@google-cloud/firestore';
16
16
  * });
17
17
  * ```
18
18
  */
19
+
19
20
  /** Display contexts where views can appear. */
20
21
  type ViewContext = 'listing' | 'detail' | 'inline';
21
22
  /** View resolution configuration for a single entity type. */
@@ -104,6 +105,24 @@ declare function defineConfig(config: FiregraphConfig): FiregraphConfig;
104
105
  */
105
106
  declare function resolveView(resolverConfig: ViewResolverConfig | undefined, availableViewNames: string[], context?: ViewContext): string;
106
107
 
108
+ /**
109
+ * Backend-agnostic timestamp.
110
+ *
111
+ * Structurally compatible with `@google-cloud/firestore`'s `Timestamp` so
112
+ * that records returned by either the Firestore or SQLite backend can be
113
+ * consumed through the same `StoredGraphRecord` shape.
114
+ *
115
+ * Firestore's native `Timestamp` already satisfies this interface, so
116
+ * existing Firestore consumers see no behavior change. The SQLite backend
117
+ * returns instances of `GraphTimestampImpl` which also satisfies it.
118
+ */
119
+ interface GraphTimestamp {
120
+ readonly seconds: number;
121
+ readonly nanoseconds: number;
122
+ toDate(): Date;
123
+ toMillis(): number;
124
+ }
125
+
107
126
  interface GraphRecord {
108
127
  aType: string;
109
128
  aUid: string;
@@ -123,8 +142,13 @@ interface StoredGraphRecord {
123
142
  bType: string;
124
143
  bUid: string;
125
144
  data: Record<string, unknown>;
126
- createdAt: Timestamp;
127
- updatedAt: Timestamp;
145
+ /**
146
+ * Backend-agnostic timestamp. Firestore returns its native `Timestamp`
147
+ * (which structurally satisfies `GraphTimestamp`); the SQLite backends
148
+ * return a `GraphTimestampImpl` instance.
149
+ */
150
+ createdAt: GraphTimestamp;
151
+ updatedAt: GraphTimestamp;
128
152
  /** Schema version — set automatically when the registry entry has migrations. */
129
153
  v?: number;
130
154
  }
@@ -618,26 +642,4 @@ interface CascadeResult extends BulkResult {
618
642
  nodeDeleted: boolean;
619
643
  }
620
644
 
621
- /**
622
- * Code generation — produces TypeScript type definitions from JSON Schema
623
- * files discovered via the entity folder convention.
624
- *
625
- * Uses `json-schema-to-typescript` to compile each entity's `schema.json`
626
- * into a TypeScript interface.
627
- *
628
- * Naming convention:
629
- * - Nodes: `{PascalName}Data` (e.g. `TaskData`)
630
- * - Edges: `{PascalName}EdgeData` (e.g. `HasStepEdgeData`)
631
- */
632
-
633
- interface CodegenOptions {
634
- /** Add banner comment at top of output. Defaults to true. */
635
- banner?: boolean;
636
- }
637
- /**
638
- * Generate TypeScript type definitions from a DiscoveryResult.
639
- * Returns the full file content as a string.
640
- */
641
- declare function generateTypes(discovery: DiscoveryResult, options?: CodegenOptions): Promise<string>;
642
-
643
- export { type ScanProtection as A, type BulkBatchError as B, type CascadeResult as C, type DynamicRegistryConfig as D, type EdgeTopology as E, type FindEdgesParams as F, type GraphClientOptions as G, type HopDefinition as H, type TraversalOptions as I, type TraversalResult as J, type ViewDefaultsConfig as K, type ViewResolverConfig as L, type MigrationExecutor as M, type NodeTypeData as N, defineConfig as O, generateTypes as P, type QueryPlan as Q, type RegistryEntry as R, type StoredMigrationStep as S, type TraversalBuilder as T, resolveView as U, type ViewContext as V, type WhereClause as W, type DynamicGraphClient as a, type GraphClient as b, type GraphRegistry as c, type DiscoveryResult as d, type GraphReader as e, type GraphRecord as f, type FindNodesParams as g, type MigrationFn as h, type MigrationStep as i, type StoredGraphRecord as j, type MigrationWriteBack as k, type QueryFilter as l, type BulkOptions as m, type BulkProgress as n, type BulkResult as o, type CodegenOptions as p, type DefineTypeOptions as q, type DiscoveredEntity as r, type EdgeTypeData as s, type FiregraphConfig as t, type GraphBatch as u, type GraphTransaction as v, type GraphWriter as w, type HopResult as x, type QueryMode as y, type QueryOptions as z };
645
+ export { type TraversalOptions as A, type BulkBatchError as B, type CascadeResult as C, type DynamicRegistryConfig as D, type EdgeTopology as E, type FindEdgesParams as F, type GraphClientOptions as G, type HopDefinition as H, type TraversalResult as I, type ViewDefaultsConfig as J, type ViewResolverConfig as K, defineConfig as L, type MigrationExecutor as M, type NodeTypeData as N, resolveView as O, type QueryPlan as Q, type RegistryEntry as R, type StoredGraphRecord as S, type TraversalBuilder as T, type ViewContext as V, type WhereClause as W, type DynamicGraphClient as a, type GraphClient as b, type DiscoveryResult as c, type GraphRegistry as d, type GraphReader as e, type MigrationWriteBack as f, type MigrationStep as g, type FindNodesParams as h, type QueryFilter as i, type GraphRecord as j, type MigrationFn as k, type StoredMigrationStep as l, type BulkOptions as m, type BulkProgress as n, type BulkResult as o, type DefineTypeOptions as p, type DiscoveredEntity as q, type EdgeTypeData as r, type FiregraphConfig as s, type GraphBatch as t, type GraphTransaction as u, type GraphWriter as v, type HopResult as w, type QueryMode as x, type QueryOptions as y, type ScanProtection as z };
@@ -1,4 +1,4 @@
1
- import { Timestamp, FieldValue, WhereFilterOp } from '@google-cloud/firestore';
1
+ import { WhereFilterOp, Timestamp, FieldValue } from '@google-cloud/firestore';
2
2
 
3
3
  /**
4
4
  * Firegraph Configuration — project-level config file support.
@@ -16,6 +16,7 @@ import { Timestamp, FieldValue, WhereFilterOp } from '@google-cloud/firestore';
16
16
  * });
17
17
  * ```
18
18
  */
19
+
19
20
  /** Display contexts where views can appear. */
20
21
  type ViewContext = 'listing' | 'detail' | 'inline';
21
22
  /** View resolution configuration for a single entity type. */
@@ -104,6 +105,24 @@ declare function defineConfig(config: FiregraphConfig): FiregraphConfig;
104
105
  */
105
106
  declare function resolveView(resolverConfig: ViewResolverConfig | undefined, availableViewNames: string[], context?: ViewContext): string;
106
107
 
108
+ /**
109
+ * Backend-agnostic timestamp.
110
+ *
111
+ * Structurally compatible with `@google-cloud/firestore`'s `Timestamp` so
112
+ * that records returned by either the Firestore or SQLite backend can be
113
+ * consumed through the same `StoredGraphRecord` shape.
114
+ *
115
+ * Firestore's native `Timestamp` already satisfies this interface, so
116
+ * existing Firestore consumers see no behavior change. The SQLite backend
117
+ * returns instances of `GraphTimestampImpl` which also satisfies it.
118
+ */
119
+ interface GraphTimestamp {
120
+ readonly seconds: number;
121
+ readonly nanoseconds: number;
122
+ toDate(): Date;
123
+ toMillis(): number;
124
+ }
125
+
107
126
  interface GraphRecord {
108
127
  aType: string;
109
128
  aUid: string;
@@ -123,8 +142,13 @@ interface StoredGraphRecord {
123
142
  bType: string;
124
143
  bUid: string;
125
144
  data: Record<string, unknown>;
126
- createdAt: Timestamp;
127
- updatedAt: Timestamp;
145
+ /**
146
+ * Backend-agnostic timestamp. Firestore returns its native `Timestamp`
147
+ * (which structurally satisfies `GraphTimestamp`); the SQLite backends
148
+ * return a `GraphTimestampImpl` instance.
149
+ */
150
+ createdAt: GraphTimestamp;
151
+ updatedAt: GraphTimestamp;
128
152
  /** Schema version — set automatically when the registry entry has migrations. */
129
153
  v?: number;
130
154
  }
@@ -618,26 +642,4 @@ interface CascadeResult extends BulkResult {
618
642
  nodeDeleted: boolean;
619
643
  }
620
644
 
621
- /**
622
- * Code generation — produces TypeScript type definitions from JSON Schema
623
- * files discovered via the entity folder convention.
624
- *
625
- * Uses `json-schema-to-typescript` to compile each entity's `schema.json`
626
- * into a TypeScript interface.
627
- *
628
- * Naming convention:
629
- * - Nodes: `{PascalName}Data` (e.g. `TaskData`)
630
- * - Edges: `{PascalName}EdgeData` (e.g. `HasStepEdgeData`)
631
- */
632
-
633
- interface CodegenOptions {
634
- /** Add banner comment at top of output. Defaults to true. */
635
- banner?: boolean;
636
- }
637
- /**
638
- * Generate TypeScript type definitions from a DiscoveryResult.
639
- * Returns the full file content as a string.
640
- */
641
- declare function generateTypes(discovery: DiscoveryResult, options?: CodegenOptions): Promise<string>;
642
-
643
- export { type ScanProtection as A, type BulkBatchError as B, type CascadeResult as C, type DynamicRegistryConfig as D, type EdgeTopology as E, type FindEdgesParams as F, type GraphClientOptions as G, type HopDefinition as H, type TraversalOptions as I, type TraversalResult as J, type ViewDefaultsConfig as K, type ViewResolverConfig as L, type MigrationExecutor as M, type NodeTypeData as N, defineConfig as O, generateTypes as P, type QueryPlan as Q, type RegistryEntry as R, type StoredMigrationStep as S, type TraversalBuilder as T, resolveView as U, type ViewContext as V, type WhereClause as W, type DynamicGraphClient as a, type GraphClient as b, type GraphRegistry as c, type DiscoveryResult as d, type GraphReader as e, type GraphRecord as f, type FindNodesParams as g, type MigrationFn as h, type MigrationStep as i, type StoredGraphRecord as j, type MigrationWriteBack as k, type QueryFilter as l, type BulkOptions as m, type BulkProgress as n, type BulkResult as o, type CodegenOptions as p, type DefineTypeOptions as q, type DiscoveredEntity as r, type EdgeTypeData as s, type FiregraphConfig as t, type GraphBatch as u, type GraphTransaction as v, type GraphWriter as w, type HopResult as x, type QueryMode as y, type QueryOptions as z };
645
+ export { type TraversalOptions as A, type BulkBatchError as B, type CascadeResult as C, type DynamicRegistryConfig as D, type EdgeTopology as E, type FindEdgesParams as F, type GraphClientOptions as G, type HopDefinition as H, type TraversalResult as I, type ViewDefaultsConfig as J, type ViewResolverConfig as K, defineConfig as L, type MigrationExecutor as M, type NodeTypeData as N, resolveView as O, type QueryPlan as Q, type RegistryEntry as R, type StoredGraphRecord as S, type TraversalBuilder as T, type ViewContext as V, type WhereClause as W, type DynamicGraphClient as a, type GraphClient as b, type DiscoveryResult as c, type GraphRegistry as d, type GraphReader as e, type MigrationWriteBack as f, type MigrationStep as g, type FindNodesParams as h, type QueryFilter as i, type GraphRecord as j, type MigrationFn as k, type StoredMigrationStep as l, type BulkOptions as m, type BulkProgress as n, type BulkResult as o, type DefineTypeOptions as p, type DiscoveredEntity as q, type EdgeTypeData as r, type FiregraphConfig as s, type GraphBatch as t, type GraphTransaction as u, type GraphWriter as v, type HopResult as w, type QueryMode as x, type QueryOptions as y, type ScanProtection as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typicalday/firegraph",
3
- "version": "0.7.1",
3
+ "version": "0.9.0",
4
4
  "description": "Generic Firestore adjacency graph client with smart query planning",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -46,6 +46,36 @@
46
46
  "types": "./dist/query-client/index.d.cts",
47
47
  "default": "./dist/query-client/index.cjs"
48
48
  }
49
+ },
50
+ "./d1": {
51
+ "import": {
52
+ "types": "./dist/d1.d.ts",
53
+ "default": "./dist/d1.js"
54
+ },
55
+ "require": {
56
+ "types": "./dist/d1.d.cts",
57
+ "default": "./dist/d1.cjs"
58
+ }
59
+ },
60
+ "./do-sqlite": {
61
+ "import": {
62
+ "types": "./dist/do-sqlite.d.ts",
63
+ "default": "./dist/do-sqlite.js"
64
+ },
65
+ "require": {
66
+ "types": "./dist/do-sqlite.d.cts",
67
+ "default": "./dist/do-sqlite.cjs"
68
+ }
69
+ },
70
+ "./backend": {
71
+ "import": {
72
+ "types": "./dist/backend.d.ts",
73
+ "default": "./dist/backend.js"
74
+ },
75
+ "require": {
76
+ "types": "./dist/backend.d.cts",
77
+ "default": "./dist/backend.cjs"
78
+ }
49
79
  }
50
80
  },
51
81
  "bin": {
@@ -72,6 +102,7 @@
72
102
  "test": "vitest run",
73
103
  "test:unit": "vitest run tests/unit/",
74
104
  "test:integration": "vitest run tests/integration/",
105
+ "test:sqlite:integration": "BACKEND=sqlite vitest run tests/integration/",
75
106
  "test:emulator": "bash tests/scripts/test-with-emulator.sh",
76
107
  "test:emulator:unit": "bash tests/scripts/test-with-emulator.sh tests/unit/",
77
108
  "test:emulator:integration": "bash tests/scripts/test-with-emulator.sh tests/integration/",
@@ -111,11 +142,14 @@
111
142
  "@commitlint/cli": "^20.5.0",
112
143
  "@commitlint/config-conventional": "^20.5.0",
113
144
  "@google-cloud/firestore": "^8.3.0",
145
+ "@types/better-sqlite3": "^7.6.13",
114
146
  "@typescript-eslint/eslint-plugin": "^8.57.2",
115
147
  "@typescript-eslint/parser": "^8.57.2",
116
148
  "@vitest/coverage-v8": "^3.0.0",
149
+ "better-sqlite3": "^12.9.0",
117
150
  "eslint": "^10.1.0",
118
151
  "eslint-config-prettier": "^10.1.8",
152
+ "eslint-plugin-react-hooks": "^7.1.0",
119
153
  "eslint-plugin-simple-import-sort": "^12.1.1",
120
154
  "husky": "^9.1.7",
121
155
  "json-schema-to-typescript": "^15.0.4",
@@ -1 +0,0 @@
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":";AAAA,OAAO,UAAU;;;ACEV,SAAS,gBAAgB,GAA4D;AAC1F,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,MAAwB,EAAE,MAAM,EAAE,OAAiB,KAAK,EAAE,KAAe;AAC/E,QAAM,OAAO,EAAE;AACf,MAAI,QAAQ,OAAO,SAAS,YAAY,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AACpE,QAAI,OAAO;AAAA,EACb;AACA,SAAO;AACT;AAEO,SAAS,cAAc,GAA0D;AACtF,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,MAAsB;AAAA,IAC1B,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,UAAU,EAAE;AAAA,IACZ,QAAQ,EAAE;AAAA,IACV,OAAO,EAAE;AAAA,EACX;AACA,QAAM,OAAO,EAAE;AACf,MAAI,QAAQ,OAAO,SAAS,YAAY,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AACpE,QAAI,OAAO;AAAA,EACb;AACA,SAAO;AACT;;;AC1BA,SAAS,oBAAoB;AAC7B,SAAS,YAAY;AAErB,IAAM,eAAe,CAAC,uBAAuB,uBAAuB,sBAAsB;AAC1F,IAAM,eAAe;AAMd,SAAS,eAAe,KAAsB;AACnD,QAAM,MAAM,OAAO,QAAQ,IAAI;AAC/B,aAAW,QAAQ,cAAc;AAC/B,QAAI;AACF,YAAM,UAAU,aAAa,KAAK,KAAK,IAAI,GAAG,MAAM;AACpD,YAAM,cAAc,QAAQ,MAAM,yBAAyB,IAAI,CAAC,KAAK;AACrE,YAAM,YAAY,YAAY,MAAM,kBAAkB;AACtD,UAAI,UAAW,QAAO,SAAS,UAAU,CAAC,GAAG,EAAE;AAAA,IACjD,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AFEO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YACE,SACgB,MAChB;AACA,UAAM,OAAO;AAFG;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAIA,SAAS,cAAc,OAAgB,MAAuC;AAC5E,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AACnD,UAAM,IAAI,iBAAiB,GAAG,IAAI,+BAA+B,kBAAkB;AAAA,EACrF;AACF;AAEA,SAAS,SAAS,OAA2B,KAAa,KAAa,UAA0B;AAC/F,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,CAAC,OAAO,UAAU,KAAK,GAAG;AAC5B,UAAM,IAAI,iBAAiB,4BAA4B,kBAAkB;AAAA,EAC3E;AACA,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAC3C;AAEA,SAAS,gBAAgB,KAA+B;AACtD,MAAI,OAAO,QAAQ,QAAQ,SAAS,QAAQ,QAAQ;AAClD,UAAM,IAAI,iBAAiB,mCAAmC,kBAAkB;AAAA,EAClF;AACF;AAIA,SAAS,QAAQ,KAA8B;AAC7C,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,SACG,IAAI,KAAK,CAAC,QAAQ;AACjB,UAAI,OAAO;AACX,UAAI,GAAG,QAAQ,CAAC,MAAe,QAAQ,CAAE;AACzC,UAAI,GAAG,OAAO,MAAM,QAAQ,IAAI,CAAC;AAAA,IACnC,CAAC,EACA,GAAG,SAAS,CAAC,QAAQ;AACpB,aAAO,IAAI,iBAAiB,sBAAsB,IAAI,OAAO,IAAI,mBAAmB,CAAC;AAAA,IACvF,CAAC;AAAA,EACL,CAAC;AACH;AAEA,SAAS,SAAS,KAAa,SAAkC;AAC/D,QAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,KAAK;AAAA,MACf;AAAA,QACE,UAAU,OAAO;AAAA,QACjB,MAAM,OAAO;AAAA,QACb,MAAM,OAAO;AAAA,QACb,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,kBAAkB,OAAO,WAAW,OAAO;AAAA,QAC7C;AAAA,MACF;AAAA,MACA,CAAC,QAAQ;AACP,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,MAAe,QAAQ,CAAE;AACzC,YAAI,GAAG,OAAO,MAAM,QAAQ,IAAI,CAAC;AAAA,MACnC;AAAA,IACF;AACA,QAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,aAAO,IAAI,iBAAiB,sBAAsB,IAAI,OAAO,IAAI,mBAAmB,CAAC;AAAA,IACvF,CAAC;AACD,QAAI,MAAM,OAAO;AACjB,QAAI,IAAI;AAAA,EACV,CAAC;AACH;AAEA,SAAS,kBAAkB,KAAa,WAA4B;AAClE,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,qBAAqB,SAAS,KAAK,IAAI,MAAM,GAAG,GAAG,CAAC;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,OAAO;AAChB,UAAM,MACJ,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,OAChD,OAAO,MAAkC,WAAW,KAAK,UAAU,OAAO,KAAK,IAChF,OAAO,OAAO,KAAK;AACzB,UAAM,IAAI,iBAAiB,qBAAqB,SAAS,KAAK,GAAG,IAAI,cAAc;AAAA,EACrF;AACA,SAAQ,OAAO,QAAoC,QAAQ;AAC7D;AAIO,IAAM,cAAN,MAAkB;AAAA,EACN;AAAA,EAEjB,YAAY,SAA8B;AACxC,UAAM,OAAO,SAAS,QAAQ;AAC9B,UAAM,OAAO,SAAS,QAAQ,eAAe;AAC7C,SAAK,UAAU,UAAU,IAAI,IAAI,IAAI;AAAA,EACvC;AAAA,EAEA,MAAc,MAAM,WAAmB,OAAmC;AACxE,UAAM,KACJ,SAAS,OAAO,UAAU,mBAAmB,KAAK,UAAU,KAAK,CAAC,CAAC,KAAK;AAC1E,UAAM,MAAM,GAAG,KAAK,OAAO,IAAI,SAAS,GAAG,EAAE;AAC7C,UAAM,MAAM,MAAM,QAAQ,GAAG;AAC7B,WAAO,kBAAkB,KAAK,SAAS;AAAA,EACzC;AAAA,EAEA,MAAc,OAAO,WAAmB,OAAkC;AACxE,UAAM,MAAM,GAAG,KAAK,OAAO,IAAI,SAAS;AACxC,UAAM,MAAM,MAAM,SAAS,KAAK,KAAK,UAAU,KAAK,CAAC;AACrD,WAAO,kBAAkB,KAAK,SAAS;AAAA,EACzC;AAAA;AAAA,EAIA,MAAM,YAAmC;AACvC,UAAM,OAAQ,MAAM,KAAK,MAAM,WAAW;AAC1C,WAAO;AAAA,MACL,YAAa,KAAK,aAA2B,CAAC,GAAG;AAAA,QAC/C,CAAC,MAAO,OAAO,MAAM,YAAY,MAAM,OAAQ,EAA8B,OAAO;AAAA,MACtF;AAAA,MACA,YAAa,KAAK,aAA2B,CAAC,GAAG,IAAI,CAAC,MAAM;AAC1D,cAAM,IAAI;AACV,eAAO;AAAA,UACL,UAAU,EAAE;AAAA,UACZ,MAAM,EAAE;AAAA,UACR,IAAI,EAAE;AAAA,UACN,cAAe,EAAE,gBAA2B;AAAA,QAC9C;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,OAAsD;AACxE,kBAAc,MAAM,KAAK,KAAK;AAC9B,UAAM,OAAQ,MAAM,KAAK,MAAM,iBAAiB,EAAE,KAAK,MAAM,IAAI,CAAC;AAClE,WAAO;AAAA,MACL,MAAM,gBAAgB,KAAK,IAAsC;AAAA,MACjE,WAAY,KAAK,YAA0C,CAAC,GAAG,IAAI,aAAa,EAAE,OAAO,OAAO;AAAA,MAChG,UAAW,KAAK,WAAyC,CAAC,GAAG,IAAI,aAAa,EAAE,OAAO,OAAO;AAAA,IAChG;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,OAA+C;AAC5D,UAAM,QAAQ,SAAS,MAAM,OAAO,GAAG,KAAK,EAAE;AAC9C,oBAAgB,MAAM,OAAO;AAC7B,UAAM,OAAQ,MAAM,KAAK,MAAM,YAAY;AAAA,MACzC,MAAM,MAAM;AAAA,MACZ;AAAA,MACA,YAAY,MAAM;AAAA,MAClB,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,MACf,OAAO,MAAM;AAAA,IACf,CAAC;AACD,WAAO;AAAA,MACL,QAAS,KAAK,SAAuC,CAAC,GAAG,IAAI,eAAe,EAAE,OAAO,OAAO;AAAA,MAC5F,SAAU,KAAK,WAAuB;AAAA,MACtC,YAAY,KAAK;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,OAA+C;AAC5D,UAAM,YAAY,MAAM,SAAS,MAAM,QAAQ,MAAM,WAAW,MAAM,SAAS,MAAM,QAAS,MAAM,SAAS,MAAM,MAAM,SAAS;AAClI,QAAI,CAAC,WAAW;AACd,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,QAAQ,SAAS,MAAM,OAAO,GAAG,KAAK,EAAE;AAC9C,oBAAgB,MAAM,OAAO;AAC7B,UAAM,OAAQ,MAAM,KAAK,MAAM,YAAY;AAAA,MACzC,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,MACf,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,MACZ;AAAA,MACA,YAAY,MAAM;AAAA,MAClB,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,MACf,OAAO,MAAM;AAAA,IACf,CAAC;AACD,WAAO;AAAA,MACL,QAAS,KAAK,SAAuC,CAAC,GAAG,IAAI,aAAa,EAAE,OAAO,OAAO;AAAA,MAC1F,SAAU,KAAK,WAAuB;AAAA,MACtC,YAAY,KAAK;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,OAA+C;AAC5D,kBAAc,MAAM,UAAU,UAAU;AACxC,QAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,WAAW,GAAG;AAC1C,YAAM,IAAI,iBAAiB,sCAAsC,kBAAkB;AAAA,IACrF;AACA,aAAS,IAAI,GAAG,IAAI,MAAM,KAAK,QAAQ,KAAK;AAC1C,YAAM,MAAM,MAAM,KAAK,CAAC;AACxB,oBAAc,IAAI,SAAS,QAAQ,CAAC,WAAW;AAC/C,UAAI,IAAI,aAAa,QAAQ,IAAI,cAAc,aAAa,IAAI,cAAc,WAAW;AACvF,cAAM,IAAI;AAAA,UACR,QAAQ,CAAC;AAAA,UACT;AAAA,QACF;AAAA,MACF;AACA,UAAI,IAAI,SAAS,SAAS,CAAC,OAAO,UAAU,IAAI,KAAK,KAAK,IAAI,QAAQ,IAAI;AACxE,cAAM,IAAI;AAAA,UACR,QAAQ,CAAC;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QAAI,MAAM,YAAY,SAAS,CAAC,OAAO,UAAU,MAAM,QAAQ,KAAK,MAAM,WAAW,IAAI;AACvF,YAAM,IAAI,iBAAiB,uCAAuC,kBAAkB;AAAA,IACtF;AACA,QAAI,MAAM,eAAe,SAAS,CAAC,OAAO,UAAU,MAAM,WAAW,KAAK,MAAM,cAAc,IAAI;AAChG,YAAM,IAAI,iBAAiB,0CAA0C,kBAAkB;AAAA,IACzF;AAEA,UAAM,OAAQ,MAAM,KAAK,OAAO,YAAY,KAAK;AACjD,WAAO;AAAA,MACL,OAAQ,KAAK,QAAsC,CAAC,GAAG,IAAI,CAAC,OAA0B;AAAA,QACpF,UAAU,EAAE;AAAA,QACZ,WAAW,EAAE;AAAA,QACb,OAAO,EAAE;AAAA,QACT,YAAa,EAAE,SAAuB,CAAC,GAAG;AAAA,QAC1C,QAAS,EAAE,SAAuC,CAAC,GAAG,IAAI,aAAa,EAAE,OAAO,OAAO;AAAA,QACvF,WAAY,EAAE,aAAyB;AAAA,MACzC,EAAE;AAAA,MACF,YAAa,KAAK,cAAyB;AAAA,MAC3C,WAAY,KAAK,aAAyB;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,OAA2C;AACtD,kBAAc,MAAM,GAAG,GAAG;AAC1B,UAAM,QAAQ,SAAS,MAAM,OAAO,GAAG,IAAI,EAAE;AAC7C,UAAM,OAAQ,MAAM,KAAK,MAAM,UAAU,EAAE,GAAG,MAAM,GAAG,MAAM,CAAC;AAC9D,WAAO;AAAA,MACL,UAAW,KAAK,WAAyC,CAAC,GAAG,IAAI,CAAC,MAAM;AACtE,cAAM,OAAO,gBAAgB,CAAC;AAC9B,YAAI,CAAC,KAAM,QAAO;AAClB,eAAO;AAAA,UACL,GAAG;AAAA,UACH,WAAY,EAAE,cAAyB;AAAA,QACzC;AAAA,MACF,CAAC,EAAE,OAAO,OAAO;AAAA,IACnB;AAAA,EACF;AACF;;;AG/QA,SAAS,WAAW,MAA4B;AAC9C,QAAM,QAAgC,CAAC;AACvC,QAAM,aAAuB,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,KAAK,CAAC,EAAE,WAAW,IAAI,GAAG;AAC5B,YAAM,MAAM,KAAK,CAAC,EAAE,MAAM,CAAC;AAC3B,YAAM,OAAO,KAAK,IAAI,CAAC;AACvB,UAAI,QAAQ,CAAC,KAAK,WAAW,IAAI,GAAG;AAClC,cAAM,GAAG,IAAI;AACb;AAAA,MACF,OAAO;AACL,cAAM,GAAG,IAAI;AAAA,MACf;AAAA,IACF,OAAO;AACL,iBAAW,KAAK,KAAK,CAAC,CAAC;AAAA,IACzB;AAAA,EACF;AACA,SAAO,EAAE,OAAO,WAAW;AAC7B;AAIA,eAAsB,YAAY,MAA+B;AAC/D,QAAM,UAAU,KAAK,CAAC;AACtB,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,QAAM,EAAE,OAAO,WAAW,IAAI,WAAW,IAAI;AAE7C,QAAM,OAAO,MAAM,OAAO,SAAS,MAAM,MAAM,EAAE,IAAI;AACrD,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,SAAS,IAAI,YAAY,EAAE,MAAM,KAAK,CAAC;AAE7C,MAAI;AACF,QAAI;AAEJ,YAAQ,SAAS;AAAA,MACf,KAAK;AACH,iBAAS,MAAM,OAAO,UAAU;AAChC;AAAA,MAEF,KAAK;AACH,YAAI,CAAC,WAAW,CAAC,GAAG;AAClB,cAAI,kCAAkC;AAAA,QACxC;AACA,iBAAS,MAAM,OAAO,cAAc,EAAE,KAAK,WAAW,CAAC,EAAE,CAAC;AAC1D;AAAA,MAEF,KAAK,cAAc;AACjB,YAAI,CAAC,WAAW,CAAC,GAAG;AAClB,cAAI,sDAAsD;AAAA,QAC5D;AACA,iBAAS,MAAM,OAAO,SAAS;AAAA,UAC7B,MAAM,WAAW,CAAC;AAAA,UAClB,OAAO,MAAM,QAAQ,SAAS,MAAM,OAAO,EAAE,IAAI;AAAA,QACnD,CAAC;AACD;AAAA,MACF;AAAA,MAEA,KAAK,cAAc;AACjB,iBAAS,MAAM,OAAO,SAAS;AAAA,UAC7B,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,UACZ,SAAS,MAAM;AAAA,UACf,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,UACZ,OAAO,MAAM,QAAQ,SAAS,MAAM,OAAO,EAAE,IAAI;AAAA,QACnD,CAAC;AACD;AAAA,MACF;AAAA,MAEA,KAAK,YAAY;AACf,cAAM,UAAU,WAAW,KAAK,GAAG;AACnC,YAAI,CAAC,SAAS;AACZ;AAAA,YACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAkBF;AAAA,QACF;AACA,YAAI;AACJ,YAAI;AACF,kBAAQ,KAAK,MAAM,OAAO;AAAA,QAC5B,QAAQ;AACN,cAAI,iBAAiB,QAAQ,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,QAC9C;AACA,iBAAS,MAAM,OAAO,SAAS,KAAM;AACrC;AAAA,MACF;AAAA,MAEA,KAAK;AACH,YAAI,CAAC,WAAW,CAAC,GAAG;AAClB,cAAI,uCAAuC;AAAA,QAC7C;AACA,iBAAS,MAAM,OAAO,OAAO,EAAE,GAAG,WAAW,KAAK,GAAG,EAAE,CAAC;AACxD;AAAA,MAEF,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,kBAAU;AACV;AAAA,MAEF;AACE;AAAA,UACE,0BAA0B,OAAO;AAAA;AAAA,QAEnC;AAAA,IACJ;AAEA,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,KAAK;AACZ,QAAI,eAAe,kBAAkB;AACnC,cAAQ,MAAM,KAAK,UAAU,EAAE,OAAO,IAAI,SAAS,MAAM,IAAI,KAAK,CAAC,CAAC;AAAA,IACtE,OAAO;AACL,cAAQ,MAAM,KAAK,UAAU,EAAE,OAAQ,IAAc,QAAQ,CAAC,CAAC;AAAA,IACjE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,SAAS,YAAkB;AACzB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,8CAA8C;AAC1D,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,aAAa;AACzB,UAAQ,IAAI,gFAAgF;AAC5F,UAAQ,IAAI,gEAAgE;AAC5E,UAAQ,IAAI,0DAA0D;AACtE,UAAQ,IAAI,iEAAiE;AAC7E,UAAQ,IAAI,+DAAiE;AAC7E,UAAQ,IAAI,0DAA0D;AACtE,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,mBAAmB;AAC/B,UAAQ,IAAI,6EAA6E;AACzF,UAAQ,IAAI,+DAA+D;AAC3E,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,uBAAuB;AACnC,UAAQ,IAAI,6CAA6C;AACzD,UAAQ,IAAI,4CAA4C;AACxD,UAAQ,IAAI,+CAA+C;AAC3D,UAAQ,IAAI,6CAA6C;AACzD,UAAQ,IAAI,4CAA4C;AACxD,UAAQ,IAAI,uDAAuD;AACnE,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,aAAa;AACzB,UAAQ,IAAI,gCAAgC;AAC5C,UAAQ,IAAI,mDAAmD;AAC/D,UAAQ,IAAI,oDAAoD;AAChE,UAAQ,IAAI,mFAAmF;AAC/F,UAAQ,IAAI,2CAA2C;AACvD,UAAQ,IAAI,EAAE;AAChB;AAEA,SAAS,IAAI,KAAoB;AAC/B,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB;","names":[]}