@typicalday/firegraph 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -90
- package/bin/firegraph.mjs +21 -7
- package/dist/{backend-BrqFkbid.d.ts → backend-U-MLShlg.d.ts} +1 -1
- package/dist/{backend-73p5Blx7.d.cts → backend-np4gEVhB.d.cts} +1 -1
- package/dist/backend.d.cts +3 -3
- package/dist/backend.d.ts +3 -3
- package/dist/{chunk-LZOIQHYN.js → chunk-6SB34IPQ.js} +20 -7
- package/dist/chunk-6SB34IPQ.js.map +1 -0
- package/dist/{chunk-SU4FNLC3.js → chunk-EEKWRX5E.js} +1 -1
- package/dist/{chunk-SU4FNLC3.js.map → chunk-EEKWRX5E.js.map} +1 -1
- package/dist/{chunk-YLGXLEUE.js → chunk-GJVVRTQT.js} +5 -14
- package/dist/chunk-GJVVRTQT.js.map +1 -0
- package/dist/cloudflare/index.cjs +151 -27
- package/dist/cloudflare/index.cjs.map +1 -1
- package/dist/cloudflare/index.d.cts +78 -3
- package/dist/cloudflare/index.d.ts +78 -3
- package/dist/cloudflare/index.js +135 -23
- package/dist/cloudflare/index.js.map +1 -1
- package/dist/codegen/index.cjs +4 -13
- package/dist/codegen/index.cjs.map +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/codegen/index.js +1 -1
- package/dist/index.cjs +89 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -21
- package/dist/index.d.ts +106 -21
- package/dist/index.js +70 -116
- package/dist/index.js.map +1 -1
- package/dist/query-client/index.cjs.map +1 -1
- package/dist/query-client/index.js +1 -1
- package/dist/{types-DOemdlVA.d.ts → types-BGWxcpI_.d.cts} +75 -1
- package/dist/{types-DOemdlVA.d.cts → types-BGWxcpI_.d.ts} +75 -1
- package/package.json +35 -27
- package/dist/chunk-LZOIQHYN.js.map +0 -1
- package/dist/chunk-YLGXLEUE.js.map +0 -1
- package/dist/editor/client/assets/index-Bq2bfzeY.js +0 -411
- package/dist/editor/client/assets/index-CJ4m_EOL.css +0 -1
- package/dist/editor/client/index.html +0 -16
- package/dist/editor/server/index.mjs +0 -51566
package/README.md
CHANGED
|
@@ -90,6 +90,7 @@ const g = createGraphClient(db, 'graph', { registry });
|
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
**Parameters:**
|
|
93
|
+
|
|
93
94
|
- `db` — A `Firestore` instance from `@google-cloud/firestore`
|
|
94
95
|
- `collectionPath` — Firestore collection path for all graph data
|
|
95
96
|
- `options.registry` — Optional `GraphRegistry` for schema validation
|
|
@@ -207,10 +208,10 @@ const result = await createTraversal(g, tourId)
|
|
|
207
208
|
})
|
|
208
209
|
.run({ maxReads: 200, returnIntermediates: true });
|
|
209
210
|
|
|
210
|
-
result.nodes;
|
|
211
|
-
result.hops;
|
|
211
|
+
result.nodes; // StoredGraphRecord[] — edges from the final hop
|
|
212
|
+
result.hops; // HopResult[] — per-hop breakdown
|
|
212
213
|
result.totalReads; // number — Firestore reads consumed
|
|
213
|
-
result.truncated;
|
|
214
|
+
result.truncated; // boolean — true if budget was hit
|
|
214
215
|
```
|
|
215
216
|
|
|
216
217
|
`createTraversal` accepts a `GraphClient` or `GraphReader`. When passed a `GraphClient`, cross-graph hops via `targetGraph` are supported (see [Cross-Graph Edges](#cross-graph-edges)).
|
|
@@ -233,33 +234,30 @@ const result = await createTraversal(g, riderId)
|
|
|
233
234
|
|
|
234
235
|
```typescript
|
|
235
236
|
await g.runTransaction(async (tx) => {
|
|
236
|
-
const result = await createTraversal(tx, tourId)
|
|
237
|
-
.follow('hasDeparture')
|
|
238
|
-
.follow('hasRider')
|
|
239
|
-
.run();
|
|
237
|
+
const result = await createTraversal(tx, tourId).follow('hasDeparture').follow('hasRider').run();
|
|
240
238
|
// Use result to make transactional writes...
|
|
241
239
|
});
|
|
242
240
|
```
|
|
243
241
|
|
|
244
242
|
#### Hop Options
|
|
245
243
|
|
|
246
|
-
| Option
|
|
247
|
-
|
|
248
|
-
| `direction`
|
|
249
|
-
| `aType`
|
|
250
|
-
| `bType`
|
|
251
|
-
| `limit`
|
|
252
|
-
| `orderBy`
|
|
253
|
-
| `filter`
|
|
254
|
-
| `targetGraph` | `string`
|
|
244
|
+
| Option | Type | Default | Description |
|
|
245
|
+
| ------------- | ------------------------ | ----------- | ---------------------------------------------------------------------------------- |
|
|
246
|
+
| `direction` | `'forward' \| 'reverse'` | `'forward'` | Edge direction |
|
|
247
|
+
| `aType` | `string` | — | Filter source node type |
|
|
248
|
+
| `bType` | `string` | — | Filter target node type |
|
|
249
|
+
| `limit` | `number` | `10` | Max edges per source node |
|
|
250
|
+
| `orderBy` | `{ field, direction? }` | — | Firestore-level ordering |
|
|
251
|
+
| `filter` | `(edge) => boolean` | — | In-memory post-filter |
|
|
252
|
+
| `targetGraph` | `string` | — | Subgraph to cross into (forward only). See [Cross-Graph Edges](#cross-graph-edges) |
|
|
255
253
|
|
|
256
254
|
#### Run Options
|
|
257
255
|
|
|
258
|
-
| Option
|
|
259
|
-
|
|
260
|
-
| `maxReads`
|
|
261
|
-
| `concurrency`
|
|
262
|
-
| `returnIntermediates` | `boolean` | `false` | Include edges from all hops
|
|
256
|
+
| Option | Type | Default | Description |
|
|
257
|
+
| --------------------- | --------- | ------- | ---------------------------- |
|
|
258
|
+
| `maxReads` | `number` | `100` | Total Firestore read budget |
|
|
259
|
+
| `concurrency` | `number` | `5` | Max parallel queries per hop |
|
|
260
|
+
| `returnIntermediates` | `boolean` | `false` | Include edges from all hops |
|
|
263
261
|
|
|
264
262
|
When `filter` is set, the `limit` is applied after filtering (in-memory), so Firestore returns all matching edges and the filter + slice happens client-side.
|
|
265
263
|
|
|
@@ -363,7 +361,7 @@ const registry = createRegistry([
|
|
|
363
361
|
axbType: 'is',
|
|
364
362
|
bType: 'tour',
|
|
365
363
|
jsonSchema: tourSchemaV2,
|
|
366
|
-
migrations,
|
|
364
|
+
migrations, // version derived as max(toVersion) = 2
|
|
367
365
|
migrationWriteBack: 'eager',
|
|
368
366
|
},
|
|
369
367
|
]);
|
|
@@ -386,11 +384,11 @@ const tour = await g.getNode(tourId);
|
|
|
386
384
|
|
|
387
385
|
Write-back controls whether migrated data is persisted back to Firestore after a read-triggered migration:
|
|
388
386
|
|
|
389
|
-
| Mode
|
|
390
|
-
|
|
391
|
-
| `'off'`
|
|
392
|
-
| `'eager'`
|
|
393
|
-
| `'background'` | Same as eager but errors are swallowed with a `console.warn`
|
|
387
|
+
| Mode | Behavior |
|
|
388
|
+
| -------------- | --------------------------------------------------------------- |
|
|
389
|
+
| `'off'` | In-memory only; Firestore document unchanged (default) |
|
|
390
|
+
| `'eager'` | Fire-and-forget write after read; inline update in transactions |
|
|
391
|
+
| `'background'` | Same as eager but errors are swallowed with a `console.warn` |
|
|
394
392
|
|
|
395
393
|
Resolution order: `entry.migrationWriteBack > client.migrationWriteBack > 'off'`
|
|
396
394
|
|
|
@@ -402,11 +400,15 @@ const g = createGraphClient(db, 'graph', {
|
|
|
402
400
|
});
|
|
403
401
|
|
|
404
402
|
// Entry-level override (takes priority)
|
|
405
|
-
createRegistry([
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
403
|
+
createRegistry([
|
|
404
|
+
{
|
|
405
|
+
aType: 'tour',
|
|
406
|
+
axbType: 'is',
|
|
407
|
+
bType: 'tour',
|
|
408
|
+
migrations,
|
|
409
|
+
migrationWriteBack: 'eager',
|
|
410
|
+
},
|
|
411
|
+
]);
|
|
410
412
|
```
|
|
411
413
|
|
|
412
414
|
#### Dynamic Registry Migrations
|
|
@@ -415,9 +417,7 @@ In dynamic mode, migrations are stored as source code strings:
|
|
|
415
417
|
|
|
416
418
|
```typescript
|
|
417
419
|
await g.defineNodeType('tour', tourSchema, 'A tour', {
|
|
418
|
-
migrations: [
|
|
419
|
-
{ fromVersion: 0, toVersion: 1, up: '(d) => ({ ...d, status: "draft" })' },
|
|
420
|
-
],
|
|
420
|
+
migrations: [{ fromVersion: 0, toVersion: 1, up: '(d) => ({ ...d, status: "draft" })' }],
|
|
421
421
|
migrationWriteBack: 'eager',
|
|
422
422
|
});
|
|
423
423
|
await g.reloadRegistry();
|
|
@@ -431,7 +431,9 @@ For custom sandboxing, pass `migrationSandbox` to `createGraphClient()`:
|
|
|
431
431
|
const g = createGraphClient(db, 'graph', {
|
|
432
432
|
registryMode: { mode: 'dynamic' },
|
|
433
433
|
migrationSandbox: (source) => {
|
|
434
|
-
const compartment = new Compartment({
|
|
434
|
+
const compartment = new Compartment({
|
|
435
|
+
/* endowments */
|
|
436
|
+
});
|
|
435
437
|
return compartment.evaluate(source);
|
|
436
438
|
},
|
|
437
439
|
});
|
|
@@ -507,14 +509,14 @@ await g.putNode('memory', generateId(), {}); // throws RegistryScopeError
|
|
|
507
509
|
|
|
508
510
|
**Pattern syntax:**
|
|
509
511
|
|
|
510
|
-
| Pattern
|
|
511
|
-
|
|
512
|
-
| `root`
|
|
513
|
-
| `memories`
|
|
514
|
-
| `workspace/tasks` | Exact path
|
|
515
|
-
| `*/memories`
|
|
516
|
-
| `**/memories`
|
|
517
|
-
| `**`
|
|
512
|
+
| Pattern | Matches |
|
|
513
|
+
| ----------------- | ---------------------------------- |
|
|
514
|
+
| `root` | Top-level collection only |
|
|
515
|
+
| `memories` | Exact subgraph name |
|
|
516
|
+
| `workspace/tasks` | Exact path |
|
|
517
|
+
| `*/memories` | `*` matches one segment |
|
|
518
|
+
| `**/memories` | `**` matches zero or more segments |
|
|
519
|
+
| `**` | Everything including root |
|
|
518
520
|
|
|
519
521
|
Omitting `allowedIn` (or passing an empty array) means the type is allowed everywhere.
|
|
520
522
|
|
|
@@ -592,9 +594,7 @@ await workflow.putNode('agent', agentId, { name: 'Backend Dev' });
|
|
|
592
594
|
await workflow.putEdge('task', taskId, 'assignedTo', 'agent', agentId, { role: 'lead' });
|
|
593
595
|
|
|
594
596
|
// Forward traversal: task → agents (automatically crosses into workflow subgraph)
|
|
595
|
-
const result = await createTraversal(g, taskId, registry)
|
|
596
|
-
.follow('assignedTo')
|
|
597
|
-
.run();
|
|
597
|
+
const result = await createTraversal(g, taskId, registry).follow('assignedTo').run();
|
|
598
598
|
// result.nodes contains the agent edges from the workflow subgraph
|
|
599
599
|
```
|
|
600
600
|
|
|
@@ -632,9 +632,7 @@ You can override the registry's `targetGraph` on a per-hop basis:
|
|
|
632
632
|
|
|
633
633
|
```typescript
|
|
634
634
|
// Use 'team' subgraph instead of registry's default
|
|
635
|
-
const result = await createTraversal(g, taskId)
|
|
636
|
-
.follow('assignedTo', { targetGraph: 'team' })
|
|
637
|
-
.run();
|
|
635
|
+
const result = await createTraversal(g, taskId).follow('assignedTo', { targetGraph: 'team' }).run();
|
|
638
636
|
```
|
|
639
637
|
|
|
640
638
|
Resolution priority: explicit hop `targetGraph` > registry `targetGraph` > no cross-graph.
|
|
@@ -647,7 +645,7 @@ For cross-cutting reads across all subgraphs, use `findEdgesGlobal`:
|
|
|
647
645
|
// Find all 'assignedTo' edges across all 'workflow' subgraphs in the database
|
|
648
646
|
const allAssignments = await g.findEdgesGlobal(
|
|
649
647
|
{ axbType: 'assignedTo', allowCollectionScan: true },
|
|
650
|
-
'workflow',
|
|
648
|
+
'workflow', // collection name to query across
|
|
651
649
|
);
|
|
652
650
|
```
|
|
653
651
|
|
|
@@ -659,17 +657,13 @@ Each hop resolves its reader from the root client. If hop 1 crosses into a subgr
|
|
|
659
657
|
|
|
660
658
|
```typescript
|
|
661
659
|
// This traversal finds agents in the workflow subgraph
|
|
662
|
-
const agents = await createTraversal(g, taskId, registry)
|
|
663
|
-
.follow('assignedTo')
|
|
664
|
-
.run();
|
|
660
|
+
const agents = await createTraversal(g, taskId, registry).follow('assignedTo').run();
|
|
665
661
|
|
|
666
662
|
// To continue traversing within the workflow subgraph,
|
|
667
663
|
// create a new traversal from the subgraph client
|
|
668
664
|
const workflow = g.subgraph(taskId, 'workflow');
|
|
669
665
|
for (const agent of agents.nodes) {
|
|
670
|
-
const mentees = await createTraversal(workflow, agent.bUid)
|
|
671
|
-
.follow('mentors')
|
|
672
|
-
.run();
|
|
666
|
+
const mentees = await createTraversal(workflow, agent.bUid).follow('mentors').run();
|
|
673
667
|
}
|
|
674
668
|
```
|
|
675
669
|
|
|
@@ -695,19 +689,19 @@ const id = generateId(); // 21-char URL-safe nanoid
|
|
|
695
689
|
|
|
696
690
|
All errors extend `FiregraphError` with a `code` property:
|
|
697
691
|
|
|
698
|
-
| Error Class
|
|
699
|
-
|
|
700
|
-
| `FiregraphError`
|
|
701
|
-
| `NodeNotFoundError`
|
|
702
|
-
| `EdgeNotFoundError`
|
|
703
|
-
| `ValidationError`
|
|
704
|
-
| `RegistryViolationError` | `REGISTRY_VIOLATION`
|
|
705
|
-
| `RegistryScopeError`
|
|
706
|
-
| `MigrationError`
|
|
707
|
-
| `DynamicRegistryError`
|
|
708
|
-
| `InvalidQueryError`
|
|
709
|
-
| `QuerySafetyError`
|
|
710
|
-
| `TraversalError`
|
|
692
|
+
| Error Class | Code | When |
|
|
693
|
+
| ------------------------ | ------------------------ | --------------------------------------------------------------- |
|
|
694
|
+
| `FiregraphError` | varies | Base class |
|
|
695
|
+
| `NodeNotFoundError` | `NODE_NOT_FOUND` | Node lookup fails (not thrown by `getNode` — it returns `null`) |
|
|
696
|
+
| `EdgeNotFoundError` | `EDGE_NOT_FOUND` | Edge lookup fails |
|
|
697
|
+
| `ValidationError` | `VALIDATION_ERROR` | Schema validation fails (registry + Zod) |
|
|
698
|
+
| `RegistryViolationError` | `REGISTRY_VIOLATION` | Triple not registered |
|
|
699
|
+
| `RegistryScopeError` | `REGISTRY_SCOPE` | Type not allowed at this subgraph scope |
|
|
700
|
+
| `MigrationError` | `MIGRATION_ERROR` | Migration function fails or chain is incomplete |
|
|
701
|
+
| `DynamicRegistryError` | `DYNAMIC_REGISTRY_ERROR` | Dynamic registry misconfiguration or misuse |
|
|
702
|
+
| `InvalidQueryError` | `INVALID_QUERY` | `findEdges` called with no filters |
|
|
703
|
+
| `QuerySafetyError` | `QUERY_SAFETY` | Query would cause a full collection scan |
|
|
704
|
+
| `TraversalError` | `TRAVERSAL_ERROR` | `run()` called with zero hops |
|
|
711
705
|
|
|
712
706
|
```typescript
|
|
713
707
|
import { FiregraphError, ValidationError } from 'firegraph';
|
|
@@ -716,7 +710,7 @@ try {
|
|
|
716
710
|
await g.putNode('tour', generateId(), { name: 123 });
|
|
717
711
|
} catch (err) {
|
|
718
712
|
if (err instanceof ValidationError) {
|
|
719
|
-
console.error(err.code);
|
|
713
|
+
console.error(err.code); // 'VALIDATION_ERROR'
|
|
720
714
|
console.error(err.details); // Zod error details
|
|
721
715
|
}
|
|
722
716
|
}
|
|
@@ -748,9 +742,9 @@ import type {
|
|
|
748
742
|
GraphClientOptions,
|
|
749
743
|
|
|
750
744
|
// Registry
|
|
751
|
-
RegistryEntry,
|
|
752
|
-
GraphRegistry,
|
|
753
|
-
EdgeTopology,
|
|
745
|
+
RegistryEntry, // includes targetGraph, allowedIn
|
|
746
|
+
GraphRegistry, // includes lookupByAxbType
|
|
747
|
+
EdgeTopology, // includes targetGraph
|
|
754
748
|
|
|
755
749
|
// Dynamic Registry
|
|
756
750
|
DynamicGraphClient,
|
|
@@ -766,7 +760,7 @@ import type {
|
|
|
766
760
|
MigrationWriteBack,
|
|
767
761
|
|
|
768
762
|
// Traversal
|
|
769
|
-
HopDefinition,
|
|
763
|
+
HopDefinition, // includes targetGraph
|
|
770
764
|
TraversalOptions,
|
|
771
765
|
HopResult,
|
|
772
766
|
TraversalResult,
|
|
@@ -784,17 +778,17 @@ import type {
|
|
|
784
778
|
|
|
785
779
|
All data lives in one Firestore collection. Each document has these fields:
|
|
786
780
|
|
|
787
|
-
| Field
|
|
788
|
-
|
|
789
|
-
| `aType`
|
|
790
|
-
| `aUid`
|
|
791
|
-
| `axbType`
|
|
792
|
-
| `bType`
|
|
793
|
-
| `bUid`
|
|
794
|
-
| `data`
|
|
795
|
-
| `v`
|
|
796
|
-
| `createdAt` | Timestamp | Server-set on create
|
|
797
|
-
| `updatedAt` | Timestamp | Server-set on create/update
|
|
781
|
+
| Field | Type | Description |
|
|
782
|
+
| ----------- | --------- | ------------------------------------------------------------------------------------------- |
|
|
783
|
+
| `aType` | string | Source node type |
|
|
784
|
+
| `aUid` | string | Source node ID |
|
|
785
|
+
| `axbType` | string | Relationship type (`is` for nodes) |
|
|
786
|
+
| `bType` | string | Target node type |
|
|
787
|
+
| `bUid` | string | Target node ID |
|
|
788
|
+
| `data` | object | User payload |
|
|
789
|
+
| `v` | number? | Schema version (derived from `max(toVersion)` of migrations; set when entry has migrations) |
|
|
790
|
+
| `createdAt` | Timestamp | Server-set on create |
|
|
791
|
+
| `updatedAt` | Timestamp | Server-set on create/update |
|
|
798
792
|
|
|
799
793
|
### Query Planning
|
|
800
794
|
|
|
@@ -840,12 +834,13 @@ Uses the Firestore Pipeline API (`db.pipeline()`). This is the recommended mode
|
|
|
840
834
|
|
|
841
835
|
Uses standard Firestore queries (`.where().get()`). Use only if you understand the limitations:
|
|
842
836
|
|
|
843
|
-
| Firestore Edition | With `data.*` Filters
|
|
844
|
-
|
|
845
|
-
| Enterprise
|
|
846
|
-
| Standard
|
|
837
|
+
| Firestore Edition | With `data.*` Filters | Risk |
|
|
838
|
+
| ----------------- | -------------------------------------- | --------------------------------- |
|
|
839
|
+
| Enterprise | Full collection scan (no index needed) | High billing on large collections |
|
|
840
|
+
| Standard | Fails without composite index | Query errors for unindexed fields |
|
|
847
841
|
|
|
848
842
|
Standard mode is appropriate for:
|
|
843
|
+
|
|
849
844
|
- **Emulator** — the emulator doesn't support pipelines, so firegraph auto-falls back to standard mode when `FIRESTORE_EMULATOR_HOST` is set
|
|
850
845
|
- **Small datasets** where full scans are acceptable
|
|
851
846
|
- Projects that manage their own composite indexes
|
|
@@ -882,6 +877,14 @@ pnpm test:emulator # Full test suite against Firestore emulator
|
|
|
882
877
|
|
|
883
878
|
Requires Node.js 18+.
|
|
884
879
|
|
|
880
|
+
## Releasing
|
|
881
|
+
|
|
882
|
+
Versions and npm publishes are automated via [release-please](https://github.com/googleapis/release-please).
|
|
883
|
+
Land PRs to `main` with conventional-commit messages; release-please opens a
|
|
884
|
+
Release PR that, when merged, tags + publishes to npm. See
|
|
885
|
+
[docs/releasing.md](docs/releasing.md) for maintainer setup (NPM token,
|
|
886
|
+
workflow permissions) and the full flow.
|
|
887
|
+
|
|
885
888
|
## License
|
|
886
889
|
|
|
887
890
|
MIT
|
package/bin/firegraph.mjs
CHANGED
|
@@ -36,7 +36,9 @@ if (subcommand === 'editor') {
|
|
|
36
36
|
try {
|
|
37
37
|
execSync('npm run build:editor', { cwd: pkgDir, stdio: 'inherit' });
|
|
38
38
|
} catch {
|
|
39
|
-
console.error(
|
|
39
|
+
console.error(
|
|
40
|
+
'Failed to build editor. Run "npm run build:editor" manually in the firegraph package directory.',
|
|
41
|
+
);
|
|
40
42
|
process.exit(1);
|
|
41
43
|
}
|
|
42
44
|
}
|
|
@@ -93,16 +95,20 @@ if (subcommand === 'editor') {
|
|
|
93
95
|
const outPath = args.out || null;
|
|
94
96
|
|
|
95
97
|
const distIndex = path.join(__dirname, '..', 'dist', 'index.js');
|
|
96
|
-
const { generateIndexConfig, discoverEntities } = await import(distIndex);
|
|
98
|
+
const { generateIndexConfig, discoverEntities, createRegistry } = await import(distIndex);
|
|
97
99
|
|
|
98
100
|
try {
|
|
99
101
|
let entities = undefined;
|
|
102
|
+
let registryEntries = undefined;
|
|
100
103
|
if (entitiesDir) {
|
|
101
104
|
const { result, warnings } = discoverEntities(entitiesDir);
|
|
102
105
|
for (const w of warnings) {
|
|
103
106
|
console.warn(` warning: ${w.message}`);
|
|
104
107
|
}
|
|
105
108
|
entities = result;
|
|
109
|
+
// Build a registry so per-entity `indexes` (from meta.json) reach
|
|
110
|
+
// the generator via RegistryEntry.indexes.
|
|
111
|
+
registryEntries = createRegistry(result).entries();
|
|
106
112
|
const nodeCount = result.nodes.size;
|
|
107
113
|
const edgeCount = result.edges.size;
|
|
108
114
|
if (nodeCount > 0 || edgeCount > 0) {
|
|
@@ -110,7 +116,7 @@ if (subcommand === 'editor') {
|
|
|
110
116
|
}
|
|
111
117
|
}
|
|
112
118
|
|
|
113
|
-
const config = generateIndexConfig(collection, entities);
|
|
119
|
+
const config = generateIndexConfig(collection, { entities, registryEntries });
|
|
114
120
|
const output = JSON.stringify(config, null, 2) + '\n';
|
|
115
121
|
|
|
116
122
|
if (outPath) {
|
|
@@ -136,7 +142,9 @@ if (subcommand === 'editor') {
|
|
|
136
142
|
console.log(' indexes Generate recommended Firestore index definitions');
|
|
137
143
|
console.log('');
|
|
138
144
|
console.log(' Editor options:');
|
|
139
|
-
console.log(
|
|
145
|
+
console.log(
|
|
146
|
+
' --config <path> Path to firegraph.config.ts (default: auto-discover in cwd)',
|
|
147
|
+
);
|
|
140
148
|
console.log(' --entities <path> Path to entities directory');
|
|
141
149
|
console.log(' --project <id> GCP project ID (default: auto-detect via ADC)');
|
|
142
150
|
console.log(' --collection <path> Firestore collection path (default: graph)');
|
|
@@ -152,7 +160,9 @@ if (subcommand === 'editor') {
|
|
|
152
160
|
console.log(' --out <path> Output file path (default: stdout)');
|
|
153
161
|
console.log('');
|
|
154
162
|
console.log(' Indexes options:');
|
|
155
|
-
console.log(
|
|
163
|
+
console.log(
|
|
164
|
+
' --entities <path> Path to entities directory (adds per-entity data field indexes)',
|
|
165
|
+
);
|
|
156
166
|
console.log(' --collection <name> Firestore collection name (default: graph)');
|
|
157
167
|
console.log(' --out <path> Output file path (default: stdout)');
|
|
158
168
|
console.log('');
|
|
@@ -161,12 +171,16 @@ if (subcommand === 'editor') {
|
|
|
161
171
|
console.log(' flags every time. CLI flags override config file values.');
|
|
162
172
|
console.log('');
|
|
163
173
|
console.log(' Examples:');
|
|
164
|
-
console.log(
|
|
174
|
+
console.log(
|
|
175
|
+
' npx firegraph editor # uses firegraph.config.ts',
|
|
176
|
+
);
|
|
165
177
|
console.log(' npx firegraph editor --config ./custom-config.ts # explicit config file');
|
|
166
178
|
console.log(' npx firegraph editor --entities ./entities # per-entity convention');
|
|
167
179
|
console.log(' npx firegraph codegen --entities ./entities # types to stdout');
|
|
168
180
|
console.log(' npx firegraph codegen --entities ./entities --out src/generated/types.ts');
|
|
169
|
-
console.log(
|
|
181
|
+
console.log(
|
|
182
|
+
' npx firegraph indexes # 4 base indexes to stdout',
|
|
183
|
+
);
|
|
170
184
|
console.log(' npx firegraph indexes --entities ./entities --out firestore.indexes.json');
|
|
171
185
|
console.log('');
|
|
172
186
|
} else {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as StoredGraphRecord, i as QueryFilter,
|
|
1
|
+
import { S as StoredGraphRecord, i as QueryFilter, z as QueryOptions, d as GraphReader, m as BulkOptions, C as CascadeResult, F as FindEdgesParams, o as BulkResult } from './types-BGWxcpI_.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Backend abstraction for firegraph.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as StoredGraphRecord, i as QueryFilter,
|
|
1
|
+
import { S as StoredGraphRecord, i as QueryFilter, z as QueryOptions, d as GraphReader, m as BulkOptions, C as CascadeResult, F as FindEdgesParams, o as BulkResult } from './types-BGWxcpI_.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Backend abstraction for firegraph.
|
package/dist/backend.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { C as CrossBackendTransactionError, S as StorageScopeSegment, a as appendStorageScope, i as isAncestorScopeUid, p as parseStorageScope, r as resolveAncestorScope } from './scope-path-B1G3YiA7.cjs';
|
|
2
|
-
import { S as StorageBackend } from './backend-
|
|
3
|
-
export { B as BatchBackend, T as TransactionBackend, U as UpdatePayload, W as WritableRecord } from './backend-
|
|
4
|
-
import './types-
|
|
2
|
+
import { S as StorageBackend } from './backend-np4gEVhB.cjs';
|
|
3
|
+
export { B as BatchBackend, T as TransactionBackend, U as UpdatePayload, W as WritableRecord } from './backend-np4gEVhB.cjs';
|
|
4
|
+
import './types-BGWxcpI_.cjs';
|
|
5
5
|
import '@google-cloud/firestore';
|
|
6
6
|
|
|
7
7
|
/**
|
package/dist/backend.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { C as CrossBackendTransactionError, S as StorageScopeSegment, a as appendStorageScope, i as isAncestorScopeUid, p as parseStorageScope, r as resolveAncestorScope } from './scope-path-B1G3YiA7.js';
|
|
2
|
-
import { S as StorageBackend } from './backend-
|
|
3
|
-
export { B as BatchBackend, T as TransactionBackend, U as UpdatePayload, W as WritableRecord } from './backend-
|
|
4
|
-
import './types-
|
|
2
|
+
import { S as StorageBackend } from './backend-U-MLShlg.js';
|
|
3
|
+
export { B as BatchBackend, T as TransactionBackend, U as UpdatePayload, W as WritableRecord } from './backend-U-MLShlg.js';
|
|
4
|
+
import './types-BGWxcpI_.js';
|
|
5
5
|
import '@google-cloud/firestore';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -214,9 +214,7 @@ async function migrateRecord(record, registry, globalWriteBack = "off") {
|
|
|
214
214
|
};
|
|
215
215
|
}
|
|
216
216
|
async function migrateRecords(records, registry, globalWriteBack = "off") {
|
|
217
|
-
return Promise.all(
|
|
218
|
-
records.map((r) => migrateRecord(r, registry, globalWriteBack))
|
|
219
|
-
);
|
|
217
|
+
return Promise.all(records.map((r) => migrateRecord(r, registry, globalWriteBack)));
|
|
220
218
|
}
|
|
221
219
|
|
|
222
220
|
// src/scope.ts
|
|
@@ -424,7 +422,8 @@ function discoveryToEntries(discovery) {
|
|
|
424
422
|
subtitleField: entity.subtitleField,
|
|
425
423
|
allowedIn: entity.allowedIn,
|
|
426
424
|
migrations: entity.migrations,
|
|
427
|
-
migrationWriteBack: entity.migrationWriteBack
|
|
425
|
+
migrationWriteBack: entity.migrationWriteBack,
|
|
426
|
+
indexes: entity.indexes
|
|
428
427
|
});
|
|
429
428
|
}
|
|
430
429
|
for (const [axbType, entity] of discovery.edges) {
|
|
@@ -452,7 +451,8 @@ function discoveryToEntries(discovery) {
|
|
|
452
451
|
allowedIn: entity.allowedIn,
|
|
453
452
|
targetGraph: resolvedTargetGraph,
|
|
454
453
|
migrations: entity.migrations,
|
|
455
|
-
migrationWriteBack: entity.migrationWriteBack
|
|
454
|
+
migrationWriteBack: entity.migrationWriteBack,
|
|
455
|
+
indexes: entity.indexes
|
|
456
456
|
});
|
|
457
457
|
}
|
|
458
458
|
}
|
|
@@ -1593,6 +1593,18 @@ function createGraphClientFromBackend(backend, options, metaBackend) {
|
|
|
1593
1593
|
return new GraphClientImpl(backend, options, metaBackend);
|
|
1594
1594
|
}
|
|
1595
1595
|
|
|
1596
|
+
// src/default-indexes.ts
|
|
1597
|
+
var DEFAULT_CORE_INDEXES = Object.freeze([
|
|
1598
|
+
{ fields: ["aUid"] },
|
|
1599
|
+
{ fields: ["bUid"] },
|
|
1600
|
+
{ fields: ["aType"] },
|
|
1601
|
+
{ fields: ["bType"] },
|
|
1602
|
+
{ fields: ["aUid", "axbType"] },
|
|
1603
|
+
{ fields: ["axbType", "bUid"] },
|
|
1604
|
+
{ fields: ["aType", "axbType"] },
|
|
1605
|
+
{ fields: ["axbType", "bType"] }
|
|
1606
|
+
]);
|
|
1607
|
+
|
|
1596
1608
|
export {
|
|
1597
1609
|
NODE_RELATION,
|
|
1598
1610
|
DEFAULT_QUERY_LIMIT,
|
|
@@ -1625,6 +1637,7 @@ export {
|
|
|
1625
1637
|
buildNodeQueryPlan,
|
|
1626
1638
|
analyzeQuerySafety,
|
|
1627
1639
|
GraphClientImpl,
|
|
1628
|
-
createGraphClientFromBackend
|
|
1640
|
+
createGraphClientFromBackend,
|
|
1641
|
+
DEFAULT_CORE_INDEXES
|
|
1629
1642
|
};
|
|
1630
|
-
//# sourceMappingURL=chunk-
|
|
1643
|
+
//# sourceMappingURL=chunk-6SB34IPQ.js.map
|