fastapi-voyager 0.9.2__py3-none-any.whl → 0.9.4__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/voyager.py +5 -0
- fastapi_voyager/web/component/route-code-display.js +4 -12
- fastapi_voyager/web/component/schema-code-display.js +24 -33
- fastapi_voyager/web/graph-ui.js +36 -7
- fastapi_voyager/web/index.html +185 -217
- fastapi_voyager/web/vue-main.js +16 -12
- {fastapi_voyager-0.9.2.dist-info → fastapi_voyager-0.9.4.dist-info}/METADATA +15 -34
- {fastapi_voyager-0.9.2.dist-info → fastapi_voyager-0.9.4.dist-info}/RECORD +12 -12
- {fastapi_voyager-0.9.2.dist-info → fastapi_voyager-0.9.4.dist-info}/WHEEL +0 -0
- {fastapi_voyager-0.9.2.dist-info → fastapi_voyager-0.9.4.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.9.2.dist-info → fastapi_voyager-0.9.4.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.4"
|
fastapi_voyager/voyager.py
CHANGED
|
@@ -144,6 +144,11 @@ class Voyager:
|
|
|
144
144
|
"""
|
|
145
145
|
full_name = full_class_name(schema)
|
|
146
146
|
bases_fields = get_bases_fields([s for s in schema.__bases__ if is_inheritance_of_pydantic_base(s)])
|
|
147
|
+
|
|
148
|
+
subset_reference = getattr(schema, const.ENSURE_SUBSET_REFERENCE, None)
|
|
149
|
+
if subset_reference and is_inheritance_of_pydantic_base(subset_reference):
|
|
150
|
+
bases_fields.update(get_bases_fields([subset_reference]))
|
|
151
|
+
|
|
147
152
|
if full_name not in self.node_set:
|
|
148
153
|
# skip meta info for normal queries
|
|
149
154
|
self.node_set[full_name] = SchemaNode(
|
|
@@ -3,12 +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
6
|
export default defineComponent({
|
|
8
7
|
name: "RouteCodeDisplay",
|
|
9
8
|
props: {
|
|
10
9
|
routeId: { type: String, required: true },
|
|
11
|
-
modelValue: { type: Boolean, default: false },
|
|
12
10
|
},
|
|
13
11
|
emits: ["close"],
|
|
14
12
|
setup(props, { emit }) {
|
|
@@ -99,32 +97,26 @@ export default defineComponent({
|
|
|
99
97
|
}
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
watch(
|
|
103
|
-
() => props.modelValue,
|
|
104
|
-
(v) => {
|
|
105
|
-
if (v) load();
|
|
106
|
-
}
|
|
107
|
-
);
|
|
108
100
|
watch(
|
|
109
101
|
() => props.routeId,
|
|
110
102
|
() => {
|
|
111
|
-
|
|
103
|
+
load();
|
|
112
104
|
}
|
|
113
105
|
);
|
|
114
106
|
|
|
115
107
|
onMounted(() => {
|
|
116
|
-
|
|
108
|
+
load();
|
|
117
109
|
});
|
|
118
110
|
|
|
119
111
|
return { loading, code, error, close, link };
|
|
120
112
|
},
|
|
121
113
|
template: `
|
|
122
|
-
<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;">
|
|
123
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)" />
|
|
124
116
|
<div v-if="link" class="q-ml-md q-mt-md" style="padding-top:4px;">
|
|
125
117
|
<a :href="link" target="_blank" rel="noopener" style="font-size:12px; color:#3b82f6;">Open in VSCode</a>
|
|
126
118
|
</div>
|
|
127
|
-
<div style="padding:40px 16px 16px 16px;
|
|
119
|
+
<div style="padding:40px 16px 16px 16px; box-sizing:border-box; overflow:auto;">
|
|
128
120
|
<div v-if="loading" style="font-family:Menlo, monospace; font-size:12px;">Loading source...</div>
|
|
129
121
|
<div v-else-if="error" style="color:#c10015; font-family:Menlo, monospace; font-size:12px;">{{ error }}</div>
|
|
130
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
|
-
modelValue: { type: Boolean, default: false },
|
|
17
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
|
-
const tab = ref("
|
|
23
|
+
const tab = ref("fields");
|
|
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,12 +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 : [];
|
|
106
102
|
|
|
107
|
-
if (
|
|
103
|
+
if (tab.value === "source") {
|
|
108
104
|
highlightLater();
|
|
109
105
|
}
|
|
110
106
|
}
|
|
@@ -120,25 +116,21 @@ export default defineComponent({
|
|
|
120
116
|
);
|
|
121
117
|
|
|
122
118
|
watch(
|
|
123
|
-
() => props.
|
|
124
|
-
(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
}
|
|
119
|
+
() => props.schemaName,
|
|
120
|
+
() => {
|
|
121
|
+
loadSource();
|
|
122
|
+
},
|
|
129
123
|
);
|
|
130
124
|
|
|
131
125
|
onMounted(() => {
|
|
132
|
-
|
|
126
|
+
loadSource();
|
|
133
127
|
});
|
|
134
128
|
|
|
135
|
-
return {
|
|
129
|
+
return { link, code, error, fields, tab };
|
|
136
130
|
},
|
|
137
131
|
template: `
|
|
138
|
-
<div class="frv-code-display" style="border: 1px solid #ccc; border-left: none; position:relative;
|
|
139
|
-
|
|
140
|
-
style="position:absolute; top:6px; right:6px; z-index:10; background:rgba(255,255,255,0.85)" />
|
|
141
|
-
<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">
|
|
142
134
|
<a :href="link" target="_blank" rel="noopener" style="font-size:12px; color:#3b82f6;">
|
|
143
135
|
Open in VSCode
|
|
144
136
|
</a>
|
|
@@ -146,25 +138,21 @@ export default defineComponent({
|
|
|
146
138
|
|
|
147
139
|
<div style="padding:8px 12px 0 12px; box-sizing:border-box;">
|
|
148
140
|
<q-tabs v-model="tab" align="left" dense active-color="primary" indicator-color="primary" class="text-grey-8">
|
|
149
|
-
<q-tab name="source" label="Source Code" />
|
|
150
141
|
<q-tab name="fields" label="Fields" />
|
|
142
|
+
<q-tab name="source" label="Source Code" />
|
|
151
143
|
</q-tabs>
|
|
152
144
|
</div>
|
|
153
145
|
<q-separator />
|
|
154
|
-
<div style="padding:8px 16px 16px 16px;
|
|
155
|
-
<div v-if="
|
|
156
|
-
<div v-else-if="error" style="color:#c10015; font-family:Menlo, monospace; font-size:12px;">{{ error }}</div>
|
|
146
|
+
<div style="padding:8px 16px 16px 16px; box-sizing:border-box; overflow:auto;">
|
|
147
|
+
<div v-if="error" style="color:#c10015; font-family:Menlo, monospace; font-size:12px;">{{ error }}</div>
|
|
157
148
|
<template v-else>
|
|
158
|
-
<div v-show="tab === 'source'">
|
|
159
|
-
<pre style="margin:0;"><code class="language-python">{{ code }}</code></pre>
|
|
160
|
-
</div>
|
|
161
149
|
<div v-show="tab === 'fields'">
|
|
162
150
|
<table style="border-collapse:collapse; width:100%; font-size:12px; font-family:Menlo, monospace;">
|
|
163
151
|
<thead>
|
|
164
152
|
<tr>
|
|
165
153
|
<th style="text-align:left; border-bottom:1px solid #ddd; padding:4px 6px;">Field</th>
|
|
166
154
|
<th style="text-align:left; border-bottom:1px solid #ddd; padding:4px 6px;">Type</th>
|
|
167
|
-
<th style="text-align:left; border-bottom:1px solid #ddd; padding:4px 6px;">
|
|
155
|
+
<th style="text-align:left; border-bottom:1px solid #ddd; padding:4px 6px;">Inherited</th>
|
|
168
156
|
</tr>
|
|
169
157
|
</thead>
|
|
170
158
|
<tbody>
|
|
@@ -179,6 +167,9 @@ export default defineComponent({
|
|
|
179
167
|
</tbody>
|
|
180
168
|
</table>
|
|
181
169
|
</div>
|
|
170
|
+
<div v-show="tab === 'source'">
|
|
171
|
+
<pre style="margin:0;"><code class="language-python">{{ code }}</code></pre>
|
|
172
|
+
</div>
|
|
182
173
|
</template>
|
|
183
174
|
</div>
|
|
184
175
|
</div>
|
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
|
|
|
@@ -101,6 +105,31 @@ export class GraphUI {
|
|
|
101
105
|
self._highlight();
|
|
102
106
|
});
|
|
103
107
|
|
|
108
|
+
// svg 背景点击高亮清空
|
|
109
|
+
|
|
110
|
+
$(document).off('click.graphui').on('click.graphui', function (evt) {
|
|
111
|
+
// 如果点击目标不在 graph 容器内,直接退出
|
|
112
|
+
const graphContainer = $(self.selector)[0];
|
|
113
|
+
if (!graphContainer || !evt.target || !graphContainer.contains(evt.target)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let isNode = false;
|
|
118
|
+
const $nodes = self.gv.nodes();
|
|
119
|
+
const node = evt.target.parentNode;
|
|
120
|
+
$nodes.each(function () {
|
|
121
|
+
if (this === node) {
|
|
122
|
+
isNode = true;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
if (!isNode && self.gv) {
|
|
126
|
+
self.gv.highlight();
|
|
127
|
+
if (self.options.resetCb) {
|
|
128
|
+
self.options.resetCb();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
104
133
|
$(document).on("keydown.graphui", function (evt) {
|
|
105
134
|
if (evt.keyCode === 27 && self.gv) {
|
|
106
135
|
self.gv.highlight();
|
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,188 @@
|
|
|
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="500"
|
|
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
|
+
<schema-code-display
|
|
182
|
+
:schema-name="schemaCodeName"
|
|
183
|
+
:schemas="state.rawSchemasFull"
|
|
184
|
+
/>
|
|
185
|
+
</q-drawer>
|
|
186
|
+
|
|
187
|
+
<q-page-container>
|
|
188
|
+
<q-splitter
|
|
189
|
+
v-model="state.splitter"
|
|
190
|
+
unit="px"
|
|
191
|
+
:limits="[200, 800]"
|
|
192
|
+
class="adjust-fit"
|
|
193
|
+
>
|
|
194
|
+
<template #before>
|
|
195
|
+
<div
|
|
196
|
+
id="tag-navigator"
|
|
197
|
+
class="column no-wrap"
|
|
198
|
+
:style="{
|
|
212
199
|
borderRight: '1px solid #e0e0e0',
|
|
213
200
|
backgroundColor: '#fff',
|
|
214
201
|
minHeight: 0,
|
|
215
202
|
height: '100%'
|
|
216
203
|
}"
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
204
|
+
>
|
|
205
|
+
<q-scroll-area class="fit">
|
|
206
|
+
<q-list dense separator>
|
|
207
|
+
<q-expansion-item
|
|
208
|
+
v-for="tag in state.rawTags"
|
|
209
|
+
:key="tag.name"
|
|
210
|
+
expand-separator
|
|
211
|
+
:model-value="state.tag === tag.name"
|
|
212
|
+
@update:model-value="(val) => toggleTag(tag.name, val)"
|
|
213
|
+
:header-class="state.tag === tag.name ? 'text-primary text-bold' : ''"
|
|
214
|
+
content-class="q-pa-none"
|
|
215
|
+
>
|
|
216
|
+
<template #header>
|
|
217
|
+
<div class="row items-center" style="width: 100%">
|
|
218
|
+
<div class="row items-end">
|
|
219
|
+
<q-icon
|
|
220
|
+
class="q-mr-sm"
|
|
221
|
+
:name="state.tag == tag.name ? 'folder' : 'folder_open'"
|
|
222
|
+
></q-icon>
|
|
223
|
+
<span>{{ tag.name }}</span>
|
|
224
|
+
</div>
|
|
237
225
|
</div>
|
|
238
|
-
</
|
|
239
|
-
</template>
|
|
226
|
+
</template>
|
|
240
227
|
|
|
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
|
|
228
|
+
<q-list separator>
|
|
229
|
+
<q-item
|
|
230
|
+
v-for="route in (state.hidePrimitiveRoute ? tag.routes.filter(r => !r.is_primitive) :tag.routes || [])"
|
|
231
|
+
:key="route.id"
|
|
232
|
+
clickable
|
|
233
|
+
v-ripple
|
|
234
|
+
:active="state.routeId === route.id"
|
|
235
|
+
active-class=""
|
|
236
|
+
@click="selectRoute(route.id)"
|
|
261
237
|
>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
238
|
+
<q-item-section>
|
|
239
|
+
<span class="q-ml-lg" style="white-space: nowrap">
|
|
240
|
+
<q-icon
|
|
241
|
+
class="q-mr-sm"
|
|
242
|
+
name="data_object"
|
|
243
|
+
></q-icon>
|
|
244
|
+
{{ route.name }}
|
|
245
|
+
</span>
|
|
246
|
+
</q-item-section>
|
|
247
|
+
</q-item>
|
|
248
|
+
<q-item
|
|
249
|
+
v-if="!tag.routes || tag.routes.length === 0"
|
|
250
|
+
dense
|
|
251
|
+
>
|
|
252
|
+
<q-item-section class="text-grey-6"
|
|
253
|
+
>No routes</q-item-section
|
|
254
|
+
>
|
|
255
|
+
</q-item>
|
|
256
|
+
</q-list>
|
|
257
|
+
</q-expansion-item>
|
|
258
|
+
</q-list>
|
|
259
|
+
</q-scroll-area>
|
|
260
|
+
</div>
|
|
261
|
+
</template>
|
|
275
262
|
|
|
263
|
+
<template #after>
|
|
264
|
+
<div id="graph" class="fit"></div>
|
|
265
|
+
</template>
|
|
266
|
+
</q-splitter>
|
|
267
|
+
</q-page-container>
|
|
268
|
+
</q-layout>
|
|
276
269
|
<!-- Detail Dialog -->
|
|
277
270
|
<q-dialog v-model="showDetail" :persistent="true" :maximized="true">
|
|
278
271
|
<detail-dialog
|
|
@@ -296,35 +289,10 @@
|
|
|
296
289
|
/>
|
|
297
290
|
</q-dialog>
|
|
298
291
|
|
|
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
|
-
@close="showRouteCode = false"
|
|
327
|
-
/>
|
|
292
|
+
<q-dialog v-model="showRouteDetail" seamless position="bottom">
|
|
293
|
+
<q-card style="width: 1100px; max-width: 1100px; max-height: 40vh">
|
|
294
|
+
<route-code-display :route-id="routeCodeId" @close="showRouteDetail=false" />
|
|
295
|
+
</q-card>
|
|
328
296
|
</q-dialog>
|
|
329
297
|
|
|
330
298
|
<!-- Dump Core Data Dialog -->
|
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -14,7 +14,7 @@ const app = createApp({
|
|
|
14
14
|
schemaId: null, // picked schema
|
|
15
15
|
showFields: "object",
|
|
16
16
|
fieldOptions: [
|
|
17
|
-
{ label: "No
|
|
17
|
+
{ label: "No field", value: "single" },
|
|
18
18
|
{ label: "Object fields", value: "object" },
|
|
19
19
|
{ label: "All fields", value: "all" },
|
|
20
20
|
],
|
|
@@ -27,12 +27,11 @@ const app = createApp({
|
|
|
27
27
|
initializing: true,
|
|
28
28
|
// Splitter size (left panel width in px)
|
|
29
29
|
splitter: 300,
|
|
30
|
+
detailDrawer: false,
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
const showDetail = ref(false);
|
|
33
34
|
const showSchemaFieldFilter = ref(false);
|
|
34
|
-
const showSchemaCode = ref(false);
|
|
35
|
-
const showRouteCode = ref(false);
|
|
36
35
|
const showDumpDialog = ref(false);
|
|
37
36
|
const dumpJson = ref("");
|
|
38
37
|
const showImportDialog = ref(false);
|
|
@@ -43,6 +42,7 @@ const app = createApp({
|
|
|
43
42
|
const schemaFieldFilterSchema = ref(null); // external schemaName for schema-field-filter
|
|
44
43
|
const schemaCodeName = ref("");
|
|
45
44
|
const routeCodeId = ref("");
|
|
45
|
+
const showRouteDetail = ref(false);
|
|
46
46
|
|
|
47
47
|
function openDetail() {
|
|
48
48
|
showDetail.value = true;
|
|
@@ -100,24 +100,26 @@ const app = createApp({
|
|
|
100
100
|
|
|
101
101
|
// create graph instance once
|
|
102
102
|
const graphUI = new GraphUI("#graph", {
|
|
103
|
-
|
|
103
|
+
onSchemaShiftClick: (id) => {
|
|
104
104
|
if (state.rawSchemas.has(id)) {
|
|
105
105
|
schemaFieldFilterSchema.value = id;
|
|
106
106
|
showSchemaFieldFilter.value = true;
|
|
107
107
|
}
|
|
108
108
|
},
|
|
109
|
-
|
|
109
|
+
onSchemaClick: (id) => {
|
|
110
110
|
if (state.rawSchemas.has(id)) {
|
|
111
111
|
schemaCodeName.value = id;
|
|
112
|
-
|
|
113
|
-
return;
|
|
112
|
+
state.detailDrawer = true;
|
|
114
113
|
}
|
|
115
114
|
if (id in state.routeItems) {
|
|
116
115
|
routeCodeId.value = id;
|
|
117
|
-
|
|
118
|
-
return;
|
|
116
|
+
showRouteDetail.value = true;
|
|
119
117
|
}
|
|
120
118
|
},
|
|
119
|
+
resetCb: () => {
|
|
120
|
+
state.detailDrawer = false;
|
|
121
|
+
showRouteDetail.value = false;
|
|
122
|
+
}
|
|
121
123
|
});
|
|
122
124
|
|
|
123
125
|
await graphUI.render(dotText, resetZoom);
|
|
@@ -204,8 +206,9 @@ const app = createApp({
|
|
|
204
206
|
state.tag = tagName;
|
|
205
207
|
state.routeId = "";
|
|
206
208
|
onGenerate();
|
|
207
|
-
return;
|
|
208
209
|
}
|
|
210
|
+
state.detailDrawer = false;
|
|
211
|
+
showRouteDetail.value = false;
|
|
209
212
|
}
|
|
210
213
|
|
|
211
214
|
function selectRoute(routeId) {
|
|
@@ -214,6 +217,8 @@ const app = createApp({
|
|
|
214
217
|
} else {
|
|
215
218
|
state.routeId = routeId;
|
|
216
219
|
}
|
|
220
|
+
state.detailDrawer = false;
|
|
221
|
+
showRouteDetail.value = false;
|
|
217
222
|
onGenerate();
|
|
218
223
|
}
|
|
219
224
|
|
|
@@ -245,14 +250,13 @@ const app = createApp({
|
|
|
245
250
|
onGenerate,
|
|
246
251
|
onReset,
|
|
247
252
|
showDetail,
|
|
253
|
+
showRouteDetail,
|
|
248
254
|
openDetail,
|
|
249
255
|
closeDetail,
|
|
250
256
|
schemaName,
|
|
251
257
|
showSchemaFieldFilter,
|
|
252
258
|
schemaFieldFilterSchema,
|
|
253
259
|
showDialog,
|
|
254
|
-
showSchemaCode,
|
|
255
|
-
showRouteCode,
|
|
256
260
|
schemaCodeName,
|
|
257
261
|
routeCodeId,
|
|
258
262
|
// 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.4
|
|
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,6 +27,8 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
|
|
28
28
|
[](https://pypi.python.org/pypi/fastapi-voyager)
|
|
29
29
|

|
|
30
|
+
[](https://pepy.tech/projects/fastapi-voyager)
|
|
31
|
+
|
|
30
32
|
|
|
31
33
|
> This repo is still in early stage, it supports pydantic v2 only
|
|
32
34
|
|
|
@@ -163,7 +165,6 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
|
|
|
163
165
|
- [ ] user can generate nodes/edges manually and connect to generated ones
|
|
164
166
|
- [ ] add owner
|
|
165
167
|
- [ ] add extra info for schema
|
|
166
|
-
- [ ] fixed left/right bar show field information
|
|
167
168
|
- [ ] display standard ER diagram `hard`
|
|
168
169
|
- [ ] display potential invalid links
|
|
169
170
|
- [ ] support dataclass (pending)
|
|
@@ -211,6 +212,15 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
|
|
|
211
212
|
- [x] improve initialization time cost
|
|
212
213
|
- [x] query route / schema info through realtime api
|
|
213
214
|
- [x] adjust fe
|
|
215
|
+
- 0.9.3
|
|
216
|
+
- [x] adjust layout
|
|
217
|
+
- [x] show field detail in right panel
|
|
218
|
+
- [x] show route info in bottom
|
|
219
|
+
- 0.9.4
|
|
220
|
+
- [x] close schema sidebar when switch tag/route
|
|
221
|
+
- [x] schema detail panel show fields by default
|
|
222
|
+
- [x] adjust schema panel's height
|
|
223
|
+
- [x] show from base information in subset case
|
|
214
224
|
|
|
215
225
|
#### 0.10
|
|
216
226
|
- [ ] support opening route in swagger
|
|
@@ -235,6 +245,9 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
|
|
|
235
245
|
- [ ] show hint for resolve, post fields
|
|
236
246
|
- [ ] display loader as edges
|
|
237
247
|
|
|
248
|
+
#### 0.13
|
|
249
|
+
- [ ] config release pipeline
|
|
250
|
+
- [ ]
|
|
238
251
|
|
|
239
252
|
## Using with pydantic-resolve
|
|
240
253
|
|
|
@@ -256,35 +269,3 @@ pydantic-resolve's @ensure_subset decorator is helpful to pick fields from `sour
|
|
|
256
269
|
- [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
|
|
257
270
|
- Quasar
|
|
258
271
|
|
|
259
|
-
|
|
260
|
-
## Changelog
|
|
261
|
-
|
|
262
|
-
- 0.9:
|
|
263
|
-
- 0.9.2:
|
|
264
|
-
- fix: missing fields in schema detail panel
|
|
265
|
-
- optimization: clean up fe codes.
|
|
266
|
-
- 0.9.1:
|
|
267
|
-
- api change: from `create_app_with_fastapi` to `create_voyager`, and expose as `from fastapi_voyager import create_voyager`
|
|
268
|
-
- optimization: lazy load vscode link and source code, speed up the initialization.
|
|
269
|
-
- 0.8:
|
|
270
|
-
- 0.8.3
|
|
271
|
-
- upgrade theme
|
|
272
|
-
- 0.8.2
|
|
273
|
-
- fix silly typo.
|
|
274
|
-
- 0.8.1
|
|
275
|
-
- add feature: hide primitive routes
|
|
276
|
-
- 0.7:
|
|
277
|
-
- 0.7.5
|
|
278
|
-
- fix show all display issue
|
|
279
|
-
- 0.7.4
|
|
280
|
-
- optimize tag/route, move to left.
|
|
281
|
-
- fresh on change, no need to click generate any more.
|
|
282
|
-
- 0.7.3
|
|
283
|
-
- fix `module_color` failure
|
|
284
|
-
- 0.7.2
|
|
285
|
-
- keep links inside filtered nodes.
|
|
286
|
-
- 0.7.1
|
|
287
|
-
- support brief mode, you can use `--module_prefix tests.service` to show links between routes and filtered schemas, to make the graph less complicated.
|
|
288
|
-
- 0.6:
|
|
289
|
-
- 0.6.2:
|
|
290
|
-
- fix generic related issue
|
|
@@ -6,18 +6,18 @@ 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=
|
|
10
|
-
fastapi_voyager/voyager.py,sha256=
|
|
11
|
-
fastapi_voyager/web/graph-ui.js,sha256=
|
|
9
|
+
fastapi_voyager/version.py,sha256=IZXkxs2gZAlAzk5SmuL6_BaHzvWZi9m7CX8vM0JMzW8,48
|
|
10
|
+
fastapi_voyager/voyager.py,sha256=QKWletUwZeHm8e8ZRcGm6OuhAdiik3tT7sOU-QnEuGA,11036
|
|
11
|
+
fastapi_voyager/web/graph-ui.js,sha256=FmKA3eeHMEeUDNuay3f54_fv4eGHT7MT0TaGqXhyMWQ,4978
|
|
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=tbrtun9cDFMwnUIN5S0gtMEs18bwZLgXuxsSaKxgcI0,13728
|
|
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=bn4Q9FqX5RJVG9bA5xgUUt5TJf9CjiXcKtn_ikLsaKQ,8357
|
|
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=
|
|
19
|
+
fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
|
|
20
|
+
fastapi_voyager/web/component/schema-code-display.js,sha256=UgFotzvqSuhnPXNOr6w_r1fV2_savRiCdokEvferutE,6244
|
|
21
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
|
|
@@ -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.4.dist-info/METADATA,sha256=jYmSddqIU_P3mROi5Rg9jjderpGJ4ASMDY4pZdoYBCs,8642
|
|
30
|
+
fastapi_voyager-0.9.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
31
|
+
fastapi_voyager-0.9.4.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
|
|
32
|
+
fastapi_voyager-0.9.4.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
|
|
33
|
+
fastapi_voyager-0.9.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|