@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
@@ -59,6 +59,10 @@ const ColumnSchema = zod_1.z
59
59
  '@_use_dynamic_default': BooleanFromString.optional(),
60
60
  '@_reference': zod_1.z.string().optional(),
61
61
  '@_virtual': BooleanFromString.optional(),
62
+ '@_formula': zod_1.z.string().optional(),
63
+ '@_virtual_type': zod_1.z.string().optional(),
64
+ '@_use_reference_qualifier': zod_1.z.string().optional(),
65
+ '@_dynamic_ref_qual': zod_1.z.string().optional(),
62
66
  '@_calculation': zod_1.z.string().optional(),
63
67
  '@_choice_field': zod_1.z.string().optional(),
64
68
  '@_function_definition': zod_1.z.string().optional(),
@@ -174,7 +178,7 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
174
178
  if (!tableDef) {
175
179
  return { success: false };
176
180
  }
177
- const recordDefs = tableDefToRecordProperties(tableDef, config.tableDefaultLanguage);
181
+ const recordDefs = tableDefToRecordProperties(tableDef, config.defaultLanguage);
178
182
  const records = [];
179
183
  for (const [key, table] of [
180
184
  ['sysDbObject', 'sys_db_object'],
@@ -182,12 +186,17 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
182
186
  ['sysChoice', 'sys_choice'],
183
187
  ['sysIndex', 'sys_index'],
184
188
  ['sysDocumentation', 'sys_documentation'],
189
+ ['sysDictionaryOverride', 'sys_dictionary_override'],
185
190
  ]) {
186
191
  for (const rec of [recordDefs[key]].flat()) {
187
192
  records.push(await factory.createRecord({
188
193
  source: file,
189
194
  table,
190
- properties: filterUndefinedProperties(rec),
195
+ properties: {
196
+ ...filterUndefinedProperties(rec),
197
+ // Decorate generated sys_db_object
198
+ ...(key === 'sysDbObject' || key === 'sysDictionary' ? { _bootstrap: true } : {}),
199
+ },
191
200
  }));
192
201
  }
193
202
  }
@@ -230,6 +239,10 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
230
239
  },
231
240
  },
232
241
  },
