@servicenow/sdk-build-plugins 4.7.2 → 4.8.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 (81) hide show
  1. package/dist/alias/alias-plugin.d.ts +2 -0
  2. package/dist/alias/alias-plugin.js +183 -0
  3. package/dist/alias/alias-plugin.js.map +1 -0
  4. package/dist/alias/alias-template-plugin.d.ts +2 -0
  5. package/dist/alias/alias-template-plugin.js +232 -0
  6. package/dist/alias/alias-template-plugin.js.map +1 -0
  7. package/dist/alias/index.d.ts +3 -0
  8. package/dist/alias/index.js +20 -0
  9. package/dist/alias/index.js.map +1 -0
  10. package/dist/alias/retry-policy-plugin.d.ts +2 -0
  11. package/dist/alias/retry-policy-plugin.js +119 -0
  12. package/dist/alias/retry-policy-plugin.js.map +1 -0
  13. package/dist/arrow-function-plugin.d.ts +1 -0
  14. package/dist/arrow-function-plugin.js +60 -21
  15. package/dist/arrow-function-plugin.js.map +1 -1
  16. package/dist/atf/test-plugin.js +1 -1
  17. package/dist/atf/test-plugin.js.map +1 -1
  18. package/dist/basic-syntax-plugin.js +7 -7
  19. package/dist/basic-syntax-plugin.js.map +1 -1
  20. package/dist/column/index.d.ts +2 -0
  21. package/dist/column/index.js +13 -0
  22. package/dist/column/index.js.map +1 -0
  23. package/dist/dashboard/dashboard-plugin.js +4 -0
  24. package/dist/dashboard/dashboard-plugin.js.map +1 -1
  25. package/dist/data-lookup-plugin.d.ts +2 -0
  26. package/dist/data-lookup-plugin.js +159 -0
  27. package/dist/data-lookup-plugin.js.map +1 -0
  28. package/dist/flow/plugins/flow-instance-plugin.js +1 -1
  29. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  30. package/dist/flow/plugins/step-instance-plugin.js +1 -1
  31. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  32. package/dist/flow/utils/flow-constants.d.ts +7 -0
  33. package/dist/flow/utils/flow-constants.js +6 -1
  34. package/dist/flow/utils/flow-constants.js.map +1 -1
  35. package/dist/flow/utils/flow-shapes.d.ts +1 -1
  36. package/dist/form-plugin.js +35 -24
  37. package/dist/form-plugin.js.map +1 -1
  38. package/dist/index.d.ts +4 -0
  39. package/dist/index.js +4 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/now-attach-plugin.d.ts +1 -1
  42. package/dist/now-config-plugin.js +2 -1
  43. package/dist/now-config-plugin.js.map +1 -1
  44. package/dist/now-delete-plugin.d.ts +2 -0
  45. package/dist/now-delete-plugin.js +64 -0
  46. package/dist/now-delete-plugin.js.map +1 -0
  47. package/dist/record-plugin.d.ts +10 -0
  48. package/dist/record-plugin.js +15 -1
  49. package/dist/record-plugin.js.map +1 -1
  50. package/dist/repack/lint/Rules.js +17 -7
  51. package/dist/repack/lint/Rules.js.map +1 -1
  52. package/dist/rest-message-plugin.d.ts +2 -0
  53. package/dist/rest-message-plugin.js +331 -0
  54. package/dist/rest-message-plugin.js.map +1 -0
  55. package/dist/script-include-plugin.js +1 -1
  56. package/dist/script-include-plugin.js.map +1 -1
  57. package/dist/server-module-plugin/sbom-builder.js +17 -7
  58. package/dist/server-module-plugin/sbom-builder.js.map +1 -1
  59. package/dist/static-content-plugin.js +17 -7
  60. package/dist/static-content-plugin.js.map +1 -1
  61. package/package.json +6 -5
  62. package/src/alias/alias-plugin.ts +221 -0
  63. package/src/alias/alias-template-plugin.ts +271 -0
  64. package/src/alias/index.ts +3 -0
  65. package/src/alias/retry-policy-plugin.ts +138 -0
  66. package/src/arrow-function-plugin.ts +67 -23
  67. package/src/atf/test-plugin.ts +1 -1
  68. package/src/basic-syntax-plugin.ts +7 -7
  69. package/src/column/index.ts +7 -0
  70. package/src/dashboard/dashboard-plugin.ts +4 -0
  71. package/src/data-lookup-plugin.ts +191 -0
  72. package/src/flow/plugins/flow-instance-plugin.ts +2 -1
  73. package/src/flow/plugins/step-instance-plugin.ts +1 -1
  74. package/src/flow/utils/flow-constants.ts +8 -0
  75. package/src/form-plugin.ts +47 -26
  76. package/src/index.ts +4 -0
  77. package/src/now-config-plugin.ts +2 -1
  78. package/src/now-delete-plugin.ts +82 -0
  79. package/src/record-plugin.ts +17 -2
  80. package/src/rest-message-plugin.ts +391 -0
  81. package/src/script-include-plugin.ts +4 -1
