@servicenow/sdk-build-core 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/dist/BuildOptions.d.ts +50 -0
  2. package/dist/BuildOptions.js +46 -0
  3. package/dist/BuildOptions.js.map +1 -0
  4. package/dist/GUID.d.ts +2 -0
  5. package/dist/GUID.js +48 -0
  6. package/dist/GUID.js.map +1 -0
  7. package/dist/Keys.d.ts +29 -0
  8. package/dist/Keys.js +258 -0
  9. package/dist/Keys.js.map +1 -0
  10. package/dist/TypeScript.d.ts +5 -0
  11. package/dist/TypeScript.js +81 -0
  12. package/dist/TypeScript.js.map +1 -0
  13. package/dist/XML.d.ts +25 -0
  14. package/dist/XML.js +72 -0
  15. package/dist/XML.js.map +1 -0
  16. package/dist/index.d.ts +8 -0
  17. package/dist/index.js +38 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/plugins/Context.d.ts +198 -0
  20. package/dist/plugins/Context.js +3 -0
  21. package/dist/plugins/Context.js.map +1 -0
  22. package/dist/plugins/Diagnostic.d.ts +10 -0
  23. package/dist/plugins/Diagnostic.js +52 -0
  24. package/dist/plugins/Diagnostic.js.map +1 -0
  25. package/dist/plugins/Plugin.d.ts +175 -0
  26. package/dist/plugins/Plugin.js +15 -0
  27. package/dist/plugins/Plugin.js.map +1 -0
  28. package/dist/plugins/behaviors/Arranger.d.ts +26 -0
  29. package/dist/plugins/behaviors/Arranger.js +3 -0
  30. package/dist/plugins/behaviors/Arranger.js.map +1 -0
  31. package/dist/plugins/behaviors/Composer.d.ts +101 -0
  32. package/dist/plugins/behaviors/Composer.js +15 -0
  33. package/dist/plugins/behaviors/Composer.js.map +1 -0
  34. package/dist/plugins/behaviors/Diagnostics.d.ts +8 -0
  35. package/dist/plugins/behaviors/Diagnostics.js +3 -0
  36. package/dist/plugins/behaviors/Diagnostics.js.map +1 -0
  37. package/dist/plugins/behaviors/Generator.d.ts +21 -0
  38. package/dist/plugins/behaviors/Generator.js +3 -0
  39. package/dist/plugins/behaviors/Generator.js.map +1 -0
  40. package/dist/plugins/behaviors/OwnedTables.d.ts +6 -0
  41. package/dist/plugins/behaviors/OwnedTables.js +3 -0
  42. package/dist/plugins/behaviors/OwnedTables.js.map +1 -0
  43. package/dist/plugins/behaviors/PostProcessor.d.ts +5 -0
  44. package/dist/plugins/behaviors/PostProcessor.js +3 -0
  45. package/dist/plugins/behaviors/PostProcessor.js.map +1 -0
  46. package/dist/plugins/behaviors/Serializer.d.ts +29 -0
  47. package/dist/plugins/behaviors/Serializer.js +3 -0
  48. package/dist/plugins/behaviors/Serializer.js.map +1 -0
  49. package/dist/plugins/behaviors/Transformer.d.ts +23 -0
  50. package/dist/plugins/behaviors/Transformer.js +3 -0
  51. package/dist/plugins/behaviors/Transformer.js.map +1 -0
  52. package/dist/plugins/behaviors/extractors/Data.d.ts +107 -0
  53. package/dist/plugins/behaviors/extractors/Data.js +191 -0
  54. package/dist/plugins/behaviors/extractors/Data.js.map +1 -0
  55. package/dist/plugins/behaviors/extractors/Extractors.d.ts +41 -0
  56. package/dist/plugins/behaviors/extractors/Extractors.js +3 -0
  57. package/dist/plugins/behaviors/extractors/Extractors.js.map +1 -0
  58. package/dist/plugins/behaviors/extractors/index.d.ts +2 -0
  59. package/dist/plugins/behaviors/extractors/index.js +19 -0
  60. package/dist/plugins/behaviors/extractors/index.js.map +1 -0
  61. package/dist/plugins/behaviors/index.d.ts +9 -0
  62. package/dist/plugins/behaviors/index.js +26 -0
  63. package/dist/plugins/behaviors/index.js.map +1 -0
  64. package/dist/plugins/index.d.ts +5 -0
  65. package/dist/plugins/index.js +23 -0
  66. package/dist/plugins/index.js.map +1 -0
  67. package/dist/plugins/util/CallExpression.d.ts +6 -0
  68. package/dist/plugins/util/CallExpression.js +93 -0
  69. package/dist/plugins/util/CallExpression.js.map +1 -0
  70. package/dist/plugins/util/CodeTransformation.d.ts +74 -0
  71. package/dist/plugins/util/CodeTransformation.js +421 -0
  72. package/dist/plugins/util/CodeTransformation.js.map +1 -0
  73. package/dist/plugins/util/ConfigurationFunction.d.ts +106 -0
  74. package/dist/plugins/util/ConfigurationFunction.js +377 -0
  75. package/dist/plugins/util/ConfigurationFunction.js.map +1 -0
  76. package/dist/plugins/util/ObjectLiteral.d.ts +9 -0
  77. package/dist/plugins/util/ObjectLiteral.js +60 -0
  78. package/dist/plugins/util/ObjectLiteral.js.map +1 -0
  79. package/dist/plugins/util/index.d.ts +4 -0
  80. package/dist/plugins/util/index.js +21 -0
  81. package/dist/plugins/util/index.js.map +1 -0
  82. package/dist/util/Debug.d.ts +8 -0
  83. package/dist/util/Debug.js +39 -0
  84. package/dist/util/Debug.js.map +1 -0
  85. package/dist/util/Util.d.ts +4 -0
  86. package/dist/util/Util.js +41 -0
  87. package/dist/util/Util.js.map +1 -0
  88. package/dist/util/XMLJsonBuilder.d.ts +18 -0
  89. package/dist/util/XMLJsonBuilder.js +59 -0
  90. package/dist/util/XMLJsonBuilder.js.map +1 -0
  91. package/dist/util/XMLUploadParser.d.ts +22 -0
  92. package/dist/util/XMLUploadParser.js +67 -0
  93. package/dist/util/XMLUploadParser.js.map +1 -0
  94. package/dist/util/index.d.ts +4 -0
  95. package/dist/util/index.js +21 -0
  96. package/dist/util/index.js.map +1 -0
  97. package/license +9 -0
  98. package/package.json +42 -0
  99. package/src/BuildOptions.ts +27 -0
  100. package/src/GUID.ts +26 -0
  101. package/src/Keys.ts +287 -0
  102. package/src/TypeScript.ts +65 -0
  103. package/src/XML.ts +85 -0
  104. package/src/index.ts +8 -0
  105. package/src/plugins/Context.ts +249 -0
  106. package/src/plugins/Diagnostic.ts +31 -0
  107. package/src/plugins/Plugin.ts +246 -0
  108. package/src/plugins/behaviors/Arranger.ts +42 -0
  109. package/src/plugins/behaviors/Composer.ts +124 -0
  110. package/src/plugins/behaviors/Diagnostics.ts +13 -0
  111. package/src/plugins/behaviors/Generator.ts +31 -0
  112. package/src/plugins/behaviors/OwnedTables.ts +5 -0
  113. package/src/plugins/behaviors/PostProcessor.ts +6 -0
  114. package/src/plugins/behaviors/Serializer.ts +39 -0
  115. package/src/plugins/behaviors/Transformer.ts +32 -0
  116. package/src/plugins/behaviors/extractors/Data.ts +247 -0
  117. package/src/plugins/behaviors/extractors/Extractors.ts +57 -0
  118. package/src/plugins/behaviors/extractors/index.ts +2 -0
  119. package/src/plugins/behaviors/index.ts +9 -0
  120. package/src/plugins/index.ts +5 -0
  121. package/src/plugins/util/CallExpression.ts +83 -0
  122. package/src/plugins/util/CodeTransformation.ts +500 -0
  123. package/src/plugins/util/ConfigurationFunction.ts +477 -0
  124. package/src/plugins/util/ObjectLiteral.ts +37 -0
  125. package/src/plugins/util/index.ts +4 -0
  126. package/src/util/Debug.ts +46 -0
  127. package/src/util/Util.ts +21 -0
  128. package/src/util/XMLJsonBuilder.ts +64 -0
  129. package/src/util/XMLUploadParser.ts +90 -0
  130. package/src/util/index.ts +4 -0
