@pbkware/fielded-text-web 0.1.0 → 0.2.1

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 (202) hide show
  1. package/README.md +68 -0
  2. package/dist/api/ft-reader.js +1 -1
  3. package/dist/api/ft-reader.js.map +1 -1
  4. package/dist/api/ft-serialization.js +4 -4
  5. package/dist/api/ft-serialization.js.map +1 -1
  6. package/dist/factory/ft-field-factory.js +1 -1
  7. package/dist/factory/ft-field-factory.js.map +1 -1
  8. package/dist/factory/ft-sequence-redirect-factory.js +3 -3
  9. package/dist/factory/ft-sequence-redirect-factory.js.map +1 -1
  10. package/dist/fields/definitions/ft-boolean-field-definition.js +1 -1
  11. package/dist/fields/definitions/ft-boolean-field-definition.js.map +1 -1
  12. package/dist/fields/definitions/ft-date-time-field-definition.js +1 -1
  13. package/dist/fields/definitions/ft-date-time-field-definition.js.map +1 -1
  14. package/dist/fields/definitions/ft-decimal-field-definition.js +1 -1
  15. package/dist/fields/definitions/ft-decimal-field-definition.js.map +1 -1
  16. package/dist/fields/definitions/ft-field-definition.js +1 -1
  17. package/dist/fields/definitions/ft-field-definition.js.map +1 -1
  18. package/dist/fields/definitions/ft-float-field-definition.js +1 -1
  19. package/dist/fields/definitions/ft-float-field-definition.js.map +1 -1
  20. package/dist/fields/definitions/ft-integer-field-definition.js +1 -1
  21. package/dist/fields/definitions/ft-integer-field-definition.js.map +1 -1
  22. package/dist/fields/definitions/ft-string-field-definition.js +1 -1
  23. package/dist/fields/definitions/ft-string-field-definition.js.map +1 -1
  24. package/dist/fields/instances/errors/ft-field-null-error.js +11 -0
  25. package/dist/fields/instances/errors/ft-field-null-error.js.map +1 -0
  26. package/dist/fields/instances/errors/ft-field-type-error.js +11 -0
  27. package/dist/fields/instances/errors/ft-field-type-error.js.map +1 -0
  28. package/dist/fields/instances/ft-boolean-field.js +14 -6
  29. package/dist/fields/instances/ft-boolean-field.js.map +1 -1
  30. package/dist/fields/instances/ft-date-time-field.js +26 -8
  31. package/dist/fields/instances/ft-date-time-field.js.map +1 -1
  32. package/dist/fields/instances/ft-decimal-field.js +14 -6
  33. package/dist/fields/instances/ft-decimal-field.js.map +1 -1
  34. package/dist/fields/instances/ft-field.js +315 -123
  35. package/dist/fields/instances/ft-field.js.map +1 -1
  36. package/dist/fields/instances/ft-float-field.js +14 -6
  37. package/dist/fields/instances/ft-float-field.js.map +1 -1
  38. package/dist/fields/instances/ft-generic-field.js +29 -67
  39. package/dist/fields/instances/ft-generic-field.js.map +1 -1
  40. package/dist/fields/instances/ft-integer-field.js +24 -8
  41. package/dist/fields/instances/ft-integer-field.js.map +1 -1
  42. package/dist/fields/instances/ft-string-field.js +13 -13
  43. package/dist/fields/instances/ft-string-field.js.map +1 -1
  44. package/dist/index.d.ts +616 -288
  45. package/dist/index.js +12 -3
  46. package/dist/index.js.map +1 -1
  47. package/dist/meta/sequences/core/ft-meta-sequence-list.js +4 -2
  48. package/dist/meta/sequences/core/ft-meta-sequence-list.js.map +1 -1
  49. package/dist/meta/sequences/redirects/ft-meta-sequence-redirect-list.js +1 -1
  50. package/dist/meta/sequences/redirects/ft-meta-sequence-redirect-list.js.map +1 -1
  51. package/dist/meta-serialization/format/ft-xml-meta-serialization.js +4 -6
  52. package/dist/meta-serialization/format/ft-xml-meta-serialization.js.map +1 -1
  53. package/dist/meta-serialization/styles/ft-number-styles-meta-serialization.js +1 -1
  54. package/dist/meta-serialization/styles/ft-number-styles-meta-serialization.js.map +1 -1
  55. package/dist/meta-serialization/types/date-time-meta-serialization.js +1 -1
  56. package/dist/meta-serialization/types/date-time-meta-serialization.js.map +1 -1
  57. package/dist/meta-serialization/types/enums/sequence-redirect-type-meta-serialisation.js +1 -1
  58. package/dist/meta-serialization/types/enums/sequence-redirect-type-meta-serialisation.js.map +1 -1
  59. package/dist/meta-serialization/types/float-meta-serialization.js +2 -2
  60. package/dist/meta-serialization/types/float-meta-serialization.js.map +1 -1
  61. package/dist/meta-serialization/types/integer-float-meta-serialization.js +2 -2
  62. package/dist/meta-serialization/types/integer-float-meta-serialization.js.map +1 -1
  63. package/dist/meta-serialization/utils/implicit-explicit-index-sorter.js +4 -10
  64. package/dist/meta-serialization/utils/implicit-explicit-index-sorter.js.map +1 -1
  65. package/dist/meta-serialization/utils/meta-serialization-sequence-name-resolver.js +25 -2
  66. package/dist/meta-serialization/utils/meta-serialization-sequence-name-resolver.js.map +1 -1
  67. package/dist/meta-serialization/utils/sequence-item-field-indices.js +2 -2
  68. package/dist/meta-serialization/utils/sequence-item-field-indices.js.map +1 -1
  69. package/dist/sequences/redirects/ft-case-insensitive-string-sequence-redirect.js +3 -1
  70. package/dist/sequences/redirects/ft-case-insensitive-string-sequence-redirect.js.map +1 -1
  71. package/dist/serialization/char-reader.js +2 -2
  72. package/dist/serialization/char-reader.js.map +1 -1
  73. package/dist/serialization/declaration-parser.js +1 -1
  74. package/dist/serialization/declaration-parser.js.map +1 -1
  75. package/dist/serialization/delimited-field-parser.js.map +1 -1
  76. package/dist/serialization/events/ft-field-heading-ready-event-args.js.map +1 -0
  77. package/dist/serialization/events/ft-field-value-ready-event-args.js.map +1 -0
  78. package/dist/serialization/events/ft-heading-line-finished-event-args.js.map +1 -0
  79. package/dist/serialization/events/ft-heading-line-started-event-args.js.map +1 -0
  80. package/dist/serialization/events/ft-record-finished-event-args.js.map +1 -0
  81. package/dist/serialization/events/ft-record-started-event-args.js.map +1 -0
  82. package/dist/serialization/events/ft-sequence-redirected-event-args.js.map +1 -0
  83. package/dist/serialization/fixed-width-field-parser.js +1 -1
  84. package/dist/serialization/fixed-width-field-parser.js.map +1 -1
  85. package/dist/serialization/ft-declared-parameters.js +9 -8
  86. package/dist/serialization/ft-declared-parameters.js.map +1 -1
  87. package/dist/serialization/{serialization-core.js → ft-serialization-core.js} +114 -11
  88. package/dist/serialization/ft-serialization-core.js.map +1 -0
  89. package/dist/{types/errors/ft-serialization-error.js → serialization/ft-serialization-error-code.js} +2 -2
  90. package/dist/serialization/ft-serialization-error-code.js.map +1 -0
  91. package/dist/{types/errors/ft-serialization-exception.js → serialization/ft-serialization-error.js} +7 -7
  92. package/dist/serialization/ft-serialization-error.js.map +1 -0
  93. package/dist/serialization/ft-serialization-reader.js +19 -47
  94. package/dist/serialization/ft-serialization-reader.js.map +1 -1
  95. package/dist/serialization/ft-serialization-writer.js +86 -49
  96. package/dist/serialization/ft-serialization-writer.js.map +1 -1
  97. package/dist/serialization/heading-line-record-parser.js +7 -7
  98. package/dist/serialization/heading-line-record-parser.js.map +1 -1
  99. package/dist/serialization/line-parser.js +1 -1
  100. package/dist/serialization/line-parser.js.map +1 -1
  101. package/dist/serialization/{ft-text-reader.js → text-reader/ft-string-reader.js} +1 -13
  102. package/dist/serialization/text-reader/ft-string-reader.js.map +1 -0
  103. package/dist/serialization/text-reader/ft-text-reader.js +13 -0
  104. package/dist/serialization/text-reader/ft-text-reader.js.map +1 -0
  105. package/dist/tsdoc-metadata.json +1 -1
  106. package/dist/types/enums/ft-heading-constraint.js +5 -0
  107. package/dist/types/enums/ft-heading-constraint.js.map +1 -1
  108. package/dist/utils/ft-comma-text.js +247 -0
  109. package/dist/utils/ft-comma-text.js.map +1 -0
  110. package/dist/utils/ft-internal-error.js +129 -0
  111. package/dist/utils/ft-internal-error.js.map +1 -0
  112. package/dist/utils/ft-result.js +2 -0
  113. package/dist/utils/ft-result.js.map +1 -0
  114. package/dist/utils/number-parse.js +33 -0
  115. package/dist/utils/number-parse.js.map +1 -0
  116. package/dist/utils/string-builder.js +40 -0
  117. package/dist/utils/string-builder.js.map +1 -0
  118. package/package.json +27 -25
  119. package/src/api/ft-reader.ts +2 -1
  120. package/src/api/ft-serialization.ts +13 -13
  121. package/src/factory/ft-field-factory.ts +1 -1
  122. package/src/factory/ft-sequence-redirect-factory.ts +4 -4
  123. package/src/fields/definitions/ft-boolean-field-definition.ts +1 -1
  124. package/src/fields/definitions/ft-date-time-field-definition.ts +1 -1
  125. package/src/fields/definitions/ft-decimal-field-definition.ts +1 -1
  126. package/src/fields/definitions/ft-field-definition.ts +1 -1
  127. package/src/fields/definitions/ft-float-field-definition.ts +1 -1
  128. package/src/fields/definitions/ft-generic-field-definition.ts +1 -1
  129. package/src/fields/definitions/ft-integer-field-definition.ts +1 -1
  130. package/src/fields/definitions/ft-string-field-definition.ts +1 -1
  131. package/src/fields/instances/errors/ft-field-null-error.ts +10 -0
  132. package/src/fields/instances/errors/ft-field-type-error.ts +10 -0
  133. package/src/fields/instances/ft-boolean-field.ts +17 -6
  134. package/src/fields/instances/ft-date-time-field.ts +28 -7
  135. package/src/fields/instances/ft-decimal-field.ts +17 -6
  136. package/src/fields/instances/ft-field.ts +329 -145
  137. package/src/fields/instances/ft-float-field.ts +17 -6
  138. package/src/fields/instances/ft-generic-field.ts +32 -88
  139. package/src/fields/instances/ft-integer-field.ts +29 -7
  140. package/src/fields/instances/ft-string-field.ts +15 -12
  141. package/src/index.ts +21 -12
  142. package/src/meta/sequences/core/ft-meta-sequence-list.ts +4 -3
  143. package/src/meta/sequences/redirects/ft-meta-sequence-redirect-list.ts +1 -1
  144. package/src/meta-serialization/format/ft-xml-meta-serialization.ts +4 -7
  145. package/src/meta-serialization/styles/ft-number-styles-meta-serialization.ts +1 -1
  146. package/src/meta-serialization/types/date-time-meta-serialization.ts +1 -1
  147. package/src/meta-serialization/types/enums/meta-element-type-meta-serialization.ts +4 -4
  148. package/src/meta-serialization/types/enums/sequence-redirect-type-meta-serialisation.ts +1 -1
  149. package/src/meta-serialization/types/float-meta-serialization.ts +2 -2
  150. package/src/meta-serialization/types/integer-float-meta-serialization.ts +2 -2
  151. package/src/meta-serialization/utils/implicit-explicit-index-sorter.ts +18 -12
  152. package/src/meta-serialization/utils/meta-serialization-sequence-name-resolver.ts +25 -2
  153. package/src/meta-serialization/utils/sequence-item-field-indices.ts +2 -2
  154. package/src/sequences/redirects/ft-case-insensitive-string-sequence-redirect.ts +3 -1
  155. package/src/serialization/char-reader.ts +2 -2
  156. package/src/serialization/declaration-parser.ts +1 -1
  157. package/src/serialization/delimited-field-parser.ts +3 -3
  158. package/src/serialization/fixed-width-field-parser.ts +4 -4
  159. package/src/serialization/ft-declared-parameters.ts +12 -11
  160. package/src/serialization/{serialization-core.ts → ft-serialization-core.ts} +133 -17
  161. package/src/{types/errors/ft-serialization-error.ts → serialization/ft-serialization-error-code.ts} +2 -2
  162. package/src/{types/errors/ft-serialization-exception.ts → serialization/ft-serialization-error.ts} +14 -9
  163. package/src/serialization/ft-serialization-reader.ts +21 -54
  164. package/src/serialization/ft-serialization-writer.ts +97 -64
  165. package/src/serialization/heading-line-record-parser.ts +10 -10
  166. package/src/serialization/line-parser.ts +1 -1
  167. package/src/serialization/{ft-text-reader.ts → text-reader/ft-string-reader.ts} +1 -24
  168. package/src/serialization/text-reader/ft-text-reader.ts +24 -0
  169. package/src/types/enums/ft-heading-constraint.ts +5 -0
  170. package/src/utils/ft-comma-text.ts +285 -0
  171. package/src/utils/ft-internal-error.ts +143 -0
  172. package/src/utils/ft-result.ts +1 -0
  173. package/src/utils/number-parse.ts +35 -0
  174. package/src/utils/string-builder.ts +47 -0
  175. package/dist/serialization/ft-text-reader.js.map +0 -1
  176. package/dist/serialization/serialization-core.js.map +0 -1
  177. package/dist/types/errors/ft-internal-error.js +0 -14
  178. package/dist/types/errors/ft-internal-error.js.map +0 -1
  179. package/dist/types/errors/ft-serialization-error.js.map +0 -1
  180. package/dist/types/errors/ft-serialization-exception.js.map +0 -1
  181. package/dist/types/events/ft-field-heading-ready-event-args.js.map +0 -1
  182. package/dist/types/events/ft-field-value-ready-event-args.js.map +0 -1
  183. package/dist/types/events/ft-heading-line-finished-event-args.js.map +0 -1
  184. package/dist/types/events/ft-heading-line-started-event-args.js.map +0 -1
  185. package/dist/types/events/ft-record-finished-event-args.js.map +0 -1
  186. package/dist/types/events/ft-record-started-event-args.js.map +0 -1
  187. package/dist/types/events/ft-sequence-redirected-event-args.js.map +0 -1
  188. package/src/types/errors/ft-internal-error.ts +0 -15
  189. /package/dist/{types → serialization}/events/ft-field-heading-ready-event-args.js +0 -0
  190. /package/dist/{types → serialization}/events/ft-field-value-ready-event-args.js +0 -0
  191. /package/dist/{types → serialization}/events/ft-heading-line-finished-event-args.js +0 -0
  192. /package/dist/{types → serialization}/events/ft-heading-line-started-event-args.js +0 -0
  193. /package/dist/{types → serialization}/events/ft-record-finished-event-args.js +0 -0
  194. /package/dist/{types → serialization}/events/ft-record-started-event-args.js +0 -0
  195. /package/dist/{types → serialization}/events/ft-sequence-redirected-event-args.js +0 -0
  196. /package/src/{types → serialization}/events/ft-field-heading-ready-event-args.ts +0 -0
  197. /package/src/{types → serialization}/events/ft-field-value-ready-event-args.ts +0 -0
  198. /package/src/{types → serialization}/events/ft-heading-line-finished-event-args.ts +0 -0
  199. /package/src/{types → serialization}/events/ft-heading-line-started-event-args.ts +0 -0
  200. /package/src/{types → serialization}/events/ft-record-finished-event-args.ts +0 -0
  201. /package/src/{types → serialization}/events/ft-record-started-event-args.ts +0 -0
  202. /package/src/{types → serialization}/events/ft-sequence-redirected-event-args.ts +0 -0
