proschi 0.13.0 → 0.14.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/README.md +33 -0
- package/dist/cli.cjs +1612 -674
- package/dist/render.cjs +219 -219
- package/dist/server.cjs +89 -21
- package/package.json +1 -1
package/dist/server.cjs
CHANGED
|
@@ -25478,6 +25478,19 @@ var BracketCounter = class {
|
|
|
25478
25478
|
return this.depth;
|
|
25479
25479
|
}
|
|
25480
25480
|
};
|
|
25481
|
+
function qualify(tokens) {
|
|
25482
|
+
let out;
|
|
25483
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
25484
|
+
const [t, next] = [tokens[i], tokens[i + 1]];
|
|
25485
|
+
const joined = t.kind === "ident" && next?.kind === "tag" && next.col === t.col + t.length;
|
|
25486
|
+
if (joined && (tokens[i + 2]?.kind === "arrow" || tokens[i - 1]?.kind === "arrow")) {
|
|
25487
|
+
out ??= tokens.slice(0, i);
|
|
25488
|
+
out.push({ ...t, value: `${t.value}.${next.value}`, length: t.length + next.length });
|
|
25489
|
+
i++;
|
|
25490
|
+
} else out?.push(t);
|
|
25491
|
+
}
|
|
25492
|
+
return out ?? tokens;
|
|
25493
|
+
}
|
|
25481
25494
|
|
|
25482
25495
|
// ../frontend/src/dsl/format.ts
|
|
25483
25496
|
function format(source2) {
|
|
@@ -25514,7 +25527,8 @@ function classify(lines) {
|
|
|
25514
25527
|
entries2.push({ kind: "blank", depth, text: "" });
|
|
25515
25528
|
continue;
|
|
25516
25529
|
}
|
|
25517
|
-
const { tokens, diagnostics } = tokenizeLine(raw, i + 1);
|
|
25530
|
+
const { tokens: lexed, diagnostics } = tokenizeLine(raw, i + 1);
|
|
25531
|
+
const tokens = qualify(lexed);
|
|
25518
25532
|
if (diagnostics.some((d) => d.severity === "error") || tokens.length === 0) {
|
|
25519
25533
|
entries2.push({ kind: "line", depth, text: original });
|
|
25520
25534
|
continue;
|
|
@@ -25669,7 +25683,8 @@ function join(raw, tokens) {
|
|
|
25669
25683
|
return out;
|
|
25670
25684
|
}
|
|
25671
25685
|
function sameTokens(text, tokens) {
|
|
25672
|
-
const { tokens:
|
|
25686
|
+
const { tokens: lexed, diagnostics } = tokenizeLine(text, 1);
|
|
25687
|
+
const again = qualify(lexed);
|
|
25673
25688
|
return diagnostics.length === 0 && again.length === tokens.length && again.every((t, i) => t.kind === tokens[i].kind && t.value === tokens[i].value);
|
|
25674
25689
|
}
|
|
25675
25690
|
function reindent(line, delta) {
|
|
@@ -28013,19 +28028,6 @@ function expand(items) {
|
|
|
28013
28028
|
}
|
|
28014
28029
|
return paths;
|
|
28015
28030
|
}
|
|
28016
|
-
function qualify(tokens) {
|
|
28017
|
-
let out;
|
|
28018
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
28019
|
-
const [t, next] = [tokens[i], tokens[i + 1]];
|
|
28020
|
-
const joined = t.kind === "ident" && next?.kind === "tag" && next.col === t.col + t.length;
|
|
28021
|
-
if (joined && (tokens[i + 2]?.kind === "arrow" || tokens[i - 1]?.kind === "arrow")) {
|
|
28022
|
-
out ??= tokens.slice(0, i);
|
|
28023
|
-
out.push({ ...t, value: `${t.value}.${next.value}`, length: t.length + next.length });
|
|
28024
|
-
i++;
|
|
28025
|
-
} else out?.push(t);
|
|
28026
|
-
}
|
|
28027
|
-
return out ?? tokens;
|
|
28028
|
-
}
|
|
28029
28031
|
function findUseCaseIn(diagram, name) {
|
|
28030
28032
|
const lower = name.toLowerCase();
|
|
28031
28033
|
return diagram.useCases.find((u) => u.name === name) ?? diagram.useCases.find((u) => u.name.toLowerCase() === lower) ?? diagram.useCases.find((u) => u.id === name);
|
|
@@ -30155,6 +30157,10 @@ var import_node_path5 = require("node:path");
|
|
|
30155
30157
|
var ImportError = class extends Error {
|
|
30156
30158
|
name = "ImportError";
|
|
30157
30159
|
};
|
|
30160
|
+
var LATEST_TAG = "latest";
|
|
30161
|
+
var VERSION_TAG = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
30162
|
+
var CLOUD_NAME_SEGMENT = /^[A-Za-z0-9_][A-Za-z0-9._+-]*$/;
|
|
30163
|
+
var MAX_CLOUD_NAME = 200;
|
|
30158
30164
|
var MAX_SPEC_LENGTH = 1024;
|
|
30159
30165
|
var SEGMENT = /^[A-Za-z0-9_][A-Za-z0-9._ +-]*$/;
|
|
30160
30166
|
var HOST = /^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)*(?::\d{1,5})?$/;
|
|
@@ -30202,8 +30208,9 @@ function parseRemoteSpec(spec) {
|
|
|
30202
30208
|
checkRef(ref, spec);
|
|
30203
30209
|
return { kind: "git", repo, path, ref };
|
|
30204
30210
|
}
|
|
30211
|
+
if (scheme === "cloud") return parseCloudSpec(spec);
|
|
30205
30212
|
if (scheme === "http") throw new ImportError(`'${spec}' uses http://; only https:// imports are allowed`);
|
|
30206
|
-
if (scheme !== "https") throw new ImportError(`'${spec}' is not a supported import source; use a relative path, git
|
|
30213
|
+
if (scheme !== "https") throw new ImportError(`'${spec}' is not a supported import source; use a relative path, git:, https:// or cloud:`);
|
|
30207
30214
|
let url;
|
|
30208
30215
|
try {
|
|
30209
30216
|
url = new URL(spec);
|
|
@@ -30215,15 +30222,48 @@ function parseRemoteSpec(spec) {
|
|
|
30215
30222
|
if (!HOST.test(url.host)) throw new ImportError(`'${spec}' has an invalid host`);
|
|
30216
30223
|
return { kind: "https", url: url.href };
|
|
30217
30224
|
}
|
|
30225
|
+
function parseCloudSpec(spec) {
|
|
30226
|
+
const body = spec.slice("cloud:".length);
|
|
30227
|
+
const split2 = body.indexOf("//");
|
|
30228
|
+
const head = split2 === -1 ? body : body.slice(0, split2);
|
|
30229
|
+
const parts = head.split(":");
|
|
30230
|
+
if (parts.length > 2) throw new ImportError(`'${spec}' is not a cloud source; write cloud:<name> or cloud:<name>:<tag>`);
|
|
30231
|
+
const name = parts[0].endsWith(".proschi") ? parts[0].slice(0, -".proschi".length) : parts[0];
|
|
30232
|
+
if (!name || name.length > MAX_CLOUD_NAME || name.split("/").some((p) => !CLOUD_NAME_SEGMENT.test(p) || p === "." || p === "..")) {
|
|
30233
|
+
throw new ImportError(
|
|
30234
|
+
`'${spec}' names an invalid diagram '${parts[0]}'; use the diagram's file name (letters, digits, '.', '_', '+', '-' and '/', no spaces), with or without .proschi`
|
|
30235
|
+
);
|
|
30236
|
+
}
|
|
30237
|
+
const tag = parts.length === 2 ? parts[1] : LATEST_TAG;
|
|
30238
|
+
if (!VERSION_TAG.test(tag)) {
|
|
30239
|
+
throw new ImportError(`'${spec}' has an invalid version tag '${tag}'; use up to 64 letters, digits, '.', '_' and '-', starting with a letter or digit, e.g. 2026-11-01`);
|
|
30240
|
+
}
|
|
30241
|
+
if (split2 === -1) return { kind: "cloud", name, tag };
|
|
30242
|
+
return { kind: "cloud", name, tag, path: repoPathSegments(body.slice(split2 + 2), spec).join("/") };
|
|
30243
|
+
}
|
|
30218
30244
|
function formatRemoteSpec(spec) {
|
|
30245
|
+
if (spec.kind === "cloud") return `cloud:${spec.name}:${spec.tag}${spec.path ? `//${spec.path}` : ""}`;
|
|
30219
30246
|
return spec.kind === "git" ? `git:${spec.repo}//${spec.path}@${spec.ref}` : spec.url;
|
|
30220
30247
|
}
|
|
30221
30248
|
function resolveImportPath(fromPath, importPath) {
|
|
30222
30249
|
if (importPath.includes("\0")) throw new ImportError(`'${importPath.replace(/\0/g, "")}' contains a NUL character`);
|
|
30223
30250
|
const remote = parseRemoteSpec(importPath);
|
|
30251
|
+
if (remote?.kind === "cloud" && fromPath && isRemoteSpec(fromPath) && parseRemoteSpec(fromPath)?.kind !== "cloud") {
|
|
30252
|
+
throw new ImportError(`'${importPath}' is a cloud import inside ${fromPath}; cloud: sources only work from your own diagrams`);
|
|
30253
|
+
}
|
|
30224
30254
|
if (remote) return formatRemoteSpec(remote);
|
|
30225
30255
|
if (isAbsolutePath(importPath)) throw new ImportError(`'${importPath}' is an absolute path; imports must be relative to the importing file`);
|
|
30226
30256
|
const from = fromPath && isRemoteSpec(fromPath) ? parseRemoteSpec(fromPath) : void 0;
|
|
30257
|
+
if (from?.kind === "cloud") {
|
|
30258
|
+
const parts2 = (from.path ?? "").split("/").slice(0, -1);
|
|
30259
|
+
for (const part of importPath.split("/")) {
|
|
30260
|
+
if (part === "" || part === ".") continue;
|
|
30261
|
+
if (part !== "..") parts2.push(part);
|
|
30262
|
+
else if (parts2.length > 0) parts2.pop();
|
|
30263
|
+
else throw new ImportError(`'${importPath}' leaves the files saved with cloud:${from.name}:${from.tag}`);
|
|
30264
|
+
}
|
|
30265
|
+
return formatRemoteSpec({ ...from, path: repoPathSegments(parts2.join("/"), importPath).join("/") });
|
|
30266
|
+
}
|
|
30227
30267
|
if (from?.kind === "git") {
|
|
30228
30268
|
const parts2 = from.path.split("/").slice(0, -1);
|
|
30229
30269
|
for (const part of importPath.split("/")) {
|
|
@@ -30260,11 +30300,14 @@ var LOCK_FILE = "proschi.lock";
|
|
|
30260
30300
|
var CACHE_DIR = ".proschi/cache";
|
|
30261
30301
|
var HASH = /^[0-9a-f]{64}$/;
|
|
30262
30302
|
var COMMIT = /^[0-9a-f]{40}([0-9a-f]{24})?$/;
|
|
30263
|
-
var emptyLock = () => ({ version: 1, git: {}, https: {} });
|
|
30303
|
+
var emptyLock = () => ({ version: 1, git: {}, https: {}, cloud: {} });
|
|
30264
30304
|
var gitKey = (repo, ref) => `${repo}@${ref}`;
|
|
30305
|
+
var cloudKey = (name, tag) => `${name}:${tag}`;
|
|
30265
30306
|
var remoteError = {
|
|
30266
30307
|
noLock: (spec) => new ImportError(`'${spec}' is a remote import and there is no ${LOCK_FILE}; run proschi imports sync`),
|
|
30267
|
-
notLocked: (spec) => new ImportError(`'${spec}' is not in ${LOCK_FILE}; run proschi imports sync`),
|
|
30308
|
+
notLocked: (spec) => spec.startsWith("cloud:") ? remoteError.cloud(spec) : new ImportError(`'${spec}' is not in ${LOCK_FILE}; run proschi imports sync`),
|
|
30309
|
+
/** A cloud version that is not pinned yet (docs/design/versions-and-cloud.md). */
|
|
30310
|
+
cloud: (spec) => new ImportError(`'${spec}' is a version in your Proschi account; run proschi imports sync to pin it (cloud sources need proschi login)`),
|
|
30268
30311
|
notCached: (spec) => new ImportError(`'${spec}' is not in the import cache (${CACHE_DIR}); run proschi imports sync`),
|
|
30269
30312
|
altered: (spec) => new ImportError(`The cached copy of '${spec}' does not match its hash in ${LOCK_FILE}; run proschi imports sync`)
|
|
30270
30313
|
};
|
|
@@ -30281,7 +30324,8 @@ function parseLock(text) {
|
|
|
30281
30324
|
const lock = emptyLock();
|
|
30282
30325
|
const git = json.git ?? {};
|
|
30283
30326
|
const https = json.https ?? {};
|
|
30284
|
-
|
|
30327
|
+
const cloud = json.cloud ?? {};
|
|
30328
|
+
if (!isRecord(git) || !isRecord(https) || !isRecord(cloud)) throw invalid('"git", "https" and "cloud" must be objects');
|
|
30285
30329
|
for (const [key, entry] of Object.entries(git)) {
|
|
30286
30330
|
const at = key.lastIndexOf("@");
|
|
30287
30331
|
const valid = at > 0 && isRecord(entry) && typeof entry.commit === "string" && COMMIT.test(entry.commit) && isRecord(entry.files) && Object.entries(entry.files).every(([path, hash]) => typeof hash === "string" && HASH.test(hash) && isGitSpec(`git:${key.slice(0, at)}//${path}@${key.slice(at + 1)}`));
|
|
@@ -30292,8 +30336,28 @@ function parseLock(text) {
|
|
|
30292
30336
|
if (typeof hash !== "string" || !HASH.test(hash) || parseRemoteSpec(url)?.kind !== "https") throw invalid(`bad https entry '${url}'`);
|
|
30293
30337
|
lock.https[url] = hash;
|
|
30294
30338
|
}
|
|
30339
|
+
for (const [key, entry] of Object.entries(cloud)) {
|
|
30340
|
+
const hash = (v) => typeof v === "string" && HASH.test(v);
|
|
30341
|
+
const valid = isCanonicalSpec(`cloud:${key}`, "cloud") && isRecord(entry) && typeof entry.tag === "string" && VERSION_TAG.test(entry.tag) && entry.tag !== LATEST_TAG && Number.isSafeInteger(entry.seq) && entry.seq > 0 && hash(entry.sha256) && (entry.source === void 0 || hash(entry.source)) && isRecord(entry.files) && Object.entries(entry.files).every(([path, h]) => hash(h) && isCanonicalSpec(`cloud:${key}//${path}`, "cloud"));
|
|
30342
|
+
if (!valid) throw invalid(`bad cloud entry '${key}'`);
|
|
30343
|
+
lock.cloud[key] = {
|
|
30344
|
+
tag: entry.tag,
|
|
30345
|
+
seq: entry.seq,
|
|
30346
|
+
sha256: entry.sha256,
|
|
30347
|
+
...entry.source === void 0 ? {} : { source: entry.source },
|
|
30348
|
+
files: { ...entry.files }
|
|
30349
|
+
};
|
|
30350
|
+
}
|
|
30295
30351
|
return lock;
|
|
30296
30352
|
}
|
|
30353
|
+
function isCanonicalSpec(spec, kind) {
|
|
30354
|
+
try {
|
|
30355
|
+
const remote = parseRemoteSpec(spec);
|
|
30356
|
+
return remote?.kind === kind && formatRemoteSpec(remote) === spec;
|
|
30357
|
+
} catch {
|
|
30358
|
+
return false;
|
|
30359
|
+
}
|
|
30360
|
+
}
|
|
30297
30361
|
function isGitSpec(spec) {
|
|
30298
30362
|
try {
|
|
30299
30363
|
return parseRemoteSpec(spec)?.kind === "git";
|
|
@@ -30305,6 +30369,10 @@ function lockedHash(lock, spec) {
|
|
|
30305
30369
|
const remote = parseRemoteSpec(spec);
|
|
30306
30370
|
if (remote?.kind === "git") return lock.git[gitKey(remote.repo, remote.ref)]?.files[remote.path];
|
|
30307
30371
|
if (remote?.kind === "https") return lock.https[remote.url];
|
|
30372
|
+
if (remote?.kind === "cloud") {
|
|
30373
|
+
const entry = lock.cloud[cloudKey(remote.name, remote.tag)];
|
|
30374
|
+
return remote.path === void 0 ? entry?.source : entry?.files[remote.path];
|
|
30375
|
+
}
|
|
30308
30376
|
return void 0;
|
|
30309
30377
|
}
|
|
30310
30378
|
|
|
@@ -30757,7 +30825,7 @@ function positionAt(text, offset) {
|
|
|
30757
30825
|
|
|
30758
30826
|
// src/server.ts
|
|
30759
30827
|
if (process.argv.includes("--version")) {
|
|
30760
|
-
console.log(true ? "0.
|
|
30828
|
+
console.log(true ? "0.14.0" : "dev");
|
|
30761
30829
|
process.exit(0);
|
|
30762
30830
|
}
|
|
30763
30831
|
var connection = (0, import_node.createConnection)(import_node.ProposedFeatures.all);
|
|
@@ -30803,7 +30871,7 @@ connection.onInitialize(() => ({
|
|
|
30803
30871
|
codeActionProvider: { codeActionKinds: [import_node.CodeActionKind.QuickFix] },
|
|
30804
30872
|
semanticTokensProvider: { legend: { tokenTypes: TOKEN_TYPES, tokenModifiers: [] }, full: true }
|
|
30805
30873
|
},
|
|
30806
|
-
serverInfo: { name: "proschi-language-server", version: true ? "0.
|
|
30874
|
+
serverInfo: { name: "proschi-language-server", version: true ? "0.14.0" : "dev" }
|
|
30807
30875
|
}));
|
|
30808
30876
|
function validate(document) {
|
|
30809
30877
|
const analysis = analysisOf(document);
|