@datasworn-community/build-tools 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +10 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/datasworn-build.d.ts +3 -0
- package/dist/cli/datasworn-build.d.ts.map +1 -0
- package/dist/cli/datasworn-build.js +33 -0
- package/dist/cli/datasworn-build.js.map +1 -0
- package/dist/cli/datasworn-migrate.d.ts +3 -0
- package/dist/cli/datasworn-migrate.d.ts.map +1 -0
- package/dist/cli/datasworn-migrate.js +26 -0
- package/dist/cli/datasworn-migrate.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +10 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/rules-package-builder.d.ts +22 -0
- package/dist/rules-package-builder.d.ts.map +1 -0
- package/dist/rules-package-builder.js +84 -0
- package/dist/rules-package-builder.js.map +1 -0
- package/dist/schema.d.ts +8 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +18 -0
- package/dist/schema.js.map +1 -0
- package/dist/validators.d.ts +8 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +35 -0
- package/dist/validators.js.map +1 -0
- package/package.json +60 -0
- package/schema-source/schema/Assets.ts +66 -0
- package/schema-source/schema/Atlas.ts +20 -0
- package/schema-source/schema/Defs.ts +126 -0
- package/schema-source/schema/DelveSites.ts +3 -0
- package/schema-source/schema/Entities.ts +1 -0
- package/schema-source/schema/Generic.ts +18 -0
- package/schema-source/schema/Moves.ts +1 -0
- package/schema-source/schema/Npcs.ts +80 -0
- package/schema-source/schema/Oracles.ts +20 -0
- package/schema-source/schema/Rarities.ts +36 -0
- package/schema-source/schema/Root.ts +94 -0
- package/schema-source/schema/Rules.ts +61 -0
- package/schema-source/schema/RulesPackages.ts +135 -0
- package/schema-source/schema/Symbols.ts +30 -0
- package/schema-source/schema/Truths.ts +53 -0
- package/schema-source/schema/Utils.ts +21 -0
- package/schema-source/schema/assets/Ability.ts +79 -0
- package/schema-source/schema/assets/Enhancement.ts +62 -0
- package/schema-source/schema/assets/Fields.ts +285 -0
- package/schema-source/schema/assets/common.ts +29 -0
- package/schema-source/schema/common/Fields.ts +327 -0
- package/schema-source/schema/common/Id.ts +64 -0
- package/schema-source/schema/common/Inputs.ts +374 -0
- package/schema-source/schema/common/Metadata.ts +142 -0
- package/schema-source/schema/common/Player.ts +17 -0
- package/schema-source/schema/common/Progress.ts +63 -0
- package/schema-source/schema/common/Range.ts +53 -0
- package/schema-source/schema/common/RollableValues.ts +152 -0
- package/schema-source/schema/common/Rolls.ts +108 -0
- package/schema-source/schema/common/Text.ts +95 -0
- package/schema-source/schema/common/index.ts +16 -0
- package/schema-source/schema/delve/DelveSite.ts +121 -0
- package/schema-source/schema/delve/DelveSiteDomain.ts +136 -0
- package/schema-source/schema/delve/DelveSiteTheme.ts +97 -0
- package/schema-source/schema/delve/common.ts +32 -0
- package/schema-source/schema/entities/EntityPrompt.ts +13 -0
- package/schema-source/schema/entities/index.ts +1 -0
- package/schema-source/schema/generic/CollectableNode.ts +75 -0
- package/schema-source/schema/generic/CollectionNode.ts +145 -0
- package/schema-source/schema/generic/Dictionary.ts +39 -0
- package/schema-source/schema/generic/EmbedOnlyNode.ts +19 -0
- package/schema-source/schema/generic/Enhance.ts +38 -0
- package/schema-source/schema/generic/IdNode.ts +40 -0
- package/schema-source/schema/generic/Mixin.ts +11 -0
- package/schema-source/schema/generic/NonCollectableNode.ts +38 -0
- package/schema-source/schema/generic/PrimaryTypeNode.ts +115 -0
- package/schema-source/schema/generic/SourcedNode.ts +51 -0
- package/schema-source/schema/index.ts +23 -0
- package/schema-source/schema/moves/ActionRoll.ts +152 -0
- package/schema-source/schema/moves/EmbeddedMove.ts +24 -0
- package/schema-source/schema/moves/Move.ts +26 -0
- package/schema-source/schema/moves/MoveCategory.ts +6 -0
- package/schema-source/schema/moves/MoveEnhancement.ts +29 -0
- package/schema-source/schema/moves/ProgressRoll.ts +184 -0
- package/schema-source/schema/moves/Trigger.ts +193 -0
- package/schema-source/schema/moves/common.ts +136 -0
- package/schema-source/schema/moves/index.ts +13 -0
- package/schema-source/schema/moves/utils.ts +132 -0
- package/schema-source/schema/object_type/ObjectType.ts +23 -0
- package/schema-source/schema/oracles/EmbeddedOracleRollable.ts +28 -0
- package/schema-source/schema/oracles/OracleCollection.ts +140 -0
- package/schema-source/schema/oracles/OracleRollable.ts +207 -0
- package/schema-source/schema/oracles/Table.ts +94 -0
- package/schema-source/schema/oracles/TableRow.ts +141 -0
- package/schema-source/schema/root/NiceSchema.ts +15 -0
- package/schema-source/schema/root/Root.ts +41 -0
- package/schema-source/schema/root/SchemaTransform.ts +89 -0
- package/schema-source/schema/root/SourceData.ts +151 -0
- package/schema-source/schema/rules/ConditionMeterRule.ts +25 -0
- package/schema-source/schema/rules/ImpactRule.ts +54 -0
- package/schema-source/schema/rules/SpecialTrackRule.ts +29 -0
- package/schema-source/schema/rules/StatRule.ts +23 -0
- package/schema-source/schema/rules/TagRule.ts +176 -0
- package/schema-source/schema/rules/TagSchema.ts +161 -0
- package/schema-source/schema/tags/canonicalTags.ts +29 -0
- package/schema-source/schema/tools/build/index.ts +48 -0
- package/schema-source/schema/tools/i18n/index.ts +9 -0
- package/schema-source/schema/utils/Computed.ts +82 -0
- package/schema-source/schema/utils/DiscriminatedUnion.ts +255 -0
- package/schema-source/schema/utils/EmbeddedNode.ts +172 -0
- package/schema-source/schema/utils/ExtractLiteralFromEnum.ts +22 -0
- package/schema-source/schema/utils/FlatIntersect.ts +107 -0
- package/schema-source/schema/utils/IfThen.ts +50 -0
- package/schema-source/schema/utils/MergeEnumSchemas.ts +16 -0
- package/schema-source/schema/utils/NonNullable.ts +6 -0
- package/schema-source/schema/utils/Nullable.ts +29 -0
- package/schema-source/schema/utils/ObjectLiteral.ts +16 -0
- package/schema-source/schema/utils/ObjectProperties.ts +4 -0
- package/schema-source/schema/utils/OmitMeta.ts +22 -0
- package/schema-source/schema/utils/OmitOptional.ts +27 -0
- package/schema-source/schema/utils/PartialExcept.ts +26 -0
- package/schema-source/schema/utils/SetNullable.ts +44 -0
- package/schema-source/schema/utils/SetOptional.ts +48 -0
- package/schema-source/schema/utils/UnionEnum.ts +82 -0
- package/schema-source/schema/utils/UnionEnumFromRecord.ts +36 -0
- package/schema-source/schema/utils/UnionOneOf.ts +80 -0
- package/schema-source/schema/utils/generateRulesetSchemas.ts +204 -0
- package/schema-source/schema/utils/string.ts +15 -0
- package/schema-source/schema/utils/typebox.ts +141 -0
- package/schema-source/scripts/const.ts +55 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type TObject, type TOmit, Type } from '@sinclair/typebox'
|
|
2
|
+
import { IdKey, SourceInfoKey } from '../../scripts/const.js'
|
|
3
|
+
|
|
4
|
+
const MetaKeys = [
|
|
5
|
+
IdKey,
|
|
6
|
+
SourceInfoKey,
|
|
7
|
+
'rendering',
|
|
8
|
+
'name',
|
|
9
|
+
'suggestions',
|
|
10
|
+
'canonical_name',
|
|
11
|
+
'color'
|
|
12
|
+
] as const
|
|
13
|
+
type MetaKeys = (typeof MetaKeys)[number]
|
|
14
|
+
/**
|
|
15
|
+
* Omits common metadata and localization keys.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export function OmitMeta<T extends TObject>(schema: T) {
|
|
19
|
+
return Type.Omit(schema, MetaKeys)
|
|
20
|
+
}
|
|
21
|
+
export type OmitMeta<T> = Omit<T, MetaKeys>
|
|
22
|
+
export type TOmitMeta<T extends TObject> = TOmit<T, MetaKeys[]>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CloneType,
|
|
3
|
+
type ObjectOptions,
|
|
4
|
+
type TObject,
|
|
5
|
+
type TOptional,
|
|
6
|
+
type TSchema,
|
|
7
|
+
Type,
|
|
8
|
+
TypeGuard
|
|
9
|
+
} from '@sinclair/typebox'
|
|
10
|
+
import { omitBy } from 'lodash-es'
|
|
11
|
+
import type { ConditionalExcept, OptionalKeysOf } from 'type-fest'
|
|
12
|
+
|
|
13
|
+
export type OmitOptional<T extends object> = Omit<T, OptionalKeysOf<T>>
|
|
14
|
+
|
|
15
|
+
export type TOmitOptional<T extends TObject> = TObject<
|
|
16
|
+
ConditionalExcept<T['properties'], TOptional<TSchema>>
|
|
17
|
+
>
|
|
18
|
+
export function OmitOptional<T extends TObject>(
|
|
19
|
+
schema: T,
|
|
20
|
+
options: ObjectOptions = {}
|
|
21
|
+
) {
|
|
22
|
+
const base = CloneType(schema)
|
|
23
|
+
|
|
24
|
+
base.properties = omitBy(base.properties, (v) => TypeGuard.IsOptional(v))
|
|
25
|
+
|
|
26
|
+
return Type.Object(base.properties, options) as TOmitOptional<T>
|
|
27
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type SchemaOptions,
|
|
3
|
+
type Static,
|
|
4
|
+
type TObject,
|
|
5
|
+
Type
|
|
6
|
+
} from '@sinclair/typebox'
|
|
7
|
+
import { Assign, type TAssign } from './FlatIntersect.js'
|
|
8
|
+
|
|
9
|
+
export type PartialExcept<T, K extends keyof T> = Pick<T, K> &
|
|
10
|
+
Partial<Omit<T, K>>
|
|
11
|
+
|
|
12
|
+
export type TPartialExcept<
|
|
13
|
+
T extends TObject,
|
|
14
|
+
_K extends (keyof Static<T>)[]
|
|
15
|
+
> = TAssign<TObject, TObject> // Simplified due to TComputed type issues
|
|
16
|
+
/** Make everything optional except for the provided keys */
|
|
17
|
+
export function PartialExcept<
|
|
18
|
+
T extends TObject,
|
|
19
|
+
K extends Array<keyof Static<T>>
|
|
20
|
+
>(schema: T, requiredKeys: [...K], options: SchemaOptions = {}) {
|
|
21
|
+
return Assign(
|
|
22
|
+
Type.Pick(schema, requiredKeys) as TObject,
|
|
23
|
+
Type.Partial(Type.Omit(schema, requiredKeys)) as TObject,
|
|
24
|
+
options
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CloneType,
|
|
3
|
+
type ObjectOptions,
|
|
4
|
+
type Static,
|
|
5
|
+
type TObject
|
|
6
|
+
} from '@sinclair/typebox'
|
|
7
|
+
import { Nullable, type TNullable } from './Nullable.js'
|
|
8
|
+
import type { ObjectProperties } from './ObjectProperties.js'
|
|
9
|
+
|
|
10
|
+
export function SetNullable<
|
|
11
|
+
T extends TObject,
|
|
12
|
+
K extends Array<keyof Static<T>>
|
|
13
|
+
>(
|
|
14
|
+
schema: T,
|
|
15
|
+
keys: [...K],
|
|
16
|
+
{
|
|
17
|
+
defaultToNull = false,
|
|
18
|
+
...options
|
|
19
|
+
}: ObjectOptions & { defaultToNull?: boolean } = {}
|
|
20
|
+
) {
|
|
21
|
+
// @ts-expect-error
|
|
22
|
+
const base = CloneType(schema, options) as TSetNullable<T, K>
|
|
23
|
+
|
|
24
|
+
const nullableOptions: ObjectOptions = {}
|
|
25
|
+
|
|
26
|
+
if (defaultToNull) nullableOptions.default = null
|
|
27
|
+
|
|
28
|
+
for (const key of keys as Array<keyof T['properties']>) {
|
|
29
|
+
if (!(key in base.properties)) continue
|
|
30
|
+
// @ts-expect-error
|
|
31
|
+
base.properties[key] = Nullable(base.properties[key], nullableOptions)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return base
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type TSetNullable<
|
|
38
|
+
T extends TObject,
|
|
39
|
+
K extends Array<keyof Static<T>>
|
|
40
|
+
> = TObject<
|
|
41
|
+
Omit<ObjectProperties<T>, K[number]> & {
|
|
42
|
+
[P in K[number]]: TNullable<ObjectProperties<T>[P]>
|
|
43
|
+
}
|
|
44
|
+
>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CloneType,
|
|
3
|
+
type ObjectOptions,
|
|
4
|
+
type Static,
|
|
5
|
+
type TObject,
|
|
6
|
+
Type,
|
|
7
|
+
TypeGuard
|
|
8
|
+
} from '@sinclair/typebox'
|
|
9
|
+
import { omit } from 'lodash-es'
|
|
10
|
+
import type * as TypeFest from 'type-fest'
|
|
11
|
+
import type { TAssign } from './FlatIntersect.js'
|
|
12
|
+
|
|
13
|
+
export type SetOptional<
|
|
14
|
+
BaseType,
|
|
15
|
+
Keys extends keyof BaseType
|
|
16
|
+
> = TypeFest.SetOptional<BaseType, Keys>
|
|
17
|
+
|
|
18
|
+
export type TSetOptional<
|
|
19
|
+
T extends TObject,
|
|
20
|
+
_K extends (keyof Static<T>)[]
|
|
21
|
+
> = TAssign<TObject, TObject> // Simplified due to TComputed type issues
|
|
22
|
+
/** Make the provided keys optional */
|
|
23
|
+
export function SetOptional<
|
|
24
|
+
T extends TObject,
|
|
25
|
+
K extends Array<keyof Static<T>>
|
|
26
|
+
>(schema: T, optionalKeys: [...K], options: ObjectOptions = {}) {
|
|
27
|
+
const base = omit(CloneType(schema), ['$id']) as T
|
|
28
|
+
|
|
29
|
+
const toRemove: string[] = []
|
|
30
|
+
|
|
31
|
+
for (const k of optionalKeys as string[]) {
|
|
32
|
+
// skip if no such prop exists
|
|
33
|
+
if (base.properties[k] == null) continue
|
|
34
|
+
// skip if it's already optional
|
|
35
|
+
if (TypeGuard.IsOptional(base.properties[k])) continue
|
|
36
|
+
base.properties[k] = Type.Optional(base.properties[k])
|
|
37
|
+
// flag key for removal from "required" array
|
|
38
|
+
toRemove.push(k)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (Array.isArray(base.required))
|
|
42
|
+
base.required = base.required.filter((k) => toRemove.includes(k))
|
|
43
|
+
|
|
44
|
+
return Type.Object(base.properties, {
|
|
45
|
+
...omit(base, ['properties', 'required']),
|
|
46
|
+
...options
|
|
47
|
+
})
|
|
48
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// adapted from https://github.com/sinclairzx81/typebox/blob/master/examples/prototypes/union-enum.ts
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CloneType,
|
|
5
|
+
Hint,
|
|
6
|
+
Kind,
|
|
7
|
+
type SchemaOptions,
|
|
8
|
+
type TEnum,
|
|
9
|
+
type TSchema,
|
|
10
|
+
Type,
|
|
11
|
+
TypeRegistry
|
|
12
|
+
} from '@sinclair/typebox'
|
|
13
|
+
import { isInteger, omit, set } from 'lodash-es'
|
|
14
|
+
import { JsonTypeDef } from '../Symbols.js'
|
|
15
|
+
|
|
16
|
+
export const EnumDescription = Symbol('EnumDescription')
|
|
17
|
+
export const Description = Symbol('Description')
|
|
18
|
+
|
|
19
|
+
export interface TUnionEnum<
|
|
20
|
+
T extends string[] | number[] | readonly string[] | readonly number[] =
|
|
21
|
+
| string[]
|
|
22
|
+
| number[]
|
|
23
|
+
| readonly string[]
|
|
24
|
+
| readonly number[]
|
|
25
|
+
> extends TSchema {
|
|
26
|
+
[Kind]: 'UnionEnum'
|
|
27
|
+
[EnumDescription]: Record<T[number], string>
|
|
28
|
+
[Description]?: string | undefined
|
|
29
|
+
static: T[number]
|
|
30
|
+
enum: [...T]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function UnionEnum<
|
|
34
|
+
T extends string[] | number[] | readonly string[] | readonly number[]
|
|
35
|
+
>(literals: T, options: SchemaOptions = {}) {
|
|
36
|
+
if (!TypeRegistry.Has('UnionEnum'))
|
|
37
|
+
TypeRegistry.Set('UnionEnum', UnionEnumCheck)
|
|
38
|
+
|
|
39
|
+
const result = {
|
|
40
|
+
...options,
|
|
41
|
+
[Kind]: 'UnionEnum',
|
|
42
|
+
enum: [...literals]
|
|
43
|
+
} as TUnionEnum<T>
|
|
44
|
+
|
|
45
|
+
if (result.enum.every(isInteger) && result[JsonTypeDef])
|
|
46
|
+
set(result[JsonTypeDef], 'metadata.typescriptType', literals.join(' | '))
|
|
47
|
+
return result
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function TUnionEnum(schema: any): schema is TUnionEnum {
|
|
51
|
+
if (!Array.isArray(schema?.enum)) return false
|
|
52
|
+
return (
|
|
53
|
+
schema.enum.every((item: any) => typeof item === 'string') ||
|
|
54
|
+
schema.enum.every((item: any) => typeof item === 'number')
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function UnionEnumCheck(schema: TUnionEnum<any>, value: unknown) {
|
|
59
|
+
return schema.enum.includes(value as string | number)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function ToEnum<T extends TUnionEnum>(schema: T) {
|
|
63
|
+
const anyOf = schema.enum.map((value) =>
|
|
64
|
+
Type.Literal(value, { description: schema[EnumDescription]?.[value] })
|
|
65
|
+
)
|
|
66
|
+
const options = omit(CloneType(schema), [
|
|
67
|
+
Kind,
|
|
68
|
+
EnumDescription,
|
|
69
|
+
Description,
|
|
70
|
+
'static',
|
|
71
|
+
'enum'
|
|
72
|
+
])
|
|
73
|
+
|
|
74
|
+
const result = Type.Union(anyOf, {
|
|
75
|
+
...options,
|
|
76
|
+
description: schema[Description],
|
|
77
|
+
|
|
78
|
+
[Hint]: 'Enum'
|
|
79
|
+
}) as unknown as TEnum
|
|
80
|
+
|
|
81
|
+
return result
|
|
82
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { SchemaOptions } from '@sinclair/typebox'
|
|
2
|
+
import { map } from 'lodash-es'
|
|
3
|
+
import {
|
|
4
|
+
Description,
|
|
5
|
+
EnumDescription,
|
|
6
|
+
type TUnionEnum,
|
|
7
|
+
UnionEnum
|
|
8
|
+
} from './UnionEnum.js'
|
|
9
|
+
|
|
10
|
+
export function UnionEnumFromRecord<K extends string>(
|
|
11
|
+
entries: Record<K, string>,
|
|
12
|
+
options?: SchemaOptions
|
|
13
|
+
): TUnionEnum<K[]>
|
|
14
|
+
export function UnionEnumFromRecord<T extends string[] | number[]>(
|
|
15
|
+
entries: Record<T[number], string>,
|
|
16
|
+
options: SchemaOptions = {}
|
|
17
|
+
): TUnionEnum<T> {
|
|
18
|
+
const literals = Object.keys(entries).map((k) =>
|
|
19
|
+
Number.isInteger(Number(k)) ? Number(k) : k
|
|
20
|
+
) as T
|
|
21
|
+
|
|
22
|
+
let description = map(
|
|
23
|
+
entries,
|
|
24
|
+
(description, literal) => ` - \`${literal?.toString()}\`: ${description}`
|
|
25
|
+
).join('\n')
|
|
26
|
+
|
|
27
|
+
if (options.description)
|
|
28
|
+
description = `${options.description}\n\n${description}`
|
|
29
|
+
|
|
30
|
+
return UnionEnum(literals, {
|
|
31
|
+
[Description]: options.description,
|
|
32
|
+
[EnumDescription]: entries,
|
|
33
|
+
...options,
|
|
34
|
+
description
|
|
35
|
+
})
|
|
36
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** adapted from https://github.com/sinclairzx81/typebox/blob/master/examples/prototypes/union-oneof.ts */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Kind,
|
|
5
|
+
type SchemaOptions,
|
|
6
|
+
type Static,
|
|
7
|
+
type TSchema,
|
|
8
|
+
TypeRegistry
|
|
9
|
+
} from '@sinclair/typebox'
|
|
10
|
+
import { Value } from '@sinclair/typebox/value'
|
|
11
|
+
import { Members } from '../Symbols.js'
|
|
12
|
+
|
|
13
|
+
export interface TUnionOneOf<T extends TSchema[]> extends TSchema {
|
|
14
|
+
[Kind]: 'UnionOneOf'
|
|
15
|
+
static: { [K in keyof T]: Static<T[K]> }[number]
|
|
16
|
+
oneOf: T
|
|
17
|
+
[Members]: T // to make JTD inference easier
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** `[Experimental]` Creates a Union type with a `oneOf` schema representation */
|
|
21
|
+
export function UnionOneOf<T extends TSchema[]>(
|
|
22
|
+
oneOf: [...T],
|
|
23
|
+
options: SchemaOptions = {}
|
|
24
|
+
) {
|
|
25
|
+
function UnionOneOfCheck(schema: TUnionOneOf<TSchema[]>, value: unknown) {
|
|
26
|
+
return (
|
|
27
|
+
schema.oneOf.reduce(
|
|
28
|
+
(acc: number, schema: any) =>
|
|
29
|
+
Value.Check(schema, value) ? acc + 1 : acc,
|
|
30
|
+
0
|
|
31
|
+
) === 1
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
if (!TypeRegistry.Has('UnionOneOf'))
|
|
35
|
+
TypeRegistry.Set('UnionOneOf', UnionOneOfCheck)
|
|
36
|
+
return {
|
|
37
|
+
...options,
|
|
38
|
+
[Kind]: 'UnionOneOf',
|
|
39
|
+
[Members]: oneOf, // to make JTD inference easier
|
|
40
|
+
oneOf
|
|
41
|
+
} as TUnionOneOf<T>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// import { TypeSystem } from '@sinclair/typebox/system'
|
|
45
|
+
// import { Value } from '@sinclair/typebox/value'
|
|
46
|
+
// import { Members } from '../../../json-typedef/symbol.js'
|
|
47
|
+
|
|
48
|
+
// const UnionOneOfKind = 'UnionOneOf'
|
|
49
|
+
|
|
50
|
+
// TypeSystem.Type(UnionOneOfKind, UnionOneOfCheck)
|
|
51
|
+
|
|
52
|
+
// function UnionOneOfCheck(schema: TUnionOneOf<TSchema[]>, value: unknown) {
|
|
53
|
+
// return (
|
|
54
|
+
// schema.oneOf.reduce(
|
|
55
|
+
// (acc: number, schema: any) =>
|
|
56
|
+
// Value.Check(schema, value) ? acc + 1 : acc,
|
|
57
|
+
// 0
|
|
58
|
+
// ) === 1
|
|
59
|
+
// )
|
|
60
|
+
// }
|
|
61
|
+
|
|
62
|
+
// export interface TUnionOneOf<T extends TSchema[] = TSchema[]> extends TSchema {
|
|
63
|
+
// [Kind]: typeof UnionOneOfKind
|
|
64
|
+
// [Members]: [...T]
|
|
65
|
+
// static: Static<TUnion<T>>
|
|
66
|
+
// oneOf: T
|
|
67
|
+
// }
|
|
68
|
+
|
|
69
|
+
// /** Creates a Union type with a `oneOf` schema representation */
|
|
70
|
+
// export function UnionOneOf<T extends TSchema[]>(
|
|
71
|
+
// oneOf: [...T],
|
|
72
|
+
// options: SchemaOptions = {}
|
|
73
|
+
// ) {
|
|
74
|
+
// return {
|
|
75
|
+
// ...options,
|
|
76
|
+
// [Kind]: UnionOneOfKind,
|
|
77
|
+
// oneOf,
|
|
78
|
+
// [Members]: oneOf
|
|
79
|
+
// } as TUnionOneOf<T>
|
|
80
|
+
// }
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CloneType,
|
|
3
|
+
type SchemaOptions,
|
|
4
|
+
type TRef,
|
|
5
|
+
type TSchema,
|
|
6
|
+
Type
|
|
7
|
+
} from '@sinclair/typebox'
|
|
8
|
+
import {
|
|
9
|
+
forEach,
|
|
10
|
+
isEmpty,
|
|
11
|
+
isNull,
|
|
12
|
+
keyBy,
|
|
13
|
+
mapValues,
|
|
14
|
+
omit,
|
|
15
|
+
omitBy,
|
|
16
|
+
set
|
|
17
|
+
} from 'lodash-es'
|
|
18
|
+
import type { SetRequired, SnakeCase } from 'type-fest'
|
|
19
|
+
import * as Player from '../common/Player.js'
|
|
20
|
+
import { CollectableType, NonCollectableType, type Rules } from '../Rules.js'
|
|
21
|
+
import { pascalCase } from './string.js'
|
|
22
|
+
import { UnionEnum } from './UnionEnum.js'
|
|
23
|
+
|
|
24
|
+
/** Build-time configuration for a tag rule, used to generate TypeBox schemas. */
|
|
25
|
+
type TagRuleConfig =
|
|
26
|
+
| TagRuleConfigPrimitive
|
|
27
|
+
| TagRuleConfigEnum
|
|
28
|
+
| TagRuleConfigIdRef
|
|
29
|
+
|
|
30
|
+
interface TagRuleConfigBase {
|
|
31
|
+
description: string
|
|
32
|
+
node_types: (CollectableType | NonCollectableType)[] | null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface TagRuleConfigPrimitive extends TagRuleConfigBase {
|
|
36
|
+
value_type: 'boolean' | 'integer'
|
|
37
|
+
array?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface TagRuleConfigEnum extends TagRuleConfigBase {
|
|
41
|
+
value_type: 'enum'
|
|
42
|
+
enum: string[]
|
|
43
|
+
array?: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface TagRuleConfigIdRef extends TagRuleConfigBase {
|
|
47
|
+
value_type: CollectableType | NonCollectableType
|
|
48
|
+
wildcard?: boolean
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function generateRulesetSchemas(rulesPackage: string, rules: Rules) {
|
|
52
|
+
const ConditionMeterKey = UnionEnum(
|
|
53
|
+
Object.keys(rules.condition_meters),
|
|
54
|
+
omit(CloneType(Player.ConditionMeterKey), 'examples', 'type')
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
const StatKey = UnionEnum(
|
|
58
|
+
Object.keys(rules.stats),
|
|
59
|
+
omit(CloneType(Player.StatKey), 'examples', 'type')
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
ConditionMeterKey,
|
|
64
|
+
StatKey,
|
|
65
|
+
// NB: Rules.tags schema shape doesn't match TagRuleConfig yet; this function is experimental
|
|
66
|
+
...generateTagSchemas(
|
|
67
|
+
rulesPackage,
|
|
68
|
+
rules.tags as unknown as Record<SnakeCase<string>, TagRuleConfig>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function generateTagSchema(
|
|
74
|
+
rulesPackage: string,
|
|
75
|
+
tagKey: string,
|
|
76
|
+
tagRule: TagRuleConfig
|
|
77
|
+
): SetRequired<TSchema, '$id' | 'description'> {
|
|
78
|
+
const $id = `${pascalCase(rulesPackage) + pascalCase(tagKey)}Tag`
|
|
79
|
+
const { description } = tagRule
|
|
80
|
+
const options = { description, $id } as SetRequired<
|
|
81
|
+
SchemaOptions,
|
|
82
|
+
'$id' | 'description'
|
|
83
|
+
>
|
|
84
|
+
|
|
85
|
+
switch (tagRule.value_type) {
|
|
86
|
+
case 'boolean':
|
|
87
|
+
case 'integer': {
|
|
88
|
+
const base = (Type as any)[pascalCase(tagRule.value_type)]()
|
|
89
|
+
return tagRule.array
|
|
90
|
+
? (Type.Array(base, options) as any)
|
|
91
|
+
: ({ ...base, ...options } as any)
|
|
92
|
+
}
|
|
93
|
+
case 'enum': {
|
|
94
|
+
const base = UnionEnum(tagRule.enum)
|
|
95
|
+
return tagRule.array
|
|
96
|
+
? (Type.Array(base, options) as any)
|
|
97
|
+
: ({ ...base, ...options } as any)
|
|
98
|
+
}
|
|
99
|
+
case 'move':
|
|
100
|
+
case 'asset':
|
|
101
|
+
case 'oracle_rollable':
|
|
102
|
+
case 'atlas_entry':
|
|
103
|
+
case 'npc':
|
|
104
|
+
case 'delve_site':
|
|
105
|
+
case 'delve_site_theme':
|
|
106
|
+
case 'delve_site_domain':
|
|
107
|
+
case 'truth':
|
|
108
|
+
case 'rarity':
|
|
109
|
+
return tagRule.wildcard
|
|
110
|
+
? (Type.Array(Type.Ref(`${pascalCase(tagRule.value_type)}WildcardId`), {
|
|
111
|
+
description
|
|
112
|
+
}) as any)
|
|
113
|
+
: (Type.Ref(`${pascalCase(tagRule.value_type)}Id`, options) as any)
|
|
114
|
+
default: {
|
|
115
|
+
const _exhaustive: never = tagRule
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Unknown tag value type: ${(_exhaustive as TagRuleConfig).value_type}`
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const anyType = [...CollectableType.enum, ...NonCollectableType.enum]
|
|
124
|
+
|
|
125
|
+
function generateTagSchemas(
|
|
126
|
+
rulesPackage: SnakeCase<string>,
|
|
127
|
+
tags: Record<SnakeCase<string>, TagRuleConfig>
|
|
128
|
+
) {
|
|
129
|
+
const allowedTagProperties = Object.fromEntries(
|
|
130
|
+
anyType.map((k: CollectableType | NonCollectableType) => [
|
|
131
|
+
k,
|
|
132
|
+
{} as Record<SnakeCase<string>, TRef>
|
|
133
|
+
])
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
const tagSchemas: Record<string, TSchema> = {}
|
|
137
|
+
|
|
138
|
+
forEach(tags, (tag, tagKey) => {
|
|
139
|
+
const schema = generateTagSchema(rulesPackage, tagKey, tag)
|
|
140
|
+
tagSchemas[schema.$id] = schema
|
|
141
|
+
const pushTo = isNull(tag.node_types) ? anyType : tag.node_types
|
|
142
|
+
|
|
143
|
+
for (const objectType of pushTo) {
|
|
144
|
+
set(
|
|
145
|
+
allowedTagProperties,
|
|
146
|
+
[objectType, tagKey].join('.'),
|
|
147
|
+
Type.Optional(Type.Ref(schema))
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const allowedTagSchemas = omitBy(
|
|
153
|
+
keyBy(
|
|
154
|
+
mapValues(allowedTagProperties, (v, k) =>
|
|
155
|
+
Type.Object(v, {
|
|
156
|
+
$id: `${pascalCase(rulesPackage)}${pascalCase(k)}Tags`,
|
|
157
|
+
additionalProperties: true
|
|
158
|
+
})
|
|
159
|
+
),
|
|
160
|
+
(v) => v.$id
|
|
161
|
+
),
|
|
162
|
+
(v) => isEmpty(v.properties)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
const tagNamespacesSchema = keyBy(
|
|
166
|
+
mapValues(allowedTagSchemas, (v, k) =>
|
|
167
|
+
Type.Object(
|
|
168
|
+
{ [rulesPackage]: Type.Optional(Type.Ref(v)) },
|
|
169
|
+
{
|
|
170
|
+
additionalProperties: true,
|
|
171
|
+
$id: k.replace(pascalCase(rulesPackage), '')
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
),
|
|
175
|
+
(v) => v.$id
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
console.log(tagNamespacesSchema)
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
...tagNamespacesSchema,
|
|
182
|
+
...tagSchemas,
|
|
183
|
+
...allowedTagSchemas
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// TODO: generate dummy schemas so the overrides can be inserted later
|
|
188
|
+
|
|
189
|
+
console.log(
|
|
190
|
+
generateTagSchemas('sundered_isles', {
|
|
191
|
+
cursed_version_of: {
|
|
192
|
+
description: 'This oracle is the cursed version of another oracle.',
|
|
193
|
+
node_types: ['oracle_rollable'],
|
|
194
|
+
value_type: 'oracle_rollable',
|
|
195
|
+
wildcard: true
|
|
196
|
+
},
|
|
197
|
+
cursed_by: {
|
|
198
|
+
description: 'This oracle has a cursed version.',
|
|
199
|
+
node_types: ['oracle_rollable'],
|
|
200
|
+
value_type: 'oracle_rollable',
|
|
201
|
+
wildcard: false
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { camelCase } from 'lodash-es'
|
|
2
|
+
import type { PascalCase as PascalCaseNoDot } from 'type-fest'
|
|
3
|
+
import type { Join, Split } from '../../pkg-core/Utils/String.js'
|
|
4
|
+
|
|
5
|
+
export function capitalize<Str extends string = string>(str: Str) {
|
|
6
|
+
return (str[0].toLocaleUpperCase('en-US') + str.slice(1)) as Capitalize<Str>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type PascalCase<T extends string> = PascalCaseNoDot<
|
|
10
|
+
Join<Split<T, '.'>, '_'>
|
|
11
|
+
>
|
|
12
|
+
|
|
13
|
+
export function pascalCase<Str extends string = string>(str: Str) {
|
|
14
|
+
return capitalize(camelCase(str.split('.').join('_'))) as PascalCase<Str>
|
|
15
|
+
}
|