@@ -11,8 +11,10 @@ import { FtQuotedType } from '../../types/enums/ft-quoted-type.js';
11
11
  import { FtSequenceInvokationDelay } from '../../types/enums/ft-sequence-invokation-delay.js';
12
12
  import { FtSequenceRedirectType } from '../../types/enums/ft-sequence-redirect-type.js';
13
13
  import { FtTruncateType } from '../../types/enums/ft-truncate-type.js';
14
- import { FtUnreachableCaseError } from '../../types/errors/ft-internal-error.js';
14
+ import { FtUnreachableCaseError } from '../../utils/ft-internal-error.js';
15
15
  import { FtFieldDefinition } from '../definitions/ft-field-definition.js';
16
+ import { FtFieldNullError } from './errors/ft-field-null-error.js';
17
+ import { FtFieldTypeError } from './errors/ft-field-type-error.js';
16
18
 
17
19
  /**
18
20
  * Abstract base class for all field instances.
@@ -26,7 +28,7 @@ export abstract class FtField {
26
28
 
27
29
  sequenceRedirectEvent: FtSequenceRedirectDelegate | undefined;
28
30
 
29
- protected valueAssigned = false;
31
+ protected _valueAssigned = false;
30
32
  protected quoted = false;
31
33
 
32
34
  private _index = 0;
@@ -48,7 +50,7 @@ export abstract class FtField {
48
50
  private _loadedLength = 0;
49
51
  private _loadedRawOffset = 0;
50
52
  private _loadedRawLength = 0;
51
- private _loadedValueText = '';
53
+ private _valueText = '';
52
54
 
53
55
  protected constructor(sequenceInvokation: FtSequenceInvokation, sequenceItem: FtSequenceItem, valueTextNullTrimmable: boolean) {
54
56
  if (!sequenceItem.fieldDefinition) {
@@ -121,12 +123,18 @@ export abstract class FtField {
121
123
  return this._headings;
122
124
  }
123
125
 
124
- get valueAssigned_(): boolean {
125
- return this.valueAssigned;
126
+ /**
127
+ * Indicates whether a value has been assigned to the field.
128
+ */
129
+ get valueAssigned(): boolean {
130
+ return this._valueAssigned;
126
131
  }
127
132
 
128
- get loadedValueText(): string {
129
- return this._loadedValueText;
133
+ /**
134
+ * The formatted text of the field value as loaded from the data or via {@link loadValueText}.
135
+ */
136
+ get valueText(): string {
137
+ return this._valueText;
130
138
  }
131
139
 
132
140
  // Properties delegating to definition
@@ -139,7 +147,7 @@ export abstract class FtField {
139
147
  }
140
148
 
141
149
  get name(): string {
142
- return this._definition.metaName;
150
+ return this._name;
143
151
  }
144
152
 
145
153
  get metaHeadings(): string[] {
@@ -252,7 +260,7 @@ export abstract class FtField {
252
260
 
253
261
  /** @internal */
254
262
  get asRedirectString(): string {
255
- return this.getAsNonNullValueText();
263
+ return this.formatValue();
256
264
  }
257
265
 
258
266
  /** @internal */
@@ -280,82 +288,192 @@ export abstract class FtField {
280
288
  return this.getAsRedirectDecimal();
281
289
  }
282
290
 
283
- // Public property accessors
284
- get asValueText(): string | null {
285
- return this.isNull() ? null : this.getAsNonNullValueText();
286
- }
287
-
288
- set asValueText(value: string | null) {
289
- this.setAsValueText(value);
290
- }
291
-
292
- get asObject(): unknown {
293
- return this.isNull() ? null : this.getAsNonNullObject();
291
+ /**
292
+ * The field value as an unspecified type.
293
+ *
294
+ * When setting the value, the actual type must be compatible with the field's expected type.
295
+ *
296
+ * @throws FtFieldNullError if the field is `null`.
297
+ */
298
+ get value(): FtField.Value {
299
+ if (this.isNull()) {
300
+ throw new FtFieldNullError(`Field value is null: ${this.name}`);
301
+ } else {
302
+ return this.getValue();
303
+ }
294
304
  }
295
305
 
296
- set asObject(value: unknown) {
297
- this.setAsObject(value);
306
+ set value(value: FtField.Value) {
307
+ this.setValue(value);
298
308
  }
299
309
 
300
- get asString(): string | null {
301
- return this.isNull() ? null : this.getAsNonNullString();
310
+ /**
311
+ * The field value as a string.
312
+ *
313
+ * @throws FtFieldTypeError if the field is not of data type `String`.
314
+ * @throws FtFieldNullError if the field is `null`.
315
+ */
316
+ get asString(): string {
317
+ if (this.isNull()) {
318
+ throw new FtFieldNullError(`String field value is null: ${this.name}`);
319
+ } else {
320
+ return this.getAsString();
321
+ }
302
322
  }
303
323
 
304
- set asString(value: string | null) {
324
+ set asString(value: string) {
305
325
  this.setAsString(value);
306
326
  }
307
327
 
328
+ /**
329
+ * The field value as a boolean.
330
+ *
331
+ * @throws FtFieldTypeError if the field is not of data type `Boolean`.
332
+ * @throws FtFieldNullError if the field is `null`.
333
+ */
308
334
  get asBoolean(): boolean {
309
- return this.getAsBoolean();
335
+ if (this.isNull()) {
336
+ throw new FtFieldNullError(`Boolean field value is null: ${this.name}`);
337
+ } else {
338
+ return this.getAsBoolean();
339
+ }
310
340
  }
311
341
 
312
342
  set asBoolean(value: boolean) {
313
343
  this.setAsBoolean(value);
314
344
  }
315
345
 
346
+ /**
347
+ * The field value as an integer.
348
+
349
+ * @throws FtFieldTypeError if the field is not of data type `Integer`.
350
+ * @throws FtFieldNullError if the field is `null`.
351
+ */
316
352
  get asInteger(): number {
317
- return this.getAsInteger();
353
+ if (this.isNull()) {
354
+ throw new FtFieldNullError(`Integer field value is null: ${this.name}`);
355
+ } else {
356
+ return this.getAsInteger();
357
+ }
318
358
  }
319
359
 
320
360
  set asInteger(value: number) {
321
361
  this.setAsInteger(value);
322
362
  }
323
363
 
364
+ /**
365
+ * The field value as a bigint.
366
+ *
367
+ * @throws FtFieldTypeError if the field is not of data type `BigInt`.
368
+ * @throws FtFieldNullError if the field is `null`.
369
+ */
324
370
  get asBigInt(): bigint {
325
- return this.getAsBigInt();
371
+ if (this.isNull()) {
372
+ throw new FtFieldNullError(`Integer field value is null: ${this.name}`);
373
+ } else {
374
+ return this.getAsBigInt();
375
+ }
326
376
  }
327
377
 
328
378
  set asBigInt(value: bigint) {
329
379
  this.setAsBigInt(value);
330
380
  }
331
381
 
382
+ /**
383
+ * The field value as a float.
384
+ *
385
+ * @throws FtFieldTypeError if the field is not of data type `Float`.
386
+ * @throws FtFieldNullError if the field is `null`.
387
+ */
332
388
  get asFloat(): number {
333
- return this.getAsFloat();
389
+ if (this.isNull()) {
390
+ throw new FtFieldNullError(`Float field value is null: ${this.name}`);
391
+ } else {
392
+ return this.getAsFloat();
393
+ }
334
394
  }
335
395
 
336
396
  set asFloat(value: number) {
337
397
  this.setAsFloat(value);
338
398
  }
339
399
 
400
+ /**
401
+ * The field value as a DateTime.
402
+ *
403
+ * @throws FtFieldTypeError if the field is not of data type `DateTime`.
404
+ * @throws FtFieldNullError if the field is `null`.
405
+ */
340
406
  get asDateTime(): Date {
341
- return this.getAsDateTime();
407
+ if (this.isNull()) {
408
+ throw new FtFieldNullError(`DateTime field value is null: ${this.name}`);
409
+ } else {
410
+ return this.getAsDateTime();
411
+ }
342
412
  }
343
413
 
344
414
  set asDateTime(value: Date) {
345
415
  this.setAsDateTime(value);
346
416
  }
347
417
 
418
+ /**
419
+ * The field value as a number (decimal).
420
+ *
421
+ * @throws FtFieldTypeError if the field is not of data type `Decimal`.
422
+ * @throws FtFieldNullError if the field is `null`.
423
+ */
348
424
  get asDecimal(): number {
349
- return this.getAsDecimal();
425
+ if (this.isNull()) {
426
+ throw new FtFieldNullError(`Decimal field value is null: ${this.name}`);
427
+ } else {
428
+ return this.getAsDecimal();
429
+ }
350
430
  }
351
431
 
352
432
  set asDecimal(value: number) {
353
433
  this.setAsDecimal(value);
354
434
  }
355
435
 
356
- // Nullable property accessors
436
+ /**
437
+ * The field value as an unspecified type or null.
438
+
439
+ * Use with caution, as this bypasses type safety. Ensure that the value being set is compatible with the field's expected type.
440
+ */
441
+ get nullableValue(): FtField.Value | null {
442
+ return this.isNull() ? null : this.getValue();
443
+ }
444
+
445
+ set nullableValue(value: FtField.Value | null) {
446
+ if (value === null) {
447
+ this.setNull();
448
+ } else {
449
+ this.setValue(value);
450
+ }
451
+ }
452
+
453
+ /**
454
+ * The field value as a string or null.
455
+ *
456
+ * @throws FtFieldTypeError if the field is not of data type `String`.
457
+ */
458
+ get asNullableString(): string | null {
459
+ return this.isNull() ? null : this.getAsString();
460
+ }
461
+
462
+ set asNullableString(value: string | null) {
463
+ if (value === null) {
464
+ this.setNull();
465
+ } else {
466
+ this.setAsString(value);
467
+ }
468
+ }
469
+
470
+ /**
471
+ * The field value as a boolean or null.
472
+ *
473
+ * @throws FtFieldTypeError if the field is not of data type `Boolean`.
474
+ */
357
475
  get asNullableBoolean(): boolean | null {
358
- return this.isNull() ? null : this.asBoolean;
476
+ return this.isNull() ? null : this.getAsBoolean();
359
477
  }
360
478
 
361
479
  set asNullableBoolean(value: boolean | null) {
@@ -366,8 +484,13 @@ export abstract class FtField {
366
484
  }
367
485
  }
368
486
 
487
+ /**
488
+ * The field value as an integer or null.
489
+ *
490
+ * @throws FtFieldTypeError if the field is not of data type `BigInt`.
491
+ */
369
492
  get asNullableInteger(): number | null {
370
- return this.isNull() ? null : this.asInteger;
493
+ return this.isNull() ? null : this.getAsInteger();
371
494
  }
372
495
 
373
496
  set asNullableInteger(value: number | null) {
@@ -378,8 +501,13 @@ export abstract class FtField {
378
501
  }
379
502
  }
380
503
 
504
+ /**
505
+ * The field value as a bigint or null.
506
+ *
507
+ * @throws FtFieldTypeError if the field is not of data type `BigInt`.
508
+ */
381
509
  get asNullableBigInt(): bigint | null {
382
- return this.isNull() ? null : this.asBigInt;
510
+ return this.isNull() ? null : this.getAsBigInt();
383
511
  }
384
512
 
385
513
  set asNullableBigInt(value: bigint | null) {
@@ -390,8 +518,13 @@ export abstract class FtField {
390
518
  }
391
519
  }
392
520
 
521
+ /**
522
+ * The field value as a float or null.
523
+ *
524
+ * @throws FtFieldTypeError if the field is not of data type `Float`.
525
+ */
393
526
  get asNullableFloat(): number | null {
394
- return this.isNull() ? null : this.asFloat;
527
+ return this.isNull() ? null : this.getAsFloat();
395
528
  }
396
529
 
397
530
  set asNullableFloat(value: number | null) {
@@ -402,8 +535,13 @@ export abstract class FtField {
402
535
  }
403
536
  }
404
537
 
538
+ /**
539
+ * The field value as a Date or null.
540
+ *
541
+ * @throws FtFieldTypeError if the field is not of data type `DateTime`.
542
+ */
405
543
  get asNullableDateTime(): Date | null {
406
- return this.isNull() ? null : this.asDateTime;
544
+ return this.isNull() ? null : this.getAsDateTime();
407
545
  }
408
546
 
409
547
  set asNullableDateTime(value: Date | null) {
@@ -414,8 +552,13 @@ export abstract class FtField {
414
552
  }
415
553
  }
416
554
 
555
+ /**
556
+ * The field value as a number (decimal) or null.
557
+ *
558
+ * @throws FtFieldTypeError if the field is not of data type `Decimal`.
559
+ */
417
560
  get asNullableDecimal(): number | null {
418
- return this.isNull() ? null : this.asDecimal;
561
+ return this.isNull() ? null : this.getAsDecimal();
419
562
  }
420
563
 
421
564
  set asNullableDecimal(value: number | null) {
@@ -426,70 +569,119 @@ export abstract class FtField {
426
569
  }
427
570
  }
428
571
 
429
- // Null handling
572
+ /**
573
+ * Checks if the field value is null.
574
+ * @returns True if the field value is null, false otherwise.
575
+ */
430
576
  isNull(): boolean {
431
577
  return this._valueIsNull;
432
578
  }
433
579
 
580
+ /**
581
+ * Sets the field value to null.
582
+ *
583
+ * Checks for sequence redirects that may be triggered by setting the field to null.
584
+ *
585
+ * @returns If this setting of field to null caused a sequence redirect, returns the index of the first field affected by the redirect. If no redirect occurred, returns FtField.NO_FIELDS_AFFECTED_INDEX.
586
+ * @throws Error if the field is constant and cannot be set to null.
587
+ */
434
588
  setNull(): number {
435
589
  if (this.constant) {
436
590
  throw new Error(`Cannot set constant field "${this.name}" to null`);
437
591
  } else {
438
592
  this._valueIsNull = true;
439
- this.valueAssigned = true;
593
+ this._valueAssigned = true;
440
594
 
441
595
  return this.checkNullSequenceRedirect(); // returns fieldsAffectedFromIndex
442
596
  }
443
597
  }
444
598
 
599
+ /**
600
+ * Loads the field value from its formatted text representation.
601
+ * @param valueText - Formatted text representation of field value. Formatting only includes data type formatting and not text formatting such as quoting, padding, truncating etc.
602
+ */
603
+ loadValueText(valueText: string): void {
604
+ this._valueText = valueText;
605
+ this.loadValueFromText(valueText);
606
+ }
607
+
608
+ /**
609
+ * Loads the field headings from an array of strings.
610
+ *
611
+ * The headings are loaded according to the field's heading constraint. See {@link loadHeading} for more information.
612
+ *
613
+ * @param value - An array of headings. The length of the array should be equal to {@link headingCount}. Extra headings are ignored and missing headings are set to empty strings.
614
+ */
445
615
  loadHeadings(value: string[]): void {
446
- for (let i = 0; i < value.length; i++) {
616
+ const valueCount = value.length;
617
+ for (let i = 0; i < valueCount; i++) {
447
618
  this.loadHeading(i, value[i]);
448
619
  }
620
+
621
+ const headingCount = this._headings.length;
622
+ if (valueCount < headingCount) {
623
+ for (let i = valueCount; i < headingCount; i++) {
624
+ this.loadHeading(i, '');
625
+ }
626
+ }
449
627
  }
450
628
 
629
+ /**
630
+ * Loads a heading into one of the field's heading lines.
631
+ *
632
+ * The heading is loaded according to the field's heading constraint.
633
+ *
634
+ * @param idx - Index of the heading to load.
635
+ * @param headingText - Text of the heading.
636
+ */
637
+
451
638
  loadHeading(idx: number, headingText: string): void {
452
- switch (this._definition.headingConstraint) {
453
- case FtHeadingConstraint.None:
454
- this._headings[idx] = headingText;
455
- break;
456
- case FtHeadingConstraint.AllConstant:
457
- if (headingText !== this._headings[idx]) {
458
- throw new Error(`Heading constraint violation: AllConstant at index ${idx} in field ${this.name}`);
459
- }
460
- break;
461
- case FtHeadingConstraint.MainConstant:
462
- if (idx !== this._definition.mainHeadingIndex) {
639
+ if (idx >= 0 && idx < this._headings.length) {
640
+ switch (this._definition.headingConstraint) {
641
+ case FtHeadingConstraint.None:
463
642
  this._headings[idx] = headingText;
464
- } else {
643
+ break;
644
+ case FtHeadingConstraint.AllConstant:
465
645
  if (headingText !== this._headings[idx]) {
466
- throw new Error(`Heading constraint violation: MainConstant in field ${this.name}`);
646
+ throw new Error(`Heading constraint violation: AllConstant at index ${idx} in field ${this.name}`);
467
647
  }
468
- }
469
- break;
470
- case FtHeadingConstraint.NameConstant:
471
- if (idx !== this._definition.mainHeadingIndex) {
472
- this._headings[idx] = headingText;
473
- } else {
474
- if (headingText.toLowerCase() === this._definition.metaName.toLowerCase()) {
648
+ break;
649
+ case FtHeadingConstraint.MainConstant:
650
+ if (idx !== this._definition.mainHeadingIndex) {
475
651
  this._headings[idx] = headingText;
476
652
  } else {
477
- throw new Error(`Heading constraint violation: NameConstant in field ${this.name}`);
653
+ if (headingText !== this._headings[idx]) {
654
+ throw new Error(`Heading constraint violation: MainConstant in field ${this.name}`);
655
+ }
478
656
  }
479
- }
480
- break;
481
- case FtHeadingConstraint.NameIsMain:
482
- this._headings[idx] = headingText;
483
- if (idx === this._definition.mainHeadingIndex) {
484
- this._name = headingText;
485
- }
486
- break;
487
- default:
488
- throw new FtUnreachableCaseError('FLH30773', this._definition.headingConstraint);
657
+ break;
658
+ case FtHeadingConstraint.NameConstant:
659
+ if (idx !== this._definition.mainHeadingIndex) {
660
+ this._headings[idx] = headingText;
661
+ } else {
662
+ if (headingText.toLowerCase() === this._definition.metaName.toLowerCase()) {
663
+ this._headings[idx] = headingText;
664
+ } else {
665
+ throw new Error(`Heading constraint violation: NameConstant in field ${this.name}`);
666
+ }
667
+ }
668
+ break;
669
+ case FtHeadingConstraint.NameIsMain:
670
+ this._headings[idx] = headingText;
671
+ if (idx === this._definition.mainHeadingIndex) {
672
+ this._name = headingText;
673
+ }
674
+ break;
675
+ default:
676
+ throw new FtUnreachableCaseError('FLH30773', this._definition.headingConstraint);
677
+ }
489
678
  }
490
679
  }
491
680
 
492
- /** @internal */
681
+ /**
682
+ * Checks if setting the field to null triggers a sequence redirect.
683
+ * @internal
684
+ */
493
685
  checkNullSequenceRedirect(): number {
494
686
  if (this._sequenceRedirectList.count > 0 && !this._sidelined) {
495
687
  let redirected = false;
@@ -518,7 +710,7 @@ export abstract class FtField {
518
710
  if (!this.constant) {
519
711
  this._valueIsNull = true;
520
712
  }
521
- this.valueAssigned = false;
713
+ this._valueAssigned = false;
522
714
  }
523
715
 
524
716
  // Loading methods (used by parsers)
@@ -533,27 +725,27 @@ export abstract class FtField {
533
725
 
534
726
  /** @internal */
535
727
  loadDelimitedValue(valueText: string, quoted: boolean): void {
536
- this._loadedValueText = valueText;
728
+ this._valueText = valueText;
537
729
 
538
730
  try {
539
731
  if (quoted) {
540
- this.loadNonNullValue(valueText);
732
+ this.loadValueFromText(valueText);
541
733
  } else {
542
734
  if (this._valueTextNullTrimmable) {
543
735
  valueText = valueText.trim();
544
736
  if (valueText.length === 0) {
545
737
  this.loadNullValue();
546
738
  } else {
547
- this.loadNonNullValue(valueText);
739
+ this.loadValueFromText(valueText);
548
740
  }
549
741
  } else {
550
742
  if (valueText.length !== 0) {
551
- this.loadNonNullValue(valueText);
743
+ this.loadValueFromText(valueText);
552
744
  } else {
553
745
  switch (this.valueQuotedType) {
554
746
  case FtQuotedType.Never:
555
747
  case FtQuotedType.Optional:
556
- this.loadNonNullValue(valueText);
748
+ this.loadValueFromText(valueText);
557
749
  break;
558
750
  case FtQuotedType.Always:
559
751
  this.loadNullValue();
@@ -571,12 +763,12 @@ export abstract class FtField {
571
763
 
572
764
  /** @internal */
573
765
  loadFixedWidthValue(valueText: string): void {
574
- this._loadedValueText = valueText;
766
+ this._valueText = valueText;
575
767
  if (valueText === this._definition.fixedWidthNullValueText) {
576
768
  this.loadNullValue();
577
769
  } else {
578
770
  try {
579
- this.loadNonNullValue(valueText);
771
+ this.loadValueFromText(valueText);
580
772
  } catch (e) {
581
773
  throw new Error(`Error loading fixed-width value for field ${this.name}: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
582
774
  }
@@ -587,7 +779,7 @@ export abstract class FtField {
587
779
  loadNullValue(): void {
588
780
  if (!this.constant) {
589
781
  this._valueIsNull = true;
590
- this.valueAssigned = true;
782
+ this._valueAssigned = true;
591
783
  this.checkNullSequenceRedirect();
592
784
  } else {
593
785
  if (!this._valueIsNull) {
@@ -596,22 +788,6 @@ export abstract class FtField {
596
788
  }
597
789
  }
598
790
 
599
- /**
600
- * Get the field value as text (for writing). Public accessor for protected method.
601
- * @internal
602
- */
603
- getValueText(): string {
604
- return this.getAsNonNullValueText();
605
- }
606
-
607
- /**
608
- * Check if the field value has been assigned. Public accessor for protected property.
609
- * @internal
610
- */
611
- isValueAssigned(): boolean {
612
- return this.valueAssigned;
613
- }
614
-
615
791
  // get dataTypeName(): string {
616
792
  // return this._definition.dataTypeName;
617
793
  // }
@@ -628,60 +804,62 @@ export abstract class FtField {
628
804
  this._valueIsNull = false;
629
805
  }
630
806
 
631
- private setAsNonNullValueText(newValue: string): void {
632
- try {
633
- this.loadNonNullValue(newValue);
634
- } catch (error) {
635
- const message = error instanceof Error ? error.message : String(error);
636
- throw new Error(`Failed to set field "${this.name}" value: ${message}`, { cause: error });
637
- }
807
+ protected getAsString(): string {
808
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsString`);
638
809
  }
639
-
640
- private setAsValueText(newValue: string | null): void {
641
- if (newValue === null) {
642
- this.setNull();
643
- } else {
644
- this.setAsNonNullValueText(newValue);
645
- }
810
+ protected getAsBoolean(): boolean {
811
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsBoolean`);
646
812
  }
647
-
648
- private setAsObject(newValue: unknown): void {
649
- if (newValue === null || newValue === undefined) {
650
- this.setNull();
651
- } else {
652
- this.setAsNonNullObject(newValue);
653
- }
813
+ protected getAsInteger(): number {
814
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsInteger`);
815
+ }
816
+ protected getAsBigInt(): bigint {
817
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsBigInt`);
818
+ }
819
+ protected getAsFloat(): number {
820
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsFloat`);
821
+ }
822
+ protected getAsDateTime(): Date {
823
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsDateTime`);
824
+ }
825
+ protected getAsDecimal(): number {
826
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsDecimal`);
654
827
  }
655
828
 
656
- private setAsString(newValue: string | null): void {
657
- if (newValue === null) {
658
- this.setNull();
659
- } else {
660
- this.setAsNonNullString(newValue);
661
- }
829
+ protected setAsString(_newValue: string): void {
830
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsString`);
831
+ }
832
+ protected setAsBoolean(_newValue: boolean): void {
833
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsBoolean`);
834
+ }
835
+ protected setAsInteger(_newValue: number): void {
836
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsInteger`);
837
+ }
838
+ protected setAsBigInt(_newValue: bigint): void {
839
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsBigInt`);
840
+ }
841
+ protected setAsFloat(_newValue: number): void {
842
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsFloat`);
843
+ }
844
+ protected setAsDateTime(_newValue: Date): void {
845
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsDateTime`);
846
+ }
847
+ protected setAsDecimal(_newValue: number): void {
848
+ throw new FtFieldTypeError(`Field ${this.name} does not support AsDecimal`);
662
849
  }
663
850
 
664
851
  // Abstract methods for subclasses
665
- protected abstract getAsNonNullValueText(): string;
666
- protected abstract loadNonNullValue(valueText: string): void;
667
-
668
- protected abstract getAsNonNullObject(): unknown;
669
- protected abstract getAsNonNullString(): string;
670
- protected abstract getAsBoolean(): boolean;
671
- protected abstract getAsInteger(): number;
672
- protected abstract getAsBigInt(): bigint;
673
- protected abstract getAsFloat(): number;
674
- protected abstract getAsDateTime(): Date;
675
- protected abstract getAsDecimal(): number;
676
-
677
- protected abstract setAsNonNullObject(newValue: unknown): void;
678
- protected abstract setAsNonNullString(newValue: string): void;
679
- protected abstract setAsBoolean(newValue: boolean): void;
680
- protected abstract setAsInteger(newValue: number): void;
681
- protected abstract setAsBigInt(newValue: bigint): void;
682
- protected abstract setAsFloat(newValue: number): void;
683
- protected abstract setAsDateTime(newValue: Date): void;
684
- protected abstract setAsDecimal(newValue: number): void;
852
+ /**
853
+ * Formats the field value to a string.
854
+ *
855
+ * The formatting includes data type formatting but not text formatting such as quoting, padding, truncating etc. This formatted string typically
856
+ * is identical to the string value in the field's {@link valueText} property (unless the field value has been modified after loading).
857
+ */
858
+ abstract formatValue(): string;
859
+ protected abstract loadValueFromText(valueText: string): void;
860
+
861
+ protected abstract getValue(): FtField.Value;
862
+ protected abstract setValue(newValue: FtField.Value): number;
685
863
 
686
864
  protected abstract getAsRedirectBoolean(): boolean;
687
865
  protected abstract getAsRedirectInteger(): bigint;
@@ -689,3 +867,9 @@ export abstract class FtField {
689
867
  protected abstract getAsRedirectDateTime(): Date;
690
868
  protected abstract getAsRedirectDecimal(): number;
691
869
  }
870
+
871
+ /** @public */
872
+ export namespace FtField {
873
+ export type Value = string | boolean | number | bigint | Date;
874
+ export type NullableValue = Value | null;
875
+ }