process-graph 1.1.1 → 1.3.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 (50) hide show
  1. package/README.md +22 -19
  2. package/dist/bin.js +4 -3
  3. package/dist/cli.d.ts +100 -14
  4. package/dist/cli.js +3 -2
  5. package/dist/{control-aDk9560d.js → control-DiygoEhm.js} +163 -1
  6. package/dist/data-file-BVMxkTug.js +95 -0
  7. package/dist/dist-81kTKnMq.js +3 -0
  8. package/dist/{dist-HLlW8vgT.js → dist-BI01yxFA.js} +256 -103
  9. package/dist/{dist-BpCBmnMl.js → dist-arkejVz2.js} +2 -112
  10. package/dist/{graphql-C1j5wQFz.d.ts → graphql-BE7xm5dB.d.ts} +3 -2
  11. package/dist/{index-DricKcyl.d.ts → index-9Au8pAmP.d.ts} +4 -4
  12. package/dist/{index-CrM5qWUA.d.ts → index-BC4tmAuQ.d.ts} +2 -2
  13. package/dist/{index-DhbvDgK4.d.ts → index-CR8xUK3S.d.ts} +388 -580
  14. package/dist/{index-CoA6vXqe.d.ts → index-D06bcdfw.d.ts} +1 -1
  15. package/dist/{index-CCfe0csl.d.ts → index-D4CnOhNf.d.ts} +34 -3
  16. package/dist/{index-CmZOswkx.d.ts → index-Dvz_I_uQ.d.ts} +17 -8
  17. package/dist/{index-CptUHq6F.d.ts → index-hri2a13-.d.ts} +1 -1
  18. package/dist/index.d.ts +4 -4
  19. package/dist/index.js +5 -5
  20. package/dist/layout.d.ts +1 -1
  21. package/dist/{parse-CuipC_2K.js → parse-C1Q1zsGt.js} +1 -1
  22. package/dist/{queries-CCexJAE2.js → queries-C8kIDRiY.js} +2 -93
  23. package/dist/react-flow.d.ts +5 -5
  24. package/dist/react-flow.js +3 -3
  25. package/dist/react.d.ts +1 -1
  26. package/dist/react.js +2 -2
  27. package/dist/{router-D4gtTlXm.js → router-Dk91O_9p.js} +27 -2
  28. package/dist/{schema-CVnS6HiX.js → schema-BPT8fmfa.js} +2 -2
  29. package/dist/{selection-BW_xXWOy.js → selection-iet5RAlP.js} +1 -1
  30. package/dist/source/github/node.d.ts +1 -1
  31. package/dist/source/github.d.ts +2 -2
  32. package/dist/source/github.js +1 -1
  33. package/dist/{source-DRDpEA8Q.js → source-D51N7K3A.js} +23 -3
  34. package/dist/store/node.d.ts +27 -2
  35. package/dist/store/node.js +2 -2
  36. package/dist/store/wasm/worker.js +2 -2
  37. package/dist/store/wasm.d.ts +1 -1
  38. package/dist/{store-BcR70CaO.js → store-DKzvLGgl.js} +252 -37
  39. package/dist/store.d.ts +115 -3
  40. package/dist/store.js +43 -9
  41. package/dist/theme-values-BJJhAkUn-ORyRUrPf.js +114 -0
  42. package/dist/theme-values-BybZ-cle.d.ts +116 -0
  43. package/dist/ui/theme.d.ts +2 -0
  44. package/dist/ui/theme.js +2 -0
  45. package/dist/ui.d.ts +6 -120
  46. package/dist/ui.js +2 -1
  47. package/dist/version-Dagrkvwt.js +5 -0
  48. package/package.json +28 -23
  49. package/dist/dist-IjrgWhHQ.js +0 -3
  50. package/dist/open-CyRXJ4bt.js +0 -29