@@ -0,0 +1,82 @@
1
+ import {
2
+ Plugin,
3
+ CallExpressionShape,
4
+ StringShape,
5
+ ObjectShape,
6
+ Record,
7
+ RecordId,
8
+ GUID,
9
+ } from '@servicenow/sdk-build-core'
10
+ import { CallExpressionPlugin } from './call-expression-plugin'
11
+
12
+ export const NowDeletePlugin = Plugin.create({
13
+ name: 'NowDeletePlugin',
14
+ nodes: [
15
+ {
16
+ node: 'CallExpression',
17
+ async toShape(node, { transform }) {
18
+ const result = await transform.toShape(node, CallExpressionPlugin)
19
+ if (
20
+ !result.success ||
21
+ !result.value.is(CallExpressionShape) ||
22
+ result.value.getCallee() !== 'Now.del'
23
+ ) {
24
+ return { success: false }
25
+ }
26
+
27
+ const callExpression = result.value
28
+ const table = callExpression
29
+ .getArgument(0)
30
+ .asString('The first argument to Now.del() must be a table name')
31
+ .getValue()
32
+
33
+ const guidOrKeys = callExpression
34
+ .getArgument(1)
35
+ .as(
36
+ [StringShape, ObjectShape],
37
+ 'The second argument to Now.del() must be a sys_id GUID or a coalesce keys object'
38
+ )
39
+
40
+ const [guid, keysShape] = guidOrKeys.isString()
41
+ ? [guidOrKeys.asString().getValue(), undefined] // Now.del(table, guid)
42
+ : [undefined, guidOrKeys.asObject()] // Now.del(table, keys)
43
+
44
+ // Convert keysShape to plain object
45
+ const keys = keysShape
46
+ ? Object.fromEntries(
47
+ keysShape
48
+ .entries()
49
+ .map(([k, v]) => [
50
+ k,
51
+ typeof v === 'string'
52
+ ? v
53
+ : (v.ifRecord()?.getId() ??
54
+ v.ifRecordId() ??
55
+ v.ifDefined()?.toString().getValue()) ||
56
+ 'NULL',
57
+ ])
58
+ )
59
+ : undefined
60
+
61
+ // Create DELETE record directly (no factory needed)
62
+ const record = new Record({
63
+ source: node,
64
+ action: 'DELETE',
65
+ installCategory: 'author_elective_update',
66
+ properties: keys ?? {},
67
+ id: new RecordId({
68
+ source: node,
69
+ table,
70
+ guid: guid ?? GUID(),
71
+ keys,
72
+ }),
73
+ })
74
+
75
+ return {
76
+ success: true,
77
+ value: record,
78
+ }
79
+ },
80
+ },
81
+ ],
82
+ })
@@ -12,6 +12,7 @@ import {
12
12
  ApplicationMenu,
13
13
  BusinessRule,
14
14
  CrossScopePrivilege,
15
+ DataLookup,
15
16
  Property,
16
17
  Role,
17
18
  Test,
@@ -19,6 +20,7 @@ import {
19
20
  ImportSet,
20
21
  Sla,
21
22
  } from '@servicenow/sdk-core/runtime/app'
23
+ import { RetryPolicy, AliasTemplate, Alias } from '@servicenow/sdk-core/runtime/alias'
22
24
  import {
23
25
  ServicePortal,
24
26
  SPWidget,
@@ -43,7 +45,7 @@ import { ClientScript } from '@servicenow/sdk-core/runtime/clientscript'
43
45
  import { ScriptAction, ScriptInclude, ScheduledScript } from '@servicenow/sdk-core/runtime/sys'
44
46
  import { List, Form } from '@servicenow/sdk-core/runtime/ui'
45
47
  import { Table } from '@servicenow/sdk-core/runtime/db'
46
- import { RestApi } from '@servicenow/sdk-core/runtime/rest'
48
+ import { RestApi, RestMessage } from '@servicenow/sdk-core/runtime/rest'
47
49
  import { EmailNotification, InboundEmailAction } from '@servicenow/sdk-core/runtime/notification'
48
50
  import { UiAction, UiPage, UiPolicy, DataPolicy } from '@servicenow/sdk-core/runtime/ui'
49
51
  import { ActionStepDefinition } from '@servicenow/sdk-core/runtime/flow'
@@ -71,6 +73,7 @@ export const RecordPlugin = Plugin.create({
71
73
  async toShape(record, { compiler }) {
72
74
  const tableName = record.getTable()
73
75
  const columnTypes = compiler.getTableColumnTypes(tableName)
76
+ const mandatoryColumns = compiler.getMandatoryColumns(tableName)
74
77
 
75
78
  const dataProperties = record.transform(({ $ }) =>
76
79
  Object.fromEntries(
@@ -97,7 +100,9 @@ export const RecordPlugin = Plugin.create({
97
100
  }
98
101
  }
99
102
 
100
- return [key, shape?.isString() ? $.def('') : $] //to avoid writing empty string values to the code
103
+ // Mandatory string fields must always be written to avoid build errors
104
+ // from missing required properties in Data<T>.
105
+ return [key, shape?.isString() ? (mandatoryColumns?.has(key) ? $ : $.def('')) : $]
101
106
  })
102
107
  )
103
108
  )
@@ -225,7 +230,10 @@ export const RecordPlugin = Plugin.create({
225
230
  })
226
231
 
227
232
  export const TableOwnership = {
233
+ sys_alias: Alias.name,
234
+ sys_alias_templates: AliasTemplate.name,
228
235
  contract_sla: Sla.name,
236
+ sys_retry_policy: RetryPolicy.name,
229
237
  sys_security_acl: Acl.name,
230
238
  sys_security_acl_role: Acl.name,
231
239
  sys_app_application: ApplicationMenu.name,
@@ -271,6 +279,12 @@ export const TableOwnership = {
271
279
  sys_ws_version: RestApi.name,
272
280
  sys_ws_header: RestApi.name,
273
281
  sys_ws_query_parameter: RestApi.name,
282
+ sys_rest_message: RestMessage.name,
283
+ sys_rest_message_headers: RestMessage.name,
284
+ sys_rest_message_fn: RestMessage.name,
285
+ sys_rest_message_fn_headers: RestMessage.name,
286
+ sys_rest_message_fn_parameters: RestMessage.name,
287
+ sys_rest_message_fn_param_defs: RestMessage.name,
274
288
  sys_user_preference: UserPreference.name,
275
289
  sys_ui_page: UiPage.name,
276
290
  sys_ui_action: UiAction.name,
@@ -338,4 +352,5 @@ export const TableOwnership = {
338
352
  sn_aia_team_member: 'AiAgenticWorkflowPlugin',
339
353
  sn_nowassist_skill_config: 'NowAssistSkillPlugin',
340
354
  sys_one_extend_capability: 'NowAssistSkillPlugin',
355
+ dl_definition: DataLookup.name,
341
356
  }
@@ -0,0 +1,391 @@
1
+ import { CallExpressionShape, Plugin, type Record as FluentRecord } from '@servicenow/sdk-build-core'
2
+ import { NowIdShape } from './now-id-plugin'
3
+
4
+ // Fluent camelCase values → DB stored values
5
+ type MsgAuthType = 'noAuthentication' | 'basic' | 'oauth2'
6
+ type FnAuthType = 'inheritFromParent' | MsgAuthType
7
+
8
+ // DB → Fluent: derive message-level auth type from record fields
9
+ function determineMsgAuthType(record: FluentRecord): MsgAuthType {
10
+ const authTypeRaw = record.get('authentication_type').ifString()?.getValue() ?? ''
11
+
12
+ if (authTypeRaw === 'oauth2') {
13
+ return 'oauth2'
14
+ }
15
+ if (authTypeRaw === 'no_authentication') {
16
+ return 'noAuthentication'
17
+ }
18
+ // 'basic' is the modern choice; 'basic_simple' is the legacy value — both map to 'basic'
19
+ if (authTypeRaw === 'basic' || authTypeRaw === 'basic_simple') {
20
+ return 'basic'
21
+ }
22
+ return 'noAuthentication'
23
+ }
24
+
25
+ // DB → Fluent: derive function-level auth type from record fields
26
+ function determineFnAuthType(record: FluentRecord): FnAuthType {
27
+ const authTypeRaw = record.get('authentication_type').ifString()?.getValue() ?? ''
28
+
29
+ if (authTypeRaw === 'oauth2') {
30
+ return 'oauth2'
31
+ }
32
+ if (authTypeRaw === 'no_authentication') {
33
+ return 'noAuthentication'
34
+ }
35
+ // 'basic' is the modern choice; 'basic_simple' is the legacy value — both map to 'basic'
36
+ if (authTypeRaw === 'basic' || authTypeRaw === 'basic_simple') {
37
+ return 'basic'
38
+ }
39
+ // 'inherit_from_parent' or empty/default means the function inherits from the parent message
40
+ return 'inheritFromParent'
41
+ }
42
+
43
+ // Fluent → DB: translate camelCase auth type to the DB authentication_type choice value
44
+ function toDbAuthType(authType: string): string {
45
+ if (authType === 'oauth2') {
46
+ return 'oauth2'
47
+ }
48
+ if (authType === 'noAuthentication') {
49
+ return 'no_authentication'
50
+ }
51
+ if (authType === 'basic') {
52
+ return 'basic'
53
+ }
54
+ if (authType === 'inheritFromParent') {
55
+ return 'inherit_from_parent'
56
+ }
57
+ throw new Error(`Unknown authentication type: ${authType}`)
58
+ }
59
+
60
+ // DB → Fluent: translate DB escape type to camelCase
61
+ function toFluentEscapeType(dbValue: string): string {
62
+ if (dbValue === 'xml') {
63
+ return 'escapeXml'
64
+ }
65
+ // 'string' (DB default) → 'noEscaping'
66
+ return 'noEscaping'
67
+ }
68
+
69
+ // Fluent → DB: translate camelCase escape type to DB value
70
+ function toDbEscapeType(escapeType: string): string {
71
+ if (escapeType === 'escapeXml') {
72
+ return 'xml'
73
+ }
74
+ // 'noEscaping' (Fluent default) → DB 'string'
75
+ return 'string'
76
+ }
77
+
78
+ export const RestMessagePlugin = Plugin.create({
79
+ name: 'RestMessagePlugin',
80
+ records: {
81
+ sys_rest_message_fn: {
82
+ coalesce: ['rest_message', 'function_name'],
83
+ },
84
+ sys_rest_message: {
85
+ relationships: {
86
+ sys_rest_message_headers: {
87
+ via: 'rest_message',
88
+ descendant: true,
89
+ },
90
+ sys_rest_message_fn: {
91
+ via: 'rest_message',
92
+ descendant: true,
93
+ relationships: {
94
+ sys_rest_message_fn_headers: {
95
+ via: 'rest_message_function',
96
+ descendant: true,
97
+ },
98
+ sys_rest_message_fn_parameters: {
99
+ via: 'rest_message_function',
100
+ descendant: true,
101
+ },
102
+ sys_rest_message_fn_param_defs: {
103
+ via: 'rest_message_function',
104
+ descendant: true,
105
+ },
106
+ },
107
+ },
108
+ },
109
+ async toShape(record, { descendants }) {
110
+ const messageHeaders = descendants.query('sys_rest_message_headers')
111
+ const functions = descendants.query('sys_rest_message_fn')
112
+ const fnHeaders = descendants.query('sys_rest_message_fn_headers')
113
+ const fnVariables = descendants.query('sys_rest_message_fn_parameters')
114
+ const fnQueryParams = descendants.query('sys_rest_message_fn_param_defs')
115
+
116
+ // DB → Fluent auth type (camelCase)
117
+ const msgAuthType = determineMsgAuthType(record)
118
+
119
+ const functionShapes = functions.map((fn) => {
120
+ const fnId = fn.getId().getValue()
121
+
122
+ const thisFnHeaders = fnHeaders.filter((h) => {
123
+ const ref = h.get('rest_message_function')
124
+ return (ref.ifString()?.getValue() ?? ref.ifRecordId()?.getValue()) === fnId
125
+ })
126
+
127
+ const thisVariables = fnVariables.filter((v) => {
128
+ const ref = v.get('rest_message_function')
129
+ return (ref.ifString()?.getValue() ?? ref.ifRecordId()?.getValue()) === fnId
130
+ })
131
+
132
+ const thisQueryParams = fnQueryParams.filter((p) => {
133
+ const ref = p.get('rest_message_function')
134
+ return (ref.ifString()?.getValue() ?? ref.ifRecordId()?.getValue()) === fnId
135
+ })
136
+
137
+ // DB → Fluent auth type (camelCase)
138
+ const fnAuthType = determineFnAuthType(fn)
139
+ const fnHasBasicProfile = !!fn.get('basic_auth_profile').ifString()?.ifNotEmpty()
140
+
141
+ const fnHeaderShapes =
142
+ thisFnHeaders.length > 0
143
+ ? thisFnHeaders.map((h) =>
144
+ h.transform(({ $ }) => ({
145
+ $id: $.val(NowIdShape.from(h)),
146
+ name: $,
147
+ value: $,
148
+ }))
149
+ )
150
+ : undefined
151
+
152
+ const fnVariableShapes =
153
+ thisVariables.length > 0
154
+ ? thisVariables.map((v) => {
155
+ // DB escape type ('xml' | 'string') → Fluent camelCase
156
+ const dbEscapeType = v.get('type').ifString()?.getValue() ?? ''
157
+ const escapeType = toFluentEscapeType(dbEscapeType)
158
+ return v.transform(({ $ }) => ({
159
+ $id: $.val(NowIdShape.from(v)),
160
+ name: $,
161
+ // Omit when it equals the default 'noEscaping'
162
+ escapeType: $.val(escapeType).def('noEscaping'),
163
+ }))
164
+ })
165
+ : undefined
166
+
167
+ const fnQueryParamShapes =
168
+ thisQueryParams.length > 0
169
+ ? thisQueryParams.map((p) =>
170
+ p.transform(({ $ }) => ({
171
+ $id: $.val(NowIdShape.from(p)),
172
+ name: $,
173
+ value: $.def(''),
174
+ order: $.toNumber().def(0),
175
+ }))
176
+ )
177
+ : undefined
178
+
179
+ return fn.transform(({ $ }) => ({
180
+ name: $.from('function_name'),
181
+ // DB stores lowercase ('get', 'post') → Fluent uses uppercase ('GET', 'POST')
182
+ httpMethod: $.from('http_method').map((v) => v.ifString()?.getValue()?.toUpperCase()),
183
+ endpoint: $.from('rest_endpoint').def(''),
184
+ content: $.def(''),
185
+ midServer: $.from('use_mid_server').def(''),
186
+ lock: $.from('lock').toBoolean().def(false),
187
+ // Emit camelCase value; omit when it equals the default 'inheritFromParent'
188
+ authenticationType: $.val(fnAuthType).def('inheritFromParent'),
189
+ basicAuthProfile: fnHasBasicProfile ? $.from('basic_auth_profile') : undefined,
190
+ oauthProfile: fnAuthType === 'oauth2' ? $.from('oauth2_profile') : undefined,
191
+ headers: $.val(fnHeaderShapes),
192
+ variables: $.val(fnVariableShapes),
193
+ queryParams: $.val(fnQueryParamShapes),
194
+ }))
195
+ })
196
+
197
+ const msgHasBasicProfile = !!record.get('basic_auth_profile').ifString()?.ifNotEmpty()
198
+
199
+ const msgHeaderShapes =
200
+ messageHeaders.length > 0
201
+ ? messageHeaders.map((h) =>
202
+ h.transform(({ $ }) => ({
203
+ $id: $.val(NowIdShape.from(h)),
204
+ name: $,
205
+ value: $,
206
+ }))
207
+ )
208
+ : undefined
209
+
210
+ return {
211
+ success: true,
212
+ value: new CallExpressionShape({
213
+ source: record,
214
+ callee: 'RestMessage',
215
+ args: [
216
+ record.transform(({ $ }) => ({
217
+ $id: $.val(NowIdShape.from(record)),
218
+ name: $,
219
+ endpoint: $.from('rest_endpoint'),
220
+ description: $.def(''),
221
+ // DB 'package_private' → Fluent 'packagePrivate'
222
+ access: $.from('access')
223
+ .map((v) => {
224
+ const val = v.ifString()?.getValue()
225
+ return val === 'package_private' ? 'packagePrivate' : val
226
+ })
227
+ .def('packagePrivate'),
228
+ // Emit camelCase value; omit when it equals the default 'noAuthentication'
229
+ authenticationType: $.val(msgAuthType).def('noAuthentication'),
230
+ basicAuthProfile: msgHasBasicProfile ? $.from('basic_auth_profile') : undefined,
231
+ oauthProfile: msgAuthType === 'oauth2' ? $.from('oauth2_profile') : undefined,
232
+ headers: $.val(msgHeaderShapes),
233
+ functions: $.val(functionShapes.length > 0 ? functionShapes : undefined),
234
+ })),
235
+ ],
236
+ }),
237
+ }
238
+ },
239
+ },
240
+ },
241
+ shapes: [
242
+ {
243
+ shape: CallExpressionShape,
244
+ fileTypes: ['fluent'],
245
+ async toRecord(callExpression, { factory }) {
246
+ if (callExpression.getCallee() !== 'RestMessage') {
247
+ return { success: false }
248
+ }
249
+
250
+ const arg = callExpression.getArgument(0).asObject()
251
+
252
+ const authType = arg.get('authenticationType').ifString()?.getValue() ?? 'noAuthentication'
253
+ const isBasic = authType === 'basic'
254
+ const isOauth2 = authType === 'oauth2'
255
+
256
+ // Fluent 'packagePrivate' → DB 'package_private'
257
+ const accessVal = arg.get('access').ifString()?.getValue() ?? 'packagePrivate'
258
+ const accessXml = accessVal === 'packagePrivate' ? 'package_private' : accessVal
259
+
260
+ const msgRecord = await factory.createRecord({
261
+ source: callExpression,
262
+ table: 'sys_rest_message',
263
+ explicitId: arg.get('$id'),
264
+ properties: arg.transform(({ $ }) => ({
265
+ name: $,
266
+ rest_endpoint: $.from('endpoint'),
267
+ description: $.def(''),
268
+ access: $.val(accessXml).def('package_private'),
269
+ // Fluent camelCase → DB choice value: 'basic' | 'oauth2' | 'no_authentication'
270
+ authentication_type: $.val(toDbAuthType(authType)).def('no_authentication'),
271
+ // 'basic': store credential profile reference
272
+ basic_auth_profile: isBasic ? $.from('basicAuthProfile') : $.val('').def(''),
273
+ oauth2_profile: isOauth2 ? $.from('oauthProfile').def('') : $.val('').def(''),
274
+ })),
275
+ })
276
+
277
+ // Create message-level header records
278
+ const headerElements = arg.get('headers').ifArray()?.getElements() ?? []
279
+ const headerRecords = await Promise.all(
280
+ headerElements.map((h) =>
281
+ factory.createRecord({
282
+ source: h,
283
+ table: 'sys_rest_message_headers',
284
+ explicitId: h.asObject().get('$id'),
285
+ properties: h.asObject().transform(({ $ }) => ({
286
+ rest_message: $.val(msgRecord.getId()),
287
+ name: $,
288
+ value: $,
289
+ })),
290
+ })
291
+ )
292
+ )
293
+
294
+ // Create function records with their child records
295
+ const fnElements = arg.get('functions').ifArray()?.getElements() ?? []
296
+ const fnRecordsWithChildren = await Promise.all(
297
+ fnElements.map(async (fnShape) => {
298
+ const fn = fnShape.asObject()
299
+ const fnAuthTypeVal = fn.get('authenticationType').ifString()?.getValue()
300
+ const fnAuthType = fnAuthTypeVal ?? 'inheritFromParent'
301
+ const fnIsBasic = fnAuthType === 'basic'
302
+ const fnIsOauth2 = fnAuthType === 'oauth2'
303
+
304
+ // All auth types translate via toDbAuthType — no special-casing
305
+ const fnAuthTypeXml = toDbAuthType(fnAuthType)
306
+
307
+ const fnRecord = await factory.createRecord({
308
+ source: fnShape,
309
+ table: 'sys_rest_message_fn',
310
+ properties: fn.transform(({ $ }) => ({
311
+ rest_message: $.val(msgRecord.getId()),
312
+ function_name: $.from('name'),
313
+ // Fluent uses uppercase ('GET', 'POST') → DB stores lowercase ('get', 'post')
314
+ http_method: $.from('httpMethod').map((v) => v.ifString()?.getValue()?.toLowerCase()),
315
+ rest_endpoint: $.from('endpoint').def(''),
316
+ content: $.def(''),
317
+ use_mid_server: $.from('midServer').def(''),
318
+ lock: $.from('lock').def(false),
319
+ // All auth types translate via toDbAuthType — no special-casing
320
+ authentication_type: $.val(fnAuthTypeXml).def('inherit_from_parent'),
321
+ basic_auth_profile: fnIsBasic ? $.from('basicAuthProfile') : $.val('').def(''),
322
+ oauth2_profile: fnIsOauth2 ? $.from('oauthProfile').def('') : $.val('').def(''),
323
+ })),
324
+ })
325
+
326
+ // Create function-level header records
327
+ const fnHeaderElements = fn.get('headers').ifArray()?.getElements() ?? []
328
+ const fnHeaderRecords = await Promise.all(
329
+ fnHeaderElements.map((h) =>
330
+ factory.createRecord({
331
+ source: h,
332
+ table: 'sys_rest_message_fn_headers',
333
+ explicitId: h.asObject().get('$id'),
334
+ properties: h.asObject().transform(({ $ }) => ({
335
+ rest_message_function: $.val(fnRecord.getId()),
336
+ name: $,
337
+ value: $,
338
+ })),
339
+ })
340
+ )
341
+ )
342
+
343
+ // Create variable substitution records (sys_rest_message_fn_parameters)
344
+ const variableElements = fn.get('variables').ifArray()?.getElements() ?? []
345
+ const variableRecords = await Promise.all(
346
+ variableElements.map((v) => {
347
+ const escapeType = v.asObject().get('escapeType').ifString()?.getValue() ?? 'noEscaping'
348
+ return factory.createRecord({
349
+ source: v,
350
+ table: 'sys_rest_message_fn_parameters',
351
+ explicitId: v.asObject().get('$id'),
352
+ properties: v.asObject().transform(({ $ }) => ({
353
+ rest_message_function: $.val(fnRecord.getId()),
354
+ name: $,
355
+ // Fluent camelCase → DB: 'xml' | 'string'
356
+ type: $.val(toDbEscapeType(escapeType)),
357
+ })),
358
+ })
359
+ })
360
+ )
361
+
362
+ // Create HTTP query parameter records (sys_rest_message_fn_param_defs)
363
+ const queryParamElements = fn.get('queryParams').ifArray()?.getElements() ?? []
364
+ const queryParamRecords = await Promise.all(
365
+ queryParamElements.map((p) =>
366
+ factory.createRecord({
367
+ source: p,
368
+ table: 'sys_rest_message_fn_param_defs',
369
+ explicitId: p.asObject().get('$id'),
370
+ properties: p.asObject().transform(({ $ }) => ({
371
+ rest_message_function: $.val(fnRecord.getId()),
372
+ name: $,
373
+ value: $.def(''),
374
+ order: $.def(0),
375
+ })),
376
+ })
377
+ )
378
+ )
379
+
380
+ return fnRecord.with(...fnHeaderRecords, ...variableRecords, ...queryParamRecords)
381
+ })
382
+ )
383
+
384
+ return {
385
+ success: true,
386
+ value: msgRecord.with(...headerRecords, ...fnRecordsWithChildren),
387
+ }
388
+ },
389
+ },
390
+ ],
391
+ })
@@ -55,7 +55,10 @@ export const ScriptIncludePlugin = Plugin.create({
55
55
  const name = prop.get('name').asString()
56
56
 
57
57
  if (!nameRegex.test(name.getValue())) {
58
- diagnostics.error(name.getOriginalNode(), `name must be a valid JavaScript identifier`)
58
+ diagnostics.warn(
59
+ name.getOriginalNode(),
60
+ `Script include name '${name.getValue()}' is not a valid JavaScript identifier. This may cause issues when invoking the script include on the platform.`
61
+ )
59
62
  }
60
63
 
61
64
  const callerAccess = prop.get('callerAccess')