mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
add map & eq to getServiceInterface
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
import { object } from "ts-matches"
|
import { object } from "ts-matches"
|
||||||
|
|
||||||
export function deepEqual(...args: unknown[]) {
|
export function deepEqual(...args: unknown[]) {
|
||||||
if (!object.test(args[args.length - 1])) return args[args.length - 1]
|
|
||||||
const objects = args.filter(object.test)
|
const objects = args.filter(object.test)
|
||||||
if (objects.length === 0) {
|
if (objects.length === 0) {
|
||||||
for (const x of args) if (x !== args[0]) return false
|
for (const x of args) if (x !== args[0]) return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (objects.length !== args.length) return false
|
if (objects.length !== args.length) return false
|
||||||
|
if (objects.some(Array.isArray) && !objects.every(Array.isArray)) return false
|
||||||
const allKeys = new Set(objects.flatMap((x) => Object.keys(x)))
|
const allKeys = new Set(objects.flatMap((x) => Object.keys(x)))
|
||||||
for (const key of allKeys) {
|
for (const key of allKeys) {
|
||||||
for (const x of objects) {
|
for (const x of objects) {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { ServiceInterfaceType } from "../types"
|
import { PackageId, ServiceInterfaceId, ServiceInterfaceType } from "../types"
|
||||||
import { knownProtocols } from "../interfaces/Host"
|
import { knownProtocols } from "../interfaces/Host"
|
||||||
import { AddressInfo, Host, Hostname, HostnameInfo } from "../types"
|
import { AddressInfo, Host, Hostname, HostnameInfo } from "../types"
|
||||||
import { Effects } from "../Effects"
|
import { Effects } from "../Effects"
|
||||||
import { DropGenerator, DropPromise } from "./Drop"
|
import { DropGenerator, DropPromise } from "./Drop"
|
||||||
import { IpAddress, IPV6_LINK_LOCAL } from "./ip"
|
import { IpAddress, IPV6_LINK_LOCAL } from "./ip"
|
||||||
|
import { deepEqual } from "./deepEqual"
|
||||||
|
|
||||||
export type UrlString = string
|
export type UrlString = string
|
||||||
export type HostId = string
|
export type HostId = string
|
||||||
@@ -227,7 +228,7 @@ function filterRec(
|
|||||||
(kind.has("ipv4") && h.kind === "ip" && h.hostname.kind === "ipv4") ||
|
(kind.has("ipv4") && h.kind === "ip" && h.hostname.kind === "ipv4") ||
|
||||||
(kind.has("ipv6") && h.kind === "ip" && h.hostname.kind === "ipv6") ||
|
(kind.has("ipv6") && h.kind === "ip" && h.hostname.kind === "ipv6") ||
|
||||||
(kind.has("localhost") &&
|
(kind.has("localhost") &&
|
||||||
["localhost", "127.0.0.1", "[::1]"].includes(h.hostname.value)) ||
|
["localhost", "127.0.0.1", "::1"].includes(h.hostname.value)) ||
|
||||||
(kind.has("link-local") &&
|
(kind.has("link-local") &&
|
||||||
h.kind === "ip" &&
|
h.kind === "ip" &&
|
||||||
h.hostname.kind === "ipv6" &&
|
h.hostname.kind === "ipv6" &&
|
||||||
@@ -328,28 +329,28 @@ const makeInterfaceFilled = async ({
|
|||||||
return interfaceFilled
|
return interfaceFilled
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetServiceInterface {
|
export class GetServiceInterface<Mapped = ServiceInterfaceFilled | null> {
|
||||||
constructor(
|
constructor(
|
||||||
readonly effects: Effects,
|
readonly effects: Effects,
|
||||||
readonly opts: { id: string; packageId?: string },
|
readonly opts: { id: string; packageId?: string },
|
||||||
|
readonly map: (interfaces: ServiceInterfaceFilled | null) => Mapped,
|
||||||
|
readonly eq: (a: Mapped, b: Mapped) => boolean,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the requested service interface. Reruns the context from which it has been called if the underlying value changes
|
* Returns the requested service interface. Reruns the context from which it has been called if the underlying value changes
|
||||||
*/
|
*/
|
||||||
async const() {
|
async const() {
|
||||||
const { id, packageId } = this.opts
|
let abort = new AbortController()
|
||||||
const callback =
|
const watch = this.watch(abort.signal)
|
||||||
this.effects.constRetry &&
|
const res = await watch.next()
|
||||||
(() => this.effects.constRetry && this.effects.constRetry())
|
if (this.effects.constRetry) {
|
||||||
const interfaceFilled = await makeInterfaceFilled({
|
watch.next().then(() => {
|
||||||
effects: this.effects,
|
abort.abort()
|
||||||
id,
|
this.effects.constRetry && this.effects.constRetry()
|
||||||
packageId,
|
})
|
||||||
callback,
|
}
|
||||||
})
|
return res.value
|
||||||
|
|
||||||
return interfaceFilled
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns the requested service interface. Does nothing if the value changes
|
* Returns the requested service interface. Does nothing if the value changes
|
||||||
@@ -362,10 +363,11 @@ export class GetServiceInterface {
|
|||||||
packageId,
|
packageId,
|
||||||
})
|
})
|
||||||
|
|
||||||
return interfaceFilled
|
return this.map(interfaceFilled)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async *watchGen(abort?: AbortSignal) {
|
private async *watchGen(abort?: AbortSignal) {
|
||||||
|
let prev = null as { value: Mapped } | null
|
||||||
const { id, packageId } = this.opts
|
const { id, packageId } = this.opts
|
||||||
const resolveCell = { resolve: () => {} }
|
const resolveCell = { resolve: () => {} }
|
||||||
this.effects.onLeaveContext(() => {
|
this.effects.onLeaveContext(() => {
|
||||||
@@ -378,12 +380,17 @@ export class GetServiceInterface {
|
|||||||
callback = resolve
|
callback = resolve
|
||||||
resolveCell.resolve = resolve
|
resolveCell.resolve = resolve
|
||||||
})
|
})
|
||||||
yield await makeInterfaceFilled({
|
const next = this.map(
|
||||||
effects: this.effects,
|
await makeInterfaceFilled({
|
||||||
id,
|
effects: this.effects,
|
||||||
packageId,
|
id,
|
||||||
callback,
|
packageId,
|
||||||
})
|
callback,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if (!prev || !this.eq(prev.value, next)) {
|
||||||
|
yield next
|
||||||
|
}
|
||||||
await waitForNext
|
await waitForNext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,9 +398,7 @@ export class GetServiceInterface {
|
|||||||
/**
|
/**
|
||||||
* Watches the requested service interface. Returns an async iterator that yields whenever the value changes
|
* Watches the requested service interface. Returns an async iterator that yields whenever the value changes
|
||||||
*/
|
*/
|
||||||
watch(
|
watch(abort?: AbortSignal): AsyncGenerator<Mapped, void, unknown> {
|
||||||
abort?: AbortSignal,
|
|
||||||
): AsyncGenerator<ServiceInterfaceFilled | null, void, unknown> {
|
|
||||||
const ctrl = new AbortController()
|
const ctrl = new AbortController()
|
||||||
abort?.addEventListener("abort", () => ctrl.abort())
|
abort?.addEventListener("abort", () => ctrl.abort())
|
||||||
return DropGenerator.of(this.watchGen(ctrl.signal), () => ctrl.abort())
|
return DropGenerator.of(this.watchGen(ctrl.signal), () => ctrl.abort())
|
||||||
@@ -404,7 +409,7 @@ export class GetServiceInterface {
|
|||||||
*/
|
*/
|
||||||
onChange(
|
onChange(
|
||||||
callback: (
|
callback: (
|
||||||
value: ServiceInterfaceFilled | null,
|
value: Mapped | null,
|
||||||
error?: Error,
|
error?: Error,
|
||||||
) => { cancel: boolean } | Promise<{ cancel: boolean }>,
|
) => { cancel: boolean } | Promise<{ cancel: boolean }>,
|
||||||
) {
|
) {
|
||||||
@@ -437,9 +442,7 @@ export class GetServiceInterface {
|
|||||||
/**
|
/**
|
||||||
* Watches the requested service interface. Returns when the predicate is true
|
* Watches the requested service interface. Returns when the predicate is true
|
||||||
*/
|
*/
|
||||||
waitFor(
|
waitFor(pred: (value: Mapped) => boolean): Promise<Mapped> {
|
||||||
pred: (value: ServiceInterfaceFilled | null) => boolean,
|
|
||||||
): Promise<ServiceInterfaceFilled | null> {
|
|
||||||
const ctrl = new AbortController()
|
const ctrl = new AbortController()
|
||||||
return DropPromise.of(
|
return DropPromise.of(
|
||||||
Promise.resolve().then(async () => {
|
Promise.resolve().then(async () => {
|
||||||
@@ -448,15 +451,57 @@ export class GetServiceInterface {
|
|||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
throw new Error("context left before predicate passed")
|
||||||
}),
|
}),
|
||||||
() => ctrl.abort(),
|
() => ctrl.abort(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getOwnServiceInterface(
|
||||||
|
effects: Effects,
|
||||||
|
id: ServiceInterfaceId,
|
||||||
|
): GetServiceInterface
|
||||||
|
export function getOwnServiceInterface<Mapped>(
|
||||||
|
effects: Effects,
|
||||||
|
id: ServiceInterfaceId,
|
||||||
|
map: (interfaces: ServiceInterfaceFilled | null) => Mapped,
|
||||||
|
eq?: (a: Mapped, b: Mapped) => boolean,
|
||||||
|
): GetServiceInterface<Mapped>
|
||||||
|
export function getOwnServiceInterface<Mapped>(
|
||||||
|
effects: Effects,
|
||||||
|
id: ServiceInterfaceId,
|
||||||
|
map?: (interfaces: ServiceInterfaceFilled | null) => Mapped,
|
||||||
|
eq?: (a: Mapped, b: Mapped) => boolean,
|
||||||
|
): GetServiceInterface<Mapped> {
|
||||||
|
return new GetServiceInterface(
|
||||||
|
effects,
|
||||||
|
{ id },
|
||||||
|
map ?? ((a) => a as Mapped),
|
||||||
|
eq ?? ((a, b) => deepEqual(a, b)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function getServiceInterface(
|
export function getServiceInterface(
|
||||||
effects: Effects,
|
effects: Effects,
|
||||||
opts: { id: string; packageId?: string },
|
opts: { id: ServiceInterfaceId; packageId: PackageId },
|
||||||
) {
|
): GetServiceInterface
|
||||||
return new GetServiceInterface(effects, opts)
|
export function getServiceInterface<Mapped>(
|
||||||
|
effects: Effects,
|
||||||
|
opts: { id: ServiceInterfaceId; packageId: PackageId },
|
||||||
|
map: (interfaces: ServiceInterfaceFilled | null) => Mapped,
|
||||||
|
eq?: (a: Mapped, b: Mapped) => boolean,
|
||||||
|
): GetServiceInterface<Mapped>
|
||||||
|
export function getServiceInterface<Mapped>(
|
||||||
|
effects: Effects,
|
||||||
|
opts: { id: ServiceInterfaceId; packageId: PackageId },
|
||||||
|
map?: (interfaces: ServiceInterfaceFilled | null) => Mapped,
|
||||||
|
eq?: (a: Mapped, b: Mapped) => boolean,
|
||||||
|
): GetServiceInterface<Mapped> {
|
||||||
|
return new GetServiceInterface(
|
||||||
|
effects,
|
||||||
|
opts,
|
||||||
|
map ?? ((a) => a as Mapped),
|
||||||
|
eq ?? ((a, b) => deepEqual(a, b)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { Effects } from "../Effects"
|
import { Effects } from "../Effects"
|
||||||
|
import { PackageId } from "../osBindings"
|
||||||
|
import { deepEqual } from "./deepEqual"
|
||||||
import { DropGenerator, DropPromise } from "./Drop"
|
import { DropGenerator, DropPromise } from "./Drop"
|
||||||
import {
|
import {
|
||||||
ServiceInterfaceFilled,
|
ServiceInterfaceFilled,
|
||||||
@@ -41,28 +43,28 @@ const makeManyInterfaceFilled = async ({
|
|||||||
return serviceInterfacesFilled
|
return serviceInterfacesFilled
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetServiceInterfaces {
|
export class GetServiceInterfaces<Mapped = ServiceInterfaceFilled[]> {
|
||||||
constructor(
|
constructor(
|
||||||
readonly effects: Effects,
|
readonly effects: Effects,
|
||||||
readonly opts: { packageId?: string },
|
readonly opts: { packageId?: string },
|
||||||
|
readonly map: (interfaces: ServiceInterfaceFilled[]) => Mapped,
|
||||||
|
readonly eq: (a: Mapped, b: Mapped) => boolean,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the service interfaces for the package. Reruns the context from which it has been called if the underlying value changes
|
* Returns the service interfaces for the package. Reruns the context from which it has been called if the underlying value changes
|
||||||
*/
|
*/
|
||||||
async const() {
|
async const() {
|
||||||
const { packageId } = this.opts
|
let abort = new AbortController()
|
||||||
const callback =
|
const watch = this.watch(abort.signal)
|
||||||
this.effects.constRetry &&
|
const res = await watch.next()
|
||||||
(() => this.effects.constRetry && this.effects.constRetry())
|
if (this.effects.constRetry) {
|
||||||
const interfaceFilled: ServiceInterfaceFilled[] =
|
watch.next().then(() => {
|
||||||
await makeManyInterfaceFilled({
|
abort.abort()
|
||||||
effects: this.effects,
|
this.effects.constRetry && this.effects.constRetry()
|
||||||
packageId,
|
|
||||||
callback,
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
return interfaceFilled
|
return res.value
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns the service interfaces for the package. Does nothing if the value changes
|
* Returns the service interfaces for the package. Does nothing if the value changes
|
||||||
@@ -75,10 +77,11 @@ export class GetServiceInterfaces {
|
|||||||
packageId,
|
packageId,
|
||||||
})
|
})
|
||||||
|
|
||||||
return interfaceFilled
|
return this.map(interfaceFilled)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async *watchGen(abort?: AbortSignal) {
|
private async *watchGen(abort?: AbortSignal) {
|
||||||
|
let prev = null as { value: Mapped } | null
|
||||||
const { packageId } = this.opts
|
const { packageId } = this.opts
|
||||||
const resolveCell = { resolve: () => {} }
|
const resolveCell = { resolve: () => {} }
|
||||||
this.effects.onLeaveContext(() => {
|
this.effects.onLeaveContext(() => {
|
||||||
@@ -91,11 +94,16 @@ export class GetServiceInterfaces {
|
|||||||
callback = resolve
|
callback = resolve
|
||||||
resolveCell.resolve = resolve
|
resolveCell.resolve = resolve
|
||||||
})
|
})
|
||||||
yield await makeManyInterfaceFilled({
|
const next = this.map(
|
||||||
effects: this.effects,
|
await makeManyInterfaceFilled({
|
||||||
packageId,
|
effects: this.effects,
|
||||||
callback,
|
packageId,
|
||||||
})
|
callback,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if (!prev || !this.eq(prev.value, next)) {
|
||||||
|
yield next
|
||||||
|
}
|
||||||
await waitForNext
|
await waitForNext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,9 +111,7 @@ export class GetServiceInterfaces {
|
|||||||
/**
|
/**
|
||||||
* Watches the service interfaces for the package. Returns an async iterator that yields whenever the value changes
|
* Watches the service interfaces for the package. Returns an async iterator that yields whenever the value changes
|
||||||
*/
|
*/
|
||||||
watch(
|
watch(abort?: AbortSignal): AsyncGenerator<Mapped, void, unknown> {
|
||||||
abort?: AbortSignal,
|
|
||||||
): AsyncGenerator<ServiceInterfaceFilled[], void, unknown> {
|
|
||||||
const ctrl = new AbortController()
|
const ctrl = new AbortController()
|
||||||
abort?.addEventListener("abort", () => ctrl.abort())
|
abort?.addEventListener("abort", () => ctrl.abort())
|
||||||
return DropGenerator.of(this.watchGen(ctrl.signal), () => ctrl.abort())
|
return DropGenerator.of(this.watchGen(ctrl.signal), () => ctrl.abort())
|
||||||
@@ -116,7 +122,7 @@ export class GetServiceInterfaces {
|
|||||||
*/
|
*/
|
||||||
onChange(
|
onChange(
|
||||||
callback: (
|
callback: (
|
||||||
value: ServiceInterfaceFilled[] | null,
|
value: Mapped | null,
|
||||||
error?: Error,
|
error?: Error,
|
||||||
) => { cancel: boolean } | Promise<{ cancel: boolean }>,
|
) => { cancel: boolean } | Promise<{ cancel: boolean }>,
|
||||||
) {
|
) {
|
||||||
@@ -149,9 +155,7 @@ export class GetServiceInterfaces {
|
|||||||
/**
|
/**
|
||||||
* Watches the service interfaces for the package. Returns when the predicate is true
|
* Watches the service interfaces for the package. Returns when the predicate is true
|
||||||
*/
|
*/
|
||||||
waitFor(
|
waitFor(pred: (value: Mapped) => boolean): Promise<Mapped> {
|
||||||
pred: (value: ServiceInterfaceFilled[] | null) => boolean,
|
|
||||||
): Promise<ServiceInterfaceFilled[] | null> {
|
|
||||||
const ctrl = new AbortController()
|
const ctrl = new AbortController()
|
||||||
return DropPromise.of(
|
return DropPromise.of(
|
||||||
Promise.resolve().then(async () => {
|
Promise.resolve().then(async () => {
|
||||||
@@ -160,15 +164,52 @@ export class GetServiceInterfaces {
|
|||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
throw new Error("context left before predicate passed")
|
||||||
}),
|
}),
|
||||||
() => ctrl.abort(),
|
() => ctrl.abort(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getOwnServiceInterfaces(effects: Effects): GetServiceInterfaces
|
||||||
|
export function getOwnServiceInterfaces<Mapped>(
|
||||||
|
effects: Effects,
|
||||||
|
map: (interfaces: ServiceInterfaceFilled[]) => Mapped,
|
||||||
|
eq?: (a: Mapped, b: Mapped) => boolean,
|
||||||
|
): GetServiceInterfaces<Mapped>
|
||||||
|
export function getOwnServiceInterfaces<Mapped>(
|
||||||
|
effects: Effects,
|
||||||
|
map?: (interfaces: ServiceInterfaceFilled[]) => Mapped,
|
||||||
|
eq?: (a: Mapped, b: Mapped) => boolean,
|
||||||
|
): GetServiceInterfaces<Mapped> {
|
||||||
|
return new GetServiceInterfaces(
|
||||||
|
effects,
|
||||||
|
{},
|
||||||
|
map ?? ((a) => a as Mapped),
|
||||||
|
eq ?? ((a, b) => deepEqual(a, b)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function getServiceInterfaces(
|
export function getServiceInterfaces(
|
||||||
effects: Effects,
|
effects: Effects,
|
||||||
opts: { packageId?: string },
|
opts: { packageId: PackageId },
|
||||||
) {
|
): GetServiceInterfaces
|
||||||
return new GetServiceInterfaces(effects, opts)
|
export function getServiceInterfaces<Mapped>(
|
||||||
|
effects: Effects,
|
||||||
|
opts: { packageId: PackageId },
|
||||||
|
map: (interfaces: ServiceInterfaceFilled[]) => Mapped,
|
||||||
|
eq?: (a: Mapped, b: Mapped) => boolean,
|
||||||
|
): GetServiceInterfaces<Mapped>
|
||||||
|
export function getServiceInterfaces<Mapped>(
|
||||||
|
effects: Effects,
|
||||||
|
opts: { packageId: PackageId },
|
||||||
|
map?: (interfaces: ServiceInterfaceFilled[]) => Mapped,
|
||||||
|
eq?: (a: Mapped, b: Mapped) => boolean,
|
||||||
|
): GetServiceInterfaces<Mapped> {
|
||||||
|
return new GetServiceInterfaces(
|
||||||
|
effects,
|
||||||
|
opts,
|
||||||
|
map ?? ((a) => a as Mapped),
|
||||||
|
eq ?? ((a, b) => deepEqual(a, b)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ import {
|
|||||||
setupOnUninit,
|
setupOnUninit,
|
||||||
} from "../../base/lib/inits"
|
} from "../../base/lib/inits"
|
||||||
import { DropGenerator } from "../../base/lib/util/Drop"
|
import { DropGenerator } from "../../base/lib/util/Drop"
|
||||||
|
import {
|
||||||
|
getOwnServiceInterface,
|
||||||
|
ServiceInterfaceFilled,
|
||||||
|
} from "../../base/lib/util/getServiceInterface"
|
||||||
|
import { getOwnServiceInterfaces } from "../../base/lib/util/getServiceInterfaces"
|
||||||
|
|
||||||
export const OSVersion = testTypeVersion("0.4.0-alpha.16")
|
export const OSVersion = testTypeVersion("0.4.0-alpha.16")
|
||||||
|
|
||||||
@@ -170,20 +175,10 @@ export class StartSdk<Manifest extends T.SDKManifest> {
|
|||||||
packageIds?: DependencyId[],
|
packageIds?: DependencyId[],
|
||||||
) => Promise<CheckDependencies<DependencyId>>,
|
) => Promise<CheckDependencies<DependencyId>>,
|
||||||
serviceInterface: {
|
serviceInterface: {
|
||||||
getOwn: <E extends Effects>(effects: E, id: ServiceInterfaceId) =>
|
getOwn: getOwnServiceInterface,
|
||||||
getServiceInterface(effects, {
|
get: getServiceInterface,
|
||||||
id,
|
getAllOwn: getOwnServiceInterfaces,
|
||||||
}),
|
getAll: getServiceInterfaces,
|
||||||
get: <E extends Effects>(
|
|
||||||
effects: E,
|
|
||||||
opts: { id: ServiceInterfaceId; packageId: PackageId },
|
|
||||||
) => getServiceInterface(effects, opts),
|
|
||||||
getAllOwn: <E extends Effects>(effects: E) =>
|
|
||||||
getServiceInterfaces(effects, {}),
|
|
||||||
getAll: <E extends Effects>(
|
|
||||||
effects: E,
|
|
||||||
opts: { packageId: PackageId },
|
|
||||||
) => getServiceInterfaces(effects, opts),
|
|
||||||
},
|
},
|
||||||
getContainerIp: (
|
getContainerIp: (
|
||||||
effects: T.Effects,
|
effects: T.Effects,
|
||||||
|
|||||||
@@ -4,38 +4,11 @@ import * as TOML from "@iarna/toml"
|
|||||||
import * as INI from "ini"
|
import * as INI from "ini"
|
||||||
import * as T from "../../../base/lib/types"
|
import * as T from "../../../base/lib/types"
|
||||||
import * as fs from "node:fs/promises"
|
import * as fs from "node:fs/promises"
|
||||||
import { asError } from "../../../base/lib/util"
|
import { asError, deepEqual } from "../../../base/lib/util"
|
||||||
import { DropGenerator, DropPromise } from "../../../base/lib/util/Drop"
|
import { DropGenerator, DropPromise } from "../../../base/lib/util/Drop"
|
||||||
|
|
||||||
const previousPath = /(.+?)\/([^/]*)$/
|
const previousPath = /(.+?)\/([^/]*)$/
|
||||||
|
|
||||||
const deepEq = (left: unknown, right: unknown) => {
|
|
||||||
if (left === right) return true
|
|
||||||
if (Array.isArray(left) && Array.isArray(right)) {
|
|
||||||
if (left.length === right.length) {
|
|
||||||
for (const idx in left) {
|
|
||||||
if (!deepEq(left[idx], right[idx])) return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
typeof left === "object" &&
|
|
||||||
typeof right === "object" &&
|
|
||||||
left &&
|
|
||||||
right
|
|
||||||
) {
|
|
||||||
const keys = new Set<keyof typeof left | keyof typeof right>([
|
|
||||||
...(Object.keys(left) as (keyof typeof left)[]),
|
|
||||||
...(Object.keys(right) as (keyof typeof right)[]),
|
|
||||||
])
|
|
||||||
for (let key of keys) {
|
|
||||||
if (!deepEq(left[key], right[key])) return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const exists = (path: string) =>
|
const exists = (path: string) =>
|
||||||
fs.access(path).then(
|
fs.access(path).then(
|
||||||
() => true,
|
() => true,
|
||||||
@@ -374,7 +347,7 @@ export class FileHelper<A> {
|
|||||||
eq?: (left: any, right: any) => boolean,
|
eq?: (left: any, right: any) => boolean,
|
||||||
): ReadType<any> {
|
): ReadType<any> {
|
||||||
map = map ?? ((a: A) => a)
|
map = map ?? ((a: A) => a)
|
||||||
eq = eq ?? deepEq
|
eq = eq ?? deepEqual
|
||||||
return {
|
return {
|
||||||
once: () => this.readOnce(map),
|
once: () => this.readOnce(map),
|
||||||
const: (effects: T.Effects) => this.readConst(effects, map, eq),
|
const: (effects: T.Effects) => this.readConst(effects, map, eq),
|
||||||
|
|||||||
Reference in New Issue
Block a user