@servicenow/sdk-build-plugins 4.6.0 → 4.7.0

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 (286) hide show
  1. package/dist/acl-plugin.js +3 -4
  2. package/dist/acl-plugin.js.map +1 -1
  3. package/dist/applicability-plugin.js +0 -2
  4. package/dist/applicability-plugin.js.map +1 -1
  5. package/dist/application-menu-plugin.js +0 -2
  6. package/dist/application-menu-plugin.js.map +1 -1
  7. package/dist/arrow-function-plugin.js +0 -1
  8. package/dist/arrow-function-plugin.js.map +1 -1
  9. package/dist/atf/test-plugin.js +6 -10
  10. package/dist/atf/test-plugin.js.map +1 -1
  11. package/dist/basic-syntax-plugin.js +10 -4
  12. package/dist/basic-syntax-plugin.js.map +1 -1
  13. package/dist/business-rule-plugin.js +0 -1
  14. package/dist/business-rule-plugin.js.map +1 -1
  15. package/dist/call-expression-plugin.js +0 -1
  16. package/dist/call-expression-plugin.js.map +1 -1
  17. package/dist/claims-plugin.js +0 -1
  18. package/dist/claims-plugin.js.map +1 -1
  19. package/dist/client-script-plugin.js +0 -1
  20. package/dist/client-script-plugin.js.map +1 -1
  21. package/dist/column-plugin.js +120 -49
  22. package/dist/column-plugin.js.map +1 -1
  23. package/dist/cross-scope-privilege-plugin.js +0 -1
  24. package/dist/cross-scope-privilege-plugin.js.map +1 -1
  25. package/dist/dashboard/dashboard-plugin.js +0 -2
  26. package/dist/dashboard/dashboard-plugin.js.map +1 -1
  27. package/dist/data-plugin.js +0 -1
  28. package/dist/data-plugin.js.map +1 -1
  29. package/dist/data-policy-plugin.d.ts +2 -0
  30. package/dist/data-policy-plugin.js +276 -0
  31. package/dist/data-policy-plugin.js.map +1 -0
  32. package/dist/email-notification-plugin.js +2 -3
  33. package/dist/email-notification-plugin.js.map +1 -1
  34. package/dist/flow/flow-logic/flow-logic-constants.d.ts +2 -0
  35. package/dist/flow/flow-logic/flow-logic-constants.js +6 -1
  36. package/dist/flow/flow-logic/flow-logic-constants.js.map +1 -1
  37. package/dist/flow/flow-logic/flow-logic-diagnostics.js +192 -56
  38. package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
  39. package/dist/flow/flow-logic/flow-logic-plugin-helpers.d.ts +2 -1
  40. package/dist/flow/flow-logic/flow-logic-plugin-helpers.js +44 -5
  41. package/dist/flow/flow-logic/flow-logic-plugin-helpers.js.map +1 -1
  42. package/dist/flow/flow-logic/flow-logic-plugin.js +279 -29
  43. package/dist/flow/flow-logic/flow-logic-plugin.js.map +1 -1
  44. package/dist/flow/flow-logic/flow-logic-shapes.d.ts +15 -0
  45. package/dist/flow/flow-logic/flow-logic-shapes.js +25 -1
  46. package/dist/flow/flow-logic/flow-logic-shapes.js.map +1 -1
  47. package/dist/flow/plugins/approval-rules-plugin.js +0 -1
  48. package/dist/flow/plugins/approval-rules-plugin.js.map +1 -1
  49. package/dist/flow/plugins/flow-action-definition-plugin.js +804 -205
  50. package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
  51. package/dist/flow/plugins/flow-data-pill-plugin.js +3 -5
  52. package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
  53. package/dist/flow/plugins/flow-definition-plugin.js +84 -17
  54. package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
  55. package/dist/flow/plugins/flow-diagnostics-plugin.js +65 -3
  56. package/dist/flow/plugins/flow-diagnostics-plugin.js.map +1 -1
  57. package/dist/flow/plugins/flow-instance-plugin.js +13 -5
  58. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  59. package/dist/flow/plugins/flow-trigger-instance-plugin.js +0 -1
  60. package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -1
  61. package/dist/flow/plugins/inline-script-plugin.js +0 -1
  62. package/dist/flow/plugins/inline-script-plugin.js.map +1 -1
  63. package/dist/flow/plugins/step-definition-plugin.js +0 -2
  64. package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
  65. package/dist/flow/plugins/step-instance-plugin.js +216 -77
  66. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  67. package/dist/flow/plugins/trigger-plugin.js +0 -2
  68. package/dist/flow/plugins/trigger-plugin.js.map +1 -1
  69. package/dist/flow/plugins/wfa-datapill-plugin.js +0 -1
  70. package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
  71. package/dist/flow/utils/datapill-transformer.js +9 -5
  72. package/dist/flow/utils/datapill-transformer.js.map +1 -1
  73. package/dist/flow/utils/flow-constants.d.ts +12 -0
  74. package/dist/flow/utils/flow-constants.js +17 -3
  75. package/dist/flow/utils/flow-constants.js.map +1 -1
  76. package/dist/flow/utils/flow-io-to-record.d.ts +1 -1
  77. package/dist/flow/utils/flow-io-to-record.js +21 -13
  78. package/dist/flow/utils/flow-io-to-record.js.map +1 -1
  79. package/dist/flow/utils/flow-pill-utils.d.ts +26 -0
  80. package/dist/flow/utils/flow-pill-utils.js +50 -0
  81. package/dist/flow/utils/flow-pill-utils.js.map +1 -0
  82. package/dist/flow/utils/flow-stage-processor.d.ts +138 -0
  83. package/dist/flow/utils/flow-stage-processor.js +665 -0
  84. package/dist/flow/utils/flow-stage-processor.js.map +1 -0
  85. package/dist/flow/utils/pill-string-parser.js +28 -43
  86. package/dist/flow/utils/pill-string-parser.js.map +1 -1
  87. package/dist/flow/utils/utils.d.ts +11 -6
  88. package/dist/flow/utils/utils.js +37 -28
  89. package/dist/flow/utils/utils.js.map +1 -1
  90. package/dist/form-plugin.js +4 -14
  91. package/dist/form-plugin.js.map +1 -1
  92. package/dist/html-import-plugin.js +0 -1
  93. package/dist/html-import-plugin.js.map +1 -1
  94. package/dist/import-sets-plugin.js +0 -2
  95. package/dist/import-sets-plugin.js.map +1 -1
  96. package/dist/inbound-email-action-plugin.js +0 -1
  97. package/dist/inbound-email-action-plugin.js.map +1 -1
  98. package/dist/index.d.ts +2 -1
  99. package/dist/index.js +5 -1
  100. package/dist/index.js.map +1 -1
  101. package/dist/instance-scan-plugin.js +0 -7
  102. package/dist/instance-scan-plugin.js.map +1 -1
  103. package/dist/json-plugin.js +0 -1
  104. package/dist/json-plugin.js.map +1 -1
  105. package/dist/list-plugin.js +4 -1
  106. package/dist/list-plugin.js.map +1 -1
  107. package/dist/now-attach-plugin.js +0 -1
  108. package/dist/now-attach-plugin.js.map +1 -1
  109. package/dist/now-config-plugin.js +1 -1
  110. package/dist/now-config-plugin.js.map +1 -1
  111. package/dist/now-id-plugin.js +0 -1
  112. package/dist/now-id-plugin.js.map +1 -1
  113. package/dist/now-include-plugin.js +0 -1
  114. package/dist/now-include-plugin.js.map +1 -1
  115. package/dist/now-ref-plugin.js +0 -1
  116. package/dist/now-ref-plugin.js.map +1 -1
  117. package/dist/now-unresolved-plugin.js +0 -1
  118. package/dist/now-unresolved-plugin.js.map +1 -1
  119. package/dist/package-json-plugin.js +3 -2
  120. package/dist/package-json-plugin.js.map +1 -1
  121. package/dist/property-plugin.js +0 -2
  122. package/dist/property-plugin.js.map +1 -1
  123. package/dist/record-plugin.d.ts +2 -0
  124. package/dist/record-plugin.js +5 -4
  125. package/dist/record-plugin.js.map +1 -1
  126. package/dist/repack/lint/Rules.d.ts +1 -2
  127. package/dist/rest-api-plugin.js +6 -5
  128. package/dist/rest-api-plugin.js.map +1 -1
  129. package/dist/role-plugin.js +0 -1
  130. package/dist/role-plugin.js.map +1 -1
  131. package/dist/schedule-script/scheduled-script-plugin.js +5 -4
  132. package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
  133. package/dist/script-action-plugin.js +0 -2
  134. package/dist/script-action-plugin.js.map +1 -1
  135. package/dist/script-include-plugin.js +4 -4
  136. package/dist/script-include-plugin.js.map +1 -1
  137. package/dist/server-module-plugin/index.js +2 -3
  138. package/dist/server-module-plugin/index.js.map +1 -1
  139. package/dist/service-catalog/catalog-clientscript-plugin.js +2 -4
  140. package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -1
  141. package/dist/service-catalog/catalog-item-plugin.js +0 -2
  142. package/dist/service-catalog/catalog-item-plugin.js.map +1 -1
  143. package/dist/service-catalog/catalog-ui-policy-plugin.js +2 -4
  144. package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -1
  145. package/dist/service-catalog/sc-record-producer-plugin.js +0 -2
  146. package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -1
  147. package/dist/service-catalog/service-catalog-base.d.ts +2 -2
  148. package/dist/service-catalog/service-catalog-base.js +2 -2
  149. package/dist/service-catalog/service-catalog-base.js.map +1 -1
  150. package/dist/service-catalog/utils.js +1 -1
  151. package/dist/service-catalog/utils.js.map +1 -1
  152. package/dist/service-catalog/variable-set-plugin.js +0 -2
  153. package/dist/service-catalog/variable-set-plugin.js.map +1 -1
  154. package/dist/service-portal/angular-provider-plugin.js +0 -2
  155. package/dist/service-portal/angular-provider-plugin.js.map +1 -1
  156. package/dist/service-portal/dependency-plugin.js +3 -5
  157. package/dist/service-portal/dependency-plugin.js.map +1 -1
  158. package/dist/service-portal/header-footer-plugin.js +3 -5
  159. package/dist/service-portal/header-footer-plugin.js.map +1 -1
  160. package/dist/service-portal/menu-plugin.js +0 -1
  161. package/dist/service-portal/menu-plugin.js.map +1 -1
  162. package/dist/service-portal/page-plugin.js +0 -1
  163. package/dist/service-portal/page-plugin.js.map +1 -1
  164. package/dist/service-portal/page-route-map-plugin.js +0 -1
  165. package/dist/service-portal/page-route-map-plugin.js.map +1 -1
  166. package/dist/service-portal/portal-plugin.js +0 -2
  167. package/dist/service-portal/portal-plugin.js.map +1 -1
  168. package/dist/service-portal/theme-plugin.js +0 -2
  169. package/dist/service-portal/theme-plugin.js.map +1 -1
  170. package/dist/service-portal/widget-plugin.js +3 -5
  171. package/dist/service-portal/widget-plugin.js.map +1 -1
  172. package/dist/sla-plugin.js +0 -2
  173. package/dist/sla-plugin.js.map +1 -1
  174. package/dist/static-content-plugin.js +32 -3
  175. package/dist/static-content-plugin.js.map +1 -1
  176. package/dist/table-plugin.js +303 -66
  177. package/dist/table-plugin.js.map +1 -1
  178. package/dist/ui-action-plugin.js +26 -17
  179. package/dist/ui-action-plugin.js.map +1 -1
  180. package/dist/ui-page-plugin.js +159 -17
  181. package/dist/ui-page-plugin.js.map +1 -1
  182. package/dist/ui-policy-plugin.js +28 -97
  183. package/dist/ui-policy-plugin.js.map +1 -1
  184. package/dist/user-preference-plugin.js +0 -2
  185. package/dist/user-preference-plugin.js.map +1 -1
  186. package/dist/utils.d.ts +5 -9
  187. package/dist/utils.js +38 -11
  188. package/dist/utils.js.map +1 -1
  189. package/dist/ux-list-menu-config-plugin.js +0 -2
  190. package/dist/ux-list-menu-config-plugin.js.map +1 -1
  191. package/dist/view-plugin.js +0 -1
  192. package/dist/view-plugin.js.map +1 -1
  193. package/dist/workspace-plugin.js +0 -2
  194. package/dist/workspace-plugin.js.map +1 -1
  195. package/package.json +6 -6
  196. package/src/acl-plugin.ts +4 -5
  197. package/src/applicability-plugin.ts +0 -2
  198. package/src/application-menu-plugin.ts +0 -2
  199. package/src/arrow-function-plugin.ts +0 -1
  200. package/src/atf/test-plugin.ts +6 -11
  201. package/src/basic-syntax-plugin.ts +11 -4
  202. package/src/business-rule-plugin.ts +1 -2
  203. package/src/call-expression-plugin.ts +0 -1
  204. package/src/claims-plugin.ts +0 -1
  205. package/src/client-script-plugin.ts +1 -2
  206. package/src/column-plugin.ts +163 -76
  207. package/src/cross-scope-privilege-plugin.ts +1 -2
  208. package/src/dashboard/dashboard-plugin.ts +0 -2
  209. package/src/data-plugin.ts +0 -1
  210. package/src/data-policy-plugin.ts +333 -0
  211. package/src/email-notification-plugin.ts +8 -4
  212. package/src/flow/flow-logic/flow-logic-constants.ts +6 -0
  213. package/src/flow/flow-logic/flow-logic-diagnostics.ts +236 -58
  214. package/src/flow/flow-logic/flow-logic-plugin-helpers.ts +59 -6
  215. package/src/flow/flow-logic/flow-logic-plugin.ts +368 -38
  216. package/src/flow/flow-logic/flow-logic-shapes.ts +25 -0
  217. package/src/flow/plugins/approval-rules-plugin.ts +0 -1
  218. package/src/flow/plugins/flow-action-definition-plugin.ts +940 -208
  219. package/src/flow/plugins/flow-data-pill-plugin.ts +3 -5
  220. package/src/flow/plugins/flow-definition-plugin.ts +159 -26
  221. package/src/flow/plugins/flow-diagnostics-plugin.ts +89 -3
  222. package/src/flow/plugins/flow-instance-plugin.ts +26 -12
  223. package/src/flow/plugins/flow-trigger-instance-plugin.ts +0 -1
  224. package/src/flow/plugins/inline-script-plugin.ts +0 -1
  225. package/src/flow/plugins/step-definition-plugin.ts +0 -2
  226. package/src/flow/plugins/step-instance-plugin.ts +259 -65
  227. package/src/flow/plugins/trigger-plugin.ts +0 -2
  228. package/src/flow/plugins/wfa-datapill-plugin.ts +0 -1
  229. package/src/flow/utils/datapill-transformer.ts +13 -5
  230. package/src/flow/utils/flow-constants.ts +19 -1
  231. package/src/flow/utils/flow-io-to-record.ts +29 -19
  232. package/src/flow/utils/flow-pill-utils.ts +48 -0
  233. package/src/flow/utils/flow-stage-processor.ts +831 -0
  234. package/src/flow/utils/pill-string-parser.ts +29 -47
  235. package/src/flow/utils/utils.ts +39 -35
  236. package/src/form-plugin.ts +5 -15
  237. package/src/html-import-plugin.ts +0 -1
  238. package/src/import-sets-plugin.ts +0 -2
  239. package/src/inbound-email-action-plugin.ts +1 -2
  240. package/src/index.ts +7 -1
  241. package/src/instance-scan-plugin.ts +0 -7
  242. package/src/json-plugin.ts +0 -1
  243. package/src/list-plugin.ts +6 -2
  244. package/src/now-attach-plugin.ts +0 -1
  245. package/src/now-config-plugin.ts +1 -1
  246. package/src/now-id-plugin.ts +0 -1
  247. package/src/now-include-plugin.ts +0 -1
  248. package/src/now-ref-plugin.ts +0 -1
  249. package/src/now-unresolved-plugin.ts +0 -1
  250. package/src/package-json-plugin.ts +8 -3
  251. package/src/property-plugin.ts +0 -2
  252. package/src/record-plugin.ts +14 -6
  253. package/src/repack/lint/Rules.ts +1 -1
  254. package/src/rest-api-plugin.ts +7 -6
  255. package/src/role-plugin.ts +1 -2
  256. package/src/schedule-script/scheduled-script-plugin.ts +11 -5
  257. package/src/script-action-plugin.ts +0 -2
  258. package/src/script-include-plugin.ts +8 -4
  259. package/src/server-module-plugin/index.ts +2 -3
  260. package/src/service-catalog/catalog-clientscript-plugin.ts +2 -4
  261. package/src/service-catalog/catalog-item-plugin.ts +0 -2
  262. package/src/service-catalog/catalog-ui-policy-plugin.ts +2 -4
  263. package/src/service-catalog/sc-record-producer-plugin.ts +0 -2
  264. package/src/service-catalog/service-catalog-base.ts +2 -2
  265. package/src/service-catalog/utils.ts +1 -1
  266. package/src/service-catalog/variable-set-plugin.ts +0 -2
  267. package/src/service-portal/angular-provider-plugin.ts +0 -2
  268. package/src/service-portal/dependency-plugin.ts +0 -2
  269. package/src/service-portal/header-footer-plugin.ts +0 -2
  270. package/src/service-portal/menu-plugin.ts +1 -2
  271. package/src/service-portal/page-plugin.ts +1 -2
  272. package/src/service-portal/page-route-map-plugin.ts +1 -2
  273. package/src/service-portal/portal-plugin.ts +0 -2
  274. package/src/service-portal/theme-plugin.ts +0 -2
  275. package/src/service-portal/widget-plugin.ts +0 -2
  276. package/src/sla-plugin.ts +0 -2
  277. package/src/static-content-plugin.ts +37 -4
  278. package/src/table-plugin.ts +371 -92
  279. package/src/ui-action-plugin.ts +30 -17
  280. package/src/ui-page-plugin.ts +188 -20
  281. package/src/ui-policy-plugin.ts +33 -130
  282. package/src/user-preference-plugin.ts +0 -2
  283. package/src/utils.ts +48 -11
  284. package/src/ux-list-menu-config-plugin.ts +0 -2
  285. package/src/view-plugin.ts +0 -1
  286. package/src/workspace-plugin.ts +0 -2
@@ -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 { 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
@@ -221,6 +229,8 @@ type TableDefinition = {
221
229
  actionsAccess: boolean | undefined
222
230
  readAccess: boolean | undefined
223
231
  isExtendable: boolean | undefined
232
+ createAccessControls: boolean | undefined
233
+ userRole: string | undefined
224
234
  scriptableTable: boolean | undefined
225
235
  attributes: string | undefined
226
236
  display: string | undefined
@@ -232,6 +242,8 @@ type SysDbObjectProperties = {
232
242
  label: string | undefined
233
243
  super_class: string | undefined
234
244
  is_extendable: boolean | undefined
245
+ create_access_controls: boolean | undefined
246
+ user_role: string | undefined
235
247
  scriptable_table: boolean | undefined
236
248
  client_scripts_access: boolean | undefined
237
249
  ws_access: boolean | undefined
@@ -263,6 +275,10 @@ type SysDictionaryProperties = {
263
275
  use_dynamic_default: boolean | undefined
264
276
  reference: string | undefined
265
277
  virtual: boolean | undefined
278
+ formula: string | undefined
279
+ virtual_type: 'script' | 'formula' | undefined
280
+ use_reference_qualifier: 'simple' | 'dynamic' | 'advanced' | undefined
281
+ dynamic_ref_qual: string | undefined
266
282
  default: string | undefined
267
283
  calculation: string | undefined
268
284
  choice_field: string | undefined
@@ -384,7 +400,6 @@ const licensingAliases = {
384
400
 
385
401
  export const TablePlugin = Plugin.create({
386
402
  name: 'TablePlugin',
387
- docs: [createSdkDocEntry('Table', ['sys_db_object'])],
388
403
  files: [
389
404
  {
390
405
  matcher: /\.xml$/,
@@ -419,7 +434,11 @@ export const TablePlugin = Plugin.create({
419
434
  await factory.createRecord({
420
435
  source: file,
421
436
  table,
422
- properties: filterUndefinedProperties(rec),
437
+ properties: {
438
+ ...filterUndefinedProperties(rec),
439
+ // Decorate generated sys_db_object
440
+ ...(key === 'sysDbObject' || key === 'sysDictionary' ? { _bootstrap: true } : {}),
441
+ },
423
442
  })
424
443
  )
425
444
  }
@@ -489,7 +508,7 @@ export const TablePlugin = Plugin.create({
489
508
  via: 'super_class',
490
509
  },
491
510
  },
492
- toShape(record, { descendants, config }) {
511
+ toShape(record, { descendants, config, compiler }) {
493
512
  const schema: { [key: string]: CallExpressionShape } = {}
494
513
  let displayColumn: string | undefined
495
514
  const columns = descendants.query('sys_dictionary')
@@ -528,29 +547,41 @@ export const TablePlugin = Plugin.create({
528
547
  override.transform(({ $ }) => ({
529
548
  baseTable: $.from('base_table'),
530
549
  default: $.from('default_value_override', 'default_value').map((flag, value) => {
531
- return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
550
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
551
+ ? value.ifString()?.getValue()
552
+ : undefined
532
553
  }),
533
554
  calculation: $.from('calculation_override', 'calculation').map((flag, value) => {
534
- return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
555
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
556
+ ? value.ifString()?.getValue()
557
+ : undefined
535
558
  }),
536
559
  referenceQualifier: $.from('reference_qual_override', 'reference_qual').map(
537
560
  (flag, value) => {
538
- return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
561
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
562
+ ? value.ifString()?.getValue()
563
+ : undefined
539
564
  }
540
565
  ),
541
566
  readOnlyOption: $.from('read_only_option_override', 'read_only_option').map(
542
567
  (flag, value) => {
543
- return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
568
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
569
+ ? value.ifString()?.getValue()
570
+ : undefined
544
571
  }
545
572
  ),
546
573
  dependent: $.from('dependent_override', 'dependent').map((flag, value) => {
547
- return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
574
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
575
+ ? value.ifString()?.getValue()
576
+ : undefined
548
577
  }),
549
578
  mandatory: $.from('mandatory_override', 'mandatory').map((flag, value) => {
550
- return flag.toBoolean()?.getValue() ? value.toBoolean()?.getValue() : undefined
579
+ return flag.ifDefined() && flag.toBoolean()?.getValue()
580
+ ? value.toBoolean()?.getValue()
581
+ : undefined
551
582
  }),
552
583
  attributes: $.from('attributes_override', 'attributes').map((flag, attrs) => {
553
- if (!flag.toBoolean()?.getValue() || !attrs.isString()) {
584
+ if (!flag.ifDefined() || !flag.toBoolean()?.getValue() || !attrs.isString()) {
554
585
  return undefined
555
586
  }
556
587
  const result: { [key: string]: string | number | boolean } = {}
@@ -603,6 +634,44 @@ export const TablePlugin = Plugin.create({
603
634
  // Avoid replacing call expressions with variable statements
604
635
  const writeAsCallExpression =
605
636
  ts.Node.isNode(originalSource) && originalSource.isKind(ts.SyntaxKind.CallExpression)
637
+
638
+ const tableName = record.get('name').asString().getValue()
639
+ const isBootstrapDbObject = record.get('_bootstrap').ifBoolean()?.getValue() === true
640
+ const nonBootstrapColumns = columns.filter(
641
+ (col) => col.get('_bootstrap').ifBoolean()?.getValue() !== true
642
+ )
643
+
644
+ // Write as augmentation if we have sys_db_object from bootstrap and columns from elsewhere
645
+ const isAugmentation = isBootstrapDbObject && nonBootstrapColumns.length > 0
646
+ if (isAugmentation) {
647
+ const augmentsExpression = new CallExpressionShape({
648
+ source: record,
649
+ callee: 'Table',
650
+ exportName: tableName,
651
+ args: [
652
+ record.transform(({ $ }) => ({
653
+ augments: $.val(tableName),
654
+ schema: $.val(schema),
655
+ })),
656
+ ],
657
+ })
658
+ registerBootstrappedTable(augmentsExpression, schema, compiler)
659
+ return {
660
+ success: true,
661
+ value: writeAsCallExpression
662
+ ? augmentsExpression
663
+ : new VariableStatementShape({
664
+ source: record,
665
+ isExported: true,
666
+ variableName: new IdentifierShape({
667
+ source: record,
668
+ name: tableName,
669
+ }),
670
+ initializer: augmentsExpression,
671
+ }),
672
+ }
673
+ }
674
+
606
675
  const callExpression = new CallExpressionShape({
607
676
  source: record,
608
677
  callee: 'Table',
@@ -693,18 +762,24 @@ export const TablePlugin = Plugin.create({
693
762
  ),
694
763
  callerAccess: $.from('caller_access')
695
764
  .map((callerAccess) => {
765
+ // An empty <caller_access/> element arrives as '', not 0 — guard before
766
+ // '' and 0 both mean "none" on the platform — omit from generated Fluent.
767
+ if (callerAccess.isString() && callerAccess.getValue() === '') {
768
+ return ''
769
+ }
696
770
  if (callerAccess.isNumber()) {
697
- return callerAccessLevels[callerAccess.getValue()]
771
+ const value = callerAccess.getValue()
772
+ return value === 0 ? '' : callerAccessLevels[value]
698
773
  }
699
774
  if (callerAccess.isString()) {
700
- const callerAccessString = callerAccess.getValue()
701
- if (!isNaN(Number(callerAccessString))) {
702
- return callerAccessLevels[Number(callerAccessString)]
775
+ const parsed = Number(callerAccess.getValue())
776
+ if (!isNaN(parsed)) {
777
+ return parsed === 0 ? '' : callerAccessLevels[parsed]
703
778
  }
704
779
  }
705
- return 'none'
780
+ return ''
706
781
  })
707
- .def('none'),
782
+ .def(''),
708
783
  display: displayColumn ? $.val(displayColumn) : undefined,
709
784
  extends: $.from('super_class').def(''),
710
785
  extensible: $.from('is_extendable').toBoolean().def(false),
@@ -725,7 +800,7 @@ export const TablePlugin = Plugin.create({
725
800
  ).def([]),
726
801
  label: $.map(
727
802
  (label) =>
728
- label.ifString() ??
803
+ label.ifString()?.ifNotEmpty() ??
729
804
  (tableDocumentation.length &&
730
805
  !isDefaultDocumentation('', tableDocumentation, config.defaultLanguage)
731
806
  ? tableDocumentation.map((doc) =>
@@ -782,12 +857,24 @@ export const TablePlugin = Plugin.create({
782
857
  name: $,
783
858
  readOnly: $.val(collectionRecord?.get('read_only')).toBoolean().def(false),
784
859
  schema: $.val(schema),
860
+ // create_access_controls and user_role only ever arrive here from the
861
+ // sys_db_object record. The platform's bootstrap XML pipeline does not
862
+ // touch either field (no references in /glide/db/bootstrap/xml/ or
863
+ // TableDescriptorProvider.createTableLevelMetaData), and the SDK's own
864
+ // bootstrap output is always paired with sys_db_object_<id>.xml that
865
+ // carries the record values. So the bootstrap-only string path through
866
+ // tableDefToRecordProperties is unreachable for these two fields.
867
+ createAccessControls: $.from('create_access_controls').toBoolean().def(false),
868
+ userRole: $.from('user_role')
869
+ .map((v) => v.ifRecordId()?.getPrimaryKey())
870
+ .def(''),
785
871
  scriptableTable: $.from('scriptable_table').toBoolean().def(false),
786
872
  textIndex: $.val(collectionRecord?.get('text_index')).toBoolean().def(false),
787
873
  }))
788
874
  .withAliasedKeys(tableAliases),
789
875
  ],
790
876
  })
877
+ registerBootstrappedTable(callExpression, schema, compiler)
791
878
  return {
792
879
  success: true,
793
880
  value: writeAsCallExpression
@@ -804,12 +891,13 @@ export const TablePlugin = Plugin.create({
804
891
  }
805
892
  },
806
893
  async toFile(record, { descendants, config, transform }) {
807
- if (config.tableOutputFormat !== 'bootstrap' || config.type === 'configuration' || record.isDeleted()) {
808
- // Defer to record plugin
894
+ if (record.isDeleted()) {
809
895
  return { success: false }
810
896
  }
811
897
 
812
- const tableName = record.get('name').asString().getValue()
898
+ const augmentsValue = record.get('augments').ifString()?.getValue()
899
+ const isAugmentation = augmentsValue !== undefined
900
+ const tableName = augmentsValue ?? record.get('name').asString().getValue()
813
901
  const columns = descendants.query('sys_dictionary')
814
902
  const choices = descendants.query('sys_choice')
815
903
  const indexes = descendants.query('sys_index')
@@ -817,6 +905,71 @@ export const TablePlugin = Plugin.create({
817
905
  const licensing = descendants.query('ua_table_licensing_config')
818
906
  const autoNumber = descendants.query('sys_number')
819
907
  const overrides = descendants.query('sys_dictionary_override')
908
+
909
+ const [
910
+ documentationFiles,
911
+ licensingFiles,
912
+ autoNumberFiles,
913
+ overrideFiles,
914
+ sysDictionaryFiles,
915
+ sysDbObjectFiles,
916
+ ] = await Promise.all([
917
+ generateRecordXml(
918
+ documentation.filter(
919
+ (doc) =>
920
+ !isDefaultDocumentation(
921
+ doc.get('element')?.toString().getValue(),
922
+ [doc],
923
+ config.defaultLanguage
924
+ )
925
+ ),
926
+ config,
927
+ transform
928
+ ),
929
+ generateRecordXml(
930
+ licensing.filter((l) => !isDefaultLicenseConfig(tableName, l)),
931
+ config,
932
+ transform
933
+ ),
934
+ generateRecordXml(autoNumber, config, transform),
935
+ generateRecordXml(overrides, config, transform),
936
+ generateRecordXml(columns, config, transform, ['_bootstrap']),
937
+ isAugmentation
938
+ ? Promise.resolve([])
939
+ : generateRecordXml([record], config, transform, ['augments', '_bootstrap']),
940
+ ])
941
+
942
+ if (config.type === 'configuration') {
943
+ // No bootstrap XML for configuration projects, just write independent record XML
944
+ const choiceSets = descendants.query('sys_choice_set')
945
+ const [choiceSetFiles, indexFiles] = await Promise.all([
946
+ Promise.all(
947
+ choiceSets.map((cs) => {
948
+ const csChoices = choices.filter(
949
+ (c) =>
950
+ c.get('name').toString().getValue() === cs.get('name').toString().getValue() &&
951
+ c.get('element').toString().getValue() ===
952
+ cs.get('element').toString().getValue()
953
+ )
954
+ return generateChoiceSetFile(cs, csChoices, config, transform)
955
+ })
956
+ ),
957
+ generateRecordXml(indexes, config, transform),
958
+ ])
959
+ return {
960
+ success: true,
961
+ value: [
962
+ ...sysDictionaryFiles,
963
+ ...choiceSetFiles,
964
+ ...indexFiles,
965
+ ...sysDbObjectFiles,
966
+ ...documentationFiles,
967
+ ...licensingFiles,
968
+ ...autoNumberFiles,
969
+ ...overrideFiles,
970
+ ],
971
+ }
972
+ }
820
973
  let displayColumn: string | undefined
821
974
  let collectionRecord: Record | undefined
822
975
  const elements: XMLBuilder[] = []
@@ -859,7 +1012,10 @@ export const TablePlugin = Plugin.create({
859
1012
  ['read_only_option', column.get('read_only_option').ifString()?.getValue()],
860
1013
  ['reference_cascade_rule', column.get('reference_cascade_rule').ifString()?.getValue()],
861
1014
  ['calculation', column.get('calculation').ifString()?.getValue()],
862
- ['choice', column.get('choice').ifNumber()?.getValue().toString()],
1015
+ // Bootstrap convention: emit `choice="0"` for the platform default ("none").
1016
+ // The component (install) path uses an empty `<choice/>` element instead, but
1017
+ // bootstrap files conventionally include the literal "0" attribute.
1018
+ ['choice', (column.get('choice').ifNumber()?.getValue() ?? 0).toString()],
863
1019
  ['choice_table', column.get('choice_table').ifString()?.getValue()],
864
1020
  ['choice_field', column.get('choice_field').ifString()?.getValue()],
865
1021
  ['display', displayValue?.toString()],
@@ -890,6 +1046,23 @@ export const TablePlugin = Plugin.create({
890
1046
  ['hint', column.get('hint').ifString()?.getValue()],
891
1047
  ['help', column.get('help').ifString()?.getValue()],
892
1048
  ['virtual', column.get('virtual').ifBoolean()?.getValue().toString()],
1049
+ ['formula', column.get('formula').ifString()?.getValue() || undefined],
1050
+ [
1051
+ 'virtual_type',
1052
+ column.get('virtual_type').ifString()?.getValue() === 'script'
1053
+ ? undefined
1054
+ : column.get('virtual_type').ifString()?.getValue(),
1055
+ ],
1056
+ [
1057
+ 'use_reference_qualifier',
1058
+ column.get('use_reference_qualifier').ifString()?.getValue() === 'simple'
1059
+ ? undefined
1060
+ : column.get('use_reference_qualifier').ifString()?.getValue(),
1061
+ ],
1062
+ [
1063
+ 'dynamic_ref_qual',
1064
+ column.get('dynamic_ref_qual').ifDefined()?.toString().getValue() || undefined,
1065
+ ],
893
1066
  ['widget', column.get('widget').ifString()?.getValue()],
894
1067
  ['reference_qual', column.get('reference_qual').ifString()?.getValue()],
895
1068
  ['reference_qual_condition', column.get('reference_qual_condition').ifString()?.getValue()],
@@ -958,7 +1131,9 @@ export const TablePlugin = Plugin.create({
958
1131
  ['audit', collectionRecord?.get('audit').ifBoolean()?.getValue().toString()],
959
1132
  ['display', displayColumn],
960
1133
  ['access', record.get('access').ifString()?.getValue()],
961
- ['caller_access', record.get('caller_access').ifNumber()?.getValue().toString()],
1134
+ // Bootstrap convention: emit `caller_access="0"` for the platform default ("none").
1135
+ // The component (install) path uses an empty `<caller_access/>` element instead.
1136
+ ['caller_access', (record.get('caller_access').ifNumber()?.getValue() ?? 0).toString()],
962
1137
  ['ws_access', record.get('ws_access').ifBoolean()?.getValue().toString()],
963
1138
  ['read_access', record.get('read_access').ifBoolean()?.getValue().toString()],
964
1139
  ['alter_access', record.get('alter_access').ifBoolean()?.getValue().toString()],
@@ -967,6 +1142,8 @@ export const TablePlugin = Plugin.create({
967
1142
  ['delete_access', record.get('delete_access').ifBoolean()?.getValue().toString()],
968
1143
  ['actions_access', record.get('actions_access').ifBoolean()?.getValue().toString()],
969
1144
  ['client_scripts_access', record.get('client_scripts_access').ifBoolean()?.getValue().toString()],
1145
+ // create_access_controls and user_role are omitted: the platform's bootstrap
1146
+ // pipeline doesn't read them. They're carried by the paired sys_db_object_<id>.xml.
970
1147
  ['scriptable_table', record.get('scriptable_table').ifBoolean()?.getValue().toString()],
971
1148
  ['attributes', collectionRecord?.get('attributes').ifString()?.getValue()],
972
1149
  ]
@@ -982,28 +1159,6 @@ export const TablePlugin = Plugin.create({
982
1159
  ]
983
1160
  ).end({ prettyPrint: true })
984
1161
 
985
- const documentationFiles = await generateRecordXml(
986
- documentation.filter(
987
- (doc) =>
988
- !isDefaultDocumentation(
989
- doc.get('element')?.toString().getValue(),
990
- [doc],
991
- config.defaultLanguage
992
- )
993
- ),
994
- config,
995
- transform
996
- )
997
-
998
- const licensingFiles = await generateRecordXml(
999
- licensing.filter((licensing) => !isDefaultLicenseConfig(tableName, licensing)),
1000
- config,
1001
- transform
1002
- )
1003
-
1004
- const autoNumberFiles = await generateRecordXml(autoNumber, config, transform)
1005
- const overrideFiles = await generateRecordXml(overrides, config, transform)
1006
-
1007
1162
  return {
1008
1163
  success: true,
1009
1164
  value: [
@@ -1017,6 +1172,8 @@ export const TablePlugin = Plugin.create({
1017
1172
  ...licensingFiles,
1018
1173
  ...autoNumberFiles,
1019
1174
  ...overrideFiles,
1175
+ ...sysDictionaryFiles,
1176
+ ...sysDbObjectFiles,
1020
1177
  ],
1021
1178
  }
1022
1179
  },
@@ -1068,7 +1225,9 @@ export const TablePlugin = Plugin.create({
1068
1225
  const table = callExpression.getArgument(0).asObject().withAliasedKeys(tableAliases)
1069
1226
  generateDeprecatedDiagnostics(table, diagnostics)
1070
1227
  const relatedRecords: Record[] = []
1071
- const tableName = table.get('name').asString()
1228
+ const augments = table.get('augments').ifString()
1229
+ const isAugmentation = augments !== undefined
1230
+ const tableName = isAugmentation ? augments! : table.get('name').asString()
1072
1231
  if (!tableName.getValue().match(tableNameRegex)) {
1073
1232
  diagnostics.error(
1074
1233
  table.get('name'),
@@ -1078,16 +1237,28 @@ export const TablePlugin = Plugin.create({
1078
1237
 
1079
1238
  let ignoreColumnNameCheck = false
1080
1239
  const scopeName = config.scope
1081
- const scopeRegex = new RegExp(`^${scopeName}_`)
1082
- const globalRegex = /^u_/
1083
- const tableNameMatch = tableName.getValue().match(scopeRegex)
1084
- if (!tableNameMatch && !isSNScope(scopeName) && scopeName !== 'global') {
1240
+ const scopePrefix = scopeName === 'global' ? 'u_' : `${scopeName}_`
1241
+ const prefixRegex = new RegExp(`^${scopePrefix}`)
1242
+ const tableNameMatch = tableName.getValue().match(prefixRegex)
1243
+ if (isAugmentation) {
1244
+ if (tableNameMatch && scopeName !== 'global') {
1245
+ const nameNode = tableName.getOriginalNode()
1246
+ if (!nameNode?.getParentIfKind(ts.SyntaxKind.AsExpression)) {
1247
+ diagnostics.error(
1248
+ table.get('augments'),
1249
+ `'augments' flag set on in-scope table '${tableName.getValue()}'`
1250
+ )
1251
+ } else {
1252
+ ignoreColumnNameCheck = true
1253
+ }
1254
+ }
1255
+ } else if (!tableNameMatch && !isSNScope(scopeName) && scopeName !== 'global') {
1085
1256
  const nameNode = tableName.getOriginalNode()
1086
1257
  if (nameNode && !nameNode.getParentIfKind(ts.SyntaxKind.AsExpression)) {
1087
1258
  // 'sn' and 'now' scoped apps ignore this validation
1088
1259
  diagnostics.error(
1089
1260
  table.get('name'),
1090
- `'name' property should start with scope prefix '${scopeName}_'`
1261
+ `'name' property should start with scope prefix '${scopePrefix}'`
1091
1262
  )
1092
1263
  } else {
1093
1264
  ignoreColumnNameCheck = true
@@ -1106,13 +1277,11 @@ export const TablePlugin = Plugin.create({
1106
1277
  )
1107
1278
  }
1108
1279
 
1109
- const globalTableNameMatch = tableName.getValue().match(globalRegex)
1110
1280
  let anyNonPrefixedGlobalColumn = false
1111
-
1112
- if (scopeName === 'global' && !globalTableNameMatch) {
1281
+ if (!isAugmentation && scopeName === 'global' && !tableNameMatch) {
1113
1282
  const schema = table.get('schema').asObject()
1114
1283
  for (const [name, _] of schema.entries()) {
1115
- if (!name.match(globalRegex)) {
1284
+ if (!name.match(prefixRegex)) {
1116
1285
  anyNonPrefixedGlobalColumn = true
1117
1286
  break
1118
1287
  }
@@ -1124,7 +1293,7 @@ export const TablePlugin = Plugin.create({
1124
1293
  `Global table 'name' property should start with custom prefix 'u_'`
1125
1294
  )
1126
1295
  }
1127
- } else if (scopeName === 'global') {
1296
+ } else if (scopeName === 'global' && !isAugmentation) {
1128
1297
  // Global table starts with custom prefix `u_`, allow any column name prefix
1129
1298
  ignoreColumnNameCheck = true
1130
1299
  }
@@ -1217,22 +1386,30 @@ export const TablePlugin = Plugin.create({
1217
1386
  relatedRecords.push(overrideRecord)
1218
1387
  } else {
1219
1388
  // Handle regular column - create sys_dictionary record
1220
- if (
1389
+ if (isAugmentation && !tableNameMatch) {
1390
+ if (!isSNScope(scopeName) && !name.match(prefixRegex)) {
1391
+ diagnostics.error(
1392
+ column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ??
1393
+ column,
1394
+ `Column name '${name}' must be prefixed with '${scopePrefix}' when augmenting a table`
1395
+ )
1396
+ }
1397
+ } else if (
1221
1398
  !ignoreColumnNameCheck &&
1222
1399
  !tableNameMatch &&
1223
1400
  !isSNScope(scopeName) &&
1224
1401
  scopeName !== 'global' &&
1225
- !name.match(scopeRegex)
1402
+ !name.match(prefixRegex)
1226
1403
  ) {
1227
1404
  // 'sn' and 'now' scoped apps ignore this validation
1228
1405
  diagnostics.error(
1229
1406
  column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ?? column,
1230
- `Column name should be prefixed with scope '${scopeName}_' if table name does not contain prefix`
1407
+ `Column name should be prefixed with scope '${scopePrefix}' if table name does not contain prefix`
1231
1408
  )
1232
- } else if (scopeName === 'global' && !globalTableNameMatch && !name.match(globalRegex)) {
1409
+ } else if (scopeName === 'global' && !tableNameMatch && !name.match(prefixRegex)) {
1233
1410
  diagnostics.error(
1234
1411
  column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ?? column,
1235
- `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`
1412
+ `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`
1236
1413
  )
1237
1414
  }
1238
1415
  const display = table.get('display').ifString()?.getValue() === name
@@ -1409,29 +1586,32 @@ export const TablePlugin = Plugin.create({
1409
1586
  }
1410
1587
 
1411
1588
  // sys_dictionary (collection)
1412
- relatedRecords.push(
1413
- await factory.createRecord({
1414
- source: statement ?? callExpression,
1415
- table: 'sys_dictionary',
1416
- properties: table.transform(({ $ }) => ({
1417
- name: $,
1418
- element: $.val(undefined),
1419
- internal_type: $.def('collection'),
1420
- attributes: $.map((attributes) => {
1421
- if (!attributes.isObject()) {
1422
- return undefined
1423
- }
1424
- const attributesObj = attributes.asObject().getValue()
1425
- return Object.entries(attributesObj)
1426
- .map(([key, value]) => `${key}=${value}`)
1427
- .join(',')
1428
- }).def(''),
1429
- audit: $.def(false),
1430
- read_only: $.from('readOnly').def(false),
1431
- text_index: $.from('textIndex').def(false),
1432
- })),
1433
- })
1434
- )
1589
+ if (!isAugmentation) {
1590
+ relatedRecords.push(
1591
+ await factory.createRecord({
1592
+ source: statement ?? callExpression,
1593
+ table: 'sys_dictionary',
1594
+ properties: table.transform(({ $ }) => ({
1595
+ name: $,
1596
+ element: $.val(undefined),
1597
+ internal_type: $.def('collection'),
1598
+ active: $.val(true),
1599
+ attributes: $.map((attributes) => {
1600
+ if (!attributes.isObject()) {
1601
+ return undefined
1602
+ }
1603
+ const attributesObj = attributes.asObject().getValue()
1604
+ return Object.entries(attributesObj)
1605
+ .map(([key, value]) => `${key}=${value}`)
1606
+ .join(',')
1607
+ }).def(''),
1608
+ audit: $.def(false),
1609
+ read_only: $.from('readOnly').def(false),
1610
+ text_index: $.from('textIndex').def(false),
1611
+ })),
1612
+ })
1613
+ )
1614
+ }
1435
1615
 
1436
1616
  const hasAction = (actionName: string, actions: Shape): boolean | undefined => {
1437
1617
  return actions
@@ -1449,6 +1629,17 @@ export const TablePlugin = Plugin.create({
1449
1629
  })
1450
1630
  : ext.ifDefined()?.toRecordId()
1451
1631
 
1632
+ const userRoleField = table.get('userRole')
1633
+ const userRoleReference = userRoleField.isString()
1634
+ ? userRoleField.getValue() === ''
1635
+ ? undefined
1636
+ : await factory.createReference({
1637
+ source: userRoleField,
1638
+ table: 'sys_user_role',
1639
+ keys: { name: userRoleField },
1640
+ })
1641
+ : userRoleField.ifDefined()?.toRecordId()
1642
+
1452
1643
  // sys_db_object
1453
1644
  const tableRecord = await factory.createRecord({
1454
1645
  source: statement ?? callExpression,
@@ -1460,11 +1651,15 @@ export const TablePlugin = Plugin.create({
1460
1651
  ws_access: $.from('allowWebServiceAccess').toBoolean().def(false),
1461
1652
  number_ref: $.val(numberRef),
1462
1653
  access: $.from('accessibleFrom').def('public'),
1463
- caller_access: $.from('callerAccess').map((callerAccess) =>
1464
- callerAccessLevels.indexOf(
1465
- (callerAccess.ifString()?.getValue() ?? 'none') as 'none' | 'tracking' | 'restricted'
1466
- )
1467
- ),
1654
+ caller_access: $.from('callerAccess').map((callerAccess) => {
1655
+ const value = callerAccess.ifString()?.getValue()
1656
+ if (!value) {
1657
+ return undefined
1658
+ }
1659
+ const index = callerAccessLevels.indexOf(value as 'none' | 'tracking' | 'restricted')
1660
+ // index 0 ('none') is the platform default — emit empty <caller_access/>, matching stock.
1661
+ return index > 0 ? index : undefined
1662
+ }),
1468
1663
  super_class: $.val(parentReference),
1469
1664
  read_access: $.from('actions')
1470
1665
  .map((actions) => hasAction('read', actions))
@@ -1481,8 +1676,14 @@ export const TablePlugin = Plugin.create({
1481
1676
  is_extendable: $.from('extensible').toBoolean().def(false),
1482
1677
  label: $.map((label) => getLabelForDefaultLanguage(label, config.defaultLanguage)),
1483
1678
  live_feed_enabled: $.from('liveFeed').toBoolean().def(false),
1484
- name: $,
1679
+ name: $.from('name', 'augments').map(
1680
+ (nameVal, augmentsVal) => augmentsVal.ifString() ?? nameVal
1681
+ ),
1682
+ create_access_controls: $.from('createAccessControls').toBoolean().def(false),
1683
+ user_role: $.val(userRoleReference),
1485
1684
  scriptable_table: $.from('scriptableTable').toBoolean().def(false),
1685
+ // Controls toFile output, not written to record XML
1686
+ augments: $.from('augments'),
1486
1687
  })),
1487
1688
  })
1488
1689
 
@@ -1569,6 +1770,18 @@ function parseTableBootstrapXml(xml: unknown): TableDefinition | null {
1569
1770
  useDynamicDefault: column['@_use_dynamic_default'],
1570
1771
  reference: column['@_reference'],
1571
1772
  isVirtual: column['@_virtual'],
1773
+ formula: column['@_formula'],
1774
+ virtualType:
1775
+ column['@_virtual_type'] === 'script' || column['@_virtual_type'] === 'formula'
1776
+ ? column['@_virtual_type']
1777
+ : undefined,
1778
+ useReferenceQualifier:
1779
+ column['@_use_reference_qualifier'] === 'simple' ||
1780
+ column['@_use_reference_qualifier'] === 'dynamic' ||
1781
+ column['@_use_reference_qualifier'] === 'advanced'
1782
+ ? column['@_use_reference_qualifier']
1783
+ : undefined,
1784
+ dynamicRefQual: column['@_dynamic_ref_qual'],
1572
1785
  calculation: column['@_calculation'],
1573
1786
  choiceField: column['@_choice_field'],
1574
1787
  functionDefinition: column['@_function_definition'],
@@ -1642,6 +1855,8 @@ function parseTableBootstrapXml(xml: unknown): TableDefinition | null {
1642
1855
  actionsAccess: table['@_actions_access'],
1643
1856
  readAccess: table['@_read_access'],
1644
1857
  isExtendable: table['@_is_extendable'],
1858
+ createAccessControls: table['@_create_access_controls'],
1859
+ userRole: table['@_user_role'],
1645
1860
  scriptableTable: table['@_scriptable_table'],
1646
1861
  attributes: table['@_attributes'],
1647
1862
  }
@@ -1663,6 +1878,8 @@ function tableDefToRecordProperties(
1663
1878
  label: tableDef.label,
1664
1879
  super_class: tableDef.extends,
1665
1880
  is_extendable: tableDef.isExtendable,
1881
+ create_access_controls: tableDef.createAccessControls,
1882
+ user_role: tableDef.userRole,
1666
1883
  scriptable_table: tableDef.scriptableTable,
1667
1884
  client_scripts_access: tableDef.clientScriptsAccess,
1668
1885
  ws_access: tableDef.wsAccess,
@@ -1712,6 +1929,10 @@ function tableDefToRecordProperties(
1712
1929
  use_dynamic_default: column.useDynamicDefault,
1713
1930
  reference: column.reference,
1714
1931
  virtual: column.isVirtual,
1932
+ formula: column.formula,
1933
+ virtual_type: column.virtualType,
1934
+ use_reference_qualifier: column.useReferenceQualifier,
1935
+ dynamic_ref_qual: column.dynamicRefQual,
1715
1936
  default: column.defaultValue,
1716
1937
  calculation: column.calculation,
1717
1938
  choice_field: column.choiceField,
@@ -1794,6 +2015,10 @@ function tableDefToRecordProperties(
1794
2015
  use_dynamic_default: undefined,
1795
2016
  reference: undefined,
1796
2017
  virtual: undefined,
2018
+ formula: undefined,
2019
+ virtual_type: undefined,
2020
+ use_reference_qualifier: undefined,
2021
+ dynamic_ref_qual: undefined,
1797
2022
  default: undefined,
1798
2023
  calculation: undefined,
1799
2024
  choice_field: undefined,
@@ -1852,7 +2077,7 @@ function addTableToGlobalGeneratedFile(tableArg: ObjectShape, sourceFilePath: st
1852
2077
  >,
1853
2078
  }
1854
2079
  const generatedTableFile: ts.SourceFile | undefined = compiler.getGeneratedTableFile()
1855
- const tableName = tableArg.get('name').asString().getValue()
2080
+ const tableName = tableArg.get('augments').ifString()?.getValue() ?? tableArg.get('name').asString().getValue()
1856
2081
  if (!(tableName.trim().length > 0 && tableName.match(tableNameRegex))) {
1857
2082
  return
1858
2083
  }
@@ -1910,6 +2135,58 @@ function addTableToGlobalGeneratedFile(tableArg: ObjectShape, sourceFilePath: st
1910
2135
  }
1911
2136
  }
1912
2137
 
2138
+ /**
2139
+ * Records a bootstrap-derived Table() shape into $$GENERATED$$_bootstrapped_tables.ts so that other plugins (e.g. RecordPlugin)
2140
+ * can resolve Data<T> column types via TypeScript during the same transform pass — no sys_dictionary query needed.
2141
+ *
2142
+ * The `declare global` augmentation lives inside the bootstrapped file (referencing its LOCAL `typeof tableName` export) rather
2143
+ * than the common table file. That avoids the duplicate-import error when a pre-existing Fluent Table() of the same name was
2144
+ * already wired into the common file, and TypeScript still merges the two TableSchemas interface declarations so both
2145
+ * contribute their columns to the resolved Data<T>.
2146
+ */
2147
+ function registerBootstrappedTable(
2148
+ tableCall: CallExpressionShape,
2149
+ schema: { [key: string]: CallExpressionShape },
2150
+ compiler: Compiler
2151
+ ): void {
2152
+ const file = compiler.getGeneratedBootstrappedTablesFile()
2153
+ if (!file || Object.keys(schema).length === 0) {
2154
+ return
2155
+ }
2156
+
2157
+ const tableArg = tableCall.getArgument(0).asObject()
2158
+ const tableName = tableArg.get('augments').ifString()?.getValue() ?? tableArg.get('name').asString().getValue()
2159
+
2160
+ const tableSchemas = file.getModule('global')?.getModule('Now')?.getModule('Internal')?.getModule('TableSchemas')
2161
+ const tables = file.getModule('global')?.getModule('Now')?.getModule('Internal')?.getInterface('Tables')
2162
+ if (!tableSchemas || !tables) {
2163
+ return
2164
+ }
2165
+
2166
+ const coreImport = file.getImportDeclaration('@servicenow/sdk/core')
2167
+ const existingImports = new Set(coreImport?.getNamedImports().map((n) => n.getName()))
2168
+ for (const callee of new Set(Object.values(schema).map((s) => s.getCallee()))) {
2169
+ if (!existingImports.has(callee)) {
2170
+ coreImport?.addNamedImport(callee)
2171
+ }
2172
+ }
2173
+
2174
+ // Replace any existing export (re-transform of same XML), then write the shape's rendered code.
2175
+ file.getVariableDeclaration(tableName)?.getVariableStatement()?.remove()
2176
+ file.addVariableStatement({
2177
+ isExported: true,
2178
+ declarationKind: ts.VariableDeclarationKind.Const,
2179
+ declarations: [{ name: tableName, initializer: tableCall.getCode() }],
2180
+ })
2181
+
2182
+ if (!tableSchemas.getInterface(tableName)) {
2183
+ tableSchemas.addInterface({ name: tableName, extends: [`Helper<typeof ${tableName}>`] })
2184
+ }
2185
+ if (!tables.getProperty(tableName)) {
2186
+ tables.addProperty({ name: tableName, type: `Table<TableSchemas.${tableName}>` })
2187
+ }
2188
+ }
2189
+
1913
2190
  function filterUndefinedProperties<T extends object>(obj: T): T {
1914
2191
  return Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== undefined)) as T
1915
2192
  }
@@ -1933,7 +2210,8 @@ function createElement(name: string, attributes: [string, string][] = [], childr
1933
2210
  async function generateRecordXml(
1934
2211
  records: Record[],
1935
2212
  config: { scope: string; scopeId: string },
1936
- transform: Transform
2213
+ transform: Transform,
2214
+ excludeFields: string[] = []
1937
2215
  ): Promise<OutputFile[]> {
1938
2216
  const files: OutputFile[] = []
1939
2217
  for (const record of records) {
@@ -1944,6 +2222,7 @@ async function generateRecordXml(
1944
2222
  record
1945
2223
  .entries()
1946
2224
  .sort(([a], [b]) => a.localeCompare(b))
2225
+ .filter(([prop]) => excludeFields.length === 0 || !excludeFields.includes(prop))
1947
2226
  .forEach(([prop, shape]) => builder.field(prop, shape))
1948
2227
 
1949
2228
  files.push({