@@ -0,0 +1,42 @@
1
+ import { Document, DocumentPointer, LinkedDocument } from './Composer'
2
+ import { Context } from '../Context'
3
+
4
+ export type Arranged<Status extends 'unresolved' | 'resolved' = 'resolved'> = {
5
+ parent?: Status extends 'unresolved' ? DocumentPointer & Arranged<'unresolved'> : LinkedDocument & Arranged // Parent is always linked because document trees are processed from top to bottom
6
+ }
7
+
8
+ // TODO: Use this return type for all behaviors. It solves the ambiguity
9
+ // between `undefined` as a valid return value and `undefined` as an indicator
10
+ // that the input was not handled.
11
+ export type Result<T> =
12
+ | {
13
+ handled: false
14
+ result?: never
15
+ }
16
+ | {
17
+ handled: true
18
+ result: T
19
+ }
20
+
21
+ /**
22
+ * An arranger function is a function which accepts a document of a known
23
+ * kind as input and returns a pointer to its parent, if applicable.
24
+ *
25
+ * @see {@linkcode Document}
26
+ * @see {@linkcode DocumentPointer}
27
+ */
28
+ export type ArrangerFunction<DocumentKind extends string = string> = (
29
+ document: Document<DocumentKind>,
30
+ context: Context
31
+ ) => Result<DocumentPointer | undefined>
32
+
33
+ // This is only imported so it can be referenced in the JS doc below
34
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
35
+ import type { Plugin } from '../Plugin'
36
+
37
+ /**
38
+ * @see {@linkcode Plugin#arrangers}
39
+ */
40
+ export type Arrangers<DocumentKinds extends string = string> = {
41
+ [K in DocumentKinds]: ArrangerFunction<K>
42
+ }
@@ -0,0 +1,124 @@
1
+ import { SupportedNode } from '@servicenow/sdk-project'
2
+ import { XmlData, PrimitiveData, Action, EntityData } from './extractors'
3
+ import { Context } from '../Context'
4
+
5
+ /**
6
+ * An unlinked document is a document that does not have an
7
+ * association with any AST node.
8
+ *
9
+ * @see {@linkcode Document}
10
+ */
11
+ export type UnlinkedDocument<DocumentKind extends string = string> = Omit<Document<DocumentKind>, 'node'>
12
+
13
+ /**
14
+ * A linked document is a document that has an association
15
+ * with an AST node.
16
+ *
17
+ * @see {@linkcode Document}
18
+ */
19
+ export type LinkedDocument<DocumentKind extends string = string, Node extends SupportedNode = SupportedNode> = Document<
20
+ DocumentKind,
21
+ Node
22
+ > & {
23
+ entity?: EntityData
24
+ node: Node
25
+ }
26
+
27
+ /**
28
+ * A document is a data structure that has a known kind, an ID,
29
+ * and an action (insert/update or delete). It may also have an
30
+ * associated AST node if the data originated from source code.
31
+ *
32
+ * @see {@linkcode LinkedDocument}
33
+ * @see {@linkcode UnlinkedDocument}
34
+ */
35
+ export type Document<DocumentKind extends string = string, Node extends SupportedNode = SupportedNode> = {
36
+ guid: string
37
+ kind: DocumentKind
38
+ data: unknown
39
+ changedData?: unknown
40
+ xmlData?: unknown
41
+ node?: Node
42
+ action?: Action
43
+ }
44
+
45
+ /**
46
+ * A document pointer is an object that represents a document
47
+ * via its ID and kind. Document pointers can be resolved to
48
+ * actual documents after all documents have been composed.
49
+ *
50
+ * @see {@linkcode Document}
51
+ */
52
+ export type DocumentPointer<DocumentKind extends string = string> = {
53
+ guid: string
54
+ kind: DocumentKind
55
+ }
56
+
57
+ /**
58
+ * A document map is an object where documents are accessible
59
+ * by their kind and ID. The keys of the object are document
60
+ * kinds, and the values are sub-objects where the keys are
61
+ * document IDs and the values are the corresponding documents.
62
+ *
63
+ * @see {@linkcode Document}
64
+ */
65
+ export type DocumentMap = {
66
+ [kinds: string]: {
67
+ [guids: string]: Document
68
+ }
69
+ }
70
+
71
+ /**
72
+ * An entity composer function is a function that accepts a
73
+ * piece of entity data from the source code and returns one
74
+ * or more linked documents derived from that entity, or returns
75
+ * undefined to indicate that it did not handle the data.
76
+ *
77
+ * @see {@linkcode Entity}
78
+ * @see {@linkcode EntityData}
79
+ */
80
+ export type EntityComposerFunction<Data extends Record<string, unknown> = Record<string, unknown>> = (
81
+ entityData: EntityData<Data>,
82
+ context: Context
83
+ ) => LinkedDocument | LinkedDocument[] | undefined
84
+
85
+ /**
86
+ * An XML composer function is a function that accepts a piece
87
+ * of data from an XML file and returns one or more unlinked
88
+ * documents derived from that data, or returns undefined to
89
+ * indicate that it did not handle the data.
90
+ *
91
+ * @see {@linkcode PrimitiveData}
92
+ * @see {@linkcode XmlData}
93
+ */
94
+ export type XmlComposerFunction<Data extends PrimitiveData = PrimitiveData> = (
95
+ xmlData: XmlData<Data>,
96
+ context: Context
97
+ ) => UnlinkedDocument | UnlinkedDocument[] | undefined
98
+
99
+ // This is only imported so it can be referenced in the JS doc below
100
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
101
+ import type { Plugin } from '../Plugin'
102
+
103
+ /**
104
+ * @see {@linkcode Plugin#composers}
105
+ */
106
+ export type Composers = {
107
+ entity?: { [kind: string]: EntityComposerFunction }
108
+ xml?: { [kind: string]: XmlComposerFunction }
109
+ }
110
+
111
+ /**
112
+ * Utility which takes an unlinked document and a node and returns the
113
+ * same document linked to the provided node.
114
+ *
115
+ * @param document The {@linkcode UnlinkedDocument} to link
116
+ * @param node The AST node to link the document to
117
+ * @returns A {@linkcode LinkedDocument}
118
+ */
119
+ export function linkDocument<const K extends string, const N extends SupportedNode>(
120
+ document: UnlinkedDocument<K>,
121
+ node: N
122
+ ): LinkedDocument<K, N> {
123
+ return { ...document, node }
124
+ }
@@ -0,0 +1,13 @@
1
+ import * as ts from 'ts-morph'
2
+ import { SupportedKindName, SupportedNodeByKindName } from '@servicenow/sdk-project'
3
+ import { Context } from '../Context'
4
+ import { FluentDiagnostic } from '../Diagnostic'
5
+
6
+ export type DiagnosticsFunction<K extends SupportedKindName | 'Node' = SupportedKindName> = (
7
+ node: K extends SupportedKindName ? SupportedNodeByKindName<K> : K extends 'Node' ? ts.Node : any,
8
+ context: Context
9
+ ) => FluentDiagnostic[]
10
+
11
+ export type Diagnostics<NodeKinds extends SupportedKindName | 'Node'> = {
12
+ [K in NodeKinds]: DiagnosticsFunction<K>
13
+ }
@@ -0,0 +1,31 @@
1
+ import { LinkedDocument, UnlinkedDocument } from './Composer'
2
+ import { Arranged } from './Arranger'
3
+ import { Context } from '../Context'
4
+
5
+ /**
6
+ * A generator function is a function that accepts an unlinked document of
7
+ * a known kind as input and returns the same document as a linked document.
8
+ * In practice, this means the generator's job is to create new AST nodes
9
+ * for documents that are not linked to any node. Documents without nodes
10
+ * are usually from XML files containing records that aren't defined in the
11
+ * source code yet.
12
+ *
13
+ * @see {@linkcode UnlinkedDocument}
14
+ * @see {@linkcode LinkedDocument}
15
+ */
16
+ export type GeneratorFunction<DocumentKind extends string = string> = (
17
+ document: UnlinkedDocument<DocumentKind> & Arranged,
18
+ context: Context,
19
+ linkedDocuments: LinkedDocument[]
20
+ ) => LinkedDocument<DocumentKind> | undefined
21
+
22
+ // This is only imported so it can be referenced in the JS doc below
23
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
24
+ import type { Plugin } from '../Plugin'
25
+
26
+ /**
27
+ * @see {@linkcode Plugin#generators}
28
+ */
29
+ export type Generators<DocumentKinds extends string = string> = {
30
+ [K in DocumentKinds]: GeneratorFunction<K>
31
+ }
@@ -0,0 +1,5 @@
1
+ import { Diagnostic } from '@servicenow/sdk-project'
2
+
3
+ export type TableOwnership = {
4
+ [table: string]: { diagnosticLevel: Diagnostic.Level }
5
+ }
@@ -0,0 +1,6 @@
1
+ import { Context } from '../Context'
2
+ import { EntityData } from './extractors'
3
+
4
+ export type PostProcessors = {
5
+ entities?: (entities: EntityData[], context: Context) => void
6
+ }
@@ -0,0 +1,39 @@
1
+ import { Document } from './Composer'
2
+ import { Arranged } from './Arranger'
3
+ import { Context } from '../Context'
4
+
5
+ /**
6
+ * A file is a data structure with a name, a target directory, and content
7
+ * as a string. The target directory must be the name of one of the magic
8
+ * directories that exist within a ServiceNow deployable app package.
9
+ */
10
+ export type File = {
11
+ name: `${string}.xml`
12
+ directory: 'dictionary' | 'unload' | 'unload.demo' | 'update' | 'apply_once' | ''
13
+ content: string
14
+ }
15
+
16
+ /**
17
+ * A serializer function is a function which accepts a document of a known
18
+ * kind as input and returns one or more files derived from that document,
19
+ * or returns undefined to indicate it did not handle the document. In practice,
20
+ * this means generating the XML content that the build system will write to
21
+ * the filesystem during the final stages of a build.
22
+ *
23
+ * @see {@linkcode Document}
24
+ */
25
+ export type SerializerFunction<DocumentKind extends string = string> = (
26
+ document: Document<DocumentKind> & Arranged,
27
+ context: Context
28
+ ) => File | File[] | undefined
29
+
30
+ // This is only imported so it can be referenced in the JS doc below
31
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
32
+ import type { Plugin } from '../Plugin'
33
+
34
+ /**
35
+ * @see {@linkcode Plugin#serializers}
36
+ */
37
+ export type Serializers<DocumentKinds extends string = string> = {
38
+ [K in DocumentKinds]: SerializerFunction<K>
39
+ }
@@ -0,0 +1,32 @@
1
+ import { SupportedKindName, SupportedNode, SupportedNodeByKindName } from '@servicenow/sdk-project'
2
+ import { LinkedDocument } from './Composer'
3
+ import { Arranged } from './Arranger'
4
+ import { Context } from '../Context'
5
+
6
+ /**
7
+ * A transformer function is a function which accepts a linked document of
8
+ * a known kind as input and returns a boolean indicating whether or not it
9
+ * handled the document. The implementation of a transformer function, if it
10
+ * chooses to handle the document, should perform the necessary transformations
11
+ * on the document's linked node to ensure the document's data is fully
12
+ * represented in the source code.
13
+ *
14
+ * @see {@linkcode LinkedDocument}
15
+ */
16
+ export type TransformerFunction<DocumentKind extends string = string, Node extends SupportedNode = SupportedNode> = (
17
+ document: LinkedDocument<DocumentKind, Node> & Arranged,
18
+ context: Context
19
+ ) => boolean
20
+
21
+ // This is only imported so it can be referenced in the JS doc below
22
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
+ import type { Plugin } from '../Plugin'
24
+
25
+ /**
26
+ * @see {@linkcode Plugin#transformers}
27
+ */
28
+ export type Transformers<DocumentKinds extends string = string> = {
29
+ [DK in DocumentKinds]: {
30
+ [NK in SupportedKindName]?: TransformerFunction<DK, SupportedNodeByKindName<NK>>
31
+ }
32
+ }
@@ -0,0 +1,247 @@
1
+ import { SupportedNode } from '@servicenow/sdk-project'
2
+
3
+ /**
4
+ * Actions indicate what should take place in the database when the XML file
5
+ * is loaded. The platform understands these three actions: insert/update,
6
+ * delete, and delete multiple.
7
+ */
8
+ export type Action = 'INSERT_OR_UPDATE' | 'DELETE' | 'delete_multiple'
9
+
10
+ /**
11
+ * Utility type for data passed to XML extractors for processing.
12
+ */
13
+ export type Xml = {
14
+ filePath: string
15
+ data: any
16
+ }
17
+
18
+ /**
19
+ * Utility type to represent a simple object structure with primitive values.
20
+ */
21
+ export type PrimitiveData = {
22
+ [x: string]: PrimitiveData | PrimitiveData[] | string | number | boolean | undefined
23
+ }
24
+
25
+ /**
26
+ * XML data is data from XML files (such as the parsed content of a record_update)
27
+ * which is not associated with any AST node.
28
+ */
29
+ export class XmlData<D extends PrimitiveData = PrimitiveData> {
30
+ constructor(
31
+ public data: D,
32
+ readonly filePath: string,
33
+ readonly kind: string,
34
+ readonly action: Action = 'INSERT_OR_UPDATE'
35
+ ) {}
36
+ }
37
+
38
+ export type WrappedData<D> = {
39
+ [K in keyof D]: D[K] extends string
40
+ ? StringData<D[K]>
41
+ : D[K] extends number
42
+ ? NumberData<D[K]>
43
+ : D[K] extends boolean
44
+ ? BooleanData<D[K]>
45
+ : D[K] extends Array<unknown>
46
+ ? ArrayData<D[K]>
47
+ : D[K] extends Record<string, unknown>
48
+ ? ObjectData<D[K]>
49
+ : Data<D[K]>
50
+ }
51
+
52
+ export abstract class Data<D = unknown> {
53
+ constructor(private readonly node: SupportedNode) {}
54
+
55
+ /**
56
+ * @deprecated Using this for an array or object is bad practice because all of the elements/properties
57
+ * will be assigned the same node as the array/object itself. This makes it difficult to bidrectionally
58
+ * sync data created this way because you don't have the granularity required to know where each of the
59
+ * element/property values (and any nested values) came from.
60
+ */
61
+ static fromValue<const D>(value: D, node: SupportedNode): Data<D> {
62
+ if (value === undefined || value === null) {
63
+ return new UndefinedData(node) as Data<D> // TODO: How can we avoid this silly cast?
64
+ } else if (typeof value === 'string') {
65
+ return new StringData(value, node)
66
+ } else if (typeof value === 'number') {
67
+ return new NumberData(value, node)
68
+ } else if (typeof value === 'boolean') {
69
+ return new BooleanData(value, node)
70
+ } else if (Array.isArray(value)) {
71
+ return ArrayData.fromArrayValue(value, node)
72
+ } else if (!!value && typeof value === 'object') {
73
+ const obj = Object.fromEntries(Object.entries(value).filter(([key]) => typeof key === 'string'))
74
+ return ObjectData.fromObjectValue(obj as typeof obj & D, node)
75
+ } else {
76
+ throw new Error(
77
+ `Unsupported data type: ${value} (Type: ${typeof value}, Class: ${value?.constructor?.name})`
78
+ )
79
+ }
80
+ }
81
+
82
+ static isUndefined(data?: Data): data is UndefinedData {
83
+ return data instanceof UndefinedData
84
+ }
85
+
86
+ static isString(data?: Data): data is StringData {
87
+ return data instanceof StringData
88
+ }
89
+
90
+ static isNumber(data?: Data): data is NumberData {
91
+ return data instanceof NumberData
92
+ }
93
+
94
+ static isBoolean(data?: Data): data is BooleanData {
95
+ return data instanceof BooleanData
96
+ }
97
+
98
+ static isArray(data?: Data): data is ArrayData {
99
+ return data instanceof ArrayData
100
+ }
101
+
102
+ static isObject(data?: Data): data is ObjectData {
103
+ return data instanceof ObjectData
104
+ }
105
+
106
+ static isEntity(data?: Data): data is EntityData {
107
+ return data instanceof EntityData
108
+ }
109
+
110
+ abstract getValue(): D
111
+
112
+ getNode() {
113
+ return this.node
114
+ }
115
+ }
116
+
117
+ export class UndefinedData extends Data<undefined> {
118
+ constructor(node: SupportedNode) {
119
+ super(node)
120
+ }
121
+
122
+ getValue() {
123
+ return undefined
124
+ }
125
+ }
126
+
127
+ export class StringData<const D extends string = string> extends Data<D> {
128
+ constructor(
129
+ private readonly value: D,
130
+ node: SupportedNode
131
+ ) {
132
+ super(node)
133
+ }
134
+
135
+ getValue() {
136
+ return this.value
137
+ }
138
+ }
139
+
140
+ export class NumberData<const D extends number = number> extends Data<D> {
141
+ constructor(
142
+ private readonly value: D,
143
+ node: SupportedNode
144
+ ) {
145
+ super(node)
146
+ }
147
+
148
+ getValue() {
149
+ return this.value
150
+ }
151
+ }
152
+
153
+ export class BooleanData<const D extends boolean = boolean> extends Data<D> {
154
+ constructor(
155
+ private readonly value: D,
156
+ node: SupportedNode
157
+ ) {
158
+ super(node)
159
+ }
160
+
161
+ getValue() {
162
+ return this.value
163
+ }
164
+ }
165
+
166
+ export class ArrayData<const D extends unknown[] = unknown[]> extends Data<D> {
167
+ constructor(
168
+ private readonly elements: WrappedData<D>,
169
+ node: SupportedNode
170
+ ) {
171
+ super(node)
172
+ }
173
+
174
+ /**
175
+ * @deprecated Using this for arrays is bad practice because all of the array elements will be
176
+ * assigned the same node as the array itself. This makes it difficult to bidrectionally sync
177
+ * data created this way because you don't have the granularity required to know where each of
178
+ * the element values (and any nested values) came from.
179
+ */
180
+ static fromArrayValue<const D extends unknown[]>(value: D, node: SupportedNode): ArrayData<D> {
181
+ const elements = value.map((value) => Data.fromValue(value, node)) as WrappedData<D>
182
+
183
+ return new ArrayData(elements, node)
184
+ }
185
+
186
+ getValue() {
187
+ return this.elements.map((element) => element.getValue()) as D
188
+ }
189
+
190
+ getElements() {
191
+ return this.elements
192
+ }
193
+ }
194
+
195
+ export class ObjectData<const D extends Record<string, unknown> = Record<string, unknown>> extends Data<D> {
196
+ constructor(
197
+ private readonly properties: WrappedData<D>,
198
+ node: SupportedNode
199
+ ) {
200
+ super(node)
201
+ }
202
+
203
+ /**
204
+ * @deprecated Using this for objects is bad practice because all of the object properties will
205
+ * be assigned the same node as the object itself. This makes it difficult to bidrectionally sync
206
+ * data created this way because you don't have the granularity required to know where each of
207
+ * the property values (and any nested values) came from.
208
+ */
209
+ static fromObjectValue<const D extends Record<string, unknown>>(value: D, node: SupportedNode): ObjectData<D> {
210
+ const properties = Object.fromEntries(
211
+ Object.entries(value).map(([key, value]) => [key, Data.fromValue(value, node)])
212
+ ) as WrappedData<D>
213
+
214
+ return new ObjectData(properties, node)
215
+ }
216
+
217
+ getValue() {
218
+ return Object.fromEntries(Object.entries(this.properties).map(([key, value]) => [key, value.getValue()])) as D
219
+ }
220
+
221
+ getProperty<const K extends keyof D>(name: K) {
222
+ return this.properties[name]
223
+ }
224
+
225
+ getProperties() {
226
+ return this.properties
227
+ }
228
+ }
229
+
230
+ export class EntityData<const D extends Record<string, unknown> = Record<string, unknown>> extends ObjectData<D> {
231
+ constructor(
232
+ private readonly kind: string,
233
+ private readonly guid: string,
234
+ entity: ObjectData<D>,
235
+ node: SupportedNode
236
+ ) {
237
+ super(entity.getProperties(), node)
238
+ }
239
+
240
+ getKind() {
241
+ return this.kind
242
+ }
243
+
244
+ getGuid() {
245
+ return this.guid
246
+ }
247
+ }
@@ -0,0 +1,57 @@
1
+ import { SupportedKindName, SupportedNodeByKindName } from '@servicenow/sdk-project'
2
+ import { Data, EntityData, Xml, XmlData } from './Data'
3
+ import { Context } from '../../Context'
4
+ import { FluentDiagnostic } from '../../Diagnostic'
5
+
6
+ export type ExtractionResult<D extends Data = Data> =
7
+ | {
8
+ handled: false
9
+ }
10
+ | {
11
+ handled: true
12
+ data: D[]
13
+ diagnostics: FluentDiagnostic[]
14
+ }
15
+
16
+ /**
17
+ * An XML extractor function accepts an {@linkcode Xml} object and returns one or
18
+ * more pieces of {@linkcode XmlData} extracted from that object. The function may
19
+ * also return undefined to indicate that it did not handle the object.
20
+ */
21
+ export type XmlExtractorFunction = (xml: Xml, context: Context) => XmlData | XmlData[] | undefined
22
+
23
+ /**
24
+ * A raw extractor function accepts an AST node and returns one or more pieces of
25
+ * {@linkcode Data} extracted from that node which cannot be composed into documents
26
+ * on their own but could be part of an entity.
27
+ */
28
+ export type RawExtractorFunction<N extends SupportedKindName | 'any' = SupportedKindName> = (
29
+ node: N extends SupportedKindName ? SupportedNodeByKindName<N> : any,
30
+ context: Context
31
+ ) => ExtractionResult<Data>
32
+
33
+ /**
34
+ * An entity extractor function accepts an AST node and returns one or more pieces
35
+ * of {@linkcode EntityData} extracted from that node which can then be composed
36
+ * into documents.
37
+ */
38
+ export type EntityExtractorFunction<N extends SupportedKindName | 'any' = SupportedKindName> = (
39
+ node: N extends SupportedKindName ? SupportedNodeByKindName<N> : any,
40
+ context: Context
41
+ ) => ExtractionResult<EntityData>
42
+
43
+ // This is only imported so it can be referenced in the JS doc below
44
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
45
+ import type { Plugin } from '../../Plugin'
46
+
47
+ /**
48
+ * @see {@linkcode Plugin#extractors}
49
+ */
50
+ export type Extractors<
51
+ RawNodeKinds extends SupportedKindName | 'any',
52
+ EntityNodeKinds extends SupportedKindName | 'any',
53
+ > = {
54
+ xml?: XmlExtractorFunction
55
+ raw?: { [K in RawNodeKinds]: RawExtractorFunction<K> }
56
+ entity?: { [K in EntityNodeKinds]: EntityExtractorFunction<K> }
57
+ }
@@ -0,0 +1,2 @@
1
+ export * from './Extractors'
2
+ export * from './Data'
@@ -0,0 +1,9 @@
1
+ export * from './extractors'
2
+ export * from './Composer'
3
+ export * from './Arranger'
4
+ export * from './Serializer'
5
+ export * from './Generator'
6
+ export * from './Transformer'
7
+ export * from './PostProcessor'
8
+ export * from './Diagnostics'
9
+ export * from './OwnedTables'
@@ -0,0 +1,5 @@
1
+ export { Plugin } from './Plugin'
2
+ export { type Context, type HandledStates } from './Context'
3
+ export * from './behaviors'
4
+ export * from './util'
5
+ export * from './Diagnostic'