@servicenow/sdk-build-core 4.6.1 → 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 (53) hide show
  1. package/dist/compiler.d.ts +2 -0
  2. package/dist/compiler.js +13 -7
  3. package/dist/compiler.js.map +1 -1
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +1 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/now-config.d.ts +37 -0
  8. package/dist/now-config.js +9 -0
  9. package/dist/now-config.js.map +1 -1
  10. package/dist/package-inventory.d.ts +15 -0
  11. package/dist/package-inventory.js +59 -0
  12. package/dist/package-inventory.js.map +1 -0
  13. package/dist/plugins/context.d.ts +2 -2
  14. package/dist/plugins/index.d.ts +0 -1
  15. package/dist/plugins/index.js +0 -1
  16. package/dist/plugins/index.js.map +1 -1
  17. package/dist/plugins/plugin.d.ts +41 -53
  18. package/dist/plugins/plugin.js +502 -160
  19. package/dist/plugins/plugin.js.map +1 -1
  20. package/dist/plugins/shape.d.ts +13 -2
  21. package/dist/plugins/shape.js +96 -15
  22. package/dist/plugins/shape.js.map +1 -1
  23. package/dist/taxonomy.js +7 -2
  24. package/dist/taxonomy.js.map +1 -1
  25. package/dist/telemetry/clients/detect-agent.d.ts +4 -0
  26. package/dist/telemetry/clients/detect-agent.js +84 -0
  27. package/dist/telemetry/clients/detect-agent.js.map +1 -0
  28. package/dist/telemetry/clients/node-client.d.ts +2 -0
  29. package/dist/telemetry/clients/node-client.js +10 -9
  30. package/dist/telemetry/clients/node-client.js.map +1 -1
  31. package/dist/telemetry/index.d.ts +1 -1
  32. package/now.config.schema.json +19 -0
  33. package/package.json +9 -5
  34. package/src/compiler.ts +14 -7
  35. package/src/index.ts +1 -0
  36. package/src/now-config.ts +11 -0
  37. package/src/package-inventory.ts +75 -0
  38. package/src/plugins/context.ts +2 -2
  39. package/src/plugins/index.ts +0 -1
  40. package/src/plugins/plugin.ts +682 -228
  41. package/src/plugins/shape.ts +115 -24
  42. package/src/taxonomy.ts +8 -2
  43. package/src/telemetry/clients/detect-agent.ts +88 -0
  44. package/src/telemetry/clients/node-client.ts +12 -8
  45. package/src/telemetry/index.ts +1 -1
  46. package/dist/plugins/cache.d.ts +0 -15
  47. package/dist/plugins/cache.js +0 -22
  48. package/dist/plugins/cache.js.map +0 -1
  49. package/dist/plugins/usage.d.ts +0 -11
  50. package/dist/plugins/usage.js +0 -26
  51. package/dist/plugins/usage.js.map +0 -1
  52. package/src/plugins/cache.ts +0 -23
  53. package/src/plugins/usage.ts +0 -26
