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
@@ -1,5 +1,42 @@
1
- import { A as syncPageSchema, C as scopeChildrenInputSchema, D as sourceDescriptionSchema, E as scopeRootsInputSchema, O as syncFilterSchema, S as relationSchema, T as scopeListingSchema, _ as gapSchema, a as ownerScopeId, b as participantSchema, c as githubGraphqlEndpoint, d as paginate, f as graphSourceContract, g as gapReasons, h as filterStates, i as issueId, j as transitionSchema, k as syncInputSchema, l as assembleSync, m as boundActivitySchema, n as accountId, o as parseScope, p as badRequestReasons, r as githubSource, s as repositoryId, t as createGitHubIssuesSource, u as createGraphSourceRouter, v as orderDirections, w as scopeEntrySchema, x as relationKinds, y as orderFields } from "../source-DRDpEA8Q.js";
1
+ import { _ as syncInputSchema, a as gapSchema, c as participantSchema, d as scopeChildrenInputSchema, f as scopeEntrySchema, g as syncFilterSchema, h as sourceDescriptionSchema, i as gapReasons, l as relationKinds, m as scopeRootsInputSchema, n as boundActivitySchema, o as orderDirections, p as scopeListingSchema, r as filterStates, s as orderFields, t as badRequestReasons, u as relationSchema, v as syncPageSchema, y as transitionSchema } from "../schema-DVAttrJg.js";
2
+ import { a as ownerScopeId, c as githubGraphqlEndpoint, d as graphSourceContract, i as issueId, l as createGraphSourceRouter, n as accountId, o as parseScope, r as githubSource, s as repositoryId, t as createGitHubIssuesSource, u as paginate } from "../source-lTKshktI.js";
2
3
  import { t as GraphSourceError } from "../errors-BZe5WzCn.js";
4
+ //#region ../../core/graph/source-contract/dist/assemble.js
5
+ const relationKey = (relation) => JSON.stringify([
6
+ relation.kind,
7
+ relation.from,
8
+ relation.to
9
+ ]);
10
+ /** Keeps one entry per key, in first-seen order, holding the latest value seen for it. */
11
+ const latestById = (items, keyOf) => {
12
+ const byKey = /* @__PURE__ */ new Map();
13
+ for (const item of items) byKey.set(keyOf(item), item);
14
+ return [...byKey.values()];
15
+ };
16
+ /**
17
+ * Merges the pages of a sync into one model. A record can appear on several pages, for example an issue in another repository reached through a dependency before its own repository is read; identity is the identifier, so it becomes one element, and where pages disagree the later page wins, being the more recent read.
18
+ * @param pages - The pages of one sync, in the order they arrived.
19
+ * @returns The model, overlay, relations and gaps of the whole sync.
20
+ */
21
+ const assembleSync = (pages) => {
22
+ const overlay = Object.fromEntries(pages.flatMap((page) => Object.entries(page.overlay)));
23
+ return {
24
+ model: {
25
+ participants: latestById(pages.flatMap((page) => page.participants), (participant) => participant.id),
26
+ events: [],
27
+ actions: [],
28
+ artefacts: [],
29
+ elements: latestById(pages.flatMap((page) => page.activities), (activity) => activity.id),
30
+ transitions: latestById(pages.flatMap((page) => page.transitions), (transition) => transition.id),
31
+ fragments: [],
32
+ projections: []
33
+ },
34
+ overlay,
35
+ relations: latestById(pages.flatMap((page) => page.relations), relationKey),
36
+ gaps: pages.flatMap((page) => page.gaps)
37
+ };
38
+ };
39
+ //#endregion
3
40
  //#region ../../core/graph/source-contract/dist/fake.js
4
41
  const gapIndex = -1;
5
42
  const cursorPattern = /^(?<scope>[^@;]+)@(?<index>-1|0|[1-9]\d*)(?:;(?<filter>.+))?$/u;
@@ -1,132 +1,9 @@
1
- import { r as overlaySchema, t as modelSchema } from "./schema-CVnS6HiX.js";
1
+ import { _ as syncInputSchema, d as scopeChildrenInputSchema, g as syncFilterSchema, h as sourceDescriptionSchema, m as scopeRootsInputSchema, p as scopeListingSchema, t as badRequestReasons, v as syncPageSchema } from "./schema-DVAttrJg.js";
2
2
  import { t as GraphSourceError } from "./errors-BZe5WzCn.js";
3
3
  import { z } from "zod";
4
4
  import { eventIterator, oc } from "@orpc/contract";
5
5
  import { withEventMeta } from "@orpc/client";
6
6
  import { implement } from "@orpc/server";
7
- //#region ../../core/graph/source-contract/dist/schema.js
8
- /**
9
- * The model's own record schemas, taken from `modelSchema` rather than restated, so a page of a sync is exactly a slice of a model and can never drift from it.
10
- */
11
- const modelShape = modelSchema.unwrap().shape;
12
- /** A participant as the model declares it: an organisation, a repository, a person. */
13
- const participantSchema = modelShape.participants.unwrap().element;
14
- /** A transition as the model declares it. A source emits one per dependency, from the prerequisite to the work that waits on it. */
15
- const transitionSchema = modelShape.transitions.unwrap().element;
16
- /** The model's `activity` element: the first variant of its element union, pinned by a type test so a reordering of the union is a compile error. */
17
- const [activityElementSchema] = modelShape.elements.unwrap().element.options;
18
- /**
19
- * An activity bound to the external record it stands for. `source` is required here, where the model leaves it optional, and it equals `id`: the source-qualified identifier (`github:org/repo#12`) is the element's identity, so the same record synced twice, or reached from two places, is the same element.
20
- */
21
- const boundActivitySchema = activityElementSchema.unwrap().extend({ source: z.string().min(1) }).refine((activity) => activity.id === activity.source, {
22
- message: "a bound activity's id must equal its source",
23
- path: ["id"]
24
- }).readonly();
25
- /** The relation kinds a source reports beside the model's transitions. */
26
- const relationKinds = [
27
- "child-of",
28
- "assigned-to",
29
- "part-of"
30
- ];
31
- /**
32
- * A structural link between two identifiers that the model has no field for: `child-of` from a sub-task activity to its parent activity, `assigned-to` from an activity to a person participant, `part-of` from one participant to the participant that contains it (a repository to its organisation). Relations carry no ordering, so a consumer decides whether a parent waits on its children.
33
- */
34
- const relationSchema = z.strictObject({
35
- kind: z.enum(relationKinds),
36
- from: z.string().min(1),
37
- to: z.string().min(1)
38
- }).readonly();
39
- /** Why part of a requested scope is missing from a sync. */
40
- const gapReasons = ["not-found", "forbidden"];
41
- /**
42
- * A part of the requested scope that the source could not read, reported as data so the rest of the sync still arrives. `target` is the source-qualified identifier of what is missing: a scope, a record, or one of a record's connections.
43
- */
44
- const gapSchema = z.strictObject({
45
- target: z.string().min(1),
46
- reason: z.enum(gapReasons),
47
- message: z.string()
48
- }).readonly();
49
- /**
50
- * One page of a sync: bound activities, the participants they name, the transitions between them, their relations, their run state, and any gaps. A page is a partial view; `assembleSync` merges a whole sync into one model.
51
- *
52
- * `next` is the cursor to resume after this page. It is absent on the last page.
53
- */
54
- const syncPageSchema = z.strictObject({
55
- participants: z.array(participantSchema).readonly(),
56
- activities: z.array(boundActivitySchema).readonly(),
57
- transitions: z.array(transitionSchema).readonly(),
58
- relations: z.array(relationSchema).readonly(),
59
- overlay: overlaySchema,
60
- gaps: z.array(gapSchema).readonly(),
61
- next: z.string().min(1).exactOptional()
62
- }).readonly();
63
- /** The run states a sync filter can select by: whether the work is still open or done. */
64
- const filterStates = ["open", "done"];
65
- /** What a sync filter can order the listed records by: when they were created, when they last changed, or how much discussion they have. */
66
- const orderFields = [
67
- "created",
68
- "updated",
69
- "comments"
70
- ];
71
- /** Which way a sync filter orders the listed records. */
72
- const orderDirections$1 = ["ascending", "descending"];
73
- /**
74
- * Narrows and orders the records each scope lists, applied on the source's side where the source supports it. Every field is optional, and an absent field does not narrow.
75
- *
76
- * - `states` keeps records whose run state is one of those given.
77
- * - `labels` keeps records carrying at least one of the labels given.
78
- * - `order` is the order the records are listed in; absent, the source's own stable order.
79
- *
80
- * A filter narrows what a scope lists, not what the listed records reach: a blocker of a listed record is read whatever its own state or labels, because it is a dependency of work the filter kept.
81
- */
82
- const syncFilterSchema = z.strictObject({
83
- states: z.array(z.enum(filterStates)).min(1).readonly().exactOptional(),
84
- labels: z.array(z.string().min(1)).min(1).readonly().exactOptional(),
85
- order: z.strictObject({
86
- field: z.enum(orderFields),
87
- direction: z.enum(orderDirections$1)
88
- }).readonly().exactOptional()
89
- }).readonly();
90
- /**
91
- * What to sync and where to resume. `scope` names what to read as source-qualified scope identifiers (for GitHub, `github:org` for every repository an owner has, or `github:org/repo`), read in the order given. `after` is a cursor from an earlier page's `next`: the sync continues with the page that followed it. A cursor is valid only with the scope list and the filter it was issued for.
92
- *
93
- * `filter` narrows and orders what each scope lists (see `syncFilterSchema`).
94
- */
95
- const syncInputSchema = z.strictObject({
96
- scope: z.array(z.string().min(1)).min(1).readonly(),
97
- after: z.string().min(1).exactOptional(),
98
- filter: syncFilterSchema.exactOptional()
99
- }).readonly();
100
- /**
101
- * A scope a person can choose to sync, as a picker lists it. `scope` is the scope identifier to pass to `sync` (for GitHub, `github:org` for an organisation, `github:org/repo` for a repository), `label` its display name, `description` the source's own description when it has one, `url` where it lives in the source's own interface, and `archived` whether the source has archived it.
102
- */
103
- const scopeEntrySchema = z.strictObject({
104
- scope: z.string().min(1),
105
- label: z.string(),
106
- description: z.string().exactOptional(),
107
- url: z.url().exactOptional(),
108
- archived: z.boolean()
109
- }).readonly();
110
- /**
111
- * One page of a scope listing. `entries` are in the source's stable listing order, `gaps` name a parent the source could not read (it does not exist, or the credentials cannot see it), and `next` is the cursor to resume after this page, absent on the last.
112
- */
113
- const scopeListingSchema = z.strictObject({
114
- entries: z.array(scopeEntrySchema).readonly(),
115
- gaps: z.array(gapSchema).readonly(),
116
- next: z.string().min(1).exactOptional()
117
- }).readonly();
118
- /** Where a listing of the top-level scopes resumes: a cursor from an earlier page's `next`. */
119
- const scopeRootsInputSchema = z.strictObject({ after: z.string().min(1).exactOptional() }).readonly();
120
- /** The scope whose child scopes to list (for GitHub, the repositories of `github:owner`), and where to resume. A cursor is valid only for the parent it was issued for. */
121
- const scopeChildrenInputSchema = z.strictObject({
122
- scope: z.string().min(1),
123
- after: z.string().min(1).exactOptional()
124
- }).readonly();
125
- /** What a source says about itself. `source` is the prefix of every identifier it emits, before the colon. */
126
- const sourceDescriptionSchema = z.strictObject({ source: z.string().regex(/^[a-z][a-z0-9-]*$/u) }).readonly();
127
- /** Why a request was rejected as malformed. */
128
- const badRequestReasons = ["invalid-cursor", "invalid-scope"];
129
- //#endregion
130
7
  //#region ../../core/graph/source-contract/dist/contract.js
