feat: move all frontend projects under the same Angular workspace (#1141)

* feat: move all frontend projects under the same Angular workspace

* Refactor/angular workspace (#1154)

* update frontend build steps

Co-authored-by: waterplea <alexander@inkin.ru>
Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
Co-authored-by: Lucy Cifferello <12953208+elvece@users.noreply.github.com>
This commit is contained in:
Aiden McClelland
2022-01-31 14:01:33 -07:00
committed by GitHub
parent 7e6c852ebd
commit 574539faec
504 changed files with 11569 additions and 78972 deletions

View File

@@ -0,0 +1,137 @@
export interface ConfigSpec { [key: string]: ValueSpec }
export type ValueType = 'string' | 'number' | 'boolean' | 'enum' | 'list' | 'object' | 'pointer' | 'union'
export type ValueSpec = ValueSpecOf<ValueType>
// core spec types. These types provide the metadata for performing validations
export type ValueSpecOf<T extends ValueType> =
T extends 'string' ? ValueSpecString :
T extends 'number' ? ValueSpecNumber :
T extends 'boolean' ? ValueSpecBoolean :
T extends 'enum' ? ValueSpecEnum :
T extends 'list' ? ValueSpecList :
T extends 'object' ? ValueSpecObject :
T extends 'pointer' ? ValueSpecPointer :
T extends 'union' ? ValueSpecUnion :
never
export interface ValueSpecString extends ListValueSpecString, WithStandalone {
type: 'string'
default?: DefaultString
nullable: boolean
}
export interface ValueSpecNumber extends ListValueSpecNumber, WithStandalone {
type: 'number'
nullable: boolean
default?: number
}
export interface ValueSpecEnum extends ListValueSpecEnum, WithStandalone {
type: 'enum'
default: string
}
export interface ValueSpecBoolean extends WithStandalone {
type: 'boolean'
default: boolean
}
export interface ValueSpecUnion extends ListValueSpecUnion, WithStandalone {
type: 'union'
}
export interface ValueSpecPointer extends WithStandalone {
type: 'pointer'
subtype: 'package' | 'system'
'package-id': string
target: 'lan-address' | 'tor-address' | 'config' | 'tor-key'
interface: string // will only exist if target = tor-key || tor-address || lan-address
selector?: string // will only exist if target = config
multi?: boolean // will only exist if target = config
}
export interface ValueSpecObject extends ListValueSpecObject, WithStandalone {
type: 'object'
}
export interface WithStandalone {
name: string
description?: string
warning?: string
}
// no lists of booleans, lists, pointers
export type ListValueSpecType = 'string' | 'number' | 'enum' | 'object' | 'union'
// represents a spec for the values of a list
export type ListValueSpecOf<T extends ListValueSpecType> =
T extends 'string' ? ListValueSpecString :
T extends 'number' ? ListValueSpecNumber :
T extends 'enum' ? ListValueSpecEnum :
T extends 'object' ? ListValueSpecObject :
T extends 'union' ? ListValueSpecUnion :
never
// represents a spec for a list
export type ValueSpecList = ValueSpecListOf<ListValueSpecType>
export interface ValueSpecListOf<T extends ListValueSpecType> extends WithStandalone {
type: 'list'
subtype: T
spec: ListValueSpecOf<T>
range: string // '[0,1]' (inclusive) OR '[0,*)' (right unbounded), normal math rules
default: string[] | number[] | DefaultString[] | object[]
}
// sometimes the type checker needs just a little bit of help
export function isValueSpecListOf<S extends ListValueSpecType> (t: ValueSpecList, s: S): t is ValueSpecListOf<S> {
return t.subtype === s
}
export interface ListValueSpecString {
pattern?: string
'pattern-description'?: string
masked: boolean
copyable: boolean
placeholder?: string
}
export interface ListValueSpecNumber {
range: string
integral: boolean
units?: string
placeholder?: string
}
export interface ListValueSpecEnum {
values: string[]
'values-set'?: Set<string>
'value-names': { [value: string]: string }
}
export interface ListValueSpecObject {
spec: ConfigSpec // this is a mapped type of the config object at this level, replacing the object's values with specs on those values
'unique-by': UniqueBy // indicates whether duplicates can be permitted in the list
'display-as'?: string // this should be a handlebars template which can make use of the entire config which corresponds to 'spec'
}
export type UniqueBy = null | string | { any: UniqueBy[] } | { all: UniqueBy[] }
export interface ListValueSpecUnion {
tag: UnionTagSpec
variants: { [key: string]: ConfigSpec }
'display-as'?: string // this may be a handlebars template which can conditionally (on tag.id) make use of each union's entries, or if left blank will display as tag.id
'unique-by': UniqueBy
default: string // this should be the variantName which one prefers a user to start with by default when creating a new union instance in a list
}
export interface UnionTagSpec {
id: string // The name of the field containing one of the union variants
name: string
description?: string
'variant-names': { // the name of each variant
[variant: string]: string
}
}
export type DefaultString = string | { charset: string, len: number }

View File

@@ -0,0 +1,241 @@
import { ValueSpec, DefaultString } from './config-types'
export class Range {
min?: number
max?: number
minInclusive: boolean
maxInclusive: boolean
static from (s: string): Range {
const r = new Range()
r.minInclusive = s.startsWith('[')
r.maxInclusive = s.endsWith(']')
const [minStr, maxStr] = s.split(',').map(a => a.trim())
r.min = minStr === '(*' ? undefined : Number(minStr.slice(1))
r.max = maxStr === '*)' ? undefined : Number(maxStr.slice(0, -1))
return r
}
checkIncludes (n: number) {
if (
this.hasMin() !== undefined &&
(
(this.min > n) ||
(!this.minInclusive && this.min == n)
)
) {
throw new Error(this.minMessage())
}
if (
this.hasMax() &&
(
(this.max < n) ||
(!this.maxInclusive && this.max == n)
)
) {
throw new Error(this.maxMessage())
}
}
hasMin (): boolean {
return this.min !== undefined
}
hasMax (): boolean {
return this.max !== undefined
}
minMessage (): string {
return `greater than${this.minInclusive ? ' or equal to' : ''} ${this.min}`
}
maxMessage (): string {
return `less than${this.maxInclusive ? ' or equal to' : ''} ${this.max}`
}
description (): string {
let message = 'Value can be any number.'
if (this.hasMin() || this.hasMax()) {
message = 'Value must be'
}
if (this.hasMin() && this.hasMax()) {
message = `${message} ${this.minMessage()} AND ${this.maxMessage()}.`
} else if (this.hasMin() && !this.hasMax()) {
message = `${message} ${this.minMessage()}.`
} else if (!this.hasMin() && this.hasMax()) {
message = `${message} ${this.maxMessage()}.`
}
return message
}
integralMin (): number | undefined {
if (this.min) {
const ceil = Math.ceil(this.min)
if (this.minInclusive) {
return ceil
} else {
if (ceil === this.min) {
return ceil + 1
} else {
return ceil
}
}
}
}
integralMax (): number | undefined {
if (this.max) {
const floor = Math.floor(this.max)
if (this.maxInclusive) {
return floor
} else {
if (floor === this.max) {
return floor - 1
} else {
return floor
}
}
}
}
}
export function getDefaultDescription (spec: ValueSpec): string {
let toReturn: string | undefined
switch (spec.type) {
case 'string':
if (typeof spec.default === 'string') {
toReturn = spec.default
} else if (typeof spec.default === 'object') {
toReturn = 'random'
}
break
case 'number':
if (typeof spec.default === 'number') {
toReturn = String(spec.default)
}
break
case 'boolean':
toReturn = spec.default === true ? 'True' : 'False'
break
case 'enum':
toReturn = spec['value-names'][spec.default]
break
}
return toReturn || ''
}
export function getDefaultString (defaultSpec: DefaultString): string {
if (typeof defaultSpec === 'string') {
return defaultSpec
} else {
let s = ''
for (let i = 0; i < defaultSpec.len; i++) {
s = s + getRandomCharInSet(defaultSpec.charset)
}
return s
}
}
// a,g,h,A-Z,,,,-
export function getRandomCharInSet (charset: string): string {
const set = stringToCharSet(charset)
let charIdx = Math.floor(Math.random() * set.len)
for (let range of set.ranges) {
if (range.len > charIdx) {
return String.fromCharCode(range.start.charCodeAt(0) + charIdx)
}
charIdx -= range.len
}
throw new Error('unreachable')
}
function stringToCharSet (charset: string): CharSet {
let set: CharSet = { ranges: [], len: 0 }
let start: string | null = null
let end: string | null = null
let in_range = false
for (let char of charset) {
switch (char) {
case ',':
if (start !== null && end !== null) {
if (start!.charCodeAt(0) > end!.charCodeAt(0)) {
throw new Error('start > end of charset')
}
const len = end.charCodeAt(0) - start.charCodeAt(0) + 1
set.ranges.push({
start,
end,
len,
})
set.len += len
start = null
end = null
in_range = false
} else if (start !== null && !in_range) {
set.len += 1
set.ranges.push({ start, end: start, len: 1 })
start = null
} else if (start !== null && in_range) {
end = ','
} else if (start === null && end === null && !in_range) {
start = ','
} else {
throw new Error('unexpected ","')
}
break
case '-':
if (start === null) {
start = '-'
} else if (!in_range) {
in_range = true
} else if (in_range && end === null) {
end = '-'
} else {
throw new Error('unexpected "-"')
}
break
default:
if (start === null) {
start = char
} else if (in_range && end === null) {
end = char
} else {
throw new Error(`unexpected "${char}"`)
}
}
}
if (start !== null && end !== null) {
if (start!.charCodeAt(0) > end!.charCodeAt(0)) {
throw new Error('start > end of charset')
}
const len = end.charCodeAt(0) - start.charCodeAt(0) + 1
set.ranges.push({
start,
end,
len,
})
set.len += len
} else if (start !== null) {
set.len += 1
set.ranges.push({
start,
end: start,
len: 1,
})
}
return set
}
interface CharSet {
ranges: {
start: string
end: string
len: number
}[]
len: number
}