@meltemi/diagrams-cli 0.0.0-stage → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -2
- package/dist/assets-command.d.ts +16 -0
- package/dist/assets-command.d.ts.map +1 -0
- package/dist/assets-command.js +145 -0
- package/dist/assets-command.js.map +1 -0
- package/dist/assist-command.d.ts +19 -0
- package/dist/assist-command.d.ts.map +1 -0
- package/dist/assist-command.js +268 -0
- package/dist/assist-command.js.map +1 -0
- package/dist/auth-command.d.ts +14 -0
- package/dist/auth-command.d.ts.map +1 -0
- package/dist/auth-command.js +233 -0
- package/dist/auth-command.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +60 -0
- package/dist/bin.js.map +1 -0
- package/dist/build-command.d.ts +23 -0
- package/dist/build-command.d.ts.map +1 -0
- package/dist/build-command.js +123 -0
- package/dist/build-command.js.map +1 -0
- package/dist/cli.d.ts +46 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +823 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud-client.d.ts +246 -0
- package/dist/cloud-client.d.ts.map +1 -0
- package/dist/cloud-client.js +591 -0
- package/dist/cloud-client.js.map +1 -0
- package/dist/cloud-command.d.ts +56 -0
- package/dist/cloud-command.d.ts.map +1 -0
- package/dist/cloud-command.js +81 -0
- package/dist/cloud-command.js.map +1 -0
- package/dist/cloud-context.d.ts +106 -0
- package/dist/cloud-context.d.ts.map +1 -0
- package/dist/cloud-context.js +185 -0
- package/dist/cloud-context.js.map +1 -0
- package/dist/cloud-errors.d.ts +37 -0
- package/dist/cloud-errors.d.ts.map +1 -0
- package/dist/cloud-errors.js +46 -0
- package/dist/cloud-errors.js.map +1 -0
- package/dist/cloud-host.d.ts +72 -0
- package/dist/cloud-host.d.ts.map +1 -0
- package/dist/cloud-host.js +129 -0
- package/dist/cloud-host.js.map +1 -0
- package/dist/cloud-manifest.d.ts +12 -0
- package/dist/cloud-manifest.d.ts.map +1 -0
- package/dist/cloud-manifest.js +21 -0
- package/dist/cloud-manifest.js.map +1 -0
- package/dist/cloud-paths.d.ts +48 -0
- package/dist/cloud-paths.d.ts.map +1 -0
- package/dist/cloud-paths.js +104 -0
- package/dist/cloud-paths.js.map +1 -0
- package/dist/credentials.d.ts +107 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +243 -0
- package/dist/credentials.js.map +1 -0
- package/dist/export-command.d.ts +35 -0
- package/dist/export-command.d.ts.map +1 -0
- package/dist/export-command.js +524 -0
- package/dist/export-command.js.map +1 -0
- package/dist/import-command.d.ts +20 -0
- package/dist/import-command.d.ts.map +1 -0
- package/dist/import-command.js +254 -0
- package/dist/import-command.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/init-command.d.ts +28 -0
- package/dist/init-command.d.ts.map +1 -0
- package/dist/init-command.js +199 -0
- package/dist/init-command.js.map +1 -0
- package/dist/login-listener.d.ts +58 -0
- package/dist/login-listener.d.ts.map +1 -0
- package/dist/login-listener.js +173 -0
- package/dist/login-listener.js.map +1 -0
- package/dist/messages/en.d.ts +533 -0
- package/dist/messages/en.d.ts.map +1 -0
- package/dist/messages/en.js +803 -0
- package/dist/messages/en.js.map +1 -0
- package/dist/messages/es.d.ts +3 -0
- package/dist/messages/es.d.ts.map +1 -0
- package/dist/messages/es.js +764 -0
- package/dist/messages/es.js.map +1 -0
- package/dist/messages/index.d.ts +442 -0
- package/dist/messages/index.d.ts.map +1 -0
- package/dist/messages/index.js +42 -0
- package/dist/messages/index.js.map +1 -0
- package/dist/messages/push-pull-en.d.ts +84 -0
- package/dist/messages/push-pull-en.d.ts.map +1 -0
- package/dist/messages/push-pull-en.js +146 -0
- package/dist/messages/push-pull-en.js.map +1 -0
- package/dist/messages/push-pull-es.d.ts +7 -0
- package/dist/messages/push-pull-es.d.ts.map +1 -0
- package/dist/messages/push-pull-es.js +144 -0
- package/dist/messages/push-pull-es.js.map +1 -0
- package/dist/open-url.d.ts +11 -0
- package/dist/open-url.d.ts.map +1 -0
- package/dist/open-url.js +36 -0
- package/dist/open-url.js.map +1 -0
- package/dist/plugins-command.d.ts +15 -0
- package/dist/plugins-command.d.ts.map +1 -0
- package/dist/plugins-command.js +106 -0
- package/dist/plugins-command.js.map +1 -0
- package/dist/preview.d.ts +54 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +249 -0
- package/dist/preview.js.map +1 -0
- package/dist/project-errors.d.ts +17 -0
- package/dist/project-errors.d.ts.map +1 -0
- package/dist/project-errors.js +85 -0
- package/dist/project-errors.js.map +1 -0
- package/dist/projects-command.d.ts +13 -0
- package/dist/projects-command.d.ts.map +1 -0
- package/dist/projects-command.js +85 -0
- package/dist/projects-command.js.map +1 -0
- package/dist/publish-command.d.ts +41 -0
- package/dist/publish-command.d.ts.map +1 -0
- package/dist/publish-command.js +311 -0
- package/dist/publish-command.js.map +1 -0
- package/dist/pull-command.d.ts +21 -0
- package/dist/pull-command.d.ts.map +1 -0
- package/dist/pull-command.js +300 -0
- package/dist/pull-command.js.map +1 -0
- package/dist/push-command.d.ts +31 -0
- package/dist/push-command.d.ts.map +1 -0
- package/dist/push-command.js +391 -0
- package/dist/push-command.js.map +1 -0
- package/dist/push-review.d.ts +16 -0
- package/dist/push-review.d.ts.map +1 -0
- package/dist/push-review.js +72 -0
- package/dist/push-review.js.map +1 -0
- package/dist/push-set.d.ts +126 -0
- package/dist/push-set.d.ts.map +1 -0
- package/dist/push-set.js +289 -0
- package/dist/push-set.js.map +1 -0
- package/dist/push-validation.d.ts +20 -0
- package/dist/push-validation.d.ts.map +1 -0
- package/dist/push-validation.js +70 -0
- package/dist/push-validation.js.map +1 -0
- package/dist/redact.d.ts +9 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/redact.js +22 -0
- package/dist/redact.js.map +1 -0
- package/dist/review-command.d.ts +44 -0
- package/dist/review-command.d.ts.map +1 -0
- package/dist/review-command.js +348 -0
- package/dist/review-command.js.map +1 -0
- package/package.json +56 -3
- package/templates/c4/diagrams/views.meltemi +16 -0
- package/templates/c4/models/system.meltemi +23 -0
- package/templates/ci/github.yml +96 -0
- package/templates/ci/gitlab.yml +53 -0
- package/templates/flow/diagrams/overview.meltemi +15 -0
- package/templates/free/diagrams/slide.meltemi +39 -0
- package/templates/purchase/diagrams/architecture.meltemi +36 -0
- package/templates/purchase/diagrams/executive.meltemi +52 -0
- package/templates/purchase/diagrams/payment.meltemi +42 -0
- package/templates/purchase/models/commerce.meltemi +30 -0
- package/templates/purchase/stories/purchase.meltemi +38 -0
- package/templates/sequence/diagrams/sign-in.meltemi +36 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cloud project as a `ProjectHost` (`cli-cloud`, "Projects and review against the cloud"): what
|
|
3
|
+
* `review --base cloud[:<n>]` compares the folder with, as `gitRevisionHost` is what `review
|
|
4
|
+
* --base <revision>` compares it with.
|
|
5
|
+
*
|
|
6
|
+
* A `ProjectHost` is synchronous, so the cloud is read once, up front: the project's files (or a
|
|
7
|
+
* version's) and the images and fonts they reference, each blob once. Every path the service
|
|
8
|
+
* answers then goes through the path rule of the cloud (`isCleanPath`) and must resolve inside the
|
|
9
|
+
* project's root; one that does not is `pull.unsafe-path` and nothing is compared. The only paths
|
|
10
|
+
* left out without a word are the losers of a name collision (`<path>~<id>`, listed in the
|
|
11
|
+
* answer's `collisions`): the service keeps them out of copies and restores for the same reason.
|
|
12
|
+
*
|
|
13
|
+
* The host is rooted at the local project root, like the git host is rooted at the repository:
|
|
14
|
+
* `<root>/<path>` answers the fetched text or bytes and `readDirectory` is made from the path set.
|
|
15
|
+
*/
|
|
16
|
+
import { createMemoryHost, joinPath } from "@meltemi/diagrams-project";
|
|
17
|
+
import { collisionLosers, insideFolder, pathProblem, printablePath, setProblems, } from "./cloud-paths.js";
|
|
18
|
+
import { CloudError, isCloudError } from "./cloud-errors.js";
|
|
19
|
+
/** The most blobs fetched at once. */
|
|
20
|
+
const BLOB_CONCURRENCY = 4;
|
|
21
|
+
/**
|
|
22
|
+
* Whether a path the cloud answered is NOT safe to put under `root` by itself, with the checks
|
|
23
|
+
* `pull` makes (`cloud-paths.ts`): it breaks the cloud's path rule, a name of it could not be kept
|
|
24
|
+
* on Windows (a device name such as `con` or `com1.txt`, a name that ends with a dot or a space), or
|
|
25
|
+
* joined to the root it leaves it (or is the root itself). What only a set of paths can show
|
|
26
|
+
* (`setProblems`) is checked by `checkCloudPaths`.
|
|
27
|
+
*/
|
|
28
|
+
export function unsafeCloudPath(root, path) {
|
|
29
|
+
return pathProblem(path) !== undefined || insideFolder(root, path) === undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fails with `pull.unsafe-path` naming the first of `paths` that is not safe under `root`: one that
|
|
33
|
+
* `unsafeCloudPath` refuses, or that cannot be written beside the others, because it differs from
|
|
34
|
+
* another only in letter case (one overwrites the other on Windows and macOS) or is a file where
|
|
35
|
+
* another path needs a folder. The path is named as `pull` names it, escaped (`printablePath`), as it
|
|
36
|
+
* is the cloud's to choose and the terminal is the person's.
|
|
37
|
+
*/
|
|
38
|
+
export function checkCloudPaths(root, paths) {
|
|
39
|
+
const all = [...paths];
|
|
40
|
+
const together = setProblems(all.filter((path) => pathProblem(path) === undefined));
|
|
41
|
+
for (const path of all) {
|
|
42
|
+
if (!unsafeCloudPath(root, path) && !together.has(path))
|
|
43
|
+
continue;
|
|
44
|
+
const shown = printablePath(path);
|
|
45
|
+
throw new CloudError("pull.unsafe-path", `The cloud project holds ${JSON.stringify(shown)}, which is not a safe path inside the folder.`, { details: { path: shown } });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Runs `task` over `items`, at most `limit` at a time; the first failure is the one thrown. */
|
|
49
|
+
async function pooled(items, limit, task) {
|
|
50
|
+
const results = new Array(items.length);
|
|
51
|
+
let next = 0;
|
|
52
|
+
let failure;
|
|
53
|
+
let failed = false;
|
|
54
|
+
const worker = async () => {
|
|
55
|
+
while (!failed && next < items.length) {
|
|
56
|
+
const index = next++;
|
|
57
|
+
try {
|
|
58
|
+
results[index] = await task(items[index]);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (!failed) {
|
|
62
|
+
failed = true;
|
|
63
|
+
failure = error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
69
|
+
if (failed)
|
|
70
|
+
throw failure;
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Reads a cloud project once: its current files, or version `version`'s, and the blobs they
|
|
75
|
+
* reference. `root` is the folder the paths are checked against (`pull.unsafe-path`, before any
|
|
76
|
+
* blob is fetched). An unknown version is `version.not-found` naming it; an unknown project,
|
|
77
|
+
* `project.not-found`.
|
|
78
|
+
*/
|
|
79
|
+
export async function fetchCloudSnapshot(client, options) {
|
|
80
|
+
const { project, root, version } = options;
|
|
81
|
+
let answer;
|
|
82
|
+
try {
|
|
83
|
+
answer =
|
|
84
|
+
version === undefined
|
|
85
|
+
? await client.files(project)
|
|
86
|
+
: await client.readVersion(project, version);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (isCloudError(error, "version.not-found")) {
|
|
90
|
+
throw new CloudError(error.code, error.message, {
|
|
91
|
+
status: error.status,
|
|
92
|
+
details: { ...error.details, project, version },
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
// The losers of a name collision (`<path>~<id>`) are display names: left out, as pull and push do.
|
|
98
|
+
const losers = collisionLosers([...Object.keys(answer.files), ...Object.keys(answer.binaries)], "collisions" in answer ? answer.collisions : []);
|
|
99
|
+
const files = new Map(Object.entries(answer.files).filter(([path]) => !losers.has(path)));
|
|
100
|
+
const hashes = new Map(Object.entries(answer.binaries).filter(([path]) => !losers.has(path)));
|
|
101
|
+
checkCloudPaths(root, [...files.keys(), ...hashes.keys()]);
|
|
102
|
+
const wanted = [...new Set(hashes.values())];
|
|
103
|
+
const fetched = await pooled(wanted, BLOB_CONCURRENCY, (hash) => client.blob(project, hash));
|
|
104
|
+
const bytesOf = new Map(wanted.map((hash, index) => [hash, fetched[index]]));
|
|
105
|
+
return {
|
|
106
|
+
revision: "revision" in answer ? answer.revision : undefined,
|
|
107
|
+
version,
|
|
108
|
+
files,
|
|
109
|
+
binaries: new Map([...hashes].map(([path, hash]) => [path, { hash, bytes: bytesOf.get(hash) }])),
|
|
110
|
+
skipped: [...new Set([...Object.keys(answer.files), ...Object.keys(answer.binaries)])].filter((path) => losers.has(path)),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** The host of a snapshot, rooted at `root`: `<root>/<path>` is the file the cloud holds there. */
|
|
114
|
+
export function cloudSnapshotHost(root, snapshot) {
|
|
115
|
+
const contents = {};
|
|
116
|
+
for (const [path, text] of snapshot.files)
|
|
117
|
+
contents[joinPath(root, path)] = text;
|
|
118
|
+
for (const [path, binary] of snapshot.binaries)
|
|
119
|
+
contents[joinPath(root, path)] = binary.bytes;
|
|
120
|
+
return createMemoryHost(contents);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The host of a cloud project (`project`, its current files or those of `version`) rooted at the
|
|
124
|
+
* local project root `root`, filled by one prefetch (`fetchCloudSnapshot`).
|
|
125
|
+
*/
|
|
126
|
+
export async function cloudHost(client, options) {
|
|
127
|
+
return cloudSnapshotHost(options.root, await fetchCloudSnapshot(client, options));
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=cloud-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-host.js","sourceRoot":"","sources":["../src/cloud-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `cloud` field of `project.json` (`cli-cloud`, "Push" and "Pull"): `{ "host": …, "project": … }`,
|
|
3
|
+
* written by `push --create` and `pull` through `setJsonObjectField`, which keeps the file's
|
|
4
|
+
* indentation, line endings and everything else byte for byte.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The text of a manifest with its `cloud` field set to the service and the cloud project: replaced
|
|
8
|
+
* when it is there (and unchanged when it is already these values), else added after the last field.
|
|
9
|
+
* A byte order mark stays. Throws when the text is not a JSON object.
|
|
10
|
+
*/
|
|
11
|
+
export declare function withCloudField(text: string, host: string, project: string): string;
|
|
12
|
+
//# sourceMappingURL=cloud-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-manifest.d.ts","sourceRoot":"","sources":["../src/cloud-manifest.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKlF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `cloud` field of `project.json` (`cli-cloud`, "Push" and "Pull"): `{ "host": …, "project": … }`,
|
|
3
|
+
* written by `push --create` and `pull` through `setJsonObjectField`, which keeps the file's
|
|
4
|
+
* indentation, line endings and everything else byte for byte.
|
|
5
|
+
*/
|
|
6
|
+
import { setJsonObjectField } from "@meltemi/diagrams-editor";
|
|
7
|
+
import { applyTextEdits } from "@meltemi/diagrams-language";
|
|
8
|
+
/** The byte order mark, which a Windows editor may put in front of a JSON file. */
|
|
9
|
+
const BYTE_ORDER_MARK = 0xfeff;
|
|
10
|
+
/**
|
|
11
|
+
* The text of a manifest with its `cloud` field set to the service and the cloud project: replaced
|
|
12
|
+
* when it is there (and unchanged when it is already these values), else added after the last field.
|
|
13
|
+
* A byte order mark stays. Throws when the text is not a JSON object.
|
|
14
|
+
*/
|
|
15
|
+
export function withCloudField(text, host, project) {
|
|
16
|
+
const bom = text.charCodeAt(0) === BYTE_ORDER_MARK;
|
|
17
|
+
const body = bom ? text.slice(1) : text;
|
|
18
|
+
const edited = applyTextEdits(body, setJsonObjectField(body, "cloud", { host, project }));
|
|
19
|
+
return bom ? String.fromCharCode(BYTE_ORDER_MARK) + edited : edited;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=cloud-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-manifest.js","sourceRoot":"","sources":["../src/cloud-manifest.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,mFAAmF;AACnF,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAY,EAAE,OAAe;IACxE,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,eAAe,CAAC;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAC1F,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a path of a cloud project must be before it may be written under a folder (`cli-cloud`,
|
|
3
|
+
* "Pull"): one rule for `pull` and for every command that reads a cloud project into a folder's
|
|
4
|
+
* place (`review --base cloud`).
|
|
5
|
+
*
|
|
6
|
+
* The cloud already refuses every path that breaks its path rule, so what arrives is what the
|
|
7
|
+
* service kept; the command line does not take that on trust, because the folder is the person's
|
|
8
|
+
* and a project can be written by anybody who edits it. A path is refused when
|
|
9
|
+
*
|
|
10
|
+
* - it is not already in canonical form (`isCleanPath`: relative, `/`-separated, no `..`, nothing
|
|
11
|
+
* hidden, no `:`, at most 12 names of at most 100 plain ASCII characters);
|
|
12
|
+
* - a name could not be written safely on Windows: it ends with a dot or a space (Windows drops
|
|
13
|
+
* both, so `a.` would be `a`) or is a device name (`con`, `nul`, `com1`, `lpt1`, ... with or
|
|
14
|
+
* without an extension);
|
|
15
|
+
* - it resolves outside the folder (`insideFolder`), which the rule above already rules out and
|
|
16
|
+
* which is checked again on the real, resolved path anyway;
|
|
17
|
+
* - in a set of paths, it differs from another only in letter case (one overwrites the other on
|
|
18
|
+
* Windows and macOS), or is a file where another path needs a folder.
|
|
19
|
+
*/
|
|
20
|
+
/** Why a path is not safe: the cloud's rule, or a name Windows cannot keep. */
|
|
21
|
+
export type PathProblem = "rule" | "windows-name" | "windows-dot";
|
|
22
|
+
/** Why a path is not safe in a set of paths. */
|
|
23
|
+
export type SetProblem = "letter-case" | "file-and-folder";
|
|
24
|
+
/** Why one path cannot be written safely anywhere, or undefined when it can. */
|
|
25
|
+
export declare function pathProblem(path: string): PathProblem | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* The paths of a set that cannot be written beside the others, each with why: the later of two
|
|
28
|
+
* paths that differ only in letter case, and a path that is also the folder of another one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function setProblems(paths: Iterable<string>): Map<string, SetProblem>;
|
|
31
|
+
/**
|
|
32
|
+
* A path the cloud answered, safe to print: control characters and line separators (an escape
|
|
33
|
+
* sequence in a name would reach the person's terminal) are written as `\uXXXX`. A path that
|
|
34
|
+
* passes the path rule has none.
|
|
35
|
+
*/
|
|
36
|
+
export declare function printablePath(path: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* The paths among `paths` that are the losers of a name collision: those the service lists as
|
|
39
|
+
* `shown` in `collisions`, and any `<path>~<id>` whose `<path>` is a valid path. The service shows
|
|
40
|
+
* such a path in its answers but never keeps one; it is never written and never sent.
|
|
41
|
+
*/
|
|
42
|
+
export declare function collisionLosers(paths: Iterable<string>, collisions: readonly unknown[]): Set<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Where a project path lands under a folder, as a native path, or undefined when the resolved
|
|
45
|
+
* result is not inside the folder (or is the folder itself). `folder` is absolute.
|
|
46
|
+
*/
|
|
47
|
+
export declare function insideFolder(folder: string, path: string): string | undefined;
|
|
48
|
+
//# sourceMappingURL=cloud-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-paths.d.ts","sourceRoot":"","sources":["../src/cloud-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,+EAA+E;AAC/E,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,cAAc,GAAG,aAAa,CAAC;AAClE,gDAAgD;AAChD,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,iBAAiB,CAAC;AAK3D,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAOjE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAkB5E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKlD;AAKD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EACvB,UAAU,EAAE,SAAS,OAAO,EAAE,GAC7B,GAAG,CAAC,MAAM,CAAC,CAcb;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO7E"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a path of a cloud project must be before it may be written under a folder (`cli-cloud`,
|
|
3
|
+
* "Pull"): one rule for `pull` and for every command that reads a cloud project into a folder's
|
|
4
|
+
* place (`review --base cloud`).
|
|
5
|
+
*
|
|
6
|
+
* The cloud already refuses every path that breaks its path rule, so what arrives is what the
|
|
7
|
+
* service kept; the command line does not take that on trust, because the folder is the person's
|
|
8
|
+
* and a project can be written by anybody who edits it. A path is refused when
|
|
9
|
+
*
|
|
10
|
+
* - it is not already in canonical form (`isCleanPath`: relative, `/`-separated, no `..`, nothing
|
|
11
|
+
* hidden, no `:`, at most 12 names of at most 100 plain ASCII characters);
|
|
12
|
+
* - a name could not be written safely on Windows: it ends with a dot or a space (Windows drops
|
|
13
|
+
* both, so `a.` would be `a`) or is a device name (`con`, `nul`, `com1`, `lpt1`, ... with or
|
|
14
|
+
* without an extension);
|
|
15
|
+
* - it resolves outside the folder (`insideFolder`), which the rule above already rules out and
|
|
16
|
+
* which is checked again on the real, resolved path anyway;
|
|
17
|
+
* - in a set of paths, it differs from another only in letter case (one overwrites the other on
|
|
18
|
+
* Windows and macOS), or is a file where another path needs a folder.
|
|
19
|
+
*/
|
|
20
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
21
|
+
import { isCleanPath } from "@meltemi/diagrams-project";
|
|
22
|
+
/** The device names Windows reserves, with or without an extension. */
|
|
23
|
+
const DEVICE_NAME = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
|
|
24
|
+
/** Why one path cannot be written safely anywhere, or undefined when it can. */
|
|
25
|
+
export function pathProblem(path) {
|
|
26
|
+
if (!isCleanPath(path))
|
|
27
|
+
return "rule";
|
|
28
|
+
for (const name of path.split("/")) {
|
|
29
|
+
if (/[. ]$/.test(name))
|
|
30
|
+
return "windows-dot";
|
|
31
|
+
if (DEVICE_NAME.test(name))
|
|
32
|
+
return "windows-name";
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The paths of a set that cannot be written beside the others, each with why: the later of two
|
|
38
|
+
* paths that differ only in letter case, and a path that is also the folder of another one.
|
|
39
|
+
*/
|
|
40
|
+
export function setProblems(paths) {
|
|
41
|
+
const sorted = [...new Set(paths)].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
42
|
+
const problems = new Map();
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
const folders = new Set();
|
|
45
|
+
for (const path of sorted) {
|
|
46
|
+
const lower = path.toLowerCase();
|
|
47
|
+
if (seen.has(lower))
|
|
48
|
+
problems.set(path, "letter-case");
|
|
49
|
+
seen.add(lower);
|
|
50
|
+
const names = lower.split("/");
|
|
51
|
+
for (let end = 1; end < names.length; end++)
|
|
52
|
+
folders.add(names.slice(0, end).join("/"));
|
|
53
|
+
}
|
|
54
|
+
for (const path of sorted) {
|
|
55
|
+
if (!problems.has(path) && folders.has(path.toLowerCase())) {
|
|
56
|
+
problems.set(path, "file-and-folder");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return problems;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A path the cloud answered, safe to print: control characters and line separators (an escape
|
|
63
|
+
* sequence in a name would reach the person's terminal) are written as `\uXXXX`. A path that
|
|
64
|
+
* passes the path rule has none.
|
|
65
|
+
*/
|
|
66
|
+
export function printablePath(path) {
|
|
67
|
+
return path.replace(/[\p{Cc}\p{Zl}\p{Zp}]/gu, (char) => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
68
|
+
}
|
|
69
|
+
/** A path the service made for the loser of a name collision: `<path>~<id>`, 16 base32 characters. */
|
|
70
|
+
const LOSER = /^(.+)~[a-z2-7]{16}$/;
|
|
71
|
+
/**
|
|
72
|
+
* The paths among `paths` that are the losers of a name collision: those the service lists as
|
|
73
|
+
* `shown` in `collisions`, and any `<path>~<id>` whose `<path>` is a valid path. The service shows
|
|
74
|
+
* such a path in its answers but never keeps one; it is never written and never sent.
|
|
75
|
+
*/
|
|
76
|
+
export function collisionLosers(paths, collisions) {
|
|
77
|
+
const losers = new Set();
|
|
78
|
+
for (const collision of collisions) {
|
|
79
|
+
const shown = typeof collision === "object" && collision !== null
|
|
80
|
+
? collision.shown
|
|
81
|
+
: undefined;
|
|
82
|
+
if (typeof shown === "string")
|
|
83
|
+
losers.add(shown);
|
|
84
|
+
}
|
|
85
|
+
for (const path of paths) {
|
|
86
|
+
const stem = LOSER.exec(path)?.[1];
|
|
87
|
+
if (stem !== undefined && isCleanPath(stem))
|
|
88
|
+
losers.add(path);
|
|
89
|
+
}
|
|
90
|
+
return losers;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Where a project path lands under a folder, as a native path, or undefined when the resolved
|
|
94
|
+
* result is not inside the folder (or is the folder itself). `folder` is absolute.
|
|
95
|
+
*/
|
|
96
|
+
export function insideFolder(folder, path) {
|
|
97
|
+
const target = resolve(folder, ...path.split("/"));
|
|
98
|
+
const from = relative(folder, target);
|
|
99
|
+
if (from === "" || from === ".." || from.startsWith(`..${sep}`) || isAbsolute(from)) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
return target;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=cloud-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-paths.js","sourceRoot":"","sources":["../src/cloud-paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the command line keeps its sign-in (`cli-cloud`, "Signing in from the command line"):
|
|
3
|
+
* `credentials.json` in the user's configuration folder, one entry `{ token, id, user }` per host,
|
|
4
|
+
* and how a command finds the host and the token it uses.
|
|
5
|
+
*
|
|
6
|
+
* - The host is `--host`, else `MELTEMI_HOST`, else `https://diagrams.meltemi.dev`: never a
|
|
7
|
+
* manifest's (`checkManifestHost` fails with `cloud.host-mismatch` when `project.json` names
|
|
8
|
+
* another one).
|
|
9
|
+
* - The token is `MELTEMI_TOKEN` in the environment, else the file's entry for the host.
|
|
10
|
+
* - The file is written owner-only (`0o600`, in a `0o700` folder) where the system has such modes,
|
|
11
|
+
* through a temporary file and a rename, so a failure never leaves half a file. Windows keeps the
|
|
12
|
+
* ACLs the user's profile folder has.
|
|
13
|
+
* - A file that cannot be read as credentials is reported (`credentials.corrupt`) and never
|
|
14
|
+
* overwritten.
|
|
15
|
+
*/
|
|
16
|
+
/** The service every cloud command talks to unless `--host` or `MELTEMI_HOST` says otherwise. */
|
|
17
|
+
export declare const DEFAULT_HOST = "https://diagrams.meltemi.dev";
|
|
18
|
+
/** The folder of the configuration, and the file of the credentials in it. */
|
|
19
|
+
export declare const CONFIGURATION_FOLDER = "meltemi-diagrams";
|
|
20
|
+
export declare const CREDENTIALS_FILE = "credentials.json";
|
|
21
|
+
type Env = Readonly<Record<string, string | undefined>>;
|
|
22
|
+
export interface ConfigurationOptions {
|
|
23
|
+
/** The environment: `APPDATA`, `XDG_CONFIG_HOME`, `HOME`, `USERPROFILE`. */
|
|
24
|
+
readonly env: Env;
|
|
25
|
+
/** The platform, `process.platform` by default. */
|
|
26
|
+
readonly platform?: NodeJS.Platform;
|
|
27
|
+
/** The user's home folder, from the environment or the system by default. */
|
|
28
|
+
readonly home?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The folder of the configuration: `%APPDATA%\meltemi-diagrams` on Windows;
|
|
32
|
+
* `$XDG_CONFIG_HOME/meltemi-diagrams` elsewhere (an absolute value only, as the specification
|
|
33
|
+
* says), else `~/.config/meltemi-diagrams`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function configurationFolder(options: ConfigurationOptions): string;
|
|
36
|
+
/** The credentials file of an environment. */
|
|
37
|
+
export declare function credentialsPath(options: ConfigurationOptions): string;
|
|
38
|
+
/**
|
|
39
|
+
* The host a cloud command talks to: `--host` (`flag`), else `MELTEMI_HOST`, else the default. An
|
|
40
|
+
* empty `MELTEMI_HOST` counts as not set, as CI expands an unset variable. The answer is the
|
|
41
|
+
* normalised origin. Throws `cloud.host-invalid` for a text that is not an origin and
|
|
42
|
+
* `cloud.insecure-host` for `http:` anywhere but the loopback (`127.0.0.1`, `localhost`, `[::1]`),
|
|
43
|
+
* before anything is sent.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveHost(options: {
|
|
46
|
+
readonly flag?: string | undefined;
|
|
47
|
+
readonly env: Env;
|
|
48
|
+
}): string;
|
|
49
|
+
/**
|
|
50
|
+
* Fails with `cloud.host-mismatch`, naming both hosts and `--host`, when a project's `cloud.host`
|
|
51
|
+
* is not the host the command resolved. A manifest never chooses where a token goes. Call it before
|
|
52
|
+
* any request; `manifestHost` is `project.json`'s `cloud.host` (absent when it names none).
|
|
53
|
+
*/
|
|
54
|
+
export declare function checkManifestHost(resolved: string, manifestHost: string | null | undefined): void;
|
|
55
|
+
export interface StoredUser {
|
|
56
|
+
readonly sub: string;
|
|
57
|
+
readonly name: string;
|
|
58
|
+
}
|
|
59
|
+
/** What is kept for one host: the token, its id at the service, and who it is for. */
|
|
60
|
+
export interface StoredEntry {
|
|
61
|
+
readonly token: string;
|
|
62
|
+
readonly id: string;
|
|
63
|
+
readonly user: StoredUser;
|
|
64
|
+
}
|
|
65
|
+
/** The token a command uses, and where it came from. */
|
|
66
|
+
export interface ResolvedToken {
|
|
67
|
+
readonly token: string;
|
|
68
|
+
/** `environment`: `MELTEMI_TOKEN`; `file`: the credentials file's entry for the host. */
|
|
69
|
+
readonly source: "environment" | "file";
|
|
70
|
+
/** The stored entry, when the token came from the file. */
|
|
71
|
+
readonly entry?: StoredEntry;
|
|
72
|
+
}
|
|
73
|
+
/** The token of `MELTEMI_TOKEN`, when it is set to something. */
|
|
74
|
+
export declare function environmentToken(env: Env): string | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* The credentials file. A missing file is an empty one. A file that is not JSON, not an object, or
|
|
77
|
+
* whose `hosts` is not an object, or whose entry for the host asked about is not an entry, is
|
|
78
|
+
* `credentials.corrupt`; nothing is written over it, so the person can read what is there.
|
|
79
|
+
* Entries of other hosts and fields it does not know are kept as they were found.
|
|
80
|
+
*/
|
|
81
|
+
export declare class CredentialsStore {
|
|
82
|
+
readonly path: string;
|
|
83
|
+
readonly folder: string;
|
|
84
|
+
constructor(options: ConfigurationOptions);
|
|
85
|
+
private corrupt;
|
|
86
|
+
private load;
|
|
87
|
+
/** Reads the file and makes its folder, so that a command finds out before it asks for a token. */
|
|
88
|
+
prepare(): void;
|
|
89
|
+
/** The hosts that have an entry. */
|
|
90
|
+
hosts(): string[];
|
|
91
|
+
/** The entry of a host, or undefined; `credentials.corrupt` when what is there is not an entry. */
|
|
92
|
+
entry(host: string): StoredEntry | undefined;
|
|
93
|
+
/** Keeps the entry of a host, replacing the one it had. */
|
|
94
|
+
save(host: string, entry: StoredEntry): void;
|
|
95
|
+
/** Forgets the entry of a host; whether there was one. The file goes when nothing is left in it. */
|
|
96
|
+
remove(host: string): boolean;
|
|
97
|
+
private unwritable;
|
|
98
|
+
/** Writes owner-only through a temporary file in the same folder, then renames it into place. */
|
|
99
|
+
private write;
|
|
100
|
+
/**
|
|
101
|
+
* The token a command uses for a host: `MELTEMI_TOKEN` when it is set (the file is not even
|
|
102
|
+
* read), else the host's entry. Undefined when there is none.
|
|
103
|
+
*/
|
|
104
|
+
resolveToken(host: string, env: Env): ResolvedToken | undefined;
|
|
105
|
+
}
|
|
106
|
+
export {};
|
|
107
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiBH,iGAAiG;AACjG,eAAO,MAAM,YAAY,iCAAiC,CAAC;AAE3D,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,qBAAqB,CAAC;AACvD,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAKnD,KAAK,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAExD,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAiBzE;AAED,8CAA8C;AAC9C,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAMrE;AAID;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACnC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;CACnB,GAAG,MAAM,CAiBT;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAQjG;AAID,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B;AAgBD,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC;IACxC,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;CAC9B;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAG7D;AAUD;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,OAAO,EAAE,oBAAoB;IAMzC,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,IAAI;IA8BZ,mGAAmG;IACnG,OAAO,IAAI,IAAI;IAaf,oCAAoC;IACpC,KAAK,IAAI,MAAM,EAAE;IAIjB,mGAAmG;IACnG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAY5C,2DAA2D;IAC3D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI;IAK5C,oGAAoG;IACpG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAgB7B,OAAO,CAAC,UAAU;IAQlB,iGAAiG;IACjG,OAAO,CAAC,KAAK;IAwBb;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,aAAa,GAAG,SAAS;CAMhE"}
|