fastapi-voyager 0.9.1__py3-none-any.whl → 0.9.3__py3-none-any.whl
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.
- fastapi_voyager/version.py +1 -1
- fastapi_voyager/web/component/route-code-display.js +4 -14
- fastapi_voyager/web/component/schema-code-display.js +19 -26
- fastapi_voyager/web/component/schema-field-filter.js +6 -3
- fastapi_voyager/web/graph-ui.js +11 -7
- fastapi_voyager/web/index.html +186 -218
- fastapi_voyager/web/vue-main.js +22 -67
- {fastapi_voyager-0.9.1.dist-info → fastapi_voyager-0.9.3.dist-info}/METADATA +61 -43
- {fastapi_voyager-0.9.1.dist-info → fastapi_voyager-0.9.3.dist-info}/RECORD +12 -12
- {fastapi_voyager-0.9.1.dist-info → fastapi_voyager-0.9.3.dist-info}/WHEEL +0 -0
- {fastapi_voyager-0.9.1.dist-info → fastapi_voyager-0.9.3.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.9.1.dist-info → fastapi_voyager-0.9.3.dist-info}/licenses/LICENSE +0 -0
fastapi_voyager/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.9.
|
|
2
|
+
__version__ = "0.9.3"
|
|
@@ -3,14 +3,10 @@ const { defineComponent, ref, watch, onMounted } = window.Vue;
|
|
|
3
3
|
// Component: RouteCodeDisplay
|
|
4
4
|
// Props:
|
|
5
5
|
// routeId: route id key in routeItems
|
|
6
|
-
// modelValue: dialog visibility
|
|
7
|
-
// routes: object map { id: { id, name, source_code } }
|
|
8
6
|
export default defineComponent({
|
|
9
7
|
name: "RouteCodeDisplay",
|
|
10
8
|
props: {
|
|
11
9
|
routeId: { type: String, required: true },
|
|
12
|
-
modelValue: { type: Boolean, default: false },
|
|
13
|
-
routes: { type: Object, default: () => ({}) },
|
|
14
10
|
},
|
|
15
11
|
emits: ["close"],
|
|
16
12
|
setup(props, { emit }) {
|
|
@@ -101,32 +97,26 @@ export default defineComponent({
|
|
|
101
97
|
}
|
|
102
98
|
}
|
|
103
99
|
|
|
104
|
-
watch(
|
|
105
|
-
() => props.modelValue,
|
|
106
|
-
(v) => {
|
|
107
|
-
if (v) load();
|
|
108
|
-
}
|
|
109
|
-
);
|
|
110
100
|
watch(
|
|
111
101
|
() => props.routeId,
|
|
112
102
|
() => {
|
|
113
|
-
|
|
103
|
+
load();
|
|
114
104
|
}
|
|
115
105
|
);
|
|
116
106
|
|
|
117
107
|
onMounted(() => {
|
|
118
|
-
|
|
108
|
+
load();
|
|
119
109
|
});
|
|
120
110
|
|
|
121
111
|
return { loading, code, error, close, link };
|
|
122
112
|
},
|
|
123
113
|
template: `
|
|
124
|
-
<div class="frv-route-code-display" style="border:1px solid #ccc; position:relative;
|
|
114
|
+
<div class="frv-route-code-display" style="border:1px solid #ccc; position:relative; background:#fff;">
|
|
125
115
|
<q-btn dense flat round icon="close" @click="close" aria-label="Close" style="position:absolute; top:6px; right:6px; z-index:10; background:rgba(255,255,255,0.85)" />
|
|
126
116
|
<div v-if="link" class="q-ml-md q-mt-md" style="padding-top:4px;">
|
|
127
117
|
<a :href="link" target="_blank" rel="noopener" style="font-size:12px; color:#3b82f6;">Open in VSCode</a>
|
|
128
118
|
</div>
|
|
129
|
-
<div style="padding:40px 16px 16px 16px;
|
|
119
|
+
<div style="padding:40px 16px 16px 16px; box-sizing:border-box; overflow:auto;">
|
|
130
120
|
<div v-if="loading" style="font-family:Menlo, monospace; font-size:12px;">Loading source...</div>
|
|
131
121
|
<div v-else-if="error" style="color:#c10015; font-family:Menlo, monospace; font-size:12px;">{{ error }}</div>
|
|
132
122
|
<pre v-else style="margin:0;"><code class="language-python">{{ code }}</code></pre>
|
|
@@ -13,23 +13,17 @@ export default defineComponent({
|
|
|
13
13
|
name: "SchemaCodeDisplay",
|
|
14
14
|
props: {
|
|
15
15
|
schemaName: { type: String, required: true },
|
|
16
|
-
|
|
17
|
-
schemas: { type: Array, default: () => [] },
|
|
16
|
+
schemas: { type: Object, default: () => ({}) },
|
|
18
17
|
},
|
|
19
|
-
emits: ["close"],
|
|
20
18
|
setup(props, { emit }) {
|
|
21
|
-
const loading = ref(false);
|
|
22
19
|
const code = ref("");
|
|
23
20
|
const link = ref("");
|
|
24
21
|
const error = ref("");
|
|
25
22
|
const fields = ref([]); // schema fields list
|
|
26
23
|
const tab = ref("source");
|
|
27
24
|
|
|
28
|
-
function close() {
|
|
29
|
-
emit("close");
|
|
30
|
-
}
|
|
31
25
|
|
|
32
|
-
function highlightLater() {
|
|
26
|
+
async function highlightLater() {
|
|
33
27
|
// wait a tick for DOM update
|
|
34
28
|
requestAnimationFrame(() => {
|
|
35
29
|
try {
|
|
@@ -38,6 +32,10 @@ export default defineComponent({
|
|
|
38
32
|
".frv-code-display pre code.language-python"
|
|
39
33
|
);
|
|
40
34
|
if (block) {
|
|
35
|
+
// If already highlighted by highlight.js, remove the flag so it can be highlighted again
|
|
36
|
+
if (block.dataset && block.dataset.highlighted) {
|
|
37
|
+
block.removeAttribute("data-highlighted");
|
|
38
|
+
}
|
|
41
39
|
window.hljs.highlightElement(block);
|
|
42
40
|
}
|
|
43
41
|
}
|
|
@@ -50,7 +48,6 @@ export default defineComponent({
|
|
|
50
48
|
async function loadSource() {
|
|
51
49
|
if (!props.schemaName) return;
|
|
52
50
|
|
|
53
|
-
loading.value = true;
|
|
54
51
|
error.value = null;
|
|
55
52
|
code.value = "";
|
|
56
53
|
link.value = "";
|
|
@@ -77,7 +74,6 @@ export default defineComponent({
|
|
|
77
74
|
} catch (e) {
|
|
78
75
|
error.value = "Failed to load source";
|
|
79
76
|
} finally {
|
|
80
|
-
loading.value = false;
|
|
81
77
|
}
|
|
82
78
|
|
|
83
79
|
try {
|
|
@@ -99,10 +95,12 @@ export default defineComponent({
|
|
|
99
95
|
} catch (e) {
|
|
100
96
|
error.value = "Failed to load source";
|
|
101
97
|
} finally {
|
|
102
|
-
loading.value = false;
|
|
103
98
|
}
|
|
104
99
|
|
|
105
|
-
|
|
100
|
+
const schema = props.schemas && props.schemas[props.schemaName];
|
|
101
|
+
fields.value = Array.isArray(schema?.fields) ? schema.fields : [];
|
|
102
|
+
|
|
103
|
+
if (tab.value === "source") {
|
|
106
104
|
highlightLater();
|
|
107
105
|
}
|
|
108
106
|
}
|
|
@@ -118,25 +116,21 @@ export default defineComponent({
|
|
|
118
116
|
);
|
|
119
117
|
|
|
120
118
|
watch(
|
|
121
|
-
() => props.
|
|
122
|
-
(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
}
|
|
119
|
+
() => props.schemaName,
|
|
120
|
+
() => {
|
|
121
|
+
loadSource();
|
|
122
|
+
},
|
|
127
123
|
);
|
|
128
124
|
|
|
129
125
|
onMounted(() => {
|
|
130
|
-
|
|
126
|
+
loadSource();
|
|
131
127
|
});
|
|
132
128
|
|
|
133
|
-
return {
|
|
129
|
+
return { link, code, error, fields, tab };
|
|
134
130
|
},
|
|
135
131
|
template: `
|
|
136
|
-
<div class="frv-code-display" style="border: 1px solid #ccc; border-left: none; position:relative;
|
|
137
|
-
|
|
138
|
-
style="position:absolute; top:6px; right:6px; z-index:10; background:rgba(255,255,255,0.85)" />
|
|
139
|
-
<div v-if="link" class="q-ml-md q-mt-md">
|
|
132
|
+
<div class="frv-code-display" style="border: 1px solid #ccc; border-left: none; position:relative; height:100%; background:#fff;">
|
|
133
|
+
<div class="q-ml-lg q-mt-md">
|
|
140
134
|
<a :href="link" target="_blank" rel="noopener" style="font-size:12px; color:#3b82f6;">
|
|
141
135
|
Open in VSCode
|
|
142
136
|
</a>
|
|
@@ -150,8 +144,7 @@ export default defineComponent({
|
|
|
150
144
|
</div>
|
|
151
145
|
<q-separator />
|
|
152
146
|
<div style="padding:8px 16px 16px 16px; height:75%; box-sizing:border-box; overflow:auto;">
|
|
153
|
-
<div v-if="
|
|
154
|
-
<div v-else-if="error" style="color:#c10015; font-family:Menlo, monospace; font-size:12px;">{{ error }}</div>
|
|
147
|
+
<div v-if="error" style="color:#c10015; font-family:Menlo, monospace; font-size:12px;">{{ error }}</div>
|
|
155
148
|
<template v-else>
|
|
156
149
|
<div v-show="tab === 'source'">
|
|
157
150
|
<pre style="margin:0;"><code class="language-python">{{ code }}</code></pre>
|
|
@@ -14,7 +14,8 @@ export default defineComponent({
|
|
|
14
14
|
name: "SchemaFieldFilter",
|
|
15
15
|
props: {
|
|
16
16
|
schemaName: { type: String, default: null }, // external injection triggers auto-query
|
|
17
|
-
|
|
17
|
+
// externally provided schemas dict (state.rawSchemasFull): { [id]: schema }
|
|
18
|
+
schemas: { type: Object, default: () => ({}) },
|
|
18
19
|
},
|
|
19
20
|
emits: ["queried", "close"],
|
|
20
21
|
setup(props, { emit }) {
|
|
@@ -39,9 +40,11 @@ export default defineComponent({
|
|
|
39
40
|
let lastAppliedExternal = null;
|
|
40
41
|
|
|
41
42
|
async function loadSchemas() {
|
|
42
|
-
//
|
|
43
|
+
// Use externally provided props.schemas dict directly; no network call.
|
|
43
44
|
state.error = null;
|
|
44
|
-
|
|
45
|
+
const dict = props.schemas && typeof props.schemas === "object" ? props.schemas : {};
|
|
46
|
+
// Flatten to array for local operations
|
|
47
|
+
state.schemas = Object.values(dict);
|
|
45
48
|
state.schemaOptions = state.schemas.map((s) => ({
|
|
46
49
|
label: `${s.name} (${s.id})`,
|
|
47
50
|
value: s.id,
|
fastapi_voyager/web/graph-ui.js
CHANGED
|
@@ -69,23 +69,27 @@ export class GraphUI {
|
|
|
69
69
|
const set = $();
|
|
70
70
|
set.push(this);
|
|
71
71
|
const obj = { set, direction: "bidirectional" };
|
|
72
|
-
|
|
72
|
+
|
|
73
|
+
const schemaName = event.currentTarget.dataset.name;
|
|
73
74
|
if (event.shiftKey && self.options.onSchemaClick) {
|
|
74
75
|
// try data-name or title text
|
|
75
|
-
const schemaName = event.currentTarget.dataset.name;
|
|
76
76
|
if (schemaName) {
|
|
77
77
|
try {
|
|
78
|
-
self.options.
|
|
78
|
+
self.options.onSchemaShiftClick(schemaName);
|
|
79
79
|
} catch (e) {
|
|
80
|
-
console.warn("
|
|
80
|
+
console.warn("onSchemaShiftClick callback failed", e);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
} else if (event.altKey && self.options.onSchemaAltClick) {
|
|
84
|
-
const schemaName = event.currentTarget.dataset.name;
|
|
85
|
-
self.options.onSchemaAltClick(schemaName);
|
|
86
83
|
} else {
|
|
87
84
|
self.currentSelection = [obj];
|
|
88
85
|
self._highlight();
|
|
86
|
+
if (schemaName) {
|
|
87
|
+
try {
|
|
88
|
+
self.options.onSchemaClick(schemaName);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.warn("onSchemaClick callback failed", e);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
89
93
|
}
|
|
90
94
|
});
|
|
91
95
|
|
fastapi_voyager/web/index.html
CHANGED
|
@@ -55,51 +55,26 @@
|
|
|
55
55
|
flex: 1 1 auto;
|
|
56
56
|
overflow: auto;
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
.adjust-fit {
|
|
60
|
+
height: calc(100% - 54px);
|
|
61
|
+
}
|
|
58
62
|
</style>
|
|
59
63
|
<body>
|
|
60
|
-
<div id="q-app"
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
position: fixed;
|
|
65
|
-
inset: 0;
|
|
66
|
-
display: flex;
|
|
67
|
-
align-items: center;
|
|
68
|
-
justify-content: center;
|
|
69
|
-
z-index: 2000;
|
|
70
|
-
background: rgba(255, 255, 255, 0.85);
|
|
71
|
-
font-size: 18px;
|
|
72
|
-
font-family: Roboto, sans-serif;
|
|
73
|
-
color: #444;
|
|
74
|
-
"
|
|
75
|
-
>
|
|
76
|
-
<div style="text-align: center">
|
|
77
|
-
<div class="q-mb-sm">Initializing...</div>
|
|
78
|
-
<q-spinner color="primary" size="32px" />
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
<div
|
|
82
|
-
style="
|
|
83
|
-
padding: 8px;
|
|
84
|
-
background-color: #fff;
|
|
85
|
-
border-bottom: 1px solid #ccc;
|
|
86
|
-
color: #211d1d;
|
|
87
|
-
flex: 0 0 auto;
|
|
88
|
-
"
|
|
89
|
-
>
|
|
90
|
-
<div class="row items-center q-col-gutter-md">
|
|
91
|
-
<div class="col-auto">
|
|
64
|
+
<div id="q-app">
|
|
65
|
+
<q-layout view="hHh lpR fff">
|
|
66
|
+
<q-header bordered class="bg-primary text-white">
|
|
67
|
+
<q-toolbar class="row text-grey-9 bg-white" style="width: 100%">
|
|
92
68
|
<div
|
|
93
|
-
|
|
94
|
-
|
|
69
|
+
class="col-auto text-primary"
|
|
70
|
+
style="font-size: 16px; font-weight: bold"
|
|
95
71
|
>
|
|
96
72
|
<q-icon class="q-mr-sm" name="satellite_alt"></q-icon>
|
|
97
73
|
<span> FastAPI Voyager </span>
|
|
98
74
|
</div>
|
|
99
|
-
|
|
100
|
-
<div class="col-auto">
|
|
101
|
-
<div class="column">
|
|
75
|
+
<div class="col-auto" style="font-size: 16px">
|
|
102
76
|
<q-option-group
|
|
77
|
+
style="margin-left: 140px"
|
|
103
78
|
v-model="state.showFields"
|
|
104
79
|
:options="state.fieldOptions"
|
|
105
80
|
@update:model-value="(val) => toggleShowField(val)"
|
|
@@ -109,170 +84,189 @@
|
|
|
109
84
|
dense
|
|
110
85
|
/>
|
|
111
86
|
</div>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
>
|
|
123
|
-
<q-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
</
|
|
132
|
-
|
|
87
|
+
<div class="col-auto q-ml-auto">
|
|
88
|
+
<q-toggle
|
|
89
|
+
class="q-mr-md"
|
|
90
|
+
v-model="state.brief"
|
|
91
|
+
label="Brief Mode"
|
|
92
|
+
@update:model-value="(val) => toggleBrief(val)"
|
|
93
|
+
title="skip middle classes, config module_prefix to enable it"
|
|
94
|
+
dense
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="col-auto">
|
|
98
|
+
<q-toggle
|
|
99
|
+
v-model="state.hidePrimitiveRoute"
|
|
100
|
+
@update:model-value="(val) => toggleHidePrimitiveRoute(val)"
|
|
101
|
+
label="Hide Primitive"
|
|
102
|
+
title="hide routes return primitive"
|
|
103
|
+
class="q-mr-md"
|
|
104
|
+
dense
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="col-auto">
|
|
108
|
+
<q-btn
|
|
109
|
+
outline
|
|
110
|
+
@click="onReset"
|
|
111
|
+
title="may be very slow"
|
|
112
|
+
class="q-mr-md"
|
|
113
|
+
label="Show All"
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="col-auto">
|
|
117
|
+
<q-btn
|
|
118
|
+
outline
|
|
119
|
+
icon="search"
|
|
120
|
+
label="Search"
|
|
121
|
+
class="q-mr-md"
|
|
122
|
+
@click="showDialog()"
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="col-auto">
|
|
126
|
+
<q-btn
|
|
127
|
+
dense
|
|
128
|
+
round
|
|
129
|
+
flat
|
|
130
|
+
icon="help_outline"
|
|
131
|
+
aria-label="Help"
|
|
132
|
+
class="q-mr-sm"
|
|
133
|
+
>
|
|
134
|
+
<q-tooltip
|
|
135
|
+
anchor="bottom middle"
|
|
136
|
+
self="top middle"
|
|
137
|
+
:offset="[0,8]"
|
|
138
|
+
>
|
|
139
|
+
<div
|
|
140
|
+
class="column items-start text-left"
|
|
141
|
+
style="line-height: 1.4; font-size: 12px"
|
|
142
|
+
>
|
|
143
|
+
<ul>
|
|
144
|
+
<li>scroll to zoom in/out</li>
|
|
145
|
+
<li>
|
|
146
|
+
click node to check highlight related nodes on the
|
|
147
|
+
chart, esc to unselect
|
|
148
|
+
</li>
|
|
149
|
+
<li>
|
|
150
|
+
shift + click to see schema's dependencies without
|
|
151
|
+
unrelated nodes
|
|
152
|
+
</li>
|
|
153
|
+
<li>alt + click to see schema details</li>
|
|
154
|
+
</ul>
|
|
155
|
+
</div>
|
|
156
|
+
</q-tooltip>
|
|
157
|
+
</q-btn>
|
|
158
|
+
</div>
|
|
159
|
+
<!-- <q-toolbar-title class="text-primary row">
|
|
160
|
+
</q-toolbar-title> -->
|
|
161
|
+
</q-toolbar>
|
|
162
|
+
</q-header>
|
|
133
163
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
/>
|
|
143
|
-
</div>
|
|
144
|
-
<div class="col-auto">
|
|
145
|
-
<q-toggle
|
|
146
|
-
v-model="state.hidePrimitiveRoute"
|
|
147
|
-
@update:model-value="(val) => toggleHidePrimitiveRoute(val)"
|
|
148
|
-
label="Hide Primitive"
|
|
149
|
-
title="hide routes return primitive"
|
|
150
|
-
dense
|
|
151
|
-
/>
|
|
152
|
-
</div>
|
|
153
|
-
<div class="col-auto">
|
|
154
|
-
<q-btn
|
|
155
|
-
outline
|
|
156
|
-
@click="onReset"
|
|
157
|
-
title="may be very slow"
|
|
158
|
-
label="Show All"
|
|
159
|
-
/>
|
|
160
|
-
</div>
|
|
161
|
-
<div class="col-auto">
|
|
162
|
-
<q-btn outline icon="search" label="Search" @click="showDialog()" />
|
|
163
|
-
</div>
|
|
164
|
-
<div class="col-auto">
|
|
164
|
+
<q-drawer
|
|
165
|
+
v-model="state.detailDrawer"
|
|
166
|
+
width="400"
|
|
167
|
+
side="right"
|
|
168
|
+
overlay
|
|
169
|
+
bordered
|
|
170
|
+
>
|
|
171
|
+
<div style="z-index: 1; position: absolute; left: -17px; top: 9px">
|
|
165
172
|
<q-btn
|
|
166
|
-
|
|
173
|
+
@click="state.detailDrawer = !state.detailDrawer"
|
|
167
174
|
round
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
<q-tooltip
|
|
174
|
-
anchor="bottom middle"
|
|
175
|
-
self="top middle"
|
|
176
|
-
:offset="[0,8]"
|
|
177
|
-
>
|
|
178
|
-
<div
|
|
179
|
-
class="column items-start text-left"
|
|
180
|
-
style="line-height: 1.4; font-size: 12px"
|
|
181
|
-
>
|
|
182
|
-
<ul>
|
|
183
|
-
<li>scroll to zoom in/out</li>
|
|
184
|
-
<li>
|
|
185
|
-
click node to check highlight related nodes on the chart,
|
|
186
|
-
esc to unselect
|
|
187
|
-
</li>
|
|
188
|
-
<li>
|
|
189
|
-
shift + click to see schema's dependencies without
|
|
190
|
-
unrelated nodes
|
|
191
|
-
</li>
|
|
192
|
-
<li>alt + click to see schema details</li>
|
|
193
|
-
</ul>
|
|
194
|
-
</div>
|
|
195
|
-
</q-tooltip>
|
|
196
|
-
</q-btn>
|
|
175
|
+
unelevated
|
|
176
|
+
color="primary"
|
|
177
|
+
dense
|
|
178
|
+
icon="chevron_right"
|
|
179
|
+
/>
|
|
197
180
|
</div>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
181
|
+
<!-- move to bottom -->
|
|
182
|
+
<schema-code-display
|
|
183
|
+
:schema-name="schemaCodeName"
|
|
184
|
+
:schemas="state.rawSchemasFull"
|
|
185
|
+
/>
|
|
186
|
+
</q-drawer>
|
|
187
|
+
|
|
188
|
+
<q-page-container>
|
|
189
|
+
<q-splitter
|
|
190
|
+
v-model="state.splitter"
|
|
191
|
+
unit="px"
|
|
192
|
+
:limits="[200, 800]"
|
|
193
|
+
class="adjust-fit"
|
|
194
|
+
>
|
|
195
|
+
<template #before>
|
|
196
|
+
<div
|
|
197
|
+
id="tag-navigator"
|
|
198
|
+
class="column no-wrap"
|
|
199
|
+
:style="{
|
|
212
200
|
borderRight: '1px solid #e0e0e0',
|
|
213
201
|
backgroundColor: '#fff',
|
|
214
202
|
minHeight: 0,
|
|
215
203
|
height: '100%'
|
|
216
204
|
}"
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
205
|
+
>
|
|
206
|
+
<q-scroll-area class="fit">
|
|
207
|
+
<q-list dense separator>
|
|
208
|
+
<q-expansion-item
|
|
209
|
+
v-for="tag in state.rawTags"
|
|
210
|
+
:key="tag.name"
|
|
211
|
+
expand-separator
|
|
212
|
+
:model-value="state.tag === tag.name"
|
|
213
|
+
@update:model-value="(val) => toggleTag(tag.name, val)"
|
|
214
|
+
:header-class="state.tag === tag.name ? 'text-primary text-bold' : ''"
|
|
215
|
+
content-class="q-pa-none"
|
|
216
|
+
>
|
|
217
|
+
<template #header>
|
|
218
|
+
<div class="row items-center" style="width: 100%">
|
|
219
|
+
<div class="row items-end">
|
|
220
|
+
<q-icon
|
|
221
|
+
class="q-mr-sm"
|
|
222
|
+
:name="state.tag == tag.name ? 'folder' : 'folder_open'"
|
|
223
|
+
></q-icon>
|
|
224
|
+
<span>{{ tag.name }}</span>
|
|
225
|
+
</div>
|
|
237
226
|
</div>
|
|
238
|
-
</
|
|
239
|
-
</template>
|
|
227
|
+
</template>
|
|
240
228
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
>
|
|
251
|
-
<q-item-section>
|
|
252
|
-
<span class="q-ml-lg" style="white-space: nowrap">
|
|
253
|
-
<q-icon class="q-mr-sm" name="data_object"></q-icon>
|
|
254
|
-
{{ route.name }}
|
|
255
|
-
</span>
|
|
256
|
-
</q-item-section>
|
|
257
|
-
</q-item>
|
|
258
|
-
<q-item v-if="!tag.routes || tag.routes.length === 0" dense>
|
|
259
|
-
<q-item-section class="text-grey-6"
|
|
260
|
-
>No routes</q-item-section
|
|
229
|
+
<q-list separator>
|
|
230
|
+
<q-item
|
|
231
|
+
v-for="route in (state.hidePrimitiveRoute ? tag.routes.filter(r => !r.is_primitive) :tag.routes || [])"
|
|
232
|
+
:key="route.id"
|
|
233
|
+
clickable
|
|
234
|
+
v-ripple
|
|
235
|
+
:active="state.routeId === route.id"
|
|
236
|
+
active-class=""
|
|
237
|
+
@click="selectRoute(route.id)"
|
|
261
238
|
>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
239
|
+
<q-item-section>
|
|
240
|
+
<span class="q-ml-lg" style="white-space: nowrap">
|
|
241
|
+
<q-icon
|
|
242
|
+
class="q-mr-sm"
|
|
243
|
+
name="data_object"
|
|
244
|
+
></q-icon>
|
|
245
|
+
{{ route.name }}
|
|
246
|
+
</span>
|
|
247
|
+
</q-item-section>
|
|
248
|
+
</q-item>
|
|
249
|
+
<q-item
|
|
250
|
+
v-if="!tag.routes || tag.routes.length === 0"
|
|
251
|
+
dense
|
|
252
|
+
>
|
|
253
|
+
<q-item-section class="text-grey-6"
|
|
254
|
+
>No routes</q-item-section
|
|
255
|
+
>
|
|
256
|
+
</q-item>
|
|
257
|
+
</q-list>
|
|
258
|
+
</q-expansion-item>
|
|
259
|
+
</q-list>
|
|
260
|
+
</q-scroll-area>
|
|
261
|
+
</div>
|
|
262
|
+
</template>
|
|
275
263
|
|
|
264
|
+
<template #after>
|
|
265
|
+
<div id="graph" class="fit"></div>
|
|
266
|
+
</template>
|
|
267
|
+
</q-splitter>
|
|
268
|
+
</q-page-container>
|
|
269
|
+
</q-layout>
|
|
276
270
|
<!-- Detail Dialog -->
|
|
277
271
|
<q-dialog v-model="showDetail" :persistent="true" :maximized="true">
|
|
278
272
|
<detail-dialog
|
|
@@ -296,36 +290,10 @@
|
|
|
296
290
|
/>
|
|
297
291
|
</q-dialog>
|
|
298
292
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
:persistent="false"
|
|
304
|
-
position="left"
|
|
305
|
-
:seamless="false"
|
|
306
|
-
>
|
|
307
|
-
<schema-code-display
|
|
308
|
-
:schema-name="schemaCodeName"
|
|
309
|
-
:model-value="showSchemaCode"
|
|
310
|
-
:schemas="state.rawSchemasFull"
|
|
311
|
-
@close="showSchemaCode = false"
|
|
312
|
-
/>
|
|
313
|
-
</q-dialog>
|
|
314
|
-
|
|
315
|
-
<!-- Route Source Code Dialog (Alt + Click on route) -->
|
|
316
|
-
<q-dialog
|
|
317
|
-
v-model="showRouteCode"
|
|
318
|
-
:maximized="true"
|
|
319
|
-
:persistent="false"
|
|
320
|
-
position="right"
|
|
321
|
-
:seamless="false"
|
|
322
|
-
>
|
|
323
|
-
<route-code-display
|
|
324
|
-
:route-id="routeCodeId"
|
|
325
|
-
:model-value="showRouteCode"
|
|
326
|
-
:routes="state.routeItems"
|
|
327
|
-
@close="showRouteCode = false"
|
|
328
|
-
/>
|
|
293
|
+
<q-dialog v-model="showRouteDetail" position="bottom">
|
|
294
|
+
<q-card style="width: 1100px; max-width: 1100px">
|
|
295
|
+
<route-code-display :route-id="routeCodeId" @close="showRouteDetail=false" />
|
|
296
|
+
</q-card>
|
|
329
297
|
</q-dialog>
|
|
330
298
|
|
|
331
299
|
<!-- Dump Core Data Dialog -->
|
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -9,16 +9,12 @@ const app = createApp({
|
|
|
9
9
|
setup() {
|
|
10
10
|
const state = reactive({
|
|
11
11
|
// options and selections
|
|
12
|
-
tag: null,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
routeOptions: [], // [{ label, value }]
|
|
16
|
-
schemaId: null,
|
|
17
|
-
schemaOptions: [], // [{ label, value }]
|
|
18
|
-
routeItems: {}, // { id: { label, value } }
|
|
12
|
+
tag: null, // picked tag
|
|
13
|
+
routeId: null, // picked route
|
|
14
|
+
schemaId: null, // picked schema
|
|
19
15
|
showFields: "object",
|
|
20
16
|
fieldOptions: [
|
|
21
|
-
{ label: "No
|
|
17
|
+
{ label: "No field", value: "single" },
|
|
22
18
|
{ label: "Object fields", value: "object" },
|
|
23
19
|
{ label: "All fields", value: "all" },
|
|
24
20
|
],
|
|
@@ -26,17 +22,16 @@ const app = createApp({
|
|
|
26
22
|
hidePrimitiveRoute: false,
|
|
27
23
|
generating: false,
|
|
28
24
|
rawTags: [], // [{ name, routes: [{ id, name }] }]
|
|
29
|
-
rawSchemas:
|
|
30
|
-
rawSchemasFull:
|
|
25
|
+
rawSchemas: new Set(), // [{ name, id }]
|
|
26
|
+
rawSchemasFull: {}, // full schemas dict: { [schema.id]: schema }
|
|
31
27
|
initializing: true,
|
|
32
28
|
// Splitter size (left panel width in px)
|
|
33
29
|
splitter: 300,
|
|
30
|
+
detailDrawer: false
|
|
34
31
|
});
|
|
32
|
+
|
|
35
33
|
const showDetail = ref(false);
|
|
36
34
|
const showSchemaFieldFilter = ref(false);
|
|
37
|
-
const showSchemaCode = ref(false);
|
|
38
|
-
const showRouteCode = ref(false);
|
|
39
|
-
// Dump/Import dialogs and rendered graph dialog
|
|
40
35
|
const showDumpDialog = ref(false);
|
|
41
36
|
const dumpJson = ref("");
|
|
42
37
|
const showImportDialog = ref(false);
|
|
@@ -47,6 +42,7 @@ const app = createApp({
|
|
|
47
42
|
const schemaFieldFilterSchema = ref(null); // external schemaName for schema-field-filter
|
|
48
43
|
const schemaCodeName = ref("");
|
|
49
44
|
const routeCodeId = ref("");
|
|
45
|
+
const showRouteDetail = ref(false)
|
|
50
46
|
|
|
51
47
|
function openDetail() {
|
|
52
48
|
showDetail.value = true;
|
|
@@ -55,47 +51,18 @@ const app = createApp({
|
|
|
55
51
|
showDetail.value = false;
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
function onFilterTags(val, update) {
|
|
59
|
-
const normalized = (val || "").toLowerCase();
|
|
60
|
-
update(() => {
|
|
61
|
-
if (!normalized) {
|
|
62
|
-
state.tagOptions = state.rawTags.map((t) => t.name);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
state.tagOptions = state.rawTags
|
|
66
|
-
.map((t) => t.name)
|
|
67
|
-
.filter((n) => n.toLowerCase().includes(normalized));
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function onFilterSchemas(val, update) {
|
|
72
|
-
const normalized = (val || "").toLowerCase();
|
|
73
|
-
update(() => {
|
|
74
|
-
const makeLabel = (s) => `${s.name} (${s.id})`;
|
|
75
|
-
let list = state.rawSchemas.map((s) => ({
|
|
76
|
-
label: makeLabel(s),
|
|
77
|
-
value: s.id,
|
|
78
|
-
}));
|
|
79
|
-
if (normalized) {
|
|
80
|
-
list = list.filter((opt) =>
|
|
81
|
-
opt.label.toLowerCase().includes(normalized)
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
state.schemaOptions = list;
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
54
|
async function loadInitial() {
|
|
89
55
|
state.initializing = true;
|
|
90
56
|
try {
|
|
91
57
|
const res = await fetch("dot");
|
|
92
58
|
const data = await res.json();
|
|
93
59
|
state.rawTags = Array.isArray(data.tags) ? data.tags : [];
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
60
|
+
const schemasArr = Array.isArray(data.schemas) ? data.schemas : [];
|
|
61
|
+
// Build dict keyed by id for faster lookups and simpler prop passing
|
|
62
|
+
state.rawSchemasFull = Object.fromEntries(
|
|
63
|
+
schemasArr.map((s) => [s.id, s])
|
|
64
|
+
);
|
|
65
|
+
state.rawSchemas = new Set(Object.keys(state.rawSchemasFull));
|
|
99
66
|
state.routeItems = data.tags
|
|
100
67
|
.map((t) => t.routes)
|
|
101
68
|
.flat()
|
|
@@ -104,13 +71,7 @@ const app = createApp({
|
|
|
104
71
|
return acc;
|
|
105
72
|
}, {});
|
|
106
73
|
|
|
107
|
-
state.tagOptions = state.rawTags.map((t) => t.name);
|
|
108
|
-
state.schemaOptions = state.rawSchemas.map((s) => ({
|
|
109
|
-
label: `${s.name} (${s.id})`,
|
|
110
|
-
value: s.id,
|
|
111
|
-
}));
|
|
112
74
|
// default route options placeholder
|
|
113
|
-
state.routeOptions = [];
|
|
114
75
|
} catch (e) {
|
|
115
76
|
console.error("Initial load failed", e);
|
|
116
77
|
} finally {
|
|
@@ -139,23 +100,20 @@ const app = createApp({
|
|
|
139
100
|
|
|
140
101
|
// create graph instance once
|
|
141
102
|
const graphUI = new GraphUI("#graph", {
|
|
142
|
-
|
|
143
|
-
if (state.rawSchemas.
|
|
103
|
+
onSchemaShiftClick: (id) => {
|
|
104
|
+
if (state.rawSchemas.has(id)) {
|
|
144
105
|
schemaFieldFilterSchema.value = id;
|
|
145
106
|
showSchemaFieldFilter.value = true;
|
|
146
107
|
}
|
|
147
108
|
},
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (state.rawSchemas.find((s) => s.id === id)) {
|
|
109
|
+
onSchemaClick: (id) => {
|
|
110
|
+
if (state.rawSchemas.has(id)) {
|
|
151
111
|
schemaCodeName.value = id;
|
|
152
|
-
|
|
153
|
-
return;
|
|
112
|
+
state.detailDrawer = true
|
|
154
113
|
}
|
|
155
114
|
if (id in state.routeItems) {
|
|
156
115
|
routeCodeId.value = id;
|
|
157
|
-
|
|
158
|
-
return;
|
|
116
|
+
showRouteDetail.value = true
|
|
159
117
|
}
|
|
160
118
|
},
|
|
161
119
|
});
|
|
@@ -282,19 +240,16 @@ const app = createApp({
|
|
|
282
240
|
toggleBrief,
|
|
283
241
|
toggleHidePrimitiveRoute,
|
|
284
242
|
selectRoute,
|
|
285
|
-
onFilterTags,
|
|
286
|
-
onFilterSchemas,
|
|
287
243
|
onGenerate,
|
|
288
244
|
onReset,
|
|
289
245
|
showDetail,
|
|
246
|
+
showRouteDetail,
|
|
290
247
|
openDetail,
|
|
291
248
|
closeDetail,
|
|
292
249
|
schemaName,
|
|
293
250
|
showSchemaFieldFilter,
|
|
294
251
|
schemaFieldFilterSchema,
|
|
295
252
|
showDialog,
|
|
296
|
-
showSchemaCode,
|
|
297
|
-
showRouteCode,
|
|
298
253
|
schemaCodeName,
|
|
299
254
|
routeCodeId,
|
|
300
255
|
// dump/import
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-voyager
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: Visualize FastAPI application's routing tree and dependencies
|
|
5
5
|
Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
|
|
6
6
|
Project-URL: Source, https://github.com/allmonday/fastapi-voyager
|
|
@@ -27,18 +27,15 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
|
|
28
28
|
[](https://pypi.python.org/pypi/fastapi-voyager)
|
|
29
29
|

|
|
30
|
-
|
|
31
|
-
<p align="center"><img src="./voyager.jpg" alt="" /></p>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
[](https://www.youtube.com/watch?v=PGlbQq1M-n8 "FastAPI Voyager")
|
|
30
|
+
[](https://pepy.tech/projects/fastapi-voyager)
|
|
35
31
|
|
|
36
32
|
|
|
37
|
-
> This repo is still in early stage,
|
|
33
|
+
> This repo is still in early stage, it supports pydantic v2 only
|
|
38
34
|
|
|
39
|
-
Inspect your API interactively
|
|
35
|
+
Inspect your API interactively!
|
|
40
36
|
|
|
41
|
-
<
|
|
37
|
+
<p align="center"><img src="./voyager.jpg" alt="" /></p>
|
|
38
|
+
<p align="center"><a target="_blank" rel="" href="https://www.youtube.com/watch?v=PGlbQq1M-n8"><img src="http://img.youtube.com/vi/PGlbQq1M-n8/0.jpg" alt="" style="max-width: 100%;"></a></p>
|
|
42
39
|
|
|
43
40
|
## Installation
|
|
44
41
|
|
|
@@ -52,11 +49,6 @@ uv add fastapi-voyager
|
|
|
52
49
|
voyager -m path.to.your.app.module --server
|
|
53
50
|
```
|
|
54
51
|
|
|
55
|
-
## Dependencies
|
|
56
|
-
|
|
57
|
-
- FastAPI
|
|
58
|
-
- [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
|
|
59
|
-
|
|
60
52
|
|
|
61
53
|
## Feature
|
|
62
54
|
|
|
@@ -79,9 +71,9 @@ voyager -m tests.demo
|
|
|
79
71
|
```
|
|
80
72
|
|
|
81
73
|
### generate the graph
|
|
82
|
-
after initialization, pick tag, rotue
|
|
74
|
+
after initialization, pick tag, rotue to render graph
|
|
83
75
|
|
|
84
|
-
<img width="
|
|
76
|
+
<img width="1628" height="765" alt="image" src="https://github.com/user-attachments/assets/b4712f82-e754-453b-aa69-24c932b8f48f" />
|
|
85
77
|
|
|
86
78
|
### highlight
|
|
87
79
|
click a node to highlight it's upperstream and downstream nodes. figure out the related models of one page, or homw many pages are related with one model.
|
|
@@ -89,9 +81,9 @@ click a node to highlight it's upperstream and downstream nodes. figure out the
|
|
|
89
81
|
<img width="1485" height="616" alt="image" style="border: 1px solid #aaa" src="https://github.com/user-attachments/assets/70c4095f-86c7-45da-a6f0-fd41ac645813" />
|
|
90
82
|
|
|
91
83
|
### filter related nodes
|
|
92
|
-
`shift` click a node to check related node, pick a field to narrow the result.
|
|
84
|
+
`shift` click a node to check related node, pick a field to narrow the result, picked node is marked as red.
|
|
93
85
|
|
|
94
|
-
<img width="
|
|
86
|
+
<img width="1423" height="552" alt="image" src="https://github.com/user-attachments/assets/468a058d-afa1-4601-a7c5-c6aad6a8a557" />
|
|
95
87
|
|
|
96
88
|
### view source code
|
|
97
89
|
`alt` click a node to show source code or open file in vscode.
|
|
@@ -168,7 +160,24 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
|
|
|
168
160
|
|
|
169
161
|
## Plan before v1.0
|
|
170
162
|
|
|
171
|
-
|
|
163
|
+
|
|
164
|
+
### backlog
|
|
165
|
+
- [ ] user can generate nodes/edges manually and connect to generated ones
|
|
166
|
+
- [ ] add owner
|
|
167
|
+
- [ ] add extra info for schema
|
|
168
|
+
- [ ] display standard ER diagram `hard`
|
|
169
|
+
- [ ] display potential invalid links
|
|
170
|
+
- [ ] support dataclass (pending)
|
|
171
|
+
|
|
172
|
+
### in analysis
|
|
173
|
+
- [ ] click field to highlight links
|
|
174
|
+
- [ ] animation effect for edges
|
|
175
|
+
- [ ] customrized right click panel
|
|
176
|
+
- [ ] show own dependencies
|
|
177
|
+
- [ ] clean up fe code
|
|
178
|
+
|
|
179
|
+
### plan:
|
|
180
|
+
#### <0.9:
|
|
172
181
|
- [x] group schemas by module hierarchy
|
|
173
182
|
- [x] module-based coloring via Analytics(module_color={...})
|
|
174
183
|
- [x] view in web browser
|
|
@@ -196,26 +205,6 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
|
|
|
196
205
|
- [x] fix duplicated link from class and parent class, it also break clicking highlight
|
|
197
206
|
- [x] refactor: abstract render module
|
|
198
207
|
|
|
199
|
-
### backlog
|
|
200
|
-
- [ ] user can generate nodes/edges manually and connect to generated ones
|
|
201
|
-
- [ ] add owner
|
|
202
|
-
- [ ] add extra info for schema
|
|
203
|
-
- [ ] fixed left/right bar show field information
|
|
204
|
-
- [ ] display standard ER diagram `hard`
|
|
205
|
-
- [ ] display potential invalid links
|
|
206
|
-
|
|
207
|
-
bugs & non feature:
|
|
208
|
-
- [ ] add tests
|
|
209
|
-
- [ ] support dataclass (pending)
|
|
210
|
-
|
|
211
|
-
### in analysis
|
|
212
|
-
- [ ] click field to highlight links
|
|
213
|
-
- [ ] animation effect for edges
|
|
214
|
-
- [ ] customrized right click panel
|
|
215
|
-
- [ ] show own dependencies
|
|
216
|
-
|
|
217
|
-
### plan:
|
|
218
|
-
|
|
219
208
|
#### 0.9
|
|
220
209
|
- [x] refactor: server.py
|
|
221
210
|
- [x] rename create_app_with_fastapi -> create_voyager
|
|
@@ -223,19 +212,36 @@ bugs & non feature:
|
|
|
223
212
|
- [x] improve initialization time cost
|
|
224
213
|
- [x] query route / schema info through realtime api
|
|
225
214
|
- [x] adjust fe
|
|
215
|
+
- [x] adjust layout (0.9.3)
|
|
216
|
+
- [x] show field detail in right panel
|
|
217
|
+
- [x] show route info in bottom
|
|
226
218
|
|
|
227
219
|
#### 0.10
|
|
228
|
-
- [ ]
|
|
229
|
-
- [ ] open route in swagger
|
|
220
|
+
- [ ] support opening route in swagger
|
|
230
221
|
- config docs path
|
|
231
222
|
- [ ] add http method for route
|
|
232
223
|
- [ ] enable/disable module cluster (may save space)
|
|
224
|
+
- [ ] logging information
|
|
225
|
+
- [ ] add tests
|
|
226
|
+
- [ ] hide brief mode if not configured
|
|
227
|
+
- [ ] optimize static resource
|
|
228
|
+
- [ ] show route count in tag expansion item
|
|
229
|
+
- [ ] route list show have a max height to trigger scrollable
|
|
230
|
+
- [ ] fix layout issue when rendering huge graph
|
|
233
231
|
|
|
234
232
|
#### 0.11
|
|
233
|
+
- [ ] improve user experience
|
|
234
|
+
- double click to show detail
|
|
235
|
+
- improve search dialog
|
|
236
|
+
|
|
237
|
+
#### 0.12
|
|
235
238
|
- [ ] integration with pydantic-resolve
|
|
236
239
|
- [ ] show hint for resolve, post fields
|
|
237
240
|
- [ ] display loader as edges
|
|
238
241
|
|
|
242
|
+
#### 0.13
|
|
243
|
+
- [ ] config release pipeline
|
|
244
|
+
- [ ]
|
|
239
245
|
|
|
240
246
|
## Using with pydantic-resolve
|
|
241
247
|
|
|
@@ -247,13 +253,25 @@ pydantic-resolve's @ensure_subset decorator is helpful to pick fields from `sour
|
|
|
247
253
|
|
|
248
254
|
## Credits
|
|
249
255
|
|
|
250
|
-
- https://apis.guru/graphql-voyager/, for inspiration.
|
|
251
|
-
- https://github.com/tintinweb/vscode-interactive-graphviz, for web visualization.
|
|
256
|
+
- https://apis.guru/graphql-voyager/, thanks for inspiration.
|
|
257
|
+
- https://github.com/tintinweb/vscode-interactive-graphviz, thanks for web visualization.
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
## Dependencies
|
|
261
|
+
|
|
262
|
+
- FastAPI
|
|
263
|
+
- [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
|
|
264
|
+
- Quasar
|
|
252
265
|
|
|
253
266
|
|
|
254
267
|
## Changelog
|
|
255
268
|
|
|
256
269
|
- 0.9:
|
|
270
|
+
- 0.9.3:
|
|
271
|
+
- enhancement: better UI
|
|
272
|
+
- 0.9.2:
|
|
273
|
+
- fix: missing fields in schema detail panel
|
|
274
|
+
- optimization: clean up fe codes.
|
|
257
275
|
- 0.9.1:
|
|
258
276
|
- api change: from `create_app_with_fastapi` to `create_voyager`, and expose as `from fastapi_voyager import create_voyager`
|
|
259
277
|
- optimization: lazy load vscode link and source code, speed up the initialization.
|
|
@@ -6,19 +6,19 @@ fastapi_voyager/render.py,sha256=qy3g1Rz1s8XkuR_6n1Q1YPwy_oMOjWjNswTHQjdz4N0,776
|
|
|
6
6
|
fastapi_voyager/server.py,sha256=pg-LHDj4yU0usDA1b2X2Kt2_OCrexFA2G9ifGxb52Uc,6196
|
|
7
7
|
fastapi_voyager/type.py,sha256=pWYKmgb9e0W_JeD7k54Mr2lxUZV_Ir9TNpewGRwHyHQ,1629
|
|
8
8
|
fastapi_voyager/type_helper.py,sha256=hjBC4E0tgBpQDlYxGg74uK07SXjsrAgictEETJfIpYM,9231
|
|
9
|
-
fastapi_voyager/version.py,sha256=
|
|
9
|
+
fastapi_voyager/version.py,sha256=Lt2a1ZjlXG3QcL31iJHgzvRFDSTSsCXmwjQNWNdNUMY,48
|
|
10
10
|
fastapi_voyager/voyager.py,sha256=pXvA3ye5Jq6aJ6YrZFbTHjJ9MzdydqWllIgY8RFK_4A,10793
|
|
11
|
-
fastapi_voyager/web/graph-ui.js,sha256=
|
|
11
|
+
fastapi_voyager/web/graph-ui.js,sha256=QYJXHOH4EWF2jbcX3usTKv4tNody0RjpnVz-eh0UKkU,4185
|
|
12
12
|
fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
|
|
13
13
|
fastapi_voyager/web/graphviz.svg.js,sha256=lvAdbjHc-lMSk4GQp-iqYA2PCFX4RKnW7dFaoe0LUHs,16005
|
|
14
|
-
fastapi_voyager/web/index.html,sha256=
|
|
14
|
+
fastapi_voyager/web/index.html,sha256=YtVoDRrgz64E7IHpcPJBzUb1DI1H5P7a-3ziTx5BHYA,13735
|
|
15
15
|
fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
|
|
16
16
|
fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
|
|
17
|
-
fastapi_voyager/web/vue-main.js,sha256=
|
|
17
|
+
fastapi_voyager/web/vue-main.js,sha256=kQJpcQ2ENzA90v1XJ1P2bNGfeFC4drTGoG5dNhQKQqc,8105
|
|
18
18
|
fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
|
|
19
|
-
fastapi_voyager/web/component/route-code-display.js,sha256=
|
|
20
|
-
fastapi_voyager/web/component/schema-code-display.js,sha256=
|
|
21
|
-
fastapi_voyager/web/component/schema-field-filter.js,sha256=
|
|
19
|
+
fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
|
|
20
|
+
fastapi_voyager/web/component/schema-code-display.js,sha256=B-NOAKQIDORvFlKrsWNFAMGweXYBXrpg6AjKt4TFQFI,6256
|
|
21
|
+
fastapi_voyager/web/component/schema-field-filter.js,sha256=PR8d2_u1UiSLrS5zqAvY2UR8LbvjBqiUDt71tm1DJTs,6345
|
|
22
22
|
fastapi_voyager/web/icon/android-chrome-192x192.png,sha256=35sBy6jmUFJCcquStaafHH1qClZIbd-X3PIKSeLkrNo,37285
|
|
23
23
|
fastapi_voyager/web/icon/android-chrome-512x512.png,sha256=eb2eDjCwIruc05029_0L9hcrkVkv8KceLn1DJMYU0zY,210789
|
|
24
24
|
fastapi_voyager/web/icon/apple-touch-icon.png,sha256=gnWK46tPnvSw1-oYZjgI02wpoO4OrIzsVzGHC5oKWO0,33187
|
|
@@ -26,8 +26,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
|
|
|
26
26
|
fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
|
|
27
27
|
fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
|
|
28
28
|
fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
|
|
29
|
-
fastapi_voyager-0.9.
|
|
30
|
-
fastapi_voyager-0.9.
|
|
31
|
-
fastapi_voyager-0.9.
|
|
32
|
-
fastapi_voyager-0.9.
|
|
33
|
-
fastapi_voyager-0.9.
|
|
29
|
+
fastapi_voyager-0.9.3.dist-info/METADATA,sha256=m7vs-SegP6z60WrAwoMBV-fqyrUwlg08_EktecMzcSM,9490
|
|
30
|
+
fastapi_voyager-0.9.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
31
|
+
fastapi_voyager-0.9.3.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
|
|
32
|
+
fastapi_voyager-0.9.3.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
|
|
33
|
+
fastapi_voyager-0.9.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|