visigraph 0.0.4 → 0.0.7
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 +31 -2
- package/dist/main.js +464 -58
- package/dist/main.js.map +1 -1
- package/package.json +6 -4
package/dist/main.js
CHANGED
|
@@ -2856,10 +2856,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2856
2856
|
* @param {string} flag
|
|
2857
2857
|
* @private
|
|
2858
2858
|
*/
|
|
2859
|
-
unknownOption(
|
|
2859
|
+
unknownOption(flag2) {
|
|
2860
2860
|
if (this._allowUnknownOption) return;
|
|
2861
2861
|
let suggestion = "";
|
|
2862
|
-
if (
|
|
2862
|
+
if (flag2.startsWith("--") && this._showSuggestionAfterError) {
|
|
2863
2863
|
let candidateFlags = [];
|
|
2864
2864
|
let command = this;
|
|
2865
2865
|
do {
|
|
@@ -2867,9 +2867,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2867
2867
|
candidateFlags = candidateFlags.concat(moreFlags);
|
|
2868
2868
|
command = command.parent;
|
|
2869
2869
|
} while (command && !command._enablePositionalOptions);
|
|
2870
|
-
suggestion = suggestSimilar(
|
|
2870
|
+
suggestion = suggestSimilar(flag2, candidateFlags);
|
|
2871
2871
|
}
|
|
2872
|
-
const message = `error: unknown option '${
|
|
2872
|
+
const message = `error: unknown option '${flag2}'${suggestion}`;
|
|
2873
2873
|
this.error(message, { code: "commander.unknownOption" });
|
|
2874
2874
|
}
|
|
2875
2875
|
/**
|
|
@@ -3662,13 +3662,13 @@ var require_directives = __commonJS({
|
|
|
3662
3662
|
onError("Verbatim tags must end with a >");
|
|
3663
3663
|
return verbatim;
|
|
3664
3664
|
}
|
|
3665
|
-
const [, handle,
|
|
3666
|
-
if (!
|
|
3665
|
+
const [, handle, suffix2] = source.match(/^(.*!)([^!]*)$/s);
|
|
3666
|
+
if (!suffix2)
|
|
3667
3667
|
onError(`The ${source} tag has no suffix`);
|
|
3668
3668
|
const prefix = this.tags[handle];
|
|
3669
3669
|
if (prefix) {
|
|
3670
3670
|
try {
|
|
3671
|
-
return prefix + decodeURIComponent(
|
|
3671
|
+
return prefix + decodeURIComponent(suffix2);
|
|
3672
3672
|
} catch (error) {
|
|
3673
3673
|
onError(String(error));
|
|
3674
3674
|
return null;
|
|
@@ -3956,36 +3956,38 @@ var require_Alias = __commonJS({
|
|
|
3956
3956
|
if (node.anchor === this.source)
|
|
3957
3957
|
found = node;
|
|
3958
3958
|
}
|
|
3959
|
+
if (found && ctx) {
|
|
3960
|
+
const { anchors: anchors2, doc: doc2, maxAliasCount } = ctx;
|
|
3961
|
+
let data = anchors2.get(found);
|
|
3962
|
+
if (!data) {
|
|
3963
|
+
toJS.toJS(found, null, ctx);
|
|
3964
|
+
data = anchors2.get(found);
|
|
3965
|
+
}
|
|
3966
|
+
if (data?.res === void 0) {
|
|
3967
|
+
const msg = "This should not happen: Alias anchor was not resolved?";
|
|
3968
|
+
throw new ReferenceError(msg);
|
|
3969
|
+
}
|
|
3970
|
+
if (maxAliasCount >= 0) {
|
|
3971
|
+
data.count += 1;
|
|
3972
|
+
if (data.aliasCount === 0)
|
|
3973
|
+
data.aliasCount = getAliasCount(doc2, found, anchors2);
|
|
3974
|
+
if (data.count * data.aliasCount > maxAliasCount) {
|
|
3975
|
+
const msg = "Excessive alias count indicates a resource exhaustion attack";
|
|
3976
|
+
throw new ReferenceError(msg);
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3959
3980
|
return found;
|
|
3960
3981
|
}
|
|
3961
3982
|
toJSON(_arg, ctx) {
|
|
3962
3983
|
if (!ctx)
|
|
3963
3984
|
return { source: this.source };
|
|
3964
|
-
const
|
|
3965
|
-
const source = this.resolve(doc, ctx);
|
|
3985
|
+
const source = this.resolve(ctx.doc, ctx);
|
|
3966
3986
|
if (!source) {
|
|
3967
3987
|
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
|
|
3968
3988
|
throw new ReferenceError(msg);
|
|
3969
3989
|
}
|
|
3970
|
-
|
|
3971
|
-
if (!data) {
|
|
3972
|
-
toJS.toJS(source, null, ctx);
|
|
3973
|
-
data = anchors2.get(source);
|
|
3974
|
-
}
|
|
3975
|
-
if (data?.res === void 0) {
|
|
3976
|
-
const msg = "This should not happen: Alias anchor was not resolved?";
|
|
3977
|
-
throw new ReferenceError(msg);
|
|
3978
|
-
}
|
|
3979
|
-
if (maxAliasCount >= 0) {
|
|
3980
|
-
data.count += 1;
|
|
3981
|
-
if (data.aliasCount === 0)
|
|
3982
|
-
data.aliasCount = getAliasCount(doc, source, anchors2);
|
|
3983
|
-
if (data.count * data.aliasCount > maxAliasCount) {
|
|
3984
|
-
const msg = "Excessive alias count indicates a resource exhaustion attack";
|
|
3985
|
-
throw new ReferenceError(msg);
|
|
3986
|
-
}
|
|
3987
|
-
}
|
|
3988
|
-
return data.res;
|
|
3990
|
+
return ctx.anchors.get(source).res;
|
|
3989
3991
|
}
|
|
3990
3992
|
toString(ctx, _onComment, _onChompKeep) {
|
|
3991
3993
|
const src = `*${this.source}`;
|
|
@@ -7987,37 +7989,38 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
7987
7989
|
}
|
|
7988
7990
|
if (badChar)
|
|
7989
7991
|
onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`);
|
|
7990
|
-
return
|
|
7992
|
+
return unfoldLines(source);
|
|
7991
7993
|
}
|
|
7992
7994
|
function singleQuotedValue(source, onError) {
|
|
7993
7995
|
if (source[source.length - 1] !== "'" || source.length === 1)
|
|
7994
7996
|
onError(source.length, "MISSING_CHAR", "Missing closing 'quote");
|
|
7995
|
-
return
|
|
7997
|
+
return unfoldLines(source.slice(1, -1)).replace(/''/g, "'");
|
|
7996
7998
|
}
|
|
7997
|
-
function
|
|
7998
|
-
|
|
7999
|
+
function unfoldLines(source) {
|
|
8000
|
+
const line = /(.*?)\r?\n/sy;
|
|
8001
|
+
let match = line.exec(source);
|
|
8002
|
+
if (!match)
|
|
8003
|
+
return source;
|
|
8004
|
+
let trimEnd, trimBoth;
|
|
7999
8005
|
try {
|
|
8000
|
-
|
|
8001
|
-
|
|
8006
|
+
trimEnd = new RegExp("(?<![ ])[ ]+$");
|
|
8007
|
+
trimBoth = new RegExp("^[ ]+|(?<![ ])[ ]+$", "g");
|
|
8002
8008
|
} catch {
|
|
8003
|
-
|
|
8004
|
-
|
|
8009
|
+
trimEnd = /[ \t]+$/;
|
|
8010
|
+
trimBoth = /^[ \t]+|[ \t]+$/g;
|
|
8005
8011
|
}
|
|
8006
|
-
let
|
|
8007
|
-
if (!match)
|
|
8008
|
-
return source;
|
|
8009
|
-
let res = match[1];
|
|
8012
|
+
let res = match[1].replace(trimEnd, "");
|
|
8010
8013
|
let sep = " ";
|
|
8011
|
-
let pos =
|
|
8012
|
-
line.lastIndex = pos;
|
|
8014
|
+
let pos = line.lastIndex;
|
|
8013
8015
|
while (match = line.exec(source)) {
|
|
8014
|
-
|
|
8016
|
+
const lm = match[1].replace(trimBoth, "");
|
|
8017
|
+
if (lm === "") {
|
|
8015
8018
|
if (sep === "\n")
|
|
8016
8019
|
res += sep;
|
|
8017
8020
|
else
|
|
8018
8021
|
sep = "\n";
|
|
8019
8022
|
} else {
|
|
8020
|
-
res += sep +
|
|
8023
|
+
res += sep + lm;
|
|
8021
8024
|
sep = " ";
|
|
8022
8025
|
}
|
|
8023
8026
|
pos = line.lastIndex;
|
|
@@ -10804,16 +10807,86 @@ function lineOf(range, lines) {
|
|
|
10804
10807
|
return `line ${line}, column ${col}`;
|
|
10805
10808
|
}
|
|
10806
10809
|
|
|
10810
|
+
// ../core/dist/kinds.js
|
|
10811
|
+
var FAMILIES = ["container", "endpoint", "entity"];
|
|
10812
|
+
var KindRegistry = class _KindRegistry {
|
|
10813
|
+
families = /* @__PURE__ */ new Map();
|
|
10814
|
+
static of(...extensions) {
|
|
10815
|
+
const r = new _KindRegistry();
|
|
10816
|
+
for (const e of extensions)
|
|
10817
|
+
r.register(e);
|
|
10818
|
+
return r;
|
|
10819
|
+
}
|
|
10820
|
+
register(extension) {
|
|
10821
|
+
for (const definition of extension.kinds) {
|
|
10822
|
+
if (!FAMILIES.includes(definition.family)) {
|
|
10823
|
+
throw new Error(`extension '${extension.name}': unknown family '${definition.family}'`);
|
|
10824
|
+
}
|
|
10825
|
+
const table = this.families.get(definition.family) ?? /* @__PURE__ */ new Map();
|
|
10826
|
+
const existing = table.get(definition.kind);
|
|
10827
|
+
if (existing) {
|
|
10828
|
+
throw new Error(`extension '${extension.name}': ${definition.family} kind '${definition.kind}' is already registered by '${existing.extension}'`);
|
|
10829
|
+
}
|
|
10830
|
+
table.set(definition.kind, { definition, extension: extension.name });
|
|
10831
|
+
this.families.set(definition.family, table);
|
|
10832
|
+
}
|
|
10833
|
+
return this;
|
|
10834
|
+
}
|
|
10835
|
+
/** True when at least one kind is registered for the family, so kinds in it are checked. */
|
|
10836
|
+
governs(family) {
|
|
10837
|
+
return (this.families.get(family)?.size ?? 0) > 0;
|
|
10838
|
+
}
|
|
10839
|
+
get(family, kind) {
|
|
10840
|
+
return this.families.get(family)?.get(kind)?.definition;
|
|
10841
|
+
}
|
|
10842
|
+
kinds(family) {
|
|
10843
|
+
return [...this.families.get(family)?.keys() ?? []].sort();
|
|
10844
|
+
}
|
|
10845
|
+
};
|
|
10846
|
+
function checkKind(registry, family, element) {
|
|
10847
|
+
if (!registry || !registry.governs(family))
|
|
10848
|
+
return [];
|
|
10849
|
+
const kind = element.kind;
|
|
10850
|
+
if (kind === void 0)
|
|
10851
|
+
return [];
|
|
10852
|
+
if (typeof kind !== "string")
|
|
10853
|
+
return [["kind", `${family} kind must be a string`]];
|
|
10854
|
+
const def = registry.get(family, kind);
|
|
10855
|
+
if (!def)
|
|
10856
|
+
return [["kind", `unknown ${family} kind '${kind}'; registered: ${registry.kinds(family).join(", ")}`]];
|
|
10857
|
+
const out = [];
|
|
10858
|
+
for (const [name, decl] of Object.entries(def.properties)) {
|
|
10859
|
+
const value = element[name];
|
|
10860
|
+
if (value === void 0) {
|
|
10861
|
+
if (decl.required)
|
|
10862
|
+
out.push([name, `${family} kind '${kind}' requires '${name}'`]);
|
|
10863
|
+
continue;
|
|
10864
|
+
}
|
|
10865
|
+
if (typeof value !== decl.type) {
|
|
10866
|
+
out.push([name, `'${name}' on ${family} kind '${kind}' must be a ${decl.type}`]);
|
|
10867
|
+
continue;
|
|
10868
|
+
}
|
|
10869
|
+
if (decl.enum && !decl.enum.includes(value)) {
|
|
10870
|
+
out.push([name, `'${name}' on ${family} kind '${kind}' must be one of ${decl.enum.join(", ")}; got '${String(value)}'`]);
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
return out;
|
|
10874
|
+
}
|
|
10875
|
+
|
|
10807
10876
|
// ../core/dist/resolve.js
|
|
10808
10877
|
function resolve(model, options = {}) {
|
|
10809
|
-
const r = new Resolver();
|
|
10878
|
+
const r = new Resolver(options.kinds);
|
|
10810
10879
|
const root = r.declare(model, model.system, void 0, { file: options.file ?? "", prefix: "" });
|
|
10811
10880
|
r.bind(root, void 0);
|
|
10812
10881
|
r.check(root);
|
|
10813
10882
|
return { root, diagnostics: r.diagnostics };
|
|
10814
10883
|
}
|
|
10815
10884
|
var Resolver = class {
|
|
10885
|
+
kinds;
|
|
10816
10886
|
diagnostics = [];
|
|
10887
|
+
constructor(kinds2) {
|
|
10888
|
+
this.kinds = kinds2;
|
|
10889
|
+
}
|
|
10817
10890
|
/** Scopes by interior identity, so a shared file is one scope. */
|
|
10818
10891
|
scopes = /* @__PURE__ */ new Map();
|
|
10819
10892
|
locs = /* @__PURE__ */ new Map();
|
|
@@ -10852,6 +10925,8 @@ var Resolver = class {
|
|
|
10852
10925
|
return;
|
|
10853
10926
|
}
|
|
10854
10927
|
scope.entities.set(entity.id, entity);
|
|
10928
|
+
for (const [suffix2, message] of checkKind(this.kinds, "entity", entity))
|
|
10929
|
+
err(`${path}.${suffix2}`, message);
|
|
10855
10930
|
});
|
|
10856
10931
|
eachListed(node.journeys, "journeys", err, (journey, path) => {
|
|
10857
10932
|
if (scope.journeys.has(journey.id)) {
|
|
@@ -10865,6 +10940,8 @@ var Resolver = class {
|
|
|
10865
10940
|
err(`${path}.id`, `duplicate container id '${child.id}'`);
|
|
10866
10941
|
return;
|
|
10867
10942
|
}
|
|
10943
|
+
for (const [suffix2, message] of checkKind(this.kinds, "container", child))
|
|
10944
|
+
err(`${path}.${suffix2}`, message);
|
|
10868
10945
|
const link = child[LINKED];
|
|
10869
10946
|
const childLoc = link ? { file: link.file, prefix: "" } : { file: loc.file, prefix: `${loc.prefix}${path}.` };
|
|
10870
10947
|
scope.children.set(child.id, this.declare(child, child.id, child, childLoc, scope.path));
|
|
@@ -10878,6 +10955,8 @@ var Resolver = class {
|
|
|
10878
10955
|
err(`${path}.id`, `duplicate endpoint id '${endpoint.id}' in container '${container.id}'`);
|
|
10879
10956
|
}
|
|
10880
10957
|
endpointIds.add(endpoint.id);
|
|
10958
|
+
for (const [suffix2, message] of checkKind(this.kinds, "endpoint", endpoint))
|
|
10959
|
+
err(`${path}.${suffix2}`, message);
|
|
10881
10960
|
});
|
|
10882
10961
|
const stateIds = /* @__PURE__ */ new Set();
|
|
10883
10962
|
eachListed(container.state, "state", err, (state, path) => {
|
|
@@ -10934,6 +11013,21 @@ var Resolver = class {
|
|
|
10934
11013
|
const loc = this.locs.get(scope);
|
|
10935
11014
|
const err = (path, message) => this.err(loc, path, message);
|
|
10936
11015
|
const node = scope.container ?? this.rootNode(scope);
|
|
11016
|
+
eachListed(node.containers, "containers", () => {
|
|
11017
|
+
}, (container, cPath) => {
|
|
11018
|
+
if (container[LINKED])
|
|
11019
|
+
return;
|
|
11020
|
+
eachListed(container.endpoints, `${cPath}.endpoints`, () => {
|
|
11021
|
+
}, (endpoint, ePath) => {
|
|
11022
|
+
for (const key of ["accepts", "returns"]) {
|
|
11023
|
+
if (endpoint[key] === void 0)
|
|
11024
|
+
continue;
|
|
11025
|
+
const entity = ref(endpoint[key], `${ePath}.${key}`, err);
|
|
11026
|
+
if (entity)
|
|
11027
|
+
this.lookupMember(scope, entity, "entity", `${ePath}.${key}`, err);
|
|
11028
|
+
}
|
|
11029
|
+
});
|
|
11030
|
+
});
|
|
10937
11031
|
eachListed(node.journeys, "journeys", err, (journey, path) => {
|
|
10938
11032
|
const hasSteps = journey.steps !== void 0;
|
|
10939
11033
|
const hasScenarios = journey.scenarios !== void 0;
|
|
@@ -10982,20 +11076,30 @@ var Resolver = class {
|
|
|
10982
11076
|
this.lookupContainer(scope, from, `${p}.from`, err);
|
|
10983
11077
|
const to = ref(step.to, `${p}.to`, err);
|
|
10984
11078
|
const target = to ? this.lookupContainer(scope, to, `${p}.to`, err) : void 0;
|
|
11079
|
+
let endpointDecl;
|
|
10985
11080
|
if (step.endpoint !== void 0) {
|
|
10986
11081
|
const endpoint = ref(step.endpoint, `${p}.endpoint`, err);
|
|
10987
11082
|
if (endpoint && target) {
|
|
10988
11083
|
if (endpoint.includes(".")) {
|
|
10989
11084
|
err(`${p}.endpoint`, `endpoint '${endpoint}' must be a bare id on the 'to' container, not a path`);
|
|
10990
|
-
} else
|
|
10991
|
-
|
|
11085
|
+
} else {
|
|
11086
|
+
endpointDecl = (target.container?.endpoints ?? []).find((e) => e.id === endpoint);
|
|
11087
|
+
if (!endpointDecl)
|
|
11088
|
+
err(`${p}.endpoint`, `container '${to}' has no endpoint '${endpoint}'`);
|
|
10992
11089
|
}
|
|
10993
11090
|
}
|
|
10994
11091
|
}
|
|
10995
11092
|
if (step.payload !== void 0) {
|
|
10996
|
-
const
|
|
10997
|
-
|
|
10998
|
-
|
|
11093
|
+
const entityRef = isRecord(step.payload) ? ref(step.payload.entity, `${p}.payload.entity`, err) : ref(step.payload, `${p}.payload`, err);
|
|
11094
|
+
const payload = entityRef ? this.lookupMember(scope, entityRef, "entity", `${p}.payload`, err) : void 0;
|
|
11095
|
+
if (payload && endpointDecl?.accepts !== void 0 && target) {
|
|
11096
|
+
const owner = this.ownerScope(target);
|
|
11097
|
+
const accepted = owner ? this.lookupMember(owner, endpointDecl.accepts, "entity", `${p}.payload`, () => {
|
|
11098
|
+
}) : void 0;
|
|
11099
|
+
if (accepted && accepted !== payload) {
|
|
11100
|
+
err(`${p}.payload`, `endpoint '${step.endpoint}' on '${to}' accepts '${endpointDecl.accepts}', not '${entityRef}'`);
|
|
11101
|
+
}
|
|
11102
|
+
}
|
|
10999
11103
|
}
|
|
11000
11104
|
if (!STEP_KINDS.includes(step.kind)) {
|
|
11001
11105
|
err(`${p}.kind`, `kind must be one of ${STEP_KINDS.join(", ")}; got '${String(step.kind)}'`);
|
|
@@ -11026,19 +11130,27 @@ var Resolver = class {
|
|
|
11026
11130
|
}
|
|
11027
11131
|
return cur;
|
|
11028
11132
|
}
|
|
11029
|
-
/** Resolve `a.b.name` as container path `a.b` plus a member id. */
|
|
11030
11133
|
lookupMember(scope, pathStr, kind, at, err) {
|
|
11031
11134
|
const segments = pathStr.split(".");
|
|
11032
11135
|
const name = segments.pop();
|
|
11033
11136
|
const owner = segments.length === 0 ? scope : this.lookupContainer(scope, segments.join("."), at, err);
|
|
11034
11137
|
if (!owner)
|
|
11035
|
-
return
|
|
11036
|
-
const
|
|
11037
|
-
if (!
|
|
11138
|
+
return void 0;
|
|
11139
|
+
const found = kind === "entity" ? owner.entities.get(name) : owner.journeys.get(name);
|
|
11140
|
+
if (!found) {
|
|
11038
11141
|
err(at, segments.length === 0 ? `unknown ${kind} '${name}'` : `unknown ${kind} '${name}' in '${segments.join(".")}'`);
|
|
11039
|
-
return
|
|
11142
|
+
return void 0;
|
|
11040
11143
|
}
|
|
11041
|
-
return
|
|
11144
|
+
return found;
|
|
11145
|
+
}
|
|
11146
|
+
/** The scope a container is declared in: where its endpoint contracts resolve from. */
|
|
11147
|
+
ownerScope(target) {
|
|
11148
|
+
for (const s of this.scopes.values()) {
|
|
11149
|
+
for (const child of s.children.values())
|
|
11150
|
+
if (child === target)
|
|
11151
|
+
return s;
|
|
11152
|
+
}
|
|
11153
|
+
return void 0;
|
|
11042
11154
|
}
|
|
11043
11155
|
};
|
|
11044
11156
|
function loadedFile(req, loc) {
|
|
@@ -11095,6 +11207,36 @@ var EXIT = {
|
|
|
11095
11207
|
usage: 2
|
|
11096
11208
|
};
|
|
11097
11209
|
|
|
11210
|
+
// ../ext-http/dist/index.js
|
|
11211
|
+
var http = {
|
|
11212
|
+
name: "@visigraph/ext-http",
|
|
11213
|
+
kinds: [
|
|
11214
|
+
{
|
|
11215
|
+
family: "endpoint",
|
|
11216
|
+
kind: "http",
|
|
11217
|
+
description: "An HTTP endpoint: a method and a path on a container.",
|
|
11218
|
+
properties: {
|
|
11219
|
+
method: {
|
|
11220
|
+
type: "string",
|
|
11221
|
+
tier: "advanced",
|
|
11222
|
+
required: true,
|
|
11223
|
+
enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"],
|
|
11224
|
+
category: "transport"
|
|
11225
|
+
},
|
|
11226
|
+
path: {
|
|
11227
|
+
type: "string",
|
|
11228
|
+
tier: "advanced",
|
|
11229
|
+
required: true,
|
|
11230
|
+
category: "transport"
|
|
11231
|
+
}
|
|
11232
|
+
}
|
|
11233
|
+
}
|
|
11234
|
+
]
|
|
11235
|
+
};
|
|
11236
|
+
|
|
11237
|
+
// src/kinds.ts
|
|
11238
|
+
var kinds = KindRegistry.of(http);
|
|
11239
|
+
|
|
11098
11240
|
// src/commands/validate.ts
|
|
11099
11241
|
function registerValidate(program3) {
|
|
11100
11242
|
program3.command("validate").argument("<model>", "path to a model file (YAML)").description("check a model and every model it links or requires; every identifier must resolve").addHelpText(
|
|
@@ -11112,7 +11254,7 @@ async function validate(file) {
|
|
|
11112
11254
|
const parsed = await loadModel(file);
|
|
11113
11255
|
let diagnostics = parsed.diagnostics;
|
|
11114
11256
|
if (parsed.model) {
|
|
11115
|
-
diagnostics = diagnostics.concat(resolve(parsed.model, { file }).diagnostics);
|
|
11257
|
+
diagnostics = diagnostics.concat(resolve(parsed.model, { file, kinds }).diagnostics);
|
|
11116
11258
|
}
|
|
11117
11259
|
for (const d of diagnostics) {
|
|
11118
11260
|
const where = d.path ? `${d.file ?? file}:${d.path}` : d.file ?? file;
|
|
@@ -11126,6 +11268,101 @@ async function validate(file) {
|
|
|
11126
11268
|
return EXIT.ok;
|
|
11127
11269
|
}
|
|
11128
11270
|
|
|
11271
|
+
// src/update.ts
|
|
11272
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
11273
|
+
import { spawn } from "child_process";
|
|
11274
|
+
import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
11275
|
+
import { homedir } from "os";
|
|
11276
|
+
import { join as join2 } from "path";
|
|
11277
|
+
import readline from "readline";
|
|
11278
|
+
|
|
11279
|
+
// src/progress.ts
|
|
11280
|
+
var CELLS = 6;
|
|
11281
|
+
var GLYPHS = {
|
|
11282
|
+
unicode: { filled: "\u25B0", empty: "\u25B1" },
|
|
11283
|
+
// ▰ ▱ black / white parallelogram
|
|
11284
|
+
ascii: { filled: "#", empty: ".", open: "[", close: "]" }
|
|
11285
|
+
};
|
|
11286
|
+
function renderProgress(done, total, opts = {}) {
|
|
11287
|
+
const cells = opts.cells ?? CELLS;
|
|
11288
|
+
const safeTotal = Math.max(total, 0);
|
|
11289
|
+
const safeDone = Math.min(Math.max(done, 0), safeTotal);
|
|
11290
|
+
const filled = safeTotal === 0 ? 0 : Math.round(safeDone / safeTotal * cells);
|
|
11291
|
+
return `${bar(filled, cells, opts.unicode ?? true)} ${safeDone}/${safeTotal}${suffix(opts.label)}`;
|
|
11292
|
+
}
|
|
11293
|
+
function renderSweep(tick, opts = {}) {
|
|
11294
|
+
const cells = opts.cells ?? CELLS;
|
|
11295
|
+
const unicode = opts.unicode ?? true;
|
|
11296
|
+
const g = unicode ? GLYPHS.unicode : GLYPHS.ascii;
|
|
11297
|
+
const pos = (tick % cells + cells) % cells;
|
|
11298
|
+
const body = Array.from({ length: cells }, (_, i) => i === pos ? g.filled : g.empty).join("");
|
|
11299
|
+
return `${unicode ? body : `${GLYPHS.ascii.open}${body}${GLYPHS.ascii.close}`}${suffix(opts.label)}`;
|
|
11300
|
+
}
|
|
11301
|
+
function bar(filled, cells, unicode) {
|
|
11302
|
+
const g = unicode ? GLYPHS.unicode : GLYPHS.ascii;
|
|
11303
|
+
const body = g.filled.repeat(filled) + g.empty.repeat(cells - filled);
|
|
11304
|
+
return unicode ? body : `${GLYPHS.ascii.open}${body}${GLYPHS.ascii.close}`;
|
|
11305
|
+
}
|
|
11306
|
+
function suffix(label) {
|
|
11307
|
+
return label ? ` ${label}` : "";
|
|
11308
|
+
}
|
|
11309
|
+
function supportsUnicode(env = process.env, platform = process.platform) {
|
|
11310
|
+
if (platform === "win32") {
|
|
11311
|
+
return Boolean(env.WT_SESSION || env.TERM_PROGRAM === "vscode" || env.ConEmuANSI === "ON");
|
|
11312
|
+
}
|
|
11313
|
+
if (env.TERM === "dumb") return false;
|
|
11314
|
+
const locale = env.LC_ALL || env.LC_CTYPE || env.LANG || "";
|
|
11315
|
+
return /utf-?8/i.test(locale) || locale === "";
|
|
11316
|
+
}
|
|
11317
|
+
var ProgressLine = class {
|
|
11318
|
+
constructor(out = process.stderr) {
|
|
11319
|
+
this.out = out;
|
|
11320
|
+
this.enabled = Boolean(out.isTTY);
|
|
11321
|
+
this.unicode = supportsUnicode();
|
|
11322
|
+
}
|
|
11323
|
+
out;
|
|
11324
|
+
timer;
|
|
11325
|
+
tick = 0;
|
|
11326
|
+
enabled;
|
|
11327
|
+
unicode;
|
|
11328
|
+
/** Animate a sweep with a label until `stop` or `finish` is called. */
|
|
11329
|
+
sweep(label, intervalMs = 120) {
|
|
11330
|
+
if (!this.enabled) return;
|
|
11331
|
+
this.clearTimer();
|
|
11332
|
+
this.draw(renderSweep(this.tick, { unicode: this.unicode, label }));
|
|
11333
|
+
this.timer = setInterval(() => {
|
|
11334
|
+
this.tick += 1;
|
|
11335
|
+
this.draw(renderSweep(this.tick, { unicode: this.unicode, label }));
|
|
11336
|
+
}, intervalMs);
|
|
11337
|
+
}
|
|
11338
|
+
/** Show `done` of `total` with a label. */
|
|
11339
|
+
update(done, total, label) {
|
|
11340
|
+
if (!this.enabled) return;
|
|
11341
|
+
this.clearTimer();
|
|
11342
|
+
this.draw(renderProgress(done, total, label === void 0 ? { unicode: this.unicode } : { unicode: this.unicode, label }));
|
|
11343
|
+
}
|
|
11344
|
+
/** Fill the bar, print the final label, and move to a new line. */
|
|
11345
|
+
finish(label) {
|
|
11346
|
+
if (!this.enabled) return;
|
|
11347
|
+
this.clearTimer();
|
|
11348
|
+
this.draw(renderProgress(CELLS, CELLS, { unicode: this.unicode, label }));
|
|
11349
|
+
this.out.write("\n");
|
|
11350
|
+
}
|
|
11351
|
+
/** Erase the line without a final message. */
|
|
11352
|
+
stop() {
|
|
11353
|
+
if (!this.enabled) return;
|
|
11354
|
+
this.clearTimer();
|
|
11355
|
+
this.out.write("\r\x1B[2K");
|
|
11356
|
+
}
|
|
11357
|
+
draw(text) {
|
|
11358
|
+
this.out.write(`\r\x1B[2K${text}`);
|
|
11359
|
+
}
|
|
11360
|
+
clearTimer() {
|
|
11361
|
+
if (this.timer) clearInterval(this.timer);
|
|
11362
|
+
this.timer = void 0;
|
|
11363
|
+
}
|
|
11364
|
+
};
|
|
11365
|
+
|
|
11129
11366
|
// src/version.ts
|
|
11130
11367
|
import { createRequire } from "module";
|
|
11131
11368
|
var load = createRequire(import.meta.url);
|
|
@@ -11133,15 +11370,184 @@ var pkg = load("../package.json");
|
|
|
11133
11370
|
var VERSION = pkg.version;
|
|
11134
11371
|
var DESCRIPTION = pkg.description;
|
|
11135
11372
|
|
|
11373
|
+
// src/update.ts
|
|
11374
|
+
var PKG_NAME = "visigraph";
|
|
11375
|
+
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
11376
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
11377
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
11378
|
+
var NO_UPDATE_ENV = "VISIGRAPH_NO_UPDATE_CHECK";
|
|
11379
|
+
var configDir = () => process.env.VISIGRAPH_HOME ?? join2(homedir(), ".visigraph");
|
|
11380
|
+
async function maybeUpdate() {
|
|
11381
|
+
const gate = shouldCheck({
|
|
11382
|
+
noUpdateEnv: process.env[NO_UPDATE_ENV] === "1",
|
|
11383
|
+
stdinTTY: Boolean(process.stdin.isTTY),
|
|
11384
|
+
stdoutTTY: Boolean(process.stdout.isTTY),
|
|
11385
|
+
configCheckDisabled: await configDisablesCheck()
|
|
11386
|
+
});
|
|
11387
|
+
if (!gate) return;
|
|
11388
|
+
const latest = await latestVersion();
|
|
11389
|
+
if (!latest || !isNewerVersion(latest, VERSION)) return;
|
|
11390
|
+
const viaNpx = invokedViaNpx({ npmCommand: process.env.npm_command, script: process.argv[1] ?? "" });
|
|
11391
|
+
console.log(`visigraph ${latest} is available; you have ${VERSION}.`);
|
|
11392
|
+
if (viaNpx) {
|
|
11393
|
+
console.log(`Run it with: npx ${PKG_NAME}@latest`);
|
|
11394
|
+
return;
|
|
11395
|
+
}
|
|
11396
|
+
if (!await confirm("Update now?")) return;
|
|
11397
|
+
const progress = new ProgressLine();
|
|
11398
|
+
progress.sweep(`installing visigraph ${latest}`);
|
|
11399
|
+
const result = await runInstall();
|
|
11400
|
+
if (!result.ok) {
|
|
11401
|
+
progress.stop();
|
|
11402
|
+
console.error("Update failed. Continuing with the current version.");
|
|
11403
|
+
if (result.output.trim()) console.error(result.output.trim());
|
|
11404
|
+
return;
|
|
11405
|
+
}
|
|
11406
|
+
progress.finish(`updated to ${latest}, relaunching`);
|
|
11407
|
+
await relaunch();
|
|
11408
|
+
}
|
|
11409
|
+
function shouldCheck(input) {
|
|
11410
|
+
if (input.noUpdateEnv) return false;
|
|
11411
|
+
if (input.configCheckDisabled) return false;
|
|
11412
|
+
if (!input.stdinTTY || !input.stdoutTTY) return false;
|
|
11413
|
+
return true;
|
|
11414
|
+
}
|
|
11415
|
+
function compareVersions(a, b) {
|
|
11416
|
+
const pa = a.split("-")[0].split(".").map(Number);
|
|
11417
|
+
const pb = b.split("-")[0].split(".").map(Number);
|
|
11418
|
+
for (let i = 0; i < 3; i++) {
|
|
11419
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
11420
|
+
if (d !== 0) return d;
|
|
11421
|
+
}
|
|
11422
|
+
return 0;
|
|
11423
|
+
}
|
|
11424
|
+
function isNewerVersion(latest, current) {
|
|
11425
|
+
return compareVersions(latest, current) > 0;
|
|
11426
|
+
}
|
|
11427
|
+
function invokedViaNpx(input) {
|
|
11428
|
+
return input.npmCommand === "exec" || /[\\/]_npx[\\/]/.test(input.script);
|
|
11429
|
+
}
|
|
11430
|
+
function registryFrom(input) {
|
|
11431
|
+
const fromEnv = input.env?.trim();
|
|
11432
|
+
if (fromEnv) return fromEnv.replace(/\/+$/, "");
|
|
11433
|
+
const line = input.npmrc?.split(/\r?\n/).find((l) => /^\s*registry\s*=/.test(l));
|
|
11434
|
+
const fromRc = line?.split("=").slice(1).join("=").trim();
|
|
11435
|
+
if (fromRc) return fromRc.replace(/\/+$/, "");
|
|
11436
|
+
return DEFAULT_REGISTRY;
|
|
11437
|
+
}
|
|
11438
|
+
function cacheIsFresh(entry, now, ttl = CACHE_TTL_MS) {
|
|
11439
|
+
return entry !== void 0 && typeof entry.latest === "string" && now - entry.checkedAt < ttl;
|
|
11440
|
+
}
|
|
11441
|
+
async function fetchLatestVersion(registry, fetchImpl = fetch) {
|
|
11442
|
+
try {
|
|
11443
|
+
const ctrl = new AbortController();
|
|
11444
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
|
11445
|
+
const res = await fetchImpl(`${registry}/${PKG_NAME}/latest`, {
|
|
11446
|
+
signal: ctrl.signal,
|
|
11447
|
+
headers: { "User-Agent": `${PKG_NAME}/${VERSION}`, accept: "application/json" }
|
|
11448
|
+
});
|
|
11449
|
+
clearTimeout(timer);
|
|
11450
|
+
if (!res.ok) return null;
|
|
11451
|
+
const data = await res.json();
|
|
11452
|
+
return typeof data.version === "string" ? data.version : null;
|
|
11453
|
+
} catch {
|
|
11454
|
+
return null;
|
|
11455
|
+
}
|
|
11456
|
+
}
|
|
11457
|
+
async function configDisablesCheck() {
|
|
11458
|
+
try {
|
|
11459
|
+
const text = await readFile2(join2(configDir(), "config.yaml"), "utf8");
|
|
11460
|
+
const cfg = (0, import_yaml2.parse)(text);
|
|
11461
|
+
return cfg?.update?.check === false;
|
|
11462
|
+
} catch {
|
|
11463
|
+
return false;
|
|
11464
|
+
}
|
|
11465
|
+
}
|
|
11466
|
+
async function latestVersion() {
|
|
11467
|
+
const cachePath = join2(configDir(), "update-check.json");
|
|
11468
|
+
const now = Date.now();
|
|
11469
|
+
try {
|
|
11470
|
+
const cached = JSON.parse(await readFile2(cachePath, "utf8"));
|
|
11471
|
+
if (cacheIsFresh(cached, now)) return cached.latest;
|
|
11472
|
+
} catch {
|
|
11473
|
+
}
|
|
11474
|
+
let npmrc;
|
|
11475
|
+
try {
|
|
11476
|
+
npmrc = await readFile2(join2(homedir(), ".npmrc"), "utf8");
|
|
11477
|
+
} catch {
|
|
11478
|
+
npmrc = void 0;
|
|
11479
|
+
}
|
|
11480
|
+
const registry = registryFrom({ env: process.env.npm_config_registry, npmrc });
|
|
11481
|
+
const latest = await fetchLatestVersion(registry);
|
|
11482
|
+
if (latest) {
|
|
11483
|
+
try {
|
|
11484
|
+
await mkdir(configDir(), { recursive: true });
|
|
11485
|
+
await writeFile(cachePath, JSON.stringify({ checkedAt: now, latest }));
|
|
11486
|
+
} catch {
|
|
11487
|
+
}
|
|
11488
|
+
}
|
|
11489
|
+
return latest;
|
|
11490
|
+
}
|
|
11491
|
+
function confirm(question) {
|
|
11492
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
11493
|
+
return new Promise((resolve2) => {
|
|
11494
|
+
rl.on("close", () => resolve2(false));
|
|
11495
|
+
rl.question(`${question} (y/N) `, (answer) => {
|
|
11496
|
+
resolve2(/^y(es)?$/i.test(answer.trim()));
|
|
11497
|
+
rl.close();
|
|
11498
|
+
});
|
|
11499
|
+
});
|
|
11500
|
+
}
|
|
11501
|
+
function runInstall() {
|
|
11502
|
+
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
11503
|
+
return new Promise((resolve2) => {
|
|
11504
|
+
const chunks = [];
|
|
11505
|
+
const child = spawn(npm, ["install", "-g", `${PKG_NAME}@latest`, "--no-fund", "--no-audit"], {
|
|
11506
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
11507
|
+
shell: false
|
|
11508
|
+
});
|
|
11509
|
+
child.stdout.on("data", (d) => chunks.push(d.toString()));
|
|
11510
|
+
child.stderr.on("data", (d) => chunks.push(d.toString()));
|
|
11511
|
+
child.on("exit", (code) => resolve2({ ok: code === 0, output: chunks.join("") }));
|
|
11512
|
+
child.on("error", (e) => resolve2({ ok: false, output: e.message }));
|
|
11513
|
+
});
|
|
11514
|
+
}
|
|
11515
|
+
function relaunch() {
|
|
11516
|
+
const script = process.argv[1];
|
|
11517
|
+
const args = process.argv.slice(2);
|
|
11518
|
+
return new Promise((_, reject) => {
|
|
11519
|
+
const child = spawn(process.execPath, [script, ...args], {
|
|
11520
|
+
stdio: "inherit",
|
|
11521
|
+
shell: false,
|
|
11522
|
+
env: { ...process.env, [NO_UPDATE_ENV]: "1" }
|
|
11523
|
+
});
|
|
11524
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
11525
|
+
child.on("error", reject);
|
|
11526
|
+
});
|
|
11527
|
+
}
|
|
11528
|
+
|
|
11136
11529
|
// src/main.ts
|
|
11530
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
11531
|
+
stream.on("error", (e) => {
|
|
11532
|
+
if (e.code === "EPIPE") process.exit(process.exitCode ?? 0);
|
|
11533
|
+
throw e;
|
|
11534
|
+
});
|
|
11535
|
+
}
|
|
11137
11536
|
var program2 = new Command();
|
|
11138
|
-
program2.name("visigraph").
|
|
11537
|
+
program2.name("visigraph").addHelpText("before", `${DESCRIPTION}
|
|
11538
|
+
`).version(VERSION, "-v, --version", "print the version and exit").helpOption("-h, --help", "print help and exit").helpCommand("help [command]", "print help for a command").showHelpAfterError("(run with --help for usage)").exitOverride();
|
|
11139
11539
|
registerValidate(program2);
|
|
11540
|
+
var INSTANT = /* @__PURE__ */ new Set(["-v", "--version", "-h", "--help", "help"]);
|
|
11541
|
+
var [flag, maybeCommand] = process.argv.slice(2);
|
|
11542
|
+
if ((flag === "-h" || flag === "--help") && maybeCommand && program2.commands.some((c) => c.name() === maybeCommand)) {
|
|
11543
|
+
process.argv.splice(2, 2, "help", maybeCommand);
|
|
11544
|
+
}
|
|
11140
11545
|
try {
|
|
11141
11546
|
if (process.argv.length <= 2) {
|
|
11142
11547
|
program2.outputHelp();
|
|
11143
11548
|
process.exitCode = EXIT.usage;
|
|
11144
11549
|
} else {
|
|
11550
|
+
if (!process.argv.slice(2).some((a) => INSTANT.has(a))) await maybeUpdate();
|
|
11145
11551
|
await program2.parseAsync(process.argv);
|
|
11146
11552
|
}
|
|
11147
11553
|
} catch (e) {
|