@datasworn-community/build-tools 0.2.2 → 0.2.4
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/dist/cli/datasworn-build.js +12 -2
- package/dist/cli/datasworn-build.js.map +1 -1
- package/dist/config.d.ts +3 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/content-package-builder.d.ts +54 -0
- package/dist/content-package-builder.d.ts.map +1 -0
- package/dist/content-package-builder.js +159 -0
- package/dist/content-package-builder.js.map +1 -0
- package/dist/id-references.d.ts +20 -0
- package/dist/id-references.d.ts.map +1 -0
- package/dist/id-references.js +96 -0
- package/dist/id-references.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/rules-package-builder.d.ts +14 -0
- package/dist/rules-package-builder.d.ts.map +1 -1
- package/dist/rules-package-builder.js +14 -1
- package/dist/rules-package-builder.js.map +1 -1
- package/package.json +6 -3
- package/schema-source/builders/IdBuilder.ts +849 -0
- package/schema-source/schema/common/Id.ts +3 -1
- package/schema-source/schema/delve/common.ts +3 -1
- package/schema-source/schema/generic/CollectableNode.ts +3 -1
- package/schema-source/schema/generic/CollectionNode.ts +3 -1
- package/schema-source/schema/generic/EmbedOnlyNode.ts +3 -1
- package/schema-source/schema/generic/NonCollectableNode.ts +3 -1
- package/schema-source/schema/generic/PrimaryTypeNode.ts +3 -1
- package/schema-source/schema/rules/TagRule.ts +3 -1
- package/schema-source/schema/utils/EmbeddedNode.ts +3 -1
- package/schema-source/schema/utils/string.ts +2 -2
- package/schema-source/scripts/augmentations.ts +70 -0
- package/schema-source/scripts/codegen/schemaToTsDeclaration.ts +290 -0
- package/schema-source/scripts/codegen/tsDocTags.ts +75 -0
- package/schema-source/scripts/codegen/writeTypescriptTypes.ts +35 -0
- package/schema-source/scripts/const.ts +40 -2
- package/schema-source/scripts/datasworn/sort.ts +80 -0
- package/schema-source/scripts/json-typedef/typedef.ts +1138 -0
- package/schema-source/scripts/json-typedef/utils.ts +349 -0
- package/schema-source/scripts/schema/buildSchema.ts +109 -0
- package/schema-source/scripts/utils/Log.ts +15 -0
- package/schema-source/scripts/utils/readWrite.ts +180 -0
- package/schema-source/scripts/validation/ajv.ts +29 -0
- package/schema-source/scripts/validation/formats.ts +6 -0
- package/schema-source/scripts/validation/keywords.ts +36 -0
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type StringOptions,
|
|
3
|
+
type TRef,
|
|
4
|
+
type TString,
|
|
5
|
+
type TUnion,
|
|
6
|
+
Type
|
|
7
|
+
} from '@sinclair/typebox'
|
|
8
|
+
import { escapeRegExp, snakeCase } from 'lodash-es'
|
|
9
|
+
import type { PascalCase } from 'type-fest'
|
|
10
|
+
import { type Datasworn, IdElements, type TypeNode } from '@datasworn-community/core'
|
|
11
|
+
import { JsonTypeDef } from '../schema/Symbols.js'
|
|
12
|
+
import { pascalCase } from '../schema/utils/string.js'
|
|
13
|
+
import {
|
|
14
|
+
COLLECTION_DEPTH_MAX,
|
|
15
|
+
COLLECTION_DEPTH_MIN,
|
|
16
|
+
CollectionsKey,
|
|
17
|
+
ContentsKey,
|
|
18
|
+
PathKeySep,
|
|
19
|
+
PrefixSep,
|
|
20
|
+
TypeSep,
|
|
21
|
+
WildcardString
|
|
22
|
+
} from '../scripts/const.js'
|
|
23
|
+
import JtdType from '../scripts/json-typedef/typedef.js'
|
|
24
|
+
|
|
25
|
+
import TypeGuard = IdElements.TypeGuard
|
|
26
|
+
import Pattern = IdElements.Pattern
|
|
27
|
+
import TypeId = IdElements.TypeId
|
|
28
|
+
|
|
29
|
+
type RegexGroupType =
|
|
30
|
+
| 'none'
|
|
31
|
+
| 'capture_group'
|
|
32
|
+
| 'non_capturing_group'
|
|
33
|
+
| 'named_capture_group'
|
|
34
|
+
|
|
35
|
+
// inexact but enough to catch some errors
|
|
36
|
+
type ValidDictKey = Exclude<string, `_${string}` | `${number}`>
|
|
37
|
+
|
|
38
|
+
type Dict<T = unknown> = Record<ValidDictKey, T>
|
|
39
|
+
|
|
40
|
+
type PropKeysOfType<O, T> = {
|
|
41
|
+
[P in string & keyof O]-?: O[P] extends T ? P : never
|
|
42
|
+
}[keyof O & string]
|
|
43
|
+
|
|
44
|
+
// type DictPropKeyIn<O> = PropKeysOfType<O, Dict>
|
|
45
|
+
type DictPropKeyIn<O> = keyof O
|
|
46
|
+
// type ArrayPropKeyIn<O> = PropKeysOfType<O, Array<any>>
|
|
47
|
+
type ArrayPropKeyIn<O> = keyof O
|
|
48
|
+
|
|
49
|
+
type EntryInProp<
|
|
50
|
+
O,
|
|
51
|
+
P extends DictPropKeyIn<O> | ArrayPropKeyIn<O>
|
|
52
|
+
> = O[P] extends Array<any> ? O[P][number] : O[P][keyof O[P]]
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Represents a single, slash-separated path element of a Datasworn ID.
|
|
56
|
+
* @template Origin The object type of the starting point of this path element.
|
|
57
|
+
*/
|
|
58
|
+
abstract class PathSymbol<Origin, Key extends keyof Origin = keyof Origin> {
|
|
59
|
+
readonly inProperty: Key
|
|
60
|
+
abstract get pattern(): RegExp
|
|
61
|
+
abstract get wildcardPattern(): RegExp
|
|
62
|
+
|
|
63
|
+
abstract clone(...args: any[]): this
|
|
64
|
+
|
|
65
|
+
constructor(inProperty: Key) {
|
|
66
|
+
this.inProperty = inProperty
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
namespace PathSymbol {
|
|
71
|
+
export class RulesPackage extends PathSymbol<null> {
|
|
72
|
+
static readonly WILDCARD = new RegExp(
|
|
73
|
+
// TODO: proper handling for standalone RulesPackage globstars
|
|
74
|
+
// `(?:${Pattern.RulesPackageElement.source}|\\${WildcardString}|\\${WildcardString}\\${WildcardString})`
|
|
75
|
+
`(?:${Pattern.RulesPackageElement.source}|${escapeRegExp(WildcardString)})`
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
get pattern() {
|
|
79
|
+
return Pattern.RulesPackageElement
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get wildcardPattern() {
|
|
83
|
+
return RulesPackage.WILDCARD
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
constructor() {
|
|
87
|
+
// @ts-expect-error
|
|
88
|
+
super(null)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
clone() {
|
|
92
|
+
return new PathSymbol.RulesPackage() as this
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export class DictKey<
|
|
97
|
+
Origin,
|
|
98
|
+
Prop extends DictPropKeyIn<Origin>
|
|
99
|
+
> extends PathSymbol<Origin, Prop> {
|
|
100
|
+
static readonly PATTERN = Pattern.DictKeyElement
|
|
101
|
+
static readonly WILDCARD = new RegExp(
|
|
102
|
+
`${DictKey.PATTERN.source}|\\${PathKeySep}\\${WildcardString}|\\${PathKeySep}\\${WildcardString}\\${WildcardString}`
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
public get minReps(): number {
|
|
106
|
+
return COLLECTION_DEPTH_MIN
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public get maxReps(): number {
|
|
110
|
+
return COLLECTION_DEPTH_MIN
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get pattern() {
|
|
114
|
+
return DictKey.PATTERN
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get wildcardPattern() {
|
|
118
|
+
return DictKey.WILDCARD
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
clone() {
|
|
122
|
+
return new DictKey<Origin, Prop>(this.inProperty) as this
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static get stringTemplateLiteral() {
|
|
126
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
127
|
+
return '${string}'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static renderDictKeys(min: number, max: number, wildcard = false): RegExp {
|
|
131
|
+
// exit early if there's nothing to do
|
|
132
|
+
if (min === 1 && max === 1)
|
|
133
|
+
return wildcard
|
|
134
|
+
? PathSymbol.DictKey.WILDCARD
|
|
135
|
+
: PathSymbol.DictKey.PATTERN
|
|
136
|
+
|
|
137
|
+
const base = wildcard
|
|
138
|
+
? `${IdPattern.PathSep}(?:${PathSymbol.DictKey.PATTERN.source}|${IdPattern.WILDCARD}|${IdPattern.GLOBSTAR})`
|
|
139
|
+
: IdPattern.PathSep + PathSymbol.DictKey.PATTERN.source
|
|
140
|
+
|
|
141
|
+
const minMax = min === max ? min.toString() : `${min},${max}`
|
|
142
|
+
|
|
143
|
+
const wrappedKey = `(?:${base}){${minMax}}`
|
|
144
|
+
|
|
145
|
+
if (!wildcard) return RegExp(wrappedKey)
|
|
146
|
+
|
|
147
|
+
return new RegExp(wrappedKey)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static repsStringTemplateLiteral(min: number, max: number) {
|
|
151
|
+
let variantsToGenerate = max - min + 1
|
|
152
|
+
|
|
153
|
+
const variants: string[] = []
|
|
154
|
+
|
|
155
|
+
while (variantsToGenerate > 0) {
|
|
156
|
+
const variantParts: string[] = []
|
|
157
|
+
|
|
158
|
+
for (let i = 0; i < variantsToGenerate; i++)
|
|
159
|
+
variantParts.push(DictKey.stringTemplateLiteral)
|
|
160
|
+
|
|
161
|
+
variants.push(variantParts.join(PathKeySep))
|
|
162
|
+
|
|
163
|
+
variantsToGenerate--
|
|
164
|
+
}
|
|
165
|
+
return variants
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static reduceReps(...dictKeys: DictKey<any, any>[]) {
|
|
169
|
+
const min = dictKeys.reduce((a, b) => a + b.minReps, 0)
|
|
170
|
+
const max = dictKeys.reduce((a, b) => a + b.maxReps, 0)
|
|
171
|
+
return { min, max }
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export class RecursiveDictKeys<
|
|
176
|
+
Origin,
|
|
177
|
+
Prop extends DictPropKeyIn<Origin>,
|
|
178
|
+
RecursiveProp extends DictPropKeyIn<EntryInProp<Origin, Prop>>
|
|
179
|
+
> extends DictKey<Origin, Prop> {
|
|
180
|
+
readonly recursiveProperty: RecursiveProp
|
|
181
|
+
|
|
182
|
+
override get minReps() {
|
|
183
|
+
return COLLECTION_DEPTH_MIN
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
override get maxReps() {
|
|
187
|
+
return COLLECTION_DEPTH_MAX
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
constructor(inProperty: Prop, recursiveProperty: RecursiveProp) {
|
|
191
|
+
super(inProperty)
|
|
192
|
+
this.recursiveProperty = recursiveProperty
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
clone<
|
|
196
|
+
CloneProp extends DictPropKeyIn<Origin> = Prop,
|
|
197
|
+
CloneRecursiveProp extends DictPropKeyIn<
|
|
198
|
+
EntryInProp<Origin, Prop>
|
|
199
|
+
> = RecursiveProp
|
|
200
|
+
>() {
|
|
201
|
+
// @ts-expect-error
|
|
202
|
+
return new RecursiveDictKeys<Origin, CloneProp, CloneRecursiveProp>(
|
|
203
|
+
this.inProperty,
|
|
204
|
+
this.recursiveProperty
|
|
205
|
+
) as this
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export class Index<
|
|
210
|
+
Origin,
|
|
211
|
+
Key extends ArrayPropKeyIn<Origin>
|
|
212
|
+
> extends PathSymbol<Origin, Key> {
|
|
213
|
+
clone() {
|
|
214
|
+
return new Index<Origin, Key>(this.inProperty) as this
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
get wildcardPattern() {
|
|
218
|
+
return new RegExp(`${Pattern.IndexElement.source}|\\${WildcardString}`)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
override get pattern() {
|
|
222
|
+
return Pattern.IndexElement
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Represents a complete Datasworn ID, consisting of one or more type prefixes
|
|
229
|
+
* @template CurrentNode The type of the current (last) node in an IdPattern. Used to provide typechecks for keys when chaining `add` methods.
|
|
230
|
+
*/
|
|
231
|
+
class IdPattern<
|
|
232
|
+
CurrentNode = Datasworn.RulesPackage
|
|
233
|
+
> extends Array<PathFormat> {
|
|
234
|
+
static readonly TypeSep = `\\${TypeSep}`
|
|
235
|
+
|
|
236
|
+
static readonly PrefixSep = PrefixSep
|
|
237
|
+
|
|
238
|
+
static readonly PathSep = `\\${PathKeySep}`
|
|
239
|
+
|
|
240
|
+
static readonly WILDCARD = `\\${WildcardString}`
|
|
241
|
+
static readonly GLOBSTAR = IdPattern.WILDCARD + IdPattern.WILDCARD
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get the left side of the ID pattern: the type prefix.
|
|
245
|
+
* @example `oracle_rollable`
|
|
246
|
+
* @example `move_category`
|
|
247
|
+
* @example `asset.ability.move`
|
|
248
|
+
*/
|
|
249
|
+
getLeftSide(): RegExp {
|
|
250
|
+
return new RegExp(this.map(({ typeId }) => typeId).join(IdPattern.TypeSep))
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
get fullTypeId() {
|
|
254
|
+
return this.map(({ typeId }) => typeId).join(TypeSep)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get the right hand side of the ID pattern: the path.
|
|
259
|
+
* @example `starforged/core/action`
|
|
260
|
+
* @example `classic/suffer`
|
|
261
|
+
* @example `classic/ritual/commune.0.commune`
|
|
262
|
+
*/
|
|
263
|
+
getRightSide(
|
|
264
|
+
groups: RegexGroupType = 'named_capture_group',
|
|
265
|
+
wildcard = false
|
|
266
|
+
): RegExp {
|
|
267
|
+
const result = this.map((part) =>
|
|
268
|
+
part.toRegexSource(groups, wildcard)
|
|
269
|
+
).join(IdPattern.TypeSep)
|
|
270
|
+
|
|
271
|
+
return new RegExp(result)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Return a new copy of this IdPattern.
|
|
276
|
+
* @param primaryTypeId An optional override for the typeId of the *last* PathFormat.
|
|
277
|
+
*/
|
|
278
|
+
clone(primaryTypeId?: string) {
|
|
279
|
+
return new IdPattern(
|
|
280
|
+
...this.map((el, i, arr) =>
|
|
281
|
+
typeof primaryTypeId === 'string' && i === arr.length - 1
|
|
282
|
+
? el.clone(primaryTypeId)
|
|
283
|
+
: el.clone()
|
|
284
|
+
)
|
|
285
|
+
) as this
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** The type ID of the most recent primary node ancestor. */
|
|
289
|
+
get primaryTypeId() {
|
|
290
|
+
return this.at(0)?.typeId ?? null
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** The type of the node itself. */
|
|
294
|
+
get nodeTypeId() {
|
|
295
|
+
return this.at(-1)?.typeId ?? null
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
get current() {
|
|
299
|
+
return (this.at(-1) as PathFormat<CurrentNode>) ?? null
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
get isEmbedded() {
|
|
303
|
+
// if there's more than one path format, it's a multi-part path, and therefore an embedded ID
|
|
304
|
+
return this.length > 1
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
createEmbedded<T extends TypeId.Embeddable>(
|
|
308
|
+
typeId: T
|
|
309
|
+
): IdPattern<Extract<TypeNode.Embedded, { type: typeof typeId }>> {
|
|
310
|
+
const withGroup = this.clone().addNewTypeGroup(typeId)
|
|
311
|
+
|
|
312
|
+
switch (TypeId.getEmbeddedPropertyType(typeId)) {
|
|
313
|
+
case 'array':
|
|
314
|
+
return withGroup.addIndex(TypeId.getEmbeddedPropertyKey(typeId) as any)
|
|
315
|
+
|
|
316
|
+
case 'dictionary':
|
|
317
|
+
return withGroup.addDictKey(
|
|
318
|
+
TypeId.getEmbeddedPropertyKey(typeId) as any
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
default:
|
|
322
|
+
throw new Error(
|
|
323
|
+
`Expected an embeddable TypeId, but got ${String(typeId)}`
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
addNewTypeGroup(typeId: string) {
|
|
329
|
+
this.push(new PathFormat(typeId))
|
|
330
|
+
return this
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
addDictKey<Key extends DictPropKeyIn<CurrentNode>>(inProperty: Key) {
|
|
334
|
+
this.current.addDictKey(inProperty)
|
|
335
|
+
return this as IdPattern<CurrentNode[Key][keyof CurrentNode[Key]]>
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
addRecursiveDictKeys<
|
|
339
|
+
Prop extends DictPropKeyIn<CurrentNode>,
|
|
340
|
+
RecursiveProp extends DictPropKeyIn<
|
|
341
|
+
CurrentNode[Prop][keyof CurrentNode[Prop]]
|
|
342
|
+
>
|
|
343
|
+
>(property: Prop, recursiveProperty: RecursiveProp) {
|
|
344
|
+
this.current.addRecursiveDictKeys(property, recursiveProperty)
|
|
345
|
+
return this as IdPattern<EntryInProp<CurrentNode, Prop>>
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
addIndex<Prop extends ArrayPropKeyIn<CurrentNode>>(inProperty: Prop) {
|
|
349
|
+
this.current.addIndex<Prop>(inProperty)
|
|
350
|
+
|
|
351
|
+
return this as IdPattern<EntryInProp<CurrentNode, Prop>>
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
toRegex(
|
|
355
|
+
lineDelimiters = false,
|
|
356
|
+
groups: RegexGroupType = 'named_capture_group',
|
|
357
|
+
wildcard = false
|
|
358
|
+
) {
|
|
359
|
+
const base =
|
|
360
|
+
this.getLeftSide().source +
|
|
361
|
+
IdPattern.PrefixSep +
|
|
362
|
+
this.getRightSide(groups, wildcard).source
|
|
363
|
+
if (lineDelimiters) return new RegExp(`^${base}$`)
|
|
364
|
+
return new RegExp(base)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
toWildcardSchema(options: StringOptions = {}) {
|
|
368
|
+
const typeName = this.fullTypeId.split(TypeSep).map(pascalCase).join('')
|
|
369
|
+
const $id = `${typeName}IdWildcard`
|
|
370
|
+
const description = `A wildcarded ${typeName}Id that can be used to match multiple ${typeName} objects.`
|
|
371
|
+
// named capture groups aren't universally available, so JSON schema docs recommend against using them
|
|
372
|
+
const pattern = this.toRegex(true, 'capture_group', true).source
|
|
373
|
+
|
|
374
|
+
return Type.String({
|
|
375
|
+
$id,
|
|
376
|
+
description,
|
|
377
|
+
pattern,
|
|
378
|
+
...options
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
toSchema(options: StringOptions = {}) {
|
|
383
|
+
const typeName = this.fullTypeId.split(TypeSep).map(pascalCase).join('')
|
|
384
|
+
const indefiniteArticle = typeName.match(/^[aeiou]/i) ? 'an' : 'a'
|
|
385
|
+
const $id = `${typeName}Id`
|
|
386
|
+
const description = `A unique ID representing ${indefiniteArticle} ${typeName} object.`
|
|
387
|
+
// named capture groups aren't universally available, so JSON schema docs recommend against using them
|
|
388
|
+
const pattern = this.toRegex(true, 'capture_group').source
|
|
389
|
+
|
|
390
|
+
return Type.String({
|
|
391
|
+
$id,
|
|
392
|
+
description,
|
|
393
|
+
pattern,
|
|
394
|
+
...options
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
static fromRoot(typeId: string) {
|
|
399
|
+
const f = new PathFormat(typeId, true)
|
|
400
|
+
return new IdPattern(f)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
constructor(...formats: PathFormat[]) {
|
|
404
|
+
super()
|
|
405
|
+
this.push(...formats)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
static createCollection<T extends TypeId.Collection>(
|
|
409
|
+
typeId: T,
|
|
410
|
+
typeRoot: TypeId.BranchKey<T>
|
|
411
|
+
) {
|
|
412
|
+
return (
|
|
413
|
+
IdPattern.fromRoot(typeId)
|
|
414
|
+
// @ts-expect-error this happens because not all union members of OracleCollection have the 'collections' property
|
|
415
|
+
.addRecursiveDictKeys(typeRoot, CollectionsKey) as IdPattern<
|
|
416
|
+
TypeNode.Collection<T>
|
|
417
|
+
>
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
static createCollectable<T extends TypeId.Collectable>(
|
|
422
|
+
typeId: T,
|
|
423
|
+
typeRoot: TypeId.BranchKey<T>
|
|
424
|
+
) {
|
|
425
|
+
return (
|
|
426
|
+
IdPattern.fromRoot(typeId)
|
|
427
|
+
// @ts-expect-error
|
|
428
|
+
.addRecursiveDictKeys(typeRoot, CollectionsKey)
|
|
429
|
+
// @ts-expect-error
|
|
430
|
+
.addDictKey(ContentsKey) as IdPattern<TypeNode.Collectable<T>>
|
|
431
|
+
)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static createNonCollectable<T extends TypeId.NonCollectable>(
|
|
435
|
+
typeId: T,
|
|
436
|
+
typeRoot: TypeId.BranchKey<T>
|
|
437
|
+
) {
|
|
438
|
+
return IdPattern.fromRoot(typeId).addDictKey(typeRoot) as IdPattern<
|
|
439
|
+
TypeNode.NonCollectable<T>
|
|
440
|
+
>
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Represents a slash-separated path in a Datasworn ID.
|
|
446
|
+
* @template Origin The object type of the origin for this path.
|
|
447
|
+
*/
|
|
448
|
+
class PathFormat<Origin = Datasworn.RulesPackage> extends Array<
|
|
449
|
+
PathSymbol<any>
|
|
450
|
+
> {
|
|
451
|
+
typeId: string
|
|
452
|
+
|
|
453
|
+
toRegexSource(group?: RegexGroupType, wildcard = false) {
|
|
454
|
+
let base = ''
|
|
455
|
+
let dictKeys: PathSymbol.DictKey<any, any>[] = []
|
|
456
|
+
const isOnlyPart = this.length === 1
|
|
457
|
+
|
|
458
|
+
// TODO: apply globstar *after*?, rather then in the static prop WILDCARD?
|
|
459
|
+
|
|
460
|
+
for (let i = 0; i < this.length; i++) {
|
|
461
|
+
const part = this[i]
|
|
462
|
+
const nextPart = this[i + 1]
|
|
463
|
+
const isFirstPart = i === 0
|
|
464
|
+
|
|
465
|
+
switch (true) {
|
|
466
|
+
case isOnlyPart &&
|
|
467
|
+
part instanceof PathSymbol.DictKey &&
|
|
468
|
+
!(part instanceof PathSymbol.RecursiveDictKeys):
|
|
469
|
+
base += `${part.pattern.source}|${IdPattern.WILDCARD}` // single-symbol paths can't be globstarred
|
|
470
|
+
break
|
|
471
|
+
case part instanceof PathSymbol.DictKey &&
|
|
472
|
+
nextPart instanceof PathSymbol.DictKey:
|
|
473
|
+
// DictKey chain not done; grab the current one and continue
|
|
474
|
+
dictKeys.push(part)
|
|
475
|
+
break
|
|
476
|
+
case part instanceof PathSymbol.DictKey &&
|
|
477
|
+
!(nextPart instanceof PathSymbol.DictKey): {
|
|
478
|
+
// end of this DictKey chain; assemble complete DictKey string, and reset array
|
|
479
|
+
dictKeys.push(part)
|
|
480
|
+
const { min, max } = PathSymbol.DictKey.reduceReps(...dictKeys)
|
|
481
|
+
const toAppend = PathSymbol.DictKey.renderDictKeys(
|
|
482
|
+
min,
|
|
483
|
+
max,
|
|
484
|
+
wildcard
|
|
485
|
+
).source
|
|
486
|
+
if (
|
|
487
|
+
!toAppend.startsWith(`(?:${IdPattern.PathSep}`) &&
|
|
488
|
+
toAppend.length > 0
|
|
489
|
+
)
|
|
490
|
+
base += IdPattern.PathSep
|
|
491
|
+
// if (min > 0 && max > 2) {
|
|
492
|
+
// toAppend = toAppend.replace(
|
|
493
|
+
// `|${IdPattern.GLOBSTAR})`,
|
|
494
|
+
// `|(?:${IdPattern.GLOBSTAR}){${min === max ? min - 1 : min - 1 + ',' + (max - 1)}})`
|
|
495
|
+
// )
|
|
496
|
+
// }
|
|
497
|
+
base += toAppend
|
|
498
|
+
dictKeys = []
|
|
499
|
+
break
|
|
500
|
+
}
|
|
501
|
+
default:
|
|
502
|
+
// standard handling
|
|
503
|
+
if (!isFirstPart) base += IdPattern.PathSep
|
|
504
|
+
base += wildcard ? part.wildcardPattern.source : part.pattern.source
|
|
505
|
+
break
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// if (canBeGlobstar) {
|
|
510
|
+
// const maxWildcardsAfterInitialGlobstar = this.length - 1
|
|
511
|
+
// const extraWildcards =
|
|
512
|
+
// maxWildcardsAfterInitialGlobstar === 1
|
|
513
|
+
// ? `(?:${IdPattern.PathSep}${IdPattern.WILDCARD})?`
|
|
514
|
+
// : maxWildcardsAfterInitialGlobstar > 1
|
|
515
|
+
// ? `(?:${IdPattern.PathSep}${IdPattern.WILDCARD}){0,${maxWildcardsAfterInitialGlobstar}}`
|
|
516
|
+
// : ''
|
|
517
|
+
// // valid to represent the whole fragment
|
|
518
|
+
// base = `${base}|${IdPattern.GLOBSTAR}${extraWildcards}`
|
|
519
|
+
// }
|
|
520
|
+
|
|
521
|
+
switch (group) {
|
|
522
|
+
case 'capture_group':
|
|
523
|
+
base = `(${base})`
|
|
524
|
+
break
|
|
525
|
+
case 'non_capturing_group':
|
|
526
|
+
base = `(?:${base})`
|
|
527
|
+
break
|
|
528
|
+
case 'named_capture_group':
|
|
529
|
+
base = `(?<${this.typeId}>${base})`
|
|
530
|
+
break
|
|
531
|
+
default:
|
|
532
|
+
// if (canBeGlobstar) base = `(?:${base})`
|
|
533
|
+
break
|
|
534
|
+
}
|
|
535
|
+
// Lone globstar is valid for any ID type (to match all IDs of the same type)
|
|
536
|
+
|
|
537
|
+
return base
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
readonly relative: boolean
|
|
541
|
+
|
|
542
|
+
constructor(typeId: string, relative = false) {
|
|
543
|
+
super()
|
|
544
|
+
if (relative) this.push(new PathSymbol.RulesPackage())
|
|
545
|
+
this.relative = relative
|
|
546
|
+
this.typeId = typeId
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
clone(newTypeId?: string) {
|
|
550
|
+
const clone = new PathFormat(newTypeId ?? this.typeId, this.relative)
|
|
551
|
+
for (const part of this) {
|
|
552
|
+
if (part instanceof PathSymbol.RulesPackage) continue
|
|
553
|
+
clone.push(part.clone())
|
|
554
|
+
}
|
|
555
|
+
return clone as this
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
createRelativeAs(newTypeId: string) {
|
|
559
|
+
return new PathFormat(newTypeId, true) as typeof this
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
addDictKey<Prop extends DictPropKeyIn<Origin>>(inProperty: Prop) {
|
|
563
|
+
this.push(new PathSymbol.DictKey<Origin, Prop>(inProperty))
|
|
564
|
+
return this as PathFormat<Origin[Prop][keyof Origin[Prop]]>
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
addRecursiveDictKeys<
|
|
568
|
+
Prop extends DictPropKeyIn<Origin>,
|
|
569
|
+
RecursiveProp extends DictPropKeyIn<EntryInProp<Origin, Prop>>
|
|
570
|
+
>(property: Prop, recursiveProperty: RecursiveProp) {
|
|
571
|
+
this.push(
|
|
572
|
+
new PathSymbol.RecursiveDictKeys<Origin, Prop, RecursiveProp>(
|
|
573
|
+
property,
|
|
574
|
+
recursiveProperty
|
|
575
|
+
)
|
|
576
|
+
)
|
|
577
|
+
return this as PathFormat<EntryInProp<Origin, Prop>>
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
addIndex<Prop extends ArrayPropKeyIn<Origin>>(inProperty: Prop) {
|
|
581
|
+
this.push(new PathSymbol.Index<Origin, Prop>(inProperty))
|
|
582
|
+
return this as PathFormat<EntryInProp<Origin, Prop>>
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const patternIndex = {} as Record<TypeId.Any, IdPattern[]>
|
|
587
|
+
|
|
588
|
+
for (const typeId of TypeId.Primary) {
|
|
589
|
+
let pattern: IdPattern
|
|
590
|
+
|
|
591
|
+
switch (true) {
|
|
592
|
+
case TypeGuard.CollectionType(typeId):
|
|
593
|
+
pattern = IdPattern.createCollection(typeId, TypeId.getBranchKey(typeId))
|
|
594
|
+
break
|
|
595
|
+
|
|
596
|
+
case TypeGuard.CollectableType(typeId):
|
|
597
|
+
pattern = IdPattern.createCollectable(typeId, TypeId.getBranchKey(typeId))
|
|
598
|
+
break
|
|
599
|
+
|
|
600
|
+
case TypeGuard.NonCollectableType(typeId):
|
|
601
|
+
pattern = IdPattern.createNonCollectable(
|
|
602
|
+
typeId,
|
|
603
|
+
TypeId.getBranchKey(typeId)
|
|
604
|
+
)
|
|
605
|
+
break
|
|
606
|
+
default:
|
|
607
|
+
throw new Error(`Expected a primary type, got ${JSON.stringify(typeId)}`)
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
patternIndex[typeId] ||= []
|
|
611
|
+
patternIndex[typeId].push(pattern)
|
|
612
|
+
|
|
613
|
+
if (TypeId.canHaveEmbed(typeId)) computeEmbeddedTypes(pattern)
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function computeEmbeddedTypes(pattern: IdPattern) {
|
|
617
|
+
const nodeTypeId = pattern.nodeTypeId as TypeId.Any
|
|
618
|
+
|
|
619
|
+
if (!TypeId.canHaveEmbed(nodeTypeId, pattern.isEmbedded)) return
|
|
620
|
+
|
|
621
|
+
const embedTypes = TypeId.getEmbeddableTypes(nodeTypeId, pattern.isEmbedded)
|
|
622
|
+
|
|
623
|
+
for (const embedType of embedTypes) {
|
|
624
|
+
const embedPattern = pattern.createEmbedded(embedType)
|
|
625
|
+
|
|
626
|
+
patternIndex[embedType] ||= []
|
|
627
|
+
patternIndex[embedType].push(embedPattern)
|
|
628
|
+
|
|
629
|
+
computeEmbeddedTypes(embedPattern)
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// patterns.sort((a, b) =>
|
|
634
|
+
// a.getLeftSide().source.localeCompare(b.getLeftSide().source, 'en-US'))
|
|
635
|
+
|
|
636
|
+
type PrimaryNodePrefix<T extends TypeId.Primary = TypeId.Primary> =
|
|
637
|
+
`${PascalCase<T>}`
|
|
638
|
+
type EmbeddedPrimaryNodePrefix<
|
|
639
|
+
T extends TypeId.EmbeddablePrimary = TypeId.EmbeddablePrimary
|
|
640
|
+
> = `Embedded${PascalCase<T>}`
|
|
641
|
+
type PrimaryNodeUnionPrefix<
|
|
642
|
+
T extends TypeId.EmbeddablePrimary = TypeId.EmbeddablePrimary
|
|
643
|
+
> = `Any${PascalCase<T>}`
|
|
644
|
+
type EmbedOnlyPrefix<
|
|
645
|
+
T extends TypeId.EmbedOnly = TypeId.EmbedOnly,
|
|
646
|
+
TParent extends TypeId.CanEmbedType<T> = TypeId.CanEmbedType<T>
|
|
647
|
+
> = `${PascalCase<TParent>}${PascalCase<T>}`
|
|
648
|
+
|
|
649
|
+
type IdSuffix = 'Id' | 'IdWildcard'
|
|
650
|
+
|
|
651
|
+
type IdSchemaName =
|
|
652
|
+
`${EmbeddedPrimaryNodePrefix | PrimaryNodePrefix | PrimaryNodeUnionPrefix | EmbedOnlyPrefix | 'Any'}${IdSuffix}`
|
|
653
|
+
|
|
654
|
+
const ids = {} as Record<IdSchemaName, TString | TUnion<TRef<string>[]>>
|
|
655
|
+
|
|
656
|
+
for (const typeId in patternIndex) {
|
|
657
|
+
const typePatterns = patternIndex[typeId as TypeId.Any]
|
|
658
|
+
|
|
659
|
+
for (const pattern of typePatterns)
|
|
660
|
+
for (const schema of [pattern.toSchema(), pattern.toWildcardSchema()]) {
|
|
661
|
+
ids[schema.$id as IdSchemaName] = schema
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const permutations = {} as Record<string, Set<string>>
|
|
666
|
+
|
|
667
|
+
function permutate(typeId: TypeId.Any, parentTypeComposite: string) {
|
|
668
|
+
if (TypeId.isPrimary(typeId) && !TypeId.isEmbedOnly(parentTypeComposite)) {
|
|
669
|
+
const typeSchema = `${pascalCase(typeId)}Id`
|
|
670
|
+
|
|
671
|
+
const parentTypePrefix = parentTypeComposite
|
|
672
|
+
.split(TypeSep)
|
|
673
|
+
.map(pascalCase)
|
|
674
|
+
.join('')
|
|
675
|
+
|
|
676
|
+
const thisSchema = parentTypePrefix + typeSchema
|
|
677
|
+
const thisWildcardSchema = `${thisSchema}Wildcard`
|
|
678
|
+
|
|
679
|
+
const anySchema = `Any${typeSchema}`
|
|
680
|
+
const anyWildcardSchema = `${anySchema}Wildcard`
|
|
681
|
+
const embeddedSchema = `Embedded${typeSchema}`
|
|
682
|
+
const embeddedWildcardSchema = `${embeddedSchema}Wildcard`
|
|
683
|
+
|
|
684
|
+
permutations[anySchema] ||= new Set([typeSchema])
|
|
685
|
+
permutations[embeddedWildcardSchema] ||= new Set()
|
|
686
|
+
permutations[embeddedSchema] ||= new Set()
|
|
687
|
+
permutations[anyWildcardSchema] ||= new Set([`${typeSchema}Wildcard`])
|
|
688
|
+
|
|
689
|
+
permutations[anySchema].add(thisSchema)
|
|
690
|
+
permutations[anyWildcardSchema].add(thisWildcardSchema)
|
|
691
|
+
|
|
692
|
+
permutations[embeddedSchema].add(thisSchema)
|
|
693
|
+
permutations[embeddedWildcardSchema].add(thisWildcardSchema)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const embeddedTypes = TypeId.getEmbeddableTypes(typeId, true)
|
|
697
|
+
|
|
698
|
+
for (const embeddedTypeId of embeddedTypes)
|
|
699
|
+
permutate(embeddedTypeId, `${parentTypeComposite}${TypeSep}${typeId}`)
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
for (const primaryTypeId in TypeId.EmbedTypeMap) {
|
|
703
|
+
const embeddedTypeIds = TypeId.EmbedTypeMap[primaryTypeId as TypeId.Embedding]
|
|
704
|
+
|
|
705
|
+
for (const embeddedTypeId of embeddedTypeIds)
|
|
706
|
+
permutate(embeddedTypeId, primaryTypeId)
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const anyIdSchemata: TRef<string>[] = []
|
|
710
|
+
const anyIdWildcardSchemata: TRef<string>[] = []
|
|
711
|
+
|
|
712
|
+
for (const $id in permutations) {
|
|
713
|
+
const schemaIds = Array.from(permutations[$id]).map((id) => Type.Ref(id))
|
|
714
|
+
ids[$id as IdSchemaName] = Type.Union(schemaIds, {
|
|
715
|
+
$id,
|
|
716
|
+
[JsonTypeDef]: { schema: JtdType.String() }
|
|
717
|
+
})
|
|
718
|
+
if ($id.endsWith('IdWildcard')) anyIdWildcardSchemata.push(...schemaIds)
|
|
719
|
+
else anyIdSchemata.push(...schemaIds)
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// @ts-expect-error
|
|
723
|
+
ids.AnyOracleRollableRowId = Type.Union(
|
|
724
|
+
Object.values(ids)
|
|
725
|
+
.filter((schema) => schema.$id?.endsWith('OracleRollableRowId'))
|
|
726
|
+
.map((schema) => Type.Ref(schema.$id as string)),
|
|
727
|
+
{
|
|
728
|
+
$id: 'AnyOracleRollableRowId',
|
|
729
|
+
[JsonTypeDef]: { schema: JtdType.String() }
|
|
730
|
+
}
|
|
731
|
+
)
|
|
732
|
+
// @ts-expect-error
|
|
733
|
+
|
|
734
|
+
ids.AnyOracleRollableRowIdWildcard = Type.Union(
|
|
735
|
+
Object.values(ids)
|
|
736
|
+
.flat()
|
|
737
|
+
.filter((schema) => schema.$id?.endsWith('OracleRollableRowIdWildcard'))
|
|
738
|
+
.map((schema) => Type.Ref(schema.$id as string)),
|
|
739
|
+
{
|
|
740
|
+
$id: 'AnyOracleRollableRowIdWildcard',
|
|
741
|
+
[JsonTypeDef]: { schema: JtdType.String() }
|
|
742
|
+
}
|
|
743
|
+
)
|
|
744
|
+
// @ts-expect-error
|
|
745
|
+
|
|
746
|
+
ids.AnyMoveConditionId = Type.Union(
|
|
747
|
+
Object.values(ids)
|
|
748
|
+
.filter(
|
|
749
|
+
(schema) =>
|
|
750
|
+
schema.$id?.endsWith('ConditionId') &&
|
|
751
|
+
!schema.$id?.endsWith('IdWildcard')
|
|
752
|
+
)
|
|
753
|
+
.map((schema) => Type.Ref(schema.$id as string)),
|
|
754
|
+
{
|
|
755
|
+
$id: 'AnyMoveConditionId',
|
|
756
|
+
[JsonTypeDef]: { schema: JtdType.String() }
|
|
757
|
+
}
|
|
758
|
+
)
|
|
759
|
+
// @ts-expect-error
|
|
760
|
+
|
|
761
|
+
ids.AnyMoveConditionIdWildcard = Type.Union(
|
|
762
|
+
Object.values(ids)
|
|
763
|
+
.flat()
|
|
764
|
+
.filter((schema) => schema.$id?.endsWith('ConditionIdWildcard'))
|
|
765
|
+
.map((schema) => Type.Ref(schema.$id as string)),
|
|
766
|
+
{
|
|
767
|
+
$id: 'AnyMoveConditionIdWildcard',
|
|
768
|
+
[JsonTypeDef]: { schema: JtdType.String() }
|
|
769
|
+
}
|
|
770
|
+
)
|
|
771
|
+
// @ts-expect-error
|
|
772
|
+
|
|
773
|
+
ids.AnyMoveOutcomeId = Type.Union(
|
|
774
|
+
Object.values(ids)
|
|
775
|
+
.filter(
|
|
776
|
+
(schema) =>
|
|
777
|
+
schema.$id?.endsWith('OutcomeId') && !schema.$id?.endsWith('IdWildcard')
|
|
778
|
+
)
|
|
779
|
+
.map((schema) => Type.Ref(schema.$id as string)),
|
|
780
|
+
{
|
|
781
|
+
$id: 'AnyMoveOutcomeId',
|
|
782
|
+
[JsonTypeDef]: { schema: JtdType.String() }
|
|
783
|
+
}
|
|
784
|
+
)
|
|
785
|
+
// @ts-expect-error
|
|
786
|
+
|
|
787
|
+
ids.AnyMoveOutcomeIdWildcard = Type.Union(
|
|
788
|
+
Object.values(ids)
|
|
789
|
+
.flat()
|
|
790
|
+
.filter((schema) => schema.$id?.endsWith('OutcomeIdWildcard'))
|
|
791
|
+
.map((schema) => Type.Ref(schema.$id as string)),
|
|
792
|
+
{
|
|
793
|
+
$id: 'AnyMoveOutcomeIdWildcard',
|
|
794
|
+
[JsonTypeDef]: { schema: JtdType.String() }
|
|
795
|
+
}
|
|
796
|
+
)
|
|
797
|
+
// @ts-expect-error
|
|
798
|
+
|
|
799
|
+
ids.AnyId = Type.Union(
|
|
800
|
+
Object.values(ids)
|
|
801
|
+
.flat()
|
|
802
|
+
.filter(
|
|
803
|
+
(schema) =>
|
|
804
|
+
schema.type === 'string' && !schema.$id?.endsWith('IdWildcard')
|
|
805
|
+
)
|
|
806
|
+
.map((schema) => Type.Ref(schema)),
|
|
807
|
+
{
|
|
808
|
+
$id: 'AnyId',
|
|
809
|
+
description:
|
|
810
|
+
'Represents any kind of non-wildcard ID, including IDs of embedded objects.',
|
|
811
|
+
[JsonTypeDef]: { schema: JtdType.String() }
|
|
812
|
+
}
|
|
813
|
+
)
|
|
814
|
+
// @ts-expect-error
|
|
815
|
+
|
|
816
|
+
ids.AnyIdWildcard = Type.Union(
|
|
817
|
+
Object.values(ids)
|
|
818
|
+
.flat()
|
|
819
|
+
.filter(
|
|
820
|
+
(schema) => schema.type === 'string' && schema.$id?.endsWith('IdWildcard')
|
|
821
|
+
)
|
|
822
|
+
.map((schema) => Type.Ref(schema)),
|
|
823
|
+
{
|
|
824
|
+
$id: 'AnyIdWildcard',
|
|
825
|
+
description:
|
|
826
|
+
'Represents any kind of wildcard ID, including IDs of embedded objects.',
|
|
827
|
+
[JsonTypeDef]: { schema: JtdType.String() }
|
|
828
|
+
}
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
// generate union types from IDs that are marked Any*Id
|
|
832
|
+
|
|
833
|
+
const idUnionNamePattern = /^Any(?<typeName>[A-Z][A-z]+)Id$/
|
|
834
|
+
|
|
835
|
+
for (const idName in ids) {
|
|
836
|
+
const match = idName.match(idUnionNamePattern)
|
|
837
|
+
if (!match?.groups) continue
|
|
838
|
+
const { typeName } = match.groups
|
|
839
|
+
const typeId = snakeCase(typeName) as TypeId.Any
|
|
840
|
+
if (!TypeId.isPrimary(typeId)) continue
|
|
841
|
+
|
|
842
|
+
const $id = `Any${typeName}` as keyof typeof ids
|
|
843
|
+
ids[$id] = Type.Union([Type.Ref(typeName), Type.Ref(`Embedded${typeName}`)], {
|
|
844
|
+
$id,
|
|
845
|
+
[JsonTypeDef]: { skip: true }
|
|
846
|
+
})
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
export default ids
|