242
+ sys_dictionary_override: {
243
+ via: { name: 'name' },
244
+ descendant: true,
245
+ },
233
246
  ua_table_licensing_config: {
234
247
  descendant: true,
235
248
  via: { name: 'name' },
@@ -255,9 +268,11 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
255
268
  const schema = {};
256
269
  let displayColumn;
257
270
  const columns = descendants.query('sys_dictionary');
271
+ const overrides = descendants.query('sys_dictionary_override');
258
272
  let collectionRecord;
259
273
  const choices = descendants.query('sys_choice');
260
274
  const documentation = descendants.query('sys_documentation');
275
+ // Process regular columns
261
276
  for (const column of columns) {
262
277
  if (column.get('internal_type').getValue() === 'collection') {
263
278
  // 'collection' sys_dictionary record only has table properties
@@ -268,12 +283,98 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
268
283
  schema[columnName] = (0, column_plugin_1.columnToCallExpression)(column, {
269
284
  choices: choices.filter((choice) => choice.get('element').asString().getValue() === columnName),
270
285
  documentation: documentation.filter((d) => d.get('element').ifString()?.getValue() === columnName),
271
- tableDefaultLanguage: config.tableDefaultLanguage,
286
+ tableDefaultLanguage: config.defaultLanguage,
272
287
  });
273
288
  if (column.get('display').ifDefined()?.toBoolean().getValue()) {
274
289
  displayColumn = columnName;
275
290
  }
276
291
  }
292
+ // Process dictionary overrides as OverrideColumn
293
+ for (const override of overrides) {
294
+ const columnName = override.get('element').asString().getValue();
295
+ schema[columnName] = new sdk_build_core_1.CallExpressionShape({
296
+ source: override,
297
+ callee: 'OverrideColumn',
298
+ args: [
299
+ override.transform(({ $ }) => ({
300
+ baseTable: $.from('base_table'),
301
+ default: $.from('default_value_override', 'default_value').map((flag, value) => {
302
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
303
+ ? value.ifString()?.getValue()
304
+ : undefined;
305
+ }),
306
+ calculation: $.from('calculation_override', 'calculation').map((flag, value) => {
307
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
308
+ ? value.ifString()?.getValue()
309
+ : undefined;
310
+ }),
311
+ referenceQualifier: $.from('reference_qual_override', 'reference_qual').map((flag, value) => {
312
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
313
+ ? value.ifString()?.getValue()
314
+ : undefined;
315
+ }),
316
+ readOnlyOption: $.from('read_only_option_override', 'read_only_option').map((flag, value) => {
317
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
318
+ ? value.ifString()?.getValue()
319
+ : undefined;
320
+ }),
321
+ dependent: $.from('dependent_override', 'dependent').map((flag, value) => {
322
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
323
+ ? value.ifString()?.getValue()
324
+ : undefined;
325
+ }),
326
+ mandatory: $.from('mandatory_override', 'mandatory').map((flag, value) => {
327
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
328
+ ? value.toBoolean()?.getValue()
329
+ : undefined;
330
+ }),
331
+ attributes: $.from('attributes_override', 'attributes').map((flag, attrs) => {
332
+ if (!flag.ifDefined() || !flag.toBoolean()?.getValue() || !attrs.isString()) {
333
+ return undefined;
334
+ }
335
+ const result = {};
336
+ attrs
337
+ .toString()
338
+ .getValue()
339
+ .split(',')
340
+ .forEach((attr) => {
341
+ if (attr === '') {
342
+ return;
343
+ }
344
+ const [key, value] = attr.split('=').map((s) => s.trim());
345
+ if (!key || value === undefined) {
346
+ return;
347
+ }
348
+ if (value === 'true') {
349
+ result[key] = true;
350
+ }
351
+ else if (value === 'false') {
352
+ result[key] = false;
353
+ }
354
+ else {
355
+ // Try to parse as number
356
+ const numValue = Number(value);
357
+ if (!isNaN(numValue) && value !== '') {
358
+ result[key] = numValue;
359
+ }
360
+ else {
361
+ result[key] = value;
362
+ }
363
+ }
364
+ });
365
+ return result;
366
+ }),
367
+ display: $.from('display_override').map((v) => {
368
+ if (!v.ifDefined()) {
369
+ return undefined;
370
+ }
371
+ const boolValue = v.toBoolean().getValue();
372
+ return boolValue === true ? true : undefined;
373
+ }),
374
+ })),
375
+ ],
376
+ });
377
+ }
277
378
  const tableDocumentation = documentation.filter((d) => !d.get('element').getValue());
278
379
  const licensing = descendants.query('ua_table_licensing_config');
279
380
  const autoNumber = descendants.query('sys_number');
@@ -281,6 +382,38 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
281
382
  const originalSource = record.getOriginalSource();
282
383
  // Avoid replacing call expressions with variable statements
283
384
  const writeAsCallExpression = sdk_build_core_1.ts.Node.isNode(originalSource) && originalSource.isKind(sdk_build_core_1.ts.SyntaxKind.CallExpression);
385
+ const tableName = record.get('name').asString().getValue();
386
+ const isBootstrapDbObject = record.get('_bootstrap').ifBoolean()?.getValue() === true;
387
+ const nonBootstrapColumns = columns.filter((col) => col.get('_bootstrap').ifBoolean()?.getValue() !== true);
388
+ // Write as augmentation if we have sys_db_object from bootstrap and columns from elsewhere
389
+ const isAugmentation = isBootstrapDbObject && nonBootstrapColumns.length > 0;
390
+ if (isAugmentation) {
391
+ const augmentsExpression = new sdk_build_core_1.CallExpressionShape({
392
+ source: record,
393
+ callee: 'Table',
394
+ exportName: tableName,
395
+ args: [
396
+ record.transform(({ $ }) => ({
397
+ augments: $.val(tableName),
398
+ schema: $.val(schema),
399
+ })),
400
+ ],
401
+ });
402
+ return {
403
+ success: true,
404
+ value: writeAsCallExpression
405
+ ? augmentsExpression
406
+ : new sdk_build_core_1.VariableStatementShape({
407
+ source: record,
408
+ isExported: true,
409
+ variableName: new sdk_build_core_1.IdentifierShape({
410
+ source: record,
411
+ name: tableName,
412
+ }),
413
+ initializer: augmentsExpression,
414
+ }),
415
+ };
416
+ }
284
417
  const callExpression = new sdk_build_core_1.CallExpressionShape({
285
418
  source: record,
286
419
  callee: 'Table',
@@ -387,15 +520,15 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
387
520
  }),
388
521
  }));
389
522
  })).def([]),
