@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.
Files changed (134) hide show
  1. package/README.md +17 -0
  2. package/dist/cli/args.d.ts +3 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/args.js +10 -0
  5. package/dist/cli/args.js.map +1 -0
  6. package/dist/cli/datasworn-build.d.ts +3 -0
  7. package/dist/cli/datasworn-build.d.ts.map +1 -0
  8. package/dist/cli/datasworn-build.js +33 -0
  9. package/dist/cli/datasworn-build.js.map +1 -0
  10. package/dist/cli/datasworn-migrate.d.ts +3 -0
  11. package/dist/cli/datasworn-migrate.d.ts.map +1 -0
  12. package/dist/cli/datasworn-migrate.js +26 -0
  13. package/dist/cli/datasworn-migrate.js.map +1 -0
  14. package/dist/config.d.ts +3 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +10 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/index.d.ts +4 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +4 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/rules-package-builder.d.ts +22 -0
  23. package/dist/rules-package-builder.d.ts.map +1 -0
  24. package/dist/rules-package-builder.js +84 -0
  25. package/dist/rules-package-builder.js.map +1 -0
  26. package/dist/schema.d.ts +8 -0
  27. package/dist/schema.d.ts.map +1 -0
  28. package/dist/schema.js +18 -0
  29. package/dist/schema.js.map +1 -0
  30. package/dist/validators.d.ts +8 -0
  31. package/dist/validators.d.ts.map +1 -0
  32. package/dist/validators.js +35 -0
  33. package/dist/validators.js.map +1 -0
  34. package/package.json +60 -0
  35. package/schema-source/schema/Assets.ts +66 -0
  36. package/schema-source/schema/Atlas.ts +20 -0
  37. package/schema-source/schema/Defs.ts +126 -0
  38. package/schema-source/schema/DelveSites.ts +3 -0
  39. package/schema-source/schema/Entities.ts +1 -0
  40. package/schema-source/schema/Generic.ts +18 -0
  41. package/schema-source/schema/Moves.ts +1 -0
  42. package/schema-source/schema/Npcs.ts +80 -0
  43. package/schema-source/schema/Oracles.ts +20 -0
  44. package/schema-source/schema/Rarities.ts +36 -0
  45. package/schema-source/schema/Root.ts +94 -0
  46. package/schema-source/schema/Rules.ts +61 -0
  47. package/schema-source/schema/RulesPackages.ts +135 -0
  48. package/schema-source/schema/Symbols.ts +30 -0
  49. package/schema-source/schema/Truths.ts +53 -0
  50. package/schema-source/schema/Utils.ts +21 -0
  51. package/schema-source/schema/assets/Ability.ts +79 -0
  52. package/schema-source/schema/assets/Enhancement.ts +62 -0
  53. package/schema-source/schema/assets/Fields.ts +285 -0
  54. package/schema-source/schema/assets/common.ts +29 -0
  55. package/schema-source/schema/common/Fields.ts +327 -0
  56. package/schema-source/schema/common/Id.ts +64 -0
  57. package/schema-source/schema/common/Inputs.ts +374 -0
  58. package/schema-source/schema/common/Metadata.ts +142 -0
  59. package/schema-source/schema/common/Player.ts +17 -0
  60. package/schema-source/schema/common/Progress.ts +63 -0
  61. package/schema-source/schema/common/Range.ts +53 -0
  62. package/schema-source/schema/common/RollableValues.ts +152 -0
  63. package/schema-source/schema/common/Rolls.ts +108 -0
  64. package/schema-source/schema/common/Text.ts +95 -0
  65. package/schema-source/schema/common/index.ts +16 -0
  66. package/schema-source/schema/delve/DelveSite.ts +121 -0
  67. package/schema-source/schema/delve/DelveSiteDomain.ts +136 -0
  68. package/schema-source/schema/delve/DelveSiteTheme.ts +97 -0
  69. package/schema-source/schema/delve/common.ts +32 -0
  70. package/schema-source/schema/entities/EntityPrompt.ts +13 -0
  71. package/schema-source/schema/entities/index.ts +1 -0
  72. package/schema-source/schema/generic/CollectableNode.ts +75 -0
  73. package/schema-source/schema/generic/CollectionNode.ts +145 -0
  74. package/schema-source/schema/generic/Dictionary.ts +39 -0
  75. package/schema-source/schema/generic/EmbedOnlyNode.ts +19 -0
  76. package/schema-source/schema/generic/Enhance.ts +38 -0
  77. package/schema-source/schema/generic/IdNode.ts +40 -0
  78. package/schema-source/schema/generic/Mixin.ts +11 -0
  79. package/schema-source/schema/generic/NonCollectableNode.ts +38 -0
  80. package/schema-source/schema/generic/PrimaryTypeNode.ts +115 -0
  81. package/schema-source/schema/generic/SourcedNode.ts +51 -0
  82. package/schema-source/schema/index.ts +23 -0
  83. package/schema-source/schema/moves/ActionRoll.ts +152 -0
  84. package/schema-source/schema/moves/EmbeddedMove.ts +24 -0
  85. package/schema-source/schema/moves/Move.ts +26 -0
  86. package/schema-source/schema/moves/MoveCategory.ts +6 -0
  87. package/schema-source/schema/moves/MoveEnhancement.ts +29 -0
  88. package/schema-source/schema/moves/ProgressRoll.ts +184 -0
  89. package/schema-source/schema/moves/Trigger.ts +193 -0
  90. package/schema-source/schema/moves/common.ts +136 -0
  91. package/schema-source/schema/moves/index.ts +13 -0
  92. package/schema-source/schema/moves/utils.ts +132 -0
  93. package/schema-source/schema/object_type/ObjectType.ts +23 -0
  94. package/schema-source/schema/oracles/EmbeddedOracleRollable.ts +28 -0
  95. package/schema-source/schema/oracles/OracleCollection.ts +140 -0
  96. package/schema-source/schema/oracles/OracleRollable.ts +207 -0
  97. package/schema-source/schema/oracles/Table.ts +94 -0
  98. package/schema-source/schema/oracles/TableRow.ts +141 -0
  99. package/schema-source/schema/root/NiceSchema.ts +15 -0
  100. package/schema-source/schema/root/Root.ts +41 -0
  101. package/schema-source/schema/root/SchemaTransform.ts +89 -0
  102. package/schema-source/schema/root/SourceData.ts +151 -0
  103. package/schema-source/schema/rules/ConditionMeterRule.ts +25 -0
  104. package/schema-source/schema/rules/ImpactRule.ts +54 -0
  105. package/schema-source/schema/rules/SpecialTrackRule.ts +29 -0
  106. package/schema-source/schema/rules/StatRule.ts +23 -0
  107. package/schema-source/schema/rules/TagRule.ts +176 -0
  108. package/schema-source/schema/rules/TagSchema.ts +161 -0
  109. package/schema-source/schema/tags/canonicalTags.ts +29 -0
  110. package/schema-source/schema/tools/build/index.ts +48 -0
  111. package/schema-source/schema/tools/i18n/index.ts +9 -0
  112. package/schema-source/schema/utils/Computed.ts +82 -0
  113. package/schema-source/schema/utils/DiscriminatedUnion.ts +255 -0
  114. package/schema-source/schema/utils/EmbeddedNode.ts +172 -0
  115. package/schema-source/schema/utils/ExtractLiteralFromEnum.ts +22 -0
  116. package/schema-source/schema/utils/FlatIntersect.ts +107 -0
  117. package/schema-source/schema/utils/IfThen.ts +50 -0
  118. package/schema-source/schema/utils/MergeEnumSchemas.ts +16 -0
  119. package/schema-source/schema/utils/NonNullable.ts +6 -0
  120. package/schema-source/schema/utils/Nullable.ts +29 -0
  121. package/schema-source/schema/utils/ObjectLiteral.ts +16 -0
  122. package/schema-source/schema/utils/ObjectProperties.ts +4 -0
  123. package/schema-source/schema/utils/OmitMeta.ts +22 -0
  124. package/schema-source/schema/utils/OmitOptional.ts +27 -0
  125. package/schema-source/schema/utils/PartialExcept.ts +26 -0
  126. package/schema-source/schema/utils/SetNullable.ts +44 -0
  127. package/schema-source/schema/utils/SetOptional.ts +48 -0
  128. package/schema-source/schema/utils/UnionEnum.ts +82 -0
  129. package/schema-source/schema/utils/UnionEnumFromRecord.ts +36 -0
  130. package/schema-source/schema/utils/UnionOneOf.ts +80 -0
  131. package/schema-source/schema/utils/generateRulesetSchemas.ts +204 -0
  132. package/schema-source/schema/utils/string.ts +15 -0
  133. package/schema-source/schema/utils/typebox.ts +141 -0
  134. 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
+ }