Files
start-os/sdk/base/lib/util/graph.ts
Aiden McClelland 377b7b12ce update/alpha.9 (#2988)
* import marketplac preview for sideload

* fix: improve state service (#2977)

* fix: fix sideload DI

* fix: update Angular

* fix: cleanup

* fix: fix version selection

* Bump node version to fix build for Angular

* misc fixes
- update node to v22
- fix chroot-and-upgrade access to prune-images
- don't self-migrate legacy packages
- #2985
- move dataVersion to volume folder
- remove "instructions.md" from s9pk
- add "docsUrl" to manifest

* version bump

* include flavor when clicking view listing from updates tab

* closes #2980

* fix: fix select button

* bring back ssh keys

* fix: drop 'portal' from all routes

* fix: implement longtap action to select table rows

* fix description for ssh page

* replace instructions with docsLink and refactor marketplace preview

* delete unused translations

* fix patchdb diffing algorithm

* continue refactor of marketplace lib show components

* Booting StartOS instead of Setting up your server on init

* misc fixes
- closes #2990
- closes #2987

* fix build

* docsUrl and clickable service headers

* don't cleanup after update until new service install succeeds

* update types

* misc fixes

* beta.35

* sdkversion, githash for sideload, correct logs for init, startos pubkey display

* bring back reboot button on install

* misc fixes

* beta.36

* better handling of setup and init for websocket errors

* reopen init and setup logs even on graceful closure

* better logging, misc fixes

* fix build

* dont let package stats hang

* dont show docsurl in marketplace if no docsurl

* re-add needs-config

* show error if init fails, shorten hover state on header icons

* fix operator precedemce

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
Co-authored-by: Alex Inkin <alexander@inkin.ru>
Co-authored-by: Mariusz Kogen <k0gen@pm.me>
2025-07-18 18:31:12 +00:00

269 lines
6.9 KiB
TypeScript

import { boolean } from "ts-matches"
import { ExtendedVersion } from "../exver"
export type Vertex<VMetadata = null, EMetadata = null> = {
metadata: VMetadata
edges: Array<Edge<EMetadata, VMetadata>>
}
export type Edge<EMetadata = null, VMetadata = null> = {
metadata: EMetadata
from: Vertex<VMetadata, EMetadata>
to: Vertex<VMetadata, EMetadata>
}
export class Graph<VMetadata = null, EMetadata = null> {
private readonly vertices: Array<Vertex<VMetadata, EMetadata>> = []
constructor() {}
dump(
metadataRepr: (metadata: VMetadata | EMetadata) => any = (a) => a,
): string {
const seen = new WeakSet()
return JSON.stringify(
this.vertices,
(k, v) => {
if (k === "metadata") return metadataRepr(v)
if (k === "from") return metadataRepr(v.metadata)
if (k === "to") return metadataRepr(v.metadata)
return v
},
2,
)
}
addVertex(
metadata: VMetadata,
fromEdges: Array<Omit<Edge<EMetadata, VMetadata>, "to">>,
toEdges: Array<Omit<Edge<EMetadata, VMetadata>, "from">>,
): Vertex<VMetadata, EMetadata> {
const vertex: Vertex<VMetadata, EMetadata> = {
metadata,
edges: [],
}
for (let edge of fromEdges) {
const vEdge = {
metadata: edge.metadata,
from: edge.from,
to: vertex,
}
edge.from.edges.push(vEdge)
vertex.edges.push(vEdge)
}
for (let edge of toEdges) {
const vEdge = {
metadata: edge.metadata,
from: vertex,
to: edge.to,
}
edge.to.edges.push(vEdge)
vertex.edges.push(vEdge)
}
this.vertices.push(vertex)
return vertex
}
findVertex(
predicate: (vertex: Vertex<VMetadata, EMetadata>) => boolean,
): Generator<Vertex<VMetadata, EMetadata>, null> {
const veritces = this.vertices
function* gen() {
for (let vertex of veritces) {
if (predicate(vertex)) {
yield vertex
}
}
return null
}
return gen()
}
addEdge(
metadata: EMetadata,
from: Vertex<VMetadata, EMetadata>,
to: Vertex<VMetadata, EMetadata>,
): Edge<EMetadata, VMetadata> {
const edge = {
metadata,
from,
to,
}
edge.from.edges.push(edge)
edge.to.edges.push(edge)
return edge
}
breadthFirstSearch(
from:
| Vertex<VMetadata, EMetadata>
| ((vertex: Vertex<VMetadata, EMetadata>) => boolean),
): Generator<Vertex<VMetadata, EMetadata>, null> {
const visited: Array<Vertex<VMetadata, EMetadata>> = []
function* rec(
vertex: Vertex<VMetadata, EMetadata>,
): Generator<Vertex<VMetadata, EMetadata>, null> {
if (visited.includes(vertex)) {
return null
}
visited.push(vertex)
yield vertex
let generators = vertex.edges
.filter((e) => e.from === vertex)
.map((e) => rec(e.to))
while (generators.length) {
let prev = generators
generators = []
for (let gen of prev) {
const next = gen.next()
if (!next.done) {
generators.push(gen)
yield next.value
}
}
}
return null
}
if (from instanceof Function) {
let generators = this.vertices.filter(from).map(rec)
return (function* () {
while (generators.length) {
let prev = generators
generators = []
for (let gen of prev) {
const next = gen.next()
if (!next.done) {
generators.push(gen)
yield next.value
}
}
}
return null
})()
} else {
return rec(from)
}
}
reverseBreadthFirstSearch(
to:
| Vertex<VMetadata, EMetadata>
| ((vertex: Vertex<VMetadata, EMetadata>) => boolean),
): Generator<Vertex<VMetadata, EMetadata>, null> {
const visited: Array<Vertex<VMetadata, EMetadata>> = []
function* rec(
vertex: Vertex<VMetadata, EMetadata>,
): Generator<Vertex<VMetadata, EMetadata>, null> {
if (visited.includes(vertex)) {
return null
}
visited.push(vertex)
yield vertex
let generators = vertex.edges
.filter((e) => e.to === vertex)
.map((e) => rec(e.from))
while (generators.length) {
let prev = generators
generators = []
for (let gen of prev) {
const next = gen.next()
if (!next.done) {
generators.push(gen)
yield next.value
}
}
}
return null
}
if (to instanceof Function) {
let generators = this.vertices.filter(to).map(rec)
return (function* () {
while (generators.length) {
let prev = generators
generators = []
for (let gen of prev) {
const next = gen.next()
if (!next.done) {
generators.push(gen)
yield next.value
}
}
}
return null
})()
} else {
return rec(to)
}
}
shortestPath(
from:
| Vertex<VMetadata, EMetadata>
| ((vertex: Vertex<VMetadata, EMetadata>) => boolean),
to:
| Vertex<VMetadata, EMetadata>
| ((vertex: Vertex<VMetadata, EMetadata>) => boolean),
): Array<Edge<EMetadata, VMetadata>> | null {
const isDone =
to instanceof Function
? to
: (v: Vertex<VMetadata, EMetadata>) => v === to
const path: Array<Edge<EMetadata, VMetadata>> = []
const visited: Array<Vertex<VMetadata, EMetadata>> = []
function* check(
vertex: Vertex<VMetadata, EMetadata>,
path: Array<Edge<EMetadata, VMetadata>>,
): Generator<undefined, Array<Edge<EMetadata, VMetadata>> | null> {
if (isDone(vertex)) {
return path
}
if (visited.includes(vertex)) {
return null
}
visited.push(vertex)
yield
let generators = vertex.edges
.filter((e) => e.from === vertex)
.map((e) => check(e.to, [...path, e]))
while (generators.length) {
let prev = generators
generators = []
for (let gen of prev) {
const next = gen.next()
if (next.done === true) {
if (next.value) {
return next.value
}
} else {
generators.push(gen)
yield
}
}
}
return null
}
if (from instanceof Function) {
let generators = this.vertices.filter(from).map((v) => check(v, []))
while (generators.length) {
let prev = generators
generators = []
for (let gen of prev) {
const next = gen.next()
if (next.done === true) {
if (next.value) {
return next.value
}
} else {
generators.push(gen)
}
}
}
} else {
const gen = check(from, [])
while (true) {
const next = gen.next()
if (next.done) {
return next.value
}
}
}
return null
}
}