131
8
  /**
132
9
  * The typed errors every procedure can end with, each carrying its detail as data. The codes are oRPC's common codes, so an HTTP transport answers with the matching status (401, 429, 400) without any per-transport mapping.
@@ -286,42 +163,6 @@ const createGraphSourceRouter = (pager) => os.router({ v1: {
286
163
  }
287
164
  } });
288
165
  //#endregion
289
- //#region ../../core/graph/source-contract/dist/assemble.js
290
- const relationKey = (relation) => JSON.stringify([
291
- relation.kind,
292
- relation.from,
293
- relation.to
294
- ]);
295
- /** Keeps one entry per key, in first-seen order, holding the latest value seen for it. */
296
- const latestById = (items, keyOf) => {
297
- const byKey = /* @__PURE__ */ new Map();
298
- for (const item of items) byKey.set(keyOf(item), item);
299
- return [...byKey.values()];
300
- };
301
- /**
302
- * Merges the pages of a sync into one model. A record can appear on several pages, for example an issue in another repository reached through a dependency before its own repository is read; identity is the identifier, so it becomes one element, and where pages disagree the later page wins, being the more recent read.
303
- * @param pages - The pages of one sync, in the order they arrived.
304
- * @returns The model, overlay, relations and gaps of the whole sync.
305
- */
306
- const assembleSync = (pages) => {
307
- const overlay = Object.fromEntries(pages.flatMap((page) => Object.entries(page.overlay)));
308
- return {
309
- model: {
310
- participants: latestById(pages.flatMap((page) => page.participants), (participant) => participant.id),
311
- events: [],
312
- actions: [],
313
- artefacts: [],
314
- elements: latestById(pages.flatMap((page) => page.activities), (activity) => activity.id),
315
- transitions: latestById(pages.flatMap((page) => page.transitions), (transition) => transition.id),
316
- fragments: [],
317
- projections: []
318
- },
319
- overlay,
320
- relations: latestById(pages.flatMap((page) => page.relations), relationKey),
321
- gaps: pages.flatMap((page) => page.gaps)
322
- };
323
- };
324
- //#endregion
325
166
  //#region ../../adapters/graph/source-github-issues/dist/cursor.js
326
167
  /**
327
168
  * Where the next page of a sync starts. A position is plain data, so a cursor resumes in any process.
@@ -545,16 +386,36 @@ const readJson = async (response) => {
545
386
  }
546
387
  };
547
388
  /**
389
+ * Asks the token port for the token a request needs. A port that cannot supply one has failed to provide credentials, which ends the sync exactly as GitHub rejecting them would, so whatever it throws is reported as `unauthorised`: a `GraphSourceError` the port threw as it is, and anything else wrapped in one whose message names the token port and the owner and whose cause is the port's own error. An abort is passed through, since it is the caller's, not the port's.
390
+ * @param transport - The token port.
391
+ * @param owner - The login the request is about, or `undefined` for the token's own holder.
392
+ * @param signal - The caller's abort signal.
393
+ * @returns The token.
394
+ * @throws GraphSourceError with `unauthorised` when the port fails.
395
+ */
396
+ const tokenFor = async (transport, owner, signal) => {
397
+ try {
398
+ return await transport.token(owner, signal);
399
+ } catch (error) {
400
+ if (error instanceof GraphSourceError || signal?.aborted === true) throw error;
401
+ const reason = error instanceof Error ? error.message : String(error);
402
+ throw new GraphSourceError({
403
+ kind: "unauthorised",
404
+ message: `the GitHub token port gave no token${owner === void 0 ? "" : ` for ${owner}`}: ${reason}`
405
+ }, { cause: error });
406
+ }
407
+ };
408
+ /**
548
409
  * Sends one GraphQL request and classifies the answer.
549
410
  * @param transport - The token, fetch and clock ports.
550
411
  * @param request - The document, its variables, the owner whose token to use and the schema for `data`.
551
412
  * @param signal - The caller's abort signal, passed to `fetch`.
552
413
  * @returns The parsed data and any `NOT_FOUND` or `FORBIDDEN` problems beside it.
553
- * @throws GraphSourceError with `unauthorised` for HTTP 401, and `rate-limited` (with the reset instant when GitHub gives one) for any form of rate limiting.
414
+ * @throws GraphSourceError with `unauthorised` for HTTP 401 or a token port that fails, and `rate-limited` (with the reset instant when GitHub gives one) for any form of rate limiting.
554
415
  * @throws Error for any other failure: an HTTP error, another GraphQL error type, or data that does not match the schema.
555
416
  */
