@medicine-wheel/developer-repository-mapping 0.0.0-stage → 0.22.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 (74) hide show
  1. package/README.md +68 -2
  2. package/dist/check.d.ts +28 -0
  3. package/dist/check.d.ts.map +1 -0
  4. package/dist/check.js +111 -0
  5. package/dist/check.js.map +1 -0
  6. package/dist/cli.d.ts +26 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +408 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/ids.d.ts +28 -0
  11. package/dist/ids.d.ts.map +1 -0
  12. package/dist/ids.js +68 -0
  13. package/dist/ids.js.map +1 -0
  14. package/dist/index.d.ts +40 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +89 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/jsonl.d.ts +19 -0
  19. package/dist/jsonl.d.ts.map +1 -0
  20. package/dist/jsonl.js +63 -0
  21. package/dist/jsonl.js.map +1 -0
  22. package/dist/lifecycle.d.ts +53 -0
  23. package/dist/lifecycle.d.ts.map +1 -0
  24. package/dist/lifecycle.js +78 -0
  25. package/dist/lifecycle.js.map +1 -0
  26. package/dist/merge.d.ts +30 -0
  27. package/dist/merge.d.ts.map +1 -0
  28. package/dist/merge.js +94 -0
  29. package/dist/merge.js.map +1 -0
  30. package/dist/node/fs.d.ts +28 -0
  31. package/dist/node/fs.d.ts.map +1 -0
  32. package/dist/node/fs.js +162 -0
  33. package/dist/node/fs.js.map +1 -0
  34. package/dist/node/git.d.ts +15 -0
  35. package/dist/node/git.d.ts.map +1 -0
  36. package/dist/node/git.js +73 -0
  37. package/dist/node/git.js.map +1 -0
  38. package/dist/node/hub.d.ts +45 -0
  39. package/dist/node/hub.d.ts.map +1 -0
  40. package/dist/node/hub.js +113 -0
  41. package/dist/node/hub.js.map +1 -0
  42. package/dist/node/index.d.ts +16 -0
  43. package/dist/node/index.d.ts.map +1 -0
  44. package/dist/node/index.js +30 -0
  45. package/dist/node/index.js.map +1 -0
  46. package/dist/node/page.d.ts +9 -0
  47. package/dist/node/page.d.ts.map +1 -0
  48. package/dist/node/page.js +219 -0
  49. package/dist/node/page.js.map +1 -0
  50. package/dist/node/server.d.ts +33 -0
  51. package/dist/node/server.d.ts.map +1 -0
  52. package/dist/node/server.js +111 -0
  53. package/dist/node/server.js.map +1 -0
  54. package/dist/read.d.ts +13 -0
  55. package/dist/read.d.ts.map +1 -0
  56. package/dist/read.js +38 -0
  57. package/dist/read.js.map +1 -0
  58. package/dist/schemas.d.ts +409 -0
  59. package/dist/schemas.d.ts.map +1 -0
  60. package/dist/schemas.js +160 -0
  61. package/dist/schemas.js.map +1 -0
  62. package/dist/summary.d.ts +31 -0
  63. package/dist/summary.d.ts.map +1 -0
  64. package/dist/summary.js +64 -0
  65. package/dist/summary.js.map +1 -0
  66. package/dist/types.d.ts +184 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +36 -0
  69. package/dist/types.js.map +1 -0
  70. package/dist/wheel.d.ts +14 -0
  71. package/dist/wheel.d.ts.map +1 -0
  72. package/dist/wheel.js +74 -0
  73. package/dist/wheel.js.map +1 -0
  74. package/package.json +63 -4
