@servicenow/sdk-build-plugins 4.5.0 → 4.6.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.
- package/dist/column-plugin.js +3 -7
- package/dist/column-plugin.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-diagnostics.js +5 -5
- package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
- package/dist/flow/plugins/flow-action-definition-plugin.js +1229 -54
- package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-data-pill-plugin.js +5 -2
- package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-definition-plugin.js +16 -42
- package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-diagnostics-plugin.d.ts +2 -2
- package/dist/flow/plugins/flow-diagnostics-plugin.js +2 -2
- package/dist/flow/plugins/flow-instance-plugin.js +68 -22
- package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/step-definition-plugin.js +2 -1
- package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/step-instance-plugin.d.ts +9 -1
- package/dist/flow/plugins/step-instance-plugin.js +649 -136
- package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/wfa-datapill-plugin.js +20 -5
- package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
- package/dist/flow/post-install.js +1 -0
- package/dist/flow/post-install.js.map +1 -1
- package/dist/flow/utils/complex-object-resolver.js +4 -1
- package/dist/flow/utils/complex-object-resolver.js.map +1 -1
- package/dist/flow/utils/complex-objects.js +1 -1
- package/dist/flow/utils/complex-objects.js.map +1 -1
- package/dist/flow/utils/flow-constants.d.ts +66 -2
- package/dist/flow/utils/flow-constants.js +402 -6
- package/dist/flow/utils/flow-constants.js.map +1 -1
- package/dist/flow/utils/flow-io-to-record.d.ts +1 -1
- package/dist/flow/utils/flow-io-to-record.js +37 -16
- package/dist/flow/utils/flow-io-to-record.js.map +1 -1
- package/dist/flow/utils/flow-shapes.js +4 -0
- package/dist/flow/utils/flow-shapes.js.map +1 -1
- package/dist/flow/utils/label-cache-parser.d.ts +9 -2
- package/dist/flow/utils/label-cache-parser.js +32 -4
- package/dist/flow/utils/label-cache-parser.js.map +1 -1
- package/dist/flow/utils/pill-shape-helpers.d.ts +15 -0
- package/dist/flow/utils/pill-shape-helpers.js +35 -0
- package/dist/flow/utils/pill-shape-helpers.js.map +1 -0
- package/dist/flow/utils/pill-string-parser.js +1 -0
- package/dist/flow/utils/pill-string-parser.js.map +1 -1
- package/dist/flow/utils/schema-to-flow-object.d.ts +6 -1
- package/dist/flow/utils/schema-to-flow-object.js +131 -15
- package/dist/flow/utils/schema-to-flow-object.js.map +1 -1
- package/dist/flow/utils/utils.d.ts +1 -0
- package/dist/flow/utils/utils.js +6 -1
- package/dist/flow/utils/utils.js.map +1 -1
- package/dist/form-plugin.js +7 -9
- package/dist/form-plugin.js.map +1 -1
- package/dist/inbound-email-action-plugin.d.ts +10 -0
- package/dist/inbound-email-action-plugin.js +128 -0
- package/dist/inbound-email-action-plugin.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/instance-scan-plugin.js +0 -5
- package/dist/instance-scan-plugin.js.map +1 -1
- package/dist/property-plugin.js +1 -1
- package/dist/property-plugin.js.map +1 -1
- package/dist/record-plugin.d.ts +7 -0
- package/dist/record-plugin.js +10 -2
- package/dist/record-plugin.js.map +1 -1
- package/dist/rest-api-plugin.js +8 -1
- package/dist/rest-api-plugin.js.map +1 -1
- package/dist/schedule-script/scheduled-script-plugin.js +8 -3
- package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
- package/dist/service-catalog/service-catalog-base.d.ts +18 -18
- package/dist/service-catalog/service-catalog-base.js +22 -22
- package/dist/service-catalog/service-catalog-base.js.map +1 -1
- package/dist/service-portal/header-footer-plugin.d.ts +2 -0
- package/dist/service-portal/header-footer-plugin.js +50 -0
- package/dist/service-portal/header-footer-plugin.js.map +1 -0
- package/dist/service-portal/menu-plugin.js +3 -22
- package/dist/service-portal/menu-plugin.js.map +1 -1
- package/dist/service-portal/page-plugin.js +3 -24
- package/dist/service-portal/page-plugin.js.map +1 -1
- package/dist/service-portal/page-route-map-plugin.d.ts +2 -0
- package/dist/service-portal/page-route-map-plugin.js +114 -0
- package/dist/service-portal/page-route-map-plugin.js.map +1 -0
- package/dist/service-portal/portal-plugin.js +21 -8
- package/dist/service-portal/portal-plugin.js.map +1 -1
- package/dist/service-portal/utils.d.ts +40 -2
- package/dist/service-portal/utils.js +283 -2
- package/dist/service-portal/utils.js.map +1 -1
- package/dist/service-portal/widget-plugin.js +9 -218
- package/dist/service-portal/widget-plugin.js.map +1 -1
- package/dist/static-content-plugin.js +4 -0
- package/dist/static-content-plugin.js.map +1 -1
- package/dist/table-plugin.js +190 -26
- package/dist/table-plugin.js.map +1 -1
- package/dist/ui-action-plugin.js +1 -4
- package/dist/ui-action-plugin.js.map +1 -1
- package/dist/ui-page-plugin.js +68 -13
- package/dist/ui-page-plugin.js.map +1 -1
- package/dist/view-plugin.js +8 -3
- package/dist/view-plugin.js.map +1 -1
- package/dist/workspace-plugin.js +39 -36
- package/dist/workspace-plugin.js.map +1 -1
- package/package.json +5 -4
- package/src/column-plugin.ts +3 -8
- package/src/flow/flow-logic/flow-logic-diagnostics.ts +5 -6
- package/src/flow/plugins/flow-action-definition-plugin.ts +1581 -61
- package/src/flow/plugins/flow-data-pill-plugin.ts +5 -2
- package/src/flow/plugins/flow-definition-plugin.ts +12 -47
- package/src/flow/plugins/flow-diagnostics-plugin.ts +2 -2
- package/src/flow/plugins/flow-instance-plugin.ts +98 -22
- package/src/flow/plugins/step-definition-plugin.ts +2 -1
- package/src/flow/plugins/step-instance-plugin.ts +772 -156
- package/src/flow/plugins/wfa-datapill-plugin.ts +25 -5
- package/src/flow/post-install.ts +1 -0
- package/src/flow/utils/complex-object-resolver.ts +4 -1
- package/src/flow/utils/complex-objects.ts +1 -1
- package/src/flow/utils/flow-constants.ts +421 -5
- package/src/flow/utils/flow-io-to-record.ts +43 -17
- package/src/flow/utils/flow-shapes.ts +4 -0
- package/src/flow/utils/label-cache-parser.ts +33 -4
- package/src/flow/utils/pill-shape-helpers.ts +42 -0
- package/src/flow/utils/pill-string-parser.ts +1 -0
- package/src/flow/utils/schema-to-flow-object.ts +183 -15
- package/src/flow/utils/utils.ts +12 -1
- package/src/form-plugin.ts +1 -3
- package/src/inbound-email-action-plugin.ts +145 -0
- package/src/index.ts +4 -0
- package/src/instance-scan-plugin.ts +0 -5
- package/src/property-plugin.ts +4 -1
- package/src/record-plugin.ts +14 -4
- package/src/rest-api-plugin.ts +7 -1
- package/src/schedule-script/scheduled-script-plugin.ts +14 -3
- package/src/service-catalog/service-catalog-base.ts +22 -22
- package/src/service-portal/header-footer-plugin.ts +57 -0
- package/src/service-portal/menu-plugin.ts +1 -23
- package/src/service-portal/page-plugin.ts +3 -28
- package/src/service-portal/page-route-map-plugin.ts +124 -0
- package/src/service-portal/portal-plugin.ts +33 -10
- package/src/service-portal/utils.ts +404 -3
- package/src/service-portal/widget-plugin.ts +14 -290
- package/src/static-content-plugin.ts +3 -0
- package/src/table-plugin.ts +226 -36
- package/src/ui-action-plugin.ts +1 -8
- package/src/ui-page-plugin.ts +76 -13
- package/src/view-plugin.ts +10 -4
- package/src/workspace-plugin.ts +43 -43
package/src/table-plugin.ts
CHANGED
|
@@ -321,6 +321,20 @@ type SysDocumentationProperties = {
|
|
|
321
321
|
help: string | undefined
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
+
type SysDictionaryOverrideProperties = {
|
|
325
|
+
name: string
|
|
326
|
+
element: string
|
|
327
|
+
base_table: string | undefined
|
|
328
|
+
default: string | undefined
|
|
329
|
+
calculation: string | undefined
|
|
330
|
+
reference_qual: string | undefined
|
|
331
|
+
read_only_option: string | undefined
|
|
332
|
+
dependent: string | undefined
|
|
333
|
+
mandatory: boolean | undefined
|
|
334
|
+
display: boolean | undefined
|
|
335
|
+
attributes: string | undefined
|
|
336
|
+
}
|
|
337
|
+
|
|
324
338
|
/**
|
|
325
339
|
* The access levels are mapped to the following values on the platform
|
|
326
340
|
*
|
|
@@ -390,7 +404,7 @@ export const TablePlugin = Plugin.create({
|
|
|
390
404
|
return { success: false }
|
|
391
405
|
}
|
|
392
406
|
|
|
393
|
-
const recordDefs = tableDefToRecordProperties(tableDef, config.
|
|
407
|
+
const recordDefs = tableDefToRecordProperties(tableDef, config.defaultLanguage)
|
|
394
408
|
const records: Record[] = []
|
|
395
409
|
for (const [key, table] of [
|
|
396
410
|
['sysDbObject', 'sys_db_object'],
|
|
@@ -398,6 +412,7 @@ export const TablePlugin = Plugin.create({
|
|
|
398
412
|
['sysChoice', 'sys_choice'],
|
|
399
413
|
['sysIndex', 'sys_index'],
|
|
400
414
|
['sysDocumentation', 'sys_documentation'],
|
|
415
|
+
['sysDictionaryOverride', 'sys_dictionary_override'],
|
|
401
416
|
] as const) {
|
|
402
417
|
for (const rec of [recordDefs[key]].flat()) {
|
|
403
418
|
records.push(
|
|
@@ -449,6 +464,10 @@ export const TablePlugin = Plugin.create({
|
|
|
449
464
|
},
|
|
450
465
|
},
|
|
451
466
|
},
|
|
467
|
+
sys_dictionary_override: {
|
|
468
|
+
via: { name: 'name' },
|
|
469
|
+
descendant: true,
|
|
470
|
+
},
|
|
452
471
|
ua_table_licensing_config: {
|
|
453
472
|
descendant: true,
|
|
454
473
|
via: { name: 'name' },
|
|
@@ -474,9 +493,12 @@ export const TablePlugin = Plugin.create({
|
|
|
474
493
|
const schema: { [key: string]: CallExpressionShape } = {}
|
|
475
494
|
let displayColumn: string | undefined
|
|
476
495
|
const columns = descendants.query('sys_dictionary')
|
|
496
|
+
const overrides = descendants.query('sys_dictionary_override')
|
|
477
497
|
let collectionRecord: Record
|
|
478
498
|
const choices = descendants.query('sys_choice')
|
|
479
499
|
const documentation = descendants.query('sys_documentation')
|
|
500
|
+
|
|
501
|
+
// Process regular columns
|
|
480
502
|
for (const column of columns) {
|
|
481
503
|
if (column.get('internal_type').getValue() === 'collection') {
|
|
482
504
|
// 'collection' sys_dictionary record only has table properties
|
|
@@ -489,13 +511,89 @@ export const TablePlugin = Plugin.create({
|
|
|
489
511
|
documentation: documentation.filter(
|
|
490
512
|
(d) => d.get('element').ifString()?.getValue() === columnName
|
|
491
513
|
),
|
|
492
|
-
tableDefaultLanguage: config.
|
|
514
|
+
tableDefaultLanguage: config.defaultLanguage,
|
|
493
515
|
})
|
|
494
516
|
if (column.get('display').ifDefined()?.toBoolean().getValue()) {
|
|
495
517
|
displayColumn = columnName
|
|
496
518
|
}
|
|
497
519
|
}
|
|
498
520
|
|
|
521
|
+
// Process dictionary overrides as OverrideColumn
|
|
522
|
+
for (const override of overrides) {
|
|
523
|
+
const columnName = override.get('element').asString().getValue()
|
|
524
|
+
schema[columnName] = new CallExpressionShape({
|
|
525
|
+
source: override,
|
|
526
|
+
callee: 'OverrideColumn',
|
|
527
|
+
args: [
|
|
528
|
+
override.transform(({ $ }) => ({
|
|
529
|
+
baseTable: $.from('base_table'),
|
|
530
|
+
default: $.from('default_value_override', 'default_value').map((flag, value) => {
|
|
531
|
+
return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
|
|
532
|
+
}),
|
|
533
|
+
calculation: $.from('calculation_override', 'calculation').map((flag, value) => {
|
|
534
|
+
return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
|
|
535
|
+
}),
|
|
536
|
+
referenceQualifier: $.from('reference_qual_override', 'reference_qual').map(
|
|
537
|
+
(flag, value) => {
|
|
538
|
+
return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
|
|
539
|
+
}
|
|
540
|
+
),
|
|
541
|
+
readOnlyOption: $.from('read_only_option_override', 'read_only_option').map(
|
|
542
|
+
(flag, value) => {
|
|
543
|
+
return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
|
|
544
|
+
}
|
|
545
|
+
),
|
|
546
|
+
dependent: $.from('dependent_override', 'dependent').map((flag, value) => {
|
|
547
|
+
return flag.toBoolean()?.getValue() ? value.ifString()?.getValue() : undefined
|
|
548
|
+
}),
|
|
549
|
+
mandatory: $.from('mandatory_override', 'mandatory').map((flag, value) => {
|
|
550
|
+
return flag.toBoolean()?.getValue() ? value.toBoolean()?.getValue() : undefined
|
|
551
|
+
}),
|
|
552
|
+
attributes: $.from('attributes_override', 'attributes').map((flag, attrs) => {
|
|
553
|
+
if (!flag.toBoolean()?.getValue() || !attrs.isString()) {
|
|
554
|
+
return undefined
|
|
555
|
+
}
|
|
556
|
+
const result: { [key: string]: string | number | boolean } = {}
|
|
557
|
+
attrs
|
|
558
|
+
.toString()
|
|
559
|
+
.getValue()
|
|
560
|
+
.split(',')
|
|
561
|
+
.forEach((attr) => {
|
|
562
|
+
if (attr === '') {
|
|
563
|
+
return
|
|
564
|
+
}
|
|
565
|
+
const [key, value] = attr.split('=').map((s) => s.trim())
|
|
566
|
+
if (!key || value === undefined) {
|
|
567
|
+
return
|
|
568
|
+
}
|
|
569
|
+
if (value === 'true') {
|
|
570
|
+
result[key] = true
|
|
571
|
+
} else if (value === 'false') {
|
|
572
|
+
result[key] = false
|
|
573
|
+
} else {
|
|
574
|
+
// Try to parse as number
|
|
575
|
+
const numValue = Number(value)
|
|
576
|
+
if (!isNaN(numValue) && value !== '') {
|
|
577
|
+
result[key] = numValue
|
|
578
|
+
} else {
|
|
579
|
+
result[key] = value
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
})
|
|
583
|
+
return result
|
|
584
|
+
}),
|
|
585
|
+
display: $.from('display_override').map((v) => {
|
|
586
|
+
if (!v.ifDefined()) {
|
|
587
|
+
return undefined
|
|
588
|
+
}
|
|
589
|
+
const boolValue = v.toBoolean().getValue()
|
|
590
|
+
return boolValue === true ? true : undefined
|
|
591
|
+
}),
|
|
592
|
+
})),
|
|
593
|
+
],
|
|
594
|
+
})
|
|
595
|
+
}
|
|
596
|
+
|
|
499
597
|
const tableDocumentation = documentation.filter((d) => !d.get('element').getValue())
|
|
500
598
|
const licensing = descendants.query('ua_table_licensing_config')
|
|
501
599
|
const autoNumber = descendants.query('sys_number')
|
|
@@ -629,14 +727,14 @@ export const TablePlugin = Plugin.create({
|
|
|
629
727
|
(label) =>
|
|
630
728
|
label.ifString() ??
|
|
631
729
|
(tableDocumentation.length &&
|
|
632
|
-
!isDefaultDocumentation('', tableDocumentation, config.
|
|
730
|
+
!isDefaultDocumentation('', tableDocumentation, config.defaultLanguage)
|
|
633
731
|
? tableDocumentation.map((doc) =>
|
|
634
732
|
doc
|
|
635
733
|
.transform(({ $ }) => ({
|
|
636
734
|
label: $.def(''),
|
|
637
735
|
help: $.def(''),
|
|
638
736
|
hint: $.def(''),
|
|
639
|
-
language: $.def(config.
|
|
737
|
+
language: $.def(config.defaultLanguage),
|
|
640
738
|
plural: $.def(''),
|
|
641
739
|
url: $.def(''),
|
|
642
740
|
urlTarget: $.from('url_target').def(''),
|
|
@@ -718,8 +816,9 @@ export const TablePlugin = Plugin.create({
|
|
|
718
816
|
const documentation = descendants.query('sys_documentation')
|
|
719
817
|
const licensing = descendants.query('ua_table_licensing_config')
|
|
720
818
|
const autoNumber = descendants.query('sys_number')
|
|
721
|
-
|
|
819
|
+
const overrides = descendants.query('sys_dictionary_override')
|
|
722
820
|
let displayColumn: string | undefined
|
|
821
|
+
let collectionRecord: Record | undefined
|
|
723
822
|
const elements: XMLBuilder[] = []
|
|
724
823
|
for (const column of columns) {
|
|
725
824
|
const displayValue = column.get('display').ifBoolean()?.getValue()
|
|
@@ -889,7 +988,7 @@ export const TablePlugin = Plugin.create({
|
|
|
889
988
|
!isDefaultDocumentation(
|
|
890
989
|
doc.get('element')?.toString().getValue(),
|
|
891
990
|
[doc],
|
|
892
|
-
config.
|
|
991
|
+
config.defaultLanguage
|
|
893
992
|
)
|
|
894
993
|
),
|
|
895
994
|
config,
|
|
@@ -903,6 +1002,7 @@ export const TablePlugin = Plugin.create({
|
|
|
903
1002
|
)
|
|
904
1003
|
|
|
905
1004
|
const autoNumberFiles = await generateRecordXml(autoNumber, config, transform)
|
|
1005
|
+
const overrideFiles = await generateRecordXml(overrides, config, transform)
|
|
906
1006
|
|
|
907
1007
|
return {
|
|
908
1008
|
success: true,
|
|
@@ -916,6 +1016,7 @@ export const TablePlugin = Plugin.create({
|
|
|
916
1016
|
...documentationFiles,
|
|
917
1017
|
...licensingFiles,
|
|
918
1018
|
...autoNumberFiles,
|
|
1019
|
+
...overrideFiles,
|
|
919
1020
|
],
|
|
920
1021
|
}
|
|
921
1022
|
},
|
|
@@ -927,6 +1028,13 @@ export const TablePlugin = Plugin.create({
|
|
|
927
1028
|
value: `sys_dictionary_${record.get('name').getValue()}_${record.get('element').getValue() || 'null'}`,
|
|
928
1029
|
}),
|
|
929
1030
|
},
|
|
1031
|
+
sys_dictionary_override: {
|
|
1032
|
+
coalesce: ['name', 'element'],
|
|
1033
|
+
getUpdateName: (record) => ({
|
|
1034
|
+
success: true,
|
|
1035
|
+
value: `sys_dictionary_override_${record.get('name').getValue()}_${record.get('element').getValue() || 'null'}`,
|
|
1036
|
+
}),
|
|
1037
|
+
},
|
|
930
1038
|
sys_documentation: {
|
|
931
1039
|
coalesce: ['name', 'element', 'language'],
|
|
932
1040
|
getUpdateName: (record) => ({
|
|
@@ -1021,7 +1129,7 @@ export const TablePlugin = Plugin.create({
|
|
|
1021
1129
|
ignoreColumnNameCheck = true
|
|
1022
1130
|
}
|
|
1023
1131
|
|
|
1024
|
-
// sys_dictionary
|
|
1132
|
+
// sys_dictionary and sys_dictionary_override
|
|
1025
1133
|
const schema = table.get('schema').asObject()
|
|
1026
1134
|
const columnIdsMap = new Map<string, string>()
|
|
1027
1135
|
for (const [name, column] of schema.entries()) {
|
|
@@ -1031,37 +1139,116 @@ export const TablePlugin = Plugin.create({
|
|
|
1031
1139
|
'Column name must only contain lowercase letters, numbers, and underscores'
|
|
1032
1140
|
)
|
|
1033
1141
|
}
|
|
1142
|
+
|
|
1143
|
+
// Check if this is an OverrideColumn
|
|
1034
1144
|
if (
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
!isSNScope(scopeName) &&
|
|
1038
|
-
scopeName !== 'global' &&
|
|
1039
|
-
!name.match(scopeRegex)
|
|
1145
|
+
column.is(CallExpressionShape) &&
|
|
1146
|
+
column.as(CallExpressionShape).getCallee() === 'OverrideColumn'
|
|
1040
1147
|
) {
|
|
1041
|
-
//
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1148
|
+
// This is an OverrideColumn() call - get the object from its first argument
|
|
1149
|
+
const columnObj = column.as(CallExpressionShape).getArgument(0).asObject()
|
|
1150
|
+
|
|
1151
|
+
// Handle OverrideColumn - create sys_dictionary_override record
|
|
1152
|
+
// Validate that the table extends another table
|
|
1153
|
+
const extendsTable = table.get('extends').ifString()?.getValue()
|
|
1154
|
+
if (!extendsTable) {
|
|
1155
|
+
diagnostics.error(
|
|
1156
|
+
column,
|
|
1157
|
+
`Cannot use OverrideColumn in table '${tableName.getValue()}' because it does not extend another table`
|
|
1158
|
+
)
|
|
1159
|
+
return { success: false }
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// Use baseTable if provided, otherwise default to extends
|
|
1163
|
+
const baseTableValue = columnObj.get('baseTable')
|
|
1164
|
+
let baseTable: string
|
|
1165
|
+
|
|
1166
|
+
if (baseTableValue.ifString()) {
|
|
1167
|
+
baseTable = baseTableValue.asString().getValue()
|
|
1168
|
+
} else {
|
|
1169
|
+
// Default to extends if baseTable not provided
|
|
1170
|
+
baseTable = extendsTable
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// Create sys_dictionary_override record
|
|
1174
|
+
const overrideRecord = await factory.createRecord({
|
|
1175
|
+
source: statement ?? callExpression,
|
|
1176
|
+
table: 'sys_dictionary_override',
|
|
1177
|
+
properties: columnObj.transform(({ $ }) => ({
|
|
1178
|
+
name: $.val(tableName),
|
|
1179
|
+
element: $.val(name),
|
|
1180
|
+
base_table: $.val(baseTable),
|
|
1181
|
+
default_value: $.from('default'),
|
|
1182
|
+
default_value_override: $.from('default').map((v) => !!v.ifDefined()),
|
|
1183
|
+
calculation: $.from('calculation'),
|
|
1184
|
+
calculation_override: $.from('calculation').map((v) => !!v.ifDefined()),
|
|
1185
|
+
reference_qual: $.from('referenceQualifier'),
|
|
1186
|
+
reference_qual_override: $.from('referenceQualifier').map((v) => !!v.ifDefined()),
|
|
1187
|
+
read_only_option: $.from('readOnlyOption'),
|
|
1188
|
+
read_only_option_override: $.from('readOnlyOption').map((v) => !!v.ifDefined()),
|
|
1189
|
+
read_only: $.from('readOnlyOption').map((readOnlyOption) => {
|
|
1190
|
+
// read_only should be true if readOnlyOption has a value
|
|
1191
|
+
return !!readOnlyOption.ifDefined()
|
|
1192
|
+
}),
|
|
1193
|
+
read_only_override: $.from('readOnlyOption').map((v) => !!v.ifDefined()),
|
|
1194
|
+
dependent: $.from('dependent'),
|
|
1195
|
+
dependent_override: $.from('dependent').map((v) => !!v.ifDefined()),
|
|
1196
|
+
mandatory: $.from('mandatory').toBoolean().def(false),
|
|
1197
|
+
mandatory_override: $.from('mandatory').map((v) => !!v.ifDefined()),
|
|
1198
|
+
attributes: $.from('attributes').map((attrs) => {
|
|
1199
|
+
if (!attrs.isObject()) {
|
|
1200
|
+
return
|
|
1201
|
+
}
|
|
1202
|
+
const attrsObj = attrs.asObject().getValue()
|
|
1203
|
+
return Object.entries(attrsObj)
|
|
1204
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
1205
|
+
.join(',')
|
|
1206
|
+
}),
|
|
1207
|
+
attributes_override: $.from('attributes').map((v) => !!v.ifDefined()),
|
|
1208
|
+
display_override: $.from('display').map((v) => {
|
|
1209
|
+
const boolShape = v.ifBoolean()
|
|
1210
|
+
if (!boolShape) {
|
|
1211
|
+
return false
|
|
1212
|
+
}
|
|
1213
|
+
return boolShape.getValue() === true
|
|
1214
|
+
}),
|
|
1215
|
+
})),
|
|
1216
|
+
})
|
|
1217
|
+
relatedRecords.push(overrideRecord)
|
|
1218
|
+
} else {
|
|
1219
|
+
// Handle regular column - create sys_dictionary record
|
|
1220
|
+
if (
|
|
1221
|
+
!ignoreColumnNameCheck &&
|
|
1222
|
+
!tableNameMatch &&
|
|
1223
|
+
!isSNScope(scopeName) &&
|
|
1224
|
+
scopeName !== 'global' &&
|
|
1225
|
+
!name.match(scopeRegex)
|
|
1226
|
+
) {
|
|
1227
|
+
// 'sn' and 'now' scoped apps ignore this validation
|
|
1228
|
+
diagnostics.error(
|
|
1229
|
+
column.getOriginalNode().getParentIfKind(ts.SyntaxKind.PropertyAssignment) ?? column,
|
|
1230
|
+
`Column name should be prefixed with scope '${scopeName}_' if table name does not contain prefix`
|
|
1231
|
+
)
|
|
1232
|
+
} else if (scopeName === 'global' && !globalTableNameMatch && !name.match(globalRegex)) {
|
|
1233
|
+
diagnostics.error(
|
|
1234
|
+
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`
|
|
1236
|
+
)
|
|
1237
|
+
}
|
|
1238
|
+
const display = table.get('display').ifString()?.getValue() === name
|
|
1239
|
+
const result = await transform.toRecord(
|
|
1240
|
+
addFieldsToColumn(
|
|
1241
|
+
{ name, table: tableName.getValue(), display },
|
|
1242
|
+
column.as(CallExpressionShape)
|
|
1243
|
+
)
|
|
1057
1244
|
)
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1245
|
+
if (!result.success) {
|
|
1246
|
+
diagnostics.error(column, 'Invalid column in table schema')
|
|
1247
|
+
return { success: false }
|
|
1248
|
+
}
|
|
1249
|
+
relatedRecords.push(result.value)
|
|
1250
|
+
columnIdsMap.set(name, result.value.getId().getValue())
|
|
1062
1251
|
}
|
|
1063
|
-
relatedRecords.push(result.value)
|
|
1064
|
-
columnIdsMap.set(name, result.value.getId().getValue())
|
|
1065
1252
|
}
|
|
1066
1253
|
|
|
1067
1254
|
// sys_index
|
|
@@ -1193,7 +1380,7 @@ export const TablePlugin = Plugin.create({
|
|
|
1193
1380
|
properties: {
|
|
1194
1381
|
name: tableName,
|
|
1195
1382
|
element: undefined,
|
|
1196
|
-
language: config.
|
|
1383
|
+
language: config.defaultLanguage,
|
|
1197
1384
|
label: table.get('label').ifString() ?? tableName,
|
|
1198
1385
|
plural: generatePlural(
|
|
1199
1386
|
table.get('label')?.ifString()?.getValue() ?? tableName.getValue()
|
|
@@ -1292,7 +1479,7 @@ export const TablePlugin = Plugin.create({
|
|
|
1292
1479
|
.map((actions) => hasAction('create', actions))
|
|
1293
1480
|
.def(false),
|
|
1294
1481
|
is_extendable: $.from('extensible').toBoolean().def(false),
|
|
1295
|
-
label: $.map((label) => getLabelForDefaultLanguage(label, config.
|
|
1482
|
+
label: $.map((label) => getLabelForDefaultLanguage(label, config.defaultLanguage)),
|
|
1296
1483
|
live_feed_enabled: $.from('liveFeed').toBoolean().def(false),
|
|
1297
1484
|
name: $,
|
|
1298
1485
|
scriptable_table: $.from('scriptableTable').toBoolean().def(false),
|
|
@@ -1469,6 +1656,7 @@ function tableDefToRecordProperties(
|
|
|
1469
1656
|
sysChoice: SysChoiceProperties[]
|
|
1470
1657
|
sysIndex: SysIndexProperties[]
|
|
1471
1658
|
sysDocumentation: SysDocumentationProperties[]
|
|
1659
|
+
sysDictionaryOverride: SysDictionaryOverrideProperties[]
|
|
1472
1660
|
} {
|
|
1473
1661
|
const sysDbObject: SysDbObjectProperties = {
|
|
1474
1662
|
name: tableDef.name,
|
|
@@ -1490,6 +1678,7 @@ function tableDefToRecordProperties(
|
|
|
1490
1678
|
const sysDictionary: SysDictionaryProperties[] = []
|
|
1491
1679
|
const sysChoice: SysChoiceProperties[] = []
|
|
1492
1680
|
const sysDocumentation: SysDocumentationProperties[] = []
|
|
1681
|
+
const sysDictionaryOverride: SysDictionaryOverrideProperties[] = []
|
|
1493
1682
|
|
|
1494
1683
|
// table documentation
|
|
1495
1684
|
if (tableDef.label && tableDef.plural) {
|
|
@@ -1647,6 +1836,7 @@ function tableDefToRecordProperties(
|
|
|
1647
1836
|
sysChoice,
|
|
1648
1837
|
sysIndex,
|
|
1649
1838
|
sysDocumentation,
|
|
1839
|
+
sysDictionaryOverride,
|
|
1650
1840
|
}
|
|
1651
1841
|
}
|
|
1652
1842
|
|
package/src/ui-action-plugin.ts
CHANGED
|
@@ -247,13 +247,6 @@ export const UiActionPlugin = Plugin.create({
|
|
|
247
247
|
),
|
|
248
248
|
})),
|
|
249
249
|
})
|
|
250
|
-
const roles = arg.get('roles').ifArray()?.getElements() ?? []
|
|
251
|
-
if (!arg.get('condition').toString().getValue().trim() && roles.length === 0) {
|
|
252
|
-
diagnostics.warn(
|
|
253
|
-
arg.get('roles').ifDefined() ?? arg.get('condition').ifDefined() ?? arg,
|
|
254
|
-
'UI Actions with an empty condition and no roles defined can be called by any logged-in users. Please restrict UI actions. The condition field should be specified to restrict execution of this UI Action to certain users. For example, current.canWrite() condition restricts the UI Action to the users who can modify the current record, gs.hasRole("admin") condition restricts the UI Action to the users with admin role.'
|
|
255
|
-
)
|
|
256
|
-
}
|
|
257
250
|
|
|
258
251
|
if (arg.get('script').is(ModuleFunctionShape) && isClient.ifBoolean()?.getValue()) {
|
|
259
252
|
diagnostics.error(isClient, 'Module scripts (sys_module) cannot be used on client-side UI Actions')
|
|
@@ -309,7 +302,7 @@ export const UiActionPlugin = Plugin.create({
|
|
|
309
302
|
})
|
|
310
303
|
)
|
|
311
304
|
}
|
|
312
|
-
|
|
305
|
+
const roles = arg.get('roles').ifArray()?.getElements() ?? []
|
|
313
306
|
const roleRecords: Record[] = []
|
|
314
307
|
for (const role of roles) {
|
|
315
308
|
const roleReference = role.isString()
|
package/src/ui-page-plugin.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type Factory,
|
|
18
18
|
type Transform,
|
|
19
19
|
} from '@servicenow/sdk-build-core'
|
|
20
|
+
import { parseDocument, DomUtils } from 'htmlparser2'
|
|
20
21
|
import { XMLParser, XMLBuilder, type X2jOptions, type XmlBuilderOptions } from 'fast-xml-parser'
|
|
21
22
|
import { create } from 'xmlbuilder2'
|
|
22
23
|
import { NowIdShape } from './now-id-plugin'
|
|
@@ -51,6 +52,62 @@ const builderOptions: XmlBuilderOptions = {
|
|
|
51
52
|
],
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
const LT_PLACEHOLDER = '\0__SDK_LT__\0'
|
|
56
|
+
const AMP_PLACEHOLDER = '\0__SDK_AMP__\0'
|
|
57
|
+
const RAW_CONTENT_TAGS = ['script', 'style', 'textarea'] as const
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Uses htmlparser2 to find script/style/textarea content and replaces `<` and
|
|
61
|
+
* `&` with placeholders. This prevents fast-xml-parser from misinterpreting
|
|
62
|
+
* JavaScript comparison operators (e.g. `a < b`) as XML tag openers, and
|
|
63
|
+
* prevents the XMLBuilder's entity escaping from converting `&` to `$[AMP]`
|
|
64
|
+
* inside script content.
|
|
65
|
+
*
|
|
66
|
+
* htmlparser2 is used instead of regex because it correctly handles edge cases
|
|
67
|
+
* like `>` inside attribute values and script tags inside HTML comments.
|
|
68
|
+
*/
|
|
69
|
+
function escapeRawContent(html: string): string {
|
|
70
|
+
const doc = parseDocument(html, { withStartIndices: true, withEndIndices: true })
|
|
71
|
+
|
|
72
|
+
const regions: { start: number; end: number }[] = []
|
|
73
|
+
for (const tag of RAW_CONTENT_TAGS) {
|
|
74
|
+
for (const el of DomUtils.getElementsByTagName(tag, doc, true)) {
|
|
75
|
+
for (const child of el.children) {
|
|
76
|
+
if (child.type === 'text' && child.startIndex != null && child.endIndex != null) {
|
|
77
|
+
const text = child.data
|
|
78
|
+
if (text.includes('<') || text.includes('&')) {
|
|
79
|
+
regions.push({ start: child.startIndex, end: child.endIndex + 1 })
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (regions.length === 0) {
|
|
87
|
+
return html
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Sort by position descending so replacements don't shift earlier indices
|
|
91
|
+
regions.sort((a, b) => b.start - a.start)
|
|
92
|
+
|
|
93
|
+
let result = html
|
|
94
|
+
for (const { start, end } of regions) {
|
|
95
|
+
const content = result.slice(start, end)
|
|
96
|
+
const escaped = content.replace(/&/g, AMP_PLACEHOLDER).replace(/</g, LT_PLACEHOLDER)
|
|
97
|
+
result = result.slice(0, start) + escaped + result.slice(end)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Restores `<` and `&` characters that were replaced by `escapeRawContent`
|
|
105
|
+
* after fast-xml-parser has finished processing.
|
|
106
|
+
*/
|
|
107
|
+
function restoreRawContent(html: string): string {
|
|
108
|
+
return html.replaceAll(LT_PLACEHOLDER, '<').replaceAll(AMP_PLACEHOLDER, '&')
|
|
109
|
+
}
|
|
110
|
+
|
|
54
111
|
const POLARIS_APPSHELL_THEME_ID = 'c86a62e2c7022010099a308dc7c26022'
|
|
55
112
|
const BYOUI_ARTIFACT_NAME_SUFFIX = 'BYOUI Files'
|
|
56
113
|
// Matches the prefix HtmlImportPlugin prepends when it resolves an `import x from '*.html'`
|
|
@@ -388,9 +445,11 @@ export const UiPagePlugin = Plugin.create({
|
|
|
388
445
|
|
|
389
446
|
if (html) {
|
|
390
447
|
try {
|
|
448
|
+
html = escapeRawContent(html)
|
|
391
449
|
const nodes = parser.parse(html)
|
|
392
450
|
const transformed = nodeTransformer(nodes)
|
|
393
451
|
html = new XMLBuilder(builderOptions).build(transformed)
|
|
452
|
+
html = restoreRawContent(html)
|
|
394
453
|
} catch (error: unknown) {
|
|
395
454
|
if (error instanceof Error) {
|
|
396
455
|
diagnostics.error(arg.get('html'), error.message)
|
|
@@ -485,12 +544,14 @@ const getUIPageSourceFilePaths = (
|
|
|
485
544
|
): { files: string[]; assetNames: string[] } => {
|
|
486
545
|
const empty = { files: [], assetNames: [] }
|
|
487
546
|
try {
|
|
488
|
-
// Derive manifest path from HTML path
|
|
489
|
-
//
|
|
490
|
-
// e.g., src/client/index.html
|
|
491
|
-
|
|
547
|
+
// Derive manifest path from HTML path by mirroring the directory structure
|
|
548
|
+
// from clientDir into staticContentDir and swapping the extension.
|
|
549
|
+
// e.g., src/client/index.html -> dist/static/index.ui-source-manifest.json
|
|
550
|
+
// src/client/admin/settings.html -> dist/static/admin/settings.ui-source-manifest.json
|
|
551
|
+
const clientAbsDir = path.join(rootDir, config.clientDir)
|
|
492
552
|
const staticContentAbsDir = path.join(rootDir, config.staticContentDir)
|
|
493
|
-
const
|
|
553
|
+
const htmlRelPath = path.relative(clientAbsDir, htmlFilePath)
|
|
554
|
+
const manifestPath = path.join(staticContentAbsDir, htmlRelPath).replace(/\.html$/, '.ui-source-manifest.json')
|
|
494
555
|
|
|
495
556
|
// Check if manifest file exists
|
|
496
557
|
try {
|
|
@@ -510,24 +571,26 @@ const getUIPageSourceFilePaths = (
|
|
|
510
571
|
|
|
511
572
|
// Derive the JS asset name from the manifest's entry field, matching
|
|
512
573
|
// static-content-plugin's formula: path.join(scope, relativePath_without_ext).
|
|
513
|
-
// The
|
|
514
|
-
//
|
|
515
|
-
//
|
|
574
|
+
// The entry path is relative to the client directory and preserves subdirectories.
|
|
575
|
+
// e.g., src/client/main.tsx -> scope/main
|
|
576
|
+
// src/client/admin/settings.tsx -> scope/admin/settings
|
|
516
577
|
if (!manifest.entry || typeof manifest.entry !== 'string') {
|
|
517
578
|
logger.warn(`No entry field in manifest at ${manifestPath}`)
|
|
518
579
|
return empty
|
|
519
580
|
}
|
|
520
|
-
const
|
|
521
|
-
const
|
|
581
|
+
const entryRelativePath = path.relative(config.clientDir, manifest.entry).replace(/\\/g, '/')
|
|
582
|
+
const entryRelativeWithoutExt = entryRelativePath.replace(/\.[^.]+$/, '')
|
|
583
|
+
const entryAssetName = path.join(config.scope, entryRelativeWithoutExt).replace(/\\/g, '/')
|
|
522
584
|
|
|
523
585
|
// Check if a source map bundle also exists in staticContentDir.
|
|
524
586
|
// static-content-plugin names source map assets as: path.join(scope, relativePath.replace('dbx', ''))
|
|
525
|
-
// e.g. main.jsdbx.map ->
|
|
587
|
+
// e.g. main.jsdbx.map -> scope/main.js.map
|
|
588
|
+
// admin/settings.jsdbx.map -> scope/admin/settings.js.map
|
|
526
589
|
const assetNames = [entryAssetName]
|
|
527
|
-
const sourceMapFilePath = path.join(staticContentAbsDir, `${
|
|
590
|
+
const sourceMapFilePath = path.join(staticContentAbsDir, `${entryRelativeWithoutExt}.jsdbx.map`)
|
|
528
591
|
try {
|
|
529
592
|
fs.accessSync(sourceMapFilePath)
|
|
530
|
-
const sourceMapAssetName = path.join(config.scope, `${
|
|
593
|
+
const sourceMapAssetName = path.join(config.scope, `${entryRelativeWithoutExt}.js.map`).replace(/\\/g, '/')
|
|
531
594
|
assetNames.push(sourceMapAssetName)
|
|
532
595
|
} catch {
|
|
533
596
|
// no source map in this build output — skip
|
package/src/view-plugin.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Plugin, Record, RecordId, Shape } from '@servicenow/sdk-build-core'
|
|
1
|
+
import { Plugin, Record, RecordId, Shape, ts } from '@servicenow/sdk-build-core'
|
|
2
2
|
|
|
3
3
|
export const ViewPlugin = Plugin.create({
|
|
4
4
|
name: 'ViewPlugin',
|
|
@@ -11,7 +11,7 @@ export const ViewPlugin = Plugin.create({
|
|
|
11
11
|
shapes: [
|
|
12
12
|
{
|
|
13
13
|
shape: Record,
|
|
14
|
-
inspect(record, { diagnostics }) {
|
|
14
|
+
inspect(record, { diagnostics, logger }) {
|
|
15
15
|
if (record.getTable() !== 'sys_ui_view') {
|
|
16
16
|
return
|
|
17
17
|
}
|
|
@@ -22,8 +22,14 @@ export const ViewPlugin = Plugin.create({
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const viewName = record.get('name')
|
|
25
|
-
if (!viewName.isString() || !/^[a-zA-Z0-9_]+$/.test(viewName.getValue())) {
|
|
26
|
-
|
|
25
|
+
if (!viewName.isString() || !/^[a-zA-Z0-9_,]+$/.test(viewName.getValue())) {
|
|
26
|
+
if (ts.Node.isNode(viewName.getOriginalSource())) {
|
|
27
|
+
diagnostics.error(viewName, `View name can only contain alphanumeric characters`)
|
|
28
|
+
} else {
|
|
29
|
+
logger.warn(
|
|
30
|
+
`[ViewPlugin] View name '${viewName.isString() ? viewName.getValue() : ''}' in ${record.getOriginalFilePath()} can only contain alphanumeric characters`
|
|
31
|
+
)
|
|
32
|
+
}
|
|
27
33
|
}
|
|
28
34
|
},
|
|
29
35
|
},
|