@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,94 @@
1
+ import {
2
+ type ObjectOptions,
3
+ type Static,
4
+ type TObject,
5
+ type TRefUnsafe,
6
+ type TString,
7
+ Type
8
+ } from '@sinclair/typebox'
9
+ import { omit } from 'lodash-es'
10
+ import { Label, MarkdownString } from '../common/Text.js'
11
+ import { FlatIntersect } from '../utils/FlatIntersect.js'
12
+ import type { OracleRollableRowText3 } from './TableRow.js'
13
+
14
+ // metadata relevant to presenting a table
15
+ export const TableMeta = Type.Object({
16
+ recommended_rolls: Type.Optional(
17
+ Type.Object({
18
+ min: Type.Integer({ default: 1 }),
19
+ max: Type.Integer({ default: 1 })
20
+ })
21
+ ),
22
+
23
+ summary: Type.Optional(
24
+ Type.Ref(MarkdownString, {
25
+ description:
26
+ 'A brief summary of the oracle table\'s intended usage, no more than a few sentences in length. This is intended for use in application tooltips and similar sorts of hints. Longer text should use the "description" key instead.'
27
+ })
28
+ ),
29
+ description: Type.Optional(
30
+ Type.Ref(MarkdownString, {
31
+ description:
32
+ "A longer description of the oracle table's intended usage, which might include multiple paragraphs. If it's only a couple sentences, use the `summary` key instead."
33
+ })
34
+ )
35
+ })
36
+
37
+ export function TableMixin<_OracleRow extends TObject>(
38
+ column_labels: ReturnType<typeof ColumnLabels>
39
+ ) {
40
+ return FlatIntersect([
41
+ TableMeta,
42
+ Type.Object({
43
+ column_labels
44
+ })
45
+ ])
46
+ }
47
+ function ColumnLabels<
48
+ TextKeys extends [...(keyof Static<typeof OracleRollableRowText3>)[]]
49
+ >(textKeys: TextKeys, options: ObjectOptions = {}) {
50
+ const props = {
51
+ roll: Type.Ref(Label)
52
+ } as Record<'roll' | TextKeys[number], TRefUnsafe<TString>>
53
+ for (const k of textKeys) props[k as keyof typeof props] = Type.Ref(Label)
54
+
55
+ return Type.Object(props, options)
56
+ }
57
+
58
+ export const TextColumnLabels = ColumnLabels(['text'], {
59
+ title: 'TextColumnLabels',
60
+ default: { roll: 'Roll', text: 'Result' }
61
+ })
62
+ export const Text2ColumnLabels = ColumnLabels(['text', 'text2'], {
63
+ title: 'Text2ColumnLabels',
64
+ default: {
65
+ roll: 'Roll',
66
+ text: 'Result',
67
+ text2: 'Details'
68
+ }
69
+ })
70
+
71
+ export const Text3ColumnLabels = ColumnLabels(['text', 'text2', 'text3'], {
72
+ title: 'Text3ColumnLabels'
73
+ })
74
+
75
+ export const SharedRollsLabels = Type.Omit(TextColumnLabels, ['text'], {
76
+ default: omit(TextColumnLabels.default, ['text']),
77
+ description:
78
+ 'Provides column labels for this table. The `roll` key refers to the roll column showing the dice range (`min` and `max` on each table row). For all other column labels, see the `name` property of each child `OracleColumn`.',
79
+ title: 'SharedRollsLabels'
80
+ })
81
+ export const SharedTextLabels = Type.Omit(TextColumnLabels, ['roll'], {
82
+ default: omit(TextColumnLabels.default, ['roll']),
83
+ title: 'SharedTextLabels'
84
+ })
85
+
86
+ export const SharedText2Labels = Type.Omit(Text2ColumnLabels, ['roll'], {
87
+ default: omit(Text2ColumnLabels.default, ['roll']),
88
+ title: 'SharedText2Labels'
89
+ })
90
+
91
+ export const SharedText3Labels = Type.Omit(Text3ColumnLabels, ['roll'], {
92
+ default: omit(Text3ColumnLabels.default, ['roll']),
93
+ title: 'SharedText3Labels'
94
+ })
@@ -0,0 +1,141 @@
1
+ import {
2
+ CloneType,
3
+ type Static,
4
+ type TObject,
5
+ type TRefUnsafe,
6
+ type TSchema,
7
+ Type
8
+ } from '@sinclair/typebox'
9
+ import Id from '../common/Id.js'
10
+ import * as Metadata from '../common/Metadata.js'
11
+ import { DiceRange, StaticDiceRange } from '../common/Range.js'
12
+ import * as Rolls from '../common/Rolls.js'
13
+ import * as Text from '../common/Text.js'
14
+ import { JsonTypeDef } from '../Symbols.js'
15
+ import * as Utils from '../Utils.js'
16
+ import { Assign } from '../utils/FlatIntersect.js'
17
+ import type { ObjectProperties } from '../utils/ObjectProperties.js'
18
+ import { type PickByType, setDescriptions } from '../utils/typebox.js'
19
+
20
+ const TableRowBase = Type.Object({
21
+ text: Type.Ref(Text.MarkdownString, {
22
+ description: 'The primary text content of this row.'
23
+ }),
24
+ icon: Type.Optional(Type.Ref(Metadata.SvgImageUrl)),
25
+ oracle_rolls: Type.Optional(
26
+ Type.Array(Type.Ref(Rolls.OracleRoll), {
27
+ description: 'Further oracle rolls prompted by this table row.'
28
+ })
29
+ ),
30
+ suggestions: Type.Optional(Type.Ref(Metadata.Suggestions)),
31
+ embed_table: Type.Optional(
32
+ Type.Ref(Id.OracleRollableId, {
33
+ releaseStage: 'experimental',
34
+ description:
35
+ 'Hints that the identified table should be rendered inside this table row.'
36
+ })
37
+ ),
38
+ template: Type.Optional(
39
+ Type.Ref(Rolls.OracleRollTemplate, { releaseStage: 'experimental' })
40
+ ),
41
+ _i18n: Type.Optional(
42
+ Type.Ref(Text.I18nHints, {
43
+ releaseStage: 'experimental'
44
+ })
45
+ )
46
+ })
47
+
48
+ export const TableRowMixin = Assign(
49
+ TableRowBase,
50
+ Type.Object({
51
+ roll: Type.Ref(DiceRange),
52
+ tags: Type.Optional(Type.Ref('Tags')),
53
+ _id: Utils.Computed(Type.Ref('AnyOracleRollableRowId'))
54
+ })
55
+ )
56
+
57
+ export const TableRowNullableMixin = setDescriptions(
58
+ Utils.SetNullable(TableRowMixin, ['roll'], { defaultToNull: true }),
59
+ {
60
+ roll: '`null` represents an unrollable row, included only for rendering purposes.'
61
+ }
62
+ )
63
+
64
+ export type TTableRow<Roll extends TSchema = TSchema> = TObject<
65
+ ObjectProperties<typeof TableRowBase> & { roll: Roll }
66
+ >
67
+
68
+ type _TableRow<
69
+ Roll = { min: number; max: number } | null,
70
+ Props extends { roll: Roll } = { roll: Roll }
71
+ > = Props & Static<typeof TableRowBase>
72
+
73
+ export function StaticRowPartial(min: number, max: number) {
74
+ return Type.Object(
75
+ { roll: StaticDiceRange(min, max) },
76
+ { additionalProperties: true }
77
+ )
78
+ }
79
+
80
+ export const OracleRollableRowText = CloneType(TableRowNullableMixin, {
81
+ $id: 'OracleRollableRowText',
82
+ description: 'Represents a row in an oracle table, with a single text cell.'
83
+ })
84
+
85
+ export type OracleRollableRowText = Static<typeof OracleRollableRowText>
86
+
87
+ export const OracleRollableRowText2 = Assign(
88
+ TableRowNullableMixin,
89
+ Type.Object({
90
+ text2: Utils.Nullable(Type.Ref(Text.MarkdownString), {
91
+ description:
92
+ 'The secondary text for this row. Use `null` to represent a cell with a blank or empty vlue.'
93
+ })
94
+ }),
95
+ {
96
+ $id: 'OracleRollableRowText2',
97
+ description:
98
+ 'Represents a row in an oracle table that provides a secondary text field.'
99
+ }
100
+ )
101
+ export type OracleRollableRowText2 = Static<typeof OracleRollableRowText2>
102
+
103
+ export const OracleRollableRowText3 = Assign(
104
+ TableRowNullableMixin,
105
+ Type.Object({
106
+ text2: Utils.Nullable(Type.Ref(Text.MarkdownString), {
107
+ description:
108
+ 'The secondary text for this row. Use `null` to represent a cell with a blank or empty value.'
109
+ }),
110
+ text3: Utils.Nullable(Type.Ref(Text.MarkdownString), {
111
+ description:
112
+ 'The tertiary text for this row. Use `null` to represent a cell with a blank or empty vlue.'
113
+ })
114
+ }),
115
+ {
116
+ $id: 'OracleRollableRowText3',
117
+ description: 'Represents a row in an oracle table with 3 text cells.'
118
+ }
119
+ )
120
+ export type OracleRollableRowText3 = Static<typeof OracleRollableRowText3>
121
+
122
+ type _StringDefaultsFor<T extends TObject> = {
123
+ [K in
124
+ | keyof PickByType<
125
+ T['properties'],
126
+ | TRefUnsafe<typeof Text.Label>
127
+ | Utils.TNullable<TRefUnsafe<typeof Text.Label>>
128
+ >
129
+ | 'roll']: string | null
130
+ }
131
+
132
+ export const OracleRollableRow = Type.Union(
133
+ [
134
+ Type.Ref(OracleRollableRowText),
135
+ Type.Ref(OracleRollableRowText2),
136
+ Type.Ref(OracleRollableRowText3)
137
+ ],
138
+ { $id: 'OracleRollableRow', [JsonTypeDef]: { skip: true } }
139
+ )
140
+
141
+ export type OracleRollableRow = Static<typeof OracleRollableRow>
@@ -0,0 +1,15 @@
1
+ import { type TSchema, TypeGuard } from '@sinclair/typebox'
2
+
3
+ /** Mutates schema. */
4
+ export function NiceSchema(schema: TSchema) {
5
+ if (TypeGuard.IsObject(schema)) schema.additionalProperties ||= false
6
+
7
+ // if (!schema.title && !('$ref' in schema)) {
8
+ // console.log(schema)
9
+ // throw new Error(
10
+ // "Couldn't infer a title for this schema, please set it manually."
11
+ // )
12
+ // }
13
+
14
+ return schema
15
+ }
@@ -0,0 +1,41 @@
1
+ import { CloneType, type SchemaOptions, type TSchema } from '@sinclair/typebox'
2
+ import { mapValues } from 'lodash-es'
3
+ import { DefsKey } from '../../scripts/const.js'
4
+ import type { Defs } from '../Defs.js'
5
+ import { SourceData } from './SourceData.js'
6
+
7
+ export interface RootOptions extends SchemaOptions {
8
+ $id: `${'https' | 'http'}://${string}`
9
+ [DefsKey]: Defs
10
+ $schema: string
11
+ title: string
12
+ }
13
+
14
+ export type TRoot<T extends TSchema = TSchema> = T & RootOptions
15
+
16
+ export function RootObject<T extends TSchema, Options extends RootOptions>(
17
+ base: T,
18
+ options: Options
19
+ ) {
20
+ const rootOptions = {
21
+ ...options,
22
+ [DefsKey]: mapValues(options[DefsKey], (v, k) =>
23
+ CloneType(v, { title: k })
24
+ ) as Defs
25
+ }
26
+
27
+ return CloneType(base, rootOptions) as unknown as TRoot<T>
28
+ }
29
+
30
+ export function SourceRootObject<
31
+ T extends TSchema,
32
+ Options extends RootOptions
33
+ >(base: T, options: Options) {
34
+ return CloneType(base, {
35
+ ...options,
36
+
37
+ [DefsKey]: mapValues(options[DefsKey], (v, k) =>
38
+ SourceData(CloneType(v, { title: k }))
39
+ ) as Defs
40
+ }) as unknown as TRoot
41
+ }
@@ -0,0 +1,89 @@
1
+ import {
2
+ CloneType,
3
+ type SchemaOptions,
4
+ type TArray,
5
+ type TBoolean,
6
+ type TInteger,
7
+ type TIntersect,
8
+ type TLiteral,
9
+ type TNot,
10
+ type TNull,
11
+ type TNumber,
12
+ type TObject,
13
+ type TRecord,
14
+ type TRef,
15
+ type TRegExp,
16
+ type TSchema,
17
+ type TString,
18
+ type TTuple,
19
+ type TUnion
20
+ } from '@sinclair/typebox'
21
+ import type { TNullable } from '../Utils.js'
22
+ import type {
23
+ TDiscriminatedUnion,
24
+ TDiscriminatorMap
25
+ } from '../utils/DiscriminatedUnion.js'
26
+ import type { TUnionEnum } from '../utils/UnionEnum.js'
27
+ import type { TUnionOneOf } from '../utils/UnionOneOf.js'
28
+
29
+ export const SchemaKind = [
30
+ 'Array',
31
+ 'Boolean',
32
+ 'Integer',
33
+ 'Intersect',
34
+ 'Literal',
35
+ 'Null',
36
+ 'Number',
37
+ 'Object',
38
+ 'Record',
39
+ 'Ref',
40
+ 'String',
41
+ 'Tuple',
42
+ 'Union',
43
+ 'RegExp',
44
+ 'Not',
45
+ // custom types
46
+ 'UnionOneOf',
47
+ 'UnionEnum',
48
+ 'DiscriminatedUnion',
49
+ 'Nullable'
50
+ ] as const
51
+
52
+ interface SchemaKindMap extends Record<SchemaKind, TSchema> {
53
+ Array: TArray
54
+ Boolean: TBoolean
55
+ Integer: TInteger
56
+ Intersect: TIntersect
57
+ Literal: TLiteral
58
+ Null: TNull
59
+ Number: TNumber
60
+ Object: TObject
61
+ Record: TRecord
62
+ Ref: TRef
63
+ String: TString
64
+ Tuple: TTuple
65
+ Union: TUnion
66
+ RegExp: TRegExp
67
+ Not: TNot
68
+ // custom types
69
+ UnionOneOf: TUnionOneOf<TSchema[]>
70
+ UnionEnum: TUnionEnum
71
+ DiscriminatedUnion: TDiscriminatedUnion<TDiscriminatorMap, string>
72
+ Nullable: TNullable
73
+ }
74
+
75
+ export type SchemaKind = (typeof SchemaKind)[number]
76
+
77
+ export type SchemaTransform<T> = (schema: T, options: SchemaOptions) => T
78
+
79
+ export type SchemaTransforms = {
80
+ [P in SchemaKind]: SchemaTransform<SchemaKindMap[P]>
81
+ }
82
+
83
+ export const SchemaTransforms = Object.fromEntries(
84
+ SchemaKind.map((kind) => [
85
+ kind,
86
+ (schema: SchemaKindMap[typeof kind], options: SchemaOptions) =>
87
+ CloneType(schema, options)
88
+ ])
89
+ ) as SchemaTransforms
@@ -0,0 +1,151 @@
1
+ import {
2
+ CloneType,
3
+ Kind,
4
+ type SchemaOptions,
5
+ type TArray,
6
+ type TIntersect,
7
+ type TNull,
8
+ type TObject,
9
+ type TRecord,
10
+ type TSchema,
11
+ type TTuple,
12
+ type TUnion
13
+ } from '@sinclair/typebox'
14
+ import { mapValues, omit } from 'lodash-es'
15
+ import type { TNullable } from '../Utils.js'
16
+ import {
17
+ ComputedPropertyBrand,
18
+ GetSourceDataSchema,
19
+ SourceOptionalBrand
20
+ } from '../utils/Computed.js'
21
+ import type { TDiscriminatedUnion } from '../utils/DiscriminatedUnion.js'
22
+ import { SetOptional } from '../utils/SetOptional.js'
23
+ import { keysWithDefaults } from '../utils/typebox.js'
24
+ import type { TUnionOneOf } from '../utils/UnionOneOf.js'
25
+ import { NiceSchema } from './NiceSchema.js'
26
+ import { type SchemaKind, SchemaTransforms } from './SchemaTransform.js'
27
+
28
+ /**
29
+ * Transform a schema into the more lenient format used for Datasworn source data.
30
+ */
31
+ export function SourceData<T extends TSchema>(
32
+ schema: T,
33
+ options: SchemaOptions = {}
34
+ ) {
35
+ const schemaKind = schema[Kind] as SchemaKind
36
+
37
+ let baseSchema: TSchema
38
+
39
+ if (GetSourceDataSchema in schema) {
40
+ const getter = schema[GetSourceDataSchema] as (s: T) => TSchema
41
+ baseSchema = getter(schema)
42
+ } else baseSchema = schema
43
+
44
+ const transform = transforms[schemaKind]
45
+ if (typeof transform === 'undefined')
46
+ throw new Error(`No transform available for Kind: ${baseSchema[Kind]}`)
47
+
48
+ const result = NiceSchema(transform(baseSchema as any, options))
49
+
50
+ return result
51
+ }
52
+
53
+ const transforms: SchemaTransforms = {
54
+ ...SchemaTransforms,
55
+ Array: <T extends TArray>(schema: T, options: SchemaOptions) => {
56
+ const result = CloneType(schema, options)
57
+ result.items = SourceData(result.items)
58
+ return result
59
+ },
60
+
61
+ Intersect: <T extends TIntersect>(schema: T, options: SchemaOptions) => {
62
+ const result = CloneType(schema, options)
63
+ result.allOf = result.allOf.map((v) => SourceData(v))
64
+
65
+ return result
66
+ },
67
+
68
+ Object: <T extends TObject>(schema: T, options: SchemaOptions) => {
69
+ const optionalProps = keysWithDefaults(schema)
70
+
71
+ for (const [key, property] of Object.entries<any>(schema.properties))
72
+ if (property[ComputedPropertyBrand] || property[SourceOptionalBrand])
73
+ optionalProps.push(key as any)
74
+
75
+ if (optionalProps.length === 0) return CloneType(schema, options)
76
+
77
+ const base = SetOptional(schema, optionalProps)
78
+
79
+ const nuOptions = omit(
80
+ CloneType(schema, options),
81
+ ...Object.keys(base),
82
+ 'required'
83
+ )
84
+
85
+ const result = CloneType(base, nuOptions) as T // defaults arent part of the type data, so it's close enough
86
+
87
+ return result
88
+ },
89
+
90
+ Record: <T extends TRecord>(schema: T, options: SchemaOptions) => {
91
+ const newSchema = CloneType(schema, options)
92
+
93
+ newSchema.patternProperties = mapValues(newSchema.patternProperties, (v) =>
94
+ SourceData(v)
95
+ )
96
+
97
+ return newSchema
98
+ },
99
+
100
+ Tuple: <T extends TTuple>(schema: T, options: SchemaOptions) => {
101
+ const result = CloneType(schema, options)
102
+
103
+ if (Array.isArray(result.items))
104
+ result.items = result.items.map((item) => SourceData(item))
105
+
106
+ return result
107
+ },
108
+ Union: <T extends TUnion>(schema: T, options: SchemaOptions) => {
109
+ const result = CloneType(schema, options)
110
+
111
+ result.anyOf = result.anyOf.map((item) => SourceData(item))
112
+
113
+ return result
114
+ },
115
+
116
+ Nullable: <T extends TNullable>(schema: T, options: SchemaOptions) => {
117
+ const result = CloneType(schema, options)
118
+
119
+ result.anyOf = result.anyOf.map((item) => SourceData(item)) as [
120
+ TSchema,
121
+ TNull
122
+ ]
123
+
124
+ return result
125
+ },
126
+ UnionOneOf: <T extends TUnionOneOf<TSchema[]>>(
127
+ schema: T,
128
+ options: SchemaOptions
129
+ ) => {
130
+ const result = CloneType(schema, options)
131
+
132
+ result.oneOf = result.oneOf.map((item) => SourceData(item))
133
+
134
+ return result
135
+ },
136
+ DiscriminatedUnion: (<T extends TDiscriminatedUnion<any, string>>(
137
+ schema: T,
138
+ options: SchemaOptions
139
+ ) => {
140
+ const result = CloneType(schema, options)
141
+
142
+ result.allOf = result.allOf.map((ifThen) => ({
143
+ ...ifThen,
144
+ // biome-ignore lint/suspicious/noThenProperty: required for JSON Schema if-then-else
145
+ then: SourceData(ifThen.then)
146
+ })) as any[]
147
+ // result.oneOf = result.oneOf.map((member) => SourceData(member)) as any
148
+
149
+ return result
150
+ }) as any
151
+ }
@@ -0,0 +1,25 @@
1
+ import { type Static, Type } from '@sinclair/typebox'
2
+ import { Meter } from '../common/Inputs.js'
3
+ import { MarkdownString } from '../common/Text.js'
4
+ import { Assign } from '../utils/FlatIntersect.js'
5
+ import { Tags } from './TagRule.js'
6
+
7
+ export const ConditionMeterRule = Assign(
8
+ Type.Object({
9
+ description: Type.Ref(MarkdownString, {
10
+ description: 'A description of this condition meter.'
11
+ }),
12
+ shared: Type.Boolean({
13
+ default: false,
14
+ description: 'Is this condition meter shared by all players?'
15
+ }),
16
+ tags: Type.Optional(Type.Ref(Tags, { releaseStage: 'experimental' }))
17
+ }),
18
+ Meter(Type.Literal(true)),
19
+ {
20
+ $id: 'ConditionMeterRule',
21
+ description: 'Describes a standard player character condition meter.'
22
+ }
23
+ )
24
+
25
+ export type ConditionMeterRule = Static<typeof ConditionMeterRule>
@@ -0,0 +1,54 @@
1
+ import { type Static, Type } from '@sinclair/typebox'
2
+ import { ConditionMeterKey } from '../common/Player.js'
3
+ import { Label, MarkdownString } from '../common/Text.js'
4
+ import { Dictionary } from '../generic/Dictionary.js'
5
+ import { Tags } from './TagRule.js'
6
+
7
+ export const ImpactRule = Type.Object(
8
+ {
9
+ label: Type.Ref(Label, {
10
+ description: 'The label for this impact.'
11
+ }),
12
+ description: Type.Ref(MarkdownString, {
13
+ description: 'A description of this impact.'
14
+ }),
15
+ shared: Type.Boolean({
16
+ default: false,
17
+ description: 'Is this impact applied to all players at once?'
18
+ }),
19
+ prevents_recovery: Type.Array(Type.Ref(ConditionMeterKey), {
20
+ default: [],
21
+ description:
22
+ "Any ruleset condition meters that can't recover when this impact is active."
23
+ }),
24
+ permanent: Type.Boolean({
25
+ default: false,
26
+ description: 'Is this impact permanent?'
27
+ }),
28
+ tags: Type.Optional(Type.Ref(Tags, { releaseStage: 'experimental' }))
29
+ },
30
+ {
31
+ $id: 'ImpactRule',
32
+ description: 'Describes a standard impact/debility.'
33
+ }
34
+ )
35
+ export type ImpactRule = Static<typeof ImpactRule>
36
+
37
+ export const ImpactCategory = Type.Object(
38
+ {
39
+ label: Type.Ref(Label, {
40
+ description: 'A label for this impact category.'
41
+ }),
42
+ description: Type.Ref(MarkdownString, {
43
+ description: 'A description of this impact category.'
44
+ }),
45
+ contents: Dictionary(Type.Ref(ImpactRule), {
46
+ description: 'A dictionary object of the Impacts in this category.'
47
+ })
48
+ },
49
+ {
50
+ $id: 'ImpactCategory',
51
+ description: 'Describes a category of standard impacts/debilities.'
52
+ }
53
+ )
54
+ export type ImpactCategory = Static<typeof ImpactCategory>
@@ -0,0 +1,29 @@
1
+ import { type Static, Type } from '@sinclair/typebox'
2
+ import { Label, MarkdownString } from '../common/Text.js'
3
+ import { Tags } from './TagRule.js'
4
+
5
+ export const SpecialTrackRule = Type.Object(
6
+ {
7
+ label: Type.Ref(Label, {
8
+ description: 'A label for this special track.'
9
+ }),
10
+ description: Type.Ref(MarkdownString, {
11
+ description: 'A description of this special track.'
12
+ }),
13
+ shared: Type.Boolean({
14
+ default: false,
15
+ description: 'Is this track shared by all players?'
16
+ }),
17
+ optional: Type.Boolean({
18
+ default: false,
19
+ description: 'Is this track an optional rule?'
20
+ }),
21
+ tags: Type.Optional(Type.Ref(Tags, { releaseStage: 'experimental' }))
22
+ },
23
+ {
24
+ $id: 'SpecialTrackRule',
25
+ description:
26
+ 'Describes a special track like Bonds (classic Ironsworn), Failure (Delve), or Legacies (Starforged).'
27
+ }
28
+ )
29
+ export type SpecialTrackRule = Static<typeof SpecialTrackRule>
@@ -0,0 +1,23 @@
1
+ import { type Static, Type } from '@sinclair/typebox'
2
+
3
+ import { Label, MarkdownString } from '../common/Text.js'
4
+ import { Tags } from './TagRule.js'
5
+
6
+ export const StatRule = Type.Object(
7
+ {
8
+ label: Type.Ref(Label, {
9
+ description: 'A label for this stat.',
10
+ examples: ['edge']
11
+ }),
12
+ description: Type.Ref(MarkdownString, {
13
+ description: 'A description of this stat.',
14
+ examples: ['Quickness, agility, and prowess when fighting at a distance.']
15
+ }),
16
+ tags: Type.Optional(Type.Ref(Tags, { releaseStage: 'experimental' }))
17
+ },
18
+ {
19
+ $id: 'StatRule',
20
+ description: 'Describes a standard player character stat.'
21
+ }
22
+ )
23
+ export type StatRule = Static<typeof StatRule>