add documentation for ai agents (#3115)

* add documentation for ai agents

* docs: consolidate CLAUDE.md and CONTRIBUTING.md, add style guidelines

- Refactor CLAUDE.md to reference CONTRIBUTING.md for build/test/format info
- Expand CONTRIBUTING.md with comprehensive build targets, env vars, and testing
- Add code style guidelines section with conventional commits
- Standardize SDK prettier config to use single quotes (matching web)
- Add project-level Claude Code settings to disable co-author attribution

* style(sdk): apply prettier with single quotes

Run prettier across sdk/base and sdk/package to apply the
standardized quote style (single quotes matching web).

* docs: add USER.md for per-developer TODO filtering

- Add agents/USER.md to .gitignore (contains user identifier)
- Document session startup flow in CLAUDE.md:
  - Create USER.md if missing, prompting for identifier
  - Filter TODOs by @username tags
  - Offer relevant TODOs on session start

* docs: add i18n documentation task to agent TODOs

* docs: document i18n ID patterns in core/

Add agents/i18n-patterns.md covering rust-i18n setup, translation file
format, t!() macro usage, key naming conventions, and locale selection.
Remove completed TODO item and add reference in CLAUDE.md.

* chore: clarify that all builds work on any OS with Docker
This commit is contained in:
Aiden McClelland
2026-02-06 00:10:16 +01:00
committed by GitHub
parent 86ca23c093
commit f2142f0bb3
280 changed files with 6793 additions and 5515 deletions

View File

@@ -1,7 +1,7 @@
import * as T from "../types"
import * as IST from "../actions/input/inputSpecTypes"
import { Action, ActionInfo } from "./setupActions"
import { ExtractInputSpecType } from "./input/builder/inputSpec"
import * as T from '../types'
import * as IST from '../actions/input/inputSpecTypes'
import { Action, ActionInfo } from './setupActions'
import { ExtractInputSpecType } from './input/builder/inputSpec'
export type RunActionInput<Input> =
| Input
@@ -53,17 +53,17 @@ type TaskBase = {
replayId?: string
}
type TaskInput<T extends ActionInfo<T.ActionId, any>> = {
kind: "partial"
kind: 'partial'
value: T.DeepPartial<GetActionInputType<T>>
}
export type TaskOptions<T extends ActionInfo<T.ActionId, any>> = TaskBase &
(
| {
when?: Exclude<T.TaskTrigger, { condition: "input-not-matches" }>
when?: Exclude<T.TaskTrigger, { condition: 'input-not-matches' }>
input?: TaskInput<T>
}
| {
when: T.TaskTrigger & { condition: "input-not-matches" }
when: T.TaskTrigger & { condition: 'input-not-matches' }
input: TaskInput<T>
}
)

View File

@@ -1,6 +1,6 @@
import { InputSpec } from "./inputSpec"
import { List } from "./list"
import { Value } from "./value"
import { Variants } from "./variants"
import { InputSpec } from './inputSpec'
import { List } from './list'
import { Value } from './value'
import { Variants } from './variants'
export { InputSpec as InputSpec, List, Value, Variants }

View File

@@ -1,9 +1,9 @@
import { ValueSpec } from "../inputSpecTypes"
import { Value } from "./value"
import { _ } from "../../../util"
import { Effects } from "../../../Effects"
import { Parser, object } from "ts-matches"
import { DeepPartial } from "../../../types"
import { ValueSpec } from '../inputSpecTypes'
import { Value } from './value'
import { _ } from '../../../util'
import { Effects } from '../../../Effects'
import { Parser, object } from 'ts-matches'
import { DeepPartial } from '../../../types'
export type LazyBuildOptions = {
effects: Effects

View File

@@ -1,4 +1,4 @@
import { InputSpec, LazyBuild } from "./inputSpec"
import { InputSpec, LazyBuild } from './inputSpec'
import {
ListValueSpecText,
Pattern,
@@ -6,8 +6,8 @@ import {
UniqueBy,
ValueSpecList,
ValueSpecListOf,
} from "../inputSpecTypes"
import { Parser, arrayOf, string } from "ts-matches"
} from '../inputSpecTypes'
import { Parser, arrayOf, string } from 'ts-matches'
export class List<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
private constructor(
@@ -55,7 +55,7 @@ export class List<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
* @description Informs the browser how to behave and which keyboard to display on mobile
* @default "text"
*/
inputmode?: ListValueSpecText["inputmode"]
inputmode?: ListValueSpecText['inputmode']
/**
* @description Displays a button that will generate a random string according to the provided charset and len attributes.
*/
@@ -65,21 +65,21 @@ export class List<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const validator = arrayOf(string)
return new List<string[]>(() => {
const spec = {
type: "text" as const,
type: 'text' as const,
placeholder: null,
minLength: null,
maxLength: null,
masked: false,
inputmode: "text" as const,
inputmode: 'text' as const,
generate: null,
patterns: aSpec.patterns || [],
...aSpec,
}
const built: ValueSpecListOf<"text"> = {
const built: ValueSpecListOf<'text'> = {
description: null,
warning: null,
default: [],
type: "list" as const,
type: 'list' as const,
minLength: null,
maxLength: null,
disabled: false,
@@ -106,7 +106,7 @@ export class List<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
minLength?: number | null
maxLength?: number | null
patterns?: Pattern[]
inputmode?: ListValueSpecText["inputmode"]
inputmode?: ListValueSpecText['inputmode']
}
}>,
) {
@@ -114,21 +114,21 @@ export class List<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
return new List<string[]>(async (options) => {
const { spec: aSpec, ...a } = await getA(options)
const spec = {
type: "text" as const,
type: 'text' as const,
placeholder: null,
minLength: null,
maxLength: null,
masked: false,
inputmode: "text" as const,
inputmode: 'text' as const,
generate: null,
patterns: aSpec.patterns || [],
...aSpec,
}
const built: ValueSpecListOf<"text"> = {
const built: ValueSpecListOf<'text'> = {
description: null,
warning: null,
default: [],
type: "list" as const,
type: 'list' as const,
minLength: null,
maxLength: null,
disabled: false,
@@ -162,7 +162,7 @@ export class List<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const { spec: previousSpecSpec, ...restSpec } = aSpec
const built = await previousSpecSpec.build(options)
const spec = {
type: "object" as const,
type: 'object' as const,
displayAs: null,
uniqueBy: null,
...restSpec,
@@ -179,7 +179,7 @@ export class List<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
warning: null,
minLength: null,
maxLength: null,
type: "list" as const,
type: 'list' as const,
disabled: false,
...value,
},

View File

@@ -1,6 +1,6 @@
import { InputSpec, LazyBuild } from "./inputSpec"
import { List } from "./list"
import { UnionRes, UnionResStaticValidatedAs, Variants } from "./variants"
import { InputSpec, LazyBuild } from './inputSpec'
import { List } from './list'
import { UnionRes, UnionResStaticValidatedAs, Variants } from './variants'
import {
Pattern,
RandomString,
@@ -9,9 +9,9 @@ import {
ValueSpecHidden,
ValueSpecText,
ValueSpecTextarea,
} from "../inputSpecTypes"
import { DefaultString } from "../inputSpecTypes"
import { _, once } from "../../../util"
} from '../inputSpecTypes'
import { DefaultString } from '../inputSpecTypes'
import { _, once } from '../../../util'
import {
Parser,
any,
@@ -23,8 +23,8 @@ import {
number,
object,
string,
} from "ts-matches"
import { DeepPartial } from "../../../types"
} from 'ts-matches'
import { DeepPartial } from '../../../types'
export const fileInfoParser = object({
path: string,
@@ -42,7 +42,7 @@ const testForAsRequiredParser = once(
function asRequiredParser<Type, Input extends { required: boolean }>(
parser: Parser<unknown, Type>,
input: Input,
): Parser<unknown, AsRequired<Type, Input["required"]>> {
): Parser<unknown, AsRequired<Type, Input['required']>> {
if (testForAsRequiredParser()(input)) return parser as any
return parser.nullable() as any
}
@@ -92,7 +92,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
spec: {
description: null,
warning: null,
type: "toggle" as const,
type: 'toggle' as const,
disabled: false,
immutable: a.immutable ?? false,
...a,
@@ -117,7 +117,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
spec: {
description: null,
warning: null,
type: "toggle" as const,
type: 'toggle' as const,
disabled: false,
immutable: false,
...(await a(options)),
@@ -191,7 +191,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
* @description Informs the browser how to behave and which keyboard to display on mobile
* @default "text"
*/
inputmode?: ValueSpecText["inputmode"]
inputmode?: ValueSpecText['inputmode']
/**
* @description Once set, the value can never be changed.
* @default false
@@ -206,7 +206,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
return new Value<AsRequired<string, Required>>(
async () => ({
spec: {
type: "text" as const,
type: 'text' as const,
description: null,
warning: null,
masked: false,
@@ -214,7 +214,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
minLength: null,
maxLength: null,
patterns: [],
inputmode: "text",
inputmode: 'text',
disabled: false,
immutable: a.immutable ?? false,
generate: a.generate ?? null,
@@ -237,7 +237,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
minLength?: number | null
maxLength?: number | null
patterns?: Pattern[]
inputmode?: ValueSpecText["inputmode"]
inputmode?: ValueSpecText['inputmode']
disabled?: string | false
generate?: null | RandomString
}>,
@@ -247,7 +247,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const a = await getA(options)
return {
spec: {
type: "text" as const,
type: 'text' as const,
description: null,
warning: null,
masked: false,
@@ -255,7 +255,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
minLength: null,
maxLength: null,
patterns: [],
inputmode: "text",
inputmode: 'text',
disabled: false,
immutable: false,
generate: a.generate ?? null,
@@ -334,7 +334,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
minRows: 3,
maxRows: 6,
placeholder: null,
type: "textarea" as const,
type: 'textarea' as const,
disabled: false,
immutable: a.immutable ?? false,
...a,
@@ -371,7 +371,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
minRows: 3,
maxRows: 6,
placeholder: null,
type: "textarea" as const,
type: 'textarea' as const,
disabled: false,
immutable: false,
...a,
@@ -444,7 +444,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
return new Value<AsRequired<number, Required>>(
() => ({
spec: {
type: "number" as const,
type: 'number' as const,
description: null,
warning: null,
min: null,
@@ -482,7 +482,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const a = await getA(options)
return {
spec: {
type: "number" as const,
type: 'number' as const,
description: null,
warning: null,
min: null,
@@ -540,7 +540,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
return new Value<AsRequired<string, Required>>(
() => ({
spec: {
type: "color" as const,
type: 'color' as const,
description: null,
warning: null,
disabled: false,
@@ -568,7 +568,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const a = await getA(options)
return {
spec: {
type: "color" as const,
type: 'color' as const,
description: null,
warning: null,
disabled: false,
@@ -618,7 +618,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
* @description Informs the browser how to behave and which date/time component to display.
* @default "datetime-local"
*/
inputmode?: ValueSpecDatetime["inputmode"]
inputmode?: ValueSpecDatetime['inputmode']
min?: string | null
max?: string | null
/**
@@ -631,10 +631,10 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
return new Value<AsRequired<string, Required>>(
() => ({
spec: {
type: "datetime" as const,
type: 'datetime' as const,
description: null,
warning: null,
inputmode: "datetime-local",
inputmode: 'datetime-local',
min: null,
max: null,
step: null,
@@ -654,7 +654,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
warning?: string | null
default: string | null
required: Required
inputmode?: ValueSpecDatetime["inputmode"]
inputmode?: ValueSpecDatetime['inputmode']
min?: string | null
max?: string | null
disabled?: false | string
@@ -665,10 +665,10 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const a = await getA(options)
return {
spec: {
type: "datetime" as const,
type: 'datetime' as const,
description: null,
warning: null,
inputmode: "datetime-local",
inputmode: 'datetime-local',
min: null,
max: null,
disabled: false,
@@ -740,7 +740,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
spec: {
description: null,
warning: null,
type: "select" as const,
type: 'select' as const,
disabled: false,
immutable: a.immutable ?? false,
...a,
@@ -766,7 +766,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
spec: {
description: null,
warning: null,
type: "select" as const,
type: 'select' as const,
disabled: false,
immutable: false,
...a,
@@ -837,7 +837,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
return new Value<(keyof Values & string)[]>(
() => ({
spec: {
type: "multiselect" as const,
type: 'multiselect' as const,
minLength: null,
maxLength: null,
warning: null,
@@ -867,7 +867,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const a = await getA(options)
return {
spec: {
type: "multiselect" as const,
type: 'multiselect' as const,
minLength: null,
maxLength: null,
warning: null,
@@ -915,7 +915,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const built = await spec.build(options as any)
return {
spec: {
type: "object" as const,
type: 'object' as const,
description: null,
warning: null,
...a,
@@ -933,7 +933,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
required: Required
}) {
const buildValue = {
type: "file" as const,
type: 'file' as const,
description: null,
warning: null,
...a,
@@ -960,7 +960,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
return new Value<AsRequired<FileInfo, Required>, FileInfo | null>(
async (options) => {
const spec = {
type: "file" as const,
type: 'file' as const,
description: null,
warning: null,
...(await a(options)),
@@ -1034,7 +1034,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const built = await a.variants.build(options as any)
return {
spec: {
type: "union" as const,
type: 'union' as const,
description: null,
warning: null,
disabled: false,
@@ -1109,7 +1109,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const built = await newValues.variants.build(options as any)
return {
spec: {
type: "union" as const,
type: 'union' as const,
description: null,
warning: null,
...newValues,
@@ -1202,7 +1202,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
return new Value<T, typeof parser._TYPE>(async () => {
return {
spec: {
type: "hidden" as const,
type: 'hidden' as const,
} as ValueSpecHidden,
validator: parser,
}
@@ -1221,7 +1221,7 @@ export class Value<Type extends StaticValidatedAs, StaticValidatedAs = Type> {
const validator = await getParser(options)
return {
spec: {
type: "hidden" as const,
type: 'hidden' as const,
} as ValueSpecHidden,
validator,
}

View File

@@ -1,12 +1,12 @@
import { DeepPartial } from "../../../types"
import { ValueSpec, ValueSpecUnion } from "../inputSpecTypes"
import { DeepPartial } from '../../../types'
import { ValueSpec, ValueSpecUnion } from '../inputSpecTypes'
import {
LazyBuild,
InputSpec,
ExtractInputSpecType,
ExtractInputSpecStaticValidatedAs,
} from "./inputSpec"
import { Parser, any, anyOf, literal, object } from "ts-matches"
} from './inputSpec'
import { Parser, any, anyOf, literal, object } from 'ts-matches'
export type UnionRes<
VariantValues extends {
@@ -19,10 +19,10 @@ export type UnionRes<
> = {
[key in keyof VariantValues]: {
selection: key
value: ExtractInputSpecType<VariantValues[key]["spec"]>
value: ExtractInputSpecType<VariantValues[key]['spec']>
other?: {
[key2 in Exclude<keyof VariantValues & string, key>]?: DeepPartial<
ExtractInputSpecType<VariantValues[key2]["spec"]>
ExtractInputSpecType<VariantValues[key2]['spec']>
>
}
}
@@ -39,10 +39,10 @@ export type UnionResStaticValidatedAs<
> = {
[key in keyof VariantValues]: {
selection: key
value: ExtractInputSpecStaticValidatedAs<VariantValues[key]["spec"]>
value: ExtractInputSpecStaticValidatedAs<VariantValues[key]['spec']>
other?: {
[key2 in Exclude<keyof VariantValues & string, key>]?: DeepPartial<
ExtractInputSpecStaticValidatedAs<VariantValues[key2]["spec"]>
ExtractInputSpecStaticValidatedAs<VariantValues[key2]['spec']>
>
}
}
@@ -106,7 +106,7 @@ export class Variants<
> {
private constructor(
public build: LazyBuild<{
spec: ValueSpecUnion["variants"]
spec: ValueSpecUnion['variants']
validator: Parser<unknown, UnionRes<VariantValues>>
}>,
public readonly validator: Parser<
@@ -126,7 +126,7 @@ export class Variants<
const staticValidators = {} as {
[K in keyof VariantValues]: Parser<
unknown,
ExtractInputSpecStaticValidatedAs<VariantValues[K]["spec"]>
ExtractInputSpecStaticValidatedAs<VariantValues[K]['spec']>
>
}
for (const key in a) {
@@ -143,7 +143,7 @@ export class Variants<
const validators = {} as {
[K in keyof VariantValues]: Parser<
unknown,
ExtractInputSpecType<VariantValues[K]["spec"]>
ExtractInputSpecType<VariantValues[K]['spec']>
>
}
const variants = {} as {

View File

@@ -1,3 +1,3 @@
export * as constants from "./inputSpecConstants"
export * as types from "./inputSpecTypes"
export * as builder from "./builder"
export * as constants from './inputSpecConstants'
export * as types from './inputSpecTypes'
export * as builder from './builder'

View File

@@ -1,8 +1,8 @@
import { SmtpValue } from "../../types"
import { GetSystemSmtp, Patterns } from "../../util"
import { InputSpec, InputSpecOf } from "./builder/inputSpec"
import { Value } from "./builder/value"
import { Variants } from "./builder/variants"
import { SmtpValue } from '../../types'
import { GetSystemSmtp, Patterns } from '../../util'
import { InputSpec, InputSpecOf } from './builder/inputSpec'
import { Value } from './builder/value'
import { Variants } from './builder/variants'
/**
* Base SMTP settings, to be used by StartOS for system wide SMTP
@@ -11,12 +11,12 @@ export const customSmtp: InputSpec<SmtpValue> = InputSpec.of<
InputSpecOf<SmtpValue>
>({
server: Value.text({
name: "SMTP Server",
name: 'SMTP Server',
required: true,
default: null,
}),
port: Value.number({
name: "Port",
name: 'Port',
required: true,
default: 587,
min: 1,
@@ -24,20 +24,20 @@ export const customSmtp: InputSpec<SmtpValue> = InputSpec.of<
integer: true,
}),
from: Value.text({
name: "From Address",
name: 'From Address',
required: true,
default: null,
placeholder: "Example Name <test@example.com>",
inputmode: "email",
placeholder: 'Example Name <test@example.com>',
inputmode: 'email',
patterns: [Patterns.emailWithName],
}),
login: Value.text({
name: "Login",
name: 'Login',
required: true,
default: null,
}),
password: Value.text({
name: "Password",
name: 'Password',
required: false,
default: null,
masked: true,
@@ -45,24 +45,24 @@ export const customSmtp: InputSpec<SmtpValue> = InputSpec.of<
})
const smtpVariants = Variants.of({
disabled: { name: "Disabled", spec: InputSpec.of({}) },
disabled: { name: 'Disabled', spec: InputSpec.of({}) },
system: {
name: "System Credentials",
name: 'System Credentials',
spec: InputSpec.of({
customFrom: Value.text({
name: "Custom From Address",
name: 'Custom From Address',
description:
"A custom from address for this service. If not provided, the system from address will be used.",
'A custom from address for this service. If not provided, the system from address will be used.',
required: false,
default: null,
placeholder: "<name>test@example.com",
inputmode: "email",
placeholder: '<name>test@example.com',
inputmode: 'email',
patterns: [Patterns.email],
}),
}),
},
custom: {
name: "Custom Credentials",
name: 'Custom Credentials',
spec: customSmtp,
},
})
@@ -71,11 +71,11 @@ const smtpVariants = Variants.of({
*/
export const smtpInputSpec = Value.dynamicUnion(async ({ effects }) => {
const smtp = await new GetSystemSmtp(effects).once()
const disabled = smtp ? [] : ["system"]
const disabled = smtp ? [] : ['system']
return {
name: "SMTP",
description: "Optionally provide an SMTP server for sending emails",
default: "disabled",
name: 'SMTP',
description: 'Optionally provide an SMTP server for sending emails',
default: 'disabled',
disabled,
variants: smtpVariants,
}

View File

@@ -1,18 +1,18 @@
export type InputSpec = Record<string, ValueSpec>
export type ValueType =
| "text"
| "textarea"
| "number"
| "color"
| "datetime"
| "toggle"
| "select"
| "multiselect"
| "list"
| "object"
| "file"
| "union"
| "hidden"
| 'text'
| 'textarea'
| 'number'
| 'color'
| 'datetime'
| 'toggle'
| 'select'
| 'multiselect'
| 'list'
| 'object'
| 'file'
| 'union'
| 'hidden'
export type ValueSpec = ValueSpecOf<ValueType>
/** core spec types. These types provide the metadata for performing validations */
// prettier-ignore
@@ -37,13 +37,13 @@ export type ValueSpecText = {
description: string | null
warning: string | null
type: "text"
type: 'text'
patterns: Pattern[]
minLength: number | null
maxLength: number | null
masked: boolean
inputmode: "text" | "email" | "tel" | "url"
inputmode: 'text' | 'email' | 'tel' | 'url'
placeholder: string | null
required: boolean
@@ -57,7 +57,7 @@ export type ValueSpecTextarea = {
description: string | null
warning: string | null
type: "textarea"
type: 'textarea'
patterns: Pattern[]
placeholder: string | null
minLength: number | null
@@ -71,7 +71,7 @@ export type ValueSpecTextarea = {
}
export type ValueSpecNumber = {
type: "number"
type: 'number'
min: number | null
max: number | null
integer: boolean
@@ -91,7 +91,7 @@ export type ValueSpecColor = {
description: string | null
warning: string | null
type: "color"
type: 'color'
required: boolean
default: string | null
disabled: false | string
@@ -101,9 +101,9 @@ export type ValueSpecDatetime = {
name: string
description: string | null
warning: string | null
type: "datetime"
type: 'datetime'
required: boolean
inputmode: "date" | "time" | "datetime-local"
inputmode: 'date' | 'time' | 'datetime-local'
min: string | null
max: string | null
default: string | null
@@ -115,7 +115,7 @@ export type ValueSpecSelect = {
name: string
description: string | null
warning: string | null
type: "select"
type: 'select'
default: string | null
disabled: false | string | string[]
immutable: boolean
@@ -127,7 +127,7 @@ export type ValueSpecMultiselect = {
description: string | null
warning: string | null
type: "multiselect"
type: 'multiselect'
minLength: number | null
maxLength: number | null
disabled: false | string | string[]
@@ -139,7 +139,7 @@ export type ValueSpecToggle = {
description: string | null
warning: string | null
type: "toggle"
type: 'toggle'
default: boolean | null
disabled: false | string
immutable: boolean
@@ -149,7 +149,7 @@ export type ValueSpecUnion = {
description: string | null
warning: string | null
type: "union"
type: 'union'
variants: Record<
string,
{
@@ -165,7 +165,7 @@ export type ValueSpecFile = {
name: string
description: string | null
warning: string | null
type: "file"
type: 'file'
extensions: string[]
required: boolean
}
@@ -173,13 +173,13 @@ export type ValueSpecObject = {
name: string
description: string | null
warning: string | null
type: "object"
type: 'object'
spec: InputSpec
}
export type ValueSpecHidden = {
type: "hidden"
type: 'hidden'
}
export type ListValueSpecType = "text" | "object"
export type ListValueSpecType = 'text' | 'object'
// prettier-ignore
export type ListValueSpecOf<T extends ListValueSpecType> =
T extends "text" ? ListValueSpecText :
@@ -190,7 +190,7 @@ export type ValueSpecListOf<T extends ListValueSpecType> = {
name: string
description: string | null
warning: string | null
type: "list"
type: 'list'
spec: ListValueSpecOf<T>
minLength: number | null
maxLength: number | null
@@ -208,18 +208,18 @@ export type Pattern = {
description: string
}
export type ListValueSpecText = {
type: "text"
type: 'text'
patterns: Pattern[]
minLength: number | null
maxLength: number | null
masked: boolean
generate: null | RandomString
inputmode: "text" | "email" | "tel" | "url"
inputmode: 'text' | 'email' | 'tel' | 'url'
placeholder: string | null
}
export type ListValueSpecObject = {
type: "object"
type: 'object'
spec: InputSpec
uniqueBy: UniqueBy
displayAs: string | null
@@ -244,5 +244,5 @@ export function isValueSpecListOf<S extends ListValueSpecType>(
t: ValueSpec,
s: S,
): t is ValueSpecListOf<S> & { spec: ListValueSpecOf<S> } {
return "spec" in t && t.spec.type === s
return 'spec' in t && t.spec.type === s
}

View File

@@ -1,16 +1,16 @@
import { InputSpec } from "./input/builder"
import { ExtractInputSpecType } from "./input/builder/inputSpec"
import * as T from "../types"
import { once } from "../util"
import { InitScript } from "../inits"
import { Parser } from "ts-matches"
import { InputSpec } from './input/builder'
import { ExtractInputSpecType } from './input/builder/inputSpec'
import * as T from '../types'
import { once } from '../util'
import { InitScript } from '../inits'
import { Parser } from 'ts-matches'
type MaybeInputSpec<Type> = {} extends Type ? null : InputSpec<Type>
export type Run<A extends Record<string, any>> = (options: {
effects: T.Effects
input: A
spec: T.inputSpecTypes.InputSpec
}) => Promise<(T.ActionResult & { version: "1" }) | null | void | undefined>
}) => Promise<(T.ActionResult & { version: '1' }) | null | void | undefined>
export type GetInput<A extends Record<string, any>> = (options: {
effects: T.Effects
}) => Promise<null | void | undefined | T.DeepPartial<A>>
@@ -65,7 +65,7 @@ export class Action<Id extends T.ActionId, Type extends Record<string, any>>
InputSpecType extends InputSpec<Record<string, any>>,
>(
id: Id,
metadata: MaybeFn<Omit<T.ActionMetadata, "hasInput">>,
metadata: MaybeFn<Omit<T.ActionMetadata, 'hasInput'>>,
inputSpec: InputSpecType,
getInput: GetInput<ExtractInputSpecType<InputSpecType>>,
run: Run<ExtractInputSpecType<InputSpecType>>,
@@ -80,7 +80,7 @@ export class Action<Id extends T.ActionId, Type extends Record<string, any>>
}
static withoutInput<Id extends T.ActionId>(
id: Id,
metadata: MaybeFn<Omit<T.ActionMetadata, "hasInput">>,
metadata: MaybeFn<Omit<T.ActionMetadata, 'hasInput'>>,
run: Run<{}>,
): Action<Id, {}> {
return new Action(
@@ -156,7 +156,7 @@ export class Actions<
}
addAction<A extends Action<T.ActionId, any>>(
action: A, // TODO: prevent duplicates
): Actions<AllActions & { [id in A["id"]]: A }> {
): Actions<AllActions & { [id in A['id']]: A }> {
return new Actions({ ...this.actions, [action.id]: action })
}
async init(effects: T.Effects): Promise<void> {