package/src/compiler.ts CHANGED
@@ -40,6 +40,7 @@ export class Compiler extends ts.Project {
40
40
  private readonly rootDir: string
41
41
  private readonly sourceFileToRelativeModuleSpecifier: Record<string, string> = {}
42
42
  private readonly generatedTableFilePath: string
43
+ private readonly generatedBootstrappedTablesFilePath: string
43
44
  private moduleProject: ts.Project | undefined
44
45
 
45
46
  constructor(
@@ -63,6 +64,7 @@ export class Compiler extends ts.Project {
63
64
 
64
65
  this.rootDir = rootDir
65
66
  this.generatedTableFilePath = path.join(this.rootDir, '$$GENERATED$$_common_table.ts')
67
+ this.generatedBootstrappedTablesFilePath = path.join(this.rootDir, '$$GENERATED$$_bootstrapped_tables.ts')
66
68
  this.addGlobalTableDefinitionFile()
67
69
  if (typesDir) {
68
70
  this.addTypesTableDeclaration(typesDir)
@@ -89,7 +91,7 @@ export class Compiler extends ts.Project {
89
91
  }
90
92
 
91
93
  addGlobalTableDefinitionFile() {
92
- const globalTableDefinitionContent = `
94
+ const tableDefinitionContent = `
93
95
  import '@servicenow/sdk/global'
94
96
  import { Table } from '@servicenow/sdk/core'
95
97
 
@@ -105,10 +107,18 @@ declare global {
105
107
  }
106
108
  }
107
109
  `
108
- this.createSourceFile(this.generatedTableFilePath, globalTableDefinitionContent, {
110
+ this.createSourceFile(this.generatedTableFilePath, tableDefinitionContent, {
109
111
  overwrite: true,
110
112
  scriptKind: ts.ScriptKind.TS,
111
113
  })
114
+ this.createSourceFile(this.generatedBootstrappedTablesFilePath, tableDefinitionContent, {
115
+ overwrite: true,
116
+ scriptKind: ts.ScriptKind.TS,
117
+ })
118
+ }
119
+
120
+ getGeneratedBootstrappedTablesFile(): ts.SourceFile | undefined {
121
+ return this.getSourceFile(this.generatedBootstrappedTablesFilePath)
112
122
  }
113
123
 
114
124
  /**
@@ -332,16 +342,15 @@ declare module '*.html' {
332
342
  return this.generatedTableFilePath === sourcePath
333
343
  }
334
344
 
335
- // Cache for table column types that need coercion (boolean/number only)
345
+ // Cache for table column types that need coercion (boolean/number only).
346
+ // Populated on first call to getTableColumnTypes(); subsequent calls return immediately.
336
347
  private tableColumnTypesCache = new Map<string, Map<string, 'boolean' | 'number' | 'array' | 'array-optional'>>()
337
348
 
338
349
  getTableColumnTypes(tableName: string): Map<string, 'boolean' | 'number' | 'array' | 'array-optional'> | undefined {
339
- // Return cached value if available
340
350
  if (this.tableColumnTypesCache.has(tableName)) {
341
351
  return this.tableColumnTypesCache.get(tableName)
342
352
  }
343
353
 
344
- // Lazily resolve and cache on first access
345
354
  const tempFilePath = path.join(this.rootDir, 'src', '$$TEMP_DATA_RESOLVER$$.ts')
346
355
  const typeDeclaration = `type T = import('@servicenow/sdk/core').Data<'${tableName}'>`
347
356
 
@@ -367,10 +376,8 @@ declare module '*.html' {
367
376
  if (typeText === 'boolean' || typeText === 'number') {
368
377
  columnTypes.set(property.getName(), typeText)
369
378
  } else if (typeText.match(/^\(.*\)\[\]$/)) {
370
- // Matches `(...)[]` -> this field should always be an array
371
379
  columnTypes.set(property.getName(), 'array')
372
380
  } else if (typeText.match(/^.*\[\]$/)) {
373
- // Matches `[]` -> this field may be an array or a single value
374
381
  columnTypes.set(property.getName(), 'array-optional')
375
382
  }
376
383
  })
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export * from './path'
17
17
  export * from './typescript'
18
18
  export * from './crypto'
19
19
  export * from './compression'
20
+ export * from './package-inventory'
20
21
  export * from './formatter'
21
22
  export * from './telemetry'
22
23
  export * from './claims'
package/src/now-config.ts CHANGED
@@ -136,6 +136,15 @@ const NetworkPolicySchema = z
136
136
 
137
137
  export type NetworkPolicy = z.infer<typeof NetworkPolicySchema>
138
138
 
139
+ /**
140
+ * Schema for Linter configuration options
141
+ */
142
+ const LinterSchema = z
143
+ .object({
144
+ module: z.object({ enabled: z.boolean().default(true) }),
145
+ })
146
+ .default({ module: { enabled: true } })
147
+
139
148
  /**
140
149
  * Network Pillar Schema for Wildcard Policy
141
150
  */
@@ -248,6 +257,7 @@ const NowConfigSchema = z
248
257
  trustedModules: z.array(z.string()).default([]),
249
258
  tsconfigPath: z.string().optional(),
250
259
  scripts: z.record(z.string(), z.string()).default({}),
260
+ linter: LinterSchema,
251
261
  tableOutputFormat: z.enum(['bootstrap', 'component']).default('bootstrap').optional(),
252
262
  defaultLanguage: z
253
263
  .string()
@@ -334,6 +344,7 @@ const NowConfigSchema = z
334
344
  | 'description'
335
345
  | 'installedAsDependency'
336
346
  | 'scripts'
347
+ | 'linter'
337
348
  | 'tableOutputFormat'
338
349
  | 'defaultLanguage'
339
350
  | 'tableDefaultLanguage'
@@ -0,0 +1,75 @@
1
+ import { crypto } from './crypto'
2
+ import { FileSystem } from './fs'
3
+ import { path } from './path'
4
+
5
+ export interface PackageInventoryConfig {
6
+ build: string
7
+ type: 'scoped' | 'global'
8
+ appVersion: string
9
+ version?: string
10
+ }
11
+
12
+ /**
13
+ * Generate SHA-256 hash for file content (matches ServiceNow's package_inventory.csv format)
14
+ */
15
+ export async function sha256(content: string | Uint8Array): Promise<string> {
16
+ const data = typeof content === 'string' ? new TextEncoder().encode(content) : content
17
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data)
18
+ const hashArray = Array.from(new Uint8Array(hashBuffer))
19
+ return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
20
+ }
21
+
22
+ /**
23
+ * Recursively find all files in a directory
24
+ */
25
+ function findAllFiles(fs: FileSystem, dir: string, baseDir: string): string[] {
26
+ const files: string[] = []
27
+
28
+ if (!FileSystem.existsSync(fs, dir)) {
29
+ return files
30
+ }
31
+
32
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
33
+ const fullPath = path.join(dir, entry.name)
34
+ if (entry.isDirectory()) {
35
+ files.push(...findAllFiles(fs, fullPath, baseDir))
36
+ } else {
37
+ files.push(path.relative(baseDir, fullPath))
38
+ }
39
+ }
40
+
41
+ return files
42
+ }
43
+
44
+ /**
45
+ * Generate package_inventory.csv from files in a directory on disk (for pack command)
46
+ */
47
+ export async function generatePackageInventoryFromDirectory(
48
+ fs: FileSystem,
49
+ appOutputDir: string,
50
+ config: PackageInventoryConfig
51
+ ): Promise<string> {
52
+ const headerLines: string[] = [
53
+ `#build=${config.build}`,
54
+ `#appVersion=${config.appVersion}`,
55
+ `#type=${config.type}`,
56
+ `#version=${config.version ?? '1.0.0'}`,
57
+ ]
58
+
59
+ // Find all files in directory
60
+ const filePaths = findAllFiles(fs, appOutputDir, appOutputDir)
61
+ .filter((f) => !f.endsWith('package_inventory.csv'))
62
+ .sort()
63
+
64
+ // Hash all files in parallel for better performance
65
+ const hashResults = await Promise.all(
66
+ filePaths.map(async (relativePath) => {
67
+ const fullPath = path.join(appOutputDir, relativePath)
68
+ const content = fs.readFileSync(fullPath)
69
+ const hash = await sha256(content)
70
+ return `${relativePath};${hash}`
71
+ })
72
+ )
73
+
74
+ return `${[...headerLines, ...hashResults].join('\n')}\n`
75
+ }
@@ -43,12 +43,12 @@ export interface Diagnostics {
43
43
 
44
44
  export interface Transform {
45
45
  toShape(node: ts.Node, ...plugins: Plugin[]): Promise<Result<Shape>>
46
- recordToShape(record: Record, database: Database, ...plugins: Plugin[]): Promise<Result<Shape>>
47
- toSubclass<const S extends Shape>(shape: S, ...plugins: Plugin[]): Promise<Result<S>>
46
+ toSubclass<const S extends Shape>(shape: S, ...plugins: Plugin[]): Promise<S>
48
47
  toRecord(node: ts.Node, ...plugins: Plugin[]): Promise<Result<Record>>
49
48
  toRecord(shape: Shape, ...plugins: Plugin[]): Promise<Result<Record>>
50
49
  toRecord(file: File, ...plugins: Plugin[]): Promise<Result<Record>>
51
50
  toRecord(source: ts.Node | Shape | File, ...plugins: Plugin[]): Promise<Result<Record>>
51
+ recordToShape(record: Record, database: Database, ...plugins: Plugin[]): Promise<Result<Shape>>
52
52
  getUpdateName(record: Record, ...plugins: Plugin[]): Promise<string>
53
53
  }
54
54
 
@@ -7,5 +7,4 @@ export * from './product'
7
7
  export * from './project'
8
8
  export * from './database'
9
9
  export * from './time'
10
- export * from './usage'
11
10
  export * from './post-install'