process-graph 1.2.0 → 1.4.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 (56) hide show
  1. package/README.md +25 -20
  2. package/dist/bin.js +4 -3
  3. package/dist/cli.d.ts +87 -75
  4. package/dist/cli.js +4 -2
  5. package/dist/data-file-BVMxkTug.js +95 -0
  6. package/dist/{dist-BxHnxpcT.js → dist-C8zDuW-y.js} +293 -510
  7. package/dist/dist-DipyhCvb.js +4 -0
  8. package/dist/{dist-BpCBmnMl.js → dist-arkejVz2.js} +2 -112
  9. package/dist/graphql-3DSUSUvL.d.ts +17 -0
  10. package/dist/{index-CoA6vXqe.d.ts → index-4f4PVH7K.d.ts} +23 -2
  11. package/dist/index-Bb90VKMc.d.ts +71 -0
  12. package/dist/{index-DxWOY8f9.d.ts → index-CR8xUK3S.d.ts} +5 -572
  13. package/dist/{index-CptUHq6F.d.ts → index-Cp5selgs.d.ts} +2 -1
  14. package/dist/{graphql-C1j5wQFz.d.ts → index-Cs1CWlEg.d.ts} +3 -16
  15. package/dist/{index-BH1aFqkJ.d.ts → index-D4CnOhNf.d.ts} +1 -1
  16. package/dist/index-DoMIav3D.d.ts +118 -0
  17. package/dist/{index-CmZOswkx.d.ts → index-Dvz_I_uQ.d.ts} +17 -8
  18. package/dist/{index-BUheRLdG.d.ts → index-LWdwpjZG.d.ts} +5 -24
  19. package/dist/{index-CrM5qWUA.d.ts → index-RwwlDYZ7.d.ts} +2 -2
  20. package/dist/index.d.ts +25 -4
  21. package/dist/index.js +4 -3
  22. package/dist/layout.d.ts +1 -1
  23. package/dist/project-CP_ye6KI.js +27 -0
  24. package/dist/react-flow.d.ts +5 -5
  25. package/dist/react-flow.js +1 -1
  26. package/dist/react.d.ts +2 -1
  27. package/dist/react.js +1 -24
  28. package/dist/{router-DvGGLnNt.js → router-Dk91O_9p.js} +2 -2
  29. package/dist/{schema-CVnS6HiX.js → schema-BPT8fmfa.js} +2 -2
  30. package/dist/schema-DVAttrJg.js +126 -0
  31. package/dist/source/github/node.d.ts +1 -1
  32. package/dist/source/github.d.ts +3 -2
  33. package/dist/source/github.js +38 -1
  34. package/dist/{source-DRDpEA8Q.js → source-lTKshktI.js} +24 -163
  35. package/dist/store/node.d.ts +27 -2
  36. package/dist/store/node.js +2 -2
  37. package/dist/store/wasm/worker.js +2 -2
  38. package/dist/store/wasm.d.ts +1 -1
  39. package/dist/{store-BJILV4JS.js → store-DKzvLGgl.js} +1 -1
  40. package/dist/store.d.ts +1 -1
  41. package/dist/store.js +2 -2
  42. package/dist/sync-CkaJO3OQ.js +213 -0
  43. package/dist/sync.d.ts +26 -0
  44. package/dist/sync.js +16 -0
  45. package/dist/theme-values-BJJhAkUn-ORyRUrPf.js +114 -0
  46. package/dist/theme-values-BybZ-cle.d.ts +116 -0
  47. package/dist/ui/theme.d.ts +2 -0
  48. package/dist/ui/theme.js +2 -0
  49. package/dist/ui.d.ts +6 -120
  50. package/dist/ui.js +2 -1
  51. package/dist/version-DMNBcTZ-.js +5 -0
  52. package/dist/view-DTeNUhGG.js +392 -0
  53. package/package.json +34 -23
  54. package/dist/dist-Cw63tKU9.js +0 -3
  55. package/dist/open-BF-UHqeW.js +0 -29
  56. package/dist/parse-CuipC_2K.js +0 -41
package/README.md CHANGED
@@ -40,24 +40,27 @@ deriveStatuses(model, { submit: "done" }, facts); // Map { "submit" => "done", "
40
40
 
41
41
  The package is built from the repository's internal packages, which are bundled into it and not published on their own; each internal package's README documents its functions and rules. Each subpath loads only the code it reaches.
42
42
 
