@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,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>
|