[ui] refactor - centralize all fetch()
calls in api.ts
This commit is contained in:
parent
c2b26ccfee
commit
e522e99209
16 changed files with 249 additions and 193 deletions
|
@ -25,6 +25,21 @@ export type IValue =
|
||||||
c: null;
|
c: null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface InvariantEntry {
|
||||||
|
attribute: string;
|
||||||
|
value: IValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InAddress =
|
||||||
|
| Address
|
||||||
|
| { t: "Attribute" | "Url" | "Uuid"; c?: string };
|
||||||
|
|
||||||
|
export type InEntry =
|
||||||
|
| IEntry
|
||||||
|
| IEntry[]
|
||||||
|
| InvariantEntry
|
||||||
|
| { entity: InAddress };
|
||||||
|
|
||||||
export interface ListingResult {
|
export interface ListingResult {
|
||||||
[key: string]: IEntry;
|
[key: string]: IEntry;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useNavigate } from "svelte-navigator";
|
import { useNavigate } from "svelte-navigator";
|
||||||
import type { PutResult } from "upend/types";
|
import { uploadFile } from "../lib/api";
|
||||||
import Icon from "./utils/Icon.svelte";
|
import Icon from "./utils/Icon.svelte";
|
||||||
import IconButton from "./utils/IconButton.svelte";
|
import IconButton from "./utils/IconButton.svelte";
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
@ -30,21 +30,7 @@
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const responses = await Promise.all(
|
const responses = await Promise.all(
|
||||||
files.map(async (file) => {
|
files.map(async (file) => uploadFile(file))
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("file", file);
|
|
||||||
|
|
||||||
const response = await fetch("api/obj", {
|
|
||||||
method: "PUT",
|
|
||||||
body: formData,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw Error(await response.text());
|
|
||||||
}
|
|
||||||
|
|
||||||
return (await response.json()) as PutResult;
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const addresses = responses.map(([_, entry]) => entry);
|
const addresses = responses.map(([_, entry]) => entry);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import { putEntityAttribute } from "../lib/api";
|
||||||
import { normUrl } from "../util/history";
|
import { normUrl } from "../util/history";
|
||||||
|
|
||||||
import Inspect from "./Inspect.svelte";
|
import Inspect from "./Inspect.svelte";
|
||||||
|
@ -19,10 +20,9 @@
|
||||||
window.open(normUrl(`/browse/${address}`), "_blank");
|
window.open(normUrl(`/browse/${address}`), "_blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
$: fetch(`api/obj/${address}/LAST_VISITED`, {
|
$: putEntityAttribute(address, "LAST_VISITED", {
|
||||||
method: "PUT",
|
t: "Number",
|
||||||
headers: { "Content-Type": "application/json" },
|
c: new Date().getTime() / 1000,
|
||||||
body: JSON.stringify({ t: "Number", c: new Date().getTime() / 1000 }),
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import type { BrowseContext } from "../util/browse";
|
import type { BrowseContext } from "../util/browse";
|
||||||
import { useParams } from "svelte-navigator";
|
import { useParams } from "svelte-navigator";
|
||||||
import { GROUP_TYPE_ADDR } from "upend/constants";
|
import { GROUP_TYPE_ADDR } from "upend/constants";
|
||||||
|
import { deleteEntry, putEntityAttribute, putEntry } from "../lib/api";
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
|
||||||
export let address: string;
|
export let address: string;
|
||||||
|
@ -119,25 +120,17 @@
|
||||||
const change = ev.detail;
|
const change = ev.detail;
|
||||||
switch (change.type) {
|
switch (change.type) {
|
||||||
case "create":
|
case "create":
|
||||||
await fetch(`api/obj`, {
|
await putEntry({
|
||||||
method: "PUT",
|
entity: address,
|
||||||
headers: { "Content-Type": "application/json" },
|
attribute: change.attribute,
|
||||||
body: JSON.stringify({
|
value: change.value,
|
||||||
entity: address,
|
|
||||||
attribute: change.attribute,
|
|
||||||
value: change.value,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "delete":
|
case "delete":
|
||||||
await fetch(`api/obj/${change.address}`, { method: "DELETE" });
|
await deleteEntry(change.address);
|
||||||
break;
|
break;
|
||||||
case "update":
|
case "update":
|
||||||
await fetch(`api/obj/${address}/${change.attribute}`, {
|
await putEntityAttribute(address, change.attribute, change.value);
|
||||||
method: "PUT",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(change.value),
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error("Unimplemented AttributeChange", change);
|
console.error("Unimplemented AttributeChange", change);
|
||||||
|
@ -151,34 +144,30 @@
|
||||||
if (!groupToAdd) {
|
if (!groupToAdd) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await fetch(`api/obj`, {
|
await putEntry([
|
||||||
method: "PUT",
|
{
|
||||||
headers: { "Content-Type": "application/json" },
|
entity: String(groupToAdd.c),
|
||||||
body: JSON.stringify([
|
attribute: "HAS",
|
||||||
{
|
value: {
|
||||||
entity: String(groupToAdd.c),
|
t: "Address",
|
||||||
attribute: "HAS",
|
c: address,
|
||||||
value: {
|
|
||||||
t: "Address",
|
|
||||||
c: address,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
entity: String(groupToAdd.c),
|
{
|
||||||
attribute: "IS",
|
entity: String(groupToAdd.c),
|
||||||
value: {
|
attribute: "IS",
|
||||||
t: "Address",
|
value: {
|
||||||
c: GROUP_TYPE_ADDR,
|
t: "Address",
|
||||||
},
|
c: GROUP_TYPE_ADDR,
|
||||||
},
|
},
|
||||||
]),
|
},
|
||||||
});
|
]);
|
||||||
revalidate();
|
revalidate();
|
||||||
groupToAdd = undefined;
|
groupToAdd = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeGroup(address: string) {
|
async function removeGroup(address: string) {
|
||||||
await fetch(`api/obj/${address}`, { method: "DELETE" });
|
await deleteEntry(address);
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import Ellipsis from "../utils/Ellipsis.svelte";
|
import Ellipsis from "../utils/Ellipsis.svelte";
|
||||||
import UpLink from "./UpLink.svelte";
|
import UpLink from "./UpLink.svelte";
|
||||||
import { useEntity } from "../../lib/entity";
|
import { useEntity } from "../../lib/entity";
|
||||||
|
import { nativeOpen as nativeOpenApi } from "../../lib/api";
|
||||||
import { readable } from "svelte/store";
|
import { readable } from "svelte/store";
|
||||||
import { notify, UpNotification } from "../../notifications";
|
import { notify, UpNotification } from "../../notifications";
|
||||||
import IconButton from "../utils/IconButton.svelte";
|
import IconButton from "../utils/IconButton.svelte";
|
||||||
|
@ -49,7 +50,7 @@
|
||||||
} in a default native application...`
|
} in a default native application...`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
fetch(`api/raw/${address}?native=1`)
|
nativeOpenApi(address)
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`${response.statusText} - ${await response.text()}`);
|
throw new Error(`${response.statusText} - ${await response.text()}`);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PutResult } from "upend/types";
|
import type { IEntry } from "upend/types";
|
||||||
import { fetchEntity, useEntity } from "../../../lib/entity";
|
|
||||||
|
import { deleteEntry, fetchEntity, putEntry } from "../../../lib/api";
|
||||||
|
import { useEntity } from "../../../lib/entity";
|
||||||
import Spinner from "../../utils/Spinner.svelte";
|
import Spinner from "../../utils/Spinner.svelte";
|
||||||
import UpObject from "../UpObject.svelte";
|
import UpObject from "../UpObject.svelte";
|
||||||
|
|
||||||
|
@ -98,87 +100,71 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
anno.on("createAnnotation", async (annotation) => {
|
anno.on("createAnnotation", async (annotation) => {
|
||||||
const lensUuidFetch = await fetch("api/obj", {
|
const [_, uuid] = await putEntry({
|
||||||
method: "PUT",
|
entity: {
|
||||||
headers: { "Content-Type": "application/json" },
|
t: "Uuid",
|
||||||
body: JSON.stringify({
|
},
|
||||||
entity: {
|
|
||||||
t: "Uuid",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
const [_, uuid] = (await lensUuidFetch.json()) as PutResult;
|
|
||||||
await fetch("api/obj", {
|
await putEntry([
|
||||||
method: "PUT",
|
{
|
||||||
headers: { "Content-Type": "application/json" },
|
entity: uuid,
|
||||||
body: JSON.stringify([
|
attribute: "ANNOTATES",
|
||||||
{
|
value: {
|
||||||
entity: uuid,
|
t: "Address",
|
||||||
attribute: "ANNOTATES",
|
c: address,
|
||||||
value: {
|
|
||||||
t: "Address",
|
|
||||||
c: address,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
|
{
|
||||||
|
entity: uuid,
|
||||||
|
attribute: "W3C_FRAGMENT_SELECTOR",
|
||||||
|
value: {
|
||||||
|
t: "String",
|
||||||
|
c: annotation.target.selector.value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...annotation.body.map((body) => {
|
||||||
|
return {
|
||||||
entity: uuid,
|
entity: uuid,
|
||||||
attribute: "W3C_FRAGMENT_SELECTOR",
|
attribute: "LBL",
|
||||||
value: {
|
value: {
|
||||||
t: "String",
|
t: "String",
|
||||||
c: annotation.target.selector.value,
|
c: body.value,
|
||||||
},
|
},
|
||||||
},
|
} as IEntry;
|
||||||
...annotation.body.map((body) => {
|
}),
|
||||||
return {
|
]);
|
||||||
entity: uuid,
|
|
||||||
attribute: "LBL",
|
|
||||||
value: {
|
|
||||||
t: "String",
|
|
||||||
c: body.value,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
anno.on("updateAnnotation", async (annotation) => {
|
anno.on("updateAnnotation", async (annotation) => {
|
||||||
const annotationObject = await fetchEntity(annotation.id);
|
const annotationObject = await fetchEntity(annotation.id);
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
annotationObject.attr["LBL"]
|
annotationObject.attr["LBL"]
|
||||||
.concat(annotationObject.attr["W3C_FRAGMENT_SELECTOR"])
|
.concat(annotationObject.attr["W3C_FRAGMENT_SELECTOR"])
|
||||||
.map(async (e) => {
|
.map(async (e) => deleteEntry(e.address))
|
||||||
fetch(`api/obj/${e.address}`, { method: "DELETE" });
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
await fetch("api/obj", {
|
await putEntry([
|
||||||
method: "PUT",
|
{
|
||||||
headers: { "Content-Type": "application/json" },
|
entity: annotation.id,
|
||||||
body: JSON.stringify([
|
attribute: "W3C_FRAGMENT_SELECTOR",
|
||||||
{
|
value: {
|
||||||
|
t: "String",
|
||||||
|
c: annotation.target.selector.value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...annotation.body.map((body) => {
|
||||||
|
return {
|
||||||
entity: annotation.id,
|
entity: annotation.id,
|
||||||
attribute: "W3C_FRAGMENT_SELECTOR",
|
attribute: "LBL",
|
||||||
value: {
|
value: {
|
||||||
t: "String",
|
t: "String",
|
||||||
c: annotation.target.selector.value,
|
c: body.value,
|
||||||
},
|
},
|
||||||
},
|
} as IEntry;
|
||||||
...annotation.body.map((body) => {
|
}),
|
||||||
return {
|
]);
|
||||||
entity: annotation.id,
|
|
||||||
attribute: "LBL",
|
|
||||||
value: {
|
|
||||||
t: "String",
|
|
||||||
c: body.value,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
anno.on("deleteAnnotation", async (annotation) => {
|
anno.on("deleteAnnotation", async (annotation) => {
|
||||||
await fetch(`api/obj/${annotation.id}`, {
|
await deleteEntry(annotation.id);
|
||||||
method: "DELETE",
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { getRaw } from "../../../lib/api";
|
||||||
import IconButton from "../../utils/IconButton.svelte";
|
import IconButton from "../../utils/IconButton.svelte";
|
||||||
|
|
||||||
import Spinner from "../../utils/Spinner.svelte";
|
import Spinner from "../../utils/Spinner.svelte";
|
||||||
export let address: string;
|
export let address: string;
|
||||||
|
|
||||||
let mode: "preview" | "full" | "markdown" = "preview";
|
let mode: "preview" | "full" | "markdown" = "preview";
|
||||||
|
|
||||||
$: textContent = (async () => {
|
$: textContent = (async () => {
|
||||||
const response = await fetch(
|
const response = await getRaw(address, mode == "preview");
|
||||||
`api/${mode == "preview" ? "thumb" : "raw"}/${address}`
|
|
||||||
);
|
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
if (mode === "markdown") {
|
if (mode === "markdown") {
|
||||||
const { marked } = await import("marked");
|
const { marked } = await import("marked");
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Link, useLocation, useNavigate } from "svelte-navigator";
|
import { Link, useLocation, useNavigate } from "svelte-navigator";
|
||||||
import { useMatch } from "svelte-navigator";
|
import { useMatch } from "svelte-navigator";
|
||||||
|
import { refreshVault } from "../../lib/api";
|
||||||
import { addEmitter } from "../AddModal.svelte";
|
import { addEmitter } from "../AddModal.svelte";
|
||||||
import Icon from "../utils/Icon.svelte";
|
import Icon from "../utils/Icon.svelte";
|
||||||
import Input from "../utils/Input.svelte";
|
import Input from "../utils/Input.svelte";
|
||||||
|
@ -34,7 +35,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rescan() {
|
async function rescan() {
|
||||||
await fetch("api/refresh", { method: "POST" });
|
refreshVault();
|
||||||
jobsEmitter.emit("reload");
|
jobsEmitter.emit("reload");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -11,18 +11,18 @@
|
||||||
import type { IJob } from "upend/types";
|
import type { IJob } from "upend/types";
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
import ProgessBar from "../utils/ProgessBar.svelte";
|
import ProgessBar from "../utils/ProgessBar.svelte";
|
||||||
|
import { fetchJobs } from "../../lib/api";
|
||||||
|
|
||||||
interface JobWithId extends IJob {
|
interface JobWithId extends IJob {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let jobs: JobWithId[] = [];
|
let jobs: IJob[] = [];
|
||||||
let activeJobs: JobWithId[] = [];
|
let activeJobs: JobWithId[] = [];
|
||||||
let timeout: number;
|
let timeout: number;
|
||||||
async function updateJobs() {
|
async function updateJobs() {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
let request = await fetch("api/jobs");
|
jobs = await fetchJobs();
|
||||||
jobs = await request.json();
|
|
||||||
|
|
||||||
activeJobs = Object.entries(jobs)
|
activeJobs = Object.entries(jobs)
|
||||||
.filter(([_, job]) => job.state == "InProgress")
|
.filter(([_, job]) => job.state == "InProgress")
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { debounce } from "lodash";
|
import { debounce } from "lodash";
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
import type { IValue, VALUE_TYPE } from "upend/types";
|
import type { IValue, VALUE_TYPE } from "upend/types";
|
||||||
|
import { fetchAllAttributes } from "../../lib/api";
|
||||||
import {
|
import {
|
||||||
baseSearchOnce,
|
baseSearchOnce,
|
||||||
createLabelled,
|
createLabelled,
|
||||||
|
@ -53,8 +54,7 @@
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "attribute": {
|
case "attribute": {
|
||||||
const req = await fetch("api/all/attributes");
|
const allAttributes = await fetchAllAttributes();
|
||||||
const allAttributes: string[] = await req.json();
|
|
||||||
options = allAttributes
|
options = allAttributes
|
||||||
.filter((attr) => attr.toLowerCase().includes(query.toLowerCase()))
|
.filter((attr) => attr.toLowerCase().includes(query.toLowerCase()))
|
||||||
.map((attribute) => {
|
.map((attribute) => {
|
||||||
|
|
127
webui/src/lib/api.ts
Normal file
127
webui/src/lib/api.ts
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import LRU from "lru-cache";
|
||||||
|
import { UpListing, UpObject } from "upend";
|
||||||
|
import type {
|
||||||
|
Address,
|
||||||
|
IJob,
|
||||||
|
InEntry,
|
||||||
|
IValue,
|
||||||
|
ListingResult,
|
||||||
|
PutResult,
|
||||||
|
VaultInfo
|
||||||
|
} from "upend/types";
|
||||||
|
import type { EntityListing } from "./entity";
|
||||||
|
|
||||||
|
export async function fetchEntity(address: string): Promise<UpObject> {
|
||||||
|
const entityFetch = await fetch(`api/obj/${address}`);
|
||||||
|
const entityResult = (await entityFetch.json()) as EntityListing;
|
||||||
|
const entityListing = new UpListing(entityResult.entries);
|
||||||
|
return entityListing.getObject(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchEntry(address: string) {
|
||||||
|
const response = await fetch(`api/raw/${address}`);
|
||||||
|
const data = await response.json();
|
||||||
|
const listing = new UpListing({ address: data });
|
||||||
|
return listing.entries[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryOnceLRU = new LRU<string, UpListing>(128);
|
||||||
|
const inFlightRequests: { [key: string]: Promise<UpListing> } = {};
|
||||||
|
|
||||||
|
export async function queryOnce(query: string): Promise<UpListing> {
|
||||||
|
const cacheResult = queryOnceLRU.get(query);
|
||||||
|
if (!cacheResult) {
|
||||||
|
if (!inFlightRequests[query]) {
|
||||||
|
console.debug(`Querying: ${query}`);
|
||||||
|
inFlightRequests[query] = new Promise((resolve, reject) => {
|
||||||
|
fetch("api/query", { method: "POST", body: query, keepalive: true })
|
||||||
|
.then(async (response) => {
|
||||||
|
resolve(new UpListing(await response.json()));
|
||||||
|
})
|
||||||
|
.catch((err) => reject(err));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.debug(`Chaining request for ${query}...`);
|
||||||
|
}
|
||||||
|
return await inFlightRequests[query];
|
||||||
|
} else {
|
||||||
|
console.debug(`Returning cached: ${query}`);
|
||||||
|
return cacheResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function putEntry(entry: InEntry): Promise<PutResult> {
|
||||||
|
const response = await fetch(`api/obj`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(entry),
|
||||||
|
});
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function putEntityAttribute(
|
||||||
|
entity: Address,
|
||||||
|
attribute: string,
|
||||||
|
value: IValue
|
||||||
|
): Promise<Address> {
|
||||||
|
const response = await fetch(`api/obj/${entity}/${attribute}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(value),
|
||||||
|
});
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uploadFile(file: File): Promise<PutResult> {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
|
||||||
|
const response = await fetch("api/obj", {
|
||||||
|
method: "PUT",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw Error(await response.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteEntry(address: Address): Promise<void> {
|
||||||
|
await fetch(`api/obj/${address}`, { method: "DELETE" });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRaw(address: Address, preview = false) {
|
||||||
|
return await fetch(`api/${preview ? "thumb" : "raw"}/${address}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function refreshVault() {
|
||||||
|
return await fetch("api/refresh", { method: "POST" });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function nativeOpen(address: Address) {
|
||||||
|
return fetch(`api/raw/${address}?native=1`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchRoots(): Promise<ListingResult> {
|
||||||
|
const response = await fetch("api/hier_roots");
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchJobs(): Promise<IJob[]> {
|
||||||
|
const response = await fetch("api/jobs");
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAllAttributes(): Promise<string[]> {
|
||||||
|
const response = await fetch("api/all/attributes");
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchInfo(): Promise<VaultInfo> {
|
||||||
|
const response = await fetch("api/info");
|
||||||
|
return await response.json();
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
// import { useSWR } from "sswr";
|
// import { useSWR } from "sswr";
|
||||||
import LRU from "lru-cache";
|
|
||||||
import { derived, Readable } from "svelte/store";
|
import { derived, Readable } from "svelte/store";
|
||||||
import { UpListing, UpObject } from "upend";
|
import { UpListing, UpObject } from "upend";
|
||||||
import type { ListingResult } from "upend/types";
|
import type { ListingResult } from "upend/types";
|
||||||
|
@ -19,9 +18,6 @@ export interface EntityListing {
|
||||||
entries: ListingResult;
|
entries: ListingResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryOnceLRU = new LRU<string, UpListing>(128);
|
|
||||||
const inFlightRequests: { [key: string]: Promise<UpListing> } = {};
|
|
||||||
|
|
||||||
export function useEntity(address: string) {
|
export function useEntity(address: string) {
|
||||||
const { data, error, revalidate } = useSWR<EntityListing, unknown>(
|
const { data, error, revalidate } = useSWR<EntityListing, unknown>(
|
||||||
`api/obj/${address}`
|
`api/obj/${address}`
|
||||||
|
@ -51,20 +47,6 @@ export function useEntity(address: string) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchEntity(address: string): Promise<UpObject> {
|
|
||||||
const entityFetch = await fetch(`api/obj/${address}`);
|
|
||||||
const entityResult = (await entityFetch.json()) as EntityListing;
|
|
||||||
const entityListing = new UpListing(entityResult.entries);
|
|
||||||
return entityListing.getObject(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchEntry(address: string) {
|
|
||||||
const response = await fetch(`api/raw/${address}`);
|
|
||||||
const data = await response.json();
|
|
||||||
const listing = new UpListing({ address: data });
|
|
||||||
return listing.entries[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function query(query: string) {
|
export function query(query: string) {
|
||||||
console.debug(`Querying: ${query}`);
|
console.debug(`Querying: ${query}`);
|
||||||
const { data, error, revalidate } = useSWR<ListingResult, unknown>(
|
const { data, error, revalidate } = useSWR<ListingResult, unknown>(
|
||||||
|
@ -82,25 +64,3 @@ export function query(query: string) {
|
||||||
revalidate,
|
revalidate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function queryOnce(query: string): Promise<UpListing> {
|
|
||||||
const cacheResult = queryOnceLRU.get(query);
|
|
||||||
if (!cacheResult) {
|
|
||||||
if (!inFlightRequests[query]) {
|
|
||||||
console.debug(`Querying: ${query}`);
|
|
||||||
inFlightRequests[query] = new Promise((resolve, reject) => {
|
|
||||||
fetch("api/query", { method: "POST", body: query, keepalive: true })
|
|
||||||
.then(async (response) => {
|
|
||||||
resolve(new UpListing(await response.json()));
|
|
||||||
})
|
|
||||||
.catch((err) => reject(err));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.debug(`Chaining request for ${query}...`);
|
|
||||||
}
|
|
||||||
return await inFlightRequests[query];
|
|
||||||
} else {
|
|
||||||
console.debug(`Returning cached: ${query}`);
|
|
||||||
return cacheResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { readable, Readable } from "svelte/store";
|
import { readable, Readable } from "svelte/store";
|
||||||
import type { VaultInfo } from "upend/types";
|
import type { VaultInfo } from "upend/types";
|
||||||
|
import { fetchInfo } from "../lib/api";
|
||||||
|
|
||||||
export const vaultInfo: Readable<VaultInfo> = readable(undefined, (set) => {
|
export const vaultInfo: Readable<VaultInfo> = readable(undefined, (set) => {
|
||||||
fetch("api/info").then(async (response) => {
|
fetchInfo().then(async (info) => {
|
||||||
set(await response.json());
|
set(info);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { UpEntry } from "upend";
|
import type { UpEntry } from "upend";
|
||||||
import type { PutResult } from "upend/types";
|
import type { InEntry } from "upend/types";
|
||||||
import { query as queryFn, queryOnce } from "../lib/entity";
|
import { putEntry, queryOnce } from "../lib/api";
|
||||||
|
import { query as queryFn } from "../lib/entity";
|
||||||
|
|
||||||
export function baseSearch(query: string) {
|
export function baseSearch(query: string) {
|
||||||
return queryFn(
|
return queryFn(
|
||||||
|
@ -25,7 +26,7 @@ export async function getObjects(
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createLabelled(label: string) {
|
export async function createLabelled(label: string) {
|
||||||
let body: unknown;
|
let body: InEntry;
|
||||||
if (label.match("^[\\w]+://[\\w]")) {
|
if (label.match("^[\\w]+://[\\w]")) {
|
||||||
body = {
|
body = {
|
||||||
entity: {
|
entity: {
|
||||||
|
@ -43,16 +44,10 @@ export async function createLabelled(label: string) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`api/obj`, {
|
try {
|
||||||
method: "PUT",
|
const [_, entry] = await putEntry(body);
|
||||||
headers: { "Content-Type": "application/json" },
|
return entry;
|
||||||
body: JSON.stringify(body),
|
} catch (error) {
|
||||||
});
|
throw new Error(`Failed to create object: ${error}`);
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Failed to create object: ${await response.text()}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [_, entry] = (await response.json()) as PutResult;
|
|
||||||
return entry;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import _ from "lodash";
|
|
||||||
|
|
||||||
import { UpListing } from "upend";
|
import { UpListing } from "upend";
|
||||||
import type { ListingResult } from "upend/types";
|
|
||||||
import AttributeView from "../components/AttributeView.svelte";
|
import AttributeView from "../components/AttributeView.svelte";
|
||||||
import UpObjectCard from "../components/display/UpObjectCard.svelte";
|
import UpObjectCard from "../components/display/UpObjectCard.svelte";
|
||||||
import Spinner from "../components/utils/Spinner.svelte";
|
import Spinner from "../components/utils/Spinner.svelte";
|
||||||
|
import { fetchRoots } from "../lib/api";
|
||||||
import { query } from "../lib/entity";
|
import { query } from "../lib/entity";
|
||||||
import { UpType } from "../lib/types";
|
import { UpType } from "../lib/types";
|
||||||
import { vaultInfo } from "../util/info";
|
import { vaultInfo } from "../util/info";
|
||||||
import { updateTitle } from "../util/title";
|
import { updateTitle } from "../util/title";
|
||||||
|
|
||||||
const roots = (async () => {
|
const roots = (async () => {
|
||||||
const response = await fetch("api/hier_roots");
|
const data = await fetchRoots();
|
||||||
const data = (await response.json()) as ListingResult;
|
|
||||||
const listing = new UpListing(data);
|
const listing = new UpListing(data);
|
||||||
return Object.values(listing.objects)
|
return Object.values(listing.objects)
|
||||||
.filter((obj) => Boolean(obj.attr["LBL"]))
|
.filter((obj) => Boolean(obj.attr["LBL"]))
|
||||||
|
|
|
@ -4669,8 +4669,8 @@ __metadata:
|
||||||
|
|
||||||
"upend@file:../tools/upend_js::locator=svelte-app%40workspace%3A.":
|
"upend@file:../tools/upend_js::locator=svelte-app%40workspace%3A.":
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
resolution: "upend@file:../tools/upend_js#../tools/upend_js::hash=fd602e&locator=svelte-app%40workspace%3A."
|
resolution: "upend@file:../tools/upend_js#../tools/upend_js::hash=d7ca1d&locator=svelte-app%40workspace%3A."
|
||||||
checksum: 14cc9cfb6f04a85ec4715b3bbb6b5636b898ccb1ac2418cae9d5e8c00ec25f6245e5be1f8f831c8f24aa3cc0ecb97942c1a569a2fdee104383beb173b40daf0d
|
checksum: 50dc93a08980c68982a46c1c7f59bac5d5e000f2d88a4b8167b4353bafc99c3706375775e78f9fc685a8e03e51cc6c3403714ce4c57ebe5254776dba1ff584b7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue