@servicenow/sdk-build-plugins 4.5.0 → 4.6.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 (175) hide show
  1. package/dist/acl-plugin.js +3 -1
  2. package/dist/acl-plugin.js.map +1 -1
  3. package/dist/atf/test-plugin.js +6 -8
  4. package/dist/atf/test-plugin.js.map +1 -1
  5. package/dist/basic-syntax-plugin.js +10 -3
  6. package/dist/basic-syntax-plugin.js.map +1 -1
  7. package/dist/column-plugin.js +99 -49
  8. package/dist/column-plugin.js.map +1 -1
  9. package/dist/flow/flow-logic/flow-logic-diagnostics.js +5 -5
  10. package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
  11. package/dist/flow/plugins/flow-action-definition-plugin.js +1229 -54
  12. package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
  13. package/dist/flow/plugins/flow-data-pill-plugin.js +5 -2
  14. package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
  15. package/dist/flow/plugins/flow-definition-plugin.js +16 -42
  16. package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
  17. package/dist/flow/plugins/flow-diagnostics-plugin.d.ts +2 -2
  18. package/dist/flow/plugins/flow-diagnostics-plugin.js +2 -2
  19. package/dist/flow/plugins/flow-instance-plugin.js +68 -22
  20. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  21. package/dist/flow/plugins/step-definition-plugin.js +2 -1
  22. package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
  23. package/dist/flow/plugins/step-instance-plugin.d.ts +9 -1
  24. package/dist/flow/plugins/step-instance-plugin.js +649 -136
  25. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  26. package/dist/flow/plugins/wfa-datapill-plugin.js +20 -5
  27. package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
  28. package/dist/flow/post-install.js +1 -0
  29. package/dist/flow/post-install.js.map +1 -1
  30. package/dist/flow/utils/complex-object-resolver.js +4 -1
  31. package/dist/flow/utils/complex-object-resolver.js.map +1 -1
  32. package/dist/flow/utils/complex-objects.js +1 -1
  33. package/dist/flow/utils/complex-objects.js.map +1 -1
  34. package/dist/flow/utils/flow-constants.d.ts +66 -2
  35. package/dist/flow/utils/flow-constants.js +402 -6
  36. package/dist/flow/utils/flow-constants.js.map +1 -1
  37. package/dist/flow/utils/flow-io-to-record.d.ts +1 -1
  38. package/dist/flow/utils/flow-io-to-record.js +37 -16
  39. package/dist/flow/utils/flow-io-to-record.js.map +1 -1
  40. package/dist/flow/utils/flow-shapes.js +4 -0
  41. package/dist/flow/utils/flow-shapes.js.map +1 -1
  42. package/dist/flow/utils/label-cache-parser.d.ts +9 -2
  43. package/dist/flow/utils/label-cache-parser.js +32 -4
  44. package/dist/flow/utils/label-cache-parser.js.map +1 -1
  45. package/dist/flow/utils/pill-shape-helpers.d.ts +15 -0
  46. package/dist/flow/utils/pill-shape-helpers.js +35 -0
  47. package/dist/flow/utils/pill-shape-helpers.js.map +1 -0
  48. package/dist/flow/utils/pill-string-parser.js +1 -0
  49. package/dist/flow/utils/pill-string-parser.js.map +1 -1
  50. package/dist/flow/utils/schema-to-flow-object.d.ts +6 -1
  51. package/dist/flow/utils/schema-to-flow-object.js +131 -15
  52. package/dist/flow/utils/schema-to-flow-object.js.map +1 -1
  53. package/dist/flow/utils/utils.d.ts +1 -0
  54. package/dist/flow/utils/utils.js +6 -1
  55. package/dist/flow/utils/utils.js.map +1 -1
  56. package/dist/form-plugin.js +7 -9
  57. package/dist/form-plugin.js.map +1 -1
  58. package/dist/inbound-email-action-plugin.d.ts +10 -0
  59. package/dist/inbound-email-action-plugin.js +128 -0
  60. package/dist/inbound-email-action-plugin.js.map +1 -0
  61. package/dist/index.d.ts +4 -0
  62. package/dist/index.js +4 -0
  63. package/dist/index.js.map +1 -1
  64. package/dist/instance-scan-plugin.js +0 -5
  65. package/dist/instance-scan-plugin.js.map +1 -1
  66. package/dist/now-config-plugin.js +1 -0
  67. package/dist/now-config-plugin.js.map +1 -1
  68. package/dist/property-plugin.js +1 -1
  69. package/dist/property-plugin.js.map +1 -1
  70. package/dist/record-plugin.d.ts +7 -0
  71. package/dist/record-plugin.js +13 -4
  72. package/dist/record-plugin.js.map +1 -1
  73. package/dist/rest-api-plugin.js +8 -1
  74. package/dist/rest-api-plugin.js.map +1 -1
  75. package/dist/schedule-script/scheduled-script-plugin.js +8 -3
  76. package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
  77. package/dist/script-include-plugin.js +4 -0
  78. package/dist/script-include-plugin.js.map +1 -1
  79. package/dist/service-catalog/catalog-clientscript-plugin.js +2 -2
  80. package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -1
  81. package/dist/service-catalog/catalog-ui-policy-plugin.js +2 -2
  82. package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -1
  83. package/dist/service-catalog/service-catalog-base.d.ts +20 -20
  84. package/dist/service-catalog/service-catalog-base.js +24 -24
  85. package/dist/service-catalog/service-catalog-base.js.map +1 -1
  86. package/dist/service-catalog/utils.js +1 -1
  87. package/dist/service-catalog/utils.js.map +1 -1
  88. package/dist/service-portal/header-footer-plugin.d.ts +2 -0
  89. package/dist/service-portal/header-footer-plugin.js +50 -0
  90. package/dist/service-portal/header-footer-plugin.js.map +1 -0
  91. package/dist/service-portal/menu-plugin.js +3 -22
  92. package/dist/service-portal/menu-plugin.js.map +1 -1
  93. package/dist/service-portal/page-plugin.js +3 -24
  94. package/dist/service-portal/page-plugin.js.map +1 -1
  95. package/dist/service-portal/page-route-map-plugin.d.ts +2 -0
  96. package/dist/service-portal/page-route-map-plugin.js +114 -0
  97. package/dist/service-portal/page-route-map-plugin.js.map +1 -0
  98. package/dist/service-portal/portal-plugin.js +21 -8
  99. package/dist/service-portal/portal-plugin.js.map +1 -1
  100. package/dist/service-portal/utils.d.ts +40 -2
  101. package/dist/service-portal/utils.js +283 -2
  102. package/dist/service-portal/utils.js.map +1 -1
  103. package/dist/service-portal/widget-plugin.js +9 -218
  104. package/dist/service-portal/widget-plugin.js.map +1 -1
  105. package/dist/static-content-plugin.js +4 -0
  106. package/dist/static-content-plugin.js.map +1 -1
  107. package/dist/table-plugin.js +377 -67
  108. package/dist/table-plugin.js.map +1 -1
  109. package/dist/ui-action-plugin.js +1 -4
  110. package/dist/ui-action-plugin.js.map +1 -1
  111. package/dist/ui-page-plugin.js +68 -13
  112. package/dist/ui-page-plugin.js.map +1 -1
  113. package/dist/ui-policy-plugin.js +28 -96
  114. package/dist/ui-policy-plugin.js.map +1 -1
  115. package/dist/utils.d.ts +5 -1
  116. package/dist/utils.js +41 -0
  117. package/dist/utils.js.map +1 -1
  118. package/dist/view-plugin.js +8 -3
  119. package/dist/view-plugin.js.map +1 -1
  120. package/dist/workspace-plugin.js +39 -36
  121. package/dist/workspace-plugin.js.map +1 -1
  122. package/package.json +5 -4
  123. package/src/acl-plugin.ts +3 -1
  124. package/src/atf/test-plugin.ts +6 -9
  125. package/src/basic-syntax-plugin.ts +11 -3
  126. package/src/column-plugin.ts +137 -75
  127. package/src/flow/flow-logic/flow-logic-diagnostics.ts +5 -6
  128. package/src/flow/plugins/flow-action-definition-plugin.ts +1581 -61
  129. package/src/flow/plugins/flow-data-pill-plugin.ts +5 -2
  130. package/src/flow/plugins/flow-definition-plugin.ts +12 -47
  131. package/src/flow/plugins/flow-diagnostics-plugin.ts +2 -2
  132. package/src/flow/plugins/flow-instance-plugin.ts +98 -22
  133. package/src/flow/plugins/step-definition-plugin.ts +2 -1
  134. package/src/flow/plugins/step-instance-plugin.ts +772 -156
  135. package/src/flow/plugins/wfa-datapill-plugin.ts +25 -5
  136. package/src/flow/post-install.ts +1 -0
  137. package/src/flow/utils/complex-object-resolver.ts +4 -1
  138. package/src/flow/utils/complex-objects.ts +1 -1
  139. package/src/flow/utils/flow-constants.ts +421 -5
  140. package/src/flow/utils/flow-io-to-record.ts +43 -17
  141. package/src/flow/utils/flow-shapes.ts +4 -0
  142. package/src/flow/utils/label-cache-parser.ts +33 -4
  143. package/src/flow/utils/pill-shape-helpers.ts +42 -0
  144. package/src/flow/utils/pill-string-parser.ts +1 -0
  145. package/src/flow/utils/schema-to-flow-object.ts +183 -15
  146. package/src/flow/utils/utils.ts +12 -1
  147. package/src/form-plugin.ts +1 -3
  148. package/src/inbound-email-action-plugin.ts +145 -0
  149. package/src/index.ts +4 -0
  150. package/src/instance-scan-plugin.ts +0 -5
  151. package/src/now-config-plugin.ts +1 -0
  152. package/src/property-plugin.ts +4 -1
  153. package/src/record-plugin.ts +25 -7
  154. package/src/rest-api-plugin.ts +7 -1
  155. package/src/schedule-script/scheduled-script-plugin.ts +14 -3
  156. package/src/script-include-plugin.ts +8 -0
  157. package/src/service-catalog/catalog-clientscript-plugin.ts +2 -2
  158. package/src/service-catalog/catalog-ui-policy-plugin.ts +2 -2
  159. package/src/service-catalog/service-catalog-base.ts +24 -24
  160. package/src/service-catalog/utils.ts +1 -1
  161. package/src/service-portal/header-footer-plugin.ts +57 -0
  162. package/src/service-portal/menu-plugin.ts +1 -23
  163. package/src/service-portal/page-plugin.ts +3 -28
  164. package/src/service-portal/page-route-map-plugin.ts +124 -0
  165. package/src/service-portal/portal-plugin.ts +33 -10
  166. package/src/service-portal/utils.ts +404 -3
  167. package/src/service-portal/widget-plugin.ts +14 -290
  168. package/src/static-content-plugin.ts +3 -0
  169. package/src/table-plugin.ts +466 -99
  170. package/src/ui-action-plugin.ts +1 -8
  171. package/src/ui-page-plugin.ts +76 -13
  172. package/src/ui-policy-plugin.ts +32 -128
  173. package/src/utils.ts +52 -0
  174. package/src/view-plugin.ts +10 -4
  175. package/src/workspace-plugin.ts +43 -43
@@ -26,7 +26,7 @@ import { create } from 'xmlbuilder2'
26
26
  import type { XMLBuilder } from 'xmlbuilder2/lib/interfaces'
27
27
  import { addFieldsToColumn } from './column/column-helper'
28
28
  import { getLabelForDefaultLanguage } from './column/column-to-record'
29
- import { createSdkDocEntry, generateDeprecatedDiagnostics } from './utils'
29
+ import { createSdkDocEntry, generateDeprecatedDiagnostics, generateChoiceSetFile } from './utils'
30
30
  import isEqual from 'lodash/isEqual'
31
31
 
32
32
  type GlobalRecord<T extends string | number | symbol, U> = globalThis.Record<T, U>
@@ -80,6 +80,10 @@ const ColumnSchema = z
80
80
  '@_use_dynamic_default': BooleanFromString.optional(),
81
81
  '@_reference': z.string().optional(),
82
82
  '@_virtual': BooleanFromString.optional(),
83
+ '@_formula': z.string().optional(),
84
+ '@_virtual_type': z.string().optional(),
85
+ '@_use_reference_qualifier': z.string().optional(),
86
+ '@_dynamic_ref_qual': z.string().optional(),
83
87
  '@_calculation': z.string().optional(),
84
88
  '@_choice_field': z.string().optional(),
85
89
  '@_function_definition': z.string().optional(),
@@ -168,6 +172,10 @@ type ColumnDefinition = {
168
172
  useDynamicDefault: boolean | undefined
169
173
  reference: string | undefined
170
174
  isVirtual: boolean | undefined
175
+ formula: string | undefined
176
+ virtualType: 'script' | 'formula' | undefined
177
+ useReferenceQualifier: 'simple' | 'dynamic' | 'advanced' | undefined
178
+ dynamicRefQual: string | undefined
171
179
  calculation: string | undefined
172
180
  choiceField: string | undefined
173
181
  functionDefinition: string | undefined
@@ -263,6 +271,10 @@ type SysDictionaryProperties = {
263
271
  use_dynamic_default: boolean | undefined
264
272
  reference: string | undefined
265
273
  virtual: boolean | undefined
274
+ formula: string | undefined
275
+ virtual_type: 'script' | 'formula' | undefined
276
+ use_reference_qualifier: 'simple' | 'dynamic' | 'advanced' | undefined
277
+ dynamic_ref_qual: string | undefined
266
278
  default: string | undefined
267
279
  calculation: string | undefined
268
280
  choice_field: string | undefined
@@ -321,6 +333,20 @@ type SysDocumentationProperties = {
321
333
  help: string | undefined
322
334
  }
323
335
 
336
+ type SysDictionaryOverrideProperties = {
337
+ name: string
338
+ element: string
339
+ base_table: string | undefined
340
+ default: string | undefined
341
+ calculation: string | undefined
342
+ reference_qual: string | undefined
343
+ read_only_option: string | undefined
344
+ dependent: string | undefined
345
+ mandatory: boolean | undefined
346
+ display: boolean | undefined
347
+ attributes: string | undefined
348
+ }
349
+
324
350
  /**
325
351
  * The access levels are mapped to the following values on the platform
326
352
  *
@@ -390,7 +416,7 @@ export const TablePlugin = Plugin.create({
390
416
  return { success: false }
391
417
  }
392
418
 
393
- const recordDefs = tableDefToRecordProperties(tableDef, config.tableDefaultLanguage)
419
+ const recordDefs = tableDefToRecordProperties(tableDef, config.defaultLanguage)
394
420
  const records: Record[] = []
395
421
  for (const [key, table] of [
396
422
  ['sysDbObject', 'sys_db_object'],
@@ -398,13 +424,18 @@ export const TablePlugin = Plugin.create({
398
424
  ['sysChoice', 'sys_choice'],
399
425
  ['sysIndex', 'sys_index'],
400
426
  ['sysDocumentation', 'sys_documentation'],
427
+ ['sysDictionaryOverride', 'sys_dictionary_override'],
401
428
  ] as const) {
402
429
  for (const rec of [recordDefs[key]].flat()) {
403
430
  records.push(
404
431
  await factory.createRecord({
405
432
  source: file,
406
433
  table,
407
- properties: filterUndefinedProperties(rec),
434
+ properties: {
435
+ ...filterUndefinedProperties(rec),
436
+ // Decorate generated sys_db_object
437
+ ...(key === 'sysDbObject' || key === 'sysDictionary' ? { _bootstrap: true } : {}),
438
+ },
408
439
  })
409
440
  )
410
441
  }
@@ -449,6 +480,10 @@ export const TablePlugin = Plugin.create({
449
480
  },
450
481
  },
451
482
  },
483
+ sys_dictionary_override: {
484
+ via: { name: 'name' },
485
+ descendant: true,
486
+ },
452
487
  ua_table_licensing_config: {
453
488
  descendant: true,
454
489
  via: { name: 'name' },
@@ -474,9 +509,12 @@ export const TablePlugin = Plugin.create({
474
509
  const schema: { [key: string]: CallExpressionShape } = {}
475
510
  let displayColumn: string | undefined
476
511
  const columns = descendants.query('sys_dictionary')
512
+ const overrides = descendants.query('sys_dictionary_override')
477
513
  let collectionRecord: Record
478
514
  const choices = descendants.query('sys_choice')
479
515
  const documentation = descendants.query('sys_documentation')
516
+
517
+ // Process regular columns
480
518
  for (const column of columns) {
481
519
  if (column.get('internal_type').getValue() === 'collection') {
482
520
  // 'collection' sys_dictionary record only has table properties
@@ -489,13 +527,101 @@ export const TablePlugin = Plugin.create({
489
527
  documentation: documentation.filter(
490
528
  (d) => d.get('element').ifString()?.getValue() === columnName
491
529
  ),
492
- tableDefaultLanguage: config.tableDefaultLanguage,
530
+ tableDefaultLanguage: config.defaultLanguage,
493
531
  })
494
532
  if (column.get('display').ifDefined()?.toBoolean().getValue()) {
495
533
  displayColumn = columnName
496
534
  }
497
535
  }
498
536
 
537
+ // Process dictionary overrides as OverrideColumn
538
+ for (const override of overrides) {
539
+ const columnName = override.get('element').asString().getValue()
540
+ schema[columnName] = new CallExpressionShape({
541
+ source: override,
542
+ callee: 'OverrideColumn',
543
+ args: [
544
+ override.transform(({ $ }) => ({
545
+ baseTable: $.from('base_table'),
546
+ default: $.from('default_value_override', 'default_value').map((flag, value) => {
547
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
548
+ ? value.ifString()?.getValue()
549
+ : undefined
550
+ }),
551
+ calculation: $.from('calculation_override', 'calculation').map((flag, value) => {
552
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
553
+ ? value.ifString()?.getValue()
554
+ : undefined
555
+ }),
556
+ referenceQualifier: $.from('reference_qual_override', 'reference_qual').map(
557
+ (flag, value) => {
558
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
559
+ ? value.ifString()?.getValue()
560
+ : undefined
561
+ }
562
+ ),
563
+ readOnlyOption: $.from('read_only_option_override', 'read_only_option').map(
564
+ (flag, value) => {
565
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
566
+ ? value.ifString()?.getValue()
567
+ : undefined
568
+ }
569
+ ),
570
+ dependent: $.from('dependent_override', 'dependent').map((flag, value) => {
571
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
572
+ ? value.ifString()?.getValue()
573
+ : undefined
574
+ }),
575
+ mandatory: $.from('mandatory_override', 'mandatory').map((flag, value) => {
576
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
577
+ ? value.toBoolean()?.getValue()
578
+ : undefined
579
+ }),
580
+ attributes: $.from('attributes_override', 'attributes').map((flag, attrs) => {
581
+ if (!flag.ifDefined() || !flag.toBoolean()?.getValue() || !attrs.isString()) {
582
+ return undefined
583
+ }
584
+ const result: { [key: string]: string | number | boolean } = {}
585
+ attrs
586
+ .toString()
587
+ .getValue()
588
+ .split(',')
589
+ .forEach((attr) => {
590
+ if (attr === '') {
591
+ return
592
+ }
593
+ const [key, value] = attr.split('=').map((s) => s.trim())
594
+ if (!key || value === undefined) {
595
+ return
596
+ }
597
+ if (value === 'true') {
598
+ result[key] = true
599
+ } else if (value === 'false') {
600
+ result[key] = false
601
+ } else {
602
+ // Try to parse as number
603
+ const numValue = Number(value)
604
+ if (!isNaN(numValue) && value !== '') {
605
+ result[key] = numValue
606
+ } else {
607
+ result[key] = value
608
+ }
609
+ }
610
+ })
611
+ return result
612
+ }),
613
+ display: $.from('display_override').map((v) => {
614
+ if (!v.ifDefined()) {
615
+ return undefined
616
+ }
617
+ const boolValue = v.toBoolean().getValue()
618
+ return boolValue === true ? true : undefined
619
+ }),
620
+ })),
621
+ ],
622
+ })
623
+ }
624
+
499
625
  const tableDocumentation = documentation.filter((d) => !d.get('element').getValue())
500
626
  const licensing = descendants.query('ua_table_licensing_config')
501
627
  const autoNumber = descendants.query('sys_number')
@@ -505,6 +631,43 @@ export const TablePlugin = Plugin.create({
505
631
  // Avoid replacing call expressions with variable statements
506
632
  const writeAsCallExpression =
507
633
  ts.Node.isNode(originalSource) && originalSource.isKind(ts.SyntaxKind.CallExpression)
634
+
635
+ const tableName = record.get('name').asString().getValue()
636
+ const isBootstrapDbObject = record.get('_bootstrap').ifBoolean()?.getValue() === true
637
+ const nonBootstrapColumns = columns.filter(
638
+ (col) => col.get('_bootstrap').ifBoolean()?.getValue() !== true
639
+ )
640
+
641
+ // Write as augmentation if we have sys_db_object from bootstrap and columns from elsewhere
642
+ const isAugmentation = isBootstrapDbObject && nonBootstrapColumns.length > 0
643
+ if (isAugmentation) {
644
+ const augmentsExpression = new CallExpressionShape({
645
+ source: record,
646
+ callee: 'Table',
647
+ exportName: tableName,
648
+ args: [
649
+ record.transform(({ $ }) => ({
650
+ augments: $.val(tableName),
651
+ schema: $.val(schema),
652
+ })),
653
+ ],
654
+ })
655
+ return {
656
+ success: true,
657
+ value: writeAsCallExpression
658
+ ? augmentsExpression
659
+ : new VariableStatementShape({
660
+ source: record,
661
+ isExported: true,
662
+ variableName: new IdentifierShape({
663
+ source: record,
664
+ name: tableName,
665
+ }),
666
+ initializer: augmentsExpression,
667
+ }),
668
+ }
669
+ }
670
+
508
671
  const callExpression = new CallExpressionShape({
509
672
  source: record,
510
673
  callee: 'Table',
@@ -627,16 +790,16 @@ export const TablePlugin = Plugin.create({
627
790
  ).def([]),
628
791
  label: $.map(
629
792
  (label) =>
630
- label.ifString() ??
793
+ label.ifString()?.ifNotEmpty() ??
631
794
  (tableDocumentation.length &&
632
- !isDefaultDocumentation('', tableDocumentation, config.tableDefaultLanguage)
795
+ !isDefaultDocumentation('', tableDocumentation, config.defaultLanguage)
633
796
  ? tableDocumentation.map((doc) =>
634
797
  doc
635
798
  .transform(({ $ }) => ({
636
799
  label: $.def(''),
637
800
  help: $.def(''),
638
801
  hint: $.def(''),
639
- language: $.def(config.tableDefaultLanguage),
802
+ language: $.def(config.defaultLanguage),
640
803
  plural: $.def(''),
641
804
  url: $.def(''),
642
805
  urlTarget: $.from('url_target').def(''),
@@ -706,20 +869,87 @@ export const TablePlugin = Plugin.create({
706
869
  }
707
870
  },
708
871
  async toFile(record, { descendants, config, transform }) {
709
- if (config.tableOutputFormat !== 'bootstrap' || config.type === 'configuration' || record.isDeleted()) {
710
- // Defer to record plugin
872
+ if (record.isDeleted()) {
711
873
  return { success: false }
712
874
  }
713
875
 
714
- const tableName = record.get('name').asString().getValue()
876
+ const augmentsValue = record.get('augments').ifString()?.getValue()
877
+ const isAugmentation = augmentsValue !== undefined
878
+ const tableName = augmentsValue ?? record.get('name').asString().getValue()
715
879
  const columns = descendants.query('sys_dictionary')
716
880
  const choices = descendants.query('sys_choice')
717
881
  const indexes = descendants.query('sys_index')
718
882
  const documentation = descendants.query('sys_documentation')
719
883
  const licensing = descendants.query('ua_table_licensing_config')
720
884
  const autoNumber = descendants.query('sys_number')
721
- let collectionRecord: Record | undefined
885
+ const overrides = descendants.query('sys_dictionary_override')
886
+
887
+ const [
888
+ documentationFiles,
889
+ licensingFiles,
890
+ autoNumberFiles,
891
+ overrideFiles,
892
+ sysDictionaryFiles,
893
+ sysDbObjectFiles,
894
+ ] = await Promise.all([
895
+ generateRecordXml(
896
+ documentation.filter(
897
+ (doc) =>
898
+ !isDefaultDocumentation(
899
+ doc.get('element')?.toString().getValue(),
900
+ [doc],
901
+ config.defaultLanguage
902
+ )
903
+ ),
904
+ config,
905
+ transform
906
+ ),
907
+ generateRecordXml(
908
+ licensing.filter((l) => !isDefaultLicenseConfig(tableName, l)),
909
+ config,
910
+ transform
911
+ ),
912
+ generateRecordXml(autoNumber, config, transform),
913
+ generateRecordXml(overrides, config, transform),
914
+ generateRecordXml(columns, config, transform, ['_bootstrap']),
915
+ isAugmentation
916
+ ? Promise.resolve([])
917
+ : generateRecordXml([record], config, transform, ['augments', '_bootstrap']),
918
+ ])
919
+
920
+ if (config.type === 'configuration') {
921
+ // No bootstrap XML for configuration projects, just write independent record XML
922
+ const choiceSets = descendants.query('sys_choice_set')
923
+ const [choiceSetFiles, indexFiles] = await Promise.all([
924
+ Promise.all(
925
+ choiceSets.map((cs) => {
926
+ const csChoices = choices.filter(
927
+ (c) =>
928
+ c.get('name').toString().getValue() === cs.get('name').toString().getValue() &&
929
+ c.get('element').toString().getValue() ===
930
+ cs.get('element').toString().getValue()
931
+ )
932
+ return generateChoiceSetFile(cs, csChoices, config, transform)
933
+ })
934
+ ),
935
+ generateRecordXml(indexes, config, transform),
936
+ ])
937
+ return {
938
+ success: true,
939
+ value: [
940
+ ...sysDictionaryFiles,
941
+ ...choiceSetFiles,
942
+ ...indexFiles,
943
+ ...sysDbObjectFiles,
944
+ ...documentationFiles,
945
+ ...licensingFiles,
946
+ ...autoNumberFiles,
947
+ ...overrideFiles,
948
+ ],
949
+ }
950
+ }
722
951
  let displayColumn: string | undefined
952
+ let collectionRecord: Record | undefined
723
953
  const elements: XMLBuilder[] = []
724
954
  for (const column of columns) {
725
955
  const displayValue = column.get('display').ifBoolean()?.getValue()
@@ -791,6 +1021,23 @@ export const TablePlugin = Plugin.create({
791
1021
  ['hint', column.get('hint').ifString()?.getValue()],
792
1022
  ['help', column.get('help').ifString()?.getValue()],
793
1023
  ['virtual', column.get('virtual').ifBoolean()?.getValue().toString()],
1024
+ ['formula', column.get('formula').ifString()?.getValue() || undefined],
1025
+ [
1026
+ 'virtual_type',
1027
+ column.get('virtual_type').ifString()?.getValue() === 'script'
1028
+ ? undefined
1029
+ : column.get('virtual_type').ifString()?.getValue(),
1030
+ ],
1031
+ [
1032
+ 'use_reference_qualifier',
1033
+ column.get('use_reference_qualifier').ifString()?.getValue() === 'simple'
1034
+ ? undefined
1035
+ : column.get('use_reference_qualifier').ifString()?.getValue(),
1036
+ ],
1037
+ [
1038
+ 'dynamic_ref_qual',
1039
+ column.get('dynamic_ref_qual').ifDefined()?.toString().getValue() || undefined,
1040
+ ],
794
1041
  ['widget', column.get('widget').ifString()?.getValue()],
795
1042
  ['reference_qual', column.get('reference_qual').ifString()?.getValue()],
796
1043
  ['reference_qual_condition', column.get('reference_qual_condition').ifString()?.getValue()],
@@ -883,27 +1130,6 @@ export const TablePlugin = Plugin.create({
883
1130
  ]
884
1131
  ).end({ prettyPrint: true })
885
1132
 
886
- const documentationFiles = await generateRecordXml(
887
- documentation.filter(
888
- (doc) =>
889
- !isDefaultDocumentation(
890
- doc.get('element')?.toString().getValue(),
891
- [doc],
892
- config.tableDefaultLanguage
893
- )
894
- ),
895
- config,
896
- transform
897
- )
898
-
899
- const licensingFiles = await generateRecordXml(
900
- licensing.filter((licensing) => !isDefaultLicenseConfig(tableName, licensing)),
901
- config,
902
- transform
903
- )
904
-
905
- const autoNumberFiles = await generateRecordXml(autoNumber, config, transform)
906
-
907
1133
  return {
908
1134
  success: true,
909
1135
  value: [
@@ -916,6 +1142,9 @@ export const TablePlugin = Plugin.create({
916
1142
  ...documentationFiles,
917
1143
  ...licensingFiles,
918
1144
  ...autoNumberFiles,
1145
+ ...overrideFiles,
1146
+ ...sysDictionaryFiles,
1147
+ ...sysDbObjectFiles,
919
1148
  ],
920
1149
  }
921
1150
  },
@@ -927,6 +1156,13 @@ export const TablePlugin = Plugin.create({
927
1156
  value: `sys_dictionary_${record.get('name').getValue()}_${record.get('element').getValue() || 'null'}`,
928
1157
  }),
929
1158
  },
1159
+ sys_dictionary_override: {
1160
+ coalesce: ['name', 'element'],
1161
+ getUpdateName: (record) => ({
1162
+ success: true,
1163
+ value: `sys_dictionary_override_${record.get('name').getValue()}_${record.get('element').getValue() || 'null'}`,
1164
+ }),
1165
+ },
930
1166
  sys_documentation: {
931
1167
  coalesce: ['name', 'element', 'language'],
932
1168
  getUpdateName: (record) => ({
@@ -960,7 +1196,9 @@ export const TablePlugin = Plugin.create({
960
1196
  const table = callExpression.getArgument(0).asObject().withAliasedKeys(tableAliases)
961
1197
  generateDeprecatedDiagnostics(table, diagnostics)
962
1198
  const relatedRecords: Record[] = []
963
- const tableName = table.get('name').asString()
1199
+ const augments = table.get('augments').ifString()
1200
+ const isAugmentation = augments !== undefined
1201
+ const tableName = isAugmentation ? augments! : table.get('name').asString()
964
1202
  if (!tableName.getValue().match(tableNameRegex)) {
965
1203
  diagnostics.error(
966
1204
  table.get('name'),
@@ -970,16 +1208,28 @@ export const TablePlugin = Plugin.create({
970
1208
 
971
1209
  let ignoreColumnNameCheck = false
972
1210
  const scopeName = config.scope
973
- const scopeRegex = new RegExp(`^${scopeName}_`)
974
- const globalRegex = /^u_/
975
- const tableNameMatch = tableName.getValue().match(scopeRegex)
976
- if (!tableNameMatch && !isSNScope(scopeName) && scopeName !== 'global') {
1211
+ const scopePrefix = scopeName === 'global' ? 'u_' : `${scopeName}_`
1212
+ const prefixRegex = new RegExp(`^${scopePrefix}`)
1213
+ const tableNameMatch = tableName.getValue().match(prefixRegex)
1214
+ if (isAugmentation) {
1215
+ if (tableNameMatch && scopeName !== 'global') {
1216
+ const nameNode = tableName.getOriginalNode()
1217
+ if (!nameNode?.getParentIfKind(ts.SyntaxKind.AsExpression)) {
1218
+ diagnostics.error(
1219
+ table.get('augments'),
1220
+ `'augments' flag set on in-scope table '${tableName.getValue()}'`
1221
+ )
1222
+ } else {
1223
+ ignoreColumnNameCheck = true
1224
+ }
1225
+ }
1226
+ } else if (!tableNameMatch && !isSNScope(scopeName) && scopeName !== 'global') {
977
1227
  const nameNode = tableName.getOriginalNode()
978
1228
  if (nameNode && !nameNode.getParentIfKind(ts.SyntaxKind.AsExpression)) {
979
1229
  // 'sn' and 'now' scoped apps ignore this validation
980
1230
  diagnostics.error(
981
1231
  table.get('name'),
982
- `'name' property should start with scope prefix '${scopeName}_'`
1232
+ `'name' property should start with scope prefix '${scopePrefix}'`
983
1233
  )
984
1234
  } else {
985
1235
  ignoreColumnNameCheck = true
@@ -998,13 +1248,11 @@ export const TablePlugin = Plugin.create({
998
1248
  )
999
1249
  }
1000
1250
 
1001
- const globalTableNameMatch = tableName.getValue().match(globalRegex)
1002
1251
  let anyNonPrefixedGlobalColumn = false
1003
-
1004
- if (scopeName === 'global' && !globalTableNameMatch) {
1252
+ if (!isAugmentation && scopeName === 'global' && !tableNameMatch) {
1005
1253
  const schema = table.get('schema').asObject()
1006
1254
  for (const [name, _] of schema.entries()) {
1007
- if (!name.match(globalRegex)) {
1255
+ if (!name.match(prefixRegex)) {
1008
1256
  anyNonPrefixedGlobalColumn = true
1009
1257
  break
1010
1258
  }
@@ -1016,12 +1264,12 @@ export const TablePlugin = Plugin.create({
1016
1264
  `Global table 'name' property should start with custom prefix 'u_'`
1017
1265
  )
1018
1266
  }
1019
- } else if (scopeName === 'global') {
1267
+ } else if (scopeName === 'global' && !isAugmentation) {
1020
1268
  // Global table starts with custom prefix `u_`, allow any column name prefix
1021
1269
  ignoreColumnNameCheck = true
1022
1270
  }
1023
1271
 
1024
- // sys_dictionary
1272
+ // sys_dictionary and sys_dictionary_override
1025
1273
  const schema = table.get('schema').asObject()
1026
1274
  const columnIdsMap = new Map<string, string>()
1027
1275
  for (const [name, column] of schema.entries()) {
@@ -1031,37 +1279,124 @@ export const TablePlugin = Plugin.create({
1031
1279
  'Column name must only contain lowercase letters, numbers, and underscores'
1032
1280
  )
1033
1281
  }
1282
+
1283
+ // Check if this is an OverrideColumn
1034
1284
  if (
1035
- !ignoreColumnNameCheck &&
1036
- !tableNameMatch &&
1037
- !isSNScope(scopeName) &&
1038
- scopeName !== 'global' &&
1039
- !name.match(scopeRegex)
1285
+ column.is(CallExpressionShape) &&
1286
+ column.as(CallExpressionShape).getCallee() === 'OverrideColumn'
1040
1287
  ) {
1041
- // 'sn' and 'now' scoped apps ignore this validation
1042
- diagnostics.error(
1043
- column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ?? column,
1044
- `Column name should be prefixed with scope '${scopeName}_' if table name does not contain prefix`
1045
- )
1046
- } else if (scopeName === 'global' && !globalTableNameMatch && !name.match(globalRegex)) {
1047
- diagnostics.error(
1048
- column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ?? column,
1049
- `Column name should be prefixed with 'u_' custom prefix if table name does not contain this prefix, such as when adding columns to an existing global table`
1050
- )
1051
- }
1052
- const display = table.get('display').ifString()?.getValue() === name
1053
- const result = await transform.toRecord(
1054
- addFieldsToColumn(
1055
- { name, table: tableName.getValue(), display },
1056
- column.as(CallExpressionShape)
1288
+ // This is an OverrideColumn() call - get the object from its first argument
1289
+ const columnObj = column.as(CallExpressionShape).getArgument(0).asObject()
1290
+
1291
+ // Handle OverrideColumn - create sys_dictionary_override record
1292
+ // Validate that the table extends another table
1293
+ const extendsTable = table.get('extends').ifString()?.getValue()
1294
+ if (!extendsTable) {
1295
+ diagnostics.error(
1296
+ column,
1297
+ `Cannot use OverrideColumn in table '${tableName.getValue()}' because it does not extend another table`
1298
+ )
1299
+ return { success: false }
1300
+ }
1301
+
1302
+ // Use baseTable if provided, otherwise default to extends
1303
+ const baseTableValue = columnObj.get('baseTable')
1304
+ let baseTable: string
1305
+
1306
+ if (baseTableValue.ifString()) {
1307
+ baseTable = baseTableValue.asString().getValue()
1308
+ } else {
1309
+ // Default to extends if baseTable not provided
1310
+ baseTable = extendsTable
1311
+ }
1312
+
1313
+ // Create sys_dictionary_override record
1314
+ const overrideRecord = await factory.createRecord({
1315
+ source: statement ?? callExpression,
1316
+ table: 'sys_dictionary_override',
1317
+ properties: columnObj.transform(({ $ }) => ({
1318
+ name: $.val(tableName),
1319
+ element: $.val(name),
1320
+ base_table: $.val(baseTable),
1321
+ default_value: $.from('default'),
1322
+ default_value_override: $.from('default').map((v) => !!v.ifDefined()),
1323
+ calculation: $.from('calculation'),
1324
+ calculation_override: $.from('calculation').map((v) => !!v.ifDefined()),
1325
+ reference_qual: $.from('referenceQualifier'),
1326
+ reference_qual_override: $.from('referenceQualifier').map((v) => !!v.ifDefined()),
1327
+ read_only_option: $.from('readOnlyOption'),
1328
+ read_only_option_override: $.from('readOnlyOption').map((v) => !!v.ifDefined()),
1329
+ read_only: $.from('readOnlyOption').map((readOnlyOption) => {
1330
+ // read_only should be true if readOnlyOption has a value
1331
+ return !!readOnlyOption.ifDefined()
1332
+ }),
1333
+ read_only_override: $.from('readOnlyOption').map((v) => !!v.ifDefined()),
1334
+ dependent: $.from('dependent'),
1335
+ dependent_override: $.from('dependent').map((v) => !!v.ifDefined()),
1336
+ mandatory: $.from('mandatory').toBoolean().def(false),
1337
+ mandatory_override: $.from('mandatory').map((v) => !!v.ifDefined()),
1338
+ attributes: $.from('attributes').map((attrs) => {
1339
+ if (!attrs.isObject()) {
1340
+ return
1341
+ }
1342
+ const attrsObj = attrs.asObject().getValue()
1343
+ return Object.entries(attrsObj)
1344
+ .map(([key, value]) => `${key}=${value}`)
1345
+ .join(',')
1346
+ }),
1347
+ attributes_override: $.from('attributes').map((v) => !!v.ifDefined()),
1348
+ display_override: $.from('display').map((v) => {
1349
+ const boolShape = v.ifBoolean()
1350
+ if (!boolShape) {
1351
+ return false
1352
+ }
1353
+ return boolShape.getValue() === true
1354
+ }),
1355
+ })),
1356
+ })
1357
+ relatedRecords.push(overrideRecord)
1358
+ } else {
1359
+ // Handle regular column - create sys_dictionary record
1360
+ if (isAugmentation && !tableNameMatch) {
1361
+ if (!isSNScope(scopeName) && !name.match(prefixRegex)) {
1362
+ diagnostics.error(
1363
+ column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ??
1364
+ column,
1365
+ `Column name '${name}' must be prefixed with '${scopePrefix}' when augmenting a table`
1366
+ )
1367
+ }
1368
+ } else if (
1369
+ !ignoreColumnNameCheck &&
1370
+ !tableNameMatch &&
1371
+ !isSNScope(scopeName) &&
1372
+ scopeName !== 'global' &&
1373
+ !name.match(prefixRegex)
1374
+ ) {
1375
+ // 'sn' and 'now' scoped apps ignore this validation
1376
+ diagnostics.error(
1377
+ column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ?? column,
1378
+ `Column name should be prefixed with scope '${scopePrefix}' if table name does not contain prefix`
1379
+ )
1380
+ } else if (scopeName === 'global' && !tableNameMatch && !name.match(prefixRegex)) {
1381
+ diagnostics.error(
1382
+ column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ?? column,
1383
+ `Column name should be prefixed with '${scopePrefix}' custom prefix if table name does not contain this prefix, such as when adding columns to an existing global table`
1384
+ )
1385
+ }
1386
+ const display = table.get('display').ifString()?.getValue() === name
1387
+ const result = await transform.toRecord(
1388
+ addFieldsToColumn(
1389
+ { name, table: tableName.getValue(), display },
1390
+ column.as(CallExpressionShape)
1391
+ )
1057
1392
  )
1058
- )
1059
- if (!result.success) {
1060
- diagnostics.error(column, 'Invalid column in table schema')
1061
- return { success: false }
1393
+ if (!result.success) {
1394
+ diagnostics.error(column, 'Invalid column in table schema')
1395
+ return { success: false }
1396
+ }
1397
+ relatedRecords.push(result.value)
1398
+ columnIdsMap.set(name, result.value.getId().getValue())
1062
1399
  }
1063
- relatedRecords.push(result.value)
1064
- columnIdsMap.set(name, result.value.getId().getValue())
1065
1400
  }
1066
1401
 
1067
1402
  // sys_index
@@ -1193,7 +1528,7 @@ export const TablePlugin = Plugin.create({
1193
1528
  properties: {
1194
1529
  name: tableName,
1195
1530
  element: undefined,
1196
- language: config.tableDefaultLanguage,
1531
+ language: config.defaultLanguage,
1197
1532
  label: table.get('label').ifString() ?? tableName,
1198
1533
  plural: generatePlural(
1199
1534
  table.get('label')?.ifString()?.getValue() ?? tableName.getValue()
@@ -1222,29 +1557,32 @@ export const TablePlugin = Plugin.create({
1222
1557
  }
1223
1558
 
1224
1559
  // sys_dictionary (collection)
1225
- relatedRecords.push(
1226
- await factory.createRecord({
1227
- source: statement ?? callExpression,
1228
- table: 'sys_dictionary',
1229
- properties: table.transform(({ $ }) => ({
1230
- name: $,
1231
- element: $.val(undefined),
1232
- internal_type: $.def('collection'),
1233
- attributes: $.map((attributes) => {
1234
- if (!attributes.isObject()) {
1235
- return undefined
1236
- }
1237
- const attributesObj = attributes.asObject().getValue()
1238
- return Object.entries(attributesObj)
1239
- .map(([key, value]) => `${key}=${value}`)
1240
- .join(',')
1241
- }).def(''),
1242
- audit: $.def(false),
1243
- read_only: $.from('readOnly').def(false),
1244
- text_index: $.from('textIndex').def(false),
1245
- })),
1246
- })
1247
- )
1560
+ if (!isAugmentation) {
1561
+ relatedRecords.push(
1562
+ await factory.createRecord({
1563
+ source: statement ?? callExpression,
1564
+ table: 'sys_dictionary',
1565
+ properties: table.transform(({ $ }) => ({
1566
+ name: $,
1567
+ element: $.val(undefined),
1568
+ internal_type: $.def('collection'),
1569
+ active: $.val(true),
1570
+ attributes: $.map((attributes) => {
1571
+ if (!attributes.isObject()) {
1572
+ return undefined
1573
+ }
1574
+ const attributesObj = attributes.asObject().getValue()
1575
+ return Object.entries(attributesObj)
1576
+ .map(([key, value]) => `${key}=${value}`)
1577
+ .join(',')
1578
+ }).def(''),
1579
+ audit: $.def(false),
1580
+ read_only: $.from('readOnly').def(false),
1581
+ text_index: $.from('textIndex').def(false),
1582
+ })),
1583
+ })
1584
+ )
1585
+ }
1248
1586
 
1249
1587
  const hasAction = (actionName: string, actions: Shape): boolean | undefined => {
1250
1588
  return actions
@@ -1292,10 +1630,14 @@ export const TablePlugin = Plugin.create({
1292
1630
  .map((actions) => hasAction('create', actions))
1293
1631
  .def(false),
1294
1632
  is_extendable: $.from('extensible').toBoolean().def(false),
1295
- label: $.map((label) => getLabelForDefaultLanguage(label, config.tableDefaultLanguage)),
1633
+ label: $.map((label) => getLabelForDefaultLanguage(label, config.defaultLanguage)),
1296
1634
  live_feed_enabled: $.from('liveFeed').toBoolean().def(false),
1297
- name: $,
1635
+ name: $.from('name', 'augments').map(
1636
+ (nameVal, augmentsVal) => augmentsVal.ifString() ?? nameVal
1637
+ ),
1298
1638
  scriptable_table: $.from('scriptableTable').toBoolean().def(false),
1639
+ // Controls toFile output, not written to record XML
1640
+ augments: $.from('augments'),
1299
1641
  })),
1300
1642
  })
1301
1643
 
@@ -1382,6 +1724,18 @@ function parseTableBootstrapXml(xml: unknown): TableDefinition | null {
1382
1724
  useDynamicDefault: column['@_use_dynamic_default'],
1383
1725
  reference: column['@_reference'],
1384
1726
  isVirtual: column['@_virtual'],
1727
+ formula: column['@_formula'],
1728
+ virtualType:
1729
+ column['@_virtual_type'] === 'script' || column['@_virtual_type'] === 'formula'
1730
+ ? column['@_virtual_type']
1731
+ : undefined,
1732
+ useReferenceQualifier:
1733
+ column['@_use_reference_qualifier'] === 'simple' ||
1734
+ column['@_use_reference_qualifier'] === 'dynamic' ||
1735
+ column['@_use_reference_qualifier'] === 'advanced'
1736
+ ? column['@_use_reference_qualifier']
1737
+ : undefined,
1738
+ dynamicRefQual: column['@_dynamic_ref_qual'],
1385
1739
  calculation: column['@_calculation'],
1386
1740
  choiceField: column['@_choice_field'],
1387
1741
  functionDefinition: column['@_function_definition'],
@@ -1469,6 +1823,7 @@ function tableDefToRecordProperties(
1469
1823
  sysChoice: SysChoiceProperties[]
1470
1824
  sysIndex: SysIndexProperties[]
1471
1825
  sysDocumentation: SysDocumentationProperties[]
1826
+ sysDictionaryOverride: SysDictionaryOverrideProperties[]
1472
1827
  } {
1473
1828
  const sysDbObject: SysDbObjectProperties = {
1474
1829
  name: tableDef.name,
@@ -1490,6 +1845,7 @@ function tableDefToRecordProperties(
1490
1845
  const sysDictionary: SysDictionaryProperties[] = []
1491
1846
  const sysChoice: SysChoiceProperties[] = []
1492
1847
  const sysDocumentation: SysDocumentationProperties[] = []
1848
+ const sysDictionaryOverride: SysDictionaryOverrideProperties[] = []
1493
1849
 
1494
1850
  // table documentation
1495
1851
  if (tableDef.label && tableDef.plural) {
@@ -1523,6 +1879,10 @@ function tableDefToRecordProperties(
1523
1879
  use_dynamic_default: column.useDynamicDefault,
1524
1880
  reference: column.reference,
1525
1881
  virtual: column.isVirtual,
1882
+ formula: column.formula,
1883
+ virtual_type: column.virtualType,
1884
+ use_reference_qualifier: column.useReferenceQualifier,
1885
+ dynamic_ref_qual: column.dynamicRefQual,
1526
1886
  default: column.defaultValue,
1527
1887
  calculation: column.calculation,
1528
1888
  choice_field: column.choiceField,
@@ -1605,6 +1965,10 @@ function tableDefToRecordProperties(
1605
1965
  use_dynamic_default: undefined,
1606
1966
  reference: undefined,
1607
1967
  virtual: undefined,
1968
+ formula: undefined,
1969
+ virtual_type: undefined,
1970
+ use_reference_qualifier: undefined,
1971
+ dynamic_ref_qual: undefined,
1608
1972
  default: undefined,
1609
1973
  calculation: undefined,
1610
1974
  choice_field: undefined,
@@ -1647,6 +2011,7 @@ function tableDefToRecordProperties(
1647
2011
  sysChoice,
1648
2012
  sysIndex,
1649
2013
  sysDocumentation,
2014
+ sysDictionaryOverride,
1650
2015
  }
1651
2016
  }
1652
2017
 
@@ -1662,7 +2027,7 @@ function addTableToGlobalGeneratedFile(tableArg: ObjectShape, sourceFilePath: st
1662
2027
  >,
1663
2028
  }
1664
2029
  const generatedTableFile: ts.SourceFile | undefined = compiler.getGeneratedTableFile()
1665
- const tableName = tableArg.get('name').asString().getValue()
2030
+ const tableName = tableArg.get('augments').ifString()?.getValue() ?? tableArg.get('name').asString().getValue()
1666
2031
  if (!(tableName.trim().length > 0 && tableName.match(tableNameRegex))) {
1667
2032
  return
1668
2033
  }
@@ -1743,7 +2108,8 @@ function createElement(name: string, attributes: [string, string][] = [], childr
1743
2108
  async function generateRecordXml(
1744
2109
  records: Record[],
1745
2110
  config: { scope: string; scopeId: string },
1746
- transform: Transform
2111
+ transform: Transform,
2112
+ excludeFields: string[] = []
1747
2113
  ): Promise<OutputFile[]> {
1748
2114
  const files: OutputFile[] = []
1749
2115
  for (const record of records) {
@@ -1754,6 +2120,7 @@ async function generateRecordXml(
1754
2120
  record
1755
2121
  .entries()
1756
2122
  .sort(([a], [b]) => a.localeCompare(b))
2123
+ .filter(([prop]) => excludeFields.length === 0 || !excludeFields.includes(prop))
1757
2124
  .forEach(([prop, shape]) => builder.field(prop, shape))
1758
2125
 
1759
2126
  files.push({