package/README.md CHANGED
@@ -40,24 +40,26 @@ 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, 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/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/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 |
56
+ | `process-graph/ui/style.css` | The compiled styles for `process-graph/ui`. | Browser |
57
+ | `process-graph/react-flow` | React Flow views of a flowchart, swimlane or state machine, laid out by `process-graph/layout`. | Browser |
58
+ | `process-graph/react-flow/style.css` | The compiled styles for `process-graph/react-flow`, React Flow's own included. | Browser |
59
+ | `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 |
60
+ | `process-graph/package.json` | The package's manifest, for reading its version or anything else it declares. | Any |
61
+
62
+ `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
63
 
62
64
  | Subpath | Peer dependencies |
63
65
  | ------------------- | ---------------------------------------------------------------------------- |
@@ -69,10 +71,11 @@ The package is built from the repository's internal packages, which are bundled
69
71
  | `store/wasm/worker` | `@orpc/contract`, `@orpc/server`, `drizzle-orm`, `@sqlite.org/sqlite-wasm` |
70
72
  | `react` | `react` |
71
73
  | `ui` | `react`, `@vanilla-extract/recipes`, `@vanilla-extract/css` (types) |
74
+ | `ui/theme` | `@vanilla-extract/css` (types) |
72
75
  | `react-flow` | `react`, `react-dom`, `@xyflow/react`, `elkjs`, `@vanilla-extract/recipes` |
73
76
  | `cli` and the bin | `commander`, `@orpc/client`, `@orpc/contract`, `@orpc/server`, `drizzle-orm` |
74
77
 
75
- The versions are the ones `package.json` declares under `peerDependencies`.
78
+ 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
79
 
77
80
  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
81
 
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-Dagrkvwt.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-IjrgWhHQ.js");
87
+ const { runProcess } = await import("./dist-81kTKnMq.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,10 @@
1
- import { F as Status, _ as StatusRule, k as Facts, m as IncomingBasis, p as ArtefactBasis } from "./index-CCfe0csl.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 { Q as GraphStoreClient } from "./index-DhbvDgK4.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, K as IdSpace, S as Element, _ as Model, o as Overlay, q as OpenIds, x as ArtefactDef } from "./index-Dvz_I_uQ.js";
3
+ import { C as Gap, n as GraphqlFetch, t as GitHubTokenPort, v as GraphSourceClient } from "./graphql-BE7xm5dB.js";
4
+ import { ut as GraphStoreClient } from "./index-CR8xUK3S.js";
5
+ import "./index-hri2a13-.js";
6
+ import { A as Flowchart, E as Swimlane, M as Handoff, g as SequenceDiagram, x as StateMachine } from "./index-D06bcdfw.js";
7
+ import { ComputedValue } from "trilean";
7
8
  //#region ../graph-cli/dist/exit-codes.d.ts
8
9
  /**
9
10
  * The exit status of every command, by meaning. A script tells the outcomes apart without reading the output: a model with problems is not a failure to run, and neither is an element that does not exist.
@@ -61,7 +62,7 @@ interface CliPorts {
61
62
  * @param signal - Aborts the read.
62
63
  */
63
64
  readonly readFile: (path: string, signal: AbortSignal | undefined) => Promise<string>;
64
- /** The facts that decision conditions read, and which input artefacts are provided. */
65
+ /** Facts the host supplies for decision conditions and input artefacts. The model's stored fact record (what `set artefact` and `set fact` recorded) answers first; these answer whatever it does not. */
65
66
  readonly facts: Facts;
66
67
  /** Aborts every store and source call a command makes. */
67
68
  readonly signal: AbortSignal | undefined;