43
- | Import | What it is | Runs in |
44
- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
45
- | `process-graph` | The model (`defineModel`, `parseModel`, `validate`), its projections and inverses, and the reasoning over a run (statuses, ready set, blocked-by, impact, what-if, critical path). | Browser, Worker, Node |
46
- | `process-graph/layout` | `layout`: positions for a projected flowchart, swimlane or state machine, in the model's `views` shape, through elkjs. | Browser, Worker, Node |
47
- | `process-graph/source/github` | The `GraphSource` port (contract, schemas, router, pagination and an in-memory fake) and the GitHub issues adapter, `createGitHubIssuesSource`. | Browser, Worker, Node |
48
- | `process-graph/source/github/node` | `githubTokenFromEnvironment`: a token port reading `GITHUB_TOKEN` or the GitHub CLI's token. | Node |
49
- | `process-graph/store` | The `GraphStore` port (contract, schemas, router, change feed, structural queries and an in-memory fake) and the SQLite store over any Drizzle SQLite database. | Browser, Worker, Node |
50
- | `process-graph/store/node` | `openNodeGraphStore`: the SQLite store on Node's built-in `node:sqlite`. | Node |
51
- | `process-graph/store/wasm` | `connectGraphStore`: connects a page to the store a dedicated worker serves. | Browser |
52
- | `process-graph/store/wasm/worker` | `serveGraphStoreInWorker`: serves the store from a dedicated worker on SQLite wasm and OPFS, shared between tabs with no special headers. | Dedicated worker |
53
- | `process-graph/react` | Headless React hooks and unstyled components: follow a stored model, derive its run, project, filter and select. | Browser |
54
- | `process-graph/ui` | Styled components over `process-graph/react`, the SVG sequence diagram and the theme contract with light and dark themes. | Browser |
55
- | `process-graph/ui/style.css` | The compiled styles for `process-graph/ui`. | Browser |
56
- | `process-graph/react-flow` | React Flow views of a flowchart, swimlane or state machine, laid out by `process-graph/layout`. | Browser |
57
- | `process-graph/react-flow/style.css` | The compiled styles for `process-graph/react-flow`, React Flow's own included. | Browser |
58
- | `process-graph/cli` | The command line as a library: `runCli` over injected ports, `nodePorts` for a Node process, and each command's operation returning plain data. | Node |
59
-
60
- `zod` and `trilean`, which the root entry needs, are dependencies. The libraries only other subpaths need are optional peer dependencies, so installing `process-graph` for its root entry pulls in nothing else; install the peers of each subpath you import beside it:
43
+ | Import | What it is | Runs in |
44
+ | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
45
+ | `process-graph` | The model (`defineModel`, `parseModel`, `validate`), its projections and inverses, each projected view as Mermaid, DOT or JSON text (`viewText`), and the reasoning over a run (statuses, ready set, blocked-by, impact, what-if, critical path). | Browser, Worker, Node |
46
+ | `process-graph/layout` | `layout`: positions for a projected flowchart, swimlane or state machine, in the model's `views` shape, through elkjs. | Browser, Worker, Node |
47
+ | `process-graph/source/github` | The `GraphSource` port (contract, schemas, router, pagination and an in-memory fake) and the GitHub issues adapter, `createGitHubIssuesSource`. | Browser, Worker, Node |
48
+ | `process-graph/source/github/node` | `githubTokenFromEnvironment`: a token port reading `GITHUB_TOKEN` or the GitHub CLI's token. | Node |
49
+ | `process-graph/store` | The `GraphStore` port (contract, schemas, router, change feed, structural queries and an in-memory fake) and the SQLite store over any Drizzle SQLite database. | Browser, Worker, Node |
50
+ | `process-graph/store/node` | `openNodeGraphStore`: the SQLite store on Node's built-in `node:sqlite`; `prepareDataFile`: makes its data file and directory private before it opens. | Node |
51
+ | `process-graph/store/wasm` | `connectGraphStore`: connects a page to the store a dedicated worker serves. | Browser |
52
+ | `process-graph/store/wasm/worker` | `serveGraphStoreInWorker`: serves the store from a dedicated worker on SQLite wasm and OPFS, shared between tabs with no special headers. | Dedicated worker |
53
+ | `process-graph/sync` | `syncScopes`: syncs sources into a store page by page, relations included, resumable from a checkpoint and narrowed by a typed filter. | Browser, Worker, Node |
54
+ | `process-graph/react` | Headless React hooks and unstyled components: follow a stored model, derive its run, project, filter and select. | Browser |
55
+ | `process-graph/ui` | Styled components over `process-graph/react`, the SVG sequence diagram and the theme contract with light and dark themes. | Browser |
56
+ | `process-graph/ui/theme` | The theme contract `vars`, `lightTheme`, `darkTheme`, `lightValues` and `darkValues` without the components, for a Vanilla Extract stylesheet that themes `process-graph/ui` without loading React. | Any |
57
+ | `process-graph/ui/style.css` | The compiled styles for `process-graph/ui`. | Browser |
58
+ | `process-graph/react-flow` | React Flow views of a flowchart, swimlane or state machine, laid out by `process-graph/layout`. | Browser |
59
+ | `process-graph/react-flow/style.css` | The compiled styles for `process-graph/react-flow`, React Flow's own included. | Browser |
60
+ | `process-graph/cli` | The command line as a library: `runCli` over injected ports, `nodePorts` for a Node process, each command's operation returning plain data, and `version`, this package's own. | Node |
61
+ | `process-graph/package.json` | The package's manifest, for reading its version or anything else it declares. | Any |
62
+
63
+ `zod` and `trilean`, which the root entry needs, are dependencies; a project with an install age gate exempts `trilean` and `trilean-regex` beside `process-graph` (see [Install](../../../README.md#install)). The libraries only other subpaths need are optional peer dependencies, so installing `process-graph` for its root entry pulls in nothing else; install the peers of each subpath you import beside it:
61
64
 
62
65
  | Subpath | Peer dependencies |
63
66
  | ------------------- | ---------------------------------------------------------------------------- |
@@ -67,12 +70,14 @@ The package is built from the repository's internal packages, which are bundled
67
70
  | `store/node` | `drizzle-orm` |
68
71
  | `store/wasm` | `@orpc/client` |
69
72
  | `store/wasm/worker` | `@orpc/contract`, `@orpc/server`, `drizzle-orm`, `@sqlite.org/sqlite-wasm` |
73
+ | `sync` | `@orpc/client` |
70
74
  | `react` | `react` |
71
75
  | `ui` | `react`, `@vanilla-extract/recipes`, `@vanilla-extract/css` (types) |
76
+ | `ui/theme` | `@vanilla-extract/css` (types) |
72
77
  | `react-flow` | `react`, `react-dom`, `@xyflow/react`, `elkjs`, `@vanilla-extract/recipes` |
73
78
  | `cli` and the bin | `commander`, `@orpc/client`, `@orpc/contract`, `@orpc/server`, `drizzle-orm` |
74
79
 
75
- The versions are the ones `package.json` declares under `peerDependencies`.
80
+ The versions are the ones `package.json` declares under `peerDependencies`. A peer with a stable version is a caret range whose floor is the version this package is tested against, so any later compatible release satisfies it. The oRPC packages, `drizzle-orm` and `@sqlite.org/sqlite-wasm` are prereleases, which have no compatible-range semantics (a range on one admits only later prereleases of the same version, which may break anything), so each is declared at the exact version tested.
76
81
 
77
82
  With Vite, exclude `@sqlite.org/sqlite-wasm` from dependency pre-bundling (`optimizeDeps.exclude`), since it loads its `.wasm` file relative to itself. A worker module serving the browser store is one call:
78
83
 
@@ -117,7 +122,7 @@ Run without them, the bin starts nothing: it names the missing peers and the com
117
122
 
118
123
  ## What is exported, and under which name
119
124
 
120
- The root entry exports the model, projection and domain layers flat, under their own names, and the DAG layer as the namespace `dag`, in its entirety: `dag.buildDag`, `dag.topologicalLayers`, `dag.criticalPath`, `dag.CycleError` and the rest. The DAG layer is namespaced because its `criticalPath` has the same name as the domain's, which answers a different question (the heaviest chain through the work still ahead of a run, not through a bare node set). The whole layer is namespaced rather than only the colliding name, so a name added to a layer later cannot start colliding silently.
125
+ The root entry exports the model, projection, export and domain layers flat, under their own names, and the DAG layer as the namespace `dag`, in its entirety: `dag.buildDag`, `dag.topologicalLayers`, `dag.criticalPath`, `dag.CycleError` and the rest. The DAG layer is namespaced because its `criticalPath` has the same name as the domain's, which answers a different question (the heaviest chain through the work still ahead of a run, not through a bare node set). The whole layer is namespaced rather than only the colliding name, so a name added to a layer later cannot start colliding silently.
121
126
 
122
127
  - A name is never shadowed. Two layers exporting the same name on one entry point is a compile error, and the unit tests compare each entry point's runtime exports with the layers it carries.
123
128
  - Types follow the same rule as values: a model, projection or domain type is a flat import, a DAG type is `dag.Dag` and so on.
package/dist/bin.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as exitCodes } from "./exit-codes-0ultCqdS.js";
3
+ import { t as version } from "./version-DMNBcTZ-.js";
3
4
  import { readFileSync } from "node:fs";
4
5
  //#region src/missing-peers.ts
5
6
  /**
@@ -66,7 +67,7 @@ const manifest = new URL("../package.json", import.meta.url);
66
67
  const declaredPeers = () => {
67
68
  const parsed = JSON.parse(readFileSync(manifest, "utf8"));
68
69
  if (typeof parsed !== "object" || parsed === null || !("peerDependencies" in parsed) || typeof parsed.peerDependencies !== "object" || parsed.peerDependencies === null) throw new Error(`${manifest.pathname} declares no peerDependencies`);
69
- return new Map(Object.entries(parsed.peerDependencies).flatMap(([name, version]) => typeof version === "string" ? [[name, version]] : []));
70
+ return new Map(Object.entries(parsed.peerDependencies).flatMap(([name, specifier]) => typeof specifier === "string" ? [[name, specifier]] : []));
70
71
  };
71
72
  /** Whether a package resolves from this bin's own location, as the command line's imports would. Only a package that is not installed counts as missing; any other resolution error is the import's to report. */
72
73
  const resolves = (name) => {
@@ -83,10 +84,10 @@ if (missing.length > 0) {
83
84
  process.stderr.write(missingPeersMessage(missing, declaredPeers(), process.env.npm_config_user_agent));
84
85
  process.exitCode = exitCodes.missingPeers;
85
86
  } else {
86
- const { packageVersion, runProcess } = await import("./dist-Cw63tKU9.js");
87
+ const { runProcess } = await import("./dist-DipyhCvb.js");
87
88
  process.exitCode = await runProcess({
88
89
  name: "process-graph",
89
- version: packageVersion(manifest)
90
+ version
90
91
  });
91
92
  }
92
93
  //#endregion
package/dist/cli.d.ts CHANGED
@@ -1,9 +1,12 @@
1
- import { A as Facts, B as Status, _ as StatusRule, k as FactRecord, m as IncomingBasis, p as ArtefactBasis } from "./index-BH1aFqkJ.js";
2
- import { E as Participant, F as OverlayStatus, G as IdSpace, K as OpenIds, S as EventDef, a as Overlay, b as ArtefactDef, g as Model, k as Transition, x as Element } from "./index-CmZOswkx.js";
3
- import { C as Gap, n as GraphqlFetch, t as GitHubTokenPort, v as GraphSourceClient } from "./graphql-C1j5wQFz.js";
4
- import { ut as GraphStoreClient } from "./index-DxWOY8f9.js";
5
- import "./index-CptUHq6F.js";
6
- import { A as Flowchart, E as Swimlane, M as Handoff, g as SequenceDiagram, x as StateMachine } from "./index-CoA6vXqe.js";
1
+ import { A as Facts, B as Status, _ as StatusRule, k as FactRecord, m as IncomingBasis, p as ArtefactBasis } from "./index-D4CnOhNf.js";
2
+ import { A as Transition, C as EventDef, D as Participant, I as OverlayStatus, S as Element, _ as Model, o as Overlay, q as OpenIds, x as ArtefactDef } from "./index-Dvz_I_uQ.js";
3
+ import { P as SyncFilter, b as Gap, h as GraphSourceClient } from "./index-Cs1CWlEg.js";
4
+ import { ut as GraphStoreClient } from "./index-CR8xUK3S.js";
5
+ import { n as GraphqlFetch, t as GitHubTokenPort } from "./graphql-3DSUSUvL.js";
6
+ import "./index-Cp5selgs.js";
7
+ import { F as Handoff, N as Flowchart, k as Swimlane, w as StateMachine, y as SequenceDiagram } from "./index-4f4PVH7K.js";
8
+ import { n as SourceLookup } from "./index-DoMIav3D.js";
9
+ import { a as flowchartToMermaid, c as swimlaneToMermaid, i as swimlaneToDot, n as sequenceToDot, o as sequenceToMermaid, r as stateMachineToDot, s as stateMachineToMermaid, t as flowchartToDot } from "./index-Bb90VKMc.js";
7
10
  import { ComputedValue } from "trilean";
8
11
  //#region ../graph-cli/dist/exit-codes.d.ts
9
12
  /**
@@ -118,6 +121,10 @@ interface NodePortsOptions {
118
121
  readonly env?: Readonly<Record<string, string | undefined>>;
119
122
  /** The home directory the default data path falls back on; the user's when absent. */
120
123
  readonly home?: string;
124
+ /**
125
+ * A data file the host owns, such as one under its own home directory. It becomes the default data path in place of `PROCESS_GRAPH_DATA` and the XDG data file, and it gets the treatment the command line gives its own XDG data file: its directory, and any missing directories above it, are created readable by the owner only and kept that way, the database is created owner-only before SQLite opens it, and an existing store others can read is narrowed, with a warning on standard error (see `prepareDataFile` in `process-graph/store/node`). A path given with `--data` still wins, and is treated as the user's. The XDG data file when absent.
126
+ */
127
+ readonly dataPath?: string;
121
128
  /** Supplies GitHub tokens; `GITHUB_TOKEN` or the GitHub CLI's token (`githubTokenFromEnvironment`) when absent. */
122
129
  readonly token?: GitHubTokenPort;
123
130
  /** How requests reach GitHub; the global `fetch` when absent. */
@@ -150,7 +157,7 @@ export declare const defaultDataPath: (env: Readonly<Record<string, string | und
150
157
  */
151
158
  export declare const packageVersion: (manifest: URL) => string;
152
159
  /**
153
- * The ports for a Node process: the SQLite store over `node:sqlite` (creating the data file's directory when needed), the GitHub issues source for `github:` scopes, the file system and the process's streams.
160
+ * The ports for a Node process: the SQLite store over `node:sqlite` (creating the data file's directory when needed, private when the command line or the host owns it), the GitHub issues source for `github:` scopes, the file system and the process's streams.
154
161
  * @param options - Replacements for any of the defaults.
155
162
  * @returns The ports.
156
163
  */
@@ -259,6 +266,22 @@ interface WhyResult {
259
266
  /** The element, then every unfinished need reached from it, in model order. */
260
267
  readonly chain: readonly WhyLink[];
261
268
  }
269
+ /** An element of a hierarchy walk, with how many `child-of` relations away it is. */
270
+ interface HierarchyLink extends ElementSummary {
271
+ readonly distance: number;
272
+ }
273
+ /** An element's sub-tasks: the elements related to it by `child-of`. */
274
+ interface ChildrenResult {
275
+ readonly element: ElementSummary;
276
+ /** Its direct children, in model order. */
277
+ readonly children: readonly ElementSummary[];
278
+ }
279
+ /** What an element is a sub-task of, up to the top of its hierarchy. */
280
+ interface AncestorsResult {
281
+ readonly element: ElementSummary;
282
+ /** Its parent, its parent's parent and so on, nearest first, each once with the fewest relations it takes to reach it. */
283
+ readonly ancestors: readonly HierarchyLink[];
284
+ }
262
285
  /**
263
286
  * The elements that are ready: not done, with every live need done.
264
287
  * @param session - The store, model and facts.
@@ -297,13 +320,27 @@ export declare const handoffsOf: (session: GraphSession) => Promise<HandoffsResu
297
320
  * @param id - The element asked about.
298
321
  */
299
322
  export declare const why: (session: GraphSession, id: string) => Promise<WhyResult>;
323
+ /**
324
+ * An element's direct sub-tasks, from the `child-of` relations a sync keeps, as the store's hierarchy query answers.
325
+ * @param session - The store and model.
326
+ * @param id - The element asked about.
327
+ * @throws CliError with `notFound` when the model or the element does not exist.
328
+ */
329
+ export declare const children: (session: GraphSession, id: string) => Promise<ChildrenResult>;
330
+ /**
331
+ * The elements an element is a sub-task of, nearest first, from the `child-of` relations a sync keeps, as the store's hierarchy query answers.
332
+ * @param session - The store and model.
333
+ * @param id - The element asked about.
334
+ * @throws CliError with `notFound` when the model or the element does not exist.
335
+ */
336
+ export declare const ancestors: (session: GraphSession, id: string) => Promise<AncestorsResult>;
300
337
  //#endregion
301
338
  //#region ../graph-cli/dist/show.d.ts
302
339
  /** The views `show` draws, as named on the command line. */
303
340
  export declare const viewNames: readonly ["flowchart", "swimlane", "state", "sequence"];
304
341
  /** One of the views `show` draws. */
305
342
  type ViewName = (typeof viewNames)[number];
306
- /** The formats `show` writes. */
343
+ /** The formats `show` writes, graph-export's, with JSON named first. */
307
344
  export declare const formatNames: readonly ["json", "mermaid", "dot"];
308
345
  /** One of the formats `show` writes. */
309
346
  type FormatName = (typeof formatNames)[number];
@@ -344,13 +381,13 @@ type ShowResult = ProjectedView & {
344
381
  */
345
382
  export declare const project: (model: Model<OpenIds>, request: Pick<ShowRequest, "view" | "subject" | "scenario">) => ProjectedView;
346
383
  /**
347
- * Writes a projected view as Mermaid or DOT source.
384
+ * Writes a projected view as Mermaid or DOT source, or as JSON, through graph-export's `viewText`, the writer the web app's downloads use too.
348
385
  * @param model - The model, for participant labels.
349
386
  * @param projected - The view.
350
- * @param format - `mermaid` or `dot`.
351
- * @returns The diagram source.
387
+ * @param format - `mermaid`, `dot` or `json`.
388
+ * @returns The text.
352
389
  */
353
- export declare const emit: (model: Model<OpenIds>, projected: ProjectedView, format: Exclude<FormatName, "json">) => string;
390
+ export declare const emit: (model: Model<OpenIds>, projected: ProjectedView, format: FormatName) => string;
354
391
  /**
355
392
  * Draws the session's model as a view in a format.
356
393
  * @param session - The store and model.
@@ -476,91 +513,66 @@ export declare const validateStored: (session: GraphSession) => Promise<Validate
476
513
  export declare const validateDocument: (text: string) => ValidateResult;
477
514
  //#endregion
478
515
  //#region ../graph-cli/dist/sync.d.ts
479
- /** Finds the source that serves scopes with an identifier prefix, such as `github`. */
480
- type SourceLookup = (prefix: string) => GraphSourceClient | undefined;
481
516
  /** What to sync. */
482
517
  interface SyncRequest {
483
518
  /** Source-qualified scope identifiers, such as `github:owner` or `github:owner/repo`, read in order. */
484
519
  readonly scopes: readonly string[];
485
- /** A cursor from an interrupted sync: the first source's sync continues after it. */
520
+ /** A cursor from an interrupted sync: the first source's sync continues after it. It is valid only with the filter it was issued under. */
486
521
  readonly after?: string;
522
+ /** Narrows and orders what each scope lists, on the source's side. */
523
+ readonly filter?: SyncFilter;
524
+ }
525
+ /** Where a sync a source interrupted carries on, as data a host acts on; the command line renders it as the commands to run. */
526
+ interface SyncInterruption {
527
+ /** The request that continues the failing source: its scopes and the sync's filter, after the cursor of the last page read from it, or from the start when none was read. */
528
+ readonly resume: SyncRequest;
529
+ /** The scopes of the sources after the failing one, which were not read; sync them afresh, under the same filter, once `resume` has finished. */
530
+ readonly remaining: readonly string[];
531
+ /** Whether pages read before the failure were written to the model. */
532
+ readonly saved: boolean;
533
+ }
534
+ /**
535
+ * Thrown by `sync` when a source fails part way. Its exit status and message are the source's failure, classified as `failureOf` classifies it; its cause is the error underneath that failure, such as the token port's own error; and what to run next is data, in `resume` and `remaining`, never text.
536
+ */
537
+ export declare class SyncInterruptedError extends CliError implements SyncInterruption {
538
+ readonly resume: SyncRequest;
539
+ readonly remaining: readonly string[];
540
+ readonly saved: boolean;
541
+ /**
542
+ * @param exitCode - The exit status the source's failure maps to.
543
+ * @param message - The source's failure.
544
+ * @param interruption - Where the sync carries on.
545
+ * @param options - The underlying error, kept as the cause.
546
+ */
547
+ constructor(exitCode: CliError["exitCode"], message: string, interruption: SyncInterruption, options?: ErrorOptions);
487
548
  }
488
549
  /** What a sync wrote. */
489
550
  interface SyncResult {
490
551
  readonly modelId: string;
491
- /** The revision the write took. */
552
+ /** The revision the last write took. */
492
553
  readonly revision: number;
493
- /** The participants, activities and transitions written, each identifier counted once. */
554
+ /** The participants, activities, transitions and relations written, each counted once. */
494
555
  readonly participants: number;
495
556
  readonly activities: number;
496
557
  readonly transitions: number;
558
+ readonly relations: number;
497
559
  /** The parts of the scope the sources could not read, in the order they were met. */
498
560
  readonly gaps: readonly Gap[];
499
561
  }
500
562
  /**
501
- * Reads every page of the scopes from their sources and merges them into the session's model in one write, creating the model when the store has none. Records already in the model are replaced by identifier and the rest are kept, so syncing again refreshes what changed.
563
+ * Reads every page of the scopes from their sources and merges each into the session's model as it is read, creating the model when the store has none: participants, bound activities, transitions, relations (sub-issues, assignees, repository membership) and run state. Records already in the model are replaced by identifier and the rest are kept, so syncing again refreshes what changed.
502
564
  *
503
- * When a source fails part way, the pages read before the failure are still written, and the error says how to resume: the failing source's scopes with `--after` and the cursor of its last page read.
565
+ * When a source fails part way, the pages read before the failure are already written, and the error carries where to carry on: the request that resumes the failing source after the cursor of its last page read, and the scopes of the sources not reached.
504
566
  * @param session - The store and model.
505
567
  * @param sources - The source for each identifier prefix.
506
- * @param request - The scopes, and a cursor to resume after.
568
+ * @param request - The scopes, a cursor to resume after, and a filter.
507
569
  * @returns What was written, and the gaps the sources reported.
508
- * @throws CliError with `usage` for a scope with no source prefix, a prefix no source serves, or `after` with scopes of more than one source; and with the source's failure, the resume command added, when a source refuses the sync.
570
+ * @throws CliError with `usage` for a scope with no source prefix, a prefix no source serves, or `after` with scopes of more than one source; and SyncInterruptedError, with the source's failure and where to carry on, when a source fails part way.
509
571
  */
510
572
  export declare const sync: (session: GraphSession, sources: SourceLookup, request: SyncRequest) => Promise<SyncResult>;
511
573
  //#endregion
512
- //#region ../graph-cli/dist/emit/mermaid.d.ts
513
- /**
514
- * A flowchart as a Mermaid `flowchart`, top to bottom. Nodes are declared in the view's order, then edges, each with its guard as the edge label.
515
- * @param view - The projected flowchart.
516
- * @returns Mermaid source, ending with a line break.
517
- */
518
- export declare const flowchartToMermaid: <I extends IdSpace>(view: Flowchart<I>) => string;
519
- /**
520
- * A swimlane as a Mermaid `flowchart`, left to right, with one subgraph per lane in the view's lane order (an empty lane is an empty subgraph, so it stays visible) and nodes with no actor outside every lane. Hand-offs, the edges that change lane, are drawn thick.
521
- * @param view - The projected swimlane.
522
- * @returns Mermaid source, ending with a line break.
523
- */
524
- export declare const swimlaneToMermaid: <I extends IdSpace>(view: Swimlane<I>) => string;
525
- /**
526
- * A state machine as a Mermaid `stateDiagram-v2`. Initial and final pseudo-states are Mermaid's `[*]`; every other state is declared with its label. Each transition carries its event, guard and effect.
527
- * @param view - The projected state machine.
528
- * @returns Mermaid source, ending with a line break.
529
- */
530
- export declare const stateMachineToMermaid: <I extends IdSpace>(view: StateMachine<I>) => string;
531
- /**
532
- * A sequence diagram as a Mermaid `sequenceDiagram`. Lifelines are declared in order of first appearance, labelled through `participantLabel`; messages follow in scenario order, wrapped in the fragments the projection drew (`alt` operands separated by `else`, `par` operands by `and`), each operand headed by its guard.
533
- * @param view - The projected sequence diagram.
534
- * @param participantLabel - The label of a lifeline's participant.
535
- * @returns Mermaid source, ending with a line break.
536
- */
537
- export declare const sequenceToMermaid: <I extends IdSpace>(view: SequenceDiagram<I>, participantLabel: (participant: I["actor"]) => string) => string;
538
- //#endregion
539
- //#region ../graph-cli/dist/emit/dot.d.ts
540
- /**
541
- * A flowchart as a Graphviz `digraph`, top to bottom. Nodes are named by their model identifiers, so the DOT source reads back to the model directly.
542
- * @param view - The projected flowchart.
543
- * @returns DOT source, ending with a line break.
544
- */
545
- export declare const flowchartToDot: <I extends IdSpace>(view: Flowchart<I>) => string;
546
- /**
547
- * A swimlane as a Graphviz `digraph`, left to right, with one `cluster` subgraph per lane in the view's lane order and nodes with no actor outside every lane. An empty lane holds one invisible point so Graphviz still draws it. Hand-offs, the edges that change lane, are drawn bold.
548
- * @param view - The projected swimlane.
549
- * @returns DOT source, ending with a line break.
550
- */
551
- export declare const swimlaneToDot: <I extends IdSpace>(view: Swimlane<I>) => string;
552
- /**
553
- * A state machine as a Graphviz `digraph`, left to right. Each transition is labelled with its event, guard and effect.
554
- * @param view - The projected state machine.
555
- * @returns DOT source, ending with a line break.
556
- */
557
- export declare const stateMachineToDot: <I extends IdSpace>(view: StateMachine<I>) => string;
558
- /**
559
- * A sequence diagram as a Graphviz `digraph`. DOT has no sequence notation, so the diagram is drawn as a grid: a column per lifeline, headed by its participant's label, with one row per message in scenario order. Each message is an arrow across its row, numbered by its step and prefixed with the fragments around it (operator and guard, outermost first), since DOT cannot draw a box around arrows.
560
- * @param view - The projected sequence diagram.
561
- * @param participantLabel - The label of a lifeline's participant.
562
- * @returns DOT source, ending with a line break.
563
- */
564
- export declare const sequenceToDot: <I extends IdSpace>(view: SequenceDiagram<I>, participantLabel: (participant: I["actor"]) => string) => string;
574
+ //#region src/version.d.ts
575
+ /** The version of the installed `process-graph` package, as its manifest names it. The bundler writes it in when the package is built, so reading it touches no file. */
576
+ export declare const version: string;
565
577
  //#endregion
566
- export type { Addition, ArtefactState, BlockedResult, CliPorts, CyclesResult, ElementStatus, ElementSummary, ExitCode, FactsResult, Failure, FormatName, GraphSession, HandoffsResult, ImpactResult, NodePortsOptions, OpenedStore, PathResult, ProgramIdentity, ProjectedView, ReadyResult, SetArtefactResult, SetFactResult, SetResult, ShowRequest, ShowResult, SourceLookup, StoredRun, SyncRequest, SyncResult, ValidateResult, ValidationIssue, ViewName, WhyLink, WhyResult, Written };
578
+ export { type Addition, type AncestorsResult, type ArtefactState, type BlockedResult, type ChildrenResult, type CliPorts, type CyclesResult, type ElementStatus, type ElementSummary, type ExitCode, type FactsResult, type Failure, type FormatName, type GraphSession, type HandoffsResult, type HierarchyLink, type ImpactResult, type NodePortsOptions, type OpenedStore, type PathResult, type ProgramIdentity, type ProjectedView, type ReadyResult, type SetArtefactResult, type SetFactResult, type SetResult, type ShowRequest, type ShowResult, type SourceLookup, type StoredRun, type SyncInterruption, type SyncRequest, type SyncResult, type ValidateResult, type ValidationIssue, type ViewName, type WhyLink, type WhyResult, type Written, flowchartToDot, flowchartToMermaid, sequenceToDot, sequenceToMermaid, stateMachineToDot, stateMachineToMermaid, swimlaneToDot, swimlaneToMermaid };
package/dist/cli.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { a as stateMachineToDot, c as sequenceToMermaid, i as sequenceToDot, l as stateMachineToMermaid, o as swimlaneToDot, r as flowchartToDot, s as flowchartToMermaid, u as swimlaneToMermaid } from "./view-DTeNUhGG.js";
1
2
  import { t as exitCodes } from "./exit-codes-0ultCqdS.js";
2
- import { A as add, B as failureOf, C as blocked, D as path, E as impactOf, F as setState, I as findRun, L as loadRun, M as factsOf, N as setArtefact, O as ready, P as setFact, R as runFacts, S as swimlaneToMermaid, T as handoffsOf, _ as stateMachineToDot, a as defaultModelId, b as sequenceToMermaid, c as validateStored, d as formatNames, f as project, g as sequenceToDot, h as flowchartToDot, i as runProcess, j as artefactStates, k as why, l as sync, m as viewNames, n as nodePorts, o as runCli, p as show, r as packageVersion, s as validateDocument, t as defaultDataPath, u as emit, v as swimlaneToDot, w as cycles, x as stateMachineToMermaid, y as flowchartToMermaid, z as CliError } from "./dist-BxHnxpcT.js";
3
- export { CliError, add, artefactStates, blocked, cycles, defaultDataPath, defaultModelId, emit, exitCodes, factsOf, failureOf, findRun, flowchartToDot, flowchartToMermaid, formatNames, handoffsOf, impactOf, loadRun, nodePorts, packageVersion, path, project, ready, runCli, runFacts, runProcess, sequenceToDot, sequenceToMermaid, setArtefact, setFact, setState, show, stateMachineToDot, stateMachineToMermaid, swimlaneToDot, swimlaneToMermaid, sync, validateDocument, validateStored, viewNames, why };
3
+ import { A as setState, C as ready, D as factsOf, E as artefactStates, F as failureOf, M as loadRun, N as runFacts, O as setArtefact, P as CliError, S as path, T as add, _ as blocked, a as defaultModelId, b as handoffsOf, c as validateStored, d as project, f as show, g as ancestors, h as sync, i as runProcess, j as findRun, k as setFact, l as emit, m as SyncInterruptedError, n as nodePorts, o as runCli, p as viewNames, r as packageVersion, s as validateDocument, t as defaultDataPath, u as formatNames, v as children, w as why, x as impactOf, y as cycles } from "./dist-C8zDuW-y.js";
4
+ import { t as version } from "./version-DMNBcTZ-.js";
5
+ export { CliError, SyncInterruptedError, add, ancestors, artefactStates, blocked, children, cycles, defaultDataPath, defaultModelId, emit, exitCodes, factsOf, failureOf, findRun, flowchartToDot, flowchartToMermaid, formatNames, handoffsOf, impactOf, loadRun, nodePorts, packageVersion, path, project, ready, runCli, runFacts, runProcess, sequenceToDot, sequenceToMermaid, setArtefact, setFact, setState, show, stateMachineToDot, stateMachineToMermaid, swimlaneToDot, swimlaneToMermaid, sync, validateDocument, validateStored, version, viewNames, why };
@@ -0,0 +1,95 @@
1
+ import { t as createSqliteGraphStore } from "./store-DKzvLGgl.js";
2
+ import { sql } from "drizzle-orm";
3
+ import { DatabaseSync } from "node:sqlite";
4
+ import { drizzle } from "drizzle-orm/node-sqlite";
5
+ import { chmodSync, closeSync, mkdirSync, openSync, statSync } from "node:fs";
6
+ import { dirname } from "node:path";
7
+ //#region ../../adapters/graph/sqlite-node/dist/open.js
8
+ /** The path `node:sqlite` reads as a private database in memory rather than a file. */
9
+ const inMemory = ":memory:";
10
+ /**
11
+ * Opens a graph store on a SQLite database file, creating the file when it does not exist. The schema is migrated on the store's first operation.
12
+ *
13
+ * A file database is switched to write-ahead logging, so readers in other processes (a command line beside a server) are not blocked by a writer. Each process's change feed sees only its own writes.
14
+ * @param path - The database file, or `":memory:"` for a private database that lives as long as the store.
15
+ * @param options - `node:sqlite` options passed through unchanged, such as `timeout` for how long a write waits for another process's lock.
16
+ * @returns The store, its database and a way to close it.
17
+ */
18
+ const openNodeGraphStore = (path, options = {}) => {
19
+ const client = new DatabaseSync(path, options);
20
+ const db = drizzle({ client });
21
+ if (path !== inMemory) db.run(sql`PRAGMA journal_mode = WAL`);
22
+ return {
23
+ store: createSqliteGraphStore({ db }),
24
+ db,
25
+ close: () => {
26
+ client.close();
27
+ }
28
+ };
29
+ };
30
+ //#endregion
31
+ //#region ../../adapters/graph/sqlite-node/dist/data-file.js
32
+ /** The data directory's mode: only its owner may list or enter it, since the store holds synced work (titles, relationships) that other users on the host must not read. */
33
+ const privateDirectoryMode = 448;
34
+ /** The data file's mode: only its owner may read or write it. SQLite creates the `-wal` and `-shm` files beside it with the same mode on Unix. */
35
+ const privateFileMode = 384;
36
+ /** The permission bits of a file mode, without its type. */
37
+ const permissionBits = 511;
38
+ /** The bits that give the file's group or anyone else any access. */
39
+ const groupAndOtherBits = 63;
40
+ /** The SQLite files a store at `path` uses: the database and its write-ahead log and shared-memory index. */
41
+ const storeFiles = (path) => [
42
+ path,
43
+ `${path}-wal`,
44
+ `${path}-shm`
45
+ ];
46
+ /** Whether an error is the file system's report that a path does not exist, or already does. */
47
+ const hasCode = (error, code) => error instanceof Error && "code" in error && error.code === code;
48
+ /**
49
+ * Narrows a path's mode to `mode` when its group or others have any access.
50
+ * @param path - The path.
51
+ * @param mode - The private mode it should have.
52
+ * @returns Whether the mode was changed; `false` too when the path does not exist.
53
+ */
54
+ const tighten = (path, mode) => {
55
+ let current;
56
+ try {
57
+ current = statSync(path).mode & permissionBits;
58
+ } catch (error) {
59
+ if (hasCode(error, "ENOENT")) return false;
60
+ throw error;
61
+ }
62
+ if ((current & groupAndOtherBits) === 0) return false;
63
+ chmodSync(path, mode);
64
+ return true;
65
+ };
66
+ /**
67
+ * Makes a data file ready for `openNodeGraphStore` to open, so a store holding synced work is never readable by other users on the host. Call it before opening the store; it does not open the database itself.
68
+ *
69
+ * The directory is created when missing (with any missing parents), with `privateDirectoryMode` when it is the caller's own. The database file is created with `privateFileMode` when missing, before SQLite opens it, so the file and the `-wal` and `-shm` files SQLite derives from it are never readable by others, whatever the umask. A file that already exists is left alone unless the directory is the caller's own; there, an existing directory or store file (database, `-wal` or `-shm`) that group or others can read is narrowed to the private mode. On Windows, where file modes mean nothing, only the directory is created.
70
+ * @param path - The database file; not `":memory:"`, which has no file.
71
+ * @param options - Whether the caller owns the directory, and the platform.
72
+ * @returns One warning line naming what was narrowed, for the caller to show, or nothing when nothing was.
73
+ * @throws Error from the file system when the directory or file cannot be created or its mode read or changed.
74
+ */
75
+ const prepareDataFile = (path, options) => {
76
+ const directory = dirname(path);
77
+ if (options.platform === "win32") {
78
+ mkdirSync(directory, { recursive: true });
79
+ return [];
80
+ }
81
+ mkdirSync(directory, options.ownsDirectory ? {
82
+ recursive: true,
83
+ mode: 448
84
+ } : { recursive: true });
85
+ try {
86
+ closeSync(openSync(path, "wx", 384));
87
+ } catch (error) {
88
+ if (!hasCode(error, "EEXIST")) throw error;
89
+ }
90
+ if (!options.ownsDirectory) return [];
91
+ const narrowed = [...tighten(directory, 448) ? [directory] : [], ...storeFiles(path).filter((file) => tighten(file, 384))];
92
+ return narrowed.length === 0 ? [] : [`made ${narrowed.join(", ")} readable by its owner only, since other users could read the synced work in it`];
93
+ };
94
+ //#endregion
95
+ export { openNodeGraphStore as i, privateDirectoryMode as n, privateFileMode as r, prepareDataFile as t };