390
- label: $.map((label) => label.ifString() ??
523
+ label: $.map((label) => label.ifString()?.ifNotEmpty() ??
391
524
  (tableDocumentation.length &&
392
- !(0, column_plugin_1.isDefaultDocumentation)('', tableDocumentation, config.tableDefaultLanguage)
525
+ !(0, column_plugin_1.isDefaultDocumentation)('', tableDocumentation, config.defaultLanguage)
393
526
  ? tableDocumentation.map((doc) => doc
394
527
  .transform(({ $ }) => ({
395
528
  label: $.def(''),
396
529
  help: $.def(''),
397
530
  hint: $.def(''),
398
- language: $.def(config.tableDefaultLanguage),
531
+ language: $.def(config.defaultLanguage),
399
532
  plural: $.def(''),
400
533
  url: $.def(''),
401
534
  urlTarget: $.from('url_target').def(''),
@@ -461,19 +594,57 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
461
594
  };
462
595
  },
463
596
  async toFile(record, { descendants, config, transform }) {
464
- if (config.tableOutputFormat !== 'bootstrap' || config.type === 'configuration' || record.isDeleted()) {
465
- // Defer to record plugin
597
+ if (record.isDeleted()) {
466
598
  return { success: false };
467
599
  }
468
- const tableName = record.get('name').asString().getValue();
600
+ const augmentsValue = record.get('augments').ifString()?.getValue();
601
+ const isAugmentation = augmentsValue !== undefined;
602
+ const tableName = augmentsValue ?? record.get('name').asString().getValue();
469
603
  const columns = descendants.query('sys_dictionary');
470
604
  const choices = descendants.query('sys_choice');
471
605
  const indexes = descendants.query('sys_index');
472
606
  const documentation = descendants.query('sys_documentation');
473
607
  const licensing = descendants.query('ua_table_licensing_config');
474
608
  const autoNumber = descendants.query('sys_number');
475
- let collectionRecord;
609
+ const overrides = descendants.query('sys_dictionary_override');
610
+ const [documentationFiles, licensingFiles, autoNumberFiles, overrideFiles, sysDictionaryFiles, sysDbObjectFiles,] = await Promise.all([
611
+ generateRecordXml(documentation.filter((doc) => !(0, column_plugin_1.isDefaultDocumentation)(doc.get('element')?.toString().getValue(), [doc], config.defaultLanguage)), config, transform),
612
+ generateRecordXml(licensing.filter((l) => !isDefaultLicenseConfig(tableName, l)), config, transform),
613
+ generateRecordXml(autoNumber, config, transform),
614
+ generateRecordXml(overrides, config, transform),
615
+ generateRecordXml(columns, config, transform, ['_bootstrap']),
616
+ isAugmentation
617
+ ? Promise.resolve([])
618
+ : generateRecordXml([record], config, transform, ['augments', '_bootstrap']),
619
+ ]);
620
+ if (config.type === 'configuration') {
621
+ // No bootstrap XML for configuration projects, just write independent record XML
622
+ const choiceSets = descendants.query('sys_choice_set');
623
+ const [choiceSetFiles, indexFiles] = await Promise.all([
624
+ Promise.all(choiceSets.map((cs) => {
625
+ const csChoices = choices.filter((c) => c.get('name').toString().getValue() === cs.get('name').toString().getValue() &&
626
+ c.get('element').toString().getValue() ===
627
+ cs.get('element').toString().getValue());
628
+ return (0, utils_1.generateChoiceSetFile)(cs, csChoices, config, transform);
629
+ })),
630
+ generateRecordXml(indexes, config, transform),
631
+ ]);
632
+ return {
633
+ success: true,
634
+ value: [
635
+ ...sysDictionaryFiles,
636
+ ...choiceSetFiles,
637
+ ...indexFiles,
638
+ ...sysDbObjectFiles,
639
+ ...documentationFiles,
640
+ ...licensingFiles,
641
+ ...autoNumberFiles,
642
+ ...overrideFiles,
643
+ ],
644
+ };
645
+ }
476
646
  let displayColumn;
647
+ let collectionRecord;
477
648
  const elements = [];
478
649
  for (const column of columns) {
479
650
  const displayValue = column.get('display').ifBoolean()?.getValue();
@@ -544,6 +715,23 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
544
715
  ['hint', column.get('hint').ifString()?.getValue()],
545
716
  ['help', column.get('help').ifString()?.getValue()],
546
717
  ['virtual', column.get('virtual').ifBoolean()?.getValue().toString()],
718
+ ['formula', column.get('formula').ifString()?.getValue() || undefined],
719
+ [
720
+ 'virtual_type',
721
+ column.get('virtual_type').ifString()?.getValue() === 'script'
722
+ ? undefined
723
+ : column.get('virtual_type').ifString()?.getValue(),
724
+ ],
725
+ [
726
+ 'use_reference_qualifier',
727
+ column.get('use_reference_qualifier').ifString()?.getValue() === 'simple'
728
+ ? undefined
729
+ : column.get('use_reference_qualifier').ifString()?.getValue(),
730
+ ],
731
+ [
732
+ 'dynamic_ref_qual',
733
+ column.get('dynamic_ref_qual').ifDefined()?.toString().getValue() || undefined,
734
+ ],
547
735
  ['widget', column.get('widget').ifString()?.getValue()],
548
736
  ['reference_qual', column.get('reference_qual').ifString()?.getValue()],
549
737
  ['reference_qual_condition', column.get('reference_qual_condition').ifString()?.getValue()],
@@ -610,9 +798,6 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
610
798
  ...indexElements,
611
799
  ]),
612
800
  ]).end({ prettyPrint: true });
613
- const documentationFiles = await generateRecordXml(documentation.filter((doc) => !(0, column_plugin_1.isDefaultDocumentation)(doc.get('element')?.toString().getValue(), [doc], config.tableDefaultLanguage)), config, transform);
614
- const licensingFiles = await generateRecordXml(licensing.filter((licensing) => !isDefaultLicenseConfig(tableName, licensing)), config, transform);
615
- const autoNumberFiles = await generateRecordXml(autoNumber, config, transform);
616
801
  return {
617
802
  success: true,
618
803
  value: [
@@ -625,6 +810,9 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
625
810
  ...documentationFiles,
626
811
  ...licensingFiles,
627
812
  ...autoNumberFiles,
813
+ ...overrideFiles,
814
+ ...sysDictionaryFiles,
815
+ ...sysDbObjectFiles,
628
816
  ],
629
817
  };
630
818
  },
@@ -636,6 +824,13 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
636
824
  value: `sys_dictionary_${record.get('name').getValue()}_${record.get('element').getValue() || 'null'}`,
637
825
  }),
638
826
  },
827
+ sys_dictionary_override: {
828
+ coalesce: ['name', 'element'],
829
+ getUpdateName: (record) => ({
830
+ success: true,
831
+ value: `sys_dictionary_override_${record.get('name').getValue()}_${record.get('element').getValue() || 'null'}`,
832
+ }),
833
+ },
639
834
  sys_documentation: {
640
835
  coalesce: ['name', 'element', 'language'],
641
836
  getUpdateName: (record) => ({
@@ -668,20 +863,33 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
668
863
  const table = callExpression.getArgument(0).asObject().withAliasedKeys(tableAliases);
669
864
  (0, utils_1.generateDeprecatedDiagnostics)(table, diagnostics);
670
865
  const relatedRecords = [];
671
- const tableName = table.get('name').asString();
866
+ const augments = table.get('augments').ifString();
867
+ const isAugmentation = augments !== undefined;
868
+ const tableName = isAugmentation ? augments : table.get('name').asString();
672
869
  if (!tableName.getValue().match(tableNameRegex)) {
673
870
  diagnostics.error(table.get('name'), 'Table name must only contain lowercase letters, numbers, and underscores and end with a letter or number');
674
871
  }
675
872
  let ignoreColumnNameCheck = false;
676
873
  const scopeName = config.scope;
677
- const scopeRegex = new RegExp(`^${scopeName}_`);
678
- const globalRegex = /^u_/;
679
- const tableNameMatch = tableName.getValue().match(scopeRegex);
680
- if (!tableNameMatch && !(0, sdk_build_core_1.isSNScope)(scopeName) && scopeName !== 'global') {
874
+ const scopePrefix = scopeName === 'global' ? 'u_' : `${scopeName}_`;
875
+ const prefixRegex = new RegExp(`^${scopePrefix}`);
876
+ const tableNameMatch = tableName.getValue().match(prefixRegex);
877
+ if (isAugmentation) {
878
+ if (tableNameMatch && scopeName !== 'global') {
879
+ const nameNode = tableName.getOriginalNode();
880
+ if (!nameNode?.getParentIfKind(sdk_build_core_1.ts.SyntaxKind.AsExpression)) {
881
+ diagnostics.error(table.get('augments'), `'augments' flag set on in-scope table '${tableName.getValue()}'`);
882
+ }
883
+ else {
884
+ ignoreColumnNameCheck = true;
885
+ }
886
+ }
887
+ }
888
+ else if (!tableNameMatch && !(0, sdk_build_core_1.isSNScope)(scopeName) && scopeName !== 'global') {
681
889
  const nameNode = tableName.getOriginalNode();
682
890
  if (nameNode && !nameNode.getParentIfKind(sdk_build_core_1.ts.SyntaxKind.AsExpression)) {
683
891
  // 'sn' and 'now' scoped apps ignore this validation
684
- diagnostics.error(table.get('name'), `'name' property should start with scope prefix '${scopeName}_'`);
892
+ diagnostics.error(table.get('name'), `'name' property should start with scope prefix '${scopePrefix}'`);
685
893
  }
686
894
  else {
687
895
  ignoreColumnNameCheck = true;
@@ -694,12 +902,11 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
694
902
  if (!exportedByName) {
695
903
  diagnostics.error(callExpression, `Table definition should be exported as a named export with the name '${tableName.getValue()}'`);
696
904
  }
697
- const globalTableNameMatch = tableName.getValue().match(globalRegex);
698
905
  let anyNonPrefixedGlobalColumn = false;
699
- if (scopeName === 'global' && !globalTableNameMatch) {
906
+ if (!isAugmentation && scopeName === 'global' && !tableNameMatch) {
700
907
  const schema = table.get('schema').asObject();
701
908
  for (const [name, _] of schema.entries()) {
702
- if (!name.match(globalRegex)) {
909
+ if (!name.match(prefixRegex)) {
703
910
  anyNonPrefixedGlobalColumn = true;
704
911
  break;
705
912
  }
@@ -708,36 +915,113 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
708
915
  diagnostics.error(table.get('name'), `Global table 'name' property should start with custom prefix 'u_'`);
709
916
  }
710
917
  }
711
- else if (scopeName === 'global') {
918
+ else if (scopeName === 'global' && !isAugmentation) {
712
919
  // Global table starts with custom prefix `u_`, allow any column name prefix
713
920
  ignoreColumnNameCheck = true;
714
921
  }
715
- // sys_dictionary
922
+ // sys_dictionary and sys_dictionary_override
716
923
  const schema = table.get('schema').asObject();
717
924
  const columnIdsMap = new Map();
718
925
  for (const [name, column] of schema.entries()) {
719
926
  if (!name.match(columnNameRegex)) {
720
927
  diagnostics.error(column.getOriginalNode().getParentIfKind(sdk_build_core_1.ts.SyntaxKind.PropertyAssignment) ?? column, 'Column name must only contain lowercase letters, numbers, and underscores');
721
928
  }
722
- if (!ignoreColumnNameCheck &&
723
- !tableNameMatch &&
724
- !(0, sdk_build_core_1.isSNScope)(scopeName) &&
725
- scopeName !== 'global' &&
726
- !name.match(scopeRegex)) {
727
- // 'sn' and 'now' scoped apps ignore this validation
728
- diagnostics.error(column.getOriginalNode().getParentIfKind(sdk_build_core_1.ts.SyntaxKind.PropertyAssignment) ?? column, `Column name should be prefixed with scope '${scopeName}_' if table name does not contain prefix`);
729
- }
730
- else if (scopeName === 'global' && !globalTableNameMatch && !name.match(globalRegex)) {
731
- diagnostics.error(column.getOriginalNode().getParentIfKind(sdk_build_core_1.ts.SyntaxKind.PropertyAssignment) ?? column, `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`);
929
+ // Check if this is an OverrideColumn
930
+ if (column.is(sdk_build_core_1.CallExpressionShape) &&
931
+ column.as(sdk_build_core_1.CallExpressionShape).getCallee() === 'OverrideColumn') {
932
+ // This is an OverrideColumn() call - get the object from its first argument
933
+ const columnObj = column.as(sdk_build_core_1.CallExpressionShape).getArgument(0).asObject();
934
+ // Handle OverrideColumn - create sys_dictionary_override record
935
+ // Validate that the table extends another table
936
+ const extendsTable = table.get('extends').ifString()?.getValue();
937
+ if (!extendsTable) {
938
+ diagnostics.error(column, `Cannot use OverrideColumn in table '${tableName.getValue()}' because it does not extend another table`);
939
+ return { success: false };
940
+ }
941
+ // Use baseTable if provided, otherwise default to extends
942
+ const baseTableValue = columnObj.get('baseTable');
943
+ let baseTable;
944
+ if (baseTableValue.ifString()) {
945
+ baseTable = baseTableValue.asString().getValue();
946
+ }
947
+ else {
948
+ // Default to extends if baseTable not provided
949
+ baseTable = extendsTable;
950
+ }
951
+ // Create sys_dictionary_override record
952
+ const overrideRecord = await factory.createRecord({
953
+ source: statement ?? callExpression,
954
+ table: 'sys_dictionary_override',
955
+ properties: columnObj.transform(({ $ }) => ({
956
+ name: $.val(tableName),
957
+ element: $.val(name),
958
+ base_table: $.val(baseTable),
959
+ default_value: $.from('default'),
960
+ default_value_override: $.from('default').map((v) => !!v.ifDefined()),
961
+ calculation: $.from('calculation'),
962
+ calculation_override: $.from('calculation').map((v) => !!v.ifDefined()),
963
+ reference_qual: $.from('referenceQualifier'),
964
+ reference_qual_override: $.from('referenceQualifier').map((v) => !!v.ifDefined()),
965
+ read_only_option: $.from('readOnlyOption'),
966
+ read_only_option_override: $.from('readOnlyOption').map((v) => !!v.ifDefined()),
967
+ read_only: $.from('readOnlyOption').map((readOnlyOption) => {
968
+ // read_only should be true if readOnlyOption has a value
969
+ return !!readOnlyOption.ifDefined();
970
+ }),
971
+ read_only_override: $.from('readOnlyOption').map((v) => !!v.ifDefined()),
972
+ dependent: $.from('dependent'),
973
+ dependent_override: $.from('dependent').map((v) => !!v.ifDefined()),
974
+ mandatory: $.from('mandatory').toBoolean().def(false),
975
+ mandatory_override: $.from('mandatory').map((v) => !!v.ifDefined()),
976
+ attributes: $.from('attributes').map((attrs) => {
977
+ if (!attrs.isObject()) {
978
+ return;
979
+ }
980
+ const attrsObj = attrs.asObject().getValue();
981
+ return Object.entries(attrsObj)
982
+ .map(([key, value]) => `${key}=${value}`)
983
+ .join(',');
984
+ }),
985
+ attributes_override: $.from('attributes').map((v) => !!v.ifDefined()),
986
+ display_override: $.from('display').map((v) => {
987
+ const boolShape = v.ifBoolean();
988
+ if (!boolShape) {
989
+ return false;
990
+ }
991
+ return boolShape.getValue() === true;
992
+ }),
993
+ })),
994
+ });
995
+ relatedRecords.push(overrideRecord);
732
996
  }
733
- const display = table.get('display').ifString()?.getValue() === name;
734
- const result = await transform.toRecord((0, column_helper_1.addFieldsToColumn)({ name, table: tableName.getValue(), display }, column.as(sdk_build_core_1.CallExpressionShape)));
735
- if (!result.success) {
736
- diagnostics.error(column, 'Invalid column in table schema');
737
- return { success: false };
997
+ else {
998
+ // Handle regular column - create sys_dictionary record
999
+ if (isAugmentation && !tableNameMatch) {
1000
+ if (!(0, sdk_build_core_1.isSNScope)(scopeName) && !name.match(prefixRegex)) {
1001
+ diagnostics.error(column.getOriginalNode().getParentIfKind(sdk_build_core_1.ts.SyntaxKind.PropertyAssignment) ??
1002
+ column, `Column name '${name}' must be prefixed with '${scopePrefix}' when augmenting a table`);
1003
+ }
1004
+ }
1005
+ else if (!ignoreColumnNameCheck &&
1006
+ !tableNameMatch &&
1007
+ !(0, sdk_build_core_1.isSNScope)(scopeName) &&
1008
+ scopeName !== 'global' &&
1009
+ !name.match(prefixRegex)) {
1010
+ // 'sn' and 'now' scoped apps ignore this validation
1011
+ diagnostics.error(column.getOriginalNode().getParentIfKind(sdk_build_core_1.ts.SyntaxKind.PropertyAssignment) ?? column, `Column name should be prefixed with scope '${scopePrefix}' if table name does not contain prefix`);
1012
+ }
1013
+ else if (scopeName === 'global' && !tableNameMatch && !name.match(prefixRegex)) {
1014
+ diagnostics.error(column.getOriginalNode().getParentIfKind(sdk_build_core_1.ts.SyntaxKind.PropertyAssignment) ?? column, `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`);
1015
+ }
1016
+ const display = table.get('display').ifString()?.getValue() === name;
1017
+ const result = await transform.toRecord((0, column_helper_1.addFieldsToColumn)({ name, table: tableName.getValue(), display }, column.as(sdk_build_core_1.CallExpressionShape)));
1018
+ if (!result.success) {
1019
+ diagnostics.error(column, 'Invalid column in table schema');
1020
+ return { success: false };
1021
+ }
1022
+ relatedRecords.push(result.value);
1023
+ columnIdsMap.set(name, result.value.getId().getValue());
738
1024
  }
739
- relatedRecords.push(result.value);
740
- columnIdsMap.set(name, result.value.getId().getValue());
741
1025
  }
742
1026
  // sys_index
743
1027
  if (table.get('index').isArray()) {
@@ -852,7 +1136,7 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
852
1136
  properties: {
853
1137
  name: tableName,
854
1138
  element: undefined,
855
- language: config.tableDefaultLanguage,
1139
+ language: config.defaultLanguage,
856
1140
  label: table.get('label').ifString() ?? tableName,
857
1141
  plural: (0, column_plugin_1.generatePlural)(table.get('label')?.ifString()?.getValue() ?? tableName.getValue()),
858
1142
  },
@@ -876,27 +1160,30 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
876
1160
  relatedRecords.push(sysNumberRecord);
877
1161
  }
878
1162
  // sys_dictionary (collection)
879
- relatedRecords.push(await factory.createRecord({
880
- source: statement ?? callExpression,
881
- table: 'sys_dictionary',
882
- properties: table.transform(({ $ }) => ({
883
- name: $,
884
- element: $.val(undefined),
885
- internal_type: $.def('collection'),
886
- attributes: $.map((attributes) => {
887
- if (!attributes.isObject()) {
888
- return undefined;
889
- }
890
- const attributesObj = attributes.asObject().getValue();
891
- return Object.entries(attributesObj)
892
- .map(([key, value]) => `${key}=${value}`)
893
- .join(',');
894
- }).def(''),
895
- audit: $.def(false),
896
- read_only: $.from('readOnly').def(false),
897
- text_index: $.from('textIndex').def(false),
898
- })),
899
- }));
1163
+ if (!isAugmentation) {
1164
+ relatedRecords.push(await factory.createRecord({
1165
+ source: statement ?? callExpression,
1166
+ table: 'sys_dictionary',
1167
+ properties: table.transform(({ $ }) => ({
1168
+ name: $,
1169
+ element: $.val(undefined),
1170
+ internal_type: $.def('collection'),
1171
+ active: $.val(true),
1172
+ attributes: $.map((attributes) => {
1173
+ if (!attributes.isObject()) {
1174
+ return undefined;
1175
+ }
1176
+ const attributesObj = attributes.asObject().getValue();
1177
+ return Object.entries(attributesObj)
1178
+ .map(([key, value]) => `${key}=${value}`)
1179
+ .join(',');
1180
+ }).def(''),
1181
+ audit: $.def(false),
1182
+ read_only: $.from('readOnly').def(false),
1183
+ text_index: $.from('textIndex').def(false),
1184
+ })),
1185
+ }));
1186
+ }
900
1187
  const hasAction = (actionName, actions) => {
901
1188
  return actions
902
1189
  .ifArray()
@@ -937,10 +1224,12 @@ exports.TablePlugin = sdk_build_core_1.Plugin.create({
937
1224
  .map((actions) => hasAction('create', actions))
938
1225
  .def(false),
939
1226
  is_extendable: $.from('extensible').toBoolean().def(false),
940
- label: $.map((label) => (0, column_to_record_1.getLabelForDefaultLanguage)(label, config.tableDefaultLanguage)),
1227
+ label: $.map((label) => (0, column_to_record_1.getLabelForDefaultLanguage)(label, config.defaultLanguage)),
941
1228
  live_feed_enabled: $.from('liveFeed').toBoolean().def(false),
942
- name: $,
1229
+ name: $.from('name', 'augments').map((nameVal, augmentsVal) => augmentsVal.ifString() ?? nameVal),
943
1230
  scriptable_table: $.from('scriptableTable').toBoolean().def(false),
1231
+ // Controls toFile output, not written to record XML
1232
+ augments: $.from('augments'),
944
1233
  })),
945
1234
  });
946
1235
  if (exportedByName) {
@@ -1024,6 +1313,16 @@ function parseTableBootstrapXml(xml) {
1024
1313
  useDynamicDefault: column['@_use_dynamic_default'],
1025
1314
  reference: column['@_reference'],
1026
1315
  isVirtual: column['@_virtual'],
1316
+ formula: column['@_formula'],
1317
+ virtualType: column['@_virtual_type'] === 'script' || column['@_virtual_type'] === 'formula'
1318
+ ? column['@_virtual_type']
1319
+ : undefined,
1320
+ useReferenceQualifier: column['@_use_reference_qualifier'] === 'simple' ||
1321
+ column['@_use_reference_qualifier'] === 'dynamic' ||
1322
+ column['@_use_reference_qualifier'] === 'advanced'
1323
+ ? column['@_use_reference_qualifier']
1324
+ : undefined,
1325
+ dynamicRefQual: column['@_dynamic_ref_qual'],
1027
1326
  calculation: column['@_calculation'],
1028
1327
  choiceField: column['@_choice_field'],
1029
1328
  functionDefinition: column['@_function_definition'],
@@ -1119,6 +1418,7 @@ function tableDefToRecordProperties(tableDef, tableDefaultLanguage = 'en') {
1119
1418
  const sysDictionary = [];
1120
1419
  const sysChoice = [];
1121
1420
  const sysDocumentation = [];
1421
+ const sysDictionaryOverride = [];
1122
1422
  // table documentation
1123
1423
  if (tableDef.label && tableDef.plural) {
1124
1424
  sysDocumentation.push({
@@ -1150,6 +1450,10 @@ function tableDefToRecordProperties(tableDef, tableDefaultLanguage = 'en') {
1150
1450
  use_dynamic_default: column.useDynamicDefault,
1151
1451
  reference: column.reference,
1152
1452
  virtual: column.isVirtual,
1453
+ formula: column.formula,
1454
+ virtual_type: column.virtualType,
1455
+ use_reference_qualifier: column.useReferenceQualifier,
1456
+ dynamic_ref_qual: column.dynamicRefQual,
1153
1457
  default: column.defaultValue,
1154
1458
  calculation: column.calculation,
1155
1459
  choice_field: column.choiceField,
@@ -1229,6 +1533,10 @@ function tableDefToRecordProperties(tableDef, tableDefaultLanguage = 'en') {
1229
1533
  use_dynamic_default: undefined,
1230
1534
  reference: undefined,
1231
1535
  virtual: undefined,
1536
+ formula: undefined,
1537
+ virtual_type: undefined,
1538
+ use_reference_qualifier: undefined,
1539
+ dynamic_ref_qual: undefined,
1232
1540
  default: undefined,
1233
1541
  calculation: undefined,
1234
1542
  choice_field: undefined,
@@ -1269,6 +1577,7 @@ function tableDefToRecordProperties(tableDef, tableDefaultLanguage = 'en') {
1269
1577
  sysChoice,
1270
1578
  sysIndex,
1271
1579
  sysDocumentation,
1580
+ sysDictionaryOverride,
1272
1581
  };
1273
1582
  }
1274
1583
  function addTableToGlobalGeneratedFile(tableArg, sourceFilePath, compiler) {
@@ -1279,7 +1588,7 @@ function addTableToGlobalGeneratedFile(tableArg, sourceFilePath, compiler) {
1279
1588
  namedImports: [],
1280
1589
  };
1281
1590
  const generatedTableFile = compiler.getGeneratedTableFile();
1282
- const tableName = tableArg.get('name').asString().getValue();
1591
+ const tableName = tableArg.get('augments').ifString()?.getValue() ?? tableArg.get('name').asString().getValue();
1283
1592
  if (!(tableName.trim().length > 0 && tableName.match(tableNameRegex))) {
1284
1593
  return;
1285
1594
  }
@@ -1355,7 +1664,7 @@ function createElement(name, attributes = [], children = []) {
1355
1664
  }
1356
1665
  return element;
1357
1666
  }
1358
- async function generateRecordXml(records, config, transform) {
1667
+ async function generateRecordXml(records, config, transform, excludeFields = []) {
1359
1668
  const files = [];
1360
1669
  for (const record of records) {
1361
1670
  const recordBuilder = (0, sdk_build_core_1.unloadBuilder)({ scope: config.scope, scopeId: config.scopeId, table: record.getTable() });
@@ -1364,6 +1673,7 @@ async function generateRecordXml(records, config, transform) {
1364
1673
  record
1365
1674
  .entries()
1366
1675
  .sort(([a], [b]) => a.localeCompare(b))
1676
+ .filter(([prop]) => excludeFields.length === 0 || !excludeFields.includes(prop))
1367
1677
  .forEach(([prop, shape]) => builder.field(prop, shape));
1368
1678
  files.push({
1369
1679
  source: record,