@atscript/typescript 0.1.32 → 0.1.34

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/utils.cjs CHANGED
@@ -436,6 +436,26 @@ var ValidatorError = class extends Error {
436
436
 
437
437
  //#endregion
438
438
  //#region packages/typescript/src/annotated-type.ts
439
+ const COMPLEX_KINDS = new Set([
440
+ "union",
441
+ "intersection",
442
+ "tuple"
443
+ ]);
444
+ const NON_PRIMITIVE_KINDS = new Set(["array", "object"]);
445
+ /** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
446
+ return new Validator(this, opts);
447
+ }
448
+ function createAnnotatedTypeNode(type, metadata, opts) {
449
+ return {
450
+ __is_atscript_annotated_type: true,
451
+ type,
452
+ metadata,
453
+ validator: validatorMethod,
454
+ id: opts?.id,
455
+ optional: opts?.optional,
456
+ ref: opts?.ref
457
+ };
458
+ }
439
459
  function isAnnotatedType(type) {
440
460
  return type && type.__is_atscript_annotated_type;
441
461
  }
@@ -454,32 +474,22 @@ function cloneRefProp(parentType, propName) {
454
474
  const existing = objType.props.get(propName);
455
475
  if (!existing) return;
456
476
  const clonedType = cloneTypeDef(existing.type);
457
- objType.props.set(propName, {
458
- __is_atscript_annotated_type: true,
459
- type: clonedType,
460
- metadata: new Map(existing.metadata),
477
+ objType.props.set(propName, createAnnotatedTypeNode(clonedType, new Map(existing.metadata), {
461
478
  id: existing.id,
462
- optional: existing.optional,
463
- validator(opts) {
464
- return new Validator(this, opts);
465
- }
466
- });
479
+ optional: existing.optional
480
+ }));
467
481
  }
468
482
  function cloneTypeDef(type) {
469
483
  if (type.kind === "object") {
470
484
  const obj = type;
485
+ const props = new Map();
486
+ for (const [k, v] of obj.props) props.set(k, createAnnotatedTypeNode(v.type, new Map(v.metadata), {
487
+ id: v.id,
488
+ optional: v.optional
489
+ }));
471
490
  return {
472
491
  kind: "object",
473
- props: new Map(Array.from(obj.props.entries()).map(([k, v]) => [k, {
474
- __is_atscript_annotated_type: true,
475
- type: v.type,
476
- metadata: new Map(v.metadata),
477
- id: v.id,
478
- optional: v.optional,
479
- validator(opts) {
480
- return new Validator(this, opts);
481
- }
482
- }])),
492
+ props,
483
493
  propsPatterns: [...obj.propsPatterns],
484
494
  tags: new Set(obj.tags)
485
495
  };
@@ -509,38 +519,24 @@ function defineAnnotatedType(_kind, base) {
509
519
  const kind = _kind || "";
510
520
  const type = base?.type || {};
511
521
  type.kind = kind;
512
- if ([
513
- "union",
514
- "intersection",
515
- "tuple"
516
- ].includes(kind)) type.items = [];
522
+ if (COMPLEX_KINDS.has(kind)) type.items = [];
517
523
  if (kind === "object") {
518
524
  type.props = new Map();
519
525
  type.propsPatterns = [];
520
526
  }
521
527
  type.tags = new Set();
522
528
  const metadata = base?.metadata || new Map();
523
- if (base) Object.assign(base, {
524
- __is_atscript_annotated_type: true,
525
- metadata,
526
- type,
527
- validator(opts) {
528
- return new Validator(this, opts);
529
- }
530
- });
531
- else base = {
529
+ const payload = {
532
530
  __is_atscript_annotated_type: true,
533
531
  metadata,
534
532
  type,
535
- validator(opts) {
536
- return new Validator(this, opts);
537
- }
533
+ validator: validatorMethod
538
534
  };
535
+ base = base ? Object.assign(base, payload) : payload;
539
536
  const handle = {
540
537
  $type: base,
541
538
  $def: type,
542
539
  $metadata: metadata,
543
- _existingObject: undefined,
544
540
  tags(...tags) {
545
541
  for (const tag of tags) this.$def.tags.add(tag);
546
542
  return this;
@@ -581,26 +577,56 @@ else base = {
581
577
  return this;
582
578
  },
583
579
  refTo(type$1, chain) {
584
- let newBase = type$1;
585
- const typeName = type$1.name || "Unknown";
586
- if (isAnnotatedType(newBase)) {
587
- let keys = "";
588
- for (const c of chain || []) {
589
- keys += `["${c}"]`;
580
+ if (isAnnotatedType(type$1)) {
581
+ let newBase = type$1;
582
+ const typeName = type$1.name || "Unknown";
583
+ if (chain) for (let i = 0; i < chain.length; i++) {
584
+ const c = chain[i];
590
585
  if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
591
- else throw new Error(`Can't find prop ${typeName}${keys}`);
586
+ else {
587
+ const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
588
+ throw new Error(`Can't find prop ${typeName}${keys}`);
589
+ }
592
590
  }
593
- if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
594
- else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
595
- this.$type = {
596
- __is_atscript_annotated_type: true,
597
- type: newBase.type,
598
- metadata,
591
+ this.$type = createAnnotatedTypeNode(newBase.type, metadata, {
599
592
  id: newBase.id,
600
- validator(opts) {
601
- return new Validator(this, opts);
602
- }
603
- };
593
+ ref: chain && chain.length > 0 ? {
594
+ type: () => type$1,
595
+ field: chain.join(".")
596
+ } : undefined
597
+ });
598
+ } else if (typeof type$1 === "function") {
599
+ const lazyType = type$1;
600
+ this.$type = createAnnotatedTypeNode({ kind: "" }, metadata, { ref: {
601
+ type: lazyType,
602
+ field: chain ? chain.join(".") : ""
603
+ } });
604
+ const node = this.$type;
605
+ const placeholder = node.type;
606
+ Object.defineProperty(node, "type", {
607
+ get() {
608
+ const t = lazyType();
609
+ if (!isAnnotatedType(t)) {
610
+ Object.defineProperty(node, "type", {
611
+ value: placeholder,
612
+ writable: false,
613
+ configurable: true
614
+ });
615
+ return placeholder;
616
+ }
617
+ let target = t;
618
+ if (chain) for (const c of chain) if (target.type.kind === "object" && target.type.props.has(c)) target = target.type.props.get(c);
619
+ else return t.type;
620
+ node.id = target.id || t.id;
621
+ Object.defineProperty(node, "type", {
622
+ value: target.type,
623
+ writable: false,
624
+ configurable: true
625
+ });
626
+ return target.type;
627
+ },
628
+ configurable: true
629
+ });
604
630
  } else throw new Error(`${type$1} is not annotated type`);
605
631
  return this;
606
632
  },
@@ -619,13 +645,9 @@ function isPhantomType(def) {
619
645
  return def.type.kind === "" && def.type.designType === "phantom";
620
646
  }
621
647
  function isAnnotatedTypeOfPrimitive(t) {
622
- if (["array", "object"].includes(t.type.kind)) return false;
648
+ if (NON_PRIMITIVE_KINDS.has(t.type.kind)) return false;
623
649
  if (!t.type.kind) return true;
624
- if ([
625
- "union",
626
- "tuple",
627
- "intersection"
628
- ].includes(t.type.kind)) {
650
+ if (COMPLEX_KINDS.has(t.type.kind)) {
629
651
  for (const item of t.type.items) if (!isAnnotatedTypeOfPrimitive(item)) return false;
630
652
  return true;
631
653
  }
@@ -664,10 +686,10 @@ function forAnnotatedType(def, handlers) {
664
686
  const firstObj = items[0].type;
665
687
  const candidates = [];
666
688
  for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
667
- const validCandidates = [];
689
+ let result = null;
668
690
  for (const candidate of candidates) {
669
691
  const values = new Set();
670
- const mapping = {};
692
+ const indexMapping = {};
671
693
  let valid = true;
672
694
  for (let i = 0; i < items.length; i++) {
673
695
  const obj = items[i].type;
@@ -682,19 +704,21 @@ function forAnnotatedType(def, handlers) {
682
704
  break;
683
705
  }
684
706
  values.add(val);
685
- mapping[String(val)] = `#/oneOf/${i}`;
707
+ indexMapping[String(val)] = i;
708
+ }
709
+ if (valid) {
710
+ if (result) return null;
711
+ result = {
712
+ propertyName: candidate,
713
+ indexMapping
714
+ };
686
715
  }
687
- if (valid) validCandidates.push({
688
- propertyName: candidate,
689
- mapping
690
- });
691
716
  }
692
- if (validCandidates.length === 1) return validCandidates[0];
693
- return null;
717
+ return result;
694
718
  }
695
719
  function buildJsonSchema(type) {
696
720
  const defs = {};
697
- let isRoot = true;
721
+ let hasDefs = false;
698
722
  const buildObject = (d) => {
699
723
  const properties = {};
700
724
  const required = [];
@@ -711,15 +735,15 @@ function buildJsonSchema(type) {
711
735
  return schema$1;
712
736
  };
713
737
  const build$1 = (def) => {
714
- if (def.id && def.type.kind === "object" && !isRoot) {
738
+ if (def.id && def.type.kind === "object" && def !== type) {
715
739
  const name = def.id;
716
740
  if (!defs[name]) {
741
+ hasDefs = true;
717
742
  defs[name] = {};
718
743
  defs[name] = buildObject(def);
719
744
  }
720
745
  return { $ref: `#/$defs/${name}` };
721
746
  }
722
- isRoot = false;
723
747
  const meta = def.metadata;
724
748
  return forAnnotatedType(def, {
725
749
  phantom() {
@@ -744,11 +768,9 @@ function buildJsonSchema(type) {
744
768
  if (disc) {
745
769
  const oneOf = d.type.items.map(build$1);
746
770
  const mapping = {};
747
- for (const [val, origPath] of Object.entries(disc.mapping)) {
748
- const idx = Number.parseInt(origPath.split("/").pop(), 10);
771
+ for (const [val, idx] of Object.entries(disc.indexMapping)) {
749
772
  const item = d.type.items[idx];
750
- if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
751
- else mapping[val] = origPath;
773
+ mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
752
774
  }
753
775
  return {
754
776
  oneOf,
@@ -798,7 +820,7 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
798
820
  });
799
821
  };
800
822
  const schema = build$1(type);
801
- if (Object.keys(defs).length > 0) return {
823
+ if (hasDefs) return {
802
824
  ...schema,
803
825
  $defs: defs
804
826
  };
@@ -813,6 +835,8 @@ function fromJsonSchema(schema) {
813
835
  const refName = s.$ref.replace(/^#\/(\$defs|definitions)\//, "");
814
836
  if (resolved.has(refName)) return resolved.get(refName);
815
837
  if (defsSource[refName]) {
838
+ const placeholder = defineAnnotatedType().designType("any").$type;
839
+ resolved.set(refName, placeholder);
816
840
  const type = convert(defsSource[refName]);
817
841
  resolved.set(refName, type);
818
842
  return type;
@@ -1036,6 +1060,7 @@ function throwFeatureDisabled(feature, option, annotation) {
1036
1060
  function flattenAnnotatedType(type, options) {
1037
1061
  const flatMap = new Map();
1038
1062
  const skipPhantom = !!options?.excludePhantomTypes;
1063
+ const visitedIds = new Set();
1039
1064
  function addFieldToFlatMap(name, def) {
1040
1065
  const existing = flatMap.get(name);
1041
1066
  if (existing) {
@@ -1072,6 +1097,13 @@ else flatUnion.item(existing);
1072
1097
  }
1073
1098
  }
1074
1099
  function flattenType(def, prefix = "", inComplexTypeOrArray = false) {
1100
+ const typeId = def.id;
1101
+ if (typeId && visitedIds.has(typeId)) {
1102
+ addFieldToFlatMap(prefix || "", def);
1103
+ if (prefix) options?.onField?.(prefix, def, def.metadata);
1104
+ return;
1105
+ }
1106
+ if (typeId) visitedIds.add(typeId);
1075
1107
  switch (def.type.kind) {
1076
1108
  case "object": {
1077
1109
  addFieldToFlatMap(prefix || "", def);
@@ -1118,20 +1150,31 @@ else flatUnion.item(existing);
1118
1150
  //#region packages/typescript/src/serialize.ts
1119
1151
  const SERIALIZE_VERSION = 1;
1120
1152
  function serializeAnnotatedType(type, options) {
1121
- const result = serializeNode(type, [], options);
1153
+ const visited = new Set();
1154
+ const result = serializeNode(type, [], options, visited);
1122
1155
  result.$v = SERIALIZE_VERSION;
1123
1156
  return result;
1124
1157
  }
1125
- function serializeNode(def, path, options) {
1158
+ function serializeNode(def, path, options, visited) {
1159
+ if (def.id && visited.has(def.id)) return {
1160
+ type: {
1161
+ kind: "$ref",
1162
+ id: def.id
1163
+ },
1164
+ metadata: {},
1165
+ ...def.optional ? { optional: true } : {},
1166
+ id: def.id
1167
+ };
1168
+ if (def.id) visited.add(def.id);
1126
1169
  const result = {
1127
- type: serializeTypeDef(def, path, options),
1170
+ type: serializeTypeDef(def, path, options, visited),
1128
1171
  metadata: serializeMetadata(def.metadata, path, def.type.kind, options)
1129
1172
  };
1130
1173
  if (def.optional) result.optional = true;
1131
1174
  if (def.id) result.id = def.id;
1132
1175
  return result;
1133
1176
  }
1134
- function serializeTypeDef(def, path, options) {
1177
+ function serializeTypeDef(def, path, options, visited) {
1135
1178
  return forAnnotatedType(def, {
1136
1179
  phantom(d) {
1137
1180
  return {
@@ -1151,13 +1194,13 @@ function serializeTypeDef(def, path, options) {
1151
1194
  },
1152
1195
  object(d) {
1153
1196
  const props = {};
1154
- for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options);
1197
+ for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options, visited);
1155
1198
  const propsPatterns = d.type.propsPatterns.map((pp) => ({
1156
1199
  pattern: {
1157
1200
  source: pp.pattern.source,
1158
1201
  flags: pp.pattern.flags
1159
1202
  },
1160
- def: serializeNode(pp.def, path, options)
1203
+ def: serializeNode(pp.def, path, options, visited)
1161
1204
  }));
1162
1205
  return {
1163
1206
  kind: "object",
@@ -1169,28 +1212,28 @@ function serializeTypeDef(def, path, options) {
1169
1212
  array(d) {
1170
1213
  return {
1171
1214
  kind: "array",
1172
- of: serializeNode(d.type.of, path, options),
1215
+ of: serializeNode(d.type.of, path, options, visited),
1173
1216
  tags: Array.from(d.type.tags)
1174
1217
  };
1175
1218
  },
1176
1219
  union(d) {
1177
1220
  return {
1178
1221
  kind: "union",
1179
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1222
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1180
1223
  tags: Array.from(d.type.tags)
1181
1224
  };
1182
1225
  },
1183
1226
  intersection(d) {
1184
1227
  return {
1185
1228
  kind: "intersection",
1186
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1229
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1187
1230
  tags: Array.from(d.type.tags)
1188
1231
  };
1189
1232
  },
1190
1233
  tuple(d) {
1191
1234
  return {
1192
1235
  kind: "tuple",
1193
- items: d.type.items.map((item) => serializeNode(item, path, options)),
1236
+ items: d.type.items.map((item) => serializeNode(item, path, options, visited)),
1194
1237
  tags: Array.from(d.type.tags)
1195
1238
  };
1196
1239
  }
@@ -1223,16 +1266,10 @@ function deserializeAnnotatedType(data) {
1223
1266
  function deserializeNode(data) {
1224
1267
  const metadata = new Map(Object.entries(data.metadata));
1225
1268
  const type = deserializeTypeDef(data.type);
1226
- const result = {
1227
- __is_atscript_annotated_type: true,
1228
- type,
1229
- metadata,
1230
- validator(opts) {
1231
- return new Validator(this, opts);
1232
- }
1233
- };
1234
- if (data.optional) result.optional = true;
1235
- if (data.id) result.id = data.id;
1269
+ const result = createAnnotatedTypeNode(type, metadata, {
1270
+ optional: data.optional || undefined,
1271
+ id: data.id || undefined
1272
+ });
1236
1273
  return result;
1237
1274
  }
1238
1275
  function deserializeTypeDef(t) {
@@ -1273,6 +1310,12 @@ function deserializeTypeDef(t) {
1273
1310
  items: t.items.map((item) => deserializeNode(item)),
1274
1311
  tags
1275
1312
  };
1313
+ case "$ref": return {
1314
+ kind: "object",
1315
+ props: new Map(),
1316
+ propsPatterns: [],
1317
+ tags
1318
+ };
1276
1319
  default: throw new Error(`Unknown serialized type kind "${t.kind}"`);
1277
1320
  }
1278
1321
  }
@@ -1284,6 +1327,7 @@ exports.ValidatorError = ValidatorError
1284
1327
  exports.annotate = annotate
1285
1328
  exports.buildJsonSchema = buildJsonSchema
1286
1329
  exports.cloneRefProp = cloneRefProp
1330
+ exports.createAnnotatedTypeNode = createAnnotatedTypeNode
1287
1331
  exports.createDataFromAnnotatedType = createDataFromAnnotatedType
1288
1332
  exports.defineAnnotatedType = defineAnnotatedType
1289
1333
  exports.deserializeAnnotatedType = deserializeAnnotatedType
package/dist/utils.d.ts CHANGED
@@ -96,6 +96,18 @@ declare class ValidatorError extends Error {
96
96
  constructor(errors: TError[]);
97
97
  }
98
98
 
99
+ /**
100
+ * Creates a minimal annotated type node from a type def and metadata map.
101
+ * Centralises the shape so callers never duplicate the boilerplate.
102
+ */
103
+ declare function createAnnotatedTypeNode(type: TAtscriptTypeDef, metadata: TMetadataMap<AtscriptMetadata>, opts?: {
104
+ id?: string;
105
+ optional?: boolean;
106
+ ref?: {
107
+ type: () => TAtscriptAnnotatedType;
108
+ field: string;
109
+ };
110
+ }): TAtscriptAnnotatedType;
99
111
  /** Type definition for union, intersection, or tuple types. */
100
112
  interface TAtscriptTypeComplex<DataType = unknown> {
101
113
  kind: 'union' | 'intersection' | 'tuple';
@@ -183,6 +195,10 @@ interface TAtscriptAnnotatedType<T extends TAtscriptTypeDef = TAtscriptTypeDef,
183
195
  metadata: TMetadataMap<AtscriptMetadata>;
184
196
  optional?: boolean;
185
197
  id?: string;
198
+ ref?: {
199
+ type: () => TAtscriptAnnotatedType;
200
+ field: string;
201
+ };
186
202
  }
187
203
  /** An annotated type that is also a class constructor (i.e. a generated interface class). */
188
204
  type TAtscriptAnnotatedTypeConstructor = TAtscriptAnnotatedType & (new (...args: any[]) => any);
@@ -235,7 +251,6 @@ interface TAnnotatedTypeHandle {
235
251
  kind: TKind;
236
252
  } & Omit<TAtscriptTypeComplex, 'kind'> & Omit<TAtscriptTypeFinal, 'kind'> & Omit<TAtscriptTypeArray, 'kind'> & Omit<TAtscriptTypeObject<string>, 'kind'>;
237
253
  $metadata: TMetadataMap<AtscriptMetadata>;
238
- _existingObject: TAtscriptAnnotatedType | undefined;
239
254
  tags(...tags: string[]): TAnnotatedTypeHandle;
240
255
  designType(value: TAtscriptTypeFinal['designType']): TAnnotatedTypeHandle;
241
256
  value(value: string | number | boolean): TAnnotatedTypeHandle;
@@ -245,9 +260,11 @@ interface TAnnotatedTypeHandle {
245
260
  propPattern(pattern: RegExp, value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
246
261
  optional(value?: boolean): TAnnotatedTypeHandle;
247
262
  copyMetadata(fromMetadata: TMetadataMap<AtscriptMetadata>): TAnnotatedTypeHandle;
248
- refTo(type: TAtscriptAnnotatedType & {
263
+ refTo(type: (TAtscriptAnnotatedType & {
249
264
  name?: string;
250
- }, chain?: string[]): TAnnotatedTypeHandle;
265
+ }) | (() => TAtscriptAnnotatedType & {
266
+ name?: string;
267
+ }), chain?: string[]): TAnnotatedTypeHandle;
251
268
  annotate(key: keyof AtscriptMetadata, value: any, asArray?: boolean): TAnnotatedTypeHandle;
252
269
  id(value: string): TAnnotatedTypeHandle;
253
270
  }
@@ -471,7 +488,11 @@ interface TSerializedTypeComplex {
471
488
  items: TSerializedAnnotatedTypeInner[];
472
489
  tags: string[];
473
490
  }
474
- type TSerializedTypeDef = TSerializedTypeFinal | TSerializedTypeObject | TSerializedTypeArray | TSerializedTypeComplex;
491
+ interface TSerializedTypeRef {
492
+ kind: '$ref';
493
+ id: string;
494
+ }
495
+ type TSerializedTypeDef = TSerializedTypeFinal | TSerializedTypeObject | TSerializedTypeArray | TSerializedTypeComplex | TSerializedTypeRef;
475
496
  /** Context passed to {@link TSerializeOptions.processAnnotation} for each annotation entry. */
476
497
  interface TProcessAnnotationContext {
477
498
  /** Annotation key, e.g. "meta.label" */
@@ -559,5 +580,5 @@ type PrimaryKeyOf<T> = T extends {
559
580
  __pk: infer PK;
560
581
  } ? PK : unknown;
561
582
 
562
- export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
583
+ export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createAnnotatedTypeNode, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
563
584
  export type { FlatOf, InferDataType, PrimaryKeyOf, TAnnotatedTypeHandle, TAtscriptAnnotatedType, TAtscriptAnnotatedTypeConstructor, TAtscriptDataType, TAtscriptTypeArray, TAtscriptTypeComplex, TAtscriptTypeDef, TAtscriptTypeFinal, TAtscriptTypeObject, TCreateDataOptions, TFlattenOptions, TJsonSchema, TMetadataMap, TProcessAnnotationContext, TSerializeOptions, TSerializedAnnotatedType, TSerializedAnnotatedTypeInner, TSerializedTypeArray, TSerializedTypeComplex, TSerializedTypeDef, TSerializedTypeFinal, TSerializedTypeObject, TValidatorOptions, TValidatorPlugin, TValidatorPluginContext, TValueResolver };