@@ -117,13 +118,17 @@ interface NodePortsOptions {
117
118
  readonly env?: Readonly<Record<string, string | undefined>>;
118
119
  /** The home directory the default data path falls back on; the user's when absent. */
119
120
  readonly home?: string;
121
+ /**
122
+ * 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.
123
+ */
124
+ readonly dataPath?: string;
120
125
  /** Supplies GitHub tokens; `GITHUB_TOKEN` or the GitHub CLI's token (`githubTokenFromEnvironment`) when absent. */
121
126
  readonly token?: GitHubTokenPort;
122
127
  /** How requests reach GitHub; the global `fetch` when absent. */
123
128
  readonly fetch?: GraphqlFetch;
124
129
  /** The clock, in milliseconds since the epoch, for a rate limit's retry time; `Date.now` when absent. */
125
130
  readonly now?: () => number;
126
- /** The facts decision conditions read and which input artefacts are provided; nothing is known when absent. */
131
+ /** Facts the host supplies for decision conditions and input artefacts, answering whatever the model's stored fact record does not; nothing more is known when absent. */
127
132
  readonly facts?: Facts;
128
133
  /** Aborts every store and source call. */
129
134
  readonly signal?: AbortSignal;
@@ -149,7 +154,7 @@ export declare const defaultDataPath: (env: Readonly<Record<string, string | und
149
154
  */
150
155
  export declare const packageVersion: (manifest: URL) => string;
151
156
  /**
152
- * 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.
157
+ * 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.
153
158
  * @param options - Replacements for any of the defaults.
154
159
  * @returns The ports.
155
160
  */
@@ -162,20 +167,29 @@ export declare const nodePorts: (options?: NodePortsOptions) => CliPorts;
162
167
  export declare const runProcess: (identity: ProgramIdentity) => Promise<ExitCode>;
163
168
  //#endregion
164
169
  //#region ../graph-cli/dist/session.d.ts
165
- /** What every operation works against: one model in one store, the facts its conditions read, and the signal that aborts its calls. */
170
+ /** What every operation works against: one model in one store, the facts a host supplies for its conditions, and the signal that aborts its calls. */
166
171
  interface GraphSession {
167
172
  readonly store: GraphStoreClient;
168
173
  /** The name the model is filed under in the store. */
169
174
  readonly modelId: string;
175
+ /** Facts the host supplies, consulted for whatever the model's stored fact record does not answer (see `runFacts`). */
170
176
  readonly facts: Facts;
171
177
  readonly signal: AbortSignal | undefined;
172
178
  }
173
- /** A stored model and its run state, as an operation reads them. */
179
+ /** A stored model, its run state and its fact record, as an operation reads them. */
174
180
  interface StoredRun {
175
181
  readonly revision: number;
176
182
  readonly model: Model<OpenIds>;
177
183
  readonly overlay: Overlay<OpenIds>;
184
+ readonly facts: FactRecord;
178
185
  }
186
+ /**
187
+ * The facts a run's reasoning reads: the model's stored fact record first, then the session's own facts for every key the record does not answer. What `set artefact` and `set fact` record therefore overrides a host's facts only where it says something.
188
+ * @param session - The session, whose facts the host supplied.
189
+ * @param run - The stored run.
190
+ * @returns The `Facts` port for graph-domain.
191
+ */
192
+ export declare const runFacts: (session: GraphSession, run: StoredRun) => Facts;
179
193
  /** An element as commands report it: its identifier and its human label. */
180
194
  interface ElementSummary {
181
195
  readonly id: string;
@@ -394,6 +408,50 @@ interface SetResult extends Written {
394
408
  * @throws CliError with `notFound` when the model or the element does not exist.
395
409
  */
396
410
  export declare const setState: (session: GraphSession, id: string, state: OverlayStatus) => Promise<SetResult>;
411
+ /** What can be recorded about an artefact: `provided`, known not provided (`absent`), or `unknown`, which forgets whatever was recorded. */
412
+ export declare const artefactStates: readonly ["provided", "absent", "unknown"];
413
+ /** One of `artefactStates`. */
414
+ type ArtefactState = (typeof artefactStates)[number];
415
+ /** The state an artefact was given. */
416
+ interface SetArtefactResult extends Written {
417
+ readonly artefact: string;
418
+ readonly state: ArtefactState;
419
+ }
420
+ /**
421
+ * Records whether an artefact of the session's model is provided, so a consumer of an input no activity produces can become ready, or forgets it with `unknown`.
422
+ * @param session - The store and model.
423
+ * @param id - The artefact.
424
+ * @param state - `provided`, `absent` or `unknown`.
425
+ * @returns The artefact, its new state and the revision the change took.
426
+ * @throws CliError with `notFound` when the model or the artefact does not exist.
427
+ */
428
+ export declare const setArtefact: (session: GraphSession, id: string, state: ArtefactState) => Promise<SetArtefactResult>;
429
+ /** The value a named fact was given, or `unknown` when it was forgotten. */
430
+ interface SetFactResult extends Written {
431
+ readonly fact: string;
432
+ readonly value: ComputedValue | "unknown";
433
+ }
434
+ /**
435
+ * Records a named fact's value for the session's model, so every condition reading that name evaluates against it, or forgets it with `unknown`, so those conditions are indeterminate again.
436
+ * @param session - The store and model.
437
+ * @param name - The fact's name, the key a condition's `reference` node gives.
438
+ * @param value - Its trilean value, or `unknown`.
439
+ * @returns The fact, its new value and the revision the change took.
440
+ * @throws CliError with `notFound` when the model does not exist.
441
+ */
442
+ export declare const setFact: (session: GraphSession, name: string, value: ComputedValue | "unknown") => Promise<SetFactResult>;
443
+ /** The session's model's fact record. */
444
+ interface FactsResult {
445
+ readonly revision: number;
446
+ readonly facts: FactRecord;
447
+ }
448
+ /**
449
+ * Reads what is recorded about the session's model's run: which artefacts are provided or absent, and each named fact's value.
450
+ * @param session - The store and model.
451
+ * @returns The fact record and the model's revision.
452
+ * @throws CliError with `notFound` when the model does not exist.
453
+ */
454
+ export declare const factsOf: (session: GraphSession) => Promise<FactsResult>;
397
455
  //#endregion
398
456
  //#region ../graph-cli/dist/validate.d.ts
399
457
  /** One problem with a model. */
@@ -431,6 +489,30 @@ interface SyncRequest {
431
489
  /** A cursor from an interrupted sync: the first source's sync continues after it. */
432
490
  readonly after?: string;
433
491
  }
492
+ /** Where a sync a source interrupted carries on, as data a host acts on; the command line renders it as the commands to run. */
493
+ interface SyncInterruption {
494
+ /** The request that continues the failing source: its scopes, after the cursor of the last page read from it, or from the start when none was read. */
495
+ readonly resume: SyncRequest;
496
+ /** The scopes of the sources after the failing one, which were not read; sync them afresh once `resume` has finished. */
497
+ readonly remaining: readonly string[];
498
+ /** Whether pages read before the failure were written to the model. */
499
+ readonly saved: boolean;
500
+ }
501
+ /**
502
+ * 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.
503
+ */
504
+ export declare class SyncInterruptedError extends CliError implements SyncInterruption {
505
+ readonly resume: SyncRequest;
506
+ readonly remaining: readonly string[];
507
+ readonly saved: boolean;
508
+ /**
509
+ * @param exitCode - The exit status the source's failure maps to.
510
+ * @param message - The source's failure.
511
+ * @param interruption - Where the sync carries on.
512
+ * @param options - The underlying error, kept as the cause.
513
+ */
514
+ constructor(exitCode: CliError["exitCode"], message: string, interruption: SyncInterruption, options?: ErrorOptions);
515
+ }
434
516
  /** What a sync wrote. */
435
517
  interface SyncResult {
436
518
  readonly modelId: string;
@@ -446,12 +528,12 @@ interface SyncResult {
446
528
  /**
447
529
  * 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.
448
530
  *
449
- * 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.
531
+ * When a source fails part way, the pages read before the failure are still 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.
450
532
  * @param session - The store and model.
451
533
  * @param sources - The source for each identifier prefix.
452
534
  * @param request - The scopes, and a cursor to resume after.
453
535
  * @returns What was written, and the gaps the sources reported.
454
- * @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.
536
+ * @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.
455
537
  */
456
538
  export declare const sync: (session: GraphSession, sources: SourceLookup, request: SyncRequest) => Promise<SyncResult>;
457
539
  //#endregion
@@ -509,4 +591,8 @@ export declare const stateMachineToDot: <I extends IdSpace>(view: StateMachine<I
509
591
  */
510
592
  export declare const sequenceToDot: <I extends IdSpace>(view: SequenceDiagram<I>, participantLabel: (participant: I["actor"]) => string) => string;
511
593
  //#endregion
512
- export type { Addition, BlockedResult, CliPorts, CyclesResult, ElementStatus, ElementSummary, ExitCode, Failure, FormatName, GraphSession, HandoffsResult, ImpactResult, NodePortsOptions, OpenedStore, PathResult, ProgramIdentity, ProjectedView, ReadyResult, SetResult, ShowRequest, ShowResult, SourceLookup, StoredRun, SyncRequest, SyncResult, ValidateResult, ValidationIssue, ViewName, WhyLink, WhyResult, Written };
594
+ //#region src/version.d.ts
595
+ /** 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. */
596
+ export declare const version: string;
597
+ //#endregion
598
+ 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, SyncInterruption, SyncRequest, SyncResult, ValidateResult, ValidationIssue, ViewName, WhyLink, WhyResult, Written };
package/dist/cli.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import { t as exitCodes } from "./exit-codes-0ultCqdS.js";
2
- import { A as add, C as blocked, D as path, E as impactOf, F as failureOf, M as findRun, N as loadRun, O as ready, P as CliError, 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 setState, 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 } from "./dist-HLlW8vgT.js";
3
- export { CliError, add, blocked, cycles, defaultDataPath, defaultModelId, emit, exitCodes, failureOf, findRun, flowchartToDot, flowchartToMermaid, formatNames, handoffsOf, impactOf, loadRun, nodePorts, packageVersion, path, project, ready, runCli, runProcess, sequenceToDot, sequenceToMermaid, setState, show, stateMachineToDot, stateMachineToMermaid, swimlaneToDot, swimlaneToMermaid, sync, validateDocument, validateStored, viewNames, why };
2
+ import { A as why, B as CliError, C as swimlaneToMermaid, D as impactOf, E as handoffsOf, F as setFact, I as setState, L as findRun, M as artefactStates, N as factsOf, O as path, P as setArtefact, R as loadRun, S as stateMachineToMermaid, T as cycles, V as failureOf, _ as sequenceToDot, a as defaultModelId, b as flowchartToMermaid, c as validateStored, d as emit, f as formatNames, g as flowchartToDot, h as viewNames, i as runProcess, j as add, k as ready, l as SyncInterruptedError, m as show, n as nodePorts, o as runCli, p as project, r as packageVersion, s as validateDocument, t as defaultDataPath, u as sync, v as stateMachineToDot, w as blocked, x as sequenceToMermaid, y as swimlaneToDot, z as runFacts } from "./dist-BI01yxFA.js";
3
+ import { t as version } from "./version-Dagrkvwt.js";
4
+ export { CliError, SyncInterruptedError, 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, version, viewNames, why };
@@ -1,3 +1,4 @@
1
+ import { createSyncEvaluator } from "trilean";
1
2
  //#region ../../core/graph/dag/dist/errors.js
2
3
  /**
3
4
  * Thrown when an operation that is only defined on an acyclic graph is given one that contains a cycle.
@@ -233,6 +234,167 @@ function assertAcyclic(dag) {
233
234
  if (cycles.length > 0) throw new CycleError(cycles);
234
235
  }
235
236
  //#endregion
237
+ //#region ../../core/graph/domain/dist/facts.js
238
+ /** The only field of a reserved key. Keys that carry it are resolved from the overlay, so a `Facts` implementation must not use it. */
239
+ const elementField = "$element";
240
+ /**
241
+ * Builds the reserved key that reads another element's run state inside a condition. It resolves to the text `"done"` or `"open"`, and is not found (so the condition is indeterminate) when the overlay says `"unknown"` or the id is not an element of the model.
242
+ *
243
+ * The value is the overlay's own state, not the derived status: a condition therefore never depends on the derivation that evaluates it, and cannot form a cycle.
244
+ * @param id - The element whose state is read.
245
+ * @returns A key to put in a trilean `reference` node.
246
+ */
247
+ const elementStatusKey = (id) => ({ [elementField]: id });
248
+ /** The only field of the key the package puts to the port to ask whether an input artefact is provided. */
249
+ const artefactField = "$artefact";
250
+ /**
251
+ * Builds the key the package asks the facts port when it needs to know whether an artefact has been provided from outside the process. An artefact is provided exactly when the port answers `{ found: true }` with the boolean `true`; a not-found or `false` answer means it is not.
252
+ *
253
+ * A provided artefact satisfies every consumer, whether or not an activity also produces it.
254
+ * @param id - The artefact.
255
+ * @returns The key the port is asked.
256
+ */
257
+ const artefactKey = (id) => ({ [artefactField]: id });
258
+ /**
259
+ * Whether the facts port reports an artefact as provided.
260
+ * @param facts - The injected source of facts.
261
+ * @param id - The artefact.
262
+ */
263
+ const isProvided = (facts, id) => {
264
+ const answer = facts(artefactKey(id));
265
+ return answer.found && answer.value.kind === "boolean" && answer.value.value;
266
+ };
267
+ /** A record that knows nothing: no artefact is provided and every named fact is unknown. */
268
+ const emptyFactRecord = {
269
+ artefacts: {},
270
+ values: {}
271
+ };
272
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
273
+ /** The artefact id an `artefactKey` key names, or `undefined` for any other key. */
274
+ const artefactOf = (key) => {
275
+ if (!isRecord(key) || Object.keys(key).length !== 1) return;
276
+ const id = key[artefactField];
277
+ return typeof id === "string" ? id : void 0;
278
+ };
279
+ /**
280
+ * Adapts a fact record to the `Facts` port. A string key is a named fact, answered with its value when the record holds it; a key built by `artefactKey` is answered with the boolean the record holds for that artefact; every other key, and any name or artefact the record does not hold, is not found.
281
+ * @param record - What is known.
282
+ * @returns A `Facts` port reading the record.
283
+ */
284
+ const factsFrom = (record) => {
285
+ const values = new Map(Object.entries(record.values));
286
+ const artefacts = new Map(Object.entries(record.artefacts));
287
+ return (key) => {
288
+ if (typeof key === "string") {
289
+ const value = values.get(key);
290
+ return value === void 0 ? { found: false } : {
291
+ found: true,
292
+ value
293
+ };
294
+ }
295
+ const id = artefactOf(key);
296
+ const provided = id === void 0 ? void 0 : artefacts.get(id);
297
+ return provided === void 0 ? { found: false } : {
298
+ found: true,
299
+ value: {
300
+ kind: "boolean",
301
+ value: provided
302
+ }
303
+ };
304
+ };
305
+ };
306
+ /**
307
+ * The named facts a condition reads: the string key of every `reference` node in it, each once, in the order first met walking the tree depth first. Reserved keys and other non-string keys are not names, so they are left out, and a reference's own key is never searched for further references.
308
+ * @param condition - A trilean predicate or expression, such as a transition's `condition`.
309
+ * @returns The names, each once.
310
+ */
311
+ const factNamesIn = (condition) => {
312
+ const names = /* @__PURE__ */ new Set();
313
+ const pending = [condition];
314
+ for (let node = pending.pop(); node !== void 0; node = pending.pop()) if (Array.isArray(node)) {
315
+ const items = node;
316
+ pending.push(...[...items].reverse());
317
+ } else if (typeof node === "object" && node !== null) {
318
+ if ("kind" in node && node.kind === "reference" && "key" in node) {
319
+ if (typeof node.key === "string") names.add(node.key);
320
+ } else {
321
+ const fields = Object.values(node);
322
+ pending.push(...[...fields].reverse());
323
+ }
324
+ }
325
+ return [...names];
326
+ };
327
+ /**
328
+ * Layers facts ports: each key is answered by the first port that finds it, so an earlier port overrides a later one only where it knows the answer.
329
+ * @param ports - The ports, most authoritative first.
330
+ * @returns A `Facts` port that is not found only when no port finds the key.
331
+ */
332
+ const layeredFacts = (...ports) => (key) => {
333
+ for (const port of ports) {
334
+ const answer = port(key);
335
+ if (answer.found) return answer;
336
+ }
337
+ return { found: false };
338
+ };
339
+ /**
340
+ * Thrown when a condition uses a node whose evaluation needs a table or a collection from the facts port, which answers single keys only.
341
+ */
342
+ var UnsupportedConditionError = class extends Error {
343
+ /** The kind of reference the condition used. */
344
+ reference;
345
+ /**
346
+ * @param reference - The unsupported kind of reference.
347
+ */
348
+ constructor(reference) {
349
+ super(`A condition uses a ${reference} reference, which the Facts port does not answer: it resolves single keys only`);
350
+ this.name = "UnsupportedConditionError";
351
+ this.reference = reference;
352
+ }
353
+ };
354
+ const evaluator = createSyncEvaluator({});
355
+ /**
356
+ * Evaluates every transition's condition.
357
+ *
358
+ * A definite true is `taken`, a definite false is `not-taken` and an indeterminate result is `indeterminate`. A transition without a condition is `taken`. Conditions are evaluated for every transition regardless of the state of its source.
359
+ * @param model - The model whose transitions are evaluated.
360
+ * @param overlay - Run state, read by `elementStatusKey` references.
361
+ * @param facts - The injected source of facts.
362
+ * @returns The outcome of every transition, in transition order.
363
+ * @throws UnsupportedConditionError when a condition contains a `lookup`, `some`, `every` or fold node.
364
+ */
365
+ const evaluateBranches = (model, overlay, facts) => {
366
+ const elementIds = new Set(model.elements.map(({ id }) => id));
367
+ const overlayOf = new Map(Object.entries(overlay));
368
+ const resolvers = {
369
+ resolveValue: (key) => {
370
+ if (!isRecord(key) || !(elementField in key)) return facts(key);
371
+ const id = key[elementField];
372
+ if (typeof id !== "string" || !elementIds.has(id)) return { found: false };
373
+ const state = overlayOf.get(id);
374
+ if (state === "unknown") return { found: false };
375
+ return {
376
+ found: true,
377
+ value: {
378
+ kind: "text",
379
+ value: state === "done" ? "done" : "open"
380
+ }
381
+ };
382
+ },
383
+ resolveLookup: () => {
384
+ throw new UnsupportedConditionError("lookup");
385
+ },
386
+ resolveCollection: () => {
387
+ throw new UnsupportedConditionError("collection");
388
+ }
389
+ };
390
+ return new Map(model.transitions.map(({ id, condition }) => {
391
+ if (condition === void 0) return [id, "taken"];
392
+ const result = evaluator.evaluatePredicate(condition, void 0, resolvers);
393
+ if (result.status === "indeterminate") return [id, "indeterminate"];
394
+ return [id, result.value ? "taken" : "not-taken"];
395
+ }));
396
+ };
397
+ //#endregion
236
398
  //#region ../../core/graph/domain/dist/control.js
237
399
  /**
238
400
  * Builds the dependency graph over every element, leaving out the transitions in `excluded`. A node needs the source of each remaining incoming transition and, for an activity, the producer of each artefact it consumes.
@@ -291,4 +453,4 @@ const controlDag = (model) => buildControl(model, new Set(loopTransitions(model)
291
453
  */
292
454
  const illegalCycles = (model) => findCycles(controlDag(model));
293
455
  //#endregion
294
- export { findCycles as a, descendantsOf as c, lookup as d, CycleError as f, assertAcyclic as i, assembleDag as l, illegalCycles as n, stronglyConnectedComponents as o, UnknownNodeError as p, loopTransitions as r, ancestorsOf as s, controlDag as t, buildDag as u };
456
+ export { UnknownNodeError as S, descendantsOf as _, artefactKey as a, lookup as b, evaluateBranches as c, isProvided as d, layeredFacts as f, ancestorsOf as g, stronglyConnectedComponents as h, UnsupportedConditionError as i, factNamesIn as l, findCycles as m, illegalCycles as n, elementStatusKey as o, assertAcyclic as p, loopTransitions as r, emptyFactRecord as s, controlDag as t, factsFrom as u, assembleDag as v, CycleError as x, buildDag as y };
@@ -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 };
@@ -0,0 +1,3 @@
1
+ import "./exit-codes-0ultCqdS.js";
2
+ import { i as runProcess } from "./dist-BI01yxFA.js";
3
+ export { runProcess };