package/README.md CHANGED
@@ -1,3 +1,69 @@
1
- # Temporary Holding Version
1
+ # @medicine-wheel/developer-repository-mapping
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Repository mapping for the Medicine Wheel Developer Suite: an owner says what each of
4
+ their repositories is, in rows tools can read, and one hub reads the others.
5
+
6
+ Each owner keeps a folder in the repository named after them:
7
+
8
+ ```
9
+ <owner>/<owner>/.miadi/repository-mapping/
10
+ manifest.json what the folder is: owner, hub, kind (owner | meta)
11
+ repositories.jsonl lifecycle, purpose and horizon in ≤ 55 words, fields, prototypes, git reading
12
+ features.jsonl features observed in a repository (a prototype's three core ones)
13
+ capabilities.jsonl platform capabilities (usually in the meta hub)
14
+ relations.jsonl forked-from, grew-into, consumes, provides, realizes, field-aligns-with
15
+ owners.jsonl meta hub only: the hubs it reads
16
+ ```
17
+
18
+ Every row says who wrote it, when, from what evidence, and whether a person has read it
19
+ since. A row a model writes starts `pending`.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm install -g @medicine-wheel/developer-repository-mapping # for the mw-repomap CLI
25
+ npm install @medicine-wheel/developer-repository-mapping # for the library
26
+ ```
27
+
28
+ Node 19 or later. Depends on `@medicine-wheel/ontology-core`, `@medicine-wheel/capabilities` and `zod`.
29
+
30
+ ## mw-repomap
31
+
32
+ ```bash
33
+ mw-repomap init jgwill --owner jgwill --meta # an empty folder in the jgwill/jgwill clone
34
+ mw-repomap observe ~/repos/jgwill/* # git readings, one JSON line per clone
35
+ mw-repomap observe ~/repos/jgwill/* --dir jgwill --write # store them on the repository rows
36
+ mw-repomap validate jgwill # severity file:line node — message; exit 1 on errors
37
+ mw-repomap list jgwill --lifecycle prototype --drift drifted
38
+ mw-repomap show jgwill/smcraft jgwill
39
+ mw-repomap move jgwill/smcraft concretize jgwill --by Guillaume --grounding jgwill/smcraft#12
40
+ mw-repomap hub jgwill # every registered hub, read or not
41
+ mw-repomap serve jgwill # http://127.0.0.1:3967/
42
+ mw-repomap wheel jgwill # the mapping as wheel nodes and edges
43
+ ```
44
+
45
+ A meta hub reads every hub it registers (from a local path, or from GitHub by default)
46
+ unless `--local`. `observe --write` and `move` refuse to rewrite a folder while any row
47
+ fails to parse. Exit codes: 0 done, 1 errors or a failed read, 2 usage error.
48
+
49
+ ## Library
50
+
51
+ ```ts
52
+ import { checkMapping, mergeMappings, reconcileLifecycle, transition, toWheelNodes } from '@medicine-wheel/developer-repository-mapping';
53
+ import { readHub, readActivity, createMappingServer } from '@medicine-wheel/developer-repository-mapping/node';
54
+ ```
55
+
56
+ The main entry is pure: ids, schemas, the lifecycle state machine (`transition`), activity
57
+ and drift (`classifyActivity`, `reconcileLifecycle`), JSONL parsing that reports bad lines
58
+ by number, `checkMapping`, the meta mapping (`mergeMappings`), `summarize`, and the
59
+ projection into the wheel (`toWheelNodes`, `toWheelEdges`). `./node` holds file, git and
60
+ HTTP access.
61
+
62
+ ## Lifecycle
63
+
64
+ `unclassified` → `explore` → `prototype` → `concretize` → `implementation` → `release` →
65
+ `maintained`, `archive` from any of them, `reopen` back to `unclassified`. How recently
66
+ anyone committed is read from git separately and compared with the declared lifecycle:
67
+ `converged`, `drifted`, `unobserved` or `undeclared`. Drift is reported, never corrected.
68
+
69
+ Spec, drawings and relational context: [`rispecs/developer-suite/repository-mapping/`](https://github.com/jgwill/medicine-wheel/tree/main/rispecs/developer-suite/repository-mapping).
@@ -0,0 +1,28 @@
1
+ /**
2
+ * What a mapping says about itself once its rows are read: duplicates, rows
3
+ * in the wrong hub, features of a repository the mapping does not hold, and
4
+ * references to nodes it does not hold.
5
+ *
6
+ * A reference to a node the mapping does not hold is a warning, not an
7
+ * error. A fork's origin lives in someone else's hub, and the platform's
8
+ * capabilities live in the meta hub; a merged reading resolves them.
9
+ */
10
+ import { type MappingIssue, type RepositoryMapping } from './types';
11
+ export interface CheckOptions {
12
+ /** A merged reading of several hubs: rows from many owners are expected. */
13
+ merged?: boolean;
14
+ }
15
+ export declare function relationKey(row: {
16
+ from: string;
17
+ type: string;
18
+ to: string;
19
+ }): string;
20
+ /**
21
+ * Whose word a relation is: the owner of the repository (or of the feature's
22
+ * repository) it starts from. `undefined` for a relation from a service, which
23
+ * only the meta hub speaks for.
24
+ */
25
+ export declare function speakingOwner(from: string): string | undefined;
26
+ export declare function checkMapping(mapping: RepositoryMapping, options?: CheckOptions): MappingIssue[];
27
+ export declare function hasErrors(issues: readonly MappingIssue[]): boolean;
28
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAA2B,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE7F,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEnF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG9D;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,GAAE,YAAiB,GAAG,YAAY,EAAE,CAoFnG;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,OAAO,CAElE"}
package/dist/check.js ADDED
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * What a mapping says about itself once its rows are read: duplicates, rows
4
+ * in the wrong hub, features of a repository the mapping does not hold, and
5
+ * references to nodes it does not hold.
6
+ *
7
+ * A reference to a node the mapping does not hold is a warning, not an
8
+ * error. A fork's origin lives in someone else's hub, and the platform's
9
+ * capabilities live in the meta hub; a merged reading resolves them.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.relationKey = relationKey;
13
+ exports.speakingOwner = speakingOwner;
14
+ exports.checkMapping = checkMapping;
15
+ exports.hasErrors = hasErrors;
16
+ const ids_1 = require("./ids");
17
+ const types_1 = require("./types");
18
+ function relationKey(row) {
19
+ return `${row.from} ${row.type} ${row.to}`;
20
+ }
21
+ /**
22
+ * Whose word a relation is: the owner of the repository (or of the feature's
23
+ * repository) it starts from. `undefined` for a relation from a service, which
24
+ * only the meta hub speaks for.
25
+ */
26
+ function speakingOwner(from) {
27
+ const repo = from.startsWith('feature:') ? (0, ids_1.featureRepository)(from) : from;
28
+ return repo ? (0, ids_1.parseRepositoryNodeId)(repo)?.owner : undefined;
29
+ }
30
+ function checkMapping(mapping, options = {}) {
31
+ const issues = [];
32
+ const { manifest } = mapping;
33
+ const error = (message, nodeId, file) => issues.push({ severity: 'error', message, ...(nodeId ? { nodeId } : {}), ...(file ? { file } : {}) });
34
+ const warning = (message, nodeId, file) => issues.push({ severity: 'warning', message, ...(nodeId ? { nodeId } : {}), ...(file ? { file } : {}) });
35
+ // GitHub names are case-insensitive: jgwill/Miadi and jgwill/miadi are one repository.
36
+ const held = new Set();
37
+ const heldFolded = new Set();
38
+ const holdOnce = (id, file) => {
39
+ if (heldFolded.has(id.toLowerCase()))
40
+ error(`${id} appears more than once`, id, file);
41
+ held.add(id);
42
+ heldFolded.add(id.toLowerCase());
43
+ };
44
+ mapping.repositories.forEach((r) => holdOnce(r.nodeId, 'repositories.jsonl'));
45
+ mapping.features.forEach((f) => holdOnce(f.nodeId, 'features.jsonl'));
46
+ mapping.capabilities.forEach((c) => holdOnce(c.nodeId, 'capabilities.jsonl'));
47
+ const repositories = new Map(mapping.repositories.map((r) => [r.nodeId, r]));
48
+ if (!options.merged) {
49
+ for (const r of mapping.repositories) {
50
+ const owner = (0, ids_1.parseRepositoryNodeId)(r.nodeId)?.owner;
51
+ if (owner && owner.toLowerCase() !== manifest.owner.toLowerCase()) {
52
+ error(`${r.nodeId} belongs in ${owner}/${owner}'s mapping, not ${manifest.hub}'s`, r.nodeId, 'repositories.jsonl');
53
+ }
54
+ }
55
+ if (manifest.kind === 'owner' && mapping.owners.length) {
56
+ error(`only a meta hub registers owners; ${manifest.hub} is an owner hub`, undefined, 'owners.jsonl');
57
+ }
58
+ for (const rel of mapping.relations) {
59
+ const owner = speakingOwner(rel.from);
60
+ if (owner && owner.toLowerCase() !== manifest.owner.toLowerCase()) {
61
+ error(`${relationKey(rel)} speaks for ${owner}'s ${rel.from}; it belongs in ${owner}/${owner}'s mapping`, rel.from, 'relations.jsonl');
62
+ }
63
+ }
64
+ }
65
+ const featureCount = new Map();
66
+ for (const f of mapping.features) {
67
+ if (!repositories.has(f.repository)) {
68
+ error(`${f.nodeId} is a feature of ${f.repository}, which this mapping does not hold`, f.nodeId, 'features.jsonl');
69
+ }
70
+ featureCount.set(f.repository, (featureCount.get(f.repository) ?? 0) + 1);
71
+ if (f.realizes && !held.has(f.realizes)) {
72
+ warning(`${f.nodeId} realizes ${f.realizes}, which this mapping does not hold`, f.nodeId, 'features.jsonl');
73
+ }
74
+ }
75
+ for (const [repo, count] of featureCount) {
76
+ if (repositories.get(repo)?.lifecycle === 'prototype' && count > types_1.PROTOTYPE_CORE_FEATURES) {
77
+ warning(`${repo} is a prototype with ${count} features; name its ${types_1.PROTOTYPE_CORE_FEATURES} core ones`, repo, 'features.jsonl');
78
+ }
79
+ }
80
+ for (const c of mapping.capabilities) {
81
+ for (const provider of c.providedBy) {
82
+ if (provider.startsWith('repository:') && !held.has(provider)) {
83
+ warning(`${c.nodeId} is provided by ${provider}, which this mapping does not hold`, c.nodeId, 'capabilities.jsonl');
84
+ }
85
+ }
86
+ }
87
+ const relations = new Set();
88
+ for (const rel of mapping.relations) {
89
+ const key = relationKey(rel);
90
+ if (relations.has(key))
91
+ error(`${key} appears more than once`, rel.from, 'relations.jsonl');
92
+ relations.add(key);
93
+ for (const end of [rel.from, rel.to]) {
94
+ const kind = end.slice(0, end.indexOf(':'));
95
+ if ((kind === 'repository' || kind === 'feature' || kind === 'capability') && !held.has(end)) {
96
+ warning(`${key}: ${end} is not in this mapping`, rel.from, 'relations.jsonl');
97
+ }
98
+ }
99
+ }
100
+ const hubs = new Set();
101
+ for (const o of mapping.owners) {
102
+ if (hubs.has(o.hub))
103
+ error(`${o.hub} is registered more than once`, undefined, 'owners.jsonl');
104
+ hubs.add(o.hub);
105
+ }
106
+ return issues;
107
+ }
108
+ function hasErrors(issues) {
109
+ return issues.some((i) => i.severity === 'error');
110
+ }
111
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mw-repomap — make, check, observe and serve a repository mapping.
4
+ *
5
+ * mw-repomap init [<hub-dir>] --owner <login> [--meta] [--description <text>]
6
+ * mw-repomap validate [<path>] [--local] [--json]
7
+ * mw-repomap list [<path>] [--lifecycle <state>] [--drift <drift>] [--revision <state>] [--field <id>] [--local] [--json]
8
+ * mw-repomap show <repository> [<path>] [--local] [--json]
9
+ * mw-repomap observe <clone>... [--dir <path>] [--by <observer>] [--write]
10
+ * mw-repomap move <repository> <event> [<path>] --by <who> [--grounding <evidence>]
11
+ * mw-repomap hub [<path>] [--json]
12
+ * mw-repomap serve [<path>] [--port <n>] [--host <address>] [--local]
13
+ * mw-repomap wheel [<path>] [--local]
14
+ *
15
+ * <path> is a mapping folder, or an owner hub clone holding
16
+ * .miadi/repository-mapping/; the current directory when absent. A meta hub
17
+ * reads every owner hub it registers, unless --local. <repository> is
18
+ * owner/name or repository:owner/name.
19
+ *
20
+ * `observe --write` and `move` rewrite the folder's JSONL, and refuse while
21
+ * any row fails to parse: a row that did not parse would not be written back.
22
+ *
23
+ * Exit codes: 0 done, 1 the mapping has errors or a read failed, 2 usage error.
24
+ */
25
+ export {};
26
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
package/dist/cli.js ADDED
@@ -0,0 +1,408 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * mw-repomap — make, check, observe and serve a repository mapping.
5
+ *
6
+ * mw-repomap init [<hub-dir>] --owner <login> [--meta] [--description <text>]
7
+ * mw-repomap validate [<path>] [--local] [--json]
8
+ * mw-repomap list [<path>] [--lifecycle <state>] [--drift <drift>] [--revision <state>] [--field <id>] [--local] [--json]
9
+ * mw-repomap show <repository> [<path>] [--local] [--json]
10
+ * mw-repomap observe <clone>... [--dir <path>] [--by <observer>] [--write]
11
+ * mw-repomap move <repository> <event> [<path>] --by <who> [--grounding <evidence>]
12
+ * mw-repomap hub [<path>] [--json]
13
+ * mw-repomap serve [<path>] [--port <n>] [--host <address>] [--local]
14
+ * mw-repomap wheel [<path>] [--local]
15
+ *
16
+ * <path> is a mapping folder, or an owner hub clone holding
17
+ * .miadi/repository-mapping/; the current directory when absent. A meta hub
18
+ * reads every owner hub it registers, unless --local. <repository> is
19
+ * owner/name or repository:owner/name.
20
+ *
21
+ * `observe --write` and `move` rewrite the folder's JSONL, and refuse while
22
+ * any row fails to parse: a row that did not parse would not be written back.
23
+ *
24
+ * Exit codes: 0 done, 1 the mapping has errors or a read failed, 2 usage error.
25
+ */
26
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ var desc = Object.getOwnPropertyDescriptor(m, k);
29
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
30
+ desc = { enumerable: true, get: function() { return m[k]; } };
31
+ }
32
+ Object.defineProperty(o, k2, desc);
33
+ }) : (function(o, m, k, k2) {
34
+ if (k2 === undefined) k2 = k;
35
+ o[k2] = m[k];
36
+ }));
37
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
38
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
39
+ }) : function(o, v) {
40
+ o["default"] = v;
41
+ });
42
+ var __importStar = (this && this.__importStar) || (function () {
43
+ var ownKeys = function(o) {
44
+ ownKeys = Object.getOwnPropertyNames || function (o) {
45
+ var ar = [];
46
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
47
+ return ar;
48
+ };
49
+ return ownKeys(o);
50
+ };
51
+ return function (mod) {
52
+ if (mod && mod.__esModule) return mod;
53
+ var result = {};
54
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
55
+ __setModuleDefault(result, mod);
56
+ return result;
57
+ };
58
+ })();
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ const os = __importStar(require("node:os"));
61
+ const path = __importStar(require("node:path"));
62
+ const check_1 = require("./check");
63
+ const ids_1 = require("./ids");
64
+ const lifecycle_1 = require("./lifecycle");
65
+ const schemas_1 = require("./schemas");
66
+ const summary_1 = require("./summary");
67
+ const types_1 = require("./types");
68
+ const wheel_1 = require("./wheel");
69
+ const fs_1 = require("./node/fs");
70
+ const git_1 = require("./node/git");
71
+ const hub_1 = require("./node/hub");
72
+ const server_1 = require("./node/server");
73
+ const USAGE = [
74
+ 'usage: mw-repomap init [<hub-dir>] --owner <login> [--meta] [--description <text>]',
75
+ ' mw-repomap validate [<path>] [--local] [--json]',
76
+ ' mw-repomap list [<path>] [--lifecycle <state>] [--drift <drift>] [--revision <state>] [--field <id>] [--local] [--json]',
77
+ ' mw-repomap show <repository> [<path>] [--local] [--json]',
78
+ ' mw-repomap observe <clone>... [--dir <path>] [--by <observer>] [--write]',
79
+ ' mw-repomap move <repository> <event> [<path>] --by <who> [--grounding <evidence>]',
80
+ ' mw-repomap hub [<path>] [--json]',
81
+ ' mw-repomap serve [<path>] [--port <n>] [--host <address>] [--local]',
82
+ ' mw-repomap wheel [<path>] [--local]',
83
+ ].join('\n');
84
+ const BOOLEAN_FLAGS = new Set(['meta', 'local', 'json', 'write', 'help']);
85
+ /** The flags each command takes. Any other flag is a usage error, so a misspelling never passes silently. */
86
+ const COMMAND_FLAGS = {
87
+ init: ['owner', 'meta', 'description'],
88
+ validate: ['local', 'json'],
89
+ list: ['lifecycle', 'drift', 'revision', 'field', 'local', 'json'],
90
+ show: ['local', 'json'],
91
+ observe: ['dir', 'by', 'write'],
92
+ move: ['by', 'grounding'],
93
+ hub: ['json'],
94
+ serve: ['port', 'host', 'local'],
95
+ wheel: ['local'],
96
+ };
97
+ class UsageError extends Error {
98
+ }
99
+ function parse(argv) {
100
+ const positional = [];
101
+ const flags = {};
102
+ for (let i = 0; i < argv.length; i++) {
103
+ const arg = argv[i];
104
+ if (!arg.startsWith('--')) {
105
+ positional.push(arg);
106
+ continue;
107
+ }
108
+ const key = arg.slice(2);
109
+ if (BOOLEAN_FLAGS.has(key)) {
110
+ flags[key] = true;
111
+ continue;
112
+ }
113
+ const value = argv[++i];
114
+ if (value === undefined || value.startsWith('--'))
115
+ throw new UsageError(`--${key} needs a value`);
116
+ flags[key] = value;
117
+ }
118
+ const allowed = COMMAND_FLAGS[positional[0]];
119
+ if (allowed) {
120
+ const unknown = Object.keys(flags).filter((key) => !allowed.includes(key));
121
+ if (unknown.length) {
122
+ throw new UsageError(`${positional[0]} does not take ${unknown.map((k) => `--${k}`).join(', ')}; it takes ${allowed.map((k) => `--${k}`).join(', ') || 'no flags'}`);
123
+ }
124
+ }
125
+ return { positional, flags };
126
+ }
127
+ /** A flag's value, `undefined` when absent. An empty value is a usage error, not an absent flag. */
128
+ const text = (flags, key) => {
129
+ const value = flags[key];
130
+ if (typeof value !== 'string')
131
+ return undefined;
132
+ if (!value.trim())
133
+ throw new UsageError(`--${key} needs a value`);
134
+ return value;
135
+ };
136
+ function printIssues(issues) {
137
+ for (const i of issues) {
138
+ const where = `${i.file ?? ''}${i.line ? `:${i.line}` : ''}`;
139
+ console.error(`${i.severity.padEnd(7)} ${where}${i.nodeId ? ` ${i.nodeId}` : ''} — ${i.message}`);
140
+ }
141
+ }
142
+ async function mappingDir(arg) {
143
+ const start = arg ?? process.cwd();
144
+ const dir = await (0, fs_1.findMappingDir)(start);
145
+ if (!dir) {
146
+ throw new Error(`no mapping folder at ${path.resolve(start)} (mw-repomap init --owner <login> makes one)`);
147
+ }
148
+ return dir;
149
+ }
150
+ async function hubOrFail(arg, local) {
151
+ const read = await (0, hub_1.readHub)(await mappingDir(arg), { local });
152
+ if (!read.merged) {
153
+ printIssues(read.issues);
154
+ throw new Error('the manifest could not be read');
155
+ }
156
+ return read;
157
+ }
158
+ /** Read the folder for a rewrite; refuse while any row fails to parse. */
159
+ async function forRewrite(dir) {
160
+ const read = await (0, fs_1.readMappingDir)(dir);
161
+ if (!read.mapping || (0, check_1.hasErrors)(read.issues)) {
162
+ printIssues(read.issues);
163
+ throw new Error('rows fail to parse; fix them before rewriting this folder (mw-repomap validate)');
164
+ }
165
+ return read.mapping;
166
+ }
167
+ const defaultObserver = () => `${os.userInfo().username}@${os.hostname()}`;
168
+ async function init(positional, flags) {
169
+ const owner = text(flags, 'owner');
170
+ if (!owner)
171
+ throw new UsageError('init needs --owner <login>');
172
+ const hubDir = path.resolve(positional[1] ?? process.cwd());
173
+ const dir = hubDir.endsWith(types_1.MAPPING_DIR) ? hubDir : path.join(hubDir, types_1.MAPPING_DIR);
174
+ const manifest = schemas_1.MappingManifestSchema.safeParse({
175
+ schema: types_1.MAPPING_SCHEMA,
176
+ version: 1,
177
+ owner,
178
+ hub: `${owner}/${owner}`,
179
+ kind: flags.meta ? 'meta' : 'owner',
180
+ ...(text(flags, 'description') ? { description: text(flags, 'description') } : {}),
181
+ });
182
+ if (!manifest.success)
183
+ throw new UsageError(manifest.error.issues.map((i) => i.message).join('; '));
184
+ await (0, fs_1.initMappingDir)(dir, manifest.data);
185
+ console.log(`${manifest.data.kind} hub ${manifest.data.hub}: ${dir}`);
186
+ return 0;
187
+ }
188
+ async function validate(positional, flags) {
189
+ const read = await (0, hub_1.readHub)(await mappingDir(positional[1]), { local: flags.local === true });
190
+ const errors = read.issues.filter((i) => i.severity === 'error').length;
191
+ const warnings = read.issues.length - errors;
192
+ if (flags.json) {
193
+ console.log(JSON.stringify({ errors, warnings, hubs: read.hubs, issues: read.issues }, null, 2));
194
+ }
195
+ else {
196
+ printIssues(read.issues);
197
+ const m = read.merged;
198
+ const rows = m ? `${m.repositories.length} repositories, ${m.features.length} features, ${m.capabilities.length} capabilities, ${m.relations.length} relations` : 'nothing read';
199
+ console.error(`${errors} errors, ${warnings} warnings across ${read.hubs.length} hub(s): ${rows}`);
200
+ }
201
+ return errors ? 1 : 0;
202
+ }
203
+ async function list(positional, flags) {
204
+ const { merged } = await hubOrFail(positional[1], flags.local === true);
205
+ const readings = new Map((0, summary_1.summarize)(merged).readings.map((r) => [r.nodeId, r]));
206
+ const rows = merged.repositories.filter((r) => {
207
+ const reading = readings.get(r.nodeId);
208
+ return ((!text(flags, 'lifecycle') || r.lifecycle === text(flags, 'lifecycle')) &&
209
+ (!text(flags, 'drift') || reading?.drift === text(flags, 'drift')) &&
210
+ (!text(flags, 'revision') || r.revision === text(flags, 'revision')) &&
211
+ (!text(flags, 'field') || r.fields.includes(text(flags, 'field'))));
212
+ });
213
+ if (flags.json) {
214
+ console.log(JSON.stringify(rows.map((r) => ({ ...r, reading: readings.get(r.nodeId) })), null, 2));
215
+ return 0;
216
+ }
217
+ const width = Math.max(10, ...rows.map((r) => r.nodeId.length - 'repository:'.length));
218
+ for (const r of rows) {
219
+ const reading = readings.get(r.nodeId);
220
+ const git = reading?.activity ? `${reading.activity}${reading.daysSinceLastCommit === undefined ? '' : ` ${reading.daysSinceLastCommit}d`}` : '-';
221
+ const purpose = r.purpose ? (r.purpose.length > 70 ? `${r.purpose.slice(0, 69)}…` : r.purpose) : `(${r.status})`;
222
+ console.log([r.nodeId.slice('repository:'.length).padEnd(width), r.lifecycle.padEnd(14), git.padEnd(12), (reading?.drift ?? '').padEnd(10), r.revision.padEnd(8), purpose].join(' '));
223
+ }
224
+ console.error(`${rows.length} of ${merged.repositories.length} repositories`);
225
+ return 0;
226
+ }
227
+ async function show(positional, flags) {
228
+ const id = positional[1] ? (0, ids_1.toRepositoryNodeId)(positional[1]) : undefined;
229
+ if (!id)
230
+ throw new UsageError('show needs a repository: owner/name');
231
+ const { merged } = await hubOrFail(positional[2], flags.local === true);
232
+ const repo = merged.repositories.find((r) => r.nodeId.toLowerCase() === id.toLowerCase());
233
+ if (!repo) {
234
+ console.error(`mw-repomap: ${id} is not in this mapping`);
235
+ return 1;
236
+ }
237
+ const features = merged.features.filter((f) => f.repository === repo.nodeId);
238
+ const relations = merged.relations.filter((r) => r.from === repo.nodeId || r.to === repo.nodeId);
239
+ const reading = (0, lifecycle_1.reconcileLifecycle)(repo);
240
+ if (flags.json) {
241
+ console.log(JSON.stringify({ repository: repo, reading, features, relations, source: merged.sources[repo.nodeId] }, null, 2));
242
+ return 0;
243
+ }
244
+ const lines = [
245
+ `${repo.nodeId} ${(0, ids_1.repositoryUrl)(repo.nodeId) ?? ''}`,
246
+ ` ${repo.lifecycle} · ${repo.origin} · purpose ${repo.status} · ${repo.revision}`,
247
+ ` git: ${reading.reason} (${reading.drift})`,
248
+ repo.purpose ? ` purpose: ${repo.purpose}` : '',
249
+ repo.horizon ? ` horizon: ${repo.horizon}` : '',
250
+ repo.fields.length ? ` fields: ${repo.fields.join(', ')}` : '',
251
+ repo.prototypes.length ? ` prototypes: ${repo.prototypes.join(', ')}` : '',
252
+ ` features (${features.length}):`,
253
+ ...features.map((f) => ` ${f.name} [${f.status}]${f.realizes ? ` → ${f.realizes}` : ''}${f.statement ? `: ${f.statement}` : ''}`),
254
+ ` relations (${relations.length}):`,
255
+ ...relations.map((r) => ` ${r.from} ${r.type} ${r.to}`),
256
+ ` read: ${repo.grounding.join(', ')}`,
257
+ ` stated by ${repo.statedBy} at ${repo.statedAt}${repo.revisedBy ? `, ${repo.revision} by ${repo.revisedBy} at ${repo.revisedAt}` : ''}`,
258
+ ` from ${merged.sources[repo.nodeId]}`,
259
+ ];
260
+ console.log(lines.filter(Boolean).join('\n'));
261
+ return 0;
262
+ }
263
+ async function observe(positional, flags) {
264
+ const clones = positional.slice(1);
265
+ if (!clones.length)
266
+ throw new UsageError('observe needs at least one clone');
267
+ const observedBy = text(flags, 'by') ?? defaultObserver();
268
+ const dir = flags.write ? await mappingDir(text(flags, 'dir')) : undefined;
269
+ const mapping = dir ? await forRewrite(dir) : undefined;
270
+ let failed = 0;
271
+ let written = 0;
272
+ for (const clone of clones) {
273
+ try {
274
+ const observed = await (0, git_1.readActivity)(path.resolve(clone), { observedBy });
275
+ const nodeId = await (0, git_1.originRepository)(path.resolve(clone));
276
+ console.log(JSON.stringify({ clone, nodeId: nodeId ?? null, observed }));
277
+ if (!mapping)
278
+ continue;
279
+ // GitHub names are case-insensitive: a clone of JGWill/miadi is jgwill/Miadi's row.
280
+ const row = nodeId ? mapping.repositories.find((r) => r.nodeId.toLowerCase() === nodeId.toLowerCase()) : undefined;
281
+ if (!row) {
282
+ console.error(`mw-repomap: ${nodeId ?? clone} is not in the mapping yet; reading not written`);
283
+ continue;
284
+ }
285
+ row.observed = observed;
286
+ written += 1;
287
+ }
288
+ catch (error) {
289
+ failed += 1;
290
+ console.error(`mw-repomap: ${clone}: ${error.message}`);
291
+ }
292
+ }
293
+ if (dir && mapping) {
294
+ await (0, fs_1.writeMappingDir)(dir, mapping);
295
+ console.error(`${written} reading(s) written to ${dir}`);
296
+ }
297
+ return failed ? 1 : 0;
298
+ }
299
+ async function move(positional, flags) {
300
+ const id = positional[1] ? (0, ids_1.toRepositoryNodeId)(positional[1]) : undefined;
301
+ const event = schemas_1.LifecycleEventSchema.safeParse(positional[2]);
302
+ const by = text(flags, 'by');
303
+ if (!id || !event.success)
304
+ throw new UsageError(`move needs a repository and an event: ${schemas_1.LifecycleEventSchema.options.join(', ')}`);
305
+ if (!by)
306
+ throw new UsageError('move needs --by <who>: a lifecycle change is a statement someone makes');
307
+ const dir = await mappingDir(positional[3]);
308
+ const mapping = await forRewrite(dir);
309
+ const row = mapping.repositories.find((r) => r.nodeId.toLowerCase() === id.toLowerCase());
310
+ if (!row) {
311
+ console.error(`mw-repomap: ${id} is not in ${dir}`);
312
+ return 1;
313
+ }
314
+ const result = (0, lifecycle_1.transition)(row.lifecycle, event.data);
315
+ if (!result.ok) {
316
+ console.error(`mw-repomap: ${result.reason}`);
317
+ return 1;
318
+ }
319
+ row.lifecycle = result.to;
320
+ row.statedBy = by;
321
+ row.statedAt = new Date().toISOString();
322
+ row.revision = 'pending';
323
+ delete row.revisedBy;
324
+ delete row.revisedAt;
325
+ const grounding = text(flags, 'grounding');
326
+ if (grounding && !row.grounding.includes(grounding))
327
+ row.grounding.push(grounding);
328
+ await (0, fs_1.writeMappingDir)(dir, mapping);
329
+ console.log(`${id}: ${result.from} --${result.event}--> ${result.to}`);
330
+ return 0;
331
+ }
332
+ async function hub(positional, flags) {
333
+ const read = await hubOrFail(positional[1], false);
334
+ if (flags.json) {
335
+ console.log(JSON.stringify({ hubs: read.hubs, summary: (0, summary_1.summarize)(read.merged) }, null, 2));
336
+ }
337
+ else {
338
+ for (const h of read.hubs) {
339
+ const errors = h.issues.filter((i) => i.severity === 'error').length;
340
+ console.log(`${h.hub.padEnd(40)} ${h.read ? `${h.repositories} repositories` : 'not read'} ${errors} errors, ${h.issues.length - errors} warnings ${h.source}`);
341
+ }
342
+ }
343
+ return read.hubs.some((h) => !h.read) ? 1 : 0;
344
+ }
345
+ async function serve(positional, flags) {
346
+ const dir = await mappingDir(positional[1]);
347
+ const port = Number(text(flags, 'port') ?? 3967);
348
+ const host = text(flags, 'host') ?? '127.0.0.1';
349
+ if (!Number.isInteger(port) || port < 0 || port > 65535)
350
+ throw new UsageError('--port needs a port number');
351
+ // Bound to loopback, answer only loopback names: a page that rebinds its own
352
+ // name to 127.0.0.1 is refused. Bound wider, the person chose who reaches it.
353
+ const loopback = server_1.LOOPBACK_HOSTS.includes(host.toLowerCase()) || host === '::1';
354
+ const server = (0, server_1.createMappingServer)(() => (0, hub_1.readHub)(dir, { local: flags.local === true }), {
355
+ ...(loopback ? { allowedHosts: server_1.LOOPBACK_HOSTS } : {}),
356
+ });
357
+ await new Promise((resolve, reject) => {
358
+ server.once('error', reject);
359
+ server.listen(port, host, () => resolve());
360
+ });
361
+ const address = server.address();
362
+ const shown = typeof address === 'object' && address ? `http://${host}:${address.port}/` : `http://${host}:${port}/`;
363
+ console.error(`mw-repomap: serving ${dir} at ${shown}`);
364
+ return new Promise(() => { });
365
+ }
366
+ async function wheel(positional, flags) {
367
+ const { merged } = await hubOrFail(positional[1], flags.local === true);
368
+ console.log(JSON.stringify({ nodes: (0, wheel_1.toWheelNodes)(merged), edges: (0, wheel_1.toWheelEdges)(merged) }, null, 2));
369
+ return 0;
370
+ }
371
+ const COMMANDS = {
372
+ init,
373
+ validate,
374
+ list,
375
+ show,
376
+ observe,
377
+ move,
378
+ hub,
379
+ serve,
380
+ wheel,
381
+ };
382
+ async function main() {
383
+ const { positional, flags } = parse(process.argv.slice(2));
384
+ const command = positional[0];
385
+ if (command === 'help' || (flags.help && !command)) {
386
+ console.log(USAGE);
387
+ return 0;
388
+ }
389
+ if (!command)
390
+ throw new UsageError('a command is needed');
391
+ const run = COMMANDS[command];
392
+ if (!run)
393
+ throw new UsageError(`unknown command: ${command}`);
394
+ return run(positional, flags);
395
+ }
396
+ main().then((code) => {
397
+ process.exitCode = code;
398
+ }, (error) => {
399
+ if (error instanceof UsageError) {
400
+ console.error(`mw-repomap: ${error.message}`);
401
+ console.error(USAGE);
402
+ process.exitCode = 2;
403
+ return;
404
+ }
405
+ console.error(`mw-repomap: ${error.message}`);
406
+ process.exitCode = 1;
407
+ });
408
+ //# sourceMappingURL=cli.js.map