556
417
  const graphql = async (transport, request, signal) => {
557
- const token = await transport.token(request.owner, signal);
418
+ const token = await tokenFor(transport, request.owner, signal);
558
419
  const response = await transport.fetch(githubGraphqlEndpoint, {
559
420
  method: "POST",
560
421
  headers: {
@@ -1313,4 +1174,4 @@ const createGitHubIssuesSource = (options) => {
1313
1174
  };
1314
1175
  };
1315
1176
  //#endregion
1316
- export { syncPageSchema as A, scopeChildrenInputSchema as C, sourceDescriptionSchema as D, scopeRootsInputSchema as E, syncFilterSchema as O, relationSchema as S, scopeListingSchema as T, gapSchema as _, ownerScopeId as a, participantSchema as b, githubGraphqlEndpoint as c, paginate as d, graphSourceContract as f, gapReasons as g, filterStates as h, issueId as i, transitionSchema as j, syncInputSchema as k, assembleSync as l, boundActivitySchema as m, accountId as n, parseScope as o, badRequestReasons as p, githubSource as r, repositoryId as s, createGitHubIssuesSource as t, createGraphSourceRouter as u, orderDirections$1 as v, scopeEntrySchema as w, relationKinds as x, orderFields as y };
1177
+ export { ownerScopeId as a, githubGraphqlEndpoint as c, graphSourceContract as d, issueId as i, createGraphSourceRouter as l, accountId as n, parseScope as o, githubSource as r, repositoryId as s, createGitHubIssuesSource as t, paginate as u };
@@ -1,4 +1,4 @@
1
- import { w as GraphStore } from "../index-DxWOY8f9.js";
1
+ import { w as GraphStore } from "../index-CR8xUK3S.js";
2
2
  import { DatabaseSyncOptions } from "node:sqlite";
3
3
  import { NodeSQLiteDatabase } from "drizzle-orm/node-sqlite";
4
4
  //#region ../../adapters/graph/sqlite-node/dist/open.d.ts
@@ -21,4 +21,29 @@ interface NodeGraphStore {
21
21
  */
22
22
  export declare const openNodeGraphStore: (path: string, options?: DatabaseSyncOptions) => NodeGraphStore;
23
23
  //#endregion
24
- export type { NodeGraphStore };
24
+ //#region ../../adapters/graph/sqlite-node/dist/data-file.d.ts
25
+ /** 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. */
26
+ export declare const privateDirectoryMode = 448;
27
+ /** 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. */
28
+ export declare const privateFileMode = 384;
29
+ /** Where a store's data file lives, and how much of its path the caller owns. */
30
+ interface DataFileOptions {
31
+ /**
32
+ * Whether the directory is the caller's own, such as an application's default data directory or one a host keeps under its own home, which is created private and kept private. A directory someone else chose, such as a path a user names on a command line, is created when missing but never has its mode changed.
33
+ */
34
+ readonly ownsDirectory: boolean;
35
+ /** The platform; on Windows, file modes mean nothing and nothing is set. */
36
+ readonly platform: NodeJS.Platform;
37
+ }
38
+ /**
39
+ * 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.
40
+ *
41
+ * 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.
42
+ * @param path - The database file; not `":memory:"`, which has no file.
43
+ * @param options - Whether the caller owns the directory, and the platform.
44
+ * @returns One warning line naming what was narrowed, for the caller to show, or nothing when nothing was.
45
+ * @throws Error from the file system when the directory or file cannot be created or its mode read or changed.
46
+ */
47
+ export declare const prepareDataFile: (path: string, options: DataFileOptions) => readonly string[];
48
+ //#endregion
49
+ export type { DataFileOptions, NodeGraphStore };
@@ -1,2 +1,2 @@
1
- import { t as openNodeGraphStore } from "../open-BF-UHqeW.js";
2
- export { openNodeGraphStore };
1
+ import { i as openNodeGraphStore, n as privateDirectoryMode, r as privateFileMode, t as prepareDataFile } from "../data-file-BVMxkTug.js";
2
+ export { openNodeGraphStore, prepareDataFile, privateDirectoryMode, privateFileMode };
@@ -1,5 +1,5 @@
1
- import { I as storeChangeSchema, t as createSqliteGraphStore, y as ChangeFeed } from "../../store-BJILV4JS.js";
2
- import { t as createGraphStoreRouter } from "../../router-DvGGLnNt.js";
1
+ import { I as storeChangeSchema, t as createSqliteGraphStore, y as ChangeFeed } from "../../store-DKzvLGgl.js";
2
+ import { t as createGraphStoreRouter } from "../../router-Dk91O_9p.js";
3
3
  import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
4
4
  import { RPCHandler } from "@orpc/server/message-port";
5
5
  import { drizzle } from "drizzle-orm/sqlite-proxy";
@@ -1,4 +1,4 @@
1
- import { ut as GraphStoreClient } from "../index-DxWOY8f9.js";
1
+ import { ut as GraphStoreClient } from "../index-CR8xUK3S.js";
2
2
  //#region ../../adapters/graph/sqlite-wasm/dist/client.d.ts
3
3
  /**
4
4
  * A client for the graph store a worker serves with `serveGraphStoreInWorker`. Every procedure of the contract is available, with its abort signal and typed errors, as if the store were in the page.
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { a as validateOverlay, c as fragmentOperators, d as overlayStatuses, f as participantKinds, l as messageModes, p as pseudoRoles, r as overlaySchema, s as elementKinds, t as modelSchema, u as notations } from "./schema-CVnS6HiX.js";
2
+ import { a as validateOverlay, c as fragmentOperators, d as overlayStatuses, f as participantKinds, l as messageModes, p as pseudoRoles, r as overlaySchema, s as elementKinds, t as modelSchema, u as notations } from "./schema-BPT8fmfa.js";
3
3
  import { t as validate } from "./validate-DF3mQZww.js";
4
4
  import { r as loopTransitions, s as emptyFactRecord } from "./control-DiygoEhm.js";
5
5
  import { z } from "zod";
package/dist/store.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as factRecordSchema, A as defaultSubscriptionCapacity, B as Reach, C as ElementRequest, D as ChangeFeedOptions, E as ChangeFeed, F as ChangeKind, G as StoredFacts, H as RelationKind, I as FactChange, J as boundItemsSchema, K as StoredModel, L as FactClearing, M as GraphStoreFailure, N as BoundActivity, O as ChangeSubscription, P as BoundItems, Q as factNameSchema, R as ModelId, S as overlayWithin, T as ModelRequest, U as Revision, V as Relation, W as StoreChange, X as factChangeSchema, Y as changeKinds, Z as factClearingSchema, _ as mergeFacts, a as blockedChainOf, at as relationSchema, b as emptyModel, c as structureOf, ct as storedFactsSchema, d as descendantsOf, dt as graphStoreContract, et as issueSchema, f as mergeRelations, g as factsWithin, h as factIssues, i as Structure, it as relationKinds, j as GraphStoreError, k as SubscribeOptions, l as ancestorsOf, lt as storedModelSchema, m as relationsWithin, n as GraphStoreRouter, nt as modelSummarySchema, o as impactOf, ot as revisionSchema, p as relationIssues, q as boundActivitySchema, r as createGraphStoreRouter, rt as reachSchema, s as readyOf, st as storeChangeSchema, t as createFakeGraphStore, tt as modelIdSchema, u as childrenOf, ut as GraphStoreClient, v as withoutFacts, w as GraphStore, x as mergeBoundItems, y as checkStored, z as ModelSummary } from "./index-DxWOY8f9.js";
1
+ import { $ as factRecordSchema, A as defaultSubscriptionCapacity, B as Reach, C as ElementRequest, D as ChangeFeedOptions, E as ChangeFeed, F as ChangeKind, G as StoredFacts, H as RelationKind, I as FactChange, J as boundItemsSchema, K as StoredModel, L as FactClearing, M as GraphStoreFailure, N as BoundActivity, O as ChangeSubscription, P as BoundItems, Q as factNameSchema, R as ModelId, S as overlayWithin, T as ModelRequest, U as Revision, V as Relation, W as StoreChange, X as factChangeSchema, Y as changeKinds, Z as factClearingSchema, _ as mergeFacts, a as blockedChainOf, at as relationSchema, b as emptyModel, c as structureOf, ct as storedFactsSchema, d as descendantsOf, dt as graphStoreContract, et as issueSchema, f as mergeRelations, g as factsWithin, h as factIssues, i as Structure, it as relationKinds, j as GraphStoreError, k as SubscribeOptions, l as ancestorsOf, lt as storedModelSchema, m as relationsWithin, n as GraphStoreRouter, nt as modelSummarySchema, o as impactOf, ot as revisionSchema, p as relationIssues, q as boundActivitySchema, r as createGraphStoreRouter, rt as reachSchema, s as readyOf, st as storeChangeSchema, t as createFakeGraphStore, tt as modelIdSchema, u as childrenOf, ut as GraphStoreClient, v as withoutFacts, w as GraphStore, x as mergeBoundItems, y as checkStored, z as ModelSummary } from "./index-CR8xUK3S.js";
2
2
  import { SQLiteAsyncDatabase } from "drizzle-orm/sqlite-core";
3
3
  import { MigrationMeta } from "drizzle-orm/migrator";
4
4
  //#region ../../adapters/graph/store-sqlite/dist/database.d.ts
package/dist/store.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { r as loopTransitions, s as emptyFactRecord } from "./control-DiygoEhm.js";
2
- import { A as modelIdSchema, C as boundItemsSchema, D as factNameSchema, E as factClearingSchema, F as revisionSchema, I as storeChangeSchema, L as storedFactsSchema, M as reachSchema, N as relationKinds, O as factRecordSchema, P as relationSchema, R as storedModelSchema, S as boundActivitySchema, T as factChangeSchema, _ as mergeBoundItems, a as ancestorsOf, b as defaultSubscriptionCapacity, c as mergeRelations, d as factIssues, f as factsWithin, g as emptyModel, h as checkStored, i as serialised, j as modelSummarySchema, k as issueSchema, l as relationIssues, m as withoutFacts, n as tables_exports, o as childrenOf, p as mergeFacts, r as migrate, s as descendantsOf, t as createSqliteGraphStore, u as relationsWithin, v as overlayWithin, w as changeKinds, x as GraphStoreError, y as ChangeFeed } from "./store-BJILV4JS.js";
3
- import { n as graphStoreContract, t as createGraphStoreRouter } from "./router-DvGGLnNt.js";
2
+ import { A as modelIdSchema, C as boundItemsSchema, D as factNameSchema, E as factClearingSchema, F as revisionSchema, I as storeChangeSchema, L as storedFactsSchema, M as reachSchema, N as relationKinds, O as factRecordSchema, P as relationSchema, R as storedModelSchema, S as boundActivitySchema, T as factChangeSchema, _ as mergeBoundItems, a as ancestorsOf, b as defaultSubscriptionCapacity, c as mergeRelations, d as factIssues, f as factsWithin, g as emptyModel, h as checkStored, i as serialised, j as modelSummarySchema, k as issueSchema, l as relationIssues, m as withoutFacts, n as tables_exports, o as childrenOf, p as mergeFacts, r as migrate, s as descendantsOf, t as createSqliteGraphStore, u as relationsWithin, v as overlayWithin, w as changeKinds, x as GraphStoreError, y as ChangeFeed } from "./store-DKzvLGgl.js";
3
+ import { n as graphStoreContract, t as createGraphStoreRouter } from "./router-Dk91O_9p.js";
4
4
  //#region ../../core/graph/store-contract/dist/structure.js
5
5
  /**
6
6
  * Whether an element is a pure control element: a `decision` or a pseudo-state. Control elements have no work of their own, so the structural queries pass through them and never report them.
@@ -0,0 +1,213 @@
1
+ import { ORPCError } from "@orpc/client";
2
+ //#region ../../core/graph/sync/dist/scopes.js
3
+ /** The separator between a scope's source prefix and the rest, as in `github:owner/repo`. */
4
+ const sourceSeparator = ":";
5
+ /** Thrown for a scope that names no source: it has no prefix before a colon. */
6
+ var InvalidScopeError = class extends Error {
7
+ /** The scope as it was given. */
8
+ scope;
9
+ /**
10
+ * @param scope - The scope that names no source.
11
+ */
12
+ constructor(scope) {
13
+ super(`"${scope}" is not a scope: write <source>:<scope>, such as github:owner/repo`);
14
+ this.name = "InvalidScopeError";
15
+ this.scope = scope;
16
+ }
17
+ };
18
+ /**
19
+ * Groups source-qualified scopes by their source prefix, keeping each prefix where it first appears and each source's scopes in the order given. A sync reads the groups in this order, one source after another.
20
+ * @param scopes - Source-qualified scopes, such as `github:owner` or `github:owner/repo`.
21
+ * @returns One group per source prefix.
22
+ * @throws InvalidScopeError for a scope with nothing before its first colon, or no colon at all.
23
+ */
24
+ const scopesBySource = (scopes) => {
25
+ const groups = /* @__PURE__ */ new Map();
26
+ for (const scope of scopes) {
27
+ const separator = scope.indexOf(sourceSeparator);
28
+ if (separator <= 0) throw new InvalidScopeError(scope);
29
+ const prefix = scope.slice(0, separator);
30
+ groups.set(prefix, [...groups.get(prefix) ?? [], scope]);
31
+ }
32
+ return [...groups].map(([prefix, grouped]) => ({
33
+ prefix,
34
+ scopes: grouped
35
+ }));
36
+ };
37
+ //#endregion
38
+ //#region ../../core/graph/sync/dist/sync.js
39
+ /** Thrown when a scope names a source no reader serves. */
40
+ var UnknownSourceError = class extends Error {
41
+ /** The source prefix no reader serves. */
42
+ prefix;
43
+ /**
44
+ * @param prefix - The source prefix no reader serves.
45
+ */
46
+ constructor(prefix) {
47
+ super(`No source reads "${prefix}" scopes.`);
48
+ this.name = "UnknownSourceError";
49
+ this.prefix = prefix;
50
+ }
51
+ };
52
+ /**
53
+ * Thrown by `syncScopes` when a source fails while it is being read: opening its stream or reading a page. Its message is the source's, its cause is what the source threw, and where to carry on is data in `resume` and `remaining`. A failure of the store, or of the request itself (an unknown source, a malformed scope), is thrown as it is.
54
+ */
55
+ var InterruptedSyncError = class extends Error {
56
+ resume;
57
+ remaining;
58
+ saved;
59
+ /**
60
+ * @param interruption - Where the sync carries on.
61
+ * @param cause - What the source threw.
62
+ */
63
+ constructor(interruption, cause) {
64
+ super(cause instanceof Error ? cause.message : String(cause), { cause });
65
+ this.name = "InterruptedSyncError";
66
+ this.resume = interruption.resume;
67
+ this.remaining = interruption.remaining;
68
+ this.saved = interruption.saved;
69
+ }
70
+ };
71
+ /** The oRPC client options carrying a signal, leaving the key out when there is none, since the options type does not accept an explicit `undefined`. */
72
+ const callOptions = (signal) => signal === void 0 ? {} : { signal };
73
+ /** The identifiers of the elements and participants a model in the store already has, or none when the store has no such model. */
74
+ const storedIdentifiers = async (store, modelId, signal) => {
75
+ try {
76
+ const stored = await store.v1.models.load({ modelId }, callOptions(signal));
77
+ return /* @__PURE__ */ new Set([...stored.model.elements.map((element) => element.id), ...stored.model.participants.map((participant) => participant.id)]);
78
+ } catch (error) {
79
+ if (error instanceof ORPCError && error.code === "NOT_FOUND") return /* @__PURE__ */ new Set();
80
+ throw error;
81
+ }
82
+ };
83
+ const relationKey = (relation) => JSON.stringify([
84
+ relation.kind,
85
+ relation.from,
86
+ relation.to
87
+ ]);
88
+ /** Splits what is waiting to be written into what can be written now, whose ends are all known, and what must wait for a later page. */
89
+ const settle = (pending, endsOf, known, last) => {
90
+ const ready = pending.filter((item) => last || endsOf(item).every((id) => known.has(id)));
91
+ return {
92
+ ready,
93
+ held: pending.filter((item) => !ready.includes(item))
94
+ };
95
+ };
96
+ /**
97
+ * Reads the scopes from their sources page by page and merges each page into the stored model as soon as it is read: participants, bound activities, transitions, relations and run state. Progress is kept as it is made, so a sync that is cancelled, rate limited or interrupted leaves everything it read in the store, and a checkpoint to carry on from.
98
+ *
99
+ * A store rejects a write that leaves a transition or a relation naming an element or participant the model lacks, and a source may report a dependency or a sub-issue whose other end a later page brings. A transition or relation is therefore held back until the model has both of its ends, and written with the page that completes it. One whose end never arrives is written with the last page, where the store's rejection says which reference is missing.
100
+ * @param request - The store, sources, model, scopes, an optional filter and checkpoint, the signal and a progress listener.
101
+ * @returns The final progress, with no checkpoint.
102
+ * @throws InvalidScopeError for a scope that names no source; UnknownSourceError for a scope whose source no reader serves; an Error for a checkpoint naming a source none of the scopes use, and for a sync of no scopes; InterruptedSyncError, after the pages before it are written, when a source fails while it is read, an abort included; and whatever the store throws.
103
+ */
104
+ const syncScopes = async (request) => {
105
+ const { store, sources, modelId, scopes, filter, resume, signal } = request;
106
+ const groups = scopesBySource(scopes).map((group) => {
107
+ const client = sources(group.prefix);
108
+ if (client === void 0) throw new UnknownSourceError(group.prefix);
109
+ return {
110
+ ...group,
111
+ client
112
+ };
113
+ });
114
+ if (groups.length === 0) throw new Error("A sync needs at least one scope.");
115
+ const startGroup = resume === void 0 ? 0 : groups.findIndex((group) => group.prefix === resume.source);
116
+ if (startGroup === -1) throw new Error("The checkpoint names a source that none of the scopes use; start the sync again.");
117
+ const known = new Set(await storedIdentifiers(store, modelId, signal));
118
+ const written = {
119
+ participants: /* @__PURE__ */ new Set(),
120
+ activities: /* @__PURE__ */ new Set(),
121
+ transitions: /* @__PURE__ */ new Set(),
122
+ relations: /* @__PURE__ */ new Set()
123
+ };
124
+ const gaps = [];
125
+ let heldTransitions = [];
126
+ let heldRelations = [];
127
+ let pages = 0;
128
+ let latest;
129
+ const checkpointAt = (source, after) => ({
130
+ modelId,
131
+ scopes,
132
+ ...filter === void 0 ? {} : { filter },
133
+ source,
134
+ ...after === void 0 ? {} : { after }
135
+ });
136
+ const write = async (page, checkpoint) => {
137
+ for (const record of [...page.participants, ...page.activities]) known.add(record.id);
138
+ const last = checkpoint === void 0;
139
+ const transitions = settle([...heldTransitions, ...page.transitions], (transition) => [transition.source, transition.target], known, last);
140
+ const relations = settle([...heldRelations, ...page.relations], (relation) => [relation.from, relation.to], known, last);
141
+ heldTransitions = transitions.held;
142
+ heldRelations = relations.held;
143
+ const { revision } = await store.v1.items.upsert({
144
+ modelId,
145
+ items: {
146
+ participants: page.participants,
147
+ activities: page.activities,
148
+ transitions: transitions.ready,
149
+ relations: relations.ready,
150
+ overlay: page.overlay
151
+ }
152
+ }, callOptions(signal));
153
+ pages += 1;
154
+ for (const participant of page.participants) written.participants.add(participant.id);
155
+ for (const activity of page.activities) written.activities.add(activity.id);
156
+ for (const transition of transitions.ready) written.transitions.add(transition.id);
157
+ for (const relation of relations.ready) written.relations.add(relationKey(relation));
158
+ gaps.push(...page.gaps);
159
+ latest = {
160
+ pages,
161
+ revision,
162
+ participants: written.participants.size,
163
+ activities: written.activities.size,
164
+ transitions: written.transitions.size,
165
+ relations: written.relations.size,
166
+ gaps: [...gaps],
167
+ checkpoint
168
+ };
169
+ request.onProgress?.(latest);
170
+ };
171
+ /** Reads the sources in order, each only after the one before has finished, since a source's failure must stop the ones after it. */
172
+ const readFrom = async (index) => {
173
+ const group = groups[index];
174
+ if (group === void 0) return;
175
+ let after = index === startGroup ? resume?.after : void 0;
176
+ const following = groups[index + 1];
177
+ const interrupted = (cause) => new InterruptedSyncError({
178
+ resume: checkpointAt(group.prefix, after),
179
+ remaining: groups.slice(index + 1).flatMap((next) => next.scopes),
180
+ saved: pages > 0
181
+ }, cause);
182
+ const iterator = (await group.client.v1.sync.stream({
183
+ scope: group.scopes,
184
+ ...after === void 0 ? {} : { after },
185
+ ...filter === void 0 ? {} : { filter }
186
+ }, callOptions(signal)).catch((cause) => {
187
+ throw interrupted(cause);
188
+ }))[Symbol.asyncIterator]();
189
+ /** Writes each page the source yields, in order, each only after the one before is written. */
190
+ const drain = async () => {
191
+ const next = await iterator.next().catch((cause) => {
192
+ throw interrupted(cause);
193
+ });
194
+ if (next.done === true) return;
195
+ const page = next.value;
196
+ await write(page, page.next !== void 0 ? checkpointAt(group.prefix, page.next) : following === void 0 ? void 0 : checkpointAt(following.prefix, void 0));
197
+ after = page.next;
198
+ await drain();
199
+ };
200
+ try {
201
+ await drain();
202
+ } catch (error) {
203
+ await iterator.return();
204
+ throw error;
205
+ }
206
+ await readFrom(index + 1);
207
+ };
208
+ await readFrom(startGroup);
209
+ if (latest === void 0) throw new Error("unreachable: every source answers a sync with at least one page");
210
+ return latest;
211
+ };
212
+ //#endregion
213
+ export { scopesBySource as a, InvalidScopeError as i, UnknownSourceError as n, syncScopes as r, InterruptedSyncError as t };
package/dist/sync.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { a as SyncRequest, c as syncScopes, i as SyncProgress, l as SyncCheckpoint, n as SourceLookup, o as SyncStore, r as SyncInterruption, s as UnknownSourceError, t as InterruptedSyncError, u as syncCheckpointSchema } from "./index-DoMIav3D.js";
2
+ //#region ../../core/graph/sync/dist/scopes.d.ts
3
+ /** The scopes of one source, in the order they were given. */
4
+ interface SourceScopes {
5
+ /** The source's identifier prefix, such as `github`. */
6
+ readonly prefix: string;
7
+ readonly scopes: readonly string[];
8
+ }
9
+ /** Thrown for a scope that names no source: it has no prefix before a colon. */
10
+ export declare class InvalidScopeError extends Error {
11
+ /** The scope as it was given. */
12
+ readonly scope: string;
13
+ /**
14
+ * @param scope - The scope that names no source.
15
+ */
16
+ constructor(scope: string);
17
+ }
18
+ /**
19
+ * Groups source-qualified scopes by their source prefix, keeping each prefix where it first appears and each source's scopes in the order given. A sync reads the groups in this order, one source after another.
20
+ * @param scopes - Source-qualified scopes, such as `github:owner` or `github:owner/repo`.
21
+ * @returns One group per source prefix.
22
+ * @throws InvalidScopeError for a scope with nothing before its first colon, or no colon at all.
23
+ */
24
+ export declare const scopesBySource: (scopes: readonly string[]) => readonly SourceScopes[];
25
+ //#endregion
26
+ export { InterruptedSyncError, type SourceLookup, type SourceScopes, type SyncCheckpoint, type SyncInterruption, type SyncProgress, type SyncRequest, type SyncStore, UnknownSourceError, syncCheckpointSchema, syncScopes };
package/dist/sync.js ADDED
@@ -0,0 +1,16 @@
1
+ import { g as syncFilterSchema } from "./schema-DVAttrJg.js";
2
+ import { a as scopesBySource, i as InvalidScopeError, n as UnknownSourceError, r as syncScopes, t as InterruptedSyncError } from "./sync-CkaJO3OQ.js";
3
+ import { z } from "zod";
4
+ //#region ../../core/graph/sync/dist/checkpoint.js
5
+ /**
6
+ * Where a sync stopped: the model, every scope it was asked for in order, the filter it read them with, the prefix of the source to read next, and the cursor after the last page it wrote within that source's scopes (absent to read them from the start). Everything before it is in the store, so a sync started from it reads only what is left. A source's cursor is valid only with the scopes and filter it was issued for, which is why both are kept with it.
7
+ */
8
+ const syncCheckpointSchema = z.strictObject({
9
+ modelId: z.string().min(1),
10
+ scopes: z.array(z.string().min(1)).readonly(),
11
+ filter: syncFilterSchema.optional(),
12
+ source: z.string().min(1),
13
+ after: z.string().min(1).optional()
14
+ }).readonly();
15
+ //#endregion
16
+ export { InterruptedSyncError, InvalidScopeError, UnknownSourceError, scopesBySource, syncCheckpointSchema, syncScopes };